diff --git a/Aircraft/Instruments-3d/FG1000/MFDPages/PFDInstruments.bck1.svg b/Aircraft/Instruments-3d/FG1000/MFDPages/PFDInstruments.bck1.svg new file mode 100644 index 000000000..5e657ea84 --- /dev/null +++ b/Aircraft/Instruments-3d/FG1000/MFDPages/PFDInstruments.bck1.svg @@ -0,0 +1,7627 @@ + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ATTIUDE FAIL + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + HDG + + + + NO GPS POSITION + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 10 + 20 + 30 + 40 + 50 + 60 + 70 + 80 + 80 + 70 + 60 + 50 + 40 + 30 + 20 + 10 + 10 + 20 + 30 + 40 + 50 + 60 + 70 + 80 + 80 + 70 + 60 + 50 + 40 + 30 + 20 + 10 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Vertical Deviance Indicator type (cf page 66) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 2 + 2 + 4 + 4 + + + + + + + + + + + + + + + + + 0 + 10 + 20 + 30 + 40 + 50 + 60 + 70 + 80 + 90 + 100 + 120 + 130 + 140 + 150 + 160 + 170 + 180 + 190 + 200 + 210 + 220 + 230 + 240 + 250 + 260 + 270 + 280 + 290 + 300 + 310 + 320 + 330 + 340 + 350 + 360 + 370 + 380 + 390 + 400 + 410 + 420 + 430 + 440 + 450 + 460 + 470 + 480 + 490 + 500 + 510 + 520 + 530 + 540 + 550 + 560 + 570 + 580 + 590 + 600 + 610 + 620 + 630 + 640 + 650 + 660 + 670 + 680 + 690 + 700 + 710 + 720 + 730 + 740 + 750 + 760 + 770 + 780 + 790 + 800 + 810 + 820 + 830 + 840 + 850 + 860 + 870 + 880 + 890 + 900 + 910 + 920 + 930 + 940 + 950 + 960 + 970 + 980 + 990 + 1000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 110 + + + + G + + + + R + + + + Y + + + + X + + + + 300 + + + 88 + 321098765432109876543210 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 88 + 800 + 88 + 800 + 88 + 800 + 88 + 800 + 88 + 800 + 88 + 800 + 88 + 800 + 88 + 800 + 88 + 800 + 88 + 800 + 88 + 800 + 88 + 800 + 88 + 800 + + + 888 + 604020008060402000806040200020406080002040608000204060 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + N + 3 + 6 + E + 12 + 15 + S + 21 + 24 + W + 30 + 33 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 000° + + + + + XTK 99NM + ANN. + SRC + + + + M + + + + + + + + + + + + + + + + + + + + XPDR + STBY + 23:59:59 + LCL + 1 + 2 + 0 + 0 + + + + + DME + NAV1 + 123.45 + 99.9NM + + + + + + 99.9NM + WPID + NAV1 + + + + + + + + + + + + + + NAV2 + WPID + 99.9NM + + + + + + + + + + + + + + NO WINDDATA + + 99 + 99 + + + + + + + + + + + 99KT + 000° + + + + + + + + XX.XXin + + + + + + OAT + + OAT + 99.9°C + + + + Ground Speed + + GS + 999KT + + + + True Airspeed + + TAS + 999KT + + + + CRS + 000° + + + + HDG + 000° + + + + + 99999 + + + + diff --git a/Aircraft/Instruments-3d/FG1000/MFDPages/PFDInstruments.svg b/Aircraft/Instruments-3d/FG1000/MFDPages/PFDInstruments.svg new file mode 100644 index 000000000..cd59bf4dd --- /dev/null +++ b/Aircraft/Instruments-3d/FG1000/MFDPages/PFDInstruments.svg @@ -0,0 +1,7629 @@ + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ATTIUDE FAIL + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + HDG + + + + NO GPS POSITION + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 10 + 20 + 30 + 40 + 50 + 60 + 70 + 80 + 80 + 70 + 60 + 50 + 40 + 30 + 20 + 10 + 10 + 20 + 30 + 40 + 50 + 60 + 70 + 80 + 80 + 70 + 60 + 50 + 40 + 30 + 20 + 10 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Vertical Deviance Indicator type (cf page 66) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 2 + 2 + 4 + 4 + + + + + + + + + + + + + + + + + 0 + 10 + 20 + 30 + 40 + 50 + 60 + 70 + 80 + 90 + 100 + 120 + 130 + 140 + 150 + 160 + 170 + 180 + 190 + 200 + 210 + 220 + 230 + 240 + 250 + 260 + 270 + 280 + 290 + 300 + 310 + 320 + 330 + 340 + 350 + 360 + 370 + 380 + 390 + 400 + 410 + 420 + 430 + 440 + 450 + 460 + 470 + 480 + 490 + 500 + 510 + 520 + 530 + 540 + 550 + 560 + 570 + 580 + 590 + 600 + 610 + 620 + 630 + 640 + 650 + 660 + 670 + 680 + 690 + 700 + 710 + 720 + 730 + 740 + 750 + 760 + 770 + 780 + 790 + 800 + 810 + 820 + 830 + 840 + 850 + 860 + 870 + 880 + 890 + 900 + 910 + 920 + 930 + 940 + 950 + 960 + 970 + 980 + 990 + 1000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 110 + + + + G + + + + R + + + + Y + + + + X + + + + 300 + + + 88 + 321098765432109876543210 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 88 + 800 + 88 + 800 + 88 + 800 + 88 + 800 + 88 + 800 + 88 + 800 + 88 + 800 + 88 + 800 + 88 + 800 + 88 + 800 + 88 + 800 + 88 + 800 + 88 + 800 + + + 888 + 604020008060402000806040200020406080002040608000204060 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + N + 3 + 6 + E + 12 + 15 + S + 21 + 24 + W + 30 + 33 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 000° + + + + XTK 99NM + ANN. + SRC + + + + M + + + + + + + + + + + + + + + + + + + + XPDR + STBY + 23:59:59 + LCL + 1 + 2 + 0 + 0 + + + + + DME + NAV1 + 123.45 + 99.9NM + + + + + + 99.9NM + WPID + NAV1 + + + + + + + + + + + + + + NAV2 + WPID + 99.9NM + + + + + + + + NO WINDDATA + + 99 + 99 + + + + + + + + + + + 99KT + 000° + + + + + + + + XX.XXin + + + + + + OAT + + OAT + 99.9°C + + + + Ground Speed + + GS + 999KT + + + + True Airspeed + + TAS + 999KT + + + + CRS + 000° + + + + HDG + 000° + + + + + 99999 + + + + 99KT + + + + + + diff --git a/Aircraft/Instruments-3d/FG1000/Nasal/ConfigStore.nas b/Aircraft/Instruments-3d/FG1000/Nasal/ConfigStore.nas index 9bf37adba..9cbd4a712 100644 --- a/Aircraft/Instruments-3d/FG1000/Nasal/ConfigStore.nas +++ b/Aircraft/Instruments-3d/FG1000/Nasal/ConfigStore.nas @@ -70,6 +70,20 @@ var ConfigStore = { "MFDHeader2": ["BRG", "XTK", "DIS", "DTK", "END", "ESA", "ETA", "ETE", "FOD", "FOB", "GS", "MSA", "TAS", "TKE", "TRK", "VSR"] , "MFDHeader3": ["BRG", "XTK", "DIS", "DTK", "END", "ESA", "ETA", "ETE", "FOD", "FOB", "GS", "MSA", "TAS", "TKE", "TRK", "VSR"] , "MFDHeader4": ["BRG", "XTK", "DIS", "DTK", "END", "ESA", "ETA", "ETE", "FOD", "FOB", "GS", "MSA", "TAS", "TKE", "TRK", "VSR"] , + + # V-speeds (Cessna 182T) + "Vx" : 54, # Short field takeoff, 2600lbs + "Vy" : 78, # 4000ft, 3100lbs + "Vr" : 78, + "Vglide" : 70, # 2600lbs + "Vne": 175, + + "Vx-visible" : 1, + "Vy-visible" : 1, + "Vr-visible" : 1, + "Vglide-visible" : 1, + "Vne-visible": 1, + }, new : func() @@ -81,7 +95,8 @@ var ConfigStore = { foreach (var i; keys(ConfigStore.configValues)) { var values = ConfigStore.configValues[i]; - obj.set(i, values[0]); + if (typeof(values) == "vector") obj.set(i, values[0]); + if (typeof(values) == "scalar") obj.set(i, values); } # Special case defaults @@ -99,10 +114,7 @@ var ConfigStore = { # Validate name is something we know. assert(contains(ConfigStore.configValues, name), "ConfigStore does not contain name " ~ name); - if (size(ConfigStore.configValues[name]) == 0) { - # If not valid values, then anything goes. - me._values[name] = value; - } else { + if (typeof(ConfigStore.configValues[name]) == "vector") { # Validate the value is part of the set of acceptable values var found = 0; foreach(var val; ConfigStore.configValues[name]) { @@ -117,6 +129,11 @@ var ConfigStore = { "(Should be one of " ~ string.join(", ", ConfigStore.configValues[name]) ~ ")"); me._values[name] = value; + }elsif (typeof(ConfigStore.configValues[name]) == "scalar") { + # If not valid values, then anything goes. + me._values[name] = value; + } else { + die("Unknown ConfigStore type " ~ typeof(ConfigStore.configValues[name])); } }, diff --git a/Aircraft/Instruments-3d/FG1000/Nasal/EIS/EISController.nas b/Aircraft/Instruments-3d/FG1000/Nasal/EIS/EISController.nas index 112cee17f..f34cb47b4 100644 --- a/Aircraft/Instruments-3d/FG1000/Nasal/EIS/EISController.nas +++ b/Aircraft/Instruments-3d/FG1000/Nasal/EIS/EISController.nas @@ -63,7 +63,7 @@ var EISController = transmitter = emesary.GlobalTransmitter; if (me._recipient == nil){ - me._recipient = emesary.Recipient.new("AirportInfoController_" ~ me._page.device.designation); + me._recipient = emesary.Recipient.new("EISController_" ~ me._page.device.designation); var pfd_obj = me._page.device; var controller = me; me._recipient.Receive = func(notification) diff --git a/Aircraft/Instruments-3d/FG1000/Nasal/FG1000.nas b/Aircraft/Instruments-3d/FG1000/Nasal/FG1000.nas index 46e7edd8a..df8c3041b 100644 --- a/Aircraft/Instruments-3d/FG1000/Nasal/FG1000.nas +++ b/Aircraft/Instruments-3d/FG1000/Nasal/FG1000.nas @@ -28,6 +28,7 @@ var nasal_dir = getprop("/sim/fg-root") ~ "/Aircraft/Instruments-3d/FG1000/Nasal io.load_nasal(nasal_dir ~ '/ConfigStore.nas', "fg1000"); io.load_nasal(nasal_dir ~ '/MFDPage.nas', "fg1000"); 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"); @@ -101,11 +102,36 @@ addMFD : func(index=nil, targetcanvas=nil, screenObject=nil) { }); } - var mfd = fg1000.MFD.new(me, me.EIS_Class, me.EIS_SVG, targetcanvas, index); + var mfd = fg1000.MFDDisplay.new(me, me.EIS_Class, me.EIS_SVG, targetcanvas, index); me.displays[index] = mfd; return index; }, +# Add a PFD, optionally setting the index. Returns the index of the PFD. +addPFD : func(index=nil, targetcanvas=nil, screenObject=nil) { + + if (index == nil) { + index = size(keys(me.displays)); + } else if (me.displays[index] != nil) { + print("FG1000 Index " ~ index ~ " already exists!"); + return + } + + if (targetcanvas == nil) { + targetcanvas = canvas.new({ + "name" : "PFD Canvas", + "size" : [1024, 768], + "view" : [1024, 768], + "mipmapping": 0, + }); + } + + var pfd = fg1000.PFDDisplay.new(me, me.EIS_Class, me.EIS_SVG, targetcanvas, index); + me.displays[index] = pfd; + return index; +}, + + display : func(index, target_object=nil) { if (me.displays[index] == nil) { print("displayMFD: unknown display index " ~ index); diff --git a/Aircraft/Instruments-3d/FG1000/Nasal/Interfaces/GenericADCPublisher.nas b/Aircraft/Instruments-3d/FG1000/Nasal/Interfaces/GenericADCPublisher.nas index 15658b3f4..d3e66a4ba 100644 --- a/Aircraft/Instruments-3d/FG1000/Nasal/Interfaces/GenericADCPublisher.nas +++ b/Aircraft/Instruments-3d/FG1000/Nasal/Interfaces/GenericADCPublisher.nas @@ -29,7 +29,7 @@ var GenericADCPublisher = { - new : func (frequency=0.5) { + new : func (frequency=0.2) { var obj = { parents : [ GenericADCPublisher, @@ -38,7 +38,24 @@ var GenericADCPublisher = }; obj.addPropMap("ADCTrueAirspeed", "/instrumentation/airspeed-indicator/true-speed-kt"); + obj.addPropMap("ADCIndicatedAirspeed", "/instrumentation/airspeed-indicator/indicated-speed-kt"); + obj.addPropMap("ADCPitchDeg", "/instrumentation/attitude-indicator/indicated-pitch-deg"); + obj.addPropMap("ADCRollDeg", "/instrumentation/attitude-indicator/indicated-roll-deg"); + obj.addPropMap("ADCTurnRate", "/instrumentation/turn-indicator/indicated-turn-rate"); + obj.addPropMap("ADCSlipSkid", "/instrumentation/slip-skid-ball/indicated-slip-skid"); + + # Assume an accurate solid-state compass + obj.addPropMap("ADCHeadingDeg", "/orientation/heading-deg"); + + obj.addPropMap("ADCAltitudeFT", "/instrumentation/altimeter/indicated-altitude-ft"); + obj.addPropMap("ADCPressureSettingInHG", "/instrumentation/altimeter/setting-inhg"); + + obj.addPropMap("ADCVerticalSpeedFPM", "/instrumentation/vertical-speed-indicator/indicated-speed-fpm"); + + obj.addPropMap("ADCOutsideAirTemperatureC", "/environment/temperature-degc"); + obj.addPropMap("ADCWindHeadingDeg", "/environment/wind-from-heading-deg"); + obj.addPropMap("ADCWindSpeedKt", "/environment/wind-speed-kt"); return obj; }, }; diff --git a/Aircraft/Instruments-3d/FG1000/Nasal/Interfaces/GenericFMSPublisher.nas b/Aircraft/Instruments-3d/FG1000/Nasal/Interfaces/GenericFMSPublisher.nas index 37ccbb3e5..cd4b122b1 100644 --- a/Aircraft/Instruments-3d/FG1000/Nasal/Interfaces/GenericFMSPublisher.nas +++ b/Aircraft/Instruments-3d/FG1000/Nasal/Interfaces/GenericFMSPublisher.nas @@ -26,6 +26,9 @@ var GenericFMSPublisher = ], }; + obj.addPropMap("FMSHeadingBug", "/autopilot/settings/heading-bug-deg"); + obj.addPropMap("FMSSelectedAlt", "/autopilot/settings/target-alt-ft"); + obj.addPropMap("FMSLegBearing", "/instrumentation/gps/wp/wp[1]/bearing-mag-deg"); obj.addPropMap("FMSLegCourseError", "/instrumentation/gps/wp/wp[1]/course-error-nm"); obj.addPropMap("FMSLegDesiredTrack", "/instrumentation/gps/indicated-track-magnetic-deg"); @@ -34,6 +37,9 @@ var GenericFMSPublisher = obj.addPropMap("FMSGroundspeed", "/instrumentation/gps/indicated-ground-speed-kt"); obj.addPropMap("FMSWayPointCourseError", "/instrumentation/gps/wp/wp[1]/course-error-nm"); + obj.addPropMap("FMSNav1From", "/instrumentation/nav/from-flag"); + obj.addPropMap("FMSNav2From", "/instrumentation/nav[1]/from-flag"); + return obj; }, diff --git a/Aircraft/Instruments-3d/FG1000/Nasal/Interfaces/PropertyPublisher.nas b/Aircraft/Instruments-3d/FG1000/Nasal/Interfaces/PropertyPublisher.nas index 2b7cf1437..a27ca1945 100644 --- a/Aircraft/Instruments-3d/FG1000/Nasal/Interfaces/PropertyPublisher.nas +++ b/Aircraft/Instruments-3d/FG1000/Nasal/Interfaces/PropertyPublisher.nas @@ -35,7 +35,11 @@ var PropMap = { getName : func() { return me._name; }, getPropPath : func() { return me._prop.getPath(); }, - getValue : func() { return me._prop.getValue(); }, + getValue : func() { + var val = me._prop.getValue(); + if (val == nil) val = 0; + return val; + }, getProp: func() { return me._prop; }, }; diff --git a/Aircraft/Instruments-3d/FG1000/Nasal/MFD.nas b/Aircraft/Instruments-3d/FG1000/Nasal/MFD.nas index 646249e25..3df92bb9e 100644 --- a/Aircraft/Instruments-3d/FG1000/Nasal/MFD.nas +++ b/Aircraft/Instruments-3d/FG1000/Nasal/MFD.nas @@ -67,12 +67,12 @@ foreach (var page; MFDPages) { io.load_nasal(nasal_dir ~ "MFDPages/" ~ page ~ '/' ~ page ~ 'Controller.nas', "fg1000"); } -var MFD = +var MFDDisplay = { new : func (fg1000instance, EIS_Class, EIS_SVG, myCanvas, device_id=1) { var obj = { - parents : [ MFD ], + parents : [ MFDDisplay ], EIS : nil, NavigationMap: nil, Surround : nil, diff --git a/Aircraft/Instruments-3d/FG1000/Nasal/MFDPage.nas b/Aircraft/Instruments-3d/FG1000/Nasal/MFDPage.nas index 6f7728983..7b3cf932b 100644 --- a/Aircraft/Instruments-3d/FG1000/Nasal/MFDPage.nas +++ b/Aircraft/Instruments-3d/FG1000/Nasal/MFDPage.nas @@ -40,8 +40,9 @@ new : func (mfd, myCanvas, device, SVGGroup, pageName, title) _group : myCanvas.createGroup(pageName ~ "Layer"), _SVGGroup : SVGGroup, parents : [ MFDPage, device.addPage(title, pageName ~ "Group") ], - _symbols : {}, + _textElements : {}, _controller : nil, + _elements : {}, }; obj.device = device; @@ -60,32 +61,64 @@ new : func (mfd, myCanvas, device, SVGGroup, pageName, title) return obj; }, +addElement : func(e) { + if (me._elements[e] == nil) { + var element = me._SVGGroup.getElementById(me.pageName ~ e); + if (element != nil) { + me._elements[e] = element; + } else { + die("Unable to find element " ~ me.pageName ~ e); + } + } else { + die("Element already exists: "~ me.pageName ~ e); + } +}, + +addElements : func(elements) { + foreach (var e; elements) { + me.addElement(e); + } +}, + +getElement : func(e) { + if (me._elements[e] == nil) me.addElement(e); + return me._elements[e]; +}, + addTextElements : func(symbols) { foreach (var s; symbols) { - me._symbols[s] = PFD.TextElement.new(me.pageName, me._SVGGroup, s); + me._textElements[s] = PFD.TextElement.new(me.pageName, me._SVGGroup, s); + } +}, + +addTextElement : func(e) { + if (me._textElements[e] == nil) { + me._textElements[e] = PFD.TextElement.new(me.pageName, me._SVGGroup, e); + } else { + die("addTextElement element already exists: "~ me.pageName ~ e); } }, getTextElement : func(symbolName) { - return me._symbols[symbolName]; + return me._textElements[symbolName]; }, highlightTextElement : func(symbolName) { - me._symbols[symbolName].highlightElement(); + me._textElements[symbolName].highlightElement(); }, unhighlightTextElement : func(symbolName) { - me._symbols[symbolName].unhighlightElement(); + me._textElements[symbolName].unhighlightElement(); }, getTextValue : func(symbolName) { - var sym = me._symbols[symbolName]; + var sym = me._textElements[symbolName]; assert(sym != nil, "Unknown text element " ~ symbolName ~ " (check your addTextElements call?)"); return sym.getValue(); }, setTextElement : func(symbolName, value) { - var sym = me._symbols[symbolName]; + var sym = me._textElements[symbolName]; assert(sym != nil, "Unknown text element " ~ symbolName ~ " (check your addTextElements call?)"); if (value == nil ) value = ""; sym.setValue(value); diff --git a/Aircraft/Instruments-3d/FG1000/Nasal/MFDPages/PFDInstruments/PFDInstruments.nas b/Aircraft/Instruments-3d/FG1000/Nasal/MFDPages/PFDInstruments/PFDInstruments.nas new file mode 100644 index 000000000..8c890621c --- /dev/null +++ b/Aircraft/Instruments-3d/FG1000/Nasal/MFDPages/PFDInstruments/PFDInstruments.nas @@ -0,0 +1,574 @@ +# 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 . +# +# 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, + _CDISource : "OFF", + _BRG1 : "OFF", + _BRG2 : "OFF", + _DME : 0, + _OMI : "", + _Map : 0, + _Multiline : 0, + _annunciation : 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", + ]); + + # 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.topMenu(device, obj, nil); + + obj.setController(fg1000.PFDInstrumentsController.new(obj, svg)); + obj.setWindDisplay(0); + obj.setCDISource("OFF"); + obj.setBRG1("OFF"); + obj.setBRG2("OFF"); + obj.setDME(0); + obj.setMap(0); + obj.setMultiline(0); + obj.setAnnunciation(0); + obj.setOMI(""); + + return obj; + }, + 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.mfd.setPageTitle(me.title); + me.getController().ondisplay(); + }, + topMenu : func(device, pg, menuitem) { + pg.clearMenu(); + pg.resetMenuColors(); + device.updateMenus(); + }, + + 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); + }, + + 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(); + } + } + }, + + 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)); + }, + + 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")); + }, + + updateHSI : func (hdg) { + me.getElement("Rose").setRotation(-hdg * D2R); + me.setTextElement("HDG-text", sprintf("%03u°", hdg)); + }, + + updateHDG : func (hdg) { + if (hdg == nil) + 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._CDISource == "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._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._BRG1 == "OFF")) { + me.getElement("BRG-circle").hide(); + } + } else { + me.getElement(brg).show(); + me.getElement(brg ~ "-pointer").show(); + me.getElement("BRG-circle").show(); + } + }, + + # Update BRG information + updateBRG1 : func(id, dst) { + me._updateBRG("BRG1", me._BRG1, id, dst); + }, + updateBRG2 : func(id, dst) { + me._updateBRG("BRG2", me._BRG2, id, dst); + }, + _updateBRG : func (brg, source, id, dst) { + if (source == "OFF") return; + + me.getElement(brg ~ "-SRC-text").setText(source); + me.getElement(brg ~ "-WP-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)); + } + }, + + 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) { + 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); + } + } + + me._CDISource = source; + }, + + updateCDI : func (heading, course, waypoint_valid, course_deviation_deg, deflection_dots, xtrk_nm, from) { + 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 { + me.getElement("CDI-GPS-XTK-text").hide(); + } + + 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: + # -1 - Data invalid + # 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-NODATA").setVisible(option == -1); + 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) { + if ((me._windDataDisplay == -1) or (me._windDataDisplay == 0)) { + return; + } + + if (me._windDataDisplay == 1) { + # Headwind/Crosswind numeric display + var alpha = (wind_hdg - hdg) * D2R; + var Vt = wind_spd * math.sin(alpha); + var Ve = wind_spd * math.cos(alpha); + if (Vt != 0) { + me.getElement("WindData-OPTN1-crosswind-text").setText(sprintf("%i", abs(Vt))); + me.getElement("WindData-OPTN1-crosswind") + .setScale(-abs(Vt)/Vt, 1) + .setTranslation(-35 * (abs(Vt)/Vt + 1), 0); + } else { + me.getElement("WindData-OPTN1-crosswind-text").setText("0"); + me.getElement("WindData-OPTN1-crosswind") + .setScale(1, 1) + .setTranslation(0, 0); + } + + if (Ve != 0) { + me.getElement("WindData-OPTN1-headwind-text").setText(sprintf("%i", abs(Ve))); + me.getElement("WindData-OPTN1-headwind") + .setScale(1, abs(Ve)/Ve) + .setTranslation(0, 515 * (1 - abs(Ve)/Ve)); + } else { + me.getElement("WindData-OPTN1-headwind-text").setText("0"); + me.getElement("WindData-OPTN1-headwind") + .setScale(1, 1) + .setTranslation(0, 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(int(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(int(wind_spd) ~ "KT"); + } else { + print("Unknown wind data option " ~ me._windDataDisplay); + } + }, + + # Enable/disable the inset PFD Map. + setMap : func (enabled) { + me._Map = enabled; + me.getElement("PFD-Map").setVisible(enabled); + }, + + # 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.getElement("MarkerText").setText(omi); + } + me._OMI = omi; + } +}; diff --git a/Aircraft/Instruments-3d/FG1000/Nasal/MFDPages/PFDInstruments/PFDInstrumentsController.nas b/Aircraft/Instruments-3d/FG1000/Nasal/MFDPages/PFDInstruments/PFDInstrumentsController.nas new file mode 100644 index 000000000..3502fa2eb --- /dev/null +++ b/Aircraft/Instruments-3d/FG1000/Nasal/MFDPages/PFDInstruments/PFDInstrumentsController.nas @@ -0,0 +1,201 @@ +# 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 . +# +# PFDInstruments Controller +var PFDInstrumentsController = +{ + new : func (page, svg) + { + var obj = { + parents : [ PFDInstrumentsController, MFDPageController.new(page) ], + _crsrToggle : 0, + _pfdrecipient : nil, + page : page, + _last_ias_kt : 0, + _last_alt_ft : 0, + _last_trend : systime(), + _selected_alt_ft : 0, + _heading : 0, + _source : "GPS", + }; + + return obj; + }, + + + # Input Handling + handleCRSR : func() { + me._crsrToggle = (! me._crsrToggle); + if (me._crsrToggle) { + } else { + me.page.hideCRSR(); + } + return emesary.Transmitter.ReceiptStatus_Finished; + }, + handleFMSInner : func(value) { + if (me._crsrToggle == 1) { + # Scroll through whatever is the current list + return emesary.Transmitter.ReceiptStatus_Finished; + } else { + # Pass to the page group controller to display and scroll through the page group menu + #return me.page.mfd.SurroundController.handleFMSInner(value); + } + }, + handleFMSOuter : func(value) { + if (me._crsrToggle == 1) { + return emesary.Transmitter.ReceiptStatus_Finished; + } else { + # Pass to the page group controller to display and scroll through the page group menu + #return me.page.mfd.SurroundController.handleFMSOuter(value); + } + }, + handleEnter : func(value) { + if (me._crsrToggle == 1) { + return emesary.Transmitter.ReceiptStatus_Finished; + } else { + return emesary.Transmitter.ReceiptStatus_NotProcessed; + } + }, + + # Handle update of the airdata information. + # ADC data is produced periodically as an entire set + handleADCData : func(data) { + var ias = data["ADCIndicatedAirspeed"]; + var alt = data["ADCAltitudeFT"]; + # estimated speed and altitude in 6s + var now = systime(); + var lookahead_ias_6sec = 6 * (ias - me._last_ias_kt) / (now - me._last_trend); + var lookahead_alt_6sec = .3 * (alt - me._last_alt_ft) / (now - me._last_trend); # scale = 1/20ft + me.page.updateIAS(ias, lookahead_ias_6sec); + me.page.updateALT(alt, lookahead_alt_6sec, me._selected_alt_ft); + me._last_ias_kt = ias; + me._last_alt_ft = alt; + me._last_trend = now; + + var pitch = data["ADCPitchDeg"]; + var roll = data["ADCRollDeg"]; + var slip = data["ADCSlipSkid"]; + me.page.updateAI(pitch, roll, slip); + + 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"]; + + me.page.updateWindData( + hdg : data["ADCHeadingDeg"], + wind_hdg : data["ADCWindHeadingDeg"], + wind_spd : data ["ADCWindSpeedKt"] + ); + + return emesary.Transmitter.ReceiptStatus_OK; + }, + + # Handle update to the FMS information. + 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"]; + } + + 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, + ); + + return emesary.Transmitter.ReceiptStatus_OK; + }, + + # Handle update of the NavCom data. + # Note that this updated on a property by property basis, so we need to check + # that the data we want exists in this notification, unlike the periodic + # publishers + handleNavComData : func(data) { + if (data["NavSelected"] != nil) { + me._navSelected = data["NavSelected"]; + } + }, + + PFDRegisterWithEmesary : func(transmitter = nil){ + if (transmitter == nil) + transmitter = emesary.GlobalTransmitter; + + if (me._pfdrecipient == nil){ + me._pfdrecipient = emesary.Recipient.new("PFDInstrumentsController_" ~ me._page.device.designation); + var pfd_obj = me._page.device; + var controller = me; + me._pfdrecipient.Receive = func(notification) + { + if (notification.NotificationType == notifications.PFDEventNotification.DefaultType and + notification.Event_Id == notifications.PFDEventNotification.ADCData and + notification.EventParameter != nil) + { + return controller.handleADCData(notification.EventParameter); + } + + if (notification.NotificationType == notifications.PFDEventNotification.DefaultType and + notification.Event_Id == notifications.PFDEventNotification.FMSData and + notification.EventParameter != nil) + { + return controller.handleFMSData(notification.EventParameter); + } + + if (notification.NotificationType == notifications.PFDEventNotification.DefaultType and + notification.Event_Id == notifications.PFDEventNotification.NavComData and + notification.EventParameter != nil) + { + return controller.handleNavComData(notification.EventParameter); + } + + return emesary.Transmitter.ReceiptStatus_NotProcessed; + }; + } + transmitter.Register(me._pfdrecipient); + me.transmitter = transmitter; + }, + PFDDeRegisterWithEmesary : func(transmitter = nil){ + # remove registration from transmitter; but keep the recipient once it is created. + if (me.transmitter != nil) + me.transmitter.DeRegister(me._pfdrecipient); + me.transmitter = nil; + }, + + # Reset controller if required when the page is displayed or hidden + ondisplay : func() { + me.RegisterWithEmesary(); + me.PFDRegisterWithEmesary(); + }, + offdisplay : func() { + me.DeRegisterWithEmesary(); + me.PFDDeRegisterWithEmesary(); + }, +}; diff --git a/Aircraft/Instruments-3d/FG1000/Nasal/MFDPages/PFDInstruments/PFDInstrumentsOptions.nas b/Aircraft/Instruments-3d/FG1000/Nasal/MFDPages/PFDInstruments/PFDInstrumentsOptions.nas new file mode 100644 index 000000000..783802dca --- /dev/null +++ b/Aircraft/Instruments-3d/FG1000/Nasal/MFDPages/PFDInstruments/PFDInstrumentsOptions.nas @@ -0,0 +1,44 @@ +# 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 . +# +# PFDInstruments Options +var PFDInstrumentsOptions = +{ + new : func() { + var obj = { parents : [PFDInstrumentsOptions] }; + obj.Options= {}; + obj.loadOptions(); + return obj; + }, + + getOption : func(type) { + return me.Options[type]; + }, + + setOption : func(type, name, value) { + me.Options[type][name] = value; + }, + + loadOptions : func() { + me.clearOptions(); + me.Options.APS = {}; + }, + + clearOptions : func() { + me.Options = {}; + }, + +}; diff --git a/Aircraft/Instruments-3d/FG1000/Nasal/MFDPages/PFDInstruments/PFDInstrumentsStyles.nas b/Aircraft/Instruments-3d/FG1000/Nasal/MFDPages/PFDInstruments/PFDInstrumentsStyles.nas new file mode 100644 index 000000000..78d6042a4 --- /dev/null +++ b/Aircraft/Instruments-3d/FG1000/Nasal/MFDPages/PFDInstruments/PFDInstrumentsStyles.nas @@ -0,0 +1,44 @@ +# 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 . +# +# PFDInstruments Styles +var PFDInstrumentsStyles = +{ + new : func() { + var obj = { parents : [ PFDInstrumentsStyles ]}; + obj.Styles = {}; + obj.loadStyles(); + return obj; + }, + + getStyle : func(type) { + return me.Styles[type]; + }, + + setStyle : func(type, name, value) { + me.Styles[type][name] = value; + }, + + loadStyles : func() { + me. clearStyles(); + me.Styles.XXX = {}; + }, + + clearStyles : func() { + me.Styles = {}; + }, + +}; diff --git a/Aircraft/Instruments-3d/FG1000/Nasal/PFD.nas b/Aircraft/Instruments-3d/FG1000/Nasal/PFD.nas new file mode 100644 index 000000000..b121c2c84 --- /dev/null +++ b/Aircraft/Instruments-3d/FG1000/Nasal/PFD.nas @@ -0,0 +1,121 @@ +# 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 . +# +# FG1000 PFD + +var PFDDisplay = +{ + new : func (fg1000instance, EIS_Class, EIS_SVG, myCanvas, device_id=1) + { + var obj = { + parents : [ PFDDisplay ], + EIS : nil, + PFDInstruments : nil, + Surround : nil, + _pageList : {}, + _fg1000 : fg1000instance, + _canvas : myCanvas, + }; + + var nasal_dir = getprop("/sim/fg-root") ~ "/Aircraft/Instruments-3d/FG1000/Nasal/"; + io.load_nasal(nasal_dir ~ "MFDPages/PFDInstruments/PFDInstruments.nas", "fg1000"); + io.load_nasal(nasal_dir ~ "MFDPages/PFDInstruments/PFDInstrumentsStyles.nas", "fg1000"); + io.load_nasal(nasal_dir ~ "MFDPages/PFDInstruments/PFDInstrumentsOptions.nas", "fg1000"); + io.load_nasal(nasal_dir ~ "MFDPages/PFDInstruments/PFDInstrumentsController.nas", "fg1000"); + + obj.ConfigStore = obj._fg1000.getConfigStore(); + + obj._svg = myCanvas.createGroup("softkeys"); + obj._svg.set("clip-frame", canvas.Element.LOCAL); + obj._svg.set("clip", "rect(0px, 1024px, 768px, 0px)"); + + var fontmapper = func (family, weight) { + #if( family == "Liberation Sans" and weight == "narrow" ) { + return "LiberationFonts/LiberationSansNarrow-Regular.ttf"; + #} + # If we don't return anything the default font is used + }; + + canvas.parsesvg(obj._svg, + EIS_SVG, + {'font-mapper': fontmapper}); + + canvas.parsesvg(obj._svg, + '/Aircraft/Instruments-3d/FG1000/MFDPages/PFDInstruments.svg', + {'font-mapper': fontmapper}); + + canvas.parsesvg(obj._svg, + '/Aircraft/Instruments-3d/FG1000/MFDPages/Surround.svg', + {'font-mapper': fontmapper}); + + obj._MFDDevice = canvas.PFD_Device.new(obj._svg, 12, "SoftKey", myCanvas, "PFD"); + obj._MFDDevice.device_id = device_id; + obj._MFDDevice.RegisterWithEmesary(); + + # Surround dynamic elements + obj._pageTitle = obj._svg.getElementById("PageTitle"); + + # Controller for the header and display on the bottom left which allows selection + # of page groups and individual pages using the FMS controller. + obj.Surround = fg1000.Surround.new(obj, myCanvas, obj._MFDDevice, obj._svg); + obj.SurroundController = obj.Surround.getController(); + + # Engine Information System. A special case as it's always displayed on the MFD. + # Note that it is passed in on the constructor + obj.EIS = EIS_Class.new(obj, myCanvas, obj._MFDDevice, obj._svg); + obj.addPage("EIS", obj.EIS); + + obj.PFDInstruments = fg1000.PFDInstruments.new(obj, myCanvas, obj._MFDDevice, obj._svg); + obj.addPage("PFDInstruments", obj.PFDInstruments); + obj.PFDInstruments.topMenu(obj._MFDDevice, obj.PFDInstruments, nil); + + # Display the Surround, and PFD Instruments + obj.Surround.setVisible(1); + obj._MFDDevice.selectPage(obj.PFDInstruments); + + return obj; + }, + getDevice : func () { + return me._MFDDevice; + }, + del: func() + { + me._MFDDevice.current_page.offdisplay(); + me._MFDDevice.DeRegisterWithEmesary(); + me.SurroundController.del(); + }, + setPageTitle: func(title) + { + me._pageTitle.setText(title); + }, + addPage : func(name, page) + { + me._pageList[name] = page; + }, + + getPage : func(name) + { + return me._pageList[name]; + }, + + getDeviceID : func() { + return me._MFDDevice.device_id; + }, + + getCanvas : func() { + return me._canvas; + } +}; diff --git a/Nasal/notifications.nas b/Nasal/notifications.nas index 562aa491e..957dac8ef 100644 --- a/Nasal/notifications.nas +++ b/Nasal/notifications.nas @@ -61,7 +61,7 @@ var PFDEventNotification_Id = 20; # # That's all that is required to ship properties between multiplayer modules via emesary. # property /sim/multiplay/transmit-filter-property-base can be set to 1 to turn off all of the standard properties and only send generics. -# this will give a packet size of 280 bytes; leaving lots of space for notifications. +# this will give a packet size of 280 bytes; leaving lots of space for notifications. # The F-14 packet size is around 53 bytes on 2017.2 compared to over 1100 bytes with the traditional method. # property /sim/multiplay/transmit-filter-property-base can be set to a number greater than 1 (e.g. 12000) to only transmit properties # where the ID is greater than the value in the property. This can further reduce packet size by only transmitting the emesary bridge data @@ -558,7 +558,7 @@ var PFDEventNotification = NavComData : 6, #event parameter contains a hash of updated Nav/Com settings NavData : 7, #event parameter contrains a single { Id: , Value: } tuple requesting a particular type of NavData FMSData : 8, #event parameter containing a hash of updated GPS/FMS information (track, ground-speed, waypoint legs etc.) - ADCData : 8, #event parameter containing a hash of updated Air Data Computer information (track, ground-speed etc.) + ADCData : 9, #event parameter containing a hash of updated Air Data Computer information (track, ground-speed etc.) DefaultType : "PFDEventNotification", diff --git a/Translations/default/menu.xml b/Translations/default/menu.xml index 8fdd8ec29..13caf08e4 100644 --- a/Translations/default/menu.xml +++ b/Translations/default/menu.xml @@ -110,7 +110,8 @@ Monitor System Performance Save Video Configuration Browse Internal Properties - FG1000 MFD + FG1000 PFD + FG1000 MFD Logging Local Weather (Test) Print Visible Scene Info diff --git a/gui/menubar.xml b/gui/menubar.xml index ccde973f5..4432a9c2e 100644 --- a/gui/menubar.xml +++ b/gui/menubar.xml @@ -822,7 +822,7 @@ - fg1000 + fg1000-pfd nasal + + + + + fg1000-mfd + + nasal +