<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://cconstant.cc/feed.xml" rel="self" type="application/atom+xml" /><link href="https://cconstant.cc/" rel="alternate" type="text/html" /><updated>2026-09-09T03:45:56-07:00</updated><id>https://cconstant.cc/feed.xml</id><title type="html">Charles Constant</title><subtitle>PhD Space Flight Dynamics and Geodesy</subtitle><author><name>Charles Constant</name><email>zcesccc@ucl.ac.uk</email></author><entry><title type="html">Twitter Satellite Constellation Visualization Bot</title><link href="https://cconstant.cc/posts/2023-05-14-ConstellationBot" rel="alternate" type="text/html" title="Twitter Satellite Constellation Visualization Bot" /><published>2023-05-14T00:00:00-07:00</published><updated>2023-05-14T00:00:00-07:00</updated><id>https://cconstant.cc/posts/ConstellationBot</id><content type="html" xml:base="https://cconstant.cc/posts/2023-05-14-ConstellationBot"><![CDATA[<h2 id="note">NOTE:</h2>
<p>This post was intended as the start of a series on how to make a Twitter constellation visualization bot. However, recent updates to the Twitter API have meant that the repetitive nature of the posts have blocked the bot so I will be stopping this effort. I tried to modify my code to work with the new API but it has proved too time-consuming. Instead, I will be posting these GIFs to my personal website on a dedicated page.</p>

<h2 id="introduction">Introduction</h2>

<p>Inspired by the pioneering work of Jonathan McDowell’s <a href="www.planet4589.org">Jonathan’s Space Report</a> and T.S. Kelso’s <a href="https://celestrak.org/">CelesTrak</a>, I embarked on a journey to create a unique project of my own. Over the Easter holidays, I developed a Twitter bot that provides daily updates on the current state of satellite mega-constellations. While my bot may not match the scale of Kelso and McDowell’s contributions, it’s my modest attempt to make satellite data more accessible and engaging for a broader audience.</p>

<p>Now that I have a working “version 1.0” of this Twitter bot, I’d like to share an overview of its creation process. Whether you’re interested in replicating it, contributing to the code, or simply curious about its inner workings, I hope you find this post informative. If you are curious to see it in action you can check out the Twitter bot here: <a href="https://twitter.com/CharlesPlusC">ConstellationBot</a></p>

<p align="center">
  <img width="550" height="600" src="/images/geom_planet_23_04_17.gif" alt="Example Constellation Plot" />
</p>

<h3 id="about-the-bot">About the Bot</h3>

<p>The bot serves as a reliable resource, delivering daily status updates on the largest satellite mega-constellations - at present, it’s tracking the top 7. It’s designed in Python, utilizing the <code class="language-plaintext highlighter-rouge">Tweepy</code> library for interacting with the <a href="https://developer.twitter.com/en/docs/twitter-api">Twitter API</a>. The bot functions autonomously through GitHub Actions and is activated by a <code class="language-plaintext highlighter-rouge">cron</code> job that triggers the Python script once daily. Future plans encompass augmenting its competencies with added features and visualizations.</p>

<p>This bot’s mission is to furnish a concise overview of the current state of major players in the arena, expediting the process of staying abreast with the newest advancements. Moreover, it visually illustrates constellation geometry, contributing critical insights into constellation operations such as orbit raising, deorbiting, anomalies, and shifts in geographical coverage. I’m in the process of enhancing a statistics module to accompany each visualization with relevant stats.</p>

<p>The code I share in this post serves only to shed light on the logic that fuels these scripts- I will be sharing the entire code repository in an upcoming post. For now, I hope this will inspire you to dive deeper, and perhaps use this method as a scaffold for you to create your own unique applications.</p>

<h3 id="method-overview">Method Overview</h3>

<p>I will outline a broad overview and share some key functions that I used below.</p>

<ol>
  <li>
    <p><strong>Selection of Constellations:</strong> I chose to track the seven largest operational constellations: Iridium, Starlink, OneWeb, Planet, Spire, and Swarm.</p>
  </li>
  <li><strong>Fetching Latest TLEs:</strong> Fetch the latest TLEs from Space-Track.org. I use the Python package <code class="language-plaintext highlighter-rouge">spacetrack</code> to do this. You will have to make a Space-Track account to use the API to fetch the latest TLEs for the selected constellations. Then you will manually have to do some digging around to find the constellation IDs for each constellation. For the ones I selected this ended up being:
    <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">constellation_cat_names</span> <span class="o">=</span> <span class="p">{</span><span class="s">"starlink"</span><span class="p">:</span> <span class="s">"STARLINK"</span><span class="p">,</span> <span class="s">"oneweb"</span><span class="p">:</span> <span class="s">"ONEWEB"</span><span class="p">,</span> <span class="s">"planet"</span><span class="p">:</span> <span class="s">"FLOCK"</span><span class="p">,</span> <span class="s">"swarm"</span><span class="p">:</span> <span class="s">"SpaceBEE"</span><span class="p">,</span> <span class="s">"spire"</span><span class="p">:</span> <span class="s">"LEMUR"</span><span class="p">,</span> <span class="s">"iridium"</span><span class="p">:</span> <span class="s">"IRIDIUM"</span><span class="p">}</span> 
</code></pre></div>    </div>
  </li>
  <li>
    <p><strong>Propagating TLEs:</strong> Use the Python package <code class="language-plaintext highlighter-rouge">sgp4</code> to propagate the TLEs if required. Two of my visualizations (the constellation geometry and ground tracks), require the state to be propagated forward. I do this for one orbital revolution (or 2pi radians of argument of latitude). The SGP4 propagator will return Earth-Centred Inertial Coordinates in the TEME (True Equator, Mean Equinox) reference frame.</p>

    <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="kn">import</span> <span class="nn">logging</span>
 <span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">List</span><span class="p">,</span> <span class="n">Union</span>
 <span class="kn">from</span> <span class="nn">sgp4.api</span> <span class="kn">import</span> <span class="n">Satrec</span>

 <span class="k">def</span> <span class="nf">sgp4_prop_TLE</span><span class="p">(</span><span class="n">TLE</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">jd_start</span><span class="p">:</span> <span class="nb">float</span><span class="p">,</span> <span class="n">jd_end</span><span class="p">:</span> <span class="nb">float</span><span class="p">,</span> <span class="n">dt</span><span class="p">:</span> <span class="nb">float</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">List</span><span class="p">[</span><span class="n">List</span><span class="p">[</span><span class="n">Union</span><span class="p">[</span><span class="nb">float</span><span class="p">,</span> <span class="nb">tuple</span><span class="p">]]]:</span>
     <span class="s">"""
     Given a TLE, a start time, end time, and time step, propagate the TLE and return the time-series of Cartesian coordinates, 
     and accompanying time-stamps (Modified Julian Day). This is a wrapper for the SGP4 routine in the sgp4.api package (Brandon Rhodes).

     Args:
         TLE (str): TLE to be propagated.
         jd_start (float): Start time of propagation in Julian Date format.
         jd_end (float): End time of propagation in Julian Date format.
         dt (float): Time step of propagation in seconds.

     Returns:
         list: A list of lists containing the time-series of Cartesian coordinates, and accompanying time-stamps (MJD).
        
     Raises:
         ValueError: If jd_start is greater than jd_end.
     """</span>
     <span class="k">if</span> <span class="n">jd_start</span> <span class="o">&gt;</span> <span class="n">jd_end</span><span class="p">:</span>
         <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="s">'jd_start must be less than jd_end'</span><span class="p">)</span>

     <span class="n">ephemeris</span> <span class="o">=</span> <span class="p">[]</span>
     <span class="n">dt_jd</span> <span class="o">=</span> <span class="n">dt</span><span class="o">/</span><span class="mi">86400</span>
     <span class="n">split_tle</span> <span class="o">=</span> <span class="n">TLE</span><span class="p">.</span><span class="n">split</span><span class="p">(</span><span class="s">'</span><span class="se">\n</span><span class="s">'</span><span class="p">)</span>
     <span class="n">s</span> <span class="o">=</span> <span class="n">split_tle</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
     <span class="n">r</span> <span class="o">=</span> <span class="n">split_tle</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
     <span class="n">fr</span> <span class="o">=</span> <span class="mf">0.0</span>
     <span class="n">satellite</span> <span class="o">=</span> <span class="n">Satrec</span><span class="p">.</span><span class="n">twoline2rv</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">r</span><span class="p">)</span>

     <span class="n">time</span> <span class="o">=</span> <span class="n">jd_start</span>
     <span class="k">while</span> <span class="n">time</span> <span class="o">&lt;</span> <span class="n">jd_end</span><span class="p">:</span>
         <span class="n">error</span><span class="p">,</span> <span class="n">position</span><span class="p">,</span> <span class="n">velocity</span> <span class="o">=</span> <span class="n">satellite</span><span class="p">.</span><span class="n">sgp4</span><span class="p">(</span><span class="n">time</span><span class="p">,</span> <span class="n">fr</span><span class="p">)</span>
         <span class="k">if</span> <span class="n">error</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">:</span>
             <span class="n">logging</span><span class="p">.</span><span class="n">error</span><span class="p">(</span><span class="s">'Satellite position could not be computed for the given date'</span><span class="p">)</span>
             <span class="k">break</span>
         <span class="k">else</span><span class="p">:</span>
             <span class="n">ephemeris</span><span class="p">.</span><span class="n">append</span><span class="p">([</span><span class="n">time</span><span class="p">,</span><span class="n">position</span><span class="p">,</span> <span class="n">velocity</span><span class="p">])</span>
         <span class="n">time</span> <span class="o">+=</span> <span class="n">dt_jd</span>

     <span class="k">return</span> <span class="n">ephemeris</span>
</code></pre></div>    </div>
  </li>
  <li><strong>Plotting Positions:</strong> You will need to store the (/time series of) positions for each satellite after each TLE is propagated. Then you can use this data to plot the satellite positions in 3D. I use <code class="language-plaintext highlighter-rouge">matplotlib</code> to plot the satellite positions in 3D and I use the <code class="language-plaintext highlighter-rouge">cartopy</code> module for the map under the ground tracks. If you want to plot the ground tracks, you will have to convert the Earth Centred Inertial(ECI) coordinates into Earth Centred Earth Fixed (ECEF)coordinates. Then you will have to convert these to latitude and longitude to project them onto a map of the Earth. The <code class="language-plaintext highlighter-rouge">astropy.coordinates</code> provides methods to do both of these conversions accurately and painlessly.
    <ol>
      <li><strong>ECI to ECEF:</strong> Use the <code class="language-plaintext highlighter-rouge">CartesianRepresentation()</code> class and make sure you convert from <em>GCRS</em> to <em>ITRS</em>.</li>
      <li><strong>ECEF to Lat/Long:</strong> Use the <code class="language-plaintext highlighter-rouge">Transformer()</code> class to convert from <em>EPSG:4978</em> to <em>EPSG:4326</em>.</li>
    </ol>
  </li>
  <li>
    <p><strong>Animating Plots:</strong> To animate these plots I rotate the plots by 5 degrees about the z-axis and then save each new plot as a numbered frame (.png format ). I then use the <code class="language-plaintext highlighter-rouge">PIL</code> module to convert the pngs into a gif. For the large constellations this becomes pretty computationally intensive so I have included some use of the <code class="language-plaintext highlighter-rouge">multiprocessing</code> module to speed things up. This parallelizes the plotting of each frame- however for Starlink this still takes around 30 minutes (and fails in Github Actions)…</p>

    <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="kn">import</span> <span class="nn">logging</span>
 <span class="kn">import</span> <span class="nn">multiprocessing</span> <span class="k">as</span> <span class="n">mp</span>
 <span class="kn">import</span> <span class="nn">os</span>
 <span class="kn">from</span> <span class="nn">PIL</span> <span class="kn">import</span> <span class="n">Image</span>
 <span class="kn">import</span> <span class="nn">time</span>
 <span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Any</span>

 <span class="k">def</span> <span class="nf">generate_geom_gif</span><span class="p">(</span><span class="n">const</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="bp">None</span><span class="p">:</span>
     <span class="s">"""
     Generate a gif of the geometry of a constellation.

     Args:
         const (str): The name of the constellation.

     Raises:
         FileNotFoundError: If an image file does not exist.
         PermissionError: If the program does not have permission to delete an image file.
     """</span>
     <span class="n">const_ephemerides</span><span class="p">,</span> <span class="n">constellation_img_paths</span> <span class="o">=</span> <span class="n">process_geom_data</span><span class="p">(</span><span class="n">const</span><span class="p">)</span>

     <span class="n">max_workers</span> <span class="o">=</span> <span class="mi">4</span> <span class="c1"># Max number of processes that can run in GitHub Actions is 5. 
</span>
     <span class="n">gif_folder</span> <span class="o">=</span> <span class="n">os</span><span class="p">.</span><span class="n">path</span><span class="p">.</span><span class="n">join</span><span class="p">(</span><span class="s">'images/constellation_anim/gifs/'</span><span class="p">,</span> <span class="n">const</span><span class="p">)</span>
     <span class="n">images_folder</span> <span class="o">=</span> <span class="n">os</span><span class="p">.</span><span class="n">path</span><span class="p">.</span><span class="n">join</span><span class="p">(</span><span class="s">'images/constellation_anim/current_geometry/'</span><span class="p">,</span> <span class="n">const</span><span class="p">)</span>
     <span class="n">os</span><span class="p">.</span><span class="n">makedirs</span><span class="p">(</span><span class="n">gif_folder</span><span class="p">,</span> <span class="n">exist_ok</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>

     <span class="k">with</span> <span class="n">mp</span><span class="p">.</span><span class="n">Pool</span><span class="p">(</span><span class="n">processes</span><span class="o">=</span><span class="n">max_workers</span><span class="p">)</span> <span class="k">as</span> <span class="n">pool</span><span class="p">:</span>
         <span class="n">pool</span><span class="p">.</span><span class="nb">map</span><span class="p">(</span><span class="n">create_frame</span><span class="p">,</span> <span class="p">[(</span><span class="n">az</span><span class="p">,</span> <span class="n">const</span><span class="p">,</span> <span class="n">const_ephemerides</span><span class="p">,</span> <span class="n">constellation_img_paths</span><span class="p">)</span> <span class="k">for</span> <span class="n">az</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">365</span><span class="p">,</span> <span class="mi">5</span><span class="p">)])</span>

     <span class="n">logging</span><span class="p">.</span><span class="n">info</span><span class="p">(</span><span class="sa">f</span><span class="s">"Combining frames into gif for </span><span class="si">{</span><span class="n">const</span><span class="si">}</span><span class="s">..."</span><span class="p">)</span>

     <span class="n">images</span> <span class="o">=</span> <span class="nb">sorted</span><span class="p">([</span><span class="n">img</span> <span class="k">for</span> <span class="n">img</span> <span class="ow">in</span> <span class="n">os</span><span class="p">.</span><span class="n">listdir</span><span class="p">(</span><span class="n">images_folder</span><span class="p">)</span> <span class="k">if</span> <span class="n">img</span><span class="p">.</span><span class="n">endswith</span><span class="p">(</span><span class="s">".png"</span><span class="p">)],</span> <span class="n">key</span><span class="o">=</span><span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="nb">int</span><span class="p">(</span><span class="n">x</span><span class="p">.</span><span class="n">split</span><span class="p">(</span><span class="s">'_'</span><span class="p">)[</span><span class="o">-</span><span class="mi">1</span><span class="p">].</span><span class="n">split</span><span class="p">(</span><span class="s">'.'</span><span class="p">)[</span><span class="mi">0</span><span class="p">]))</span>
     <span class="k">with</span> <span class="n">Image</span><span class="p">.</span><span class="nb">open</span><span class="p">(</span><span class="n">os</span><span class="p">.</span><span class="n">path</span><span class="p">.</span><span class="n">join</span><span class="p">(</span><span class="n">images_folder</span><span class="p">,</span> <span class="n">images</span><span class="p">[</span><span class="mi">0</span><span class="p">]))</span> <span class="k">as</span> <span class="n">first_image</span><span class="p">:</span>
         <span class="n">image_list</span> <span class="o">=</span> <span class="p">[</span><span class="n">Image</span><span class="p">.</span><span class="nb">open</span><span class="p">(</span><span class="n">os</span><span class="p">.</span><span class="n">path</span><span class="p">.</span><span class="n">join</span><span class="p">(</span><span class="n">images_folder</span><span class="p">,</span> <span class="n">img</span><span class="p">))</span> <span class="k">for</span> <span class="n">img</span> <span class="ow">in</span> <span class="n">images</span><span class="p">[</span><span class="mi">1</span><span class="p">:]]</span>
         <span class="n">first_image</span><span class="p">.</span><span class="n">save</span><span class="p">(</span><span class="n">os</span><span class="p">.</span><span class="n">path</span><span class="p">.</span><span class="n">join</span><span class="p">(</span><span class="n">gif_folder</span><span class="p">,</span> <span class="sa">f</span><span class="s">'geom_</span><span class="si">{</span><span class="n">const</span><span class="si">}</span><span class="s">_</span><span class="si">{</span><span class="n">time</span><span class="p">.</span><span class="n">strftime</span><span class="p">(</span><span class="s">"%y_%m_%d"</span><span class="p">)</span><span class="si">}</span><span class="s">.gif'</span><span class="p">),</span> <span class="n">save_all</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span> <span class="n">append_images</span><span class="o">=</span><span class="n">image_list</span><span class="p">,</span> <span class="n">duration</span><span class="o">=</span><span class="mi">110</span><span class="p">,</span> <span class="n">loop</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span>

     <span class="n">logging</span><span class="p">.</span><span class="n">info</span><span class="p">(</span><span class="sa">f</span><span class="s">"Finished creating .gif file for </span><span class="si">{</span><span class="n">const</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>

     <span class="n">logging</span><span class="p">.</span><span class="n">info</span><span class="p">(</span><span class="sa">f</span><span class="s">"Deleting frames for </span><span class="si">{</span><span class="n">const</span><span class="si">}</span><span class="s">..."</span><span class="p">)</span>
     <span class="k">for</span> <span class="n">img</span> <span class="ow">in</span> <span class="n">images</span><span class="p">:</span>
         <span class="k">try</span><span class="p">:</span>
             <span class="n">os</span><span class="p">.</span><span class="n">remove</span><span class="p">(</span><span class="n">os</span><span class="p">.</span><span class="n">path</span><span class="p">.</span><span class="n">join</span><span class="p">(</span><span class="n">images_folder</span><span class="p">,</span> <span class="n">img</span><span class="p">))</span>
         <span class="k">except</span> <span class="nb">FileNotFoundError</span><span class="p">:</span>
             <span class="n">logging</span><span class="p">.</span><span class="n">error</span><span class="p">(</span><span class="sa">f</span><span class="s">"File </span><span class="si">{</span><span class="n">img</span><span class="si">}</span><span class="s"> not found."</span><span class="p">)</span>
         <span class="k">except</span> <span class="n">PermissionError</span><span class="p">:</span>
             <span class="n">logging</span><span class="p">.</span><span class="n">error</span><span class="p">(</span><span class="sa">f</span><span class="s">"Permission denied to delete file </span><span class="si">{</span><span class="n">img</span><span class="si">}</span><span class="s">."</span><span class="p">)</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p><strong>Computing Statistics:</strong> At this stage, I also compute statistics for the constellation. For the time being this is somewhat rudimentary but it works as follows: I have created a JSON file that contains the number of satellites in each constellation, the number of satellites in each altitude band (from 0-2000 in 100km bands), and the number of satellites in each inclination band (from 0-180 in 10 degree bands), and a timestamp to go with this data. Every time I pull data pertaining to a constellation this JSON file gets populated.</p>
  </li>
  <li>
    <p><strong>Selecting Statistics:</strong> Then I use the <code class="language-plaintext highlighter-rouge">json</code> module to read the last two entries for a given constellation. I calculate the differences between all the bands and select the largest difference. I then manually generate a tweet that says something like “Starlink has added 10 satellites in the 1000-1100km altitude band since yesterday”.</p>
  </li>
  <li>
    <p><strong>Generating Tweet Text:</strong> I pass the statistics and the string with the largest difference along with the name of the constellation to the <code class="language-plaintext highlighter-rouge">openai</code> module’s <code class="language-plaintext highlighter-rouge">Completion</code> class to generate the text. If there is no change in the statistics then I have a different prompt to generate a generic tweet about this constellation.</p>

    <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="kn">import</span> <span class="nn">logging</span>
 <span class="kn">import</span> <span class="nn">openai</span>
 <span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">List</span><span class="p">,</span> <span class="nb">str</span>

 <span class="k">def</span> <span class="nf">generate_tweet</span><span class="p">(</span><span class="n">constellation_name</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">viz_type</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">openai_api_key</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
     <span class="s">"""
     Generates a tweet for the given constellation using OpenAI's GPT-3 API.

     Args:
         constellation_name (str): The name of the constellation.
         viz_type (str): The type of visualization.
         openai_api_key (str): The API key for OpenAI.

     Returns:
         str: The generated tweet.

     Raises:
         ValueError: If viz_type is not valid.
     """</span>
     <span class="n">possible_viz_types</span> <span class="o">=</span> <span class="p">[</span><span class="s">"latest_state"</span><span class="p">,</span> <span class="s">"current_geometry"</span><span class="p">,</span> <span class="s">"ground_tracks"</span><span class="p">]</span>
     <span class="k">if</span> <span class="n">viz_type</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">possible_viz_types</span><span class="p">:</span>
         <span class="k">raise</span> <span class="nb">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="s">"Invalid viz_type </span><span class="si">{</span><span class="n">viz_type</span><span class="si">}</span><span class="s">. Must be one of </span><span class="si">{</span><span class="n">possible_viz_types</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
        
     <span class="n">prompt</span> <span class="o">=</span> <span class="n">create_gpt3_prompt</span><span class="p">(</span><span class="n">constellation_name</span><span class="p">,</span> <span class="n">viz_type</span><span class="p">)</span>

     <span class="n">openai</span><span class="p">.</span><span class="n">api_key</span> <span class="o">=</span> <span class="n">openai_api_key</span>
     <span class="n">response</span> <span class="o">=</span> <span class="n">openai</span><span class="p">.</span><span class="n">Completion</span><span class="p">.</span><span class="n">create</span><span class="p">(</span>
     <span class="n">engine</span><span class="o">=</span><span class="s">"text-davinci-002"</span><span class="p">,</span>
     <span class="n">prompt</span><span class="o">=</span><span class="n">prompt</span><span class="p">,</span>
     <span class="n">temperature</span><span class="o">=</span><span class="mf">0.72</span><span class="p">,</span>
     <span class="n">max_tokens</span><span class="o">=</span><span class="mi">200</span><span class="p">,</span>
     <span class="n">top_p</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span>
     <span class="n">frequency_penalty</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span>
     <span class="n">presence_penalty</span><span class="o">=</span><span class="mi">0</span>
     <span class="p">)</span> <span class="c1"># the parameters here took some trial and error
</span>
     <span class="n">tweet_text</span> <span class="o">=</span> <span class="n">response</span><span class="p">.</span><span class="n">choices</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">text</span><span class="p">.</span><span class="n">strip</span><span class="p">()</span>
     <span class="n">tweet_text</span> <span class="o">=</span> <span class="s">"Constellation-bot: "</span> <span class="o">+</span> <span class="n">tweet_text</span>
     <span class="n">logging</span><span class="p">.</span><span class="n">info</span><span class="p">(</span><span class="sa">f</span><span class="s">"gpt tweet: </span><span class="si">{</span><span class="n">tweet_text</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>

     <span class="c1"># If the generated tweet is too short or contains invalid characters, generate a default tweet
</span>     <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">tweet_text</span><span class="p">)</span> <span class="o">&lt;</span> <span class="mi">10</span> <span class="ow">or</span> <span class="ow">not</span> <span class="n">tweet_text</span><span class="p">.</span><span class="n">isascii</span><span class="p">():</span>
         <span class="n">tweet_text</span> <span class="o">=</span> <span class="sa">f</span><span class="s">"Check out the latest state of the </span><span class="si">{</span><span class="n">constellation_name</span><span class="si">}</span><span class="s"> constellation! #spacex #megaconstellations #satellites"</span>
     <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">tweet_text</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">280</span><span class="p">:</span>
         <span class="n">tweet_text</span> <span class="o">=</span> <span class="sa">f</span><span class="s">"Check out the latest state of the </span><span class="si">{</span><span class="n">constellation_name</span><span class="si">}</span><span class="s"> constellation! #spacex #megaconstellations #satellites"</span>
     <span class="k">return</span> <span class="n">tweet_text</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p><strong>Posting Tweets:</strong> Finally, I use the <code class="language-plaintext highlighter-rouge">tweepy</code> module to post the text and the tweet. You will have to set up a Twitter developer account to generate the necessary API keys and tokens to post the tweets. In order to run this script automatically and not have my tokens exposed on Github I have also set up a Github Secret to store these keys and tokens in my repository environment variables. <code class="language-plaintext highlighter-rouge">tweepy</code> does not yet have a build in method to post GIFs so I had to repurpose the code used for posting PNG images. I will make this class available in my repo in the near future.</p>
  </li>
  <li><strong>Automating the process :</strong> Finally I set up a Github Actions workflow <code class="language-plaintext highlighter-rouge">cron</code> job to run the script once a day with a different constellation and visualization type. If the script fails/succeeds I get a notification through the Github app on my phone.</li>
</ol>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">name</span><span class="pi">:</span> <span class="s">Cron twice-daily tweet</span>

<span class="na">on</span><span class="pi">:</span>
  <span class="na">schedule</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="na">cron</span><span class="pi">:</span> <span class="s1">'</span><span class="s">0</span><span class="nv"> </span><span class="s">17</span><span class="nv"> </span><span class="s">*</span><span class="nv"> </span><span class="s">*</span><span class="nv"> </span><span class="s">*'</span>
  <span class="na">workflow_dispatch</span><span class="pi">:</span>

<span class="na">jobs</span><span class="pi">:</span>
  <span class="na">cron</span><span class="pi">:</span>
    <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
    <span class="na">timeout-minutes</span><span class="pi">:</span> <span class="m">60</span>
    <span class="na">steps</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/checkout@v3</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Set up Python</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/setup-python@v3</span>
        <span class="na">with</span><span class="pi">:</span>
          <span class="na">python-version</span><span class="pi">:</span> <span class="s1">'</span><span class="s">3.8'</span>

      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Upgrade pip</span>
        <span class="na">run</span><span class="pi">:</span> <span class="s">pip install --upgrade pip</span>

      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Install system dependencies</span>
        <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
          <span class="s">sudo apt-get update</span>
          <span class="s">sudo apt-get install -y libproj-dev proj-data proj-bin</span>
          <span class="s">sudo apt-get install -y libgeos-dev</span>

      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Install dependencies</span>
        <span class="na">run</span><span class="pi">:</span> <span class="s">pip install -r requirements.txt</span>

      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Run cron_tweet.py</span>
        <span class="na">run</span><span class="pi">:</span> <span class="s">python source/twitterbot/cron_tweet.py</span>
        <span class="na">env</span><span class="pi">:</span>
          <span class="na">PYTHONPATH</span><span class="pi">:</span> <span class="s">$</span>
          <span class="na">TWIT_CONSUMER_KEY</span><span class="pi">:</span> <span class="s">$</span>
          <span class="na">TWIT_CONSUMER_SECRET</span><span class="pi">:</span> <span class="s">$</span>
          <span class="na">TWIT_ACCESS_TOKEN</span><span class="pi">:</span> <span class="s">$</span>
          <span class="na">TWIT_ACCESS_TOKEN_SECRET</span><span class="pi">:</span> <span class="s">$</span>
          <span class="na">OPENAI_API_KEY</span><span class="pi">:</span> <span class="s">$</span>
          <span class="na">SLTRACK_PWD</span><span class="pi">:</span> <span class="s">$</span>
          <span class="na">SLTRACK_USR</span><span class="pi">:</span> <span class="s">$</span> 
</code></pre></div></div>

<p>As ever if you have any questions or comments please feel free to message me. I hope you enjoyed this post and I hope you enjoy the bot!</p>]]></content><author><name>Charles Constant</name><email>zcesccc@ucl.ac.uk</email></author><summary type="html"><![CDATA[NOTE: This post was intended as the start of a series on how to make a Twitter constellation visualization bot. However, recent updates to the Twitter API have meant that the repetitive nature of the posts have blocked the bot so I will be stopping this effort. I tried to modify my code to work with the new API but it has proved too time-consuming. Instead, I will be posting these GIFs to my personal website on a dedicated page.]]></summary></entry><entry><title type="html">Positioning for Space Traffic Management: an Overview of Challenges and Solutions</title><link href="https://cconstant.cc/posts/2023-03-22-STMandPositioning" rel="alternate" type="text/html" title="Positioning for Space Traffic Management: an Overview of Challenges and Solutions" /><published>2023-03-22T00:00:00-07:00</published><updated>2023-03-22T00:00:00-07:00</updated><id>https://cconstant.cc/posts/STMandPositioning</id><content type="html" xml:base="https://cconstant.cc/posts/2023-03-22-STMandPositioning"><![CDATA[<p>Now for a bit of a technical one… This posts assumes you have some knowledge of TLEs and orbital mechanics. But even if you don’t I think there will be something of interest here for you anyway.</p>

<h2 id="navigating-the-challenges-of-space-traffic-management">Navigating the Challenges of Space Traffic Management</h2>

<p>The growing importance of space traffic management is underscored by the limitations of the Two-Line Element (TLE) format and the need for increased transparency in the measurement and generation process of TLEs. Accurate positioning of uncooperative assets is vital for the safety and sustainability of the space environment. As highlighted by Oltrogge et al. (2018), Space Situational Awareness (SSA) data must not only meet but significantly surpass operators’ probability of collision thresholds. Positional error thresholds for Low Earth Orbit (LEO) objects, assuming a probability of collision threshold of 0.01%, typically range from 100-200 meters (Alfano, 2021; Oltrogge, 2018).</p>

<h2 id="assessing-validation-using-geodetic-spheres">Assessing Validation Using Geodetic Spheres</h2>
<p>Some SSA systems claim to produce ephemerides with covariances of a few hundred meters for both cooperative (Abraham, 2018) and uncooperative (Nicolls, 2017; Conkey, 2022) tracked assets. However, caution must be exercised when interpreting these claims. For instance, Nicolls (2017) found an orbit determination error of a few hundred meters when validating against International Laser Ranging Service truth ephemeris for the Stella Geodetic Reference Sphere. The use of geodetic reference spheres for validating orbit determination (OD) solutions is not without its drawbacks. These spheres, by their very nature, perform unusually well in simplistic force models like SGP4, which assume the spacecraft to be a sphere when calculating many non-conservative forces (e.g., solar radiation pressure and aerodynamic drag). Consequently, errors resulting from mismodeling geometry and attitude are “hidden.” Furthermore, geodetic spheres have small area-to-mass ratios, which reduce errors associated with calculating non-conservative forces.</p>

<p>It’s important to note that mismodeling spacecraft geometry and attitude are some of the largest sources of error in computing non-conservative forces (Vallado, 2014). As a result, the accuracy and precision achieved using geodetic spheres as calibration tools should be considered a lower-bound or best-case scenario in orbit validation. Spacecraft with different geometries, attitudes, and orbital geometries will likely perform worse. Nevertheless, geodetic spheres offer valuable insight into the potential for uncooperative tracking.</p>

<p align="center">
  <img width="550" height="600" src="https://raw.githubusercontent.com/CharlesPlusC/CharlesPlusC.github.io/master/images/Starlette-sphere.png" alt="A Starlette Geodetic Sphere (Image Credit:CNES)" />
</p>

<h2 id="understanding-the-impact-of-positional-error">Understanding the Impact of Positional Error</h2>
<p>Defining “good” accuracy depends on the requirements we set for ourselves. To better grasp the consequences of positional error, consider this example:</p>

<p>Suppose a spacecraft operator believes their spacecraft is at an altitude of 1200 km (typical of OneWeb constellation satellites). Ignoring all other orbital perturbations except for the monopole gravity term, they calculate the acceleration onto their spacecraft using the formula:</p>

<p>${g_h} = g_0(\frac{R_e}{R_e + h})^2$</p>

<p>Where ${h}$ is the altitude, ${g_h}$ is gravitational acceleration at altitude ${h}$, ${g_0}$ is the standard gravitational acceleration (9.80665 m/s$^2$), and ${R_e}$ is the mean radius of the Earth (6731 km).</p>

<p>Using this formula, the gravitational acceleration at 1200 km is calculated as:</p>

<p>${g_{1200{Km}}} = 9.80665(\frac{6371}{6371 + 1200})^2 = 6.9443147 m/s^2$</p>

<p>Now, imagine the spacecraft is actually one meter higher than the operator thought. This would give the spacecraft a gravitational acceleration of:</p>

<p>${g_{1200+1m}} = 9.80665(\frac{6371}{6371 + 1200.001})^2 = 6.9443129 m/s^2$</p>

<p>The difference in acceleration between both states is:</p>

<p>$\Delta a = 6.9443129 - 6.9443147 = 1.8\times10^{-6}$</p>

<p>Calculating the overall distance between both states over a 24-hour period (86400 seconds), we find:</p>

<p>$\frac{1}{2}at^2 = \frac{1}{2}\times 1.8\times10^{-6}\times86400^2 = 6718.464m$</p>

<p>This difference greatly exceeds the 100-200 meter positional error threshold we are shooting for in LEO.
This example demonstrates how small errors in initial conditions can lead to disproportionate errors in position over time. Furthermore, this effect is accentuated in LEO due to the increasingly strong impact of monopole gravity with decreasing altitude. Gravity is roughly 1000 times greater than any other acceleration in LEO (Montenbruck, 2000), making not only the calculation of gravitational force of paramount importance, but also highlighting the importance that the inputs to the equations of motion be precise and accurate.</p>

<h2 id="tackling-positional-degradation-in-space-situational-awareness-data">Tackling Positional Degradation in Space Situational Awareness Data</h2>
<p>The decline of positional accuracy of orbits over time poses a significant challenge for SSA systems. In this section, we discuss three primary solutions for mitigating positional degradation in SSA data:</p>

<ul>
  <li>
    <p><em>Increasing Measurement Frequency</em>: By increasing the frequency of spacecraft position measurements, the growth of positional error can be constrained. For instance, if the spacecraft’s position is re-measured with a 1-meter error every 24 hours, the observer will theoretically never be off by more than approximately 6700 meters. However, if the period is reduced to 12 hours, the error will never exceed roughly 1700 meters.</p>
  </li>
  <li>
    <p><em>Improving Initial Conditions Accuracy</em>: Enhancing the accuracy and precision of a spacecraft’s initial position measurement can also reduce positional error. For example, in the previous section, if the initial error is reduced from 1 meter to 0.5 meters, the error after 24 hours will be around 3300 meters. Beyond hardware improvements, this can also be achieved through data fusion techniques that combine measurements from different sensors.</p>
  </li>
  <li>
    <p><em>Improving Orbit</em> Propagation: Even with perfect initial conditions and frequent measurements, a propagated orbit will still degrade due to force model and numerical integrator errors. Utilizing a higher-fidelity orbit propagator, which models the spacecraft’s interaction with the physical environment more accurately, will result in fewer errors.</p>
  </li>
</ul>

<p>Real-time positioning in Low Earth Orbits (LEO) has reached centimeter-level accuracy (Li, 2019). However this level of accuracy necessitates a “cooperative” approach to space traffic management, where two-way communication occurs between the ground station and the satellite in question.</p>

<p align="center">
  <img width="550" height="600" src="https://raw.githubusercontent.com/CharlesPlusC/CharlesPlusC.github.io/master/images/radar-leolabs.png" alt="LEOLabs' Australian Radars for Uncooperative Space Surveillance" />
</p>

<p>In contrast, the United States Space Surveillance Network (USSSN) aims to disseminate information about a wide range of Resident Space Objects (RSOs) to various operators (Wilson, 2019). Most RSOs, however, either lack Global Navigation Satellite Systems (GNSS) receivers or are unable or unwilling to share information with the USSSN. This “uncooperative” approach presents significant challenges, as there is no telemetry to aid the tracking process for most RSOs. Consequently, the USSSN must make cost-benefit trade-offs concerning measurement frequency, initial conditions accuracy, and force model complexity. For instance, high-fidelity atmospheric density models like the TIE-GCM (Qian, 2013) are readily available but are not widely used in orbit propagators due to their high computational cost (Licata, 2021).</p>

<p>In summary, mitigating positional degradation in SSA catalogs requires a combination of increasing measurement frequency, improving initial conditions accuracy, and using high-fidelity orbit propagators. However, the uncooperative nature of tracking RSOs poses significant challenges for SSA systems and necessitates cost-benefit trade-offs in terms of resource allocation and computational complexity.</p>

<p>As the number of satellites and space debris in orbit continues to grow, ensuring the safety and sustainability of the space environment becomes increasingly crucial. The limitations of the TLE format and the lack of transparency in the measurement and generation process of TLEs are critical factors to consider in space traffic management. The quest for precise positioning of uncooperative assets is a key component in addressing these challenges.</p>

<p>Moving forward, it is essential to invest in research and development that seeks to improve SSA systems and develop new tracking technologies. This may include advancements in sensor technology, data fusion techniques, and high-fidelity orbit propagators. Additionally, fostering international collaboration and data sharing among different countries and organizations can help address the challenges associated with uncooperative tracking and enable more accurate and reliable space traffic management.</p>

<p>In conclusion, the future of space traffic management hinges on our ability to enhance the accuracy and reliability of positional information for satellites and other space objects. By tackling the challenges associated with uncooperative tracking and positional degradation, we can help ensure the long-term sustainability of the space environment and enable safe and efficient space operations for all stakeholders involved.</p>

<h2 id="references">References</h2>

<ul>
  <li>
    <p>Oltrogge, D. et al. (2018). “The “we” approach to space traffic management.” <em>15th International Conference on Space Operations</em>.</p>
  </li>
  <li>
    <p>Alfano et al. (2021). “SSA positional and dimensional accuracy requirements for Space Traffic Coordination and Management.” <em>2021 Advanced Maui Optical Space Surveillance Technologies Conference</em>.</p>
  </li>
  <li>
    <p>Nicolls, M. (2017). “Conjunction Assessment for Commercial Satellite Constellations Using Commercial Radar Data Sources.” <em>2017 Advanced Maui Optical Space Surveillance Technologies Conference</em>.</p>
  </li>
  <li>
    <p>Vallado, D. (2014). “A critical assessment of satellite drag and atmospheric density modeling.” <em>Acta Astronautica</em>, 95, 141–165.</p>
  </li>
  <li>
    <p>Montebruck, O. &amp; Eberhard, G. (2000). “Satellite Orbits Models, Methods and Applications.” <em>Springer-Verlag Berlin</em>.</p>
  </li>
  <li>
    <p>Li et al. (2019). “LEO enhanced Global Navigation Satellite System (LeGNSS) for real-time precise positioning services.” <em>Advances in Space Research</em>, 63(1), 73-93.</p>
  </li>
  <li>
    <p>Qian, L. (2013). “The NCAR TIE-GCM: A community model of the coupled thermosphere/ionosphere system.” <em>Geophysical Monograph Series</em>, 201, 73-83.</p>
  </li>
  <li>
    <p>Wilson, T. (2019). “18th Space Control Squadron - Small Satellite Support Presentation.” <em>Sept.2019</em>.</p>
  </li>
  <li>
    <p>Licata, M. (2021). “Impact of Space Weather Driver Forecast Uncertainty on Drag and Orbit Prediction.” <em>Advances in the Astronautical Sciences</em>, 175, 1941-1959.</p>
  </li>
</ul>]]></content><author><name>Charles Constant</name><email>zcesccc@ucl.ac.uk</email></author><summary type="html"><![CDATA[Now for a bit of a technical one… This posts assumes you have some knowledge of TLEs and orbital mechanics. But even if you don’t I think there will be something of interest here for you anyway.]]></summary></entry><entry><title type="html">Tracking Objects in Space: The Rise and Reign of Two-Line Elements</title><link href="https://cconstant.cc/posts/2023-03-17-TLEblog" rel="alternate" type="text/html" title="Tracking Objects in Space: The Rise and Reign of Two-Line Elements" /><published>2023-03-17T00:00:00-07:00</published><updated>2023-03-17T00:00:00-07:00</updated><id>https://cconstant.cc/posts/TLEblog</id><content type="html" xml:base="https://cconstant.cc/posts/2023-03-17-TLEblog"><![CDATA[<h2 id="brief-history-of-tles">Brief History of TLEs</h2>
<p>Keeping track of objects in space is no easy task. The first real concerted effort to systematically track earth orbiting satellites started back in 1957, with the launch of Sputnik 1. The US government quickly realized they needed a way to keep track of all the objects in space and thus, Project Space Track was born. In the early days, tracking was done through a combination of radar, telescopes, radio, and even citizen observations. These observations were then manually reduced, and corrections were determined in order to generate orbital elements that could be used for predictions.</p>

<p>The goal was to develop a method that would provide a fast-updating situational report of objects in space using the computing power that was available at the time for “large-scale space applications and simulations” (Hujsak, 1979). In pursuit of a concise data format for efficient data processing, Two-Line Elements (TLEs) were created to describe the orbits of objects in space. This ultimately led to the creation of SpaceTrack Report 2 (Lane and Hoots, 1979) and the adoption of the TLE format by the North American Aerospace Defense Command (NORAD). The model used to generate this data, Simplified General Perturbations 4 (SGP4), is an analytical model that eventually became the go-to model for commercial and scientific spacecraft operators.</p>

<p><img src="/images/tle_diag.png" alt="Breakdown of the TLE Data Format" title="Breakdown of the TLE Data Format" /></p>

<h2 id="what-are-tles-how-are-they-made-today">What are TLEs? How are they made today?</h2>

<p>But what exactly are TLEs? A single TLE is a two-line, 69-character description of a satellite’s identity, orbital geometry (using Keplerian elements), the orbit epoch for which this information was generated, and a term related to the ballistic coefficient (B-star). The TLE format is described in more detail in Vallado (2006). However, it’s important to note that the B-star term is widely understood to be somewhat problematic as the simplified force modelling and the way in which the data is processed means that this term ends up serving as a catch-all term for model errors (Vallado, 2006).</p>

<p>Today, TLEs are generated from a variety of data sources, such as radars, optical sensors, and in-orbit sensors. All these sources fall under the United States Space Surveillance Network (USSSN), as seen in the image below.</p>

<p>Since March 1998, T.S. Kelso has been pioneering the dissemination of TLEs through his website Celestrak.org. TLEs are now generated by the 18th Space Control Squadron (18th SpCS) and shared through their website (space-track.org). Spacetrack serves as the de-facto source of data for a multitude of SSA products the community relies on. These include but are not limited to conjunction (collision) data messages, decay and re-entry predictions.</p>

<p><img src="/images/Space_Surveillance_Network.png" alt="USSSN as of 2018" title="USSSN as of 2018" /></p>

<p>More recently, Celestrak has also started sharing Operator TLEs for certain spacecraft. Operator TLEs (also referred to as Supplemental TLEs or SupTLEs) are TLEs that are “derived directly from owner/operator-supplied orbital data” instead of 18th SPCS measurements. This type of TLE is valuable as the operator-supplied orbital data used to generate them is typically derived from on-board GNSS receivers (Murley, 1982a), or operator ephemerides (Johnson, 2022), both of which are usually orders of magnitude more accurate than TLE data. When fit to high-accuracy ephemerides (e.g.GPS SEM Almanac orbits), these TLEs can be orders of magnitude more accurate than a run-of-the-mill NORAD TLE (Supplemental TLE Link). In practice, this means going from multi-kilometre error (~5-10 Km) to sub-kilometre error (~0.85 Km)</p>

<p>The graph below illustrates the performance of NORAD TLEs against supplemental TLEs on GPS spacecraft (work by T.S.Kelso. See https://celestrak.org for more information).
<img src="/images/NORADvSupKELSO.png" alt="Comparison of the performance of NORAD TLEs against supplemental TLEs on GPS spacecraft as illustrated by T.S.Kelso" title="Comparison of the performance of NORAD TLEs against supplemental TLEs on GPS spacecraft as illustrated by T.S.Kelso's work" /></p>

<h2 id="limitations-of-tles">Limitations of TLEs</h2>
<p>Whilst TLEs are the current bedrock of many SSA products, one should be aware of their (many) limitations. For example, TLEs are generated by fitting tracking data to the SGP4 force model (using batch-least squares), which while widely used, remains relatively inaccurate compared to state-of-the-art propagators. This saves on computational cost but also on accuracy.</p>

<p>TLEs are provided without any information pertaining to their accuracy (covariance matrices). This is done purposefully by the 18th Space Control Squadron to limit the ability of external parties to infer information about the sensors the USSSN disposes of. Furthermore, utilizing TLEs fitted to SGP4 with other force models will result in degraded performance. This means that, unfortunately any TLE is “stuck” with the SGP4 force model. Additionally, the accuracy of TLEs is known to degrade rapidly over time. They must be updated frequently with new tracking data to keep providing a representative picture of reality.</p>

<p>Despite these limitations, TLEs remain a valuable tool to provide a quick and efficient way to track and predict the orbits of objects in space and are widely used by spacecraft operators and other organizations involved in space activities. The recent addition of Operator TLEs, which are derived from more accurate operator-supplied data, adds an extra level of precision to TLE predictions.</p>

<p>TLEs have played a crucial role in the development of space monitoring and traffic management since the launch of Sputnik 1. The TLE format, while not without its limitations, provides a concise and efficient way to track and predict the orbits of objects in space. As space traffic continues to increase, it’s essential that the valid use cases of TLE continue to be monitored and that the space traffic management community continues to build tools that meet their ever-evolving needs. The recent addition of a public facing repository of Operator TLEs is an example of a tool that helps to improve the safety of space operations.</p>]]></content><author><name>Charles Constant</name><email>zcesccc@ucl.ac.uk</email></author><summary type="html"><![CDATA[Brief History of TLEs Keeping track of objects in space is no easy task. The first real concerted effort to systematically track earth orbiting satellites started back in 1957, with the launch of Sputnik 1. The US government quickly realized they needed a way to keep track of all the objects in space and thus, Project Space Track was born. In the early days, tracking was done through a combination of radar, telescopes, radio, and even citizen observations. These observations were then manually reduced, and corrections were determined in order to generate orbital elements that could be used for predictions.]]></summary></entry></feed>