Getting Started With Star Tracker Attitude Determination

The basic idea is simpler than people make it sound. You take a picture of the sky with a star tracker, match the dots you see against a catalog, and solve for the quaternion that aligns them. That quaternion is your attitude relative to an inertial frame. Most of the frustration comes from the parts everyone glosses over, not the core algorithm. I started working with star trackers around 2014, back when we were debugging a CubeSat attitude determination system that kept failing during eclipse transitions. The code I wrote for that project lives in a GitHub repo now, and the core functions are fairly reusable. I won't link the repo directly here because it changes often, but the logic is standard enough that anyone can reconstruct it. Here's what the typical flow looks like in practice. You have a star catalog, usually SIMBAD or a reduced version like HIPPARCOS. You have a current star image from your tracker. You extract centroids from the image, pattern-match against the catalog, and then use a method like QUEST or Davenport's q-method to compute the attitude quaternion. That's the textbook version. The real version involves more edge cases than you'd expect.

One thing beginners consistently miss is that centroiding precision matters far more than most people account for. A well-implemented sub-pixel centroider can push measurement noise down to around 0.001 degrees per axis. A naive pixel-level centroid will sit around 0.01 degrees and your attitude solution will reflect that immediately. When I was first writing my code, I assumed the star tracker hardware handled this. It didn't. I had to write my own Gaussian fitting routine for the centroid extraction because the raw images from our sensor needed it. Another counter-intuitive point: the catalog you use matters more than you think. The default SIMBAD catalog has over 7 million stars. Loading that into MATLAB takes roughly 45 seconds and consumes about 2 GB of RAM. For an attitude determination loop running at 1 Hz, that's unacceptable. I ended up using a magnitude-limited catalog with about 9,000 stars visible to a typical star tracker. It loaded in under 0.3 seconds and gave me essentially the same attitude accuracy because you only need the brightest, most easily identifiable stars anyway. Here's the practical code structure I use. The main script handles the pipeline: read the star image, call the centroid extractor, run the pattern recognizer, then feed the observed and catalog star pairs into the attitude solver. The centroid function uses a weighted centroid approach with a Gaussian fit on the peak pixel neighborhood. The pattern recognizer uses the triangle method, which compares angular separations between triplets of observed stars against a precomputed lookup table of triangle separations from the catalog.

The star catalog preparation step is critical and boring. I precompute all pairwise angular separations for the visible stars, sort them, and store them in a structure array. This precomputation takes about 12 seconds on a typical laptop for a 9,000-star catalog. It only needs to happen once, so I save the result to a .mat file and load that instead of recomputing every session. This cut my development iteration time from about 2 hours down to roughly 15 minutes per test cycle. When you get to the QUEST solver itself, MATLAB has a compact implementation that runs in about 0.8 milliseconds per attitude solution on a modern processor. The bottleneck is almost never the quaternion calculation. It's the star matching. If your pattern recognition fails to find a valid star triplet, the whole solution breaks. I've seen this happen when the tracker is partially in Earth's limb contamination zone. The stray light shifts the centroid positions by several pixels and the triangle angles no longer match any catalog triplet within your tolerance. The workaround I settled on was adding a fallback mode. If the triplet matching fails, the code switches to a brute-force nearest-neighbor search with a wider angular tolerance. This is slower, taking about 80 milliseconds instead of 2, but it recovers the solution in cases where the centroid shift is moderate. I set a threshold at 3 arcminutes of centroid deviation. Below that, I use the fast triplet method. Above that, I fall back to the brute force approach. It handles most edge cases without manual intervention.

Get the Full Details

Attitude Determination using UKF algorithm using only the small star... | Download Scientific ...
Attitude Determination using UKF algorithm using only the small star... | Download Scientific ...

There are situations where this code simply won't work and you need to know about them upfront. If your star tracker can't see at least 3 stars with sufficient signal-to-noise ratio, attitude determination is impossible. Period. This happens during certain maneuver profiles where the spacecraft is pointing too close to the Sun or Earth limb. My system would output a quality flag in those cases rather than producing a garbage quaternion, but it requires you to check that flag and handle it in your flight software. Producing an attitude solution with fewer than 3 matched stars is a common failure mode in student projects I've reviewed. A second limitation people don't talk about enough is field of view. If your star tracker has a narrow FOV, say 20 degrees by 20 degrees, you'll lose star acquisitions during high-rate rotations. The code can't recover from missing stars entirely. It can only interpolate between previous solutions or rely on a gyroscope backup. I always recommend coupling the star tracker attitude solution with a gyroscope integration loop, even if just for redundancy. The MATLAB simulation part is straightforward, but the real spacecraft doesn't give you much margin for error when the tracker temporarily goes blind. For the actual code, here's the minimal structure. You'll need a star catalog file, the image processing toolbox for centroid extraction, and a quaternion library or your own implementation. The main function takes a FITS image and returns a quaternion in the J2000 frame. The helper functions handle centroiding, catalog matching, and attitude solving separately so you can debug each piece independently.

I spent about three weeks getting a production-ready version working. The first two weeks were mostly fighting with the pattern recognition. The triangle method works in theory but the star catalog coordinate preprocessing is where most bugs hide. Proper right ascension and declination to unit vector conversion, proper handling of epoch precession, these details matter. One wrong sign in the precession matrix cost me two days of debugging because the attitude solution looked plausible but was offset by roughly 0.5 degrees systematically. If you're starting from scratch, I'd suggest beginning with just the centroid extraction and visualization. Get that working cleanly before adding the pattern matching. Plot the centroids on the image, verify they're accurate, then move on. It's tempting to write everything at once, but you'll spend weeks going back to fix early decisions rather than weeks moving forward.

Testing Your Implementation

The best test is a simulated star field with known attitude. Generate synthetic star images using your catalog and a known quaternion, add realistic noise, and verify your code recovers the quaternion within expected bounds. For a good star tracker at 0.001 degree centroid precision, you should see attitude recovery within about 0.002 to 0.005 degrees RMS across multiple trials. If your error is larger, check your centroider first, then your pattern matcher, then your attitude solver. Comparing against known reference solutions from tools like NASA's NAIF SPICE kernel system or the European Space Astronomy Centre's simulation tools is also useful. I validated my code against ESOC's star tracker simulator and the results were within 0.003 degrees RMS, which is acceptable for most applications but shows there's still room for improvement if you need higher precision. The code itself is fairly standard and you'll find similar implementations across various university research groups and open source projects. The value is in the details: how you handle catalog loading, how you tune your centroid threshold, how you manage the fallback logic when star matching fails. Those are the parts that separate code that works once from code that works reliably in production.

Attitude determination model based on a star tracker. | Download Scientific Diagram
Attitude determination model based on a star tracker. | Download Scientific Diagram