Untitled

 avatar
unknown
csharp
5 months ago
16 kB
6
Indexable
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Unity.Mathematics;
using Unity.VisualScripting;
using Unity.VisualScripting.FullSerializer;
using UnityEngine;

public class Wing : MonoBehaviour
{
    public Rigidbody rb;

    public AeroSurfaceConfig config;

    public Transform wingAerodynamicCenter;

    public float controllAngle;

    private List<gizmo> g = new List<gizmo>();  //only for debugging

    private float defaultBend;

    private void Start()
    {
        defaultBend = transform.localEulerAngles.z;

        if(wingAerodynamicCenter == null)
        {
            wingAerodynamicCenter = transform;
        }
    }

    public Total GetSegmentForce(Vector3 com, Vector3 wind, float p)
    {
        Total FM = new Total();
        if (!gameObject.activeInHierarchy || config == null) return FM;

        Vector3 r = wingAerodynamicCenter.position - com;

        //---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ????
        //Calculate Relative Velocity to the segment
        Vector3 Vs = Vector3.zero; //slipstream velocity
        Vector3 Vind = Vector3.zero; //induced velocity by other segments 
        Vector3 Vqs = Vector3.zero; //Velocity pertrubation ? (Sec. III D.)

        Vector3 V = -rb.velocity - Vector3.Cross(rb.angularVelocity, r) + Vs + Vind + Vqs + wind;
        Vector3 localV = transform.InverseTransformDirection(V); //should be local now
        localV = new Vector3(0, localV.y, localV.z);

        //p = Weather.instance.GetAirDensity(wingAerodynamicCenter.position); //Air density, Moved to Plane.cs so instance only gets claled once
        float a = Mathf.Atan(localV.y/localV.z); //Alpha(Angle of Attack)


        //Calculate effect of wing aspect ratio
        float AR = config.aspectRatio;
        float Cla = config.liftSlope;
        float CLa = Cla * (AR / (AR + 2 * (AR + 4) / (AR + 2))); //corrected lift slope with effect of AR


        //Calculate impact of controll surfaces
        float n = Mathf.Lerp(0.8f, 0.4f, (Mathf.Abs(controllAngle) - 10) / 50); //Magic formula by Ivan Pensionerov (Estimated based on graph in the expensive book "emperial factor to account for the effects if viscosity")
        float flapChord = config.meanChord * config.flapFraction;
        float airfoildChord = config.meanChord * (1 - config.flapFraction);
        float theta = Mathf.Acos(2*flapChord/airfoildChord-1);
        float liftEffectivness = 1 - (theta - Mathf.Sin(theta)) / Mathf.PI;
        float DeltaCL = CLa * liftEffectivness * n * controllAngle * Mathf.Deg2Rad;
        float DeltaCLmax = DeltaCL * Mathf.Clamp01(1 - 0.5f * ((flapChord / airfoildChord) - 0.1f) / 0.3f); //Magic formula by Ivan Pensionerov (Estimated based on graph in the expensive book)

        float correctedZeroLiftAoA = config.zeroLiftAoA * Mathf.Deg2Rad - DeltaCL / CLa; // zero lift angle of attack after controll surface effects

        float CLmaxP = CLa * (config.stallAngleHigh * Mathf.Deg2Rad - config.zeroLiftAoA * Mathf.Deg2Rad) + DeltaCLmax;
        float CLmaxN = CLa * (config.stallAngleLow * Mathf.Deg2Rad - config.zeroLiftAoA * Mathf.Deg2Rad) + DeltaCLmax;

        float aStallP = correctedZeroLiftAoA + CLmaxP / CLa; //positive stall angle after controll surface effect
        float aStallN = correctedZeroLiftAoA + CLmaxN / CLa; //negative stall angle after controll surface effect


        float Cl = 0; //Lift Coefficient
        float Cd = 0; //Drag Coefficient
        float Cm = 0; //Moment(torque) Coefficient

        // Low angles of attack mode and stall mode curves are stitched together by a line segment. 
        float paddingAngle = Mathf.Deg2Rad * Mathf.Lerp(8, 14, Mathf.Abs(Mathf.Abs(controllAngle) - 50) / 50);
        float paddedStallAngleHigh = aStallP + paddingAngle;
        float paddedStallAngleLow = aStallN - paddingAngle;

        //calculate CL, Cd, Cm based on low/high angle of attack mode
        if (aStallN < a && a < aStallP) //low angle of attack
        {
            Vector3 coefficients = CalculateCoefficientsLowAoA(CLa, a, correctedZeroLiftAoA, AR);
            Cl = coefficients.x;
            Cd = coefficients.y;
            Cm = coefficients.z;
        }
        else
        {
            if (a > paddedStallAngleHigh || a < paddedStallAngleLow)
            {
                // Stall mode.
                Vector3 coefficients = CalculateCoefficientsStall(CLa, a, correctedZeroLiftAoA, AR, aStallP, aStallN);
                Cl = coefficients.x;
                Cd = coefficients.y;
                Cm = coefficients.z;

                g.Add(new gizmo() { 
                    c = Color.red,
                    p = wingAerodynamicCenter.position,
                    r = 0.2f
                });
            }
            else
            {
                g.Add(new gizmo()
                {
                    c = Color.yellow,
                    p = wingAerodynamicCenter.position,
                    r = 0.2f
                });

                // Linear stitching in-between stall and low angles of attack modes.
                Vector3 aerodynamicCoefficientsLow;
                Vector3 aerodynamicCoefficientsStall;
                float lerpParam;

                if (a >= aStallP)
                {
                    aerodynamicCoefficientsLow = CalculateCoefficientsLowAoA(CLa, aStallP, correctedZeroLiftAoA, AR);
                    aerodynamicCoefficientsStall = CalculateCoefficientsStall(CLa, paddedStallAngleHigh, correctedZeroLiftAoA, AR, aStallP, aStallN);
                    lerpParam = (a - aStallP) / (paddedStallAngleHigh - aStallP);
                }
                else
                {
                    aerodynamicCoefficientsLow = CalculateCoefficientsLowAoA(CLa, aStallN, correctedZeroLiftAoA, AR);
                    aerodynamicCoefficientsStall = CalculateCoefficientsStall(CLa, paddedStallAngleLow, correctedZeroLiftAoA, AR, aStallP, aStallN);
                    lerpParam = (a - aStallN) / (paddedStallAngleLow - aStallN);
                }
                Vector3 coefficients = Vector3.Lerp(aerodynamicCoefficientsLow, aerodynamicCoefficientsStall, lerpParam);
                Cl = coefficients.x;
                Cd = coefficients.y;
                Cm = coefficients.z;
            }
        }

        //float CFx = Cl * Mathf.Sin(a) - Cd * Mathf.Sin(a); //Force Coefficient x
        //float CFy = -Cl * Mathf.Sin(a) - Cd * Mathf.Sin(a); //Force Coefficient y

        //Vector3 CF = new Vector3(CFx,0,CFy); //Force Coefficient
        //Vector3 CM = new Vector3(0,Cm,0); //Moment(torque) Coefficient

        //FM.force = 0.5f * p * config.span * config.meanChord * Vector3.Scale(Vector3.Scale(V, V), CF);
        //FM.moment = 0.5f * p * config.span * config.meanChord* config.meanChord * Vector3.Scale(Vector3.Scale(V, V), CM);

        Vector3 dragDir = transform.TransformDirection(localV.normalized);
        Vector3 liftDir = Vector3.Cross(dragDir, transform.right);

        float dynamicPressure = 0.5f * p * localV.sqrMagnitude;
        float area = config.span * config.meanChord;

        Vector3 lift = liftDir * Cl * dynamicPressure * area;
        Vector3 drag = dragDir * Cd * dynamicPressure * area;
        Vector3 torque = -transform.forward * Cm * dynamicPressure * area * config.meanChord;

        Debug.DrawLine(wingAerodynamicCenter.position, wingAerodynamicCenter.position + lift /3000, Color.green);
        Debug.DrawLine(wingAerodynamicCenter.position, wingAerodynamicCenter.position + drag /3000, Color.red);

        FM.force = lift + drag;
        FM.moment = Vector3.Cross(r, FM.force) + torque;

        //bend wing
        if (Application.isPlaying)
        {
            float f = transform.InverseTransformDirection((lift + drag + torque) / 3000).y;
            float BendFac = Mathf.Clamp(f / 20, -1, 1);
            Vector3 currentEulerAngles = transform.localEulerAngles;
            transform.localEulerAngles = new Vector3(currentEulerAngles.x, currentEulerAngles.y, 3);
        }

        return FM;
    }

    public Vector3 GetSegmentForceCoeficients(Vector3 com, float a)
    {
        controllAngle = 3;

        //Calculate effect of wing aspect ratio
        float AR = config.aspectRatio;
        float Cla = config.liftSlope;
        float CLa = Cla * (AR / (AR + 2 * (AR + 4) / (AR + 2))); //corrected lift slope with effect of AR


        //Calculate impact of controll surfaces
        float n = Mathf.Lerp(0.8f, 0.4f, (Mathf.Abs(controllAngle) - 10) / 50); //Magic formula by Ivan Pensionerov (Estimated based on graph in the expensive book "emperial factor to account for the effects if viscosity")

        Debug.Log("nf: " + n);//correct
        
        float flapChord = config.meanChord * config.flapFraction;
        float airfoildChord = config.meanChord * (1 - config.flapFraction);
        float theta = Mathf.Acos(2 * flapChord / airfoildChord - 1);

        Debug.Log("Theta : " + theta);//correct

        float liftEffectivness = 1 - (theta - Mathf.Sin(theta)) / Mathf.PI;

        Debug.Log("Lift Effectivness: " + liftEffectivness);//correct

        float DeltaCL = CLa * liftEffectivness * n * controllAngle * Mathf.Deg2Rad;

        Debug.Log("Delta CL: " + DeltaCL);//correct

        float DeltaCLmax = DeltaCL * Mathf.Clamp01(1 - 0.5f * ((flapChord/airfoildChord) - 0.1f) / 0.3f); //Magic formula by Ivan Pensionerov (Estimated based on graph in the expensive book)

        Debug.Log("Delta CL max: " + DeltaCLmax); //correct

        float correctedZeroLiftAoA = config.zeroLiftAoA * Mathf.Deg2Rad - DeltaCL / CLa; // zero lift angle of attack after controll surface effects

        Debug.Log("Corrected zero lift aoa: " + correctedZeroLiftAoA); //correct

        float CLmaxP = CLa * (config.stallAngleHigh * Mathf.Deg2Rad - config.zeroLiftAoA * Mathf.Deg2Rad) + DeltaCLmax;

        Debug.Log("Delta CL max P: " + CLmaxP); //correct

        float CLmaxN = CLa * (config.stallAngleLow * Mathf.Deg2Rad - config.zeroLiftAoA * Mathf.Deg2Rad) + DeltaCLmax;

        Debug.Log("Delta CL max N: " + CLmaxN); //correct

        float aStallP = correctedZeroLiftAoA + CLmaxP / CLa; //positive stall angle after controll surface effect
        float aStallN = correctedZeroLiftAoA + CLmaxN / CLa; //negative stall angle after controll surface effect


        float Cl = 0; //Lift Coefficient
        float Cd = 0; //Drag Coefficient
        float Cm = 0; //Moment(torque) Coefficient

        // Low angles of attack mode and stall mode curves are stitched together by a line segment. 
        float paddingAngle = Mathf.Deg2Rad * Mathf.Lerp(8, 14, Mathf.Abs(Mathf.Abs(controllAngle) - 50) / 50);
        float paddedStallAngleHigh = aStallP + paddingAngle;
        float paddedStallAngleLow = aStallN - paddingAngle;

        Debug.Log("Stall Angle High: " + aStallP); //correct
        Debug.Log("Stall Angle Low: " + aStallN); //correct

        Debug.Log("padding Angle: " + paddingAngle * Mathf.Rad2Deg); //correct

        Debug.Log("Padded Stall Angle High: " + paddedStallAngleHigh); //correct
        Debug.Log("Padded Stall Angle Low: " + paddedStallAngleLow); //correct

        //calculate CL, Cd, Cm based on low/high angle of attack mode
        if (aStallN < a && a < aStallP) //low angle of attack
        {
            Vector3 coefficients = CalculateCoefficientsLowAoA(CLa, a, correctedZeroLiftAoA, AR);
            Cl = coefficients.x;
            Cd = coefficients.y;
            Cm = coefficients.z;
        }
        else
        {
            if (a > paddedStallAngleHigh || a < paddedStallAngleLow)
            {
                // Stall mode.
                Vector3 coefficients = CalculateCoefficientsStall(CLa, a, correctedZeroLiftAoA, AR, aStallP, aStallN);
                Cl = coefficients.x;
                Cd = coefficients.y;
                Cm = coefficients.z;
            }
            else
            {
                // Linear stitching in-between stall and low angles of attack modes.
                Vector3 aerodynamicCoefficientsLow;
                Vector3 aerodynamicCoefficientsStall;
                float lerpParam;

                if (a >= aStallP)
                {
                    aerodynamicCoefficientsLow = CalculateCoefficientsLowAoA(CLa, aStallP, correctedZeroLiftAoA, AR);
                    aerodynamicCoefficientsStall = CalculateCoefficientsStall(CLa, paddedStallAngleHigh, correctedZeroLiftAoA, AR, aStallP, aStallN);
                    lerpParam = (a - aStallP) / (paddedStallAngleHigh - aStallP);
                }
                else
                {
                    aerodynamicCoefficientsLow = CalculateCoefficientsLowAoA(CLa, aStallN, correctedZeroLiftAoA, AR);
                    aerodynamicCoefficientsStall = CalculateCoefficientsStall(CLa, paddedStallAngleLow, correctedZeroLiftAoA, AR, aStallP, aStallN);
                    lerpParam = (a - aStallN) / (paddedStallAngleLow - aStallN);
                }
                Vector3 coefficients = Vector3.Lerp(aerodynamicCoefficientsLow, aerodynamicCoefficientsStall, lerpParam);
                Cl = coefficients.x;
                Cd = coefficients.y;
                Cm = coefficients.z;
            }
        }

        return new Vector3(Cl, Cd, Cm);
    }

    private Vector3 CalculateCoefficientsLowAoA(float CLa, float a, float correctedZeroLiftAoA, float AR)
    {
        Vector3 c;

        c.x = CLa * (a - correctedZeroLiftAoA); //Lift coefficient

        float ai = c.x / (Mathf.PI * AR); //induced angle of attack
        float aeff = a - correctedZeroLiftAoA - ai; //efficint angle of attack

        float CT = config.skinFriction * Mathf.Cos(aeff); //Normal force coefficient
        float CN = (c.x + CT * Mathf.Sin(aeff)) / Mathf.Cos(aeff);//Tangent force coefficient

        c.y = CN * Mathf.Sin(aeff) + CT * Mathf.Cos(aeff); //Drag coefficient
        c.z = -CN * (0.25f - 0.175f * (1 - 2 * Mathf.Abs(aeff) / Mathf.PI)); //Moment coefficient

        return c;
    }

    private Vector3 CalculateCoefficientsStall(float CLa, float a, float correctedZeroLiftAoA, float AR, float aStallP, float aStallN)
    {
        Vector3 c;

        float CLlowAoA;
        float fac;
        if (a >= aStallP)
        {
            CLlowAoA = CLa * (aStallP - correctedZeroLiftAoA);
            fac = 1 - (a - aStallP) * 1 / (90 - aStallP);
        }
        else
        {
            CLlowAoA = CLa * (aStallN - correctedZeroLiftAoA);
            fac = 1 - (a - aStallN) * 1 / (-90 - aStallN);
        }

        float ai = (CLlowAoA / (Mathf.PI * AR)) * fac; //induced angle of attack, tappered to 0 at a+-90
        float aeff = a - correctedZeroLiftAoA - ai; //efficint angle of attack

        float controllAngleRad = controllAngle * Mathf.Deg2Rad; //be carefull with Radians and Degree
        float Cd90 = -0.0426f * controllAngleRad * controllAngleRad + 0.21f * controllAngleRad + 1.98f; //drag at 90° based on flap angle(convex or concave wing structure)
        float CN = Cd90 * Mathf.Sin(aeff) * (1 / (0.56f + 0.44f * Mathf.Abs(Mathf.Sin(aeff))) - 0.41f * (1 - Mathf.Exp(-17 / AR)));
        float CT = 0.5f * config.skinFriction * Mathf.Cos(aeff);

        c.x = CN * Mathf.Cos(aeff) - CT * Mathf.Sin(aeff);
        c.y = CN * Mathf.Sin(aeff) + CT * Mathf.Cos(aeff);
        c.z = -CN * (0.25f - 0.175f * (1 - 2 * Mathf.Abs(aeff) / Mathf.PI));

        return c;
    }

    private void OnDrawGizmos()
    {
        foreach(gizmo g in g)
        {
            Gizmos.color = g.c;
            Gizmos.DrawSphere(g.p, g.r);
        }

        g.Clear();
    }
}


[Serializable]
public class gizmo
{
    public Color c;
    public Vector3 p;
    public float r;
}
Editor is loading...
Leave a Comment