Flight Application

Radio RX/TX

Commands

Has a bunch of commands that can be called via radio, with an argument.

Contains a dictionary of commands mapping their 2 byte header to a function.

radio_utils.commands.copy_file(task, args)[source]

Copy a file from source to dest

Parameters
  • task – The task that called this function

  • args (str) – json string [source, dest]

radio_utils.commands.delete_file(task, file)[source]

Delete file

Parameters
  • task – The task that called this function

  • file (str) – The path to the file to delete

radio_utils.commands.exec_py(task, args)[source]

Execute the python code, and do not return the result

Parameters
  • task – The task that called this function

  • args (str) – The python code to execute

async radio_utils.commands.hreset(self)[source]

Hard reset

radio_utils.commands.list_dir(task, path)[source]

List the contents of a directory, and downlink the result

Parameters
  • task – The task that called this function

  • path (str) – The path to the directory to list

radio_utils.commands.move_file(task, args)[source]

Move a file from source to dest. Does not work when moving from sd to flash, should copy files instead.

Parameters
  • task – The task that called this function

  • args (str) – json string [source, dest]

radio_utils.commands.noop(self)[source]

No operation

radio_utils.commands.query(task, args)[source]

Execute the query as python and return the result

radio_utils.commands.request_file(task, file)[source]

Request a file to be downlinked

Parameters
  • task – The task that called this function

  • file (str) – The path to the file to downlink

radio_utils.commands.tq_len(task)[source]

Return the length of the transmission queue

Message Classes

class radio_utils.message.Message(priority, str, with_ack=False)[source]

The most basic message type. Supports ascii messages no longer than 251 bytes. Other message types should inherit from this class.

Parameters
  • priority (int) – The priority of the message (higher is better)

  • str (str | bytes | bytearray) – The message to send

ack()[source]

Called when the message is acknowledged.

done()[source]

Returns true if the message is done sending.

no_ack()[source]

Called when the message fails to be acknowledged.

packet()[source]

Returns the byte representation of the message, and if it should be sent with or without ack.

class radio_utils.naive.NaiveMessage(priority, str)[source]

Bases: Message

Transmits the message 249 bytes at a time. Sets special headers for the first packet, middle packets, and last packet.

Parameters
  • priority (int) – The priority of the message (higher is better)

  • str (str | bytes | bytearray) – The message to send

ack()[source]

Called when the message is acknowledged.

done()[source]

Returns true if the message is done sending.

packet()[source]

Returns the byte representation of the message, and if it should be sent with or without ack.

class radio_utils.chunk.ChunkMessage(priority, path)[source]

Bases: Message

Transmits the message 251 bytes at a time. Sets special headers for the first packet, middle packets, and last packet. Reads from a file one chunk at a time.

Parameters
  • priority (int) – The priority of the message (higher is better)

  • path – The path to the file containing the message to send

ack()[source]

Called when the message is acknowledged.

done()[source]

Returns true if the message is done sending.

packet()[source]

Reads the next chunk of data from sd, and returns this is a packet. Always requests an ack.

Transmission Queue

The Transmission Queue is a max heap of messages to be transmitted.

Messages must support the __lt__, __le__, __eq__, __ge__, and __gt__ operators. This enables to the max heap to compare messages based on their priority.

radio_utils.transmission_queue.clear()[source]

Clears the transmission queue

radio_utils.transmission_queue.empty()[source]

Returns if the transmission queue is empty

radio_utils.transmission_queue.peek()[source]

Returns the next message to be transmitted

Returns

The next message to be transmitted

Return type

Message | NaiveMessage | ChunkMessage

radio_utils.transmission_queue.pop()[source]

Returns the next message to be transmitted and removes it from the transmission queue

Returns

The next message to be transmitted

Return type

Message | NaiveMessage | ChunkMessage

radio_utils.transmission_queue.push(msg)[source]

Push a msg into the transmission queue

Parameters

msg (Message | NaiveMessage | ChunkMessage) – The message to push

Guidance, navigation, and control aries

Control

control.bcross(b, ω, k=0.0007)[source]

Frames

frames.ERA(utime)[source]

Returns the the ERA (Earth Rotation Angle) at a certain unix time stamp. Inspired by SOFA’s iauEra00 function.

Parameters

utime (int) – A unix timestamp

Returns

The Earth Rotation Angle in radians.

frames.convert_ecef_to_geoc(ecef, degrees=False)[source]

Converts from ECEF (Earth Centered Earth Fixed) to geocentric coordinates.

Parameters
  • ecef (numpy.array) – ECEF coordinates in km.

  • degrees (bool, optional) – If True, returns the coordinates in degrees.

Returns

A 3x1 numpy arary containing the geocentric coordinates long, lat, alt (radians, radians, km)

frames.earth_rotation(utime)[source]

Computes rotation matrix based on the Earth Rotation Angle (ERA) at a certain unix time stamp.

frames.ecef_to_eci(utime)[source]

Returns the rotation matrix from ECEF (Earth Centered Earth Fixed) to ECI (Earth Centered Inertial).

Parameters

utime (int) – A unix timestamp

Returns

A 3x3 numpy array.

frames.eci_to_ecef(utime)[source]

Returns the rotation matrix from ECI (Earth Centered Inertial) to ECEF (Earth Centered Earth Fixed). Applies correction for Earth-rotation. Based on SatelliteDynamic’s rECItoECEF.

Parameters

utime (int) – A unix timestamp

Returns

A 3x3 numpy array.

frames.mjd(utime)[source]

Returns the Modified Julian Date (MJD) for a given unix timestamp.

frames.ned_to_ecef(lon, lat)[source]

Returns the rotation matrix for transforming coordinates in an earth-centered NED frame to coordinates in an ECEF frame.

Parameters
  • lon (float) – Longitude in radians (geocentric).

  • lat – Latitude in radians (geocentric).

  • lat – float

Returns

A 3x3 numpy array.

frames.rotZ(theta)[source]

Returns the rotation matrix for a given angle around the z-axis.

Parameters

theta (float) – Angle in radians.

Returns

A 3x3 numpy array.

Mathutils

mathutils.block(S)[source]

Returns a block matrix from a list of lists of matrices For example:

A = eye(2)
B = zeros((2, 3))
C = ones((4, 2))
D = ones((4, 3)) * 4
block([[A, B],
       [C, D]])
array([[1., 0., 0., 0., 0.],
       [0., 1., 0., 0., 0.],
       [1., 1., 4., 4., 4.],
       [1., 1., 4., 4., 4.],
       [1., 1., 4., 4., 4.],
       [1., 1., 4., 4., 4.]])
mathutils.hat(v)[source]

Converts v to a matrix such that hat(v)w = cross(v, w)

Parameters

v (numpy.ndarray) – vector

Returns

3x3 numpy array

mathutils.quaternion_mul(q1, q2)[source]

Multiplies a scalar-first unit quaternion by another scalar-first unit quaternion

mathutils.quaternion_to_left_matrix(q)[source]

Converts a scalar-first unit quaternion into the left-side matrix for quaternion multiplication

mathutils.quaternion_to_rotation_matrix(q)[source]

Converts a scalar-first unit quaternion into the rotation matrix Qv = qvq+

MEKF

Multiplicative Extended Kalman Filter Based on Zac Manchester’s Formulation Writen by Aleksei Seletskiy

A Multiplicative Extended Kalman filter (MEKF) is a variant of an Extended Kalman Filter (EKF). The important features of this MEKF are: - We eliminate rigibody dynamics by assuming we have a near perfect gyro (after the bias β is removed). - We assume our gyro sample is rate is high enough that ω is essentialy constant over a sample period. - Thus we treat ω (angular velocity) as a the control. - We are running an EKF with a local axis-angle error vector, but the global state is stored using a quaternion. - The gyro bias (β) is estimated.

mekf.propagate_state(q, β, ω, δt)[source]

State propogation function. Substracts out the gyro bias from the gyro reading, then propogates the state forward by the time step.

Parameters
  • q (numpy.array) – Quaternion attitude vector

  • β (numpy.array) – Gyro bias axis-angle vector

  • ω (numpy.array) – Measured angular velocity

  • δt (float) – Time step

Returns

New quaternion attitude vector

mekf.step(ω, δt, nr_mag, nr_sun, br_mag, br_sun)[source]

Updates the state of the MEKF by one itteration of sensor readings.

Parameters
  • ω – Gyroscope reading

  • δt – Time step

  • nr_mag – Inertial frame magnetic field vector

  • nr_sun – Inertial frame sun pointing vector

  • br_mag – Measured body frame magnetic field vector

  • br_sun – Measured body frame sun pointing vector

Orbital Mechanics

orbital_mechanics.d_state(x)[source]

Time derivative of the satellite’s state (position, velocity) in the ECI frame. Takes into account spherical gravity and the J2 perturbation.

Parameters

x (numpy.ndarray) – 6-vector, first 3 are position (km), last 3 are velocity (km/s)

Returns

6-vector, first 3 are velocity (km/s), last 3 are acceleration (km/s^2)

orbital_mechanics.propogate(state, time_forward, integration_step=5)[source]

Propogate the satelite state forward time_forward (s), in steps of integration_step (s) using rk4 integration. Takes into account spherical gravity and the J2 perturbation. In the ECI frame

Parameters
  • state (numpy.ndarray) – 6-vector, first 3 are position (km), last 3 are velocity (km/s)

  • time_forward (float) – time to propogate forward (s)

  • integration_step (float) – time step for rk4 integration (s)

Returns

propogated state, 6-vector, first 3 are position (km), last 3 are velocity (km/s)

orbital_mechanics.rk4(x, dt, f)[source]

Runge-Kutta order 4 integration

Parameters
  • x (numpy.ndarray) – 6-vector, first 3 are position, last 3 are velocity

  • dt (float) – time step

  • f (function) – function defining the time derivative of x, ie: f(x)=dx

Returns

The integrated state

Sun Position

sun_position.approx_sun_position_ECI(utime)[source]

Formula taken from “Satellite Orbits: Models, Methods and Applications” by Motenbruck and Gill See section 3.3.2 on page 70 for the formula Modified to take unix time as input.

Args:
  • utime: unix timestamp

Returns:
  • sun pointing in Earth Centered Intertial (ECI) frame (km)

sun_position.unix_time_to_julian_day(unix_time)[source]

Takes in a unix timestamp and returns the julian day

Tasks

Beacon Task

class Tasks.beacon_task.task[source]

Bases: Task

beacon_packet()[source]

Creates a beacon packet containing the: CPU temp, IMU temp, gyro, acceleration, magnetic, and state byte. The state byte is the index of the current state in the alphabetically ordered state list. This data is packed into a c struct using struct.pack.

If no IMU is attached it returns a packet of 0s.

async main_task()[source]

Pushes a beacon packet onto the transmission queue.

Deployment Manager Task

Manages deployment of the cubesat’s antenna.

When contact is established with the groundstation (f_contact is True, set by the radio task) the cubesat switches to normal mode. If f_burn is false and the cubesat has been up for 5 minutes, the burnwire is activated. Every 24 hours the cubesat activates the burn wire.

class Tasks.deployment_manager.deployment_manager[source]

Bases: Task

async main_task()[source]

Manages deployment by activating burnwires when appropriate, and switching to normal operations if contact is established.

GNC Task

class Tasks.gnc.task[source]

Bases: Task

async main_task()[source]

Guidance, navigation and control task.

Uses the b-cross control law to detumble the satelite.

IMU Task

class Tasks.imu_task.task[source]

Bases: Task

async main_task()[source]

Prints the IMU data to the console.

Radio Task

Radio Task:

Manages all radio communication for the cubesat.

Tasks.radio.should_transmit()[source]

Return if we should transmit

class Tasks.radio.task[source]

Bases: Task

handle_chunk(header, response)[source]

Handler function for the chunk message type

handle_command(response)[source]

Handler function for commands

handle_naive(header, response)[source]

Handler function for the naive message type

async main_task()[source]

Manages all RF communications for the satelite.

In order to transmit something one should push to the transmission queue.

try_write(file, mode, data)[source]

Try to write to the sd card. If it fails, print an error

Safety Task

class Tasks.safety.task[source]

Bases: Task

async main_task()[source]

If the voltage is too low or the temp is to high, switch to safe mode. If the voltage is high enough and the temp is low enough, switch to normal mode.

Time Task

class Tasks.time_task.task[source]

Bases: Task

async main_task()[source]

Contains the code for the user defined task.

Parameters
  • *args – Variable number of arguments used for task execution.

  • **kwargs – Variable number of keyword arguments used for task execution.