Module MAPLEAF.ENV.environment
Main wrapper and data classes that tie together all of the environmental models and are queried by instances of Rocket
Expand source code
''' Main wrapper and data classes that tie together all of the environmental models and are queried by instances of `MAPLEAF.Rocket.Rocket` '''
import math
from collections import namedtuple
from typing import Union
from MAPLEAF.ENV import (LaunchRail, atmosphericModelFactory,
earthModelFactory, meanWindModelFactory,
turbulenceModelFactory)
from MAPLEAF.IO import SubDictReader, defaultConfigValues
from MAPLEAF.Motion import (AngularVelocity, ForceMomentSystem, Quaternion,
RigidBodyState, RigidBodyState_3DoF, Vector)
__all__ = [ "EnvironmentalConditions", "Environment" ]
# This named tuple is the object used to return information from the Environmental models to the rocket
# It is subsequently passed to all rocket objects, who use it in their calculations if they have
# relevant calculations to do (aerodynamics, air-breathing propulsion)
EnvironmentalConditions = namedtuple(
"EnvironmentalConditions",
[
"ASLAltitude",
"Temp",
"Pressure",
"Density",
"DynamicViscosity",
"Wind",
"MeanWind",
"TurbWind",
],
)
class Environment():
'''
Class wraps Wind Models, Atmospheric properties models, and earth/gravity models, presenting a
single interface for communication with flight vehicles
'''
def __init__(self, simDefinition=None, silent=False):
''' Sets up the Wind, Atmospheric, and Earth models requested in the Sim Definition file '''
self.launchRail = None
if simDefinition != None:
# Whenever we're running a real simulation, should always end up here
envDictReader = SubDictReader("Environment", simDefinition)
self.meanWindModel = meanWindModelFactory(envDictReader, silent=silent)
self.turbulenceModel = turbulenceModelFactory(envDictReader, silent=silent)
self.atmosphericModel = atmosphericModelFactory(envDictReader=envDictReader)
self.earthModel = earthModelFactory(envDictReader)
self.launchSiteElevation = envDictReader.tryGetFloat("LaunchSite.elevation")
self.launchSiteLatitude = envDictReader.tryGetFloat("LaunchSite.latitude")
self.launchSiteLongitude = envDictReader.tryGetFloat("LaunchSite.longitude")
# Check if being launched from a launch rail
launchRailLength = envDictReader.getFloat("LaunchSite.railLength")
if launchRailLength > 0:
# Initialize a launch rail, aligned with the rocket's initial direction
initialRocketPosition_towerFrame = envDictReader.getVector("Rocket.position")
# Check whether precise initial orientation has been specified
rotationAxis = envDictReader.tryGetVector("Rocket.rotationAxis", defaultValue=None)
if rotationAxis != None:
rotationAngle = math.radians(envDictReader.getFloat("Rocket.rotationAngle"))
initOrientation = Quaternion(rotationAxis, rotationAngle)
else:
# Calculate initial orientation quaternion in launch tower frame
rotationAxis = envDictReader.tryGetVector("Rocket.rotationAxis", defaultValue=None)
if rotationAxis != None:
rotationAngle = math.radians(self.rocketDictReader.getFloat("Rocket.rotationAngle"))
initOrientation = Quaternion(rotationAxis, rotationAngle)
else:
# Calculate initial orientation quaternion in launch tower frame
initialDirection = envDictReader.getVector("Rocket.initialDirection").normalize()
angleFromVertical = Vector(0,0,1).angle(initialDirection)
rotationAxis = Vector(0,0,1).crossProduct(initialDirection)
initOrientation = Quaternion(rotationAxis, angleFromVertical)
# TODO: Get from rocket, or calculate the same way - so that it works with rotationAxis + Angle
initialDirection = envDictReader.getVector("Rocket.initialDirection").normalize()
angleFromVertical = Vector(0,0,1).angle(initialDirection)
rotationAxis = Vector(0,0,1).crossProduct(initialDirection)
initOrientation = Quaternion(rotationAxis, angleFromVertical)
initPosition_seaLevelENUFrame = initialRocketPosition_towerFrame + Vector(0, 0, self.launchSiteElevation)
launchTowerState_local = RigidBodyState(position=initPosition_seaLevelENUFrame, orientation=initOrientation)
launchTowerState_global = self.earthModel.convertIntoGlobalFrame(launchTowerState_local, self.launchSiteLatitude, self.launchSiteLongitude)
towerDirection_global = launchTowerState_global.orientation.rotate(Vector(0, 0, 1))
self.launchRail = LaunchRail(launchTowerState_global.position, towerDirection_global, launchRailLength, earthRotationRate=self.earthModel.rotationRate)
else:
# Construct default environment from default parameters when no sim definition is passed in
# Need additional default value here in case a SimDefinition is not passed in (otherwise SimDefinition takes care of default values)
self.meanWindModel = meanWindModelFactory()
self.turbulenceModel = None
self.atmosphericModel = atmosphericModelFactory()
self.earthModel = earthModelFactory()
self.launchSiteElevation = float(defaultConfigValues["Environment.LaunchSite.elevation"])
self.launchSiteLatitude = float(defaultConfigValues["Environment.LaunchSite.latitude"])
self.launchSiteLongitude = float(defaultConfigValues["Environment.LaunchSite.longitude"])
def convertInitialStateToGlobalFrame(self, initialStateInLaunchTowerFrame):
'''
Used to convert the rocket's initial kinematic state from the launch tower frame (in which it is defined) and
into the global inertia frame
Takes a rigid body state defined in the launch tower frame and converts it to the global frame
For a flat earth model, this just adjusts for the altitude of the launch site,
but for a rotating earth model, this modifies every part of the rigid body state:
1. Position is redefined relative to the center of the earth (Acoording to lat/lon)
2. The velocity of earth's rotation is added to the velocity
3. The orientation is redefined relative to the global frame
4. The angular velocity of the earth is added to the rocket's initial state
.. note:: If a launch rail is being used, set the launch-tower-frame angular velocity to zero before doing the conversion
'''
# In all cases, first redefine the present state relative to sea level
initialStateInLaunchTowerFrame.position += Vector(0,0,self.launchSiteElevation)
# Set launch-tower-frame angular velocity to zero if using a launch rail
if self.launchRail != None:
initialStateInLaunchTowerFrame.angularVelocity = AngularVelocity(0,0,0)
# Call the current earth model's conversion function
return self.earthModel.convertIntoGlobalFrame(initialStateInLaunchTowerFrame, self.launchSiteLatitude, self.launchSiteLongitude)
def convertStateToENUFrame(self, globalFrameState: Union[RigidBodyState, RigidBodyState_3DoF]) -> Union[RigidBodyState, RigidBodyState_3DoF]:
altitude = self.earthModel.getAltitude(*globalFrameState.position)
position = Vector(0, 0, altitude) # Frame moves with the aircraft so x/y are always zero
inertialToENURotation = self.earthModel.getInertialToENUFrameRotation(*globalFrameState.position)
ENUToGlobalRotation = inertialToENURotation.conjugate()
velocity = ENUToGlobalRotation.rotate(globalFrameState.velocity)
try:
orientation = ENUToGlobalRotation * globalFrameState.orientation
angVel = ENUToGlobalRotation.rotate(globalFrameState.angularVelocity)
return RigidBodyState(position, velocity, orientation, angVel)
except:
return RigidBodyState_3DoF(position, velocity)
#### Get all air/atmospheric properties ####
def getAirProperties(self, position: Vector, time=None) -> EnvironmentalConditions:
''' Pass in a vector representing the aircraft's position in the global inertial frame of reference '''
# ASL Altitude is calculated differently depending on whether the earth is modelled as flat, round, or ellipsoidal
ASLAltitude = self.earthModel.getAltitude(*position)
# Ground level is remain constant as that of the launch site
AGLAltitude = ASLAltitude - self.launchSiteElevation
# Get wind based on AGL, in North-East-Up frame
# TODO: This is only desirable for low-altitude flights, should switch to ASL altitude once out of the atmospheric boundary layer
meanWind, turbWind = self.getWind(AGLAltitude, time)
# Add earth's rotation speed to the mean wind
# For No/Flat Earth models, the rotationRate will be zero and this will have no effect
distanceFromRotationAxis = math.sqrt(position.X**2 + position.Y**2)
earthRotationSpeed = self.earthModel.rotationRate * distanceFromRotationAxis
# Earth's surface rotates towards the east, so add the rotation velocity to the East component (X) of the local ENU frame
meanWind = meanWind + Vector(earthRotationSpeed,0,0)
# Wind is calculated in the North-East-Up frame, and needs to be rotated into the global inertial frame
# For No/Flat Earth models, the rotation will return a zero-rotation Quaternion, so this has no effect
orientationOfENUFrameInGlobalFrame = self.earthModel.getInertialToENUFrameRotation(*position)
meanWind = orientationOfENUFrameInGlobalFrame.rotate(meanWind)
turbWind = orientationOfENUFrameInGlobalFrame.rotate(turbWind)
# Now calculate total wind in global frame
totalWind = meanWind + turbWind
# Add air properties based on ASL, return the result
return EnvironmentalConditions(
ASLAltitude,
*self.atmosphericModel.getAirProperties(ASLAltitude, time), # Asterisk unpacks the returned values
totalWind,
meanWind,
turbWind
)
#### Wind properties ####
def getWind(self, AGLElevation, time=None):
''' Returns wind in the North-East-Up (Y-X-Z) frame '''
meanWindVel = self.meanWindModel.getMeanWind(AGLElevation)
if self.turbulenceModel != None:
turbWindVel = self.turbulenceModel.getTurbVelocity(AGLElevation, meanWindVel, time)
else:
turbWindVel = Vector(0,0,0)
return meanWindVel, turbWindVel
#### Wrappers for launch rail functions ####
def applyLaunchRailMotionConstraints(self, state, time):
if self.launchRail == None:
# Majority of the time we'll be off the rail and this will run
return state
onLaunchRail, adjustedState = self.launchRail.applyLaunchTowerMotionConstraint(state, time)
# Delete rail if we've left it
if not onLaunchRail:
print("Launch rail cleared")
self.launchRail = None
return adjustedState
def applyLaunchTowerForce(self, state, time, unadjustedForce):
if self.launchRail == None:
# Majority of the time we'll be off the rail and this will run
return unadjustedForce
return self.launchRail.applyLaunchTowerForce(state, time, unadjustedForce)
#### Gravity ####
def getGravityForce(self, inertia, state) -> ForceMomentSystem:
'''
Inputs:
inertia: (`MAPLEAF.Motion.Inertia`)
state: (`MAPLEAF.Motion.RigidBodyState`/`MAPLEAF.Motion.RigidBodyState_3DoF`)
Returns:
gravityForce: (ForceMomentSystem) defined in the rocket's local frame
'''
# Get gravity force in the global frame
gravityForce = self.earthModel.getGravityForce(inertia, state)
try:
# Convert to local frame if in 6DoF simulation
gravityForce = state.orientation.conjugate().rotate(gravityForce) # rotate into local frame when in 6DoF mode
except AttributeError:
pass # Don't do anything in 3DoF mode (No local frame exists)
return ForceMomentSystem(gravityForce, inertia.CG)
Classes
class Environment (simDefinition=None, silent=False)
-
Class wraps Wind Models, Atmospheric properties models, and earth/gravity models, presenting a single interface for communication with flight vehicles
Sets up the Wind, Atmospheric, and Earth models requested in the Sim Definition file
Expand source code
class Environment(): ''' Class wraps Wind Models, Atmospheric properties models, and earth/gravity models, presenting a single interface for communication with flight vehicles ''' def __init__(self, simDefinition=None, silent=False): ''' Sets up the Wind, Atmospheric, and Earth models requested in the Sim Definition file ''' self.launchRail = None if simDefinition != None: # Whenever we're running a real simulation, should always end up here envDictReader = SubDictReader("Environment", simDefinition) self.meanWindModel = meanWindModelFactory(envDictReader, silent=silent) self.turbulenceModel = turbulenceModelFactory(envDictReader, silent=silent) self.atmosphericModel = atmosphericModelFactory(envDictReader=envDictReader) self.earthModel = earthModelFactory(envDictReader) self.launchSiteElevation = envDictReader.tryGetFloat("LaunchSite.elevation") self.launchSiteLatitude = envDictReader.tryGetFloat("LaunchSite.latitude") self.launchSiteLongitude = envDictReader.tryGetFloat("LaunchSite.longitude") # Check if being launched from a launch rail launchRailLength = envDictReader.getFloat("LaunchSite.railLength") if launchRailLength > 0: # Initialize a launch rail, aligned with the rocket's initial direction initialRocketPosition_towerFrame = envDictReader.getVector("Rocket.position") # Check whether precise initial orientation has been specified rotationAxis = envDictReader.tryGetVector("Rocket.rotationAxis", defaultValue=None) if rotationAxis != None: rotationAngle = math.radians(envDictReader.getFloat("Rocket.rotationAngle")) initOrientation = Quaternion(rotationAxis, rotationAngle) else: # Calculate initial orientation quaternion in launch tower frame rotationAxis = envDictReader.tryGetVector("Rocket.rotationAxis", defaultValue=None) if rotationAxis != None: rotationAngle = math.radians(self.rocketDictReader.getFloat("Rocket.rotationAngle")) initOrientation = Quaternion(rotationAxis, rotationAngle) else: # Calculate initial orientation quaternion in launch tower frame initialDirection = envDictReader.getVector("Rocket.initialDirection").normalize() angleFromVertical = Vector(0,0,1).angle(initialDirection) rotationAxis = Vector(0,0,1).crossProduct(initialDirection) initOrientation = Quaternion(rotationAxis, angleFromVertical) # TODO: Get from rocket, or calculate the same way - so that it works with rotationAxis + Angle initialDirection = envDictReader.getVector("Rocket.initialDirection").normalize() angleFromVertical = Vector(0,0,1).angle(initialDirection) rotationAxis = Vector(0,0,1).crossProduct(initialDirection) initOrientation = Quaternion(rotationAxis, angleFromVertical) initPosition_seaLevelENUFrame = initialRocketPosition_towerFrame + Vector(0, 0, self.launchSiteElevation) launchTowerState_local = RigidBodyState(position=initPosition_seaLevelENUFrame, orientation=initOrientation) launchTowerState_global = self.earthModel.convertIntoGlobalFrame(launchTowerState_local, self.launchSiteLatitude, self.launchSiteLongitude) towerDirection_global = launchTowerState_global.orientation.rotate(Vector(0, 0, 1)) self.launchRail = LaunchRail(launchTowerState_global.position, towerDirection_global, launchRailLength, earthRotationRate=self.earthModel.rotationRate) else: # Construct default environment from default parameters when no sim definition is passed in # Need additional default value here in case a SimDefinition is not passed in (otherwise SimDefinition takes care of default values) self.meanWindModel = meanWindModelFactory() self.turbulenceModel = None self.atmosphericModel = atmosphericModelFactory() self.earthModel = earthModelFactory() self.launchSiteElevation = float(defaultConfigValues["Environment.LaunchSite.elevation"]) self.launchSiteLatitude = float(defaultConfigValues["Environment.LaunchSite.latitude"]) self.launchSiteLongitude = float(defaultConfigValues["Environment.LaunchSite.longitude"]) def convertInitialStateToGlobalFrame(self, initialStateInLaunchTowerFrame): ''' Used to convert the rocket's initial kinematic state from the launch tower frame (in which it is defined) and into the global inertia frame Takes a rigid body state defined in the launch tower frame and converts it to the global frame For a flat earth model, this just adjusts for the altitude of the launch site, but for a rotating earth model, this modifies every part of the rigid body state: 1. Position is redefined relative to the center of the earth (Acoording to lat/lon) 2. The velocity of earth's rotation is added to the velocity 3. The orientation is redefined relative to the global frame 4. The angular velocity of the earth is added to the rocket's initial state .. note:: If a launch rail is being used, set the launch-tower-frame angular velocity to zero before doing the conversion ''' # In all cases, first redefine the present state relative to sea level initialStateInLaunchTowerFrame.position += Vector(0,0,self.launchSiteElevation) # Set launch-tower-frame angular velocity to zero if using a launch rail if self.launchRail != None: initialStateInLaunchTowerFrame.angularVelocity = AngularVelocity(0,0,0) # Call the current earth model's conversion function return self.earthModel.convertIntoGlobalFrame(initialStateInLaunchTowerFrame, self.launchSiteLatitude, self.launchSiteLongitude) def convertStateToENUFrame(self, globalFrameState: Union[RigidBodyState, RigidBodyState_3DoF]) -> Union[RigidBodyState, RigidBodyState_3DoF]: altitude = self.earthModel.getAltitude(*globalFrameState.position) position = Vector(0, 0, altitude) # Frame moves with the aircraft so x/y are always zero inertialToENURotation = self.earthModel.getInertialToENUFrameRotation(*globalFrameState.position) ENUToGlobalRotation = inertialToENURotation.conjugate() velocity = ENUToGlobalRotation.rotate(globalFrameState.velocity) try: orientation = ENUToGlobalRotation * globalFrameState.orientation angVel = ENUToGlobalRotation.rotate(globalFrameState.angularVelocity) return RigidBodyState(position, velocity, orientation, angVel) except: return RigidBodyState_3DoF(position, velocity) #### Get all air/atmospheric properties #### def getAirProperties(self, position: Vector, time=None) -> EnvironmentalConditions: ''' Pass in a vector representing the aircraft's position in the global inertial frame of reference ''' # ASL Altitude is calculated differently depending on whether the earth is modelled as flat, round, or ellipsoidal ASLAltitude = self.earthModel.getAltitude(*position) # Ground level is remain constant as that of the launch site AGLAltitude = ASLAltitude - self.launchSiteElevation # Get wind based on AGL, in North-East-Up frame # TODO: This is only desirable for low-altitude flights, should switch to ASL altitude once out of the atmospheric boundary layer meanWind, turbWind = self.getWind(AGLAltitude, time) # Add earth's rotation speed to the mean wind # For No/Flat Earth models, the rotationRate will be zero and this will have no effect distanceFromRotationAxis = math.sqrt(position.X**2 + position.Y**2) earthRotationSpeed = self.earthModel.rotationRate * distanceFromRotationAxis # Earth's surface rotates towards the east, so add the rotation velocity to the East component (X) of the local ENU frame meanWind = meanWind + Vector(earthRotationSpeed,0,0) # Wind is calculated in the North-East-Up frame, and needs to be rotated into the global inertial frame # For No/Flat Earth models, the rotation will return a zero-rotation Quaternion, so this has no effect orientationOfENUFrameInGlobalFrame = self.earthModel.getInertialToENUFrameRotation(*position) meanWind = orientationOfENUFrameInGlobalFrame.rotate(meanWind) turbWind = orientationOfENUFrameInGlobalFrame.rotate(turbWind) # Now calculate total wind in global frame totalWind = meanWind + turbWind # Add air properties based on ASL, return the result return EnvironmentalConditions( ASLAltitude, *self.atmosphericModel.getAirProperties(ASLAltitude, time), # Asterisk unpacks the returned values totalWind, meanWind, turbWind ) #### Wind properties #### def getWind(self, AGLElevation, time=None): ''' Returns wind in the North-East-Up (Y-X-Z) frame ''' meanWindVel = self.meanWindModel.getMeanWind(AGLElevation) if self.turbulenceModel != None: turbWindVel = self.turbulenceModel.getTurbVelocity(AGLElevation, meanWindVel, time) else: turbWindVel = Vector(0,0,0) return meanWindVel, turbWindVel #### Wrappers for launch rail functions #### def applyLaunchRailMotionConstraints(self, state, time): if self.launchRail == None: # Majority of the time we'll be off the rail and this will run return state onLaunchRail, adjustedState = self.launchRail.applyLaunchTowerMotionConstraint(state, time) # Delete rail if we've left it if not onLaunchRail: print("Launch rail cleared") self.launchRail = None return adjustedState def applyLaunchTowerForce(self, state, time, unadjustedForce): if self.launchRail == None: # Majority of the time we'll be off the rail and this will run return unadjustedForce return self.launchRail.applyLaunchTowerForce(state, time, unadjustedForce) #### Gravity #### def getGravityForce(self, inertia, state) -> ForceMomentSystem: ''' Inputs: inertia: (`MAPLEAF.Motion.Inertia`) state: (`MAPLEAF.Motion.RigidBodyState`/`MAPLEAF.Motion.RigidBodyState_3DoF`) Returns: gravityForce: (ForceMomentSystem) defined in the rocket's local frame ''' # Get gravity force in the global frame gravityForce = self.earthModel.getGravityForce(inertia, state) try: # Convert to local frame if in 6DoF simulation gravityForce = state.orientation.conjugate().rotate(gravityForce) # rotate into local frame when in 6DoF mode except AttributeError: pass # Don't do anything in 3DoF mode (No local frame exists) return ForceMomentSystem(gravityForce, inertia.CG)
Methods
def applyLaunchRailMotionConstraints(self, state, time)
-
Expand source code
def applyLaunchRailMotionConstraints(self, state, time): if self.launchRail == None: # Majority of the time we'll be off the rail and this will run return state onLaunchRail, adjustedState = self.launchRail.applyLaunchTowerMotionConstraint(state, time) # Delete rail if we've left it if not onLaunchRail: print("Launch rail cleared") self.launchRail = None return adjustedState
def applyLaunchTowerForce(self, state, time, unadjustedForce)
-
Expand source code
def applyLaunchTowerForce(self, state, time, unadjustedForce): if self.launchRail == None: # Majority of the time we'll be off the rail and this will run return unadjustedForce return self.launchRail.applyLaunchTowerForce(state, time, unadjustedForce)
def convertInitialStateToGlobalFrame(self, initialStateInLaunchTowerFrame)
-
Used to convert the rocket's initial kinematic state from the launch tower frame (in which it is defined) and into the global inertia frame
Takes a rigid body state defined in the launch tower frame and converts it to the global frame For a flat earth model, this just adjusts for the altitude of the launch site, but for a rotating earth model, this modifies every part of the rigid body state: 1. Position is redefined relative to the center of the earth (Acoording to lat/lon)
2. The velocity of earth's rotation is added to the velocity
3. The orientation is redefined relative to the global frame
4. The angular velocity of the earth is added to the rocket's initial stateNote: If a launch rail is being used, set the launch-tower-frame angular velocity to zero before doing the conversion
Expand source code
def convertInitialStateToGlobalFrame(self, initialStateInLaunchTowerFrame): ''' Used to convert the rocket's initial kinematic state from the launch tower frame (in which it is defined) and into the global inertia frame Takes a rigid body state defined in the launch tower frame and converts it to the global frame For a flat earth model, this just adjusts for the altitude of the launch site, but for a rotating earth model, this modifies every part of the rigid body state: 1. Position is redefined relative to the center of the earth (Acoording to lat/lon) 2. The velocity of earth's rotation is added to the velocity 3. The orientation is redefined relative to the global frame 4. The angular velocity of the earth is added to the rocket's initial state .. note:: If a launch rail is being used, set the launch-tower-frame angular velocity to zero before doing the conversion ''' # In all cases, first redefine the present state relative to sea level initialStateInLaunchTowerFrame.position += Vector(0,0,self.launchSiteElevation) # Set launch-tower-frame angular velocity to zero if using a launch rail if self.launchRail != None: initialStateInLaunchTowerFrame.angularVelocity = AngularVelocity(0,0,0) # Call the current earth model's conversion function return self.earthModel.convertIntoGlobalFrame(initialStateInLaunchTowerFrame, self.launchSiteLatitude, self.launchSiteLongitude)
def convertStateToENUFrame(self, globalFrameState: Union[RigidBodyState, RigidBodyState_3DoF]) ‑> Union[RigidBodyState, RigidBodyState_3DoF]
-
Expand source code
def convertStateToENUFrame(self, globalFrameState: Union[RigidBodyState, RigidBodyState_3DoF]) -> Union[RigidBodyState, RigidBodyState_3DoF]: altitude = self.earthModel.getAltitude(*globalFrameState.position) position = Vector(0, 0, altitude) # Frame moves with the aircraft so x/y are always zero inertialToENURotation = self.earthModel.getInertialToENUFrameRotation(*globalFrameState.position) ENUToGlobalRotation = inertialToENURotation.conjugate() velocity = ENUToGlobalRotation.rotate(globalFrameState.velocity) try: orientation = ENUToGlobalRotation * globalFrameState.orientation angVel = ENUToGlobalRotation.rotate(globalFrameState.angularVelocity) return RigidBodyState(position, velocity, orientation, angVel) except: return RigidBodyState_3DoF(position, velocity)
def getAirProperties(self, position: Vector, time=None) ‑> EnvironmentalConditions
-
Pass in a vector representing the aircraft's position in the global inertial frame of reference
Expand source code
def getAirProperties(self, position: Vector, time=None) -> EnvironmentalConditions: ''' Pass in a vector representing the aircraft's position in the global inertial frame of reference ''' # ASL Altitude is calculated differently depending on whether the earth is modelled as flat, round, or ellipsoidal ASLAltitude = self.earthModel.getAltitude(*position) # Ground level is remain constant as that of the launch site AGLAltitude = ASLAltitude - self.launchSiteElevation # Get wind based on AGL, in North-East-Up frame # TODO: This is only desirable for low-altitude flights, should switch to ASL altitude once out of the atmospheric boundary layer meanWind, turbWind = self.getWind(AGLAltitude, time) # Add earth's rotation speed to the mean wind # For No/Flat Earth models, the rotationRate will be zero and this will have no effect distanceFromRotationAxis = math.sqrt(position.X**2 + position.Y**2) earthRotationSpeed = self.earthModel.rotationRate * distanceFromRotationAxis # Earth's surface rotates towards the east, so add the rotation velocity to the East component (X) of the local ENU frame meanWind = meanWind + Vector(earthRotationSpeed,0,0) # Wind is calculated in the North-East-Up frame, and needs to be rotated into the global inertial frame # For No/Flat Earth models, the rotation will return a zero-rotation Quaternion, so this has no effect orientationOfENUFrameInGlobalFrame = self.earthModel.getInertialToENUFrameRotation(*position) meanWind = orientationOfENUFrameInGlobalFrame.rotate(meanWind) turbWind = orientationOfENUFrameInGlobalFrame.rotate(turbWind) # Now calculate total wind in global frame totalWind = meanWind + turbWind # Add air properties based on ASL, return the result return EnvironmentalConditions( ASLAltitude, *self.atmosphericModel.getAirProperties(ASLAltitude, time), # Asterisk unpacks the returned values totalWind, meanWind, turbWind )
def getGravityForce(self, inertia, state) ‑> ForceMomentSystem
-
Inputs
inertia: (
Inertia
) state: (RigidBodyState
/RigidBodyState_3DoF
)Returns
gravityForce
- (ForceMomentSystem) defined in the rocket's local frame
Expand source code
def getGravityForce(self, inertia, state) -> ForceMomentSystem: ''' Inputs: inertia: (`MAPLEAF.Motion.Inertia`) state: (`MAPLEAF.Motion.RigidBodyState`/`MAPLEAF.Motion.RigidBodyState_3DoF`) Returns: gravityForce: (ForceMomentSystem) defined in the rocket's local frame ''' # Get gravity force in the global frame gravityForce = self.earthModel.getGravityForce(inertia, state) try: # Convert to local frame if in 6DoF simulation gravityForce = state.orientation.conjugate().rotate(gravityForce) # rotate into local frame when in 6DoF mode except AttributeError: pass # Don't do anything in 3DoF mode (No local frame exists) return ForceMomentSystem(gravityForce, inertia.CG)
def getWind(self, AGLElevation, time=None)
-
Returns wind in the North-East-Up (Y-X-Z) frame
Expand source code
def getWind(self, AGLElevation, time=None): ''' Returns wind in the North-East-Up (Y-X-Z) frame ''' meanWindVel = self.meanWindModel.getMeanWind(AGLElevation) if self.turbulenceModel != None: turbWindVel = self.turbulenceModel.getTurbVelocity(AGLElevation, meanWindVel, time) else: turbWindVel = Vector(0,0,0) return meanWindVel, turbWindVel
class EnvironmentalConditions (ASLAltitude, Temp, Pressure, Density, DynamicViscosity, Wind, MeanWind, TurbWind)
-
EnvironmentalConditions(ASLAltitude, Temp, Pressure, Density, DynamicViscosity, Wind, MeanWind, TurbWind)
Ancestors
- builtins.tuple
Instance variables
var ASLAltitude
-
Alias for field number 0
var Density
-
Alias for field number 3
var DynamicViscosity
-
Alias for field number 4
var MeanWind
-
Alias for field number 6
var Pressure
-
Alias for field number 2
var Temp
-
Alias for field number 1
var TurbWind
-
Alias for field number 7
var Wind
-
Alias for field number 5