mirror of
https://github.com/OpenSquawk/OpenSquawk
synced 2026-08-11 20:15:32 +08:00
Rebuild learn modules with updated chapters
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -56,6 +56,18 @@ const readabilityScale = [
|
|||||||
{ level: 5, word: 'five', description: 'Perfectly readable' }
|
{ level: 5, word: 'five', description: 'Perfectly readable' }
|
||||||
]
|
]
|
||||||
|
|
||||||
|
const emergencyProblems = [
|
||||||
|
'engine failure on climb',
|
||||||
|
'smoke in the cabin',
|
||||||
|
'hydraulic failure',
|
||||||
|
'medical emergency on board',
|
||||||
|
'pressurization loss'
|
||||||
|
]
|
||||||
|
|
||||||
|
const pushDelayOptions = [3, 5, 7, 10]
|
||||||
|
const defaultSpeedRestrictions = [180, 200, 210, 220]
|
||||||
|
const approachAltitudeOptions = [3000, 4000, 5000, 6000]
|
||||||
|
|
||||||
const airlines: AirlineData[] = [
|
const airlines: AirlineData[] = [
|
||||||
{ code: 'DLH', call: 'Lufthansa' },
|
{ code: 'DLH', call: 'Lufthansa' },
|
||||||
{ code: 'BAW', call: 'Speedbird' },
|
{ code: 'BAW', call: 'Speedbird' },
|
||||||
@@ -73,9 +85,18 @@ const airportsData: AirportData[] = [
|
|||||||
runways: ['25C', '25R', '07C', '07R', '18'],
|
runways: ['25C', '25R', '07C', '07R', '18'],
|
||||||
stands: ['V155', 'A12', 'B24', 'G5', 'H43', 'L21'],
|
stands: ['V155', 'A12', 'B24', 'G5', 'H43', 'L21'],
|
||||||
sids: ['ANEKI 7S', 'TOBAK 5Q', 'OBOKA 6N'],
|
sids: ['ANEKI 7S', 'TOBAK 5Q', 'OBOKA 6N'],
|
||||||
|
stars: ['ANEKI 2A', 'TOBAK 3G', 'UNOKO 1S'],
|
||||||
transitions: ['ANEKI', 'TOBAK', 'OBOKA'],
|
transitions: ['ANEKI', 'TOBAK', 'OBOKA'],
|
||||||
|
arrivalTransitions: ['ANEKI', 'TOBAK', 'UNOKO'],
|
||||||
approaches: ['ILS Z 25C', 'ILS Y 07C'],
|
approaches: ['ILS Z 25C', 'ILS Y 07C'],
|
||||||
|
missedApproaches: [
|
||||||
|
'Climb straight ahead to 3000 feet, then as published.',
|
||||||
|
'As published.'
|
||||||
|
],
|
||||||
taxi: ['N3 U4', 'V A', 'N7 K', 'S V12'],
|
taxi: ['N3 U4', 'V A', 'N7 K', 'S V12'],
|
||||||
|
taxiIn: ['S N7', 'V12 S1', 'A K5', 'N3 U4'],
|
||||||
|
speedRestrictions: [200, 210],
|
||||||
|
emergencyHeadings: ['180', '220', '270'],
|
||||||
freqs: {
|
freqs: {
|
||||||
atis: '126.350',
|
atis: '126.350',
|
||||||
delivery: '121.900',
|
delivery: '121.900',
|
||||||
@@ -94,9 +115,18 @@ const airportsData: AirportData[] = [
|
|||||||
runways: ['26R', '26L', '08R', '08L'],
|
runways: ['26R', '26L', '08R', '08L'],
|
||||||
stands: ['211', '214', '302', 'N16', 'H45'],
|
stands: ['211', '214', '302', 'N16', 'H45'],
|
||||||
sids: ['OBAXA 3S', 'MERSI 6S', 'TULSI 5M'],
|
sids: ['OBAXA 3S', 'MERSI 6S', 'TULSI 5M'],
|
||||||
|
stars: ['OBAXA 4A', 'MERSI 3S', 'TULSI 2M'],
|
||||||
transitions: ['OBAXA', 'MERSI', 'TULSI'],
|
transitions: ['OBAXA', 'MERSI', 'TULSI'],
|
||||||
|
arrivalTransitions: ['OBAXA', 'MERSI', 'TULSI'],
|
||||||
approaches: ['ILS Z 26R', 'ILS Y 08R'],
|
approaches: ['ILS Z 26R', 'ILS Y 08R'],
|
||||||
|
missedApproaches: [
|
||||||
|
'Climb to 4000 feet, then as published.',
|
||||||
|
'As published.'
|
||||||
|
],
|
||||||
taxi: ['L4 N3', 'P3 W2', 'S1 D2'],
|
taxi: ['L4 N3', 'P3 W2', 'S1 D2'],
|
||||||
|
taxiIn: ['P3 W2', 'S1 D2', 'L4 N3'],
|
||||||
|
speedRestrictions: [190, 210],
|
||||||
|
emergencyHeadings: ['200', '240', '280'],
|
||||||
freqs: {
|
freqs: {
|
||||||
atis: '122.130',
|
atis: '122.130',
|
||||||
delivery: '121.775',
|
delivery: '121.775',
|
||||||
@@ -115,9 +145,18 @@ const airportsData: AirportData[] = [
|
|||||||
runways: ['24', '36L', '18C', '09'],
|
runways: ['24', '36L', '18C', '09'],
|
||||||
stands: ['D14', 'E22', 'F8', 'H4'],
|
stands: ['D14', 'E22', 'F8', 'H4'],
|
||||||
sids: ['SUGOL 2S', 'ANDIK 2S', 'ARNEM 2V'],
|
sids: ['SUGOL 2S', 'ANDIK 2S', 'ARNEM 2V'],
|
||||||
|
stars: ['SUGOL 2A', 'ANDIK 2S', 'ARNEM 2V'],
|
||||||
transitions: ['SUGOL', 'ANDIK', 'ARNEM'],
|
transitions: ['SUGOL', 'ANDIK', 'ARNEM'],
|
||||||
|
arrivalTransitions: ['SUGOL', 'ANDIK', 'ARNEM'],
|
||||||
approaches: ['ILS Z 24', 'ILS Y 18C'],
|
approaches: ['ILS Z 24', 'ILS Y 18C'],
|
||||||
|
missedApproaches: [
|
||||||
|
'Climb straight ahead to 2000 feet, then left turn ANDIK.',
|
||||||
|
'As published.'
|
||||||
|
],
|
||||||
taxi: ['A5 B2', 'K1 V3', 'W4 S8'],
|
taxi: ['A5 B2', 'K1 V3', 'W4 S8'],
|
||||||
|
taxiIn: ['A5 B2', 'K1 V3', 'W4 S8'],
|
||||||
|
speedRestrictions: [180, 200],
|
||||||
|
emergencyHeadings: ['180', '210', '250'],
|
||||||
freqs: {
|
freqs: {
|
||||||
atis: '136.050',
|
atis: '136.050',
|
||||||
delivery: '121.800',
|
delivery: '121.800',
|
||||||
@@ -235,6 +274,16 @@ function visibilityToWords(vis: string): string {
|
|||||||
return vis
|
return vis
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function minutesToWords(minutes: number): string {
|
||||||
|
const value = Math.max(1, Math.round(minutes))
|
||||||
|
const unit = value === 1 ? 'minute' : 'minutes'
|
||||||
|
return `${digitsToWords(value.toString())} ${unit}`
|
||||||
|
}
|
||||||
|
|
||||||
|
function speedToWords(speed: number): string {
|
||||||
|
return `${digitsToWords(Math.round(speed).toString())} knots`
|
||||||
|
}
|
||||||
|
|
||||||
function altitudeToWords(value: number): string {
|
function altitudeToWords(value: number): string {
|
||||||
const thousands = Math.floor(value / 1000)
|
const thousands = Math.floor(value / 1000)
|
||||||
const remainder = value % 1000
|
const remainder = value % 1000
|
||||||
@@ -257,7 +306,12 @@ export function createBaseScenario(): Scenario {
|
|||||||
const taxiRoute = choice(airport.taxi)
|
const taxiRoute = choice(airport.taxi)
|
||||||
const sid = choice(airport.sids)
|
const sid = choice(airport.sids)
|
||||||
const transition = choice(airport.transitions)
|
const transition = choice(airport.transitions)
|
||||||
const approach = choice(airport.approaches)
|
const arrivalRunway = choice(destination.runways)
|
||||||
|
const arrivalStand = choice(destination.stands)
|
||||||
|
const arrivalTaxiRoute = choice(destination.taxiIn ?? destination.taxi)
|
||||||
|
const arrivalStar = choice(destination.stars ?? destination.sids)
|
||||||
|
const arrivalTransition = choice(destination.arrivalTransitions ?? destination.transitions)
|
||||||
|
const approach = choice(destination.approaches)
|
||||||
const altitude = choice([4000, 5000, 6000, 7000])
|
const altitude = choice([4000, 5000, 6000, 7000])
|
||||||
const climbAltitude = altitude + 2000
|
const climbAltitude = altitude + 2000
|
||||||
const squawk = generateSquawk()
|
const squawk = generateSquawk()
|
||||||
@@ -272,6 +326,26 @@ export function createBaseScenario(): Scenario {
|
|||||||
const dewpoint = Math.max(temperature - randInt(2, 6), -10)
|
const dewpoint = Math.max(temperature - randInt(2, 6), -10)
|
||||||
const atisCode = choice(atisLetters)
|
const atisCode = choice(atisLetters)
|
||||||
const remarks = choice(['NOSIG', 'BECMG 4000', 'TEMPO -SHRA'])
|
const remarks = choice(['NOSIG', 'BECMG 4000', 'TEMPO -SHRA'])
|
||||||
|
const arrivalQnh = randInt(980, 1035)
|
||||||
|
const arrivalWindDirection = randInt(0, 35) * 10
|
||||||
|
const arrivalWindSpeed = randInt(3, 18)
|
||||||
|
const approachAltitude = choice(approachAltitudeOptions)
|
||||||
|
const speedRestriction = choice(destination.speedRestrictions ?? defaultSpeedRestrictions)
|
||||||
|
const pushDelay = choice(pushDelayOptions)
|
||||||
|
const vectorHeading = (randInt(0, 35) * 10).toString().padStart(3, '0')
|
||||||
|
const emergencyHeadingRaw = destination.emergencyHeadings
|
||||||
|
? choice(destination.emergencyHeadings)
|
||||||
|
: (randInt(0, 35) * 10).toString()
|
||||||
|
const emergencyHeading = emergencyHeadingRaw.toString().padStart(3, '0')
|
||||||
|
const missedApproach = choice(destination.missedApproaches ?? ['as published'])
|
||||||
|
const emergencyIntentOptions = [
|
||||||
|
`return to ${airport.city}`,
|
||||||
|
`divert to ${destination.city}`,
|
||||||
|
'land at nearest suitable aerodrome',
|
||||||
|
'hold to troubleshoot'
|
||||||
|
]
|
||||||
|
const emergencyProblem = choice(emergencyProblems)
|
||||||
|
const emergencyIntent = choice(emergencyIntentOptions)
|
||||||
const now = new Date()
|
const now = new Date()
|
||||||
const minute = Math.floor(now.getUTCMinutes() / 5) * 5
|
const minute = Math.floor(now.getUTCMinutes() / 5) * 5
|
||||||
const timestamp = `${now.getUTCDate().toString().padStart(2, '0')}${now
|
const timestamp = `${now.getUTCDate().toString().padStart(2, '0')}${now
|
||||||
@@ -283,6 +357,17 @@ export function createBaseScenario(): Scenario {
|
|||||||
const metar = `${airport.icao} ${timestamp} ${metarWindGroup} ${visibility} ${cloud} ${tempGroup} Q${qnh
|
const metar = `${airport.icao} ${timestamp} ${metarWindGroup} ${visibility} ${cloud} ${tempGroup} Q${qnh
|
||||||
.toString()
|
.toString()
|
||||||
.padStart(4, '0')} ${remarks}`
|
.padStart(4, '0')} ${remarks}`
|
||||||
|
const arrivalWind = `${arrivalWindDirection.toString().padStart(3, '0')}/${arrivalWindSpeed
|
||||||
|
.toString()
|
||||||
|
.padStart(2, '0')}`
|
||||||
|
const arrivalRunwayWords = runwayToWords(arrivalRunway)
|
||||||
|
const arrivalWindWords = windToWords(arrivalWindDirection, arrivalWindSpeed)
|
||||||
|
const arrivalQnhWords = qnhToWords(arrivalQnh)
|
||||||
|
const approachAltitudeWords = altitudeToWords(approachAltitude)
|
||||||
|
const speedRestrictionWords = speedToWords(speedRestriction)
|
||||||
|
const pushDelayWords = minutesToWords(pushDelay)
|
||||||
|
const vectorHeadingWords = digitsToWords(vectorHeading)
|
||||||
|
const emergencyHeadingWords = digitsToWords(emergencyHeading)
|
||||||
|
|
||||||
const frequencies: Frequency[] = [
|
const frequencies: Frequency[] = [
|
||||||
{ type: 'ATIS', label: 'ATIS', value: airport.freqs.atis },
|
{ type: 'ATIS', label: 'ATIS', value: airport.freqs.atis },
|
||||||
@@ -324,6 +409,16 @@ export function createBaseScenario(): Scenario {
|
|||||||
sid,
|
sid,
|
||||||
transition,
|
transition,
|
||||||
approach,
|
approach,
|
||||||
|
arrivalRunway,
|
||||||
|
arrivalRunwayWords,
|
||||||
|
arrivalTaxiRoute,
|
||||||
|
arrivalStand,
|
||||||
|
arrivalStar,
|
||||||
|
arrivalTransition,
|
||||||
|
arrivalQnh,
|
||||||
|
arrivalQnhWords,
|
||||||
|
arrivalWind,
|
||||||
|
arrivalWindWords,
|
||||||
altitudes: {
|
altitudes: {
|
||||||
initial: altitude,
|
initial: altitude,
|
||||||
climb: climbAltitude,
|
climb: climbAltitude,
|
||||||
@@ -352,6 +447,17 @@ export function createBaseScenario(): Scenario {
|
|||||||
temperatureWords: temperatureToWords(temperature),
|
temperatureWords: temperatureToWords(temperature),
|
||||||
dewpoint,
|
dewpoint,
|
||||||
dewpointWords: temperatureToWords(dewpoint),
|
dewpointWords: temperatureToWords(dewpoint),
|
||||||
|
approachAltitude,
|
||||||
|
approachAltitudeWords,
|
||||||
|
pushDelayMinutes: pushDelay,
|
||||||
|
pushDelayWords,
|
||||||
|
speedRestriction,
|
||||||
|
speedRestrictionWords,
|
||||||
|
vectorHeading,
|
||||||
|
vectorHeadingWords,
|
||||||
|
emergencyHeading,
|
||||||
|
emergencyHeadingWords,
|
||||||
|
missedApproach,
|
||||||
metar,
|
metar,
|
||||||
metarSegments: {
|
metarSegments: {
|
||||||
wind: metarWindGroup,
|
wind: metarWindGroup,
|
||||||
@@ -372,6 +478,31 @@ export function createBaseScenario(): Scenario {
|
|||||||
approachFreq: airport.freqs.approach,
|
approachFreq: airport.freqs.approach,
|
||||||
centerFreq: airport.freqs.center,
|
centerFreq: airport.freqs.center,
|
||||||
transLevel: airport.transLevel,
|
transLevel: airport.transLevel,
|
||||||
remarks
|
remarks,
|
||||||
|
emergencyProblem,
|
||||||
|
emergencyIntent
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function cloneScenario(scenario: Scenario): Scenario {
|
||||||
|
if (typeof structuredClone === 'function') {
|
||||||
|
return structuredClone(scenario)
|
||||||
|
}
|
||||||
|
return JSON.parse(JSON.stringify(scenario)) as Scenario
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ScenarioGenerator = (() => Scenario) & { reset: () => void }
|
||||||
|
|
||||||
|
export function createScenarioSeries(): ScenarioGenerator {
|
||||||
|
let cached: Scenario | null = null
|
||||||
|
const generator = () => {
|
||||||
|
if (!cached) {
|
||||||
|
cached = createBaseScenario()
|
||||||
|
}
|
||||||
|
return cloneScenario(cached)
|
||||||
|
}
|
||||||
|
generator.reset = () => {
|
||||||
|
cached = null
|
||||||
|
}
|
||||||
|
return generator
|
||||||
|
}
|
||||||
|
|||||||
@@ -15,9 +15,15 @@ export type AirportData = {
|
|||||||
runways: string[]
|
runways: string[]
|
||||||
stands: string[]
|
stands: string[]
|
||||||
sids: string[]
|
sids: string[]
|
||||||
|
stars?: string[]
|
||||||
transitions: string[]
|
transitions: string[]
|
||||||
|
arrivalTransitions?: string[]
|
||||||
approaches: string[]
|
approaches: string[]
|
||||||
|
missedApproaches?: string[]
|
||||||
taxi: string[]
|
taxi: string[]
|
||||||
|
taxiIn?: string[]
|
||||||
|
speedRestrictions?: number[]
|
||||||
|
emergencyHeadings?: string[]
|
||||||
freqs: {
|
freqs: {
|
||||||
atis: string
|
atis: string
|
||||||
delivery: string
|
delivery: string
|
||||||
@@ -52,6 +58,16 @@ export type Scenario = {
|
|||||||
sid: string
|
sid: string
|
||||||
transition: string
|
transition: string
|
||||||
approach: string
|
approach: string
|
||||||
|
arrivalRunway: string
|
||||||
|
arrivalRunwayWords: string
|
||||||
|
arrivalTaxiRoute: string
|
||||||
|
arrivalStand: string
|
||||||
|
arrivalStar: string
|
||||||
|
arrivalTransition: string
|
||||||
|
arrivalQnh: number
|
||||||
|
arrivalQnhWords: string
|
||||||
|
arrivalWind: string
|
||||||
|
arrivalWindWords: string
|
||||||
altitudes: {
|
altitudes: {
|
||||||
initial: number
|
initial: number
|
||||||
climb: number
|
climb: number
|
||||||
@@ -80,6 +96,17 @@ export type Scenario = {
|
|||||||
temperatureWords: string
|
temperatureWords: string
|
||||||
dewpoint: number
|
dewpoint: number
|
||||||
dewpointWords: string
|
dewpointWords: string
|
||||||
|
approachAltitude: number
|
||||||
|
approachAltitudeWords: string
|
||||||
|
pushDelayMinutes: number
|
||||||
|
pushDelayWords: string
|
||||||
|
speedRestriction: number
|
||||||
|
speedRestrictionWords: string
|
||||||
|
vectorHeading: string
|
||||||
|
vectorHeadingWords: string
|
||||||
|
emergencyHeading: string
|
||||||
|
emergencyHeadingWords: string
|
||||||
|
missedApproach: string
|
||||||
metar: string
|
metar: string
|
||||||
metarSegments: {
|
metarSegments: {
|
||||||
wind: string
|
wind: string
|
||||||
@@ -101,6 +128,8 @@ export type Scenario = {
|
|||||||
centerFreq: string
|
centerFreq: string
|
||||||
transLevel: string
|
transLevel: string
|
||||||
remarks: string
|
remarks: string
|
||||||
|
emergencyProblem: string
|
||||||
|
emergencyIntent: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type LessonField = {
|
export type LessonField = {
|
||||||
|
|||||||
Reference in New Issue
Block a user