this script puts the viewer above the selected body and computes tangential, angular and radial speeds for it, with respect to its "parent" body (e.g. Earth-Sol, ISS-Earth, Dactyl-Ida, etc.)
LUA precision limitations arise at high time speeds, or when viewer is really close to small bodies
Code: Select all
--*********************************************************************
--*********************************************************************
-- A script for computing speeds
-- coded by Toti
--*********************************************************************
--*********************************************************************
--*********************************************************************
-- constants
--*********************************************************************
DELAY = 0.5
HORIG = 0
VORIG = -1
KM_MLY = 9466411.842
SEC_DAY = 86400
ANGLE = 20
TAN_ANGLE = math.tan(math.rad(ANGLE)) -- this one is used by viewBodyAbove(), but it's done here because of
-- performance
HOW_FAR = 1.1 -- this one is used by viewBodyAbove(), it's a measure of how close
-- the viewer is from parent body
DELTA_S = 1000 -- time increment in seconds (default: 100 sec. = 16.6 min. you can reduce
-- this in order to get smaller "differentials")
DELTA_D = DELTA_S / SEC_DAY -- time increment converted to days (default: about 0.011 days)
obs = celestia:getobserver()
--*********************************************************************
-- flags and status save and restoration functions
--*********************************************************************
function setRenderOn ()
-- sets the necessary renderflags on
celestia:setrenderflags{constellations=false,eclipseshadows=true,galaxies=true,grid=false,lightdelay=false,markers=true,
nightmaps=true,orbits=true,planets=true,ringshadows=true,smoothlines=false,stars=true}
end
function setRenderOff ()
-- sets the necessary renderflags off
celestia:setrenderflags{markers=false,orbits=false}
-- a 'labels' render flag would be handy:
celestia:setlabelflags{asteroids=false,comets=false,moons=false,planets=false,spacecraft=false,constellations=false,
galaxies=false,locations=false,stars=false}
end
function setOrbits (sType)
-- sets the orbit on for this particular body's type. (There is probably a direct way to do this):
local t = {}
t.Asteroid = false
t.Comet = false
t.Moon = false
t.Planet = false
t.Spacecraft= false
if sType == "asteroid" then
t.Asteroid = true
elseif sType == "comet" then
t.Comet = true
elseif sType == "moon" then
t.Moon = true
elseif sType == "planet" then
t.Planet = true
elseif sType == "spacecraft" then
t.Spacecraft= true
end
celestia:setorbitflags(t)
end
function setLabels (sType)
-- sets the label on for this particular body's type. (Again, there is probably a direct way to do this):
local t = {}
t.asteroids = false
t.comets = false
t.moons = false
t.planets = false
t.spacecraft = false
t.constellations = false
t.galaxies = false
t.locations = false
t.stars = false
if sType == "asteroid" then
t.asteroids = true
elseif sType == "comet" then
t.comets = true
elseif sType == "moon" then
t.moons = true
elseif sType == "planet" then
t.planets = true
elseif sType == "spacecraft" then
t.spacecraft = true
t.planets = true
end
celestia:setlabelflags(t)
end
function celestia_cleanup_callback()
-- restore user's settings
celestia:setrenderflags(oldrf)
celestia:setlabelflags(oldlf)
celestia:setorbitflags(oldof)
end
function saveOldSettings()
--store user's settings
oldrf = celestia:getrenderflags()
oldlf = celestia:getlabelflags()
oldof = celestia:getorbitflags()
end
--*********************************************************************
-- some support functions
--*********************************************************************
function selectpair()
local s = celestia:getselection()
local p
if s:type() == "null" then -- nothing selected
s = nil
else
if s:type() == "star" then -- stars does not have parent, so select its first son
p = s
s = p:getchildren()[1]
if s ~= nil then -- this star does not have orbiting bodies
celestia:select(s)
end
else -- the son is another class of body
p = s:getinfo()['parent']
end
end
if s ~= nil then
setRenderOn()
setOrbits(s:type())
setLabels(s:type())
celestia:unmarkall()
s:mark ("#ff0000", "diamond", 30)
p:mark ("#ffff00", "diamond", 50)
else
setRenderOff()
end
return s, p
end
function viewBodyAbove (norm, sPos, pPos)
-- puts the observer perpendicular to the local orbit's plane, "above" the parent.
-- because our line of sight will be locally orthogonal to the orbital plane, we can use simple trigonometrics to
-- calculate the distance we need to be placed from parent, in order to see both son and parent with an angle = ANGLE
local radiusVector = sPos-pPos
local distance = radiusVector:length() * HOW_FAR / TAN_ANGLE
-- place the viewer
obs:setposition ( pPos + (norm * distance) )
-- keep the top of the screen oriented along radiusvector
obs:lookat (pPos, radiusVector)
end
--*********************************************************************
-- speed functions
--*********************************************************************
function tspeed (p0, p1, dt)
-- tangential speed
return (p1-p0):length() * KM_MLY / dt
end
function aspeed (p0, p1, dt)
-- angular speed
return math.deg( math.acos( (p0*p1)/( p0:length() * p1:length() ) ) ) / dt
end
function rspeed (p0, p1, dt)
-- radial speed
return (p1:length() - p0:length()) * KM_MLY / dt
end
--*********************************************************************
-- main script
--*********************************************************************
--save Celestia status
saveOldSettings()
repeat
son, par = selectpair()
if son ~= nil then
parPos = par:getposition()
sonPos = son:getposition()
sonPosR1 = parPos - sonPos
t1 = celestia:gettime()
t0 = t1 - DELTA_D
sonPosR0 = par:getposition(t0) - son:getposition(t0) -- parent is moving
ts = tspeed (sonPosR0, sonPosR1, DELTA_S)
as = aspeed (sonPosR0, sonPosR1, DELTA_S)
rs = rspeed (sonPosR0, sonPosR1, DELTA_S)
celestia:print(string.format("Speeds relative to " .. par:name() .. "\nTangential: %4.5f km/sec\nAngular: %14.15f deg/sec\nRadial: %4.5f km/sec", ts, as, rs), DELAY, HORIG, VORIG)
-- compute a normal for the orbit. It must be done inside the loop because of local planar accuracy
normal = (sonPosR0^sonPosR1):normalize()
-- if you want to move freely, just comment this line
viewBodyAbove (normal, sonPos, parPos)
end
wait()
until 1==2
EDIT: I have modified the way speeds are computed (see below)
Please post bugs, suggestions and comments here.
Bye