You are on page 1of 2

Simulating Starlink

The goal here is to render the most accurate representation of how Starlink will actually look in the night sky, to all the key observers: eyes, cameras,
telescopes

Video flythrough

Tl;dr: Pretend starlink satellites are like stars in orbit around earth, and they reflect sunlight (more at some angles than others)

In [21]: # imports
from PIL import Image
import numpy as np

# to show one of the gifs


def show_gif(fname):
import base64
from IPython import display
with open(fname, 'rb') as fd:
b64 = base64.b64encode(fd.read()).decode('ascii')
return display.HTML(f'<img src="data:image/gif;base64,{b64}" />')

In [3]: Image.open("./england_42000.png")

Out[3]:

Outline

1. Photometry
Getting a rendered screen brightness of astronomical objects
Simulating Starlink satellites' brightness
Reflectance mapping
Calibrating to best available observed magnitudes
Atmospheric extinction
Earth surface and atmosphere brightness
Calibration to approximate real-world eyes, cameras, telescopes
2. Dynamics
Ephemeris and orbital calculation of satellites
3. Limitations and other notes
4. Sources

1. Photometry

Getting a rendered screen brightness of astronomical objects

Apparent magnitude 𝑣 𝑚𝑎𝑔 is the standard numerical scale of star brightness established by Hipparchus; a brightness sensitivity scale is calibrated to the
human eye
Absolute magnitude 𝑎𝑏𝑠𝑚𝑎𝑔 is defined as the apparent magnitude of a light source at a distance 10 parsec from the observer
The relationship between magnitude and distance comes from this and is:
𝑎𝑏𝑠𝑚𝑎𝑔 = 𝑣𝑚𝑎𝑔 − 5𝑙𝑜𝑔10 (𝑑𝑝𝑐 ) + 5 = 𝑣𝑚𝑎𝑔 + 5(1 − 𝑙𝑜𝑔10 (𝑑𝑝𝑐 ))

To position objects in space, we use kilometers, not parsecs, so a conversion factor is introduced to convert km to parsec
𝑘𝑚𝑝𝑝 = 30, 856, 775, 812, 800/𝑓𝑟𝑎𝑐𝑘𝑖𝑙𝑜𝑚𝑒𝑡𝑒𝑟𝑠𝑝𝑎𝑟𝑠𝑒𝑐
Then these equations are used to convert between absolute and apparent magnitude
𝑎𝑏𝑠𝑚𝑎𝑔 = 𝑣𝑚𝑎𝑔 + 5(1 − 𝑙𝑜𝑔10 (𝑑𝑝𝑐 )) = 𝑣𝑚𝑎𝑔 + 5(1 − 𝑙𝑜𝑔10 (𝑑𝑘𝑚 /𝑘𝑚𝑝𝑝 ))
𝑣𝑚𝑎𝑔 = 𝑎𝑏𝑠𝑚𝑎𝑔 − 5(1 − 𝑙𝑜𝑔10 (𝑑𝑘𝑚 /𝑘𝑚𝑝𝑝 ))
To convert from magnitudes to pixel brightnesses, we use the relative brightness calculations found here, implemented as follows:

m_ref = -1.46 # reference apparent magnitude, Sirius

# M = Absolute magnitude (input)

# Apparent magnitude (https://en.wikipedia.org/wiki/Absolute_magnitude#Apparent_magnitude)


V = M - 5*(1 - log10(d_km / km_per_pc))

# Brightness value (https://en.wikipedia.org/wiki/Apparent_magnitude#Calculations)


B = 10**(0.4*(m_ref- m))

So, B describes how many multiples brighter or dimmer an object is than the reference star Sirius, as seen from Earth.

Simulating Starlink satellites' brightness

During orbit raise, Starlink satellites' solar panels are oriented like this:

In [8]: Image.open("orbit_raise.png")

Out[8]:

In final orbits, Starlink satellites are oriented like this:

In [9]: Image.open("final_orbit.png")

Out[9]:

As a brightness mitigation, Starlink have implemented a visor for the satellite, shading the back of the solar panel and bottom of spacecraft.

Before

In [12]: Image.open("without_visorsat.png")

Out[12]:

After

In [13]: Image.open("with_visorsat.png")

Out[13]:

There are SO MANY varying observations of Starlink's apparent magnitude in different conditions. Mallama in Source 1 showed that neither a phase-angle
model nor a flat-panel model provides a good fit to observations.

With that said, these are the best-available estimates of Starlink's magnitude (adjusted to 550km viewing distance) at each phase of deployment (Orbit-raising,
on-station) and each design (Original, DarkSat, VisorSat)

Phase Original DarkSat VisorSat

Orbit-raising 5.42 [5] 6.2 [5] 6.68[3]

On-station 2.8 [2] to 4.63 [1] ~4.93 5.59[4]

Atmospheric extinction

As the satellites pass into the earth's shadow, the light reaching them also passes through the earth's atmosphere. We simulate this using the atmospheric
scattering simulation technique mentioned here, but modify it to consider the spacecraft as the camera.

This has the effect of coloring the starlink trails red as they pass into earth's shadow, which can be seen in this image (magnitudes inaccurate here)

In [24]: Image.open("exaggerated_extinction.png")

Out[24]:

Albedo / reflectance mapping

To get a "good" measure of how bright the satellite is at various camera angles and sun angles, I did the following:

Using Unreal Engine 4.26, I applied realistic materials to a high poly 3D model of the Starlink satellite (pictured above)
I then took 14,400 (120x120) photos of this 3D model at different rotations:
120 angles x in [0-360)° angle between camera direction and spacecraft solar panel
120 angles y in [0-360)° angle between sun direction and spacecraft solar panel

In [23]: show_gif("StarlinkRotations.gif")

Out[23]:

x is the angle between the camera (normalized position relative to spacecraft) and the spacecraft (normalized negative spacecraft position (points to
earth)). Specifically, when in parking orbit configuration:
0 [deg] = camera is looking directly at the top of base plate, down the solar panel, towards earth
90 [deg] = camera is looking directly at the back of the solar panel
180 [deg] = camera is looking directly at the bottom of the base plate
270 [deg] = camera is looking directly at the front of the solar panel
360 [deg] = camera is looking directly at the top of base plate, down the solar panel, towards earth (again)
y is the angle between the spacecraft and the sun.
0 [deg] = sunlight is shining directly onto the front of the solar panel
90 [deg] = sunlight is shining directly onto the bottom of the base plate (antenna face)
180 [deg] = sunlight is shining directly onto the back of the solar panel
270 [deg] = sunlight is shining directly onto the top of the base plate
360 [deg] = sunlight is shining directly onto the front of the solar panel (again)

Albedo / reflectance mapping lookup tables

Now, to create something usable in photometric simulation, I took the norm() (mean square) of all 14,400 images

Example:

In [12]: # (full code in `PhaseAnalysis.ipynb`)


img = Image.open("NSKY StarlinkSatPhases_120_120-4209.jpg")
norm = np.linalg.norm(np.array(img))
print("Norm of this image:", norm)
img

Norm of this image: 317382.6336206819

Out[12]:

In [13]: # (full code in `PhaseAnalysis.ipynb`)


img = Image.open("NSKY StarlinkSatPhases_120_120-4216.jpg")
norm = np.linalg.norm(np.array(img))
print("Norm of this image:", norm)
img

Norm of this image: 85910.04859153555

Out[13]:

Then I created albedo/reflectance lookup tables for all designs and phases, where:

Values along the width are x and values along the height are y both map [0, size of image) to [0,360) degrees or [0, 2/𝑝𝑖) radians
The (0,0) point is the top left of the images, and the (120,120) point is bottom right
The brightness of any pixel at (x,y) gives the norm measured at that point (normalized so that all values lie within [0,1], or, [0,255] in the case of 8-bit
uint images

In [28]: Image.open("../../assets/data/starlink/flarefullscale/FinalOrbit_Starlink.tiff")

Out[28]:

In [30]: Image.open("../../assets/data/starlink/flarefullscale/FinalOrbit_Visorsat.tiff")

Out[30]:

In [36]: Image.open("../../assets/data/starlink/flarefullscale/OrbitRaise_Starlink.tiff").resize((120,120))

Out[36]:

In [37]: Image.open("../../assets/data/starlink/flarefullscale/OrbitRaise_Visorsat.tiff").resize((120,120))

Out[37]:

In the first two images, the brightness in the bottom and top right is the solar panel reflecting sunlight. In the second two images (orbit-raise config), that
solar panel changes position, moving to the bottom left and bottom right.
The brightest part of the spacecraft (across all conditions) is the bottom (antennas face).
You can see that Visorsat strongly decreases the brightness of the base plate (antennas face), and (to a lesser degree) the solar panel back

Without accounting for reflectance, a wide-angle ~30sec long exposure in rural England at 02:50 Dec 29 2020 looks like this (all Visorsats)

In [18]: Image.open("extinction_0250am_visorsat.png")

Out[18]:

But using the reflectance lookup as a multiplier on the magnitude of the spacecraft looks like this (all Visorsats)

In [19]: Image.open("reflectance_AND_extinction_0250am_visorsat.png")

Out[19]:

Calibrating to best available observed magnitudes

Using only the lookup tables and a reference (absolute) magnitude of -0.95 (hand-tuned), we can get close to observed magnitudes for the 'original' starlink
satellites

Phase Original VisorSat

Orbit-raising 3.33 3.46

On-station 4.06 4.67

Clearly, VisorSat numbers here are way off. But, by hand-calibrating absolute magnitudes to fit observations (tedious, yes!), we get:

I get (simulated observation):

@ 550 km -- the hand-calibrated magnitudes

Phase Original VisorSat

Orbit-raising ~3.68 ~6.69

On-station ~4.48 ~7.79

By using:

@ 10pc -- the hand-calibrated magnitudes

Phase Original VisorSat

Orbit-raising 57.0 60.0

On-station 57.0 60.0

Earth surface and atmosphere brightness

For earth's night light illumination, I am using the NPP-VIIRS data from 2016 (in 6x 16384x8192 32bit tiles (yes, oof!)). The values in that dataset are
nanoWatts/cm2/sr, multiplied by 1e9, so Watts/cm2/sr
I would like (eventually) to match that to the magnitudes established in the Photometry section, but haven't the time to.
Instead, I have visually approximated the brightness by using reference images, like this one:

In [20]: Image.open("ISS_UK.jpg")

Out[20]:

Calibration to approximate real-world eyes, cameras, telescopes

All the graphics programs (earth, starlink sats, etc...) are fed into a 32bit frame buffer, which is where I do tonemapping, gain, and gamma adjustments, and
then that is drawn to the 8bit final screen buffer.

To match the images coming from the program to real world as best as possible, I calibrated the simulation gain, gamma to real-world timelapses I've taken:

In [41]: A = Image.open('../calibration/frame_358.jpg')
A

Out[41]:

In [42]: B = Image.open('../calibration/calibration_frame358_gain_gamma_match4.png')
B

Out[42]:

2. Dynamics
Ephemeris and orbital calculation of satellites

Ephemeris is provided by JPL's de438 ephemeris


Planetary rotation is provided by the Report of the IAU Working Group on Cartographic Coordinates and Rotational Elements: 2009
All starlink satellites are assumed to have circular orbits and set their velocity according to their altitude:

𝑑𝜃 1
𝑑𝑡
=

((𝑎𝑙𝑡𝑖𝑡𝑢𝑑𝑒+𝑟𝑒𝑎𝑟𝑡ℎ )∗1,000)3
𝜇

3. Limitations and other notes


We don't take into account all the view and sun angles that can occur when the spacecraft's solar panel is not directly pointed at the sun. If it is in a knife-
edge orientation, we don't capture the reduction in brightness. The knife-edge orientation is one of SpaceX's planned mitigations for astronomy.
Earth atmosphere scattering doesn't account for multiscattering which prolongs sky brightness after sunset.
The Starlink magnitudes here are hand calibrated and may not be what the satellites really reflect. Source [1] showed that satellite flares can increase
brightness by 10,000x (4 magnitudes), and that behavior is not modeled in this simulation. I thought perhaps the reflectance mapping would show this flare
effect, but the increases in reflectance lookups are relatively smooth, not sharply discontinuous like I would expect for a flare situation.

4. Sources
1. Starlink Satellite Brightness Before VisorSat

"The mean of 830 visual magnitudes adjusted to a distance of 550 km (the operational altitude) is 4.63 +/-0.02. The data on DarkSat, the low-albedo
satellite, indicate that it is fainter than the others by 1.6 (=6.23) magnitudes, however, there is considerable uncertainty in this value due to the small
number of observations. Some satellites were observed to flare by 10,000 times their normal brightness"
10,000x normal brightness is -4.0 on the magnitude scale meaning the flare magnitude was about 0.63
All above measurements are NOT visorsats, only STARLINK-1436 onward have visors
Flare observations:

Observer Starlink # UTC Date UTC Time Magnitude

Amorim 1288 2020-04-08 22:40:10 -6

Amorim 1255 2020-04-10 21:37:45 -7

Amorim 1277 2020-04-10 22:13:17 -3

Amorim 1262 2020-04-10 22:19:47 -3.5

Young 1258 2020-04-17 02:27:08 -4

Young 1296 2020-04-17 02:27:22 -8

Young 1292 2020-04-17 02:27:50 -8

Young 1260 2020-04-17 02:28:22 -4

All the above satellites were launched 2020-03-18, about a month prior to observations, so they would be in orbit raising. This is corroborated by "These
satellites were between altitudes 380 and 425 km"[1]
Flaring magnitudes averaged: -5.4375

2. A Flat-Panel Brightness Model for the Starlink Satellites and Measurement of their Absolute Visual Magnitude

On-station observations

Cat # Cat Name UTC Date UTC Time Magnitude

44747 STARLINK-1042 2020-Feb-27 23:45:00 5.2

44761 STARLINK-1056 2020-Feb-27 23:50:00 5.6


44759 STARLINK-1054 2020-Feb-27 23:55:00 5.8

44767 STARLINK-1062 2020-Feb-28 00:00:00 6.4

44758 STARLINK-1053 2020-Feb-28 00:05:00 5.9

44766 STARLINK-1061 2020-Feb-28 00:10:00 6.6

44771 STARLINK-1067 2020-Feb-28 00:15:00 6.5

44763 STARLINK-1058 2020-Feb-28 00:20:00 6.8

44761 STARLINK-1056 2020-Mar-15 23:59:30 4.5

44759 STARLINK-1054 2020-Mar-16 00:04:30 4.7

44767 STARLINK-1062 2020-Mar-16 00:10:00 5.0

44758 STARLINK-1053 2020-Mar-16 00:15:00 5.5

44766 STARLINK-1061 2020-Mar-16 00:20:00 5.5

44763 STARLINK-1058 2020-Mar-16 00:30:30 6.1

Derived absolute magnitude @ 1,000km = 4.1 +/- 0.1.


Measurements above and estimates are for orbit raising

3. VisorSat - 9 Orbit-raising Observations

During orbit raising, average magnitude 7.98 |Starlink | Date | UTC | 1km Mag.| PA| |--------:|------:|-------:|-----------:|---:| |1522 |2020-Aug-18 | 00:36| 7.23|
68| |1523 |2020-Aug-19 | 17:56| 8.35| 86| |1526 |2020-Aug-18 | 18:25| 7.97| 97| |1534 |2020-Aug-19 | 18:07| 7.86| 99| |1555 |2020-Aug-18 | 00:31| 7.70| 70|
|1576 |2020-Aug-19 | 18:00| 8.38| 90| |1580 |2020-Aug-19 | 18:03| 8.27| 92| |1582 |2020-Aug-18 | 00:34| 7.53| 69| |1584 |2020-Aug-19 | 17:53| 8.31| 83| |1591
|2020-Aug-19 | 18:05| 8.15| 94|

4. VisorSat - 47 On-station Observations

The average of the 47 magnitudes, after adjustment to the standard 1,000 km distance, is 6.89 with a standard deviation of the mean of 0.09.

Starlink UTC Mag.

1581 0:34:48 7.5

1526 0:40:11 7.5

1584 0:45:10 7.5

1523 0:50:10 6.8

1576 0:55:10 5.8

1580 1:00:08 7.0

1591 1:05:07 6.0

1534 1:10:09 6.1

1544 1:15:07 5.5

1565 1:20:08 4.0

1560 1:25:07 4.2

1556 1:34:00 6.0

1557 1:39:10 6.2

1567 1:44:20 6.3

1558 1:49:00 6.5

1569 1:53:50 6.7

1555 1:58:20 6.2

1582 2:03:15 5.9

1522 2:07:50 6.0

1581 2:12:30 7.0

Starlink UTC Mag.

1584 0:39:46 5.8

1523 0:44:44 5.8

1576 0:49:43 6.3

1580 0:54:41 7.1

1591 0:59:42 5.5

1534 1:04:42 5.8

1544 1:09:41 6.1

1565 1:14:39 6.2

1560 1:19:38 5.5

1556 1:29:25 5.5

1557 1:34:03 6.2

1567 1:38:58 6.5

Starlink UTC Mag.

1552 0:19:16 7.1

1581 0:27:15 5.7

1526 0:29:20 5.8

1584 0:34:19 6.0

1523 0:39:17 6.2

1576 0:44:16 5.5

1580 0:49:14 6.9

1591 0:54:14 5.7

1534 0:59:12 5.8

1544 1:04:11 6.0

1565 1:09:08 5.9

1560 1:14:07 6.3

1556 1:23:30 6.0

1557 1:28:20 6.0

1567 1:33:00 6.8

5. First observations and magnitude measurement of Starlink’s Darksat

During orbit-raise
Darksat: 7.46 ± 0.04 at a range of 976.50 km
STARLINK-1113, an estimated Sloan g′ magnitude of 6.59 ± 0.05 at a range of 941.62 km

In [ ]:

You might also like