The great thing about the #30DayMapChallenge is that you can choose how many challenges to get involved in. Some people go all out and manage every day. I tend to be more selective. Day 09 - Space seemed like a great opportunity to dig out an old globe model and look outwards from planet earth. Researching possibilities, I stumbled across the amazing Skyfield that is able to predict the positions of Earth satellites.
Behind the Scenes
Skyfield from https://rhodesmill.org/skyfield/earth-satellites.html
Satellite positions from http://celestrak.org/NORAD/elements/
Working thorugh the code snippets on the Skyfield pages, I ended up with this code to get the ISS positions:
from skyfield.api import load, wgs84
import math
stations_url = 'http://celestrak.com/NORAD/elements/stations.txt'
#stations_url = 'https://celestrak.org/NORAD/elements/gp.php?GROUP=starlink&FORMAT=tle'
#stations_url = 'https://celestrak.org/NORAD/elements/gp.php?GROUP=active&FORMAT=tle'
satellites = load.tle_file(stations_url)
print('Loaded', len(satellites), 'satellites')
#for sat in satellites:
# print(sat.epoch.utc_jpl())
#sat = satellites[0]
ts = load.timescale()
for hour in range(0, 25, 1):
for min in range(0, 61, 1):
t = ts.utc(2022, 11, 9, hour, min, 0)
# print("lat, lon, height")
# for sat in satellites:
sat = satellites[0]
geocentric = sat.at(t)
# print(geocentric.position.km)
lat, lon = wgs84.latlon_of(geocentric)
height = wgs84.height_of(geocentric)
height_km = height.km
if (math.isnan(lat.degrees) == False and math.isnan(lon.degrees) == False and math.isnan(height_km) == False):
print(f"({lat.degrees},{lon.degrees},{height_km}),")
This gives a set of points that we can load into a python list
points = [
(30.197778074499244,-41.69238013378573,414.3551803386221),
...
many entries excluded
...
(51.44188187480004,9.04164498351871,419.61570652925167)
]
In Blender, we can use this points list to create a point cloud
def point_cloud(ob_name, coords, edges=[], faces=[]):
"""Create point cloud object based on given coordinates and name.
Keyword arguments:
ob_name -- new object name
coords -- float triplets eg: [(-1.0, 1.0, 0.0), (-1.0, -1.0, 0.0)]
"""
# Create new mesh and a new object
me = bpy.data.meshes.new(ob_name + "Mesh")
ob = bpy.data.objects.new(ob_name, me)
# Make a mesh from a list of vertices/edges/faces
me.from_pydata(coords, edges, faces)
# Display name and update the mesh
ob.show_name = True
me.update()
return ob
Stringing everything together we end up with a short function to create the ISS trails and positions.
getXYZfromLatLon() was covered in Globe Flights and addCurve() will be covered in my next post...
def showPoints():
R = 6371 / 1000
coords = []
for p in points:
newPoint = getXYZfromLatLon(R+p[2]/1000, p[0], p[1])
coords.append(newPoint)
pc = point_cloud('satellites', coords)
bpy.context.collection.objects.link(pc)
addCurve(coords)
showPoints()