Question [SOLVED] oapi.get_airspeedvector(vi, REFFRAME.LOCAL) returning type nil?

Thunder Chicken

Fine Threads since 2008
Donator
Joined
Mar 22, 2008
Messages
4,367
Reaction score
3,302
Points
138
Location
Massachusetts
I am trying to create a simple airspeed hold autopilot using a Lua MFD script. To do this, I am trying to get the airspeed vector so I can extract the forward velocity as my control target. This is how I am trying to do it:

airspeed_vector = oapi.get_airspeedvector(vi, REFFRAME.LOCAL)

The script runs, but when I try to check the type of airspeed vector using the following code to print to the MFD display:

actionstr = type(airspeed_vector)

it just returns 'nil'

If I read the documentation correctly, oapi.get_airspeedvector should deliver a three element table keyed with x, y, z. I am not sure what I am missing or doing wrong here.

Any pointers? Thanks for your help in advance.
 

dgatsoulis

ele2png user
Donator
Joined
Dec 2, 2009
Messages
1,927
Reaction score
340
Points
98
Location
Sparta
is "vi" the handle of the vessel or is it the interface?. Try oapi.get_airspeedvector(REFFRAME.LOCAL), so it returns the airspeed vector of the focus vessel. If the returned type is "table", the you did not obtain the vessel's handle correctly in your post above.

hobj = oapi.get_objhandle(name) (or hobj = oapi.get_objhandle(idx)) will get you the handle of the vessel, if you provide its name (or its index number).
airspeed_vector = oapi.get_airspeedvector(hobj, REFFRAME.LOCAL) will return a table of the airspeed_vector with x,y and z values.
 

Thunder Chicken

Fine Threads since 2008
Donator
Joined
Mar 22, 2008
Messages
4,367
Reaction score
3,302
Points
138
Location
Massachusetts
is "vi" the handle of the vessel or is it the interface?. Try oapi.get_airspeedvector(REFFRAME.LOCAL), so it returns the airspeed vector of the focus vessel. If the returned type is "table", the you did not obtain the vessel's handle correctly in your post above.

hobj = oapi.get_objhandle(name) (or hobj = oapi.get_objhandle(idx)) will get you the handle of the vessel, if you provide its name (or its index number).
airspeed_vector = oapi.get_airspeedvector(hobj, REFFRAME.LOCAL) will return a table of the airspeed_vector with x,y and z values.

I was using this to get the interface for the vessel from which the MFD was accessed:

vi = vessel.get_focusinterface()

oapi.get_airspeedvector(REFFRAME.LOCAL) does seem to work, but it doesn't return a Vector3, but a table with x,y,z (which is the correct documented behavior IIRC).

I guess my question now is will this cause problems if other vessels are in the simulation? I've just started dabbling with Lua so I'm still low on the learning curve.
 

dgatsoulis

ele2png user
Donator
Joined
Dec 2, 2009
Messages
1,927
Reaction score
340
Points
98
Location
Sparta
Yeah the syntax needs a bit of getting used to it. If you have the vessel interface, you can use the vessel methods instead of the general orbiter API functions. Here is an example:
Code:
--create annotation on screen
note = oapi.create_annotation()
note:set_pos (0.7,0.06,0.9,0.95) --(x_start, y_start, x_end, y_end) 0 is left/top, 1 is right/bottom
note:set_colour ({r=1,g=0.6,b=0.2}) --rgb values 0 to 1. fractions can be used as well.
note:set_size(1)

v = vessel.get_interface("GL-01") --get the interface of the vessel named GL-01 in the scenario

goals = 0 --set a goal to run a loop
while goals < 1 do --loop
    local alt = v:get_altitude(ALTMODE.GROUND) --get the altitude from the ground
    local vel = v:get_airspeedvector(REFFRAME.HORIZON) --get the airspeed vector
    msg = string.format("Alt: %.3f km\nvel_x: %.1f m/s\nvel_y: %.1f m/s\nvel_z: %.1f m/s",alt/1000, vel.x, vel.y, vel.z)
    note:set_text(msg) --set the text on screen and update on every frame
    proc.skip() --important!!! give Orbiter the chance to "talk" to Lua and update the values for the next frame
    if alt > 10e3 then 
        goals = 1  
    end
    if goals > 0 then end --close the loop
end

proc.wait_simdt(5) --wait for 5 seconds
oapi.del_annotation(note) --delete the annotation

This example script above, will run in a loop while there is a vessel named GL-01 in the scenario and it is below 10000 meters in altitude. During that time, it will display on screen the altitude (in km) and the components of the airspeed vector in the local horizon frame (in m/s).
If the altitude exceeds 10000 meters, the loop will end. The on screen annotation will remain for another five seconds and then it will be deleted.

Hope this helps get you started.
:cheers:
 

Thunder Chicken

Fine Threads since 2008
Donator
Joined
Mar 22, 2008
Messages
4,367
Reaction score
3,302
Points
138
Location
Massachusetts
Thanks! My next question was how to run a loop to implement the autopilot, but I think I see how it could be done in the code snippet that you provided.

Is getting the vessel interface necessary/preferred for any reason if I am running the MFD from that vessel?

EDIT: Actually, I have some questions regarding using a boolean flag to toggle the autopilot. Right now I have the button press functions that set airspeed_hold = true or airspeed_hold = false. I am assuming that there has to be some loop that is continuously checking the flag status, and if it is true, will engage the autopilot function, and when it is false, will shut it off. I'm not quite sure where to set up the while loop relative to the function statements.

I am also trying to set the autopilot a couple different ways. I have a HOLD button that simply uses the current airspeed as the autopilot target, and a SET button where I could manually enter an airspeed target in a dialog box. But I am not sure if I can enter values into the MFD.
 
Last edited:

dgatsoulis

ele2png user
Donator
Joined
Dec 2, 2009
Messages
1,927
Reaction score
340
Points
98
Location
Sparta
Thanks! My next question was how to run a loop to implement the autopilot, but I think I see how it could be done in the code snippet that you provided.

Is getting the vessel interface necessary/preferred for any reason if I am running the MFD from that vessel?
If your goal is to set the thrust then you can only do it with the vessel functions in Lua.
v:get_thrusterlevel(ht) or v:get_thrustergrouplevel(type) to get the current thrust of a thruster or thruster group and v:set_thrusterlevel(ht,lvl) or v:set_thrustergrouplevel(type,lvl) to set the thrust.
I hadn't noticed that you wanted to do this from a script MFD, I thought it was a "direct" script.

A few months ago, (with Martin's help) I had posted a small keypress detection .dll, that can check for key-presses in Lua. (Such a feature was missing from Lua ever since it was introduced in Orbiter). If you don't want to write a whole MFD, simply to activate a holdspeed autopilot, you can use that.

If on the other hand you want to control the holdspeed AP through and MFD, please describe how you'd like it to behave.

I'll try to post an example for which ever one you choose (tonight if I have the time, or some time tomorrow at the latest).
 

Thunder Chicken

Fine Threads since 2008
Donator
Joined
Mar 22, 2008
Messages
4,367
Reaction score
3,302
Points
138
Location
Massachusetts
I've put what I have for my Lua script below.

So I have three buttons set up in an Airspeed Hold autopilot to hold (HLD), set (SET), or release (REL) the autopilot.

Pressing HLD should set the airspeed_hold flag to true, and use the airspeed at the time of the keypress as the airspeed target.
Pressing SET should set the airspeed_hold flag to true, and ideally allow some way to manually enter the airspeed target.
Pressing REL should set the airspeed_hold flag to false, and disable the autopilot function.

I currently have the above keypresses in the MFD doing the right thing as far as setting the flag and collecting the target airspeed in the HLD function. What I am missing:
  1. I don't know how to enter a target value into the MFD when SET is pressed (I'm not even sure this is possible). I would need some sort of dialog box.
  2. I don't quite see how to pass the flag status and airspeed target into a loop where I can implement the control logic.
I have already played with the vessel functions to set the thruster levels. The question is how to set up a loop where the auto

Code:
--[[
Name = Airspeed Hold
Script = AirspeedHold.cfg
Key = 0x1E ; 'A'
Persist = vessel
END_PARSE
--]]

-- global reference to vessel associated with MFD (needed?)
vi = vessel.get_focusinterface()
actionstr = ''

-- the list of button labels
btn_label = {'HOL', 'SET','REL'}

-- callback function: button label for button 'bt' (>= 0)
function buttonlabel(bt)
  if bt < 3 then
    return btn_label[bt+1]
  end
  return nil
end

-- callback function: button menu entries
function buttonmenu()
  return btn_menu,3
end

-- callback function: update display
function update(skp)
  ch,cw = skp:get_charsize()
  mfd:set_title(skp,'Airspeed Hold')
  skp:text(cw*5,ch*10,actionstr,#actionstr)
  return true
end

-- respond to button presses
function consumebutton(bt,event)
  if bt==0 then
    if event%PANEL_MOUSE.LBPRESSED == PANEL_MOUSE.LBDOWN then
      button1_func(airspeed_hold, airspeed_target)
    end
    return true
  elseif bt==1 then
    if event%PANEL_MOUSE.LBPRESSED == PANEL_MOUSE.LBDOWN then
      button2_func(airspeed_hold, airspeed_target)
    end
    return true
  elseif bt==2 then
    if event%PANEL_MOUSE.LBPRESSED == PANEL_MOUSE.LBDOWN then
      button3_func(airspeed_hold)
    end
    return true
  else
    -- just for fun, let's trap unassigned buttons as well
    if event%PANEL_MOUSE.LBPRESSED == PANEL_MOUSE.LBDOWN then
      button_inv_func()
    end
    return true
  end
end

-- action for button 1
function button1_func(airspeed_hold, airspeed_target)
  airspeed_hold = true
  actionstr = 'Airspeed hold engaged'
  airspeed_vector = oapi.get_airspeedvector(REFFRAME.LOCAL)
  airspeed_target = airspeed_vector.z
  actionstr = 'Airspeed target: '..string.format("%.1f", airspeed_target)..' m/s'
  mfd:invalidate_display()
end

-- action for button 2
function button2_func(airspeed_hold, airspeed_target)
  airspeed_hold = true
  actionstr = 'Airspeed hold engaged'
  mfd:invalidate_display()
end

-- action for button 3
function button3_func(airspeed_hold)
  airspeed_hold = false
  actionstr = 'Airspeed hold released'
  mfd:invalidate_display()
end

-- dummy action for invalid buttons
function button_inv_func()
  actionstr = "Invalid key press"
  mfd:invalidate_display()
end
 

dgatsoulis

ele2png user
Donator
Joined
Dec 2, 2009
Messages
1,927
Reaction score
340
Points
98
Location
Sparta
Just noticed the edited post #5. So you want both a hold and set speed AP.

But I am not sure if I can enter values into the MFD.
Yes you can with str = proc.wait_input(title). The returned value is a string, but you can convert to a number with hold_vel = tonumber(str).
I'll try to post an example with a "direct" script and then we can see how to convert it to a script MFD.
--------------------------------EDIT------------------------------------

Ok, much clearer now. My MFD scripting is a bit rusty, but I'll give it a shot.
 

Thunder Chicken

Fine Threads since 2008
Donator
Joined
Mar 22, 2008
Messages
4,367
Reaction score
3,302
Points
138
Location
Massachusetts
Yes you can with str = proc.wait_input(title). The returned value is a string, but you can convert to a number with hold_vel = tonumber(str).

I tried first reading it in as a string but the script and Orbiter hangs on this:

airspeed_target_str = proc.wait_input('Enter target airspeed in m/s: ')
 

Thunder Chicken

Fine Threads since 2008
Donator
Joined
Mar 22, 2008
Messages
4,367
Reaction score
3,302
Points
138
Location
Massachusetts
So I am starting to see the parallels to the API functions in the module code, so I need to put the autopilot code in a prestep or poststep instance. I put the following code in my Lua script and, as expected, it set the thrusters to 100% when the autopilot was selected.

Code:
function poststep(simt,simdt,mjd)
    vi:set_thrustergrouplevel(THGROUP.MAIN,1)
end

But when I attempt to simply turn the thruster on and off using the autopilot boolean flag nothing happens:

Code:
function poststep(simt,simdt,mjd)
    if airspeed_hold == true then
        vi:set_thrustergrouplevel(THGROUP.MAIN,1)
    else
        vi:set_thrustergrouplevel(THGROUP.MAIN,0)
    end
end

I'm not sure if that flag is being passed properly.
 

dgatsoulis

ele2png user
Donator
Joined
Dec 2, 2009
Messages
1,927
Reaction score
340
Points
98
Location
Sparta
Here is a "direct" script for a hold airspeed ap example, with keypress detection. Alt-W to engage/disengage the AP. Alt-S to set the speed. I've commented everything, so it should be clear. Unzip in your Orbiter directory and run the "Airspeed_ap_test" scenario. The keystate.dll in the zip file should go in your Orbiter_root directory and it is used to detect the key presses. I don't know if I'll have the time to make an MFD script tomorrow, but I'll probably have time during this next weekend.
 

Attachments

  • airspeed.zip
    7.5 KB · Views: 2

Thunder Chicken

Fine Threads since 2008
Donator
Joined
Mar 22, 2008
Messages
4,367
Reaction score
3,302
Points
138
Location
Massachusetts
I got the following running - control logic is quick and dirty but it works and can be fined tuned.

It still doesn't like the proc.wait_input - causes the script and Orbiter to hang. I am wondering if that is an OS thing (I'm on Linux running Orbiter 2016 on Wine).

Code:
--[[
Name = Airspeed Hold
Script = AirspeedHold.cfg
Key = 0x1E ; 'A'
Persist = vessel
END_PARSE
--]]

-- global reference to vessel associated with MFD
vi = vessel.get_focusinterface()

statusstr = ''
actionstr = ''
airspeed_hold = false

-- the list of button labels
btn_label = {'HOL','SET','REL'}

-- callback function: button label for button 'bt' (>= 0)
function buttonlabel(bt)
  if bt < 3 then
    return btn_label[bt+1]
  end
  return nil
end

-- callback function: button menu entries
function buttonmenu()
  return btn_menu,3
end

-- callback function: update display
function update(skp)
  ch,cw = skp:get_charsize()
  mfd:set_title(skp,'Airspeed Hold')
  skp:text(cw*5,ch*10,statusstr,#statusstr)
  skp:text(cw*5,ch*13,actionstr,#actionstr)
  return true
end

-- respond to button presses
function consumebutton(bt,event)
    if bt==0 then
        if event%PANEL_MOUSE.LBPRESSED == PANEL_MOUSE.LBDOWN then
            airspeed_vector = vi:get_airspeedvector(REFFRAME.LOCAL)
            airspeed_target = airspeed_vector.z
            statusstr = 'Airspeed hold engaged'
            actionstr = 'Airspeed target: '..string.format("%.1f", airspeed_target)..' m/s'      
            airspeed_hold = true
            mfd:invalidate_display()
        end
        return true
    elseif bt==1 then
        if event%PANEL_MOUSE.LBPRESSED == PANEL_MOUSE.LBDOWN then
            --airspeed_target_str = proc.wait_input('Enter target airspeed in m/s: ')
            --airspeed_target = tonumber(airspeed_target_str)
            airspeed_target = 100
            statusstr = 'Airspeed hold engaged'
            actionstr = 'Airspeed target: '..string.format("%.1f", airspeed_target)..' m/s'
            airspeed_hold = true
            mfd:invalidate_display()
        end
        return true
    elseif bt==2 then
        if event%PANEL_MOUSE.LBPRESSED == PANEL_MOUSE.LBDOWN then
            statusstr = 'Airspeed hold released'
            actionstr = ''                      
            airspeed_hold = false
            mfd:invalidate_display()
        end
        return true
    else
        if event%PANEL_MOUSE.LBPRESSED == PANEL_MOUSE.LBDOWN then
            statusstr = ''          
            actionstr = 'Invalid key press'
            mfd:invalidate_display()
        end
        return true
    end
end

function poststep(simt,simdt,mjd)

    if airspeed_hold == true then

        airspeed_vector = vi:get_airspeedvector(REFFRAME.LOCAL)
        airspeed = airspeed_vector.z
        old_thruster_level = vi:get_thrustergrouplevel(THGROUP.MAIN)
        new_thruster_level = math.min(old_thruster_level*(airspeed_target/airspeed), 1)
        thruster_level = old_thruster_level + 0.5*(new_thruster_level - old_thruster_level)
        vi:set_thrustergrouplevel(THGROUP.MAIN,thruster_level)

    end

end
 

dgatsoulis

ele2png user
Donator
Joined
Dec 2, 2009
Messages
1,927
Reaction score
340
Points
98
Location
Sparta
I am wondering if that is an OS thing (I'm on Linux running Orbiter 2016 on Wine).
Not an OS thing. Hangs on me too (Windows 10). It appears that the MFD script doesn't like the loop that proc.wait_input creates.

I got an input window opening with the code below, but the string is not returned to the airspeed_target variable

Code:
--[[
Name = Airspeed Hold
Script = AirspeedHold.cfg
Key = 0x1E ; 'A'
Persist = vessel
END_PARSE
--]]

-- global reference to vessel associated with MFD
vi = vessel.get_focusinterface()

statusstr = ''
actionstr = ''
str = ''
airspeed_hold = false

-- the list of button labels
btn_label = {'HOL','SET','REL'}

-- callback function: button label for button 'bt' (>= 0)
function buttonlabel(bt)
  if bt < 3 then
    return btn_label[bt+1]
  end
  return nil
end

-- callback function: button menu entries
function buttonmenu()
  return btn_menu,3
end

-- callback function: update display
function update(skp)
  ch,cw = skp:get_charsize()
  mfd:set_title(skp,'Airspeed Hold')
  skp:text(cw*5,ch*10,statusstr,#statusstr)
  skp:text(cw*5,ch*13,actionstr,#actionstr)
  return true
end

-- respond to button presses
function consumebutton(bt,event)
    if bt==0 then
        if event%PANEL_MOUSE.LBPRESSED == PANEL_MOUSE.LBDOWN then
            airspeed_vector = vi:get_airspeedvector(REFFRAME.LOCAL)
            airspeed_target = airspeed_vector.z
            statusstr = 'Airspeed hold engaged'
            actionstr = 'Airspeed target: '..string.format("%.1f", airspeed_target)..' m/s'     
            airspeed_hold = true
            mfd:invalidate_display()
        end
        return true
    elseif bt==1 then
        if event%PANEL_MOUSE.LBPRESSED == PANEL_MOUSE.LBDOWN then
            str = oapi.receive_input(oapi.open_inputbox("title"))
            --airspeed_target = vi:get_groundspeed()
            if type(tonumber(str)) == 'number' and tonumber(str) > 0 then
                airspeed_tgt = tonumber(str)
                statusstr = 'Airspeed hold engaged'
                actionstr = 'Airspeed target: '..string.format("%.1f", airspeed_target)..' m/s'
                airspeed_hold = true
                mfd:invalidate_display()
            end
        end
        return true
    elseif bt==2 then
        if event%PANEL_MOUSE.LBPRESSED == PANEL_MOUSE.LBDOWN then
            statusstr = 'Airspeed hold released'
            actionstr = ''                     
            airspeed_hold = false
            mfd:invalidate_display()
        end
        return true
    else
        if event%PANEL_MOUSE.LBPRESSED == PANEL_MOUSE.LBDOWN then
            statusstr = ''         
            actionstr = 'Invalid key press'
            mfd:invalidate_display()
        end
        return true
    end
end

function poststep(simt,simdt,mjd)

    if airspeed_hold == true then

        airspeed_vector = vi:get_airspeedvector(REFFRAME.LOCAL)
        airspeed = airspeed_vector.z
        old_thruster_level = vi:get_thrustergrouplevel(THGROUP.MAIN)
        new_thruster_level = math.min(old_thruster_level*(airspeed_target/airspeed), 1)
        thruster_level = old_thruster_level + 0.5*(new_thruster_level - old_thruster_level)
        vi:set_thrustergrouplevel(THGROUP.MAIN,thruster_level)

    end

end
 

dgatsoulis

ele2png user
Donator
Joined
Dec 2, 2009
Messages
1,927
Reaction score
340
Points
98
Location
Sparta
finally got it to work (in somewhat the intended way). The set button simply sets the airspeed target and then the hold button either holds the set speed or if no set speed exists, holds the current speed.

Code:
--[[
Name = Airspeed Hold
Script = AirspeedHold.cfg
Key = 0x1E ; 'A'
Persist = vessel
END_PARSE
--]]

-- global reference to vessel associated with MFD
vi = vessel.get_focusinterface()

statusstr = ''
actionstr = ''
str = '0'
airspeed_hold = false

-- the list of button labels
btn_label = {'HOL','SET','REL'}

-- callback function: button label for button 'bt' (>= 0)
function buttonlabel(bt)
  if bt < 3 then
    return btn_label[bt+1]
  end
  return nil
end

-- callback function: button menu entries
function buttonmenu()
  return btn_menu,3
end

-- callback function: update display
function update(skp)
  ch,cw = skp:get_charsize()
  mfd:set_title(skp,'Airspeed Hold')
  skp:text(cw*5,ch*10,statusstr,#statusstr)
  skp:text(cw*5,ch*13,actionstr,#actionstr)
 -- skp:text(cw*5,ch*15,str,#str)
  return true
end

-- respond to button presses
function consumebutton(bt,event)
    if bt==0 then
        if event%PANEL_MOUSE.LBPRESSED == PANEL_MOUSE.LBDOWN then
            airspeed_vector = vi:get_airspeedvector(REFFRAME.LOCAL)
            str = oapi.receive_input()
            if type(tonumber(str)) == 'number' and tonumber(str) > 0  and tonumber(str) < 1000 then
                airspeed_target = tonumber(str)
                statusstr = 'Airspeed hold engaged'
                actionstr = 'Airspeed target: '..string.format("%.1f", airspeed_target)..' m/s'
            else
                airspeed_target = airspeed_vector.z
                statusstr = 'Airspeed hold engaged'
                actionstr = 'Airspeed target: '..string.format("%.1f", airspeed_target)..' m/s'
            end
            airspeed_hold = true
            mfd:invalidate_display()
        end
        return true
    elseif bt==1 then
        if event%PANEL_MOUSE.LBPRESSED == PANEL_MOUSE.LBDOWN then
            oapi.open_inputbox("title")
            --airspeed_target = vi:get_groundspeed()
            --if type(tonumber(str)) == 'number' and tonumber(str) > 0 then
            --    airspeed_tgt = tonumber(str)
            --    statusstr = 'Airspeed hold engaged'
            --    actionstr = 'Airspeed target: '..string.format("%.1f", airspeed_target)..' m/s'
            airspeed_hold = true
            mfd:invalidate_display()
            --end
        end
        return true
    elseif bt==2 then
        if event%PANEL_MOUSE.LBPRESSED == PANEL_MOUSE.LBDOWN then
            statusstr = 'Airspeed hold released'
            actionstr = ''                     
            airspeed_hold = false
            mfd:invalidate_display()
        end
        return true
    else
        if event%PANEL_MOUSE.LBPRESSED == PANEL_MOUSE.LBDOWN then
            statusstr = ''         
            actionstr = 'Invalid key press'
            mfd:invalidate_display()
        end
        return true
    end
end

function poststep(simt,simdt,mjd)

    if airspeed_hold == true then

        airspeed_vector = vi:get_airspeedvector(REFFRAME.LOCAL)
        airspeed = airspeed_vector.z
        str = oapi.receive_input()
        if type(tonumber(str)) == 'number' and tonumber(str) > 0  and tonumber(str) < 1000 then
                airspeed_target = tonumber(str)
                --statusstr = 'Airspeed hold engaged'
                --actionstr = 'Airspeed target: '..string.format("%.1f", airspeed_target)..' m/s'
                --airspeed_hold = true
                --mfd:invalidate_display()
            end
        old_thruster_level = vi:get_thrustergrouplevel(THGROUP.MAIN)
        new_thruster_level = math.min(old_thruster_level*(airspeed_target/airspeed), 1)
        thruster_level = old_thruster_level + 0.5*(new_thruster_level - old_thruster_level)
        vi:set_thrustergrouplevel(THGROUP.MAIN,thruster_level)
      end

end
 

Thunder Chicken

Fine Threads since 2008
Donator
Joined
Mar 22, 2008
Messages
4,367
Reaction score
3,302
Points
138
Location
Massachusetts
I actually got that working by adding + and - buttons that would increase or decrease the airspeed target by increments, and changing the SET button to an engage ENG button. So I can set the airspeed target, hit engage, and the autopilot will engage. Doing it with MFD buttons while flying is probably preferred to having to go to the keyboard to fill out a dialog box. I see that you used oapi.receive_input() instead of proc.wait_input(). I might try implementing that and see if it works and if I like it. The +/- keypresses seems to work well while flying.

One side question - is there any way to check the Lua script before attempting to load it in Orbiter? Right now I am finding bugs in my code when I go to try to load the MFD and it fails and crashes which tells me something is wrong but doesn't really tell me what is wrong.

Code:
--[[
Name = Airspeed Hold
Script = AirspeedHold.cfg
Key = 0x1E ; 'A'
Persist = vessel
END_PARSE
--]]

-- global reference to vessel associated with MFD
vi = vessel.get_focusinterface()

statusstr = ''
actionstr = ''
airspeed_hold = false
airspeed_target = 100
airspeed_target_default = 100

-- the list of button labels
btn_label = {'HOL','ENG','REL',' + ',' - '}

-- callback function: button label for button 'bt' (>= 0)
function buttonlabel(bt)
  if bt < 5 then
    return btn_label[bt+1]
  end
  return nil
end

-- callback function: button menu entries
function buttonmenu()
  return btn_menu,5
end

-- callback function: update display
function update(skp)
  ch,cw = skp:get_charsize()
  mfd:set_title(skp,'Airspeed Hold')
  skp:text(cw*5,ch*10,statusstr,#statusstr)
  skp:text(cw*5,ch*13,actionstr,#actionstr)
  return true
end

-- respond to button presses
function consumebutton(bt,event)
    if bt==0 then
        if event%PANEL_MOUSE.LBPRESSED == PANEL_MOUSE.LBDOWN then
            airspeed_vector = vi:get_airspeedvector(REFFRAME.LOCAL)
            airspeed_target = airspeed_vector.z
            airspeed_hold = true
            statusstr = 'Airspeed hold engaged'
            actionstr = 'Airspeed target: '..string.format("%.1f", airspeed_target)..' m/s'                  
            mfd:invalidate_display()
        end
        return true
    elseif bt==1 then
        if event%PANEL_MOUSE.LBPRESSED == PANEL_MOUSE.LBDOWN then
            airspeed_hold = true          
            statusstr = 'Airspeed hold engaged'
            actionstr = 'Airspeed target: '..string.format("%.1f", airspeed_target)..' m/s'
            mfd:invalidate_display()
        end
        return true
    elseif bt==2 then
        if event%PANEL_MOUSE.LBPRESSED == PANEL_MOUSE.LBDOWN then
            airspeed_hold = false
            airspeed_target = airspeed_target_default          
            statusstr = 'Airspeed hold released'
            actionstr = ''          
            mfd:invalidate_display()
        end
        return true
    elseif bt==3 then
        if event%PANEL_MOUSE.LBPRESSED == PANEL_MOUSE.LBDOWN then          
            airspeed_target = airspeed_target + 5
            statusstr = 'Set airspeed target'          
            actionstr = 'Airspeed target: '..string.format("%.1f", airspeed_target)..' m/s'                      
            mfd:invalidate_display()
        end
        return true
    elseif bt==4 then
        if event%PANEL_MOUSE.LBPRESSED == PANEL_MOUSE.LBDOWN then
            airspeed_target = airspeed_target - 5
            statusstr = 'Set airspeed target'          
            actionstr = 'Airspeed target: '..string.format("%.1f", airspeed_target)..' m/s'                      
            mfd:invalidate_display()
        end
        return true
    else
        if event%PANEL_MOUSE.LBPRESSED == PANEL_MOUSE.LBDOWN then
            statusstr = ''          
            actionstr = 'Invalid key press'
            mfd:invalidate_display()
        end
        return true
    end
end

function poststep(simt,simdt,mjd)

    if airspeed_hold == true then

        airspeed_vector = vi:get_airspeedvector(REFFRAME.LOCAL)
        airspeed = airspeed_vector.z
        old_thruster_level = vi:get_thrustergrouplevel(THGROUP.MAIN)
        new_thruster_level = math.min(old_thruster_level*(airspeed_target/airspeed))
        thruster_level = math.max(math.min(old_thruster_level + 1.1*(new_thruster_level - old_thruster_level),1),0)
        vi:set_thrustergrouplevel(THGROUP.MAIN,thruster_level)

    end

end
 

dgatsoulis

ele2png user
Donator
Joined
Dec 2, 2009
Messages
1,927
Reaction score
340
Points
98
Location
Sparta
Activate
I actually got that working by adding + and - buttons that would increase or decrease the airspeed target by increments, and changing the SET button to an engage ENG button. So I can set the airspeed target, hit engage, and the autopilot will engage. Doing it with MFD buttons while flying is probably preferred to having to go to the keyboard to fill out a dialog box. I see that you used oapi.receive_input() instead of proc.wait_input(). I might try implementing that and see if it works and if I like it. The +/- keypresses seems to work well while flying.
Good to hear you got it working. I also don't really like popup windows, as I feel that they break the immersion, especially on ships with a nice VC. The method you chose is probably better. Perhaps add a "factor" or "adj" button to change the scale of the increased/decreased value. (ie: x1, x10 x100 )

One side note though, you are getting the airspeed in the local (vessel) reference frame and you are using the z component of that vector to calculate the airspeed. Are you sure you want it like that? If your nose is pointing down and you are doing 200 m/s vertical, it will register as 200 m/s in the z direction of the vessel's local frame, even though you'll have no horizontal speed. Perhaps the local horizon frame would be a better pick? Or even get_groundspeed() (that is a scalar, not a vector).

Unless that's how you want it to behave, so that it doesn't turn on the engines when the ship is not fighting gravity (which makes sense I suppose).

One side question - is there any way to check the Lua script before attempting to load it in Orbiter? Right now I am finding bugs in my code when I go to try to load the MFD and it fails and crashes which tells me something is wrong but doesn't really tell me what is wrong.
Activate LuaConsole in the modules tab.
Hit F4 or click on the functions button in the top/center infobar and select Lua Console Window.
In the window type: run_global("Config/MFD/AirspeedHold.cfg")
Since it is an ScriptMFD .cfg file, it will not actually run a Lua script in Orbiter, but it should return errors (and the corresponding lines) if they occur. (unclosed parentheses, unexpected returned values... not closed if statements... things like that).
For example:
Config/MFD/AirspeedHold.cfg 21: '}' expected to (to close '{' at line 18) near 'function'
Any changes you make to the cfg file, won't turn up directly in Orbiter, you'll need to close the simulation and run the scenario again.

Direct Lua scripts on the other hand, run "live", so if you get an error, you can see where it is, make the change and run it again via the run("scriptname") command. (needs to be a .lua file).

The path for the run_global command is your Orbiter root and you need to enter the file extension, while the path for the run command is the Orbiter root\Script folder and you don't enter the extension (it is assumed to be a lua file).

:cheers:
 
Top