# Getting Started¶

Here we go through a simplified rocket trajectory simulation to get you started. Let’s start by importing the rocketpy module.

```
[1]:
```

```
from rocketpy import Environment, SolidMotor, Rocket, Flight
```

If you are using Jupyter Notebooks, it is recommended to run the following line to make matplotlib plots which will be shown later interactive and higher quality.

```
[2]:
```

```
%matplotlib notebook
```

## Setting Up a Simulation¶

### Creating an Environment for Spaceport America¶

```
[2]:
```

```
Env = Environment(
railLength=5.2,
latitude=32.990254,
longitude=-106.974998,
elevation=1400
)
```

To get weather data from the GFS forecast, available online, we run the following lines.

First, we set tomorrow’s date.

```
[3]:
```

```
import datetime
tomorrow = datetime.date.today() + datetime.timedelta(days=1)
Env.setDate((tomorrow.year, tomorrow.month, tomorrow.day, 12)) # Hour given in UTC time
```

Then, we tell Env to use a GFS forecast to get the atmospheric conditions for flight.

Don’t mind the warning, it just means that not all variables, such as wind speed or atmospheric temperature, are available at all altitudes given by the forecast.

```
[4]:
```

```
Env.setAtmosphericModel(type='Forecast', file='GFS')
```

```
C:\Users\GiovaniCeotto\Repositories\RocketPy\rocketpy\Environment.py:1956: UserWarning: Some values were missing from this weather dataset, therefore, certain pressure levels were removed.
warnings.warn(
```

We can see what the weather will look like by calling the info method!

```
[5]:
```

```
Env.info()
```

```
Launch Site Details
Launch Rail Length: 5.2 m
Launch Date: 2021-02-25 12:00:00 UTC
Launch Site Latitude: 32.99025°
Launch Site Longitude: -106.97500°
Reference Datum: SIRGAS2000
Launch Site UTM coordinates: 315468.64 W 3651938.65 N
Launch Site UTM zone: 13S
Launch Site Surface Elevation: 1471.5 m
Atmospheric Model Details
Atmospheric Model Type: Forecast
Forecast Maximum Height: 54.863 km
Forecast Time Period: From 2021-02-24 12:00:00 to 2021-03-06 12:00:00 UTC
Forecast Hour Interval: 3 hrs
Forecast Latitude Range: From -90.0 ° To 90.0 °
Forecast Longitude Range: From 0.0 ° To 359.75 °
Surface Atmospheric Conditions
Surface Wind Speed: 1.30 m/s
Surface Wind Direction: 353.37°
Surface Wind Heading: 173.37°
Surface Pressure: 852.03 hPa
Surface Temperature: 283.96 K
Surface Air Density: 1.045 kg/m³
Surface Speed of Sound: 337.81 m/s
Atmospheric Model Plots
```

### Creating a Motor¶

A solid rocket motor is used in this case. To create a motor, the SolidMotor class is used and the required arguments are given.

The SolidMotor class requires the user to have a thrust curve ready. This can come either from a .eng file for a commercial motor, such as below, or a .csv file from a static test measurement.

Besides the thrust curve, other parameters such as grain properties and nozzle dimensions must also be given.

```
[6]:
```

```
Pro75M1670 = SolidMotor(
thrustSource="../../data/motors/Cesaroni_M1670.eng",
burnOut=3.9,
grainNumber=5,
grainSeparation=5/1000,
grainDensity=1815,
grainOuterRadius=33/1000,
grainInitialInnerRadius=15/1000,
grainInitialHeight=120/1000,
nozzleRadius=33/1000,
throatRadius=11/1000,
interpolationMethod='linear'
)
```

To see what our thrust curve looks like, along with other import properties, we invoke the info method yet again. You may try the allInfo method if you want more information all at once!

```
[7]:
```

```
Pro75M1670.info()
```

```
Motor Details
Total Burning Time: 3.9 s
Total Propellant Mass: 2.956 kg
Propellant Exhaust Velocity: 2038.745 m/s
Average Thrust: 1545.218 N
Maximum Thrust: 2200.0 N at 0.15 s after ignition.
Total Impulse: 6026.350 Ns
Plots
```

### Creating a Rocket¶

A rocket is composed of several components. Namely, we must have a motor (good thing we have the Pro75M1670 ready), a couple of aerodynamic surfaces (nose cone, fins and tail) and parachutes (if we are not launching a missile).

Let’s start by initializing our rocket, named Calisto, supplying it with the Pro75M1670 engine, entering its inertia properties, some dimensions and also its drag curves.

```
[8]:
```

```
Calisto = Rocket(
motor=Pro75M1670,
radius=127/2000,
mass=19.197-2.956,
inertiaI=6.60,
inertiaZ=0.0351,
distanceRocketNozzle=-1.255,
distanceRocketPropellant=-0.85704,
powerOffDrag='../../data/calisto/powerOffDragCurve.csv',
powerOnDrag='../../data/calisto/powerOnDragCurve.csv'
)
Calisto.setRailButtons([0.2, -0.5])
```

#### Adding Aerodynamic Surfaces¶

Now we define the aerodynamic surfaces. They are really straight forward.

```
[9]:
```

```
NoseCone = Calisto.addNose(length=0.55829, kind="vonKarman", distanceToCM=0.71971)
FinSet = Calisto.addFins(4, span=0.100, rootChord=0.120, tipChord=0.040, distanceToCM=-1.04956)
Tail = Calisto.addTail(topRadius=0.0635, bottomRadius=0.0435, length=0.060, distanceToCM=-1.194656)
```

#### Adding Parachutes¶

Finally, we have parachutes! Calisto will have two parachutes, Drogue and Main.

Both parachutes are activated by some special algorithm, which is usually really complex and a trade secret. Most algorithms are based on pressure sampling only, while some also use acceleration info.

RocketPy allows you to define a trigger function which will decide when to activate the ejection event for each parachute. This trigger function is supplied with pressure measurement at a predefined sampling rate. This pressure signal is usually noisy, so artificial noise parameters can be given. Call help(Rocket.addParachute) for more details. Furthermore, the trigger function also recieves the complete state vector of the rocket, allowing us to use velocity, acceleration or even attitude to decide when the parachute event should be triggered.

Here, we define our trigger functions rather simply using Python. However, you can call the exact code which will fly inside your rocket as well.

```
[10]:
```

```
def drogueTrigger(p, y):
# p = pressure
# y = [x, y, z, vx, vy, vz, e0, e1, e2, e3, w1, w2, w3]
# activate drogue when vz < 0 m/s.
return True if y[5] < 0 else False
def mainTrigger(p, y):
# p = pressure
# y = [x, y, z, vx, vy, vz, e0, e1, e2, e3, w1, w2, w3]
# activate main when vz < 0 m/s and z < 800 m.
return True if y[5] < 0 and y[2] < 800 else False
Main = Calisto.addParachute('Main',
CdS=10.0,
trigger=mainTrigger,
samplingRate=105,
lag=1.5,
noise=(0, 8.3, 0.5))
Drogue = Calisto.addParachute('Drogue',
CdS=1.0,
trigger=drogueTrigger,
samplingRate=105,
lag=1.5,
noise=(0, 8.3, 0.5))
```

Just be careful if you run this last cell multiple times! If you do so, your rocket will end up with lots of parachutes which activate together, which may cause problems during the flight simulation. We advise you to re-run all cells which define our rocket before running this, preventing unwanted old parachutes. Alternatively, you can run the following lines to remove parachutes.

```
Calisto.parachutes.remove(Drogue)
Calisto.parachutes.remove(Main)
```

## Simulating a Flight¶

Simulating a flight trajectory is as simples as initializing a Flight class object givin the rocket and environement set up above as inputs. The launch rail inclination and heading are also given here.

```
[11]:
```

```
TestFlight = Flight(rocket=Calisto, environment=Env, inclination=85, heading=0)
```

## Analysing the Results¶

RocketPy gives you many plots, thats for sure! They are divided into sections to keep them organized. Alternatively, see the Flight class documentation to see how to get plots for specific variables only, instead of all of them at once.

```
[12]:
```

```
TestFlight.allInfo()
```

```
Initial Conditions
Position - x: 0.00 m | y: 0.00 m | z: 1471.47 m
Velocity - Vx: 0.00 m/s | Vy: 0.00 m/s | Vz: 0.00 m/s
Attitude - e0: 0.999 | e1: -0.044 | e2: -0.000 | e3: 0.000
Euler Angles - Spin φ : 0.00° | Nutation θ: -5.00° | Precession ψ: 0.00°
Angular Velocity - ω1: 0.00 rad/s | ω2: 0.00 rad/s| ω3: 0.00 rad/s
Launch Rail Orientation
Launch Rail Inclination: 85.00°
Launch Rail Heading: 0.00°
Surface Wind Conditions
Frontal Surface Wind Speed: -1.29 m/s
Lateral Surface Wind Speed: -0.15 m/s
Rail Departure State
Rail Departure Time: 0.363 s
Rail Departure Velocity: 25.800 m/s
Rail Departure Static Margin: 2.133 c
Rail Departure Angle of Attack: 2.857°
Rail Departure Thrust-Weight Ratio: 10.143
Rail Departure Reynolds Number: 1.946e+05
BurnOut State
BurnOut time: 3.900 s
Altitude at burnOut: 656.382 m (AGL)
Rocket velocity at burnOut: 280.170 m/s
Freestream velocity at burnOut: 280.271 m/s
Mach Number at burnOut: 0.835
Kinetic energy at burnOut: 6.374e+05 J
Apogee
Apogee Altitude: 4780.609 m (ASL) | 3309.143 m (AGL)
Apogee Time: 25.874 s
Apogee Freestream Speed: 25.265 m/s
Events
Drogue Ejection Triggered at: 25.876 s
Drogue Parachute Inflated at: 27.376 s
Drogue Parachute Inflated with Freestream Speed of: 29.080 m/s
Drogue Parachute Inflated at Height of: 3298.143 m (AGL)
Impact
X Impact: 2015.767 m
Y Impact: 1344.453 m
Time of Impact: 202.495 s
Velocity at Impact: -17.477 m/s
Maximum Values
Maximum Speed: 286.278 m/s at 3.38 s
Maximum Mach Number: 0.852 Mach at 3.38 s
Maximum Reynolds Number: 2.056e+06 at 3.31 s
Maximum Dynamic Pressure: 4.070e+04 Pa at 3.35 s
Maximum Acceleration: 105.104 m/s² at 0.15 s
Maximum Gs: 10.718 g at 0.15 s
Maximum Upper Rail Button Normal Force: 0.257 N
Maximum Upper Rail Button Shear Force: 0.257 N
Maximum Lower Rail Button Normal Force: 0.257 N
Maximum Lower Rail Button Shear Force: 0.257 N
Numerical Integration Information
Maximum Allowed Flight Time: 600.000000 s
Maximum Allowed Time Step: inf s
Minimum Allowed Time Step: 0.000000e+00 s
Relative Error Tolerance: 1e-06
Absolute Error Tolerance: [0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 1e-06, 1e-06, 1e-06, 1e-06, 0.001, 0.001, 0.001]
Allow Event Overshoot: True
Terminate Simulation on Apogee: False
Number of Time Steps Used: 730
Number of Derivative Functions Evaluation: 2060
Average Function Evaluations per Time Step: 2.821918
Trajectory 3d Plot
```

```
Trajectory Kinematic Plots
```

```
Angular Position Plots
```

```
Path, Attitude and Lateral Attitude Angle plots
```

```
Trajectory Angular Velocity and Acceleration Plots
```

```
Trajectory Force Plots
```

```
Trajectory Energy Plots
```

```
Trajectory Fluid Mechanics Plots
```

```
Trajectory Stability and Control Plots
```

## Using Simulation for Design¶

Here, we go through a couple of examples which make use of RockePy in cool ways to help us design our rocket.

### Dynamic Stability Analysis¶

Ever wondered how static stability translates into dynamic stability? Different static margins result in different dynamic behaviour, which also depends on the rocket’s rotational inertial.

Let’s make use of RocketPy’s helper class called Function to explore how the dynamic stability of Calisto varies if we change the fins span by a certain factor.

```
[13]:
```

```
# Helper class
from rocketpy import Function
# Prepare Rocket Class
Calisto = Rocket(motor=Pro75M1670,
radius=127/2000,
mass=19.197-2.956,
inertiaI=6.60,
inertiaZ=0.0351,
distanceRocketNozzle=-1.255,
distanceRocketPropellant=-0.85704,
powerOffDrag='../../data/calisto/powerOffDragCurve.csv',
powerOnDrag='../../data/calisto/powerOnDragCurve.csv')
Calisto.setRailButtons([0.2, -0.5])
Nose = Calisto.addNose(length=0.55829, kind="vonKarman", distanceToCM=0.71971)
FinSet = Calisto.addFins(4, span=0.1, rootChord=0.120, tipChord=0.040, distanceToCM=-1.04956)
Tail = Calisto.addTail(topRadius=0.0635, bottomRadius=0.0435, length=0.060, distanceToCM=-1.194656)
# Prepare Environment Class
Env = Environment(5.2, 9.8)
Env.setAtmosphericModel(type='CostumAtmosphere', wind_v=-5)
# Simulate Different Static Margins by Varying Fin Position
simulation_results = []
for factor in [0.5, 0.7, 0.9, 1.1, 1.3]:
# Modify rocket fin set by removing previous one and adding new one
Calisto.aerodynamicSurfaces.remove(FinSet)
FinSet = Calisto.addFins(4, span=0.1, rootChord=0.120, tipChord=0.040, distanceToCM=-1.04956*factor)
# Simulate
print('Simulating Rocket with Static Margin of {:1.3f}->{:1.3f} c'.format(Calisto.staticMargin(0), Calisto.staticMargin(Calisto.motor.burnOutTime)))
TestFlight = Flight(rocket=Calisto, environment=Env, inclination=90, heading=0, maxTimeStep=0.01, maxTime=5, terminateOnApogee=True, verbose=True)
# Post process flight data
TestFlight.postProcess()
# Store Results
staticMarginAtIginition = Calisto.staticMargin(0)
staticMarginAtOutOfRail = Calisto.staticMargin(TestFlight.outOfRailTime)
staticMarginAtSteadyState = Calisto.staticMargin(TestFlight.tFinal)
simulation_results += [(TestFlight.attitudeAngle, '{:1.2f} c | {:1.2f} c | {:1.2f} c'.format(staticMarginAtIginition, staticMarginAtOutOfRail, staticMarginAtSteadyState))]
Function.comparePlots(simulation_results, lower=0, upper=1.5, xlabel='Time (s)', ylabel='Attitude Angle (deg)')
```

```
Simulating Rocket with Static Margin of -1.444->-0.405 c
Simulation Completed at Time: 5.0000 s
Simulating Rocket with Static Margin of -0.046->0.993 c
Simulation Completed at Time: 5.0000 s
Simulating Rocket with Static Margin of 1.352->2.391 c
Simulation Completed at Time: 5.0000 s
Simulating Rocket with Static Margin of 2.750->3.789 c
Simulation Completed at Time: 5.0000 s
Simulating Rocket with Static Margin of 4.147->5.186 c
Simulation Completed at Time: 5.0000 s
```

### Characteristic Frequency Calculation¶

Here we analyse the characterist frequency of oscilation of our rocket just as it leaves the launch rail. Note that when we ran TestFlight.allInfo(), one of the plots already showed us the frequency spectrum of our flight. Here, however, we have more control of what we are plotting.

```
[14]:
```

```
import numpy as np
import matplotlib.pyplot as plt
Env = Environment(
railLength=5.2,
latitude=32.990254,
longitude=-106.974998,
elevation=1400
)
Env.setAtmosphericModel(type='CostumAtmosphere', wind_v=-5)
# Prepare Motor
Pro75M1670 = SolidMotor(
thrustSource="../../data/motors/Cesaroni_M1670.eng",
burnOut=3.9,
grainNumber=5,
grainSeparation=5/1000,
grainDensity=1815,
grainOuterRadius=33/1000,
grainInitialInnerRadius=15/1000,
grainInitialHeight=120/1000,
nozzleRadius=33/1000,
throatRadius=11/1000,
interpolationMethod='linear'
)
# Prepare Rocket
Calisto = Rocket(
motor=Pro75M1670,
radius=127/2000,
mass=19.197-2.956,
inertiaI=6.60,
inertiaZ=0.0351,
distanceRocketNozzle=-1.255,
distanceRocketPropellant=-0.85704,
powerOffDrag='../../data/calisto/powerOffDragCurve.csv',
powerOnDrag='../../data/calisto/powerOnDragCurve.csv'
)
Calisto.setRailButtons([0.2, -0.5])
Nose = Calisto.addNose(length=0.55829, kind="vonKarman", distanceToCM=0.71971)
FinSet = Calisto.addFins(4, span=0.1, rootChord=0.120, tipChord=0.040, distanceToCM=-1.04956)
Tail = Calisto.addTail(topRadius=0.0635, bottomRadius=0.0435, length=0.060, distanceToCM=-1.194656)
# Simulate first 5 seconds of Flight
TestFlight = Flight(rocket=Calisto, environment=Env, inclination=90, heading=0, maxTimeStep=0.01, maxTime=5)
TestFlight.postProcess()
# Perform a Fourier Analysis
Fs = 100.0; # sampling rate
Ts = 1.0/Fs; # sampling interval
t = np.arange(1,400,Ts) # time vector
ff = 5; # frequency of the signal
y = TestFlight.attitudeAngle(t) - np.mean(TestFlight.attitudeAngle(t))
n = len(y) # length of the signal
k = np.arange(n)
T = n/Fs
frq = k/T # two sides frequency range
frq = frq[range(n//2)] # one side frequency range
Y = np.fft.fft(y)/n # fft computing and normalization
Y = Y[range(n//2)]
fig, ax = plt.subplots(2, 1)
ax[0].plot(t,y)
ax[0].set_xlabel('Time')
ax[0].set_ylabel('Signal')
ax[0].set_xlim((0, 5))
ax[1].plot(frq,abs(Y),'r') # plotting the spectrum
ax[1].set_xlabel('Freq (Hz)')
ax[1].set_ylabel('|Y(freq)|')
ax[1].set_xlim((0, 5))
plt.subplots_adjust(hspace=0.5)
plt.show()
```

### Apogee as a Function of Mass¶

This one is a classic one! We always need to know how much our rocket’s apogee will change when our payload gets havier.

```
[15]:
```

```
def apogee(mass):
# Prepare Environment
Env = Environment(
railLength=5.2,
latitude=32.990254,
longitude=-106.974998,
elevation=1400,
date=(2018, 6, 20, 18)
)
Env.setAtmosphericModel(type='CostumAtmosphere', wind_v=-5)
# Prepare Motor
Pro75M1670 = SolidMotor(
thrustSource="../../data/motors/Cesaroni_M1670.eng",
burnOut=3.9,
grainNumber=5,
grainSeparation=5/1000,
grainDensity=1815,
grainOuterRadius=33/1000,
grainInitialInnerRadius=15/1000,
grainInitialHeight=120/1000,
nozzleRadius=33/1000,
throatRadius=11/1000,
interpolationMethod='linear'
)
# Prepare Rocket
Calisto = Rocket(
motor=Pro75M1670,
radius=127/2000,
mass=mass,
inertiaI=6.60,
inertiaZ=0.0351,
distanceRocketNozzle=-1.255,
distanceRocketPropellant=-0.85704,
powerOffDrag='../../data/calisto/powerOffDragCurve.csv',
powerOnDrag='../../data/calisto/powerOnDragCurve.csv'
)
Calisto.setRailButtons([0.2, -0.5])
Nose = Calisto.addNose(length=0.55829, kind="vonKarman", distanceToCM=0.71971)
FinSet = Calisto.addFins(4, span=0.1, rootChord=0.120, tipChord=0.040, distanceToCM=-1.04956)
Tail = Calisto.addTail(topRadius=0.0635, bottomRadius=0.0435, length=0.060, distanceToCM=-1.194656)
# Simulate Flight until Apogee
TestFlight = Flight(rocket=Calisto, environment=Env, inclination=85, heading=0, terminateOnApogee=True)
return TestFlight.apogee
apogeebymass = Function(apogee, inputs="Mass (kg)", outputs="Estimated Apogee (m)")
apogeebymass.plot(8,20,20)
```

### Out of Rail Speed as a Function of Mass¶

To finish off, lets make a really important plot. Out of rail speed is the speed our rocket has when it is leaving the launch rail. This is crucial to make sure it can fly safely after leaving the rail. A common rule of thumb is that our rocket’s out of rail speed should be 4 times the wind speed so that it does not stall and become unstable.

```
[16]:
```

```
def speed(mass):
# Prepare Environment
Env = Environment(
railLength=5.2,
latitude=32.990254,
longitude=-106.974998,
elevation=1400,
date=(2018, 6, 20, 18)
)
Env.setAtmosphericModel(type='CostumAtmosphere', wind_v=-5)
# Prepare Motor
Pro75M1670 = SolidMotor(
thrustSource="../../data/motors/Cesaroni_M1670.eng",
burnOut=3.9,
grainNumber=5,
grainSeparation=5/1000,
grainDensity=1815,
grainOuterRadius=33/1000,
grainInitialInnerRadius=15/1000,
grainInitialHeight=120/1000,
nozzleRadius=33/1000,
throatRadius=11/1000,
interpolationMethod='linear'
)
# Prepare Rocket
Calisto = Rocket(
motor=Pro75M1670,
radius=127/2000,
mass=mass,
inertiaI=6.60,
inertiaZ=0.0351,
distanceRocketNozzle=-1.255,
distanceRocketPropellant=-0.85704,
powerOffDrag='../../data/calisto/powerOffDragCurve.csv',
powerOnDrag='../../data/calisto/powerOnDragCurve.csv'
)
Calisto.setRailButtons([0.2, -0.5])
Nose = Calisto.addNose(length=0.55829, kind="vonKarman", distanceToCM=0.71971)
FinSet = Calisto.addFins(4, span=0.1, rootChord=0.120, tipChord=0.040, distanceToCM=-1.04956)
Tail = Calisto.addTail(topRadius=0.0635, bottomRadius=0.0435, length=0.060, distanceToCM=-1.194656)
# Simulate Flight until Apogee
TestFlight = Flight(rocket=Calisto, environment=Env, inclination=85, heading=0, terminateOnApogee=True)
return TestFlight.outOfRailVelocity
speedbymass = Function(speed, inputs="Mass (kg)", outputs="Out of Rail Speed (m/s)")
speedbymass.plot(8,20,20)
```