72constexpr double sqr(
double x) {
return x*x; }
78 MagnitudedAccelDt = MagnitudeAccel = Magnitude = TurbDirection = 0.0;
83 spike = target_time = strength = 0.0;
84 wind_from_clockwise = 0.0;
87 vGustNED.InitMatrix();
88 vTurbulenceNED.InitMatrix();
89 vCosineGust.InitMatrix();
92 windspeed_at_20ft = 0.;
93 probability_of_exceedence_index = 0;
98 << 500.0 << 1750.0 << 3750.0 << 7500.0 << 15000.0 << 25000.0 << 35000.0 << 45000.0 << 55000.0 << 65000.0 << 75000.0 << 80000.0
99 << 1 << 3.2 << 2.2 << 1.5 << 0.0 << 0.0 << 0.0 << 0.0 << 0.0 << 0.0 << 0.0 << 0.0 << 0.0
100 << 2 << 4.2 << 3.6 << 3.3 << 1.6 << 0.0 << 0.0 << 0.0 << 0.0 << 0.0 << 0.0 << 0.0 << 0.0
101 << 3 << 6.6 << 6.9 << 7.4 << 6.7 << 4.6 << 2.7 << 0.4 << 0.0 << 0.0 << 0.0 << 0.0 << 0.0
102 << 4 << 8.6 << 9.6 << 10.6 << 10.1 << 8.0 << 6.6 << 5.0 << 4.2 << 2.7 << 0.0 << 0.0 << 0.0
103 << 5 << 11.8 << 13.0 << 16.0 << 15.1 << 11.6 << 9.7 << 8.1 << 8.2 << 7.9 << 4.9 << 3.2 << 2.1
104 << 6 << 15.6 << 17.6 << 23.0 << 23.6 << 22.1 << 20.0 << 16.0 << 15.1 << 12.1 << 7.9 << 6.2 << 5.1
105 << 7 << 18.7 << 21.5 << 28.4 << 30.2 << 30.7 << 31.0 << 25.2 << 23.1 << 17.5 << 10.7 << 8.4 << 7.2;
127 vGustNED.InitMatrix();
128 vTurbulenceNED.InitMatrix();
129 vCosineGust.InitMatrix();
131 oneMinusCosineGust.gustProfile.Running =
false;
132 oneMinusCosineGust.gustProfile.elapsedTime = 0.0;
142 if (Holding)
return false;
145 if (oneMinusCosineGust.gustProfile.Running) CosineGust();
147 vTotalWindNED = vWindNED + vGustNED + vCosineGust + vTurbulenceNED;
150 if (vWindNED(
eX) != 0.0) psiw = atan2( vWindNED(
eY), vWindNED(
eX) );
151 if (psiw < 0) psiw += 2*
M_PI;
163 if (vWindNED.Magnitude() == 0.0) {
167 vWindNED(
eNorth) = speed * cos(psiw);
168 vWindNED(
eEast) = speed * sin(psiw);
169 vWindNED(
eDown) = 0.0;
177 return vWindNED.Magnitude();
193void FGWinds::Turbulence(
double h)
199 vTurbPQR(
eP) = wind_from_clockwise;
200 if (TurbGain == 0.0)
return;
203 if (TurbGain < 0.0) TurbGain = 0.0;
204 if (TurbGain > 1.0) TurbGain = 1.0;
205 if (TurbRate < 0.0) TurbRate = 0.0;
206 if (TurbRate > 30.0) TurbRate = 30.0;
207 if (Rhythmicity < 0.0) Rhythmicity = 0.0;
208 if (Rhythmicity > 1.0) Rhythmicity = 1.0;
211 double time =
FDMExec->GetSimTime();
212 double sinewave = sin( time * TurbRate * 6.283185307 );
215 if (target_time == 0.0) {
216 strength = random = 1 - 2.0*(double(rand())/double(RAND_MAX));
217 target_time = time + 0.71 + (random * 0.5);
219 if (time > target_time) {
227 vTurbulenceNED.InitMatrix();
228 double delta = strength * max_vs * TurbGain * (1-Rhythmicity) * spike;
231 vTurbulenceNED(
eDown) = sinewave * max_vs * TurbGain * Rhythmicity;
232 vTurbulenceNED(
eDown)+= delta;
233 if (
in.DistanceAGL/
in.wingspan < 3.0)
234 vTurbulenceNED(
eDown) *=
in.DistanceAGL/
in.wingspan * 0.3333;
237 vTurbulenceNED(
eNorth) = sin( delta * 3.0 );
238 vTurbulenceNED(
eEast) = cos( delta * 3.0 );
241 vTurbPQR(
eP) += delta * 0.04;
251 if (probability_of_exceedence_index == 0 ||
in.V == 0) {
252 vTurbulenceNED(
eNorth) = vTurbulenceNED(
eEast) = vTurbulenceNED(
eDown) = 0.0;
253 vTurbPQR(
eP) = vTurbPQR(
eQ) = vTurbPQR(
eR) = 0.0;
258 double b_w =
in.wingspan, L_u, L_w, sig_u, sig_w;
260 if (b_w == 0.) b_w = 30.;
263 if (h <= 10.) h = 10;
267 L_u = h/pow(0.177 + 0.000823*h, 1.2);
269 sig_w = 0.1*windspeed_at_20ft;
270 sig_u = sig_w/pow(0.177 + 0.000823*h, 0.4);
271 }
else if (h <= 2000) {
273 L_u = L_w = 1000 + (h-1000.)/1000.*750.;
274 sig_u = sig_w = 0.1*windspeed_at_20ft
275 + (h-1000.)/1000.*(POE_Table->GetValue(probability_of_exceedence_index, h) - 0.1*windspeed_at_20ft);
278 sig_u = sig_w = POE_Table->GetValue(probability_of_exceedence_index, h);
284 xi_u_km1 = 0, nu_u_km1 = 0,
285 xi_v_km1 = 0, xi_v_km2 = 0, nu_v_km1 = 0, nu_v_km2 = 0,
286 xi_w_km1 = 0, xi_w_km2 = 0, nu_w_km1 = 0, nu_w_km2 = 0,
287 xi_p_km1 = 0, nu_p_km1 = 0,
288 xi_q_km1 = 0, xi_r_km1 = 0;
292 T_V =
in.totalDeltaT,
293 sig_p = 1.9/sqrt(L_w*b_w)*sig_w,
296 L_p = sqrt(L_w*b_w)/2.6,
306 xi_u=0, xi_v=0, xi_w=0, xi_p=0, xi_q=0, xi_r=0;
315 C_BL = 1/tau_u/tan(T_V/2/tau_u),
316 C_BLp = 1/tau_p/tan(T_V/2/tau_p),
317 C_BLq = 1/tau_q/tan(T_V/2/tau_q),
318 C_BLr = 1/tau_r/tan(T_V/2/tau_r);
324 xi_u = -(1 - C_BL*tau_u)/(1 + C_BL*tau_u)*xi_u_km1
325 + sig_u*sqrt(2*tau_u/T_V)/(1 + C_BL*tau_u)*(nu_u + nu_u_km1);
326 xi_v = -2*(
sqr(omega_v) -
sqr(C_BL))/
sqr(omega_v + C_BL)*xi_v_km1
327 -
sqr(omega_v - C_BL)/
sqr(omega_v + C_BL) * xi_v_km2
328 + sig_u*sqrt(3*omega_v/T_V)/
sqr(omega_v + C_BL)*(
329 (C_BL + omega_v/sqrt(3.))*nu_v
330 + 2/sqrt(3.)*omega_v*nu_v_km1
331 + (omega_v/sqrt(3.) - C_BL)*nu_v_km2);
332 xi_w = -2*(
sqr(omega_w) -
sqr(C_BL))/
sqr(omega_w + C_BL)*xi_w_km1
333 -
sqr(omega_w - C_BL)/
sqr(omega_w + C_BL) * xi_w_km2
334 + sig_w*sqrt(3*omega_w/T_V)/
sqr(omega_w + C_BL)*(
335 (C_BL + omega_w/sqrt(3.))*nu_w
336 + 2/sqrt(3.)*omega_w*nu_w_km1
337 + (omega_w/sqrt(3.) - C_BL)*nu_w_km2);
338 xi_p = -(1 - C_BLp*tau_p)/(1 + C_BLp*tau_p)*xi_p_km1
339 + sig_p*sqrt(2*tau_p/T_V)/(1 + C_BLp*tau_p) * (nu_p + nu_p_km1);
340 xi_q = -(1 - 4*b_w*C_BLq/
M_PI/
in.V)/(1 + 4*b_w*C_BLq/
M_PI/
in.V) * xi_q_km1
341 + C_BLq/
in.V/(1 + 4*b_w*C_BLq/
M_PI/
in.V) * (xi_w - xi_w_km1);
342 xi_r = - (1 - 3*b_w*C_BLr/
M_PI/
in.V)/(1 + 3*b_w*C_BLr/
M_PI/
in.V) * xi_r_km1
343 + C_BLr/
in.V/(1 + 3*b_w*C_BLr/
M_PI/
in.V) * (xi_v - xi_v_km1);
348 xi_u = (1 - T_V/tau_u) *xi_u_km1 + sig_u*sqrt(2*T_V/tau_u)*nu_u;
349 xi_v = (1 - 2*T_V/tau_u)*xi_v_km1 + sig_u*sqrt(4*T_V/tau_u)*nu_v;
350 xi_w = (1 - 2*T_V/tau_w)*xi_w_km1 + sig_w*sqrt(4*T_V/tau_w)*nu_w;
351 xi_p = (1 - T_V/tau_p) *xi_p_km1 + sig_p*sqrt(2*T_V/tau_p)*nu_p;
352 xi_q = (1 - T_V/tau_q) *xi_q_km1 +
M_PI/4/b_w*(xi_w - xi_w_km1);
353 xi_r = (1 - T_V/tau_r) *xi_r_km1 +
M_PI/3/b_w*(xi_v - xi_v_km1);
357 double cospsi = cos(psiw), sinpsi = sin(psiw);
358 vTurbulenceNED(
eNorth) = cospsi*xi_u + sinpsi*xi_v;
359 vTurbulenceNED(
eEast) = -sinpsi*xi_u + cospsi*xi_v;
360 vTurbulenceNED(
eDown) = xi_w;
362 vTurbPQR(
eP) = cospsi*xi_p + sinpsi*xi_q;
363 vTurbPQR(
eQ) = -sinpsi*xi_p + cospsi*xi_q;
367 vTurbPQR =
in.Tl2b*vTurbPQR;
370 xi_u_km1 = xi_u; nu_u_km1 = nu_u;
371 xi_v_km2 = xi_v_km1; xi_v_km1 = xi_v; nu_v_km2 = nu_v_km1; nu_v_km1 = nu_v;
372 xi_w_km2 = xi_w_km1; xi_w_km1 = xi_w; nu_w_km2 = nu_w_km1; nu_w_km1 = nu_w;
373 xi_p_km1 = xi_p; nu_p_km1 = nu_p;
388double FGWinds::CosineGustProfile(
double startDuration,
double steadyDuration,
double endDuration,
double elapsedTime)
391 if (elapsedTime >= 0 && elapsedTime <= startDuration) {
392 factor = (1.0 - cos(
M_PI*elapsedTime/startDuration))/2.0;
393 }
else if (elapsedTime > startDuration && (elapsedTime <= (startDuration + steadyDuration))) {
395 }
else if (elapsedTime > (startDuration + steadyDuration) && elapsedTime <= (startDuration + steadyDuration + endDuration)) {
396 factor = (1-cos(
M_PI*(1-(elapsedTime-(startDuration + steadyDuration))/endDuration)))/2.0;
406void FGWinds::CosineGust()
410 double factor = CosineGustProfile( profile.startupDuration,
411 profile.steadyDuration,
413 profile.elapsedTime);
415 oneMinusCosineGust.vWind.Normalize();
417 if (oneMinusCosineGust.vWindTransformed.Magnitude() == 0.0) {
418 switch (oneMinusCosineGust.gustFrame) {
420 oneMinusCosineGust.vWindTransformed =
in.Tl2b.Inverse() * oneMinusCosineGust.vWind;
423 oneMinusCosineGust.vWindTransformed =
in.Tl2b.Inverse() *
in.Tw2b * oneMinusCosineGust.vWind;
427 oneMinusCosineGust.vWindTransformed = oneMinusCosineGust.vWind;
434 vCosineGust = factor * oneMinusCosineGust.vWindTransformed * oneMinusCosineGust.magnitude;
436 profile.elapsedTime +=
in.totalDeltaT;
438 if (profile.elapsedTime > (profile.startupDuration + profile.steadyDuration + profile.endDuration)) {
439 profile.Running =
false;
440 profile.elapsedTime = 0.0;
441 oneMinusCosineGust.vWindTransformed.InitMatrix();
442 vCosineGust.InitMatrix(0);
450 for (
unsigned int i=0;
i<UpDownBurstCells.size();
i++)
delete UpDownBurstCells[
i];
451 UpDownBurstCells.clear();
453 for (
int i=0;
i<num;
i++) UpDownBurstCells.push_back(
new struct UpDownBurst);
462double FGWinds::DistanceFromRingCenter(
double lat,
double lon)
466 double dLat2 = deltaLat/2.0;
467 double dLong2 = deltaLong/2.0;
468 double a = sin(dLat2)*sin(dLat2)
469 + cos(lat)*cos(
in.
latitude)*sin(dLong2)*sin(dLong2);
470 double c = 2.0*atan2(sqrt(a), sqrt(1.0 - a));
477void FGWinds::UpDownBurst()
480 for (
unsigned int i=0;
i<UpDownBurstCells.size();
i++) {
481 DistanceFromRingCenter(UpDownBurstCells[
i]->ringLatitude, UpDownBurstCells[
i]->ringLongitude);
488void FGWinds::bind(
void)
490 typedef double (
FGWinds::*PMF)(int)
const;
491 typedef int (
FGWinds::*PMFt)(void)
const;
492 typedef void (
FGWinds::*PMFd)(int,double);
493 typedef void (
FGWinds::*PMFi)(int);
494 typedef double (
FGWinds::*Ptr)(void)
const;
554 PropertyManager->Tie(
"atmosphere/turbulence/milspec/windspeed_at_20ft_AGL-fps",
587void FGWinds::Debug(
int from)
596 if (from == 0) cout <<
"Instantiated: FGWinds" << endl;
597 if (from == 1) cout <<
"Destroyed: FGWinds" << endl;
JSBSim::FGFDMExec * FDMExec
static constexpr double radtodeg
static double GaussianRandomNumber(void)
FGPropertyManager * PropertyManager
bool InitModel(void) override
FGModel(FGFDMExec *)
Constructor.
virtual bool Run(bool Holding)
Runs the model; called by the Executive.
virtual void SetGustNED(int idx, double gust)
Sets a gust component in NED frame.
virtual void SetTurbType(tType tt)
Turbulence models available: ttNone, ttStandard, ttBerndt, ttCulp, ttMilspec, ttTustin.
virtual const FGColumnVector3 & GetTotalWindNED(void) const
Retrieves the total wind components in NED frame.
virtual double GetWindspeed(void) const
virtual void SetWindspeed(double speed)
virtual const FGColumnVector3 & GetGustNED(void) const
Retrieves the gust components in NED frame.
bool InitModel(void) override
virtual tType GetTurbType() const
virtual void SetProbabilityOfExceedence(int idx)
allowable range: 0-7, 3=light, 4=moderate, 6=severe turbulence
virtual double GetTurbNED(int idx) const
Retrieves a turbulence component in NED frame.
virtual void GustMagnitude(double mag)
Specifies the magnitude of the gust in feet/second.
virtual void SetTurbRate(double tr)
virtual void StartupGustDuration(double dur)
Specifies the duration of the startup portion of the gust.
virtual void SetWindspeed20ft(double ws)
virtual void SetWindPsi(double dir)
Sets the direction that the wind is coming from.
bool Run(bool Holding) override
Runs the winds model; called by the Executive Can pass in a value indicating if the executive is dire...
void NumberOfUpDownburstCells(int num)
virtual void EndGustDuration(double dur)
Specifies the length of time it takes for the gust to return to zero velocity.
virtual void SetTurbGain(double tg)
struct JSBSim::FGWinds::Inputs in
virtual void StartGust(bool running)
Initiates the execution of the gust.
virtual void SetRhythmicity(double r)
enum JSBSim::FGWinds::tType turbType
virtual void GustXComponent(double x)
Specifies the X component of velocity in the specified gust frame (ft/sec).
virtual void GustFrame(eGustFrame gFrame)
Specifies the frame that the gust direction vector components are specified in.
virtual const FGColumnVector3 & GetTurbPQR(void) const
virtual void GustYComponent(double y)
Specifies the Y component of velocity in the specified gust frame (ft/sec).
virtual double GetTurbGain() const
virtual double GetRhythmicity() const
virtual int GetProbabilityOfExceedence() const
FGWinds(FGFDMExec *)
Constructor.
virtual const FGColumnVector3 & GetWindNED(void) const
Retrieves the wind components in NED frame.
virtual void GustZComponent(double z)
Specifies the Z component of velocity in the specified gust frame (ft/sec).
virtual double GetWindspeed20ft() const
virtual double GetTurbRate() const
virtual double GetWindPsi(void) const
Retrieves the direction that the wind is coming from.
virtual void SetTurbNED(int idx, double turb)
Sets a turbulence component in NED frame.
virtual void SteadyGustDuration(double dur)
Specifies the length of time that the gust is at a steady, full strength.
virtual void SetWindNED(double wN, double wE, double wD)
Sets the wind components in NED frame.
constexpr double sqr(double x)
simply square a value
Stores information about a specified Up- or Down-burst.