Line data Source code
1 : /*
2 : This program is free software: you can redistribute it and/or modify
3 : it under the terms of the GNU General Public License as published by
4 : the Free Software Foundation, either version 3 of the License, or
5 : (at your option) any later version.
6 :
7 : This program is distributed in the hope that it will be useful,
8 : but WITHOUT ANY WARRANTY; without even the implied warranty of
9 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 : GNU General Public License for more details.
11 :
12 : You should have received a copy of the GNU General Public License
13 : along with this program. If not, see <http://www.gnu.org/licenses/>.
14 : */
15 : /*
16 : parent class for aircraft simulators
17 : */
18 :
19 : #include "SIM_Aircraft.h"
20 :
21 : #include <stdio.h>
22 : #include <sys/time.h>
23 : #include <unistd.h>
24 :
25 : #include <GCS_MAVLink/GCS.h>
26 : #include <AP_Logger/AP_Logger.h>
27 : #include <AP_Param/AP_Param.h>
28 : #include <AP_Declination/AP_Declination.h>
29 : #include <AP_Terrain/AP_Terrain.h>
30 : #include <AP_Scheduler/AP_Scheduler.h>
31 : #include <AP_BoardConfig/AP_BoardConfig.h>
32 : #include <AP_JSON/AP_JSON.h>
33 : #include <AP_Filesystem/AP_Filesystem.h>
34 : #include <AP_HAL_SITL/HAL_SITL_Class.h>
35 : #include <AP_Vehicle/AP_Vehicle_Type.h>
36 :
37 : using namespace SITL;
38 :
39 : extern const AP_HAL::HAL& hal;
40 :
41 : // the SITL HAL can add information about pausing the simulation and its effect on the UART. Not present when we're compiling for simulation-on-hardware
42 : #if CONFIG_HAL_BOARD == HAL_BOARD_SITL
43 : extern const HAL_SITL& hal_sitl;
44 : #endif
45 :
46 : Aircraft *Aircraft::instances[MAX_SIM_INSTANCES];
47 :
48 : /*
49 : parent class for all simulator types
50 : */
51 :
52 35615 : Aircraft::Aircraft(const char *frame_str)
53 : {
54 : // make the SIM_* variables available to simulator backends
55 2095 : sitl = AP::sitl();
56 :
57 2095 : set_speedup(1.0f);
58 :
59 2095 : last_wall_time_us = get_wall_time_us();
60 :
61 : // allow for orientation settings, such as with tailsitters
62 : enum ap_var_type ptype;
63 2095 : ahrs_orientation = (AP_Int8 *)AP_Param::find("AHRS_ORIENTATION", &ptype);
64 :
65 : // ahrs_orientation->get() returns ROTATION_NONE here, regardless of the actual value
66 2095 : enum Rotation imu_rotation = ahrs_orientation?(enum Rotation)ahrs_orientation->get():ROTATION_NONE;
67 2095 : last_imu_rotation = imu_rotation;
68 : // sitl is null if running example program
69 2095 : if (sitl) {
70 2094 : sitl->ahrs_rotation.from_rotation(imu_rotation);
71 2094 : sitl->ahrs_rotation_inv = sitl->ahrs_rotation.transposed();
72 : }
73 :
74 : // init rangefinder array to NaN to signify no data
75 23045 : for (uint8_t i = 0; i < ARRAY_SIZE(rangefinder_m); i++){
76 20950 : rangefinder_m[i] = nanf("");
77 : }
78 2095 : }
79 :
80 2094 : void Aircraft::set_start_location(const Location &start_loc, const float start_yaw)
81 : {
82 2094 : home = start_loc;
83 2094 : origin = home;
84 2094 : position.xy().zero();
85 2094 : home_yaw = start_yaw;
86 2094 : home_is_set = true;
87 :
88 2094 : ::printf("Home: %f %f alt=%fm hdg=%f\n",
89 2094 : home.lat*1e-7,
90 2094 : home.lng*1e-7,
91 2094 : home.alt*0.01,
92 2094 : home_yaw);
93 :
94 2094 : location = home;
95 2094 : ground_level = home.alt * 0.01f;
96 :
97 : #if 0
98 : // useful test for home position being very different from origin
99 : home.offset(-3000*1000, 1800*1000);
100 : #endif
101 :
102 2094 : dcm.from_euler(0.0f, 0.0f, radians(home_yaw));
103 2094 : }
104 :
105 : /*
106 : return difference in altitude between home position and current loc
107 : */
108 612398459 : float Aircraft::ground_height_difference() const
109 : {
110 : #if AP_TERRAIN_AVAILABLE
111 612398459 : AP_Terrain *terrain = AP::terrain();
112 : float h1, h2;
113 612398459 : if (sitl &&
114 611882279 : terrain != nullptr &&
115 1215333783 : sitl->terrain_enable &&
116 1828248422 : terrain->height_amsl(home, h1, false) &&
117 548231985 : terrain->height_amsl(location, h2, false)) {
118 547023873 : h2 += local_ground_level;
119 547023873 : return h2 - h1;
120 : }
121 : #endif
122 65374586 : return local_ground_level;
123 : }
124 :
125 283388 : float Aircraft::ambient_outside_temperature_degC() const
126 : {
127 : // FIXME: stop applying autopilot warming to this value
128 283388 : return baro_temperature_degC();
129 : }
130 :
131 : // the ambient temperature for the autopilot baro sensors, *not* the
132 : // ambient temperature for the outside of the vehicle. This
133 : // temperature is adjusted for things like the simulated board heating
134 : // up
135 283388 : float Aircraft::baro_temperature_degC() const
136 : {
137 : // FIXME: AP_Baro_SITL should be getting temperature from the
138 : // simulated aircraft, not the other way around!
139 : #if !APM_BUILD_TYPE(APM_BUILD_AP_Periph) // Periph does not instantiate Baro
140 283388 : return AP::baro().get_temperature();
141 : #endif
142 : return 25.0;
143 : }
144 :
145 : // returns the expected ambient pressure for the vehicle. So pressure
146 : // drops preceived by the sensors due to airflow should not be
147 : // included in this number.
148 3208 : float Aircraft::ambient_outside_pressure_Pascal() const
149 : {
150 : // FIXME: this includes airflow-related things
151 3208 : return AP::baro().get_pressure();
152 : }
153 :
154 : /*
155 : return current height above ground level (metres)
156 : */
157 550973446 : float Aircraft::hagl() const
158 : {
159 550973446 : return (-position.z) + home.alt * 0.01f - ground_level - frame_height - ground_height_difference();
160 : }
161 :
162 : /*
163 : return true if we are on the ground
164 : */
165 547822322 : bool Aircraft::on_ground() const
166 : {
167 547822322 : return hagl() <= 0.001f; // prevent bouncing around ground
168 : }
169 :
170 : /*
171 : update location from position
172 : */
173 187275482 : void Aircraft::update_position(void)
174 : {
175 187275482 : location = origin;
176 187275482 : location.offset(position.x, position.y);
177 :
178 187275482 : location.alt = static_cast<int32_t>(home.alt - position.z * 100.0f);
179 :
180 : #if 0
181 : Vector3d pos_home = position;
182 : pos_home.xy() += home.get_distance_NE_double(origin);
183 :
184 : // logging of raw sitl data
185 : Vector3f accel_ef = dcm * accel_body;
186 : // @LoggerMessage: SITL
187 : // @Description: Simulation data
188 : // @Field: TimeUS: Time since system startup
189 : // @Field: VN: Velocity - North component
190 : // @Field: VE: Velocity - East component
191 : // @Field: VD: Velocity - Down component
192 : // @Field: AN: Acceleration - North component
193 : // @Field: AE: Acceleration - East component
194 : // @Field: AD: Acceleration - Down component
195 : // @Field: PN: Position - North component
196 : // @Field: PE: Position - East component
197 : // @Field: PD: Position - Down component
198 : AP::logger().WriteStreaming("SITL", "TimeUS,VN,VE,VD,AN,AE,AD,PN,PE,PD", "Qfffffffff",
199 : AP_HAL::micros64(),
200 : velocity_ef.x, velocity_ef.y, velocity_ef.z,
201 : accel_ef.x, accel_ef.y, accel_ef.z,
202 : pos_home.x, pos_home.y, pos_home.z);
203 : #endif
204 :
205 187275482 : if (!disable_origin_movement) {
206 186153551 : uint32_t now = AP_HAL::millis();
207 186153551 : if (now - last_one_hz_ms >= 1000) {
208 : // shift origin of position at 1Hz to current location
209 : // this prevents spherical errors building up in the GPS data
210 154065 : last_one_hz_ms = now;
211 154065 : Vector2d diffNE = origin.get_distance_NE_double(location);
212 154065 : position.xy() -= diffNE;
213 154065 : smoothing.position.xy() -= diffNE;
214 154065 : origin.lat = location.lat;
215 154065 : origin.lng = location.lng;
216 : }
217 : }
218 187275482 : }
219 :
220 : /*
221 : update body magnetic field from position and rotation
222 : */
223 187275482 : void Aircraft::update_mag_field_bf()
224 : {
225 : // get the magnetic field intensity and orientation
226 : float intensity;
227 : float declination;
228 : float inclination;
229 187275482 : AP_Declination::get_mag_field_ef(location.lat * 1e-7f, location.lng * 1e-7f, intensity, declination, inclination);
230 :
231 : // create a field vector and rotate to the required orientation
232 187275482 : Vector3f mag_ef(1e3f * intensity, 0.0f, 0.0f);
233 187275482 : Matrix3f R;
234 187275482 : R.from_euler(0.0f, -radians(inclination), radians(declination));
235 187275482 : mag_ef = R * mag_ef;
236 :
237 : // calculate frame height above ground
238 187275482 : const float frame_height_agl = fmaxf((-position.z) + home.alt * 0.01f - ground_level, 0.0f);
239 :
240 187275482 : if (!sitl) {
241 : // running example program
242 0 : return;
243 : }
244 :
245 : // calculate scaling factor that varies from 1 at ground level to 1/8 at sitl->mag_anomaly_hgt
246 : // Assume magnetic anomaly strength scales with 1/R**3
247 187275482 : float anomaly_scaler = (sitl->mag_anomaly_hgt / (frame_height_agl + sitl->mag_anomaly_hgt));
248 187275482 : anomaly_scaler = anomaly_scaler * anomaly_scaler * anomaly_scaler;
249 :
250 : // add scaled anomaly to earth field
251 187275482 : mag_ef += sitl->mag_anomaly_ned.get() * anomaly_scaler;
252 :
253 : // Rotate into body frame
254 187275482 : mag_bf = dcm.transposed() * mag_ef;
255 :
256 : // add motor interference
257 187275482 : mag_bf += sitl->mag_mot.get() * battery_current;
258 : }
259 :
260 : /* advance time by deltat in seconds */
261 187275482 : void Aircraft::time_advance()
262 : {
263 : // we only advance time if it hasn't been advanced already by the
264 : // backend
265 187275482 : if (last_time_us == time_now_us) {
266 187275482 : time_now_us += frame_time_us;
267 : }
268 187275482 : last_time_us = time_now_us;
269 187275482 : if (use_time_sync) {
270 187275482 : sync_frame_time();
271 : }
272 187275482 : }
273 :
274 : /* setup the frame step time */
275 6399 : void Aircraft::setup_frame_time(float new_rate, float new_speedup)
276 : {
277 6399 : rate_hz = new_rate;
278 6399 : target_speedup = new_speedup;
279 6399 : frame_time_us = uint64_t(1.0e6f/rate_hz);
280 :
281 6399 : last_wall_time_us = get_wall_time_us();
282 6399 : }
283 :
284 : /* adjust frame_time calculation */
285 169142737 : void Aircraft::adjust_frame_time(float new_rate)
286 : {
287 169142737 : frame_time_us = uint64_t(1.0e6f/new_rate);
288 169142737 : rate_hz = new_rate;
289 169142737 : }
290 :
291 : /*
292 : try to synchronise simulation time with wall clock time, taking
293 : into account desired speedup
294 : This tries to take account of possible granularity of
295 : get_wall_time_us() so it works reasonably well on windows
296 : */
297 187275482 : void Aircraft::sync_frame_time(void)
298 : {
299 187275482 : frame_counter++;
300 187275482 : uint64_t now = get_wall_time_us();
301 187275482 : uint64_t dt_us = now - last_wall_time_us;
302 :
303 187275482 : const float target_dt_us = 1.0e6/(rate_hz*target_speedup);
304 :
305 : // accumulate sleep debt if we're running too fast
306 187275482 : sleep_debt_us += target_dt_us - dt_us;
307 :
308 187275482 : if (sleep_debt_us < -1.0e5) {
309 : // don't let a large negative debt build up
310 138774552 : sleep_debt_us = -1.0e5;
311 : }
312 187275482 : if (sleep_debt_us > min_sleep_time) {
313 : // sleep if we have built up a debt of min_sleep_tim
314 : #if CONFIG_HAL_BOARD == HAL_BOARD_SITL
315 209981 : usleep(sleep_debt_us);
316 : #elif CONFIG_HAL_BOARD == HAL_BOARD_CHIBIOS
317 : hal.scheduler->delay_microseconds(sleep_debt_us);
318 : #else
319 : // ??
320 : #endif
321 209981 : sleep_debt_us -= (get_wall_time_us() - now);
322 : }
323 187275482 : last_wall_time_us = get_wall_time_us();
324 :
325 187275482 : uint32_t now_ms = last_wall_time_us / 1000ULL;
326 187275482 : float dt_wall = (now_ms - last_fps_report_ms) * 0.001;
327 187275482 : if (dt_wall > 0.01) { // 0.01s average
328 534914 : achieved_rate_hz = (frame_counter - last_frame_count) / dt_wall;
329 : #if 0
330 : ::printf("Rate: target:%.1f achieved:%.1f speedup %.1f/%.1f\n",
331 : rate_hz*target_speedup, achieved_rate_hz,
332 : achieved_rate_hz/rate_hz, target_speedup);
333 : #endif
334 534914 : last_frame_count = frame_counter;
335 534914 : last_fps_report_ms = now_ms;
336 : }
337 187275482 : }
338 :
339 : /* add noise based on throttle level (from 0..1) */
340 64098694 : void Aircraft::add_noise(float throttle)
341 : {
342 64098694 : gyro += Vector3f(rand_normal(0, 1),
343 64098694 : rand_normal(0, 1),
344 128197388 : rand_normal(0, 1)) * gyro_noise * fabsf(throttle);
345 64098694 : accel_body += Vector3f(rand_normal(0, 1),
346 64098694 : rand_normal(0, 1),
347 128197388 : rand_normal(0, 1)) * accel_noise * fabsf(throttle);
348 64098694 : }
349 :
350 : /*
351 : normal distribution random numbers
352 : See
353 : http://en.literateprograms.org/index.php?title=Special:DownloadCode/Box-Muller_transform_%28C%29&oldid=7011
354 : */
355 384686853 : double Aircraft::rand_normal(double mean, double stddev)
356 : {
357 : static double n2 = 0.0;
358 : static int n2_cached = 0;
359 384686853 : if (!n2_cached) {
360 : double x, y, r;
361 : do
362 : {
363 244826974 : x = 2.0 * rand()/RAND_MAX - 1;
364 244826974 : y = 2.0 * rand()/RAND_MAX - 1;
365 244826974 : r = x*x + y*y;
366 244826974 : } while (is_zero(r) || r > 1.0);
367 192343429 : const double d = sqrt(-2.0 * log(r)/r);
368 192343429 : const double n1 = x * d;
369 192343429 : n2 = y * d;
370 192343429 : const double result = n1 * stddev + mean;
371 192343429 : n2_cached = 1;
372 192343429 : return result;
373 : } else {
374 192343424 : n2_cached = 0;
375 192343424 : return n2 * stddev + mean;
376 : }
377 : }
378 :
379 :
380 :
381 :
382 : /*
383 : fill a sitl_fdm structure from the simulator state
384 : */
385 187275482 : void Aircraft::fill_fdm(struct sitl_fdm &fdm)
386 : {
387 187275482 : bool is_smoothed = false;
388 187275482 : if (use_smoothing) {
389 159454654 : smooth_sensors();
390 159454654 : is_smoothed = true;
391 : }
392 187275482 : fdm.timestamp_us = time_now_us;
393 : // keep track of the number of frames processed so that the IMUs can follow
394 187275482 : if (flightaxis_sync_imus_to_frames) {
395 0 : fdm.flightaxis_imu_frame_num++;
396 : }
397 :
398 187275482 : if (fdm.home.lat == 0 && fdm.home.lng == 0) {
399 : // initialise home
400 2094 : fdm.home = home;
401 : }
402 187275482 : fdm.is_lock_step_scheduled = lock_step_scheduled;
403 187275482 : fdm.latitude = location.lat * 1.0e-7;
404 187275482 : fdm.longitude = location.lng * 1.0e-7;
405 187275482 : fdm.altitude = location.alt * 1.0e-2;
406 187275482 : fdm.heading = degrees(atan2f(velocity_ef.y, velocity_ef.x));
407 187275482 : fdm.speedN = velocity_ef.x;
408 187275482 : fdm.speedE = velocity_ef.y;
409 187275482 : fdm.speedD = velocity_ef.z;
410 187275482 : fdm.xAccel = accel_body.x;
411 187275482 : fdm.yAccel = accel_body.y;
412 187275482 : fdm.zAccel = accel_body.z;
413 187275482 : fdm.rollRate = degrees(gyro.x);
414 187275482 : fdm.pitchRate = degrees(gyro.y);
415 187275482 : fdm.yawRate = degrees(gyro.z);
416 : float r, p, y;
417 187275482 : dcm.to_euler(&r, &p, &y);
418 187275482 : fdm.rollDeg = degrees(r);
419 187275482 : fdm.pitchDeg = degrees(p);
420 187275482 : fdm.yawDeg = degrees(y);
421 187275482 : fdm.quaternion.from_rotation_matrix(dcm);
422 187275482 : fdm.airspeed = airspeed_pitot;
423 187275482 : fdm.velocity_air_bf = velocity_air_bf;
424 187275482 : fdm.battery_voltage = battery_voltage;
425 187275482 : fdm.battery_current = battery_current;
426 187275482 : fdm.motor_mask = motor_mask | sitl->vibe_motor_mask;
427 187275482 : memcpy(fdm.rpm, rpm, sizeof(fdm.rpm));
428 187275482 : fdm.rcin_chan_count = rcin_chan_count;
429 187275482 : fdm.range = rangefinder_range();
430 187275482 : memcpy(fdm.rcin, rcin, rcin_chan_count * sizeof(float));
431 187275482 : fdm.bodyMagField = mag_bf;
432 :
433 187275482 : fdm.bodyMagField.rotate(sitl->imu_orientation);
434 :
435 : // copy laser scanner results
436 187275482 : fdm.scanner.points = scanner.points;
437 187275482 : fdm.scanner.ranges = scanner.ranges;
438 :
439 : // copy rangefinder
440 187275482 : memcpy(fdm.rangefinder_m, rangefinder_m, sizeof(fdm.rangefinder_m));
441 :
442 187275482 : fdm.wind_vane_apparent.direction = wind_vane_apparent.direction;
443 187275482 : fdm.wind_vane_apparent.speed = wind_vane_apparent.speed;
444 :
445 187275482 : fdm.wind_ef = wind_ef;
446 :
447 187275482 : if (is_smoothed) {
448 159454654 : fdm.xAccel = smoothing.accel_body.x;
449 159454654 : fdm.yAccel = smoothing.accel_body.y;
450 159454654 : fdm.zAccel = smoothing.accel_body.z;
451 159454654 : fdm.rollRate = degrees(smoothing.gyro.x);
452 159454654 : fdm.pitchRate = degrees(smoothing.gyro.y);
453 159454654 : fdm.yawRate = degrees(smoothing.gyro.z);
454 159454654 : fdm.speedN = smoothing.velocity_ef.x;
455 159454654 : fdm.speedE = smoothing.velocity_ef.y;
456 159454654 : fdm.speedD = smoothing.velocity_ef.z;
457 159454654 : fdm.latitude = smoothing.location.lat * 1.0e-7;
458 159454654 : fdm.longitude = smoothing.location.lng * 1.0e-7;
459 159454654 : fdm.altitude = smoothing.location.alt * 1.0e-2;
460 : }
461 :
462 :
463 187275482 : if (ahrs_orientation != nullptr) {
464 187275482 : enum Rotation imu_rotation = (enum Rotation)ahrs_orientation->get();
465 187275482 : if (imu_rotation != last_imu_rotation) {
466 2 : sitl->ahrs_rotation.from_rotation(imu_rotation);
467 2 : sitl->ahrs_rotation_inv = sitl->ahrs_rotation.transposed();
468 2 : last_imu_rotation = imu_rotation;
469 : }
470 187275482 : if (imu_rotation != ROTATION_NONE) {
471 2905 : Matrix3f m = dcm;
472 2905 : m = m * sitl->ahrs_rotation_inv;
473 :
474 2905 : m.to_euler(&r, &p, &y);
475 2905 : fdm.rollDeg = degrees(r);
476 2905 : fdm.pitchDeg = degrees(p);
477 2905 : fdm.yawDeg = degrees(y);
478 2905 : fdm.quaternion.from_rotation_matrix(m);
479 : }
480 : }
481 :
482 : // in the first call here, if a speedup option is specified, overwrite it
483 187275482 : if (is_equal(last_speedup, -1.0f) && !is_equal(get_speedup(), 1.0f)) {
484 0 : sitl->speedup.set(get_speedup());
485 : }
486 :
487 187275482 : if (!is_equal(last_speedup, float(sitl->speedup)) && sitl->speedup > 0) {
488 2210 : set_speedup(sitl->speedup);
489 2210 : last_speedup = sitl->speedup;
490 : }
491 :
492 : #if HAL_LOGGING_ENABLED
493 : #if CONFIG_HAL_BOARD == HAL_BOARD_SITL
494 : // the SITL HAL can add information about pausing the simulation
495 : // and its effect on the UART. Not present when we're compiling
496 : // for simulation-on-hardware
497 187275482 : const uint32_t full_count = hal_sitl.get_uart_output_full_queue_count();
498 : #else
499 : const uint32_t full_count = 0;
500 : #endif
501 : // for EKF comparison log relhome pos and velocity at loop rate
502 : static uint16_t last_ticks;
503 : // FIXME: stop using AP_Scheduler here! It's from "the other side"
504 187275482 : const auto *scheduler = AP_Scheduler::get_singleton();
505 187275482 : if (scheduler == nullptr) {
506 : // not instantiated in examples
507 0 : return;
508 : }
509 187275482 : uint16_t ticks = scheduler->ticks();
510 187275482 : if (last_ticks != ticks) {
511 41686253 : last_ticks = ticks;
512 : // @LoggerMessage: SIM2
513 : // @Description: Additional simulator state
514 : // @Field: TimeUS: Time since system startup
515 : // @Field: PN: North position from home
516 : // @Field: PE: East position from home
517 : // @Field: PD: Down position from home
518 : // @Field: VN: Velocity north
519 : // @Field: VE: Velocity east
520 : // @Field: VD: Velocity down
521 : // @Field: As: Airspeed
522 : // @Field: ASpdU: Achieved simulation speedup value
523 : // @Field: UFC: Number of times simulation paused for serial0 output
524 41686253 : Vector3d pos = get_position_relhome();
525 41686253 : Vector3f vel = get_velocity_ef();
526 83372506 : AP::logger().WriteStreaming(
527 : "SIM2",
528 : "TimeUS,PN,PE,PD,VN,VE,VD,As,ASpdU,UFC",
529 : "QdddfffffI",
530 : AP_HAL::micros64(),
531 : pos.x, pos.y, pos.z,
532 41686253 : vel.x, vel.y, vel.z,
533 41686253 : airspeed_pitot,
534 41686253 : achieved_rate_hz/rate_hz,
535 : full_count
536 : );
537 : }
538 : #endif
539 : }
540 :
541 : /*
542 : rover and copter have special handling for horizontal rangefinders
543 : as distance to obstacles - this takes effect for yaw-only
544 : orientations
545 : */
546 : #define SITL_RANGEFINDER_AS_OBJECT_SENSOR (APM_BUILD_TYPE(APM_BUILD_ArduCopter) || APM_BUILD_TYPE(APM_BUILD_Rover))
547 : #define SITL_RANGEFINDER_IS_YAW_ONLY(orientation) (orientation <= ROTATION_YAW_315)
548 :
549 : // returns perpendicular height to surface rangefinder is bouncing off
550 1685241307 : float Aircraft::perpendicular_distance_to_rangefinder_surface() const
551 : {
552 : #if SITL_RANGEFINDER_AS_OBJECT_SENSOR
553 1024972219 : const auto orientation = (Rotation)sitl->sonar_rot.get();
554 1024972219 : if (SITL_RANGEFINDER_IS_YAW_ONLY(orientation)) {
555 : // assume these are avoidance sensors
556 632825 : return sitl->measure_distance_at_angle_bf(location, sitl->sonar_rot.get()*45);
557 : }
558 : #endif
559 :
560 : // default is ground sensing rangefinders
561 1684608482 : return sitl->state.height_agl;
562 : }
563 :
564 1750708325 : float Aircraft::rangefinder_range() const
565 : {
566 1750708325 : float altitude = perpendicular_distance_to_rangefinder_surface();
567 :
568 : // sensor position offset in body frame
569 1750708325 : const Vector3f relPosSensorBF = sitl->rngfnd_pos_offset;
570 :
571 : // n.b. the following code is assuming rotation-pitch-270:
572 : // adjust altitude for position of the sensor on the vehicle if position offset is non-zero
573 1750708325 : if (!relPosSensorBF.is_zero()) {
574 : // get a rotation matrix following DCM conventions (body to earth)
575 0 : Matrix3f rotmat;
576 0 : sitl->state.quaternion.rotation_matrix(rotmat);
577 : // rotate the offset into earth frame
578 0 : const Vector3f relPosSensorEF = rotmat * relPosSensorBF;
579 : // correct the altitude at the sensor
580 0 : altitude -= relPosSensorEF.z;
581 : }
582 :
583 1750708325 : altitude += sitl->sonar_offset;
584 :
585 1750708325 : const auto orientation = (Rotation)sitl->sonar_rot.get();
586 : #if SITL_RANGEFINDER_AS_OBJECT_SENSOR
587 :
588 1024972219 : float roll = sitl->state.rollDeg;
589 1024972219 : float pitch = sitl->state.pitchDeg;
590 :
591 1024972219 : if (roll > 0) {
592 278701392 : roll -= rangefinder_beam_width();
593 278701392 : if (roll < 0) {
594 0 : roll = 0;
595 : }
596 : } else {
597 746270827 : roll += rangefinder_beam_width();
598 746270827 : if (roll > 0) {
599 0 : roll = 0;
600 : }
601 : }
602 1024972219 : if (pitch > 0) {
603 237901391 : pitch -= rangefinder_beam_width();
604 237901391 : if (pitch < 0) {
605 0 : pitch = 0;
606 : }
607 : } else {
608 787070828 : pitch += rangefinder_beam_width();
609 787070828 : if (pitch > 0) {
610 0 : pitch = 0;
611 : }
612 : }
613 :
614 : /*
615 : rover and copter using SITL rangefinders as obstacle sensors
616 : */
617 1024972219 : if (SITL_RANGEFINDER_IS_YAW_ONLY(orientation)) {
618 632825 : if (fabs(roll) >= 90.0 || fabs(pitch) >= 90.0) {
619 : // not going to hit the ground....
620 0 : return INFINITY;
621 : }
622 632825 : altitude /= cosf(radians(roll)) * cosf(radians(pitch));
623 : } else
624 : #endif
625 : {
626 : // adjust for rotation based on orientation of the sensor
627 1750075500 : Matrix3f rotmat;
628 1750075500 : sitl->state.quaternion.rotation_matrix(rotmat);
629 1750075500 : Vector3f v{1, 0, 0};
630 1750075500 : v.rotate(orientation);
631 1750075500 : v = rotmat * v;
632 :
633 1750075500 : if (!is_positive(v.z)) {
634 17522462 : return INFINITY;
635 : }
636 1737480126 : altitude /= v.z;
637 :
638 : // this is awful, but there are drawbacks to assuming an
639 : // infinite plane. If we don't do this here then we end up
640 : // with a ridiculous rangefinder range, and that can cause
641 : // floating point exceptions when we return a distance in cm
642 : // from the AP_RangeFinder_SITL.
643 1737480126 : if (altitude > 100000) {
644 4927088 : return INFINITY;
645 : }
646 : }
647 :
648 : // Add some noise on reading
649 1733185863 : altitude += sitl->sonar_noise * rand_float();
650 :
651 : // our starting positions can disagree with the terrain database:
652 1733185863 : if (altitude < 0) {
653 62931252 : altitude = 0;
654 : }
655 :
656 1733185863 : return altitude;
657 : }
658 :
659 : #if defined(__CYGWIN__) || defined(__CYGWIN64__)
660 : extern "C" { uint32_t timeGetTime(); }
661 : #endif
662 :
663 : // potentially replace this with a call to AP_HAL::Util::get_hw_rtc
664 374769439 : uint64_t Aircraft::get_wall_time_us() const
665 : {
666 : #if defined(__CYGWIN__) || defined(__CYGWIN64__)
667 : static uint32_t tPrev;
668 : static uint64_t last_ret_us;
669 : if (tPrev == 0) {
670 : tPrev = timeGetTime();
671 : return 0;
672 : }
673 : uint32_t now = timeGetTime();
674 : last_ret_us += (uint64_t)((now - tPrev)*1000UL);
675 : tPrev = now;
676 : return last_ret_us;
677 : #elif CONFIG_HAL_BOARD == HAL_BOARD_SITL
678 : struct timespec ts;
679 374769439 : clock_gettime(CLOCK_MONOTONIC, &ts);
680 374769439 : return uint64_t(ts.tv_sec * 1000000ULL + ts.tv_nsec / 1000ULL);
681 : #else
682 : return AP_HAL::micros64();
683 : #endif
684 : }
685 :
686 : /*
687 : set simulation speedup
688 : */
689 6399 : void Aircraft::set_speedup(float speedup)
690 : {
691 6399 : setup_frame_time(rate_hz, speedup);
692 6399 : }
693 :
694 187275482 : void Aircraft::update_home()
695 : {
696 187275482 : if (!home_is_set) {
697 0 : if (sitl == nullptr) {
698 0 : return;
699 : }
700 : const Location loc{
701 0 : int32_t(sitl->opos.lat.get() * 1.0e7),
702 0 : int32_t(sitl->opos.lng.get() * 1.0e7),
703 0 : int32_t(sitl->opos.alt.get() * 1.0e2),
704 : Location::AltFrame::ABSOLUTE
705 0 : };
706 0 : set_start_location(loc, sitl->opos.hdg.get());
707 : }
708 : }
709 :
710 187275482 : void Aircraft::update_model(const struct sitl_input &input)
711 : {
712 187275482 : local_ground_level = 0.0f;
713 187275482 : if (sitl != nullptr) {
714 187275482 : update(input);
715 : } else {
716 0 : time_advance();
717 : }
718 187275482 : }
719 :
720 : /*
721 : update the simulation attitude and relative position
722 : */
723 169080479 : void Aircraft::update_dynamics(const Vector3f &rot_accel)
724 : {
725 169080479 : WITH_SEMAPHORE(pose_sem);
726 :
727 169080479 : const float delta_time = frame_time_us * 1.0e-6f;
728 :
729 : // update eas2tas and air density
730 169080479 : eas2tas = AP_Baro::get_EAS2TAS_for_alt_amsl(location.alt*0.01);
731 169080479 : air_density = AP_Baro::get_air_density_for_alt_amsl(location.alt*0.01);
732 :
733 : // update rotational rates in body frame
734 169080479 : gyro += rot_accel * delta_time;
735 :
736 169080479 : gyro.x = constrain_float(gyro.x, -radians(2000.0f), radians(2000.0f));
737 169080479 : gyro.y = constrain_float(gyro.y, -radians(2000.0f), radians(2000.0f));
738 169080479 : gyro.z = constrain_float(gyro.z, -radians(2000.0f), radians(2000.0f));
739 :
740 : // limit body accel to 64G
741 169080479 : const float accel_limit = 64*GRAVITY_MSS;
742 169080479 : accel_body.x = constrain_float(accel_body.x, -accel_limit, accel_limit);
743 169080479 : accel_body.y = constrain_float(accel_body.y, -accel_limit, accel_limit);
744 169080479 : accel_body.z = constrain_float(accel_body.z, -accel_limit, accel_limit);
745 :
746 : // update attitude
747 169080479 : dcm.rotate(gyro * delta_time);
748 169080479 : dcm.normalize();
749 :
750 169080479 : Vector3f accel_earth = dcm * accel_body;
751 169080479 : accel_earth += Vector3f(0.0f, 0.0f, GRAVITY_MSS);
752 :
753 : // if we're on the ground, then our vertical acceleration is limited
754 : // to zero. This effectively adds the force of the ground on the aircraft
755 169080479 : if (on_ground() && accel_earth.z > 0) {
756 60950890 : accel_earth.z = 0;
757 : }
758 :
759 : // work out acceleration as seen by the accelerometers. It sees the kinematic
760 : // acceleration (ie. real movement), plus gravity
761 169080479 : accel_body = dcm.transposed() * (accel_earth + Vector3f(0.0f, 0.0f, -GRAVITY_MSS));
762 :
763 : // new velocity vector
764 169080479 : velocity_ef += accel_earth * delta_time;
765 :
766 169080479 : const bool was_on_ground = on_ground();
767 : // new position vector
768 169080479 : position += (velocity_ef * delta_time).todouble();
769 :
770 : // velocity relative to air mass, in earth frame
771 169080479 : velocity_air_ef = velocity_ef - wind_ef;
772 :
773 : // velocity relative to airmass in body frame
774 169080479 : velocity_air_bf = dcm.transposed() * velocity_air_ef;
775 :
776 : // airspeed
777 169080479 : update_eas_airspeed();
778 :
779 : // constrain height to the ground
780 169080479 : if (on_ground()) {
781 61425013 : if (!was_on_ground && AP_HAL::millis() - last_ground_contact_ms > 1000) {
782 704 : GCS_SEND_TEXT(MAV_SEVERITY_INFO, "SIM Hit ground at %f m/s", velocity_ef.z);
783 704 : last_ground_contact_ms = AP_HAL::millis();
784 : }
785 61425013 : position.z = -(ground_level + frame_height - home.alt * 0.01f + ground_height_difference());
786 :
787 : // get speed of ground movement (for ship takeoff/landing)
788 61425013 : float yaw_rate = 0;
789 : #if AP_SIM_SHIP_ENABLED
790 61425013 : const Vector2f ship_movement = sitl->models.shipsim.get_ground_speed_adjustment(location, yaw_rate);
791 61425013 : const Vector3f gnd_movement(ship_movement.x, ship_movement.y, 0);
792 : #else
793 : const Vector3f gnd_movement;
794 : #endif
795 61425013 : switch (ground_behavior) {
796 3613461 : case GROUND_BEHAVIOR_NONE:
797 3613461 : break;
798 43849048 : case GROUND_BEHAVIOR_NO_MOVEMENT: {
799 : // zero roll/pitch, but keep yaw
800 : float r, p, y;
801 43849048 : dcm.to_euler(&r, &p, &y);
802 43849048 : y = y + yaw_rate * delta_time;
803 43849048 : dcm.from_euler(0.0f, 0.0f, y);
804 : // X, Y movement tracks ground movement
805 43849048 : velocity_ef.x = gnd_movement.x;
806 43849048 : velocity_ef.y = gnd_movement.y;
807 43849048 : if (velocity_ef.z > 0.0f) {
808 597 : velocity_ef.z = 0.0f;
809 : }
810 43849048 : gyro.zero();
811 43849048 : use_smoothing = true;
812 43849048 : break;
813 : }
814 13873237 : case GROUND_BEHAVIOR_FWD_ONLY: {
815 : // zero roll/pitch, but keep yaw
816 : float r, p, y;
817 13873237 : dcm.to_euler(&r, &p, &y);
818 13873237 : if (velocity_ef.length() < 5) {
819 : // at high speeds don't constrain pitch, otherwise we
820 : // can get stuck in takeoff
821 13432325 : p = 0;
822 : } else {
823 440912 : p = MAX(p, 0);
824 : }
825 13873237 : y = y + yaw_rate * delta_time;
826 13873237 : dcm.from_euler(0.0f, p, y);
827 : // only fwd movement
828 13873237 : Vector3f v_bf = dcm.transposed() * velocity_ef;
829 13873237 : v_bf.y = 0.0f;
830 13873237 : if (v_bf.x < 0.0f) {
831 10705099 : v_bf.x = 0.0f;
832 : }
833 :
834 13873237 : Vector3f gnd_movement_bf = dcm.transposed() * gnd_movement;
835 :
836 : // lateral speed equals ground movement
837 13873237 : v_bf.y = gnd_movement_bf.y;
838 :
839 13873237 : if (!gnd_movement_bf.is_zero()) {
840 : // fwd speed slowly approaches ground movement to simulate wheel friction
841 0 : const float tconst = 20; // seconds
842 0 : const float alpha = delta_time/(delta_time+tconst/M_2PI);
843 0 : v_bf.x += (gnd_movement.x - v_bf.x) * alpha;
844 : }
845 :
846 13873237 : velocity_ef = dcm * v_bf;
847 13873237 : if (velocity_ef.z > 0.0f) {
848 7501 : velocity_ef.z = 0.0f;
849 : }
850 13873237 : gyro.zero();
851 13873237 : gyro.z = yaw_rate;
852 13873237 : use_smoothing = true;
853 13873237 : break;
854 : }
855 89267 : case GROUND_BEHAVIOR_TAILSITTER: {
856 : // rotate normal refernce frame to get yaw angle, then rotate back
857 89267 : Matrix3f rot;
858 89267 : rot.from_rotation(ROTATION_PITCH_270);
859 : float r, p, y;
860 89267 : (dcm * rot).to_euler(&r, &p, &y);
861 89267 : y = y + yaw_rate * delta_time;
862 89267 : dcm.from_euler(0.0, 0.0, y);
863 89267 : rot.from_rotation(ROTATION_PITCH_90);
864 89267 : dcm *= rot;
865 : // X, Y movement tracks ground movement
866 89267 : velocity_ef.x = gnd_movement.x;
867 89267 : velocity_ef.y = gnd_movement.y;
868 89267 : if (velocity_ef.z > 0.0f) {
869 2 : velocity_ef.z = 0.0f;
870 : }
871 89267 : gyro.zero();
872 89267 : gyro.x = yaw_rate;
873 89267 : use_smoothing = true;
874 89267 : break;
875 : }
876 : }
877 : }
878 :
879 : // update slung payload
880 : #if AP_SIM_SLUNGPAYLOAD_ENABLED
881 169080479 : sitl->models.slung_payload_sim.update(get_position_relhome(), velocity_ef, accel_earth, wind_ef);
882 : #endif
883 :
884 : // update tether
885 : #if AP_SIM_TETHER_ENABLED
886 169080479 : sitl->models.tether_sim.update(location);
887 : #endif
888 :
889 : // allow for changes in physics step
890 169080479 : adjust_frame_time(constrain_float(sitl->loop_rate_hz, rate_hz-1, rate_hz+1));
891 169080479 : }
892 :
893 : /*
894 : update wind vector
895 : */
896 166729106 : void Aircraft::update_wind(const struct sitl_input &input)
897 : {
898 : // wind vector in earth frame
899 166729106 : wind_ef = Vector3f(cosf(radians(input.wind.direction))*cosf(radians(input.wind.dir_z)),
900 166729106 : sinf(radians(input.wind.direction))*cosf(radians(input.wind.dir_z)),
901 333458212 : sinf(radians(input.wind.dir_z))) * input.wind.speed;
902 :
903 166729106 : wind_ef.z += get_local_updraft(position + home.get_distance_NED_double(origin));
904 :
905 166729106 : const float wind_turb = input.wind.turbulence * 10.0f; // scale input.wind.turbulence to match standard deviation when using iir_coef=0.98
906 166729106 : const float iir_coef = 0.98f; // filtering high frequencies from turbulence
907 :
908 166729106 : if (wind_turb > 0 && !on_ground()) {
909 :
910 0 : turbulence_azimuth = turbulence_azimuth + (2 * rand());
911 :
912 0 : turbulence_horizontal_speed =
913 0 : static_cast<float>(turbulence_horizontal_speed * iir_coef+wind_turb * rand_normal(0, 1) * (1 - iir_coef));
914 :
915 0 : turbulence_vertical_speed = static_cast<float>((turbulence_vertical_speed * iir_coef) + (wind_turb * rand_normal(0, 1) * (1 - iir_coef)));
916 :
917 0 : wind_ef += Vector3f(
918 0 : cosf(radians(turbulence_azimuth)) * turbulence_horizontal_speed,
919 0 : sinf(radians(turbulence_azimuth)) * turbulence_horizontal_speed,
920 0 : turbulence_vertical_speed);
921 : }
922 :
923 : // the AHRS wants wind with opposite sense
924 166729106 : wind_ef = -wind_ef;
925 166729106 : }
926 :
927 : /*
928 : smooth sensors for kinematic consistancy when we interact with the ground
929 : */
930 159454654 : void Aircraft::smooth_sensors(void)
931 : {
932 159454654 : uint64_t now = time_now_us;
933 159454654 : Vector3d delta_pos = position - smoothing.position;
934 159454654 : if (smoothing.last_update_us == 0 || delta_pos.length() > 10) {
935 1714 : smoothing.position = position;
936 1714 : smoothing.rotation_b2e = dcm;
937 1714 : smoothing.accel_body = accel_body;
938 1714 : smoothing.velocity_ef = velocity_ef;
939 1714 : smoothing.gyro = gyro;
940 1714 : smoothing.last_update_us = now;
941 1714 : smoothing.location = location;
942 1714 : printf("Smoothing reset at %.3f\n", now * 1.0e-6f);
943 1714 : return;
944 : }
945 159452940 : const float delta_time = (now - smoothing.last_update_us) * 1.0e-6f;
946 159452940 : if (delta_time < 0 || delta_time > 0.1) {
947 0 : return;
948 : }
949 :
950 : // calculate required accel to get us to desired position and velocity in the time_constant
951 159452940 : const float time_constant = 0.1f;
952 159452940 : Vector3f dvel = (velocity_ef - smoothing.velocity_ef) + (delta_pos / time_constant).tofloat();
953 159452940 : Vector3f accel_e = dvel / time_constant + (dcm * accel_body + Vector3f(0.0f, 0.0f, GRAVITY_MSS));
954 159452940 : const float accel_limit = 14 * GRAVITY_MSS;
955 159452940 : accel_e.x = constrain_float(accel_e.x, -accel_limit, accel_limit);
956 159452940 : accel_e.y = constrain_float(accel_e.y, -accel_limit, accel_limit);
957 159452940 : accel_e.z = constrain_float(accel_e.z, -accel_limit, accel_limit);
958 159452940 : smoothing.accel_body = smoothing.rotation_b2e.transposed() * (accel_e + Vector3f(0.0f, 0.0f, -GRAVITY_MSS));
959 :
960 : // calculate rotational rate to get us to desired attitude in time constant
961 159452940 : Quaternion desired_q, current_q, error_q;
962 159452940 : desired_q.from_rotation_matrix(dcm);
963 159452940 : desired_q.normalize();
964 159452940 : current_q.from_rotation_matrix(smoothing.rotation_b2e);
965 159452940 : current_q.normalize();
966 159452940 : error_q = desired_q / current_q;
967 159452940 : error_q.normalize();
968 :
969 159452940 : Vector3f angle_differential;
970 159452940 : error_q.to_axis_angle(angle_differential);
971 159452940 : smoothing.gyro = gyro + angle_differential / time_constant;
972 :
973 : float R, P, Y;
974 159452940 : smoothing.rotation_b2e.to_euler(&R, &P, &Y);
975 : float R2, P2, Y2;
976 159452940 : dcm.to_euler(&R2, &P2, &Y2);
977 :
978 : #if 0
979 : // @LoggerMessage: SMOO
980 : // @Description: Smoothed sensor data fed to EKF to avoid inconsistencies
981 : // @Field: TimeUS: Time since system startup
982 : // @Field: AEx: Angular Velocity (around x-axis)
983 : // @Field: AEy: Angular Velocity (around y-axis)
984 : // @Field: AEz: Angular Velocity (around z-axis)
985 : // @Field: DPx: Velocity (along x-axis)
986 : // @Field: DPy: Velocity (along y-axis)
987 : // @Field: DPz: Velocity (along z-axis)
988 : // @Field: R: Roll
989 : // @Field: P: Pitch
990 : // @Field: Y: Yaw
991 : // @Field: R2: DCM Roll
992 : // @Field: P2: DCM Pitch
993 : // @Field: Y2: DCM Yaw
994 : AP::logger().WriteStreaming("SMOO", "TimeUS,AEx,AEy,AEz,DPx,DPy,DPz,R,P,Y,R2,P2,Y2",
995 : "Qffffffffffff",
996 : AP_HAL::micros64(),
997 : degrees(angle_differential.x),
998 : degrees(angle_differential.y),
999 : degrees(angle_differential.z),
1000 : delta_pos.x, delta_pos.y, delta_pos.z,
1001 : degrees(R), degrees(P), degrees(Y),
1002 : degrees(R2), degrees(P2), degrees(Y2));
1003 : #endif
1004 :
1005 :
1006 : // integrate to get new attitude
1007 159452940 : smoothing.rotation_b2e.rotate(smoothing.gyro * delta_time);
1008 159452940 : smoothing.rotation_b2e.normalize();
1009 :
1010 : // integrate to get new position
1011 159452940 : smoothing.velocity_ef += accel_e * delta_time;
1012 159452940 : smoothing.position += (smoothing.velocity_ef * delta_time).todouble();
1013 :
1014 159452940 : smoothing.location = origin;
1015 159452940 : smoothing.location.offset(smoothing.position.x, smoothing.position.y);
1016 159452940 : smoothing.location.alt = static_cast<int32_t>(home.alt - smoothing.position.z * 100.0f);
1017 :
1018 159452940 : smoothing.last_update_us = now;
1019 : }
1020 :
1021 : /*
1022 : return a filtered servo input as a value from -1 to 1
1023 : servo is assumed to be 1000 to 2000, trim at 1500
1024 : */
1025 209084155 : float Aircraft::filtered_servo_angle(const struct sitl_input &input, uint8_t idx)
1026 : {
1027 209084155 : uint16_t pwm = input.servos[idx];
1028 209084155 : if (pwm == 0) {
1029 : // invalid input, servo does not move, apply current value.
1030 : // glad we have a zero-momentum system.
1031 5295439 : pwm = servo_filter[idx].angle_pwm();
1032 : }
1033 209084155 : return servo_filter[idx].filter_angle(pwm, frame_time_us * 1.0e-6);
1034 : }
1035 :
1036 : /*
1037 : return a filtered servo input as a value from 0 to 1
1038 : servo is assumed to be 1000 to 2000
1039 : */
1040 135454372 : float Aircraft::filtered_servo_range(const struct sitl_input &input, uint8_t idx)
1041 : {
1042 135454372 : return servo_filter[idx].filter_range(input.servos[idx], frame_time_us * 1.0e-6);
1043 : }
1044 :
1045 : // setup filtering for servo
1046 7073484 : void Aircraft::filtered_servo_setup(uint8_t idx, uint16_t pwm_min, uint16_t pwm_max, float deflection_deg)
1047 : {
1048 7073484 : servo_filter[idx].set_pwm_range(pwm_min, pwm_max);
1049 7073484 : servo_filter[idx].set_deflection(deflection_deg);
1050 7073484 : }
1051 :
1052 : // extrapolate sensors by a given delta time in seconds
1053 0 : void Aircraft::extrapolate_sensors(float delta_time)
1054 : {
1055 0 : Vector3f accel_earth = dcm * accel_body;
1056 0 : accel_earth.z += GRAVITY_MSS;
1057 :
1058 0 : dcm.rotate(gyro * delta_time);
1059 0 : dcm.normalize();
1060 :
1061 : // work out acceleration as seen by the accelerometers. It sees the kinematic
1062 : // acceleration (ie. real movement), plus gravity
1063 0 : accel_body = dcm.transposed() * (accel_earth + Vector3f(0,0,-GRAVITY_MSS));
1064 :
1065 : // new velocity and position vectors
1066 0 : velocity_ef += accel_earth * delta_time;
1067 0 : position += (velocity_ef * delta_time).todouble();
1068 0 : velocity_air_ef = velocity_ef - wind_ef;
1069 0 : velocity_air_bf = dcm.transposed() * velocity_air_ef;
1070 0 : }
1071 :
1072 81459271 : bool Aircraft::Clamp::clamped(Aircraft &aircraft, const struct sitl_input &input)
1073 : {
1074 81459271 : const auto clamp_ch = AP::sitl()->clamp_ch;
1075 81459271 : if (clamp_ch < 1) {
1076 81122231 : return false;
1077 : }
1078 337040 : const uint32_t clamp_idx = clamp_ch - 1;
1079 337040 : if (clamp_idx > ARRAY_SIZE(input.servos)) {
1080 0 : return false;
1081 : }
1082 337040 : const uint16_t servo_pos = input.servos[clamp_idx];
1083 337040 : bool new_clamped = currently_clamped;
1084 337040 : if (servo_pos == 0) {
1085 : // invalid value, do nothing
1086 278370 : } else if (servo_pos < 1200) {
1087 203845 : if (currently_clamped) {
1088 2 : GCS_SEND_TEXT(MAV_SEVERITY_INFO, "SITL: Clamp: released vehicle");
1089 2 : new_clamped = false;
1090 : }
1091 203845 : grab_attempted = false;
1092 : } else {
1093 : // re-clamp if < 10cm from home
1094 74525 : if (servo_pos > 1800 && !grab_attempted) {
1095 5 : const Vector3d pos = aircraft.get_position_relhome();
1096 5 : const float distance_from_home = pos.length();
1097 : // GCS_SEND_TEXT(MAV_SEVERITY_INFO, "SITL: Clamp: dist=%f", distance_from_home);
1098 5 : if (distance_from_home < 0.5) {
1099 4 : GCS_SEND_TEXT(MAV_SEVERITY_INFO, "SITL: Clamp: grabbed vehicle");
1100 4 : new_clamped = true;
1101 1 : } else if (!grab_attempted) {
1102 1 : GCS_SEND_TEXT(MAV_SEVERITY_INFO, "SITL: Clamp: missed vehicle");
1103 : }
1104 5 : grab_attempted = true;
1105 : }
1106 : }
1107 :
1108 337040 : currently_clamped = new_clamped;
1109 :
1110 337040 : return currently_clamped;
1111 : }
1112 :
1113 : // simple battery consumption model
1114 : // does not support the behavior documented by SIM_BATT_VOLTAGE and SIM_BATT_CAP_AH parameters
1115 10283911 : void Aircraft::update_battery()
1116 : {
1117 : // lose 0.7V at full throttle (from the user-specified voltage)
1118 10283911 : battery_voltage = sitl->batt_voltage - 0.7f * fabsf(sitl->throttle);
1119 : // assume 50A at full throttle
1120 10283911 : battery_current = 50.0f * fabsf(sitl->throttle);
1121 10283911 : }
1122 :
1123 0 : void Aircraft::update_battery(const struct sitl_input &input)
1124 : {
1125 0 : update_battery();
1126 0 : }
1127 :
1128 178759942 : void Aircraft::update_external_payload(const struct sitl_input &input)
1129 : {
1130 178759942 : external_payload_mass = 0;
1131 :
1132 : #if AP_SIM_SPRAYER_ENABLED
1133 : // update sprayer
1134 178759942 : if (sitl->sprayer_sim.is_enabled()) {
1135 271619 : sitl->sprayer_sim.update(input);
1136 271619 : external_payload_mass += sitl->sprayer_sim.payload_mass();
1137 : }
1138 : #endif // AP_SIM_SPRAYER_ENABLED
1139 :
1140 : {
1141 178759942 : const float range = rangefinder_range();
1142 178759942 : if (!isinf(range) && range > 100000) {
1143 0 : AP_HAL::panic("Bad rangefinder calculation");
1144 : }
1145 1966359362 : for (uint8_t i=0; i<ARRAY_SIZE(rangefinder_m); i++) {
1146 1787599420 : rangefinder_m[i] = range;
1147 : }
1148 : }
1149 :
1150 : // update i2c
1151 178759942 : sitl->i2c_sim.update(*this);
1152 :
1153 : #if AP_SIM_BUZZER_ENABLED
1154 : // update buzzer
1155 178759942 : if (sitl->buzzer_sim.is_enabled()) {
1156 0 : sitl->buzzer_sim.update(input);
1157 : }
1158 : #endif // AP_SIM_BUZZER_ENABLED
1159 :
1160 : // update grippers
1161 : #if AP_SIM_GRIPPER_ENABLED
1162 178759942 : if (sitl->gripper_sim.is_enabled()) {
1163 810128 : sitl->gripper_sim.set_alt(hagl());
1164 810128 : sitl->gripper_sim.update(input);
1165 810128 : external_payload_mass += sitl->gripper_sim.payload_mass();
1166 : }
1167 : #endif // AP_SIM_GRIPPER_ENABLED
1168 : #if AP_SIM_GRIPPER_EPM_ENABLED
1169 178759942 : if (sitl->gripper_epm_sim.is_enabled()) {
1170 0 : sitl->gripper_epm_sim.update(input);
1171 0 : external_payload_mass += sitl->gripper_epm_sim.payload_mass();
1172 : }
1173 : #endif // AP_SIM_GRIPPER_EPM_ENABLED
1174 :
1175 : #if AP_SIM_PARACHUTE_ENABLED
1176 : // update parachute
1177 178759942 : if (sitl->parachute_sim.is_enabled()) {
1178 1215553 : sitl->parachute_sim.update(input);
1179 : // TODO: add drag to vehicle, presumably proportional to velocity
1180 : }
1181 : #endif // AP_SIM_PARACHUTE_ENABLED
1182 :
1183 : #if AP_SIM_PRECLAND_ENABLED
1184 : // update precland
1185 178759942 : if (sitl->precland_sim.is_enabled()) {
1186 946580 : sitl->precland_sim.update(get_location());
1187 946580 : if (sitl->precland_sim._over_precland_base) {
1188 0 : local_ground_level += sitl->precland_sim._device_height;
1189 : }
1190 : }
1191 : #endif // AP_SIM_PRECLAND_ENABLED
1192 :
1193 : // update RichenPower generator
1194 178759942 : if (richenpower) {
1195 113479 : richenpower->update(input);
1196 : }
1197 :
1198 : #if AP_SIM_LOWEHEISER_ENABLED
1199 : // update Loweheiser generator
1200 178759942 : if (loweheiser) {
1201 922454 : loweheiser->update(*this);
1202 : }
1203 : #endif
1204 :
1205 178759942 : if (fetteconewireesc) {
1206 658525 : fetteconewireesc->update(*this);
1207 : }
1208 :
1209 : #if AP_SIM_VOLZ_ENABLED
1210 178759942 : if (volz) {
1211 218651 : volz->update(*this);
1212 : }
1213 : #endif // AP_SIM_VOLZ_ENABLED
1214 :
1215 : #if AP_SIM_SHIP_ENABLED
1216 178759942 : sitl->models.shipsim.update();
1217 : #endif
1218 :
1219 : // update IntelligentEnergy 2.4kW generator
1220 178759942 : if (ie24) {
1221 420295 : ie24->update(input);
1222 : }
1223 :
1224 : #if AP_TEST_DRONECAN_DRIVERS
1225 : sitl->dronecan_sim.update();
1226 : #endif
1227 :
1228 : #if AP_SIM_GPIO_LED_1_ENABLED
1229 : sim_led1.update(*this);
1230 : #endif
1231 : #if AP_SIM_GPIO_LED_2_ENABLED
1232 : sim_led2.update(*this);
1233 : #endif
1234 : #if AP_SIM_GPIO_LED_3_ENABLED
1235 : sim_led3.update(*this);
1236 : #endif
1237 : #if AP_SIM_GPIO_LED_RGB_ENABLED
1238 : sim_ledrgb.update(*this);
1239 : #endif
1240 :
1241 : #if AP_SIM_MOUNT_ENABLED
1242 1608839478 : for (uint8_t i = 0; i < GIMBAL_SIM_MAX; i++) {
1243 1430079536 : if (gimbal_sims[i] != nullptr) {
1244 844397 : gimbal_sims[i]->update(*this);
1245 : }
1246 : }
1247 : #endif
1248 178759942 : }
1249 :
1250 88212468 : void Aircraft::add_shove_forces(Vector3f &rot_accel, Vector3f &body_accel)
1251 : {
1252 88212468 : const uint32_t now = AP_HAL::millis();
1253 88212468 : if (sitl == nullptr) {
1254 0 : return;
1255 : }
1256 88212468 : if (sitl->shove.t == 0) {
1257 88138634 : return;
1258 : }
1259 73834 : if (sitl->shove.start_ms == 0) {
1260 5 : sitl->shove.start_ms = now;
1261 : }
1262 73834 : if (now - sitl->shove.start_ms < uint32_t(sitl->shove.t)) {
1263 : // FIXME: can we get a vector operation here instead?
1264 73829 : body_accel.x += sitl->shove.x;
1265 73829 : body_accel.y += sitl->shove.y;
1266 73829 : body_accel.z += sitl->shove.z;
1267 : } else {
1268 5 : sitl->shove.start_ms = 0;
1269 : // save as well as set: the parameter was written to storage to
1270 : // ask for the shove, so clearing only the live value leaves
1271 : // storage still asking for one. A GCS - or the test suite
1272 : // putting parameters back after a test - then sees the live
1273 : // value already at zero and has no reason to write, and the
1274 : // next reboot loads the old duration and shoves again.
1275 5 : sitl->shove.t.set_and_save(0);
1276 : }
1277 : }
1278 :
1279 166729106 : float Aircraft::get_local_updraft(const Vector3d ¤tPos)
1280 : {
1281 166729106 : int scenario = sitl->thermal_scenario;
1282 :
1283 : #define MAX_THERMALS 10
1284 :
1285 : float thermals_w[MAX_THERMALS];
1286 : float thermals_r[MAX_THERMALS];
1287 : float thermals_x[MAX_THERMALS];
1288 : float thermals_y[MAX_THERMALS];
1289 :
1290 166729106 : int n_thermals = 0;
1291 :
1292 166729106 : switch (scenario) {
1293 165658798 : case 0:
1294 165658798 : return 0;
1295 1070308 : case 1:
1296 1070308 : n_thermals = 1;
1297 1070308 : thermals_w[0] = 2.0;
1298 1070308 : thermals_r[0] = 80.0;
1299 1070308 : thermals_x[0] = -180.0;
1300 1070308 : thermals_y[0] = -260.0;
1301 1070308 : break;
1302 0 : case 2:
1303 0 : n_thermals = 1;
1304 0 : thermals_w[0] = 4.0;
1305 0 : thermals_r[0] = 30.0;
1306 0 : thermals_x[0] = -180.0;
1307 0 : thermals_y[0] = -260.0;
1308 0 : break;
1309 0 : case 3:
1310 0 : n_thermals = 1;
1311 0 : thermals_w[0] = 2.0;
1312 0 : thermals_r[0] = 30.0;
1313 0 : thermals_x[0] = -180.0;
1314 0 : thermals_y[0] = -260.0;
1315 0 : break;
1316 0 : case 4:
1317 0 : n_thermals = 1;
1318 0 : thermals_w[0] = 5.0;
1319 0 : thermals_r[0] = 30.0;
1320 0 : thermals_x[0] = 0;
1321 0 : thermals_y[0] = 0;
1322 0 : break;
1323 0 : default:
1324 0 : AP_BoardConfig::config_error("Bad thermal scenario");
1325 : }
1326 :
1327 : // Wind drift at this altitude
1328 1070308 : float driftX = sitl->wind_speed * (currentPos.z+100) * cosf(sitl->wind_direction * DEG_TO_RAD);
1329 1070308 : float driftY = sitl->wind_speed * (currentPos.z+100) * sinf(sitl->wind_direction * DEG_TO_RAD);
1330 :
1331 : int iThermal;
1332 1070308 : float w = 0.0f;
1333 : float r2;
1334 2140616 : for (iThermal=0;iThermal<n_thermals;iThermal++) {
1335 1070308 : Vector3d thermalPos(thermals_x[iThermal] + driftX/thermals_w[iThermal],
1336 1070308 : thermals_y[iThermal] + driftY/thermals_w[iThermal],
1337 1070308 : 0);
1338 1070308 : Vector3d relVec = currentPos - thermalPos;
1339 1070308 : r2 = relVec.x*relVec.x + relVec.y*relVec.y;
1340 1070308 : w += thermals_w[iThermal]*exp(-r2/(thermals_r[iThermal]*thermals_r[iThermal]));
1341 : }
1342 :
1343 1070308 : return w;
1344 : }
1345 :
1346 81459271 : void Aircraft::add_twist_forces(Vector3f &rot_accel)
1347 : {
1348 81459271 : if (sitl == nullptr) {
1349 0 : return;
1350 : }
1351 81459271 : if (sitl->gnd_behav != -1) {
1352 1048 : ground_behavior = (GroundBehaviour)sitl->gnd_behav.get();
1353 : }
1354 81459271 : const uint32_t now = AP_HAL::millis();
1355 81459271 : if (sitl == nullptr) {
1356 0 : return;
1357 : }
1358 81459271 : if (sitl->twist.t == 0) {
1359 81333134 : return;
1360 : }
1361 126137 : if (sitl->twist.start_ms == 0) {
1362 101 : sitl->twist.start_ms = now;
1363 : }
1364 126137 : if (now - sitl->twist.start_ms < uint32_t(sitl->twist.t)) {
1365 : // FIXME: can we get a vector operation here instead?
1366 126102 : rot_accel.x += sitl->twist.x;
1367 126102 : rot_accel.y += sitl->twist.y;
1368 126102 : rot_accel.z += sitl->twist.z;
1369 : } else {
1370 35 : sitl->twist.start_ms = 0;
1371 35 : sitl->twist.t.set(0);
1372 : }
1373 : }
1374 :
1375 : // add body-frame force due to slung payload and tether
1376 81459271 : void Aircraft::add_external_forces(Vector3f &body_accel)
1377 : {
1378 81459271 : Vector3f total_force;
1379 : #if AP_SIM_SLUNGPAYLOAD_ENABLED
1380 81459271 : Vector3f forces_ef_slung;
1381 81459271 : sitl->models.slung_payload_sim.get_forces_on_vehicle(forces_ef_slung);
1382 81459271 : total_force += forces_ef_slung;
1383 : #endif
1384 :
1385 : #if AP_SIM_TETHER_ENABLED
1386 81459271 : Vector3f forces_ef_tether;
1387 81459271 : sitl->models.tether_sim.get_forces_on_vehicle(forces_ef_tether);
1388 81459271 : total_force += forces_ef_tether;
1389 : #endif
1390 :
1391 : // convert ef forces to body-frame accelerations (acceleration = force / mass)
1392 81459271 : const Vector3f accel_bf_tether = dcm.transposed() * total_force / mass;
1393 81459271 : body_accel += accel_bf_tether;
1394 81459271 : }
1395 :
1396 : /*
1397 : get position relative to home
1398 : */
1399 398928352 : Vector3d Aircraft::get_position_relhome() const
1400 : {
1401 398928352 : Vector3d pos = position;
1402 398928352 : pos.xy() += home.get_distance_NE_double(origin);
1403 398928352 : return pos;
1404 : }
1405 :
1406 : // get air density in kg/m^3
1407 0 : float Aircraft::get_air_density(float alt_amsl) const
1408 : {
1409 0 : return AP_Baro::get_air_density_for_alt_amsl(alt_amsl);
1410 : }
1411 :
1412 : /*
1413 : update EAS airspeed and pitot speed
1414 : */
1415 169080479 : void Aircraft::update_eas_airspeed()
1416 : {
1417 169080479 : airspeed = velocity_air_ef.length() / eas2tas;
1418 :
1419 : /*
1420 : airspeed as seen by a fwd pitot tube (limited to 120m/s)
1421 : */
1422 169080479 : airspeed_pitot = airspeed;
1423 :
1424 : // calculate angle between the local flow vector and a pitot tube aligned with the X body axis
1425 169080479 : const float pitot_aoa = atan2f(sqrtf(sq(velocity_air_bf.y) + sq(velocity_air_bf.z)), velocity_air_bf.x);
1426 :
1427 : /*
1428 : assume the pitot can correctly capture airspeed up to 20 degrees off the nose
1429 : and follows a cose law outside that range
1430 : */
1431 169080479 : const float max_pitot_aoa = radians(20);
1432 169080479 : if (pitot_aoa > radians(90)) {
1433 45049860 : airspeed_pitot = 0;
1434 124030619 : } else if (pitot_aoa > max_pitot_aoa) {
1435 39717605 : const float gain_factor = M_PI_2 / (radians(90) - max_pitot_aoa);
1436 39717605 : airspeed_pitot *= cosf((pitot_aoa - max_pitot_aoa) * gain_factor);
1437 : }
1438 169080479 : }
1439 :
1440 : /*
1441 : set pose on the aircraft, called from scripting
1442 : */
1443 80 : bool Aircraft::set_pose(uint8_t instance, const Location &loc, const Quaternion &quat, const Vector3f &velocity_ef, const Vector3f &gyro_rads)
1444 : {
1445 80 : if (instance >= MAX_SIM_INSTANCES || instances[instance] == nullptr) {
1446 0 : return false;
1447 : }
1448 80 : auto &aircraft = *instances[instance];
1449 80 : WITH_SEMAPHORE(aircraft.pose_sem);
1450 :
1451 80 : quat.rotation_matrix(aircraft.dcm);
1452 80 : aircraft.velocity_ef = velocity_ef;
1453 80 : aircraft.location = loc;
1454 80 : aircraft.position = aircraft.home.get_distance_NED_double(loc);
1455 80 : aircraft.smoothing.position = aircraft.position;
1456 80 : aircraft.smoothing.rotation_b2e = aircraft.dcm;
1457 80 : aircraft.smoothing.velocity_ef = velocity_ef;
1458 80 : aircraft.smoothing.location = loc;
1459 80 : aircraft.gyro = gyro_rads;
1460 :
1461 80 : return true;
1462 80 : }
1463 :
1464 : /*
1465 : wrapper for scripting access
1466 : */
1467 80 : bool SITL::SIM::set_pose(uint8_t instance, const Location &loc, const Quaternion &quat, const Vector3f &velocity_ef, const Vector3f &gyro_rads)
1468 : {
1469 80 : return Aircraft::set_pose(instance, loc, quat, velocity_ef, gyro_rads);
1470 : }
1471 :
|