From b9f0dd0466760c553db66e5159ab0712708f468d Mon Sep 17 00:00:00 2001 From: BARANGER Emmanuel Date: Sun, 5 Jan 2014 21:23:18 +0100 Subject: [PATCH 1/6] RadarDist : Fixed a problem onboard radar happens automatically when you are in range of an mp gamer that uses "OpenRadar" by 5H1N0B1 --- .../Instruments-3d/radardist/radardist.nas | 258 +++++++++--------- 1 file changed, 129 insertions(+), 129 deletions(-) diff --git a/Aircraft/Instruments-3d/radardist/radardist.nas b/Aircraft/Instruments-3d/radardist/radardist.nas index 6e2e0b5be..910f2e307 100644 --- a/Aircraft/Instruments-3d/radardist/radardist.nas +++ b/Aircraft/Instruments-3d/radardist/radardist.nas @@ -45,159 +45,159 @@ var FT2M = 0.3048; var NM2KM = 1.852; var my_maxrange = func(a) { - max_range = 0; - radar_range = 0; - radar_area = 0; - acname = aircraftData[a] or 0; - if ( acname ) { - have_radar = radarData[acname][4]; - if ( have_radar != "none" and have_radar != "unknown") { - radar_area = radarData[acname][7]; - radar_range = radarData[acname][5]; - if ( radar_area > 0 ) { max_range = radar_range / radar_area } - } - } - return( max_range ); + max_range = 0; + radar_range = 0; + radar_area = 0; + acname = aircraftData[a] or 0; + if ( acname ) { + have_radar = radarData[acname][4]; + if ( have_radar != "none" and have_radar != "unknown") { + radar_area = radarData[acname][7]; + radar_range = radarData[acname][5]; + if ( radar_area > 0 ) { max_range = radar_range / radar_area } + } + } + return( max_range ); } var get_ecm_type_num = func(a) { - acname = aircraftData[a] or 0; - var num = 0; - if ( acname ) { - num = radarData[acname][8]; - } - return( num ); + acname = aircraftData[a] or 0; + var num = 0; + if ( acname ) { + num = radarData[acname][8]; + } + return( num ); } var get_aircraft_name = func( t ) { - # Get the multiplayer aircraft name. - mpnode_string = t; - mpnode = props.globals.getNode(mpnode_string); - if ( find("tanker", mpnode_string) > 0 ) { - cutname = "KC135"; - } else { - mpname_node_string = mpnode_string ~ "/sim/model/path"; - mpname_node = props.globals.getNode(mpname_node_string); - if (mpname_node == nil) { return(0) } + # Get the multiplayer aircraft name. + mpnode_string = t; + mpnode = props.globals.getNode(mpnode_string); + if ( find("tanker", mpnode_string) > 0 ) { + cutname = "KC135"; + } else { + mpname_node_string = mpnode_string ~ "/sim/model/path"; + mpname_node = props.globals.getNode(mpname_node_string); + if (mpname_node == nil) { return(0) } - var mpname = mpname_node.getValue(); - if (mpname == nil) { return(0) } + var mpname = mpname_node.getValue(); + if (mpname == nil) { return(0) } - splitname = split("/", mpname); - cutname = splitname[1]; - - } - return( cutname ); + splitname = split("/", mpname); + # + # cutname = splitname[1]; + # + # **** by 5H1N0B1 05/01/2014 + # Fixed a problem onboard radar happens automatically when you are in range of an mp gamer that uses "OpenRadar" + # + cutname = splitname[size(splitname)-1]; + + } + return( cutname ); } var radis = func(t, my_radarcorr) { - cutname = get_aircraft_name(t); - # Calculate the rcs detection range, - # if aircraft is not found in list, 0 (generic) will be used. - acname = aircraftData[cutname]; - if ( acname == nil ) { acname = 0 } - rcs_4r = radarData[acname][3]; + cutname = get_aircraft_name(t); + # Calculate the rcs detection range, + # if aircraft is not found in list, 0 (generic) will be used. + acname = aircraftData[cutname]; + if ( acname == nil ) { acname = 0 } + rcs_4r = radarData[acname][3]; - # Add a correction factor for altitude, as lower alt means - # shorter radar distance (due to air turbulence). - alt_corr = 1; - alt_ac = mpnode.getNode("position/altitude-ft").getValue(); - if (alt_ac <= 1000) { - alt_corr = 0.6; - } elsif ((alt_ac > 1000) and (alt_ac <= 5000)) { - alt_corr = 0.8; - } - # Add a correction factor for altitude AGL. Skip if AI tanker. - agl_corr = 1; - if ( find("tanker", t) == 0 ) { - mp_lon = mpnode.getNode("position/longitude-deg").getValue(); - pos_elev = geo.elevation(mp_lat, mp_lon); - if (pos_elev != nil) { - mp_agl = alt_ac - ( pos_elev / FT2M ); - if (mp_agl <= 40) { - agl_corr = 0.03; - } elsif ((mp_agl > 40) and (mp_agl <= 80)) { - agl_corr = 0.07; - } elsif ((mp_agl > 80) and (mp_agl <= 120)) { - agl_corr = 0.25; - } elsif ((mp_agl > 120) and (mp_agl <= 300)) { - agl_corr = 0.4; - } elsif ((mp_agl > 300) and (mp_agl <= 600)) { - agl_corr = 0.7; - } elsif ((mp_agl > 600) and (mp_agl <= 1000)) { - agl_corr = 0.85; - } - } - } - # Calculate the detection distance for this multiplayer. - det_range = my_radarcorr * rcs_4r * alt_corr * agl_corr / NM2KM; + # Add a correction factor for altitude, as lower alt means + # shorter radar distance (due to air turbulence). + alt_corr = 1; + alt_ac = mpnode.getNode("position/altitude-ft").getValue(); + if (alt_ac <= 1000) { + alt_corr = 0.6; + } elsif ((alt_ac > 1000) and (alt_ac <= 5000)) { + alt_corr = 0.8; + } + # Add a correction factor for altitude AGL. Skip if AI tanker. + agl_corr = 1; + if ( find("tanker", t) == 0 ) { + mp_lon = mpnode.getNode("position/longitude-deg").getValue(); + pos_elev = geo.elevation(mp_lat, mp_lon); + if (pos_elev != nil) { + mp_agl = alt_ac - ( pos_elev / FT2M ); + if (mp_agl <= 40) { + agl_corr = 0.03; + } elsif ((mp_agl > 40) and (mp_agl <= 80)) { + agl_corr = 0.07; + } elsif ((mp_agl > 80) and (mp_agl <= 120)) { + agl_corr = 0.25; + } elsif ((mp_agl > 120) and (mp_agl <= 300)) { + agl_corr = 0.4; + } elsif ((mp_agl > 300) and (mp_agl <= 600)) { + agl_corr = 0.7; + } elsif ((mp_agl > 600) and (mp_agl <= 1000)) { + agl_corr = 0.85; + } + } + } + # Calculate the detection distance for this multiplayer. + det_range = my_radarcorr * rcs_4r * alt_corr * agl_corr / NM2KM; - # Compare if aircraft is in detection range and return. - act_range = mpnode.getNode("radar/range-nm").getValue() or 500; - if (det_range >= act_range) { - return(1); - } - return(0); + # Compare if aircraft is in detection range and return. + act_range = mpnode.getNode("radar/range-nm").getValue() or 500; + if (det_range >= act_range) { + return(1); + } + return(0); } var radar_horizon = func(our_alt_ft, target_alt_ft) { - if (our_alt_ft < 0 or our_alt_ft == nil) { our_alt_ft = 0 } - if (target_alt_ft < 0 or target_alt_ft == nil) { target_alt_ft = 0 } - return( 2.2 * ( math.sqrt(our_alt_ft * FT2M) + math.sqrt(target_alt_ft * FT2M) ) ); + if (our_alt_ft < 0 or our_alt_ft == nil) { our_alt_ft = 0 } + if (target_alt_ft < 0 or target_alt_ft == nil) { target_alt_ft = 0 } + return( 2.2 * ( math.sqrt(our_alt_ft * FT2M) + math.sqrt(target_alt_ft * FT2M) ) ); } var load_data = func { - # a) converts aircraft model name to lookup (index) number in aircraftData{}. - # b) appends ordered list of data into radarData[], - # data is: - # - acname (the index number) - # - the first (if several) aircraft model name corresponding to this type, - # - RCS(m2), - # - 4th root of RCS, - # - radar type, - # - max. radar range(km), - # - max. radar range target seize(RCS)m2, - # - 4th root of radar RCS. - var data_node = props.globals.getNode("instrumentation/radar-performance/data"); - var aircraft_types = data_node.getChildren(); - foreach( var t; aircraft_types ) { - var index = t.getIndex(); - var aircraft_names = t.getChildren(); - foreach( var n; aircraft_names) { - if ( n.getName() == "name") { - aircraftData[n.getValue()] = index; - } - } - var t_list = [ - index, - t.getNode("name[0]").getValue(), - t.getNode("rcs-sq-meter").getValue(), - t.getNode("rcs-4th-root").getValue(), - t.getNode("radar-type").getValue(), - t.getNode("max-radar-rng-km").getValue(), - t.getNode("max-target-sq-meter").getValue(), - t.getNode("max-target-4th-root").getValue(), - t.getNode("ecm-type-num").getValue() - ]; - append(radarData, t_list); - } + # a) converts aircraft model name to lookup (index) number in aircraftData{}. + # b) appends ordered list of data into radarData[], + # data is: + # - acname (the index number) + # - the first (if several) aircraft model name corresponding to this type, + # - RCS(m2), + # - 4th root of RCS, + # - radar type, + # - max. radar range(km), + # - max. radar range target seize(RCS)m2, + # - 4th root of radar RCS. + var data_node = props.globals.getNode("instrumentation/radar-performance/data"); + var aircraft_types = data_node.getChildren(); + foreach( var t; aircraft_types ) { + var index = t.getIndex(); + var aircraft_names = t.getChildren(); + foreach( var n; aircraft_names) { + if ( n.getName() == "name") { + aircraftData[n.getValue()] = index; + } + } + var t_list = [ + index, + t.getNode("name[0]").getValue(), + t.getNode("rcs-sq-meter").getValue(), + t.getNode("rcs-4th-root").getValue(), + t.getNode("radar-type").getValue(), + t.getNode("max-radar-rng-km").getValue(), + t.getNode("max-target-sq-meter").getValue(), + t.getNode("max-target-4th-root").getValue(), + t.getNode("ecm-type-num").getValue() + ]; + append(radarData, t_list); + } } var launched = 0; var init = func { - if (! launched) { - print("Initializing Radar Data"); - io.read_properties(data_path, props.globals); - load_data(); - launched = 1; - } + if (! launched) { + print("Initializing Radar Data"); + io.read_properties(data_path, props.globals); + load_data(); + launched = 1; + } } - - - - - - From 9af485ad93aa146def7fdb46e6430da48731d580 Mon Sep 17 00:00:00 2001 From: Gijs de Rooy Date: Wed, 8 Jan 2014 21:45:48 +0100 Subject: [PATCH 2/6] Navigation display: - remove debug prints - proper ADF/ILS display - add LCD/CRT option - fix ranges --- Nasal/canvas/map/airports-nd.model | 36 +- Nasal/canvas/map/boeingND.svg | 2417 ++++------------------------ Nasal/canvas/map/dme.model | 6 +- Nasal/canvas/map/fixes.model | 4 +- Nasal/canvas/map/navaids.model | 2 +- Nasal/canvas/map/navdisplay.mfd | 729 +++++---- Nasal/canvas/map/route.model | 4 +- 7 files changed, 765 insertions(+), 2433 deletions(-) diff --git a/Nasal/canvas/map/airports-nd.model b/Nasal/canvas/map/airports-nd.model index 16c06387d..52eceb225 100644 --- a/Nasal/canvas/map/airports-nd.model +++ b/Nasal/canvas/map/airports-nd.model @@ -2,31 +2,27 @@ var AirportsNDModel = {}; AirportsNDModel.new = func make(AirportsNDModel, LayerModel); AirportsNDModel.init = func { - #print("Updating AirportsNDModel"); - me._view.reset(); - var results = positioned.findWithinRange(me._controller.query_range()*2, "airport"); + var results = positioned.findWithinRange(me._controller.query_range(), "airport"); var numResults = 0; foreach(result; results) { - if (numResults < 50) { - var apt = airportinfo(result.id); - var runways = apt.runways; - var runway_keys = sort(keys(runways),string.icmp); - var validApt = 0; - foreach(var rwy; runway_keys){ - var r = runways[rwy]; - if (r.length > 1890) # Only display suitably large airports - validApt = 1; - if (result.id == getprop("autopilot/route-manager/destination/airport") or result.id == getprop("autopilot/route-manager/departure/airport")) - validApt = 1; - } + var apt = airportinfo(result.id); + var runways = apt.runways; + var runway_keys = sort(keys(runways),string.icmp); + var validApt = 0; + foreach(var rwy; runway_keys){ + var r = runways[rwy]; + if (r.length > 1890) # Only display suitably large airports + validApt = 1; + if (result.id == getprop("autopilot/route-manager/destination/airport") or result.id == getprop("autopilot/route-manager/departure/airport")) + validApt = 1; + } - if(validApt) { - #canvas.draw_apt(me.apt_group, result.lat,result.lon,result.id,i); - me.push(result); - numResults += 1; - } + if(validApt) { + #canvas.draw_apt(me.apt_group, result.lat,result.lon,result.id,i); + me.push(result); + numResults += 1; } } # set RefPos and hdg to apt !! diff --git a/Nasal/canvas/map/boeingND.svg b/Nasal/canvas/map/boeingND.svg index 402c6dbaf..641be3035 100644 --- a/Nasal/canvas/map/boeingND.svg +++ b/Nasal/canvas/map/boeingND.svg @@ -17,548 +17,206 @@ height="1024" width="1024" version="1.1">image/svg+xmlGijs de Rooy - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -image/svg+xmlGijs de Rooy - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -30 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + style="font-size:39.99999619px">30 @@ -1634,10 +601,10 @@ id="text3088" y="-108.93858" x="-845.96948" - style="font-size:36.000011px;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" + style="font-size:36.00001144px;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;font-family:Liberation Sans;-inkscape-font-specification:Liberation Sans" xml:space="preserve" transform="matrix(0,-1,1,0,0,0)">24 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + style="font-size:39.99995041px">24 @@ -1728,10 +635,10 @@ id="text3096" y="-1071.5541" x="-879.19897" - style="font-size:36.000046px;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" + style="font-size:36.00004578px;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;font-family:Liberation Sans;-inkscape-font-specification:Liberation Sans" xml:space="preserve" transform="matrix(-0.866026,-0.5,0.5,-0.866026,0,0)">18 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + style="font-size:40.00000381px">18 @@ -1822,10 +669,10 @@ id="text3104" y="-1581.0712" x="-51.792171" - style="font-size:36.000004px;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" + style="font-size:36.00000381px;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;font-family:Liberation Sans;-inkscape-font-specification:Liberation Sans" xml:space="preserve" transform="matrix(-0.866025,0.5,-0.5,-0.866025,0,0)">12 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + style="font-size:40.00001144px">12 @@ -1916,10 +703,10 @@ id="text3112" y="-1123.9312" x="812.80585" - style="font-size:36.000042px;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" + style="font-size:36.00004196px;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;font-family:Liberation Sans;-inkscape-font-specification:Liberation Sans" xml:space="preserve" transform="matrix(0,1,-1,0,0,0)">6 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + style="font-size:39.99998093px">6 @@ -2010,10 +737,10 @@ id="text3120" y="-158.40652" x="844.79596" - style="font-size:36.000095px;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" + style="font-size:36.00009537px;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;font-family:Liberation Sans;-inkscape-font-specification:Liberation Sans" xml:space="preserve" transform="matrix(0.866026,0.5,-0.5,0.866026,0,0)"> - - - - - - - - - - - - - - - - - - -33 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + style="font-size:80.43321228px">33 @@ -2466,10 +957,10 @@ id="text3928" y="265.32059" x="-503.69513" - style="font-size:36.000031px;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" + style="font-size:36.00003052px;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;font-family:Liberation Sans;-inkscape-font-specification:Liberation Sans" xml:space="preserve" transform="matrix(0.5,-0.866026,0.866026,0.5,0,0)">27 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + style="font-size:80.43321228px">27 @@ -2560,10 +991,10 @@ id="text3936" y="-541.37213" x="-1019.1653" - style="font-size:35.999966px;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" + style="font-size:35.99996567px;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;font-family:Liberation Sans;-inkscape-font-specification:Liberation Sans" xml:space="preserve" transform="matrix(-0.5,-0.866025,0.866025,-0.5,0,0)">21 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + style="font-size:80.43321228px">21 @@ -2654,10 +1025,10 @@ id="text3944" y="-1392.2814" x="-557.84332" - style="font-size:36.000027px;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" + style="font-size:36.0000267px;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;font-family:Liberation Sans;-inkscape-font-specification:Liberation Sans" xml:space="preserve" transform="scale(-1,-1)">15 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + style="font-size:80.43321228px">15 @@ -2748,10 +1059,10 @@ id="text3952" y="-1431.3702" x="409.84222" - style="font-size:36.000061px;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" + style="font-size:36.00006104px;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;font-family:Liberation Sans;-inkscape-font-specification:Liberation Sans" xml:space="preserve" transform="matrix(-0.5,0.866026,-0.866026,-0.5,0,0)">9 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + style="font-size:80.43321228px">9 @@ -2842,10 +1093,10 @@ id="text3960" y="-611.09729" x="941.70276" - style="font-size:36.000008px;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" + style="font-size:36.00000763px;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;font-family:Liberation Sans;-inkscape-font-specification:Liberation Sans" xml:space="preserve" transform="matrix(0.5,0.866025,-0.866025,0.5,0,0)">3 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + style="font-size:80.43321228px">3 WXR + ARPT + WPT + STA + N + + W + + E + + S - \ No newline at end of file + + + \ No newline at end of file diff --git a/Nasal/canvas/map/dme.model b/Nasal/canvas/map/dme.model index 90853158d..53e4d0f61 100644 --- a/Nasal/canvas/map/dme.model +++ b/Nasal/canvas/map/dme.model @@ -11,12 +11,10 @@ DMEModel.init = func { )); } - var results = positioned.findWithinRange(me._controller.query_range()*2 ,"dme"); + var results = positioned.findWithinRange(me._controller.query_range() ,"dme"); foreach(result; results) { me.push(result); } me.notifyView(); -} - - +} \ No newline at end of file diff --git a/Nasal/canvas/map/fixes.model b/Nasal/canvas/map/fixes.model index 5d46eeee4..97e2697f9 100644 --- a/Nasal/canvas/map/fixes.model +++ b/Nasal/canvas/map/fixes.model @@ -4,10 +4,10 @@ FixModel.new = func make( LayerModel, FixModel ); FixModel.init = func { me._view.reset(); # wraps removeAllChildren() ATM - var results = positioned.findWithinRange( me._controller['query_range']()*2 ,"fix"); + var results = positioned.findWithinRange( me._controller['query_range']() ,"fix"); var numNum = 0; foreach(result; results) { - # Skip airport navaids (real thing makes distinction between high/low altitude fixes) + # Skip airport fixes if(string.match(result.id,"*[^0-9]")) { me.push(result); numNum = numNum + 1; diff --git a/Nasal/canvas/map/navaids.model b/Nasal/canvas/map/navaids.model index 65cf37344..e4edbf2d6 100644 --- a/Nasal/canvas/map/navaids.model +++ b/Nasal/canvas/map/navaids.model @@ -2,7 +2,7 @@ var NavaidModel = {}; NavaidModel.new = func make(LayerModel, NavaidModel); NavaidModel.init = func { me._view.reset(); - var navaids = findNavaidsWithinRange(15); + var navaids = findNavaidsWithinRange(me._controller.query_range()); foreach(var n; navaids) me.push(n); me.notifyView(); diff --git a/Nasal/canvas/map/navdisplay.mfd b/Nasal/canvas/map/navdisplay.mfd index f65dc9b87..be31ee61a 100644 --- a/Nasal/canvas/map/navdisplay.mfd +++ b/Nasal/canvas/map/navdisplay.mfd @@ -2,14 +2,10 @@ # Boeing Navigation Display by Gijs de Rooy # ============================================================================== - ## # do we really need to keep track of each drawable here ?? var i = 0; - - - ## # pseudo DSL-ish: use these as placeholders in the config hash below var ALWAYS = func 1; @@ -27,15 +23,13 @@ var trigger_update = func(layer) layer._model.init(); # any aircraft-specific ND behavior should be wrapped here, # to isolate/decouple things in the generic NavDisplay class # -# Note to Gijs: this may look weird and confusing, but it' actually requires -# less coding now, and it is now even possible to configure things via a little -# XML wrapper # TODO: move this to an XML config file # -var NDStyles = { +var NDStyles = +{ ## - # this configures the 744 ND to help generalize the NavDisplay class itself - 'B747-400': { + # this configures the Boeing 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"; @@ -53,61 +47,52 @@ var NDStyles = { layers: [ { 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') and nd.in_mode('toggle_display_mode', ['MAP']); - if(nd.rangeNm() <= 40 and visible) { + var visible=nd.get_switch('toggle_waypoints') and nd.in_mode('toggle_display_mode', ['MAP']) and (nd.rangeNm() <= 40); + if(visible) trigger_update( layer ); - } layer._view.setVisible(visible); - + layer._view.setVisible(visible); }, # end of layer update predicate }, # end of fixes layer # Should redraw every 10 seconds { name:'storms', update_on:['toggle_range','toggle_weather','toggle_display_mode'], predicate: func(nd, layer) { - # print("Running fixes predicate"); var visible=nd.get_switch('toggle_weather') and nd.get_switch('toggle_display_mode') != "PLAN"; - if (visible) { - trigger_update( layer ); - } layer._view.setVisible(visible); - + if (visible) + 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); - - }, # end of layer update predicate - }, # end of storms layer + if (visible) + trigger_update( layer ); + layer._view.setVisible(visible); + }, + }, { name:'airports-nd', update_on:['toggle_range','toggle_airports','toggle_display_mode'], predicate: func(nd, layer) { - # print("Running airports-nd predicate"); var visible = nd.get_switch('toggle_airports') and nd.in_mode('toggle_display_mode', ['MAP']); - if (nd.rangeNm() <= 80 and visible) { + if (visible) trigger_update( layer ); # clear & redraw - } layer._view.setVisible( visible); - }, # end of layer update predicate }, # end of airports layer + # Should distinct between low and high altitude navaids. Hiding above 40 NM for now, to prevent clutter/lag. { name:'vor', 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) { + var visible = nd.get_switch('toggle_stations') and nd.in_mode('toggle_display_mode', ['MAP']) and (nd.rangeNm() <= 40); + if(visible) trigger_update( layer ); # clear & redraw - } layer._view.setVisible( visible ); }, # end of layer update predicate }, # end of VOR layer + # Should distinct between low and high altitude navaids. Hiding above 40 NM for now, to prevent clutter/lag. { 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){ + var visible = nd.get_switch('toggle_stations') and nd.in_mode('toggle_display_mode', ['MAP']) and (nd.rangeNm() <= 40); + if(visible) trigger_update( layer ); # clear & redraw - } layer._view.setVisible( visible ); }, # end of layer update predicate }, # end of DME layer @@ -119,18 +104,17 @@ var NDStyles = { }, # 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") and nd.in_mode('toggle_display_mode', ['MAP','PLAN'])) ; + 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 ); + layer._view.setVisible( visible ); }, # end of layer update predicate }, # end of airports-nd layer { name:'route', update_on:['toggle_display_mode',], predicate: func(nd, layer) { var visible= (nd.in_mode('toggle_display_mode', ['MAP','PLAN'])); - if (visible) { + if (visible) trigger_update( layer ); # clear & redraw - } layer._view.setVisible( visible ); }, # end of layer update predicate }, # end of route layer @@ -244,35 +228,49 @@ var NDStyles = { ], # end of vector with features - }, # end of 744 ND style -##### -## -## add support for other aircraft/ND types and styles here (737, 757, 777 etc) -## -## - -}; # end of NDStyles - + }, # end of Boeing ND style +}; ## # encapsulate hdg/lat/lon source, so that the ND may also display AI/MP aircraft in a pilot-view at some point (aka stress-testing) # 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; +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_hgg = func getprop("instrumentation/afds/settings/heading"); + m.get_trk_mag= func + { + if(getprop("/velocities/groundspeed-kt") > 80) + { + getprop("/orientation/track-magnetic-deg"); + } + else + { + getprop("/orientation/heading-magnetic-deg"); + } + }; + m.get_trk_tru = func + { + if(getprop("/velocities/groundspeed-kt") > 80) + { + getprop("/orientation/track-deg"); + } + else + { + getprop("/orientation/heading-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; } - ## # configure aircraft specific cockpit switches here # these are some defaults, can be overridden when calling NavDisplay.new() - @@ -284,18 +282,21 @@ return m; # TODO: switches are ND specific, so move to the NDStyle hash! var default_switches = { - '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'}, - 'toggle_waypoints': {path: '/inputs/wpt', value:0, type:'BOOL'}, - '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/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'}, + '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'}, + 'toggle_waypoints': {path: '/inputs/wpt', value:0, type:'BOOL'}, + '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/tfc',value:0, type:'BOOL'}, + 'toggle_centered': {path: '/inputs/nd-centered',value:0, type:'BOOL'}, + 'toggle_lh_vor_adf': {path: '/inputs/lh-vor-adf',value:0, type:'INT'}, + 'toggle_rh_vor_adf': {path: '/inputs/rh-vor-adf',value:0, type:'INT'}, + 'toggle_display_mode': {path: '/mfd/display-mode', value:'MAP', type:'STRING'}, # valid values are: APP, MAP, PLAN or VOR + 'toggle_display_type': {path: '/mfd/display-type', value:'CRT', type:'STRING'}, # valid values are: CRT or LCD + 'toggle_true_north': {path: '/mfd/true-north', value:0, type:'BOOL'}, }; # Hack to update weather radar once every 10 seconds @@ -319,30 +320,28 @@ update_apl_sym(); # - introduce a MFD class (use it also for PFD/EICAS) # - introduce a SGSubsystem class and use it here # - introduce a Boeing NavDisplay class -var NavDisplay = { - +var NavDisplay = +{ # reset handler - handle_reinit: func { - print("Cleaning up NavDisplay listeners"); - # shut down all timers and other loops here - me.update_timer.stop(); - foreach(var l; me.listeners) - removelistener(l); + handle_reinit: func + { + # shut down all timers and other loops here + me.update_timer.stop(); + foreach(var l; me.listeners) + removelistener(l); }, - - listen: func(p,c) { - append(me.listeners, setlistener(p,c)); + listen: func(p,c) + { + append(me.listeners, setlistener(p,c)); }, - # listeners for cockpit switches - listen_switch: func(s,c) { - # print("event setup for: ", id(c)); - me.listen( me.get_full_switch_path(s), func { - # print("listen_switch triggered:", s, " callback id:", id(c) ); - c(); - }); - - }, + listen_switch: func(s,c) + { + me.listen( me.get_full_switch_path(s), func + { + c(); + }); + }, # get the full property path for a given switch get_full_switch_path: func (s) { @@ -351,17 +350,18 @@ var NavDisplay = { }, # helper method for getting configurable cockpit switches (which are usually different in each aircraft) - get_switch: func(s) { - var switch = me.efis_switches[s]; - var path = me.efis_path ~ switch.path ; - #print(s,":Getting switch prop:", path); - - return getprop( path ); - }, + get_switch: func(s) + { + var switch = me.efis_switches[s]; + var path = me.efis_path ~ switch.path ; + return getprop( path ); + }, # for creating NDs that are driven by AI traffic instead of the main aircraft (generalization rocks!) - connectAI: func(source=nil) { - me.aircraft_source = { + connectAI: func(source=nil) + { + 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(), @@ -373,83 +373,55 @@ var NavDisplay = { }; }, # of connectAI - # TODO: the ctor should allow customization, for different aircraft + # TODO: the ctor should allow customization, for different aircraft # especially properties and SVG files/handles (747, 757, 777 etc) - new : func(prop1, switches=default_switches, style='B747-400') { - var m = { parents : [NavDisplay]}; + new : func(prop1, switches=default_switches, style='Boeing') + { + var m = { parents : [NavDisplay]}; - m.listeners=[]; # for cleanup handling - m.aircraft_source = NDSourceDriver.new(); # uses the main aircraft as the driver/source (speeds, position, heading) - - m.nd_style = NDStyles[style]; # look up ND specific stuff (file names etc) + m.listeners=[]; # for cleanup handling + m.aircraft_source = NDSourceDriver.new(); # uses the main aircraft as the driver/source (speeds, position, heading) + m.nd_style = NDStyles[style]; # look up ND specific stuff (file names etc) m.radio_list=["instrumentation/comm/frequencies","instrumentation/comm[1]/frequencies", "instrumentation/nav/frequencies","instrumentation/nav[1]/frequencies"]; m.mfd_mode_list=["APP","VOR","MAP","PLAN"]; - - m.efis_path = prop1; - m.efis_switches = switches ; - - # just an alias, to avoid having to rewrite the old code for now - m.rangeNm = func m.get_switch('toggle_range'); - + m.efis_path = prop1; + m.efis_switches = switches; + m.rangeNm = func m.get_switch('toggle_range'); m.efis = props.globals.initNode(prop1); m.mfd = m.efis.initNode("mfd"); - # TODO: unify this with switch handling m.mfd_mode_num = m.mfd.initNode("mode-num",2,"INT"); - m.mfd_display_mode = m.mfd.initNode("display-mode",m.mfd_mode_list[2],"STRING"); m.std_mode = m.efis.initNode("inputs/setting-std",0,"BOOL"); - m.previous_set = m.efis.initNode("inhg-previos",29.92); # watch out typo here, check other files before fixing ! + m.previous_set = m.efis.initNode("inhg-previous",29.92); m.kpa_mode = m.efis.initNode("inputs/kpa-mode",0,"BOOL"); m.kpa_output = m.efis.initNode("inhg-kpa",29.92); m.kpa_prevoutput = m.efis.initNode("inhg-kpa-previous",29.92); m.temp = m.efis.initNode("fixed-temp",0); m.alt_meters = m.efis.initNode("inputs/alt-meters",0,"BOOL"); m.fpv = m.efis.initNode("inputs/fpv",0,"BOOL"); - m.nd_centered = m.efis.initNode("inputs/nd-centered",0,"BOOL"); m.mins_mode = m.efis.initNode("inputs/minimums-mode",0,"BOOL"); m.mins_mode_txt = m.efis.initNode("minimums-mode-text","RADIO","STRING"); m.minimums = m.efis.initNode("minimums",250,"INT"); m.mk_minimums = props.globals.getNode("instrumentation/mk-viii/inputs/arinc429/decision-height"); - - # TODO: these are switches, can be unified with switch handling hash above (eventually): + # TODO: these are switches, can be unified with switch handling hash above (eventually): - m.rh_vor_adf = m.efis.initNode("inputs/rh-vor-adf",0,"INT"); # not yet in switches hash - m.lh_vor_adf = m.efis.initNode("inputs/lh-vor-adf",0,"INT"); # not yet in switches hash - m.nd_plan_wpt = m.efis.initNode("inputs/plan-wpt-index", 0, "INT"); # ditto - - ### - # initialize all switches based on the defaults specified in the switch hash - # - foreach(var switch; keys( m.efis_switches ) ) - props.globals.initNode - ( m.get_full_switch_path (switch), - m.efis_switches[switch].value, - m.efis_switches[switch].type - ); - - + m.nd_plan_wpt = m.efis.initNode("inputs/plan-wpt-index", 0, "INT"); # ditto return m; - }, + }, newMFD: func(canvas_group) { - me.listen("/sim/signals/reinit", func me.handle_reinit() ); - me.update_timer = maketimer(0.05, func me.update() ); # TODO: make interval configurable via ctor me.nd = canvas_group; - - # load the specified SVG file into the me.nd group and populate all sub groups canvas.parsesvg(me.nd, me.nd_style.svg_filename, {'font-mapper': me.nd_style.font_mapper}); - me.symbols = {}; # storage for SVG elements, to avoid namespace pollution (all SVG elements end up here) foreach(var feature; me.nd_style.features ) { - # print("Setting up SVG feature:", feature.id); me.symbols[feature.id] = me.nd.getElementById(feature.id); if(contains(feature.impl,'init')) feature.impl.init(me.nd, feature); # call The element's init code (i.e. updateCenter) } @@ -457,49 +429,51 @@ var NavDisplay = { ### this is the "old" method that's less flexible, we want to use the style hash instead (see above) # because things are much better configurable that way # now look up all required SVG elements and initialize member fields using the same name to have a convenient handle - foreach(var element; ["wind", - "dmeLDist","dmeRDist","vorLId","vorRId", - "range","status.wxr","status.wpt", - "status.sta","status.arpt"]) + foreach(var element; ["wind","dmeLDist","dmeRDist","dmeL","dmeR","vorL","vorR","vorLId","vorRId", + "range","status.wxr","status.wpt","hdgGroup","status.sta","status.arpt"]) me.symbols[element] = me.nd.getElementById(element); # 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","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(); + foreach(var element; ["windArrow","compassApp","northUp","aplSymMap","aplSymMapCtr","aplSymVor", + "staFromL2","staToL2","staFromR2","staToR2", + "locPtr","hdgTrk","truMag","altArc","planArcs", + "trkInd","compass","HdgBugCRT","TrkBugLCD","HdgBugLCD","selHdgLine","curHdgPtr", + "staFromL","staToL","staFromR","staToR"] ) + me.symbols[element] = me.nd.getElementById(element).updateCenter(); + + foreach(var element; ["HdgBugCRT2","TrkBugLCD2","HdgBugLCD2","selHdgLine2","curHdgPtr2","vorCrsPtr2"] ) + me.symbols[element] = me.nd.getElementById(element).setCenter(512,565); # this should probably be using Philosopher's new SymbolLayer ? me.map = me.nd.createChild("map","map") .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: + # this callback will be passed onto the model via the controller hash, and used for the positioned queries, to specify max query range: var get_range = func me.get_switch('toggle_range'); # predicate for the draw controller - var is_tuned = func(freq) { - var nav1=getprop("instrumentation/nav[0]/frequencies/selected-mhz"); - var nav2=getprop("instrumentation/nav[1]/frequencies/selected-mhz"); - if (freq == nav1 or freq == nav2) return 1; - return 0; + var is_tuned = func(freq) + { + var nav1=getprop("instrumentation/nav[0]/frequencies/selected-mhz"); + var nav2=getprop("instrumentation/nav[1]/frequencies/selected-mhz"); + if(freq == nav1 or freq == nav2) return 1; + return 0; } # another predicate for the draw controller - var get_course_by_freq = func(freq) { - if (freq == getprop("instrumentation/nav[0]/frequencies/selected-mhz")) - return getprop("instrumentation/nav[0]/radials/selected-deg"); - else - return getprop("instrumentation/nav[1]/radials/selected-deg"); + var get_course_by_freq = func(freq) + { + if(freq == getprop("instrumentation/nav[0]/frequencies/selected-mhz")) + return getprop("instrumentation/nav[0]/radials/selected-deg"); + else + return getprop("instrumentation/nav[1]/radials/selected-deg"); } - var get_current_position = func { - return [ - me.aircraft_source.get_lat(), me.aircraft_source.get_lon() - ]; + var get_current_position = func + { + return [me.aircraft_source.get_lat(), me.aircraft_source.get_lon()]; } # a hash with controller callbacks, will be passed onto draw routines to customize behavior/appearance @@ -507,11 +481,13 @@ var NavDisplay = { # so we need some simple way to communicate between frontend<->backend until we have real controllers # for now, a single controller hash is shared by most layers - unsupported callbacks are simply ignored by the draw files # - var controller = { query_range: func get_range(), - is_tuned:is_tuned, - get_tuned_course:get_course_by_freq, - get_position: get_current_position, - }; + var controller = + { + query_range: func get_range(), + is_tuned:is_tuned, + get_tuned_course:get_course_by_freq, + get_position: get_current_position, + }; ### # set up various layers, controlled via callbacks in the controller hash @@ -523,69 +499,66 @@ var NavDisplay = { me.layers={}; # storage container for all ND specific layers # look up all required layers as specified per the NDStyle hash and do the initial setup for event handling - foreach(var layer; me.nd_style.layers) { - print("newMFD(): Setting up ND layer:", layer.name); - # huge hack for the alt-arc, which is not rendered as a map group, but directly as part of the toplevel ND group - var render_target = (!contains(layer,'not_a_map') or !layer.not_a_map) ? me.map : me.nd; - var the_layer = me.layers[layer.name] = canvas.MAP_LAYERS[layer.name].new( render_target, layer.name, controller ); + foreach(var layer; me.nd_style.layers) + { + # huge hack for the alt-arc, which is not rendered as a map group, but directly as part of the toplevel ND group + var render_target = (!contains(layer,'not_a_map') or !layer.not_a_map) ? me.map : me.nd; + var the_layer = me.layers[layer.name] = canvas.MAP_LAYERS[layer.name].new( render_target, layer.name, controller ); - # now register all layer specific notification listeners and their corresponding update predicate/callback - # pass the ND instance and the layer handle to the predicate when it is called - # so that it can directly access the ND instance and its own layer (without having to know the layer's name) + # now register all layer specific notification listeners and their corresponding update predicate/callback + # pass the ND instance and the layer handle to the predicate when it is called + # so that it can directly access the ND instance and its own layer (without having to know the layer's name) - var event_handler = make_event_handler(layer.predicate, the_layer); - foreach(var event; layer.update_on) { - # print("Setting up subscription:", event, " for ", layer.name, " handler id:", id(event_handler) ); - me.listen_switch(event, event_handler ) ; - } # foreach event subscription - # and now update/init each layer once by calling its update predicate for initialization - event_handler(); - } # foreach layer - - print("navdisplay.mfd:ND layer setup completed"); + var event_handler = make_event_handler(layer.predicate, the_layer); + foreach(var event; layer.update_on) + { + me.listen_switch(event, event_handler ) ; + } # foreach event subscription + # and now update/init each layer once by calling its update predicate for initialization + event_handler(); + } # foreach layer # start the update timer, which makes sure that the update() will be called me.update_timer.start(); - - # next, radio & autopilot & listeners - # TODO: move this to .init field in layers hash or to model files - foreach(var n; var radios = [ "instrumentation/nav/frequencies/selected-mhz", + # next, radio & autopilot & listeners + # TODO: move this to .init field in layers hash or to model files + foreach(var n; var radios = [ "instrumentation/nav/frequencies/selected-mhz", "instrumentation/nav[1]/frequencies/selected-mhz"]) - me.listen(n, func() { - me.drawvor(); - 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()) { - 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! - } - }); - me.listen("/autopilot/route-manager/current-wp", func(activeWp) { - canvas.updatewp( activeWp.getValue() ); - }); - + me.listen(n, func() + { +# me.drawvor(); +# 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()) + { + setprop(me.get_full_switch_path('toggle_display_mode'),getprop(me.get_full_switch_path('toggle_display_mode'))); + } + else + { + #me.route_group.removeAllChildren(); # HACK! + } + }); + me.listen("/autopilot/route-manager/current-wp", func(activeWp) + { + canvas.updatewp( activeWp.getValue() ); + }); }, - 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; + in_mode:func(switch, modes) + { + foreach(var m; modes) if(me.get_switch(switch)==m) return 1; return 0; }, - # each model should keep track of when it last got updated, using current lat/lon # in update(), we can then check if the aircraft has traveled more than 0.5-1 nm (depending on selected range) # and update each model accordingly update: func() # FIXME: This stuff is still too aircraft specific, cannot easily be reused by other aircraft { - ## # important constants var m1 = 111132.92; @@ -605,7 +578,7 @@ var NavDisplay = { 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')) { + if(me.get_switch('toggle_true_north')) { me.symbols.truMag.setText("TRU"); var userHdg=userHdgTru; var userTrk=userTrkTru; @@ -616,23 +589,24 @@ var NavDisplay = { } 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(); + var dispLCD = me.get_switch('toggle_display_type') == "LCD"; # this should only ever happen when testing the experimental AI/MP ND driver hash (not critical) if (!userHdg or !userTrk or !userLat or !userLon) { print("aircraft source invalid, returning !"); - return; + return; } - if (me.get_switch('toggle_centered') or me.in_mode('toggle_display_mode', ['PLAN'])) + if(me.in_mode('toggle_display_mode', ['PLAN'])) me.map.setTranslation(512,512); + elsif(me.get_switch('toggle_centered')) + me.map.setTranslation(512,565); 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; @@ -641,23 +615,78 @@ var NavDisplay = { latNm = latlen*M2NM; #60 at equator lonNm = lonlen*M2NM; #60 at equator - me.symbols.windArrow.setRotation((getprop("/environment/wind-from-heading-deg")-userHdgMag)*D2R); + me.symbols.windArrow.setRotation((getprop("/environment/wind-from-heading-deg")-userHdg)*D2R); me.symbols.wind.setText(sprintf("%3.0f / %2.0f",getprop("/environment/wind-from-heading-deg"),getprop("/environment/wind-speed-kt"))); - if ((var navid0=getprop("instrumentation/nav/nav-id"))!=nil ) - me.symbols.vorLId.setText(navid0); - if ((var navid1=getprop("instrumentation/nav[1]/nav-id"))!=nil ) - me.symbols.vorRId.setText(navid1); - if((var nav0dist=getprop("instrumentation/nav/nav-distance"))!=nil ) - me.symbols.dmeLDist.setText(sprintf("%3.1f",nav0dist*0.000539)); - if((var nav1dist=getprop("instrumentation/nav[1]/nav-distance"))!=nil ) - me.symbols.dmeRDist.setText(sprintf("%3.1f",nav1dist*0.000539)); - - me.symbols.range.setText(sprintf("%3.0f",me.rangeNm() )); + if(me.get_switch('toggle_lh_vor_adf') == 1) + { + me.symbols.vorL.setText("VOR L"); + me.symbols.vorL.setColor(0.195,0.96,0.097); + me.symbols.dmeL.setText("DME"); + me.symbols.dmeL.setColor(0.195,0.96,0.097); + if(getprop("instrumentation/nav/in-range")) + me.symbols.vorLId.setText(getprop("instrumentation/nav/nav-id")); + else + me.symbols.vorLId.setText(getprop("instrumentation/nav/frequencies/selected-mhz-fmt")); + me.symbols.vorLId.setColor(0.195,0.96,0.097); + if(getprop("instrumentation/nav/dme-in-range")) + me.symbols.dmeLDist.setText(sprintf("%3.1f",getprop("instrumentation/nav/nav-distance")*0.000539)); + else me.symbols.dmeLDist.setText(" ---"); + me.symbols.dmeLDist.setColor(0.195,0.96,0.097); + } elsif(me.get_switch('toggle_lh_vor_adf') == -1) { + me.symbols.vorL.setText("ADF L"); + me.symbols.vorL.setColor(0,0.6,0.85); + me.symbols.dmeL.setText(""); + me.symbols.dmeL.setColor(0,0.6,0.85); + if((var navident=getprop("instrumentation/adf/ident")) != "") + me.symbols.vorLId.setText(navident); + else me.symbols.vorLId.setText(sprintf("%3d",getprop("instrumentation/adf/frequencies/selected-khz"))); + me.symbols.vorLId.setColor(0,0.6,0.85); + me.symbols.dmeLDist.setText(""); + me.symbols.dmeLDist.setColor(0,0.6,0.85); + } else { + me.symbols.vorL.setText(""); + me.symbols.dmeL.setText(""); + me.symbols.vorLId.setText(""); + me.symbols.dmeLDist.setText(""); + } + if(me.get_switch('toggle_rh_vor_adf') == 1) { + me.symbols.vorR.setText("VOR R"); + me.symbols.vorR.setColor(0.195,0.96,0.097); + me.symbols.dmeR.setText("DME"); + me.symbols.dmeR.setColor(0.195,0.96,0.097); + if(getprop("instrumentation/nav[1]/in-range")) + me.symbols.vorRId.setText(getprop("instrumentation/nav[1]/nav-id")); + else + me.symbols.vorRId.setText(getprop("instrumentation/nav[1]/frequencies/selected-mhz-fmt")); + me.symbols.vorRId.setColor(0.195,0.96,0.097); + if(getprop("instrumentation/nav[1]/dme-in-range")) + me.symbols.dmeRDist.setText(sprintf("%3.1f",getprop("instrumentation/nav[1]/nav-distance")*0.000539)); + else me.symbols.dmeRDist.setText(" ---"); + me.symbols.dmeRDist.setColor(0.195,0.96,0.097); + } elsif(me.get_switch('toggle_rh_vor_adf') == -1) { + me.symbols.vorR.setText("ADF R"); + me.symbols.vorR.setColor(0,0.6,0.85); + me.symbols.dmeR.setText(""); + me.symbols.dmeR.setColor(0,0.6,0.85); + if((var navident=getprop("instrumentation/adf[1]/ident")) != "") + me.symbols.vorRId.setText(navident); + else me.symbols.vorRId.setText(sprintf("%3d",getprop("instrumentation/adf[1]/frequencies/selected-khz"))); + me.symbols.vorRId.setColor(0,0.6,0.85); + me.symbols.dmeRDist.setText(""); + me.symbols.dmeRDist.setColor(0,0.6,0.85); + } else { + me.symbols.vorR.setText(""); + me.symbols.dmeR.setText(""); + me.symbols.vorRId.setText(""); + me.symbols.dmeRDist.setText(""); + } + + me.symbols.range.setText(sprintf("%3.0f",me.rangeNm()/2)); # reposition the map, change heading & range: if(me.in_mode('toggle_display_mode', ['PLAN'])) { - me.symbols.windArrow.hide(); + me.symbols.windArrow.setVisible(!dispLCD); 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")); @@ -668,26 +697,37 @@ var NavDisplay = { 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 + # The set range of the map does not correspond to what we see in-sim!! + me.map._node.getNode("range",1).setDoubleValue(me.rangeNm()/3.2); # avoid this here, use a listener instead + var vhdg_bug = getprop("autopilot/settings/heading-bug-deg"); if(me.in_mode('toggle_display_mode', ['MAP'])) { - me.symbols.rotateComp.setRotation(-userTrk*D2R); - me.symbols.rotateComp2.setRotation(-userTrk*D2R); + me.symbols.HdgBugCRT.setRotation((vhdg_bug-userTrk)*D2R); + me.symbols.HdgBugLCD.setRotation((vhdg_bug-userTrk)*D2R); + me.symbols.TrkBugLCD.setRotation((vhdg_bug-userTrk)*D2R); + me.symbols.selHdgLine.setRotation((vhdg_bug-userTrk)*D2R); + me.symbols.HdgBugCRT2.setRotation((vhdg_bug-userTrk)*D2R); + me.symbols.TrkBugLCD2.setRotation((vhdg_bug-userTrk)*D2R); + me.symbols.selHdgLine2.setRotation((vhdg_bug-userTrk)*D2R); me.symbols.trkInd.setRotation(0); - me.symbols.curHdgPtr.setRotation(userHdg*D2R); - me.symbols.curHdgPtr2.setRotation(userHdg*D2R); - me.map._node.getNode("hdg",1).setDoubleValue(userTrk); + me.symbols.curHdgPtr.setRotation((userHdg-userTrk)*D2R); + me.symbols.curHdgPtr2.setRotation((userHdg-userTrk)*D2R); + me.map._node.getNode("hdg",1).setDoubleValue(userTrkTru); me.symbols.compass.setRotation(-userTrk*D2R); me.symbols.compassApp.setRotation(-userTrk*D2R); me.symbols.hdgTrk.setText("TRK"); } if(me.in_mode('toggle_display_mode', ['APP','VOR'])) { - me.symbols.rotateComp.setRotation(-userHdg*D2R); - me.symbols.rotateComp2.setRotation(-userHdg*D2R); + me.symbols.HdgBugCRT.setRotation((vhdg_bug-userHdg)*D2R); + me.symbols.HdgBugLCD.setRotation((vhdg_bug-userHdg)*D2R); + me.symbols.selHdgLine.setRotation((vhdg_bug-userHdg)*D2R); + me.symbols.HdgBugCRT2.setRotation((vhdg_bug-userHdg)*D2R); + me.symbols.HdgBugLCD2.setRotation((vhdg_bug-userHdg)*D2R); + me.symbols.selHdgLine2.setRotation((vhdg_bug-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.curHdgPtr.setRotation(0); + me.symbols.curHdgPtr2.setRotation(0); + me.map._node.getNode("hdg",1).setDoubleValue(userHdgTru); me.symbols.compass.setRotation(-userHdg*D2R); me.symbols.compassApp.setRotation(-userHdg*D2R); me.symbols.hdgTrk.setText("HDG"); @@ -727,31 +767,127 @@ var NavDisplay = { } 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); + var magVar = getprop("environment/magnetic-variation-deg"); + if(me.in_mode('toggle_display_mode', ['APP','MAP','VOR','PLAN'])) + { + if(getprop("instrumentation/nav/heading-deg") != nil) + var nav0hdg=getprop("instrumentation/nav/heading-deg") - userHdg - magVar; + if(getprop("instrumentation/nav[1]/heading-deg") != nil) + var nav1hdg=getprop("instrumentation/nav[1]/heading-deg") - userHdg - magVar; + var adf0hdg=getprop("instrumentation/adf/indicated-bearing-deg"); + var adf1hdg=getprop("instrumentation/adf[1]/indicated-bearing-deg"); + if(!me.get_switch('toggle_centered')) + { + if(me.in_mode('toggle_display_mode', ['PLAN'])) + me.symbols.trkInd.hide(); + else + me.symbols.trkInd.show(); + if((getprop("instrumentation/nav/in-range") and me.get_switch('toggle_lh_vor_adf') == 1)) { + me.symbols.staFromL.setVisible(staPtrVis); + me.symbols.staToL.setVisible(staPtrVis); + me.symbols.staFromL.setColor(0.195,0.96,0.097); + me.symbols.staToL.setColor(0.195,0.96,0.097); + me.symbols.staFromL.setRotation((nav0hdg+180)*D2R); + me.symbols.staToL.setRotation(nav0hdg*D2R); + } + elsif(getprop("instrumentation/adf/in-range") and (me.get_switch('toggle_lh_vor_adf') == -1)) { + me.symbols.staFromL.setVisible(staPtrVis); + me.symbols.staToL.setVisible(staPtrVis); + me.symbols.staFromL.setColor(0,0.6,0.85); + me.symbols.staToL.setColor(0,0.6,0.85); + me.symbols.staFromL.setRotation((adf0hdg+180)*D2R); + me.symbols.staToL.setRotation(adf0hdg*D2R); + } else { + me.symbols.staFromL.hide(); + me.symbols.staToL.hide(); + } + if((getprop("instrumentation/nav[1]/in-range") and me.get_switch('toggle_rh_vor_adf') == 1)) { + me.symbols.staFromR.setVisible(staPtrVis); + me.symbols.staToR.setVisible(staPtrVis); + me.symbols.staFromR.setColor(0.195,0.96,0.097); + me.symbols.staToR.setColor(0.195,0.96,0.097); + me.symbols.staFromR.setRotation((nav1hdg+180)*D2R); + me.symbols.staToR.setRotation(nav1hdg*D2R); + } elsif(getprop("instrumentation/adf[1]/in-range") and (me.get_switch('toggle_rh_vor_adf') == -1)) { + me.symbols.staFromR.setVisible(staPtrVis); + me.symbols.staToR.setVisible(staPtrVis); + me.symbols.staFromR.setColor(0,0.6,0.85); + me.symbols.staToR.setColor(0,0.6,0.85); + me.symbols.staFromR.setRotation((adf1hdg+180)*D2R); + me.symbols.staToR.setRotation(adf1hdg*D2R); + } else { + me.symbols.staFromR.hide(); + me.symbols.staToR.hide(); + } + me.symbols.staFromL2.hide(); + me.symbols.staToL2.hide(); + me.symbols.staFromR2.hide(); + me.symbols.staToR2.hide(); + me.symbols.curHdgPtr2.hide(); + me.symbols.HdgBugCRT2.hide(); + me.symbols.TrkBugLCD2.hide(); + me.symbols.HdgBugLCD2.hide(); + me.symbols.selHdgLine2.hide(); + me.symbols.curHdgPtr.setVisible(staPtrVis); + me.symbols.TrkBugLCD.hide(); + me.symbols.HdgBugCRT.setVisible(staPtrVis and !dispLCD); + me.symbols.HdgBugLCD.setVisible(staPtrVis and dispLCD); + me.symbols.selHdgLine.setVisible(staPtrVis); + } else { + me.symbols.trkInd.hide(); + if((getprop("instrumentation/nav/in-range") and me.get_switch('toggle_lh_vor_adf') == 1)) { + me.symbols.staFromL2.setVisible(staPtrVis); + me.symbols.staToL2.setVisible(staPtrVis); + me.symbols.staFromL2.setColor(0.195,0.96,0.097); + me.symbols.staToL2.setColor(0.195,0.96,0.097); + me.symbols.staFromL2.setRotation((nav0hdg+180)*D2R); + me.symbols.staToL2.setRotation(nav0hdg*D2R); + } elsif(getprop("instrumentation/adf/in-range") and (me.get_switch('toggle_lh_vor_adf') == -1)) { + me.symbols.staFromL2.setVisible(staPtrVis); + me.symbols.staToL2.setVisible(staPtrVis); + me.symbols.staFromL2.setColor(0,0.6,0.85); + me.symbols.staToL2.setColor(0,0.6,0.85); + me.symbols.staFromL2.setRotation((adf0hdg+180)*D2R); + me.symbols.staToL2.setRotation(adf0hdg*D2R); + } else { + me.symbols.staFromL2.hide(); + me.symbols.staToL2.hide(); + } + if((getprop("instrumentation/nav[1]/in-range") and me.get_switch('toggle_rh_vor_adf') == 1)) { + me.symbols.staFromR2.setVisible(staPtrVis); + me.symbols.staToR2.setVisible(staPtrVis); + me.symbols.staFromR2.setColor(0.195,0.96,0.097); + me.symbols.staToR2.setColor(0.195,0.96,0.097); + me.symbols.staFromR2.setRotation((nav1hdg+180)*D2R); + me.symbols.staToR2.setRotation(nav1hdg*D2R); + } elsif(getprop("instrumentation/adf[1]/in-range") and (me.get_switch('toggle_rh_vor_adf') == -1)) { + me.symbols.staFromR2.setVisible(staPtrVis); + me.symbols.staToR2.setVisible(staPtrVis); + me.symbols.staFromR2.setColor(0,0.6,0.85); + me.symbols.staToR2.setColor(0,0.6,0.85); + me.symbols.staFromR2.setRotation((adf1hdg+180)*D2R); + me.symbols.staToR2.setRotation(adf1hdg*D2R); + } else { + me.symbols.staFromR2.hide(); + me.symbols.staToR2.hide(); + } + me.symbols.staFromL.hide(); + me.symbols.staToL.hide(); + me.symbols.staFromR.hide(); + me.symbols.staToR.hide(); + me.symbols.curHdgPtr.hide(); + me.symbols.HdgBugCRT.hide(); + me.symbols.TrkBugLCD.hide(); + me.symbols.HdgBugLCD.hide(); + me.symbols.selHdgLine.hide(); + me.symbols.curHdgPtr2.setVisible(staPtrVis); + me.symbols.TrkBugLCD2.hide(); + me.symbols.HdgBugCRT2.setVisible(staPtrVis and !dispLCD); + me.symbols.HdgBugLCD2.setVisible(staPtrVis and dispLCD); + me.symbols.selHdgLine2.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')); @@ -776,27 +912,6 @@ var NavDisplay = { me.symbols.altArc.hide(); } - ## 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); - 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); - me.symbols.staFromL2.setRotation((nav0hdg-userHdgMag+180)*D2R); - } - if (var nav0hdg=getprop("instrumentation/nav/heading-deg") != nil) { - me.symbols.staToL.setRotation((nav0hdg-userHdgMag)*D2R); - 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); - 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 @@ -804,19 +919,14 @@ var NavDisplay = { ## will be updated at frame rate too - wasteful ... (check the performance monitor!) foreach(var feature; me.nd_style.features ) { - - # for stuff that always needs to be updated - if (contains(feature.impl, 'common')) feature.impl.common(me); - # conditional stuff - if(!contains(feature.impl, 'predicate')) continue; # no conditional stuff - if ( var result=feature.impl.predicate(me) ) { - # print("Update predicate true for ", feature.id); - feature.impl.is_true(me, result); # pass the result to the predicate - } - else { - # print("Update predicate false for ", feature.id); - feature.impl.is_false( me, result ); # pass the result to the predicate - } + # for stuff that always needs to be updated + if (contains(feature.impl, 'common')) feature.impl.common(me); + # conditional stuff + if(!contains(feature.impl, 'predicate')) continue; # no conditional stuff + if ( var result=feature.impl.predicate(me) ) + feature.impl.is_true(me, result); # pass the result to the predicate + else + feature.impl.is_false( me, result ); # pass the result to the predicate } ## update the status flags shown on the ND (wxr, wpt, arpt, sta) @@ -826,6 +936,5 @@ var NavDisplay = { 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'])); - } -}; +}; \ No newline at end of file diff --git a/Nasal/canvas/map/route.model b/Nasal/canvas/map/route.model index ae43efb07..80df9e496 100644 --- a/Nasal/canvas/map/route.model +++ b/Nasal/canvas/map/route.model @@ -5,9 +5,7 @@ RouteModel.new = func make(LayerModel, RouteModel); RouteModel.init = func { me._view.reset(); if (!getprop("/autopilot/route-manager/active")) - print("Cannot draw route, route manager inactive!") and return; - - print("TODO: route.model is still an empty stub, see route.draw instead"); + return; ## TODO: all the model stuff is still inside the draw file for now, this just ensures that it will be called once foreach(var t; [nil] ) From 7ca8482b07cd776eaff736742a3a523dd3ddc7f0 Mon Sep 17 00:00:00 2001 From: Philosopher Date: Thu, 9 Jan 2014 21:04:36 -0600 Subject: [PATCH 3/6] MapStructure work & (partial) integration In time for 3.0. The API is still not fully complete, and not fully cleaned up, but this is good enough for this release cycle (and it should offer benefit longer term, if not now -- hopefully performance as well). Many thanks to Hooray as well, who has helped prepare things while I could not, and often suggested ideas. --- Nasal/canvas/MapStructure.nas | 361 +++++--- Nasal/canvas/api.nas | 34 +- Nasal/canvas/map.nas | 16 +- Nasal/canvas/map/DME.lcontroller | 32 + Nasal/canvas/map/DME.scontroller | 12 + Nasal/canvas/map/DME.symbol | 35 + Nasal/canvas/map/FIX.lcontroller | 33 + Nasal/canvas/map/FIX.scontroller | 12 + Nasal/canvas/map/FIX.symbol | 30 + Nasal/canvas/map/NDB.lcontroller | 30 + Nasal/canvas/map/NDB.scontroller | 12 + Nasal/canvas/map/NDB.symbol | 18 + Nasal/canvas/map/VOR.lcontroller | 7 +- Nasal/canvas/map/VOR.scontroller | 5 +- Nasal/canvas/map/VOR.symbol | 89 +- Nasal/canvas/map/WPT.lcontroller | 39 + Nasal/canvas/map/WPT.scontroller | 12 + Nasal/canvas/map/WPT.symbol | 34 + .../map/{airport-nd.draw => airports-nd.draw} | 0 Nasal/canvas/map/navdisplay.mfd | 829 ++++++++++-------- Nasal/canvas/map/route.model | 4 +- 21 files changed, 1086 insertions(+), 558 deletions(-) create mode 100644 Nasal/canvas/map/DME.lcontroller create mode 100644 Nasal/canvas/map/DME.scontroller create mode 100644 Nasal/canvas/map/DME.symbol create mode 100644 Nasal/canvas/map/FIX.lcontroller create mode 100644 Nasal/canvas/map/FIX.scontroller create mode 100644 Nasal/canvas/map/FIX.symbol create mode 100644 Nasal/canvas/map/NDB.lcontroller create mode 100644 Nasal/canvas/map/NDB.scontroller create mode 100644 Nasal/canvas/map/NDB.symbol create mode 100644 Nasal/canvas/map/WPT.lcontroller create mode 100644 Nasal/canvas/map/WPT.scontroller create mode 100644 Nasal/canvas/map/WPT.symbol rename Nasal/canvas/map/{airport-nd.draw => airports-nd.draw} (100%) diff --git a/Nasal/canvas/MapStructure.nas b/Nasal/canvas/MapStructure.nas index 5474e9145..2ec29eb80 100644 --- a/Nasal/canvas/MapStructure.nas +++ b/Nasal/canvas/MapStructure.nas @@ -1,3 +1,123 @@ +var dump_obj = func(m) { + var h = {}; + foreach (var k; keys(m)) + if (k != "parents") + h[k] = m[k]; + debug.dump(h); +}; + +## +# must be either of: +# 1) draw* callback, 2) SVG filename, 3) Drawable class (with styling/LOD support) +var SymbolDrawable = { + new: func() { + }, +}; + +## wrapper for each element +## i.e. keeps the canvas and texture map coordinates +var CachedElement = { + new: func(canvas_path, name, source, offset) { + var m = {parents:[CachedElement] }; + m.canvas_src = canvas_path; + m.name = name; + m.source = source; + m.offset = offset; + return m; + }, # new() + render: func(group) { + # create a raster image child in the render target/group + return + group.createChild("image", me.name) + .setFile( me.canvas_src ) + # TODO: fix .setSourceRect() to accept a single vector for coordinates ... + .setSourceRect(left:me.source[0],top:me.source[1],right:me.source[2],bottom:me.source[3] , normalized:0) + .setTranslation(me.offset); # FIXME: make sure this stays like this and isn't overridden + }, # render() +}; # of CachedElement + +var SymbolCache = { + # We can draw symbols either with left/top, centered, + # or right/bottom alignment. Specify two in a vector + # to mix and match, e.g. left/centered would be + # [SymbolCache.DRAW_LEFT_TOP,SymbolCache.DRAW_CENTERED] + DRAW_LEFT_TOP: 0.0, + DRAW_CENTERED: 0.5, + DRAW_RIGHT_BOTTOM: 1.0, + new: func(dim...) { + var m = { parents:[SymbolCache] }; + # to keep track of the next free caching spot (in px) + m.next_free = [0, 0]; + # to store each type of symbol + m.dict = {}; + if (size(dim) == 1 and typeof(dim[0]) == 'vector') + dim = dim[0]; + # Two sizes: canvas and symbol + if (size(dim) == 2) { + var canvas_x = var canvas_y = dim[0]; + var image_x = var image_y = dim[1]; + # Two widths (canvas and symbol) and then height/width ratio + } else if (size(dim) == 3) { + var (canvas_x,image_x,ratio) = dim; + var canvas_y = canvas_x * ratio; + var image_y = image_x * ratio; + # Explicit canvas and symbol widths/heights + } else if (size(dim) == 4) { + var (canvas_x,canvas_y,image_x,image_y) = dim; + } + m.canvas_sz = [canvas_x, canvas_y]; + m.image_sz = [image_x, image_y]; + + # allocate a canvas + m.canvas_texture = canvas.new( { + "name": "SymbolCache"~canvas_x~'x'~canvas_y, + "size": m.canvas_sz, + "view": m.canvas_sz, + "mipmapping": 1 + }); + + # add a placement + m.canvas_texture.addPlacement( {"type": "ref"} ); + + return m; + }, + add: func(name, callback, draw_mode=0) { + if (typeof(draw_mode) == 'scalar') + var draw_mode0 = var draw_mode1 = draw_mode; + else var (draw_mode0,draw_mode1) = draw_mode; + # get canvas texture that we use as cache + # get next free spot in texture (column/row) + # run the draw callback and render into a group + var gr = me.canvas_texture.createGroup(); + gr.setTranslation( me.next_free[0] + me.image_sz[0]*draw_mode0, + me.next_free[1] + me.image_sz[1]*draw_mode1); + #settimer(func debug.dump ( gr.getTransformedBounds() ), 0); # XXX: these are only updated when rendered + #debug.dump ( gr.getTransformedBounds() ); + gr.update(); # apparently this doesn't result in sane output from .getTransformedBounds() either + #debug.dump ( gr.getTransformedBounds() ); + # draw the symbol + callback(gr); + # get the bounding box, i.e. coordinates for texture map, or use the .setTranslation() params + var coords = me.next_free~me.next_free; + foreach (var i; [0,1]) + coords[i+2] += me.image_sz[i]; + # get the offset we used to position correctly in the bounds of the canvas + var offset = [me.image_sz[0]*draw_mode0, me.image_sz[1]*draw_mode1]; + # store texture map coordinates in lookup map using the name as identifier + me.dict[name] = CachedElement.new(me.canvas_texture.getPath(), name, coords, offset ); + # update next free position in cache (column/row) + me.next_free[0] += me.image_sz[0]; + if (me.next_free[0] >= me.canvas_sz[0]) + { me.next_free[0] = 0; me.next_free[1] += me.image_sz[1] } + if (me.next_free[1] >= me.canvas_sz[1]) + die("SymbolCache: ran out of space after adding '"~name~"'"); + }, # add() + get: func(name) { + if(!contains(me.dict,name)) die("No SymbolCache entry for key:"~ name); + return me.dict[name]; + }, # get() +}; + var Symbol = { # Static/singleton: registry: {}, @@ -29,7 +149,7 @@ Symbol.Controller = { # Static/singleton: registry: {}, add: func(type, class) - registry[type] = class, + me.registry[type] = class, get: func(type) if ((var class = me.registry[type]) == nil) die("unknown type '"~type~"'"); @@ -58,13 +178,17 @@ var getpos_fromghost = func(positioned_g) # (geo.Coord and positioned ghost currently) Symbol.Controller.getpos = func(obj) { if (typeof(obj) == 'ghost') - if (ghosttype(obj) == 'positioned' or ghosttype(obj) == 'Navaid') + if (ghosttype(obj) == 'positioned' or ghosttype(obj) == 'Navaid' or ghosttype(obj)=='Fix' or ghosttype(obj)=='flightplan-leg') return getpos_fromghost(obj); else die("bad ghost of type '"~ghosttype(obj)~"'"); if (typeof(obj) == 'hash') if (isa(obj, geo.Coord)) return obj.latlon(); + if (contains(obj,'lat') and contains(obj,'lon')) + return [obj.lat, obj.lon]; + + debug.dump(obj); die("no suitable getpos() found! Of type: "~typeof(obj)); }; @@ -82,38 +206,17 @@ var DotSym = { element_id: nil, # Static/singleton: makeinstance: func(name, hash) { - assert_ms(hash, - "element_type", # type of Canvas element - #"element_id", # optional Canvas id - #"init", # initialize routine - "draw", # init/update routine - #getpos", # get position from model in [x_units,y_units] (optional) - ); - hash.parents = [DotSym]; + if (!isa(hash, DotSym)) + die("OOP error"); + #assert_ms(hash, + # "element_type", # type of Canvas element + # #"element_id", # optional Canvas id + # #"init", # initialize routine + # "draw", # init/update routine + # #getpos", # get position from model in [x_units,y_units] (optional) + #); return Symbol.add(name, hash); }, - readinstance: func(file, name=nil) { - #print(file); - if (name == nil) - var name = split("/", file)[-1]; - if (substr(name, size(name)-4) == ".draw") - name = substr(name, 0, size(name)-5); - var code = io.readfile(file); - var code = call(compile, [code], var err=[]); - if (size(err)) { - if (substr(err[0], 0, 12) == "Parse error:") { # hack around Nasal feature - var e = split(" at line ", err[0]); - if (size(e) == 2) - err[0] = string.join("", [e[0], "\n at ", file, ", line ", e[1], "\n "]); - } - for (var i = 1; (var c = caller(i)) != nil; i += 1) - err ~= subvec(c, 2, 2); - debug.printerror(err); - return; - } - call(code, nil, nil, var hash = { parents:[DotSym] }); - me.makeinstance(name, hash); - }, # For the instances returned from makeinstance: # @param group The Canvas group to add this to. # @param model A correct object (e.g. positioned ghost) as @@ -132,6 +235,10 @@ var DotSym = { ), }; if (m.controller != nil) { + #print("Creating controller"); + temp = m.controller.new(m.model,m); + if (temp != nil) + m.controller = temp; #print("Initializing controller"); m.controller.init(model); } @@ -281,93 +388,117 @@ SymbolLayer.Controller = { die("searchCmd() not implemented for this SymbolLayer.Controller type!"), }; # of SymbolLayer.Controller -settimer(func { -Map.Controller = { -# Static/singleton: - registry: {}, - add: func(type, class) - me.registry[type] = class, - get: func(type) - if ((var class = me.registry[type]) == nil) - die("unknown type '"~type~"'"); - else return class, - # Calls corresonding controller constructor - # @param map The #SymbolMap this controller is responsible for. - new: func(type, layer, arg...) - return call((var class = me.get(type)).new, [map]~arg, class), +var AnimatedLayer = { }; -####### LOAD FILES ####### -#print("loading files"); -(func { - var FG_ROOT = getprop("/sim/fg-root"); - var load = func(file, name) { - #print(file); - if (name == nil) - var name = split("/", file)[-1]; - if (substr(name, size(name)-4) == ".draw") - name = substr(name, 0, size(name)-5); - #print("reading file"); - var code = io.readfile(file); - #print("compiling file"); - # This segfaults for some reason: - #var code = call(compile, [code], var err=[]); - var code = call(func compile(code, file), [code], var err=[]); - if (size(err)) { - #print("handling error"); - if (substr(err[0], 0, 12) == "Parse error:") { # hack around Nasal feature - var e = split(" at line ", err[0]); - if (size(e) == 2) - err[0] = string.join("", [e[0], "\n at ", file, ", line ", e[1], "\n "]); +var CompassLayer = { +}; + +var AltitudeArcLayer = { +}; + +load_MapStructure = func { + Map.Controller = { + # Static/singleton: + registry: {}, + add: func(type, class) + me.registry[type] = class, + get: func(type) + if ((var class = me.registry[type]) == nil) + die("unknown type '"~type~"'"); + else return class, + # Calls corresonding controller constructor + # @param map The #SymbolMap this controller is responsible for. + new: func(type, layer, arg...) + return call((var class = me.get(type)).new, [map]~arg, class), + }; + + ####### LOAD FILES ####### + #print("loading files"); + (func { + var FG_ROOT = getprop("/sim/fg-root"); + var load = func(file, name) { + #print(file); + if (name == nil) + var name = split("/", file)[-1]; + if (substr(name, size(name)-4) == ".draw") + name = substr(name, 0, size(name)-5); + #print("reading file"); + var code = io.readfile(file); + #print("compiling file"); + # This segfaults for some reason: + #var code = call(compile, [code], var err=[]); + var code = call(func compile(code, file), [code], var err=[]); + if (size(err)) { + #print("handling error"); + if (substr(err[0], 0, 12) == "Parse error:") { # hack around Nasal feature + var e = split(" at line ", err[0]); + if (size(e) == 2) + err[0] = string.join("", [e[0], "\n at ", file, ", line ", e[1], "\n "]); + } + for (var i = 1; (var c = caller(i)) != nil; i += 1) + err ~= subvec(c, 2, 2); + debug.printerror(err); + return; } - for (var i = 1; (var c = caller(i)) != nil; i += 1) - err ~= subvec(c, 2, 2); - debug.printerror(err); - return; + #print("calling code"); + call(code, nil, nil, var hash = {}); + #debug.dump(keys(hash)); + return hash; + }; + + var load_deps = func(name) { + load(FG_ROOT~"/Nasal/canvas/map/"~name~".lcontroller", name); + load(FG_ROOT~"/Nasal/canvas/map/"~name~".symbol", name); + load(FG_ROOT~"/Nasal/canvas/map/"~name~".scontroller", name); } - #print("calling code"); - call(code, nil, nil, var hash = {}); - #debug.dump(keys(hash)); - return hash; - }; - load(FG_ROOT~"/Nasal/canvas/map/VOR.lcontroller", "VOR"); - DotSym.readinstance(FG_ROOT~"/Nasal/canvas/map/VOR.symbol", "VOR"); - load(FG_ROOT~"/Nasal/canvas/map/VOR.scontroller", "VOR"); - load(FG_ROOT~"/Nasal/canvas/map/aircraftpos.controller", "VOR"); -})(); -#print("finished loading files"); -####### TEST SYMBOL ####### -if (0) -settimer(func { - if (caller(0)[0] != globals.canvas) - return call(caller(0)[1], arg, nil, globals.canvas); + foreach( var name; ['VOR','FIX','NDB','DME','WPT'] ) + load_deps( name ); + load(FG_ROOT~"/Nasal/canvas/map/aircraftpos.controller", name); - print("Running MapStructure test code"); - var TestCanvas = canvas.new({ - "name": "Map Test", - "size": [1024, 1024], - "view": [1024, 1024], - "mipmapping": 1 - }); - var dlg = canvas.Window.new([400, 400], "dialog"); - dlg.setCanvas(TestCanvas); - var TestMap = TestCanvas.createGroup().createChild("map"); # we should not directly use a canvas here, but instead a LayeredMap.new() - TestMap.addLayer(factory: SymbolLayer, type_arg: "VOR"); # the ID should be also exposed in the property tree for each group (layer), i.e. better debugging - # Center the map's origin: - TestMap.setTranslation(512,512); # FIXME: don't hardcode these values, but read in canvas texture dimensions, otherwise it will break once someone uses non 1024x1024 textures ... - # Initialize a range (TODO: LayeredMap.Controller): - TestMap.set("range", 100); - # Little cursor of current position: - TestMap.createChild("path").rect(-5,-5,10,10).setColorFill(1,1,1).setColor(0,1,0); - # And make it move with our aircraft: - TestMap.setController("Aircraft position"); # from aircraftpos.controller - dlg.del = func() { - TestMap.del(); - # call inherited 'del' - delete(me, "del"); - me.del(); - }; -}, 1); -}, 0); # end ugly module init timer hack + ### + # set up a cache for 32x32 symbols + var SymbolCache32x32 = SymbolCache.new(1024,32); + var drawVOR = func(color, width=3) return func(group) { + # print("drawing vor"); + var bbox = group.createChild("path") + .moveTo(-15,0) + .lineTo(-7.5,12.5) + .lineTo(7.5,12.5) + .lineTo(15,0) + .lineTo(7.5,-12.5) + .lineTo(-7.5,-12.5) + .close() + .setStrokeLineWidth(width) + .setColor( color ); + # debug.dump( bbox.getBoundingBox() ); + }; + + var cachedVOR1 = SymbolCache32x32.add( "VOR-BLUE", drawVOR( color:[0, 0.6, 0.85], width:3), SymbolCache.DRAW_CENTERED ); + var cachedVOR2 = SymbolCache32x32.add( "VOR-RED" , drawVOR( color:[1.0, 0, 0], width: 3), SymbolCache.DRAW_CENTERED ); + var cachedVOR3 = SymbolCache32x32.add( "VOR-GREEN" , drawVOR( color:[0, 1, 0], width: 3), SymbolCache.DRAW_CENTERED ); + var cachedVOR4 = SymbolCache32x32.add( "VOR-WHITE" , drawVOR( color:[1, 1, 1], width: 3), SymbolCache.DRAW_CENTERED ); + + # STRESS TEST + if (0) { + for(var i=0;i <= 1024/32*4 - 4; i+=1) + SymbolCache32x32.add( "VOR-YELLOW"~i , drawVOR( color:[1, 1, 0], width: 3) ); + + var dlg = canvas.Window.new([640,320],"dialog"); + var my_canvas = dlg.createCanvas().setColorBackground(1,1,1,1); + var root = my_canvas.createGroup(); + + SymbolCache32x32.get(name:"VOR-BLUE").render( group: root ).setGeoPosition(getprop("/position/latitude-deg"),getprop("/position/longitude-deg")); + } + + })(); + #print("finished loading files"); + ####### TEST SYMBOL ####### + + canvas.load_MapStructure = func; + +}; # load_MapStructure + +setlistener("/nasal/canvas/loaded", load_MapStructure); # end ugly module init listener hack diff --git a/Nasal/canvas/api.nas b/Nasal/canvas/api.nas index 8cccc71f8..bd42c061d 100644 --- a/Nasal/canvas/api.nas +++ b/Nasal/canvas/api.nas @@ -462,47 +462,45 @@ var Map = { addLayer: func(factory, type_arg=nil, priority=nil) { if (!contains(me, "layers")) - me.layers = []; + me.layers = {}; + + if(contains(me.layers, type_arg)) + print("addLayer() warning: overwriting existing layer:", type_arg); + + # print("addLayer():", type_arg); # Argument handling if (type_arg != nil) var type = factory.get(type_arg); else var type = factory; + me.layers[type_arg]= type.new(me); if (priority == nil) priority = type.df_priority; - append(me.layers, [type.new(me), priority]); if (priority != nil) - me._sort_priority(); + me.layers[type_arg].setInt("z-index", priority); return me; }, - setPos: func(lat,lon,hdg=nil) + getLayer: func(type_arg) me.layers[type_arg], + setPos: func(lat, lon, hdg=nil, range=nil) { me.set("ref-lat", lat); me.set("ref-lon", lon); if (hdg != nil) me.set("hdg", hdg); - - # me.map.set("range", 100); + if (range != nil) + me.set("range", range); }, # Update each layer on this Map. Called by # me.controller. update: func { - foreach (var l; me.layers) - call(l[0].update, arg, l[0]); + foreach (var l; keys(me.layers)) { + var layer = me.layers[l]; + call(layer.update, arg, layer); + } return me; }, -# private: - _sort_priority: func() - { - me.layers = sort(me.layers, me._sort_cmp); - forindex (var i; me.layers) - me.layers[i].set("z-index", i); - }, - _sort_cmp: func(a,b) { - a[1] != b[1] and a[1] != nil and b[1] != nil and (a[1] < b[1] ? -1 : 1) - }, }; # Text diff --git a/Nasal/canvas/map.nas b/Nasal/canvas/map.nas index 1b91fe76b..187619913 100644 --- a/Nasal/canvas/map.nas +++ b/Nasal/canvas/map.nas @@ -429,10 +429,16 @@ var files_with = func(ext) { } return results; } -foreach(var ext; var extensions = ['.draw','.model','.layer']) - load_modules(files_with(ext)); +setlistener("/nasal/canvas/loaded", func { + foreach(var ext; var extensions = ['.draw','.model','.layer']) + load_modules(files_with(ext)); + + if (contains(canvas,"load_MapStructure")) + load_MapStructure(); + + # canvas.MFD = {EFIS:}; # where we'll be storing all MFDs + # TODO: should be inside a separate subfolder, i.e. canvas/map/mfd + load_modules( files_with('.mfd'), 'canvas' ); +}); -# canvas.MFD = {EFIS:}; # where we'll be storing all MFDs -# TODO: should be inside a separate subfolder, i.e. canvas/map/mfd -load_modules( files_with('.mfd'), 'canvas' ); diff --git a/Nasal/canvas/map/DME.lcontroller b/Nasal/canvas/map/DME.lcontroller new file mode 100644 index 000000000..b2d3c714e --- /dev/null +++ b/Nasal/canvas/map/DME.lcontroller @@ -0,0 +1,32 @@ +# Class things: +var name = 'DME'; +var parents = [SymbolLayer.Controller]; +var __self__ = caller(0)[0]; +SymbolLayer.Controller.add(name, __self__); +SymbolLayer.add(name, { + parents: [SymbolLayer], + type: name, # Symbol type + df_controller: __self__, # controller to use by default -- this one +}); +var a_instance = nil; +var new = func(layer) { + var m = { + parents: [__self__], + layer: layer, + listeners: [], + query_range_nm: 25, + query_type:'dme', + }; + __self__.a_instance = m; + return m; +}; +var del = func() { + foreach (var l; me.listeners) + removelistener(l); +}; + +var searchCmd = func { + #print("Running query:", me.query_type); + return positioned.findWithinRange(me.query_range_nm, me.query_type); # the range should also be exposed, it will typically be controlled via a GUI widget or NavDisplay switch +}; + diff --git a/Nasal/canvas/map/DME.scontroller b/Nasal/canvas/map/DME.scontroller new file mode 100644 index 000000000..0e54d4dc2 --- /dev/null +++ b/Nasal/canvas/map/DME.scontroller @@ -0,0 +1,12 @@ +# Class things: +var name = 'DME'; +var parents = [Symbol.Controller]; +var __self__ = caller(0)[0]; +Symbol.Controller.add(name, __self__); +Symbol.registry[ name ].df_controller = __self__; +var new = func(model) ; # this controller doesn't need an instance +var LayerController = SymbolLayer.Controller.registry[ name ]; +var isActive = func(model) LayerController.a_instance.isActive(model); +var is_tuned = func() + die( name~".scontroller.is_tuned /MUST/ be provided by implementation" ); + diff --git a/Nasal/canvas/map/DME.symbol b/Nasal/canvas/map/DME.symbol new file mode 100644 index 000000000..fbf523227 --- /dev/null +++ b/Nasal/canvas/map/DME.symbol @@ -0,0 +1,35 @@ +# Class things: +var name = 'DME'; +var parents = [DotSym]; +var __self__ = caller(0)[0]; +DotSym.makeinstance( name, __self__ ); + +var element_type = "group"; # we want a group, becomes "me.element" +var icon_dme = nil; + +var draw = func { + # Init + if (me.icon_dme == nil) { + me.icon_dme = me.element.createChild("path") + .moveTo(-15,0) + .line(-12.5,-7.5) + .line(7.5,-12.5) + .line(12.5,7.5) + .lineTo(7.5,-12.5) + .line(12.5,-7.5) + .line(7.5,12.5) + .line(-12.5,7.5) + .lineTo(15,0) + .lineTo(7.5,12.5) + .vert(14.5) + .horiz(-14.5) + .vert(-14.5) + .close() + .setStrokeLineWidth(3); + } + if (me.controller != nil and me.controller.is_tuned(me.model.frequency/100)) + me.icon_dme.setColor(0,1,0); + else + me.icon_dme.setColor(0,0.6,0.85); +}; + diff --git a/Nasal/canvas/map/FIX.lcontroller b/Nasal/canvas/map/FIX.lcontroller new file mode 100644 index 000000000..ed89fb06b --- /dev/null +++ b/Nasal/canvas/map/FIX.lcontroller @@ -0,0 +1,33 @@ +# Class things: +var name = 'FIX'; +var parents = [SymbolLayer.Controller]; +var __self__ = caller(0)[0]; +SymbolLayer.Controller.add(name, __self__); +SymbolLayer.add(name, { + parents: [SymbolLayer], + type: name, # Symbol type + df_controller: __self__, # controller to use by default -- this one +}); +var a_instance = nil; +var new = func(layer) { + var m = { + parents: [__self__], + layer: layer, + listeners: [], + query_range_nm: 25, + query_type:'fix', + }; + __self__.a_instance = m; + return m; +}; +var del = func() { + #print("VOR.lcontroller.del()"); + foreach (var l; me.listeners) + removelistener(l); +}; + +var searchCmd = func { + #print("Running query:", me.query_type); + return positioned.findWithinRange(me.query_range_nm, me.query_type); # the range should also be exposed, it will typically be controlled via a GUI widget or NavDisplay switch +}; + diff --git a/Nasal/canvas/map/FIX.scontroller b/Nasal/canvas/map/FIX.scontroller new file mode 100644 index 000000000..5726f86ab --- /dev/null +++ b/Nasal/canvas/map/FIX.scontroller @@ -0,0 +1,12 @@ +# Class things: +var name = 'FIX'; +var parents = [Symbol.Controller]; +var __self__ = caller(0)[0]; +Symbol.Controller.add(name, __self__); +Symbol.registry[ name ].df_controller = __self__; +var new = func(model) ; # this controller doesn't need an instance +var LayerController = SymbolLayer.Controller.registry[ name ]; +var isActive = func(model) LayerController.a_instance.isActive(model); +var query_range = func() + die( name~".scontroller.query_range /MUST/ be provided by implementation" ); + diff --git a/Nasal/canvas/map/FIX.symbol b/Nasal/canvas/map/FIX.symbol new file mode 100644 index 000000000..c2eee2c11 --- /dev/null +++ b/Nasal/canvas/map/FIX.symbol @@ -0,0 +1,30 @@ +# Class things: +var name = 'FIX'; +var parents = [DotSym]; +var __self__ = caller(0)[0]; +DotSym.makeinstance( name, __self__ ); + +var element_type = "group"; # we want a group, becomes "me.element" +var icon_fix = nil; +var text_fix = nil; + +var draw = func { + if (me.icon_fix != nil) return; + # the fix symbol + me.icon_fix = me.element.createChild("path") + .moveTo(-15,15) + .lineTo(0,-15) + .lineTo(15,15) + .close() + .setStrokeLineWidth(3) + .setColor(0,0.6,0.85) + .setScale(0.5,0.5); # FIXME: do proper LOD handling here - we need to scale according to current texture dimensions vs. original/design dimensions + # the fix label + me.text_fix = me.element.createChild("text") + .setDrawMode( canvas.Text.TEXT ) + .setText(me.model.id) + .setFont("LiberationFonts/LiberationSans-Regular.ttf") + .setFontSize(28) + .setTranslation(5,25); +}; + diff --git a/Nasal/canvas/map/NDB.lcontroller b/Nasal/canvas/map/NDB.lcontroller new file mode 100644 index 000000000..3250fc0f7 --- /dev/null +++ b/Nasal/canvas/map/NDB.lcontroller @@ -0,0 +1,30 @@ +# Class things: +var name = 'NDB'; +var parents = [SymbolLayer.Controller]; +var __self__ = caller(0)[0]; +SymbolLayer.Controller.add(name, __self__); +SymbolLayer.add(name, { + parents: [SymbolLayer], + type: name, # Symbol type + df_controller: __self__, # controller to use by default -- this one +}); +var new = func(layer) { + var m = { + parents: [__self__], + layer: layer, + listeners: [], + query_range_nm: 25, + query_type:'ndb', + }; + return m; +}; +var del = func() { + foreach (var l; me.listeners) + removelistener(l); +}; + +var searchCmd = func { + #print("Running query:", me.query_type); + return positioned.findWithinRange(me.query_range_nm, me.query_type); # the range should also be exposed, it will typically be controlled via a GUI widget or NavDisplay switch +}; + diff --git a/Nasal/canvas/map/NDB.scontroller b/Nasal/canvas/map/NDB.scontroller new file mode 100644 index 000000000..7d1011db0 --- /dev/null +++ b/Nasal/canvas/map/NDB.scontroller @@ -0,0 +1,12 @@ +# Class things: +var name = 'NDB'; +var parents = [Symbol.Controller]; +var __self__ = caller(0)[0]; +Symbol.Controller.add(name, __self__); +Symbol.registry[ name ].df_controller = __self__; +var new = func(model) ; # this controller doesn't need an instance +var LayerController = SymbolLayer.Controller.registry[ name ]; +var isActive = func(model) LayerController.a_instance.isActive(model); +var query_range = func() + die( name~".scontroller.query_range /MUST/ be provided by implementation" ); + diff --git a/Nasal/canvas/map/NDB.symbol b/Nasal/canvas/map/NDB.symbol new file mode 100644 index 000000000..541b4cebb --- /dev/null +++ b/Nasal/canvas/map/NDB.symbol @@ -0,0 +1,18 @@ +# Class things: +var name = 'NDB'; +var parents = [DotSym]; +var __self__ = caller(0)[0]; +DotSym.makeinstance( name, __self__ ); + +var element_type = "group"; # we want a group, becomes "me.element", which we parse a SVG onto +var svg_path = "/gui/dialogs/images/ndb_symbol.svg"; # speaking of path, this is our path to use +var local_svg_path = nil; # track changes in the SVG's path + +var draw = func { + if (me.svg_path == me.local_svg_path) return; + me.element.removeAllChildren(); + me.local_svg_path = me.svg_path; + canvas.parsesvg(me.element, me.svg_path); + me.inited = 1; +}; + diff --git a/Nasal/canvas/map/VOR.lcontroller b/Nasal/canvas/map/VOR.lcontroller index 1f63e4e67..f01f83da9 100644 --- a/Nasal/canvas/map/VOR.lcontroller +++ b/Nasal/canvas/map/VOR.lcontroller @@ -7,6 +7,7 @@ SymbolLayer.add("VOR", { type: "VOR", # Symbol type df_controller: __self__, # controller to use by default -- this one }); +var a_instance = nil; var new = func(layer) { var m = { parents: [__self__], @@ -14,6 +15,7 @@ var new = func(layer) { active_vors: [], navNs: props.globals.getNode("instrumentation").getChildren("nav"), listeners: [], + query_type:'vor', }; setsize(m.active_vors, size(m.navNs)); foreach (var navN; m.navNs) { @@ -24,6 +26,7 @@ var new = func(layer) { } #call(debug.dump, keys(layer)); m.changed_freq(update:0); + __self__.a_instance = m; return m; }; var del = func() { @@ -46,7 +49,7 @@ var changed_freq = func(update=1) { if (update) me.layer.update(); }; var searchCmd = func { - #print("Run query"); - return positioned.findWithinRange(100, 'vor'); # the range should also be exposed, it will typically be controlled via a GUI widget or NavDisplay switch + #print("Running query:", me.query_type); + return positioned.findWithinRange(100, me.query_type); # the range should also be exposed, it will typically be controlled via a GUI widget or NavDisplay switch }; diff --git a/Nasal/canvas/map/VOR.scontroller b/Nasal/canvas/map/VOR.scontroller index b95bcc4f2..65fe8f408 100644 --- a/Nasal/canvas/map/VOR.scontroller +++ b/Nasal/canvas/map/VOR.scontroller @@ -1,10 +1,11 @@ # Class things: var parents = [Symbol.Controller]; var __self__ = caller(0)[0]; +Symbol.Controller.add("VOR", __self__); Symbol.registry["VOR"].df_controller = __self__; var new = func(model) ; # this controller doesn't need an instance -var LayerController = SymbolLayer.registry["VOR"]; -var isActive = func(model) LayerController.isActive(model); +var LayerController = SymbolLayer.Controller.registry["VOR"]; +var isActive = func(model) LayerController.a_instance.isActive(model); var query_range = func() die("VOR.scontroller.query_range /MUST/ be provided by implementation"); diff --git a/Nasal/canvas/map/VOR.symbol b/Nasal/canvas/map/VOR.symbol index aaa259dcc..faf37c206 100644 --- a/Nasal/canvas/map/VOR.symbol +++ b/Nasal/canvas/map/VOR.symbol @@ -1,52 +1,57 @@ -# Read by the DotSym.readinstance; each variable becomes a derived class's member/method +# Class things: +var name = 'VOR'; +var parents = [DotSym]; +var __self__ = caller(0)[0]; +DotSym.makeinstance( name, __self__ ); var element_type = "group"; # we want a group, becomes "me.element" -var inited = 0; # this allows us to track whether draw() is an init() or an update() +var icon_vor = nil; var range_vor = nil; # two elements that get drawn when needed var radial_vor = nil; # if one is nil, the other has to be nil var draw = func { - if (me.inited) { - # Update - if (me.controller.isActive(me.model)) { - if (me.range_vor == nil) { - var rangeNm = me.controller.query_range(); - # print("VOR is tuned:", me.model.id); - var radius = (me.model.range_nm/rangeNm)*345; - me.range_vor = me.element.createChild("path") - .moveTo(-radius,0) - .arcSmallCW(radius,radius,0,2*radius,0) - .arcSmallCW(radius,radius,0,-2*radius,0) - .setStrokeLineWidth(3) - .setStrokeDashArray([5, 15, 5, 15, 5]) - .setColor(0,1,0); + # Init + if (me.icon_vor == nil) { + me.icon_vor = me.element.createChild("path") + .moveTo(-15,0) + .lineTo(-7.5,12.5) + .lineTo(7.5,12.5) + .lineTo(15,0) + .lineTo(7.5,-12.5) + .lineTo(-7.5,-12.5) + .close() + .setStrokeLineWidth(3) + .setColor(0,0.6,0.85); + } + # Update + if (me.controller.isActive(me.model)) { + if (me.range_vor == nil) { + var rangeNm = me.controller.query_range(); + # print("VOR is tuned:", me.model.id); + var radius = (me.model.range_nm/rangeNm)*345; + me.range_vor = me.element.createChild("path") + .moveTo(-radius,0) + .arcSmallCW(radius,radius,0,2*radius,0) + .arcSmallCW(radius,radius,0,-2*radius,0) + .setStrokeLineWidth(3) + .setStrokeDashArray([5, 15, 5, 15, 5]) + .setColor(0,1,0); - var course = controller.get_tuned_course(me.model.frequency/100); - vor_grp.createChild("path") - .moveTo(0,-radius) - .vert(2*radius) - .setStrokeLineWidth(3) - .setStrokeDashArray([15, 5, 15, 5, 15]) - .setColor(0,1,0) - .setRotation(course*D2R); - icon_vor.setColor(0,1,0); - } - me.range_vor.show(); - me.radial_vor.show(); - } else { - me.range_vor.hide(); - me.radial_vor.hide(); + var course = me.controller.get_tuned_course(me.model.frequency/100); + me.radial_vor = me.element.createChild("path") + .moveTo(0,-radius) + .vert(2*radius) + .setStrokeLineWidth(3) + .setStrokeDashArray([15, 5, 15, 5, 15]) + .setColor(0,1,0) + .setRotation(course*D2R); + me.icon_vor.setColor(0,1,0); } - } else # Init - me.element.createChild("path") - .moveTo(-15,0) - .lineTo(-7.5,12.5) - .lineTo(7.5,12.5) - .lineTo(15,0) - .lineTo(7.5,-12.5) - .lineTo(-7.5,-12.5) - .close() - .setStrokeLineWidth(3) - .setColor(0,0.6,0.85); + me.range_vor.show(); + me.radial_vor.show(); + } elsif (me.range_vor != nil) { + me.range_vor.hide(); + me.radial_vor.hide(); + } }; diff --git a/Nasal/canvas/map/WPT.lcontroller b/Nasal/canvas/map/WPT.lcontroller new file mode 100644 index 000000000..13bcc2f63 --- /dev/null +++ b/Nasal/canvas/map/WPT.lcontroller @@ -0,0 +1,39 @@ +# Class things: +var name = 'WPT'; # for waypoints +var parents = [SymbolLayer.Controller]; +var __self__ = caller(0)[0]; +SymbolLayer.Controller.add(name, __self__); +SymbolLayer.add(name, { + parents: [SymbolLayer], + type: name, # Symbol type + df_controller: __self__, # controller to use by default -- this one +}); +var new = func(layer) { + var m = { + parents: [__self__], + layer: layer, + listeners: [], + query_range_nm: 25, + query_type:'vor', + }; + return m; +}; +var del = func() { + #print("VOR.lcontroller.del()"); + foreach (var l; me.listeners) + removelistener(l); +}; + +var searchCmd = func { + #print("Running query: WPT"); + + var fp = flightplan(); + var fpSize = fp.getPlanSize(); + var result = []; + for (var i = 1; i 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(), + }, # end of tas behavior callbacks + }, # end of tas hash + { + id: 'wpActiveId', + impl: { + init: func(nd,symbol), + predicate: func(nd) getprop("/autopilot/route-manager/wp/id") != nil and getprop("autopilot/route-manager/active"), + 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"), + is_true: func(nd) { + nd.symbols.wpActiveDist.setText(sprintf("%3.01fNM",getprop("/autopilot/route-manager/wp/dist"))); + nd.symbols.wpActiveDist.show(); + }, + is_false: func(nd) nd.symbols.wpActiveDist.hide(), + }, # of wpActiveDist.impl + }, # of wpActiveDist + { + id: 'eta', + impl: { + init: func(nd,symbol), + predicate: func(nd) getprop("autopilot/route-manager/wp/eta") != nil and getprop("autopilot/route-manager/active"), + 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); + if (h>24) h=h-24; + etaSec=etaSec-3600*h; + var m = math.floor(etaSec/60); + etaSec=etaSec-60*m; + var s = etaSec; + nd.symbols.eta.setText(sprintf("%02.0f%02.0f.%02.0fz",h,m,s)); + nd.symbols.eta.show(); + }, + is_false: func(nd) nd.symbols.eta.hide(), + }, # of eta.impl + }, # of eta + { + id:'hdg', + impl: { + init: func(nd,symbol), + predicate: ALWAYS, # always true + is_true: func(nd) nd.symbols.hdg.setText(sprintf("%03.0f", nd.aircraft_source.get_hdg_mag() )), + is_false: NOTHING, + }, # of hdg.impl + }, # of hdg + { + id:'gs', + impl: { + init: func(nd,symbol), + common: func(nd) nd.symbols.gs.setText(sprintf("%3.0f",nd.aircraft_source.get_spd() )), + predicate: func(nd) nd.aircraft_source.get_spd() >= 30, + is_true: func(nd) { + nd.symbols.gs.setFontSize(36); + }, + is_false: func(nd) nd.symbols.gs.setFontSize(52), + }, # of gs.impl + }, # of gs + { + 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"), + is_true: func(nd) nd.symbols.rangeArcs.show(), + is_false: func(nd) nd.symbols.rangeArcs.hide(), + }, # of rangeArcs.impl + }, # of rangeArcs + + + ], # end of vector with features + + + }, # end of Boeing style +##### +## +## add support for other aircraft/ND types and styles here (Airbus etc) ## -## this loads and configures existing layers (currently, *.layer files in Nasal/canvas/map) ## - layers: [ - { name:'fixes', 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); - if(visible) - trigger_update( layer ); - layer._view.setVisible(visible); - }, # end of layer update predicate - }, # end of fixes layer - - # Should redraw every 10 seconds - { name:'storms', update_on:['toggle_range','toggle_weather','toggle_display_mode'], predicate: func(nd, layer) { - var visible=nd.get_switch('toggle_weather') and nd.get_switch('toggle_display_mode') != "PLAN"; - if (visible) - 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) { - var visible=nd.get_switch('toggle_display_mode') == "PLAN"; - if (visible) - trigger_update( layer ); - layer._view.setVisible(visible); - }, - }, - - { name:'airports-nd', 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']); - if (visible) - trigger_update( layer ); # clear & redraw - layer._view.setVisible( visible); - }, # end of layer update predicate - }, # end of airports layer - - # Should distinct between low and high altitude navaids. Hiding above 40 NM for now, to prevent clutter/lag. - { name:'vor', 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']) and (nd.rangeNm() <= 40); - if(visible) - trigger_update( layer ); # clear & redraw - layer._view.setVisible( visible ); - }, # end of layer update predicate - }, # end of VOR layer - - # Should distinct between low and high altitude navaids. Hiding above 40 NM for now, to prevent clutter/lag. - { 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']) and (nd.rangeNm() <= 40); - if(visible) - trigger_update( layer ); # clear & redraw - 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( 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") 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_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 - -## 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(), # 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) getprop("/velocities/airspeed-kt") > 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(), - }, # end of tas behavior callbacks - }, # end of tas hash - { - id: 'wpActiveId', - impl: { - init: func(nd,symbol), - predicate: func(nd) getprop("/autopilot/route-manager/wp/id") != nil and getprop("autopilot/route-manager/active"), - 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"), - is_true: func(nd) { - nd.symbols.wpActiveDist.setText(sprintf("%3.01fNM",getprop("/autopilot/route-manager/wp/dist"))); - nd.symbols.wpActiveDist.show(); - }, - is_false: func(nd) nd.symbols.wpActiveDist.hide(), - }, # of wpActiveDist.impl - }, # of wpActiveDist - { - id: 'eta', - impl: { - init: func(nd,symbol), - predicate: func(nd) getprop("autopilot/route-manager/wp/eta") != nil and getprop("autopilot/route-manager/active"), - 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); - if (h>24) h=h-24; - etaSec=etaSec-3600*h; - var m = math.floor(etaSec/60); - etaSec=etaSec-60*m; - var s = etaSec; - nd.symbols.eta.setText(sprintf("%02.0f%02.0f.%02.0fz",h,m,s)); - nd.symbols.eta.show(); - }, - is_false: func(nd) nd.symbols.eta.hide(), - }, # of eta.impl - }, # of eta - { id:'hdg', - impl: { - init: func(nd,symbol), - predicate: ALWAYS, # always true - is_true: func(nd) nd.symbols.hdg.setText(sprintf("%03.0f", nd.aircraft_source.get_hdg_mag() )), - is_false: NOTHING, - }, # of hdg.impl - }, # of hdg - - { id:'gs', - impl: { - init: func(nd,symbol), - common: func(nd) nd.symbols.gs.setText(sprintf("%3.0f",nd.aircraft_source.get_spd() )), - predicate: func(nd) nd.aircraft_source.get_spd() >= 30, - is_true: func(nd) { - nd.symbols.gs.setFontSize(36); - }, - is_false: func(nd) nd.symbols.gs.setFontSize(52), - }, # of gs.impl - }, # of gs - - { id:'rangeArcs', - impl: { - init: func(nd,symbol), - 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 - }, # of rangeArcs - - ], # end of vector with features - - - }, # end of Boeing ND style -}; +}; # end of NDStyles ## # encapsulate hdg/lat/lon source, so that the ND may also display AI/MP aircraft in a pilot-view at some point (aka stress-testing) # var NDSourceDriver = {}; -NDSourceDriver.new = func -{ +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"); @@ -246,7 +304,7 @@ NDSourceDriver.new = func { if(getprop("/velocities/groundspeed-kt") > 80) { - getprop("/orientation/track-magnetic-deg"); + getprop("/orientation/track-magnetic-deg"); } else { @@ -273,8 +331,8 @@ NDSourceDriver.new = func ## # configure aircraft specific cockpit switches here -# these are some defaults, can be overridden when calling NavDisplay.new() - -# see the 744 ND.nas file the backend code should never deal directly with +# these are some defaults, can be overridden when calling NavDisplay.new() - +# see the 744 ND.nas file the backend code should never deal directly with # aircraft specific properties using getprop. # To get started implementing your own ND, just copy the switches hash to your # ND.nas file and map the keys to your cockpit properties - and things will just work. @@ -282,21 +340,21 @@ NDSourceDriver.new = func # TODO: switches are ND specific, so move to the NDStyle hash! var default_switches = { - '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'}, - 'toggle_waypoints': {path: '/inputs/wpt', value:0, type:'BOOL'}, - '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/tfc',value:0, type:'BOOL'}, - 'toggle_centered': {path: '/inputs/nd-centered',value:0, type:'BOOL'}, - 'toggle_lh_vor_adf': {path: '/inputs/lh-vor-adf',value:0, type:'INT'}, - 'toggle_rh_vor_adf': {path: '/inputs/rh-vor-adf',value:0, type:'INT'}, - 'toggle_display_mode': {path: '/mfd/display-mode', value:'MAP', type:'STRING'}, # valid values are: APP, MAP, PLAN or VOR - 'toggle_display_type': {path: '/mfd/display-type', value:'CRT', type:'STRING'}, # valid values are: CRT or LCD - 'toggle_true_north': {path: '/mfd/true-north', value:0, type:'BOOL'}, + 'toggle_range': {path: '/inputs/range-nm', value:40, 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'}, + 'toggle_waypoints': {path: '/inputs/wpt', value:0, type:'BOOL'}, + '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_centered': {path: '/inputs/nd-centered',value:0, type:'BOOL'}, + 'toggle_lh_vor_adf': {path: '/inputs/lh-vor-adf',value:0, type:'INT'}, + 'toggle_rh_vor_adf': {path: '/inputs/rh-vor-adf',value:0, type:'INT'}, + 'toggle_display_mode': {path: '/mfd/display-mode', value:'MAP', type:'STRING'}, # valid values are: APP, MAP, PLAN or VOR + 'toggle_display_type': {path: '/mfd/display-type', value:'CRT', type:'STRING'}, # valid values are: CRT or LCD + 'toggle_true_north': {path: '/mfd/true-north', value:0, type:'BOOL'}, }; # Hack to update weather radar once every 10 seconds @@ -316,239 +374,268 @@ var update_apl_sym = func { update_apl_sym(); ## -# TODO: +# TODO: # - introduce a MFD class (use it also for PFD/EICAS) # - introduce a SGSubsystem class and use it here # - introduce a Boeing NavDisplay class -var NavDisplay = -{ - # reset handler - handle_reinit: func - { +var NavDisplay = { + + # reset handler + handle_reinit: func { + print("Cleaning up NavDisplay listeners"); # shut down all timers and other loops here - me.update_timer.stop(); + me.update_timer.stop(); foreach(var l; me.listeners) removelistener(l); - }, - listen: func(p,c) - { - append(me.listeners, setlistener(p,c)); - }, - # listeners for cockpit switches - listen_switch: func(s,c) - { - me.listen( me.get_full_switch_path(s), func - { - c(); - }); }, - # get the full property path for a given switch - get_full_switch_path: func (s) { - # debug.dump( me.efis_switches[s] ); - return me.efis_path ~ me.efis_switches[s].path; # FIXME: should be using props.nas instead of ~ - }, + listen: func(p,c) { + append(me.listeners, setlistener(p,c)); + }, - # helper method for getting configurable cockpit switches (which are usually different in each aircraft) - get_switch: func(s) - { + # listeners for cockpit switches + listen_switch: func(s,c) { + # print("event setup for: ", id(c)); + me.listen( me.get_full_switch_path(s), func { + # print("listen_switch triggered:", s, " callback id:", id(c) ); + c(); + }); + }, + + # get the full property path for a given switch + get_full_switch_path: func (s) { + # debug.dump( me.efis_switches[s] ); + return me.efis_path ~ me.efis_switches[s].path; # FIXME: should be using props.nas instead of ~ + }, + + # helper method for getting configurable cockpit switches (which are usually different in each aircraft) + get_switch: func(s) { var switch = me.efis_switches[s]; var path = me.efis_path ~ switch.path ; + #print(s,":Getting switch prop:", path); + return getprop( path ); }, - # for creating NDs that are driven by AI traffic instead of the main aircraft (generalization rocks!) - connectAI: func(source=nil) - { - me.aircraft_source = - { + # 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_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 + }, # of connectAI # TODO: the ctor should allow customization, for different aircraft - # especially properties and SVG files/handles (747, 757, 777 etc) - new : func(prop1, switches=default_switches, style='Boeing') - { + # especially properties and SVG files/handles (747, 757, 777 etc) + new : func(prop1, switches=default_switches, style='Boeing') { var m = { parents : [NavDisplay]}; m.listeners=[]; # for cleanup handling m.aircraft_source = NDSourceDriver.new(); # uses the main aircraft as the driver/source (speeds, position, heading) m.nd_style = NDStyles[style]; # look up ND specific stuff (file names etc) - m.radio_list=["instrumentation/comm/frequencies","instrumentation/comm[1]/frequencies", - "instrumentation/nav/frequencies","instrumentation/nav[1]/frequencies"]; - m.mfd_mode_list=["APP","VOR","MAP","PLAN"]; + + m.radio_list=["instrumentation/comm/frequencies","instrumentation/comm[1]/frequencies", + "instrumentation/nav/frequencies", "instrumentation/nav[1]/frequencies"]; + m.mfd_mode_list=["APP","VOR","MAP","PLAN"]; + m.efis_path = prop1; m.efis_switches = switches; + + # just an alias, to avoid having to rewrite the old code for now m.rangeNm = func m.get_switch('toggle_range'); - m.efis = props.globals.initNode(prop1); - m.mfd = m.efis.initNode("mfd"); - # TODO: unify this with switch handling - m.mfd_mode_num = m.mfd.initNode("mode-num",2,"INT"); - m.std_mode = m.efis.initNode("inputs/setting-std",0,"BOOL"); - m.previous_set = m.efis.initNode("inhg-previous",29.92); - m.kpa_mode = m.efis.initNode("inputs/kpa-mode",0,"BOOL"); - m.kpa_output = m.efis.initNode("inhg-kpa",29.92); - m.kpa_prevoutput = m.efis.initNode("inhg-kpa-previous",29.92); - m.temp = m.efis.initNode("fixed-temp",0); - m.alt_meters = m.efis.initNode("inputs/alt-meters",0,"BOOL"); - m.fpv = m.efis.initNode("inputs/fpv",0,"BOOL"); - - m.mins_mode = m.efis.initNode("inputs/minimums-mode",0,"BOOL"); - m.mins_mode_txt = m.efis.initNode("minimums-mode-text","RADIO","STRING"); - m.minimums = m.efis.initNode("minimums",250,"INT"); - m.mk_minimums = props.globals.getNode("instrumentation/mk-viii/inputs/arinc429/decision-height"); - # TODO: these are switches, can be unified with switch handling hash above (eventually): - - m.nd_plan_wpt = m.efis.initNode("inputs/plan-wpt-index", 0, "INT"); # ditto - return m; + + m.efis = props.globals.initNode(prop1); + m.mfd = m.efis.initNode("mfd"); + + # TODO: unify this with switch handling + m.mfd_mode_num = m.mfd .initNode("mode-num",2,"INT"); + m.std_mode = m.efis.initNode("inputs/setting-std",0,"BOOL"); + m.previous_set = m.efis.initNode("inhg-previous",29.92); + m.kpa_mode = m.efis.initNode("inputs/kpa-mode",0,"BOOL"); + m.kpa_output = m.efis.initNode("inhg-kpa",29.92); + m.kpa_prevoutput = m.efis.initNode("inhg-kpa-previous",29.92); + m.temp = m.efis.initNode("fixed-temp",0); + m.alt_meters = m.efis.initNode("inputs/alt-meters",0,"BOOL"); + m.fpv = m.efis.initNode("inputs/fpv",0,"BOOL"); + + m.mins_mode = m.efis.initNode("inputs/minimums-mode",0,"BOOL"); + m.mins_mode_txt = m.efis.initNode("minimums-mode-text","RADIO","STRING"); + m.minimums = m.efis.initNode("minimums",250,"INT"); + m.mk_minimums = props.globals.getNode("instrumentation/mk-viii/inputs/arinc429/decision-height"); + + # TODO: these are switches, can be unified with switch handling hash above (eventually): + m.nd_plan_wpt = m.efis.initNode("inputs/plan-wpt-index", 0, "INT"); # not yet in switches hash + + ### + # initialize all switches based on the defaults specified in the switch hash + # + foreach(var switch; keys( m.efis_switches ) ) + props.globals.initNode + ( m.get_full_switch_path (switch), + m.efis_switches[switch].value, + m.efis_switches[switch].type + ); + + + return m; }, newMFD: func(canvas_group) { me.listen("/sim/signals/reinit", func me.handle_reinit() ); me.update_timer = maketimer(0.05, func me.update() ); # TODO: make interval configurable via ctor me.nd = canvas_group; + # load the specified SVG file into the me.nd group and populate all sub groups - + canvas.parsesvg(me.nd, me.nd_style.svg_filename, {'font-mapper': me.nd_style.font_mapper}); me.symbols = {}; # storage for SVG elements, to avoid namespace pollution (all SVG elements end up here) foreach(var feature; me.nd_style.features ) { - me.symbols[feature.id] = me.nd.getElementById(feature.id); - if(contains(feature.impl,'init')) feature.impl.init(me.nd, feature); # call The element's init code (i.e. updateCenter) + # print("Setting up SVG feature:", feature.id); + me.symbols[feature.id] = me.nd.getElementById(feature.id); + if(contains(feature.impl,'init')) feature.impl.init(me.nd, feature); # call The element's init code (i.e. updateCenter) } - + ### this is the "old" method that's less flexible, we want to use the style hash instead (see above) # because things are much better configurable that way # now look up all required SVG elements and initialize member fields using the same name to have a convenient handle foreach(var element; ["wind","dmeLDist","dmeRDist","dmeL","dmeR","vorL","vorR","vorLId","vorRId", - "range","status.wxr","status.wpt","hdgGroup","status.sta","status.arpt"]) - me.symbols[element] = me.nd.getElementById(element); + "range","status.wxr","status.wpt","hdgGroup","status.sta","status.arpt"]) + me.symbols[element] = me.nd.getElementById(element); # 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; ["windArrow","compassApp","northUp","aplSymMap","aplSymMapCtr","aplSymVor", - "staFromL2","staToL2","staFromR2","staToR2", - "locPtr","hdgTrk","truMag","altArc","planArcs", - "trkInd","compass","HdgBugCRT","TrkBugLCD","HdgBugLCD","selHdgLine","curHdgPtr", - "staFromL","staToL","staFromR","staToR"] ) - me.symbols[element] = me.nd.getElementById(element).updateCenter(); + "staFromL2","staToL2","staFromR2","staToR2", + "locPtr","hdgTrk","truMag","altArc","planArcs", + "trkInd","compass","HdgBugCRT","TrkBugLCD","HdgBugLCD","selHdgLine","curHdgPtr", + "staFromL","staToL","staFromR","staToR"] ) + me.symbols[element] = me.nd.getElementById(element).updateCenter(); foreach(var element; ["HdgBugCRT2","TrkBugLCD2","HdgBugLCD2","selHdgLine2","curHdgPtr2","vorCrsPtr2"] ) - me.symbols[element] = me.nd.getElementById(element).setCenter(512,565); + me.symbols[element] = me.nd.getElementById(element).setCenter(512,565); # this should probably be using Philosopher's new SymbolLayer ? me.map = me.nd.createChild("map","map") .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: var get_range = func me.get_switch('toggle_range'); - + # predicate for the draw controller - var is_tuned = func(freq) - { + var is_tuned = func(freq) { var nav1=getprop("instrumentation/nav[0]/frequencies/selected-mhz"); var nav2=getprop("instrumentation/nav[1]/frequencies/selected-mhz"); - if(freq == nav1 or freq == nav2) return 1; - return 0; + if (freq == nav1 or freq == nav2) return 1; + return 0; } # another predicate for the draw controller - var get_course_by_freq = func(freq) - { - if(freq == getprop("instrumentation/nav[0]/frequencies/selected-mhz")) + var get_course_by_freq = func(freq) { + if (freq == getprop("instrumentation/nav[0]/frequencies/selected-mhz")) return getprop("instrumentation/nav[0]/radials/selected-deg"); - else + else return getprop("instrumentation/nav[1]/radials/selected-deg"); } - var get_current_position = func - { - return [me.aircraft_source.get_lat(), me.aircraft_source.get_lon()]; + var get_current_position = func { + return [ + me.aircraft_source.get_lat(), me.aircraft_source.get_lon() + ]; } # a hash with controller callbacks, will be passed onto draw routines to customize behavior/appearance # the point being that draw routines don't know anything about their frontends (instrument or GUI dialog) # so we need some simple way to communicate between frontend<->backend until we have real controllers # for now, a single controller hash is shared by most layers - unsupported callbacks are simply ignored by the draw files - # - var controller = - { - query_range: func get_range(), - is_tuned:is_tuned, + # + var controller = { + query_range: func get_range(), + is_tuned:is_tuned, get_tuned_course:get_course_by_freq, get_position: get_current_position, - }; - + }; + + # FIXME: MapStructure: big hack + canvas.Symbol.Controller.get("VOR").query_range = controller.query_range; + canvas.Symbol.Controller.get("VOR").get_tuned_course = controller.get_tuned_course; + canvas.Symbol.Controller.get("DME").is_tuned = controller.is_tuned; + ### # set up various layers, controlled via callbacks in the controller hash # revisit this code once Philosopher's "Smart MVC Symbols/Layers" work is committed and integrated # helper / closure generator var make_event_handler = func(predicate, layer) func predicate(me, layer); - + me.layers={}; # storage container for all ND specific layers # look up all required layers as specified per the NDStyle hash and do the initial setup for event handling - - foreach(var layer; me.nd_style.layers) - { + foreach(var layer; me.nd_style.layers) { + if(layer['disabled']) continue; # skip this layer + #print("newMFD(): Setting up ND layer:", layer.name); # huge hack for the alt-arc, which is not rendered as a map group, but directly as part of the toplevel ND group var render_target = (!contains(layer,'not_a_map') or !layer.not_a_map) ? me.map : me.nd; - var the_layer = me.layers[layer.name] = canvas.MAP_LAYERS[layer.name].new( render_target, layer.name, controller ); + + var the_layer = nil; + if(!layer['isMapStructure']) + the_layer = me.layers[layer.name] = canvas.MAP_LAYERS[layer.name].new( render_target, layer.name, controller ); + else { + #print("Setting up MapStructure-based layer for ND, name:", layer.name); + render_target.addLayer(factory: canvas.SymbolLayer, type_arg: layer.name); + the_layer = me.layers[layer.name] = render_target.getLayer(layer.name); + } # now register all layer specific notification listeners and their corresponding update predicate/callback # pass the ND instance and the layer handle to the predicate when it is called # so that it can directly access the ND instance and its own layer (without having to know the layer's name) - - var event_handler = make_event_handler(layer.predicate, the_layer); - foreach(var event; layer.update_on) - { - me.listen_switch(event, event_handler ) ; + var event_handler = make_event_handler(layer.predicate, the_layer); + foreach(var event; layer.update_on) { + # print("Setting up subscription:", event, " for ", layer.name, " handler id:", id(event_handler) ); + me.listen_switch(event, event_handler); } # foreach event subscription # and now update/init each layer once by calling its update predicate for initialization event_handler(); } # foreach layer - # start the update timer, which makes sure that the update() will be called + #print("navdisplay.mfd:ND layer setup completed"); + + # start the update timer, which makes sure that the update() will be called me.update_timer.start(); - + + # next, radio & autopilot & listeners # TODO: move this to .init field in layers hash or to model files - foreach(var n; var radios = [ "instrumentation/nav/frequencies/selected-mhz", - "instrumentation/nav[1]/frequencies/selected-mhz"]) - me.listen(n, func() - { -# me.drawvor(); -# me.drawdme(); - }); + foreach(var n; var radios = [ + "instrumentation/nav/frequencies/selected-mhz", + "instrumentation/nav[1]/frequencies/selected-mhz"]) + me.listen(n, func() { + # me.drawvor(); + # 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()) - { - setprop(me.get_full_switch_path('toggle_display_mode'),getprop(me.get_full_switch_path('toggle_display_mode'))); - } - else - { + me.listen("/autopilot/route-manager/active", func(active) { + if(active.getValue()) { + me.drawroute(); + me.drawrunways(); + } else { + #print("TODO: navdisplay.mfd: implement route-manager/layer clearing!"); #me.route_group.removeAllChildren(); # HACK! } }); - me.listen("/autopilot/route-manager/current-wp", func(activeWp) - { + me.listen("/autopilot/route-manager/current-wp", func(activeWp) { canvas.updatewp( activeWp.getValue() ); }); + }, - + 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; @@ -563,21 +650,20 @@ var NavDisplay = # important constants var m1 = 111132.92; var m2 = -559.82; - var m3 = 1.175; + var m3 = 1.175; var m4 = -0.0023; - var p1 = 111412.84; - var p2 = -93.5; - var p3 = 0.118; + var p1 = 111412.84; + var p2 = -93.5; + var p3 = 0.118; var latNm = 60; var lonNm = 60; # fgcommand('profiler-start'); - # Heading update - var userHdgMag = me.aircraft_source.get_hdg_mag(); - var userHdgTru = me.aircraft_source.get_hdg_tru(); - var userTrkMag = me.aircraft_source.get_trk_mag(); - var userTrkTru = me.aircraft_source.get_trk_tru(); + var userHdgMag = me.aircraft_source.get_hdg_mag(); + 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; @@ -588,7 +674,7 @@ var NavDisplay = var userTrk=userTrkMag; } if (me.aircraft_source.get_spd() < 80) - userTrk = userHdg; + userTrk = userHdg; var userLat = me.aircraft_source.get_lat(); var userLon = me.aircraft_source.get_lon(); var userSpd = me.aircraft_source.get_spd(); @@ -600,24 +686,24 @@ var NavDisplay = print("aircraft source invalid, returning !"); return; } - + if(me.in_mode('toggle_display_mode', ['PLAN'])) me.map.setTranslation(512,512); elsif(me.get_switch('toggle_centered')) me.map.setTranslation(512,565); else me.map.setTranslation(512,824); - # Calculate length in NM of one degree at current location TODO: expose as methods, for external callbacks + # 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; var latlen = m1 + (m2 * math.cos(2 * userLatR)) + (m3 * math.cos(4 * userLatR)) + (m4 * math.cos(6 * userLatR)); var lonlen = (p1 * math.cos(userLatR)) + (p2 * math.cos(3 * userLatR)) + (p3 * math.cos(5 * userLatR)); latNm = latlen*M2NM; #60 at equator lonNm = lonlen*M2NM; #60 at equator - + me.symbols.windArrow.setRotation((getprop("/environment/wind-from-heading-deg")-userHdg)*D2R); me.symbols.wind.setText(sprintf("%3.0f / %2.0f",getprop("/environment/wind-from-heading-deg"),getprop("/environment/wind-speed-kt"))); - + if(me.get_switch('toggle_lh_vor_adf') == 1) { me.symbols.vorL.setText("VOR L"); @@ -681,10 +767,10 @@ var NavDisplay = me.symbols.vorRId.setText(""); me.symbols.dmeRDist.setText(""); } - + me.symbols.range.setText(sprintf("%3.0f",me.rangeNm()/2)); - # reposition the map, change heading & range: + # reposition the map, change heading & range: if(me.in_mode('toggle_display_mode', ['PLAN'])) { me.symbols.windArrow.setVisible(!dispLCD); me.map._node.getNode("hdg",1).setDoubleValue(0); @@ -738,7 +824,7 @@ var NavDisplay = 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.show(); me.symbols.locPtr.setTranslation(deflection*150,0); if(abs(deflection < 0.99)) me.symbols.locPtr.setColorFill(1,0,1,1); @@ -759,13 +845,13 @@ var NavDisplay = me.symbols.hdgGroup.setTranslation(0,0); me.symbols.compassApp.hide(); } - + 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']); var magVar = getprop("environment/magnetic-variation-deg"); if(me.in_mode('toggle_display_mode', ['APP','MAP','VOR','PLAN'])) @@ -887,14 +973,14 @@ var NavDisplay = me.symbols.selHdgLine2.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) { @@ -911,7 +997,7 @@ var NavDisplay = } else { me.symbols.altArc.hide(); } - + ## 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 @@ -931,10 +1017,9 @@ 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') 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'])); } -}; \ No newline at end of file +}; diff --git a/Nasal/canvas/map/route.model b/Nasal/canvas/map/route.model index 80df9e496..27dfc3487 100644 --- a/Nasal/canvas/map/route.model +++ b/Nasal/canvas/map/route.model @@ -4,8 +4,8 @@ RouteModel.new = func make(LayerModel, RouteModel); RouteModel.init = func { me._view.reset(); - if (!getprop("/autopilot/route-manager/active")) - return; + if (!getprop("/autopilot/route-manager/active")) + return; ## TODO: all the model stuff is still inside the draw file for now, this just ensures that it will be called once foreach(var t; [nil] ) From baf9019a32180165a5cbec53d989c0ffb523b8f7 Mon Sep 17 00:00:00 2001 From: Gijs de Rooy Date: Sat, 11 Jan 2014 21:22:40 +0100 Subject: [PATCH 4/6] =?UTF-8?q?New=20joystick:=20Logitech=20G940=20by=20Ma?= =?UTF-8?q?rtin=20M=C4=9B=C5=99insk=C3=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Input/Joysticks/Logitech/g940.xml | 661 ++++++++++++++++++++++++++++++ 1 file changed, 661 insertions(+) create mode 100644 Input/Joysticks/Logitech/g940.xml diff --git a/Input/Joysticks/Logitech/g940.xml b/Input/Joysticks/Logitech/g940.xml new file mode 100644 index 000000000..a29bc2409 --- /dev/null +++ b/Input/Joysticks/Logitech/g940.xml @@ -0,0 +1,661 @@ + + + + + G940 + + + + + Aileron + + property-scale + /controls/flight/aileron + + + + + Elevator + + property-scale + /controls/flight/elevator + -1.0 + + + + + Aileron trim + + property-scale + /controls/flight/aileron-trim + + + + + + + View direction + + 12 + + + View left + true + + + nasal + + + + + View right + true + + + nasal + + + + + + + View elevation + + 13 + + + View down + true + + property-adjust + /sim/current-view/goal-pitch-offset-deg + 3.0 + + + + View up + true + + property-adjust + /sim/current-view/goal-pitch-offset-deg + -3.0 + + + + + + + + + + + + + + + + + + + + + + Throttle right engine + + 6 + + + + nasal + + + + + + + + Throttle left engine + + 7 + + + property-scale + /controls/engines/engine[0]/throttle + -1.0 + -0.5 + + + + + Mixture + + 9 + + + property-scale + /controls/engines/engine/mixture + -1.0 + -0.5 + + + + + Propeller pitch + + 8 + + + property-scale + /controls/engines/engine/propeller-pitch + -1.0 + -0.5 + + + + + View zoom + + 14 + + + Zoom out + true + + nasal + + + + + Zoom in + true + + nasal + + + + + + + Reset zoom + + 15 + + + Reset zoom to default + false + + property-assign + /sim/current-view/field-of-view + /sim/view/config/default-field-of-view-deg + + + + Reset zoom for greater overview + false + + nasal + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Rudder + + 2 + + + property-scale + /controls/flight/rudder + 2 + + + + + + 4 + + Left differential break + + property-scale + /controls/gear/brake-left + -1.0 + -0.5 + + + + + + 3 + + Right differential break + + property-scale + /controls/gear/brake-right + -1.0 + -0.5 + + + + + From 304c9e6e77d9da3f490c65b4f1b6d1ac3355b11f Mon Sep 17 00:00:00 2001 From: Gijs de Rooy Date: Sat, 11 Jan 2014 21:26:20 +0100 Subject: [PATCH 5/6] =?UTF-8?q?Genius=20F-31U=20update=20by=20Martin=20M?= =?UTF-8?q?=C4=9B=C5=99insk=C3=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Input/Joysticks/Genius/f31.xml | 100 +++++++++++++++++++-------------- 1 file changed, 59 insertions(+), 41 deletions(-) diff --git a/Input/Joysticks/Genius/f31.xml b/Input/Joysticks/Genius/f31.xml index 783594e88..30f3862f1 100644 --- a/Input/Joysticks/Genius/f31.xml +++ b/Input/Joysticks/Genius/f31.xml @@ -1,28 +1,36 @@ - + Padix Co. Ltd. 10-Button USB Joystick @@ -44,7 +52,7 @@ Buttons: property-scale /controls/flight/elevator 2 - -1.0 + -1.0 @@ -58,7 +66,7 @@ Buttons: property-scale /controls/flight/rudder 2 - -1.0 + -1.0 @@ -75,7 +83,7 @@ Buttons: - View Direction + View direction 4 6 @@ -85,7 +93,7 @@ Buttons: true + 3.0 @@ -145,28 +153,37 @@ Buttons: property-adjust /sim/current-view/goal-pitch-offset-deg - -3.0 + -3.0 - - - + + + From bb640294d8babaa524490ad0a4e5e53d3e022c3e Mon Sep 17 00:00:00 2001 From: James Turner Date: Sat, 11 Jan 2014 22:32:22 +0000 Subject: [PATCH 6/6] Fix Uberhader-Rembrandt bug on Mac. My parser is stricter than your parser. (Unless you're on Ati-non-Mac) --- Shaders/ubershader-gbuffer.frag | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Shaders/ubershader-gbuffer.frag b/Shaders/ubershader-gbuffer.frag index ab38c13f9..12732ae51 100644 --- a/Shaders/ubershader-gbuffer.frag +++ b/Shaders/ubershader-gbuffer.frag @@ -130,7 +130,7 @@ void main (void) ////////////////////////////////////////////////////////////////////// //begin DIRT ////////////////////////////////////////////////////////////////////// - if (dirt_enabled > 0.0){ + if (dirt_enabled >= 1){ vec3 dirtFactorIn = vec3 (dirt_r_factor, dirt_g_factor, dirt_b_factor); vec3 dirtFactor = reflmap.rgb * dirtFactorIn.rgb; //dirtFactor.r = smoothstep(0.0, 1.0, dirtFactor.r);