def test_backward_upsidedown(self): cp = CP(Vector2D(1, 0), 12, Angle(10)) calculated = cp.calculate(Angle(-175)) self.assertEqual(-8, calculated.x)
def test_0_length(self): cp = CP(Vector2D(1, 0), 0, Angle(10)) calculated = cp.calculate(Angle(1)) self.assertEqual(1, calculated.x)
def test_down_vertical(self): cp = CP(Vector2D(1, 0), 12, Angle(10)) calculated = cp.calculate(Angle(-90)) self.assertEqual(-5, calculated.x)
def test_backward_upsidedown_stalled(self): cp = CP(Vector2D(1, 0), 12, Angle(10)) calculated = cp.calculate(Angle(-130)) self.assertEqual(-6.5, calculated.x)
def test_down_stalled(self): cp = CP(Vector2D(1, 0), 12, Angle(10)) calculated = cp.calculate(Angle(-50)) self.assertEqual(-3.5, calculated.x)
def test_just_past_vertical(self): cp = CP(Vector2D(1, 0), 12, Angle(10)) calculated = cp.calculate(Angle(91)) self.assertEqual(-5.0375, calculated.x)
def test_normal(self): cp = CP(Vector2D(1, 0), 12, Angle(10)) calculated = cp.calculate(Angle(1)) self.assertEqual(-2, calculated.x)
def test_null_angle(self): cp = CP(Vector2D(1, 0), 12, None) calculated = cp.calculate(Angle(1)) self.assertEqual(1, calculated.x)
class Surface: ''' A plane has multiple services that generate lift and drag forces''' def __init__(self, name, relative_pos=Vector2D(0, 0), chord_length=1, angle=Angle(0), area=1, lift_curve=None, drag_curve=None, atmosphere=Atmosphere()): self._point = Point(relative_pos) self.name = name self.angle = angle self.area = area self._atmosphere = atmosphere self.lift_curve = lift_curve self.drag_curve = drag_curve if lift_curve is not None: stall_angle = lift_curve.stall_angle() else: stall_angle = None self.cp = CP(relative_pos, chord_length, stall_angle) self.velocity = Vector2D(0, 0) self.current_cp = Vector2D(0, 0) def aoa(self, velocity): vel_angle = velocity.angle() return self.angle.minus(vel_angle) def calculate_forces(self, translation_velocity, angular_velocity, altitude): surface_velocity = self._point.total_velocity( translation_velocity, angular_velocity) self.velocity = surface_velocity air_density = self._atmosphere.get_air_density(altitude) velocity_magnitude = surface_velocity.magnitude() aoa = self.aoa(surface_velocity) forces = [] current_cp = self.cp.calculate(aoa) self.current_cp = current_cp CL = 0 if self.lift_curve is not None: CL = self.lift_curve.calculate_lift_coefficient(aoa) lift_mag = self.calculate_lift(CL, velocity_magnitude, air_density) lift_dir = Surface.get_lift_unit(aoa, surface_velocity) lift_force = lift_dir.scale(lift_mag) forces.append(Force("lift", Force.LIFT, current_cp, lift_force)) CD = 0 if self.drag_curve is not None: CD = self.drag_curve.calculate_drag_coefficient(aoa, CL) drag_mag = self.calculate_drag( CD, velocity_magnitude, air_density) drag_dir = surface_velocity.reverse().unit() drag_vector = drag_dir.scale(drag_mag) drag_force = Force("drag", Force.DRAG, current_cp, drag_vector) forces.append(drag_force) return forces def get_lift_unit(aoa, surface_velocity): if abs(aoa.relative_degrees()) <= 90: return surface_velocity.rotate(Angle(90)).unit() else: # trailing edge is leading so velocity vector is reversed return surface_velocity.rotate(Angle(-90)).unit() def calculate_lift(self, CL, velocity_magnitude, air_density): return (air_density * velocity_magnitude**2 * self.area * CL) \ / 2 def calculate_drag(self, CD, velocity_magnitude, air_density): return CD * self.area * (air_density * velocity_magnitude**2) \ / 2