8a7bc16829
Previously select XPDR->CODE->7 and then another number caused very odd transponder codes. Turns out this was due to extra whitespace in the label causing various string concatenations and evaluations to fail.
959 lines
34 KiB
Text
959 lines
34 KiB
Text
# 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/>.
|
|
#
|
|
# PFDInstruments
|
|
var PFDInstruments =
|
|
{
|
|
|
|
COLORS : {
|
|
green : [0, 1, 0],
|
|
white : [1, 1, 1],
|
|
black : [0, 0, 0],
|
|
lightblue : [0, 1, 1],
|
|
darkblue : [0, 0, 1],
|
|
red : [1, 0, 0],
|
|
magenta : [1, 0, 1],
|
|
},
|
|
|
|
new : func (mfd, myCanvas, device, svg)
|
|
{
|
|
var obj = {
|
|
parents : [
|
|
PFDInstruments,
|
|
MFDPage.new(mfd, myCanvas, device, svg, "PFDInstruments", "PFD Instruments")
|
|
],
|
|
|
|
_ias_already_exceeded : 0,
|
|
_windDataDisplay : 0,
|
|
_BRG1 : "OFF",
|
|
_BRG2 : "OFF",
|
|
_DME : 0,
|
|
_OMI : "",
|
|
_Multiline : 0,
|
|
_annunciation : 0,
|
|
_fd_enabled : 1, # Mark the Flight Director as enabled, as it is visible in the SVG.
|
|
_selected_spd : 0,
|
|
_selected_spd_visible : 0,
|
|
};
|
|
|
|
# Hide various elements for the moment. TODO - implement
|
|
obj.device.svg.getElementById("PFDInstrumentsFailures").setVisible(0);
|
|
obj.device.svg.getElementById("PFDInstrumentsGSPD").setVisible(0);
|
|
|
|
obj.addTextElements([
|
|
"Speed110",
|
|
"VSIText",
|
|
"TAS-text", "GSPD-text",
|
|
"Alt11100",
|
|
"AltBigC", "AltSmallC",
|
|
"BARO-text", "OAT-text",
|
|
"HDG-text",
|
|
"SelectedHDG-text",
|
|
"SelectedALT-text",
|
|
"SelectedSPD-text",
|
|
"XPDR-DIGIT-3-text", "XPDR-DIGIT-2-text", "XPDR-DIGIT-1-text", "XPDR-DIGIT-0-text",
|
|
"XPDR-MODE-text",
|
|
"TIME-text",
|
|
"GS-type",
|
|
"MarkerText",
|
|
]);
|
|
|
|
# Set clipping for the various tapes
|
|
var clips = {
|
|
PitchScale : "rect(70,664,370,256)",
|
|
SpeedLint1 : "rect(252,226,318,204)",
|
|
SpeedTape : "rect(115,239,455,156)",
|
|
LintAlt : "rect(115,808,455,704)",
|
|
AltLint00011 : "rect(252,804,318,771)",
|
|
};
|
|
|
|
foreach(var id; keys(clips)) {
|
|
var clip = clips[id];
|
|
obj.device.svg.getElementById("PFDInstruments" ~ id).set("clip", clip);
|
|
}
|
|
|
|
obj._SVGGroup.setInt("z-index", 10);
|
|
obj.insetMap = fg1000.NavMap.new(obj, obj.getElement("PFD-Map-Display"), [119,601], "rect(-109px, 109px, 109px, -109px)", 0, 2);
|
|
|
|
# Flight Plan Window group
|
|
obj.flightplanList = PFD.GroupElement.new(
|
|
obj.pageName,
|
|
svg,
|
|
[ "FlightPlanArrow", "FlightPlanID", "FlightPlanType", "FlightPlanDTK", "FlightPlanDIS"],
|
|
5,
|
|
"FlightPlanArrow",
|
|
1,
|
|
"FlightPlanScrollTrough",
|
|
"FlightPlanScrollThumb",
|
|
120
|
|
);
|
|
|
|
obj.setController(fg1000.PFDInstrumentsController.new(obj, svg));
|
|
|
|
obj.setWindDisplay(0);
|
|
obj.setCDISource("GPS");
|
|
obj.setBRG1("OFF");
|
|
obj.setBRG2("OFF");
|
|
obj.setDME(0);
|
|
obj.setMultiline(0);
|
|
obj.setAnnunciation(0);
|
|
obj.setOMI("");
|
|
obj.setInsetMapVisible(0);
|
|
obj.updateHDG(0);
|
|
obj.updateSelectedALT(0);
|
|
obj.updateCRS(0);
|
|
obj.setFlightPlanVisible(0);
|
|
|
|
return obj;
|
|
},
|
|
|
|
topMenu : func(device, pg, menuitem) {
|
|
pg.clearMenu();
|
|
pg.resetMenuColors();
|
|
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, func(dev, pg, mi) { pg.getController().incrCDISource(); } );
|
|
#pg.addMenuItem(6, "DME", pg, func(dev, pg, mi) { pg.toggleDME(); } ); # TODO
|
|
pg.addMenuItem(7, "XPDR", pg, pg.mfd.PFDInstruments.transponderMenu);
|
|
pg.addMenuItem(8, "IDENT", pg, pg.mfd.PFDInstruments.setIdent); # TODO
|
|
pg.addMenuItem(9, "TMR/REF", pg); # TODO
|
|
pg.addMenuItem(10, "NRST", pg, pg.mfd.PFDInstruments.toggleNRST, func(svg, mi) { pg.mfd.PFDInstruments.toggleNRSTDisplay(device, pg, svg, mi); } );
|
|
pg.addMenuItem(11, "ALERTS", pg); # TODO
|
|
device.updateMenus();
|
|
},
|
|
|
|
toggleNRST : func (device, pg, mi) {
|
|
pg.mfd.NearestAirports.toggleDisplay();
|
|
device.updateMenus();
|
|
},
|
|
|
|
toggleNRSTDisplay : func(device, pg, svg, mi) {
|
|
var bg_name = sprintf("SoftKey%d-bg",mi.menu_id);
|
|
if (pg.mfd.NearestAirports.visible()) {
|
|
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
|
|
},
|
|
|
|
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); pg.mfd.PFDInstruments.topMenu(dev, pg, mi); } );
|
|
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 = me.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, func(dev, pg, mi) { pg.getController().incrBRG1(); });
|
|
pg.addMenuItem(5, "HSI FRMT", pg); # TODO
|
|
pg.addMenuItem(6, "BRG2", pg, func(dev, pg, mi) { pg.getController().incrBRG2(); });
|
|
pg.addMenuItem(8, "ALT UNIT ", 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();
|
|
},
|
|
|
|
windMenu : func(device, pg, menuitem) {
|
|
pg.clearMenu();
|
|
pg.resetMenuColors();
|
|
pg.addMenuItem(2, "OPTN1", pg,
|
|
func(dev, pg, mi) { pg.mfd.PFDInstruments.setWindDisplay(1); device.updateMenus(); }, # Action callback
|
|
func(svg, mi) { pg.mfd.PFDInstruments.toggleWindDisplay(device, svg, mi, 1); } # Display callback
|
|
);
|
|
pg.addMenuItem(3, "OPTN2", pg,
|
|
func(dev, pg, mi) { pg.mfd.PFDInstruments.setWindDisplay(2); device.updateMenus(); }, # Action callback
|
|
func(svg, mi) { pg.mfd.PFDInstruments.toggleWindDisplay(device, svg, mi, 2); } # Display callback
|
|
);
|
|
pg.addMenuItem(4, "OPTN3", pg,
|
|
func(dev, pg, mi) { pg.mfd.PFDInstruments.setWindDisplay(3); device.updateMenus(); }, # Action callback
|
|
func(svg, mi) { pg.mfd.PFDInstruments.toggleWindDisplay(device, svg, mi, 3); } # Display callback
|
|
);
|
|
pg.addMenuItem(5, "OFF", pg,
|
|
func(dev, pg, mi) { pg.mfd.PFDInstruments.setWindDisplay(0); device.updateMenus(); }, # Action callback
|
|
func(svg, mi) { pg.mfd.PFDInstruments.toggleWindDisplay(device, svg, mi, 0); } # Display callback
|
|
);
|
|
pg.addMenuItem(10, "BACK", pg, pg.mfd.PFDInstruments.topMenu);
|
|
pg.addMenuItem(11, "ALERTS", pg); # TODO
|
|
device.updateMenus();
|
|
},
|
|
|
|
toggleWindDisplay : func(device, svg, mi, wind_value) {
|
|
var bg_name = sprintf("SoftKey%d-bg",mi.menu_id);
|
|
if (me._windDataDisplay == wind_value) {
|
|
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
|
|
},
|
|
|
|
transponderMenu : func(device, pg, menuitem) {
|
|
pg.clearMenu();
|
|
pg.resetMenuColors();
|
|
pg.addMenuItem(2, "STBY", pg, pg.mfd.PFDInstruments.setTransponderMode);
|
|
pg.addMenuItem(3, "ON", pg, pg.mfd.PFDInstruments.setTransponderMode);
|
|
pg.addMenuItem(4, "ALT", pg, pg.mfd.PFDInstruments.setTransponderMode);
|
|
pg.addMenuItem(5, "GND", pg, pg.mfd.PFDInstruments.setTransponderMode);
|
|
pg.addMenuItem(6, "VFR", pg, pg.mfd.PFDInstruments.setVFR);
|
|
pg.addMenuItem(7, "CODE", pg, pg.mfd.PFDInstruments.codeMenu);
|
|
pg.addMenuItem(8, "IDENT", pg, pg.mfd.PFDInstruments.setIdent);
|
|
pg.addMenuItem(10, "BACK", pg, pg.mfd.PFDInstruments.topMenu);
|
|
pg.addMenuItem(11, "ALERTS", pg); # TODO
|
|
device.updateMenus();
|
|
},
|
|
|
|
codeMenu : func(device, pg, menuitem) {
|
|
pg.clearMenu();
|
|
pg.resetMenuColors();
|
|
pg.addMenuItem(0, "0", pg, pg.mfd.PFDInstruments.setTransponderDigit);
|
|
pg.addMenuItem(1, "1", pg, pg.mfd.PFDInstruments.setTransponderDigit);
|
|
pg.addMenuItem(2, "2", pg, pg.mfd.PFDInstruments.setTransponderDigit);
|
|
pg.addMenuItem(3, "3", pg, pg.mfd.PFDInstruments.setTransponderDigit);
|
|
pg.addMenuItem(4, "4", pg, pg.mfd.PFDInstruments.setTransponderDigit);
|
|
pg.addMenuItem(5, "5", pg, pg.mfd.PFDInstruments.setTransponderDigit);
|
|
pg.addMenuItem(6, "6", pg, pg.mfd.PFDInstruments.setTransponderDigit);
|
|
pg.addMenuItem(7, "7", pg, pg.mfd.PFDInstruments.setTransponderDigit);
|
|
pg.addMenuItem(8, "IDENT", pg, pg.mfd.PFDInstruments.setIdent);
|
|
pg.addMenuItem(9, "BKSP", pg, pg.mfd.PFDInstruments.setTransponderDigit);
|
|
pg.addMenuItem(10, "BACK", pg, pg.mfd.PFDInstruments.transponderMenu);
|
|
pg.addMenuItem(11, "ALERTS", pg); # TODO
|
|
device.updateMenus();
|
|
},
|
|
|
|
setTransponderMode : func(device, pg, menuitem) {
|
|
# Get the transponder mode from the menuitem itself.
|
|
pg.getController().setTransponderMode(menuitem.title);
|
|
},
|
|
|
|
setVFR : func(device, pg, menuitem) {
|
|
# Set VFR Mode - 1200
|
|
pg.getController().setTransponderCode(1200);
|
|
},
|
|
|
|
setIdent : func(device, pg, menuitem) {
|
|
# Ident the transponder
|
|
pg.getController().setTransponderIdent(1);
|
|
},
|
|
|
|
setTransponderDigit: func(device, pg, menuitem) {
|
|
# Set a transponder digit. Get digit from menu item.
|
|
pg.getController().setTransponderDigit(menuitem.title);
|
|
},
|
|
|
|
offdisplay : func() {
|
|
me._group.setVisible(0);
|
|
|
|
# Reset the menu colours. Shouldn't have to do this here, but
|
|
# there's not currently an obvious other location to do so.
|
|
for(var i = 0; i < 12; i +=1) {
|
|
var name = sprintf("SoftKey%d",i);
|
|
me.device.svg.getElementById(name ~ "-bg").setColorFill(0.0,0.0,0.0);
|
|
me.device.svg.getElementById(name).setColor(1.0,1.0,1.0);
|
|
}
|
|
me.getController().offdisplay();
|
|
},
|
|
ondisplay : func() {
|
|
me._group.setVisible(1);
|
|
me.getController().ondisplay();
|
|
},
|
|
|
|
updateAI: func(pitch, roll, slip) {
|
|
if (pitch > 80)
|
|
pitch = 80;
|
|
elsif (pitch < -80)
|
|
pitch = -80;
|
|
me.getElement("Horizon")
|
|
.setCenter(459, 282.8 - 6.849 * pitch)
|
|
.setRotation(-roll * D2R)
|
|
.setTranslation(0, pitch * 6.849);
|
|
me.getElement("bankPointer")
|
|
.setRotation(-roll * D2R);
|
|
me.getElement("SlipSkid")
|
|
.setTranslation(slip * 10, 0);
|
|
},
|
|
|
|
updateFD : func(enabled, pitch, roll, fd_pitch, fd_roll) {
|
|
if (enabled) {
|
|
me.getElement("FlightDirector")
|
|
.setCenter(459,282.8)
|
|
.setRotation(-(roll - fd_roll) * D2R)
|
|
.setTranslation(0, -(fd_pitch - pitch) * 6.849)
|
|
.setVisible(1);
|
|
me._fd_enabled = 1;
|
|
} else if (me._fd_enabled == 1) {
|
|
me.getElement("FlightDirector").setVisible(0);
|
|
me._fd_enabled = 0;
|
|
}
|
|
|
|
# Overrides - command bars disappear if pitch exceeeds -20/+30, roll 65
|
|
if ((pitch < -20.0) or (pitch > 30.0) or (roll < -65.0) or (roll > 65.0)) {
|
|
me.getElement("FlightDirector").setVisible(0);
|
|
}
|
|
},
|
|
|
|
updateIAS: func (ias, ias_trend) {
|
|
if (ias >= 10) {
|
|
me.setTextElement("Speed110", sprintf("% 2u",num(math.floor(ias/10))));
|
|
} else {
|
|
me.setTextElement("Speed110", "");
|
|
}
|
|
|
|
me.getElement("SpeedLint1").setTranslation(0,(math.mod(ias,10) + (ias >= 10)*10) * 36);
|
|
me.getElement("SpeedTape").setTranslation(0,ias * 5.711);
|
|
|
|
ias_trend = math.clamp(ias_trend, -30, 30);
|
|
me.getElement("Airspeed-Trend-Indicator")
|
|
.setScale(1,ias_trend)
|
|
.setTranslation(0, -284.5 * (ias_trend - 1));
|
|
|
|
var vne = me.mfd.ConfigStore.get("Vne");
|
|
|
|
if (ias > vne and ! me._ias_already_exceeded) {
|
|
me._ias_already_exceeded = 1;
|
|
me.getElement("IAS-bg").setColorFill(1,0,0);
|
|
} elsif (ias < vne and me._ias_already_exceeded) {
|
|
me._ias_already_exceeded = 0;
|
|
me.getElement("IAS-bg").setColorFill(0,0,0);
|
|
}
|
|
|
|
foreach (var v; ["Vx", "Vy", "Vr", "Vglide"]) {
|
|
var spd = me.mfd.ConfigStore.get(v);
|
|
var visible = me.mfd.ConfigStore.get(v ~ "-visible");
|
|
if (visible and abs(spd - ias) < 30) {
|
|
me.getElement("IAS-" ~ v)
|
|
.setTranslation(0, (ias - spd) * 5.711)
|
|
.show();
|
|
} else {
|
|
me.getElement("IAS-" ~ v).hide();
|
|
}
|
|
}
|
|
|
|
if ((me._selected_spd_visible) and ((me._selected_spd - ias) < 30)) {
|
|
me.getElement("SelectedSPD-bug")
|
|
.setTranslation(0, (ias - me._selected_spd) * 5.711)
|
|
.show();
|
|
} else {
|
|
me.getElement("SelectedSPD-bug").hide();
|
|
}
|
|
},
|
|
|
|
updateVSI: func (vsi) {
|
|
me.getElement("VSI").setTranslation(0, math.clamp(vsi, -4500, 4500) * -0.03465);
|
|
me.setTextElement("VSIText", num(math.round(vsi, 10)));
|
|
},
|
|
|
|
updateTAS: func (tas) {
|
|
me.setTextElement("TAS-text", sprintf("%iKT", tas));
|
|
#me.getElement("GSPD-text").setText(sprintf("%iKT", tas));
|
|
},
|
|
|
|
updateGS : func (deflection_norm, type) {
|
|
me.getElement("GS-ILS").setTranslation(0, - deflection_norm * 100);
|
|
me.setTextElement("GS-type", type);
|
|
},
|
|
|
|
updateALT: func (alt, alt_trend, selected_alt) {
|
|
if (alt < 0) {
|
|
me.setTextElement("Alt11100", sprintf("-% 3i",abs(math.ceil(alt/100))));
|
|
} elsif (alt < 100) {
|
|
me.setTextElement("Alt11100", "");
|
|
} else {
|
|
me.setTextElement("Alt11100", sprintf("% 3i",math.floor(alt/100)));
|
|
}
|
|
|
|
me.getElement("AltLint00011").setTranslation(0,math.fmod(alt,100) * 1.24);
|
|
|
|
if (alt> -1000 and alt< 1000000) {
|
|
var Offset10 = 0;
|
|
var Offset100 = 0;
|
|
var Offset1000 = 0;
|
|
var Ne = 0;
|
|
var Alt10 = math.mod(alt,100);
|
|
var Alt100 = int(math.mod(alt/100,10));
|
|
var Alt1000 = int(math.mod(alt/1000,10));
|
|
var Alt10000 = int(math.mod(alt/10000,10));
|
|
var Alt20 = math.mod(Alt10,20)/20;
|
|
|
|
|
|
if (alt< 0) {
|
|
var Ne = 1;
|
|
var alt= -alt;
|
|
}
|
|
|
|
if (Alt10 >= 80) Alt100 += Alt20;
|
|
if (Alt10 >= 80 and Alt100 >= 9) Alt1000 += Alt20;
|
|
if (Alt10 >= 80 and Alt100 >= 9 and Alt1000 >= 9) Alt10000 += Alt20;
|
|
if (alt> 100) Offset10 = 100;
|
|
if (alt> 1000) Offset100 = 10;
|
|
if (alt> 10000) Offset1000 = 10;
|
|
|
|
if (Ne) {
|
|
me.getElement("LintAlt").setTranslation(0,(math.mod(alt,100))*-0.57375);
|
|
var altCentral = -(int(alt/100)*100);
|
|
} else {
|
|
me.getElement("LintAlt").setTranslation(0,(math.mod(alt,100))*0.57375);
|
|
var altCentral = (int(alt/100)*100);
|
|
}
|
|
|
|
me.setTextElement("AltBigC", "");
|
|
me.setTextElement("AltSmallC", "");
|
|
for (var place = 1; place <= 6; place += 1) {
|
|
var altUP = altCentral + (place*100);
|
|
var altDOWN = altCentral - (place*100);
|
|
var offset = -30.078;
|
|
var prefix = "";
|
|
|
|
if (altUP < 0) {
|
|
altUP = -altUP;
|
|
prefix = "-";
|
|
offset += 15.039;
|
|
}
|
|
var AltBigUP = "";
|
|
var AltSmallUP = "0";
|
|
|
|
if (altUP == 0) {
|
|
AltBigUP = "";
|
|
AltSmallUP = "0";
|
|
} elsif (math.mod(altUP,500) == 0 and altUP != 0) {
|
|
AltBigUP = sprintf(prefix~"%1d", altUP);
|
|
AltSmallUP = "";
|
|
} elsif (altUP < 1000 and (math.mod(altUP,500))) {
|
|
AltBigUP = "";
|
|
AltSmallUP = sprintf(prefix~"%1d", int(math.mod(altUP,1000)));
|
|
offset = -30.078;
|
|
} elsif ((altUP < 10000) and (altUP >= 1000) and (math.mod(altUP,500))) {
|
|
AltBigUP = sprintf(prefix~"%1d", int(altUP/1000));
|
|
AltSmallUP = sprintf("%1d", int(math.mod(altUP,1000)));
|
|
offset += 15.039;
|
|
} else {
|
|
AltBigUP = sprintf(prefix~"%1d", int(altUP/1000));
|
|
mod = int(math.mod(altUP,1000));
|
|
AltSmallUP = sprintf("%1d", mod);
|
|
offset += 30.078;
|
|
}
|
|
|
|
me.getElement("AltBigU"~place).setText(AltBigUP);
|
|
me.getElement("AltSmallU"~place).setText(AltSmallUP);
|
|
me.getElement("AltSmallU"~place).setTranslation(offset,0);
|
|
|
|
offset = -30.078;
|
|
prefix = "";
|
|
if (altDOWN < 0) {
|
|
altDOWN = -altDOWN;
|
|
prefix = "-";
|
|
offset += 15.039;
|
|
}
|
|
|
|
if (altDOWN == 0) {
|
|
AltBigDOWN = "";
|
|
AltSmallDOWN = "0";
|
|
} elsif (math.mod(altDOWN,500) == 0 and altDOWN != 0) {
|
|
AltBigDOWN = sprintf(prefix~"%1d", altDOWN);
|
|
AltSmallDOWN = "";
|
|
} elsif (altDOWN < 1000 and (math.mod(altDOWN,500))) {
|
|
AltBigDOWN = "";
|
|
AltSmallDOWN = sprintf(prefix~"%1d", int(math.mod(altDOWN,1000)));
|
|
offset = -30.078;
|
|
} elsif ((altDOWN < 10000) and (altDOWN >= 1000) and (math.mod(altDOWN,500))) {
|
|
AltBigDOWN = sprintf(prefix~"%1d", int(altDOWN/1000));
|
|
AltSmallDOWN = sprintf("%1d", int(math.mod(altDOWN,1000)));
|
|
offset += 15.039;
|
|
} else {
|
|
AltBigDOWN = sprintf(prefix~"%1d", int(altDOWN/1000));
|
|
AltSmallDOWN = sprintf("%1d", int(math.mod(altDOWN,1000)));
|
|
offset += 30.078;
|
|
}
|
|
|
|
me.getElement("AltBigD"~place).setText(AltBigDOWN);
|
|
me.getElement("AltSmallD"~place).setText(AltSmallDOWN);
|
|
me.getElement("AltSmallD"~place).setTranslation(offset,0);
|
|
}
|
|
}
|
|
|
|
alt_trend = math.clamp(alt_trend, -15, 15);
|
|
me.getElement("Altitude-Trend-Indicator")
|
|
.setScale(1,alt_trend)
|
|
.setTranslation(0, -284.5 * (alt_trend - 1));
|
|
|
|
var delta_alt = alt - selected_alt;
|
|
delta_alt = math.clamp(delta_alt, -300, 300);
|
|
me.getElement("SelectedALT-bug").setTranslation(0, delta_alt * 0.567); # 170/300 = 0.567
|
|
},
|
|
|
|
updateBARO : func (baro) {
|
|
# TODO: Support hPa and inhg
|
|
#var fmt = me._baro_unit == "inhg" ? "%.2fin" : "%i%shPa";
|
|
var fmt = "%.2fIN";
|
|
me.setTextElement("BARO-text", sprintf(fmt, baro));
|
|
},
|
|
|
|
updateOAT : func (oat) {
|
|
# TODO: Support FAHRENHEIT
|
|
me.setTextElement("OAT-text", sprintf((abs(oat) < 10) ? "%.1f %s" : "%i %s", oat, "°C"));
|
|
},
|
|
|
|
updateTime : func (time_sec) {
|
|
var sec = math.mod(time_sec, 60);
|
|
var mins = math.mod((time_sec - sec) / 60, 60);
|
|
var hours = math.mod((time_sec - mins - sec) / 3600, 12);
|
|
me.setTextElement("TIME-text", sprintf("%02d:%02d:%02d", hours, mins, sec));
|
|
},
|
|
|
|
updateHSI : func (hdg) {
|
|
me.getElement("Rose").setRotation(-hdg * D2R);
|
|
me.setTextElement("HDG-text", sprintf("%03u°", hdg));
|
|
},
|
|
|
|
updateHDG : func (hdg) {
|
|
me.getElement("Heading-bug").setRotation(hdg * D2R);
|
|
me.setTextElement("SelectedHDG-text", sprintf("%03d°%s", hdg, ""));
|
|
},
|
|
|
|
# Indicate the selected course, from a given source (OFF, NAV, GPS)
|
|
updateCRS : func (crs) {
|
|
me.getElement("SelectedCRS-text")
|
|
.setText(sprintf("%03d°%s", crs, ""))
|
|
.setColor(me.getController().getCDISource() == "GPS" ? me.COLORS.magenta : me.COLORS.green);
|
|
},
|
|
|
|
updateSelectedALT : func (selected_alt) {
|
|
me.setTextElement("SelectedALT-text", sprintf("%i", selected_alt));
|
|
},
|
|
|
|
setSelectedSPDVisible : func(visible) {
|
|
me._selected_spd_visible = visible;
|
|
if (visible) {
|
|
me.getElement("SelectedSPD-text").show();
|
|
me.getElement("SelectedSPD-bg").show();
|
|
me.getElement("SelectedSPD-bug").show();
|
|
me.getElement("SelectedSPD-symbol").show();
|
|
} else {
|
|
me.getElement("SelectedSPD-text").hide();
|
|
me.getElement("SelectedSPD-bg").hide();
|
|
me.getElement("SelectedSPD-bug").hide();
|
|
me.getElement("SelectedSPD-symbol").hide();
|
|
}
|
|
},
|
|
|
|
updateSelectedSPD : func (selected_spd) {
|
|
me._selected_spd = selected_spd;
|
|
me.setTextElement("SelectedSPD-text", sprintf("%ikt", me._selected_spd));
|
|
},
|
|
|
|
setBRG1 : func(option) { me._setBRG("BRG1",option); },
|
|
setBRG2 : func(option) { me._setBRG("BRG2",option); },
|
|
|
|
_setBRG : func (brg, option) {
|
|
if (option == "OFF") {
|
|
me.getElement(brg).hide();
|
|
me.getElement(brg ~ "-pointer").hide();
|
|
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 {
|
|
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(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.getController().getBRG2(), valid, id, dst, current_heading, brg_heading);
|
|
},
|
|
_updateBRG : func (brg, source, valid, id, dst, current_heading, brg_heading) {
|
|
if (source == "OFF") return;
|
|
|
|
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("");
|
|
} 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 {
|
|
# 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();
|
|
}
|
|
},
|
|
|
|
toggleDME : func() {
|
|
me.setDME(! me._DME);
|
|
},
|
|
|
|
setDME : func (enabled) {
|
|
me._DME = enabled;
|
|
me.getElement("DME1").setVisible(enabled);
|
|
},
|
|
|
|
updateDME : func (mode, freq, dst) {
|
|
if (me._DME == 0) return;
|
|
me.getElement("DME1-SRC-text").setText(mode);
|
|
me.getElement("DME1-FREQ-text").setText(sprintf("%.2f", freq));
|
|
me.getElement("DME1-DST-text").setText(sprintf("%.2fNM", dst));
|
|
},
|
|
|
|
setCDISource : func(source) {
|
|
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.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, 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(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").setText("NO DATA").show();
|
|
} else {
|
|
me.getElement(source ~ "-CDI").show();
|
|
|
|
var rot = (course - heading) * D2R;
|
|
me.getElement("CDI").setRotation(rot).show();
|
|
me.getElement("GPS-CTI-diamond").setRotation(course_deviation_deg * D2R).setVisible(source == "GPS");
|
|
|
|
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")
|
|
.setText(sprintf("XTK %.2fNM", abs(xtrk_nm)))
|
|
.show();
|
|
} else {
|
|
me.getElement("CDI-GPS-XTK-text").hide();
|
|
}
|
|
|
|
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(source ~ "-CDI").setTranslation(80 * scale / 2.4, 0);
|
|
|
|
# Display the appropriate TO/FROM indication for the selected source,
|
|
# switching all others off.
|
|
me.getElement(source ~ "-TO").setVisible(from == 0);
|
|
me.getElement(source ~ "-FROM").setVisible(from);
|
|
}
|
|
},
|
|
|
|
# Update the wind display. There are three options:
|
|
# 0 - No wind data displayed
|
|
# 1 - Numeric headwind and crosswind components
|
|
# 2 - Direction arrow and numeric speed
|
|
# 3 - Direction arrow, and numeric True direction and speet
|
|
setWindDisplay : func(option) {
|
|
me.getElement("WindData").setVisible(option != 0);
|
|
me.getElement("WindData-OPTN1").setVisible(option == 1);
|
|
me.getElement("WindData-OPTN2").setVisible(option == 2);
|
|
me.getElement("WindData-OPTN3").setVisible(option == 3);
|
|
|
|
me._windDataDisplay = option;
|
|
},
|
|
|
|
updateWindData : func (hdg, wind_hdg, wind_spd, no_data) {
|
|
if (no_data and (me._windDataDisplay > 0)) {
|
|
me.getElement("WindData-NODATA").show();
|
|
me.getElement("WindData-NODATA-bg").show();
|
|
return;
|
|
} else {
|
|
me.getElement("WindData-NODATA").hide();
|
|
me.getElement("WindData-NODATA-bg").hide();
|
|
}
|
|
|
|
var alpha = (wind_hdg - hdg);
|
|
|
|
# Stop the wind arrows oscillating
|
|
if (wind_spd < 1) {
|
|
alpha = 0;
|
|
wind_hdg = 0;
|
|
}
|
|
|
|
if (me._windDataDisplay == 0) {
|
|
me.getElement("WindData").hide();
|
|
} elsif (me._windDataDisplay == 1) {
|
|
# Headwind/Crosswind numeric display
|
|
var Vt = wind_spd * math.sin(alpha * D2R);
|
|
var Ve = wind_spd * math.cos(alpha * D2R);
|
|
me.getElement("WindData-OPTN1-crosswind-text").setText(sprintf("%i", abs(Vt)));
|
|
me.getElement("WindData-OPTN1-crosswind").setRotation(Vt > 0.1 ? 180*D2R : 0);
|
|
me.getElement("WindData-OPTN1-headwind-text").setText(sprintf("%i", abs(Ve)));
|
|
me.getElement("WindData-OPTN1-headwind").setRotation(Ve > 0.1 ? 180*D2R : 0);
|
|
} elsif (me._windDataDisplay == 2) {
|
|
# Direction arrow and numeric speed
|
|
me.getElement("WindData-OPTN2-HDG").setRotation((alpha + 180) * D2R);
|
|
me.getElement("WindData-OPTN2-SPD-text").setText(sprintf("%i", wind_spd));
|
|
} elsif (me._windDataDisplay == 3) {
|
|
# Direction arrow with numeric true direction and speed
|
|
me.getElement("WindData-OPTN3-HDG").setRotation((alpha + 180) * D2R);
|
|
me.getElement("WindData-OPTN3-HDG-text").setText(sprintf("%03i°T", wind_hdg));
|
|
me.getElement("WindData-OPTN3-SPD-text").setText(sprintf("%iKT", wind_spd));
|
|
} else {
|
|
print("Unknown wind data option " ~ me._windDataDisplay);
|
|
}
|
|
},
|
|
|
|
# Enable/disable the multiline display on the right hand side of the PFD
|
|
setMultiline : func(enabled) {
|
|
me._Multiline = enabled;
|
|
me.getElement("PFD-Multilines").setVisible(enabled);
|
|
},
|
|
|
|
# Enable/disable the warning annunication window.
|
|
setAnnunciation : func(enabled) {
|
|
me._annunciation = enabled;
|
|
me.getElement("Annunciation").setVisible(enabled);
|
|
},
|
|
|
|
# set the Outer, Middle, Inner indicator
|
|
setOMI : func(omi) {
|
|
if (omi == "") {
|
|
me.getElement("OMI").hide();
|
|
} else {
|
|
me.getElement("OMI").show();
|
|
me.setTextElement("MarkerText", omi);
|
|
}
|
|
me._OMI = omi;
|
|
},
|
|
|
|
setInsetMapVisible :func(enabled ) {
|
|
me.getElement("PFD-Map").setVisible(enabled);
|
|
me.getElement("PFD-Map-bg").setVisible(enabled);
|
|
me.insetMap.setVisible(enabled);
|
|
},
|
|
|
|
setFlightPlanVisible : func(enabled) {
|
|
me.getElement("FlightPlanGroup").setVisible(enabled);
|
|
},
|
|
|
|
# Update the FlightPlan display with an updated flightplan.
|
|
setFlightPlan : func(fp) {
|
|
var elements = [];
|
|
|
|
if (fp == nil) return;
|
|
|
|
var current_wp = fp.current;
|
|
|
|
for (var i = 0; i < fp.getPlanSize(); i = i + 1) {
|
|
var wp = fp.getWP(i);
|
|
|
|
var element = {
|
|
FlightPlanArrow : 0,
|
|
FlightPlanID : "",
|
|
FlightPlanType : "",
|
|
FlightPlanDTK : 0,
|
|
FlightPlanDIS : 0,
|
|
};
|
|
|
|
if (wp.wp_name != nil) element.FlightPlanID = substr(wp.wp_name, 0, 7);
|
|
if (wp.wp_role != nil) element.FlightPlanType = substr(wp.wp_role, 0, 4);
|
|
|
|
if (i < current_wp) {
|
|
# Passed waypoints are blanked out on the display
|
|
element.FlightPlanDIS = "___nm";
|
|
element.FlightPlanDTK = "___°";
|
|
} else {
|
|
if (wp.leg_distance != nil) element.FlightPlanDIS = sprintf("%.1fnm", wp.leg_distance);
|
|
if (wp.leg_bearing != nil) element.FlightPlanDTK = sprintf("%03d°", wp.leg_bearing);
|
|
}
|
|
append(elements, element);
|
|
}
|
|
|
|
me.flightplanList.setValues(elements);
|
|
me.flightplanList.setCRSR(current_wp);
|
|
|
|
# Determine a suitable name to display, using the flightplan name if there is one,
|
|
# but falling back to the flightplan departure / destination airports, or failing
|
|
# that the IDs of the first and last waypoints.
|
|
if ((fp.id == nil) or (fp.id == "default-flightplan")) {
|
|
var from = "????";
|
|
var dest = "????";
|
|
|
|
if ((fp.getWP(0) != nil) and (fp.getWP(0).wp_name != nil)) {
|
|
from = fp.getWP(0).wp_name;
|
|
}
|
|
|
|
if ((fp.getWP(fp.getPlanSize() -1) != nil) and (fp.getWP(fp.getPlanSize() -1).wp_name != nil)) {
|
|
dest = fp.getWP(fp.getPlanSize() -1).wp_name;
|
|
}
|
|
|
|
if (fp.departure != nil) from = fp.departure.id;
|
|
if (fp.destination != nil) dest = fp.destination.id;
|
|
me.getElement("FlightPlanName").setText(from ~ " / " ~ dest);
|
|
} else {
|
|
me.getElement("FlightPlanName").setText(fp.id);
|
|
}
|
|
},
|
|
|
|
# Update the FlightPlan display to indicate the current waypoint
|
|
updateFlightPlan : func(current_wp) {
|
|
if (current_wp == -1) return;
|
|
me.flightplanList.setCRSR(current_wp);
|
|
me.flightplanList.displayGroup();
|
|
},
|
|
|
|
# Update the Transponder display
|
|
updateTransponder : func(mode, code, ident, edit=0) {
|
|
# Data validation on the mode
|
|
if (mode < 0) mode = 0;
|
|
if (mode > 5) mode = 5;
|
|
|
|
# Ensure the code is a 4 digit string representation of a number.
|
|
# Normally this means padding with 0's at the left e.g. 42 becomes 0042.
|
|
# In editing mode we enter values from the left, so we want " " padding on the
|
|
# right, so 42 becomes "42 ". Note also that the code is a string value
|
|
# so we can have "004 "
|
|
if (edit) {
|
|
code = sprintf("%-4s", code);
|
|
} else {
|
|
code = sprintf("%04i", code);
|
|
}
|
|
|
|
# Colour of display. White in OFF, STDBY, TEST modes, green in GND, ON, ALT
|
|
var r = 1.0;
|
|
var g = 1.0;
|
|
var b = 1.0;
|
|
if (mode > 2) {
|
|
r = 0.2;
|
|
g = 1.0;
|
|
b = 0.2;
|
|
}
|
|
|
|
if (ident) {
|
|
me.getElement("XPDR-MODE-text").setText("IDNT").setColor(r,g,b);
|
|
} else {
|
|
me.getElement("XPDR-MODE-text").setText(TRANSPONDER_MODES[mode]).setColor(r,g,b);
|
|
}
|
|
|
|
var codes = split("", code);
|
|
me.getElement("XPDR-DIGIT-3-text").setText(chr(code[0])).setColor(r,g,b);
|
|
me.getElement("XPDR-DIGIT-2-text").setText(chr(code[1])).setColor(r,g,b);
|
|
me.getElement("XPDR-DIGIT-1-text").setText(chr(code[2])).setColor(r,g,b);
|
|
me.getElement("XPDR-DIGIT-0-text").setText(chr(code[3])).setColor(r,g,b);
|
|
},
|
|
};
|