/** Calculates and returns the thrust produced by this propeller. * Given the excess power available from the engine (in watts), the thrust is * calculated, as well as the current RPM. The RPM is calculated by integrating * the torque provided by the engine over what the propeller "absorbs" * (essentially the "drag" of the propeller). * @param PowerAvailable this is the excess power provided by the engine to * accelerate the prop. It could be negative, dictating that the propeller * would be slowed. * @return the thrust in newtons */ public double Calculate(double EnginePower, double rho, double Vel, double speedOfSound, double deltaTime) { deltaT = deltaTime; double omega, PowerAvailable; double RPS = RPM * (1d / 60d); double machInv = 1d / speedOfSound; // Calculate helical tip Mach double Area = 0.25d * Diameter * Diameter * Math.PI; double Vtip = RPS * Diameter * Math.PI; HelicalTipMach = Math.Sqrt(Vtip * Vtip + Vel * Vel) * machInv; PowerAvailable = EnginePower - GetPowerRequired(rho, Vel); if (RPS > 0d) { J = Vel / (Diameter * RPS); // Calculate J normally } else { J = Vel / Diameter; } if (MaxPitch == MinPitch) // Fixed pitch prop { ThrustCoeff = cThrustFP.Evaluate((float)J); } else // Variable pitch prop { ThrustCoeff = cThrust.GetValue(J, Pitch); } // Apply optional scaling factor to Ct (default value = 1) ThrustCoeff *= CtFactor * CtTweak; // Apply optional Mach effects from CT_MACH table double CtMachFactor = 1; if (CtMach != null) { CtMachFactor = Math.Pow(CtMach.Evaluate((float)HelicalTipMach), MachPowTweak); ThrustCoeff *= CtMachFactor; } Thrust = ThrustCoeff * RPS * RPS * D4 * rho; //Debug.Log("CT = " + ThrustCoeff + " CtFactor = " + CtFactor + "\n\rCtTweak = " + CtTweak + " CtMachFactor = " + CtMachFactor + "\n\rJ = " + J + "\n\rHelicalTipMach = " + HelicalTipMach + " SoundSpeed = " + speedOfSound); /*// Induced velocity in the propeller disk area. This formula is obtained * // from momentum theory - see B. W. McCormick, "Aerodynamics, Aeronautics, * // and Flight Mechanics" 1st edition, eqn. 6.15 (propeller analysis chapter). * // Since Thrust and Vel can both be negative we need to adjust this formula * // To handle sign (direction) separately from magnitude. * double Vel2sum = Vel*Math.Abs(Vel) + 2.0*Thrust/(rho*Area); * * if( Vel2sum > 0.0) * Vinduced = 0.5 * (-Vel + Math.Sqrt(Vel2sum)); * else * Vinduced = 0.5 * (-Vel - Math.Sqrt(-Vel2sum)); * * // We need to drop the case where the downstream velocity is opposite in * // direction to the aircraft velocity. For example, in such a case, the * // direction of the airflow on the tail would be opposite to the airflow on * // the wing tips. When such complicated airflows occur, the momentum theory * // breaks down and the formulas above are no longer applicable * // (see H. Glauert, "The Elements of Airfoil and Airscrew Theory", * // 2nd edition, ?6.3, pp. 219-221) * * if ((Vel+2.0*Vinduced)*Vel < 0.0) * // The momentum theory is no longer applicable so let's assume the induced * // saturates to -0.5*Vel so that the total velocity Vel+2*Vinduced equals 0. * Vinduced = -0.5*Vel; * * // P-factor is simulated by a shift of the acting location of the thrust. * // The shift is a multiple of the angle between the propeller shaft axis * // and the relative wind that goes through the propeller disk. * if (P_Factor > 0.0001) { * double tangentialVel = localAeroVel.Magnitude(eV, eW); * * if (tangentialVel > 0.0001) { * double angle = atan2(tangentialVel, localAeroVel(eU)); * double factor = Sense * P_Factor * angle / tangentialVel; * SetActingLocationY( GetLocationY() + factor * localAeroVel(eW)); * SetActingLocationZ( GetLocationZ() + factor * localAeroVel(eV)); * } * }*/ omega = RPS * 2d * Math.PI; // The Ixx value and rotation speed given below are for rotation about the // natural axis of the engine. The transform takes place in the base class // FGForce::GetBodyForces() function. /*vH(eX) = Ixx*omega*Sense; * vH(eY) = 0.0; * vH(eZ) = 0.0;*/ if (omega > 0d) { ExcessTorque = PowerAvailable / omega; } else { ExcessTorque = PowerAvailable; } RPM = (RPS + (ExcessTorque / (Ixx * 2.0 * Math.PI) * deltaT)) * 60d; if (RPM < 0d) { RPM = 0d; // Engine won't turn backwards } // Transform Torque and momentum first, as PQR is used in this // equation and cannot be transformed itself. //vMn = in.PQR*(Transform()*vH) + Transform()*vTorque; // hacky mach drag -- should not be needed with nuFAR /*if (MachDrag != null) * { * double machDrag = MachDrag.Evaluate((float)(Vel * machInv)); * machDrag *= machDrag * D4 * RPS * RPS * rho * 0.00004d; * Thrust -= machDrag; * }*/ //MonoBehaviour.print("Prop running: thrust " + Thrust + ", Ct " + ThrustCoeff + ", RPM " + RPM + ", PAvail " + PowerAvailable + ", J " + J + ", delta RPM " + (RPM - RPS * 60d)); return(Thrust); }
/*public Vector3 GetPFactor() * { * double px = 0.0, py, pz; * * py = Thrust * Sense * (ActingLocation.y - GetLocationY()) / 12.0; * pz = Thrust * Sense * (ActingLocation.z - GetLocationZ()) / 12.0; * * return Vector3(px, py, pz); * }*/ /** Retrieves the power required (or "absorbed") by the propeller - * i.e. the power required to keep spinning the propeller at the current * velocity, air density, and rotational rate. */ public double GetPowerRequired(double rho, double Vel) { double cPReq, J; double RPS = RPM * (1d / 60d); if (RPS != 0d) { J = Vel / (Diameter * RPS); } else { J = Vel / Diameter; } if (MaxPitch == MinPitch) // Fixed pitch prop { cPReq = cPowerFP.Evaluate((float)J); } else { // Variable pitch prop if (ConstantSpeed != 0) // Constant Speed Mode { // do normal calculation when propeller is neither feathered nor reversed // Note: This method of feathering and reversing was added to support the // turboprop model. It's left here for backward compatablity, but // now feathering and reversing should be done in Manual Pitch Mode. if (!Feathered) { if (!Reversed) { double rpmReq = MinRPM + (MaxRPM - MinRPM) * Advance; double dRPM = rpmReq - RPM; // The pitch of a variable propeller cannot be changed when the RPMs are // too low - the oil pump does not work. if (RPM > 200d) { Pitch -= dRPM * deltaT; } if (Pitch < MinPitch) { Pitch = MinPitch; } else if (Pitch > MaxPitch) { Pitch = MaxPitch; } } else // Reversed propeller { // when reversed calculate propeller pitch depending on throttle lever position // (beta range for taxing full reverse for braking) double PitchReq = MinPitch - (MinPitch - ReversePitch) * Reverse_coef; // The pitch of a variable propeller cannot be changed when the RPMs are // too low - the oil pump does not work. if (RPM > 200d) { Pitch += (PitchReq - Pitch) * (1d / 200d); } if (RPM > MaxRPM) { Pitch += (MaxRPM - RPM) / 50; if (Pitch < ReversePitch) { Pitch = ReversePitch; } else if (Pitch > MaxPitch) { Pitch = MaxPitch; } } } } else // Feathered propeller { // ToDo: Make feathered and reverse settings done via FGKinemat Pitch += (MaxPitch - Pitch) * (1d / 300d); // just a guess (about 5 sec to fully feathered) if (Pitch > MaxPitch) { Pitch = MaxPitch; } } } else // Manual Pitch Mode, pitch is controlled externally { } cPReq = cPower.GetValue(J, Pitch); } // Apply optional scaling factor to Cp (default value = 1) cPReq *= CpFactor * CpTweak; // Apply optional Mach effects from CP_MACH table if (CpMach != null) { cPReq *= Math.Pow(CpMach.Evaluate((float)HelicalTipMach), MachPowTweak); } double local_RPS = RPS < 0.01d ? 0.01d : RPS; PowerRequired = cPReq * local_RPS * local_RPS * local_RPS * D5 * rho; vTorque.x = (-Sense * PowerRequired / (local_RPS * 2.0 * Math.PI)); //Debug.Log("Cp = " + cPReq); return(PowerRequired); }