Skip to content

Commit cc6b696

Browse files
authored
feat(a380x/fms): selected navaids (flybywiresim#8954)
* feat(utils): add lat, lon overload * fix(utils): long lat/lon string format * chore: add navaid to navigation provider interface * feat(a380x/fms): selected navaids * fix: derp
1 parent 7a01d70 commit cc6b696

File tree

4 files changed

+164
-53
lines changed

4 files changed

+164
-53
lines changed

fbw-a32nx/src/systems/fmgc/src/navigation/NavigationProvider.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) 2023 FlyByWire Simulations
22
// SPDX-License-Identifier: GPL-3.0
33

4+
import { SelectedNavaid } from '@fmgc/navigation/Navigation';
45
import { Coordinates } from 'msfs-geo';
56

67
/**
@@ -37,4 +38,10 @@ export interface NavigationProvider {
3738
* @returns radio altimeter height in feet or null if invalid
3839
*/
3940
getRadioHeight(): number | null;
41+
42+
/**
43+
* Get the navaids selected by the FMS.
44+
* @param cdu The CDU to get navaids for, defaults to 1.
45+
*/
46+
getSelectedNavaids(cdu?: 1 | 2): SelectedNavaid[];
4047
}

fbw-a380x/src/systems/instruments/src/MFD/pages/FMS/POSITION/MfdFmsPositionNavaids.tsx

Lines changed: 132 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,46 @@
1-
import { ClockEvents, FSComponent, Subject, VNode } from '@microsoft/msfs-sdk';
2-
3-
import './MfdFmsPositionNavaids.scss';
4-
import { NavaidSubsectionCode } from '@flybywiresim/fbw-sdk';
51
import { AbstractMfdPageProps } from 'instruments/src/MFD/MFD';
6-
import { Footer } from 'instruments/src/MFD/pages/common/Footer';
7-
82
import { Button } from 'instruments/src/MFD/pages/common/Button';
9-
import { FmsPage } from 'instruments/src/MFD/pages/common/FmsPage';
10-
import { MfdSimvars } from 'instruments/src/MFD/shared/MFDSimvarPublisher';
11-
import { InputField } from 'instruments/src/MFD/pages/common/InputField';
123
import {
134
FrequencyILSFormat,
145
FrequencyVORDMEFormat,
156
InboundCourseFormat,
167
LsCourseFormat,
178
NavaidIdentFormat,
189
} from 'instruments/src/MFD/pages/common/DataEntryFormats';
10+
import { FmsPage } from 'instruments/src/MFD/pages/common/FmsPage';
11+
import { Footer } from 'instruments/src/MFD/pages/common/Footer';
12+
import { InputField } from 'instruments/src/MFD/pages/common/InputField';
1913
import { TopTabNavigator, TopTabNavigatorPage } from 'instruments/src/MFD/pages/common/TopTabNavigator';
20-
import { NavigationDatabaseService } from '@fmgc/index';
14+
import { MfdSimvars } from 'instruments/src/MFD/shared/MFDSimvarPublisher';
2115
import { NXSystemMessages } from 'instruments/src/MFD/shared/NXSystemMessages';
16+
17+
import { coordinateToString, NavaidSubsectionCode } from '@flybywiresim/fbw-sdk';
18+
import { NavigationDatabaseService, SelectedNavaidType } from '@fmgc/index';
2219
import { NavRadioTuningStatus } from '@fmgc/navigation/NavaidTuner';
20+
import { ClockEvents, FSComponent, SimVarValueType, Subject, VNode } from '@microsoft/msfs-sdk';
21+
22+
import './MfdFmsPositionNavaids.scss';
2323

2424
interface MfdFmsPositionNavaidsProps extends AbstractMfdPageProps {}
2525

26+
interface SelectedNavaid {
27+
ident: Subject<string>;
28+
frequencyOrChannel: Subject<string>;
29+
class: Subject<string>;
30+
}
31+
32+
const NAVAID_TYPE_STRINGS: Record<SelectedNavaidType, string> = {
33+
[SelectedNavaidType.None]: '',
34+
[SelectedNavaidType.Dme]: 'DME',
35+
[SelectedNavaidType.Vor]: 'VOR',
36+
[SelectedNavaidType.VorDme]: 'VOR/DME',
37+
[SelectedNavaidType.VorTac]: 'VOR/TAC',
38+
[SelectedNavaidType.Tacan]: 'TACAN',
39+
[SelectedNavaidType.Ils]: 'ILS/DME',
40+
[SelectedNavaidType.Gls]: 'GLS',
41+
[SelectedNavaidType.Mls]: 'MLS',
42+
};
43+
2644
export class MfdFmsPositionNavaids extends FmsPage<MfdFmsPositionNavaidsProps> {
2745
private navaidsSelectedPageIndex = Subject.create<number>(0);
2846

@@ -66,25 +84,15 @@ export class MfdFmsPositionNavaids extends FmsPage<MfdFmsPositionNavaidsProps> {
6684

6785
private lsCourseEnteredByPilot = Subject.create<boolean>(false);
6886

69-
private firstRowIdent = Subject.create<string | null>(null);
70-
71-
private firstRowFrequency = Subject.create<string | null>(null);
72-
73-
private firstRowClass = Subject.create<string | null>(null);
74-
75-
private secondRowIdent = Subject.create<string | null>(null);
76-
77-
private secondRowFrequency = Subject.create<string | null>(null);
78-
79-
private secondRowClass = Subject.create<string | null>(null);
80-
81-
private thirdRowIdentRef = FSComponent.createRef<HTMLDivElement>();
82-
83-
private thirdRowIdent = Subject.create<string | null>(null);
87+
private readonly selectedNavaids: SelectedNavaid[] = Array.from({ length: 3 }, () => ({
88+
ident: Subject.create(''),
89+
frequencyOrChannel: Subject.create(''),
90+
class: Subject.create(''),
91+
}));
8492

85-
private thirdRowFrequency = Subject.create<string | null>(null);
93+
private readonly radioNavMode = Subject.create('');
8694

87-
private thirdRowClass = Subject.create<string | null>(null);
95+
private readonly radioNavPosition = Subject.create('');
8896

8997
private deselectedNavaids = [
9098
Subject.create<string | null>(null),
@@ -146,11 +154,51 @@ export class MfdFmsPositionNavaids extends FmsPage<MfdFmsPositionNavaidsProps> {
146154
}
147155
});
148156

149-
// Third line for selected navaids table: Display LS if set
150-
this.thirdRowFrequency.set(mmr.frequency ? mmr.frequency.toFixed(2) : '');
151-
this.thirdRowClass.set(mmr.ident ? 'ILS/DME' : '');
152-
this.thirdRowIdent.set(mmr.ident ?? null);
153-
this.thirdRowIdentRef.instance.style.visibility = mmr.ident ? 'visible' : 'hidden';
157+
const selectedNavaids = this.props.fmcService.master?.navigation.getSelectedNavaids();
158+
159+
if (selectedNavaids) {
160+
for (const [i, navaid] of selectedNavaids.entries()) {
161+
if (i === 0) {
162+
// display vor is not shown on this page on A380
163+
continue;
164+
}
165+
this.selectedNavaids[i - 1].ident.set(navaid.ident ?? '');
166+
this.selectedNavaids[i - 1].frequencyOrChannel.set(
167+
navaid.facility === null || navaid.frequency === null ? '' : navaid.frequency.toFixed(2),
168+
);
169+
this.selectedNavaids[i - 1].class.set(navaid.facility !== null ? NAVAID_TYPE_STRINGS[navaid.type] : '');
170+
}
171+
// fake it until we make it
172+
if (selectedNavaids[1].facility !== null && selectedNavaids[2].facility !== null) {
173+
this.radioNavMode.set('DME/DME');
174+
this.radioNavPosition.set(
175+
coordinateToString(
176+
SimVar.GetSimVarValue('PLANE LATITUDE', SimVarValueType.Degree),
177+
SimVar.GetSimVarValue('PLANE LONGITUDE', SimVarValueType.Degree),
178+
false,
179+
),
180+
);
181+
} else if (selectedNavaids[1].facility !== null) {
182+
this.radioNavMode.set('VOR/DME');
183+
this.radioNavPosition.set(
184+
coordinateToString(
185+
SimVar.GetSimVarValue('PLANE LATITUDE', SimVarValueType.Degree),
186+
SimVar.GetSimVarValue('PLANE LONGITUDE', SimVarValueType.Degree),
187+
false,
188+
),
189+
);
190+
} else {
191+
this.radioNavMode.set('');
192+
}
193+
} else {
194+
for (const sel of this.selectedNavaids) {
195+
sel.ident.set('');
196+
sel.frequencyOrChannel.set('');
197+
sel.class.set('');
198+
}
199+
this.radioNavMode.set('');
200+
this.radioNavPosition.set('');
201+
}
154202
}
155203

156204
private async parseNavaid(navaid: string, onlyVor = false) {
@@ -414,34 +462,71 @@ export class MfdFmsPositionNavaids extends FmsPage<MfdFmsPositionNavaidsProps> {
414462
<div class="mfd-label br bb">IDENT</div>
415463
<div class="mfd-label br bb">FREQ/CHAN</div>
416464
<div class="mfd-label bb">CLASS</div>
417-
<div class="mfd-label br">{this.firstRowIdent}</div>
418-
<div class="mfd-value br">{this.firstRowFrequency}</div>
419-
<div class="mfd-value">{this.firstRowClass}</div>
420-
<div class="mfd-label br">{this.secondRowIdent}</div>
421-
<div class="mfd-value br">{this.secondRowFrequency}</div>
422-
<div class="mfd-value">{this.secondRowClass}</div>
423465
<div class="mfd-label br">
424-
<div ref={this.thirdRowIdentRef}>
466+
<div class={{ invisible: this.selectedNavaids[0].ident.map((v) => v.length === 0) }}>
425467
<Button
426-
label={this.thirdRowIdent.map((it) => (
468+
label={this.selectedNavaids[0].ident.map((it) => (
427469
<>{it}</>
428470
))}
429471
onClick={() => {}}
430472
showArrow
431473
menuItems={Subject.create([{ label: 'DATA NAVAID', action: () => {} }])}
432474
idPrefix={`${this.props.mfd.uiService.captOrFo}_MFD_dataNavaid`}
433475
disabled={Subject.create(true)}
476+
buttonStyle="min-width: 107px;"
434477
/>
435478
</div>
436479
</div>
437-
<div class="mfd-value br">{this.thirdRowFrequency}</div>
438-
<div class="mfd-value">{this.thirdRowClass}</div>
439-
</div>
440-
<div class="mfd-label" style="padding-left: 30px; margin-bottom: 20px;">
441-
RADIO NAV MODE
480+
<div class="mfd-value br">{this.selectedNavaids[0].frequencyOrChannel}</div>
481+
<div class="mfd-value">{this.selectedNavaids[0].class}</div>
482+
<div class="mfd-label br">
483+
<div class={{ invisible: this.selectedNavaids[1].ident.map((v) => v.length === 0) }}>
484+
<Button
485+
label={this.selectedNavaids[1].ident.map((it) => (
486+
<>{it}</>
487+
))}
488+
onClick={() => {}}
489+
showArrow
490+
menuItems={Subject.create([{ label: 'DATA NAVAID', action: () => {} }])}
491+
idPrefix={`${this.props.mfd.uiService.captOrFo}_MFD_dataNavaid`}
492+
disabled={Subject.create(true)}
493+
buttonStyle="min-width: 107px;"
494+
/>
495+
</div>
496+
</div>
497+
<div class="mfd-value br">{this.selectedNavaids[1].frequencyOrChannel}</div>
498+
<div class="mfd-value">{this.selectedNavaids[1].class}</div>
499+
<div class="mfd-label br">
500+
<div class={{ invisible: this.selectedNavaids[2].ident.map((v) => v.length === 0) }}>
501+
<Button
502+
label={this.selectedNavaids[2].ident.map((it) => (
503+
<>{it}</>
504+
))}
505+
onClick={() => {}}
506+
showArrow
507+
menuItems={Subject.create([{ label: 'DATA NAVAID', action: () => {} }])}
508+
idPrefix={`${this.props.mfd.uiService.captOrFo}_MFD_dataNavaid`}
509+
disabled={Subject.create(true)}
510+
buttonStyle="min-width: 107px;"
511+
/>
512+
</div>
513+
</div>
514+
<div class="mfd-value br">{this.selectedNavaids[2].frequencyOrChannel}</div>
515+
<div class="mfd-value">{this.selectedNavaids[2].class}</div>
442516
</div>
443-
<div class="mfd-label" style="padding-left: 30px; margin-bottom: 10px;">
444-
RADIO POSITION
517+
<div style="display: grid; grid-template-columns: 190px 320px; margin-left: 80px;">
518+
<div class="mfd-label" style="justify-content: right; margin-bottom: 20px;">
519+
RADIO NAV MODE
520+
</div>
521+
<div class="mfd-value" style="justify-content: left; margin-left: 20px; margin-bottom: 20px;">
522+
{this.radioNavMode}
523+
</div>
524+
<div class="mfd-label" style="justify-content: right; margin-bottom: 20px;">
525+
RADIO POSITION
526+
</div>
527+
<div class="mfd-value" style="justify-content: left; margin-left: 20px; margin-bottom: 20px;">
528+
{this.radioNavPosition}
529+
</div>
445530
</div>
446531
<div style="border-bottom: 1px solid lightgrey; width: 100%; height: 3px; margin-bottom: 15px;" />
447532
<div class="mfd-label" style="padding-left: 15px; margin-bottom: 10px;">

fbw-a380x/src/systems/instruments/src/MFD/pages/common/style.scss

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -774,3 +774,11 @@
774774
text-align: center;
775775
margin-bottom: 2px;
776776
}
777+
778+
.hidden {
779+
display: none;
780+
}
781+
782+
.invisible {
783+
visibility: hidden;
784+
}

fbw-common/src/systems/instruments/src/Convert.ts

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,15 @@ function decimalToDms(deg: number, lng: boolean) {
5959
};
6060
}
6161

62-
export function coordinateToString(coordinate: Coordinates, shortVersion: boolean): string {
63-
const dmsLat = decimalToDms(coordinate.lat, false);
64-
const dmsLon = decimalToDms(coordinate.long, true);
62+
export function coordinateToString(coordinate: Coordinates, shortVersion: boolean): string;
63+
export function coordinateToString(lat: number, lon: number, shortVersion: boolean): string;
64+
export function coordinateToString(arg0: number | Coordinates, arg1: number | boolean, arg2?: boolean): string {
65+
const lat = typeof arg0 === 'object' ? arg0.lat : arg0;
66+
const lon = typeof arg0 === 'object' ? arg0.long : (arg1 as number);
67+
const shortVersion = typeof arg0 === 'object' ? !!arg1 : !!arg2;
68+
69+
const dmsLat = decimalToDms(lat, false);
70+
const dmsLon = decimalToDms(lon, true);
6571

6672
dmsLon.deg = Number(dmsLon.deg);
6773
dmsLat.sec = Math.ceil(Number(dmsLat.sec / 100));
@@ -80,7 +86,12 @@ export function coordinateToString(coordinate: Coordinates, shortVersion: boolea
8086
return `${dmsLat.deg}W${dmsLon.deg}`;
8187
}
8288

83-
const lat = `${dmsLat.deg}°${dmsLat.min}.${dmsLat.sec}${dmsLat.dir}`;
84-
const lon = `${dmsLon.deg}°${dmsLon.min}.${dmsLon.sec}${dmsLon.dir}`;
85-
return `${lat}/${lon}`;
89+
const latDegStr = dmsLat.deg.toString().padStart(2, '0');
90+
const lonDegStr = dmsLon.deg.toString().padStart(3, '0');
91+
const latMinStr = dmsLat.min.toString().padStart(2, '0');
92+
const lonMinStr = dmsLon.min.toString().padStart(2, '0');
93+
94+
const latStr = `${latDegStr}°${latMinStr}.${dmsLat.sec}${dmsLat.dir}`;
95+
const lonStr = `${lonDegStr}°${lonMinStr}.${dmsLon.sec}${dmsLon.dir}`;
96+
return `${latStr}/${lonStr}`;
8697
}

0 commit comments

Comments
 (0)