24 strength = factor = 0.0;
25 cycle_timer = 60 * (rand() % 31);
40 setDiameter(scFileNode->getDoubleValue(
"diameter-ft", 0.0) / 6076.11549);
41 setHeight(scFileNode->getDoubleValue(
"height-msl", 5000.0));
46 factor = 8.0 * max_strength / (diameter * diameter * diameter);
48 _surface_wind_from_deg_node =
49 fgGetNode(
"/environment/config/boundary/entry[0]/wind-from-heading-deg",
true);
50 _surface_wind_speed_node =
51 fgGetNode(
"/environment/config/boundary/entry[0]/wind-speed-kt",
true);
52 _aloft_wind_from_deg_node =
53 fgGetNode(
"/environment/config/aloft/entry[2]/wind-from-heading-deg",
true);
54 _aloft_wind_speed_node =
55 fgGetNode(
"/environment/config/aloft/entry[2]/wind-speed-kt",
true);
64 tie(
"alt-rel", SGRawValuePointer<double>(&alt_rel));
65 tie(
"time", SGRawValuePointer<double>(&time));
66 tie(
"xx", SGRawValuePointer<double>(&xx));
67 tie(
"is-forming", SGRawValuePointer<bool>(&is_forming));
68 tie(
"is-formed", SGRawValuePointer<bool>(&is_formed));
69 tie(
"is-dying", SGRawValuePointer<bool>(&is_dying));
70 tie(
"is-dead", SGRawValuePointer<bool>(&is_dead));
83double FGAIThermal::get_strength_fac(
double alt_frac)
85 double PI = 4.0 * atan(1.0);
88 if (alt_frac <= 0.0) {
90 }
else if ((alt_frac > 0.0) && (alt_frac <= 0.1)) {
91 fac = (0.1 * (pow((10.0 * alt_frac), 10.0)));
92 }
else if ((alt_frac > 0.1) && (alt_frac <= 1.0)) {
93 fac = 0.4175 - 0.5825 * (cos(PI * (1.0 - sqrt(alt_frac)) + PI));
94 }
else if ((alt_frac > 1.0) && (alt_frac < 1.1)) {
95 fac = (0.5 * (1.0 + cos(PI * ((-2.0 * alt_frac) * 5.0))));
96 }
else if (alt_frac >= 1.1) {
104void FGAIThermal::Run(
double dt)
115 const double tmin1 = 5.0;
117 const double tmin2 = 20.0;
119 const double tmin3 = 25.0;
120 const double alive_cycle_time = tmin3 * 60.0;
122 const double tmin4 = 30.0;
124 const double t1 = tmin1 / tmin3;
125 const double t2 = tmin2 / tmin3;
126 const double t3 = 1.0;
127 const double t4 = tmin4 / tmin3;
130 time = cycle_timer / alive_cycle_time;
135 cycle_timer = 60 * (rand() % 31);
139 double thermal_foot_lat = (
pos.getLatitudeDeg());
140 double thermal_foot_lon = (
pos.getLongitudeDeg());
143 double MaxUpdraft = max_strength;
145 double MinUpdraft = -max_strength * 0.25;
147 double maxstrengthavail;
152 const double Rmax = diameter / 2.0;
154 const double shaping = 0.8;
163 double slice_center_lon;
164 double slice_center_lat;
172 if (dt_count >= 10.0) {
174 ground_elev_ft = alt * SG_METER_TO_FEET;
184 if (user_altitude < 1.0) { user_altitude = 1.0; };
185 double user_altitude_agl = (user_altitude - ground_elev_ft);
197 double wind_heading_rad;
200 double global_sink = -1.0;
206 double PI = 4.0 * atan(1.0);
217 maxstrengthavail = xx * get_strength_fac(alt_rel / xx);
224 }
else if ((time > t1) && (time <= t2)) {
225 maxstrengthavail = get_strength_fac((alt_rel));
232 }
else if ((time > t2) && (time <= t3)) {
233 xx = ((time - t2) / (1.0 - t2));
234 maxstrengthavail = get_strength_fac(alt_rel - xx);
242 maxstrengthavail = 0.0;
252 if ((alt_rel >= 0.0) && (alt_rel < 1.0)) {
253 Rsink = (shaping * Rmax) + (((1.0 - shaping) * Rmax * alt_rel) /
altitude_agl_ft);
254 }
else if ((alt_rel >= 1.0) && (alt_rel < 1.1)) {
255 Rsink = (Rmax / 2.0) * (1.0 + cos((10.0 * PI * alt_rel) - (2.0 * PI)));
261 Rup = r_up_frac * Rsink;
265 v_up_max = maxstrengthavail * MaxUpdraft;
266 v_up_min = maxstrengthavail * MinUpdraft;
280 double ground_wind_from_deg = _surface_wind_from_deg_node->getDoubleValue();
281 double ground_wind_speed_kts = _surface_wind_speed_node->getDoubleValue();
282 double aloft_wind_from_deg = _aloft_wind_from_deg_node->getDoubleValue();
283 double aloft_wind_speed_kts = _aloft_wind_speed_node->getDoubleValue();
285 double ground_wind_from_rad = (PI / 2.0) - PI * (ground_wind_from_deg / 180.0);
286 double aloft_wind_from_rad = (PI / 2.0) - PI * (aloft_wind_from_deg / 180.0);
288 wind_heading_rad = PI + 0.5 * (ground_wind_from_rad + aloft_wind_from_rad);
290 wind_speed = ground_wind_speed_kts + user_altitude * ((aloft_wind_speed_kts - ground_wind_speed_kts) / 9000.0);
292 double dt_center_alt = -(tan(0.034906 * wind_speed)) * (
altitude_agl_ft - user_altitude_agl);
296 double dt_slice_lon_FT = (dt_center_alt * cos(wind_heading_rad));
297 double dt_slice_lat_FT = (dt_center_alt * sin(wind_heading_rad));
302 slice_center_lon = thermal_foot_lon + dt_slice_lon;
303 slice_center_lat = thermal_foot_lat + dt_slice_lat;
305 dist_center = SGGeodesy::distanceNm(SGGeod::fromDeg(slice_center_lon, slice_center_lat),
310 if (max_strength >= 0.0) {
312 if ((dist_center >= 0.0) && (dist_center < Rup)) {
313 Vup = v_up_max * cos(dist_center * PI / (2.0 * Rup));
314 }
else if ((dist_center > Rup) && (dist_center <= ((Rup + Rsink) / 2.0))) {
315 Vup = v_up_min * cos((dist_center - (Rup + Rsink) / 2.0) * PI / (2.0 * ((Rup + Rsink) / 2.0 - Rup)));
316 }
else if ((dist_center > ((Rup + Rsink) / 2.0)) && dist_center <= Rsink) {
317 Vup = (global_sink + v_up_min) / 2.0 + (global_sink - v_up_min) / 2.0 * cos((dist_center - Rsink) * PI / ((Rsink - Rup) / 2.0));
324 if (alt_rel <= 1.1) {
325 double fac = (1.0 - (1.0 - 1.815 * alt_rel) * (1.0 - 1.815 * alt_rel));
326 Vup = fac * (global_sink + ((v_up_max - global_sink) / 2.0) * (1.0 + cos(dist_center * PI / Rmax)));
333 if (((alt_rel > 1.0) && (alt_rel < 1.1)) && (dist_center > Rsink)) {
334 Vup = global_sink * (11.0 - 10.0 * alt_rel);
337 if (alt_rel >= 1.1) {
virtual void readFromScenario(SGPropertyNode *scFileNode)
FGAIBase(object_type ot, bool enableHot)
bool getGroundElevationM(const SGGeod &pos, double &elev, const simgear::BVHMaterial **material) const
virtual bool init(ModelSearchOrder searchOrder)
void setAltitude(double altitude_ft)
virtual void update(double dt)
void tie(const char *aRelPath, const SGRawValue< T > &aRawValue)
Tied-properties helper, record nodes which are tied for easy un-tie-ing.
void setMaxStrength(double s)
void readFromScenario(SGPropertyNode *scFileNode) override
void setDiameter(double d)
void update(double dt) override
bool init(ModelSearchOrder searchOrder) override
SGGeod get_aircraft_position() const
SGPropertyNode * fgGetNode(const char *path, bool create)
Get a property node.