FlightGear next
AISim.cpp
Go to the documentation of this file.
1// AISim.cxx -- interface to the AI Sim
2//
3// Written by Erik Hofman, started November 2016
4//
5// Copyright (C) 2016-2020 by Erik Hofman <erik@ehofman.com>
6//
7//
8// This program is free software; you can redistribute it and/or
9// modify it under the terms of the GNU General Public License as
10// published by the Free Software Foundation; either version 2 of the
11// License, or (at your option) any later version.
12//
13// This program is distributed in the hope that it will be useful, but
14// WITHOUT ANY WARRANTY; without even the implied warranty of
15// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16// General Public License for more details.
17//
18// You should have received a copy of the GNU General Public License
19// along with this program; if not, write to the Free Software
20// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21//
22
23#include "AISim.hpp"
24
25#include <fenv.h>
26
27#include <cmath>
28#include <limits>
29#include <cstdio>
30
31#include <string>
32#include <fstream>
33#include <streambuf>
34
35#ifdef ENABLE_SP_FDM
36# include <simgear/constants.h>
37# include <simgear/math/simd.hxx>
38# include <simgear/math/simd4x4.hxx>
39# include <simgear/math/sg_geodesy.hxx>
40
41# include <Aircraft/controls.hxx>
42# include <Main/fg_props.hxx>
43# include <Main/globals.hxx>
44# include <FDM/flight.hxx>
45# include <cJSON.h>
46#else
47# include "simd.hxx"
48# include "simd4x4.hxx"
49#endif
50
51#define FEET_TO_INCHES 12.0f
52#define INCHES_TO_FEET (1.0f/FEET_TO_INCHES)
53
54FGAISim::FGAISim(double dt)
55{
56 simd4x4::zeros(xCDYLT);
57 simd4x4::zeros(xClmnT);
58
59 for (size_t i=0; i<AISIM_MAX; ++i)
60 {
61 contact_pos[i] = 0.0f;
62 contact_spring[i] = contact_damp[i] = 0.0f;
63 FT[i] = MT[i] = 0.0f;
64 }
65
66#ifdef ENABLE_SP_FDM
67 SGPath aircraft_path( fgGetString("/sim/fg-root") );
68 SGPropertyNode_ptr aero = fgGetNode("sim/aero", true);
69 aircraft_path.append("Aircraft-aisim");
70 aircraft_path.append(aero->getStringValue());
71 aircraft_path.concat(".json");
72
73 load(aircraft_path.str());
74#else
75 load("");
76#endif
77
78 set_rudder_norm(0.0f);
79 set_elevator_norm(0.0f);
80 set_aileron_norm(0.0f);
81 set_throttle_norm(0.0f);
82 set_flaps_norm(0.0f);
83 set_brake_norm(0.0f);
84
85 set_velocity_fps(0.0f);
86 set_alpha_rad(0.0f);
87 set_beta_rad(0.0f);
88
89 /* useful constants assigned to a vector */
90 xCp[SIDE] = CYp;
91 xCr[SIDE] = CYr;
92 xCp[ROLL] = Clp;
93 xCr[ROLL] = Clr;
94 xCp[YAW] = Cnp;
95 xCr[YAW] = Cnr;
96
97 xCq[LIFT] = -CLq;
98 xCadot[LIFT] = -CLadot;
99
100 xCq[PITCH] = Cmq;
101 xCadot[PITCH] = Cmadot;
102
103 xCDYLT.ptr()[MIN][LIFT] = -CLmin;
104 xCDYLT.ptr()[MIN][DRAG] = -CDmin;
105
106 /* m is assigned in the load function */
107 inv_mass = aiVec3(1.0f)/mass;
108
109 // calculate aircraft position when resting on the landing gear.
110 float cg_agl = -cg[Z];
111 if (no_contacts)
112 {
113 for (size_t i=0; i<no_contacts; ++i) {
114 cg_agl += contact_pos[i][Z];
115 }
116 cg_agl /= static_cast<float>(no_contacts);
117 }
118 set_altitude_agl_ft(cg_agl);
119
120 // Contact point at the center of gravity
121 contact_pos[no_contacts] = 0.0f;
122 contact_spring[no_contacts] = -20000.0f;
123 contact_damp[no_contacts] = -2000.0f;
124 no_contacts++;
125
126 aiMtx4 mcg;
127 simd4x4::unit(mcg);
128 simd4x4::translate(mcg, cg);
129
130 mJ = aiMtx4( I[XX], 0.0f, -I[XZ], 0.0f,
131 0.0f, I[YY], 0.0f, 0.0f,
132 -I[XZ], 0.0f, I[ZZ], 0.0f,
133 0.0f, 0.0f, 0.0f, 0.0f);
134 mJinv = invert_inertia(mJ);
135 mJ *= mcg;
136 mJinv *= matrix_inverse(mcg);
137}
138
139FGAISim::~FGAISim()
140{
141}
142
143// Initialize the AISim flight model, dt is the time increment for
144// each subsequent iteration through the EOM
145void
146FGAISim::init()
147{
148// feenableexcept(FE_INVALID | FE_OVERFLOW);
149#ifdef ENABLE_SP_FDM
150 // do init common to all the FDM's
151 common_init();
152
153 // now do init specific to the AISim
154 SG_LOG( SG_FLIGHT, SG_INFO, "Starting initializing AISim" );
155
156 // cg_agl holds the lowest contact point of the aircraft
157 set_Altitude( get_Altitude() + cg_agl );
158 set_location_geod( get_Latitude(), get_Longitude(), get_Altitude() );
159 set_euler_angles_rad( get_Phi(), get_Theta(), get_Psi() );
160 set_velocity_fps( fgGetFloat("sim/presets/uBody-fps"),
161 fgGetFloat("sim/presets/vBody-fps"),
162 fgGetFloat("sim/presets/wBody-fps"));
163#endif
164}
165
166void
167FGAISim::update(double ddt)
168{
169#ifdef ENABLE_SP_FDM
170 if (is_suspended() || ddt == 0)
171 return;
172#endif
173
174 // initialize all of AISim vars
175 aiVec3 dt(ddt);
176
177#ifdef ENABLE_SP_FDM
178 copy_to_AISim();
179#endif
180
181#if 0
182 printf("pitch: % 7.5f, roll: % 7.5f, heading: % 7.5f\n", euler[THETA], euler[PHI], euler[PSI]);
183 printf("wind north: % 7.5f, east: % 7.5f, down: % 7.5f\n", wind_ned[0], wind_ned[1], wind_ned[2]);
184#endif
185
186 /* Earth-to-Body-Axis Transformation Matrix */
187 /* matrices to compensate for pitch, roll and heading */
188 aiVec3 vector = euler; // simd4::normalize alters the vector
189 float len = simd4::normalize(vector);
190 aiMtx4 mNed2Body = simd4x4::rotation_matrix(len, vector);
191 aiMtx4 mBody2Ned = simd4x4::transpose(mNed2Body);
192 aiVec3 wind = mNed2Body*wind_ned;
193
194 /* Air-Relative velocity vector */
195 vUVWaero = vUVW + wind;
196 update_velocity( simd4::magnitude( vUVWaero ) );
197
198 /* Wind angles */
199 aiVec3 prevAOA = AOA;
200 alpha = (vUVWaero[U] == 0.0f) ? 0.0f : std::atan2(vUVWaero[W], vUVWaero[U]);
201 set_alpha_rad( alpha );
202
203 beta = (velocity == 0.0f) ? 0.0f : std::asin(vUVWaero[V]/velocity);
204 set_beta_rad( beta );
205
206 /* set_alpha_rad and set_beta_rad set the new AOA */
207 AOAdot = (AOA - prevAOA)/dt;
208
209#if 0
210 printf("velocity: %f, vUVWaero: %3.2f, %3.2f, %3.2f, mach: %3.2f\n", velocity, vUVWaero[U], vUVWaero[V], vUVWaero[W], mach);
211 printf("cg_agl: %5.4f, alpha: %5.4f, beta: %5.4f, adot: %5.4f, bdot: %5.4f\n", cg_agl, AOA[ALPHA], AOA[BETA], AOAdot[ALPHA], AOAdot[BETA]);
212#endif
213
214 /* Force and Moment Coefficients */
215 /* Sum all Drag, Side, Lift, Roll, Pitch and Yaw and Thrust coefficients */
216 float p = vPQR[P];
217 float q = vPQR[Q];
218 float r = vPQR[R];
219 float adot = AOAdot[ALPHA];
220
221 /*
222 * CDYL[LIFT] = (CLq*q + CLadot*adot)*cbar_2U;
223 * Clmn[PITCH] = (Cmq*q + Cmadot*adot)*cbar_2U;
224 *
225 * CDYL[SIDE] = (CYp*p + CYr*r)*b_2U;
226 * Clmn[ROLL] = (Clp*p + Clr*r)*b_2U;
227 * Clmn[YAW] = (Cnp*p + Cnr*r)*b_2U;
228 */
229 aiVec4 Ccbar2U = (xCq*q + xCadot*adot)*cbar_2U;
230 aiVec4 Cb2U = (xCp*p + xCr*r)*b_2U;
231
232 /* xCDYLT and xClmnT already have their factors applied */
233 /* in the functions in the header file. */
234 aiVec4 CDYL(0.0f, Cb2U[SIDE], Ccbar2U[LIFT]);
235 aiVec4 Clmn(Cb2U[ROLL], Ccbar2U[PITCH], Cb2U[YAW]);
236 size_t i = 3;
237 do {
238 CDYL += static_cast<aiVec4>(xCDYLT.m4x4()[i]);
239 Clmn += static_cast<aiVec4>(xClmnT.m4x4()[i]);
240 }
241 while(i--);
242
243 float CL = CDYL[LIFT];
244 CDYL += aiVec3(CDi*CL*CL, 0.0f, 0.0f);
245#if 0
246 printf(" p: %6.3f, q: %6.3f, r: %6.3f, adot: %6.3f\n", p, q, r, adot);
247 printf(" CLa: %6.3f, CLadot: %6.3f, CLq: %6.3f\n", xCDYLT.ptr()[ALPHA][LIFT],CLadot*adot,CLq*q);
248 printf(" CDa: %6.3f, CDb: %6.3f, CDi: %6.3f\n", xCDYLT.ptr()[ALPHA][DRAG],xCDYLT.ptr()[BETA][DRAG],CDi*CL*CL);
249 printf(" CYb: %6.3f, CYp: %6.3f, CYr: %6.3f\n", xCDYLT.ptr()[BETA][SIDE],CYp*p,CYr*r);
250 printf(" Cma: %6.3f, Cmadot: %6.3f, Cmq: %6.3f\n", xClmnT.ptr()[ALPHA][PITCH],Cmadot*adot,Cmq*q);
251 printf(" Clb: %6.3f, Clp: %6.3f, Clr: %6.3f\n", xClmnT.ptr()[BETA][ROLL],Clp*p,Clr*r);
252 printf(" Cnb: %6.3f, Cnp: %6.3f, Cnr: %6.3f\n", xClmnT.ptr()[BETA][YAW],Cnp*p,Cnr*r);
253
254 printf(" Cmde: %6.3f\n", xClmnT.ptr()[ELEVATOR][PITCH]);
255 printf(" CYdr: %6.3f, Cldr: %6.3f, Cndr: %6.3f\n", xCDYLT.ptr()[RUDDER][SIDE], xClmnT.ptr()[RUDDER][ROLL], xClmnT.ptr()[RUDDER][YAW]);
256 printf(" Clda: %6.3f, CYda: %6.3f\n", xClmnT.ptr()[AILERON][ROLL], xClmnT.ptr()[AILERON][YAW]);
257 printf(" Cldf: %6.3f, CDdf: %6.3f, Cmdf: %6.3f\n", xCDYLT.ptr()[FLAPS][LIFT], xCDYLT.ptr()[FLAPS][DRAG], xClmnT.ptr()[FLAPS][PITCH]);
258 printf("\n");
259#endif
260
261 /* State Accelerations (convert coefficients to forces and moments) */
262 aiVec3 FDYL = CDYL*Coef2Force;
263 aiVec3 Mlmn = Clmn*Coef2Moment;
264
265 /* convert from wind axes to body axes */
266 vector = AOA;
267 len = simd4::normalize(vector);
268 aiMtx4 mWind2Body = simd4x4::rotation_matrix(len, vector);
269 aiVec3 FXYZ_body = mWind2Body*FDYL;
270
271 aiVec3 gravity_body = mNed2Body*gravity_ned;
272 FXYZ_body += gravity_body*mass;
273
274 /* Thrust */
275 float Cth = rho*throttle*throttle;
276 i = no_engines-1;
277 do
278 {
279 aiVec3 FEngine = FT[i]*(Cth*n2[i]);
280 aiVec3 MEngine = MT[i]*Cth;
281
282 FXYZ_body += FEngine;
283 Mlmn += MEngine;
284#if 0
285 printf("FT[%lu]: %5.4f, %5.4f, %5.4f\n", i, FEngine[X], FEngine[Y], FEngine[Z]);
286 printf("MT[%lu]: %5.4f, % 7.5f, %5.4f\n", i, MEngine[ROLL], MEngine[PITCH], MEngine[YAW]);
287#endif
288 }
289 while (i--);
290#if 0
291 printf("FXYZ: %5.4f, %5.4f, %5.4f\n", FXYZ_body[X], FXYZ_body[Y], FXYZ_body[Z]);
292 printf("Mlmn: %5.4f, % 7.5f, %5.4f\n", Mlmn[ROLL], Mlmn[PITCH], Mlmn[YAW]);
293#endif
294
295 /* contact point (landing gear) forces and moments */
296 WoW = false;
297 if (no_contacts && cg_agl < 10.0f)
298 {
299 size_t WoW_main = 0;
300 i = 0;
301 do
302 {
303 aiVec3 lg_ground_ned = mBody2Ned*contact_pos[i];
304 if (lg_ground_ned[Z] > cg_agl)
305 { // weight on wheel
306 aiVec3 lg_vrot = simd4::cross(vPQR, contact_pos[i]);
307 aiVec3 lg_cg_vned = mBody2Ned*lg_vrot;
308 aiVec3 lg_vned = vNED + lg_cg_vned;
309 float Fn = std::min((contact_spring[i]*lg_ground_ned[Z] +
310 contact_damp[i]*lg_vned[Z]), 0.0f);
311
312 aiVec3 Fgear(0.0f, 0.0f, Fn);
313 aiVec3 Fbody = mNed2Body*Fgear;
314 aiVec3 Fbrake = mu_body*Fbody;
315
316 aiVec3 FLGear = Fbody + Fbrake;
317 FXYZ_body += FLGear;
318
319// aiVec3 MLGear = simd4::cross(contact_pos[i], FLGear);
320// Mlmn += MLGear;
321#if 0
322 printf("gear: %lu: pos: % 3.2f % 3.2f % 3.2f\n", i,
323 lg_ground_ned[0], lg_ground_ned[1], lg_ground_ned[2]);
324 printf(" Fbody: % 7.2f % 7.2f % 7.2f\n", Fbody[0], Fbody[1], Fbody[2]);
325 printf(" Fbrake: % 7.2f % 7.2f % 7.2f\n", Fbrake[0], Fbrake[1], Fbrake[2]);
326 printf(" Fgear: % 7.2f % 7.2f % 7.2f\n", lg_ground_ned[0], lg_ground_ned[1], lg_ground_ned[2]);
327 printf(" Mgear: % 7.2f % 7.2f % 7.2f\n", MLGear[0], MLGear[1], MLGear[2]);
328#endif
329 if (i<3) WoW_main++;
330 }
331 WoW = (WoW_main == 3);
332 }
333 while(++i < no_contacts);
334 }
335
336 /* local body accelrations */
337 XYZdot = FXYZ_body*inv_mass;
338#if 0
339printf("AOAdot: %5.4f, AOA: %5.4f, up: XYZdot: % 7.5f, XYZ: % 7.5f, gravity: % 7.5f\n", AOAdot[ALPHA], AOA[ALPHA], XYZdot[Z], FXYZ_body[Z]/mass, gravity_body[DOWN]);
340#endif
341
342#if 0
343 printf("XYZdot % 7.5f, % 7.5f, % 7.5f\n", XYZdot[X], XYZdot[Y], XYZdot[Z]);
344 printf("Mlmn: % 7.5f, % 7.5f, % 7.5f\n", Mlmn[ROLL], Mlmn[PITCH], Mlmn[YAW]);
345#endif
346
347 /* Dynamic Equations */
348
349 /* body-axis translational accelerations: forward, sideward, upward */
350 vUVWdot = XYZdot - simd4::cross(vPQR, vUVW);
351 vUVW += vUVWdot*dt;
352
353 /* body-axis rotational accelerations: rolling, pitching, yawing */
354 vPQRdot = mJinv*(Mlmn - vPQR*(mJ*vPQR));
355 vPQR += vPQRdot*dt;
356#if 0
357 printf("PQRdot: % 7.5f, % 7.5f, % 7.5f\n", vPQRdot[P], vPQRdot[Q], vPQRdot[R]);
358 printf("PQR: % 7.5f, % 7.5f, % 7.5f\n", vPQR[P], vPQR[Q], vPQR[R]);
359 printf("UVWdot: % 7.5f, % 7.5f, % 7.5f\n", vUVWdot[U], vUVWdot[V], vUVWdot[W]);
360 printf("UVW: % 7.5f, % 7.5f, %1.7f\n", vUVW[U], vUVW[V], vUVW[W]);
361#endif
362
363 /* position of center of mass wrt earth: north, east, down */
364 vNED = mBody2Ned*vUVW;
365 aiVec3 NEDdist = vNED*dt;
366#if 0
367 printf("vNED: % 7.5f, % 7.5f, % 7.5f\n", vNED[NORTH], vNED[EAST], vNED[DOWN]);
368 printf("NEDdist: % 7.5f, % 7.5f, % 7.5f\n", NEDdist[NORTH], NEDdist[EAST], NEDdist[DOWN]);
369#endif
370
371#ifdef ENABLE_SP_FDM
372 double dist = simd4::magnitude( aiVec2(NEDdist) );
373
374 double lat2 = 0.0, lon2 = 0.0, az2 = 0.0;
375 geo_direct_wgs_84( 0.0, location_geod[LATITUDE] * SGD_RADIANS_TO_DEGREES,
376 location_geod[LONGITUDE] * SGD_RADIANS_TO_DEGREES,
377 euler[PSI] * SGD_RADIANS_TO_DEGREES,
378 dist * SG_FEET_TO_METER, &lat2, &lon2, &az2 );
379 set_location_geod( lat2 * SGD_DEGREES_TO_RADIANS,
380 lon2 * SGD_DEGREES_TO_RADIANS,
381 location_geod[ALTITUDE] - NEDdist[DOWN] );
382// set_heading_rad( az2 * SGD_DEGREES_TO_RADIANS );
383#else
384 location_geod[X] += NEDdist[X];
385 location_geod[Y] += NEDdist[Y];
386 location_geod[Z] -= NEDdist[Z];
387 set_altitude_cg_agl_ft(location_geod[DOWN]);
388#endif
389#if 0
390 printf("GEOD: % 7.5f, % 7.5f, % 7.5f\n", location_geod[0], location_geod[1], location_geod[2]);
391#endif
392
393#if 0
394{
395 vector = simd4::normalize(euler);
396 float len = simd4::normalize(vector);
397 aiMtx4 mWind2Body = simd4x4::rotation_matrix(len, vector);
398 euler_dot = mWind2Body*vPQR;
399 euler += euler_dot*dt;
400}
401#else
402 /* angle of body wrt earth: phi (roll), theta (pitch), psi (heading) */
403 float sin_p = std::sin(euler[PHI]);
404 float cos_p = std::cos(euler[PHI]);
405 float sin_t = std::sin(euler[THETA]);
406 float cos_t = std::cos(euler[THETA]);
407 if (std::abs(cos_t) < 0.00001f) cos_t = std::copysign(0.00001f,cos_t);
408
409 if (cos_t == 0.0f) {
410 euler_dot[PSI] = 0.0f;
411 } else {
412 euler_dot[PSI] = (q*sin_p + r*cos_p)/cos_t;
413 }
414 euler_dot[THETA] = q*cos_p - r*sin_p;
415/* euler_dot[PHI] = p + (q*sin_p + r*cos_p)*sin_t/cos_t; */
416 euler_dot[PHI] = p + euler_dot[PSI]*sin_t;
417 euler += euler_dot*dt;
418#if 0
419 printf("euler: % 7.5f, % 7.5f, % 7.5f\n", euler[PHI], euler[THETA], euler[PSI]);
420#endif
421#endif
422
423#ifdef ENABLE_SP_FDM
424 copy_from_AISim();
425#endif
426}
427
428#ifdef ENABLE_SP_FDM
429bool
430FGAISim::copy_to_AISim()
431{
432 set_rudder_norm(globals->get_controls()->get_rudder());
433 set_elevator_norm(globals->get_controls()->get_elevator());
434 set_aileron_norm(globals->get_controls()->get_aileron());
435 set_flaps_norm(globals->get_controls()->get_flaps());
436 set_throttle_norm(globals->get_controls()->get_throttle(0));
437 set_brake_norm(0.5f*(globals->get_controls()->get_brake_left()
438 +globals->get_controls()->get_brake_right()));
439
440
441 set_altitude_asl_ft(get_Altitude());
442 set_altitude_agl_ft(get_Altitude_AGL());
443
444// set_location_geod(get_Latitude(), get_Longitude(), altitde);
445// set_velocity_fps(get_V_calibrated_kts());
446
447 return true;
448}
449
450bool
451FGAISim::copy_from_AISim()
452{
453 // Mass properties and geometry values
454// _set_Inertias( mass, I[XX], I[YY], I[ZZ], I[XZ] );
455 _set_CG_Position( cg[X], cg[Y], cg[Z]);
456
457 // Accelerations
458 _set_Accels_Body( vUVWdot[U], vUVWdot[V], vUVWdot[W] );
459
460 // Velocities
461 _set_V_equiv_kts( velocity*std::sqrt(sigma) * SG_FPS_TO_KT );
462 _set_V_calibrated_kts( std::sqrt( 2.0f*qbar*sigma/rho) * SG_FPS_TO_KT );
463 _set_V_ground_speed( simd4::magnitude(aiVec2(vNED)) * SG_FPS_TO_KT );
464 _set_Mach_number( mach );
465
466 _set_Velocities_Local( vNED[NORTH], vNED[EAST], vNED[DOWN] );
467// _set_Velocities_Local_Airmass( vUVWaero[U], vUVWaero[V], vUVWaero[W] );
468 _set_Velocities_Body( vUVW[U], vUVW[V], vUVW[W] );
469 _set_Omega_Body( vPQR[P], vPQR[Q], vPQR[R] );
470 _set_Euler_Rates( euler_dot[PHI], euler_dot[THETA], euler_dot[PSI] );
471
472 // Positions
473 double lon = location_geod[LONGITUDE];
474 double lat_geod = location_geod[LATITUDE];
475 double altitude = location_geod[ALTITUDE];
476 double lat_geoc;
477
478 sgGeodToGeoc( lat_geod, altitude, &sl_radius, &lat_geoc );
479 _set_Geocentric_Position( lat_geoc, lon, sl_radius+altitude );
480
481 _set_Geodetic_Position( lat_geod, lon, altitude );
482
483 _set_Euler_Angles( euler[PHI], euler[THETA],
484 SGMiscd::normalizePeriodic(0, SGD_2PI, euler[PSI]) );
485
486 set_altitude_agl_ft( altitude - get_Runway_altitude() );
487 _set_Altitude_AGL( cg_agl );
488
489// _set_Alpha( alpha );
490// _set_Beta( beta );
491
492// _set_Gamma_vert_rad( Gamma_vert_rad );
493
494// _set_Density( Density );
495
496// _set_Static_pressure( Static_pressure );
497
498// _set_Static_temperature( Static_temperature );
499
500 _set_Sea_level_radius( sl_radius * SG_METER_TO_FEET );
501// _set_Earth_position_angle( Earth_position_angle );
502
503// _set_Runway_altitude( get_Runway_altitude() );
504
505 _set_Climb_Rate( -vNED[DOWN] );
506
507 _update_ground_elev_at_pos();
508
509 return true;
510}
511#endif
512
513// ----------------------------------------------------------------------------
514
515#define OMEGA_EARTH 0.00007272205217f
516#define MAX_ALT 101
517
518// 1976 Standard Atmosphere - Density (slugs/ft2): 0 - 101,000 ft
519float FGAISim::density[MAX_ALT] = {
520 0.0023771699, 0.0023083901, 0.0022411400, 0.0021753900, 0.0021111399,
521 0.0020483399, 0.0019869800, 0.0019270401, 0.0018685000, 0.0018113200,
522 0.0017554900, 0.0017009900, 0.0016477900, 0.0015958800, 0.0015452200,
523 0.0014958100, 0.0014476100, 0.0014006100, 0.0013547899, 0.0013101200,
524 0.0012665900, 0.0012241700, 0.0011828500, 0.0011426000, 0.0011034100,
525 0.0010652600, 0.0010281201, 0.0009919840, 0.0009568270, 0.0009226310,
526 0.0008893780, 0.0008570500, 0.0008256280, 0.0007950960, 0.0007654340,
527 0.0007366270, 0.0007086570, 0.0006759540, 0.0006442340, 0.0006140020,
528 0.0005851890, 0.0005577280, 0.0005315560, 0.0005066120, 0.0004828380,
529 0.0004601800, 0.0004385860, 0.0004180040, 0.0003983890, 0.0003796940,
530 0.0003618760, 0.0003448940, 0.0003287090, 0.0003132840, 0.0002985830,
531 0.0002845710, 0.0002712170, 0.0002584900, 0.0002463600, 0.0002347990,
532 0.0002237810, 0.0002132790, 0.0002032710, 0.0001937320, 0.0001846410,
533 0.0001759760, 0.0001676290, 0.0001595480, 0.0001518670, 0.0001445660,
534 0.0001376250, 0.0001310260, 0.0001247530, 0.0001187880, 0.0001131160,
535 0.0001077220, 0.0001025920, 0.0000977131, 0.0000930725, 0.0000886582,
536 0.0000844590, 0.0000804641, 0.0000766632, 0.0000730467, 0.0000696054,
537 0.0000663307, 0.0000632142, 0.0000602481, 0.0000574249, 0.0000547376,
538 0.0000521794, 0.0000497441, 0.0000474254, 0.0000452178, 0.0000431158,
539 0.0000411140, 0.0000392078, 0.0000373923, 0.0000356632, 0.0000340162,
540 0.0000324473
541};
542
543// 1976 Standard Atmosphere - Speed of sound (ft/s): 0 - 101,000 ft
544float FGAISim::vsound[MAX_ALT] = {
545 1116.450, 1112.610, 1108.750, 1104.880, 1100.990, 1097.090, 1093.180,
546 1089.250, 1085.310, 1081.360, 1077.390, 1073.400, 1069.400, 1065.390,
547 1061.360, 1057.310, 1053.250, 1049.180, 1045.080, 1040.970, 1036.850,
548 1032.710, 1028.550, 1024.380, 1020.190, 1015.980, 1011.750, 1007.510,
549 1003.240, 998.963, 994.664, 990.347, 986.010, 981.655, 977.280,
550 972.885, 968.471, 968.076, 968.076, 968.076, 968.076, 968.076,
551 968.076, 968.076, 968.076, 968.076, 968.076, 968.076, 968.076,
552 968.076, 968.076, 968.076, 968.076, 968.076, 968.076, 968.076,
553 968.076, 968.076, 968.076, 968.076, 968.076, 968.076, 968.076,
554 968.076, 968.076, 968.076, 968.337, 969.017, 969.698, 970.377,
555 971.056, 971.735, 972.413, 973.091, 973.768, 974.445, 975.121,
556 975.797, 976.472, 977.147, 977.822, 978.496, 979.169, 979.842,
557 980.515, 981.187, 981.858, 982.530, 983.200, 983.871, 984.541,
558 985.210, 985.879, 986.547, 987.215, 987.883, 988.550, 989.217,
559 989.883, 990.549, 991.214
560};
561
562void
563FGAISim::update_velocity(float v)
564{
565 velocity = v;
566
567 /* altitude related */
568 float alt_kft = _MINMAX(location_geod[ALTITUDE]/1000.0f, 0, MAX_ALT);
569 float alt_idx = std::floor(alt_kft);
570 size_t idx = static_cast<size_t>(alt_idx);
571 float fract = alt_kft - alt_idx;
572 float ifract = 1.0f - fract;
573
574 /* linear interpolation for density */
575 rho = ifract*density[idx] + fract*density[idx+1];
576 qbar = 0.5f*rho*v*v;
577 sigma = rho/density[0];
578
579 float Sqbar = Sw*qbar;
580 float Sbqbar = Sqbar*span;
581 float Sqbarcbar = Sqbar*cbar;
582
583 Coef2Force = aiVec3(Sqbar);
584 Coef2Moment = aiVec3(Sbqbar);
585 Coef2Moment[PITCH] = Sqbarcbar;
586
587 /* linear interpolation for speed of sound */
588 float vs = ifract*vsound[idx] + fract*vsound[idx+1];
589 mach = v/vs;
590
591 /* useful semi-constants */
592 if (v == 0.0f) {
593 cbar_2U = b_2U = 0.0f;
594 }
595 else
596 {
597 b_2U = 0.5f*span/v;
598 cbar_2U = 0.5f*cbar/v;
599 }
600}
601
602// structural: x is pos. aft., y is pos. right, z is pos. up. in inches.
603// body: x is pos. fwd., y is pos. right, z is pos. down in feet.
604void
605FGAISim::struct_to_body(aiVec3 &pos)
606{
607 pos *= INCHES_TO_FEET;
608 pos[0] = -pos[0];
609 pos[2] = -pos[2];
610}
611
612simd4x4_t<float,4>
613FGAISim::matrix_inverse(aiMtx4 mtx)
614{
615 aiMtx4 dst;
616 aiVec4 v1, v2;
617
618 dst = simd4x4::transpose(mtx);
619
620 v1 = static_cast<aiVec4>(mtx.m4x4()[3]);
621 v2 = static_cast<aiVec4>(mtx.m4x4()[0]);
622 dst.ptr()[3][0] = -simd4::dot(v1, v2);
623
624 v2 = static_cast<aiVec4>(mtx.m4x4()[1]);
625 dst.ptr()[3][1] = -simd4::dot(v1, v2);
626
627 v2 = static_cast<aiVec4>(mtx.m4x4()[2]);
628 dst.ptr()[3][2] = -simd4::dot(v1, v2);
629
630 return dst;
631}
632
633simd4x4_t<float,4>
634FGAISim::invert_inertia(aiMtx4 mtx)
635{
636 float Ixx, Iyy, Izz, Ixz;
637 float k1, k3, k4, k6;
638 float denom;
639
640 Ixx = mtx.ptr()[0][0];
641 Iyy = mtx.ptr()[1][1];
642 Izz = mtx.ptr()[2][2];
643 Ixz = -mtx.ptr()[0][2];
644
645 k1 = Iyy*Izz;
646 k3 = Iyy*Ixz;
647 denom = 1.0f/(Ixx*k1 - Ixz*k3);
648
649 k1 *= denom;
650 k3 *= denom;
651 k4 = (Izz*Ixx - Ixz*Ixz)*denom;
652 k6 = Ixx*Iyy*denom;
653
654 return aiMtx4( k1, 0.0f, k3, 0.0f,
655 0.0f, k4, 0.0f, 0.0f,
656 k3, 0.0f, k6, 0.0f,
657 0.0f, 0.0f, 0.0f, 0.0f );
658}
659
660
661std::map<std::string,float>
662FGAISim::jsonParse(const char *str)
663{
664 cJSON *json = ::cJSON_Parse(str);
665 jsonMap rv;
666 if (json)
667 {
668 for (int i=0; i<::cJSON_GetArraySize(json); ++i)
669 {
670 cJSON* cj = ::cJSON_GetArrayItem(json, i);
671 if (cj->string)
672 {
673 if (cj->valuedouble) {
674 rv.emplace(cj->string, cj->valuedouble);
675 }
676 else if (cj->type == cJSON_Array)
677 {
678 cJSON* child = cj->child;
679 for (int j=0; child; child = child->next, ++j)
680 {
681 std::string str = cj->string;
682 str += '[';
683 str += std::to_string(j);
684 str += ']';
685 if (child->type == cJSON_Object)
686 {
687 cJSON* subchild = child->child;
688 for (int k=0; subchild; subchild = subchild->next, ++k)
689 {
690 std::string substr = str;
691 substr += '/';
692 substr += subchild->string;
693
694 if (subchild->type == cJSON_Array)
695 {
696 cJSON* array = subchild->child;
697 for (int l=0; array; array = array->next, ++l)
698 {
699 std::string arraystr = substr;
700 arraystr += '[';
701 arraystr += std::to_string(l);
702 arraystr += ']';
703 rv.emplace(arraystr, array->valuedouble);
704 }
705 }
706 else {
707 rv.emplace(substr, subchild->valuedouble);
708 }
709 }
710 }
711 else {
712 rv.emplace(str, child->valuedouble);
713 }
714 }
715 }
716 }
717 }
718 ::cJSON_Delete(json);
719 }
720 else
721 {
722 std::string err = ::cJSON_GetErrorPtr();
723 err = err.substr(0, 16);
724 err = "AISim: Can't parse Aircraft data around: "+err;
725 SG_LOG(SG_FLIGHT, SG_ALERT, err );
726 }
727 return rv;
728}
729
730bool
731FGAISim::load(std::string path)
732{
733 std::ifstream file(path);
734 std::string jsonString;
735
736 file.seekg(0, std::ios::end);
737 jsonString.reserve(file.tellg());
738 file.seekg(0, std::ios::beg);
739
740 jsonString.assign((std::istreambuf_iterator<char>(file)),
741 std::istreambuf_iterator<char>());
742
743 jsonMap data = jsonParse(jsonString.c_str());
744
745 Sw = data["Sw"];
746 cbar = data["cbar"];
747 span = data["b"];
748
749 mass = data["mass"]/AISIM_G;
750
751 I[XX] = data["Ixx"];
752 I[YY] = data["Iyy"];
753 I[ZZ] = data["Izz"];
754 I[XZ] = data["Ixz"];
755
756 // Center of gravity, gears and engines are in the structural frame
757 // positions are in inches
758 // 0: X-axis is directed afterwards,
759 // 1: Y-axis is directed towards the right,
760 // 2: Z-axis is directed upwards
761 //
762 // c.g. is relative to the aero reference point
763 cg[X] = data["cg[0]"];
764 cg[Y] = data["cg[1]"];
765 cg[Z] = data["cg[2]"];
766 struct_to_body(cg);
767
768 // Gear ground contact points relative to center of gravity.
769 no_contacts = 0;
770 do
771 {
772 size_t i = no_contacts;
773 std::string gearstr = "gear[" + std::to_string(i) + "]";
774
775 float spring = data[gearstr + "/spring"];
776 if (!spring) break;
777
778 contact_pos[i][X] = data[gearstr + "/pos[0]"];
779 contact_pos[i][Y] = data[gearstr + "/pos[1]"];
780 contact_pos[i][Z] = data[gearstr + "/pos[2]"];
781 struct_to_body(contact_pos[i]);
782
783 contact_spring[i] = -data[gearstr + "/spring"];
784 contact_damp[i] = -data[gearstr + "/damp"];
785#if 0
786 printf("%lu: pos: % 3.2f, % 3.2f, % 3.2f, spring: % 7.1f, damp: % 7.1f\n", i, contact_pos[i][X], contact_pos[i][Y], contact_pos[i][Z], contact_spring[i], contact_damp[i]);
787#endif
788 }
789 while (++no_contacts < AISIM_MAX);
790
791 /* Thuster / propulsion locations relative to c.g. */
792 no_engines = 0;
793 do
794 {
795 size_t i = no_engines;
796 std::string engstr = "engine[" + std::to_string(i) + "]";
797
798 float FTmax = data[engstr + "/FT_max"];
799 if (!FTmax) break;
800
801 aiVec3 pos;
802 pos[0] = data[engstr + "/pos[0]"];
803 pos[1] = data[engstr + "/pos[1]"];
804 pos[2] = data[engstr + "/pos[2]"];
805 struct_to_body(pos);
806
807 // Thruster orientation is in the following sequence: pitch, roll, yaw
808 aiVec3 orientation(data[engstr + "/dir[1]"], // roll (degrees)
809 data[engstr + "/dir[0]"], // pitch (degrees)
810 data[engstr + "/dir[2]"]); // yaw (degrees)
811 orientation *= SG_DEGREES_TO_RADIANS;
812
813 float len = simd4::normalize(orientation);
814 aiMtx4 mWind2Body = simd4x4::rotation_matrix(len, orientation);
815 aiVec3 dir = mWind2Body*aiVec3(1.0f, 0.0f, 0.0f);
816 aiVec3 rot = simd4::cross(pos, dir);
817
818 float rho = 0.002379f;
819// float MTmax = data[engstr + "/MT_max"];
820
821 float max_rpm = data[engstr + "/rpm_max"];
822 if (max_rpm == 0.0f) {
823 n2[i] = 1.0f;
824 }
825 else
826 {
827 n2[i] = max_rpm/60.0f;
828 n2[i] *= n2[i];
829 }
830
831 FTmax /= (rho*n2[i]);
832// MTmax /= rho / max_rpm;
833
834 FT[i] = dir * FTmax;
835 MT[i] = rot * FT[i]; // + dir * MTmax;
836 }
837 while(++no_engines < AISIM_MAX);
838
839 float de_max = data["de_max"]*SG_DEGREES_TO_RADIANS;
840 float dr_max = data["dr_max"]*SG_DEGREES_TO_RADIANS;
841 float da_max = data["da_max"]*SG_DEGREES_TO_RADIANS;
842 float df_max = data["df_max"]*SG_DEGREES_TO_RADIANS;
843
844 /* aerodynamic coefficients */
845 CLmin = data["CLmin"];
846 CLa = data["CLa"];
847 CLadot = data["CLadot"];
848 CLq = data["CLq"];
849 CLdf_n = data["CLdf"]*df_max;
850
851 CDmin = data["CDmin"];
852 CDa = data["CDa"];
853 CDb = data["CDb"];
854 CDi = data["CDi"];
855 CDdf_n = data["CDdf"]*df_max;
856
857 CYb = data["CYb"];
858 CYp = data["CYp"];
859 CYr = data["CYr"];
860 CYdr_n = data["CYdr"]*dr_max;
861
862 Clb = data["Clb"];
863 Clp = data["Clp"];
864 Clr = data["Clr"];
865 Clda_n = data["Clda"]*da_max;
866 Cldr_n = data["Cldr"]*dr_max;
867
868 Cma = data["Cma"];
869 Cmadot = data["Cmadot"];
870 Cmq = data["Cmq"];
871 Cmde_n = data["Cmde"]*de_max;
872 Cmdf_n = data["Cmdf"]*df_max;
873
874 Cnb = data["Cnb "];
875 Cnp = data["Cnp"];
876 Cnr = data["Cnr"];
877 Cnda_n = data["Cnda"]*da_max;
878 Cndr_n = data["Cndr"]*dr_max;
879
880 return true;
881}
882
883
884// Register the subsystem.
885#if 0
886SGSubsystemMgr::Registrant<FGAISim> registrantFGAISim;
887#endif
double altitude
Definition ADA.cxx:46
#define throttle
Definition ADA.cxx:130
double lat_geoc
Definition ADA.cxx:44
#define MAX_ALT
Definition AISim.cpp:516
#define INCHES_TO_FEET
Definition AISim.cpp:52
#define p(x)
#define i(x)
std::string fgGetString(const char *name, const char *defaultValue)
Get a string value for a property.
Definition fg_props.cxx:556
FGGlobals * globals
Definition globals.cxx:142
#define Z
Definition js_demo.cxx:10
#define R
#define W
#define Y
SGPropertyNode * fgGetNode(const char *path, bool create)
Get a property node.
Definition proptest.cpp:27
float fgGetFloat(const char *name, float defaultValue)
Get a float value for a property.
Definition proptest.cpp:29