added state writing at configurable time steps
This commit is contained in:
parent
71845520b2
commit
92e9981eaf
|
@ -11,7 +11,7 @@
|
||||||
<OutputPath>.</OutputPath>
|
<OutputPath>.</OutputPath>
|
||||||
<Name>BoSLOO</Name>
|
<Name>BoSLOO</Name>
|
||||||
<RootNamespace>BoSLOO</RootNamespace>
|
<RootNamespace>BoSLOO</RootNamespace>
|
||||||
<InterpreterId>MSBuild|bosloo_env|$(MSBuildProjectFullPath)</InterpreterId>
|
<InterpreterId>MSBuild|BoSLOOenv|$(MSBuildProjectFullPath)</InterpreterId>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||||
<DebugSymbols>true</DebugSymbols>
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
|
80
OrbitSim.py
80
OrbitSim.py
|
@ -1,12 +1,17 @@
|
||||||
import os, json, numpy, pygame, time, threading
|
import os, json, numpy, pygame, time, threading, jsonpickle
|
||||||
from renderer import *
|
from renderer import *
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
|
|
||||||
groundControlPath = "GroundControlFiles"
|
groundControlPath = "GroundControl"
|
||||||
|
stateFilePath = os.path.join("SatState.json")
|
||||||
|
|
||||||
configPath = os.path.join("ConfigFiles", "OrbitSim")
|
configPath = os.path.join("ConfigFiles", "OrbitSim")
|
||||||
configFilename = os.path.join(configPath, "Universe.cfg")
|
configFilename = os.path.join(configPath, "Universe.cfg")
|
||||||
|
satSavePath = os.path.join(configPath, "Orbit.cfg")
|
||||||
mapFilename = os.path.join(configPath, "Map.png")
|
mapFilename = os.path.join(configPath, "Map.png")
|
||||||
|
|
||||||
|
STATE_EVENT = pygame.event.custom_type()
|
||||||
|
|
||||||
def config():
|
def config():
|
||||||
"""Returns the config dictionary. Generates with default values if no config dictionary exists."""
|
"""Returns the config dictionary. Generates with default values if no config dictionary exists."""
|
||||||
|
|
||||||
|
@ -18,7 +23,8 @@ def config():
|
||||||
"G": 6.674e-11,
|
"G": 6.674e-11,
|
||||||
"earthMass": 5.972e24, #in kg
|
"earthMass": 5.972e24, #in kg
|
||||||
"earthRadius": 6378000, #meters
|
"earthRadius": 6378000, #meters
|
||||||
"timeScale": 1 #higher number go faster wheeeeee
|
"timeScale": 1, #higher number go faster wheeeeee
|
||||||
|
"updateTick": 300 #seconds to wait between save to file
|
||||||
}
|
}
|
||||||
with open(configFilename, "w") as file:
|
with open(configFilename, "w") as file:
|
||||||
json.dump(config_dic, file, indent = 4)
|
json.dump(config_dic, file, indent = 4)
|
||||||
|
@ -27,14 +33,67 @@ def config():
|
||||||
with open(configFilename) as file:
|
with open(configFilename) as file:
|
||||||
return json.load(file)
|
return json.load(file)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class OrbitingBody:
|
class OrbitingBody:
|
||||||
"""a zero-mass point object parented to a planet"""
|
"""a zero-mass point object parented to a planet"""
|
||||||
def __init__(self, location:Point, velocity:Point, name, displaySize, parentPlanet):
|
def __init__(self, location:Point, velocity:Point, name, displaySize, parentPlanet):
|
||||||
self.location = location
|
self.location = location
|
||||||
|
self.resetLocation = location.copy()
|
||||||
self.velocity = velocity
|
self.velocity = velocity
|
||||||
|
self.resetVelocity = velocity.copy()
|
||||||
self.name = name
|
self.name = name
|
||||||
self.displaySize = displaySize #the size of the object on camera in pixels, for visibility reasons
|
self.displaySize = displaySize #the size of the object on camera in pixels, for visibility reasons
|
||||||
self.parentPlanet = parentPlanet
|
self.parentPlanet = parentPlanet
|
||||||
|
self.lastDelta = 0
|
||||||
|
self.lastSecondDelta = 0
|
||||||
|
self.keepFreeze = 3
|
||||||
|
|
||||||
|
def stationKeep(self):
|
||||||
|
currDelta = Point.subtract(self.resetLocation, self.location).magnitude()
|
||||||
|
currSecondDelta = currDelta - self.lastDelta
|
||||||
|
if (currSecondDelta > 0) and (self.lastSecondDelta <= 0) and self.keepFreeze <= 0:
|
||||||
|
self.location = self.resetLocation.copy()
|
||||||
|
self.velocity = self.resetVelocity.copy()
|
||||||
|
self.keepFreeze = 3
|
||||||
|
elif self.keepFreeze > 0:
|
||||||
|
self.keepFreeze -= 1
|
||||||
|
self.lastDelta = currDelta
|
||||||
|
self.lastSecondDelta = currSecondDelta
|
||||||
|
|
||||||
|
def latLongAlt(self):
|
||||||
|
rho, theta, phi = self.location.polar()
|
||||||
|
rawLat, rawLong = self.parentPlanet.sphericalToLatLong(theta, phi) #negative lat is north, positive lat is south, positive long is east, negative long is west
|
||||||
|
return (rho - self.parentPlanet.radius), rawLat, rawLong
|
||||||
|
|
||||||
|
def writeStateReadable(self):
|
||||||
|
alt, lat, long = self.latLongAlt()
|
||||||
|
stateDic = {
|
||||||
|
"notes": "lat: pos S, neg N; long: pos E, neg W",
|
||||||
|
"latitude": lat,
|
||||||
|
"longitude": long,
|
||||||
|
"altitude": alt,
|
||||||
|
"velocity": self.velocity.magnitude()
|
||||||
|
}
|
||||||
|
with open(stateFilePath, "w") as file:
|
||||||
|
json.dump(stateDic, file, indent=4)
|
||||||
|
|
||||||
|
def saveState(self):
|
||||||
|
stateDic = {
|
||||||
|
"location": jsonpickle.encode(self.location),
|
||||||
|
"velocity": jsonpickle.encode(self.velocity),
|
||||||
|
}
|
||||||
|
|
||||||
|
def loadState(self):
|
||||||
|
if os.path.exists(satSavePath):
|
||||||
|
with open(satSavePath) as file:
|
||||||
|
state = json.load(file)
|
||||||
|
self.location = jsonpickle.decode(state["location"])
|
||||||
|
self.velocity = jsonpickle.decode(state["velocity"])
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
class Planet:
|
class Planet:
|
||||||
"""A massive body at 0,0,0 and a given radius."""
|
"""A massive body at 0,0,0 and a given radius."""
|
||||||
|
@ -56,7 +115,7 @@ class Planet:
|
||||||
"""Converts theta and phi spherical coordinates to latitude and longitude. -> lat, long"""
|
"""Converts theta and phi spherical coordinates to latitude and longitude. -> lat, long"""
|
||||||
rotRadian = self.rotationPercentage/100 * 2 * math.pi
|
rotRadian = self.rotationPercentage/100 * 2 * math.pi
|
||||||
lat = math.degrees(phi - (math.pi/2)) #negative lat is north, positive is south
|
lat = math.degrees(phi - (math.pi/2)) #negative lat is north, positive is south
|
||||||
long = theta - rotRadian #positive long is east, negative is west
|
long = rotRadian - theta #positive long is east, negative is west
|
||||||
if long < -math.pi:
|
if long < -math.pi:
|
||||||
long += math.pi*2
|
long += math.pi*2
|
||||||
elif long > math.pi:
|
elif long > math.pi:
|
||||||
|
@ -97,6 +156,7 @@ def physicsUpdate(objects, orbitlines, deltaTime):
|
||||||
accel = Point.scalarMult(Point.subtract(obj.location, obj.parentPlanet.location).normalize(),-(config()["G"] * obj.parentPlanet.mass)/(Point.subtract(obj.location, obj.parentPlanet.location).magnitude() ** 2))
|
accel = Point.scalarMult(Point.subtract(obj.location, obj.parentPlanet.location).normalize(),-(config()["G"] * obj.parentPlanet.mass)/(Point.subtract(obj.location, obj.parentPlanet.location).magnitude() ** 2))
|
||||||
obj.velocity = Point.add(obj.velocity, Point.scalarMult(accel, deltaTime))
|
obj.velocity = Point.add(obj.velocity, Point.scalarMult(accel, deltaTime))
|
||||||
obj.location = Point.add(obj.location, Point.scalarMult(obj.velocity, deltaTime))
|
obj.location = Point.add(obj.location, Point.scalarMult(obj.velocity, deltaTime))
|
||||||
|
obj.stationKeep()
|
||||||
elif type(obj).__name__ == "Planet":
|
elif type(obj).__name__ == "Planet":
|
||||||
obj.rotate(deltaTime)
|
obj.rotate(deltaTime)
|
||||||
for line in orbitlines:
|
for line in orbitlines:
|
||||||
|
@ -116,10 +176,12 @@ if __name__=="__main__":
|
||||||
running = True
|
running = True
|
||||||
display = False
|
display = False
|
||||||
thisEarth = deepcopy(Planet.Earth)
|
thisEarth = deepcopy(Planet.Earth)
|
||||||
sat = OrbitingBody(Point(0, config()["earthRadius"], config()["earthRadius"] - 800000), Point(-8900,0,0), "BoSLOO", 5, thisEarth)
|
sat = OrbitingBody(Point(0, config()["earthRadius"], config()["earthRadius"] - 800000), Point(-6900,0,0), "BoSLOO", 5, thisEarth)
|
||||||
orbitlines = []
|
orbitlines = []
|
||||||
renderObjects = [thisEarth, sat, orbitlines]
|
renderObjects = [thisEarth, sat, orbitlines]
|
||||||
|
configFile = config()
|
||||||
clock = pygame.time.Clock()
|
clock = pygame.time.Clock()
|
||||||
|
stateTimer = pygame.time.set_timer(STATE_EVENT, configFile["updateTick"]*1000)
|
||||||
mapThread = threading.Thread()
|
mapThread = threading.Thread()
|
||||||
|
|
||||||
save = False
|
save = False
|
||||||
|
@ -130,7 +192,7 @@ if __name__=="__main__":
|
||||||
clock.tick(FPS)
|
clock.tick(FPS)
|
||||||
if display:
|
if display:
|
||||||
#deltaTime = frameTime * config()["timeScale"]
|
#deltaTime = frameTime * config()["timeScale"]
|
||||||
deltaTime = (clock.get_time()/1000) * config()["timeScale"]
|
deltaTime = (clock.get_time()/1000) * configFile["timeScale"]
|
||||||
physicsUpdate(renderObjects, orbitlines, deltaTime)
|
physicsUpdate(renderObjects, orbitlines, deltaTime)
|
||||||
camera.renderFrame(save=save)
|
camera.renderFrame(save=save)
|
||||||
save=False
|
save=False
|
||||||
|
@ -142,7 +204,7 @@ if __name__=="__main__":
|
||||||
elif event.type == pygame.MOUSEBUTTONDOWN:
|
elif event.type == pygame.MOUSEBUTTONDOWN:
|
||||||
if not display:
|
if not display:
|
||||||
display = True
|
display = True
|
||||||
camera = Camera(window, Point(10 * config()["earthRadius"], 0, 0), thisEarth, renderObjects)
|
camera = Camera(window, Point(10 * configFile["earthRadius"], 0, 0), thisEarth, renderObjects)
|
||||||
camera.renderFrame()
|
camera.renderFrame()
|
||||||
pygame.display.flip()
|
pygame.display.flip()
|
||||||
else:
|
else:
|
||||||
|
@ -151,6 +213,10 @@ if __name__=="__main__":
|
||||||
mapThread = threading.Thread(target=camera.saveGroundTrack())
|
mapThread = threading.Thread(target=camera.saveGroundTrack())
|
||||||
mapThread.start()
|
mapThread.start()
|
||||||
|
|
||||||
|
elif event.type == STATE_EVENT:
|
||||||
|
sat.writeStateReadable()
|
||||||
|
configFile = config()
|
||||||
|
|
||||||
#time.sleep(frameTime)
|
#time.sleep(frameTime)
|
||||||
|
|
||||||
pygame.quit()
|
pygame.quit()
|
||||||
|
|
7
SatState.json
Normal file
7
SatState.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"notes": "lat: pos S, neg N; long: pos E, neg W",
|
||||||
|
"latitude": -9.621613589753375,
|
||||||
|
"longitude": -135.404396876101,
|
||||||
|
"altitude": 2246794.2098555025,
|
||||||
|
"velocity": 6778.7033882673195
|
||||||
|
}
|
12
renderer.py
12
renderer.py
|
@ -11,6 +11,9 @@ class Point:
|
||||||
def __init__(self, x, y, z):
|
def __init__(self, x, y, z):
|
||||||
self.vector = numpy.array([x, y, z])
|
self.vector = numpy.array([x, y, z])
|
||||||
|
|
||||||
|
def copy(self):
|
||||||
|
return Point(self.vector[0], self.vector[1], self.vector[2])
|
||||||
|
|
||||||
def polar(self):
|
def polar(self):
|
||||||
"""Converts the vector rectangular coordinates to polar coordinates."""
|
"""Converts the vector rectangular coordinates to polar coordinates."""
|
||||||
if self.vector[0] == 0:
|
if self.vector[0] == 0:
|
||||||
|
@ -28,7 +31,7 @@ class Point:
|
||||||
return [rho, theta, phi]
|
return [rho, theta, phi]
|
||||||
|
|
||||||
def magnitude(self):
|
def magnitude(self):
|
||||||
return numpy.linalg.norm(self.vector)
|
return float(numpy.linalg.norm(self.vector))
|
||||||
|
|
||||||
def normalize(self):
|
def normalize(self):
|
||||||
self.vector = self.vector/self.magnitude()
|
self.vector = self.vector/self.magnitude()
|
||||||
|
@ -209,16 +212,13 @@ class Camera:
|
||||||
#pygame.draw.circle(screenSurface, (150,255,150), (int(intersectPoint.vector[1]), int(intersectPoint.vector[2])), 5)
|
#pygame.draw.circle(screenSurface, (150,255,150), (int(intersectPoint.vector[1]), int(intersectPoint.vector[2])), 5)
|
||||||
|
|
||||||
#generate text
|
#generate text
|
||||||
rho, theta, phi = sat.location.polar()
|
|
||||||
if rho < self.target.radius:
|
|
||||||
0 == 0
|
|
||||||
|
|
||||||
rawLat, rawLong = self.target.sphericalToLatLong(theta, phi)
|
alt, rawLat, rawLong = sat.latLongAlt()
|
||||||
self.updateTrackList(rawLat, rawLong)
|
self.updateTrackList(rawLat, rawLong)
|
||||||
latString = f"Latitude: {round(rawLat,4)}⁰ S" if rawLat >= 0 else f"Latitude: {-round(rawLat,4)}⁰ N"
|
latString = f"Latitude: {round(rawLat,4)}⁰ S" if rawLat >= 0 else f"Latitude: {-round(rawLat,4)}⁰ N"
|
||||||
longString = f"Longitude: {round(rawLong,4)}⁰ E" if rawLong >= 0 else f"Longitude: {-round(rawLong,4)}⁰ W"
|
longString = f"Longitude: {round(rawLong,4)}⁰ E" if rawLong >= 0 else f"Longitude: {-round(rawLong,4)}⁰ W"
|
||||||
font.render_to(backSurface, (0,0), f"Speed: {round(sat.velocity.magnitude()/1000,3)} km/s", (255,255,255))
|
font.render_to(backSurface, (0,0), f"Speed: {round(sat.velocity.magnitude()/1000,3)} km/s", (255,255,255))
|
||||||
font.render_to(backSurface, (0,20), f"Altitude: {round((rho - self.target.radius)/1000)} km", (255,255,255))
|
font.render_to(backSurface, (0,20), f"Altitude: {round((alt)/1000)} km", (255,255,255))
|
||||||
font.render_to(backSurface, (0,50), latString, (255,255,255))
|
font.render_to(backSurface, (0,50), latString, (255,255,255))
|
||||||
font.render_to(backSurface, (0,70), longString, (255,255,255))
|
font.render_to(backSurface, (0,70), longString, (255,255,255))
|
||||||
|
|
||||||
|
|
BIN
testMap.png
BIN
testMap.png
Binary file not shown.
Before Width: | Height: | Size: 2.2 MiB After Width: | Height: | Size: 2.2 MiB |
Loading…
Reference in a new issue