feat(learn-pfd): add pitch-based speed hold step and target speed zones

This commit is contained in:
itsrubberduck
2026-02-21 00:08:16 +01:00
parent 7cfe21c490
commit 06f277e3ee
7 changed files with 116 additions and 23 deletions

View File

@@ -38,7 +38,8 @@ const DRAG_COEFFICIENT = 0.03
const MASS = 150000
const GRAVITY = 32.174
const KT_TO_FPS = 1.68781
const SPEED_PITCH_COUPLING = 0.5
const TRIM_PITCH_DEG = 2
const SPEED_PITCH_COUPLING = 1.2
const INITIAL_SPEED = 220
const INITIAL_ALTITUDE = 5000
@@ -131,9 +132,9 @@ export function useAirbusFBW() {
// --- Speed ---
const speedFps2 = state.speed * KT_TO_FPS
const pitchRad = state.pitch * Math.PI / 180
const trimmedPitchRad = (state.pitch - TRIM_PITCH_DEG) * Math.PI / 180
const drag = DRAG_COEFFICIENT * speedFps2 * speedFps2
const climbPenalty = Math.sin(pitchRad) * MASS * GRAVITY * SPEED_PITCH_COUPLING
const climbPenalty = Math.sin(trimmedPitchRad) * MASS * GRAVITY * SPEED_PITCH_COUPLING
const netForce = thrust - drag - climbPenalty
const acceleration = netForce / MASS
const speedDelta = (acceleration / KT_TO_FPS) * dt

View File

@@ -3,14 +3,15 @@ import { ref, computed, watch } from 'vue'
import type { LearnPfdPhase, LearnPfdScenario, FlightLabButton, PfdInteractionGoal, PfdElement, PfdLayoutMode } from '../../data/flightlab/types'
import type { FlightState } from './useAirbusFBW'
const mainPhaseIds = ['welcome', 'horizon_intro', 'pitch_intro', 'speed_intro', 'alt_intro', 'vs_intro', 'heading_intro', 'combined', 'free_practice', 'end']
const mainPhaseIds = ['welcome', 'horizon_intro', 'pitch_intro', 'speed_intro', 'speed_hold_pitch', 'alt_intro', 'vs_intro', 'heading_intro', 'combined', 'free_practice', 'end']
const goalNextPhase: Record<string, string> = {
'horizon_intro': 'horizon_roll_right',
'horizon_roll_right': 'pitch_intro',
'pitch_intro': 'pitch_down',
'pitch_down': 'speed_intro',
'speed_intro': 'alt_intro',
'speed_intro': 'speed_hold_pitch',
'speed_hold_pitch': 'alt_intro',
'alt_intro': 'vs_intro',
'vs_intro': 'heading_intro',
'heading_intro': 'combined',

View File

@@ -75,19 +75,35 @@ const phases: LearnPfdPhase[] = [
// --- Phase 6: Speed Intro ---
{
id: 'speed_intro',
atcMessage: 'Links erscheint jetzt das Speed Tape. Das zeigt dir wie schnell du fliegst, in Knoten. Schieb den Schubhebel auf etwa siebzig Prozent nach vorne — und schau was mit der Geschwindigkeit passiert.',
explanation: 'Das Speed Tape zeigt die angezeigte Fluggeschwindigkeit (IAS) in Knoten.',
atcMessage: 'Links erscheint jetzt das Speed Tape. Erster Schritt: Setz den Schubhebel sauber auf siebzig Prozent.',
explanation: 'Wir stabilisieren zuerst den Schub auf 70%, bevor wir Geschwindigkeit über Pitch kontrollieren.',
visibleElements: ['attitude', 'speedTape'],
layoutMode: 'split',
interactionGoal: { parameter: 'speed', target: 280, tolerance: 30, holdMs: 2000 },
interactionGoal: { parameter: 'throttlePercent', target: 70, tolerance: 4, holdMs: 1500 },
goalTimeoutMs: 20000,
goalHint: 'Schieb den Schubhebel bis ungefähr siebzig Prozent. Die Zahl auf dem Speed Tape sollte steigen.',
goalHint: 'Schubhebel auf die 70%-Marke setzen und kurz halten.',
buttons: [
{ id: 'skip_speed', label: 'Weiter', icon: 'mdi-arrow-right', next: 'alt_intro', type: 'primary' },
{ id: 'skip_speed', label: 'Weiter', icon: 'mdi-arrow-right', next: 'speed_hold_pitch', type: 'primary' },
],
},
// --- Phase 7: Altitude Intro ---
// --- Phase 7: Hold Speed With Pitch ---
{
id: 'speed_hold_pitch',
atcMessage: 'Jetzt kommt der wichtige Teil: Halte die Geschwindigkeit im roten Bereich. Der Schub bleibt bei siebzig Prozent — du steuerst das nur mit Pitch. Nase leicht hoch macht langsamer, Nase leicht runter macht schneller.',
explanation: 'Energie-Management: Mit konstantem Schub kontrollierst du die Geschwindigkeit über den Pitchwinkel.',
visibleElements: ['attitude', 'speedTape'],
layoutMode: 'split',
interactionGoal: { parameter: 'speed', target: 225, tolerance: 8, holdMs: 3000 },
speedTargetRange: { min: 217, max: 233 },
goalTimeoutMs: 25000,
goalHint: 'Halte den Schub bei 70%. Korrigiere nur mit Pitch, bis die Speed-Zahl im roten Band bleibt.',
buttons: [
{ id: 'skip_speed_hold', label: 'Weiter', icon: 'mdi-arrow-right', next: 'alt_intro', type: 'primary' },
],
},
// --- Phase 8: Altitude Intro ---
{
id: 'alt_intro',
atcMessage: 'Rechts kommt jetzt das Altitude Tape. Das zeigt deine Höhe in Fuß. Zieh den Stick leicht nach hinten — du steigst, und die Höhe nimmt zu.',
@@ -102,7 +118,7 @@ const phases: LearnPfdPhase[] = [
],
},
// --- Phase 8: Vertical Speed Intro ---
// --- Phase 9: Vertical Speed Intro ---
{
id: 'vs_intro',
atcMessage: 'Neben der Höhe siehst du jetzt die Vertical Speed Anzeige. Die zeigt dir, wie schnell du steigst oder sinkst. Plus heißt steigen, minus heißt sinken. Probier beides aus.',
@@ -117,7 +133,7 @@ const phases: LearnPfdPhase[] = [
],
},
// --- Phase 9: Heading Intro ---
// --- Phase 10: Heading Intro ---
{
id: 'heading_intro',
atcMessage: 'Unten erscheint jetzt das Heading. Das ist dein Kompass — es zeigt in welche Richtung du fliegst. Neig das Flugzeug nach rechts und beobachte, wie sich das Heading ändert.',
@@ -132,7 +148,7 @@ const phases: LearnPfdPhase[] = [
],
},
// --- Phase 10: Combined ---
// --- Phase 11: Combined ---
{
id: 'combined',
atcMessage: 'Sehr gut! Jetzt hast du alle Instrumente vor dir. Das ist das komplette PFD. Nimm dir einen Moment und spiel damit. Steig auf 8000 Fuß.',
@@ -146,7 +162,7 @@ const phases: LearnPfdPhase[] = [
],
},
// --- Phase 11: Free Practice ---
// --- Phase 12: Free Practice ---
{
id: 'free_practice',
atcMessage: 'Du hast das PFD verstanden. Ab jetzt kannst du frei üben. Experimentier mit dem Stick und dem Schubhebel. Schau dir an, wie alles zusammenhängt.',
@@ -157,7 +173,7 @@ const phases: LearnPfdPhase[] = [
],
},
// --- Phase 12: End ---
// --- Phase 13: End ---
{
id: 'end',
atcMessage: 'Fantastisch! Du kannst jetzt das PFD eines Airbus A320 lesen. Horizont, Geschwindigkeit, Höhe, Steigrate und Heading — alles klar.',

View File

@@ -120,9 +120,14 @@ export type FlightLabWSEvent =
export type PfdElement = 'attitude' | 'speedTape' | 'altitudeTape' | 'verticalSpeed' | 'heading'
export type PfdLayoutMode = 'model-focus' | 'split' | 'pfd-focus'
export interface PfdSpeedTargetRange {
min: number
max: number
}
export interface PfdInteractionGoal {
/** What to check: pitch angle, altitude, heading, speed, bank */
parameter: 'pitch' | 'altitude' | 'heading' | 'speed' | 'bankAngle' | 'verticalSpeed'
/** What to check: pitch angle, altitude, heading, speed, bank, thrust */
parameter: 'pitch' | 'altitude' | 'heading' | 'speed' | 'bankAngle' | 'verticalSpeed' | 'throttlePercent'
/** Target value */
target: number
/** Acceptable deviation (+/-) */
@@ -142,6 +147,8 @@ export interface LearnPfdPhase extends FlightLabPhase {
goalTimeoutMs?: number
/** Hint spoken via TTS if user struggles */
goalHint?: string
/** Optional highlighted target range on speed tape */
speedTargetRange?: PfdSpeedTargetRange
}
export interface LearnPfdScenario {