예제 #1
0
def forceFromCoefficients(rocketState, environment, Cd, Cl, CMx, CMy, CMz,
                          CPLocation, refArea, refLength):
    ''' Initialize ForceMomentSystem from all aerodynamic coefficients '''
    q = AeroParameters.getDynamicPressure(rocketState, environment)
    if q == 0.0:
        # No force without dynamic pressure
        return ForceMomentSystem(Vector(0, 0, 0))
    nonDimConstant = q * refArea

    #### Drag ####
    localFrameAirVel = AeroParameters.getLocalFrameAirVel(
        rocketState, environment)
    dragDirection = localFrameAirVel.normalize()
    dragForce = dragDirection * Cd

    #### Lift ####
    # Find the lift force direction
    # Lift force will be in the same plane as the normalForceDirection & rocketFrameAirVel vectors
    # But, the lift force will be perpendicular to rocketFraeAirVel, whereas the normalForceDirection might form an acute angle with it
    # Rotate the normal force direction vector until it's perpendicular with rocketFrameAirVel
    normalForceDir = AeroParameters.getNormalAeroForceDirection(
        rocketState, environment)
    rotAxis = localFrameAirVel.crossProduct(normalForceDir)
    angle = math.pi / 2 - localFrameAirVel.angle(normalForceDir)
    rotatorQuat = Quaternion(rotAxis, angle)
    liftDirection = rotatorQuat.rotate(normalForceDir)
    liftForce = liftDirection * Cl

    # Combine and redimensionalize
    totalForce = (dragForce + liftForce) * nonDimConstant

    #### Moments ####
    moments = Vector(CMx, CMy, CMz) * nonDimConstant * refLength

    return ForceMomentSystem(totalForce, CPLocation, moments)
예제 #2
0
def forceFromCdCN(rocketState,
                  environment,
                  Cd,
                  CN,
                  CPLocation,
                  refArea,
                  moment=None):
    ''' 
        Convenience function for Barrowman Aero methods
        Initialize ForceMomentSystem from aerodynamic coefficients Cd and CN
        Cd should NOT already be adjusted for angle of attack 
        Moment should be a dimensional moment vector
    '''
    angleOfAttack = AeroParameters.getTotalAOA(rocketState, environment)
    q = AeroParameters.getDynamicPressure(rocketState, environment)

    #### Axial Force ####
    CA = getDragToAxialForceFactor(angleOfAttack) * Cd
    axialForce = Vector(0, 0, -CA)  #Axial force is in the negative Z-direction

    #### Normal Force ####
    normalForce = AeroParameters.getNormalAeroForceDirection(
        rocketState, environment) * CN

    totalForce = (axialForce + normalForce) * refArea * q
    return ForceMomentSystem(totalForce, CPLocation, moment)
예제 #3
0
    def getAppliedForce(self, rocketState, time, environment, CG):
        Aref = self.rocket.Aref
        Mach = AeroParameters.getMachNumber(rocketState, environment)

        # Normal Force --------------------------------------------------------------------------------
        # TODO: Account for rate of pitch/yaw rotation in AOA calculation? Or do separate Pitch/Yaw damping moments?
        AOA = AeroParameters.getTotalAOA(rocketState, environment)
        normalForceCoefficient = AeroFunctions.Barrowman_GetCN(
            AOA, Aref, 0, self.baseArea)

        # Drag Force ---------------------------------------------------------------------------------------------
        #### Skin Friction ####
        skinFrictionDragCoefficient, rollDampingMoment = AeroFunctions.getCylindricalSkinFrictionDragCoefficientAndRollDampingMoment(rocketState, environment, self.length, Mach,  \
            self.surfaceRoughness, self.wettedArea, Aref, self.rocket.finenessRatio, self.rocket.fullyTurbulentBL)

        #### Pressure ####
        pressureDragCoefficient = self._getCd_Pressure(Mach)

        totalDragCoefficient = pressureDragCoefficient + skinFrictionDragCoefficient

        # Damping moments --------------------------------------------------------------------------------------
        # Roll damping due to skin friction
        dampingMoments = rollDampingMoment

        # Combine forces and return total -------------------------------------------------------------------------------------
        return AeroFunctions.forceFromCdCN(rocketState,
                                           environment,
                                           totalDragCoefficient,
                                           normalForceCoefficient,
                                           self.CPLocation,
                                           Aref,
                                           moment=dampingMoments)
예제 #4
0
    def getAppliedForce(self, rocketState, time, environment, CG):
        Aref = self.rocket.Aref

        # Normal Force ----------------------------------------------------------------------------------------
        AOA = AeroParameters.getTotalAOA(rocketState, environment)
        # Niskanen Eqn 3.26 - originally from Galejs
        normalForceCoefficient = 1.1 * (math.sin(AOA))**2
        normalForceCoefficient *= self.planformArea / Aref

        # Drag -----------------------------------------------------------------------------------------------
        # Skin Friction
        Mach = AeroParameters.getMachNumber(rocketState, environment)
        skinFrictionDragCoefficient, rollDampingMoment = AeroFunctions.getCylindricalSkinFrictionDragCoefficientAndRollDampingMoment(rocketState, environment, self.length, Mach, self.surfaceRoughness, \
            self.wettedArea, Aref, self.rocket.finenessRatio, self.rocket.fullyTurbulentBL)
        
        #There is no pressure drag associated with bodytubes - skin friction drag is total drag

        # Damping moments --------------------------------------------------------------------------------------
        dampingMoments = self._computeLongitudinalDampingMoments(rocketState, environment, CG)
        
        # Roll damping due to skin friction
        dampingMoments += rollDampingMoment

        # Compute & dimensionalize total-------------------------------------------------------------------------
        return AeroFunctions.forceFromCdCN(rocketState, environment, skinFrictionDragCoefficient, normalForceCoefficient, self.CPLocation, Aref, moment=dampingMoments)
예제 #5
0
    def getAppliedForce(self, rocketState, time, environment,
                        CG) -> ForceMomentSystem:
        Mach = AeroParameters.getMachNumber(rocketState, environment)
        Aref = self.rocket.Aref

        #### Normal Force ####
        AOA = AeroParameters.getTotalAOA(rocketState, environment)
        CN = AeroFunctions.Barrowman_GetCN(AOA, Aref, self.topArea,
                                           self.bottomArea)

        #### Pressure Drag ####
        if self.startDiameter > self.endDiameter:
            # Pressure base drag
            Cd_base = AeroFunctions.getBaseDragCoefficient(Mach)
            Cd_pressure = Cd_base * self.CdAdjustmentFactor

        else:
            # Pressure drag calculated like a nose cone
            if Mach < 1:
                # Niskanen pg. 48 eq. 3.87 - Power law interpolation
                Cd_pressure = self.SubsonicCdPolyCoeffs[
                    0] * Mach**self.SubsonicCdPolyCoeffs[1]

            elif Mach > 1 and Mach < 1.3:
                # Interpolate in transonic region - derived from Niskanen Appendix B, Eqns B.3 - B.6
                Cd_pressure = self.TransonicCdPolyCoeffs[0] + self.TransonicCdPolyCoeffs[1]*Mach +  \
                    self.TransonicCdPolyCoeffs[2]*Mach**2 + self.TransonicCdPolyCoeffs[3]*Mach**3

            else:
                Cd_pressure = getSupersonicPressureDragCoeff_Hoerner(
                    self.coneHalfAngle, Mach)

        # Make reference are the rocket's, not this objects
        Cd_pressure *= self.frontalArea / Aref

        #### Skin Friction Drag ####
        if self.wettedArea == 0:
            skinFrictionDragCoefficient = 0
            rollDampingMoment = Vector(0, 0, 0)
        else:
            skinFrictionDragCoefficient, rollDampingMoment = AeroFunctions.getCylindricalSkinFrictionDragCoefficientAndRollDampingMoment(rocketState, environment, \
                 self.length, Mach, self.surfaceRoughness, self.wettedArea, Aref, self.rocket.finenessRatio, self.rocket.fullyTurbulentBL)

        #### Total Drag ####
        Cd = Cd_pressure + skinFrictionDragCoefficient

        #### Assemble & return final force object ####
        return AeroFunctions.forceFromCdCN(rocketState,
                                           environment,
                                           Cd,
                                           CN,
                                           self.CPLocation,
                                           Aref,
                                           moment=rollDampingMoment)
예제 #6
0
파일: Fins.py 프로젝트: henrystoldt/MAPLEAF
    def _getCPXPos(self, Mach):
        ''' 
            Finds the chordwise position of the center of pressure, relative to the tip of the root chord (self.position) 
            Methods from Tactical missile design by Fleeman through Niskanen
        '''
        if Mach < 0.5:
            XF = self.XMACLeadingEdge + 0.25 * self.MACLength  #As per open rocket documentation

        elif Mach >= 2.0:
            beta = AeroParameters.getBeta(Mach)
            CPCalc = lambda AR, B: (AR * B - 0.67) / (
                2 * AR * B - 1
            )  #Eq 3.35 of open rocket documentation (greater than M = 2)
            XF = self.MACLength * CPCalc(
                self.aspectRatio,
                beta) + self.XMACLeadingEdge  #As per open rocket documentation

        else:
            #Between the two extremes is a polynomial curve fit
            #Fifth order polynomial fit as per Niskanen Eqn 3.36 (Originally from Fleeman)
            # Old code: XF = np.polyval(self.x, Mach)*self.MACLength + self.XMACLeadingEdge
            # Evaluate polynomial manually for maximum speed (8.5x improvement for 6 coeffs)
            # Speed test in test/speedTests/PolyvalSpeed.py
            polyval = 0
            nTerms = len(self.x)
            for i in range(nTerms):
                polyval += self.x[i] * Mach**(nTerms - 1 - i)

            XF = polyval * self.MACLength + self.XMACLeadingEdge  # Polyval defines the fraction of the MAC that the Cp is behind the leading edge

        return XF
예제 #7
0
파일: Fins.py 프로젝트: henrystoldt/MAPLEAF
 def subsonicNormalForce(Mach):  # Subsonic linear method
     tempBeta = AeroParameters.getBeta(Mach)
     CnAlpha = getFinCnAlpha_Subsonic_Barrowman(self.span,
                                                self.planformArea,
                                                tempBeta,
                                                self.midChordSweep)
     return getSubsonicFinNormalForce(airVelRelativeToFin,
                                      unitSpanTangentialAirVelocity,
                                      finNormal, self.spanwiseDirection,
                                      self.CPSpanWisePosition.length(),
                                      CnAlpha, self)
예제 #8
0
    def test_getNormalAeroForceDirection(self):
        zeroAngVel = AngularVelocity(0,0,0)
        zeroPos = Vector(0,0,0)
        
        state1 = RigidBodyState(zeroPos, self.dummyVelocity1, self.dummyOrientation1, zeroAngVel)
        assertVectorsAlmostEqual(self, AeroParameters.getNormalAeroForceDirection(state1, self.currentConditions), Vector(0, -1, 0))

        state2 = RigidBodyState(zeroPos, self.dummyVelocity1, self.dummyOrientation2, zeroAngVel)
        assertVectorsAlmostEqual(self, AeroParameters.getNormalAeroForceDirection(state2, self.currentConditions), Vector(0, 1, 0))

        state3 = RigidBodyState(zeroPos, self.dummyVelocity1, self.dummyOrientation3, zeroAngVel)
        assertVectorsAlmostEqual(self, AeroParameters.getNormalAeroForceDirection(state3, self.currentConditions), Vector(1, 0, 0))

        state4 = RigidBodyState(zeroPos, self.dummyVelocity1, self.dummyOrientation4, zeroAngVel)
        assertVectorsAlmostEqual(self, AeroParameters.getNormalAeroForceDirection(state4, self.currentConditions), Vector(1, 0, 0))

        state5 = RigidBodyState(zeroPos, self.dummyVelocity1, self.dummyOrientation5, zeroAngVel)
        assertVectorsAlmostEqual(self, AeroParameters.getNormalAeroForceDirection(state5, self.currentConditions), Vector(0.707, -0.707, 0),3)

        assertVectorsAlmostEqual(self, AeroParameters.getNormalAeroForceDirection(self.rocketState8, self.currentConditions), Vector(-0.99999, 0.0022736, 0),3)

        # Prep rocket by calling getLocalFrameAirVel first to cache AOA result, then get cached result from getTotalAOA
        def testLocalFrameAirVelNormalForceDir(vel, orientation, expectedResult, precision=7):
            state = RigidBodyState(Vector(0,0,0), vel, orientation, AngularVelocity(0,0,0))
            assertVectorsAlmostEqual(self, AeroParameters.getNormalAeroForceDirection(state, self.currentConditions), expectedResult, precision)

        testLocalFrameAirVelNormalForceDir(self.dummyVelocity1, self.dummyOrientation1, Vector(0, -1, 0))
        testLocalFrameAirVelNormalForceDir(self.dummyVelocity1, self.dummyOrientation2, Vector(0, 1, 0))
        testLocalFrameAirVelNormalForceDir(self.dummyVelocity1, self.dummyOrientation3, Vector(1, 0, 0))
        testLocalFrameAirVelNormalForceDir(self.dummyVelocity1, self.dummyOrientation4, Vector(1, 0, 0))
        testLocalFrameAirVelNormalForceDir(self.dummyVelocity1, self.dummyOrientation5, Vector(0.707, -0.707, 0), 3)
        testLocalFrameAirVelNormalForceDir(self.rocketState8.velocity, self.rocketState8.orientation, Vector(-0.99999, 0.0022736, 0),3)
예제 #9
0
    def test_getTotalAOA(self):
        def getDummyState(vel, orientation):
            zeroVec = Vector(0,0,0)
            zeroAngVel = AngularVelocity(rotationVector=zeroVec)
            return RigidBodyState(zeroVec, vel, orientation, zeroAngVel)

        state1 = getDummyState(self.dummyVelocity1, self.dummyOrientation1)
        self.assertAlmostEqual(AeroParameters.getTotalAOA(state1, self.currentConditions), math.radians(2))
        
        state2 = getDummyState(self.dummyVelocity1, self.dummyOrientation2)
        self.assertAlmostEqual(AeroParameters.getTotalAOA(state2, self.currentConditions), math.radians(2))

        state3 = getDummyState(self.dummyVelocity2, self.dummyOrientation3)
        self.assertAlmostEqual(AeroParameters.getTotalAOA(state3, self.currentConditions), math.radians(0.862405226))

        state4 = getDummyState(self.dummyVelocity2, self.dummyOrientation4)
        self.assertAlmostEqual(AeroParameters.getTotalAOA(state4, self.currentConditions), math.radians(2.862405226))

        self.assertAlmostEqual(AeroParameters.getTotalAOA(self.rocketState4, self.currentConditions), 0)

        state5 = getDummyState(self.dummyVelocity3, self.dummyOrientation6)
        self.assertAlmostEqual(AeroParameters.getTotalAOA(state5, self.currentConditions), math.radians(90))

        # Prep rocket by calling getLocalFrameAirVel first to cache AOA result, then get cached result from getTotalAOA
        def testLocalFrameAirVelAOA(vel, orientation, expectedResult):
            state = RigidBodyState(Vector(0,0,0), vel, orientation, AngularVelocity(rotationVector=Vector(0,0,0)))
            self.assertAlmostEqual(AeroParameters.getTotalAOA(state, self.currentConditions), math.radians(expectedResult))
            
        testLocalFrameAirVelAOA(self.dummyVelocity1, self.dummyOrientation1, 2)
        testLocalFrameAirVelAOA(self.dummyVelocity1, self.dummyOrientation2, 2)
        testLocalFrameAirVelAOA(self.dummyVelocity2, self.dummyOrientation3, 0.862405226)
        testLocalFrameAirVelAOA(self.dummyVelocity2, self.dummyOrientation4, 2.862405226)
        testLocalFrameAirVelAOA(self.rocketState4.velocity, self.rocketState4.orientation, 0)
        testLocalFrameAirVelAOA(self.dummyVelocity3, self.dummyOrientation6, 90)
예제 #10
0
    def getAppliedForce(self, rocketState, time, environment,
                        CG) -> ForceMomentSystem:
        Mach = AeroParameters.getMachNumber(rocketState, environment)
        Aref = self.rocket.Aref

        #### Normal Force ####
        AOA = AeroParameters.getTotalAOA(rocketState, environment)
        CN = AeroFunctions.Barrowman_GetCN(AOA, Aref, self.topArea,
                                           self.bottomArea)

        #### Pressure Drag ####
        Cd_base = AeroFunctions.getBaseDragCoefficient(Mach)
        Cd_pressure = Cd_base * self.CdAdjustmentFactor
        Cd_pressure *= self.frontalArea / self.rocket.Aref

        noEngine = (self.stage.engineShutOffTime == None)
        if noEngine or time > self.stage.engineShutOffTime:
            # Add base drag if engine is off
            Cd_pressure += Cd_base * self.bottomArea / Aref

        #### Skin Friction Drag ####
        if self.wettedArea == 0:
            skinFrictionDragCoefficient = 0
            rollDampingMoment = Vector(0, 0, 0)
        else:
            skinFrictionDragCoefficient, rollDampingMoment = AeroFunctions.getCylindricalSkinFrictionDragCoefficientAndRollDampingMoment(rocketState, environment, \
                 self.length, Mach, self.surfaceRoughness, self.wettedArea, Aref, self.rocket.finenessRatio, self.rocket.fullyTurbulentBL)

        #### Total Drag ####
        Cd = Cd_pressure + skinFrictionDragCoefficient

        #### Assemble & return final force object ####
        return AeroFunctions.forceFromCdCN(rocketState,
                                           environment,
                                           Cd,
                                           CN,
                                           self.CPLocation,
                                           Aref,
                                           moment=rollDampingMoment)
예제 #11
0
 def getAppliedForce(self, rocketState, time, environment, CG):
     '''
         Calculates force/moment applied by the recovery system using a simple drag coefficient + area model
     '''
     #### Calculate Aero Force ####
     if self.currentStage == 0:
         # No recovery system deployed yet
         return ForceMomentSystem(Vector(0,0,0))
     else:
         # 3DoF force-only aero
         airVel = AeroParameters.getAirVelRelativeToVehicle(rocketState, environment)
         dragForceMagnitude = self.chuteCds[self.currentStage] * self.chuteAreas[self.currentStage] * AeroParameters.getDynamicPressure(rocketState, environment)
         totalForce = airVel.normalize() * dragForceMagnitude
         return ForceMomentSystem(totalForce)
예제 #12
0
 def getDesiredMoments(self, rocketState, environment, targetOrientation,
                       time, dt):
     '''
         Inputs: 
             gainKeyList:        iterable of length nKeyColumns, containing the data required to interpolate the PIDxy and PIDz coefficients in the gain table
             rocketState:        must have .orientation attribute (Quaternion)
             targetOrientation:  (Quaternion)
             _:                  currently unused (time argument)
             dt:                 (numeric) time since last execution of the control system
     '''
     orientationError = self._getOrientationError(rocketState,
                                                  targetOrientation)
     gainKeyList = AeroParameters.getAeroPropertiesList(
         self.keyFunctionList, rocketState, environment)
     self.updateCoefficientsFromGainTable(gainKeyList)
     return self.getNewSetPoint(orientationError, dt)
예제 #13
0
    def getAppliedForce(self, state, time, environment, rocketCG):
        airspeed = max(
            AeroParameters.getLocalFrameAirVel(state, environment).length(),
            0.0000001)
        redimConst = self.Lref / (2 * airspeed)
        # Calculate moment coefficients from damping coefficients
        localFrameAngularVelocity = Vector(*state.angularVelocity)
        zMomentCoeff = self.zDampingCoeffs * localFrameAngularVelocity * redimConst
        yMomentCoeff = self.yDampingCoeffs * localFrameAngularVelocity * redimConst
        xMomentCoeff = self.xDampingCoeffs * localFrameAngularVelocity * redimConst
        momentCoeffs = [xMomentCoeff, yMomentCoeff, zMomentCoeff]

        return AeroFunctions.forceFromCoefficients(state, environment, 0, 0,
                                                   *momentCoeffs,
                                                   self.position, self.Aref,
                                                   self.Lref)
예제 #14
0
    def _getAeroCoefficients(self, state, environment):
        keys = AeroParameters.getAeroPropertiesList(self.parameterFunctions,
                                                    state, environment)

        if len(keys) > 1:
            # Multi-dimensional linear interpolation
            interpolatedCoefficients = self._interpAeroCoefficients(keys)[0]
        else:
            # 1D linear interpolation
            interpolatedCoefficients = linInterp(self.keys, self.values,
                                                 keys[0])

        aeroCoefficients = [0.0] * 5
        for i in range(len(interpolatedCoefficients)):
            indexInCoeffArray = self.aeroCoeffIndices[i]
            aeroCoefficients[indexInCoeffArray] = interpolatedCoefficients[i]

        return aeroCoefficients
예제 #15
0
파일: Fins.py 프로젝트: henrystoldt/MAPLEAF
        def supersonicNormalForce(Mach):  # Supersonic Busemann method
            gamma = AeroFunctions.getGamma()
            tempBeta = AeroParameters.getBeta(Mach)
            K1, K2, K3, Kstar = getBusemannCoefficients(Mach, tempBeta, gamma)

            # Mach Cone coords
            machAngle = asin(1 / Mach)
            machCone_negZPerRadius = 1 / tan(machAngle)
            machConeEdgeZPos = []
            outerRadius = self.spanSliceRadii[-1]
            for i in range(len(self.spanSliceRadii)):
                machConeAtCurrentRadius = (
                    outerRadius - self.spanSliceRadii[i]
                ) * machCone_negZPerRadius + self.tipPosition
                machConeEdgeZPos.append(machConeAtCurrentRadius)

            return getSupersonicFinNormalForce(
                airVelRelativeToFin, unitSpanTangentialAirVelocity, finNormal,
                machConeEdgeZPos, self.spanwiseDirection,
                self.CPSpanWisePosition.length(), K1, K2, K3, Kstar, self)
예제 #16
0
    def setTargetActuatorDeflections(self, desiredMoments, state, environment,
                                     time):
        '''
            Inputs:
                desiredMoments: (3-length iterable) contains desired moments about the x,y,and z axes
                time:           time at which the moments are requested (time of current control loop execution)
        '''
        # Construct key vector, starting with non-moment components:
        keyVector = AeroParameters.getAeroPropertiesList(
            self.keyFunctionList, state, environment)
        for desiredMoment in desiredMoments:
            keyVector.append(desiredMoment)

        newActuatorPositionTargets = self._getPositionTargets(*keyVector)

        for i in range(len(self.actuatorList)):
            self.actuatorList[i].setTargetDeflection(
                newActuatorPositionTargets[i], time)

        return list(newActuatorPositionTargets)
예제 #17
0
    def getFinSliceArgs(self, vel, orientation, angVel, fin1, finSlicePosition=0):
        rocketState = RigidBodyState(Vector(0,0,0), vel, orientation, angVel)
        CG = self.rocket2.getCG(0, rocketState)

        rocketVelocity = AeroParameters.getLocalFrameAirVel(rocketState, self.currentConditions)

        # Add fin velocities due to motion of the rocket
        velocityDueToRocketPitchYaw = rocketState.angularVelocity.crossProduct(fin1.position - CG)*(-1) #The negative puts it in the wind frame

        #We need to find the angle between the rocket velocity vector and the plane described by the fin deflection angle 
        #and the shaft normal
        finNormal = fin1.spanwiseDirection.crossProduct(Vector(0, 0, 1))
        finDeflectionAngle = fin1.finAngle # Adjusted by parent finset during each timestep
        if(finDeflectionAngle != 0):
            rotation = Quaternion(axisOfRotation = fin1.spanwiseDirection, angle=math.radians(finDeflectionAngle))
            finNormal = rotation.rotate(finNormal)

        finUnitSpanTangentialVelocity = rocketState.angularVelocity.crossProduct(fin1.spanwiseDirection)*(-1)
        finVelWithoutRoll = rocketVelocity + velocityDueToRocketPitchYaw

        return finSlicePosition, finVelWithoutRoll, finUnitSpanTangentialVelocity, finNormal, fin1.spanwiseDirection, fin1.stallAngle
예제 #18
0
def getCylindricalSkinFrictionDragCoefficientAndRollDampingMoment(
        state,
        environment,
        length,
        Mach,
        surfaceRoughness,
        wettedArea,
        Aref,
        finenessRatio,
        assumeFullyTurbulent=True):
    ''' Equations from Barrowman section 4.0, Niskanen section 3.4 '''
    # Get a compressiblity-corrected flat plate skin friction drag coefficient, normalized by wetted area
    skinFrictionDragCoefficient = getSkinFrictionCoefficient(
        state, environment, length, Mach, surfaceRoughness,
        assumeFullyTurbulent)
    # Account for the fact that the rocket is cylindrical, and not a flat plate (Barrowman Eqn 4-16)
    skinFrictionDragCoefficient *= (1 + (0.5 / finenessRatio))
    # Rebase coefficient to the reference area
    skinFrictionDragCoefficient *= (wettedArea / Aref)

    # Approximate avg radius as the radius of a cylinder which would have the same wetted area + length
    avgRadius = (wettedArea / length) / (2 * math.pi)
    rollingSurfaceVel = avgRadius * state.angularVelocity.Z
    # Assume velocities are perpendicular
    airVel = AeroParameters.getLocalFrameAirVel(state, environment).length()
    # Use total velocity to redimensionalize coefficient
    totalVelSquared = airVel**2 + rollingSurfaceVel**2

    if totalVelSquared == 0:
        return skinFrictionDragCoefficient, Vector(0, 0, 0)
    else:
        totalSurfaceForce = skinFrictionDragCoefficient * 0.5 * totalVelSquared * environment.Density * Aref
        # Calculate roll damping component of total friction force using similar triangles
        rollDampingForce = totalSurfaceForce * (rollingSurfaceVel /
                                                math.sqrt(totalVelSquared))
        # Calculate resulting moment
        rollDampingMoment = Vector(0, 0, -rollDampingForce * avgRadius)

    return skinFrictionDragCoefficient, rollDampingMoment
예제 #19
0
    def test_getRocketRollAngle(self):
        def getDummyState(vel, orientation):
            zeroVec = Vector(0,0,0)
            zeroAngVel = AngularVelocity(rotationVector=zeroVec)
            return RigidBodyState(zeroVec, vel, orientation, zeroAngVel)
        
        state1 = getDummyState(self.dummyVelocity1, self.dummyOrientation4)
        rollAngle1 = AeroParameters.getRollAngle(state1, self.currentConditions)

        state2 = getDummyState(self.dummyVelocity1, self.dummyOrientation1)
        rollAngle2 = AeroParameters.getRollAngle(state2, self.currentConditions)

        state3 = getDummyState(self.dummyVelocity1, self.dummyOrientation2)
        rollAngle3 = AeroParameters.getRollAngle(state3, self.currentConditions)

        state4 = getDummyState(self.dummyVelocity1, self.dummyOrientation5)
        rollAngle4 = AeroParameters.getRollAngle(state4, self.currentConditions)

        state5 = getDummyState(self.dummyVelocity2, self.dummyOrientation4)
        rollAngle5 = AeroParameters.getRollAngle(state5, self.currentConditions)

        state6 = getDummyState(self.dummyVelocity1, self.dummyOrientation3)
        rollAngle6 = AeroParameters.getRollAngle(state6, self.currentConditions)

        self.assertAlmostEqual(rollAngle1, 180)
        self.assertAlmostEqual(rollAngle2, 90)
        self.assertAlmostEqual(rollAngle3, 270)
        self.assertAlmostEqual(rollAngle4, 135)
        self.assertAlmostEqual(rollAngle5, 0)
        self.assertAlmostEqual(rollAngle6, 180)

        # Prep rocket by calling getLocalFrameAirVel first to cache AOA result, then get cached result from getTotalAOA
        def testLocalFrameAirVelRollAngle(vel, orientation, expectedResult):
            state = RigidBodyState(Vector(0,0,0), vel, orientation, AngularVelocity(rotationVector=Vector(0,0,0)))
            self.assertAlmostEqual(AeroParameters.getRollAngle(state, self.currentConditions), expectedResult)

        testLocalFrameAirVelRollAngle(self.dummyVelocity1, self.dummyOrientation4, 180)
        testLocalFrameAirVelRollAngle(self.dummyVelocity1, self.dummyOrientation1, 90)
        testLocalFrameAirVelRollAngle(self.dummyVelocity1, self.dummyOrientation2, 270)
        testLocalFrameAirVelRollAngle(self.dummyVelocity1, self.dummyOrientation5, 135)
        testLocalFrameAirVelRollAngle(self.dummyVelocity2, self.dummyOrientation4, 0)
        testLocalFrameAirVelRollAngle(self.dummyVelocity1, self.dummyOrientation3, 180)
예제 #20
0
 def testLocalFrameAirVelNormalForceDir(vel, orientation, expectedResult, precision=7):
     state = RigidBodyState(Vector(0,0,0), vel, orientation, AngularVelocity(0,0,0))
     assertVectorsAlmostEqual(self, AeroParameters.getNormalAeroForceDirection(state, self.currentConditions), expectedResult, precision)
예제 #21
0
def getSkinFrictionCoefficient(state,
                               environment,
                               length,
                               Mach,
                               surfaceRoughness,
                               assumeFullyTurbulent=True):
    ''' 
        Calculates the skin friction drag coefficient (normalized by wetted area, not cross-sectional area).  
        Uses the methods described in (Barrowman, 1967, pg. 43-48) and in (Niskanen, 2009, pg. 43-46) 

        Used for skin friction calculations for all external rocket components

        Inputs:
          * state: (RigidBodyState)
          * environment: (EnvironmentalConditions)
          * length: (float) - reference length for the current rocket component, used for Reynolds number calculation
          * Mach: (float) - current Mach number
          * surfaceRoughness (float) - micrometers

        Outputs:
          * Returns the skin friction coefficient normalized by **wetted area**, must be converted to be normalized by cross-sectional area before summing with other coefficients.
    '''
    Re = AeroParameters.getReynoldsNumber(state, environment, length)
    smoothSurface = (surfaceRoughness == 0)  # True or False

    if smoothSurface:
        # Very large (unreachable) number
        criticalRe = 1e100
    else:
        criticalRe = 51 * (surfaceRoughness / length)**(
            -1.039)  # Eqn 4-7 (Barrowman)

    # Get skin friction factor (Mach number independent)
    if Re > criticalRe:
        # Re is above Critical value
        # Friction determined by roughness
        skinFrictionCoefficient = 0.032 * (surfaceRoughness / length)**0.2
    elif assumeFullyTurbulent:
        if Re < 1e4:
            # Limit for low Reynolds numbers
            skinFrictionCoefficient = 0.0148  # Eqn 3.81 (Niskanen) - match up with turbulent flow correlation
        else:
            # Eqn from Niskanen
            skinFrictionCoefficient = 1 / ((1.5 * math.log(Re) - 5.6)**2)
    else:
        # Laminar / Transitional flow
        if Re < 1e4:
            # Limit for low Reynolds numbers
            skinFrictionCoefficient = 0.01328  # Match up with transitional flow eqn
        elif Re < 5e5:
            # Laminar flow
            skinFrictionCoefficient = 1.328 / math.sqrt(
                Re)  # Eqn 4-2 (Barrowman)
        else:
            # Transitional Flow
            skinFrictionCoefficient = 1 / (
                (1.5 * math.log(Re) - 5.6)**2
            ) - 1700 / Re  # Eqn 4-6 (Barrowman), subtraction term from from Prandtl

    # Calculate compressibility correction
    if Mach < 0.9:
        # Subsonic
        compressibilityCorrectionFactor = _subSonicSkinFrictionCompressiblityCorrectionFactor(
            Mach, smoothSurface)
    elif Mach < 1.1:
        # Transonic
        subsonicFactor = _subSonicSkinFrictionCompressiblityCorrectionFactor(
            Mach, smoothSurface)
        supersonicFactor = _supersonicSkinFrictionCompressiblityCorrectionFactor(
            Mach, smoothSurface)
        # Linearly interpolate in the Mach 0.9-1.1 region - same method as that used in OpenRocket v15.03
        compressibilityCorrectionFactor = subsonicFactor + (
            (supersonicFactor - subsonicFactor) * (Mach - 0.9) / 0.2)
    else:
        # Supersonic
        compressibilityCorrectionFactor = _supersonicSkinFrictionCompressiblityCorrectionFactor(
            Mach, smoothSurface)

    # Return corrected factor
    return skinFrictionCoefficient * compressibilityCorrectionFactor
예제 #22
0
파일: Fins.py 프로젝트: henrystoldt/MAPLEAF
    def _getPreComputedFinAeroData(self, rocketState, environment, CG):
        #General Info ---------------------------------------------------------------------------------------------------------------------
        Aref = self.rocket.Aref
        Mach = AeroParameters.getMachNumber(rocketState, environment)
        dynamicPressure = AeroParameters.getDynamicPressure(
            rocketState, environment)

        # Skin Friction Drag -------------------------------------------------------------------------------------------------------------

        skinFrictionCoefficient = AeroFunctions.getSkinFrictionCoefficient(
            rocketState, environment, self.MACLength, Mach,
            self.surfaceRoughness, self.rocket.fullyTurbulentBL)
        # Adjust to the rocket reference area (skinFrictionCoefficient is based on wetted area)
        skinFrictionDragCoefficient = skinFrictionCoefficient * (
            self.wettedArea / Aref)
        # Correct for additional surface area due to fin thickness - Niskanen Eqn 3.85
        skinFrictionDragCoefficient *= (1 +
                                        2 * self.thickness / self.MACLength)

        # Pressure Drag ------------------------------------------------------------------------------------------------------------------

        # Leading edge drag coefficient
        if self.leadingEdgeShape == "Round":
            leadingEdgeCd = AeroFunctions.getCylinderCrossFlowCd_ZeroBaseDrag(
                Mach)
            LEthickness = self.leadingEdgeRadius * 2
        elif self.leadingEdgeShape == "Blunt":
            leadingEdgeCd = AeroFunctions.getBluntBodyCd_ZeroBaseDrag(Mach)
            LEthickness = self.leadingEdgeThickness

        # Adjust for leading edge angle and convert reference area to rocket reference area - Barrowman Eqn 4-22
        leadingEdgeCdAdjustmentFactor = LEthickness * self.span * cos(
            self.sweepAngle)**2 / Aref
        leadingEdgeCd *= leadingEdgeCdAdjustmentFactor

        # Trailing edge drag coefficient - simpler method from Niskanen section 3.4.4. Corrected to use only the trailing edge area, not the full fin frontal area
        # more intricate method available in Barrowman
        baseDragCd = AeroFunctions.getBaseDragCoefficient(Mach)
        if self.trailingEdgeShape == "Tapered":
            TEthickness = 0  # Zero base drag
        elif self.trailingEdgeShape == "Round":
            TEthickness = self.trailingEdgeRadius  # 0.5 base drag
        elif self.trailingEdgeShape == "Blunt":
            TEthickness = self.trailingEdgeThickness  # Full base drag

        # Convert to standard rocket reference Area
        trailingEdgeCd = baseDragCd * self.span * TEthickness / Aref

        # Thickness / Wave Drag
        #TODO: This section doesn't seem to be working quite right
        if Mach <= 1:
            # Thickness drag, subsonic
            thicknessDrag = 4*skinFrictionCoefficient*((self.thickness/self.rootChord)*cos(self.midChordSweep) + \
                (30 * (self.thickness/self.rootChord)**4 * cos(self.midChordSweep)**2) /  \
                    (self.subsonicFinThicknessK - Mach**2 * cos(self.midChordSweep)**2)**(3/2))
        else:
            # Supersonic wave drag
            # Using simplistic method from Hoerner - assumes diamond profile (pg 17-12, Eqn 29)
            # TODO: Implement method from Barrowman's FIN program
            thicknessDrag = 2.3 * self.aspectRatio * (self.thickness /
                                                      self.MACLength)**2

        thicknessDrag *= self.planformArea / Aref

        pressureDragCoefficient = leadingEdgeCd + trailingEdgeCd + thicknessDrag

        # Total Drag --------------------------------------------------------------------------------------------------------------------

        totalDragCoefficient = pressureDragCoefficient + skinFrictionDragCoefficient

        localFrameRocketVelocity = AeroParameters.getLocalFrameAirVel(
            rocketState, environment)
        axialPositionRelCG = self.position - CG
        finVelocityDueToRocketPitchYaw = rocketState.angularVelocity.crossProduct(
            axialPositionRelCG)
        airVelRelativeToFin = localFrameRocketVelocity - finVelocityDueToRocketPitchYaw  # The negative puts it in the wind frame
        CPXPos = self._getCPXPos(Mach)

        #### Transfer info to fins ####
        return PreComputedFinAeroData(airVelRelativeToFin, CPXPos,
                                      totalDragCoefficient)
예제 #23
0
    def _getAppliedForce(self, time, state):
        ''' Get the total force currently being experienced by the rocket, used by self.rigidBody to calculate the rocket's acceleration '''
        ### Precomputations and Logging ###
        environment = self._getEnvironmentalConditions(time, state)               # Get and log current air/wind properties
        rocketInertia = self.getInertia(time, state)                                            # Get and log current rocket inertia

        if self.derivativeEvaluationLog is not None:
            self.derivativeEvaluationLog.newLogRow(time)
            self.derivativeEvaluationLog.logValue("Position(m)", state.position)
            self.derivativeEvaluationLog.logValue("Velocity(m/s)", state.velocity)

        ### Component Forces ###
        if not self.isUnderChute:            
            # Precompute and log
            Mach = AeroParameters.getMachNumber(state, environment)
            unitRe = AeroParameters.getReynoldsNumber(state, environment, 1.0)
            AOA = AeroParameters.getTotalAOA(state, environment)
            rollAngle = AeroParameters.getRollAngle(state, environment)

            if self.derivativeEvaluationLog is not None:
                self.derivativeEvaluationLog.logValue("Mach", Mach)
                self.derivativeEvaluationLog.logValue("UnitRe", unitRe)
                self.derivativeEvaluationLog.logValue("AOA(deg)", math.degrees(AOA))
                self.derivativeEvaluationLog.logValue("RollAngle(deg)", rollAngle)
                self.derivativeEvaluationLog.logValue("OrientationQuaternion", state.orientation)
                self.derivativeEvaluationLog.logValue("AngularVelocity(rad/s)", state.angularVelocity)

            # This function will be the inherited function CompositeObject.getAppliedForce
            componentForces = self.getAppliedForce(state, time, environment, rocketInertia.CG) 

        else:
            # When under chute, neglect forces from other components
            componentForces = self.recoverySystem.getAppliedForce(state, time, environment, Vector(0,0,-1))
            
            # Log the recovery system's applied force (Normally handled in CompositeObject.getAppliedForce)
            if hasattr(self.recoverySystem, "forcesLog"):
                self.recoverySystem.forcesLog.append(componentForces.force)
                self.recoverySystem.momentsLog.append(componentForces.moment)
    
        # Move Force-Moment system to rocket CG
        componentForces = componentForces.getAt(rocketInertia.CG)
           
        ### Gravity ###
        gravityForce = self.environment.getGravityForce(rocketInertia, state)        
        totalForce = componentForces + gravityForce

        ### Launch Rail ###
        totalForce = self.environment.applyLaunchTowerForce(state, time, totalForce)        

        if self.derivativeEvaluationLog is not None:
            self.derivativeEvaluationLog.logValue("Wind(m/s)", environment.Wind)
            self.derivativeEvaluationLog.logValue("AirDensity(kg/m^3)", environment.Density)
            self.derivativeEvaluationLog.logValue("CG(m)", rocketInertia.CG)
            self.derivativeEvaluationLog.logValue("MOI(kg*m^2)", rocketInertia.MOI)
            self.derivativeEvaluationLog.logValue("Mass(kg)", rocketInertia.mass)

            CPZ = AeroFunctions._getCPZ(componentForces)            
            self.derivativeEvaluationLog.logValue("CPZ(m)", CPZ)
            self.derivativeEvaluationLog.logValue("AeroF(N)", componentForces.force)
            self.derivativeEvaluationLog.logValue("AeroM(Nm)", componentForces.moment)
            self.derivativeEvaluationLog.logValue("GravityF(N)", gravityForce.force)
            self.derivativeEvaluationLog.logValue("TotalF(N)", totalForce.force)

        return totalForce
예제 #24
0
파일: Fins.py 프로젝트: henrystoldt/MAPLEAF
    def _barrowmanAeroFunc(self,
                           rocketState,
                           time,
                           environment,
                           precomputedData,
                           CG=Vector(0, 0, 0),
                           finDeflectionAngle=None):
        '''
            Precomputed Data is a named tuple (PreComputedFinAeroData) which contains data/results from the parts of the fin aerodynamics calculation
                that are common to all fins in a FinSet (drag calculations mostly). These calculations are performed at the FinSet level.
            Only the parts of the Fin Aero Computation that change from fin to fin (normal force mostly, since different fins can have different angles of attack) are computed here
        '''
        Mach = AeroParameters.getMachNumber(rocketState, environment)
        dynamicPressure = AeroParameters.getDynamicPressure(
            rocketState, environment)

        if finDeflectionAngle == None:
            finDeflectionAngle = self.finAngle  # Adjusted by parent finset during each timestep, when the FinSet is controlled

        # Unpack precomputedData
        airVelRelativeToFin, CPXPos, totalDragCoefficient = precomputedData

        #### Compute normal force-----------------------------------------------------------------------------------------------------------------

        # Find fin normal vector after fin deflection
        finDeflectionRotation = Quaternion(
            axisOfRotation=self.spanwiseDirection,
            angle=radians(finDeflectionAngle))
        finNormal = finDeflectionRotation.rotate(self.undeflectedFinNormal)
        # Get the tangential velocity component vector, per unit radial distance from the rocket centerline
        rollAngVel = AngularVelocity(0, 0, rocketState.angularVelocity.Z)
        unitSpanTangentialAirVelocity = rollAngVel.crossProduct(
            self.spanwiseDirection) * (-1)

        def subsonicNormalForce(Mach):  # Subsonic linear method
            tempBeta = AeroParameters.getBeta(Mach)
            CnAlpha = getFinCnAlpha_Subsonic_Barrowman(self.span,
                                                       self.planformArea,
                                                       tempBeta,
                                                       self.midChordSweep)
            return getSubsonicFinNormalForce(airVelRelativeToFin,
                                             unitSpanTangentialAirVelocity,
                                             finNormal, self.spanwiseDirection,
                                             self.CPSpanWisePosition.length(),
                                             CnAlpha, self)

        def supersonicNormalForce(Mach):  # Supersonic Busemann method
            gamma = AeroFunctions.getGamma()
            tempBeta = AeroParameters.getBeta(Mach)
            K1, K2, K3, Kstar = getBusemannCoefficients(Mach, tempBeta, gamma)

            # Mach Cone coords
            machAngle = asin(1 / Mach)
            machCone_negZPerRadius = 1 / tan(machAngle)
            machConeEdgeZPos = []
            outerRadius = self.spanSliceRadii[-1]
            for i in range(len(self.spanSliceRadii)):
                machConeAtCurrentRadius = (
                    outerRadius - self.spanSliceRadii[i]
                ) * machCone_negZPerRadius + self.tipPosition
                machConeEdgeZPos.append(machConeAtCurrentRadius)

            return getSupersonicFinNormalForce(
                airVelRelativeToFin, unitSpanTangentialAirVelocity, finNormal,
                machConeEdgeZPos, self.spanwiseDirection,
                self.CPSpanWisePosition.length(), K1, K2, K3, Kstar, self)

        if Mach <= 0.8:
            normalForceMagnitude, finMoment = subsonicNormalForce(Mach)

        elif Mach < 1.4:
            # Interpolate in transonic region
            # TODO: Do this with less function evaluations? Perhaps precompute AOA and Mach combinations and simply interpolate? Lazy precompute? Cython?
            x1, x2 = 0.8, 1.4  # Start, end pts of interpolated region
            dx = 0.001

            # Find normal force and derivative at start of interpolation interval
            f_x1, m_x1 = subsonicNormalForce(x1)
            f_x12, m_x12 = subsonicNormalForce(x1 + dx)

            # Find normal force and derivative at end of interpolation interval
            f_x2, m_x2 = supersonicNormalForce(x2)
            f_x22, m_x22 = supersonicNormalForce(x2 + dx)

            normalForceMagnitude = Interpolation.cubicInterp(
                Mach, x1, x2, f_x1, f_x2, f_x12, f_x22, dx)
            finMoment = Interpolation.cubicInterp(Mach, x1, x2, m_x1, m_x2,
                                                  m_x12, m_x22, dx)
        else:
            normalForceMagnitude, finMoment = supersonicNormalForce(Mach)

        # Complete redimensionalization of normal force coefficients by multiplying by dynamic pressure
        # Direct the normal force along the fin's normal direction
        normalForce = normalForceMagnitude * dynamicPressure * finNormal
        finMoment *= dynamicPressure

        #### Get axial force-----------------------------------------------------------------------------------------------------------------------

        avgAOA = getFinSliceAngleOfAttack(
            self.spanSliceRadii[round(len(self.spanSliceAreas) / 2)],
            airVelRelativeToFin, unitSpanTangentialAirVelocity, finNormal,
            self.spanwiseDirection,
            self.stallAngle)  # Approximate average fin AOA
        totalAxialForceCoefficient = AeroFunctions.getDragToAxialForceFactor(
            avgAOA) * totalDragCoefficient
        axialForceMagnitude = totalAxialForceCoefficient * self.rocket.Aref * dynamicPressure
        axialForceDirection = self.spanwiseDirection.crossProduct(finNormal)
        axialForce = axialForceDirection * axialForceMagnitude

        #### Get CP Location ----------------------------------------------------------------------------------------------------------------------

        CPChordWisePosition = self.position - Vector(
            0, 0, CPXPos
        )  # Ignoring the change in CP position due to fin deflection for now
        globalCP = self.CPSpanWisePosition + CPChordWisePosition

        #### Assemble total force moment system objects--------------------------------------------------------------------------------------------

        totalForce = normalForce + axialForce
        return ForceMomentSystem(totalForce,
                                 globalCP,
                                 moment=Vector(0, 0, finMoment)), globalCP
예제 #25
0
 def testLocalFrameAirVelAOA(vel, orientation, expectedResult):
     state = RigidBodyState(Vector(0,0,0), vel, orientation, AngularVelocity(rotationVector=Vector(0,0,0)))
     self.assertAlmostEqual(AeroParameters.getTotalAOA(state, self.currentConditions), math.radians(expectedResult))
예제 #26
0
 def test_getAOSS(self):
     test1State = RigidBodyState(velocity=Vector(0, 1, 1))
     expectedAOA = math.radians(45)
     computedAOA = AeroParameters.getAOSS(test1State, self.currentConditions)
     self.assertAlmostEqual(expectedAOA, computedAOA)
예제 #27
0
 def test_getBeta(self):
     self.assertAlmostEqual(AeroParameters.getBeta(0.5), 0.86602540)
     self.assertAlmostEqual(AeroParameters.getBeta(1.5), 1.118033989)
예제 #28
0
 def test_getReynoldsNumber(self):
     environment = self.environment.getAirProperties(Vector(0,0,0))
     self.assertAlmostEqual(AeroParameters.getReynoldsNumber(self.rocketState1, environment, 1), 13701279.02972, 2)
예제 #29
0
 def test_getMachNumber(self):
     environment = self.environment.getAirProperties(Vector(0,0,0))
     # Assumes gamma=1.4, R=287
     self.assertAlmostEqual(AeroParameters.getMachNumber(self.rocketState1, environment), 0.587781235)
예제 #30
0
 def test_dynamicPressure(self):
     self.assertAlmostEqual(AeroParameters.getDynamicPressure(self.rocketState1,self.currentConditions), self.correctDynamicPressure1)
     self.assertAlmostEqual(AeroParameters.getDynamicPressure(self.rocketState3,self.currentConditions), self.correctDynamicPressure2)