/* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* SITL.cpp - software in the loop state */ #include "SITL.h" #include #include #if CONFIG_HAL_BOARD == HAL_BOARD_SITL #include #include extern const AP_HAL::HAL& hal; namespace SITL { SITL *SITL::_singleton = nullptr; // table of user settable parameters const AP_Param::GroupInfo SITL::var_info[] = { AP_GROUPINFO("GYR_RND", 1, SITL, gyro_noise, 0), AP_GROUPINFO("ACC_RND", 2, SITL, accel_noise, 0), AP_GROUPINFO("DRIFT_SPEED", 5, SITL, drift_speed, 0.05f), AP_GROUPINFO("DRIFT_TIME", 6, SITL, drift_time, 5), AP_GROUPINFO("ENGINE_MUL", 8, SITL, engine_mul, 1), AP_GROUPINFO("WIND_SPD", 9, SITL, wind_speed, 0), AP_GROUPINFO("WIND_DIR", 10, SITL, wind_direction, 180), AP_GROUPINFO("WIND_TURB", 11, SITL, wind_turbulance, 0), AP_GROUPINFO("SERVO_SPEED", 16, SITL, servo_speed, 0.14), AP_GROUPINFO("BATT_VOLTAGE", 19, SITL, batt_voltage, 12.6f), AP_GROUPINFO("ACCEL_FAIL", 21, SITL, accel_fail, 0), AP_GROUPINFO("SONAR_GLITCH", 23, SITL, sonar_glitch, 0), AP_GROUPINFO("SONAR_RND", 24, SITL, sonar_noise, 0), AP_GROUPINFO("RC_FAIL", 25, SITL, rc_fail, 0), AP_GROUPINFO("FLOAT_EXCEPT", 28, SITL, float_exception, 1), AP_GROUPINFO("ACC_BIAS", 30, SITL, accel_bias, 0), AP_GROUPINFO("SONAR_SCALE", 32, SITL, sonar_scale, 12.1212f), AP_GROUPINFO("FLOW_ENABLE", 33, SITL, flow_enable, 0), AP_GROUPINFO("TERRAIN", 34, SITL, terrain_enable, 1), AP_GROUPINFO("FLOW_RATE", 35, SITL, flow_rate, 10), AP_GROUPINFO("FLOW_DELAY", 36, SITL, flow_delay, 0), AP_GROUPINFO("WIND_DELAY", 40, SITL, wind_delay, 0), AP_GROUPINFO("ACC2_RND", 42, SITL, accel2_noise, 0), AP_GROUPINFO("GYR_SCALE", 44, SITL, gyro_scale, 0), AP_GROUPINFO("ADSB_COUNT", 45, SITL, adsb_plane_count, -1), AP_GROUPINFO("ADSB_RADIUS", 46, SITL, adsb_radius_m, 10000), AP_GROUPINFO("ADSB_ALT", 47, SITL, adsb_altitude_m, 1000), AP_GROUPINFO("PIN_MASK", 50, SITL, pin_mask, 0), AP_GROUPINFO("ADSB_TX", 51, SITL, adsb_tx, 0), AP_GROUPINFO("SPEEDUP", 52, SITL, speedup, -1), AP_GROUPINFO("IMU_POS", 53, SITL, imu_pos_offset, 0), AP_GROUPINFO("SONAR_POS", 55, SITL, rngfnd_pos_offset, 0), AP_GROUPINFO("FLOW_POS", 56, SITL, optflow_pos_offset, 0), AP_GROUPINFO("ACC2_BIAS", 57, SITL, accel2_bias, 0), AP_GROUPINFO("ENGINE_FAIL", 58, SITL, engine_fail, 0), AP_SUBGROUPINFO(shipsim, "SHIP_", 59, SITL, ShipSim), AP_SUBGROUPEXTENSION("", 60, SITL, var_mag), AP_SUBGROUPEXTENSION("", 61, SITL, var_gps), AP_SUBGROUPEXTENSION("", 62, SITL, var_info3), AP_SUBGROUPEXTENSION("", 63, SITL, var_info2), AP_GROUPEND }; // second table of user settable parameters for SITL. const AP_Param::GroupInfo SITL::var_info2[] = { AP_GROUPINFO("TEMP_START", 1, SITL, temp_start, 25), AP_GROUPINFO("TEMP_FLIGHT", 2, SITL, temp_flight, 35), AP_GROUPINFO("TEMP_TCONST", 3, SITL, temp_tconst, 30), AP_GROUPINFO("TEMP_BFACTOR", 4, SITL, temp_baro_factor, 0), AP_GROUPINFO("WIND_DIR_Z", 10, SITL, wind_dir_z, 0), AP_GROUPINFO("WIND_T" ,15, SITL, wind_type, SITL::WIND_TYPE_SQRT), AP_GROUPINFO("WIND_T_ALT" ,16, SITL, wind_type_alt, 60), AP_GROUPINFO("WIND_T_COEF", 17, SITL, wind_type_coef, 0.01f), AP_GROUPINFO("RC_CHANCOUNT",21, SITL, rc_chancount, 16), // @Group: SPR_ // @Path: ./SIM_Sprayer.cpp AP_SUBGROUPINFO(sprayer_sim, "SPR_", 22, SITL, Sprayer), // @Group: GRPS_ // @Path: ./SIM_Gripper_Servo.cpp AP_SUBGROUPINFO(gripper_sim, "GRPS_", 23, SITL, Gripper_Servo), // @Group: GRPE_ // @Path: ./SIM_Gripper_EPM.cpp AP_SUBGROUPINFO(gripper_epm_sim, "GRPE_", 24, SITL, Gripper_EPM), // weight on wheels pin AP_GROUPINFO("WOW_PIN", 25, SITL, wow_pin, -1), // vibration frequencies on each axis AP_GROUPINFO("VIB_FREQ", 26, SITL, vibe_freq, 0), // @Path: ./SIM_Parachute.cpp AP_SUBGROUPINFO(parachute_sim, "PARA_", 27, SITL, Parachute), // enable bandwidth limitting on telemetry ports: AP_GROUPINFO("BAUDLIMIT_EN", 28, SITL, telem_baudlimit_enable, 0), // @Group: PLD_ // @Path: ./SIM_Precland.cpp AP_SUBGROUPINFO(precland_sim, "PLD_", 29, SITL, SIM_Precland), // apply a force to the vehicle over a period of time: AP_GROUPINFO("SHOVE_X", 30, SITL, shove.x, 0), AP_GROUPINFO("SHOVE_Y", 31, SITL, shove.y, 0), AP_GROUPINFO("SHOVE_Z", 32, SITL, shove.z, 0), AP_GROUPINFO("SHOVE_TIME", 33, SITL, shove.t, 0), // optical flow sensor measurement noise in rad/sec AP_GROUPINFO("FLOW_RND", 34, SITL, flow_noise, 0.05f), // accel and gyro fail masks AP_GROUPINFO("GYR_FAIL_MSK", 35, SITL, gyro_fail_mask, 0), AP_GROUPINFO("ACC_FAIL_MSK", 36, SITL, accel_fail_mask, 0), AP_GROUPINFO("TWIST_X", 37, SITL, twist.x, 0), AP_GROUPINFO("TWIST_Y", 38, SITL, twist.y, 0), AP_GROUPINFO("TWIST_Z", 39, SITL, twist.z, 0), AP_GROUPINFO("TWIST_TIME", 40, SITL, twist.t, 0), AP_GROUPINFO("GND_BEHAV", 41, SITL, gnd_behav, -1), // sailboat wave and tide simulation parameters AP_GROUPINFO("WAVE_ENABLE", 44, SITL, wave.enable, 0.0f), AP_GROUPINFO("WAVE_LENGTH", 45, SITL, wave.length, 10.0f), AP_GROUPINFO("WAVE_AMP", 46, SITL, wave.amp, 0.5f), AP_GROUPINFO("WAVE_DIR", 47, SITL, wave.direction, 0.0f), AP_GROUPINFO("WAVE_SPEED", 48, SITL, wave.speed, 0.5f), AP_GROUPINFO("TIDE_DIR", 49, SITL, tide.direction, 0.0f), AP_GROUPINFO("TIDE_SPEED", 50, SITL, tide.speed, 0.0f), // the following coordinates are for CMAC, in Canberra AP_GROUPINFO("OPOS_LAT", 51, SITL, opos.lat, -35.363261f), AP_GROUPINFO("OPOS_LNG", 52, SITL, opos.lng, 149.165230f), AP_GROUPINFO("OPOS_ALT", 53, SITL, opos.alt, 584.0f), AP_GROUPINFO("OPOS_HDG", 54, SITL, opos.hdg, 353.0f), // extra delay per main loop AP_GROUPINFO("LOOP_DELAY", 55, SITL, loop_delay, 0), // @Path: ./SIM_Buzzer.cpp AP_SUBGROUPINFO(buzzer_sim, "BZ_", 56, SITL, Buzzer), // @Path: ./SIM_ToneAlarm.cpp AP_SUBGROUPINFO(tonealarm_sim, "TA_", 57, SITL, ToneAlarm), AP_GROUPINFO("EFI_TYPE", 58, SITL, efi_type, SITL::EFI_TYPE_NONE), AP_GROUPINFO("SAFETY_STATE", 59, SITL, _safety_switch_state, 0), // max motor vibration frequency AP_GROUPINFO("VIB_MOT_MAX", 61, SITL, vibe_motor, 0.0f), // minimum throttle for simulated ins noise AP_GROUPINFO("INS_THR_MIN", 62, SITL, ins_noise_throttle_min, 0.1f), // amplitude scaling of motor noise relative to gyro/accel noise AP_GROUPINFO("VIB_MOT_MULT", 63, SITL, vibe_motor_scale, 1.0f), AP_GROUPEND }; // third table of user settable parameters for SITL. const AP_Param::GroupInfo SITL::var_info3[] = { AP_GROUPINFO("ODOM_ENABLE", 1, SITL, odom_enable, 0), AP_GROUPINFO("LED_LAYOUT", 11, SITL, led_layout, 0), // Scenario for thermalling simulation, for soaring AP_GROUPINFO("THML_SCENARI", 12, SITL, thermal_scenario, 0), // vicon sensor position (position offsets in body frame) AP_GROUPINFO("VICON_POS", 14, SITL, vicon_pos_offset, 0), // Buyoancy for submarines AP_GROUPINFO_FRAME("BUOYANCY", 15, SITL, buoyancy, 1, AP_PARAM_FRAME_SUB), // vicon glitch in NED frame AP_GROUPINFO("VICON_GLIT", 16, SITL, vicon_glitch, 0), // vicon failure AP_GROUPINFO("VICON_FAIL", 17, SITL, vicon_fail, 0), // vicon yaw (in earth frame) AP_GROUPINFO("VICON_YAW", 18, SITL, vicon_yaw, 0), // vicon yaw error in degrees (added to reported yaw sent to vehicle) AP_GROUPINFO("VICON_YAWERR", 19, SITL, vicon_yaw_error, 0), // vicon message type mask AP_GROUPINFO("VICON_TMASK", 20, SITL, vicon_type_mask, 3), // vicon velocity glitch in NED frame AP_GROUPINFO("VICON_VGLI", 21, SITL, vicon_vel_glitch, 0), AP_GROUPINFO("RATE_HZ", 22, SITL, loop_rate_hz, 1200), // count of simulated IMUs AP_GROUPINFO("IMU_COUNT", 23, SITL, imu_count, 2), // @Path: ./SIM_RichenPower.cpp AP_SUBGROUPINFO(richenpower_sim, "RICH_", 31, SITL, RichenPower), // user settable parameters for the 1st barometer AP_GROUPINFO("BARO_RND", 35, SITL, baro_noise[0], 0.2f), AP_GROUPINFO("BARO_DRIFT", 36, SITL, baro_drift[0], 0), AP_GROUPINFO("BARO_DISABLE", 37, SITL, baro_disable[0], 0), AP_GROUPINFO("BARO_GLITCH", 38, SITL, baro_glitch[0], 0), AP_GROUPINFO("BARO_FREEZE", 39, SITL, baro_freeze[0], 0), // user settable parameters for the 2nd barometer AP_GROUPINFO("BARO2_RND", 41, SITL, baro_noise[1], 0.1f), AP_GROUPINFO("BARO2_DRIFT", 42, SITL, baro_drift[1], 0), AP_GROUPINFO("BARO2_DISABL", 43, SITL, baro_disable[1], 0), AP_GROUPINFO("BARO2_GLITCH", 44, SITL, baro_glitch[1], 0), AP_GROUPINFO("BARO2_FREEZE", 45, SITL, baro_freeze[1], 0), // user settable common barometer parameters AP_GROUPINFO("BARO_DELAY", 47, SITL, baro_delay, 0), AP_GROUPINFO("BARO_COUNT", 48, SITL, baro_count, 1), // user settable parameters for the 1st airspeed sensor AP_GROUPINFO("ARSPD_RND", 50, SITL, arspd_noise[0], 2.0), AP_GROUPINFO("ARSPD_OFS", 51, SITL, arspd_offset[0], 2013), AP_GROUPINFO("ARSPD_FAIL", 52, SITL, arspd_fail[0], 0), AP_GROUPINFO("ARSPD_FAILP", 53, SITL, arspd_fail_pressure[0], 0), AP_GROUPINFO("ARSPD_PITOT", 54, SITL, arspd_fail_pitot_pressure[0], 0), // user settable parameters for the 2nd airspeed sensor AP_GROUPINFO("ARSPD2_RND", 56, SITL, arspd_noise[1], 2.0), AP_GROUPINFO("ARSPD2_OFS", 57, SITL, arspd_offset[1], 2013), AP_GROUPINFO("ARSPD2_FAIL", 58, SITL, arspd_fail[1], 0), AP_GROUPINFO("ARSPD2_FAILP", 59, SITL, arspd_fail_pressure[1], 0), AP_GROUPINFO("ARSPD2_PITOT", 60, SITL, arspd_fail_pitot_pressure[1], 0), // user settable common airspeed parameters AP_GROUPINFO("ARSPD_SIGN", 62, SITL, arspd_signflip, 0), AP_GROUPEND }; // GPS SITL parameters const AP_Param::GroupInfo SITL::var_gps[] = { AP_GROUPINFO("GPS_DISABLE", 1, SITL, gps_disable[0], 0), AP_GROUPINFO("GPS_DELAY", 2, SITL, gps_delay[0], 1), AP_GROUPINFO("GPS_TYPE", 3, SITL, gps_type[0], SITL::GPS_TYPE_UBLOX), AP_GROUPINFO("GPS_BYTELOSS", 4, SITL, gps_byteloss[0], 0), AP_GROUPINFO("GPS_NUMSATS", 5, SITL, gps_numsats[0], 10), AP_GROUPINFO("GPS_GLITCH", 6, SITL, gps_glitch[0], 0), AP_GROUPINFO("GPS_HZ", 7, SITL, gps_hertz[0], 5), AP_GROUPINFO("GPS_DRIFTALT", 8, SITL, gps_drift_alt[0], 0), AP_GROUPINFO("GPS_POS1", 9, SITL, gps_pos_offset[0], 0), AP_GROUPINFO("GPS_NOISE", 10, SITL, gps_noise[0], 0), AP_GROUPINFO("GPS_LOCKTIME", 11, SITL, gps_lock_time[0], 0), AP_GROUPINFO("GPS_ALT_OFS", 12, SITL, gps_alt_offset[0], 0), AP_GROUPINFO("GPS_HDG", 13, SITL, gps_hdg_enabled[0], 0), AP_GROUPINFO("GPS_ACC", 14, SITL, gps_accuracy[0], 0.3), AP_GROUPINFO("GPS_VERR", 15, SITL, gps_vel_err[0], 0), AP_GROUPINFO("GPS2_DISABLE", 30, SITL, gps_disable[1], 1), AP_GROUPINFO("GPS2_DELAY", 31, SITL, gps_delay[1], 1), AP_GROUPINFO("GPS2_TYPE", 32, SITL, gps_type[1], SITL::GPS_TYPE_UBLOX), AP_GROUPINFO("GPS2_BYTELOS", 33, SITL, gps_byteloss[1], 0), AP_GROUPINFO("GPS2_NUMSATS", 34, SITL, gps_numsats[1], 10), AP_GROUPINFO("GPS2_GLTCH", 35, SITL, gps_glitch[1], 0), AP_GROUPINFO("GPS2_HZ", 36, SITL, gps_hertz[1], 5), AP_GROUPINFO("GPS2_DRFTALT", 37, SITL, gps_drift_alt[1], 0), AP_GROUPINFO("GPS2_POS1", 38, SITL, gps_pos_offset[1], 0), AP_GROUPINFO("GPS2_NOISE", 39, SITL, gps_noise[1], 0), AP_GROUPINFO("GPS2_LCKTIME", 40, SITL, gps_lock_time[1], 0), AP_GROUPINFO("GPS2_ALT_OFS", 41, SITL, gps_alt_offset[1], 0), AP_GROUPINFO("GPS2_HDG", 42, SITL, gps_hdg_enabled[1], 0), AP_GROUPINFO("GPS2_ACC", 43, SITL, gps_accuracy[1], 0.3), AP_GROUPINFO("GPS2_VERR", 44, SITL, gps_vel_err[1], 0), AP_GROUPEND }; // Mag SITL parameters const AP_Param::GroupInfo SITL::var_mag[] = { AP_GROUPINFO("MAG_RND", 1, SITL, mag_noise, 0), AP_GROUPINFO("MAG_MOT", 2, SITL, mag_mot, 0), AP_GROUPINFO("MAG_DELAY", 3, SITL, mag_delay, 0), AP_GROUPINFO("MAG_OFS", 4, SITL, mag_ofs[0], 0), AP_GROUPINFO("MAG_ALY", 5, SITL, mag_anomaly_ned, 0), AP_GROUPINFO("MAG_ALY_HGT", 6, SITL, mag_anomaly_hgt, 1.0f), AP_GROUPINFO("MAG_DIA", 7, SITL, mag_diag[0], 0), AP_GROUPINFO("MAG_ODI", 8, SITL, mag_offdiag[0], 0), AP_GROUPINFO("MAG_ORIENT", 9, SITL, mag_orient[0], 0), AP_GROUPINFO("MAG1_SCALING", 10, SITL, mag_scaling[0], 1), AP_GROUPINFO("MAG1_DEVID", 11, SITL, mag_devid[0], 97539), AP_GROUPINFO("MAG2_DEVID", 12, SITL, mag_devid[1], 131874), AP_GROUPINFO("MAG3_DEVID", 13, SITL, mag_devid[2], 263178), AP_GROUPINFO("MAG4_DEVID", 14, SITL, mag_devid[3], 97283), AP_GROUPINFO("MAG5_DEVID", 15, SITL, mag_devid[4], 97795), AP_GROUPINFO("MAG6_DEVID", 16, SITL, mag_devid[5], 98051), AP_GROUPINFO("MAG7_DEVID", 17, SITL, mag_devid[6], 0), AP_GROUPINFO("MAG8_DEVID", 18, SITL, mag_devid[7], 0), AP_GROUPINFO("MAG1_FAIL", 26, SITL, mag_fail[0], 0), #if HAL_COMPASS_MAX_SENSORS > 1 AP_GROUPINFO("MAG2_OFS", 19, SITL, mag_ofs[1], 0), AP_GROUPINFO("MAG2_DIA", 20, SITL, mag_diag[1], 0), AP_GROUPINFO("MAG2_ODI", 21, SITL, mag_offdiag[1], 0), AP_GROUPINFO("MAG2_ORIENT", 22, SITL, mag_orient[1], 0), AP_GROUPINFO("MAG2_FAIL", 27, SITL, mag_fail[1], 0), AP_GROUPINFO("MAG2_SCALING", 28, SITL, mag_scaling[1], 1), #endif #if HAL_COMPASS_MAX_SENSORS > 2 AP_GROUPINFO("MAG3_OFS", 23, SITL, mag_ofs[2], 0), AP_GROUPINFO("MAG3_DIA", 24, SITL, mag_diag[2], 0), AP_GROUPINFO("MAG3_ODI", 25, SITL, mag_offdiag[2], 0), AP_GROUPINFO("MAG3_FAIL", 29, SITL, mag_fail[2], 0), AP_GROUPINFO("MAG3_SCALING", 30, SITL, mag_scaling[2], 1), AP_GROUPINFO("MAG3_ORIENT", 36, SITL, mag_orient[2], 0), #endif AP_GROUPEND }; /* report SITL state via MAVLink */ void SITL::simstate_send(mavlink_channel_t chan) { float yaw; // convert to same conventions as DCM yaw = state.yawDeg; if (yaw > 180) { yaw -= 360; } mavlink_msg_simstate_send(chan, ToRad(state.rollDeg), ToRad(state.pitchDeg), ToRad(yaw), state.xAccel, state.yAccel, state.zAccel, radians(state.rollRate), radians(state.pitchRate), radians(state.yawRate), state.latitude*1.0e7, state.longitude*1.0e7); } /* report SITL state to AP_Logger */ void SITL::Log_Write_SIMSTATE() { float yaw; // convert to same conventions as DCM yaw = state.yawDeg; if (yaw > 180) { yaw -= 360; } struct log_AHRS pkt = { LOG_PACKET_HEADER_INIT(LOG_SIMSTATE_MSG), time_us : AP_HAL::micros64(), roll : (int16_t)(state.rollDeg*100), pitch : (int16_t)(state.pitchDeg*100), yaw : (uint16_t)(wrap_360_cd(yaw*100)), alt : (float)state.altitude, lat : (int32_t)(state.latitude*1.0e7), lng : (int32_t)(state.longitude*1.0e7), q1 : state.quaternion.q1, q2 : state.quaternion.q2, q3 : state.quaternion.q3, q4 : state.quaternion.q4, }; AP::logger().WriteBlock(&pkt, sizeof(pkt)); } /* convert a set of roll rates from earth frame to body frame output values are in radians/second */ void SITL::convert_body_frame(double rollDeg, double pitchDeg, double rollRate, double pitchRate, double yawRate, double *p, double *q, double *r) { double phi, theta, phiDot, thetaDot, psiDot; phi = ToRad(rollDeg); theta = ToRad(pitchDeg); phiDot = ToRad(rollRate); thetaDot = ToRad(pitchRate); psiDot = ToRad(yawRate); *p = phiDot - psiDot*sin(theta); *q = cos(phi)*thetaDot + sin(phi)*psiDot*cos(theta); *r = cos(phi)*psiDot*cos(theta) - sin(phi)*thetaDot; } /* convert angular velocities from body frame to earth frame. all inputs and outputs are in radians/s */ Vector3f SITL::convert_earth_frame(const Matrix3f &dcm, const Vector3f &gyro) { float p = gyro.x; float q = gyro.y; float r = gyro.z; float phi, theta, psi; dcm.to_euler(&phi, &theta, &psi); float phiDot = p + tanf(theta)*(q*sinf(phi) + r*cosf(phi)); float thetaDot = q*cosf(phi) - r*sinf(phi); if (fabsf(cosf(theta)) < 1.0e-20f) { theta += 1.0e-10f; } float psiDot = (q*sinf(phi) + r*cosf(phi))/cosf(theta); return Vector3f(phiDot, thetaDot, psiDot); } // get the rangefinder reading for the desired rotation, returns -1 for no data float SITL::get_rangefinder(uint8_t instance) { if (instance < RANGEFINDER_MAX_INSTANCES) { return state.rangefinder_m[instance]; } return -1; }; } // namespace SITL namespace AP { SITL::SITL *sitl() { return SITL::SITL::get_singleton(); } }; #endif // CONFIG_HAL_BOARD