--[[ Miami Mike's Glider Simulator for the FrSky Taranis X9D ]]-- --[[ FOR TARANIS X9D, X9D+ and X9E: Add this file to /SCRIPTS/TELEMETRY/ and set it up as a telemetry screen. Flap offset is initialized according to the VRIGHT flap position at startup. To adjust flap offset set radio controls for flap neutral and press PLUS (+). To switch between cross-tail and v-tail, Press MINUS (-). For more information, see https://www.rcgroups.com/forums/member.php?u=15789 --]] --[[ FOR HORUS X10, X10S and X12S: Add this file to /SCRIPTS/TELEMETRY/ and run it as a one-time script from the SDcard browser. --]] --[[ code changes from L' Shems, rcgroups: Added single aileron visualisations when flaps are set to the same channels as the ailerons. added Y-cable support left aileron inversion for ailerons when both ailerons are set to the same channel added colors to visualise up and down surface movements better drawn all surfaces with lines, and no more line per line surface drawing to avoid CPU-limit on Horus added channel order lookup table for easy changing of channnel order added throws to visualise alignement with different geometrical flaps and ailerons added offset for ailerons added mm visualisation as option instead of % added zoom mode to visualise trims (set flaps to zero, and reinitialise the offsets) --zoom mode is left as soon as throws exceed visual limits added option passthrough possibility for channel order, throws, and shown units (mm or %) added example file to load with your model settings. properly scaled control surfaces, to draw only within window (prevent crash on Horus and Taranis) some dummy functions and values are there in the LCD table. These are needed for interoperability with the Appman and widget support from www.justfly.solutions. They can be ommited if they bother you. --]] local GREY = GREY or function() return 0 end --create non-existing function GREY for X7 RIGHT = RIGHT or 0 -- creating global RIGHT if not existing (OpenTX 2.0) --localisations neccesary to rescale drawing functions to fit in Horus and X7 LCD displays if LCD_W ~= 212 and not LCD then FORCE = FORCE or 0 --FORCE used on taranis to define gray pixel mixing behaviour --FORCE is no mixing, standard mode on color display ERASE = ERASE or 0 --doesn't exist on Horus local LCD = {} --table to contain scaled lcd functions replacing the global lcd functions LCD.scale = {x = LCD_W/212, y=LCD_H/64} if LCD_W == 480 then --on Horus, replace exiting clearfunction LCD.clear = function() lcd.drawFilledRectangle(0,0,LCD_W,LCD_H,MAINVIEW_PANES_COLOR ) end else LCD.clear = lcd.clear end LCD.drawText = function(x,y,t,f) lcd.drawText(LCD.scale.x*x,LCD.scale.y*y,t,f) end LCD.drawNumber = function(x,y,n,f) lcd.drawNumber(LCD.scale.x*x,LCD.scale.y*y,n,f) end LCD.drawLine = function(x,y,x2,y2,p,f) lcd.drawLine(LCD.scale.x*x,LCD.scale.y*y,LCD.scale.x*x2,LCD.scale.y*y2,p,f) end LCD.drawPoint = function(x,y) lcd.drawPoint(LCD.scale.x*x ,LCD.scale.y*y) end LCD.drawFilledRectangle = function(x,y,w,h,f) lcd.drawFilledRectangle(LCD.scale.x*x,LCD.scale.y*y,LCD.scale.x*w,LCD.scale.y*h,f) end LCD.drawRectangle = function(x,y,w,h,f) lcd.drawRectangle(LCD.scale.x*x,LCD.scale.y*y,LCD.scale.x*w,LCD.scale.y*h,f) end end local lcd = LCD or lcd --replace global lcd functions with the LCD ones local LCD_W= lcd.W or LCD_W --replace global screenwidth with LCD ones local LCD_H = lcd.H or LCD_H --replace global screenhight with LCD ones local LCD=LCD or {} LCD.zone = LCD.zone or {w=LCD_W,h=LCD_H} --settings for determining scale for Appman LCD.size = LCD.size or {w=212,h=64} --settings for determining scale for Appman LCD.scale = LCD.scale or {x = 1,y=1} --setting scale for Appman LCD.set=LCD.set or function() end --make dummy function when not loaded by Appman. local scaleX = LCD.scale.x --store AppMan setting local scaleY = LCD.scale.y --store AppMan setting local options = ... or {} --options are passed in a table, so you can pass 'named' options, and do not have to consider order of options passed. local throws = -- to visualise alignement when having different throws on flaps and ailerons {ailerons=30 -- half the total travel, so throw around offset ,flaps=30 -- half the total travel, so throw around offset ,rudder = 20 ,elevator = 15 } if options.throws then for k,v in pairs(throws) do throws[k]=options.throws[k] or v --only replace existing options end end --[[ The channel assignments below are those used in Mike Shellim's F3J/TD setups, available from http://www.rc-soar.com/opentx/setups/f3j/index.htm Edit the "ch1" - "ch6" if needed to match your model setup. The names are choosen so they fit the OpenTX 2.2 X7 servo naming space. Match them on your model for your own convenience :) ]]-- local servoOrder = {} servoOrder.AilR = "ch1" --right aileron, used as flaperon or for crow servoOrder.AilL = "ch2" --left aileron, used as flaperon or for crow servoOrder.FlpR = "ch3" --right flap, used as flaperon and brakes in combination with crow servoOrder.FlpL = "ch4" --left flap, used as flaperon and brakes in combination with crow servoOrder.Elev = "ch5" --elevator servoOrder.Rudd = "ch6" --rudder servoOrder.VtR = servoOrder.Elev --right V-Tail servoOrder.VtL = servoOrder.Rudd --right V-Tail if options.servoOrder then for k,v in pairs(servoOrder) do servoOrder[k]=options.servoOrder[k] or v --only replace valid passed options end end servoOrder.Mot = options.servoOrder and options.servoOrder.Mot --keep it empty if it is not passed. Optional. local unit = options.unit if unit ~= "mm" then-- show percentage or value (mm). unit = "%" -- unit = "mm" --comment to show % end local zoomFactor = 10 -- zooming in after start, to visualise camber settings and trims local zoom = 1 -- set initial zoom to 1 local NORMAL = 0 if LCD_W == 128 then NORMAL = SMLSIZE end local CLIPPED = NORMAL + INVERS + BLINK local flap_Offset local aileron_Offset local VTAIL = false local VLEFT = false local VRIGHT = not false ---[[ local Draw = { -- glider 006,009,205,009,000,015,211,015,001,014,005,010,206,010,210,014, 103,005,103,008,103,016,103,020,104,001,104,004,104,021,104,025, 105,026,105,039,105,000,106,000,106,026,106,039,107,001,107,004, 107,021,107,025,108,005,108,008,108,016,108,020, } --]] local function Draw_Outline(X1, Y1, X2, Y2) lcd.drawLine(X1, Y1, X2, Y2, SOLID, ERASE + (TEXT_INVERTED_BGCOLOR or 0)) lcd.drawPoint(X1, Y1) lcd.drawPoint(X2, Y2) end local function Draw_Rectangle(X1, X2, Y, Deflection) local Y1=Y local attribute = (MAINVIEW_PANES_COLOR or 0) + ERASE if Deflection > 0 then Y1=Y1 - Deflection + 1 attribute = TEXT_INVERTED_BGCOLOR or GREY(5) end --error when drawing rectangle with size less then 1, and make it positive as it is now a rectangle hight Deflection = math.max(1,math.abs(Deflection)) lcd.drawFilledRectangle (X1,Y1,X2-X1+1,Deflection, FORCE + (MAINVIEW_PANES_COLOR or 0) ) --blacken the space lcd.drawFilledRectangle (X1,Y1,X2-X1+1,Deflection, attribute) --to erase it properly lcd.drawRectangle (X1,Y1,X2-X1+1,Deflection, FORCE + (TEXT_INVERTED_BGCOLOR or 0)) -- lcd.drawLine (X1,Y,X2,Y,SOLID, FORCE + (TEXT_INVERTED_BGCOLOR or 0) ) end local function Draw_Ruddervator(side, Deflection) if Deflection ~= 0 then local X1 = side and 107 or 104 local X2 = side and 131 or 80 lcd.drawLine(X1, 47, X1, 47 - Deflection, SOLID, FORCE) lcd.drawLine(X2, 35, X2, 35 - Deflection, SOLID, FORCE) lcd.drawLine(X1, 47-Deflection, X2, 35 - Deflection, SOLID, FORCE) end end local function Get_Data(controlSurface, offset) offset = offset or 0 local Value = getValue(servoOrder[controlSurface]) --lookup the correct channel for this surface local Percentage = math.floor(.5 + Value / 1.024, 1) local Attribute = NORMAL if Value < -1024 then Value = -1024 Attribute = CLIPPED elseif Value > 1024 then Value = 1024 Attribute = CLIPPED end Value = Value - offset if controlSurface ~= "Mot" then if math.abs(Value*zoom)<1024 then --check if we can still zoom in on trims Value = zoom*Value --if so, zoom in else zoom = 1 --if not, set zoom to zero end end if (controlSurface == "AilL") or (controlSurface == "AilR") then Value = Value*throws.ailerons/throws.max --scale the value on the max value, to visualise differences elseif (controlSurface == "FlpL") or (controlSurface == "FlpR") then Value = Value*throws.flaps/throws.max --scale the value on the max value, to visualise differences end local Adjust = (Value < 0) and 0 or 1 local Deflection = (math.floor(1 + (991 + Adjust + Value) / 64) - 16) if (controlSurface == "Elev") then --because of lines and diagonals, we need to limit the deflection --otherwise we still see the rudder beneath. Deflection = (math.floor(1 + (991 + Adjust + 45/100*Value) / 64) - 16) end return Value, Deflection, Percentage, Attribute end ------------------------------------------------------------------------------------ local function init_func() flap_Offset = getValue(servoOrder.FlpR) aileron_Offset = getValue(servoOrder.AilR) zoom = zoomFactor throws.max = math.max (throws.ailerons*(1024+math.abs(aileron_Offset))/1024 ,throws.flaps*(1024+math.abs(flap_Offset))/1024 ) -- the maximum throw to be shown, from zero offset end local function bg_func() end local function run_func(event) LCD.scale.x = LCD.zone.w/212 --calc relative scale for AppMan LCD.scale.y = LCD.zone.h/64 --calc relative scale for AppMan LCD.set(LCD) --set LCD for AppMan if event == (EVT_ROT_RIGHT or EVT_PLUS_FIRST) then init_func() elseif event == (EVT_ROT_LEFT or EVT_MINUS_FIRST) then VTAIL = not VTAIL end lcd.clear() for i = 0, 14 do lcd.drawLine(Draw[4*i+1], Draw[4*i+2], Draw[4*i+3], Draw[4*i+4], SOLID, FORCE) end local Value; local Deflection; local Percentage; local Attribute local checkZoom = 0 -- right aileron Value, Deflection, Percentage,Attribute = Get_Data("AilR",aileron_Offset) if servoOrder.AilR == servoOrder.FlpR then --only ailerons Draw_Rectangle(109, 211, 15, Deflection) --so one big one else Draw_Rectangle(161, 211, 15, Deflection) end if unit == "%" then lcd.drawNumber(193, 1, Percentage, Attribute + PREC1 + RIGHT) else lcd.drawNumber(193, 1, Value/1024*throws.max/zoom*10, Attribute + PREC1 + RIGHT) end lcd.drawText(193, 1, unit, Attribute) checkZoom = math.max(math.abs(Deflection),checkZoom) -- left aileron Value, Deflection, Percentage, Attribute = Get_Data("AilL",aileron_Offset) if servoOrder.AilL == servoOrder.AilR then --Y wired ailerons Value = - Value Deflection = - Deflection Percentage = - Percentage end if servoOrder.AilR == servoOrder.FlpR then --only ailerons Draw_Rectangle(0, 102, 15, Deflection) --so draw one big one else Draw_Rectangle(0, 50, 15, Deflection) end if unit == "%" then lcd.drawNumber(32, 1, Percentage, Attribute + PREC1+ RIGHT) else lcd.drawNumber(32, 1, Value/1024*throws.max/zoom*10, Attribute + PREC1 + RIGHT) end lcd.drawText(32, 1, unit, Attribute) checkZoom = math.max(math.abs(Deflection),checkZoom) -- right flap if servoOrder.AilR ~= servoOrder.FlpR then --seperate ailerons Value, Deflection, Percentage, Attribute = Get_Data("FlpR", flap_Offset) Draw_Rectangle(109, 159, 15, Deflection) if unit == "%" then lcd.drawNumber(141, 1, Percentage, Attribute + PREC1+ RIGHT) else lcd.drawNumber(141, 1, Value/1024*throws.max/zoom*10, Attribute + PREC1 + RIGHT) end lcd.drawText(141, 1, unit, Attribute) checkZoom = math.max(math.abs(Deflection),checkZoom) -- left flap Value, Deflection, Percentage, Attribute = Get_Data("FlpL", flap_Offset) Draw_Rectangle(52, 102, 15, Deflection) if unit == "%" then lcd.drawNumber(84, 1, Percentage, Attribute + PREC1+ RIGHT) else lcd.drawNumber(84, 1, Value/1024*throws.max/zoom*10, Attribute + PREC1 + RIGHT) end lcd.drawText(84, 1, unit, Attribute) checkZoom = math.max(math.abs(Deflection),checkZoom) end if VTAIL then lcd.drawLine(80, 32, 80, 35, SOLID, FORCE) lcd.drawLine(131, 32, 131, 35, SOLID, FORCE) lcd.drawLine(104, 047, 107, 047, SOLID, FORCE) -- right ruddervator Value, Deflection, Percentage, Attribute = Get_Data("VtR") if Deflection < 8 then lcd.drawLine(124, 31, 107, 39, SOLID, FORCE) lcd.drawLine(130, 31, 124, 31, SOLID, FORCE) end lcd.drawLine(130, 36, 107, 047, SOLID, FORCE) Draw_Ruddervator(VRIGHT, Deflection) lcd.drawText(141, 56, "Right V", NORMAL) lcd.drawNumber(206, 56, Percentage, Attribute + PREC1+ RIGHT) lcd.drawText(206, 56, "%", Attribute) checkZoom = math.max(math.abs(Deflection),checkZoom) -- left ruddervator Value, Deflection, Percentage, Attribute = Get_Data("VtL") if Deflection < 8 then lcd.drawLine(87, 31, 104,39, SOLID, FORCE) lcd.drawLine(81, 31, 87,31, SOLID, FORCE) end lcd.drawLine(81,36,104,47, SOLID, FORCE) Draw_Ruddervator(VLEFT, Deflection) lcd.drawText(1, 56, "Left V", NORMAL) lcd.drawNumber(61, 56, Percentage, Attribute + PREC1+ RIGHT) lcd.drawText(61, 56, "%", Attribute) checkZoom = math.max(math.abs(Deflection),checkZoom) else lcd.drawLine(89, 40, 122, 40, SOLID, FORCE) lcd.drawLine(81, 48, 89, 40, SOLID, FORCE) lcd.drawLine(130, 48, 122, 40, SOLID, FORCE) lcd.drawLine(81, 48, 130, 48, SOLID, FORCE) -- Elevat Value, Deflection, Percentage, Attribute = Get_Data("Elev") Draw_Rectangle(81, 130, 48, Deflection) lcd.drawText(1, 56, "Ele", NORMAL) if unit == "%" then lcd.drawNumber(60, 56, Percentage, Attribute + PREC1+ RIGHT) else lcd.drawNumber(60, 56, Value/1024*throws.elevator/zoom *10, Attribute + PREC1+ RIGHT) end lcd.drawText(60, 56, unit, Attribute) checkZoom = math.max(math.abs(Deflection),checkZoom) -- Rudder Value, Deflection, Percentage, Attribute = Get_Data("Rudd") if math.abs(Deflection) > 1 then local Z1 = (Deflection < 0) and 0 or 1 local X1 = 105 + Z1+1 if Deflection < 0 then X1 = 105 + Z1 + Deflection +1 end lcd.drawLine(105 + Z1, 31, 105 + Z1, 55, SOLID, FORCE) lcd.drawLine(105 + Z1 + Deflection, 39, 105 + Z1 + Deflection, 63, SOLID, FORCE) lcd.drawLine(105 + Z1, 31, 105 + Z1 + Deflection, 39, SOLID, FORCE) lcd.drawLine(105 + Z1 , 55, 105 + Z1+ Deflection , 63, SOLID, FORCE) if math.abs(Deflection)>2 then --draw a block to erase the Elevat --lcd.drawFilledRectangle(X1,40,math.abs(Deflection)-1,15,SOLID + (MAINVIEW_PANES_COLOR or 0)) lcd.drawFilledRectangle(X1,40,math.abs(Deflection)-1,15,ERASE + (MAINVIEW_PANES_COLOR or 0)) end else lcd.drawLine(105, 31, 105, 62, SOLID, FORCE) lcd.drawLine(106, 31, 106, 62, SOLID, FORCE) end if unit == "%" then lcd.drawText(141, 56, "Rud", NORMAL) lcd.drawNumber(206, 56, Percentage, Attribute + PREC1+ RIGHT) lcd.drawText(206, 56, unit, Attribute) else lcd.drawText(132, 56, "Rud", NORMAL) lcd.drawNumber(195, 56, Value/1024*throws.rudder/zoom * 10, Attribute + PREC1+ RIGHT) lcd.drawText(195, 56, unit, Attribute) end checkZoom = math.max(math.abs(Deflection),checkZoom) end if servoOrder.Mot then -- upper left Value, Deflection, Percentage, Attribute = Get_Data("Mot") lcd.drawText(1, 30, "Mot", NORMAL) -- You can give each channel a short name. lcd.drawNumber(60, 30, Percentage/10, RIGHT + Attribute) if Percentage <= -100*10 then lcd.drawText(60, 30, "% OFF", Attribute) elseif Percentage <= 0*10 then lcd.drawText(60, 30, "% < HALF", Attribute) elseif Percentage <= 99*10 then lcd.drawText(60, 30, "% > HALF", Attribute) else lcd.drawText(60, 30, "% FULL", Attribute) end end -- Up to three additional channel displays of your choosing are available. --[[ servoOrder[19] = "ch19" -- lower left Value, Deflection, Percentage, Attribute = Get_Data(19) lcd.drawText(1, 41, "19", Normal) lcd.drawNumber(45, 41, Percentage, Attribute + PREC1) lcd.drawText(45, 41, "%", Attribute) ]]-- --[[ servoOrder[10] = "ch10" -- upper VRIGHT Value, Deflection, Percentage, Attribute = Get_Data(10) lcd.drawText(161, 33, "10", Normal) lcd.drawNumber(205, 33, Percentage, Attribute + PREC1) lcd.drawText(205, 33, "%", Attribute) ]]-- --[[ servoOrder[11] = "ch11" -- lower VRIGHT Value, Deflection, Percentage, Attribute = Get_Data(11) lcd.drawText(161, 41, "11", Normal) lcd.drawNumber(205, 41, Percentage, Attribute + PREC1) lcd.drawText(205, 41, "%", Attribute) ]]-- lcd.drawText(206-12*4, 30, "zoom: " .. zoom .. "x",((zoom == 1) and NORMAL or (INVERS+BLINK))) LCD.scale.x = scaleX --return scale for AppMan LCD.scale.y = scaleY --return scale for AppMan return 0 end return { run=run_func, background=bg_func, init=init_func } -- glidsim.lua Version 1.0 for OpenTX 2.1.9, by Mike Naylor, Feb 15, 2017. -- glidsim.lua Version 2.1 for OpenTX 2.1.9, 2.0, 2.2, adapted by L'Shems, Januari, 2018. -- glidsim.lua Version 2.2 for OpenTX 2.1.9, 2.0, 2.2, adapted by L'Shems, March, 2018.