1
0
Fork 0

FG1000 HSI BRG/CDI controls

- BRG1/BRG2 now correctly show direction and distance to NAV1/NAV2/GPS/ADF
- CDI displays correctly against course, switchable between GPS/NAV1/NAV2
This commit is contained in:
Stuart Buchanan 2018-03-16 15:21:39 +00:00
parent 74d1257f5d
commit 7b526adb69
9 changed files with 274 additions and 179 deletions

View file

@ -46,7 +46,8 @@ var GenericADCPublisher =
obj.addPropMap("ADCSlipSkid", "/instrumentation/slip-skid-ball/indicated-slip-skid");
# Assume an accurate solid-state compass
obj.addPropMap("ADCHeadingDeg", "/orientation/heading-magnetic-deg");
obj.addPropMap("ADCHeadingMagneticDeg", "/orientation/heading-magnetic-deg");
obj.addPropMap("ADCMagneticVariationDeg", "/environment/magnetic-variation-deg");
obj.addPropMap("ADCAltitudeFT", "/instrumentation/altimeter/indicated-altitude-ft");
obj.addPropMap("ADCPressureSettingInHG", "/instrumentation/altimeter/setting-inhg");

View file

@ -31,7 +31,7 @@ var GenericFMSPublisher =
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("FMSLegBearingMagDeg", "/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/wp/wp[1]/desired-course-deg");
@ -57,7 +57,7 @@ var GenericFMSPublisher =
# 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["FMSLegBearingMagDeg"] == -9999) gpsdata["FMSLegBearingMagDeg"] = nil;
if (gpsdata["FMSLegDistanceNM"] == -1) gpsdata["FMSLegDistanceNM"] = nil;
# A couple of calculated values used by the MFD Header display

View file

@ -68,6 +68,13 @@ var GenericNavComPublisher =
obj._periodicPublisher.addPropMap("Nav1HeadingDeg", "/instrumentation/nav/heading-deg");
obj._periodicPublisher.addPropMap("Nav1RadialDeg", "/instrumentation/nav/radials/selected-deg");
obj._periodicPublisher.addPropMap("Nav1DistanceMeters", "/instrumentation/nav/nav-distance");
obj._periodicPublisher.addPropMap("Nav1CourseDeviationDeg", "/instrumentation/nav/crosstrack-heading-error-deg");
obj._periodicPublisher.addPropMap("Nav1CrosstrackErrorM", "/instrumentation/nav/crosstrack-error-m");
obj._periodicPublisher.addPropMap("Nav1Localizer", "/instrumentation/nav/nav-loc");
obj._periodicPublisher.addPropMap("Nav1Deflection", "/instrumentation/nav/heading-needle-deflection-norm");
obj._periodicPublisher.addPropMap("Nav1From", "/instrumentation/nav/from-flag");
obj._triggeredPublisher.addPropMap("Nav1Volume", "/instrumentation/nav/nav-volume");
obj._triggeredPublisher.addPropMap("Nav1AudioID", "/instrumentation/nav/audio-btn");
obj._triggeredPublisher.addPropMap("Nav1Serviceable", "/instrumentation/nav/operable");
@ -79,6 +86,12 @@ var GenericNavComPublisher =
obj._periodicPublisher.addPropMap("Nav2HeadingDeg", "/instrumentation/nav[1]/heading-deg");
obj._periodicPublisher.addPropMap("Nav2RadialDeg", "/instrumentation/nav[1]/radials/selected-deg");
obj._periodicPublisher.addPropMap("Nav2DistanceMeters", "/instrumentation/nav[1]/nav-distance");
obj._periodicPublisher.addPropMap("Nav2CourseDeviationDeg", "/instrumentation/nav[1]/crosstrack-heading-error-deg");
obj._periodicPublisher.addPropMap("Nav2CrosstrackErrorM", "/instrumentation/nav[1]/crosstrack-error-m");
obj._periodicPublisher.addPropMap("Nav2Localizer", "/instrumentation/nav[1]/nav-loc");
obj._periodicPublisher.addPropMap("Nav2Deflection", "/instrumentation/nav[1]/heading-needle-deflection-norm");
obj._periodicPublisher.addPropMap("Nav2From", "/instrumentation/nav/from-flag");
obj._triggeredPublisher.addPropMap("Nav2Volume", "/instrumentation/nav[1]/nav-volume");
obj._triggeredPublisher.addPropMap("Nav2AudioID", "/instrumentation/nav[1]/audio-btn");
obj._triggeredPublisher.addPropMap("Nav2Serviceable", "/instrumentation/nav[1]/operable");

View file

@ -55,6 +55,7 @@ var GenericNavComUpdater =
obj.addPropMap("Nav1SelectedFreq", "/instrumentation/nav/frequencies/selected-mhz");
obj.addPropMap("Nav1StandbyFreq", "/instrumentation/nav/frequencies/standby-mhz");
obj.addPropMap("Nav1ID", "/instrumentation/nav/nav-id");
obj.addPropMap("Nav1RadialDeg", "/instrumentation/nav/radials/selected-deg");
obj.addPropMap("Nav1Volume", "/instrumentation/nav/nav-volume");
obj.addPropMap("Nav1AudioID", "/instrumentation/nav/audio-btn");
obj.addPropMap("Nav1Serviceable", "/instrumentation/nav/operable");
@ -62,6 +63,7 @@ var GenericNavComUpdater =
obj.addPropMap("Nav2SelectedFreq", "/instrumentation/nav[1]/frequencies/selected-mhz");
obj.addPropMap("Nav2StandbyFreq", "/instrumentation/nav[1]/frequencies/standby-mhz");
obj.addPropMap("Nav2ID", "/instrumentation/nav[1]/nav-id");
obj.addPropMap("Nav2RadialDeg", "/instrumentation/nav[1]/radials/selected-deg");
obj.addPropMap("Nav2Volume", "/instrumentation/nav[1]/nav-volume");
obj.addPropMap("Nav2AudioID", "/instrumentation/nav[1]/audio-btn");
obj.addPropMap("Nav2Serviceable", "/instrumentation/nav[1]/operable");

View file

@ -45,7 +45,7 @@ handleNavOuter : func (value) { return me.page.mfd.SurroundController.han
handleNavInner : func (value) { return me.page.mfd.SurroundController.handleNavInner(value); },
handleNavToggle : func (value) { return me.page.mfd.SurroundController.handleNavToggle(value); },
handleHeading : func (value) { return me.page.mfd.SurroundController.handleHeading(value); },
handleHeadingPress : func (value) { return emesary.Transmitter.ReceiptStatus_NotProcessed; },
handleHeadingPress : func (value) { return me.page.mfd.SurroundController.handleHeadingPress(value); },
# Joystick
handleRange : func (value) { return emesary.Transmitter.ReceiptStatus_NotProcessed; },
@ -54,8 +54,8 @@ handleJoystickHorizontal : func (value) { return emesary.Transmitter.ReceiptStat
#CRS/BARO
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; },
handleCRS : func (value) { return me.page.mfd.SurroundController.handleCRS(value); },
handleCRSCenter : func (value) { return me.page.mfd.SurroundController.handleCRSCenter(value); },
handleComOuter : func (value) { return me.page.mfd.SurroundController.handleComOuter(value); },
handleComInner : func (value) { return me.page.mfd.SurroundController.handleComInner(value); },

View file

@ -28,10 +28,6 @@ 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 = {
@ -42,7 +38,6 @@ var PFDInstruments =
_ias_already_exceeded : 0,
_windDataDisplay : 0,
_CDISource : "GPS",
_BRG1 : "OFF",
_BRG2 : "OFF",
_DME : 0,
@ -116,7 +111,7 @@ var PFDInstruments =
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, pg.incrCDI);
pg.addMenuItem(5, "CDI", pg, func(dev, pg, mi) { pg.getController().incrCDISource(); } );
#pg.addMenuItem(6, "DME", pg, func(dev, pg, mi) { pg.toggleDME(); } ); # TODO
pg.addMenuItem(7, "XPDR", pg); # TODO
pg.addMenuItem(8, "IDENT", pg); # TODO
@ -126,23 +121,7 @@ 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);
@ -207,9 +186,9 @@ var PFDInstruments =
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, pg.mfd.PFDInstruments.incrBRG1); # TODO
pg.addMenuItem(4, "BRG1", pg, func(dev, pg, mi) { pg.getController().incrBRG1(); });
pg.addMenuItem(5, "HSI FRMT", pg); # TODO
pg.addMenuItem(6, "BRG2", pg, pg.mfd.PFDInstruments.incrBRG2); # TODO
pg.addMenuItem(6, "BRG2", pg, func(dev, pg, mi) { pg.getController().incrBRG2(); });
#pg.addMenuItem(8, "IDENT", pg); # TODO
pg.addMenuItem(8, "ALT UNIT ", pg); # TODO
pg.addMenuItem(9, "STD BARO", pg, func(dev, pg, mi) { pg.getController().setStdBaro(); } );
@ -218,29 +197,6 @@ var PFDInstruments =
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();
@ -513,31 +469,22 @@ var PFDInstruments =
updateCRS : func (crs) {
me.getElement("SelectedCRS-text")
.setText(sprintf("%03d°%s", crs, ""))
.setColor(me._CDISource == "GPS" ? me.COLORS.magenta : me.COLORS.green);
.setColor(me.getController().getCDISource() == "GPS" ? me.COLORS.magenta : me.COLORS.green);
},
updateSelectedALT : func (selected_alt) {
me.setTextElement("SelectedALT-text", sprintf("%i", selected_alt));
},
# Bearing (BRG) settings
# "OFF", "NAV1", "NAV2", "GPS", "ADF"
getBRG1 : func() { return me._BRG1; },
getBRG2 : func() { return me._BRG2; },
setBRG1 : func(option) { me._setBRG("BRG1",option); },
setBRG2 : func(option) { me._setBRG("BRG2",option); },
setBRG1 : func(option) {
me._BRG1 = option;
me._setBRG("BRG1",option);
},
setBRG2 : func(option) {
me._BRG2 = option;
me._setBRG("BRG2",option);
},
_setBRG : func (brg, option) {
if (option == "OFF") {
me.getElement(brg).hide();
me.getElement(brg ~ "-pointer").hide();
if ((me._BRG1 == "OFF") and (me._BRG2 == "OFF")) {
if ((me.getController().getBRG1() == "OFF") and (me.getController().getBRG2() == "OFF")) {
# Hide the circle if there are now BRGs selected
me.getElement("BRG-circle").hide();
}
} else {
@ -560,10 +507,10 @@ var PFDInstruments =
# Update BRG information
updateBRG1 : func(valid, id, dst, current_heading, brg_heading) {
me._updateBRG("BRG1", me._BRG1, valid, id, dst, current_heading, brg_heading);
me._updateBRG("BRG1", me.getController().getBRG1(), valid, id, dst, current_heading, brg_heading);
},
updateBRG2 : func(valid, id, dst, current_heading, brg_heading) {
me._updateBRG("BRG2", me._BRG2, valid, id, dst, current_heading, brg_heading);
me._updateBRG("BRG2", me.getController().getBRG2(), valid, id, dst, current_heading, brg_heading);
},
_updateBRG : func (brg, source, valid, id, dst, current_heading, brg_heading) {
if (source == "OFF") return;
@ -608,74 +555,45 @@ var PFDInstruments =
},
setCDISource : func(source) {
if (source == "OFF") {
foreach (var s; ["GPS", "NAV1", "NAV2"]) {
foreach (var t; ["pointer", "CDI", "FROM", "TO"]) {
me.getElement(s ~ "-" ~ t).hide();
}
}
me.getElement("CDI-GPS-ANN-text").hide();
me.getElement("CDI-GPS-XTK-text").hide();
me.getElement("CDI-SRC-text").hide();
me.getElement("CDI").hide();
me.getElement("GPS-CTI-diamond").hide();
me.getElement("SelectedCRS").hide();
} else {
me.getElement("CDI").show();
me.getElement("SelectedCRS").show();
if (source == "GPS") {
me.getElement("CDI-GPS-ANN-text").show();
me.getElement("GPS-CTI-diamond").show();
} else {
me.getElement("CDI-GPS-ANN-text").hide();
me.getElement("GPS-CTI-diamond").hide();
}
# Localizers are mapped to the NAV1/2 elements, but have reduced deflection
# and a different label.
me.getElement("CDI-SRC-text")
.setText(source)
.setColor(source == "GPS" ? me.COLORS.magenta : me.COLORS.green)
.show();
if (source == "LOC1") source = "NAV1";
if (source == "LOC2") source = "NAV2";
foreach (var s; ["GPS", "NAV1", "NAV2"]) {
me.getElement(s ~ "-pointer").setVisible(source == s);
me.getElement(s ~ "-CDI").setVisible(source == s);
me.getElement(s ~ "-FROM").setVisible(source == s);
me.getElement(s ~ "-TO").setVisible(source == s);
}
foreach (var s; ["GPS", "NAV1", "NAV2"]) {
me.getElement(s ~ "-pointer").setVisible(source == s);
me.getElement(s ~ "-CDI").setVisible(source == s);
me.getElement(s ~ "-FROM").setVisible(source == s);
me.getElement(s ~ "-TO").setVisible(source == s);
}
me._CDISource = source;
me.getElement("CDI-SRC-text")
.setText(source)
.setColor(source == "GPS" ? me.COLORS.magenta : me.COLORS.green)
.setVisible(source != "OFF");
},
updateCDI : func (heading, course, waypoint_valid, course_deviation_deg, deflection_dots, xtrk_nm, from, annun) {
if (me._CDISource == "OFF") return;
updateCDI : func (heading, course, waypoint_valid, course_deviation_deg, deflection_dots, xtrk_nm, from, annun, loc) {
var source = me.getController().getCDISource();
if (source == "OFF") return;
# While the user selects between GPS, NAV1, NAV2, we display localizers as LOC1 and LOC2
if ((source == "NAV1") and (loc == 1)) me.getElement("CDI-SRC-text").setText("LOC1");
if ((source == "NAV2") and (loc == 1)) me.getElement("CDI-SRC-text").setText("LOC2");
if (waypoint_valid == 0) {
me.getElement(me._CDISource ~ "-CDI").hide();
me.getElement(me._CDISource ~ "-FROM").hide();
me.getElement(me._CDISource ~ "-TO").hide();
me.getElement(me._CDISource ~ "-pointer").hide();
me.getElement(source ~ "-CDI").hide();
me.getElement(source ~ "-FROM").hide();
me.getElement(source ~ "-TO").hide();
me.getElement(source ~ "-pointer").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();
me.getElement("CDI-GPS-ANN-text").setText("NO DATA").show();
} else {
me.getElement(me._CDISource ~ "-CDI").show();
me.getElement(source ~ "-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);
me.getElement("GPS-CTI-diamond").setRotation(course_deviation_deg * D2R).setVisible(source == "GPS");
if ((me._CDISource == "GPS") and (abs(deflection_dots) > 2.0)) {
if ((source == "GPS") and (abs(deflection_dots) > 2.0)) {
# Only display the cross-track error if the error exceeds the maximum
# deflection of two dots.
me.getElement("CDI-GPS-XTK-text")
@ -685,15 +603,19 @@ var PFDInstruments =
me.getElement("CDI-GPS-XTK-text").hide();
}
if (me._CDISource == "GPS") me.getElement("CDI-GPS-ANN-text").setText(annun).show();
if (source == "GPS") {
me.getElement("CDI-GPS-ANN-text").setText(annun).show();
} else {
me.getElement("CDI-GPS-ANN-text").hide();
}
var scale = math.clamp(deflection_dots, -2.4, 2.4);
me.getElement(me._CDISource ~ "-CDI").setTranslation(80 * scale / 2.4, 0);
me.getElement(source ~ "-CDI").setTranslation(80 * scale / 2.4, 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);
me.getElement(source ~ "-TO").setVisible(from == 0);
me.getElement(source ~ "-FROM").setVisible(from);
}
},

View file

@ -1,3 +1,4 @@
# Copyright 2018 Stuart Buchanan
# This file is part of FlightGear.
#
@ -17,6 +18,9 @@
# PFDInstruments Controller
var PFDInstrumentsController =
{
CDI_SOURCE : [ "GPS", "NAV1", "NAV2" ],
BRG_SOURCE : ["OFF", "NAV1", "NAV2", "GPS", "ADF"],
new : func (page, svg)
{
var obj = {
@ -24,13 +28,19 @@ var PFDInstrumentsController =
_crsrToggle : 0,
_pfdrecipient : nil,
page : page,
_CDISource : 0,
_BRG1Source : 0,
_BRG2Source : 0,
_last_ias_kt : 0,
_last_alt_ft : 0,
_last_trend : systime(),
_selected_alt_ft : 0,
_heading : 0,
_source : "GPS",
_from :0,
_mag_var : 0,
_leg_from :0,
_leg_id : "",
_leg_bearing : 0,
_leg_distance_nm : 0,
@ -45,6 +55,11 @@ var PFDInstrumentsController =
_nav1_heading_deg :0.0,
_nav1_in_range : 0,
_nav1_distance_m :0,
_nav1_radial_deg : 0,
_nav1_in_range : 0,
_nav1_distance_m : 0,
_nav1_deviation_deg : 0,
_nav1_loc : 0,
_nav2_id : "",
_nav2_freq : 0.0,
@ -52,6 +67,11 @@ var PFDInstrumentsController =
_nav2_heading_deg : 0.0,
_nav2_in_range : 0,
_nav2_distance_m :0,
_nav2_radial_deg : 0,
_nav2_in_range : 0,
_nav2_distance_m : 0,
_nav2_deviation_deg : 0,
_nav2_loc : 0,
_adf_freq : 0.0,
_adf_in_range : 0,
@ -114,6 +134,43 @@ var PFDInstrumentsController =
me.transmitter.NotifyAll(notification);
},
incrCDISource : func() {
me._CDISource = math.mod(me._CDISource + 1, size(PFDInstrumentsController.CDI_SOURCE));
var src = PFDInstrumentsController.CDI_SOURCE[me._CDISource];
# If we're changing to NAV1 or NAV2, we also change the selected NAV.
if ((src == "NAV1") or (src == "NAV2")) {
var data = {};
data["NavSelected"] = (src == "NAV1") ? 1 : 2;
var notification = notifications.PFDEventNotification.new(
"MFD",
me._page.mfd.getDeviceID(),
notifications.PFDEventNotification.NavComData,
data);
me.transmitter.NotifyAll(notification);
}
me.page.setCDISource(src);
},
getCDISource : func() {
return PFDInstrumentsController.CDI_SOURCE[me._CDISource];
},
incrBRG1 : func() {
me._BRG1Source = math.mod(me._BRG1Source + 1, size(PFDInstrumentsController.BRG_SOURCE));
me.page.setBRG1(PFDInstrumentsController.BRG_SOURCE[me._BRG1Source]);
},
incrBRG2 : func() {
me._BRG2Source = math.mod(me._BRG2Source + 1, size(PFDInstrumentsController.BRG_SOURCE));
me.page.setBRG2(PFDInstrumentsController.BRG_SOURCE[me._BRG2Source]);
},
getBRG1 : func() { return PFDInstrumentsController.BRG_SOURCE[me._BRG1Source]; },
getBRG2 : func() { return PFDInstrumentsController.BRG_SOURCE[me._BRG2Source]; },
# Handle update of the airdata information.
# ADC data is produced periodically as an entire set
handleADCData : func(data) {
@ -139,13 +196,14 @@ var PFDInstrumentsController =
me.page.updateBARO(data["ADCPressureSettingInHG"]);
me.page.updateOAT(data["ADCOutsideAirTemperatureC"]);
me.page.updateHSI(data["ADCHeadingDeg"]);
me._heading = data["ADCHeadingDeg"];
me.page.updateHSI(data["ADCHeadingMagneticDeg"]);
me._heading_magnetic_deg = data["ADCHeadingMagneticDeg"];
me._mag_var = data["ADCMagneticVariationDeg"];
# If we're "flying" at < 10kts, then we won't have sufficient delta between
# airspeed and groundspeed to determine wind
me.page.updateWindData(
hdg : data["ADCHeadingDeg"],
hdg : data["ADCHeadingMagneticDeg"],
wind_hdg : data["ADCWindHeadingDeg"],
wind_spd : data ["ADCWindSpeedKt"],
no_data: (data["ADCIndicatedAirspeed"] < 1.0)
@ -166,53 +224,56 @@ var PFDInstrumentsController =
if (data["FMSLegValid"] != nil) me._leg_valid = data["FMSLegValid"];
if (me._leg_valid == 0) {
# 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.0,
xtrk_nm : 0,
from: 0,
annun: "NO DATA",
);
if (me._navSelected == 1) {
if (data["FMSNav1From"] != nil) me._leg_from = data["FMSNav1From"];
} else {
if (data["FMSNav2From"] != nil) me._leg_from = data["FMSNav2From"];
}
if (data["FMSLegBearing"] != nil) me.page.updateCRS(data["FMSLegBearing"]);
if (data["FMSLegID"] != nil) me._leg_id = data["FMSLegID"];
if (data["FMSLegBearingMagDeg"] != nil) me._leg_bearing = data["FMSLegBearingMagDeg"];
if (data["FMSLegDistanceNM"] != nil) me._leg_distance_nm = data["FMSLegDistanceNM"];
if (data["FMSLegTrackErrorAngle"] != nil) me._leg_deviation_deg = data["FMSLegTrackErrorAngle"];
if (me._navSelected == 1) {
if (data["FMSNav1From"] != nil) me._from = data["FMSNav1From"];
# TODO: Proper cross-track error based on source and flight phase.
if (data["FMSLegCourseError"] != nil) me._deflection_dots = data["FMSLegCourseError"] /2.0;
if (data["FMSLegCourseError"] != nil) me._leg_xtrk_nm = data["FMSLegCourseError"];
if (me.getCDISource() == "GPS") {
if (me._leg_valid == 0) {
# No valid leg data, likely because there's no GPS course set
me.page.updateCRS(0);
me.page.updateCDI(
heading: me._heading_magnetic_deg,
course: 0,
waypoint_valid: 0,
course_deviation_deg : 0,
deflection_dots : 0.0,
xtrk_nm : 0,
from: 0,
annun: "NO DATA",
loc : 0,
);
} else {
if (data["FMSNav2From"] != nil) me._from = data["FMSNav2From"];
me.page.updateCRS(me._leg_bearing);
me.page.updateCDI(
heading: me._heading_magnetic_deg,
course: me._leg_bearing,
waypoint_valid: me._leg_valid,
course_deviation_deg : me._leg_deviation_deg,
deflection_dots : me._deflection_dots,
xtrk_nm : me._leg_xtrk_nm,
from: me._leg_from,
annun: "ENR",
loc: 0,
);
}
if (data["FMSLegID"] != nil) me._leg_id = data["FMSLegID"];
if (data["FMSLegBearing"] != nil) me._leg_bearing = data["FMSLegBearing"];
if (data["FMSLegDistanceNM"] != nil) me._leg_distance_nm = data["FMSLegDistanceNM"];
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"] /2.0;
if (data["FMSLegCourseError"] != nil) me._leg_xtrk_nm = data["FMSLegCourseError"];
me.page.updateCDI(
heading: me._heading,
course: me._leg_bearing,
waypoint_valid: me._leg_valid,
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.updateBRG1(me._leg_valid, me._leg_id, me._leg_distance_nm, me._heading, me._leg_bearing);
if (me.page.getBRG2() == "GPS") me.page.updateBRG2(me._leg_valid, me._leg_id, me._leg_distance_nm, me._heading, me._leg_bearing);
if (me.getBRG1() == "GPS") me.page.updateBRG1(me._leg_valid, me._leg_id, me._leg_distance_nm, me._heading_magnetic_deg, me._leg_bearing);
if (me.getBRG2() == "GPS") me.page.updateBRG2(me._leg_valid, me._leg_id, me._leg_distance_nm, me._heading_magnetic_deg, me._leg_bearing);
return emesary.Transmitter.ReceiptStatus_OK;
},
@ -229,7 +290,13 @@ var PFDInstrumentsController =
if (data["Nav1RadialDeg"] != nil) me._nav1_radial_deg = data["Nav1RadialDeg"];
if (data["Nav1InRange"] != nil) me._nav1_in_range = data["Nav1InRange"];
if (data["Nav1DistanceMeters"] != nil) me._nav1_distance_m = data["Nav1DistanceMeters"];
if (data["Nav1CourseDeviationDeg"] != nil) me._nav1_deviation_deg = data["Nav1CourseDeviationDeg"];
# Deflection range is [-10,10], while deflection_dots is [-2.4, 2.4];
if (data["Nav1Deflection"] != nil) me._nav1_deflection = data["Nav1Deflection"] * 2.4;
if (data["Nav1CrosstrackErrorM"] != nil) me._nav1_crosstrack_m = data["Nav1CrosstrackErrorM"];
if (data["Nav1From"] != nil) me._nav1_from = data["Nav1From"];
if (data["Nav1Localizer"] != nil) me._nav1_loc = data["Nav1Localizer"];
if (data["Nav2SelectedFreq"] != nil) me._nav2_freq = data["Nav2SelectedFreq"];
if (data["Nav2ID"] != nil) me._nav2_id = data["Nav2ID"];
@ -237,18 +304,57 @@ var PFDInstrumentsController =
if (data["Nav2RadialDeg"] != nil) me._nav2_radial_deg = data["Nav2RadialDeg"];
if (data["Nav2InRange"] != nil) me._nav2_in_range = data["Nav2InRange"];
if (data["Nav2DistanceMeters"] != nil) me._nav2_distance_m = data["Nav2DistanceMeters"];
if (data["Nav2CourseDeviationDeg"] != nil) me._nav2_deviation_deg = data["Nav1CourseDeviationDeg"];
# Deflection range is [-1,1], while deflection_dots is [-2.4, 2.4];
if (data["Nav2Deflection"] != nil) me._nav2_deflection = data["Nav2Deflection"] * 2.4;
if (data["Nav2CrosstrackErrorM"] != nil) me._nav2_crosstrack_m = data["Nav2CrosstrackErrorM"];
if (data["Nav2From"] != nil) me._nav2_from = data["Nav2From"];
if (data["Nav2Localizer"] != nil) me._nav2_loc = data["Nav2Localizer"];
if (data["ADFSelectedFreq"] != nil) me._adf_freq = data["ADFSelectedFreq"];
if (data["ADFInRange"] != nil) me._adf_in_range = data["ADFInRange"];
if (data["ADFHeadingDeg"] !=nil) me._adf_heading_deg = data["ADFInRange"];
if (me.page.getBRG1() == "NAV1") me.page.updateBRG1(me._nav1_in_range, me._nav1_id, me._nav1_distance_m * M2NM, me._heading, me._nav1_heading_deg);
if (me.page.getBRG1() == "NAV2") me.page.updateBRG1(me._nav2_in_range, me._nav2_id, me._nav2_distance_m * M2NM, me._heading, me._nav2_heading_deg);
if (me.page.getBRG1() == "ADF") me.page.updateBRG1(me._adf_in_range, sprintf("%.1f", me._adf_freq), 0, me._heading, me._adf_heading_deg);
if (me.getBRG1() == "NAV1") me.page.updateBRG1(me._nav1_in_range, me._nav1_id, me._nav1_distance_m * M2NM, me._heading_magnetic_deg, me._nav1_heading_deg);
if (me.getBRG1() == "NAV2") me.page.updateBRG1(me._nav2_in_range, me._nav2_id, me._nav2_distance_m * M2NM, me._heading_magnetic_deg, me._nav2_heading_deg);
if (me.getBRG1() == "ADF") me.page.updateBRG1(me._adf_in_range, sprintf("%.1f", me._adf_freq), 0, me._heading_magnetic_deg, me._adf_heading_deg);
if (me.page.getBRG2() == "NAV1") me.page.updateBRG2(me._nav1_in_range, me._nav1_id, me._nav1_distance_m * M2NM, me._heading, me._nav1_heading_deg);
if (me.page.getBRG2() == "NAV2") me.page.updateBRG2(me._nav2_in_range, me._nav2_id, me._nav2_distance_m * M2NM, me._heading, me._nav2_heading_deg);
if (me.page.getBRG2() == "ADF") me.page.updateBRG2(me._adf_in_range, sprintf("%.1f", me._adf_freq), 0, me._heading, me._adf_heading_deg);
if (me.getBRG2() == "NAV1") me.page.updateBRG2(me._nav1_in_range, me._nav1_id, me._nav1_distance_m * M2NM, me._heading_magnetic_deg, me._nav1_heading_deg);
if (me.getBRG2() == "NAV2") me.page.updateBRG2(me._nav2_in_range, me._nav2_id, me._nav2_distance_m * M2NM, me._heading_magnetic_deg, me._nav2_heading_deg);
if (me.getBRG2() == "ADF") me.page.updateBRG2(me._adf_in_range, sprintf("%.1f", me._adf_freq), 0, me._heading_magnetic_deg, me._adf_heading_deg);
if (me.getCDISource() == "NAV1") {
me.page.updateCRS(me._nav1_radial_deg);
me.page.updateCDI(
heading: me._heading_magnetic_deg,
course: me._nav1_radial_deg,
waypoint_valid: me._nav1_in_range,
course_deviation_deg : me._nav1_deviation_deg,
deflection_dots : me._nav1_deflection,
xtrk_nm : me._nav1_crosstrack_m * M2NM,
from: me._nav1_from,
annun: "",
loc : me._nav1_loc,
);
}
if (me.getCDISource() == "NAV2") {
me.page.updateCRS(me._nav2_radial_deg);
me.page.updateCDI(
heading: me._heading_magnetic_deg,
course: me._nav2_radial_deg,
waypoint_valid: me._nav2_in_range,
course_deviation_deg : me._nav2_deviation_deg,
deflection_dots : me._nav2_deflection,
xtrk_nm : me._nav2_crosstrack_m * M2NM,
from: me._nav2_from,
annun: "",
loc : me._nav2_loc,
);
}
return emesary.Transmitter.ReceiptStatus_OK;
},
PFDRegisterWithEmesary : func(transmitter = nil){

View file

@ -57,7 +57,7 @@ var PAGE_GROUPS = [
# Mapping for header labels to specific FMS or ADC messages, and sprintf formatting
# to use
var HEADER_MAPPING = {
"BRG" : { message : "FMSLegBearing", format : "%d"},
"BRG" : { message : "FMSLegBearingMagDeg", format : "%d"},
"XTK" : { message : "FMSLegCourseError", format : "%.1fnm"},
"DIS" : { message : "FMSDistance", format : "%.1fnm"},
"DTK" : { message : "FMSLegDesiredTrack", format : "%d"},

View file

@ -31,11 +31,16 @@ var SurroundController =
_com2standby : 0.0,
_nav1active : 0.0,
_nav1standby : 0.0,
_nav1radial : 0.0,
_nav1_heading_deg : 0.0,
_nav2active : 0.0,
_nav2standby : 0.0,
_nav2radial : 0.0,
_nav2_heading_deg : 0.0,
_pressure_settings_inhg : 0.0,
_selected_alt_ft : 0.0,
_heading_bug_deg : 0.0,
_heading_deg : 0.0,
};
obj.RegisterWithEmesary();
@ -81,8 +86,13 @@ var SurroundController =
if (data["Nav1SelectedFreq"] != nil) me._nav1active = data["Nav1SelectedFreq"];
if (data["Nav1StandbyFreq"] != nil) me._nav1standby = data["Nav1StandbyFreq"];
if (data["Nav1RadialDeg"] != nil) me._nav1radial = data["Nav1RadialDeg"];
if (data["Nav1HeadingDeg"] != nil) me._nav1_heading_deg = data["Nav1HeadingDeg"];
if (data["Nav2SelectedFreq"] != nil) me._nav2active = data["Nav2SelectedFreq"];
if (data["Nav2StandbyFreq"] != nil) me._nav2standby = data["Nav2StandbyFreq"];
if (data["Nav2RadialDeg"] != nil) me._nav2radial = data["Nav2RadialDeg"];
if (data["Nav2HeadingDeg"] != nil) me._nav2_heading_deg = data["Nav2HeadingDeg"];
# pass through to the page
me._page.handleNavComData(data);
@ -93,6 +103,7 @@ var SurroundController =
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"];
if (data["ADCHeadingMagneticDeg"] != nil) me._heading_deg = data["ADCHeadingMagneticDeg"];
# Pass FMS and ADC data straight to the page to display in the header fields
me._page.updateHeaderData(data);
@ -211,7 +222,7 @@ var SurroundController =
},
# Switch between Nav1 and Nav2.
handleNavToggle : func (value)
handleNavToggle : func ()
{
var data={};
@ -225,6 +236,13 @@ var SurroundController =
return emesary.Transmitter.ReceiptStatus_Finished;
},
setNav : func(value) {
var data={};
data["NavSelected"] = value;
me.sendNavComDataNotification(data);
return emesary.Transmitter.ReceiptStatus_Finished;
},
# Outer COM dial changes the integer value of the standby selected COM
# frequency, wrapping on limits. Leaves the fractional part unchanged
handleComOuter : func (value) {
@ -366,6 +384,32 @@ var SurroundController =
return emesary.Transmitter.ReceiptStatus_Finished;
},
handleCRS : func(value) {
var incr_or_decr = (value > 0) ? 1 : -1;
var data={};
if (me._navselected == 1) {
data["Nav1RadialDeg"] = math.mod(me._nav1radial + incr_or_decr, 360);
} else {
data["Nav2RadialDeg"] = math.mod(me._nav2radial + incr_or_decr, 360);
}
me.sendNavComDataNotification(data);
return emesary.Transmitter.ReceiptStatus_Finished;
},
handleCRSCenter : func(value) {
var data = {};
if (me._navselected == 1) {
data["Nav1RadialDeg"] = me._nav1_heading_deg;
} else {
data["Nav2RadialDeg"] = me._nav2_heading_deg;
}
me.sendNavComDataNotification(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);
@ -396,6 +440,13 @@ var SurroundController =
return emesary.Transmitter.ReceiptStatus_Finished;
},
handleHeadingPress : func(value) {
var data = {};
data["FMSHeadingBug"] = me._heading_deg;
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.