Skip to content

Commit

Permalink
add direct law PFD indications
Browse files Browse the repository at this point in the history
  • Loading branch information
flogross89 committed Jan 2, 2025
1 parent c4d27c0 commit 9062486
Show file tree
Hide file tree
Showing 8 changed files with 293 additions and 116 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { DisplayComponent, EventBus, FSComponent, Subject, Subscribable, VNode } from '@microsoft/msfs-sdk';
import { getDisplayIndex } from 'instruments/src/PFD/PFD';
import { Arinc429Word } from '@flybywiresim/fbw-sdk';
import { Arinc429ConsumerSubject, Arinc429Word } from '@flybywiresim/fbw-sdk';
import { FlightPathDirector } from './FlightPathDirector';
import { FlightPathVector } from './FlightPathVector';
import { Arinc429Values } from './shared/ArincValueProvider';
Expand All @@ -11,18 +11,22 @@ interface AttitudeIndicatorFixedUpperProps {
}

export class AttitudeIndicatorFixedUpper extends DisplayComponent<AttitudeIndicatorFixedUpperProps> {
private readonly sub = this.props.bus.getSubscriber<Arinc429Values>();

private roll = new Arinc429Word(0);

private pitch = new Arinc429Word(0);

private visibilitySub = Subject.create('hidden');

private readonly fcdcDiscreteWord1 = Arinc429ConsumerSubject.create(this.sub.on('fcdcDiscreteWord1'));

private readonly isNormalLawActive = this.fcdcDiscreteWord1.map((dw) => dw.bitValue(11) && !dw.isFailureWarning());

onAfterRender(node: VNode): void {
super.onAfterRender(node);

const sub = this.props.bus.getSubscriber<Arinc429Values>();

sub.on('rollAr').handle((roll) => {
this.sub.on('rollAr').handle((roll) => {
this.roll = roll;
if (!this.roll.isNormalOperation()) {
this.visibilitySub.set('hidden');
Expand All @@ -31,7 +35,7 @@ export class AttitudeIndicatorFixedUpper extends DisplayComponent<AttitudeIndica
}
});

sub.on('pitchAr').handle((pitch) => {
this.sub.on('pitchAr').handle((pitch) => {
this.pitch = pitch;
if (!this.pitch.isNormalOperation()) {
this.visibilitySub.set('hidden');
Expand All @@ -44,11 +48,19 @@ export class AttitudeIndicatorFixedUpper extends DisplayComponent<AttitudeIndica
render(): VNode {
return (
<g id="AttitudeUpperInfoGroup" visibility={this.visibilitySub}>
<g id="RollProtGroup" class="SmallStroke Green">
<g
id="RollProtGroup"
class="SmallStroke Green"
style={{ display: this.isNormalLawActive.map((nl) => (nl ? 'block' : 'none')) }}
>
<path id="RollProtRight" d="m105.64 62.887 1.5716-0.8008m-1.5716-0.78293 1.5716-0.8008" />
<path id="RollProtLeft" d="m32.064 61.303-1.5716-0.8008m1.5716 2.3845-1.5716-0.8008" />
</g>
<g id="RollProtLost" style="display: none" class="NormalStroke Amber">
<g
id="RollProtLost"
class="NormalStroke Amber"
style={{ display: this.isNormalLawActive.map((nl) => (!nl ? 'block' : 'none')) }}
>
<path id="RollProtLostRight" d="m107.77 60.696-1.7808 1.7818m1.7808 0-1.7808-1.7818" />
<path id="RollProtLostLeft" d="m30.043 62.478 1.7808-1.7818m-1.7808 0 1.7808 1.7818" />
</g>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
VNode,
} from '@microsoft/msfs-sdk';

import { Arinc429Register, Arinc429Word, ArincEventBus } from '@flybywiresim/fbw-sdk';
import { Arinc429ConsumerSubject, Arinc429Register, Arinc429Word, ArincEventBus } from '@flybywiresim/fbw-sdk';
import {
calculateHorizonOffsetFromPitch,
calculateVerticalOffsetFromRoll,
Expand Down Expand Up @@ -109,18 +109,22 @@ interface HorizonProps {
}

export class Horizon extends DisplayComponent<HorizonProps> {
private readonly sub = this.props.bus.getArincSubscriber<Arinc429Values>();

private pitchGroupRef = FSComponent.createRef<SVGGElement>();

private rollGroupRef = FSComponent.createRef<SVGGElement>();

private yOffset = Subject.create(0);

private readonly fcdcDiscreteWord1 = Arinc429ConsumerSubject.create(this.sub.on('fcdcDiscreteWord1'));

private readonly isNormalLawActive = this.fcdcDiscreteWord1.map((dw) => dw.bitValue(11) && !dw.isFailureWarning());

onAfterRender(node: VNode): void {
super.onAfterRender(node);

const apfd = this.props.bus.getArincSubscriber<Arinc429Values>();

apfd
this.sub
.on('pitchAr')
.withArinc429Precision(3)
.handle((pitch) => {
Expand All @@ -137,7 +141,7 @@ export class Horizon extends DisplayComponent<HorizonProps> {
this.yOffset.set(yOffset);
});

apfd
this.sub
.on('rollAr')
.withArinc429Precision(2)
.handle((roll) => {
Expand Down Expand Up @@ -189,19 +193,35 @@ export class Horizon extends DisplayComponent<HorizonProps> {
<path d="m47.906-19.177h42h0" />
</g>

<g id="PitchProtUpper" class="NormalStroke Green">
<g
id="PitchProtUpper"
class="NormalStroke Green"
style={{ display: this.isNormalLawActive.map((nl) => (nl ? 'inherit' : 'none')) }}
>
<path d="m51.506 31.523h4m-4-1.4h4" />
<path d="m86.306 31.523h-4m4-1.4h-4" />
</g>
<g id="PitchProtLostUpper" style="display: none" class="NormalStroke Amber">
<g
id="PitchProtLostUpper"
class="NormalStroke Amber"
style={{ display: this.isNormalLawActive.map((nl) => (!nl ? 'inherit' : 'none')) }}
>
<path d="m52.699 30.116 1.4142 1.4142m-1.4142 0 1.4142-1.4142" />
<path d="m85.114 31.53-1.4142-1.4142m1.4142 0-1.4142 1.4142" />
</g>
<g id="PitchProtLower" class="NormalStroke Green">
<g
id="PitchProtLower"
class="NormalStroke Green"
style={{ display: this.isNormalLawActive.map((nl) => (nl ? 'inherit' : 'none')) }}
>
<path d="m59.946 104.52h4m-4-1.4h4" />
<path d="m77.867 104.52h-4m4-1.4h-4" />
</g>
<g id="PitchProtLostLower" style="display: none" class="NormalStroke Amber">
<g
id="PitchProtLostLower"
class="NormalStroke Amber"
style={{ display: this.isNormalLawActive.map((nl) => (!nl ? 'inherit' : 'none')) }}
>
<path d="m61.199 103.12 1.4142 1.4142m-1.4142 0 1.4142-1.4142" />
<path d="m76.614 104.53-1.4142-1.4142m1.4142 0-1.4142 1.4142" />
</g>
Expand Down
20 changes: 18 additions & 2 deletions fbw-a380x/src/systems/instruments/src/PFD/FMA.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
import { ArmedLateralMode, isArmed, LateralMode, VerticalMode } from '@shared/autopilot';
import { Arinc429Values } from './shared/ArincValueProvider';
import { PFDSimvars } from './shared/PFDSimvarPublisher';
import { Arinc429Word } from '@flybywiresim/fbw-sdk';
import { Arinc429ConsumerSubject, Arinc429Word, Arinc429WordData } from '@flybywiresim/fbw-sdk';

abstract class ShowForSecondsComponent<T extends ComponentProps> extends DisplayComponent<T> {
private timeout: number = 0;
Expand Down Expand Up @@ -89,6 +89,10 @@ export class FMA extends DisplayComponent<{ bus: EventBus; isAttExcessive: Subsc

private readonly approachCapability = ConsumerSubject.create(this.sub.on('approachCapability'), 0);

private readonly fcdcDiscreteWord1 = Arinc429ConsumerSubject.create(this.sub.on('fcdcDiscreteWord1'));

private readonly fwcFlightPhase = ConsumerSubject.create(this.sub.on('fwcFlightPhase'), 0);

private disconnectApForLdg = MappedSubject.create(
([ap1, ap2, ra, altitude, landingElevation, verticalMode, selectedFpa, selectedVs, approachCapability]) => {
return (
Expand Down Expand Up @@ -126,6 +130,8 @@ export class FMA extends DisplayComponent<{ bus: EventBus; isAttExcessive: Subsc
this.props.isAttExcessive.get(),
this.armedVerticalModeSub.get(),
this.setHoldSpeed,
this.fcdcDiscreteWord1.get(),
this.fwcFlightPhase.get(),
this.trkFpaDeselected.get(),
this.tcasRaInhibited.get(),
this.tdReached,
Expand Down Expand Up @@ -1347,19 +1353,23 @@ const getBC3Message = (
isAttExcessive: boolean,
armedVerticalMode: number,
setHoldSpeed: boolean,
fcdcWord1: Arinc429WordData,
fwcFlightPhase: number,
trkFpaDeselectedTCAS: boolean,
tcasRaInhibited: boolean,
tdReached: boolean,
disconnectApForLdg: boolean,
) => {
const flightPhaseForWarning =
fwcFlightPhase >= 2 && fwcFlightPhase <= 11 && !(fwcFlightPhase >= 4 && fwcFlightPhase <= 7);
const armedVerticalBitmask = armedVerticalMode;
const TCASArmed = (armedVerticalBitmask >> 6) & 1;

let text: string;
let className: string;

// All currently unused message are set to false
if (false) {
if (fcdcWord1.bitValue(15) && !fcdcWord1.isFailureWarning() && flightPhaseForWarning) {
text = 'USE MAN PITCH TRIM';
className = 'PulseAmber9Seconds Amber';
} else if (false) {
Expand Down Expand Up @@ -1425,11 +1435,17 @@ class BC3Cell extends DisplayComponent<{

private tdReached = false;

private readonly fcdcDiscreteWord1 = Arinc429ConsumerSubject.create(this.sub.on('fcdcDiscreteWord1'));

private readonly fwcFlightPhase = ConsumerSubject.create(this.sub.on('fwcFlightPhase'), 0);

private fillBC3Cell() {
const [text, className] = getBC3Message(
this.isAttExcessive,
this.armedVerticalMode,
this.setHoldSpeed,
this.fcdcDiscreteWord1.get(),
this.fwcFlightPhase.get(),
this.trkFpaDeselected,
this.tcasRaInhibited,
this.tdReached,
Expand Down
76 changes: 34 additions & 42 deletions fbw-a380x/src/systems/instruments/src/PFD/SpeedIndicator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -214,8 +214,7 @@ class VAlphaProtBar extends DisplayComponent<{ bus: ArincEventBus }> {
}

class FlapsSpeedPointBugs extends DisplayComponent<{ bus: ArincEventBus }> {

private readonly sub = this.props.bus.getArincSubscriber<PFDSimvars & Arinc429Values>()
private readonly sub = this.props.bus.getArincSubscriber<PFDSimvars & Arinc429Values>();

private greenDotBug = FSComponent.createRef<SVGGElement>();

Expand All @@ -231,69 +230,63 @@ class FlapsSpeedPointBugs extends DisplayComponent<{ bus: ArincEventBus }> {
private readonly leftMainGearCompressedConsumer = ConsumerSubject.create(
this.sub.on('leftMainGearCompressed').whenChanged(),
true,
)
);

private readonly rightMainGearCompressedConsumer = ConsumerSubject.create(
this.sub.on('rightMainGearCompressed').whenChanged(),
true,
)
);

private readonly airspeedRaw = ConsumerSubject.create(this.sub.on('speed').whenChanged(), null);

private readonly airspeed = Arinc429RegisterSubject.createEmpty();

private readonly shortTermManagedSpeedExists =
MappedSubject.create(
([shortTermManagedSpeed, leftGearCompressed, rightGearCompressed, ]) => shortTermManagedSpeed > 0 && (!leftGearCompressed || !rightGearCompressed),
private readonly shortTermManagedSpeedExists = MappedSubject.create(
([shortTermManagedSpeed, leftGearCompressed, rightGearCompressed]) =>
shortTermManagedSpeed > 0 && (!leftGearCompressed || !rightGearCompressed),
this.shortTermManagedSpeedConsumer,
this.leftMainGearCompressedConsumer,
this.rightMainGearCompressedConsumer
)
this.rightMainGearCompressedConsumer,
);

private readonly shortTermVisibility = this.shortTermManagedSpeedExists.map((v) => (v ? 'visible' : 'hidden'));

private readonly shortTermPath = MappedSubject.create(
([ias, shortTermSpeed]) => {
if(ias.isNormalOperation() && shortTermSpeed) {
const diff = Math.abs(ias.value - shortTermSpeed);
if(diff < DisplayRange ) {
return 'm20.29 80.85a1.2592 1.2599 0 1 0-2.5184 0 1.2592 1.2599 0 1 0 2.5184 0z'
} else if (ias.value > shortTermSpeed) {
return 'm 17.91,80.60c 4.07e-4,0.6238 0.5384,1.1293 1.2019,1.1293 0.6635,0 1.2015,-0.5055 1.2019,-1.1293h -1.2019z';
if (ias.isNormalOperation() && shortTermSpeed) {
const diff = Math.abs(ias.value - shortTermSpeed);
if (diff < DisplayRange) {
return 'm20.29 80.85a1.2592 1.2599 0 1 0-2.5184 0 1.2592 1.2599 0 1 0 2.5184 0z';
} else if (ias.value > shortTermSpeed) {
return 'm 17.91,80.60c 4.07e-4,0.6238 0.5384,1.1293 1.2019,1.1293 0.6635,0 1.2015,-0.5055 1.2019,-1.1293h -1.2019z';
} else {
return 'm 17.91,80.60c 4.07e-4,0.6743 0.5612,1.2207 1.2530,1.2207 0.6917,0 1.2525,-0.5464 1.2530,-1.2207h -1.2530z';
}
} else {
return 'm 17.91,80.60c 4.07e-4,0.6743 0.5612,1.2207 1.2530,1.2207 0.6917,0 1.2525,-0.5464 1.2530,-1.2207h -1.2530z';
}
} else {
return '';
}
return '';
}
},
this.airspeed,
this.shortTermManagedSpeedConsumer
)

this.shortTermManagedSpeedConsumer,
);

private readonly shortTermStyle =
MappedSubject.create(
private readonly shortTermStyle = MappedSubject.create(
([shortTermVisible, ias, shortTermManagedSpeed]) => {
if(shortTermVisible && ias.isNormalOperation()) {
return `transform: translate(0px, ${getSpeedTapeOffsetAlwaysVisible(ias.value,
shortTermManagedSpeed)}px)`
if (shortTermVisible && ias.isNormalOperation()) {
return `transform: translate(0px, ${getSpeedTapeOffsetAlwaysVisible(ias.value, shortTermManagedSpeed)}px)`;
}
return '';
},
this.shortTermManagedSpeedExists,
this.airspeed,
this.shortTermManagedSpeedConsumer
)
this.shortTermManagedSpeedConsumer,
);

render(): VNode {
return (
<>
<g id="ShortTermManagedSpeed" visibility={this.shortTermVisibility} style={this.shortTermStyle}>
<path
class="Fill Magenta"
d={this.shortTermPath}
/>
<g id="ShortTermManagedSpeed" visibility={this.shortTermVisibility} style={this.shortTermStyle}>
<path class="Fill Magenta" d={this.shortTermPath} />
</g>
<g id="GreenDotSpeedMarker" ref={this.greenDotBug} style="transform:translate3d(0px, 0px,0px)">
<path class="ThickOutline" d="m20.29 80.85a1.2592 1.2599 0 1 0-2.5184 0 1.2592 1.2599 0 1 0 2.5184 0z" />
Expand All @@ -311,15 +304,13 @@ class FlapsSpeedPointBugs extends DisplayComponent<{ bus: ArincEventBus }> {
S
</text>
</g>

</>
);
}

onAfterRender(node: VNode): void {
super.onAfterRender(node);


this.airspeedRaw.sub((w) => this.airspeed.setWord(w));

this.sub
Expand Down Expand Up @@ -359,14 +350,14 @@ class FlapsSpeedPointBugs extends DisplayComponent<{ bus: ArincEventBus }> {
}

const getSpeedTapeOffset = (speed: number): number => (-speed * DistanceSpacing) / ValueSpacing;
const getSpeedTapeOffsetAlwaysVisible = (currentSpeed : number, bugSpeed : number) => {
const getSpeedTapeOffsetAlwaysVisible = (currentSpeed: number, bugSpeed: number) => {
const diff = Math.abs(currentSpeed - bugSpeed);
if(diff < DisplayRange) {
if (diff < DisplayRange) {
return getSpeedTapeOffset(bugSpeed);
} else {
return getSpeedTapeOffset(currentSpeed > bugSpeed? currentSpeed - DisplayRange : currentSpeed + DisplayRange); // speed always visible on tape
return getSpeedTapeOffset(currentSpeed > bugSpeed ? currentSpeed - DisplayRange : currentSpeed + DisplayRange); // speed always visible on tape
}
}
};

export class AirspeedIndicatorOfftape extends DisplayComponent<{ bus: ArincEventBus }> {
private lowerRef = FSComponent.createRef<SVGGElement>();
Expand Down Expand Up @@ -1490,10 +1481,11 @@ class VProtBug extends DisplayComponent<{ bus: EventBus }> {
.whenChanged()
.handle((vm) => {
this.Vmax = vm;
// console.log(vm);

this.handleVProtBugDisplay();
});

sub
.on('fcdcDiscreteWord1')
.whenChanged()
Expand Down
Loading

0 comments on commit 9062486

Please sign in to comment.