1
0
Fork 0

FG1000 - Add working CDI to PFD.

CDI now displays correct GPS course, and BRG1/BRG2 softkeys (under PFD)
now display GPS, NAV1, NAV2, ADF with bearing arrows.
This commit is contained in:
Stuart Buchanan 2018-03-06 22:12:22 +00:00
parent c37f1ff7bb
commit c46486c7de
5 changed files with 1388 additions and 1289 deletions

File diff suppressed because it is too large Load diff

Before

Width:  |  Height:  |  Size: 468 KiB

After

Width:  |  Height:  |  Size: 468 KiB

View file

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

View file

@ -22,53 +22,88 @@
#
var GenericNavComPublisher =
{
new : func () {
new : func (frequency=0.5) {
var obj = {
parents : [
GenericNavComPublisher,
TriggeredPropertyPublisher.new(notifications.PFDEventNotification.NavComData)
],
};
# We have two publishers here:
#
# 1) a triggered publisher for properties that will change ocassionally, but which
# we need to update immediately. These are typically settings.
#
# 2) a periodic publisher which triggers every 0.5s to update data values.
obj._triggeredPublisher = TriggeredPropertyPublisher.new(notifications.PFDEventNotification.NavComData);
obj._periodicPublisher = PeriodicPropertyPublisher.new(notifications.PFDEventNotification.NavComData, frequency);
# Hack to handle cases where there is no selected Com or NAV frequency
if (getprop("/instrumentation/com-selected") == nil) setprop("/instrumentation/com-selected", 1);
if (getprop("/instrumentation/nav-selected") == nil) setprop("/instrumentation/nav-selected", 1);
obj.addPropMap("Comm1SelectedFreq", "/instrumentation/comm/frequencies/selected-mhz");
obj.addPropMap("Comm1StandbyFreq", "/instrumentation/comm/frequencies/standby-mhz");
obj.addPropMap("Comm1AirportID", "/instrumentation/comm/airport-id");
obj.addPropMap("Comm1StationName", "/instrumentation/comm/station-name");
obj.addPropMap("Comm1StationType", "/instrumentation/comm/station-type");
obj.addPropMap("Comm1Volume", "/instrumentation/comm/volume");
obj.addPropMap("Comm1Serviceable", "/instrumentation/comm/serviceable");
obj._triggeredPublisher.addPropMap("Comm1SelectedFreq", "/instrumentation/comm/frequencies/selected-mhz");
obj._triggeredPublisher.addPropMap("Comm1StandbyFreq", "/instrumentation/comm/frequencies/standby-mhz");
obj._triggeredPublisher.addPropMap("Comm1AirportID", "/instrumentation/comm/airport-id");
obj._triggeredPublisher.addPropMap("Comm1StationName", "/instrumentation/comm/station-name");
obj._triggeredPublisher.addPropMap("Comm1StationType", "/instrumentation/comm/station-type");
obj._triggeredPublisher.addPropMap("Comm1Volume", "/instrumentation/comm/volume");
obj._triggeredPublisher.addPropMap("Comm1Serviceable", "/instrumentation/comm/serviceable");
obj.addPropMap("Comm2SelectedFreq", "/instrumentation/comm[1]/frequencies/selected-mhz");
obj.addPropMap("Comm2StandbyFreq", "/instrumentation/comm[1]/frequencies/standby-mhz");
obj.addPropMap("Comm2AirportID", "/instrumentation/comm[1]/airport-id");
obj.addPropMap("Comm2StationName", "/instrumentation/comm[1]/station-name");
obj.addPropMap("Comm2StationType", "/instrumentation/comm[1]/station-type");
obj.addPropMap("Comm2Volume", "/instrumentation/comm[1]/volume");
obj.addPropMap("Comm2Serviceable", "/instrumentation/comm[1]/serviceable");
obj._triggeredPublisher.addPropMap("Comm2SelectedFreq", "/instrumentation/comm[1]/frequencies/selected-mhz");
obj._triggeredPublisher.addPropMap("Comm2StandbyFreq", "/instrumentation/comm[1]/frequencies/standby-mhz");
obj._triggeredPublisher.addPropMap("Comm2AirportID", "/instrumentation/comm[1]/airport-id");
obj._triggeredPublisher.addPropMap("Comm2StationName", "/instrumentation/comm[1]/station-name");
obj._triggeredPublisher.addPropMap("Comm2StationType", "/instrumentation/comm[1]/station-type");
obj._triggeredPublisher.addPropMap("Comm2Volume", "/instrumentation/comm[1]/volume");
obj._triggeredPublisher.addPropMap("Comm2Serviceable", "/instrumentation/comm[1]/serviceable");
obj.addPropMap("CommSelected", "/instrumentation/com-selected");
obj._triggeredPublisher.addPropMap("CommSelected", "/instrumentation/com-selected");
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("Nav1Volume", "/instrumentation/nav/nav-volume");
obj.addPropMap("Nav1AudioID", "/instrumentation/nav/audio-btn");
obj.addPropMap("Nav1Serviceable", "/instrumentation/nav/operable");
obj._triggeredPublisher.addPropMap("Nav1SelectedFreq", "/instrumentation/nav/frequencies/selected-mhz");
obj._triggeredPublisher.addPropMap("Nav1StandbyFreq", "/instrumentation/nav/frequencies/standby-mhz");
obj._periodicPublisher.addPropMap("Nav1ID", "/instrumentation/nav/nav-id");
obj._periodicPublisher.addPropMap("Nav1InRange", "/instrumentation/nav/in-range");
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._triggeredPublisher.addPropMap("Nav1Volume", "/instrumentation/nav/nav-volume");
obj._triggeredPublisher.addPropMap("Nav1AudioID", "/instrumentation/nav/audio-btn");
obj._triggeredPublisher.addPropMap("Nav1Serviceable", "/instrumentation/nav/operable");
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("Nav2Volume", "/instrumentation/nav[1]/nav-volume");
obj.addPropMap("Nav2AudioID", "/instrumentation/nav[1]/audio-btn");
obj.addPropMap("Nav2Serviceable", "/instrumentation/nav[1]/operable");
obj._triggeredPublisher.addPropMap("Nav2SelectedFreq", "/instrumentation/nav[1]/frequencies/selected-mhz");
obj._triggeredPublisher.addPropMap("Nav2StandbyFreq", "/instrumentation/nav[1]/frequencies/standby-mhz");
obj._periodicPublisher.addPropMap("Nav2ID", "/instrumentation/nav[1]/nav-id");
obj._periodicPublisher.addPropMap("Nav2InRange", "/instrumentation/nav[1]/in-range");
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._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");
obj.addPropMap("NavSelected", "/instrumentation/nav-selected");
obj._triggeredPublisher.addPropMap("ADFSelectedFreq", "/instrumentation/adf/frequencies/selected-khz");
obj._periodicPublisher.addPropMap("ADFInRange", "/instrumentation/adf/in-range");
obj._periodicPublisher.addPropMap("ADFHeadingDeg", "/instrumentation/adf/indicated-bearing-deg");
obj._triggeredPublisher.addPropMap("ADFVolume", "/instrumentation/adf/volume-norm");
obj._triggeredPublisher.addPropMap("ADFServiceable", "/instrumentation/adf/operable");
obj._triggeredPublisher.addPropMap("NavSelected", "/instrumentation/nav-selected");
return obj;
},
start : func() {
me._triggeredPublisher.start();
me._periodicPublisher.start();
},
stop : func() {
me._triggeredPublisher.stop();
me._periodicPublisher.stop();
},
};

View file

@ -180,7 +180,7 @@ var PFDInstruments =
},
displayDCLTR : func(svg, mi) {
mi.title = pg.mfd.PFDInstruments.insetMap.getDCLTRTitle();
mi.title = me.insetMap.getDCLTRTitle();
svg.setText(mi.title);
svg.setVisible(1);
},
@ -537,35 +537,57 @@ var PFDInstruments =
if (option == "OFF") {
me.getElement(brg).hide();
me.getElement(brg ~ "-pointer").hide();
if ((me._BRG1 == "OFF") and (me._BRG1 == "OFF")) {
if ((me._BRG1 == "OFF") and (me._BRG2 == "OFF")) {
me.getElement("BRG-circle").hide();
}
} else {
me.getElement(brg).show();
me.getElement(brg ~ "-pointer").show();
me.getElement("BRG-circle").show();
me.getElement(brg ~ "-SRC-text").setText(option);
me.getElement(brg ~ "-WPID-text").setText("----");
if (option == "ADF") {
# Special case. We won't have a distance and the "ID" will be the ADF
# frequency
me.getElement(brg ~ "-DST-text").setText("");
} else {
me.getElement(brg ~ "-DST-text").setText("--nm");
}
}
},
# Update BRG information
updateBRG1 : func(id, dst) {
me._updateBRG("BRG1", me._BRG1, id, dst);
updateBRG1 : func(valid, id, dst, current_heading, brg_heading) {
me._updateBRG("BRG1", me._BRG1, valid, id, dst, current_heading, brg_heading);
},
updateBRG2 : func(id, dst) {
me._updateBRG("BRG2", me._BRG2, id, dst);
updateBRG2 : func(valid, id, dst, current_heading, brg_heading) {
me._updateBRG("BRG2", me._BRG2, valid, id, dst, current_heading, brg_heading);
},
_updateBRG : func (brg, source, id, dst) {
_updateBRG : func (brg, source, valid, id, dst, current_heading, brg_heading) {
if (source == "OFF") return;
me.getElement(brg ~ "-SRC-text").setText(source);
me.getElement(brg ~ "-WP-text").setText(id);
if (valid) {
me.getElement(brg ~ "-SRC-text").setText(source);
me.getElement(brg ~ "-WPID-text").setText(id);
if (source == "ADF") {
# Special case. We won't have a distance and the "ID" will be the ADF
# frequency
me.getElement(brg ~ "-DST-text").setText("");
if (source == "ADF") {
# Special case. We won't have a distance and the "ID" will be the ADF
# frequency
me.getElement(brg ~ "-DST-text").setText("");
} else {
me.getElement(brg ~ "-DST-text").setText(sprintf("%.1fNM", dst));
}
var rot = (brg_heading - current_heading) * D2R;
me.getElement(brg ~ "-pointer").setRotation(rot).show();
} else {
me.getElement(brg ~ "-DST-text").setText(sprintf("%.1fNM", dst));
# Data is not valid - hide the pointer and display NO DATA
me.getElement(brg ~ "-SRC-text").setText(source);
me.getElement(brg ~ "-WPID-text").setText("NO DATA");
me.getElement(brg ~ "-DST-text").setText("");
me.getElement(brg ~ "-pointer").hide();
}
},
@ -639,6 +661,7 @@ var PFDInstruments =
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("CDI").setRotation(0);
me.getElement("GPS-CTI-diamond").hide();
me.getElement("CDI-GPS-XTK-text").hide();
@ -647,18 +670,16 @@ var PFDInstruments =
me.getElement(me._CDISource ~ "-CDI").show();
var rot = (course - heading) * D2R;
me.getElement("CDI")
.setRotation(rot)
.show();
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)) {
if ((me._CDISource == "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")
.setText(sprintf("XTK %iNM", abs(xtrk_nm)))
.setText(sprintf("XTK %.2fNM", abs(xtrk_nm)))
.show();
} else {
me.getElement("CDI-GPS-XTK-text").hide();
@ -667,7 +688,7 @@ var PFDInstruments =
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);
me.getElement(me._CDISource ~ "-CDI").setTranslation(80 * scale / 2.4, 0);
# Display the appropriate TO/FROM indication for the selected source,
# switching all others off.

View file

@ -35,8 +35,27 @@ var PFDInstrumentsController =
_leg_bearing : 0,
_leg_distance_nm : 0,
_leg_deviation_deg : 0,
_deflection_dots :0,
_deflection_dots : 0.0,
_leg_xtrk_nm : 0,
_leg_valid : 0,
_nav1_id : "",
_nav1_freq : 0.0,
_nav1_radial_deg : 0,
_nav1_heading_deg :0.0,
_nav1_in_range : 0,
_nav1_distance_m :0,
_nav2_id : "",
_nav2_freq : 0.0,
_nav2_radial_deg :0,
_nav2_heading_deg : 0.0,
_nav2_in_range : 0,
_nav2_distance_m :0,
_adf_freq : 0.0,
_adf_in_range : 0,
_adf_heading_deg : 0.0,
};
return obj;
@ -145,7 +164,9 @@ var PFDInstrumentsController =
me._selected_alt_ft = data["FMSSelectedAlt"];
}
if (data["FMSLegValid"] == "false") {
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(
@ -153,14 +174,11 @@ var PFDInstrumentsController =
course: 0,
waypoint_valid: 0,
course_deviation_deg : 0,
deflection_dots : 0,
deflection_dots : 0.0,
xtrk_nm : 0,
from: 0,
annun: "NO DATA",
);
# 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"]);
@ -173,28 +191,29 @@ var PFDInstrumentsController =
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"];
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: (data["FMSLegValid"] == "true"),
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.setBRG1(me._leg_id, me._leg_bearing);
if (me.page.getBRG2() == "GPS") me.page.setBRG2(me._leg_id, me._leg_bearing);
}
# 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);
return emesary.Transmitter.ReceiptStatus_OK;
},
@ -203,9 +222,33 @@ var PFDInstrumentsController =
# that the data we want exists in this notification, unlike the periodic
# publishers
handleNavComData : func(data) {
if (data["NavSelected"] != nil) {
me._navSelected = data["NavSelected"];
}
if (data["NavSelected"] != nil) me._navSelected = data["NavSelected"];
if (data["Nav1SelectedFreq"] != nil) me._nav1_freq = data["Nav1SelectedFreq"];
if (data["Nav1ID"] != nil) me._nav1_id = data["Nav1ID"];
if (data["Nav1HeadingDeg"] != nil) me._nav1_heading_deg = data["Nav1HeadingDeg"];
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["Nav2SelectedFreq"] != nil) me._nav2_freq = data["Nav2SelectedFreq"];
if (data["Nav2ID"] != nil) me._nav2_id = data["Nav2ID"];
if (data["Nav2HeadingDeg"] != nil) me._nav2_heading_deg = data["Nav2HeadingDeg"];
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["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.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);
},
PFDRegisterWithEmesary : func(transmitter = nil){