Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use uv to streamline setup and update README #121

Merged
merged 12 commits into from
Jan 8, 2025
20 changes: 8 additions & 12 deletions .github/workflows/integration_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,28 +15,24 @@ jobs:
timeout-minutes: 5
strategy:
matrix:
python-version: ['3.13']
os: [ubuntu-latest, windows-latest]
fail-fast: False

steps:
- uses: actions/checkout@v4

- name: Set up Python ${{ matrix.python-version }}
- name: Install uv
uses: astral-sh/setup-uv@v5

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'
cache-dependency-path: '**/requirements*.txt'
python-version-file: "pyproject.toml"

- name: Install dependencies
run: |
python -W ignore -m pip install --upgrade pip
python -W ignore -m pip install .
python -W ignore -m pip install .[dev]
pip install pytest
- name: Install the project
run: uv sync

- name: Run integration tests
timeout-minutes: 3
run: |
pytest -v tests/test_integration.py
uv run pytest -v tests/test_integration.py
25 changes: 11 additions & 14 deletions .github/workflows/unit_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,31 +11,28 @@
jobs:
pytest:
name: pytest
runs-on: ${{matrix.os}}
runs-on: ubuntu-latest
timeout-minutes: 5
strategy:
matrix:
python-version: ['3.13']
os: [ubuntu-latest]
fail-fast: False
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}

- name: Install uv
uses: astral-sh/setup-uv@v5

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'
cache-dependency-path: '**/requirements*.txt'
- name: Install dependencies
run: |
python -W ignore -m pip install --upgrade pip
python -W ignore -m pip install .
python -W ignore -m pip install .[dev]
python-version-file: "pyproject.toml"

- name: Install the project
run: uv sync

- name: Run tests with coverage
run: |
# Run tests with coverage except for the integration tests
pytest --cov=airbrakes --cov-report=term --cov-report=html --ignore=tests/test_integration.py -v tests/
uv run pytest --cov=airbrakes --cov-report=term --cov-report=html --ignore=tests/test_integration.py -v tests/

# Add this new step to upload the coverage report as an artifact
- name: Upload coverage report
Expand Down
115 changes: 89 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,31 @@
# AirbrakesV2 🚀

# Table of Contents

- [Overview](#overview)
- [Design](#design)
- [System Architecture Flowchart](#system-architecture-flowchart)
- [Launch Data](#launch-data)
- [File Structure](#file-structure)
- [Quick Start](#quick-start)
- [Local Setup](#local-setup)
- [1. Clone the repository](#1-clone-the-repository)
- [2. Install the project](#2-install-the-project)
- [3. Install the pre-commit hook](#3-install-the-pre-commit-hook)
- [4. Make your changes and commit](#4-make-your-changes-and-commit)
- [Advanced Local Usage](#advanced-local-usage)
- [Running Mock Launches](#running-mock-launches)
- [Running Tests](#running-tests)
- [Running the Linter](#running-the-linter)
- [Pi Usage](#pi-usage)
- [Connecting to the Pi (SSH)](#connecting-to-the-pi-ssh)
- [Install and start the pigpio daemon on the Raspberry Pi](#install-and-start-the-pigpio-daemon-on-the-raspberry-pi)
- [Run a real flight with real hardware](#run-a-real-flight-with-real-hardware)
- [Running Test Scripts](#running-test-scripts)
- [Contributing](#contributing)
- [License](#license)


## Overview
This project is for controlling our Air brakes system with the goal of making our rocket "hit" its target apogee. We have a Raspberry Pi 4 as the brains of our system which runs our code. It connects to a servo motor to control the extension of our air brakes and an [IMU](https://www.microstrain.com/inertial-sensors/3dm-cx5-15) (basically an altimeter, accelerometer, and gyroscope). The code follows the [finite state machine](https://www.tutorialspoint.com/design_pattern/state_pattern.htm) design pattern, using the [`AirbrakesContext`](https://github.com/NCSU-High-Powered-Rocketry-Club/AirbrakesV2/blob/main/airbrakes/airbrakes.py) to manage interactions between the states, hardware, logging, and data processing.

Expand Down Expand Up @@ -121,67 +147,94 @@ AirbrakesV2/
| ├── data_handling/
│ │ ├── [files related to the processing of data ...]
│ ├── [files which control the airbrakes at a high level ...]
| ├── main.py [main file used to run on the rocket]
| ├── constants.py [file for constants used in the project]
├── tests/ [used for testing all the code]
│ ├── ...
├── logs/ [log files made by the logger]
│ ├── ...
├── launch_data/ [real flight data collected from the rocket]
│ ├── ...
├── scripts/ [small files to test individual components like the servo]
│ ├── ...
├── main.py [main file used to run on the rocket]
├── constants.py [file for constants used in the project]
├── pyproject.toml [configuration file for the project]
├── README.md
```

## Quick Start

This project strongly recommends using [`uv`](https://docs.astral.sh/uv/) to manage and install
the project. To quickly run the mock simulation, simply run:

```bash
uvx --from git+https://github.com/NCSU-High-Powered-Rocketry-Club/AirbrakesV2.git mock
```

You should see the mock simulation running with a display!

_Note: We will continue using `uv` for the rest of this README, if you don't want to use `uv`, you can set up the project using Python. See [Legacy Project Setup](legacy_project_setup.md) for more information._

## Local Setup

**This project uses Python 3.13. Using an older version may not work since we use newer language features**
If you want to contribute to the project, you will need to set up the project locally. Luckily,
the only other thing you need to install is [`git`](https://git-scm.com/) for version control.

### Clone the repository:
### 1. Clone the repository:

```
git clone https://github.com/NCSU-High-Powered-Rocketry-Club/AirbrakesV2.git
cd AirbrakesV2
```

### Set up a virtual environment:

### 2. Install the project:
```bash
python -m venv .venv
uv run mock
```

This will install the project, including development dependencies, activate the virtual environment and run the mock simulation.

_Note: It is important to use `uv run` instead of `uvx` since the `uvx` environment is isolated from
the project. See the [uv documentation](https://docs.astral.sh/uv/concepts/tools/#relationship-to-uv-run) for more information._

# For Linux
source .venv/bin/activate
# For Windows
.\.venv\Scripts\activate
_Note 2: The more "correct" command to run is `uv sync`. This will install the project and its dependencies, but not run the mock simulation._

### 3. Install the pre-commit hook:
```
uv run pre-commit install
```
This will install a pre-commit hook that will run the linter before you commit your changes.

### Install the required dependencies:
### 4. Make your changes and commit:

After you have made your changes, you can commit them:
```bash
pip install .[dev]
git add .
git commit -m "Your commit message"
```
_There are libraries that only fully work when running on the Pi (gpiozero, mscl), so if you're having trouble importing them locally, program the best you can and test your changes on the pi._

You will see the linter run now. If one of the checks failed, you can resolve them by following the
instructions in [Running the Linter](#running-the-linter).

```bash
git push -u origin branch-name
```
pre-commit install
```
This will install a pre-commit hook that will run the linter before you commit your changes.

## Local Usage
## Advanced Local Usage

### Running Mock Launches
Testing our code can be difficult, so we've developed a way to run mock launches based on previous flight data--the rocket pretends, in real-time, that it's flying through a previous launch.

To run a mock launch, make sure to first specify the path to the CSV file for the previous launch's data in `constants.py` and then run:
To run a mock launch, run:
```bash
python3 main.py -m
uv run mock
```
If you want to run a mock launch, but with the real servo running, run:
```bash
python3 main.py -m -r
uv run mock -r
```
There are some additional options you can use when running a mock launch. To view them all, run:
```bash
python3 main.py --help
uv run mock --help
```

### Running Tests
Expand All @@ -191,9 +244,11 @@ _Note: Unit tests do not work on Windows (only `test_integration.py` will work).

To run the tests, run this command from the project root directory:
```bash
pytest
uv run pytest
```

If your virtual environment is activated, you can simply run the tests with `pytest`

To generate a coverage report from the tests:
```bash
pytest --cov=airbrakes --cov-report=term
Expand All @@ -216,11 +271,14 @@ ruff format .

## Pi Usage

_There are libraries that only fully work when running on the Pi (gpiozero, mscl), so if you're having trouble importing them locally, program the best you can and test your changes on the pi._


### Connecting to the Pi (SSH)
In order to connect to the Pi, you need first to set up a mobile hotspot with the name `HPRC`, password `tacholycos`, and `2.4 GHz` band. Next, turn on the Pi and it will automatically connect to your hotspot. Once it's connected, find the Pi's IP Address, and in your terminal run:
```bash
ssh pi@[IP.ADDRESS]
# Its password is raspberry
# Its password is "raspberry"
cd AirbrakesV2/
```

Expand All @@ -231,11 +289,16 @@ _Every time the pi boots up, you must run this in order for the servo to work. W
sudo pigpiod
```

### Run a real flight with real hardware:
```bash
uv run real
```

### Running Test Scripts
During development, you may want to run individual scripts to test components. For example, to test the servo, run:
```bash
# Make sure you are in the root directory,
python3 -m scripts.run_servo
# Make sure you are in the root directory:
uv run scripts/run_servo.py
```

## Contributing
Expand Down
2 changes: 1 addition & 1 deletion airbrakes/airbrakes.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
from collections import deque
from typing import TYPE_CHECKING

from airbrakes.constants import ServoExtension
from airbrakes.data_handling.apogee_predictor import ApogeePredictor
from airbrakes.data_handling.data_processor import IMUDataProcessor
from airbrakes.data_handling.imu_data_packet import EstimatedDataPacket
from airbrakes.data_handling.logger import Logger
from airbrakes.hardware.imu import IMU
from airbrakes.hardware.servo import Servo
from airbrakes.state import StandbyState, State
from constants import ServoExtension

if TYPE_CHECKING:
from airbrakes.data_handling.processed_data_packet import ProcessedDataPacket
Expand Down
File renamed without changes.
6 changes: 3 additions & 3 deletions airbrakes/data_handling/apogee_predictor.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@

from scipy.optimize import curve_fit

from airbrakes.data_handling.processed_data_packet import ProcessedDataPacket
from constants import (
from airbrakes.constants import (
APOGEE_PREDICTION_MIN_PACKETS,
BUFFER_SIZE_IN_BYTES,
CURVE_FIT_INITIAL,
Expand All @@ -28,7 +27,8 @@
STOP_SIGNAL,
UNCERTAINTY_THRESHOLD,
)
from utils import modify_multiprocessing_queue_windows
from airbrakes.data_handling.processed_data_packet import ProcessedDataPacket
from airbrakes.utils import modify_multiprocessing_queue_windows

PREDICTED_COAST_TIMESTAMPS = np.arange(0, FLIGHT_LENGTH_SECONDS, INTEGRATION_TIME_STEP_SECONDS)

Expand Down
7 changes: 5 additions & 2 deletions airbrakes/data_handling/data_processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@
import numpy.typing as npt
from scipy.spatial.transform import Rotation as R

from airbrakes.constants import (
ACCEL_DEADBAND_METERS_PER_SECOND_SQUARED,
GRAVITY_METERS_PER_SECOND_SQUARED,
)
from airbrakes.data_handling.imu_data_packet import EstimatedDataPacket
from airbrakes.data_handling.processed_data_packet import ProcessedDataPacket
from constants import ACCEL_DEADBAND_METERS_PER_SECOND_SQUARED, GRAVITY_METERS_PER_SECOND_SQUARED
from utils import deadband
from airbrakes.utils import deadband


class IMUDataProcessor:
Expand Down
10 changes: 5 additions & 5 deletions airbrakes/data_handling/logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,23 @@
from pathlib import Path
from typing import Any, Literal

from utils import modify_multiprocessing_queue_windows
from airbrakes.utils import modify_multiprocessing_queue_windows

if sys.platform != "win32":
from faster_fifo import Queue

from msgspec import to_builtins

from airbrakes.data_handling.imu_data_packet import EstimatedDataPacket, IMUDataPacket
from airbrakes.data_handling.logged_data_packet import LoggedDataPacket
from airbrakes.data_handling.processed_data_packet import ProcessedDataPacket
from constants import (
from airbrakes.constants import (
BUFFER_SIZE_IN_BYTES,
IDLE_LOG_CAPACITY,
LOG_BUFFER_SIZE,
MAX_GET_TIMEOUT_SECONDS,
STOP_SIGNAL,
)
from airbrakes.data_handling.imu_data_packet import EstimatedDataPacket, IMUDataPacket
from airbrakes.data_handling.logged_data_packet import LoggedDataPacket
from airbrakes.data_handling.processed_data_packet import ProcessedDataPacket


class Logger:
Expand Down
12 changes: 6 additions & 6 deletions airbrakes/hardware/imu.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,7 @@
else:
from multiprocessing.queues import Empty

from airbrakes.data_handling.imu_data_packet import (
EstimatedDataPacket,
IMUDataPacket,
RawDataPacket,
)
from constants import (
from airbrakes.constants import (
BUFFER_SIZE_IN_BYTES,
ESTIMATED_DESCRIPTOR_SET,
IMU_TIMEOUT_SECONDS,
Expand All @@ -31,6 +26,11 @@
RAW_DESCRIPTOR_SET,
STOP_SIGNAL,
)
from airbrakes.data_handling.imu_data_packet import (
EstimatedDataPacket,
IMUDataPacket,
RawDataPacket,
)


class IMU:
Expand Down
2 changes: 1 addition & 1 deletion airbrakes/hardware/servo.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import gpiozero

from constants import SERVO_DELAY_SECONDS, ServoExtension
from airbrakes.constants import SERVO_DELAY_SECONDS, ServoExtension


class Servo:
Expand Down
Loading
Loading