Satellites Overhead Visualization Notebook

This Jupyter Notebook is meant to illustrate how the SatChecker FOV API (https://satchecker.cps.iau.org/fov) can be used with Python/Plotly to visualize all satellites overhead a given location.

[36]:
import numpy as np
import plotly.graph_objects as go
import plotly.io as pio
import requests

pio.renderers.default = "notebook"

%matplotlib inline
[37]:
# Set up the FOV query parameters
julian_date = 2460613.194803  # Example JD
latitude = -33
longitude = -117
elevation = 100 # meters
min_altitude = 0
illuminated_only = "false"
min_range = 0
max_range = 1500000
# Make the API request
response = requests.get(
    f"https://dev.satchecker.cps.iau.noirlab.edu/fov/satellites-above-horizon/?latitude={latitude}&longitude={longitude}&elevation={elevation}&julian_date={julian_date}&illuminated_only={illuminated_only}&min_altitude={min_altitude}&min_range={min_range}&max_range={max_range}",
    timeout=60
)
data = response.json()
[38]:
# Extract satellite positions
satellites = {}

for sat_data in data['data']:
    sat_key = f"{sat_data['name']} ({sat_data['norad_id']})"

    if sat_key not in satellites:
        satellites[sat_key] = []

    # Add ra, dec, and julian_date
    satellites[sat_key].append([
        sat_data['ra'],
        sat_data['dec'],
        sat_data['julian_date']
    ])

# print total number of satellites
print(f"Total number of satellites: {len(data['data'])}")

Total number of satellites: 2684
[39]:
fig = go.Figure()
ranges = [sat_data['range_km'] for sat_data in data['data']]
min_range = min(ranges)
max_range = max(ranges)
mid_range = 10**(np.log10(min_range) + (np.log10(max_range) - np.log10(min_range))/2)


# Add scatter points for satellites
for sat_data in data['data']:
    alt = sat_data['altitude']
    az = sat_data['azimuth']
    range_km = sat_data['range_km']
    sat_name = f"{sat_data['name']} ({sat_data['norad_id']})"

    r = 90 - alt  # Inverse for polar plot
    theta = az


    fig.add_trace(go.Scatterpolar(
        r=[90 - sat_data['altitude']],
        theta=[sat_data['azimuth']],
        mode='markers',
        marker=dict(
            size=3,
            color=[np.log10(range_km)],
            cmin=np.log10(min_range),
            cmax=np.log10(max_range),
            colorscale='Turbo',
            showscale=True if range_km == ranges[0] else False,
            colorbar=dict(
            title="Range (km)",
            # Show actual range values at min, middle, and max points
            ticktext=[f"{min_range:.0f}",
                     f"{mid_range:.0f}",
                     f"{max_range:.0f}"],
            tickvals=[np.log10(min_range),
                     np.log10(mid_range),
                     np.log10(max_range)],
            tickmode="array"
        )
        ),
        hovertext=f"Name: {sat_name}<br>"
                 f"Altitude: {sat_data['altitude']:.1f}°<br>"
                 f"Azimuth: {sat_data['azimuth']:.1f}°<br>"
                 f"Range: {range_km:.1f} km",
        hoverinfo='text',
        showlegend=False
    ))

# Configure layout
fig.update_layout(
    polar=dict(
        radialaxis=dict(
            range=[0, 91],
            ticktext=[f"{90-tick}°" for tick in [0, 10, 20, 30, 40, 50, 60, 70, 80, 90]],  # noqa: E501
            tickvals=[0, 10, 20, 30, 40, 50, 60, 70, 80, 90],
            tickmode="array",
            gridcolor="lightgray",
            showgrid=True,
            linewidth=0.5,
        ),
        angularaxis=dict(
            ticktext=['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW'],
            tickvals=np.arange(0, 360, 45),
            direction='clockwise',
            gridcolor="lightgray",
            showgrid=True,
            linewidth=0.5,
        ),
        bgcolor='white',
    ),
    showlegend=False,
    title='All-Sky View with Alt/Az Grid',
    width=700,
    height=700,

)

fig.show()