Pytellite is an open-source Python simulator for modeling the attitude dynamics and (in the near future) orbital mechanics of satellites. It allows users to define a satellite's physical properties, actuators, and initial state, define the control law, and then propagates its motion over time. It supports a software-in-the-loop style simulation, where the controller uses estimated attitude and angular velocity obtained from a Kalman filter. Noisy gyro and startracker measurements are synthesized, and an error-state MEKF is used to estimate the state. The core feature of Pytellite is its integrated, web-based 3D visualization of the satellite and reference systems, which provides real-time, intuitive feedback on the satellite's orientation and trajectory, alongside plots of system variables.
The goal of Pytellite is to provide a medium-fidelity, visual, easy-setup satellite simulator for learning, rapid prototyping, and preliminary design. By prioritizing ease of use and clear visualization, the project aims to help users quickly test attitude control laws and gain an intuitive understanding of how a satellite behaves in orbit, making it a practical sandbox for education and early-stage design exploration.
As of late September 2025, new features are being implemented on a weekly basis. The source code is available on Github. You're invited to explore a live demo to get a feel for the project's direction and capabilities at Pytellite.org. You may also deploy Pytellite as a web app on your localhost.
I am working on writing a fully fledged documentation for Pytellite, which will include details on how to use each functionality as well as all the theory and calculations happening behind the scenes. For now, I've written here a very brief section on the basic equations being used.
When first exploring Pytellite, I recommend testing out the three presets I made. The first one is the torque-free motion of an object with an initial angular velocity which is almost perfectly aligned with its intermediate inertia axis. Due to the intermediate axis theorem, this leads to an instability on its rotation axis. The second one reproduces the example 7.1 found in Fundamentals of Spacecraft Attitude Determination and Control by Markley (2014), which uses a "linear" control law for inertial pointing with error quaternion and angular rate feedback (more on that later). Finally, the third example is fine pointing but now using attitude and angular rate estimation using realistic gyro and startracker noise.
Pytellite can estimate the attitude and angular velocity as if the satellite was only provided with gyro and startracker (or other attitude sensor) measurements (instead of the ground truth). You can activate this functionality in the Estimation tab when you are setting up your simulation. Activating this option means that the attitude and angular velocity information fed into the control laws are those obtained by the error state multiplicative extended Kalman filter (MEKF). You can read the detailed documentation I wrote for this filter implementation here. I will not detail this algorithm here because it is quite long and technical.
It's important to know that for attitude/angular velocity estimation, gyro measurements are used for dynamic model replacement. In other words, the propagation steps of the angular velocity are done with the measurements of the gyro, instead of integrating Euler's rigid body equations. This is often done in real satellites according to Markley (2014) and Wie (2008), to avoid the computational cost of propagating these equations and because oftentimes the dynamic model has large enough uncertainties such that integrating the exact equations isn't necessarily more accurate than simply taking the gyro measurements. The downside is that for large control torques, the estimation errors will be large and most importantly, you'll see that the 3-sigma bounds that the MEKF predicts (dashed lines in the Estimation error plots) do not accurately represent the actual estimation errors. In other words, the model becomes overconfident in its estimates, because the control torque is not modeled when propagating the covariance matrix. For small control torques, this is not an issue.
Notice that the confidence bounds for the estimate in the attitude error have a sawtooth shape. This is because when attitude measurements arrive, the covariance is updated and decreases. Until they arrive again, the covariance is propagated and increases gradually.
In parallel to the attitude estimation, Pytellite computes the ground truth, or the actual values of the angular velocity and attitude (in the form of an attitude quaternion). This is computed with:
Here, ω is the angular velocity of the body frame with respect to the inertial frame, in body frame coordinates; J is the inertia matrix in body coordinates; L is the effective wheel control torque in body coordinates; h is the wheel angular momentum in body coordinates; q is the current attitude quaternion, from body to inertial frame; qc is the desired attitude quaternion; ẟq is the error quaternion; ϵ and η are the vector and scalar components of the quaternion, respectively; ⊗ is the left quaternion product.
I chose to plot the three components of the attitude rotation vector instead of the quaternion. Hence, I map the quaternion to the rotation vector, but just for plotting. Recall that the rotation vector has a singularity at 180 degrees. I plan on adding different attitude representations later (quaternion, MRP, Euler angles, etc.).
Currently, the code assumes that there are three reaction wheels whose axes are aligned with the principal axes of inertia of the satellite. The following torque control laws are implemented in Pytellite, obtained from Eqs. (7.12) and (7.14) from Markley:
Here, kp and kd are the proportional and derivative gains, respectively. The first law is the "linear" option on Pytellite (although not technically linear due to the sign factor), and the second is the nonlinear controller option at the simulation configuration page. In general, I've found the nonlinear controller to have better performance.
When estimation is deactivated and only the ground truth is computed, Pytellite integrates these equations using an adaptive step-size solver and feeds interpolated state solutions to the frontend at fixed time intervals, allowing the satellite motion to play smoothly. I chose this over a fixed step size (which is used when estimation is activated) to speed up the computation. Quaternion interpolation is handled with a slerp function, which ensures a smooth representation in the animation even if the states are computed at disparate step sizes.
When estimation is activated, the integration runs on a fixed timestep. An RK45 integration step is used for all the states except for the quaternion, which is propagated exactly (with quaternion multiplication) to ensure maintaining the norm to 1. No quaternion normalization is used (or needed).
Math-heavy functions are accelerated using Numba's JIT compilation. This accelerates the computation speed by a factor of 10, and in some functions even more. Note that by default if you run Pytellite locally, Numba is deactivated (to allow for quick iteration, as Numba as long "cold starts" to compile all the functions).
I have many ideas I want to implement in Pytellite, and I'm adding them on a weekly basis! First, I want to simulate the satellite's motion along its orbit too. This will allow to simulate more interesting control laws, such as nadir and Sun pointing, and add perturbation torques which require the knowledge of the satellite's position, such as solar radiation pressure, gravity gradient, and drag. Other ideas I want to implement are adding realistic actuator dynamics and limits, a detumbling law, adding momentum dumping, and more. If you have any ideas you'd like to see implemented, please shoot me a mail at cab@lle.ro.
v2.0: added software-in-the-loop functionality with attitude and angular velocity estimation with an error state MEKF. Added estimation tab to configure sensor properties. Added pointing error, gyro bias, and estimate errors with confidence bounds plots. Added brief documentation.
v1.2: Added Numba JIT compilation, simulation speed is >x10 faster. Added reaction wheel angular momentum (3 wheels, aligned with principal inertia axes). Added a nonlinear PD error quaternion and angular velocity control law for inertial pointing.
v1.1: Added a linear PD error quaternion and angular velocity control law for inertial pointing.
v1.0: Pytellite.org is live! Satellite torque-free attitude dynamics.