diff --git a/Aircraft/Instruments-3d/FG1000/Models/NearestIntersections.svg b/Aircraft/Instruments-3d/FG1000/Models/NearestIntersections.svg new file mode 100644 index 000000000..f2c705ca0 --- /dev/null +++ b/Aircraft/Instruments-3d/FG1000/Models/NearestIntersections.svg @@ -0,0 +1,790 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + NEAREST INT + KSFO + 359 + 200nm + + + + INFORMATION + + N 56 45.29 + + + KSFO + 359 + 200nm + + KSFO + 359 + 200nm + + KSFO + 359 + 200nm + + KSFO + 359 + 200nm + + KSFO + 359 + 200nm + + KSFO + 359 + 200nm + + KSFO + 359 + 200nm + + KSFO + 359 + 200nm + + KSFO + 359 + 200nm + + KSFO + 359 + 200nm + + + + REFERENCE VOR + VOR + E 56 45.78 + 116.650 + 123 + 23nm + + + diff --git a/Aircraft/Instruments-3d/FG1000/Nasal/Interfaces/NavDataInterface.nas b/Aircraft/Instruments-3d/FG1000/Nasal/Interfaces/NavDataInterface.nas index 721747a98..685fd5ad5 100644 --- a/Aircraft/Instruments-3d/FG1000/Nasal/Interfaces/NavDataInterface.nas +++ b/Aircraft/Instruments-3d/FG1000/Nasal/Interfaces/NavDataInterface.nas @@ -41,6 +41,26 @@ getNearestAirports : func() return apts; }, +# Find the nearest nav aids of a given type within 200nm, to a maximum of 25. +getNavDataWithinRange: func(type) +{ + # To make this more efficient for areas with a high density of fixes, we'll try + # a small radius first and expand until we have reached 200nm or have 25 nav aids. + var radius = 0; + var navdata = []; + + while ((radius <= 200) and (size(navdata) < 25)) { + radius = radius + 50; + navdata = findNavaidsWithinRange(radius, type); + } + + if (size(navdata) > 25) { + navdata = subvec(navdata, 0, 25); + } + + return navdata; +}, + # Find a specific airport by ID. Return an array of airport objects getAirportById : func(id) { @@ -209,6 +229,10 @@ RegisterWithEmesary : func() notification.EventParameter.Value = controller.getNavDataById(notification.EventParameter.Value); return emesary.Transmitter.ReceiptStatus_Finished; } + if (id == "NavDataWithinRange") { + notification.EventParameter.Value = controller.getNavDataWithinRange(notification.EventParameter.Value); + return emesary.Transmitter.ReceiptStatus_Finished; + } if (id == "Flightplan") { notification.EventParameter.Value = controller.getFlightplan(); return emesary.Transmitter.ReceiptStatus_Finished; diff --git a/Aircraft/Instruments-3d/FG1000/Nasal/NavigationMap/NavigationMapStyles.nas b/Aircraft/Instruments-3d/FG1000/Nasal/NavigationMap/NavigationMapStyles.nas index 2c72fa045..280db8d97 100644 --- a/Aircraft/Instruments-3d/FG1000/Nasal/NavigationMap/NavigationMapStyles.nas +++ b/Aircraft/Instruments-3d/FG1000/Nasal/NavigationMap/NavigationMapStyles.nas @@ -47,9 +47,17 @@ var NavigationMapStyles = me.Styles.FLT.line_width = 3; me.Styles.FIX = {}; - me.Styles.FIX.color = [1,0,0]; + me.Styles.FIX.color = [0,0,0]; # White outline + me.Styles.FIX.fill_color = [1,1,1,1]; # Black fill me.Styles.FIX.scale_factor = 0.4; # 40% + me.Styles.FIX.text_offset = [0, -12]; + me.Styles.FIX.text_color = [0,0,0,1]; # Black text ... + me.Styles.FIX.text_bgcolor = [1,1,1,1]; # ... on a white background + me.Styles.FIX.text_mode = canvas.Text.TEXT + canvas.Text.FILLEDBOUNDINGBOX; + me.Styles.FIX.text_padding = 2; + me.Styles.FIX.text_alignment = 'center-bottom'; + me.Styles.VOR = {}; me.Styles.VOR.range_line_width = 2; me.Styles.VOR.radial_line_width = 1; diff --git a/Aircraft/Instruments-3d/FG1000/Nasal/NearestAirports/NearestAirports.nas b/Aircraft/Instruments-3d/FG1000/Nasal/NearestAirports/NearestAirports.nas index 20c32ed83..dc4784635 100644 --- a/Aircraft/Instruments-3d/FG1000/Nasal/NearestAirports/NearestAirports.nas +++ b/Aircraft/Instruments-3d/FG1000/Nasal/NearestAirports/NearestAirports.nas @@ -126,7 +126,7 @@ var NearestAirports = var apt = apts[i]; var crsAndDst = courseAndDistance(apt); - # Display the cours and distance in NM . + # Display the course and distance in NM . # 248 is the extended ASCII code for the degree symbol var crs = sprintf("%i%c", crsAndDst[0], 248); var dst = sprintf("%.1fnm", crsAndDst[1]); diff --git a/Aircraft/Instruments-3d/FG1000/Nasal/NearestIntersections/NearestIntersections.nas b/Aircraft/Instruments-3d/FG1000/Nasal/NearestIntersections/NearestIntersections.nas index 048a23e73..7d390cece 100644 --- a/Aircraft/Instruments-3d/FG1000/Nasal/NearestIntersections/NearestIntersections.nas +++ b/Aircraft/Instruments-3d/FG1000/Nasal/NearestIntersections/NearestIntersections.nas @@ -10,34 +10,143 @@ var NearestIntersections = ], }; - obj.topMenu(device, obj, nil); - obj.setController(fg1000.NearestIntersectionsController.new(obj, svg)); + # Dynamic elements. There is a single dynamic element containing the list of + # the 25 nearest intersections. + obj.select = PFD.GroupElement.new( + obj.pageName, + svg, + [ "Arrow", "ID", "CRS", "DST"], + 11, + "Arrow", + 1, + "ScrollTrough", + "ScrollThumb", + 250 - 116 + ); + + # Other dynamic text elements + obj.addTextElements(["Lat", "Lon", "VORID", "VORFreq", "VORCRS", "VORDST"]); + + obj.topMenu(device, obj, nil); + return obj; }, + + # Indicate which group is selected by colour of the softkeys + display_toggle : func(device, svg, mi, group) { + var bg_name = sprintf("SoftKey%d-bg",mi.menu_id); + if (me.getController().getSelectedGroup() == group) { + 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 + }, + offdisplay : func() { - me._group.setVisible(0); + # The Nearest... pages use the underlying navigation map. + me.mfd.NavigationMap.offdisplayPartial(); # 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.resetMenuColors(); + me.getController().offdisplay(); }, ondisplay : func() { - me._group.setVisible(1); - me.mfd.setPageTitle(me.title); me.getController().ondisplay(); + + # The Nearest... pages use the underlying navigation map. + me.mfd.NavigationMap.ondisplayPartial(); + + me.mfd.setPageTitle(me.title); }, + updateNavData : func(navdata) { + + if ((navdata == nil) or (size(navdata) == 0)) return; + + var navDataList = []; + for (var i = 0; i < size(navdata); i = i + 1) { + var nav = navdata[i]; + var crsAndDst = courseAndDistance(nav); + + # Display the course and distance in NM . + # 248 is the extended ASCII code for the degree symbol + var crs = sprintf("%i%c", crsAndDst[0], 248); + var dst = sprintf("%.1fnm", crsAndDst[1]); + + # Convert into something we can pass straight to the UIGroup. + append(navDataList, { + Arrow : nav.id, + ID: nav.id, + CRS: crs, + DST: dst, + }); + } + + me.select.setValues(navDataList); + + if (size(navDataList) > 0) { + me.updateNavDataItem(navdata[0]); + me.select.showCRSR(); + } else { + me.select.hideCRSR(); + me.setTextElement("Lat", "NONE WITHIN 200NM"); + me.setTextElement("VORID", ""); + me.setTextElement("VORFreq", ""); + me.setTextElement("VORCRS", ""); + me.setTextElement("VORDST", ""); + } + }, + updateNavDataItem : func(nav) { + + if (nav == nil) return; + + if (nav.lat < 0.0) { + me.setTextElement("Lat", sprintf("S %.4f", -nav.lat)); + } else { + me.setTextElement("Lat", sprintf("N %.4f", nav.lat)); + } + + if (nav.lon < 0.0) { + me.setTextElement("Lon", sprintf("W%3.4f", -nav.lon)); + } else { + me.setTextElement("Lon", sprintf("E%3.4f", nav.lon)); + } + + # Determine the nearest VOR, and the bearing and distance TO the VOR, + var vordata = me.getController().getNearestNavData("vor"); + + if ((vordata != nil ) and (size(vordata) > 0)) { + var crsAndDst = courseAndDistance(nav, vordata[0]); + var crs = sprintf("%i%c", crsAndDst[0], 248); + var dst = sprintf("%.1fnm", crsAndDst[1]); + me.setTextElement("VORID", vordata[0].id); + me.setTextElement("VORFreq", sprintf("%.2f", vordata[0].frequency / 100.0)); + me.setTextElement("VORCRS", crs); + me.setTextElement("VORDST", dst); + } else { + me.setTextElement("VORID", ""); + me.setTextElement("VORFreq", ""); + me.setTextElement("VORCRS", ""); + me.setTextElement("VORDST", ""); + } + + # Display the DTO line to the airport + me.mfd.NavigationMap.getController().setDTOLineTarget(nav.lat, nav.lon); + }, + topMenu : func(device, pg, menuitem) { pg.clearMenu(); pg.resetMenuColors(); + pg.addMenuItem(0, "ENGINE", pg, pg.mfd.EIS.engineMenu); + pg.addMenuItem(2, "MAP", pg, pg.mfd.NavigationMap.mapMenu); + device.updateMenus(); }, - - }; diff --git a/Aircraft/Instruments-3d/FG1000/Nasal/NearestIntersections/NearestIntersectionsController.nas b/Aircraft/Instruments-3d/FG1000/Nasal/NearestIntersections/NearestIntersectionsController.nas index 8d73e13bf..34f7138de 100644 --- a/Aircraft/Instruments-3d/FG1000/Nasal/NearestIntersections/NearestIntersectionsController.nas +++ b/Aircraft/Instruments-3d/FG1000/Nasal/NearestIntersections/NearestIntersectionsController.nas @@ -1,59 +1,140 @@ # NearestIntersections Controller var NearestIntersectionsController = { + UIGROUP : { + NONE : 0, # No group currently selected, + APT : 1, + RNWY : 2, + FREQ : 3, + APR : 4, + }, + new : func (page, svg) { - var obj = { - parents : [ NearestIntersectionsController, MFDPageController.new(page) ], - _crsrToggle : 0, - _recipient : nil, - _page : page, - }; + var obj = { parents : [ NearestIntersectionsController, MFDPageController.new(page) ] }; + + # Current active UI group. + obj.page = page; + obj._crsrToggle = 0; return obj; }, + selectAirports : func() { + me.selectGroup(NearestAirportsController.UIGROUP.APT) + }, + selectRunways : func() { + me.selectGroup(NearestAirportsController.UIGROUP.RNWY); + }, + selectFrequencies : func() { + me.selectGroup(NearestAirportsController.UIGROUP.FREQ); + }, + selectApproaches : func() { + me.selectGroup(NearestAirportsController.UIGROUP.APR); + }, + getSelectedGroup : func() { + return me._currentGroup; + }, + selectGroup : func(grp) { + me._currentGroup = grp; + # The current Airport is always highlighted - we're either changing it directly, + # or viewing the selected airport. + if (grp == NearestAirportsController.UIGROUP.RNWY) me.page.runwaySelect.highlightElement() else me.page.runwaySelect.unhighlightElement(); + if (grp == NearestAirportsController.UIGROUP.FREQ) me.page.freqSelect.showCRSR() else me.page.freqSelect.hideCRSR(); + if (grp == NearestAirportsController.UIGROUP.APR) me.page.approachSelect.showCRSR() else me.page.approachSelect.hideCRSR(); + me._crsrToggle = 1; + }, # 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 + me.page.select.incrSmall(value); + var id = me.page.select.getValue(); + var data = me.getNavDataItem(id); + if ((data != nil) and (size(data) >0)) me.page.updateNavDataItem(data[0]); 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); + return me.page.mfd.SurroundController.handleFMSInner(value); } }, handleFMSOuter : func(value) { if (me._crsrToggle == 1) { + # Scroll through whatever is the current list + me.page.select.incrSmall(value); + var id = me.page.select.getValue(); + var data = me.getNavDataItem(id); + if ((data != nil) and (size(data) >0)) me.page.updateNavDataItem(data[0]); 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); + return me.page.mfd.SurroundController.handleFMSOuter(value); } }, handleEnter : func(value) { if (me._crsrToggle == 1) { + me.page.select.incrSmall(value); + var id = me.page.select.getValue(); + var data = me.getNavDataItem(id); + if ((data != nil) and (size(data) >0)) me.page.updateNavDataItem(data[0]); return emesary.Transmitter.ReceiptStatus_Finished; } else { return emesary.Transmitter.ReceiptStatus_NotProcessed; } }, + handleRange : func(val) + { + # Pass any range entries to the NavMapController + me.page.mfd.NavigationMap.getController().handleRange(val); + }, + + # Reset controller if required when the page is displayed or hidden ondisplay : func() { me.RegisterWithEmesary(); + var fixes = me.getNearestNavData("fix"); + me.page.updateNavData(fixes); + me.page.mfd.NavigationMap.getController().enableDTO(1); }, offdisplay : func() { + me.page.mfd.NavigationMap.getController().enableDTO(0); me.DeRegisterWithEmesary(); }, + getNearestNavData : func(type) { + var notification = notifications.PFDEventNotification.new( + "MFD", + 1, + notifications.PFDEventNotification.NavData, + {Id: "NavDataWithinRange", Value: type}); + + var response = me._transmitter.NotifyAll(notification); + + if (! me._transmitter.IsFailed(response)) { + return notification.EventParameter.Value; + } else { + return nil; + } + }, + + getNavDataItem : func(id) { + # Use Emesary to get the airport + var notification = notifications.PFDEventNotification.new( + "MFD", + 1, + notifications.PFDEventNotification.NavData, + {Id: "NavDataByID", Value: id}); + + var response = me._transmitter.NotifyAll(notification); + + if (! me._transmitter.IsFailed(response)) { + return notification.EventParameter.Value; + } else { + return nil; + } + }, };