Holds all flight software for the UConn Propulsive Landing team's rockets. 😄
All source code utilizes Hungarian Notation.
Create features in branches originating from the dev branch. When a feature is complete, make a pull request to merge it into dev.
- Install git (https://git-scm.com/install/)
- Clone this repo.
- Run
sudo apt update - Run
xargs -a linux_library_requirements.txt sudo apt-get install -y - Run
mkdir ThirdPartyLibrariesand thencd ThirdPartyLibraries - Clone WiringPi library with
git clone https://github.com/WiringPi/WiringPi.git - Run
cd WiringPi/wiringPi - Run
make - Run
sudo make install - Go back to
ThirdPartyLibrariesdirectory - Clone PCA9685 library with
git clone https://github.com/barulicm/PiPCA9685.git - Run
cd PiPCA9685 - Run
sudo cmake --workflow --preset install - Build the repo. Make sure you're in release mode because we want to use the hardware files.
- Create a logs folder inside the repo directory with
mkdir logs - Run the executable that gets created in the
build/folder. (NOTE: If working with GPS, run gps_setup.sh first)
In order to connect to the Raspberry Pi, it needs to be connected to Wi-Fi, and you need to know the IP address.
All 3 Raspberry Pis, including the 2 RPI5s and the 1 RPI4, are automatically configured to connect to the TP-Link Archer C54 router that
we bought. Once you know the IP address, run
ssh host@{IP-Address}, where host is the Pi's username and the IP address is the IP address the RPI is connected to.
Troubleshooting:
- Make sure you are connected to the same Wi-Fi as the Pi.
- If you used the same IP address as another Pi, it will yell at you. In that case, run the command
ssh-keygen -R {IP-address}.
If you buy more Raspberry Pis, Raspberry Pi's website will help you get started (https://www.raspberrypi.com/documentation/computers/getting-started.html). The main thing is to make sure SSH, I2C, Raspberry Pi Connect, and Serial Port are enabled.
- Install git (https://git-scm.com/install/)
- Clone this repo.
- Install brew with
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" - Run
brew bundle install - Build the repo. Make sure you're in either debug or simulation mode.
- Create a logs folder inside the repo directory with
mkdir logs - Run the executable that gets created in the
build/folder. (NOTE: If working with GPS, run gps_setup.sh first)
Windows is a little different because it doesn't come installed with a compiler, and since we use some POSIX libraries, we use WSL instead.
- Follow WSL tutorial below
- Follow Install Required Tools
- Follow Authenticate with GitHub
- Open a
WSLsession and typecode . - Install the
WSLextension on VS Code. - Install the CMake Tools extension on VS Code.
- Build the repo. Make sure you're in either debug or simulation mode.
- Create a logs folder inside the repo directory with
mkdir logs - Run the executable that gets created in the
build/folder. (NOTE: If working with GPS, run gps_setup.sh first)
-
Make sure all dependencies are installed
- linux_library_requirements.txt for Linux operating systems (Raspberry Pi)
- Brewfile for mac
-
If you get
warning depend.make has modification timeissue: try the commands:make clean make all
-
When SSHing into the Pi, the IP address might change, so go to the router's IP address to look at the exact IP address the Pi is connected to.
-
On Mac, if you installed the
CMake Toolsextension on VS Code and you haveninjainstalled, it might try to automatically select it. When you try to build withmake, it will throw an error.
-
Clone the repository:
git clone https://github.com/Propulsive-Landing/ferda.git
-
Enter the new folder:
cd ferda -
Generate the build files using CMake:
- For release:
(this uses the actual hardware sensors)
cmake -Bbuild -DCMAKE_BUILD_TYPE=Release . - For debug:
cmake -Bbuild -DCMAKE_BUILD_TYPE=Debug . - For Simulation:
cmake -Bbuild -DCMAKE_BUILD_TYPE=Simulation .
- For release:
-
Enter the newly generated
buildfolder:cd build -
Build the source:
make all
-
Run the executable inside the
buildfolder:sudo ./Ferda
-
Create a
logsfolder in the root of the project (without it, no logs will be saved).
The XBee module is a radio module that is used by our flight computer to send and receive data from ground control, such as when we want to instruct the rocket to launch or tell it to abort. To use the XBee from our Raspberry Pi, we must configure it properly. Here's how:
- XBee is currently (4/24/2026) configured to act as a terminal that only outputs values when there is a newline character.
- In hardware/RF.cpp, we configure serial port settings. Most importantly, the baud rate should be 38400, but make sure that the XBees are configured properly using XCTU software.
- For debugging, use
stty -F /dev/{RFPort} -ato see all serial line settings because most of the time, the baud rate is not set to what the other XBee uses or echo is not turned off. - Use
sttyto configure the device:(stty -F /dev/ttyUSB0
/dev/ttyUSB0may change per device). - Configure settings:
Disable settings using a minus sign and enable settings without it.
stty -F /dev/ttyUSB0 -settingToDisable settingToEnable
- The device configuration should resemble:
speed 38400 baud; line = 0; -echo
We use Adafruit's GPS module with a USB-C port to help with our navigation, but first we need to configure the GPS to specific settings. Every GPS receiver uses NMEA sentences which are ASCII text strings that display information such as position, speed, and time. The ones that we care about are:
- RMC (Recommended Minimum Specific GNSS Data)
- GGA (Global Positioning System Fix Data)
To write settings, we use the talker ID
PMTK. In every PMTK sentence we write, we need to compute the checksum, which is the XOR of every bit, and end with\r\n. These configuration steps are taken care of ingps_setup.sh. For the script to work, make sure python is installed and createa virtual environment where you have installedpyserialandadafruit-circuitpython-gps
- In
gps_setup.sh, we callgps_startup.pyto make sure the GPS has a fix because, in my testing, the settings only applied if there was a fix. There are also nice libraries for dealing with Adafruit GPS in Python. - Then we configure the baud rate to 9600 and disable echo.
- Then we use printf to write the setting,
$PMTK314,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*28\r\n'which enables the sentences we want. - Then we use printf to write the setting,
$PMTK251,38400*27\r\n'which changes the baud rate to 38400. - Then we need to change the baud rate for our serial port so we do
stty -F "$PORT" 38400 raw -echo -ixon - Lastly, we change the update rate to be 10 Hz using
'$PMTK220,100*2F\r\n'.
Documentation on PMTK command packets can be found in PMTK_A11.pdf. Documentation on the NMEA sentences can be found in CD+PA1616S+Datasheet.v03.pdf.
We currently have 2 bash scripts
gps_setup.shstartup.sh
gps_setup.sh is meant to configure all of the GPS settings where startup.sh is meant to run gps_setup.sh and then run the program
The camera can now load a persisted OpenCV calibration file at startup. By default it looks for ../calibration/camera_calibration.json when the executable is run from the build/ directory.
- Capture a set of checkerboard images from the camera at the same resolution used for flight.
- Build the calibration tool and run it from the
build/directory:Replace./CameraCalibrate ../calibration_images ../calibration/camera_calibration.json 9 6 0.024
9 6with the checkerboard inner-corner count and0.024with the square size in meters. - Restart Ferda. If the calibration file is present and valid, the camera uses it for
cv::undistortPoints(); otherwise it falls back to the current built-in constants.
The calibration output stores the camera matrix, distortion coefficients, image size, checkerboard dimensions, and RMS reprojection error. Keep one file per physical camera if the hardware changes.
Software-in-the-loop (SIL) testing allows you to connect your flight software to MATLAB's Simulink environment for real-time simulation. Follow the steps below to set up and run the SIL testing environment. SIL is not currently supported on Mac.
Install WSL2 - help
To run your flight software in a Linux environment on Windows, you need to install WSL2 (Windows Subsystem for Linux). The steps can vary depending on your setup, so you may have to do some troubleshooting to install it properly. Follow these steps:
-
Open PowerShell as Administrator and run:
wsl --install
-
Set up your preferred Linux distribution (e.g., Ubuntu) as your default WSL instance.
-
Ensure WSL2 is set as the default version:
wsl --set-default-version 2
-
Once your distribution is set up, enter your WSL terminal and update your package list:
sudo apt update
To build the flight software, you need to install several tools in your WSL environment:
-
CMake – for managing the build process:
sudo apt install cmake
-
Make – a build automation tool:
sudo apt install build-essential
-
Clang – a C++ compiler:
sudo apt install clang
-
Git – to clone and manage your repositories:
sudo apt install git
-
GitHub CLI (gh) – for interacting with GitHub:
sudo apt install gh
To interact with private GitHub repositories and push code, you need to authenticate with GitHub CLI:
-
Log in using the GitHub CLI:
gh auth login
-
Follow the prompts to authenticate via a web browser or with a GitHub token. Choose HTTPS as the protocol when prompted.
-
To get the host machine's IP: Open a WSL terminal and run:
ip route show | grep -i default | awk '{ print $3}'
Look for the IP address under the "Ethernet adapter vEthernet (WSL)" section.
-
To get the WSL instance's IP: In your WSL terminal, run:
hostname -I
-
Clone the flight software repository in your WSL terminal and switch to the correct branch (likely dev):
git clone https://github.com/Propulsive-Landing/ferda.git cd ferda git switch dev -
Build the software in simulation mode using the:
cmake -DCMAKE_BUILD_TYPE=Simulation -DSIM_LOCAL_PORT=8002 -DSIM_SERVER_PORT=8003 -DSIM_SERVER_IP="[Insert Host IP here]" -Bbuild . cd build make all
NOTE: Ports may change in future versions of simulation.
- In MATLAB, open the Simulink model for flight simulation.
- Set the UDPSend block to the WSL IP and port 8002, and ensure the UDPReceive is set to receive from any source.
- Install Simulink Desktop Real-Time to run the simulation in real time using the Add-On Manager in MATLAB.
- Make sure you have the Real-Time Kernel installed. A guide on this is shown here
- Set Simulink to Connected IO mode and start the simulation.
Switch back to WSL and run the flight software:
sudo ./FerdaFor users who prefer running the flight software directly on Windows without WSL, you can follow this guide to set up and run the simulation loop using Windows-native tools and loopback IP. However, please note that the flight computer uses a Linux-based OS, so discrepancies may occur.
To build and run the flight software natively on Windows, you'll need to install several development tools:
-
CMake – Download and install from the official website: CMake.
- Make sure to add CMake to your system path during installation.
-
Visual Studio (or Build Tools for Visual Studio) – This will provide a C++ compiler. You can install the Desktop development with C++ workload via the Visual Studio Installer or download Build Tools for Visual Studio from here.
- Ensure that the "MSVC" compiler and CMake support are selected during installation.
-
Git – Download and install Git from the Git website. This will allow you to clone the repository and manage version control.
-
GitHub CLI (gh) – Install GitHub CLI by downloading it from GitHub. Once installed, authenticate via:
gh auth login
Follow the prompts to authenticate with your GitHub account.
-
Clone the Repository: Open a Git Bash or command prompt on Windows and clone the repository:
git clone https://github.com/Propulsive-Landing/ferda.git cd ferda git switch dev -
Generate the Build Files Using CMake: In your Git Bash, command prompt, or terminal of choice, configure the build for simulation mode:
cmake -Bbuild -DCMAKE_BUILD_TYPE=Simulation -DSIM_LOCAL_PORT=8002 -DSIM_SERVER_PORT=8003 -DSIM_SERVER_IP="127.0.0.1"Explanation of flags:
-DCMAKE_BUILD_TYPE=Simulation: This flag sets the build for simulation.-DSIM_LOCAL_PORT=8002: Port used to receive data from Simulink.-DSIM_SERVER_PORT=8003: Port to send data to Simulink.-DSIM_SERVER_IP="127.0.0.1": Since we are running everything on the same machine, use127.0.0.1(loopback IP).
-
Compile the Software: Navigate to the build directory and compile the software:
cd build cmake --build .
This will generate the
Ferda.exeexecutable file in the build folder. -
Create a Logs Directory (Optional): If logging is enabled, make sure you have a
logsdirectory in the root of the project:mkdir logs
-
Launch MATLAB and Simulink: Open the Simulink model for flight simulation in MATLAB.
-
Configure UDPSend/UDPReceive Blocks:
- Set the UDPSend block to send data to the loopback IP (
127.0.0.1) on port8002. - Ensure that the UDPReceive block is configured to listen on port
8003.
- Set the UDPSend block to send data to the loopback IP (
-
Install Simulink Desktop Real-Time: You need the Simulink Desktop Real-Time add-on to run simulations in real-time. You can install this via the Add-On Manager in MATLAB.
-
Set Simulink to Connected IO Mode:
- Set Simulink to Connected IO mode to simulate real-time hardware.
-
Start the Simulation: Start the Simulink simulation.
- The rocket should remain static until your flight software commands it.
- Make sure your model is running in real time with 1:1 real-time to simulation
-
Return to the Command Prompt: Switch back to your command prompt or Git Bash.
-
Run the Flight Software: Start the flight software by running:
./Ferda.exe
Notes:
- Run Ferda (FSW) through WSL first if you're having trouble communicating with the simulation.
- WSL can be tricky to initially set up, but there is plenty of available documentation to resolve these issues. Start here and use Google if you still are having issues.
The flight software should be connected with the Simulink simulation, sending actuator commands and receiving simulated sensor data.
We have separated all of our header files into the include directory, all non-hardware files into the src directory, and all hardware files into the hardware directory. Depending on the build type that you used, we either use hardware if built in Release mode, hardware_test if built in Debug mode, or hardware_simulation if built in Simulation mode.
READMEs for src and hardware files are located in src files README and hardware files README.
If you add a new sensor, it belongs in the 3 hardware directories
All config files listed below are in the ferda directory but should at some point be in a Config folder:
- Angles.csv
- Height.csv
- Translation.csv
- linux_library_requirements.txt
- Brewfile
We do have a tests folder, but it has not been touched in a while. If you generate any unit tests, they should go here.
For all our GPIO pin handling, we have switched over to WiringPi because it was the easiest library to use.
For more information about Raspberry Pi's GPIO look here GPIO
For all our PWM handling, we wanted to use hardware PWM signals, but depending on which Raspberry Pi we are using, it can have a maximum of 4 hardware PWM GPIO pins. We decided to buy PCA9685 boards, which can generate hardware PWM signals. For more information about the PCA9685 look here PCA9685
- Look for command through RF.cpp
- Change mode based on command
We use the State machine pattern where we have defined several modes/states and one while loop in main. We use RF to change modes but modes can also change based on a condition in the code as well.
For our current Mode Flow, see here
In Mode.hpp, we define the Modes:
enum Phase
{
Standby,
Calibration,
ActuatorCalibration,
TestTVC,
ChirpTVC,
Idle,
Launch,
Land,
Terminate,
Abort,
Safe,
HotfireIdle,
ASITest,
WaterFlow,
ThreeSecondHotfire
};
In RF.hpp, a subset of the commands look like below where Command is an enum where we add the RF command
if (input_line == "ABORT")
ParsedCommand = RF::Command::ABORT;
else if (input_line == "ABORT_PAD")
ParsedCommand = RF::Command::ABORT_PAD;
else if (input_line == "ABORT_GROUND")
ParsedCommand = RF::Command::ABORT_GROUND;
else if (input_line == "TestTVC")
ParsedCommand = RF::Command::TestTVC;
else if (input_line == "ChirpTVC")
ParsedCommand = RF::Command::ChirpTVC;
else if (input_line == "Idle")
ParsedCommand = RF::Command::Idle;
else if (input_line == "Standby")
ParsedCommand = RF::Command::Standby;
In Main.cpp, the while loop looks like:
while (mode.Update(navigation, controller, gps, igniter, imu, magnetometer, valveControl, sparkPlug, pressureTransducer, loadCell, camera))
{
}
We either exit out of the while loop when the Mode is Terminate which returns false from Update() or if we hit Abort, before we ignite, then we exit the program right then and there
This is the flight software that runs on the Raspberry Pi, but like I've mentioned in Flow, the flight software is expecting to see commands through the radio, which is where Ground Control comes in. All Ground Control is found in Ground Control GitHub Link. To communicate with the rocket, we use ground control and the XBees.