-- "PMCO - Pilot Monitoring Callouts" FREEWARE LUA plugin
-- Version: forked from 2.3beta (13.05.2021), see Version below
-- by Nils Unger
-- port to ToLiss/XPlane 11 by Holger Teutsch (hotbso)
-- print(string.format("PMCO - PLANE_ICAO: %s", PLANE_ICAO))
print(string.format("############ PMCO - PLANE_ICAO: %s", PLANE_ICAO))
-- ToLiss only
if PLANE_ICAO == "A319" or PLANE_ICAO == "A321" or PLANE_ICAO == "A21N" or PLANE_ICAO == "A346" or PLANE_ICAO == "A359" then
-- =========================
local Version = "XPL 1.5"
-- =========================
-- ##################################################
-- ############ edit user options here ##############
-- ##################################################
local pV1 = 1 -- play PMCO V1 sound? 0 = never, 1 = yes
local V1_timing = 2 -- V1 will be announced at the speed of V1 - V1_timing. If you want V1 to be announced slightly before V1 is reached on the PFD, enter the number of kts.
local pCallFlapsOnGround = 0 -- play flaps callouts on the ground; 0 = no, 1 = yes
local pGearFlapDelay = 0 -- delay gear and flap callouts for this amount of time (in milliseconds)
local sound_set = "Copila"
-- ##################################################
-- ##### end of user options, no editing below ######
-- ##################################################
-- flags and variables (NOT to edit by user!!!) -----
local Volume = 0.4 -- default volume of callouts (soundset.cfg may override this)
local bPlayV1 = pV1 -- decision to play PMCO V1 callout
if PLANE_ICAO == "A346" then bPlayV1 = 0 end -- A346 has a hardwired internal V1 call
local PFD_delay = 650 -- milliseconds
-- flags and variables
local iBaseClock = 100 -- milliseconds
local iClockNo = -1 -- so we start with zero equals init
local iV1Select, iVrSelect = 0, 0 -- kts
local iIAS = 0 -- kts
local iALT = 0 -- ft
local iEng1_N1, iEng2_N1 = 0, 0 -- %
local iVertSpeed = 0 -- ft/min
local iAccelLateral = 0.0 -- ft/sec2
local iMaxDecel = 0.0
local iGS = 0.0 -- kts
local iThrustLeversOld = 0
local bThrustLeversReject = false
local iFlapRunning, iFlapLeverOld, iFlapDelay, iFlapLandingCfg = 0, 0, 2, 0
local sPhase = "init"
local blockFlexSet, blockToGaSet, blockClbSet = true, true, true
local block80kts = true
local block100kts, blockV1, blockRotate, blockLiftoff, blockPosClimb = true, true, true, true, true
local blockTouchdown, blockSpoilers, blockRevers, blockDecel, block70kts = true, true, true, true, true
local blockStop, blockStandUp, blockGo = true, true, true
local bCommitedToStop = false
local bMeasureDecel = false
local decel_ts
local lat0, lon0, alt0
-- need a table to save upvalues
local dr = {
time_sec = XPLMFindDataRef("sim/time/total_running_time_sec"),
ias = XPLMFindDataRef("sim/flightmodel/position/indicated_airspeed2"),
gs = XPLMFindDataRef("sim/flightmodel/position/groundspeed"),
THS = XPLMFindDataRef("AirbusFBW/PitchTrimPosition"),
accel = XPLMFindDataRef("sim/flightmodel/position/local_az"), -- +z points to tail
spoiler_armed = XPLMFindDataRef("sim/cockpit2/controls/speedbrake_ratio"),
on_ground = XPLMFindDataRef("sim/flightmodel/forces/fnrml_gear"),
flap = XPLMFindDataRef("AirbusFBW/FlapLeverRatio"),
spoilers = XPLMFindDataRef("AirbusFBW/SDSpoilerArray"),
reversers = XPLMFindDataRef("AirbusFBW/ENGRevArray"),
pitch = XPLMFindDataRef("sim/flightmodel/position/theta"),
lat = XPLMFindDataRef("sim/flightmodel/position/latitude"),
lon = XPLMFindDataRef("sim/flightmodel/position/longitude"),
ra = XPLMFindDataRef("sim/flightmodel/position/y_agl"),
N1 = XPLMFindDataRef("sim/flightmodel/engine/ENGN_N1_"),
TLA = XPLMFindDataRef("AirbusFBW/throttle_input"),
}
-- dr.gear_lever = XPLMFindDataRef("1-sim/gearhandle") // seems to be wrong way round
if PLANE_ICAO == "A359" then
dr.gear_lever = XPLMFindDataRef("1-sim/gear/flag")
dr.VS = XPLMFindDataRef("airbus_qpac/pfdoutputs/captain/vertical_speed")
dr.V1 = XPLMFindDataRef("1-sim/fms/perf/speed/v1")
dr.VR = XPLMFindDataRef("1-sim/fms/perf/speed/vr")
else
dr.gear_lever = XPLMFindDataRef("AirbusFBW/GearLever")
dr.VS = XPLMFindDataRef("toliss_airbus/pfdoutputs/captain/vertical_speed")
dr.V1 = XPLMFindDataRef("toliss_airbus/performance/V1")
dr.VR = XPLMFindDataRef("toliss_airbus/performance/VR")
end
-- dump dr table vals:
-- for k, v in pairs(dr) do
-- print(string.format("PMCO - %s: %s", k, XPLMGetDataf(v)))
-- end
local sounds = {
["100knots.wav"] = { pre = PFD_delay, post = 600 },
["70knots.wav"] = { pre = PFD_delay, post = 1000 },
["80knots.wav"] = { pre = 100, post = 100 },
["climbSet.wav"] = { pre = 1000, post = 1000 },
["decel.wav"] = { pre = 100, post = 700 },
["flaps1.wav"] = { pre = pGearFlapDelay, post = 1000 },
["flaps2.wav"] = { pre = pGearFlapDelay, post = 1000 },
["flaps3.wav"] = { pre = pGearFlapDelay, post = 1000 },
["flapsFull.wav"] = { pre = pGearFlapDelay, post = 1000 },
["flapsUp.wav"] = { pre = pGearFlapDelay, post = 1000 },
["flexSet.wav"] = { pre = 800, post = 1000 }, -- 800 = spool up delay
["gearDown.wav"] = { pre = 1000, post = 1000 },
["gearUp.wav"] = { pre = 1000, post = 1000 },
["go.wav"] = { pre = 500, post = 1000 },
["positiveClimb.wav"] = { pre = 0, post = 1000 },
["reverseGreen.wav"] = { pre = 10, post = 1000 },
["rotate.wav"] = { pre = PFD_delay, post = 1000 },
["spoilers.wav"] = { pre = 1200, post = 1700 },
["standUp.wav"] = { pre = 1000, post = 1000 },
["stop.wav"] = { pre = 700, post = 1200 },
["togaSet.wav"] = { pre = 1000, post = 1000 },
["v1.wav"] = { pre = PFD_delay, post = 1000 }
}
local sound_dir = SCRIPT_DIRECTORY .. "PMCOSounds/" .. sound_set .. "/"
-- initialize sounds
for s, sr in pairs(sounds) do
local si = load_WAV_file(sound_dir .. s)
sr.si = si
set_sound_gain(si, Volume)
end
local wait_until_sound = 0
local wait_after_sound = 0
local sound_to_play
local next_tick = 0
local now = 0
-- used to reinit when spawned in the air
local function init_state()
iClockNo = -1
iThrustLeversOld = 0
bThrustLeversReject = false
iFlapRunning, iFlapLeverOld, iFlapDelay, iFlapLandingCfg = 0, 0, 2, 0
sPhase = "init"
blockFlexSet, blockToGaSet, blockClbSet = true, true, true
block80kts = true
block100kts, blockV1, blockRotate, blockLiftoff, blockPosClimb = true, true, true, true, true
blockTouchdown, blockSpoilers, blockRevers, blockDecel, block70kts = true, true, true, true, true
blockStop, blockStandUp, blockGo = true, true, true
bCommitedToStop = false
bMeasureDecel = false
lat0 = nil
end
local log, schedule_sound -- forward declaration
-- function declaration
-- this runs in the frame loop, efficient coding please
function PMCO_callouts() -- callouts logics function to play the callouts
-- read & process raw sim data (with different clocks):
-- ####################################################
-- always try to feel the deceleration, even if waiting before sound
-- measuring of max decel:
-- #######################
iAccelLateral = XPLMGetDataf(dr.accel) * 3.28084 -- lateral acceleration [ft/sec2]
if bMeasureDecel and (iAccelLateral > iMaxDecel) then
iMaxDecel = iAccelLateral -- log max decel from touchdown to 70kts
end
now = math.floor(XPLMGetDataf(dr.time_sec) * 1000)
if now < wait_until_sound then return end
if sound_to_play then
play_sound(sound_to_play)
sound_to_play = nil
end
if now < wait_after_sound then return end
if now < next_tick then return end
next_tick = now + iBaseClock
iClockNo = iClockNo + 1
if iClockNo > 4 then iClockNo = 1 end
-- every clock (base clock = fast)
iIAS = XPLMGetDataf(dr.ias) -- indicated air speed [kts]
iGS = XPLMGetDataf(dr.gs) * 1.9438445 -- ground speed [kts]
-- every even clock (2x base clock = middle)
if (iClockNo % 2 == 0) then
iVertSpeed = XPLMGetDataf(dr.VS) -- vertical speed [ft/min]
iGNDSpoilersArmed = XPLMGetDataf(dr.spoiler_armed)-- speed brake lever
local v = XPLMGetDatavi(dr.spoilers, 0, 1)
iGNDSpoilersUp = v[0] -- ground spoilers (inner left representing all)
iTHSval = XPLMGetDataf(dr.THS) -- trimmable horizontal stabilizer: -4.0...+13.5
-- ground spoilers armed
if iGNDSpoilersArmed < 0 then
iGNDSpoilersArmed = 1 -- yes
else
iGNDSpoilersArmed = 0 -- no
end
end -- 2x base clock
-- every fourth clock (4x base clock = slow)
if ((iClockNo == 0) or (iClockNo == 1)) then
local N1 = XPLMGetDatavf(dr.N1, 0, 2)
iEng1_N1 = N1[0] -- L engine N1 [%]
iEng2_N1 = N1[1] -- R engine N1 [%]
iPitch = XPLMGetDataf(dr.pitch) -- pitch [°]
if XPLMGetDataf(dr.on_ground) > 1.0 then iOnGround = 1 else iOnGround = 0 end -- 1 = on the ground; 0 = airborne
local alt = XPLMGetDataf(dr.ra) -- m
local lat = XPLMGetDataf(dr.lat)
local lon = XPLMGetDataf(dr.lon)
-- test for teleportation = load of a situation
if (lat0 ~= nil) then
local R = 6371001.0 -- IUGG average radius of earth in m
local d_1deg = R * math.pi / 180 -- distance 1 deg in m, ~ 60 sm
-- simplified euclidian distance
local x = (lon - lon0) * math.cos(math.rad((lat + lat0)/2)) * d_1deg
local y = (lat - lat0) * d_1deg
local z = alt - alt0
local d = math.sqrt(x*x + y*y + z*z)
local v = d / ((now - now0) * 0.001)
if v > 340 then -- supersonic
log("teleportation detected")
init_state() -- reinit
return
end
end
lat0, lon0, alt0, now0 = lat, lon, alt, now
iALT = alt * 3.28084 -- radar altitude [ft]
local TLA = XPLMGetDatavf(dr.TLA, 0, 1)
iThrustLevers = TLA[0] -- thrust levers (left lever representing both)
iFlapLever = math.floor(XPLMGetDataf(dr.flap) * 4.0 + 0.5)
iGearLever = XPLMGetDatai(dr.gear_lever) -- gear lever
-- engines running
if (iEng1_N1 < 15) and (iEng2_N1 < 15) then
bEnginesRunning = false
else
bEnginesRunning = true
end
-- log(" N1: " .. iEng1_N1 .. ", " .. iEng2_N1)
-- thrust lever position (detents)
if iThrustLevers == 0 then sThrustLevers = "IDLE"
elseif (iThrustLevers > 0) and (iThrustLevers < 0.69) then
sThrustLevers = "MAN"
bThrustLeversReject = false
elseif math.abs(iThrustLevers - 0.7) < 0.05 then sThrustLevers = "CL"
elseif math.abs(iThrustLevers - 0.88) < 0.05 then sThrustLevers = "FLX"
elseif math.abs(iThrustLevers - 1.0) < 0.05 then sThrustLevers = "TOGA"
elseif iThrustLevers < 0.0 then sThrustLevers = "REV"
else
sThrustLevers = "undefined"
end
-- log(" Levers: " .. iThrustLevers .. ", " .. sThrustLevers .. ", Ground: " .. iOnGround)
if (iThrustLevers <= 0.0) and (iThrustLeversOld > 0) then
if ((sPhase == "takeoff") or (sPhase == "touchAndGo")) then bThrustLeversReject = true end
end
iThrustLeversOld = iThrustLevers
-- flap callout delay (to avoid multiple callouts)
if iFlapRunning < iFlapDelay then iFlapRunning = iFlapRunning + 1 end -- increment delay timer
if iFlapLever ~= iFlapLeverOld then -- flap lever was moved
iFlapRunning = 0 -- reset delay
iFlapLeverOld = iFlapLever -- save current value
end
-- -- ipc.display("iFlapRunning = " .. iFlapRunning)
-- landing gear
if iGearLever == 0 then
iGearUp = 1
else
iGearUp = 0
end
end -- 4x base clock
-- initialize toggle switches (first loop)
-- #######################################
if iClockNo == 0 then -- init only with first loop
log("## initialize switch positions (first loop) ##")
-- initialize gear lever
if iGearUp == 1 then
blockGearUp, blockGearDn = true, false -- allow "gear down" callout
log("initialize gear: up")
else
blockGearDn, blockGearUp = true, false -- allow "gear up" callout
log("initialize gear: down")
end
-- initialize flap lever
if iFlapLever == 0 then
blockFlapsUp, blockFlaps1, blockFlaps2, blockFlaps3, blockFlapsFull = true, false, false, false, false
log("initialize flaps: up")
elseif iFlapLever == 1 then
blockFlapsUp, blockFlaps1, blockFlaps2, blockFlaps3, blockFlapsFull = false, true, false, false, false
log("initialize flaps: 1")
elseif iFlapLever == 2 then
blockFlapsUp, blockFlaps1, blockFlaps2, blockFlaps3, blockFlapsFull = false, false, true, false, false
log("initialize flaps: 2")
elseif iFlapLever == 3 then
blockFlapsUp, blockFlaps1, blockFlaps2, blockFlaps3, blockFlapsFull = false, false, false, true, false
log("initialize flaps: 3")
elseif iFlapLever == 4 then
blockFlapsUp, blockFlaps1, blockFlaps2, blockFlaps3, blockFlapsFull = false, false, false, false, true
log("initialize flaps: full")
end
end -- first loop
-- phases (main scenarios):
-- ########################
if ((iClockNo == 0) or (iClockNo == 2)) then -- not every cycle
if not (sPhase == "enginesOff") and not bEnginesRunning and (iOnGround == 1) then -- engines off
sPhase = "enginesOff"
log("++ PMCO phase = " .. sPhase)
end
if not (sPhase == "taxiing") and bEnginesRunning and (iGS < 40) and ((sThrustLevers == "IDLE") or (sThrustLevers == "MAN")) then -- taxiing
sPhase = "taxiing"
log("++ PMCO phase = " .. sPhase)
blockStop, blockSpoilers, blockRevers, blockDecel, block70kts = true, true, true, true, true
elseif not ((sPhase == "takeoff") or (sPhase == "touchAndGo")) and bEnginesRunning and (iOnGround == 1) and ((sThrustLevers == "FLX") or (sThrustLevers == "TOGA")) then -- takeoff
sPhase = "takeoff"
log("++ PMCO phase = " .. sPhase .. " (" .. string.format("%.1f",iGS) .. "kts_GS and lever position '" .. sThrustLevers .. "')")
blockFlexSet, blockToGaSet = false, false
block80kts = false
block100kts, blockV1, blockRotate, blockLiftoff = false, false, false, false
iV1Select = XPLMGetDataf(dr.V1)
if iV1Select == 0 then iV1Select = 1000 end -- if not set skip callout
iVrSelect = XPLMGetDataf(dr.VR)
if iVrSelect == 0 then iVrSelect = 1000 end
elseif not (sPhase == "rejectedTakeoff") and ((sPhase == "takeoff") or (sPhase == "touchAndGo")) and (iGS > 30) and bThrustLeversReject then -- rejected takeoff
sPhase = "rejectedTakeoff"
log("++ PMCO phase = " .. sPhase)
blockStop = false
decel_ts = now
if (iGS > 100) then
blockDecel, blockRevers = false, false
decel_ts = now + 7 * 1000
end
if (iGS > 75) then block70kts = false end
blockFlexSet, blockToGaSet = true, true
block80kts = true
block100kts, blockV1, blockRotate = true, true, true
blockStandUp, blockGo = true, true
bMeasureDecel = true
iMaxDecel = 0.0
elseif not (sPhase == "climbOut") and ((sPhase == "takeoff") or (sPhase == "goAround") or (sPhase == "touchAndGo")) and (iVertSpeed > 500) then -- climb out
sPhase = "climbOut"
log("++ PMCO phase = " .. sPhase)
blockPosClimb = false
blockFlexSet, blockToGaSet = true, true
block80kts = true
block100kts, blockV1, blockRotate = true, true, true
blockStandUp, blockGo = true, true
elseif not (sPhase == "cruise") and ((sPhase == "climbOut") or (sPhase == "init")) and (iOnGround == 0) and (iThrustLevers <= 0.705) then -- cruise or pattern
if not (sPhase == "init") and (sThrustLevers == "CL") then blockClbSet = false end -- to avoid the callout when loading the plug-in off the ground
sPhase = "cruise"
log("++ PMCO phase = " .. sPhase)
blockLiftoff, blockPosClimb = true, true
elseif not (sPhase == "landing") and (sPhase == "cruise") and (iGearUp == 0) and (iVertSpeed < -500) then -- landing
sPhase = "landing"
log("++ PMCO phase = " .. sPhase)
blockTouchdown, blockSpoilers, blockRevers, blockDecel, block70kts = false, false, false, false, false
blockClbSet = true
bCommitedToStop = false
bMeasureDecel = true
iMaxDecel = 0.0
elseif not (sPhase == "goAround") and (sPhase == "landing") and (sThrustLevers == "TOGA") then -- go-around
sPhase = "goAround"
log("++ PMCO phase = " .. sPhase)
blockToGaSet = false
blockTouchdown, blockSpoilers, blockRevers, blockDecel, block70kts = true, true, true, true, true
elseif not (sPhase == "touchAndGo") and (sPhase == "landing") and (iOnGround == 1) and (iGNDSpoilersArmed == 0) and not bCommitedToStop then -- touch-and-go
sPhase = "touchAndGo"
log("++ PMCO phase = " .. sPhase)
blockStandUp = false
blockSpoilers, blockRevers, blockDecel, block70kts = true, true, true, true
iFlapLandingCfg = iFlapLever -- save landing flaps
end
end
-- callouts & logs:
-- ################
if ((iClockNo == 0) or (iClockNo == 3)) then -- non time-critical callouts
if (iGearUp == 0) and not blockGearDn then -- "gear down" callout
log("'gear down' callout (" .. string.format("%.0f",iIAS) .. "kts_IAS and " .. string.format("%.0f",iALT) .. "ft_ALT and " .. string.format("%.0f",iVertSpeed) .. "ft/min_VS)")
blockGearDn, blockGearUp = true, false -- play only once
schedule_sound("gearDown.wav")
return
end
if (iGearUp == 1) and not blockGearUp then -- "gear up" callout
blockGearUp, blockGearDn = true, false -- play only once
log("'gear up' callout (" .. string.format("%.0f",iIAS) .. "kts_IAS and " .. string.format("%.0f",iALT) .. "ft_ALT and " .. string.format("%.0f",iVertSpeed) .. "ft/min_VS)")
schedule_sound("gearUp.wav")
return
end
if (iFlapLever == 0) and (iFlapRunning >= iFlapDelay) and not blockFlapsUp then -- "flaps up" callout
blockFlapsUp, blockFlaps1, blockFlaps2, blockFlaps3, blockFlapsFull = true, false, false, false, false
if (iOnGround == 0) or (pCallFlapsOnGround == 1) then -- play on the ground?
log("'flaps 0' callout (" .. string.format("%.0f",iIAS) .. "kts_IAS and " .. string.format("%.0f",iALT) .. "ft_ALT and " .. string.format("%.0f",iVertSpeed) .. "ft/min_VS)")
schedule_sound("flapsUp.wav")
return
end
end
if (iFlapLever == 1) and (iFlapRunning >= iFlapDelay) and not blockFlaps1 then -- "flaps 1" callout
blockFlapsUp, blockFlaps1, blockFlaps2, blockFlaps3, blockFlapsFull = false, true, false, false, false
if (iOnGround == 0) or (pCallFlapsOnGround == 1) then -- play on the ground?
log("'flaps 1' callout (" .. string.format("%.0f",iIAS) .. "kts_IAS and " .. string.format("%.0f",iALT) .. "ft_ALT and " .. string.format("%.0f",iVertSpeed) .. "ft/min_VS)")
schedule_sound("flaps1.wav")
return
end
end
if (iFlapLever == 2) and (iFlapRunning >= iFlapDelay) and not blockFlaps2 then -- "flaps 2" callout
blockFlapsUp, blockFlaps1, blockFlaps2, blockFlaps3, blockFlapsFull = false, false, true, false, false
if ((iOnGround == 0) or (pCallFlapsOnGround == 1)) and not ((sPhase == "goAround") or (sPhase == "touchAndGo")) then -- don't play during go-around or touch-and-go
log("'flaps 2' callout (" .. string.format("%.0f",iIAS) .. "kts_IAS and " .. string.format("%.0f",iALT) .. "ft_ALT and " .. string.format("%.0f",iVertSpeed) .. "ft/min_VS)")
schedule_sound("flaps2.wav")
return
end
end
if (iFlapLever == 3) and (iFlapRunning >= iFlapDelay) and not blockFlaps3 then -- "flaps 3" callout
blockFlapsUp, blockFlaps1, blockFlaps2, blockFlaps3, blockFlapsFull = false, false, false, true, false
if ((iOnGround == 0) or (pCallFlapsOnGround == 1)) and not ((sPhase == "goAround") or (sPhase == "touchAndGo")) then -- don't play during go-around or touch-and-go
log("'flaps 3' callout (" .. string.format("%.0f",iIAS) .. "kts_IAS and " .. string.format("%.0f",iALT) .. "ft_ALT and " .. string.format("%.0f",iVertSpeed) .. "ft/min_VS)")
schedule_sound("flaps3.wav")
return
end
end
if (iFlapLever == 4) and (iFlapRunning >= iFlapDelay) and not blockFlapsFull then -- "flaps full" callout
blockFlapsUp, blockFlaps1, blockFlaps2, blockFlaps3, blockFlapsFull = false, false, false, false, true
if (iOnGround == 0) or (pCallFlapsOnGround == 1) then -- play on the ground?
log("'flaps full' callout (" .. string.format("%.0f",iIAS) .. "kts_IAS and " .. string.format("%.0f",iALT) .. "ft_ALT and " .. string.format("%.0f",iVertSpeed) .. "ft/min_VS)")
schedule_sound("flapsFull.wav")
return
end
end
if (sThrustLevers == "FLX") and (iEng1_N1 >= 75.0) and not blockFlexSet then -- "flex set" callout
blockFlexSet = true
log("'flex set' callout (" .. string.format("%.1f",iEng1_N1) .. "%_N1 and " .. string.format("%.1f",iGS) .. "kts_GS)")
schedule_sound("flexSet.wav")
return
end
if (sThrustLevers == "TOGA") and (iEng1_N1 >= 80.0) and not blockToGaSet then -- "toga set" callout
blockToGaSet, blockFlexSet = true, true
blockGo, blockStandUp = true, true
if (iOnGround == 1) then
blockLiftoff, blockRotate = false, false
end
log("'toga set' callout (" .. string.format("%.1f",iEng1_N1) .. "%_N1 and " .. string.format("%.1f",iGS) .. "kts_GS and " .. string.format("%.1f",iIAS) .. "kts_IAS)")
schedule_sound("togaSet.wav")
return
end
if not blockClbSet then -- "climb set" callout
blockClbSet = true
log(string.format("'climb set' callout (%.0f kts_IAS and %.0f ft_ALT and %.0f ft/min_VS)", iIAS, iALT, iVertSpeed))
schedule_sound("climbSet.wav")
return
end
if not blockPosClimb then -- "positive climb" callout
blockPosClimb = true
log(string.format("'positive climb' callout (%.0f kts_IAS and %.0f ft_ALT and %.0f ft/min_VS)", iIAS, iALT, iVertSpeed))
schedule_sound("positiveClimb.wav")
return
end
if not blockStandUp and (iPitch <= 0.0) then -- "stand up" callout
blockStandUp = true
blockGo = false
log("'stand up' callout (" .. string.format("%.0f",iGS) .. "kts_GS and " .. string.format("%.0f",iIAS) .. "kts_IAS and " .. string.format("%.1f",iPitch) .. "deg_Pitch and " .. string.format("%.2f",iTHSval) .. "THS)")
schedule_sound("standUp.wav")
return
end
if not ((sThrustLevers == "IDLE") or (sThrustLevers == "REV")) and (math.abs(iTHSval) < 0.8) and (iFlapLever < iFlapLandingCfg) and not blockGo then -- "Go" callout
blockGo, blockStandUp = true, true
blockToGaSet = false
log("'go' callout (" .. string.format("%.0f",iGS) .. "kts_GS and " .. string.format("%.0f",iIAS) .. "kts_IAS and " .. string.format("%.1f",iPitch) .. "deg_Pitch and " .. string.format("%.2f",iTHSval) .. "THS and Flaps='" .. iFlapLever .. "')")
schedule_sound("go.wav")
return
end
end
--
-- ######### copied 100kts call-out for 80kts:
if (iGS >= 80.0) and not block80kts then -- "80kts" callout
block80kts = true
log(string.format("'80kts' callout (%.1f %%_N1 and %.1f kts_IAS)", iEng1_N1, iIAS))
schedule_sound("80knots.wav")
return
end
if (iIAS >= 100.0) and not block100kts then -- "100kts" callout
block100kts = true
log(string.format("'100kts' callout (%.1f %%_N1 and %.1f kts_IAS)", iEng1_N1, iIAS))
schedule_sound("100knots.wav")
return
end
if (bPlayV1 == 1)and (iIAS >= iV1Select) and not blockV1 then -- "V1" callout
blockV1 = true
log(string.format("'V1' callout (%.1f %%_N1 and %.1f kts_IAS)", iEng1_N1, iIAS))
schedule_sound("v1.wav")
return
end
if not blockStop then -- "stop" callout
blockStop = true
log("'stop' callout (" .. string.format("%.1f",iGS) .. "kts_GS and ".. string.format("%.1f",iIAS) .. "kts_IAS)")
schedule_sound("stop.wav")
return
end
if (iIAS >= iVrSelect) and not blockRotate then -- "rotate" callout
blockRotate = true
log(string.format("'rotate' callout (%.1f kts_IAS)", iIAS))
schedule_sound("rotate.wav")
return
end
if (iOnGround == 0) and not blockLiftoff then -- log liftoff
blockLiftoff = true
log("> liftoff! < (" .. string.format("%.1f",iEng1_N1) .. "%_N1 and " .. string.format("%.1f",iIAS) .. "kts_IAS and Flaps='" .. iFlapLever .. "')")
end
if (iOnGround == 1) and not blockTouchdown then -- log touchdown
blockTouchdown = true
log(string.format("> touchdown! < (%.0f kts_GS and %.0f kts_IAS and %.0f ft/min_VS and %.1f deg_Pitch and Flaps='%d')", iGS, iIAS, iVertSpeed, iPitch, iFlapLever))
decel_ts = now + 7 * 1000 -- allow Rev Green before decel
end
if (iGNDSpoilersUp == 1) and not blockSpoilers then -- "spoilers" callout
blockSpoilers = true
log(string.format("'spoilers' callout (%.1f kts_GS and %.1f ft/sec2_accel)", iGS, iAccelLateral))
schedule_sound("spoilers.wav")
return
end
if (sThrustLevers == "REV") and not blockRevers then -- "reverse green" callout
blockSpoilers, bCommitedToStop = true, true
v = XPLMGetDatavi(dr.reversers, 0, 1)
if v[0] == 2 then -- 2 = fully deployed, take rev 1 for both
blockRevers = true
decel_ts = now
log(string.format("'reverse green' callout (%.1f kts_GS and %.1f ft/sec2_accel)", iGS, iAccelLateral))
schedule_sound("reverseGreen.wav")
return
end
end
if (iMaxDecel >= 0.75 * 5.0) and (iOnGround == 1) and not blockDecel and (now > decel_ts) then -- "decel" callout
blockDecel, blockRevers, blockSpoilers = true, true, true
bCommitedToStop = true
log(string.format("'decel' callout (%.1f kts_GS and %.1f ft/sec2_accel)", iGS, iAccelLateral))
schedule_sound("decel.wav")
return
end
if (iGS <= 70) and not block70kts then -- "70kts" callout
block70kts, blockDecel, blockRevers, blockSpoilers = true, true, true, true
bCommitedToStop = true
bMeasureDecel = false -- stop measuring
log(string.format("'70kts' callout (%.1f kts_GS and %.1f ft/sec2_accel, [%.1f max])", iGS, iAccelLateral, iMaxDecel))
schedule_sound("70knots.wav")
return
end
end -- end of callout function
function log(str) -- custom log function
local temp = os.date("*t", os.time())
print(string.format("PMCO: %02d:%02d:%02d %s", temp.hour, temp.min, temp.sec, str))
end
function schedule_sound(wav)
local sr = sounds[wav]
wait_until_sound = now + sr.pre
sound_to_play = sr.si
wait_after_sound = now + sr.post
end
log(Version .. " - ToLiss detected")
-- ##### start of plugin code #####
do_every_frame("PMCO_callouts()") -- run the callouts function
end -- ToLiss only