-- "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 (modified for FF A350) if PLANE_ICAO == "A319" or PLANE_ICAO == "A20N" or PLANE_ICAO == "A321" or PLANE_ICAO == "A21N" or PLANE_ICAO == "A339" or PLANE_ICAO == "A359" then --if PLANE_ICAO == "A319" or PLANE_ICAO == "A321" or PLANE_ICAO == "A21N" or PLANE_ICAO == "A346" 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" -- need Volume 0.4 local Volume = 0.3 -- volume of callouts --local sound_set = "CyRanny" -- needs Volume 1.0 --local Volume = 1.0 -- volume of callouts -- ################################################## -- ##### 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) // v1.6 moved to user-editable section above 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"), -- VS dataref for FF A350 1.7.0 now same as ToLiss, airbus_qpac/* no longer used VS = XPLMFindDataRef("toliss_airbus/pfdoutputs/captain/vertical_speed") } -- 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.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.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 dataref %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