--[[ LUA telemetry script for F5J training Displays the altitude at engine cut-off, 10s after that, and max reached during the flight Displays the remaining work time, remaining engine time and the flight time NB: You probably want your timer 1 set to Countdown: Voice with Minute Call and timer 2 set to Countdown: Voice IMO, this works best if you have the 'Amber' sound packages (search RC Groups). If the engine is used more that 30s or twice in the same flight, the flight is declared invalid (a badflight.wav file can be used to signal this with audio; you have to provide this file yourself) Switch F is used to signal the end of the flight and stop the timers (up) then reset everything (down) Customization (look for these numbers in the code below): (1) CH1 is used for the throttle (2) The value where the throttle is meant to be active (defaults to -1024) (3) Switch F is used to end (up) then reset (down) the flight (4) Language for the sounds (for the texts, change the texts in the drawDashboard() and drawFault() functions) Releases 1.00 Initial revision 1.01 Updated the timer class - this corrects the voice countdown not restarting Mike, ON4MJ --]] -- Customization stuff local THR = getFieldInfo( 'ch1' ).id -- (1) Change 'ch1' to your throttle output local ALT = getFieldInfo( 'altitude' ).id local SF = getFieldInfo( 'sf' ).id -- (3) Change 'sf' to another switch if you want (especially if you swapped it with SH) local THROTTLE_MIN = -1020 -- (2) (normally -1024) local SOUND_PATH = '/SOUNDS/EN/' -- (4) -- -- Timer wrapping -- -- NB: -- countdownBeep integer (none, beep, voice) -- minuteBeep bool -- persistent integer (none, flight, manual reset) -- local function createTimer( timerId, startValue, countdownBeep, minuteBeep, persistent, startIt ) -- Precondition: timerId is either 0 or 1 local id = timerId local timer = model.getTimer( id ) local target = 0 local function getVal() timer.value = model.getTimer( id ).value return timer.value end local function setTarget( t ) target = t end local function start() timer.mode = 1 model.setTimer( id, timer ) end local function stop() timer = model.getTimer( id ) timer.mode = 0 model.setTimer( id, timer ) return timer.value end local function reset() timer.value = timer.start model.setTimer( id, timer ) model.resetTimer( id ) end local function drawImpl( x, y, val, att ) lcd.drawTimer( x, y, val, att ) return val end local function draw( x, y, att ) return drawImpl( x, y, getVal(), att ) end local function drawReverse( x, y, att ) return drawImpl( x, y, target - getVal(), att ) end -- "constructor" if countdownBeep ~= null then timer.countdownBeep = countdownBeep end if minuteBeep ~= null then timer.minuteBeep = minuteBeep end if persistent ~= null then timer.persistent = persistent end if startValue then timer.value = startValue timer.start = startValue target = startValue end timer.mode = startIt and 1 or 0 model.setTimer( id, timer ) return { start = start, stop = stop, reset = reset, draw = draw, drawReverse = drawReverse, getVal = getVal, setTarget = setTarget } end -- The F5J stuff local alt = 0 local alt10 = 0 local max = 0 local state = 1 -- 1=reset; 2=launch; 3=cutoff; 4=glide; 5=landed; 6=disqualify local timer1 = createTimer( 0, 600 ) -- flight time local timer2 = createTimer( 1, 30 ) -- engine time local time3 = 0 local THROTTLE_MIN_HYSTERESIS = THROTTLE_MIN + 4 local function vocalEnabled() return true end local function handleMax() local a = getValue( ALT ) if a > max then max = a end return a end local function disqualify() if vocalEnabled() then playFile( SOUND_PATH .. 'badflight.wav' ) end timer2.stop() timer1.stop() state = 6 end local function checkThrottle() if getValue( THR ) > THROTTLE_MIN_HYSTERESIS then disqualify() end end local function checkReset() if getValue( SF ) < 0 then timer2.stop() timer1.stop() timer2.reset() timer1.reset() time3 = 0 alt = 0 alt10 = 0 max = 0 state = 1 return true end return false end local function checkEnd() return (getValue( SF ) > 0) end local function goToLandedState() timer2.stop() timer1.stop() state = 5 end -- state transition functions local function resetState() -- wait for take-off if getValue( THR ) > THROTTLE_MIN_HYSTERESIS then timer2.start() timer1.start() state = 2 end end local function launchState() -- wait for the motor cut if checkEnd() then goToLandedState() elseif getValue( THR ) <= THROTTLE_MIN then timer2.stop() alt = getValue( ALT ) max = alt if vocalEnabled() then playNumber( alt, 6, 0 ) end state = 3 elseif timer2.getVal() < 0 then disqualify() end end local function cutoffState() local a = handleMax() if checkEnd() then goToLandedState() else -- wait for the 10s end if (time3 - (30 - timer2.getVal())) >= 10 then alt10 = a if vocalEnabled() then playNumber( alt10, 6, 0 ) end state = 4 else checkThrottle() end end end local function glideState() handleMax() -- wait for the end of flight if checkEnd() then goToLandedState() else checkThrottle() end end local function landedState() -- wait for reset checkReset() end local function disqualifyState() -- wait for end if checkEnd() then state = 5 end end local functions = { resetState, launchState, cutoffState, glideState, landedState, disqualifyState } function background() time3 = 600 - timer1.getVal() functions[ state ]() end local function drawDashboard() lcd.drawFilledRectangle( 0, 23, 212, 18, 0 ) lcd.drawText( 4, 4, "Alt cut:", MIDSIZE ) lcd.drawNumber( 64, 4, alt, MIDSIZE+LEFT ) lcd.drawText( lcd.getLastPos(), 4, "m", 0 ) lcd.drawText( 4, 26, "Alt 10s:", MIDSIZE+INVERS ) lcd.drawNumber( 64, 26, alt10, MIDSIZE+LEFT+INVERS ) lcd.drawText( lcd.getLastPos(), 26, "m", INVERS ) lcd.drawText( 4, 48, "Alt max:", MIDSIZE ) lcd.drawNumber( 64, 48, max, MIDSIZE+LEFT ) lcd.drawText( lcd.getLastPos(), 48, "m", 0 ) lcd.drawText( 120, 4, "Work:", MIDSIZE ) timer1.draw( 174, 4, MIDSIZE ) lcd.drawText( 120, 26, "Engine:", MIDSIZE+INVERS ) timer2.draw( 174, 26, MIDSIZE+INVERS ) lcd.drawText( 120, 48, "Flight:", MIDSIZE ) lcd.drawTimer( 174, 48, time3, MIDSIZE ) end local function drawFault() lcd.drawRectangle( 4, 4, 204, 56 ) lcd.drawText( 40, 25, "INVALID FLIGHT", DBLSIZE+BLINK ) end local function run() background() if state < 6 then drawDashboard() else drawFault() end end return {background=background, run=run}