FG1000 PFD Inset Map, HDG, BARO, ALT controls
- Add inset map for PFD - Add bindings to set the HDG, BARO, Altitude
This commit is contained in:
parent
e95dfc5b02
commit
c37f1ff7bb
14 changed files with 801 additions and 166 deletions
|
@ -24,11 +24,11 @@
|
|||
borderopacity="1"
|
||||
inkscape:pageopacity="1"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="7.4375004"
|
||||
inkscape:cx="279.11962"
|
||||
inkscape:cy="261.78273"
|
||||
inkscape:zoom="3.7187502"
|
||||
inkscape:cx="99.463593"
|
||||
inkscape:cy="150.41314"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer6"
|
||||
inkscape:current-layer="svg3140"
|
||||
showgrid="false"
|
||||
showguides="false"
|
||||
inkscape:guide-bbox="true"
|
||||
|
@ -75,7 +75,7 @@
|
|||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
|
@ -6998,13 +6998,67 @@
|
|||
id="PFDInstrumentsPFD-Map"
|
||||
inkscape:label="PFD-Map">
|
||||
<rect
|
||||
inkscape:label="PFD-Map-bg"
|
||||
y="492"
|
||||
x="13"
|
||||
height="218"
|
||||
width="215"
|
||||
id="PFD-Map-bg"
|
||||
style="opacity:1;fill:#000000;fill-opacity:1;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
style="display:inline;opacity:1;fill:#000000;fill-opacity:1;stroke:#ffffff;stroke-width:1.005;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="PFDInstrumentsPFD-Map-bg-1"
|
||||
width="218.99536"
|
||||
height="218.99538"
|
||||
x="9.3594561"
|
||||
y="491.24179"
|
||||
inkscape:label="PFD-Map-bg" />
|
||||
<g
|
||||
id="PFDInstrumentsPFD-Map-Display"
|
||||
inkscape:label="#g14879">
|
||||
<rect
|
||||
style="opacity:1;fill:#000000;fill-opacity:1;stroke:#ffffff;stroke-width:1.005;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="PFDInstrumentsPFD-Map-bg"
|
||||
width="218.99536"
|
||||
height="218.99538"
|
||||
x="9.5023146"
|
||||
y="491.50232"
|
||||
inkscape:label="PFD-Map-bg" />
|
||||
</g>
|
||||
<rect
|
||||
style="opacity:1;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.97517461;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||
id="PFDInstrumentsOrientation-bg"
|
||||
width="79.886292"
|
||||
height="21.989275"
|
||||
x="146.78056"
|
||||
y="492.74817"
|
||||
inkscape:label="#rect14859" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;line-height:125%;font-family:Sans;-inkscape-font-specification:'Sans, Normal';text-align:center;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
x="186.54517"
|
||||
y="508.86316"
|
||||
id="PFDInstrumentsOrientationDisplay"
|
||||
sodipodi:linespacing="125%"
|
||||
inkscape:label="#text14869"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan14871"
|
||||
x="186.54517"
|
||||
y="508.86316"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;line-height:125%;font-family:Sans;-inkscape-font-specification:'Sans, Normal';text-align:center;writing-mode:lr-tb;text-anchor:middle;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none">NORTH UP</tspan></text>
|
||||
<rect
|
||||
inkscape:label="#rect14859"
|
||||
y="688.24396"
|
||||
x="147.18393"
|
||||
height="21.989275"
|
||||
width="79.886292"
|
||||
id="rect14873"
|
||||
style="opacity:1;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.97517461;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
||||
<text
|
||||
sodipodi:linespacing="125%"
|
||||
id="PFDInstrumentsRangeDisplay"
|
||||
y="704.09003"
|
||||
x="223.3855"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;line-height:125%;font-family:Sans;-inkscape-font-specification:'Sans, Normal';text-align:end;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:end;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
xml:space="preserve"
|
||||
inkscape:label="#text14875"><tspan
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;line-height:125%;font-family:Sans;-inkscape-font-specification:'Sans, Normal';text-align:end;writing-mode:lr-tb;text-anchor:end"
|
||||
y="704.09003"
|
||||
x="223.3855"
|
||||
id="tspan14877"
|
||||
sodipodi:role="line">15nm</tspan></text>
|
||||
</g>
|
||||
<g
|
||||
id="PFDInstrumentsBRG2"
|
||||
|
|
Before Width: | Height: | Size: 464 KiB After Width: | Height: | Size: 468 KiB |
|
@ -22,6 +22,36 @@
|
|||
|
||||
var ConfigStore = {
|
||||
|
||||
# Layer display configuration:
|
||||
# enabled - whether this layer has been enabled by the user
|
||||
# declutter - the maximum declutter level (0-3) that this layer is visible in
|
||||
# range - the maximum range this layer is visible (configured by user)
|
||||
# max_range - the maximum range value that a user can configure for this layer.
|
||||
layerRanges : {
|
||||
DTO : { enabled: 0, declutter: 3, range: 2000, max_range: 2000 },
|
||||
|
||||
GRID : { enabled: 0, declutter: 1, range: 20, max_range: 2000 },
|
||||
DME : { enabled: 1, declutter: 1, range: 150, max_range: 300 },
|
||||
VOR_FG1000 : { enabled: 1, declutter: 1, range: 150, max_range: 300 },
|
||||
NDB : { enabled: 1, declutter: 1, range: 15, max_range: 30 },
|
||||
FIX : { enabled: 1, declutter: 1, range: 15, max_range: 30 },
|
||||
RTE : { enabled: 1, declutter: 3, range: 2000, max_range: 2000 },
|
||||
WPT : { enabled: 1, declutter: 3, range: 2000, max_range: 2000 },
|
||||
|
||||
APS : { enabled: 1, declutter: 3, range: 2000, max_range: 2000 },
|
||||
FLT : { enabled: 1, declutter: 3, range: 2000, max_range: 2000 },
|
||||
|
||||
WXR : { enabled: 1, declutter: 2, range: 2000, max_range: 2000 },
|
||||
|
||||
APT : { enabled: 1, declutter: 2, range: 150, max_range: 300 },
|
||||
|
||||
TFC : { enabled: 0, declutter: 3, range: 150, max_range: 2000},
|
||||
|
||||
OpenAIP : { enabled: 1, declutter: 1, range: 150, max_range: 300 },
|
||||
STAMEN : { enabled: 1, declutter: 3, range: 500, max_range: 2000 },
|
||||
STAMEN_terrain : { enabled: 1, declutter: 3, range: 500, max_range: 2000 },
|
||||
},
|
||||
|
||||
configValues : {
|
||||
"DisplayUnitsNavAngle": ["MAGNETIC", "TRUE"] ,
|
||||
"DisplayUnitsDistanceAndSpeed": ["NAUTICAL", "METRIC"] ,
|
||||
|
@ -91,6 +121,7 @@ var ConfigStore = {
|
|||
var obj ={
|
||||
parents : [ ConfigStore ],
|
||||
_values : {},
|
||||
_layerRanges : {},
|
||||
};
|
||||
|
||||
foreach (var i; keys(ConfigStore.configValues)) {
|
||||
|
@ -107,6 +138,10 @@ var ConfigStore = {
|
|||
#obj.set("MFDHeader4", "ESA");
|
||||
obj.set("MFDHeader4", "FOD");
|
||||
|
||||
foreach (var i; keys(ConfigStore.layerRanges)) {
|
||||
obj._layerRanges[i] = ConfigStore.layerRanges[i];
|
||||
}
|
||||
|
||||
return obj;
|
||||
},
|
||||
|
||||
|
@ -140,4 +175,28 @@ var ConfigStore = {
|
|||
get : func(name) {
|
||||
return me._values[name];
|
||||
},
|
||||
|
||||
getLayer : func(name) {
|
||||
return me._layerRanges[name];
|
||||
},
|
||||
|
||||
getLayerNames : func() {
|
||||
return keys(me._layerRanges);
|
||||
},
|
||||
|
||||
isLayerEnabled : func(name) {
|
||||
return me._layerRanges[name].enabled;
|
||||
},
|
||||
setLayerEnabled : func(name, enabled) {
|
||||
me._layerRanges[name].enabled = enabled;
|
||||
},
|
||||
toggleLayerEnabled : func(name) {
|
||||
me._layerRanges[name].enabled = ! me._layerRanges[name].enabled;
|
||||
},
|
||||
|
||||
configureLayer : func(layer, enabled, range) {
|
||||
me._layerRanges[layer].enabled = enabled;
|
||||
me._layerRanges[layer].range = math.min(range, me._layerRanges[layer].max_range);
|
||||
},
|
||||
|
||||
};
|
||||
|
|
|
@ -167,3 +167,43 @@ var SURFACE_TYPES = {
|
|||
9 : "DIRT",
|
||||
0 : "GRAVEL",
|
||||
};
|
||||
|
||||
# Vertical ranges, and labels.
|
||||
# 28 ranges from 500ft to 2000nm, measuring the vertical map distance.
|
||||
# Vertical size of the map (once the nav box and softkey area is removed) is 689px.
|
||||
# 2000nm = 12,152,000ft.
|
||||
var RANGES = [{range: 500/6076.12, label: "500ft"},
|
||||
{range: 750/6076.12, label: "750ft"},
|
||||
{range: 1000/6076.12, label: "1000ft"},
|
||||
{range: 1500/6076.12, label: "1500ft"},
|
||||
{range: 2000/6076.12, label: "2000ft"},
|
||||
{range: 0.5, label: "0.5nm"},
|
||||
{range: 0.75, label: "0.75nm"},
|
||||
{range: 1, label: "1nm"},
|
||||
{range: 2, label: "2nm"},
|
||||
{range: 3, label: "3nm"},
|
||||
{range: 4, label: "4nm"},
|
||||
{range: 6, label: "6nm"},
|
||||
{range: 8, label: "8nm"},
|
||||
{range: 10, label: "10nm"},
|
||||
{range: 12, label: "12nm"},
|
||||
{range: 15, label: "15nm"},
|
||||
{range: 20, label: "20nm"},
|
||||
{range: 25, label: "25nm"},
|
||||
{range: 30, label: "30nm"},
|
||||
{range: 40, label: "40nm"},
|
||||
{range: 50, label: "50nm"},
|
||||
{range: 75, label: "75nm"},
|
||||
{range: 100, label: "100nm"},
|
||||
{range: 200, label: "200nm"},
|
||||
{range: 500, label: "500nm"},
|
||||
{range: 1000, label: "1000nm"},
|
||||
{range: 1500, label: "1500nm"},
|
||||
{range: 2000, label: "2000nm"}, ];
|
||||
|
||||
var ORIENTATIONS = [
|
||||
{ label: "NORTH UP" },
|
||||
{ label: "TRK UP" },
|
||||
{ label: "DTK UP" },
|
||||
{ label: "HDG UP" },
|
||||
];
|
||||
|
|
|
@ -31,6 +31,7 @@ io.load_nasal(nasal_dir ~ '/MFDPageController.nas', "fg1000");
|
|||
io.load_nasal(nasal_dir ~ '/PFD.nas', "fg1000");
|
||||
io.load_nasal(nasal_dir ~ '/MFD.nas', "fg1000");
|
||||
io.load_nasal(nasal_dir ~ '/GUI.nas', "fg1000");
|
||||
io.load_nasal(nasal_dir ~ '/NavMap.nas', "fg1000");
|
||||
|
||||
var FG1000 = {
|
||||
|
||||
|
|
|
@ -29,14 +29,17 @@ var GenericFMSPublisher =
|
|||
obj.addPropMap("FMSHeadingBug", "/autopilot/settings/heading-bug-deg");
|
||||
obj.addPropMap("FMSSelectedAlt", "/autopilot/settings/target-alt-ft");
|
||||
|
||||
obj.addPropMap("FMSLegValid", "/instrumentation/gps/wp/wp[1]/valid");
|
||||
obj.addPropMap("FMSLegID", "/instrumentation/gps/wp/wp[1]/ID");
|
||||
obj.addPropMap("FMSLegBearing", "/instrumentation/gps/wp/wp[1]/bearing-mag-deg");
|
||||
obj.addPropMap("FMSLegDistanceNM", "/instrumentation/gps/wp/wp[1]/distance-nm");
|
||||
obj.addPropMap("FMSLegCourseError", "/instrumentation/gps/wp/wp[1]/course-error-nm");
|
||||
obj.addPropMap("FMSLegDesiredTrack", "/instrumentation/gps/indicated-track-magnetic-deg");
|
||||
obj.addPropMap("FMSLegDesiredTrack", "/instrumentation/gps/wp/wp[1]/desired-course-deg");
|
||||
obj.addPropMap("FMSLegTrackErrorAngle", "/instrumentation/gps/wp/wp[1]/course-deviation-deg");
|
||||
obj.addPropMap("FMSLegTrack", "/instrumentation/gps/indicated-track-magnetic-deg");
|
||||
obj.addPropMap("FMSGroundspeed", "/instrumentation/gps/indicated-ground-speed-kt");
|
||||
obj.addPropMap("FMSWayPointCourseError", "/instrumentation/gps/wp/wp[1]/course-error-nm");
|
||||
|
||||
obj.addPropMap("FMSGroundspeed", "/instrumentation/gps/indicated-ground-speed-kt");
|
||||
|
||||
obj.addPropMap("FMSNav1From", "/instrumentation/nav/from-flag");
|
||||
obj.addPropMap("FMSNav2From", "/instrumentation/nav[1]/from-flag");
|
||||
|
||||
|
@ -52,6 +55,11 @@ var GenericFMSPublisher =
|
|||
gpsdata[name] = propmap.getValue();
|
||||
}
|
||||
|
||||
# Some GPS properties have odd values to indicate that nothing is set, so
|
||||
# remove them from the data set.
|
||||
if (gpsdata["FMSLegBearing"] == -9999) gpsdata["FMSLegBearing"] = nil;
|
||||
if (gpsdata["FMSLegDistanceNM"] == -1) gpsdata["FMSLegDistanceNM"] = nil;
|
||||
|
||||
# A couple of calculated values used by the MFD Header display
|
||||
var total_fuel = getprop("/consumables/fuel/tank[0]/indicated-level-gal_us") or 0.0;
|
||||
total_fuel = total_fuel + (getprop("/consumables/fuel/tank[1]/indicated-level-gal_us") or 0.0);
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
# Copyright 2018 Stuart Buchanan
|
||||
# This file is part of FlightGear.
|
||||
#
|
||||
# Foobar is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# FlightGear is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with FlightGear. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# FMS Interface using Emesary to update FMS properties from Emesary messages.
|
||||
|
||||
var GenericFMSUpdater =
|
||||
{
|
||||
new : func () {
|
||||
var obj = {
|
||||
parents : [
|
||||
GenericFMSUpdater,
|
||||
PropertyUpdater.new(
|
||||
notifications.PFDEventNotification.DefaultType,
|
||||
notifications.PFDEventNotification.FMSData,
|
||||
)
|
||||
],
|
||||
};
|
||||
|
||||
obj.addPropMap("FMSHeadingBug", "/autopilot/settings/heading-bug-deg");
|
||||
obj.addPropMap("FMSSelectedAlt", "/autopilot/settings/target-alt-ft");
|
||||
obj.addPropMap("FMSPressureSettingInHG", "/instrumentation/altimeter/setting-inhg");
|
||||
return obj;
|
||||
},
|
||||
};
|
|
@ -24,6 +24,7 @@ io.load_nasal(nasal_dir ~ 'Interfaces/GenericNavComPublisher.nas', "fg1000");
|
|||
io.load_nasal(nasal_dir ~ 'Interfaces/GenericNavComUpdater.nas', "fg1000");
|
||||
io.load_nasal(nasal_dir ~ 'Interfaces/NavDataInterface.nas', "fg1000");
|
||||
io.load_nasal(nasal_dir ~ 'Interfaces/GenericFMSPublisher.nas', "fg1000");
|
||||
io.load_nasal(nasal_dir ~ 'Interfaces/GenericFMSUpdater.nas', "fg1000");
|
||||
io.load_nasal(nasal_dir ~ 'Interfaces/GenericADCPublisher.nas', "fg1000");
|
||||
|
||||
|
||||
|
@ -51,6 +52,7 @@ var GenericInterfaceController = {
|
|||
obj.navcomUpdater = fg1000.GenericNavComUpdater.new();
|
||||
obj.navdataInterface = fg1000.NavDataInterface.new();
|
||||
obj.gpsPublisher = fg1000.GenericFMSPublisher.new();
|
||||
obj.gpsUpdater = fg1000.GenericFMSUpdater.new();
|
||||
obj.adcPublisher = fg1000.GenericADCPublisher.new();
|
||||
return obj;
|
||||
},
|
||||
|
@ -62,6 +64,7 @@ var GenericInterfaceController = {
|
|||
me.navcomUpdater.start();
|
||||
me.navdataInterface.start();
|
||||
me.gpsPublisher.start();
|
||||
me.gpsUpdater.start();
|
||||
me.adcPublisher.start();
|
||||
},
|
||||
|
||||
|
@ -72,6 +75,7 @@ var GenericInterfaceController = {
|
|||
me.navcomUpdater.stop();
|
||||
me.navdataInterface.stop();
|
||||
me.gpsPublisher.stop();
|
||||
me.gpsUpdater.stop();
|
||||
me.adcPublisher.stop();
|
||||
},
|
||||
};
|
||||
|
|
|
@ -146,5 +146,14 @@ getDevice : func() {
|
|||
getMFD : func() {
|
||||
return me.mfd;
|
||||
},
|
||||
getPageName : func () {
|
||||
return me.pageName;
|
||||
},
|
||||
getSVG : func() {
|
||||
return me._SVGGroup;
|
||||
},
|
||||
getGroup : func() {
|
||||
return me._group;
|
||||
}
|
||||
|
||||
};
|
||||
|
|
|
@ -44,7 +44,7 @@ handleNavFreqTransfer : func (value) { return me.page.mfd.SurroundController.han
|
|||
handleNavOuter : func (value) { return me.page.mfd.SurroundController.handleNavOuter(value); },
|
||||
handleNavInner : func (value) { return me.page.mfd.SurroundController.handleNavInner(value); },
|
||||
handleNavToggle : func (value) { return me.page.mfd.SurroundController.handleNavToggle(value); },
|
||||
handleHeading : func (value) { return emesary.Transmitter.ReceiptStatus_NotProcessed; },
|
||||
handleHeading : func (value) { return me.page.mfd.SurroundController.handleHeading(value); },
|
||||
handleHeadingPress : func (value) { return emesary.Transmitter.ReceiptStatus_NotProcessed; },
|
||||
|
||||
# Joystick
|
||||
|
@ -53,7 +53,7 @@ handleJoystickHorizontal : func (value) { return emesary.Transmitter.ReceiptStat
|
|||
handleJoystickHorizontal : func (value) { return emesary.Transmitter.ReceiptStatus_NotProcessed; },
|
||||
|
||||
#CRS/BARO
|
||||
handleBaro : func (value) { return emesary.Transmitter.ReceiptStatus_NotProcessed; },
|
||||
handleBaro : func (value) { return me.page.mfd.SurroundController.handleBaro(value); },
|
||||
handleCRS : func (value) { return emesary.Transmitter.ReceiptStatus_NotProcessed; },
|
||||
handleCRSCenter : func (value) { return emesary.Transmitter.ReceiptStatus_NotProcessed; },
|
||||
|
||||
|
@ -94,8 +94,8 @@ handleMenu : func (value) { return emesary.Transmitter.ReceiptStatus_NotProcess
|
|||
handleProc : func (value) { return emesary.Transmitter.ReceiptStatus_NotProcessed; },
|
||||
handleEnter : func (value) { return emesary.Transmitter.ReceiptStatus_NotProcessed; },
|
||||
|
||||
handleAltOuter : func (value) { return emesary.Transmitter.ReceiptStatus_NotProcessed; },
|
||||
handleAltInner : func (value) { return emesary.Transmitter.ReceiptStatus_NotProcessed; },
|
||||
handleAltOuter : func (value) { return me.page.mfd.SurroundController.handleAltOuter(value); },
|
||||
handleAltInner : func (value) { return me.page.mfd.SurroundController.handleAltInner(value); },
|
||||
|
||||
RegisterWithEmesary : func()
|
||||
{
|
||||
|
|
|
@ -17,75 +17,7 @@
|
|||
# Navigation Map Controller
|
||||
var NavigationMapController =
|
||||
{
|
||||
# Vertical ranges, and labels.
|
||||
# 28 ranges from 500ft to 2000nm, measuring the vertical map distance.
|
||||
# Vertical size of the map (once the nav box and softkey area is removed) is 689px.
|
||||
# 2000nm = 12,152,000ft.
|
||||
RANGES : [{range: 500/6076.12, label: "500ft"},
|
||||
{range: 750/6076.12, label: "750ft"},
|
||||
{range: 1000/6076.12, label: "1000ft"},
|
||||
{range: 1500/6076.12, label: "1500ft"},
|
||||
{range: 2000/6076.12, label: "2000ft"},
|
||||
{range: 0.5, label: "0.5nm"},
|
||||
{range: 0.75, label: "0.75nm"},
|
||||
{range: 1, label: "1nm"},
|
||||
{range: 2, label: "2nm"},
|
||||
{range: 3, label: "3nm"},
|
||||
{range: 4, label: "4nm"},
|
||||
{range: 6, label: "6nm"},
|
||||
{range: 8, label: "8nm"},
|
||||
{range: 10, label: "10nm"},
|
||||
{range: 12, label: "12nm"},
|
||||
{range: 15, label: "15nm"},
|
||||
{range: 20, label: "20nm"},
|
||||
{range: 25, label: "25nm"},
|
||||
{range: 30, label: "30nm"},
|
||||
{range: 40, label: "40nm"},
|
||||
{range: 50, label: "50nm"},
|
||||
{range: 75, label: "75nm"},
|
||||
{range: 100, label: "100nm"},
|
||||
{range: 200, label: "200nm"},
|
||||
{range: 500, label: "500nm"},
|
||||
{range: 1000, label: "1000nm"},
|
||||
{range: 1500, label: "1500nm"},
|
||||
{range: 2000, label: "2000nm"}, ],
|
||||
|
||||
ORIENTATIONS : [
|
||||
{ label: "NORTH UP" },
|
||||
{ label: "TRK UP" },
|
||||
{ label: "DTK UP" },
|
||||
{ label: "HDG UP" },
|
||||
],
|
||||
|
||||
# Layer display configuration:
|
||||
# enabled - whether this layer has been enabled by the user
|
||||
# declutter - the maximum declutter level (0-3) that this layer is visible in
|
||||
# range - the maximum range this layer is visible (configured by user)
|
||||
# max_range - the maximum range value that a user can configure for this layer.
|
||||
LAYER_RANGES : {
|
||||
DTO : { enabled: 0, declutter: 3, range: 2000, max_range: 2000 },
|
||||
|
||||
GRID : { enabled: 0, declutter: 1, range: 20, max_range: 2000 },
|
||||
DME : { enabled: 1, declutter: 1, range: 150, max_range: 300 },
|
||||
VOR_FG1000 : { enabled: 1, declutter: 1, range: 150, max_range: 300 },
|
||||
NDB : { enabled: 1, declutter: 1, range: 15, max_range: 30 },
|
||||
FIX : { enabled: 1, declutter: 1, range: 15, max_range: 30 },
|
||||
RTE : { enabled: 1, declutter: 3, range: 2000, max_range: 2000 },
|
||||
WPT : { enabled: 1, declutter: 3, range: 2000, max_range: 2000 },
|
||||
|
||||
APS : { enabled: 1, declutter: 3, range: 2000, max_range: 2000 },
|
||||
FLT : { enabled: 1, declutter: 3, range: 2000, max_range: 2000 },
|
||||
|
||||
WXR : { enabled: 1, declutter: 2, range: 2000, max_range: 2000 },
|
||||
|
||||
APT : { enabled: 1, declutter: 2, range: 150, max_range: 300 },
|
||||
|
||||
TFC : { enabled: 0, declutter: 3, range: 150, max_range: 2000},
|
||||
|
||||
OpenAIP : { enabled: 1, declutter: 1, range: 150, max_range: 300 },
|
||||
STAMEN : { enabled: 1, declutter: 3, range: 500, max_range: 2000 },
|
||||
STAMEN_terrain : { enabled: 1, declutter: 3, range: 500, max_range: 2000 },
|
||||
},
|
||||
|
||||
# TODO: Add STAMEN topo layer, which is visible on all levels as opposed to
|
||||
# roads, railways, boundaries, cities which are only visible on declutter 0.
|
||||
|
@ -96,7 +28,6 @@ var NavigationMapController =
|
|||
# Airways levels.
|
||||
AIRWAYS : [ "AIRWAYS", "AIRWY ON", "AIRWY LO", "AIRWY HI"],
|
||||
|
||||
|
||||
new : func (page, svg)
|
||||
{
|
||||
var obj = { parents : [ NavigationMapController, MFDPageController.new(page) ] };
|
||||
|
@ -105,7 +36,7 @@ var NavigationMapController =
|
|||
obj.airways = 0;
|
||||
obj.page = page;
|
||||
obj.setZoom(obj.current_zoom);
|
||||
obj.setOrientation(obj.ORIENTATIONS[0]);
|
||||
obj.setOrientation(fg1000.ORIENTATIONS[0]);
|
||||
|
||||
return obj;
|
||||
},
|
||||
|
@ -116,12 +47,12 @@ var NavigationMapController =
|
|||
me.setZoom(me.current_zoom +1);
|
||||
},
|
||||
setZoom : func(zoom) {
|
||||
if ((zoom < 0) or (zoom > (size(me.RANGES) - 1))) return;
|
||||
if ((zoom < 0) or (zoom > (size(fg1000.RANGES) - 1))) return;
|
||||
me.current_zoom = zoom;
|
||||
# Ranges above represent vertical ranges, but the display is a rectangle, so
|
||||
# we need to use the diagonal range of the 1024 x 689, which is 617px.
|
||||
# 617px is 1.8 x 689/2, so we need to increase the range values by x1.8
|
||||
me.page.setRange(me.RANGES[zoom].range, me.RANGES[zoom].label);
|
||||
me.page.setRange(fg1000.RANGES[zoom].range, fg1000.RANGES[zoom].label);
|
||||
me.updateVisibility();
|
||||
},
|
||||
setOrientation : func(orientation) {
|
||||
|
@ -129,10 +60,10 @@ var NavigationMapController =
|
|||
},
|
||||
updateVisibility : func() {
|
||||
# Determine which layers should be visible.
|
||||
foreach (var layer_name; keys(me.LAYER_RANGES)) {
|
||||
var layer = me.LAYER_RANGES[layer_name];
|
||||
foreach (var layer_name; me.page.mfd.ConfigStore.getLayerNames()) {
|
||||
var layer = me.page.mfd.ConfigStore.getLayer(layer_name);
|
||||
if (layer.enabled and
|
||||
(me.RANGES[me.current_zoom].range <= layer.range) and
|
||||
(fg1000.RANGES[me.current_zoom].range <= layer.range) and
|
||||
(me.declutter <= layer.declutter) )
|
||||
{
|
||||
me.page.MFDMap.getLayer(layer_name).setVisible(1);
|
||||
|
@ -141,15 +72,11 @@ var NavigationMapController =
|
|||
}
|
||||
}
|
||||
},
|
||||
configureLayer : func(layer, enabled, range) {
|
||||
me.LAYER_RANGES[layer].enabled = enabled;
|
||||
me.LAYER_RANGES[layer].range = math.min(range, me.LAYER_RANGES[layer].max_range);
|
||||
},
|
||||
isEnabled : func(layer) {
|
||||
return me.LAYER_RANGES[layer].enabled;
|
||||
return me.page.mfd.ConfigStore.isLayerEnabled(layer);
|
||||
},
|
||||
toggleLayer : func(layer) {
|
||||
me.LAYER_RANGES[layer].enabled = ! me.LAYER_RANGES[layer].enabled;
|
||||
me.page.mfd.ConfigStore.toggleLayerEnabled(layer);
|
||||
me.updateVisibility();
|
||||
},
|
||||
|
||||
|
@ -162,6 +89,10 @@ var NavigationMapController =
|
|||
me.updateVisibility();
|
||||
},
|
||||
|
||||
getDCLTRTitle : func() {
|
||||
return me.DCLTR[me.declutter];
|
||||
},
|
||||
|
||||
# Increment through the AIRWAYS levels. At present this doesn't do anything
|
||||
# except change the label. It should enable/disable different airways
|
||||
# information.
|
||||
|
@ -171,13 +102,16 @@ var NavigationMapController =
|
|||
device.updateMenus();
|
||||
me.updateVisibility();
|
||||
},
|
||||
getAIRWAYSTitle : func() {
|
||||
return me.AIRWAYS[me.airways];
|
||||
},
|
||||
|
||||
# Set the DTO line target
|
||||
setDTOLineTarget : func(lat, lon) {
|
||||
me.page.MFDMap.getLayer("DTO").controller.setTarget(lat,lon);
|
||||
},
|
||||
enableDTO : func(enable) {
|
||||
me.LAYER_RANGES["DTO"].enabled = enable;
|
||||
me.page.mfd.ConfigStore.setLayerEnabled("DTO", enable);
|
||||
me.updateVisibility();
|
||||
},
|
||||
|
||||
|
|
|
@ -28,6 +28,10 @@ var PFDInstruments =
|
|||
magenta : [1, 0, 1],
|
||||
},
|
||||
|
||||
CDI_SOURCE : [ "GPS", "NAV1", "NAV2" ],
|
||||
|
||||
BRG_SOURCE : ["OFF", "NAV1", "NAV2", "GPS", "ADF"],
|
||||
|
||||
new : func (mfd, myCanvas, device, svg)
|
||||
{
|
||||
var obj = {
|
||||
|
@ -38,7 +42,7 @@ var PFDInstruments =
|
|||
|
||||
_ias_already_exceeded : 0,
|
||||
_windDataDisplay : 0,
|
||||
_CDISource : "OFF",
|
||||
_CDISource : "GPS",
|
||||
_BRG1 : "OFF",
|
||||
_BRG2 : "OFF",
|
||||
_DME : 0,
|
||||
|
@ -46,6 +50,7 @@ var PFDInstruments =
|
|||
_Map : 0,
|
||||
_Multiline : 0,
|
||||
_annunciation : 0,
|
||||
|
||||
};
|
||||
|
||||
# Hide various elements for the moment. TODO - implement
|
||||
|
@ -78,12 +83,17 @@ var PFDInstruments =
|
|||
obj.device.svg.getElementById("PFDInstruments" ~ id).set("clip", clip);
|
||||
}
|
||||
|
||||
#obj.insetMap = fg1000.NavMap.new(obj, [130,170], "rect(-160px, 160px, 160px, -160px)", -100, 2);
|
||||
obj._SVGGroup.setInt("z-index", 10);
|
||||
#obj._SVGGroup.setVisible(0);
|
||||
#obj.insetMap = fg1000.NavMap.new(obj, [119,601], "rect(-109px, 109px, 109px, -109px)", 50, 2);
|
||||
obj.insetMap = fg1000.NavMap.new(obj, obj.getElement("PFD-Map-Display"), [119,601], "rect(-109px, 109px, 109px, -109px)", 0, 2);
|
||||
|
||||
#obj.topMenu(device, obj, nil);
|
||||
|
||||
obj.setController(fg1000.PFDInstrumentsController.new(obj, svg));
|
||||
obj.setWindDisplay(0);
|
||||
obj.setCDISource("OFF");
|
||||
obj.setCDISource("GPS");
|
||||
obj.setBRG1("OFF");
|
||||
obj.setBRG2("OFF");
|
||||
obj.setDME(0);
|
||||
|
@ -91,6 +101,10 @@ var PFDInstruments =
|
|||
obj.setMultiline(0);
|
||||
obj.setAnnunciation(0);
|
||||
obj.setOMI("");
|
||||
obj.setInsetMapVisible(0);
|
||||
obj.updateHDG(0);
|
||||
obj.updateSelectedALT(0);
|
||||
obj.updateCRS(0);
|
||||
|
||||
return obj;
|
||||
},
|
||||
|
@ -99,11 +113,11 @@ var PFDInstruments =
|
|||
topMenu : func(device, pg, menuitem) {
|
||||
pg.clearMenu();
|
||||
pg.resetMenuColors();
|
||||
pg.addMenuItem(1, "INSET", pg); # TODO
|
||||
pg.addMenuItem(1, "INSET", pg, pg.mfd.PFDInstruments.insetMenu);
|
||||
pg.addMenuItem(3, "PFD", pg, pg.mfd.PFDInstruments.PFDMenu);
|
||||
pg.addMenuItem(4, "OBS", pg); # TODO
|
||||
pg.addMenuItem(5, "CDI", pg); # TODO
|
||||
pg.addMenuItem(6, "DME", pg); # TODO
|
||||
pg.addMenuItem(5, "CDI", pg, pg.incrCDI);
|
||||
#pg.addMenuItem(6, "DME", pg, func(dev, pg, mi) { pg.toggleDME(); } ); # TODO
|
||||
pg.addMenuItem(7, "XPDR", pg); # TODO
|
||||
pg.addMenuItem(8, "IDENT", pg); # TODO
|
||||
pg.addMenuItem(9, "TMR/REF", pg); # TODO
|
||||
|
@ -112,24 +126,121 @@ var PFDInstruments =
|
|||
device.updateMenus();
|
||||
},
|
||||
|
||||
incrCDI : func(dev, pg, mi) {
|
||||
var idx = -1;
|
||||
for (var i = 0; i < size(PFDInstruments.CDI_SOURCE); i = i + 1) {
|
||||
if (PFDInstruments.CDI_SOURCE[i] == pg._CDISource) {
|
||||
idx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (idx == -1) die("Unabled to increment CDI. _CDISource:" ~ me._CDISource);
|
||||
|
||||
idx = math.mod(idx + 1, size(PFDInstruments.CDI_SOURCE));
|
||||
pg.setCDISource(PFDInstruments.CDI_SOURCE[idx]);
|
||||
},
|
||||
|
||||
insetMenu : func(device, pg, menuitem) {
|
||||
|
||||
# Switch on the inset Map
|
||||
pg.setInsetMapVisible(1);
|
||||
|
||||
pg.clearMenu();
|
||||
pg.resetMenuColors();
|
||||
pg.addMenuItem(0, "OFF", pg, func(dev, pg, mi) { pg.setInsetMapVisible(0); }); # TODO
|
||||
pg.addMenuItem(1, "DCLTR", pg,
|
||||
func(dev, pg, mi) { pg.insetMap.incrDCLTR(dev, mi); device.updateMenus(); },
|
||||
func(svg, mi) { pg.displayDCLTR(svg, mi); },
|
||||
);
|
||||
#pg.addMenuItem(2, "WXLGND", pg); # Optional
|
||||
|
||||
# TODO: Support TRFC-1 to add traffic layer, TRFC-2 to just display a traffic map
|
||||
pg.addMenuItem(3, "TRAFFIC", pg,
|
||||
func(dev, pg, mi) { pg.insetMap.toggleLayer("TFC"); device.updateMenus(); }, # callback
|
||||
func(svg, mi) { pg.display_toggle(device, svg, mi, "TFC"); }
|
||||
);
|
||||
|
||||
pg.addMenuItem(4, "TOPO", pg,
|
||||
func(dev, pg, mi) { pg.insetMap.toggleLayer("STAMEN"); device.updateMenus(); }, # callback
|
||||
func(svg, mi) { pg.display_toggle(device, svg, mi, "STAMEN"); }
|
||||
);
|
||||
|
||||
pg.addMenuItem(5, "TERRAIN", pg,
|
||||
func(dev, pg, mi) { pg.insetMap.toggleLayer("STAMEN_terrain"); device.updateMenus(); }, # callback
|
||||
func(svg, mi) { pg.display_toggle(device, svg, mi, "STAMEN_terrain"); }
|
||||
);
|
||||
#pg.addMenuItem(6, "STRMSCP", pg); # TODO
|
||||
#pg.addMenuItem(7, "NEXRAD", pg); # TODO
|
||||
#pg.addMenuItem(8, "XM LTNG", pg); # TODO
|
||||
#pg.addMenuItem(9, "METAR", pg); # TODO
|
||||
pg.addMenuItem(10, "BACK", pg, pg.mfd.PFDInstruments.topMenu);
|
||||
pg.addMenuItem(11, "ALERTS", pg); # TODO
|
||||
device.updateMenus();
|
||||
},
|
||||
|
||||
displayDCLTR : func(svg, mi) {
|
||||
mi.title = pg.mfd.PFDInstruments.insetMap.getDCLTRTitle();
|
||||
svg.setText(mi.title);
|
||||
svg.setVisible(1);
|
||||
},
|
||||
|
||||
# Display map toggle softkeys which change color depending
|
||||
# on whether a particular layer is enabled or not.
|
||||
display_toggle : func(device, svg, mi, layer) {
|
||||
var bg_name = sprintf("SoftKey%d-bg",mi.menu_id);
|
||||
if (me.insetMap.isEnabled(layer)) {
|
||||
device.svg.getElementById(bg_name).setColorFill(0.5,0.5,0.5);
|
||||
svg.setColor(0.0,0.0,0.0);
|
||||
} else {
|
||||
device.svg.getElementById(bg_name).setColorFill(0.0,0.0,0.0);
|
||||
svg.setColor(1.0,1.0,1.0);
|
||||
}
|
||||
svg.setText(mi.title);
|
||||
svg.setVisible(1); # display function
|
||||
},
|
||||
|
||||
PFDMenu : func(device, pg, menuitem) {
|
||||
pg.clearMenu();
|
||||
pg.resetMenuColors();
|
||||
pg.addMenuItem(0, "SYN VIS", pg); # TODO
|
||||
pg.addMenuItem(1, "DFLTS", pg);
|
||||
pg.addMenuItem(2, "WIND", pg, pg.mfd.PFDInstruments.windMenu);
|
||||
pg.addMenuItem(3, "DME", pg); # TODO
|
||||
pg.addMenuItem(4, "BRG1", pg); # TODO
|
||||
#pg.addMenuItem(3, "DME", pg); # TODO
|
||||
pg.addMenuItem(4, "BRG1", pg, pg.mfd.PFDInstruments.incrBRG1); # TODO
|
||||
pg.addMenuItem(5, "HSI FRMT", pg); # TODO
|
||||
pg.addMenuItem(6, "BRG2", pg); # TODO
|
||||
pg.addMenuItem(6, "BRG2", pg, pg.mfd.PFDInstruments.incrBRG2); # TODO
|
||||
#pg.addMenuItem(8, "IDENT", pg); # TODO
|
||||
pg.addMenuItem(8, "ALT UNIT ", pg); # TODO
|
||||
pg.addMenuItem(9, "STD BARO", pg); # TODO
|
||||
pg.addMenuItem(9, "STD BARO", pg, func(dev, pg, mi) { pg.getController().setStdBaro(); } );
|
||||
pg.addMenuItem(10, "BACK", pg, pg.mfd.PFDInstruments.topMenu);
|
||||
pg.addMenuItem(11, "ALERTS", pg); # TODO
|
||||
device.updateMenus();
|
||||
},
|
||||
|
||||
incrBRG1 : func(dev, pg, mi) { pg.mfd.PFDInstruments.incrBRG("BRG1"); },
|
||||
incrBRG2 : func(dev, pg, mi) { pg.mfd.PFDInstruments.incrBRG("BRG2"); },
|
||||
|
||||
incrBRG : func(brg) {
|
||||
var curr = (brg == "BRG1" ? me.getBRG1() : me.getBRG2());
|
||||
var idx = -1;
|
||||
for (var i = 0; i < size(PFDInstruments.BRG_SOURCE); i = i + 1) {
|
||||
if (PFDInstruments.BRG_SOURCE[i] == curr) {
|
||||
idx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (idx == -1) die("Unabled to increment BRG. curr:" ~ curr);
|
||||
|
||||
idx = math.mod(idx + 1, size(PFDInstruments.BRG_SOURCE));
|
||||
if (brg == "BRG1") {
|
||||
me.setBRG1(PFDInstruments.BRG_SOURCE[idx]);
|
||||
} else {
|
||||
me.setBRG2(PFDInstruments.BRG_SOURCE[idx]);
|
||||
}
|
||||
},
|
||||
|
||||
windMenu : func(device, pg, menuitem) {
|
||||
pg.clearMenu();
|
||||
pg.resetMenuColors();
|
||||
|
@ -379,7 +490,7 @@ var PFDInstruments =
|
|||
updateBARO : func (baro) {
|
||||
# TODO: Support hPa and inhg
|
||||
#var fmt = me._baro_unit == "inhg" ? "%.2fin" : "%i%shPa";
|
||||
var fmt = "%.2fin";
|
||||
var fmt = "%.2fIN";
|
||||
me.setTextElement("BARO-text", sprintf(fmt, baro));
|
||||
},
|
||||
|
||||
|
@ -394,7 +505,6 @@ var PFDInstruments =
|
|||
},
|
||||
|
||||
updateHDG : func (hdg) {
|
||||
if (hdg == nil)
|
||||
me.getElement("Heading-bug").setRotation(hdg * D2R);
|
||||
me.setTextElement("SelectedHDG-text", sprintf("%03d°%s", hdg, ""));
|
||||
},
|
||||
|
@ -459,6 +569,10 @@ var PFDInstruments =
|
|||
}
|
||||
},
|
||||
|
||||
toggleDME : func() {
|
||||
me.setDME(! me._DME);
|
||||
},
|
||||
|
||||
setDME : func (enabled) {
|
||||
me._DME = enabled;
|
||||
me.getElement("DME1").setVisible(enabled);
|
||||
|
@ -518,34 +632,48 @@ var PFDInstruments =
|
|||
me._CDISource = source;
|
||||
},
|
||||
|
||||
updateCDI : func (heading, course, waypoint_valid, course_deviation_deg, deflection_dots, xtrk_nm, from) {
|
||||
updateCDI : func (heading, course, waypoint_valid, course_deviation_deg, deflection_dots, xtrk_nm, from, annun) {
|
||||
if (me._CDISource == "OFF") return;
|
||||
|
||||
var rot = (course - heading) * D2R;
|
||||
me.getElement("CDI")
|
||||
.setRotation(rot)
|
||||
.show();
|
||||
me.getElement("GPS-CTI-diamond")
|
||||
.setVisible(waypoint_valid)
|
||||
.setRotation(course_deviation_deg * D2R);
|
||||
|
||||
if ((me._CDISource == "GPS") and (deflection_dots > 2)) {
|
||||
# Only display the cross-track error if the error exceeds the maximum
|
||||
# deflection of two dots.
|
||||
me.getElement("CDI-GPS-XTK-text")
|
||||
.setText(sprintf("XTK %iNM", abs(xtrk_nm)))
|
||||
.show();
|
||||
} else {
|
||||
if (waypoint_valid == 0) {
|
||||
me.getElement(me._CDISource ~ "-CDI").hide();
|
||||
me.getElement(me._CDISource ~ "-FROM").hide();
|
||||
me.getElement(me._CDISource ~ "-TO").hide();
|
||||
me.getElement("CDI").setRotation(0);
|
||||
me.getElement("GPS-CTI-diamond").hide();
|
||||
me.getElement("CDI-GPS-XTK-text").hide();
|
||||
me.getElement("CDI-GPS-ANN-text").hide();
|
||||
} else {
|
||||
me.getElement(me._CDISource ~ "-CDI").show();
|
||||
|
||||
var rot = (course - heading) * D2R;
|
||||
me.getElement("CDI")
|
||||
.setRotation(rot)
|
||||
.show();
|
||||
me.getElement("GPS-CTI-diamond")
|
||||
.setVisible(waypoint_valid)
|
||||
.setRotation(course_deviation_deg * D2R);
|
||||
|
||||
if ((me._CDISource == "GPS") and (deflection_dots > 2)) {
|
||||
# Only display the cross-track error if the error exceeds the maximum
|
||||
# deflection of two dots.
|
||||
me.getElement("CDI-GPS-XTK-text")
|
||||
.setText(sprintf("XTK %iNM", abs(xtrk_nm)))
|
||||
.show();
|
||||
} else {
|
||||
me.getElement("CDI-GPS-XTK-text").hide();
|
||||
}
|
||||
|
||||
if (me._CDISource == "GPS") me.getElement("CDI-GPS-ANN-text").setText(annun).show();
|
||||
|
||||
var scale = math.clamp(deflection_dots, -2.4, 2.4);
|
||||
me.getElement(me._CDISource ~ "-CDI").setTranslation(65 * scale, 0);
|
||||
|
||||
# Display the appropriate TO/FROM indication for the selected source,
|
||||
# switching all others off.
|
||||
me.getElement(me._CDISource ~ "-TO").setVisible(from == 0);
|
||||
me.getElement(me._CDISource ~ "-FROM").setVisible(from);
|
||||
}
|
||||
|
||||
var scale = math.clamp(deflection_dots, -2.4, 2.4);
|
||||
me.getElement(me._CDISource ~ "-CDI").setTranslation(65 * scale, 0);
|
||||
|
||||
# Display the appropriate TO/FROM indication for the selected source,
|
||||
# switching all others off.
|
||||
me.getElement(me._CDISource ~ "-TO").setVisible(from == 0);
|
||||
me.getElement(me._CDISource ~ "-FROM").setVisible(from);
|
||||
},
|
||||
|
||||
# Update the wind display. There are three options:
|
||||
|
@ -631,5 +759,11 @@ var PFDInstruments =
|
|||
me.getElement("MarkerText").setText(omi);
|
||||
}
|
||||
me._OMI = omi;
|
||||
}
|
||||
},
|
||||
|
||||
setInsetMapVisible :func(enabled ) {
|
||||
me.getElement("PFD-Map").setVisible(enabled);
|
||||
me.getElement("PFD-Map-bg").setVisible(enabled);
|
||||
me.insetMap.setVisible(enabled);
|
||||
},
|
||||
};
|
||||
|
|
|
@ -17,11 +17,6 @@
|
|||
# PFDInstruments Controller
|
||||
var PFDInstrumentsController =
|
||||
{
|
||||
|
||||
|
||||
# Declutter levels.
|
||||
WIND : [ "DCLTR", "DCLTR-1", "DCLTR-2", "DCLTR-3"],
|
||||
|
||||
new : func (page, svg)
|
||||
{
|
||||
var obj = {
|
||||
|
@ -35,6 +30,13 @@ var PFDInstrumentsController =
|
|||
_selected_alt_ft : 0,
|
||||
_heading : 0,
|
||||
_source : "GPS",
|
||||
_from :0,
|
||||
_leg_id : "",
|
||||
_leg_bearing : 0,
|
||||
_leg_distance_nm : 0,
|
||||
_leg_deviation_deg : 0,
|
||||
_deflection_dots :0,
|
||||
_leg_xtrk_nm : 0,
|
||||
};
|
||||
|
||||
return obj;
|
||||
|
@ -74,6 +76,25 @@ var PFDInstrumentsController =
|
|||
}
|
||||
},
|
||||
|
||||
handleRange : func(val)
|
||||
{
|
||||
var incr_or_decr = (val > 0) ? me.page.insetMap.zoomIn() : me.page.insetMap.zoomOut();
|
||||
},
|
||||
|
||||
# Set the STD BARO to 29.92 in Hg
|
||||
setStdBaro : func() {
|
||||
var data = {};
|
||||
data["FMSPressureSettingInHG"] = 29.92;
|
||||
|
||||
var notification = notifications.PFDEventNotification.new(
|
||||
"MFD",
|
||||
me._page.mfd.getDeviceID(),
|
||||
notifications.PFDEventNotification.FMSData,
|
||||
data);
|
||||
|
||||
me.transmitter.NotifyAll(notification);
|
||||
},
|
||||
|
||||
# Handle update of the airdata information.
|
||||
# ADC data is produced periodically as an entire set
|
||||
handleADCData : func(data) {
|
||||
|
@ -97,6 +118,7 @@ var PFDInstrumentsController =
|
|||
me.page.updateVSI(data["ADCVerticalSpeedFPM"]);
|
||||
me.page.updateTAS(data["ADCTrueAirspeed"]);
|
||||
me.page.updateBARO(data["ADCPressureSettingInHG"]);
|
||||
|
||||
me.page.updateOAT(data["ADCOutsideAirTemperatureC"]);
|
||||
me.page.updateHSI(data["ADCHeadingDeg"]);
|
||||
me._heading = data["ADCHeadingDeg"];
|
||||
|
@ -113,31 +135,65 @@ var PFDInstrumentsController =
|
|||
return emesary.Transmitter.ReceiptStatus_OK;
|
||||
},
|
||||
|
||||
# Handle update to the FMS information.
|
||||
# Handle update to the FMS information. Note that there is no guarantee
|
||||
# that the entire set of FMS data will be available.
|
||||
handleFMSData : func(data) {
|
||||
|
||||
me.page.updateHDG(data["FMSHeadingBug"]);
|
||||
me.page.updateSelectedALT(data["FMSSelectedAlt"]);
|
||||
me._selected_alt_ft = data["FMSSelectedAlt"];
|
||||
|
||||
me.page.updateCRS(data["FMSLegBearing"]);
|
||||
|
||||
var from = 0;
|
||||
if (me._navSelected == 1) {
|
||||
from = data["FMSNav1From"];
|
||||
} else {
|
||||
from = data["FMSNav2From"];
|
||||
if (data["FMSHeadingBug"] != nil) me.page.updateHDG(data["FMSHeadingBug"]);
|
||||
if (data["FMSSelectedAlt"] != nil) {
|
||||
me.page.updateSelectedALT(data["FMSSelectedAlt"]);
|
||||
me._selected_alt_ft = data["FMSSelectedAlt"];
|
||||
}
|
||||
|
||||
me.page.updateCDI(
|
||||
heading: me._heading,
|
||||
course: data["FMSLegBearing"],
|
||||
waypoint_valid: 1,
|
||||
course_deviation_deg : data["FMSLegTrackErrorAngle"],
|
||||
deflection_dots : data["FMSLegCourseError"], # TODO: proper conversion depending on source, environment
|
||||
xtrk_nm : data["FMSLegCourseError"],
|
||||
from: from,
|
||||
);
|
||||
if (data["FMSLegValid"] == "false") {
|
||||
# No valid leg data, likely because there's no GPS course set
|
||||
me.page.updateCRS(0);
|
||||
me.page.updateCDI(
|
||||
heading: me._heading,
|
||||
course: 0,
|
||||
waypoint_valid: 0,
|
||||
course_deviation_deg : 0,
|
||||
deflection_dots : 0,
|
||||
xtrk_nm : 0,
|
||||
from: 0,
|
||||
);
|
||||
|
||||
# Update the bearing indicators with GPS data if that's what we're displaying.
|
||||
if (me.page.getBRG1() == "GPS") me.page.setBRG1("NONE", 0);
|
||||
if (me.page.getBRG2() == "GPS") me.page.setBRG2("NONE", 0);
|
||||
} else {
|
||||
|
||||
if (data["FMSLegBearing"] != nil) me.page.updateCRS(data["FMSLegBearing"]);
|
||||
|
||||
if (me._navSelected == 1) {
|
||||
if (data["FMSNav1From"] != nil) me._from = data["FMSNav1From"];
|
||||
} else {
|
||||
if (data["FMSNav2From"] != nil) me._from = data["FMSNav2From"];
|
||||
}
|
||||
|
||||
if (data["FMSLegID"] != nil) me._leg_id = data["FMSLegID"];
|
||||
if (data["FMSLegBearing"] != nil) me._leg_bearing = data["FMSLegBearing"];
|
||||
if (data["FMSLegTrackErrorAngle"] != nil) me._leg_deviation_deg = data["FMSLegTrackErrorAngle"];
|
||||
|
||||
# TODO: Proper cross-track error based on source and flight phase.
|
||||
if (data["FMSLegCourseError"] != nil) me.deflection_dots = data["FMSLegCourseError"];
|
||||
if (data["FMSLegCourseError"] != nil) me._leg_xtrk_nm = data["FMSLegCourseError"];
|
||||
|
||||
me.page.updateCDI(
|
||||
heading: me._heading,
|
||||
course: me._leg_bearing,
|
||||
waypoint_valid: (data["FMSLegValid"] == "true"),
|
||||
course_deviation_deg : me._leg_deviation_deg,
|
||||
deflection_dots : me._deflection_dots,
|
||||
xtrk_nm : me._leg_xtrk_nm,
|
||||
from: me._from,
|
||||
annun: "ENR"
|
||||
);
|
||||
|
||||
# Update the bearing indicators with GPS data if that's what we're displaying.
|
||||
if (me.page.getBRG1() == "GPS") me.page.setBRG1(me._leg_id, me._leg_bearing);
|
||||
if (me.page.getBRG2() == "GPS") me.page.setBRG2(me._leg_id, me._leg_bearing);
|
||||
}
|
||||
|
||||
return emesary.Transmitter.ReceiptStatus_OK;
|
||||
},
|
||||
|
|
|
@ -33,6 +33,9 @@ var SurroundController =
|
|||
_nav1standby : 0.0,
|
||||
_nav2active : 0.0,
|
||||
_nav2standby : 0.0,
|
||||
_pressure_settings_inhg : 0.0,
|
||||
_selected_alt_ft : 0.0,
|
||||
_heading_bug_deg : 0.0,
|
||||
};
|
||||
|
||||
obj.RegisterWithEmesary();
|
||||
|
@ -54,6 +57,17 @@ var SurroundController =
|
|||
me.transmitter.NotifyAll(notification);
|
||||
},
|
||||
|
||||
# Helper function to notify the Emesary bridge of a FMSData update.
|
||||
sendFMSDataNotification : func(data) {
|
||||
var notification = notifications.PFDEventNotification.new(
|
||||
"MFD",
|
||||
me._page.mfd.getDeviceID(),
|
||||
notifications.PFDEventNotification.FMSData,
|
||||
data);
|
||||
|
||||
me.transmitter.NotifyAll(notification);
|
||||
},
|
||||
|
||||
handleNavComData : func(data) {
|
||||
|
||||
# Store off particularly important data for control.
|
||||
|
@ -75,6 +89,15 @@ var SurroundController =
|
|||
return emesary.Transmitter.ReceiptStatus_OK;
|
||||
},
|
||||
|
||||
handleFMSADCData : func(data) {
|
||||
if (data["ADCPressureSettingInHG"] != nil) me._pressure_settings_inhg = data["ADCPressureSettingInHG"];
|
||||
if (data["FMSSelectedAlt"] != nil) me._selected_alt_ft = data["FMSSelectedAlt"];
|
||||
if (data["FMSHeadingBug"] != nil) me._heading_bug_deg = data["FMSHeadingBug"];
|
||||
|
||||
# Pass FMS and ADC data straight to the page to display in the header fields
|
||||
me._page.updateHeaderData(data);
|
||||
return emesary.Transmitter.ReceiptStatus_OK;
|
||||
},
|
||||
|
||||
#
|
||||
# Handle the various COM and NAV controls at the top left and top right of the Fascia
|
||||
|
@ -334,6 +357,45 @@ var SurroundController =
|
|||
handleComVolToggle : func (value) {
|
||||
},
|
||||
|
||||
handleBaro : func(value) {
|
||||
var incr_or_decr = (value > 0) ? 1 : -1;
|
||||
var press = me._pressure_settings_inhg + (incr_or_decr * 0.01);
|
||||
var data = {};
|
||||
data["FMSPressureSettingInHG"] = sprintf("%.2f", press);
|
||||
me.sendFMSDataNotification(data);
|
||||
return emesary.Transmitter.ReceiptStatus_Finished;
|
||||
},
|
||||
|
||||
handleAltInner : func(value) {
|
||||
var incr_or_decr = (value > 0) ? 1 : -1;
|
||||
var alt = int(me._selected_alt_ft + incr_or_decr * 100);
|
||||
if (alt < 0) alt = 0;
|
||||
var data = {};
|
||||
data["FMSSelectedAlt"] = alt;
|
||||
me.sendFMSDataNotification(data);
|
||||
return emesary.Transmitter.ReceiptStatus_Finished;
|
||||
},
|
||||
|
||||
handleAltOuter : func(value) {
|
||||
var incr_or_decr = (value > 0) ? 1 : -1;
|
||||
var alt = int(me._selected_alt_ft + incr_or_decr * 1000);
|
||||
if (alt < 0) alt = 0;
|
||||
var data = {};
|
||||
data["FMSSelectedAlt"] = alt;
|
||||
me.sendFMSDataNotification(data);
|
||||
return emesary.Transmitter.ReceiptStatus_Finished;
|
||||
},
|
||||
|
||||
handleHeading : func(value) {
|
||||
var incr_or_decr = (value > 0) ? 1 : -1;
|
||||
var hdg = me._heading_bug_deg + incr_or_decr;
|
||||
hdg = math.mod(hdg, 360);
|
||||
var data = {};
|
||||
data["FMSHeadingBug"] = sprintf("%i", hdg);
|
||||
me.sendFMSDataNotification(data);
|
||||
return emesary.Transmitter.ReceiptStatus_Finished;
|
||||
},
|
||||
|
||||
# These methods are slightly unusual in that they are called by other
|
||||
# controllers when the CRSR is not active. Hence they aren't referenced
|
||||
# in the RegisterWithEmesary call below.
|
||||
|
@ -381,10 +443,8 @@ var SurroundController =
|
|||
(notification.Event_Id == notifications.PFDEventNotification.ADCData) )
|
||||
and notification.EventParameter != nil)
|
||||
{
|
||||
# Pass FMS and ADC data straight to the page to display in the header fields
|
||||
return controller._page.updateHeaderData(notification.EventParameter);
|
||||
return controller.handleFMSADCData(notification.EventParameter);
|
||||
}
|
||||
|
||||
}
|
||||
return emesary.Transmitter.ReceiptStatus_NotProcessed;
|
||||
};
|
||||
|
|
239
Aircraft/Instruments-3d/FG1000/Nasal/NavMap.nas
Normal file
239
Aircraft/Instruments-3d/FG1000/Nasal/NavMap.nas
Normal file
|
@ -0,0 +1,239 @@
|
|||
# Copyright 2018 Stuart Buchanan
|
||||
# This file is part of FlightGear.
|
||||
#
|
||||
# FlightGear is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# FlightGear is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with FlightGear. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# Common Navigation map functions
|
||||
var NavMap = {
|
||||
|
||||
# Declutter levels.
|
||||
DCLTR : [ "DCLTR", "DCLTR-1", "DCLTR-2", "DCLTR-3"],
|
||||
|
||||
# Airways levels.
|
||||
AIRWAYS : [ "AIRWAYS", "AIRWY ON", "AIRWY LO", "AIRWY HI"],
|
||||
|
||||
new : func(page, element, center, clip="", zindex=0, vis_shift=0 )
|
||||
{
|
||||
var obj = {
|
||||
parents : [ NavMap ],
|
||||
_group : page.getGroup(),
|
||||
_svg : page.getSVG(),
|
||||
_page : page,
|
||||
_pageName : page.getPageName(),
|
||||
current_zoom : 13,
|
||||
declutter : 0,
|
||||
airways : 0,
|
||||
vis_shift : vis_shift,
|
||||
};
|
||||
|
||||
element.setTranslation(center[0], center[1]);
|
||||
|
||||
obj.Styles = fg1000.NavigationMapStyles.new();
|
||||
obj.Options = fg1000.NavigationMapOptions.new();
|
||||
|
||||
obj.Map = element.createChild("map");
|
||||
obj.Map.setScreenRange(689/2.0);
|
||||
|
||||
obj._rangeDisplay = obj._svg.getElementById(obj._pageName ~ "RangeDisplay");
|
||||
if (obj._rangeDisplay == nil) die("Unable to find element " ~ obj._pageName ~ "RangeDisplay");
|
||||
|
||||
obj._orientationDisplay = obj._svg.getElementById(obj._pageName ~ "OrientationDisplay");
|
||||
if (obj._orientationDisplay == nil) die("Unable to find element " ~ obj._pageName ~ "OrientationDisplay");
|
||||
|
||||
# Initialize the controllers:
|
||||
var ctrl_ns = canvas.Map.Controller.get("Aircraft position");
|
||||
var source = ctrl_ns.SOURCES["current-pos"];
|
||||
if (source == nil) {
|
||||
# TODO: amend
|
||||
var source = ctrl_ns.SOURCES["current-pos"] = {
|
||||
getPosition: func subvec(geo.aircraft_position().latlon(), 0, 2),
|
||||
getAltitude: func getprop('/position/altitude-ft'),
|
||||
getHeading: func {
|
||||
if (me.aircraft_heading)
|
||||
getprop('/orientation/heading-deg')
|
||||
else 0
|
||||
},
|
||||
aircraft_heading: 1,
|
||||
};
|
||||
}
|
||||
setlistener("/sim/gui/dialogs/map-canvas/aircraft-heading-up", func(n) {
|
||||
source.aircraft_heading = n.getBoolValue();
|
||||
}, 1);
|
||||
# Make it move with our aircraft:
|
||||
obj.Map.setController("Aircraft position", "current-pos"); # from aircraftpos.controller
|
||||
|
||||
if (clip != "") {
|
||||
obj.Map.set("clip-frame", canvas.Element.LOCAL);
|
||||
obj.Map.set("clip", clip);
|
||||
}
|
||||
|
||||
if (zindex != 0) {
|
||||
element.setInt("z-index", zindex);
|
||||
}
|
||||
|
||||
var r = func(name,vis=1,zindex=nil) return caller(0)[0];
|
||||
# TODO: we'll need some z-indexing here, right now it's just random
|
||||
foreach(var type; [r('GRID'),r('DTO',0),r('TFC',0),r('APT'),r('DME'),r('VOR_FG1000'),r('NDB'),r('FIX',0),r('GPS'),r('RTE'),r('WPT'),r('FLT'),r('WXR',0),r('APS')] ) {
|
||||
obj.Map.addLayer(
|
||||
factory: canvas.SymbolLayer,
|
||||
type_arg: type.name,
|
||||
priority: 4,
|
||||
style: obj.Styles.getStyle(type.name),
|
||||
options: obj.Options.getOption(type.name),
|
||||
visible: type.vis);
|
||||
}
|
||||
|
||||
foreach(var type; [ r('STAMEN_terrain'),r('STAMEN'), r('OpenAIP') ]) {
|
||||
obj.Map.addLayer(
|
||||
factory: canvas.OverlayLayer,
|
||||
type_arg: type.name,
|
||||
priority: 1,
|
||||
style: obj.Styles.getStyle(type.name),
|
||||
options: obj.Options.getOption(type.name),
|
||||
visible: 0);
|
||||
}
|
||||
|
||||
obj.setZoom(obj.current_zoom);
|
||||
obj.setOrientation(0);
|
||||
obj.Map.setVisible(0);
|
||||
return obj;
|
||||
},
|
||||
|
||||
toggleLayerVisible : func(name) {
|
||||
(var l = me.Map.getLayer(name)).setVisible(l.getVisible());
|
||||
},
|
||||
|
||||
setLayerVisible : func(name,n=1) {
|
||||
me.Map.getLayer(name).setVisible(n);
|
||||
},
|
||||
|
||||
setRange : func(range, label) {
|
||||
me.Map.setRange(range);
|
||||
me._rangeDisplay.setText(label);
|
||||
},
|
||||
|
||||
setOrientation : func(orientation) {
|
||||
# TODO - implment this
|
||||
me._orientationDisplay.setText(fg1000.ORIENTATIONS[orientation].label);
|
||||
},
|
||||
|
||||
setScreenRange : func(range) {
|
||||
me.Map.setScreenRange(range);
|
||||
},
|
||||
|
||||
zoomIn : func() {
|
||||
me.setZoom(me.current_zoom -1);
|
||||
},
|
||||
|
||||
zoomOut : func() {
|
||||
me.setZoom(me.current_zoom +1);
|
||||
},
|
||||
|
||||
setZoom : func(zoom) {
|
||||
if ((zoom < 0) or (zoom > (size(fg1000.RANGES) - 1))) return;
|
||||
me.current_zoom = zoom;
|
||||
# Ranges above represent vertical ranges, but the display is a rectangle, so
|
||||
# we need to use the diagonal range of the 1024 x 689, which is 617px.
|
||||
# 617px is 1.8 x 689/2, so we need to increase the range values by x1.8
|
||||
me.setRange(fg1000.RANGES[zoom].range, fg1000.RANGES[zoom].label);
|
||||
me.updateVisibility();
|
||||
},
|
||||
|
||||
updateVisibility : func() {
|
||||
# Determine which layers should be visible.
|
||||
foreach (var layer_name; me._page.mfd.ConfigStore.getLayerNames()) {
|
||||
var layer = me._page.mfd.ConfigStore.getLayer(layer_name);
|
||||
|
||||
# Layers are only displayed if:
|
||||
# 1) the user has enabled them.
|
||||
# 2) The current zoom level is _less_ than the maximum range for the layer
|
||||
# (i.e. as the range gets larger, we remove layers). Note that for
|
||||
# inset maps, the range that items are removed is lower.
|
||||
# 3) They haven't been removed due to the declutter level.
|
||||
var effective_zoom = math.clamp(me.current_zoom + me.vis_shift, 0, size(fg1000.RANGES));
|
||||
var effective_range = fg1000.RANGES[effective_zoom].range;
|
||||
if (layer.enabled and
|
||||
(effective_range <= layer.range) and
|
||||
(me.declutter <= layer.declutter) )
|
||||
{
|
||||
me.Map.getLayer(layer_name).setVisible(1);
|
||||
} else {
|
||||
me.Map.getLayer(layer_name).setVisible(0);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
isEnabled : func(layer) {
|
||||
return me._page.mfd.ConfigStore.isLayerEnabled(layer);
|
||||
},
|
||||
|
||||
toggleLayer : func(layer) {
|
||||
me._page.mfd.ConfigStore.toggleLayerEnabled(layer);
|
||||
me.updateVisibility();
|
||||
},
|
||||
|
||||
# Increment through the de-clutter levels, which impact what layers are
|
||||
# displayed. We also need to update the declutter menu item.
|
||||
incrDCLTR : func(device, menuItem) {
|
||||
me.declutter = math.mod(me.declutter +1, 4);
|
||||
me.updateVisibility();
|
||||
return me.DCLTR[me.declutter];
|
||||
},
|
||||
|
||||
getDCLTRTitle : func() {
|
||||
return me.DCLTR[me.declutter];
|
||||
},
|
||||
|
||||
# Increment through the AIRWAYS levels. At present this doesn't do anything
|
||||
# except change the label. It should enable/disable different airways
|
||||
# information.
|
||||
incrAIRWAYS : func(device, menuItem) {
|
||||
me.airways = math.mod(me.airways +1, 4);
|
||||
me.updateVisibility();
|
||||
return me.AIRWAYS[me.airways];
|
||||
},
|
||||
|
||||
getAIRWAYSTitle : func() {
|
||||
return me.AIRWAYS[me.airways];
|
||||
},
|
||||
|
||||
# Set the DTO line target
|
||||
setDTOLineTarget : func(lat, lon) {
|
||||
me.Map.getLayer("DTO").controller.setTarget(lat,lon);
|
||||
},
|
||||
enableDTO : func(enable) {
|
||||
me._page.mfd.ConfigStore.setLayerEnabled("DTO", enable);
|
||||
me.updateVisibility();
|
||||
},
|
||||
|
||||
handleRange : func(val)
|
||||
{
|
||||
var incr_or_decr = (val > 0) ? 1 : -1;
|
||||
me.setZoom(me.current_zoom + incr_or_decr);
|
||||
},
|
||||
|
||||
getMap : func() {
|
||||
return me.Map;
|
||||
},
|
||||
show : func() {
|
||||
me.Map.show();
|
||||
},
|
||||
hide : func() {
|
||||
me.Map.hide();
|
||||
},
|
||||
setVisible : func(visible) {
|
||||
me.Map.setVisible(visible);
|
||||
}
|
||||
|
||||
};
|
Loading…
Reference in a new issue