Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
name: CI

on:
push:
branches: [main, develop]
pull_request:
workflow_dispatch:

concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true

permissions:
contents: read
pull-requests: write

jobs:
run:
name: Run script to publish simulation report
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Check out repository
uses: actions/checkout@v6

- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.14"

- name: Install the locked dependencies
run: |
python -m pip install "uv==0.12.0"
uv sync --locked --extra dev

- name: Lint
run: uv run ruff check

- name: Format check
run: uv run ruff format --check

- name: Run script
run: |
uv run python simulation.py

- name: Upload report.html as artifact
id: artifact-upload-step
if: ${{ !cancelled() }}
uses: actions/upload-artifact@v7
with:
name: report
path: _output/report.html
archive: false

- name: Comment on PR
uses: actions/github-script@v9
if: github.event_name == 'pull_request'
with:
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: `Simulation report is ready!

View it here:
${{ steps.artifact-upload-step.outputs.artifact-url }}`
})
12 changes: 12 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Repo specific
_output

# VSCode specific
.vscode/

# Python specific
.venv
.ci-venv
.ruff_cache
__pycache__
.pytest_cache
1 change: 1 addition & 0 deletions .python-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.14
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
# RocketTrajectorySim
This repositry hosts a rocket trajectory simulation server that runs a simulation on pipeline
This repositry hosts a rocket trajectory simulation server that runs a simulation on pipeline.
85 changes: 85 additions & 0 deletions custom_rocket.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
from rocketpy import Rocket
from rocketpy.motors import CylindricalTank, Fluid, HybridMotor
from rocketpy.motors.tank import MassFlowRateBasedTank
from rocketpy.sensors import Accelerometer, Barometer, GnssReceiver, Gyroscope


def create_custom_rocket():
"""
Create a custom rocket with a hybrid motor and an oxidizer tank.
"""

tank_shape = CylindricalTank(0.133, height=0.83)
oxidizer_tank = MassFlowRateBasedTank(
name="oxidizer_tank",
geometry=tank_shape,
flux_time=(0, 30),
initial_liquid_mass=13.9,
initial_gas_mass=2.13,
liquid_mass_flow_rate_in=0,
liquid_mass_flow_rate_out=0.425,
gas_mass_flow_rate_in=0,
gas_mass_flow_rate_out=0,
liquid=Fluid(name="HTP", density=1390),
gas=Fluid(name="N2", density=68),
)

hybrid_motor = HybridMotor(
thrust_source=1080,
dry_mass=0,
dry_inertia=(0, 0, 0),
center_of_dry_mass_position=0.015,
burn_time=(0, 30),
reshape_thrust_curve=False,
grain_number=1,
grain_separation=0,
grain_outer_radius=0.00843,
grain_initial_inner_radius=0.0295,
grain_initial_height=0.2757,
grain_density=900,
nozzle_radius=0.04425,
throat_radius=0.023,
interpolation_method="linear",
nozzle_position=0,
grains_center_of_mass_position=0.13785,
coordinate_system_orientation="nozzle_to_combustion_chamber",
)

# Add tank to motor
hybrid_motor.add_tank(tank=oxidizer_tank, position=1.4)

rocket = Rocket(
radius=0.22,
mass=70,
inertia=(26.54, 26.38, 1.6312),
center_of_mass_without_motor=0,
power_off_drag=0.25,
power_on_drag=0.25,
coordinate_system_orientation="tail_to_nose",
)

rocket.add_motor(hybrid_motor, position=-0.8)
rocket.add_nose(
length=1,
kind="vonKarman",
position=1.5,
)
rocket.add_trapezoidal_fins(
n=4,
span=0.4,
root_chord=0.5,
tip_chord=0.2,
position=-0.3,
)

gyro = Gyroscope(sampling_rate=100)
accelerometer = Accelerometer(sampling_rate=100)
gnss = GnssReceiver(sampling_rate=100)
baro = Barometer(sampling_rate=100)
rocket.add_sensor(gyro, position=0)
rocket.add_sensor(accelerometer, position=0)
rocket.add_sensor(gnss, position=0)
rocket.add_sensor(baro, position=0)
# rocket.draw()

return rocket
16 changes: 16 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[project]
name = "rockettrajectorysim"
version = "0.0.1"
description = "This repositry hosts a rocket trajectory simulation server that runs a simulation on pipeline"
readme = "README.md"
authors = [
{ name = "zuorenchen", email = "zuorenchen@m110.nthu.edu.tw" }
]
requires-python = ">=3.14"
dependencies = [
"rocketpy==1.13.0",
]
[project.optional-dependencies]
dev = [
"ruff>=0.16.1",
]
69 changes: 69 additions & 0 deletions simulation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import base64
import os

import matplotlib.pyplot as plt
from rocketpy import Environment, Flight

from custom_rocket import create_custom_rocket

plt.style.use("seaborn-v0_8-colorblind")

output_path = os.path.join(os.path.dirname(__file__), "_output/")


def _encode_image(path):
with open(path, "rb") as image_file:
encoded = base64.b64encode(image_file.read()).decode("utf-8")
return f'<img src="data:image/png;base64,{encoded}">'


def main():
"""Create and run a simulation then pack to report"""

# Create environment
env = Environment(
gravity=9.78825, # 大劉南/Q037/一等水準點 內政部103年公告二等重力點測量成果(計劃年度:2014) 全國衛星追蹤站暨基本控制點查詢系統
date=(2026, 8, 1, 7), # 15:00 local time
latitude=22.17492027, # 國家科學及技術委員會短期科研探空火箭發射場域
longitude=120.8926564,
elevation=31.9331,
datum="WGS84",
timezone="UTC",
max_expected_height=10000,
)
env.set_atmospheric_model(type="Windy", file="ECMWF")
env_plot_path = output_path + "env.png"
env.plots.info(filename=env_plot_path)

# Create rocket from script
custom_rocket = create_custom_rocket()

# Simulate a flight
test_flight = Flight(
rocket=custom_rocket,
environment=env,
inclination=85,
heading=90,
rail_length=12,
)
flight_plot_path = output_path + "trajectory_3d.png"
test_flight.plots.trajectory_3d(filename=flight_plot_path)

# Create report
html_report = f"""
<html>
<body>
<h1>Environment</h1>
{_encode_image(env_plot_path)}
<h1>Flight 3D</h1>
{_encode_image(flight_plot_path)}
</body>
</html>
"""

with open(output_path + "report.html", "w", encoding="utf+8") as f:
f.write(html_report)


if __name__ == "__main__":
main()
Loading