diff --git a/BoSLOO.pyproj b/BoSLOO.pyproj
index 1e0b50d..8241d3e 100644
--- a/BoSLOO.pyproj
+++ b/BoSLOO.pyproj
@@ -11,7 +11,7 @@
.
BoSLOO
BoSLOO
- MSBuild|bosloo_env|$(MSBuildProjectFullPath)
+ MSBuild|BoSLOOenv|$(MSBuildProjectFullPath)
true
diff --git a/OrbitSim.py b/OrbitSim.py
index 2972ed5..565334c 100644
--- a/OrbitSim.py
+++ b/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 copy import deepcopy
-groundControlPath = "GroundControlFiles"
+groundControlPath = "GroundControl"
+stateFilePath = os.path.join("SatState.json")
+
configPath = os.path.join("ConfigFiles", "OrbitSim")
configFilename = os.path.join(configPath, "Universe.cfg")
+satSavePath = os.path.join(configPath, "Orbit.cfg")
mapFilename = os.path.join(configPath, "Map.png")
+STATE_EVENT = pygame.event.custom_type()
+
def config():
"""Returns the config dictionary. Generates with default values if no config dictionary exists."""
@@ -18,7 +23,8 @@ def config():
"G": 6.674e-11,
"earthMass": 5.972e24, #in kg
"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:
json.dump(config_dic, file, indent = 4)
@@ -27,14 +33,67 @@ def config():
with open(configFilename) as file:
return json.load(file)
+
+
class OrbitingBody:
"""a zero-mass point object parented to a planet"""
def __init__(self, location:Point, velocity:Point, name, displaySize, parentPlanet):
self.location = location
+ self.resetLocation = location.copy()
self.velocity = velocity
+ self.resetVelocity = velocity.copy()
self.name = name
self.displaySize = displaySize #the size of the object on camera in pixels, for visibility reasons
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:
"""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"""
rotRadian = self.rotationPercentage/100 * 2 * math.pi
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:
long += math.pi*2
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))
obj.velocity = Point.add(obj.velocity, Point.scalarMult(accel, deltaTime))
obj.location = Point.add(obj.location, Point.scalarMult(obj.velocity, deltaTime))
+ obj.stationKeep()
elif type(obj).__name__ == "Planet":
obj.rotate(deltaTime)
for line in orbitlines:
@@ -116,10 +176,12 @@ if __name__=="__main__":
running = True
display = False
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 = []
renderObjects = [thisEarth, sat, orbitlines]
+ configFile = config()
clock = pygame.time.Clock()
+ stateTimer = pygame.time.set_timer(STATE_EVENT, configFile["updateTick"]*1000)
mapThread = threading.Thread()
save = False
@@ -130,7 +192,7 @@ if __name__=="__main__":
clock.tick(FPS)
if display:
#deltaTime = frameTime * config()["timeScale"]
- deltaTime = (clock.get_time()/1000) * config()["timeScale"]
+ deltaTime = (clock.get_time()/1000) * configFile["timeScale"]
physicsUpdate(renderObjects, orbitlines, deltaTime)
camera.renderFrame(save=save)
save=False
@@ -142,7 +204,7 @@ if __name__=="__main__":
elif event.type == pygame.MOUSEBUTTONDOWN:
if not display:
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()
pygame.display.flip()
else:
@@ -150,6 +212,10 @@ if __name__=="__main__":
if not mapThread.is_alive():
mapThread = threading.Thread(target=camera.saveGroundTrack())
mapThread.start()
+
+ elif event.type == STATE_EVENT:
+ sat.writeStateReadable()
+ configFile = config()
#time.sleep(frameTime)
diff --git a/SatState.json b/SatState.json
new file mode 100644
index 0000000..1773608
--- /dev/null
+++ b/SatState.json
@@ -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
+}
\ No newline at end of file
diff --git a/renderer.py b/renderer.py
index 51409c2..3d6841c 100644
--- a/renderer.py
+++ b/renderer.py
@@ -11,6 +11,9 @@ class Point:
def __init__(self, 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):
"""Converts the vector rectangular coordinates to polar coordinates."""
if self.vector[0] == 0:
@@ -28,7 +31,7 @@ class Point:
return [rho, theta, phi]
def magnitude(self):
- return numpy.linalg.norm(self.vector)
+ return float(numpy.linalg.norm(self.vector))
def normalize(self):
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)
#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)
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"
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,70), longString, (255,255,255))
diff --git a/testMap.png b/testMap.png
index 2887863..d9d96c7 100644
Binary files a/testMap.png and b/testMap.png differ