## Challenge:Given a satellite TLE, a point target, a ground station, and a 48-hour time window, schedule mode transitions such that 120MB of captured data are downlinked and the spacecraft survives the entire duration.
There are 4 modes:- sun_point, for charging batteries- imaging, for imaging the target- data_downlink, for downlinking data to the ground station- wheel_desaturate, for desaturating reaction wheels
The challenge only requires UTC time stamps and mode transitions; you do not need to find the spacecraft attitude needed for the modes. Also assume that mode transitions are instantaneous. Mode transitions must occur at 1-minute intervals (ie when seconds is 00). Any mode may transition to any other mode.
## A very unscientific manual trial-and-error approach:First, I generated visibility intervals for the target and the ground station with this convenient Python package (https://rhodesmill.org/skyfield), assuming minimum elevation angle of 30 degrees for no particular reason. With imaging and downlink intervals in place, I filled in the remaining time with sun_point.I then submitted a schedule of the transitions between mode intervals.
Upon submitting a proposed schedule, the simulator runs and prints out relevant state at one-minute intervals (eg reaction wheel RPM, battery levels, component temperature, etc). At the end, it outputs either success, or the first thing that caused failure.Possible failure reasons include:- Target was not visible during imaging mode- Target was not illuminated by the Sun during imaging mode- Ground station was not visible during downlink mode- Battery fell below 10% capacity- Imager temperature too high- Comms temperature too high- Reaction wheels saturated- Did not collect and downlink enough data
After submitting this preliminary schedule, which failed, I removed imaging passes that were not illuminated. (I suppose I could have filtered them out in the previous step when generating visibility intervals, but I didn't).
The reaction wheels will saturate over time, so I added wheel_desaturates as needed. The imager and comms will drain battery and overheat if left on for too long, so I shortened those intervals as needed.
When I finally got to the end of the 48-hour window without breaking the spacecraft, the schedule failed because it didn't collect enough data. I was puzzled because it seemed like there weren't enough overflights to do so, when I realized that I had arbitrarily assumed a minimum elevation angle of 30 degrees. It turns out that the minimum elevation angle was more like 0.
Adding in some more imaging and downlink intervals with lower elevation angle, I managed to generate a successful schedule, and was rewarded with cute ASCII art of a satellite and the flag.
In conclusion, doing squeaky wheel scheduling by hand really makes me appreciate the existence of automated planning and scheduling.
Final schedule:```2020-04-22T00:00:00Z sun_point2020-04-22T09:29:00Z imaging2020-04-22T09:35:00Z sun_point2020-04-22T10:47:00Z data_downlink2020-04-22T10:52:00Z sun_point2020-04-22T11:07:00Z imaging2020-04-22T11:08:00Z sun_point2020-04-22T18:00:00Z wheel_desaturate2020-04-22T19:00:00Z sun_point2020-04-22T22:22:00Z data_downlink2020-04-22T22:27:00Z sun_point2020-04-23T00:00:00Z data_downlink2020-04-23T00:03:00Z sun_point2020-04-23T07:00:00Z wheel_desaturate2020-04-23T08:00:00Z sun_point2020-04-23T09:33:00Z data_downlink2020-04-23T09:35:00Z sun_point2020-04-23T09:50:00Z imaging2020-04-23T09:57:00Z sun_point2020-04-23T11:12:00Z data_downlink2020-04-23T11:14:00Z sun_point2020-04-23T15:00:00Z wheel_desaturate2020-04-23T16:00:00Z sun_point2020-04-23T22:44:00Z data_downlink2020-04-23T22:48:00Z sun_point```
Relevant code:
```from skyfield.api import EarthSatellite, Topos, loadimport mathimport numpy
ts = load.timescale(builtin=True)
satname = "USA 224"line1 = "1 37348U 11002A   20053.50800700  .00010600  00000-0  95354-4 0    09"line2 = "2 37348  97.9000 166.7120 0540467 271.5258 235.8003 14.76330431    04"satellite = EarthSatellite(line1, line2, satname, ts)
st = ts.utc(2020, 4, 22, 0, 0, 0)et = ts.utc(2020, 4, 24, 0, 0, 0)
#visibility intervalstarget = Topos('35.234722 N', '53.920833 E')t, events = satellite.find_events(target, st, et, altitude_degrees=0.0)print("Target visibility intervals")for ti, event in zip(t, events):    name = ('rise', 'culminate', 'set')[event]    print(ti.utc_jpl(), name)
#downlink intervalsgs = Topos('64.977488 N', '147.510697 W')t, events = satellite.find_events(gs, st, et, altitude_degrees=5.0)print("Ground station passes")for ti, event in zip(t, events):    name = ('rise', 'culminate', 'set')[event]    print(ti.utc_jpl(), name)```