Thread Tools
This thread is privately moderated by vulturetec, who may elect to delete unwanted replies.
Mar 04, 2018, 10:30 PM
ʇsol
vulturetec's Avatar
Discussion

Flight-Time logger for OpenTX/Taranis


This is a simple LUA script I wrote to help keep track of the number of flights and flight time on individual models. The logging data is stored in GVARs and therefore will remain intact as long as the GVARs 8 & 9 (on all flight modes) are not changed. The logger data can be backed up along with the model setup using companion.

The script is intended for fixed wing aircraft, primarily gliders. Flights are automatically counted when the aircraft is above a specified height above the ground for a length of time. The flight ends and the flight time calculated when the aircraft drops below that same height. Alternatively a toggle switch can be used to manually start and stop flights.

Requirements:
  • Originally written for use with OpenTX 2.2.1 on a FrSky Taranis X9D+ it may work on other platforms but has not been tested.
  • For fully automatic operation the script relies on telemetry data from a variometer (VARI-N, VARI-H, or G-RX8) with the sensor's altitude reset before takeoff. In some situations the sensor calibration may cause it to immediately show very high readings at power-up, if that happens the telemetry should be reset immediately or false flight times will be recorded.
  • Manual operation can be configured that will use a toggle switch input instead of altitude.

Limitations:

The flight times currently cannot exceed 1024 hours. This may be revised in future versions. However, the flight counter is not restricted to 1024 flights (it has been tested to over 1,000,000 flights)


Configuration:
The script is loaded on to the SD card into the SCRIPTS/TELEMETRY directory, then a telemetry page needs to be configured to use it.

The first several lines of the script contain variables that can be configured. Multiple configurations are possible by saving the script as a different name, then loading the customized script into individual models as needed.
  • height_trigger = [height] : The height above ground that triggers a valid flight. The value is in the same units that the telemetry reports. For example, if the telemetry is configured to reports and display in feet then set the height trigger in terms of feet.
  • flight_initiate_time = [seconds] : The number of seconds the model must be at or above the "height_trigger" for the flight to start. This should be set to at least several seconds. In manual-trigger mode this specifies how long the toggle must be "on" before the flight is considered "started".
  • flight_terminate_time = [seconds] : The number of seconds the model must be below the "height_trigger" for the flight to end. Like the initiate time, this should be set to at least several seconds. In manual-trigger mode this specifies how long the toggle must be "off" before the flight is considered "ended".
  • display_average_time = [true/false] : The average flight time per flight can be displayed alongside the daily totals. Set this value to "true" to show the average times, "false" to surpress the average time displays.
  • force_manual = [true/false] : Forces the script to use a toggle to manually start and end flights. TRUE uses a toggle (mt_switch must be configured), FALSE uses altitude data. This option is intended to be used with a normal two-position toggle (switch "SF" on the Taranis, for example)
  • mt_switch = "[switch name]" : The toggle switch used when 'force_manual' is on. Example: to use switch SF this line would read mt_switch = "sf"


Log display:


Last Flight: The time of the last flight flown. This line will reset to zeros after powering off the transmitter.
Today: The number of flights and time flown on the current day. The averages show the average minutes/seconds flown.
Last Day: The number of flights and time flown on the last day flights were logged.
Total: The total number of flights and flight time logged.

This is the same data with the average times turned off:


Script:

Note: Much of the embedded comments/documentation lines will by word-wrapped by the forum software. Copying the text and inserting them into a text editor should retain the correct formatting of the code - or just download the zipped .lua.

Code:
-- Flight Logger beta 1.3
-- Simple flight-logger for OpenTX 221 Taranis X9D+
--
-- Mar-2018 - S.Keating www.scottkeating.net (vulturetec on rcg)
-- 
-- Configuration
local height_trigger = 30		-- height that triggers/terminates a flight (in the units the telemetry is set for)
local flight_initiate_time = 6	-- seconds required above the trigger height to initiate a flight
local flight_terminate_time = 7	-- seconds required below the trigger height to terminate a flight
local display_average_time = true	-- toggles the display the average time in decimal minutes
local force_manual = false		-- toggles triggering flights with a manual switch
local mt_switch = "sf"			-- manual switch to use if "force_manual" is toggled on

local altitude				-- altitude 
local inflight = false		-- in-flight flag
local inflight_start = 0 	-- in-flight start timer
local inflight_end = 0		-- in-flight end-timer
local flight_count  		-- flight counter
local flightime_start = 0	-- flight time start-time
local flighttime_end = 0	-- flight time end-timer
local flighttime_total		-- flight time total
local flight_time_current = {}	-- current flight time HMS
local flight_time_today = {}	-- today's flight time HMS
local flight_total_time = {}	-- total flight time HMS
local flight_time_last = {}		-- last day's flight time HMS
local atime = {}				-- used for average time calculations
local flight_count_today = 0	-- number of flights today
local flight_count_total = 0	-- total number of flights
local flight_count_last = 0		-- last day's flights

local last_Y = 42			-- row and column positions
local current_Y = 21
local today_Y = 32
local totals_Y = 52
local title_Y = 11
local flights_X = 78
local times_X = 134

local function round(num, numDecimalPlaces)
-- round the value num to the specified decimal places
  local mult = 10^(numDecimalPlaces or 0)
  return math.floor(num * mult + 0.5) / mult
end

local function xbias(xval,fc)
-- bias the x-position to try centering the numbers a little
	local b=0
	if fc>9 then
		if fc>99 then
			b=6
		else
			b=3
		end
	end
	
	return (xval-b)
end



local function calc_flight_totals(nsets,fc)
-- calculate flight count based on the nset and fc registers

	if nsets<0 then 
		nsets = 0
	end
	
	fc = fc + (nsets * 1024)
	
	return fc
end

local function creatett(tt,ttarray,rsarray)
-- tt = time value in seconds*100
-- ttarray = 3-element array representing time in h/m/s
-- rsarray = true/false; true=reset array times to zeros

	local fls
	local hh
	local hh1
	local mm
	local mm1
	local ss
	
	if rsarray then
		ttarray[1]=0
		ttarray[2]=0
		ttarray[3]=0
	end
	
	local fltot = math.floor(tt / 100)
	hh, mm = math.modf(fltot / 3600)
	mm = mm * 60
	mm, ss = math.modf((mm * 60) / 60)
	
	ss = (ss * 60) + ttarray[3]
	mm1, ss = math.modf(ss / 60)
	mm = mm + mm1 + ttarray[2]
	hh1, mm = math.modf(mm / 60)
	hh = hh + hh1 + ttarray[1]
	
	ttarray[1] = hh
	ttarray[2] = (mm * 60)
	ttarray[3] = (ss * 60)

end

local function avgtime(timearray,counts)
-- calculate the average flight time in minutes from the time in "timearray" by "counts"
	local avg = 0
	local avgst = "0:00"
	local mmtime = 0
	atime[1]=0
	atime[2]=0
	atime[3]=0
	
	if counts > 0 then
		avg = (((timearray[1]*3600) + (timearray[2]*60) + timearray[3])/counts)*100
		creatett(avg,atime,true)
		mmtime = (atime[1]*60) + atime[2]	
		avgst = mmtime .. ":" .. string.format("%02d",  atime[3])
	end
	
	return avgst
end

local function show_time_string(ftarray)
	local fls
	 fls = string.format("%02d", ftarray[1]) .. ":"
	 fls = fls .. string.format("%02d", ftarray[2]) .. ":"
	 fls = fls .. string.format("%02d",  ftarray[3])
	 
	 return fls
end

local function compdates()
-- Compare today's date against the date in memory, matched returns true
-- If the stored DAY is 0 then we force the date comparison to TRUE, which will happen
-- if the gvars for "today's" data is wiped for some reason. In that case the "last day's"
-- data would be unintentionally reset to zeros

	local datenow = getDateTime()
	local mm = model.getGlobalVariable(8,7)
	local dd = model.getGlobalVariable(8,8)
	local retval
	
	if mm == datenow.mon and dd == datenow.day then
			retval = true
	elseif dd == 0 then									
			retval = true
	else
			retval = false
	end
	
	return retval
end

local function set_gvar_today()
-- store today's date in the GVAR 9, FM7 and FM8
	local datenow = getDateTime()
	model.setGlobalVariable(8,7,datenow.mon)
	model.setGlobalVariable(8,8,datenow.day)
	
end

local function set_gvar_timetoday()
-- store today's flight times in the GVAR 9, FM2, FM3, and FM4
	model.setGlobalVariable(8,2,flight_time_today[1])
	model.setGlobalVariable(8,3,flight_time_today[2])
	model.setGlobalVariable(8,4,flight_time_today[3])
	set_gvar_today()				-- set today's date in memory
end

local function set_gvar_timelast()
-- store today's flight times in the GVAR 8, FM5, FM6, and FM7
	model.setGlobalVariable(7,5,flight_time_last[1])
	model.setGlobalVariable(7,6,flight_time_last[2])
	model.setGlobalVariable(7,7,flight_time_last[3])
end

local function set_gvar_totaltime()
-- store the total flight time in the global variables (GVARs)
	model.setGlobalVariable(7,2,flight_total_time[1])
	model.setGlobalVariable(7,3,flight_total_time[2])
	model.setGlobalVariable(7,4,flight_total_time[3])
end

local function set_gvar_resettoday()
-- reset today's totals (configures a new day)

	-- swap the last day's data to "last"
	model.setGlobalVariable(8,5,model.getGlobalVariable(8,0))
	model.setGlobalVariable(8,6,model.getGlobalVariable(8,1))
	model.setGlobalVariable(7,5,model.getGlobalVariable(8,2))
	model.setGlobalVariable(7,6,model.getGlobalVariable(8,3))
	model.setGlobalVariable(7,7,model.getGlobalVariable(8,4))
	
	
	-- configure a new day 
	-- set_gvar_today()				-- set today's date in memory
	model.setGlobalVariable(8,7,0)	-- set the date in memory to "0"
	model.setGlobalVariable(8,8,0)
	model.setGlobalVariable(8,2,0)	-- set the flight time totals to 0
	model.setGlobalVariable(8,3,0)
	model.setGlobalVariable(8,4,0)
	model.setGlobalVariable(8,0,0)	-- set the flight counters to 0
	model.setGlobalVariable(8,1,0)
	
end

local function set_gvar_totalflights()
-- store the total flight count in the global variables (GVARs)
-- the flight count is stored in GV8 FM0 and FM1

	local nsets = math.floor(flight_count / 1024)
	local fc = flight_count - (1024 * nsets)
	
	model.setGlobalVariable(7,0,nsets )
	model.setGlobalVariable(7,1,flight_count)
	
end

local function set_gvar_flightslast()
-- store the flight count for the last day in the global variables (GVARs)
-- the flight count is stored in GV9 FM0 and FM1

	local nsets = math.floor(flight_count_last / 1024)
	local fc = flight_count_last - (1024 * nsets)
	
	model.setGlobalVariable(8,5,nsets )
	model.setGlobalVariable(8,6,flight_count_last)
	
end

local function set_gvar_flightstoday()
-- store the total flight count in the global variables (GVARs)
-- the flight count is stored in GV9 FM0 and FM1

	local nsets = math.floor(flight_count_today / 1024)
	local fc = flight_count_today - (1024 * nsets)
	
	model.setGlobalVariable(8,0,nsets )
	model.setGlobalVariable(8,1,flight_count_today)
	
end

local function get_gvar_totaltime()
-- retrieve the total flight time from the global variables (GVARs)
	flight_total_time[1] = model.getGlobalVariable(7,2)
	flight_total_time[2] = model.getGlobalVariable(7,3)
	flight_total_time[3] = model.getGlobalVariable(7,4)
end

local function get_gvar_timelast()
-- retrieve the last day flight time from the global variables (GVARs)
	flight_time_last[1] = model.getGlobalVariable(7,5)
	flight_time_last[2] = model.getGlobalVariable(7,6)
	flight_time_last[3] = model.getGlobalVariable(7,7)
end

local function get_gvar_timetoday()
-- get today's total time from the GVARs
	flight_time_today[1] = model.getGlobalVariable(8,2)
	flight_time_today[2] = model.getGlobalVariable(8,3)
	flight_time_today[3] = model.getGlobalVariable(8,4)
end

local function get_gvar_flightslast()
-- retrieve the total flights from the last day from the GVARs

	flight_count_last = calc_flight_totals(model.getGlobalVariable(8,5),model.getGlobalVariable(8,6))
	
end

local function get_gvar_flightstoday()
-- retrieve the total flights today from the GVARs

	flight_count_today = calc_flight_totals(model.getGlobalVariable(8,0),model.getGlobalVariable(8,1))
	
end

local function get_gvar_totalflights()
-- retrieve the total flight count from the global variables (GVARs)
-- the flight count is stored in GV8 FM0 and FM1

	flight_count = calc_flight_totals(model.getGlobalVariable(7,0),model.getGlobalVariable(7,1))
		
end

local function manual_trigger()
-- manual flight-trigger toggle check
	local sp = 0
	
	if force_manual then
		if getValue(mt_switch)>0 then
			sp = 2
		else	
			sp = 1
		end
	end
	
	return sp
end

local function start_flight_trigger(altitude)
-- check for inflight-start status
	local sft = false
	
	if manual_trigger() == 0 and altitude>height_trigger then
		sft = true
	elseif manual_trigger() == 2 then
		sft = true
	end
	
	return sft
end

local function end_flight_trigger(altitude)
-- check for inflight-end status
	local eft = false
	
	if manual_trigger() == 0 and altitude<height_trigger then
		eft = true
	elseif manual_trigger() == 1 then
		eft = true
	end
	
	return eft
	
end

local function bg()
	local timediff
	
	altitude = getValue('Alt')
	
-- new-flight start-check
	if not inflight then									-- If not already in flight then check for flight-start triggers: new-flights are counted when above height_trigger for at least the specified time
		if start_flight_trigger(altitude) then					-- If the model is above the height_trigger then
			if inflight_start == 0 then						-- set the time the trigger was first detected
				inflight_start = getTime()	
				flighttime_start = inflight_start			
			else
				if getTime() - inflight_start > flight_initiate_time then 	-- If the start-time has already been noted, then if the model has been above the trigger height long enough count it as a flight
					inflight = true											-- And set "inflight" to true, noting that the model is currently in "a flight"
					flight_count = flight_count + 1			
					flight_count_today = flight_count_today + 1
					inflight_end = 0	
				end
			end
		else
			inflight_start = 0								-- Any time the altitude is below the trigger_height while not in "a flight" then reset the in-flight start time to 0
		end
	end
	
-- end-of-flight check
	if inflight then										-- If currently in a "flight", check for flight termination
		if end_flight_trigger(altitude) then					-- a flight terminates when the model drops below the trigger height
			if inflight_end == 0 then						-- and stays below that height for a specified time
				inflight_end = getTime()
				flighttime_end = inflight_end
			else
				if getTime() - inflight_end > flight_terminate_time  then	-- once the specified time elapses,
					inflight = false										-- terminate the flight officially
					
					-- Calculate the actual flight-time
					timediff = flighttime_end - flighttime_start			-- calculate the length of the flight in seconds
					creatett(timediff,flight_time_current,true)					-- load the current flight time (HMS)
					creatett(timediff,flight_total_time,false)					-- load the total flight time (HMS)
					creatett(timediff,flight_time_today,false)					-- load today's flight time (HMS)
										
					-- Store the totals in the GVARs
					set_gvar_totalflights()				
					set_gvar_totaltime()
					set_gvar_flightstoday()
					set_gvar_timetoday()
					
					-- reset variables for the next flight 
					inflight_start = 0						
					inflight_end = 0
					flighttime_end = 0
					flighttime_start = 0
					
				end
			end
		else	
			inflight_end = 0	
			
		end
	end
	
end

local function show_current_flighttime()
-- Display the time for the last-flown flight
	if not inflight then
		lcd.drawText(times_X - 16,current_Y,show_time_string(flight_time_current),0)
	else
		lcd.drawText(times_X - 16,current_Y,show_time_string(flight_time_current),INVERS + 0)
	end
end

local function show_todays_times()
-- Display the total time flown today
	local flc=0

	lcd.drawText(times_X - 16,today_Y, show_time_string(flight_time_today) , 0)
		
		if display_average_time then
			if inflight then
				if flight_count_today > 1 then
					flc = flight_count_today -1
				end	
			else
				flc = flight_count_today
			end
		
			lcd.drawText(lcd.getLastPos(),today_Y+1," " .. avgtime(flight_time_today,flc) .. " avg", SMLSIZE)	
		
		end
	
end

local function show_last_times()
-- Show the last flight day's time
		lcd.drawText(times_X - 16,last_Y, show_time_string(flight_time_last), 0)

		if display_average_time then
			lcd.drawText(lcd.getLastPos(),last_Y+1," " .. avgtime(flight_time_last,flight_count_last) .. " avg", SMLSIZE)
		end
		
end

local function show_total_flighttime()
-- Show the total flight time
		lcd.drawText(times_X - 16,totals_Y, show_time_string(flight_total_time), 0)
end

local function show_flights()
-- total flights
	lcd.drawText(xbias(flights_X,flight_count), totals_Y, flight_count, 0)	
end


local function show_todays_flights()
-- Display the number of flights today
	lcd.drawText(xbias(flights_X,flight_count_today), today_Y, flight_count_today, 0)
end

local function show_last_flights()
-- Display the number of flights from the last flight day
	lcd.drawText(xbias(flights_X,flight_count_last), last_Y, flight_count_last, 0)
end

local function show_headers()

	local minfo = model.getInfo()
	local mname = minfo.name
	local hdrline = mname .. " Flight Log"
	local sp = 18 - (math.floor(string.len(hdrline)/2))
	local rp
	local lp
	
	-- TOP TITLE
	lcd.drawText(1,1,string.rep(" ",(sp*2)),0)
	lp = lcd.getLastRightPos()
	lcd.drawText(lp,1,hdrline, 0)
	rp = lcd.getLastRightPos()
	lcd.drawLine(rp-1,9,lp,9,SOLID,FORCE)
		
	lcd.drawText(flights_X - 11,title_Y,"Flights",0) -- Total Flights TITLE
	lcd.drawText(times_X - 11,title_Y,"Time",0)		 -- Total time TITLE
	
	-- Row headers
	if not inflight then
		lcd.drawText(1,current_Y,"Last flight",SMLSIZE)
	else
		lcd.drawText(1,current_Y,"Last flight",INVERS + SMLSIZE)
	end
	
	lcd.drawText(1,today_Y,"         Today",SMLSIZE)
    lcd.drawText(1,totals_Y,"         Total",SMLSIZE)
	lcd.drawText(1,last_Y,"    Last Day",SMLSIZE)
end

local function configuretoday()
-- configure the setup for today
	if not compdates() then		-- if the stored date isn't today then do...
		set_gvar_resettoday()
	end
	
	get_gvar_flightstoday()		-- set today's flight counter
	get_gvar_timetoday()		-- set today's times
	
end

local function checkinit()

	if model.getGlobalVariable(7,4)==1025 and model.getGlobalVariable(7,3)==1025 and model.getGlobalVariable(7,4)==1025 then
		for a = 7,8 do
			for b = 0,8 do
				model.setGlobalVariable(a,b,0)
			end
		end
	end

end

local function init()
-- init function
	checkinit()
	flight_time_current[1]=0
	flight_time_current[2]=0
	flight_time_current[3]=0
	atime[1]=0
	atime[2]=0
	atime[3]=0
	flight_initiate_time = (flight_initiate_time * 100) - 1
	flight_terminate_time = (flight_terminate_time * 100) - 1
	get_gvar_totalflights()
	get_gvar_totaltime()
	configuretoday()
	get_gvar_timelast()
	get_gvar_flightslast()
	lcd.clear()
end

local function run(event)	
	lcd.clear()
	show_headers()
	show_flights()
	show_current_flighttime()
	show_total_flighttime()
	show_todays_flights()
	show_todays_times()
	show_last_flights()
	show_last_times()
end

return { run=run, background=bg, init=init}
Latest blog entry: Sagitta 600 build 2018
Sign up now
to remove ads between posts
Mar 11, 2018, 11:33 AM
Pompano Hill Flyers
Miami Mike's Avatar
Quote:
Originally Posted by vulturetec
This is a simple LUA script I wrote...
Nice job! I'm sure people are wondering what one of your more complex scripts would look like.
Quote:
Note: Much of the embedded comments/documentation lines will be word-wrapped by the forum software. Copying the text and inserting them into a text editor should retain the correct formatting of the code...
A good way to handle that is to use block comments, which are comments beginning with an opening level zero long bracket ("--[[") and ending with a corresponding closing long bracket ("]]"). The following entire block can be pasted into a script with no effect:
Code:
--[[ This is a comment in a Lua script.
It doesn't matter how many lines the comment has,
all of this text will be ignored until the closing
long bracket appears, which looks like this: ]]

--[[
Or if desired it can be done
like this, with the brackets
occupying lines of their own.
]]

--[[
If you like, you can even make the closing
long bracket a mirror image of the
opening bracket, like this:
]]--

--[[
... but keep in mind that if you do that, any text
after the closing bracket on the same line will
also be ignored. In other words...
]]-- this text will also be ignored.
Latest blog entry: Shepard Tone Vario Update
Mar 11, 2018, 04:26 PM
ʇsol
vulturetec's Avatar
I didn't know about the brackets, thanks for that, Mike!

Here's my log screen after FSS #2 today.



I had one really horrid 1:37 round, and one in the 4:30 range. With my logger triggering at 30ft it adds some time at launch and drops some landing - but the automatically logged results are still pretty close.
Latest blog entry: Sagitta 600 build 2018
Oct 17, 2018, 08:26 PM
W.F.G.D
jbitz's Avatar
Vulturetec.

You mentioned "altitude reset" on the G-RX8, I have searched all over for a "how to" on that very item. Can you point me in the right direction.

John
Oct 19, 2018, 01:39 PM
ʇsol
vulturetec's Avatar
John - I just answered in the other thread... but I'll add here, the altitude/vario are reset/zeroed by holding the enter key for a couple of seconds, then select "reset", then "reset telemetry". This works to zero the GPS distance calculation too.
Latest blog entry: Sagitta 600 build 2018


Quick Reply
Message:
Thread Tools

Similar Threads
Category Thread Thread Starter Forum Replies Last Post
Discussion FrSky Taranis, Taranis Plus, Taranis E - OpenTX 2.1.X Jet_Flyer Radios 7190 Oct 26, 2018 12:37 PM
Discussion FrSky - We need a Taranis for Surface Radios with OpenTx! Thorvald Radios 20 Aug 04, 2018 12:05 PM
Discussion Migrate models from Taranis (OpenTx 1.?) to Taranis Plus (OpenTx 2.2.0) Mr Rowl FrSky 1 Aug 23, 2017 08:33 AM
FAQ Resources for FrSky Taranis and OpenTX scott page FrSky 28 Mar 15, 2014 06:00 PM