aboutsummaryrefslogtreecommitdiff
path: root/src/modules
diff options
context:
space:
mode:
Diffstat (limited to 'src/modules')
-rwxr-xr-xsrc/modules/attitude_estimator_ekf/attitude_estimator_ekf_main.cpp12
-rwxr-xr-xsrc/modules/attitude_estimator_ekf/attitude_estimator_ekf_params.c15
-rwxr-xr-xsrc/modules/attitude_estimator_ekf/attitude_estimator_ekf_params.h1
-rwxr-xr-xsrc/modules/attitude_estimator_so3/attitude_estimator_so3_main.cpp21
-rw-r--r--src/modules/bottle_drop/bottle_drop.cpp906
-rw-r--r--src/modules/bottle_drop/bottle_drop_params.c131
-rw-r--r--src/modules/bottle_drop/module.mk45
-rw-r--r--src/modules/commander/accelerometer_calibration.cpp56
-rw-r--r--src/modules/commander/accelerometer_calibration.h3
-rw-r--r--src/modules/commander/airspeed_calibration.cpp145
-rw-r--r--src/modules/commander/airspeed_calibration.h2
-rw-r--r--src/modules/commander/baro_calibration.cpp2
-rw-r--r--src/modules/commander/baro_calibration.h2
-rw-r--r--src/modules/commander/calibration_messages.h3
-rw-r--r--src/modules/commander/calibration_routines.cpp12
-rw-r--r--src/modules/commander/calibration_routines.h3
-rw-r--r--src/modules/commander/commander.cpp1422
-rw-r--r--src/modules/commander/commander_helper.cpp33
-rw-r--r--src/modules/commander/commander_helper.h12
-rw-r--r--src/modules/commander/commander_params.c91
-rw-r--r--src/modules/commander/commander_tests/commander_tests.cpp4
-rw-r--r--src/modules/commander/commander_tests/state_machine_helper_test.cpp226
-rw-r--r--src/modules/commander/commander_tests/state_machine_helper_test.h2
-rw-r--r--src/modules/commander/gyro_calibration.cpp9
-rw-r--r--src/modules/commander/mag_calibration.cpp6
-rw-r--r--src/modules/commander/mag_calibration.h2
-rw-r--r--src/modules/commander/px4_custom_mode.h2
-rw-r--r--src/modules/commander/state_machine_helper.cpp734
-rw-r--r--src/modules/commander/state_machine_helper.h28
-rw-r--r--src/modules/controllib/block/Block.hpp5
-rw-r--r--src/modules/controllib/blocks.cpp16
-rw-r--r--src/modules/controllib/blocks.hpp19
-rw-r--r--src/modules/controllib/module.mk2
-rw-r--r--src/modules/dataman/dataman.c73
-rw-r--r--src/modules/dataman/dataman.h18
-rw-r--r--src/modules/dataman/module.mk2
-rw-r--r--src/modules/ekf_att_pos_estimator/ekf_att_pos_estimator_main.cpp1062
-rw-r--r--src/modules/ekf_att_pos_estimator/estimator_21states.cpp2142
-rw-r--r--src/modules/ekf_att_pos_estimator/estimator_21states.h (renamed from src/modules/ekf_att_pos_estimator/estimator.h)123
-rw-r--r--src/modules/ekf_att_pos_estimator/estimator_23states.cpp (renamed from src/modules/ekf_att_pos_estimator/estimator.cpp)1503
-rw-r--r--src/modules/ekf_att_pos_estimator/estimator_23states.h371
-rw-r--r--src/modules/ekf_att_pos_estimator/estimator_utilities.cpp162
-rw-r--r--src/modules/ekf_att_pos_estimator/estimator_utilities.h93
-rw-r--r--src/modules/ekf_att_pos_estimator/module.mk5
-rw-r--r--src/modules/fw_att_control/fw_att_control_main.cpp112
-rw-r--r--src/modules/fw_att_control/module.mk2
-rw-r--r--src/modules/fw_pos_control_l1/fw_pos_control_l1_main.cpp787
-rw-r--r--src/modules/fw_pos_control_l1/fw_pos_control_l1_params.c222
-rw-r--r--src/modules/fw_pos_control_l1/landingslope.cpp16
-rw-r--r--src/modules/fw_pos_control_l1/landingslope.h8
-rw-r--r--src/modules/fw_pos_control_l1/module.mk9
-rw-r--r--src/modules/fw_pos_control_l1/mtecs/limitoverride.cpp71
-rw-r--r--src/modules/fw_pos_control_l1/mtecs/limitoverride.h107
-rw-r--r--src/modules/fw_pos_control_l1/mtecs/mTecs.cpp317
-rw-r--r--src/modules/fw_pos_control_l1/mtecs/mTecs.h156
-rw-r--r--src/modules/fw_pos_control_l1/mtecs/mTecs_blocks.h220
-rw-r--r--src/modules/fw_pos_control_l1/mtecs/mTecs_params.c426
-rw-r--r--src/modules/gpio_led/gpio_led.c9
-rw-r--r--src/modules/gpio_led/module.mk2
-rw-r--r--src/modules/mavlink/mavlink.c13
-rw-r--r--src/modules/mavlink/mavlink_bridge_header.h7
-rw-r--r--src/modules/mavlink/mavlink_ftp.cpp853
-rw-r--r--src/modules/mavlink/mavlink_ftp.h180
-rw-r--r--src/modules/mavlink/mavlink_main.cpp1494
-rw-r--r--src/modules/mavlink/mavlink_main.h376
-rw-r--r--src/modules/mavlink/mavlink_messages.cpp1877
-rw-r--r--src/modules/mavlink/mavlink_messages.h4
-rw-r--r--src/modules/mavlink/mavlink_mission.cpp834
-rw-r--r--src/modules/mavlink/mavlink_mission.h196
-rw-r--r--src/modules/mavlink/mavlink_orb_subscription.cpp32
-rw-r--r--src/modules/mavlink/mavlink_orb_subscription.h4
-rw-r--r--src/modules/mavlink/mavlink_parameters.cpp197
-rw-r--r--src/modules/mavlink/mavlink_parameters.h115
-rw-r--r--src/modules/mavlink/mavlink_rate_limiter.cpp2
-rw-r--r--src/modules/mavlink/mavlink_receiver.cpp626
-rw-r--r--src/modules/mavlink/mavlink_receiver.h29
-rw-r--r--src/modules/mavlink/mavlink_stream.cpp29
-rw-r--r--src/modules/mavlink/mavlink_stream.h38
-rw-r--r--src/modules/mavlink/mavlink_tests/mavlink_ftp_test.cpp761
-rw-r--r--src/modules/mavlink/mavlink_tests/mavlink_ftp_test.h108
-rw-r--r--src/modules/mavlink/mavlink_tests/mavlink_ftp_test_data.py7
-rw-r--r--src/modules/mavlink/mavlink_tests/mavlink_tests.cpp (renamed from src/modules/navigator/navigator_state.h)28
-rw-r--r--src/modules/mavlink/mavlink_tests/module.mk50
-rw-r--r--src/modules/mavlink/module.mk6
-rw-r--r--src/modules/mavlink/waypoints.h111
-rw-r--r--src/modules/mc_att_control/mc_att_control_main.cpp218
-rw-r--r--src/modules/mc_pos_control/mc_pos_control_main.cpp491
-rw-r--r--src/modules/navigator/datalinkloss.cpp231
-rw-r--r--src/modules/navigator/datalinkloss.h98
-rw-r--r--src/modules/navigator/datalinkloss_params.c126
-rw-r--r--src/modules/navigator/enginefailure.cpp149
-rw-r--r--src/modules/navigator/enginefailure.h83
-rw-r--r--src/modules/navigator/geofence.cpp73
-rw-r--r--src/modules/navigator/geofence.h63
-rw-r--r--src/modules/navigator/geofence_params.c36
-rw-r--r--src/modules/navigator/gpsfailure.cpp178
-rw-r--r--src/modules/navigator/gpsfailure.h97
-rw-r--r--src/modules/navigator/gpsfailure_params.c97
-rw-r--r--src/modules/navigator/loiter.cpp93
-rw-r--r--src/modules/navigator/loiter.h (renamed from src/modules/mavlink/mavlink_commands.h)45
-rw-r--r--src/modules/navigator/mission.cpp735
-rw-r--r--src/modules/navigator/mission.h179
-rw-r--r--src/modules/navigator/mission_block.cpp264
-rw-r--r--src/modules/navigator/mission_block.h102
-rw-r--r--src/modules/navigator/mission_feasibility_checker.cpp32
-rw-r--r--src/modules/navigator/mission_params.c97
-rw-r--r--src/modules/navigator/module.mk21
-rw-r--r--src/modules/navigator/navigator.h280
-rw-r--r--src/modules/navigator/navigator_main.cpp1543
-rw-r--r--src/modules/navigator/navigator_mission.cpp319
-rw-r--r--src/modules/navigator/navigator_mission.h105
-rw-r--r--src/modules/navigator/navigator_mode.cpp98
-rw-r--r--src/modules/navigator/navigator_mode.h99
-rw-r--r--src/modules/navigator/navigator_params.c103
-rw-r--r--src/modules/navigator/rcloss.cpp184
-rw-r--r--src/modules/navigator/rcloss.h88
-rw-r--r--src/modules/navigator/rcloss_params.c60
-rw-r--r--src/modules/navigator/rtl.cpp317
-rw-r--r--src/modules/navigator/rtl.h96
-rw-r--r--src/modules/navigator/rtl_params.c98
-rw-r--r--src/modules/position_estimator_inav/position_estimator_inav_main.c213
-rw-r--r--src/modules/position_estimator_inav/position_estimator_inav_params.c234
-rw-r--r--src/modules/position_estimator_inav/position_estimator_inav_params.h21
-rw-r--r--src/modules/px4iofirmware/controls.c95
-rw-r--r--src/modules/px4iofirmware/dsm.c10
-rw-r--r--src/modules/px4iofirmware/i2c.c5
-rw-r--r--src/modules/px4iofirmware/mixer.cpp64
-rw-r--r--src/modules/px4iofirmware/module.mk3
-rw-r--r--src/modules/px4iofirmware/protocol.h15
-rw-r--r--src/modules/px4iofirmware/px4io.c72
-rw-r--r--src/modules/px4iofirmware/px4io.h7
-rw-r--r--src/modules/px4iofirmware/registers.c61
-rw-r--r--src/modules/px4iofirmware/sbus.c101
-rw-r--r--src/modules/sdlog2/module.mk2
-rw-r--r--src/modules/sdlog2/sdlog2.c309
-rw-r--r--src/modules/sdlog2/sdlog2_format.h8
-rw-r--r--src/modules/sdlog2/sdlog2_messages.h145
-rw-r--r--src/modules/sensors/module.mk2
-rw-r--r--src/modules/sensors/sensor_params.c92
-rw-r--r--src/modules/sensors/sensors.cpp260
-rw-r--r--src/modules/systemlib/board_serial.c8
-rw-r--r--src/modules/systemlib/board_serial.h2
-rw-r--r--src/modules/systemlib/circuit_breaker.c146
-rw-r--r--src/modules/systemlib/circuit_breaker.h68
-rw-r--r--src/modules/systemlib/cpuload.c2
-rw-r--r--src/modules/systemlib/err.c2
-rw-r--r--src/modules/systemlib/hx_stream.c2
-rw-r--r--src/modules/systemlib/mcu_version.c109
-rw-r--r--src/modules/systemlib/mcu_version.h (renamed from src/modules/mavlink/util.h)31
-rw-r--r--src/modules/systemlib/mixer/mixer.h21
-rw-r--r--src/modules/systemlib/mixer/mixer_load.c2
-rw-r--r--src/modules/systemlib/mixer/mixer_multirotor.cpp26
-rw-r--r--src/modules/systemlib/module.mk7
-rw-r--r--src/modules/systemlib/otp.c2
-rw-r--r--src/modules/systemlib/otp.h2
-rw-r--r--src/modules/systemlib/param/param.c5
-rw-r--r--src/modules/systemlib/param/param.h2
-rw-r--r--src/modules/systemlib/perf_counter.h2
-rw-r--r--src/modules/systemlib/pwm_limit/pwm_limit.c1
-rw-r--r--src/modules/systemlib/rc_check.c13
-rw-r--r--src/modules/systemlib/state_table.h27
-rw-r--r--src/modules/systemlib/systemlib.c6
-rw-r--r--src/modules/uORB/Publication.cpp2
-rw-r--r--src/modules/uORB/Subscription.cpp2
-rw-r--r--src/modules/uORB/module.mk2
-rw-r--r--src/modules/uORB/objects_common.cpp41
-rw-r--r--src/modules/uORB/topics/actuator_armed.h3
-rw-r--r--src/modules/uORB/topics/differential_pressure.h2
-rw-r--r--src/modules/uORB/topics/esc_status.h19
-rw-r--r--src/modules/uORB/topics/estimator_status.h6
-rw-r--r--src/modules/uORB/topics/home_position.h5
-rw-r--r--src/modules/uORB/topics/manual_control_setpoint.h15
-rw-r--r--src/modules/uORB/topics/mission.h23
-rw-r--r--src/modules/uORB/topics/mission_result.h10
-rw-r--r--src/modules/uORB/topics/multirotor_motor_limits.h69
-rw-r--r--src/modules/uORB/topics/offboard_control_setpoint.h203
-rw-r--r--src/modules/uORB/topics/position_setpoint_triplet.h38
-rw-r--r--src/modules/uORB/topics/rc_channels.h72
-rw-r--r--src/modules/uORB/topics/satellite_info.h89
-rw-r--r--src/modules/uORB/topics/sensor_combined.h24
-rw-r--r--src/modules/uORB/topics/tecs_status.h94
-rw-r--r--src/modules/uORB/topics/telemetry_status.h28
-rw-r--r--src/modules/uORB/topics/test_motor.h70
-rw-r--r--src/modules/uORB/topics/vehicle_attitude_setpoint.h2
-rw-r--r--src/modules/uORB/topics/vehicle_command.h23
-rw-r--r--src/modules/uORB/topics/vehicle_control_mode.h2
-rw-r--r--src/modules/uORB/topics/vehicle_force_setpoint.h65
-rw-r--r--src/modules/uORB/topics/vehicle_global_position.h19
-rw-r--r--src/modules/uORB/topics/vehicle_gps_position.h16
-rw-r--r--src/modules/uORB/topics/vehicle_local_position_setpoint.h3
-rw-r--r--src/modules/uORB/topics/vehicle_status.h81
-rw-r--r--src/modules/uORB/topics/vision_position_estimate.h82
-rw-r--r--src/modules/uORB/topics/wind_estimate.h68
-rw-r--r--src/modules/uavcan/.gitignore1
-rw-r--r--src/modules/uavcan/actuators/esc.cpp175
-rw-r--r--src/modules/uavcan/actuators/esc.hpp112
-rw-r--r--src/modules/uavcan/module.mk79
-rw-r--r--src/modules/uavcan/sensors/baro.cpp113
-rw-r--r--src/modules/uavcan/sensors/baro.hpp68
-rw-r--r--src/modules/uavcan/sensors/gnss.cpp173
-rw-r--r--src/modules/uavcan/sensors/gnss.hpp85
-rw-r--r--src/modules/uavcan/sensors/mag.cpp150
-rw-r--r--src/modules/uavcan/sensors/mag.hpp70
-rw-r--r--src/modules/uavcan/sensors/sensor_bridge.cpp161
-rw-r--r--src/modules/uavcan/sensors/sensor_bridge.hpp133
-rw-r--r--src/modules/uavcan/uavcan_clock.cpp (renamed from src/modules/mavlink/mavlink_commands.cpp)63
-rw-r--r--src/modules/uavcan/uavcan_main.cpp647
-rw-r--r--src/modules/uavcan/uavcan_main.hpp130
-rw-r--r--src/modules/uavcan/uavcan_params.c73
-rw-r--r--src/modules/unit_test/module.mk1
-rw-r--r--src/modules/unit_test/unit_test.cpp27
-rw-r--r--src/modules/unit_test/unit_test.h111
212 files changed, 25603 insertions, 7017 deletions
diff --git a/src/modules/attitude_estimator_ekf/attitude_estimator_ekf_main.cpp b/src/modules/attitude_estimator_ekf/attitude_estimator_ekf_main.cpp
index 5bcce1b4d..e2dbc30dd 100755
--- a/src/modules/attitude_estimator_ekf/attitude_estimator_ekf_main.cpp
+++ b/src/modules/attitude_estimator_ekf/attitude_estimator_ekf_main.cpp
@@ -206,10 +206,6 @@ const unsigned int loop_interval_alarm = 6500; // loop interval in microseconds
0, 0, 1.f
}; /**< init: identity matrix */
- // print text
- printf("Extended Kalman Filter Attitude Estimator initialized..\n\n");
- fflush(stdout);
-
int overloadcounter = 19;
/* Initialize filter */
@@ -342,7 +338,7 @@ const unsigned int loop_interval_alarm = 6500; // loop interval in microseconds
if (gps_updated) {
orb_copy(ORB_ID(vehicle_gps_position), sub_gps, &gps);
- if (gps.eph_m < 20.0f && hrt_elapsed_time(&gps.timestamp_position) < 1000000) {
+ if (gps.eph < 20.0f && hrt_elapsed_time(&gps.timestamp_position) < 1000000) {
mag_decl = math::radians(get_mag_declination(gps.lat / 1e7f, gps.lon / 1e7f));
/* update mag declination rotation matrix */
@@ -401,7 +397,7 @@ const unsigned int loop_interval_alarm = 6500; // loop interval in microseconds
hrt_abstime vel_t = 0;
bool vel_valid = false;
- if (ekf_params.acc_comp == 1 && gps.fix_type >= 3 && gps.eph_m < 10.0f && gps.vel_ned_valid && hrt_absolute_time() < gps.timestamp_velocity + 500000) {
+ if (ekf_params.acc_comp == 1 && gps.fix_type >= 3 && gps.eph < 10.0f && gps.vel_ned_valid && hrt_absolute_time() < gps.timestamp_velocity + 500000) {
vel_valid = true;
if (gps_updated) {
vel_t = gps.timestamp_velocity;
@@ -410,7 +406,7 @@ const unsigned int loop_interval_alarm = 6500; // loop interval in microseconds
vel(2) = gps.vel_d_m_s;
}
- } else if (ekf_params.acc_comp == 2 && gps.eph_m < 5.0f && global_pos.timestamp != 0 && hrt_absolute_time() < global_pos.timestamp + 20000) {
+ } else if (ekf_params.acc_comp == 2 && gps.eph < 5.0f && global_pos.timestamp != 0 && hrt_absolute_time() < global_pos.timestamp + 20000) {
vel_valid = true;
if (global_pos_updated) {
vel_t = global_pos.timestamp;
@@ -477,7 +473,7 @@ const unsigned int loop_interval_alarm = 6500; // loop interval in microseconds
parameters_update(&ekf_param_handles, &ekf_params);
/* update mag declination rotation matrix */
- if (gps.eph_m < 20.0f && hrt_elapsed_time(&gps.timestamp_position) < 1000000) {
+ if (gps.eph < 20.0f && hrt_elapsed_time(&gps.timestamp_position) < 1000000) {
mag_decl = math::radians(get_mag_declination(gps.lat / 1e7f, gps.lon / 1e7f));
} else {
diff --git a/src/modules/attitude_estimator_ekf/attitude_estimator_ekf_params.c b/src/modules/attitude_estimator_ekf/attitude_estimator_ekf_params.c
index 3ff3d9922..bc0e3b93a 100755
--- a/src/modules/attitude_estimator_ekf/attitude_estimator_ekf_params.c
+++ b/src/modules/attitude_estimator_ekf/attitude_estimator_ekf_params.c
@@ -61,11 +61,6 @@ PARAM_DEFINE_FLOAT(EKF_ATT_V4_R2, 100.0f);
/* offset estimation - UNUSED */
PARAM_DEFINE_FLOAT(EKF_ATT_V4_R3, 0.0f);
-/* offsets in roll, pitch and yaw of sensor plane and body */
-PARAM_DEFINE_FLOAT(ATT_ROLL_OFF3, 0.0f);
-PARAM_DEFINE_FLOAT(ATT_PITCH_OFF3, 0.0f);
-PARAM_DEFINE_FLOAT(ATT_YAW_OFF3, 0.0f);
-
/* magnetic declination, in degrees */
PARAM_DEFINE_FLOAT(ATT_MAG_DECL, 0.0f);
@@ -85,10 +80,6 @@ int parameters_init(struct attitude_estimator_ekf_param_handles *h)
h->r2 = param_find("EKF_ATT_V4_R2");
h->r3 = param_find("EKF_ATT_V4_R3");
- h->roll_off = param_find("ATT_ROLL_OFF3");
- h->pitch_off = param_find("ATT_PITCH_OFF3");
- h->yaw_off = param_find("ATT_YAW_OFF3");
-
h->mag_decl = param_find("ATT_MAG_DECL");
h->acc_comp = param_find("ATT_ACC_COMP");
@@ -109,12 +100,8 @@ int parameters_update(const struct attitude_estimator_ekf_param_handles *h, stru
param_get(h->r2, &(p->r[2]));
param_get(h->r3, &(p->r[3]));
- param_get(h->roll_off, &(p->roll_off));
- param_get(h->pitch_off, &(p->pitch_off));
- param_get(h->yaw_off, &(p->yaw_off));
-
param_get(h->mag_decl, &(p->mag_decl));
- p->mag_decl *= M_PI / 180.0f;
+ p->mag_decl *= M_PI_F / 180.0f;
param_get(h->acc_comp, &(p->acc_comp));
diff --git a/src/modules/attitude_estimator_ekf/attitude_estimator_ekf_params.h b/src/modules/attitude_estimator_ekf/attitude_estimator_ekf_params.h
index 74a141609..5985541ca 100755
--- a/src/modules/attitude_estimator_ekf/attitude_estimator_ekf_params.h
+++ b/src/modules/attitude_estimator_ekf/attitude_estimator_ekf_params.h
@@ -54,7 +54,6 @@ struct attitude_estimator_ekf_params {
struct attitude_estimator_ekf_param_handles {
param_t r0, r1, r2, r3;
param_t q0, q1, q2, q3, q4;
- param_t roll_off, pitch_off, yaw_off;
param_t mag_decl;
param_t acc_comp;
};
diff --git a/src/modules/attitude_estimator_so3/attitude_estimator_so3_main.cpp b/src/modules/attitude_estimator_so3/attitude_estimator_so3_main.cpp
index e55b01160..e49027e47 100755
--- a/src/modules/attitude_estimator_so3/attitude_estimator_so3_main.cpp
+++ b/src/modules/attitude_estimator_so3/attitude_estimator_so3_main.cpp
@@ -392,8 +392,6 @@ void NonlinearSO3AHRSupdate(float gx, float gy, float gz, float ax, float ay, fl
*/
int attitude_estimator_so3_thread_main(int argc, char *argv[])
{
- const unsigned int loop_interval_alarm = 6500; // loop interval in microseconds
-
//! Time constant
float dt = 0.005f;
@@ -438,11 +436,9 @@ int attitude_estimator_so3_thread_main(int argc, char *argv[])
orb_advert_t att_pub = orb_advertise(ORB_ID(vehicle_attitude), &att);
int loopcounter = 0;
- int printcounter = 0;
thread_running = true;
- float sensor_update_hz[3] = {0.0f, 0.0f, 0.0f};
// XXX write this out to perf regs
/* keep track of sensor updates */
@@ -513,7 +509,7 @@ int attitude_estimator_so3_thread_main(int argc, char *argv[])
gyro_offsets[0] /= offset_count;
gyro_offsets[1] /= offset_count;
gyro_offsets[2] /= offset_count;
- warnx("gyro initialized, offsets: %.5f %.5f %.5f", gyro_offsets[0], gyro_offsets[1], gyro_offsets[2]);
+ warnx("gyro initialized, offsets: %.5f %.5f %.5f", (double)gyro_offsets[0], (double)gyro_offsets[1], (double)gyro_offsets[2]);
}
} else {
@@ -523,12 +519,9 @@ int attitude_estimator_so3_thread_main(int argc, char *argv[])
/* Calculate data time difference in seconds */
dt = (raw.timestamp - last_measurement) / 1000000.0f;
last_measurement = raw.timestamp;
- uint8_t update_vect[3] = {0, 0, 0};
/* Fill in gyro measurements */
if (sensor_last_timestamp[0] != raw.timestamp) {
- update_vect[0] = 1;
- sensor_update_hz[0] = 1e6f / (raw.timestamp - sensor_last_timestamp[0]);
sensor_last_timestamp[0] = raw.timestamp;
}
@@ -538,8 +531,6 @@ int attitude_estimator_so3_thread_main(int argc, char *argv[])
/* update accelerometer measurements */
if (sensor_last_timestamp[1] != raw.accelerometer_timestamp) {
- update_vect[1] = 1;
- sensor_update_hz[1] = 1e6f / (raw.timestamp - sensor_last_timestamp[1]);
sensor_last_timestamp[1] = raw.accelerometer_timestamp;
}
@@ -549,8 +540,6 @@ int attitude_estimator_so3_thread_main(int argc, char *argv[])
/* update magnetometer measurements */
if (sensor_last_timestamp[2] != raw.magnetometer_timestamp) {
- update_vect[2] = 1;
- sensor_update_hz[2] = 1e6f / (raw.timestamp - sensor_last_timestamp[2]);
sensor_last_timestamp[2] = raw.magnetometer_timestamp;
}
@@ -569,8 +558,6 @@ int attitude_estimator_so3_thread_main(int argc, char *argv[])
continue;
}
- uint64_t timing_start = hrt_absolute_time();
-
// NOTE : Accelerometer is reversed.
// Because proper mount of PX4 will give you a reversed accelerometer readings.
NonlinearSO3AHRSupdate(gyro[0], gyro[1], gyro[2],
@@ -609,9 +596,9 @@ int attitude_estimator_so3_thread_main(int argc, char *argv[])
/* due to inputs or numerical failure the output is invalid, skip it */
// Due to inputs or numerical failure the output is invalid
warnx("infinite euler angles, rotation matrix:");
- warnx("%.3f %.3f %.3f", Rot_matrix[0], Rot_matrix[1], Rot_matrix[2]);
- warnx("%.3f %.3f %.3f", Rot_matrix[3], Rot_matrix[4], Rot_matrix[5]);
- warnx("%.3f %.3f %.3f", Rot_matrix[6], Rot_matrix[7], Rot_matrix[8]);
+ warnx("%.3f %.3f %.3f", (double)Rot_matrix[0], (double)Rot_matrix[1], (double)Rot_matrix[2]);
+ warnx("%.3f %.3f %.3f", (double)Rot_matrix[3], (double)Rot_matrix[4], (double)Rot_matrix[5]);
+ warnx("%.3f %.3f %.3f", (double)Rot_matrix[6], (double)Rot_matrix[7], (double)Rot_matrix[8]);
// Don't publish anything
continue;
}
diff --git a/src/modules/bottle_drop/bottle_drop.cpp b/src/modules/bottle_drop/bottle_drop.cpp
new file mode 100644
index 000000000..e0bcbc6e9
--- /dev/null
+++ b/src/modules/bottle_drop/bottle_drop.cpp
@@ -0,0 +1,906 @@
+/****************************************************************************
+ *
+ * Copyright (c) 2013, 2014 PX4 Development Team. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/**
+ * @file bottle_drop.cpp
+ *
+ * Bottle drop module for Outback Challenge 2014, Team Swiss Fang
+ *
+ * @author Dominik Juchli <juchlid@ethz.ch>
+ * @author Julian Oes <joes@student.ethz.ch>
+ */
+
+#include <nuttx/config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <math.h>
+#include <poll.h>
+#include <time.h>
+#include <sys/ioctl.h>
+#include <drivers/device/device.h>
+#include <drivers/drv_hrt.h>
+#include <arch/board/board.h>
+#include <uORB/uORB.h>
+#include <uORB/topics/vehicle_command.h>
+#include <uORB/topics/actuator_controls.h>
+#include <uORB/topics/wind_estimate.h>
+#include <uORB/topics/parameter_update.h>
+#include <systemlib/systemlib.h>
+#include <systemlib/param/param.h>
+#include <systemlib/err.h>
+#include <geo/geo.h>
+#include <dataman/dataman.h>
+#include <mathlib/mathlib.h>
+#include <mavlink/mavlink_log.h>
+
+
+/**
+ * bottle_drop app start / stop handling function
+ *
+ * @ingroup apps
+ */
+extern "C" __EXPORT int bottle_drop_main(int argc, char *argv[]);
+
+class BottleDrop
+{
+public:
+ /**
+ * Constructor
+ */
+ BottleDrop();
+
+ /**
+ * Destructor, also kills task.
+ */
+ ~BottleDrop();
+
+ /**
+ * Start the task.
+ *
+ * @return OK on success.
+ */
+ int start();
+
+ /**
+ * Display status.
+ */
+ void status();
+
+ void open_bay();
+ void close_bay();
+ void drop();
+ void lock_release();
+
+private:
+ bool _task_should_exit; /**< if true, task should exit */
+ int _main_task; /**< handle for task */
+ int _mavlink_fd;
+
+ int _command_sub;
+ int _wind_estimate_sub;
+ struct vehicle_command_s _command;
+ struct vehicle_global_position_s _global_pos;
+ map_projection_reference_s ref;
+
+ orb_advert_t _actuator_pub;
+ struct actuator_controls_s _actuators;
+
+ bool _drop_approval;
+ hrt_abstime _doors_opened;
+ hrt_abstime _drop_time;
+
+ float _alt_clearance;
+
+ struct position_s {
+ double lat; ///< degrees
+ double lon; ///< degrees
+ float alt; ///< m
+ } _target_position, _drop_position;
+
+ enum DROP_STATE {
+ DROP_STATE_INIT = 0,
+ DROP_STATE_TARGET_VALID,
+ DROP_STATE_TARGET_SET,
+ DROP_STATE_BAY_OPEN,
+ DROP_STATE_DROPPED,
+ DROP_STATE_BAY_CLOSED
+ } _drop_state;
+
+ struct mission_s _onboard_mission;
+ orb_advert_t _onboard_mission_pub;
+
+ void task_main();
+
+ void handle_command(struct vehicle_command_s *cmd);
+
+ void answer_command(struct vehicle_command_s *cmd, enum VEHICLE_CMD_RESULT result);
+
+ /**
+ * Set the actuators
+ */
+ int actuators_publish();
+
+ /**
+ * Shim for calling task_main from task_create.
+ */
+ static void task_main_trampoline(int argc, char *argv[]);
+};
+
+namespace bottle_drop
+{
+BottleDrop *g_bottle_drop;
+}
+
+BottleDrop::BottleDrop() :
+
+ _task_should_exit(false),
+ _main_task(-1),
+ _mavlink_fd(-1),
+ _command_sub(-1),
+ _wind_estimate_sub(-1),
+ _command {},
+ _global_pos {},
+ ref {},
+ _actuator_pub(-1),
+ _actuators {},
+ _drop_approval(false),
+ _doors_opened(0),
+ _drop_time(0),
+ _alt_clearance(70.0f),
+ _target_position {},
+ _drop_position {},
+ _drop_state(DROP_STATE_INIT),
+ _onboard_mission {},
+ _onboard_mission_pub(-1)
+{
+}
+
+BottleDrop::~BottleDrop()
+{
+ if (_main_task != -1) {
+
+ /* task wakes up every 100ms or so at the longest */
+ _task_should_exit = true;
+
+ /* wait for a second for the task to quit at our request */
+ unsigned i = 0;
+
+ do {
+ /* wait 20ms */
+ usleep(20000);
+
+ /* if we have given up, kill it */
+ if (++i > 50) {
+ task_delete(_main_task);
+ break;
+ }
+ } while (_main_task != -1);
+ }
+
+ bottle_drop::g_bottle_drop = nullptr;
+}
+
+int
+BottleDrop::start()
+{
+ ASSERT(_main_task == -1);
+
+ /* start the task */
+ _main_task = task_spawn_cmd("bottle_drop",
+ SCHED_DEFAULT,
+ SCHED_PRIORITY_DEFAULT + 15,
+ 1500,
+ (main_t)&BottleDrop::task_main_trampoline,
+ nullptr);
+
+ if (_main_task < 0) {
+ warn("task start failed");
+ return -errno;
+ }
+
+ return OK;
+}
+
+
+void
+BottleDrop::status()
+{
+ warnx("drop state: %d", _drop_state);
+}
+
+void
+BottleDrop::open_bay()
+{
+ _actuators.control[0] = -1.0f;
+ _actuators.control[1] = 1.0f;
+
+ if (_doors_opened == 0) {
+ _doors_opened = hrt_absolute_time();
+ }
+ warnx("open doors");
+
+ actuators_publish();
+
+ usleep(500 * 1000);
+}
+
+void
+BottleDrop::close_bay()
+{
+ // closed door and locked survival kit
+ _actuators.control[0] = 1.0f;
+ _actuators.control[1] = -1.0f;
+
+ _doors_opened = 0;
+
+ actuators_publish();
+
+ // delay until the bay is closed
+ usleep(500 * 1000);
+}
+
+void
+BottleDrop::drop()
+{
+
+ // update drop actuator, wait 0.5s until the doors are open before dropping
+ hrt_abstime starttime = hrt_absolute_time();
+
+ // force the door open if we have to
+ if (_doors_opened == 0) {
+ open_bay();
+ }
+
+ while (hrt_elapsed_time(&_doors_opened) < 500 * 1000 && hrt_elapsed_time(&starttime) < 2000000) {
+ usleep(50000);
+ warnx("delayed by door!");
+ }
+
+ _actuators.control[2] = 1.0f;
+
+ _drop_time = hrt_absolute_time();
+ actuators_publish();
+
+ warnx("dropping now");
+
+ // Give it time to drop
+ usleep(1000 * 1000);
+}
+
+void
+BottleDrop::lock_release()
+{
+ _actuators.control[2] = -1.0f;
+ actuators_publish();
+
+ warnx("closing release");
+}
+
+int
+BottleDrop::actuators_publish()
+{
+ _actuators.timestamp = hrt_absolute_time();
+
+ // lazily publish _actuators only once available
+ if (_actuator_pub > 0) {
+ return orb_publish(ORB_ID(actuator_controls_2), _actuator_pub, &_actuators);
+
+ } else {
+ _actuator_pub = orb_advertise(ORB_ID(actuator_controls_2), &_actuators);
+ if (_actuator_pub > 0) {
+ return OK;
+ } else {
+ return -1;
+ }
+ }
+}
+
+void
+BottleDrop::task_main()
+{
+
+ _mavlink_fd = open(MAVLINK_LOG_DEVICE, 0);
+ mavlink_log_info(_mavlink_fd, "[bottle_drop] started");
+
+ _command_sub = orb_subscribe(ORB_ID(vehicle_command));
+ _wind_estimate_sub = orb_subscribe(ORB_ID(wind_estimate));
+
+ bool updated = false;
+
+ float z_0; // ground properties
+ float turn_radius; // turn radius of the UAV
+ float precision; // Expected precision of the UAV
+
+ float ground_distance = _alt_clearance; // Replace by closer estimate in loop
+
+ // constant
+ float g = CONSTANTS_ONE_G; // constant of gravity [m/s^2]
+ float m = 0.5f; // mass of bottle [kg]
+ float rho = 1.2f; // air density [kg/m^3]
+ float A = ((0.063f * 0.063f) / 4.0f * M_PI_F); // Bottle cross section [m^2]
+ float dt_freefall_prediction = 0.01f; // step size of the free fall prediction [s]
+
+ // Has to be estimated by experiment
+ float cd = 0.86f; // Drag coefficient for a cylinder with a d/l ratio of 1/3 []
+ float t_signal =
+ 0.084f; // Time span between sending the signal and the bottle top reaching level height with the bottom of the plane [s]
+ float t_door =
+ 0.7f; // The time the system needs to open the door + safety, is also the time the palyload needs to safely escape the shaft [s]
+
+
+ // Definition
+ float h_0; // height over target
+ float az; // acceleration in z direction[m/s^2]
+ float vz; // velocity in z direction [m/s]
+ float z; // fallen distance [m]
+ float h; // height over target [m]
+ float ax; // acceleration in x direction [m/s^2]
+ float vx; // ground speed in x direction [m/s]
+ float x; // traveled distance in x direction [m]
+ float vw; // wind speed [m/s]
+ float vrx; // relative velocity in x direction [m/s]
+ float v; // relative speed vector [m/s]
+ float Fd; // Drag force [N]
+ float Fdx; // Drag force in x direction [N]
+ float Fdz; // Drag force in z direction [N]
+ float x_drop, y_drop; // coordinates of the drop point in reference to the target (projection of NED)
+ float x_t, y_t; // coordinates of the target in reference to the target x_t = 0, y_t = 0 (projection of NED)
+ float x_l, y_l; // local position in projected coordinates
+ float x_f, y_f; // to-be position of the UAV after dt_runs seconds in projected coordinates
+ double x_f_NED, y_f_NED; // to-be position of the UAV after dt_runs seconds in NED
+ float distance_open_door; // The distance the UAV travels during its doors open [m]
+ float approach_error = 0.0f; // The error in radians between current ground vector and desired ground vector
+ float distance_real = 0; // The distance between the UAVs position and the drop point [m]
+ float future_distance = 0; // The distance between the UAVs to-be position and the drop point [m]
+
+ unsigned counter = 0;
+
+ param_t param_gproperties = param_find("BD_GPROPERTIES");
+ param_t param_turn_radius = param_find("BD_TURNRADIUS");
+ param_t param_precision = param_find("BD_PRECISION");
+ param_t param_cd = param_find("BD_OBJ_CD");
+ param_t param_mass = param_find("BD_OBJ_MASS");
+ param_t param_surface = param_find("BD_OBJ_SURFACE");
+
+
+ param_get(param_precision, &precision);
+ param_get(param_turn_radius, &turn_radius);
+ param_get(param_gproperties, &z_0);
+ param_get(param_cd, &cd);
+ param_get(param_mass, &m);
+ param_get(param_surface, &A);
+
+ int vehicle_global_position_sub = orb_subscribe(ORB_ID(vehicle_global_position));
+
+ struct parameter_update_s update;
+ memset(&update, 0, sizeof(update));
+ int parameter_update_sub = orb_subscribe(ORB_ID(parameter_update));
+
+ struct mission_item_s flight_vector_s {};
+ struct mission_item_s flight_vector_e {};
+
+ flight_vector_s.nav_cmd = NAV_CMD_WAYPOINT;
+ flight_vector_s.acceptance_radius = 50; // TODO: make parameter
+ flight_vector_s.autocontinue = true;
+ flight_vector_s.altitude_is_relative = false;
+
+ flight_vector_e.nav_cmd = NAV_CMD_WAYPOINT;
+ flight_vector_e.acceptance_radius = 50; // TODO: make parameter
+ flight_vector_e.autocontinue = true;
+ flight_vector_s.altitude_is_relative = false;
+
+ struct wind_estimate_s wind;
+
+ // wakeup source(s)
+ struct pollfd fds[1];
+
+ // Setup of loop
+ fds[0].fd = _command_sub;
+ fds[0].events = POLLIN;
+
+ // Whatever state the bay is in, we want it closed on startup
+ lock_release();
+ close_bay();
+
+ while (!_task_should_exit) {
+
+ /* wait for up to 100ms for data */
+ int pret = poll(&fds[0], (sizeof(fds) / sizeof(fds[0])), 50);
+
+ /* this is undesirable but not much we can do - might want to flag unhappy status */
+ if (pret < 0) {
+ warn("poll error %d, %d", pret, errno);
+ continue;
+ }
+
+ /* vehicle commands updated */
+ if (fds[0].revents & POLLIN) {
+ orb_copy(ORB_ID(vehicle_command), _command_sub, &_command);
+ handle_command(&_command);
+ }
+
+ orb_check(vehicle_global_position_sub, &updated);
+ if (updated) {
+ /* copy global position */
+ orb_copy(ORB_ID(vehicle_global_position), vehicle_global_position_sub, &_global_pos);
+ }
+
+ if (_global_pos.timestamp == 0) {
+ continue;
+ }
+
+ const unsigned sleeptime_us = 9500;
+
+ hrt_abstime last_run = hrt_absolute_time();
+ float dt_runs = sleeptime_us / 1e6f;
+
+ // switch to faster updates during the drop
+ while (_drop_state > DROP_STATE_INIT) {
+
+ // Get wind estimate
+ orb_check(_wind_estimate_sub, &updated);
+ if (updated) {
+ orb_copy(ORB_ID(wind_estimate), _wind_estimate_sub, &wind);
+ }
+
+ // Get vehicle position
+ orb_check(vehicle_global_position_sub, &updated);
+ if (updated) {
+ // copy global position
+ orb_copy(ORB_ID(vehicle_global_position), vehicle_global_position_sub, &_global_pos);
+ }
+
+ // Get parameter updates
+ orb_check(parameter_update_sub, &updated);
+ if (updated) {
+ // copy global position
+ orb_copy(ORB_ID(parameter_update), parameter_update_sub, &update);
+
+ // update all parameters
+ param_get(param_gproperties, &z_0);
+ param_get(param_turn_radius, &turn_radius);
+ param_get(param_precision, &precision);
+ }
+
+ orb_check(_command_sub, &updated);
+ if (updated) {
+ orb_copy(ORB_ID(vehicle_command), _command_sub, &_command);
+ handle_command(&_command);
+ }
+
+
+ float windspeed_norm = sqrtf(wind.windspeed_north * wind.windspeed_north + wind.windspeed_east * wind.windspeed_east);
+ float groundspeed_body = sqrtf(_global_pos.vel_n * _global_pos.vel_n + _global_pos.vel_e * _global_pos.vel_e);
+ ground_distance = _global_pos.alt - _target_position.alt;
+
+ // Distance to drop position and angle error to approach vector
+ // are relevant in all states greater than target valid (which calculates these positions)
+ if (_drop_state > DROP_STATE_TARGET_VALID) {
+ distance_real = fabsf(get_distance_to_next_waypoint(_global_pos.lat, _global_pos.lon, _drop_position.lat, _drop_position.lon));
+
+ float ground_direction = atan2f(_global_pos.vel_e, _global_pos.vel_n);
+ float approach_direction = get_bearing_to_next_waypoint(flight_vector_s.lat, flight_vector_s.lon, flight_vector_e.lat, flight_vector_e.lon);
+
+ approach_error = _wrap_pi(ground_direction - approach_direction);
+
+ if (counter % 90 == 0) {
+ mavlink_log_info(_mavlink_fd, "drop distance %u, heading error %u", (unsigned)distance_real, (unsigned)math::degrees(approach_error));
+ }
+ }
+
+ switch (_drop_state) {
+
+ case DROP_STATE_TARGET_VALID:
+ {
+
+ az = g; // acceleration in z direction[m/s^2]
+ vz = 0; // velocity in z direction [m/s]
+ z = 0; // fallen distance [m]
+ h_0 = _global_pos.alt - _target_position.alt; // height over target at start[m]
+ h = h_0; // height over target [m]
+ ax = 0; // acceleration in x direction [m/s^2]
+ vx = groundspeed_body;// XXX project // ground speed in x direction [m/s]
+ x = 0; // traveled distance in x direction [m]
+ vw = 0; // wind speed [m/s]
+ vrx = 0; // relative velocity in x direction [m/s]
+ v = groundspeed_body; // relative speed vector [m/s]
+ Fd = 0; // Drag force [N]
+ Fdx = 0; // Drag force in x direction [N]
+ Fdz = 0; // Drag force in z direction [N]
+
+
+ // Compute the distance the bottle will travel after it is dropped in body frame coordinates --> x
+ while (h > 0.05f) {
+ // z-direction
+ vz = vz + az * dt_freefall_prediction;
+ z = z + vz * dt_freefall_prediction;
+ h = h_0 - z;
+
+ // x-direction
+ vw = windspeed_norm * logf(h / z_0) / logf(ground_distance / z_0);
+ vx = vx + ax * dt_freefall_prediction;
+ x = x + vx * dt_freefall_prediction;
+ vrx = vx + vw;
+
+ // drag force
+ v = sqrtf(vz * vz + vrx * vrx);
+ Fd = 0.5f * rho * A * cd * (v * v);
+ Fdx = Fd * vrx / v;
+ Fdz = Fd * vz / v;
+
+ // acceleration
+ az = g - Fdz / m;
+ ax = -Fdx / m;
+ }
+
+ // compute drop vector
+ x = groundspeed_body * t_signal + x;
+
+ x_t = 0.0f;
+ y_t = 0.0f;
+
+ float wind_direction_n, wind_direction_e;
+
+ if (windspeed_norm < 0.5f) { // If there is no wind, an arbitrarily direction is chosen
+ wind_direction_n = 1.0f;
+ wind_direction_e = 0.0f;
+
+ } else {
+ wind_direction_n = wind.windspeed_north / windspeed_norm;
+ wind_direction_e = wind.windspeed_east / windspeed_norm;
+ }
+
+ x_drop = x_t + x * wind_direction_n;
+ y_drop = y_t + x * wind_direction_e;
+ map_projection_reproject(&ref, x_drop, y_drop, &_drop_position.lat, &_drop_position.lon);
+ _drop_position.alt = _target_position.alt + _alt_clearance;
+
+ // Compute flight vector
+ map_projection_reproject(&ref, x_drop + 2 * turn_radius * wind_direction_n, y_drop + 2 * turn_radius * wind_direction_e,
+ &(flight_vector_s.lat), &(flight_vector_s.lon));
+ flight_vector_s.altitude = _drop_position.alt;
+ map_projection_reproject(&ref, x_drop - turn_radius * wind_direction_n, y_drop - turn_radius * wind_direction_e,
+ &flight_vector_e.lat, &flight_vector_e.lon);
+ flight_vector_e.altitude = _drop_position.alt;
+
+ // Save WPs in datamanager
+ const ssize_t len = sizeof(struct mission_item_s);
+
+ if (dm_write(DM_KEY_WAYPOINTS_ONBOARD, 0, DM_PERSIST_IN_FLIGHT_RESET, &flight_vector_s, len) != len) {
+ warnx("ERROR: could not save onboard WP");
+ }
+
+ if (dm_write(DM_KEY_WAYPOINTS_ONBOARD, 1, DM_PERSIST_IN_FLIGHT_RESET, &flight_vector_e, len) != len) {
+ warnx("ERROR: could not save onboard WP");
+ }
+
+ _onboard_mission.count = 2;
+ _onboard_mission.current_seq = 0;
+
+ if (_onboard_mission_pub > 0) {
+ orb_publish(ORB_ID(onboard_mission), _onboard_mission_pub, &_onboard_mission);
+
+ } else {
+ _onboard_mission_pub = orb_advertise(ORB_ID(onboard_mission), &_onboard_mission);
+ }
+
+ float approach_direction = get_bearing_to_next_waypoint(flight_vector_s.lat, flight_vector_s.lon, flight_vector_e.lat, flight_vector_e.lon);
+ mavlink_log_critical(_mavlink_fd, "position set, approach heading: %u", (unsigned)distance_real, (unsigned)math::degrees(approach_direction + M_PI_F));
+
+ _drop_state = DROP_STATE_TARGET_SET;
+ }
+ break;
+
+ case DROP_STATE_TARGET_SET:
+ {
+ float distance_wp2 = get_distance_to_next_waypoint(_global_pos.lat, _global_pos.lon, flight_vector_e.lat, flight_vector_e.lon);
+
+ if (distance_wp2 < distance_real) {
+ _onboard_mission.current_seq = 0;
+ orb_publish(ORB_ID(onboard_mission), _onboard_mission_pub, &_onboard_mission);
+ } else {
+
+ // We're close enough - open the bay
+ distance_open_door = math::max(10.0f, 3.0f * fabsf(t_door * groundspeed_body));
+
+ if (isfinite(distance_real) && distance_real < distance_open_door &&
+ fabsf(approach_error) < math::radians(20.0f)) {
+ open_bay();
+ _drop_state = DROP_STATE_BAY_OPEN;
+ mavlink_log_info(_mavlink_fd, "#audio: opening bay");
+ }
+ }
+ }
+ break;
+
+ case DROP_STATE_BAY_OPEN:
+ {
+ if (_drop_approval) {
+ map_projection_project(&ref, _global_pos.lat, _global_pos.lon, &x_l, &y_l);
+ x_f = x_l + _global_pos.vel_n * dt_runs;
+ y_f = y_l + _global_pos.vel_e * dt_runs;
+ map_projection_reproject(&ref, x_f, y_f, &x_f_NED, &y_f_NED);
+ future_distance = get_distance_to_next_waypoint(x_f_NED, y_f_NED, _drop_position.lat, _drop_position.lon);
+
+ if (isfinite(distance_real) &&
+ (distance_real < precision) && ((distance_real < future_distance))) {
+ drop();
+ _drop_state = DROP_STATE_DROPPED;
+ mavlink_log_info(_mavlink_fd, "#audio: payload dropped");
+ } else {
+
+ float distance_wp2 = get_distance_to_next_waypoint(_global_pos.lat, _global_pos.lon, flight_vector_e.lat, flight_vector_e.lon);
+
+ if (distance_wp2 < distance_real) {
+ _onboard_mission.current_seq = 0;
+ orb_publish(ORB_ID(onboard_mission), _onboard_mission_pub, &_onboard_mission);
+ }
+ }
+ }
+ }
+ break;
+
+ case DROP_STATE_DROPPED:
+ /* 2s after drop, reset and close everything again */
+ if ((hrt_elapsed_time(&_doors_opened) > 2 * 1000 * 1000)) {
+ _drop_state = DROP_STATE_INIT;
+ _drop_approval = false;
+ lock_release();
+ close_bay();
+ mavlink_log_info(_mavlink_fd, "#audio: closing bay");
+
+ // remove onboard mission
+ _onboard_mission.current_seq = -1;
+ _onboard_mission.count = 0;
+ orb_publish(ORB_ID(onboard_mission), _onboard_mission_pub, &_onboard_mission);
+ }
+ break;
+ }
+
+ counter++;
+
+ // update_actuators();
+
+ // run at roughly 100 Hz
+ usleep(sleeptime_us);
+
+ dt_runs = hrt_elapsed_time(&last_run) / 1e6f;
+ last_run = hrt_absolute_time();
+ }
+ }
+
+ warnx("exiting.");
+
+ _main_task = -1;
+ _exit(0);
+}
+
+void
+BottleDrop::handle_command(struct vehicle_command_s *cmd)
+{
+ switch (cmd->command) {
+ case VEHICLE_CMD_CUSTOM_0:
+ /*
+ * param1 and param2 set to 1: open and drop
+ * param1 set to 1: open
+ * else: close (and don't drop)
+ */
+ if (cmd->param1 > 0.5f && cmd->param2 > 0.5f) {
+ open_bay();
+ drop();
+ mavlink_log_critical(_mavlink_fd, "drop bottle");
+
+ } else if (cmd->param1 > 0.5f) {
+ open_bay();
+ mavlink_log_critical(_mavlink_fd, "opening bay");
+
+ } else {
+ lock_release();
+ close_bay();
+ mavlink_log_critical(_mavlink_fd, "closing bay");
+ }
+
+ answer_command(cmd, VEHICLE_CMD_RESULT_ACCEPTED);
+ break;
+
+ case VEHICLE_CMD_PAYLOAD_PREPARE_DEPLOY:
+
+ switch ((int)(cmd->param1 + 0.5f)) {
+ case 0:
+ _drop_approval = false;
+ mavlink_log_critical(_mavlink_fd, "got drop position, no approval");
+ break;
+
+ case 1:
+ _drop_approval = true;
+ mavlink_log_critical(_mavlink_fd, "got drop position and approval");
+ break;
+
+ default:
+ _drop_approval = false;
+ warnx("param1 val unknown");
+ break;
+ }
+
+ // XXX check all fields (2-3)
+ _alt_clearance = cmd->param4;
+ _target_position.lat = cmd->param5;
+ _target_position.lon = cmd->param6;
+ _target_position.alt = cmd->param7;
+ _drop_state = DROP_STATE_TARGET_VALID;
+ mavlink_log_info(_mavlink_fd, "got target: %8.4f, %8.4f, %8.4f", (double)_target_position.lat,
+ (double)_target_position.lon, (double)_target_position.alt);
+ map_projection_init(&ref, _target_position.lat, _target_position.lon);
+ answer_command(cmd, VEHICLE_CMD_RESULT_ACCEPTED);
+ break;
+
+ case VEHICLE_CMD_PAYLOAD_CONTROL_DEPLOY:
+
+ if (cmd->param1 < 0) {
+
+ // Clear internal states
+ _drop_approval = false;
+ _drop_state = DROP_STATE_INIT;
+
+ // Abort if mission is present
+ _onboard_mission.current_seq = -1;
+
+ if (_onboard_mission_pub > 0) {
+ orb_publish(ORB_ID(onboard_mission), _onboard_mission_pub, &_onboard_mission);
+ }
+
+ } else {
+ switch ((int)(cmd->param1 + 0.5f)) {
+ case 0:
+ _drop_approval = false;
+ break;
+
+ case 1:
+ _drop_approval = true;
+ mavlink_log_info(_mavlink_fd, "#audio: got drop approval");
+ break;
+
+ default:
+ _drop_approval = false;
+ break;
+ // XXX handle other values
+ }
+ }
+
+ answer_command(cmd, VEHICLE_CMD_RESULT_ACCEPTED);
+ break;
+
+ default:
+ break;
+ }
+}
+
+void
+BottleDrop::answer_command(struct vehicle_command_s *cmd, enum VEHICLE_CMD_RESULT result)
+{
+ switch (result) {
+ case VEHICLE_CMD_RESULT_ACCEPTED:
+ break;
+
+ case VEHICLE_CMD_RESULT_DENIED:
+ mavlink_log_critical(_mavlink_fd, "command denied: %u", cmd->command);
+ break;
+
+ case VEHICLE_CMD_RESULT_FAILED:
+ mavlink_log_critical(_mavlink_fd, "command failed: %u", cmd->command);
+ break;
+
+ case VEHICLE_CMD_RESULT_TEMPORARILY_REJECTED:
+ mavlink_log_critical(_mavlink_fd, "command temporarily rejected: %u", cmd->command);
+ break;
+
+ case VEHICLE_CMD_RESULT_UNSUPPORTED:
+ mavlink_log_critical(_mavlink_fd, "command unsupported: %u", cmd->command);
+ break;
+
+ default:
+ break;
+ }
+}
+
+void
+BottleDrop::task_main_trampoline(int argc, char *argv[])
+{
+ bottle_drop::g_bottle_drop->task_main();
+}
+
+static void usage()
+{
+ errx(1, "usage: bottle_drop {start|stop|status}");
+}
+
+int bottle_drop_main(int argc, char *argv[])
+{
+ if (argc < 2) {
+ usage();
+ }
+
+ if (!strcmp(argv[1], "start")) {
+
+ if (bottle_drop::g_bottle_drop != nullptr) {
+ errx(1, "already running");
+ }
+
+ bottle_drop::g_bottle_drop = new BottleDrop;
+
+ if (bottle_drop::g_bottle_drop == nullptr) {
+ errx(1, "alloc failed");
+ }
+
+ if (OK != bottle_drop::g_bottle_drop->start()) {
+ delete bottle_drop::g_bottle_drop;
+ bottle_drop::g_bottle_drop = nullptr;
+ err(1, "start failed");
+ }
+
+ return 0;
+ }
+
+ if (bottle_drop::g_bottle_drop == nullptr) {
+ errx(1, "not running");
+ }
+
+ if (!strcmp(argv[1], "stop")) {
+ delete bottle_drop::g_bottle_drop;
+ bottle_drop::g_bottle_drop = nullptr;
+
+ } else if (!strcmp(argv[1], "status")) {
+ bottle_drop::g_bottle_drop->status();
+
+ } else if (!strcmp(argv[1], "drop")) {
+ bottle_drop::g_bottle_drop->drop();
+
+ } else if (!strcmp(argv[1], "open")) {
+ bottle_drop::g_bottle_drop->open_bay();
+
+ } else if (!strcmp(argv[1], "close")) {
+ bottle_drop::g_bottle_drop->close_bay();
+
+ } else if (!strcmp(argv[1], "lock")) {
+ bottle_drop::g_bottle_drop->lock_release();
+
+ } else {
+ usage();
+ }
+
+ return 0;
+}
diff --git a/src/modules/bottle_drop/bottle_drop_params.c b/src/modules/bottle_drop/bottle_drop_params.c
new file mode 100644
index 000000000..51ebfb9a1
--- /dev/null
+++ b/src/modules/bottle_drop/bottle_drop_params.c
@@ -0,0 +1,131 @@
+/****************************************************************************
+ *
+ * Copyright (c) 2013, 2014 PX4 Development Team. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/**
+ * @file bottle_drop_params.c
+ * Bottle drop parameters
+ *
+ * @author Dominik Juchli <juchlid@ethz.ch>
+ */
+
+#include <nuttx/config.h>
+#include <systemlib/param/param.h>
+
+/**
+ * Ground drag property
+ *
+ * This parameter encodes the ground drag coefficient and the corresponding
+ * decrease in wind speed from the plane altitude to ground altitude.
+ *
+ * @unit unknown
+ * @min 0.001
+ * @max 0.1
+ * @group Payload drop
+ */
+PARAM_DEFINE_FLOAT(BD_GPROPERTIES, 0.03f);
+
+/**
+ * Plane turn radius
+ *
+ * The planes known minimal turn radius - use a higher value
+ * to make the plane maneuver more distant from the actual drop
+ * position. This is to ensure the wings are level during the drop.
+ *
+ * @unit meter
+ * @min 30.0
+ * @max 500.0
+ * @group Payload drop
+ */
+PARAM_DEFINE_FLOAT(BD_TURNRADIUS, 120.0f);
+
+/**
+ * Drop precision
+ *
+ * If the system is closer than this distance on passing over the
+ * drop position, it will release the payload. This is a safeguard
+ * to prevent a drop out of the required accuracy.
+ *
+ * @unit meter
+ * @min 1.0
+ * @max 80.0
+ * @group Payload drop
+ */
+PARAM_DEFINE_FLOAT(BD_PRECISION, 30.0f);
+
+/**
+ * Payload drag coefficient of the dropped object
+ *
+ * The drag coefficient (cd) is the typical drag
+ * constant for air. It is in general object specific,
+ * but the closest primitive shape to the actual object
+ * should give good results:
+ * http://en.wikipedia.org/wiki/Drag_coefficient
+ *
+ * @unit meter
+ * @min 0.08
+ * @max 1.5
+ * @group Payload drop
+ */
+PARAM_DEFINE_FLOAT(BD_OBJ_CD, 0.1f);
+
+/**
+ * Payload mass
+ *
+ * A typical small toy ball:
+ * 0.025 kg
+ *
+ * OBC water bottle:
+ * 0.6 kg
+ *
+ * @unit kilogram
+ * @min 0.001
+ * @max 5.0
+ * @group Payload drop
+ */
+PARAM_DEFINE_FLOAT(BD_OBJ_MASS, 0.6f);
+
+/**
+ * Payload front surface area
+ *
+ * A typical small toy ball:
+ * (0.045 * 0.045) / 4.0 * pi = 0.001590 m^2
+ *
+ * OBC water bottle:
+ * (0.063 * 0.063) / 4.0 * pi = 0.003117 m^2
+ *
+ * @unit m^2
+ * @min 0.001
+ * @max 0.5
+ * @group Payload drop
+ */
+PARAM_DEFINE_FLOAT(BD_OBJ_SURFACE, 0.00311724531f);
diff --git a/src/modules/bottle_drop/module.mk b/src/modules/bottle_drop/module.mk
new file mode 100644
index 000000000..df9f5473b
--- /dev/null
+++ b/src/modules/bottle_drop/module.mk
@@ -0,0 +1,45 @@
+############################################################################
+#
+# Copyright (c) 2013, 2014 PX4 Development Team. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# 3. Neither the name PX4 nor the names of its contributors may be
+# used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+############################################################################
+
+#
+# Daemon application
+#
+
+MODULE_COMMAND = bottle_drop
+
+SRCS = bottle_drop.cpp \
+ bottle_drop_params.c
+
+MAXOPTIMIZATION = -Os
+
+MODULE_STACKSIZE = 1200
diff --git a/src/modules/commander/accelerometer_calibration.cpp b/src/modules/commander/accelerometer_calibration.cpp
index 7180048ff..0cb41489f 100644
--- a/src/modules/commander/accelerometer_calibration.cpp
+++ b/src/modules/commander/accelerometer_calibration.cpp
@@ -1,7 +1,6 @@
/****************************************************************************
*
- * Copyright (C) 2013 PX4 Development Team. All rights reserved.
- * Author: Anton Babushkin <anton.babushkin@me.com>
+ * Copyright (c) 2013 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -84,7 +83,7 @@
* | accel_T[1][i] |
* [ accel_T[2][i] ]
*
- * b = [ accel_corr_ref[0][i] ] // One measurement per axis is enough
+ * b = [ accel_corr_ref[0][i] ] // One measurement per side is enough
* | accel_corr_ref[2][i] |
* [ accel_corr_ref[4][i] ]
*
@@ -131,6 +130,7 @@
#include <fcntl.h>
#include <sys/prctl.h>
#include <math.h>
+#include <float.h>
#include <mathlib/mathlib.h>
#include <string.h>
#include <drivers/drv_hrt.h>
@@ -158,8 +158,15 @@ int calculate_calibration_values(float accel_ref[6][3], float accel_T[3][3], flo
int do_accel_calibration(int mavlink_fd)
{
+ int fd;
+
mavlink_log_info(mavlink_fd, CAL_STARTED_MSG, sensor_name);
+ mavlink_log_info(mavlink_fd, "You need to put the system on all six sides");
+ sleep(3);
+ mavlink_log_info(mavlink_fd, "Follow the instructions on the screen");
+ sleep(5);
+
struct accel_scale accel_scale = {
0.0f,
1.0f,
@@ -172,7 +179,7 @@ int do_accel_calibration(int mavlink_fd)
int res = OK;
/* reset all offsets to zero and all scales to one */
- int fd = open(ACCEL_DEVICE_PATH, 0);
+ fd = open(ACCEL_DEVICE_PATH, 0);
res = ioctl(fd, ACCELIOCSSCALE, (long unsigned int)&accel_scale);
close(fd);
@@ -223,7 +230,7 @@ int do_accel_calibration(int mavlink_fd)
if (res == OK) {
/* apply new scaling and offsets */
- int fd = open(ACCEL_DEVICE_PATH, 0);
+ fd = open(ACCEL_DEVICE_PATH, 0);
res = ioctl(fd, ACCELIOCSSCALE, (long unsigned int)&accel_scale);
close(fd);
@@ -256,7 +263,7 @@ int do_accel_calibration_measurements(int mavlink_fd, float accel_offs[3], float
const int samples_num = 2500;
float accel_ref[6][3];
bool data_collected[6] = { false, false, false, false, false, false };
- const char *orientation_strs[6] = { "x+", "x-", "y+", "y-", "z+", "z-" };
+ const char *orientation_strs[6] = { "front", "back", "left", "right", "top", "bottom" };
int sensor_combined_sub = orb_subscribe(ORB_ID(sensor_combined));
@@ -285,29 +292,37 @@ int do_accel_calibration_measurements(int mavlink_fd, float accel_offs[3], float
break;
}
- mavlink_log_info(mavlink_fd, "directions left: %s%s%s%s%s%s",
- (!data_collected[0]) ? "x+ " : "",
- (!data_collected[1]) ? "x- " : "",
- (!data_collected[2]) ? "y+ " : "",
- (!data_collected[3]) ? "y- " : "",
- (!data_collected[4]) ? "z+ " : "",
- (!data_collected[5]) ? "z- " : "");
+ /* inform user which axes are still needed */
+ mavlink_log_info(mavlink_fd, "pending: %s%s%s%s%s%s",
+ (!data_collected[0]) ? "front " : "",
+ (!data_collected[1]) ? "back " : "",
+ (!data_collected[2]) ? "left " : "",
+ (!data_collected[3]) ? "right " : "",
+ (!data_collected[4]) ? "up " : "",
+ (!data_collected[5]) ? "down " : "");
+
+ /* allow user enough time to read the message */
+ sleep(3);
int orient = detect_orientation(mavlink_fd, sensor_combined_sub);
if (orient < 0) {
- res = ERROR;
- break;
+ mavlink_log_info(mavlink_fd, "invalid motion, hold still...");
+ sleep(3);
+ continue;
}
+ /* inform user about already handled side */
if (data_collected[orient]) {
- mavlink_log_info(mavlink_fd, "%s done, rotate to a different axis", orientation_strs[orient]);
+ mavlink_log_info(mavlink_fd, "%s side done, rotate to a different side", orientation_strs[orient]);
+ sleep(4);
continue;
}
- mavlink_log_info(mavlink_fd, "accel measurement started: %s axis", orientation_strs[orient]);
+ mavlink_log_info(mavlink_fd, "Hold still, starting to measure %s side", orientation_strs[orient]);
+ sleep(1);
read_accelerometer_avg(sensor_combined_sub, &(accel_ref[orient][0]), samples_num);
- mavlink_log_info(mavlink_fd, "result for %s axis: [ %.2f %.2f %.2f ]", orientation_strs[orient],
+ mavlink_log_info(mavlink_fd, "result for %s side: [ %.2f %.2f %.2f ]", orientation_strs[orient],
(double)accel_ref[orient][0],
(double)accel_ref[orient][1],
(double)accel_ref[orient][2]);
@@ -398,7 +413,7 @@ int detect_orientation(int mavlink_fd, int sub_sensor_combined)
/* is still now */
if (t_still == 0) {
/* first time */
- mavlink_log_info(mavlink_fd, "detected rest position, waiting...");
+ mavlink_log_info(mavlink_fd, "detected rest position, hold still...");
t_still = t;
t_timeout = t + timeout;
@@ -416,6 +431,7 @@ int detect_orientation(int mavlink_fd, int sub_sensor_combined)
/* not still, reset still start time */
if (t_still != 0) {
mavlink_log_info(mavlink_fd, "detected motion, hold still...");
+ sleep(3);
t_still = 0;
}
}
@@ -524,7 +540,7 @@ int mat_invert3(float src[3][3], float dst[3][3])
src[0][1] * (src[1][0] * src[2][2] - src[1][2] * src[2][0]) +
src[0][2] * (src[1][0] * src[2][1] - src[1][1] * src[2][0]);
- if (det == 0.0f) {
+ if (fabsf(det) < FLT_EPSILON) {
return ERROR; // Singular matrix
}
diff --git a/src/modules/commander/accelerometer_calibration.h b/src/modules/commander/accelerometer_calibration.h
index 1cf9c0977..6b7e16d44 100644
--- a/src/modules/commander/accelerometer_calibration.h
+++ b/src/modules/commander/accelerometer_calibration.h
@@ -1,7 +1,6 @@
/****************************************************************************
*
- * Copyright (C) 2013 PX4 Development Team. All rights reserved.
- * Author: Anton Babushkin <anton.babushkin@me.com>
+ * Copyright (c) 2013 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
diff --git a/src/modules/commander/airspeed_calibration.cpp b/src/modules/commander/airspeed_calibration.cpp
index 5d21d89d0..cae1d7684 100644
--- a/src/modules/commander/airspeed_calibration.cpp
+++ b/src/modules/commander/airspeed_calibration.cpp
@@ -51,6 +51,7 @@
#include <mavlink/mavlink_log.h>
#include <systemlib/param/param.h>
#include <systemlib/err.h>
+#include <systemlib/airspeed.h>
/* oddly, ERROR is not defined for c++ */
#ifdef ERROR
@@ -60,23 +61,30 @@ static const int ERROR = -1;
static const char *sensor_name = "dpress";
+#define HUMAN_ASPD_CAL_FAILED_MSG "Calibration failed, see http://px4.io/help/aspd"
+
+static void feedback_calibration_failed(int mavlink_fd)
+{
+ sleep(5);
+ mavlink_log_critical(mavlink_fd, CAL_FAILED_MSG, sensor_name);
+ mavlink_log_critical(mavlink_fd, HUMAN_ASPD_CAL_FAILED_MSG);
+}
+
int do_airspeed_calibration(int mavlink_fd)
{
/* give directions */
mavlink_log_info(mavlink_fd, CAL_STARTED_MSG, sensor_name);
- mavlink_log_info(mavlink_fd, "ensure airspeed sensor is not registering wind");
- const int calibration_count = 2000;
+ const unsigned calibration_count = 2000;
int diff_pres_sub = orb_subscribe(ORB_ID(differential_pressure));
struct differential_pressure_s diff_pres;
- int calibration_counter = 0;
float diff_pres_offset = 0.0f;
/* Reset sensor parameters */
struct airspeed_scale airscale = {
- 0.0f,
+ diff_pres_offset,
1.0f,
};
@@ -95,12 +103,29 @@ int do_airspeed_calibration(int mavlink_fd)
}
if (!paramreset_successful) {
- warn("FAILED to set scale / offsets for airspeed");
- mavlink_log_critical(mavlink_fd, "dpress reset failed");
- mavlink_log_info(mavlink_fd, CAL_FAILED_MSG, sensor_name);
- return ERROR;
+
+ /* only warn if analog scaling is zero */
+ float analog_scaling = 0.0f;
+ param_get(param_find("SENS_DPRES_ANSC"), &(analog_scaling));
+ if (fabsf(analog_scaling) < 0.1f) {
+ mavlink_log_critical(mavlink_fd, "No airspeed sensor, see http://px4.io/help/aspd");
+ close(diff_pres_sub);
+ return ERROR;
+ }
+
+ /* set scaling offset parameter */
+ if (param_set(param_find("SENS_DPRES_OFF"), &(diff_pres_offset))) {
+ mavlink_log_critical(mavlink_fd, CAL_FAILED_SET_PARAMS_MSG);
+ close(diff_pres_sub);
+ return ERROR;
+ }
}
+ unsigned calibration_counter = 0;
+
+ mavlink_log_critical(mavlink_fd, "Ensure sensor is not measuring wind");
+ usleep(500 * 1000);
+
while (calibration_counter < calibration_count) {
/* wait blocking for new data */
@@ -112,16 +137,17 @@ int do_airspeed_calibration(int mavlink_fd)
if (poll_ret) {
orb_copy(ORB_ID(differential_pressure), diff_pres_sub, &diff_pres);
+
diff_pres_offset += diff_pres.differential_pressure_raw_pa;
calibration_counter++;
if (calibration_counter % (calibration_count / 20) == 0) {
- mavlink_log_info(mavlink_fd, CAL_PROGRESS_MSG, sensor_name, (calibration_counter * 100) / calibration_count);
+ mavlink_log_info(mavlink_fd, CAL_PROGRESS_MSG, sensor_name, (calibration_counter * 80) / calibration_count);
}
} else if (poll_ret == 0) {
/* any poll failure for 1s is a reason to abort */
- mavlink_log_critical(mavlink_fd, CAL_FAILED_MSG, sensor_name);
+ feedback_calibration_failed(mavlink_fd);
close(diff_pres_sub);
return ERROR;
}
@@ -131,6 +157,16 @@ int do_airspeed_calibration(int mavlink_fd)
if (isfinite(diff_pres_offset)) {
+ int fd_scale = open(AIRSPEED_DEVICE_PATH, 0);
+ airscale.offset_pa = diff_pres_offset;
+ if (fd_scale > 0) {
+ if (OK != ioctl(fd_scale, AIRSPEEDIOCSSCALE, (long unsigned int)&airscale)) {
+ mavlink_log_critical(mavlink_fd, "airspeed offset update failed");
+ }
+
+ close(fd_scale);
+ }
+
if (param_set(param_find("SENS_DPRES_OFF"), &(diff_pres_offset))) {
mavlink_log_critical(mavlink_fd, CAL_FAILED_SET_PARAMS_MSG);
close(diff_pres_sub);
@@ -147,14 +183,93 @@ int do_airspeed_calibration(int mavlink_fd)
return ERROR;
}
- mavlink_log_info(mavlink_fd, CAL_DONE_MSG, sensor_name);
- tune_neutral(true);
+ } else {
+ feedback_calibration_failed(mavlink_fd);
close(diff_pres_sub);
- return OK;
+ return ERROR;
+ }
- } else {
- mavlink_log_info(mavlink_fd, CAL_FAILED_MSG, sensor_name);
+ mavlink_log_critical(mavlink_fd, "Offset of %d Pascal", (int)diff_pres_offset);
+
+ /* wait 500 ms to ensure parameter propagated through the system */
+ usleep(500 * 1000);
+
+ mavlink_log_critical(mavlink_fd, "Create airflow now");
+
+ calibration_counter = 0;
+ const unsigned maxcount = 3000;
+
+ /* just take a few samples and make sure pitot tubes are not reversed, timeout after ~30 seconds */
+ while (calibration_counter < maxcount) {
+
+ /* wait blocking for new data */
+ struct pollfd fds[1];
+ fds[0].fd = diff_pres_sub;
+ fds[0].events = POLLIN;
+
+ int poll_ret = poll(fds, 1, 1000);
+
+ if (poll_ret) {
+ orb_copy(ORB_ID(differential_pressure), diff_pres_sub, &diff_pres);
+
+ calibration_counter++;
+
+ if (fabsf(diff_pres.differential_pressure_raw_pa) < 50.0f) {
+ if (calibration_counter % 500 == 0) {
+ mavlink_log_info(mavlink_fd, "Create air pressure! (got %d, wanted: 50 Pa)",
+ (int)diff_pres.differential_pressure_raw_pa);
+ }
+ continue;
+ }
+
+ /* do not allow negative values */
+ if (diff_pres.differential_pressure_raw_pa < 0.0f) {
+ mavlink_log_info(mavlink_fd, "ERROR: Negative pressure difference detected! (%d Pa)",
+ (int)diff_pres.differential_pressure_raw_pa);
+ mavlink_log_critical(mavlink_fd, "Swap static and dynamic ports!");
+ close(diff_pres_sub);
+
+ /* the user setup is wrong, wipe the calibration to force a proper re-calibration */
+
+ diff_pres_offset = 0.0f;
+ if (param_set(param_find("SENS_DPRES_OFF"), &(diff_pres_offset))) {
+ mavlink_log_critical(mavlink_fd, CAL_FAILED_SET_PARAMS_MSG);
+ close(diff_pres_sub);
+ return ERROR;
+ }
+
+ /* save */
+ mavlink_log_info(mavlink_fd, CAL_PROGRESS_MSG, sensor_name, 0);
+ (void)param_save_default();
+
+ close(diff_pres_sub);
+
+ feedback_calibration_failed(mavlink_fd);
+ return ERROR;
+ } else {
+ mavlink_log_info(mavlink_fd, "Positive pressure: OK (%d Pa)",
+ (int)diff_pres.differential_pressure_raw_pa);
+ break;
+ }
+
+ } else if (poll_ret == 0) {
+ /* any poll failure for 1s is a reason to abort */
+ feedback_calibration_failed(mavlink_fd);
+ close(diff_pres_sub);
+ return ERROR;
+ }
+ }
+
+ if (calibration_counter == maxcount) {
+ feedback_calibration_failed(mavlink_fd);
close(diff_pres_sub);
return ERROR;
}
+
+ mavlink_log_info(mavlink_fd, CAL_PROGRESS_MSG, sensor_name, 100);
+
+ mavlink_log_info(mavlink_fd, CAL_DONE_MSG, sensor_name);
+ tune_neutral(true);
+ close(diff_pres_sub);
+ return OK;
}
diff --git a/src/modules/commander/airspeed_calibration.h b/src/modules/commander/airspeed_calibration.h
index 71c701fc2..8c6b65ff1 100644
--- a/src/modules/commander/airspeed_calibration.h
+++ b/src/modules/commander/airspeed_calibration.h
@@ -1,6 +1,6 @@
/****************************************************************************
*
- * Copyright (C) 2013 PX4 Development Team. All rights reserved.
+ * Copyright (c) 2013, 2014 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
diff --git a/src/modules/commander/baro_calibration.cpp b/src/modules/commander/baro_calibration.cpp
index 3123c4087..da98644b4 100644
--- a/src/modules/commander/baro_calibration.cpp
+++ b/src/modules/commander/baro_calibration.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
*
- * Copyright (C) 2013 PX4 Development Team. All rights reserved.
+ * Copyright (c) 2013 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
diff --git a/src/modules/commander/baro_calibration.h b/src/modules/commander/baro_calibration.h
index bc42bc6cf..ce78ae700 100644
--- a/src/modules/commander/baro_calibration.h
+++ b/src/modules/commander/baro_calibration.h
@@ -1,6 +1,6 @@
/****************************************************************************
*
- * Copyright (C) 2013 PX4 Development Team. All rights reserved.
+ * Copyright (c) 2013 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
diff --git a/src/modules/commander/calibration_messages.h b/src/modules/commander/calibration_messages.h
index fd8b8564d..b1e209efc 100644
--- a/src/modules/commander/calibration_messages.h
+++ b/src/modules/commander/calibration_messages.h
@@ -1,7 +1,6 @@
/****************************************************************************
*
- * Copyright (C) 2013 PX4 Development Team. All rights reserved.
- * Author: Anton Babushkin <anton.babushkin@me.com>
+ * Copyright (c) 2013 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
diff --git a/src/modules/commander/calibration_routines.cpp b/src/modules/commander/calibration_routines.cpp
index be38ea104..5796204bf 100644
--- a/src/modules/commander/calibration_routines.cpp
+++ b/src/modules/commander/calibration_routines.cpp
@@ -1,7 +1,6 @@
/****************************************************************************
*
- * Copyright (C) 2012 PX4 Development Team. All rights reserved.
- * Author: Lorenz Meier <lm@inf.ethz.ch>
+ * Copyright (c) 2012 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -40,6 +39,7 @@
*/
#include <math.h>
+#include <float.h>
#include "calibration_routines.h"
@@ -170,7 +170,7 @@ int sphere_fit_least_squares(const float x[], const float y[], const float z[],
float aA, aB, aC, nA, nB, nC, dA, dB, dC;
//Iterate N times, ignore stop condition.
- int n = 0;
+ unsigned int n = 0;
while (n < max_iterations) {
n++;
@@ -179,9 +179,9 @@ int sphere_fit_least_squares(const float x[], const float y[], const float z[],
aA = Q2 + 16.0f * (A2 - 2.0f * A * x_sum + x_sum2);
aB = Q2 + 16.0f * (B2 - 2.0f * B * y_sum + y_sum2);
aC = Q2 + 16.0f * (C2 - 2.0f * C * z_sum + z_sum2);
- aA = (aA == 0.0f) ? 1.0f : aA;
- aB = (aB == 0.0f) ? 1.0f : aB;
- aC = (aC == 0.0f) ? 1.0f : aC;
+ aA = (fabsf(aA) < FLT_EPSILON) ? 1.0f : aA;
+ aB = (fabsf(aB) < FLT_EPSILON) ? 1.0f : aB;
+ aC = (fabsf(aC) < FLT_EPSILON) ? 1.0f : aC;
//Compute next iteration
nA = A - ((F2 + 16.0f * (B * XY + C * XZ + x_sum * (-A2 - Q0) + A * (x_sum2 + Q1 - C * z_sum - B * y_sum))) / aA);
diff --git a/src/modules/commander/calibration_routines.h b/src/modules/commander/calibration_routines.h
index e3e7fbafd..3c8e49ee9 100644
--- a/src/modules/commander/calibration_routines.h
+++ b/src/modules/commander/calibration_routines.h
@@ -1,7 +1,6 @@
/****************************************************************************
*
- * Copyright (C) 2012 PX4 Development Team. All rights reserved.
- * Author: Lorenz Meier <lm@inf.ethz.ch>
+ * Copyright (c) 2012 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
diff --git a/src/modules/commander/commander.cpp b/src/modules/commander/commander.cpp
index 65922b2a5..e081955d0 100644
--- a/src/modules/commander/commander.cpp
+++ b/src/modules/commander/commander.cpp
@@ -1,11 +1,6 @@
/****************************************************************************
*
- * Copyright (C) 2013 PX4 Development Team. All rights reserved.
- * Author: Petri Tanskanen <petri.tanskanen@inf.ethz.ch>
- * Lorenz Meier <lm@inf.ethz.ch>
- * Thomas Gubler <thomasgubler@student.ethz.ch>
- * Julian Oes <joes@student.ethz.ch>
- * Anton Babushkin <anton.babushkin@me.com>
+ * Copyright (C) 2013-2014 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -38,8 +33,13 @@
/**
* @file commander.cpp
- * Main system state machine implementation.
+ * Main fail-safe handling.
*
+ * @author Petri Tanskanen <petri.tanskanen@inf.ethz.ch>
+ * @author Lorenz Meier <lm@inf.ethz.ch>
+ * @author Thomas Gubler <thomasgubler@student.ethz.ch>
+ * @author Julian Oes <julian@oes.ch>
+ * @author Anton Babushkin <anton.babushkin@me.com>
*/
#include <nuttx/config.h>
@@ -52,12 +52,14 @@
#include <fcntl.h>
#include <errno.h>
#include <systemlib/err.h>
+#include <systemlib/circuit_breaker.h>
#include <debug.h>
#include <sys/prctl.h>
#include <sys/stat.h>
#include <string.h>
#include <math.h>
#include <poll.h>
+#include <float.h>
#include <uORB/uORB.h>
#include <uORB/topics/sensor_combined.h>
@@ -76,6 +78,10 @@
#include <uORB/topics/parameter_update.h>
#include <uORB/topics/differential_pressure.h>
#include <uORB/topics/safety.h>
+#include <uORB/topics/system_power.h>
+#include <uORB/topics/mission.h>
+#include <uORB/topics/mission_result.h>
+#include <uORB/topics/telemetry_status.h>
#include <drivers/drv_led.h>
#include <drivers/drv_hrt.h>
@@ -87,6 +93,9 @@
#include <systemlib/err.h>
#include <systemlib/cpuload.h>
#include <systemlib/rc_check.h>
+#include <geo/geo.h>
+#include <systemlib/state_table.h>
+#include <dataman/dataman.h>
#include "px4_custom_mode.h"
#include "commander_helper.h"
@@ -117,9 +126,9 @@ extern struct system_load_s system_load;
#define STICK_ON_OFF_HYSTERESIS_TIME_MS 1000
#define STICK_ON_OFF_COUNTER_LIMIT (STICK_ON_OFF_HYSTERESIS_TIME_MS*COMMANDER_MONITORING_LOOPSPERMSEC)
-#define POSITION_TIMEOUT (600 * 1000) /**< consider the local or global position estimate invalid after 600ms */
+#define POSITION_TIMEOUT (2 * 1000 * 1000) /**< consider the local or global position estimate invalid after 600ms */
#define FAILSAFE_DEFAULT_TIMEOUT (3 * 1000 * 1000) /**< hysteresis time - the failsafe will trigger after 3 seconds in this state */
-#define RC_TIMEOUT 500000
+#define OFFBOARD_TIMEOUT 500000
#define DIFFPRESS_TIMEOUT 2000000
#define PRINT_INTERVAL 5000000
@@ -154,12 +163,14 @@ static bool on_usb_power = false;
static float takeoff_alt = 5.0f;
static int parachute_enabled = 0;
-static float eph_epv_threshold = 5.0f;
+static float eph_threshold = 5.0f;
+static float epv_threshold = 10.0f;
static struct vehicle_status_s status;
static struct actuator_armed_s armed;
static struct safety_s safety;
static struct vehicle_control_mode_s control_mode;
+static struct offboard_control_setpoint_s sp_offboard;
/* tasks waiting for low prio thread */
typedef enum {
@@ -196,7 +207,9 @@ void usage(const char *reason);
/**
* React to commands that are sent e.g. from the mavlink module.
*/
-bool handle_command(struct vehicle_status_s *status, const struct safety_s *safety, struct vehicle_command_s *cmd, struct actuator_armed_s *armed, struct home_position_s *home, struct vehicle_global_position_s *global_pos, orb_advert_t *home_pub);
+bool handle_command(struct vehicle_status_s *status, const struct safety_s *safety, struct vehicle_command_s *cmd,
+ struct actuator_armed_s *armed, struct home_position_s *home, struct vehicle_global_position_s *global_pos,
+ orb_advert_t *home_pub);
/**
* Mainloop of commander.
@@ -207,8 +220,6 @@ void control_status_leds(vehicle_status_s *status, const actuator_armed_s *actua
void check_valid(hrt_abstime timestamp, hrt_abstime timeout, bool valid_in, bool *valid_out, bool *changed);
-void check_mode_switches(struct manual_control_setpoint_s *sp_man, struct vehicle_status_s *status);
-
transition_result_t set_main_state_rc(struct vehicle_status_s *status, struct manual_control_setpoint_s *sp_man);
void set_control_mode();
@@ -219,7 +230,8 @@ void print_reject_arm(const char *msg);
void print_status();
-transition_result_t check_navigation_state_machine(struct vehicle_status_s *status, struct vehicle_control_mode_s *control_mode, struct vehicle_local_position_s *local_pos);
+transition_result_t check_navigation_state_machine(struct vehicle_status_s *status,
+ struct vehicle_control_mode_s *control_mode, struct vehicle_local_position_s *local_pos);
transition_result_t arm_disarm(bool arm, const int mavlink_fd, const char *armedBy);
@@ -249,7 +261,7 @@ int commander_main(int argc, char *argv[])
daemon_task = task_spawn_cmd("commander",
SCHED_DEFAULT,
SCHED_PRIORITY_MAX - 40,
- 2950,
+ 3200,
commander_thread_main,
(argv) ? (const char **)&argv[2] : (const char **)NULL);
@@ -278,25 +290,36 @@ int commander_main(int argc, char *argv[])
exit(0);
}
- if (!strcmp(argv[1], "status")) {
- if (thread_running) {
- warnx("\tcommander is running");
- print_status();
+ /* commands needing the app to run below */
+ if (!thread_running) {
+ warnx("\tcommander not started");
+ exit(1);
+ }
- } else {
- warnx("\tcommander not started");
- }
+ if (!strcmp(argv[1], "status")) {
+ print_status();
+ exit(0);
+ }
+ if (!strcmp(argv[1], "check")) {
+ int mavlink_fd_local = open(MAVLINK_LOG_DEVICE, 0);
+ int checkres = prearm_check(&status, mavlink_fd_local);
+ close(mavlink_fd_local);
+ warnx("FINAL RESULT: %s", (checkres == 0) ? "OK" : "FAILED");
exit(0);
}
if (!strcmp(argv[1], "arm")) {
- arm_disarm(true, mavlink_fd, "command line");
+ int mavlink_fd_local = open(MAVLINK_LOG_DEVICE, 0);
+ arm_disarm(true, mavlink_fd_local, "command line");
+ close(mavlink_fd_local);
exit(0);
}
- if (!strcmp(argv[1], "2")) {
- arm_disarm(false, mavlink_fd, "command line");
+ if (!strcmp(argv[1], "disarm")) {
+ int mavlink_fd_local = open(MAVLINK_LOG_DEVICE, 0);
+ arm_disarm(false, mavlink_fd_local, "command line");
+ close(mavlink_fd_local);
exit(0);
}
@@ -316,7 +339,9 @@ void usage(const char *reason)
void print_status()
{
+ warnx("type: %s", (status.is_rotary_wing) ? "ROTARY" : "PLANE");
warnx("usb powered: %s", (on_usb_power) ? "yes" : "no");
+ warnx("avionics rail: %6.2f V", (double)status.avionics_power_rail_voltage);
/* read all relevant states */
int state_sub = orb_subscribe(ORB_ID(vehicle_status));
@@ -367,16 +392,17 @@ void print_status()
static orb_advert_t status_pub;
-transition_result_t arm_disarm(bool arm, const int mavlink_fd, const char *armedBy)
+transition_result_t arm_disarm(bool arm, const int mavlink_fd_local, const char *armedBy)
{
transition_result_t arming_res = TRANSITION_NOT_CHANGED;
// Transition the armed state. By passing mavlink_fd to arming_state_transition it will
// output appropriate error messages if the state cannot transition.
- arming_res = arming_state_transition(&status, &safety, arm ? ARMING_STATE_ARMED : ARMING_STATE_STANDBY, &armed, mavlink_fd);
+ arming_res = arming_state_transition(&status, &safety, arm ? ARMING_STATE_ARMED : ARMING_STATE_STANDBY, &armed,
+ true /* fRunPreArmChecks */, mavlink_fd_local);
if (arming_res == TRANSITION_CHANGED && mavlink_fd) {
- mavlink_log_info(mavlink_fd, "[cmd] %s by %s", arm ? "ARMED" : "DISARMED", armedBy);
+ mavlink_log_info(mavlink_fd_local, "[cmd] %s by %s", arm ? "ARMED" : "DISARMED", armedBy);
} else if (arming_res == TRANSITION_DENIED) {
tune_negative(true);
@@ -385,133 +411,114 @@ transition_result_t arm_disarm(bool arm, const int mavlink_fd, const char *armed
return arming_res;
}
-bool handle_command(struct vehicle_status_s *status, const struct safety_s *safety, struct vehicle_command_s *cmd, struct actuator_armed_s *armed, struct home_position_s *home, struct vehicle_global_position_s *global_pos, orb_advert_t *home_pub)
+bool handle_command(struct vehicle_status_s *status_local, const struct safety_s *safety_local,
+ struct vehicle_command_s *cmd, struct actuator_armed_s *armed_local,
+ struct home_position_s *home, struct vehicle_global_position_s *global_pos, orb_advert_t *home_pub)
{
- /* result of the command */
- enum VEHICLE_CMD_RESULT result = VEHICLE_CMD_RESULT_UNSUPPORTED;
- bool ret = false;
-
/* only handle commands that are meant to be handled by this system and component */
- if (cmd->target_system != status->system_id || ((cmd->target_component != status->component_id) && (cmd->target_component != 0))) { // component_id 0: valid for all components
+ if (cmd->target_system != status_local->system_id || ((cmd->target_component != status_local->component_id)
+ && (cmd->target_component != 0))) { // component_id 0: valid for all components
return false;
}
- /* only handle high-priority commands here */
+ /* result of the command */
+ enum VEHICLE_CMD_RESULT cmd_result = VEHICLE_CMD_RESULT_UNSUPPORTED;
/* request to set different system mode */
switch (cmd->command) {
case VEHICLE_CMD_DO_SET_MODE: {
- uint8_t base_mode = (uint8_t) cmd->param1;
- uint8_t custom_main_mode = (uint8_t) cmd->param2;
- transition_result_t arming_res = TRANSITION_NOT_CHANGED;
-
- /* set HIL state */
- hil_state_t new_hil_state = (base_mode & MAV_MODE_FLAG_HIL_ENABLED) ? HIL_STATE_ON : HIL_STATE_OFF;
- int hil_ret = hil_state_transition(new_hil_state, status_pub, status, mavlink_fd);
+ uint8_t base_mode = (uint8_t)cmd->param1;
+ uint8_t custom_main_mode = (uint8_t)cmd->param2;
- /* if HIL got enabled, reset battery status state */
- if (hil_ret == OK && status->hil_state == HIL_STATE_ON) {
- /* reset the arming mode to disarmed */
- arming_res = arming_state_transition(status, safety, ARMING_STATE_STANDBY, armed);
+ transition_result_t arming_ret = TRANSITION_NOT_CHANGED;
- if (arming_res != TRANSITION_DENIED) {
- mavlink_log_info(mavlink_fd, "[cmd] HIL: Reset ARMED state to standby");
+ transition_result_t main_ret = TRANSITION_NOT_CHANGED;
- } else {
- mavlink_log_info(mavlink_fd, "[cmd] HIL: FAILED resetting armed state");
- }
- }
-
- if (hil_ret == OK) {
- ret = true;
- }
+ /* set HIL state */
+ hil_state_t new_hil_state = (base_mode & MAV_MODE_FLAG_HIL_ENABLED) ? HIL_STATE_ON : HIL_STATE_OFF;
+ transition_result_t hil_ret = hil_state_transition(new_hil_state, status_pub, status_local, mavlink_fd);
// Transition the arming state
- arming_res = arm_disarm(base_mode & MAV_MODE_FLAG_SAFETY_ARMED, mavlink_fd, "set mode command");
-
- if (arming_res == TRANSITION_CHANGED) {
- ret = true;
- }
-
- /* set main state */
- transition_result_t main_res = TRANSITION_DENIED;
+ arming_ret = arm_disarm(base_mode & MAV_MODE_FLAG_SAFETY_ARMED, mavlink_fd, "set mode command");
if (base_mode & MAV_MODE_FLAG_CUSTOM_MODE_ENABLED) {
/* use autopilot-specific mode */
if (custom_main_mode == PX4_CUSTOM_MAIN_MODE_MANUAL) {
/* MANUAL */
- main_res = main_state_transition(status, MAIN_STATE_MANUAL);
+ main_ret = main_state_transition(status_local, MAIN_STATE_MANUAL);
} else if (custom_main_mode == PX4_CUSTOM_MAIN_MODE_ALTCTL) {
/* ALTCTL */
- main_res = main_state_transition(status, MAIN_STATE_ALTCTL);
+ main_ret = main_state_transition(status_local, MAIN_STATE_ALTCTL);
} else if (custom_main_mode == PX4_CUSTOM_MAIN_MODE_POSCTL) {
/* POSCTL */
- main_res = main_state_transition(status, MAIN_STATE_POSCTL);
+ main_ret = main_state_transition(status_local, MAIN_STATE_POSCTL);
} else if (custom_main_mode == PX4_CUSTOM_MAIN_MODE_AUTO) {
/* AUTO */
- main_res = main_state_transition(status, MAIN_STATE_AUTO);
+ main_ret = main_state_transition(status_local, MAIN_STATE_AUTO_MISSION);
} else if (custom_main_mode == PX4_CUSTOM_MAIN_MODE_ACRO) {
/* ACRO */
- main_res = main_state_transition(status, MAIN_STATE_ACRO);
+ main_ret = main_state_transition(status_local, MAIN_STATE_ACRO);
+
+ } else if (custom_main_mode == PX4_CUSTOM_MAIN_MODE_OFFBOARD) {
+ /* OFFBOARD */
+ main_ret = main_state_transition(status_local, MAIN_STATE_OFFBOARD);
}
} else {
/* use base mode */
if (base_mode & MAV_MODE_FLAG_AUTO_ENABLED) {
/* AUTO */
- main_res = main_state_transition(status, MAIN_STATE_AUTO);
+ main_ret = main_state_transition(status_local, MAIN_STATE_AUTO_MISSION);
} else if (base_mode & MAV_MODE_FLAG_MANUAL_INPUT_ENABLED) {
if (base_mode & MAV_MODE_FLAG_GUIDED_ENABLED) {
/* POSCTL */
- main_res = main_state_transition(status, MAIN_STATE_POSCTL);
+ main_ret = main_state_transition(status_local, MAIN_STATE_POSCTL);
} else if (base_mode & MAV_MODE_FLAG_STABILIZE_ENABLED) {
/* MANUAL */
- main_res = main_state_transition(status, MAIN_STATE_MANUAL);
+ main_ret = main_state_transition(status_local, MAIN_STATE_MANUAL);
}
}
}
- if (main_res == TRANSITION_CHANGED) {
- ret = true;
- }
-
- if (arming_res != TRANSITION_DENIED && main_res != TRANSITION_DENIED) {
- result = VEHICLE_CMD_RESULT_ACCEPTED;
+ if (hil_ret != TRANSITION_DENIED && arming_ret != TRANSITION_DENIED && main_ret != TRANSITION_DENIED) {
+ cmd_result = VEHICLE_CMD_RESULT_ACCEPTED;
} else {
- result = VEHICLE_CMD_RESULT_TEMPORARILY_REJECTED;
+ cmd_result = VEHICLE_CMD_RESULT_TEMPORARILY_REJECTED;
}
-
- break;
}
+ break;
case VEHICLE_CMD_COMPONENT_ARM_DISARM: {
- // Follow exactly what the mavlink spec says for values: 0.0f for disarm, 1.0f for arm.
- // We use an float epsilon delta to test float equality.
- if (cmd->param1 != 0.0f && (fabsf(cmd->param1 - 1.0f) > 2.0f * FLT_EPSILON)) {
- mavlink_log_info(mavlink_fd, "Unsupported ARM_DISARM parameter: %.6f", cmd->param1);
+ // Adhere to MAVLink specs, but base on knowledge that these fundamentally encode ints
+ // for logic state parameters
+
+ if (static_cast<int>(cmd->param1 + 0.5f) != 0 && static_cast<int>(cmd->param1 + 0.5f) != 1) {
+ mavlink_log_critical(mavlink_fd, "Unsupported ARM_DISARM param: %.3f", (double)cmd->param1);
} else {
+ bool cmd_arms = (static_cast<int>(cmd->param1 + 0.5f) == 1);
+
// Flick to inair restore first if this comes from an onboard system
- if (cmd->source_system == status->system_id && cmd->source_component == status->component_id) {
- status->arming_state = ARMING_STATE_IN_AIR_RESTORE;
+ if (cmd->source_system == status_local->system_id && cmd->source_component == status_local->component_id) {
+ status_local->arming_state = ARMING_STATE_IN_AIR_RESTORE;
}
- transition_result_t arming_res = arm_disarm(cmd->param1 != 0.0f, mavlink_fd, "arm/disarm component command");
+ transition_result_t arming_res = arm_disarm(cmd_arms, mavlink_fd, "arm/disarm component command");
if (arming_res == TRANSITION_DENIED) {
- mavlink_log_critical(mavlink_fd, "#audio: REJECTING component arm cmd");
- result = VEHICLE_CMD_RESULT_TEMPORARILY_REJECTED;
+ mavlink_log_critical(mavlink_fd, "REJECTING component arm cmd");
+ cmd_result = VEHICLE_CMD_RESULT_TEMPORARILY_REJECTED;
} else {
- result = VEHICLE_CMD_RESULT_ACCEPTED;
+ cmd_result = VEHICLE_CMD_RESULT_ACCEPTED;
}
}
}
@@ -519,43 +526,79 @@ bool handle_command(struct vehicle_status_s *status, const struct safety_s *safe
case VEHICLE_CMD_OVERRIDE_GOTO: {
// TODO listen vehicle_command topic directly from navigator (?)
- unsigned int mav_goto = cmd->param1;
+
+ // Increase by 0.5f and rely on the integer cast
+ // implicit floor(). This is the *safest* way to
+ // convert from floats representing small ints to actual ints.
+ unsigned int mav_goto = (cmd->param1 + 0.5f);
if (mav_goto == 0) { // MAV_GOTO_DO_HOLD
- status->set_nav_state = NAV_STATE_LOITER;
- status->set_nav_state_timestamp = hrt_absolute_time();
- mavlink_log_critical(mavlink_fd, "#audio: pause mission cmd");
- result = VEHICLE_CMD_RESULT_ACCEPTED;
- ret = true;
+ status_local->nav_state = NAVIGATION_STATE_AUTO_LOITER;
+ mavlink_log_critical(mavlink_fd, "Pause mission cmd");
+ cmd_result = VEHICLE_CMD_RESULT_ACCEPTED;
} else if (mav_goto == 1) { // MAV_GOTO_DO_CONTINUE
- status->set_nav_state = NAV_STATE_MISSION;
- status->set_nav_state_timestamp = hrt_absolute_time();
- mavlink_log_critical(mavlink_fd, "#audio: continue mission cmd");
- result = VEHICLE_CMD_RESULT_ACCEPTED;
- ret = true;
+ status_local->nav_state = NAVIGATION_STATE_AUTO_MISSION;
+ mavlink_log_critical(mavlink_fd, "Continue mission cmd");
+ cmd_result = VEHICLE_CMD_RESULT_ACCEPTED;
} else {
- mavlink_log_info(mavlink_fd, "Unsupported OVERRIDE_GOTO: %f %f %f %f %f %f %f %f", cmd->param1, cmd->param2, cmd->param3, cmd->param4, cmd->param5, cmd->param6, cmd->param7);
+ mavlink_log_critical(mavlink_fd, "REJ CMD: %.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f",
+ (double)cmd->param1,
+ (double)cmd->param2,
+ (double)cmd->param3,
+ (double)cmd->param4,
+ (double)cmd->param5,
+ (double)cmd->param6,
+ (double)cmd->param7);
}
}
break;
/* Flight termination */
- case VEHICLE_CMD_DO_SET_SERVO: { //xxx: needs its own mavlink command
-
- //XXX: to enable the parachute, a param needs to be set
- //xxx: for safety only for now, param3 is unused by VEHICLE_CMD_DO_SET_SERVO
- if (armed->armed && cmd->param3 > 0.5 && parachute_enabled) {
- transition_result_t failsafe_res = failsafe_state_transition(status, FAILSAFE_STATE_TERMINATION);
- result = VEHICLE_CMD_RESULT_ACCEPTED;
- ret = true;
+ case VEHICLE_CMD_DO_FLIGHTTERMINATION: {
+ if (cmd->param1 > 0.5f) {
+ //XXX update state machine?
+ armed_local->force_failsafe = true;
+ warnx("forcing failsafe (termination)");
} else {
- /* reject parachute depoyment not armed */
- result = VEHICLE_CMD_RESULT_TEMPORARILY_REJECTED;
+ armed_local->force_failsafe = false;
+ warnx("disabling failsafe (termination)");
}
+ /* param2 is currently used for other failsafe modes */
+ status_local->engine_failure_cmd = false;
+ status_local->data_link_lost_cmd = false;
+ status_local->gps_failure_cmd = false;
+ status_local->rc_signal_lost_cmd = false;
+
+ if ((int)cmd->param2 <= 0) {
+ /* reset all commanded failure modes */
+ warnx("reset all non-flighttermination failsafe commands");
+
+ } else if ((int)cmd->param2 == 1) {
+ /* trigger engine failure mode */
+ status_local->engine_failure_cmd = true;
+ warnx("engine failure mode commanded");
+
+ } else if ((int)cmd->param2 == 2) {
+ /* trigger data link loss mode */
+ status_local->data_link_lost_cmd = true;
+ warnx("data link loss mode commanded");
+
+ } else if ((int)cmd->param2 == 3) {
+ /* trigger gps loss mode */
+ status_local->gps_failure_cmd = true;
+ warnx("gps loss mode commanded");
+
+ } else if ((int)cmd->param2 == 4) {
+ /* trigger rc loss mode */
+ status_local->rc_signal_lost_cmd = true;
+ warnx("rc loss mode commanded");
+ }
+
+ cmd_result = VEHICLE_CMD_RESULT_ACCEPTED;
}
break;
@@ -564,17 +607,17 @@ bool handle_command(struct vehicle_status_s *status, const struct safety_s *safe
if (use_current) {
/* use current position */
- if (status->condition_global_position_valid) {
+ if (status_local->condition_global_position_valid) {
home->lat = global_pos->lat;
home->lon = global_pos->lon;
home->alt = global_pos->alt;
home->timestamp = hrt_absolute_time();
- result = VEHICLE_CMD_RESULT_ACCEPTED;
+ cmd_result = VEHICLE_CMD_RESULT_ACCEPTED;
} else {
- result = VEHICLE_CMD_RESULT_TEMPORARILY_REJECTED;
+ cmd_result = VEHICLE_CMD_RESULT_TEMPORARILY_REJECTED;
}
} else {
@@ -585,10 +628,10 @@ bool handle_command(struct vehicle_status_s *status, const struct safety_s *safe
home->timestamp = hrt_absolute_time();
- result = VEHICLE_CMD_RESULT_ACCEPTED;
+ cmd_result = VEHICLE_CMD_RESULT_ACCEPTED;
}
- if (result == VEHICLE_CMD_RESULT_ACCEPTED) {
+ if (cmd_result == VEHICLE_CMD_RESULT_ACCEPTED) {
warnx("home: lat = %.7f, lon = %.7f, alt = %.2f ", home->lat, home->lon, (double)home->alt);
mavlink_log_info(mavlink_fd, "[cmd] home: %.7f, %.7f, %.2f", home->lat, home->lon, (double)home->alt);
@@ -601,7 +644,36 @@ bool handle_command(struct vehicle_status_s *status, const struct safety_s *safe
}
/* mark home position as set */
- status->condition_home_position_valid = true;
+ status_local->condition_home_position_valid = true;
+ }
+ }
+ break;
+
+ case VEHICLE_CMD_NAV_GUIDED_ENABLE: {
+ transition_result_t res = TRANSITION_DENIED;
+ static main_state_t main_state_pre_offboard = MAIN_STATE_MANUAL;
+
+ if (status_local->main_state != MAIN_STATE_OFFBOARD) {
+ main_state_pre_offboard = status_local->main_state;
+ }
+
+ if (cmd->param1 > 0.5f) {
+ res = main_state_transition(status_local, MAIN_STATE_OFFBOARD);
+
+ if (res == TRANSITION_DENIED) {
+ print_reject_mode(status_local, "OFFBOARD");
+ status_local->offboard_control_set_by_command = false;
+
+ } else {
+ /* Set flag that offboard was set via command, main state is not overridden by rc */
+ status_local->offboard_control_set_by_command = true;
+ }
+
+ } else {
+ /* If the mavlink command is used to enable or disable offboard control:
+ * switch back to previous mode when disabling */
+ res = main_state_transition(status_local, main_state_pre_offboard);
+ status_local->offboard_control_set_by_command = false;
}
}
break;
@@ -610,6 +682,11 @@ bool handle_command(struct vehicle_status_s *status, const struct safety_s *safe
case VEHICLE_CMD_PREFLIGHT_CALIBRATION:
case VEHICLE_CMD_PREFLIGHT_SET_SENSOR_OFFSETS:
case VEHICLE_CMD_PREFLIGHT_STORAGE:
+ case VEHICLE_CMD_CUSTOM_0:
+ case VEHICLE_CMD_CUSTOM_1:
+ case VEHICLE_CMD_CUSTOM_2:
+ case VEHICLE_CMD_PAYLOAD_PREPARE_DEPLOY:
+ case VEHICLE_CMD_PAYLOAD_CONTROL_DEPLOY:
/* ignore commands that handled in low prio loop */
break;
@@ -620,17 +697,18 @@ bool handle_command(struct vehicle_status_s *status, const struct safety_s *safe
break;
}
- if (result != VEHICLE_CMD_RESULT_UNSUPPORTED) {
+ if (cmd_result != VEHICLE_CMD_RESULT_UNSUPPORTED) {
/* already warned about unsupported commands in "default" case */
- answer_command(*cmd, result);
+ answer_command(*cmd, cmd_result);
}
/* send any requested ACKs */
- if (cmd->confirmation > 0 && result != VEHICLE_CMD_RESULT_UNSUPPORTED) {
+ if (cmd->confirmation > 0 && cmd_result != VEHICLE_CMD_RESULT_UNSUPPORTED) {
/* send acknowledge command */
// XXX TODO
}
+ return true;
}
int commander_thread_main(int argc, char *argv[])
@@ -647,42 +725,63 @@ int commander_thread_main(int argc, char *argv[])
param_t _param_component_id = param_find("MAV_COMP_ID");
param_t _param_takeoff_alt = param_find("NAV_TAKEOFF_ALT");
param_t _param_enable_parachute = param_find("NAV_PARACHUTE_EN");
+ param_t _param_enable_datalink_loss = param_find("COM_DL_LOSS_EN");
+ param_t _param_datalink_loss_timeout = param_find("COM_DL_LOSS_T");
+ param_t _param_rc_loss_timeout = param_find("COM_RC_LOSS_T");
+ param_t _param_datalink_regain_timeout = param_find("COM_DL_REG_T");
+ param_t _param_ef_throttle_thres = param_find("COM_EF_THROT");
+ param_t _param_ef_current2throttle_thres = param_find("COM_EF_C2T");
+ param_t _param_ef_time_thres = param_find("COM_EF_TIME");
/* welcome user */
warnx("starting");
- char *main_states_str[MAIN_STATE_MAX];
- main_states_str[0] = "MANUAL";
- main_states_str[1] = "ALTCTL";
- main_states_str[2] = "POSCTL";
- main_states_str[3] = "AUTO";
- main_states_str[4] = "ACRO";
-
- char *arming_states_str[ARMING_STATE_MAX];
- arming_states_str[0] = "INIT";
- arming_states_str[1] = "STANDBY";
- arming_states_str[2] = "ARMED";
- arming_states_str[3] = "ARMED_ERROR";
- arming_states_str[4] = "STANDBY_ERROR";
- arming_states_str[5] = "REBOOT";
- arming_states_str[6] = "IN_AIR_RESTORE";
-
- char *failsafe_states_str[FAILSAFE_STATE_MAX];
- failsafe_states_str[0] = "NORMAL";
- failsafe_states_str[1] = "RTL";
- failsafe_states_str[2] = "LAND";
- failsafe_states_str[3] = "TERMINATION";
+ const char *main_states_str[MAIN_STATE_MAX];
+ main_states_str[MAIN_STATE_MANUAL] = "MANUAL";
+ main_states_str[MAIN_STATE_ALTCTL] = "ALTCTL";
+ main_states_str[MAIN_STATE_POSCTL] = "POSCTL";
+ main_states_str[MAIN_STATE_AUTO_MISSION] = "AUTO_MISSION";
+ main_states_str[MAIN_STATE_AUTO_LOITER] = "AUTO_LOITER";
+ main_states_str[MAIN_STATE_AUTO_RTL] = "AUTO_RTL";
+ main_states_str[MAIN_STATE_ACRO] = "ACRO";
+ main_states_str[MAIN_STATE_OFFBOARD] = "OFFBOARD";
+
+ const char *arming_states_str[ARMING_STATE_MAX];
+ arming_states_str[ARMING_STATE_INIT] = "INIT";
+ arming_states_str[ARMING_STATE_STANDBY] = "STANDBY";
+ arming_states_str[ARMING_STATE_ARMED] = "ARMED";
+ arming_states_str[ARMING_STATE_ARMED_ERROR] = "ARMED_ERROR";
+ arming_states_str[ARMING_STATE_STANDBY_ERROR] = "STANDBY_ERROR";
+ arming_states_str[ARMING_STATE_REBOOT] = "REBOOT";
+ arming_states_str[ARMING_STATE_IN_AIR_RESTORE] = "IN_AIR_RESTORE";
+
+ const char *nav_states_str[NAVIGATION_STATE_MAX];
+ nav_states_str[NAVIGATION_STATE_MANUAL] = "MANUAL";
+ nav_states_str[NAVIGATION_STATE_ALTCTL] = "ALTCTL";
+ nav_states_str[NAVIGATION_STATE_POSCTL] = "POSCTL";
+ nav_states_str[NAVIGATION_STATE_AUTO_MISSION] = "AUTO_MISSION";
+ nav_states_str[NAVIGATION_STATE_AUTO_LOITER] = "AUTO_LOITER";
+ nav_states_str[NAVIGATION_STATE_AUTO_RTL] = "AUTO_RTL";
+ nav_states_str[NAVIGATION_STATE_AUTO_RCRECOVER] = "AUTO_RCRECOVER";
+ nav_states_str[NAVIGATION_STATE_AUTO_RTGS] = "AUTO_RTGS";
+ nav_states_str[NAVIGATION_STATE_AUTO_LANDENGFAIL] = "AUTO_LANDENGFAIL";
+ nav_states_str[NAVIGATION_STATE_AUTO_LANDGPSFAIL] = "AUTO_LANDGPSFAIL";
+ nav_states_str[NAVIGATION_STATE_ACRO] = "ACRO";
+ nav_states_str[NAVIGATION_STATE_LAND] = "LAND";
+ nav_states_str[NAVIGATION_STATE_DESCEND] = "DESCEND";
+ nav_states_str[NAVIGATION_STATE_TERMINATION] = "TERMINATION";
+ nav_states_str[NAVIGATION_STATE_OFFBOARD] = "OFFBOARD";
/* pthread for slow low prio thread */
pthread_t commander_low_prio_thread;
/* initialize */
if (led_init() != 0) {
- warnx("ERROR: Failed to initialize leds");
+ warnx("ERROR: LED INIT FAIL");
}
if (buzzer_init() != OK) {
- warnx("ERROR: Failed to initialize buzzer");
+ warnx("ERROR: BUZZER INIT FAIL");
}
mavlink_fd = open(MAVLINK_LOG_DEVICE, 0);
@@ -693,11 +792,10 @@ int commander_thread_main(int argc, char *argv[])
// We want to accept RC inputs as default
status.rc_input_blocked = false;
status.main_state = MAIN_STATE_MANUAL;
- status.set_nav_state = NAV_STATE_NONE;
- status.set_nav_state_timestamp = 0;
+ status.nav_state = NAVIGATION_STATE_MANUAL;
status.arming_state = ARMING_STATE_INIT;
status.hil_state = HIL_STATE_OFF;
- status.failsafe_state = FAILSAFE_STATE_NORMAL;
+ status.failsafe = false;
/* neither manual nor offboard control commands have been received */
status.offboard_control_signal_found_once = false;
@@ -706,6 +804,7 @@ int commander_thread_main(int argc, char *argv[])
/* mark all signals lost as long as they haven't been found */
status.rc_signal_lost = true;
status.offboard_control_signal_lost = true;
+ status.data_link_lost = true;
/* set battery warning flag */
status.battery_warning = VEHICLE_BATTERY_WARNING_NONE;
@@ -717,9 +816,24 @@ int commander_thread_main(int argc, char *argv[])
status.counter++;
status.timestamp = hrt_absolute_time();
+ status.condition_power_input_valid = true;
+ status.avionics_power_rail_voltage = -1.0f;
+
+ // CIRCUIT BREAKERS
+ status.circuit_breaker_engaged_power_check = false;
+ status.circuit_breaker_engaged_airspd_check = false;
+ status.circuit_breaker_engaged_enginefailure_check = false;
+ status.circuit_breaker_engaged_gpsfailure_check = false;
+
/* publish initial state */
status_pub = orb_advertise(ORB_ID(vehicle_status), &status);
+ if (status_pub < 0) {
+ warnx("ERROR: orb_advertise for topic vehicle_status failed (uorb app running?).\n");
+ warnx("exiting.");
+ exit(ERROR);
+ }
+
/* armed topic */
orb_advert_t armed_pub;
/* Initialize armed with all false */
@@ -736,13 +850,32 @@ int commander_thread_main(int argc, char *argv[])
struct home_position_s home;
memset(&home, 0, sizeof(home));
- if (status_pub < 0) {
- warnx("ERROR: orb_advertise for topic vehicle_status failed (uorb app running?).\n");
- warnx("exiting.");
- exit(ERROR);
- }
+ /* init mission state, do it here to allow navigator to use stored mission even if mavlink failed to start */
+ orb_advert_t mission_pub = -1;
+ mission_s mission;
+
+ if (dm_read(DM_KEY_MISSION_STATE, 0, &mission, sizeof(mission_s)) == sizeof(mission_s)) {
+ if (mission.dataman_id >= 0 && mission.dataman_id <= 1) {
+ warnx("loaded mission state: dataman_id=%d, count=%u, current=%d", mission.dataman_id, mission.count,
+ mission.current_seq);
+ mavlink_log_info(mavlink_fd, "[cmd] dataman_id=%d, count=%u, current=%d",
+ mission.dataman_id, mission.count, mission.current_seq);
- mavlink_log_info(mavlink_fd, "[cmd] started");
+ } else {
+ const char *missionfail = "reading mission state failed";
+ warnx("%s", missionfail);
+ mavlink_log_critical(mavlink_fd, missionfail);
+
+ /* initialize mission state in dataman */
+ mission.dataman_id = 0;
+ mission.count = 0;
+ mission.current_seq = 0;
+ dm_write(DM_KEY_MISSION_STATE, 0, DM_PERSIST_POWER_ON_RESET, &mission, sizeof(mission_s));
+ }
+
+ mission_pub = orb_advertise(ORB_ID(offboard_mission), &mission);
+ orb_publish(ORB_ID(offboard_mission), mission_pub, &mission);
+ }
int ret;
@@ -769,14 +902,13 @@ int commander_thread_main(int argc, char *argv[])
hrt_abstime last_idle_time = 0;
hrt_abstime start_time = 0;
- hrt_abstime last_auto_state_valid = 0;
bool status_changed = true;
bool param_init_forced = true;
bool updated = false;
- bool rc_calibration_ok = (OK == rc_calibration_check(mavlink_fd));
+ rc_calibration_check(mavlink_fd);
/* Subscribe to safety topic */
int safety_sub = orb_subscribe(ORB_ID(safety));
@@ -784,6 +916,11 @@ int commander_thread_main(int argc, char *argv[])
safety.safety_switch_available = false;
safety.safety_off = false;
+ /* Subscribe to mission result topic */
+ int mission_result_sub = orb_subscribe(ORB_ID(mission_result));
+ struct mission_result_s mission_result;
+ memset(&mission_result, 0, sizeof(mission_result));
+
/* Subscribe to manual control data */
int sp_man_sub = orb_subscribe(ORB_ID(manual_control_setpoint));
struct manual_control_setpoint_s sp_man;
@@ -791,9 +928,21 @@ int commander_thread_main(int argc, char *argv[])
/* Subscribe to offboard control data */
int sp_offboard_sub = orb_subscribe(ORB_ID(offboard_control_setpoint));
- struct offboard_control_setpoint_s sp_offboard;
memset(&sp_offboard, 0, sizeof(sp_offboard));
+ /* Subscribe to telemetry status topics */
+ int telemetry_subs[TELEMETRY_STATUS_ORB_ID_NUM];
+ uint64_t telemetry_last_heartbeat[TELEMETRY_STATUS_ORB_ID_NUM];
+ uint64_t telemetry_last_dl_loss[TELEMETRY_STATUS_ORB_ID_NUM];
+ bool telemetry_lost[TELEMETRY_STATUS_ORB_ID_NUM];
+
+ for (int i = 0; i < TELEMETRY_STATUS_ORB_ID_NUM; i++) {
+ telemetry_subs[i] = orb_subscribe(telemetry_status_orb_id[i]);
+ telemetry_last_heartbeat[i] = 0;
+ telemetry_last_dl_loss[i] = 0;
+ telemetry_lost[i] = true;
+ }
+
/* Subscribe to global position */
int global_position_sub = orb_subscribe(ORB_ID(vehicle_global_position));
struct vehicle_global_position_s global_position;
@@ -817,6 +966,8 @@ int commander_thread_main(int argc, char *argv[])
int gps_sub = orb_subscribe(ORB_ID(vehicle_gps_position));
struct vehicle_gps_position_s gps_position;
memset(&gps_position, 0, sizeof(gps_position));
+ gps_position.eph = FLT_MAX;
+ gps_position.epv = FLT_MAX;
/* Subscribe to sensor topic */
int sensor_sub = orb_subscribe(ORB_ID(sensor_combined));
@@ -853,6 +1004,16 @@ int commander_thread_main(int argc, char *argv[])
struct position_setpoint_triplet_s pos_sp_triplet;
memset(&pos_sp_triplet, 0, sizeof(pos_sp_triplet));
+ /* Subscribe to system power */
+ int system_power_sub = orb_subscribe(ORB_ID(system_power));
+ struct system_power_s system_power;
+ memset(&system_power, 0, sizeof(system_power));
+
+ /* Subscribe to actuator controls (outputs) */
+ int actuator_controls_sub = orb_subscribe(ORB_ID_VEHICLE_ATTITUDE_CONTROLS);
+ struct actuator_controls_s actuator_controls;
+ memset(&actuator_controls, 0, sizeof(actuator_controls));
+
control_status_leds(&status, &armed, true);
/* now initialized */
@@ -861,6 +1022,24 @@ int commander_thread_main(int argc, char *argv[])
start_time = hrt_absolute_time();
+ transition_result_t arming_ret;
+
+ int32_t datalink_loss_enabled = false;
+ int32_t datalink_loss_timeout = 10;
+ float rc_loss_timeout = 0.5;
+ int32_t datalink_regain_timeout = 0;
+
+ /* Thresholds for engine failure detection */
+ int32_t ef_throttle_thres = 1.0f;
+ int32_t ef_current2throttle_thres = 0.0f;
+ int32_t ef_time_thres = 1000.0f;
+ uint64_t timestamp_engine_healthy = 0; /**< absolute time when engine was healty */
+
+ /* check which state machines for changes, clear "changed" flag */
+ bool arming_state_changed = false;
+ bool main_state_changed = false;
+ bool failsafe_old = false;
+
while (!thread_should_exit) {
if (mavlink_fd < 0 && counter % (1000000 / MAVLINK_OPEN_INTERVAL) == 0) {
@@ -868,6 +1047,9 @@ int commander_thread_main(int argc, char *argv[])
mavlink_fd = open(MAVLINK_LOG_DEVICE, 0);
}
+ arming_ret = TRANSITION_NOT_CHANGED;
+
+
/* update parameters */
orb_check(param_changed_sub, &updated);
@@ -898,15 +1080,32 @@ int commander_thread_main(int argc, char *argv[])
/* check and update system / component ID */
param_get(_param_system_id, &(status.system_id));
param_get(_param_component_id, &(status.component_id));
+
+ status.circuit_breaker_engaged_power_check =
+ circuit_breaker_enabled("CBRK_SUPPLY_CHK", CBRK_SUPPLY_CHK_KEY);
+ status.circuit_breaker_engaged_airspd_check =
+ circuit_breaker_enabled("CBRK_AIRSPD_CHK", CBRK_AIRSPD_CHK_KEY);
+ status.circuit_breaker_engaged_enginefailure_check =
+ circuit_breaker_enabled("CBRK_ENGINEFAIL", CBRK_ENGINEFAIL_KEY);
+ status.circuit_breaker_engaged_gpsfailure_check =
+ circuit_breaker_enabled("CBRK_GPSFAIL", CBRK_GPSFAIL_KEY);
+
status_changed = true;
/* re-check RC calibration */
- rc_calibration_ok = (OK == rc_calibration_check(mavlink_fd));
+ rc_calibration_check(mavlink_fd);
}
/* navigation parameters */
param_get(_param_takeoff_alt, &takeoff_alt);
param_get(_param_enable_parachute, &parachute_enabled);
+ param_get(_param_enable_datalink_loss, &datalink_loss_enabled);
+ param_get(_param_datalink_loss_timeout, &datalink_loss_timeout);
+ param_get(_param_rc_loss_timeout, &rc_loss_timeout);
+ param_get(_param_datalink_regain_timeout, &datalink_regain_timeout);
+ param_get(_param_ef_throttle_thres, &ef_throttle_thres);
+ param_get(_param_ef_current2throttle_thres, &ef_current2throttle_thres);
+ param_get(_param_ef_time_thres, &ef_time_thres);
}
orb_check(sp_man_sub, &updated);
@@ -921,10 +1120,67 @@ int commander_thread_main(int argc, char *argv[])
orb_copy(ORB_ID(offboard_control_setpoint), sp_offboard_sub, &sp_offboard);
}
+ if (sp_offboard.timestamp != 0 &&
+ sp_offboard.timestamp + OFFBOARD_TIMEOUT > hrt_absolute_time()) {
+ if (status.offboard_control_signal_lost) {
+ status.offboard_control_signal_lost = false;
+ status_changed = true;
+ }
+
+ } else {
+ if (!status.offboard_control_signal_lost) {
+ status.offboard_control_signal_lost = true;
+ status_changed = true;
+ }
+ }
+
+ for (int i = 0; i < TELEMETRY_STATUS_ORB_ID_NUM; i++) {
+ orb_check(telemetry_subs[i], &updated);
+
+ if (updated) {
+ struct telemetry_status_s telemetry;
+ memset(&telemetry, 0, sizeof(telemetry));
+
+ orb_copy(telemetry_status_orb_id[i], telemetry_subs[i], &telemetry);
+
+ /* perform system checks when new telemetry link connected */
+ if (mavlink_fd &&
+ telemetry_last_heartbeat[i] == 0 &&
+ telemetry.heartbeat_time > 0 &&
+ hrt_elapsed_time(&telemetry.heartbeat_time) < datalink_loss_timeout * 1e6) {
+
+ (void)rc_calibration_check(mavlink_fd);
+ }
+
+ telemetry_last_heartbeat[i] = telemetry.heartbeat_time;
+ }
+ }
+
orb_check(sensor_sub, &updated);
if (updated) {
orb_copy(ORB_ID(sensor_combined), sensor_sub, &sensors);
+
+ /* Check if the barometer is healthy and issue a warning in the GCS if not so.
+ * Because the barometer is used for calculating AMSL altitude which is used to ensure
+ * vertical separation from other airtraffic the operator has to know when the
+ * barometer is inoperational.
+ * */
+ if (hrt_elapsed_time(&sensors.baro_timestamp) < FAILSAFE_DEFAULT_TIMEOUT) {
+ /* handle the case where baro was regained */
+ if (status.barometer_failure) {
+ status.barometer_failure = false;
+ status_changed = true;
+ mavlink_log_critical(mavlink_fd, "baro healthy");
+ }
+
+ } else {
+ if (!status.barometer_failure) {
+ status.barometer_failure = true;
+ status_changed = true;
+ mavlink_log_critical(mavlink_fd, "baro failed");
+ }
+ }
}
orb_check(diff_pres_sub, &updated);
@@ -933,6 +1189,27 @@ int commander_thread_main(int argc, char *argv[])
orb_copy(ORB_ID(differential_pressure), diff_pres_sub, &diff_pres);
}
+ orb_check(system_power_sub, &updated);
+
+ if (updated) {
+ orb_copy(ORB_ID(system_power), system_power_sub, &system_power);
+
+ if (hrt_elapsed_time(&system_power.timestamp) < 200000) {
+ if (system_power.servo_valid &&
+ !system_power.brick_valid &&
+ !system_power.usb_connected) {
+ /* flying only on servo rail, this is unsafe */
+ status.condition_power_input_valid = false;
+
+ } else {
+ status.condition_power_input_valid = true;
+ }
+
+ /* copy avionics voltage */
+ status.avionics_power_rail_voltage = system_power.voltage5V_v;
+ }
+ }
+
check_valid(diff_pres.timestamp, DIFFPRESS_TIMEOUT, true, &(status.condition_airspeed_valid), &status_changed);
/* update safety topic */
@@ -943,10 +1220,13 @@ int commander_thread_main(int argc, char *argv[])
/* disarm if safety is now on and still armed */
if (status.hil_state == HIL_STATE_OFF && safety.safety_switch_available && !safety.safety_off && armed.armed) {
- arming_state_t new_arming_state = (status.arming_state == ARMING_STATE_ARMED ? ARMING_STATE_STANDBY : ARMING_STATE_STANDBY_ERROR);
+ arming_state_t new_arming_state = (status.arming_state == ARMING_STATE_ARMED ? ARMING_STATE_STANDBY :
+ ARMING_STATE_STANDBY_ERROR);
- if (TRANSITION_CHANGED == arming_state_transition(&status, &safety, new_arming_state, &armed)) {
- mavlink_log_info(mavlink_fd, "[cmd] DISARMED by safety switch");
+ if (TRANSITION_CHANGED == arming_state_transition(&status, &safety, new_arming_state, &armed,
+ true /* fRunPreArmChecks */, mavlink_fd)) {
+ mavlink_log_info(mavlink_fd, "DISARMED by safety switch");
+ arming_state_changed = true;
}
}
}
@@ -959,39 +1239,50 @@ int commander_thread_main(int argc, char *argv[])
orb_copy(ORB_ID(vehicle_global_position), global_position_sub, &global_position);
}
+ /* update local position estimate */
+ orb_check(local_position_sub, &updated);
+
+ if (updated) {
+ /* position changed */
+ orb_copy(ORB_ID(vehicle_local_position), local_position_sub, &local_position);
+ }
+
/* update condition_global_position_valid */
/* hysteresis for EPH/EPV */
- bool eph_epv_good;
+ bool eph_good;
if (status.condition_global_position_valid) {
- if (global_position.eph > eph_epv_threshold * 2.0f || global_position.epv > eph_epv_threshold * 2.0f) {
- eph_epv_good = false;
+ if (global_position.eph > eph_threshold * 2.5f) {
+ eph_good = false;
} else {
- eph_epv_good = true;
+ eph_good = true;
}
} else {
- if (global_position.eph < eph_epv_threshold && global_position.epv < eph_epv_threshold) {
- eph_epv_good = true;
+ if (global_position.eph < eph_threshold) {
+ eph_good = true;
} else {
- eph_epv_good = false;
+ eph_good = false;
}
}
- check_valid(global_position.timestamp, POSITION_TIMEOUT, eph_epv_good, &(status.condition_global_position_valid), &status_changed);
-
- /* check if GPS fix is ok */
+ check_valid(global_position.timestamp, POSITION_TIMEOUT, eph_good, &(status.condition_global_position_valid),
+ &status_changed);
/* update home position */
if (!status.condition_home_position_valid && status.condition_global_position_valid && !armed.armed &&
- (global_position.eph < eph_epv_threshold) && (global_position.epv < eph_epv_threshold)) {
+ (global_position.eph < eph_threshold) && (global_position.epv < epv_threshold)) {
home.lat = global_position.lat;
home.lon = global_position.lon;
home.alt = global_position.alt;
+ home.x = local_position.x;
+ home.y = local_position.y;
+ home.z = local_position.z;
+
warnx("home: lat = %.7f, lon = %.7f, alt = %.2f ", home.lat, home.lon, (double)home.alt);
mavlink_log_info(mavlink_fd, "[cmd] home: %.7f, %.7f, %.2f", home.lat, home.lon, (double)home.alt);
@@ -1008,20 +1299,12 @@ int commander_thread_main(int argc, char *argv[])
tune_positive(true);
}
- /* update local position estimate */
- orb_check(local_position_sub, &updated);
-
- if (updated) {
- /* position changed */
- orb_copy(ORB_ID(vehicle_local_position), local_position_sub, &local_position);
- }
-
/* update condition_local_position_valid and condition_local_altitude_valid */
/* hysteresis for EPH */
bool local_eph_good;
- if (status.condition_global_position_valid) {
- if (local_position.eph > eph_epv_threshold * 2.0f) {
+ if (status.condition_local_position_valid) {
+ if (local_position.eph > eph_threshold * 2.5f) {
local_eph_good = false;
} else {
@@ -1029,38 +1312,31 @@ int commander_thread_main(int argc, char *argv[])
}
} else {
- if (local_position.eph < eph_epv_threshold) {
+ if (local_position.eph < eph_threshold) {
local_eph_good = true;
} else {
local_eph_good = false;
}
}
- check_valid(local_position.timestamp, POSITION_TIMEOUT, local_position.xy_valid && local_eph_good, &(status.condition_local_position_valid), &status_changed);
- check_valid(local_position.timestamp, POSITION_TIMEOUT, local_position.z_valid, &(status.condition_local_altitude_valid), &status_changed);
- static bool published_condition_landed_fw = false;
+ check_valid(local_position.timestamp, POSITION_TIMEOUT, local_position.xy_valid
+ && local_eph_good, &(status.condition_local_position_valid), &status_changed);
+ check_valid(local_position.timestamp, POSITION_TIMEOUT, local_position.z_valid,
+ &(status.condition_local_altitude_valid), &status_changed);
- if (status.is_rotary_wing && status.condition_local_altitude_valid) {
+ if (status.condition_local_altitude_valid) {
if (status.condition_landed != local_position.landed) {
status.condition_landed = local_position.landed;
status_changed = true;
- published_condition_landed_fw = false; //make sure condition_landed is published again if the system type changes
if (status.condition_landed) {
- mavlink_log_critical(mavlink_fd, "#audio: LANDED");
+ mavlink_log_critical(mavlink_fd, "LANDED MODE");
} else {
- mavlink_log_critical(mavlink_fd, "#audio: IN AIR");
+ mavlink_log_critical(mavlink_fd, "IN AIR MODE");
}
}
-
- } else {
- if (!published_condition_landed_fw) {
- status.condition_landed = false; // Fixedwing does not have a landing detector currently
- published_condition_landed_fw = true;
- status_changed = true;
- }
}
/* update battery status */
@@ -1068,13 +1344,18 @@ int commander_thread_main(int argc, char *argv[])
if (updated) {
orb_copy(ORB_ID(battery_status), battery_sub, &battery);
+ orb_copy(ORB_ID_VEHICLE_ATTITUDE_CONTROLS, actuator_controls_sub, &actuator_controls);
/* only consider battery voltage if system has been running 2s and battery voltage is valid */
if (hrt_absolute_time() > start_time + 2000000 && battery.voltage_filtered_v > 0.0f) {
status.battery_voltage = battery.voltage_filtered_v;
status.battery_current = battery.current_a;
status.condition_battery_voltage_valid = true;
- status.battery_remaining = battery_remaining_estimate_voltage(battery.voltage_filtered_v, battery.discharged_mah);
+
+ /* get throttle (if armed), as we only care about energy negative throttle also counts */
+ float throttle = (armed.armed) ? fabsf(actuator_controls.control[3]) : 0.0f;
+ status.battery_remaining = battery_remaining_estimate_voltage(battery.voltage_filtered_v, battery.discharged_mah,
+ throttle);
}
}
@@ -1131,28 +1412,39 @@ int commander_thread_main(int argc, char *argv[])
last_idle_time = system_load.tasks[0].total_runtime;
/* check if board is connected via USB */
- //struct stat statbuf;
- //on_usb_power = (stat("/dev/ttyACM0", &statbuf) == 0);
+ struct stat statbuf;
+ on_usb_power = (stat("/dev/ttyACM0", &statbuf) == 0);
}
/* if battery voltage is getting lower, warn using buzzer, etc. */
- if (status.condition_battery_voltage_valid && status.battery_remaining < 0.25f && !low_battery_voltage_actions_done) {
+ if (status.condition_battery_voltage_valid && status.battery_remaining < 0.18f && !low_battery_voltage_actions_done) {
low_battery_voltage_actions_done = true;
- mavlink_log_critical(mavlink_fd, "#audio: WARNING: LOW BATTERY");
+ mavlink_log_critical(mavlink_fd, "LOW BATTERY, RETURN TO LAND ADVISED");
status.battery_warning = VEHICLE_BATTERY_WARNING_LOW;
status_changed = true;
- } else if (status.condition_battery_voltage_valid && status.battery_remaining < 0.1f && !critical_battery_voltage_actions_done && low_battery_voltage_actions_done) {
+ } else if (!on_usb_power && status.condition_battery_voltage_valid && status.battery_remaining < 0.09f
+ && !critical_battery_voltage_actions_done && low_battery_voltage_actions_done) {
/* critical battery voltage, this is rather an emergency, change state machine */
critical_battery_voltage_actions_done = true;
- mavlink_log_critical(mavlink_fd, "#audio: EMERGENCY: CRITICAL BATTERY");
+ mavlink_log_emergency(mavlink_fd, "CRITICAL BATTERY, LAND IMMEDIATELY");
status.battery_warning = VEHICLE_BATTERY_WARNING_CRITICAL;
if (armed.armed) {
- arming_state_transition(&status, &safety, ARMING_STATE_ARMED_ERROR, &armed);
+ arming_ret = arming_state_transition(&status, &safety, ARMING_STATE_ARMED_ERROR, &armed, true /* fRunPreArmChecks */,
+ mavlink_fd);
+
+ if (arming_ret == TRANSITION_CHANGED) {
+ arming_state_changed = true;
+ }
} else {
- arming_state_transition(&status, &safety, ARMING_STATE_STANDBY_ERROR, &armed);
+ arming_ret = arming_state_transition(&status, &safety, ARMING_STATE_STANDBY_ERROR, &armed, true /* fRunPreArmChecks */,
+ mavlink_fd);
+
+ if (arming_ret == TRANSITION_CHANGED) {
+ arming_state_changed = true;
+ }
}
status_changed = true;
@@ -1162,11 +1454,16 @@ int commander_thread_main(int argc, char *argv[])
/* If in INIT state, try to proceed to STANDBY state */
if (status.arming_state == ARMING_STATE_INIT && low_prio_task == LOW_PRIO_TASK_NONE) {
- // XXX check for sensors
- arming_state_transition(&status, &safety, ARMING_STATE_STANDBY, &armed);
+ /* TODO: check for sensors */
+ arming_ret = arming_state_transition(&status, &safety, ARMING_STATE_STANDBY, &armed, true /* fRunPreArmChecks */,
+ mavlink_fd);
+
+ if (arming_ret == TRANSITION_CHANGED) {
+ arming_state_changed = true;
+ }
} else {
- // XXX: Add emergency stuff if sensors are lost
+ /* TODO: Add emergency stuff if sensors are lost */
}
@@ -1185,28 +1482,79 @@ int commander_thread_main(int argc, char *argv[])
orb_copy(ORB_ID(vehicle_gps_position), gps_sub, &gps_position);
}
- /* start RC input check */
- if (!status.rc_input_blocked && sp_man.timestamp != 0 && hrt_absolute_time() < sp_man.timestamp + RC_TIMEOUT) {
+ /* Initialize map projection if gps is valid */
+ if (!map_projection_global_initialized()
+ && (gps_position.eph < eph_threshold)
+ && (gps_position.epv < epv_threshold)
+ && hrt_elapsed_time((hrt_abstime *)&gps_position.timestamp_position) < 1e6) {
+ /* set reference for global coordinates <--> local coordiantes conversion and map_projection */
+ globallocalconverter_init((double)gps_position.lat * 1.0e-7, (double)gps_position.lon * 1.0e-7,
+ (float)gps_position.alt * 1.0e-3f, hrt_absolute_time());
+ }
+
+ /* check if GPS fix is ok */
+ if (status.circuit_breaker_engaged_gpsfailure_check ||
+ (gps_position.fix_type >= 3 &&
+ hrt_elapsed_time(&gps_position.timestamp_position) < FAILSAFE_DEFAULT_TIMEOUT)) {
+ /* handle the case where gps was regained */
+ if (status.gps_failure) {
+ status.gps_failure = false;
+ status_changed = true;
+ mavlink_log_critical(mavlink_fd, "gps regained");
+ }
+
+ } else {
+ if (!status.gps_failure) {
+ status.gps_failure = true;
+ status_changed = true;
+ mavlink_log_critical(mavlink_fd, "gps fix lost");
+ }
+ }
+
+ /* start mission result check */
+ orb_check(mission_result_sub, &updated);
+
+ if (updated) {
+ orb_copy(ORB_ID(mission_result), mission_result_sub, &mission_result);
+
+ /* Check for geofence violation */
+ if (armed.armed && (mission_result.geofence_violated || mission_result.flight_termination)) {
+ //XXX: make this configurable to select different actions (e.g. navigation modes)
+ /* this will only trigger if geofence is activated via param and a geofence file is present, also there is a circuit breaker to disable the actual flight termination in the px4io driver */
+ armed.force_failsafe = true;
+ status_changed = true;
+ static bool flight_termination_printed = false;
+
+ if (!flight_termination_printed) {
+ warnx("Flight termination because of navigator request or geofence");
+ mavlink_log_critical(mavlink_fd, "GF violation: flight termination");
+ flight_termination_printed = true;
+ }
+
+ if (counter % (1000000 / COMMANDER_MONITORING_INTERVAL) == 0) {
+ mavlink_log_critical(mavlink_fd, "GF violation: flight termination");
+ }
+ } // no reset is done here on purpose, on geofence violation we want to stay in flighttermination
+ }
+
+ /* RC input check */
+ if (!status.rc_input_blocked && sp_man.timestamp != 0 &&
+ hrt_absolute_time() < sp_man.timestamp + (uint64_t)(rc_loss_timeout * 1e6f)) {
/* handle the case where RC signal was regained */
if (!status.rc_signal_found_once) {
status.rc_signal_found_once = true;
- mavlink_log_critical(mavlink_fd, "#audio: detected RC signal first time");
+ mavlink_log_critical(mavlink_fd, "detected RC signal first time");
status_changed = true;
} else {
if (status.rc_signal_lost) {
- mavlink_log_critical(mavlink_fd, "#audio: RC signal regained");
+ mavlink_log_critical(mavlink_fd, "RC signal regained");
status_changed = true;
}
}
status.rc_signal_lost = false;
- transition_result_t arming_res; // store all transitions results here
-
- /* arm/disarm by RC */
- arming_res = TRANSITION_NOT_CHANGED;
-
/* check if left stick is in lower left position and we are in MANUAL or AUTO_READY mode or (ASSIST mode and landed) -> disarm
* do it only for rotary wings */
if (status.is_rotary_wing &&
@@ -1216,8 +1564,15 @@ int commander_thread_main(int argc, char *argv[])
if (stick_off_counter > STICK_ON_OFF_COUNTER_LIMIT) {
/* disarm to STANDBY if ARMED or to STANDBY_ERROR if ARMED_ERROR */
- arming_state_t new_arming_state = (status.arming_state == ARMING_STATE_ARMED ? ARMING_STATE_STANDBY : ARMING_STATE_STANDBY_ERROR);
- arming_res = arming_state_transition(&status, &safety, new_arming_state, &armed);
+ arming_state_t new_arming_state = (status.arming_state == ARMING_STATE_ARMED ? ARMING_STATE_STANDBY :
+ ARMING_STATE_STANDBY_ERROR);
+ arming_ret = arming_state_transition(&status, &safety, new_arming_state, &armed, true /* fRunPreArmChecks */,
+ mavlink_fd);
+
+ if (arming_ret == TRANSITION_CHANGED) {
+ arming_state_changed = true;
+ }
+
stick_off_counter = 0;
} else {
@@ -1232,14 +1587,21 @@ int commander_thread_main(int argc, char *argv[])
if (status.arming_state == ARMING_STATE_STANDBY &&
sp_man.r > STICK_ON_OFF_LIMIT && sp_man.z < 0.1f) {
if (stick_on_counter > STICK_ON_OFF_COUNTER_LIMIT) {
- if (safety.safety_switch_available && !safety.safety_off && status.hil_state == HIL_STATE_OFF) {
- print_reject_arm("#audio: NOT ARMING: Press safety switch first.");
- } else if (status.main_state != MAIN_STATE_MANUAL) {
- print_reject_arm("#audio: NOT ARMING: Switch to MANUAL mode first.");
+ /* we check outside of the transition function here because the requirement
+ * for being in manual mode only applies to manual arming actions.
+ * the system can be armed in auto if armed via the GCS.
+ */
+ if (status.main_state != MAIN_STATE_MANUAL) {
+ print_reject_arm("NOT ARMING: Switch to MANUAL mode first.");
} else {
- arming_res = arming_state_transition(&status, &safety, ARMING_STATE_ARMED, &armed);
+ arming_ret = arming_state_transition(&status, &safety, ARMING_STATE_ARMED, &armed, true /* fRunPreArmChecks */,
+ mavlink_fd);
+
+ if (arming_ret == TRANSITION_CHANGED) {
+ arming_state_changed = true;
+ }
}
stick_on_counter = 0;
@@ -1252,22 +1614,24 @@ int commander_thread_main(int argc, char *argv[])
stick_on_counter = 0;
}
- if (arming_res == TRANSITION_CHANGED) {
+ if (arming_ret == TRANSITION_CHANGED) {
if (status.arming_state == ARMING_STATE_ARMED) {
- mavlink_log_info(mavlink_fd, "[cmd] ARMED by RC");
+ mavlink_log_info(mavlink_fd, "ARMED by RC");
} else {
- mavlink_log_info(mavlink_fd, "[cmd] DISARMED by RC");
+ mavlink_log_info(mavlink_fd, "DISARMED by RC");
}
- } else if (arming_res == TRANSITION_DENIED) {
- /* DENIED here indicates bug in the commander */
- mavlink_log_critical(mavlink_fd, "ERROR: arming state transition denied");
- }
+ arming_state_changed = true;
- if (status.failsafe_state != FAILSAFE_STATE_NORMAL) {
- /* recover from failsafe */
- (void)failsafe_state_transition(&status, FAILSAFE_STATE_NORMAL);
+ } else if (arming_ret == TRANSITION_DENIED) {
+ /*
+ * the arming transition can be denied to a number of reasons:
+ * - pre-flight check failed (sensors not ok or not calibrated)
+ * - safety not disabled
+ * - system not in manual mode
+ */
+ tune_negative(true);
}
/* evaluate the main state machine according to mode switches */
@@ -1276,102 +1640,99 @@ int commander_thread_main(int argc, char *argv[])
/* play tune on mode change only if armed, blink LED always */
if (main_res == TRANSITION_CHANGED) {
tune_positive(armed.armed);
+ main_state_changed = true;
} else if (main_res == TRANSITION_DENIED) {
/* DENIED here indicates bug in the commander */
- mavlink_log_critical(mavlink_fd, "ERROR: main state transition denied");
- }
-
- /* set navigation state */
- /* RETURN switch, overrides MISSION switch */
- if (sp_man.return_switch == SWITCH_POS_ON) {
- /* switch to RTL if not already landed after RTL and home position set */
- status.set_nav_state = NAV_STATE_RTL;
- status.set_nav_state_timestamp = hrt_absolute_time();
-
- } else {
-
- /* LOITER switch */
- if (sp_man.loiter_switch == SWITCH_POS_ON) {
- /* stick is in LOITER position */
- status.set_nav_state = NAV_STATE_LOITER;
- status.set_nav_state_timestamp = hrt_absolute_time();
-
- } else if (sp_man.loiter_switch != SWITCH_POS_NONE) {
- /* stick is in MISSION position */
- status.set_nav_state = NAV_STATE_MISSION;
- status.set_nav_state_timestamp = hrt_absolute_time();
-
- } else if ((sp_man.return_switch == SWITCH_POS_OFF || sp_man.return_switch == SWITCH_POS_MIDDLE) &&
- pos_sp_triplet.nav_state == NAV_STATE_RTL) {
- /* RETURN switch is in normal mode, no MISSION switch mapped, interrupt if in RTL state */
- status.set_nav_state = NAV_STATE_MISSION;
- status.set_nav_state_timestamp = hrt_absolute_time();
- }
+ mavlink_log_critical(mavlink_fd, "main state transition denied");
}
} else {
if (!status.rc_signal_lost) {
- mavlink_log_critical(mavlink_fd, "#audio: CRITICAL: RC SIGNAL LOST");
+ mavlink_log_critical(mavlink_fd, "RC SIGNAL LOST");
status.rc_signal_lost = true;
status_changed = true;
}
+ }
- if (armed.armed) {
- if (status.main_state == MAIN_STATE_AUTO) {
- /* check if AUTO mode still allowed */
- transition_result_t auto_res = main_state_transition(&status, MAIN_STATE_AUTO);
-
- if (auto_res == TRANSITION_NOT_CHANGED) {
- last_auto_state_valid = hrt_absolute_time();
- }
+ /* data links check */
+ bool have_link = false;
+
+ for (int i = 0; i < TELEMETRY_STATUS_ORB_ID_NUM; i++) {
+ if (telemetry_last_heartbeat[i] != 0 &&
+ hrt_elapsed_time(&telemetry_last_heartbeat[i]) < datalink_loss_timeout * 1e6) {
+ /* handle the case where data link was regained,
+ * accept datalink as healthy only after datalink_regain_timeout seconds
+ * */
+ if (telemetry_lost[i] &&
+ hrt_elapsed_time(&telemetry_last_dl_loss[i]) > datalink_regain_timeout * 1e6) {
+
+ mavlink_log_critical(mavlink_fd, "data link %i regained", i);
+ telemetry_lost[i] = false;
+ have_link = true;
+
+ } else if (!telemetry_lost[i]) {
+ /* telemetry was healthy also in last iteration
+ * we don't have to check a timeout */
+ have_link = true;
+ }
- /* still invalid state after the timeout interval, execute failsafe */
- if ((hrt_elapsed_time(&last_auto_state_valid) > FAILSAFE_DEFAULT_TIMEOUT) && (auto_res == TRANSITION_DENIED)) {
- /* AUTO mode denied, don't try RTL, switch to failsafe state LAND */
- auto_res = failsafe_state_transition(&status, FAILSAFE_STATE_LAND);
+ } else {
+ telemetry_last_dl_loss[i] = hrt_absolute_time();
- if (auto_res == TRANSITION_DENIED) {
- /* LAND not allowed, set TERMINATION state */
- (void)failsafe_state_transition(&status, FAILSAFE_STATE_TERMINATION);
- }
- }
+ if (!telemetry_lost[i]) {
+ mavlink_log_critical(mavlink_fd, "data link %i lost", i);
+ telemetry_lost[i] = true;
+ }
+ }
+ }
- } else {
- /* failsafe for manual modes */
- transition_result_t manual_res = TRANSITION_DENIED;
+ if (have_link) {
+ /* handle the case where data link was regained */
+ if (status.data_link_lost) {
+ status.data_link_lost = false;
+ status_changed = true;
+ }
- if (!status.condition_landed) {
- /* vehicle is not landed, try to perform RTL */
- manual_res = failsafe_state_transition(&status, FAILSAFE_STATE_RTL);
- }
+ } else {
+ if (!status.data_link_lost) {
+ mavlink_log_critical(mavlink_fd, "ALL DATA LINKS LOST");
+ status.data_link_lost = true;
+ status.data_link_lost_counter++;
+ status_changed = true;
+ }
+ }
- if (manual_res == TRANSITION_DENIED) {
- /* RTL not allowed (no global position estimate) or not wanted, try LAND */
- manual_res = failsafe_state_transition(&status, FAILSAFE_STATE_LAND);
+ /* Check engine failure
+ * only for fixed wing for now
+ */
+ if (!status.circuit_breaker_engaged_enginefailure_check &&
+ status.is_rotary_wing == false &&
+ armed.armed &&
+ ((actuator_controls.control[3] > ef_throttle_thres &&
+ battery.current_a / actuator_controls.control[3] <
+ ef_current2throttle_thres) ||
+ (status.engine_failure))) {
+ /* potential failure, measure time */
+ if (timestamp_engine_healthy > 0 &&
+ hrt_elapsed_time(&timestamp_engine_healthy) >
+ ef_time_thres * 1e6 &&
+ !status.engine_failure) {
+ status.engine_failure = true;
+ status_changed = true;
+ mavlink_log_critical(mavlink_fd, "Engine Failure");
+ }
- if (manual_res == TRANSITION_DENIED) {
- /* LAND not allowed, set TERMINATION state */
- (void)failsafe_state_transition(&status, FAILSAFE_STATE_TERMINATION);
- }
- }
- }
+ } else {
+ /* no failure reset flag */
+ timestamp_engine_healthy = hrt_absolute_time();
- } else {
- if (status.failsafe_state != FAILSAFE_STATE_NORMAL) {
- /* reset failsafe when disarmed */
- (void)failsafe_state_transition(&status, FAILSAFE_STATE_NORMAL);
- }
+ if (status.engine_failure) {
+ status.engine_failure = false;
+ status_changed = true;
}
}
- // TODO remove this hack
- /* flight termination in manual mode if assist switch is on POSCTL position */
- if (!status.is_rotary_wing && parachute_enabled && armed.armed && status.main_state == MAIN_STATE_MANUAL && sp_man.posctl_switch == SWITCH_POS_ON) {
- if (TRANSITION_CHANGED == failsafe_state_transition(&status, FAILSAFE_STATE_TERMINATION)) {
- tune_positive(armed.armed);
- }
- }
/* handle commands last, as the system needs to be updated to handle them */
orb_check(cmd_sub, &updated);
@@ -1386,10 +1747,56 @@ int commander_thread_main(int argc, char *argv[])
}
}
- /* check which state machines for changes, clear "changed" flag */
- bool arming_state_changed = check_arming_state_changed();
- bool main_state_changed = check_main_state_changed();
- bool failsafe_state_changed = check_failsafe_state_changed();
+ /* Check for failure combinations which lead to flight termination */
+ if (armed.armed) {
+ /* At this point the data link and the gps system have been checked
+ * If we are not in a manual (RC stick controlled mode)
+ * and both failed we want to terminate the flight */
+ if (status.main_state != MAIN_STATE_MANUAL &&
+ status.main_state != MAIN_STATE_ACRO &&
+ status.main_state != MAIN_STATE_ALTCTL &&
+ status.main_state != MAIN_STATE_POSCTL &&
+ ((status.data_link_lost && status.gps_failure) ||
+ (status.data_link_lost_cmd && status.gps_failure_cmd))) {
+ armed.force_failsafe = true;
+ status_changed = true;
+ static bool flight_termination_printed = false;
+
+ if (!flight_termination_printed) {
+ warnx("Flight termination because of data link loss && gps failure");
+ mavlink_log_critical(mavlink_fd, "DL and GPS lost: flight termination");
+ flight_termination_printed = true;
+ }
+
+ if (counter % (1000000 / COMMANDER_MONITORING_INTERVAL) == 0) {
+ mavlink_log_critical(mavlink_fd, "DL and GPS lost: flight termination");
+ }
+ }
+
+ /* At this point the rc signal and the gps system have been checked
+ * If we are in manual (controlled with RC):
+ * if both failed we want to terminate the flight */
+ if ((status.main_state == MAIN_STATE_ACRO ||
+ status.main_state == MAIN_STATE_MANUAL ||
+ status.main_state == MAIN_STATE_ALTCTL ||
+ status.main_state == MAIN_STATE_POSCTL) &&
+ ((status.rc_signal_lost && status.gps_failure) ||
+ (status.rc_signal_lost_cmd && status.gps_failure_cmd))) {
+ armed.force_failsafe = true;
+ status_changed = true;
+ static bool flight_termination_printed = false;
+
+ if (!flight_termination_printed) {
+ warnx("Flight termination because of RC signal loss && gps failure");
+ flight_termination_printed = true;
+ }
+
+ if (counter % (1000000 / COMMANDER_MONITORING_INTERVAL) == 0) {
+ mavlink_log_critical(mavlink_fd, "RC and GPS lost: flight termination");
+ }
+ }
+ }
+
hrt_abstime t1 = hrt_absolute_time();
@@ -1400,13 +1807,17 @@ int commander_thread_main(int argc, char *argv[])
/* update home position on arming if at least 2s from commander start spent to avoid setting home on in-air restart */
if (armed.armed && !was_armed && hrt_absolute_time() > start_time + 2000000 && status.condition_global_position_valid &&
- (global_position.eph < eph_epv_threshold) && (global_position.epv < eph_epv_threshold)) {
+ (global_position.eph < eph_threshold) && (global_position.epv < epv_threshold)) {
// TODO remove code duplication
home.lat = global_position.lat;
home.lon = global_position.lon;
home.alt = global_position.alt;
+ home.x = local_position.x;
+ home.y = local_position.y;
+ home.z = local_position.z;
+
warnx("home: lat = %.7f, lon = %.7f, alt = %.2f ", home.lat, home.lon, (double)home.alt);
mavlink_log_info(mavlink_fd, "home: %.7f, %.7f, %.2f", home.lat, home.lon, (double)home.alt);
@@ -1421,18 +1832,39 @@ int commander_thread_main(int argc, char *argv[])
/* mark home position as set */
status.condition_home_position_valid = true;
}
+
+ arming_state_changed = false;
}
was_armed = armed.armed;
+ /* now set navigation state according to failsafe and main state */
+ bool nav_state_changed = set_nav_state(&status, (bool)datalink_loss_enabled,
+ mission_result.finished,
+ mission_result.stay_in_failsafe);
+
+ // TODO handle mode changes by commands
if (main_state_changed) {
status_changed = true;
+ warnx("main state: %s", main_states_str[status.main_state]);
mavlink_log_info(mavlink_fd, "[cmd] main state: %s", main_states_str[status.main_state]);
+ main_state_changed = false;
}
- if (failsafe_state_changed) {
+ if (status.failsafe != failsafe_old) {
status_changed = true;
- mavlink_log_info(mavlink_fd, "[cmd] failsafe state: %s", failsafe_states_str[status.failsafe_state]);
+ if (status.failsafe) {
+ mavlink_log_critical(mavlink_fd, "failsafe mode on");
+ } else {
+ mavlink_log_critical(mavlink_fd, "failsafe mode off");
+ }
+ failsafe_old = status.failsafe;
+ }
+
+ if (nav_state_changed) {
+ status_changed = true;
+ warnx("nav state: %s", nav_states_str[status.nav_state]);
+ mavlink_log_info(mavlink_fd, "[cmd] nav state: %s", nav_states_str[status.nav_state]);
}
/* publish states (armed, control mode, vehicle status) at least with 5 Hz */
@@ -1449,7 +1881,8 @@ int commander_thread_main(int argc, char *argv[])
}
/* play arming and battery warning tunes */
- if (!arm_tune_played && armed.armed && (!safety.safety_switch_available || (safety.safety_switch_available && safety.safety_off))) {
+ if (!arm_tune_played && armed.armed && (!safety.safety_switch_available || (safety.safety_switch_available
+ && safety.safety_off))) {
/* play tune when armed */
set_tune(TONE_ARMING_WARNING_TUNE);
arm_tune_played = true;
@@ -1458,7 +1891,7 @@ int commander_thread_main(int argc, char *argv[])
/* play tune on battery critical */
set_tune(TONE_BATTERY_WARNING_FAST_TUNE);
- } else if (status.battery_warning == VEHICLE_BATTERY_WARNING_LOW || status.failsafe_state != FAILSAFE_STATE_NORMAL) {
+ } else if (status.battery_warning == VEHICLE_BATTERY_WARNING_LOW || status.failsafe) {
/* play tune on battery warning or failsafe */
set_tune(TONE_BATTERY_WARNING_SLOW_TUNE);
@@ -1517,6 +1950,7 @@ int commander_thread_main(int argc, char *argv[])
close(diff_pres_sub);
close(param_changed_sub);
close(battery_sub);
+ close(mission_pub);
thread_running = false;
@@ -1536,22 +1970,22 @@ check_valid(hrt_abstime timestamp, hrt_abstime timeout, bool valid_in, bool *val
}
void
-control_status_leds(vehicle_status_s *status, const actuator_armed_s *actuator_armed, bool changed)
+control_status_leds(vehicle_status_s *status_local, const actuator_armed_s *actuator_armed, bool changed)
{
/* driving rgbled */
if (changed) {
bool set_normal_color = false;
/* set mode */
- if (status->arming_state == ARMING_STATE_ARMED) {
+ if (status_local->arming_state == ARMING_STATE_ARMED) {
rgbled_set_mode(RGBLED_MODE_ON);
set_normal_color = true;
- } else if (status->arming_state == ARMING_STATE_ARMED_ERROR) {
+ } else if (status_local->arming_state == ARMING_STATE_ARMED_ERROR) {
rgbled_set_mode(RGBLED_MODE_BLINK_FAST);
rgbled_set_color(RGBLED_COLOR_RED);
- } else if (status->arming_state == ARMING_STATE_STANDBY) {
+ } else if (status_local->arming_state == ARMING_STATE_STANDBY) {
rgbled_set_mode(RGBLED_MODE_BREATHE);
set_normal_color = true;
@@ -1562,12 +1996,12 @@ control_status_leds(vehicle_status_s *status, const actuator_armed_s *actuator_a
if (set_normal_color) {
/* set color */
- if (status->battery_warning == VEHICLE_BATTERY_WARNING_LOW || status->failsafe_state != FAILSAFE_STATE_NORMAL) {
+ if (status_local->battery_warning == VEHICLE_BATTERY_WARNING_LOW || status_local->failsafe) {
rgbled_set_color(RGBLED_COLOR_AMBER);
/* VEHICLE_BATTERY_WARNING_CRITICAL handled as ARMING_STATE_ARMED_ERROR / ARMING_STATE_STANDBY_ERROR */
} else {
- if (status->condition_local_position_valid) {
+ if (status_local->condition_local_position_valid) {
rgbled_set_color(RGBLED_COLOR_GREEN);
} else {
@@ -1600,7 +2034,7 @@ control_status_leds(vehicle_status_s *status, const actuator_armed_s *actuator_a
#endif
/* give system warnings on error LED, XXX maybe add memory usage warning too */
- if (status->load > 0.95f) {
+ if (status_local->load > 0.95f) {
if (leds_counter % 2 == 0) {
led_toggle(LED_AMBER);
}
@@ -1613,11 +2047,29 @@ control_status_leds(vehicle_status_s *status, const actuator_armed_s *actuator_a
}
transition_result_t
-set_main_state_rc(struct vehicle_status_s *status, struct manual_control_setpoint_s *sp_man)
+set_main_state_rc(struct vehicle_status_s *status_local, struct manual_control_setpoint_s *sp_man)
{
/* set main state according to RC switches */
transition_result_t res = TRANSITION_DENIED;
+ /* if offboard is set allready by a mavlink command, abort */
+ if (status.offboard_control_set_by_command) {
+ return main_state_transition(status_local, MAIN_STATE_OFFBOARD);
+ }
+
+ /* offboard switch overrides main switch */
+ if (sp_man->offboard_switch == SWITCH_POS_ON) {
+ res = main_state_transition(status_local, MAIN_STATE_OFFBOARD);
+
+ if (res == TRANSITION_DENIED) {
+ print_reject_mode(status_local, "OFFBOARD");
+
+ } else {
+ return res;
+ }
+ }
+
+ /* offboard switched off or denied, check main mode switch */
switch (sp_man->mode_switch) {
case SWITCH_POS_NONE:
res = TRANSITION_NOT_CHANGED;
@@ -1625,58 +2077,101 @@ set_main_state_rc(struct vehicle_status_s *status, struct manual_control_setpoin
case SWITCH_POS_OFF: // MANUAL
if (sp_man->acro_switch == SWITCH_POS_ON) {
- res = main_state_transition(status, MAIN_STATE_ACRO);
+ res = main_state_transition(status_local, MAIN_STATE_ACRO);
} else {
- res = main_state_transition(status, MAIN_STATE_MANUAL);
+ res = main_state_transition(status_local, MAIN_STATE_MANUAL);
}
+
// TRANSITION_DENIED is not possible here
break;
case SWITCH_POS_MIDDLE: // ASSIST
if (sp_man->posctl_switch == SWITCH_POS_ON) {
- res = main_state_transition(status, MAIN_STATE_POSCTL);
+ res = main_state_transition(status_local, MAIN_STATE_POSCTL);
if (res != TRANSITION_DENIED) {
break; // changed successfully or already in this state
}
- // else fallback to ALTCTL
- print_reject_mode(status, "POSCTL");
+ print_reject_mode(status_local, "POSCTL");
}
- res = main_state_transition(status, MAIN_STATE_ALTCTL);
+ // fallback to ALTCTL
+ res = main_state_transition(status_local, MAIN_STATE_ALTCTL);
if (res != TRANSITION_DENIED) {
break; // changed successfully or already in this mode
}
if (sp_man->posctl_switch != SWITCH_POS_ON) {
- print_reject_mode(status, "ALTCTL");
+ print_reject_mode(status_local, "ALTCTL");
}
- // else fallback to MANUAL
- res = main_state_transition(status, MAIN_STATE_MANUAL);
+ // fallback to MANUAL
+ res = main_state_transition(status_local, MAIN_STATE_MANUAL);
// TRANSITION_DENIED is not possible here
break;
case SWITCH_POS_ON: // AUTO
- res = main_state_transition(status, MAIN_STATE_AUTO);
+ if (sp_man->return_switch == SWITCH_POS_ON) {
+ res = main_state_transition(status_local, MAIN_STATE_AUTO_RTL);
+
+ if (res != TRANSITION_DENIED) {
+ break; // changed successfully or already in this state
+ }
+
+ print_reject_mode(status_local, "AUTO_RTL");
+
+ // fallback to LOITER if home position not set
+ res = main_state_transition(status_local, MAIN_STATE_AUTO_LOITER);
+
+ if (res != TRANSITION_DENIED) {
+ break; // changed successfully or already in this state
+ }
+
+ } else if (sp_man->loiter_switch == SWITCH_POS_ON) {
+ res = main_state_transition(status_local, MAIN_STATE_AUTO_LOITER);
+
+ if (res != TRANSITION_DENIED) {
+ break; // changed successfully or already in this state
+ }
+
+ print_reject_mode(status_local, "AUTO_LOITER");
+
+ } else {
+ res = main_state_transition(status_local, MAIN_STATE_AUTO_MISSION);
+
+ if (res != TRANSITION_DENIED) {
+ break; // changed successfully or already in this state
+ }
+
+ print_reject_mode(status_local, "AUTO_MISSION");
+
+ // fallback to LOITER if home position not set
+ res = main_state_transition(status_local, MAIN_STATE_AUTO_LOITER);
+
+ if (res != TRANSITION_DENIED) {
+ break; // changed successfully or already in this state
+ }
+ }
+
+ // fallback to POSCTL
+ res = main_state_transition(status_local, MAIN_STATE_POSCTL);
if (res != TRANSITION_DENIED) {
- break; // changed successfully or already in this state
+ break; // changed successfully or already in this state
}
- // else fallback to ALTCTL (POSCTL likely will not work too)
- print_reject_mode(status, "AUTO");
- res = main_state_transition(status, MAIN_STATE_ALTCTL);
+ // fallback to ALTCTL
+ res = main_state_transition(status_local, MAIN_STATE_ALTCTL);
if (res != TRANSITION_DENIED) {
break; // changed successfully or already in this state
}
- // else fallback to MANUAL
- res = main_state_transition(status, MAIN_STATE_MANUAL);
+ // fallback to MANUAL
+ res = main_state_transition(status_local, MAIN_STATE_MANUAL);
// TRANSITION_DENIED is not possible here
break;
@@ -1688,127 +2183,203 @@ set_main_state_rc(struct vehicle_status_s *status, struct manual_control_setpoin
}
void
-
set_control_mode()
{
- /* set vehicle_control_mode according to main state and failsafe state */
+ /* set vehicle_control_mode according to set_navigation_state */
control_mode.flag_armed = armed.armed;
+ /* TODO: check this */
control_mode.flag_external_manual_override_ok = !status.is_rotary_wing;
control_mode.flag_system_hil_enabled = status.hil_state == HIL_STATE_ON;
+ control_mode.flag_control_offboard_enabled = false;
+
+ switch (status.nav_state) {
+ case NAVIGATION_STATE_MANUAL:
+ control_mode.flag_control_manual_enabled = true;
+ control_mode.flag_control_auto_enabled = false;
+ control_mode.flag_control_rates_enabled = status.is_rotary_wing;
+ control_mode.flag_control_attitude_enabled = status.is_rotary_wing;
+ control_mode.flag_control_altitude_enabled = false;
+ control_mode.flag_control_climb_rate_enabled = false;
+ control_mode.flag_control_position_enabled = false;
+ control_mode.flag_control_velocity_enabled = false;
+ control_mode.flag_control_termination_enabled = false;
+ break;
+
+ case NAVIGATION_STATE_ALTCTL:
+ control_mode.flag_control_manual_enabled = true;
+ control_mode.flag_control_auto_enabled = false;
+ control_mode.flag_control_rates_enabled = true;
+ control_mode.flag_control_attitude_enabled = true;
+ control_mode.flag_control_altitude_enabled = true;
+ control_mode.flag_control_climb_rate_enabled = true;
+ control_mode.flag_control_position_enabled = false;
+ control_mode.flag_control_velocity_enabled = false;
+ control_mode.flag_control_termination_enabled = false;
+ break;
+
+ case NAVIGATION_STATE_POSCTL:
+ control_mode.flag_control_manual_enabled = true;
+ control_mode.flag_control_auto_enabled = false;
+ control_mode.flag_control_rates_enabled = true;
+ control_mode.flag_control_attitude_enabled = true;
+ control_mode.flag_control_altitude_enabled = true;
+ control_mode.flag_control_climb_rate_enabled = true;
+ control_mode.flag_control_position_enabled = true;
+ control_mode.flag_control_velocity_enabled = true;
+ control_mode.flag_control_termination_enabled = false;
+ break;
+
+ case NAVIGATION_STATE_AUTO_MISSION:
+ case NAVIGATION_STATE_AUTO_LOITER:
+ case NAVIGATION_STATE_AUTO_RTL:
+ case NAVIGATION_STATE_AUTO_RCRECOVER:
+ case NAVIGATION_STATE_AUTO_RTGS:
+ case NAVIGATION_STATE_AUTO_LANDENGFAIL:
+ control_mode.flag_control_manual_enabled = false;
+ control_mode.flag_control_auto_enabled = true;
+ control_mode.flag_control_rates_enabled = true;
+ control_mode.flag_control_attitude_enabled = true;
+ control_mode.flag_control_altitude_enabled = true;
+ control_mode.flag_control_climb_rate_enabled = true;
+ control_mode.flag_control_position_enabled = true;
+ control_mode.flag_control_velocity_enabled = true;
+ control_mode.flag_control_termination_enabled = false;
+ break;
+
+ case NAVIGATION_STATE_AUTO_LANDGPSFAIL:
+ control_mode.flag_control_manual_enabled = false;
+ control_mode.flag_control_auto_enabled = false;
+ control_mode.flag_control_rates_enabled = true;
+ control_mode.flag_control_attitude_enabled = true;
+ control_mode.flag_control_altitude_enabled = false;
+ control_mode.flag_control_climb_rate_enabled = true;
+ control_mode.flag_control_position_enabled = false;
+ control_mode.flag_control_velocity_enabled = false;
+ control_mode.flag_control_termination_enabled = false;
+ break;
+
+ case NAVIGATION_STATE_ACRO:
+ control_mode.flag_control_manual_enabled = true;
+ control_mode.flag_control_auto_enabled = false;
+ control_mode.flag_control_rates_enabled = true;
+ control_mode.flag_control_attitude_enabled = false;
+ control_mode.flag_control_altitude_enabled = false;
+ control_mode.flag_control_climb_rate_enabled = false;
+ control_mode.flag_control_position_enabled = false;
+ control_mode.flag_control_velocity_enabled = false;
+ control_mode.flag_control_termination_enabled = false;
+ break;
+
+
+ case NAVIGATION_STATE_LAND:
+ control_mode.flag_control_manual_enabled = false;
+ control_mode.flag_control_auto_enabled = true;
+ control_mode.flag_control_rates_enabled = true;
+ control_mode.flag_control_attitude_enabled = true;
+ /* in failsafe LAND mode position may be not available */
+ control_mode.flag_control_position_enabled = status.condition_local_position_valid;
+ control_mode.flag_control_velocity_enabled = status.condition_local_position_valid;
+ control_mode.flag_control_altitude_enabled = true;
+ control_mode.flag_control_climb_rate_enabled = true;
+ control_mode.flag_control_termination_enabled = false;
+ break;
- control_mode.flag_control_termination_enabled = false;
+ case NAVIGATION_STATE_DESCEND:
+ /* TODO: check if this makes sense */
+ control_mode.flag_control_manual_enabled = false;
+ control_mode.flag_control_auto_enabled = true;
+ control_mode.flag_control_rates_enabled = true;
+ control_mode.flag_control_attitude_enabled = true;
+ control_mode.flag_control_position_enabled = false;
+ control_mode.flag_control_velocity_enabled = false;
+ control_mode.flag_control_altitude_enabled = false;
+ control_mode.flag_control_climb_rate_enabled = true;
+ control_mode.flag_control_termination_enabled = false;
+ break;
+
+ case NAVIGATION_STATE_TERMINATION:
+ /* disable all controllers on termination */
+ control_mode.flag_control_manual_enabled = false;
+ control_mode.flag_control_auto_enabled = false;
+ control_mode.flag_control_rates_enabled = false;
+ control_mode.flag_control_attitude_enabled = false;
+ control_mode.flag_control_position_enabled = false;
+ control_mode.flag_control_velocity_enabled = false;
+ control_mode.flag_control_altitude_enabled = false;
+ control_mode.flag_control_climb_rate_enabled = false;
+ control_mode.flag_control_termination_enabled = true;
+ break;
- /* set this flag when navigator should act */
- bool navigator_enabled = false;
+ case NAVIGATION_STATE_OFFBOARD:
+ control_mode.flag_control_manual_enabled = false;
+ control_mode.flag_control_auto_enabled = false;
+ control_mode.flag_control_offboard_enabled = true;
- switch (status.failsafe_state) {
- case FAILSAFE_STATE_NORMAL:
- switch (status.main_state) {
- case MAIN_STATE_MANUAL:
- control_mode.flag_control_manual_enabled = true;
- control_mode.flag_control_auto_enabled = false;
- control_mode.flag_control_rates_enabled = status.is_rotary_wing;
- control_mode.flag_control_attitude_enabled = status.is_rotary_wing;
+ switch (sp_offboard.mode) {
+ case OFFBOARD_CONTROL_MODE_DIRECT_RATES:
+ control_mode.flag_control_rates_enabled = true;
+ control_mode.flag_control_attitude_enabled = false;
control_mode.flag_control_altitude_enabled = false;
control_mode.flag_control_climb_rate_enabled = false;
control_mode.flag_control_position_enabled = false;
control_mode.flag_control_velocity_enabled = false;
break;
- case MAIN_STATE_ALTCTL:
- control_mode.flag_control_manual_enabled = true;
- control_mode.flag_control_auto_enabled = false;
+ case OFFBOARD_CONTROL_MODE_DIRECT_ATTITUDE:
control_mode.flag_control_rates_enabled = true;
control_mode.flag_control_attitude_enabled = true;
- control_mode.flag_control_altitude_enabled = true;
- control_mode.flag_control_climb_rate_enabled = true;
+ control_mode.flag_control_altitude_enabled = false;
+ control_mode.flag_control_climb_rate_enabled = false;
+ control_mode.flag_control_position_enabled = false;
+ control_mode.flag_control_velocity_enabled = false;
+ break;
+
+ case OFFBOARD_CONTROL_MODE_DIRECT_FORCE:
+ control_mode.flag_control_rates_enabled = true;
+ control_mode.flag_control_attitude_enabled = false;
+ control_mode.flag_control_force_enabled = true;
+ control_mode.flag_control_altitude_enabled = false;
+ control_mode.flag_control_climb_rate_enabled = false;
control_mode.flag_control_position_enabled = false;
control_mode.flag_control_velocity_enabled = false;
break;
- case MAIN_STATE_POSCTL:
- control_mode.flag_control_manual_enabled = true;
- control_mode.flag_control_auto_enabled = false;
+ case OFFBOARD_CONTROL_MODE_DIRECT_LOCAL_NED:
+ case OFFBOARD_CONTROL_MODE_DIRECT_LOCAL_OFFSET_NED:
+ case OFFBOARD_CONTROL_MODE_DIRECT_BODY_NED:
+ case OFFBOARD_CONTROL_MODE_DIRECT_BODY_OFFSET_NED:
control_mode.flag_control_rates_enabled = true;
control_mode.flag_control_attitude_enabled = true;
control_mode.flag_control_altitude_enabled = true;
control_mode.flag_control_climb_rate_enabled = true;
control_mode.flag_control_position_enabled = true;
control_mode.flag_control_velocity_enabled = true;
+ //XXX: the flags could depend on sp_offboard.ignore
break;
- case MAIN_STATE_AUTO:
- navigator_enabled = true;
- break;
-
- case MAIN_STATE_ACRO:
- control_mode.flag_control_manual_enabled = true;
- control_mode.flag_control_auto_enabled = false;
- control_mode.flag_control_rates_enabled = true;
+ default:
+ control_mode.flag_control_rates_enabled = false;
control_mode.flag_control_attitude_enabled = false;
control_mode.flag_control_altitude_enabled = false;
control_mode.flag_control_climb_rate_enabled = false;
control_mode.flag_control_position_enabled = false;
control_mode.flag_control_velocity_enabled = false;
- break;
-
- default:
- break;
}
-
- break;
-
- case FAILSAFE_STATE_RTL:
- navigator_enabled = true;
- break;
-
- case FAILSAFE_STATE_LAND:
- navigator_enabled = true;
- break;
-
- case FAILSAFE_STATE_TERMINATION:
- /* disable all controllers on termination */
- control_mode.flag_control_manual_enabled = false;
- control_mode.flag_control_auto_enabled = false;
- control_mode.flag_control_rates_enabled = false;
- control_mode.flag_control_attitude_enabled = false;
- control_mode.flag_control_position_enabled = false;
- control_mode.flag_control_velocity_enabled = false;
- control_mode.flag_control_altitude_enabled = false;
- control_mode.flag_control_climb_rate_enabled = false;
- control_mode.flag_control_termination_enabled = true;
break;
default:
break;
}
-
- /* navigator has control, set control mode flags according to nav state*/
- if (navigator_enabled) {
- control_mode.flag_control_manual_enabled = false;
- control_mode.flag_control_auto_enabled = true;
- control_mode.flag_control_rates_enabled = true;
- control_mode.flag_control_attitude_enabled = true;
-
- /* in failsafe LAND mode position may be not available */
- control_mode.flag_control_position_enabled = status.condition_local_position_valid;
- control_mode.flag_control_velocity_enabled = status.condition_local_position_valid;
-
- control_mode.flag_control_altitude_enabled = true;
- control_mode.flag_control_climb_rate_enabled = true;
- }
}
void
-print_reject_mode(struct vehicle_status_s *status, const char *msg)
+print_reject_mode(struct vehicle_status_s *status_local, const char *msg)
{
hrt_abstime t = hrt_absolute_time();
if (t - last_print_mode_reject_time > PRINT_MODE_REJECT_INTERVAL) {
last_print_mode_reject_time = t;
- char s[80];
- sprintf(s, "#audio: REJECT %s", msg);
- mavlink_log_critical(mavlink_fd, s);
+ mavlink_log_critical(mavlink_fd, "REJECT %s", msg);
/* only buzz if armed, because else we're driving people nuts indoors
they really need to look at the leds as well. */
@@ -1823,9 +2394,7 @@ print_reject_arm(const char *msg)
if (t - last_print_mode_reject_time > PRINT_MODE_REJECT_INTERVAL) {
last_print_mode_reject_time = t;
- char s[80];
- sprintf(s, "#audio: %s", msg);
- mavlink_log_critical(mavlink_fd, s);
+ mavlink_log_critical(mavlink_fd, msg);
tune_negative(true);
}
}
@@ -1838,12 +2407,12 @@ void answer_command(struct vehicle_command_s &cmd, enum VEHICLE_CMD_RESULT resul
break;
case VEHICLE_CMD_RESULT_DENIED:
- mavlink_log_critical(mavlink_fd, "#audio: command denied: %u", cmd.command);
+ mavlink_log_critical(mavlink_fd, "command denied: %u", cmd.command);
tune_negative(true);
break;
case VEHICLE_CMD_RESULT_FAILED:
- mavlink_log_critical(mavlink_fd, "#audio: command failed: %u", cmd.command);
+ mavlink_log_critical(mavlink_fd, "command failed: %u", cmd.command);
tune_negative(true);
break;
@@ -1854,7 +2423,7 @@ void answer_command(struct vehicle_command_s &cmd, enum VEHICLE_CMD_RESULT resul
break;
case VEHICLE_CMD_RESULT_UNSUPPORTED:
- mavlink_log_critical(mavlink_fd, "#audio: command unsupported: %u", cmd.command);
+ mavlink_log_critical(mavlink_fd, "command unsupported: %u", cmd.command);
tune_negative(true);
break;
@@ -1939,9 +2508,8 @@ void *commander_low_prio_loop(void *arg)
int calib_ret = ERROR;
/* try to go to INIT/PREFLIGHT arming state */
-
- // XXX disable interrupts in arming_state_transition
- if (TRANSITION_DENIED == arming_state_transition(&status, &safety, ARMING_STATE_INIT, &armed)) {
+ if (TRANSITION_DENIED == arming_state_transition(&status, &safety, ARMING_STATE_INIT, &armed,
+ true /* fRunPreArmChecks */, mavlink_fd)) {
answer_command(cmd, VEHICLE_CMD_RESULT_DENIED);
break;
}
@@ -2004,7 +2572,7 @@ void *commander_low_prio_loop(void *arg)
tune_negative(true);
}
- arming_state_transition(&status, &safety, ARMING_STATE_STANDBY, &armed);
+ arming_state_transition(&status, &safety, ARMING_STATE_STANDBY, &armed, true /* fRunPreArmChecks */, mavlink_fd);
break;
}
diff --git a/src/modules/commander/commander_helper.cpp b/src/modules/commander/commander_helper.cpp
index 940a04aa1..2022e99fb 100644
--- a/src/modules/commander/commander_helper.cpp
+++ b/src/modules/commander/commander_helper.cpp
@@ -1,9 +1,6 @@
/****************************************************************************
*
- * Copyright (C) 2013 PX4 Development Team. All rights reserved.
- * Author: Thomas Gubler <thomasgubler@student.ethz.ch>
- * Julian Oes <joes@student.ethz.ch>
- * Anton Babushkin <anton.babushkin@me.com>
+ * Copyright (c) 2013, 2014 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -37,6 +34,11 @@
/**
* @file commander_helper.cpp
* Commander helper functions implementations
+ *
+ * @author Thomas Gubler <thomasgubler@student.ethz.ch>
+ * @author Julian Oes <julian@oes.ch>
+ * @author Anton Babushkin <anton.babushkin@me.com>
+ *
*/
#include <stdio.h>
@@ -209,12 +211,18 @@ int led_init()
/* the blue LED is only available on FMUv1 & AeroCore but not FMUv2 */
(void)ioctl(leds, LED_ON, LED_BLUE);
+ /* switch blue off */
+ led_off(LED_BLUE);
+
/* we consider the amber led mandatory */
if (ioctl(leds, LED_ON, LED_AMBER)) {
warnx("Amber LED: ioctl fail\n");
return ERROR;
}
+ /* switch amber off */
+ led_off(LED_AMBER);
+
/* then try RGB LEDs, this can fail on FMUv1*/
rgbleds = open(RGBLED_DEVICE_PATH, 0);
@@ -273,15 +281,17 @@ void rgbled_set_pattern(rgbled_pattern_t *pattern)
}
}
-float battery_remaining_estimate_voltage(float voltage, float discharged)
+float battery_remaining_estimate_voltage(float voltage, float discharged, float throttle_normalized)
{
float ret = 0;
static param_t bat_v_empty_h;
static param_t bat_v_full_h;
static param_t bat_n_cells_h;
static param_t bat_capacity_h;
- static float bat_v_empty = 3.2f;
- static float bat_v_full = 4.0f;
+ static param_t bat_v_load_drop_h;
+ static float bat_v_empty = 3.4f;
+ static float bat_v_full = 4.2f;
+ static float bat_v_load_drop = 0.06f;
static int bat_n_cells = 3;
static float bat_capacity = -1.0f;
static bool initialized = false;
@@ -289,23 +299,26 @@ float battery_remaining_estimate_voltage(float voltage, float discharged)
if (!initialized) {
bat_v_empty_h = param_find("BAT_V_EMPTY");
- bat_v_full_h = param_find("BAT_V_FULL");
+ bat_v_full_h = param_find("BAT_V_CHARGED");
bat_n_cells_h = param_find("BAT_N_CELLS");
bat_capacity_h = param_find("BAT_CAPACITY");
+ bat_v_load_drop_h = param_find("BAT_V_LOAD_DROP");
initialized = true;
}
if (counter % 100 == 0) {
param_get(bat_v_empty_h, &bat_v_empty);
param_get(bat_v_full_h, &bat_v_full);
+ param_get(bat_v_load_drop_h, &bat_v_load_drop);
param_get(bat_n_cells_h, &bat_n_cells);
param_get(bat_capacity_h, &bat_capacity);
}
counter++;
- /* remaining charge estimate based on voltage */
- float remaining_voltage = (voltage - bat_n_cells * bat_v_empty) / (bat_n_cells * (bat_v_full - bat_v_empty));
+ /* remaining charge estimate based on voltage and internal resistance (drop under load) */
+ float bat_v_full_dynamic = bat_v_full - (bat_v_load_drop * throttle_normalized);
+ float remaining_voltage = (voltage - (bat_n_cells * bat_v_empty)) / (bat_n_cells * (bat_v_full_dynamic - bat_v_empty));
if (bat_capacity > 0.0f) {
/* if battery capacity is known, use discharged current for estimate, but don't show more than voltage estimate */
diff --git a/src/modules/commander/commander_helper.h b/src/modules/commander/commander_helper.h
index e75f2592f..4a77fe487 100644
--- a/src/modules/commander/commander_helper.h
+++ b/src/modules/commander/commander_helper.h
@@ -1,8 +1,6 @@
/****************************************************************************
*
- * Copyright (C) 2013 PX4 Development Team. All rights reserved.
- * Author: Thomas Gubler <thomasgubler@student.ethz.ch>
- * Julian Oes <joes@student.ethz.ch>
+ * Copyright (c) 2013, 2014 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -36,6 +34,9 @@
/**
* @file commander_helper.h
* Commander helper functions definitions
+ *
+ * @author Thomas Gubler <thomasgubler@student.ethz.ch>
+ * @author Julian Oes <julian@oes.ch>
*/
#ifndef COMMANDER_HELPER_H_
@@ -77,8 +78,11 @@ void rgbled_set_pattern(rgbled_pattern_t *pattern);
* Use integral of current if battery capacity known (BAT_CAPACITY parameter set),
* else use simple estimate based on voltage.
*
+ * @param voltage the current battery voltage
+ * @param discharged the discharged capacity
+ * @param throttle_normalized the normalized throttle magnitude from 0 to 1. Negative throttle should be converted to this range as well, as it consumes energy.
* @return the estimated remaining capacity in 0..1
*/
-float battery_remaining_estimate_voltage(float voltage, float discharged);
+float battery_remaining_estimate_voltage(float voltage, float discharged, float throttle_normalized);
#endif /* COMMANDER_HELPER_H_ */
diff --git a/src/modules/commander/commander_params.c b/src/modules/commander/commander_params.c
index 80ca68f21..1b0c4258b 100644
--- a/src/modules/commander/commander_params.c
+++ b/src/modules/commander/commander_params.c
@@ -39,7 +39,7 @@
*
* @author Lorenz Meier <lm@inf.ethz.ch>
* @author Thomas Gubler <thomasgubler@student.ethz.ch>
- * @author Julian Oes <joes@student.ethz.ch>
+ * @author Julian Oes <julian@oes.ch>
*/
#include <nuttx/config.h>
@@ -65,7 +65,17 @@ PARAM_DEFINE_FLOAT(BAT_V_EMPTY, 3.4f);
*
* @group Battery Calibration
*/
-PARAM_DEFINE_FLOAT(BAT_V_FULL, 3.9f);
+PARAM_DEFINE_FLOAT(BAT_V_CHARGED, 4.2f);
+
+/**
+ * Voltage drop per cell on 100% load
+ *
+ * This implicitely defines the internal resistance
+ * to maximum current ratio and assumes linearity.
+ *
+ * @group Battery Calibration
+ */
+PARAM_DEFINE_FLOAT(BAT_V_LOAD_DROP, 0.07f);
/**
* Number of cells.
@@ -84,3 +94,80 @@ PARAM_DEFINE_INT32(BAT_N_CELLS, 3);
* @group Battery Calibration
*/
PARAM_DEFINE_FLOAT(BAT_CAPACITY, -1.0f);
+
+/**
+ * Datalink loss mode enabled.
+ *
+ * Set to 1 to enable actions triggered when the datalink is lost.
+ *
+ * @group commander
+ * @min 0
+ * @max 1
+ */
+PARAM_DEFINE_INT32(COM_DL_LOSS_EN, 0);
+
+ /** Datalink loss time threshold
+ *
+ * After this amount of seconds without datalink the data link lost mode triggers
+ *
+ * @group commander
+ * @unit second
+ * @min 0
+ * @max 30
+ */
+PARAM_DEFINE_INT32(COM_DL_LOSS_T, 10);
+
+/** Datalink regain time threshold
+ *
+ * After a data link loss: after this this amount of seconds with a healthy datalink the 'datalink loss'
+ * flag is set back to false
+ *
+ * @group commander
+ * @unit second
+ * @min 0
+ * @max 30
+ */
+PARAM_DEFINE_INT32(COM_DL_REG_T, 0);
+
+/** Engine Failure Throttle Threshold
+ *
+ * Engine failure triggers only above this throttle value
+ *
+ * @group commander
+ * @min 0.0f
+ * @max 1.0f
+ */
+PARAM_DEFINE_FLOAT(COM_EF_THROT, 0.5f);
+
+/** Engine Failure Current/Throttle Threshold
+ *
+ * Engine failure triggers only below this current/throttle value
+ *
+ * @group commander
+ * @min 0.0f
+ * @max 7.0f
+ */
+PARAM_DEFINE_FLOAT(COM_EF_C2T, 5.0f);
+
+/** Engine Failure Time Threshold
+ *
+ * Engine failure triggers only if the throttle threshold and the
+ * current to throttle threshold are violated for this time
+ *
+ * @group commander
+ * @unit second
+ * @min 0.0f
+ * @max 7.0f
+ */
+PARAM_DEFINE_FLOAT(COM_EF_TIME, 10.0f);
+
+/** RC loss time threshold
+ *
+ * After this amount of seconds without RC connection the rc lost flag is set to true
+ *
+ * @group commander
+ * @unit second
+ * @min 0
+ * @max 35
+ */
+PARAM_DEFINE_FLOAT(COM_RC_LOSS_T, 0.5);
diff --git a/src/modules/commander/commander_tests/commander_tests.cpp b/src/modules/commander/commander_tests/commander_tests.cpp
index 0abb84a82..2bfa5d0bb 100644
--- a/src/modules/commander/commander_tests/commander_tests.cpp
+++ b/src/modules/commander/commander_tests/commander_tests.cpp
@@ -48,7 +48,5 @@ extern "C" __EXPORT int commander_tests_main(int argc, char *argv[]);
int commander_tests_main(int argc, char *argv[])
{
- stateMachineHelperTest();
-
- return 0;
+ return stateMachineHelperTest() ? 0 : -1;
}
diff --git a/src/modules/commander/commander_tests/state_machine_helper_test.cpp b/src/modules/commander/commander_tests/state_machine_helper_test.cpp
index ee0d08391..874090e93 100644
--- a/src/modules/commander/commander_tests/state_machine_helper_test.cpp
+++ b/src/modules/commander/commander_tests/state_machine_helper_test.cpp
@@ -49,7 +49,7 @@ public:
StateMachineHelperTest();
virtual ~StateMachineHelperTest();
- virtual void runTests(void);
+ virtual bool run_tests(void);
private:
bool armingStateTransitionTest();
@@ -183,12 +183,12 @@ bool StateMachineHelperTest::armingStateTransitionTest(void)
// Safety switch arming tests
- { "transition: init to standby, no safety switch",
+ { "transition: standby to armed, no safety switch",
{ ARMING_STATE_STANDBY, ATT_DISARMED, ATT_READY_TO_ARM }, HIL_STATE_OFF, ATT_SENSORS_INITIALIZED, ATT_SAFETY_NOT_AVAILABLE, ATT_SAFETY_OFF,
ARMING_STATE_ARMED,
{ ARMING_STATE_ARMED, ATT_ARMED, ATT_READY_TO_ARM }, TRANSITION_CHANGED },
- { "transition: init to standby, safety switch off",
+ { "transition: standby to armed, safety switch off",
{ ARMING_STATE_STANDBY, ATT_DISARMED, ATT_READY_TO_ARM }, HIL_STATE_OFF, ATT_SENSORS_INITIALIZED, ATT_SAFETY_AVAILABLE, ATT_SAFETY_OFF,
ARMING_STATE_ARMED,
{ ARMING_STATE_ARMED, ATT_ARMED, ATT_READY_TO_ARM }, TRANSITION_CHANGED },
@@ -286,7 +286,7 @@ bool StateMachineHelperTest::armingStateTransitionTest(void)
armed.ready_to_arm = test->current_state.ready_to_arm;
// Attempt transition
- transition_result_t result = arming_state_transition(&status, &safety, test->requested_state, &armed);
+ transition_result_t result = arming_state_transition(&status, &safety, test->requested_state, &armed, false /* no pre-arm checks */, 0 /* no mavlink_fd */);
// Validate result of transition
ut_assert(test->assertMsg, test->expected_transition_result == result);
@@ -300,70 +300,151 @@ bool StateMachineHelperTest::armingStateTransitionTest(void)
bool StateMachineHelperTest::mainStateTransitionTest(void)
{
- struct vehicle_status_s current_state;
- main_state_t new_main_state;
+ // This structure represent a single test case for testing Main State transitions.
+ typedef struct {
+ const char* assertMsg; // Text to show when test case fails
+ uint8_t condition_bits; // Bits for various condition_* values
+ main_state_t from_state; // State prior to transition request
+ main_state_t to_state; // State to transition to
+ transition_result_t expected_transition_result; // Expected result from main_state_transition call
+ } MainTransitionTest_t;
+
+ // Bits for condition_bits
+ #define MTT_ALL_NOT_VALID 0
+ #define MTT_ROTARY_WING 1 << 0
+ #define MTT_LOC_ALT_VALID 1 << 1
+ #define MTT_LOC_POS_VALID 1 << 2
+ #define MTT_HOME_POS_VALID 1 << 3
+ #define MTT_GLOBAL_POS_VALID 1 << 4
- // Identical states.
- current_state.main_state = MAIN_STATE_MANUAL;
- new_main_state = MAIN_STATE_MANUAL;
- ut_assert("no transition: identical states",
- TRANSITION_NOT_CHANGED == main_state_transition(&current_state, new_main_state));
- ut_assert("current state: manual", MAIN_STATE_MANUAL == current_state.main_state);
-
- // AUTO to MANUAL.
- current_state.main_state = MAIN_STATE_AUTO;
- new_main_state = MAIN_STATE_MANUAL;
- ut_assert("transition changed: auto to manual",
- TRANSITION_CHANGED == main_state_transition(&current_state, new_main_state));
- ut_assert("new state: manual", MAIN_STATE_MANUAL == current_state.main_state);
-
- // MANUAL to ALTCTRL.
- current_state.main_state = MAIN_STATE_MANUAL;
- current_state.condition_local_altitude_valid = true;
- new_main_state = MAIN_STATE_ALTCTL;
- ut_assert("tranisition: manual to altctrl",
- TRANSITION_CHANGED == main_state_transition(&current_state, new_main_state));
- ut_assert("new state: altctrl", MAIN_STATE_ALTCTL == current_state.main_state);
-
- // MANUAL to ALTCTRL, invalid local altitude.
- current_state.main_state = MAIN_STATE_MANUAL;
- current_state.condition_local_altitude_valid = false;
- new_main_state = MAIN_STATE_ALTCTL;
- ut_assert("no transition: invalid local altitude",
- TRANSITION_DENIED == main_state_transition(&current_state, new_main_state));
- ut_assert("current state: manual", MAIN_STATE_MANUAL == current_state.main_state);
-
- // MANUAL to POSCTRL.
- current_state.main_state = MAIN_STATE_MANUAL;
- current_state.condition_local_position_valid = true;
- new_main_state = MAIN_STATE_POSCTL;
- ut_assert("transition: manual to posctrl",
- TRANSITION_CHANGED == main_state_transition(&current_state, new_main_state));
- ut_assert("current state: posctrl", MAIN_STATE_POSCTL == current_state.main_state);
-
- // MANUAL to POSCTRL, invalid local position.
- current_state.main_state = MAIN_STATE_MANUAL;
- current_state.condition_local_position_valid = false;
- new_main_state = MAIN_STATE_POSCTL;
- ut_assert("no transition: invalid position",
- TRANSITION_DENIED == main_state_transition(&current_state, new_main_state));
- ut_assert("current state: manual", MAIN_STATE_MANUAL == current_state.main_state);
-
- // MANUAL to AUTO.
- current_state.main_state = MAIN_STATE_MANUAL;
- current_state.condition_global_position_valid = true;
- new_main_state = MAIN_STATE_AUTO;
- ut_assert("transition: manual to auto",
- TRANSITION_CHANGED == main_state_transition(&current_state, new_main_state));
- ut_assert("current state: auto", MAIN_STATE_AUTO == current_state.main_state);
-
- // MANUAL to AUTO, invalid global position.
- current_state.main_state = MAIN_STATE_MANUAL;
- current_state.condition_global_position_valid = false;
- new_main_state = MAIN_STATE_AUTO;
- ut_assert("no transition: invalid global position",
- TRANSITION_DENIED == main_state_transition(&current_state, new_main_state));
- ut_assert("current state: manual", MAIN_STATE_MANUAL == current_state.main_state);
+ static const MainTransitionTest_t rgMainTransitionTests[] = {
+
+ // TRANSITION_NOT_CHANGED tests
+
+ { "no transition: identical states",
+ MTT_ALL_NOT_VALID,
+ MAIN_STATE_MANUAL, MAIN_STATE_MANUAL, TRANSITION_NOT_CHANGED },
+
+ // TRANSITION_CHANGED tests
+
+ { "transition: MANUAL to ACRO",
+ MTT_ALL_NOT_VALID,
+ MAIN_STATE_MANUAL, MAIN_STATE_ACRO, TRANSITION_CHANGED },
+
+ { "transition: ACRO to MANUAL",
+ MTT_ALL_NOT_VALID,
+ MAIN_STATE_ACRO, MAIN_STATE_MANUAL, TRANSITION_CHANGED },
+
+ { "transition: MANUAL to AUTO_MISSION - global position valid, home position valid",
+ MTT_GLOBAL_POS_VALID | MTT_HOME_POS_VALID,
+ MAIN_STATE_MANUAL, MAIN_STATE_AUTO_MISSION, TRANSITION_CHANGED },
+
+ { "transition: AUTO_MISSION to MANUAL - global position valid, home position valid",
+ MTT_GLOBAL_POS_VALID | MTT_HOME_POS_VALID,
+ MAIN_STATE_AUTO_MISSION, MAIN_STATE_MANUAL, TRANSITION_CHANGED },
+
+ { "transition: MANUAL to AUTO_LOITER - global position valid",
+ MTT_GLOBAL_POS_VALID,
+ MAIN_STATE_MANUAL, MAIN_STATE_AUTO_LOITER, TRANSITION_CHANGED },
+
+ { "transition: AUTO_LOITER to MANUAL - global position valid",
+ MTT_GLOBAL_POS_VALID,
+ MAIN_STATE_AUTO_LOITER, MAIN_STATE_MANUAL, TRANSITION_CHANGED },
+
+ { "transition: MANUAL to AUTO_RTL - global position valid, home position valid",
+ MTT_GLOBAL_POS_VALID | MTT_HOME_POS_VALID,
+ MAIN_STATE_MANUAL, MAIN_STATE_AUTO_RTL, TRANSITION_CHANGED },
+
+ { "transition: AUTO_RTL to MANUAL - global position valid, home position valid",
+ MTT_GLOBAL_POS_VALID | MTT_HOME_POS_VALID,
+ MAIN_STATE_AUTO_RTL, MAIN_STATE_MANUAL, TRANSITION_CHANGED },
+
+ { "transition: MANUAL to ALTCTL - not rotary",
+ MTT_ALL_NOT_VALID,
+ MAIN_STATE_MANUAL, MAIN_STATE_ALTCTL, TRANSITION_CHANGED },
+
+ { "transition: MANUAL to ALTCTL - rotary, global position not valid, local altitude valid",
+ MTT_ROTARY_WING | MTT_LOC_ALT_VALID,
+ MAIN_STATE_MANUAL, MAIN_STATE_ALTCTL, TRANSITION_CHANGED },
+
+ { "transition: MANUAL to ALTCTL - rotary, global position valid, local altitude not valid",
+ MTT_ROTARY_WING | MTT_GLOBAL_POS_VALID,
+ MAIN_STATE_MANUAL, MAIN_STATE_ALTCTL, TRANSITION_CHANGED },
+
+ { "transition: ALTCTL to MANUAL - local altitude valid",
+ MTT_LOC_ALT_VALID,
+ MAIN_STATE_ALTCTL, MAIN_STATE_MANUAL, TRANSITION_CHANGED },
+
+ { "transition: MANUAL to POSCTL - local position not valid, global position valid",
+ MTT_GLOBAL_POS_VALID,
+ MAIN_STATE_MANUAL, MAIN_STATE_POSCTL, TRANSITION_CHANGED },
+
+ { "transition: MANUAL to POSCTL - local position valid, global position not valid",
+ MTT_LOC_POS_VALID,
+ MAIN_STATE_MANUAL, MAIN_STATE_POSCTL, TRANSITION_CHANGED },
+
+ { "transition: POSCTL to MANUAL - local position valid, global position valid",
+ MTT_LOC_POS_VALID,
+ MAIN_STATE_POSCTL, MAIN_STATE_MANUAL, TRANSITION_CHANGED },
+
+ // TRANSITION_DENIED tests
+
+ { "no transition: MANUAL to AUTO_MISSION - global position not valid",
+ MTT_ALL_NOT_VALID,
+ MAIN_STATE_MANUAL, MAIN_STATE_AUTO_MISSION, TRANSITION_DENIED },
+
+ { "no transition: MANUAL to AUTO_LOITER - global position not valid",
+ MTT_ALL_NOT_VALID,
+ MAIN_STATE_MANUAL, MAIN_STATE_AUTO_LOITER, TRANSITION_DENIED },
+
+ { "no transition: MANUAL to AUTO_RTL - global position not valid, home position not valid",
+ MTT_ALL_NOT_VALID,
+ MAIN_STATE_MANUAL, MAIN_STATE_AUTO_RTL, TRANSITION_DENIED },
+
+ { "no transition: MANUAL to AUTO_RTL - global position not valid, home position valid",
+ MTT_HOME_POS_VALID,
+ MAIN_STATE_MANUAL, MAIN_STATE_AUTO_RTL, TRANSITION_DENIED },
+
+ { "no transition: MANUAL to AUTO_RTL - global position valid, home position not valid",
+ MTT_GLOBAL_POS_VALID,
+ MAIN_STATE_MANUAL, MAIN_STATE_AUTO_RTL, TRANSITION_DENIED },
+
+ { "no transition: MANUAL to ALTCTL - rotary, global position not valid, local altitude not valid",
+ MTT_ROTARY_WING,
+ MAIN_STATE_MANUAL, MAIN_STATE_ALTCTL, TRANSITION_DENIED },
+
+ { "no transition: MANUAL to POSCTL - local position not valid, global position not valid",
+ MTT_ALL_NOT_VALID,
+ MAIN_STATE_MANUAL, MAIN_STATE_POSCTL, TRANSITION_DENIED },
+ };
+
+ size_t cMainTransitionTests = sizeof(rgMainTransitionTests) / sizeof(rgMainTransitionTests[0]);
+ for (size_t i=0; i<cMainTransitionTests; i++) {
+ const MainTransitionTest_t* test = &rgMainTransitionTests[i];
+
+ // Setup initial machine state
+ struct vehicle_status_s current_state;
+ current_state.main_state = test->from_state;
+ current_state.is_rotary_wing = test->condition_bits & MTT_ROTARY_WING;
+ current_state.condition_local_altitude_valid = test->condition_bits & MTT_LOC_ALT_VALID;
+ current_state.condition_local_position_valid = test->condition_bits & MTT_LOC_POS_VALID;
+ current_state.condition_home_position_valid = test->condition_bits & MTT_HOME_POS_VALID;
+ current_state.condition_global_position_valid = test->condition_bits & MTT_GLOBAL_POS_VALID;
+
+ // Attempt transition
+ transition_result_t result = main_state_transition(&current_state, test->to_state);
+
+ // Validate result of transition
+ ut_assert(test->assertMsg, test->expected_transition_result == result);
+ if (test->expected_transition_result == result) {
+ if (test->expected_transition_result == TRANSITION_CHANGED) {
+ ut_assert(test->assertMsg, test->to_state == current_state.main_state);
+ } else {
+ ut_assert(test->assertMsg, test->from_state == current_state.main_state);
+ }
+ }
+ }
+
return true;
}
@@ -407,16 +488,13 @@ bool StateMachineHelperTest::isSafeTest(void)
return true;
}
-void StateMachineHelperTest::runTests(void)
+bool StateMachineHelperTest::run_tests(void)
{
ut_run_test(armingStateTransitionTest);
ut_run_test(mainStateTransitionTest);
ut_run_test(isSafeTest);
+
+ return (_tests_failed == 0);
}
-void stateMachineHelperTest(void)
-{
- StateMachineHelperTest* test = new StateMachineHelperTest();
- test->runTests();
- test->printResults();
-}
+ut_declare_test(stateMachineHelperTest, StateMachineHelperTest) \ No newline at end of file
diff --git a/src/modules/commander/commander_tests/state_machine_helper_test.h b/src/modules/commander/commander_tests/state_machine_helper_test.h
index bbf66255e..cf6719095 100644
--- a/src/modules/commander/commander_tests/state_machine_helper_test.h
+++ b/src/modules/commander/commander_tests/state_machine_helper_test.h
@@ -39,6 +39,6 @@
#ifndef STATE_MACHINE_HELPER_TEST_H_
#define STATE_MACHINE_HELPER_TEST_
-void stateMachineHelperTest(void);
+bool stateMachineHelperTest(void);
#endif /* STATE_MACHINE_HELPER_TEST_H_ */
diff --git a/src/modules/commander/gyro_calibration.cpp b/src/modules/commander/gyro_calibration.cpp
index cbc2844c1..8ab14dd52 100644
--- a/src/modules/commander/gyro_calibration.cpp
+++ b/src/modules/commander/gyro_calibration.cpp
@@ -63,7 +63,10 @@ static const char *sensor_name = "gyro";
int do_gyro_calibration(int mavlink_fd)
{
mavlink_log_info(mavlink_fd, CAL_STARTED_MSG, sensor_name);
- mavlink_log_info(mavlink_fd, "don't move system");
+ mavlink_log_info(mavlink_fd, "HOLD STILL");
+
+ /* wait for the user to respond */
+ sleep(2);
struct gyro_scale gyro_scale = {
0.0f,
@@ -92,7 +95,7 @@ int do_gyro_calibration(int mavlink_fd)
unsigned poll_errcount = 0;
/* subscribe to gyro sensor topic */
- int sub_sensor_gyro = orb_subscribe(ORB_ID(sensor_gyro));
+ int sub_sensor_gyro = orb_subscribe(ORB_ID(sensor_gyro0));
struct gyro_report gyro_report;
while (calibration_counter < calibration_count) {
@@ -104,7 +107,7 @@ int do_gyro_calibration(int mavlink_fd)
int poll_ret = poll(fds, 1, 1000);
if (poll_ret > 0) {
- orb_copy(ORB_ID(sensor_gyro), sub_sensor_gyro, &gyro_report);
+ orb_copy(ORB_ID(sensor_gyro0), sub_sensor_gyro, &gyro_report);
gyro_scale.x_offset += gyro_report.x;
gyro_scale.y_offset += gyro_report.y;
gyro_scale.z_offset += gyro_report.z;
diff --git a/src/modules/commander/mag_calibration.cpp b/src/modules/commander/mag_calibration.cpp
index 0ead22f77..7be8de9c6 100644
--- a/src/modules/commander/mag_calibration.cpp
+++ b/src/modules/commander/mag_calibration.cpp
@@ -145,7 +145,7 @@ int do_mag_calibration(int mavlink_fd)
}
if (res == OK) {
- int sub_mag = orb_subscribe(ORB_ID(sensor_mag));
+ int sub_mag = orb_subscribe(ORB_ID(sensor_mag0));
struct mag_report mag;
/* limit update rate to get equally spaced measurements over time (in ms) */
@@ -155,7 +155,7 @@ int do_mag_calibration(int mavlink_fd)
uint64_t calibration_deadline = hrt_absolute_time() + calibration_interval;
unsigned poll_errcount = 0;
- mavlink_log_info(mavlink_fd, "rotate in a figure 8 around all axis");
+ mavlink_log_info(mavlink_fd, "Turn on all sides: front/back,left/right,up/down");
calibration_counter = 0;
@@ -170,7 +170,7 @@ int do_mag_calibration(int mavlink_fd)
int poll_ret = poll(fds, 1, 1000);
if (poll_ret > 0) {
- orb_copy(ORB_ID(sensor_mag), sub_mag, &mag);
+ orb_copy(ORB_ID(sensor_mag0), sub_mag, &mag);
x[calibration_counter] = mag.x;
y[calibration_counter] = mag.y;
diff --git a/src/modules/commander/mag_calibration.h b/src/modules/commander/mag_calibration.h
index a101cd7b1..c14f948cc 100644
--- a/src/modules/commander/mag_calibration.h
+++ b/src/modules/commander/mag_calibration.h
@@ -1,6 +1,6 @@
/****************************************************************************
*
- * Copyright (C) 2013 PX4 Development Team. All rights reserved.
+ * Copyright (c) 2013, 2014 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
diff --git a/src/modules/commander/px4_custom_mode.h b/src/modules/commander/px4_custom_mode.h
index e0f8dc95d..eaf309288 100644
--- a/src/modules/commander/px4_custom_mode.h
+++ b/src/modules/commander/px4_custom_mode.h
@@ -16,6 +16,7 @@ enum PX4_CUSTOM_MAIN_MODE {
PX4_CUSTOM_MAIN_MODE_POSCTL,
PX4_CUSTOM_MAIN_MODE_AUTO,
PX4_CUSTOM_MAIN_MODE_ACRO,
+ PX4_CUSTOM_MAIN_MODE_OFFBOARD,
};
enum PX4_CUSTOM_SUB_MODE_AUTO {
@@ -25,6 +26,7 @@ enum PX4_CUSTOM_SUB_MODE_AUTO {
PX4_CUSTOM_SUB_MODE_AUTO_MISSION,
PX4_CUSTOM_SUB_MODE_AUTO_RTL,
PX4_CUSTOM_SUB_MODE_AUTO_LAND,
+ PX4_CUSTOM_SUB_MODE_AUTO_RTGS
};
union px4_custom_mode {
diff --git a/src/modules/commander/state_machine_helper.cpp b/src/modules/commander/state_machine_helper.cpp
index 87309b238..e37019d02 100644
--- a/src/modules/commander/state_machine_helper.cpp
+++ b/src/modules/commander/state_machine_helper.cpp
@@ -1,8 +1,6 @@
/****************************************************************************
*
- * Copyright (C) 2013 PX4 Development Team. All rights reserved.
- * Author: Thomas Gubler <thomasgubler@student.ethz.ch>
- * Julian Oes <joes@student.ethz.ch>
+ * Copyright (c) 2013, 2014 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -36,6 +34,9 @@
/**
* @file state_machine_helper.cpp
* State machine helper functions implementations
+ *
+ * @author Thomas Gubler <thomasgubler@student.ethz.ch>
+ * @author Julian Oes <julian@oes.ch>
*/
#include <stdio.h>
@@ -45,14 +46,19 @@
#include <dirent.h>
#include <fcntl.h>
#include <string.h>
+#include <math.h>
#include <uORB/uORB.h>
#include <uORB/topics/vehicle_status.h>
#include <uORB/topics/actuator_controls.h>
+#include <uORB/topics/differential_pressure.h>
+#include <uORB/topics/airspeed.h>
#include <systemlib/systemlib.h>
#include <systemlib/param/param.h>
#include <systemlib/err.h>
#include <drivers/drv_hrt.h>
+#include <drivers/drv_accel.h>
+#include <drivers/drv_airspeed.h>
#include <drivers/drv_device.h>
#include <mavlink/mavlink_log.h>
@@ -65,28 +71,24 @@
#endif
static const int ERROR = -1;
-static bool arming_state_changed = true;
-static bool main_state_changed = true;
-static bool failsafe_state_changed = true;
-
// This array defines the arming state transitions. The rows are the new state, and the columns
// are the current state. Using new state and current state you can index into the array which
// will be true for a valid transition or false for a invalid transition. In some cases even
// though the transition is marked as true additional checks must be made. See arming_state_transition
// code for those checks.
static const bool arming_transitions[ARMING_STATE_MAX][ARMING_STATE_MAX] = {
- // INIT, STANDBY, ARMED, ARMED_ERROR, STANDBY_ERROR, REBOOT, IN_AIR_RESTORE
- { /* ARMING_STATE_INIT */ true, true, false, false, false, false, false },
- { /* ARMING_STATE_STANDBY */ true, true, true, true, false, false, false },
- { /* ARMING_STATE_ARMED */ false, true, true, false, false, false, true },
- { /* ARMING_STATE_ARMED_ERROR */ false, false, true, true, false, false, false },
- { /* ARMING_STATE_STANDBY_ERROR */ true, true, false, true, true, false, false },
- { /* ARMING_STATE_REBOOT */ true, true, false, false, true, true, true },
- { /* ARMING_STATE_IN_AIR_RESTORE */ false, false, false, false, false, false, false }, // NYI
+ // INIT, STANDBY, ARMED, ARMED_ERROR, STANDBY_ERROR, REBOOT, IN_AIR_RESTORE
+ { /* ARMING_STATE_INIT */ true, true, false, false, false, false, false },
+ { /* ARMING_STATE_STANDBY */ true, true, true, true, false, false, false },
+ { /* ARMING_STATE_ARMED */ false, true, true, false, false, false, true },
+ { /* ARMING_STATE_ARMED_ERROR */ false, false, true, true, false, false, false },
+ { /* ARMING_STATE_STANDBY_ERROR */ true, true, false, true, true, false, false },
+ { /* ARMING_STATE_REBOOT */ true, true, false, false, true, true, true },
+ { /* ARMING_STATE_IN_AIR_RESTORE */ false, false, false, false, false, false, false }, // NYI
};
// You can index into the array with an arming_state_t in order to get it's textual representation
-static const char *state_names[ARMING_STATE_MAX] = {
+static const char * const state_names[ARMING_STATE_MAX] = {
"ARMING_STATE_INIT",
"ARMING_STATE_STANDBY",
"ARMING_STATE_ARMED",
@@ -97,28 +99,42 @@ static const char *state_names[ARMING_STATE_MAX] = {
};
transition_result_t
-arming_state_transition(struct vehicle_status_s *status, /// current vehicle status
- const struct safety_s *safety, /// current safety settings
- arming_state_t new_arming_state, /// arming state requested
- struct actuator_armed_s *armed, /// current armed status
- const int mavlink_fd) /// mavlink fd for error reporting, 0 for none
+arming_state_transition(struct vehicle_status_s *status, ///< current vehicle status
+ const struct safety_s *safety, ///< current safety settings
+ arming_state_t new_arming_state, ///< arming state requested
+ struct actuator_armed_s *armed, ///< current armed status
+ bool fRunPreArmChecks, ///< true: run the pre-arm checks, false: no pre-arm checks, for unit testing
+ const int mavlink_fd) ///< mavlink fd for error reporting, 0 for none
{
// Double check that our static arrays are still valid
ASSERT(ARMING_STATE_INIT == 0);
ASSERT(ARMING_STATE_IN_AIR_RESTORE == ARMING_STATE_MAX - 1);
- /*
- * Perform an atomic state update
- */
- irqstate_t flags = irqsave();
-
transition_result_t ret = TRANSITION_DENIED;
+ arming_state_t current_arming_state = status->arming_state;
+ bool feedback_provided = false;
/* only check transition if the new state is actually different from the current one */
- if (new_arming_state == status->arming_state) {
+ if (new_arming_state == current_arming_state) {
ret = TRANSITION_NOT_CHANGED;
} else {
+
+ /*
+ * Get sensing state if necessary
+ */
+ int prearm_ret = OK;
+
+ /* only perform the check if we have to */
+ if (fRunPreArmChecks && new_arming_state == ARMING_STATE_ARMED) {
+ prearm_ret = prearm_check(status, mavlink_fd);
+ }
+
+ /*
+ * Perform an atomic state update
+ */
+ irqstate_t flags = irqsave();
+
/* enforce lockdown in HIL */
if (status->hil_state == HIL_STATE_ON) {
armed->lockdown = true;
@@ -133,15 +149,55 @@ arming_state_transition(struct vehicle_status_s *status, /// current
if (valid_transition) {
// We have a good transition. Now perform any secondary validation.
if (new_arming_state == ARMING_STATE_ARMED) {
- // Fail transition if we need safety switch press
- // Allow if coming from in air restore
+
+ // Do not perform pre-arm checks if coming from in air restore
// Allow if HIL_STATE_ON
- if (status->arming_state != ARMING_STATE_IN_AIR_RESTORE && status->hil_state == HIL_STATE_OFF && safety->safety_switch_available && !safety->safety_off) {
- if (mavlink_fd) {
- mavlink_log_critical(mavlink_fd, "#audio: NOT ARMING: Press safety switch first.");
+ if (status->arming_state != ARMING_STATE_IN_AIR_RESTORE &&
+ status->hil_state == HIL_STATE_OFF) {
+
+ // Fail transition if pre-arm check fails
+ if (prearm_ret) {
+ /* the prearm check already prints the reject reason */
+ feedback_provided = true;
+ valid_transition = false;
+
+ // Fail transition if we need safety switch press
+ } else if (safety->safety_switch_available && !safety->safety_off) {
+
+ mavlink_log_critical(mavlink_fd, "NOT ARMING: Press safety switch first!");
+ feedback_provided = true;
+ valid_transition = false;
+ }
+
+ // Perform power checks only if circuit breaker is not
+ // engaged for these checks
+ if (!status->circuit_breaker_engaged_power_check) {
+ // Fail transition if power is not good
+ if (!status->condition_power_input_valid) {
+
+ mavlink_log_critical(mavlink_fd, "NOT ARMING: Connect power module.");
+ feedback_provided = true;
+ valid_transition = false;
+ }
+
+ // Fail transition if power levels on the avionics rail
+ // are measured but are insufficient
+ if (status->condition_power_input_valid && (status->avionics_power_rail_voltage > 0.0f)) {
+ // Check avionics rail voltages
+ if (status->avionics_power_rail_voltage < 4.75f) {
+ mavlink_log_critical(mavlink_fd, "NOT ARMING: Avionics power low: %6.2f Volt", (double)status->avionics_power_rail_voltage);
+ feedback_provided = true;
+ valid_transition = false;
+ } else if (status->avionics_power_rail_voltage < 4.9f) {
+ mavlink_log_critical(mavlink_fd, "CAUTION: Avionics power low: %6.2f Volt", (double)status->avionics_power_rail_voltage);
+ feedback_provided = true;
+ } else if (status->avionics_power_rail_voltage > 5.4f) {
+ mavlink_log_critical(mavlink_fd, "CAUTION: Avionics power high: %6.2f Volt", (double)status->avionics_power_rail_voltage);
+ feedback_provided = true;
+ }
+ }
}
- valid_transition = false;
}
} else if (new_arming_state == ARMING_STATE_STANDBY && status->arming_state == ARMING_STATE_ARMED_ERROR) {
@@ -156,6 +212,8 @@ arming_state_transition(struct vehicle_status_s *status, /// current
/* Sensors need to be initialized for STANDBY state */
if (new_arming_state == ARMING_STATE_STANDBY && !status->condition_system_sensors_initialized) {
+ mavlink_log_critical(mavlink_fd, "NOT ARMING: Sensors not operational.");
+ feedback_provided = true;
valid_transition = false;
}
@@ -165,21 +223,21 @@ arming_state_transition(struct vehicle_status_s *status, /// current
armed->ready_to_arm = new_arming_state == ARMING_STATE_ARMED || new_arming_state == ARMING_STATE_STANDBY;
ret = TRANSITION_CHANGED;
status->arming_state = new_arming_state;
- arming_state_changed = true;
}
- }
- /* end of atomic state update */
- irqrestore(flags);
+ /* end of atomic state update */
+ irqrestore(flags);
+ }
if (ret == TRANSITION_DENIED) {
- static const char *errMsg = "Invalid arming transition from %s to %s";
+ const char * str = "INVAL: %s - %s";
+ /* only print to console here by default as this is too technical to be useful during operation */
+ warnx(str, state_names[status->arming_state], state_names[new_arming_state]);
- if (mavlink_fd) {
- mavlink_log_critical(mavlink_fd, errMsg, state_names[status->arming_state], state_names[new_arming_state]);
+ /* print to MAVLink if we didn't provide any feedback yet */
+ if (!feedback_provided) {
+ mavlink_log_critical(mavlink_fd, str, state_names[status->arming_state], state_names[new_arming_state]);
}
-
- warnx(errMsg, state_names[status->arming_state], state_names[new_arming_state]);
}
return ret;
@@ -199,69 +257,67 @@ bool is_safe(const struct vehicle_status_s *status, const struct safety_s *safet
}
}
-bool
-check_arming_state_changed()
-{
- if (arming_state_changed) {
- arming_state_changed = false;
- return true;
-
- } else {
- return false;
- }
-}
-
transition_result_t
main_state_transition(struct vehicle_status_s *status, main_state_t new_main_state)
{
transition_result_t ret = TRANSITION_DENIED;
- /* transition may be denied even if requested the same state because conditions may be changed */
+ /* transition may be denied even if the same state is requested because conditions may have changed */
switch (new_main_state) {
case MAIN_STATE_MANUAL:
- ret = TRANSITION_CHANGED;
- break;
-
case MAIN_STATE_ACRO:
ret = TRANSITION_CHANGED;
break;
case MAIN_STATE_ALTCTL:
-
/* need at minimum altitude estimate */
+ /* TODO: add this for fixedwing as well */
if (!status->is_rotary_wing ||
(status->condition_local_altitude_valid ||
status->condition_global_position_valid)) {
ret = TRANSITION_CHANGED;
}
-
break;
case MAIN_STATE_POSCTL:
-
/* need at minimum local position estimate */
if (status->condition_local_position_valid ||
status->condition_global_position_valid) {
ret = TRANSITION_CHANGED;
}
-
break;
- case MAIN_STATE_AUTO:
-
+ case MAIN_STATE_AUTO_LOITER:
/* need global position estimate */
if (status->condition_global_position_valid) {
ret = TRANSITION_CHANGED;
}
+ break;
+ case MAIN_STATE_AUTO_MISSION:
+ case MAIN_STATE_AUTO_RTL:
+ /* need global position and home position */
+ if (status->condition_global_position_valid && status->condition_home_position_valid) {
+ ret = TRANSITION_CHANGED;
+ }
break;
- }
+ case MAIN_STATE_OFFBOARD:
+
+ /* need offboard signal */
+ if (!status->offboard_control_signal_lost) {
+ ret = TRANSITION_CHANGED;
+ }
+
+ break;
+
+ case MAIN_STATE_MAX:
+ default:
+ break;
+ }
if (ret == TRANSITION_CHANGED) {
if (status->main_state != new_main_state) {
status->main_state = new_main_state;
- main_state_changed = true;
-
} else {
ret = TRANSITION_NOT_CHANGED;
}
@@ -270,70 +326,35 @@ main_state_transition(struct vehicle_status_s *status, main_state_t new_main_sta
return ret;
}
-bool
-check_main_state_changed()
-{
- if (main_state_changed) {
- main_state_changed = false;
- return true;
-
- } else {
- return false;
- }
-}
-
-bool
-check_failsafe_state_changed()
-{
- if (failsafe_state_changed) {
- failsafe_state_changed = false;
- return true;
-
- } else {
- return false;
- }
-}
-
/**
-* Transition from one hil state to another
-*/
-int hil_state_transition(hil_state_t new_state, int status_pub, struct vehicle_status_s *current_status, const int mavlink_fd)
+ * Transition from one hil state to another
+ */
+transition_result_t hil_state_transition(hil_state_t new_state, int status_pub, struct vehicle_status_s *current_status, const int mavlink_fd)
{
- bool valid_transition = false;
- int ret = ERROR;
+ transition_result_t ret = TRANSITION_DENIED;
if (current_status->hil_state == new_state) {
- valid_transition = true;
+ ret = TRANSITION_NOT_CHANGED;
} else {
-
switch (new_state) {
-
case HIL_STATE_OFF:
-
/* we're in HIL and unexpected things can happen if we disable HIL now */
mavlink_log_critical(mavlink_fd, "#audio: Not switching off HIL (safety)");
- valid_transition = false;
-
+ ret = TRANSITION_DENIED;
break;
case HIL_STATE_ON:
-
if (current_status->arming_state == ARMING_STATE_INIT
|| current_status->arming_state == ARMING_STATE_STANDBY
|| current_status->arming_state == ARMING_STATE_STANDBY_ERROR) {
- mavlink_log_critical(mavlink_fd, "Switched to ON hil state");
- valid_transition = true;
-
- // Disable publication of all attached sensors
-
+ /* Disable publication of all attached sensors */
/* list directory */
DIR *d;
d = opendir("/dev");
if (d) {
-
struct dirent *direntry;
char devname[24];
@@ -388,290 +409,309 @@ int hil_state_transition(hil_state_t new_state, int status_pub, struct vehicle_s
printf("Disabling %s: %s\n", devname, (block_ret == OK) ? "OK" : "ERROR");
}
-
closedir(d);
+ ret = TRANSITION_CHANGED;
+ mavlink_log_critical(mavlink_fd, "Switched to ON hil state");
+
} else {
/* failed opening dir */
- warnx("FAILED LISTING DEVICE ROOT DIRECTORY");
- return 1;
+ mavlink_log_info(mavlink_fd, "FAILED LISTING DEVICE ROOT DIRECTORY");
+ ret = TRANSITION_DENIED;
}
+ } else {
+ mavlink_log_critical(mavlink_fd, "Not switching to HIL when armed");
+ ret = TRANSITION_DENIED;
}
-
break;
default:
- warnx("Unknown hil state");
+ warnx("Unknown HIL state");
break;
}
}
- if (valid_transition) {
+ if (ret == TRANSITION_CHANGED) {
current_status->hil_state = new_state;
-
current_status->timestamp = hrt_absolute_time();
- orb_publish(ORB_ID(vehicle_status), status_pub, current_status);
-
// XXX also set lockdown here
-
- ret = OK;
-
- } else {
- mavlink_log_critical(mavlink_fd, "REJECTING invalid hil state transition");
+ orb_publish(ORB_ID(vehicle_status), status_pub, current_status);
}
-
return ret;
}
-
/**
-* Transition from one failsafe state to another
-*/
-transition_result_t failsafe_state_transition(struct vehicle_status_s *status, failsafe_state_t new_failsafe_state)
+ * Check failsafe and main status and set navigation status for navigator accordingly
+ */
+bool set_nav_state(struct vehicle_status_s *status, const bool data_link_loss_enabled, const bool mission_finished,
+ const bool stay_in_failsafe)
{
- transition_result_t ret = TRANSITION_DENIED;
+ navigation_state_t nav_state_old = status->nav_state;
- /* transition may be denied even if requested the same state because conditions may be changed */
- if (status->failsafe_state == FAILSAFE_STATE_TERMINATION) {
- /* transitions from TERMINATION to other states not allowed */
- if (new_failsafe_state == FAILSAFE_STATE_TERMINATION) {
- ret = TRANSITION_NOT_CHANGED;
+ bool armed = (status->arming_state == ARMING_STATE_ARMED || status->arming_state == ARMING_STATE_ARMED_ERROR);
+ status->failsafe = false;
+
+ /* evaluate main state to decide in normal (non-failsafe) mode */
+ switch (status->main_state) {
+ case MAIN_STATE_ACRO:
+ case MAIN_STATE_MANUAL:
+ case MAIN_STATE_ALTCTL:
+ case MAIN_STATE_POSCTL:
+ /* require RC for all manual modes */
+ if ((status->rc_signal_lost || status->rc_signal_lost_cmd) && armed) {
+ status->failsafe = true;
+
+ if (status->condition_global_position_valid && status->condition_home_position_valid) {
+ status->nav_state = NAVIGATION_STATE_AUTO_RCRECOVER;
+ } else if (status->condition_local_position_valid) {
+ status->nav_state = NAVIGATION_STATE_LAND;
+ } else if (status->condition_local_altitude_valid) {
+ status->nav_state = NAVIGATION_STATE_DESCEND;
+ } else {
+ status->nav_state = NAVIGATION_STATE_TERMINATION;
+ }
+
+ } else {
+ switch (status->main_state) {
+ case MAIN_STATE_ACRO:
+ status->nav_state = NAVIGATION_STATE_ACRO;
+ break;
+
+ case MAIN_STATE_MANUAL:
+ status->nav_state = NAVIGATION_STATE_MANUAL;
+ break;
+
+ case MAIN_STATE_ALTCTL:
+ status->nav_state = NAVIGATION_STATE_ALTCTL;
+ break;
+
+ case MAIN_STATE_POSCTL:
+ status->nav_state = NAVIGATION_STATE_POSCTL;
+ break;
+
+ default:
+ status->nav_state = NAVIGATION_STATE_MANUAL;
+ break;
+ }
}
+ break;
- } else {
- switch (new_failsafe_state) {
- case FAILSAFE_STATE_NORMAL:
- /* always allowed (except from TERMINATION state) */
- ret = TRANSITION_CHANGED;
- break;
+ case MAIN_STATE_AUTO_MISSION:
+
+ /* go into failsafe
+ * - if commanded to do so
+ * - if we have an engine failure
+ * - depending on datalink, RC and if the mission is finished */
+
+ /* first look at the commands */
+ if (status->engine_failure_cmd) {
+ status->nav_state = NAVIGATION_STATE_AUTO_LANDENGFAIL;
+ } else if (status->data_link_lost_cmd) {
+ status->nav_state = NAVIGATION_STATE_AUTO_RTGS;
+ } else if (status->gps_failure_cmd) {
+ status->nav_state = NAVIGATION_STATE_AUTO_LANDGPSFAIL;
+ } else if (status->rc_signal_lost_cmd) {
+ status->nav_state = NAVIGATION_STATE_AUTO_RCRECOVER;
+
+ /* finished handling commands which have priority, now handle failures */
+ } else if (status->gps_failure) {
+ status->nav_state = NAVIGATION_STATE_AUTO_LANDGPSFAIL;
+ } else if (status->engine_failure) {
+ status->nav_state = NAVIGATION_STATE_AUTO_LANDENGFAIL;
+
+ /* datalink loss enabled:
+ * check for datalink lost: this should always trigger RTGS */
+ } else if (data_link_loss_enabled && status->data_link_lost) {
+ status->failsafe = true;
+
+ if (status->condition_global_position_valid && status->condition_home_position_valid) {
+ status->nav_state = NAVIGATION_STATE_AUTO_RTGS;
+ } else if (status->condition_local_position_valid) {
+ status->nav_state = NAVIGATION_STATE_LAND;
+ } else if (status->condition_local_altitude_valid) {
+ status->nav_state = NAVIGATION_STATE_DESCEND;
+ } else {
+ status->nav_state = NAVIGATION_STATE_TERMINATION;
+ }
- case FAILSAFE_STATE_RTL:
+ /* datalink loss disabled:
+ * check if both, RC and datalink are lost during the mission
+ * or RC is lost after the mission is finished: this should always trigger RCRECOVER */
+ } else if (!data_link_loss_enabled && ((status->rc_signal_lost && status->data_link_lost) ||
+ (status->rc_signal_lost && mission_finished))) {
+ status->failsafe = true;
- /* global position and home position required for RTL */
if (status->condition_global_position_valid && status->condition_home_position_valid) {
- status->set_nav_state = NAV_STATE_RTL;
- status->set_nav_state_timestamp = hrt_absolute_time();
- ret = TRANSITION_CHANGED;
+ status->nav_state = NAVIGATION_STATE_AUTO_RCRECOVER;
+ } else if (status->condition_local_position_valid) {
+ status->nav_state = NAVIGATION_STATE_LAND;
+ } else if (status->condition_local_altitude_valid) {
+ status->nav_state = NAVIGATION_STATE_DESCEND;
+ } else {
+ status->nav_state = NAVIGATION_STATE_TERMINATION;
}
- break;
+ /* stay where you are if you should stay in failsafe, otherwise everything is perfect */
+ } else if (!stay_in_failsafe){
+ status->nav_state = NAVIGATION_STATE_AUTO_MISSION;
+ }
+ break;
- case FAILSAFE_STATE_LAND:
+ case MAIN_STATE_AUTO_LOITER:
+ /* go into failsafe on a engine failure */
+ if (status->engine_failure) {
+ status->nav_state = NAVIGATION_STATE_AUTO_LANDENGFAIL;
+ /* also go into failsafe if just datalink is lost */
+ } else if (status->data_link_lost && data_link_loss_enabled) {
+ status->failsafe = true;
- /* at least relative altitude estimate required for landing */
- if (status->condition_local_altitude_valid || status->condition_global_position_valid) {
- status->set_nav_state = NAV_STATE_LAND;
- status->set_nav_state_timestamp = hrt_absolute_time();
- ret = TRANSITION_CHANGED;
+ if (status->condition_global_position_valid && status->condition_home_position_valid) {
+ status->nav_state = NAVIGATION_STATE_AUTO_RTGS;
+ } else if (status->condition_local_position_valid) {
+ status->nav_state = NAVIGATION_STATE_LAND;
+ } else if (status->condition_local_altitude_valid) {
+ status->nav_state = NAVIGATION_STATE_DESCEND;
+ } else {
+ status->nav_state = NAVIGATION_STATE_TERMINATION;
}
- break;
+ /* go into failsafe if RC is lost and datalink loss is not set up */
+ } else if (status->rc_signal_lost && !data_link_loss_enabled) {
+ status->failsafe = true;
- case FAILSAFE_STATE_TERMINATION:
- /* always allowed */
- ret = TRANSITION_CHANGED;
- break;
+ if (status->condition_global_position_valid && status->condition_home_position_valid) {
+ status->nav_state = NAVIGATION_STATE_AUTO_RTGS;
+ } else if (status->condition_local_position_valid) {
+ status->nav_state = NAVIGATION_STATE_LAND;
+ } else if (status->condition_local_altitude_valid) {
+ status->nav_state = NAVIGATION_STATE_DESCEND;
+ } else {
+ status->nav_state = NAVIGATION_STATE_TERMINATION;
+ }
- default:
- break;
+ /* don't bother if RC is lost if datalink is connected */
+ } else if (status->rc_signal_lost) {
+
+ /* this mode is ok, we don't need RC for loitering */
+ status->nav_state = NAVIGATION_STATE_AUTO_LOITER;
+ } else {
+ /* everything is perfect */
+ status->nav_state = NAVIGATION_STATE_AUTO_LOITER;
+ }
+ break;
+
+ case MAIN_STATE_AUTO_RTL:
+ /* require global position and home, also go into failsafe on an engine failure */
+
+ if (status->engine_failure) {
+ status->nav_state = NAVIGATION_STATE_AUTO_LANDENGFAIL;
+ } else if ((!status->condition_global_position_valid ||
+ !status->condition_home_position_valid)) {
+ status->failsafe = true;
+
+ if (status->condition_local_position_valid) {
+ status->nav_state = NAVIGATION_STATE_LAND;
+ } else if (status->condition_local_altitude_valid) {
+ status->nav_state = NAVIGATION_STATE_DESCEND;
+ } else {
+ status->nav_state = NAVIGATION_STATE_TERMINATION;
+ }
+ } else {
+ status->nav_state = NAVIGATION_STATE_AUTO_RTL;
}
+ break;
+
+ case MAIN_STATE_OFFBOARD:
+ /* require offboard control, otherwise stay where you are */
+ if (status->offboard_control_signal_lost && !status->rc_signal_lost) {
+ status->failsafe = true;
- if (ret == TRANSITION_CHANGED) {
- if (status->failsafe_state != new_failsafe_state) {
- status->failsafe_state = new_failsafe_state;
- failsafe_state_changed = true;
+ status->nav_state = NAVIGATION_STATE_POSCTL;
+ } else if (status->offboard_control_signal_lost && status->rc_signal_lost) {
+ status->failsafe = true;
+ if (status->condition_local_position_valid) {
+ status->nav_state = NAVIGATION_STATE_LAND;
+ } else if (status->condition_local_altitude_valid) {
+ status->nav_state = NAVIGATION_STATE_DESCEND;
} else {
- ret = TRANSITION_NOT_CHANGED;
+ status->nav_state = NAVIGATION_STATE_TERMINATION;
}
+ } else {
+ status->nav_state = NAVIGATION_STATE_OFFBOARD;
}
+ default:
+ break;
}
- return ret;
+ return status->nav_state != nav_state_old;
}
+int prearm_check(const struct vehicle_status_s *status, const int mavlink_fd)
+{
+ int ret;
+ bool failed = false;
+
+ int fd = open(ACCEL_DEVICE_PATH, O_RDONLY);
+
+ if (fd < 0) {
+ mavlink_log_critical(mavlink_fd, "ARM FAIL: ACCEL SENSOR MISSING");
+ failed = true;
+ goto system_eval;
+ }
+
+ ret = ioctl(fd, ACCELIOCSELFTEST, 0);
+
+ if (ret != OK) {
+ mavlink_log_critical(mavlink_fd, "ARM FAIL: ACCEL CALIBRATION");
+ failed = true;
+ goto system_eval;
+ }
+
+ /* check measurement result range */
+ struct accel_report acc;
+ ret = read(fd, &acc, sizeof(acc));
+
+ if (ret == sizeof(acc)) {
+ /* evaluate values */
+ float accel_magnitude = sqrtf(acc.x * acc.x + acc.y * acc.y + acc.z * acc.z);
+ if (accel_magnitude < 4.0f || accel_magnitude > 15.0f /* m/s^2 */) {
+ mavlink_log_critical(mavlink_fd, "ARM FAIL: ACCEL RANGE, hold still");
+ /* this is frickin' fatal */
+ failed = true;
+ goto system_eval;
+ }
+ } else {
+ mavlink_log_critical(mavlink_fd, "ARM FAIL: ACCEL READ");
+ /* this is frickin' fatal */
+ failed = true;
+ goto system_eval;
+ }
+
+ /* Perform airspeed check only if circuit breaker is not
+ * engaged and it's not a rotary wing */
+ if (!status->circuit_breaker_engaged_airspd_check && !status->is_rotary_wing) {
+ /* accel done, close it */
+ close(fd);
+ fd = orb_subscribe(ORB_ID(airspeed));
+
+ struct airspeed_s airspeed;
-// /*
-// * Wrapper functions (to be used in the commander), all functions assume lock on current_status
-// */
-
-// /* These functions decide if an emergency exits and then switch to SYSTEM_STATE_MISSION_ABORT or SYSTEM_STATE_GROUND_ERROR
-// *
-// * START SUBSYSTEM/EMERGENCY FUNCTIONS
-// * */
-
-// void update_state_machine_subsystem_present(int status_pub, struct vehicle_status_s *current_status, subsystem_type_t *subsystem_type)
-// {
-// current_status->onboard_control_sensors_present |= 1 << *subsystem_type;
-// current_status->counter++;
-// current_status->timestamp = hrt_absolute_time();
-// orb_publish(ORB_ID(vehicle_status), status_pub, current_status);
-// }
-
-// void update_state_machine_subsystem_notpresent(int status_pub, struct vehicle_status_s *current_status, subsystem_type_t *subsystem_type)
-// {
-// current_status->onboard_control_sensors_present &= ~(1 << *subsystem_type);
-// current_status->counter++;
-// current_status->timestamp = hrt_absolute_time();
-// orb_publish(ORB_ID(vehicle_status), status_pub, current_status);
-
-// /* if a subsystem was removed something went completely wrong */
-
-// switch (*subsystem_type) {
-// case SUBSYSTEM_TYPE_GYRO:
-// //global_data_send_mavlink_statustext_message_out("Commander: gyro not present", MAV_SEVERITY_EMERGENCY);
-// state_machine_emergency_always_critical(status_pub, current_status);
-// break;
-
-// case SUBSYSTEM_TYPE_ACC:
-// //global_data_send_mavlink_statustext_message_out("Commander: accelerometer not present", MAV_SEVERITY_EMERGENCY);
-// state_machine_emergency_always_critical(status_pub, current_status);
-// break;
-
-// case SUBSYSTEM_TYPE_MAG:
-// //global_data_send_mavlink_statustext_message_out("Commander: magnetometer not present", MAV_SEVERITY_EMERGENCY);
-// state_machine_emergency_always_critical(status_pub, current_status);
-// break;
-
-// case SUBSYSTEM_TYPE_GPS:
-// {
-// uint8_t flight_env = global_data_parameter_storage->pm.param_values[PARAM_FLIGHT_ENV];
-
-// if (flight_env == PX4_FLIGHT_ENVIRONMENT_OUTDOOR) {
-// //global_data_send_mavlink_statustext_message_out("Commander: GPS not present", MAV_SEVERITY_EMERGENCY);
-// state_machine_emergency(status_pub, current_status);
-// }
-// }
-// break;
-
-// default:
-// break;
-// }
-
-// }
-
-// void update_state_machine_subsystem_enabled(int status_pub, struct vehicle_status_s *current_status, subsystem_type_t *subsystem_type)
-// {
-// current_status->onboard_control_sensors_enabled |= 1 << *subsystem_type;
-// current_status->counter++;
-// current_status->timestamp = hrt_absolute_time();
-// orb_publish(ORB_ID(vehicle_status), status_pub, current_status);
-// }
-
-// void update_state_machine_subsystem_disabled(int status_pub, struct vehicle_status_s *current_status, subsystem_type_t *subsystem_type)
-// {
-// current_status->onboard_control_sensors_enabled &= ~(1 << *subsystem_type);
-// current_status->counter++;
-// current_status->timestamp = hrt_absolute_time();
-// orb_publish(ORB_ID(vehicle_status), status_pub, current_status);
-
-// /* if a subsystem was disabled something went completely wrong */
-
-// switch (*subsystem_type) {
-// case SUBSYSTEM_TYPE_GYRO:
-// //global_data_send_mavlink_statustext_message_out("Commander: EMERGENCY - gyro disabled", MAV_SEVERITY_EMERGENCY);
-// state_machine_emergency_always_critical(status_pub, current_status);
-// break;
-
-// case SUBSYSTEM_TYPE_ACC:
-// //global_data_send_mavlink_statustext_message_out("Commander: EMERGENCY - accelerometer disabled", MAV_SEVERITY_EMERGENCY);
-// state_machine_emergency_always_critical(status_pub, current_status);
-// break;
-
-// case SUBSYSTEM_TYPE_MAG:
-// //global_data_send_mavlink_statustext_message_out("Commander: EMERGENCY - magnetometer disabled", MAV_SEVERITY_EMERGENCY);
-// state_machine_emergency_always_critical(status_pub, current_status);
-// break;
-
-// case SUBSYSTEM_TYPE_GPS:
-// {
-// uint8_t flight_env = (uint8_t)(global_data_parameter_storage->pm.param_values[PARAM_FLIGHT_ENV]);
-
-// if (flight_env == PX4_FLIGHT_ENVIRONMENT_OUTDOOR) {
-// //global_data_send_mavlink_statustext_message_out("Commander: EMERGENCY - GPS disabled", MAV_SEVERITY_EMERGENCY);
-// state_machine_emergency(status_pub, current_status);
-// }
-// }
-// break;
-
-// default:
-// break;
-// }
-
-// }
-
-
-///* END SUBSYSTEM/EMERGENCY FUNCTIONS*/
-//
-//int update_state_machine_mode_request(int status_pub, struct vehicle_status_s *current_status, const int mavlink_fd, uint8_t mode)
-//{
-// int ret = 1;
-//
-//// /* Switch on HIL if in standby and not already in HIL mode */
-//// if ((mode & VEHICLE_MODE_FLAG_HIL_ENABLED)
-//// && !current_status->flag_hil_enabled) {
-//// if ((current_status->state_machine == SYSTEM_STATE_STANDBY)) {
-//// /* Enable HIL on request */
-//// current_status->flag_hil_enabled = true;
-//// ret = OK;
-//// state_machine_publish(status_pub, current_status, mavlink_fd);
-//// publish_armed_status(current_status);
-//// printf("[cmd] Enabling HIL, locking down all actuators for safety.\n\t(Arming the system will not activate them while in HIL mode)\n");
-////
-//// } else if (current_status->state_machine != SYSTEM_STATE_STANDBY &&
-//// current_status->flag_fmu_armed) {
-////
-//// mavlink_log_critical(mavlink_fd, "REJECTING HIL, disarm first!")
-////
-//// } else {
-////
-//// mavlink_log_critical(mavlink_fd, "REJECTING HIL, not in standby.")
-//// }
-//// }
-//
-// /* switch manual / auto */
-// if (mode & VEHICLE_MODE_FLAG_AUTO_ENABLED) {
-// update_state_machine_mode_auto(status_pub, current_status, mavlink_fd);
-//
-// } else if (mode & VEHICLE_MODE_FLAG_STABILIZED_ENABLED) {
-// update_state_machine_mode_stabilized(status_pub, current_status, mavlink_fd);
-//
-// } else if (mode & VEHICLE_MODE_FLAG_GUIDED_ENABLED) {
-// update_state_machine_mode_guided(status_pub, current_status, mavlink_fd);
-//
-// } else if (mode & VEHICLE_MODE_FLAG_MANUAL_INPUT_ENABLED) {
-// update_state_machine_mode_manual(status_pub, current_status, mavlink_fd);
-// }
-//
-// /* vehicle is disarmed, mode requests arming */
-// if (!(current_status->flag_fmu_armed) && (mode & VEHICLE_MODE_FLAG_SAFETY_ARMED)) {
-// /* only arm in standby state */
-// // XXX REMOVE
-// if (current_status->state_machine == SYSTEM_STATE_STANDBY || current_status->state_machine == SYSTEM_STATE_PREFLIGHT) {
-// do_state_update(status_pub, current_status, mavlink_fd, (commander_state_machine_t)SYSTEM_STATE_GROUND_READY);
-// ret = OK;
-// printf("[cmd] arming due to command request\n");
-// }
-// }
-//
-// /* vehicle is armed, mode requests disarming */
-// if (current_status->flag_fmu_armed && !(mode & VEHICLE_MODE_FLAG_SAFETY_ARMED)) {
-// /* only disarm in ground ready */
-// if (current_status->state_machine == SYSTEM_STATE_GROUND_READY) {
-// do_state_update(status_pub, current_status, mavlink_fd, (commander_state_machine_t)SYSTEM_STATE_STANDBY);
-// ret = OK;
-// printf("[cmd] disarming due to command request\n");
-// }
-// }
-//
-// /* NEVER actually switch off HIL without reboot */
-// if (current_status->flag_hil_enabled && !(mode & VEHICLE_MODE_FLAG_HIL_ENABLED)) {
-// warnx("DENYING request to switch off HIL. Please power cycle (safety reasons)\n");
-// mavlink_log_critical(mavlink_fd, "Power-cycle to exit HIL");
-// ret = ERROR;
-// }
-//
-// return ret;
-//}
+ if ((ret = orb_copy(ORB_ID(airspeed), fd, &airspeed)) ||
+ (hrt_elapsed_time(&airspeed.timestamp) > (50 * 1000))) {
+ mavlink_log_critical(mavlink_fd, "ARM FAIL: AIRSPEED SENSOR MISSING");
+ failed = true;
+ goto system_eval;
+ }
+ if (fabsf(airspeed.indicated_airspeed_m_s > 6.0f)) {
+ mavlink_log_critical(mavlink_fd, "AIRSPEED WARNING: WIND OR CALIBRATION MISSING");
+ // XXX do not make this fatal yet
+ }
+ }
+
+system_eval:
+ close(fd);
+ return (failed);
+}
diff --git a/src/modules/commander/state_machine_helper.h b/src/modules/commander/state_machine_helper.h
index 0ddd4f05a..61d0f29d0 100644
--- a/src/modules/commander/state_machine_helper.h
+++ b/src/modules/commander/state_machine_helper.h
@@ -1,8 +1,6 @@
/****************************************************************************
*
- * Copyright (C) 2013 PX4 Development Team. All rights reserved.
- * Author: Thomas Gubler <thomasgubler@student.ethz.ch>
- * Julian Oes <joes@student.ethz.ch>
+ * Copyright (c) 2013, 2014 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -36,14 +34,14 @@
/**
* @file state_machine_helper.h
* State machine helper functions definitions
+ *
+ * @author Thomas Gubler <thomasgubler@student.ethz.ch>
+ * @author Julian Oes <julian@oes.ch>
*/
#ifndef STATE_MACHINE_HELPER_H_
#define STATE_MACHINE_HELPER_H_
-#define GPS_NOFIX_COUNTER_LIMIT 4 //need GPS_NOFIX_COUNTER_LIMIT gps packets with a bad fix to call an error (if outdoor)
-#define GPS_GOTFIX_COUNTER_REQUIRED 4 //need GPS_GOTFIX_COUNTER_REQUIRED gps packets with a good fix to obtain position lock
-
#include <uORB/uORB.h>
#include <uORB/topics/vehicle_status.h>
#include <uORB/topics/actuator_armed.h>
@@ -56,25 +54,17 @@ typedef enum {
} transition_result_t;
-transition_result_t arming_state_transition(struct vehicle_status_s *current_state, const struct safety_s *safety,
- arming_state_t new_arming_state, struct actuator_armed_s *armed, const int mavlink_fd = 0);
-
bool is_safe(const struct vehicle_status_s *current_state, const struct safety_s *safety, const struct actuator_armed_s *armed);
-bool check_arming_state_changed();
+transition_result_t arming_state_transition(struct vehicle_status_s *current_state, const struct safety_s *safety,
+ arming_state_t new_arming_state, struct actuator_armed_s *armed, bool fRunPreArmChecks, const int mavlink_fd);
transition_result_t main_state_transition(struct vehicle_status_s *current_state, main_state_t new_main_state);
-bool check_main_state_changed();
-
-transition_result_t failsafe_state_transition(struct vehicle_status_s *status, failsafe_state_t new_failsafe_state);
-
-bool check_navigation_state_changed();
-
-bool check_failsafe_state_changed();
+transition_result_t hil_state_transition(hil_state_t new_state, int status_pub, struct vehicle_status_s *current_state, const int mavlink_fd);
-void set_navigation_state_changed();
+bool set_nav_state(struct vehicle_status_s *status, const bool data_link_loss_enabled, const bool mission_finished, const bool stay_in_failsafe);
-int hil_state_transition(hil_state_t new_state, int status_pub, struct vehicle_status_s *current_state, const int mavlink_fd);
+int prearm_check(const struct vehicle_status_s *status, const int mavlink_fd);
#endif /* STATE_MACHINE_HELPER_H_ */
diff --git a/src/modules/controllib/block/Block.hpp b/src/modules/controllib/block/Block.hpp
index 42ca3a742..d2f9cdf07 100644
--- a/src/modules/controllib/block/Block.hpp
+++ b/src/modules/controllib/block/Block.hpp
@@ -93,6 +93,11 @@ protected:
List<uORB::SubscriptionNode *> _subscriptions;
List<uORB::PublicationNode *> _publications;
List<BlockParamBase *> _params;
+
+private:
+ /* this class has pointer data members and should not be copied (private constructor) */
+ Block(const control::Block&);
+ Block operator=(const control::Block&);
};
class __EXPORT SuperBlock :
diff --git a/src/modules/controllib/blocks.cpp b/src/modules/controllib/blocks.cpp
index c6c374300..f739446fa 100644
--- a/src/modules/controllib/blocks.cpp
+++ b/src/modules/controllib/blocks.cpp
@@ -121,6 +121,9 @@ int blockLimitSymTest()
float BlockLowPass::update(float input)
{
+ if (!isfinite(getState())) {
+ setState(input);
+ }
float b = 2 * float(M_PI) * getFCut() * getDt();
float a = b / (1 + b);
setState(a * input + (1 - a)*getState());
@@ -293,7 +296,18 @@ int blockIntegralTrapTest()
float BlockDerivative::update(float input)
{
- float output = _lowPass.update((input - getU()) / getDt());
+ float output;
+ if (_initialized) {
+ output = _lowPass.update((input - getU()) / getDt());
+ } else {
+ // if this is the first call to update
+ // we have no valid derivative
+ // and so we use the assumption the
+ // input value is not changing much,
+ // which is the best we can do here.
+ output = 0.0f;
+ _initialized = true;
+ }
setU(input);
return output;
}
diff --git a/src/modules/controllib/blocks.hpp b/src/modules/controllib/blocks.hpp
index 66e929038..bffc355a8 100644
--- a/src/modules/controllib/blocks.hpp
+++ b/src/modules/controllib/blocks.hpp
@@ -114,7 +114,7 @@ public:
// methods
BlockLowPass(SuperBlock *parent, const char *name) :
Block(parent, name),
- _state(0),
+ _state(0.0f/0.0f /* initialize to invalid val, force into is_finite() check on first call */),
_fCut(this, "") // only one parameter, no need to name
{};
virtual ~BlockLowPass() {};
@@ -238,9 +238,25 @@ public:
BlockDerivative(SuperBlock *parent, const char *name) :
SuperBlock(parent, name),
_u(0),
+ _initialized(false),
_lowPass(this, "LP")
{};
virtual ~BlockDerivative() {};
+
+ /**
+ * Update the state and get current derivative
+ *
+ * This call updates the state and gets the current
+ * derivative. As the derivative is only valid
+ * on the second call to update, it will return
+ * no change (0) on the first. To get a closer
+ * estimate of the derivative on the first call,
+ * call setU() one time step before using the
+ * return value of update().
+ *
+ * @param input the variable to calculate the derivative of
+ * @return the current derivative
+ */
float update(float input);
// accessors
void setU(float u) {_u = u;}
@@ -249,6 +265,7 @@ public:
protected:
// attributes
float _u; /**< previous input */
+ bool _initialized;
BlockLowPass _lowPass; /**< low pass filter */
};
diff --git a/src/modules/controllib/module.mk b/src/modules/controllib/module.mk
index f0139a4b7..2860d1efc 100644
--- a/src/modules/controllib/module.mk
+++ b/src/modules/controllib/module.mk
@@ -39,3 +39,5 @@ SRCS = test_params.c \
block/BlockParam.cpp \
uorb/blocks.cpp \
blocks.cpp
+
+MAXOPTIMIZATION = -Os
diff --git a/src/modules/dataman/dataman.c b/src/modules/dataman/dataman.c
index 1a65313e8..705e54be3 100644
--- a/src/modules/dataman/dataman.c
+++ b/src/modules/dataman/dataman.c
@@ -50,6 +50,7 @@
#include <string.h>
#include "dataman.h"
+#include <systemlib/param/param.h>
/**
* data manager app start / stop handling function
@@ -61,6 +62,8 @@ __EXPORT int dataman_main(int argc, char *argv[]);
__EXPORT ssize_t dm_read(dm_item_t item, unsigned char index, void *buffer, size_t buflen);
__EXPORT ssize_t dm_write(dm_item_t item, unsigned char index, dm_persitence_t persistence, const void *buffer, size_t buflen);
__EXPORT int dm_clear(dm_item_t item);
+__EXPORT void dm_lock(dm_item_t item);
+__EXPORT void dm_unlock(dm_item_t item);
__EXPORT int dm_restart(dm_reset_reason restart_type);
/** Types of function calls supported by the worker task */
@@ -113,12 +116,17 @@ static const unsigned g_per_item_max_index[DM_KEY_NUM_KEYS] = {
DM_KEY_FENCE_POINTS_MAX,
DM_KEY_WAYPOINTS_OFFBOARD_0_MAX,
DM_KEY_WAYPOINTS_OFFBOARD_1_MAX,
- DM_KEY_WAYPOINTS_ONBOARD_MAX
+ DM_KEY_WAYPOINTS_ONBOARD_MAX,
+ DM_KEY_MISSION_STATE_MAX
};
/* Table of offset for index 0 of each item type */
static unsigned int g_key_offsets[DM_KEY_NUM_KEYS];
+/* Item type lock mutexes */
+static sem_t *g_item_locks[DM_KEY_NUM_KEYS];
+static sem_t g_sys_state_mutex;
+
/* The data manager store file handle and file name */
static int g_fd = -1, g_task_fd = -1;
static const char *k_data_manager_device_path = "/fs/microsd/dataman";
@@ -138,22 +146,22 @@ static work_q_t g_work_q; /* pending work items. To be consumed by worker thread
sem_t g_work_queued_sema; /* To notify worker thread a work item has been queued */
sem_t g_init_sema;
-static bool g_task_should_exit; /**< if true, dataman task should exit */
+static bool g_task_should_exit; /**< if true, dataman task should exit */
#define DM_SECTOR_HDR_SIZE 4 /* data manager per item header overhead */
static const unsigned k_sector_size = DM_MAX_DATA_SIZE + DM_SECTOR_HDR_SIZE; /* total item sorage space */
static void init_q(work_q_t *q)
{
- sq_init(&(q->q)); /* Initialize the NuttX queue structure */
+ sq_init(&(q->q)); /* Initialize the NuttX queue structure */
sem_init(&(q->mutex), 1, 1); /* Queue is initially unlocked */
- q->size = q->max_size = 0; /* Queue is initially empty */
+ q->size = q->max_size = 0; /* Queue is initially empty */
}
static inline void
destroy_q(work_q_t *q)
{
- sem_destroy(&(q->mutex)); /* Destroy the queue lock */
+ sem_destroy(&(q->mutex)); /* Destroy the queue lock */
}
static inline void
@@ -187,7 +195,7 @@ create_work_item(void)
if (item) {
item->first = 1;
lock_queue(&g_free_q);
- for (int i = 1; i < k_work_item_allocation_chunk_size; i++) {
+ for (size_t i = 1; i < k_work_item_allocation_chunk_size; i++) {
(item + i)->first = 0;
sq_addfirst(&(item + i)->link, &(g_free_q.q));
}
@@ -317,7 +325,9 @@ _write(dm_item_t item, unsigned char index, dm_persitence_t persistence, const v
buffer[1] = persistence;
buffer[2] = 0;
buffer[3] = 0;
- memcpy(buffer + DM_SECTOR_HDR_SIZE, buf, count);
+ if (count > 0) {
+ memcpy(buffer + DM_SECTOR_HDR_SIZE, buf, count);
+ }
count += DM_SECTOR_HDR_SIZE;
len = -1;
@@ -567,6 +577,32 @@ dm_clear(dm_item_t item)
return enqueue_work_item_and_wait_for_result(work);
}
+__EXPORT void
+dm_lock(dm_item_t item)
+{
+ /* Make sure data manager has been started and is not shutting down */
+ if ((g_fd < 0) || g_task_should_exit)
+ return;
+ if (item >= DM_KEY_NUM_KEYS)
+ return;
+ if (g_item_locks[item]) {
+ sem_wait(g_item_locks[item]);
+ }
+}
+
+__EXPORT void
+dm_unlock(dm_item_t item)
+{
+ /* Make sure data manager has been started and is not shutting down */
+ if ((g_fd < 0) || g_task_should_exit)
+ return;
+ if (item >= DM_KEY_NUM_KEYS)
+ return;
+ if (g_item_locks[item]) {
+ sem_post(g_item_locks[item]);
+ }
+}
+
/* Tell the data manager about the type of the last reset */
__EXPORT int
dm_restart(dm_reset_reason reason)
@@ -593,9 +629,6 @@ task_main(int argc, char *argv[])
{
work_q_item_t *work;
- /* inform about start */
- warnx("Initializing..");
-
/* Initialize global variables */
g_key_offsets[0] = 0;
@@ -607,6 +640,12 @@ task_main(int argc, char *argv[])
for (unsigned i = 0; i < dm_number_of_funcs; i++)
g_func_counts[i] = 0;
+ /* Initialize the item type locks, for now only DM_KEY_MISSION_STATE supports locking */
+ sem_init(&g_sys_state_mutex, 1, 1); /* Initially unlocked */
+ for (unsigned i = 0; i < DM_KEY_NUM_KEYS; i++)
+ g_item_locks[i] = NULL;
+ g_item_locks[DM_KEY_MISSION_STATE] = &g_sys_state_mutex;
+
g_task_should_exit = false;
init_q(&g_work_q);
@@ -652,16 +691,15 @@ task_main(int argc, char *argv[])
if (sys_restart_val == DM_INIT_REASON_POWER_ON) {
warnx("Power on restart");
_restart(DM_INIT_REASON_POWER_ON);
- }
- else if (sys_restart_val == DM_INIT_REASON_IN_FLIGHT) {
+ } else if (sys_restart_val == DM_INIT_REASON_IN_FLIGHT) {
warnx("In flight restart");
_restart(DM_INIT_REASON_IN_FLIGHT);
- }
- else
+ } else {
warnx("Unknown restart");
- }
- else
+ }
+ } else {
warnx("Unknown restart");
+ }
/* We use two file descriptors, one for the caller context and one for the worker thread */
/* They are actually the same but we need to some way to reject caller request while the */
@@ -742,6 +780,7 @@ task_main(int argc, char *argv[])
destroy_q(&g_work_q);
destroy_q(&g_free_q);
sem_destroy(&g_work_queued_sema);
+ sem_destroy(&g_sys_state_mutex);
return 0;
}
@@ -754,7 +793,7 @@ start(void)
sem_init(&g_init_sema, 1, 0);
/* start the worker thread */
- if ((task = task_spawn_cmd("dataman", SCHED_DEFAULT, SCHED_PRIORITY_MAX - 5, 2000, task_main, NULL)) <= 0) {
+ if ((task = task_spawn_cmd("dataman", SCHED_DEFAULT, SCHED_PRIORITY_DEFAULT, 2000, task_main, NULL)) <= 0) {
warn("task start failed");
return -1;
}
diff --git a/src/modules/dataman/dataman.h b/src/modules/dataman/dataman.h
index 1dfb26f73..d625bf985 100644
--- a/src/modules/dataman/dataman.h
+++ b/src/modules/dataman/dataman.h
@@ -53,16 +53,20 @@ extern "C" {
DM_KEY_WAYPOINTS_OFFBOARD_0, /* Mission way point coordinates sent over mavlink */
DM_KEY_WAYPOINTS_OFFBOARD_1, /* (alernate between 0 and 1) */
DM_KEY_WAYPOINTS_ONBOARD, /* Mission way point coordinates generated onboard */
+ DM_KEY_MISSION_STATE, /* Persistent mission state */
DM_KEY_NUM_KEYS /* Total number of item types defined */
} dm_item_t;
+ #define DM_KEY_WAYPOINTS_OFFBOARD(_id) (_id == 0 ? DM_KEY_WAYPOINTS_OFFBOARD_0 : DM_KEY_WAYPOINTS_OFFBOARD_1)
+
/** The maximum number of instances for each item type */
enum {
DM_KEY_SAFE_POINTS_MAX = 8,
DM_KEY_FENCE_POINTS_MAX = GEOFENCE_MAX_VERTICES,
DM_KEY_WAYPOINTS_OFFBOARD_0_MAX = NUM_MISSIONS_SUPPORTED,
DM_KEY_WAYPOINTS_OFFBOARD_1_MAX = NUM_MISSIONS_SUPPORTED,
- DM_KEY_WAYPOINTS_ONBOARD_MAX = NUM_MISSIONS_SUPPORTED
+ DM_KEY_WAYPOINTS_ONBOARD_MAX = NUM_MISSIONS_SUPPORTED,
+ DM_KEY_MISSION_STATE_MAX = 1
};
/** Data persistence levels */
@@ -101,6 +105,18 @@ extern "C" {
size_t buflen /* Length in bytes of data to retrieve */
);
+ /** Lock all items of this type */
+ __EXPORT void
+ dm_lock(
+ dm_item_t item /* The item type to clear */
+ );
+
+ /** Unlock all items of this type */
+ __EXPORT void
+ dm_unlock(
+ dm_item_t item /* The item type to clear */
+ );
+
/** Erase all items of this type */
__EXPORT int
dm_clear(
diff --git a/src/modules/dataman/module.mk b/src/modules/dataman/module.mk
index 234607b3d..7ebe09fb7 100644
--- a/src/modules/dataman/module.mk
+++ b/src/modules/dataman/module.mk
@@ -40,3 +40,5 @@ MODULE_COMMAND = dataman
SRCS = dataman.c
MODULE_STACKSIZE = 1200
+
+MAXOPTIMIZATION = -Os
diff --git a/src/modules/ekf_att_pos_estimator/ekf_att_pos_estimator_main.cpp b/src/modules/ekf_att_pos_estimator/ekf_att_pos_estimator_main.cpp
index 0a6d3fa8f..e7805daa9 100644
--- a/src/modules/ekf_att_pos_estimator/ekf_att_pos_estimator_main.cpp
+++ b/src/modules/ekf_att_pos_estimator/ekf_att_pos_estimator_main.cpp
@@ -49,15 +49,16 @@
#include <math.h>
#include <poll.h>
#include <time.h>
-#include <drivers/drv_hrt.h>
+#include <float.h>
#define SENSOR_COMBINED_SUB
-
+#include <drivers/drv_hrt.h>
#include <drivers/drv_gyro.h>
#include <drivers/drv_accel.h>
#include <drivers/drv_mag.h>
#include <drivers/drv_baro.h>
+#include <drivers/drv_range_finder.h>
#ifdef SENSOR_COMBINED_SUB
#include <uORB/topics/sensor_combined.h>
#endif
@@ -74,6 +75,7 @@
#include <uORB/topics/estimator_status.h>
#include <uORB/topics/actuator_armed.h>
#include <uORB/topics/home_position.h>
+#include <uORB/topics/wind_estimate.h>
#include <systemlib/param/param.h>
#include <systemlib/err.h>
#include <geo/geo.h>
@@ -82,7 +84,7 @@
#include <mathlib/mathlib.h>
#include <mavlink/mavlink_log.h>
-#include "estimator.h"
+#include "estimator_23states.h"
@@ -95,8 +97,10 @@ extern "C" __EXPORT int ekf_att_pos_estimator_main(int argc, char *argv[]);
__EXPORT uint32_t millis();
-static uint64_t last_run = 0;
+__EXPORT uint64_t getMicros();
+
static uint64_t IMUmsec = 0;
+static uint64_t IMUusec = 0;
static const uint64_t FILTER_INIT_DELAY = 1 * 1000 * 1000;
uint32_t millis()
@@ -104,6 +108,11 @@ uint32_t millis()
return IMUmsec;
}
+uint64_t getMicros()
+{
+ return IMUusec;
+}
+
class FixedwingEstimator
{
public:
@@ -112,6 +121,10 @@ public:
*/
FixedwingEstimator();
+ /* we do not want people ever copying this class */
+ FixedwingEstimator(const FixedwingEstimator& that) = delete;
+ FixedwingEstimator operator=(const FixedwingEstimator&) = delete;
+
/**
* Destructor, also kills the sensors task.
*/
@@ -120,11 +133,18 @@ public:
/**
* Start the sensors task.
*
- * @return OK on success.
+ * @return OK on success.
*/
int start();
/**
+ * Task status
+ *
+ * @return true if the mainloop is running
+ */
+ bool task_running() { return _task_running; }
+
+ /**
* Print the current status.
*/
void print_status();
@@ -134,10 +154,25 @@ public:
*/
int trip_nan();
+ /**
+ * Enable logging.
+ *
+ * @param enable Set to true to enable logging, false to disable
+ */
+ int enable_logging(bool enable);
+
+ /**
+ * Set debug level.
+ *
+ * @param debug Desired debug level - 0 to disable.
+ */
+ int set_debuglevel(unsigned debug) { _debug = debug; return 0; }
+
private:
bool _task_should_exit; /**< if true, sensor task should exit */
- int _estimator_task; /**< task handle for sensor task */
+ bool _task_running; /**< if true, task is running in its mainloop */
+ int _estimator_task; /**< task handle for sensor task */
#ifndef SENSOR_COMBINED_SUB
int _gyro_sub; /**< gyro sensor subscription */
int _accel_sub; /**< accel sensor subscription */
@@ -145,6 +180,7 @@ private:
#else
int _sensor_combined_sub;
#endif
+ int _distance_sub; /**< distance measurement */
int _airspeed_sub; /**< airspeed subscription */
int _baro_sub; /**< barometer subscription */
int _gps_sub; /**< GPS subscription */
@@ -158,6 +194,7 @@ private:
orb_advert_t _global_pos_pub; /**< global position */
orb_advert_t _local_pos_pub; /**< position in local frame */
orb_advert_t _estimator_status_pub; /**< status of the estimator */
+ orb_advert_t _wind_pub; /**< wind estimate */
struct vehicle_attitude_s _att; /**< vehicle attitude */
struct gyro_report _gyro;
@@ -169,6 +206,8 @@ private:
struct vehicle_global_position_s _global_pos; /**< global vehicle position */
struct vehicle_local_position_s _local_pos; /**< local vehicle position */
struct vehicle_gps_position_s _gps; /**< GPS position */
+ struct wind_estimate_s _wind; /**< wind estimate */
+ struct range_finder_report _distance; /**< distance estimate */
struct gyro_scale _gyro_offsets;
struct accel_scale _accel_offsets;
@@ -181,24 +220,29 @@ private:
struct map_projection_reference_s _pos_ref;
float _baro_ref; /**< barometer reference altitude */
- float _baro_gps_offset; /**< offset between GPS and baro */
+ float _baro_ref_offset; /**< offset between initial baro reference and GPS init baro altitude */
+ float _baro_gps_offset; /**< offset between baro altitude (at GPS init time) and GPS altitude */
- perf_counter_t _loop_perf; /**< loop performance counter */
+ perf_counter_t _loop_perf; ///< loop performance counter
perf_counter_t _perf_gyro; ///<local performance counter for gyro updates
- perf_counter_t _perf_accel; ///<local performance counter for accel updates
perf_counter_t _perf_mag; ///<local performance counter for mag updates
perf_counter_t _perf_gps; ///<local performance counter for gps updates
perf_counter_t _perf_baro; ///<local performance counter for baro updates
perf_counter_t _perf_airspeed; ///<local performance counter for airspeed updates
+ perf_counter_t _perf_reset; ///<local performance counter for filter resets
- bool _initialized;
bool _baro_init;
bool _gps_initialized;
- uint64_t _gps_start_time;
- uint64_t _filter_start_time;
+ hrt_abstime _gps_start_time;
+ hrt_abstime _filter_start_time;
+ hrt_abstime _last_sensor_timestamp;
+ hrt_abstime _last_run;
+ hrt_abstime _distance_last_valid;
bool _gyro_valid;
bool _accel_valid;
bool _mag_valid;
+ bool _ekf_logging; ///< log EKF state
+ unsigned _debug; ///< debug level - default 0
int _mavlink_fd;
@@ -246,6 +290,10 @@ private:
AttPosEKF *_ekf;
+ float _velocity_xy_filtered;
+ float _velocity_z_filtered;
+ float _airspeed_filtered;
+
/**
* Update our local parameter cache.
*/
@@ -268,9 +316,16 @@ private:
static void task_main_trampoline(int argc, char *argv[]);
/**
- * Main sensor collection task.
+ * Main filter task.
*/
void task_main();
+
+ /**
+ * Check filter sanity state
+ *
+ * @return zero if ok, non-zero for a filter error condition.
+ */
+ int check_filter_state();
};
namespace estimator
@@ -282,12 +337,13 @@ namespace estimator
#endif
static const int ERROR = -1;
-FixedwingEstimator *g_estimator;
+FixedwingEstimator *g_estimator = nullptr;
}
FixedwingEstimator::FixedwingEstimator() :
_task_should_exit(false),
+ _task_running(false),
_estimator_task(-1),
/* subscriptions */
@@ -298,6 +354,7 @@ FixedwingEstimator::FixedwingEstimator() :
#else
_sensor_combined_sub(-1),
#endif
+ _distance_sub(-1),
_airspeed_sub(-1),
_baro_sub(-1),
_gps_sub(-1),
@@ -312,6 +369,7 @@ FixedwingEstimator::FixedwingEstimator() :
_global_pos_pub(-1),
_local_pos_pub(-1),
_estimator_status_pub(-1),
+ _wind_pub(-1),
_att({}),
_gyro({}),
@@ -323,39 +381,54 @@ FixedwingEstimator::FixedwingEstimator() :
_global_pos({}),
_local_pos({}),
_gps({}),
+ _wind({}),
+ _distance{},
_gyro_offsets({}),
_accel_offsets({}),
_mag_offsets({}),
#ifdef SENSOR_COMBINED_SUB
- _sensor_combined({}),
+ _sensor_combined{},
#endif
+ _pos_ref{},
_baro_ref(0.0f),
+ _baro_ref_offset(0.0f),
_baro_gps_offset(0.0f),
/* performance counters */
- _loop_perf(perf_alloc(PC_COUNT, "ekf_att_pos_estimator")),
- _perf_gyro(perf_alloc(PC_COUNT, "ekf_att_pos_gyro_upd")),
- _perf_accel(perf_alloc(PC_COUNT, "ekf_att_pos_accel_upd")),
- _perf_mag(perf_alloc(PC_COUNT, "ekf_att_pos_mag_upd")),
- _perf_gps(perf_alloc(PC_COUNT, "ekf_att_pos_gps_upd")),
- _perf_baro(perf_alloc(PC_COUNT, "ekf_att_pos_baro_upd")),
- _perf_airspeed(perf_alloc(PC_COUNT, "ekf_att_pos_aspd_upd")),
+ _loop_perf(perf_alloc(PC_ELAPSED, "ekf_att_pos_estimator")),
+ _perf_gyro(perf_alloc(PC_INTERVAL, "ekf_att_pos_gyro_upd")),
+ _perf_mag(perf_alloc(PC_INTERVAL, "ekf_att_pos_mag_upd")),
+ _perf_gps(perf_alloc(PC_INTERVAL, "ekf_att_pos_gps_upd")),
+ _perf_baro(perf_alloc(PC_INTERVAL, "ekf_att_pos_baro_upd")),
+ _perf_airspeed(perf_alloc(PC_INTERVAL, "ekf_att_pos_aspd_upd")),
+ _perf_reset(perf_alloc(PC_COUNT, "ekf_att_pos_reset")),
/* states */
- _initialized(false),
_baro_init(false),
_gps_initialized(false),
+ _gps_start_time(0),
+ _filter_start_time(0),
+ _last_sensor_timestamp(0),
+ _last_run(0),
+ _distance_last_valid(0),
_gyro_valid(false),
_accel_valid(false),
_mag_valid(false),
+ _ekf_logging(true),
+ _debug(0),
_mavlink_fd(-1),
- _ekf(nullptr)
+ _parameters{},
+ _parameter_handles{},
+ _ekf(nullptr),
+ _velocity_xy_filtered(0.0f),
+ _velocity_z_filtered(0.0f),
+ _airspeed_filtered(0.0f)
{
- last_run = hrt_absolute_time();
+ _last_run = hrt_absolute_time();
_parameter_handles.vel_delay_ms = param_find("PE_VEL_DELAY_MS");
_parameter_handles.pos_delay_ms = param_find("PE_POS_DELAY_MS");
@@ -439,10 +512,20 @@ FixedwingEstimator::~FixedwingEstimator()
} while (_estimator_task != -1);
}
+ delete _ekf;
+
estimator::g_estimator = nullptr;
}
int
+FixedwingEstimator::enable_logging(bool logging)
+{
+ _ekf_logging = logging;
+
+ return 0;
+}
+
+int
FixedwingEstimator::parameters_update()
{
@@ -481,6 +564,7 @@ FixedwingEstimator::parameters_update()
_ekf->gyroProcessNoise = _parameters.gyro_pnoise;
_ekf->accelProcessNoise = _parameters.acc_pnoise;
_ekf->airspeedMeasurementSigma = _parameters.eas_noise;
+ _ekf->rngFinderPitch = 0.0f; // XXX base on SENS_BOARD_Y_OFF
}
return OK;
@@ -500,6 +584,123 @@ FixedwingEstimator::vehicle_status_poll()
}
}
+int
+FixedwingEstimator::check_filter_state()
+{
+ /*
+ * CHECK IF THE INPUT DATA IS SANE
+ */
+
+ struct ekf_status_report ekf_report;
+
+ int check = _ekf->CheckAndBound(&ekf_report);
+
+ const char* const feedback[] = { 0,
+ "NaN in states, resetting",
+ "stale sensor data, resetting",
+ "got initial position lock",
+ "excessive gyro offsets",
+ "velocity diverted, check accel config",
+ "excessive covariances",
+ "unknown condition, resetting"};
+
+ // Print out error condition
+ if (check) {
+ unsigned warn_index = static_cast<unsigned>(check);
+ unsigned max_warn_index = (sizeof(feedback) / sizeof(feedback[0]));
+
+ if (max_warn_index < warn_index) {
+ warn_index = max_warn_index;
+ }
+
+ // Do not warn about accel offset if we have no position updates
+ if (!(warn_index == 5 && _ekf->staticMode)) {
+ warnx("reset: %s", feedback[warn_index]);
+ mavlink_log_critical(_mavlink_fd, "[ekf check] %s", feedback[warn_index]);
+ }
+ }
+
+ struct estimator_status_report rep;
+ memset(&rep, 0, sizeof(rep));
+
+ // If error flag is set, we got a filter reset
+ if (check && ekf_report.error) {
+
+ // Count the reset condition
+ perf_count(_perf_reset);
+
+ } else if (_ekf_logging) {
+ _ekf->GetFilterState(&ekf_report);
+ }
+
+ if (_ekf_logging || check) {
+ rep.timestamp = hrt_absolute_time();
+
+ rep.nan_flags |= (((uint8_t)ekf_report.angNaN) << 0);
+ rep.nan_flags |= (((uint8_t)ekf_report.summedDelVelNaN) << 1);
+ rep.nan_flags |= (((uint8_t)ekf_report.KHNaN) << 2);
+ rep.nan_flags |= (((uint8_t)ekf_report.KHPNaN) << 3);
+ rep.nan_flags |= (((uint8_t)ekf_report.PNaN) << 4);
+ rep.nan_flags |= (((uint8_t)ekf_report.covarianceNaN) << 5);
+ rep.nan_flags |= (((uint8_t)ekf_report.kalmanGainsNaN) << 6);
+ rep.nan_flags |= (((uint8_t)ekf_report.statesNaN) << 7);
+
+ rep.health_flags |= (((uint8_t)ekf_report.velHealth) << 0);
+ rep.health_flags |= (((uint8_t)ekf_report.posHealth) << 1);
+ rep.health_flags |= (((uint8_t)ekf_report.hgtHealth) << 2);
+ rep.health_flags |= (((uint8_t)!ekf_report.gyroOffsetsExcessive) << 3);
+ rep.health_flags |= (((uint8_t)ekf_report.onGround) << 4);
+ rep.health_flags |= (((uint8_t)ekf_report.staticMode) << 5);
+ rep.health_flags |= (((uint8_t)ekf_report.useCompass) << 6);
+ rep.health_flags |= (((uint8_t)ekf_report.useAirspeed) << 7);
+
+ rep.timeout_flags |= (((uint8_t)ekf_report.velTimeout) << 0);
+ rep.timeout_flags |= (((uint8_t)ekf_report.posTimeout) << 1);
+ rep.timeout_flags |= (((uint8_t)ekf_report.hgtTimeout) << 2);
+ rep.timeout_flags |= (((uint8_t)ekf_report.imuTimeout) << 3);
+
+ if (_debug > 10) {
+
+ if (rep.health_flags < ((1 << 0) | (1 << 1) | (1 << 2) | (1 << 3))) {
+ warnx("health: VEL:%s POS:%s HGT:%s OFFS:%s",
+ ((rep.health_flags & (1 << 0)) ? "OK" : "ERR"),
+ ((rep.health_flags & (1 << 1)) ? "OK" : "ERR"),
+ ((rep.health_flags & (1 << 2)) ? "OK" : "ERR"),
+ ((rep.health_flags & (1 << 3)) ? "OK" : "ERR"));
+ }
+
+ if (rep.timeout_flags) {
+ warnx("timeout: %s%s%s%s",
+ ((rep.timeout_flags & (1 << 0)) ? "VEL " : ""),
+ ((rep.timeout_flags & (1 << 1)) ? "POS " : ""),
+ ((rep.timeout_flags & (1 << 2)) ? "HGT " : ""),
+ ((rep.timeout_flags & (1 << 3)) ? "IMU " : ""));
+ }
+ }
+
+ // Copy all states or at least all that we can fit
+ unsigned ekf_n_states = ekf_report.n_states;
+ unsigned max_states = (sizeof(rep.states) / sizeof(rep.states[0]));
+ rep.n_states = (ekf_n_states < max_states) ? ekf_n_states : max_states;
+
+ for (unsigned i = 0; i < rep.n_states; i++) {
+ rep.states[i] = ekf_report.states[i];
+ }
+
+ for (unsigned i = 0; i < rep.n_states; i++) {
+ rep.states[i] = ekf_report.states[i];
+ }
+
+ if (_estimator_status_pub > 0) {
+ orb_publish(ORB_ID(estimator_status), _estimator_status_pub, &rep);
+ } else {
+ _estimator_status_pub = orb_advertise(ORB_ID(estimator_status), &rep);
+ }
+ }
+
+ return check;
+}
+
void
FixedwingEstimator::task_main_trampoline(int argc, char *argv[])
{
@@ -516,13 +717,14 @@ FixedwingEstimator::task_main()
_filter_start_time = hrt_absolute_time();
if (!_ekf) {
- errx(1, "failed allocating EKF filter - out of RAM!");
+ errx(1, "OUT OF MEM!");
}
/*
* do subscriptions
*/
- _baro_sub = orb_subscribe(ORB_ID(sensor_baro));
+ _distance_sub = orb_subscribe(ORB_ID(sensor_range_finder));
+ _baro_sub = orb_subscribe(ORB_ID(sensor_baro0));
_airspeed_sub = orb_subscribe(ORB_ID(airspeed));
_gps_sub = orb_subscribe(ORB_ID(vehicle_gps_position));
_vstatus_sub = orb_subscribe(ORB_ID(vehicle_status));
@@ -544,14 +746,14 @@ FixedwingEstimator::task_main()
#else
_sensor_combined_sub = orb_subscribe(ORB_ID(sensor_combined));
/* XXX remove this!, BUT increase the data buffer size! */
- orb_set_interval(_sensor_combined_sub, 4);
+ orb_set_interval(_sensor_combined_sub, 9);
#endif
/* sets also parameters in the EKF object */
parameters_update();
- Vector3f lastAngRate = {0.0f, 0.0f, 0.0f};
- Vector3f lastAccel = {0.0f, 0.0f, 0.0f};
+ Vector3f lastAngRate;
+ Vector3f lastAccel;
/* wakeup source(s) */
struct pollfd fds[2];
@@ -571,10 +773,20 @@ FixedwingEstimator::task_main()
bool newHgtData = false;
bool newAdsData = false;
bool newDataMag = false;
+ bool newRangeData = false;
+
+ float posNED[3] = {0.0f, 0.0f, 0.0f}; // North, East Down position (m)
+
+ uint64_t last_gps = 0;
+ _gps.vel_n_m_s = 0.0f;
+ _gps.vel_e_m_s = 0.0f;
+ _gps.vel_d_m_s = 0.0f;
+
+ _task_running = true;
while (!_task_should_exit) {
- /* wait for up to 500ms for data */
+ /* wait for up to 100ms for data */
int pret = poll(&fds[0], (sizeof(fds) / sizeof(fds[0])), 100);
/* timed out - periodic check for _task_should_exit, etc. */
@@ -583,7 +795,7 @@ FixedwingEstimator::task_main()
/* this is undesirable but not much we can do - might want to flag unhappy status */
if (pret < 0) {
- warn("poll error %d, %d", pret, errno);
+ warn("POLL ERR %d, %d", pret, errno);
continue;
}
@@ -609,8 +821,6 @@ FixedwingEstimator::task_main()
bool accel_updated;
bool mag_updated;
- hrt_abstime last_sensor_timestamp;
-
perf_count(_perf_gyro);
/* Reset baro reference if switching to HIL, reset sensor states */
@@ -634,12 +844,12 @@ FixedwingEstimator::task_main()
_baro_init = false;
_gps_initialized = false;
- last_sensor_timestamp = hrt_absolute_time();
- last_run = last_sensor_timestamp;
+ _last_sensor_timestamp = hrt_absolute_time();
+ _last_run = _last_sensor_timestamp;
_ekf->ZeroVariables();
_ekf->dtIMU = 0.01f;
- _filter_start_time = last_sensor_timestamp;
+ _filter_start_time = _last_sensor_timestamp;
/* now skip this loop and get data on the next one, which will also re-init the filter */
continue;
@@ -657,15 +867,15 @@ FixedwingEstimator::task_main()
orb_check(_accel_sub, &accel_updated);
if (accel_updated) {
- perf_count(_perf_accel);
orb_copy(ORB_ID(sensor_accel), _accel_sub, &_accel);
}
- last_sensor_timestamp = _gyro.timestamp;
- IMUmsec = _gyro.timestamp / 1e3f;
+ _last_sensor_timestamp = _gyro.timestamp;
+ IMUmsec = _gyro.timestamp / 1e3;
+ IMUusec = _gyro.timestamp;
- float deltaT = (_gyro.timestamp - last_run) / 1e6f;
- last_run = _gyro.timestamp;
+ float deltaT = (_gyro.timestamp - _last_run) / 1e6f;
+ _last_run = _gyro.timestamp;
/* guard against too large deltaT's */
if (!isfinite(deltaT) || deltaT > 1.0f || deltaT < 0.000001f) {
@@ -725,17 +935,18 @@ FixedwingEstimator::task_main()
// Copy gyro and accel
- last_sensor_timestamp = _sensor_combined.timestamp;
- IMUmsec = _sensor_combined.timestamp / 1e3f;
+ _last_sensor_timestamp = _sensor_combined.timestamp;
+ IMUmsec = _sensor_combined.timestamp / 1e3;
+ IMUusec = _sensor_combined.timestamp;
- float deltaT = (_sensor_combined.timestamp - last_run) / 1e6f;
+ float deltaT = (_sensor_combined.timestamp - _last_run) / 1e6f;
/* guard against too large deltaT's */
if (!isfinite(deltaT) || deltaT > 1.0f || deltaT < 0.000001f) {
deltaT = 0.01f;
}
- last_run = _sensor_combined.timestamp;
+ _last_run = _sensor_combined.timestamp;
// Always store data, independent of init status
/* fill in last data set */
@@ -753,6 +964,7 @@ FixedwingEstimator::task_main()
}
_gyro_valid = true;
+ perf_count(_perf_gyro);
}
if (accel_updated) {
@@ -784,6 +996,8 @@ FixedwingEstimator::task_main()
#endif
+ //warnx("dang: %8.4f %8.4f dvel: %8.4f %8.4f", _ekf->dAngIMU.x, _ekf->dAngIMU.z, _ekf->dVelIMU.x, _ekf->dVelIMU.z);
+
bool airspeed_updated;
orb_check(_airspeed_sub, &airspeed_updated);
@@ -803,8 +1017,6 @@ FixedwingEstimator::task_main()
if (gps_updated) {
- uint64_t last_gps = _gps.timestamp_position;
-
orb_copy(ORB_ID(vehicle_gps_position), _gps_sub, &_gps);
perf_count(_perf_gps);
@@ -817,11 +1029,17 @@ FixedwingEstimator::task_main()
_gps_start_time = hrt_absolute_time();
/* check if we had a GPS outage for a long time */
- if (hrt_elapsed_time(&last_gps) > 5 * 1000 * 1000) {
+ float gps_elapsed = hrt_elapsed_time(&last_gps) / 1e6f;
+
+ const float pos_reset_threshold = 5.0f; // seconds
+
+ /* timeout of 5 seconds */
+ if (gps_elapsed > pos_reset_threshold) {
_ekf->ResetPosition();
_ekf->ResetVelocity();
_ekf->ResetStoredStates();
}
+ _ekf->updateDtGpsFilt(math::constrain((_gps.timestamp_position - last_gps) / 1e6f, 0.01f, pos_reset_threshold));
/* fuse GPS updates */
@@ -853,6 +1071,8 @@ FixedwingEstimator::task_main()
newDataGps = true;
+ last_gps = _gps.timestamp_position;
+
}
}
@@ -861,7 +1081,14 @@ FixedwingEstimator::task_main()
orb_check(_baro_sub, &baro_updated);
if (baro_updated) {
- orb_copy(ORB_ID(sensor_baro), _baro_sub, &_baro);
+
+ hrt_abstime baro_last = _baro.timestamp;
+
+ orb_copy(ORB_ID(sensor_baro0), _baro_sub, &_baro);
+
+ float baro_elapsed = (_baro.timestamp - baro_last) / 1e6f;
+
+ _ekf->updateDtHgtFilt(math::constrain(baro_elapsed, 0.001f, 0.1f));
_ekf->baroHgt = _baro.altitude;
@@ -871,6 +1098,8 @@ FixedwingEstimator::task_main()
warnx("ALT REF INIT");
}
+ perf_count(_perf_baro);
+
newHgtData = true;
} else {
newHgtData = false;
@@ -921,127 +1150,52 @@ FixedwingEstimator::task_main()
newDataMag = false;
}
+ orb_check(_distance_sub, &newRangeData);
- /**
- * CHECK IF THE INPUT DATA IS SANE
- */
- int check = _ekf->CheckAndBound();
-
- const char* ekfname = "[ekf] ";
-
- switch (check) {
- case 0:
- /* all ok */
- break;
- case 1:
- {
- const char* str = "NaN in states, resetting";
- warnx("%s", str);
- mavlink_log_critical(_mavlink_fd, "%s%s", ekfname, str);
- break;
- }
- case 2:
- {
- const char* str = "stale IMU data, resetting";
- warnx("%s", str);
- mavlink_log_critical(_mavlink_fd, "%s%s", ekfname, str);
- break;
- }
- case 3:
- {
- const char* str = "switching to dynamic state";
- warnx("%s", str);
- mavlink_log_info(_mavlink_fd, "%s%s", ekfname, str);
- break;
- }
-
- default:
- {
- const char* str = "unknown reset condition";
- warnx("%s", str);
- mavlink_log_critical(_mavlink_fd, "%s%s", ekfname, str);
- }
- }
-
- // warn on fatal resets
- if (check == 1) {
- warnx("NUMERIC ERROR IN FILTER");
- }
-
- // If non-zero, we got a filter reset
- if (check) {
-
- struct ekf_status_report ekf_report;
-
- _ekf->GetLastErrorState(&ekf_report);
-
- struct estimator_status_report rep;
- memset(&rep, 0, sizeof(rep));
- rep.timestamp = hrt_absolute_time();
-
- rep.states_nan = ekf_report.statesNaN;
- rep.covariance_nan = ekf_report.covarianceNaN;
- rep.kalman_gain_nan = ekf_report.kalmanGainsNaN;
-
- // Copy all states or at least all that we can fit
- unsigned i = 0;
- unsigned ekf_n_states = (sizeof(ekf_report.states) / sizeof(ekf_report.states[0]));
- unsigned max_states = (sizeof(rep.states) / sizeof(rep.states[0]));
- rep.n_states = (ekf_n_states < max_states) ? ekf_n_states : max_states;
-
- while ((i < ekf_n_states) && (i < max_states)) {
-
- rep.states[i] = ekf_report.states[i];
- i++;
- }
+ if (newRangeData) {
+ orb_copy(ORB_ID(sensor_range_finder), _distance_sub, &_distance);
- if (_estimator_status_pub > 0) {
- orb_publish(ORB_ID(estimator_status), _estimator_status_pub, &rep);
+ if (_distance.valid) {
+ _ekf->rngMea = _distance.distance;
+ _distance_last_valid = _distance.timestamp;
} else {
- _estimator_status_pub = orb_advertise(ORB_ID(estimator_status), &rep);
+ newRangeData = false;
}
+ }
- /* set sensors to de-initialized state */
- _gyro_valid = false;
- _accel_valid = false;
- _mag_valid = false;
-
- _baro_init = false;
- _gps_initialized = false;
- last_sensor_timestamp = hrt_absolute_time();
- last_run = last_sensor_timestamp;
-
- _ekf->ZeroVariables();
- _ekf->dtIMU = 0.01f;
-
- // Let the system re-initialize itself
+ /*
+ * CHECK IF ITS THE RIGHT TIME TO RUN THINGS ALREADY
+ */
+ if (hrt_elapsed_time(&_filter_start_time) < FILTER_INIT_DELAY) {
continue;
-
}
-
/**
* PART TWO: EXECUTE THE FILTER
+ *
+ * We run the filter only once all data has been fetched
**/
- if ((hrt_elapsed_time(&_filter_start_time) > FILTER_INIT_DELAY) && _baro_init && _gyro_valid && _accel_valid && _mag_valid) {
+ if (_baro_init && _gyro_valid && _accel_valid && _mag_valid) {
float initVelNED[3];
- if (!_gps_initialized && _gps.fix_type > 2 && _gps.eph_m < _parameters.pos_stddev_threshold && _gps.epv_m < _parameters.pos_stddev_threshold) {
-
- initVelNED[0] = _gps.vel_n_m_s;
- initVelNED[1] = _gps.vel_e_m_s;
- initVelNED[2] = _gps.vel_d_m_s;
+ /* Initialize the filter first */
+ if (!_gps_initialized && _gps.fix_type > 2 && _gps.eph < _parameters.pos_stddev_threshold && _gps.epv < _parameters.pos_stddev_threshold) {
// GPS is in scaled integers, convert
double lat = _gps.lat / 1.0e7;
double lon = _gps.lon / 1.0e7;
float gps_alt = _gps.alt / 1e3f;
+ initVelNED[0] = _gps.vel_n_m_s;
+ initVelNED[1] = _gps.vel_e_m_s;
+ initVelNED[2] = _gps.vel_d_m_s;
+
// Set up height correctly
- orb_copy(ORB_ID(sensor_baro), _baro_sub, &_baro);
- _baro_gps_offset = _baro_ref - _baro.altitude;
+ orb_copy(ORB_ID(sensor_baro0), _baro_sub, &_baro);
+ _baro_ref_offset = _ekf->states[9]; // this should become zero in the local frame
+ _baro_gps_offset = _baro.altitude - gps_alt;
_ekf->baroHgt = _baro.altitude;
_ekf->hgtMea = 1.0f * (_ekf->baroHgt - (_baro_ref));
@@ -1065,10 +1219,13 @@ FixedwingEstimator::task_main()
map_projection_init(&_pos_ref, lat, lon);
mavlink_log_info(_mavlink_fd, "[ekf] ref: LA %.4f,LO %.4f,ALT %.2f", lat, lon, (double)gps_alt);
+
+ #if 0
warnx("HOME/REF: LA %8.4f,LO %8.4f,ALT %8.2f V: %8.4f %8.4f %8.4f", lat, lon, (double)gps_alt,
(double)_ekf->velNED[0], (double)_ekf->velNED[1], (double)_ekf->velNED[2]);
- warnx("BARO: %8.4f m / ref: %8.4f m / gps offs: %8.4f m", (double)_ekf->baroHgt, (double)_baro_ref, (double)_baro_gps_offset);
- warnx("GPS: eph: %8.4f, epv: %8.4f, declination: %8.4f", (double)_gps.eph_m, (double)_gps.epv_m, (double)math::degrees(declination));
+ warnx("BARO: %8.4f m / ref: %8.4f m / gps offs: %8.4f m", (double)_ekf->baroHgt, (double)_baro_ref, (double)_baro_ref_offset);
+ warnx("GPS: eph: %8.4f, epv: %8.4f, declination: %8.4f", (double)_gps.eph, (double)_gps.epv, (double)math::degrees(declination));
+ #endif
_gps_initialized = true;
@@ -1077,264 +1234,308 @@ FixedwingEstimator::task_main()
initVelNED[0] = 0.0f;
initVelNED[1] = 0.0f;
initVelNED[2] = 0.0f;
- _ekf->posNED[0] = 0.0f;
- _ekf->posNED[1] = 0.0f;
- _ekf->posNED[2] = 0.0f;
-
- _ekf->posNE[0] = _ekf->posNED[0];
- _ekf->posNE[1] = _ekf->posNED[1];
+ _ekf->posNE[0] = posNED[0];
+ _ekf->posNE[1] = posNED[1];
_local_pos.ref_alt = _baro_ref;
+ _baro_ref_offset = 0.0f;
_baro_gps_offset = 0.0f;
_ekf->InitialiseFilter(initVelNED, 0.0, 0.0, 0.0f, 0.0f);
- }
- }
-
- // If valid IMU data and states initialised, predict states and covariances
- if (_ekf->statesInitialised) {
- // Run the strapdown INS equations every IMU update
- _ekf->UpdateStrapdownEquationsNED();
-#if 0
- // debug code - could be tunred into a filter mnitoring/watchdog function
- float tempQuat[4];
-
- for (uint8_t j = 0; j <= 3; j++) tempQuat[j] = states[j];
-
- quat2eul(eulerEst, tempQuat);
-
- for (uint8_t j = 0; j <= 2; j++) eulerDif[j] = eulerEst[j] - ahrsEul[j];
-
- if (eulerDif[2] > pi) eulerDif[2] -= 2 * pi;
-
- if (eulerDif[2] < -pi) eulerDif[2] += 2 * pi;
-
-#endif
- // store the predicted states for subsequent use by measurement fusion
- _ekf->StoreStates(IMUmsec);
- // Check if on ground - status is used by covariance prediction
- _ekf->OnGroundCheck();
- // sum delta angles and time used by covariance prediction
- _ekf->summedDelAng = _ekf->summedDelAng + _ekf->correctedDelAng;
- _ekf->summedDelVel = _ekf->summedDelVel + _ekf->dVelIMU;
- dt += _ekf->dtIMU;
-
- // perform a covariance prediction if the total delta angle has exceeded the limit
- // or the time limit will be exceeded at the next IMU update
- if ((dt >= (_ekf->covTimeStepMax - _ekf->dtIMU)) || (_ekf->summedDelAng.length() > _ekf->covDelAngMax)) {
- _ekf->CovariancePrediction(dt);
- _ekf->summedDelAng.zero();
- _ekf->summedDelVel.zero();
- dt = 0.0f;
- }
-
- _initialized = true;
- }
-
- // Fuse GPS Measurements
- if (newDataGps && _gps_initialized) {
- // Convert GPS measurements to Pos NE, hgt and Vel NED
- _ekf->velNED[0] = _gps.vel_n_m_s;
- _ekf->velNED[1] = _gps.vel_e_m_s;
- _ekf->velNED[2] = _gps.vel_d_m_s;
- _ekf->calcposNED(_ekf->posNED, _ekf->gpsLat, _ekf->gpsLon, _ekf->gpsHgt, _ekf->latRef, _ekf->lonRef, _ekf->hgtRef);
-
- _ekf->posNE[0] = _ekf->posNED[0];
- _ekf->posNE[1] = _ekf->posNED[1];
- // set fusion flags
- _ekf->fuseVelData = true;
- _ekf->fusePosData = true;
- // recall states stored at time of measurement after adjusting for delays
- _ekf->RecallStates(_ekf->statesAtVelTime, (IMUmsec - _parameters.vel_delay_ms));
- _ekf->RecallStates(_ekf->statesAtPosTime, (IMUmsec - _parameters.pos_delay_ms));
- // run the fusion step
- _ekf->FuseVelposNED();
-
- } else if (_ekf->statesInitialised) {
- // Convert GPS measurements to Pos NE, hgt and Vel NED
- _ekf->velNED[0] = 0.0f;
- _ekf->velNED[1] = 0.0f;
- _ekf->velNED[2] = 0.0f;
- _ekf->posNED[0] = 0.0f;
- _ekf->posNED[1] = 0.0f;
- _ekf->posNED[2] = 0.0f;
-
- _ekf->posNE[0] = _ekf->posNED[0];
- _ekf->posNE[1] = _ekf->posNED[1];
- // set fusion flags
- _ekf->fuseVelData = true;
- _ekf->fusePosData = true;
- // recall states stored at time of measurement after adjusting for delays
- _ekf->RecallStates(_ekf->statesAtVelTime, (IMUmsec - _parameters.vel_delay_ms));
- _ekf->RecallStates(_ekf->statesAtPosTime, (IMUmsec - _parameters.pos_delay_ms));
- // run the fusion step
- _ekf->FuseVelposNED();
-
- } else {
- _ekf->fuseVelData = false;
- _ekf->fusePosData = false;
- }
-
- if (newHgtData && _ekf->statesInitialised) {
- // Could use a blend of GPS and baro alt data if desired
- _ekf->hgtMea = 1.0f * (_ekf->baroHgt - _baro_ref);
- _ekf->fuseHgtData = true;
- // recall states stored at time of measurement after adjusting for delays
- _ekf->RecallStates(_ekf->statesAtHgtTime, (IMUmsec - _parameters.height_delay_ms));
- // run the fusion step
- _ekf->FuseVelposNED();
- } else {
- _ekf->fuseHgtData = false;
- }
-
- // Fuse Magnetometer Measurements
- if (newDataMag && _ekf->statesInitialised) {
- _ekf->fuseMagData = true;
- _ekf->RecallStates(_ekf->statesAtMagMeasTime, (IMUmsec - _parameters.mag_delay_ms)); // Assume 50 msec avg delay for magnetometer data
-
- } else {
- _ekf->fuseMagData = false;
- }
+ } else if (_ekf->statesInitialised) {
- if (_ekf->statesInitialised) _ekf->FuseMagnetometer();
+ // We're apparently initialized in this case now
+ // check (and reset the filter as needed)
+ int check = check_filter_state();
- // Fuse Airspeed Measurements
- if (newAdsData && _ekf->statesInitialised && _ekf->VtasMeas > 8.0f) {
- _ekf->fuseVtasData = true;
- _ekf->RecallStates(_ekf->statesAtVtasMeasTime, (IMUmsec - _parameters.tas_delay_ms)); // assume 100 msec avg delay for airspeed data
- _ekf->FuseAirspeed();
+ if (check) {
+ // Let the system re-initialize itself
+ continue;
+ }
- } else {
- _ekf->fuseVtasData = false;
- }
+ // Run the strapdown INS equations every IMU update
+ _ekf->UpdateStrapdownEquationsNED();
+
+ // store the predicted states for subsequent use by measurement fusion
+ _ekf->StoreStates(IMUmsec);
+ // Check if on ground - status is used by covariance prediction
+ _ekf->OnGroundCheck();
+ // sum delta angles and time used by covariance prediction
+ _ekf->summedDelAng = _ekf->summedDelAng + _ekf->correctedDelAng;
+ _ekf->summedDelVel = _ekf->summedDelVel + _ekf->dVelIMU;
+ dt += _ekf->dtIMU;
+
+ // perform a covariance prediction if the total delta angle has exceeded the limit
+ // or the time limit will be exceeded at the next IMU update
+ if ((dt >= (_ekf->covTimeStepMax - _ekf->dtIMU)) || (_ekf->summedDelAng.length() > _ekf->covDelAngMax)) {
+ _ekf->CovariancePrediction(dt);
+ _ekf->summedDelAng.zero();
+ _ekf->summedDelVel.zero();
+ dt = 0.0f;
+ }
- // Publish results
- if (_initialized && (check == OK)) {
-
-
-
- // State vector:
- // 0-3: quaternions (q0, q1, q2, q3)
- // 4-6: Velocity - m/sec (North, East, Down)
- // 7-9: Position - m (North, East, Down)
- // 10-12: Delta Angle bias - rad (X,Y,Z)
- // 13-14: Wind Vector - m/sec (North,East)
- // 15-17: Earth Magnetic Field Vector - milligauss (North, East, Down)
- // 18-20: Body Magnetic Field Vector - milligauss (X,Y,Z)
-
- math::Quaternion q(_ekf->states[0], _ekf->states[1], _ekf->states[2], _ekf->states[3]);
- math::Matrix<3, 3> R = q.to_dcm();
- math::Vector<3> euler = R.to_euler();
-
- for (int i = 0; i < 3; i++) for (int j = 0; j < 3; j++)
- _att.R[i][j] = R(i, j);
-
- _att.timestamp = last_sensor_timestamp;
- _att.q[0] = _ekf->states[0];
- _att.q[1] = _ekf->states[1];
- _att.q[2] = _ekf->states[2];
- _att.q[3] = _ekf->states[3];
- _att.q_valid = true;
- _att.R_valid = true;
-
- _att.timestamp = last_sensor_timestamp;
- _att.roll = euler(0);
- _att.pitch = euler(1);
- _att.yaw = euler(2);
-
- _att.rollspeed = _ekf->angRate.x - _ekf->states[10];
- _att.pitchspeed = _ekf->angRate.y - _ekf->states[11];
- _att.yawspeed = _ekf->angRate.z - _ekf->states[12];
- // gyro offsets
- _att.rate_offsets[0] = _ekf->states[10];
- _att.rate_offsets[1] = _ekf->states[11];
- _att.rate_offsets[2] = _ekf->states[12];
-
- /* lazily publish the attitude only once available */
- if (_att_pub > 0) {
- /* publish the attitude setpoint */
- orb_publish(ORB_ID(vehicle_attitude), _att_pub, &_att);
+ // Fuse GPS Measurements
+ if (newDataGps && _gps_initialized) {
+ // Convert GPS measurements to Pos NE, hgt and Vel NED
+
+ float gps_dt = (_gps.timestamp_position - last_gps) / 1e6f;
+
+ // Calculate acceleration predicted by GPS velocity change
+ if (((fabsf(_ekf->velNED[0] - _gps.vel_n_m_s) > FLT_EPSILON) ||
+ (fabsf(_ekf->velNED[1] - _gps.vel_e_m_s) > FLT_EPSILON) ||
+ (fabsf(_ekf->velNED[2] - _gps.vel_d_m_s) > FLT_EPSILON)) && (gps_dt > 0.00001f)) {
+
+ _ekf->accelGPSNED[0] = (_ekf->velNED[0] - _gps.vel_n_m_s) / gps_dt;
+ _ekf->accelGPSNED[1] = (_ekf->velNED[1] - _gps.vel_e_m_s) / gps_dt;
+ _ekf->accelGPSNED[2] = (_ekf->velNED[2] - _gps.vel_d_m_s) / gps_dt;
+ }
+
+ _ekf->velNED[0] = _gps.vel_n_m_s;
+ _ekf->velNED[1] = _gps.vel_e_m_s;
+ _ekf->velNED[2] = _gps.vel_d_m_s;
+ _ekf->calcposNED(posNED, _ekf->gpsLat, _ekf->gpsLon, _ekf->gpsHgt, _ekf->latRef, _ekf->lonRef, _ekf->hgtRef);
+
+ _ekf->posNE[0] = posNED[0];
+ _ekf->posNE[1] = posNED[1];
+ // set fusion flags
+ _ekf->fuseVelData = true;
+ _ekf->fusePosData = true;
+ // recall states stored at time of measurement after adjusting for delays
+ _ekf->RecallStates(_ekf->statesAtVelTime, (IMUmsec - _parameters.vel_delay_ms));
+ _ekf->RecallStates(_ekf->statesAtPosTime, (IMUmsec - _parameters.pos_delay_ms));
+ // run the fusion step
+ _ekf->FuseVelposNED();
+
+ } else if (!_gps_initialized) {
+
+ // force static mode
+ _ekf->staticMode = true;
+
+ // Convert GPS measurements to Pos NE, hgt and Vel NED
+ _ekf->velNED[0] = 0.0f;
+ _ekf->velNED[1] = 0.0f;
+ _ekf->velNED[2] = 0.0f;
+
+ _ekf->posNE[0] = 0.0f;
+ _ekf->posNE[1] = 0.0f;
+ // set fusion flags
+ _ekf->fuseVelData = true;
+ _ekf->fusePosData = true;
+ // recall states stored at time of measurement after adjusting for delays
+ _ekf->RecallStates(_ekf->statesAtVelTime, (IMUmsec - _parameters.vel_delay_ms));
+ _ekf->RecallStates(_ekf->statesAtPosTime, (IMUmsec - _parameters.pos_delay_ms));
+ // run the fusion step
+ _ekf->FuseVelposNED();
+
+ } else {
+ _ekf->fuseVelData = false;
+ _ekf->fusePosData = false;
+ }
- } else {
- /* advertise and publish */
- _att_pub = orb_advertise(ORB_ID(vehicle_attitude), &_att);
- }
- }
+ if (newHgtData) {
+ // Could use a blend of GPS and baro alt data if desired
+ _ekf->hgtMea = 1.0f * (_ekf->baroHgt - _baro_ref);
+ _ekf->fuseHgtData = true;
+ // recall states stored at time of measurement after adjusting for delays
+ _ekf->RecallStates(_ekf->statesAtHgtTime, (IMUmsec - _parameters.height_delay_ms));
+ // run the fusion step
+ _ekf->FuseVelposNED();
+
+ } else {
+ _ekf->fuseHgtData = false;
+ }
- if (_gps_initialized) {
- _local_pos.timestamp = last_sensor_timestamp;
- _local_pos.x = _ekf->states[7];
- _local_pos.y = _ekf->states[8];
- // XXX need to announce change of Z reference somehow elegantly
- _local_pos.z = _ekf->states[9] - _baro_gps_offset;
+ // Fuse Magnetometer Measurements
+ if (newDataMag) {
+ _ekf->fuseMagData = true;
+ _ekf->RecallStates(_ekf->statesAtMagMeasTime, (IMUmsec - _parameters.mag_delay_ms)); // Assume 50 msec avg delay for magnetometer data
- _local_pos.vx = _ekf->states[4];
- _local_pos.vy = _ekf->states[5];
- _local_pos.vz = _ekf->states[6];
+ _ekf->magstate.obsIndex = 0;
+ _ekf->FuseMagnetometer();
+ _ekf->FuseMagnetometer();
+ _ekf->FuseMagnetometer();
- _local_pos.xy_valid = _gps_initialized;
- _local_pos.z_valid = true;
- _local_pos.v_xy_valid = _gps_initialized;
- _local_pos.v_z_valid = true;
- _local_pos.xy_global = true;
+ } else {
+ _ekf->fuseMagData = false;
+ }
- _local_pos.z_global = false;
- _local_pos.yaw = _att.yaw;
+ // Fuse Airspeed Measurements
+ if (newAdsData && _ekf->VtasMeas > 7.0f) {
+ _ekf->fuseVtasData = true;
+ _ekf->RecallStates(_ekf->statesAtVtasMeasTime, (IMUmsec - _parameters.tas_delay_ms)); // assume 100 msec avg delay for airspeed data
+ _ekf->FuseAirspeed();
- /* lazily publish the local position only once available */
- if (_local_pos_pub > 0) {
- /* publish the attitude setpoint */
- orb_publish(ORB_ID(vehicle_local_position), _local_pos_pub, &_local_pos);
+ } else {
+ _ekf->fuseVtasData = false;
+ }
- } else {
- /* advertise and publish */
- _local_pos_pub = orb_advertise(ORB_ID(vehicle_local_position), &_local_pos);
- }
+ if (newRangeData) {
+ _ekf->fuseRngData = true;
+ _ekf->useRangeFinder = true;
+ _ekf->RecallStates(_ekf->statesAtRngTime, (IMUmsec - 100.0f));
+ _ekf->GroundEKF();
+ }
- _global_pos.timestamp = _local_pos.timestamp;
- if (_local_pos.xy_global) {
- double est_lat, est_lon;
- map_projection_reproject(&_pos_ref, _local_pos.x, _local_pos.y, &est_lat, &est_lon);
- _global_pos.lat = est_lat;
- _global_pos.lon = est_lon;
- _global_pos.time_gps_usec = _gps.time_gps_usec;
- _global_pos.eph = _gps.eph_m;
- _global_pos.epv = _gps.epv_m;
- }
+ // Output results
+ math::Quaternion q(_ekf->states[0], _ekf->states[1], _ekf->states[2], _ekf->states[3]);
+ math::Matrix<3, 3> R = q.to_dcm();
+ math::Vector<3> euler = R.to_euler();
+
+ for (int i = 0; i < 3; i++) for (int j = 0; j < 3; j++)
+ _att.R[i][j] = R(i, j);
+
+ _att.timestamp = _last_sensor_timestamp;
+ _att.q[0] = _ekf->states[0];
+ _att.q[1] = _ekf->states[1];
+ _att.q[2] = _ekf->states[2];
+ _att.q[3] = _ekf->states[3];
+ _att.q_valid = true;
+ _att.R_valid = true;
+
+ _att.timestamp = _last_sensor_timestamp;
+ _att.roll = euler(0);
+ _att.pitch = euler(1);
+ _att.yaw = euler(2);
+
+ _att.rollspeed = _ekf->angRate.x - _ekf->states[10];
+ _att.pitchspeed = _ekf->angRate.y - _ekf->states[11];
+ _att.yawspeed = _ekf->angRate.z - _ekf->states[12];
+ // gyro offsets
+ _att.rate_offsets[0] = _ekf->states[10];
+ _att.rate_offsets[1] = _ekf->states[11];
+ _att.rate_offsets[2] = _ekf->states[12];
+
+ /* lazily publish the attitude only once available */
+ if (_att_pub > 0) {
+ /* publish the attitude setpoint */
+ orb_publish(ORB_ID(vehicle_attitude), _att_pub, &_att);
+
+ } else {
+ /* advertise and publish */
+ _att_pub = orb_advertise(ORB_ID(vehicle_attitude), &_att);
+ }
- if (_local_pos.v_xy_valid) {
- _global_pos.vel_n = _local_pos.vx;
- _global_pos.vel_e = _local_pos.vy;
- } else {
- _global_pos.vel_n = 0.0f;
- _global_pos.vel_e = 0.0f;
- }
+ if (_gps_initialized) {
+ _local_pos.timestamp = _last_sensor_timestamp;
+ _local_pos.x = _ekf->states[7];
+ _local_pos.y = _ekf->states[8];
+ // XXX need to announce change of Z reference somehow elegantly
+ _local_pos.z = _ekf->states[9] - _baro_ref_offset;
+
+ _local_pos.vx = _ekf->states[4];
+ _local_pos.vy = _ekf->states[5];
+ _local_pos.vz = _ekf->states[6];
+
+ _local_pos.xy_valid = _gps_initialized;
+ _local_pos.z_valid = true;
+ _local_pos.v_xy_valid = _gps_initialized;
+ _local_pos.v_z_valid = true;
+ _local_pos.xy_global = true;
+
+ _velocity_xy_filtered = 0.95f*_velocity_xy_filtered + 0.05f*sqrtf(_local_pos.vx*_local_pos.vx + _local_pos.vy*_local_pos.vy);
+ _velocity_z_filtered = 0.95f*_velocity_z_filtered + 0.05f*fabsf(_local_pos.vz);
+ _airspeed_filtered = 0.95f*_airspeed_filtered + 0.05f*_airspeed.true_airspeed_m_s;
+
+
+ /* crude land detector for fixedwing only,
+ * TODO: adapt so that it works for both, maybe move to another location
+ */
+ if (_velocity_xy_filtered < 5
+ && _velocity_z_filtered < 10
+ && _airspeed_filtered < 10) {
+ _local_pos.landed = true;
+ } else {
+ _local_pos.landed = false;
+ }
+
+ _local_pos.z_global = false;
+ _local_pos.yaw = _att.yaw;
+
+ /* lazily publish the local position only once available */
+ if (_local_pos_pub > 0) {
+ /* publish the attitude setpoint */
+ orb_publish(ORB_ID(vehicle_local_position), _local_pos_pub, &_local_pos);
+
+ } else {
+ /* advertise and publish */
+ _local_pos_pub = orb_advertise(ORB_ID(vehicle_local_position), &_local_pos);
+ }
+
+ _global_pos.timestamp = _local_pos.timestamp;
+
+ if (_local_pos.xy_global) {
+ double est_lat, est_lon;
+ map_projection_reproject(&_pos_ref, _local_pos.x, _local_pos.y, &est_lat, &est_lon);
+ _global_pos.lat = est_lat;
+ _global_pos.lon = est_lon;
+ _global_pos.time_gps_usec = _gps.time_gps_usec;
+ _global_pos.eph = _gps.eph;
+ _global_pos.epv = _gps.epv;
+ }
+
+ if (_local_pos.v_xy_valid) {
+ _global_pos.vel_n = _local_pos.vx;
+ _global_pos.vel_e = _local_pos.vy;
+ } else {
+ _global_pos.vel_n = 0.0f;
+ _global_pos.vel_e = 0.0f;
+ }
+
+ /* local pos alt is negative, change sign and add alt offsets */
+ _global_pos.alt = _baro_ref + (-_local_pos.z) - _baro_gps_offset;
+
+ if (_local_pos.v_z_valid) {
+ _global_pos.vel_d = _local_pos.vz;
+ }
+
+ /* terrain altitude */
+ _global_pos.terrain_alt = _ekf->hgtRef - _ekf->flowStates[1];
+ _global_pos.terrain_alt_valid = (_distance_last_valid > 0) &&
+ (hrt_elapsed_time(&_distance_last_valid) < 20 * 1000 * 1000);
+
+ _global_pos.yaw = _local_pos.yaw;
+
+ _global_pos.eph = _gps.eph;
+ _global_pos.epv = _gps.epv;
+
+ _global_pos.timestamp = _local_pos.timestamp;
+
+ /* lazily publish the global position only once available */
+ if (_global_pos_pub > 0) {
+ /* publish the global position */
+ orb_publish(ORB_ID(vehicle_global_position), _global_pos_pub, &_global_pos);
+
+ } else {
+ /* advertise and publish */
+ _global_pos_pub = orb_advertise(ORB_ID(vehicle_global_position), &_global_pos);
+ }
+
+ if (hrt_elapsed_time(&_wind.timestamp) > 99000) {
+ _wind.timestamp = _global_pos.timestamp;
+ _wind.windspeed_north = _ekf->windSpdFiltNorth;
+ _wind.windspeed_east = _ekf->windSpdFiltEast;
+ // XXX we need to do something smart about the covariance here
+ // but we default to the estimate covariance for now
+ _wind.covariance_north = _ekf->P[14][14];
+ _wind.covariance_east = _ekf->P[15][15];
+
+ /* lazily publish the wind estimate only once available */
+ if (_wind_pub > 0) {
+ /* publish the wind estimate */
+ orb_publish(ORB_ID(wind_estimate), _wind_pub, &_wind);
+
+ } else {
+ /* advertise and publish */
+ _wind_pub = orb_advertise(ORB_ID(wind_estimate), &_wind);
+ }
+ }
- /* local pos alt is negative, change sign and add alt offsets */
- _global_pos.alt = _local_pos.ref_alt + _baro_gps_offset + (-_local_pos.z);
+ }
- if (_local_pos.v_z_valid) {
- _global_pos.vel_d = _local_pos.vz;
}
- _global_pos.yaw = _local_pos.yaw;
-
- _global_pos.eph = _gps.eph_m;
- _global_pos.epv = _gps.epv_m;
-
- _global_pos.timestamp = _local_pos.timestamp;
-
- /* lazily publish the global position only once available */
- if (_global_pos_pub > 0) {
- /* publish the attitude setpoint */
- orb_publish(ORB_ID(vehicle_global_position), _global_pos_pub, &_global_pos);
-
- } else {
- /* advertise and publish */
- _global_pos_pub = orb_advertise(ORB_ID(vehicle_global_position), &_global_pos);
- }
}
}
@@ -1342,6 +1543,8 @@ FixedwingEstimator::task_main()
perf_end(_loop_perf);
}
+ _task_running = false;
+
warnx("exiting.\n");
_estimator_task = -1;
@@ -1357,7 +1560,7 @@ FixedwingEstimator::start()
_estimator_task = task_spawn_cmd("ekf_att_pos_estimator",
SCHED_DEFAULT,
SCHED_PRIORITY_MAX - 40,
- 6000,
+ 7500,
(main_t)&FixedwingEstimator::task_main_trampoline,
nullptr);
@@ -1384,32 +1587,44 @@ FixedwingEstimator::print_status()
// 4-6: Velocity - m/sec (North, East, Down)
// 7-9: Position - m (North, East, Down)
// 10-12: Delta Angle bias - rad (X,Y,Z)
- // 13-14: Wind Vector - m/sec (North,East)
- // 15-17: Earth Magnetic Field Vector - gauss (North, East, Down)
- // 18-20: Body Magnetic Field Vector - gauss (X,Y,Z)
+ // 13: Accelerometer offset
+ // 14-15: Wind Vector - m/sec (North,East)
+ // 16-18: Earth Magnetic Field Vector - gauss (North, East, Down)
+ // 19-21: Body Magnetic Field Vector - gauss (X,Y,Z)
printf("dtIMU: %8.6f IMUmsec: %d\n", (double)_ekf->dtIMU, (int)IMUmsec);
- printf("ref alt: %8.6f\n", (double)_local_pos.ref_alt);
+ printf("baro alt: %8.4f GPS alt: %8.4f\n", (double)_baro.altitude, (double)(_gps.alt / 1e3f));
+ printf("ref alt: %8.4f baro ref offset: %8.4f baro GPS offset: %8.4f\n", (double)_baro_ref, (double)_baro_ref_offset, (double)_baro_gps_offset);
printf("dvel: %8.6f %8.6f %8.6f accel: %8.6f %8.6f %8.6f\n", (double)_ekf->dVelIMU.x, (double)_ekf->dVelIMU.y, (double)_ekf->dVelIMU.z, (double)_ekf->accel.x, (double)_ekf->accel.y, (double)_ekf->accel.z);
printf("dang: %8.4f %8.4f %8.4f dang corr: %8.4f %8.4f %8.4f\n" , (double)_ekf->dAngIMU.x, (double)_ekf->dAngIMU.y, (double)_ekf->dAngIMU.z, (double)_ekf->correctedDelAng.x, (double)_ekf->correctedDelAng.y, (double)_ekf->correctedDelAng.z);
- printf("states (quat) [1-4]: %8.4f, %8.4f, %8.4f, %8.4f\n", (double)_ekf->states[0], (double)_ekf->states[1], (double)_ekf->states[2], (double)_ekf->states[3]);
- printf("states (vel m/s) [5-7]: %8.4f, %8.4f, %8.4f\n", (double)_ekf->states[4], (double)_ekf->states[5], (double)_ekf->states[6]);
- printf("states (pos m) [8-10]: %8.4f, %8.4f, %8.4f\n", (double)_ekf->states[7], (double)_ekf->states[8], (double)_ekf->states[9]);
- printf("states (delta ang) [11-13]: %8.4f, %8.4f, %8.4f\n", (double)_ekf->states[10], (double)_ekf->states[11], (double)_ekf->states[12]);
- printf("states (wind) [14-15]: %8.4f, %8.4f\n", (double)_ekf->states[13], (double)_ekf->states[14]);
- printf("states (earth mag) [16-18]: %8.4f, %8.4f, %8.4f\n", (double)_ekf->states[15], (double)_ekf->states[16], (double)_ekf->states[17]);
- printf("states (body mag) [19-21]: %8.4f, %8.4f, %8.4f\n", (double)_ekf->states[18], (double)_ekf->states[19], (double)_ekf->states[20]);
+ printf("states (quat) [0-3]: %8.4f, %8.4f, %8.4f, %8.4f\n", (double)_ekf->states[0], (double)_ekf->states[1], (double)_ekf->states[2], (double)_ekf->states[3]);
+ printf("states (vel m/s) [4-6]: %8.4f, %8.4f, %8.4f\n", (double)_ekf->states[4], (double)_ekf->states[5], (double)_ekf->states[6]);
+ printf("states (pos m) [7-9]: %8.4f, %8.4f, %8.4f\n", (double)_ekf->states[7], (double)_ekf->states[8], (double)_ekf->states[9]);
+ printf("states (delta ang) [10-12]: %8.4f, %8.4f, %8.4f\n", (double)_ekf->states[10], (double)_ekf->states[11], (double)_ekf->states[12]);
+
+ if (n_states == 23) {
+ printf("states (accel offs) [13]: %8.4f\n", (double)_ekf->states[13]);
+ printf("states (wind) [14-15]: %8.4f, %8.4f\n", (double)_ekf->states[14], (double)_ekf->states[15]);
+ printf("states (earth mag) [16-18]: %8.4f, %8.4f, %8.4f\n", (double)_ekf->states[16], (double)_ekf->states[17], (double)_ekf->states[18]);
+ printf("states (body mag) [19-21]: %8.4f, %8.4f, %8.4f\n", (double)_ekf->states[19], (double)_ekf->states[20], (double)_ekf->states[21]);
+ printf("states (terrain) [22]: %8.4f\n", (double)_ekf->states[22]);
+
+ } else {
+ printf("states (wind) [13-14]: %8.4f, %8.4f\n", (double)_ekf->states[13], (double)_ekf->states[14]);
+ printf("states (earth mag) [15-17]: %8.4f, %8.4f, %8.4f\n", (double)_ekf->states[15], (double)_ekf->states[16], (double)_ekf->states[17]);
+ printf("states (body mag) [18-20]: %8.4f, %8.4f, %8.4f\n", (double)_ekf->states[18], (double)_ekf->states[19], (double)_ekf->states[20]);
+ }
printf("states: %s %s %s %s %s %s %s %s %s %s\n",
- (_ekf->statesInitialised) ? "INITIALIZED" : "NON_INIT",
- (_ekf->onGround) ? "ON_GROUND" : "AIRBORNE",
- (_ekf->fuseVelData) ? "FUSE_VEL" : "INH_VEL",
- (_ekf->fusePosData) ? "FUSE_POS" : "INH_POS",
- (_ekf->fuseHgtData) ? "FUSE_HGT" : "INH_HGT",
- (_ekf->fuseMagData) ? "FUSE_MAG" : "INH_MAG",
- (_ekf->fuseVtasData) ? "FUSE_VTAS" : "INH_VTAS",
- (_ekf->useAirspeed) ? "USE_AIRSPD" : "IGN_AIRSPD",
- (_ekf->useCompass) ? "USE_COMPASS" : "IGN_COMPASS",
- (_ekf->staticMode) ? "STATIC_MODE" : "DYNAMIC_MODE");
+ (_ekf->statesInitialised) ? "INITIALIZED" : "NON_INIT",
+ (_ekf->onGround) ? "ON_GROUND" : "AIRBORNE",
+ (_ekf->fuseVelData) ? "FUSE_VEL" : "INH_VEL",
+ (_ekf->fusePosData) ? "FUSE_POS" : "INH_POS",
+ (_ekf->fuseHgtData) ? "FUSE_HGT" : "INH_HGT",
+ (_ekf->fuseMagData) ? "FUSE_MAG" : "INH_MAG",
+ (_ekf->fuseVtasData) ? "FUSE_VTAS" : "INH_VTAS",
+ (_ekf->useAirspeed) ? "USE_AIRSPD" : "IGN_AIRSPD",
+ (_ekf->useCompass) ? "USE_COMPASS" : "IGN_COMPASS",
+ (_ekf->staticMode) ? "STATIC_MODE" : "DYNAMIC_MODE");
}
int FixedwingEstimator::trip_nan() {
@@ -1464,7 +1679,7 @@ int FixedwingEstimator::trip_nan() {
int ekf_att_pos_estimator_main(int argc, char *argv[])
{
if (argc < 1)
- errx(1, "usage: ekf_att_pos_estimator {start|stop|status}");
+ errx(1, "usage: ekf_att_pos_estimator {start|stop|status|logging}");
if (!strcmp(argv[1], "start")) {
@@ -1482,6 +1697,14 @@ int ekf_att_pos_estimator_main(int argc, char *argv[])
err(1, "start failed");
}
+ /* avoid memory fragmentation by not exiting start handler until the task has fully started */
+ while (estimator::g_estimator == nullptr || !estimator::g_estimator->task_running()) {
+ usleep(50000);
+ printf(".");
+ fflush(stdout);
+ }
+ printf("\n");
+
exit(0);
}
@@ -1518,6 +1741,29 @@ int ekf_att_pos_estimator_main(int argc, char *argv[])
}
}
+ if (!strcmp(argv[1], "logging")) {
+ if (estimator::g_estimator) {
+ int ret = estimator::g_estimator->enable_logging(true);
+
+ exit(ret);
+
+ } else {
+ errx(1, "not running");
+ }
+ }
+
+ if (!strcmp(argv[1], "debug")) {
+ if (estimator::g_estimator) {
+ int debug = strtoul(argv[2], NULL, 10);
+ int ret = estimator::g_estimator->set_debuglevel(debug);
+
+ exit(ret);
+
+ } else {
+ errx(1, "not running");
+ }
+ }
+
warnx("unrecognized command");
return 1;
}
diff --git a/src/modules/ekf_att_pos_estimator/estimator_21states.cpp b/src/modules/ekf_att_pos_estimator/estimator_21states.cpp
new file mode 100644
index 000000000..67bfec4ea
--- /dev/null
+++ b/src/modules/ekf_att_pos_estimator/estimator_21states.cpp
@@ -0,0 +1,2142 @@
+#include "estimator_21states.h"
+
+#include <string.h>
+
+AttPosEKF::AttPosEKF() :
+ fusionModeGPS(0),
+ covSkipCount(0),
+ EAS2TAS(1.0f),
+ statesInitialised(false),
+ fuseVelData(false),
+ fusePosData(false),
+ fuseHgtData(false),
+ fuseMagData(false),
+ fuseVtasData(false),
+ onGround(true),
+ staticMode(true),
+ useAirspeed(true),
+ useCompass(true),
+ numericalProtection(true),
+ storeIndex(0),
+ magDeclination(0.0f)
+{
+ InitialiseParameters();
+}
+
+AttPosEKF::~AttPosEKF()
+{
+}
+
+void AttPosEKF::UpdateStrapdownEquationsNED()
+{
+ Vector3f delVelNav;
+ float q00;
+ float q11;
+ float q22;
+ float q33;
+ float q01;
+ float q02;
+ float q03;
+ float q12;
+ float q13;
+ float q23;
+ Mat3f Tbn;
+ Mat3f Tnb;
+ float rotationMag;
+ float qUpdated[4];
+ float quatMag;
+ double deltaQuat[4];
+ const Vector3f gravityNED = {0.0,0.0,GRAVITY_MSS};
+
+// Remove sensor bias errors
+ correctedDelAng.x = dAngIMU.x - states[10];
+ correctedDelAng.y = dAngIMU.y - states[11];
+ correctedDelAng.z = dAngIMU.z - states[12];
+ dVelIMU.x = dVelIMU.x;
+ dVelIMU.y = dVelIMU.y;
+ dVelIMU.z = dVelIMU.z;
+
+// Save current measurements
+ Vector3f prevDelAng = correctedDelAng;
+
+// Apply corrections for earths rotation rate and coning errors
+// * and + operators have been overloaded
+ correctedDelAng = correctedDelAng - Tnb*earthRateNED*dtIMU + 8.333333333333333e-2f*(prevDelAng % correctedDelAng);
+// Convert the rotation vector to its equivalent quaternion
+ rotationMag = correctedDelAng.length();
+ if (rotationMag < 1e-12f)
+ {
+ deltaQuat[0] = 1.0;
+ deltaQuat[1] = 0.0;
+ deltaQuat[2] = 0.0;
+ deltaQuat[3] = 0.0;
+ }
+ else
+ {
+ deltaQuat[0] = cos(0.5f*rotationMag);
+ double rotScaler = (sin(0.5f*rotationMag))/rotationMag;
+ deltaQuat[1] = correctedDelAng.x*rotScaler;
+ deltaQuat[2] = correctedDelAng.y*rotScaler;
+ deltaQuat[3] = correctedDelAng.z*rotScaler;
+ }
+
+// Update the quaternions by rotating from the previous attitude through
+// the delta angle rotation quaternion
+ qUpdated[0] = states[0]*deltaQuat[0] - states[1]*deltaQuat[1] - states[2]*deltaQuat[2] - states[3]*deltaQuat[3];
+ qUpdated[1] = states[0]*deltaQuat[1] + states[1]*deltaQuat[0] + states[2]*deltaQuat[3] - states[3]*deltaQuat[2];
+ qUpdated[2] = states[0]*deltaQuat[2] + states[2]*deltaQuat[0] + states[3]*deltaQuat[1] - states[1]*deltaQuat[3];
+ qUpdated[3] = states[0]*deltaQuat[3] + states[3]*deltaQuat[0] + states[1]*deltaQuat[2] - states[2]*deltaQuat[1];
+
+// Normalise the quaternions and update the quaternion states
+ quatMag = sqrtf(sq(qUpdated[0]) + sq(qUpdated[1]) + sq(qUpdated[2]) + sq(qUpdated[3]));
+ if (quatMag > 1e-16f)
+ {
+ float quatMagInv = 1.0f/quatMag;
+ states[0] = quatMagInv*qUpdated[0];
+ states[1] = quatMagInv*qUpdated[1];
+ states[2] = quatMagInv*qUpdated[2];
+ states[3] = quatMagInv*qUpdated[3];
+ }
+
+// Calculate the body to nav cosine matrix
+ q00 = sq(states[0]);
+ q11 = sq(states[1]);
+ q22 = sq(states[2]);
+ q33 = sq(states[3]);
+ q01 = states[0]*states[1];
+ q02 = states[0]*states[2];
+ q03 = states[0]*states[3];
+ q12 = states[1]*states[2];
+ q13 = states[1]*states[3];
+ q23 = states[2]*states[3];
+
+ Tbn.x.x = q00 + q11 - q22 - q33;
+ Tbn.y.y = q00 - q11 + q22 - q33;
+ Tbn.z.z = q00 - q11 - q22 + q33;
+ Tbn.x.y = 2*(q12 - q03);
+ Tbn.x.z = 2*(q13 + q02);
+ Tbn.y.x = 2*(q12 + q03);
+ Tbn.y.z = 2*(q23 - q01);
+ Tbn.z.x = 2*(q13 - q02);
+ Tbn.z.y = 2*(q23 + q01);
+
+ Tnb = Tbn.transpose();
+
+// transform body delta velocities to delta velocities in the nav frame
+// * and + operators have been overloaded
+ //delVelNav = Tbn*dVelIMU + gravityNED*dtIMU;
+ delVelNav.x = Tbn.x.x*dVelIMU.x + Tbn.x.y*dVelIMU.y + Tbn.x.z*dVelIMU.z + gravityNED.x*dtIMU;
+ delVelNav.y = Tbn.y.x*dVelIMU.x + Tbn.y.y*dVelIMU.y + Tbn.y.z*dVelIMU.z + gravityNED.y*dtIMU;
+ delVelNav.z = Tbn.z.x*dVelIMU.x + Tbn.z.y*dVelIMU.y + Tbn.z.z*dVelIMU.z + gravityNED.z*dtIMU;
+
+// calculate the magnitude of the nav acceleration (required for GPS
+// variance estimation)
+ accNavMag = delVelNav.length()/dtIMU;
+
+// If calculating position save previous velocity
+ float lastVelocity[3];
+ lastVelocity[0] = states[4];
+ lastVelocity[1] = states[5];
+ lastVelocity[2] = states[6];
+
+// Sum delta velocities to get velocity
+ states[4] = states[4] + delVelNav.x;
+ states[5] = states[5] + delVelNav.y;
+ states[6] = states[6] + delVelNav.z;
+
+// If calculating postions, do a trapezoidal integration for position
+ states[7] = states[7] + 0.5f*(states[4] + lastVelocity[0])*dtIMU;
+ states[8] = states[8] + 0.5f*(states[5] + lastVelocity[1])*dtIMU;
+ states[9] = states[9] + 0.5f*(states[6] + lastVelocity[2])*dtIMU;
+
+ // Constrain states (to protect against filter divergence)
+ //ConstrainStates();
+}
+
+void AttPosEKF::CovariancePrediction(float dt)
+{
+ // scalars
+ float daxCov;
+ float dayCov;
+ float dazCov;
+ float dvxCov;
+ float dvyCov;
+ float dvzCov;
+ float dvx;
+ float dvy;
+ float dvz;
+ float dax;
+ float day;
+ float daz;
+ float q0;
+ float q1;
+ float q2;
+ float q3;
+ float dax_b;
+ float day_b;
+ float daz_b;
+
+ // arrays
+ float processNoise[21];
+ float SF[14];
+ float SG[8];
+ float SQ[11];
+ float SPP[13] = {0};
+ float nextP[21][21];
+
+ // calculate covariance prediction process noise
+ windVelSigma = dt*0.1f;
+ dAngBiasSigma = dt*5.0e-7f;
+ magEarthSigma = dt*3.0e-4f;
+ magBodySigma = dt*3.0e-4f;
+ for (uint8_t i= 0; i<=9; i++) processNoise[i] = 1.0e-9f;
+ for (uint8_t i=10; i<=12; i++) processNoise[i] = dAngBiasSigma;
+ if (onGround) processNoise[12] = dAngBiasSigma * yawVarScale;
+ for (uint8_t i=13; i<=14; i++) processNoise[i] = windVelSigma;
+ for (uint8_t i=15; i<=17; i++) processNoise[i] = magEarthSigma;
+ for (uint8_t i=18; i<=20; i++) processNoise[i] = magBodySigma;
+ for (uint8_t i= 0; i<=20; i++) processNoise[i] = sq(processNoise[i]);
+
+ // set variables used to calculate covariance growth
+ dvx = summedDelVel.x;
+ dvy = summedDelVel.y;
+ dvz = summedDelVel.z;
+ dax = summedDelAng.x;
+ day = summedDelAng.y;
+ daz = summedDelAng.z;
+ q0 = states[0];
+ q1 = states[1];
+ q2 = states[2];
+ q3 = states[3];
+ dax_b = states[10];
+ day_b = states[11];
+ daz_b = states[12];
+ daxCov = sq(dt*1.4544411e-2f);
+ dayCov = sq(dt*1.4544411e-2f);
+ dazCov = sq(dt*1.4544411e-2f);
+ if (onGround) dazCov = dazCov * sq(yawVarScale);
+ dvxCov = sq(dt*0.5f);
+ dvyCov = sq(dt*0.5f);
+ dvzCov = sq(dt*0.5f);
+
+ // Predicted covariance calculation
+ SF[0] = 2*dvx*q1 + 2*dvy*q2 + 2*dvz*q3;
+ SF[1] = 2*dvx*q3 + 2*dvy*q0 - 2*dvz*q1;
+ SF[2] = 2*dvx*q0 - 2*dvy*q3 + 2*dvz*q2;
+ SF[3] = day/2 - day_b/2;
+ SF[4] = daz/2 - daz_b/2;
+ SF[5] = dax/2 - dax_b/2;
+ SF[6] = dax_b/2 - dax/2;
+ SF[7] = daz_b/2 - daz/2;
+ SF[8] = day_b/2 - day/2;
+ SF[9] = q1/2;
+ SF[10] = q2/2;
+ SF[11] = q3/2;
+ SF[12] = 2*dvz*q0;
+ SF[13] = 2*dvy*q1;
+
+ SG[0] = q0/2;
+ SG[1] = sq(q3);
+ SG[2] = sq(q2);
+ SG[3] = sq(q1);
+ SG[4] = sq(q0);
+ SG[5] = 2*q2*q3;
+ SG[6] = 2*q1*q3;
+ SG[7] = 2*q1*q2;
+
+ SQ[0] = dvzCov*(SG[5] - 2*q0*q1)*(SG[1] - SG[2] - SG[3] + SG[4]) - dvyCov*(SG[5] + 2*q0*q1)*(SG[1] - SG[2] + SG[3] - SG[4]) + dvxCov*(SG[6] - 2*q0*q2)*(SG[7] + 2*q0*q3);
+ SQ[1] = dvzCov*(SG[6] + 2*q0*q2)*(SG[1] - SG[2] - SG[3] + SG[4]) - dvxCov*(SG[6] - 2*q0*q2)*(SG[1] + SG[2] - SG[3] - SG[4]) + dvyCov*(SG[5] + 2*q0*q1)*(SG[7] - 2*q0*q3);
+ SQ[2] = dvzCov*(SG[5] - 2*q0*q1)*(SG[6] + 2*q0*q2) - dvyCov*(SG[7] - 2*q0*q3)*(SG[1] - SG[2] + SG[3] - SG[4]) - dvxCov*(SG[7] + 2*q0*q3)*(SG[1] + SG[2] - SG[3] - SG[4]);
+ SQ[3] = (dayCov*q1*SG[0])/2 - (dazCov*q1*SG[0])/2 - (daxCov*q2*q3)/4;
+ SQ[4] = (dazCov*q2*SG[0])/2 - (daxCov*q2*SG[0])/2 - (dayCov*q1*q3)/4;
+ SQ[5] = (daxCov*q3*SG[0])/2 - (dayCov*q3*SG[0])/2 - (dazCov*q1*q2)/4;
+ SQ[6] = (daxCov*q1*q2)/4 - (dazCov*q3*SG[0])/2 - (dayCov*q1*q2)/4;
+ SQ[7] = (dazCov*q1*q3)/4 - (daxCov*q1*q3)/4 - (dayCov*q2*SG[0])/2;
+ SQ[8] = (dayCov*q2*q3)/4 - (daxCov*q1*SG[0])/2 - (dazCov*q2*q3)/4;
+ SQ[9] = sq(SG[0]);
+ SQ[10] = sq(q1);
+
+ SPP[0] = SF[12] + SF[13] - 2*dvx*q2;
+ SPP[1] = 2*dvx*q0 - 2*dvy*q3 + 2*dvz*q2;
+ SPP[2] = 2*dvx*q3 + 2*dvy*q0 - 2*dvz*q1;
+ SPP[3] = SF[11];
+ SPP[4] = SF[10];
+ SPP[5] = SF[9];
+ SPP[6] = SF[7];
+ SPP[7] = SF[8];
+
+ nextP[0][0] = P[0][0] + P[1][0]*SF[6] + P[2][0]*SPP[7] + P[3][0]*SPP[6] + P[10][0]*SPP[5] + P[11][0]*SPP[4] + P[12][0]*SPP[3] + (daxCov*SQ[10])/4 + SF[6]*(P[0][1] + P[1][1]*SF[6] + P[2][1]*SPP[7] + P[3][1]*SPP[6] + P[10][1]*SPP[5] + P[11][1]*SPP[4] + P[12][1]*SPP[3]) + SPP[7]*(P[0][2] + P[1][2]*SF[6] + P[2][2]*SPP[7] + P[3][2]*SPP[6] + P[10][2]*SPP[5] + P[11][2]*SPP[4] + P[12][2]*SPP[3]) + SPP[6]*(P[0][3] + P[1][3]*SF[6] + P[2][3]*SPP[7] + P[3][3]*SPP[6] + P[10][3]*SPP[5] + P[11][3]*SPP[4] + P[12][3]*SPP[3]) + SPP[5]*(P[0][10] + P[1][10]*SF[6] + P[2][10]*SPP[7] + P[3][10]*SPP[6] + P[10][10]*SPP[5] + P[11][10]*SPP[4] + P[12][10]*SPP[3]) + SPP[4]*(P[0][11] + P[1][11]*SF[6] + P[2][11]*SPP[7] + P[3][11]*SPP[6] + P[10][11]*SPP[5] + P[11][11]*SPP[4] + P[12][11]*SPP[3]) + SPP[3]*(P[0][12] + P[1][12]*SF[6] + P[2][12]*SPP[7] + P[3][12]*SPP[6] + P[10][12]*SPP[5] + P[11][12]*SPP[4] + P[12][12]*SPP[3]) + (dayCov*sq(q2))/4 + (dazCov*sq(q3))/4;
+ nextP[0][1] = P[0][1] + SQ[8] + P[1][1]*SF[6] + P[2][1]*SPP[7] + P[3][1]*SPP[6] + P[10][1]*SPP[5] + P[11][1]*SPP[4] + P[12][1]*SPP[3] + SF[5]*(P[0][0] + P[1][0]*SF[6] + P[2][0]*SPP[7] + P[3][0]*SPP[6] + P[10][0]*SPP[5] + P[11][0]*SPP[4] + P[12][0]*SPP[3]) + SF[4]*(P[0][2] + P[1][2]*SF[6] + P[2][2]*SPP[7] + P[3][2]*SPP[6] + P[10][2]*SPP[5] + P[11][2]*SPP[4] + P[12][2]*SPP[3]) + SPP[7]*(P[0][3] + P[1][3]*SF[6] + P[2][3]*SPP[7] + P[3][3]*SPP[6] + P[10][3]*SPP[5] + P[11][3]*SPP[4] + P[12][3]*SPP[3]) + SPP[3]*(P[0][11] + P[1][11]*SF[6] + P[2][11]*SPP[7] + P[3][11]*SPP[6] + P[10][11]*SPP[5] + P[11][11]*SPP[4] + P[12][11]*SPP[3]) - SPP[4]*(P[0][12] + P[1][12]*SF[6] + P[2][12]*SPP[7] + P[3][12]*SPP[6] + P[10][12]*SPP[5] + P[11][12]*SPP[4] + P[12][12]*SPP[3]) - (q0*(P[0][10] + P[1][10]*SF[6] + P[2][10]*SPP[7] + P[3][10]*SPP[6] + P[10][10]*SPP[5] + P[11][10]*SPP[4] + P[12][10]*SPP[3]))/2;
+ nextP[0][2] = P[0][2] + SQ[7] + P[1][2]*SF[6] + P[2][2]*SPP[7] + P[3][2]*SPP[6] + P[10][2]*SPP[5] + P[11][2]*SPP[4] + P[12][2]*SPP[3] + SF[3]*(P[0][0] + P[1][0]*SF[6] + P[2][0]*SPP[7] + P[3][0]*SPP[6] + P[10][0]*SPP[5] + P[11][0]*SPP[4] + P[12][0]*SPP[3]) + SF[5]*(P[0][3] + P[1][3]*SF[6] + P[2][3]*SPP[7] + P[3][3]*SPP[6] + P[10][3]*SPP[5] + P[11][3]*SPP[4] + P[12][3]*SPP[3]) + SPP[6]*(P[0][1] + P[1][1]*SF[6] + P[2][1]*SPP[7] + P[3][1]*SPP[6] + P[10][1]*SPP[5] + P[11][1]*SPP[4] + P[12][1]*SPP[3]) - SPP[3]*(P[0][10] + P[1][10]*SF[6] + P[2][10]*SPP[7] + P[3][10]*SPP[6] + P[10][10]*SPP[5] + P[11][10]*SPP[4] + P[12][10]*SPP[3]) + SPP[5]*(P[0][12] + P[1][12]*SF[6] + P[2][12]*SPP[7] + P[3][12]*SPP[6] + P[10][12]*SPP[5] + P[11][12]*SPP[4] + P[12][12]*SPP[3]) - (q0*(P[0][11] + P[1][11]*SF[6] + P[2][11]*SPP[7] + P[3][11]*SPP[6] + P[10][11]*SPP[5] + P[11][11]*SPP[4] + P[12][11]*SPP[3]))/2;
+ nextP[0][3] = P[0][3] + SQ[6] + P[1][3]*SF[6] + P[2][3]*SPP[7] + P[3][3]*SPP[6] + P[10][3]*SPP[5] + P[11][3]*SPP[4] + P[12][3]*SPP[3] + SF[4]*(P[0][0] + P[1][0]*SF[6] + P[2][0]*SPP[7] + P[3][0]*SPP[6] + P[10][0]*SPP[5] + P[11][0]*SPP[4] + P[12][0]*SPP[3]) + SF[3]*(P[0][1] + P[1][1]*SF[6] + P[2][1]*SPP[7] + P[3][1]*SPP[6] + P[10][1]*SPP[5] + P[11][1]*SPP[4] + P[12][1]*SPP[3]) + SF[6]*(P[0][2] + P[1][2]*SF[6] + P[2][2]*SPP[7] + P[3][2]*SPP[6] + P[10][2]*SPP[5] + P[11][2]*SPP[4] + P[12][2]*SPP[3]) + SPP[4]*(P[0][10] + P[1][10]*SF[6] + P[2][10]*SPP[7] + P[3][10]*SPP[6] + P[10][10]*SPP[5] + P[11][10]*SPP[4] + P[12][10]*SPP[3]) - SPP[5]*(P[0][11] + P[1][11]*SF[6] + P[2][11]*SPP[7] + P[3][11]*SPP[6] + P[10][11]*SPP[5] + P[11][11]*SPP[4] + P[12][11]*SPP[3]) - (q0*(P[0][12] + P[1][12]*SF[6] + P[2][12]*SPP[7] + P[3][12]*SPP[6] + P[10][12]*SPP[5] + P[11][12]*SPP[4] + P[12][12]*SPP[3]))/2;
+ nextP[0][4] = P[0][4] + P[1][4]*SF[6] + P[2][4]*SPP[7] + P[3][4]*SPP[6] + P[10][4]*SPP[5] + P[11][4]*SPP[4] + P[12][4]*SPP[3] + SF[2]*(P[0][0] + P[1][0]*SF[6] + P[2][0]*SPP[7] + P[3][0]*SPP[6] + P[10][0]*SPP[5] + P[11][0]*SPP[4] + P[12][0]*SPP[3]) + SF[0]*(P[0][1] + P[1][1]*SF[6] + P[2][1]*SPP[7] + P[3][1]*SPP[6] + P[10][1]*SPP[5] + P[11][1]*SPP[4] + P[12][1]*SPP[3]) + SPP[0]*(P[0][2] + P[1][2]*SF[6] + P[2][2]*SPP[7] + P[3][2]*SPP[6] + P[10][2]*SPP[5] + P[11][2]*SPP[4] + P[12][2]*SPP[3]) - SPP[2]*(P[0][3] + P[1][3]*SF[6] + P[2][3]*SPP[7] + P[3][3]*SPP[6] + P[10][3]*SPP[5] + P[11][3]*SPP[4] + P[12][3]*SPP[3]);
+ nextP[0][5] = P[0][5] + P[1][5]*SF[6] + P[2][5]*SPP[7] + P[3][5]*SPP[6] + P[10][5]*SPP[5] + P[11][5]*SPP[4] + P[12][5]*SPP[3] + SF[1]*(P[0][0] + P[1][0]*SF[6] + P[2][0]*SPP[7] + P[3][0]*SPP[6] + P[10][0]*SPP[5] + P[11][0]*SPP[4] + P[12][0]*SPP[3]) + SF[0]*(P[0][2] + P[1][2]*SF[6] + P[2][2]*SPP[7] + P[3][2]*SPP[6] + P[10][2]*SPP[5] + P[11][2]*SPP[4] + P[12][2]*SPP[3]) + SF[2]*(P[0][3] + P[1][3]*SF[6] + P[2][3]*SPP[7] + P[3][3]*SPP[6] + P[10][3]*SPP[5] + P[11][3]*SPP[4] + P[12][3]*SPP[3]) - SPP[0]*(P[0][1] + P[1][1]*SF[6] + P[2][1]*SPP[7] + P[3][1]*SPP[6] + P[10][1]*SPP[5] + P[11][1]*SPP[4] + P[12][1]*SPP[3]);
+ nextP[0][6] = P[0][6] + P[1][6]*SF[6] + P[2][6]*SPP[7] + P[3][6]*SPP[6] + P[10][6]*SPP[5] + P[11][6]*SPP[4] + P[12][6]*SPP[3] + SF[1]*(P[0][1] + P[1][1]*SF[6] + P[2][1]*SPP[7] + P[3][1]*SPP[6] + P[10][1]*SPP[5] + P[11][1]*SPP[4] + P[12][1]*SPP[3]) + SF[0]*(P[0][3] + P[1][3]*SF[6] + P[2][3]*SPP[7] + P[3][3]*SPP[6] + P[10][3]*SPP[5] + P[11][3]*SPP[4] + P[12][3]*SPP[3]) + SPP[0]*(P[0][0] + P[1][0]*SF[6] + P[2][0]*SPP[7] + P[3][0]*SPP[6] + P[10][0]*SPP[5] + P[11][0]*SPP[4] + P[12][0]*SPP[3]) - SPP[1]*(P[0][2] + P[1][2]*SF[6] + P[2][2]*SPP[7] + P[3][2]*SPP[6] + P[10][2]*SPP[5] + P[11][2]*SPP[4] + P[12][2]*SPP[3]);
+ nextP[0][7] = P[0][7] + P[1][7]*SF[6] + P[2][7]*SPP[7] + P[3][7]*SPP[6] + P[10][7]*SPP[5] + P[11][7]*SPP[4] + P[12][7]*SPP[3] + dt*(P[0][4] + P[1][4]*SF[6] + P[2][4]*SPP[7] + P[3][4]*SPP[6] + P[10][4]*SPP[5] + P[11][4]*SPP[4] + P[12][4]*SPP[3]);
+ nextP[0][8] = P[0][8] + P[1][8]*SF[6] + P[2][8]*SPP[7] + P[3][8]*SPP[6] + P[10][8]*SPP[5] + P[11][8]*SPP[4] + P[12][8]*SPP[3] + dt*(P[0][5] + P[1][5]*SF[6] + P[2][5]*SPP[7] + P[3][5]*SPP[6] + P[10][5]*SPP[5] + P[11][5]*SPP[4] + P[12][5]*SPP[3]);
+ nextP[0][9] = P[0][9] + P[1][9]*SF[6] + P[2][9]*SPP[7] + P[3][9]*SPP[6] + P[10][9]*SPP[5] + P[11][9]*SPP[4] + P[12][9]*SPP[3] + dt*(P[0][6] + P[1][6]*SF[6] + P[2][6]*SPP[7] + P[3][6]*SPP[6] + P[10][6]*SPP[5] + P[11][6]*SPP[4] + P[12][6]*SPP[3]);
+ nextP[0][10] = P[0][10] + P[1][10]*SF[6] + P[2][10]*SPP[7] + P[3][10]*SPP[6] + P[10][10]*SPP[5] + P[11][10]*SPP[4] + P[12][10]*SPP[3];
+ nextP[0][11] = P[0][11] + P[1][11]*SF[6] + P[2][11]*SPP[7] + P[3][11]*SPP[6] + P[10][11]*SPP[5] + P[11][11]*SPP[4] + P[12][11]*SPP[3];
+ nextP[0][12] = P[0][12] + P[1][12]*SF[6] + P[2][12]*SPP[7] + P[3][12]*SPP[6] + P[10][12]*SPP[5] + P[11][12]*SPP[4] + P[12][12]*SPP[3];
+ nextP[0][13] = P[0][13] + P[1][13]*SF[6] + P[2][13]*SPP[7] + P[3][13]*SPP[6] + P[10][13]*SPP[5] + P[11][13]*SPP[4] + P[12][13]*SPP[3];
+ nextP[0][14] = P[0][14] + P[1][14]*SF[6] + P[2][14]*SPP[7] + P[3][14]*SPP[6] + P[10][14]*SPP[5] + P[11][14]*SPP[4] + P[12][14]*SPP[3];
+ nextP[0][15] = P[0][15] + P[1][15]*SF[6] + P[2][15]*SPP[7] + P[3][15]*SPP[6] + P[10][15]*SPP[5] + P[11][15]*SPP[4] + P[12][15]*SPP[3];
+ nextP[0][16] = P[0][16] + P[1][16]*SF[6] + P[2][16]*SPP[7] + P[3][16]*SPP[6] + P[10][16]*SPP[5] + P[11][16]*SPP[4] + P[12][16]*SPP[3];
+ nextP[0][17] = P[0][17] + P[1][17]*SF[6] + P[2][17]*SPP[7] + P[3][17]*SPP[6] + P[10][17]*SPP[5] + P[11][17]*SPP[4] + P[12][17]*SPP[3];
+ nextP[0][18] = P[0][18] + P[1][18]*SF[6] + P[2][18]*SPP[7] + P[3][18]*SPP[6] + P[10][18]*SPP[5] + P[11][18]*SPP[4] + P[12][18]*SPP[3];
+ nextP[0][19] = P[0][19] + P[1][19]*SF[6] + P[2][19]*SPP[7] + P[3][19]*SPP[6] + P[10][19]*SPP[5] + P[11][19]*SPP[4] + P[12][19]*SPP[3];
+ nextP[0][20] = P[0][20] + P[1][20]*SF[6] + P[2][20]*SPP[7] + P[3][20]*SPP[6] + P[10][20]*SPP[5] + P[11][20]*SPP[4] + P[12][20]*SPP[3];
+ nextP[1][0] = P[1][0] + SQ[8] + P[0][0]*SF[5] + P[2][0]*SF[4] + P[3][0]*SPP[7] + P[11][0]*SPP[3] - P[12][0]*SPP[4] - (P[10][0]*q0)/2 + SF[6]*(P[1][1] + P[0][1]*SF[5] + P[2][1]*SF[4] + P[3][1]*SPP[7] + P[11][1]*SPP[3] - P[12][1]*SPP[4] - (P[10][1]*q0)/2) + SPP[7]*(P[1][2] + P[0][2]*SF[5] + P[2][2]*SF[4] + P[3][2]*SPP[7] + P[11][2]*SPP[3] - P[12][2]*SPP[4] - (P[10][2]*q0)/2) + SPP[6]*(P[1][3] + P[0][3]*SF[5] + P[2][3]*SF[4] + P[3][3]*SPP[7] + P[11][3]*SPP[3] - P[12][3]*SPP[4] - (P[10][3]*q0)/2) + SPP[5]*(P[1][10] + P[0][10]*SF[5] + P[2][10]*SF[4] + P[3][10]*SPP[7] + P[11][10]*SPP[3] - P[12][10]*SPP[4] - (P[10][10]*q0)/2) + SPP[4]*(P[1][11] + P[0][11]*SF[5] + P[2][11]*SF[4] + P[3][11]*SPP[7] + P[11][11]*SPP[3] - P[12][11]*SPP[4] - (P[10][11]*q0)/2) + SPP[3]*(P[1][12] + P[0][12]*SF[5] + P[2][12]*SF[4] + P[3][12]*SPP[7] + P[11][12]*SPP[3] - P[12][12]*SPP[4] - (P[10][12]*q0)/2);
+ nextP[1][1] = P[1][1] + P[0][1]*SF[5] + P[2][1]*SF[4] + P[3][1]*SPP[7] + P[11][1]*SPP[3] - P[12][1]*SPP[4] + daxCov*SQ[9] - (P[10][1]*q0)/2 + SF[5]*(P[1][0] + P[0][0]*SF[5] + P[2][0]*SF[4] + P[3][0]*SPP[7] + P[11][0]*SPP[3] - P[12][0]*SPP[4] - (P[10][0]*q0)/2) + SF[4]*(P[1][2] + P[0][2]*SF[5] + P[2][2]*SF[4] + P[3][2]*SPP[7] + P[11][2]*SPP[3] - P[12][2]*SPP[4] - (P[10][2]*q0)/2) + SPP[7]*(P[1][3] + P[0][3]*SF[5] + P[2][3]*SF[4] + P[3][3]*SPP[7] + P[11][3]*SPP[3] - P[12][3]*SPP[4] - (P[10][3]*q0)/2) + SPP[3]*(P[1][11] + P[0][11]*SF[5] + P[2][11]*SF[4] + P[3][11]*SPP[7] + P[11][11]*SPP[3] - P[12][11]*SPP[4] - (P[10][11]*q0)/2) - SPP[4]*(P[1][12] + P[0][12]*SF[5] + P[2][12]*SF[4] + P[3][12]*SPP[7] + P[11][12]*SPP[3] - P[12][12]*SPP[4] - (P[10][12]*q0)/2) + (dayCov*sq(q3))/4 + (dazCov*sq(q2))/4 - (q0*(P[1][10] + P[0][10]*SF[5] + P[2][10]*SF[4] + P[3][10]*SPP[7] + P[11][10]*SPP[3] - P[12][10]*SPP[4] - (P[10][10]*q0)/2))/2;
+ nextP[1][2] = P[1][2] + SQ[5] + P[0][2]*SF[5] + P[2][2]*SF[4] + P[3][2]*SPP[7] + P[11][2]*SPP[3] - P[12][2]*SPP[4] - (P[10][2]*q0)/2 + SF[3]*(P[1][0] + P[0][0]*SF[5] + P[2][0]*SF[4] + P[3][0]*SPP[7] + P[11][0]*SPP[3] - P[12][0]*SPP[4] - (P[10][0]*q0)/2) + SF[5]*(P[1][3] + P[0][3]*SF[5] + P[2][3]*SF[4] + P[3][3]*SPP[7] + P[11][3]*SPP[3] - P[12][3]*SPP[4] - (P[10][3]*q0)/2) + SPP[6]*(P[1][1] + P[0][1]*SF[5] + P[2][1]*SF[4] + P[3][1]*SPP[7] + P[11][1]*SPP[3] - P[12][1]*SPP[4] - (P[10][1]*q0)/2) - SPP[3]*(P[1][10] + P[0][10]*SF[5] + P[2][10]*SF[4] + P[3][10]*SPP[7] + P[11][10]*SPP[3] - P[12][10]*SPP[4] - (P[10][10]*q0)/2) + SPP[5]*(P[1][12] + P[0][12]*SF[5] + P[2][12]*SF[4] + P[3][12]*SPP[7] + P[11][12]*SPP[3] - P[12][12]*SPP[4] - (P[10][12]*q0)/2) - (q0*(P[1][11] + P[0][11]*SF[5] + P[2][11]*SF[4] + P[3][11]*SPP[7] + P[11][11]*SPP[3] - P[12][11]*SPP[4] - (P[10][11]*q0)/2))/2;
+ nextP[1][3] = P[1][3] + SQ[4] + P[0][3]*SF[5] + P[2][3]*SF[4] + P[3][3]*SPP[7] + P[11][3]*SPP[3] - P[12][3]*SPP[4] - (P[10][3]*q0)/2 + SF[4]*(P[1][0] + P[0][0]*SF[5] + P[2][0]*SF[4] + P[3][0]*SPP[7] + P[11][0]*SPP[3] - P[12][0]*SPP[4] - (P[10][0]*q0)/2) + SF[3]*(P[1][1] + P[0][1]*SF[5] + P[2][1]*SF[4] + P[3][1]*SPP[7] + P[11][1]*SPP[3] - P[12][1]*SPP[4] - (P[10][1]*q0)/2) + SF[6]*(P[1][2] + P[0][2]*SF[5] + P[2][2]*SF[4] + P[3][2]*SPP[7] + P[11][2]*SPP[3] - P[12][2]*SPP[4] - (P[10][2]*q0)/2) + SPP[4]*(P[1][10] + P[0][10]*SF[5] + P[2][10]*SF[4] + P[3][10]*SPP[7] + P[11][10]*SPP[3] - P[12][10]*SPP[4] - (P[10][10]*q0)/2) - SPP[5]*(P[1][11] + P[0][11]*SF[5] + P[2][11]*SF[4] + P[3][11]*SPP[7] + P[11][11]*SPP[3] - P[12][11]*SPP[4] - (P[10][11]*q0)/2) - (q0*(P[1][12] + P[0][12]*SF[5] + P[2][12]*SF[4] + P[3][12]*SPP[7] + P[11][12]*SPP[3] - P[12][12]*SPP[4] - (P[10][12]*q0)/2))/2;
+ nextP[1][4] = P[1][4] + P[0][4]*SF[5] + P[2][4]*SF[4] + P[3][4]*SPP[7] + P[11][4]*SPP[3] - P[12][4]*SPP[4] - (P[10][4]*q0)/2 + SF[2]*(P[1][0] + P[0][0]*SF[5] + P[2][0]*SF[4] + P[3][0]*SPP[7] + P[11][0]*SPP[3] - P[12][0]*SPP[4] - (P[10][0]*q0)/2) + SF[0]*(P[1][1] + P[0][1]*SF[5] + P[2][1]*SF[4] + P[3][1]*SPP[7] + P[11][1]*SPP[3] - P[12][1]*SPP[4] - (P[10][1]*q0)/2) + SPP[0]*(P[1][2] + P[0][2]*SF[5] + P[2][2]*SF[4] + P[3][2]*SPP[7] + P[11][2]*SPP[3] - P[12][2]*SPP[4] - (P[10][2]*q0)/2) - SPP[2]*(P[1][3] + P[0][3]*SF[5] + P[2][3]*SF[4] + P[3][3]*SPP[7] + P[11][3]*SPP[3] - P[12][3]*SPP[4] - (P[10][3]*q0)/2);
+ nextP[1][5] = P[1][5] + P[0][5]*SF[5] + P[2][5]*SF[4] + P[3][5]*SPP[7] + P[11][5]*SPP[3] - P[12][5]*SPP[4] - (P[10][5]*q0)/2 + SF[1]*(P[1][0] + P[0][0]*SF[5] + P[2][0]*SF[4] + P[3][0]*SPP[7] + P[11][0]*SPP[3] - P[12][0]*SPP[4] - (P[10][0]*q0)/2) + SF[0]*(P[1][2] + P[0][2]*SF[5] + P[2][2]*SF[4] + P[3][2]*SPP[7] + P[11][2]*SPP[3] - P[12][2]*SPP[4] - (P[10][2]*q0)/2) + SF[2]*(P[1][3] + P[0][3]*SF[5] + P[2][3]*SF[4] + P[3][3]*SPP[7] + P[11][3]*SPP[3] - P[12][3]*SPP[4] - (P[10][3]*q0)/2) - SPP[0]*(P[1][1] + P[0][1]*SF[5] + P[2][1]*SF[4] + P[3][1]*SPP[7] + P[11][1]*SPP[3] - P[12][1]*SPP[4] - (P[10][1]*q0)/2);
+ nextP[1][6] = P[1][6] + P[0][6]*SF[5] + P[2][6]*SF[4] + P[3][6]*SPP[7] + P[11][6]*SPP[3] - P[12][6]*SPP[4] - (P[10][6]*q0)/2 + SF[1]*(P[1][1] + P[0][1]*SF[5] + P[2][1]*SF[4] + P[3][1]*SPP[7] + P[11][1]*SPP[3] - P[12][1]*SPP[4] - (P[10][1]*q0)/2) + SF[0]*(P[1][3] + P[0][3]*SF[5] + P[2][3]*SF[4] + P[3][3]*SPP[7] + P[11][3]*SPP[3] - P[12][3]*SPP[4] - (P[10][3]*q0)/2) + SPP[0]*(P[1][0] + P[0][0]*SF[5] + P[2][0]*SF[4] + P[3][0]*SPP[7] + P[11][0]*SPP[3] - P[12][0]*SPP[4] - (P[10][0]*q0)/2) - SPP[1]*(P[1][2] + P[0][2]*SF[5] + P[2][2]*SF[4] + P[3][2]*SPP[7] + P[11][2]*SPP[3] - P[12][2]*SPP[4] - (P[10][2]*q0)/2);
+ nextP[1][7] = P[1][7] + P[0][7]*SF[5] + P[2][7]*SF[4] + P[3][7]*SPP[7] + P[11][7]*SPP[3] - P[12][7]*SPP[4] - (P[10][7]*q0)/2 + dt*(P[1][4] + P[0][4]*SF[5] + P[2][4]*SF[4] + P[3][4]*SPP[7] + P[11][4]*SPP[3] - P[12][4]*SPP[4] - (P[10][4]*q0)/2);
+ nextP[1][8] = P[1][8] + P[0][8]*SF[5] + P[2][8]*SF[4] + P[3][8]*SPP[7] + P[11][8]*SPP[3] - P[12][8]*SPP[4] - (P[10][8]*q0)/2 + dt*(P[1][5] + P[0][5]*SF[5] + P[2][5]*SF[4] + P[3][5]*SPP[7] + P[11][5]*SPP[3] - P[12][5]*SPP[4] - (P[10][5]*q0)/2);
+ nextP[1][9] = P[1][9] + P[0][9]*SF[5] + P[2][9]*SF[4] + P[3][9]*SPP[7] + P[11][9]*SPP[3] - P[12][9]*SPP[4] - (P[10][9]*q0)/2 + dt*(P[1][6] + P[0][6]*SF[5] + P[2][6]*SF[4] + P[3][6]*SPP[7] + P[11][6]*SPP[3] - P[12][6]*SPP[4] - (P[10][6]*q0)/2);
+ nextP[1][10] = P[1][10] + P[0][10]*SF[5] + P[2][10]*SF[4] + P[3][10]*SPP[7] + P[11][10]*SPP[3] - P[12][10]*SPP[4] - (P[10][10]*q0)/2;
+ nextP[1][11] = P[1][11] + P[0][11]*SF[5] + P[2][11]*SF[4] + P[3][11]*SPP[7] + P[11][11]*SPP[3] - P[12][11]*SPP[4] - (P[10][11]*q0)/2;
+ nextP[1][12] = P[1][12] + P[0][12]*SF[5] + P[2][12]*SF[4] + P[3][12]*SPP[7] + P[11][12]*SPP[3] - P[12][12]*SPP[4] - (P[10][12]*q0)/2;
+ nextP[1][13] = P[1][13] + P[0][13]*SF[5] + P[2][13]*SF[4] + P[3][13]*SPP[7] + P[11][13]*SPP[3] - P[12][13]*SPP[4] - (P[10][13]*q0)/2;
+ nextP[1][14] = P[1][14] + P[0][14]*SF[5] + P[2][14]*SF[4] + P[3][14]*SPP[7] + P[11][14]*SPP[3] - P[12][14]*SPP[4] - (P[10][14]*q0)/2;
+ nextP[1][15] = P[1][15] + P[0][15]*SF[5] + P[2][15]*SF[4] + P[3][15]*SPP[7] + P[11][15]*SPP[3] - P[12][15]*SPP[4] - (P[10][15]*q0)/2;
+ nextP[1][16] = P[1][16] + P[0][16]*SF[5] + P[2][16]*SF[4] + P[3][16]*SPP[7] + P[11][16]*SPP[3] - P[12][16]*SPP[4] - (P[10][16]*q0)/2;
+ nextP[1][17] = P[1][17] + P[0][17]*SF[5] + P[2][17]*SF[4] + P[3][17]*SPP[7] + P[11][17]*SPP[3] - P[12][17]*SPP[4] - (P[10][17]*q0)/2;
+ nextP[1][18] = P[1][18] + P[0][18]*SF[5] + P[2][18]*SF[4] + P[3][18]*SPP[7] + P[11][18]*SPP[3] - P[12][18]*SPP[4] - (P[10][18]*q0)/2;
+ nextP[1][19] = P[1][19] + P[0][19]*SF[5] + P[2][19]*SF[4] + P[3][19]*SPP[7] + P[11][19]*SPP[3] - P[12][19]*SPP[4] - (P[10][19]*q0)/2;
+ nextP[1][20] = P[1][20] + P[0][20]*SF[5] + P[2][20]*SF[4] + P[3][20]*SPP[7] + P[11][20]*SPP[3] - P[12][20]*SPP[4] - (P[10][20]*q0)/2;
+ nextP[2][0] = P[2][0] + SQ[7] + P[0][0]*SF[3] + P[3][0]*SF[5] + P[1][0]*SPP[6] - P[10][0]*SPP[3] + P[12][0]*SPP[5] - (P[11][0]*q0)/2 + SF[6]*(P[2][1] + P[0][1]*SF[3] + P[3][1]*SF[5] + P[1][1]*SPP[6] - P[10][1]*SPP[3] + P[12][1]*SPP[5] - (P[11][1]*q0)/2) + SPP[7]*(P[2][2] + P[0][2]*SF[3] + P[3][2]*SF[5] + P[1][2]*SPP[6] - P[10][2]*SPP[3] + P[12][2]*SPP[5] - (P[11][2]*q0)/2) + SPP[6]*(P[2][3] + P[0][3]*SF[3] + P[3][3]*SF[5] + P[1][3]*SPP[6] - P[10][3]*SPP[3] + P[12][3]*SPP[5] - (P[11][3]*q0)/2) + SPP[5]*(P[2][10] + P[0][10]*SF[3] + P[3][10]*SF[5] + P[1][10]*SPP[6] - P[10][10]*SPP[3] + P[12][10]*SPP[5] - (P[11][10]*q0)/2) + SPP[4]*(P[2][11] + P[0][11]*SF[3] + P[3][11]*SF[5] + P[1][11]*SPP[6] - P[10][11]*SPP[3] + P[12][11]*SPP[5] - (P[11][11]*q0)/2) + SPP[3]*(P[2][12] + P[0][12]*SF[3] + P[3][12]*SF[5] + P[1][12]*SPP[6] - P[10][12]*SPP[3] + P[12][12]*SPP[5] - (P[11][12]*q0)/2);
+ nextP[2][1] = P[2][1] + SQ[5] + P[0][1]*SF[3] + P[3][1]*SF[5] + P[1][1]*SPP[6] - P[10][1]*SPP[3] + P[12][1]*SPP[5] - (P[11][1]*q0)/2 + SF[5]*(P[2][0] + P[0][0]*SF[3] + P[3][0]*SF[5] + P[1][0]*SPP[6] - P[10][0]*SPP[3] + P[12][0]*SPP[5] - (P[11][0]*q0)/2) + SF[4]*(P[2][2] + P[0][2]*SF[3] + P[3][2]*SF[5] + P[1][2]*SPP[6] - P[10][2]*SPP[3] + P[12][2]*SPP[5] - (P[11][2]*q0)/2) + SPP[7]*(P[2][3] + P[0][3]*SF[3] + P[3][3]*SF[5] + P[1][3]*SPP[6] - P[10][3]*SPP[3] + P[12][3]*SPP[5] - (P[11][3]*q0)/2) + SPP[3]*(P[2][11] + P[0][11]*SF[3] + P[3][11]*SF[5] + P[1][11]*SPP[6] - P[10][11]*SPP[3] + P[12][11]*SPP[5] - (P[11][11]*q0)/2) - SPP[4]*(P[2][12] + P[0][12]*SF[3] + P[3][12]*SF[5] + P[1][12]*SPP[6] - P[10][12]*SPP[3] + P[12][12]*SPP[5] - (P[11][12]*q0)/2) - (q0*(P[2][10] + P[0][10]*SF[3] + P[3][10]*SF[5] + P[1][10]*SPP[6] - P[10][10]*SPP[3] + P[12][10]*SPP[5] - (P[11][10]*q0)/2))/2;
+ nextP[2][2] = P[2][2] + P[0][2]*SF[3] + P[3][2]*SF[5] + P[1][2]*SPP[6] - P[10][2]*SPP[3] + P[12][2]*SPP[5] + dayCov*SQ[9] + (dazCov*SQ[10])/4 - (P[11][2]*q0)/2 + SF[3]*(P[2][0] + P[0][0]*SF[3] + P[3][0]*SF[5] + P[1][0]*SPP[6] - P[10][0]*SPP[3] + P[12][0]*SPP[5] - (P[11][0]*q0)/2) + SF[5]*(P[2][3] + P[0][3]*SF[3] + P[3][3]*SF[5] + P[1][3]*SPP[6] - P[10][3]*SPP[3] + P[12][3]*SPP[5] - (P[11][3]*q0)/2) + SPP[6]*(P[2][1] + P[0][1]*SF[3] + P[3][1]*SF[5] + P[1][1]*SPP[6] - P[10][1]*SPP[3] + P[12][1]*SPP[5] - (P[11][1]*q0)/2) - SPP[3]*(P[2][10] + P[0][10]*SF[3] + P[3][10]*SF[5] + P[1][10]*SPP[6] - P[10][10]*SPP[3] + P[12][10]*SPP[5] - (P[11][10]*q0)/2) + SPP[5]*(P[2][12] + P[0][12]*SF[3] + P[3][12]*SF[5] + P[1][12]*SPP[6] - P[10][12]*SPP[3] + P[12][12]*SPP[5] - (P[11][12]*q0)/2) + (daxCov*sq(q3))/4 - (q0*(P[2][11] + P[0][11]*SF[3] + P[3][11]*SF[5] + P[1][11]*SPP[6] - P[10][11]*SPP[3] + P[12][11]*SPP[5] - (P[11][11]*q0)/2))/2;
+ nextP[2][3] = P[2][3] + SQ[3] + P[0][3]*SF[3] + P[3][3]*SF[5] + P[1][3]*SPP[6] - P[10][3]*SPP[3] + P[12][3]*SPP[5] - (P[11][3]*q0)/2 + SF[4]*(P[2][0] + P[0][0]*SF[3] + P[3][0]*SF[5] + P[1][0]*SPP[6] - P[10][0]*SPP[3] + P[12][0]*SPP[5] - (P[11][0]*q0)/2) + SF[3]*(P[2][1] + P[0][1]*SF[3] + P[3][1]*SF[5] + P[1][1]*SPP[6] - P[10][1]*SPP[3] + P[12][1]*SPP[5] - (P[11][1]*q0)/2) + SF[6]*(P[2][2] + P[0][2]*SF[3] + P[3][2]*SF[5] + P[1][2]*SPP[6] - P[10][2]*SPP[3] + P[12][2]*SPP[5] - (P[11][2]*q0)/2) + SPP[4]*(P[2][10] + P[0][10]*SF[3] + P[3][10]*SF[5] + P[1][10]*SPP[6] - P[10][10]*SPP[3] + P[12][10]*SPP[5] - (P[11][10]*q0)/2) - SPP[5]*(P[2][11] + P[0][11]*SF[3] + P[3][11]*SF[5] + P[1][11]*SPP[6] - P[10][11]*SPP[3] + P[12][11]*SPP[5] - (P[11][11]*q0)/2) - (q0*(P[2][12] + P[0][12]*SF[3] + P[3][12]*SF[5] + P[1][12]*SPP[6] - P[10][12]*SPP[3] + P[12][12]*SPP[5] - (P[11][12]*q0)/2))/2;
+ nextP[2][4] = P[2][4] + P[0][4]*SF[3] + P[3][4]*SF[5] + P[1][4]*SPP[6] - P[10][4]*SPP[3] + P[12][4]*SPP[5] - (P[11][4]*q0)/2 + SF[2]*(P[2][0] + P[0][0]*SF[3] + P[3][0]*SF[5] + P[1][0]*SPP[6] - P[10][0]*SPP[3] + P[12][0]*SPP[5] - (P[11][0]*q0)/2) + SF[0]*(P[2][1] + P[0][1]*SF[3] + P[3][1]*SF[5] + P[1][1]*SPP[6] - P[10][1]*SPP[3] + P[12][1]*SPP[5] - (P[11][1]*q0)/2) + SPP[0]*(P[2][2] + P[0][2]*SF[3] + P[3][2]*SF[5] + P[1][2]*SPP[6] - P[10][2]*SPP[3] + P[12][2]*SPP[5] - (P[11][2]*q0)/2) - SPP[2]*(P[2][3] + P[0][3]*SF[3] + P[3][3]*SF[5] + P[1][3]*SPP[6] - P[10][3]*SPP[3] + P[12][3]*SPP[5] - (P[11][3]*q0)/2);
+ nextP[2][5] = P[2][5] + P[0][5]*SF[3] + P[3][5]*SF[5] + P[1][5]*SPP[6] - P[10][5]*SPP[3] + P[12][5]*SPP[5] - (P[11][5]*q0)/2 + SF[1]*(P[2][0] + P[0][0]*SF[3] + P[3][0]*SF[5] + P[1][0]*SPP[6] - P[10][0]*SPP[3] + P[12][0]*SPP[5] - (P[11][0]*q0)/2) + SF[0]*(P[2][2] + P[0][2]*SF[3] + P[3][2]*SF[5] + P[1][2]*SPP[6] - P[10][2]*SPP[3] + P[12][2]*SPP[5] - (P[11][2]*q0)/2) + SF[2]*(P[2][3] + P[0][3]*SF[3] + P[3][3]*SF[5] + P[1][3]*SPP[6] - P[10][3]*SPP[3] + P[12][3]*SPP[5] - (P[11][3]*q0)/2) - SPP[0]*(P[2][1] + P[0][1]*SF[3] + P[3][1]*SF[5] + P[1][1]*SPP[6] - P[10][1]*SPP[3] + P[12][1]*SPP[5] - (P[11][1]*q0)/2);
+ nextP[2][6] = P[2][6] + P[0][6]*SF[3] + P[3][6]*SF[5] + P[1][6]*SPP[6] - P[10][6]*SPP[3] + P[12][6]*SPP[5] - (P[11][6]*q0)/2 + SF[1]*(P[2][1] + P[0][1]*SF[3] + P[3][1]*SF[5] + P[1][1]*SPP[6] - P[10][1]*SPP[3] + P[12][1]*SPP[5] - (P[11][1]*q0)/2) + SF[0]*(P[2][3] + P[0][3]*SF[3] + P[3][3]*SF[5] + P[1][3]*SPP[6] - P[10][3]*SPP[3] + P[12][3]*SPP[5] - (P[11][3]*q0)/2) + SPP[0]*(P[2][0] + P[0][0]*SF[3] + P[3][0]*SF[5] + P[1][0]*SPP[6] - P[10][0]*SPP[3] + P[12][0]*SPP[5] - (P[11][0]*q0)/2) - SPP[1]*(P[2][2] + P[0][2]*SF[3] + P[3][2]*SF[5] + P[1][2]*SPP[6] - P[10][2]*SPP[3] + P[12][2]*SPP[5] - (P[11][2]*q0)/2);
+ nextP[2][7] = P[2][7] + P[0][7]*SF[3] + P[3][7]*SF[5] + P[1][7]*SPP[6] - P[10][7]*SPP[3] + P[12][7]*SPP[5] - (P[11][7]*q0)/2 + dt*(P[2][4] + P[0][4]*SF[3] + P[3][4]*SF[5] + P[1][4]*SPP[6] - P[10][4]*SPP[3] + P[12][4]*SPP[5] - (P[11][4]*q0)/2);
+ nextP[2][8] = P[2][8] + P[0][8]*SF[3] + P[3][8]*SF[5] + P[1][8]*SPP[6] - P[10][8]*SPP[3] + P[12][8]*SPP[5] - (P[11][8]*q0)/2 + dt*(P[2][5] + P[0][5]*SF[3] + P[3][5]*SF[5] + P[1][5]*SPP[6] - P[10][5]*SPP[3] + P[12][5]*SPP[5] - (P[11][5]*q0)/2);
+ nextP[2][9] = P[2][9] + P[0][9]*SF[3] + P[3][9]*SF[5] + P[1][9]*SPP[6] - P[10][9]*SPP[3] + P[12][9]*SPP[5] - (P[11][9]*q0)/2 + dt*(P[2][6] + P[0][6]*SF[3] + P[3][6]*SF[5] + P[1][6]*SPP[6] - P[10][6]*SPP[3] + P[12][6]*SPP[5] - (P[11][6]*q0)/2);
+ nextP[2][10] = P[2][10] + P[0][10]*SF[3] + P[3][10]*SF[5] + P[1][10]*SPP[6] - P[10][10]*SPP[3] + P[12][10]*SPP[5] - (P[11][10]*q0)/2;
+ nextP[2][11] = P[2][11] + P[0][11]*SF[3] + P[3][11]*SF[5] + P[1][11]*SPP[6] - P[10][11]*SPP[3] + P[12][11]*SPP[5] - (P[11][11]*q0)/2;
+ nextP[2][12] = P[2][12] + P[0][12]*SF[3] + P[3][12]*SF[5] + P[1][12]*SPP[6] - P[10][12]*SPP[3] + P[12][12]*SPP[5] - (P[11][12]*q0)/2;
+ nextP[2][13] = P[2][13] + P[0][13]*SF[3] + P[3][13]*SF[5] + P[1][13]*SPP[6] - P[10][13]*SPP[3] + P[12][13]*SPP[5] - (P[11][13]*q0)/2;
+ nextP[2][14] = P[2][14] + P[0][14]*SF[3] + P[3][14]*SF[5] + P[1][14]*SPP[6] - P[10][14]*SPP[3] + P[12][14]*SPP[5] - (P[11][14]*q0)/2;
+ nextP[2][15] = P[2][15] + P[0][15]*SF[3] + P[3][15]*SF[5] + P[1][15]*SPP[6] - P[10][15]*SPP[3] + P[12][15]*SPP[5] - (P[11][15]*q0)/2;
+ nextP[2][16] = P[2][16] + P[0][16]*SF[3] + P[3][16]*SF[5] + P[1][16]*SPP[6] - P[10][16]*SPP[3] + P[12][16]*SPP[5] - (P[11][16]*q0)/2;
+ nextP[2][17] = P[2][17] + P[0][17]*SF[3] + P[3][17]*SF[5] + P[1][17]*SPP[6] - P[10][17]*SPP[3] + P[12][17]*SPP[5] - (P[11][17]*q0)/2;
+ nextP[2][18] = P[2][18] + P[0][18]*SF[3] + P[3][18]*SF[5] + P[1][18]*SPP[6] - P[10][18]*SPP[3] + P[12][18]*SPP[5] - (P[11][18]*q0)/2;
+ nextP[2][19] = P[2][19] + P[0][19]*SF[3] + P[3][19]*SF[5] + P[1][19]*SPP[6] - P[10][19]*SPP[3] + P[12][19]*SPP[5] - (P[11][19]*q0)/2;
+ nextP[2][20] = P[2][20] + P[0][20]*SF[3] + P[3][20]*SF[5] + P[1][20]*SPP[6] - P[10][20]*SPP[3] + P[12][20]*SPP[5] - (P[11][20]*q0)/2;
+ nextP[3][0] = P[3][0] + SQ[6] + P[0][0]*SF[4] + P[1][0]*SF[3] + P[2][0]*SF[6] + P[10][0]*SPP[4] - P[11][0]*SPP[5] - (P[12][0]*q0)/2 + SF[6]*(P[3][1] + P[0][1]*SF[4] + P[1][1]*SF[3] + P[2][1]*SF[6] + P[10][1]*SPP[4] - P[11][1]*SPP[5] - (P[12][1]*q0)/2) + SPP[7]*(P[3][2] + P[0][2]*SF[4] + P[1][2]*SF[3] + P[2][2]*SF[6] + P[10][2]*SPP[4] - P[11][2]*SPP[5] - (P[12][2]*q0)/2) + SPP[6]*(P[3][3] + P[0][3]*SF[4] + P[1][3]*SF[3] + P[2][3]*SF[6] + P[10][3]*SPP[4] - P[11][3]*SPP[5] - (P[12][3]*q0)/2) + SPP[5]*(P[3][10] + P[0][10]*SF[4] + P[1][10]*SF[3] + P[2][10]*SF[6] + P[10][10]*SPP[4] - P[11][10]*SPP[5] - (P[12][10]*q0)/2) + SPP[4]*(P[3][11] + P[0][11]*SF[4] + P[1][11]*SF[3] + P[2][11]*SF[6] + P[10][11]*SPP[4] - P[11][11]*SPP[5] - (P[12][11]*q0)/2) + SPP[3]*(P[3][12] + P[0][12]*SF[4] + P[1][12]*SF[3] + P[2][12]*SF[6] + P[10][12]*SPP[4] - P[11][12]*SPP[5] - (P[12][12]*q0)/2);
+ nextP[3][1] = P[3][1] + SQ[4] + P[0][1]*SF[4] + P[1][1]*SF[3] + P[2][1]*SF[6] + P[10][1]*SPP[4] - P[11][1]*SPP[5] - (P[12][1]*q0)/2 + SF[5]*(P[3][0] + P[0][0]*SF[4] + P[1][0]*SF[3] + P[2][0]*SF[6] + P[10][0]*SPP[4] - P[11][0]*SPP[5] - (P[12][0]*q0)/2) + SF[4]*(P[3][2] + P[0][2]*SF[4] + P[1][2]*SF[3] + P[2][2]*SF[6] + P[10][2]*SPP[4] - P[11][2]*SPP[5] - (P[12][2]*q0)/2) + SPP[7]*(P[3][3] + P[0][3]*SF[4] + P[1][3]*SF[3] + P[2][3]*SF[6] + P[10][3]*SPP[4] - P[11][3]*SPP[5] - (P[12][3]*q0)/2) + SPP[3]*(P[3][11] + P[0][11]*SF[4] + P[1][11]*SF[3] + P[2][11]*SF[6] + P[10][11]*SPP[4] - P[11][11]*SPP[5] - (P[12][11]*q0)/2) - SPP[4]*(P[3][12] + P[0][12]*SF[4] + P[1][12]*SF[3] + P[2][12]*SF[6] + P[10][12]*SPP[4] - P[11][12]*SPP[5] - (P[12][12]*q0)/2) - (q0*(P[3][10] + P[0][10]*SF[4] + P[1][10]*SF[3] + P[2][10]*SF[6] + P[10][10]*SPP[4] - P[11][10]*SPP[5] - (P[12][10]*q0)/2))/2;
+ nextP[3][2] = P[3][2] + SQ[3] + P[0][2]*SF[4] + P[1][2]*SF[3] + P[2][2]*SF[6] + P[10][2]*SPP[4] - P[11][2]*SPP[5] - (P[12][2]*q0)/2 + SF[3]*(P[3][0] + P[0][0]*SF[4] + P[1][0]*SF[3] + P[2][0]*SF[6] + P[10][0]*SPP[4] - P[11][0]*SPP[5] - (P[12][0]*q0)/2) + SF[5]*(P[3][3] + P[0][3]*SF[4] + P[1][3]*SF[3] + P[2][3]*SF[6] + P[10][3]*SPP[4] - P[11][3]*SPP[5] - (P[12][3]*q0)/2) + SPP[6]*(P[3][1] + P[0][1]*SF[4] + P[1][1]*SF[3] + P[2][1]*SF[6] + P[10][1]*SPP[4] - P[11][1]*SPP[5] - (P[12][1]*q0)/2) - SPP[3]*(P[3][10] + P[0][10]*SF[4] + P[1][10]*SF[3] + P[2][10]*SF[6] + P[10][10]*SPP[4] - P[11][10]*SPP[5] - (P[12][10]*q0)/2) + SPP[5]*(P[3][12] + P[0][12]*SF[4] + P[1][12]*SF[3] + P[2][12]*SF[6] + P[10][12]*SPP[4] - P[11][12]*SPP[5] - (P[12][12]*q0)/2) - (q0*(P[3][11] + P[0][11]*SF[4] + P[1][11]*SF[3] + P[2][11]*SF[6] + P[10][11]*SPP[4] - P[11][11]*SPP[5] - (P[12][11]*q0)/2))/2;
+ nextP[3][3] = P[3][3] + P[0][3]*SF[4] + P[1][3]*SF[3] + P[2][3]*SF[6] + P[10][3]*SPP[4] - P[11][3]*SPP[5] + (dayCov*SQ[10])/4 + dazCov*SQ[9] - (P[12][3]*q0)/2 + SF[4]*(P[3][0] + P[0][0]*SF[4] + P[1][0]*SF[3] + P[2][0]*SF[6] + P[10][0]*SPP[4] - P[11][0]*SPP[5] - (P[12][0]*q0)/2) + SF[3]*(P[3][1] + P[0][1]*SF[4] + P[1][1]*SF[3] + P[2][1]*SF[6] + P[10][1]*SPP[4] - P[11][1]*SPP[5] - (P[12][1]*q0)/2) + SF[6]*(P[3][2] + P[0][2]*SF[4] + P[1][2]*SF[3] + P[2][2]*SF[6] + P[10][2]*SPP[4] - P[11][2]*SPP[5] - (P[12][2]*q0)/2) + SPP[4]*(P[3][10] + P[0][10]*SF[4] + P[1][10]*SF[3] + P[2][10]*SF[6] + P[10][10]*SPP[4] - P[11][10]*SPP[5] - (P[12][10]*q0)/2) - SPP[5]*(P[3][11] + P[0][11]*SF[4] + P[1][11]*SF[3] + P[2][11]*SF[6] + P[10][11]*SPP[4] - P[11][11]*SPP[5] - (P[12][11]*q0)/2) + (daxCov*sq(q2))/4 - (q0*(P[3][12] + P[0][12]*SF[4] + P[1][12]*SF[3] + P[2][12]*SF[6] + P[10][12]*SPP[4] - P[11][12]*SPP[5] - (P[12][12]*q0)/2))/2;
+ nextP[3][4] = P[3][4] + P[0][4]*SF[4] + P[1][4]*SF[3] + P[2][4]*SF[6] + P[10][4]*SPP[4] - P[11][4]*SPP[5] - (P[12][4]*q0)/2 + SF[2]*(P[3][0] + P[0][0]*SF[4] + P[1][0]*SF[3] + P[2][0]*SF[6] + P[10][0]*SPP[4] - P[11][0]*SPP[5] - (P[12][0]*q0)/2) + SF[0]*(P[3][1] + P[0][1]*SF[4] + P[1][1]*SF[3] + P[2][1]*SF[6] + P[10][1]*SPP[4] - P[11][1]*SPP[5] - (P[12][1]*q0)/2) + SPP[0]*(P[3][2] + P[0][2]*SF[4] + P[1][2]*SF[3] + P[2][2]*SF[6] + P[10][2]*SPP[4] - P[11][2]*SPP[5] - (P[12][2]*q0)/2) - SPP[2]*(P[3][3] + P[0][3]*SF[4] + P[1][3]*SF[3] + P[2][3]*SF[6] + P[10][3]*SPP[4] - P[11][3]*SPP[5] - (P[12][3]*q0)/2);
+ nextP[3][5] = P[3][5] + P[0][5]*SF[4] + P[1][5]*SF[3] + P[2][5]*SF[6] + P[10][5]*SPP[4] - P[11][5]*SPP[5] - (P[12][5]*q0)/2 + SF[1]*(P[3][0] + P[0][0]*SF[4] + P[1][0]*SF[3] + P[2][0]*SF[6] + P[10][0]*SPP[4] - P[11][0]*SPP[5] - (P[12][0]*q0)/2) + SF[0]*(P[3][2] + P[0][2]*SF[4] + P[1][2]*SF[3] + P[2][2]*SF[6] + P[10][2]*SPP[4] - P[11][2]*SPP[5] - (P[12][2]*q0)/2) + SF[2]*(P[3][3] + P[0][3]*SF[4] + P[1][3]*SF[3] + P[2][3]*SF[6] + P[10][3]*SPP[4] - P[11][3]*SPP[5] - (P[12][3]*q0)/2) - SPP[0]*(P[3][1] + P[0][1]*SF[4] + P[1][1]*SF[3] + P[2][1]*SF[6] + P[10][1]*SPP[4] - P[11][1]*SPP[5] - (P[12][1]*q0)/2);
+ nextP[3][6] = P[3][6] + P[0][6]*SF[4] + P[1][6]*SF[3] + P[2][6]*SF[6] + P[10][6]*SPP[4] - P[11][6]*SPP[5] - (P[12][6]*q0)/2 + SF[1]*(P[3][1] + P[0][1]*SF[4] + P[1][1]*SF[3] + P[2][1]*SF[6] + P[10][1]*SPP[4] - P[11][1]*SPP[5] - (P[12][1]*q0)/2) + SF[0]*(P[3][3] + P[0][3]*SF[4] + P[1][3]*SF[3] + P[2][3]*SF[6] + P[10][3]*SPP[4] - P[11][3]*SPP[5] - (P[12][3]*q0)/2) + SPP[0]*(P[3][0] + P[0][0]*SF[4] + P[1][0]*SF[3] + P[2][0]*SF[6] + P[10][0]*SPP[4] - P[11][0]*SPP[5] - (P[12][0]*q0)/2) - SPP[1]*(P[3][2] + P[0][2]*SF[4] + P[1][2]*SF[3] + P[2][2]*SF[6] + P[10][2]*SPP[4] - P[11][2]*SPP[5] - (P[12][2]*q0)/2);
+ nextP[3][7] = P[3][7] + P[0][7]*SF[4] + P[1][7]*SF[3] + P[2][7]*SF[6] + P[10][7]*SPP[4] - P[11][7]*SPP[5] - (P[12][7]*q0)/2 + dt*(P[3][4] + P[0][4]*SF[4] + P[1][4]*SF[3] + P[2][4]*SF[6] + P[10][4]*SPP[4] - P[11][4]*SPP[5] - (P[12][4]*q0)/2);
+ nextP[3][8] = P[3][8] + P[0][8]*SF[4] + P[1][8]*SF[3] + P[2][8]*SF[6] + P[10][8]*SPP[4] - P[11][8]*SPP[5] - (P[12][8]*q0)/2 + dt*(P[3][5] + P[0][5]*SF[4] + P[1][5]*SF[3] + P[2][5]*SF[6] + P[10][5]*SPP[4] - P[11][5]*SPP[5] - (P[12][5]*q0)/2);
+ nextP[3][9] = P[3][9] + P[0][9]*SF[4] + P[1][9]*SF[3] + P[2][9]*SF[6] + P[10][9]*SPP[4] - P[11][9]*SPP[5] - (P[12][9]*q0)/2 + dt*(P[3][6] + P[0][6]*SF[4] + P[1][6]*SF[3] + P[2][6]*SF[6] + P[10][6]*SPP[4] - P[11][6]*SPP[5] - (P[12][6]*q0)/2);
+ nextP[3][10] = P[3][10] + P[0][10]*SF[4] + P[1][10]*SF[3] + P[2][10]*SF[6] + P[10][10]*SPP[4] - P[11][10]*SPP[5] - (P[12][10]*q0)/2;
+ nextP[3][11] = P[3][11] + P[0][11]*SF[4] + P[1][11]*SF[3] + P[2][11]*SF[6] + P[10][11]*SPP[4] - P[11][11]*SPP[5] - (P[12][11]*q0)/2;
+ nextP[3][12] = P[3][12] + P[0][12]*SF[4] + P[1][12]*SF[3] + P[2][12]*SF[6] + P[10][12]*SPP[4] - P[11][12]*SPP[5] - (P[12][12]*q0)/2;
+ nextP[3][13] = P[3][13] + P[0][13]*SF[4] + P[1][13]*SF[3] + P[2][13]*SF[6] + P[10][13]*SPP[4] - P[11][13]*SPP[5] - (P[12][13]*q0)/2;
+ nextP[3][14] = P[3][14] + P[0][14]*SF[4] + P[1][14]*SF[3] + P[2][14]*SF[6] + P[10][14]*SPP[4] - P[11][14]*SPP[5] - (P[12][14]*q0)/2;
+ nextP[3][15] = P[3][15] + P[0][15]*SF[4] + P[1][15]*SF[3] + P[2][15]*SF[6] + P[10][15]*SPP[4] - P[11][15]*SPP[5] - (P[12][15]*q0)/2;
+ nextP[3][16] = P[3][16] + P[0][16]*SF[4] + P[1][16]*SF[3] + P[2][16]*SF[6] + P[10][16]*SPP[4] - P[11][16]*SPP[5] - (P[12][16]*q0)/2;
+ nextP[3][17] = P[3][17] + P[0][17]*SF[4] + P[1][17]*SF[3] + P[2][17]*SF[6] + P[10][17]*SPP[4] - P[11][17]*SPP[5] - (P[12][17]*q0)/2;
+ nextP[3][18] = P[3][18] + P[0][18]*SF[4] + P[1][18]*SF[3] + P[2][18]*SF[6] + P[10][18]*SPP[4] - P[11][18]*SPP[5] - (P[12][18]*q0)/2;
+ nextP[3][19] = P[3][19] + P[0][19]*SF[4] + P[1][19]*SF[3] + P[2][19]*SF[6] + P[10][19]*SPP[4] - P[11][19]*SPP[5] - (P[12][19]*q0)/2;
+ nextP[3][20] = P[3][20] + P[0][20]*SF[4] + P[1][20]*SF[3] + P[2][20]*SF[6] + P[10][20]*SPP[4] - P[11][20]*SPP[5] - (P[12][20]*q0)/2;
+ nextP[4][0] = P[4][0] + P[0][0]*SF[2] + P[1][0]*SF[0] + P[2][0]*SPP[0] - P[3][0]*SPP[2] + SF[6]*(P[4][1] + P[0][1]*SF[2] + P[1][1]*SF[0] + P[2][1]*SPP[0] - P[3][1]*SPP[2]) + SPP[7]*(P[4][2] + P[0][2]*SF[2] + P[1][2]*SF[0] + P[2][2]*SPP[0] - P[3][2]*SPP[2]) + SPP[6]*(P[4][3] + P[0][3]*SF[2] + P[1][3]*SF[0] + P[2][3]*SPP[0] - P[3][3]*SPP[2]) + SPP[5]*(P[4][10] + P[0][10]*SF[2] + P[1][10]*SF[0] + P[2][10]*SPP[0] - P[3][10]*SPP[2]) + SPP[4]*(P[4][11] + P[0][11]*SF[2] + P[1][11]*SF[0] + P[2][11]*SPP[0] - P[3][11]*SPP[2]) + SPP[3]*(P[4][12] + P[0][12]*SF[2] + P[1][12]*SF[0] + P[2][12]*SPP[0] - P[3][12]*SPP[2]);
+ nextP[4][1] = P[4][1] + P[0][1]*SF[2] + P[1][1]*SF[0] + P[2][1]*SPP[0] - P[3][1]*SPP[2] + SF[5]*(P[4][0] + P[0][0]*SF[2] + P[1][0]*SF[0] + P[2][0]*SPP[0] - P[3][0]*SPP[2]) + SF[4]*(P[4][2] + P[0][2]*SF[2] + P[1][2]*SF[0] + P[2][2]*SPP[0] - P[3][2]*SPP[2]) + SPP[7]*(P[4][3] + P[0][3]*SF[2] + P[1][3]*SF[0] + P[2][3]*SPP[0] - P[3][3]*SPP[2]) + SPP[3]*(P[4][11] + P[0][11]*SF[2] + P[1][11]*SF[0] + P[2][11]*SPP[0] - P[3][11]*SPP[2]) - SPP[4]*(P[4][12] + P[0][12]*SF[2] + P[1][12]*SF[0] + P[2][12]*SPP[0] - P[3][12]*SPP[2]) - (q0*(P[4][10] + P[0][10]*SF[2] + P[1][10]*SF[0] + P[2][10]*SPP[0] - P[3][10]*SPP[2]))/2;
+ nextP[4][2] = P[4][2] + P[0][2]*SF[2] + P[1][2]*SF[0] + P[2][2]*SPP[0] - P[3][2]*SPP[2] + SF[3]*(P[4][0] + P[0][0]*SF[2] + P[1][0]*SF[0] + P[2][0]*SPP[0] - P[3][0]*SPP[2]) + SF[5]*(P[4][3] + P[0][3]*SF[2] + P[1][3]*SF[0] + P[2][3]*SPP[0] - P[3][3]*SPP[2]) + SPP[6]*(P[4][1] + P[0][1]*SF[2] + P[1][1]*SF[0] + P[2][1]*SPP[0] - P[3][1]*SPP[2]) - SPP[3]*(P[4][10] + P[0][10]*SF[2] + P[1][10]*SF[0] + P[2][10]*SPP[0] - P[3][10]*SPP[2]) + SPP[5]*(P[4][12] + P[0][12]*SF[2] + P[1][12]*SF[0] + P[2][12]*SPP[0] - P[3][12]*SPP[2]) - (q0*(P[4][11] + P[0][11]*SF[2] + P[1][11]*SF[0] + P[2][11]*SPP[0] - P[3][11]*SPP[2]))/2;
+ nextP[4][3] = P[4][3] + P[0][3]*SF[2] + P[1][3]*SF[0] + P[2][3]*SPP[0] - P[3][3]*SPP[2] + SF[4]*(P[4][0] + P[0][0]*SF[2] + P[1][0]*SF[0] + P[2][0]*SPP[0] - P[3][0]*SPP[2]) + SF[3]*(P[4][1] + P[0][1]*SF[2] + P[1][1]*SF[0] + P[2][1]*SPP[0] - P[3][1]*SPP[2]) + SF[6]*(P[4][2] + P[0][2]*SF[2] + P[1][2]*SF[0] + P[2][2]*SPP[0] - P[3][2]*SPP[2]) + SPP[4]*(P[4][10] + P[0][10]*SF[2] + P[1][10]*SF[0] + P[2][10]*SPP[0] - P[3][10]*SPP[2]) - SPP[5]*(P[4][11] + P[0][11]*SF[2] + P[1][11]*SF[0] + P[2][11]*SPP[0] - P[3][11]*SPP[2]) - (q0*(P[4][12] + P[0][12]*SF[2] + P[1][12]*SF[0] + P[2][12]*SPP[0] - P[3][12]*SPP[2]))/2;
+ nextP[4][4] = P[4][4] + P[0][4]*SF[2] + P[1][4]*SF[0] + P[2][4]*SPP[0] - P[3][4]*SPP[2] + dvyCov*sq(SG[7] - 2*q0*q3) + dvzCov*sq(SG[6] + 2*q0*q2) + SF[2]*(P[4][0] + P[0][0]*SF[2] + P[1][0]*SF[0] + P[2][0]*SPP[0] - P[3][0]*SPP[2]) + SF[0]*(P[4][1] + P[0][1]*SF[2] + P[1][1]*SF[0] + P[2][1]*SPP[0] - P[3][1]*SPP[2]) + SPP[0]*(P[4][2] + P[0][2]*SF[2] + P[1][2]*SF[0] + P[2][2]*SPP[0] - P[3][2]*SPP[2]) - SPP[2]*(P[4][3] + P[0][3]*SF[2] + P[1][3]*SF[0] + P[2][3]*SPP[0] - P[3][3]*SPP[2]) + dvxCov*sq(SG[1] + SG[2] - SG[3] - SG[4]);
+ nextP[4][5] = P[4][5] + SQ[2] + P[0][5]*SF[2] + P[1][5]*SF[0] + P[2][5]*SPP[0] - P[3][5]*SPP[2] + SF[1]*(P[4][0] + P[0][0]*SF[2] + P[1][0]*SF[0] + P[2][0]*SPP[0] - P[3][0]*SPP[2]) + SF[0]*(P[4][2] + P[0][2]*SF[2] + P[1][2]*SF[0] + P[2][2]*SPP[0] - P[3][2]*SPP[2]) + SF[2]*(P[4][3] + P[0][3]*SF[2] + P[1][3]*SF[0] + P[2][3]*SPP[0] - P[3][3]*SPP[2]) - SPP[0]*(P[4][1] + P[0][1]*SF[2] + P[1][1]*SF[0] + P[2][1]*SPP[0] - P[3][1]*SPP[2]);
+ nextP[4][6] = P[4][6] + SQ[1] + P[0][6]*SF[2] + P[1][6]*SF[0] + P[2][6]*SPP[0] - P[3][6]*SPP[2] + SF[1]*(P[4][1] + P[0][1]*SF[2] + P[1][1]*SF[0] + P[2][1]*SPP[0] - P[3][1]*SPP[2]) + SF[0]*(P[4][3] + P[0][3]*SF[2] + P[1][3]*SF[0] + P[2][3]*SPP[0] - P[3][3]*SPP[2]) + SPP[0]*(P[4][0] + P[0][0]*SF[2] + P[1][0]*SF[0] + P[2][0]*SPP[0] - P[3][0]*SPP[2]) - SPP[1]*(P[4][2] + P[0][2]*SF[2] + P[1][2]*SF[0] + P[2][2]*SPP[0] - P[3][2]*SPP[2]);
+ nextP[4][7] = P[4][7] + P[0][7]*SF[2] + P[1][7]*SF[0] + P[2][7]*SPP[0] - P[3][7]*SPP[2] + dt*(P[4][4] + P[0][4]*SF[2] + P[1][4]*SF[0] + P[2][4]*SPP[0] - P[3][4]*SPP[2]);
+ nextP[4][8] = P[4][8] + P[0][8]*SF[2] + P[1][8]*SF[0] + P[2][8]*SPP[0] - P[3][8]*SPP[2] + dt*(P[4][5] + P[0][5]*SF[2] + P[1][5]*SF[0] + P[2][5]*SPP[0] - P[3][5]*SPP[2]);
+ nextP[4][9] = P[4][9] + P[0][9]*SF[2] + P[1][9]*SF[0] + P[2][9]*SPP[0] - P[3][9]*SPP[2] + dt*(P[4][6] + P[0][6]*SF[2] + P[1][6]*SF[0] + P[2][6]*SPP[0] - P[3][6]*SPP[2]);
+ nextP[4][10] = P[4][10] + P[0][10]*SF[2] + P[1][10]*SF[0] + P[2][10]*SPP[0] - P[3][10]*SPP[2];
+ nextP[4][11] = P[4][11] + P[0][11]*SF[2] + P[1][11]*SF[0] + P[2][11]*SPP[0] - P[3][11]*SPP[2];
+ nextP[4][12] = P[4][12] + P[0][12]*SF[2] + P[1][12]*SF[0] + P[2][12]*SPP[0] - P[3][12]*SPP[2];
+ nextP[4][13] = P[4][13] + P[0][13]*SF[2] + P[1][13]*SF[0] + P[2][13]*SPP[0] - P[3][13]*SPP[2];
+ nextP[4][14] = P[4][14] + P[0][14]*SF[2] + P[1][14]*SF[0] + P[2][14]*SPP[0] - P[3][14]*SPP[2];
+ nextP[4][15] = P[4][15] + P[0][15]*SF[2] + P[1][15]*SF[0] + P[2][15]*SPP[0] - P[3][15]*SPP[2];
+ nextP[4][16] = P[4][16] + P[0][16]*SF[2] + P[1][16]*SF[0] + P[2][16]*SPP[0] - P[3][16]*SPP[2];
+ nextP[4][17] = P[4][17] + P[0][17]*SF[2] + P[1][17]*SF[0] + P[2][17]*SPP[0] - P[3][17]*SPP[2];
+ nextP[4][18] = P[4][18] + P[0][18]*SF[2] + P[1][18]*SF[0] + P[2][18]*SPP[0] - P[3][18]*SPP[2];
+ nextP[4][19] = P[4][19] + P[0][19]*SF[2] + P[1][19]*SF[0] + P[2][19]*SPP[0] - P[3][19]*SPP[2];
+ nextP[4][20] = P[4][20] + P[0][20]*SF[2] + P[1][20]*SF[0] + P[2][20]*SPP[0] - P[3][20]*SPP[2];
+ nextP[5][0] = P[5][0] + P[0][0]*SF[1] + P[2][0]*SF[0] + P[3][0]*SF[2] - P[1][0]*SPP[0] + SF[6]*(P[5][1] + P[0][1]*SF[1] + P[2][1]*SF[0] + P[3][1]*SF[2] - P[1][1]*SPP[0]) + SPP[7]*(P[5][2] + P[0][2]*SF[1] + P[2][2]*SF[0] + P[3][2]*SF[2] - P[1][2]*SPP[0]) + SPP[6]*(P[5][3] + P[0][3]*SF[1] + P[2][3]*SF[0] + P[3][3]*SF[2] - P[1][3]*SPP[0]) + SPP[5]*(P[5][10] + P[0][10]*SF[1] + P[2][10]*SF[0] + P[3][10]*SF[2] - P[1][10]*SPP[0]) + SPP[4]*(P[5][11] + P[0][11]*SF[1] + P[2][11]*SF[0] + P[3][11]*SF[2] - P[1][11]*SPP[0]) + SPP[3]*(P[5][12] + P[0][12]*SF[1] + P[2][12]*SF[0] + P[3][12]*SF[2] - P[1][12]*SPP[0]);
+ nextP[5][1] = P[5][1] + P[0][1]*SF[1] + P[2][1]*SF[0] + P[3][1]*SF[2] - P[1][1]*SPP[0] + SF[5]*(P[5][0] + P[0][0]*SF[1] + P[2][0]*SF[0] + P[3][0]*SF[2] - P[1][0]*SPP[0]) + SF[4]*(P[5][2] + P[0][2]*SF[1] + P[2][2]*SF[0] + P[3][2]*SF[2] - P[1][2]*SPP[0]) + SPP[7]*(P[5][3] + P[0][3]*SF[1] + P[2][3]*SF[0] + P[3][3]*SF[2] - P[1][3]*SPP[0]) + SPP[3]*(P[5][11] + P[0][11]*SF[1] + P[2][11]*SF[0] + P[3][11]*SF[2] - P[1][11]*SPP[0]) - SPP[4]*(P[5][12] + P[0][12]*SF[1] + P[2][12]*SF[0] + P[3][12]*SF[2] - P[1][12]*SPP[0]) - (q0*(P[5][10] + P[0][10]*SF[1] + P[2][10]*SF[0] + P[3][10]*SF[2] - P[1][10]*SPP[0]))/2;
+ nextP[5][2] = P[5][2] + P[0][2]*SF[1] + P[2][2]*SF[0] + P[3][2]*SF[2] - P[1][2]*SPP[0] + SF[3]*(P[5][0] + P[0][0]*SF[1] + P[2][0]*SF[0] + P[3][0]*SF[2] - P[1][0]*SPP[0]) + SF[5]*(P[5][3] + P[0][3]*SF[1] + P[2][3]*SF[0] + P[3][3]*SF[2] - P[1][3]*SPP[0]) + SPP[6]*(P[5][1] + P[0][1]*SF[1] + P[2][1]*SF[0] + P[3][1]*SF[2] - P[1][1]*SPP[0]) - SPP[3]*(P[5][10] + P[0][10]*SF[1] + P[2][10]*SF[0] + P[3][10]*SF[2] - P[1][10]*SPP[0]) + SPP[5]*(P[5][12] + P[0][12]*SF[1] + P[2][12]*SF[0] + P[3][12]*SF[2] - P[1][12]*SPP[0]) - (q0*(P[5][11] + P[0][11]*SF[1] + P[2][11]*SF[0] + P[3][11]*SF[2] - P[1][11]*SPP[0]))/2;
+ nextP[5][3] = P[5][3] + P[0][3]*SF[1] + P[2][3]*SF[0] + P[3][3]*SF[2] - P[1][3]*SPP[0] + SF[4]*(P[5][0] + P[0][0]*SF[1] + P[2][0]*SF[0] + P[3][0]*SF[2] - P[1][0]*SPP[0]) + SF[3]*(P[5][1] + P[0][1]*SF[1] + P[2][1]*SF[0] + P[3][1]*SF[2] - P[1][1]*SPP[0]) + SF[6]*(P[5][2] + P[0][2]*SF[1] + P[2][2]*SF[0] + P[3][2]*SF[2] - P[1][2]*SPP[0]) + SPP[4]*(P[5][10] + P[0][10]*SF[1] + P[2][10]*SF[0] + P[3][10]*SF[2] - P[1][10]*SPP[0]) - SPP[5]*(P[5][11] + P[0][11]*SF[1] + P[2][11]*SF[0] + P[3][11]*SF[2] - P[1][11]*SPP[0]) - (q0*(P[5][12] + P[0][12]*SF[1] + P[2][12]*SF[0] + P[3][12]*SF[2] - P[1][12]*SPP[0]))/2;
+ nextP[5][4] = P[5][4] + SQ[2] + P[0][4]*SF[1] + P[2][4]*SF[0] + P[3][4]*SF[2] - P[1][4]*SPP[0] + SF[2]*(P[5][0] + P[0][0]*SF[1] + P[2][0]*SF[0] + P[3][0]*SF[2] - P[1][0]*SPP[0]) + SF[0]*(P[5][1] + P[0][1]*SF[1] + P[2][1]*SF[0] + P[3][1]*SF[2] - P[1][1]*SPP[0]) + SPP[0]*(P[5][2] + P[0][2]*SF[1] + P[2][2]*SF[0] + P[3][2]*SF[2] - P[1][2]*SPP[0]) - SPP[2]*(P[5][3] + P[0][3]*SF[1] + P[2][3]*SF[0] + P[3][3]*SF[2] - P[1][3]*SPP[0]);
+ nextP[5][5] = P[5][5] + P[0][5]*SF[1] + P[2][5]*SF[0] + P[3][5]*SF[2] - P[1][5]*SPP[0] + dvxCov*sq(SG[7] + 2*q0*q3) + dvzCov*sq(SG[5] - 2*q0*q1) + SF[1]*(P[5][0] + P[0][0]*SF[1] + P[2][0]*SF[0] + P[3][0]*SF[2] - P[1][0]*SPP[0]) + SF[0]*(P[5][2] + P[0][2]*SF[1] + P[2][2]*SF[0] + P[3][2]*SF[2] - P[1][2]*SPP[0]) + SF[2]*(P[5][3] + P[0][3]*SF[1] + P[2][3]*SF[0] + P[3][3]*SF[2] - P[1][3]*SPP[0]) - SPP[0]*(P[5][1] + P[0][1]*SF[1] + P[2][1]*SF[0] + P[3][1]*SF[2] - P[1][1]*SPP[0]) + dvyCov*sq(SG[1] - SG[2] + SG[3] - SG[4]);
+ nextP[5][6] = P[5][6] + SQ[0] + P[0][6]*SF[1] + P[2][6]*SF[0] + P[3][6]*SF[2] - P[1][6]*SPP[0] + SF[1]*(P[5][1] + P[0][1]*SF[1] + P[2][1]*SF[0] + P[3][1]*SF[2] - P[1][1]*SPP[0]) + SF[0]*(P[5][3] + P[0][3]*SF[1] + P[2][3]*SF[0] + P[3][3]*SF[2] - P[1][3]*SPP[0]) + SPP[0]*(P[5][0] + P[0][0]*SF[1] + P[2][0]*SF[0] + P[3][0]*SF[2] - P[1][0]*SPP[0]) - SPP[1]*(P[5][2] + P[0][2]*SF[1] + P[2][2]*SF[0] + P[3][2]*SF[2] - P[1][2]*SPP[0]);
+ nextP[5][7] = P[5][7] + P[0][7]*SF[1] + P[2][7]*SF[0] + P[3][7]*SF[2] - P[1][7]*SPP[0] + dt*(P[5][4] + P[0][4]*SF[1] + P[2][4]*SF[0] + P[3][4]*SF[2] - P[1][4]*SPP[0]);
+ nextP[5][8] = P[5][8] + P[0][8]*SF[1] + P[2][8]*SF[0] + P[3][8]*SF[2] - P[1][8]*SPP[0] + dt*(P[5][5] + P[0][5]*SF[1] + P[2][5]*SF[0] + P[3][5]*SF[2] - P[1][5]*SPP[0]);
+ nextP[5][9] = P[5][9] + P[0][9]*SF[1] + P[2][9]*SF[0] + P[3][9]*SF[2] - P[1][9]*SPP[0] + dt*(P[5][6] + P[0][6]*SF[1] + P[2][6]*SF[0] + P[3][6]*SF[2] - P[1][6]*SPP[0]);
+ nextP[5][10] = P[5][10] + P[0][10]*SF[1] + P[2][10]*SF[0] + P[3][10]*SF[2] - P[1][10]*SPP[0];
+ nextP[5][11] = P[5][11] + P[0][11]*SF[1] + P[2][11]*SF[0] + P[3][11]*SF[2] - P[1][11]*SPP[0];
+ nextP[5][12] = P[5][12] + P[0][12]*SF[1] + P[2][12]*SF[0] + P[3][12]*SF[2] - P[1][12]*SPP[0];
+ nextP[5][13] = P[5][13] + P[0][13]*SF[1] + P[2][13]*SF[0] + P[3][13]*SF[2] - P[1][13]*SPP[0];
+ nextP[5][14] = P[5][14] + P[0][14]*SF[1] + P[2][14]*SF[0] + P[3][14]*SF[2] - P[1][14]*SPP[0];
+ nextP[5][15] = P[5][15] + P[0][15]*SF[1] + P[2][15]*SF[0] + P[3][15]*SF[2] - P[1][15]*SPP[0];
+ nextP[5][16] = P[5][16] + P[0][16]*SF[1] + P[2][16]*SF[0] + P[3][16]*SF[2] - P[1][16]*SPP[0];
+ nextP[5][17] = P[5][17] + P[0][17]*SF[1] + P[2][17]*SF[0] + P[3][17]*SF[2] - P[1][17]*SPP[0];
+ nextP[5][18] = P[5][18] + P[0][18]*SF[1] + P[2][18]*SF[0] + P[3][18]*SF[2] - P[1][18]*SPP[0];
+ nextP[5][19] = P[5][19] + P[0][19]*SF[1] + P[2][19]*SF[0] + P[3][19]*SF[2] - P[1][19]*SPP[0];
+ nextP[5][20] = P[5][20] + P[0][20]*SF[1] + P[2][20]*SF[0] + P[3][20]*SF[2] - P[1][20]*SPP[0];
+ nextP[6][0] = P[6][0] + P[1][0]*SF[1] + P[3][0]*SF[0] + P[0][0]*SPP[0] - P[2][0]*SPP[1] + SF[6]*(P[6][1] + P[1][1]*SF[1] + P[3][1]*SF[0] + P[0][1]*SPP[0] - P[2][1]*SPP[1]) + SPP[7]*(P[6][2] + P[1][2]*SF[1] + P[3][2]*SF[0] + P[0][2]*SPP[0] - P[2][2]*SPP[1]) + SPP[6]*(P[6][3] + P[1][3]*SF[1] + P[3][3]*SF[0] + P[0][3]*SPP[0] - P[2][3]*SPP[1]) + SPP[5]*(P[6][10] + P[1][10]*SF[1] + P[3][10]*SF[0] + P[0][10]*SPP[0] - P[2][10]*SPP[1]) + SPP[4]*(P[6][11] + P[1][11]*SF[1] + P[3][11]*SF[0] + P[0][11]*SPP[0] - P[2][11]*SPP[1]) + SPP[3]*(P[6][12] + P[1][12]*SF[1] + P[3][12]*SF[0] + P[0][12]*SPP[0] - P[2][12]*SPP[1]);
+ nextP[6][1] = P[6][1] + P[1][1]*SF[1] + P[3][1]*SF[0] + P[0][1]*SPP[0] - P[2][1]*SPP[1] + SF[5]*(P[6][0] + P[1][0]*SF[1] + P[3][0]*SF[0] + P[0][0]*SPP[0] - P[2][0]*SPP[1]) + SF[4]*(P[6][2] + P[1][2]*SF[1] + P[3][2]*SF[0] + P[0][2]*SPP[0] - P[2][2]*SPP[1]) + SPP[7]*(P[6][3] + P[1][3]*SF[1] + P[3][3]*SF[0] + P[0][3]*SPP[0] - P[2][3]*SPP[1]) + SPP[3]*(P[6][11] + P[1][11]*SF[1] + P[3][11]*SF[0] + P[0][11]*SPP[0] - P[2][11]*SPP[1]) - SPP[4]*(P[6][12] + P[1][12]*SF[1] + P[3][12]*SF[0] + P[0][12]*SPP[0] - P[2][12]*SPP[1]) - (q0*(P[6][10] + P[1][10]*SF[1] + P[3][10]*SF[0] + P[0][10]*SPP[0] - P[2][10]*SPP[1]))/2;
+ nextP[6][2] = P[6][2] + P[1][2]*SF[1] + P[3][2]*SF[0] + P[0][2]*SPP[0] - P[2][2]*SPP[1] + SF[3]*(P[6][0] + P[1][0]*SF[1] + P[3][0]*SF[0] + P[0][0]*SPP[0] - P[2][0]*SPP[1]) + SF[5]*(P[6][3] + P[1][3]*SF[1] + P[3][3]*SF[0] + P[0][3]*SPP[0] - P[2][3]*SPP[1]) + SPP[6]*(P[6][1] + P[1][1]*SF[1] + P[3][1]*SF[0] + P[0][1]*SPP[0] - P[2][1]*SPP[1]) - SPP[3]*(P[6][10] + P[1][10]*SF[1] + P[3][10]*SF[0] + P[0][10]*SPP[0] - P[2][10]*SPP[1]) + SPP[5]*(P[6][12] + P[1][12]*SF[1] + P[3][12]*SF[0] + P[0][12]*SPP[0] - P[2][12]*SPP[1]) - (q0*(P[6][11] + P[1][11]*SF[1] + P[3][11]*SF[0] + P[0][11]*SPP[0] - P[2][11]*SPP[1]))/2;
+ nextP[6][3] = P[6][3] + P[1][3]*SF[1] + P[3][3]*SF[0] + P[0][3]*SPP[0] - P[2][3]*SPP[1] + SF[4]*(P[6][0] + P[1][0]*SF[1] + P[3][0]*SF[0] + P[0][0]*SPP[0] - P[2][0]*SPP[1]) + SF[3]*(P[6][1] + P[1][1]*SF[1] + P[3][1]*SF[0] + P[0][1]*SPP[0] - P[2][1]*SPP[1]) + SF[6]*(P[6][2] + P[1][2]*SF[1] + P[3][2]*SF[0] + P[0][2]*SPP[0] - P[2][2]*SPP[1]) + SPP[4]*(P[6][10] + P[1][10]*SF[1] + P[3][10]*SF[0] + P[0][10]*SPP[0] - P[2][10]*SPP[1]) - SPP[5]*(P[6][11] + P[1][11]*SF[1] + P[3][11]*SF[0] + P[0][11]*SPP[0] - P[2][11]*SPP[1]) - (q0*(P[6][12] + P[1][12]*SF[1] + P[3][12]*SF[0] + P[0][12]*SPP[0] - P[2][12]*SPP[1]))/2;
+ nextP[6][4] = P[6][4] + SQ[1] + P[1][4]*SF[1] + P[3][4]*SF[0] + P[0][4]*SPP[0] - P[2][4]*SPP[1] + SF[2]*(P[6][0] + P[1][0]*SF[1] + P[3][0]*SF[0] + P[0][0]*SPP[0] - P[2][0]*SPP[1]) + SF[0]*(P[6][1] + P[1][1]*SF[1] + P[3][1]*SF[0] + P[0][1]*SPP[0] - P[2][1]*SPP[1]) + SPP[0]*(P[6][2] + P[1][2]*SF[1] + P[3][2]*SF[0] + P[0][2]*SPP[0] - P[2][2]*SPP[1]) - SPP[2]*(P[6][3] + P[1][3]*SF[1] + P[3][3]*SF[0] + P[0][3]*SPP[0] - P[2][3]*SPP[1]);
+ nextP[6][5] = P[6][5] + SQ[0] + P[1][5]*SF[1] + P[3][5]*SF[0] + P[0][5]*SPP[0] - P[2][5]*SPP[1] + SF[1]*(P[6][0] + P[1][0]*SF[1] + P[3][0]*SF[0] + P[0][0]*SPP[0] - P[2][0]*SPP[1]) + SF[0]*(P[6][2] + P[1][2]*SF[1] + P[3][2]*SF[0] + P[0][2]*SPP[0] - P[2][2]*SPP[1]) + SF[2]*(P[6][3] + P[1][3]*SF[1] + P[3][3]*SF[0] + P[0][3]*SPP[0] - P[2][3]*SPP[1]) - SPP[0]*(P[6][1] + P[1][1]*SF[1] + P[3][1]*SF[0] + P[0][1]*SPP[0] - P[2][1]*SPP[1]);
+ nextP[6][6] = P[6][6] + P[1][6]*SF[1] + P[3][6]*SF[0] + P[0][6]*SPP[0] - P[2][6]*SPP[1] + dvxCov*sq(SG[6] - 2*q0*q2) + dvyCov*sq(SG[5] + 2*q0*q1) + SF[1]*(P[6][1] + P[1][1]*SF[1] + P[3][1]*SF[0] + P[0][1]*SPP[0] - P[2][1]*SPP[1]) + SF[0]*(P[6][3] + P[1][3]*SF[1] + P[3][3]*SF[0] + P[0][3]*SPP[0] - P[2][3]*SPP[1]) + SPP[0]*(P[6][0] + P[1][0]*SF[1] + P[3][0]*SF[0] + P[0][0]*SPP[0] - P[2][0]*SPP[1]) - SPP[1]*(P[6][2] + P[1][2]*SF[1] + P[3][2]*SF[0] + P[0][2]*SPP[0] - P[2][2]*SPP[1]) + dvzCov*sq(SG[1] - SG[2] - SG[3] + SG[4]);
+ nextP[6][7] = P[6][7] + P[1][7]*SF[1] + P[3][7]*SF[0] + P[0][7]*SPP[0] - P[2][7]*SPP[1] + dt*(P[6][4] + P[1][4]*SF[1] + P[3][4]*SF[0] + P[0][4]*SPP[0] - P[2][4]*SPP[1]);
+ nextP[6][8] = P[6][8] + P[1][8]*SF[1] + P[3][8]*SF[0] + P[0][8]*SPP[0] - P[2][8]*SPP[1] + dt*(P[6][5] + P[1][5]*SF[1] + P[3][5]*SF[0] + P[0][5]*SPP[0] - P[2][5]*SPP[1]);
+ nextP[6][9] = P[6][9] + P[1][9]*SF[1] + P[3][9]*SF[0] + P[0][9]*SPP[0] - P[2][9]*SPP[1] + dt*(P[6][6] + P[1][6]*SF[1] + P[3][6]*SF[0] + P[0][6]*SPP[0] - P[2][6]*SPP[1]);
+ nextP[6][10] = P[6][10] + P[1][10]*SF[1] + P[3][10]*SF[0] + P[0][10]*SPP[0] - P[2][10]*SPP[1];
+ nextP[6][11] = P[6][11] + P[1][11]*SF[1] + P[3][11]*SF[0] + P[0][11]*SPP[0] - P[2][11]*SPP[1];
+ nextP[6][12] = P[6][12] + P[1][12]*SF[1] + P[3][12]*SF[0] + P[0][12]*SPP[0] - P[2][12]*SPP[1];
+ nextP[6][13] = P[6][13] + P[1][13]*SF[1] + P[3][13]*SF[0] + P[0][13]*SPP[0] - P[2][13]*SPP[1];
+ nextP[6][14] = P[6][14] + P[1][14]*SF[1] + P[3][14]*SF[0] + P[0][14]*SPP[0] - P[2][14]*SPP[1];
+ nextP[6][15] = P[6][15] + P[1][15]*SF[1] + P[3][15]*SF[0] + P[0][15]*SPP[0] - P[2][15]*SPP[1];
+ nextP[6][16] = P[6][16] + P[1][16]*SF[1] + P[3][16]*SF[0] + P[0][16]*SPP[0] - P[2][16]*SPP[1];
+ nextP[6][17] = P[6][17] + P[1][17]*SF[1] + P[3][17]*SF[0] + P[0][17]*SPP[0] - P[2][17]*SPP[1];
+ nextP[6][18] = P[6][18] + P[1][18]*SF[1] + P[3][18]*SF[0] + P[0][18]*SPP[0] - P[2][18]*SPP[1];
+ nextP[6][19] = P[6][19] + P[1][19]*SF[1] + P[3][19]*SF[0] + P[0][19]*SPP[0] - P[2][19]*SPP[1];
+ nextP[6][20] = P[6][20] + P[1][20]*SF[1] + P[3][20]*SF[0] + P[0][20]*SPP[0] - P[2][20]*SPP[1];
+ nextP[7][0] = P[7][0] + P[4][0]*dt + SF[6]*(P[7][1] + P[4][1]*dt) + SPP[7]*(P[7][2] + P[4][2]*dt) + SPP[6]*(P[7][3] + P[4][3]*dt) + SPP[5]*(P[7][10] + P[4][10]*dt) + SPP[4]*(P[7][11] + P[4][11]*dt) + SPP[3]*(P[7][12] + P[4][12]*dt);
+ nextP[7][1] = P[7][1] + P[4][1]*dt + SF[5]*(P[7][0] + P[4][0]*dt) + SF[4]*(P[7][2] + P[4][2]*dt) + SPP[7]*(P[7][3] + P[4][3]*dt) + SPP[3]*(P[7][11] + P[4][11]*dt) - SPP[4]*(P[7][12] + P[4][12]*dt) - (q0*(P[7][10] + P[4][10]*dt))/2;
+ nextP[7][2] = P[7][2] + P[4][2]*dt + SF[3]*(P[7][0] + P[4][0]*dt) + SF[5]*(P[7][3] + P[4][3]*dt) + SPP[6]*(P[7][1] + P[4][1]*dt) - SPP[3]*(P[7][10] + P[4][10]*dt) + SPP[5]*(P[7][12] + P[4][12]*dt) - (q0*(P[7][11] + P[4][11]*dt))/2;
+ nextP[7][3] = P[7][3] + P[4][3]*dt + SF[4]*(P[7][0] + P[4][0]*dt) + SF[3]*(P[7][1] + P[4][1]*dt) + SF[6]*(P[7][2] + P[4][2]*dt) + SPP[4]*(P[7][10] + P[4][10]*dt) - SPP[5]*(P[7][11] + P[4][11]*dt) - (q0*(P[7][12] + P[4][12]*dt))/2;
+ nextP[7][4] = P[7][4] + P[4][4]*dt + SF[0]*(P[7][1] + P[4][1]*dt) + SF[2]*(P[7][0] + P[4][0]*dt) + SPP[0]*(P[7][2] + P[4][2]*dt) - SPP[2]*(P[7][3] + P[4][3]*dt);
+ nextP[7][5] = P[7][5] + P[4][5]*dt + SF[1]*(P[7][0] + P[4][0]*dt) + SF[0]*(P[7][2] + P[4][2]*dt) + SF[2]*(P[7][3] + P[4][3]*dt) - SPP[0]*(P[7][1] + P[4][1]*dt);
+ nextP[7][6] = P[7][6] + P[4][6]*dt + SF[1]*(P[7][1] + P[4][1]*dt) + SF[0]*(P[7][3] + P[4][3]*dt) + SPP[0]*(P[7][0] + P[4][0]*dt) - SPP[1]*(P[7][2] + P[4][2]*dt);
+ nextP[7][7] = P[7][7] + P[4][7]*dt + dt*(P[7][4] + P[4][4]*dt);
+ nextP[7][8] = P[7][8] + P[4][8]*dt + dt*(P[7][5] + P[4][5]*dt);
+ nextP[7][9] = P[7][9] + P[4][9]*dt + dt*(P[7][6] + P[4][6]*dt);
+ nextP[7][10] = P[7][10] + P[4][10]*dt;
+ nextP[7][11] = P[7][11] + P[4][11]*dt;
+ nextP[7][12] = P[7][12] + P[4][12]*dt;
+ nextP[7][13] = P[7][13] + P[4][13]*dt;
+ nextP[7][14] = P[7][14] + P[4][14]*dt;
+ nextP[7][15] = P[7][15] + P[4][15]*dt;
+ nextP[7][16] = P[7][16] + P[4][16]*dt;
+ nextP[7][17] = P[7][17] + P[4][17]*dt;
+ nextP[7][18] = P[7][18] + P[4][18]*dt;
+ nextP[7][19] = P[7][19] + P[4][19]*dt;
+ nextP[7][20] = P[7][20] + P[4][20]*dt;
+ nextP[8][0] = P[8][0] + P[5][0]*dt + SF[6]*(P[8][1] + P[5][1]*dt) + SPP[7]*(P[8][2] + P[5][2]*dt) + SPP[6]*(P[8][3] + P[5][3]*dt) + SPP[5]*(P[8][10] + P[5][10]*dt) + SPP[4]*(P[8][11] + P[5][11]*dt) + SPP[3]*(P[8][12] + P[5][12]*dt);
+ nextP[8][1] = P[8][1] + P[5][1]*dt + SF[5]*(P[8][0] + P[5][0]*dt) + SF[4]*(P[8][2] + P[5][2]*dt) + SPP[7]*(P[8][3] + P[5][3]*dt) + SPP[3]*(P[8][11] + P[5][11]*dt) - SPP[4]*(P[8][12] + P[5][12]*dt) - (q0*(P[8][10] + P[5][10]*dt))/2;
+ nextP[8][2] = P[8][2] + P[5][2]*dt + SF[3]*(P[8][0] + P[5][0]*dt) + SF[5]*(P[8][3] + P[5][3]*dt) + SPP[6]*(P[8][1] + P[5][1]*dt) - SPP[3]*(P[8][10] + P[5][10]*dt) + SPP[5]*(P[8][12] + P[5][12]*dt) - (q0*(P[8][11] + P[5][11]*dt))/2;
+ nextP[8][3] = P[8][3] + P[5][3]*dt + SF[4]*(P[8][0] + P[5][0]*dt) + SF[3]*(P[8][1] + P[5][1]*dt) + SF[6]*(P[8][2] + P[5][2]*dt) + SPP[4]*(P[8][10] + P[5][10]*dt) - SPP[5]*(P[8][11] + P[5][11]*dt) - (q0*(P[8][12] + P[5][12]*dt))/2;
+ nextP[8][4] = P[8][4] + P[5][4]*dt + SF[0]*(P[8][1] + P[5][1]*dt) + SF[2]*(P[8][0] + P[5][0]*dt) + SPP[0]*(P[8][2] + P[5][2]*dt) - SPP[2]*(P[8][3] + P[5][3]*dt);
+ nextP[8][5] = P[8][5] + P[5][5]*dt + SF[1]*(P[8][0] + P[5][0]*dt) + SF[0]*(P[8][2] + P[5][2]*dt) + SF[2]*(P[8][3] + P[5][3]*dt) - SPP[0]*(P[8][1] + P[5][1]*dt);
+ nextP[8][6] = P[8][6] + P[5][6]*dt + SF[1]*(P[8][1] + P[5][1]*dt) + SF[0]*(P[8][3] + P[5][3]*dt) + SPP[0]*(P[8][0] + P[5][0]*dt) - SPP[1]*(P[8][2] + P[5][2]*dt);
+ nextP[8][7] = P[8][7] + P[5][7]*dt + dt*(P[8][4] + P[5][4]*dt);
+ nextP[8][8] = P[8][8] + P[5][8]*dt + dt*(P[8][5] + P[5][5]*dt);
+ nextP[8][9] = P[8][9] + P[5][9]*dt + dt*(P[8][6] + P[5][6]*dt);
+ nextP[8][10] = P[8][10] + P[5][10]*dt;
+ nextP[8][11] = P[8][11] + P[5][11]*dt;
+ nextP[8][12] = P[8][12] + P[5][12]*dt;
+ nextP[8][13] = P[8][13] + P[5][13]*dt;
+ nextP[8][14] = P[8][14] + P[5][14]*dt;
+ nextP[8][15] = P[8][15] + P[5][15]*dt;
+ nextP[8][16] = P[8][16] + P[5][16]*dt;
+ nextP[8][17] = P[8][17] + P[5][17]*dt;
+ nextP[8][18] = P[8][18] + P[5][18]*dt;
+ nextP[8][19] = P[8][19] + P[5][19]*dt;
+ nextP[8][20] = P[8][20] + P[5][20]*dt;
+ nextP[9][0] = P[9][0] + P[6][0]*dt + SF[6]*(P[9][1] + P[6][1]*dt) + SPP[7]*(P[9][2] + P[6][2]*dt) + SPP[6]*(P[9][3] + P[6][3]*dt) + SPP[5]*(P[9][10] + P[6][10]*dt) + SPP[4]*(P[9][11] + P[6][11]*dt) + SPP[3]*(P[9][12] + P[6][12]*dt);
+ nextP[9][1] = P[9][1] + P[6][1]*dt + SF[5]*(P[9][0] + P[6][0]*dt) + SF[4]*(P[9][2] + P[6][2]*dt) + SPP[7]*(P[9][3] + P[6][3]*dt) + SPP[3]*(P[9][11] + P[6][11]*dt) - SPP[4]*(P[9][12] + P[6][12]*dt) - (q0*(P[9][10] + P[6][10]*dt))/2;
+ nextP[9][2] = P[9][2] + P[6][2]*dt + SF[3]*(P[9][0] + P[6][0]*dt) + SF[5]*(P[9][3] + P[6][3]*dt) + SPP[6]*(P[9][1] + P[6][1]*dt) - SPP[3]*(P[9][10] + P[6][10]*dt) + SPP[5]*(P[9][12] + P[6][12]*dt) - (q0*(P[9][11] + P[6][11]*dt))/2;
+ nextP[9][3] = P[9][3] + P[6][3]*dt + SF[4]*(P[9][0] + P[6][0]*dt) + SF[3]*(P[9][1] + P[6][1]*dt) + SF[6]*(P[9][2] + P[6][2]*dt) + SPP[4]*(P[9][10] + P[6][10]*dt) - SPP[5]*(P[9][11] + P[6][11]*dt) - (q0*(P[9][12] + P[6][12]*dt))/2;
+ nextP[9][4] = P[9][4] + P[6][4]*dt + SF[0]*(P[9][1] + P[6][1]*dt) + SF[2]*(P[9][0] + P[6][0]*dt) + SPP[0]*(P[9][2] + P[6][2]*dt) - SPP[2]*(P[9][3] + P[6][3]*dt);
+ nextP[9][5] = P[9][5] + P[6][5]*dt + SF[1]*(P[9][0] + P[6][0]*dt) + SF[0]*(P[9][2] + P[6][2]*dt) + SF[2]*(P[9][3] + P[6][3]*dt) - SPP[0]*(P[9][1] + P[6][1]*dt);
+ nextP[9][6] = P[9][6] + P[6][6]*dt + SF[1]*(P[9][1] + P[6][1]*dt) + SF[0]*(P[9][3] + P[6][3]*dt) + SPP[0]*(P[9][0] + P[6][0]*dt) - SPP[1]*(P[9][2] + P[6][2]*dt);
+ nextP[9][7] = P[9][7] + P[6][7]*dt + dt*(P[9][4] + P[6][4]*dt);
+ nextP[9][8] = P[9][8] + P[6][8]*dt + dt*(P[9][5] + P[6][5]*dt);
+ nextP[9][9] = P[9][9] + P[6][9]*dt + dt*(P[9][6] + P[6][6]*dt);
+ nextP[9][10] = P[9][10] + P[6][10]*dt;
+ nextP[9][11] = P[9][11] + P[6][11]*dt;
+ nextP[9][12] = P[9][12] + P[6][12]*dt;
+ nextP[9][13] = P[9][13] + P[6][13]*dt;
+ nextP[9][14] = P[9][14] + P[6][14]*dt;
+ nextP[9][15] = P[9][15] + P[6][15]*dt;
+ nextP[9][16] = P[9][16] + P[6][16]*dt;
+ nextP[9][17] = P[9][17] + P[6][17]*dt;
+ nextP[9][18] = P[9][18] + P[6][18]*dt;
+ nextP[9][19] = P[9][19] + P[6][19]*dt;
+ nextP[9][20] = P[9][20] + P[6][20]*dt;
+ nextP[10][0] = P[10][0] + P[10][1]*SF[6] + P[10][2]*SPP[7] + P[10][3]*SPP[6] + P[10][10]*SPP[5] + P[10][11]*SPP[4] + P[10][12]*SPP[3];
+ nextP[10][1] = P[10][1] + P[10][0]*SF[5] + P[10][2]*SF[4] + P[10][3]*SPP[7] + P[10][11]*SPP[3] - P[10][12]*SPP[4] - (P[10][10]*q0)/2;
+ nextP[10][2] = P[10][2] + P[10][0]*SF[3] + P[10][3]*SF[5] + P[10][1]*SPP[6] - P[10][10]*SPP[3] + P[10][12]*SPP[5] - (P[10][11]*q0)/2;
+ nextP[10][3] = P[10][3] + P[10][0]*SF[4] + P[10][1]*SF[3] + P[10][2]*SF[6] + P[10][10]*SPP[4] - P[10][11]*SPP[5] - (P[10][12]*q0)/2;
+ nextP[10][4] = P[10][4] + P[10][1]*SF[0] + P[10][0]*SF[2] + P[10][2]*SPP[0] - P[10][3]*SPP[2];
+ nextP[10][5] = P[10][5] + P[10][0]*SF[1] + P[10][2]*SF[0] + P[10][3]*SF[2] - P[10][1]*SPP[0];
+ nextP[10][6] = P[10][6] + P[10][1]*SF[1] + P[10][3]*SF[0] + P[10][0]*SPP[0] - P[10][2]*SPP[1];
+ nextP[10][7] = P[10][7] + P[10][4]*dt;
+ nextP[10][8] = P[10][8] + P[10][5]*dt;
+ nextP[10][9] = P[10][9] + P[10][6]*dt;
+ nextP[10][10] = P[10][10];
+ nextP[10][11] = P[10][11];
+ nextP[10][12] = P[10][12];
+ nextP[10][13] = P[10][13];
+ nextP[10][14] = P[10][14];
+ nextP[10][15] = P[10][15];
+ nextP[10][16] = P[10][16];
+ nextP[10][17] = P[10][17];
+ nextP[10][18] = P[10][18];
+ nextP[10][19] = P[10][19];
+ nextP[10][20] = P[10][20];
+ nextP[11][0] = P[11][0] + P[11][1]*SF[6] + P[11][2]*SPP[7] + P[11][3]*SPP[6] + P[11][10]*SPP[5] + P[11][11]*SPP[4] + P[11][12]*SPP[3];
+ nextP[11][1] = P[11][1] + P[11][0]*SF[5] + P[11][2]*SF[4] + P[11][3]*SPP[7] + P[11][11]*SPP[3] - P[11][12]*SPP[4] - (P[11][10]*q0)/2;
+ nextP[11][2] = P[11][2] + P[11][0]*SF[3] + P[11][3]*SF[5] + P[11][1]*SPP[6] - P[11][10]*SPP[3] + P[11][12]*SPP[5] - (P[11][11]*q0)/2;
+ nextP[11][3] = P[11][3] + P[11][0]*SF[4] + P[11][1]*SF[3] + P[11][2]*SF[6] + P[11][10]*SPP[4] - P[11][11]*SPP[5] - (P[11][12]*q0)/2;
+ nextP[11][4] = P[11][4] + P[11][1]*SF[0] + P[11][0]*SF[2] + P[11][2]*SPP[0] - P[11][3]*SPP[2];
+ nextP[11][5] = P[11][5] + P[11][0]*SF[1] + P[11][2]*SF[0] + P[11][3]*SF[2] - P[11][1]*SPP[0];
+ nextP[11][6] = P[11][6] + P[11][1]*SF[1] + P[11][3]*SF[0] + P[11][0]*SPP[0] - P[11][2]*SPP[1];
+ nextP[11][7] = P[11][7] + P[11][4]*dt;
+ nextP[11][8] = P[11][8] + P[11][5]*dt;
+ nextP[11][9] = P[11][9] + P[11][6]*dt;
+ nextP[11][10] = P[11][10];
+ nextP[11][11] = P[11][11];
+ nextP[11][12] = P[11][12];
+ nextP[11][13] = P[11][13];
+ nextP[11][14] = P[11][14];
+ nextP[11][15] = P[11][15];
+ nextP[11][16] = P[11][16];
+ nextP[11][17] = P[11][17];
+ nextP[11][18] = P[11][18];
+ nextP[11][19] = P[11][19];
+ nextP[11][20] = P[11][20];
+ nextP[12][0] = P[12][0] + P[12][1]*SF[6] + P[12][2]*SPP[7] + P[12][3]*SPP[6] + P[12][10]*SPP[5] + P[12][11]*SPP[4] + P[12][12]*SPP[3];
+ nextP[12][1] = P[12][1] + P[12][0]*SF[5] + P[12][2]*SF[4] + P[12][3]*SPP[7] + P[12][11]*SPP[3] - P[12][12]*SPP[4] - (P[12][10]*q0)/2;
+ nextP[12][2] = P[12][2] + P[12][0]*SF[3] + P[12][3]*SF[5] + P[12][1]*SPP[6] - P[12][10]*SPP[3] + P[12][12]*SPP[5] - (P[12][11]*q0)/2;
+ nextP[12][3] = P[12][3] + P[12][0]*SF[4] + P[12][1]*SF[3] + P[12][2]*SF[6] + P[12][10]*SPP[4] - P[12][11]*SPP[5] - (P[12][12]*q0)/2;
+ nextP[12][4] = P[12][4] + P[12][1]*SF[0] + P[12][0]*SF[2] + P[12][2]*SPP[0] - P[12][3]*SPP[2];
+ nextP[12][5] = P[12][5] + P[12][0]*SF[1] + P[12][2]*SF[0] + P[12][3]*SF[2] - P[12][1]*SPP[0];
+ nextP[12][6] = P[12][6] + P[12][1]*SF[1] + P[12][3]*SF[0] + P[12][0]*SPP[0] - P[12][2]*SPP[1];
+ nextP[12][7] = P[12][7] + P[12][4]*dt;
+ nextP[12][8] = P[12][8] + P[12][5]*dt;
+ nextP[12][9] = P[12][9] + P[12][6]*dt;
+ nextP[12][10] = P[12][10];
+ nextP[12][11] = P[12][11];
+ nextP[12][12] = P[12][12];
+ nextP[12][13] = P[12][13];
+ nextP[12][14] = P[12][14];
+ nextP[12][15] = P[12][15];
+ nextP[12][16] = P[12][16];
+ nextP[12][17] = P[12][17];
+ nextP[12][18] = P[12][18];
+ nextP[12][19] = P[12][19];
+ nextP[12][20] = P[12][20];
+ nextP[13][0] = P[13][0] + P[13][1]*SF[6] + P[13][2]*SPP[7] + P[13][3]*SPP[6] + P[13][10]*SPP[5] + P[13][11]*SPP[4] + P[13][12]*SPP[3];
+ nextP[13][1] = P[13][1] + P[13][0]*SF[5] + P[13][2]*SF[4] + P[13][3]*SPP[7] + P[13][11]*SPP[3] - P[13][12]*SPP[4] - (P[13][10]*q0)/2;
+ nextP[13][2] = P[13][2] + P[13][0]*SF[3] + P[13][3]*SF[5] + P[13][1]*SPP[6] - P[13][10]*SPP[3] + P[13][12]*SPP[5] - (P[13][11]*q0)/2;
+ nextP[13][3] = P[13][3] + P[13][0]*SF[4] + P[13][1]*SF[3] + P[13][2]*SF[6] + P[13][10]*SPP[4] - P[13][11]*SPP[5] - (P[13][12]*q0)/2;
+ nextP[13][4] = P[13][4] + P[13][1]*SF[0] + P[13][0]*SF[2] + P[13][2]*SPP[0] - P[13][3]*SPP[2];
+ nextP[13][5] = P[13][5] + P[13][0]*SF[1] + P[13][2]*SF[0] + P[13][3]*SF[2] - P[13][1]*SPP[0];
+ nextP[13][6] = P[13][6] + P[13][1]*SF[1] + P[13][3]*SF[0] + P[13][0]*SPP[0] - P[13][2]*SPP[1];
+ nextP[13][7] = P[13][7] + P[13][4]*dt;
+ nextP[13][8] = P[13][8] + P[13][5]*dt;
+ nextP[13][9] = P[13][9] + P[13][6]*dt;
+ nextP[13][10] = P[13][10];
+ nextP[13][11] = P[13][11];
+ nextP[13][12] = P[13][12];
+ nextP[13][13] = P[13][13];
+ nextP[13][14] = P[13][14];
+ nextP[13][15] = P[13][15];
+ nextP[13][16] = P[13][16];
+ nextP[13][17] = P[13][17];
+ nextP[13][18] = P[13][18];
+ nextP[13][19] = P[13][19];
+ nextP[13][20] = P[13][20];
+ nextP[14][0] = P[14][0] + P[14][1]*SF[6] + P[14][2]*SPP[7] + P[14][3]*SPP[6] + P[14][10]*SPP[5] + P[14][11]*SPP[4] + P[14][12]*SPP[3];
+ nextP[14][1] = P[14][1] + P[14][0]*SF[5] + P[14][2]*SF[4] + P[14][3]*SPP[7] + P[14][11]*SPP[3] - P[14][12]*SPP[4] - (P[14][10]*q0)/2;
+ nextP[14][2] = P[14][2] + P[14][0]*SF[3] + P[14][3]*SF[5] + P[14][1]*SPP[6] - P[14][10]*SPP[3] + P[14][12]*SPP[5] - (P[14][11]*q0)/2;
+ nextP[14][3] = P[14][3] + P[14][0]*SF[4] + P[14][1]*SF[3] + P[14][2]*SF[6] + P[14][10]*SPP[4] - P[14][11]*SPP[5] - (P[14][12]*q0)/2;
+ nextP[14][4] = P[14][4] + P[14][1]*SF[0] + P[14][0]*SF[2] + P[14][2]*SPP[0] - P[14][3]*SPP[2];
+ nextP[14][5] = P[14][5] + P[14][0]*SF[1] + P[14][2]*SF[0] + P[14][3]*SF[2] - P[14][1]*SPP[0];
+ nextP[14][6] = P[14][6] + P[14][1]*SF[1] + P[14][3]*SF[0] + P[14][0]*SPP[0] - P[14][2]*SPP[1];
+ nextP[14][7] = P[14][7] + P[14][4]*dt;
+ nextP[14][8] = P[14][8] + P[14][5]*dt;
+ nextP[14][9] = P[14][9] + P[14][6]*dt;
+ nextP[14][10] = P[14][10];
+ nextP[14][11] = P[14][11];
+ nextP[14][12] = P[14][12];
+ nextP[14][13] = P[14][13];
+ nextP[14][14] = P[14][14];
+ nextP[14][15] = P[14][15];
+ nextP[14][16] = P[14][16];
+ nextP[14][17] = P[14][17];
+ nextP[14][18] = P[14][18];
+ nextP[14][19] = P[14][19];
+ nextP[14][20] = P[14][20];
+ nextP[15][0] = P[15][0] + P[15][1]*SF[6] + P[15][2]*SPP[7] + P[15][3]*SPP[6] + P[15][10]*SPP[5] + P[15][11]*SPP[4] + P[15][12]*SPP[3];
+ nextP[15][1] = P[15][1] + P[15][0]*SF[5] + P[15][2]*SF[4] + P[15][3]*SPP[7] + P[15][11]*SPP[3] - P[15][12]*SPP[4] - (P[15][10]*q0)/2;
+ nextP[15][2] = P[15][2] + P[15][0]*SF[3] + P[15][3]*SF[5] + P[15][1]*SPP[6] - P[15][10]*SPP[3] + P[15][12]*SPP[5] - (P[15][11]*q0)/2;
+ nextP[15][3] = P[15][3] + P[15][0]*SF[4] + P[15][1]*SF[3] + P[15][2]*SF[6] + P[15][10]*SPP[4] - P[15][11]*SPP[5] - (P[15][12]*q0)/2;
+ nextP[15][4] = P[15][4] + P[15][1]*SF[0] + P[15][0]*SF[2] + P[15][2]*SPP[0] - P[15][3]*SPP[2];
+ nextP[15][5] = P[15][5] + P[15][0]*SF[1] + P[15][2]*SF[0] + P[15][3]*SF[2] - P[15][1]*SPP[0];
+ nextP[15][6] = P[15][6] + P[15][1]*SF[1] + P[15][3]*SF[0] + P[15][0]*SPP[0] - P[15][2]*SPP[1];
+ nextP[15][7] = P[15][7] + P[15][4]*dt;
+ nextP[15][8] = P[15][8] + P[15][5]*dt;
+ nextP[15][9] = P[15][9] + P[15][6]*dt;
+ nextP[15][10] = P[15][10];
+ nextP[15][11] = P[15][11];
+ nextP[15][12] = P[15][12];
+ nextP[15][13] = P[15][13];
+ nextP[15][14] = P[15][14];
+ nextP[15][15] = P[15][15];
+ nextP[15][16] = P[15][16];
+ nextP[15][17] = P[15][17];
+ nextP[15][18] = P[15][18];
+ nextP[15][19] = P[15][19];
+ nextP[15][20] = P[15][20];
+ nextP[16][0] = P[16][0] + P[16][1]*SF[6] + P[16][2]*SPP[7] + P[16][3]*SPP[6] + P[16][10]*SPP[5] + P[16][11]*SPP[4] + P[16][12]*SPP[3];
+ nextP[16][1] = P[16][1] + P[16][0]*SF[5] + P[16][2]*SF[4] + P[16][3]*SPP[7] + P[16][11]*SPP[3] - P[16][12]*SPP[4] - (P[16][10]*q0)/2;
+ nextP[16][2] = P[16][2] + P[16][0]*SF[3] + P[16][3]*SF[5] + P[16][1]*SPP[6] - P[16][10]*SPP[3] + P[16][12]*SPP[5] - (P[16][11]*q0)/2;
+ nextP[16][3] = P[16][3] + P[16][0]*SF[4] + P[16][1]*SF[3] + P[16][2]*SF[6] + P[16][10]*SPP[4] - P[16][11]*SPP[5] - (P[16][12]*q0)/2;
+ nextP[16][4] = P[16][4] + P[16][1]*SF[0] + P[16][0]*SF[2] + P[16][2]*SPP[0] - P[16][3]*SPP[2];
+ nextP[16][5] = P[16][5] + P[16][0]*SF[1] + P[16][2]*SF[0] + P[16][3]*SF[2] - P[16][1]*SPP[0];
+ nextP[16][6] = P[16][6] + P[16][1]*SF[1] + P[16][3]*SF[0] + P[16][0]*SPP[0] - P[16][2]*SPP[1];
+ nextP[16][7] = P[16][7] + P[16][4]*dt;
+ nextP[16][8] = P[16][8] + P[16][5]*dt;
+ nextP[16][9] = P[16][9] + P[16][6]*dt;
+ nextP[16][10] = P[16][10];
+ nextP[16][11] = P[16][11];
+ nextP[16][12] = P[16][12];
+ nextP[16][13] = P[16][13];
+ nextP[16][14] = P[16][14];
+ nextP[16][15] = P[16][15];
+ nextP[16][16] = P[16][16];
+ nextP[16][17] = P[16][17];
+ nextP[16][18] = P[16][18];
+ nextP[16][19] = P[16][19];
+ nextP[16][20] = P[16][20];
+ nextP[17][0] = P[17][0] + P[17][1]*SF[6] + P[17][2]*SPP[7] + P[17][3]*SPP[6] + P[17][10]*SPP[5] + P[17][11]*SPP[4] + P[17][12]*SPP[3];
+ nextP[17][1] = P[17][1] + P[17][0]*SF[5] + P[17][2]*SF[4] + P[17][3]*SPP[7] + P[17][11]*SPP[3] - P[17][12]*SPP[4] - (P[17][10]*q0)/2;
+ nextP[17][2] = P[17][2] + P[17][0]*SF[3] + P[17][3]*SF[5] + P[17][1]*SPP[6] - P[17][10]*SPP[3] + P[17][12]*SPP[5] - (P[17][11]*q0)/2;
+ nextP[17][3] = P[17][3] + P[17][0]*SF[4] + P[17][1]*SF[3] + P[17][2]*SF[6] + P[17][10]*SPP[4] - P[17][11]*SPP[5] - (P[17][12]*q0)/2;
+ nextP[17][4] = P[17][4] + P[17][1]*SF[0] + P[17][0]*SF[2] + P[17][2]*SPP[0] - P[17][3]*SPP[2];
+ nextP[17][5] = P[17][5] + P[17][0]*SF[1] + P[17][2]*SF[0] + P[17][3]*SF[2] - P[17][1]*SPP[0];
+ nextP[17][6] = P[17][6] + P[17][1]*SF[1] + P[17][3]*SF[0] + P[17][0]*SPP[0] - P[17][2]*SPP[1];
+ nextP[17][7] = P[17][7] + P[17][4]*dt;
+ nextP[17][8] = P[17][8] + P[17][5]*dt;
+ nextP[17][9] = P[17][9] + P[17][6]*dt;
+ nextP[17][10] = P[17][10];
+ nextP[17][11] = P[17][11];
+ nextP[17][12] = P[17][12];
+ nextP[17][13] = P[17][13];
+ nextP[17][14] = P[17][14];
+ nextP[17][15] = P[17][15];
+ nextP[17][16] = P[17][16];
+ nextP[17][17] = P[17][17];
+ nextP[17][18] = P[17][18];
+ nextP[17][19] = P[17][19];
+ nextP[17][20] = P[17][20];
+ nextP[18][0] = P[18][0] + P[18][1]*SF[6] + P[18][2]*SPP[7] + P[18][3]*SPP[6] + P[18][10]*SPP[5] + P[18][11]*SPP[4] + P[18][12]*SPP[3];
+ nextP[18][1] = P[18][1] + P[18][0]*SF[5] + P[18][2]*SF[4] + P[18][3]*SPP[7] + P[18][11]*SPP[3] - P[18][12]*SPP[4] - (P[18][10]*q0)/2;
+ nextP[18][2] = P[18][2] + P[18][0]*SF[3] + P[18][3]*SF[5] + P[18][1]*SPP[6] - P[18][10]*SPP[3] + P[18][12]*SPP[5] - (P[18][11]*q0)/2;
+ nextP[18][3] = P[18][3] + P[18][0]*SF[4] + P[18][1]*SF[3] + P[18][2]*SF[6] + P[18][10]*SPP[4] - P[18][11]*SPP[5] - (P[18][12]*q0)/2;
+ nextP[18][4] = P[18][4] + P[18][1]*SF[0] + P[18][0]*SF[2] + P[18][2]*SPP[0] - P[18][3]*SPP[2];
+ nextP[18][5] = P[18][5] + P[18][0]*SF[1] + P[18][2]*SF[0] + P[18][3]*SF[2] - P[18][1]*SPP[0];
+ nextP[18][6] = P[18][6] + P[18][1]*SF[1] + P[18][3]*SF[0] + P[18][0]*SPP[0] - P[18][2]*SPP[1];
+ nextP[18][7] = P[18][7] + P[18][4]*dt;
+ nextP[18][8] = P[18][8] + P[18][5]*dt;
+ nextP[18][9] = P[18][9] + P[18][6]*dt;
+ nextP[18][10] = P[18][10];
+ nextP[18][11] = P[18][11];
+ nextP[18][12] = P[18][12];
+ nextP[18][13] = P[18][13];
+ nextP[18][14] = P[18][14];
+ nextP[18][15] = P[18][15];
+ nextP[18][16] = P[18][16];
+ nextP[18][17] = P[18][17];
+ nextP[18][18] = P[18][18];
+ nextP[18][19] = P[18][19];
+ nextP[18][20] = P[18][20];
+ nextP[19][0] = P[19][0] + P[19][1]*SF[6] + P[19][2]*SPP[7] + P[19][3]*SPP[6] + P[19][10]*SPP[5] + P[19][11]*SPP[4] + P[19][12]*SPP[3];
+ nextP[19][1] = P[19][1] + P[19][0]*SF[5] + P[19][2]*SF[4] + P[19][3]*SPP[7] + P[19][11]*SPP[3] - P[19][12]*SPP[4] - (P[19][10]*q0)/2;
+ nextP[19][2] = P[19][2] + P[19][0]*SF[3] + P[19][3]*SF[5] + P[19][1]*SPP[6] - P[19][10]*SPP[3] + P[19][12]*SPP[5] - (P[19][11]*q0)/2;
+ nextP[19][3] = P[19][3] + P[19][0]*SF[4] + P[19][1]*SF[3] + P[19][2]*SF[6] + P[19][10]*SPP[4] - P[19][11]*SPP[5] - (P[19][12]*q0)/2;
+ nextP[19][4] = P[19][4] + P[19][1]*SF[0] + P[19][0]*SF[2] + P[19][2]*SPP[0] - P[19][3]*SPP[2];
+ nextP[19][5] = P[19][5] + P[19][0]*SF[1] + P[19][2]*SF[0] + P[19][3]*SF[2] - P[19][1]*SPP[0];
+ nextP[19][6] = P[19][6] + P[19][1]*SF[1] + P[19][3]*SF[0] + P[19][0]*SPP[0] - P[19][2]*SPP[1];
+ nextP[19][7] = P[19][7] + P[19][4]*dt;
+ nextP[19][8] = P[19][8] + P[19][5]*dt;
+ nextP[19][9] = P[19][9] + P[19][6]*dt;
+ nextP[19][10] = P[19][10];
+ nextP[19][11] = P[19][11];
+ nextP[19][12] = P[19][12];
+ nextP[19][13] = P[19][13];
+ nextP[19][14] = P[19][14];
+ nextP[19][15] = P[19][15];
+ nextP[19][16] = P[19][16];
+ nextP[19][17] = P[19][17];
+ nextP[19][18] = P[19][18];
+ nextP[19][19] = P[19][19];
+ nextP[19][20] = P[19][20];
+ nextP[20][0] = P[20][0] + P[20][1]*SF[6] + P[20][2]*SPP[7] + P[20][3]*SPP[6] + P[20][10]*SPP[5] + P[20][11]*SPP[4] + P[20][12]*SPP[3];
+ nextP[20][1] = P[20][1] + P[20][0]*SF[5] + P[20][2]*SF[4] + P[20][3]*SPP[7] + P[20][11]*SPP[3] - P[20][12]*SPP[4] - (P[20][10]*q0)/2;
+ nextP[20][2] = P[20][2] + P[20][0]*SF[3] + P[20][3]*SF[5] + P[20][1]*SPP[6] - P[20][10]*SPP[3] + P[20][12]*SPP[5] - (P[20][11]*q0)/2;
+ nextP[20][3] = P[20][3] + P[20][0]*SF[4] + P[20][1]*SF[3] + P[20][2]*SF[6] + P[20][10]*SPP[4] - P[20][11]*SPP[5] - (P[20][12]*q0)/2;
+ nextP[20][4] = P[20][4] + P[20][1]*SF[0] + P[20][0]*SF[2] + P[20][2]*SPP[0] - P[20][3]*SPP[2];
+ nextP[20][5] = P[20][5] + P[20][0]*SF[1] + P[20][2]*SF[0] + P[20][3]*SF[2] - P[20][1]*SPP[0];
+ nextP[20][6] = P[20][6] + P[20][1]*SF[1] + P[20][3]*SF[0] + P[20][0]*SPP[0] - P[20][2]*SPP[1];
+ nextP[20][7] = P[20][7] + P[20][4]*dt;
+ nextP[20][8] = P[20][8] + P[20][5]*dt;
+ nextP[20][9] = P[20][9] + P[20][6]*dt;
+ nextP[20][10] = P[20][10];
+ nextP[20][11] = P[20][11];
+ nextP[20][12] = P[20][12];
+ nextP[20][13] = P[20][13];
+ nextP[20][14] = P[20][14];
+ nextP[20][15] = P[20][15];
+ nextP[20][16] = P[20][16];
+ nextP[20][17] = P[20][17];
+ nextP[20][18] = P[20][18];
+ nextP[20][19] = P[20][19];
+ nextP[20][20] = P[20][20];
+
+ for (unsigned i = 0; i < n_states; i++)
+ {
+ nextP[i][i] = nextP[i][i] + processNoise[i];
+ }
+
+ // If on ground or no magnetometer fitted, inhibit magnetometer bias updates by
+ // setting the coresponding covariance terms to zero.
+ if (onGround || !useCompass)
+ {
+ zeroRows(nextP,15,20);
+ zeroCols(nextP,15,20);
+ }
+
+ // If on ground or not using airspeed sensing, inhibit wind velocity
+ // covariance growth.
+ if (onGround || !useAirspeed)
+ {
+ zeroRows(nextP,13,14);
+ zeroCols(nextP,13,14);
+ }
+
+ // If the total position variance exceds 1E6 (1000m), then stop covariance
+ // growth by setting the predicted to the previous values
+ // This prevent an ill conditioned matrix from occurring for long periods
+ // without GPS
+ if ((P[7][7] + P[8][8]) > 1E6f)
+ {
+ for (uint8_t i=7; i<=8; i++)
+ {
+ for (unsigned j = 0; j < n_states; j++)
+ {
+ nextP[i][j] = P[i][j];
+ nextP[j][i] = P[j][i];
+ }
+ }
+ }
+
+ if (onGround || staticMode) {
+ // copy the portion of the variances we want to
+ // propagate
+ for (unsigned i = 0; i < 14; i++) {
+ P[i][i] = nextP[i][i];
+
+ // force symmetry for observable states
+ // force zero for non-observable states
+ for (unsigned i = 1; i < n_states; i++)
+ {
+ for (uint8_t j = 0; j < i; j++)
+ {
+ if ((i > 12) || (j > 12)) {
+ P[i][j] = 0.0f;
+ } else {
+ P[i][j] = 0.5f * (nextP[i][j] + nextP[j][i]);
+ }
+ P[j][i] = P[i][j];
+ }
+ }
+ }
+
+ } else {
+
+ // Copy covariance
+ for (unsigned i = 0; i < n_states; i++) {
+ P[i][i] = nextP[i][i];
+ }
+
+ // force symmetry for observable states
+ for (unsigned i = 1; i < n_states; i++)
+ {
+ for (uint8_t j = 0; j < i; j++)
+ {
+ P[i][j] = 0.5f * (nextP[i][j] + nextP[j][i]);
+ P[j][i] = P[i][j];
+ }
+ }
+
+ }
+
+ ConstrainVariances();
+}
+
+void AttPosEKF::FuseVelposNED()
+{
+
+// declare variables used by fault isolation logic
+ uint32_t gpsRetryTime = 30000; // time in msec before GPS fusion will be retried following innovation consistency failure
+ uint32_t gpsRetryTimeNoTAS = 5000; // retry time if no TAS measurement available
+ uint32_t hgtRetryTime = 5000; // height measurement retry time
+ uint32_t horizRetryTime;
+
+// declare variables used to check measurement errors
+ float velInnov[3] = {0.0f,0.0f,0.0f};
+ float posInnov[2] = {0.0f,0.0f};
+ float hgtInnov = 0.0f;
+
+// declare variables used to control access to arrays
+ bool fuseData[6] = {false,false,false,false,false,false};
+ uint8_t stateIndex;
+ uint8_t obsIndex;
+ uint8_t indexLimit;
+
+// declare variables used by state and covariance update calculations
+ float velErr;
+ float posErr;
+ float R_OBS[6];
+ float observation[6];
+ float SK;
+ float quatMag;
+
+// Perform sequential fusion of GPS measurements. This assumes that the
+// errors in the different velocity and position components are
+// uncorrelated which is not true, however in the absence of covariance
+// data from the GPS receiver it is the only assumption we can make
+// so we might as well take advantage of the computational efficiencies
+// associated with sequential fusion
+ if (fuseVelData || fusePosData || fuseHgtData)
+ {
+ // set the GPS data timeout depending on whether airspeed data is present
+ if (useAirspeed) horizRetryTime = gpsRetryTime;
+ else horizRetryTime = gpsRetryTimeNoTAS;
+
+ // Form the observation vector
+ for (uint8_t i=0; i<=2; i++) observation[i] = velNED[i];
+ for (uint8_t i=3; i<=4; i++) observation[i] = posNE[i-3];
+ observation[5] = -(hgtMea);
+
+ // Estimate the GPS Velocity, GPS horiz position and height measurement variances.
+ velErr = 0.2f*accNavMag; // additional error in GPS velocities caused by manoeuvring
+ posErr = 0.2f*accNavMag; // additional error in GPS position caused by manoeuvring
+ R_OBS[0] = 0.04f + sq(velErr);
+ R_OBS[1] = R_OBS[0];
+ R_OBS[2] = 0.08f + sq(velErr);
+ R_OBS[3] = R_OBS[2];
+ R_OBS[4] = 4.0f + sq(posErr);
+ R_OBS[5] = 4.0f;
+
+ // Set innovation variances to zero default
+ for (uint8_t i = 0; i<=5; i++)
+ {
+ varInnovVelPos[i] = 0.0f;
+ }
+ // calculate innovations and check GPS data validity using an innovation consistency check
+ if (fuseVelData)
+ {
+ // test velocity measurements
+ uint8_t imax = 2;
+ if (fusionModeGPS == 1) imax = 1;
+ for (uint8_t i = 0; i<=imax; i++)
+ {
+ velInnov[i] = statesAtVelTime[i+4] - velNED[i];
+ stateIndex = 4 + i;
+ varInnovVelPos[i] = P[stateIndex][stateIndex] + R_OBS[i];
+ }
+ // apply a 5-sigma threshold
+ current_ekf_state.velHealth = (sq(velInnov[0]) + sq(velInnov[1]) + sq(velInnov[2])) < 25.0f * (varInnovVelPos[0] + varInnovVelPos[1] + varInnovVelPos[2]);
+ current_ekf_state.velTimeout = (millis() - current_ekf_state.velFailTime) > horizRetryTime;
+ if (current_ekf_state.velHealth || current_ekf_state.velTimeout)
+ {
+ current_ekf_state.velHealth = true;
+ current_ekf_state.velFailTime = millis();
+ }
+ else
+ {
+ current_ekf_state.velHealth = false;
+ }
+ }
+ if (fusePosData)
+ {
+ // test horizontal position measurements
+ posInnov[0] = statesAtPosTime[7] - posNE[0];
+ posInnov[1] = statesAtPosTime[8] - posNE[1];
+ varInnovVelPos[3] = P[7][7] + R_OBS[3];
+ varInnovVelPos[4] = P[8][8] + R_OBS[4];
+ // apply a 10-sigma threshold
+ current_ekf_state.posHealth = (sq(posInnov[0]) + sq(posInnov[1])) < 100.0f*(varInnovVelPos[3] + varInnovVelPos[4]);
+ current_ekf_state.posTimeout = (millis() - current_ekf_state.posFailTime) > horizRetryTime;
+ if (current_ekf_state.posHealth || current_ekf_state.posTimeout)
+ {
+ current_ekf_state.posHealth = true;
+ current_ekf_state.posFailTime = millis();
+ }
+ else
+ {
+ current_ekf_state.posHealth = false;
+ }
+ }
+ // test height measurements
+ if (fuseHgtData)
+ {
+ hgtInnov = statesAtHgtTime[9] + hgtMea;
+ varInnovVelPos[5] = P[9][9] + R_OBS[5];
+ // apply a 10-sigma threshold
+ current_ekf_state.hgtHealth = sq(hgtInnov) < 100.0f*varInnovVelPos[5];
+ current_ekf_state.hgtTimeout = (millis() - current_ekf_state.hgtFailTime) > hgtRetryTime;
+ if (current_ekf_state.hgtHealth || current_ekf_state.hgtTimeout)
+ {
+ current_ekf_state.hgtHealth = true;
+ current_ekf_state.hgtFailTime = millis();
+ }
+ else
+ {
+ current_ekf_state.hgtHealth = false;
+ }
+ }
+ // Set range for sequential fusion of velocity and position measurements depending
+ // on which data is available and its health
+ if (fuseVelData && fusionModeGPS == 0 && current_ekf_state.velHealth)
+ {
+ fuseData[0] = true;
+ fuseData[1] = true;
+ fuseData[2] = true;
+ }
+ if (fuseVelData && fusionModeGPS == 1 && current_ekf_state.velHealth)
+ {
+ fuseData[0] = true;
+ fuseData[1] = true;
+ }
+ if (fusePosData && fusionModeGPS <= 2 && current_ekf_state.posHealth)
+ {
+ fuseData[3] = true;
+ fuseData[4] = true;
+ }
+ if (fuseHgtData && current_ekf_state.hgtHealth)
+ {
+ fuseData[5] = true;
+ }
+ // Limit range of states modified when on ground
+ if(!onGround)
+ {
+ indexLimit = 20;
+ }
+ else
+ {
+ indexLimit = 12;
+ }
+ // Fuse measurements sequentially
+ for (obsIndex=0; obsIndex<=5; obsIndex++)
+ {
+ if (fuseData[obsIndex])
+ {
+ stateIndex = 4 + obsIndex;
+ // Calculate the measurement innovation, using states from a
+ // different time coordinate if fusing height data
+ if (obsIndex >= 0 && obsIndex <= 2)
+ {
+ innovVelPos[obsIndex] = statesAtVelTime[stateIndex] - observation[obsIndex];
+ }
+ else if (obsIndex == 3 || obsIndex == 4)
+ {
+ innovVelPos[obsIndex] = statesAtPosTime[stateIndex] - observation[obsIndex];
+ }
+ else if (obsIndex == 5)
+ {
+ innovVelPos[obsIndex] = statesAtHgtTime[stateIndex] - observation[obsIndex];
+ }
+ // Calculate the Kalman Gain
+ // Calculate innovation variances - also used for data logging
+ varInnovVelPos[obsIndex] = P[stateIndex][stateIndex] + R_OBS[obsIndex];
+ SK = 1.0/varInnovVelPos[obsIndex];
+ for (uint8_t i= 0; i<=indexLimit; i++)
+ {
+ Kfusion[i] = P[i][stateIndex]*SK;
+ }
+ // Calculate state corrections and re-normalise the quaternions
+ for (uint8_t i = 0; i<=indexLimit; i++)
+ {
+ states[i] = states[i] - Kfusion[i] * innovVelPos[obsIndex];
+ }
+ quatMag = sqrt(states[0]*states[0] + states[1]*states[1] + states[2]*states[2] + states[3]*states[3]);
+ if (quatMag > 1e-12f) // divide by 0 protection
+ {
+ for (uint8_t i = 0; i<=3; i++)
+ {
+ states[i] = states[i] / quatMag;
+ }
+ }
+ // Update the covariance - take advantage of direct observation of a
+ // single state at index = stateIndex to reduce computations
+ // Optimised implementation of standard equation P = (I - K*H)*P;
+ for (uint8_t i= 0; i<=indexLimit; i++)
+ {
+ for (uint8_t j= 0; j<=indexLimit; j++)
+ {
+ KHP[i][j] = Kfusion[i] * P[stateIndex][j];
+ }
+ }
+ for (uint8_t i= 0; i<=indexLimit; i++)
+ {
+ for (uint8_t j= 0; j<=indexLimit; j++)
+ {
+ P[i][j] = P[i][j] - KHP[i][j];
+ }
+ }
+ }
+ }
+ }
+
+ ForceSymmetry();
+ ConstrainVariances();
+
+ //printf("velh: %s, posh: %s, hgth: %s\n", ((velHealth) ? "OK" : "FAIL"), ((posHealth) ? "OK" : "FAIL"), ((hgtHealth) ? "OK" : "FAIL"));
+}
+
+void AttPosEKF::FuseMagnetometer()
+{
+ uint8_t obsIndex;
+ uint8_t indexLimit;
+ float DCM[3][3] =
+ {
+ {1.0f,0.0f,0.0f} ,
+ {0.0f,1.0f,0.0f} ,
+ {0.0f,0.0f,1.0f}
+ };
+ float MagPred[3] = {0.0f,0.0f,0.0f};
+ float SK_MX[6];
+ float SK_MY[5];
+ float SK_MZ[6];
+ float SH_MAG[9] = {0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f};
+
+// Perform sequential fusion of Magnetometer measurements.
+// This assumes that the errors in the different components are
+// uncorrelated which is not true, however in the absence of covariance
+// data fit is the only assumption we can make
+// so we might as well take advantage of the computational efficiencies
+// associated with sequential fusion
+ if (useCompass && (fuseMagData || obsIndex == 1 || obsIndex == 2))
+ {
+ // Limit range of states modified when on ground
+ if(!onGround)
+ {
+ indexLimit = 20;
+ }
+ else
+ {
+ indexLimit = 12;
+ }
+
+ static float q0 = 0.0f;
+ static float q1 = 0.0f;
+ static float q2 = 0.0f;
+ static float q3 = 1.0f;
+ static float magN = 0.4f;
+ static float magE = 0.0f;
+ static float magD = 0.3f;
+
+ static float R_MAG = 0.0025f;
+
+ float H_MAG[21] = {0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f};
+
+ // Sequential fusion of XYZ components to spread processing load across
+ // three prediction time steps.
+
+ // Calculate observation jacobians and Kalman gains
+ if (fuseMagData)
+ {
+ static float magXbias = 0.0f;
+ static float magYbias = 0.0f;
+ static float magZbias = 0.0f;
+
+ // Copy required states to local variable names
+ q0 = statesAtMagMeasTime[0];
+ q1 = statesAtMagMeasTime[1];
+ q2 = statesAtMagMeasTime[2];
+ q3 = statesAtMagMeasTime[3];
+ magN = statesAtMagMeasTime[15];
+ magE = statesAtMagMeasTime[16];
+ magD = statesAtMagMeasTime[17];
+ magXbias = statesAtMagMeasTime[18];
+ magYbias = statesAtMagMeasTime[19];
+ magZbias = statesAtMagMeasTime[20];
+
+ // rotate predicted earth components into body axes and calculate
+ // predicted measurments
+ DCM[0][0] = q0*q0 + q1*q1 - q2*q2 - q3*q3;
+ DCM[0][1] = 2*(q1*q2 + q0*q3);
+ DCM[0][2] = 2*(q1*q3-q0*q2);
+ DCM[1][0] = 2*(q1*q2 - q0*q3);
+ DCM[1][1] = q0*q0 - q1*q1 + q2*q2 - q3*q3;
+ DCM[1][2] = 2*(q2*q3 + q0*q1);
+ DCM[2][0] = 2*(q1*q3 + q0*q2);
+ DCM[2][1] = 2*(q2*q3 - q0*q1);
+ DCM[2][2] = q0*q0 - q1*q1 - q2*q2 + q3*q3;
+ MagPred[0] = DCM[0][0]*magN + DCM[0][1]*magE + DCM[0][2]*magD + magXbias;
+ MagPred[1] = DCM[1][0]*magN + DCM[1][1]*magE + DCM[1][2]*magD + magYbias;
+ MagPred[2] = DCM[2][0]*magN + DCM[2][1]*magE + DCM[2][2]*magD + magZbias;
+
+ // scale magnetometer observation error with total angular rate
+ R_MAG = 0.0025f + sq(0.05f*dAngIMU.length()/dtIMU);
+
+ // Calculate observation jacobians
+ SH_MAG[0] = 2*magD*q3 + 2*magE*q2 + 2*magN*q1;
+ SH_MAG[1] = 2*magD*q0 - 2*magE*q1 + 2*magN*q2;
+ SH_MAG[2] = 2*magD*q1 + 2*magE*q0 - 2*magN*q3;
+ SH_MAG[3] = sq(q3);
+ SH_MAG[4] = sq(q2);
+ SH_MAG[5] = sq(q1);
+ SH_MAG[6] = sq(q0);
+ SH_MAG[7] = 2*magN*q0;
+ SH_MAG[8] = 2*magE*q3;
+
+ for (uint8_t i=0; i<=20; i++) H_MAG[i] = 0;
+ H_MAG[0] = SH_MAG[7] + SH_MAG[8] - 2*magD*q2;
+ H_MAG[1] = SH_MAG[0];
+ H_MAG[2] = 2*magE*q1 - 2*magD*q0 - 2*magN*q2;
+ H_MAG[3] = SH_MAG[2];
+ H_MAG[15] = SH_MAG[5] - SH_MAG[4] - SH_MAG[3] + SH_MAG[6];
+ H_MAG[16] = 2*q0*q3 + 2*q1*q2;
+ H_MAG[17] = 2*q1*q3 - 2*q0*q2;
+ H_MAG[18] = 1.0f;
+
+ // Calculate Kalman gain
+ SK_MX[0] = 1/(P[18][18] + R_MAG + P[1][18]*SH_MAG[0] + P[3][18]*SH_MAG[2] - P[15][18]*(SH_MAG[3] + SH_MAG[4] - SH_MAG[5] - SH_MAG[6]) - (2*magD*q0 - 2*magE*q1 + 2*magN*q2)*(P[18][2] + P[1][2]*SH_MAG[0] + P[3][2]*SH_MAG[2] - P[15][2]*(SH_MAG[3] + SH_MAG[4] - SH_MAG[5] - SH_MAG[6]) + P[16][2]*(2*q0*q3 + 2*q1*q2) - P[17][2]*(2*q0*q2 - 2*q1*q3) - P[2][2]*(2*magD*q0 - 2*magE*q1 + 2*magN*q2) + P[0][2]*(SH_MAG[7] + SH_MAG[8] - 2*magD*q2)) + (SH_MAG[7] + SH_MAG[8] - 2*magD*q2)*(P[18][0] + P[1][0]*SH_MAG[0] + P[3][0]*SH_MAG[2] - P[15][0]*(SH_MAG[3] + SH_MAG[4] - SH_MAG[5] - SH_MAG[6]) + P[16][0]*(2*q0*q3 + 2*q1*q2) - P[17][0]*(2*q0*q2 - 2*q1*q3) - P[2][0]*(2*magD*q0 - 2*magE*q1 + 2*magN*q2) + P[0][0]*(SH_MAG[7] + SH_MAG[8] - 2*magD*q2)) + SH_MAG[0]*(P[18][1] + P[1][1]*SH_MAG[0] + P[3][1]*SH_MAG[2] - P[15][1]*(SH_MAG[3] + SH_MAG[4] - SH_MAG[5] - SH_MAG[6]) + P[16][1]*(2*q0*q3 + 2*q1*q2) - P[17][1]*(2*q0*q2 - 2*q1*q3) - P[2][1]*(2*magD*q0 - 2*magE*q1 + 2*magN*q2) + P[0][1]*(SH_MAG[7] + SH_MAG[8] - 2*magD*q2)) + SH_MAG[2]*(P[18][3] + P[1][3]*SH_MAG[0] + P[3][3]*SH_MAG[2] - P[15][3]*(SH_MAG[3] + SH_MAG[4] - SH_MAG[5] - SH_MAG[6]) + P[16][3]*(2*q0*q3 + 2*q1*q2) - P[17][3]*(2*q0*q2 - 2*q1*q3) - P[2][3]*(2*magD*q0 - 2*magE*q1 + 2*magN*q2) + P[0][3]*(SH_MAG[7] + SH_MAG[8] - 2*magD*q2)) - (SH_MAG[3] + SH_MAG[4] - SH_MAG[5] - SH_MAG[6])*(P[18][15] + P[1][15]*SH_MAG[0] + P[3][15]*SH_MAG[2] - P[15][15]*(SH_MAG[3] + SH_MAG[4] - SH_MAG[5] - SH_MAG[6]) + P[16][15]*(2*q0*q3 + 2*q1*q2) - P[17][15]*(2*q0*q2 - 2*q1*q3) - P[2][15]*(2*magD*q0 - 2*magE*q1 + 2*magN*q2) + P[0][15]*(SH_MAG[7] + SH_MAG[8] - 2*magD*q2)) + P[16][18]*(2*q0*q3 + 2*q1*q2) - P[17][18]*(2*q0*q2 - 2*q1*q3) - P[2][18]*(2*magD*q0 - 2*magE*q1 + 2*magN*q2) + (2*q0*q3 + 2*q1*q2)*(P[18][16] + P[1][16]*SH_MAG[0] + P[3][16]*SH_MAG[2] - P[15][16]*(SH_MAG[3] + SH_MAG[4] - SH_MAG[5] - SH_MAG[6]) + P[16][16]*(2*q0*q3 + 2*q1*q2) - P[17][16]*(2*q0*q2 - 2*q1*q3) - P[2][16]*(2*magD*q0 - 2*magE*q1 + 2*magN*q2) + P[0][16]*(SH_MAG[7] + SH_MAG[8] - 2*magD*q2)) - (2*q0*q2 - 2*q1*q3)*(P[18][17] + P[1][17]*SH_MAG[0] + P[3][17]*SH_MAG[2] - P[15][17]*(SH_MAG[3] + SH_MAG[4] - SH_MAG[5] - SH_MAG[6]) + P[16][17]*(2*q0*q3 + 2*q1*q2) - P[17][17]*(2*q0*q2 - 2*q1*q3) - P[2][17]*(2*magD*q0 - 2*magE*q1 + 2*magN*q2) + P[0][17]*(SH_MAG[7] + SH_MAG[8] - 2*magD*q2)) + P[0][18]*(SH_MAG[7] + SH_MAG[8] - 2*magD*q2));
+ SK_MX[1] = SH_MAG[3] + SH_MAG[4] - SH_MAG[5] - SH_MAG[6];
+ SK_MX[2] = 2*magD*q0 - 2*magE*q1 + 2*magN*q2;
+ SK_MX[3] = SH_MAG[7] + SH_MAG[8] - 2*magD*q2;
+ SK_MX[4] = 2*q0*q2 - 2*q1*q3;
+ SK_MX[5] = 2*q0*q3 + 2*q1*q2;
+ Kfusion[0] = SK_MX[0]*(P[0][18] + P[0][1]*SH_MAG[0] + P[0][3]*SH_MAG[2] + P[0][0]*SK_MX[3] - P[0][2]*SK_MX[2] - P[0][15]*SK_MX[1] + P[0][16]*SK_MX[5] - P[0][17]*SK_MX[4]);
+ Kfusion[1] = SK_MX[0]*(P[1][18] + P[1][1]*SH_MAG[0] + P[1][3]*SH_MAG[2] + P[1][0]*SK_MX[3] - P[1][2]*SK_MX[2] - P[1][15]*SK_MX[1] + P[1][16]*SK_MX[5] - P[1][17]*SK_MX[4]);
+ Kfusion[2] = SK_MX[0]*(P[2][18] + P[2][1]*SH_MAG[0] + P[2][3]*SH_MAG[2] + P[2][0]*SK_MX[3] - P[2][2]*SK_MX[2] - P[2][15]*SK_MX[1] + P[2][16]*SK_MX[5] - P[2][17]*SK_MX[4]);
+ Kfusion[3] = SK_MX[0]*(P[3][18] + P[3][1]*SH_MAG[0] + P[3][3]*SH_MAG[2] + P[3][0]*SK_MX[3] - P[3][2]*SK_MX[2] - P[3][15]*SK_MX[1] + P[3][16]*SK_MX[5] - P[3][17]*SK_MX[4]);
+ Kfusion[4] = SK_MX[0]*(P[4][18] + P[4][1]*SH_MAG[0] + P[4][3]*SH_MAG[2] + P[4][0]*SK_MX[3] - P[4][2]*SK_MX[2] - P[4][15]*SK_MX[1] + P[4][16]*SK_MX[5] - P[4][17]*SK_MX[4]);
+ Kfusion[5] = SK_MX[0]*(P[5][18] + P[5][1]*SH_MAG[0] + P[5][3]*SH_MAG[2] + P[5][0]*SK_MX[3] - P[5][2]*SK_MX[2] - P[5][15]*SK_MX[1] + P[5][16]*SK_MX[5] - P[5][17]*SK_MX[4]);
+ Kfusion[6] = SK_MX[0]*(P[6][18] + P[6][1]*SH_MAG[0] + P[6][3]*SH_MAG[2] + P[6][0]*SK_MX[3] - P[6][2]*SK_MX[2] - P[6][15]*SK_MX[1] + P[6][16]*SK_MX[5] - P[6][17]*SK_MX[4]);
+ Kfusion[7] = SK_MX[0]*(P[7][18] + P[7][1]*SH_MAG[0] + P[7][3]*SH_MAG[2] + P[7][0]*SK_MX[3] - P[7][2]*SK_MX[2] - P[7][15]*SK_MX[1] + P[7][16]*SK_MX[5] - P[7][17]*SK_MX[4]);
+ Kfusion[8] = SK_MX[0]*(P[8][18] + P[8][1]*SH_MAG[0] + P[8][3]*SH_MAG[2] + P[8][0]*SK_MX[3] - P[8][2]*SK_MX[2] - P[8][15]*SK_MX[1] + P[8][16]*SK_MX[5] - P[8][17]*SK_MX[4]);
+ Kfusion[9] = SK_MX[0]*(P[9][18] + P[9][1]*SH_MAG[0] + P[9][3]*SH_MAG[2] + P[9][0]*SK_MX[3] - P[9][2]*SK_MX[2] - P[9][15]*SK_MX[1] + P[9][16]*SK_MX[5] - P[9][17]*SK_MX[4]);
+ Kfusion[10] = SK_MX[0]*(P[10][18] + P[10][1]*SH_MAG[0] + P[10][3]*SH_MAG[2] + P[10][0]*SK_MX[3] - P[10][2]*SK_MX[2] - P[10][15]*SK_MX[1] + P[10][16]*SK_MX[5] - P[10][17]*SK_MX[4]);
+ Kfusion[11] = SK_MX[0]*(P[11][18] + P[11][1]*SH_MAG[0] + P[11][3]*SH_MAG[2] + P[11][0]*SK_MX[3] - P[11][2]*SK_MX[2] - P[11][15]*SK_MX[1] + P[11][16]*SK_MX[5] - P[11][17]*SK_MX[4]);
+ Kfusion[12] = SK_MX[0]*(P[12][18] + P[12][1]*SH_MAG[0] + P[12][3]*SH_MAG[2] + P[12][0]*SK_MX[3] - P[12][2]*SK_MX[2] - P[12][15]*SK_MX[1] + P[12][16]*SK_MX[5] - P[12][17]*SK_MX[4]);
+ Kfusion[13] = SK_MX[0]*(P[13][18] + P[13][1]*SH_MAG[0] + P[13][3]*SH_MAG[2] + P[13][0]*SK_MX[3] - P[13][2]*SK_MX[2] - P[13][15]*SK_MX[1] + P[13][16]*SK_MX[5] - P[13][17]*SK_MX[4]);
+ Kfusion[14] = SK_MX[0]*(P[14][18] + P[14][1]*SH_MAG[0] + P[14][3]*SH_MAG[2] + P[14][0]*SK_MX[3] - P[14][2]*SK_MX[2] - P[14][15]*SK_MX[1] + P[14][16]*SK_MX[5] - P[14][17]*SK_MX[4]);
+ Kfusion[15] = SK_MX[0]*(P[15][18] + P[15][1]*SH_MAG[0] + P[15][3]*SH_MAG[2] + P[15][0]*SK_MX[3] - P[15][2]*SK_MX[2] - P[15][15]*SK_MX[1] + P[15][16]*SK_MX[5] - P[15][17]*SK_MX[4]);
+ Kfusion[16] = SK_MX[0]*(P[16][18] + P[16][1]*SH_MAG[0] + P[16][3]*SH_MAG[2] + P[16][0]*SK_MX[3] - P[16][2]*SK_MX[2] - P[16][15]*SK_MX[1] + P[16][16]*SK_MX[5] - P[16][17]*SK_MX[4]);
+ Kfusion[17] = SK_MX[0]*(P[17][18] + P[17][1]*SH_MAG[0] + P[17][3]*SH_MAG[2] + P[17][0]*SK_MX[3] - P[17][2]*SK_MX[2] - P[17][15]*SK_MX[1] + P[17][16]*SK_MX[5] - P[17][17]*SK_MX[4]);
+ Kfusion[18] = SK_MX[0]*(P[18][18] + P[18][1]*SH_MAG[0] + P[18][3]*SH_MAG[2] + P[18][0]*SK_MX[3] - P[18][2]*SK_MX[2] - P[18][15]*SK_MX[1] + P[18][16]*SK_MX[5] - P[18][17]*SK_MX[4]);
+ Kfusion[19] = SK_MX[0]*(P[19][18] + P[19][1]*SH_MAG[0] + P[19][3]*SH_MAG[2] + P[19][0]*SK_MX[3] - P[19][2]*SK_MX[2] - P[19][15]*SK_MX[1] + P[19][16]*SK_MX[5] - P[19][17]*SK_MX[4]);
+ Kfusion[20] = SK_MX[0]*(P[20][18] + P[20][1]*SH_MAG[0] + P[20][3]*SH_MAG[2] + P[20][0]*SK_MX[3] - P[20][2]*SK_MX[2] - P[20][15]*SK_MX[1] + P[20][16]*SK_MX[5] - P[20][17]*SK_MX[4]);
+ varInnovMag[0] = 1.0f/SK_MX[0];
+ innovMag[0] = MagPred[0] - magData.x;
+
+ // reset the observation index to 0 (we start by fusing the X
+ // measurement)
+ obsIndex = 0;
+ }
+ else if (obsIndex == 1) // we are now fusing the Y measurement
+ {
+ // Calculate observation jacobians
+ for (unsigned int i=0; i<n_states; i++) H_MAG[i] = 0;
+ H_MAG[0] = SH_MAG[2];
+ H_MAG[1] = SH_MAG[1];
+ H_MAG[2] = SH_MAG[0];
+ H_MAG[3] = 2*magD*q2 - SH_MAG[8] - SH_MAG[7];
+ H_MAG[15] = 2*q1*q2 - 2*q0*q3;
+ H_MAG[16] = SH_MAG[4] - SH_MAG[3] - SH_MAG[5] + SH_MAG[6];
+ H_MAG[17] = 2*q0*q1 + 2*q2*q3;
+ H_MAG[19] = 1;
+
+ // Calculate Kalman gain
+ SK_MY[0] = 1/(P[19][19] + R_MAG + P[0][19]*SH_MAG[2] + P[1][19]*SH_MAG[1] + P[2][19]*SH_MAG[0] - P[16][19]*(SH_MAG[3] - SH_MAG[4] + SH_MAG[5] - SH_MAG[6]) - (2*q0*q3 - 2*q1*q2)*(P[19][15] + P[0][15]*SH_MAG[2] + P[1][15]*SH_MAG[1] + P[2][15]*SH_MAG[0] - P[16][15]*(SH_MAG[3] - SH_MAG[4] + SH_MAG[5] - SH_MAG[6]) - P[15][15]*(2*q0*q3 - 2*q1*q2) + P[17][15]*(2*q0*q1 + 2*q2*q3) - P[3][15]*(SH_MAG[7] + SH_MAG[8] - 2*magD*q2)) + (2*q0*q1 + 2*q2*q3)*(P[19][17] + P[0][17]*SH_MAG[2] + P[1][17]*SH_MAG[1] + P[2][17]*SH_MAG[0] - P[16][17]*(SH_MAG[3] - SH_MAG[4] + SH_MAG[5] - SH_MAG[6]) - P[15][17]*(2*q0*q3 - 2*q1*q2) + P[17][17]*(2*q0*q1 + 2*q2*q3) - P[3][17]*(SH_MAG[7] + SH_MAG[8] - 2*magD*q2)) - (SH_MAG[7] + SH_MAG[8] - 2*magD*q2)*(P[19][3] + P[0][3]*SH_MAG[2] + P[1][3]*SH_MAG[1] + P[2][3]*SH_MAG[0] - P[16][3]*(SH_MAG[3] - SH_MAG[4] + SH_MAG[5] - SH_MAG[6]) - P[15][3]*(2*q0*q3 - 2*q1*q2) + P[17][3]*(2*q0*q1 + 2*q2*q3) - P[3][3]*(SH_MAG[7] + SH_MAG[8] - 2*magD*q2)) - P[15][19]*(2*q0*q3 - 2*q1*q2) + P[17][19]*(2*q0*q1 + 2*q2*q3) + SH_MAG[2]*(P[19][0] + P[0][0]*SH_MAG[2] + P[1][0]*SH_MAG[1] + P[2][0]*SH_MAG[0] - P[16][0]*(SH_MAG[3] - SH_MAG[4] + SH_MAG[5] - SH_MAG[6]) - P[15][0]*(2*q0*q3 - 2*q1*q2) + P[17][0]*(2*q0*q1 + 2*q2*q3) - P[3][0]*(SH_MAG[7] + SH_MAG[8] - 2*magD*q2)) + SH_MAG[1]*(P[19][1] + P[0][1]*SH_MAG[2] + P[1][1]*SH_MAG[1] + P[2][1]*SH_MAG[0] - P[16][1]*(SH_MAG[3] - SH_MAG[4] + SH_MAG[5] - SH_MAG[6]) - P[15][1]*(2*q0*q3 - 2*q1*q2) + P[17][1]*(2*q0*q1 + 2*q2*q3) - P[3][1]*(SH_MAG[7] + SH_MAG[8] - 2*magD*q2)) + SH_MAG[0]*(P[19][2] + P[0][2]*SH_MAG[2] + P[1][2]*SH_MAG[1] + P[2][2]*SH_MAG[0] - P[16][2]*(SH_MAG[3] - SH_MAG[4] + SH_MAG[5] - SH_MAG[6]) - P[15][2]*(2*q0*q3 - 2*q1*q2) + P[17][2]*(2*q0*q1 + 2*q2*q3) - P[3][2]*(SH_MAG[7] + SH_MAG[8] - 2*magD*q2)) - (SH_MAG[3] - SH_MAG[4] + SH_MAG[5] - SH_MAG[6])*(P[19][16] + P[0][16]*SH_MAG[2] + P[1][16]*SH_MAG[1] + P[2][16]*SH_MAG[0] - P[16][16]*(SH_MAG[3] - SH_MAG[4] + SH_MAG[5] - SH_MAG[6]) - P[15][16]*(2*q0*q3 - 2*q1*q2) + P[17][16]*(2*q0*q1 + 2*q2*q3) - P[3][16]*(SH_MAG[7] + SH_MAG[8] - 2*magD*q2)) - P[3][19]*(SH_MAG[7] + SH_MAG[8] - 2*magD*q2));
+ SK_MY[1] = SH_MAG[3] - SH_MAG[4] + SH_MAG[5] - SH_MAG[6];
+ SK_MY[2] = SH_MAG[7] + SH_MAG[8] - 2*magD*q2;
+ SK_MY[3] = 2*q0*q3 - 2*q1*q2;
+ SK_MY[4] = 2*q0*q1 + 2*q2*q3;
+ Kfusion[0] = SK_MY[0]*(P[0][19] + P[0][0]*SH_MAG[2] + P[0][1]*SH_MAG[1] + P[0][2]*SH_MAG[0] - P[0][3]*SK_MY[2] - P[0][16]*SK_MY[1] - P[0][15]*SK_MY[3] + P[0][17]*SK_MY[4]);
+ Kfusion[1] = SK_MY[0]*(P[1][19] + P[1][0]*SH_MAG[2] + P[1][1]*SH_MAG[1] + P[1][2]*SH_MAG[0] - P[1][3]*SK_MY[2] - P[1][16]*SK_MY[1] - P[1][15]*SK_MY[3] + P[1][17]*SK_MY[4]);
+ Kfusion[2] = SK_MY[0]*(P[2][19] + P[2][0]*SH_MAG[2] + P[2][1]*SH_MAG[1] + P[2][2]*SH_MAG[0] - P[2][3]*SK_MY[2] - P[2][16]*SK_MY[1] - P[2][15]*SK_MY[3] + P[2][17]*SK_MY[4]);
+ Kfusion[3] = SK_MY[0]*(P[3][19] + P[3][0]*SH_MAG[2] + P[3][1]*SH_MAG[1] + P[3][2]*SH_MAG[0] - P[3][3]*SK_MY[2] - P[3][16]*SK_MY[1] - P[3][15]*SK_MY[3] + P[3][17]*SK_MY[4]);
+ Kfusion[4] = SK_MY[0]*(P[4][19] + P[4][0]*SH_MAG[2] + P[4][1]*SH_MAG[1] + P[4][2]*SH_MAG[0] - P[4][3]*SK_MY[2] - P[4][16]*SK_MY[1] - P[4][15]*SK_MY[3] + P[4][17]*SK_MY[4]);
+ Kfusion[5] = SK_MY[0]*(P[5][19] + P[5][0]*SH_MAG[2] + P[5][1]*SH_MAG[1] + P[5][2]*SH_MAG[0] - P[5][3]*SK_MY[2] - P[5][16]*SK_MY[1] - P[5][15]*SK_MY[3] + P[5][17]*SK_MY[4]);
+ Kfusion[6] = SK_MY[0]*(P[6][19] + P[6][0]*SH_MAG[2] + P[6][1]*SH_MAG[1] + P[6][2]*SH_MAG[0] - P[6][3]*SK_MY[2] - P[6][16]*SK_MY[1] - P[6][15]*SK_MY[3] + P[6][17]*SK_MY[4]);
+ Kfusion[7] = SK_MY[0]*(P[7][19] + P[7][0]*SH_MAG[2] + P[7][1]*SH_MAG[1] + P[7][2]*SH_MAG[0] - P[7][3]*SK_MY[2] - P[7][16]*SK_MY[1] - P[7][15]*SK_MY[3] + P[7][17]*SK_MY[4]);
+ Kfusion[8] = SK_MY[0]*(P[8][19] + P[8][0]*SH_MAG[2] + P[8][1]*SH_MAG[1] + P[8][2]*SH_MAG[0] - P[8][3]*SK_MY[2] - P[8][16]*SK_MY[1] - P[8][15]*SK_MY[3] + P[8][17]*SK_MY[4]);
+ Kfusion[9] = SK_MY[0]*(P[9][19] + P[9][0]*SH_MAG[2] + P[9][1]*SH_MAG[1] + P[9][2]*SH_MAG[0] - P[9][3]*SK_MY[2] - P[9][16]*SK_MY[1] - P[9][15]*SK_MY[3] + P[9][17]*SK_MY[4]);
+ Kfusion[10] = SK_MY[0]*(P[10][19] + P[10][0]*SH_MAG[2] + P[10][1]*SH_MAG[1] + P[10][2]*SH_MAG[0] - P[10][3]*SK_MY[2] - P[10][16]*SK_MY[1] - P[10][15]*SK_MY[3] + P[10][17]*SK_MY[4]);
+ Kfusion[11] = SK_MY[0]*(P[11][19] + P[11][0]*SH_MAG[2] + P[11][1]*SH_MAG[1] + P[11][2]*SH_MAG[0] - P[11][3]*SK_MY[2] - P[11][16]*SK_MY[1] - P[11][15]*SK_MY[3] + P[11][17]*SK_MY[4]);
+ Kfusion[12] = SK_MY[0]*(P[12][19] + P[12][0]*SH_MAG[2] + P[12][1]*SH_MAG[1] + P[12][2]*SH_MAG[0] - P[12][3]*SK_MY[2] - P[12][16]*SK_MY[1] - P[12][15]*SK_MY[3] + P[12][17]*SK_MY[4]);
+ Kfusion[13] = SK_MY[0]*(P[13][19] + P[13][0]*SH_MAG[2] + P[13][1]*SH_MAG[1] + P[13][2]*SH_MAG[0] - P[13][3]*SK_MY[2] - P[13][16]*SK_MY[1] - P[13][15]*SK_MY[3] + P[13][17]*SK_MY[4]);
+ Kfusion[14] = SK_MY[0]*(P[14][19] + P[14][0]*SH_MAG[2] + P[14][1]*SH_MAG[1] + P[14][2]*SH_MAG[0] - P[14][3]*SK_MY[2] - P[14][16]*SK_MY[1] - P[14][15]*SK_MY[3] + P[14][17]*SK_MY[4]);
+ Kfusion[15] = SK_MY[0]*(P[15][19] + P[15][0]*SH_MAG[2] + P[15][1]*SH_MAG[1] + P[15][2]*SH_MAG[0] - P[15][3]*SK_MY[2] - P[15][16]*SK_MY[1] - P[15][15]*SK_MY[3] + P[15][17]*SK_MY[4]);
+ Kfusion[16] = SK_MY[0]*(P[16][19] + P[16][0]*SH_MAG[2] + P[16][1]*SH_MAG[1] + P[16][2]*SH_MAG[0] - P[16][3]*SK_MY[2] - P[16][16]*SK_MY[1] - P[16][15]*SK_MY[3] + P[16][17]*SK_MY[4]);
+ Kfusion[17] = SK_MY[0]*(P[17][19] + P[17][0]*SH_MAG[2] + P[17][1]*SH_MAG[1] + P[17][2]*SH_MAG[0] - P[17][3]*SK_MY[2] - P[17][16]*SK_MY[1] - P[17][15]*SK_MY[3] + P[17][17]*SK_MY[4]);
+ Kfusion[18] = SK_MY[0]*(P[18][19] + P[18][0]*SH_MAG[2] + P[18][1]*SH_MAG[1] + P[18][2]*SH_MAG[0] - P[18][3]*SK_MY[2] - P[18][16]*SK_MY[1] - P[18][15]*SK_MY[3] + P[18][17]*SK_MY[4]);
+ Kfusion[19] = SK_MY[0]*(P[19][19] + P[19][0]*SH_MAG[2] + P[19][1]*SH_MAG[1] + P[19][2]*SH_MAG[0] - P[19][3]*SK_MY[2] - P[19][16]*SK_MY[1] - P[19][15]*SK_MY[3] + P[19][17]*SK_MY[4]);
+ Kfusion[20] = SK_MY[0]*(P[20][19] + P[20][0]*SH_MAG[2] + P[20][1]*SH_MAG[1] + P[20][2]*SH_MAG[0] - P[20][3]*SK_MY[2] - P[20][16]*SK_MY[1] - P[20][15]*SK_MY[3] + P[20][17]*SK_MY[4]);
+ varInnovMag[1] = 1.0f/SK_MY[0];
+ innovMag[1] = MagPred[1] - magData.y;
+ }
+ else if (obsIndex == 2) // we are now fusing the Z measurement
+ {
+ // Calculate observation jacobians
+ for (uint8_t i=0; i<=20; i++) H_MAG[i] = 0;
+ H_MAG[0] = SH_MAG[1];
+ H_MAG[1] = 2*magN*q3 - 2*magE*q0 - 2*magD*q1;
+ H_MAG[2] = SH_MAG[7] + SH_MAG[8] - 2*magD*q2;
+ H_MAG[3] = SH_MAG[0];
+ H_MAG[15] = 2*q0*q2 + 2*q1*q3;
+ H_MAG[16] = 2*q2*q3 - 2*q0*q1;
+ H_MAG[17] = SH_MAG[3] - SH_MAG[4] - SH_MAG[5] + SH_MAG[6];
+ H_MAG[20] = 1;
+
+ // Calculate Kalman gain
+ SK_MZ[0] = 1/(P[20][20] + R_MAG + P[0][20]*SH_MAG[1] + P[3][20]*SH_MAG[0] + P[17][20]*(SH_MAG[3] - SH_MAG[4] - SH_MAG[5] + SH_MAG[6]) - (2*magD*q1 + 2*magE*q0 - 2*magN*q3)*(P[20][1] + P[0][1]*SH_MAG[1] + P[3][1]*SH_MAG[0] + P[17][1]*(SH_MAG[3] - SH_MAG[4] - SH_MAG[5] + SH_MAG[6]) + P[15][1]*(2*q0*q2 + 2*q1*q3) - P[16][1]*(2*q0*q1 - 2*q2*q3) - P[1][1]*(2*magD*q1 + 2*magE*q0 - 2*magN*q3) + P[2][1]*(SH_MAG[7] + SH_MAG[8] - 2*magD*q2)) + (SH_MAG[7] + SH_MAG[8] - 2*magD*q2)*(P[20][2] + P[0][2]*SH_MAG[1] + P[3][2]*SH_MAG[0] + P[17][2]*(SH_MAG[3] - SH_MAG[4] - SH_MAG[5] + SH_MAG[6]) + P[15][2]*(2*q0*q2 + 2*q1*q3) - P[16][2]*(2*q0*q1 - 2*q2*q3) - P[1][2]*(2*magD*q1 + 2*magE*q0 - 2*magN*q3) + P[2][2]*(SH_MAG[7] + SH_MAG[8] - 2*magD*q2)) + SH_MAG[1]*(P[20][0] + P[0][0]*SH_MAG[1] + P[3][0]*SH_MAG[0] + P[17][0]*(SH_MAG[3] - SH_MAG[4] - SH_MAG[5] + SH_MAG[6]) + P[15][0]*(2*q0*q2 + 2*q1*q3) - P[16][0]*(2*q0*q1 - 2*q2*q3) - P[1][0]*(2*magD*q1 + 2*magE*q0 - 2*magN*q3) + P[2][0]*(SH_MAG[7] + SH_MAG[8] - 2*magD*q2)) + SH_MAG[0]*(P[20][3] + P[0][3]*SH_MAG[1] + P[3][3]*SH_MAG[0] + P[17][3]*(SH_MAG[3] - SH_MAG[4] - SH_MAG[5] + SH_MAG[6]) + P[15][3]*(2*q0*q2 + 2*q1*q3) - P[16][3]*(2*q0*q1 - 2*q2*q3) - P[1][3]*(2*magD*q1 + 2*magE*q0 - 2*magN*q3) + P[2][3]*(SH_MAG[7] + SH_MAG[8] - 2*magD*q2)) + (SH_MAG[3] - SH_MAG[4] - SH_MAG[5] + SH_MAG[6])*(P[20][17] + P[0][17]*SH_MAG[1] + P[3][17]*SH_MAG[0] + P[17][17]*(SH_MAG[3] - SH_MAG[4] - SH_MAG[5] + SH_MAG[6]) + P[15][17]*(2*q0*q2 + 2*q1*q3) - P[16][17]*(2*q0*q1 - 2*q2*q3) - P[1][17]*(2*magD*q1 + 2*magE*q0 - 2*magN*q3) + P[2][17]*(SH_MAG[7] + SH_MAG[8] - 2*magD*q2)) + P[15][20]*(2*q0*q2 + 2*q1*q3) - P[16][20]*(2*q0*q1 - 2*q2*q3) - P[1][20]*(2*magD*q1 + 2*magE*q0 - 2*magN*q3) + (2*q0*q2 + 2*q1*q3)*(P[20][15] + P[0][15]*SH_MAG[1] + P[3][15]*SH_MAG[0] + P[17][15]*(SH_MAG[3] - SH_MAG[4] - SH_MAG[5] + SH_MAG[6]) + P[15][15]*(2*q0*q2 + 2*q1*q3) - P[16][15]*(2*q0*q1 - 2*q2*q3) - P[1][15]*(2*magD*q1 + 2*magE*q0 - 2*magN*q3) + P[2][15]*(SH_MAG[7] + SH_MAG[8] - 2*magD*q2)) - (2*q0*q1 - 2*q2*q3)*(P[20][16] + P[0][16]*SH_MAG[1] + P[3][16]*SH_MAG[0] + P[17][16]*(SH_MAG[3] - SH_MAG[4] - SH_MAG[5] + SH_MAG[6]) + P[15][16]*(2*q0*q2 + 2*q1*q3) - P[16][16]*(2*q0*q1 - 2*q2*q3) - P[1][16]*(2*magD*q1 + 2*magE*q0 - 2*magN*q3) + P[2][16]*(SH_MAG[7] + SH_MAG[8] - 2*magD*q2)) + P[2][20]*(SH_MAG[7] + SH_MAG[8] - 2*magD*q2));
+ SK_MZ[1] = SH_MAG[3] - SH_MAG[4] - SH_MAG[5] + SH_MAG[6];
+ SK_MZ[2] = 2*magD*q1 + 2*magE*q0 - 2*magN*q3;
+ SK_MZ[3] = SH_MAG[7] + SH_MAG[8] - 2*magD*q2;
+ SK_MZ[4] = 2*q0*q1 - 2*q2*q3;
+ SK_MZ[5] = 2*q0*q2 + 2*q1*q3;
+ Kfusion[0] = SK_MZ[0]*(P[0][20] + P[0][0]*SH_MAG[1] + P[0][3]*SH_MAG[0] - P[0][1]*SK_MZ[2] + P[0][2]*SK_MZ[3] + P[0][17]*SK_MZ[1] + P[0][15]*SK_MZ[5] - P[0][16]*SK_MZ[4]);
+ Kfusion[1] = SK_MZ[0]*(P[1][20] + P[1][0]*SH_MAG[1] + P[1][3]*SH_MAG[0] - P[1][1]*SK_MZ[2] + P[1][2]*SK_MZ[3] + P[1][17]*SK_MZ[1] + P[1][15]*SK_MZ[5] - P[1][16]*SK_MZ[4]);
+ Kfusion[2] = SK_MZ[0]*(P[2][20] + P[2][0]*SH_MAG[1] + P[2][3]*SH_MAG[0] - P[2][1]*SK_MZ[2] + P[2][2]*SK_MZ[3] + P[2][17]*SK_MZ[1] + P[2][15]*SK_MZ[5] - P[2][16]*SK_MZ[4]);
+ Kfusion[3] = SK_MZ[0]*(P[3][20] + P[3][0]*SH_MAG[1] + P[3][3]*SH_MAG[0] - P[3][1]*SK_MZ[2] + P[3][2]*SK_MZ[3] + P[3][17]*SK_MZ[1] + P[3][15]*SK_MZ[5] - P[3][16]*SK_MZ[4]);
+ Kfusion[4] = SK_MZ[0]*(P[4][20] + P[4][0]*SH_MAG[1] + P[4][3]*SH_MAG[0] - P[4][1]*SK_MZ[2] + P[4][2]*SK_MZ[3] + P[4][17]*SK_MZ[1] + P[4][15]*SK_MZ[5] - P[4][16]*SK_MZ[4]);
+ Kfusion[5] = SK_MZ[0]*(P[5][20] + P[5][0]*SH_MAG[1] + P[5][3]*SH_MAG[0] - P[5][1]*SK_MZ[2] + P[5][2]*SK_MZ[3] + P[5][17]*SK_MZ[1] + P[5][15]*SK_MZ[5] - P[5][16]*SK_MZ[4]);
+ Kfusion[6] = SK_MZ[0]*(P[6][20] + P[6][0]*SH_MAG[1] + P[6][3]*SH_MAG[0] - P[6][1]*SK_MZ[2] + P[6][2]*SK_MZ[3] + P[6][17]*SK_MZ[1] + P[6][15]*SK_MZ[5] - P[6][16]*SK_MZ[4]);
+ Kfusion[7] = SK_MZ[0]*(P[7][20] + P[7][0]*SH_MAG[1] + P[7][3]*SH_MAG[0] - P[7][1]*SK_MZ[2] + P[7][2]*SK_MZ[3] + P[7][17]*SK_MZ[1] + P[7][15]*SK_MZ[5] - P[7][16]*SK_MZ[4]);
+ Kfusion[8] = SK_MZ[0]*(P[8][20] + P[8][0]*SH_MAG[1] + P[8][3]*SH_MAG[0] - P[8][1]*SK_MZ[2] + P[8][2]*SK_MZ[3] + P[8][17]*SK_MZ[1] + P[8][15]*SK_MZ[5] - P[8][16]*SK_MZ[4]);
+ Kfusion[9] = SK_MZ[0]*(P[9][20] + P[9][0]*SH_MAG[1] + P[9][3]*SH_MAG[0] - P[9][1]*SK_MZ[2] + P[9][2]*SK_MZ[3] + P[9][17]*SK_MZ[1] + P[9][15]*SK_MZ[5] - P[9][16]*SK_MZ[4]);
+ Kfusion[10] = SK_MZ[0]*(P[10][20] + P[10][0]*SH_MAG[1] + P[10][3]*SH_MAG[0] - P[10][1]*SK_MZ[2] + P[10][2]*SK_MZ[3] + P[10][17]*SK_MZ[1] + P[10][15]*SK_MZ[5] - P[10][16]*SK_MZ[4]);
+ Kfusion[11] = SK_MZ[0]*(P[11][20] + P[11][0]*SH_MAG[1] + P[11][3]*SH_MAG[0] - P[11][1]*SK_MZ[2] + P[11][2]*SK_MZ[3] + P[11][17]*SK_MZ[1] + P[11][15]*SK_MZ[5] - P[11][16]*SK_MZ[4]);
+ Kfusion[12] = SK_MZ[0]*(P[12][20] + P[12][0]*SH_MAG[1] + P[12][3]*SH_MAG[0] - P[12][1]*SK_MZ[2] + P[12][2]*SK_MZ[3] + P[12][17]*SK_MZ[1] + P[12][15]*SK_MZ[5] - P[12][16]*SK_MZ[4]);
+ Kfusion[13] = SK_MZ[0]*(P[13][20] + P[13][0]*SH_MAG[1] + P[13][3]*SH_MAG[0] - P[13][1]*SK_MZ[2] + P[13][2]*SK_MZ[3] + P[13][17]*SK_MZ[1] + P[13][15]*SK_MZ[5] - P[13][16]*SK_MZ[4]);
+ Kfusion[14] = SK_MZ[0]*(P[14][20] + P[14][0]*SH_MAG[1] + P[14][3]*SH_MAG[0] - P[14][1]*SK_MZ[2] + P[14][2]*SK_MZ[3] + P[14][17]*SK_MZ[1] + P[14][15]*SK_MZ[5] - P[14][16]*SK_MZ[4]);
+ Kfusion[15] = SK_MZ[0]*(P[15][20] + P[15][0]*SH_MAG[1] + P[15][3]*SH_MAG[0] - P[15][1]*SK_MZ[2] + P[15][2]*SK_MZ[3] + P[15][17]*SK_MZ[1] + P[15][15]*SK_MZ[5] - P[15][16]*SK_MZ[4]);
+ Kfusion[16] = SK_MZ[0]*(P[16][20] + P[16][0]*SH_MAG[1] + P[16][3]*SH_MAG[0] - P[16][1]*SK_MZ[2] + P[16][2]*SK_MZ[3] + P[16][17]*SK_MZ[1] + P[16][15]*SK_MZ[5] - P[16][16]*SK_MZ[4]);
+ Kfusion[17] = SK_MZ[0]*(P[17][20] + P[17][0]*SH_MAG[1] + P[17][3]*SH_MAG[0] - P[17][1]*SK_MZ[2] + P[17][2]*SK_MZ[3] + P[17][17]*SK_MZ[1] + P[17][15]*SK_MZ[5] - P[17][16]*SK_MZ[4]);
+ Kfusion[18] = SK_MZ[0]*(P[18][20] + P[18][0]*SH_MAG[1] + P[18][3]*SH_MAG[0] - P[18][1]*SK_MZ[2] + P[18][2]*SK_MZ[3] + P[18][17]*SK_MZ[1] + P[18][15]*SK_MZ[5] - P[18][16]*SK_MZ[4]);
+ Kfusion[19] = SK_MZ[0]*(P[19][20] + P[19][0]*SH_MAG[1] + P[19][3]*SH_MAG[0] - P[19][1]*SK_MZ[2] + P[19][2]*SK_MZ[3] + P[19][17]*SK_MZ[1] + P[19][15]*SK_MZ[5] - P[19][16]*SK_MZ[4]);
+ Kfusion[20] = SK_MZ[0]*(P[20][20] + P[20][0]*SH_MAG[1] + P[20][3]*SH_MAG[0] - P[20][1]*SK_MZ[2] + P[20][2]*SK_MZ[3] + P[20][17]*SK_MZ[1] + P[20][15]*SK_MZ[5] - P[20][16]*SK_MZ[4]);
+ varInnovMag[2] = 1.0f/SK_MZ[0];
+ innovMag[2] = MagPred[2] - magData.z;
+
+ }
+
+ // Check the innovation for consistency and don't fuse if > 5Sigma
+ if ((innovMag[obsIndex]*innovMag[obsIndex]/varInnovMag[obsIndex]) < 25.0)
+ {
+ // correct the state vector
+ for (uint8_t j= 0; j<=indexLimit; j++)
+ {
+ states[j] = states[j] - Kfusion[j] * innovMag[obsIndex];
+ }
+ // normalise the quaternion states
+ float quatMag = sqrt(states[0]*states[0] + states[1]*states[1] + states[2]*states[2] + states[3]*states[3]);
+ if (quatMag > 1e-12)
+ {
+ for (uint8_t j= 0; j<=3; j++)
+ {
+ float quatMagInv = 1.0f/quatMag;
+ states[j] = states[j] * quatMagInv;
+ }
+ }
+ // correct the covariance P = (I - K*H)*P
+ // take advantage of the empty columns in KH to reduce the
+ // number of operations
+ for (uint8_t i = 0; i<=indexLimit; i++)
+ {
+ for (uint8_t j = 0; j<=3; j++)
+ {
+ KH[i][j] = Kfusion[i] * H_MAG[j];
+ }
+ for (uint8_t j = 4; j<=17; j++) KH[i][j] = 0.0f;
+ if (!onGround)
+ {
+ for (uint8_t j = 15; j<=20; j++)
+ {
+ KH[i][j] = Kfusion[i] * H_MAG[j];
+ }
+ }
+ else
+ {
+ for (uint8_t j = 15; j<=20; j++)
+ {
+ KH[i][j] = 0.0f;
+ }
+ }
+ }
+ for (uint8_t i = 0; i<=indexLimit; i++)
+ {
+ for (uint8_t j = 0; j<=indexLimit; j++)
+ {
+ KHP[i][j] = 0.0f;
+ for (uint8_t k = 0; k<=3; k++)
+ {
+ KHP[i][j] = KHP[i][j] + KH[i][k] * P[k][j];
+ }
+ if (!onGround)
+ {
+ for (uint8_t k = 15; k<=20; k++)
+ {
+ KHP[i][j] = KHP[i][j] + KH[i][k] * P[k][j];
+ }
+ }
+ }
+ }
+ }
+ for (uint8_t i = 0; i<=indexLimit; i++)
+ {
+ for (uint8_t j = 0; j<=indexLimit; j++)
+ {
+ P[i][j] = P[i][j] - KHP[i][j];
+ }
+ }
+ }
+ obsIndex = obsIndex + 1;
+
+ ForceSymmetry();
+ ConstrainVariances();
+}
+
+void AttPosEKF::FuseAirspeed()
+{
+ float vn;
+ float ve;
+ float vd;
+ float vwn;
+ float vwe;
+ const float R_TAS = 2.0f;
+ float SH_TAS[3];
+ float Kfusion[21];
+ float VtasPred;
+
+ // Copy required states to local variable names
+ vn = statesAtVtasMeasTime[4];
+ ve = statesAtVtasMeasTime[5];
+ vd = statesAtVtasMeasTime[6];
+ vwn = statesAtVtasMeasTime[13];
+ vwe = statesAtVtasMeasTime[14];
+
+ // Need to check that it is flying before fusing airspeed data
+ // Calculate the predicted airspeed
+ VtasPred = sqrtf((ve - vwe)*(ve - vwe) + (vn - vwn)*(vn - vwn) + vd*vd);
+ // Perform fusion of True Airspeed measurement
+ if (useAirspeed && fuseVtasData && (VtasPred > 1.0f) && (VtasMeas > 8.0f))
+ {
+ // Calculate observation jacobians
+ SH_TAS[0] = 1/(sqrt(sq(ve - vwe) + sq(vn - vwn) + sq(vd)));
+ SH_TAS[1] = (SH_TAS[0]*(2.0f*ve - 2*vwe))/2.0f;
+ SH_TAS[2] = (SH_TAS[0]*(2.0f*vn - 2*vwn))/2.0f;
+
+ float H_TAS[21];
+ for (uint8_t i=0; i<=20; i++) H_TAS[i] = 0.0f;
+ H_TAS[4] = SH_TAS[2];
+ H_TAS[5] = SH_TAS[1];
+ H_TAS[6] = vd*SH_TAS[0];
+ H_TAS[13] = -SH_TAS[2];
+ H_TAS[14] = -SH_TAS[1];
+
+ // Calculate Kalman gains
+ float SK_TAS = 1.0f/(R_TAS + SH_TAS[2]*(P[4][4]*SH_TAS[2] + P[5][4]*SH_TAS[1] - P[13][4]*SH_TAS[2] - P[14][4]*SH_TAS[1] + P[6][4]*vd*SH_TAS[0]) + SH_TAS[1]*(P[4][5]*SH_TAS[2] + P[5][5]*SH_TAS[1] - P[13][5]*SH_TAS[2] - P[14][5]*SH_TAS[1] + P[6][5]*vd*SH_TAS[0]) - SH_TAS[2]*(P[4][13]*SH_TAS[2] + P[5][13]*SH_TAS[1] - P[13][13]*SH_TAS[2] - P[14][13]*SH_TAS[1] + P[6][13]*vd*SH_TAS[0]) - SH_TAS[1]*(P[4][14]*SH_TAS[2] + P[5][14]*SH_TAS[1] - P[13][14]*SH_TAS[2] - P[14][14]*SH_TAS[1] + P[6][14]*vd*SH_TAS[0]) + vd*SH_TAS[0]*(P[4][6]*SH_TAS[2] + P[5][6]*SH_TAS[1] - P[13][6]*SH_TAS[2] - P[14][6]*SH_TAS[1] + P[6][6]*vd*SH_TAS[0]));
+ Kfusion[0] = SK_TAS*(P[0][4]*SH_TAS[2] - P[0][13]*SH_TAS[2] + P[0][5]*SH_TAS[1] - P[0][14]*SH_TAS[1] + P[0][6]*vd*SH_TAS[0]);
+ Kfusion[1] = SK_TAS*(P[1][4]*SH_TAS[2] - P[1][13]*SH_TAS[2] + P[1][5]*SH_TAS[1] - P[1][14]*SH_TAS[1] + P[1][6]*vd*SH_TAS[0]);
+ Kfusion[2] = SK_TAS*(P[2][4]*SH_TAS[2] - P[2][13]*SH_TAS[2] + P[2][5]*SH_TAS[1] - P[2][14]*SH_TAS[1] + P[2][6]*vd*SH_TAS[0]);
+ Kfusion[3] = SK_TAS*(P[3][4]*SH_TAS[2] - P[3][13]*SH_TAS[2] + P[3][5]*SH_TAS[1] - P[3][14]*SH_TAS[1] + P[3][6]*vd*SH_TAS[0]);
+ Kfusion[4] = SK_TAS*(P[4][4]*SH_TAS[2] - P[4][13]*SH_TAS[2] + P[4][5]*SH_TAS[1] - P[4][14]*SH_TAS[1] + P[4][6]*vd*SH_TAS[0]);
+ Kfusion[5] = SK_TAS*(P[5][4]*SH_TAS[2] - P[5][13]*SH_TAS[2] + P[5][5]*SH_TAS[1] - P[5][14]*SH_TAS[1] + P[5][6]*vd*SH_TAS[0]);
+ Kfusion[6] = SK_TAS*(P[6][4]*SH_TAS[2] - P[6][13]*SH_TAS[2] + P[6][5]*SH_TAS[1] - P[6][14]*SH_TAS[1] + P[6][6]*vd*SH_TAS[0]);
+ Kfusion[7] = SK_TAS*(P[7][4]*SH_TAS[2] - P[7][13]*SH_TAS[2] + P[7][5]*SH_TAS[1] - P[7][14]*SH_TAS[1] + P[7][6]*vd*SH_TAS[0]);
+ Kfusion[8] = SK_TAS*(P[8][4]*SH_TAS[2] - P[8][13]*SH_TAS[2] + P[8][5]*SH_TAS[1] - P[8][14]*SH_TAS[1] + P[8][6]*vd*SH_TAS[0]);
+ Kfusion[9] = SK_TAS*(P[9][4]*SH_TAS[2] - P[9][13]*SH_TAS[2] + P[9][5]*SH_TAS[1] - P[9][14]*SH_TAS[1] + P[9][6]*vd*SH_TAS[0]);
+ Kfusion[10] = SK_TAS*(P[10][4]*SH_TAS[2] - P[10][13]*SH_TAS[2] + P[10][5]*SH_TAS[1] - P[10][14]*SH_TAS[1] + P[10][6]*vd*SH_TAS[0]);
+ Kfusion[11] = SK_TAS*(P[11][4]*SH_TAS[2] - P[11][13]*SH_TAS[2] + P[11][5]*SH_TAS[1] - P[11][14]*SH_TAS[1] + P[11][6]*vd*SH_TAS[0]);
+ Kfusion[12] = SK_TAS*(P[12][4]*SH_TAS[2] - P[12][13]*SH_TAS[2] + P[12][5]*SH_TAS[1] - P[12][14]*SH_TAS[1] + P[12][6]*vd*SH_TAS[0]);
+ Kfusion[13] = SK_TAS*(P[13][4]*SH_TAS[2] - P[13][13]*SH_TAS[2] + P[13][5]*SH_TAS[1] - P[13][14]*SH_TAS[1] + P[13][6]*vd*SH_TAS[0]);
+ Kfusion[14] = SK_TAS*(P[14][4]*SH_TAS[2] - P[14][13]*SH_TAS[2] + P[14][5]*SH_TAS[1] - P[14][14]*SH_TAS[1] + P[14][6]*vd*SH_TAS[0]);
+ Kfusion[15] = SK_TAS*(P[15][4]*SH_TAS[2] - P[15][13]*SH_TAS[2] + P[15][5]*SH_TAS[1] - P[15][14]*SH_TAS[1] + P[15][6]*vd*SH_TAS[0]);
+ Kfusion[16] = SK_TAS*(P[16][4]*SH_TAS[2] - P[16][13]*SH_TAS[2] + P[16][5]*SH_TAS[1] - P[16][14]*SH_TAS[1] + P[16][6]*vd*SH_TAS[0]);
+ Kfusion[17] = SK_TAS*(P[17][4]*SH_TAS[2] - P[17][13]*SH_TAS[2] + P[17][5]*SH_TAS[1] - P[17][14]*SH_TAS[1] + P[17][6]*vd*SH_TAS[0]);
+ Kfusion[18] = SK_TAS*(P[18][4]*SH_TAS[2] - P[18][13]*SH_TAS[2] + P[18][5]*SH_TAS[1] - P[18][14]*SH_TAS[1] + P[18][6]*vd*SH_TAS[0]);
+ Kfusion[19] = SK_TAS*(P[19][4]*SH_TAS[2] - P[19][13]*SH_TAS[2] + P[19][5]*SH_TAS[1] - P[19][14]*SH_TAS[1] + P[19][6]*vd*SH_TAS[0]);
+ Kfusion[20] = SK_TAS*(P[20][4]*SH_TAS[2] - P[20][13]*SH_TAS[2] + P[20][5]*SH_TAS[1] - P[20][14]*SH_TAS[1] + P[20][6]*vd*SH_TAS[0]);
+ varInnovVtas = 1.0f/SK_TAS;
+
+ // Calculate the measurement innovation
+ innovVtas = VtasPred - VtasMeas;
+ // Check the innovation for consistency and don't fuse if > 5Sigma
+ if ((innovVtas*innovVtas*SK_TAS) < 25.0)
+ {
+ // correct the state vector
+ for (uint8_t j=0; j<=20; j++)
+ {
+ states[j] = states[j] - Kfusion[j] * innovVtas;
+ }
+ // normalise the quaternion states
+ float quatMag = sqrt(states[0]*states[0] + states[1]*states[1] + states[2]*states[2] + states[3]*states[3]);
+ if (quatMag > 1e-12f)
+ {
+ for (uint8_t j= 0; j<=3; j++)
+ {
+ float quatMagInv = 1.0f/quatMag;
+ states[j] = states[j] * quatMagInv;
+ }
+ }
+ // correct the covariance P = (I - K*H)*P
+ // take advantage of the empty columns in H to reduce the
+ // number of operations
+ for (uint8_t i = 0; i<=20; i++)
+ {
+ for (uint8_t j = 0; j<=3; j++) KH[i][j] = 0.0;
+ for (uint8_t j = 4; j<=6; j++)
+ {
+ KH[i][j] = Kfusion[i] * H_TAS[j];
+ }
+ for (uint8_t j = 7; j<=12; j++) KH[i][j] = 0.0;
+ for (uint8_t j = 13; j<=14; j++)
+ {
+ KH[i][j] = Kfusion[i] * H_TAS[j];
+ }
+ for (uint8_t j = 15; j<=20; j++) KH[i][j] = 0.0;
+ }
+ for (uint8_t i = 0; i<=20; i++)
+ {
+ for (uint8_t j = 0; j<=20; j++)
+ {
+ KHP[i][j] = 0.0;
+ for (uint8_t k = 4; k<=6; k++)
+ {
+ KHP[i][j] = KHP[i][j] + KH[i][k] * P[k][j];
+ }
+ for (uint8_t k = 13; k<=14; k++)
+ {
+ KHP[i][j] = KHP[i][j] + KH[i][k] * P[k][j];
+ }
+ }
+ }
+ for (uint8_t i = 0; i<=20; i++)
+ {
+ for (uint8_t j = 0; j<=20; j++)
+ {
+ P[i][j] = P[i][j] - KHP[i][j];
+ }
+ }
+ }
+ }
+
+ ForceSymmetry();
+ ConstrainVariances();
+}
+
+void AttPosEKF::zeroRows(float (&covMat)[n_states][n_states], uint8_t first, uint8_t last)
+{
+ uint8_t row;
+ uint8_t col;
+ for (row=first; row<=last; row++)
+ {
+ for (col=0; col<n_states; col++)
+ {
+ covMat[row][col] = 0.0;
+ }
+ }
+}
+
+void AttPosEKF::zeroCols(float (&covMat)[n_states][n_states], uint8_t first, uint8_t last)
+{
+ uint8_t row;
+ uint8_t col;
+ for (col=first; col<=last; col++)
+ {
+ for (row=0; row < n_states; row++)
+ {
+ covMat[row][col] = 0.0;
+ }
+ }
+}
+
+float AttPosEKF::sq(float valIn)
+{
+ return valIn*valIn;
+}
+
+// Store states in a history array along with time stamp
+void AttPosEKF::StoreStates(uint64_t timestamp_ms)
+{
+ for (unsigned i=0; i<n_states; i++)
+ storedStates[i][storeIndex] = states[i];
+ statetimeStamp[storeIndex] = timestamp_ms;
+ storeIndex++;
+ if (storeIndex == data_buffer_size)
+ storeIndex = 0;
+}
+
+void AttPosEKF::ResetStoredStates()
+{
+ // reset all stored states
+ memset(&storedStates[0][0], 0, sizeof(storedStates));
+ memset(&statetimeStamp[0], 0, sizeof(statetimeStamp));
+
+ // reset store index to first
+ storeIndex = 0;
+
+ // overwrite all existing states
+ for (unsigned i = 0; i < n_states; i++) {
+ storedStates[i][storeIndex] = states[i];
+ }
+
+ statetimeStamp[storeIndex] = millis();
+
+ // increment to next storage index
+ storeIndex++;
+}
+
+// Output the state vector stored at the time that best matches that specified by msec
+int AttPosEKF::RecallStates(float statesForFusion[n_states], uint64_t msec)
+{
+ int ret = 0;
+
+ // int64_t bestTimeDelta = 200;
+ // unsigned bestStoreIndex = 0;
+ // for (unsigned storeIndex = 0; storeIndex < data_buffer_size; storeIndex++)
+ // {
+ // // The time delta can also end up as negative number,
+ // // since we might compare future to past or past to future
+ // // therefore cast to int64.
+ // int64_t timeDelta = (int64_t)msec - (int64_t)statetimeStamp[storeIndex];
+ // if (timeDelta < 0) {
+ // timeDelta = -timeDelta;
+ // }
+
+ // if (timeDelta < bestTimeDelta)
+ // {
+ // bestStoreIndex = storeIndex;
+ // bestTimeDelta = timeDelta;
+ // }
+ // }
+ // if (bestTimeDelta < 200) // only output stored state if < 200 msec retrieval error
+ // {
+ // for (uint8_t i=0; i < n_states; i++) {
+ // if (isfinite(storedStates[i][bestStoreIndex])) {
+ // statesForFusion[i] = storedStates[i][bestStoreIndex];
+ // } else if (isfinite(states[i])) {
+ // statesForFusion[i] = states[i];
+ // } else {
+ // // There is not much we can do here, except reporting the error we just
+ // // found.
+ // ret++;
+ // }
+ // }
+ // else // otherwise output current state
+ // {
+ for (uint8_t i=0; i < n_states; i++) {
+ if (isfinite(states[i])) {
+ statesForFusion[i] = states[i];
+ } else {
+ ret++;
+ }
+ }
+ // }
+
+ return ret;
+}
+
+void AttPosEKF::quat2Tnb(Mat3f &Tnb, const float (&quat)[4])
+{
+ // Calculate the nav to body cosine matrix
+ float q00 = sq(quat[0]);
+ float q11 = sq(quat[1]);
+ float q22 = sq(quat[2]);
+ float q33 = sq(quat[3]);
+ float q01 = quat[0]*quat[1];
+ float q02 = quat[0]*quat[2];
+ float q03 = quat[0]*quat[3];
+ float q12 = quat[1]*quat[2];
+ float q13 = quat[1]*quat[3];
+ float q23 = quat[2]*quat[3];
+
+ Tnb.x.x = q00 + q11 - q22 - q33;
+ Tnb.y.y = q00 - q11 + q22 - q33;
+ Tnb.z.z = q00 - q11 - q22 + q33;
+ Tnb.y.x = 2*(q12 - q03);
+ Tnb.z.x = 2*(q13 + q02);
+ Tnb.x.y = 2*(q12 + q03);
+ Tnb.z.y = 2*(q23 - q01);
+ Tnb.x.z = 2*(q13 - q02);
+ Tnb.y.z = 2*(q23 + q01);
+}
+
+void AttPosEKF::quat2Tbn(Mat3f &Tbn, const float (&quat)[4])
+{
+ // Calculate the body to nav cosine matrix
+ float q00 = sq(quat[0]);
+ float q11 = sq(quat[1]);
+ float q22 = sq(quat[2]);
+ float q33 = sq(quat[3]);
+ float q01 = quat[0]*quat[1];
+ float q02 = quat[0]*quat[2];
+ float q03 = quat[0]*quat[3];
+ float q12 = quat[1]*quat[2];
+ float q13 = quat[1]*quat[3];
+ float q23 = quat[2]*quat[3];
+
+ Tbn.x.x = q00 + q11 - q22 - q33;
+ Tbn.y.y = q00 - q11 + q22 - q33;
+ Tbn.z.z = q00 - q11 - q22 + q33;
+ Tbn.x.y = 2*(q12 - q03);
+ Tbn.x.z = 2*(q13 + q02);
+ Tbn.y.x = 2*(q12 + q03);
+ Tbn.y.z = 2*(q23 - q01);
+ Tbn.z.x = 2*(q13 - q02);
+ Tbn.z.y = 2*(q23 + q01);
+}
+
+void AttPosEKF::eul2quat(float (&quat)[4], const float (&eul)[3])
+{
+ float u1 = cos(0.5f*eul[0]);
+ float u2 = cos(0.5f*eul[1]);
+ float u3 = cos(0.5f*eul[2]);
+ float u4 = sin(0.5f*eul[0]);
+ float u5 = sin(0.5f*eul[1]);
+ float u6 = sin(0.5f*eul[2]);
+ quat[0] = u1*u2*u3+u4*u5*u6;
+ quat[1] = u4*u2*u3-u1*u5*u6;
+ quat[2] = u1*u5*u3+u4*u2*u6;
+ quat[3] = u1*u2*u6-u4*u5*u3;
+}
+
+void AttPosEKF::quat2eul(float (&y)[3], const float (&u)[4])
+{
+ y[0] = atan2f((2.0f*(u[2]*u[3]+u[0]*u[1])) , (u[0]*u[0]-u[1]*u[1]-u[2]*u[2]+u[3]*u[3]));
+ y[1] = -asinf(2.0f*(u[1]*u[3]-u[0]*u[2]));
+ y[2] = atan2f((2.0f*(u[1]*u[2]+u[0]*u[3])) , (u[0]*u[0]+u[1]*u[1]-u[2]*u[2]-u[3]*u[3]));
+}
+
+void AttPosEKF::calcvelNED(float (&velNED)[3], float gpsCourse, float gpsGndSpd, float gpsVelD)
+{
+ velNED[0] = gpsGndSpd*cosf(gpsCourse);
+ velNED[1] = gpsGndSpd*sinf(gpsCourse);
+ velNED[2] = gpsVelD;
+}
+
+void AttPosEKF::calcposNED(float (&posNED)[3], float lat, float lon, float hgt, float latRef, float lonRef, float hgtRef)
+{
+ posNED[0] = earthRadius * (lat - latRef);
+ posNED[1] = earthRadius * cos(latRef) * (lon - lonRef);
+ posNED[2] = -(hgt - hgtRef);
+}
+
+void AttPosEKF::calcLLH(float (&posNED)[3], float lat, float lon, float hgt, float latRef, float lonRef, float hgtRef)
+{
+ lat = latRef + posNED[0] * earthRadiusInv;
+ lon = lonRef + posNED[1] * earthRadiusInv / cos(latRef);
+ hgt = hgtRef - posNED[2];
+}
+
+void AttPosEKF::OnGroundCheck()
+{
+ onGround = (((sq(velNED[0]) + sq(velNED[1]) + sq(velNED[2])) < 4.0f) && (VtasMeas < 8.0f));
+ if (staticMode) {
+ staticMode = !(GPSstatus > GPS_FIX_2D);
+ }
+}
+
+void AttPosEKF::calcEarthRateNED(Vector3f &omega, float latitude)
+{
+ //Define Earth rotation vector in the NED navigation frame
+ omega.x = earthRate*cosf(latitude);
+ omega.y = 0.0f;
+ omega.z = -earthRate*sinf(latitude);
+}
+
+void AttPosEKF::CovarianceInit()
+{
+ // Calculate the initial covariance matrix P
+ P[0][0] = 0.25f * sq(1.0f*deg2rad);
+ P[1][1] = 0.25f * sq(1.0f*deg2rad);
+ P[2][2] = 0.25f * sq(1.0f*deg2rad);
+ P[3][3] = 0.25f * sq(10.0f*deg2rad);
+ P[4][4] = sq(0.7);
+ P[5][5] = P[4][4];
+ P[6][6] = sq(0.7);
+ P[7][7] = sq(15.0);
+ P[8][8] = P[7][7];
+ P[9][9] = sq(5.0);
+ P[10][10] = sq(0.1*deg2rad*dtIMU);
+ P[11][11] = P[10][10];
+ P[12][12] = P[10][10];
+ P[13][13] = sq(8.0f);
+ P[14][4] = P[13][13];
+ P[15][15] = sq(0.02f);
+ P[16][16] = P[15][15];
+ P[17][17] = P[15][15];
+ P[18][18] = sq(0.02f);
+ P[19][19] = P[18][18];
+ P[20][20] = P[18][18];
+}
+
+float AttPosEKF::ConstrainFloat(float val, float min, float max)
+{
+ return (val > max) ? max : ((val < min) ? min : val);
+}
+
+void AttPosEKF::ConstrainVariances()
+{
+ if (!numericalProtection) {
+ return;
+ }
+
+ // State vector:
+ // 0-3: quaternions (q0, q1, q2, q3)
+ // 4-6: Velocity - m/sec (North, East, Down)
+ // 7-9: Position - m (North, East, Down)
+ // 10-12: Delta Angle bias - rad (X,Y,Z)
+ // 13-14: Wind Vector - m/sec (North,East)
+ // 15-17: Earth Magnetic Field Vector - gauss (North, East, Down)
+ // 18-20: Body Magnetic Field Vector - gauss (X,Y,Z)
+
+ // Constrain quaternion variances
+ for (unsigned i = 0; i < 4; i++) {
+ P[i][i] = ConstrainFloat(P[i][i], 0.0f, 1.0f);
+ }
+
+ // Constrain velocitie variances
+ for (unsigned i = 4; i < 7; i++) {
+ P[i][i] = ConstrainFloat(P[i][i], 0.0f, 1.0e3f);
+ }
+
+ // Constrain position variances
+ for (unsigned i = 7; i < 10; i++) {
+ P[i][i] = ConstrainFloat(P[i][i], 0.0f, 1.0e6f);
+ }
+
+ // Angle bias variances
+ for (unsigned i = 10; i < 13; i++) {
+ P[i][i] = ConstrainFloat(P[i][i], 0.0f, sq(0.175f * dtIMU));
+ }
+
+ // Wind velocity variances
+ for (unsigned i = 13; i < 15; i++) {
+ P[i][i] = ConstrainFloat(P[i][i], 0.0f, 1.0e3f);
+ }
+
+ // Earth magnetic field variances
+ for (unsigned i = 15; i < 18; i++) {
+ P[i][i] = ConstrainFloat(P[i][i], 0.0f, 1.0f);
+ }
+
+ // Body magnetic field variances
+ for (unsigned i = 18; i < 21; i++) {
+ P[i][i] = ConstrainFloat(P[i][i], 0.0f, 1.0f);
+ }
+
+}
+
+void AttPosEKF::ConstrainStates()
+{
+ if (!numericalProtection) {
+ return;
+ }
+
+ // State vector:
+ // 0-3: quaternions (q0, q1, q2, q3)
+ // 4-6: Velocity - m/sec (North, East, Down)
+ // 7-9: Position - m (North, East, Down)
+ // 10-12: Delta Angle bias - rad (X,Y,Z)
+ // 13-14: Wind Vector - m/sec (North,East)
+ // 15-17: Earth Magnetic Field Vector - gauss (North, East, Down)
+ // 18-20: Body Magnetic Field Vector - gauss (X,Y,Z)
+
+
+ // Constrain quaternion
+ for (unsigned i = 0; i < 4; i++) {
+ states[i] = ConstrainFloat(states[i], -1.0f, 1.0f);
+ }
+
+ // Constrain velocities to what GPS can do for us
+ for (unsigned i = 4; i < 7; i++) {
+ states[i] = ConstrainFloat(states[i], -5.0e2f, 5.0e2f);
+ }
+
+ // Constrain position to a reasonable vehicle range (in meters)
+ for (unsigned i = 7; i < 9; i++) {
+ states[i] = ConstrainFloat(states[i], -1.0e6f, 1.0e6f);
+ }
+
+ // Constrain altitude
+ states[9] = ConstrainFloat(states[9], -4.0e4f, 1.0e4f);
+
+ // Angle bias limit - set to 8 degrees / sec
+ for (unsigned i = 10; i < 13; i++) {
+ states[i] = ConstrainFloat(states[i], -0.12f * dtIMU, 0.12f * dtIMU);
+ }
+
+ // Wind velocity limits - assume 120 m/s max velocity
+ for (unsigned i = 13; i < 15; i++) {
+ states[i] = ConstrainFloat(states[i], -120.0f, 120.0f);
+ }
+
+ // Earth magnetic field limits (in Gauss)
+ for (unsigned i = 15; i < 18; i++) {
+ states[i] = ConstrainFloat(states[i], -1.0f, 1.0f);
+ }
+
+ // Body magnetic field variances (in Gauss).
+ // the max offset should be in this range.
+ for (unsigned i = 18; i < 21; i++) {
+ states[i] = ConstrainFloat(states[i], -0.5f, 0.5f);
+ }
+
+}
+
+void AttPosEKF::ForceSymmetry()
+{
+ if (!numericalProtection) {
+ return;
+ }
+
+ // Force symmetry on the covariance matrix to prevent ill-conditioning
+ // of the matrix which would cause the filter to blow-up
+ for (unsigned i = 1; i < n_states; i++)
+ {
+ for (uint8_t j = 0; j < i; j++)
+ {
+ P[i][j] = 0.5f * (P[i][j] + P[j][i]);
+ P[j][i] = P[i][j];
+ }
+ }
+}
+
+bool AttPosEKF::FilterHealthy()
+{
+ if (!statesInitialised) {
+ return false;
+ }
+
+ // XXX Check state vector for NaNs and ill-conditioning
+
+ // Check if any of the major inputs timed out
+ if (current_ekf_state.posTimeout || current_ekf_state.velTimeout || current_ekf_state.hgtTimeout) {
+ return false;
+ }
+
+ // Nothing fired, return ok.
+ return true;
+}
+
+/**
+ * Reset the filter position states
+ *
+ * This resets the position to the last GPS measurement
+ * or to zero in case of static position.
+ */
+void AttPosEKF::ResetPosition(void)
+{
+ if (staticMode) {
+ states[7] = 0;
+ states[8] = 0;
+ } else if (GPSstatus >= GPS_FIX_3D) {
+
+ // reset the states from the GPS measurements
+ states[7] = posNE[0];
+ states[8] = posNE[1];
+ }
+}
+
+/**
+ * Reset the height state.
+ *
+ * This resets the height state with the last altitude measurements
+ */
+void AttPosEKF::ResetHeight(void)
+{
+ // write to the state vector
+ states[9] = -hgtMea;
+}
+
+/**
+ * Reset the velocity state.
+ */
+void AttPosEKF::ResetVelocity(void)
+{
+ if (staticMode) {
+ states[4] = 0.0f;
+ states[5] = 0.0f;
+ states[6] = 0.0f;
+ } else if (GPSstatus >= GPS_FIX_3D) {
+
+ states[4] = velNED[0]; // north velocity from last reading
+ states[5] = velNED[1]; // east velocity from last reading
+ states[6] = velNED[2]; // down velocity from last reading
+ }
+}
+
+
+void AttPosEKF::FillErrorReport(struct ekf_status_report *err)
+{
+ for (int i = 0; i < n_states; i++)
+ {
+ err->states[i] = states[i];
+ }
+
+ err->velHealth = current_ekf_state.velHealth;
+ err->posHealth = current_ekf_state.posHealth;
+ err->hgtHealth = current_ekf_state.hgtHealth;
+ err->velTimeout = current_ekf_state.velTimeout;
+ err->posTimeout = current_ekf_state.posTimeout;
+ err->hgtTimeout = current_ekf_state.hgtTimeout;
+}
+
+bool AttPosEKF::StatesNaN(struct ekf_status_report *err_report) {
+ bool err = false;
+
+ // check all states and covariance matrices
+ for (unsigned i = 0; i < n_states; i++) {
+ for (unsigned j = 0; j < n_states; j++) {
+ if (!isfinite(KH[i][j])) {
+
+ err_report->covarianceNaN = true;
+ err = true;
+ } // intermediate result used for covariance updates
+ if (!isfinite(KHP[i][j])) {
+
+ err_report->covarianceNaN = true;
+ err = true;
+ } // intermediate result used for covariance updates
+ if (!isfinite(P[i][j])) {
+
+ err_report->covarianceNaN = true;
+ err = true;
+ } // covariance matrix
+ }
+
+ if (!isfinite(Kfusion[i])) {
+
+ err_report->kalmanGainsNaN = true;
+ err = true;
+ } // Kalman gains
+
+ if (!isfinite(states[i])) {
+
+ err_report->statesNaN = true;
+ err = true;
+ } // state matrix
+ }
+
+ if (err) {
+ FillErrorReport(err_report);
+ }
+
+ return err;
+
+}
+
+/**
+ * Check the filter inputs and bound its operational state
+ *
+ * This check will reset the filter states if required
+ * due to a failure of consistency or timeout checks.
+ * it should be run after the measurement data has been
+ * updated, but before any of the fusion steps are
+ * executed.
+ */
+int AttPosEKF::CheckAndBound()
+{
+
+ // Store the old filter state
+ bool currStaticMode = staticMode;
+
+ // Reset the filter if the states went NaN
+ if (StatesNaN(&last_ekf_error)) {
+
+ InitializeDynamic(velNED, magDeclination);
+
+ return 1;
+ }
+
+ // Reset the filter if the IMU data is too old
+ if (dtIMU > 0.2f) {
+
+ ResetVelocity();
+ ResetPosition();
+ ResetHeight();
+ ResetStoredStates();
+
+ // that's all we can do here, return
+ return 2;
+ }
+
+ // Check if we're on ground - this also sets static mode.
+ OnGroundCheck();
+
+ // Check if we switched between states
+ if (currStaticMode != staticMode) {
+ ResetVelocity();
+ ResetPosition();
+ ResetHeight();
+ ResetStoredStates();
+
+ return 3;
+ }
+
+ return 0;
+}
+
+void AttPosEKF::AttitudeInit(float ax, float ay, float az, float mx, float my, float mz, float declination, float *initQuat)
+{
+ float initialRoll, initialPitch;
+ float cosRoll, sinRoll, cosPitch, sinPitch;
+ float magX, magY;
+ float initialHdg, cosHeading, sinHeading;
+
+ initialRoll = atan2(-ay, -az);
+ initialPitch = atan2(ax, -az);
+
+ cosRoll = cosf(initialRoll);
+ sinRoll = sinf(initialRoll);
+ cosPitch = cosf(initialPitch);
+ sinPitch = sinf(initialPitch);
+
+ magX = mx * cosPitch + my * sinRoll * sinPitch + mz * cosRoll * sinPitch;
+
+ magY = my * cosRoll - mz * sinRoll;
+
+ initialHdg = atan2f(-magY, magX);
+ /* true heading is the mag heading minus declination */
+ initialHdg += declination;
+
+ cosRoll = cosf(initialRoll * 0.5f);
+ sinRoll = sinf(initialRoll * 0.5f);
+
+ cosPitch = cosf(initialPitch * 0.5f);
+ sinPitch = sinf(initialPitch * 0.5f);
+
+ cosHeading = cosf(initialHdg * 0.5f);
+ sinHeading = sinf(initialHdg * 0.5f);
+
+ initQuat[0] = cosRoll * cosPitch * cosHeading + sinRoll * sinPitch * sinHeading;
+ initQuat[1] = sinRoll * cosPitch * cosHeading - cosRoll * sinPitch * sinHeading;
+ initQuat[2] = cosRoll * sinPitch * cosHeading + sinRoll * cosPitch * sinHeading;
+ initQuat[3] = cosRoll * cosPitch * sinHeading - sinRoll * sinPitch * cosHeading;
+}
+
+void AttPosEKF::InitializeDynamic(float (&initvelNED)[3], float declination)
+{
+
+ // Clear the init flag
+ statesInitialised = false;
+
+ magDeclination = declination;
+
+ ZeroVariables();
+
+ // Calculate initial filter quaternion states from raw measurements
+ float initQuat[4];
+ Vector3f initMagXYZ;
+ initMagXYZ = magData - magBias;
+ AttitudeInit(accel.x, accel.y, accel.z, initMagXYZ.x, initMagXYZ.y, initMagXYZ.z, magDeclination, initQuat);
+
+ // Calculate initial Tbn matrix and rotate Mag measurements into NED
+ // to set initial NED magnetic field states
+ Mat3f DCM;
+ quat2Tbn(DCM, initQuat);
+ Vector3f initMagNED;
+ initMagXYZ = magData - magBias;
+ initMagNED.x = DCM.x.x*initMagXYZ.x + DCM.x.y*initMagXYZ.y + DCM.x.z*initMagXYZ.z;
+ initMagNED.y = DCM.y.x*initMagXYZ.x + DCM.y.y*initMagXYZ.y + DCM.y.z*initMagXYZ.z;
+ initMagNED.z = DCM.z.x*initMagXYZ.x + DCM.z.y*initMagXYZ.y + DCM.z.z*initMagXYZ.z;
+
+
+
+ // write to state vector
+ for (uint8_t j=0; j<=3; j++) states[j] = initQuat[j]; // quaternions
+ for (uint8_t j=0; j<=2; j++) states[j+4] = initvelNED[j]; // velocities
+ for (uint8_t j=0; j<=7; j++) states[j+7] = 0.0f; // positiions, dAngBias, windVel
+ states[15] = initMagNED.x; // Magnetic Field North
+ states[16] = initMagNED.y; // Magnetic Field East
+ states[17] = initMagNED.z; // Magnetic Field Down
+ states[18] = magBias.x; // Magnetic Field Bias X
+ states[19] = magBias.y; // Magnetic Field Bias Y
+ states[20] = magBias.z; // Magnetic Field Bias Z
+
+ statesInitialised = true;
+
+ // initialise the covariance matrix
+ CovarianceInit();
+
+ //Define Earth rotation vector in the NED navigation frame
+ calcEarthRateNED(earthRateNED, latRef);
+
+ //Initialise summed variables used by covariance prediction
+ summedDelAng.x = 0.0f;
+ summedDelAng.y = 0.0f;
+ summedDelAng.z = 0.0f;
+ summedDelVel.x = 0.0f;
+ summedDelVel.y = 0.0f;
+ summedDelVel.z = 0.0f;
+}
+
+void AttPosEKF::InitialiseFilter(float (&initvelNED)[3], double referenceLat, double referenceLon, float referenceHgt, float declination)
+{
+ //store initial lat,long and height
+ latRef = referenceLat;
+ lonRef = referenceLon;
+ hgtRef = referenceHgt;
+
+ memset(&last_ekf_error, 0, sizeof(last_ekf_error));
+
+ InitializeDynamic(initvelNED, declination);
+}
+
+void AttPosEKF::ZeroVariables()
+{
+ // Do the data structure init
+ for (unsigned i = 0; i < n_states; i++) {
+ for (unsigned j = 0; j < n_states; j++) {
+ KH[i][j] = 0.0f; // intermediate result used for covariance updates
+ KHP[i][j] = 0.0f; // intermediate result used for covariance updates
+ P[i][j] = 0.0f; // covariance matrix
+ }
+
+ Kfusion[i] = 0.0f; // Kalman gains
+ states[i] = 0.0f; // state matrix
+ }
+
+ for (unsigned i = 0; i < data_buffer_size; i++) {
+
+ for (unsigned j = 0; j < n_states; j++) {
+ storedStates[j][i] = 0.0f;
+ }
+
+ statetimeStamp[i] = 0;
+ }
+
+ memset(&current_ekf_state, 0, sizeof(current_ekf_state));
+}
+
+void AttPosEKF::GetFilterState(struct ekf_status_report *state)
+{
+ memcpy(state, &current_ekf_state, sizeof(state));
+}
+
+void AttPosEKF::GetLastErrorState(struct ekf_status_report *last_error)
+{
+ memcpy(last_error, &last_ekf_error, sizeof(last_error));
+}
diff --git a/src/modules/ekf_att_pos_estimator/estimator.h b/src/modules/ekf_att_pos_estimator/estimator_21states.h
index e821089f2..a19ff1995 100644
--- a/src/modules/ekf_att_pos_estimator/estimator.h
+++ b/src/modules/ekf_att_pos_estimator/estimator_21states.h
@@ -1,75 +1,6 @@
-#include <math.h>
-#include <stdint.h>
-
#pragma once
-#define GRAVITY_MSS 9.80665f
-#define deg2rad 0.017453292f
-#define rad2deg 57.295780f
-#define pi 3.141592657f
-#define earthRate 0.000072921f
-#define earthRadius 6378145.0f
-#define earthRadiusInv 1.5678540e-7f
-
-class Vector3f
-{
-private:
-public:
- float x;
- float y;
- float z;
-
- float length(void) const;
- void zero(void);
-};
-
-class Mat3f
-{
-private:
-public:
- Vector3f x;
- Vector3f y;
- Vector3f z;
-
- Mat3f();
-
- void identity();
- Mat3f transpose(void) const;
-};
-
-Vector3f operator*(float sclIn1, Vector3f vecIn1);
-Vector3f operator+( Vector3f vecIn1, Vector3f vecIn2);
-Vector3f operator-( Vector3f vecIn1, Vector3f vecIn2);
-Vector3f operator*( Mat3f matIn, Vector3f vecIn);
-Vector3f operator%( Vector3f vecIn1, Vector3f vecIn2);
-Vector3f operator*(Vector3f vecIn1, float sclIn1);
-
-void swap_var(float &d1, float &d2);
-
-const unsigned int n_states = 23;
-const unsigned int data_buffer_size = 50;
-
-enum GPS_FIX {
- GPS_FIX_NOFIX = 0,
- GPS_FIX_2D = 2,
- GPS_FIX_3D = 3
-};
-
-struct ekf_status_report {
- bool velHealth;
- bool posHealth;
- bool hgtHealth;
- bool velTimeout;
- bool posTimeout;
- bool hgtTimeout;
- uint32_t velFailTime;
- uint32_t posFailTime;
- uint32_t hgtFailTime;
- float states[n_states];
- bool statesNaN;
- bool covarianceNaN;
- bool kalmanGainsNaN;
-};
+#include "estimator_utilities.h"
class AttPosEKF {
@@ -78,8 +9,6 @@ public:
AttPosEKF();
~AttPosEKF();
-
-
/* ##############################################
*
* M A I N F I L T E R P A R A M E T E R S
@@ -141,25 +70,6 @@ public:
accelProcessNoise = 0.5f;
}
- struct {
- unsigned obsIndex;
- float MagPred[3];
- float SH_MAG[9];
- float q0;
- float q1;
- float q2;
- float q3;
- float magN;
- float magE;
- float magD;
- float magXbias;
- float magYbias;
- float magZbias;
- float R_MAG;
- Mat3f DCM;
- } magstate;
-
-
// Global variables
float KH[n_states][n_states]; // intermediate result used for covariance updates
float KHP[n_states][n_states]; // intermediate result used for covariance updates
@@ -174,7 +84,6 @@ public:
float statesAtHgtTime[n_states]; // States at the effective measurement time for the hgtMea measurement
float statesAtMagMeasTime[n_states]; // filter satates at the effective measurement time
float statesAtVtasMeasTime[n_states]; // filter states at the effective measurement time
- float statesAtRngTime[n_states]; // filter states at the effective measurement time
Vector3f correctedDelAng; // delta angles about the xyz body axes corrected for errors (rad)
Vector3f correctedDelVel; // delta velocities along the XYZ body axes corrected for errors (m/s)
@@ -183,10 +92,6 @@ public:
float accNavMag; // magnitude of navigation accel (- used to adjust GPS obs variance (m/s^2)
Vector3f earthRateNED; // earths angular rate vector in NED (rad/s)
Vector3f angRate; // angular rate vector in XYZ body axes measured by the IMU (rad/s)
-
- Mat3f Tbn; // transformation matrix from body to NED coordinates
- Mat3f Tnb; // transformation amtrix from NED to body coordinates
-
Vector3f accel; // acceleration vector in XYZ body axes measured by the IMU (m/s^2)
Vector3f dVelIMU;
Vector3f dAngIMU;
@@ -198,30 +103,26 @@ public:
float velNED[3]; // North, East, Down velocity obs (m/s)
float posNE[2]; // North, East position obs (m)
float hgtMea; // measured height (m)
- float baroHgtOffset; ///< the baro (weather) offset from normalized altitude
- float rngMea; // Ground distance
float posNED[3]; // North, East Down position (m)
float innovMag[3]; // innovation output
float varInnovMag[3]; // innovation variance output
Vector3f magData; // magnetometer flux radings in X,Y,Z body axes
float innovVtas; // innovation output
- float innovRng; ///< Range finder innovation
float varInnovVtas; // innovation variance output
float VtasMeas; // true airspeed measurement (m/s)
- float magDeclination; ///< magnetic declination
- double latRef; // WGS-84 latitude of reference point (rad)
- double lonRef; // WGS-84 longitude of reference point (rad)
+ float magDeclination;
+ float latRef; // WGS-84 latitude of reference point (rad)
+ float lonRef; // WGS-84 longitude of reference point (rad)
float hgtRef; // WGS-84 height of reference point (m)
- bool refSet; ///< flag to indicate if the reference position has been set
Vector3f magBias; // states representing magnetometer bias vector in XYZ body axes
- unsigned covSkipCount; // Number of state prediction frames (IMU daya updates to skip before doing the covariance prediction
+ uint8_t covSkipCount; // Number of state prediction frames (IMU daya updates to skip before doing the covariance prediction
// GPS input data variables
float gpsCourse;
float gpsVelD;
- double gpsLat;
- double gpsLon;
+ float gpsLat;
+ float gpsLon;
float gpsHgt;
uint8_t GPSstatus;
@@ -235,13 +136,11 @@ public:
bool fuseHgtData; // this boolean causes the hgtMea obs to be fused
bool fuseMagData; // boolean true when magnetometer data is to be fused
bool fuseVtasData; // boolean true when airspeed data is to be fused
- bool fuseRngData; ///< true when range data is fused
bool onGround; ///< boolean true when the flight vehicle is on the ground (not flying)
bool staticMode; ///< boolean true if no position feedback is fused
bool useAirspeed; ///< boolean true if airspeed data is being used
bool useCompass; ///< boolean true if magnetometer data is being used
- bool useRangeFinder; ///< true when rangefinder is being used
struct ekf_status_report current_ekf_state;
struct ekf_status_report last_ekf_error;
@@ -261,10 +160,6 @@ void FuseMagnetometer();
void FuseAirspeed();
-void FuseRangeFinder();
-
-void FuseOpticalFlow();
-
void zeroRows(float (&covMat)[n_states][n_states], uint8_t first, uint8_t last);
void zeroCols(float (&covMat)[n_states][n_states], uint8_t first, uint8_t last);
@@ -285,7 +180,7 @@ void StoreStates(uint64_t timestamp_ms);
* time-wise where valid states were updated and invalid remained at the old
* value.
*/
-int RecallStates(float *statesForFusion, uint64_t msec);
+int RecallStates(float statesForFusion[n_states], uint64_t msec);
void ResetStoredStates();
@@ -299,7 +194,7 @@ static void quat2eul(float (&eul)[3], const float (&quat)[4]);
static void calcvelNED(float (&velNED)[3], float gpsCourse, float gpsGndSpd, float gpsVelD);
-static void calcposNED(float (&posNED)[3], double lat, double lon, float hgt, double latRef, double lonRef, float hgtRef);
+static void calcposNED(float (&posNED)[3], float lat, float lon, float hgt, float latRef, float lonRef, float hgtRef);
static void calcLLH(float (&posNED)[3], float lat, float lon, float hgt, float latRef, float lonRef, float hgtRef);
diff --git a/src/modules/ekf_att_pos_estimator/estimator.cpp b/src/modules/ekf_att_pos_estimator/estimator_23states.cpp
index 5db1adbb3..c17e034ad 100644
--- a/src/modules/ekf_att_pos_estimator/estimator.cpp
+++ b/src/modules/ekf_att_pos_estimator/estimator_23states.cpp
@@ -1,151 +1,159 @@
-#include "estimator.h"
+#include "estimator_23states.h"
#include <string.h>
-#include <stdarg.h>
-
-// Define EKF_DEBUG here to enable the debug print calls
-// if the macro is not set, these will be completely
-// optimized out by the compiler.
-//#define EKF_DEBUG
-
-#ifdef EKF_DEBUG
#include <stdio.h>
+#include <stdarg.h>
+#include <math.h>
-static void
-ekf_debug_print(const char *fmt, va_list args)
-{
- fprintf(stderr, "%s: ", "[ekf]");
- vfprintf(stderr, fmt, args);
-
- fprintf(stderr, "\n");
-}
-
-static void
-ekf_debug(const char *fmt, ...)
-{
- va_list args;
-
- va_start(args, fmt);
- ekf_debug_print(fmt, args);
-}
-
-#else
-
-static void ekf_debug(const char *fmt, ...) { while(0){} }
+#ifndef M_PI_F
+#define M_PI_F ((float)M_PI)
#endif
-float Vector3f::length(void) const
-{
- return sqrt(x*x + y*y + z*z);
-}
-
-void Vector3f::zero(void)
-{
- x = 0.0f;
- y = 0.0f;
- z = 0.0f;
-}
-
-Mat3f::Mat3f() {
- identity();
-}
-
-void Mat3f::identity() {
- x.x = 1.0f;
- x.y = 0.0f;
- x.z = 0.0f;
-
- y.x = 0.0f;
- y.y = 1.0f;
- y.z = 0.0f;
-
- z.x = 0.0f;
- z.y = 0.0f;
- z.z = 1.0f;
-}
-
-Mat3f Mat3f::transpose(void) const
-{
- Mat3f ret = *this;
- swap_var(ret.x.y, ret.y.x);
- swap_var(ret.x.z, ret.z.x);
- swap_var(ret.y.z, ret.z.y);
- return ret;
-}
-
-// overload + operator to provide a vector addition
-Vector3f operator+( Vector3f vecIn1, Vector3f vecIn2)
-{
- Vector3f vecOut;
- vecOut.x = vecIn1.x + vecIn2.x;
- vecOut.y = vecIn1.y + vecIn2.y;
- vecOut.z = vecIn1.z + vecIn2.z;
- return vecOut;
-}
-
-// overload - operator to provide a vector subtraction
-Vector3f operator-( Vector3f vecIn1, Vector3f vecIn2)
-{
- Vector3f vecOut;
- vecOut.x = vecIn1.x - vecIn2.x;
- vecOut.y = vecIn1.y - vecIn2.y;
- vecOut.z = vecIn1.z - vecIn2.z;
- return vecOut;
-}
-
-// overload * operator to provide a matrix vector product
-Vector3f operator*( Mat3f matIn, Vector3f vecIn)
-{
- Vector3f vecOut;
- vecOut.x = matIn.x.x*vecIn.x + matIn.x.y*vecIn.y + matIn.x.z*vecIn.z;
- vecOut.y = matIn.y.x*vecIn.x + matIn.y.y*vecIn.y + matIn.y.z*vecIn.z;
- vecOut.z = matIn.x.x*vecIn.x + matIn.z.y*vecIn.y + matIn.z.z*vecIn.z;
- return vecOut;
-}
-
-// overload % operator to provide a vector cross product
-Vector3f operator%( Vector3f vecIn1, Vector3f vecIn2)
+#define EKF_COVARIANCE_DIVERGED 1.0e8f
+
+AttPosEKF::AttPosEKF() :
+ covTimeStepMax(0.0f),
+ covDelAngMax(0.0f),
+ rngFinderPitch(0.0f),
+ a1(0.0f),
+ a2(0.0f),
+ a3(0.0f),
+ yawVarScale(0.0f),
+ windVelSigma(0.0f),
+ dAngBiasSigma(0.0f),
+ dVelBiasSigma(0.0f),
+ magEarthSigma(0.0f),
+ magBodySigma(0.0f),
+ gndHgtSigma(0.0f),
+ vneSigma(0.0f),
+ vdSigma(0.0f),
+ posNeSigma(0.0f),
+ posDSigma(0.0f),
+ magMeasurementSigma(0.0f),
+ airspeedMeasurementSigma(0.0f),
+ gyroProcessNoise(0.0f),
+ accelProcessNoise(0.0f),
+ EAS2TAS(1.0f),
+ magstate{},
+ resetMagState{},
+ KH{},
+ KHP{},
+ P{},
+ Kfusion{},
+ states{},
+ resetStates{},
+ storedStates{},
+ statetimeStamp{},
+ lastVelPosFusion(millis()),
+ statesAtVelTime{},
+ statesAtPosTime{},
+ statesAtHgtTime{},
+ statesAtMagMeasTime{},
+ statesAtVtasMeasTime{},
+ statesAtRngTime{},
+ statesAtFlowTime{},
+ correctedDelAng(),
+ correctedDelVel(),
+ summedDelAng(),
+ summedDelVel(),
+ accNavMag(),
+ earthRateNED(),
+ angRate(),
+ lastGyroOffset(),
+ delAngTotal(),
+ Tbn(),
+ Tnb(),
+ accel(),
+ dVelIMU(),
+ dAngIMU(),
+ dtIMU(0.005f),
+ dtIMUfilt(0.005f),
+ dtVelPos(0.01f),
+ dtVelPosFilt(0.01f),
+ dtHgtFilt(0.01f),
+ dtGpsFilt(0.1f),
+ windSpdFiltNorth(0.0f),
+ windSpdFiltEast(0.0f),
+ windSpdFiltAltitude(0.0f),
+ windSpdFiltClimb(0.0f),
+ fusionModeGPS(0),
+ innovVelPos{},
+ varInnovVelPos{},
+ velNED{},
+ accelGPSNED{},
+ posNE{},
+ hgtMea(0.0f),
+ baroHgtOffset(0.0f),
+ rngMea(0.0f),
+ innovMag{},
+ varInnovMag{},
+ magData{},
+ losData{},
+ innovVtas(0.0f),
+ innovRng(0.0f),
+ innovOptFlow{},
+ varInnovOptFlow{},
+ varInnovVtas(0.0f),
+ varInnovRng(0.0f),
+ VtasMeas(0.0f),
+ magDeclination(0.0f),
+ latRef(0.0f),
+ lonRef(-M_PI_F),
+ hgtRef(0.0f),
+ refSet(false),
+ magBias(),
+ covSkipCount(0),
+ gpsLat(0.0),
+ gpsLon(-M_PI),
+ gpsHgt(0.0f),
+ GPSstatus(0),
+ baroHgt(0.0f),
+ statesInitialised(false),
+ fuseVelData(false),
+ fusePosData(false),
+ fuseHgtData(false),
+ fuseMagData(false),
+ fuseVtasData(false),
+ fuseRngData(false),
+ fuseOptFlowData(false),
+
+ inhibitWindStates(true),
+ inhibitMagStates(true),
+ inhibitGndState(true),
+ inhibitScaleState(true),
+
+ onGround(true),
+ staticMode(true),
+ useAirspeed(true),
+ useCompass(true),
+ useRangeFinder(true),
+ useOpticalFlow(false),
+
+ ekfDiverged(false),
+ lastReset(0),
+ current_ekf_state{},
+ last_ekf_error{},
+ numericalProtection(true),
+ storeIndex(0),
+ storedOmega{},
+ Popt{},
+ flowStates{},
+ prevPosN(0.0f),
+ prevPosE(0.0f),
+ auxFlowObsInnov{},
+ auxFlowObsInnovVar{},
+ fScaleFactorVar(0.0f),
+ Tnb_flow{},
+ R_LOS(0.0f),
+ auxFlowTestRatio{},
+ auxRngTestRatio(0.0f),
+ flowInnovGate(0.0f),
+ auxFlowInnovGate(0.0f),
+ rngInnovGate(0.0f),
+ minFlowRng(0.0f),
+ moCompR_LOS(0.0f)
{
- Vector3f vecOut;
- vecOut.x = vecIn1.y*vecIn2.z - vecIn1.z*vecIn2.y;
- vecOut.y = vecIn1.z*vecIn2.x - vecIn1.x*vecIn2.z;
- vecOut.z = vecIn1.x*vecIn2.y - vecIn1.y*vecIn2.x;
- return vecOut;
-}
-
-// overload * operator to provide a vector scaler product
-Vector3f operator*(Vector3f vecIn1, float sclIn1)
-{
- Vector3f vecOut;
- vecOut.x = vecIn1.x * sclIn1;
- vecOut.y = vecIn1.y * sclIn1;
- vecOut.z = vecIn1.z * sclIn1;
- return vecOut;
-}
-
-// overload * operator to provide a vector scaler product
-Vector3f operator*(float sclIn1, Vector3f vecIn1)
-{
- Vector3f vecOut;
- vecOut.x = vecIn1.x * sclIn1;
- vecOut.y = vecIn1.y * sclIn1;
- vecOut.z = vecIn1.z * sclIn1;
- return vecOut;
-}
-
-void swap_var(float &d1, float &d2)
-{
- float tmp = d1;
- d1 = d2;
- d2 = tmp;
-}
-
-AttPosEKF::AttPosEKF()
-
- /* NOTE: DO NOT initialize class members here. Use ZeroVariables()
- * instead to allow clean in-air re-initialization.
- */
-{
-
+ memset(&last_ekf_error, 0, sizeof(last_ekf_error));
+ memset(&current_ekf_state, 0, sizeof(current_ekf_state));
ZeroVariables();
InitialiseParameters();
}
@@ -171,7 +179,7 @@ void AttPosEKF::UpdateStrapdownEquationsNED()
float qUpdated[4];
float quatMag;
float deltaQuat[4];
- const Vector3f gravityNED = {0.0,0.0,GRAVITY_MSS};
+ const Vector3f gravityNED(0.0f, 0.0f, GRAVITY_MSS);
// Remove sensor bias errors
correctedDelAng.x = dAngIMU.x - states[10];
@@ -181,6 +189,10 @@ void AttPosEKF::UpdateStrapdownEquationsNED()
dVelIMU.y = dVelIMU.y;
dVelIMU.z = dVelIMU.z - states[13];
+ delAngTotal.x += correctedDelAng.x;
+ delAngTotal.y += correctedDelAng.y;
+ delAngTotal.z += correctedDelAng.z;
+
// Save current measurements
Vector3f prevDelAng = correctedDelAng;
@@ -199,8 +211,12 @@ void AttPosEKF::UpdateStrapdownEquationsNED()
}
else
{
- deltaQuat[0] = cosf(0.5f*rotationMag);
- float rotScaler = (sinf(0.5f*rotationMag))/rotationMag;
+ // We are using double here as we are unsure how small
+ // the angle differences are and if we get into numeric
+ // issues with float. The runtime impact is not measurable
+ // for these quantities.
+ deltaQuat[0] = cos(0.5*(double)rotationMag);
+ float rotScaler = (sin(0.5*(double)rotationMag))/(double)rotationMag;
deltaQuat[1] = correctedDelAng.x*rotScaler;
deltaQuat[2] = correctedDelAng.y*rotScaler;
deltaQuat[3] = correctedDelAng.z*rotScaler;
@@ -277,6 +293,9 @@ void AttPosEKF::UpdateStrapdownEquationsNED()
// Constrain states (to protect against filter divergence)
ConstrainStates();
+
+ // update filtered IMU time step length
+ dtIMUfilt = 0.99f * dtIMUfilt + 0.01f * dtIMU;
}
void AttPosEKF::CovariancePrediction(float dt)
@@ -312,15 +331,28 @@ void AttPosEKF::CovariancePrediction(float dt)
float nextP[n_states][n_states];
// calculate covariance prediction process noise
- for (uint8_t i= 0; i<=9; i++) processNoise[i] = 1.0e-9f;
+ for (uint8_t i= 0; i<4; i++) processNoise[i] = 1.0e-9f;
+ for (uint8_t i= 4; i<10; i++) processNoise[i] = 1.0e-9f;
for (uint8_t i=10; i<=12; i++) processNoise[i] = dt * dAngBiasSigma;
// scale gyro bias noise when on ground to allow for faster bias estimation
for (uint8_t i=10; i<=12; i++) processNoise[i] = dt * dAngBiasSigma;
processNoise[13] = dVelBiasSigma;
- for (uint8_t i=14; i<=15; i++) processNoise[i] = dt * windVelSigma;
- for (uint8_t i=16; i<=18; i++) processNoise[i] = dt * magEarthSigma;
- for (uint8_t i=19; i<=21; i++) processNoise[i] = dt * magBodySigma;
- processNoise[22] = dt * sqrtf(sq(states[4]) + sq(states[5])) * gndHgtSigma;
+ if (!inhibitWindStates) {
+ for (uint8_t i=14; i<=15; i++) processNoise[i] = dt * windVelSigma;
+ } else {
+ for (uint8_t i=14; i<=15; i++) processNoise[i] = 0;
+ }
+ if (!inhibitMagStates) {
+ for (uint8_t i=16; i<=18; i++) processNoise[i] = dt * magEarthSigma;
+ for (uint8_t i=19; i<=21; i++) processNoise[i] = dt * magBodySigma;
+ } else {
+ for (uint8_t i=16; i<=21; i++) processNoise[i] = 0;
+ }
+ if (!inhibitGndState) {
+ processNoise[22] = dt * sqrtf(sq(states[4]) + sq(states[5])) * gndHgtSigma;
+ } else {
+ processNoise[22] = 0;
+ }
// square all sigmas
for (unsigned i = 0; i < n_states; i++) processNoise[i] = sq(processNoise[i]);
@@ -932,30 +964,6 @@ void AttPosEKF::CovariancePrediction(float dt)
nextP[i][i] = nextP[i][i] + processNoise[i];
}
- // If on ground or no magnetometer fitted, inhibit magnetometer bias updates by
- // setting the coresponding covariance terms to zero.
- if (onGround || !useCompass)
- {
- zeroRows(nextP,16,21);
- zeroCols(nextP,16,21);
- }
-
- // If on ground or not using airspeed sensing, inhibit wind velocity
- // covariance growth.
- if (onGround || !useAirspeed)
- {
- zeroRows(nextP,14,15);
- zeroCols(nextP,14,15);
- }
-
- // If on ground, inhibit terrain offset updates by
- // setting the coresponding covariance terms to zero.
- if (onGround)
- {
- zeroRows(nextP,22,22);
- zeroCols(nextP,22,22);
- }
-
// If the total position variance exceds 1E6 (1000m), then stop covariance
// growth by setting the predicted to the previous values
// This prevent an ill conditioned matrix from occurring for long periods
@@ -972,57 +980,46 @@ void AttPosEKF::CovariancePrediction(float dt)
}
}
- if (onGround || staticMode) {
- // copy the portion of the variances we want to
- // propagate
- for (unsigned i = 0; i <= 13; i++) {
- P[i][i] = nextP[i][i];
+ // Copy covariance
+ for (unsigned i = 0; i < n_states; i++) {
+ P[i][i] = nextP[i][i];
+ }
- // force symmetry for observable states
- // force zero for non-observable states
- for (unsigned i = 1; i < n_states; i++)
- {
- for (uint8_t j = 0; j < i; j++)
- {
- if ((i > 13) || (j > 13)) {
- P[i][j] = 0.0f;
- } else {
- P[i][j] = 0.5f * (nextP[i][j] + nextP[j][i]);
- }
- P[j][i] = P[i][j];
- }
- }
+ // force symmetry for observable states
+ for (unsigned i = 1; i < n_states; i++)
+ {
+ for (uint8_t j = 0; j < i; j++)
+ {
+ P[i][j] = 0.5f * (nextP[i][j] + nextP[j][i]);
+ P[j][i] = P[i][j];
}
+ }
- } else {
-
- // Copy covariance
- for (unsigned i = 0; i < n_states; i++) {
- P[i][i] = nextP[i][i];
- }
+ ConstrainVariances();
+}
- // force symmetry for observable states
- for (unsigned i = 1; i < n_states; i++)
- {
- for (uint8_t j = 0; j < i; j++)
- {
- P[i][j] = 0.5f * (nextP[i][j] + nextP[j][i]);
- P[j][i] = P[i][j];
- }
- }
+void AttPosEKF::updateDtGpsFilt(float dt)
+{
+ dtGpsFilt = ConstrainFloat(dt, 0.001f, 2.0f) * 0.05f + dtGpsFilt * 0.95f;
+}
- }
+void AttPosEKF::updateDtHgtFilt(float dt)
+{
+ dtHgtFilt = ConstrainFloat(dt, 0.001f, 2.0f) * 0.05f + dtHgtFilt * 0.95f;
+}
- ConstrainVariances();
+void AttPosEKF::updateDtVelPosFilt(float dt)
+{
+ dtVelPosFilt = ConstrainFloat(dt, 0.0005f, 2.0f) * 0.05f + dtVelPosFilt * 0.95f;
}
void AttPosEKF::FuseVelposNED()
{
// declare variables used by fault isolation logic
- uint32_t gpsRetryTime = 30000; // time in msec before GPS fusion will be retried following innovation consistency failure
- uint32_t gpsRetryTimeNoTAS = 5000; // retry time if no TAS measurement available
- uint32_t hgtRetryTime = 5000; // height measurement retry time
+ uint32_t gpsRetryTime = 3000; // time in msec before GPS fusion will be retried following innovation consistency failure
+ uint32_t gpsRetryTimeNoTAS = 500; // retry time if no TAS measurement available
+ uint32_t hgtRetryTime = 500; // height measurement retry time
uint32_t horizRetryTime;
// declare variables used to check measurement errors
@@ -1034,7 +1031,7 @@ void AttPosEKF::FuseVelposNED()
bool fuseData[6] = {false,false,false,false,false,false};
uint8_t stateIndex;
uint8_t obsIndex;
- uint8_t indexLimit;
+ uint8_t indexLimit = 22;
// declare variables used by state and covariance update calculations
float velErr;
@@ -1052,6 +1049,18 @@ void AttPosEKF::FuseVelposNED()
// associated with sequential fusion
if (fuseVelData || fusePosData || fuseHgtData)
{
+ uint64_t tNow = getMicros();
+ updateDtVelPosFilt((tNow - lastVelPosFusion) / 1e6f);
+ lastVelPosFusion = tNow;
+
+ // scaler according to the number of repetitions of the
+ // same measurement in one fusion step
+ float gpsVarianceScaler = dtGpsFilt / dtVelPosFilt;
+
+ // scaler according to the number of repetitions of the
+ // same measurement in one fusion step
+ float hgtVarianceScaler = dtHgtFilt / dtVelPosFilt;
+
// set the GPS data timeout depending on whether airspeed data is present
if (useAirspeed) horizRetryTime = gpsRetryTime;
else horizRetryTime = gpsRetryTimeNoTAS;
@@ -1064,18 +1073,13 @@ void AttPosEKF::FuseVelposNED()
// Estimate the GPS Velocity, GPS horiz position and height measurement variances.
velErr = 0.2f*accNavMag; // additional error in GPS velocities caused by manoeuvring
posErr = 0.2f*accNavMag; // additional error in GPS position caused by manoeuvring
- R_OBS[0] = sq(vneSigma) + sq(velErr);
+ R_OBS[0] = gpsVarianceScaler * sq(vneSigma) + sq(velErr);
R_OBS[1] = R_OBS[0];
- R_OBS[2] = sq(vdSigma) + sq(velErr);
- R_OBS[3] = sq(posNeSigma) + sq(posErr);
+ R_OBS[2] = gpsVarianceScaler * sq(vdSigma) + sq(velErr);
+ R_OBS[3] = gpsVarianceScaler * sq(posNeSigma) + sq(posErr);
R_OBS[4] = R_OBS[3];
- R_OBS[5] = sq(posDSigma) + sq(posErr);
+ R_OBS[5] = hgtVarianceScaler * sq(posDSigma) + sq(posErr);
- // Set innovation variances to zero default
- for (uint8_t i = 0; i<=5; i++)
- {
- varInnovVelPos[i] = 0.0f;
- }
// calculate innovations and check GPS data validity using an innovation consistency check
if (fuseVelData)
{
@@ -1091,10 +1095,16 @@ void AttPosEKF::FuseVelposNED()
// apply a 5-sigma threshold
current_ekf_state.velHealth = (sq(velInnov[0]) + sq(velInnov[1]) + sq(velInnov[2])) < 25.0f * (varInnovVelPos[0] + varInnovVelPos[1] + varInnovVelPos[2]);
current_ekf_state.velTimeout = (millis() - current_ekf_state.velFailTime) > horizRetryTime;
- if (current_ekf_state.velHealth || current_ekf_state.velTimeout)
- {
+ if (current_ekf_state.velHealth || staticMode) {
current_ekf_state.velHealth = true;
current_ekf_state.velFailTime = millis();
+ } else if (current_ekf_state.velTimeout || !current_ekf_state.posHealth) {
+ // XXX check
+ current_ekf_state.velHealth = true;
+ ResetVelocity();
+ ResetStoredStates();
+ // do not fuse bad data
+ fuseVelData = false;
}
else
{
@@ -1115,6 +1125,17 @@ void AttPosEKF::FuseVelposNED()
{
current_ekf_state.posHealth = true;
current_ekf_state.posFailTime = millis();
+
+ if (current_ekf_state.posTimeout) {
+ ResetPosition();
+
+ // XXX cross-check the state reset
+ ResetStoredStates();
+
+ // do not fuse position data on this time
+ // step
+ fusePosData = false;
+ }
}
else
{
@@ -1129,10 +1150,18 @@ void AttPosEKF::FuseVelposNED()
// apply a 10-sigma threshold
current_ekf_state.hgtHealth = sq(hgtInnov) < 100.0f*varInnovVelPos[5];
current_ekf_state.hgtTimeout = (millis() - current_ekf_state.hgtFailTime) > hgtRetryTime;
- if (current_ekf_state.hgtHealth || current_ekf_state.hgtTimeout)
+ if (current_ekf_state.hgtHealth || current_ekf_state.hgtTimeout || staticMode)
{
current_ekf_state.hgtHealth = true;
current_ekf_state.hgtFailTime = millis();
+
+ // if we just reset from a timeout, do not fuse
+ // the height data, but reset height and stored states
+ if (current_ekf_state.hgtTimeout) {
+ ResetHeight();
+ ResetStoredStates();
+ fuseHgtData = false;
+ }
}
else
{
@@ -1161,15 +1190,6 @@ void AttPosEKF::FuseVelposNED()
{
fuseData[5] = true;
}
- // Limit range of states modified when on ground
- if(!onGround)
- {
- indexLimit = 22;
- }
- else
- {
- indexLimit = 13;
- }
// Fuse measurements sequentially
for (obsIndex=0; obsIndex<=5; obsIndex++)
{
@@ -1178,7 +1198,7 @@ void AttPosEKF::FuseVelposNED()
stateIndex = 4 + obsIndex;
// Calculate the measurement innovation, using states from a
// different time coordinate if fusing height data
- if (obsIndex >= 0 && obsIndex <= 2)
+ if (obsIndex <= 2)
{
innovVelPos[obsIndex] = statesAtVelTime[stateIndex] - observation[obsIndex];
}
@@ -1193,7 +1213,7 @@ void AttPosEKF::FuseVelposNED()
// Calculate the Kalman Gain
// Calculate innovation variances - also used for data logging
varInnovVelPos[obsIndex] = P[stateIndex][stateIndex] + R_OBS[obsIndex];
- SK = 1.0/varInnovVelPos[obsIndex];
+ SK = 1.0/(double)varInnovVelPos[obsIndex];
for (uint8_t i= 0; i<=indexLimit; i++)
{
Kfusion[i] = P[i][stateIndex]*SK;
@@ -1203,6 +1223,22 @@ void AttPosEKF::FuseVelposNED()
if (obsIndex != 5) {
Kfusion[13] = 0;
}
+ // Don't update wind states if inhibited
+ if (inhibitWindStates) {
+ Kfusion[14] = 0;
+ Kfusion[15] = 0;
+ }
+ // Don't update magnetic field states if inhibited
+ if (inhibitMagStates) {
+ for (uint8_t i = 16; i<=21; i++)
+ {
+ Kfusion[i] = 0;
+ }
+ }
+ // Don't update terrain state if inhibited
+ if (inhibitGndState) {
+ Kfusion[22] = 0;
+ }
// Calculate state corrections and re-normalise the quaternions
for (uint8_t i = 0; i<=indexLimit; i++)
@@ -1269,7 +1305,6 @@ void AttPosEKF::FuseMagnetometer()
for (uint8_t i = 0; i < n_states; i++) {
H_MAG[i] = 0.0f;
}
- unsigned indexLimit;
// Perform sequential fusion of Magnetometer measurements.
// This assumes that the errors in the different components are
@@ -1277,23 +1312,10 @@ void AttPosEKF::FuseMagnetometer()
// data fit is the only assumption we can make
// so we might as well take advantage of the computational efficiencies
// associated with sequential fusion
- if (useCompass && (fuseMagData || obsIndex == 1 || obsIndex == 2))
+ if (useCompass && fuseMagData && (obsIndex < 3))
{
- // Limit range of states modified when on ground
- if(!onGround)
- {
- indexLimit = n_states;
- }
- else
- {
- indexLimit = 13 + 1;
- }
-
- // Sequential fusion of XYZ components to spread processing load across
- // three prediction time steps.
-
// Calculate observation jacobians and Kalman gains
- if (fuseMagData)
+ if (obsIndex == 0)
{
// Copy required states to local variable names
q0 = statesAtMagMeasTime[0];
@@ -1377,22 +1399,33 @@ void AttPosEKF::FuseMagnetometer()
Kfusion[12] = SK_MX[0]*(P[12][19] + P[12][1]*SH_MAG[0] + P[12][3]*SH_MAG[2] + P[12][0]*SK_MX[3] - P[12][2]*SK_MX[2] - P[12][16]*SK_MX[1] + P[12][17]*SK_MX[5] - P[12][18]*SK_MX[4]);
// Only height measurements are allowed to modify the Z delta velocity bias state. This improves the stability of the estimate
Kfusion[13] = 0.0f;//SK_MX[0]*(P[13][19] + P[13][1]*SH_MAG[0] + P[13][3]*SH_MAG[2] + P[13][0]*SK_MX[3] - P[13][2]*SK_MX[2] - P[13][16]*SK_MX[1] + P[13][17]*SK_MX[5] - P[13][18]*SK_MX[4]);
- Kfusion[14] = SK_MX[0]*(P[14][19] + P[14][1]*SH_MAG[0] + P[14][3]*SH_MAG[2] + P[14][0]*SK_MX[3] - P[14][2]*SK_MX[2] - P[14][16]*SK_MX[1] + P[14][17]*SK_MX[5] - P[14][18]*SK_MX[4]);
- Kfusion[15] = SK_MX[0]*(P[15][19] + P[15][1]*SH_MAG[0] + P[15][3]*SH_MAG[2] + P[15][0]*SK_MX[3] - P[15][2]*SK_MX[2] - P[15][16]*SK_MX[1] + P[15][17]*SK_MX[5] - P[15][18]*SK_MX[4]);
- Kfusion[16] = SK_MX[0]*(P[16][19] + P[16][1]*SH_MAG[0] + P[16][3]*SH_MAG[2] + P[16][0]*SK_MX[3] - P[16][2]*SK_MX[2] - P[16][16]*SK_MX[1] + P[16][17]*SK_MX[5] - P[16][18]*SK_MX[4]);
- Kfusion[17] = SK_MX[0]*(P[17][19] + P[17][1]*SH_MAG[0] + P[17][3]*SH_MAG[2] + P[17][0]*SK_MX[3] - P[17][2]*SK_MX[2] - P[17][16]*SK_MX[1] + P[17][17]*SK_MX[5] - P[17][18]*SK_MX[4]);
- Kfusion[18] = SK_MX[0]*(P[18][19] + P[18][1]*SH_MAG[0] + P[18][3]*SH_MAG[2] + P[18][0]*SK_MX[3] - P[18][2]*SK_MX[2] - P[18][16]*SK_MX[1] + P[18][17]*SK_MX[5] - P[18][18]*SK_MX[4]);
- Kfusion[19] = SK_MX[0]*(P[19][19] + P[19][1]*SH_MAG[0] + P[19][3]*SH_MAG[2] + P[19][0]*SK_MX[3] - P[19][2]*SK_MX[2] - P[19][16]*SK_MX[1] + P[19][17]*SK_MX[5] - P[19][18]*SK_MX[4]);
- Kfusion[20] = SK_MX[0]*(P[20][19] + P[20][1]*SH_MAG[0] + P[20][3]*SH_MAG[2] + P[20][0]*SK_MX[3] - P[20][2]*SK_MX[2] - P[20][16]*SK_MX[1] + P[20][17]*SK_MX[5] - P[20][18]*SK_MX[4]);
- Kfusion[21] = SK_MX[0]*(P[21][19] + P[21][1]*SH_MAG[0] + P[21][3]*SH_MAG[2] + P[21][0]*SK_MX[3] - P[21][2]*SK_MX[2] - P[21][16]*SK_MX[1] + P[21][17]*SK_MX[5] - P[21][18]*SK_MX[4]);
- Kfusion[22] = SK_MX[0]*(P[22][19] + P[22][1]*SH_MAG[0] + P[22][3]*SH_MAG[2] + P[22][0]*SK_MX[3] - P[22][2]*SK_MX[2] - P[22][16]*SK_MX[1] + P[22][17]*SK_MX[5] - P[22][18]*SK_MX[4]);
+ // Estimation of selected states is inhibited by setting their Kalman gains to zero
+ if (!inhibitWindStates) {
+ Kfusion[14] = SK_MX[0]*(P[14][19] + P[14][1]*SH_MAG[0] + P[14][3]*SH_MAG[2] + P[14][0]*SK_MX[3] - P[14][2]*SK_MX[2] - P[14][16]*SK_MX[1] + P[14][17]*SK_MX[5] - P[14][18]*SK_MX[4]);
+ Kfusion[15] = SK_MX[0]*(P[15][19] + P[15][1]*SH_MAG[0] + P[15][3]*SH_MAG[2] + P[15][0]*SK_MX[3] - P[15][2]*SK_MX[2] - P[15][16]*SK_MX[1] + P[15][17]*SK_MX[5] - P[15][18]*SK_MX[4]);
+ } else {
+ Kfusion[14] = 0;
+ Kfusion[15] = 0;
+ }
+ if (!inhibitMagStates) {
+ Kfusion[16] = SK_MX[0]*(P[16][19] + P[16][1]*SH_MAG[0] + P[16][3]*SH_MAG[2] + P[16][0]*SK_MX[3] - P[16][2]*SK_MX[2] - P[16][16]*SK_MX[1] + P[16][17]*SK_MX[5] - P[16][18]*SK_MX[4]);
+ Kfusion[17] = SK_MX[0]*(P[17][19] + P[17][1]*SH_MAG[0] + P[17][3]*SH_MAG[2] + P[17][0]*SK_MX[3] - P[17][2]*SK_MX[2] - P[17][16]*SK_MX[1] + P[17][17]*SK_MX[5] - P[17][18]*SK_MX[4]);
+ Kfusion[18] = SK_MX[0]*(P[18][19] + P[18][1]*SH_MAG[0] + P[18][3]*SH_MAG[2] + P[18][0]*SK_MX[3] - P[18][2]*SK_MX[2] - P[18][16]*SK_MX[1] + P[18][17]*SK_MX[5] - P[18][18]*SK_MX[4]);
+ Kfusion[19] = SK_MX[0]*(P[19][19] + P[19][1]*SH_MAG[0] + P[19][3]*SH_MAG[2] + P[19][0]*SK_MX[3] - P[19][2]*SK_MX[2] - P[19][16]*SK_MX[1] + P[19][17]*SK_MX[5] - P[19][18]*SK_MX[4]);
+ Kfusion[20] = SK_MX[0]*(P[20][19] + P[20][1]*SH_MAG[0] + P[20][3]*SH_MAG[2] + P[20][0]*SK_MX[3] - P[20][2]*SK_MX[2] - P[20][16]*SK_MX[1] + P[20][17]*SK_MX[5] - P[20][18]*SK_MX[4]);
+ Kfusion[21] = SK_MX[0]*(P[21][19] + P[21][1]*SH_MAG[0] + P[21][3]*SH_MAG[2] + P[21][0]*SK_MX[3] - P[21][2]*SK_MX[2] - P[21][16]*SK_MX[1] + P[21][17]*SK_MX[5] - P[21][18]*SK_MX[4]);
+ } else {
+ for (uint8_t i=16; i <= 21; i++) {
+ Kfusion[i] = 0;
+ }
+ }
+ if (!inhibitGndState) {
+ Kfusion[22] = SK_MX[0]*(P[22][19] + P[22][1]*SH_MAG[0] + P[22][3]*SH_MAG[2] + P[22][0]*SK_MX[3] - P[22][2]*SK_MX[2] - P[22][16]*SK_MX[1] + P[22][17]*SK_MX[5] - P[22][18]*SK_MX[4]);
+ } else {
+ Kfusion[22] = 0;
+ }
varInnovMag[0] = 1.0f/SK_MX[0];
innovMag[0] = MagPred[0] - magData.x;
-
- // reset the observation index to 0 (we start by fusing the X
- // measurement)
- obsIndex = 0;
- fuseMagData = false;
}
else if (obsIndex == 1) // we are now fusing the Y measurement
{
@@ -1437,15 +1470,29 @@ void AttPosEKF::FuseMagnetometer()
Kfusion[12] = SK_MY[0]*(P[12][20] + P[12][0]*SH_MAG[2] + P[12][1]*SH_MAG[1] + P[12][2]*SH_MAG[0] - P[12][3]*SK_MY[2] - P[12][17]*SK_MY[1] - P[12][16]*SK_MY[3] + P[12][18]*SK_MY[4]);
// Only height measurements are allowed to modify the Z delta velocity bias state. This improves the stability of the estimate
Kfusion[13] = 0.0f;//SK_MY[0]*(P[13][20] + P[13][0]*SH_MAG[2] + P[13][1]*SH_MAG[1] + P[13][2]*SH_MAG[0] - P[13][3]*SK_MY[2] - P[13][17]*SK_MY[1] - P[13][16]*SK_MY[3] + P[13][18]*SK_MY[4]);
- Kfusion[14] = SK_MY[0]*(P[14][20] + P[14][0]*SH_MAG[2] + P[14][1]*SH_MAG[1] + P[14][2]*SH_MAG[0] - P[14][3]*SK_MY[2] - P[14][17]*SK_MY[1] - P[14][16]*SK_MY[3] + P[14][18]*SK_MY[4]);
- Kfusion[15] = SK_MY[0]*(P[15][20] + P[15][0]*SH_MAG[2] + P[15][1]*SH_MAG[1] + P[15][2]*SH_MAG[0] - P[15][3]*SK_MY[2] - P[15][17]*SK_MY[1] - P[15][16]*SK_MY[3] + P[15][18]*SK_MY[4]);
- Kfusion[16] = SK_MY[0]*(P[16][20] + P[16][0]*SH_MAG[2] + P[16][1]*SH_MAG[1] + P[16][2]*SH_MAG[0] - P[16][3]*SK_MY[2] - P[16][17]*SK_MY[1] - P[16][16]*SK_MY[3] + P[16][18]*SK_MY[4]);
- Kfusion[17] = SK_MY[0]*(P[17][20] + P[17][0]*SH_MAG[2] + P[17][1]*SH_MAG[1] + P[17][2]*SH_MAG[0] - P[17][3]*SK_MY[2] - P[17][17]*SK_MY[1] - P[17][16]*SK_MY[3] + P[17][18]*SK_MY[4]);
- Kfusion[18] = SK_MY[0]*(P[18][20] + P[18][0]*SH_MAG[2] + P[18][1]*SH_MAG[1] + P[18][2]*SH_MAG[0] - P[18][3]*SK_MY[2] - P[18][17]*SK_MY[1] - P[18][16]*SK_MY[3] + P[18][18]*SK_MY[4]);
- Kfusion[19] = SK_MY[0]*(P[19][20] + P[19][0]*SH_MAG[2] + P[19][1]*SH_MAG[1] + P[19][2]*SH_MAG[0] - P[19][3]*SK_MY[2] - P[19][17]*SK_MY[1] - P[19][16]*SK_MY[3] + P[19][18]*SK_MY[4]);
- Kfusion[20] = SK_MY[0]*(P[20][20] + P[20][0]*SH_MAG[2] + P[20][1]*SH_MAG[1] + P[20][2]*SH_MAG[0] - P[20][3]*SK_MY[2] - P[20][17]*SK_MY[1] - P[20][16]*SK_MY[3] + P[20][18]*SK_MY[4]);
- Kfusion[21] = SK_MY[0]*(P[21][20] + P[21][0]*SH_MAG[2] + P[21][1]*SH_MAG[1] + P[21][2]*SH_MAG[0] - P[21][3]*SK_MY[2] - P[21][17]*SK_MY[1] - P[21][16]*SK_MY[3] + P[21][18]*SK_MY[4]);
- Kfusion[22] = SK_MY[0]*(P[22][20] + P[22][0]*SH_MAG[2] + P[22][1]*SH_MAG[1] + P[22][2]*SH_MAG[0] - P[22][3]*SK_MY[2] - P[22][17]*SK_MY[1] - P[22][16]*SK_MY[3] + P[22][18]*SK_MY[4]);
+ // Estimation of selected states is inhibited by setting their Kalman gains to zero
+ if (!inhibitWindStates) {
+ Kfusion[14] = SK_MY[0]*(P[14][20] + P[14][0]*SH_MAG[2] + P[14][1]*SH_MAG[1] + P[14][2]*SH_MAG[0] - P[14][3]*SK_MY[2] - P[14][17]*SK_MY[1] - P[14][16]*SK_MY[3] + P[14][18]*SK_MY[4]);
+ Kfusion[15] = SK_MY[0]*(P[15][20] + P[15][0]*SH_MAG[2] + P[15][1]*SH_MAG[1] + P[15][2]*SH_MAG[0] - P[15][3]*SK_MY[2] - P[15][17]*SK_MY[1] - P[15][16]*SK_MY[3] + P[15][18]*SK_MY[4]);
+ } else {
+ Kfusion[14] = 0;
+ Kfusion[15] = 0;
+ }
+ if (!inhibitMagStates) {
+ Kfusion[16] = SK_MY[0]*(P[16][20] + P[16][0]*SH_MAG[2] + P[16][1]*SH_MAG[1] + P[16][2]*SH_MAG[0] - P[16][3]*SK_MY[2] - P[16][17]*SK_MY[1] - P[16][16]*SK_MY[3] + P[16][18]*SK_MY[4]);
+ Kfusion[17] = SK_MY[0]*(P[17][20] + P[17][0]*SH_MAG[2] + P[17][1]*SH_MAG[1] + P[17][2]*SH_MAG[0] - P[17][3]*SK_MY[2] - P[17][17]*SK_MY[1] - P[17][16]*SK_MY[3] + P[17][18]*SK_MY[4]);
+ Kfusion[18] = SK_MY[0]*(P[18][20] + P[18][0]*SH_MAG[2] + P[18][1]*SH_MAG[1] + P[18][2]*SH_MAG[0] - P[18][3]*SK_MY[2] - P[18][17]*SK_MY[1] - P[18][16]*SK_MY[3] + P[18][18]*SK_MY[4]);
+ Kfusion[19] = SK_MY[0]*(P[19][20] + P[19][0]*SH_MAG[2] + P[19][1]*SH_MAG[1] + P[19][2]*SH_MAG[0] - P[19][3]*SK_MY[2] - P[19][17]*SK_MY[1] - P[19][16]*SK_MY[3] + P[19][18]*SK_MY[4]);
+ Kfusion[20] = SK_MY[0]*(P[20][20] + P[20][0]*SH_MAG[2] + P[20][1]*SH_MAG[1] + P[20][2]*SH_MAG[0] - P[20][3]*SK_MY[2] - P[20][17]*SK_MY[1] - P[20][16]*SK_MY[3] + P[20][18]*SK_MY[4]);
+ Kfusion[21] = SK_MY[0]*(P[21][20] + P[21][0]*SH_MAG[2] + P[21][1]*SH_MAG[1] + P[21][2]*SH_MAG[0] - P[21][3]*SK_MY[2] - P[21][17]*SK_MY[1] - P[21][16]*SK_MY[3] + P[21][18]*SK_MY[4]);
+ } else {
+ Kfusion[16] = 0;
+ Kfusion[17] = 0;
+ Kfusion[18] = 0;
+ Kfusion[19] = 0;
+ Kfusion[20] = 0;
+ Kfusion[21] = 0;
+ }
varInnovMag[1] = 1.0f/SK_MY[0];
innovMag[1] = MagPred[1] - magData.y;
}
@@ -1493,31 +1540,45 @@ void AttPosEKF::FuseMagnetometer()
Kfusion[12] = SK_MZ[0]*(P[12][21] + P[12][0]*SH_MAG[1] + P[12][3]*SH_MAG[0] - P[12][1]*SK_MZ[2] + P[12][2]*SK_MZ[3] + P[12][18]*SK_MZ[1] + P[12][16]*SK_MZ[5] - P[12][17]*SK_MZ[4]);
// Only height measurements are allowed to modify the Z delta velocity bias state. This improves the stability of the estimate
Kfusion[13] = 0.0f;//SK_MZ[0]*(P[13][21] + P[13][0]*SH_MAG[1] + P[13][3]*SH_MAG[0] - P[13][1]*SK_MZ[2] + P[13][2]*SK_MZ[3] + P[13][18]*SK_MZ[1] + P[13][16]*SK_MZ[5] - P[13][17]*SK_MZ[4]);
- Kfusion[14] = SK_MZ[0]*(P[14][21] + P[14][0]*SH_MAG[1] + P[14][3]*SH_MAG[0] - P[14][1]*SK_MZ[2] + P[14][2]*SK_MZ[3] + P[14][18]*SK_MZ[1] + P[14][16]*SK_MZ[5] - P[14][17]*SK_MZ[4]);
- Kfusion[15] = SK_MZ[0]*(P[15][21] + P[15][0]*SH_MAG[1] + P[15][3]*SH_MAG[0] - P[15][1]*SK_MZ[2] + P[15][2]*SK_MZ[3] + P[15][18]*SK_MZ[1] + P[15][16]*SK_MZ[5] - P[15][17]*SK_MZ[4]);
- Kfusion[16] = SK_MZ[0]*(P[16][21] + P[16][0]*SH_MAG[1] + P[16][3]*SH_MAG[0] - P[16][1]*SK_MZ[2] + P[16][2]*SK_MZ[3] + P[16][18]*SK_MZ[1] + P[16][16]*SK_MZ[5] - P[16][17]*SK_MZ[4]);
- Kfusion[17] = SK_MZ[0]*(P[17][21] + P[17][0]*SH_MAG[1] + P[17][3]*SH_MAG[0] - P[17][1]*SK_MZ[2] + P[17][2]*SK_MZ[3] + P[17][18]*SK_MZ[1] + P[17][16]*SK_MZ[5] - P[17][17]*SK_MZ[4]);
- Kfusion[18] = SK_MZ[0]*(P[18][21] + P[18][0]*SH_MAG[1] + P[18][3]*SH_MAG[0] - P[18][1]*SK_MZ[2] + P[18][2]*SK_MZ[3] + P[18][18]*SK_MZ[1] + P[18][16]*SK_MZ[5] - P[18][17]*SK_MZ[4]);
- Kfusion[19] = SK_MZ[0]*(P[19][21] + P[19][0]*SH_MAG[1] + P[19][3]*SH_MAG[0] - P[19][1]*SK_MZ[2] + P[19][2]*SK_MZ[3] + P[19][18]*SK_MZ[1] + P[19][16]*SK_MZ[5] - P[19][17]*SK_MZ[4]);
- Kfusion[20] = SK_MZ[0]*(P[20][21] + P[20][0]*SH_MAG[1] + P[20][3]*SH_MAG[0] - P[20][1]*SK_MZ[2] + P[20][2]*SK_MZ[3] + P[20][18]*SK_MZ[1] + P[20][16]*SK_MZ[5] - P[20][17]*SK_MZ[4]);
- Kfusion[21] = SK_MZ[0]*(P[21][21] + P[21][0]*SH_MAG[1] + P[21][3]*SH_MAG[0] - P[21][1]*SK_MZ[2] + P[21][2]*SK_MZ[3] + P[21][18]*SK_MZ[1] + P[21][16]*SK_MZ[5] - P[21][17]*SK_MZ[4]);
- Kfusion[22] = SK_MZ[0]*(P[22][21] + P[22][0]*SH_MAG[1] + P[22][3]*SH_MAG[0] - P[22][1]*SK_MZ[2] + P[22][2]*SK_MZ[3] + P[22][18]*SK_MZ[1] + P[22][16]*SK_MZ[5] - P[22][17]*SK_MZ[4]);
+ // Estimation of selected states is inhibited by setting their Kalman gains to zero
+ if (!inhibitWindStates) {
+ Kfusion[14] = SK_MZ[0]*(P[14][21] + P[14][0]*SH_MAG[1] + P[14][3]*SH_MAG[0] - P[14][1]*SK_MZ[2] + P[14][2]*SK_MZ[3] + P[14][18]*SK_MZ[1] + P[14][16]*SK_MZ[5] - P[14][17]*SK_MZ[4]);
+ Kfusion[15] = SK_MZ[0]*(P[15][21] + P[15][0]*SH_MAG[1] + P[15][3]*SH_MAG[0] - P[15][1]*SK_MZ[2] + P[15][2]*SK_MZ[3] + P[15][18]*SK_MZ[1] + P[15][16]*SK_MZ[5] - P[15][17]*SK_MZ[4]);
+ } else {
+ Kfusion[14] = 0;
+ Kfusion[15] = 0;
+ }
+ if (!inhibitMagStates) {
+ Kfusion[16] = SK_MZ[0]*(P[16][21] + P[16][0]*SH_MAG[1] + P[16][3]*SH_MAG[0] - P[16][1]*SK_MZ[2] + P[16][2]*SK_MZ[3] + P[16][18]*SK_MZ[1] + P[16][16]*SK_MZ[5] - P[16][17]*SK_MZ[4]);
+ Kfusion[17] = SK_MZ[0]*(P[17][21] + P[17][0]*SH_MAG[1] + P[17][3]*SH_MAG[0] - P[17][1]*SK_MZ[2] + P[17][2]*SK_MZ[3] + P[17][18]*SK_MZ[1] + P[17][16]*SK_MZ[5] - P[17][17]*SK_MZ[4]);
+ Kfusion[18] = SK_MZ[0]*(P[18][21] + P[18][0]*SH_MAG[1] + P[18][3]*SH_MAG[0] - P[18][1]*SK_MZ[2] + P[18][2]*SK_MZ[3] + P[18][18]*SK_MZ[1] + P[18][16]*SK_MZ[5] - P[18][17]*SK_MZ[4]);
+ Kfusion[19] = SK_MZ[0]*(P[19][21] + P[19][0]*SH_MAG[1] + P[19][3]*SH_MAG[0] - P[19][1]*SK_MZ[2] + P[19][2]*SK_MZ[3] + P[19][18]*SK_MZ[1] + P[19][16]*SK_MZ[5] - P[19][17]*SK_MZ[4]);
+ Kfusion[20] = SK_MZ[0]*(P[20][21] + P[20][0]*SH_MAG[1] + P[20][3]*SH_MAG[0] - P[20][1]*SK_MZ[2] + P[20][2]*SK_MZ[3] + P[20][18]*SK_MZ[1] + P[20][16]*SK_MZ[5] - P[20][17]*SK_MZ[4]);
+ Kfusion[21] = SK_MZ[0]*(P[21][21] + P[21][0]*SH_MAG[1] + P[21][3]*SH_MAG[0] - P[21][1]*SK_MZ[2] + P[21][2]*SK_MZ[3] + P[21][18]*SK_MZ[1] + P[21][16]*SK_MZ[5] - P[21][17]*SK_MZ[4]);
+ } else {
+ Kfusion[16] = 0;
+ Kfusion[17] = 0;
+ Kfusion[18] = 0;
+ Kfusion[19] = 0;
+ Kfusion[20] = 0;
+ Kfusion[21] = 0;
+ }
varInnovMag[2] = 1.0f/SK_MZ[0];
innovMag[2] = MagPred[2] - magData.z;
}
// Check the innovation for consistency and don't fuse if > 5Sigma
- if ((innovMag[obsIndex]*innovMag[obsIndex]/varInnovMag[obsIndex]) < 25.0)
+ if ((innovMag[obsIndex]*innovMag[obsIndex]/varInnovMag[obsIndex]) < 25.0f)
{
// correct the state vector
- for (uint8_t j= 0; j < indexLimit; j++)
+ for (uint8_t j= 0; j < n_states; j++)
{
states[j] = states[j] - Kfusion[j] * innovMag[obsIndex];
}
// normalise the quaternion states
float quatMag = sqrt(states[0]*states[0] + states[1]*states[1] + states[2]*states[2] + states[3]*states[3]);
- if (quatMag > 1e-12)
+ if (quatMag > 1e-12f)
{
for (uint8_t j= 0; j<=3; j++)
{
@@ -1528,7 +1589,7 @@ void AttPosEKF::FuseMagnetometer()
// correct the covariance P = (I - K*H)*P
// take advantage of the empty columns in KH to reduce the
// number of operations
- for (uint8_t i = 0; i < indexLimit; i++)
+ for (uint8_t i = 0; i < n_states; i++)
{
for (uint8_t j = 0; j <= 3; j++)
{
@@ -1550,9 +1611,9 @@ void AttPosEKF::FuseMagnetometer()
}
}
}
- for (uint8_t i = 0; i < indexLimit; i++)
+ for (uint8_t i = 0; i < n_states; i++)
{
- for (uint8_t j = 0; j < indexLimit; j++)
+ for (uint8_t j = 0; j < n_states; j++)
{
KHP[i][j] = 0.0f;
for (uint8_t k = 0; k <= 3; k++)
@@ -1569,9 +1630,9 @@ void AttPosEKF::FuseMagnetometer()
}
}
}
- for (uint8_t i = 0; i < indexLimit; i++)
+ for (uint8_t i = 0; i < n_states; i++)
{
- for (uint8_t j = 0; j < indexLimit; j++)
+ for (uint8_t j = 0; j < n_states; j++)
{
P[i][j] = P[i][j] - KHP[i][j];
}
@@ -1608,11 +1669,37 @@ void AttPosEKF::FuseAirspeed()
// Perform fusion of True Airspeed measurement
if (useAirspeed && fuseVtasData && (VtasPred > 1.0f) && (VtasMeas > 8.0f))
{
+
+ float altDiff = fabsf(windSpdFiltAltitude - hgtMea);
+
+ if (isfinite(windSpdFiltClimb)) {
+ windSpdFiltClimb = ((1.0f - 0.0002f) * windSpdFiltClimb) + (0.0002f * states[6]);
+ } else {
+ windSpdFiltClimb = states[6];
+ }
+
+ if (altDiff < 20.0f) {
+ // Lowpass the output of the wind estimate - we want a long-term
+ // stable estimate, but not start to load into the overall dynamics
+ // of the system (which adjusting covariances would do)
+
+ // Change filter coefficient based on altitude change rate
+ float windFiltCoeff = ConstrainFloat(fabsf(windSpdFiltClimb) / 1000.0f, 0.00005f, 0.2f);
+
+ windSpdFiltNorth = ((1.0f - windFiltCoeff) * windSpdFiltNorth) + (windFiltCoeff * vwn);
+ windSpdFiltEast = ((1.0f - windFiltCoeff) * windSpdFiltEast) + (windFiltCoeff * vwe);
+ } else {
+ windSpdFiltNorth = vwn;
+ windSpdFiltEast = vwe;
+ }
+
+ windSpdFiltAltitude = hgtMea;
+
// Calculate observation jacobians
SH_TAS[0] = 1/(sqrt(sq(ve - vwe) + sq(vn - vwn) + sq(vd)));
SH_TAS[1] = (SH_TAS[0]*(2.0f*ve - 2*vwe))/2.0f;
SH_TAS[2] = (SH_TAS[0]*(2.0f*vn - 2*vwn))/2.0f;
-
+
float H_TAS[n_states];
for (uint8_t i = 0; i < n_states; i++) H_TAS[i] = 0.0f;
H_TAS[4] = SH_TAS[2];
@@ -1647,21 +1734,32 @@ void AttPosEKF::FuseAirspeed()
Kfusion[12] = SK_TAS*(P[12][4]*SH_TAS[2] - P[12][14]*SH_TAS[2] + P[12][5]*SH_TAS[1] - P[12][15]*SH_TAS[1] + P[12][6]*vd*SH_TAS[0]);
// Only height measurements are allowed to modify the Z delta velocity bias state. This improves the stability of the estimate
Kfusion[13] = 0.0f;//SK_TAS*(P[13][4]*SH_TAS[2] - P[13][14]*SH_TAS[2] + P[13][5]*SH_TAS[1] - P[13][15]*SH_TAS[1] + P[13][6]*vd*SH_TAS[0]);
- Kfusion[14] = SK_TAS*(P[14][4]*SH_TAS[2] - P[14][14]*SH_TAS[2] + P[14][5]*SH_TAS[1] - P[14][15]*SH_TAS[1] + P[14][6]*vd*SH_TAS[0]);
- Kfusion[15] = SK_TAS*(P[15][4]*SH_TAS[2] - P[15][14]*SH_TAS[2] + P[15][5]*SH_TAS[1] - P[15][15]*SH_TAS[1] + P[15][6]*vd*SH_TAS[0]);
- Kfusion[16] = SK_TAS*(P[16][4]*SH_TAS[2] - P[16][14]*SH_TAS[2] + P[16][5]*SH_TAS[1] - P[16][15]*SH_TAS[1] + P[16][6]*vd*SH_TAS[0]);
- Kfusion[17] = SK_TAS*(P[17][4]*SH_TAS[2] - P[17][14]*SH_TAS[2] + P[17][5]*SH_TAS[1] - P[17][15]*SH_TAS[1] + P[17][6]*vd*SH_TAS[0]);
- Kfusion[18] = SK_TAS*(P[18][4]*SH_TAS[2] - P[18][14]*SH_TAS[2] + P[18][5]*SH_TAS[1] - P[18][15]*SH_TAS[1] + P[18][6]*vd*SH_TAS[0]);
- Kfusion[19] = SK_TAS*(P[19][4]*SH_TAS[2] - P[19][14]*SH_TAS[2] + P[19][5]*SH_TAS[1] - P[19][15]*SH_TAS[1] + P[19][6]*vd*SH_TAS[0]);
- Kfusion[20] = SK_TAS*(P[20][4]*SH_TAS[2] - P[20][14]*SH_TAS[2] + P[20][5]*SH_TAS[1] - P[20][15]*SH_TAS[1] + P[20][6]*vd*SH_TAS[0]);
- Kfusion[21] = SK_TAS*(P[21][4]*SH_TAS[2] - P[21][14]*SH_TAS[2] + P[21][5]*SH_TAS[1] - P[21][15]*SH_TAS[1] + P[21][6]*vd*SH_TAS[0]);
- Kfusion[22] = SK_TAS*(P[22][4]*SH_TAS[2] - P[22][14]*SH_TAS[2] + P[22][5]*SH_TAS[1] - P[22][15]*SH_TAS[1] + P[22][6]*vd*SH_TAS[0]);
+ // Estimation of selected states is inhibited by setting their Kalman gains to zero
+ if (!inhibitWindStates) {
+ Kfusion[14] = SK_TAS*(P[14][4]*SH_TAS[2] - P[14][14]*SH_TAS[2] + P[14][5]*SH_TAS[1] - P[14][15]*SH_TAS[1] + P[14][6]*vd*SH_TAS[0]);
+ Kfusion[15] = SK_TAS*(P[15][4]*SH_TAS[2] - P[15][14]*SH_TAS[2] + P[15][5]*SH_TAS[1] - P[15][15]*SH_TAS[1] + P[15][6]*vd*SH_TAS[0]);
+ } else {
+ Kfusion[14] = 0;
+ Kfusion[15] = 0;
+ }
+ if (!inhibitMagStates) {
+ Kfusion[16] = SK_TAS*(P[16][4]*SH_TAS[2] - P[16][14]*SH_TAS[2] + P[16][5]*SH_TAS[1] - P[16][15]*SH_TAS[1] + P[16][6]*vd*SH_TAS[0]);
+ Kfusion[17] = SK_TAS*(P[17][4]*SH_TAS[2] - P[17][14]*SH_TAS[2] + P[17][5]*SH_TAS[1] - P[17][15]*SH_TAS[1] + P[17][6]*vd*SH_TAS[0]);
+ Kfusion[18] = SK_TAS*(P[18][4]*SH_TAS[2] - P[18][14]*SH_TAS[2] + P[18][5]*SH_TAS[1] - P[18][15]*SH_TAS[1] + P[18][6]*vd*SH_TAS[0]);
+ Kfusion[19] = SK_TAS*(P[19][4]*SH_TAS[2] - P[19][14]*SH_TAS[2] + P[19][5]*SH_TAS[1] - P[19][15]*SH_TAS[1] + P[19][6]*vd*SH_TAS[0]);
+ Kfusion[20] = SK_TAS*(P[20][4]*SH_TAS[2] - P[20][14]*SH_TAS[2] + P[20][5]*SH_TAS[1] - P[20][15]*SH_TAS[1] + P[20][6]*vd*SH_TAS[0]);
+ Kfusion[21] = SK_TAS*(P[21][4]*SH_TAS[2] - P[21][14]*SH_TAS[2] + P[21][5]*SH_TAS[1] - P[21][15]*SH_TAS[1] + P[21][6]*vd*SH_TAS[0]);
+ } else {
+ for (uint8_t i=16; i <= 21; i++) {
+ Kfusion[i] = 0;
+ }
+ }
varInnovVtas = 1.0f/SK_TAS;
// Calculate the measurement innovation
innovVtas = VtasPred - VtasMeas;
// Check the innovation for consistency and don't fuse if > 5Sigma
- if ((innovVtas*innovVtas*SK_TAS) < 25.0)
+ if ((innovVtas*innovVtas*SK_TAS) < 25.0f)
{
// correct the state vector
for (uint8_t j=0; j <= 22; j++)
@@ -1757,9 +1855,9 @@ void AttPosEKF::FuseRangeFinder()
float ptd = statesAtRngTime[22];
// Need to check that our range finder tilt angle is less than 30 degrees and we are using range finder data
- SH_RNG[4] = sin(rngFinderPitch);
- cosRngTilt = - Tbn.z.x * SH_RNG[4] + Tbn.z.z * cos(rngFinderPitch);
- if (useRangeFinder && cosRngTilt > 0.87f)
+ SH_RNG[4] = sinf(rngFinderPitch);
+ cosRngTilt = - Tbn.z.x * SH_RNG[4] + Tbn.z.z * cosf(rngFinderPitch);
+ if (useRangeFinder && fuseRngData && cosRngTilt > 0.87f)
{
// Calculate observation jacobian and Kalman gain ignoring all states other than the terrain offset
// This prevents the range finder measurement modifying any of the other filter states and significantly reduces computations
@@ -1780,13 +1878,18 @@ void AttPosEKF::FuseRangeFinder()
SK_RNG[5] = SH_RNG[2];
Kfusion[22] = SK_RNG[0]*(P[22][9]*SH_RNG[3] - P[22][22]*SH_RNG[3] + P[22][0]*SH_RNG[1]*SK_RNG[2]*SK_RNG[5] - P[22][1]*SH_RNG[1]*SK_RNG[1]*SK_RNG[5] - P[22][2]*SH_RNG[1]*SK_RNG[4]*SK_RNG[5] + P[22][3]*SH_RNG[1]*SK_RNG[3]*SK_RNG[5]);
+ // Calculate the innovation variance for data logging
+ varInnovRng = 1.0f/SK_RNG[0];
+
// Calculate the measurement innovation
rngPred = (ptd - pd)/cosRngTilt;
innovRng = rngPred - rngMea;
- //printf("mea=%5.1f, pred=%5.1f, pd=%5.1f, ptd=%5.2f\n", rngMea, rngPred, pd, ptd);
- // Check the innovation for consistency and don't fuse if > 5Sigma
- if ((innovRng*innovRng*SK_RNG[0]) < 25)
+ // calculate the innovation consistency test ratio
+ auxRngTestRatio = sq(innovRng) / (sq(rngInnovGate) * varInnovRng);
+
+ // Check the innovation for consistency and don't fuse if out of bounds
+ if (auxRngTestRatio < 1.0f)
{
// correct the state vector
states[22] = states[22] - Kfusion[22] * innovRng;
@@ -1799,6 +1902,391 @@ void AttPosEKF::FuseRangeFinder()
}
+void AttPosEKF::FuseOptFlow()
+{
+// static uint8_t obsIndex;
+// static float SH_LOS[13];
+// static float SKK_LOS[15];
+// static float SK_LOS[2];
+// static float q0 = 0.0f;
+// static float q1 = 0.0f;
+// static float q2 = 0.0f;
+// static float q3 = 1.0f;
+// static float vn = 0.0f;
+// static float ve = 0.0f;
+// static float vd = 0.0f;
+// static float pd = 0.0f;
+// static float ptd = 0.0f;
+// static float R_LOS = 0.01f;
+// static float losPred[2];
+
+// // Transformation matrix from nav to body axes
+// Mat3f Tnb_local;
+// // Transformation matrix from body to sensor axes
+// // assume camera is aligned with Z body axis plus a misalignment
+// // defined by 3 small angles about X, Y and Z body axis
+// Mat3f Tbs;
+// Tbs.x.y = a3;
+// Tbs.y.x = -a3;
+// Tbs.x.z = -a2;
+// Tbs.z.x = a2;
+// Tbs.y.z = a1;
+// Tbs.z.y = -a1;
+// // Transformation matrix from navigation to sensor axes
+// Mat3f Tns;
+// float H_LOS[n_states];
+// for (uint8_t i = 0; i < n_states; i++) {
+// H_LOS[i] = 0.0f;
+// }
+// Vector3f velNED_local;
+// Vector3f relVelSensor;
+
+// // Perform sequential fusion of optical flow measurements only when in the air and tilt is less than 30 deg.
+// if (useOpticalFlow && (fuseOptFlowData || obsIndex == 1) && !onGround && Tbs.z.z > 0.866f && rngMea > 5.0f && rngMea < 39.0f)
+// {
+// // Sequential fusion of XY components to spread processing load across
+// // two prediction time steps.
+
+// // Calculate observation jacobians and Kalman gains
+// if (fuseOptFlowData)
+// {
+// // Copy required states to local variable names
+// q0 = statesAtOptFlowTime[0];
+// q1 = statesAtOptFlowTime[1];
+// q2 = statesAtOptFlowTime[2];
+// q3 = statesAtOptFlowTime[3];
+// vn = statesAtOptFlowTime[4];
+// ve = statesAtOptFlowTime[5];
+// vd = statesAtOptFlowTime[6];
+// pd = statesAtOptFlowTime[9];
+// ptd = statesAtOptFlowTime[22];
+// velNED_local.x = vn;
+// velNED_local.y = ve;
+// velNED_local.z = vd;
+
+// // calculate rotation from NED to body axes
+// float q00 = sq(q0);
+// float q11 = sq(q1);
+// float q22 = sq(q2);
+// float q33 = sq(q3);
+// float q01 = q0 * q1;
+// float q02 = q0 * q2;
+// float q03 = q0 * q3;
+// float q12 = q1 * q2;
+// float q13 = q1 * q3;
+// float q23 = q2 * q3;
+// Tnb_local.x.x = q00 + q11 - q22 - q33;
+// Tnb_local.y.y = q00 - q11 + q22 - q33;
+// Tnb_local.z.z = q00 - q11 - q22 + q33;
+// Tnb_local.y.x = 2*(q12 - q03);
+// Tnb_local.z.x = 2*(q13 + q02);
+// Tnb_local.x.y = 2*(q12 + q03);
+// Tnb_local.z.y = 2*(q23 - q01);
+// Tnb_local.x.z = 2*(q13 - q02);
+// Tnb_local.y.z = 2*(q23 + q01);
+
+// // calculate transformation from NED to sensor axes
+// Tns = Tbs*Tnb_local;
+
+// // calculate range from ground plain to centre of sensor fov assuming flat earth
+// float range = ConstrainFloat(((ptd - pd)/Tns.z.z),0.5f,100.0f);
+
+// // calculate relative velocity in sensor frame
+// relVelSensor = Tns*velNED_local;
+
+// // divide velocity by range and include angular rate effects to get predicted angular LOS rates relative to X and Y axes
+// losPred[0] = relVelSensor.y/range;
+// losPred[1] = -relVelSensor.x/range;
+
+// //printf("relVelSensor.x=%5.1f, relVelSensor.y=%5.1f\n", relVelSensor.x, relVelSensor.y);
+// //printf("Xpred=%5.2f, Xmea=%5.2f, Ypred=%5.2f, Ymea=%5.2f, delAng.x=%4.4f, delAng.y=%4.4f\n", losPred[0], losData[0], losPred[1], losData[1], delAng.x, delAng.y);
+// //printf("omegaX=%5.2f, omegaY=%5.2f, velY=%5.1f velX=%5.1f\n, range=%5.1f\n", delAngRel.x/dt, delAngRel.y/dt, relVelSensor.y, relVelSensor.x, range);
+
+// // Calculate observation jacobians
+// SH_LOS[0] = a1*(2*q0*q1 + 2*q2*q3) + a2*(2*q0*q2 - 2*q1*q3) - sq(q0) + sq(q1) + sq(q2) - sq(q3);
+// SH_LOS[1] = vd*(a2*(sq(q0) - sq(q1) - sq(q2) + sq(q3)) + 2*q0*q2 - 2*q1*q3 - a3*(2*q0*q1 + 2*q2*q3)) - ve*(a3*(sq(q0) - sq(q1) + sq(q2) - sq(q3)) + 2*q0*q3 + 2*q1*q2 + a2*(2*q0*q1 - 2*q2*q3)) + vn*(a2*(2*q0*q2 + 2*q1*q3) + a3*(2*q0*q3 - 2*q1*q2) - sq(q0) - sq(q1) + sq(q2) + sq(q3));
+// SH_LOS[2] = ve*(a1*(2*q0*q1 - 2*q2*q3) + a3*(2*q0*q3 + 2*q1*q2) - sq(q0) + sq(q1) - sq(q2) + sq(q3)) - vd*(a1*(sq(q0) - sq(q1) - sq(q2) + sq(q3)) + 2*q0*q1 + 2*q2*q3 + a3*(2*q0*q2 - 2*q1*q3)) + vn*(a3*(sq(q0) + sq(q1) - sq(q2) - sq(q3)) + 2*q0*q3 - 2*q1*q2 - a1*(2*q0*q2 + 2*q1*q3));
+// SH_LOS[3] = 1/(pd - ptd);
+// SH_LOS[4] = 2*q1 - 2*a2*q3 + 2*a3*q2;
+// SH_LOS[5] = 2*a2*q2 - 2*q0 + 2*a3*q3;
+// SH_LOS[6] = 2*q2 + 2*a2*q0 - 2*a3*q1;
+// SH_LOS[7] = 1/sq(pd - ptd);
+// SH_LOS[8] = 2*q2 + 2*a1*q3 - 2*a3*q1;
+// SH_LOS[9] = 2*q3 - 2*a1*q2 + 2*a3*q0;
+// SH_LOS[10] = 2*a1*q1 - 2*q0 + 2*a3*q3;
+// SH_LOS[11] = 2*q3 + 2*a2*q1 + 2*a3*q0;
+// SH_LOS[12] = 2*q1 + 2*a1*q0 + 2*a3*q2;
+
+// for (uint8_t i = 0; i < n_states; i++) H_LOS[i] = 0;
+// H_LOS[0] = - SH_LOS[2]*SH_LOS[3]*(2*a1*q1 - 2*q0 + 2*a2*q2) - SH_LOS[0]*SH_LOS[3]*(ve*SH_LOS[10] - vd*SH_LOS[12] + vn*SH_LOS[9]);
+// H_LOS[1] = - SH_LOS[2]*SH_LOS[3]*(2*q1 + 2*a1*q0 - 2*a2*q3) - SH_LOS[0]*SH_LOS[3]*(vd*SH_LOS[10] + ve*SH_LOS[12] - vn*SH_LOS[8]);
+// H_LOS[2] = SH_LOS[0]*SH_LOS[3]*(vd*SH_LOS[9] + ve*SH_LOS[8] + vn*SH_LOS[12]) - SH_LOS[2]*SH_LOS[3]*(2*q2 + 2*a2*q0 + 2*a1*q3);
+// H_LOS[3] = SH_LOS[2]*SH_LOS[3]*(2*q3 - 2*a1*q2 + 2*a2*q1) + SH_LOS[0]*SH_LOS[3]*(vd*SH_LOS[8] - ve*SH_LOS[9] + vn*SH_LOS[10]);
+// H_LOS[4] = -SH_LOS[0]*SH_LOS[3]*(a3*(sq(q0) + sq(q1) - sq(q2) - sq(q3)) + 2*q0*q3 - 2*q1*q2 - a1*(2*q0*q2 + 2*q1*q3));
+// H_LOS[5] = -SH_LOS[0]*SH_LOS[3]*(a1*(2*q0*q1 - 2*q2*q3) + a3*(2*q0*q3 + 2*q1*q2) - sq(q0) + sq(q1) - sq(q2) + sq(q3));
+// H_LOS[6] = SH_LOS[0]*SH_LOS[3]*(a1*(sq(q0) - sq(q1) - sq(q2) + sq(q3)) + 2*q0*q1 + 2*q2*q3 + a3*(2*q0*q2 - 2*q1*q3));
+// H_LOS[9] = SH_LOS[0]*SH_LOS[2]*SH_LOS[7];
+// H_LOS[22] = -SH_LOS[0]*SH_LOS[2]*SH_LOS[7];
+
+// // Calculate Kalman gain
+// SKK_LOS[0] = a2*(sq(q0) - sq(q1) - sq(q2) + sq(q3)) + 2*q0*q2 - 2*q1*q3 - a3*(2*q0*q1 + 2*q2*q3);
+// SKK_LOS[1] = a3*(sq(q0) - sq(q1) + sq(q2) - sq(q3)) + 2*q0*q3 + 2*q1*q2 + a2*(2*q0*q1 - 2*q2*q3);
+// SKK_LOS[2] = a2*(2*q0*q2 + 2*q1*q3) + a3*(2*q0*q3 - 2*q1*q2) - sq(q0) - sq(q1) + sq(q2) + sq(q3);
+// SKK_LOS[3] = a1*(sq(q0) - sq(q1) - sq(q2) + sq(q3)) + 2*q0*q1 + 2*q2*q3 + a3*(2*q0*q2 - 2*q1*q3);
+// SKK_LOS[4] = a1*(2*q0*q1 - 2*q2*q3) + a3*(2*q0*q3 + 2*q1*q2) - sq(q0) + sq(q1) - sq(q2) + sq(q3);
+// SKK_LOS[5] = a3*(sq(q0) + sq(q1) - sq(q2) - sq(q3)) + 2*q0*q3 - 2*q1*q2 - a1*(2*q0*q2 + 2*q1*q3);
+// SKK_LOS[6] = SH_LOS[2]*SH_LOS[3]*(2*q3 - 2*a1*q2 + 2*a2*q1) + SH_LOS[0]*SH_LOS[3]*(vd*SH_LOS[8] - ve*SH_LOS[9] + vn*SH_LOS[10]);
+// SKK_LOS[7] = SH_LOS[2]*SH_LOS[3]*(2*q1 + 2*a1*q0 - 2*a2*q3) + SH_LOS[0]*SH_LOS[3]*(vd*SH_LOS[10] + ve*SH_LOS[12] - vn*SH_LOS[8]);
+// SKK_LOS[8] = SH_LOS[2]*SH_LOS[3]*(2*a1*q1 - 2*q0 + 2*a2*q2) + SH_LOS[0]*SH_LOS[3]*(ve*SH_LOS[10] - vd*SH_LOS[12] + vn*SH_LOS[9]);
+// SKK_LOS[9] = SH_LOS[2]*SH_LOS[3]*(2*q2 + 2*a2*q0 + 2*a1*q3) - SH_LOS[0]*SH_LOS[3]*(vd*SH_LOS[9] + ve*SH_LOS[8] + vn*SH_LOS[12]);
+// SKK_LOS[10] = SH_LOS[1]*SH_LOS[3]*(2*a1*q1 - 2*q0 + 2*a2*q2) + SH_LOS[0]*SH_LOS[3]*(vd*SH_LOS[6] - ve*SH_LOS[11] + vn*SH_LOS[5]);
+// SKK_LOS[11] = SH_LOS[1]*SH_LOS[3]*(2*q1 + 2*a1*q0 - 2*a2*q3) - SH_LOS[0]*SH_LOS[3]*(vd*SH_LOS[11] + ve*SH_LOS[6] + vn*SH_LOS[4]);
+// SKK_LOS[12] = SH_LOS[1]*SH_LOS[3]*(2*q2 + 2*a2*q0 + 2*a1*q3) - SH_LOS[0]*SH_LOS[3]*(vd*SH_LOS[5] + ve*SH_LOS[4] - vn*SH_LOS[6]);
+// SKK_LOS[13] = SH_LOS[1]*SH_LOS[3]*(2*q3 - 2*a1*q2 + 2*a2*q1) - SH_LOS[0]*SH_LOS[3]*(ve*SH_LOS[5] - vd*SH_LOS[4] + vn*SH_LOS[11]);
+// SKK_LOS[14] = SH_LOS[0];
+
+// SK_LOS[0] = 1/(R_LOS + SKK_LOS[8]*(P[0][0]*SKK_LOS[8] + P[1][0]*SKK_LOS[7] + P[2][0]*SKK_LOS[9] - P[3][0]*SKK_LOS[6] - P[9][0]*SH_LOS[2]*SH_LOS[7]*SKK_LOS[14] + P[22][0]*SH_LOS[2]*SH_LOS[7]*SKK_LOS[14] + P[4][0]*SH_LOS[3]*SKK_LOS[5]*SKK_LOS[14] + P[5][0]*SH_LOS[3]*SKK_LOS[4]*SKK_LOS[14] - P[6][0]*SH_LOS[3]*SKK_LOS[3]*SKK_LOS[14]) + SKK_LOS[7]*(P[0][1]*SKK_LOS[8] + P[1][1]*SKK_LOS[7] + P[2][1]*SKK_LOS[9] - P[3][1]*SKK_LOS[6] - P[9][1]*SH_LOS[2]*SH_LOS[7]*SKK_LOS[14] + P[22][1]*SH_LOS[2]*SH_LOS[7]*SKK_LOS[14] + P[4][1]*SH_LOS[3]*SKK_LOS[5]*SKK_LOS[14] + P[5][1]*SH_LOS[3]*SKK_LOS[4]*SKK_LOS[14] - P[6][1]*SH_LOS[3]*SKK_LOS[3]*SKK_LOS[14]) + SKK_LOS[9]*(P[0][2]*SKK_LOS[8] + P[1][2]*SKK_LOS[7] + P[2][2]*SKK_LOS[9] - P[3][2]*SKK_LOS[6] - P[9][2]*SH_LOS[2]*SH_LOS[7]*SKK_LOS[14] + P[22][2]*SH_LOS[2]*SH_LOS[7]*SKK_LOS[14] + P[4][2]*SH_LOS[3]*SKK_LOS[5]*SKK_LOS[14] + P[5][2]*SH_LOS[3]*SKK_LOS[4]*SKK_LOS[14] - P[6][2]*SH_LOS[3]*SKK_LOS[3]*SKK_LOS[14]) - SKK_LOS[6]*(P[0][3]*SKK_LOS[8] + P[1][3]*SKK_LOS[7] + P[2][3]*SKK_LOS[9] - P[3][3]*SKK_LOS[6] - P[9][3]*SH_LOS[2]*SH_LOS[7]*SKK_LOS[14] + P[22][3]*SH_LOS[2]*SH_LOS[7]*SKK_LOS[14] + P[4][3]*SH_LOS[3]*SKK_LOS[5]*SKK_LOS[14] + P[5][3]*SH_LOS[3]*SKK_LOS[4]*SKK_LOS[14] - P[6][3]*SH_LOS[3]*SKK_LOS[3]*SKK_LOS[14]) - SH_LOS[2]*SH_LOS[7]*SKK_LOS[14]*(P[0][9]*SKK_LOS[8] + P[1][9]*SKK_LOS[7] + P[2][9]*SKK_LOS[9] - P[3][9]*SKK_LOS[6] - P[9][9]*SH_LOS[2]*SH_LOS[7]*SKK_LOS[14] + P[22][9]*SH_LOS[2]*SH_LOS[7]*SKK_LOS[14] + P[4][9]*SH_LOS[3]*SKK_LOS[5]*SKK_LOS[14] + P[5][9]*SH_LOS[3]*SKK_LOS[4]*SKK_LOS[14] - P[6][9]*SH_LOS[3]*SKK_LOS[3]*SKK_LOS[14]) + SH_LOS[2]*SH_LOS[7]*SKK_LOS[14]*(P[0][22]*SKK_LOS[8] + P[1][22]*SKK_LOS[7] + P[2][22]*SKK_LOS[9] - P[3][22]*SKK_LOS[6] - P[9][22]*SH_LOS[2]*SH_LOS[7]*SKK_LOS[14] + P[22][22]*SH_LOS[2]*SH_LOS[7]*SKK_LOS[14] + P[4][22]*SH_LOS[3]*SKK_LOS[5]*SKK_LOS[14] + P[5][22]*SH_LOS[3]*SKK_LOS[4]*SKK_LOS[14] - P[6][22]*SH_LOS[3]*SKK_LOS[3]*SKK_LOS[14]) + SH_LOS[3]*SKK_LOS[5]*SKK_LOS[14]*(P[0][4]*SKK_LOS[8] + P[1][4]*SKK_LOS[7] + P[2][4]*SKK_LOS[9] - P[3][4]*SKK_LOS[6] - P[9][4]*SH_LOS[2]*SH_LOS[7]*SKK_LOS[14] + P[22][4]*SH_LOS[2]*SH_LOS[7]*SKK_LOS[14] + P[4][4]*SH_LOS[3]*SKK_LOS[5]*SKK_LOS[14] + P[5][4]*SH_LOS[3]*SKK_LOS[4]*SKK_LOS[14] - P[6][4]*SH_LOS[3]*SKK_LOS[3]*SKK_LOS[14]) + SH_LOS[3]*SKK_LOS[4]*SKK_LOS[14]*(P[0][5]*SKK_LOS[8] + P[1][5]*SKK_LOS[7] + P[2][5]*SKK_LOS[9] - P[3][5]*SKK_LOS[6] - P[9][5]*SH_LOS[2]*SH_LOS[7]*SKK_LOS[14] + P[22][5]*SH_LOS[2]*SH_LOS[7]*SKK_LOS[14] + P[4][5]*SH_LOS[3]*SKK_LOS[5]*SKK_LOS[14] + P[5][5]*SH_LOS[3]*SKK_LOS[4]*SKK_LOS[14] - P[6][5]*SH_LOS[3]*SKK_LOS[3]*SKK_LOS[14]) - SH_LOS[3]*SKK_LOS[3]*SKK_LOS[14]*(P[0][6]*SKK_LOS[8] + P[1][6]*SKK_LOS[7] + P[2][6]*SKK_LOS[9] - P[3][6]*SKK_LOS[6] - P[9][6]*SH_LOS[2]*SH_LOS[7]*SKK_LOS[14] + P[22][6]*SH_LOS[2]*SH_LOS[7]*SKK_LOS[14] + P[4][6]*SH_LOS[3]*SKK_LOS[5]*SKK_LOS[14] + P[5][6]*SH_LOS[3]*SKK_LOS[4]*SKK_LOS[14] - P[6][6]*SH_LOS[3]*SKK_LOS[3]*SKK_LOS[14]));
+// Kfusion[0] = -SK_LOS[0]*(P[0][0]*SKK_LOS[8] + P[0][1]*SKK_LOS[7] - P[0][3]*SKK_LOS[6] + P[0][2]*SKK_LOS[9] - P[0][9]*SH_LOS[2]*SH_LOS[7]*SKK_LOS[14] + P[0][22]*SH_LOS[2]*SH_LOS[7]*SKK_LOS[14] + P[0][4]*SH_LOS[3]*SKK_LOS[5]*SKK_LOS[14] + P[0][5]*SH_LOS[3]*SKK_LOS[4]*SKK_LOS[14] - P[0][6]*SH_LOS[3]*SKK_LOS[3]*SKK_LOS[14]);
+// Kfusion[1] = -SK_LOS[0]*(P[1][0]*SKK_LOS[8] + P[1][1]*SKK_LOS[7] - P[1][3]*SKK_LOS[6] + P[1][2]*SKK_LOS[9] - P[1][9]*SH_LOS[2]*SH_LOS[7]*SKK_LOS[14] + P[1][22]*SH_LOS[2]*SH_LOS[7]*SKK_LOS[14] + P[1][4]*SH_LOS[3]*SKK_LOS[5]*SKK_LOS[14] + P[1][5]*SH_LOS[3]*SKK_LOS[4]*SKK_LOS[14] - P[1][6]*SH_LOS[3]*SKK_LOS[3]*SKK_LOS[14]);
+// Kfusion[2] = -SK_LOS[0]*(P[2][0]*SKK_LOS[8] + P[2][1]*SKK_LOS[7] - P[2][3]*SKK_LOS[6] + P[2][2]*SKK_LOS[9] - P[2][9]*SH_LOS[2]*SH_LOS[7]*SKK_LOS[14] + P[2][22]*SH_LOS[2]*SH_LOS[7]*SKK_LOS[14] + P[2][4]*SH_LOS[3]*SKK_LOS[5]*SKK_LOS[14] + P[2][5]*SH_LOS[3]*SKK_LOS[4]*SKK_LOS[14] - P[2][6]*SH_LOS[3]*SKK_LOS[3]*SKK_LOS[14]);
+// Kfusion[3] = -SK_LOS[0]*(P[3][0]*SKK_LOS[8] + P[3][1]*SKK_LOS[7] - P[3][3]*SKK_LOS[6] + P[3][2]*SKK_LOS[9] - P[3][9]*SH_LOS[2]*SH_LOS[7]*SKK_LOS[14] + P[3][22]*SH_LOS[2]*SH_LOS[7]*SKK_LOS[14] + P[3][4]*SH_LOS[3]*SKK_LOS[5]*SKK_LOS[14] + P[3][5]*SH_LOS[3]*SKK_LOS[4]*SKK_LOS[14] - P[3][6]*SH_LOS[3]*SKK_LOS[3]*SKK_LOS[14]);
+// Kfusion[4] = -SK_LOS[0]*(P[4][0]*SKK_LOS[8] + P[4][1]*SKK_LOS[7] - P[4][3]*SKK_LOS[6] + P[4][2]*SKK_LOS[9] - P[4][9]*SH_LOS[2]*SH_LOS[7]*SKK_LOS[14] + P[4][22]*SH_LOS[2]*SH_LOS[7]*SKK_LOS[14] + P[4][4]*SH_LOS[3]*SKK_LOS[5]*SKK_LOS[14] + P[4][5]*SH_LOS[3]*SKK_LOS[4]*SKK_LOS[14] - P[4][6]*SH_LOS[3]*SKK_LOS[3]*SKK_LOS[14]);
+// Kfusion[5] = -SK_LOS[0]*(P[5][0]*SKK_LOS[8] + P[5][1]*SKK_LOS[7] - P[5][3]*SKK_LOS[6] + P[5][2]*SKK_LOS[9] - P[5][9]*SH_LOS[2]*SH_LOS[7]*SKK_LOS[14] + P[5][22]*SH_LOS[2]*SH_LOS[7]*SKK_LOS[14] + P[5][4]*SH_LOS[3]*SKK_LOS[5]*SKK_LOS[14] + P[5][5]*SH_LOS[3]*SKK_LOS[4]*SKK_LOS[14] - P[5][6]*SH_LOS[3]*SKK_LOS[3]*SKK_LOS[14]);
+// Kfusion[6] = -SK_LOS[0]*(P[6][0]*SKK_LOS[8] + P[6][1]*SKK_LOS[7] - P[6][3]*SKK_LOS[6] + P[6][2]*SKK_LOS[9] - P[6][9]*SH_LOS[2]*SH_LOS[7]*SKK_LOS[14] + P[6][22]*SH_LOS[2]*SH_LOS[7]*SKK_LOS[14] + P[6][4]*SH_LOS[3]*SKK_LOS[5]*SKK_LOS[14] + P[6][5]*SH_LOS[3]*SKK_LOS[4]*SKK_LOS[14] - P[6][6]*SH_LOS[3]*SKK_LOS[3]*SKK_LOS[14]);
+// Kfusion[7] = -SK_LOS[0]*(P[7][0]*SKK_LOS[8] + P[7][1]*SKK_LOS[7] - P[7][3]*SKK_LOS[6] + P[7][2]*SKK_LOS[9] - P[7][9]*SH_LOS[2]*SH_LOS[7]*SKK_LOS[14] + P[7][22]*SH_LOS[2]*SH_LOS[7]*SKK_LOS[14] + P[7][4]*SH_LOS[3]*SKK_LOS[5]*SKK_LOS[14] + P[7][5]*SH_LOS[3]*SKK_LOS[4]*SKK_LOS[14] - P[7][6]*SH_LOS[3]*SKK_LOS[3]*SKK_LOS[14]);
+// Kfusion[8] = -SK_LOS[0]*(P[8][0]*SKK_LOS[8] + P[8][1]*SKK_LOS[7] - P[8][3]*SKK_LOS[6] + P[8][2]*SKK_LOS[9] - P[8][9]*SH_LOS[2]*SH_LOS[7]*SKK_LOS[14] + P[8][22]*SH_LOS[2]*SH_LOS[7]*SKK_LOS[14] + P[8][4]*SH_LOS[3]*SKK_LOS[5]*SKK_LOS[14] + P[8][5]*SH_LOS[3]*SKK_LOS[4]*SKK_LOS[14] - P[8][6]*SH_LOS[3]*SKK_LOS[3]*SKK_LOS[14]);
+// Kfusion[9] = -SK_LOS[0]*(P[9][0]*SKK_LOS[8] + P[9][1]*SKK_LOS[7] - P[9][3]*SKK_LOS[6] + P[9][2]*SKK_LOS[9] - P[9][9]*SH_LOS[2]*SH_LOS[7]*SKK_LOS[14] + P[9][22]*SH_LOS[2]*SH_LOS[7]*SKK_LOS[14] + P[9][4]*SH_LOS[3]*SKK_LOS[5]*SKK_LOS[14] + P[9][5]*SH_LOS[3]*SKK_LOS[4]*SKK_LOS[14] - P[9][6]*SH_LOS[3]*SKK_LOS[3]*SKK_LOS[14]);
+// Kfusion[10] = -SK_LOS[0]*(P[10][0]*SKK_LOS[8] + P[10][1]*SKK_LOS[7] - P[10][3]*SKK_LOS[6] + P[10][2]*SKK_LOS[9] - P[10][9]*SH_LOS[2]*SH_LOS[7]*SKK_LOS[14] + P[10][22]*SH_LOS[2]*SH_LOS[7]*SKK_LOS[14] + P[10][4]*SH_LOS[3]*SKK_LOS[5]*SKK_LOS[14] + P[10][5]*SH_LOS[3]*SKK_LOS[4]*SKK_LOS[14] - P[10][6]*SH_LOS[3]*SKK_LOS[3]*SKK_LOS[14]);
+// Kfusion[11] = -SK_LOS[0]*(P[11][0]*SKK_LOS[8] + P[11][1]*SKK_LOS[7] - P[11][3]*SKK_LOS[6] + P[11][2]*SKK_LOS[9] - P[11][9]*SH_LOS[2]*SH_LOS[7]*SKK_LOS[14] + P[11][22]*SH_LOS[2]*SH_LOS[7]*SKK_LOS[14] + P[11][4]*SH_LOS[3]*SKK_LOS[5]*SKK_LOS[14] + P[11][5]*SH_LOS[3]*SKK_LOS[4]*SKK_LOS[14] - P[11][6]*SH_LOS[3]*SKK_LOS[3]*SKK_LOS[14]);
+// Kfusion[12] = -SK_LOS[0]*(P[12][0]*SKK_LOS[8] + P[12][1]*SKK_LOS[7] - P[12][3]*SKK_LOS[6] + P[12][2]*SKK_LOS[9] - P[12][9]*SH_LOS[2]*SH_LOS[7]*SKK_LOS[14] + P[12][22]*SH_LOS[2]*SH_LOS[7]*SKK_LOS[14] + P[12][4]*SH_LOS[3]*SKK_LOS[5]*SKK_LOS[14] + P[12][5]*SH_LOS[3]*SKK_LOS[4]*SKK_LOS[14] - P[12][6]*SH_LOS[3]*SKK_LOS[3]*SKK_LOS[14]);
+// Kfusion[13] = 0.0f;//-SK_LOS[0]*(P[13][0]*SKK_LOS[8] + P[13][1]*SKK_LOS[7] - P[13][3]*SKK_LOS[6] + P[13][2]*SKK_LOS[9] - P[13][9]*SH_LOS[2]*SH_LOS[7]*SKK_LOS[14] + P[13][22]*SH_LOS[2]*SH_LOS[7]*SKK_LOS[14] + P[13][4]*SH_LOS[3]*SKK_LOS[5]*SKK_LOS[14] + P[13][5]*SH_LOS[3]*SKK_LOS[4]*SKK_LOS[14] - P[13][6]*SH_LOS[3]*SKK_LOS[3]*SKK_LOS[14]);
+// Kfusion[14] = -SK_LOS[0]*(P[14][0]*SKK_LOS[8] + P[14][1]*SKK_LOS[7] - P[14][3]*SKK_LOS[6] + P[14][2]*SKK_LOS[9] - P[14][9]*SH_LOS[2]*SH_LOS[7]*SKK_LOS[14] + P[14][22]*SH_LOS[2]*SH_LOS[7]*SKK_LOS[14] + P[14][4]*SH_LOS[3]*SKK_LOS[5]*SKK_LOS[14] + P[14][5]*SH_LOS[3]*SKK_LOS[4]*SKK_LOS[14] - P[14][6]*SH_LOS[3]*SKK_LOS[3]*SKK_LOS[14]);
+// Kfusion[15] = -SK_LOS[0]*(P[15][0]*SKK_LOS[8] + P[15][1]*SKK_LOS[7] - P[15][3]*SKK_LOS[6] + P[15][2]*SKK_LOS[9] - P[15][9]*SH_LOS[2]*SH_LOS[7]*SKK_LOS[14] + P[15][22]*SH_LOS[2]*SH_LOS[7]*SKK_LOS[14] + P[15][4]*SH_LOS[3]*SKK_LOS[5]*SKK_LOS[14] + P[15][5]*SH_LOS[3]*SKK_LOS[4]*SKK_LOS[14] - P[15][6]*SH_LOS[3]*SKK_LOS[3]*SKK_LOS[14]);
+// Kfusion[16] = -SK_LOS[0]*(P[16][0]*SKK_LOS[8] + P[16][1]*SKK_LOS[7] - P[16][3]*SKK_LOS[6] + P[16][2]*SKK_LOS[9] - P[16][9]*SH_LOS[2]*SH_LOS[7]*SKK_LOS[14] + P[16][22]*SH_LOS[2]*SH_LOS[7]*SKK_LOS[14] + P[16][4]*SH_LOS[3]*SKK_LOS[5]*SKK_LOS[14] + P[16][5]*SH_LOS[3]*SKK_LOS[4]*SKK_LOS[14] - P[16][6]*SH_LOS[3]*SKK_LOS[3]*SKK_LOS[14]);
+// Kfusion[17] = -SK_LOS[0]*(P[17][0]*SKK_LOS[8] + P[17][1]*SKK_LOS[7] - P[17][3]*SKK_LOS[6] + P[17][2]*SKK_LOS[9] - P[17][9]*SH_LOS[2]*SH_LOS[7]*SKK_LOS[14] + P[17][22]*SH_LOS[2]*SH_LOS[7]*SKK_LOS[14] + P[17][4]*SH_LOS[3]*SKK_LOS[5]*SKK_LOS[14] + P[17][5]*SH_LOS[3]*SKK_LOS[4]*SKK_LOS[14] - P[17][6]*SH_LOS[3]*SKK_LOS[3]*SKK_LOS[14]);
+// Kfusion[18] = -SK_LOS[0]*(P[18][0]*SKK_LOS[8] + P[18][1]*SKK_LOS[7] - P[18][3]*SKK_LOS[6] + P[18][2]*SKK_LOS[9] - P[18][9]*SH_LOS[2]*SH_LOS[7]*SKK_LOS[14] + P[18][22]*SH_LOS[2]*SH_LOS[7]*SKK_LOS[14] + P[18][4]*SH_LOS[3]*SKK_LOS[5]*SKK_LOS[14] + P[18][5]*SH_LOS[3]*SKK_LOS[4]*SKK_LOS[14] - P[18][6]*SH_LOS[3]*SKK_LOS[3]*SKK_LOS[14]);
+// Kfusion[19] = -SK_LOS[0]*(P[19][0]*SKK_LOS[8] + P[19][1]*SKK_LOS[7] - P[19][3]*SKK_LOS[6] + P[19][2]*SKK_LOS[9] - P[19][9]*SH_LOS[2]*SH_LOS[7]*SKK_LOS[14] + P[19][22]*SH_LOS[2]*SH_LOS[7]*SKK_LOS[14] + P[19][4]*SH_LOS[3]*SKK_LOS[5]*SKK_LOS[14] + P[19][5]*SH_LOS[3]*SKK_LOS[4]*SKK_LOS[14] - P[19][6]*SH_LOS[3]*SKK_LOS[3]*SKK_LOS[14]);
+// Kfusion[20] = -SK_LOS[0]*(P[20][0]*SKK_LOS[8] + P[20][1]*SKK_LOS[7] - P[20][3]*SKK_LOS[6] + P[20][2]*SKK_LOS[9] - P[20][9]*SH_LOS[2]*SH_LOS[7]*SKK_LOS[14] + P[20][22]*SH_LOS[2]*SH_LOS[7]*SKK_LOS[14] + P[20][4]*SH_LOS[3]*SKK_LOS[5]*SKK_LOS[14] + P[20][5]*SH_LOS[3]*SKK_LOS[4]*SKK_LOS[14] - P[20][6]*SH_LOS[3]*SKK_LOS[3]*SKK_LOS[14]);
+// Kfusion[21] = -SK_LOS[0]*(P[21][0]*SKK_LOS[8] + P[21][1]*SKK_LOS[7] - P[21][3]*SKK_LOS[6] + P[21][2]*SKK_LOS[9] - P[21][9]*SH_LOS[2]*SH_LOS[7]*SKK_LOS[14] + P[21][22]*SH_LOS[2]*SH_LOS[7]*SKK_LOS[14] + P[21][4]*SH_LOS[3]*SKK_LOS[5]*SKK_LOS[14] + P[21][5]*SH_LOS[3]*SKK_LOS[4]*SKK_LOS[14] - P[21][6]*SH_LOS[3]*SKK_LOS[3]*SKK_LOS[14]);
+// Kfusion[22] = -SK_LOS[0]*(P[22][0]*SKK_LOS[8] + P[22][1]*SKK_LOS[7] - P[22][3]*SKK_LOS[6] + P[22][2]*SKK_LOS[9] - P[22][9]*SH_LOS[2]*SH_LOS[7]*SKK_LOS[14] + P[22][22]*SH_LOS[2]*SH_LOS[7]*SKK_LOS[14] + P[22][4]*SH_LOS[3]*SKK_LOS[5]*SKK_LOS[14] + P[22][5]*SH_LOS[3]*SKK_LOS[4]*SKK_LOS[14] - P[22][6]*SH_LOS[3]*SKK_LOS[3]*SKK_LOS[14]);
+// varInnovOptFlow[0] = 1.0f/SK_LOS[0];
+// innovOptFlow[0] = losPred[0] - losData[0];
+
+// // set the observation index to 1 to fuse the y component next time round and reset the commence fusion flag
+// obsIndex = 1;
+// fuseOptFlowData = false;
+// }
+// else if (obsIndex == 1) // we are now fusing the Y measurement
+// {
+// // Calculate observation jacobians
+// for (uint8_t i = 0; i < n_states; i++) H_LOS[i] = 0;
+// H_LOS[0] = SH_LOS[1]*SH_LOS[3]*(2*a1*q1 - 2*q0 + 2*a2*q2) + SH_LOS[0]*SH_LOS[3]*(vd*SH_LOS[6] - ve*SH_LOS[11] + vn*SH_LOS[5]);
+// H_LOS[1] = SH_LOS[1]*SH_LOS[3]*(2*q1 + 2*a1*q0 - 2*a2*q3) - SH_LOS[0]*SH_LOS[3]*(vd*SH_LOS[11] + ve*SH_LOS[6] + vn*SH_LOS[4]);
+// H_LOS[2] = SH_LOS[1]*SH_LOS[3]*(2*q2 + 2*a2*q0 + 2*a1*q3) - SH_LOS[0]*SH_LOS[3]*(vd*SH_LOS[5] + ve*SH_LOS[4] - vn*SH_LOS[6]);
+// H_LOS[3] = SH_LOS[0]*SH_LOS[3]*(ve*SH_LOS[5] - vd*SH_LOS[4] + vn*SH_LOS[11]) - SH_LOS[1]*SH_LOS[3]*(2*q3 - 2*a1*q2 + 2*a2*q1);
+// H_LOS[4] = SH_LOS[0]*SH_LOS[3]*(a2*(2*q0*q2 + 2*q1*q3) + a3*(2*q0*q3 - 2*q1*q2) - sq(q0) - sq(q1) + sq(q2) + sq(q3));
+// H_LOS[5] = -SH_LOS[0]*SH_LOS[3]*(a3*(sq(q0) - sq(q1) + sq(q2) - sq(q3)) + 2*q0*q3 + 2*q1*q2 + a2*(2*q0*q1 - 2*q2*q3));
+// H_LOS[6] = SH_LOS[0]*SH_LOS[3]*(a2*(sq(q0) - sq(q1) - sq(q2) + sq(q3)) + 2*q0*q2 - 2*q1*q3 - a3*(2*q0*q1 + 2*q2*q3));
+// H_LOS[9] = -SH_LOS[0]*SH_LOS[1]*SH_LOS[7];
+// H_LOS[22] = SH_LOS[0]*SH_LOS[1]*SH_LOS[7];
+
+// // Calculate Kalman gains
+// SK_LOS[1] = 1/(R_LOS + SKK_LOS[12]*(P[0][2]*SKK_LOS[10] + P[1][2]*SKK_LOS[11] + P[2][2]*SKK_LOS[12] - P[3][2]*SKK_LOS[13] - P[9][2]*SH_LOS[1]*SH_LOS[7]*SKK_LOS[14] + P[22][2]*SH_LOS[1]*SH_LOS[7]*SKK_LOS[14] + P[4][2]*SH_LOS[3]*SKK_LOS[2]*SKK_LOS[14] - P[5][2]*SH_LOS[3]*SKK_LOS[1]*SKK_LOS[14] + P[6][2]*SH_LOS[3]*SKK_LOS[0]*SKK_LOS[14]) - SKK_LOS[13]*(P[0][3]*SKK_LOS[10] + P[1][3]*SKK_LOS[11] + P[2][3]*SKK_LOS[12] - P[3][3]*SKK_LOS[13] - P[9][3]*SH_LOS[1]*SH_LOS[7]*SKK_LOS[14] + P[22][3]*SH_LOS[1]*SH_LOS[7]*SKK_LOS[14] + P[4][3]*SH_LOS[3]*SKK_LOS[2]*SKK_LOS[14] - P[5][3]*SH_LOS[3]*SKK_LOS[1]*SKK_LOS[14] + P[6][3]*SH_LOS[3]*SKK_LOS[0]*SKK_LOS[14]) + SKK_LOS[10]*(P[0][0]*SKK_LOS[10] + P[1][0]*SKK_LOS[11] + P[2][0]*(SH_LOS[1]*SH_LOS[3]*(2*q2 + 2*a2*q0 + 2*a1*q3) - SH_LOS[3]*SKK_LOS[14]*(vd*SH_LOS[5] + ve*SH_LOS[4] - vn*SH_LOS[6])) - P[3][0]*(SH_LOS[1]*SH_LOS[3]*(2*q3 - 2*a1*q2 + 2*a2*q1) - SH_LOS[3]*SKK_LOS[14]*(ve*SH_LOS[5] - vd*SH_LOS[4] + vn*SH_LOS[11])) - P[9][0]*SH_LOS[1]*SH_LOS[7]*SKK_LOS[14] + P[22][0]*SH_LOS[1]*SH_LOS[7]*SKK_LOS[14] + P[4][0]*SH_LOS[3]*SKK_LOS[2]*SKK_LOS[14] - P[5][0]*SH_LOS[3]*SKK_LOS[1]*SKK_LOS[14] + P[6][0]*SH_LOS[3]*SKK_LOS[0]*SKK_LOS[14]) + SKK_LOS[11]*(P[0][1]*SKK_LOS[10] + P[1][1]*SKK_LOS[11] + P[2][1]*(SH_LOS[1]*SH_LOS[3]*(2*q2 + 2*a2*q0 + 2*a1*q3) - SH_LOS[3]*SKK_LOS[14]*(vd*SH_LOS[5] + ve*SH_LOS[4] - vn*SH_LOS[6])) - P[3][1]*(SH_LOS[1]*SH_LOS[3]*(2*q3 - 2*a1*q2 + 2*a2*q1) - SH_LOS[3]*SKK_LOS[14]*(ve*SH_LOS[5] - vd*SH_LOS[4] + vn*SH_LOS[11])) - P[9][1]*SH_LOS[1]*SH_LOS[7]*SKK_LOS[14] + P[22][1]*SH_LOS[1]*SH_LOS[7]*SKK_LOS[14] + P[4][1]*SH_LOS[3]*SKK_LOS[2]*SKK_LOS[14] - P[5][1]*SH_LOS[3]*SKK_LOS[1]*SKK_LOS[14] + P[6][1]*SH_LOS[3]*SKK_LOS[0]*SKK_LOS[14]) - SH_LOS[1]*SH_LOS[7]*SKK_LOS[14]*(P[0][9]*SKK_LOS[10] + P[1][9]*SKK_LOS[11] + P[2][9]*SKK_LOS[12] - P[3][9]*SKK_LOS[13] - P[9][9]*SH_LOS[1]*SH_LOS[7]*SKK_LOS[14] + P[22][9]*SH_LOS[1]*SH_LOS[7]*SKK_LOS[14] + P[4][9]*SH_LOS[3]*SKK_LOS[2]*SKK_LOS[14] - P[5][9]*SH_LOS[3]*SKK_LOS[1]*SKK_LOS[14] + P[6][9]*SH_LOS[3]*SKK_LOS[0]*SKK_LOS[14]) + SH_LOS[1]*SH_LOS[7]*SKK_LOS[14]*(P[0][22]*SKK_LOS[10] + P[1][22]*SKK_LOS[11] + P[2][22]*SKK_LOS[12] - P[3][22]*SKK_LOS[13] - P[9][22]*SH_LOS[1]*SH_LOS[7]*SKK_LOS[14] + P[22][22]*SH_LOS[1]*SH_LOS[7]*SKK_LOS[14] + P[4][22]*SH_LOS[3]*SKK_LOS[2]*SKK_LOS[14] - P[5][22]*SH_LOS[3]*SKK_LOS[1]*SKK_LOS[14] + P[6][22]*SH_LOS[3]*SKK_LOS[0]*SKK_LOS[14]) + SH_LOS[3]*SKK_LOS[2]*SKK_LOS[14]*(P[0][4]*SKK_LOS[10] + P[1][4]*SKK_LOS[11] + P[2][4]*SKK_LOS[12] - P[3][4]*SKK_LOS[13] - P[9][4]*SH_LOS[1]*SH_LOS[7]*SKK_LOS[14] + P[22][4]*SH_LOS[1]*SH_LOS[7]*SKK_LOS[14] + P[4][4]*SH_LOS[3]*SKK_LOS[2]*SKK_LOS[14] - P[5][4]*SH_LOS[3]*SKK_LOS[1]*SKK_LOS[14] + P[6][4]*SH_LOS[3]*SKK_LOS[0]*SKK_LOS[14]) - SH_LOS[3]*SKK_LOS[1]*SKK_LOS[14]*(P[0][5]*SKK_LOS[10] + P[1][5]*SKK_LOS[11] + P[2][5]*SKK_LOS[12] - P[3][5]*SKK_LOS[13] - P[9][5]*SH_LOS[1]*SH_LOS[7]*SKK_LOS[14] + P[22][5]*SH_LOS[1]*SH_LOS[7]*SKK_LOS[14] + P[4][5]*SH_LOS[3]*SKK_LOS[2]*SKK_LOS[14] - P[5][5]*SH_LOS[3]*SKK_LOS[1]*SKK_LOS[14] + P[6][5]*SH_LOS[3]*SKK_LOS[0]*SKK_LOS[14]) + SH_LOS[3]*SKK_LOS[0]*SKK_LOS[14]*(P[0][6]*SKK_LOS[10] + P[1][6]*SKK_LOS[11] + P[2][6]*SKK_LOS[12] - P[3][6]*SKK_LOS[13] - P[9][6]*SH_LOS[1]*SH_LOS[7]*SKK_LOS[14] + P[22][6]*SH_LOS[1]*SH_LOS[7]*SKK_LOS[14] + P[4][6]*SH_LOS[3]*SKK_LOS[2]*SKK_LOS[14] - P[5][6]*SH_LOS[3]*SKK_LOS[1]*SKK_LOS[14] + P[6][6]*SH_LOS[3]*SKK_LOS[0]*SKK_LOS[14]));
+// Kfusion[0] = SK_LOS[1]*(P[0][0]*SKK_LOS[10] - P[0][3]*SKK_LOS[13] + P[0][1]*SKK_LOS[11] + P[0][2]*SKK_LOS[12] - P[0][9]*SH_LOS[1]*SH_LOS[7]*SKK_LOS[14] + P[0][22]*SH_LOS[1]*SH_LOS[7]*SKK_LOS[14] + P[0][6]*SH_LOS[3]*SKK_LOS[0]*SKK_LOS[14] + P[0][4]*SH_LOS[3]*SKK_LOS[2]*SKK_LOS[14] - P[0][5]*SH_LOS[3]*SKK_LOS[1]*SKK_LOS[14]);
+// Kfusion[1] = SK_LOS[1]*(P[1][0]*SKK_LOS[10] - P[1][3]*SKK_LOS[13] + P[1][1]*SKK_LOS[11] + P[1][2]*SKK_LOS[12] - P[1][9]*SH_LOS[1]*SH_LOS[7]*SKK_LOS[14] + P[1][22]*SH_LOS[1]*SH_LOS[7]*SKK_LOS[14] + P[1][6]*SH_LOS[3]*SKK_LOS[0]*SKK_LOS[14] + P[1][4]*SH_LOS[3]*SKK_LOS[2]*SKK_LOS[14] - P[1][5]*SH_LOS[3]*SKK_LOS[1]*SKK_LOS[14]);
+// Kfusion[2] = SK_LOS[1]*(P[2][0]*SKK_LOS[10] - P[2][3]*SKK_LOS[13] + P[2][1]*SKK_LOS[11] + P[2][2]*SKK_LOS[12] - P[2][9]*SH_LOS[1]*SH_LOS[7]*SKK_LOS[14] + P[2][22]*SH_LOS[1]*SH_LOS[7]*SKK_LOS[14] + P[2][6]*SH_LOS[3]*SKK_LOS[0]*SKK_LOS[14] + P[2][4]*SH_LOS[3]*SKK_LOS[2]*SKK_LOS[14] - P[2][5]*SH_LOS[3]*SKK_LOS[1]*SKK_LOS[14]);
+// Kfusion[3] = SK_LOS[1]*(P[3][0]*SKK_LOS[10] - P[3][3]*SKK_LOS[13] + P[3][1]*SKK_LOS[11] + P[3][2]*SKK_LOS[12] - P[3][9]*SH_LOS[1]*SH_LOS[7]*SKK_LOS[14] + P[3][22]*SH_LOS[1]*SH_LOS[7]*SKK_LOS[14] + P[3][6]*SH_LOS[3]*SKK_LOS[0]*SKK_LOS[14] + P[3][4]*SH_LOS[3]*SKK_LOS[2]*SKK_LOS[14] - P[3][5]*SH_LOS[3]*SKK_LOS[1]*SKK_LOS[14]);
+// Kfusion[4] = SK_LOS[1]*(P[4][0]*SKK_LOS[10] - P[4][3]*SKK_LOS[13] + P[4][1]*SKK_LOS[11] + P[4][2]*SKK_LOS[12] - P[4][9]*SH_LOS[1]*SH_LOS[7]*SKK_LOS[14] + P[4][22]*SH_LOS[1]*SH_LOS[7]*SKK_LOS[14] + P[4][6]*SH_LOS[3]*SKK_LOS[0]*SKK_LOS[14] + P[4][4]*SH_LOS[3]*SKK_LOS[2]*SKK_LOS[14] - P[4][5]*SH_LOS[3]*SKK_LOS[1]*SKK_LOS[14]);
+// Kfusion[5] = SK_LOS[1]*(P[5][0]*SKK_LOS[10] - P[5][3]*SKK_LOS[13] + P[5][1]*SKK_LOS[11] + P[5][2]*SKK_LOS[12] - P[5][9]*SH_LOS[1]*SH_LOS[7]*SKK_LOS[14] + P[5][22]*SH_LOS[1]*SH_LOS[7]*SKK_LOS[14] + P[5][6]*SH_LOS[3]*SKK_LOS[0]*SKK_LOS[14] + P[5][4]*SH_LOS[3]*SKK_LOS[2]*SKK_LOS[14] - P[5][5]*SH_LOS[3]*SKK_LOS[1]*SKK_LOS[14]);
+// Kfusion[6] = SK_LOS[1]*(P[6][0]*SKK_LOS[10] - P[6][3]*SKK_LOS[13] + P[6][1]*SKK_LOS[11] + P[6][2]*SKK_LOS[12] - P[6][9]*SH_LOS[1]*SH_LOS[7]*SKK_LOS[14] + P[6][22]*SH_LOS[1]*SH_LOS[7]*SKK_LOS[14] + P[6][6]*SH_LOS[3]*SKK_LOS[0]*SKK_LOS[14] + P[6][4]*SH_LOS[3]*SKK_LOS[2]*SKK_LOS[14] - P[6][5]*SH_LOS[3]*SKK_LOS[1]*SKK_LOS[14]);
+// Kfusion[7] = SK_LOS[1]*(P[7][0]*SKK_LOS[10] - P[7][3]*SKK_LOS[13] + P[7][1]*SKK_LOS[11] + P[7][2]*SKK_LOS[12] - P[7][9]*SH_LOS[1]*SH_LOS[7]*SKK_LOS[14] + P[7][22]*SH_LOS[1]*SH_LOS[7]*SKK_LOS[14] + P[7][6]*SH_LOS[3]*SKK_LOS[0]*SKK_LOS[14] + P[7][4]*SH_LOS[3]*SKK_LOS[2]*SKK_LOS[14] - P[7][5]*SH_LOS[3]*SKK_LOS[1]*SKK_LOS[14]);
+// Kfusion[8] = SK_LOS[1]*(P[8][0]*SKK_LOS[10] - P[8][3]*SKK_LOS[13] + P[8][1]*SKK_LOS[11] + P[8][2]*SKK_LOS[12] - P[8][9]*SH_LOS[1]*SH_LOS[7]*SKK_LOS[14] + P[8][22]*SH_LOS[1]*SH_LOS[7]*SKK_LOS[14] + P[8][6]*SH_LOS[3]*SKK_LOS[0]*SKK_LOS[14] + P[8][4]*SH_LOS[3]*SKK_LOS[2]*SKK_LOS[14] - P[8][5]*SH_LOS[3]*SKK_LOS[1]*SKK_LOS[14]);
+// Kfusion[9] = SK_LOS[1]*(P[9][0]*SKK_LOS[10] - P[9][3]*SKK_LOS[13] + P[9][1]*SKK_LOS[11] + P[9][2]*SKK_LOS[12] - P[9][9]*SH_LOS[1]*SH_LOS[7]*SKK_LOS[14] + P[9][22]*SH_LOS[1]*SH_LOS[7]*SKK_LOS[14] + P[9][6]*SH_LOS[3]*SKK_LOS[0]*SKK_LOS[14] + P[9][4]*SH_LOS[3]*SKK_LOS[2]*SKK_LOS[14] - P[9][5]*SH_LOS[3]*SKK_LOS[1]*SKK_LOS[14]);
+// Kfusion[10] = SK_LOS[1]*(P[10][0]*SKK_LOS[10] - P[10][3]*SKK_LOS[13] + P[10][1]*SKK_LOS[11] + P[10][2]*SKK_LOS[12] - P[10][9]*SH_LOS[1]*SH_LOS[7]*SKK_LOS[14] + P[10][22]*SH_LOS[1]*SH_LOS[7]*SKK_LOS[14] + P[10][6]*SH_LOS[3]*SKK_LOS[0]*SKK_LOS[14] + P[10][4]*SH_LOS[3]*SKK_LOS[2]*SKK_LOS[14] - P[10][5]*SH_LOS[3]*SKK_LOS[1]*SKK_LOS[14]);
+// Kfusion[11] = SK_LOS[1]*(P[11][0]*SKK_LOS[10] - P[11][3]*SKK_LOS[13] + P[11][1]*SKK_LOS[11] + P[11][2]*SKK_LOS[12] - P[11][9]*SH_LOS[1]*SH_LOS[7]*SKK_LOS[14] + P[11][22]*SH_LOS[1]*SH_LOS[7]*SKK_LOS[14] + P[11][6]*SH_LOS[3]*SKK_LOS[0]*SKK_LOS[14] + P[11][4]*SH_LOS[3]*SKK_LOS[2]*SKK_LOS[14] - P[11][5]*SH_LOS[3]*SKK_LOS[1]*SKK_LOS[14]);
+// Kfusion[12] = SK_LOS[1]*(P[12][0]*SKK_LOS[10] - P[12][3]*SKK_LOS[13] + P[12][1]*SKK_LOS[11] + P[12][2]*SKK_LOS[12] - P[12][9]*SH_LOS[1]*SH_LOS[7]*SKK_LOS[14] + P[12][22]*SH_LOS[1]*SH_LOS[7]*SKK_LOS[14] + P[12][6]*SH_LOS[3]*SKK_LOS[0]*SKK_LOS[14] + P[12][4]*SH_LOS[3]*SKK_LOS[2]*SKK_LOS[14] - P[12][5]*SH_LOS[3]*SKK_LOS[1]*SKK_LOS[14]);
+// Kfusion[13] = 0.0f;//SK_LOS[1]*(P[13][0]*SKK_LOS[10] - P[13][3]*SKK_LOS[13] + P[13][1]*SKK_LOS[11] + P[13][2]*SKK_LOS[12] - P[13][9]*SH_LOS[1]*SH_LOS[7]*SKK_LOS[14] + P[13][22]*SH_LOS[1]*SH_LOS[7]*SKK_LOS[14] + P[13][6]*SH_LOS[3]*SKK_LOS[0]*SKK_LOS[14] + P[13][4]*SH_LOS[3]*SKK_LOS[2]*SKK_LOS[14] - P[13][5]*SH_LOS[3]*SKK_LOS[1]*SKK_LOS[14]);
+// Kfusion[14] = SK_LOS[1]*(P[14][0]*SKK_LOS[10] - P[14][3]*SKK_LOS[13] + P[14][1]*SKK_LOS[11] + P[14][2]*SKK_LOS[12] - P[14][9]*SH_LOS[1]*SH_LOS[7]*SKK_LOS[14] + P[14][22]*SH_LOS[1]*SH_LOS[7]*SKK_LOS[14] + P[14][6]*SH_LOS[3]*SKK_LOS[0]*SKK_LOS[14] + P[14][4]*SH_LOS[3]*SKK_LOS[2]*SKK_LOS[14] - P[14][5]*SH_LOS[3]*SKK_LOS[1]*SKK_LOS[14]);
+// Kfusion[15] = SK_LOS[1]*(P[15][0]*SKK_LOS[10] - P[15][3]*SKK_LOS[13] + P[15][1]*SKK_LOS[11] + P[15][2]*SKK_LOS[12] - P[15][9]*SH_LOS[1]*SH_LOS[7]*SKK_LOS[14] + P[15][22]*SH_LOS[1]*SH_LOS[7]*SKK_LOS[14] + P[15][6]*SH_LOS[3]*SKK_LOS[0]*SKK_LOS[14] + P[15][4]*SH_LOS[3]*SKK_LOS[2]*SKK_LOS[14] - P[15][5]*SH_LOS[3]*SKK_LOS[1]*SKK_LOS[14]);
+// Kfusion[16] = SK_LOS[1]*(P[16][0]*SKK_LOS[10] - P[16][3]*SKK_LOS[13] + P[16][1]*SKK_LOS[11] + P[16][2]*SKK_LOS[12] - P[16][9]*SH_LOS[1]*SH_LOS[7]*SKK_LOS[14] + P[16][22]*SH_LOS[1]*SH_LOS[7]*SKK_LOS[14] + P[16][6]*SH_LOS[3]*SKK_LOS[0]*SKK_LOS[14] + P[16][4]*SH_LOS[3]*SKK_LOS[2]*SKK_LOS[14] - P[16][5]*SH_LOS[3]*SKK_LOS[1]*SKK_LOS[14]);
+// Kfusion[17] = SK_LOS[1]*(P[17][0]*SKK_LOS[10] - P[17][3]*SKK_LOS[13] + P[17][1]*SKK_LOS[11] + P[17][2]*SKK_LOS[12] - P[17][9]*SH_LOS[1]*SH_LOS[7]*SKK_LOS[14] + P[17][22]*SH_LOS[1]*SH_LOS[7]*SKK_LOS[14] + P[17][6]*SH_LOS[3]*SKK_LOS[0]*SKK_LOS[14] + P[17][4]*SH_LOS[3]*SKK_LOS[2]*SKK_LOS[14] - P[17][5]*SH_LOS[3]*SKK_LOS[1]*SKK_LOS[14]);
+// Kfusion[18] = SK_LOS[1]*(P[18][0]*SKK_LOS[10] - P[18][3]*SKK_LOS[13] + P[18][1]*SKK_LOS[11] + P[18][2]*SKK_LOS[12] - P[18][9]*SH_LOS[1]*SH_LOS[7]*SKK_LOS[14] + P[18][22]*SH_LOS[1]*SH_LOS[7]*SKK_LOS[14] + P[18][6]*SH_LOS[3]*SKK_LOS[0]*SKK_LOS[14] + P[18][4]*SH_LOS[3]*SKK_LOS[2]*SKK_LOS[14] - P[18][5]*SH_LOS[3]*SKK_LOS[1]*SKK_LOS[14]);
+// Kfusion[19] = SK_LOS[1]*(P[19][0]*SKK_LOS[10] - P[19][3]*SKK_LOS[13] + P[19][1]*SKK_LOS[11] + P[19][2]*SKK_LOS[12] - P[19][9]*SH_LOS[1]*SH_LOS[7]*SKK_LOS[14] + P[19][22]*SH_LOS[1]*SH_LOS[7]*SKK_LOS[14] + P[19][6]*SH_LOS[3]*SKK_LOS[0]*SKK_LOS[14] + P[19][4]*SH_LOS[3]*SKK_LOS[2]*SKK_LOS[14] - P[19][5]*SH_LOS[3]*SKK_LOS[1]*SKK_LOS[14]);
+// Kfusion[20] = SK_LOS[1]*(P[20][0]*SKK_LOS[10] - P[20][3]*SKK_LOS[13] + P[20][1]*SKK_LOS[11] + P[20][2]*SKK_LOS[12] - P[20][9]*SH_LOS[1]*SH_LOS[7]*SKK_LOS[14] + P[20][22]*SH_LOS[1]*SH_LOS[7]*SKK_LOS[14] + P[20][6]*SH_LOS[3]*SKK_LOS[0]*SKK_LOS[14] + P[20][4]*SH_LOS[3]*SKK_LOS[2]*SKK_LOS[14] - P[20][5]*SH_LOS[3]*SKK_LOS[1]*SKK_LOS[14]);
+// Kfusion[21] = SK_LOS[1]*(P[21][0]*SKK_LOS[10] - P[21][3]*SKK_LOS[13] + P[21][1]*SKK_LOS[11] + P[21][2]*SKK_LOS[12] - P[21][9]*SH_LOS[1]*SH_LOS[7]*SKK_LOS[14] + P[21][22]*SH_LOS[1]*SH_LOS[7]*SKK_LOS[14] + P[21][6]*SH_LOS[3]*SKK_LOS[0]*SKK_LOS[14] + P[21][4]*SH_LOS[3]*SKK_LOS[2]*SKK_LOS[14] - P[21][5]*SH_LOS[3]*SKK_LOS[1]*SKK_LOS[14]);
+// Kfusion[22] = SK_LOS[1]*(P[22][0]*SKK_LOS[10] - P[22][3]*SKK_LOS[13] + P[22][1]*SKK_LOS[11] + P[22][2]*SKK_LOS[12] - P[22][9]*SH_LOS[1]*SH_LOS[7]*SKK_LOS[14] + P[22][22]*SH_LOS[1]*SH_LOS[7]*SKK_LOS[14] + P[22][6]*SH_LOS[3]*SKK_LOS[0]*SKK_LOS[14] + P[22][4]*SH_LOS[3]*SKK_LOS[2]*SKK_LOS[14] - P[22][5]*SH_LOS[3]*SKK_LOS[1]*SKK_LOS[14]);
+// varInnovOptFlow[1] = 1.0f/SK_LOS[1];
+// innovOptFlow[1] = losPred[1] - losData[1];
+
+// // reset the observation index
+// obsIndex = 0;
+// fuseOptFlowData = false;
+// }
+
+// // Check the innovation for consistency and don't fuse if > 3Sigma
+// if ((innovOptFlow[obsIndex]*innovOptFlow[obsIndex]/varInnovOptFlow[obsIndex]) < 9.0f)
+// {
+// // correct the state vector
+// for (uint8_t j = 0; j < n_states; j++)
+// {
+// states[j] = states[j] - Kfusion[j] * innovOptFlow[obsIndex];
+// }
+// // normalise the quaternion states
+// float quatMag = sqrt(states[0]*states[0] + states[1]*states[1] + states[2]*states[2] + states[3]*states[3]);
+// if (quatMag > 1e-12f)
+// {
+// for (uint8_t j= 0; j<=3; j++)
+// {
+// float quatMagInv = 1.0f/quatMag;
+// states[j] = states[j] * quatMagInv;
+// }
+// }
+// // correct the covariance P = (I - K*H)*P
+// // take advantage of the empty columns in KH to reduce the
+// // number of operations
+// for (uint8_t i = 0; i < n_states; i++)
+// {
+// for (uint8_t j = 0; j <= 6; j++)
+// {
+// KH[i][j] = Kfusion[i] * H_LOS[j];
+// }
+// for (uint8_t j = 7; j <= 8; j++)
+// {
+// KH[i][j] = 0.0f;
+// }
+// KH[i][9] = Kfusion[i] * H_LOS[9];
+// for (uint8_t j = 10; j <= 21; j++)
+// {
+// KH[i][j] = 0.0f;
+// }
+// KH[i][22] = Kfusion[i] * H_LOS[22];
+// }
+// for (uint8_t i = 0; i < n_states; i++)
+// {
+// for (uint8_t j = 0; j < n_states; j++)
+// {
+// KHP[i][j] = 0.0f;
+// for (uint8_t k = 0; k <= 6; k++)
+// {
+// KHP[i][j] = KHP[i][j] + KH[i][k] * P[k][j];
+// }
+// KHP[i][j] = KHP[i][j] + KH[i][9] * P[9][j];
+// KHP[i][j] = KHP[i][j] + KH[i][22] * P[2][j];
+// }
+// }
+// }
+// for (uint8_t i = 0; i < n_states; i++)
+// {
+// for (uint8_t j = 0; j < n_states; j++)
+// {
+// P[i][j] = P[i][j] - KHP[i][j];
+// }
+// }
+// ForceSymmetry();
+// ConstrainVariances();
+// }
+}
+
+/*
+Estimation of optical flow sensor focal length scale factor and terrain height using a two state EKF
+This fiter requires optical flow rates that are not motion compensated
+Range to ground measurement is assumed to be via a narrow beam type sensor - eg laser
+*/
+void AttPosEKF::GroundEKF()
+{
+ // propagate ground position state noise each time this is called using the difference in position since the last observations and an RMS gradient assumption
+ // limit distance to prevent intialisation afer bad gps causing bad numerical conditioning
+ if (!inhibitGndState) {
+ float distanceTravelledSq;
+ distanceTravelledSq = sq(statesAtRngTime[7] - prevPosN) + sq(statesAtRngTime[8] - prevPosE);
+ prevPosN = statesAtRngTime[7];
+ prevPosE = statesAtRngTime[8];
+ distanceTravelledSq = min(distanceTravelledSq, 100.0f);
+ Popt[1][1] += (distanceTravelledSq * sq(gndHgtSigma));
+ }
+ // we aren't using optical flow measurements in this hacked implementation so set the covariances for this state to zero to avoid numerical problems
+ Popt[0][0] = 0.0f;
+ Popt[0][1] = 0.0f;
+ Popt[1][0] = 0.0f;
+
+ // Fuse range finder data
+ // Need to check that our range finder tilt angle is less than 30 degrees
+ float cosRngTilt = - Tbn.z.x * sinf(rngFinderPitch) + Tbn.z.z * cosf(rngFinderPitch);
+ if (useRangeFinder && fuseRngData && cosRngTilt > 0.87f) {
+ float range; // range from camera to centre of image
+ float q0; // quaternion at optical flow measurement time
+ float q1; // quaternion at optical flow measurement time
+ float q2; // quaternion at optical flow measurement time
+ float q3; // quaternion at optical flow measurement time
+ float R_RNG = 0.5; // range measurement variance (m^2) TODO make this a function of range and tilt to allow for sensor, alignment and AHRS errors
+
+ // Copy required states to local variable names
+ q0 = statesAtRngTime[0];
+ q1 = statesAtRngTime[1];
+ q2 = statesAtRngTime[2];
+ q3 = statesAtRngTime[3];
+
+ // calculate Kalman gains
+ float SK_RNG[3];
+ SK_RNG[0] = sq(q0) - sq(q1) - sq(q2) + sq(q3);
+ SK_RNG[1] = 1/(R_RNG + Popt[1][1]/sq(SK_RNG[0]));
+ SK_RNG[2] = 1/SK_RNG[0];
+ float K_RNG[2];
+ if (!inhibitScaleState) {
+ K_RNG[0] = Popt[0][1]*SK_RNG[1]*SK_RNG[2];
+ } else {
+ K_RNG[0] = 0.0f;
+ }
+ if (!inhibitGndState) {
+ K_RNG[1] = Popt[1][1]*SK_RNG[1]*SK_RNG[2];
+ } else {
+ K_RNG[1] = 0.0f;
+ }
+
+ // Calculate the innovation variance for data logging
+ varInnovRng = 1.0f/SK_RNG[1];
+
+ // constrain terrain height to be below the vehicle
+ flowStates[1] = maxf(flowStates[1], statesAtRngTime[9] + minFlowRng);
+
+ // estimate range to centre of image
+ range = (flowStates[1] - statesAtRngTime[9]) * SK_RNG[2];
+
+ // Calculate the measurement innovation
+ innovRng = range - rngMea;
+
+ // calculate the innovation consistency test ratio
+ auxRngTestRatio = sq(innovRng) / (sq(rngInnovGate) * varInnovRng);
+
+ // Check the innovation for consistency and don't fuse if out of bounds
+ if (auxRngTestRatio < 1.0f)
+ {
+ // correct the state
+ for (uint8_t i = 0; i < 2 ; i++) {
+ flowStates[i] -= K_RNG[i] * innovRng;
+ }
+ // constrain the states
+
+ // constrain focal length to 0.1 to 10 mm
+ flowStates[0] = ConstrainFloat(flowStates[0], 0.1f, 10.0f);
+ // constrain altitude
+ flowStates[1] = maxf(flowStates[1], statesAtRngTime[9] + minFlowRng);
+
+ // correct the covariance matrix
+ float nextPopt[2][2];
+ nextPopt[0][0] = Popt[0][0] - (Popt[0][1]*Popt[1][0]*SK_RNG[1]*SK_RNG[2]) * SK_RNG[2];
+ nextPopt[0][1] = Popt[0][1] - (Popt[0][1]*Popt[1][1]*SK_RNG[1]*SK_RNG[2]) * SK_RNG[2];
+ nextPopt[1][0] = -Popt[1][0]*((Popt[1][1]*SK_RNG[1]*SK_RNG[2]) * SK_RNG[2] - 1.0f);
+ nextPopt[1][1] = -Popt[1][1]*((Popt[1][1]*SK_RNG[1]*SK_RNG[2]) * SK_RNG[2] - 1.0f);
+ // prevent the state variances from becoming negative and maintain symmetry
+ Popt[0][0] = maxf(nextPopt[0][0],0.0f);
+ Popt[1][1] = maxf(nextPopt[1][1],0.0f);
+ Popt[0][1] = 0.5f * (nextPopt[0][1] + nextPopt[1][0]);
+ Popt[1][0] = Popt[0][1];
+ }
+ }
+}
+
void AttPosEKF::zeroCols(float (&covMat)[n_states][n_states], uint8_t first, uint8_t last)
{
uint8_t row;
@@ -1817,6 +2305,24 @@ float AttPosEKF::sq(float valIn)
return valIn*valIn;
}
+float AttPosEKF::maxf(float valIn1, float valIn2)
+{
+ if (valIn1 >= valIn2) {
+ return valIn1;
+ } else {
+ return valIn2;
+ }
+}
+
+float AttPosEKF::min(float valIn1, float valIn2)
+{
+ if (valIn1 <= valIn2) {
+ return valIn1;
+ } else {
+ return valIn2;
+ }
+}
+
// Store states in a history array along with time stamp
void AttPosEKF::StoreStates(uint64_t timestamp_ms)
{
@@ -1855,21 +2361,21 @@ int AttPosEKF::RecallStates(float* statesForFusion, uint64_t msec)
int64_t bestTimeDelta = 200;
unsigned bestStoreIndex = 0;
- for (unsigned storeIndex = 0; storeIndex < data_buffer_size; storeIndex++)
+ for (unsigned storeIndexLocal = 0; storeIndexLocal < data_buffer_size; storeIndexLocal++)
{
// Work around a GCC compiler bug - we know 64bit support on ARM is
// sketchy in GCC.
uint64_t timeDelta;
- if (msec > statetimeStamp[storeIndex]) {
- timeDelta = msec - statetimeStamp[storeIndex];
+ if (msec > statetimeStamp[storeIndexLocal]) {
+ timeDelta = msec - statetimeStamp[storeIndexLocal];
} else {
- timeDelta = statetimeStamp[storeIndex] - msec;
+ timeDelta = statetimeStamp[storeIndexLocal] - msec;
}
- if (timeDelta < bestTimeDelta)
+ if (timeDelta < (uint64_t)bestTimeDelta)
{
- bestStoreIndex = storeIndex;
+ bestStoreIndex = storeIndexLocal;
bestTimeDelta = timeDelta;
}
}
@@ -1926,7 +2432,7 @@ void AttPosEKF::quat2Tnb(Mat3f &Tnb, const float (&quat)[4])
Tnb.y.z = 2*(q23 + q01);
}
-void AttPosEKF::quat2Tbn(Mat3f &Tbn, const float (&quat)[4])
+void AttPosEKF::quat2Tbn(Mat3f &Tbn_ret, const float (&quat)[4])
{
// Calculate the body to nav cosine matrix
float q00 = sq(quat[0]);
@@ -1940,15 +2446,15 @@ void AttPosEKF::quat2Tbn(Mat3f &Tbn, const float (&quat)[4])
float q13 = quat[1]*quat[3];
float q23 = quat[2]*quat[3];
- Tbn.x.x = q00 + q11 - q22 - q33;
- Tbn.y.y = q00 - q11 + q22 - q33;
- Tbn.z.z = q00 - q11 - q22 + q33;
- Tbn.x.y = 2*(q12 - q03);
- Tbn.x.z = 2*(q13 + q02);
- Tbn.y.x = 2*(q12 + q03);
- Tbn.y.z = 2*(q23 - q01);
- Tbn.z.x = 2*(q13 - q02);
- Tbn.z.y = 2*(q23 + q01);
+ Tbn_ret.x.x = q00 + q11 - q22 - q33;
+ Tbn_ret.y.y = q00 - q11 + q22 - q33;
+ Tbn_ret.z.z = q00 - q11 - q22 + q33;
+ Tbn_ret.x.y = 2*(q12 - q03);
+ Tbn_ret.x.z = 2*(q13 + q02);
+ Tbn_ret.y.x = 2*(q12 + q03);
+ Tbn_ret.y.z = 2*(q23 - q01);
+ Tbn_ret.z.x = 2*(q13 - q02);
+ Tbn_ret.z.y = 2*(q23 + q01);
}
void AttPosEKF::eul2quat(float (&quat)[4], const float (&eul)[3])
@@ -1979,26 +2485,44 @@ void AttPosEKF::calcvelNED(float (&velNED)[3], float gpsCourse, float gpsGndSpd,
velNED[2] = gpsVelD;
}
-void AttPosEKF::calcposNED(float (&posNED)[3], double lat, double lon, float hgt, double latRef, double lonRef, float hgtRef)
+void AttPosEKF::calcposNED(float (&posNED)[3], double lat, double lon, float hgt, double latReference, double lonReference, float hgtReference)
{
- posNED[0] = earthRadius * (lat - latRef);
- posNED[1] = earthRadius * cos(latRef) * (lon - lonRef);
- posNED[2] = -(hgt - hgtRef);
+ posNED[0] = earthRadius * (lat - latReference);
+ posNED[1] = earthRadius * cos(latReference) * (lon - lonReference);
+ posNED[2] = -(hgt - hgtReference);
}
-void AttPosEKF::calcLLH(float (&posNED)[3], float lat, float lon, float hgt, float latRef, float lonRef, float hgtRef)
+void AttPosEKF::calcLLH(float posNED[3], double &lat, double &lon, float &hgt, double latRef, double lonRef, float hgtRef)
{
- lat = latRef + posNED[0] * earthRadiusInv;
- lon = lonRef + posNED[1] * earthRadiusInv / cos(latRef);
+ lat = latRef + (double)posNED[0] * earthRadiusInv;
+ lon = lonRef + (double)posNED[1] * earthRadiusInv / cos(latRef);
hgt = hgtRef - posNED[2];
}
void AttPosEKF::OnGroundCheck()
{
- onGround = (((sq(velNED[0]) + sq(velNED[1]) + sq(velNED[2])) < 4.0f) && (VtasMeas < 6.0f));
+ onGround = (((sq(velNED[0]) + sq(velNED[1]) + sq(velNED[2])) < 4.0f) && (VtasMeas < 8.0f));
if (staticMode) {
staticMode = (!refSet || (GPSstatus < GPS_FIX_3D));
}
+ // don't update wind states if there is no airspeed measurement
+ if (onGround || !useAirspeed) {
+ inhibitWindStates = true;
+ } else {
+ inhibitWindStates =false;
+ }
+ // don't update magnetic field states if on ground or not using compass
+ if (onGround || !useCompass) {
+ inhibitMagStates = true;
+ } else {
+ inhibitMagStates = false;
+ }
+ // don't update terrain offset state if on ground
+ if (onGround) {
+ inhibitGndState = true;
+ } else {
+ inhibitGndState = false;
+ }
}
void AttPosEKF::calcEarthRateNED(Vector3f &omega, float latitude)
@@ -2026,8 +2550,8 @@ void AttPosEKF::CovarianceInit()
P[11][11] = P[10][10];
P[12][12] = P[10][10];
P[13][13] = sq(0.2f*dtIMU);
- P[14][14] = sq(8.0f);
- P[15][14] = P[14][14];
+ P[14][14] = sq(0.0f);
+ P[15][15] = P[14][14];
P[16][16] = sq(0.02f);
P[17][17] = P[16][16];
P[18][18] = P[16][16];
@@ -2037,21 +2561,21 @@ void AttPosEKF::CovarianceInit()
P[22][22] = sq(0.5f);
}
-float AttPosEKF::ConstrainFloat(float val, float min, float max)
+float AttPosEKF::ConstrainFloat(float val, float min_val, float max_val)
{
float ret;
- if (val > max) {
- ret = max;
- ekf_debug("> max: %8.4f, val: %8.4f", max, val);
- } else if (val < min) {
- ret = min;
- ekf_debug("< min: %8.4f, val: %8.4f", min, val);
+ if (val > max_val) {
+ ret = max_val;
+ ekf_debug("> max: %8.4f, val: %8.4f", (double)max_val, (double)val);
+ } else if (val < min_val) {
+ ret = min_val;
+ ekf_debug("< min: %8.4f, val: %8.4f", (double)min_val, (double)val);
} else {
ret = val;
}
if (!isfinite(val)) {
- ekf_debug("constrain: non-finite!");
+ //ekf_debug("constrain: non-finite!");
}
return ret;
@@ -2194,10 +2718,71 @@ void AttPosEKF::ForceSymmetry()
{
P[i][j] = 0.5f * (P[i][j] + P[j][i]);
P[j][i] = P[i][j];
+
+ if ((fabsf(P[i][j]) > EKF_COVARIANCE_DIVERGED) ||
+ (fabsf(P[j][i]) > EKF_COVARIANCE_DIVERGED)) {
+ current_ekf_state.covariancesExcessive = true;
+ current_ekf_state.error |= true;
+ InitializeDynamic(velNED, magDeclination);
+ return;
+ }
+
+ float symmetric = 0.5f * (P[i][j] + P[j][i]);
+ P[i][j] = symmetric;
+ P[j][i] = symmetric;
}
}
}
+bool AttPosEKF::GyroOffsetsDiverged()
+{
+ // Detect divergence by looking for rapid changes of the gyro offset
+ Vector3f current_bias;
+ current_bias.x = states[10];
+ current_bias.y = states[11];
+ current_bias.z = states[12];
+
+ Vector3f delta = current_bias - lastGyroOffset;
+ float delta_len = delta.length();
+ float delta_len_scaled = 0.0f;
+
+ // Protect against division by zero
+ if (delta_len > 0.0f) {
+ float cov_mag = ConstrainFloat((P[10][10] + P[11][11] + P[12][12]), 1e-12f, 1e-8f);
+ delta_len_scaled = (5e-7 / (double)cov_mag) * (double)delta_len / (double)dtIMU;
+ }
+
+ bool diverged = (delta_len_scaled > 1.0f);
+ lastGyroOffset = current_bias;
+ current_ekf_state.error |= diverged;
+ current_ekf_state.gyroOffsetsExcessive = diverged;
+
+ return diverged;
+}
+
+bool AttPosEKF::VelNEDDiverged()
+{
+ Vector3f current_vel;
+ current_vel.x = states[4];
+ current_vel.y = states[5];
+ current_vel.z = states[6];
+
+ Vector3f gps_vel;
+ gps_vel.x = velNED[0];
+ gps_vel.y = velNED[1];
+ gps_vel.z = velNED[2];
+
+ Vector3f delta = current_vel - gps_vel;
+ float delta_len = delta.length();
+
+ bool excessive = (delta_len > 20.0f);
+
+ current_ekf_state.error |= excessive;
+ current_ekf_state.velOffsetExcessive = excessive;
+
+ return excessive;
+}
+
bool AttPosEKF::FilterHealthy()
{
if (!statesInitialised) {
@@ -2262,42 +2847,26 @@ void AttPosEKF::ResetVelocity(void)
}
}
-
-void AttPosEKF::FillErrorReport(struct ekf_status_report *err)
-{
- for (unsigned i = 0; i < n_states; i++)
- {
- err->states[i] = states[i];
- }
-
- err->velHealth = current_ekf_state.velHealth;
- err->posHealth = current_ekf_state.posHealth;
- err->hgtHealth = current_ekf_state.hgtHealth;
- err->velTimeout = current_ekf_state.velTimeout;
- err->posTimeout = current_ekf_state.posTimeout;
- err->hgtTimeout = current_ekf_state.hgtTimeout;
-}
-
-bool AttPosEKF::StatesNaN(struct ekf_status_report *err_report) {
+bool AttPosEKF::StatesNaN() {
bool err = false;
// check all integrators
if (!isfinite(summedDelAng.x) || !isfinite(summedDelAng.y) || !isfinite(summedDelAng.z)) {
- err_report->statesNaN = true;
+ current_ekf_state.angNaN = true;
ekf_debug("summedDelAng NaN: x: %f y: %f z: %f", (double)summedDelAng.x, (double)summedDelAng.y, (double)summedDelAng.z);
err = true;
goto out;
} // delta angles
if (!isfinite(correctedDelAng.x) || !isfinite(correctedDelAng.y) || !isfinite(correctedDelAng.z)) {
- err_report->statesNaN = true;
+ current_ekf_state.angNaN = true;
ekf_debug("correctedDelAng NaN: x: %f y: %f z: %f", (double)correctedDelAng.x, (double)correctedDelAng.y, (double)correctedDelAng.z);
err = true;
goto out;
} // delta angles
if (!isfinite(summedDelVel.x) || !isfinite(summedDelVel.y) || !isfinite(summedDelVel.z)) {
- err_report->statesNaN = true;
+ current_ekf_state.summedDelVelNaN = true;
ekf_debug("summedDelVel NaN: x: %f y: %f z: %f", (double)summedDelVel.x, (double)summedDelVel.y, (double)summedDelVel.z);
err = true;
goto out;
@@ -2308,7 +2877,7 @@ bool AttPosEKF::StatesNaN(struct ekf_status_report *err_report) {
for (unsigned j = 0; j < n_states; j++) {
if (!isfinite(KH[i][j])) {
- err_report->covarianceNaN = true;
+ current_ekf_state.KHNaN = true;
err = true;
ekf_debug("KH NaN");
goto out;
@@ -2316,7 +2885,7 @@ bool AttPosEKF::StatesNaN(struct ekf_status_report *err_report) {
if (!isfinite(KHP[i][j])) {
- err_report->covarianceNaN = true;
+ current_ekf_state.KHPNaN = true;
err = true;
ekf_debug("KHP NaN");
goto out;
@@ -2324,7 +2893,7 @@ bool AttPosEKF::StatesNaN(struct ekf_status_report *err_report) {
if (!isfinite(P[i][j])) {
- err_report->covarianceNaN = true;
+ current_ekf_state.covarianceNaN = true;
err = true;
ekf_debug("P NaN");
} // covariance matrix
@@ -2332,7 +2901,7 @@ bool AttPosEKF::StatesNaN(struct ekf_status_report *err_report) {
if (!isfinite(Kfusion[i])) {
- err_report->kalmanGainsNaN = true;
+ current_ekf_state.kalmanGainsNaN = true;
ekf_debug("Kfusion NaN");
err = true;
goto out;
@@ -2340,7 +2909,7 @@ bool AttPosEKF::StatesNaN(struct ekf_status_report *err_report) {
if (!isfinite(states[i])) {
- err_report->statesNaN = true;
+ current_ekf_state.statesNaN = true;
ekf_debug("states NaN: i: %u val: %f", i, (double)states[i]);
err = true;
goto out;
@@ -2349,7 +2918,7 @@ bool AttPosEKF::StatesNaN(struct ekf_status_report *err_report) {
out:
if (err) {
- FillErrorReport(err_report);
+ current_ekf_state.error |= true;
}
return err;
@@ -2365,47 +2934,105 @@ out:
* updated, but before any of the fusion steps are
* executed.
*/
-int AttPosEKF::CheckAndBound()
+int AttPosEKF::CheckAndBound(struct ekf_status_report *last_error)
{
// Store the old filter state
bool currStaticMode = staticMode;
+ // Limit reset rate to 5 Hz to allow the filter
+ // to settle
+ if (millis() - lastReset < 200) {
+ return 0;
+ }
+
+ if (ekfDiverged) {
+ ekfDiverged = false;
+ }
+
+ int ret = 0;
+
+ // Check if we're on ground - this also sets static mode.
+ OnGroundCheck();
+
// Reset the filter if the states went NaN
- if (StatesNaN(&last_ekf_error)) {
+ if (StatesNaN()) {
ekf_debug("re-initializing dynamic");
- InitializeDynamic(velNED, magDeclination);
+ // Reset and fill error report
+ InitializeDynamic(velNED, magDeclination);
- return 1;
+ ret = 1;
}
// Reset the filter if the IMU data is too old
if (dtIMU > 0.3f) {
+ current_ekf_state.imuTimeout = true;
+
+ // Fill error report
+ GetFilterState(&last_ekf_error);
+
ResetVelocity();
ResetPosition();
ResetHeight();
ResetStoredStates();
+ // Timeout cleared with this reset
+ current_ekf_state.imuTimeout = false;
+
// that's all we can do here, return
- return 2;
+ ret = 2;
}
- // Check if we're on ground - this also sets static mode.
- OnGroundCheck();
-
// Check if we switched between states
if (currStaticMode != staticMode) {
+ // Fill error report, but not setting error flag
+ GetFilterState(&last_ekf_error);
+
ResetVelocity();
ResetPosition();
ResetHeight();
ResetStoredStates();
- return 3;
+ ret = 0;
+ }
+
+ // Reset the filter if gyro offsets are excessive
+ if (GyroOffsetsDiverged()) {
+
+ // Reset and fill error report
+ InitializeDynamic(velNED, magDeclination);
+
+ // that's all we can do here, return
+ ret = 4;
+ }
+
+ // Reset the filter if it diverges too far from GPS
+ if (VelNEDDiverged()) {
+
+ // Reset and fill error report
+ InitializeDynamic(velNED, magDeclination);
+
+ // that's all we can do here, return
+ ret = 5;
+ }
+
+ // The excessive covariance detection already
+ // reset the filter. Just need to report here.
+ if (last_ekf_error.covariancesExcessive) {
+ ret = 6;
+ }
+
+ if (ret) {
+ ekfDiverged = true;
+ lastReset = millis();
+
+ // This reads the last error and clears it
+ GetLastErrorState(last_error);
}
- return 0;
+ return ret;
}
void AttPosEKF::AttitudeInit(float ax, float ay, float az, float mx, float my, float mz, float declination, float *initQuat)
@@ -2456,6 +3083,36 @@ void AttPosEKF::AttitudeInit(float ax, float ay, float az, float mx, float my, f
void AttPosEKF::InitializeDynamic(float (&initvelNED)[3], float declination)
{
+ if (current_ekf_state.error) {
+ GetFilterState(&last_ekf_error);
+ }
+
+ ZeroVariables();
+
+ // Reset error states
+ current_ekf_state.error = false;
+ current_ekf_state.angNaN = false;
+ current_ekf_state.summedDelVelNaN = false;
+ current_ekf_state.KHNaN = false;
+ current_ekf_state.KHPNaN = false;
+ current_ekf_state.PNaN = false;
+ current_ekf_state.covarianceNaN = false;
+ current_ekf_state.kalmanGainsNaN = false;
+ current_ekf_state.statesNaN = false;
+
+ current_ekf_state.velHealth = true;
+ current_ekf_state.posHealth = true;
+ current_ekf_state.hgtHealth = true;
+
+ current_ekf_state.velTimeout = false;
+ current_ekf_state.posTimeout = false;
+ current_ekf_state.hgtTimeout = false;
+
+ fuseVelData = false;
+ fusePosData = false;
+ fuseHgtData = false;
+ fuseMagData = false;
+ fuseVtasData = false;
// Fill variables with valid data
velNED[0] = initvelNED[0];
@@ -2471,12 +3128,12 @@ void AttPosEKF::InitializeDynamic(float (&initvelNED)[3], float declination)
// Calculate initial Tbn matrix and rotate Mag measurements into NED
// to set initial NED magnetic field states
- Mat3f DCM;
- quat2Tbn(DCM, initQuat);
+ quat2Tbn(Tbn, initQuat);
+ Tnb = Tbn.transpose();
Vector3f initMagNED;
- initMagNED.x = DCM.x.x*initMagXYZ.x + DCM.x.y*initMagXYZ.y + DCM.x.z*initMagXYZ.z;
- initMagNED.y = DCM.y.x*initMagXYZ.x + DCM.y.y*initMagXYZ.y + DCM.y.z*initMagXYZ.z;
- initMagNED.z = DCM.z.x*initMagXYZ.x + DCM.z.y*initMagXYZ.y + DCM.z.z*initMagXYZ.z;
+ initMagNED.x = Tbn.x.x*initMagXYZ.x + Tbn.x.y*initMagXYZ.y + Tbn.x.z*initMagXYZ.z;
+ initMagNED.y = Tbn.y.x*initMagXYZ.x + Tbn.y.y*initMagXYZ.y + Tbn.y.z*initMagXYZ.z;
+ initMagNED.z = Tbn.z.x*initMagXYZ.x + Tbn.z.y*initMagXYZ.y + Tbn.z.z*initMagXYZ.z;
magstate.q0 = initQuat[0];
magstate.q1 = initQuat[1];
@@ -2489,12 +3146,16 @@ void AttPosEKF::InitializeDynamic(float (&initvelNED)[3], float declination)
magstate.magYbias = magBias.y;
magstate.magZbias = magBias.z;
magstate.R_MAG = sq(magMeasurementSigma);
- magstate.DCM = DCM;
+ magstate.DCM = Tbn;
// write to state vector
for (uint8_t j=0; j<=3; j++) states[j] = initQuat[j]; // quaternions
for (uint8_t j=4; j<=6; j++) states[j] = initvelNED[j-4]; // velocities
- for (uint8_t j=7; j<=15; j++) states[j] = 0.0f; // positions, dAngBias, dVelBias, windVel
+ // positions:
+ states[7] = posNE[0];
+ states[8] = posNE[1];
+ states[9] = -hgtMea;
+ for (uint8_t j=10; j<=15; j++) states[j] = 0.0f; // dAngBias, dVelBias, windVel
states[16] = initMagNED.x; // Magnetic Field North
states[17] = initMagNED.y; // Magnetic Field East
states[18] = initMagNED.z; // Magnetic Field Down
@@ -2525,14 +3186,16 @@ void AttPosEKF::InitialiseFilter(float (&initvelNED)[3], double referenceLat, do
hgtRef = referenceHgt;
refSet = true;
- // we are at reference altitude, so measurement must be zero
- hgtMea = 0.0f;
+ // we are at reference position, so measurement must be zero
+ posNE[0] = 0.0f;
+ posNE[1] = 0.0f;
+
+ // we are at an unknown, possibly non-zero altitude - so altitude
+ // is not reset (hgtMea)
// the baro offset must be this difference now
baroHgtOffset = baroHgt - referenceHgt;
- memset(&last_ekf_error, 0, sizeof(last_ekf_error));
-
InitializeDynamic(initvelNED, declination);
}
@@ -2540,27 +3203,13 @@ void AttPosEKF::ZeroVariables()
{
// Initialize on-init initialized variables
- fusionModeGPS = 0;
- covSkipCount = 0;
- statesInitialised = false;
- fuseVelData = false;
- fusePosData = false;
- fuseHgtData = false;
- fuseMagData = false;
- fuseVtasData = false;
- onGround = true;
- staticMode = true;
- useAirspeed = true;
- useCompass = true;
- useRangeFinder = true;
- numericalProtection = true;
- refSet = false;
+ dtIMUfilt = ConstrainFloat(dtIMU, 0.001f, 0.02f);
+ dtVelPosFilt = ConstrainFloat(dtVelPos, 0.04f, 0.5f);
+ dtGpsFilt = 1.0f / 5.0f;
+ dtHgtFilt = 1.0f / 100.0f;
storeIndex = 0;
- gpsHgt = 0.0f;
- baroHgt = 0.0f;
- GPSstatus = 0;
- VtasMeas = 0.0f;
- magDeclination = 0.0f;
+
+ lastVelPosFusion = millis();
// Do the data structure init
for (unsigned i = 0; i < n_states; i++) {
@@ -2577,9 +3226,16 @@ void AttPosEKF::ZeroVariables()
correctedDelAng.zero();
summedDelAng.zero();
summedDelVel.zero();
-
dAngIMU.zero();
dVelIMU.zero();
+ lastGyroOffset.zero();
+
+ windSpdFiltNorth = 0.0f;
+ windSpdFiltEast = 0.0f;
+ // setting the altitude to zero will give us a higher
+ // gain to adjust faster in the first step
+ windSpdFiltAltitude = 0.0f;
+ windSpdFiltClimb = 0.0f;
for (unsigned i = 0; i < data_buffer_size; i++) {
@@ -2598,12 +3254,31 @@ void AttPosEKF::ZeroVariables()
}
-void AttPosEKF::GetFilterState(struct ekf_status_report *state)
+void AttPosEKF::GetFilterState(struct ekf_status_report *err)
{
- memcpy(state, &current_ekf_state, sizeof(*state));
+
+ // Copy states
+ for (unsigned i = 0; i < n_states; i++) {
+ current_ekf_state.states[i] = states[i];
+ }
+ current_ekf_state.n_states = n_states;
+ current_ekf_state.onGround = onGround;
+ current_ekf_state.staticMode = staticMode;
+ current_ekf_state.useCompass = useCompass;
+ current_ekf_state.useAirspeed = useAirspeed;
+
+ memcpy(err, &current_ekf_state, sizeof(*err));
+
+ // err->velHealth = current_ekf_state.velHealth;
+ // err->posHealth = current_ekf_state.posHealth;
+ // err->hgtHealth = current_ekf_state.hgtHealth;
+ // err->velTimeout = current_ekf_state.velTimeout;
+ // err->posTimeout = current_ekf_state.posTimeout;
+ // err->hgtTimeout = current_ekf_state.hgtTimeout;
}
void AttPosEKF::GetLastErrorState(struct ekf_status_report *last_error)
{
memcpy(last_error, &last_ekf_error, sizeof(*last_error));
+ memset(&last_ekf_error, 0, sizeof(last_ekf_error));
}
diff --git a/src/modules/ekf_att_pos_estimator/estimator_23states.h b/src/modules/ekf_att_pos_estimator/estimator_23states.h
new file mode 100644
index 000000000..a607955a8
--- /dev/null
+++ b/src/modules/ekf_att_pos_estimator/estimator_23states.h
@@ -0,0 +1,371 @@
+#pragma once
+
+#include "estimator_utilities.h"
+
+const unsigned int n_states = 23;
+const unsigned int data_buffer_size = 50;
+
+class AttPosEKF {
+
+public:
+
+ AttPosEKF();
+ ~AttPosEKF();
+
+
+
+ /* ##############################################
+ *
+ * M A I N F I L T E R P A R A M E T E R S
+ *
+ * ########################################### */
+
+ /*
+ * parameters are defined here and initialised in
+ * the InitialiseParameters() (which is just 20 lines down)
+ */
+
+ float covTimeStepMax; // maximum time allowed between covariance predictions
+ float covDelAngMax; // maximum delta angle between covariance predictions
+ float rngFinderPitch; // pitch angle of laser range finder in radians. Zero is aligned with the Z body axis. Positive is RH rotation about Y body axis.
+
+ float a1; // optical flow sensor misalgnment angle about X axis (rad)
+ float a2; // optical flow sensor misalgnment angle about Y axis (rad)
+ float a3; // optical flow sensor misalgnment angle about Z axis (rad)
+
+ float yawVarScale;
+ float windVelSigma;
+ float dAngBiasSigma;
+ float dVelBiasSigma;
+ float magEarthSigma;
+ float magBodySigma;
+ float gndHgtSigma;
+
+ float vneSigma;
+ float vdSigma;
+ float posNeSigma;
+ float posDSigma;
+ float magMeasurementSigma;
+ float airspeedMeasurementSigma;
+
+ float gyroProcessNoise;
+ float accelProcessNoise;
+
+ float EAS2TAS; // ratio f true to equivalent airspeed
+
+ void InitialiseParameters()
+ {
+ covTimeStepMax = 0.07f; // maximum time allowed between covariance predictions
+ covDelAngMax = 0.02f; // maximum delta angle between covariance predictions
+ rngFinderPitch = 0.0f; // pitch angle of laser range finder in radians. Zero is aligned with the Z body axis. Positive is RH rotation about Y body axis.
+ EAS2TAS = 1.0f;
+ a1 = 0.0f; // optical flow sensor misalgnment angle about X axis (rad)
+ a2 = 0.0f; // optical flow sensor misalgnment angle about Y axis (rad)
+ a3 = 0.0f; // optical flow sensor misalgnment angle about Z axis (rad)
+
+ yawVarScale = 1.0f;
+ windVelSigma = 0.1f;
+ dAngBiasSigma = 5.0e-7f;
+ dVelBiasSigma = 1e-4f;
+ magEarthSigma = 3.0e-4f;
+ magBodySigma = 3.0e-4f;
+ gndHgtSigma = 0.02f; // assume 2% terrain gradient 1-sigma
+
+ vneSigma = 0.2f;
+ vdSigma = 0.3f;
+ posNeSigma = 2.0f;
+ posDSigma = 2.0f;
+
+ magMeasurementSigma = 0.05;
+ airspeedMeasurementSigma = 1.4f;
+ gyroProcessNoise = 1.4544411e-2f;
+ accelProcessNoise = 0.5f;
+
+ gndHgtSigma = 0.1f; // terrain gradient 1-sigma
+ R_LOS = 0.03f; // optical flow measurement noise variance (rad/sec)^2
+ flowInnovGate = 3.0f; // number of standard deviations applied to the optical flow innovation consistency check
+ auxFlowInnovGate = 10.0f; // number of standard deviations applied to the optical flow innovation consistency check used by the auxiliary filter
+ rngInnovGate = 10.0f; // number of standard deviations applied to the rnage finder innovation consistency check
+ minFlowRng = 0.01f; //minimum range between ground and flow sensor
+ moCompR_LOS = 0.2; // scaler from sensor gyro rate to uncertainty in LOS rate
+ }
+
+ struct mag_state_struct {
+ unsigned obsIndex;
+ float MagPred[3];
+ float SH_MAG[9];
+ float q0;
+ float q1;
+ float q2;
+ float q3;
+ float magN;
+ float magE;
+ float magD;
+ float magXbias;
+ float magYbias;
+ float magZbias;
+ float R_MAG;
+ Mat3f DCM;
+ };
+
+ struct mag_state_struct magstate;
+ struct mag_state_struct resetMagState;
+
+
+
+
+ // Global variables
+ float KH[n_states][n_states]; // intermediate result used for covariance updates
+ float KHP[n_states][n_states]; // intermediate result used for covariance updates
+ float P[n_states][n_states]; // covariance matrix
+ float Kfusion[n_states]; // Kalman gains
+ float states[n_states]; // state matrix
+ float resetStates[n_states];
+ float storedStates[n_states][data_buffer_size]; // state vectors stored for the last 50 time steps
+ uint32_t statetimeStamp[data_buffer_size]; // time stamp for each state vector stored
+
+ // Times
+ uint64_t lastVelPosFusion; // the time of the last velocity fusion, in the standard time unit of the filter
+
+ float statesAtVelTime[n_states]; // States at the effective measurement time for posNE and velNED measurements
+ float statesAtPosTime[n_states]; // States at the effective measurement time for posNE and velNED measurements
+ float statesAtHgtTime[n_states]; // States at the effective measurement time for the hgtMea measurement
+ float statesAtMagMeasTime[n_states]; // filter satates at the effective measurement time
+ float statesAtVtasMeasTime[n_states]; // filter states at the effective measurement time
+ float statesAtRngTime[n_states]; // filter states at the effective measurement time
+ float statesAtFlowTime[n_states]; // States at the effective optical flow measurement time
+
+ Vector3f correctedDelAng; // delta angles about the xyz body axes corrected for errors (rad)
+ Vector3f correctedDelVel; // delta velocities along the XYZ body axes corrected for errors (m/s)
+ Vector3f summedDelAng; // summed delta angles about the xyz body axes corrected for errors (rad)
+ Vector3f summedDelVel; // summed delta velocities along the XYZ body axes corrected for errors (m/s)
+ float accNavMag; // magnitude of navigation accel (- used to adjust GPS obs variance (m/s^2)
+ Vector3f earthRateNED; // earths angular rate vector in NED (rad/s)
+ Vector3f angRate; // angular rate vector in XYZ body axes measured by the IMU (rad/s)
+ Vector3f lastGyroOffset; // Last gyro offset
+ Vector3f delAngTotal;
+
+ Mat3f Tbn; // transformation matrix from body to NED coordinates
+ Mat3f Tnb; // transformation amtrix from NED to body coordinates
+
+ Vector3f accel; // acceleration vector in XYZ body axes measured by the IMU (m/s^2)
+ Vector3f dVelIMU;
+ Vector3f dAngIMU;
+ float dtIMU; // time lapsed since the last IMU measurement or covariance update (sec), this may have significant jitter
+ float dtIMUfilt; // average time between IMU measurements (sec)
+ float dtVelPos; // time lapsed since the last position / velocity fusion (seconds), this may have significant jitter
+ float dtVelPosFilt; // average time between position / velocity fusion steps
+ float dtHgtFilt; // average time between height measurement updates
+ float dtGpsFilt; // average time between gps measurement updates
+ float windSpdFiltNorth; // average wind speed north component
+ float windSpdFiltEast; // average wind speed east component
+ float windSpdFiltAltitude; // the last altitude used to filter wind speed
+ float windSpdFiltClimb; // filtered climb rate
+ uint8_t fusionModeGPS; // 0 = GPS outputs 3D velocity, 1 = GPS outputs 2D velocity, 2 = GPS outputs no velocity
+ float innovVelPos[6]; // innovation output
+ float varInnovVelPos[6]; // innovation variance output
+
+ float velNED[3]; // North, East, Down velocity obs (m/s)
+ float accelGPSNED[3]; // Acceleration predicted by GPS in earth frame
+ float posNE[2]; // North, East position obs (m)
+ float hgtMea; // measured height (m)
+ float baroHgtOffset; ///< the baro (weather) offset from normalized altitude
+ float rngMea; // Ground distance
+
+ float innovMag[3]; // innovation output
+ float varInnovMag[3]; // innovation variance output
+ Vector3f magData; // magnetometer flux radings in X,Y,Z body axes
+ float losData[2]; // optical flow LOS rate measurements (rad/sec)
+ float innovVtas; // innovation output
+ float innovRng; ///< Range finder innovation
+ float innovOptFlow[2]; // optical flow LOS innovations (rad/sec)
+ float varInnovOptFlow[2]; // optical flow innovations variances (rad/sec)^2
+ float varInnovVtas; // innovation variance output
+ float varInnovRng; // range finder innovation variance
+ float VtasMeas; // true airspeed measurement (m/s)
+ float magDeclination; ///< magnetic declination
+ double latRef; // WGS-84 latitude of reference point (rad)
+ double lonRef; // WGS-84 longitude of reference point (rad)
+ float hgtRef; // WGS-84 height of reference point (m)
+ bool refSet; ///< flag to indicate if the reference position has been set
+ Vector3f magBias; // states representing magnetometer bias vector in XYZ body axes
+ unsigned covSkipCount; // Number of state prediction frames (IMU daya updates to skip before doing the covariance prediction
+
+ // GPS input data variables
+ double gpsLat;
+ double gpsLon;
+ float gpsHgt;
+ uint8_t GPSstatus;
+
+ // Baro input
+ float baroHgt;
+
+ bool statesInitialised;
+
+ bool fuseVelData; // this boolean causes the posNE and velNED obs to be fused
+ bool fusePosData; // this boolean causes the posNE and velNED obs to be fused
+ bool fuseHgtData; // this boolean causes the hgtMea obs to be fused
+ bool fuseMagData; // boolean true when magnetometer data is to be fused
+ bool fuseVtasData; // boolean true when airspeed data is to be fused
+ bool fuseRngData; ///< true when range data is fused
+ bool fuseOptFlowData; // true when optical flow data is fused
+
+ bool inhibitWindStates; // true when wind states and covariances are to remain constant
+ bool inhibitMagStates; // true when magnetic field states and covariances are to remain constant
+ bool inhibitGndState; // true when the terrain ground height offset state and covariances are to remain constant
+ bool inhibitScaleState; // true when the focal length scale factor state and covariances are to remain constant
+
+ bool onGround; ///< boolean true when the flight vehicle is on the ground (not flying)
+ bool staticMode; ///< boolean true if no position feedback is fused
+ bool useAirspeed; ///< boolean true if airspeed data is being used
+ bool useCompass; ///< boolean true if magnetometer data is being used
+ bool useRangeFinder; ///< true when rangefinder is being used
+ bool useOpticalFlow; // true when optical flow data is being used
+
+ bool ekfDiverged;
+ uint64_t lastReset;
+
+ struct ekf_status_report current_ekf_state;
+ struct ekf_status_report last_ekf_error;
+
+ bool numericalProtection;
+
+ unsigned storeIndex;
+
+ // Optical Flow error estimation
+ float storedOmega[3][data_buffer_size]; // angular rate vector stored for the last 50 time steps used by optical flow eror estimators
+
+ // Two state EKF used to estimate focal length scale factor and terrain position
+ float Popt[2][2]; // state covariance matrix
+ float flowStates[2]; // flow states [scale factor, terrain position]
+ float prevPosN; // north position at last measurement
+ float prevPosE; // east position at last measurement
+ float auxFlowObsInnov[2]; // optical flow observation innovations from focal length scale factor estimator
+ float auxFlowObsInnovVar[2]; // innovation variance for optical flow observations from focal length scale factor estimator
+ float fScaleFactorVar; // optical flow sensor focal length scale factor variance
+ Mat3f Tnb_flow; // Transformation matrix from nav to body at the time fo the optical flow measurement
+ float R_LOS; // Optical flow observation noise variance (rad/sec)^2
+ float auxFlowTestRatio[2]; // ratio of X and Y flow observation innovations to fault threshold
+ float auxRngTestRatio; // ratio of range observation innovations to fault threshold
+ float flowInnovGate; // number of standard deviations used for the innovation consistency check
+ float auxFlowInnovGate; // number of standard deviations applied to the optical flow innovation consistency check
+ float rngInnovGate; // number of standard deviations used for the innovation consistency check
+ float minFlowRng; // minimum range over which to fuse optical flow measurements
+ float moCompR_LOS; // scaler from sensor gyro rate to uncertainty in LOS rate
+
+void updateDtGpsFilt(float dt);
+
+void updateDtHgtFilt(float dt);
+
+void UpdateStrapdownEquationsNED();
+
+void CovariancePrediction(float dt);
+
+void FuseVelposNED();
+
+void FuseMagnetometer();
+
+void FuseAirspeed();
+
+void FuseRangeFinder();
+
+void FuseOptFlow();
+
+void GroundEKF();
+
+void zeroRows(float (&covMat)[n_states][n_states], uint8_t first, uint8_t last);
+
+void zeroCols(float (&covMat)[n_states][n_states], uint8_t first, uint8_t last);
+
+void quatNorm(float (&quatOut)[4], const float quatIn[4]);
+
+// store staes along with system time stamp in msces
+void StoreStates(uint64_t timestamp_ms);
+
+/**
+ * Recall the state vector.
+ *
+ * Recalls the vector stored at closest time to the one specified by msec
+ *
+ * @return zero on success, integer indicating the number of invalid states on failure.
+ * Does only copy valid states, if the statesForFusion vector was initialized
+ * correctly by the caller, the result can be safely used, but is a mixture
+ * time-wise where valid states were updated and invalid remained at the old
+ * value.
+ */
+int RecallStates(float *statesForFusion, uint64_t msec);
+
+void ResetStoredStates();
+
+void quat2Tbn(Mat3f &TBodyNed, const float (&quat)[4]);
+
+void calcEarthRateNED(Vector3f &omega, float latitude);
+
+static void eul2quat(float (&quat)[4], const float (&eul)[3]);
+
+static void quat2eul(float (&eul)[3], const float (&quat)[4]);
+
+static void calcvelNED(float (&velNED)[3], float gpsCourse, float gpsGndSpd, float gpsVelD);
+
+void calcposNED(float (&posNED)[3], double lat, double lon, float hgt, double latRef, double lonRef, float hgtRef);
+
+static void calcLLH(float posNED[3], double &lat, double &lon, float &hgt, double latRef, double lonRef, float hgtRef);
+
+static void quat2Tnb(Mat3f &Tnb, const float (&quat)[4]);
+
+static float sq(float valIn);
+
+static float maxf(float valIn1, float valIn2);
+
+static float min(float valIn1, float valIn2);
+
+void OnGroundCheck();
+
+void CovarianceInit();
+
+void InitialiseFilter(float (&initvelNED)[3], double referenceLat, double referenceLon, float referenceHgt, float declination);
+
+float ConstrainFloat(float val, float min, float max);
+
+void ConstrainVariances();
+
+void ConstrainStates();
+
+void ForceSymmetry();
+
+int CheckAndBound(struct ekf_status_report *last_error);
+
+void ResetPosition();
+
+void ResetVelocity();
+
+void ZeroVariables();
+
+void GetFilterState(struct ekf_status_report *state);
+
+void GetLastErrorState(struct ekf_status_report *last_error);
+
+bool StatesNaN();
+
+void InitializeDynamic(float (&initvelNED)[3], float declination);
+
+protected:
+
+void updateDtVelPosFilt(float dt);
+
+bool FilterHealthy();
+
+bool GyroOffsetsDiverged();
+
+bool VelNEDDiverged();
+
+void ResetHeight(void);
+
+void AttitudeInit(float ax, float ay, float az, float mx, float my, float mz, float declination, float *initQuat);
+
+};
+
+uint32_t millis();
+
+uint64_t getMicros();
+
diff --git a/src/modules/ekf_att_pos_estimator/estimator_utilities.cpp b/src/modules/ekf_att_pos_estimator/estimator_utilities.cpp
new file mode 100644
index 000000000..77cc1eeeb
--- /dev/null
+++ b/src/modules/ekf_att_pos_estimator/estimator_utilities.cpp
@@ -0,0 +1,162 @@
+
+#include "estimator_utilities.h"
+
+// Define EKF_DEBUG here to enable the debug print calls
+// if the macro is not set, these will be completely
+// optimized out by the compiler.
+//#define EKF_DEBUG
+
+#ifdef EKF_DEBUG
+#include <stdio.h>
+#include <stdarg.h>
+
+static void
+ekf_debug_print(const char *fmt, va_list args)
+{
+ fprintf(stderr, "%s: ", "[ekf]");
+ vfprintf(stderr, fmt, args);
+
+ fprintf(stderr, "\n");
+}
+
+void
+ekf_debug(const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ ekf_debug_print(fmt, args);
+}
+
+#else
+
+void ekf_debug(const char *fmt, ...) { while(0){} }
+#endif
+
+float Vector3f::length(void) const
+{
+ return sqrt(x*x + y*y + z*z);
+}
+
+void Vector3f::zero(void)
+{
+ x = 0.0f;
+ y = 0.0f;
+ z = 0.0f;
+}
+
+Mat3f::Mat3f() :
+ x{1.0f, 0.0f, 0.0f},
+ y{0.0f, 1.0f, 0.0f},
+ z{0.0f, 0.0f, 1.0f}
+{
+}
+
+void Mat3f::identity() {
+ x.x = 1.0f;
+ x.y = 0.0f;
+ x.z = 0.0f;
+
+ y.x = 0.0f;
+ y.y = 1.0f;
+ y.z = 0.0f;
+
+ z.x = 0.0f;
+ z.y = 0.0f;
+ z.z = 1.0f;
+}
+
+Mat3f Mat3f::transpose(void) const
+{
+ Mat3f ret = *this;
+ swap_var(ret.x.y, ret.y.x);
+ swap_var(ret.x.z, ret.z.x);
+ swap_var(ret.y.z, ret.z.y);
+ return ret;
+}
+
+// overload + operator to provide a vector addition
+Vector3f operator+( Vector3f vecIn1, Vector3f vecIn2)
+{
+ Vector3f vecOut;
+ vecOut.x = vecIn1.x + vecIn2.x;
+ vecOut.y = vecIn1.y + vecIn2.y;
+ vecOut.z = vecIn1.z + vecIn2.z;
+ return vecOut;
+}
+
+// overload - operator to provide a vector subtraction
+Vector3f operator-( Vector3f vecIn1, Vector3f vecIn2)
+{
+ Vector3f vecOut;
+ vecOut.x = vecIn1.x - vecIn2.x;
+ vecOut.y = vecIn1.y - vecIn2.y;
+ vecOut.z = vecIn1.z - vecIn2.z;
+ return vecOut;
+}
+
+// overload * operator to provide a matrix vector product
+Vector3f operator*( Mat3f matIn, Vector3f vecIn)
+{
+ Vector3f vecOut;
+ vecOut.x = matIn.x.x*vecIn.x + matIn.x.y*vecIn.y + matIn.x.z*vecIn.z;
+ vecOut.y = matIn.y.x*vecIn.x + matIn.y.y*vecIn.y + matIn.y.z*vecIn.z;
+ vecOut.z = matIn.x.x*vecIn.x + matIn.z.y*vecIn.y + matIn.z.z*vecIn.z;
+ return vecOut;
+}
+
+// overload * operator to provide a matrix product
+Mat3f operator*( Mat3f matIn1, Mat3f matIn2)
+{
+ Mat3f matOut;
+ matOut.x.x = matIn1.x.x*matIn2.x.x + matIn1.x.y*matIn2.y.x + matIn1.x.z*matIn2.z.x;
+ matOut.x.y = matIn1.x.x*matIn2.x.y + matIn1.x.y*matIn2.y.y + matIn1.x.z*matIn2.z.y;
+ matOut.x.z = matIn1.x.x*matIn2.x.z + matIn1.x.y*matIn2.y.z + matIn1.x.z*matIn2.z.z;
+
+ matOut.y.x = matIn1.y.x*matIn2.x.x + matIn1.y.y*matIn2.y.x + matIn1.y.z*matIn2.z.x;
+ matOut.y.y = matIn1.y.x*matIn2.x.y + matIn1.y.y*matIn2.y.y + matIn1.y.z*matIn2.z.y;
+ matOut.y.z = matIn1.y.x*matIn2.x.z + matIn1.y.y*matIn2.y.z + matIn1.y.z*matIn2.z.z;
+
+ matOut.z.x = matIn1.z.x*matIn2.x.x + matIn1.z.y*matIn2.y.x + matIn1.z.z*matIn2.z.x;
+ matOut.z.y = matIn1.z.x*matIn2.x.y + matIn1.z.y*matIn2.y.y + matIn1.z.z*matIn2.z.y;
+ matOut.z.z = matIn1.z.x*matIn2.x.z + matIn1.z.y*matIn2.y.z + matIn1.z.z*matIn2.z.z;
+
+ return matOut;
+}
+
+// overload % operator to provide a vector cross product
+Vector3f operator%( Vector3f vecIn1, Vector3f vecIn2)
+{
+ Vector3f vecOut;
+ vecOut.x = vecIn1.y*vecIn2.z - vecIn1.z*vecIn2.y;
+ vecOut.y = vecIn1.z*vecIn2.x - vecIn1.x*vecIn2.z;
+ vecOut.z = vecIn1.x*vecIn2.y - vecIn1.y*vecIn2.x;
+ return vecOut;
+}
+
+// overload * operator to provide a vector scaler product
+Vector3f operator*(Vector3f vecIn1, float sclIn1)
+{
+ Vector3f vecOut;
+ vecOut.x = vecIn1.x * sclIn1;
+ vecOut.y = vecIn1.y * sclIn1;
+ vecOut.z = vecIn1.z * sclIn1;
+ return vecOut;
+}
+
+// overload * operator to provide a vector scaler product
+Vector3f operator*(float sclIn1, Vector3f vecIn1)
+{
+ Vector3f vecOut;
+ vecOut.x = vecIn1.x * sclIn1;
+ vecOut.y = vecIn1.y * sclIn1;
+ vecOut.z = vecIn1.z * sclIn1;
+ return vecOut;
+}
+
+void swap_var(float &d1, float &d2)
+{
+ float tmp = d1;
+ d1 = d2;
+ d2 = tmp;
+}
diff --git a/src/modules/ekf_att_pos_estimator/estimator_utilities.h b/src/modules/ekf_att_pos_estimator/estimator_utilities.h
new file mode 100644
index 000000000..a6b670c4d
--- /dev/null
+++ b/src/modules/ekf_att_pos_estimator/estimator_utilities.h
@@ -0,0 +1,93 @@
+#include <math.h>
+#include <stdint.h>
+
+#pragma once
+
+#define GRAVITY_MSS 9.80665f
+#define deg2rad 0.017453292f
+#define rad2deg 57.295780f
+#define pi 3.141592657f
+#define earthRate 0.000072921f
+#define earthRadius 6378145.0
+#define earthRadiusInv 1.5678540e-7
+
+class Vector3f
+{
+private:
+public:
+ float x;
+ float y;
+ float z;
+
+ Vector3f(float a=0.0f, float b=0.0f, float c=0.0f) :
+ x(a),
+ y(b),
+ z(c)
+ {}
+
+ float length(void) const;
+ void zero(void);
+};
+
+class Mat3f
+{
+private:
+public:
+ Vector3f x;
+ Vector3f y;
+ Vector3f z;
+
+ Mat3f();
+
+ void identity();
+ Mat3f transpose(void) const;
+};
+
+Vector3f operator*(float sclIn1, Vector3f vecIn1);
+Vector3f operator+( Vector3f vecIn1, Vector3f vecIn2);
+Vector3f operator-( Vector3f vecIn1, Vector3f vecIn2);
+Vector3f operator*( Mat3f matIn, Vector3f vecIn);
+Mat3f operator*( Mat3f matIn1, Mat3f matIn2);
+Vector3f operator%( Vector3f vecIn1, Vector3f vecIn2);
+Vector3f operator*(Vector3f vecIn1, float sclIn1);
+
+void swap_var(float &d1, float &d2);
+
+enum GPS_FIX {
+ GPS_FIX_NOFIX = 0,
+ GPS_FIX_2D = 2,
+ GPS_FIX_3D = 3
+};
+
+struct ekf_status_report {
+ bool error;
+ bool velHealth;
+ bool posHealth;
+ bool hgtHealth;
+ bool velTimeout;
+ bool posTimeout;
+ bool hgtTimeout;
+ bool imuTimeout;
+ bool onGround;
+ bool staticMode;
+ bool useCompass;
+ bool useAirspeed;
+ uint32_t velFailTime;
+ uint32_t posFailTime;
+ uint32_t hgtFailTime;
+ float states[32];
+ unsigned n_states;
+ bool angNaN;
+ bool summedDelVelNaN;
+ bool KHNaN;
+ bool KHPNaN;
+ bool PNaN;
+ bool covarianceNaN;
+ bool kalmanGainsNaN;
+ bool statesNaN;
+ bool gyroOffsetsExcessive;
+ bool covariancesExcessive;
+ bool velOffsetExcessive;
+};
+
+void ekf_debug(const char *fmt, ...);
diff --git a/src/modules/ekf_att_pos_estimator/module.mk b/src/modules/ekf_att_pos_estimator/module.mk
index 6fefec2c2..36d854ddd 100644
--- a/src/modules/ekf_att_pos_estimator/module.mk
+++ b/src/modules/ekf_att_pos_estimator/module.mk
@@ -39,4 +39,7 @@ MODULE_COMMAND = ekf_att_pos_estimator
SRCS = ekf_att_pos_estimator_main.cpp \
ekf_att_pos_estimator_params.c \
- estimator.cpp
+ estimator_23states.cpp \
+ estimator_utilities.cpp
+
+EXTRACXXFLAGS = -Weffc++
diff --git a/src/modules/fw_att_control/fw_att_control_main.cpp b/src/modules/fw_att_control/fw_att_control_main.cpp
index 178b590ae..e770c11a2 100644
--- a/src/modules/fw_att_control/fw_att_control_main.cpp
+++ b/src/modules/fw_att_control/fw_att_control_main.cpp
@@ -1,7 +1,6 @@
/****************************************************************************
*
- * Copyright (c) 2013 PX4 Development Team. All rights reserved.
- * Author: Lorenz Meier
+ * Copyright (c) 2013, 2014 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -64,6 +63,7 @@
#include <uORB/topics/vehicle_control_mode.h>
#include <uORB/topics/parameter_update.h>
#include <uORB/topics/vehicle_global_position.h>
+#include <uORB/topics/vehicle_status.h>
#include <systemlib/param/param.h>
#include <systemlib/err.h>
#include <systemlib/pid/pid.h>
@@ -99,13 +99,21 @@ public:
/**
* Start the sensors task.
*
- * @return OK on success.
+ * @return OK on success.
*/
int start();
+ /**
+ * Task status
+ *
+ * @return true if the mainloop is running
+ */
+ bool task_running() { return _task_running; }
+
private:
bool _task_should_exit; /**< if true, sensor task should exit */
+ bool _task_running; /**< if true, task is running in its mainloop */
int _control_task; /**< task handle for sensor task */
int _att_sub; /**< vehicle attitude subscription */
@@ -117,6 +125,7 @@ private:
int _params_sub; /**< notification of parameter updates */
int _manual_sub; /**< notification of manual control updates */
int _global_pos_sub; /**< global position subscription */
+ int _vehicle_status_sub; /**< vehicle status subscription */
orb_advert_t _rate_sp_pub; /**< rate setpoint publication */
orb_advert_t _attitude_sp_pub; /**< attitude setpoint point */
@@ -132,12 +141,14 @@ private:
struct actuator_controls_s _actuators; /**< actuator control inputs */
struct actuator_controls_s _actuators_airframe; /**< actuator control inputs */
struct vehicle_global_position_s _global_pos; /**< global position */
+ struct vehicle_status_s _vehicle_status; /**< vehicle status */
perf_counter_t _loop_perf; /**< loop performance counter */
perf_counter_t _nonfinite_input_perf; /**< performance counter for non finite input */
perf_counter_t _nonfinite_output_perf; /**< performance counter for non finite output */
bool _setpoint_valid; /**< flag if the position control setpoint is valid */
+ bool _debug; /**< if set to true, print debug output */
struct {
float tconst;
@@ -268,6 +279,11 @@ private:
void global_pos_poll();
/**
+ * Check for vehicle status updates.
+ */
+ void vehicle_status_poll();
+
+ /**
* Shim for calling task_main from task_create.
*/
static void task_main_trampoline(int argc, char *argv[]);
@@ -276,6 +292,7 @@ private:
* Main sensor collection task.
*/
void task_main();
+
};
namespace att_control
@@ -287,12 +304,13 @@ namespace att_control
#endif
static const int ERROR = -1;
-FixedwingAttitudeControl *g_control;
+FixedwingAttitudeControl *g_control = nullptr;
}
FixedwingAttitudeControl::FixedwingAttitudeControl() :
_task_should_exit(false),
+ _task_running(false),
_control_task(-1),
/* subscriptions */
@@ -303,6 +321,7 @@ FixedwingAttitudeControl::FixedwingAttitudeControl() :
_params_sub(-1),
_manual_sub(-1),
_global_pos_sub(-1),
+ _vehicle_status_sub(-1),
/* publications */
_rate_sp_pub(-1),
@@ -315,7 +334,8 @@ FixedwingAttitudeControl::FixedwingAttitudeControl() :
_nonfinite_input_perf(perf_alloc(PC_COUNT, "fw att control nonfinite input")),
_nonfinite_output_perf(perf_alloc(PC_COUNT, "fw att control nonfinite output")),
/* states */
- _setpoint_valid(false)
+ _setpoint_valid(false),
+ _debug(false)
{
/* safely initialize structs */
_att = {};
@@ -327,6 +347,7 @@ FixedwingAttitudeControl::FixedwingAttitudeControl() :
_actuators = {};
_actuators_airframe = {};
_global_pos = {};
+ _vehicle_status = {};
_parameter_handles.tconst = param_find("FW_ATT_TC");
@@ -520,7 +541,7 @@ FixedwingAttitudeControl::vehicle_accel_poll()
orb_check(_accel_sub, &accel_updated);
if (accel_updated) {
- orb_copy(ORB_ID(sensor_accel), _accel_sub, &_accel);
+ orb_copy(ORB_ID(sensor_accel0), _accel_sub, &_accel);
}
}
@@ -550,6 +571,18 @@ FixedwingAttitudeControl::global_pos_poll()
}
void
+FixedwingAttitudeControl::vehicle_status_poll()
+{
+ /* check if there is new status information */
+ bool vehicle_status_updated;
+ orb_check(_vehicle_status_sub, &vehicle_status_updated);
+
+ if (vehicle_status_updated) {
+ orb_copy(ORB_ID(vehicle_status), _vehicle_status_sub, &_vehicle_status);
+ }
+}
+
+void
FixedwingAttitudeControl::task_main_trampoline(int argc, char *argv[])
{
att_control::g_control->task_main();
@@ -568,12 +601,13 @@ FixedwingAttitudeControl::task_main()
*/
_att_sp_sub = orb_subscribe(ORB_ID(vehicle_attitude_setpoint));
_att_sub = orb_subscribe(ORB_ID(vehicle_attitude));
- _accel_sub = orb_subscribe(ORB_ID(sensor_accel));
+ _accel_sub = orb_subscribe(ORB_ID(sensor_accel0));
_airspeed_sub = orb_subscribe(ORB_ID(airspeed));
_vcontrol_mode_sub = orb_subscribe(ORB_ID(vehicle_control_mode));
_params_sub = orb_subscribe(ORB_ID(parameter_update));
_manual_sub = orb_subscribe(ORB_ID(manual_control_setpoint));
_global_pos_sub = orb_subscribe(ORB_ID(vehicle_global_position));
+ _vehicle_status_sub = orb_subscribe(ORB_ID(vehicle_status));
/* rate limit vehicle status updates to 5Hz */
orb_set_interval(_vcontrol_mode_sub, 200);
@@ -588,6 +622,7 @@ FixedwingAttitudeControl::task_main()
vehicle_accel_poll();
vehicle_control_mode_poll();
vehicle_manual_poll();
+ vehicle_status_poll();
/* wakeup source(s) */
struct pollfd fds[2];
@@ -598,6 +633,8 @@ FixedwingAttitudeControl::task_main()
fds[1].fd = _att_sub;
fds[1].events = POLLIN;
+ _task_running = true;
+
while (!_task_should_exit) {
static int loop_counter = 0;
@@ -654,6 +691,8 @@ FixedwingAttitudeControl::task_main()
global_pos_poll();
+ vehicle_status_poll();
+
/* lock integrator until control is started */
bool lock_integrator;
@@ -689,7 +728,8 @@ FixedwingAttitudeControl::task_main()
perf_count(_nonfinite_input_perf);
}
} else {
- airspeed = _airspeed.true_airspeed_m_s;
+ /* prevent numerical drama by requiring 0.5 m/s minimal speed */
+ airspeed = math::max(0.5f, _airspeed.true_airspeed_m_s);
}
/*
@@ -706,15 +746,29 @@ FixedwingAttitudeControl::task_main()
float pitch_sp = _parameters.pitchsp_offset_rad;
float throttle_sp = 0.0f;
- if (_vcontrol_mode.flag_control_velocity_enabled || _vcontrol_mode.flag_control_position_enabled) {
+ /* Read attitude setpoint from uorb if
+ * - velocity control or position control is enabled (pos controller is running)
+ * - manual control is disabled (another app may send the setpoint, but it should
+ * for sure not be set from the remote control values)
+ */
+ if (_vcontrol_mode.flag_control_velocity_enabled ||
+ _vcontrol_mode.flag_control_position_enabled ||
+ !_vcontrol_mode.flag_control_manual_enabled) {
+ /* read in attitude setpoint from attitude setpoint uorb topic */
roll_sp = _att_sp.roll_body + _parameters.rollsp_offset_rad;
pitch_sp = _att_sp.pitch_body + _parameters.pitchsp_offset_rad;
throttle_sp = _att_sp.thrust;
/* reset integrals where needed */
- if (_att_sp.roll_reset_integral)
+ if (_att_sp.roll_reset_integral) {
_roll_ctrl.reset_integrator();
-
+ }
+ if (_att_sp.pitch_reset_integral) {
+ _pitch_ctrl.reset_integrator();
+ }
+ if (_att_sp.yaw_reset_integral) {
+ _yaw_ctrl.reset_integrator();
+ }
} else {
/*
* Scale down roll and pitch as the setpoints are radians
@@ -758,6 +812,13 @@ FixedwingAttitudeControl::task_main()
}
}
+ /* If the aircraft is on ground reset the integrators */
+ if (_vehicle_status.condition_landed) {
+ _roll_ctrl.reset_integrator();
+ _pitch_ctrl.reset_integrator();
+ _yaw_ctrl.reset_integrator();
+ }
+
/* Prepare speed_body_u and speed_body_w */
float speed_body_u = 0.0f;
float speed_body_v = 0.0f;
@@ -767,7 +828,7 @@ FixedwingAttitudeControl::task_main()
speed_body_v = _att.R[0][1] * _global_pos.vel_n + _att.R[1][1] * _global_pos.vel_e + _att.R[2][1] * _global_pos.vel_d;
speed_body_w = _att.R[0][2] * _global_pos.vel_n + _att.R[1][2] * _global_pos.vel_e + _att.R[2][2] * _global_pos.vel_d;
} else {
- if (loop_counter % 10 == 0) {
+ if (_debug && loop_counter % 10 == 0) {
warnx("Did not get a valid R\n");
}
}
@@ -790,7 +851,7 @@ FixedwingAttitudeControl::task_main()
_roll_ctrl.reset_integrator();
perf_count(_nonfinite_output_perf);
- if (loop_counter % 10 == 0) {
+ if (_debug && loop_counter % 10 == 0) {
warnx("roll_u %.4f", (double)roll_u);
}
}
@@ -803,7 +864,7 @@ FixedwingAttitudeControl::task_main()
if (!isfinite(pitch_u)) {
_pitch_ctrl.reset_integrator();
perf_count(_nonfinite_output_perf);
- if (loop_counter % 10 == 0) {
+ if (_debug && loop_counter % 10 == 0) {
warnx("pitch_u %.4f, _yaw_ctrl.get_desired_rate() %.4f,"
" airspeed %.4f, airspeed_scaling %.4f,"
" roll_sp %.4f, pitch_sp %.4f,"
@@ -827,21 +888,25 @@ FixedwingAttitudeControl::task_main()
if (!isfinite(yaw_u)) {
_yaw_ctrl.reset_integrator();
perf_count(_nonfinite_output_perf);
- if (loop_counter % 10 == 0) {
+ if (_debug && loop_counter % 10 == 0) {
warnx("yaw_u %.4f", (double)yaw_u);
}
}
- /* throttle passed through */
- _actuators.control[3] = (isfinite(throttle_sp)) ? throttle_sp : 0.0f;
+ /* throttle passed through if it is finite and if no engine failure was
+ * detected */
+ _actuators.control[3] = (isfinite(throttle_sp) &&
+ !(_vehicle_status.engine_failure ||
+ _vehicle_status.engine_failure_cmd)) ?
+ throttle_sp : 0.0f;
if (!isfinite(throttle_sp)) {
- if (loop_counter % 10 == 0) {
+ if (_debug && loop_counter % 10 == 0) {
warnx("throttle_sp %.4f", (double)throttle_sp);
}
}
} else {
perf_count(_nonfinite_input_perf);
- if (loop_counter % 10 == 0) {
+ if (_debug && loop_counter % 10 == 0) {
warnx("Non-finite setpoint roll_sp: %.4f, pitch_sp %.4f", (double)roll_sp, (double)pitch_sp);
}
}
@@ -914,6 +979,7 @@ FixedwingAttitudeControl::task_main()
warnx("exiting.\n");
_control_task = -1;
+ _task_running = false;
_exit(0);
}
@@ -959,6 +1025,14 @@ int fw_att_control_main(int argc, char *argv[])
err(1, "start failed");
}
+ /* avoid memory fragmentation by not exiting start handler until the task has fully started */
+ while (att_control::g_control == nullptr || !att_control::g_control->task_running()) {
+ usleep(50000);
+ printf(".");
+ fflush(stdout);
+ }
+ printf("\n");
+
exit(0);
}
diff --git a/src/modules/fw_att_control/module.mk b/src/modules/fw_att_control/module.mk
index 1e600757e..89c6494c5 100644
--- a/src/modules/fw_att_control/module.mk
+++ b/src/modules/fw_att_control/module.mk
@@ -39,3 +39,5 @@ MODULE_COMMAND = fw_att_control
SRCS = fw_att_control_main.cpp \
fw_att_control_params.c
+
+MODULE_STACKSIZE = 1200
diff --git a/src/modules/fw_pos_control_l1/fw_pos_control_l1_main.cpp b/src/modules/fw_pos_control_l1/fw_pos_control_l1_main.cpp
index 116d3cc63..6017369aa 100644
--- a/src/modules/fw_pos_control_l1/fw_pos_control_l1_main.cpp
+++ b/src/modules/fw_pos_control_l1/fw_pos_control_l1_main.cpp
@@ -1,7 +1,6 @@
/****************************************************************************
*
- * Copyright (c) 2013 PX4 Development Team. All rights reserved.
- * Author: Lorenz Meier
+ * Copyright (c) 2013, 2014 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -89,8 +88,10 @@
#include <launchdetection/LaunchDetector.h>
#include <ecl/l1/ecl_l1_pos_controller.h>
#include <external_lgpl/tecs/tecs.h>
-#include <drivers/drv_range_finder.h>
#include "landingslope.h"
+#include "mtecs/mTecs.h"
+
+static int _control_task = -1; /**< task handle for sensor task */
/**
@@ -100,6 +101,8 @@
*/
extern "C" __EXPORT int fw_pos_control_l1_main(int argc, char *argv[]);
+using namespace launchdetection;
+
class FixedwingPositionControl
{
public:
@@ -116,28 +119,35 @@ public:
/**
* Start the sensors task.
*
- * @return OK on success.
+ * @return OK on success.
+ */
+ static int start();
+
+ /**
+ * Task status
+ *
+ * @return true if the mainloop is running
*/
- int start();
+ bool task_running() { return _task_running; }
private:
int _mavlink_fd;
bool _task_should_exit; /**< if true, sensor task should exit */
- int _control_task; /**< task handle for sensor task */
+ bool _task_running; /**< if true, task is running in its mainloop */
int _global_pos_sub;
int _pos_sp_triplet_sub;
int _att_sub; /**< vehicle attitude subscription */
- int _attitude_sub; /**< raw rc channels data subscription */
int _airspeed_sub; /**< airspeed subscription */
- int _control_mode_sub; /**< vehicle status subscription */
+ int _control_mode_sub; /**< control mode subscription */
+ int _vehicle_status_sub; /**< vehicle status subscription */
int _params_sub; /**< notification of parameter updates */
int _manual_control_sub; /**< notification of manual control updates */
int _sensor_combined_sub; /**< for body frame accelerations */
- int _range_finder_sub; /**< range finder subscription */
orb_advert_t _attitude_sp_pub; /**< attitude setpoint */
+ orb_advert_t _tecs_status_pub; /**< TECS status publication */
orb_advert_t _nav_capabilities_pub; /**< navigation capabilities publication */
struct vehicle_attitude_s _att; /**< vehicle attitude */
@@ -145,46 +155,31 @@ private:
struct navigation_capabilities_s _nav_capabilities; /**< navigation capabilities */
struct manual_control_setpoint_s _manual; /**< r/c channel data */
struct airspeed_s _airspeed; /**< airspeed */
- struct vehicle_control_mode_s _control_mode; /**< vehicle status */
+ struct vehicle_control_mode_s _control_mode; /**< control mode */
+ struct vehicle_status_s _vehicle_status; /**< vehicle status */
struct vehicle_global_position_s _global_pos; /**< global vehicle position */
struct position_setpoint_triplet_s _pos_sp_triplet; /**< triplet of mission items */
struct sensor_combined_s _sensor_combined; /**< for body frame accelerations */
- struct range_finder_report _range_finder; /**< range finder report */
perf_counter_t _loop_perf; /**< loop performance counter */
- bool _setpoint_valid; /**< flag if the position control setpoint is valid */
-
- /** manual control states */
- float _altctrl_hold_heading; /**< heading the system should hold in altctrl mode */
- double _loiter_hold_lat;
- double _loiter_hold_lon;
- float _loiter_hold_alt;
- bool _loiter_hold;
-
- double _launch_lat;
- double _launch_lon;
- float _launch_alt;
- bool _launch_valid;
-
/* land states */
- /* not in non-abort mode for landing yet */
bool land_noreturn_horizontal;
bool land_noreturn_vertical;
bool land_stayonground;
bool land_motor_lim;
bool land_onslope;
+ bool land_useterrain;
/* takeoff/launch states */
- bool launch_detected;
- bool usePreTakeoffThrust;
+ LaunchDetectionResult launch_detection_state;
bool last_manual; ///< true if the last iteration was in manual mode (used to determine when a reset is needed)
/* Landingslope object */
Landingslope landingslope;
-
float flare_curve_alt_rel_last;
+
/* heading hold */
float target_bearing;
@@ -201,15 +196,22 @@ private:
ECL_L1_Pos_Controller _l1_control;
TECS _tecs;
+ fwPosctrl::mTecs _mTecs;
+ bool _was_pos_control_mode;
struct {
float l1_period;
float l1_damping;
float time_const;
+ float time_const_throt;
float min_sink_rate;
float max_sink_rate;
float max_climb_rate;
+ float climbout_diff;
+ float heightrate_p;
+ float heightrate_ff;
+ float speedrate_p;
float throttle_damp;
float integrator_gain;
float vertical_accel_limit;
@@ -229,18 +231,18 @@ private:
float throttle_min;
float throttle_max;
float throttle_cruise;
+ float throttle_slew_max;
float throttle_land_max;
- float heightrate_p;
- float speedrate_p;
-
float land_slope_angle;
float land_H1_virt;
float land_flare_alt_relative;
float land_thrust_lim_alt_relative;
float land_heading_hold_horizontal_distance;
- float range_finder_rel_alt;
+ float land_flare_pitch_min_deg;
+ float land_flare_pitch_max_deg;
+ int land_use_terrain_estimate;
} _parameters; /**< local copies of interesting parameters */
@@ -250,9 +252,14 @@ private:
param_t l1_damping;
param_t time_const;
+ param_t time_const_throt;
param_t min_sink_rate;
param_t max_sink_rate;
param_t max_climb_rate;
+ param_t climbout_diff;
+ param_t heightrate_p;
+ param_t heightrate_ff;
+ param_t speedrate_p;
param_t throttle_damp;
param_t integrator_gain;
param_t vertical_accel_limit;
@@ -272,18 +279,18 @@ private:
param_t throttle_min;
param_t throttle_max;
param_t throttle_cruise;
+ param_t throttle_slew_max;
param_t throttle_land_max;
- param_t heightrate_p;
- param_t speedrate_p;
-
param_t land_slope_angle;
param_t land_H1_virt;
param_t land_flare_alt_relative;
param_t land_thrust_lim_alt_relative;
param_t land_heading_hold_horizontal_distance;
- param_t range_finder_rel_alt;
+ param_t land_flare_pitch_min_deg;
+ param_t land_flare_pitch_max_deg;
+ param_t land_use_terrain_estimate;
} _parameter_handles; /**< handles for interesting parameters */
@@ -300,20 +307,19 @@ private:
void control_update();
/**
- * Check for changes in vehicle status.
+ * Check for changes in control mode
*/
void vehicle_control_mode_poll();
/**
- * Check for airspeed updates.
+ * Check for changes in vehicle status.
*/
- bool vehicle_airspeed_poll();
+ void vehicle_status_poll();
/**
- * Check for range finder updates.
+ * Check for airspeed updates.
*/
- bool range_finder_poll();
-
+ bool vehicle_airspeed_poll();
/**
* Check for position updates.
@@ -336,18 +342,18 @@ private:
void navigation_capabilities_publish();
/**
- * Get the relative alt either from the difference between estimate and waypoint or from the laser range finder
+ * Return the terrain estimate during landing: uses the wp altitude value or the terrain estimate if available
*/
- float get_relative_landingalt(float land_setpoint_alt, float current_alt, const struct range_finder_report &range_finder, float range_finder_use_relative_alt);
+ float get_terrain_altitude_landing(float land_setpoint_alt, const struct vehicle_global_position_s &global_pos);
/**
* Control position.
*/
- bool control_position(const math::Vector<2> &global_pos, const math::Vector<2> &ground_speed,
+ bool control_position(const math::Vector<2> &global_pos, const math::Vector<3> &ground_speed,
const struct position_setpoint_triplet_s &_pos_sp_triplet);
float calculate_target_airspeed(float airspeed_demand);
- void calculate_gndspeed_undershoot(const math::Vector<2> &current_position, const math::Vector<2> &ground_speed, const struct position_setpoint_triplet_s &pos_sp_triplet);
+ void calculate_gndspeed_undershoot(const math::Vector<2> &current_position, const math::Vector<2> &ground_speed_2d, const struct position_setpoint_triplet_s &pos_sp_triplet);
/**
* Shim for calling task_main from task_create.
@@ -368,6 +374,20 @@ private:
* Reset landing state
*/
void reset_landing_state();
+
+ /*
+ * Call TECS : a wrapper function to call one of the TECS implementations (mTECS is called only if enabled via parameter)
+ * XXX need to clean up/remove this function once mtecs fully replaces TECS
+ */
+ void tecs_update_pitch_throttle(float alt_sp, float v_sp, float eas2tas,
+ float pitch_min_rad, float pitch_max_rad,
+ float throttle_min, float throttle_max, float throttle_cruise,
+ bool climbout_mode, float climbout_pitch_min_rad,
+ float altitude,
+ const math::Vector<3> &ground_speed,
+ tecs_mode mode = TECS_MODE_NORMAL,
+ bool pitch_max_special = false);
+
};
namespace l1_control
@@ -379,14 +399,14 @@ namespace l1_control
#endif
static const int ERROR = -1;
-FixedwingPositionControl *g_control;
+FixedwingPositionControl *g_control = nullptr;
}
FixedwingPositionControl::FixedwingPositionControl() :
_mavlink_fd(-1),
_task_should_exit(false),
- _control_task(-1),
+ _task_running(false),
/* subscriptions */
_global_pos_sub(-1),
@@ -394,44 +414,51 @@ FixedwingPositionControl::FixedwingPositionControl() :
_att_sub(-1),
_airspeed_sub(-1),
_control_mode_sub(-1),
+ _vehicle_status_sub(-1),
_params_sub(-1),
_manual_control_sub(-1),
- _range_finder_sub(-1),
+ _sensor_combined_sub(-1),
/* publications */
_attitude_sp_pub(-1),
+ _tecs_status_pub(-1),
_nav_capabilities_pub(-1),
+/* states */
+ _att(),
+ _att_sp(),
+ _nav_capabilities(),
+ _manual(),
+ _airspeed(),
+ _control_mode(),
+ _vehicle_status(),
+ _global_pos(),
+ _pos_sp_triplet(),
+ _sensor_combined(),
+
/* performance counters */
_loop_perf(perf_alloc(PC_ELAPSED, "fw l1 control")),
-/* states */
- _setpoint_valid(false),
- _loiter_hold(false),
land_noreturn_horizontal(false),
land_noreturn_vertical(false),
land_stayonground(false),
land_motor_lim(false),
land_onslope(false),
- launch_detected(false),
- usePreTakeoffThrust(false),
+ land_useterrain(false),
+ launch_detection_state(LAUNCHDETECTION_RES_NONE),
last_manual(false),
+ landingslope(),
flare_curve_alt_rel_last(0.0f),
+ target_bearing(0.0f),
launchDetector(),
_airspeed_error(0.0f),
_airspeed_valid(false),
+ _airspeed_last_valid(0),
_groundspeed_undershoot(0.0f),
_global_pos_valid(false),
- _att(),
- _att_sp(),
- _nav_capabilities(),
- _manual(),
- _airspeed(),
- _control_mode(),
- _global_pos(),
- _pos_sp_triplet(),
- _sensor_combined(),
- _range_finder()
+ _l1_control(),
+ _mTecs(),
+ _was_pos_control_mode(false)
{
_nav_capabilities.turn_distance = 0.0f;
@@ -447,6 +474,7 @@ FixedwingPositionControl::FixedwingPositionControl() :
_parameter_handles.roll_limit = param_find("FW_R_LIM");
_parameter_handles.throttle_min = param_find("FW_THR_MIN");
_parameter_handles.throttle_max = param_find("FW_THR_MAX");
+ _parameter_handles.throttle_slew_max = param_find("FW_THR_SLEW_MAX");
_parameter_handles.throttle_cruise = param_find("FW_THR_CRUISE");
_parameter_handles.throttle_land_max = param_find("FW_THR_LND_MAX");
@@ -455,12 +483,16 @@ FixedwingPositionControl::FixedwingPositionControl() :
_parameter_handles.land_flare_alt_relative = param_find("FW_LND_FLALT");
_parameter_handles.land_thrust_lim_alt_relative = param_find("FW_LND_TLALT");
_parameter_handles.land_heading_hold_horizontal_distance = param_find("FW_LND_HHDIST");
- _parameter_handles.range_finder_rel_alt = param_find("FW_LND_RFRALT");
+ _parameter_handles.land_flare_pitch_min_deg = param_find("FW_FLARE_PMIN");
+ _parameter_handles.land_flare_pitch_max_deg = param_find("FW_FLARE_PMAX");
+ _parameter_handles.land_use_terrain_estimate= param_find("FW_LND_USETER");
_parameter_handles.time_const = param_find("FW_T_TIME_CONST");
+ _parameter_handles.time_const_throt = param_find("FW_T_THRO_CONST");
_parameter_handles.min_sink_rate = param_find("FW_T_SINK_MIN");
_parameter_handles.max_sink_rate = param_find("FW_T_SINK_MAX");
_parameter_handles.max_climb_rate = param_find("FW_T_CLMB_MAX");
+ _parameter_handles.climbout_diff = param_find("FW_CLMBOUT_DIFF");
_parameter_handles.throttle_damp = param_find("FW_T_THR_DAMP");
_parameter_handles.integrator_gain = param_find("FW_T_INTEG_GAIN");
_parameter_handles.vertical_accel_limit = param_find("FW_T_VERT_ACC");
@@ -470,6 +502,7 @@ FixedwingPositionControl::FixedwingPositionControl() :
_parameter_handles.speed_weight = param_find("FW_T_SPDWEIGHT");
_parameter_handles.pitch_damping = param_find("FW_T_PTCH_DAMP");
_parameter_handles.heightrate_p = param_find("FW_T_HRATE_P");
+ _parameter_handles.heightrate_ff = param_find("FW_T_HRATE_FF");
_parameter_handles.speedrate_p = param_find("FW_T_SRATE_P");
/* fetch initial parameter values */
@@ -519,10 +552,12 @@ FixedwingPositionControl::parameters_update()
param_get(_parameter_handles.throttle_min, &(_parameters.throttle_min));
param_get(_parameter_handles.throttle_max, &(_parameters.throttle_max));
param_get(_parameter_handles.throttle_cruise, &(_parameters.throttle_cruise));
+ param_get(_parameter_handles.throttle_slew_max, &(_parameters.throttle_slew_max));
param_get(_parameter_handles.throttle_land_max, &(_parameters.throttle_land_max));
param_get(_parameter_handles.time_const, &(_parameters.time_const));
+ param_get(_parameter_handles.time_const_throt, &(_parameters.time_const_throt));
param_get(_parameter_handles.min_sink_rate, &(_parameters.min_sink_rate));
param_get(_parameter_handles.max_sink_rate, &(_parameters.max_sink_rate));
param_get(_parameter_handles.throttle_damp, &(_parameters.throttle_damp));
@@ -534,26 +569,37 @@ FixedwingPositionControl::parameters_update()
param_get(_parameter_handles.speed_weight, &(_parameters.speed_weight));
param_get(_parameter_handles.pitch_damping, &(_parameters.pitch_damping));
param_get(_parameter_handles.max_climb_rate, &(_parameters.max_climb_rate));
+ param_get(_parameter_handles.climbout_diff, &(_parameters.climbout_diff));
param_get(_parameter_handles.heightrate_p, &(_parameters.heightrate_p));
+ param_get(_parameter_handles.heightrate_ff, &(_parameters.heightrate_ff));
param_get(_parameter_handles.speedrate_p, &(_parameters.speedrate_p));
param_get(_parameter_handles.land_slope_angle, &(_parameters.land_slope_angle));
param_get(_parameter_handles.land_H1_virt, &(_parameters.land_H1_virt));
param_get(_parameter_handles.land_flare_alt_relative, &(_parameters.land_flare_alt_relative));
param_get(_parameter_handles.land_thrust_lim_alt_relative, &(_parameters.land_thrust_lim_alt_relative));
- param_get(_parameter_handles.land_heading_hold_horizontal_distance, &(_parameters.land_heading_hold_horizontal_distance));
- param_get(_parameter_handles.range_finder_rel_alt, &(_parameters.range_finder_rel_alt));
+ /* check if negative value for 2/3 of flare altitude is set for throttle cut */
+ if (_parameters.land_thrust_lim_alt_relative < 0.0f) {
+ _parameters.land_thrust_lim_alt_relative = 0.66f * _parameters.land_flare_alt_relative;
+ }
+
+ param_get(_parameter_handles.land_heading_hold_horizontal_distance, &(_parameters.land_heading_hold_horizontal_distance));
+ param_get(_parameter_handles.land_flare_pitch_min_deg, &(_parameters.land_flare_pitch_min_deg));
+ param_get(_parameter_handles.land_flare_pitch_max_deg, &(_parameters.land_flare_pitch_max_deg));
+ param_get(_parameter_handles.land_use_terrain_estimate, &(_parameters.land_use_terrain_estimate));
_l1_control.set_l1_damping(_parameters.l1_damping);
_l1_control.set_l1_period(_parameters.l1_period);
_l1_control.set_l1_roll_limit(math::radians(_parameters.roll_limit));
_tecs.set_time_const(_parameters.time_const);
+ _tecs.set_time_const_throt(_parameters.time_const_throt);
_tecs.set_min_sink_rate(_parameters.min_sink_rate);
_tecs.set_max_sink_rate(_parameters.max_sink_rate);
_tecs.set_throttle_damp(_parameters.throttle_damp);
+ _tecs.set_throttle_slewrate(_parameters.throttle_slew_max);
_tecs.set_integrator_gain(_parameters.integrator_gain);
_tecs.set_vertical_accel_limit(_parameters.vertical_accel_limit);
_tecs.set_height_comp_filter_omega(_parameters.height_comp_filter_omega);
@@ -565,6 +611,7 @@ FixedwingPositionControl::parameters_update()
_tecs.set_indicated_airspeed_max(_parameters.airspeed_max);
_tecs.set_max_climb_rate(_parameters.max_climb_rate);
_tecs.set_heightrate_p(_parameters.heightrate_p);
+ _tecs.set_heightrate_ff(_parameters.heightrate_ff);
_tecs.set_speedrate_p(_parameters.speedrate_p);
/* sanity check parameters */
@@ -589,29 +636,33 @@ FixedwingPositionControl::parameters_update()
/* Update Launch Detector Parameters */
launchDetector.updateParams();
+ /* Update the mTecs */
+ _mTecs.updateParams();
+
return OK;
}
void
FixedwingPositionControl::vehicle_control_mode_poll()
{
- bool vstatus_updated;
+ bool updated;
- /* Check HIL state if vehicle status has changed */
- orb_check(_control_mode_sub, &vstatus_updated);
+ orb_check(_control_mode_sub, &updated);
- if (vstatus_updated) {
+ if (updated) {
+ orb_copy(ORB_ID(vehicle_control_mode), _control_mode_sub, &_control_mode);
+ }
+}
- bool was_armed = _control_mode.flag_armed;
+void
+FixedwingPositionControl::vehicle_status_poll()
+{
+ bool updated;
- orb_copy(ORB_ID(vehicle_control_mode), _control_mode_sub, &_control_mode);
+ orb_check(_vehicle_status_sub, &updated);
- if (!was_armed && _control_mode.flag_armed) {
- _launch_lat = _global_pos.lat;
- _launch_lon = _global_pos.lon;
- _launch_alt = _global_pos.alt;
- _launch_valid = true;
- }
+ if (updated) {
+ orb_copy(ORB_ID(vehicle_status), _vehicle_status_sub, &_vehicle_status);
}
}
@@ -641,20 +692,6 @@ FixedwingPositionControl::vehicle_airspeed_poll()
return airspeed_updated;
}
-bool
-FixedwingPositionControl::range_finder_poll()
-{
- /* check if there is a range finder measurement */
- bool range_finder_updated;
- orb_check(_range_finder_sub, &range_finder_updated);
-
- if (range_finder_updated) {
- orb_copy(ORB_ID(sensor_range_finder), _range_finder_sub, &_range_finder);
- }
-
- return range_finder_updated;
-}
-
void
FixedwingPositionControl::vehicle_attitude_poll()
{
@@ -692,14 +729,23 @@ FixedwingPositionControl::vehicle_setpoint_poll()
if (pos_sp_triplet_updated) {
orb_copy(ORB_ID(position_setpoint_triplet), _pos_sp_triplet_sub, &_pos_sp_triplet);
- _setpoint_valid = true;
}
}
void
FixedwingPositionControl::task_main_trampoline(int argc, char *argv[])
{
+ l1_control::g_control = new FixedwingPositionControl();
+
+ if (l1_control::g_control == nullptr) {
+ warnx("OUT OF MEM");
+ return;
+ }
+
+ /* only returns on exit */
l1_control::g_control->task_main();
+ delete l1_control::g_control;
+ l1_control::g_control = nullptr;
}
float
@@ -734,15 +780,15 @@ FixedwingPositionControl::calculate_target_airspeed(float airspeed_demand)
}
void
-FixedwingPositionControl::calculate_gndspeed_undershoot(const math::Vector<2> &current_position, const math::Vector<2> &ground_speed, const struct position_setpoint_triplet_s &pos_sp_triplet)
+FixedwingPositionControl::calculate_gndspeed_undershoot(const math::Vector<2> &current_position, const math::Vector<2> &ground_speed_2d, const struct position_setpoint_triplet_s &pos_sp_triplet)
{
- if (_global_pos_valid && !(pos_sp_triplet.current.type == SETPOINT_TYPE_LOITER)) {
+ if (pos_sp_triplet.current.valid && !(pos_sp_triplet.current.type == SETPOINT_TYPE_LOITER)) {
/* rotate ground speed vector with current attitude */
math::Vector<2> yaw_vector(_R_nb(0, 0), _R_nb(1, 0));
yaw_vector.normalize();
- float ground_speed_body = yaw_vector * ground_speed;
+ float ground_speed_body = yaw_vector * ground_speed_2d;
/* The minimum desired ground speed is the minimum airspeed projected on to the ground using the altitude and horizontal difference between the waypoints if available*/
float distance = 0.0f;
@@ -783,41 +829,45 @@ void FixedwingPositionControl::navigation_capabilities_publish()
}
}
-float FixedwingPositionControl::get_relative_landingalt(float land_setpoint_alt, float current_alt, const struct range_finder_report &range_finder, float range_finder_use_relative_alt)
+float FixedwingPositionControl::get_terrain_altitude_landing(float land_setpoint_alt, const struct vehicle_global_position_s &global_pos)
{
- float rel_alt_estimated = current_alt - land_setpoint_alt;
-
- /* only use range finder if:
- * parameter (range_finder_use_relative_alt) > 0
- * the measurement is valid
- * the estimated relative altitude (from global altitude estimate and landing waypoint) <= range_finder_use_relative_alt
- */
- if (range_finder_use_relative_alt < 0 || !range_finder.valid || rel_alt_estimated > range_finder_use_relative_alt ) {
- return rel_alt_estimated;
+ if (!isfinite(global_pos.terrain_alt)) {
+ return land_setpoint_alt;
}
- return range_finder.distance;
-
+ /* Decide if the terrain estimation can be used, once we switched to using the terrain we stick with it
+ * for the whole landing */
+ if (_parameters.land_use_terrain_estimate && (global_pos.terrain_alt_valid || land_useterrain)) {
+ if(!land_useterrain) {
+ mavlink_log_info(_mavlink_fd, "#audio: Landing, using terrain estimate");
+ land_useterrain = true;
+ }
+ return global_pos.terrain_alt;
+ } else {
+ return land_setpoint_alt;
+ }
}
bool
-FixedwingPositionControl::control_position(const math::Vector<2> &current_position, const math::Vector<2> &ground_speed,
+FixedwingPositionControl::control_position(const math::Vector<2> &current_position, const math::Vector<3> &ground_speed,
const struct position_setpoint_triplet_s &pos_sp_triplet)
{
bool setpoint = true;
- calculate_gndspeed_undershoot(current_position, ground_speed, pos_sp_triplet);
+ math::Vector<2> ground_speed_2d = {ground_speed(0), ground_speed(1)};
+ calculate_gndspeed_undershoot(current_position, ground_speed_2d, pos_sp_triplet);
float eas2tas = 1.0f; // XXX calculate actual number based on current measurements
- // XXX re-visit
- float baro_altitude = _global_pos.alt;
-
/* filter speed and altitude for controller */
math::Vector<3> accel_body(_sensor_combined.accelerometer_m_s2);
math::Vector<3> accel_earth = _R_nb * accel_body;
- _tecs.update_50hz(baro_altitude, _airspeed.indicated_airspeed_m_s, _R_nb, accel_body, accel_earth);
+ if (!_mTecs.getEnabled()) {
+ _tecs.update_50hz(_global_pos.alt /* XXX might switch to alt err here */, _airspeed.indicated_airspeed_m_s, _R_nb, accel_body, accel_earth);
+ }
+
+ /* define altitude error */
float altitude_error = _pos_sp_triplet.current.alt - _global_pos.alt;
/* no throttle limit as default */
@@ -827,7 +877,17 @@ FixedwingPositionControl::control_position(const math::Vector<2> &current_positi
// XXX this should only execute if auto AND safety off (actuators active),
// else integrators should be constantly reset.
- if (_control_mode.flag_control_position_enabled) {
+ if (pos_sp_triplet.current.valid) {
+
+ if (!_was_pos_control_mode) {
+ /* reset integrators */
+ if (_mTecs.getEnabled()) {
+ _mTecs.resetIntegrators();
+ _mTecs.resetDerivatives(_airspeed.true_airspeed_m_s);
+ }
+ }
+
+ _was_pos_control_mode = true;
/* get circle mode */
bool was_circle_mode = _l1_control.circle_mode();
@@ -841,6 +901,10 @@ FixedwingPositionControl::control_position(const math::Vector<2> &current_positi
/* current waypoint (the one currently heading for) */
math::Vector<2> curr_wp((float)pos_sp_triplet.current.lat, (float)pos_sp_triplet.current.lon);
+ /* Initialize attitude controller integrator reset flags to 0 */
+ _att_sp.roll_reset_integral = false;
+ _att_sp.pitch_reset_integral = false;
+ _att_sp.yaw_reset_integral = false;
/* previous waypoint */
math::Vector<2> prev_wp;
@@ -859,39 +923,44 @@ FixedwingPositionControl::control_position(const math::Vector<2> &current_positi
}
- if (pos_sp_triplet.current.type == SETPOINT_TYPE_NORMAL) {
+ if (pos_sp_triplet.current.type == SETPOINT_TYPE_POSITION) {
/* waypoint is a plain navigation waypoint */
- _l1_control.navigate_waypoints(prev_wp, curr_wp, current_position, ground_speed);
+ _l1_control.navigate_waypoints(prev_wp, curr_wp, current_position, ground_speed_2d);
_att_sp.roll_body = _l1_control.nav_roll();
_att_sp.yaw_body = _l1_control.nav_bearing();
- _tecs.update_pitch_throttle(_R_nb, _att.pitch, _global_pos.alt, _pos_sp_triplet.current.alt, calculate_target_airspeed(_parameters.airspeed_trim),
- _airspeed.indicated_airspeed_m_s, eas2tas,
- false, math::radians(_parameters.pitch_limit_min),
- _parameters.throttle_min, _parameters.throttle_max, _parameters.throttle_cruise,
- math::radians(_parameters.pitch_limit_min), math::radians(_parameters.pitch_limit_max));
+ tecs_update_pitch_throttle(_pos_sp_triplet.current.alt, calculate_target_airspeed(_parameters.airspeed_trim), eas2tas,
+ math::radians(_parameters.pitch_limit_min), math::radians(_parameters.pitch_limit_max),
+ _parameters.throttle_min, _parameters.throttle_max, _parameters.throttle_cruise,
+ false, math::radians(_parameters.pitch_limit_min), _global_pos.alt, ground_speed);
} else if (pos_sp_triplet.current.type == SETPOINT_TYPE_LOITER) {
/* waypoint is a loiter waypoint */
_l1_control.navigate_loiter(curr_wp, current_position, pos_sp_triplet.current.loiter_radius,
- pos_sp_triplet.current.loiter_direction, ground_speed);
+ pos_sp_triplet.current.loiter_direction, ground_speed_2d);
_att_sp.roll_body = _l1_control.nav_roll();
_att_sp.yaw_body = _l1_control.nav_bearing();
- _tecs.update_pitch_throttle(_R_nb, _att.pitch, _global_pos.alt, _pos_sp_triplet.current.alt, calculate_target_airspeed(_parameters.airspeed_trim),
- _airspeed.indicated_airspeed_m_s, eas2tas,
- false, math::radians(_parameters.pitch_limit_min),
- _parameters.throttle_min, _parameters.throttle_max, _parameters.throttle_cruise,
- math::radians(_parameters.pitch_limit_min), math::radians(_parameters.pitch_limit_max));
+ tecs_update_pitch_throttle(_pos_sp_triplet.current.alt, calculate_target_airspeed(_parameters.airspeed_trim), eas2tas,
+ math::radians(_parameters.pitch_limit_min), math::radians(_parameters.pitch_limit_max),
+ _parameters.throttle_min, _parameters.throttle_max, _parameters.throttle_cruise,
+ false, math::radians(_parameters.pitch_limit_min), _global_pos.alt, ground_speed);
} else if (pos_sp_triplet.current.type == SETPOINT_TYPE_LAND) {
float bearing_lastwp_currwp = get_bearing_to_next_waypoint(prev_wp(0), prev_wp(1), curr_wp(0), curr_wp(1));
+ float bearing_airplane_currwp = get_bearing_to_next_waypoint(current_position(0), current_position(1), curr_wp(0), curr_wp(1));
/* Horizontal landing control */
/* switch to heading hold for the last meters, continue heading hold after */
float wp_distance = get_distance_to_next_waypoint(current_position(0), current_position(1), curr_wp(0), curr_wp(1));
+ /* calculate a waypoint distance value which is 0 when the aircraft is behind the waypoint */
+ float wp_distance_save = wp_distance;
+ if (fabsf(bearing_airplane_currwp - bearing_lastwp_currwp) >= math::radians(90.0f)) {
+ wp_distance_save = 0.0f;
+ }
+
//warnx("wp dist: %d, alt err: %d, noret: %s", (int)wp_distance, (int)altitude_error, (land_noreturn) ? "YES" : "NO");
if (wp_distance < _parameters.land_heading_hold_horizontal_distance || land_noreturn_horizontal) {
@@ -908,7 +977,7 @@ FixedwingPositionControl::control_position(const math::Vector<2> &current_positi
// warnx("NORET: %d, target_bearing: %d, yaw: %d", (int)land_noreturn_horizontal, (int)math::degrees(target_bearing), (int)math::degrees(_att.yaw));
- _l1_control.navigate_heading(target_bearing, _att.yaw, ground_speed);
+ _l1_control.navigate_heading(target_bearing, _att.yaw, ground_speed_2d);
/* limit roll motion to prevent wings from touching the ground first */
_att_sp.roll_body = math::constrain(_att_sp.roll_body, math::radians(-10.0f), math::radians(10.0f));
@@ -918,7 +987,7 @@ FixedwingPositionControl::control_position(const math::Vector<2> &current_positi
} else {
/* normal navigation */
- _l1_control.navigate_waypoints(prev_wp, curr_wp, current_position, ground_speed);
+ _l1_control.navigate_waypoints(prev_wp, curr_wp, current_position, ground_speed_2d);
}
_att_sp.roll_body = _l1_control.nav_roll();
@@ -927,30 +996,30 @@ FixedwingPositionControl::control_position(const math::Vector<2> &current_positi
/* Vertical landing control */
//xxx: using the tecs altitude controller for slope control for now
-
-// /* do not go down too early */
-// if (wp_distance > 50.0f) {
-// altitude_error = (_global_triplet.current.alt + 25.0f) - _global_pos.alt;
-// }
/* apply minimum pitch (flare) and limit roll if close to touch down, altitude error is negative (going down) */
// XXX this could make a great param
- float flare_pitch_angle_rad = -math::radians(5.0f);//math::radians(pos_sp_triplet.current.param1)
float throttle_land = _parameters.throttle_min + (_parameters.throttle_max - _parameters.throttle_min) * 0.1f;
float airspeed_land = 1.3f * _parameters.airspeed_min;
float airspeed_approach = 1.3f * _parameters.airspeed_min;
+ /* Get an estimate of the terrain altitude if available, otherwise terrain_alt will be
+ * equal to _pos_sp_triplet.current.alt */
+ float terrain_alt = get_terrain_altitude_landing(_pos_sp_triplet.current.alt, _global_pos);
+
/* Calculate distance (to landing waypoint) and altitude of last ordinary waypoint L */
- float L_wp_distance = get_distance_to_next_waypoint(prev_wp(0), prev_wp(1), curr_wp(0), curr_wp(1));
- float L_altitude_rel = landingslope.getLandingSlopeRelativeAltitude(L_wp_distance);
+ float L_altitude_rel = _pos_sp_triplet.previous.valid ?
+ _pos_sp_triplet.previous.alt - terrain_alt : 0.0f;
- float bearing_airplane_currwp = get_bearing_to_next_waypoint(current_position(0), current_position(1), curr_wp(0), curr_wp(1));
float landing_slope_alt_rel_desired = landingslope.getLandingSlopeRelativeAltitudeSave(wp_distance, bearing_lastwp_currwp, bearing_airplane_currwp);
- float relative_alt = get_relative_landingalt(_pos_sp_triplet.current.alt, _global_pos.alt, _range_finder, _parameters.range_finder_rel_alt);
-
- if ( (relative_alt < landingslope.flare_relative_alt()) || land_noreturn_vertical) { //checking for land_noreturn to avoid unwanted climb out
-
+ /* Check if we should start flaring with a vertical and a
+ * horizontal limit (with some tolerance)
+ * The horizontal limit is only applied when we are in front of the wp
+ */
+ if (((_global_pos.alt < terrain_alt + landingslope.flare_relative_alt()) &&
+ (wp_distance_save < landingslope.flare_length() + 5.0f)) ||
+ land_noreturn_vertical) { //checking for land_noreturn to avoid unwanted climb out
/* land with minimal speed */
// /* force TECS to only control speed with pitch, altitude is only implicitely controlled now */
@@ -959,7 +1028,7 @@ FixedwingPositionControl::control_position(const math::Vector<2> &current_positi
/* kill the throttle if param requests it */
throttle_max = _parameters.throttle_max;
- if (relative_alt < landingslope.motor_lim_relative_alt() || land_motor_lim) {
+ if (_global_pos.alt < terrain_alt + landingslope.motor_lim_relative_alt() || land_motor_lim) {
throttle_max = math::min(throttle_max, _parameters.throttle_land_max);
if (!land_motor_lim) {
land_motor_lim = true;
@@ -977,11 +1046,14 @@ FixedwingPositionControl::control_position(const math::Vector<2> &current_positi
land_stayonground = true;
}
- _tecs.update_pitch_throttle(_R_nb, _att.pitch, _pos_sp_triplet.current.alt + relative_alt, _pos_sp_triplet.current.alt + flare_curve_alt_rel, calculate_target_airspeed(airspeed_land),
- _airspeed.indicated_airspeed_m_s, eas2tas,
- false, flare_pitch_angle_rad,
- 0.0f, throttle_max, throttle_land,
- flare_pitch_angle_rad, math::radians(15.0f));
+ tecs_update_pitch_throttle(terrain_alt + flare_curve_alt_rel,
+ calculate_target_airspeed(airspeed_land), eas2tas,
+ math::radians(_parameters.land_flare_pitch_min_deg),
+ math::radians(_parameters.land_flare_pitch_max_deg),
+ 0.0f, throttle_max, throttle_land,
+ false, land_motor_lim ? math::radians(_parameters.land_flare_pitch_min_deg) : math::radians(_parameters.pitch_limit_min),
+ _global_pos.alt, ground_speed,
+ land_motor_lim ? TECS_MODE_LAND_THROTTLELIM : TECS_MODE_LAND);
if (!land_noreturn_vertical) {
mavlink_log_info(_mavlink_fd, "#audio: Landing, flaring");
@@ -994,11 +1066,15 @@ FixedwingPositionControl::control_position(const math::Vector<2> &current_positi
/* intersect glide slope:
* minimize speed to approach speed
- * if current position is higher or within 10m of slope follow the glide slope
- * if current position is below slope -10m continue on maximum of previous wp altitude or L_altitude until the intersection with the slope
+ * if current position is higher than the slope follow the glide slope (sink to the
+ * glide slope)
+ * also if the system captures the slope it should stay
+ * on the slope (bool land_onslope)
+ * if current position is below the slope continue at previous wp altitude
+ * until the intersection with slope
* */
- float altitude_desired_rel = relative_alt;
- if (relative_alt > landing_slope_alt_rel_desired - 10.0f) {
+ float altitude_desired_rel;
+ if (_global_pos.alt > terrain_alt + landing_slope_alt_rel_desired || land_onslope) {
/* stay on slope */
altitude_desired_rel = landing_slope_alt_rel_desired;
if (!land_onslope) {
@@ -1007,82 +1083,116 @@ FixedwingPositionControl::control_position(const math::Vector<2> &current_positi
}
} else {
/* continue horizontally */
- altitude_desired_rel = math::max(relative_alt, L_altitude_rel);
+ altitude_desired_rel = _pos_sp_triplet.previous.valid ? L_altitude_rel :
+ _global_pos.alt - terrain_alt;
}
- _tecs.update_pitch_throttle(_R_nb, _att.pitch, _pos_sp_triplet.current.alt + relative_alt, _pos_sp_triplet.current.alt + altitude_desired_rel, calculate_target_airspeed(airspeed_approach),
- _airspeed.indicated_airspeed_m_s, eas2tas,
- false, math::radians(_parameters.pitch_limit_min),
- _parameters.throttle_min, _parameters.throttle_max, _parameters.throttle_cruise,
- math::radians(_parameters.pitch_limit_min), math::radians(_parameters.pitch_limit_max));
+ tecs_update_pitch_throttle(terrain_alt + altitude_desired_rel,
+ calculate_target_airspeed(airspeed_approach), eas2tas,
+ math::radians(_parameters.pitch_limit_min),
+ math::radians(_parameters.pitch_limit_max),
+ _parameters.throttle_min,
+ _parameters.throttle_max,
+ _parameters.throttle_cruise,
+ false,
+ math::radians(_parameters.pitch_limit_min),
+ _global_pos.alt,
+ ground_speed);
}
} else if (pos_sp_triplet.current.type == SETPOINT_TYPE_TAKEOFF) {
/* Perform launch detection */
-// warnx("Launch detection running");
- if(!launch_detected) { //do not do further checks once a launch was detected
- if (launchDetector.launchDetectionEnabled()) {
- static hrt_abstime last_sent = 0;
- if(hrt_absolute_time() - last_sent > 4e6) {
-// warnx("Launch detection running");
- mavlink_log_info(_mavlink_fd, "#audio: Launchdetection running");
- last_sent = hrt_absolute_time();
- }
- launchDetector.update(_sensor_combined.accelerometer_m_s2[0]);
- if (launchDetector.getLaunchDetected()) {
- launch_detected = true;
- mavlink_log_info(_mavlink_fd, "#audio: Takeoff");
- }
- } else {
- /* no takeoff detection --> fly */
- launch_detected = true;
- warnx("launchdetection off");
+ if (launchDetector.launchDetectionEnabled() &&
+ launch_detection_state != LAUNCHDETECTION_RES_DETECTED_ENABLEMOTORS) {
+ /* Inform user that launchdetection is running */
+ static hrt_abstime last_sent = 0;
+ if(hrt_absolute_time() - last_sent > 4e6) {
+ mavlink_log_info(_mavlink_fd, "#audio: Launchdetection running");
+ last_sent = hrt_absolute_time();
}
+
+ /* Detect launch */
+ launchDetector.update(_sensor_combined.accelerometer_m_s2[0]);
+
+ /* update our copy of the laucn detection state */
+ launch_detection_state = launchDetector.getLaunchDetected();
+ } else {
+ /* no takeoff detection --> fly */
+ launch_detection_state = LAUNCHDETECTION_RES_DETECTED_ENABLEMOTORS;
}
- _l1_control.navigate_waypoints(prev_wp, curr_wp, current_position, ground_speed);
- _att_sp.roll_body = _l1_control.nav_roll();
- _att_sp.yaw_body = _l1_control.nav_bearing();
+ /* Set control values depending on the detection state */
+ if (launch_detection_state != LAUNCHDETECTION_RES_NONE) {
+ /* Launch has been detected, hence we have to control the plane. */
+
+ _l1_control.navigate_waypoints(prev_wp, curr_wp, current_position, ground_speed_2d);
+ _att_sp.roll_body = _l1_control.nav_roll();
+ _att_sp.yaw_body = _l1_control.nav_bearing();
- if (launch_detected) {
- usePreTakeoffThrust = false;
+ /* Select throttle: only in LAUNCHDETECTION_RES_DETECTED_ENABLEMOTORS we want to use
+ * full throttle, otherwise we use the preTakeOff Throttle */
+ float takeoff_throttle = launch_detection_state !=
+ LAUNCHDETECTION_RES_DETECTED_ENABLEMOTORS ?
+ launchDetector.getThrottlePreTakeoff() : _parameters.throttle_max;
- /* apply minimum pitch and limit roll if target altitude is not within 10 meters */
- if (altitude_error > 15.0f) {
+ /* select maximum pitch: the launchdetector may impose another limit for the pitch
+ * depending on the state of the launch */
+ float takeoff_pitch_max_deg = launchDetector.getPitchMax(_parameters.pitch_limit_max);
+ float takeoff_pitch_max_rad = math::radians(takeoff_pitch_max_deg);
+
+ /* apply minimum pitch and limit roll if target altitude is not within climbout_diff
+ * meters */
+ if (_parameters.climbout_diff > 0.001f && altitude_error > _parameters.climbout_diff) {
/* enforce a minimum of 10 degrees pitch up on takeoff, or take parameter */
- _tecs.update_pitch_throttle(_R_nb, _att.pitch, _global_pos.alt, _pos_sp_triplet.current.alt, calculate_target_airspeed(1.3f * _parameters.airspeed_min),
- _airspeed.indicated_airspeed_m_s, eas2tas,
- true, math::max(math::radians(pos_sp_triplet.current.pitch_min), math::radians(10.0f)),
- _parameters.throttle_min, _parameters.throttle_max, _parameters.throttle_cruise,
- math::radians(_parameters.pitch_limit_min), math::radians(_parameters.pitch_limit_max));
+ tecs_update_pitch_throttle(_pos_sp_triplet.current.alt,
+ calculate_target_airspeed(1.3f * _parameters.airspeed_min),
+ eas2tas,
+ math::radians(_parameters.pitch_limit_min),
+ takeoff_pitch_max_rad,
+ _parameters.throttle_min, takeoff_throttle,
+ _parameters.throttle_cruise,
+ true,
+ math::max(math::radians(pos_sp_triplet.current.pitch_min),
+ math::radians(10.0f)),
+ _global_pos.alt,
+ ground_speed,
+ TECS_MODE_TAKEOFF,
+ takeoff_pitch_max_deg != _parameters.pitch_limit_max);
/* limit roll motion to ensure enough lift */
- _att_sp.roll_body = math::constrain(_att_sp.roll_body, math::radians(-15.0f), math::radians(15.0f));
+ _att_sp.roll_body = math::constrain(_att_sp.roll_body, math::radians(-15.0f),
+ math::radians(15.0f));
} else {
-
- _tecs.update_pitch_throttle(_R_nb, _att.pitch, _global_pos.alt, _pos_sp_triplet.current.alt, calculate_target_airspeed(_parameters.airspeed_trim),
- _airspeed.indicated_airspeed_m_s, eas2tas,
- false, math::radians(_parameters.pitch_limit_min),
- _parameters.throttle_min, _parameters.throttle_max, _parameters.throttle_cruise,
- math::radians(_parameters.pitch_limit_min), math::radians(_parameters.pitch_limit_max));
+ tecs_update_pitch_throttle(_pos_sp_triplet.current.alt,
+ calculate_target_airspeed(_parameters.airspeed_trim),
+ eas2tas,
+ math::radians(_parameters.pitch_limit_min),
+ math::radians(_parameters.pitch_limit_max),
+ _parameters.throttle_min,
+ takeoff_throttle,
+ _parameters.throttle_cruise,
+ false,
+ math::radians(_parameters.pitch_limit_min),
+ _global_pos.alt,
+ ground_speed);
}
-
} else {
- usePreTakeoffThrust = true;
+ /* Tell the attitude controller to stop integrating while we are waiting
+ * for the launch */
+ _att_sp.roll_reset_integral = true;
+ _att_sp.pitch_reset_integral = true;
+ _att_sp.yaw_reset_integral = true;
+
+ /* Set default roll and pitch setpoints during detection phase */
+ _att_sp.roll_body = 0.0f;
+ _att_sp.pitch_body = math::max(math::radians(pos_sp_triplet.current.pitch_min),
+ math::radians(10.0f));
}
- }
-
- // warnx("nav bearing: %8.4f bearing err: %8.4f target bearing: %8.4f", (double)_l1_control.nav_bearing(),
- // (double)_l1_control.bearing_error(), (double)_l1_control.target_bearing());
- // warnx("prev wp: %8.4f/%8.4f, next wp: %8.4f/%8.4f prev:%s", (double)prev_wp(0), (double)prev_wp(1),
- // (double)next_wp(0), (double)next_wp(1), (pos_sp_triplet.previous_valid) ? "valid" : "invalid");
- // XXX at this point we always want no loiter hold if a
- // mission is active
- _loiter_hold = false;
+ }
/* reset landing state */
if (pos_sp_triplet.current.type != SETPOINT_TYPE_LAND) {
@@ -1099,91 +1209,10 @@ FixedwingPositionControl::control_position(const math::Vector<2> &current_positi
_att_sp.roll_reset_integral = true;
}
- } else if (0/* posctrl mode enabled */) {
-
- /** POSCTRL FLIGHT **/
-
- if (0/* switched from another mode to posctrl */) {
- _altctrl_hold_heading = _att.yaw;
- }
-
- if (0/* posctrl on and manual control yaw non-zero */) {
- _altctrl_hold_heading = _att.yaw + _manual.r;
- }
-
- //XXX not used
-
- /* climb out control */
-// bool climb_out = false;
-//
-// /* user wants to climb out */
-// if (_manual.pitch > 0.3f && _manual.throttle > 0.8f) {
-// climb_out = true;
-// }
-
- /* if in altctrl mode, set airspeed based on manual control */
-
- // XXX check if ground speed undershoot should be applied here
- float altctrl_airspeed = _parameters.airspeed_min +
- (_parameters.airspeed_max - _parameters.airspeed_min) *
- _manual.z;
-
- _l1_control.navigate_heading(_altctrl_hold_heading, _att.yaw, ground_speed);
- _att_sp.roll_body = _l1_control.nav_roll();
- _att_sp.yaw_body = _l1_control.nav_bearing();
- _tecs.update_pitch_throttle(_R_nb, _att.pitch, _global_pos.alt, _global_pos.alt + _manual.x * 2.0f,
- altctrl_airspeed,
- _airspeed.indicated_airspeed_m_s, eas2tas,
- false, _parameters.pitch_limit_min,
- _parameters.throttle_min, _parameters.throttle_max, _parameters.throttle_cruise,
- _parameters.pitch_limit_min, _parameters.pitch_limit_max);
-
- } else if (0/* altctrl mode enabled */) {
-
- /** ALTCTRL FLIGHT **/
-
- if (0/* switched from another mode to altctrl */) {
- _altctrl_hold_heading = _att.yaw;
- }
-
- if (0/* altctrl on and manual control yaw non-zero */) {
- _altctrl_hold_heading = _att.yaw + _manual.r;
- }
-
- /* if in altctrl mode, set airspeed based on manual control */
-
- // XXX check if ground speed undershoot should be applied here
- float altctrl_airspeed = _parameters.airspeed_min +
- (_parameters.airspeed_max - _parameters.airspeed_min) *
- _manual.z;
-
- /* user switched off throttle */
- if (_manual.z < 0.1f) {
- throttle_max = 0.0f;
- /* switch to pure pitch based altitude control, give up speed */
- _tecs.set_speed_weight(0.0f);
- }
-
- /* climb out control */
- bool climb_out = false;
-
- /* user wants to climb out */
- if (_manual.x > 0.3f && _manual.z > 0.8f) {
- climb_out = true;
- }
-
- _l1_control.navigate_heading(_altctrl_hold_heading, _att.yaw, ground_speed);
- _att_sp.roll_body = _manual.y;
- _att_sp.yaw_body = _manual.r;
- _tecs.update_pitch_throttle(_R_nb, _att.pitch, _global_pos.alt, _global_pos.alt + _manual.x * 2.0f,
- altctrl_airspeed,
- _airspeed.indicated_airspeed_m_s, eas2tas,
- climb_out, _parameters.pitch_limit_min,
- _parameters.throttle_min, _parameters.throttle_max, _parameters.throttle_cruise,
- _parameters.pitch_limit_min, _parameters.pitch_limit_max);
-
} else {
+ _was_pos_control_mode = false;
+
/** MANUAL FLIGHT **/
/* no flight mode applies, do not publish an attitude setpoint */
@@ -1196,13 +1225,26 @@ FixedwingPositionControl::control_position(const math::Vector<2> &current_positi
}
}
- if (usePreTakeoffThrust) {
+ if (_vehicle_status.engine_failure || _vehicle_status.engine_failure_cmd) {
+ /* Set thrust to 0 to minimize damage */
+ _att_sp.thrust = 0.0f;
+ } else if (pos_sp_triplet.current.type == SETPOINT_TYPE_TAKEOFF &&
+ launch_detection_state != LAUNCHDETECTION_RES_DETECTED_ENABLEMOTORS) {
+ /* making sure again that the correct thrust is used,
+ * without depending on library calls for safety reasons */
_att_sp.thrust = launchDetector.getThrottlePreTakeoff();
+ } else {
+ /* Copy thrust and pitch values from tecs */
+ _att_sp.thrust = math::min(_mTecs.getEnabled() ? _mTecs.getThrottleSetpoint() :
+ _tecs.get_throttle_demand(), throttle_max);
}
- else {
- _att_sp.thrust = math::min(_tecs.get_throttle_demand(), throttle_max);
+
+ /* During a takeoff waypoint while waiting for launch the pitch sp is set
+ * already (not by tecs) */
+ if (!(pos_sp_triplet.current.type == SETPOINT_TYPE_TAKEOFF &&
+ launch_detection_state == LAUNCHDETECTION_RES_NONE)) {
+ _att_sp.pitch_body = _mTecs.getEnabled() ? _mTecs.getPitchSetpoint() : _tecs.get_pitch_demand();
}
- _att_sp.pitch_body = _tecs.get_pitch_demand();
if (_control_mode.flag_control_position_enabled) {
last_manual = false;
@@ -1218,10 +1260,6 @@ void
FixedwingPositionControl::task_main()
{
- /* inform about start */
- warnx("Initializing..");
- fflush(stdout);
-
/*
* do subscriptions
*/
@@ -1230,13 +1268,15 @@ FixedwingPositionControl::task_main()
_att_sub = orb_subscribe(ORB_ID(vehicle_attitude));
_sensor_combined_sub = orb_subscribe(ORB_ID(sensor_combined));
_control_mode_sub = orb_subscribe(ORB_ID(vehicle_control_mode));
+ _vehicle_status_sub = orb_subscribe(ORB_ID(vehicle_status));
_airspeed_sub = orb_subscribe(ORB_ID(airspeed));
_params_sub = orb_subscribe(ORB_ID(parameter_update));
_manual_control_sub = orb_subscribe(ORB_ID(manual_control_setpoint));
- _range_finder_sub = orb_subscribe(ORB_ID(sensor_range_finder));
- /* rate limit vehicle status updates to 5Hz */
+ /* rate limit control mode updates to 5Hz */
orb_set_interval(_control_mode_sub, 200);
+ /* rate limit vehicle status updates to 5Hz */
+ orb_set_interval(_vehicle_status_sub, 200);
/* rate limit position updates to 50 Hz */
orb_set_interval(_global_pos_sub, 20);
@@ -1256,6 +1296,8 @@ FixedwingPositionControl::task_main()
fds[1].fd = _global_pos_sub;
fds[1].events = POLLIN;
+ _task_running = true;
+
while (!_task_should_exit) {
/* wait for up to 500ms for data */
@@ -1273,9 +1315,12 @@ FixedwingPositionControl::task_main()
perf_begin(_loop_perf);
- /* check vehicle status for changes to publication state */
+ /* check vehicle control mode for changes to publication state */
vehicle_control_mode_poll();
+ /* check vehicle status for changes to publication state */
+ vehicle_status_poll();
+
/* only update parameters if they changed */
if (fds[0].revents & POLLIN) {
/* read from param to clear updated flag */
@@ -1295,14 +1340,6 @@ FixedwingPositionControl::task_main()
_mavlink_fd = open(MAVLINK_LOG_DEVICE, 0);
}
- static uint64_t last_run = 0;
- float deltaT = (hrt_absolute_time() - last_run) / 1000000.0f;
- last_run = hrt_absolute_time();
-
- /* guard against too large deltaT's */
- if (deltaT > 1.0f)
- deltaT = 0.01f;
-
/* load local copies */
orb_copy(ORB_ID(vehicle_global_position), _global_pos_sub, &_global_pos);
@@ -1313,10 +1350,9 @@ FixedwingPositionControl::task_main()
vehicle_setpoint_poll();
vehicle_sensor_combined_poll();
vehicle_airspeed_poll();
- range_finder_poll();
// vehicle_baro_poll();
- math::Vector<2> ground_speed(_global_pos.vel_n, _global_pos.vel_e);
+ math::Vector<3> ground_speed(_global_pos.vel_n, _global_pos.vel_e, _global_pos.vel_d);
math::Vector<2> current_position((float)_global_pos.lat, (float)_global_pos.lon);
/*
@@ -1356,6 +1392,8 @@ FixedwingPositionControl::task_main()
perf_end(_loop_perf);
}
+ _task_running = false;
+
warnx("exiting.\n");
_control_task = -1;
@@ -1364,8 +1402,7 @@ FixedwingPositionControl::task_main()
void FixedwingPositionControl::reset_takeoff_state()
{
- launch_detected = false;
- usePreTakeoffThrust = false;
+ launch_detection_state = LAUNCHDETECTION_RES_NONE;
launchDetector.reset();
}
@@ -1376,6 +1413,107 @@ void FixedwingPositionControl::reset_landing_state()
land_stayonground = false;
land_motor_lim = false;
land_onslope = false;
+ land_useterrain = false;
+}
+
+void FixedwingPositionControl::tecs_update_pitch_throttle(float alt_sp, float v_sp, float eas2tas,
+ float pitch_min_rad, float pitch_max_rad,
+ float throttle_min, float throttle_max, float throttle_cruise,
+ bool climbout_mode, float climbout_pitch_min_rad,
+ float altitude,
+ const math::Vector<3> &ground_speed,
+ tecs_mode mode, bool pitch_max_special)
+{
+ if (_mTecs.getEnabled()) {
+ /* Using mtecs library: prepare arguments for mtecs call */
+ float flightPathAngle = 0.0f;
+ float ground_speed_length = ground_speed.length();
+ if (ground_speed_length > FLT_EPSILON) {
+ flightPathAngle = -asinf(ground_speed(2)/ground_speed_length);
+ }
+ fwPosctrl::LimitOverride limitOverride;
+ if (_vehicle_status.engine_failure || _vehicle_status.engine_failure_cmd) {
+ /* Force the slow downwards spiral */
+ limitOverride.enablePitchMinOverride(-1.0f);
+ limitOverride.enablePitchMaxOverride(5.0f);
+
+ } else if (climbout_mode) {
+ limitOverride.enablePitchMinOverride(M_RAD_TO_DEG_F * climbout_pitch_min_rad);
+ } else {
+ limitOverride.disablePitchMinOverride();
+ }
+
+ if (pitch_max_special) {
+ /* Use the maximum pitch from the argument */
+ limitOverride.enablePitchMaxOverride(M_RAD_TO_DEG_F * pitch_max_rad);
+ } else {
+ /* use pitch max set by MT param */
+ limitOverride.disablePitchMaxOverride();
+ }
+ _mTecs.updateAltitudeSpeed(flightPathAngle, altitude, alt_sp, _airspeed.true_airspeed_m_s, v_sp, mode,
+ limitOverride);
+ } else {
+ if (_vehicle_status.engine_failure || _vehicle_status.engine_failure_cmd) {
+ /* Force the slow downwards spiral */
+ pitch_min_rad = M_DEG_TO_RAD_F * -1.0f;
+ pitch_max_rad = M_DEG_TO_RAD_F * 5.0f;
+ }
+
+/* No underspeed protection in landing mode */
+ _tecs.set_detect_underspeed_enabled(!(mode == TECS_MODE_LAND || mode == TECS_MODE_LAND_THROTTLELIM));
+
+ /* Using tecs library */
+ _tecs.update_pitch_throttle(_R_nb, _att.pitch, altitude, alt_sp, v_sp,
+ _airspeed.indicated_airspeed_m_s, eas2tas,
+ climbout_mode, climbout_pitch_min_rad,
+ throttle_min, throttle_max, throttle_cruise,
+ pitch_min_rad, pitch_max_rad);
+
+ struct TECS::tecs_state s;
+ _tecs.get_tecs_state(s);
+
+ struct tecs_status_s t;
+
+ t.timestamp = s.timestamp;
+
+ switch (s.mode) {
+ case TECS::ECL_TECS_MODE_NORMAL:
+ t.mode = TECS_MODE_NORMAL;
+ break;
+ case TECS::ECL_TECS_MODE_UNDERSPEED:
+ t.mode = TECS_MODE_UNDERSPEED;
+ break;
+ case TECS::ECL_TECS_MODE_BAD_DESCENT:
+ t.mode = TECS_MODE_BAD_DESCENT;
+ break;
+ case TECS::ECL_TECS_MODE_CLIMBOUT:
+ t.mode = TECS_MODE_CLIMBOUT;
+ break;
+ }
+
+ t.altitudeSp = s.hgt_dem;
+ t.altitude_filtered = s.hgt;
+ t.airspeedSp = s.spd_dem;
+ t.airspeed_filtered = s.spd;
+
+ t.flightPathAngleSp = s.dhgt_dem;
+ t.flightPathAngle = s.dhgt;
+ t.flightPathAngleFiltered = s.dhgt;
+
+ t.airspeedDerivativeSp = s.dspd_dem;
+ t.airspeedDerivative = s.dspd;
+
+ t.totalEnergyRateSp = s.thr;
+ t.totalEnergyRate = s.ithr;
+ t.energyDistributionRateSp = s.ptch;
+ t.energyDistributionRate = s.iptch;
+
+ if (_tecs_status_pub > 0) {
+ orb_publish(ORB_ID(tecs_status), _tecs_status_pub, &t);
+ } else {
+ _tecs_status_pub = orb_advertise(ORB_ID(tecs_status), &t);
+ }
+ }
}
int
@@ -1387,7 +1525,7 @@ FixedwingPositionControl::start()
_control_task = task_spawn_cmd("fw_pos_control_l1",
SCHED_DEFAULT,
SCHED_PRIORITY_MAX - 5,
- 4048,
+ 2000,
(main_t)&FixedwingPositionControl::task_main_trampoline,
nullptr);
@@ -1409,17 +1547,18 @@ int fw_pos_control_l1_main(int argc, char *argv[])
if (l1_control::g_control != nullptr)
errx(1, "already running");
- l1_control::g_control = new FixedwingPositionControl;
-
- if (l1_control::g_control == nullptr)
- errx(1, "alloc failed");
-
- if (OK != l1_control::g_control->start()) {
- delete l1_control::g_control;
- l1_control::g_control = nullptr;
+ if (OK != FixedwingPositionControl::start()) {
err(1, "start failed");
}
+ /* avoid memory fragmentation by not exiting start handler until the task has fully started */
+ while (l1_control::g_control == nullptr || !l1_control::g_control->task_running()) {
+ usleep(50000);
+ printf(".");
+ fflush(stdout);
+ }
+ printf("\n");
+
exit(0);
}
diff --git a/src/modules/fw_pos_control_l1/fw_pos_control_l1_params.c b/src/modules/fw_pos_control_l1/fw_pos_control_l1_params.c
index 52128e1b7..c00d82232 100644
--- a/src/modules/fw_pos_control_l1/fw_pos_control_l1_params.c
+++ b/src/modules/fw_pos_control_l1/fw_pos_control_l1_params.c
@@ -83,6 +83,17 @@ PARAM_DEFINE_FLOAT(FW_L1_DAMPING, 0.75f);
PARAM_DEFINE_FLOAT(FW_THR_CRUISE, 0.7f);
/**
+ * Throttle max slew rate
+ *
+ * Maximum slew rate for the commanded throttle
+ *
+ * @min 0.0
+ * @max 1.0
+ * @group L1 Control
+ */
+PARAM_DEFINE_FLOAT(FW_THR_SLEW_MAX, 0.0f);
+
+/**
* Negative pitch limit
*
* The minimum negative pitch the controller will output.
@@ -120,8 +131,8 @@ PARAM_DEFINE_FLOAT(FW_R_LIM, 45.0f);
/**
* Throttle limit max
*
- * This is the maximum throttle % that can be used by the controller.
- * For overpowered aircraft, this should be reduced to a value that
+ * This is the maximum throttle % that can be used by the controller.
+ * For overpowered aircraft, this should be reduced to a value that
* provides sufficient thrust to climb at the maximum pitch angle PTCH_MAX.
*
* @group L1 Control
@@ -131,10 +142,10 @@ PARAM_DEFINE_FLOAT(FW_THR_MAX, 1.0f);
/**
* Throttle limit min
*
- * This is the minimum throttle % that can be used by the controller.
- * For electric aircraft this will normally be set to zero, but can be set
- * to a small non-zero value if a folding prop is fitted to prevent the
- * prop from folding and unfolding repeatedly in-flight or to provide
+ * This is the minimum throttle % that can be used by the controller.
+ * For electric aircraft this will normally be set to zero, but can be set
+ * to a small non-zero value if a folding prop is fitted to prevent the
+ * prop from folding and unfolding repeatedly in-flight or to provide
* some aerodynamic drag from a turning prop to improve the descent rate.
*
* For aircraft with internal combustion engine this parameter should be set
@@ -147,7 +158,7 @@ PARAM_DEFINE_FLOAT(FW_THR_MIN, 0.0f);
/**
* Throttle limit value before flare
*
- * This throttle value will be set as throttle limit at FW_LND_TLALT,
+ * This throttle value will be set as throttle limit at FW_LND_TLALT,
* before arcraft will flare.
*
* @group L1 Control
@@ -155,19 +166,31 @@ PARAM_DEFINE_FLOAT(FW_THR_MIN, 0.0f);
PARAM_DEFINE_FLOAT(FW_THR_LND_MAX, 1.0f);
/**
+ * Climbout Altitude difference
+ *
+ * If the altitude error exceeds this parameter, the system will climb out
+ * with maximum throttle and minimum airspeed until it is closer than this
+ * distance to the desired altitude. Mostly used for takeoff waypoints / modes.
+ * Set to zero to disable climbout mode (not recommended).
+ *
+ * @group L1 Control
+ */
+PARAM_DEFINE_FLOAT(FW_CLMBOUT_DIFF, 25.0f);
+
+/**
* Maximum climb rate
*
- * This is the best climb rate that the aircraft can achieve with
- * the throttle set to THR_MAX and the airspeed set to the
- * default value. For electric aircraft make sure this number can be
- * achieved towards the end of flight when the battery voltage has reduced.
- * The setting of this parameter can be checked by commanding a positive
- * altitude change of 100m in loiter, RTL or guided mode. If the throttle
- * required to climb is close to THR_MAX and the aircraft is maintaining
- * airspeed, then this parameter is set correctly. If the airspeed starts
- * to reduce, then the parameter is set to high, and if the throttle
- * demand required to climb and maintain speed is noticeably less than
- * FW_THR_MAX, then either FW_T_CLMB_MAX should be increased or
+ * This is the best climb rate that the aircraft can achieve with
+ * the throttle set to THR_MAX and the airspeed set to the
+ * default value. For electric aircraft make sure this number can be
+ * achieved towards the end of flight when the battery voltage has reduced.
+ * The setting of this parameter can be checked by commanding a positive
+ * altitude change of 100m in loiter, RTL or guided mode. If the throttle
+ * required to climb is close to THR_MAX and the aircraft is maintaining
+ * airspeed, then this parameter is set correctly. If the airspeed starts
+ * to reduce, then the parameter is set to high, and if the throttle
+ * demand required to climb and maintain speed is noticeably less than
+ * FW_THR_MAX, then either FW_T_CLMB_MAX should be increased or
* FW_THR_MAX reduced.
*
* @group L1 Control
@@ -177,57 +200,68 @@ PARAM_DEFINE_FLOAT(FW_T_CLMB_MAX, 5.0f);
/**
* Minimum descent rate
*
- * This is the sink rate of the aircraft with the throttle
- * set to THR_MIN and flown at the same airspeed as used
+ * This is the sink rate of the aircraft with the throttle
+ * set to THR_MIN and flown at the same airspeed as used
* to measure FW_T_CLMB_MAX.
*
- * @group L1 Control
+ * @group Fixed Wing TECS
*/
PARAM_DEFINE_FLOAT(FW_T_SINK_MIN, 2.0f);
/**
* Maximum descent rate
*
- * This sets the maximum descent rate that the controller will use.
- * If this value is too large, the aircraft can over-speed on descent.
- * This should be set to a value that can be achieved without
- * exceeding the lower pitch angle limit and without over-speeding
+ * This sets the maximum descent rate that the controller will use.
+ * If this value is too large, the aircraft can over-speed on descent.
+ * This should be set to a value that can be achieved without
+ * exceeding the lower pitch angle limit and without over-speeding
* the aircraft.
*
- * @group L1 Control
+ * @group Fixed Wing TECS
*/
PARAM_DEFINE_FLOAT(FW_T_SINK_MAX, 5.0f);
/**
* TECS time constant
*
- * This is the time constant of the TECS control algorithm (in seconds).
+ * This is the time constant of the TECS control algorithm (in seconds).
* Smaller values make it faster to respond, larger values make it slower
* to respond.
*
- * @group L1 Control
+ * @group Fixed Wing TECS
*/
PARAM_DEFINE_FLOAT(FW_T_TIME_CONST, 5.0f);
/**
+ * TECS Throttle time constant
+ *
+ * This is the time constant of the TECS throttle control algorithm (in seconds).
+ * Smaller values make it faster to respond, larger values make it slower
+ * to respond.
+ *
+ * @group Fixed Wing TECS
+ */
+PARAM_DEFINE_FLOAT(FW_T_THRO_CONST, 8.0f);
+
+/**
* Throttle damping factor
*
- * This is the damping gain for the throttle demand loop.
+ * This is the damping gain for the throttle demand loop.
* Increase to add damping to correct for oscillations in speed and height.
*
- * @group L1 Control
+ * @group Fixed Wing TECS
*/
PARAM_DEFINE_FLOAT(FW_T_THR_DAMP, 0.5f);
/**
* Integrator gain
*
- * This is the integrator gain on the control loop.
- * Increasing this gain increases the speed at which speed
- * and height offsets are trimmed out, but reduces damping and
+ * This is the integrator gain on the control loop.
+ * Increasing this gain increases the speed at which speed
+ * and height offsets are trimmed out, but reduces damping and
* increases overshoot.
*
- * @group L1 Control
+ * @group Fixed Wing TECS
*/
PARAM_DEFINE_FLOAT(FW_T_INTEG_GAIN, 0.1f);
@@ -235,98 +269,105 @@ PARAM_DEFINE_FLOAT(FW_T_INTEG_GAIN, 0.1f);
* Maximum vertical acceleration
*
* This is the maximum vertical acceleration (in metres/second square)
- * either up or down that the controller will use to correct speed
- * or height errors. The default value of 7 m/s/s (equivalent to +- 0.7 g)
- * allows for reasonably aggressive pitch changes if required to recover
+ * either up or down that the controller will use to correct speed
+ * or height errors. The default value of 7 m/s/s (equivalent to +- 0.7 g)
+ * allows for reasonably aggressive pitch changes if required to recover
* from under-speed conditions.
*
- * @group L1 Control
+ * @group Fixed Wing TECS
*/
PARAM_DEFINE_FLOAT(FW_T_VERT_ACC, 7.0f);
/**
* Complementary filter "omega" parameter for height
*
- * This is the cross-over frequency (in radians/second) of the complementary
- * filter used to fuse vertical acceleration and barometric height to obtain
- * an estimate of height rate and height. Increasing this frequency weights
- * the solution more towards use of the barometer, whilst reducing it weights
+ * This is the cross-over frequency (in radians/second) of the complementary
+ * filter used to fuse vertical acceleration and barometric height to obtain
+ * an estimate of height rate and height. Increasing this frequency weights
+ * the solution more towards use of the barometer, whilst reducing it weights
* the solution more towards use of the accelerometer data.
*
- * @group L1 Control
+ * @group Fixed Wing TECS
*/
PARAM_DEFINE_FLOAT(FW_T_HGT_OMEGA, 3.0f);
/**
* Complementary filter "omega" parameter for speed
*
- * This is the cross-over frequency (in radians/second) of the complementary
- * filter used to fuse longitudinal acceleration and airspeed to obtain an
+ * This is the cross-over frequency (in radians/second) of the complementary
+ * filter used to fuse longitudinal acceleration and airspeed to obtain an
* improved airspeed estimate. Increasing this frequency weights the solution
- * more towards use of the arispeed sensor, whilst reducing it weights the
+ * more towards use of the arispeed sensor, whilst reducing it weights the
* solution more towards use of the accelerometer data.
*
- * @group L1 Control
+ * @group Fixed Wing TECS
*/
PARAM_DEFINE_FLOAT(FW_T_SPD_OMEGA, 2.0f);
/**
* Roll -> Throttle feedforward
*
- * Increasing this gain turn increases the amount of throttle that will
- * be used to compensate for the additional drag created by turning.
- * Ideally this should be set to approximately 10 x the extra sink rate
- * in m/s created by a 45 degree bank turn. Increase this gain if
- * the aircraft initially loses energy in turns and reduce if the
- * aircraft initially gains energy in turns. Efficient high aspect-ratio
- * aircraft (eg powered sailplanes) can use a lower value, whereas
+ * Increasing this gain turn increases the amount of throttle that will
+ * be used to compensate for the additional drag created by turning.
+ * Ideally this should be set to approximately 10 x the extra sink rate
+ * in m/s created by a 45 degree bank turn. Increase this gain if
+ * the aircraft initially loses energy in turns and reduce if the
+ * aircraft initially gains energy in turns. Efficient high aspect-ratio
+ * aircraft (eg powered sailplanes) can use a lower value, whereas
* inefficient low aspect-ratio models (eg delta wings) can use a higher value.
*
- * @group L1 Control
+ * @group Fixed Wing TECS
*/
PARAM_DEFINE_FLOAT(FW_T_RLL2THR, 10.0f);
/**
* Speed <--> Altitude priority
*
- * This parameter adjusts the amount of weighting that the pitch control
- * applies to speed vs height errors. Setting it to 0.0 will cause the
- * pitch control to control height and ignore speed errors. This will
- * normally improve height accuracy but give larger airspeed errors.
- * Setting it to 2.0 will cause the pitch control loop to control speed
- * and ignore height errors. This will normally reduce airspeed errors,
- * but give larger height errors. The default value of 1.0 allows the pitch
- * control to simultaneously control height and speed.
- * Note to Glider Pilots - set this parameter to 2.0 (The glider will
+ * This parameter adjusts the amount of weighting that the pitch control
+ * applies to speed vs height errors. Setting it to 0.0 will cause the
+ * pitch control to control height and ignore speed errors. This will
+ * normally improve height accuracy but give larger airspeed errors.
+ * Setting it to 2.0 will cause the pitch control loop to control speed
+ * and ignore height errors. This will normally reduce airspeed errors,
+ * but give larger height errors. The default value of 1.0 allows the pitch
+ * control to simultaneously control height and speed.
+ * Note to Glider Pilots - set this parameter to 2.0 (The glider will
* adjust its pitch angle to maintain airspeed, ignoring changes in height).
*
- * @group L1 Control
+ * @group Fixed Wing TECS
*/
PARAM_DEFINE_FLOAT(FW_T_SPDWEIGHT, 1.0f);
/**
* Pitch damping factor
*
- * This is the damping gain for the pitch demand loop. Increase to add
- * damping to correct for oscillations in height. The default value of 0.0
- * will work well provided the pitch to servo controller has been tuned
+ * This is the damping gain for the pitch demand loop. Increase to add
+ * damping to correct for oscillations in height. The default value of 0.0
+ * will work well provided the pitch to servo controller has been tuned
* properly.
*
- * @group L1 Control
+ * @group Fixed Wing TECS
*/
PARAM_DEFINE_FLOAT(FW_T_PTCH_DAMP, 0.0f);
/**
* Height rate P factor
*
- * @group L1 Control
+ * @group Fixed Wing TECS
*/
PARAM_DEFINE_FLOAT(FW_T_HRATE_P, 0.05f);
/**
+ * Height rate FF factor
+ *
+ * @group Fixed Wing TECS
+ */
+PARAM_DEFINE_FLOAT(FW_T_HRATE_FF, 0.0f);
+
+/**
* Speed rate P factor
*
- * @group L1 Control
+ * @group Fixed Wing TECS
*/
PARAM_DEFINE_FLOAT(FW_T_SRATE_P, 0.05f);
@@ -345,18 +386,23 @@ PARAM_DEFINE_FLOAT(FW_LND_ANG, 5.0f);
PARAM_DEFINE_FLOAT(FW_LND_HVIRT, 10.0f);
/**
- * Landing flare altitude (relative)
+ * Landing flare altitude (relative to landing altitude)
*
+ * @unit meter
* @group L1 Control
*/
-PARAM_DEFINE_FLOAT(FW_LND_FLALT, 15.0f);
+PARAM_DEFINE_FLOAT(FW_LND_FLALT, 8.0f);
/**
- * Landing throttle limit altitude (relative)
+ * Landing throttle limit altitude (relative landing altitude)
+ *
+ * Default of -1.0f lets the system default to applying throttle
+ * limiting at 2/3 of the flare altitude.
*
+ * @unit meter
* @group L1 Control
*/
-PARAM_DEFINE_FLOAT(FW_LND_TLALT, 5.0f);
+PARAM_DEFINE_FLOAT(FW_LND_TLALT, -1.0f);
/**
* Landing heading hold horizontal distance
@@ -366,12 +412,28 @@ PARAM_DEFINE_FLOAT(FW_LND_TLALT, 5.0f);
PARAM_DEFINE_FLOAT(FW_LND_HHDIST, 15.0f);
/**
- * Relative altitude threshold for range finder measurements for use during landing
+ * Enable or disable usage of terrain estimate during landing
*
- * range finder measurements will only be used if the estimated relative altitude (gobal_pos.alt - landing_waypoint.alt) is < FW_LND_RFRALT
- * set to < 0 to disable
- * the correct value of this parameter depends on your range measuring device as well as on the terrain at the landing location
+ * 0: disabled, 1: enabled
*
* @group L1 Control
*/
-PARAM_DEFINE_FLOAT(FW_LND_RFRALT, -1.0f);
+PARAM_DEFINE_INT32(FW_LND_USETER, 0);
+
+/**
+ * Flare, minimum pitch
+ *
+ * Minimum pitch during flare, a positive sign means nose up
+ * Applied once FW_LND_TLALT is reached
+ *
+ */
+PARAM_DEFINE_FLOAT(FW_FLARE_PMIN, 2.5f);
+
+/**
+ * Flare, maximum pitch
+ *
+ * Maximum pitch during flare, a positive sign means nose up
+ * Applied once FW_LND_TLALT is reached
+ *
+ */
+PARAM_DEFINE_FLOAT(FW_FLARE_PMAX, 15.0f);
diff --git a/src/modules/fw_pos_control_l1/landingslope.cpp b/src/modules/fw_pos_control_l1/landingslope.cpp
index 8ce465fe8..42e00da05 100644
--- a/src/modules/fw_pos_control_l1/landingslope.cpp
+++ b/src/modules/fw_pos_control_l1/landingslope.cpp
@@ -46,16 +46,16 @@
#include <unistd.h>
#include <mathlib/mathlib.h>
-void Landingslope::update(float landing_slope_angle_rad,
- float flare_relative_alt,
- float motor_lim_relative_alt,
- float H1_virt)
+void Landingslope::update(float landing_slope_angle_rad_new,
+ float flare_relative_alt_new,
+ float motor_lim_relative_alt_new,
+ float H1_virt_new)
{
- _landing_slope_angle_rad = landing_slope_angle_rad;
- _flare_relative_alt = flare_relative_alt;
- _motor_lim_relative_alt = motor_lim_relative_alt;
- _H1_virt = H1_virt;
+ _landing_slope_angle_rad = landing_slope_angle_rad_new;
+ _flare_relative_alt = flare_relative_alt_new;
+ _motor_lim_relative_alt = motor_lim_relative_alt_new;
+ _H1_virt = H1_virt_new;
calculateSlopeValues();
}
diff --git a/src/modules/fw_pos_control_l1/landingslope.h b/src/modules/fw_pos_control_l1/landingslope.h
index b54fd501c..a5975ad43 100644
--- a/src/modules/fw_pos_control_l1/landingslope.h
+++ b/src/modules/fw_pos_control_l1/landingslope.h
@@ -123,10 +123,10 @@ public:
float getFlareCurveAbsoluteAltitudeSave(float wp_distance, float bearing_lastwp_currwp, float bearing_airplane_currwp, float wp_altitude);
- void update(float landing_slope_angle_rad,
- float flare_relative_alt,
- float motor_lim_relative_alt,
- float H1_virt);
+ void update(float landing_slope_angle_rad_new,
+ float flare_relative_alt_new,
+ float motor_lim_relative_alt_new,
+ float H1_virt_new);
inline float landing_slope_angle_rad() {return _landing_slope_angle_rad;}
diff --git a/src/modules/fw_pos_control_l1/module.mk b/src/modules/fw_pos_control_l1/module.mk
index cf419ec7e..15b575b50 100644
--- a/src/modules/fw_pos_control_l1/module.mk
+++ b/src/modules/fw_pos_control_l1/module.mk
@@ -1,6 +1,6 @@
############################################################################
#
-# Copyright (c) 2013 PX4 Development Team. All rights reserved.
+# Copyright (c) 2013, 2014 PX4 Development Team. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
@@ -39,4 +39,9 @@ MODULE_COMMAND = fw_pos_control_l1
SRCS = fw_pos_control_l1_main.cpp \
fw_pos_control_l1_params.c \
- landingslope.cpp
+ landingslope.cpp \
+ mtecs/mTecs.cpp \
+ mtecs/limitoverride.cpp \
+ mtecs/mTecs_params.c
+
+MODULE_STACKSIZE = 1200
diff --git a/src/modules/fw_pos_control_l1/mtecs/limitoverride.cpp b/src/modules/fw_pos_control_l1/mtecs/limitoverride.cpp
new file mode 100644
index 000000000..58795edb6
--- /dev/null
+++ b/src/modules/fw_pos_control_l1/mtecs/limitoverride.cpp
@@ -0,0 +1,71 @@
+/****************************************************************************
+ *
+ * Copyright (c) 2013 PX4 Development Team. All rights reserved.
+ * Author: @author Thomas Gubler <thomasgubler@gmail.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+
+/**
+ * @file limitoverride.cpp
+ *
+ * @author Thomas Gubler <thomasgubler@gmail.com>
+ */
+
+#include "limitoverride.h"
+
+namespace fwPosctrl {
+
+bool LimitOverride::applyOverride(BlockOutputLimiter &outputLimiterThrottle,
+ BlockOutputLimiter &outputLimiterPitch)
+{
+ bool ret = false;
+
+ if (overrideThrottleMinEnabled) {
+ outputLimiterThrottle.setMin(overrideThrottleMin);
+ ret = true;
+ }
+ if (overrideThrottleMaxEnabled) {
+ outputLimiterThrottle.setMax(overrideThrottleMax);
+ ret = true;
+ }
+ if (overridePitchMinEnabled) {
+ outputLimiterPitch.setMin(overridePitchMin);
+ ret = true;
+ }
+ if (overridePitchMaxEnabled) {
+ outputLimiterPitch.setMax(overridePitchMax);
+ ret = true;
+ }
+
+ return ret;
+}
+
+} /* namespace fwPosctrl */
diff --git a/src/modules/fw_pos_control_l1/mtecs/limitoverride.h b/src/modules/fw_pos_control_l1/mtecs/limitoverride.h
new file mode 100644
index 000000000..64c2e7bbd
--- /dev/null
+++ b/src/modules/fw_pos_control_l1/mtecs/limitoverride.h
@@ -0,0 +1,107 @@
+/****************************************************************************
+ *
+ * Copyright (c) 2013 PX4 Development Team. All rights reserved.
+ * Author: @author Thomas Gubler <thomasgubler@gmail.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+
+/**
+ * @file limitoverride.h
+ *
+ * @author Thomas Gubler <thomasgubler@gmail.com>
+ */
+
+
+#ifndef LIMITOVERRIDE_H_
+#define LIMITOVERRIDE_H_
+
+#include "mTecs_blocks.h"
+
+namespace fwPosctrl
+{
+
+/* A small class which provides helper functions to override control output limits which are usually set by
+* parameters in special cases
+*/
+class LimitOverride
+{
+public:
+ LimitOverride() :
+ overrideThrottleMinEnabled(false),
+ overrideThrottleMaxEnabled(false),
+ overridePitchMinEnabled(false),
+ overridePitchMaxEnabled(false)
+ {};
+
+ ~LimitOverride() {};
+
+ /*
+ * Override the limits of the outputlimiter instances given by the arguments with the limits saved in
+ * this class (if enabled)
+ * @return true if the limit was applied
+ */
+ bool applyOverride(BlockOutputLimiter &outputLimiterThrottle,
+ BlockOutputLimiter &outputLimiterPitch);
+
+ /* Functions to enable or disable the override */
+ void enableThrottleMinOverride(float value) { enable(&overrideThrottleMinEnabled,
+ &overrideThrottleMin, value); }
+ void disableThrottleMinOverride() { disable(&overrideThrottleMinEnabled); }
+ void enableThrottleMaxOverride(float value) { enable(&overrideThrottleMaxEnabled,
+ &overrideThrottleMax, value); }
+ void disableThrottleMaxOverride() { disable(&overrideThrottleMaxEnabled); }
+ void enablePitchMinOverride(float value) { enable(&overridePitchMinEnabled,
+ &overridePitchMin, value); }
+ void disablePitchMinOverride() { disable(&overridePitchMinEnabled); }
+ void enablePitchMaxOverride(float value) { enable(&overridePitchMaxEnabled,
+ &overridePitchMax, value); }
+ void disablePitchMaxOverride() { disable(&overridePitchMaxEnabled); }
+
+protected:
+ bool overrideThrottleMinEnabled;
+ float overrideThrottleMin;
+ bool overrideThrottleMaxEnabled;
+ float overrideThrottleMax;
+ bool overridePitchMinEnabled;
+ float overridePitchMin; //in degrees (replaces param values)
+ bool overridePitchMaxEnabled;
+ float overridePitchMax; //in degrees (replaces param values)
+
+ /* Enable a specific limit override */
+ void enable(bool *flag, float *limit, float value) { *flag = true; *limit = value; };
+
+ /* Disable a specific limit override */
+ void disable(bool *flag) { *flag = false; };
+};
+
+} /* namespace fwPosctrl */
+
+#endif /* LIMITOVERRIDE_H_ */
diff --git a/src/modules/fw_pos_control_l1/mtecs/mTecs.cpp b/src/modules/fw_pos_control_l1/mtecs/mTecs.cpp
new file mode 100644
index 000000000..bffeefc1f
--- /dev/null
+++ b/src/modules/fw_pos_control_l1/mtecs/mTecs.cpp
@@ -0,0 +1,317 @@
+/****************************************************************************
+ *
+ * Copyright (c) 2013 PX4 Development Team. All rights reserved.
+ * Author: @author Thomas Gubler <thomasgubler@gmail.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+
+/**
+ * @file mTecs.cpp
+ *
+ * @author Thomas Gubler <thomasgubler@gmail.com>
+ */
+
+#include "mTecs.h"
+
+#include <lib/geo/geo.h>
+#include <stdio.h>
+
+namespace fwPosctrl {
+
+mTecs::mTecs() :
+ SuperBlock(NULL, "MT"),
+ /* Parameters */
+ _mTecsEnabled(this, "ENABLED"),
+ _airspeedMin(this, "FW_AIRSPD_MIN", false),
+ /* Publications */
+ _status(&getPublications(), ORB_ID(tecs_status)),
+ /* control blocks */
+ _controlTotalEnergy(this, "THR"),
+ _controlEnergyDistribution(this, "PIT", true),
+ _controlAltitude(this, "FPA", true),
+ _controlAirSpeed(this, "ACC"),
+ _flightPathAngleLowpass(this, "FPA_LP"),
+ _altitudeLowpass(this, "ALT_LP"),
+ _airspeedLowpass(this, "A_LP"),
+ _airspeedDerivative(this, "AD"),
+ _throttleSp(0.0f),
+ _pitchSp(0.0f),
+ _BlockOutputLimiterTakeoffThrottle(this, "TKF_THR"),
+ _BlockOutputLimiterTakeoffPitch(this, "TKF_PIT", true),
+ _BlockOutputLimiterUnderspeedThrottle(this, "USP_THR"),
+ _BlockOutputLimiterUnderspeedPitch(this, "USP_PIT", true),
+ _BlockOutputLimiterLandThrottle(this, "LND_THR"),
+ _BlockOutputLimiterLandPitch(this, "LND_PIT", true),
+ timestampLastIteration(hrt_absolute_time()),
+ _firstIterationAfterReset(true),
+ _dtCalculated(false),
+ _counter(0),
+ _debug(false)
+{
+}
+
+mTecs::~mTecs()
+{
+}
+
+int mTecs::updateAltitudeSpeed(float flightPathAngle, float altitude, float altitudeSp, float airspeed,
+ float airspeedSp, tecs_mode mode, LimitOverride limitOverride)
+{
+ /* check if all input arguments are numbers and abort if not so */
+ if (!isfinite(flightPathAngle) || !isfinite(altitude) ||
+ !isfinite(altitudeSp) || !isfinite(airspeed) || !isfinite(airspeedSp) || !isfinite(mode)) {
+ return -1;
+ }
+
+ /* time measurement */
+ updateTimeMeasurement();
+
+ /* Filter altitude */
+ float altitudeFiltered = _altitudeLowpass.update(altitude);
+
+
+ /* calculate flight path angle setpoint from altitude setpoint */
+ float flightPathAngleSp = _controlAltitude.update(altitudeSp - altitudeFiltered);
+
+ /* Debug output */
+ if (_counter % 10 == 0) {
+ debug("***");
+ debug("updateAltitudeSpeed: altitudeSp %.4f, altitude %.4f, altitude filtered %.4f, flightPathAngleSp %.4f", (double)altitudeSp, (double)altitude, (double)altitudeFiltered, (double)flightPathAngleSp);
+ }
+
+ /* Write part of the status message */
+ _status.altitudeSp = altitudeSp;
+ _status.altitude_filtered = altitudeFiltered;
+
+
+ /* use flightpath angle setpoint for total energy control */
+ return updateFlightPathAngleSpeed(flightPathAngle, flightPathAngleSp, airspeed,
+ airspeedSp, mode, limitOverride);
+}
+
+int mTecs::updateFlightPathAngleSpeed(float flightPathAngle, float flightPathAngleSp, float airspeed,
+ float airspeedSp, tecs_mode mode, LimitOverride limitOverride)
+{
+ /* check if all input arguments are numbers and abort if not so */
+ if (!isfinite(flightPathAngle) || !isfinite(flightPathAngleSp) ||
+ !isfinite(airspeed) || !isfinite(airspeedSp) || !isfinite(mode)) {
+ return -1;
+ }
+
+ /* time measurement */
+ updateTimeMeasurement();
+
+ /* Filter airspeed */
+ float airspeedFiltered = _airspeedLowpass.update(airspeed);
+
+ /* calculate longitudinal acceleration setpoint from airspeed setpoint*/
+ float accelerationLongitudinalSp = _controlAirSpeed.update(airspeedSp - airspeedFiltered);
+
+ /* Debug output */
+ if (_counter % 10 == 0) {
+ debug("updateFlightPathAngleSpeed airspeedSp %.4f, airspeed %.4f airspeedFiltered %.4f,"
+ "accelerationLongitudinalSp%.4f",
+ (double)airspeedSp, (double)airspeed,
+ (double)airspeedFiltered, (double)accelerationLongitudinalSp);
+ }
+
+ /* Write part of the status message */
+ _status.airspeedSp = airspeedSp;
+ _status.airspeed_filtered = airspeedFiltered;
+
+
+ /* use longitudinal acceleration setpoint for total energy control */
+ return updateFlightPathAngleAcceleration(flightPathAngle, flightPathAngleSp, airspeedFiltered,
+ accelerationLongitudinalSp, mode, limitOverride);
+}
+
+int mTecs::updateFlightPathAngleAcceleration(float flightPathAngle, float flightPathAngleSp, float airspeedFiltered,
+ float accelerationLongitudinalSp, tecs_mode mode, LimitOverride limitOverride)
+{
+ /* check if all input arguments are numbers and abort if not so */
+ if (!isfinite(flightPathAngle) || !isfinite(flightPathAngleSp) ||
+ !isfinite(airspeedFiltered) || !isfinite(accelerationLongitudinalSp) || !isfinite(mode)) {
+ return -1;
+ }
+ /* time measurement */
+ updateTimeMeasurement();
+
+ /* update parameters first */
+ updateParams();
+
+ /* Filter flightpathangle */
+ float flightPathAngleFiltered = _flightPathAngleLowpass.update(flightPathAngle);
+
+ /* calculate values (energies) */
+ float flightPathAngleError = flightPathAngleSp - flightPathAngleFiltered;
+ float airspeedDerivative = 0.0f;
+ if(_airspeedDerivative.getDt() > 0.0f) {
+ airspeedDerivative = _airspeedDerivative.update(airspeedFiltered);
+ }
+ float airspeedDerivativeNorm = airspeedDerivative / CONSTANTS_ONE_G;
+ float airspeedDerivativeSp = accelerationLongitudinalSp;
+ float airspeedDerivativeNormSp = airspeedDerivativeSp / CONSTANTS_ONE_G;
+ float airspeedDerivativeNormError = airspeedDerivativeNormSp - airspeedDerivativeNorm;
+
+ float totalEnergyRate = flightPathAngleFiltered + airspeedDerivativeNorm;
+ float totalEnergyRateError = flightPathAngleError + airspeedDerivativeNormError;
+ float totalEnergyRateSp = flightPathAngleSp + airspeedDerivativeNormSp;
+ float totalEnergyRateError2 = totalEnergyRateSp - totalEnergyRate;
+
+ float energyDistributionRate = flightPathAngleFiltered - airspeedDerivativeNorm;
+ float energyDistributionRateError = flightPathAngleError - airspeedDerivativeNormError;
+ float energyDistributionRateSp = flightPathAngleSp - airspeedDerivativeNormSp;
+ float energyDistributionRateError2 = energyDistributionRateSp - energyDistributionRate;
+
+ /* Debug output */
+ if (_counter % 10 == 0) {
+ debug("totalEnergyRateSp %.2f, totalEnergyRate %.2f, totalEnergyRateError %.2f totalEnergyRateError2 %.2f airspeedDerivativeNorm %.4f",
+ (double)totalEnergyRateSp, (double)totalEnergyRate, (double)totalEnergyRateError, (double)totalEnergyRateError2, (double)airspeedDerivativeNorm);
+ debug("energyDistributionRateSp %.2f, energyDistributionRate %.2f, energyDistributionRateError %.2f energyDistributionRateError2 %.2f",
+ (double)energyDistributionRateSp, (double)energyDistributionRate, (double)energyDistributionRateError, (double)energyDistributionRateError2);
+ }
+
+ /* Check airspeed: if below safe value switch to underspeed mode (if not in land or takeoff mode) */
+ if (mode != TECS_MODE_LAND && mode != TECS_MODE_TAKEOFF && airspeedFiltered < _airspeedMin.get()) {
+ mode = TECS_MODE_UNDERSPEED;
+ }
+
+ /* Set special ouput limiters if we are not in TECS_MODE_NORMAL */
+ BlockOutputLimiter *outputLimiterThrottle = &_controlTotalEnergy.getOutputLimiter();
+ BlockOutputLimiter *outputLimiterPitch = &_controlEnergyDistribution.getOutputLimiter();
+ if (mode == TECS_MODE_TAKEOFF) {
+ outputLimiterThrottle = &_BlockOutputLimiterTakeoffThrottle;
+ outputLimiterPitch = &_BlockOutputLimiterTakeoffPitch;
+ } else if (mode == TECS_MODE_LAND) {
+ // only limit pitch but do not limit throttle
+ outputLimiterPitch = &_BlockOutputLimiterLandPitch;
+ } else if (mode == TECS_MODE_LAND_THROTTLELIM) {
+ outputLimiterThrottle = &_BlockOutputLimiterLandThrottle;
+ outputLimiterPitch = &_BlockOutputLimiterLandPitch;
+ } else if (mode == TECS_MODE_UNDERSPEED) {
+ outputLimiterThrottle = &_BlockOutputLimiterUnderspeedThrottle;
+ outputLimiterPitch = &_BlockOutputLimiterUnderspeedPitch;
+ }
+
+ /* Apply overrride given by the limitOverride argument (this is used for limits which are not given by
+ * parameters such as pitch limits with takeoff waypoints or throttle limits when the launchdetector
+ * is running) */
+ limitOverride.applyOverride(*outputLimiterThrottle, *outputLimiterPitch);
+
+ /* Write part of the status message */
+ _status.flightPathAngleSp = flightPathAngleSp;
+ _status.flightPathAngle = flightPathAngle;
+ _status.flightPathAngleFiltered = flightPathAngleFiltered;
+ _status.airspeedDerivativeSp = airspeedDerivativeSp;
+ _status.airspeedDerivative = airspeedDerivative;
+ _status.totalEnergyRateSp = totalEnergyRateSp;
+ _status.totalEnergyRate = totalEnergyRate;
+ _status.energyDistributionRateSp = energyDistributionRateSp;
+ _status.energyDistributionRate = energyDistributionRate;
+ _status.mode = mode;
+
+ /** update control blocks **/
+ /* update total energy rate control block */
+ _throttleSp = _controlTotalEnergy.update(totalEnergyRateSp, totalEnergyRateError, outputLimiterThrottle);
+
+ /* update energy distribution rate control block */
+ _pitchSp = _controlEnergyDistribution.update(energyDistributionRateSp, energyDistributionRateError, outputLimiterPitch);
+
+
+ if (_counter % 10 == 0) {
+ debug("_throttleSp %.1f, _pitchSp %.1f, flightPathAngleSp %.1f, flightPathAngle %.1f accelerationLongitudinalSp %.1f, airspeedDerivative %.1f",
+ (double)_throttleSp, (double)_pitchSp,
+ (double)flightPathAngleSp, (double)flightPathAngle,
+ (double)accelerationLongitudinalSp, (double)airspeedDerivative);
+ }
+
+ /* publish status messge */
+ _status.update();
+
+ /* clean up */
+ _firstIterationAfterReset = false;
+ _dtCalculated = false;
+
+ _counter++;
+
+ return 0;
+}
+
+void mTecs::resetIntegrators()
+{
+ _controlTotalEnergy.getIntegral().setY(0.0f);
+ _controlEnergyDistribution.getIntegral().setY(0.0f);
+ timestampLastIteration = hrt_absolute_time();
+ _firstIterationAfterReset = true;
+}
+
+void mTecs::resetDerivatives(float airspeed)
+{
+ _airspeedDerivative.setU(airspeed);
+}
+
+
+void mTecs::updateTimeMeasurement()
+{
+ if (!_dtCalculated) {
+ float deltaTSeconds = 0.0f;
+ if (!_firstIterationAfterReset) {
+ hrt_abstime timestampNow = hrt_absolute_time();
+ deltaTSeconds = (float)(timestampNow - timestampLastIteration) * 1e-6f;
+ timestampLastIteration = timestampNow;
+ }
+ setDt(deltaTSeconds);
+
+ _dtCalculated = true;
+ }
+}
+
+void mTecs::debug_print(const char *fmt, va_list args)
+{
+ fprintf(stderr, "%s: ", "[mtecs]");
+ vfprintf(stderr, fmt, args);
+
+ fprintf(stderr, "\n");
+}
+
+void mTecs::debug(const char *fmt, ...) {
+
+ if (!_debug) {
+ return;
+ }
+
+ va_list args;
+
+ va_start(args, fmt);
+ debug_print(fmt, args);
+}
+
+} /* namespace fwPosctrl */
diff --git a/src/modules/fw_pos_control_l1/mtecs/mTecs.h b/src/modules/fw_pos_control_l1/mtecs/mTecs.h
new file mode 100644
index 000000000..ae6867d38
--- /dev/null
+++ b/src/modules/fw_pos_control_l1/mtecs/mTecs.h
@@ -0,0 +1,156 @@
+/****************************************************************************
+ *
+ * Copyright (c) 2013 PX4 Development Team. All rights reserved.
+ * Author: @author Thomas Gubler <thomasgubler@gmail.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+
+/**
+ * @file mTecs.h
+ *
+ * @author Thomas Gubler <thomasgubler@gmail.com>
+ */
+
+
+#ifndef MTECS_H_
+#define MTECS_H_
+
+#include "mTecs_blocks.h"
+#include "limitoverride.h"
+
+#include <controllib/block/BlockParam.hpp>
+#include <drivers/drv_hrt.h>
+#include <uORB/Publication.hpp>
+#include <uORB/topics/tecs_status.h>
+
+namespace fwPosctrl
+{
+
+/* Main class of the mTecs */
+class mTecs : public control::SuperBlock
+{
+public:
+ mTecs();
+ virtual ~mTecs();
+
+ /*
+ * Control in altitude setpoint and speed mode
+ */
+ int updateAltitudeSpeed(float flightPathAngle, float altitude, float altitudeSp, float airspeed,
+ float airspeedSp, tecs_mode mode, LimitOverride limitOverride);
+
+ /*
+ * Control in flightPathAngle setpoint (flollow a slope etc.) and speed mode
+ */
+ int updateFlightPathAngleSpeed(float flightPathAngle, float flightPathAngleSp, float airspeed,
+ float airspeedSp, tecs_mode mode, LimitOverride limitOverride);
+
+ /*
+ * Control in flightPathAngle setpoint (flollow a slope etc.) and acceleration mode (base case)
+ */
+ int updateFlightPathAngleAcceleration(float flightPathAngle, float flightPathAngleSp, float airspeedFiltered,
+ float accelerationLongitudinalSp, tecs_mode mode, LimitOverride limitOverride);
+
+ /*
+ * Reset all integrators
+ */
+ void resetIntegrators();
+
+ /*
+ * Reset all derivative calculations
+ */
+ void resetDerivatives(float airspeed);
+
+ /* Accessors */
+ bool getEnabled() { return _mTecsEnabled.get() > 0; }
+ float getThrottleSetpoint() { return _throttleSp; }
+ float getPitchSetpoint() { return _pitchSp; }
+ float airspeedLowpassUpdate(float input) { return _airspeedLowpass.update(input); }
+
+protected:
+ /* parameters */
+ control::BlockParamInt _mTecsEnabled; /**< 1 if mTecs is enabled */
+ control::BlockParamFloat _airspeedMin; /**< minimal airspeed */
+
+ /* Publications */
+ uORB::Publication<tecs_status_s> _status; /**< publish internal values for logging */
+
+ /* control blocks */
+ BlockFFPILimitedCustom _controlTotalEnergy; /**< FFPI controller for total energy control: output
+ is throttle */
+ BlockFFPILimitedCustom _controlEnergyDistribution; /**< FFPI controller for energy distribution control:
+ output is pitch */
+ BlockPDLimited _controlAltitude; /**< PD controller for altitude: output is the flight
+ path angle setpoint */
+ BlockPDLimited _controlAirSpeed; /**< PD controller for airspeed: output is acceleration
+ setpoint */
+
+ /* Other calculation Blocks */
+ control::BlockLowPass _flightPathAngleLowpass; /**< low pass filter for the flight path angle */
+ control::BlockLowPass _altitudeLowpass; /**< low pass filter for altitude */
+ control::BlockLowPass _airspeedLowpass; /**< low pass filter for airspeed */
+ control::BlockDerivative _airspeedDerivative; /**< airspeed derivative calulation */
+
+ /* Output setpoints */
+ float _throttleSp; /**< Throttle Setpoint from 0 to 1 */
+ float _pitchSp; /**< Pitch Setpoint from -pi to pi */
+
+ /* Output Limits in special modes */
+ BlockOutputLimiter _BlockOutputLimiterTakeoffThrottle; /**< Throttle Limits during takeoff */
+ BlockOutputLimiter _BlockOutputLimiterTakeoffPitch; /**< Pitch Limit during takeoff */
+ BlockOutputLimiter _BlockOutputLimiterUnderspeedThrottle; /**< Throttle Limits when underspeed is detected */
+ BlockOutputLimiter _BlockOutputLimiterUnderspeedPitch; /**< Pitch Limit when underspeed is detected */
+ BlockOutputLimiter _BlockOutputLimiterLandThrottle; /**< Throttle Limits during landing (only in
+ last phase)*/
+ BlockOutputLimiter _BlockOutputLimiterLandPitch; /**< Pitch Limit during landing */
+
+ /* Time measurements */
+ hrt_abstime timestampLastIteration; /**< Saves the result of hrt_absolute_time() of the last iteration */
+
+ bool _firstIterationAfterReset; /**< True during the first iteration after a reset */
+ bool _dtCalculated; /**< True if dt has been calculated in this iteration */
+
+ int _counter;
+ bool _debug; ///< Set true to enable debug output
+
+
+ static void debug_print(const char *fmt, va_list args);
+ void debug(const char *fmt, ...);
+
+ /*
+ * Measure and update the time step dt if this was not already done in the current iteration
+ */
+ void updateTimeMeasurement();
+};
+
+} /* namespace fwPosctrl */
+
+#endif /* MTECS_H_ */
diff --git a/src/modules/fw_pos_control_l1/mtecs/mTecs_blocks.h b/src/modules/fw_pos_control_l1/mtecs/mTecs_blocks.h
new file mode 100644
index 000000000..bb2065472
--- /dev/null
+++ b/src/modules/fw_pos_control_l1/mtecs/mTecs_blocks.h
@@ -0,0 +1,220 @@
+/****************************************************************************
+ *
+ * Copyright (c) 2013 PX4 Development Team. All rights reserved.
+ * Author: @author Thomas Gubler <thomasgubler@gmail.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+
+/**
+ * @file mTecs_blocks.h
+ *
+ * Custom blocks for the mTecs
+ *
+ * @author Thomas Gubler <thomasgubler@gmail.com>
+ */
+
+#pragma once
+
+#include <controllib/blocks.hpp>
+#include <systemlib/err.h>
+
+namespace fwPosctrl
+{
+
+using namespace control;
+
+/* An block which can be used to limit the output */
+class BlockOutputLimiter: public SuperBlock
+{
+public:
+// methods
+ BlockOutputLimiter(SuperBlock *parent, const char *name, bool fAngularLimit = false) :
+ SuperBlock(parent, name),
+ _isAngularLimit(fAngularLimit),
+ _min(this, "MIN"),
+ _max(this, "MAX")
+ {};
+ virtual ~BlockOutputLimiter() {};
+ /*
+ * Imposes the limits given by _min and _max on value
+ *
+ * @param value is changed to be on the interval _min to _max
+ * @param difference if the value is changed this corresponds to the change of value * (-1)
+ * otherwise unchanged
+ * @return: true if the limit is applied, false otherwise
+ */
+ bool limit(float& value, float& difference) {
+ float minimum = getIsAngularLimit() ? getMin() * M_DEG_TO_RAD_F : getMin();
+ float maximum = getIsAngularLimit() ? getMax() * M_DEG_TO_RAD_F : getMax();
+ if (value < minimum) {
+ difference = value - minimum;
+ value = minimum;
+ return true;
+ } else if (value > maximum) {
+ difference = value - maximum;
+ value = maximum;
+ return true;
+ }
+ return false;
+ }
+//accessor:
+ bool getIsAngularLimit() {return _isAngularLimit ;}
+ float getMin() { return _min.get(); }
+ float getMax() { return _max.get(); }
+ void setMin(float value) { _min.set(value); }
+ void setMax(float value) { _max.set(value); }
+protected:
+//attributes
+ bool _isAngularLimit;
+ control::BlockParamFloat _min;
+ control::BlockParamFloat _max;
+};
+
+
+/* A combination of feed forward, P and I gain using the output limiter*/
+class BlockFFPILimited: public SuperBlock
+{
+public:
+// methods
+ BlockFFPILimited(SuperBlock *parent, const char *name, bool isAngularLimit = false) :
+ SuperBlock(parent, name),
+ _outputLimiter(this, "", isAngularLimit),
+ _integral(this, "I"),
+ _kFF(this, "FF"),
+ _kP(this, "P"),
+ _kI(this, "I"),
+ _offset(this, "OFF")
+ {};
+ virtual ~BlockFFPILimited() {};
+ float update(float inputValue, float inputError) { return calcLimitedOutput(inputValue, inputError, _outputLimiter); }
+// accessors
+ BlockIntegral &getIntegral() { return _integral; }
+ float getKFF() { return _kFF.get(); }
+ float getKP() { return _kP.get(); }
+ float getKI() { return _kI.get(); }
+ float getOffset() { return _offset.get(); }
+ BlockOutputLimiter &getOutputLimiter() { return _outputLimiter; };
+protected:
+ BlockOutputLimiter _outputLimiter;
+
+ float calcUnlimitedOutput(float inputValue, float inputError) {return getOffset() + getKFF() * inputValue + getKP() * inputError + getKI() * getIntegral().update(inputError);}
+ float calcLimitedOutput(float inputValue, float inputError, BlockOutputLimiter &outputLimiter) {
+ float difference = 0.0f;
+ float integralYPrevious = _integral.getY();
+ float output = calcUnlimitedOutput(inputValue, inputError);
+ if(outputLimiter.limit(output, difference) &&
+ (((difference < 0) && (getKI() * getIntegral().getY() < 0)) ||
+ ((difference > 0) && (getKI() * getIntegral().getY() > 0)))) {
+ getIntegral().setY(integralYPrevious);
+ }
+ return output;
+ }
+private:
+ BlockIntegral _integral;
+ BlockParamFloat _kFF;
+ BlockParamFloat _kP;
+ BlockParamFloat _kI;
+ BlockParamFloat _offset;
+};
+
+/* A combination of feed forward, P and I gain using the output limiter with the option to provide a special output limiter (for example for takeoff)*/
+class BlockFFPILimitedCustom: public BlockFFPILimited
+{
+public:
+// methods
+ BlockFFPILimitedCustom(SuperBlock *parent, const char *name, bool isAngularLimit = false) :
+ BlockFFPILimited(parent, name, isAngularLimit)
+ {};
+ virtual ~BlockFFPILimitedCustom() {};
+ float update(float inputValue, float inputError, BlockOutputLimiter *outputLimiter = NULL) {
+ return calcLimitedOutput(inputValue, inputError, outputLimiter == NULL ? _outputLimiter : *outputLimiter);
+ }
+};
+
+/* A combination of P gain and output limiter */
+class BlockPLimited: public SuperBlock
+{
+public:
+// methods
+ BlockPLimited(SuperBlock *parent, const char *name, bool isAngularLimit = false) :
+ SuperBlock(parent, name),
+ _kP(this, "P"),
+ _outputLimiter(this, "", isAngularLimit)
+ {};
+ virtual ~BlockPLimited() {};
+ float update(float input) {
+ float difference = 0.0f;
+ float output = getKP() * input;
+ getOutputLimiter().limit(output, difference);
+ return output;
+ }
+// accessors
+ BlockOutputLimiter &getOutputLimiter() { return _outputLimiter; };
+ float getKP() { return _kP.get(); }
+private:
+ control::BlockParamFloat _kP;
+ BlockOutputLimiter _outputLimiter;
+};
+
+/* A combination of P, D gains and output limiter */
+class BlockPDLimited: public SuperBlock
+{
+public:
+// methods
+ BlockPDLimited(SuperBlock *parent, const char *name, bool isAngularLimit = false) :
+ SuperBlock(parent, name),
+ _kP(this, "P"),
+ _kD(this, "D"),
+ _derivative(this, "D"),
+ _outputLimiter(this, "", isAngularLimit)
+ {};
+ virtual ~BlockPDLimited() {};
+ float update(float input) {
+ float difference = 0.0f;
+ float output = getKP() * input + (getDerivative().getDt() > 0.0f ? getKD() * getDerivative().update(input) : 0.0f);
+ getOutputLimiter().limit(output, difference);
+
+ return output;
+ }
+// accessors
+ float getKP() { return _kP.get(); }
+ float getKD() { return _kD.get(); }
+ BlockDerivative &getDerivative() { return _derivative; }
+ BlockOutputLimiter &getOutputLimiter() { return _outputLimiter; };
+private:
+ control::BlockParamFloat _kP;
+ control::BlockParamFloat _kD;
+ BlockDerivative _derivative;
+ BlockOutputLimiter _outputLimiter;
+};
+
+}
+
diff --git a/src/modules/fw_pos_control_l1/mtecs/mTecs_params.c b/src/modules/fw_pos_control_l1/mtecs/mTecs_params.c
new file mode 100644
index 000000000..58a1e9f6b
--- /dev/null
+++ b/src/modules/fw_pos_control_l1/mtecs/mTecs_params.c
@@ -0,0 +1,426 @@
+/****************************************************************************
+ *
+ * Copyright (c) 2013 PX4 Development Team. All rights reserved.
+ * Author: @author Thomas Gubler <thomasgubler@gmail.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+
+/**
+ * @file mTecs_params.c
+ *
+ * @author Thomas Gubler <thomasgubler@gmail.com>
+ */
+
+#include <nuttx/config.h>
+#include <systemlib/param/param.h>
+
+/*
+ * Controller parameters, accessible via MAVLink
+ */
+
+/**
+ * mTECS enabled
+ *
+ * Set to 1 to enable mTECS
+ *
+ * @min 0
+ * @max 1
+ * @group mTECS
+ */
+PARAM_DEFINE_INT32(MT_ENABLED, 0);
+
+/**
+ * Total Energy Rate Control Feedforward
+ * Maps the total energy rate setpoint to the throttle setpoint
+ *
+ * @min 0.0
+ * @max 10.0
+ * @group mTECS
+ */
+PARAM_DEFINE_FLOAT(MT_THR_FF, 0.7f);
+
+/**
+ * Total Energy Rate Control P
+ * Maps the total energy rate error to the throttle setpoint
+ *
+ * @min 0.0
+ * @max 10.0
+ * @group mTECS
+ */
+PARAM_DEFINE_FLOAT(MT_THR_P, 0.1f);
+
+/**
+ * Total Energy Rate Control I
+ * Maps the integrated total energy rate to the throttle setpoint
+ *
+ * @min 0.0
+ * @max 10.0
+ * @group mTECS
+ */
+PARAM_DEFINE_FLOAT(MT_THR_I, 0.25f);
+
+/**
+ * Total Energy Rate Control Offset (Cruise throttle sp)
+ *
+ * @min 0.0
+ * @max 10.0
+ * @group mTECS
+ */
+PARAM_DEFINE_FLOAT(MT_THR_OFF, 0.7f);
+
+/**
+ * Energy Distribution Rate Control Feedforward
+ * Maps the energy distribution rate setpoint to the pitch setpoint
+ *
+ * @min 0.0
+ * @max 10.0
+ * @group mTECS
+ */
+PARAM_DEFINE_FLOAT(MT_PIT_FF, 0.4f);
+
+/**
+ * Energy Distribution Rate Control P
+ * Maps the energy distribution rate error to the pitch setpoint
+ *
+ * @min 0.0
+ * @max 10.0
+ * @group mTECS
+ */
+PARAM_DEFINE_FLOAT(MT_PIT_P, 0.03f);
+
+/**
+ * Energy Distribution Rate Control I
+ * Maps the integrated energy distribution rate error to the pitch setpoint
+ *
+ * @min 0.0
+ * @max 10.0
+ * @group mTECS
+ */
+PARAM_DEFINE_FLOAT(MT_PIT_I, 0.03f);
+
+
+/**
+ * Total Energy Distribution Offset (Cruise pitch sp)
+ *
+ * @min 0.0
+ * @max 10.0
+ * @group mTECS
+ */
+PARAM_DEFINE_FLOAT(MT_PIT_OFF, 0.0f);
+
+/**
+ * Minimal Throttle Setpoint
+ *
+ * @min 0.0
+ * @max 1.0
+ * @group mTECS
+ */
+PARAM_DEFINE_FLOAT(MT_THR_MIN, 0.0f);
+
+/**
+ * Maximal Throttle Setpoint
+ *
+ * @min 0.0
+ * @max 1.0
+ * @group mTECS
+ */
+PARAM_DEFINE_FLOAT(MT_THR_MAX, 1.0f);
+
+/**
+ * Minimal Pitch Setpoint in Degrees
+ *
+ * @min -90.0f
+ * @max 90.0f
+ * @unit deg
+ * @group mTECS
+ */
+PARAM_DEFINE_FLOAT(MT_PIT_MIN, -45.0f);
+
+/**
+ * Maximal Pitch Setpoint in Degrees
+ *
+ * @min -90.0f
+ * @max 90.0f
+ * @unit deg
+ * @group mTECS
+ */
+PARAM_DEFINE_FLOAT(MT_PIT_MAX, 20.0f);
+
+/**
+ * Lowpass (cutoff freq.) for altitude
+ *
+ * @group mTECS
+ */
+PARAM_DEFINE_FLOAT(MT_ALT_LP, 1.0f);
+
+/**
+ * Lowpass (cutoff freq.) for the flight path angle
+ *
+ * @group mTECS
+ */
+PARAM_DEFINE_FLOAT(MT_FPA_LP, 1.0f);
+
+/**
+ * P gain for the altitude control
+ * Maps the altitude error to the flight path angle setpoint
+ *
+ * @min 0.0f
+ * @max 10.0f
+ * @group mTECS
+ */
+PARAM_DEFINE_FLOAT(MT_FPA_P, 0.3f);
+
+/**
+ * D gain for the altitude control
+ * Maps the change of altitude error to the flight path angle setpoint
+ *
+ * @min 0.0f
+ * @max 10.0f
+ * @group mTECS
+ */
+PARAM_DEFINE_FLOAT(MT_FPA_D, 0.0f);
+
+/**
+ * Lowpass for FPA error derivative calculation (see MT_FPA_D)
+ *
+ * @group mTECS
+ */
+PARAM_DEFINE_FLOAT(MT_FPA_D_LP, 1.0f);
+
+
+/**
+ * Minimal flight path angle setpoint
+ *
+ * @min -90.0f
+ * @max 90.0f
+ * @unit deg
+ * @group mTECS
+ */
+PARAM_DEFINE_FLOAT(MT_FPA_MIN, -20.0f);
+
+/**
+ * Maximal flight path angle setpoint
+ *
+ * @min -90.0f
+ * @max 90.0f
+ * @unit deg
+ * @group mTECS
+ */
+PARAM_DEFINE_FLOAT(MT_FPA_MAX, 30.0f);
+
+/**
+ * Lowpass (cutoff freq.) for airspeed
+ *
+ * @group mTECS
+ */
+PARAM_DEFINE_FLOAT(MT_A_LP, 0.5f);
+
+/**
+ * Airspeed derivative calculation lowpass
+ *
+ * @group mTECS
+ */
+PARAM_DEFINE_FLOAT(MT_AD_LP, 0.5f);
+
+/**
+ * P gain for the airspeed control
+ * Maps the airspeed error to the acceleration setpoint
+ *
+ * @min 0.0f
+ * @max 10.0f
+ * @group mTECS
+ */
+PARAM_DEFINE_FLOAT(MT_ACC_P, 0.3f);
+
+/**
+ * D gain for the airspeed control
+ * Maps the change of airspeed error to the acceleration setpoint
+ *
+ * @min 0.0f
+ * @max 10.0f
+ * @group mTECS
+ */
+PARAM_DEFINE_FLOAT(MT_ACC_D, 0.0f);
+
+/**
+ * Lowpass for ACC error derivative calculation (see MT_ACC_D)
+ *
+ * @group mTECS
+ */
+PARAM_DEFINE_FLOAT(MT_ACC_D_LP, 0.5f);
+
+/**
+ * Minimal acceleration (air)
+ *
+ * @unit m/s^2
+ * @group mTECS
+ */
+PARAM_DEFINE_FLOAT(MT_ACC_MIN, -40.0f);
+
+/**
+ * Maximal acceleration (air)
+ *
+* @unit m/s^2
+ * @group mTECS
+ */
+PARAM_DEFINE_FLOAT(MT_ACC_MAX, 40.0f);
+
+/**
+ * Minimal throttle during takeoff
+ *
+ * @min 0.0f
+ * @max 1.0f
+ * @group mTECS
+ */
+PARAM_DEFINE_FLOAT(MT_TKF_THR_MIN, 1.0f);
+
+/**
+ * Maximal throttle during takeoff
+ *
+ * @min 0.0f
+ * @max 1.0f
+ * @group mTECS
+ */
+PARAM_DEFINE_FLOAT(MT_TKF_THR_MAX, 1.0f);
+
+/**
+ * Minimal pitch during takeoff
+ *
+ * @min -90.0f
+ * @max 90.0f
+ * @unit deg
+ * @group mTECS
+ */
+PARAM_DEFINE_FLOAT(MT_TKF_PIT_MIN, 0.0f);
+
+/**
+ * Maximal pitch during takeoff
+ *
+ * @min -90.0f
+ * @max 90.0f
+ * @unit deg
+ * @group mTECS
+ */
+PARAM_DEFINE_FLOAT(MT_TKF_PIT_MAX, 45.0f);
+
+/**
+ * Minimal throttle in underspeed mode
+ *
+ * @min 0.0f
+ * @max 1.0f
+ * @group mTECS
+ */
+PARAM_DEFINE_FLOAT(MT_USP_THR_MIN, 1.0f);
+
+/**
+ * Maximal throttle in underspeed mode
+ *
+ * @min 0.0f
+ * @max 1.0f
+ * @group mTECS
+ */
+PARAM_DEFINE_FLOAT(MT_USP_THR_MAX, 1.0f);
+
+/**
+ * Minimal pitch in underspeed mode
+ *
+ * @min -90.0f
+ * @max 90.0f
+ * @unit deg
+ * @group mTECS
+ */
+PARAM_DEFINE_FLOAT(MT_USP_PIT_MIN, -45.0f);
+
+/**
+ * Maximal pitch in underspeed mode
+ *
+ * @min -90.0f
+ * @max 90.0f
+ * @unit deg
+ * @group mTECS
+ */
+PARAM_DEFINE_FLOAT(MT_USP_PIT_MAX, 0.0f);
+
+/**
+ * Minimal throttle in landing mode (only last phase of landing)
+ *
+ * @min 0.0f
+ * @max 1.0f
+ * @group mTECS
+ */
+PARAM_DEFINE_FLOAT(MT_LND_THR_MIN, 0.0f);
+
+/**
+ * Maximal throttle in landing mode (only last phase of landing)
+ *
+ * @min 0.0f
+ * @max 1.0f
+ * @group mTECS
+ */
+PARAM_DEFINE_FLOAT(MT_LND_THR_MAX, 0.0f);
+
+/**
+ * Minimal pitch in landing mode
+ *
+ * @min -90.0f
+ * @max 90.0f
+ * @unit deg
+ * @group mTECS
+ */
+PARAM_DEFINE_FLOAT(MT_LND_PIT_MIN, -5.0f);
+
+/**
+ * Maximal pitch in landing mode
+ *
+ * @min -90.0f
+ * @max 90.0f
+ * @unit deg
+ * @group mTECS
+ */
+PARAM_DEFINE_FLOAT(MT_LND_PIT_MAX, 15.0f);
+
+/**
+ * Integrator Limit for Total Energy Rate Control
+ *
+ * @min 0.0f
+ * @max 10.0f
+ * @group mTECS
+ */
+PARAM_DEFINE_FLOAT(MT_THR_I_MAX, 10.0f);
+
+/**
+ * Integrator Limit for Energy Distribution Rate Control
+ *
+ * @min 0.0f
+ * @max 10.0f
+ * @group mTECS
+ */
+PARAM_DEFINE_FLOAT(MT_PIT_I_MAX, 10.0f);
diff --git a/src/modules/gpio_led/gpio_led.c b/src/modules/gpio_led/gpio_led.c
index 6dfd22fdf..7758faed7 100644
--- a/src/modules/gpio_led/gpio_led.c
+++ b/src/modules/gpio_led/gpio_led.c
@@ -181,16 +181,13 @@ int gpio_led_main(int argc, char *argv[])
} else {
gpio_led_started = true;
warnx("start, using pin: %s", pin_name);
+ exit(0);
}
-
- exit(0);
-
-
} else if (!strcmp(argv[1], "stop")) {
if (gpio_led_started) {
gpio_led_started = false;
warnx("stop");
-
+ exit(0);
} else {
errx(1, "not running");
}
@@ -264,7 +261,7 @@ void gpio_led_cycle(FAR void *arg)
pattern = 0x2A; // *_*_*_ fast blink (armed, error)
} else if (priv->status.arming_state == ARMING_STATE_ARMED) {
- if (priv->status.battery_warning == VEHICLE_BATTERY_WARNING_NONE && priv->status.failsafe_state == FAILSAFE_STATE_NORMAL) {
+ if (priv->status.battery_warning == VEHICLE_BATTERY_WARNING_NONE && !priv->status.failsafe) {
pattern = 0x3f; // ****** solid (armed)
} else {
diff --git a/src/modules/gpio_led/module.mk b/src/modules/gpio_led/module.mk
index 3b8553489..70c75b10c 100644
--- a/src/modules/gpio_led/module.mk
+++ b/src/modules/gpio_led/module.mk
@@ -37,3 +37,5 @@
MODULE_COMMAND = gpio_led
SRCS = gpio_led.c
+
+MAXOPTIMIZATION = -Os
diff --git a/src/modules/mavlink/mavlink.c b/src/modules/mavlink/mavlink.c
index e49288a74..9afe74af1 100644
--- a/src/modules/mavlink/mavlink.c
+++ b/src/modules/mavlink/mavlink.c
@@ -68,14 +68,17 @@ PARAM_DEFINE_INT32(MAV_TYPE, MAV_TYPE_FIXED_WING);
* @group MAVLink
*/
PARAM_DEFINE_INT32(MAV_USEHILGPS, 0);
+/**
+ * Forward external setpoint messages
+ * If set to 1 incomming external setpoint messages will be directly forwarded to the controllers if in offboard
+ * control mode
+ * @group MAVLink
+ */
+PARAM_DEFINE_INT32(MAV_FWDEXTSP, 1);
mavlink_system_t mavlink_system = {
100,
- 50,
- MAV_TYPE_FIXED_WING,
- 0,
- 0,
- 0
+ 50
}; // System ID, 1-255, Component/Subsystem ID, 1-255
/*
diff --git a/src/modules/mavlink/mavlink_bridge_header.h b/src/modules/mavlink/mavlink_bridge_header.h
index 374d1511c..0cd08769e 100644
--- a/src/modules/mavlink/mavlink_bridge_header.h
+++ b/src/modules/mavlink/mavlink_bridge_header.h
@@ -44,7 +44,12 @@
__BEGIN_DECLS
-#define MAVLINK_USE_CONVENIENCE_FUNCTIONS
+/*
+ * We are NOT using convenience functions,
+ * but instead send messages with a custom function.
+ * So we do NOT do this:
+ * #define MAVLINK_USE_CONVENIENCE_FUNCTIONS
+ */
/* use efficient approach, see mavlink_helpers.h */
#define MAVLINK_SEND_UART_BYTES mavlink_send_uart_bytes
diff --git a/src/modules/mavlink/mavlink_ftp.cpp b/src/modules/mavlink/mavlink_ftp.cpp
new file mode 100644
index 000000000..f17497aa8
--- /dev/null
+++ b/src/modules/mavlink/mavlink_ftp.cpp
@@ -0,0 +1,853 @@
+/****************************************************************************
+ *
+ * Copyright (c) 2014 PX4 Development Team. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/// @file mavlink_ftp.cpp
+/// @author px4dev, Don Gagne <don@thegagnes.com>
+
+#include <crc32.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <errno.h>
+
+#include "mavlink_ftp.h"
+#include "mavlink_tests/mavlink_ftp_test.h"
+
+// Uncomment the line below to get better debug output. Never commit with this left on.
+//#define MAVLINK_FTP_DEBUG
+
+MavlinkFTP *
+MavlinkFTP::get_server(void)
+{
+ static MavlinkFTP server;
+ return &server;
+}
+
+MavlinkFTP::MavlinkFTP() :
+ _request_bufs{},
+ _request_queue{},
+ _request_queue_sem{},
+ _utRcvMsgFunc{},
+ _ftp_test{}
+{
+ // initialise the request freelist
+ dq_init(&_request_queue);
+ sem_init(&_request_queue_sem, 0, 1);
+
+ // initialize session list
+ for (size_t i=0; i<kMaxSession; i++) {
+ _session_fds[i] = -1;
+ }
+
+ // drop work entries onto the free list
+ for (unsigned i = 0; i < kRequestQueueSize; i++) {
+ _return_request(&_request_bufs[i]);
+ }
+
+}
+
+#ifdef MAVLINK_FTP_UNIT_TEST
+void
+MavlinkFTP::set_unittest_worker(ReceiveMessageFunc_t rcvMsgFunc, MavlinkFtpTest *ftp_test)
+{
+ _utRcvMsgFunc = rcvMsgFunc;
+ _ftp_test = ftp_test;
+}
+#endif
+
+void
+MavlinkFTP::handle_message(Mavlink* mavlink, mavlink_message_t *msg)
+{
+ // get a free request
+ struct Request* req = _get_request();
+
+ // if we couldn't get a request slot, just drop it
+ if (req == nullptr) {
+ warnx("Dropping FTP request: queue full\n");
+ return;
+ }
+
+ if (msg->msgid == MAVLINK_MSG_ID_FILE_TRANSFER_PROTOCOL) {
+ mavlink_msg_file_transfer_protocol_decode(msg, &req->message);
+
+#ifdef MAVLINK_FTP_UNIT_TEST
+ if (!_utRcvMsgFunc) {
+ warnx("Incorrectly written unit test\n");
+ return;
+ }
+ // We use fake ids when unit testing
+ req->serverSystemId = MavlinkFtpTest::serverSystemId;
+ req->serverComponentId = MavlinkFtpTest::serverComponentId;
+ req->serverChannel = MavlinkFtpTest::serverChannel;
+#else
+ // Not unit testing, use the real thing
+ req->serverSystemId = mavlink->get_system_id();
+ req->serverComponentId = mavlink->get_component_id();
+ req->serverChannel = mavlink->get_channel();
+#endif
+
+ // This is the system id we want to target when sending
+ req->targetSystemId = msg->sysid;
+
+ if (req->message.target_system == req->serverSystemId) {
+ req->mavlink = mavlink;
+#ifdef MAVLINK_FTP_UNIT_TEST
+ // We are running in Unit Test mode. Don't queue, just call _worket directly.
+ _process_request(req);
+#else
+ // We are running in normal mode. Queue the request to the worker
+ work_queue(LPWORK, &req->work, &MavlinkFTP::_worker_trampoline, req, 0);
+#endif
+ return;
+ }
+ }
+
+ _return_request(req);
+}
+
+/// @brief Queued static work queue routine to handle mavlink messages
+void
+MavlinkFTP::_worker_trampoline(void *arg)
+{
+ Request* req = reinterpret_cast<Request *>(arg);
+ MavlinkFTP* server = MavlinkFTP::get_server();
+
+ // call the server worker with the work item
+ server->_process_request(req);
+}
+
+/// @brief Processes an FTP message
+void
+MavlinkFTP::_process_request(Request *req)
+{
+ PayloadHeader *payload = reinterpret_cast<PayloadHeader *>(&req->message.payload[0]);
+
+ ErrorCode errorCode = kErrNone;
+
+ // basic sanity checks; must validate length before use
+ if (payload->size > kMaxDataLength) {
+ errorCode = kErrInvalidDataSize;
+ goto out;
+ }
+
+#ifdef MAVLINK_FTP_DEBUG
+ printf("ftp: channel %u opc %u size %u offset %u\n", req->serverChannel, payload->opcode, payload->size, payload->offset);
+#endif
+
+ switch (payload->opcode) {
+ case kCmdNone:
+ break;
+
+ case kCmdTerminateSession:
+ errorCode = _workTerminate(payload);
+ break;
+
+ case kCmdResetSessions:
+ errorCode = _workReset(payload);
+ break;
+
+ case kCmdListDirectory:
+ errorCode = _workList(payload);
+ break;
+
+ case kCmdOpenFileRO:
+ errorCode = _workOpen(payload, O_RDONLY);
+ break;
+
+ case kCmdCreateFile:
+ errorCode = _workOpen(payload, O_CREAT | O_EXCL | O_WRONLY);
+ break;
+
+ case kCmdOpenFileWO:
+ errorCode = _workOpen(payload, O_CREAT | O_WRONLY);
+ break;
+
+ case kCmdReadFile:
+ errorCode = _workRead(payload);
+ break;
+
+ case kCmdWriteFile:
+ errorCode = _workWrite(payload);
+ break;
+
+ case kCmdRemoveFile:
+ errorCode = _workRemoveFile(payload);
+ break;
+
+ case kCmdRename:
+ errorCode = _workRename(payload);
+ break;
+
+ case kCmdTruncateFile:
+ errorCode = _workTruncateFile(payload);
+ break;
+
+ case kCmdCreateDirectory:
+ errorCode = _workCreateDirectory(payload);
+ break;
+
+ case kCmdRemoveDirectory:
+ errorCode = _workRemoveDirectory(payload);
+ break;
+
+
+ case kCmdCalcFileCRC32:
+ errorCode = _workCalcFileCRC32(payload);
+ break;
+
+ default:
+ errorCode = kErrUnknownCommand;
+ break;
+ }
+
+out:
+ // handle success vs. error
+ if (errorCode == kErrNone) {
+ payload->req_opcode = payload->opcode;
+ payload->opcode = kRspAck;
+#ifdef MAVLINK_FTP_DEBUG
+ warnx("FTP: ack\n");
+#endif
+ } else {
+ int r_errno = errno;
+ warnx("FTP: nak %u", errorCode);
+ payload->req_opcode = payload->opcode;
+ payload->opcode = kRspNak;
+ payload->size = 1;
+ payload->data[0] = errorCode;
+ if (errorCode == kErrFailErrno) {
+ payload->size = 2;
+ payload->data[1] = r_errno;
+ }
+ }
+
+
+ // respond to the request
+ _reply(req);
+
+ _return_request(req);
+}
+
+/// @brief Sends the specified FTP reponse message out through mavlink
+void
+MavlinkFTP::_reply(Request *req)
+{
+ PayloadHeader *payload = reinterpret_cast<PayloadHeader *>(&req->message.payload[0]);
+
+ payload->seqNumber = payload->seqNumber + 1;
+
+ mavlink_message_t msg;
+ msg.checksum = 0;
+#ifndef MAVLINK_FTP_UNIT_TEST
+ uint16_t len =
+#endif
+ mavlink_msg_file_transfer_protocol_pack_chan(req->serverSystemId, // Sender system id
+ req->serverComponentId, // Sender component id
+ req->serverChannel, // Channel to send on
+ &msg, // Message to pack payload into
+ 0, // Target network
+ req->targetSystemId, // Target system id
+ 0, // Target component id
+ (const uint8_t*)payload); // Payload to pack into message
+
+ bool success = true;
+#ifdef MAVLINK_FTP_UNIT_TEST
+ // Unit test hook is set, call that instead
+ _utRcvMsgFunc(&msg, _ftp_test);
+#else
+ Mavlink *mavlink = req->mavlink;
+
+ mavlink->lockMessageBufferMutex();
+ success = mavlink->message_buffer_write(&msg, len);
+ mavlink->unlockMessageBufferMutex();
+
+#endif
+
+ if (!success) {
+ warnx("FTP TX ERR");
+ }
+#ifdef MAVLINK_FTP_DEBUG
+ else {
+ warnx("wrote: sys: %d, comp: %d, chan: %d, checksum: %d",
+ req->serverSystemId,
+ req->serverComponentId,
+ req->serverChannel,
+ msg.checksum);
+ }
+#endif
+}
+
+/// @brief Responds to a List command
+MavlinkFTP::ErrorCode
+MavlinkFTP::_workList(PayloadHeader* payload)
+{
+ char dirPath[kMaxDataLength];
+ strncpy(dirPath, _data_as_cstring(payload), kMaxDataLength);
+
+ DIR *dp = opendir(dirPath);
+
+ if (dp == nullptr) {
+ warnx("FTP: can't open path '%s'", dirPath);
+ return kErrFailErrno;
+ }
+
+#ifdef MAVLINK_FTP_DEBUG
+ warnx("FTP: list %s offset %d", dirPath, payload->offset);
+#endif
+
+ ErrorCode errorCode = kErrNone;
+ struct dirent entry, *result = nullptr;
+ unsigned offset = 0;
+
+ // move to the requested offset
+ seekdir(dp, payload->offset);
+
+ for (;;) {
+ // read the directory entry
+ if (readdir_r(dp, &entry, &result)) {
+ warnx("FTP: list %s readdir_r failure\n", dirPath);
+ errorCode = kErrFailErrno;
+ break;
+ }
+
+ // no more entries?
+ if (result == nullptr) {
+ if (payload->offset != 0 && offset == 0) {
+ // User is requesting subsequent dir entries but there were none. This means the user asked
+ // to seek past EOF.
+ errorCode = kErrEOF;
+ }
+ // Otherwise we are just at the last directory entry, so we leave the errorCode at kErrorNone to signal that
+ break;
+ }
+
+ uint32_t fileSize = 0;
+ char buf[256];
+ char direntType;
+
+ // Determine the directory entry type
+ switch (entry.d_type) {
+ case DTYPE_FILE:
+ // For files we get the file size as well
+ direntType = kDirentFile;
+ snprintf(buf, sizeof(buf), "%s/%s", dirPath, entry.d_name);
+ struct stat st;
+ if (stat(buf, &st) == 0) {
+ fileSize = st.st_size;
+ }
+ break;
+ case DTYPE_DIRECTORY:
+ if (strcmp(entry.d_name, ".") == 0 || strcmp(entry.d_name, "..") == 0) {
+ // Don't bother sending these back
+ direntType = kDirentSkip;
+ } else {
+ direntType = kDirentDir;
+ }
+ break;
+ default:
+ // We only send back file and diretory entries, skip everything else
+ direntType = kDirentSkip;
+ }
+
+ if (direntType == kDirentSkip) {
+ // Skip send only dirent identifier
+ buf[0] = '\0';
+ } else if (direntType == kDirentFile) {
+ // Files send filename and file length
+ snprintf(buf, sizeof(buf), "%s\t%d", entry.d_name, fileSize);
+ } else {
+ // Everything else just sends name
+ strncpy(buf, entry.d_name, sizeof(buf));
+ buf[sizeof(buf)-1] = 0;
+ }
+ size_t nameLen = strlen(buf);
+
+ // Do we have room for the name, the one char directory identifier and the null terminator?
+ if ((offset + nameLen + 2) > kMaxDataLength) {
+ break;
+ }
+
+ // Move the data into the buffer
+ payload->data[offset++] = direntType;
+ strcpy((char *)&payload->data[offset], buf);
+#ifdef MAVLINK_FTP_DEBUG
+ printf("FTP: list %s %s\n", dirPath, (char *)&payload->data[offset-1]);
+#endif
+ offset += nameLen + 1;
+ }
+
+ closedir(dp);
+ payload->size = offset;
+
+ return errorCode;
+}
+
+/// @brief Responds to an Open command
+MavlinkFTP::ErrorCode
+MavlinkFTP::_workOpen(PayloadHeader* payload, int oflag)
+{
+ int session_index = _find_unused_session();
+ if (session_index < 0) {
+ warnx("FTP: Open failed - out of sessions\n");
+ return kErrNoSessionsAvailable;
+ }
+
+ char *filename = _data_as_cstring(payload);
+
+ uint32_t fileSize = 0;
+ struct stat st;
+ if (stat(filename, &st) != 0) {
+ // fail only if requested open for read
+ if (oflag & O_RDONLY)
+ return kErrFailErrno;
+ else
+ st.st_size = 0;
+ }
+ fileSize = st.st_size;
+
+ int fd = ::open(filename, oflag);
+ if (fd < 0) {
+ return kErrFailErrno;
+ }
+ _session_fds[session_index] = fd;
+
+ payload->session = session_index;
+ payload->size = sizeof(uint32_t);
+ *((uint32_t*)payload->data) = fileSize;
+
+ return kErrNone;
+}
+
+/// @brief Responds to a Read command
+MavlinkFTP::ErrorCode
+MavlinkFTP::_workRead(PayloadHeader* payload)
+{
+ int session_index = payload->session;
+
+ if (!_valid_session(session_index)) {
+ return kErrInvalidSession;
+ }
+
+ // Seek to the specified position
+#ifdef MAVLINK_FTP_DEBUG
+ warnx("seek %d", payload->offset);
+#endif
+ if (lseek(_session_fds[session_index], payload->offset, SEEK_SET) < 0) {
+ // Unable to see to the specified location
+ warnx("seek fail");
+ return kErrEOF;
+ }
+
+ int bytes_read = ::read(_session_fds[session_index], &payload->data[0], kMaxDataLength);
+ if (bytes_read < 0) {
+ // Negative return indicates error other than eof
+ warnx("read fail %d", bytes_read);
+ return kErrFailErrno;
+ }
+
+ payload->size = bytes_read;
+
+ return kErrNone;
+}
+
+/// @brief Responds to a Write command
+MavlinkFTP::ErrorCode
+MavlinkFTP::_workWrite(PayloadHeader* payload)
+{
+ int session_index = payload->session;
+
+ if (!_valid_session(session_index)) {
+ return kErrInvalidSession;
+ }
+
+ // Seek to the specified position
+#ifdef MAVLINK_FTP_DEBUG
+ warnx("seek %d", payload->offset);
+#endif
+ if (lseek(_session_fds[session_index], payload->offset, SEEK_SET) < 0) {
+ // Unable to see to the specified location
+ warnx("seek fail");
+ return kErrFailErrno;
+ }
+
+ int bytes_written = ::write(_session_fds[session_index], &payload->data[0], payload->size);
+ if (bytes_written < 0) {
+ // Negative return indicates error other than eof
+ warnx("write fail %d", bytes_written);
+ return kErrFailErrno;
+ }
+
+ payload->size = sizeof(uint32_t);
+ *((uint32_t*)payload->data) = bytes_written;
+
+ return kErrNone;
+}
+
+/// @brief Responds to a RemoveFile command
+MavlinkFTP::ErrorCode
+MavlinkFTP::_workRemoveFile(PayloadHeader* payload)
+{
+ char file[kMaxDataLength];
+ strncpy(file, _data_as_cstring(payload), kMaxDataLength);
+
+ if (unlink(file) == 0) {
+ payload->size = 0;
+ return kErrNone;
+ } else {
+ return kErrFailErrno;
+ }
+}
+
+/// @brief Responds to a TruncateFile command
+MavlinkFTP::ErrorCode
+MavlinkFTP::_workTruncateFile(PayloadHeader* payload)
+{
+ char file[kMaxDataLength];
+ const char temp_file[] = "/fs/microsd/.trunc.tmp";
+ strncpy(file, _data_as_cstring(payload), kMaxDataLength);
+ payload->size = 0;
+
+ // emulate truncate(file, payload->offset) by
+ // copying to temp and overwrite with O_TRUNC flag.
+
+ struct stat st;
+ if (stat(file, &st) != 0) {
+ return kErrFailErrno;
+ }
+
+ if (!S_ISREG(st.st_mode)) {
+ errno = EISDIR;
+ return kErrFailErrno;
+ }
+
+ // check perms allow us to write (not romfs)
+ if (!(st.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH))) {
+ errno = EROFS;
+ return kErrFailErrno;
+ }
+
+ if (payload->offset == (unsigned)st.st_size) {
+ // nothing to do
+ return kErrNone;
+ }
+ else if (payload->offset == 0) {
+ // 1: truncate all data
+ int fd = ::open(file, O_TRUNC | O_WRONLY);
+ if (fd < 0) {
+ return kErrFailErrno;
+ }
+
+ ::close(fd);
+ return kErrNone;
+ }
+ else if (payload->offset > (unsigned)st.st_size) {
+ // 2: extend file
+ int fd = ::open(file, O_WRONLY);
+ if (fd < 0) {
+ return kErrFailErrno;
+ }
+
+ if (lseek(fd, payload->offset - 1, SEEK_SET) < 0) {
+ ::close(fd);
+ return kErrFailErrno;
+ }
+
+ bool ok = 1 == ::write(fd, "", 1);
+ ::close(fd);
+
+ return (ok)? kErrNone : kErrFailErrno;
+ }
+ else {
+ // 3: truncate
+ if (_copy_file(file, temp_file, payload->offset) != 0) {
+ return kErrFailErrno;
+ }
+ if (_copy_file(temp_file, file, payload->offset) != 0) {
+ return kErrFailErrno;
+ }
+ if (::unlink(temp_file) != 0) {
+ return kErrFailErrno;
+ }
+
+ return kErrNone;
+ }
+}
+
+/// @brief Responds to a Terminate command
+MavlinkFTP::ErrorCode
+MavlinkFTP::_workTerminate(PayloadHeader* payload)
+{
+ if (!_valid_session(payload->session)) {
+ return kErrInvalidSession;
+ }
+
+ ::close(_session_fds[payload->session]);
+ _session_fds[payload->session] = -1;
+
+ payload->size = 0;
+
+ return kErrNone;
+}
+
+/// @brief Responds to a Reset command
+MavlinkFTP::ErrorCode
+MavlinkFTP::_workReset(PayloadHeader* payload)
+{
+ for (size_t i=0; i<kMaxSession; i++) {
+ if (_session_fds[i] != -1) {
+ ::close(_session_fds[i]);
+ _session_fds[i] = -1;
+ }
+ }
+
+ payload->size = 0;
+
+ return kErrNone;
+}
+
+/// @brief Responds to a Rename command
+MavlinkFTP::ErrorCode
+MavlinkFTP::_workRename(PayloadHeader* payload)
+{
+ char oldpath[kMaxDataLength];
+ char newpath[kMaxDataLength];
+
+ char *ptr = _data_as_cstring(payload);
+ size_t oldpath_sz = strlen(ptr);
+
+ if (oldpath_sz == payload->size) {
+ // no newpath
+ errno = EINVAL;
+ return kErrFailErrno;
+ }
+
+ strncpy(oldpath, ptr, kMaxDataLength);
+ strncpy(newpath, ptr + oldpath_sz + 1, kMaxDataLength);
+
+ if (rename(oldpath, newpath) == 0) {
+ payload->size = 0;
+ return kErrNone;
+ } else {
+ return kErrFailErrno;
+ }
+}
+
+/// @brief Responds to a RemoveDirectory command
+MavlinkFTP::ErrorCode
+MavlinkFTP::_workRemoveDirectory(PayloadHeader* payload)
+{
+ char dir[kMaxDataLength];
+ strncpy(dir, _data_as_cstring(payload), kMaxDataLength);
+
+ if (rmdir(dir) == 0) {
+ payload->size = 0;
+ return kErrNone;
+ } else {
+ return kErrFailErrno;
+ }
+}
+
+/// @brief Responds to a CreateDirectory command
+MavlinkFTP::ErrorCode
+MavlinkFTP::_workCreateDirectory(PayloadHeader* payload)
+{
+ char dir[kMaxDataLength];
+ strncpy(dir, _data_as_cstring(payload), kMaxDataLength);
+
+ if (mkdir(dir, S_IRWXU | S_IRWXG | S_IRWXO) == 0) {
+ payload->size = 0;
+ return kErrNone;
+ } else {
+ return kErrFailErrno;
+ }
+}
+
+/// @brief Responds to a CalcFileCRC32 command
+MavlinkFTP::ErrorCode
+MavlinkFTP::_workCalcFileCRC32(PayloadHeader* payload)
+{
+ char file_buf[256];
+ uint32_t checksum = 0;
+ ssize_t bytes_read;
+ strncpy(file_buf, _data_as_cstring(payload), kMaxDataLength);
+
+ int fd = ::open(file_buf, O_RDONLY);
+ if (fd < 0) {
+ return kErrFailErrno;
+ }
+
+ do {
+ bytes_read = ::read(fd, file_buf, sizeof(file_buf));
+ if (bytes_read < 0) {
+ int r_errno = errno;
+ ::close(fd);
+ errno = r_errno;
+ return kErrFailErrno;
+ }
+
+ checksum = crc32part((uint8_t*)file_buf, bytes_read, checksum);
+ } while (bytes_read == sizeof(file_buf));
+
+ ::close(fd);
+
+ payload->size = sizeof(uint32_t);
+ *((uint32_t*)payload->data) = checksum;
+ return kErrNone;
+}
+
+/// @brief Returns true if the specified session is a valid open session
+bool
+MavlinkFTP::_valid_session(unsigned index)
+{
+ if ((index >= kMaxSession) || (_session_fds[index] < 0)) {
+ return false;
+ }
+ return true;
+}
+
+/// @brief Returns an unused session index
+int
+MavlinkFTP::_find_unused_session(void)
+{
+ for (size_t i=0; i<kMaxSession; i++) {
+ if (_session_fds[i] == -1) {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+/// @brief Guarantees that the payload data is null terminated.
+/// @return Returns a pointer to the payload data as a char *
+char *
+MavlinkFTP::_data_as_cstring(PayloadHeader* payload)
+{
+ // guarantee nul termination
+ if (payload->size < kMaxDataLength) {
+ payload->data[payload->size] = '\0';
+ } else {
+ payload->data[kMaxDataLength - 1] = '\0';
+ }
+
+ // and return data
+ return (char *)&(payload->data[0]);
+}
+
+/// @brief Returns a unused Request entry. NULL if none available.
+MavlinkFTP::Request *
+MavlinkFTP::_get_request(void)
+{
+ _lock_request_queue();
+ Request* req = reinterpret_cast<Request *>(dq_remfirst(&_request_queue));
+ _unlock_request_queue();
+ return req;
+}
+
+/// @brief Locks a semaphore to provide exclusive access to the request queue
+void
+MavlinkFTP::_lock_request_queue(void)
+{
+ do {}
+ while (sem_wait(&_request_queue_sem) != 0);
+}
+
+/// @brief Unlocks the semaphore providing exclusive access to the request queue
+void
+MavlinkFTP::_unlock_request_queue(void)
+{
+ sem_post(&_request_queue_sem);
+}
+
+/// @brief Returns a no longer needed request to the queue
+void
+MavlinkFTP::_return_request(Request *req)
+{
+ _lock_request_queue();
+ dq_addlast(&req->work.dq, &_request_queue);
+ _unlock_request_queue();
+}
+
+/// @brief Copy file (with limited space)
+int
+MavlinkFTP::_copy_file(const char *src_path, const char *dst_path, ssize_t length)
+{
+ char buff[512];
+ int src_fd = -1, dst_fd = -1;
+ int op_errno = 0;
+
+ src_fd = ::open(src_path, O_RDONLY);
+ if (src_fd < 0) {
+ return -1;
+ }
+
+ dst_fd = ::open(dst_path, O_CREAT | O_TRUNC | O_WRONLY);
+ if (dst_fd < 0) {
+ op_errno = errno;
+ ::close(src_fd);
+ errno = op_errno;
+ return -1;
+ }
+
+ while (length > 0) {
+ ssize_t bytes_read, bytes_written;
+ size_t blen = (length > sizeof(buff))? sizeof(buff) : length;
+
+ bytes_read = ::read(src_fd, buff, blen);
+ if (bytes_read == 0) {
+ // EOF
+ break;
+ }
+ else if (bytes_read < 0) {
+ warnx("cp: read");
+ op_errno = errno;
+ break;
+ }
+
+ bytes_written = ::write(dst_fd, buff, bytes_read);
+ if (bytes_written != bytes_read) {
+ warnx("cp: short write");
+ op_errno = errno;
+ break;
+ }
+
+ length -= bytes_written;
+ }
+
+ ::close(src_fd);
+ ::close(dst_fd);
+
+ errno = op_errno;
+ return (length > 0)? -1 : 0;
+}
diff --git a/src/modules/mavlink/mavlink_ftp.h b/src/modules/mavlink/mavlink_ftp.h
new file mode 100644
index 000000000..bef6775a9
--- /dev/null
+++ b/src/modules/mavlink/mavlink_ftp.h
@@ -0,0 +1,180 @@
+/****************************************************************************
+ *
+ * Copyright (c) 2014 PX4 Development Team. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+#pragma once
+
+/// @file mavlink_ftp.h
+/// @author px4dev, Don Gagne <don@thegagnes.com>
+
+#include <dirent.h>
+#include <queue.h>
+
+#include <nuttx/wqueue.h>
+#include <systemlib/err.h>
+
+#include "mavlink_messages.h"
+#include "mavlink_main.h"
+
+class MavlinkFtpTest;
+
+/// @brief MAVLink remote file server. Support FTP like commands using MAVLINK_MSG_ID_FILE_TRANSFER_PROTOCOL message.
+/// A limited number of requests (kRequestQueueSize) may be outstanding at a time. Additional messages will be discarded.
+class MavlinkFTP
+{
+public:
+ /// @brief Returns the one Mavlink FTP server in the system.
+ static MavlinkFTP* get_server(void);
+
+ /// @brief Contructor is only public so unit test code can new objects.
+ MavlinkFTP();
+
+ /// @brief Adds the specified message to the work queue.
+ void handle_message(Mavlink* mavlink, mavlink_message_t *msg);
+
+ typedef void (*ReceiveMessageFunc_t)(const mavlink_message_t *msg, MavlinkFtpTest* ftpTest);
+
+ /// @brief Sets up the server to run in unit test mode.
+ /// @param rcvmsgFunc Function which will be called to handle outgoing mavlink messages.
+ /// @param ftp_test MavlinkFtpTest object which the function is associated with
+ void set_unittest_worker(ReceiveMessageFunc_t rcvMsgFunc, MavlinkFtpTest *ftp_test);
+
+ /// @brief This is the payload which is in mavlink_file_transfer_protocol_t.payload. We pad the structure ourselves to
+ /// 32 bit alignment to avoid usage of any pack pragmas.
+ struct PayloadHeader
+ {
+ uint16_t seqNumber; ///< sequence number for message
+ uint8_t session; ///< Session id for read and write commands
+ uint8_t opcode; ///< Command opcode
+ uint8_t size; ///< Size of data
+ uint8_t req_opcode; ///< Request opcode returned in kRspAck, kRspNak message
+ uint8_t padding[2]; ///< 32 bit aligment padding
+ uint32_t offset; ///< Offsets for List and Read commands
+ uint8_t data[]; ///< command data, varies by Opcode
+ };
+
+ /// @brief Command opcodes
+ enum Opcode : uint8_t
+ {
+ kCmdNone, ///< ignored, always acked
+ kCmdTerminateSession, ///< Terminates open Read session
+ kCmdResetSessions, ///< Terminates all open Read sessions
+ kCmdListDirectory, ///< List files in <path> from <offset>
+ kCmdOpenFileRO, ///< Opens file at <path> for reading, returns <session>
+ kCmdReadFile, ///< Reads <size> bytes from <offset> in <session>
+ kCmdCreateFile, ///< Creates file at <path> for writing, returns <session>
+ kCmdWriteFile, ///< Writes <size> bytes to <offset> in <session>
+ kCmdRemoveFile, ///< Remove file at <path>
+ kCmdCreateDirectory, ///< Creates directory at <path>
+ kCmdRemoveDirectory, ///< Removes Directory at <path>, must be empty
+ kCmdOpenFileWO, ///< Opens file at <path> for writing, returns <session>
+ kCmdTruncateFile, ///< Truncate file at <path> to <offset> length
+ kCmdRename, ///< Rename <path1> to <path2>
+ kCmdCalcFileCRC32, ///< Calculate CRC32 for file at <path>
+
+ kRspAck = 128, ///< Ack response
+ kRspNak ///< Nak response
+ };
+
+ /// @brief Error codes returned in Nak response PayloadHeader.data[0].
+ enum ErrorCode : uint8_t
+ {
+ kErrNone,
+ kErrFail, ///< Unknown failure
+ kErrFailErrno, ///< Command failed, errno sent back in PayloadHeader.data[1]
+ kErrInvalidDataSize, ///< PayloadHeader.size is invalid
+ kErrInvalidSession, ///< Session is not currently open
+ kErrNoSessionsAvailable, ///< All available Sessions in use
+ kErrEOF, ///< Offset past end of file for List and Read commands
+ kErrUnknownCommand ///< Unknown command opcode
+ };
+
+private:
+ /// @brief Unit of work which is queued to work_queue
+ struct Request
+ {
+ work_s work; ///< work queue entry
+ Mavlink *mavlink; ///< Mavlink to reply to
+ uint8_t serverSystemId; ///< System ID to send from
+ uint8_t serverComponentId; ///< Component ID to send from
+ uint8_t serverChannel; ///< Channel to send to
+ uint8_t targetSystemId; ///< System ID to target reply to
+
+ mavlink_file_transfer_protocol_t message; ///< Protocol message
+ };
+
+ Request *_get_request(void);
+ void _return_request(Request *req);
+ void _lock_request_queue(void);
+ void _unlock_request_queue(void);
+
+ char *_data_as_cstring(PayloadHeader* payload);
+
+ static void _worker_trampoline(void *arg);
+ void _process_request(Request *req);
+ void _reply(Request *req);
+ int _copy_file(const char *src_path, const char *dst_path, ssize_t length);
+
+ ErrorCode _workList(PayloadHeader *payload);
+ ErrorCode _workOpen(PayloadHeader *payload, int oflag);
+ ErrorCode _workRead(PayloadHeader *payload);
+ ErrorCode _workWrite(PayloadHeader *payload);
+ ErrorCode _workTerminate(PayloadHeader *payload);
+ ErrorCode _workReset(PayloadHeader* payload);
+ ErrorCode _workRemoveDirectory(PayloadHeader *payload);
+ ErrorCode _workCreateDirectory(PayloadHeader *payload);
+ ErrorCode _workRemoveFile(PayloadHeader *payload);
+ ErrorCode _workTruncateFile(PayloadHeader *payload);
+ ErrorCode _workRename(PayloadHeader *payload);
+ ErrorCode _workCalcFileCRC32(PayloadHeader *payload);
+
+ static const unsigned kRequestQueueSize = 2; ///< Max number of queued requests
+ Request _request_bufs[kRequestQueueSize]; ///< Request buffers which hold work
+ dq_queue_t _request_queue; ///< Queue of available Request buffers
+ sem_t _request_queue_sem; ///< Semaphore for locking access to _request_queue
+
+ int _find_unused_session(void);
+ bool _valid_session(unsigned index);
+
+ static const char kDirentFile = 'F'; ///< Identifies File returned from List command
+ static const char kDirentDir = 'D'; ///< Identifies Directory returned from List command
+ static const char kDirentSkip = 'S'; ///< Identifies Skipped entry from List command
+
+ /// @brief Maximum data size in RequestHeader::data
+ static const uint8_t kMaxDataLength = MAVLINK_MSG_FILE_TRANSFER_PROTOCOL_FIELD_PAYLOAD_LEN - sizeof(PayloadHeader);
+
+ static const unsigned kMaxSession = 2; ///< Max number of active sessions
+ int _session_fds[kMaxSession]; ///< Session file descriptors, 0 for empty slot
+
+ ReceiveMessageFunc_t _utRcvMsgFunc; ///< Unit test override for mavlink message sending
+ MavlinkFtpTest *_ftp_test; ///< Additional parameter to _utRcvMsgFunc;
+};
diff --git a/src/modules/mavlink/mavlink_main.cpp b/src/modules/mavlink/mavlink_main.cpp
index a9b8323f3..fb9f65cf5 100644
--- a/src/modules/mavlink/mavlink_main.cpp
+++ b/src/modules/mavlink/mavlink_main.cpp
@@ -18,7 +18,6 @@
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
@@ -73,15 +72,16 @@
#include <mavlink/mavlink_log.h>
#include <uORB/topics/parameter_update.h>
-#include <uORB/topics/mission.h>
-#include <uORB/topics/mission_result.h>
#include "mavlink_bridge_header.h"
#include "mavlink_main.h"
#include "mavlink_messages.h"
#include "mavlink_receiver.h"
#include "mavlink_rate_limiter.h"
-#include "mavlink_commands.h"
+
+#ifndef MAVLINK_CRC_EXTRA
+#error MAVLINK_CRC_EXTRA has to be defined on PX4 systems
+#endif
/* oddly, ERROR is not defined for c++ */
#ifdef ERROR
@@ -90,14 +90,19 @@
static const int ERROR = -1;
#define DEFAULT_DEVICE_NAME "/dev/ttyS1"
-#define MAX_DATA_RATE 10000 // max data rate in bytes/s
+#define MAX_DATA_RATE 20000 // max data rate in bytes/s
#define MAIN_LOOP_DELAY 10000 // 100 Hz @ 1000 bytes/s data rate
+#define TX_BUFFER_GAP MAVLINK_MAX_PACKET_LEN
+
static Mavlink *_mavlink_instances = nullptr;
/* TODO: if this is a class member it crashes */
static struct file_operations fops;
+static const uint8_t mavlink_message_lengths[256] = MAVLINK_MESSAGE_LENGTHS;
+static const uint8_t mavlink_message_crcs[256] = MAVLINK_MESSAGE_CRCS;
+
/**
* mavlink app start / stop handling function
*
@@ -105,108 +110,7 @@ static struct file_operations fops;
*/
extern "C" __EXPORT int mavlink_main(int argc, char *argv[]);
-static uint64_t last_write_success_times[6] = {0};
-static uint64_t last_write_try_times[6] = {0};
-
-/*
- * Internal function to send the bytes through the right serial port
- */
-void
-mavlink_send_uart_bytes(mavlink_channel_t channel, const uint8_t *ch, int length)
-{
- Mavlink *instance;
-
- switch (channel) {
- case MAVLINK_COMM_0:
- instance = Mavlink::get_instance(0);
- break;
-
- case MAVLINK_COMM_1:
- instance = Mavlink::get_instance(1);
- break;
-
- case MAVLINK_COMM_2:
- instance = Mavlink::get_instance(2);
- break;
-
- case MAVLINK_COMM_3:
- instance = Mavlink::get_instance(3);
- break;
-#ifdef MAVLINK_COMM_4
-
- case MAVLINK_COMM_4:
- instance = Mavlink::get_instance(4);
- break;
-#endif
-#ifdef MAVLINK_COMM_5
-
- case MAVLINK_COMM_5:
- instance = Mavlink::get_instance(5);
- break;
-#endif
-#ifdef MAVLINK_COMM_6
-
- case MAVLINK_COMM_6:
- instance = Mavlink::get_instance(6);
- break;
-#endif
- default:
- return;
- }
-
- int uart = instance->get_uart_fd();
-
- ssize_t desired = (sizeof(uint8_t) * length);
-
- /*
- * Check if the OS buffer is full and disable HW
- * flow control if it continues to be full
- */
- int buf_free = 0;
-
- if (instance->get_flow_control_enabled()
- && ioctl(uart, FIONWRITE, (unsigned long)&buf_free) == 0) {
-
- /* Disable hardware flow control:
- * if no successful write since a defined time
- * and if the last try was not the last successful write
- */
- if (last_write_try_times[(unsigned)channel] != 0 &&
- hrt_elapsed_time(&last_write_success_times[(unsigned)channel]) > 500 * 1000UL &&
- last_write_success_times[(unsigned)channel] !=
- last_write_try_times[(unsigned)channel])
- {
- warnx("DISABLING HARDWARE FLOW CONTROL");
- instance->enable_flow_control(false);
- }
-
- }
-
- /* If the wait until transmit flag is on, only transmit after we've received messages.
- Otherwise, transmit all the time. */
- if (instance->should_transmit()) {
- last_write_try_times[(unsigned)channel] = hrt_absolute_time();
-
- /* check if there is space in the buffer, let it overflow else */
- if (!ioctl(uart, FIONWRITE, (unsigned long)&buf_free)) {
-
- if (buf_free < desired) {
- /* we don't want to send anything just in half, so return */
- return;
- }
- }
-
- ssize_t ret = write(uart, ch, desired);
- if (ret != desired) {
- warnx("TX FAIL");
- } else {
- last_write_success_times[(unsigned)channel] = last_write_try_times[(unsigned)channel];
- }
- }
-
-
-
-}
+extern mavlink_system_t mavlink_system;
static void usage(void);
@@ -214,39 +118,64 @@ Mavlink::Mavlink() :
_device_name(DEFAULT_DEVICE_NAME),
_task_should_exit(false),
next(nullptr),
+ _instance_id(0),
_mavlink_fd(-1),
_task_running(false),
_hil_enabled(false),
_use_hil_gps(false),
+ _forward_externalsp(false),
_is_usb_uart(false),
_wait_to_transmit(false),
_received_messages(false),
_main_loop_delay(1000),
_subscriptions(nullptr),
_streams(nullptr),
- _mission_pub(-1),
+ _mission_manager(nullptr),
+ _parameters_manager(nullptr),
_mode(MAVLINK_MODE_NORMAL),
+ _channel(MAVLINK_COMM_0),
+ _logbuffer {},
_total_counter(0),
+ _receive_thread {},
_verbose(false),
_forwarding_on(false),
_passing_on(false),
+ _ftp_on(false),
_uart_fd(-1),
+ _baudrate(57600),
+ _datarate(1000),
+ _datarate_events(500),
+ _rate_mult(1.0f),
_mavlink_param_queue_index(0),
+ mavlink_link_termination_allowed(false),
_subscribe_to_stream(nullptr),
_subscribe_to_stream_rate(0.0f),
_flow_control_enabled(true),
- _message_buffer({}),
+ _last_write_success_time(0),
+ _last_write_try_time(0),
+ _bytes_tx(0),
+ _bytes_txerr(0),
+ _bytes_rx(0),
+ _bytes_timestamp(0),
+ _rate_tx(0.0f),
+ _rate_txerr(0.0f),
+ _rate_rx(0.0f),
+ _rstatus {},
+ _message_buffer {},
+ _message_buffer_mutex {},
+ _send_mutex {},
_param_initialized(false),
_param_system_id(0),
_param_component_id(0),
- _param_system_type(0),
+ _param_system_type(MAV_TYPE_FIXED_WING),
_param_use_hil_gps(0),
+ _param_forward_externalsp(0),
+ _system_type(0),
-/* performance counters */
- _loop_perf(perf_alloc(PC_ELAPSED, "mavlink"))
+ /* performance counters */
+ _loop_perf(perf_alloc(PC_ELAPSED, "mavlink_el")),
+ _txerr_perf(perf_alloc(PC_COUNT, "mavlink_txe"))
{
- _wpm = &_wpm_s;
- mission.count = 0;
fops.ioctl = (int (*)(file *, int, long unsigned int))&mavlink_dev_ioctl;
_instance_id = Mavlink::instance_count();
@@ -291,11 +220,14 @@ Mavlink::Mavlink() :
errx(1, "instance ID is out of range");
break;
}
+
+ _rstatus.type = TELEMETRY_STATUS_RADIO_TYPE_GENERIC;
}
Mavlink::~Mavlink()
{
perf_free(_loop_perf);
+ perf_free(_txerr_perf);
if (_task_running) {
/* task wakes up every 10ms or so at the longest */
@@ -321,6 +253,12 @@ Mavlink::~Mavlink()
}
void
+Mavlink::count_txerr()
+{
+ perf_count(_txerr_perf);
+}
+
+void
Mavlink::set_mode(enum MAVLINK_MODE mode)
{
_mode = mode;
@@ -405,6 +343,27 @@ Mavlink::destroy_all_instances()
return OK;
}
+int
+Mavlink::get_status_all_instances()
+{
+ Mavlink *inst = ::_mavlink_instances;
+
+ unsigned iterations = 0;
+
+ while (inst != nullptr) {
+
+ printf("\ninstance #%u:\n", iterations);
+ inst->display_status();
+
+ /* move on */
+ inst = inst->next;
+ iterations++;
+ }
+
+ /* return an error if there are no instances */
+ return (iterations == 0);
+}
+
bool
Mavlink::instance_exists(const char *device_name, Mavlink *self)
{
@@ -424,13 +383,18 @@ Mavlink::instance_exists(const char *device_name, Mavlink *self)
}
void
-Mavlink::forward_message(mavlink_message_t *msg, Mavlink *self)
+Mavlink::forward_message(const mavlink_message_t *msg, Mavlink *self)
{
Mavlink *inst;
LL_FOREACH(_mavlink_instances, inst) {
if (inst != self) {
- inst->pass_message(msg);
+
+ /* if not in normal mode, we are an onboard link
+ * onboard links should only pass on messages from the same system ID */
+ if (!(self->_mode != MAVLINK_MODE_NORMAL && msg->sysid != mavlink_system.sysid)) {
+ inst->pass_message(msg);
+ }
}
}
}
@@ -478,10 +442,27 @@ Mavlink::mavlink_dev_ioctl(struct file *filep, int cmd, unsigned long arg)
case (int)MAVLINK_IOC_SEND_TEXT_EMERGENCY: {
const char *txt = (const char *)arg;
-// printf("logmsg: %s\n", txt);
struct mavlink_logmessage msg;
strncpy(msg.text, txt, sizeof(msg.text));
+ switch (cmd) {
+ case MAVLINK_IOC_SEND_TEXT_INFO:
+ msg.severity = MAV_SEVERITY_INFO;
+ break;
+
+ case MAVLINK_IOC_SEND_TEXT_CRITICAL:
+ msg.severity = MAV_SEVERITY_CRITICAL;
+ break;
+
+ case MAVLINK_IOC_SEND_TEXT_EMERGENCY:
+ msg.severity = MAV_SEVERITY_EMERGENCY;
+ break;
+
+ default:
+ msg.severity = MAV_SEVERITY_INFO;
+ break;
+ }
+
Mavlink *inst;
LL_FOREACH(_mavlink_instances, inst) {
if (!inst->_task_should_exit) {
@@ -500,41 +481,73 @@ Mavlink::mavlink_dev_ioctl(struct file *filep, int cmd, unsigned long arg)
void Mavlink::mavlink_update_system(void)
{
-
if (!_param_initialized) {
_param_system_id = param_find("MAV_SYS_ID");
_param_component_id = param_find("MAV_COMP_ID");
_param_system_type = param_find("MAV_TYPE");
_param_use_hil_gps = param_find("MAV_USEHILGPS");
- _param_initialized = true;
+ _param_forward_externalsp = param_find("MAV_FWDEXTSP");
}
/* update system and component id */
int32_t system_id;
param_get(_param_system_id, &system_id);
- if (system_id > 0 && system_id < 255) {
- mavlink_system.sysid = system_id;
- }
-
int32_t component_id;
param_get(_param_component_id, &component_id);
- if (component_id > 0 && component_id < 255) {
- mavlink_system.compid = component_id;
+
+ /* only allow system ID and component ID updates
+ * after reboot - not during operation */
+ if (!_param_initialized) {
+ if (system_id > 0 && system_id < 255) {
+ mavlink_system.sysid = system_id;
+ }
+
+ if (component_id > 0 && component_id < 255) {
+ mavlink_system.compid = component_id;
+ }
+
+ _param_initialized = true;
+ }
+
+ /* warn users that they need to reboot to take this
+ * into effect
+ */
+ if (system_id != mavlink_system.sysid) {
+ send_statustext_critical("Save params and reboot to change SYSID");
+ }
+
+ if (component_id != mavlink_system.compid) {
+ send_statustext_critical("Save params and reboot to change COMPID");
}
int32_t system_type;
param_get(_param_system_type, &system_type);
if (system_type >= 0 && system_type < MAV_TYPE_ENUM_END) {
- mavlink_system.type = system_type;
+ _system_type = system_type;
}
int32_t use_hil_gps;
param_get(_param_use_hil_gps, &use_hil_gps);
_use_hil_gps = (bool)use_hil_gps;
+
+ int32_t forward_externalsp;
+ param_get(_param_forward_externalsp, &forward_externalsp);
+
+ _forward_externalsp = (bool)forward_externalsp;
+}
+
+int Mavlink::get_system_id()
+{
+ return mavlink_system.sysid;
+}
+
+int Mavlink::get_component_id()
+{
+ return mavlink_system.compid;
}
int Mavlink::mavlink_open_uart(int baud, const char *uart_name, struct termios *uart_config_original, bool *is_usb)
@@ -586,7 +599,8 @@ int Mavlink::mavlink_open_uart(int baud, const char *uart_name, struct termios *
case 921600: speed = B921600; break;
default:
- warnx("ERROR: Unsupported baudrate: %d\n\tsupported examples:\n\t9600, 19200, 38400, 57600\t\n115200\n230400\n460800\n921600\n", baud);
+ warnx("ERROR: Unsupported baudrate: %d\n\tsupported examples:\n\t9600, 19200, 38400, 57600\t\n115200\n230400\n460800\n921600\n",
+ baud);
return -EINVAL;
}
@@ -647,6 +661,9 @@ int Mavlink::mavlink_open_uart(int baud, const char *uart_name, struct termios *
if (enable_flow_control(true)) {
warnx("hardware flow control not supported");
}
+
+ } else {
+ _flow_control_enabled = false;
}
return _uart_fd;
@@ -688,8 +705,7 @@ Mavlink::set_hil_enabled(bool hil_enabled)
/* enable HIL */
if (hil_enabled && !_hil_enabled) {
_hil_enabled = true;
- float rate_mult = _datarate / 1000.0f;
- configure_stream("HIL_CONTROLS", 15.0f * rate_mult);
+ configure_stream("HIL_CONTROLS", 200.0f);
}
/* disable HIL */
@@ -704,817 +720,188 @@ Mavlink::set_hil_enabled(bool hil_enabled)
return ret;
}
-extern mavlink_system_t mavlink_system;
-
-int Mavlink::mavlink_pm_queued_send()
-{
- if (_mavlink_param_queue_index < param_count()) {
- mavlink_pm_send_param(param_for_index(_mavlink_param_queue_index));
- _mavlink_param_queue_index++;
- return 0;
-
- } else {
- return 1;
- }
-}
-
-void Mavlink::mavlink_pm_start_queued_send()
-{
- _mavlink_param_queue_index = 0;
-}
-
-int Mavlink::mavlink_pm_send_param_for_index(uint16_t index)
+unsigned
+Mavlink::get_free_tx_buf()
{
- return mavlink_pm_send_param(param_for_index(index));
-}
-
-int Mavlink::mavlink_pm_send_param_for_name(const char *name)
-{
- return mavlink_pm_send_param(param_find(name));
-}
-
-int Mavlink::mavlink_pm_send_param(param_t param)
-{
- if (param == PARAM_INVALID) { return 1; }
-
- /* buffers for param transmission */
- char name_buf[MAVLINK_MSG_PARAM_VALUE_FIELD_PARAM_ID_LEN];
- float val_buf;
- mavlink_message_t tx_msg;
-
- /* query parameter type */
- param_type_t type = param_type(param);
- /* copy parameter name */
- strncpy((char *)name_buf, param_name(param), MAVLINK_MSG_PARAM_VALUE_FIELD_PARAM_ID_LEN);
-
/*
- * Map onboard parameter type to MAVLink type,
- * endianess matches (both little endian)
- */
- uint8_t mavlink_type;
-
- if (type == PARAM_TYPE_INT32) {
- mavlink_type = MAVLINK_TYPE_INT32_T;
-
- } else if (type == PARAM_TYPE_FLOAT) {
- mavlink_type = MAVLINK_TYPE_FLOAT;
-
- } else {
- mavlink_type = MAVLINK_TYPE_FLOAT;
- }
-
- /*
- * get param value, since MAVLink encodes float and int params in the same
- * space during transmission, copy param onto float val_buf
+ * Check if the OS buffer is full and disable HW
+ * flow control if it continues to be full
*/
+ int buf_free = 0;
+ (void) ioctl(_uart_fd, FIONWRITE, (unsigned long)&buf_free);
- int ret;
-
- if ((ret = param_get(param, &val_buf)) != OK) {
- return ret;
+ if (get_flow_control_enabled() && buf_free < TX_BUFFER_GAP) {
+ /* Disable hardware flow control:
+ * if no successful write since a defined time
+ * and if the last try was not the last successful write
+ */
+ if (_last_write_try_time != 0 &&
+ hrt_elapsed_time(&_last_write_success_time) > 500 * 1000UL &&
+ _last_write_success_time != _last_write_try_time) {
+ warnx("DISABLING HARDWARE FLOW CONTROL");
+ enable_flow_control(false);
+ }
}
- mavlink_msg_param_value_pack_chan(mavlink_system.sysid,
- mavlink_system.compid,
- _channel,
- &tx_msg,
- name_buf,
- val_buf,
- mavlink_type,
- param_count(),
- param_get_index(param));
- mavlink_missionlib_send_message(&tx_msg);
- return OK;
+ return buf_free;
}
-void Mavlink::mavlink_pm_message_handler(const mavlink_channel_t chan, const mavlink_message_t *msg)
+void
+Mavlink::send_message(const uint8_t msgid, const void *msg)
{
- switch (msg->msgid) {
- case MAVLINK_MSG_ID_PARAM_REQUEST_LIST: {
- mavlink_param_request_list_t req;
- mavlink_msg_param_request_list_decode(msg, &req);
- if (req.target_system == mavlink_system.sysid &&
- (req.target_component == mavlink_system.compid || req.target_component == MAV_COMP_ID_ALL)) {
- /* Start sending parameters */
- mavlink_pm_start_queued_send();
- mavlink_missionlib_send_gcs_string("[mavlink pm] sending list");
- }
- } break;
-
- case MAVLINK_MSG_ID_PARAM_SET: {
-
- /* Handle parameter setting */
-
- if (msg->msgid == MAVLINK_MSG_ID_PARAM_SET) {
- mavlink_param_set_t mavlink_param_set;
- mavlink_msg_param_set_decode(msg, &mavlink_param_set);
-
- if (mavlink_param_set.target_system == mavlink_system.sysid && ((mavlink_param_set.target_component == mavlink_system.compid) || (mavlink_param_set.target_component == MAV_COMP_ID_ALL))) {
- /* local name buffer to enforce null-terminated string */
- char name[MAVLINK_MSG_PARAM_VALUE_FIELD_PARAM_ID_LEN + 1];
- strncpy(name, mavlink_param_set.param_id, MAVLINK_MSG_PARAM_VALUE_FIELD_PARAM_ID_LEN);
- /* enforce null termination */
- name[MAVLINK_MSG_PARAM_VALUE_FIELD_PARAM_ID_LEN] = '\0';
- /* attempt to find parameter, set and send it */
- param_t param = param_find(name);
-
- if (param == PARAM_INVALID) {
- char buf[MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN];
- sprintf(buf, "[pm] unknown: %s", name);
- mavlink_missionlib_send_gcs_string(buf);
-
- } else {
- /* set and send parameter */
- param_set(param, &(mavlink_param_set.param_value));
- mavlink_pm_send_param(param);
- }
- }
- }
- } break;
-
- case MAVLINK_MSG_ID_PARAM_REQUEST_READ: {
- mavlink_param_request_read_t mavlink_param_request_read;
- mavlink_msg_param_request_read_decode(msg, &mavlink_param_request_read);
-
- if (mavlink_param_request_read.target_system == mavlink_system.sysid && ((mavlink_param_request_read.target_component == mavlink_system.compid) || (mavlink_param_request_read.target_component == MAV_COMP_ID_ALL))) {
- /* when no index is given, loop through string ids and compare them */
- if (mavlink_param_request_read.param_index == -1) {
- /* local name buffer to enforce null-terminated string */
- char name[MAVLINK_MSG_PARAM_VALUE_FIELD_PARAM_ID_LEN + 1];
- strncpy(name, mavlink_param_request_read.param_id, MAVLINK_MSG_PARAM_VALUE_FIELD_PARAM_ID_LEN);
- /* enforce null termination */
- name[MAVLINK_MSG_PARAM_VALUE_FIELD_PARAM_ID_LEN] = '\0';
- /* attempt to find parameter and send it */
- mavlink_pm_send_param_for_name(name);
-
- } else {
- /* when index is >= 0, send this parameter again */
- mavlink_pm_send_param_for_index(mavlink_param_request_read.param_index);
- }
- }
-
- } break;
+ /* If the wait until transmit flag is on, only transmit after we've received messages.
+ Otherwise, transmit all the time. */
+ if (!should_transmit()) {
+ return;
}
-}
-void Mavlink::publish_mission()
-{
- /* Initialize mission publication if necessary */
- if (_mission_pub < 0) {
- _mission_pub = orb_advertise(ORB_ID(offboard_mission), &mission);
+ pthread_mutex_lock(&_send_mutex);
- } else {
- orb_publish(ORB_ID(offboard_mission), _mission_pub, &mission);
- }
-}
-
-int Mavlink::map_mavlink_mission_item_to_mission_item(const mavlink_mission_item_t *mavlink_mission_item, struct mission_item_s *mission_item)
-{
- /* only support global waypoints for now */
- switch (mavlink_mission_item->frame) {
- case MAV_FRAME_GLOBAL:
- mission_item->lat = (double)mavlink_mission_item->x;
- mission_item->lon = (double)mavlink_mission_item->y;
- mission_item->altitude = mavlink_mission_item->z;
- mission_item->altitude_is_relative = false;
- break;
+ unsigned buf_free = get_free_tx_buf();
- case MAV_FRAME_GLOBAL_RELATIVE_ALT:
- mission_item->lat = (double)mavlink_mission_item->x;
- mission_item->lon = (double)mavlink_mission_item->y;
- mission_item->altitude = mavlink_mission_item->z;
- mission_item->altitude_is_relative = true;
- break;
+ uint8_t payload_len = mavlink_message_lengths[msgid];
+ unsigned packet_len = payload_len + MAVLINK_NUM_NON_PAYLOAD_BYTES;
- case MAV_FRAME_LOCAL_NED:
- case MAV_FRAME_LOCAL_ENU:
- return MAV_MISSION_UNSUPPORTED_FRAME;
+ _last_write_try_time = hrt_absolute_time();
- case MAV_FRAME_MISSION:
- default:
- return MAV_MISSION_ERROR;
+ /* check if there is space in the buffer, let it overflow else */
+ if ((buf_free < TX_BUFFER_GAP) || (buf_free < packet_len)) {
+ /* no enough space in buffer to send */
+ count_txerr();
+ count_txerrbytes(packet_len);
+ pthread_mutex_unlock(&_send_mutex);
+ return;
}
- switch (mavlink_mission_item->command) {
- case MAV_CMD_NAV_TAKEOFF:
- mission_item->pitch_min = mavlink_mission_item->param1;
- break;
-
- default:
- mission_item->acceptance_radius = mavlink_mission_item->param2;
- mission_item->time_inside = mavlink_mission_item->param1;
- break;
- }
+ uint8_t buf[MAVLINK_MAX_PACKET_LEN];
- mission_item->yaw = _wrap_pi(mavlink_mission_item->param4 * M_DEG_TO_RAD_F);
- mission_item->loiter_radius = fabsf(mavlink_mission_item->param3);
- mission_item->loiter_direction = (mavlink_mission_item->param3 > 0) ? 1 : -1; /* 1 if positive CW, -1 if negative CCW */
- mission_item->nav_cmd = (NAV_CMD)mavlink_mission_item->command;
+ /* header */
+ buf[0] = MAVLINK_STX;
+ buf[1] = payload_len;
+ /* use mavlink's internal counter for the TX seq */
+ buf[2] = mavlink_get_channel_status(_channel)->current_tx_seq++;
+ buf[3] = mavlink_system.sysid;
+ buf[4] = mavlink_system.compid;
+ buf[5] = msgid;
- mission_item->autocontinue = mavlink_mission_item->autocontinue;
- // mission_item->index = mavlink_mission_item->seq;
- mission_item->origin = ORIGIN_MAVLINK;
+ /* payload */
+ memcpy(&buf[MAVLINK_NUM_HEADER_BYTES], msg, payload_len);
- return OK;
-}
+ /* checksum */
+ uint16_t checksum;
+ crc_init(&checksum);
+ crc_accumulate_buffer(&checksum, (const char *) &buf[1], MAVLINK_CORE_HEADER_LEN + payload_len);
+ crc_accumulate(mavlink_message_crcs[msgid], &checksum);
-int Mavlink::map_mission_item_to_mavlink_mission_item(const struct mission_item_s *mission_item, mavlink_mission_item_t *mavlink_mission_item)
-{
- if (mission_item->altitude_is_relative) {
- mavlink_mission_item->frame = MAV_FRAME_GLOBAL;
+ buf[MAVLINK_NUM_HEADER_BYTES + payload_len] = (uint8_t)(checksum & 0xFF);
+ buf[MAVLINK_NUM_HEADER_BYTES + payload_len + 1] = (uint8_t)(checksum >> 8);
- } else {
- mavlink_mission_item->frame = MAV_FRAME_GLOBAL_RELATIVE_ALT;
- }
+ /* send message to UART */
+ ssize_t ret = write(_uart_fd, buf, packet_len);
- switch (mission_item->nav_cmd) {
- case NAV_CMD_TAKEOFF:
- mavlink_mission_item->param1 = mission_item->pitch_min;
- break;
+ if (ret != (int) packet_len) {
+ count_txerr();
+ count_txerrbytes(packet_len);
- default:
- mavlink_mission_item->param2 = mission_item->acceptance_radius;
- mavlink_mission_item->param1 = mission_item->time_inside;
- break;
+ } else {
+ _last_write_success_time = _last_write_try_time;
+ count_txbytes(packet_len);
}
- mavlink_mission_item->x = (float)mission_item->lat;
- mavlink_mission_item->y = (float)mission_item->lon;
- mavlink_mission_item->z = mission_item->altitude;
-
- mavlink_mission_item->param4 = mission_item->yaw * M_RAD_TO_DEG_F;
- mavlink_mission_item->param3 = mission_item->loiter_radius * (float)mission_item->loiter_direction;
- mavlink_mission_item->command = mission_item->nav_cmd;
- mavlink_mission_item->autocontinue = mission_item->autocontinue;
- // mavlink_mission_item->seq = mission_item->index;
-
- return OK;
+ pthread_mutex_unlock(&_send_mutex);
}
-void Mavlink::mavlink_wpm_init(mavlink_wpm_storage *state)
-{
- state->size = 0;
- state->max_size = MAVLINK_WPM_MAX_WP_COUNT;
- state->current_state = MAVLINK_WPM_STATE_IDLE;
- state->current_partner_sysid = 0;
- state->current_partner_compid = 0;
- state->timestamp_lastaction = 0;
- state->timestamp_last_send_setpoint = 0;
- state->timestamp_last_send_request = 0;
- state->timeout = MAVLINK_WPM_PROTOCOL_TIMEOUT_DEFAULT;
- state->current_dataman_id = 0;
-}
-
-/*
- * @brief Sends an waypoint ack message
- */
-void Mavlink::mavlink_wpm_send_waypoint_ack(uint8_t sysid, uint8_t compid, uint8_t type)
-{
- mavlink_message_t msg;
- mavlink_mission_ack_t wpa;
-
- wpa.target_system = sysid;
- wpa.target_component = compid;
- wpa.type = type;
-
- mavlink_msg_mission_ack_encode_chan(mavlink_system.sysid, _mavlink_wpm_comp_id, _channel, &msg, &wpa);
- mavlink_missionlib_send_message(&msg);
-
- if (_verbose) { warnx("Sent waypoint ack (%u) to ID %u", wpa.type, wpa.target_system); }
-}
-
-/*
- * @brief Broadcasts the new target waypoint and directs the MAV to fly there
- *
- * This function broadcasts its new active waypoint sequence number and
- * sends a message to the controller, advising it to fly to the coordinates
- * of the waypoint with a given orientation
- *
- * @param seq The waypoint sequence number the MAV should fly to.
- */
-void Mavlink::mavlink_wpm_send_waypoint_current(uint16_t seq)
+void
+Mavlink::resend_message(mavlink_message_t *msg)
{
- if (seq < _wpm->size) {
- mavlink_message_t msg;
- mavlink_mission_current_t wpc;
-
- wpc.seq = seq;
-
- mavlink_msg_mission_current_encode_chan(mavlink_system.sysid, _mavlink_wpm_comp_id, _channel, &msg, &wpc);
- mavlink_missionlib_send_message(&msg);
-
- } else if (seq == 0 && _wpm->size == 0) {
-
- /* don't broadcast if no WPs */
-
- } else {
- mavlink_missionlib_send_gcs_string("ERROR: wp index out of bounds");
+ /* If the wait until transmit flag is on, only transmit after we've received messages.
+ Otherwise, transmit all the time. */
+ if (!should_transmit()) {
+ return;
}
-}
-void Mavlink::mavlink_wpm_send_waypoint_count(uint8_t sysid, uint8_t compid, uint16_t count)
-{
- mavlink_message_t msg;
- mavlink_mission_count_t wpc;
-
- wpc.target_system = sysid;
- wpc.target_component = compid;
- wpc.count = mission.count;
-
- mavlink_msg_mission_count_encode_chan(mavlink_system.sysid, _mavlink_wpm_comp_id, _channel, &msg, &wpc);
- mavlink_missionlib_send_message(&msg);
-
- if (_verbose) { warnx("Sent waypoint count (%u) to ID %u", wpc.count, wpc.target_system); }
-}
-
-void Mavlink::mavlink_wpm_send_waypoint(uint8_t sysid, uint8_t compid, uint16_t seq)
-{
+ pthread_mutex_lock(&_send_mutex);
- struct mission_item_s mission_item;
- ssize_t len = sizeof(struct mission_item_s);
+ unsigned buf_free = get_free_tx_buf();
- dm_item_t dm_current;
+ _last_write_try_time = hrt_absolute_time();
- if (_wpm->current_dataman_id == 0) {
- dm_current = DM_KEY_WAYPOINTS_OFFBOARD_0;
+ unsigned packet_len = msg->len + MAVLINK_NUM_NON_PAYLOAD_BYTES;
- } else {
- dm_current = DM_KEY_WAYPOINTS_OFFBOARD_1;
+ /* check if there is space in the buffer, let it overflow else */
+ if ((buf_free < TX_BUFFER_GAP) || (buf_free < packet_len)) {
+ /* no enough space in buffer to send */
+ count_txerr();
+ count_txerrbytes(packet_len);
+ pthread_mutex_unlock(&_send_mutex);
+ return;
}
- if (dm_read(dm_current, seq, &mission_item, len) == len) {
+ uint8_t buf[MAVLINK_MAX_PACKET_LEN];
- /* create mission_item_s from mavlink_mission_item_t */
- mavlink_mission_item_t wp;
- map_mission_item_to_mavlink_mission_item(&mission_item, &wp);
+ /* header and payload */
+ memcpy(&buf[0], &msg->magic, MAVLINK_NUM_HEADER_BYTES + msg->len);
- mavlink_message_t msg;
- wp.target_system = sysid;
- wp.target_component = compid;
- wp.seq = seq;
- mavlink_msg_mission_item_encode_chan(mavlink_system.sysid, _mavlink_wpm_comp_id, _channel, &msg, &wp);
- mavlink_missionlib_send_message(&msg);
+ /* checksum */
+ buf[MAVLINK_NUM_HEADER_BYTES + msg->len] = (uint8_t)(msg->checksum & 0xFF);
+ buf[MAVLINK_NUM_HEADER_BYTES + msg->len + 1] = (uint8_t)(msg->checksum >> 8);
- if (_verbose) { warnx("Sent waypoint %u to ID %u", wp.seq, wp.target_system); }
+ /* send message to UART */
+ ssize_t ret = write(_uart_fd, buf, packet_len);
- } else {
- mavlink_wpm_send_waypoint_ack(_wpm->current_partner_sysid, _wpm->current_partner_compid, MAV_MISSION_ERROR);
- mavlink_missionlib_send_gcs_string("#audio: Unable to read from micro SD");
-
- if (_verbose) { warnx("ERROR: could not read WP%u", seq); }
- }
-}
-
-void Mavlink::mavlink_wpm_send_waypoint_request(uint8_t sysid, uint8_t compid, uint16_t seq)
-{
- if (seq < _wpm->max_size) {
- mavlink_message_t msg;
- mavlink_mission_request_t wpr;
- wpr.target_system = sysid;
- wpr.target_component = compid;
- wpr.seq = seq;
- mavlink_msg_mission_request_encode_chan(mavlink_system.sysid, _mavlink_wpm_comp_id, _channel, &msg, &wpr);
- mavlink_missionlib_send_message(&msg);
- _wpm->timestamp_last_send_request = hrt_absolute_time();
-
- if (_verbose) { warnx("Sent waypoint request %u to ID %u", wpr.seq, wpr.target_system); }
+ if (ret != (int) packet_len) {
+ count_txerr();
+ count_txerrbytes(packet_len);
} else {
- mavlink_missionlib_send_gcs_string("ERROR: Waypoint index exceeds list capacity");
+ _last_write_success_time = _last_write_try_time;
+ count_txbytes(packet_len);
}
-}
-/*
- * @brief emits a message that a waypoint reached
- *
- * This function broadcasts a message that a waypoint is reached.
- *
- * @param seq The waypoint sequence number the MAV has reached.
- */
-void Mavlink::mavlink_wpm_send_waypoint_reached(uint16_t seq)
-{
- mavlink_message_t msg;
- mavlink_mission_item_reached_t wp_reached;
-
- wp_reached.seq = seq;
-
- mavlink_msg_mission_item_reached_encode_chan(mavlink_system.sysid, _mavlink_wpm_comp_id, _channel, &msg, &wp_reached);
- mavlink_missionlib_send_message(&msg);
-
- if (_verbose) { warnx("Sent waypoint %u reached message", wp_reached.seq); }
+ pthread_mutex_unlock(&_send_mutex);
}
-void Mavlink::mavlink_waypoint_eventloop(uint64_t now)
+void
+Mavlink::handle_message(const mavlink_message_t *msg)
{
- /* check for timed-out operations */
- if (now - _wpm->timestamp_lastaction > _wpm->timeout && _wpm->current_state != MAVLINK_WPM_STATE_IDLE) {
+ /* handle packet with mission manager */
+ _mission_manager->handle_message(msg);
- mavlink_missionlib_send_gcs_string("Operation timeout");
+ /* handle packet with parameter component */
+ _parameters_manager->handle_message(msg);
- _wpm->current_state = MAVLINK_WPM_STATE_IDLE;
- _wpm->current_partner_sysid = 0;
- _wpm->current_partner_compid = 0;
-
- } else if (now - _wpm->timestamp_last_send_request > 500000 && _wpm->current_state == MAVLINK_WPM_STATE_GETLIST_GETWPS) {
- /* try to get WP again after short timeout */
- mavlink_wpm_send_waypoint_request(_wpm->current_partner_sysid, _wpm->current_partner_compid, _wpm->current_wp_id);
+ if (get_forwarding_on()) {
+ /* forward any messages to other mavlink instances */
+ Mavlink::forward_message(msg, this);
}
}
-
-void Mavlink::mavlink_wpm_message_handler(const mavlink_message_t *msg)
+void
+Mavlink::send_statustext_info(const char *string)
{
- uint64_t now = hrt_absolute_time();
-
- switch (msg->msgid) {
-
- case MAVLINK_MSG_ID_MISSION_ACK: {
- mavlink_mission_ack_t wpa;
- mavlink_msg_mission_ack_decode(msg, &wpa);
-
- if ((msg->sysid == _wpm->current_partner_sysid && msg->compid == _wpm->current_partner_compid) && (wpa.target_system == mavlink_system.sysid /*&& wpa.target_component == mavlink_wpm_comp_id*/)) {
- _wpm->timestamp_lastaction = now;
-
- if (_wpm->current_state == MAVLINK_WPM_STATE_SENDLIST || _wpm->current_state == MAVLINK_WPM_STATE_SENDLIST_SENDWPS) {
- if (_wpm->current_wp_id == _wpm->size - 1) {
-
- _wpm->current_state = MAVLINK_WPM_STATE_IDLE;
- _wpm->current_wp_id = 0;
- }
- }
-
- } else {
- mavlink_missionlib_send_gcs_string("REJ. WP CMD: curr partner id mismatch");
- }
-
- break;
- }
-
- case MAVLINK_MSG_ID_MISSION_SET_CURRENT: {
- mavlink_mission_set_current_t wpc;
- mavlink_msg_mission_set_current_decode(msg, &wpc);
-
- if (wpc.target_system == mavlink_system.sysid /*&& wpc.target_component == mavlink_wpm_comp_id*/) {
- _wpm->timestamp_lastaction = now;
-
- if (_wpm->current_state == MAVLINK_WPM_STATE_IDLE) {
- if (wpc.seq < _wpm->size) {
-
- mission.current_index = wpc.seq;
- publish_mission();
-
- /* don't answer yet, wait for the navigator to respond, then publish the mission_result */
-// mavlink_wpm_send_waypoint_current(wpc.seq);
-
- } else {
- mavlink_missionlib_send_gcs_string("IGN WP CURR CMD: Not in list");
- }
-
- } else {
- mavlink_missionlib_send_gcs_string("IGN WP CURR CMD: Busy");
- }
- }
-
- break;
- }
-
- case MAVLINK_MSG_ID_MISSION_REQUEST_LIST: {
- mavlink_mission_request_list_t wprl;
- mavlink_msg_mission_request_list_decode(msg, &wprl);
-
- if (wprl.target_system == mavlink_system.sysid /*&& wprl.target_component == mavlink_wpm_comp_id*/) {
- _wpm->timestamp_lastaction = now;
-
- if (_wpm->current_state == MAVLINK_WPM_STATE_IDLE || _wpm->current_state == MAVLINK_WPM_STATE_SENDLIST) {
- if (_wpm->size > 0) {
-
- _wpm->current_state = MAVLINK_WPM_STATE_SENDLIST;
- _wpm->current_wp_id = 0;
- _wpm->current_partner_sysid = msg->sysid;
- _wpm->current_partner_compid = msg->compid;
-
- } else {
- if (_verbose) { warnx("No waypoints send"); }
- }
-
- _wpm->current_count = _wpm->size;
- mavlink_wpm_send_waypoint_count(msg->sysid, msg->compid, _wpm->current_count);
-
- } else {
- mavlink_missionlib_send_gcs_string("IGN REQUEST LIST: Busy");
- }
- }
-
- break;
- }
-
- case MAVLINK_MSG_ID_MISSION_REQUEST: {
- mavlink_mission_request_t wpr;
- mavlink_msg_mission_request_decode(msg, &wpr);
-
- if (msg->sysid == _wpm->current_partner_sysid && msg->compid == _wpm->current_partner_compid && wpr.target_system == mavlink_system.sysid /*&& wpr.target_component == mavlink_wpm_comp_id*/) {
- _wpm->timestamp_lastaction = now;
-
- if (wpr.seq >= _wpm->size) {
-
- mavlink_missionlib_send_gcs_string("REJ. WP CMD: Req. WP not in list");
-
- break;
- }
-
- /*
- * Ensure that we are in the correct state and that the first request has id 0
- * and the following requests have either the last id (re-send last waypoint) or last_id+1 (next waypoint)
- */
- if (_wpm->current_state == MAVLINK_WPM_STATE_SENDLIST) {
-
- if (wpr.seq == 0) {
- if (_verbose) { warnx("Got ITEM_REQUEST of waypoint %u from %u changing to STATE_SENDLIST_SENDWPS", wpr.seq, msg->sysid); }
-
- _wpm->current_state = MAVLINK_WPM_STATE_SENDLIST_SENDWPS;
-
- } else {
- mavlink_missionlib_send_gcs_string("REJ. WP CMD: First id != 0");
-
- break;
- }
-
- } else if (_wpm->current_state == MAVLINK_WPM_STATE_SENDLIST_SENDWPS) {
-
- if (wpr.seq == _wpm->current_wp_id) {
-
- if (_verbose) { warnx("Got ITEM_REQUEST of waypoint %u (again) from %u staying in STATE_SENDLIST_SENDWPS", wpr.seq, msg->sysid); }
-
- } else if (wpr.seq == _wpm->current_wp_id + 1) {
-
- if (_verbose) { warnx("Got ITEM_REQUEST of waypoint %u from %u staying in STATE_SENDLIST_SENDWPS", wpr.seq, msg->sysid); }
-
- } else {
- mavlink_missionlib_send_gcs_string("REJ. WP CMD: Req. WP was unexpected");
-
- break;
- }
-
- } else {
-
- mavlink_missionlib_send_gcs_string("REJ. WP CMD: Busy");
-
- break;
- }
-
- _wpm->current_wp_id = wpr.seq;
- _wpm->current_state = MAVLINK_WPM_STATE_SENDLIST_SENDWPS;
-
- if (wpr.seq < _wpm->size) {
-
- mavlink_wpm_send_waypoint(_wpm->current_partner_sysid, _wpm->current_partner_compid, _wpm->current_wp_id);
-
- } else {
- mavlink_wpm_send_waypoint_ack(_wpm->current_partner_sysid, _wpm->current_partner_compid, MAV_MISSION_ERROR);
-
- mavlink_missionlib_send_gcs_string("ERROR: Waypoint out of bounds");
- }
-
-
- } else {
- //we we're target but already communicating with someone else
- if ((wpr.target_system == mavlink_system.sysid /*&& wpr.target_component == mavlink_wpm_comp_id*/) && !(msg->sysid == _wpm->current_partner_sysid && msg->compid == _wpm->current_partner_compid)) {
-
- mavlink_missionlib_send_gcs_string("REJ. WP CMD: Busy");
-
- if (_verbose) { warnx("Ignored MAVLINK_MSG_ID_MISSION_ITEM_REQUEST from ID %u because i'm already talking to ID %u.", msg->sysid, _wpm->current_partner_sysid); }
- }
- }
-
- break;
- }
-
- case MAVLINK_MSG_ID_MISSION_COUNT: {
- mavlink_mission_count_t wpc;
- mavlink_msg_mission_count_decode(msg, &wpc);
-
- if (wpc.target_system == mavlink_system.sysid/* && wpc.target_component == mavlink_wpm_comp_id*/) {
- _wpm->timestamp_lastaction = now;
-
- if (_wpm->current_state == MAVLINK_WPM_STATE_IDLE) {
-
- if (wpc.count > NUM_MISSIONS_SUPPORTED) {
- if (_verbose) { warnx("Too many waypoints: %d, supported: %d", wpc.count, NUM_MISSIONS_SUPPORTED); }
-
- mavlink_wpm_send_waypoint_ack(_wpm->current_partner_sysid, _wpm->current_partner_compid, MAV_MISSION_NO_SPACE);
- break;
- }
-
- if (wpc.count == 0) {
- mavlink_missionlib_send_gcs_string("WP COUNT 0");
-
- break;
- }
-
- _wpm->current_state = MAVLINK_WPM_STATE_GETLIST;
- _wpm->current_wp_id = 0;
- _wpm->current_partner_sysid = msg->sysid;
- _wpm->current_partner_compid = msg->compid;
- _wpm->current_count = wpc.count;
-
- mavlink_wpm_send_waypoint_request(_wpm->current_partner_sysid, _wpm->current_partner_compid, _wpm->current_wp_id);
-
- } else if (_wpm->current_state == MAVLINK_WPM_STATE_GETLIST) {
-
- if (_wpm->current_wp_id == 0) {
- mavlink_missionlib_send_gcs_string("WP CMD OK AGAIN");
-
- } else {
- mavlink_missionlib_send_gcs_string("REJ. WP CMD: Busy with WP");
- }
-
- } else {
- mavlink_missionlib_send_gcs_string("IGN MISSION_COUNT CMD: Busy");
- }
- }
- }
- break;
-
- case MAVLINK_MSG_ID_MISSION_ITEM: {
- mavlink_mission_item_t wp;
- mavlink_msg_mission_item_decode(msg, &wp);
-
- if (wp.target_system == mavlink_system.sysid && wp.target_component == _mavlink_wpm_comp_id) {
-
- _wpm->timestamp_lastaction = now;
-
- /*
- * ensure that we are in the correct state and that the first waypoint has id 0
- * and the following waypoints have the correct ids
- */
-
- if (_wpm->current_state == MAVLINK_WPM_STATE_GETLIST) {
-
- if (wp.seq != 0) {
- mavlink_missionlib_send_gcs_string("Ignored MISSION_ITEM WP not 0");
- break;
- }
-
- } else if (_wpm->current_state == MAVLINK_WPM_STATE_GETLIST_GETWPS) {
-
- if (wp.seq >= _wpm->current_count) {
- mavlink_missionlib_send_gcs_string("Ignored MISSION_ITEM WP out of bounds");
- break;
- }
-
- if (wp.seq != _wpm->current_wp_id) {
- mavlink_missionlib_send_gcs_string("IGN: waypoint ID mismatch");
- mavlink_wpm_send_waypoint_request(_wpm->current_partner_sysid, _wpm->current_partner_compid, _wpm->current_wp_id);
- break;
- }
- }
-
- _wpm->current_state = MAVLINK_WPM_STATE_GETLIST_GETWPS;
-
- struct mission_item_s mission_item;
-
- int ret = map_mavlink_mission_item_to_mission_item(&wp, &mission_item);
-
- if (ret != OK) {
- mavlink_wpm_send_waypoint_ack(_wpm->current_partner_sysid, _wpm->current_partner_compid, ret);
- _wpm->current_state = MAVLINK_WPM_STATE_IDLE;
- break;
- }
-
- ssize_t len = sizeof(struct mission_item_s);
-
- dm_item_t dm_next;
-
- if (_wpm->current_dataman_id == 0) {
- dm_next = DM_KEY_WAYPOINTS_OFFBOARD_1;
- mission.dataman_id = 1;
-
- } else {
- dm_next = DM_KEY_WAYPOINTS_OFFBOARD_0;
- mission.dataman_id = 0;
- }
-
- if (dm_write(dm_next, wp.seq, DM_PERSIST_IN_FLIGHT_RESET, &mission_item, len) != len) {
- mavlink_wpm_send_waypoint_ack(_wpm->current_partner_sysid, _wpm->current_partner_compid, MAV_MISSION_ERROR);
- mavlink_missionlib_send_gcs_string("#audio: Unable to write on micro SD");
- _wpm->current_state = MAVLINK_WPM_STATE_IDLE;
- break;
- }
-
-// if (wp.current) {
-// warnx("current is: %d", wp.seq);
-// mission.current_index = wp.seq;
-// }
- // XXX ignore current set
- mission.current_index = -1;
-
- _wpm->current_wp_id = wp.seq + 1;
-
- if (_wpm->current_wp_id == _wpm->current_count && _wpm->current_state == MAVLINK_WPM_STATE_GETLIST_GETWPS) {
-
- if (_verbose) { warnx("Got all %u waypoints, changing state to MAVLINK_WPM_STATE_IDLE", _wpm->current_count); }
-
- mavlink_wpm_send_waypoint_ack(_wpm->current_partner_sysid, _wpm->current_partner_compid, MAV_MISSION_ACCEPTED);
-
- mission.count = _wpm->current_count;
-
- publish_mission();
-
- _wpm->current_dataman_id = mission.dataman_id;
- _wpm->size = _wpm->current_count;
-
- _wpm->current_state = MAVLINK_WPM_STATE_IDLE;
-
- } else {
- mavlink_wpm_send_waypoint_request(_wpm->current_partner_sysid, _wpm->current_partner_compid, _wpm->current_wp_id);
- }
- }
-
- break;
- }
-
- case MAVLINK_MSG_ID_MISSION_CLEAR_ALL: {
- mavlink_mission_clear_all_t wpca;
- mavlink_msg_mission_clear_all_decode(msg, &wpca);
-
- if (wpca.target_system == mavlink_system.sysid /*&& wpca.target_component == mavlink_wpm_comp_id */) {
-
- if (_wpm->current_state == MAVLINK_WPM_STATE_IDLE) {
- _wpm->timestamp_lastaction = now;
-
- _wpm->size = 0;
-
- /* prepare mission topic */
- mission.dataman_id = -1;
- mission.count = 0;
- mission.current_index = -1;
- publish_mission();
-
- if (dm_clear(DM_KEY_WAYPOINTS_OFFBOARD_0) == OK && dm_clear(DM_KEY_WAYPOINTS_OFFBOARD_1) == OK) {
- mavlink_wpm_send_waypoint_ack(_wpm->current_partner_sysid, _wpm->current_partner_compid, MAV_MISSION_ACCEPTED);
-
- } else {
- mavlink_wpm_send_waypoint_ack(_wpm->current_partner_sysid, _wpm->current_partner_compid, MAV_MISSION_ERROR);
- }
-
-
- } else {
- mavlink_missionlib_send_gcs_string("IGN WP CLEAR CMD: Busy");
-
- if (_verbose) { warnx("IGN WP CLEAR CMD: Busy"); }
- }
- }
-
- break;
- }
-
- default: {
- /* other messages might should get caught by mavlink and others */
- break;
- }
- }
+ send_statustext(MAV_SEVERITY_INFO, string);
}
void
-Mavlink::mavlink_missionlib_send_message(mavlink_message_t *msg)
+Mavlink::send_statustext_critical(const char *string)
{
- uint8_t buf[MAVLINK_MAX_PACKET_LEN];
-
- uint16_t len = mavlink_msg_to_send_buffer(buf, msg);
- mavlink_send_uart_bytes(_channel, buf, len);
+ send_statustext(MAV_SEVERITY_CRITICAL, string);
}
-
-
-int
-Mavlink::mavlink_missionlib_send_gcs_string(const char *string)
+void
+Mavlink::send_statustext_emergency(const char *string)
{
- const int len = MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN;
- mavlink_statustext_t statustext;
- statustext.severity = MAV_SEVERITY_INFO;
-
- int i = 0;
-
- while (i < len - 1) {
- statustext.text[i] = string[i];
-
- if (string[i] == '\0') {
- break;
- }
-
- i++;
- }
-
- if (i > 1) {
- /* Enforce null termination */
- statustext.text[i] = '\0';
+ send_statustext(MAV_SEVERITY_EMERGENCY, string);
+}
- mavlink_msg_statustext_send(_channel, statustext.severity, statustext.text);
- return OK;
+void
+Mavlink::send_statustext(unsigned char severity, const char *string)
+{
+ struct mavlink_logmessage logmsg;
+ strncpy(logmsg.text, string, sizeof(logmsg.text));
+ logmsg.severity = severity;
- } else {
- return 1;
- }
+ mavlink_logbuffer_write(&_logbuffer, &logmsg);
}
MavlinkOrbSubscription *Mavlink::add_orb_subscription(const orb_id_t topic)
@@ -1537,11 +924,17 @@ MavlinkOrbSubscription *Mavlink::add_orb_subscription(const orb_id_t topic)
return sub_new;
}
+unsigned int
+Mavlink::interval_from_rate(float rate)
+{
+ return (rate > 0.0f) ? (1000000.0f / rate) : 0;
+}
+
int
Mavlink::configure_stream(const char *stream_name, const float rate)
{
/* calculate interval in us, 0 means disabled stream */
- unsigned int interval = (rate > 0.0f) ? (1000000.0f / rate) : 0;
+ unsigned int interval = interval_from_rate(rate);
/* search if stream exists */
MavlinkStream *stream;
@@ -1572,10 +965,8 @@ Mavlink::configure_stream(const char *stream_name, const float rate)
if (strcmp(stream_name, streams_list[i]->get_name()) == 0) {
/* create new instance */
- stream = streams_list[i]->new_instance();
- stream->set_channel(get_channel());
+ stream = streams_list[i]->new_instance(this);
stream->set_interval(interval);
- stream->subscribe(this);
LL_APPEND(_streams, stream);
return OK;
@@ -1589,6 +980,31 @@ Mavlink::configure_stream(const char *stream_name, const float rate)
}
void
+Mavlink::adjust_stream_rates(const float multiplier)
+{
+ /* do not allow to push us to zero */
+ if (multiplier < 0.01f) {
+ return;
+ }
+
+ /* search if stream exists */
+ MavlinkStream *stream;
+ LL_FOREACH(_streams, stream) {
+ /* set new interval */
+ unsigned interval = stream->get_interval();
+ interval /= multiplier;
+
+ /* allow max ~600 Hz */
+ if (interval < 1600) {
+ interval = 1600;
+ }
+
+ /* set new interval */
+ stream->set_interval(interval * multiplier);
+ }
+}
+
+void
Mavlink::configure_stream_threadsafe(const char *stream_name, const float rate)
{
/* orb subscription must be done from the main thread,
@@ -1619,11 +1035,23 @@ Mavlink::configure_stream_threadsafe(const char *stream_name, const float rate)
int
Mavlink::message_buffer_init(int size)
{
+
_message_buffer.size = size;
_message_buffer.write_ptr = 0;
_message_buffer.read_ptr = 0;
- _message_buffer.data = (char*)malloc(_message_buffer.size);
- return (_message_buffer.data == 0) ? ERROR : OK;
+ _message_buffer.data = (char *)malloc(_message_buffer.size);
+
+ int ret;
+
+ if (_message_buffer.data == 0) {
+ ret = ERROR;
+ _message_buffer.size = 0;
+
+ } else {
+ ret = OK;
+ }
+
+ return ret;
}
void
@@ -1655,7 +1083,7 @@ Mavlink::message_buffer_is_empty()
bool
-Mavlink::message_buffer_write(void *ptr, int size)
+Mavlink::message_buffer_write(const void *ptr, int size)
{
// bytes available to write
int available = _message_buffer.read_ptr - _message_buffer.write_ptr - 1;
@@ -1722,7 +1150,7 @@ Mavlink::message_buffer_mark_read(int n)
}
void
-Mavlink::pass_message(mavlink_message_t *msg)
+Mavlink::pass_message(const mavlink_message_t *msg)
{
if (_passing_on) {
/* size is 8 bytes plus variable payload */
@@ -1733,7 +1161,31 @@ Mavlink::pass_message(mavlink_message_t *msg)
}
}
+float
+Mavlink::get_rate_mult()
+{
+ return _rate_mult;
+}
+
+void
+Mavlink::update_rate_mult()
+{
+ float const_rate = 0.0f;
+ float rate = 0.0f;
+ MavlinkStream *stream;
+ LL_FOREACH(_streams, stream) {
+ if (stream->const_rate()) {
+ const_rate += stream->get_size() * 1000000.0f / stream->get_interval();
+
+ } else {
+ rate += stream->get_size() * 1000000.0f / stream->get_interval();
+ }
+ }
+
+ /* don't scale up rates, only scale down if needed */
+ _rate_mult = fminf(1.0f, ((float)_datarate - const_rate) / rate);
+}
int
Mavlink::task_main(int argc, char *argv[])
@@ -1751,7 +1203,7 @@ Mavlink::task_main(int argc, char *argv[])
* set error flag instead */
bool err_flag = false;
- while ((ch = getopt(argc, argv, "b:r:d:m:fpvw")) != EOF) {
+ while ((ch = getopt(argc, argv, "b:r:d:m:fpvwx")) != EOF) {
switch (ch) {
case 'b':
_baudrate = strtoul(optarg, NULL, 10);
@@ -1786,7 +1238,10 @@ Mavlink::task_main(int argc, char *argv[])
_mode = MAVLINK_MODE_CUSTOM;
} else if (strcmp(optarg, "camera") == 0) {
- _mode = MAVLINK_MODE_CAMERA;
+ // left in here for compatibility
+ _mode = MAVLINK_MODE_ONBOARD;
+ } else if (strcmp(optarg, "onboard") == 0) {
+ _mode = MAVLINK_MODE_ONBOARD;
}
break;
@@ -1807,6 +1262,10 @@ Mavlink::task_main(int argc, char *argv[])
_wait_to_transmit = true;
break;
+ case 'x':
+ _ftp_on = true;
+ break;
+
default:
err_flag = true;
break;
@@ -1842,8 +1301,8 @@ Mavlink::task_main(int argc, char *argv[])
warnx("mode: CUSTOM");
break;
- case MAVLINK_MODE_CAMERA:
- warnx("mode: CAMERA");
+ case MAVLINK_MODE_ONBOARD:
+ warnx("mode: ONBOARD");
break;
default:
@@ -1851,8 +1310,6 @@ Mavlink::task_main(int argc, char *argv[])
break;
}
- _mavlink_wpm_comp_id = MAV_COMP_ID_MISSIONPLANNER;
-
warnx("data rate: %d Bytes/s, port: %s, baud: %d", _datarate, _device_name, _baudrate);
/* flush stdout in case MAVLink is about to take it over */
@@ -1868,13 +1325,19 @@ Mavlink::task_main(int argc, char *argv[])
return ERROR;
}
+ /* initialize send mutex */
+ pthread_mutex_init(&_send_mutex, NULL);
+
/* initialize mavlink text message buffering */
mavlink_logbuffer_init(&_logbuffer, 5);
/* if we are passing on mavlink messages, we need to prepare a buffer for this instance */
- if (_passing_on) {
- /* initialize message buffer if multiplexing is on */
- if (OK != message_buffer_init(300)) {
+ if (_passing_on || _ftp_on) {
+ /* initialize message buffer if multiplexing is on or its needed for FTP.
+ * make space for two messages plus off-by-one space as we use the empty element
+ * marker ring buffer approach.
+ */
+ if (OK != message_buffer_init(2 * sizeof(mavlink_message_t) + 1)) {
errx(1, "can't allocate message buffer, exiting");
}
@@ -1894,13 +1357,6 @@ Mavlink::task_main(int argc, char *argv[])
/* start the MAVLink receiver */
_receive_thread = MavlinkReceiver::receive_start(this);
- /* initialize waypoint manager */
- mavlink_wpm_init(_wpm);
-
- int mission_result_sub = orb_subscribe(ORB_ID(mission_result));
- struct mission_result_s mission_result;
- memset(&mission_result, 0, sizeof(mission_result));
-
_task_running = true;
MavlinkOrbSubscription *param_sub = add_orb_subscription(ORB_ID(parameter_update));
@@ -1911,49 +1367,63 @@ Mavlink::task_main(int argc, char *argv[])
struct vehicle_status_s status;
status_sub->update(&status_time, &status);
- MavlinkCommandsStream commands_stream(this, _channel);
-
- /* add default streams depending on mode and intervals depending on datarate */
- float rate_mult = _datarate / 1000.0f;
+ /* add default streams depending on mode */
+ /* HEARTBEAT is constant rate stream, rate never adjusted */
configure_stream("HEARTBEAT", 1.0f);
+ /* STATUSTEXT stream is like normal stream but gets messages from logbuffer instead of uORB */
+ configure_stream("STATUSTEXT", 20.0f);
+
+ /* COMMAND_LONG stream: use high rate to avoid commands skipping */
+ configure_stream("COMMAND_LONG", 100.0f);
+
+ /* PARAM_VALUE stream */
+ _parameters_manager = (MavlinkParametersManager *) MavlinkParametersManager::new_instance(this);
+ _parameters_manager->set_interval(interval_from_rate(30.0f));
+ LL_APPEND(_streams, _parameters_manager);
+
+ /* MISSION_STREAM stream, actually sends all MISSION_XXX messages at some rate depending on
+ * remote requests rate. Rate specified here controls how much bandwidth we will reserve for
+ * mission messages. */
+ _mission_manager = (MavlinkMissionManager *) MavlinkMissionManager::new_instance(this);
+ _mission_manager->set_interval(interval_from_rate(10.0f));
+ _mission_manager->set_verbose(_verbose);
+ LL_APPEND(_streams, _mission_manager);
+
switch (_mode) {
case MAVLINK_MODE_NORMAL:
configure_stream("SYS_STATUS", 1.0f);
configure_stream("GPS_GLOBAL_ORIGIN", 0.5f);
- configure_stream("HIGHRES_IMU", 1.0f * rate_mult);
- configure_stream("ATTITUDE", 10.0f * rate_mult);
- configure_stream("VFR_HUD", 10.0f * rate_mult);
- configure_stream("GPS_RAW_INT", 1.0f * rate_mult);
- configure_stream("GLOBAL_POSITION_INT", 3.0f * rate_mult);
- configure_stream("LOCAL_POSITION_NED", 3.0f * rate_mult);
- configure_stream("RC_CHANNELS_RAW", 1.0f * rate_mult);
- configure_stream("NAMED_VALUE_FLOAT", 1.0f * rate_mult);
- configure_stream("GLOBAL_POSITION_SETPOINT_INT", 3.0f * rate_mult);
- configure_stream("ROLL_PITCH_YAW_THRUST_SETPOINT", 3.0f * rate_mult);
+ configure_stream("HIGHRES_IMU", 1.0f);
+ configure_stream("ATTITUDE", 10.0f);
+ configure_stream("VFR_HUD", 8.0f);
+ configure_stream("GPS_RAW_INT", 1.0f);
+ configure_stream("GLOBAL_POSITION_INT", 3.0f);
+ configure_stream("LOCAL_POSITION_NED", 3.0f);
+ configure_stream("RC_CHANNELS_RAW", 1.0f);
+ configure_stream("POSITION_TARGET_GLOBAL_INT", 3.0f);
+ configure_stream("ATTITUDE_TARGET", 3.0f);
configure_stream("DISTANCE_SENSOR", 0.5f);
+ configure_stream("OPTICAL_FLOW", 5.0f);
break;
- case MAVLINK_MODE_CAMERA:
+ case MAVLINK_MODE_ONBOARD:
configure_stream("SYS_STATUS", 1.0f);
- configure_stream("ATTITUDE", 15.0f * rate_mult);
- configure_stream("GLOBAL_POSITION_INT", 15.0f * rate_mult);
- configure_stream("CAMERA_CAPTURE", 1.0f);
+ configure_stream("ATTITUDE", 50.0f);
+ configure_stream("GLOBAL_POSITION_INT", 50.0f);
+ configure_stream("CAMERA_CAPTURE", 2.0f);
+ configure_stream("ATTITUDE_TARGET", 10.0f);
+ configure_stream("POSITION_TARGET_GLOBAL_INT", 10.0f);
+ configure_stream("VFR_HUD", 10.0f);
break;
default:
break;
}
- /* don't send parameters on startup without request */
- _mavlink_param_queue_index = param_count();
-
- MavlinkRateLimiter slow_rate_limiter(2000000.0f / rate_mult);
- MavlinkRateLimiter fast_rate_limiter(30000.0f / rate_mult);
-
/* set main loop delay depending on data rate to minimize CPU overhead */
- _main_loop_delay = MAIN_LOOP_DELAY / rate_mult;
+ _main_loop_delay = MAIN_LOOP_DELAY * 1000 / _datarate;
/* now the instance is fully initialized and we can bump the instance count */
LL_APPEND(_mavlink_instances, this);
@@ -1966,6 +1436,8 @@ Mavlink::task_main(int argc, char *argv[])
hrt_abstime t = hrt_absolute_time();
+ update_rate_mult();
+
if (param_sub->update(&param_time, nullptr)) {
/* parameters updated */
mavlink_update_system();
@@ -1976,14 +1448,12 @@ Mavlink::task_main(int argc, char *argv[])
set_hil_enabled(status.hil_state == HIL_STATE_ON);
}
- /* update commands stream */
- commands_stream.update(t);
-
/* check for requested subscriptions */
if (_subscribe_to_stream != nullptr) {
if (OK == configure_stream(_subscribe_to_stream, _subscribe_to_stream_rate)) {
if (_subscribe_to_stream_rate > 0.0f) {
- warnx("stream %s on device %s enabled with rate %.1f Hz", _subscribe_to_stream, _device_name, (double)_subscribe_to_stream_rate);
+ warnx("stream %s on device %s enabled with rate %.1f Hz", _subscribe_to_stream, _device_name,
+ (double)_subscribe_to_stream_rate);
} else {
warnx("stream %s on device %s disabled", _subscribe_to_stream, _device_name);
@@ -2003,70 +1473,68 @@ Mavlink::task_main(int argc, char *argv[])
stream->update(t);
}
- bool updated;
- orb_check(mission_result_sub, &updated);
+ /* pass messages from other UARTs or FTP worker */
+ if (_passing_on || _ftp_on) {
- if (updated) {
- orb_copy(ORB_ID(mission_result), mission_result_sub, &mission_result);
-
- if (_verbose) { warnx("Got mission result: new current: %d", mission_result.index_current_mission); }
-
- if (mission_result.mission_reached) {
- mavlink_wpm_send_waypoint_reached((uint16_t)mission_result.mission_index_reached);
- }
+ bool is_part;
+ uint8_t *read_ptr;
+ uint8_t *write_ptr;
- mavlink_wpm_send_waypoint_current((uint16_t)mission_result.index_current_mission);
+ pthread_mutex_lock(&_message_buffer_mutex);
+ int available = message_buffer_get_ptr((void **)&read_ptr, &is_part);
+ pthread_mutex_unlock(&_message_buffer_mutex);
- } else {
- if (slow_rate_limiter.check(t)) {
- mavlink_wpm_send_waypoint_current((uint16_t)mission_result.index_current_mission);
- }
- }
+ if (available > 0) {
+ // Reconstruct message from buffer
- if (fast_rate_limiter.check(t)) {
- mavlink_pm_queued_send();
- mavlink_waypoint_eventloop(hrt_absolute_time());
+ mavlink_message_t msg;
+ write_ptr = (uint8_t *)&msg;
- if (!mavlink_logbuffer_is_empty(&_logbuffer)) {
- struct mavlink_logmessage msg;
- int lb_ret = mavlink_logbuffer_read(&_logbuffer, &msg);
+ // Pull a single message from the buffer
+ size_t read_count = available;
- if (lb_ret == OK) {
- mavlink_missionlib_send_gcs_string(msg.text);
+ if (read_count > sizeof(mavlink_message_t)) {
+ read_count = sizeof(mavlink_message_t);
}
- }
- }
- /* pass messages from other UARTs */
- if (_passing_on) {
+ memcpy(write_ptr, read_ptr, read_count);
- bool is_part;
- void *read_ptr;
+ // We hold the mutex until after we complete the second part of the buffer. If we don't
+ // we may end up breaking the empty slot overflow detection semantics when we mark the
+ // possibly partial read below.
+ pthread_mutex_lock(&_message_buffer_mutex);
- /* guard get ptr by mutex */
- pthread_mutex_lock(&_message_buffer_mutex);
- int available = message_buffer_get_ptr(&read_ptr, &is_part);
- pthread_mutex_unlock(&_message_buffer_mutex);
-
- if (available > 0) {
- /* write first part of buffer */
- _mavlink_resend_uart(_channel, (const mavlink_message_t*)read_ptr);
- message_buffer_mark_read(available);
+ message_buffer_mark_read(read_count);
/* write second part of buffer if there is some */
- if (is_part) {
- /* guard get ptr by mutex */
- pthread_mutex_lock(&_message_buffer_mutex);
- available = message_buffer_get_ptr(&read_ptr, &is_part);
- pthread_mutex_unlock(&_message_buffer_mutex);
-
- _mavlink_resend_uart(_channel, (const mavlink_message_t*)read_ptr);
+ if (is_part && read_count < sizeof(mavlink_message_t)) {
+ write_ptr += read_count;
+ available = message_buffer_get_ptr((void **)&read_ptr, &is_part);
+ read_count = sizeof(mavlink_message_t) - read_count;
+ memcpy(write_ptr, read_ptr, read_count);
message_buffer_mark_read(available);
}
+
+ pthread_mutex_unlock(&_message_buffer_mutex);
+
+ resend_message(&msg);
}
}
+ /* update TX/RX rates*/
+ if (t > _bytes_timestamp + 1000000) {
+ if (_bytes_timestamp != 0) {
+ float dt = (t - _bytes_timestamp) / 1000.0f;
+ _rate_tx = _bytes_tx / dt;
+ _rate_txerr = _bytes_txerr / dt;
+ _rate_rx = _bytes_rx / dt;
+ _bytes_tx = 0;
+ _bytes_txerr = 0;
+ _bytes_rx = 0;
+ }
+ _bytes_timestamp = t;
+ }
perf_end(_loop_perf);
}
@@ -2112,10 +1580,11 @@ Mavlink::task_main(int argc, char *argv[])
/* close mavlink logging device */
close(_mavlink_fd);
- if (_passing_on) {
+ if (_passing_on || _ftp_on) {
message_buffer_destroy();
pthread_mutex_destroy(&_message_buffer_mutex);
}
+
/* destroy log buffer */
mavlink_logbuffer_destroy(&_logbuffer);
@@ -2130,11 +1599,21 @@ int Mavlink::start_helper(int argc, char *argv[])
/* create the instance in task context */
Mavlink *instance = new Mavlink();
- /* this will actually only return once MAVLink exits */
- int res = instance->task_main(argc, argv);
+ int res;
+
+ if (!instance) {
- /* delete instance on main thread end */
- delete instance;
+ /* out of memory */
+ res = -ENOMEM;
+ warnx("OUT OF MEM");
+
+ } else {
+ /* this will actually only return once MAVLink exits */
+ res = instance->task_main(argc, argv);
+
+ /* delete instance on main thread end */
+ delete instance;
+ }
return res;
}
@@ -2157,7 +1636,7 @@ Mavlink::start(int argc, char *argv[])
task_spawn_cmd(buf,
SCHED_DEFAULT,
SCHED_PRIORITY_DEFAULT,
- 1950,
+ 2800,
(main_t)&Mavlink::start_helper,
(const char **)argv);
@@ -2185,13 +1664,50 @@ Mavlink::start(int argc, char *argv[])
}
void
-Mavlink::status()
+Mavlink::display_status()
{
- warnx("running");
+
+ if (_rstatus.heartbeat_time > 0) {
+ printf("\tGCS heartbeat:\t%llu us ago\n", hrt_elapsed_time(&_rstatus.heartbeat_time));
+ }
+
+ printf("\tmavlink chan: #%u\n", _channel);
+
+ if (_rstatus.timestamp > 0) {
+
+ printf("\ttype:\t\t");
+
+ switch (_rstatus.type) {
+ case TELEMETRY_STATUS_RADIO_TYPE_3DR_RADIO:
+ printf("3DR RADIO\n");
+ break;
+
+ default:
+ printf("UNKNOWN RADIO\n");
+ break;
+ }
+
+ printf("\trssi:\t\t%d\n", _rstatus.rssi);
+ printf("\tremote rssi:\t%u\n", _rstatus.remote_rssi);
+ printf("\ttxbuf:\t\t%u\n", _rstatus.txbuf);
+ printf("\tnoise:\t\t%d\n", _rstatus.noise);
+ printf("\tremote noise:\t%u\n", _rstatus.remote_noise);
+ printf("\trx errors:\t%u\n", _rstatus.rxerrors);
+ printf("\tfixed:\t\t%u\n", _rstatus.fixed);
+
+ } else {
+ printf("\tno telem status.\n");
+ }
+
+ printf("\trates:\n");
+ printf("\ttx: %.3f kB/s\n", (double)_rate_tx);
+ printf("\ttxerr: %.3f kB/s\n", (double)_rate_txerr);
+ printf("\trx: %.3f kB/s\n", (double)_rate_rx);
+ printf("\trate mult: %.3f\n", (double)_rate_mult);
}
int
-Mavlink::stream(int argc, char *argv[])
+Mavlink::stream_command(int argc, char *argv[])
{
const char *device_name = DEFAULT_DEVICE_NAME;
float rate = -1.0f;
@@ -2254,7 +1770,7 @@ Mavlink::stream(int argc, char *argv[])
static void usage()
{
- warnx("usage: mavlink {start|stop-all|stream} [-d device] [-b baudrate] [-r rate] [-m mode] [-s stream] [-f] [-p] [-v] [-w]");
+ warnx("usage: mavlink {start|stop-all|stream} [-d device] [-b baudrate]\n\t[-r rate][-m mode] [-s stream] [-f] [-p] [-v] [-w] [-x]");
}
int mavlink_main(int argc, char *argv[])
@@ -2275,11 +1791,11 @@ int mavlink_main(int argc, char *argv[])
} else if (!strcmp(argv[1], "stop-all")) {
return Mavlink::destroy_all_instances();
- // } else if (!strcmp(argv[1], "status")) {
- // mavlink::g_mavlink->status();
+ } else if (!strcmp(argv[1], "status")) {
+ return Mavlink::get_status_all_instances();
} else if (!strcmp(argv[1], "stream")) {
- return Mavlink::stream(argc, argv);
+ return Mavlink::stream_command(argc, argv);
} else {
usage();
diff --git a/src/modules/mavlink/mavlink_main.h b/src/modules/mavlink/mavlink_main.h
index 85a88442c..ad5e5001b 100644
--- a/src/modules/mavlink/mavlink_main.h
+++ b/src/modules/mavlink/mavlink_main.h
@@ -50,54 +50,15 @@
#include <uORB/uORB.h>
#include <uORB/topics/mission.h>
+#include <uORB/topics/mission_result.h>
+#include <uORB/topics/telemetry_status.h>
#include "mavlink_bridge_header.h"
#include "mavlink_orb_subscription.h"
#include "mavlink_stream.h"
#include "mavlink_messages.h"
-
-// FIXME XXX - TO BE MOVED TO XML
-enum MAVLINK_WPM_STATES {
- MAVLINK_WPM_STATE_IDLE = 0,
- MAVLINK_WPM_STATE_SENDLIST,
- MAVLINK_WPM_STATE_SENDLIST_SENDWPS,
- MAVLINK_WPM_STATE_GETLIST,
- MAVLINK_WPM_STATE_GETLIST_GETWPS,
- MAVLINK_WPM_STATE_GETLIST_GOTALL,
- MAVLINK_WPM_STATE_ENUM_END
-};
-
-enum MAVLINK_WPM_CODES {
- MAVLINK_WPM_CODE_OK = 0,
- MAVLINK_WPM_CODE_ERR_WAYPOINT_ACTION_NOT_SUPPORTED,
- MAVLINK_WPM_CODE_ERR_WAYPOINT_FRAME_NOT_SUPPORTED,
- MAVLINK_WPM_CODE_ERR_WAYPOINT_OUT_OF_BOUNDS,
- MAVLINK_WPM_CODE_ERR_WAYPOINT_MAX_NUMBER_EXCEEDED,
- MAVLINK_WPM_CODE_ENUM_END
-};
-
-
-#define MAVLINK_WPM_MAX_WP_COUNT 255
-#define MAVLINK_WPM_PROTOCOL_TIMEOUT_DEFAULT 5000000 ///< Protocol communication timeout in useconds
-#define MAVLINK_WPM_SETPOINT_DELAY_DEFAULT 1000000 ///< When to send a new setpoint
-#define MAVLINK_WPM_PROTOCOL_DELAY_DEFAULT 40000
-
-
-struct mavlink_wpm_storage {
- uint16_t size;
- uint16_t max_size;
- enum MAVLINK_WPM_STATES current_state;
- int16_t current_wp_id; ///< Waypoint in current transmission
- uint16_t current_count;
- uint8_t current_partner_sysid;
- uint8_t current_partner_compid;
- uint64_t timestamp_lastaction;
- uint64_t timestamp_last_send_setpoint;
- uint64_t timestamp_last_send_request;
- uint32_t timeout;
- int current_dataman_id;
-};
-
+#include "mavlink_mission.h"
+#include "mavlink_parameters.h"
class Mavlink
{
@@ -123,60 +84,73 @@ public:
/**
* Display the mavlink status.
*/
- void status();
+ void display_status();
+
+ static int stream_command(int argc, char *argv[]);
- static int stream(int argc, char *argv[]);
+ static int instance_count();
- static int instance_count();
+ static Mavlink *new_instance();
- static Mavlink *new_instance();
+ static Mavlink *get_instance(unsigned instance);
- static Mavlink *get_instance(unsigned instance);
+ static Mavlink *get_instance_for_device(const char *device_name);
- static Mavlink *get_instance_for_device(const char *device_name);
+ static int destroy_all_instances();
- static int destroy_all_instances();
+ static int get_status_all_instances();
- static bool instance_exists(const char *device_name, Mavlink *self);
+ static bool instance_exists(const char *device_name, Mavlink *self);
- static void forward_message(mavlink_message_t *msg, Mavlink *self);
+ static void forward_message(const mavlink_message_t *msg, Mavlink *self);
- static int get_uart_fd(unsigned index);
+ static int get_uart_fd(unsigned index);
- int get_uart_fd();
+ int get_uart_fd();
+
+ /**
+ * Get the MAVLink system id.
+ *
+ * @return The system ID of this vehicle
+ */
+ int get_system_id();
+
+ /**
+ * Get the MAVLink component id.
+ *
+ * @return The component ID of this vehicle
+ */
+ int get_component_id();
const char *_device_name;
enum MAVLINK_MODE {
MAVLINK_MODE_NORMAL = 0,
MAVLINK_MODE_CUSTOM,
- MAVLINK_MODE_CAMERA
+ MAVLINK_MODE_ONBOARD
};
- void set_mode(enum MAVLINK_MODE);
- enum MAVLINK_MODE get_mode() { return _mode; }
+ void set_mode(enum MAVLINK_MODE);
+ enum MAVLINK_MODE get_mode() { return _mode; }
- bool get_hil_enabled() { return _hil_enabled; }
+ bool get_hil_enabled() { return _hil_enabled; }
- bool get_use_hil_gps() { return _use_hil_gps; }
+ bool get_use_hil_gps() { return _use_hil_gps; }
- bool get_flow_control_enabled() { return _flow_control_enabled; }
+ bool get_forward_externalsp() { return _forward_externalsp; }
- bool get_forwarding_on() { return _forwarding_on; }
+ bool get_flow_control_enabled() { return _flow_control_enabled; }
- /**
- * Handle waypoint related messages.
- */
- void mavlink_wpm_message_handler(const mavlink_message_t *msg);
+ bool get_forwarding_on() { return _forwarding_on; }
- static int start_helper(int argc, char *argv[]);
+ static int start_helper(int argc, char *argv[]);
/**
* Handle parameter related messages.
*/
- void mavlink_pm_message_handler(const mavlink_channel_t chan, const mavlink_message_t *msg);
+ void mavlink_pm_message_handler(const mavlink_channel_t chan, const mavlink_message_t *msg);
- void get_mavlink_mode_and_state(struct vehicle_status_s *status, struct position_setpoint_triplet_s *pos_sp_triplet, uint8_t *mavlink_state, uint8_t *mavlink_base_mode, uint32_t *mavlink_custom_mode);
+ void get_mavlink_mode_and_state(struct vehicle_status_s *status, struct position_setpoint_triplet_s *pos_sp_triplet, uint8_t *mavlink_state, uint8_t *mavlink_base_mode, uint32_t *mavlink_custom_mode);
/**
* Enable / disable Hardware in the Loop simulation mode.
@@ -186,90 +160,182 @@ public:
* requested change could not be made or was
* redundant.
*/
- int set_hil_enabled(bool hil_enabled);
+ int set_hil_enabled(bool hil_enabled);
+
+ void send_message(const uint8_t msgid, const void *msg);
+
+ /**
+ * Resend message as is, don't change sequence number and CRC.
+ */
+ void resend_message(mavlink_message_t *msg);
+
+ void handle_message(const mavlink_message_t *msg);
MavlinkOrbSubscription *add_orb_subscription(const orb_id_t topic);
- int get_instance_id();
+ int get_instance_id();
/**
* Enable / disable hardware flow control.
*
* @param enabled True if hardware flow control should be enabled
*/
- int enable_flow_control(bool enabled);
+ int enable_flow_control(bool enabled);
+
+ mavlink_channel_t get_channel();
+
+ void configure_stream_threadsafe(const char *stream_name, float rate);
- mavlink_channel_t get_channel();
+ bool _task_should_exit; /**< if true, mavlink task should exit */
- bool _task_should_exit; /**< if true, mavlink task should exit */
+ int get_mavlink_fd() { return _mavlink_fd; }
- int get_mavlink_fd() { return _mavlink_fd; }
+ /**
+ * Send a status text with loglevel INFO
+ *
+ * @param string the message to send (will be capped by mavlink max string length)
+ */
+ void send_statustext_info(const char *string);
+ /**
+ * Send a status text with loglevel CRITICAL
+ *
+ * @param string the message to send (will be capped by mavlink max string length)
+ */
+ void send_statustext_critical(const char *string);
+
+ /**
+ * Send a status text with loglevel EMERGENCY
+ *
+ * @param string the message to send (will be capped by mavlink max string length)
+ */
+ void send_statustext_emergency(const char *string);
+
+ /**
+ * Send a status text with loglevel, the difference from mavlink_log_xxx() is that message sent
+ * only on this mavlink connection. Useful for reporting communication specific, not system-wide info
+ * only to client interested in it. Message will be not sent immediately but queued in buffer as
+ * for mavlink_log_xxx().
+ *
+ * @param string the message to send (will be capped by mavlink max string length)
+ * @param severity the log level
+ */
+ void send_statustext(unsigned char severity, const char *string);
+
+ MavlinkStream * get_streams() const { return _streams; }
+
+ float get_rate_mult();
/* Functions for waiting to start transmission until message received. */
- void set_has_received_messages(bool received_messages) { _received_messages = received_messages; }
- bool get_has_received_messages() { return _received_messages; }
- void set_wait_to_transmit(bool wait) { _wait_to_transmit = wait; }
- bool get_wait_to_transmit() { return _wait_to_transmit; }
- bool should_transmit() { return (!_wait_to_transmit || (_wait_to_transmit && _received_messages)); }
+ void set_has_received_messages(bool received_messages) { _received_messages = received_messages; }
+ bool get_has_received_messages() { return _received_messages; }
+ void set_wait_to_transmit(bool wait) { _wait_to_transmit = wait; }
+ bool get_wait_to_transmit() { return _wait_to_transmit; }
+ bool should_transmit() { return (!_wait_to_transmit || (_wait_to_transmit && _received_messages)); }
+
+ bool message_buffer_write(const void *ptr, int size);
+
+ void lockMessageBufferMutex(void) { pthread_mutex_lock(&_message_buffer_mutex); }
+ void unlockMessageBufferMutex(void) { pthread_mutex_unlock(&_message_buffer_mutex); }
+
+ /**
+ * Count a transmision error
+ */
+ void count_txerr();
+
+ /**
+ * Count transmitted bytes
+ */
+ void count_txbytes(unsigned n) { _bytes_tx += n; };
+
+ /**
+ * Count bytes not transmitted because of errors
+ */
+ void count_txerrbytes(unsigned n) { _bytes_txerr += n; };
+
+ /**
+ * Count received bytes
+ */
+ void count_rxbytes(unsigned n) { _bytes_rx += n; };
+
+ /**
+ * Get the receive status of this MAVLink link
+ */
+ struct telemetry_status_s& get_rx_status() { return _rstatus; }
+
+ struct mavlink_logbuffer *get_logbuffer() { return &_logbuffer; }
+
+ unsigned get_system_type() { return _system_type; }
protected:
- Mavlink *next;
+ Mavlink *next;
private:
- int _instance_id;
+ int _instance_id;
- int _mavlink_fd;
- bool _task_running;
+ int _mavlink_fd;
+ bool _task_running;
/* states */
- bool _hil_enabled; /**< Hardware In the Loop mode */
- bool _use_hil_gps; /**< Accept GPS HIL messages (for example from an external motion capturing system to fake indoor gps) */
- bool _is_usb_uart; /**< Port is USB */
- bool _wait_to_transmit; /**< Wait to transmit until received messages. */
- bool _received_messages; /**< Whether we've received valid mavlink messages. */
+ bool _hil_enabled; /**< Hardware In the Loop mode */
+ bool _use_hil_gps; /**< Accept GPS HIL messages (for example from an external motion capturing system to fake indoor gps) */
+ bool _forward_externalsp; /**< Forward external setpoint messages to controllers directly if in offboard mode */
+ bool _is_usb_uart; /**< Port is USB */
+ bool _wait_to_transmit; /**< Wait to transmit until received messages. */
+ bool _received_messages; /**< Whether we've received valid mavlink messages. */
- unsigned _main_loop_delay; /**< mainloop delay, depends on data rate */
+ unsigned _main_loop_delay; /**< mainloop delay, depends on data rate */
- MavlinkOrbSubscription *_subscriptions;
- MavlinkStream *_streams;
+ MavlinkOrbSubscription *_subscriptions;
+ MavlinkStream *_streams;
- orb_advert_t _mission_pub;
- struct mission_s mission;
- MAVLINK_MODE _mode;
+ MavlinkMissionManager *_mission_manager;
+ MavlinkParametersManager *_parameters_manager;
- uint8_t _mavlink_wpm_comp_id;
- mavlink_channel_t _channel;
+ MAVLINK_MODE _mode;
- struct mavlink_logbuffer _logbuffer;
- unsigned int _total_counter;
+ mavlink_channel_t _channel;
- pthread_t _receive_thread;
+ struct mavlink_logbuffer _logbuffer;
+ unsigned int _total_counter;
- /* Allocate storage space for waypoints */
- mavlink_wpm_storage _wpm_s;
- mavlink_wpm_storage *_wpm;
+ pthread_t _receive_thread;
- bool _verbose;
- bool _forwarding_on;
- bool _passing_on;
- int _uart_fd;
- int _baudrate;
- int _datarate;
+ bool _verbose;
+ bool _forwarding_on;
+ bool _passing_on;
+ bool _ftp_on;
+ int _uart_fd;
+ int _baudrate;
+ int _datarate; ///< data rate for normal streams (attitude, position, etc.)
+ int _datarate_events; ///< data rate for params, waypoints, text messages
+ float _rate_mult;
/**
* If the queue index is not at 0, the queue sending
* logic will send parameters from the current index
* to len - 1, the end of the param list.
*/
- unsigned int _mavlink_param_queue_index;
+ unsigned int _mavlink_param_queue_index;
+
+ bool mavlink_link_termination_allowed;
- bool mavlink_link_termination_allowed;
+ char *_subscribe_to_stream;
+ float _subscribe_to_stream_rate;
- char *_subscribe_to_stream;
- float _subscribe_to_stream_rate;
+ bool _flow_control_enabled;
+ uint64_t _last_write_success_time;
+ uint64_t _last_write_try_time;
- bool _flow_control_enabled;
+ unsigned _bytes_tx;
+ unsigned _bytes_txerr;
+ unsigned _bytes_rx;
+ uint64_t _bytes_timestamp;
+ float _rate_tx;
+ float _rate_txerr;
+ float _rate_rx;
+
+ struct telemetry_status_s _rstatus; ///< receive status
struct mavlink_message_buffer {
int write_ptr;
@@ -277,79 +343,45 @@ private:
int size;
char *data;
};
+
mavlink_message_buffer _message_buffer;
pthread_mutex_t _message_buffer_mutex;
+ pthread_mutex_t _send_mutex;
- perf_counter_t _loop_perf; /**< loop performance counter */
bool _param_initialized;
param_t _param_system_id;
param_t _param_component_id;
param_t _param_system_type;
param_t _param_use_hil_gps;
+ param_t _param_forward_externalsp;
- /**
- * Send one parameter.
- *
- * @param param The parameter id to send.
- * @return zero on success, nonzero on failure.
- */
- int mavlink_pm_send_param(param_t param);
+ unsigned _system_type;
- /**
- * Send one parameter identified by index.
- *
- * @param index The index of the parameter to send.
- * @return zero on success, nonzero else.
- */
- int mavlink_pm_send_param_for_index(uint16_t index);
+ perf_counter_t _loop_perf; /**< loop performance counter */
+ perf_counter_t _txerr_perf; /**< TX error counter */
- /**
- * Send one parameter identified by name.
- *
- * @param name The index of the parameter to send.
- * @return zero on success, nonzero else.
- */
- int mavlink_pm_send_param_for_name(const char *name);
+ void mavlink_update_system();
- /**
- * Send a queue of parameters, one parameter per function call.
- *
- * @return zero on success, nonzero on failure
- */
- int mavlink_pm_queued_send(void);
+ int mavlink_open_uart(int baudrate, const char *uart_name, struct termios *uart_config_original, bool *is_usb);
/**
- * Start sending the parameter queue.
+ * Get the free space in the transmit buffer
*
- * This function will not directly send parameters, but instead
- * activate the sending of one parameter on each call of
- * mavlink_pm_queued_send().
- * @see mavlink_pm_queued_send()
+ * @return free space in the UART TX buffer
*/
- void mavlink_pm_start_queued_send();
-
- void mavlink_update_system();
+ unsigned get_free_tx_buf();
- void mavlink_waypoint_eventloop(uint64_t now);
- void mavlink_wpm_send_waypoint_reached(uint16_t seq);
- void mavlink_wpm_send_waypoint_request(uint8_t sysid, uint8_t compid, uint16_t seq);
- void mavlink_wpm_send_waypoint(uint8_t sysid, uint8_t compid, uint16_t seq);
- void mavlink_wpm_send_waypoint_count(uint8_t sysid, uint8_t compid, uint16_t count);
- void mavlink_wpm_send_waypoint_current(uint16_t seq);
- void mavlink_wpm_send_waypoint_ack(uint8_t sysid, uint8_t compid, uint8_t type);
- void mavlink_wpm_init(mavlink_wpm_storage *state);
- int map_mission_item_to_mavlink_mission_item(const struct mission_item_s *mission_item, mavlink_mission_item_t *mavlink_mission_item);
- int map_mavlink_mission_item_to_mission_item(const mavlink_mission_item_t *mavlink_mission_item, struct mission_item_s *mission_item);
- void publish_mission();
-
- void mavlink_missionlib_send_message(mavlink_message_t *msg);
- int mavlink_missionlib_send_gcs_string(const char *string);
-
- int mavlink_open_uart(int baudrate, const char *uart_name, struct termios *uart_config_original, bool *is_usb);
+ static unsigned int interval_from_rate(float rate);
int configure_stream(const char *stream_name, const float rate);
- void configure_stream_threadsafe(const char *stream_name, const float rate);
+
+ /**
+ * Adjust the stream rates based on the current rate
+ *
+ * @param multiplier if greater than 1, the transmission rate will increase, if smaller than one decrease
+ */
+ void adjust_stream_rates(const float multiplier);
int message_buffer_init(int size);
@@ -359,13 +391,16 @@ private:
int message_buffer_is_empty();
- bool message_buffer_write(void *ptr, int size);
-
int message_buffer_get_ptr(void **ptr, bool *is_part);
void message_buffer_mark_read(int n);
- void pass_message(mavlink_message_t *msg);
+ void pass_message(const mavlink_message_t *msg);
+
+ /**
+ * Update rate mult so total bitrate will be equal to _datarate.
+ */
+ void update_rate_mult();
static int mavlink_dev_ioctl(struct file *filep, int cmd, unsigned long arg);
@@ -374,4 +409,7 @@ private:
*/
int task_main(int argc, char *argv[]);
+ /* do not allow copying this class */
+ Mavlink(const Mavlink&);
+ Mavlink operator=(const Mavlink&);
};
diff --git a/src/modules/mavlink/mavlink_messages.cpp b/src/modules/mavlink/mavlink_messages.cpp
index 4433377c6..978aee118 100644
--- a/src/modules/mavlink/mavlink_messages.cpp
+++ b/src/modules/mavlink/mavlink_messages.cpp
@@ -40,12 +40,11 @@
*/
#include <stdio.h>
+
#include <commander/px4_custom_mode.h>
#include <lib/geo/geo.h>
-
#include <uORB/uORB.h>
#include <uORB/topics/sensor_combined.h>
-#include <uORB/topics/rc_channels.h>
#include <uORB/topics/vehicle_attitude.h>
#include <uORB/topics/vehicle_gps_position.h>
#include <uORB/topics/vehicle_global_position.h>
@@ -73,11 +72,11 @@
#include <drivers/drv_rc_input.h>
#include <drivers/drv_pwm_output.h>
#include <drivers/drv_range_finder.h>
-
#include <systemlib/err.h>
+#include <mavlink/mavlink_log.h>
#include "mavlink_messages.h"
-
+#include "mavlink_main.h"
static uint16_t cm_uint16_from_m_float(float m);
static void get_mavlink_mode_state(struct vehicle_status_s *status, struct position_setpoint_triplet_s *pos_sp_triplet,
@@ -120,50 +119,94 @@ void get_mavlink_mode_state(struct vehicle_status_s *status, struct position_set
union px4_custom_mode custom_mode;
custom_mode.data = 0;
- if (pos_sp_triplet->nav_state == NAV_STATE_NONE) {
- /* use main state when navigator is not active */
- if (status->main_state == MAIN_STATE_MANUAL) {
- *mavlink_base_mode |= MAV_MODE_FLAG_MANUAL_INPUT_ENABLED | (status->is_rotary_wing ? MAV_MODE_FLAG_STABILIZE_ENABLED : 0);
+ switch (status->nav_state) {
+
+ case NAVIGATION_STATE_MANUAL:
+ *mavlink_base_mode |= MAV_MODE_FLAG_MANUAL_INPUT_ENABLED
+ | (status->is_rotary_wing ? MAV_MODE_FLAG_STABILIZE_ENABLED : 0);
custom_mode.main_mode = PX4_CUSTOM_MAIN_MODE_MANUAL;
+ break;
+
+ case NAVIGATION_STATE_ACRO:
+ *mavlink_base_mode |= MAV_MODE_FLAG_MANUAL_INPUT_ENABLED;
+ custom_mode.main_mode = PX4_CUSTOM_MAIN_MODE_ACRO;
+ break;
- } else if (status->main_state == MAIN_STATE_ALTCTL) {
- *mavlink_base_mode |= MAV_MODE_FLAG_MANUAL_INPUT_ENABLED | MAV_MODE_FLAG_STABILIZE_ENABLED;
+ case NAVIGATION_STATE_ALTCTL:
+ *mavlink_base_mode |= MAV_MODE_FLAG_MANUAL_INPUT_ENABLED
+ | MAV_MODE_FLAG_STABILIZE_ENABLED;
custom_mode.main_mode = PX4_CUSTOM_MAIN_MODE_ALTCTL;
+ break;
- } else if (status->main_state == MAIN_STATE_POSCTL) {
- *mavlink_base_mode |= MAV_MODE_FLAG_MANUAL_INPUT_ENABLED | MAV_MODE_FLAG_STABILIZE_ENABLED | MAV_MODE_FLAG_GUIDED_ENABLED;
+ case NAVIGATION_STATE_POSCTL:
+ *mavlink_base_mode |= MAV_MODE_FLAG_MANUAL_INPUT_ENABLED
+ | MAV_MODE_FLAG_STABILIZE_ENABLED
+ | MAV_MODE_FLAG_GUIDED_ENABLED;
custom_mode.main_mode = PX4_CUSTOM_MAIN_MODE_POSCTL;
+ break;
- } else if (status->main_state == MAIN_STATE_AUTO) {
- *mavlink_base_mode |= MAV_MODE_FLAG_AUTO_ENABLED | MAV_MODE_FLAG_STABILIZE_ENABLED | MAV_MODE_FLAG_GUIDED_ENABLED;
+ case NAVIGATION_STATE_AUTO_MISSION:
+ *mavlink_base_mode |= MAV_MODE_FLAG_AUTO_ENABLED
+ | MAV_MODE_FLAG_STABILIZE_ENABLED
+ | MAV_MODE_FLAG_GUIDED_ENABLED;
custom_mode.main_mode = PX4_CUSTOM_MAIN_MODE_AUTO;
- custom_mode.sub_mode = PX4_CUSTOM_SUB_MODE_AUTO_READY;
-
- } else if (status->main_state == MAIN_STATE_ACRO) {
- *mavlink_base_mode |= MAV_MODE_FLAG_MANUAL_INPUT_ENABLED;
- custom_mode.main_mode = PX4_CUSTOM_MAIN_MODE_ACRO;
- }
+ custom_mode.sub_mode = PX4_CUSTOM_SUB_MODE_AUTO_MISSION;
+ break;
- } else {
- /* use navigation state when navigator is active */
- *mavlink_base_mode |= MAV_MODE_FLAG_AUTO_ENABLED | MAV_MODE_FLAG_STABILIZE_ENABLED | MAV_MODE_FLAG_GUIDED_ENABLED;
- custom_mode.main_mode = PX4_CUSTOM_MAIN_MODE_AUTO;
+ case NAVIGATION_STATE_AUTO_LOITER:
+ *mavlink_base_mode |= MAV_MODE_FLAG_AUTO_ENABLED
+ | MAV_MODE_FLAG_STABILIZE_ENABLED
+ | MAV_MODE_FLAG_GUIDED_ENABLED;
+ custom_mode.main_mode = PX4_CUSTOM_MAIN_MODE_AUTO;
+ custom_mode.sub_mode = PX4_CUSTOM_SUB_MODE_AUTO_LOITER;
+ break;
+
+ case NAVIGATION_STATE_AUTO_RTL:
+ /* fallthrough */
+ case NAVIGATION_STATE_AUTO_RCRECOVER:
+ *mavlink_base_mode |= MAV_MODE_FLAG_AUTO_ENABLED
+ | MAV_MODE_FLAG_STABILIZE_ENABLED
+ | MAV_MODE_FLAG_GUIDED_ENABLED;
+ custom_mode.main_mode = PX4_CUSTOM_MAIN_MODE_AUTO;
+ custom_mode.sub_mode = PX4_CUSTOM_SUB_MODE_AUTO_RTL;
+ break;
+
+ case NAVIGATION_STATE_LAND:
+ case NAVIGATION_STATE_AUTO_LANDENGFAIL:
+ case NAVIGATION_STATE_AUTO_LANDGPSFAIL:
+ /* fallthrough */
+ case NAVIGATION_STATE_DESCEND:
+ *mavlink_base_mode |= MAV_MODE_FLAG_AUTO_ENABLED
+ | MAV_MODE_FLAG_STABILIZE_ENABLED
+ | MAV_MODE_FLAG_GUIDED_ENABLED;
+ custom_mode.main_mode = PX4_CUSTOM_MAIN_MODE_AUTO;
+ custom_mode.sub_mode = PX4_CUSTOM_SUB_MODE_AUTO_LAND;
+ break;
- if (pos_sp_triplet->nav_state == NAV_STATE_READY) {
- custom_mode.sub_mode = PX4_CUSTOM_SUB_MODE_AUTO_READY;
+ case NAVIGATION_STATE_AUTO_RTGS:
+ *mavlink_base_mode |= MAV_MODE_FLAG_AUTO_ENABLED
+ | MAV_MODE_FLAG_STABILIZE_ENABLED
+ | MAV_MODE_FLAG_GUIDED_ENABLED;
+ custom_mode.main_mode = PX4_CUSTOM_MAIN_MODE_AUTO;
+ custom_mode.sub_mode = PX4_CUSTOM_SUB_MODE_AUTO_RTGS;
+ break;
- } else if (pos_sp_triplet->nav_state == NAV_STATE_LOITER) {
- custom_mode.sub_mode = PX4_CUSTOM_SUB_MODE_AUTO_LOITER;
+ case NAVIGATION_STATE_TERMINATION:
+ *mavlink_base_mode |= MAV_MODE_FLAG_MANUAL_INPUT_ENABLED;
+ custom_mode.main_mode = PX4_CUSTOM_MAIN_MODE_MANUAL;
+ break;
- } else if (pos_sp_triplet->nav_state == NAV_STATE_MISSION) {
- custom_mode.sub_mode = PX4_CUSTOM_SUB_MODE_AUTO_MISSION;
+ case NAVIGATION_STATE_OFFBOARD:
+ *mavlink_base_mode |= MAV_MODE_FLAG_AUTO_ENABLED
+ | MAV_MODE_FLAG_STABILIZE_ENABLED
+ | MAV_MODE_FLAG_GUIDED_ENABLED;
+ custom_mode.main_mode = PX4_CUSTOM_MAIN_MODE_OFFBOARD;
+ break;
- } else if (pos_sp_triplet->nav_state == NAV_STATE_RTL) {
- custom_mode.sub_mode = PX4_CUSTOM_SUB_MODE_AUTO_RTL;
+ case NAVIGATION_STATE_MAX:
+ /* this is an unused case, ignore */
+ break;
- } else if (pos_sp_triplet->nav_state == NAV_STATE_LAND) {
- custom_mode.sub_mode = PX4_CUSTOM_SUB_MODE_AUTO_LAND;
- }
}
*mavlink_custom_mode = custom_mode.data;
@@ -205,43 +248,221 @@ public:
return "HEARTBEAT";
}
- static MavlinkStream *new_instance()
+ uint8_t get_id()
{
- return new MavlinkStreamHeartbeat();
+ return MAVLINK_MSG_ID_HEARTBEAT;
}
-private:
- MavlinkOrbSubscription *status_sub;
- MavlinkOrbSubscription *pos_sp_triplet_sub;
+ static MavlinkStream *new_instance(Mavlink *mavlink)
+ {
+ return new MavlinkStreamHeartbeat(mavlink);
+ }
-protected:
- void subscribe(Mavlink *mavlink)
+ unsigned get_size()
{
- status_sub = mavlink->add_orb_subscription(ORB_ID(vehicle_status));
- pos_sp_triplet_sub = mavlink->add_orb_subscription(ORB_ID(position_setpoint_triplet));
+ return MAVLINK_MSG_ID_HEARTBEAT_LEN + MAVLINK_NUM_NON_PAYLOAD_BYTES;
+ }
+
+ bool const_rate() {
+ return true;
}
+private:
+ MavlinkOrbSubscription *_status_sub;
+ MavlinkOrbSubscription *_pos_sp_triplet_sub;
+
+ /* do not allow top copying this class */
+ MavlinkStreamHeartbeat(MavlinkStreamHeartbeat &);
+ MavlinkStreamHeartbeat& operator = (const MavlinkStreamHeartbeat &);
+
+protected:
+ explicit MavlinkStreamHeartbeat(Mavlink *mavlink) : MavlinkStream(mavlink),
+ _status_sub(_mavlink->add_orb_subscription(ORB_ID(vehicle_status))),
+ _pos_sp_triplet_sub(_mavlink->add_orb_subscription(ORB_ID(position_setpoint_triplet)))
+ {}
+
void send(const hrt_abstime t)
{
struct vehicle_status_s status;
struct position_setpoint_triplet_s pos_sp_triplet;
- if (status_sub->update(&status) && pos_sp_triplet_sub->update(&pos_sp_triplet)) {
- uint8_t mavlink_state = 0;
- uint8_t mavlink_base_mode = 0;
- uint32_t mavlink_custom_mode = 0;
- get_mavlink_mode_state(&status, &pos_sp_triplet, &mavlink_state, &mavlink_base_mode, &mavlink_custom_mode);
+ /* always send the heartbeat, independent of the update status of the topics */
+ if (!_status_sub->update(&status)) {
+ /* if topic update failed fill it with defaults */
+ memset(&status, 0, sizeof(status));
+ }
- mavlink_msg_heartbeat_send(_channel,
- mavlink_system.type,
- MAV_AUTOPILOT_PX4,
- mavlink_base_mode,
- mavlink_custom_mode,
- mavlink_state);
+ if (!_pos_sp_triplet_sub->update(&pos_sp_triplet)) {
+ /* if topic update failed fill it with defaults */
+ memset(&pos_sp_triplet, 0, sizeof(pos_sp_triplet));
}
+
+ mavlink_heartbeat_t msg;
+
+ msg.base_mode = 0;
+ msg.custom_mode = 0;
+ get_mavlink_mode_state(&status, &pos_sp_triplet, &msg.system_status, &msg.base_mode, &msg.custom_mode);
+ msg.type = _mavlink->get_system_type();
+ msg.autopilot = MAV_AUTOPILOT_PX4;
+ msg.mavlink_version = 3;
+
+ _mavlink->send_message(MAVLINK_MSG_ID_HEARTBEAT, &msg);
}
};
+class MavlinkStreamStatustext : public MavlinkStream
+{
+public:
+ const char *get_name() const
+ {
+ return MavlinkStreamStatustext::get_name_static();
+ }
+
+ static const char *get_name_static()
+ {
+ return "STATUSTEXT";
+ }
+
+ uint8_t get_id()
+ {
+ return MAVLINK_MSG_ID_STATUSTEXT;
+ }
+
+ static MavlinkStream *new_instance(Mavlink *mavlink)
+ {
+ return new MavlinkStreamStatustext(mavlink);
+ }
+
+ unsigned get_size() {
+ return mavlink_logbuffer_is_empty(_mavlink->get_logbuffer()) ? 0 : (MAVLINK_MSG_ID_STATUSTEXT_LEN + MAVLINK_NUM_NON_PAYLOAD_BYTES);
+ }
+
+private:
+ /* do not allow top copying this class */
+ MavlinkStreamStatustext(MavlinkStreamStatustext &);
+ MavlinkStreamStatustext& operator = (const MavlinkStreamStatustext &);
+ FILE *fp = nullptr;
+
+protected:
+ explicit MavlinkStreamStatustext(Mavlink *mavlink) : MavlinkStream(mavlink)
+ {}
+
+ ~MavlinkStreamStatustext() {
+ if (fp) {
+ fclose(fp);
+ }
+ }
+
+ void send(const hrt_abstime t)
+ {
+ if (!mavlink_logbuffer_is_empty(_mavlink->get_logbuffer())) {
+ struct mavlink_logmessage logmsg;
+ int lb_ret = mavlink_logbuffer_read(_mavlink->get_logbuffer(), &logmsg);
+
+ if (lb_ret == OK) {
+ mavlink_statustext_t msg;
+
+ msg.severity = logmsg.severity;
+ strncpy(msg.text, logmsg.text, sizeof(msg.text));
+
+ _mavlink->send_message(MAVLINK_MSG_ID_STATUSTEXT, &msg);
+
+ /* write log messages in first instance to disk */
+ if (_mavlink->get_instance_id() == 0) {
+ if (fp) {
+ fputs(msg.text, fp);
+ fputs("\n", fp);
+ fsync(fileno(fp));
+ } else {
+ /* string to hold the path to the log */
+ char log_file_name[32] = "";
+ char log_file_path[64] = "";
+
+ timespec ts;
+ clock_gettime(CLOCK_REALTIME, &ts);
+ /* use GPS time for log file naming, e.g. /fs/microsd/2014-01-19/19_37_52.bin */
+ time_t gps_time_sec = ts.tv_sec + (ts.tv_nsec / 1e9);
+ struct tm tt;
+ gmtime_r(&gps_time_sec, &tt);
+
+ // XXX we do not want to interfere here with the SD log app
+ strftime(log_file_name, sizeof(log_file_name), "msgs_%Y_%m_%d_%H_%M_%S.txt", &tt);
+ snprintf(log_file_path, sizeof(log_file_path), "/fs/microsd/%s", log_file_name);
+ fp = fopen(log_file_path, "ab");
+ }
+ }
+ }
+ }
+ }
+};
+
+class MavlinkStreamCommandLong : public MavlinkStream
+{
+public:
+ const char *get_name() const
+ {
+ return MavlinkStreamCommandLong::get_name_static();
+ }
+
+ static const char *get_name_static()
+ {
+ return "COMMAND_LONG";
+ }
+
+ uint8_t get_id()
+ {
+ return MAVLINK_MSG_ID_COMMAND_LONG;
+ }
+
+ static MavlinkStream *new_instance(Mavlink *mavlink)
+ {
+ return new MavlinkStreamCommandLong(mavlink);
+ }
+
+ unsigned get_size() {
+ return 0; // commands stream is not regular and not predictable
+ }
+
+private:
+ MavlinkOrbSubscription *_cmd_sub;
+ uint64_t _cmd_time;
+
+ /* do not allow top copying this class */
+ MavlinkStreamCommandLong(MavlinkStreamCommandLong &);
+ MavlinkStreamCommandLong& operator = (const MavlinkStreamCommandLong &);
+
+protected:
+ explicit MavlinkStreamCommandLong(Mavlink *mavlink) : MavlinkStream(mavlink),
+ _cmd_sub(_mavlink->add_orb_subscription(ORB_ID(vehicle_command))),
+ _cmd_time(0)
+ {}
+
+ void send(const hrt_abstime t)
+ {
+ struct vehicle_command_s cmd;
+
+ if (_cmd_sub->update(&_cmd_time, &cmd)) {
+ /* only send commands for other systems/components */
+ if (cmd.target_system != mavlink_system.sysid || cmd.target_component != mavlink_system.compid) {
+ mavlink_command_long_t msg;
+
+ msg.target_system = cmd.target_system;
+ msg.target_component = cmd.target_component;
+ msg.command = cmd.command;
+ msg.confirmation = cmd.confirmation;
+ msg.param1 = cmd.param1;
+ msg.param2 = cmd.param2;
+ msg.param3 = cmd.param3;
+ msg.param4 = cmd.param4;
+ msg.param5 = cmd.param5;
+ msg.param6 = cmd.param6;
+ msg.param7 = cmd.param7;
+
+ _mavlink->send_message(MAVLINK_MSG_ID_COMMAND_LONG, &msg);
+ }
+ }
+ }
+};
class MavlinkStreamSysStatus : public MavlinkStream
{
@@ -251,44 +472,60 @@ public:
return MavlinkStreamSysStatus::get_name_static();
}
- static const char *get_name_static ()
+ static const char *get_name_static()
{
return "SYS_STATUS";
}
- static MavlinkStream *new_instance()
+ uint8_t get_id()
{
- return new MavlinkStreamSysStatus();
+ return MAVLINK_MSG_ID_SYS_STATUS;
}
-private:
- MavlinkOrbSubscription *status_sub;
+ static MavlinkStream *new_instance(Mavlink *mavlink)
+ {
+ return new MavlinkStreamSysStatus(mavlink);
+ }
-protected:
- void subscribe(Mavlink *mavlink)
+ unsigned get_size()
{
- status_sub = mavlink->add_orb_subscription(ORB_ID(vehicle_status));
+ return MAVLINK_MSG_ID_SYS_STATUS_LEN + MAVLINK_NUM_NON_PAYLOAD_BYTES;
}
+private:
+ MavlinkOrbSubscription *_status_sub;
+
+ /* do not allow top copying this class */
+ MavlinkStreamSysStatus(MavlinkStreamSysStatus &);
+ MavlinkStreamSysStatus& operator = (const MavlinkStreamSysStatus &);
+
+protected:
+ explicit MavlinkStreamSysStatus(Mavlink *mavlink) : MavlinkStream(mavlink),
+ _status_sub(_mavlink->add_orb_subscription(ORB_ID(vehicle_status)))
+ {}
+
void send(const hrt_abstime t)
{
struct vehicle_status_s status;
- if (status_sub->update(&status)) {
- mavlink_msg_sys_status_send(_channel,
- status.onboard_control_sensors_present,
- status.onboard_control_sensors_enabled,
- status.onboard_control_sensors_health,
- status.load * 1000.0f,
- status.battery_voltage * 1000.0f,
- status.battery_current * 100.0f,
- status.battery_remaining * 100.0f,
- status.drop_rate_comm,
- status.errors_comm,
- status.errors_count1,
- status.errors_count2,
- status.errors_count3,
- status.errors_count4);
+ if (_status_sub->update(&status)) {
+ mavlink_sys_status_t msg;
+
+ msg.onboard_control_sensors_present = status.onboard_control_sensors_present;
+ msg.onboard_control_sensors_enabled = status.onboard_control_sensors_enabled;
+ msg.onboard_control_sensors_health = status.onboard_control_sensors_health;
+ msg.load = status.load * 1000.0f;
+ msg.voltage_battery = status.battery_voltage * 1000.0f;
+ msg.current_battery = status.battery_current * 100.0f;
+ msg.drop_rate_comm = status.drop_rate_comm;
+ msg.errors_comm = status.errors_comm;
+ msg.errors_count1 = status.errors_count1;
+ msg.errors_count2 = status.errors_count2;
+ msg.errors_count3 = status.errors_count3;
+ msg.errors_count4 = status.errors_count4;
+ msg.battery_remaining = status.battery_remaining * 100.0f;
+
+ _mavlink->send_message(MAVLINK_MSG_ID_SYS_STATUS, &msg);
}
}
};
@@ -307,73 +544,94 @@ public:
return "HIGHRES_IMU";
}
- static MavlinkStream *new_instance()
+ uint8_t get_id()
{
- return new MavlinkStreamHighresIMU();
+ return MAVLINK_MSG_ID_HIGHRES_IMU;
+ }
+
+ static MavlinkStream *new_instance(Mavlink *mavlink)
+ {
+ return new MavlinkStreamHighresIMU(mavlink);
+ }
+
+ unsigned get_size()
+ {
+ return MAVLINK_MSG_ID_HIGHRES_IMU_LEN + MAVLINK_NUM_NON_PAYLOAD_BYTES;
}
private:
- MavlinkOrbSubscription *sensor_sub;
- uint64_t sensor_time;
+ MavlinkOrbSubscription *_sensor_sub;
+ uint64_t _sensor_time;
+
+ uint64_t _accel_timestamp;
+ uint64_t _gyro_timestamp;
+ uint64_t _mag_timestamp;
+ uint64_t _baro_timestamp;
- uint64_t accel_timestamp;
- uint64_t gyro_timestamp;
- uint64_t mag_timestamp;
- uint64_t baro_timestamp;
+ /* do not allow top copying this class */
+ MavlinkStreamHighresIMU(MavlinkStreamHighresIMU &);
+ MavlinkStreamHighresIMU& operator = (const MavlinkStreamHighresIMU &);
protected:
- explicit MavlinkStreamHighresIMU() : MavlinkStream(),
- sensor_time(0),
- accel_timestamp(0),
- gyro_timestamp(0),
- mag_timestamp(0),
- baro_timestamp(0)
+ explicit MavlinkStreamHighresIMU(Mavlink *mavlink) : MavlinkStream(mavlink),
+ _sensor_sub(_mavlink->add_orb_subscription(ORB_ID(sensor_combined))),
+ _sensor_time(0),
+ _accel_timestamp(0),
+ _gyro_timestamp(0),
+ _mag_timestamp(0),
+ _baro_timestamp(0)
{}
- void subscribe(Mavlink *mavlink)
- {
- sensor_sub = mavlink->add_orb_subscription(ORB_ID(sensor_combined));
- }
-
void send(const hrt_abstime t)
{
struct sensor_combined_s sensor;
- if (sensor_sub->update(&sensor_time, &sensor)) {
+ if (_sensor_sub->update(&_sensor_time, &sensor)) {
uint16_t fields_updated = 0;
- if (accel_timestamp != sensor.accelerometer_timestamp) {
+ if (_accel_timestamp != sensor.accelerometer_timestamp) {
/* mark first three dimensions as changed */
fields_updated |= (1 << 0) | (1 << 1) | (1 << 2);
- accel_timestamp = sensor.accelerometer_timestamp;
+ _accel_timestamp = sensor.accelerometer_timestamp;
}
- if (gyro_timestamp != sensor.timestamp) {
+ if (_gyro_timestamp != sensor.timestamp) {
/* mark second group dimensions as changed */
fields_updated |= (1 << 3) | (1 << 4) | (1 << 5);
- gyro_timestamp = sensor.timestamp;
+ _gyro_timestamp = sensor.timestamp;
}
- if (mag_timestamp != sensor.magnetometer_timestamp) {
+ if (_mag_timestamp != sensor.magnetometer_timestamp) {
/* mark third group dimensions as changed */
fields_updated |= (1 << 6) | (1 << 7) | (1 << 8);
- mag_timestamp = sensor.magnetometer_timestamp;
+ _mag_timestamp = sensor.magnetometer_timestamp;
}
- if (baro_timestamp != sensor.baro_timestamp) {
+ if (_baro_timestamp != sensor.baro_timestamp) {
/* mark last group dimensions as changed */
fields_updated |= (1 << 9) | (1 << 11) | (1 << 12);
- baro_timestamp = sensor.baro_timestamp;
+ _baro_timestamp = sensor.baro_timestamp;
}
- mavlink_msg_highres_imu_send(_channel,
- sensor.timestamp,
- sensor.accelerometer_m_s2[0], sensor.accelerometer_m_s2[1], sensor.accelerometer_m_s2[2],
- sensor.gyro_rad_s[0], sensor.gyro_rad_s[1], sensor.gyro_rad_s[2],
- sensor.magnetometer_ga[0], sensor.magnetometer_ga[1], sensor.magnetometer_ga[2],
- sensor.baro_pres_mbar, sensor.differential_pressure_pa,
- sensor.baro_alt_meter, sensor.baro_temp_celcius,
- fields_updated);
+ mavlink_highres_imu_t msg;
+
+ msg.time_usec = sensor.timestamp;
+ msg.xacc = sensor.accelerometer_m_s2[0];
+ msg.yacc = sensor.accelerometer_m_s2[1];
+ msg.zacc = sensor.accelerometer_m_s2[2];
+ msg.xgyro = sensor.gyro_rad_s[0];
+ msg.ygyro = sensor.gyro_rad_s[1];
+ msg.zgyro = sensor.gyro_rad_s[2];
+ msg.xmag = sensor.magnetometer_ga[0];
+ msg.ymag = sensor.magnetometer_ga[1];
+ msg.zmag = sensor.magnetometer_ga[2];
+ msg.abs_pressure = sensor.baro_pres_mbar;
+ msg.diff_pressure = sensor.differential_pressure_pa;
+ msg.pressure_alt = sensor.baro_alt_meter;
+ msg.temperature = sensor.baro_temp_celcius;
+ msg.fields_updated = fields_updated;
+
+ _mavlink->send_message(MAVLINK_MSG_ID_HIGHRES_IMU, &msg);
}
}
};
@@ -392,34 +650,52 @@ public:
return "ATTITUDE";
}
- static MavlinkStream *new_instance()
+ uint8_t get_id()
+ {
+ return MAVLINK_MSG_ID_ATTITUDE;
+ }
+
+ static MavlinkStream *new_instance(Mavlink *mavlink)
+ {
+ return new MavlinkStreamAttitude(mavlink);
+ }
+
+ unsigned get_size()
{
- return new MavlinkStreamAttitude();
+ return MAVLINK_MSG_ID_ATTITUDE_LEN + MAVLINK_NUM_NON_PAYLOAD_BYTES;
}
private:
- MavlinkOrbSubscription *att_sub;
- uint64_t att_time;
+ MavlinkOrbSubscription *_att_sub;
+ uint64_t _att_time;
+
+ /* do not allow top copying this class */
+ MavlinkStreamAttitude(MavlinkStreamAttitude &);
+ MavlinkStreamAttitude& operator = (const MavlinkStreamAttitude &);
+
protected:
- explicit MavlinkStreamAttitude() : MavlinkStream(),
- att_time(0)
+ explicit MavlinkStreamAttitude(Mavlink *mavlink) : MavlinkStream(mavlink),
+ _att_sub(_mavlink->add_orb_subscription(ORB_ID(vehicle_attitude))),
+ _att_time(0)
{}
- void subscribe(Mavlink *mavlink)
- {
- att_sub = mavlink->add_orb_subscription(ORB_ID(vehicle_attitude));
- }
-
void send(const hrt_abstime t)
{
struct vehicle_attitude_s att;
- if (att_sub->update(&att_time, &att)) {
- mavlink_msg_attitude_send(_channel,
- att.timestamp / 1000,
- att.roll, att.pitch, att.yaw,
- att.rollspeed, att.pitchspeed, att.yawspeed);
+ if (_att_sub->update(&_att_time, &att)) {
+ mavlink_attitude_t msg;
+
+ msg.time_boot_ms = att.timestamp / 1000;
+ msg.roll = att.roll;
+ msg.pitch = att.pitch;
+ msg.yaw = att.yaw;
+ msg.rollspeed = att.rollspeed;
+ msg.pitchspeed = att.pitchspeed;
+ msg.yawspeed = att.yawspeed;
+
+ _mavlink->send_message(MAVLINK_MSG_ID_ATTITUDE, &msg);
}
}
};
@@ -438,39 +714,52 @@ public:
return "ATTITUDE_QUATERNION";
}
- static MavlinkStream *new_instance()
+ uint8_t get_id()
+ {
+ return MAVLINK_MSG_ID_ATTITUDE_QUATERNION;
+ }
+
+ static MavlinkStream *new_instance(Mavlink *mavlink)
+ {
+ return new MavlinkStreamAttitudeQuaternion(mavlink);
+ }
+
+ unsigned get_size()
{
- return new MavlinkStreamAttitudeQuaternion();
+ return MAVLINK_MSG_ID_ATTITUDE_QUATERNION_LEN + MAVLINK_NUM_NON_PAYLOAD_BYTES;
}
private:
- MavlinkOrbSubscription *att_sub;
- uint64_t att_time;
+ MavlinkOrbSubscription *_att_sub;
+ uint64_t _att_time;
+
+ /* do not allow top copying this class */
+ MavlinkStreamAttitudeQuaternion(MavlinkStreamAttitudeQuaternion &);
+ MavlinkStreamAttitudeQuaternion& operator = (const MavlinkStreamAttitudeQuaternion &);
protected:
- explicit MavlinkStreamAttitudeQuaternion() : MavlinkStream(),
- att_time(0)
+ explicit MavlinkStreamAttitudeQuaternion(Mavlink *mavlink) : MavlinkStream(mavlink),
+ _att_sub(_mavlink->add_orb_subscription(ORB_ID(vehicle_attitude))),
+ _att_time(0)
{}
- void subscribe(Mavlink *mavlink)
- {
- att_sub = mavlink->add_orb_subscription(ORB_ID(vehicle_attitude));
- }
-
void send(const hrt_abstime t)
{
struct vehicle_attitude_s att;
- if (att_sub->update(&att_time, &att)) {
- mavlink_msg_attitude_quaternion_send(_channel,
- att.timestamp / 1000,
- att.q[0],
- att.q[1],
- att.q[2],
- att.q[3],
- att.rollspeed,
- att.pitchspeed,
- att.yawspeed);
+ if (_att_sub->update(&_att_time, &att)) {
+ mavlink_attitude_quaternion_t msg;
+
+ msg.time_boot_ms = att.timestamp / 1000;
+ msg.q1 = att.q[0];
+ msg.q2 = att.q[1];
+ msg.q3 = att.q[2];
+ msg.q4 = att.q[3];
+ msg.rollspeed = att.rollspeed;
+ msg.pitchspeed = att.pitchspeed;
+ msg.yawspeed = att.yawspeed;
+
+ _mavlink->send_message(MAVLINK_MSG_ID_ATTITUDE_QUATERNION, &msg);
}
}
};
@@ -490,45 +779,60 @@ public:
return "VFR_HUD";
}
- static MavlinkStream *new_instance()
+ uint8_t get_id()
{
- return new MavlinkStreamVFRHUD();
+ return MAVLINK_MSG_ID_VFR_HUD;
+ }
+
+ static MavlinkStream *new_instance(Mavlink *mavlink)
+ {
+ return new MavlinkStreamVFRHUD(mavlink);
+ }
+
+ unsigned get_size()
+ {
+ return MAVLINK_MSG_ID_VFR_HUD_LEN + MAVLINK_NUM_NON_PAYLOAD_BYTES;
}
private:
- MavlinkOrbSubscription *att_sub;
- uint64_t att_time;
+ MavlinkOrbSubscription *_att_sub;
+ uint64_t _att_time;
+
+ MavlinkOrbSubscription *_pos_sub;
+ uint64_t _pos_time;
- MavlinkOrbSubscription *pos_sub;
- uint64_t pos_time;
+ MavlinkOrbSubscription *_armed_sub;
+ uint64_t _armed_time;
- MavlinkOrbSubscription *armed_sub;
- uint64_t armed_time;
+ MavlinkOrbSubscription *_act_sub;
+ uint64_t _act_time;
- MavlinkOrbSubscription *act_sub;
- uint64_t act_time;
+ MavlinkOrbSubscription *_airspeed_sub;
+ uint64_t _airspeed_time;
- MavlinkOrbSubscription *airspeed_sub;
- uint64_t airspeed_time;
+ MavlinkOrbSubscription *_sensor_combined_sub;
+ uint64_t _sensor_combined_time;
+
+ /* do not allow top copying this class */
+ MavlinkStreamVFRHUD(MavlinkStreamVFRHUD &);
+ MavlinkStreamVFRHUD& operator = (const MavlinkStreamVFRHUD &);
protected:
- explicit MavlinkStreamVFRHUD() : MavlinkStream(),
- att_time(0),
- pos_time(0),
- armed_time(0),
- act_time(0),
- airspeed_time(0)
+ explicit MavlinkStreamVFRHUD(Mavlink *mavlink) : MavlinkStream(mavlink),
+ _att_sub(_mavlink->add_orb_subscription(ORB_ID(vehicle_attitude))),
+ _att_time(0),
+ _pos_sub(_mavlink->add_orb_subscription(ORB_ID(vehicle_global_position))),
+ _pos_time(0),
+ _armed_sub(_mavlink->add_orb_subscription(ORB_ID(actuator_armed))),
+ _armed_time(0),
+ _act_sub(_mavlink->add_orb_subscription(ORB_ID(actuator_controls_0))),
+ _act_time(0),
+ _airspeed_sub(_mavlink->add_orb_subscription(ORB_ID(airspeed))),
+ _airspeed_time(0),
+ _sensor_combined_sub(_mavlink->add_orb_subscription(ORB_ID(sensor_combined))),
+ _sensor_combined_time(0)
{}
- void subscribe(Mavlink *mavlink)
- {
- att_sub = mavlink->add_orb_subscription(ORB_ID(vehicle_attitude));
- pos_sub = mavlink->add_orb_subscription(ORB_ID(vehicle_global_position));
- armed_sub = mavlink->add_orb_subscription(ORB_ID(actuator_armed));
- act_sub = mavlink->add_orb_subscription(ORB_ID(actuator_controls_0));
- airspeed_sub = mavlink->add_orb_subscription(ORB_ID(airspeed));
- }
-
void send(const hrt_abstime t)
{
struct vehicle_attitude_s att;
@@ -536,25 +840,26 @@ protected:
struct actuator_armed_s armed;
struct actuator_controls_s act;
struct airspeed_s airspeed;
+ struct sensor_combined_s sensor_combined;
- bool updated = att_sub->update(&att_time, &att);
- updated |= pos_sub->update(&pos_time, &pos);
- updated |= armed_sub->update(&armed_time, &armed);
- updated |= act_sub->update(&act_time, &act);
- updated |= airspeed_sub->update(&airspeed_time, &airspeed);
+ bool updated = _att_sub->update(&_att_time, &att);
+ updated |= _pos_sub->update(&_pos_time, &pos);
+ updated |= _armed_sub->update(&_armed_time, &armed);
+ updated |= _act_sub->update(&_act_time, &act);
+ updated |= _airspeed_sub->update(&_airspeed_time, &airspeed);
+ updated |= _sensor_combined_sub->update(&_sensor_combined_time, &sensor_combined);
if (updated) {
- float groundspeed = sqrtf(pos.vel_n * pos.vel_n + pos.vel_e * pos.vel_e);
- uint16_t heading = _wrap_2pi(att.yaw) * M_RAD_TO_DEG_F;
- float throttle = armed.armed ? act.control[3] * 100.0f : 0.0f;
-
- mavlink_msg_vfr_hud_send(_channel,
- airspeed.true_airspeed_m_s,
- groundspeed,
- heading,
- throttle,
- pos.alt,
- -pos.vel_d);
+ mavlink_vfr_hud_t msg;
+
+ msg.airspeed = airspeed.true_airspeed_m_s;
+ msg.groundspeed = sqrtf(pos.vel_n * pos.vel_n + pos.vel_e * pos.vel_e);
+ msg.heading = _wrap_2pi(att.yaw) * M_RAD_TO_DEG_F;
+ msg.throttle = armed.armed ? act.control[3] * 100.0f : 0.0f;
+ msg.alt = sensor_combined.baro_alt_meter;
+ msg.climb = -pos.vel_d;
+
+ _mavlink->send_message(MAVLINK_MSG_ID_VFR_HUD, &msg);
}
}
};
@@ -573,41 +878,54 @@ public:
return "GPS_RAW_INT";
}
- static MavlinkStream *new_instance()
+ uint8_t get_id()
{
- return new MavlinkStreamGPSRawInt();
+ return MAVLINK_MSG_ID_GPS_RAW_INT;
+ }
+
+ static MavlinkStream *new_instance(Mavlink *mavlink)
+ {
+ return new MavlinkStreamGPSRawInt(mavlink);
+ }
+
+ unsigned get_size()
+ {
+ return MAVLINK_MSG_ID_GPS_RAW_INT_LEN + MAVLINK_NUM_NON_PAYLOAD_BYTES;
}
private:
- MavlinkOrbSubscription *gps_sub;
- uint64_t gps_time;
+ MavlinkOrbSubscription *_gps_sub;
+ uint64_t _gps_time;
+
+ /* do not allow top copying this class */
+ MavlinkStreamGPSRawInt(MavlinkStreamGPSRawInt &);
+ MavlinkStreamGPSRawInt& operator = (const MavlinkStreamGPSRawInt &);
protected:
- explicit MavlinkStreamGPSRawInt() : MavlinkStream(),
- gps_time(0)
+ explicit MavlinkStreamGPSRawInt(Mavlink *mavlink) : MavlinkStream(mavlink),
+ _gps_sub(_mavlink->add_orb_subscription(ORB_ID(vehicle_gps_position))),
+ _gps_time(0)
{}
- void subscribe(Mavlink *mavlink)
- {
- gps_sub = mavlink->add_orb_subscription(ORB_ID(vehicle_gps_position));
- }
-
void send(const hrt_abstime t)
{
struct vehicle_gps_position_s gps;
- if (gps_sub->update(&gps_time, &gps)) {
- mavlink_msg_gps_raw_int_send(_channel,
- gps.timestamp_position,
- gps.fix_type,
- gps.lat,
- gps.lon,
- gps.alt,
- cm_uint16_from_m_float(gps.eph_m),
- cm_uint16_from_m_float(gps.epv_m),
- gps.vel_m_s * 100.0f,
- _wrap_2pi(gps.cog_rad) * M_RAD_TO_DEG_F * 1e2f,
- gps.satellites_visible);
+ if (_gps_sub->update(&_gps_time, &gps)) {
+ mavlink_gps_raw_int_t msg;
+
+ msg.time_usec = gps.timestamp_position;
+ msg.fix_type = gps.fix_type;
+ msg.lat = gps.lat;
+ msg.lon = gps.lon;
+ msg.alt = gps.alt;
+ msg.eph = cm_uint16_from_m_float(gps.eph);
+ msg.epv = cm_uint16_from_m_float(gps.epv);
+ msg.vel = cm_uint16_from_m_float(gps.vel_m_s),
+ msg.cog = _wrap_2pi(gps.cog_rad) * M_RAD_TO_DEG_F * 1e2f,
+ msg.satellites_visible = gps.satellites_used;
+
+ _mavlink->send_message(MAVLINK_MSG_ID_GPS_RAW_INT, &msg);
}
}
};
@@ -626,49 +944,62 @@ public:
return "GLOBAL_POSITION_INT";
}
- static MavlinkStream *new_instance()
+ uint8_t get_id()
+ {
+ return MAVLINK_MSG_ID_GLOBAL_POSITION_INT;
+ }
+
+ static MavlinkStream *new_instance(Mavlink *mavlink)
{
- return new MavlinkStreamGlobalPositionInt();
+ return new MavlinkStreamGlobalPositionInt(mavlink);
+ }
+
+ unsigned get_size()
+ {
+ return MAVLINK_MSG_ID_GLOBAL_POSITION_INT_LEN + MAVLINK_NUM_NON_PAYLOAD_BYTES;
}
private:
- MavlinkOrbSubscription *pos_sub;
- uint64_t pos_time;
+ MavlinkOrbSubscription *_pos_sub;
+ uint64_t _pos_time;
- MavlinkOrbSubscription *home_sub;
- uint64_t home_time;
+ MavlinkOrbSubscription *_home_sub;
+ uint64_t _home_time;
+
+ /* do not allow top copying this class */
+ MavlinkStreamGlobalPositionInt(MavlinkStreamGlobalPositionInt &);
+ MavlinkStreamGlobalPositionInt& operator = (const MavlinkStreamGlobalPositionInt &);
protected:
- explicit MavlinkStreamGlobalPositionInt() : MavlinkStream(),
- pos_time(0),
- home_time(0)
+ explicit MavlinkStreamGlobalPositionInt(Mavlink *mavlink) : MavlinkStream(mavlink),
+ _pos_sub(_mavlink->add_orb_subscription(ORB_ID(vehicle_global_position))),
+ _pos_time(0),
+ _home_sub(_mavlink->add_orb_subscription(ORB_ID(home_position))),
+ _home_time(0)
{}
- void subscribe(Mavlink *mavlink)
- {
- pos_sub = mavlink->add_orb_subscription(ORB_ID(vehicle_global_position));
- home_sub = mavlink->add_orb_subscription(ORB_ID(home_position));
- }
-
void send(const hrt_abstime t)
{
struct vehicle_global_position_s pos;
struct home_position_s home;
- bool updated = pos_sub->update(&pos_time, &pos);
- updated |= home_sub->update(&home_time, &home);
+ bool updated = _pos_sub->update(&_pos_time, &pos);
+ updated |= _home_sub->update(&_home_time, &home);
if (updated) {
- mavlink_msg_global_position_int_send(_channel,
- pos.timestamp / 1000,
- pos.lat * 1e7,
- pos.lon * 1e7,
- pos.alt * 1000.0f,
- (pos.alt - home.alt) * 1000.0f,
- pos.vel_n * 100.0f,
- pos.vel_e * 100.0f,
- pos.vel_d * 100.0f,
- _wrap_2pi(pos.yaw) * M_RAD_TO_DEG_F * 100.0f);
+ mavlink_global_position_int_t msg;
+
+ msg.time_boot_ms = pos.timestamp / 1000;
+ msg.lat = pos.lat * 1e7;
+ msg.lon = pos.lon * 1e7;
+ msg.alt = pos.alt * 1000.0f;
+ msg.relative_alt = (pos.alt - home.alt) * 1000.0f;
+ msg.vx = pos.vel_n * 100.0f;
+ msg.vy = pos.vel_e * 100.0f;
+ msg.vz = pos.vel_d * 100.0f;
+ msg.hdg = _wrap_2pi(pos.yaw) * M_RAD_TO_DEG_F * 100.0f;
+
+ _mavlink->send_message(MAVLINK_MSG_ID_GLOBAL_POSITION_INT, &msg);
}
}
};
@@ -687,44 +1018,56 @@ public:
return "LOCAL_POSITION_NED";
}
- static MavlinkStream *new_instance()
+ uint8_t get_id()
{
- return new MavlinkStreamLocalPositionNED();
+ return MAVLINK_MSG_ID_LOCAL_POSITION_NED;
+ }
+
+ static MavlinkStream *new_instance(Mavlink *mavlink)
+ {
+ return new MavlinkStreamLocalPositionNED(mavlink);
+ }
+
+ unsigned get_size()
+ {
+ return MAVLINK_MSG_ID_LOCAL_POSITION_NED_LEN + MAVLINK_NUM_NON_PAYLOAD_BYTES;
}
private:
- MavlinkOrbSubscription *pos_sub;
- uint64_t pos_time;
+ MavlinkOrbSubscription *_pos_sub;
+ uint64_t _pos_time;
+
+ /* do not allow top copying this class */
+ MavlinkStreamLocalPositionNED(MavlinkStreamLocalPositionNED &);
+ MavlinkStreamLocalPositionNED& operator = (const MavlinkStreamLocalPositionNED &);
protected:
- explicit MavlinkStreamLocalPositionNED() : MavlinkStream(),
- pos_time(0)
+ explicit MavlinkStreamLocalPositionNED(Mavlink *mavlink) : MavlinkStream(mavlink),
+ _pos_sub(_mavlink->add_orb_subscription(ORB_ID(vehicle_local_position))),
+ _pos_time(0)
{}
- void subscribe(Mavlink *mavlink)
- {
- pos_sub = mavlink->add_orb_subscription(ORB_ID(vehicle_local_position));
- }
-
void send(const hrt_abstime t)
{
struct vehicle_local_position_s pos;
- if (pos_sub->update(&pos_time, &pos)) {
- mavlink_msg_local_position_ned_send(_channel,
- pos.timestamp / 1000,
- pos.x,
- pos.y,
- pos.z,
- pos.vx,
- pos.vy,
- pos.vz);
+ if (_pos_sub->update(&_pos_time, &pos)) {
+ mavlink_local_position_ned_t msg;
+
+ msg.time_boot_ms = pos.timestamp / 1000;
+ msg.x = pos.x;
+ msg.y = pos.y;
+ msg.z = pos.z;
+ msg.vx = pos.vx;
+ msg.vy = pos.vy;
+ msg.vz = pos.vz;
+
+ _mavlink->send_message(MAVLINK_MSG_ID_LOCAL_POSITION_NED, &msg);
}
}
};
-
class MavlinkStreamViconPositionEstimate : public MavlinkStream
{
public:
@@ -738,38 +1081,51 @@ public:
return "VICON_POSITION_ESTIMATE";
}
- static MavlinkStream *new_instance()
+ uint8_t get_id()
{
- return new MavlinkStreamViconPositionEstimate();
+ return MAVLINK_MSG_ID_VICON_POSITION_ESTIMATE;
+ }
+
+ static MavlinkStream *new_instance(Mavlink *mavlink)
+ {
+ return new MavlinkStreamViconPositionEstimate(mavlink);
+ }
+
+ unsigned get_size()
+ {
+ return MAVLINK_MSG_ID_VICON_POSITION_ESTIMATE_LEN + MAVLINK_NUM_NON_PAYLOAD_BYTES;
}
private:
- MavlinkOrbSubscription *pos_sub;
- uint64_t pos_time;
+ MavlinkOrbSubscription *_pos_sub;
+ uint64_t _pos_time;
+
+ /* do not allow top copying this class */
+ MavlinkStreamViconPositionEstimate(MavlinkStreamViconPositionEstimate &);
+ MavlinkStreamViconPositionEstimate& operator = (const MavlinkStreamViconPositionEstimate &);
protected:
- explicit MavlinkStreamViconPositionEstimate() : MavlinkStream(),
- pos_time(0)
+ explicit MavlinkStreamViconPositionEstimate(Mavlink *mavlink) : MavlinkStream(mavlink),
+ _pos_sub(_mavlink->add_orb_subscription(ORB_ID(vehicle_vicon_position))),
+ _pos_time(0)
{}
- void subscribe(Mavlink *mavlink)
- {
- pos_sub = mavlink->add_orb_subscription(ORB_ID(vehicle_vicon_position));
- }
-
void send(const hrt_abstime t)
{
struct vehicle_vicon_position_s pos;
- if (pos_sub->update(&pos_time, &pos)) {
- mavlink_msg_vicon_position_estimate_send(_channel,
- pos.timestamp / 1000,
- pos.x,
- pos.y,
- pos.z,
- pos.roll,
- pos.pitch,
- pos.yaw);
+ if (_pos_sub->update(&_pos_time, &pos)) {
+ mavlink_vicon_position_estimate_t msg;
+
+ msg.usec = pos.timestamp;
+ msg.x = pos.x;
+ msg.y = pos.y;
+ msg.z = pos.z;
+ msg.roll = pos.roll;
+ msg.pitch = pos.pitch;
+ msg.yaw = pos.yaw;
+
+ _mavlink->send_message(MAVLINK_MSG_ID_VICON_POSITION_ESTIMATE, &msg);
}
}
};
@@ -788,37 +1144,54 @@ public:
return "GPS_GLOBAL_ORIGIN";
}
- static MavlinkStream *new_instance()
+ uint8_t get_id()
{
- return new MavlinkStreamGPSGlobalOrigin();
+ return MAVLINK_MSG_ID_GPS_GLOBAL_ORIGIN;
}
-private:
- MavlinkOrbSubscription *home_sub;
+ static MavlinkStream *new_instance(Mavlink *mavlink)
+ {
+ return new MavlinkStreamGPSGlobalOrigin(mavlink);
+ }
-protected:
- void subscribe(Mavlink *mavlink)
+ unsigned get_size()
{
- home_sub = mavlink->add_orb_subscription(ORB_ID(home_position));
+ return _home_sub->is_published() ? (MAVLINK_MSG_ID_GPS_GLOBAL_ORIGIN_LEN + MAVLINK_NUM_NON_PAYLOAD_BYTES) : 0;
}
+private:
+ MavlinkOrbSubscription *_home_sub;
+
+ /* do not allow top copying this class */
+ MavlinkStreamGPSGlobalOrigin(MavlinkStreamGPSGlobalOrigin &);
+ MavlinkStreamGPSGlobalOrigin& operator = (const MavlinkStreamGPSGlobalOrigin &);
+
+protected:
+ explicit MavlinkStreamGPSGlobalOrigin(Mavlink *mavlink) : MavlinkStream(mavlink),
+ _home_sub(_mavlink->add_orb_subscription(ORB_ID(home_position)))
+ {}
+
void send(const hrt_abstime t)
{
/* we're sending the GPS home periodically to ensure the
* the GCS does pick it up at one point */
- if (home_sub->is_published()) {
+ if (_home_sub->is_published()) {
struct home_position_s home;
- if (home_sub->update(&home)) {
- mavlink_msg_gps_global_origin_send(_channel,
- (int32_t)(home.lat * 1e7),
- (int32_t)(home.lon * 1e7),
- (int32_t)(home.alt) * 1000.0f);
+ if (_home_sub->update(&home)) {
+ mavlink_gps_global_origin_t msg;
+
+ msg.latitude = home.lat * 1e7;
+ msg.longitude = home.lon * 1e7;
+ msg.altitude = home.alt * 1e3f;
+
+ _mavlink->send_message(MAVLINK_MSG_ID_GPS_GLOBAL_ORIGIN, &msg);
}
}
}
};
+
template <int N>
class MavlinkStreamServoOutputRaw : public MavlinkStream
{
@@ -828,6 +1201,11 @@ public:
return MavlinkStreamServoOutputRaw<N>::get_name_static();
}
+ uint8_t get_id()
+ {
+ return MAVLINK_MSG_ID_SERVO_OUTPUT_RAW;
+ }
+
static const char *get_name_static()
{
switch (N) {
@@ -845,21 +1223,28 @@ public:
}
}
- static MavlinkStream *new_instance()
+ static MavlinkStream *new_instance(Mavlink *mavlink)
{
- return new MavlinkStreamServoOutputRaw<N>();
+ return new MavlinkStreamServoOutputRaw<N>(mavlink);
+ }
+
+ unsigned get_size()
+ {
+ return MAVLINK_MSG_ID_SERVO_OUTPUT_RAW_LEN + MAVLINK_NUM_NON_PAYLOAD_BYTES;
}
private:
- MavlinkOrbSubscription *act_sub;
- uint64_t act_time;
+ MavlinkOrbSubscription *_act_sub;
+ uint64_t _act_time;
-protected:
- explicit MavlinkStreamServoOutputRaw() : MavlinkStream(),
- act_time(0)
- {}
+ /* do not allow top copying this class */
+ MavlinkStreamServoOutputRaw(MavlinkStreamServoOutputRaw &);
+ MavlinkStreamServoOutputRaw& operator = (const MavlinkStreamServoOutputRaw &);
- void subscribe(Mavlink *mavlink)
+protected:
+ explicit MavlinkStreamServoOutputRaw(Mavlink *mavlink) : MavlinkStream(mavlink),
+ _act_sub(nullptr),
+ _act_time(0)
{
orb_id_t act_topics[] = {
ORB_ID(actuator_outputs_0),
@@ -868,25 +1253,28 @@ protected:
ORB_ID(actuator_outputs_3)
};
- act_sub = mavlink->add_orb_subscription(act_topics[N]);
+ _act_sub = _mavlink->add_orb_subscription(act_topics[N]);
}
void send(const hrt_abstime t)
{
struct actuator_outputs_s act;
- if (act_sub->update(&act_time, &act)) {
- mavlink_msg_servo_output_raw_send(_channel,
- act.timestamp / 1000,
- N,
- act.output[0],
- act.output[1],
- act.output[2],
- act.output[3],
- act.output[4],
- act.output[5],
- act.output[6],
- act.output[7]);
+ if (_act_sub->update(&_act_time, &act)) {
+ mavlink_servo_output_raw_t msg;
+
+ msg.time_usec = act.timestamp;
+ msg.port = N;
+ msg.servo1_raw = act.output[0];
+ msg.servo2_raw = act.output[1];
+ msg.servo3_raw = act.output[2];
+ msg.servo4_raw = act.output[3];
+ msg.servo5_raw = act.output[4];
+ msg.servo6_raw = act.output[5];
+ msg.servo7_raw = act.output[6];
+ msg.servo8_raw = act.output[7];
+
+ _mavlink->send_message(MAVLINK_MSG_ID_SERVO_OUTPUT_RAW, &msg);
}
}
};
@@ -905,44 +1293,54 @@ public:
return "HIL_CONTROLS";
}
- static MavlinkStream *new_instance()
+ uint8_t get_id()
{
- return new MavlinkStreamHILControls();
+ return MAVLINK_MSG_ID_HIL_CONTROLS;
+ }
+
+ static MavlinkStream *new_instance(Mavlink *mavlink)
+ {
+ return new MavlinkStreamHILControls(mavlink);
+ }
+
+ unsigned get_size()
+ {
+ return MAVLINK_MSG_ID_HIL_CONTROLS_LEN + MAVLINK_NUM_NON_PAYLOAD_BYTES;
}
private:
- MavlinkOrbSubscription *status_sub;
- uint64_t status_time;
+ MavlinkOrbSubscription *_status_sub;
+ uint64_t _status_time;
+
+ MavlinkOrbSubscription *_pos_sp_triplet_sub;
+ uint64_t _pos_sp_triplet_time;
- MavlinkOrbSubscription *pos_sp_triplet_sub;
- uint64_t pos_sp_triplet_time;
+ MavlinkOrbSubscription *_act_sub;
+ uint64_t _act_time;
- MavlinkOrbSubscription *act_sub;
- uint64_t act_time;
+ /* do not allow top copying this class */
+ MavlinkStreamHILControls(MavlinkStreamHILControls &);
+ MavlinkStreamHILControls& operator = (const MavlinkStreamHILControls &);
protected:
- explicit MavlinkStreamHILControls() : MavlinkStream(),
- status_time(0),
- pos_sp_triplet_time(0),
- act_time(0)
+ explicit MavlinkStreamHILControls(Mavlink *mavlink) : MavlinkStream(mavlink),
+ _status_sub(_mavlink->add_orb_subscription(ORB_ID(vehicle_status))),
+ _status_time(0),
+ _pos_sp_triplet_sub(_mavlink->add_orb_subscription(ORB_ID(position_setpoint_triplet))),
+ _pos_sp_triplet_time(0),
+ _act_sub(_mavlink->add_orb_subscription(ORB_ID(actuator_outputs_0))),
+ _act_time(0)
{}
- void subscribe(Mavlink *mavlink)
- {
- status_sub = mavlink->add_orb_subscription(ORB_ID(vehicle_status));
- pos_sp_triplet_sub = mavlink->add_orb_subscription(ORB_ID(position_setpoint_triplet));
- act_sub = mavlink->add_orb_subscription(ORB_ID(actuator_outputs_0));
- }
-
void send(const hrt_abstime t)
{
struct vehicle_status_s status;
struct position_setpoint_triplet_s pos_sp_triplet;
struct actuator_outputs_s act;
- bool updated = act_sub->update(&act_time, &act);
- updated |= pos_sp_triplet_sub->update(&pos_sp_triplet_time, &pos_sp_triplet);
- updated |= status_sub->update(&status_time, &status);
+ bool updated = _act_sub->update(&_act_time, &act);
+ updated |= _pos_sp_triplet_sub->update(&_pos_sp_triplet_time, &pos_sp_triplet);
+ updated |= _status_sub->update(&_status_time, &status);
if (updated && (status.arming_state == ARMING_STATE_ARMED)) {
/* translate the current syste state to mavlink state and mode */
@@ -951,13 +1349,21 @@ protected:
uint32_t mavlink_custom_mode;
get_mavlink_mode_state(&status, &pos_sp_triplet, &mavlink_state, &mavlink_base_mode, &mavlink_custom_mode);
- if (mavlink_system.type == MAV_TYPE_QUADROTOR ||
- mavlink_system.type == MAV_TYPE_HEXAROTOR ||
- mavlink_system.type == MAV_TYPE_OCTOROTOR) {
- /* set number of valid outputs depending on vehicle type */
+ float out[8];
+
+ const float pwm_center = (PWM_HIGHEST_MAX + PWM_LOWEST_MIN) / 2;
+
+ unsigned system_type = _mavlink->get_system_type();
+
+ /* scale outputs depending on system type */
+ if (system_type == MAV_TYPE_QUADROTOR ||
+ system_type == MAV_TYPE_HEXAROTOR ||
+ system_type == MAV_TYPE_OCTOROTOR) {
+ /* multirotors: set number of rotor outputs depending on type */
+
unsigned n;
- switch (mavlink_system.type) {
+ switch (system_type) {
case MAV_TYPE_QUADROTOR:
n = 4;
break;
@@ -971,102 +1377,117 @@ protected:
break;
}
- /* scale / assign outputs depending on system type */
- float out[8];
-
for (unsigned i = 0; i < 8; i++) {
- if (i < n) {
- if (mavlink_base_mode & MAV_MODE_FLAG_SAFETY_ARMED) {
- /* scale fake PWM out 900..2100 us to 0..1 for normal multirotors */
+ if (act.output[i] > PWM_LOWEST_MIN / 2) {
+ if (i < n) {
+ /* scale PWM out 900..2100 us to 0..1 for rotors */
out[i] = (act.output[i] - PWM_LOWEST_MIN) / (PWM_HIGHEST_MAX - PWM_LOWEST_MIN);
} else {
- /* send 0 when disarmed */
- out[i] = 0.0f;
+ /* scale PWM out 900..2100 us to -1..1 for other channels */
+ out[i] = (act.output[i] - pwm_center) / ((PWM_HIGHEST_MAX - PWM_LOWEST_MIN) / 2);
}
} else {
- out[i] = -1.0f;
+ /* send 0 when disarmed and for disabled channels */
+ out[i] = 0.0f;
}
}
- mavlink_msg_hil_controls_send(_channel,
- hrt_absolute_time(),
- out[0], out[1], out[2], out[3], out[4], out[5], out[6], out[7],
- mavlink_base_mode,
- 0);
} else {
-
- /* fixed wing: scale all channels except throttle -1 .. 1
- * because we know that we set the mixers up this way
- */
-
- float out[8];
-
- const float pwm_center = (PWM_HIGHEST_MAX + PWM_LOWEST_MIN) / 2;
+ /* fixed wing: scale throttle to 0..1 and other channels to -1..1 */
for (unsigned i = 0; i < 8; i++) {
- if (i != 3) {
- /* scale fake PWM out 900..2100 us to -1..+1 for normal channels */
- out[i] = (act.output[i] - pwm_center) / ((PWM_HIGHEST_MAX - PWM_LOWEST_MIN) / 2);
+ if (act.output[i] > PWM_LOWEST_MIN / 2) {
+ if (i != 3) {
+ /* scale PWM out 900..2100 us to -1..1 for normal channels */
+ out[i] = (act.output[i] - pwm_center) / ((PWM_HIGHEST_MAX - PWM_LOWEST_MIN) / 2);
- } else {
+ } else {
+ /* scale PWM out 900..2100 us to 0..1 for throttle */
+ out[i] = (act.output[i] - PWM_LOWEST_MIN) / (PWM_HIGHEST_MAX - PWM_LOWEST_MIN);
+ }
- /* scale fake PWM out 900..2100 us to 0..1 for throttle */
- out[i] = (act.output[i] - PWM_LOWEST_MIN) / (PWM_HIGHEST_MAX - PWM_LOWEST_MIN);
+ } else {
+ /* set 0 for disabled channels */
+ out[i] = 0.0f;
}
-
}
-
- mavlink_msg_hil_controls_send(_channel,
- hrt_absolute_time(),
- out[0], out[1], out[2], out[3], out[4], out[5], out[6], out[7],
- mavlink_base_mode,
- 0);
}
+
+ mavlink_hil_controls_t msg;
+
+ msg.time_usec = hrt_absolute_time();
+ msg.roll_ailerons = out[0];
+ msg.pitch_elevator = out[1];
+ msg.yaw_rudder = out[2];
+ msg.throttle = out[3];
+ msg.aux1 = out[4];
+ msg.aux2 = out[5];
+ msg.aux3 = out[6];
+ msg.aux4 = out[7];
+ msg.mode = mavlink_base_mode;
+ msg.nav_mode = 0;
+
+ _mavlink->send_message(MAVLINK_MSG_ID_HIL_CONTROLS, &msg);
}
}
};
-class MavlinkStreamGlobalPositionSetpointInt : public MavlinkStream
+class MavlinkStreamPositionTargetGlobalInt : public MavlinkStream
{
public:
const char *get_name() const
{
- return MavlinkStreamGlobalPositionSetpointInt::get_name_static();
+ return MavlinkStreamPositionTargetGlobalInt::get_name_static();
}
static const char *get_name_static()
{
- return "GLOBAL_POSITION_SETPOINT_INT";
+ return "POSITION_TARGET_GLOBAL_INT";
}
- static MavlinkStream *new_instance()
+ uint8_t get_id()
{
- return new MavlinkStreamGlobalPositionSetpointInt();
+ return MAVLINK_MSG_ID_POSITION_TARGET_GLOBAL_INT;
}
-private:
- MavlinkOrbSubscription *pos_sp_triplet_sub;
+ static MavlinkStream *new_instance(Mavlink *mavlink)
+ {
+ return new MavlinkStreamPositionTargetGlobalInt(mavlink);
+ }
-protected:
- void subscribe(Mavlink *mavlink)
+ unsigned get_size()
{
- pos_sp_triplet_sub = mavlink->add_orb_subscription(ORB_ID(position_setpoint_triplet));
+ return MAVLINK_MSG_ID_POSITION_TARGET_GLOBAL_INT_LEN + MAVLINK_NUM_NON_PAYLOAD_BYTES;
}
+private:
+ MavlinkOrbSubscription *_pos_sp_triplet_sub;
+
+ /* do not allow top copying this class */
+ MavlinkStreamPositionTargetGlobalInt(MavlinkStreamPositionTargetGlobalInt &);
+ MavlinkStreamPositionTargetGlobalInt& operator = (const MavlinkStreamPositionTargetGlobalInt &);
+
+protected:
+ explicit MavlinkStreamPositionTargetGlobalInt(Mavlink *mavlink) : MavlinkStream(mavlink),
+ _pos_sp_triplet_sub(_mavlink->add_orb_subscription(ORB_ID(position_setpoint_triplet)))
+ {}
+
void send(const hrt_abstime t)
{
struct position_setpoint_triplet_s pos_sp_triplet;
- if (pos_sp_triplet_sub->update(&pos_sp_triplet)) {
- mavlink_msg_global_position_setpoint_int_send(_channel,
- MAV_FRAME_GLOBAL,
- (int32_t)(pos_sp_triplet.current.lat * 1e7),
- (int32_t)(pos_sp_triplet.current.lon * 1e7),
- (int32_t)(pos_sp_triplet.current.alt * 1000),
- (int16_t)(pos_sp_triplet.current.yaw * M_RAD_TO_DEG_F * 100.0f));
+ if (_pos_sp_triplet_sub->update(&pos_sp_triplet)) {
+ mavlink_position_target_global_int_t msg{};
+
+ msg.coordinate_frame = MAV_FRAME_GLOBAL;
+ msg.lat_int = pos_sp_triplet.current.lat * 1e7;
+ msg.lon_int = pos_sp_triplet.current.lon * 1e7;
+ msg.alt = pos_sp_triplet.current.alt;
+
+ _mavlink->send_message(MAVLINK_MSG_ID_POSITION_TARGET_GLOBAL_INT, &msg);
}
}
};
@@ -1082,135 +1503,124 @@ public:
static const char *get_name_static()
{
- return "LOCAL_POSITION_SETPOINT";
+ return "POSITION_TARGET_LOCAL_NED";
+ }
+
+ uint8_t get_id()
+ {
+ return MAVLINK_MSG_ID_POSITION_TARGET_LOCAL_NED;
}
- static MavlinkStream *new_instance()
+ static MavlinkStream *new_instance(Mavlink *mavlink)
{
- return new MavlinkStreamLocalPositionSetpoint();
+ return new MavlinkStreamLocalPositionSetpoint(mavlink);
+ }
+
+ unsigned get_size()
+ {
+ return MAVLINK_MSG_ID_POSITION_TARGET_LOCAL_NED_LEN + MAVLINK_NUM_NON_PAYLOAD_BYTES;
}
private:
- MavlinkOrbSubscription *pos_sp_sub;
- uint64_t pos_sp_time;
+ MavlinkOrbSubscription *_pos_sp_sub;
+ uint64_t _pos_sp_time;
+
+ /* do not allow top copying this class */
+ MavlinkStreamLocalPositionSetpoint(MavlinkStreamLocalPositionSetpoint &);
+ MavlinkStreamLocalPositionSetpoint& operator = (const MavlinkStreamLocalPositionSetpoint &);
protected:
- explicit MavlinkStreamLocalPositionSetpoint() : MavlinkStream(),
- pos_sp_time(0)
+ explicit MavlinkStreamLocalPositionSetpoint(Mavlink *mavlink) : MavlinkStream(mavlink),
+ _pos_sp_sub(_mavlink->add_orb_subscription(ORB_ID(vehicle_local_position_setpoint))),
+ _pos_sp_time(0)
{}
- void subscribe(Mavlink *mavlink)
- {
- pos_sp_sub = mavlink->add_orb_subscription(ORB_ID(vehicle_local_position_setpoint));
- }
-
void send(const hrt_abstime t)
{
struct vehicle_local_position_setpoint_s pos_sp;
- if (pos_sp_sub->update(&pos_sp_time, &pos_sp)) {
- mavlink_msg_local_position_setpoint_send(_channel,
- MAV_FRAME_LOCAL_NED,
- pos_sp.x,
- pos_sp.y,
- pos_sp.z,
- pos_sp.yaw);
+ if (_pos_sp_sub->update(&_pos_sp_time, &pos_sp)) {
+ mavlink_position_target_local_ned_t msg{};
+
+ msg.time_boot_ms = pos_sp.timestamp / 1000;
+ msg.coordinate_frame = MAV_FRAME_LOCAL_NED;
+ msg.x = pos_sp.x;
+ msg.y = pos_sp.y;
+ msg.z = pos_sp.z;
+
+ _mavlink->send_message(MAVLINK_MSG_ID_POSITION_TARGET_LOCAL_NED, &msg);
}
}
};
-class MavlinkStreamRollPitchYawThrustSetpoint : public MavlinkStream
+class MavlinkStreamAttitudeTarget : public MavlinkStream
{
public:
const char *get_name() const
{
- return MavlinkStreamRollPitchYawThrustSetpoint::get_name_static();
+ return MavlinkStreamAttitudeTarget::get_name_static();
}
static const char *get_name_static()
{
- return "ROLL_PITCH_YAW_THRUST_SETPOINT";
+ return "ATTITUDE_TARGET";
}
- static MavlinkStream *new_instance()
+ uint8_t get_id()
{
- return new MavlinkStreamRollPitchYawThrustSetpoint();
+ return MAVLINK_MSG_ID_ATTITUDE_TARGET;
}
-private:
- MavlinkOrbSubscription *att_sp_sub;
- uint64_t att_sp_time;
-
-protected:
- explicit MavlinkStreamRollPitchYawThrustSetpoint() : MavlinkStream(),
- att_sp_time(0)
- {}
-
- void subscribe(Mavlink *mavlink)
+ static MavlinkStream *new_instance(Mavlink *mavlink)
{
- att_sp_sub = mavlink->add_orb_subscription(ORB_ID(vehicle_attitude_setpoint));
+ return new MavlinkStreamAttitudeTarget(mavlink);
}
- void send(const hrt_abstime t)
+ unsigned get_size()
{
- struct vehicle_attitude_setpoint_s att_sp;
-
- if (att_sp_sub->update(&att_sp_time, &att_sp)) {
- mavlink_msg_roll_pitch_yaw_thrust_setpoint_send(_channel,
- att_sp.timestamp / 1000,
- att_sp.roll_body,
- att_sp.pitch_body,
- att_sp.yaw_body,
- att_sp.thrust);
- }
+ return MAVLINK_MSG_ID_ATTITUDE_TARGET_LEN + MAVLINK_NUM_NON_PAYLOAD_BYTES;
}
-};
+private:
+ MavlinkOrbSubscription *_att_sp_sub;
+ MavlinkOrbSubscription *_att_rates_sp_sub;
+ uint64_t _att_sp_time;
+ uint64_t _att_rates_sp_time;
-class MavlinkStreamRollPitchYawRatesThrustSetpoint : public MavlinkStream
-{
-public:
- const char *get_name() const
- {
- return MavlinkStreamRollPitchYawRatesThrustSetpoint::get_name_static();
- }
+ /* do not allow top copying this class */
+ MavlinkStreamAttitudeTarget(MavlinkStreamAttitudeTarget &);
+ MavlinkStreamAttitudeTarget& operator = (const MavlinkStreamAttitudeTarget &);
- static const char *get_name_static()
- {
- return "ROLL_PITCH_YAW_RATES_THRUST_SETPOINT";
- }
+protected:
+ explicit MavlinkStreamAttitudeTarget(Mavlink *mavlink) : MavlinkStream(mavlink),
+ _att_sp_sub(_mavlink->add_orb_subscription(ORB_ID(vehicle_attitude_setpoint))),
+ _att_rates_sp_sub(_mavlink->add_orb_subscription(ORB_ID(vehicle_rates_setpoint))),
+ _att_sp_time(0),
+ _att_rates_sp_time(0)
+ {}
- static MavlinkStream *new_instance()
+ void send(const hrt_abstime t)
{
- return new MavlinkStreamRollPitchYawRatesThrustSetpoint();
- }
+ struct vehicle_attitude_setpoint_s att_sp;
-private:
- MavlinkOrbSubscription *att_rates_sp_sub;
- uint64_t att_rates_sp_time;
+ if (_att_sp_sub->update(&_att_sp_time, &att_sp)) {
-protected:
- explicit MavlinkStreamRollPitchYawRatesThrustSetpoint() : MavlinkStream(),
- att_rates_sp_time(0)
- {}
+ struct vehicle_rates_setpoint_s att_rates_sp;
+ (void)_att_rates_sp_sub->update(&_att_rates_sp_time, &att_rates_sp);
- void subscribe(Mavlink *mavlink)
- {
- att_rates_sp_sub = mavlink->add_orb_subscription(ORB_ID(vehicle_rates_setpoint));
- }
+ mavlink_attitude_target_t msg{};
- void send(const hrt_abstime t)
- {
- struct vehicle_rates_setpoint_s att_rates_sp;
+ msg.time_boot_ms = att_sp.timestamp / 1000;
+ mavlink_euler_to_quaternion(att_sp.roll_body, att_sp.pitch_body, att_sp.yaw_body, msg.q);
+
+ msg.body_roll_rate = att_rates_sp.roll;
+ msg.body_pitch_rate = att_rates_sp.pitch;
+ msg.body_yaw_rate = att_rates_sp.yaw;
- if (att_rates_sp_sub->update(&att_rates_sp_time, &att_rates_sp)) {
- mavlink_msg_roll_pitch_yaw_rates_thrust_setpoint_send(_channel,
- att_rates_sp.timestamp / 1000,
- att_rates_sp.roll,
- att_rates_sp.pitch,
- att_rates_sp.yaw,
- att_rates_sp.thrust);
+ msg.thrust = att_sp.thrust;
+
+ _mavlink->send_message(MAVLINK_MSG_ID_ATTITUDE_TARGET, &msg);
}
}
};
@@ -1229,72 +1639,135 @@ public:
return "RC_CHANNELS_RAW";
}
- static MavlinkStream *new_instance()
+ uint8_t get_id()
+ {
+ return MAVLINK_MSG_ID_RC_CHANNELS_RAW;
+ }
+
+ static MavlinkStream *new_instance(Mavlink *mavlink)
+ {
+ return new MavlinkStreamRCChannelsRaw(mavlink);
+ }
+
+ unsigned get_size()
{
- return new MavlinkStreamRCChannelsRaw();
+ return _rc_sub->is_published() ? ((RC_INPUT_MAX_CHANNELS / 8) * (MAVLINK_MSG_ID_RC_CHANNELS_RAW_LEN + MAVLINK_NUM_NON_PAYLOAD_BYTES) +
+ MAVLINK_MSG_ID_RC_CHANNELS_LEN + MAVLINK_NUM_NON_PAYLOAD_BYTES) : 0;
}
private:
- MavlinkOrbSubscription *rc_sub;
- uint64_t rc_time;
+ MavlinkOrbSubscription *_rc_sub;
+ uint64_t _rc_time;
+
+ /* do not allow top copying this class */
+ MavlinkStreamRCChannelsRaw(MavlinkStreamRCChannelsRaw &);
+ MavlinkStreamRCChannelsRaw& operator = (const MavlinkStreamRCChannelsRaw &);
protected:
- explicit MavlinkStreamRCChannelsRaw() : MavlinkStream(),
- rc_time(0)
+ explicit MavlinkStreamRCChannelsRaw(Mavlink *mavlink) : MavlinkStream(mavlink),
+ _rc_sub(_mavlink->add_orb_subscription(ORB_ID(input_rc))),
+ _rc_time(0)
{}
- void subscribe(Mavlink *mavlink)
- {
- rc_sub = mavlink->add_orb_subscription(ORB_ID(input_rc));
- }
-
void send(const hrt_abstime t)
{
struct rc_input_values rc;
- if (rc_sub->update(&rc_time, &rc)) {
+ if (_rc_sub->update(&_rc_time, &rc)) {
const unsigned port_width = 8;
- // Deprecated message (but still needed for compatibility!)
+ /* deprecated message (but still needed for compatibility!) */
for (unsigned i = 0; (i * port_width) < rc.channel_count; i++) {
- /* Channels are sent in MAVLink main loop at a fixed interval */
- mavlink_msg_rc_channels_raw_send(_channel,
- rc.timestamp_publication / 1000,
- i,
- (rc.channel_count > (i * port_width) + 0) ? rc.values[(i * port_width) + 0] : UINT16_MAX,
- (rc.channel_count > (i * port_width) + 1) ? rc.values[(i * port_width) + 1] : UINT16_MAX,
- (rc.channel_count > (i * port_width) + 2) ? rc.values[(i * port_width) + 2] : UINT16_MAX,
- (rc.channel_count > (i * port_width) + 3) ? rc.values[(i * port_width) + 3] : UINT16_MAX,
- (rc.channel_count > (i * port_width) + 4) ? rc.values[(i * port_width) + 4] : UINT16_MAX,
- (rc.channel_count > (i * port_width) + 5) ? rc.values[(i * port_width) + 5] : UINT16_MAX,
- (rc.channel_count > (i * port_width) + 6) ? rc.values[(i * port_width) + 6] : UINT16_MAX,
- (rc.channel_count > (i * port_width) + 7) ? rc.values[(i * port_width) + 7] : UINT16_MAX,
- rc.rssi);
+ /* channels are sent in MAVLink main loop at a fixed interval */
+ mavlink_rc_channels_raw_t msg;
+
+ msg.time_boot_ms = rc.timestamp_publication / 1000;
+ msg.port = i;
+ msg.chan1_raw = (rc.channel_count > (i * port_width) + 0) ? rc.values[(i * port_width) + 0] : UINT16_MAX;
+ msg.chan2_raw = (rc.channel_count > (i * port_width) + 1) ? rc.values[(i * port_width) + 1] : UINT16_MAX;
+ msg.chan3_raw = (rc.channel_count > (i * port_width) + 2) ? rc.values[(i * port_width) + 2] : UINT16_MAX;
+ msg.chan4_raw = (rc.channel_count > (i * port_width) + 3) ? rc.values[(i * port_width) + 3] : UINT16_MAX;
+ msg.chan5_raw = (rc.channel_count > (i * port_width) + 4) ? rc.values[(i * port_width) + 4] : UINT16_MAX;
+ msg.chan6_raw = (rc.channel_count > (i * port_width) + 5) ? rc.values[(i * port_width) + 5] : UINT16_MAX;
+ msg.chan7_raw = (rc.channel_count > (i * port_width) + 6) ? rc.values[(i * port_width) + 6] : UINT16_MAX;
+ msg.chan8_raw = (rc.channel_count > (i * port_width) + 7) ? rc.values[(i * port_width) + 7] : UINT16_MAX;
+ msg.rssi = rc.rssi;
+
+ _mavlink->send_message(MAVLINK_MSG_ID_RC_CHANNELS_RAW, &msg);
}
- // New message
- mavlink_msg_rc_channels_send(_channel,
- rc.timestamp_publication / 1000,
- rc.channel_count,
- ((rc.channel_count > 0) ? rc.values[0] : UINT16_MAX),
- ((rc.channel_count > 1) ? rc.values[1] : UINT16_MAX),
- ((rc.channel_count > 2) ? rc.values[2] : UINT16_MAX),
- ((rc.channel_count > 3) ? rc.values[3] : UINT16_MAX),
- ((rc.channel_count > 4) ? rc.values[4] : UINT16_MAX),
- ((rc.channel_count > 5) ? rc.values[5] : UINT16_MAX),
- ((rc.channel_count > 6) ? rc.values[6] : UINT16_MAX),
- ((rc.channel_count > 7) ? rc.values[7] : UINT16_MAX),
- ((rc.channel_count > 8) ? rc.values[8] : UINT16_MAX),
- ((rc.channel_count > 9) ? rc.values[9] : UINT16_MAX),
- ((rc.channel_count > 10) ? rc.values[10] : UINT16_MAX),
- ((rc.channel_count > 11) ? rc.values[11] : UINT16_MAX),
- ((rc.channel_count > 12) ? rc.values[12] : UINT16_MAX),
- ((rc.channel_count > 13) ? rc.values[13] : UINT16_MAX),
- ((rc.channel_count > 14) ? rc.values[14] : UINT16_MAX),
- ((rc.channel_count > 15) ? rc.values[15] : UINT16_MAX),
- ((rc.channel_count > 16) ? rc.values[16] : UINT16_MAX),
- ((rc.channel_count > 17) ? rc.values[17] : UINT16_MAX),
- rc.rssi);
+ /* new message */
+ mavlink_rc_channels_t msg;
+
+ msg.time_boot_ms = rc.timestamp_publication / 1000;
+ msg.chancount = rc.channel_count;
+ msg.chan1_raw = (rc.channel_count > 0) ? rc.values[0] : UINT16_MAX;
+ msg.chan2_raw = (rc.channel_count > 1) ? rc.values[1] : UINT16_MAX;
+ msg.chan3_raw = (rc.channel_count > 2) ? rc.values[2] : UINT16_MAX;
+ msg.chan4_raw = (rc.channel_count > 3) ? rc.values[3] : UINT16_MAX;
+ msg.chan5_raw = (rc.channel_count > 4) ? rc.values[4] : UINT16_MAX;
+ msg.chan6_raw = (rc.channel_count > 5) ? rc.values[5] : UINT16_MAX;
+ msg.chan7_raw = (rc.channel_count > 6) ? rc.values[6] : UINT16_MAX;
+ msg.chan8_raw = (rc.channel_count > 7) ? rc.values[7] : UINT16_MAX;
+ msg.chan9_raw = (rc.channel_count > 8) ? rc.values[8] : UINT16_MAX;
+ msg.chan10_raw = (rc.channel_count > 9) ? rc.values[9] : UINT16_MAX;
+ msg.chan11_raw = (rc.channel_count > 10) ? rc.values[10] : UINT16_MAX;
+ msg.chan12_raw = (rc.channel_count > 11) ? rc.values[11] : UINT16_MAX;
+ msg.chan13_raw = (rc.channel_count > 12) ? rc.values[12] : UINT16_MAX;
+ msg.chan14_raw = (rc.channel_count > 13) ? rc.values[13] : UINT16_MAX;
+ msg.chan15_raw = (rc.channel_count > 14) ? rc.values[14] : UINT16_MAX;
+ msg.chan16_raw = (rc.channel_count > 15) ? rc.values[15] : UINT16_MAX;
+ msg.chan17_raw = (rc.channel_count > 16) ? rc.values[16] : UINT16_MAX;
+ msg.chan18_raw = (rc.channel_count > 17) ? rc.values[17] : UINT16_MAX;
+
+ /* RSSI has a max value of 100, and when Spektrum or S.BUS are
+ * available, the RSSI field is invalid, as they do not provide
+ * an RSSI measurement. Use an out of band magic value to signal
+ * these digital ports. XXX revise MAVLink spec to address this.
+ * One option would be to use the top bit to toggle between RSSI
+ * and input source mode.
+ *
+ * Full RSSI field: 0b 1 111 1111
+ *
+ * ^ If bit is set, RSSI encodes type + RSSI
+ *
+ * ^ These three bits encode a total of 8
+ * digital RC input types.
+ * 0: PPM, 1: SBUS, 2: Spektrum, 2: ST24
+ * ^ These four bits encode a total of
+ * 16 RSSI levels. 15 = full, 0 = no signal
+ *
+ */
+
+ /* Initialize RSSI with the special mode level flag */
+ msg.rssi = (1 << 7);
+
+ /* Set RSSI */
+ msg.rssi |= (rc.rssi <= 100) ? ((rc.rssi / 7) + 1) : 15;
+
+ switch (rc.input_source) {
+ case RC_INPUT_SOURCE_PX4FMU_PPM:
+ /* fallthrough */
+ case RC_INPUT_SOURCE_PX4IO_PPM:
+ msg.rssi |= (0 << 4);
+ break;
+ case RC_INPUT_SOURCE_PX4IO_SPEKTRUM:
+ msg.rssi |= (1 << 4);
+ break;
+ case RC_INPUT_SOURCE_PX4IO_SBUS:
+ msg.rssi |= (2 << 4);
+ break;
+ case RC_INPUT_SOURCE_PX4IO_ST24:
+ msg.rssi |= (3 << 4);
+ break;
+ }
+
+ if (rc.rc_lost) {
+ /* RSSI is by definition zero */
+ msg.rssi = 0;
+ }
+
+ _mavlink->send_message(MAVLINK_MSG_ID_RC_CHANNELS, &msg);
}
}
};
@@ -1313,37 +1786,50 @@ public:
return "MANUAL_CONTROL";
}
- static MavlinkStream *new_instance()
+ uint8_t get_id()
+ {
+ return MAVLINK_MSG_ID_MANUAL_CONTROL;
+ }
+
+ static MavlinkStream *new_instance(Mavlink *mavlink)
+ {
+ return new MavlinkStreamManualControl(mavlink);
+ }
+
+ unsigned get_size()
{
- return new MavlinkStreamManualControl();
+ return _manual_sub->is_published() ? (MAVLINK_MSG_ID_MANUAL_CONTROL_LEN + MAVLINK_NUM_NON_PAYLOAD_BYTES) : 0;
}
private:
- MavlinkOrbSubscription *manual_sub;
- uint64_t manual_time;
+ MavlinkOrbSubscription *_manual_sub;
+ uint64_t _manual_time;
+
+ /* do not allow top copying this class */
+ MavlinkStreamManualControl(MavlinkStreamManualControl &);
+ MavlinkStreamManualControl& operator = (const MavlinkStreamManualControl &);
protected:
- explicit MavlinkStreamManualControl() : MavlinkStream(),
- manual_time(0)
+ explicit MavlinkStreamManualControl(Mavlink *mavlink) : MavlinkStream(mavlink),
+ _manual_sub(_mavlink->add_orb_subscription(ORB_ID(manual_control_setpoint))),
+ _manual_time(0)
{}
- void subscribe(Mavlink *mavlink)
- {
- manual_sub = mavlink->add_orb_subscription(ORB_ID(manual_control_setpoint));
- }
-
void send(const hrt_abstime t)
{
struct manual_control_setpoint_s manual;
- if (manual_sub->update(&manual_time, &manual)) {
- mavlink_msg_manual_control_send(_channel,
- mavlink_system.sysid,
- manual.x * 1000,
- manual.y * 1000,
- manual.z * 1000,
- manual.r * 1000,
- 0);
+ if (_manual_sub->update(&_manual_time, &manual)) {
+ mavlink_manual_control_t msg;
+
+ msg.target = mavlink_system.sysid;
+ msg.x = manual.x * 1000;
+ msg.y = manual.y * 1000;
+ msg.z = manual.z * 1000;
+ msg.r = manual.r * 1000;
+ msg.buttons = 0;
+
+ _mavlink->send_message(MAVLINK_MSG_ID_MANUAL_CONTROL, &msg);
}
}
};
@@ -1362,37 +1848,52 @@ public:
return "OPTICAL_FLOW";
}
- static MavlinkStream *new_instance()
+ uint8_t get_id()
{
- return new MavlinkStreamOpticalFlow();
+ return MAVLINK_MSG_ID_OPTICAL_FLOW;
+ }
+
+ static MavlinkStream *new_instance(Mavlink *mavlink)
+ {
+ return new MavlinkStreamOpticalFlow(mavlink);
+ }
+
+ unsigned get_size()
+ {
+ return _flow_sub->is_published() ? (MAVLINK_MSG_ID_OPTICAL_FLOW_LEN + MAVLINK_NUM_NON_PAYLOAD_BYTES) : 0;
}
private:
- MavlinkOrbSubscription *flow_sub;
- uint64_t flow_time;
+ MavlinkOrbSubscription *_flow_sub;
+ uint64_t _flow_time;
+
+ /* do not allow top copying this class */
+ MavlinkStreamOpticalFlow(MavlinkStreamOpticalFlow &);
+ MavlinkStreamOpticalFlow& operator = (const MavlinkStreamOpticalFlow &);
protected:
- explicit MavlinkStreamOpticalFlow() : MavlinkStream(),
- flow_time(0)
+ explicit MavlinkStreamOpticalFlow(Mavlink *mavlink) : MavlinkStream(mavlink),
+ _flow_sub(_mavlink->add_orb_subscription(ORB_ID(optical_flow))),
+ _flow_time(0)
{}
- void subscribe(Mavlink *mavlink)
- {
- flow_sub = mavlink->add_orb_subscription(ORB_ID(optical_flow));
- }
-
void send(const hrt_abstime t)
{
struct optical_flow_s flow;
- if (flow_sub->update(&flow_time, &flow)) {
- mavlink_msg_optical_flow_send(_channel,
- flow.timestamp,
- flow.sensor_id,
- flow.flow_raw_x, flow.flow_raw_y,
- flow.flow_comp_x_m, flow.flow_comp_y_m,
- flow.quality,
- flow.ground_distance_m);
+ if (_flow_sub->update(&_flow_time, &flow)) {
+ mavlink_optical_flow_t msg;
+
+ msg.time_usec = flow.timestamp;
+ msg.sensor_id = flow.sensor_id;
+ msg.flow_x = flow.flow_raw_x;
+ msg.flow_y = flow.flow_raw_y;
+ msg.flow_comp_m_x = flow.flow_comp_x_m;
+ msg.flow_comp_m_y = flow.flow_comp_y_m;
+ msg.quality = flow.quality;
+ msg.ground_distance = flow.ground_distance_m;
+
+ _mavlink->send_message(MAVLINK_MSG_ID_OPTICAL_FLOW, &msg);
}
}
};
@@ -1410,51 +1911,69 @@ public:
return "ATTITUDE_CONTROLS";
}
- static MavlinkStream *new_instance()
+ uint8_t get_id()
{
- return new MavlinkStreamAttitudeControls();
+ return 0;
+ }
+
+ static MavlinkStream *new_instance(Mavlink *mavlink)
+ {
+ return new MavlinkStreamAttitudeControls(mavlink);
+ }
+
+ unsigned get_size()
+ {
+ return 4 * (MAVLINK_MSG_ID_NAMED_VALUE_FLOAT_LEN + MAVLINK_NUM_NON_PAYLOAD_BYTES);
}
private:
- MavlinkOrbSubscription *att_ctrl_sub;
- uint64_t att_ctrl_time;
+ MavlinkOrbSubscription *_att_ctrl_sub;
+ uint64_t _att_ctrl_time;
+
+ /* do not allow top copying this class */
+ MavlinkStreamAttitudeControls(MavlinkStreamAttitudeControls &);
+ MavlinkStreamAttitudeControls& operator = (const MavlinkStreamAttitudeControls &);
protected:
- explicit MavlinkStreamAttitudeControls() : MavlinkStream(),
- att_ctrl_time(0)
+ explicit MavlinkStreamAttitudeControls(Mavlink *mavlink) : MavlinkStream(mavlink),
+ _att_ctrl_sub(_mavlink->add_orb_subscription(ORB_ID_VEHICLE_ATTITUDE_CONTROLS)),
+ _att_ctrl_time(0)
{}
- void subscribe(Mavlink *mavlink)
- {
- att_ctrl_sub = mavlink->add_orb_subscription(ORB_ID_VEHICLE_ATTITUDE_CONTROLS);
- }
-
void send(const hrt_abstime t)
{
struct actuator_controls_s att_ctrl;
- if (att_ctrl_sub->update(&att_ctrl_time, &att_ctrl)) {
+ if (_att_ctrl_sub->update(&_att_ctrl_time, &att_ctrl)) {
/* send, add spaces so that string buffer is at least 10 chars long */
- mavlink_msg_named_value_float_send(_channel,
- att_ctrl.timestamp / 1000,
- "rll ctrl ",
- att_ctrl.control[0]);
- mavlink_msg_named_value_float_send(_channel,
- att_ctrl.timestamp / 1000,
- "ptch ctrl ",
- att_ctrl.control[1]);
- mavlink_msg_named_value_float_send(_channel,
- att_ctrl.timestamp / 1000,
- "yaw ctrl ",
- att_ctrl.control[2]);
- mavlink_msg_named_value_float_send(_channel,
- att_ctrl.timestamp / 1000,
- "thr ctrl ",
- att_ctrl.control[3]);
+ mavlink_named_value_float_t msg;
+
+ msg.time_boot_ms = att_ctrl.timestamp / 1000;
+
+ snprintf(msg.name, sizeof(msg.name), "rll ctrl");
+ msg.value = att_ctrl.control[0];
+
+ _mavlink->send_message(MAVLINK_MSG_ID_NAMED_VALUE_FLOAT, &msg);
+
+ snprintf(msg.name, sizeof(msg.name), "ptch ctrl");
+ msg.value = att_ctrl.control[1];
+
+ _mavlink->send_message(MAVLINK_MSG_ID_NAMED_VALUE_FLOAT, &msg);
+
+ snprintf(msg.name, sizeof(msg.name), "yaw ctrl");
+ msg.value = att_ctrl.control[2];
+
+ _mavlink->send_message(MAVLINK_MSG_ID_NAMED_VALUE_FLOAT, &msg);
+
+ snprintf(msg.name, sizeof(msg.name), "thr ctrl");
+ msg.value = att_ctrl.control[3];
+
+ _mavlink->send_message(MAVLINK_MSG_ID_NAMED_VALUE_FLOAT, &msg);
}
}
};
+
class MavlinkStreamNamedValueFloat : public MavlinkStream
{
public:
@@ -1468,41 +1987,54 @@ public:
return "NAMED_VALUE_FLOAT";
}
- static MavlinkStream *new_instance()
+ uint8_t get_id()
{
- return new MavlinkStreamNamedValueFloat();
+ return MAVLINK_MSG_ID_NAMED_VALUE_FLOAT;
+ }
+
+ static MavlinkStream *new_instance(Mavlink *mavlink)
+ {
+ return new MavlinkStreamNamedValueFloat(mavlink);
+ }
+
+ unsigned get_size()
+ {
+ return MAVLINK_MSG_ID_NAMED_VALUE_FLOAT_LEN + MAVLINK_NUM_NON_PAYLOAD_BYTES;
}
private:
- MavlinkOrbSubscription *debug_sub;
- uint64_t debug_time;
+ MavlinkOrbSubscription *_debug_sub;
+ uint64_t _debug_time;
+
+ /* do not allow top copying this class */
+ MavlinkStreamNamedValueFloat(MavlinkStreamNamedValueFloat &);
+ MavlinkStreamNamedValueFloat& operator = (const MavlinkStreamNamedValueFloat &);
protected:
- explicit MavlinkStreamNamedValueFloat() : MavlinkStream(),
- debug_time(0)
+ explicit MavlinkStreamNamedValueFloat(Mavlink *mavlink) : MavlinkStream(mavlink),
+ _debug_sub(_mavlink->add_orb_subscription(ORB_ID(debug_key_value))),
+ _debug_time(0)
{}
- void subscribe(Mavlink *mavlink)
- {
- debug_sub = mavlink->add_orb_subscription(ORB_ID(debug_key_value));
- }
-
void send(const hrt_abstime t)
{
struct debug_key_value_s debug;
- if (debug_sub->update(&debug_time, &debug)) {
+ if (_debug_sub->update(&_debug_time, &debug)) {
+ mavlink_named_value_float_t msg;
+
+ msg.time_boot_ms = debug.timestamp_ms;
+ memcpy(msg.name, debug.key, sizeof(msg.name));
/* enforce null termination */
- debug.key[sizeof(debug.key) - 1] = '\0';
+ msg.name[sizeof(msg.name) - 1] = '\0';
+ msg.value = debug.value;
- mavlink_msg_named_value_float_send(_channel,
- debug.timestamp_ms,
- debug.key,
- debug.value);
+ _mavlink->send_message(MAVLINK_MSG_ID_NAMED_VALUE_FLOAT, &msg);
}
}
};
+
class MavlinkStreamCameraCapture : public MavlinkStream
{
public:
@@ -1516,38 +2048,58 @@ public:
return "CAMERA_CAPTURE";
}
- static MavlinkStream *new_instance()
+ uint8_t get_id()
{
- return new MavlinkStreamCameraCapture();
+ return 0;
}
-private:
- MavlinkOrbSubscription *status_sub;
+ static MavlinkStream *new_instance(Mavlink *mavlink)
+ {
+ return new MavlinkStreamCameraCapture(mavlink);
+ }
-protected:
- void subscribe(Mavlink *mavlink)
+ unsigned get_size()
{
- status_sub = mavlink->add_orb_subscription(ORB_ID(vehicle_status));
+ return MAVLINK_MSG_ID_COMMAND_LONG_LEN + MAVLINK_NUM_NON_PAYLOAD_BYTES;
}
+private:
+ MavlinkOrbSubscription *_status_sub;
+
+ /* do not allow top copying this class */
+ MavlinkStreamCameraCapture(MavlinkStreamCameraCapture &);
+ MavlinkStreamCameraCapture& operator = (const MavlinkStreamCameraCapture &);
+
+protected:
+ explicit MavlinkStreamCameraCapture(Mavlink *mavlink) : MavlinkStream(mavlink),
+ _status_sub(_mavlink->add_orb_subscription(ORB_ID(vehicle_status)))
+ {}
+
void send(const hrt_abstime t)
{
struct vehicle_status_s status;
- (void)status_sub->update(&status);
+ (void)_status_sub->update(&status);
- if (status.arming_state == ARMING_STATE_ARMED
- || status.arming_state == ARMING_STATE_ARMED_ERROR) {
+ mavlink_command_long_t msg;
- /* send camera capture on */
- mavlink_msg_command_long_send(_channel, mavlink_system.sysid, 0, MAV_CMD_DO_CONTROL_VIDEO, 0, 0, 0, 0, 1, 0, 0, 0);
+ msg.target_system = mavlink_system.sysid;
+ msg.target_component = MAV_COMP_ID_ALL;
+ msg.command = MAV_CMD_DO_CONTROL_VIDEO;
+ msg.confirmation = 0;
+ msg.param1 = 0;
+ msg.param2 = 0;
+ msg.param3 = 0;
+ /* set camera capture ON/OFF depending on arming state */
+ msg.param4 = (status.arming_state == ARMING_STATE_ARMED || status.arming_state == ARMING_STATE_ARMED_ERROR) ? 1 : 0;
+ msg.param5 = 0;
+ msg.param6 = 0;
+ msg.param7 = 0;
- } else {
- /* send camera capture off */
- mavlink_msg_command_long_send(_channel, mavlink_system.sysid, 0, MAV_CMD_DO_CONTROL_VIDEO, 0, 0, 0, 0, 0, 0, 0, 0);
- }
+ _mavlink->send_message(MAVLINK_MSG_ID_COMMAND_LONG, &msg);
}
};
+
class MavlinkStreamDistanceSensor : public MavlinkStream
{
public:
@@ -1561,45 +2113,63 @@ public:
return "DISTANCE_SENSOR";
}
- static MavlinkStream *new_instance()
+ uint8_t get_id()
+ {
+ return MAVLINK_MSG_ID_DISTANCE_SENSOR;
+ }
+
+ static MavlinkStream *new_instance(Mavlink *mavlink)
+ {
+ return new MavlinkStreamDistanceSensor(mavlink);
+ }
+
+ unsigned get_size()
{
- return new MavlinkStreamDistanceSensor();
+ return _range_sub->is_published() ? (MAVLINK_MSG_ID_DISTANCE_SENSOR_LEN + MAVLINK_NUM_NON_PAYLOAD_BYTES) : 0;
}
private:
- MavlinkOrbSubscription *range_sub;
- uint64_t range_time;
+ MavlinkOrbSubscription *_range_sub;
+ uint64_t _range_time;
+
+ /* do not allow top copying this class */
+ MavlinkStreamDistanceSensor(MavlinkStreamDistanceSensor &);
+ MavlinkStreamDistanceSensor& operator = (const MavlinkStreamDistanceSensor &);
protected:
- explicit MavlinkStreamDistanceSensor() : MavlinkStream(),
- range_time(0)
+ explicit MavlinkStreamDistanceSensor(Mavlink *mavlink) : MavlinkStream(mavlink),
+ _range_sub(_mavlink->add_orb_subscription(ORB_ID(sensor_range_finder))),
+ _range_time(0)
{}
- void subscribe(Mavlink *mavlink)
- {
- range_sub = mavlink->add_orb_subscription(ORB_ID(sensor_range_finder));
- }
-
void send(const hrt_abstime t)
{
struct range_finder_report range;
- if (range_sub->update(&range_time, &range)) {
+ if (_range_sub->update(&_range_time, &range)) {
+
+ mavlink_distance_sensor_t msg;
- uint8_t type;
+ msg.time_boot_ms = range.timestamp / 1000;
switch (range.type) {
- case RANGE_FINDER_TYPE_LASER:
- type = MAV_DISTANCE_SENSOR_LASER;
+ case RANGE_FINDER_TYPE_LASER:
+ msg.type = MAV_DISTANCE_SENSOR_LASER;
+ break;
+
+ default:
+ msg.type = MAV_DISTANCE_SENSOR_LASER;
break;
}
- uint8_t id = 0;
- uint8_t orientation = 0;
- uint8_t covariance = 20;
+ msg.id = 0;
+ msg.orientation = 0;
+ msg.min_distance = range.minimum_distance * 100;
+ msg.max_distance = range.minimum_distance * 100;
+ msg.current_distance = range.distance * 100;
+ msg.covariance = 20;
- mavlink_msg_distance_sensor_send(_channel, range.timestamp / 1000, type, id, orientation,
- range.minimum_distance*100, range.maximum_distance*100, range.distance*100, covariance);
+ _mavlink->send_message(MAVLINK_MSG_ID_DISTANCE_SENSOR, &msg);
}
}
};
@@ -1607,6 +2177,8 @@ protected:
StreamListItem *streams_list[] = {
new StreamListItem(&MavlinkStreamHeartbeat::new_instance, &MavlinkStreamHeartbeat::get_name_static),
+ new StreamListItem(&MavlinkStreamStatustext::new_instance, &MavlinkStreamStatustext::get_name_static),
+ new StreamListItem(&MavlinkStreamCommandLong::new_instance, &MavlinkStreamCommandLong::get_name_static),
new StreamListItem(&MavlinkStreamSysStatus::new_instance, &MavlinkStreamSysStatus::get_name_static),
new StreamListItem(&MavlinkStreamHighresIMU::new_instance, &MavlinkStreamHighresIMU::get_name_static),
new StreamListItem(&MavlinkStreamAttitude::new_instance, &MavlinkStreamAttitude::get_name_static),
@@ -1615,16 +2187,16 @@ StreamListItem *streams_list[] = {
new StreamListItem(&MavlinkStreamGPSRawInt::new_instance, &MavlinkStreamGPSRawInt::get_name_static),
new StreamListItem(&MavlinkStreamGlobalPositionInt::new_instance, &MavlinkStreamGlobalPositionInt::get_name_static),
new StreamListItem(&MavlinkStreamLocalPositionNED::new_instance, &MavlinkStreamLocalPositionNED::get_name_static),
+ new StreamListItem(&MavlinkStreamViconPositionEstimate::new_instance, &MavlinkStreamViconPositionEstimate::get_name_static),
new StreamListItem(&MavlinkStreamGPSGlobalOrigin::new_instance, &MavlinkStreamGPSGlobalOrigin::get_name_static),
new StreamListItem(&MavlinkStreamServoOutputRaw<0>::new_instance, &MavlinkStreamServoOutputRaw<0>::get_name_static),
new StreamListItem(&MavlinkStreamServoOutputRaw<1>::new_instance, &MavlinkStreamServoOutputRaw<1>::get_name_static),
new StreamListItem(&MavlinkStreamServoOutputRaw<2>::new_instance, &MavlinkStreamServoOutputRaw<2>::get_name_static),
new StreamListItem(&MavlinkStreamServoOutputRaw<3>::new_instance, &MavlinkStreamServoOutputRaw<3>::get_name_static),
new StreamListItem(&MavlinkStreamHILControls::new_instance, &MavlinkStreamHILControls::get_name_static),
- new StreamListItem(&MavlinkStreamGlobalPositionSetpointInt::new_instance, &MavlinkStreamGlobalPositionSetpointInt::get_name_static),
+ new StreamListItem(&MavlinkStreamPositionTargetGlobalInt::new_instance, &MavlinkStreamPositionTargetGlobalInt::get_name_static),
new StreamListItem(&MavlinkStreamLocalPositionSetpoint::new_instance, &MavlinkStreamLocalPositionSetpoint::get_name_static),
- new StreamListItem(&MavlinkStreamRollPitchYawThrustSetpoint::new_instance, &MavlinkStreamRollPitchYawThrustSetpoint::get_name_static),
- new StreamListItem(&MavlinkStreamRollPitchYawRatesThrustSetpoint::new_instance, &MavlinkStreamRollPitchYawRatesThrustSetpoint::get_name_static),
+ new StreamListItem(&MavlinkStreamAttitudeTarget::new_instance, &MavlinkStreamAttitudeTarget::get_name_static),
new StreamListItem(&MavlinkStreamRCChannelsRaw::new_instance, &MavlinkStreamRCChannelsRaw::get_name_static),
new StreamListItem(&MavlinkStreamManualControl::new_instance, &MavlinkStreamManualControl::get_name_static),
new StreamListItem(&MavlinkStreamOpticalFlow::new_instance, &MavlinkStreamOpticalFlow::get_name_static),
@@ -1632,6 +2204,5 @@ StreamListItem *streams_list[] = {
new StreamListItem(&MavlinkStreamNamedValueFloat::new_instance, &MavlinkStreamNamedValueFloat::get_name_static),
new StreamListItem(&MavlinkStreamCameraCapture::new_instance, &MavlinkStreamCameraCapture::get_name_static),
new StreamListItem(&MavlinkStreamDistanceSensor::new_instance, &MavlinkStreamDistanceSensor::get_name_static),
- new StreamListItem(&MavlinkStreamViconPositionEstimate::new_instance, &MavlinkStreamViconPositionEstimate::get_name_static),
nullptr
};
diff --git a/src/modules/mavlink/mavlink_messages.h b/src/modules/mavlink/mavlink_messages.h
index ee64d0e42..7e4416609 100644
--- a/src/modules/mavlink/mavlink_messages.h
+++ b/src/modules/mavlink/mavlink_messages.h
@@ -46,10 +46,10 @@
class StreamListItem {
public:
- MavlinkStream* (*new_instance)();
+ MavlinkStream* (*new_instance)(Mavlink *mavlink);
const char* (*get_name)();
- StreamListItem(MavlinkStream* (*inst)(), const char* (*name)()) :
+ StreamListItem(MavlinkStream* (*inst)(Mavlink *mavlink), const char* (*name)()) :
new_instance(inst),
get_name(name) {};
diff --git a/src/modules/mavlink/mavlink_mission.cpp b/src/modules/mavlink/mavlink_mission.cpp
new file mode 100644
index 000000000..a3c127cdc
--- /dev/null
+++ b/src/modules/mavlink/mavlink_mission.cpp
@@ -0,0 +1,834 @@
+/****************************************************************************
+ *
+ * Copyright (c) 2012-2014 PX4 Development Team. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/**
+ * @file mavlink_mission.cpp
+ * MAVLink mission manager implementation.
+ *
+ * @author Lorenz Meier <lm@inf.ethz.ch>
+ * @author Julian Oes <joes@student.ethz.ch>
+ * @author Anton Babushkin <anton.babushkin@me.com>
+ */
+
+#include "mavlink_mission.h"
+#include "mavlink_main.h"
+
+#include <math.h>
+#include <lib/geo/geo.h>
+#include <systemlib/err.h>
+#include <drivers/drv_hrt.h>
+
+#include <dataman/dataman.h>
+#include <uORB/topics/mission.h>
+#include <uORB/topics/mission_result.h>
+
+/* oddly, ERROR is not defined for c++ */
+#ifdef ERROR
+# undef ERROR
+#endif
+static const int ERROR = -1;
+
+#define CHECK_SYSID_COMPID_MISSION(_msg) (_msg.target_system == mavlink_system.sysid && \
+ ((_msg.target_component == mavlink_system.compid) || \
+ (_msg.target_component == MAV_COMP_ID_MISSIONPLANNER) || \
+ (_msg.target_component == MAV_COMP_ID_ALL)))
+
+MavlinkMissionManager::MavlinkMissionManager(Mavlink *mavlink) : MavlinkStream(mavlink),
+ _state(MAVLINK_WPM_STATE_IDLE),
+ _time_last_recv(0),
+ _time_last_sent(0),
+ _action_timeout(MAVLINK_MISSION_PROTOCOL_TIMEOUT_DEFAULT),
+ _retry_timeout(MAVLINK_MISSION_RETRY_TIMEOUT_DEFAULT),
+ _max_count(DM_KEY_WAYPOINTS_OFFBOARD_0_MAX),
+ _dataman_id(0),
+ _count(0),
+ _current_seq(0),
+ _transfer_dataman_id(0),
+ _transfer_count(0),
+ _transfer_seq(0),
+ _transfer_current_seq(0),
+ _transfer_partner_sysid(0),
+ _transfer_partner_compid(0),
+ _offboard_mission_sub(-1),
+ _mission_result_sub(-1),
+ _offboard_mission_pub(-1),
+ _slow_rate_limiter(_interval / 10.0f),
+ _verbose(false)
+{
+ _offboard_mission_sub = orb_subscribe(ORB_ID(offboard_mission));
+ _mission_result_sub = orb_subscribe(ORB_ID(mission_result));
+
+ init_offboard_mission();
+}
+
+MavlinkMissionManager::~MavlinkMissionManager()
+{
+ close(_offboard_mission_pub);
+ close(_mission_result_sub);
+}
+
+unsigned
+MavlinkMissionManager::get_size()
+{
+ if (_state == MAVLINK_WPM_STATE_SENDLIST) {
+ return MAVLINK_MSG_ID_MISSION_ITEM_LEN + MAVLINK_NUM_NON_PAYLOAD_BYTES;
+
+ } else if (_state == MAVLINK_WPM_STATE_GETLIST) {
+ return MAVLINK_MSG_ID_MISSION_REQUEST + MAVLINK_NUM_NON_PAYLOAD_BYTES;
+
+ } else {
+ return 0;
+ }
+}
+
+void
+MavlinkMissionManager::init_offboard_mission()
+{
+ mission_s mission_state;
+ if (dm_read(DM_KEY_MISSION_STATE, 0, &mission_state, sizeof(mission_s)) == sizeof(mission_s)) {
+ _dataman_id = mission_state.dataman_id;
+ _count = mission_state.count;
+ _current_seq = mission_state.current_seq;
+
+ warnx("offboard mission init: dataman_id=%d, count=%u, current_seq=%d", _dataman_id, _count, _current_seq);
+
+ } else {
+ _dataman_id = 0;
+ _count = 0;
+ _current_seq = 0;
+ warnx("offboard mission init: ERROR, reading mission state failed");
+ }
+}
+
+/**
+ * Write new mission state to dataman and publish offboard_mission topic to notify navigator about changes.
+ */
+int
+MavlinkMissionManager::update_active_mission(int dataman_id, unsigned count, int seq)
+{
+ struct mission_s mission;
+
+ mission.dataman_id = dataman_id;
+ mission.count = count;
+ mission.current_seq = seq;
+
+ /* update mission state in dataman */
+ int res = dm_write(DM_KEY_MISSION_STATE, 0, DM_PERSIST_POWER_ON_RESET, &mission, sizeof(mission_s));
+
+ if (res == sizeof(mission_s)) {
+ /* update active mission state */
+ _dataman_id = dataman_id;
+ _count = count;
+ _current_seq = seq;
+
+ /* mission state saved successfully, publish offboard_mission topic */
+ if (_offboard_mission_pub < 0) {
+ _offboard_mission_pub = orb_advertise(ORB_ID(offboard_mission), &mission);
+
+ } else {
+ orb_publish(ORB_ID(offboard_mission), _offboard_mission_pub, &mission);
+ }
+
+ return OK;
+
+ } else {
+ warnx("ERROR: can't save mission state");
+ _mavlink->send_statustext(MAV_SEVERITY_CRITICAL, "ERROR: can't save mission state");
+
+ return ERROR;
+ }
+}
+
+void
+MavlinkMissionManager::send_mission_ack(uint8_t sysid, uint8_t compid, uint8_t type)
+{
+ mavlink_mission_ack_t wpa;
+
+ wpa.target_system = sysid;
+ wpa.target_component = compid;
+ wpa.type = type;
+
+ _mavlink->send_message(MAVLINK_MSG_ID_MISSION_ACK, &wpa);
+
+ if (_verbose) { warnx("WPM: Send MISSION_ACK type %u to ID %u", wpa.type, wpa.target_system); }
+}
+
+
+void
+MavlinkMissionManager::send_mission_current(uint16_t seq)
+{
+ if (seq < _count) {
+ mavlink_mission_current_t wpc;
+
+ wpc.seq = seq;
+
+ _mavlink->send_message(MAVLINK_MSG_ID_MISSION_CURRENT, &wpc);
+
+ } else if (seq == 0 && _count == 0) {
+ /* don't broadcast if no WPs */
+
+ } else {
+ if (_verbose) { warnx("WPM: Send MISSION_CURRENT ERROR: seq %u out of bounds", seq); }
+
+ _mavlink->send_statustext_critical("ERROR: wp index out of bounds");
+ }
+}
+
+
+void
+MavlinkMissionManager::send_mission_count(uint8_t sysid, uint8_t compid, uint16_t count)
+{
+ _time_last_sent = hrt_absolute_time();
+
+ mavlink_mission_count_t wpc;
+
+ wpc.target_system = sysid;
+ wpc.target_component = compid;
+ wpc.count = _count;
+
+ _mavlink->send_message(MAVLINK_MSG_ID_MISSION_COUNT, &wpc);
+
+ if (_verbose) { warnx("WPM: Send MISSION_COUNT %u to ID %u", wpc.count, wpc.target_system); }
+}
+
+
+void
+MavlinkMissionManager::send_mission_item(uint8_t sysid, uint8_t compid, uint16_t seq)
+{
+ dm_item_t dm_item = DM_KEY_WAYPOINTS_OFFBOARD(_dataman_id);
+ struct mission_item_s mission_item;
+
+ if (dm_read(dm_item, seq, &mission_item, sizeof(struct mission_item_s)) == sizeof(struct mission_item_s)) {
+ _time_last_sent = hrt_absolute_time();
+
+ /* create mission_item_s from mavlink_mission_item_t */
+ mavlink_mission_item_t wp;
+ format_mavlink_mission_item(&mission_item, &wp);
+
+ wp.target_system = sysid;
+ wp.target_component = compid;
+ wp.seq = seq;
+ wp.current = (_current_seq == seq) ? 1 : 0;
+
+ _mavlink->send_message(MAVLINK_MSG_ID_MISSION_ITEM, &wp);
+
+ if (_verbose) { warnx("WPM: Send MISSION_ITEM seq %u to ID %u", wp.seq, wp.target_system); }
+
+ } else {
+ send_mission_ack(_transfer_partner_sysid, _transfer_partner_compid, MAV_MISSION_ERROR);
+ _mavlink->send_statustext_critical("Unable to read from micro SD");
+
+ if (_verbose) { warnx("WPM: Send MISSION_ITEM ERROR: could not read seq %u from dataman ID %i", seq, _dataman_id); }
+ }
+}
+
+
+void
+MavlinkMissionManager::send_mission_request(uint8_t sysid, uint8_t compid, uint16_t seq)
+{
+ if (seq < _max_count) {
+ _time_last_sent = hrt_absolute_time();
+
+ mavlink_mission_request_t wpr;
+ wpr.target_system = sysid;
+ wpr.target_component = compid;
+ wpr.seq = seq;
+
+ _mavlink->send_message(MAVLINK_MSG_ID_MISSION_REQUEST, &wpr);
+
+ if (_verbose) { warnx("WPM: Send MISSION_REQUEST seq %u to ID %u", wpr.seq, wpr.target_system); }
+
+ } else {
+ _mavlink->send_statustext_critical("ERROR: Waypoint index exceeds list capacity");
+
+ if (_verbose) { warnx("WPM: Send MISSION_REQUEST ERROR: seq %u exceeds list capacity", seq); }
+ }
+}
+
+
+void
+MavlinkMissionManager::send_mission_item_reached(uint16_t seq)
+{
+ mavlink_mission_item_reached_t wp_reached;
+
+ wp_reached.seq = seq;
+
+ _mavlink->send_message(MAVLINK_MSG_ID_MISSION_ITEM_REACHED, &wp_reached);
+
+ if (_verbose) { warnx("WPM: Send MISSION_ITEM_REACHED reached_seq %u", wp_reached.seq); }
+}
+
+
+void
+MavlinkMissionManager::send(const hrt_abstime now)
+{
+ /* update interval for slow rate limiter */
+ _slow_rate_limiter.set_interval(_interval * 10 / _mavlink->get_rate_mult());
+
+ bool updated = false;
+ orb_check(_mission_result_sub, &updated);
+
+ if (updated) {
+ mission_result_s mission_result;
+ orb_copy(ORB_ID(mission_result), _mission_result_sub, &mission_result);
+
+ _current_seq = mission_result.seq_current;
+
+ if (_verbose) { warnx("WPM: got mission result, new current_seq: %d", _current_seq); }
+
+ if (mission_result.reached) {
+ send_mission_item_reached((uint16_t)mission_result.seq_reached);
+ }
+
+ send_mission_current(_current_seq);
+
+ } else {
+ if (_slow_rate_limiter.check(now)) {
+ send_mission_current(_current_seq);
+ }
+ }
+
+ /* check for timed-out operations */
+ if (_state != MAVLINK_WPM_STATE_IDLE && hrt_elapsed_time(&_time_last_recv) > _action_timeout) {
+ _mavlink->send_statustext_critical("Operation timeout");
+
+ if (_verbose) { warnx("WPM: Last operation (state=%u) timed out, changing state to MAVLINK_WPM_STATE_IDLE", _state); }
+
+ _state = MAVLINK_WPM_STATE_IDLE;
+
+ } else if (_state == MAVLINK_WPM_STATE_GETLIST && hrt_elapsed_time(&_time_last_sent) > _retry_timeout) {
+ /* try to request item again after timeout */
+ send_mission_request(_transfer_partner_sysid, _transfer_partner_compid, _transfer_seq);
+
+ } else if (_state == MAVLINK_WPM_STATE_SENDLIST && hrt_elapsed_time(&_time_last_sent) > _retry_timeout) {
+ if (_transfer_seq == 0) {
+ /* try to send items count again after timeout */
+ send_mission_count(_transfer_partner_sysid, _transfer_partner_compid, _transfer_count);
+
+ } else {
+ /* try to send item again after timeout */
+ send_mission_item(_transfer_partner_sysid, _transfer_partner_compid, _transfer_seq - 1);
+ }
+ }
+}
+
+
+void
+MavlinkMissionManager::handle_message(const mavlink_message_t *msg)
+{
+ switch (msg->msgid) {
+ case MAVLINK_MSG_ID_MISSION_ACK:
+ handle_mission_ack(msg);
+ break;
+
+ case MAVLINK_MSG_ID_MISSION_SET_CURRENT:
+ handle_mission_set_current(msg);
+ break;
+
+ case MAVLINK_MSG_ID_MISSION_REQUEST_LIST:
+ handle_mission_request_list(msg);
+ break;
+
+ case MAVLINK_MSG_ID_MISSION_REQUEST:
+ handle_mission_request(msg);
+ break;
+
+ case MAVLINK_MSG_ID_MISSION_COUNT:
+ handle_mission_count(msg);
+ break;
+
+ case MAVLINK_MSG_ID_MISSION_ITEM:
+ handle_mission_item(msg);
+ break;
+
+ case MAVLINK_MSG_ID_MISSION_CLEAR_ALL:
+ handle_mission_clear_all(msg);
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+void
+MavlinkMissionManager::handle_mission_ack(const mavlink_message_t *msg)
+{
+ mavlink_mission_ack_t wpa;
+ mavlink_msg_mission_ack_decode(msg, &wpa);
+
+ if (CHECK_SYSID_COMPID_MISSION(wpa)) {
+ if ((msg->sysid == _transfer_partner_sysid && msg->compid == _transfer_partner_compid)) {
+ if (_state == MAVLINK_WPM_STATE_SENDLIST) {
+ _time_last_recv = hrt_absolute_time();
+
+ if (_transfer_seq == _count) {
+ if (_verbose) { warnx("WPM: MISSION_ACK OK all items sent, switch to state IDLE"); }
+
+ } else {
+ _mavlink->send_statustext_critical("WPM: ERR: not all items sent -> IDLE");
+
+ if (_verbose) { warnx("WPM: MISSION_ACK ERROR: not all items sent, switch to state IDLE anyway"); }
+ }
+
+ _state = MAVLINK_WPM_STATE_IDLE;
+ }
+
+ } else {
+ _mavlink->send_statustext_critical("REJ. WP CMD: partner id mismatch");
+
+ if (_verbose) { warnx("WPM: MISSION_ACK ERROR: rejected, partner ID mismatch"); }
+ }
+ }
+}
+
+
+void
+MavlinkMissionManager::handle_mission_set_current(const mavlink_message_t *msg)
+{
+ mavlink_mission_set_current_t wpc;
+ mavlink_msg_mission_set_current_decode(msg, &wpc);
+
+ if (CHECK_SYSID_COMPID_MISSION(wpc)) {
+ if (_state == MAVLINK_WPM_STATE_IDLE) {
+ _time_last_recv = hrt_absolute_time();
+
+ if (wpc.seq < _count) {
+ if (update_active_mission(_dataman_id, _count, wpc.seq) == OK) {
+ if (_verbose) { warnx("WPM: MISSION_SET_CURRENT seq=%d OK", wpc.seq); }
+
+ } else {
+ if (_verbose) { warnx("WPM: MISSION_SET_CURRENT seq=%d ERROR", wpc.seq); }
+
+ _mavlink->send_statustext_critical("WPM: WP CURR CMD: Error setting ID");
+ }
+
+ } else {
+ if (_verbose) { warnx("WPM: MISSION_SET_CURRENT seq=%d ERROR: not in list", wpc.seq); }
+
+ _mavlink->send_statustext_critical("WPM: WP CURR CMD: Not in list");
+ }
+
+ } else {
+ if (_verbose) { warnx("WPM: MISSION_SET_CURRENT ERROR: busy"); }
+
+ _mavlink->send_statustext_critical("WPM: IGN WP CURR CMD: Busy");
+ }
+ }
+}
+
+
+void
+MavlinkMissionManager::handle_mission_request_list(const mavlink_message_t *msg)
+{
+ mavlink_mission_request_list_t wprl;
+ mavlink_msg_mission_request_list_decode(msg, &wprl);
+
+ if (CHECK_SYSID_COMPID_MISSION(wprl)) {
+ if (_state == MAVLINK_WPM_STATE_IDLE || _state == MAVLINK_WPM_STATE_SENDLIST) {
+ _time_last_recv = hrt_absolute_time();
+
+ if (_count > 0) {
+ _state = MAVLINK_WPM_STATE_SENDLIST;
+ _transfer_seq = 0;
+ _transfer_count = _count;
+ _transfer_partner_sysid = msg->sysid;
+ _transfer_partner_compid = msg->compid;
+
+ if (_verbose) { warnx("WPM: MISSION_REQUEST_LIST OK, %u mission items to send", _transfer_count); }
+
+ } else {
+ if (_verbose) { warnx("WPM: MISSION_REQUEST_LIST OK nothing to send, mission is empty"); }
+
+ _mavlink->send_statustext_info("WPM: mission is empty");
+ }
+
+ send_mission_count(msg->sysid, msg->compid, _count);
+
+ } else {
+ if (_verbose) { warnx("WPM: MISSION_REQUEST_LIST ERROR: busy"); }
+
+ _mavlink->send_statustext_critical("IGN REQUEST LIST: Busy");
+ }
+ }
+}
+
+
+void
+MavlinkMissionManager::handle_mission_request(const mavlink_message_t *msg)
+{
+ mavlink_mission_request_t wpr;
+ mavlink_msg_mission_request_decode(msg, &wpr);
+
+ if (CHECK_SYSID_COMPID_MISSION(wpr)) {
+ if (msg->sysid == _transfer_partner_sysid && msg->compid == _transfer_partner_compid) {
+ if (_state == MAVLINK_WPM_STATE_SENDLIST) {
+ _time_last_recv = hrt_absolute_time();
+
+ /* _transfer_seq contains sequence of expected request */
+ if (wpr.seq == _transfer_seq && _transfer_seq < _transfer_count) {
+ if (_verbose) { warnx("WPM: MISSION_ITEM_REQUEST seq %u from ID %u", wpr.seq, msg->sysid); }
+
+ _transfer_seq++;
+
+ } else if (wpr.seq == _transfer_seq - 1) {
+ if (_verbose) { warnx("WPM: MISSION_ITEM_REQUEST seq %u from ID %u (again)", wpr.seq, msg->sysid); }
+
+ } else {
+ if (_transfer_seq > 0 && _transfer_seq < _transfer_count) {
+ if (_verbose) { warnx("WPM: MISSION_ITEM_REQUEST ERROR: seq %u from ID %u unexpected, must be %i or %i", wpr.seq, msg->sysid, _transfer_seq - 1, _transfer_seq); }
+
+ } else if (_transfer_seq <= 0) {
+ if (_verbose) { warnx("WPM: MISSION_ITEM_REQUEST ERROR: seq %u from ID %u unexpected, must be %i", wpr.seq, msg->sysid, _transfer_seq); }
+
+ } else {
+ if (_verbose) { warnx("WPM: MISSION_ITEM_REQUEST ERROR: seq %u from ID %u unexpected, must be %i", wpr.seq, msg->sysid, _transfer_seq - 1); }
+ }
+
+ _state = MAVLINK_WPM_STATE_IDLE;
+
+ send_mission_ack(_transfer_partner_sysid, _transfer_partner_compid, MAV_MISSION_ERROR);
+ _mavlink->send_statustext_critical("WPM: REJ. CMD: Req. WP was unexpected");
+ return;
+ }
+
+ /* double check bounds in case of items count changed */
+ if (wpr.seq < _count) {
+ send_mission_item(_transfer_partner_sysid, _transfer_partner_compid, wpr.seq);
+
+ } else {
+ if (_verbose) { warnx("WPM: MISSION_ITEM_REQUEST ERROR: seq %u out of bound [%u, %u]", (unsigned)wpr.seq, (unsigned)wpr.seq, (unsigned)_count - 1); }
+
+ _state = MAVLINK_WPM_STATE_IDLE;
+
+ send_mission_ack(_transfer_partner_sysid, _transfer_partner_compid, MAV_MISSION_ERROR);
+ _mavlink->send_statustext_critical("WPM: REJ. CMD: Req. WP was unexpected");
+ }
+
+ } else if (_state == MAVLINK_WPM_STATE_IDLE) {
+ if (_verbose) { warnx("WPM: MISSION_ITEM_REQUEST ERROR: no transfer"); }
+
+ _mavlink->send_statustext_critical("IGN MISSION_ITEM_REQUEST: No active transfer");
+
+ } else {
+ if (_verbose) { warnx("WPM: MISSION_ITEM_REQUEST ERROR: busy (state %d).", _state); }
+
+ _mavlink->send_statustext_critical("WPM: REJ. CMD: Busy");
+ }
+
+ } else {
+ _mavlink->send_statustext_critical("WPM: REJ. CMD: partner id mismatch");
+
+ if (_verbose) { warnx("WPM: MISSION_ITEM_REQUEST ERROR: rejected, partner ID mismatch"); }
+ }
+ }
+}
+
+
+void
+MavlinkMissionManager::handle_mission_count(const mavlink_message_t *msg)
+{
+ mavlink_mission_count_t wpc;
+ mavlink_msg_mission_count_decode(msg, &wpc);
+
+ if (CHECK_SYSID_COMPID_MISSION(wpc)) {
+ if (_state == MAVLINK_WPM_STATE_IDLE) {
+ _time_last_recv = hrt_absolute_time();
+
+ if (wpc.count > _max_count) {
+ if (_verbose) { warnx("WPM: MISSION_COUNT ERROR: too many waypoints (%d), supported: %d", wpc.count, _max_count); }
+
+ send_mission_ack(_transfer_partner_sysid, _transfer_partner_compid, MAV_MISSION_NO_SPACE);
+ return;
+ }
+
+ if (wpc.count == 0) {
+ if (_verbose) { warnx("WPM: MISSION_COUNT 0, clearing waypoints list and staying in state MAVLINK_WPM_STATE_IDLE"); }
+
+ /* alternate dataman ID anyway to let navigator know about changes */
+ update_active_mission(_dataman_id == 0 ? 1 : 0, 0, 0);
+ _mavlink->send_statustext_info("WPM: COUNT 0: CLEAR MISSION");
+
+ // TODO send ACK?
+ return;
+ }
+
+ if (_verbose) { warnx("WPM: MISSION_COUNT %u from ID %u, changing state to MAVLINK_WPM_STATE_GETLIST", wpc.count, msg->sysid); }
+
+ _state = MAVLINK_WPM_STATE_GETLIST;
+ _transfer_seq = 0;
+ _transfer_partner_sysid = msg->sysid;
+ _transfer_partner_compid = msg->compid;
+ _transfer_count = wpc.count;
+ _transfer_dataman_id = _dataman_id == 0 ? 1 : 0; // use inactive storage for transmission
+ _transfer_current_seq = -1;
+
+ } else if (_state == MAVLINK_WPM_STATE_GETLIST) {
+ _time_last_recv = hrt_absolute_time();
+
+ if (_transfer_seq == 0) {
+ /* looks like our MISSION_REQUEST was lost, try again */
+ if (_verbose) { warnx("WPM: MISSION_COUNT %u from ID %u (again)", wpc.count, msg->sysid); }
+
+ _mavlink->send_statustext_info("WP CMD OK TRY AGAIN");
+
+ } else {
+ if (_verbose) { warnx("WPM: MISSION_COUNT ERROR: busy, already receiving seq %u", _transfer_seq); }
+
+ _mavlink->send_statustext_critical("WPM: REJ. CMD: Busy");
+ return;
+ }
+
+ } else {
+ if (_verbose) { warnx("WPM: MISSION_COUNT ERROR: busy, state %i", _state); }
+
+ _mavlink->send_statustext_critical("WPM: IGN MISSION_COUNT: Busy");
+ return;
+ }
+
+ send_mission_request(_transfer_partner_sysid, _transfer_partner_compid, _transfer_seq);
+ }
+}
+
+
+void
+MavlinkMissionManager::handle_mission_item(const mavlink_message_t *msg)
+{
+ mavlink_mission_item_t wp;
+ mavlink_msg_mission_item_decode(msg, &wp);
+
+ if (CHECK_SYSID_COMPID_MISSION(wp)) {
+ if (_state == MAVLINK_WPM_STATE_GETLIST) {
+ _time_last_recv = hrt_absolute_time();
+
+ if (wp.seq != _transfer_seq) {
+ if (_verbose) { warnx("WPM: MISSION_ITEM ERROR: seq %u was not the expected %u", wp.seq, _transfer_seq); }
+
+ /* don't send request here, it will be performed in eventloop after timeout */
+ return;
+ }
+
+ } else if (_state == MAVLINK_WPM_STATE_IDLE) {
+ if (_verbose) { warnx("WPM: MISSION_ITEM ERROR: no transfer"); }
+
+ _mavlink->send_statustext_critical("IGN MISSION_ITEM: No transfer");
+ return;
+
+ } else {
+ if (_verbose) { warnx("WPM: MISSION_ITEM ERROR: busy, state %i", _state); }
+
+ _mavlink->send_statustext_critical("IGN MISSION_ITEM: Busy");
+ return;
+ }
+
+ struct mission_item_s mission_item;
+ int ret = parse_mavlink_mission_item(&wp, &mission_item);
+
+ if (ret != OK) {
+ if (_verbose) { warnx("WPM: MISSION_ITEM ERROR: seq %u invalid item", wp.seq); }
+
+ _mavlink->send_statustext_critical("IGN MISSION_ITEM: Busy");
+
+ send_mission_ack(_transfer_partner_sysid, _transfer_partner_compid, ret);
+ _state = MAVLINK_WPM_STATE_IDLE;
+ return;
+ }
+
+ dm_item_t dm_item = DM_KEY_WAYPOINTS_OFFBOARD(_transfer_dataman_id);
+
+ if (dm_write(dm_item, wp.seq, DM_PERSIST_POWER_ON_RESET, &mission_item, sizeof(struct mission_item_s)) != sizeof(struct mission_item_s)) {
+ if (_verbose) { warnx("WPM: MISSION_ITEM ERROR: error writing seq %u to dataman ID %i", wp.seq, _transfer_dataman_id); }
+
+ send_mission_ack(_transfer_partner_sysid, _transfer_partner_compid, MAV_MISSION_ERROR);
+ _mavlink->send_statustext_critical("Unable to write on micro SD");
+ _state = MAVLINK_WPM_STATE_IDLE;
+ return;
+ }
+
+ /* waypoint marked as current */
+ if (wp.current) {
+ _transfer_current_seq = wp.seq;
+ }
+
+ if (_verbose) { warnx("WPM: MISSION_ITEM seq %u received", wp.seq); }
+
+ _transfer_seq = wp.seq + 1;
+
+ if (_transfer_seq == _transfer_count) {
+ /* got all new mission items successfully */
+ if (_verbose) { warnx("WPM: MISSION_ITEM got all %u items, current_seq=%u, changing state to MAVLINK_WPM_STATE_IDLE", _transfer_count, _transfer_current_seq); }
+
+ _mavlink->send_statustext_info("WPM: Transfer complete.");
+
+ _state = MAVLINK_WPM_STATE_IDLE;
+
+ if (update_active_mission(_transfer_dataman_id, _transfer_count, _transfer_current_seq) == OK) {
+ send_mission_ack(_transfer_partner_sysid, _transfer_partner_compid, MAV_MISSION_ACCEPTED);
+
+ } else {
+ send_mission_ack(_transfer_partner_sysid, _transfer_partner_compid, MAV_MISSION_ERROR);
+ }
+
+ } else {
+ /* request next item */
+ send_mission_request(_transfer_partner_sysid, _transfer_partner_compid, _transfer_seq);
+ }
+ }
+}
+
+
+void
+MavlinkMissionManager::handle_mission_clear_all(const mavlink_message_t *msg)
+{
+ mavlink_mission_clear_all_t wpca;
+ mavlink_msg_mission_clear_all_decode(msg, &wpca);
+
+ if (CHECK_SYSID_COMPID_MISSION(wpca)) {
+
+ if (_state == MAVLINK_WPM_STATE_IDLE) {
+ /* don't touch mission items storage itself, but only items count in mission state */
+ _time_last_recv = hrt_absolute_time();
+
+ if (update_active_mission(_dataman_id == 0 ? 1 : 0, 0, 0) == OK) {
+ if (_verbose) { warnx("WPM: CLEAR_ALL OK"); }
+ send_mission_ack(_transfer_partner_sysid, _transfer_partner_compid, MAV_MISSION_ACCEPTED);
+
+ } else {
+ send_mission_ack(_transfer_partner_sysid, _transfer_partner_compid, MAV_MISSION_ERROR);
+ }
+
+ } else {
+ _mavlink->send_statustext_critical("WPM: IGN CLEAR CMD: Busy");
+
+ if (_verbose) { warnx("WPM: CLEAR_ALL IGNORED: busy"); }
+ }
+ }
+}
+
+int
+MavlinkMissionManager::parse_mavlink_mission_item(const mavlink_mission_item_t *mavlink_mission_item, struct mission_item_s *mission_item)
+{
+ /* only support global waypoints for now */
+ switch (mavlink_mission_item->frame) {
+ case MAV_FRAME_GLOBAL:
+ mission_item->lat = (double)mavlink_mission_item->x;
+ mission_item->lon = (double)mavlink_mission_item->y;
+ mission_item->altitude = mavlink_mission_item->z;
+ mission_item->altitude_is_relative = false;
+ break;
+
+ case MAV_FRAME_GLOBAL_RELATIVE_ALT:
+ mission_item->lat = (double)mavlink_mission_item->x;
+ mission_item->lon = (double)mavlink_mission_item->y;
+ mission_item->altitude = mavlink_mission_item->z;
+ mission_item->altitude_is_relative = true;
+ break;
+
+ case MAV_FRAME_LOCAL_NED:
+ case MAV_FRAME_LOCAL_ENU:
+ return MAV_MISSION_UNSUPPORTED_FRAME;
+
+ case MAV_FRAME_MISSION:
+ default:
+ return MAV_MISSION_ERROR;
+ }
+
+ switch (mavlink_mission_item->command) {
+ case MAV_CMD_NAV_TAKEOFF:
+ mission_item->pitch_min = mavlink_mission_item->param1;
+ break;
+ case MAV_CMD_DO_JUMP:
+ mission_item->do_jump_mission_index = mavlink_mission_item->param1;
+ mission_item->do_jump_current_count = 0;
+ mission_item->do_jump_repeat_count = mavlink_mission_item->param2;
+ break;
+ default:
+ mission_item->acceptance_radius = mavlink_mission_item->param2;
+ mission_item->time_inside = mavlink_mission_item->param1;
+ break;
+ }
+
+ mission_item->yaw = _wrap_pi(mavlink_mission_item->param4 * M_DEG_TO_RAD_F);
+ mission_item->loiter_radius = fabsf(mavlink_mission_item->param3);
+ mission_item->loiter_direction = (mavlink_mission_item->param3 > 0) ? 1 : -1; /* 1 if positive CW, -1 if negative CCW */
+ mission_item->nav_cmd = (NAV_CMD)mavlink_mission_item->command;
+
+ mission_item->autocontinue = mavlink_mission_item->autocontinue;
+ // mission_item->index = mavlink_mission_item->seq;
+ mission_item->origin = ORIGIN_MAVLINK;
+
+ /* reset DO_JUMP count */
+ mission_item->do_jump_current_count = 0;
+
+ return MAV_MISSION_ACCEPTED;
+}
+
+
+int
+MavlinkMissionManager::format_mavlink_mission_item(const struct mission_item_s *mission_item, mavlink_mission_item_t *mavlink_mission_item)
+{
+ if (mission_item->altitude_is_relative) {
+ mavlink_mission_item->frame = MAV_FRAME_GLOBAL_RELATIVE_ALT;
+
+ } else {
+ mavlink_mission_item->frame = MAV_FRAME_GLOBAL;
+ }
+
+ switch (mission_item->nav_cmd) {
+ case NAV_CMD_TAKEOFF:
+ mavlink_mission_item->param1 = mission_item->pitch_min;
+ break;
+
+ case NAV_CMD_DO_JUMP:
+ mavlink_mission_item->param1 = mission_item->do_jump_mission_index;
+ mavlink_mission_item->param2 = mission_item->do_jump_repeat_count;
+ break;
+
+ default:
+ mavlink_mission_item->param2 = mission_item->acceptance_radius;
+ mavlink_mission_item->param1 = mission_item->time_inside;
+ break;
+ }
+
+ mavlink_mission_item->x = (float)mission_item->lat;
+ mavlink_mission_item->y = (float)mission_item->lon;
+ mavlink_mission_item->z = mission_item->altitude;
+
+ mavlink_mission_item->param4 = mission_item->yaw * M_RAD_TO_DEG_F;
+ mavlink_mission_item->param3 = mission_item->loiter_radius * (float)mission_item->loiter_direction;
+ mavlink_mission_item->command = mission_item->nav_cmd;
+ mavlink_mission_item->autocontinue = mission_item->autocontinue;
+ // mavlink_mission_item->seq = mission_item->index;
+
+ return OK;
+}
diff --git a/src/modules/mavlink/mavlink_mission.h b/src/modules/mavlink/mavlink_mission.h
new file mode 100644
index 000000000..a7bb94c22
--- /dev/null
+++ b/src/modules/mavlink/mavlink_mission.h
@@ -0,0 +1,196 @@
+/****************************************************************************
+ *
+ * Copyright (c) 2012-2014 PX4 Development Team. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/**
+ * @file mavlink_mission.h
+ * MAVLink mission manager interface definition.
+ *
+ * @author Lorenz Meier <lm@inf.ethz.ch>
+ * @author Julian Oes <joes@student.ethz.ch>
+ * @author Anton Babushkin <anton.babushkin@me.com>
+ */
+
+#pragma once
+
+#include <uORB/uORB.h>
+
+#include "mavlink_bridge_header.h"
+#include "mavlink_rate_limiter.h"
+#include "mavlink_stream.h"
+
+enum MAVLINK_WPM_STATES {
+ MAVLINK_WPM_STATE_IDLE = 0,
+ MAVLINK_WPM_STATE_SENDLIST,
+ MAVLINK_WPM_STATE_GETLIST,
+ MAVLINK_WPM_STATE_ENUM_END
+};
+
+enum MAVLINK_WPM_CODES {
+ MAVLINK_WPM_CODE_OK = 0,
+ MAVLINK_WPM_CODE_ERR_WAYPOINT_ACTION_NOT_SUPPORTED,
+ MAVLINK_WPM_CODE_ERR_WAYPOINT_FRAME_NOT_SUPPORTED,
+ MAVLINK_WPM_CODE_ERR_WAYPOINT_OUT_OF_BOUNDS,
+ MAVLINK_WPM_CODE_ERR_WAYPOINT_MAX_NUMBER_EXCEEDED,
+ MAVLINK_WPM_CODE_ENUM_END
+};
+
+#define MAVLINK_MISSION_PROTOCOL_TIMEOUT_DEFAULT 5000000 ///< Protocol communication action timeout in useconds
+#define MAVLINK_MISSION_RETRY_TIMEOUT_DEFAULT 500000 ///< Protocol communication retry timeout in useconds
+
+class MavlinkMissionManager : public MavlinkStream {
+public:
+ ~MavlinkMissionManager();
+
+ const char *get_name() const
+ {
+ return MavlinkMissionManager::get_name_static();
+ }
+
+ static const char *get_name_static()
+ {
+ return "MISSION_ITEM";
+ }
+
+ uint8_t get_id()
+ {
+ return MAVLINK_MSG_ID_MISSION_ITEM;
+ }
+
+ static MavlinkStream *new_instance(Mavlink *mavlink)
+ {
+ return new MavlinkMissionManager(mavlink);
+ }
+
+ unsigned get_size();
+
+ void handle_message(const mavlink_message_t *msg);
+
+ void set_verbose(bool v) { _verbose = v; }
+
+private:
+ enum MAVLINK_WPM_STATES _state; ///< Current state
+
+ uint64_t _time_last_recv;
+ uint64_t _time_last_sent;
+
+ uint32_t _action_timeout;
+ uint32_t _retry_timeout;
+ unsigned _max_count; ///< Maximum number of mission items
+
+ int _dataman_id; ///< Dataman storage ID for active mission
+ unsigned _count; ///< Count of items in active mission
+ int _current_seq; ///< Current item sequence in active mission
+
+ int _transfer_dataman_id; ///< Dataman storage ID for current transmission
+ unsigned _transfer_count; ///< Items count in current transmission
+ unsigned _transfer_seq; ///< Item sequence in current transmission
+ unsigned _transfer_current_seq; ///< Current item ID for current transmission (-1 means not initialized)
+ unsigned _transfer_partner_sysid; ///< Partner system ID for current transmission
+ unsigned _transfer_partner_compid; ///< Partner component ID for current transmission
+
+ int _offboard_mission_sub;
+ int _mission_result_sub;
+ orb_advert_t _offboard_mission_pub;
+
+ MavlinkRateLimiter _slow_rate_limiter;
+
+ bool _verbose;
+
+ /* do not allow top copying this class */
+ MavlinkMissionManager(MavlinkMissionManager &);
+ MavlinkMissionManager& operator = (const MavlinkMissionManager &);
+
+ void init_offboard_mission();
+
+ int update_active_mission(int dataman_id, unsigned count, int seq);
+
+ /**
+ * @brief Sends an waypoint ack message
+ */
+ void send_mission_ack(uint8_t sysid, uint8_t compid, uint8_t type);
+
+ /**
+ * @brief Broadcasts the new target waypoint and directs the MAV to fly there
+ *
+ * This function broadcasts its new active waypoint sequence number and
+ * sends a message to the controller, advising it to fly to the coordinates
+ * of the waypoint with a given orientation
+ *
+ * @param seq The waypoint sequence number the MAV should fly to.
+ */
+ void send_mission_current(uint16_t seq);
+
+ void send_mission_count(uint8_t sysid, uint8_t compid, uint16_t count);
+
+ void send_mission_item(uint8_t sysid, uint8_t compid, uint16_t seq);
+
+ void send_mission_request(uint8_t sysid, uint8_t compid, uint16_t seq);
+
+ /**
+ * @brief emits a message that a waypoint reached
+ *
+ * This function broadcasts a message that a waypoint is reached.
+ *
+ * @param seq The waypoint sequence number the MAV has reached.
+ */
+ void send_mission_item_reached(uint16_t seq);
+
+ void handle_mission_ack(const mavlink_message_t *msg);
+
+ void handle_mission_set_current(const mavlink_message_t *msg);
+
+ void handle_mission_request_list(const mavlink_message_t *msg);
+
+ void handle_mission_request(const mavlink_message_t *msg);
+
+ void handle_mission_count(const mavlink_message_t *msg);
+
+ void handle_mission_item(const mavlink_message_t *msg);
+
+ void handle_mission_clear_all(const mavlink_message_t *msg);
+
+ /**
+ * Parse mavlink MISSION_ITEM message to get mission_item_s.
+ */
+ int parse_mavlink_mission_item(const mavlink_mission_item_t *mavlink_mission_item, struct mission_item_s *mission_item);
+
+ /**
+ * Format mission_item_s as mavlink MISSION_ITEM message.
+ */
+ int format_mavlink_mission_item(const struct mission_item_s *mission_item, mavlink_mission_item_t *mavlink_mission_item);
+
+protected:
+ explicit MavlinkMissionManager(Mavlink *mavlink);
+
+ void send(const hrt_abstime t);
+};
diff --git a/src/modules/mavlink/mavlink_orb_subscription.cpp b/src/modules/mavlink/mavlink_orb_subscription.cpp
index 3807323c2..734f0903a 100644
--- a/src/modules/mavlink/mavlink_orb_subscription.cpp
+++ b/src/modules/mavlink/mavlink_orb_subscription.cpp
@@ -44,15 +44,13 @@
#include <uORB/uORB.h>
#include <stdio.h>
-#include <systemlib/err.h>
-
#include "mavlink_orb_subscription.h"
MavlinkOrbSubscription::MavlinkOrbSubscription(const orb_id_t topic) :
- _fd(orb_subscribe(_topic)),
- _published(false),
+ next(nullptr),
_topic(topic),
- next(nullptr)
+ _fd(orb_subscribe(_topic)),
+ _published(false)
{
}
@@ -79,23 +77,21 @@ MavlinkOrbSubscription::update(uint64_t *time, void* data)
time_topic = 0;
}
- if (time_topic != *time) {
-
- if (orb_copy(_topic, _fd, data)) {
- /* error copying topic data */
- memset(data, 0, _topic->o_size);
- //warnx("err copy, fd: %d, obj: %s, size: %d", _fd, _topic->o_name, _topic->o_size);
- return false;
+ if (orb_copy(_topic, _fd, data)) {
+ /* error copying topic data */
+ memset(data, 0, _topic->o_size);
+ return false;
- } else {
- /* data copied successfully */
- _published = true;
+ } else {
+ /* data copied successfully */
+ _published = true;
+ if (time_topic != *time) {
*time = time_topic;
return true;
- }
- } else {
- return false;
+ } else {
+ return false;
+ }
}
}
diff --git a/src/modules/mavlink/mavlink_orb_subscription.h b/src/modules/mavlink/mavlink_orb_subscription.h
index 71efb43af..7af454df6 100644
--- a/src/modules/mavlink/mavlink_orb_subscription.h
+++ b/src/modules/mavlink/mavlink_orb_subscription.h
@@ -82,6 +82,10 @@ private:
const orb_id_t _topic; ///< topic metadata
int _fd; ///< subscription handle
bool _published; ///< topic was ever published
+
+ /* do not allow copying this class */
+ MavlinkOrbSubscription(const MavlinkOrbSubscription&);
+ MavlinkOrbSubscription operator=(const MavlinkOrbSubscription&);
};
diff --git a/src/modules/mavlink/mavlink_parameters.cpp b/src/modules/mavlink/mavlink_parameters.cpp
new file mode 100644
index 000000000..cd5f53d88
--- /dev/null
+++ b/src/modules/mavlink/mavlink_parameters.cpp
@@ -0,0 +1,197 @@
+/****************************************************************************
+ *
+ * Copyright (c) 2014 PX4 Development Team. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/**
+ * @file mavlink_parameters.cpp
+ * Mavlink parameters manager implementation.
+ *
+ * @author Anton Babushkin <anton.babushkin@me.com>
+ */
+
+#include <stdio.h>
+
+#include "mavlink_parameters.h"
+#include "mavlink_main.h"
+
+MavlinkParametersManager::MavlinkParametersManager(Mavlink *mavlink) : MavlinkStream(mavlink),
+ _send_all_index(-1)
+{
+}
+
+unsigned
+MavlinkParametersManager::get_size()
+{
+ if (_send_all_index >= 0) {
+ return MAVLINK_MSG_ID_PARAM_VALUE_LEN + MAVLINK_NUM_NON_PAYLOAD_BYTES;
+
+ } else {
+ return 0;
+ }
+}
+
+void
+MavlinkParametersManager::handle_message(const mavlink_message_t *msg)
+{
+ switch (msg->msgid) {
+ case MAVLINK_MSG_ID_PARAM_REQUEST_LIST: {
+ /* request all parameters */
+ mavlink_param_request_list_t req_list;
+ mavlink_msg_param_request_list_decode(msg, &req_list);
+
+ if (req_list.target_system == mavlink_system.sysid &&
+ (req_list.target_component == mavlink_system.compid || req_list.target_component == MAV_COMP_ID_ALL)) {
+
+ _send_all_index = 0;
+ _mavlink->send_statustext_info("[pm] sending list");
+ }
+ break;
+ }
+
+ case MAVLINK_MSG_ID_PARAM_SET: {
+ /* set parameter */
+ if (msg->msgid == MAVLINK_MSG_ID_PARAM_SET) {
+ mavlink_param_set_t set;
+ mavlink_msg_param_set_decode(msg, &set);
+
+ if (set.target_system == mavlink_system.sysid &&
+ (set.target_component == mavlink_system.compid || set.target_component == MAV_COMP_ID_ALL)) {
+
+ /* local name buffer to enforce null-terminated string */
+ char name[MAVLINK_MSG_PARAM_VALUE_FIELD_PARAM_ID_LEN + 1];
+ strncpy(name, set.param_id, MAVLINK_MSG_PARAM_VALUE_FIELD_PARAM_ID_LEN);
+ /* enforce null termination */
+ name[MAVLINK_MSG_PARAM_VALUE_FIELD_PARAM_ID_LEN] = '\0';
+ /* attempt to find parameter, set and send it */
+ param_t param = param_find(name);
+
+ if (param == PARAM_INVALID) {
+ char buf[MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN];
+ sprintf(buf, "[pm] unknown param: %s", name);
+ _mavlink->send_statustext_info(buf);
+
+ } else {
+ /* set and send parameter */
+ param_set(param, &(set.param_value));
+ send_param(param);
+ }
+ }
+ }
+ break;
+ }
+
+ case MAVLINK_MSG_ID_PARAM_REQUEST_READ: {
+ /* request one parameter */
+ mavlink_param_request_read_t req_read;
+ mavlink_msg_param_request_read_decode(msg, &req_read);
+
+ if (req_read.target_system == mavlink_system.sysid &&
+ (req_read.target_component == mavlink_system.compid || req_read.target_component == MAV_COMP_ID_ALL)) {
+
+ /* when no index is given, loop through string ids and compare them */
+ if (req_read.param_index < 0) {
+ /* local name buffer to enforce null-terminated string */
+ char name[MAVLINK_MSG_PARAM_VALUE_FIELD_PARAM_ID_LEN + 1];
+ strncpy(name, req_read.param_id, MAVLINK_MSG_PARAM_VALUE_FIELD_PARAM_ID_LEN);
+ /* enforce null termination */
+ name[MAVLINK_MSG_PARAM_VALUE_FIELD_PARAM_ID_LEN] = '\0';
+ /* attempt to find parameter and send it */
+ send_param(param_find(name));
+
+ } else {
+ /* when index is >= 0, send this parameter again */
+ send_param(param_for_index(req_read.param_index));
+ }
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+}
+
+void
+MavlinkParametersManager::send(const hrt_abstime t)
+{
+ /* send all parameters if requested */
+ if (_send_all_index >= 0) {
+ send_param(param_for_index(_send_all_index));
+ _send_all_index++;
+ if (_send_all_index >= (int) param_count()) {
+ _send_all_index = -1;
+ }
+ }
+}
+
+void
+MavlinkParametersManager::send_param(param_t param)
+{
+ if (param == PARAM_INVALID) {
+ return;
+ }
+
+ mavlink_param_value_t msg;
+
+ /*
+ * get param value, since MAVLink encodes float and int params in the same
+ * space during transmission, copy param onto float val_buf
+ */
+ if (param_get(param, &msg.param_value) != OK) {
+ return;
+ }
+
+ msg.param_count = param_count();
+ msg.param_index = param_get_index(param);
+
+ /* copy parameter name */
+ strncpy(msg.param_id, param_name(param), MAVLINK_MSG_PARAM_VALUE_FIELD_PARAM_ID_LEN);
+
+ /* query parameter type */
+ param_type_t type = param_type(param);
+
+ /*
+ * Map onboard parameter type to MAVLink type,
+ * endianess matches (both little endian)
+ */
+ if (type == PARAM_TYPE_INT32) {
+ msg.param_type = MAVLINK_TYPE_INT32_T;
+
+ } else if (type == PARAM_TYPE_FLOAT) {
+ msg.param_type = MAVLINK_TYPE_FLOAT;
+
+ } else {
+ msg.param_type = MAVLINK_TYPE_FLOAT;
+ }
+
+ _mavlink->send_message(MAVLINK_MSG_ID_PARAM_VALUE, &msg);
+}
diff --git a/src/modules/mavlink/mavlink_parameters.h b/src/modules/mavlink/mavlink_parameters.h
new file mode 100644
index 000000000..5576e6b84
--- /dev/null
+++ b/src/modules/mavlink/mavlink_parameters.h
@@ -0,0 +1,115 @@
+/****************************************************************************
+ *
+ * Copyright (c) 2012-2014 PX4 Development Team. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/**
+ * @file mavlink_parameters.h
+ * Mavlink parameters manager definition.
+ *
+ * @author Anton Babushkin <anton.babushkin@me.com>
+ */
+
+#pragma once
+
+#include <systemlib/param/param.h>
+
+#include "mavlink_bridge_header.h"
+#include "mavlink_stream.h"
+
+class MavlinkParametersManager : public MavlinkStream
+{
+public:
+ const char *get_name() const
+ {
+ return MavlinkParametersManager::get_name_static();
+ }
+
+ static const char *get_name_static()
+ {
+ return "PARAM_VALUE";
+ }
+
+ uint8_t get_id()
+ {
+ return MAVLINK_MSG_ID_PARAM_VALUE;
+ }
+
+ static MavlinkStream *new_instance(Mavlink *mavlink)
+ {
+ return new MavlinkParametersManager(mavlink);
+ }
+
+ unsigned get_size();
+
+ void handle_message(const mavlink_message_t *msg);
+
+ /**
+ * Send one parameter identified by index.
+ *
+ * @param index The index of the parameter to send.
+ * @return zero on success, nonzero else.
+ */
+ void start_send_one(int index);
+
+
+ /**
+ * Send one parameter identified by name.
+ *
+ * @param name The index of the parameter to send.
+ * @return zero on success, nonzero else.
+ */
+ int start_send_for_name(const char *name);
+
+ /**
+ * Start sending the parameter queue.
+ *
+ * This function will not directly send parameters, but instead
+ * activate the sending of one parameter on each call of
+ * mavlink_pm_queued_send().
+ * @see mavlink_pm_queued_send()
+ */
+ void start_send_all();
+
+private:
+ int _send_all_index;
+
+ /* do not allow top copying this class */
+ MavlinkParametersManager(MavlinkParametersManager &);
+ MavlinkParametersManager& operator = (const MavlinkParametersManager &);
+
+protected:
+ explicit MavlinkParametersManager(Mavlink *mavlink);
+
+ void send(const hrt_abstime t);
+
+ void send_param(param_t param);
+};
diff --git a/src/modules/mavlink/mavlink_rate_limiter.cpp b/src/modules/mavlink/mavlink_rate_limiter.cpp
index 9cdda8b17..d3b3e1cde 100644
--- a/src/modules/mavlink/mavlink_rate_limiter.cpp
+++ b/src/modules/mavlink/mavlink_rate_limiter.cpp
@@ -64,7 +64,7 @@ MavlinkRateLimiter::check(hrt_abstime t)
uint64_t dt = t - _last_sent;
if (dt > 0 && dt >= _interval) {
- _last_sent = (t / _interval) * _interval;
+ _last_sent = t;
return true;
}
diff --git a/src/modules/mavlink/mavlink_receiver.cpp b/src/modules/mavlink/mavlink_receiver.cpp
index 9c528adbe..ca00d1a67 100644
--- a/src/modules/mavlink/mavlink_receiver.cpp
+++ b/src/modules/mavlink/mavlink_receiver.cpp
@@ -37,6 +37,7 @@
*
* @author Lorenz Meier <lm@inf.ethz.ch>
* @author Anton Babushkin <anton.babushkin@me.com>
+ * @author Thomas Gubler <thomasgubler@gmail.com>
*/
/* XXX trim includes */
@@ -54,6 +55,7 @@
#include <drivers/drv_gyro.h>
#include <drivers/drv_mag.h>
#include <drivers/drv_baro.h>
+#include <drivers/drv_range_finder.h>
#include <time.h>
#include <float.h>
#include <unistd.h>
@@ -79,7 +81,6 @@ __BEGIN_DECLS
#include "mavlink_bridge_header.h"
#include "mavlink_receiver.h"
#include "mavlink_main.h"
-#include "util.h"
__END_DECLS
@@ -87,7 +88,9 @@ static const float mg2ms2 = CONSTANTS_ONE_G / 1000.0f;
MavlinkReceiver::MavlinkReceiver(Mavlink *parent) :
_mavlink(parent),
-
+ status{},
+ hil_local_pos{},
+ _control_mode{},
_global_pos_pub(-1),
_local_pos_pub(-1),
_attitude_pub(-1),
@@ -101,17 +104,28 @@ MavlinkReceiver::MavlinkReceiver(Mavlink *parent) :
_battery_pub(-1),
_cmd_pub(-1),
_flow_pub(-1),
+ _range_pub(-1),
_offboard_control_sp_pub(-1),
+ _global_vel_sp_pub(-1),
+ _att_sp_pub(-1),
+ _rates_sp_pub(-1),
+ _force_sp_pub(-1),
+ _pos_sp_triplet_pub(-1),
_vicon_position_pub(-1),
+ _vision_position_pub(-1),
_telemetry_status_pub(-1),
_rc_pub(-1),
_manual_pub(-1),
+ _control_mode_sub(orb_subscribe(ORB_ID(vehicle_control_mode))),
_hil_frames(0),
_old_timestamp(0),
_hil_local_proj_inited(0),
- _hil_local_alt0(0.0)
+ _hil_local_alt0(0.0f),
+ _hil_local_proj_ref{}
{
- memset(&hil_local_pos, 0, sizeof(hil_local_pos));
+
+ // make sure the FTP server is started
+ (void)MavlinkFTP::get_server();
}
MavlinkReceiver::~MavlinkReceiver()
@@ -126,6 +140,10 @@ MavlinkReceiver::handle_message(mavlink_message_t *msg)
handle_message_command_long(msg);
break;
+ case MAVLINK_MSG_ID_COMMAND_INT:
+ handle_message_command_int(msg);
+ break;
+
case MAVLINK_MSG_ID_OPTICAL_FLOW:
handle_message_optical_flow(msg);
break;
@@ -138,8 +156,16 @@ MavlinkReceiver::handle_message(mavlink_message_t *msg)
handle_message_vicon_position_estimate(msg);
break;
- case MAVLINK_MSG_ID_SET_QUAD_SWARM_ROLL_PITCH_YAW_THRUST:
- handle_message_quad_swarm_roll_pitch_yaw_thrust(msg);
+ case MAVLINK_MSG_ID_SET_POSITION_TARGET_LOCAL_NED:
+ handle_message_set_position_target_local_ned(msg);
+ break;
+
+ case MAVLINK_MSG_ID_SET_ATTITUDE_TARGET:
+ handle_message_set_attitude_target(msg);
+ break;
+
+ case MAVLINK_MSG_ID_VISION_POSITION_ESTIMATE:
+ handle_message_vision_position_estimate(msg);
break;
case MAVLINK_MSG_ID_RADIO_STATUS:
@@ -150,6 +176,18 @@ MavlinkReceiver::handle_message(mavlink_message_t *msg)
handle_message_manual_control(msg);
break;
+ case MAVLINK_MSG_ID_HEARTBEAT:
+ handle_message_heartbeat(msg);
+ break;
+
+ case MAVLINK_MSG_ID_REQUEST_DATA_STREAM:
+ handle_message_request_data_stream(msg);
+ break;
+
+ case MAVLINK_MSG_ID_FILE_TRANSFER_PROTOCOL:
+ MavlinkFTP::get_server()->handle_message(_mavlink, msg);
+ break;
+
default:
break;
}
@@ -174,6 +212,10 @@ MavlinkReceiver::handle_message(mavlink_message_t *msg)
handle_message_hil_state_quaternion(msg);
break;
+ case MAVLINK_MSG_ID_HIL_OPTICAL_FLOW:
+ handle_message_hil_optical_flow(msg);
+ break;
+
default:
break;
}
@@ -219,7 +261,8 @@ MavlinkReceiver::handle_message_command_long(mavlink_message_t *msg)
} else {
if (msg->sysid == mavlink_system.sysid && msg->compid == mavlink_system.compid) {
- warnx("ignoring CMD spoofed with same SYS/COMP ID");
+ warnx("ignoring CMD spoofed with same SYS/COMP (%d/%d) ID",
+ mavlink_system.sysid, mavlink_system.compid);
return;
}
@@ -253,6 +296,62 @@ MavlinkReceiver::handle_message_command_long(mavlink_message_t *msg)
}
void
+MavlinkReceiver::handle_message_command_int(mavlink_message_t *msg)
+{
+ /* command */
+ mavlink_command_int_t cmd_mavlink;
+ mavlink_msg_command_int_decode(msg, &cmd_mavlink);
+
+ if (cmd_mavlink.target_system == mavlink_system.sysid && ((cmd_mavlink.target_component == mavlink_system.compid)
+ || (cmd_mavlink.target_component == MAV_COMP_ID_ALL))) {
+ //check for MAVLINK terminate command
+ if (cmd_mavlink.command == MAV_CMD_PREFLIGHT_REBOOT_SHUTDOWN && ((int)cmd_mavlink.param1) == 3) {
+ /* This is the link shutdown command, terminate mavlink */
+ warnx("terminated by remote command");
+ fflush(stdout);
+ usleep(50000);
+
+ /* terminate other threads and this thread */
+ _mavlink->_task_should_exit = true;
+
+ } else {
+
+ if (msg->sysid == mavlink_system.sysid && msg->compid == mavlink_system.compid) {
+ warnx("ignoring CMD spoofed with same SYS/COMP (%d/%d) ID",
+ mavlink_system.sysid, mavlink_system.compid);
+ return;
+ }
+
+ struct vehicle_command_s vcmd;
+ memset(&vcmd, 0, sizeof(vcmd));
+
+ /* Copy the content of mavlink_command_int_t cmd_mavlink into command_t cmd */
+ vcmd.param1 = cmd_mavlink.param1;
+ vcmd.param2 = cmd_mavlink.param2;
+ vcmd.param3 = cmd_mavlink.param3;
+ vcmd.param4 = cmd_mavlink.param4;
+ /* these are coordinates as 1e7 scaled integers to work around the 32 bit floating point limits */
+ vcmd.param5 = ((double)cmd_mavlink.x) / 1e7;
+ vcmd.param6 = ((double)cmd_mavlink.y) / 1e7;
+ vcmd.param7 = cmd_mavlink.z;
+ // XXX do proper translation
+ vcmd.command = (enum VEHICLE_CMD)cmd_mavlink.command;
+ vcmd.target_system = cmd_mavlink.target_system;
+ vcmd.target_component = cmd_mavlink.target_component;
+ vcmd.source_system = msg->sysid;
+ vcmd.source_component = msg->compid;
+
+ if (_cmd_pub < 0) {
+ _cmd_pub = orb_advertise(ORB_ID(vehicle_command), &vcmd);
+
+ } else {
+ orb_publish(ORB_ID(vehicle_command), _cmd_pub, &vcmd);
+ }
+ }
+ }
+}
+
+void
MavlinkReceiver::handle_message_optical_flow(mavlink_message_t *msg)
{
/* optical flow */
@@ -281,6 +380,50 @@ MavlinkReceiver::handle_message_optical_flow(mavlink_message_t *msg)
}
void
+MavlinkReceiver::handle_message_hil_optical_flow(mavlink_message_t *msg)
+{
+ /* optical flow */
+ mavlink_hil_optical_flow_t flow;
+ mavlink_msg_hil_optical_flow_decode(msg, &flow);
+
+ struct optical_flow_s f;
+ memset(&f, 0, sizeof(f));
+
+ f.timestamp = hrt_absolute_time();
+ f.flow_timestamp = flow.time_usec;
+ f.flow_raw_x = flow.integrated_x;
+ f.flow_raw_y = flow.integrated_y;
+ f.ground_distance_m = flow.distance;
+ f.quality = flow.quality;
+ f.sensor_id = flow.sensor_id;
+
+ if (_flow_pub < 0) {
+ _flow_pub = orb_advertise(ORB_ID(optical_flow), &f);
+
+ } else {
+ orb_publish(ORB_ID(optical_flow), _flow_pub, &f);
+ }
+
+ /* Use distance value for range finder report */
+ struct range_finder_report r;
+ memset(&r, 0, sizeof(f));
+
+ r.timestamp = hrt_absolute_time();
+ r.error_count = 0;
+ r.type = RANGE_FINDER_TYPE_LASER;
+ r.distance = flow.distance;
+ r.minimum_distance = 0.0f;
+ r.maximum_distance = 40.0f; // this is set to match the typical range of real sensors, could be made configurable
+ r.valid = (r.distance > r.minimum_distance) && (r.distance < r.maximum_distance);
+
+ if (_range_pub < 0) {
+ _range_pub = orb_advertise(ORB_ID(sensor_range_finder), &r);
+ } else {
+ orb_publish(ORB_ID(sensor_range_finder), _range_pub, &r);
+ }
+}
+
+void
MavlinkReceiver::handle_message_set_mode(mavlink_message_t *msg)
{
mavlink_set_mode_t new_mode;
@@ -340,57 +483,267 @@ MavlinkReceiver::handle_message_vicon_position_estimate(mavlink_message_t *msg)
}
void
-MavlinkReceiver::handle_message_quad_swarm_roll_pitch_yaw_thrust(mavlink_message_t *msg)
+MavlinkReceiver::handle_message_set_position_target_local_ned(mavlink_message_t *msg)
{
- mavlink_set_quad_swarm_roll_pitch_yaw_thrust_t quad_motors_setpoint;
- mavlink_msg_set_quad_swarm_roll_pitch_yaw_thrust_decode(msg, &quad_motors_setpoint);
+ mavlink_set_position_target_local_ned_t set_position_target_local_ned;
+ mavlink_msg_set_position_target_local_ned_decode(msg, &set_position_target_local_ned);
+
+ struct offboard_control_setpoint_s offboard_control_sp;
+ memset(&offboard_control_sp, 0, sizeof(offboard_control_sp));//XXX breaks compatibility with multiple setpoints
+
+ /* Only accept messages which are intended for this system */
+ if ((mavlink_system.sysid == set_position_target_local_ned.target_system ||
+ set_position_target_local_ned.target_system == 0) &&
+ (mavlink_system.compid == set_position_target_local_ned.target_component ||
+ set_position_target_local_ned.target_component == 0)) {
+
+ /* convert mavlink type (local, NED) to uORB offboard control struct */
+ switch (set_position_target_local_ned.coordinate_frame) {
+ case MAV_FRAME_LOCAL_NED:
+ offboard_control_sp.mode = OFFBOARD_CONTROL_MODE_DIRECT_LOCAL_NED;
+ break;
+ case MAV_FRAME_LOCAL_OFFSET_NED:
+ offboard_control_sp.mode = OFFBOARD_CONTROL_MODE_DIRECT_LOCAL_OFFSET_NED;
+ break;
+ case MAV_FRAME_BODY_NED:
+ offboard_control_sp.mode = OFFBOARD_CONTROL_MODE_DIRECT_BODY_NED;
+ break;
+ case MAV_FRAME_BODY_OFFSET_NED:
+ offboard_control_sp.mode = OFFBOARD_CONTROL_MODE_DIRECT_BODY_OFFSET_NED;
+ break;
+ default:
+ /* invalid setpoint, avoid publishing */
+ return;
+ }
+ offboard_control_sp.position[0] = set_position_target_local_ned.x;
+ offboard_control_sp.position[1] = set_position_target_local_ned.y;
+ offboard_control_sp.position[2] = set_position_target_local_ned.z;
+ offboard_control_sp.velocity[0] = set_position_target_local_ned.vx;
+ offboard_control_sp.velocity[1] = set_position_target_local_ned.vy;
+ offboard_control_sp.velocity[2] = set_position_target_local_ned.vz;
+ offboard_control_sp.acceleration[0] = set_position_target_local_ned.afx;
+ offboard_control_sp.acceleration[1] = set_position_target_local_ned.afy;
+ offboard_control_sp.acceleration[2] = set_position_target_local_ned.afz;
+ offboard_control_sp.yaw = set_position_target_local_ned.yaw;
+ offboard_control_sp.yaw_rate = set_position_target_local_ned.yaw_rate;
+ offboard_control_sp.isForceSetpoint = (bool)(set_position_target_local_ned.type_mask & (1 << 9));
+
+ /* If we are in force control mode, for now set offboard mode to force control */
+ if (offboard_control_sp.isForceSetpoint) {
+ offboard_control_sp.mode = OFFBOARD_CONTROL_MODE_DIRECT_FORCE;
+ }
- if (mavlink_system.sysid < 4) {
- struct offboard_control_setpoint_s offboard_control_sp;
- memset(&offboard_control_sp, 0, sizeof(offboard_control_sp));
+ /* set ignore flags */
+ for (int i = 0; i < 9; i++) {
+ offboard_control_sp.ignore &= ~(1 << i);
+ offboard_control_sp.ignore |= (set_position_target_local_ned.type_mask & (1 << i));
+ }
- uint8_t ml_mode = 0;
- bool ml_armed = false;
+ offboard_control_sp.ignore &= ~(1 << OFB_IGN_BIT_YAW);
+ if (set_position_target_local_ned.type_mask & (1 << 10)) {
+ offboard_control_sp.ignore |= (1 << OFB_IGN_BIT_YAW);
+ }
- switch (quad_motors_setpoint.mode) {
- case 0:
- ml_armed = false;
- break;
+ offboard_control_sp.ignore &= ~(1 << OFB_IGN_BIT_YAWRATE);
+ if (set_position_target_local_ned.type_mask & (1 << 11)) {
+ offboard_control_sp.ignore |= (1 << OFB_IGN_BIT_YAWRATE);
+ }
- case 1:
- ml_mode = OFFBOARD_CONTROL_MODE_DIRECT_RATES;
- ml_armed = true;
+ offboard_control_sp.timestamp = hrt_absolute_time();
- break;
+ if (_offboard_control_sp_pub < 0) {
+ _offboard_control_sp_pub = orb_advertise(ORB_ID(offboard_control_setpoint), &offboard_control_sp);
- case 2:
- ml_mode = OFFBOARD_CONTROL_MODE_DIRECT_ATTITUDE;
- ml_armed = true;
+ } else {
+ orb_publish(ORB_ID(offboard_control_setpoint), _offboard_control_sp_pub, &offboard_control_sp);
+ }
- break;
+ /* If we are in offboard control mode and offboard control loop through is enabled
+ * also publish the setpoint topic which is read by the controller */
+ if (_mavlink->get_forward_externalsp()) {
+ bool updated;
+ orb_check(_control_mode_sub, &updated);
+ if (updated) {
+ orb_copy(ORB_ID(vehicle_control_mode), _control_mode_sub, &_control_mode);
+ }
+ if (_control_mode.flag_control_offboard_enabled) {
+ if (offboard_control_sp.isForceSetpoint &&
+ offboard_control_sp_ignore_position_all(offboard_control_sp) &&
+ offboard_control_sp_ignore_velocity_all(offboard_control_sp)) {
+ /* The offboard setpoint is a force setpoint only, directly writing to the force
+ * setpoint topic and not publishing the setpoint triplet topic */
+ struct vehicle_force_setpoint_s force_sp;
+ force_sp.x = offboard_control_sp.acceleration[0];
+ force_sp.y = offboard_control_sp.acceleration[1];
+ force_sp.z = offboard_control_sp.acceleration[2];
+ //XXX: yaw
+ if (_force_sp_pub < 0) {
+ _force_sp_pub = orb_advertise(ORB_ID(vehicle_force_setpoint), &force_sp);
+ } else {
+ orb_publish(ORB_ID(vehicle_force_setpoint), _force_sp_pub, &force_sp);
+ }
+ } else {
+ /* It's not a pure force setpoint: publish to setpoint triplet topic */
+ struct position_setpoint_triplet_s pos_sp_triplet;
+ pos_sp_triplet.previous.valid = false;
+ pos_sp_triplet.next.valid = false;
+ pos_sp_triplet.current.valid = true;
+ pos_sp_triplet.current.type = SETPOINT_TYPE_POSITION; //XXX support others
+
+ /* set the local pos values if the setpoint type is 'local pos' and none
+ * of the local pos fields is set to 'ignore' */
+ if (offboard_control_sp.mode == OFFBOARD_CONTROL_MODE_DIRECT_LOCAL_NED &&
+ !offboard_control_sp_ignore_position_some(offboard_control_sp)) {
+ pos_sp_triplet.current.position_valid = true;
+ pos_sp_triplet.current.x = offboard_control_sp.position[0];
+ pos_sp_triplet.current.y = offboard_control_sp.position[1];
+ pos_sp_triplet.current.z = offboard_control_sp.position[2];
+ } else {
+ pos_sp_triplet.current.position_valid = false;
+ }
- case 3:
- ml_mode = OFFBOARD_CONTROL_MODE_DIRECT_VELOCITY;
- break;
+ /* set the local vel values if the setpoint type is 'local pos' and none
+ * of the local vel fields is set to 'ignore' */
+ if (offboard_control_sp.mode == OFFBOARD_CONTROL_MODE_DIRECT_LOCAL_NED &&
+ !offboard_control_sp_ignore_velocity_some(offboard_control_sp)) {
+ pos_sp_triplet.current.velocity_valid = true;
+ pos_sp_triplet.current.vx = offboard_control_sp.velocity[0];
+ pos_sp_triplet.current.vy = offboard_control_sp.velocity[1];
+ pos_sp_triplet.current.vz = offboard_control_sp.velocity[2];
+ } else {
+ pos_sp_triplet.current.velocity_valid = false;
+ }
+
+ /* set the local acceleration values if the setpoint type is 'local pos' and none
+ * of the accelerations fields is set to 'ignore' */
+ if (offboard_control_sp.mode == OFFBOARD_CONTROL_MODE_DIRECT_LOCAL_NED &&
+ !offboard_control_sp_ignore_acceleration_some(offboard_control_sp)) {
+ pos_sp_triplet.current.acceleration_valid = true;
+ pos_sp_triplet.current.a_x = offboard_control_sp.acceleration[0];
+ pos_sp_triplet.current.a_y = offboard_control_sp.acceleration[1];
+ pos_sp_triplet.current.a_z = offboard_control_sp.acceleration[2];
+ pos_sp_triplet.current.acceleration_is_force =
+ offboard_control_sp.isForceSetpoint;
+
+ } else {
+ pos_sp_triplet.current.acceleration_valid = false;
+ }
+
+ /* set the yaw sp value if the setpoint type is 'local pos' and the yaw
+ * field is not set to 'ignore' */
+ if (offboard_control_sp.mode == OFFBOARD_CONTROL_MODE_DIRECT_LOCAL_NED &&
+ !offboard_control_sp_ignore_yaw(offboard_control_sp)) {
+ pos_sp_triplet.current.yaw_valid = true;
+ pos_sp_triplet.current.yaw = offboard_control_sp.yaw;
+
+ } else {
+ pos_sp_triplet.current.yaw_valid = false;
+ }
+
+ /* set the yawrate sp value if the setpoint type is 'local pos' and the yawrate
+ * field is not set to 'ignore' */
+ if (offboard_control_sp.mode == OFFBOARD_CONTROL_MODE_DIRECT_LOCAL_NED &&
+ !offboard_control_sp_ignore_yawrate(offboard_control_sp)) {
+ pos_sp_triplet.current.yawspeed_valid = true;
+ pos_sp_triplet.current.yawspeed = offboard_control_sp.yaw_rate;
+
+ } else {
+ pos_sp_triplet.current.yawspeed_valid = false;
+ }
+ //XXX handle global pos setpoints (different MAV frames)
+
+
+ if (_pos_sp_triplet_pub < 0) {
+ _pos_sp_triplet_pub = orb_advertise(ORB_ID(position_setpoint_triplet),
+ &pos_sp_triplet);
+ } else {
+ orb_publish(ORB_ID(position_setpoint_triplet), _pos_sp_triplet_pub,
+ &pos_sp_triplet);
+ }
+
+ }
+
+ }
- case 4:
- ml_mode = OFFBOARD_CONTROL_MODE_DIRECT_POSITION;
- break;
}
+ }
+}
+
+void
+MavlinkReceiver::handle_message_vision_position_estimate(mavlink_message_t *msg)
+{
+ mavlink_vision_position_estimate_t pos;
+ mavlink_msg_vision_position_estimate_decode(msg, &pos);
- offboard_control_sp.p1 = (float)quad_motors_setpoint.roll[mavlink_system.sysid - 1] / (float)INT16_MAX;
- offboard_control_sp.p2 = (float)quad_motors_setpoint.pitch[mavlink_system.sysid - 1] / (float)INT16_MAX;
- offboard_control_sp.p3 = (float)quad_motors_setpoint.yaw[mavlink_system.sysid - 1] / (float)INT16_MAX;
- offboard_control_sp.p4 = (float)quad_motors_setpoint.thrust[mavlink_system.sysid - 1] / (float)UINT16_MAX;
+ struct vision_position_estimate vision_position;
+ memset(&vision_position, 0, sizeof(vision_position));
- if (quad_motors_setpoint.thrust[mavlink_system.sysid - 1] == 0) {
- ml_armed = false;
+ // Use the component ID to identify the vision sensor
+ vision_position.id = msg->compid;
+
+ vision_position.timestamp_boot = hrt_absolute_time();
+ vision_position.timestamp_computer = pos.usec;
+ vision_position.x = pos.x;
+ vision_position.y = pos.y;
+ vision_position.z = pos.z;
+
+ // XXX fix this
+ vision_position.vx = 0.0f;
+ vision_position.vy = 0.0f;
+ vision_position.vz = 0.0f;
+
+ math::Quaternion q;
+ q.from_euler(pos.roll, pos.pitch, pos.yaw);
+
+ vision_position.q[0] = q(0);
+ vision_position.q[1] = q(1);
+ vision_position.q[2] = q(2);
+ vision_position.q[3] = q(3);
+
+ if (_vision_position_pub < 0) {
+ _vision_position_pub = orb_advertise(ORB_ID(vision_position_estimate), &vision_position);
+
+ } else {
+ orb_publish(ORB_ID(vision_position_estimate), _vision_position_pub, &vision_position);
+ }
+}
+
+void
+MavlinkReceiver::handle_message_set_attitude_target(mavlink_message_t *msg)
+{
+ mavlink_set_attitude_target_t set_attitude_target;
+ mavlink_msg_set_attitude_target_decode(msg, &set_attitude_target);
+
+ struct offboard_control_setpoint_s offboard_control_sp;
+ memset(&offboard_control_sp, 0, sizeof(offboard_control_sp)); //XXX breaks compatibility with multiple setpoints
+
+ /* Only accept messages which are intended for this system */
+ if ((mavlink_system.sysid == set_attitude_target.target_system ||
+ set_attitude_target.target_system == 0) &&
+ (mavlink_system.compid == set_attitude_target.target_component ||
+ set_attitude_target.target_component == 0)) {
+ for (int i = 0; i < 4; i++) {
+ offboard_control_sp.attitude[i] = set_attitude_target.q[i];
+ }
+ offboard_control_sp.attitude_rate[0] = set_attitude_target.body_roll_rate;
+ offboard_control_sp.attitude_rate[1] = set_attitude_target.body_pitch_rate;
+ offboard_control_sp.attitude_rate[2] = set_attitude_target.body_yaw_rate;
+
+ /* set correct ignore flags for body rate fields: copy from mavlink message */
+ for (int i = 0; i < 3; i++) {
+ offboard_control_sp.ignore &= ~(1 << (i + OFB_IGN_BIT_BODYRATE_X));
+ offboard_control_sp.ignore |= (set_attitude_target.type_mask & (1 << i)) << OFB_IGN_BIT_BODYRATE_X;
}
+ /* set correct ignore flags for thrust field: copy from mavlink message */
+ offboard_control_sp.ignore &= ~(1 << OFB_IGN_BIT_THRUST);
+ offboard_control_sp.ignore |= ((set_attitude_target.type_mask & (1 << 6)) << OFB_IGN_BIT_THRUST);
+ /* set correct ignore flags for attitude field: copy from mavlink message */
+ offboard_control_sp.ignore &= ~(1 << OFB_IGN_BIT_ATT);
+ offboard_control_sp.ignore |= ((set_attitude_target.type_mask & (1 << 7)) << OFB_IGN_BIT_ATT);
- offboard_control_sp.armed = ml_armed;
- offboard_control_sp.mode = static_cast<enum OFFBOARD_CONTROL_MODE>(ml_mode);
offboard_control_sp.timestamp = hrt_absolute_time();
+ offboard_control_sp.mode =OFFBOARD_CONTROL_MODE_DIRECT_ATTITUDE; //XXX handle rate control mode
if (_offboard_control_sp_pub < 0) {
_offboard_control_sp_pub = orb_advertise(ORB_ID(offboard_control_setpoint), &offboard_control_sp);
@@ -398,33 +751,90 @@ MavlinkReceiver::handle_message_quad_swarm_roll_pitch_yaw_thrust(mavlink_message
} else {
orb_publish(ORB_ID(offboard_control_setpoint), _offboard_control_sp_pub, &offboard_control_sp);
}
+
+ /* If we are in offboard control mode and offboard control loop through is enabled
+ * also publish the setpoint topic which is read by the controller */
+ if (_mavlink->get_forward_externalsp()) {
+ bool updated;
+ orb_check(_control_mode_sub, &updated);
+ if (updated) {
+ orb_copy(ORB_ID(vehicle_control_mode), _control_mode_sub, &_control_mode);
+ }
+
+ if (_control_mode.flag_control_offboard_enabled) {
+
+ /* Publish attitude setpoint if attitude and thrust ignore bits are not set */
+ if (!(offboard_control_sp_ignore_attitude(offboard_control_sp) ||
+ offboard_control_sp_ignore_thrust(offboard_control_sp))) {
+ struct vehicle_attitude_setpoint_s att_sp;
+ att_sp.timestamp = hrt_absolute_time();
+ mavlink_quaternion_to_euler(set_attitude_target.q,
+ &att_sp.roll_body, &att_sp.pitch_body, &att_sp.yaw_body);
+ mavlink_quaternion_to_dcm(set_attitude_target.q, att_sp.R_body);
+ att_sp.R_valid = true;
+ att_sp.thrust = set_attitude_target.thrust;
+ memcpy(att_sp.q_d, set_attitude_target.q, sizeof(att_sp.q_d));
+ att_sp.q_d_valid = true;
+ if (_att_sp_pub < 0) {
+ _att_sp_pub = orb_advertise(ORB_ID(vehicle_attitude_setpoint), &att_sp);
+ } else {
+ orb_publish(ORB_ID(vehicle_attitude_setpoint), _att_sp_pub, &att_sp);
+ }
+ }
+
+ /* Publish attitude rate setpoint if bodyrate and thrust ignore bits are not set */
+ ///XXX add support for ignoring individual axes
+ if (!(offboard_control_sp_ignore_bodyrates_some(offboard_control_sp) ||
+ offboard_control_sp_ignore_thrust(offboard_control_sp))) {
+ struct vehicle_rates_setpoint_s rates_sp;
+ rates_sp.timestamp = hrt_absolute_time();
+ rates_sp.roll = set_attitude_target.body_roll_rate;
+ rates_sp.pitch = set_attitude_target.body_pitch_rate;
+ rates_sp.yaw = set_attitude_target.body_yaw_rate;
+ rates_sp.thrust = set_attitude_target.thrust;
+
+ if (_att_sp_pub < 0) {
+ _rates_sp_pub = orb_advertise(ORB_ID(vehicle_rates_setpoint), &rates_sp);
+ } else {
+ orb_publish(ORB_ID(vehicle_rates_setpoint), _rates_sp_pub, &rates_sp);
+ }
+ }
+ }
+
+ }
}
}
void
MavlinkReceiver::handle_message_radio_status(mavlink_message_t *msg)
{
- mavlink_radio_status_t rstatus;
- mavlink_msg_radio_status_decode(msg, &rstatus);
-
- struct telemetry_status_s tstatus;
- memset(&tstatus, 0, sizeof(tstatus));
-
- tstatus.timestamp = hrt_absolute_time();
- tstatus.type = TELEMETRY_STATUS_RADIO_TYPE_3DR_RADIO;
- tstatus.rssi = rstatus.rssi;
- tstatus.remote_rssi = rstatus.remrssi;
- tstatus.txbuf = rstatus.txbuf;
- tstatus.noise = rstatus.noise;
- tstatus.remote_noise = rstatus.remnoise;
- tstatus.rxerrors = rstatus.rxerrors;
- tstatus.fixed = rstatus.fixed;
+ /* telemetry status supported only on first TELEMETRY_STATUS_ORB_ID_NUM mavlink channels */
+ if (_mavlink->get_channel() < TELEMETRY_STATUS_ORB_ID_NUM) {
+ mavlink_radio_status_t rstatus;
+ mavlink_msg_radio_status_decode(msg, &rstatus);
+
+ struct telemetry_status_s &tstatus = _mavlink->get_rx_status();
+
+ tstatus.timestamp = hrt_absolute_time();
+ tstatus.telem_time = tstatus.timestamp;
+ /* tstatus.heartbeat_time is set by system heartbeats */
+ tstatus.type = TELEMETRY_STATUS_RADIO_TYPE_3DR_RADIO;
+ tstatus.rssi = rstatus.rssi;
+ tstatus.remote_rssi = rstatus.remrssi;
+ tstatus.txbuf = rstatus.txbuf;
+ tstatus.noise = rstatus.noise;
+ tstatus.remote_noise = rstatus.remnoise;
+ tstatus.rxerrors = rstatus.rxerrors;
+ tstatus.fixed = rstatus.fixed;
+ tstatus.system_id = msg->sysid;
+ tstatus.component_id = msg->compid;
+
+ if (_telemetry_status_pub < 0) {
+ _telemetry_status_pub = orb_advertise(telemetry_status_orb_id[_mavlink->get_channel()], &tstatus);
- if (_telemetry_status_pub < 0) {
- _telemetry_status_pub = orb_advertise(ORB_ID(telemetry_status), &tstatus);
-
- } else {
- orb_publish(ORB_ID(telemetry_status), _telemetry_status_pub, &tstatus);
+ } else {
+ orb_publish(telemetry_status_orb_id[_mavlink->get_channel()], _telemetry_status_pub, &tstatus);
+ }
}
}
@@ -443,6 +853,8 @@ MavlinkReceiver::handle_message_manual_control(mavlink_message_t *msg)
manual.r = man.r / 1000.0f;
manual.z = man.z / 1000.0f;
+ warnx("pitch: %.2f, roll: %.2f, yaw: %.2f, throttle: %.2f", (double)manual.x, (double)manual.y, (double)manual.r, (double)manual.z);
+
if (_manual_pub < 0) {
_manual_pub = orb_advertise(ORB_ID(manual_control_setpoint), &manual);
@@ -452,6 +864,53 @@ MavlinkReceiver::handle_message_manual_control(mavlink_message_t *msg)
}
void
+MavlinkReceiver::handle_message_heartbeat(mavlink_message_t *msg)
+{
+ /* telemetry status supported only on first TELEMETRY_STATUS_ORB_ID_NUM mavlink channels */
+ if (_mavlink->get_channel() < TELEMETRY_STATUS_ORB_ID_NUM) {
+ mavlink_heartbeat_t hb;
+ mavlink_msg_heartbeat_decode(msg, &hb);
+
+ /* ignore own heartbeats, accept only heartbeats from GCS */
+ if (msg->sysid != mavlink_system.sysid && hb.type == MAV_TYPE_GCS) {
+
+ struct telemetry_status_s &tstatus = _mavlink->get_rx_status();
+
+ /* set heartbeat time and topic time and publish -
+ * the telem status also gets updated on telemetry events
+ */
+ tstatus.timestamp = hrt_absolute_time();
+ tstatus.heartbeat_time = tstatus.timestamp;
+
+ if (_telemetry_status_pub < 0) {
+ _telemetry_status_pub = orb_advertise(telemetry_status_orb_id[_mavlink->get_channel()], &tstatus);
+
+ } else {
+ orb_publish(telemetry_status_orb_id[_mavlink->get_channel()], _telemetry_status_pub, &tstatus);
+ }
+ }
+ }
+}
+
+void
+MavlinkReceiver::handle_message_request_data_stream(mavlink_message_t *msg)
+{
+ mavlink_request_data_stream_t req;
+ mavlink_msg_request_data_stream_decode(msg, &req);
+
+ if (req.target_system == mavlink_system.sysid && req.target_component == mavlink_system.compid) {
+ float rate = req.start_stop ? (1000.0f / req.req_message_rate) : 0.0f;
+
+ MavlinkStream *stream;
+ LL_FOREACH(_mavlink->get_streams(), stream) {
+ if (req.req_stream_id == stream->get_id()) {
+ _mavlink->configure_stream_threadsafe(stream->get_name(), rate);
+ }
+ }
+ }
+}
+
+void
MavlinkReceiver::handle_message_hil_sensor(mavlink_message_t *msg)
{
mavlink_hil_sensor_t imu;
@@ -495,10 +954,10 @@ MavlinkReceiver::handle_message_hil_sensor(mavlink_message_t *msg)
gyro.temperature = imu.temperature;
if (_gyro_pub < 0) {
- _gyro_pub = orb_advertise(ORB_ID(sensor_gyro), &gyro);
+ _gyro_pub = orb_advertise(ORB_ID(sensor_gyro0), &gyro);
} else {
- orb_publish(ORB_ID(sensor_gyro), _gyro_pub, &gyro);
+ orb_publish(ORB_ID(sensor_gyro0), _gyro_pub, &gyro);
}
}
@@ -517,10 +976,10 @@ MavlinkReceiver::handle_message_hil_sensor(mavlink_message_t *msg)
accel.temperature = imu.temperature;
if (_accel_pub < 0) {
- _accel_pub = orb_advertise(ORB_ID(sensor_accel), &accel);
+ _accel_pub = orb_advertise(ORB_ID(sensor_accel0), &accel);
} else {
- orb_publish(ORB_ID(sensor_accel), _accel_pub, &accel);
+ orb_publish(ORB_ID(sensor_accel0), _accel_pub, &accel);
}
}
@@ -538,10 +997,10 @@ MavlinkReceiver::handle_message_hil_sensor(mavlink_message_t *msg)
mag.z = imu.zmag;
if (_mag_pub < 0) {
- _mag_pub = orb_advertise(ORB_ID(sensor_mag), &mag);
+ _mag_pub = orb_advertise(ORB_ID(sensor_mag0), &mag);
} else {
- orb_publish(ORB_ID(sensor_mag), _mag_pub, &mag);
+ orb_publish(ORB_ID(sensor_mag0), _mag_pub, &mag);
}
}
@@ -556,10 +1015,10 @@ MavlinkReceiver::handle_message_hil_sensor(mavlink_message_t *msg)
baro.temperature = imu.temperature;
if (_baro_pub < 0) {
- _baro_pub = orb_advertise(ORB_ID(sensor_baro), &baro);
+ _baro_pub = orb_advertise(ORB_ID(sensor_baro0), &baro);
} else {
- orb_publish(ORB_ID(sensor_baro), _baro_pub, &baro);
+ orb_publish(ORB_ID(sensor_baro0), _baro_pub, &baro);
}
}
@@ -667,12 +1126,11 @@ MavlinkReceiver::handle_message_hil_gps(mavlink_message_t *msg)
hil_gps.lat = gps.lat;
hil_gps.lon = gps.lon;
hil_gps.alt = gps.alt;
- hil_gps.eph_m = (float)gps.eph * 1e-2f; // from cm to m
- hil_gps.epv_m = (float)gps.epv * 1e-2f; // from cm to m
+ hil_gps.eph = (float)gps.eph * 1e-2f; // from cm to m
+ hil_gps.epv = (float)gps.epv * 1e-2f; // from cm to m
hil_gps.timestamp_variance = timestamp;
hil_gps.s_variance_m_s = 5.0f;
- hil_gps.p_variance_m = hil_gps.eph_m * hil_gps.eph_m;
hil_gps.timestamp_velocity = timestamp;
hil_gps.vel_m_s = (float)gps.vel * 1e-2f; // from cm/s to m/s
@@ -682,9 +1140,8 @@ MavlinkReceiver::handle_message_hil_gps(mavlink_message_t *msg)
hil_gps.vel_ned_valid = true;
hil_gps.cog_rad = _wrap_pi(gps.cog * M_DEG_TO_RAD_F * 1e-2f);
- hil_gps.timestamp_satellites = timestamp;
hil_gps.fix_type = gps.fix_type;
- hil_gps.satellites_visible = gps.satellites_visible;
+ hil_gps.satellites_used = gps.satellites_visible; //TODO: rename mavlink_hil_gps_t sats visible to used?
if (_gps_pub < 0) {
_gps_pub = orb_advertise(ORB_ID(vehicle_gps_position), &hil_gps);
@@ -835,10 +1292,10 @@ MavlinkReceiver::handle_message_hil_state_quaternion(mavlink_message_t *msg)
accel.temperature = 25.0f;
if (_accel_pub < 0) {
- _accel_pub = orb_advertise(ORB_ID(sensor_accel), &accel);
+ _accel_pub = orb_advertise(ORB_ID(sensor_accel0), &accel);
} else {
- orb_publish(ORB_ID(sensor_accel), _accel_pub, &accel);
+ orb_publish(ORB_ID(sensor_accel0), _accel_pub, &accel);
}
}
@@ -902,18 +1359,13 @@ MavlinkReceiver::receive_thread(void *arg)
/* handle generic messages and commands */
handle_message(&msg);
- /* handle packet with waypoint component */
- _mavlink->mavlink_wpm_message_handler(&msg);
-
- /* handle packet with parameter component */
- _mavlink->mavlink_pm_message_handler(_mavlink->get_channel(), &msg);
-
- if (_mavlink->get_forwarding_on()) {
- /* forward any messages to other mavlink instances */
- Mavlink::forward_message(&msg, _mavlink);
- }
+ /* handle packet with parent object */
+ _mavlink->handle_message(&msg);
}
}
+
+ /* count received bytes */
+ _mavlink->count_rxbytes(nread);
}
}
@@ -948,7 +1400,7 @@ MavlinkReceiver::receive_start(Mavlink *parent)
struct sched_param param;
(void)pthread_attr_getschedparam(&receiveloop_attr, &param);
- param.sched_priority = SCHED_PRIORITY_MAX - 40;
+ param.sched_priority = SCHED_PRIORITY_MAX - 80;
(void)pthread_attr_setschedparam(&receiveloop_attr, &param);
pthread_attr_setstacksize(&receiveloop_attr, 2900);
diff --git a/src/modules/mavlink/mavlink_receiver.h b/src/modules/mavlink/mavlink_receiver.h
index 9ab84b58a..e5f2c6a73 100644
--- a/src/modules/mavlink/mavlink_receiver.h
+++ b/src/modules/mavlink/mavlink_receiver.h
@@ -36,6 +36,7 @@
* MAVLink 1.0 uORB listener definition
*
* @author Lorenz Meier <lm@inf.ethz.ch>
+ * @author Anton Babushkin <anton.babushkin@me.com>
*/
#pragma once
@@ -44,6 +45,7 @@
#include <uORB/uORB.h>
#include <uORB/topics/sensor_combined.h>
#include <uORB/topics/rc_channels.h>
+#include <uORB/topics/vehicle_control_mode.h>
#include <uORB/topics/vehicle_attitude.h>
#include <uORB/topics/vehicle_gps_position.h>
#include <uORB/topics/vehicle_global_position.h>
@@ -53,8 +55,10 @@
#include <uORB/topics/offboard_control_setpoint.h>
#include <uORB/topics/vehicle_command.h>
#include <uORB/topics/vehicle_local_position_setpoint.h>
+#include <uORB/topics/vehicle_global_velocity_setpoint.h>
#include <uORB/topics/position_setpoint_triplet.h>
#include <uORB/topics/vehicle_vicon_position.h>
+#include <uORB/topics/vision_position_estimate.h>
#include <uORB/topics/vehicle_attitude_setpoint.h>
#include <uORB/topics/vehicle_rates_setpoint.h>
#include <uORB/topics/optical_flow.h>
@@ -67,6 +71,9 @@
#include <uORB/topics/debug_key_value.h>
#include <uORB/topics/airspeed.h>
#include <uORB/topics/battery_status.h>
+#include <uORB/topics/vehicle_force_setpoint.h>
+
+#include "mavlink_ftp.h"
class Mavlink;
@@ -100,18 +107,23 @@ public:
static void *start_helper(void *context);
private:
- perf_counter_t _loop_perf; /**< loop performance counter */
-
Mavlink *_mavlink;
void handle_message(mavlink_message_t *msg);
void handle_message_command_long(mavlink_message_t *msg);
+ void handle_message_command_int(mavlink_message_t *msg);
void handle_message_optical_flow(mavlink_message_t *msg);
+ void handle_message_hil_optical_flow(mavlink_message_t *msg);
void handle_message_set_mode(mavlink_message_t *msg);
void handle_message_vicon_position_estimate(mavlink_message_t *msg);
+ void handle_message_vision_position_estimate(mavlink_message_t *msg);
void handle_message_quad_swarm_roll_pitch_yaw_thrust(mavlink_message_t *msg);
+ void handle_message_set_position_target_local_ned(mavlink_message_t *msg);
+ void handle_message_set_attitude_target(mavlink_message_t *msg);
void handle_message_radio_status(mavlink_message_t *msg);
void handle_message_manual_control(mavlink_message_t *msg);
+ void handle_message_heartbeat(mavlink_message_t *msg);
+ void handle_message_request_data_stream(mavlink_message_t *msg);
void handle_message_hil_sensor(mavlink_message_t *msg);
void handle_message_hil_gps(mavlink_message_t *msg);
void handle_message_hil_state_quaternion(mavlink_message_t *msg);
@@ -120,6 +132,7 @@ private:
mavlink_status_t status;
struct vehicle_local_position_s hil_local_pos;
+ struct vehicle_control_mode_s _control_mode;
orb_advert_t _global_pos_pub;
orb_advert_t _local_pos_pub;
orb_advert_t _attitude_pub;
@@ -133,14 +146,26 @@ private:
orb_advert_t _battery_pub;
orb_advert_t _cmd_pub;
orb_advert_t _flow_pub;
+ orb_advert_t _range_pub;
orb_advert_t _offboard_control_sp_pub;
+ orb_advert_t _global_vel_sp_pub;
+ orb_advert_t _att_sp_pub;
+ orb_advert_t _rates_sp_pub;
+ orb_advert_t _force_sp_pub;
+ orb_advert_t _pos_sp_triplet_pub;
orb_advert_t _vicon_position_pub;
+ orb_advert_t _vision_position_pub;
orb_advert_t _telemetry_status_pub;
orb_advert_t _rc_pub;
orb_advert_t _manual_pub;
+ int _control_mode_sub;
int _hil_frames;
uint64_t _old_timestamp;
bool _hil_local_proj_inited;
float _hil_local_alt0;
struct map_projection_reference_s _hil_local_proj_ref;
+
+ /* do not allow copying this class */
+ MavlinkReceiver(const MavlinkReceiver&);
+ MavlinkReceiver operator=(const MavlinkReceiver&);
};
diff --git a/src/modules/mavlink/mavlink_stream.cpp b/src/modules/mavlink/mavlink_stream.cpp
index 5ec30bd33..5b9e45d3e 100644
--- a/src/modules/mavlink/mavlink_stream.cpp
+++ b/src/modules/mavlink/mavlink_stream.cpp
@@ -43,7 +43,11 @@
#include "mavlink_stream.h"
#include "mavlink_main.h"
-MavlinkStream::MavlinkStream() : _interval(1000000), _last_sent(0), _channel(MAVLINK_COMM_0), next(nullptr)
+MavlinkStream::MavlinkStream(Mavlink *mavlink) :
+ next(nullptr),
+ _mavlink(mavlink),
+ _interval(1000000),
+ _last_sent(0)
{
}
@@ -61,26 +65,27 @@ MavlinkStream::set_interval(const unsigned int interval)
}
/**
- * Set mavlink channel
- */
-void
-MavlinkStream::set_channel(mavlink_channel_t channel)
-{
- _channel = channel;
-}
-
-/**
* Update subscriptions and send message if necessary
*/
int
MavlinkStream::update(const hrt_abstime t)
{
uint64_t dt = t - _last_sent;
+ unsigned int interval = _interval;
+
+ if (!const_rate()) {
+ interval /= _mavlink->get_rate_mult();
+ }
- if (dt > 0 && dt >= _interval) {
+ if (dt > 0 && dt >= interval) {
/* interval expired, send message */
send(t);
- _last_sent = (t / _interval) * _interval;
+ if (const_rate()) {
+ _last_sent = (t / _interval) * _interval;
+
+ } else {
+ _last_sent = t;
+ }
return 0;
}
diff --git a/src/modules/mavlink/mavlink_stream.h b/src/modules/mavlink/mavlink_stream.h
index a41ace48e..ef22dc443 100644
--- a/src/modules/mavlink/mavlink_stream.h
+++ b/src/modules/mavlink/mavlink_stream.h
@@ -46,36 +46,60 @@
class Mavlink;
class MavlinkStream;
-#include "mavlink_main.h"
-
class MavlinkStream
{
public:
MavlinkStream *next;
- MavlinkStream();
+ MavlinkStream(Mavlink *mavlink);
virtual ~MavlinkStream();
+
+ /**
+ * Get the interval
+ *
+ * @param interval the inveral in microseconds (us) between messages
+ */
void set_interval(const unsigned int interval);
- void set_channel(mavlink_channel_t channel);
+
+ /**
+ * Get the interval
+ *
+ * @return the inveral in microseconds (us) between messages
+ */
+ unsigned get_interval() { return _interval; }
/**
* @return 0 if updated / sent, -1 if unchanged
*/
int update(const hrt_abstime t);
- static MavlinkStream *new_instance();
+ static MavlinkStream *new_instance(const Mavlink *mavlink);
static const char *get_name_static();
- virtual void subscribe(Mavlink *mavlink) = 0;
virtual const char *get_name() const = 0;
+ virtual uint8_t get_id() = 0;
+
+ /**
+ * @return true if steam rate shouldn't be adjusted
+ */
+ virtual bool const_rate() { return false; }
+
+ /**
+ * Get maximal total messages size on update
+ */
+ virtual unsigned get_size() = 0;
protected:
- mavlink_channel_t _channel;
+ Mavlink * _mavlink;
unsigned int _interval;
virtual void send(const hrt_abstime t) = 0;
private:
hrt_abstime _last_sent;
+
+ /* do not allow top copying this class */
+ MavlinkStream(const MavlinkStream&);
+ MavlinkStream& operator=(const MavlinkStream&);
};
diff --git a/src/modules/mavlink/mavlink_tests/mavlink_ftp_test.cpp b/src/modules/mavlink/mavlink_tests/mavlink_ftp_test.cpp
new file mode 100644
index 000000000..4caa820b6
--- /dev/null
+++ b/src/modules/mavlink/mavlink_tests/mavlink_ftp_test.cpp
@@ -0,0 +1,761 @@
+/****************************************************************************
+ *
+ * Copyright (C) 2014 PX4 Development Team. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/// @file mavlink_ftp_test.cpp
+/// @author Don Gagne <don@thegagnes.com>
+
+#include <sys/stat.h>
+#include <crc32.h>
+#include <stdio.h>
+#include <fcntl.h>
+
+#include "mavlink_ftp_test.h"
+#include "../mavlink_ftp.h"
+
+/// @brief Test case file name for Read command. File are generated using mavlink_ftp_test_data.py
+const MavlinkFtpTest::ReadTestCase MavlinkFtpTest::_rgReadTestCases[] = {
+ { "/etc/unit_test_data/mavlink_tests/test_238.data", MAVLINK_MSG_FILE_TRANSFER_PROTOCOL_FIELD_PAYLOAD_LEN - sizeof(MavlinkFTP::PayloadHeader) - 1}, // Read takes less than single packet
+ { "/etc/unit_test_data/mavlink_tests/test_239.data", MAVLINK_MSG_FILE_TRANSFER_PROTOCOL_FIELD_PAYLOAD_LEN - sizeof(MavlinkFTP::PayloadHeader) }, // Read completely fills single packet
+ { "/etc/unit_test_data/mavlink_tests/test_240.data", MAVLINK_MSG_FILE_TRANSFER_PROTOCOL_FIELD_PAYLOAD_LEN - sizeof(MavlinkFTP::PayloadHeader) + 1 }, // Read take two packets
+};
+
+const char MavlinkFtpTest::_unittest_microsd_dir[] = "/fs/microsd/ftp_unit_test_dir";
+const char MavlinkFtpTest::_unittest_microsd_file[] = "/fs/microsd/ftp_unit_test_dir/file";
+
+MavlinkFtpTest::MavlinkFtpTest() :
+ _ftp_server{},
+ _reply_msg{},
+ _lastOutgoingSeqNumber{}
+{
+}
+
+MavlinkFtpTest::~MavlinkFtpTest()
+{
+
+}
+
+/// @brief Called before every test to initialize the FTP Server.
+void MavlinkFtpTest::_init(void)
+{
+ _ftp_server = new MavlinkFTP;;
+ _ftp_server->set_unittest_worker(MavlinkFtpTest::receive_message, this);
+
+ _cleanup_microsd();
+}
+
+/// @brief Called after every test to take down the FTP Server.
+void MavlinkFtpTest::_cleanup(void)
+{
+ delete _ftp_server;
+
+ _cleanup_microsd();
+}
+
+/// @brief Tests for correct behavior of an Ack response.
+bool MavlinkFtpTest::_ack_test(void)
+{
+ MavlinkFTP::PayloadHeader payload;
+ mavlink_file_transfer_protocol_t ftp_msg;
+ MavlinkFTP::PayloadHeader *reply;
+
+ payload.opcode = MavlinkFTP::kCmdNone;
+
+ bool success = _send_receive_msg(&payload, // FTP payload header
+ 0, // size in bytes of data
+ nullptr, // Data to start into FTP message payload
+ &ftp_msg, // Response from server
+ &reply); // Payload inside FTP message response
+ if (!success) {
+ return false;
+ }
+
+ ut_compare("Didn't get Ack back", reply->opcode, MavlinkFTP::kRspAck);
+ ut_compare("Incorrect payload size", reply->size, 0);
+
+ return true;
+}
+
+/// @brief Tests for correct response to an invalid opcpde.
+bool MavlinkFtpTest::_bad_opcode_test(void)
+{
+ MavlinkFTP::PayloadHeader payload;
+ mavlink_file_transfer_protocol_t ftp_msg;
+ MavlinkFTP::PayloadHeader *reply;
+
+ payload.opcode = 0xFF; // bogus opcode
+
+ bool success = _send_receive_msg(&payload, // FTP payload header
+ 0, // size in bytes of data
+ nullptr, // Data to start into FTP message payload
+ &ftp_msg, // Response from server
+ &reply); // Payload inside FTP message response
+ if (!success) {
+ return false;
+ }
+
+ ut_compare("Didn't get Nak back", reply->opcode, MavlinkFTP::kRspNak);
+ ut_compare("Incorrect payload size", reply->size, 1);
+ ut_compare("Incorrect error code", reply->data[0], MavlinkFTP::kErrUnknownCommand);
+
+ return true;
+}
+
+/// @brief Tests for correct reponse to a payload which an invalid data size field.
+bool MavlinkFtpTest::_bad_datasize_test(void)
+{
+ mavlink_message_t msg;
+ MavlinkFTP::PayloadHeader payload;
+ mavlink_file_transfer_protocol_t ftp_msg;
+ MavlinkFTP::PayloadHeader *reply;
+
+ payload.opcode = MavlinkFTP::kCmdListDirectory;
+
+ _setup_ftp_msg(&payload, 0, nullptr, &msg);
+
+ // Set the data size to be one larger than is legal
+ ((MavlinkFTP::PayloadHeader*)((mavlink_file_transfer_protocol_t*)msg.payload64)->payload)->size = MAVLINK_MSG_FILE_TRANSFER_PROTOCOL_FIELD_PAYLOAD_LEN + 1;
+
+ _ftp_server->handle_message(nullptr /* mavlink */, &msg);
+
+ if (!_decode_message(&_reply_msg, &ftp_msg, &reply)) {
+ return false;
+ }
+
+ ut_compare("Didn't get Nak back", reply->opcode, MavlinkFTP::kRspNak);
+ ut_compare("Incorrect payload size", reply->size, 1);
+ ut_compare("Incorrect error code", reply->data[0], MavlinkFTP::kErrInvalidDataSize);
+
+ return true;
+}
+
+bool MavlinkFtpTest::_list_test(void)
+{
+ MavlinkFTP::PayloadHeader payload;
+ mavlink_file_transfer_protocol_t ftp_msg;
+ MavlinkFTP::PayloadHeader *reply;
+
+ char response1[] = "Dempty_dir|Ftest_238.data\t238|Ftest_239.data\t239|Ftest_240.data\t240";
+ char response2[] = "Ddev|Detc|Dfs|Dobj";
+
+ struct _testCase {
+ const char *dir; ///< Directory to run List command on
+ char *response; ///< Expected response entries from List command
+ int response_count; ///< Number of directories that should be returned
+ bool success; ///< true: List command should succeed, false: List command should fail
+ };
+ struct _testCase rgTestCases[] = {
+ { "/bogus", nullptr, 0, false },
+ { "/etc/unit_test_data/mavlink_tests", response1, 4, true },
+ { "/", response2, 4, true },
+ };
+
+ for (size_t i=0; i<sizeof(rgTestCases)/sizeof(rgTestCases[0]); i++) {
+ const struct _testCase *test = &rgTestCases[i];
+
+ payload.opcode = MavlinkFTP::kCmdListDirectory;
+ payload.offset = 0;
+
+ bool success = _send_receive_msg(&payload, // FTP payload header
+ strlen(test->dir)+1, // size in bytes of data
+ (uint8_t*)test->dir, // Data to start into FTP message payload
+ &ftp_msg, // Response from server
+ &reply); // Payload inside FTP message response
+ if (!success) {
+ return false;
+ }
+
+ if (test->success) {
+ ut_compare("Didn't get Ack back", reply->opcode, MavlinkFTP::kRspAck);
+ ut_compare("Incorrect payload size", reply->size, strlen(test->response) + 1);
+
+ // The return order of directories from the List command is not repeatable. So we can't do a direct comparison
+ // to a hardcoded return result string.
+
+ // Convert null terminators to seperator char so we can use strok to parse returned data
+ for (uint8_t j=0; j<reply->size-1; j++) {
+ if (reply->data[j] == 0) {
+ reply->data[j] = '|';
+ }
+ }
+
+ // Loop over returned directory entries trying to find then in the response list
+ char *dir;
+ int response_count = 0;
+ dir = strtok((char *)&reply->data[0], "|");
+ while (dir != nullptr) {
+ ut_assert("Returned directory not found in expected response", strstr(test->response, dir));
+ response_count++;
+ dir = strtok(nullptr, "|");
+ }
+
+ ut_compare("Incorrect number of directory entires returned", test->response_count, response_count);
+ } else {
+ ut_compare("Didn't get Nak back", reply->opcode, MavlinkFTP::kRspNak);
+ ut_compare("Incorrect payload size", reply->size, 2);
+ ut_compare("Incorrect error code", reply->data[0], MavlinkFTP::kErrFailErrno);
+ }
+ }
+
+ return true;
+}
+
+/// @brief Tests for correct reponse to a List command on a valid directory, but with an offset that
+/// is beyond the last directory entry.
+bool MavlinkFtpTest::_list_eof_test(void)
+{
+ MavlinkFTP::PayloadHeader payload;
+ mavlink_file_transfer_protocol_t ftp_msg;
+ MavlinkFTP::PayloadHeader *reply;
+ const char *dir = "/";
+
+ payload.opcode = MavlinkFTP::kCmdListDirectory;
+ payload.offset = 4; // offset past top level dirs
+
+ bool success = _send_receive_msg(&payload, // FTP payload header
+ strlen(dir)+1, // size in bytes of data
+ (uint8_t*)dir, // Data to start into FTP message payload
+ &ftp_msg, // Response from server
+ &reply); // Payload inside FTP message response
+ if (!success) {
+ return false;
+ }
+
+ ut_compare("Didn't get Nak back", reply->opcode, MavlinkFTP::kRspNak);
+ ut_compare("Incorrect payload size", reply->size, 1);
+ ut_compare("Incorrect error code", reply->data[0], MavlinkFTP::kErrEOF);
+
+ return true;
+}
+
+/// @brief Tests for correct reponse to an Open command on a file which does not exist.
+bool MavlinkFtpTest::_open_badfile_test(void)
+{
+ MavlinkFTP::PayloadHeader payload;
+ mavlink_file_transfer_protocol_t ftp_msg;
+ MavlinkFTP::PayloadHeader *reply;
+ const char *dir = "/foo"; // non-existent file
+
+ payload.opcode = MavlinkFTP::kCmdOpenFileRO;
+ payload.offset = 0;
+
+ bool success = _send_receive_msg(&payload, // FTP payload header
+ strlen(dir)+1, // size in bytes of data
+ (uint8_t*)dir, // Data to start into FTP message payload
+ &ftp_msg, // Response from server
+ &reply); // Payload inside FTP message response
+ if (!success) {
+ return false;
+ }
+
+ ut_compare("Didn't get Nak back", reply->opcode, MavlinkFTP::kRspNak);
+ ut_compare("Incorrect payload size", reply->size, 2);
+ ut_compare("Incorrect error code", reply->data[0], MavlinkFTP::kErrFailErrno);
+
+ return true;
+}
+
+/// @brief Tests for correct reponse to an Open command on a file, followed by Terminate
+bool MavlinkFtpTest::_open_terminate_test(void)
+{
+ MavlinkFTP::PayloadHeader payload;
+ mavlink_file_transfer_protocol_t ftp_msg;
+ MavlinkFTP::PayloadHeader *reply;
+
+ for (size_t i=0; i<sizeof(_rgReadTestCases)/sizeof(_rgReadTestCases[0]); i++) {
+ struct stat st;
+ const ReadTestCase *test = &_rgReadTestCases[i];
+
+ payload.opcode = MavlinkFTP::kCmdOpenFileRO;
+ payload.offset = 0;
+
+ bool success = _send_receive_msg(&payload, // FTP payload header
+ strlen(test->file)+1, // size in bytes of data
+ (uint8_t*)test->file, // Data to start into FTP message payload
+ &ftp_msg, // Response from server
+ &reply); // Payload inside FTP message response
+ if (!success) {
+ return false;
+ }
+
+ ut_compare("stat failed", stat(test->file, &st), 0);
+
+
+ ut_compare("Didn't get Ack back", reply->opcode, MavlinkFTP::kRspAck);
+ ut_compare("Incorrect payload size", reply->size, sizeof(uint32_t));
+ ut_compare("File size incorrect", *((uint32_t*)&reply->data[0]), (uint32_t)st.st_size);
+
+ payload.opcode = MavlinkFTP::kCmdTerminateSession;
+ payload.session = reply->session;
+ payload.size = 0;
+
+ success = _send_receive_msg(&payload, // FTP payload header
+ 0, // size in bytes of data
+ nullptr, // Data to start into FTP message payload
+ &ftp_msg, // Response from server
+ &reply); // Payload inside FTP message response
+ if (!success) {
+ return false;
+ }
+
+ ut_compare("Didn't get Ack back", reply->opcode, MavlinkFTP::kRspAck);
+ ut_compare("Incorrect payload size", reply->size, 0);
+ }
+
+ return true;
+}
+
+/// @brief Tests for correct reponse to a Terminate command on an invalid session.
+bool MavlinkFtpTest::_terminate_badsession_test(void)
+{
+ MavlinkFTP::PayloadHeader payload;
+ mavlink_file_transfer_protocol_t ftp_msg;
+ MavlinkFTP::PayloadHeader *reply;
+ const char *file = _rgReadTestCases[0].file;
+
+ payload.opcode = MavlinkFTP::kCmdOpenFileRO;
+ payload.offset = 0;
+
+ bool success = _send_receive_msg(&payload, // FTP payload header
+ strlen(file)+1, // size in bytes of data
+ (uint8_t*)file, // Data to start into FTP message payload
+ &ftp_msg, // Response from server
+ &reply); // Payload inside FTP message response
+ if (!success) {
+ return false;
+ }
+
+ ut_compare("Didn't get Ack back", reply->opcode, MavlinkFTP::kRspAck);
+
+ payload.opcode = MavlinkFTP::kCmdTerminateSession;
+ payload.session = reply->session + 1;
+ payload.size = 0;
+
+ success = _send_receive_msg(&payload, // FTP payload header
+ 0, // size in bytes of data
+ nullptr, // Data to start into FTP message payload
+ &ftp_msg, // Response from server
+ &reply); // Payload inside FTP message response
+ if (!success) {
+ return false;
+ }
+
+ ut_compare("Didn't get Nak back", reply->opcode, MavlinkFTP::kRspNak);
+ ut_compare("Incorrect payload size", reply->size, 1);
+ ut_compare("Incorrect error code", reply->data[0], MavlinkFTP::kErrInvalidSession);
+
+ return true;
+}
+
+/// @brief Tests for correct reponse to a Read command on an open session.
+bool MavlinkFtpTest::_read_test(void)
+{
+ MavlinkFTP::PayloadHeader payload;
+ mavlink_file_transfer_protocol_t ftp_msg;
+ MavlinkFTP::PayloadHeader *reply;
+
+ for (size_t i=0; i<sizeof(_rgReadTestCases)/sizeof(_rgReadTestCases[0]); i++) {
+ struct stat st;
+ const ReadTestCase *test = &_rgReadTestCases[i];
+
+ // Read in the file so we can compare it to what we get back
+ ut_compare("stat failed", stat(test->file, &st), 0);
+ uint8_t *bytes = new uint8_t[st.st_size];
+ ut_assert("new failed", bytes != nullptr);
+ int fd = ::open(test->file, O_RDONLY);
+ ut_assert("open failed", fd != -1);
+ int bytes_read = ::read(fd, bytes, st.st_size);
+ ut_compare("read failed", bytes_read, st.st_size);
+ ::close(fd);
+
+ // Test case data files are created for specific boundary conditions
+ ut_compare("Test case data files are out of date", test->length, st.st_size);
+
+ payload.opcode = MavlinkFTP::kCmdOpenFileRO;
+ payload.offset = 0;
+
+ bool success = _send_receive_msg(&payload, // FTP payload header
+ strlen(test->file)+1, // size in bytes of data
+ (uint8_t*)test->file, // Data to start into FTP message payload
+ &ftp_msg, // Response from server
+ &reply); // Payload inside FTP message response
+ if (!success) {
+ return false;
+ }
+
+ ut_compare("Didn't get Ack back", reply->opcode, MavlinkFTP::kRspAck);
+
+ payload.opcode = MavlinkFTP::kCmdReadFile;
+ payload.session = reply->session;
+ payload.offset = 0;
+
+ success = _send_receive_msg(&payload, // FTP payload header
+ 0, // size in bytes of data
+ nullptr, // Data to start into FTP message payload
+ &ftp_msg, // Response from server
+ &reply); // Payload inside FTP message response
+ if (!success) {
+ return false;
+ }
+
+ ut_compare("Didn't get Ack back", reply->opcode, MavlinkFTP::kRspAck);
+ ut_compare("Offset incorrect", reply->offset, 0);
+
+ if (test->length <= MAVLINK_MSG_FILE_TRANSFER_PROTOCOL_FIELD_PAYLOAD_LEN - sizeof(MavlinkFTP::PayloadHeader)) {
+ ut_compare("Payload size incorrect", reply->size, (uint32_t)st.st_size);
+ ut_compare("File contents differ", memcmp(reply->data, bytes, st.st_size), 0);
+ }
+
+ payload.opcode = MavlinkFTP::kCmdTerminateSession;
+ payload.session = reply->session;
+ payload.size = 0;
+
+ success = _send_receive_msg(&payload, // FTP payload header
+ 0, // size in bytes of data
+ nullptr, // Data to start into FTP message payload
+ &ftp_msg, // Response from server
+ &reply); // Payload inside FTP message response
+ if (!success) {
+ return false;
+ }
+
+ ut_compare("Didn't get Ack back", reply->opcode, MavlinkFTP::kRspAck);
+ ut_compare("Incorrect payload size", reply->size, 0);
+ }
+
+ return true;
+}
+
+/// @brief Tests for correct reponse to a Read command on an invalid session.
+bool MavlinkFtpTest::_read_badsession_test(void)
+{
+ MavlinkFTP::PayloadHeader payload;
+ mavlink_file_transfer_protocol_t ftp_msg;
+ MavlinkFTP::PayloadHeader *reply;
+ const char *file = _rgReadTestCases[0].file;
+
+ payload.opcode = MavlinkFTP::kCmdOpenFileRO;
+ payload.offset = 0;
+
+ bool success = _send_receive_msg(&payload, // FTP payload header
+ strlen(file)+1, // size in bytes of data
+ (uint8_t*)file, // Data to start into FTP message payload
+ &ftp_msg, // Response from server
+ &reply); // Payload inside FTP message response
+ if (!success) {
+ return false;
+ }
+
+ ut_compare("Didn't get Ack back", reply->opcode, MavlinkFTP::kRspAck);
+
+ payload.opcode = MavlinkFTP::kCmdReadFile;
+ payload.session = reply->session + 1; // Invalid session
+ payload.offset = 0;
+
+ success = _send_receive_msg(&payload, // FTP payload header
+ 0, // size in bytes of data
+ nullptr, // Data to start into FTP message payload
+ &ftp_msg, // Response from server
+ &reply); // Payload inside FTP message response
+ if (!success) {
+ return false;
+ }
+
+ ut_compare("Didn't get Nak back", reply->opcode, MavlinkFTP::kRspNak);
+ ut_compare("Incorrect payload size", reply->size, 1);
+ ut_compare("Incorrect error code", reply->data[0], MavlinkFTP::kErrInvalidSession);
+
+ return true;
+}
+
+bool MavlinkFtpTest::_removedirectory_test(void)
+{
+ MavlinkFTP::PayloadHeader payload;
+ mavlink_file_transfer_protocol_t ftp_msg;
+ MavlinkFTP::PayloadHeader *reply;
+ int fd;
+
+ struct _testCase {
+ const char *dir;
+ bool success;
+ bool deleteFile;
+ };
+ static const struct _testCase rgTestCases[] = {
+ { "/bogus", false, false },
+ { "/etc/unit_test_data/mavlink_tests/empty_dir", false, false },
+ { _unittest_microsd_dir, false, false },
+ { _unittest_microsd_file, false, false },
+ { _unittest_microsd_dir, true, true },
+ };
+
+ ut_compare("mkdir failed", ::mkdir(_unittest_microsd_dir, S_IRWXU | S_IRWXG | S_IRWXO), 0);
+ ut_assert("open failed", (fd = ::open(_unittest_microsd_file, O_CREAT | O_EXCL)) != -1);
+ ::close(fd);
+
+ for (size_t i=0; i<sizeof(rgTestCases)/sizeof(rgTestCases[0]); i++) {
+ const struct _testCase *test = &rgTestCases[i];
+
+ if (test->deleteFile) {
+ ut_compare("unlink failed", ::unlink(_unittest_microsd_file), 0);
+ }
+
+ payload.opcode = MavlinkFTP::kCmdRemoveDirectory;
+ payload.offset = 0;
+
+ bool success = _send_receive_msg(&payload, // FTP payload header
+ strlen(test->dir)+1, // size in bytes of data
+ (uint8_t*)test->dir, // Data to start into FTP message payload
+ &ftp_msg, // Response from server
+ &reply); // Payload inside FTP message response
+ if (!success) {
+ return false;
+ }
+
+ if (test->success) {
+ ut_compare("Didn't get Ack back", reply->opcode, MavlinkFTP::kRspAck);
+ ut_compare("Incorrect payload size", reply->size, 0);
+ } else {
+ ut_compare("Didn't get Nak back", reply->opcode, MavlinkFTP::kRspNak);
+ ut_compare("Incorrect payload size", reply->size, 2);
+ ut_compare("Incorrect error code", reply->data[0], MavlinkFTP::kErrFailErrno);
+ }
+ }
+
+ return true;
+}
+
+bool MavlinkFtpTest::_createdirectory_test(void)
+{
+ MavlinkFTP::PayloadHeader payload;
+ mavlink_file_transfer_protocol_t ftp_msg;
+ MavlinkFTP::PayloadHeader *reply;
+
+ struct _testCase {
+ const char *dir;
+ bool success;
+ };
+ static const struct _testCase rgTestCases[] = {
+ { "/etc/bogus", false },
+ { _unittest_microsd_dir, true },
+ { _unittest_microsd_dir, false },
+ { "/fs/microsd/bogus/bogus", false },
+ };
+
+ for (size_t i=0; i<sizeof(rgTestCases)/sizeof(rgTestCases[0]); i++) {
+ const struct _testCase *test = &rgTestCases[i];
+
+ payload.opcode = MavlinkFTP::kCmdCreateDirectory;
+ payload.offset = 0;
+
+ bool success = _send_receive_msg(&payload, // FTP payload header
+ strlen(test->dir)+1, // size in bytes of data
+ (uint8_t*)test->dir, // Data to start into FTP message payload
+ &ftp_msg, // Response from server
+ &reply); // Payload inside FTP message response
+ if (!success) {
+ return false;
+ }
+
+ if (test->success) {
+ ut_compare("Didn't get Ack back", reply->opcode, MavlinkFTP::kRspAck);
+ ut_compare("Incorrect payload size", reply->size, 0);
+ } else {
+ ut_compare("Didn't get Nak back", reply->opcode, MavlinkFTP::kRspNak);
+ ut_compare("Incorrect payload size", reply->size, 2);
+ ut_compare("Incorrect error code", reply->data[0], MavlinkFTP::kErrFailErrno);
+ }
+ }
+
+ return true;
+}
+
+bool MavlinkFtpTest::_removefile_test(void)
+{
+ MavlinkFTP::PayloadHeader payload;
+ mavlink_file_transfer_protocol_t ftp_msg;
+ MavlinkFTP::PayloadHeader *reply;
+ int fd;
+
+ struct _testCase {
+ const char *file;
+ bool success;
+ };
+ static const struct _testCase rgTestCases[] = {
+ { "/bogus", false },
+ { _rgReadTestCases[0].file, false },
+ { _unittest_microsd_dir, false },
+ { _unittest_microsd_file, true },
+ { _unittest_microsd_file, false },
+ };
+
+ ut_compare("mkdir failed", ::mkdir(_unittest_microsd_dir, S_IRWXU | S_IRWXG | S_IRWXO), 0);
+ ut_assert("open failed", (fd = ::open(_unittest_microsd_file, O_CREAT | O_EXCL)) != -1);
+ ::close(fd);
+
+ for (size_t i=0; i<sizeof(rgTestCases)/sizeof(rgTestCases[0]); i++) {
+ const struct _testCase *test = &rgTestCases[i];
+
+ payload.opcode = MavlinkFTP::kCmdRemoveFile;
+ payload.offset = 0;
+
+ bool success = _send_receive_msg(&payload, // FTP payload header
+ strlen(test->file)+1, // size in bytes of data
+ (uint8_t*)test->file, // Data to start into FTP message payload
+ &ftp_msg, // Response from server
+ &reply); // Payload inside FTP message response
+ if (!success) {
+ return false;
+ }
+
+ if (test->success) {
+ ut_compare("Didn't get Ack back", reply->opcode, MavlinkFTP::kRspAck);
+ ut_compare("Incorrect payload size", reply->size, 0);
+ } else {
+ ut_compare("Didn't get Nak back", reply->opcode, MavlinkFTP::kRspNak);
+ ut_compare("Incorrect payload size", reply->size, 2);
+ ut_compare("Incorrect error code", reply->data[0], MavlinkFTP::kErrFailErrno);
+ }
+ }
+
+ return true;
+}
+
+/// @brief Static method used as callback from MavlinkFTP. This method will be called by MavlinkFTP when
+/// it needs to send a message out on Mavlink.
+void MavlinkFtpTest::receive_message(const mavlink_message_t *msg, MavlinkFtpTest *ftp_test)
+{
+ ftp_test->_receive_message(msg);
+}
+
+/// @brief Non-Static version of receive_message
+void MavlinkFtpTest::_receive_message(const mavlink_message_t *msg)
+{
+ // Move the message into our own member variable
+ memcpy(&_reply_msg, msg, sizeof(mavlink_message_t));
+}
+
+/// @brief Decode and validate the incoming message
+bool MavlinkFtpTest::_decode_message(const mavlink_message_t *msg, ///< Mavlink message to decode
+ mavlink_file_transfer_protocol_t *ftp_msg, ///< Decoded FTP message
+ MavlinkFTP::PayloadHeader **payload) ///< Payload inside FTP message response
+{
+ mavlink_msg_file_transfer_protocol_decode(msg, ftp_msg);
+
+ // Make sure the targets are correct
+ ut_compare("Target network non-zero", ftp_msg->target_network, 0);
+ ut_compare("Target system id mismatch", ftp_msg->target_system, clientSystemId);
+ ut_compare("Target component id mismatch", ftp_msg->target_component, clientComponentId);
+
+ *payload = reinterpret_cast<MavlinkFTP::PayloadHeader *>(ftp_msg->payload);
+
+ // Make sure we have a good sequence number
+ ut_compare("Sequence number mismatch", (*payload)->seqNumber, _lastOutgoingSeqNumber + 1);
+
+ // Bump sequence number for next outgoing message
+ _lastOutgoingSeqNumber++;
+
+ return true;
+}
+
+/// @brief Initializes an FTP message into a mavlink message
+void MavlinkFtpTest::_setup_ftp_msg(MavlinkFTP::PayloadHeader *payload_header, ///< FTP payload header
+ uint8_t size, ///< size in bytes of data
+ const uint8_t *data, ///< Data to start into FTP message payload
+ mavlink_message_t *msg) ///< Returned mavlink message
+{
+ uint8_t payload_bytes[MAVLINK_MSG_FILE_TRANSFER_PROTOCOL_FIELD_PAYLOAD_LEN];
+ MavlinkFTP::PayloadHeader *payload = reinterpret_cast<MavlinkFTP::PayloadHeader *>(payload_bytes);
+
+ memcpy(payload, payload_header, sizeof(MavlinkFTP::PayloadHeader));
+
+ payload->seqNumber = _lastOutgoingSeqNumber;
+ payload->size = size;
+ if (size != 0) {
+ memcpy(payload->data, data, size);
+ }
+
+ payload->padding[0] = 0;
+ payload->padding[1] = 0;
+
+ msg->checksum = 0;
+ mavlink_msg_file_transfer_protocol_pack(clientSystemId, // Sender system id
+ clientComponentId, // Sender component id
+ msg, // Message to pack payload into
+ 0, // Target network
+ serverSystemId, // Target system id
+ serverComponentId, // Target component id
+ payload_bytes); // Payload to pack into message
+}
+
+/// @brief Sends the specified FTP message to the server and returns response
+bool MavlinkFtpTest::_send_receive_msg(MavlinkFTP::PayloadHeader *payload_header, ///< FTP payload header
+ uint8_t size, ///< size in bytes of data
+ const uint8_t *data, ///< Data to start into FTP message payload
+ mavlink_file_transfer_protocol_t *ftp_msg_reply, ///< Response from server
+ MavlinkFTP::PayloadHeader **payload_reply) ///< Payload inside FTP message response
+{
+ mavlink_message_t msg;
+
+ _setup_ftp_msg(payload_header, size, data, &msg);
+ _ftp_server->handle_message(nullptr /* mavlink */, &msg);
+ return _decode_message(&_reply_msg, ftp_msg_reply, payload_reply);
+}
+
+/// @brief Cleans up an files created on microsd during testing
+void MavlinkFtpTest::_cleanup_microsd(void)
+{
+ ::unlink(_unittest_microsd_file);
+ ::rmdir(_unittest_microsd_dir);
+}
+
+/// @brief Runs all the unit tests
+bool MavlinkFtpTest::run_tests(void)
+{
+ ut_run_test(_ack_test);
+ ut_run_test(_bad_opcode_test);
+ ut_run_test(_bad_datasize_test);
+ ut_run_test(_list_test);
+ ut_run_test(_list_eof_test);
+ ut_run_test(_open_badfile_test);
+ ut_run_test(_open_terminate_test);
+ ut_run_test(_terminate_badsession_test);
+ ut_run_test(_read_test);
+ ut_run_test(_read_badsession_test);
+ ut_run_test(_removedirectory_test);
+ ut_run_test(_createdirectory_test);
+ ut_run_test(_removefile_test);
+
+ return (_tests_failed == 0);
+
+}
+
+ut_declare_test(mavlink_ftp_test, MavlinkFtpTest)
diff --git a/src/modules/mavlink/mavlink_tests/mavlink_ftp_test.h b/src/modules/mavlink/mavlink_tests/mavlink_ftp_test.h
new file mode 100644
index 000000000..2696192cc
--- /dev/null
+++ b/src/modules/mavlink/mavlink_tests/mavlink_ftp_test.h
@@ -0,0 +1,108 @@
+/****************************************************************************
+ *
+ * Copyright (C) 2014 PX4 Development Team. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/// @file mavlink_ftp_test.h
+/// @author Don Gagne <don@thegagnes.com>
+
+#pragma once
+
+#include <unit_test/unit_test.h>
+#include "../mavlink_bridge_header.h"
+#include "../mavlink_ftp.h"
+
+class MavlinkFtpTest : public UnitTest
+{
+public:
+ MavlinkFtpTest();
+ virtual ~MavlinkFtpTest();
+
+ virtual bool run_tests(void);
+
+ static void receive_message(const mavlink_message_t *msg, MavlinkFtpTest* ftpTest);
+
+ static const uint8_t serverSystemId = 50; ///< System ID for server
+ static const uint8_t serverComponentId = 1; ///< Component ID for server
+ static const uint8_t serverChannel = 0; ///< Channel to send to
+
+ static const uint8_t clientSystemId = 1; ///< System ID for client
+ static const uint8_t clientComponentId = 0; ///< Component ID for client
+
+ // We don't want any of these
+ MavlinkFtpTest(const MavlinkFtpTest&);
+ MavlinkFtpTest& operator=(const MavlinkFtpTest&);
+
+private:
+ virtual void _init(void);
+ virtual void _cleanup(void);
+
+ bool _ack_test(void);
+ bool _bad_opcode_test(void);
+ bool _bad_datasize_test(void);
+ bool _list_test(void);
+ bool _list_eof_test(void);
+ bool _open_badfile_test(void);
+ bool _open_terminate_test(void);
+ bool _terminate_badsession_test(void);
+ bool _read_test(void);
+ bool _read_badsession_test(void);
+ bool _removedirectory_test(void);
+ bool _createdirectory_test(void);
+ bool _removefile_test(void);
+
+ void _receive_message(const mavlink_message_t *msg);
+ void _setup_ftp_msg(MavlinkFTP::PayloadHeader *payload_header, uint8_t size, const uint8_t *data, mavlink_message_t *msg);
+ bool _decode_message(const mavlink_message_t *msg, mavlink_file_transfer_protocol_t *ftp_msg, MavlinkFTP::PayloadHeader **payload);
+ bool _send_receive_msg(MavlinkFTP::PayloadHeader *payload_header,
+ uint8_t size,
+ const uint8_t *data,
+ mavlink_file_transfer_protocol_t *ftp_msg_reply,
+ MavlinkFTP::PayloadHeader **payload_reply);
+ void _cleanup_microsd(void);
+
+ MavlinkFTP *_ftp_server;
+
+ mavlink_message_t _reply_msg;
+
+ uint16_t _lastOutgoingSeqNumber;
+
+ struct ReadTestCase {
+ const char *file;
+ const uint16_t length;
+ };
+ static const ReadTestCase _rgReadTestCases[];
+
+ static const char _unittest_microsd_dir[];
+ static const char _unittest_microsd_file[];
+};
+
+bool mavlink_ftp_test(void);
diff --git a/src/modules/mavlink/mavlink_tests/mavlink_ftp_test_data.py b/src/modules/mavlink/mavlink_tests/mavlink_ftp_test_data.py
new file mode 100644
index 000000000..a7e68f2a3
--- /dev/null
+++ b/src/modules/mavlink/mavlink_tests/mavlink_ftp_test_data.py
@@ -0,0 +1,7 @@
+import sys
+print 'Arguments: file - ' + sys.argv[1] + ', length - ' + sys.argv[2]
+file = open(sys.argv[1], 'w')
+for i in range(int(sys.argv[2])):
+ b = bytearray([i & 0xFF])
+ file.write(b)
+file.close() \ No newline at end of file
diff --git a/src/modules/navigator/navigator_state.h b/src/modules/mavlink/mavlink_tests/mavlink_tests.cpp
index 476f93414..10cbcb0ec 100644
--- a/src/modules/navigator/navigator_state.h
+++ b/src/modules/mavlink/mavlink_tests/mavlink_tests.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
*
- * Copyright (c) 2013 PX4 Development Team. All rights reserved.
+ * Copyright (C) 2014 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -32,24 +32,16 @@
****************************************************************************/
/**
- * @file navigator_state.h
- *
- * Navigator state
- *
- * @author Anton Babushkin <anton.babushkin@me.com>
+ * @file mavlink_ftp_tests.cpp
*/
-#ifndef NAVIGATOR_STATE_H_
-#define NAVIGATOR_STATE_H_
+#include <systemlib/err.h>
+
+#include "mavlink_ftp_test.h"
-typedef enum {
- NAV_STATE_NONE = 0,
- NAV_STATE_READY,
- NAV_STATE_LOITER,
- NAV_STATE_MISSION,
- NAV_STATE_RTL,
- NAV_STATE_LAND,
- NAV_STATE_MAX
-} nav_state_t;
+extern "C" __EXPORT int mavlink_tests_main(int argc, char *argv[]);
-#endif /* NAVIGATOR_STATE_H_ */
+int mavlink_tests_main(int argc, char *argv[])
+{
+ return mavlink_ftp_test() ? 0 : -1;
+}
diff --git a/src/modules/mavlink/mavlink_tests/module.mk b/src/modules/mavlink/mavlink_tests/module.mk
new file mode 100644
index 000000000..1cc28cce1
--- /dev/null
+++ b/src/modules/mavlink/mavlink_tests/module.mk
@@ -0,0 +1,50 @@
+############################################################################
+#
+# Copyright (c) 2014 PX4 Development Team. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# 3. Neither the name PX4 nor the names of its contributors may be
+# used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+############################################################################
+
+#
+# System state machine tests.
+#
+
+MODULE_COMMAND = mavlink_tests
+SRCS = mavlink_tests.cpp \
+ mavlink_ftp_test.cpp \
+ ../mavlink_ftp.cpp \
+ ../mavlink.c
+
+INCLUDE_DIRS += $(MAVLINK_SRC)/include/mavlink
+
+MODULE_STACKSIZE = 5000
+
+MAXOPTIMIZATION = -Os
+
+EXTRACXXFLAGS = -Weffc++ -DMAVLINK_FTP_UNIT_TEST
diff --git a/src/modules/mavlink/module.mk b/src/modules/mavlink/module.mk
index f532e26fe..91fdd6154 100644
--- a/src/modules/mavlink/module.mk
+++ b/src/modules/mavlink/module.mk
@@ -39,14 +39,18 @@ MODULE_COMMAND = mavlink
SRCS += mavlink_main.cpp \
mavlink.c \
mavlink_receiver.cpp \
+ mavlink_mission.cpp \
+ mavlink_parameters.cpp \
mavlink_orb_subscription.cpp \
mavlink_messages.cpp \
mavlink_stream.cpp \
mavlink_rate_limiter.cpp \
- mavlink_commands.cpp
+ mavlink_ftp.cpp
INCLUDE_DIRS += $(MAVLINK_SRC)/include/mavlink
MAXOPTIMIZATION = -Os
MODULE_STACKSIZE = 1024
+
+EXTRACXXFLAGS = -Weffc++
diff --git a/src/modules/mavlink/waypoints.h b/src/modules/mavlink/waypoints.h
deleted file mode 100644
index 532eff7aa..000000000
--- a/src/modules/mavlink/waypoints.h
+++ /dev/null
@@ -1,111 +0,0 @@
-/****************************************************************************
- *
- * Copyright (c) 2009-2014 PX4 Development Team. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * 3. Neither the name PX4 nor the names of its contributors may be
- * used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
- * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- *
- ****************************************************************************/
-
-/**
- * @file waypoints.h
- * MAVLink waypoint protocol definition (BSD-relicensed).
- *
- * @author Petri Tanskanen <petri.tanskanen@inf.ethz.ch>
- * @author Lorenz Meier <lm@inf.ethz.ch>
- * @author Thomas Gubler <thomasgubler@student.ethz.ch>
- * @author Julian Oes <joes@student.ethz.ch>
- */
-
-#ifndef WAYPOINTS_H_
-#define WAYPOINTS_H_
-
-/* This assumes you have the mavlink headers on your include path
- or in the same folder as this source file */
-
-#include <v1.0/mavlink_types.h>
-#include "mavlink_bridge_header.h"
-#include <stdbool.h>
-#include <uORB/topics/mission.h>
-
-enum MAVLINK_WPM_STATES {
- MAVLINK_WPM_STATE_IDLE = 0,
- MAVLINK_WPM_STATE_SENDLIST,
- MAVLINK_WPM_STATE_SENDLIST_SENDWPS,
- MAVLINK_WPM_STATE_GETLIST,
- MAVLINK_WPM_STATE_GETLIST_GETWPS,
- MAVLINK_WPM_STATE_GETLIST_GOTALL,
- MAVLINK_WPM_STATE_ENUM_END
-};
-
-enum MAVLINK_WPM_CODES {
- MAVLINK_WPM_CODE_OK = 0,
- MAVLINK_WPM_CODE_ERR_WAYPOINT_ACTION_NOT_SUPPORTED,
- MAVLINK_WPM_CODE_ERR_WAYPOINT_FRAME_NOT_SUPPORTED,
- MAVLINK_WPM_CODE_ERR_WAYPOINT_OUT_OF_BOUNDS,
- MAVLINK_WPM_CODE_ERR_WAYPOINT_MAX_NUMBER_EXCEEDED,
- MAVLINK_WPM_CODE_ENUM_END
-};
-
-
-#define MAVLINK_WPM_MAX_WP_COUNT 255
-#define MAVLINK_WPM_PROTOCOL_TIMEOUT_DEFAULT 5000000 ///< Protocol communication timeout in useconds
-#define MAVLINK_WPM_SETPOINT_DELAY_DEFAULT 1000000 ///< When to send a new setpoint
-#define MAVLINK_WPM_PROTOCOL_DELAY_DEFAULT 40000
-
-
-struct mavlink_wpm_storage {
- uint16_t size;
- uint16_t max_size;
- enum MAVLINK_WPM_STATES current_state;
- int16_t current_wp_id; ///< Waypoint in current transmission
- uint16_t current_count;
- uint8_t current_partner_sysid;
- uint8_t current_partner_compid;
- uint64_t timestamp_lastaction;
- uint64_t timestamp_last_send_setpoint;
- uint32_t timeout;
- int current_dataman_id;
-};
-
-typedef struct mavlink_wpm_storage mavlink_wpm_storage;
-
-int map_mavlink_mission_item_to_mission_item(const mavlink_mission_item_t *mavlink_mission_item, struct mission_item_s *mission_item);
-int map_mission_item_to_mavlink_mission_item(const struct mission_item_s *mission_item, mavlink_mission_item_t *mavlink_mission_item);
-
-
-void mavlink_wpm_init(mavlink_wpm_storage *state);
-void mavlink_waypoint_eventloop(uint64_t now);
-void mavlink_wpm_message_handler(const mavlink_message_t *msg);
-
-extern void mavlink_missionlib_current_waypoint_changed(uint16_t index, float param1,
- float param2, float param3, float param4, float param5_lat_x,
- float param6_lon_y, float param7_alt_z, uint8_t frame, uint16_t command);
-
-static uint8_t missionlib_msg_buf[MAVLINK_MAX_PACKET_LEN];
-
-#endif /* WAYPOINTS_H_ */
diff --git a/src/modules/mc_att_control/mc_att_control_main.cpp b/src/modules/mc_att_control/mc_att_control_main.cpp
index b024589a6..81a13edb8 100644
--- a/src/modules/mc_att_control/mc_att_control_main.cpp
+++ b/src/modules/mc_att_control/mc_att_control_main.cpp
@@ -60,8 +60,6 @@
#include <drivers/drv_hrt.h>
#include <arch/board/board.h>
#include <uORB/uORB.h>
-#include <uORB/Subscription.hpp>
-#include <uORB/Publication.hpp>
#include <uORB/topics/vehicle_attitude_setpoint.h>
#include <uORB/topics/manual_control_setpoint.h>
#include <uORB/topics/actuator_controls.h>
@@ -74,6 +72,7 @@
#include <systemlib/err.h>
#include <systemlib/perf_counter.h>
#include <systemlib/systemlib.h>
+#include <systemlib/circuit_breaker.h>
#include <lib/mathlib/mathlib.h>
#include <lib/geo/geo.h>
@@ -113,17 +112,19 @@ private:
bool _task_should_exit; /**< if true, sensor task should exit */
int _control_task; /**< task handle for sensor task */
- uORB::SubscriptionTiny _v_att_sub; /**< vehicle attitude subscription */
- uORB::SubscriptionTiny _v_att_sp_sub; /**< vehicle attitude setpoint subscription */
- uORB::SubscriptionTiny _v_rates_sp_sub; /**< vehicle rates setpoint subscription */
- uORB::SubscriptionTiny _v_control_mode_sub; /**< vehicle control mode subscription */
- uORB::SubscriptionTiny _params_sub; /**< parameter updates subscription */
- uORB::SubscriptionTiny _manual_control_sp_sub; /**< manual control setpoint subscription */
- uORB::SubscriptionTiny _armed_sub; /**< arming status subscription */
+ int _v_att_sub; /**< vehicle attitude subscription */
+ int _v_att_sp_sub; /**< vehicle attitude setpoint subscription */
+ int _v_rates_sp_sub; /**< vehicle rates setpoint subscription */
+ int _v_control_mode_sub; /**< vehicle control mode subscription */
+ int _params_sub; /**< parameter updates subscription */
+ int _manual_control_sp_sub; /**< manual control setpoint subscription */
+ int _armed_sub; /**< arming status subscription */
- uORB::PublicationTiny _att_sp_pub; /**< attitude setpoint publication */
- uORB::PublicationTiny _v_rates_sp_pub; /**< rate setpoint publication */
- uORB::PublicationTiny _actuators_0_pub; /**< attitude actuator controls publication */
+ orb_advert_t _att_sp_pub; /**< attitude setpoint publication */
+ orb_advert_t _v_rates_sp_pub; /**< rate setpoint publication */
+ orb_advert_t _actuators_0_pub; /**< attitude actuator controls publication */
+
+ bool _actuators_0_circuit_breaker_enabled; /**< circuit breaker to suppress output */
struct vehicle_attitude_s _v_att; /**< vehicle attitude */
struct vehicle_attitude_setpoint_s _v_att_sp; /**< vehicle attitude setpoint */
@@ -189,6 +190,36 @@ private:
int parameters_update();
/**
+ * Check for parameter update and handle it.
+ */
+ void parameter_update_poll();
+
+ /**
+ * Check for changes in vehicle control mode.
+ */
+ void vehicle_control_mode_poll();
+
+ /**
+ * Check for changes in manual inputs.
+ */
+ void vehicle_manual_poll();
+
+ /**
+ * Check for attitude setpoint updates.
+ */
+ void vehicle_attitude_setpoint_poll();
+
+ /**
+ * Check for rates setpoint updates.
+ */
+ void vehicle_rates_setpoint_poll();
+
+ /**
+ * Check for arming status updates.
+ */
+ void arming_status_poll();
+
+ /**
* Attitude controller.
*/
void control_attitude(float dt);
@@ -227,18 +258,19 @@ MulticopterAttitudeControl::MulticopterAttitudeControl() :
_control_task(-1),
/* subscriptions */
- _v_att_sub(ORB_ID(vehicle_attitude)),
- _v_att_sp_sub(ORB_ID(vehicle_attitude_setpoint)),
- _v_rates_sp_sub(ORB_ID(vehicle_rates_setpoint)),
- _v_control_mode_sub(ORB_ID(vehicle_control_mode)),
- _params_sub(ORB_ID(parameter_update)),
- _manual_control_sp_sub(ORB_ID(manual_control_setpoint)),
- _armed_sub(ORB_ID(actuator_armed)),
+ _v_att_sub(-1),
+ _v_att_sp_sub(-1),
+ _v_control_mode_sub(-1),
+ _params_sub(-1),
+ _manual_control_sp_sub(-1),
+ _armed_sub(-1),
/* publications */
- _att_sp_pub(ORB_ID(vehicle_attitude_setpoint)),
- _v_rates_sp_pub(ORB_ID(vehicle_rates_setpoint)),
- _actuators_0_pub(ORB_ID_VEHICLE_ATTITUDE_CONTROLS),
+ _att_sp_pub(-1),
+ _v_rates_sp_pub(-1),
+ _actuators_0_pub(-1),
+
+ _actuators_0_circuit_breaker_enabled(false),
/* performance counters */
_loop_perf(perf_alloc(PC_ELAPSED, "mc_att_control"))
@@ -375,9 +407,88 @@ MulticopterAttitudeControl::parameters_update()
param_get(_params_handles.acro_yaw_max, &v);
_params.acro_rate_max(2) = math::radians(v);
+ _actuators_0_circuit_breaker_enabled = circuit_breaker_enabled("CBRK_RATE_CTRL", CBRK_RATE_CTRL_KEY);
+
return OK;
}
+void
+MulticopterAttitudeControl::parameter_update_poll()
+{
+ bool updated;
+
+ /* Check HIL state if vehicle status has changed */
+ orb_check(_params_sub, &updated);
+
+ if (updated) {
+ struct parameter_update_s param_update;
+ orb_copy(ORB_ID(parameter_update), _params_sub, &param_update);
+ parameters_update();
+ }
+}
+
+void
+MulticopterAttitudeControl::vehicle_control_mode_poll()
+{
+ bool updated;
+
+ /* Check HIL state if vehicle status has changed */
+ orb_check(_v_control_mode_sub, &updated);
+
+ if (updated) {
+ orb_copy(ORB_ID(vehicle_control_mode), _v_control_mode_sub, &_v_control_mode);
+ }
+}
+
+void
+MulticopterAttitudeControl::vehicle_manual_poll()
+{
+ bool updated;
+
+ /* get pilots inputs */
+ orb_check(_manual_control_sp_sub, &updated);
+
+ if (updated) {
+ orb_copy(ORB_ID(manual_control_setpoint), _manual_control_sp_sub, &_manual_control_sp);
+ }
+}
+
+void
+MulticopterAttitudeControl::vehicle_attitude_setpoint_poll()
+{
+ /* check if there is a new setpoint */
+ bool updated;
+ orb_check(_v_att_sp_sub, &updated);
+
+ if (updated) {
+ orb_copy(ORB_ID(vehicle_attitude_setpoint), _v_att_sp_sub, &_v_att_sp);
+ }
+}
+
+void
+MulticopterAttitudeControl::vehicle_rates_setpoint_poll()
+{
+ /* check if there is a new setpoint */
+ bool updated;
+ orb_check(_v_rates_sp_sub, &updated);
+
+ if (updated) {
+ orb_copy(ORB_ID(vehicle_rates_setpoint), _v_rates_sp_sub, &_v_rates_sp);
+ }
+}
+
+void
+MulticopterAttitudeControl::arming_status_poll()
+{
+ /* check if there is a new setpoint */
+ bool updated;
+ orb_check(_armed_sub, &updated);
+
+ if (updated) {
+ orb_copy(ORB_ID(actuator_armed), _armed_sub, &_armed);
+ }
+}
+
/*
* Attitude controller.
* Input: 'manual_control_setpoint' and 'vehicle_attitude_setpoint' topics (depending on mode)
@@ -394,7 +505,7 @@ MulticopterAttitudeControl::control_attitude(float dt)
if (_v_control_mode.flag_control_velocity_enabled || _v_control_mode.flag_control_climb_rate_enabled) {
/* in assisted modes poll 'vehicle_attitude_setpoint' topic and modify it */
- (void) _v_att_sp_sub.update(&_v_att_sp);
+ vehicle_attitude_setpoint_poll();
}
if (!_v_control_mode.flag_control_climb_rate_enabled) {
@@ -449,7 +560,7 @@ MulticopterAttitudeControl::control_attitude(float dt)
} else {
/* in non-manual mode use 'vehicle_attitude_setpoint' topic */
- (void) _v_att_sp_sub.update(&_v_att_sp);
+ vehicle_attitude_setpoint_poll();
/* reset yaw setpoint after non-manual control mode */
_reset_yaw_sp = true;
@@ -477,7 +588,12 @@ MulticopterAttitudeControl::control_attitude(float dt)
if (publish_att_sp) {
_v_att_sp.timestamp = hrt_absolute_time();
- _att_sp_pub.update(&_v_att_sp);
+ if (_att_sp_pub > 0) {
+ orb_publish(ORB_ID(vehicle_attitude_setpoint), _att_sp_pub, &_v_att_sp);
+
+ } else {
+ _att_sp_pub = orb_advertise(ORB_ID(vehicle_attitude_setpoint), &_v_att_sp);
+ }
}
/* rotation matrix for current state */
@@ -605,8 +721,17 @@ MulticopterAttitudeControl::task_main_trampoline(int argc, char *argv[])
void
MulticopterAttitudeControl::task_main()
{
- warnx("started");
- fflush(stdout);
+
+ /*
+ * do subscriptions
+ */
+ _v_att_sp_sub = orb_subscribe(ORB_ID(vehicle_attitude_setpoint));
+ _v_rates_sp_sub = orb_subscribe(ORB_ID(vehicle_rates_setpoint));
+ _v_att_sub = orb_subscribe(ORB_ID(vehicle_attitude));
+ _v_control_mode_sub = orb_subscribe(ORB_ID(vehicle_control_mode));
+ _params_sub = orb_subscribe(ORB_ID(parameter_update));
+ _manual_control_sp_sub = orb_subscribe(ORB_ID(manual_control_setpoint));
+ _armed_sub = orb_subscribe(ORB_ID(actuator_armed));
/* initialize parameters cache */
parameters_update();
@@ -614,7 +739,7 @@ MulticopterAttitudeControl::task_main()
/* wakeup source: vehicle attitude */
struct pollfd fds[1];
- fds[0].fd = _v_att_sub.getHandle();
+ fds[0].fd = _v_att_sub;
fds[0].events = POLLIN;
while (!_task_should_exit) {
@@ -651,15 +776,13 @@ MulticopterAttitudeControl::task_main()
}
/* copy attitude topic */
- _v_att_sub.update(&_v_att);
+ orb_copy(ORB_ID(vehicle_attitude), _v_att_sub, &_v_att);
/* check for updates in other topics */
- if (_params_sub.updated()) {
- parameters_update();
- }
- (void) _v_control_mode_sub.update(&_v_control_mode);
- (void) _armed_sub.update(&_armed);
- (void) _manual_control_sp_sub.update(&_manual_control_sp);
+ parameter_update_poll();
+ vehicle_control_mode_poll();
+ arming_status_poll();
+ vehicle_manual_poll();
if (_v_control_mode.flag_control_attitude_enabled) {
control_attitude(dt);
@@ -671,7 +794,12 @@ MulticopterAttitudeControl::task_main()
_v_rates_sp.thrust = _thrust_sp;
_v_rates_sp.timestamp = hrt_absolute_time();
- _v_rates_sp_pub.update(&_v_rates_sp);
+ if (_v_rates_sp_pub > 0) {
+ orb_publish(ORB_ID(vehicle_rates_setpoint), _v_rates_sp_pub, &_v_rates_sp);
+
+ } else {
+ _v_rates_sp_pub = orb_advertise(ORB_ID(vehicle_rates_setpoint), &_v_rates_sp);
+ }
} else {
/* attitude controller disabled, poll rates setpoint topic */
@@ -690,11 +818,16 @@ MulticopterAttitudeControl::task_main()
_v_rates_sp.thrust = _thrust_sp;
_v_rates_sp.timestamp = hrt_absolute_time();
- _v_rates_sp_pub.update(&_v_rates_sp);
+ if (_v_rates_sp_pub > 0) {
+ orb_publish(ORB_ID(vehicle_rates_setpoint), _v_rates_sp_pub, &_v_rates_sp);
+
+ } else {
+ _v_rates_sp_pub = orb_advertise(ORB_ID(vehicle_rates_setpoint), &_v_rates_sp);
+ }
} else {
/* attitude controller disabled, poll rates setpoint topic */
- _v_rates_sp_sub.update(&_v_rates_sp);
+ vehicle_rates_setpoint_poll();
_rates_sp(0) = _v_rates_sp.roll;
_rates_sp(1) = _v_rates_sp.pitch;
_rates_sp(2) = _v_rates_sp.yaw;
@@ -712,7 +845,14 @@ MulticopterAttitudeControl::task_main()
_actuators.control[3] = (isfinite(_thrust_sp)) ? _thrust_sp : 0.0f;
_actuators.timestamp = hrt_absolute_time();
- _actuators_0_pub.update(&_actuators);
+ if (!_actuators_0_circuit_breaker_enabled) {
+ if (_actuators_0_pub > 0) {
+ orb_publish(ORB_ID(actuator_controls_0), _actuators_0_pub, &_actuators);
+
+ } else {
+ _actuators_0_pub = orb_advertise(ORB_ID(actuator_controls_0), &_actuators);
+ }
+ }
}
}
diff --git a/src/modules/mc_pos_control/mc_pos_control_main.cpp b/src/modules/mc_pos_control/mc_pos_control_main.cpp
index 09960d106..5918d6bc5 100644
--- a/src/modules/mc_pos_control/mc_pos_control_main.cpp
+++ b/src/modules/mc_pos_control/mc_pos_control_main.cpp
@@ -76,6 +76,7 @@
#define TILT_COS_MAX 0.7f
#define SIGMA 0.000001f
+#define MIN_DIST 0.01f
/**
* Multicopter position control app start / stop handling function
@@ -105,34 +106,38 @@ public:
int start();
private:
+ const float alt_ctl_dz = 0.2f;
- bool _task_should_exit; /**< if true, task should exit */
+ bool _task_should_exit; /**< if true, task should exit */
int _control_task; /**< task handle for task */
int _mavlink_fd; /**< mavlink fd */
int _att_sub; /**< vehicle attitude subscription */
int _att_sp_sub; /**< vehicle attitude setpoint */
int _control_mode_sub; /**< vehicle control mode subscription */
- int _params_sub; /**< notification of parameter updates */
- int _manual_sub; /**< notification of manual control updates */
+ int _params_sub; /**< notification of parameter updates */
+ int _manual_sub; /**< notification of manual control updates */
int _arming_sub; /**< arming status of outputs */
int _local_pos_sub; /**< vehicle local position */
- int _pos_sp_triplet_sub; /**< position setpoint triplet */
+ int _pos_sp_triplet_sub; /**< position setpoint triplet */
+ int _local_pos_sp_sub; /**< offboard local position setpoint */
+ int _global_vel_sp_sub; /**< offboard global velocity setpoint */
orb_advert_t _att_sp_pub; /**< attitude setpoint publication */
orb_advert_t _local_pos_sp_pub; /**< vehicle local position setpoint publication */
orb_advert_t _global_vel_sp_pub; /**< vehicle global velocity setpoint publication */
struct vehicle_attitude_s _att; /**< vehicle attitude */
- struct vehicle_attitude_setpoint_s _att_sp; /**< vehicle attitude setpoint */
- struct manual_control_setpoint_s _manual; /**< r/c channel data */
- struct vehicle_control_mode_s _control_mode; /**< vehicle control mode */
+ struct vehicle_attitude_setpoint_s _att_sp; /**< vehicle attitude setpoint */
+ struct manual_control_setpoint_s _manual; /**< r/c channel data */
+ struct vehicle_control_mode_s _control_mode; /**< vehicle control mode */
struct actuator_armed_s _arming; /**< actuator arming status */
- struct vehicle_local_position_s _local_pos; /**< vehicle local position */
+ struct vehicle_local_position_s _local_pos; /**< vehicle local position */
struct position_setpoint_triplet_s _pos_sp_triplet; /**< vehicle global position setpoint triplet */
struct vehicle_local_position_setpoint_s _local_pos_sp; /**< vehicle local position setpoint */
struct vehicle_global_velocity_setpoint_s _global_vel_sp; /**< vehicle global velocity setpoint */
+
struct {
param_t thr_min;
param_t thr_max;
@@ -175,12 +180,15 @@ private:
bool _reset_pos_sp;
bool _reset_alt_sp;
+ bool _mode_auto;
math::Vector<3> _pos;
math::Vector<3> _pos_sp;
math::Vector<3> _vel;
math::Vector<3> _vel_sp;
math::Vector<3> _vel_prev; /**< velocity on previous step */
+ math::Vector<3> _vel_ff;
+ math::Vector<3> _sp_move_rate;
/**
* Update our local parameter cache.
@@ -214,6 +222,29 @@ private:
void reset_alt_sp();
/**
+ * Check if position setpoint is too far from current position and adjust it if needed.
+ */
+ void limit_pos_sp_offset();
+
+ /**
+ * Set position setpoint using manual control
+ */
+ void control_manual(float dt);
+
+ /**
+ * Set position setpoint using offboard control
+ */
+ void control_offboard(float dt);
+
+ bool cross_sphere_line(const math::Vector<3>& sphere_c, float sphere_r,
+ const math::Vector<3> line_a, const math::Vector<3> line_b, math::Vector<3>& res);
+
+ /**
+ * Set position setpoint for AUTO
+ */
+ void control_auto(float dt);
+
+ /**
* Select between barometric and global (AMSL) altitudes
*/
void select_alt(bool global);
@@ -256,6 +287,7 @@ MulticopterPositionControl::MulticopterPositionControl() :
_arming_sub(-1),
_local_pos_sub(-1),
_pos_sp_triplet_sub(-1),
+ _global_vel_sp_sub(-1),
/* publications */
_att_sp_pub(-1),
@@ -266,7 +298,8 @@ MulticopterPositionControl::MulticopterPositionControl() :
_ref_timestamp(0),
_reset_pos_sp(true),
- _reset_alt_sp(true)
+ _reset_alt_sp(true),
+ _mode_auto(false)
{
memset(&_att, 0, sizeof(_att));
memset(&_att_sp, 0, sizeof(_att_sp));
@@ -293,6 +326,8 @@ MulticopterPositionControl::MulticopterPositionControl() :
_vel.zero();
_vel_sp.zero();
_vel_prev.zero();
+ _vel_ff.zero();
+ _sp_move_rate.zero();
_params_handles.thr_min = param_find("MPC_THR_MIN");
_params_handles.thr_max = param_find("MPC_THR_MAX");
@@ -388,9 +423,11 @@ MulticopterPositionControl::parameters_update(bool force)
param_get(_params_handles.z_vel_max, &v);
_params.vel_max(2) = v;
param_get(_params_handles.xy_ff, &v);
+ v = math::constrain(v, 0.0f, 1.0f);
_params.vel_ff(0) = v;
_params.vel_ff(1) = v;
param_get(_params_handles.z_ff, &v);
+ v = math::constrain(v, 0.0f, 1.0f);
_params.vel_ff(2) = v;
_params.sp_offs_max = _params.vel_max.edivide(_params.pos_p) * 2.0f;
@@ -466,7 +503,7 @@ MulticopterPositionControl::update_ref()
{
if (_local_pos.ref_timestamp != _ref_timestamp) {
double lat_sp, lon_sp;
- float alt_sp;
+ float alt_sp = 0.0f;
if (_ref_timestamp != 0) {
/* calculate current position setpoint in global frame */
@@ -493,9 +530,12 @@ MulticopterPositionControl::reset_pos_sp()
{
if (_reset_pos_sp) {
_reset_pos_sp = false;
- _pos_sp(0) = _pos(0);
- _pos_sp(1) = _pos(1);
- mavlink_log_info(_mavlink_fd, "[mpc] reset pos sp: %.2f, %.2f", (double)_pos_sp(0), (double)_pos_sp(1));
+ /* shift position setpoint to make attitude setpoint continuous */
+ _pos_sp(0) = _pos(0) + (_vel(0) - _att_sp.R_body[0][2] * _att_sp.thrust / _params.vel_p(0)
+ - _params.vel_ff(0) * _sp_move_rate(0)) / _params.pos_p(0);
+ _pos_sp(1) = _pos(1) + (_vel(1) - _att_sp.R_body[1][2] * _att_sp.thrust / _params.vel_p(1)
+ - _params.vel_ff(1) * _sp_move_rate(1)) / _params.pos_p(1);
+ mavlink_log_info(_mavlink_fd, "[mpc] reset pos sp: %d, %d", (int)_pos_sp(0), (int)_pos_sp(1));
}
}
@@ -504,18 +544,321 @@ MulticopterPositionControl::reset_alt_sp()
{
if (_reset_alt_sp) {
_reset_alt_sp = false;
- _pos_sp(2) = _pos(2);
- mavlink_log_info(_mavlink_fd, "[mpc] reset alt sp: %.2f", -(double)_pos_sp(2));
+ _pos_sp(2) = _pos(2) + (_vel(2) - _params.vel_ff(2) * _sp_move_rate(2)) / _params.pos_p(2);
+ mavlink_log_info(_mavlink_fd, "[mpc] reset alt sp: %d", -(int)_pos_sp(2));
+ }
+}
+
+void
+MulticopterPositionControl::limit_pos_sp_offset()
+{
+ math::Vector<3> pos_sp_offs;
+ pos_sp_offs.zero();
+
+ if (_control_mode.flag_control_position_enabled) {
+ pos_sp_offs(0) = (_pos_sp(0) - _pos(0)) / _params.sp_offs_max(0);
+ pos_sp_offs(1) = (_pos_sp(1) - _pos(1)) / _params.sp_offs_max(1);
+ }
+
+ if (_control_mode.flag_control_altitude_enabled) {
+ pos_sp_offs(2) = (_pos_sp(2) - _pos(2)) / _params.sp_offs_max(2);
+ }
+
+ float pos_sp_offs_norm = pos_sp_offs.length();
+
+ if (pos_sp_offs_norm > 1.0f) {
+ pos_sp_offs /= pos_sp_offs_norm;
+ _pos_sp = _pos + pos_sp_offs.emult(_params.sp_offs_max);
+ }
+}
+
+void
+MulticopterPositionControl::control_manual(float dt)
+{
+ _sp_move_rate.zero();
+
+ if (_control_mode.flag_control_altitude_enabled) {
+ /* move altitude setpoint with throttle stick */
+ _sp_move_rate(2) = -scale_control(_manual.z - 0.5f, 0.5f, alt_ctl_dz);
+ }
+
+ if (_control_mode.flag_control_position_enabled) {
+ /* move position setpoint with roll/pitch stick */
+ _sp_move_rate(0) = _manual.x;
+ _sp_move_rate(1) = _manual.y;
+ }
+
+ /* limit setpoint move rate */
+ float sp_move_norm = _sp_move_rate.length();
+
+ if (sp_move_norm > 1.0f) {
+ _sp_move_rate /= sp_move_norm;
+ }
+
+ /* _sp_move_rate scaled to 0..1, scale it to max speed and rotate around yaw */
+ math::Matrix<3, 3> R_yaw_sp;
+ R_yaw_sp.from_euler(0.0f, 0.0f, _att_sp.yaw_body);
+ _sp_move_rate = R_yaw_sp * _sp_move_rate.emult(_params.vel_max);
+
+ if (_control_mode.flag_control_altitude_enabled) {
+ /* reset alt setpoint to current altitude if needed */
+ reset_alt_sp();
+ }
+
+ if (_control_mode.flag_control_position_enabled) {
+ /* reset position setpoint to current position if needed */
+ reset_pos_sp();
+ }
+
+ /* feed forward setpoint move rate with weight vel_ff */
+ _vel_ff = _sp_move_rate.emult(_params.vel_ff);
+
+ /* move position setpoint */
+ _pos_sp += _sp_move_rate * dt;
+
+ /* check if position setpoint is too far from actual position */
+ math::Vector<3> pos_sp_offs;
+ pos_sp_offs.zero();
+
+ if (_control_mode.flag_control_position_enabled) {
+ pos_sp_offs(0) = (_pos_sp(0) - _pos(0)) / _params.sp_offs_max(0);
+ pos_sp_offs(1) = (_pos_sp(1) - _pos(1)) / _params.sp_offs_max(1);
+ }
+
+ if (_control_mode.flag_control_altitude_enabled) {
+ pos_sp_offs(2) = (_pos_sp(2) - _pos(2)) / _params.sp_offs_max(2);
+ }
+
+ float pos_sp_offs_norm = pos_sp_offs.length();
+
+ if (pos_sp_offs_norm > 1.0f) {
+ pos_sp_offs /= pos_sp_offs_norm;
+ _pos_sp = _pos + pos_sp_offs.emult(_params.sp_offs_max);
+ }
+}
+
+void
+MulticopterPositionControl::control_offboard(float dt)
+{
+ bool updated;
+ orb_check(_pos_sp_triplet_sub, &updated);
+
+ if (updated) {
+ orb_copy(ORB_ID(position_setpoint_triplet), _pos_sp_triplet_sub, &_pos_sp_triplet);
+ }
+
+ if (_pos_sp_triplet.current.valid) {
+ if (_control_mode.flag_control_position_enabled && _pos_sp_triplet.current.position_valid) {
+ /* control position */
+ _pos_sp(0) = _pos_sp_triplet.current.x;
+ _pos_sp(1) = _pos_sp_triplet.current.y;
+ _pos_sp(2) = _pos_sp_triplet.current.z;
+
+ } else if (_control_mode.flag_control_velocity_enabled && _pos_sp_triplet.current.velocity_valid) {
+ /* control velocity */
+ /* reset position setpoint to current position if needed */
+ reset_pos_sp();
+
+ /* set position setpoint move rate */
+ _sp_move_rate(0) = _pos_sp_triplet.current.vx;
+ _sp_move_rate(1) = _pos_sp_triplet.current.vy;
+ }
+
+ if (_pos_sp_triplet.current.yaw_valid) {
+ _att_sp.yaw_body = _pos_sp_triplet.current.yaw;
+ } else if (_pos_sp_triplet.current.yawspeed_valid) {
+ _att_sp.yaw_body = _att_sp.yaw_body + _pos_sp_triplet.current.yawspeed * dt;
+ }
+
+ if (_control_mode.flag_control_altitude_enabled) {
+ /* reset alt setpoint to current altitude if needed */
+ reset_alt_sp();
+
+ /* set altitude setpoint move rate */
+ _sp_move_rate(2) = _pos_sp_triplet.current.vz;
+ }
+
+ /* feed forward setpoint move rate with weight vel_ff */
+ _vel_ff = _sp_move_rate.emult(_params.vel_ff);
+
+ /* move position setpoint */
+ _pos_sp += _sp_move_rate * dt;
+
+ } else {
+ reset_pos_sp();
+ reset_alt_sp();
+ }
+}
+
+bool
+MulticopterPositionControl::cross_sphere_line(const math::Vector<3>& sphere_c, float sphere_r,
+ const math::Vector<3> line_a, const math::Vector<3> line_b, math::Vector<3>& res)
+{
+ /* project center of sphere on line */
+ /* normalized AB */
+ math::Vector<3> ab_norm = line_b - line_a;
+ ab_norm.normalize();
+ math::Vector<3> d = line_a + ab_norm * ((sphere_c - line_a) * ab_norm);
+ float cd_len = (sphere_c - d).length();
+
+ /* we have triangle CDX with known CD and CX = R, find DX */
+ if (sphere_r > cd_len) {
+ /* have two roots, select one in A->B direction from D */
+ float dx_len = sqrtf(sphere_r * sphere_r - cd_len * cd_len);
+ res = d + ab_norm * dx_len;
+ return true;
+
+ } else {
+ /* have no roots, return D */
+ res = d;
+ return false;
+ }
+}
+
+void
+MulticopterPositionControl::control_auto(float dt)
+{
+ if (!_mode_auto) {
+ _mode_auto = true;
+ /* reset position setpoint on AUTO mode activation */
+ reset_pos_sp();
+ reset_alt_sp();
+ }
+
+ bool updated;
+ orb_check(_pos_sp_triplet_sub, &updated);
+
+ if (updated) {
+ orb_copy(ORB_ID(position_setpoint_triplet), _pos_sp_triplet_sub, &_pos_sp_triplet);
+ }
+
+ if (_pos_sp_triplet.current.valid) {
+ /* in case of interrupted mission don't go to waypoint but stay at current position */
+ _reset_pos_sp = true;
+ _reset_alt_sp = true;
+
+ /* project setpoint to local frame */
+ math::Vector<3> curr_sp;
+ map_projection_project(&_ref_pos,
+ _pos_sp_triplet.current.lat, _pos_sp_triplet.current.lon,
+ &curr_sp.data[0], &curr_sp.data[1]);
+ curr_sp(2) = -(_pos_sp_triplet.current.alt - _ref_alt);
+
+ /* scaled space: 1 == position error resulting max allowed speed, L1 = 1 in this space */
+ math::Vector<3> scale = _params.pos_p.edivide(_params.vel_max); // TODO add mult param here
+
+ /* convert current setpoint to scaled space */
+ math::Vector<3> curr_sp_s = curr_sp.emult(scale);
+
+ /* by default use current setpoint as is */
+ math::Vector<3> pos_sp_s = curr_sp_s;
+
+ if (_pos_sp_triplet.current.type == SETPOINT_TYPE_POSITION && _pos_sp_triplet.previous.valid) {
+ /* follow "previous - current" line */
+ math::Vector<3> prev_sp;
+ map_projection_project(&_ref_pos,
+ _pos_sp_triplet.previous.lat, _pos_sp_triplet.previous.lon,
+ &prev_sp.data[0], &prev_sp.data[1]);
+ prev_sp(2) = -(_pos_sp_triplet.previous.alt - _ref_alt);
+
+ if ((curr_sp - prev_sp).length() > MIN_DIST) {
+
+ /* find X - cross point of L1 sphere and trajectory */
+ math::Vector<3> pos_s = _pos.emult(scale);
+ math::Vector<3> prev_sp_s = prev_sp.emult(scale);
+ math::Vector<3> prev_curr_s = curr_sp_s - prev_sp_s;
+ math::Vector<3> curr_pos_s = pos_s - curr_sp_s;
+ float curr_pos_s_len = curr_pos_s.length();
+ if (curr_pos_s_len < 1.0f) {
+ /* copter is closer to waypoint than L1 radius */
+ /* check next waypoint and use it to avoid slowing down when passing via waypoint */
+ if (_pos_sp_triplet.next.valid) {
+ math::Vector<3> next_sp;
+ map_projection_project(&_ref_pos,
+ _pos_sp_triplet.next.lat, _pos_sp_triplet.next.lon,
+ &next_sp.data[0], &next_sp.data[1]);
+ next_sp(2) = -(_pos_sp_triplet.next.alt - _ref_alt);
+
+ if ((next_sp - curr_sp).length() > MIN_DIST) {
+ math::Vector<3> next_sp_s = next_sp.emult(scale);
+
+ /* calculate angle prev - curr - next */
+ math::Vector<3> curr_next_s = next_sp_s - curr_sp_s;
+ math::Vector<3> prev_curr_s_norm = prev_curr_s.normalized();
+
+ /* cos(a) * curr_next, a = angle between current and next trajectory segments */
+ float cos_a_curr_next = prev_curr_s_norm * curr_next_s;
+
+ /* cos(b), b = angle pos - curr_sp - prev_sp */
+ float cos_b = -curr_pos_s * prev_curr_s_norm / curr_pos_s_len;
+
+ if (cos_a_curr_next > 0.0f && cos_b > 0.0f) {
+ float curr_next_s_len = curr_next_s.length();
+ /* if curr - next distance is larger than L1 radius, limit it */
+ if (curr_next_s_len > 1.0f) {
+ cos_a_curr_next /= curr_next_s_len;
+ }
+
+ /* feed forward position setpoint offset */
+ math::Vector<3> pos_ff = prev_curr_s_norm *
+ cos_a_curr_next * cos_b * cos_b * (1.0f - curr_pos_s_len) *
+ (1.0f - expf(-curr_pos_s_len * curr_pos_s_len * 20.0f));
+ pos_sp_s += pos_ff;
+ }
+ }
+ }
+
+ } else {
+ bool near = cross_sphere_line(pos_s, 1.0f, prev_sp_s, curr_sp_s, pos_sp_s);
+ if (near) {
+ /* L1 sphere crosses trajectory */
+
+ } else {
+ /* copter is too far from trajectory */
+ /* if copter is behind prev waypoint, go directly to prev waypoint */
+ if ((pos_sp_s - prev_sp_s) * prev_curr_s < 0.0f) {
+ pos_sp_s = prev_sp_s;
+ }
+
+ /* if copter is in front of curr waypoint, go directly to curr waypoint */
+ if ((pos_sp_s - curr_sp_s) * prev_curr_s > 0.0f) {
+ pos_sp_s = curr_sp_s;
+ }
+
+ pos_sp_s = pos_s + (pos_sp_s - pos_s).normalized();
+ }
+ }
+ }
+ }
+
+ /* move setpoint not faster than max allowed speed */
+ math::Vector<3> pos_sp_old_s = _pos_sp.emult(scale);
+
+ /* difference between current and desired position setpoints, 1 = max speed */
+ math::Vector<3> d_pos_m = (pos_sp_s - pos_sp_old_s).edivide(_params.pos_p);
+ float d_pos_m_len = d_pos_m.length();
+ if (d_pos_m_len > dt) {
+ pos_sp_s = pos_sp_old_s + (d_pos_m / d_pos_m_len * dt).emult(_params.pos_p);
+ }
+
+ /* scale result back to normal space */
+ _pos_sp = pos_sp_s.edivide(scale);
+
+ /* update yaw setpoint if needed */
+ if (isfinite(_pos_sp_triplet.current.yaw)) {
+ _att_sp.yaw_body = _pos_sp_triplet.current.yaw;
+ }
+
+ } else {
+ /* no waypoint, do nothing, setpoint was already reset */
}
}
void
MulticopterPositionControl::task_main()
{
- warnx("started");
_mavlink_fd = open(MAVLINK_LOG_DEVICE, 0);
- mavlink_log_info(_mavlink_fd, "[mpc] started");
/*
* do subscriptions
@@ -528,6 +871,9 @@ MulticopterPositionControl::task_main()
_arming_sub = orb_subscribe(ORB_ID(actuator_armed));
_local_pos_sub = orb_subscribe(ORB_ID(vehicle_local_position));
_pos_sp_triplet_sub = orb_subscribe(ORB_ID(position_setpoint_triplet));
+ _local_pos_sp_sub = orb_subscribe(ORB_ID(vehicle_local_position_setpoint));
+ _global_vel_sp_sub = orb_subscribe(ORB_ID(vehicle_global_velocity_setpoint));
+
parameters_update(true);
@@ -544,11 +890,6 @@ MulticopterPositionControl::task_main()
hrt_abstime t_prev = 0;
- const float alt_ctl_dz = 0.2f;
- const float pos_ctl_dz = 0.05f;
-
- math::Vector<3> sp_move_rate;
- sp_move_rate.zero();
math::Vector<3> thrust_int;
thrust_int.zero();
math::Matrix<3, 3> R;
@@ -607,96 +948,27 @@ MulticopterPositionControl::task_main()
_vel(1) = _local_pos.vy;
_vel(2) = _local_pos.vz;
- sp_move_rate.zero();
+ _vel_ff.zero();
+ _sp_move_rate.zero();
/* select control source */
if (_control_mode.flag_control_manual_enabled) {
/* manual control */
- if (_control_mode.flag_control_altitude_enabled) {
- /* reset alt setpoint to current altitude if needed */
- reset_alt_sp();
-
- /* move altitude setpoint with throttle stick */
- sp_move_rate(2) = -scale_control(_manual.z - 0.5f, 0.5f, alt_ctl_dz);
- }
-
- if (_control_mode.flag_control_position_enabled) {
- /* reset position setpoint to current position if needed */
- reset_pos_sp();
-
- /* move position setpoint with roll/pitch stick */
- sp_move_rate(0) = _manual.x;
- sp_move_rate(1) = _manual.y;
- }
-
- /* limit setpoint move rate */
- float sp_move_norm = sp_move_rate.length();
-
- if (sp_move_norm > 1.0f) {
- sp_move_rate /= sp_move_norm;
- }
-
- /* scale to max speed and rotate around yaw */
- math::Matrix<3, 3> R_yaw_sp;
- R_yaw_sp.from_euler(0.0f, 0.0f, _att_sp.yaw_body);
- sp_move_rate = R_yaw_sp * sp_move_rate.emult(_params.vel_max);
-
- /* move position setpoint */
- _pos_sp += sp_move_rate * dt;
-
- /* check if position setpoint is too far from actual position */
- math::Vector<3> pos_sp_offs;
- pos_sp_offs.zero();
-
- if (_control_mode.flag_control_position_enabled) {
- pos_sp_offs(0) = (_pos_sp(0) - _pos(0)) / _params.sp_offs_max(0);
- pos_sp_offs(1) = (_pos_sp(1) - _pos(1)) / _params.sp_offs_max(1);
- }
-
- if (_control_mode.flag_control_altitude_enabled) {
- pos_sp_offs(2) = (_pos_sp(2) - _pos(2)) / _params.sp_offs_max(2);
- }
+ control_manual(dt);
+ _mode_auto = false;
- float pos_sp_offs_norm = pos_sp_offs.length();
-
- if (pos_sp_offs_norm > 1.0f) {
- pos_sp_offs /= pos_sp_offs_norm;
- _pos_sp = _pos + pos_sp_offs.emult(_params.sp_offs_max);
- }
+ } else if (_control_mode.flag_control_offboard_enabled) {
+ /* offboard control */
+ control_offboard(dt);
+ _mode_auto = false;
} else {
/* AUTO */
- bool updated;
- orb_check(_pos_sp_triplet_sub, &updated);
-
- if (updated) {
- orb_copy(ORB_ID(position_setpoint_triplet), _pos_sp_triplet_sub, &_pos_sp_triplet);
- }
-
- if (_pos_sp_triplet.current.valid) {
- /* in case of interrupted mission don't go to waypoint but stay at current position */
- _reset_pos_sp = true;
- _reset_alt_sp = true;
-
- /* project setpoint to local frame */
- map_projection_project(&_ref_pos,
- _pos_sp_triplet.current.lat, _pos_sp_triplet.current.lon,
- &_pos_sp.data[0], &_pos_sp.data[1]);
- _pos_sp(2) = -(_pos_sp_triplet.current.alt - _ref_alt);
-
- /* update yaw setpoint if needed */
- if (isfinite(_pos_sp_triplet.current.yaw)) {
- _att_sp.yaw_body = _pos_sp_triplet.current.yaw;
- }
-
- } else {
- /* no waypoint, loiter, reset position setpoint if needed */
- reset_pos_sp();
- reset_alt_sp();
- }
+ control_auto(dt);
}
/* fill local position setpoint */
+ _local_pos_sp.timestamp = hrt_absolute_time();
_local_pos_sp.x = _pos_sp(0);
_local_pos_sp.y = _pos_sp(1);
_local_pos_sp.z = _pos_sp(2);
@@ -710,6 +982,7 @@ MulticopterPositionControl::task_main()
_local_pos_sp_pub = orb_advertise(ORB_ID(vehicle_local_position_setpoint), &_local_pos_sp);
}
+
if (!_control_mode.flag_control_manual_enabled && _pos_sp_triplet.current.valid && _pos_sp_triplet.current.type == SETPOINT_TYPE_IDLE) {
/* idle state, don't run controller and set zero thrust */
R.identity();
@@ -735,7 +1008,7 @@ MulticopterPositionControl::task_main()
/* run position & altitude controllers, calculate velocity setpoint */
math::Vector<3> pos_err = _pos_sp - _pos;
- _vel_sp = pos_err.emult(_params.pos_p) + sp_move_rate.emult(_params.vel_ff);
+ _vel_sp = pos_err.emult(_params.pos_p) + _vel_ff;
if (!_control_mode.flag_control_altitude_enabled) {
_reset_alt_sp = true;
@@ -753,15 +1026,6 @@ MulticopterPositionControl::task_main()
_vel_sp(2) = _params.land_speed;
}
- if (!_control_mode.flag_control_manual_enabled) {
- /* limit 3D speed only in non-manual modes */
- float vel_sp_norm = _vel_sp.edivide(_params.vel_max).length();
-
- if (vel_sp_norm > 1.0f) {
- _vel_sp /= vel_sp_norm;
- }
- }
-
_global_vel_sp.vx = _vel_sp(0);
_global_vel_sp.vy = _vel_sp(1);
_global_vel_sp.vz = _vel_sp(2);
@@ -814,7 +1078,7 @@ MulticopterPositionControl::task_main()
math::Vector<3> vel_err = _vel_sp - _vel;
/* derivative of velocity error, not includes setpoint acceleration */
- math::Vector<3> vel_err_d = (sp_move_rate - _vel).emult(_params.pos_p) - (_vel - _vel_prev) / dt;
+ math::Vector<3> vel_err_d = (_sp_move_rate - _vel).emult(_params.pos_p) - (_vel - _vel_prev) / dt;
_vel_prev = _vel;
/* thrust vector in NED frame */
@@ -862,7 +1126,7 @@ MulticopterPositionControl::task_main()
if (_control_mode.flag_control_velocity_enabled) {
/* limit max tilt */
- if (thr_min >= 0.0f && tilt_max < M_PI / 2 - 0.05f) {
+ if (thr_min >= 0.0f && tilt_max < M_PI_F / 2 - 0.05f) {
/* absolute horizontal thrust */
float thrust_sp_xy_len = math::Vector<2>(thrust_sp(0), thrust_sp(1)).length();
@@ -1038,6 +1302,7 @@ MulticopterPositionControl::task_main()
/* position controller disabled, reset setpoints */
_reset_alt_sp = true;
_reset_pos_sp = true;
+ _mode_auto = false;
reset_int_z = true;
reset_int_xy = true;
}
diff --git a/src/modules/navigator/datalinkloss.cpp b/src/modules/navigator/datalinkloss.cpp
new file mode 100644
index 000000000..e789fd10d
--- /dev/null
+++ b/src/modules/navigator/datalinkloss.cpp
@@ -0,0 +1,231 @@
+/****************************************************************************
+ *
+ * Copyright (c) 2013-2014 PX4 Development Team. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+/**
+ * @file datalinkloss.cpp
+ * Helper class for Data Link Loss Mode according to the OBC rules
+ *
+ * @author Thomas Gubler <thomasgubler@gmail.com>
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+#include <fcntl.h>
+
+#include <mavlink/mavlink_log.h>
+#include <systemlib/err.h>
+#include <geo/geo.h>
+
+#include <uORB/uORB.h>
+#include <uORB/topics/mission.h>
+#include <uORB/topics/home_position.h>
+
+#include "navigator.h"
+#include "datalinkloss.h"
+
+#define DELAY_SIGMA 0.01f
+
+DataLinkLoss::DataLinkLoss(Navigator *navigator, const char *name) :
+ MissionBlock(navigator, name),
+ _param_commsholdwaittime(this, "CH_T"),
+ _param_commsholdlat(this, "CH_LAT"),
+ _param_commsholdlon(this, "CH_LON"),
+ _param_commsholdalt(this, "CH_ALT"),
+ _param_airfieldhomelat(this, "NAV_AH_LAT", false),
+ _param_airfieldhomelon(this, "NAV_AH_LON", false),
+ _param_airfieldhomealt(this, "NAV_AH_ALT", false),
+ _param_airfieldhomewaittime(this, "AH_T"),
+ _param_numberdatalinklosses(this, "N"),
+ _param_skipcommshold(this, "CHSK"),
+ _dll_state(DLL_STATE_NONE)
+{
+ /* load initial params */
+ updateParams();
+ /* initial reset */
+ on_inactive();
+}
+
+DataLinkLoss::~DataLinkLoss()
+{
+}
+
+void
+DataLinkLoss::on_inactive()
+{
+ /* reset DLL state only if setpoint moved */
+ if (!_navigator->get_can_loiter_at_sp()) {
+ _dll_state = DLL_STATE_NONE;
+ }
+}
+
+void
+DataLinkLoss::on_activation()
+{
+ _dll_state = DLL_STATE_NONE;
+ updateParams();
+ advance_dll();
+ set_dll_item();
+}
+
+void
+DataLinkLoss::on_active()
+{
+ if (is_mission_item_reached()) {
+ updateParams();
+ advance_dll();
+ set_dll_item();
+ }
+}
+
+void
+DataLinkLoss::set_dll_item()
+{
+ struct position_setpoint_triplet_s *pos_sp_triplet = _navigator->get_position_setpoint_triplet();
+
+ set_previous_pos_setpoint();
+ _navigator->set_can_loiter_at_sp(false);
+
+ switch (_dll_state) {
+ case DLL_STATE_FLYTOCOMMSHOLDWP: {
+ _mission_item.lat = (double)(_param_commsholdlat.get()) * 1.0e-7;
+ _mission_item.lon = (double)(_param_commsholdlon.get()) * 1.0e-7;
+ _mission_item.altitude_is_relative = false;
+ _mission_item.altitude = _param_commsholdalt.get();
+ _mission_item.yaw = NAN;
+ _mission_item.loiter_radius = _navigator->get_loiter_radius();
+ _mission_item.loiter_direction = 1;
+ _mission_item.nav_cmd = NAV_CMD_LOITER_TIME_LIMIT;
+ _mission_item.acceptance_radius = _navigator->get_acceptance_radius();
+ _mission_item.time_inside = _param_commsholdwaittime.get() < 0.0f ? 0.0f : _param_commsholdwaittime.get();
+ _mission_item.pitch_min = 0.0f;
+ _mission_item.autocontinue = true;
+ _mission_item.origin = ORIGIN_ONBOARD;
+
+ _navigator->set_can_loiter_at_sp(true);
+ break;
+ }
+ case DLL_STATE_FLYTOAIRFIELDHOMEWP: {
+ _mission_item.lat = (double)(_param_airfieldhomelat.get()) * 1.0e-7;
+ _mission_item.lon = (double)(_param_airfieldhomelon.get()) * 1.0e-7;
+ _mission_item.altitude_is_relative = false;
+ _mission_item.altitude = _param_airfieldhomealt.get();
+ _mission_item.yaw = NAN;
+ _mission_item.loiter_radius = _navigator->get_loiter_radius();
+ _mission_item.loiter_direction = 1;
+ _mission_item.nav_cmd = NAV_CMD_LOITER_TIME_LIMIT;
+ _mission_item.time_inside = _param_airfieldhomewaittime.get() < 0.0f ? 0.0f : _param_airfieldhomewaittime.get();
+ _mission_item.acceptance_radius = _navigator->get_acceptance_radius();
+ _mission_item.pitch_min = 0.0f;
+ _mission_item.autocontinue = true;
+ _mission_item.origin = ORIGIN_ONBOARD;
+
+ _navigator->set_can_loiter_at_sp(true);
+ break;
+ }
+ case DLL_STATE_TERMINATE: {
+ /* Request flight termination from the commander */
+ _navigator->get_mission_result()->flight_termination = true;
+ _navigator->publish_mission_result();
+ reset_mission_item_reached();
+ warnx("not switched to manual: request flight termination");
+ pos_sp_triplet->previous.valid = false;
+ pos_sp_triplet->current.valid = false;
+ pos_sp_triplet->next.valid = false;
+ break;
+ }
+ default:
+ break;
+ }
+
+ reset_mission_item_reached();
+
+ /* convert mission item to current position setpoint and make it valid */
+ mission_item_to_position_setpoint(&_mission_item, &pos_sp_triplet->current);
+ pos_sp_triplet->next.valid = false;
+
+ _navigator->set_position_setpoint_triplet_updated();
+}
+
+void
+DataLinkLoss::advance_dll()
+{
+ switch (_dll_state) {
+ case DLL_STATE_NONE:
+ /* Check the number of data link losses. If above home fly home directly */
+ /* if number of data link losses limit is not reached fly to comms hold wp */
+ if (_navigator->get_vstatus()->data_link_lost_counter > _param_numberdatalinklosses.get()) {
+ warnx("%d data link losses, limit is %d, fly to airfield home",
+ _navigator->get_vstatus()->data_link_lost_counter, _param_numberdatalinklosses.get());
+ mavlink_log_info(_navigator->get_mavlink_fd(), "#audio: too many DL losses, fly to airfield home");
+ _navigator->get_mission_result()->stay_in_failsafe = true;
+ _navigator->publish_mission_result();
+ reset_mission_item_reached();
+ _dll_state = DLL_STATE_FLYTOAIRFIELDHOMEWP;
+ } else {
+ if (!_param_skipcommshold.get()) {
+ warnx("fly to comms hold, datalink loss counter: %d", _navigator->get_vstatus()->data_link_lost_counter);
+ mavlink_log_info(_navigator->get_mavlink_fd(), "#audio: fly to comms hold");
+ _dll_state = DLL_STATE_FLYTOCOMMSHOLDWP;
+ } else {
+ /* comms hold wp not active, fly to airfield home directly */
+ warnx("Skipping comms hold wp. Flying directly to airfield home");
+ mavlink_log_info(_navigator->get_mavlink_fd(), "#audio: fly to airfield home, comms hold skipped");
+ _dll_state = DLL_STATE_FLYTOAIRFIELDHOMEWP;
+ }
+ }
+ break;
+ case DLL_STATE_FLYTOCOMMSHOLDWP:
+ warnx("fly to airfield home");
+ mavlink_log_info(_navigator->get_mavlink_fd(), "#audio: fly to airfield home");
+ _dll_state = DLL_STATE_FLYTOAIRFIELDHOMEWP;
+ _navigator->get_mission_result()->stay_in_failsafe = true;
+ _navigator->publish_mission_result();
+ reset_mission_item_reached();
+ break;
+ case DLL_STATE_FLYTOAIRFIELDHOMEWP:
+ _dll_state = DLL_STATE_TERMINATE;
+ warnx("time is up, state should have been changed manually by now");
+ mavlink_log_info(_navigator->get_mavlink_fd(), "#audio: no manual control, terminating");
+ _navigator->get_mission_result()->stay_in_failsafe = true;
+ _navigator->publish_mission_result();
+ reset_mission_item_reached();
+ break;
+ case DLL_STATE_TERMINATE:
+ warnx("dll end");
+ _dll_state = DLL_STATE_END;
+ break;
+
+ default:
+ break;
+ }
+}
diff --git a/src/modules/navigator/datalinkloss.h b/src/modules/navigator/datalinkloss.h
new file mode 100644
index 000000000..31e0e3907
--- /dev/null
+++ b/src/modules/navigator/datalinkloss.h
@@ -0,0 +1,98 @@
+/***************************************************************************
+ *
+ * Copyright (c) 2013-2014 PX4 Development Team. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+/**
+ * @file datalinkloss.h
+ * Helper class for Data Link Loss Mode acording to the OBC rules
+ *
+ * @author Thomas Gubler <thomasgubler@gmail.com>
+ */
+
+#ifndef NAVIGATOR_DATALINKLOSS_H
+#define NAVIGATOR_DATALINKLOSS_H
+
+#include <controllib/blocks.hpp>
+#include <controllib/block/BlockParam.hpp>
+
+#include <uORB/Subscription.hpp>
+
+#include "navigator_mode.h"
+#include "mission_block.h"
+
+class Navigator;
+
+class DataLinkLoss : public MissionBlock
+{
+public:
+ DataLinkLoss(Navigator *navigator, const char *name);
+
+ ~DataLinkLoss();
+
+ virtual void on_inactive();
+
+ virtual void on_activation();
+
+ virtual void on_active();
+
+private:
+ /* Params */
+ control::BlockParamFloat _param_commsholdwaittime;
+ control::BlockParamInt _param_commsholdlat; // * 1e7
+ control::BlockParamInt _param_commsholdlon; // * 1e7
+ control::BlockParamFloat _param_commsholdalt;
+ control::BlockParamInt _param_airfieldhomelat; // * 1e7
+ control::BlockParamInt _param_airfieldhomelon; // * 1e7
+ control::BlockParamFloat _param_airfieldhomealt;
+ control::BlockParamFloat _param_airfieldhomewaittime;
+ control::BlockParamInt _param_numberdatalinklosses;
+ control::BlockParamInt _param_skipcommshold;
+
+ enum DLLState {
+ DLL_STATE_NONE = 0,
+ DLL_STATE_FLYTOCOMMSHOLDWP = 1,
+ DLL_STATE_FLYTOAIRFIELDHOMEWP = 2,
+ DLL_STATE_TERMINATE = 3,
+ DLL_STATE_END = 4
+ } _dll_state;
+
+ /**
+ * Set the DLL item
+ */
+ void set_dll_item();
+
+ /**
+ * Move to next DLL item
+ */
+ void advance_dll();
+
+};
+#endif
diff --git a/src/modules/navigator/datalinkloss_params.c b/src/modules/navigator/datalinkloss_params.c
new file mode 100644
index 000000000..6780c0c88
--- /dev/null
+++ b/src/modules/navigator/datalinkloss_params.c
@@ -0,0 +1,126 @@
+/****************************************************************************
+ *
+ * Copyright (c) 2014 PX4 Development Team. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * AS IS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/**
+ * @file datalinkloss_params.c
+ *
+ * Parameters for DLL
+ *
+ * @author Thomas Gubler <thomasgubler@gmail.com>
+ */
+
+#include <nuttx/config.h>
+
+#include <systemlib/param/param.h>
+
+/*
+ * Data Link Loss parameters, accessible via MAVLink
+ */
+
+/**
+ * Comms hold wait time
+ *
+ * The amount of time in seconds the system should wait at the comms hold waypoint
+ *
+ * @unit seconds
+ * @min 0.0
+ * @group DLL
+ */
+PARAM_DEFINE_FLOAT(NAV_DLL_CH_T, 120.0f);
+
+/**
+ * Comms hold Lat
+ *
+ * Latitude of comms hold waypoint
+ *
+ * @unit degrees * 1e7
+ * @min 0.0
+ * @group DLL
+ */
+PARAM_DEFINE_INT32(NAV_DLL_CH_LAT, -266072120);
+
+/**
+ * Comms hold Lon
+ *
+ * Longitude of comms hold waypoint
+ *
+ * @unit degrees * 1e7
+ * @min 0.0
+ * @group DLL
+ */
+PARAM_DEFINE_INT32(NAV_DLL_CH_LON, 1518453890);
+
+/**
+ * Comms hold alt
+ *
+ * Altitude of comms hold waypoint
+ *
+ * @unit m
+ * @min 0.0
+ * @group DLL
+ */
+PARAM_DEFINE_FLOAT(NAV_DLL_CH_ALT, 600.0f);
+
+/**
+ * Aifield hole wait time
+ *
+ * The amount of time in seconds the system should wait at the airfield home waypoint
+ *
+ * @unit seconds
+ * @min 0.0
+ * @group DLL
+ */
+PARAM_DEFINE_FLOAT(NAV_DLL_AH_T, 120.0f);
+
+/**
+ * Number of allowed Datalink timeouts
+ *
+ * After more than this number of data link timeouts the aircraft returns home directly
+ *
+ * @group DLL
+ * @min 0
+ * @max 1000
+ */
+PARAM_DEFINE_INT32(NAV_DLL_N, 2);
+
+/**
+ * Skip comms hold wp
+ *
+ * If set to 1 the system will skip the comms hold wp on data link loss and will directly fly to
+ * airfield home
+ *
+ * @group DLL
+ * @min 0
+ * @max 1
+ */
+PARAM_DEFINE_INT32(NAV_DLL_CHSK, 0);
diff --git a/src/modules/navigator/enginefailure.cpp b/src/modules/navigator/enginefailure.cpp
new file mode 100644
index 000000000..e007b208b
--- /dev/null
+++ b/src/modules/navigator/enginefailure.cpp
@@ -0,0 +1,149 @@
+/****************************************************************************
+ *
+ * Copyright (c) 2013-2014 PX4 Development Team. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+ /* @file enginefailure.cpp
+ * Helper class for a fixedwing engine failure mode
+ *
+ * @author Thomas Gubler <thomasgubler@gmail.com>
+ */
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+#include <fcntl.h>
+
+#include <mavlink/mavlink_log.h>
+#include <systemlib/err.h>
+#include <geo/geo.h>
+
+#include <uORB/uORB.h>
+#include <uORB/topics/mission.h>
+
+#include "navigator.h"
+#include "enginefailure.h"
+
+#define DELAY_SIGMA 0.01f
+
+EngineFailure::EngineFailure(Navigator *navigator, const char *name) :
+ MissionBlock(navigator, name),
+ _ef_state(EF_STATE_NONE)
+{
+ /* load initial params */
+ updateParams();
+ /* initial reset */
+ on_inactive();
+}
+
+EngineFailure::~EngineFailure()
+{
+}
+
+void
+EngineFailure::on_inactive()
+{
+ _ef_state = EF_STATE_NONE;
+}
+
+void
+EngineFailure::on_activation()
+{
+ _ef_state = EF_STATE_NONE;
+ advance_ef();
+ set_ef_item();
+}
+
+void
+EngineFailure::on_active()
+{
+ if (is_mission_item_reached()) {
+ advance_ef();
+ set_ef_item();
+ }
+}
+
+void
+EngineFailure::set_ef_item()
+{
+ struct position_setpoint_triplet_s *pos_sp_triplet = _navigator->get_position_setpoint_triplet();
+
+ /* make sure we have the latest params */
+ updateParams();
+
+ set_previous_pos_setpoint();
+ _navigator->set_can_loiter_at_sp(false);
+
+ switch (_ef_state) {
+ case EF_STATE_LOITERDOWN: {
+ //XXX create mission item at ground (below?) here
+
+ _mission_item.lat = _navigator->get_global_position()->lat;
+ _mission_item.lon = _navigator->get_global_position()->lon;
+ _mission_item.altitude_is_relative = false;
+ //XXX setting altitude to a very low value, evaluate other options
+ _mission_item.altitude = _navigator->get_home_position()->alt - 1000.0f;
+ _mission_item.yaw = NAN;
+ _mission_item.loiter_radius = _navigator->get_loiter_radius();
+ _mission_item.loiter_direction = 1;
+ _mission_item.nav_cmd = NAV_CMD_LOITER_UNLIMITED;
+ _mission_item.acceptance_radius = _navigator->get_acceptance_radius();
+ _mission_item.pitch_min = 0.0f;
+ _mission_item.autocontinue = true;
+ _mission_item.origin = ORIGIN_ONBOARD;
+
+ _navigator->set_can_loiter_at_sp(true);
+ break;
+ }
+ default:
+ break;
+ }
+
+ reset_mission_item_reached();
+
+ /* convert mission item to current position setpoint and make it valid */
+ mission_item_to_position_setpoint(&_mission_item, &pos_sp_triplet->current);
+ pos_sp_triplet->next.valid = false;
+
+ _navigator->set_position_setpoint_triplet_updated();
+}
+
+void
+EngineFailure::advance_ef()
+{
+ switch (_ef_state) {
+ case EF_STATE_NONE:
+ mavlink_log_info(_navigator->get_mavlink_fd(), "#audio: Engine failure. Loitering down");
+ _ef_state = EF_STATE_LOITERDOWN;
+ break;
+ default:
+ break;
+ }
+}
diff --git a/src/modules/navigator/enginefailure.h b/src/modules/navigator/enginefailure.h
new file mode 100644
index 000000000..2c48c2fce
--- /dev/null
+++ b/src/modules/navigator/enginefailure.h
@@ -0,0 +1,83 @@
+/***************************************************************************
+ *
+ * Copyright (c) 2013-2014 PX4 Development Team. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+/**
+ * @file enginefailure.h
+ * Helper class for a fixedwing engine failure mode
+ *
+ * @author Thomas Gubler <thomasgubler@gmail.com>
+ */
+
+#ifndef NAVIGATOR_ENGINEFAILURE_H
+#define NAVIGATOR_ENGINEFAILURE_H
+
+#include <controllib/blocks.hpp>
+#include <controllib/block/BlockParam.hpp>
+
+#include <uORB/Subscription.hpp>
+
+#include "navigator_mode.h"
+#include "mission_block.h"
+
+class Navigator;
+
+class EngineFailure : public MissionBlock
+{
+public:
+ EngineFailure(Navigator *navigator, const char *name);
+
+ ~EngineFailure();
+
+ virtual void on_inactive();
+
+ virtual void on_activation();
+
+ virtual void on_active();
+
+private:
+ enum EFState {
+ EF_STATE_NONE = 0,
+ EF_STATE_LOITERDOWN = 1,
+ } _ef_state;
+
+ /**
+ * Set the DLL item
+ */
+ void set_ef_item();
+
+ /**
+ * Move to next EF item
+ */
+ void advance_ef();
+
+};
+#endif
diff --git a/src/modules/navigator/geofence.cpp b/src/modules/navigator/geofence.cpp
index bc8dbca50..0f431ded2 100644
--- a/src/modules/navigator/geofence.cpp
+++ b/src/modules/navigator/geofence.cpp
@@ -48,6 +48,7 @@
#include <ctype.h>
#include <nuttx/config.h>
#include <unistd.h>
+#include <mavlink/mavlink_log.h>
/* Oddly, ERROR is not defined for C++ */
@@ -62,7 +63,12 @@ Geofence::Geofence() :
_altitude_min(0),
_altitude_max(0),
_verticesCount(0),
- param_geofence_on(this, "ON")
+ _param_geofence_on(this, "ON"),
+ _param_altitude_mode(this, "ALTMODE"),
+ _param_source(this, "SOURCE"),
+ _param_counter_threshold(this, "COUNT"),
+ _outside_counter(0),
+ _mavlinkFd(-1)
{
/* Load initial params */
updateParams();
@@ -74,27 +80,69 @@ Geofence::~Geofence()
}
-bool Geofence::inside(const struct vehicle_global_position_s *vehicle)
+bool Geofence::inside(const struct vehicle_global_position_s &global_position)
{
- double lat = vehicle->lat / 1e7d;
- double lon = vehicle->lon / 1e7d;
- float alt = vehicle->alt;
+ return inside(global_position.lat, global_position.lon, global_position.alt);
+}
+
+bool Geofence::inside(const struct vehicle_global_position_s &global_position, float baro_altitude_amsl)
+{
+ return inside(global_position.lat, global_position.lon, baro_altitude_amsl);
+}
+
- return inside(lat, lon, vehicle->alt);
+bool Geofence::inside(const struct vehicle_global_position_s &global_position,
+ const struct vehicle_gps_position_s &gps_position,float baro_altitude_amsl) {
+ updateParams();
+
+ if (getAltitudeMode() == Geofence::GF_ALT_MODE_WGS84) {
+ if (getSource() == Geofence::GF_SOURCE_GLOBALPOS) {
+ return inside(global_position);
+ } else {
+ return inside((double)gps_position.lat * 1.0e-7, (double)gps_position.lon * 1.0e-7,
+ (double)gps_position.alt * 1.0e-3);
+ }
+ } else {
+ if (getSource() == Geofence::GF_SOURCE_GLOBALPOS) {
+ return inside(global_position, baro_altitude_amsl);
+ } else {
+ return inside((double)gps_position.lat * 1.0e-7, (double)gps_position.lon * 1.0e-7,
+ baro_altitude_amsl);
+ }
+ }
}
bool Geofence::inside(double lat, double lon, float altitude)
{
+ bool inside_fence = inside_polygon(lat, lon, altitude);
+
+ if (inside_fence) {
+ _outside_counter = 0;
+ return inside_fence;
+ } {
+ _outside_counter++;
+ if(_outside_counter > _param_counter_threshold.get()) {
+ return inside_fence;
+ } else {
+ return true;
+ }
+ }
+}
+
+
+bool Geofence::inside_polygon(double lat, double lon, float altitude)
+{
/* Return true if geofence is disabled */
- if (param_geofence_on.get() != 1)
+ if (_param_geofence_on.get() != 1)
return true;
if (valid()) {
if (!isEmpty()) {
/* Vertical check */
- if (altitude > _altitude_max || altitude < _altitude_min)
+ if (altitude > _altitude_max || altitude < _altitude_min) {
return false;
+ }
/*Horizontal check */
/* Adaptation of algorithm originally presented as
@@ -116,9 +164,9 @@ bool Geofence::inside(double lat, double lon, float altitude)
}
// skip vertex 0 (return point)
- if (((temp_vertex_i.lon) >= lon != (temp_vertex_j.lon >= lon)) &&
- (lat <= (temp_vertex_j.lat - temp_vertex_i.lat) * (lon - temp_vertex_i.lon) /
- (temp_vertex_j.lon - temp_vertex_i.lon) + temp_vertex_i.lat)) {
+ if (((double)temp_vertex_i.lon >= lon) != ((double)temp_vertex_j.lon >= lon) &&
+ (lat <= (double)(temp_vertex_j.lat - temp_vertex_i.lat) * (lon - (double)temp_vertex_i.lon) /
+ (double)(temp_vertex_j.lon - temp_vertex_i.lon) + (double)temp_vertex_i.lat)) {
c = !c;
}
@@ -284,8 +332,10 @@ Geofence::loadFromFile(const char *filename)
{
_verticesCount = pointCounter;
warnx("Geofence: imported successfully");
+ mavlink_log_info(_mavlinkFd, "Geofence imported");
} else {
warnx("Geofence: import error");
+ mavlink_log_critical(_mavlinkFd, "#audio: Geofence import error");
}
return ERROR;
@@ -294,4 +344,5 @@ Geofence::loadFromFile(const char *filename)
int Geofence::clearDm()
{
dm_clear(DM_KEY_FENCE_POINTS);
+ return OK;
}
diff --git a/src/modules/navigator/geofence.h b/src/modules/navigator/geofence.h
index 2eb126ab5..9d647cb68 100644
--- a/src/modules/navigator/geofence.h
+++ b/src/modules/navigator/geofence.h
@@ -42,6 +42,9 @@
#define GEOFENCE_H_
#include <uORB/topics/fence.h>
+#include <uORB/topics/vehicle_global_position.h>
+#include <uORB/topics/vehicle_gps_position.h>
+#include <uORB/topics/sensor_combined.h>
#include <controllib/blocks.hpp>
#include <controllib/block/BlockParam.hpp>
@@ -49,30 +52,32 @@
class Geofence : public control::SuperBlock
{
-private:
- orb_advert_t _fence_pub; /**< publish fence topic */
-
- float _altitude_min;
- float _altitude_max;
-
- unsigned _verticesCount;
-
- /* Params */
- control::BlockParamInt param_geofence_on;
public:
Geofence();
~Geofence();
+ /* Altitude mode, corresponding to the param GF_ALTMODE */
+ enum {
+ GF_ALT_MODE_WGS84 = 0,
+ GF_ALT_MODE_AMSL = 1
+ };
+
+ /* Source, corresponding to the param GF_SOURCE */
+ enum {
+ GF_SOURCE_GLOBALPOS = 0,
+ GF_SOURCE_GPS = 1
+ };
+
/**
- * Return whether craft is inside geofence.
+ * Return whether system is inside geofence.
*
* Calculate whether point is inside arbitrary polygon
* @param craft pointer craft coordinates
- * @param fence pointer to array of coordinates, one per vertex. First and last vertex are assumed connected
- * @return true: craft is inside fence, false:craft is outside fence
+ * @return true: system is inside fence, false: system is outside fence
*/
- bool inside(const struct vehicle_global_position_s *craft);
- bool inside(double lat, double lon, float altitude);
+ bool inside(const struct vehicle_global_position_s &global_position,
+ const struct vehicle_gps_position_s &gps_position,float baro_altitude_amsl);
+ bool inside_polygon(double lat, double lon, float altitude);
int clearDm();
@@ -88,6 +93,34 @@ public:
int loadFromFile(const char *filename);
bool isEmpty() {return _verticesCount == 0;}
+
+ int getAltitudeMode() { return _param_altitude_mode.get(); }
+
+ int getSource() { return _param_source.get(); }
+
+ void setMavlinkFd(int value) { _mavlinkFd = value; }
+
+private:
+ orb_advert_t _fence_pub; /**< publish fence topic */
+
+ float _altitude_min;
+ float _altitude_max;
+
+ unsigned _verticesCount;
+
+ /* Params */
+ control::BlockParamInt _param_geofence_on;
+ control::BlockParamInt _param_altitude_mode;
+ control::BlockParamInt _param_source;
+ control::BlockParamInt _param_counter_threshold;
+
+ uint8_t _outside_counter;
+
+ int _mavlinkFd;
+
+ bool inside(double lat, double lon, float altitude);
+ bool inside(const struct vehicle_global_position_s &global_position);
+ bool inside(const struct vehicle_global_position_s &global_position, float baro_altitude_amsl);
};
diff --git a/src/modules/navigator/geofence_params.c b/src/modules/navigator/geofence_params.c
index 653b1ad84..fca3918e1 100644
--- a/src/modules/navigator/geofence_params.c
+++ b/src/modules/navigator/geofence_params.c
@@ -58,3 +58,39 @@
* @group Geofence
*/
PARAM_DEFINE_INT32(GF_ON, 1);
+
+/**
+ * Geofence altitude mode
+ *
+ * Select which altitude reference should be used
+ * 0 = WGS84, 1 = AMSL
+ *
+ * @min 0
+ * @max 1
+ * @group Geofence
+ */
+PARAM_DEFINE_INT32(GF_ALTMODE, 0);
+
+/**
+ * Geofence source
+ *
+ * Select which position source should be used. Selecting GPS instead of global position makes sure that there is
+ * no dependence on the position estimator
+ * 0 = global position, 1 = GPS
+ *
+ * @min 0
+ * @max 1
+ * @group Geofence
+ */
+PARAM_DEFINE_INT32(GF_SOURCE, 0);
+
+/**
+ * Geofence counter limit
+ *
+ * Set how many subsequent position measurements outside of the fence are needed before geofence violation is triggered
+ *
+ * @min -1
+ * @max 10
+ * @group Geofence
+ */
+PARAM_DEFINE_INT32(GF_COUNT, -1);
diff --git a/src/modules/navigator/gpsfailure.cpp b/src/modules/navigator/gpsfailure.cpp
new file mode 100644
index 000000000..cd55f60b0
--- /dev/null
+++ b/src/modules/navigator/gpsfailure.cpp
@@ -0,0 +1,178 @@
+/****************************************************************************
+ *
+ * Copyright (c) 2013-2014 PX4 Development Team. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+/**
+ * @file gpsfailure.cpp
+ * Helper class for gpsfailure mode according to the OBC rules
+ *
+ * @author Thomas Gubler <thomasgubler@gmail.com>
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+#include <fcntl.h>
+
+#include <mavlink/mavlink_log.h>
+#include <systemlib/err.h>
+#include <geo/geo.h>
+
+#include <uORB/uORB.h>
+#include <uORB/topics/mission.h>
+#include <uORB/topics/home_position.h>
+
+#include "navigator.h"
+#include "gpsfailure.h"
+
+#define DELAY_SIGMA 0.01f
+
+GpsFailure::GpsFailure(Navigator *navigator, const char *name) :
+ MissionBlock(navigator, name),
+ _param_loitertime(this, "LT"),
+ _param_openlooploiter_roll(this, "R"),
+ _param_openlooploiter_pitch(this, "P"),
+ _param_openlooploiter_thrust(this, "TR"),
+ _gpsf_state(GPSF_STATE_NONE),
+ _timestamp_activation(0)
+{
+ /* load initial params */
+ updateParams();
+ /* initial reset */
+ on_inactive();
+}
+
+GpsFailure::~GpsFailure()
+{
+}
+
+void
+GpsFailure::on_inactive()
+{
+ /* reset GPSF state only if setpoint moved */
+ if (!_navigator->get_can_loiter_at_sp()) {
+ _gpsf_state = GPSF_STATE_NONE;
+ }
+}
+
+void
+GpsFailure::on_activation()
+{
+ _gpsf_state = GPSF_STATE_NONE;
+ _timestamp_activation = hrt_absolute_time();
+ updateParams();
+ advance_gpsf();
+ set_gpsf_item();
+}
+
+void
+GpsFailure::on_active()
+{
+
+ switch (_gpsf_state) {
+ case GPSF_STATE_LOITER: {
+ /* Position controller does not run in this mode:
+ * navigator has to publish an attitude setpoint */
+ _navigator->get_att_sp()->roll_body = M_DEG_TO_RAD_F * _param_openlooploiter_roll.get();
+ _navigator->get_att_sp()->pitch_body = M_DEG_TO_RAD_F * _param_openlooploiter_pitch.get();
+ _navigator->get_att_sp()->thrust = _param_openlooploiter_thrust.get();
+ _navigator->publish_att_sp();
+
+ /* Measure time */
+ hrt_abstime elapsed = hrt_elapsed_time(&_timestamp_activation);
+
+ //warnx("open loop loiter, posctl enabled %u, elapsed %.1fs, thrust %.2f",
+ //_navigator->get_control_mode()->flag_control_position_enabled, elapsed * 1e-6, (double)_param_openlooploiter_thrust.get());
+ if (elapsed > _param_loitertime.get() * 1e6f) {
+ /* no recovery, adavance the state machine */
+ warnx("gps not recovered, switch to next state");
+ advance_gpsf();
+ }
+ break;
+ }
+ case GPSF_STATE_TERMINATE:
+ set_gpsf_item();
+ advance_gpsf();
+ break;
+ default:
+ break;
+ }
+}
+
+void
+GpsFailure::set_gpsf_item()
+{
+ struct position_setpoint_triplet_s *pos_sp_triplet = _navigator->get_position_setpoint_triplet();
+
+ /* Set pos sp triplet to invalid to stop pos controller */
+ pos_sp_triplet->previous.valid = false;
+ pos_sp_triplet->current.valid = false;
+ pos_sp_triplet->next.valid = false;
+
+ switch (_gpsf_state) {
+ case GPSF_STATE_TERMINATE: {
+ /* Request flight termination from the commander */
+ _navigator->get_mission_result()->flight_termination = true;
+ _navigator->publish_mission_result();
+ warnx("gps fail: request flight termination");
+ }
+ default:
+ break;
+ }
+
+ reset_mission_item_reached();
+ _navigator->set_position_setpoint_triplet_updated();
+}
+
+void
+GpsFailure::advance_gpsf()
+{
+ updateParams();
+
+ switch (_gpsf_state) {
+ case GPSF_STATE_NONE:
+ _gpsf_state = GPSF_STATE_LOITER;
+ warnx("gpsf loiter");
+ mavlink_log_info(_navigator->get_mavlink_fd(), "#audio: open loop loiter");
+ break;
+ case GPSF_STATE_LOITER:
+ _gpsf_state = GPSF_STATE_TERMINATE;
+ warnx("gpsf terminate");
+ mavlink_log_info(_navigator->get_mavlink_fd(), "#audio: no gps recovery, termination");
+ warnx("mavlink sent");
+ break;
+ case GPSF_STATE_TERMINATE:
+ warnx("gpsf end");
+ _gpsf_state = GPSF_STATE_END;
+ default:
+ break;
+ }
+}
diff --git a/src/modules/navigator/gpsfailure.h b/src/modules/navigator/gpsfailure.h
new file mode 100644
index 000000000..e9e72babf
--- /dev/null
+++ b/src/modules/navigator/gpsfailure.h
@@ -0,0 +1,97 @@
+/***************************************************************************
+ *
+ * Copyright (c) 2013-2014 PX4 Development Team. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+/**
+ * @file gpsfailure.h
+ * Helper class for Data Link Loss Mode according to the OBC rules
+ *
+ * @author Thomas Gubler <thomasgubler@gmail.com>
+ */
+
+#ifndef NAVIGATOR_GPSFAILURE_H
+#define NAVIGATOR_GPSFAILURE_H
+
+#include <controllib/blocks.hpp>
+#include <controllib/block/BlockParam.hpp>
+
+#include <uORB/uORB.h>
+#include <uORB/Publication.hpp>
+#include <uORB/topics/vehicle_attitude_setpoint.h>
+
+#include <drivers/drv_hrt.h>
+
+#include "navigator_mode.h"
+#include "mission_block.h"
+
+class Navigator;
+
+class GpsFailure : public MissionBlock
+{
+public:
+ GpsFailure(Navigator *navigator, const char *name);
+
+ ~GpsFailure();
+
+ virtual void on_inactive();
+
+ virtual void on_activation();
+
+ virtual void on_active();
+
+private:
+ /* Params */
+ control::BlockParamFloat _param_loitertime;
+ control::BlockParamFloat _param_openlooploiter_roll;
+ control::BlockParamFloat _param_openlooploiter_pitch;
+ control::BlockParamFloat _param_openlooploiter_thrust;
+
+ enum GPSFState {
+ GPSF_STATE_NONE = 0,
+ GPSF_STATE_LOITER = 1,
+ GPSF_STATE_TERMINATE = 2,
+ GPSF_STATE_END = 3,
+ } _gpsf_state;
+
+ hrt_abstime _timestamp_activation; //*< timestamp when this mode was activated */
+
+ /**
+ * Set the GPSF item
+ */
+ void set_gpsf_item();
+
+ /**
+ * Move to next GPSF item
+ */
+ void advance_gpsf();
+
+};
+#endif
diff --git a/src/modules/navigator/gpsfailure_params.c b/src/modules/navigator/gpsfailure_params.c
new file mode 100644
index 000000000..39d179eed
--- /dev/null
+++ b/src/modules/navigator/gpsfailure_params.c
@@ -0,0 +1,97 @@
+/****************************************************************************
+ *
+ * Copyright (c) 2014 PX4 Development Team. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * AS IS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/**
+ * @file gpsfailure_params.c
+ *
+ * Parameters for GPSF navigation mode
+ *
+ * @author Thomas Gubler <thomasgubler@gmail.com>
+ */
+
+#include <nuttx/config.h>
+
+#include <systemlib/param/param.h>
+
+/*
+ * GPS Failure Navigation Mode parameters, accessible via MAVLink
+ */
+
+/**
+ * Loiter time
+ *
+ * The amount of time in seconds the system should do open loop loiter and wait for gps recovery
+ * before it goes into flight termination.
+ *
+ * @unit seconds
+ * @min 0.0
+ * @group GPSF
+ */
+PARAM_DEFINE_FLOAT(NAV_GPSF_LT, 30.0f);
+
+/**
+ * Open loop loiter roll
+ *
+ * Roll in degrees during the open loop loiter
+ *
+ * @unit deg
+ * @min 0.0
+ * @max 30.0
+ * @group GPSF
+ */
+PARAM_DEFINE_FLOAT(NAV_GPSF_R, 15.0f);
+
+/**
+ * Open loop loiter pitch
+ *
+ * Pitch in degrees during the open loop loiter
+ *
+ * @unit deg
+ * @min -30.0
+ * @max 30.0
+ * @group GPSF
+ */
+PARAM_DEFINE_FLOAT(NAV_GPSF_P, 0.0f);
+
+/**
+ * Open loop loiter thrust
+ *
+ * Thrust value which is set during the open loop loiter
+ *
+ * @min 0.0
+ * @max 1.0
+ * @group GPSF
+ */
+PARAM_DEFINE_FLOAT(NAV_GPSF_TR, 0.7f);
+
+
diff --git a/src/modules/navigator/loiter.cpp b/src/modules/navigator/loiter.cpp
new file mode 100644
index 000000000..f827e70c9
--- /dev/null
+++ b/src/modules/navigator/loiter.cpp
@@ -0,0 +1,93 @@
+/****************************************************************************
+ *
+ * Copyright (c) 2013-2014 PX4 Development Team. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+/**
+ * @file loiter.cpp
+ *
+ * Helper class to loiter
+ *
+ * @author Julian Oes <julian@oes.ch>
+ * @author Anton Babushkin <anton.babushkin@me.com>
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <math.h>
+#include <fcntl.h>
+
+#include <mavlink/mavlink_log.h>
+#include <systemlib/err.h>
+
+#include <uORB/uORB.h>
+#include <uORB/topics/position_setpoint_triplet.h>
+
+#include "loiter.h"
+#include "navigator.h"
+
+Loiter::Loiter(Navigator *navigator, const char *name) :
+ MissionBlock(navigator, name)
+{
+ /* load initial params */
+ updateParams();
+}
+
+Loiter::~Loiter()
+{
+}
+
+void
+Loiter::on_inactive()
+{
+}
+
+void
+Loiter::on_activation()
+{
+ /* set current mission item to loiter */
+ set_loiter_item(&_mission_item);
+
+ /* convert mission item to current setpoint */
+ struct position_setpoint_triplet_s *pos_sp_triplet = _navigator->get_position_setpoint_triplet();
+ pos_sp_triplet->previous.valid = false;
+ mission_item_to_position_setpoint(&_mission_item, &pos_sp_triplet->current);
+ pos_sp_triplet->next.valid = false;
+
+ _navigator->set_can_loiter_at_sp(pos_sp_triplet->current.type == SETPOINT_TYPE_LOITER);
+
+ _navigator->set_position_setpoint_triplet_updated();
+}
+
+void
+Loiter::on_active()
+{
+}
diff --git a/src/modules/mavlink/mavlink_commands.h b/src/modules/navigator/loiter.h
index abdba34b9..37ab57a07 100644
--- a/src/modules/mavlink/mavlink_commands.h
+++ b/src/modules/navigator/loiter.h
@@ -1,4 +1,4 @@
-/****************************************************************************
+/***************************************************************************
*
* Copyright (c) 2014 PX4 Development Team. All rights reserved.
*
@@ -30,36 +30,35 @@
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
-
/**
- * @file mavlink_commands.h
- * Mavlink commands stream definition.
+ * @file loiter.h
+ *
+ * Helper class to loiter
*
- * @author Anton Babushkin <anton.babushkin@me.com>
+ * @author Julian Oes <julian@oes.ch>
*/
-#ifndef MAVLINK_COMMANDS_H_
-#define MAVLINK_COMMANDS_H_
+#ifndef NAVIGATOR_LOITER_H
+#define NAVIGATOR_LOITER_H
-#include <uORB/uORB.h>
-#include <uORB/topics/vehicle_command.h>
+#include <controllib/blocks.hpp>
+#include <controllib/block/BlockParam.hpp>
-class Mavlink;
-class MavlinkCommansStream;
+#include "navigator_mode.h"
+#include "mission_block.h"
-#include "mavlink_main.h"
-
-class MavlinkCommandsStream
+class Loiter : public MissionBlock
{
-private:
- MavlinkOrbSubscription *_cmd_sub;
- struct vehicle_command_s *_cmd;
- mavlink_channel_t _channel;
- uint64_t _cmd_time;
-
public:
- MavlinkCommandsStream(Mavlink *mavlink, mavlink_channel_t channel);
- void update(const hrt_abstime t);
+ Loiter(Navigator *navigator, const char *name);
+
+ ~Loiter();
+
+ virtual void on_inactive();
+
+ virtual void on_activation();
+
+ virtual void on_active();
};
-#endif /* MAVLINK_COMMANDS_H_ */
+#endif
diff --git a/src/modules/navigator/mission.cpp b/src/modules/navigator/mission.cpp
new file mode 100644
index 000000000..0765e9b7c
--- /dev/null
+++ b/src/modules/navigator/mission.cpp
@@ -0,0 +1,735 @@
+/****************************************************************************
+ *
+ * Copyright (c) 2013-2014 PX4 Development Team. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+/**
+ * @file navigator_mission.cpp
+ *
+ * Helper class to access missions
+ *
+ * @author Julian Oes <julian@oes.ch>
+ * @author Thomas Gubler <thomasgubler@gmail.com>
+ * @author Anton Babushkin <anton.babushkin@me.com>
+ */
+
+#include <sys/types.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <float.h>
+
+#include <drivers/drv_hrt.h>
+
+#include <dataman/dataman.h>
+#include <mavlink/mavlink_log.h>
+#include <systemlib/err.h>
+#include <geo/geo.h>
+#include <lib/mathlib/mathlib.h>
+
+#include <uORB/uORB.h>
+#include <uORB/topics/mission.h>
+#include <uORB/topics/mission_result.h>
+
+#include "mission.h"
+#include "navigator.h"
+
+Mission::Mission(Navigator *navigator, const char *name) :
+ MissionBlock(navigator, name),
+ _param_onboard_enabled(this, "MIS_ONBOARD_EN", false),
+ _param_takeoff_alt(this, "MIS_TAKEOFF_ALT", false),
+ _param_dist_1wp(this, "MIS_DIST_1WP", false),
+ _param_altmode(this, "MIS_ALTMODE", false),
+ _onboard_mission({0}),
+ _offboard_mission({0}),
+ _current_onboard_mission_index(-1),
+ _current_offboard_mission_index(-1),
+ _need_takeoff(true),
+ _takeoff(false),
+ _mission_type(MISSION_TYPE_NONE),
+ _inited(false),
+ _dist_1wp_ok(false),
+ _missionFeasiblityChecker(),
+ _min_current_sp_distance_xy(FLT_MAX),
+ _mission_item_previous_alt(NAN),
+ _distance_current_previous(0.0f)
+{
+ /* load initial params */
+ updateParams();
+}
+
+Mission::~Mission()
+{
+}
+
+void
+Mission::on_inactive()
+{
+ if (_inited) {
+ /* check if missions have changed so that feedback to ground station is given */
+ bool onboard_updated = false;
+ orb_check(_navigator->get_onboard_mission_sub(), &onboard_updated);
+ if (onboard_updated) {
+ update_onboard_mission();
+ }
+
+ bool offboard_updated = false;
+ orb_check(_navigator->get_offboard_mission_sub(), &offboard_updated);
+ if (offboard_updated) {
+ update_offboard_mission();
+ }
+
+ } else {
+ /* read mission topics on initialization */
+ _inited = true;
+
+ update_onboard_mission();
+ update_offboard_mission();
+ }
+
+ /* require takeoff after non-loiter or landing */
+ if (!_navigator->get_can_loiter_at_sp() || _navigator->get_vstatus()->condition_landed) {
+ _need_takeoff = true;
+ }
+}
+
+void
+Mission::on_activation()
+{
+ set_mission_items();
+}
+
+void
+Mission::on_active()
+{
+ /* check if anything has changed */
+ bool onboard_updated = false;
+ orb_check(_navigator->get_onboard_mission_sub(), &onboard_updated);
+ if (onboard_updated) {
+ update_onboard_mission();
+ }
+
+ bool offboard_updated = false;
+ orb_check(_navigator->get_offboard_mission_sub(), &offboard_updated);
+ if (offboard_updated) {
+ update_offboard_mission();
+ }
+
+ /* reset mission items if needed */
+ if (onboard_updated || offboard_updated) {
+ set_mission_items();
+ }
+
+ /* lets check if we reached the current mission item */
+ if (_mission_type != MISSION_TYPE_NONE && is_mission_item_reached()) {
+ if (_mission_item.autocontinue) {
+ /* switch to next waypoint if 'autocontinue' flag set */
+ advance_mission();
+ set_mission_items();
+
+ } else {
+ /* else just report that item reached */
+ if (_mission_type == MISSION_TYPE_OFFBOARD) {
+ if (!(_navigator->get_mission_result()->seq_reached == _current_offboard_mission_index && _navigator->get_mission_result()->reached)) {
+ set_mission_item_reached();
+ }
+ }
+ }
+
+ } else if (_mission_type != MISSION_TYPE_NONE &&_param_altmode.get() == MISSION_ALTMODE_FOH) {
+ altitude_sp_foh_update();
+ } else {
+ /* if waypoint position reached allow loiter on the setpoint */
+ if (_waypoint_position_reached && _mission_item.nav_cmd != NAV_CMD_IDLE) {
+ _navigator->set_can_loiter_at_sp(true);
+ }
+ }
+}
+
+void
+Mission::update_onboard_mission()
+{
+ if (orb_copy(ORB_ID(onboard_mission), _navigator->get_onboard_mission_sub(), &_onboard_mission) == OK) {
+ /* accept the current index set by the onboard mission if it is within bounds */
+ if (_onboard_mission.current_seq >=0
+ && _onboard_mission.current_seq < (int)_onboard_mission.count) {
+ _current_onboard_mission_index = _onboard_mission.current_seq;
+ } else {
+ /* if less WPs available, reset to first WP */
+ if (_current_onboard_mission_index >= (int)_onboard_mission.count) {
+ _current_onboard_mission_index = 0;
+ /* if not initialized, set it to 0 */
+ } else if (_current_onboard_mission_index < 0) {
+ _current_onboard_mission_index = 0;
+ }
+ /* otherwise, just leave it */
+ }
+
+ } else {
+ _onboard_mission.count = 0;
+ _onboard_mission.current_seq = 0;
+ _current_onboard_mission_index = 0;
+ }
+}
+
+void
+Mission::update_offboard_mission()
+{
+ if (orb_copy(ORB_ID(offboard_mission), _navigator->get_offboard_mission_sub(), &_offboard_mission) == OK) {
+ warnx("offboard mission updated: dataman_id=%d, count=%d, current_seq=%d", _offboard_mission.dataman_id, _offboard_mission.count, _offboard_mission.current_seq);
+ /* determine current index */
+ if (_offboard_mission.current_seq >= 0 && _offboard_mission.current_seq < (int)_offboard_mission.count) {
+ _current_offboard_mission_index = _offboard_mission.current_seq;
+ } else {
+ /* if less items available, reset to first item */
+ if (_current_offboard_mission_index >= (int)_offboard_mission.count) {
+ _current_offboard_mission_index = 0;
+
+ /* if not initialized, set it to 0 */
+ } else if (_current_offboard_mission_index < 0) {
+ _current_offboard_mission_index = 0;
+ }
+ /* otherwise, just leave it */
+ }
+
+ /* Check mission feasibility, for now do not handle the return value,
+ * however warnings are issued to the gcs via mavlink from inside the MissionFeasiblityChecker */
+ dm_item_t dm_current = DM_KEY_WAYPOINTS_OFFBOARD(_offboard_mission.dataman_id);
+
+ _missionFeasiblityChecker.checkMissionFeasible(_navigator->get_vstatus()->is_rotary_wing,
+ dm_current, (size_t) _offboard_mission.count, _navigator->get_geofence(),
+ _navigator->get_home_position()->alt);
+
+ } else {
+ warnx("offboard mission update failed");
+ _offboard_mission.count = 0;
+ _offboard_mission.current_seq = 0;
+ _current_offboard_mission_index = 0;
+ }
+
+ set_current_offboard_mission_item();
+}
+
+
+void
+Mission::advance_mission()
+{
+ if (_takeoff) {
+ _takeoff = false;
+
+ } else {
+ switch (_mission_type) {
+ case MISSION_TYPE_ONBOARD:
+ _current_onboard_mission_index++;
+ break;
+
+ case MISSION_TYPE_OFFBOARD:
+ _current_offboard_mission_index++;
+ break;
+
+ case MISSION_TYPE_NONE:
+ default:
+ break;
+ }
+ }
+}
+
+bool
+Mission::check_dist_1wp()
+{
+ if (_dist_1wp_ok) {
+ /* always return true after at least one successful check */
+ return true;
+ }
+
+ /* check if first waypoint is not too far from home */
+ if (_param_dist_1wp.get() > 0.0f) {
+ if (_navigator->get_vstatus()->condition_home_position_valid) {
+ struct mission_item_s mission_item;
+
+ /* find first waypoint (with lat/lon) item in datamanager */
+ for (unsigned i = 0; i < _offboard_mission.count; i++) {
+ if (dm_read(DM_KEY_WAYPOINTS_OFFBOARD(_offboard_mission.dataman_id), i,
+ &mission_item, sizeof(mission_item_s)) == sizeof(mission_item_s)) {
+
+ /* check only items with valid lat/lon */
+ if ( mission_item.nav_cmd == NAV_CMD_WAYPOINT ||
+ mission_item.nav_cmd == NAV_CMD_LOITER_TIME_LIMIT ||
+ mission_item.nav_cmd == NAV_CMD_LOITER_TURN_COUNT ||
+ mission_item.nav_cmd == NAV_CMD_LOITER_UNLIMITED ||
+ mission_item.nav_cmd == NAV_CMD_TAKEOFF ||
+ mission_item.nav_cmd == NAV_CMD_PATHPLANNING) {
+
+ /* check distance from home to item */
+ float dist_to_1wp = get_distance_to_next_waypoint(
+ mission_item.lat, mission_item.lon,
+ _navigator->get_home_position()->lat, _navigator->get_home_position()->lon);
+
+ if (dist_to_1wp < _param_dist_1wp.get()) {
+ _dist_1wp_ok = true;
+ if (dist_to_1wp > ((_param_dist_1wp.get() * 3) / 2)) {
+ /* allow at 2/3 distance, but warn */
+ mavlink_log_critical(_navigator->get_mavlink_fd(), "Warning: First waypoint very far: %d m", (int)dist_to_1wp);
+ }
+ return true;
+
+ } else {
+ /* item is too far from home */
+ mavlink_log_critical(_navigator->get_mavlink_fd(), "Waypoint too far: %d m,[MIS_DIST_1WP=%d]", (int)dist_to_1wp, (int)_param_dist_1wp.get());
+ return false;
+ }
+ }
+
+ } else {
+ /* error reading, mission is invalid */
+ mavlink_log_info(_navigator->get_mavlink_fd(), "error reading offboard mission");
+ return false;
+ }
+ }
+
+ /* no waypoints found in mission, then we will not fly far away */
+ _dist_1wp_ok = true;
+ return true;
+
+ } else {
+ mavlink_log_info(_navigator->get_mavlink_fd(), "no home position");
+ return false;
+ }
+
+ } else {
+ return true;
+ }
+}
+
+void
+Mission::set_mission_items()
+{
+ /* make sure param is up to date */
+ updateParams();
+
+ /* reset the altitude foh logic, if altitude foh is enabled (param) a new foh element starts now */
+ altitude_sp_foh_reset();
+
+ struct position_setpoint_triplet_s *pos_sp_triplet = _navigator->get_position_setpoint_triplet();
+
+ /* set previous position setpoint to current */
+ set_previous_pos_setpoint();
+
+ /* Copy previous mission item altitude (can be extended to a copy of the full mission item if needed) */
+ if (pos_sp_triplet->previous.valid) {
+ _mission_item_previous_alt = _mission_item.altitude;
+ }
+
+ /* get home distance state */
+ bool home_dist_ok = check_dist_1wp();
+ /* the home dist check provides user feedback, so we initialize it to this */
+ bool user_feedback_done = !home_dist_ok;
+
+ /* try setting onboard mission item */
+ if (_param_onboard_enabled.get() && read_mission_item(true, true, &_mission_item)) {
+ /* if mission type changed, notify */
+ if (_mission_type != MISSION_TYPE_ONBOARD) {
+ mavlink_log_critical(_navigator->get_mavlink_fd(), "onboard mission now running");
+ }
+ _mission_type = MISSION_TYPE_ONBOARD;
+
+ /* try setting offboard mission item */
+ } else if (home_dist_ok && read_mission_item(false, true, &_mission_item)) {
+ /* if mission type changed, notify */
+ if (_mission_type != MISSION_TYPE_OFFBOARD) {
+ mavlink_log_critical(_navigator->get_mavlink_fd(), "offboard mission now running");
+ }
+ _mission_type = MISSION_TYPE_OFFBOARD;
+
+ } else {
+ /* no mission available or mission finished, switch to loiter */
+ if (_mission_type != MISSION_TYPE_NONE) {
+ mavlink_log_critical(_navigator->get_mavlink_fd(), "mission finished");
+
+ /* use last setpoint for loiter */
+ _navigator->set_can_loiter_at_sp(true);
+
+ } else if (!user_feedback_done) {
+ /* only tell users that we got no mission if there has not been any
+ * better, more specific feedback yet
+ */
+ mavlink_log_critical(_navigator->get_mavlink_fd(), "no valid mission available");
+ }
+ _mission_type = MISSION_TYPE_NONE;
+
+ /* set loiter mission item */
+ set_loiter_item(&_mission_item);
+
+ /* update position setpoint triplet */
+ pos_sp_triplet->previous.valid = false;
+ mission_item_to_position_setpoint(&_mission_item, &pos_sp_triplet->current);
+ pos_sp_triplet->next.valid = false;
+
+ /* reuse setpoint for LOITER only if it's not IDLE */
+ _navigator->set_can_loiter_at_sp(pos_sp_triplet->current.type == SETPOINT_TYPE_LOITER);
+
+ reset_mission_item_reached();
+ set_mission_finished();
+
+ _navigator->set_position_setpoint_triplet_updated();
+ return;
+ }
+
+ /* do takeoff on first waypoint for rotary wing vehicles */
+ if (_navigator->get_vstatus()->is_rotary_wing) {
+ /* force takeoff if landed (additional protection) */
+ if (!_takeoff && _navigator->get_vstatus()->condition_landed) {
+ _need_takeoff = true;
+ }
+
+ /* new current mission item set, check if we need takeoff */
+ if (_need_takeoff && (
+ _mission_item.nav_cmd == NAV_CMD_TAKEOFF ||
+ _mission_item.nav_cmd == NAV_CMD_WAYPOINT ||
+ _mission_item.nav_cmd == NAV_CMD_LOITER_TIME_LIMIT ||
+ _mission_item.nav_cmd == NAV_CMD_LOITER_TURN_COUNT ||
+ _mission_item.nav_cmd == NAV_CMD_LOITER_UNLIMITED ||
+ _mission_item.nav_cmd == NAV_CMD_RETURN_TO_LAUNCH)) {
+ _takeoff = true;
+ _need_takeoff = false;
+ }
+ }
+
+ if (_takeoff) {
+ /* do takeoff before going to setpoint */
+ /* set mission item as next position setpoint */
+ mission_item_to_position_setpoint(&_mission_item, &pos_sp_triplet->next);
+
+ /* calculate takeoff altitude */
+ float takeoff_alt = _mission_item.altitude;
+ if (_mission_item.altitude_is_relative) {
+ takeoff_alt += _navigator->get_home_position()->alt;
+ }
+
+ /* takeoff to at least NAV_TAKEOFF_ALT above home/ground, even if first waypoint is lower */
+ if (_navigator->get_vstatus()->condition_landed) {
+ takeoff_alt = fmaxf(takeoff_alt, _navigator->get_global_position()->alt + _param_takeoff_alt.get());
+
+ } else {
+ takeoff_alt = fmaxf(takeoff_alt, _navigator->get_home_position()->alt + _param_takeoff_alt.get());
+ }
+
+ /* check if we already above takeoff altitude */
+ if (_navigator->get_global_position()->alt < takeoff_alt - _navigator->get_acceptance_radius()) {
+ mavlink_log_critical(_navigator->get_mavlink_fd(), "takeoff to %.1f meters above home", (double)(takeoff_alt - _navigator->get_home_position()->alt));
+
+ _mission_item.nav_cmd = NAV_CMD_TAKEOFF;
+ _mission_item.lat = _navigator->get_global_position()->lat;
+ _mission_item.lon = _navigator->get_global_position()->lon;
+ _mission_item.altitude = takeoff_alt;
+ _mission_item.altitude_is_relative = false;
+ _mission_item.autocontinue = true;
+ _mission_item.time_inside = 0;
+
+ mission_item_to_position_setpoint(&_mission_item, &pos_sp_triplet->current);
+
+ _navigator->set_position_setpoint_triplet_updated();
+ return;
+
+ } else {
+ /* skip takeoff */
+ _takeoff = false;
+ }
+ }
+
+ /* set current position setpoint from mission item */
+ mission_item_to_position_setpoint(&_mission_item, &pos_sp_triplet->current);
+
+ /* require takeoff after landing or idle */
+ if (pos_sp_triplet->current.type == SETPOINT_TYPE_LAND || pos_sp_triplet->current.type == SETPOINT_TYPE_IDLE) {
+ _need_takeoff = true;
+ }
+
+ _navigator->set_can_loiter_at_sp(false);
+ reset_mission_item_reached();
+
+ if (_mission_type == MISSION_TYPE_OFFBOARD) {
+ set_current_offboard_mission_item();
+ }
+ // TODO: report onboard mission item somehow
+
+ if (_mission_item.autocontinue && _mission_item.time_inside <= 0.001f) {
+ /* try to read next mission item */
+ struct mission_item_s mission_item_next;
+
+ if (read_mission_item(_mission_type == MISSION_TYPE_ONBOARD, false, &mission_item_next)) {
+ /* got next mission item, update setpoint triplet */
+ mission_item_to_position_setpoint(&mission_item_next, &pos_sp_triplet->next);
+
+ } else {
+ /* next mission item is not available */
+ pos_sp_triplet->next.valid = false;
+ }
+
+ } else {
+ /* vehicle will be paused on current waypoint, don't set next item */
+ pos_sp_triplet->next.valid = false;
+ }
+
+ /* Save the distance between the current sp and the previous one */
+ if (pos_sp_triplet->current.valid && pos_sp_triplet->previous.valid) {
+ _distance_current_previous = get_distance_to_next_waypoint(pos_sp_triplet->current.lat,
+ pos_sp_triplet->current.lon,
+ pos_sp_triplet->previous.lat,
+ pos_sp_triplet->previous.lon);
+ }
+
+ _navigator->set_position_setpoint_triplet_updated();
+}
+
+void
+Mission::altitude_sp_foh_update()
+{
+ struct position_setpoint_triplet_s *pos_sp_triplet = _navigator->get_position_setpoint_triplet();
+
+ /* Don't change setpoint if last and current waypoint are not valid */
+ if (!pos_sp_triplet->previous.valid || !pos_sp_triplet->current.valid ||
+ !isfinite(_mission_item_previous_alt)) {
+ return;
+ }
+
+ /* Do not try to find a solution if the last waypoint is inside the acceptance radius of the current one */
+ if (_distance_current_previous - _mission_item.acceptance_radius < 0.0f) {
+ return;
+ }
+
+ /* Don't do FOH for landing and takeoff waypoints, the ground may be near
+ * and the FW controller has a custom landing logic */
+ if (_mission_item.nav_cmd == NAV_CMD_LAND || _mission_item.nav_cmd == NAV_CMD_TAKEOFF) {
+ return;
+ }
+
+
+ /* Calculate distance to current waypoint */
+ float d_current = get_distance_to_next_waypoint(_mission_item.lat, _mission_item.lon,
+ _navigator->get_global_position()->lat, _navigator->get_global_position()->lon);
+
+ /* Save distance to waypoint if it is the smallest ever achieved, however make sure that
+ * _min_current_sp_distance_xy is never larger than the distance between the current and the previous wp */
+ _min_current_sp_distance_xy = math::min(math::min(d_current, _min_current_sp_distance_xy),
+ _distance_current_previous);
+
+ /* if the minimal distance is smaller then the acceptance radius, we should be at waypoint alt
+ * navigator will soon switch to the next waypoint item (if there is one) as soon as we reach this altitude */
+ if (_min_current_sp_distance_xy < _mission_item.acceptance_radius) {
+ pos_sp_triplet->current.alt = _mission_item.altitude;
+ } else {
+ /* update the altitude sp of the 'current' item in the sp triplet, but do not update the altitude sp
+ * of the mission item as it is used to check if the mission item is reached
+ * The setpoint is set linearly and such that the system reaches the current altitude at the acceptance
+ * radius around the current waypoint
+ **/
+ float delta_alt = (_mission_item.altitude - _mission_item_previous_alt);
+ float grad = -delta_alt/(_distance_current_previous - _mission_item.acceptance_radius);
+ float a = _mission_item_previous_alt - grad * _distance_current_previous;
+ pos_sp_triplet->current.alt = a + grad * _min_current_sp_distance_xy;
+
+ }
+
+ _navigator->set_position_setpoint_triplet_updated();
+}
+
+void
+Mission::altitude_sp_foh_reset()
+{
+ _min_current_sp_distance_xy = FLT_MAX;
+}
+
+bool
+Mission::read_mission_item(bool onboard, bool is_current, struct mission_item_s *mission_item)
+{
+ /* select onboard/offboard mission */
+ int *mission_index_ptr;
+ struct mission_s *mission;
+ dm_item_t dm_item;
+ int mission_index_next;
+
+ if (onboard) {
+ /* onboard mission */
+ mission_index_next = _current_onboard_mission_index + 1;
+ mission_index_ptr = is_current ? &_current_onboard_mission_index : &mission_index_next;
+
+ mission = &_onboard_mission;
+
+ dm_item = DM_KEY_WAYPOINTS_ONBOARD;
+
+ } else {
+ /* offboard mission */
+ mission_index_next = _current_offboard_mission_index + 1;
+ mission_index_ptr = is_current ? &_current_offboard_mission_index : &mission_index_next;
+
+ mission = &_offboard_mission;
+
+ dm_item = DM_KEY_WAYPOINTS_OFFBOARD(_offboard_mission.dataman_id);
+ }
+
+ /* Repeat this several times in case there are several DO JUMPS that we need to follow along, however, after
+ * 10 iterations we have to assume that the DO JUMPS are probably cycling and give up. */
+ for (int i = 0; i < 10; i++) {
+
+ if (*mission_index_ptr < 0 || *mission_index_ptr >= (int)mission->count) {
+ /* mission item index out of bounds */
+ return false;
+ }
+
+ const ssize_t len = sizeof(struct mission_item_s);
+
+ /* read mission item to temp storage first to not overwrite current mission item if data damaged */
+ struct mission_item_s mission_item_tmp;
+
+ /* read mission item from datamanager */
+ if (dm_read(dm_item, *mission_index_ptr, &mission_item_tmp, len) != len) {
+ /* not supposed to happen unless the datamanager can't access the SD card, etc. */
+ mavlink_log_critical(_navigator->get_mavlink_fd(),
+ "ERROR waypoint could not be read");
+ return false;
+ }
+
+ /* check for DO_JUMP item, and whether it hasn't not already been repeated enough times */
+ if (mission_item_tmp.nav_cmd == NAV_CMD_DO_JUMP) {
+
+ /* do DO_JUMP as many times as requested */
+ if (mission_item_tmp.do_jump_current_count < mission_item_tmp.do_jump_repeat_count) {
+
+ /* only raise the repeat count if this is for the current mission item
+ * but not for the next mission item */
+ if (is_current) {
+ (mission_item_tmp.do_jump_current_count)++;
+ /* save repeat count */
+ if (dm_write(dm_item, *mission_index_ptr, DM_PERSIST_POWER_ON_RESET,
+ &mission_item_tmp, len) != len) {
+ /* not supposed to happen unless the datamanager can't access the
+ * dataman */
+ mavlink_log_critical(_navigator->get_mavlink_fd(),
+ "ERROR DO JUMP waypoint could not be written");
+ return false;
+ }
+ }
+ /* set new mission item index and repeat
+ * we don't have to validate here, if it's invalid, we should realize this later .*/
+ *mission_index_ptr = mission_item_tmp.do_jump_mission_index;
+
+ } else {
+ if (is_current) {
+ mavlink_log_critical(_navigator->get_mavlink_fd(),
+ "DO JUMP repetitions completed");
+ }
+ /* no more DO_JUMPS, therefore just try to continue with next mission item */
+ (*mission_index_ptr)++;
+ }
+
+ } else {
+ /* if it's not a DO_JUMP, then we were successful */
+ memcpy(mission_item, &mission_item_tmp, sizeof(struct mission_item_s));
+ return true;
+ }
+ }
+
+ /* we have given up, we don't want to cycle forever */
+ mavlink_log_critical(_navigator->get_mavlink_fd(),
+ "ERROR DO JUMP is cycling, giving up");
+ return false;
+}
+
+void
+Mission::save_offboard_mission_state()
+{
+ mission_s mission_state;
+
+ /* lock MISSION_STATE item */
+ dm_lock(DM_KEY_MISSION_STATE);
+
+ /* read current state */
+ int read_res = dm_read(DM_KEY_MISSION_STATE, 0, &mission_state, sizeof(mission_s));
+
+ if (read_res == sizeof(mission_s)) {
+ /* data read successfully, check dataman ID and items count */
+ if (mission_state.dataman_id == _offboard_mission.dataman_id && mission_state.count == _offboard_mission.count) {
+ /* navigator may modify only sequence, write modified state only if it changed */
+ if (mission_state.current_seq != _current_offboard_mission_index) {
+ if (dm_write(DM_KEY_MISSION_STATE, 0, DM_PERSIST_POWER_ON_RESET, &mission_state, sizeof(mission_s)) != sizeof(mission_s)) {
+ warnx("ERROR: can't save mission state");
+ mavlink_log_critical(_navigator->get_mavlink_fd(), "ERROR: can't save mission state");
+ }
+ }
+ }
+
+ } else {
+ /* invalid data, this must not happen and indicates error in offboard_mission publisher */
+ mission_state.dataman_id = _offboard_mission.dataman_id;
+ mission_state.count = _offboard_mission.count;
+ mission_state.current_seq = _current_offboard_mission_index;
+
+ warnx("ERROR: invalid mission state");
+ mavlink_log_critical(_navigator->get_mavlink_fd(), "ERROR: invalid mission state");
+
+ /* write modified state only if changed */
+ if (dm_write(DM_KEY_MISSION_STATE, 0, DM_PERSIST_POWER_ON_RESET, &mission_state, sizeof(mission_s)) != sizeof(mission_s)) {
+ warnx("ERROR: can't save mission state");
+ mavlink_log_critical(_navigator->get_mavlink_fd(), "ERROR: can't save mission state");
+ }
+ }
+
+ /* unlock MISSION_STATE item */
+ dm_unlock(DM_KEY_MISSION_STATE);
+}
+
+void
+Mission::set_mission_item_reached()
+{
+ _navigator->get_mission_result()->reached = true;
+ _navigator->get_mission_result()->seq_reached = _current_offboard_mission_index;
+ _navigator->publish_mission_result();
+ reset_mission_item_reached();
+}
+
+void
+Mission::set_current_offboard_mission_item()
+{
+ warnx("current offboard mission index: %d", _current_offboard_mission_index);
+ _navigator->get_mission_result()->reached = false;
+ _navigator->get_mission_result()->finished = false;
+ _navigator->get_mission_result()->seq_current = _current_offboard_mission_index;
+ _navigator->publish_mission_result();
+
+ save_offboard_mission_state();
+}
+
+void
+Mission::set_mission_finished()
+{
+ _navigator->get_mission_result()->finished = true;
+ _navigator->publish_mission_result();
+}
diff --git a/src/modules/navigator/mission.h b/src/modules/navigator/mission.h
new file mode 100644
index 000000000..ea7cc0927
--- /dev/null
+++ b/src/modules/navigator/mission.h
@@ -0,0 +1,179 @@
+/****************************************************************************
+ *
+ * Copyright (c) 2013-2014 PX4 Development Team. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+/**
+ * @file mission.h
+ *
+ * Navigator mode to access missions
+ *
+ * @author Julian Oes <julian@oes.ch>
+ * @author Thomas Gubler <thomasgubler@gmail.com>
+ * @author Anton Babushkin <anton.babushkin@me.com>
+ */
+
+#ifndef NAVIGATOR_MISSION_H
+#define NAVIGATOR_MISSION_H
+
+#include <drivers/drv_hrt.h>
+
+#include <controllib/blocks.hpp>
+#include <controllib/block/BlockParam.hpp>
+
+#include <dataman/dataman.h>
+
+#include <uORB/uORB.h>
+#include <uORB/topics/vehicle_global_position.h>
+#include <uORB/topics/position_setpoint_triplet.h>
+#include <uORB/topics/home_position.h>
+#include <uORB/topics/vehicle_status.h>
+#include <uORB/topics/mission.h>
+#include <uORB/topics/mission_result.h>
+
+#include "navigator_mode.h"
+#include "mission_block.h"
+#include "mission_feasibility_checker.h"
+
+class Navigator;
+
+class Mission : public MissionBlock
+{
+public:
+ Mission(Navigator *navigator, const char *name);
+
+ virtual ~Mission();
+
+ virtual void on_inactive();
+
+ virtual void on_activation();
+
+ virtual void on_active();
+
+ enum mission_altitude_mode {
+ MISSION_ALTMODE_ZOH = 0,
+ MISSION_ALTMODE_FOH = 1
+ };
+
+private:
+ /**
+ * Update onboard mission topic
+ */
+ void update_onboard_mission();
+
+ /**
+ * Update offboard mission topic
+ */
+ void update_offboard_mission();
+
+ /**
+ * Move on to next mission item or switch to loiter
+ */
+ void advance_mission();
+
+ /**
+ * Check distance to first waypoint (with lat/lon)
+ * @return true only if it's not too far from home (< MIS_DIST_1WP)
+ */
+ bool check_dist_1wp();
+
+ /**
+ * Set new mission items
+ */
+ void set_mission_items();
+
+ /**
+ * Updates the altitude sp to follow a foh
+ */
+ void altitude_sp_foh_update();
+
+ /**
+ * Resets the altitude sp foh logic
+ */
+ void altitude_sp_foh_reset();
+
+ /**
+ * Read current or next mission item from the dataman and watch out for DO_JUMPS
+ * @return true if successful
+ */
+ bool read_mission_item(bool onboard, bool is_current, struct mission_item_s *mission_item);
+
+ /**
+ * Save current offboard mission state to dataman
+ */
+ void save_offboard_mission_state();
+
+ /**
+ * Set a mission item as reached
+ */
+ void set_mission_item_reached();
+
+ /**
+ * Set the current offboard mission item
+ */
+ void set_current_offboard_mission_item();
+
+ /**
+ * Set that the mission is finished if one exists or that none exists
+ */
+ void set_mission_finished();
+
+ control::BlockParamInt _param_onboard_enabled;
+ control::BlockParamFloat _param_takeoff_alt;
+ control::BlockParamFloat _param_dist_1wp;
+ control::BlockParamInt _param_altmode;
+
+ struct mission_s _onboard_mission;
+ struct mission_s _offboard_mission;
+
+ int _current_onboard_mission_index;
+ int _current_offboard_mission_index;
+ bool _need_takeoff; /**< if true, then takeoff must be performed before going to the first waypoint (if needed) */
+ bool _takeoff; /**< takeoff state flag */
+
+ enum {
+ MISSION_TYPE_NONE,
+ MISSION_TYPE_ONBOARD,
+ MISSION_TYPE_OFFBOARD
+ } _mission_type;
+
+ bool _inited;
+ bool _dist_1wp_ok;
+
+ MissionFeasibilityChecker _missionFeasiblityChecker; /**< class that checks if a mission is feasible */
+
+ float _min_current_sp_distance_xy; /**< minimum distance which was achieved to the current waypoint */
+ float _mission_item_previous_alt; /**< holds the altitude of the previous mission item,
+ can be replaced by a full copy of the previous mission item if needed*/
+ float _distance_current_previous; /**< distance from previous to current sp in pos_sp_triplet,
+ only use if current and previous are valid */
+};
+
+#endif
diff --git a/src/modules/navigator/mission_block.cpp b/src/modules/navigator/mission_block.cpp
new file mode 100644
index 000000000..723caec7c
--- /dev/null
+++ b/src/modules/navigator/mission_block.cpp
@@ -0,0 +1,264 @@
+/****************************************************************************
+ *
+ * Copyright (c) 2014 PX4 Development Team. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+/**
+ * @file mission_block.cpp
+ *
+ * Helper class to use mission items
+ *
+ * @author Julian Oes <julian@oes.ch>
+ */
+
+#include <sys/types.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <math.h>
+#include <float.h>
+
+#include <systemlib/err.h>
+#include <geo/geo.h>
+#include <mavlink/mavlink_log.h>
+
+#include <uORB/uORB.h>
+
+#include "navigator.h"
+#include "mission_block.h"
+
+
+MissionBlock::MissionBlock(Navigator *navigator, const char *name) :
+ NavigatorMode(navigator, name),
+ _mission_item({0}),
+ _waypoint_position_reached(false),
+ _waypoint_yaw_reached(false),
+ _time_first_inside_orbit(0)
+{
+}
+
+MissionBlock::~MissionBlock()
+{
+}
+
+bool
+MissionBlock::is_mission_item_reached()
+{
+ if (_mission_item.nav_cmd == NAV_CMD_IDLE) {
+ return false;
+ }
+
+ if (_mission_item.nav_cmd == NAV_CMD_LAND) {
+ return _navigator->get_vstatus()->condition_landed;
+ }
+
+ /* TODO: count turns */
+ if ((/*_mission_item.nav_cmd == NAV_CMD_LOITER_TURN_COUNT ||*/
+ _mission_item.nav_cmd == NAV_CMD_LOITER_UNLIMITED)) {
+ return false;
+ }
+
+ hrt_abstime now = hrt_absolute_time();
+
+ if (!_waypoint_position_reached) {
+ float dist = -1.0f;
+ float dist_xy = -1.0f;
+ float dist_z = -1.0f;
+
+ float altitude_amsl = _mission_item.altitude_is_relative
+ ? _mission_item.altitude + _navigator->get_home_position()->alt
+ : _mission_item.altitude;
+
+ dist = get_distance_to_point_global_wgs84(_mission_item.lat, _mission_item.lon, altitude_amsl,
+ _navigator->get_global_position()->lat,
+ _navigator->get_global_position()->lon,
+ _navigator->get_global_position()->alt,
+ &dist_xy, &dist_z);
+
+ if (_mission_item.nav_cmd == NAV_CMD_TAKEOFF && _navigator->get_vstatus()->is_rotary_wing) {
+ /* require only altitude for takeoff for multicopter */
+ if (_navigator->get_global_position()->alt >
+ altitude_amsl - _navigator->get_acceptance_radius()) {
+ _waypoint_position_reached = true;
+ }
+ } else if (_mission_item.nav_cmd == NAV_CMD_TAKEOFF) {
+ /* for takeoff mission items use the parameter for the takeoff acceptance radius */
+ if (dist >= 0.0f && dist <= _navigator->get_acceptance_radius()) {
+ _waypoint_position_reached = true;
+ }
+ } else if (!_navigator->get_vstatus()->is_rotary_wing &&
+ (_mission_item.nav_cmd == NAV_CMD_LOITER_UNLIMITED ||
+ _mission_item.nav_cmd == NAV_CMD_LOITER_TIME_LIMIT ||
+ _mission_item.nav_cmd == NAV_CMD_LOITER_TURN_COUNT)) {
+ /* Loiter mission item on a non rotary wing: the aircraft is going to circle the
+ * coordinates with a radius equal to the loiter_radius field. It is not flying
+ * through the waypoint center.
+ * Therefore the item is marked as reached once the system reaches the loiter
+ * radius (+ some margin). Time inside and turn count is handled elsewhere.
+ */
+ if (dist >= 0.0f && dist <= _mission_item.loiter_radius * 1.2f) {
+ _waypoint_position_reached = true;
+ }
+ } else {
+ /* for normal mission items used their acceptance radius */
+ if (dist >= 0.0f && dist <= _mission_item.acceptance_radius) {
+ _waypoint_position_reached = true;
+ }
+ }
+ }
+
+ if (_waypoint_position_reached && !_waypoint_yaw_reached) {
+
+ /* TODO: removed takeoff, why? */
+ if (_navigator->get_vstatus()->is_rotary_wing && isfinite(_mission_item.yaw)) {
+
+ /* check yaw if defined only for rotary wing except takeoff */
+ float yaw_err = _wrap_pi(_mission_item.yaw - _navigator->get_global_position()->yaw);
+
+ if (fabsf(yaw_err) < 0.2f) { /* TODO: get rid of magic number */
+ _waypoint_yaw_reached = true;
+ }
+
+ } else {
+ _waypoint_yaw_reached = true;
+ }
+ }
+
+ /* check if the current waypoint was reached */
+ if (_waypoint_position_reached && _waypoint_yaw_reached) {
+
+ if (_time_first_inside_orbit == 0) {
+ _time_first_inside_orbit = now;
+
+ // if (_mission_item.time_inside > 0.01f) {
+ // mavlink_log_info(_mavlink_fd, "#audio: waypoint reached, wait for %.1fs",
+ // (double)_mission_item.time_inside);
+ // }
+ }
+
+ /* check if the MAV was long enough inside the waypoint orbit */
+ if (now - _time_first_inside_orbit >= (hrt_abstime)_mission_item.time_inside * 1e6f) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void
+MissionBlock::reset_mission_item_reached()
+{
+ _waypoint_position_reached = false;
+ _waypoint_yaw_reached = false;
+ _time_first_inside_orbit = 0;
+}
+
+void
+MissionBlock::mission_item_to_position_setpoint(const struct mission_item_s *item, struct position_setpoint_s *sp)
+{
+ sp->valid = true;
+ sp->lat = item->lat;
+ sp->lon = item->lon;
+ sp->alt = item->altitude_is_relative ? item->altitude + _navigator->get_home_position()->alt : item->altitude;
+ sp->yaw = item->yaw;
+ sp->loiter_radius = item->loiter_radius;
+ sp->loiter_direction = item->loiter_direction;
+ sp->pitch_min = item->pitch_min;
+
+ switch (item->nav_cmd) {
+ case NAV_CMD_IDLE:
+ sp->type = SETPOINT_TYPE_IDLE;
+ break;
+
+ case NAV_CMD_TAKEOFF:
+ sp->type = SETPOINT_TYPE_TAKEOFF;
+ break;
+
+ case NAV_CMD_LAND:
+ sp->type = SETPOINT_TYPE_LAND;
+ break;
+
+ case NAV_CMD_LOITER_TIME_LIMIT:
+ case NAV_CMD_LOITER_TURN_COUNT:
+ case NAV_CMD_LOITER_UNLIMITED:
+ sp->type = SETPOINT_TYPE_LOITER;
+ break;
+
+ default:
+ sp->type = SETPOINT_TYPE_POSITION;
+ break;
+ }
+}
+
+void
+MissionBlock::set_previous_pos_setpoint()
+{
+ struct position_setpoint_triplet_s *pos_sp_triplet = _navigator->get_position_setpoint_triplet();
+
+ if (pos_sp_triplet->current.valid) {
+ memcpy(&pos_sp_triplet->previous, &pos_sp_triplet->current, sizeof(struct position_setpoint_s));
+ }
+}
+
+void
+MissionBlock::set_loiter_item(struct mission_item_s *item)
+{
+ if (_navigator->get_vstatus()->condition_landed) {
+ /* landed, don't takeoff, but switch to IDLE mode */
+ item->nav_cmd = NAV_CMD_IDLE;
+
+ } else {
+ item->nav_cmd = NAV_CMD_LOITER_UNLIMITED;
+
+ struct position_setpoint_triplet_s *pos_sp_triplet = _navigator->get_position_setpoint_triplet();
+
+ if (_navigator->get_can_loiter_at_sp() && pos_sp_triplet->current.valid) {
+ /* use current position setpoint */
+ item->lat = pos_sp_triplet->current.lat;
+ item->lon = pos_sp_triplet->current.lon;
+ item->altitude = pos_sp_triplet->current.alt;
+
+ } else {
+ /* use current position */
+ item->lat = _navigator->get_global_position()->lat;
+ item->lon = _navigator->get_global_position()->lon;
+ item->altitude = _navigator->get_global_position()->alt;
+ }
+
+ item->altitude_is_relative = false;
+ item->yaw = NAN;
+ item->loiter_radius = _navigator->get_loiter_radius();
+ item->loiter_direction = 1;
+ item->acceptance_radius = _navigator->get_acceptance_radius();
+ item->time_inside = 0.0f;
+ item->pitch_min = 0.0f;
+ item->autocontinue = false;
+ item->origin = ORIGIN_ONBOARD;
+ }
+}
diff --git a/src/modules/navigator/mission_block.h b/src/modules/navigator/mission_block.h
new file mode 100644
index 000000000..adf50bc39
--- /dev/null
+++ b/src/modules/navigator/mission_block.h
@@ -0,0 +1,102 @@
+/****************************************************************************
+ *
+ * Copyright (c) 2014 PX4 Development Team. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+/**
+ * @file mission_block.h
+ *
+ * Helper class to use mission items
+ *
+ * @author Julian Oes <julian@oes.ch>
+ */
+
+#ifndef NAVIGATOR_MISSION_BLOCK_H
+#define NAVIGATOR_MISSION_BLOCK_H
+
+#include <drivers/drv_hrt.h>
+
+#include <uORB/topics/mission.h>
+#include <uORB/topics/vehicle_global_position.h>
+#include <uORB/topics/position_setpoint_triplet.h>
+
+#include "navigator_mode.h"
+
+class Navigator;
+
+class MissionBlock : public NavigatorMode
+{
+public:
+ /**
+ * Constructor
+ */
+ MissionBlock(Navigator *navigator, const char *name);
+
+ /**
+ * Destructor
+ */
+ virtual ~MissionBlock();
+
+protected:
+ /**
+ * Check if mission item has been reached
+ * @return true if successfully reached
+ */
+ bool is_mission_item_reached();
+ /**
+ * Reset all reached flags
+ */
+ void reset_mission_item_reached();
+
+ /**
+ * Convert a mission item to a position setpoint
+ *
+ * @param the mission item to convert
+ * @param the position setpoint that needs to be set
+ */
+ void mission_item_to_position_setpoint(const mission_item_s *item, position_setpoint_s *sp);
+
+ /**
+ * Set previous position setpoint to current setpoint
+ */
+ void set_previous_pos_setpoint();
+
+ /**
+ * Set a loiter mission item, if possible reuse the position setpoint, otherwise take the current position
+ */
+ void set_loiter_item(struct mission_item_s *item);
+
+ mission_item_s _mission_item;
+ bool _waypoint_position_reached;
+ bool _waypoint_yaw_reached;
+ hrt_abstime _time_first_inside_orbit;
+};
+
+#endif
diff --git a/src/modules/navigator/mission_feasibility_checker.cpp b/src/modules/navigator/mission_feasibility_checker.cpp
index e1a6854b2..389cdd0d2 100644
--- a/src/modules/navigator/mission_feasibility_checker.cpp
+++ b/src/modules/navigator/mission_feasibility_checker.cpp
@@ -84,7 +84,12 @@ bool MissionFeasibilityChecker::checkMissionFeasible(bool isRotarywing, dm_item_
bool MissionFeasibilityChecker::checkMissionFeasibleRotarywing(dm_item_t dm_current, size_t nMissionItems, Geofence &geofence, float home_alt)
{
- return (checkGeofence(dm_current, nMissionItems, geofence) && checkHomePositionAltitude(dm_current, nMissionItems, home_alt));
+ /* Perform checks and issue feedback to the user for all checks */
+ bool resGeofence = checkGeofence(dm_current, nMissionItems, geofence);
+ bool resHomeAltitude = checkHomePositionAltitude(dm_current, nMissionItems, home_alt);
+
+ /* Mission is only marked as feasible if all checks return true */
+ return (resGeofence && resHomeAltitude);
}
bool MissionFeasibilityChecker::checkMissionFeasibleFixedwing(dm_item_t dm_current, size_t nMissionItems, Geofence &geofence, float home_alt)
@@ -93,7 +98,13 @@ bool MissionFeasibilityChecker::checkMissionFeasibleFixedwing(dm_item_t dm_curre
updateNavigationCapabilities();
// warnx("_nav_caps.landing_slope_angle_rad %.4f, _nav_caps.landing_horizontal_slope_displacement %.4f", _nav_caps.landing_slope_angle_rad, _nav_caps.landing_horizontal_slope_displacement);
- return (checkFixedWingLanding(dm_current, nMissionItems) && checkGeofence(dm_current, nMissionItems, geofence) && checkHomePositionAltitude(dm_current, nMissionItems, home_alt));
+ /* Perform checks and issue feedback to the user for all checks */
+ bool resLanding = checkFixedWingLanding(dm_current, nMissionItems);
+ bool resGeofence = checkGeofence(dm_current, nMissionItems, geofence);
+ bool resHomeAltitude = checkHomePositionAltitude(dm_current, nMissionItems, home_alt);
+
+ /* Mission is only marked as feasible if all checks return true */
+ return (resLanding && resGeofence && resHomeAltitude);
}
bool MissionFeasibilityChecker::checkGeofence(dm_item_t dm_current, size_t nMissionItems, Geofence &geofence)
@@ -109,7 +120,7 @@ bool MissionFeasibilityChecker::checkGeofence(dm_item_t dm_current, size_t nMiss
return false;
}
- if (!geofence.inside(missionitem.lat, missionitem.lon, missionitem.altitude)) {
+ if (!geofence.inside_polygon(missionitem.lat, missionitem.lon, missionitem.altitude)) {
mavlink_log_info(_mavlink_fd, "#audio: Geofence violation waypoint %d", i);
return false;
}
@@ -135,12 +146,15 @@ bool MissionFeasibilityChecker::checkHomePositionAltitude(dm_item_t dm_current,
}
}
- if (home_alt > missionitem.altitude) {
+ /* calculate the global waypoint altitude */
+ float wp_alt = (missionitem.altitude_is_relative) ? missionitem.altitude + home_alt : missionitem.altitude;
+
+ if (home_alt > wp_alt) {
if (throw_error) {
- mavlink_log_info(_mavlink_fd, "Waypoint %d below home", i);
+ mavlink_log_critical(_mavlink_fd, "Rejecting Mission: Waypoint %d below home", i);
return false;
} else {
- mavlink_log_info(_mavlink_fd, "#audio: warning waypoint %d below home", i);
+ mavlink_log_critical(_mavlink_fd, "Warning: Waypoint %d below home", i);
return true;
}
}
@@ -213,13 +227,13 @@ bool MissionFeasibilityChecker::checkFixedWingLanding(dm_item_t dm_current, size
}
}
-
-// float slope_alt = wp_altitude + _H0 * expf(-math::max(0.0f, _flare_length - wp_distance)/_flare_constant) - _H1_virt;
+ /* No landing waypoints or no waypoints */
+ return true;
}
void MissionFeasibilityChecker::updateNavigationCapabilities()
{
- int res = orb_copy(ORB_ID(navigation_capabilities), _capabilities_sub, &_nav_caps);
+ (void)orb_copy(ORB_ID(navigation_capabilities), _capabilities_sub, &_nav_caps);
}
void MissionFeasibilityChecker::init()
diff --git a/src/modules/navigator/mission_params.c b/src/modules/navigator/mission_params.c
new file mode 100644
index 000000000..04c01fe51
--- /dev/null
+++ b/src/modules/navigator/mission_params.c
@@ -0,0 +1,97 @@
+/****************************************************************************
+ *
+ * Copyright (c) 2014 PX4 Development Team. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/**
+ * @file mission_params.c
+ *
+ * Parameters for mission.
+ *
+ * @author Julian Oes <joes@student.ethz.ch>
+ */
+
+#include <nuttx/config.h>
+
+#include <systemlib/param/param.h>
+
+/*
+ * Mission parameters, accessible via MAVLink
+ */
+
+/**
+ * Take-off altitude
+ *
+ * Even if first waypoint has altitude less then MIS_TAKEOFF_ALT above home position, system will climb to
+ * MIS_TAKEOFF_ALT on takeoff, then go to waypoint.
+ *
+ * @unit meters
+ * @group Mission
+ */
+PARAM_DEFINE_FLOAT(MIS_TAKEOFF_ALT, 10.0f);
+
+/**
+ * Enable persistent onboard mission storage
+ *
+ * When enabled, missions that have been uploaded by the GCS are stored
+ * and reloaded after reboot persistently.
+ *
+ * @min 0
+ * @max 1
+ * @group Mission
+ */
+PARAM_DEFINE_INT32(MIS_ONBOARD_EN, 1);
+
+/**
+ * Maximal horizontal distance from home to first waypoint
+ *
+ * Failsafe check to prevent running mission stored from previous flight at a new takeoff location.
+ * Set a value of zero or less to disable. The mission will not be started if the current
+ * waypoint is more distant than MIS_DIS_1WP from the current position.
+ *
+ * @min 0
+ * @max 1000
+ * @group Mission
+ */
+PARAM_DEFINE_FLOAT(MIS_DIST_1WP, 500);
+
+/**
+ * Altitude setpoint mode
+ *
+ * 0: the system will follow a zero order hold altitude setpoint
+ * 1: the system will follow a first order hold altitude setpoint
+ * values follow the definition in enum mission_altitude_mode
+ *
+ * @min 0
+ * @max 1
+ * @group Mission
+ */
+PARAM_DEFINE_INT32(MIS_ALTMODE, 0);
diff --git a/src/modules/navigator/module.mk b/src/modules/navigator/module.mk
index 6ea9dec2b..c44d4c35e 100644
--- a/src/modules/navigator/module.mk
+++ b/src/modules/navigator/module.mk
@@ -1,6 +1,6 @@
############################################################################
#
-# Copyright (c) 2013 PX4 Development Team. All rights reserved.
+# Copyright (c) 2013-2014 PX4 Development Team. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
@@ -39,11 +39,26 @@ MODULE_COMMAND = navigator
SRCS = navigator_main.cpp \
navigator_params.c \
- navigator_mission.cpp \
+ navigator_mode.cpp \
+ mission_block.cpp \
+ mission.cpp \
+ mission_params.c \
+ loiter.cpp \
+ rtl.cpp \
+ rtl_params.c \
mission_feasibility_checker.cpp \
geofence.cpp \
- geofence_params.c
+ geofence_params.c \
+ datalinkloss.cpp \
+ datalinkloss_params.c \
+ rcloss.cpp \
+ rcloss_params.c \
+ enginefailure.cpp \
+ gpsfailure.cpp \
+ gpsfailure_params.c
INCLUDE_DIRS += $(MAVLINK_SRC)/include/mavlink
MODULE_STACKSIZE = 1200
+
+MAXOPTIMIZATION = -Os
diff --git a/src/modules/navigator/navigator.h b/src/modules/navigator/navigator.h
new file mode 100644
index 000000000..9cd609955
--- /dev/null
+++ b/src/modules/navigator/navigator.h
@@ -0,0 +1,280 @@
+/***************************************************************************
+ *
+ * Copyright (c) 2013-2014 PX4 Development Team. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+/**
+ * @file navigator.h
+ * Helper class to access missions
+ * @author Julian Oes <julian@oes.ch>
+ * @author Anton Babushkin <anton.babushkin@me.com>
+ * @author Thomas Gubler <thomasgubler@gmail.com>
+ */
+
+#ifndef NAVIGATOR_H
+#define NAVIGATOR_H
+
+#include <systemlib/perf_counter.h>
+
+#include <controllib/blocks.hpp>
+#include <controllib/block/BlockParam.hpp>
+
+#include <uORB/uORB.h>
+#include <uORB/topics/mission.h>
+#include <uORB/topics/vehicle_control_mode.h>
+#include <uORB/topics/position_setpoint_triplet.h>
+#include <uORB/topics/vehicle_global_position.h>
+#include <uORB/topics/vehicle_gps_position.h>
+#include <uORB/topics/parameter_update.h>
+#include <uORB/topics/mission_result.h>
+#include <uORB/topics/vehicle_attitude_setpoint.h>
+
+#include "navigator_mode.h"
+#include "mission.h"
+#include "loiter.h"
+#include "rtl.h"
+#include "datalinkloss.h"
+#include "enginefailure.h"
+#include "gpsfailure.h"
+#include "rcloss.h"
+#include "geofence.h"
+
+/**
+ * Number of navigation modes that need on_active/on_inactive calls
+ */
+#define NAVIGATOR_MODE_ARRAY_SIZE 7
+
+class Navigator : public control::SuperBlock
+{
+public:
+ /**
+ * Constructor
+ */
+ Navigator();
+
+ /**
+ * Destructor, also kills the navigators task.
+ */
+ ~Navigator();
+
+ /**
+ * Start the navigator task.
+ *
+ * @return OK on success.
+ */
+ int start();
+
+ /**
+ * Display the navigator status.
+ */
+ void status();
+
+ /**
+ * Add point to geofence
+ */
+ void add_fence_point(int argc, char *argv[]);
+
+ /**
+ * Load fence from file
+ */
+ void load_fence_from_file(const char *filename);
+
+ /**
+ * Publish the mission result so commander and mavlink know what is going on
+ */
+ void publish_mission_result();
+
+ /**
+ * Publish the attitude sp, only to be used in very special modes when position control is deactivated
+ * Example: mode that is triggered on gps failure
+ */
+ void publish_att_sp();
+
+ /**
+ * Setters
+ */
+ void set_can_loiter_at_sp(bool can_loiter) { _can_loiter_at_sp = can_loiter; }
+ void set_position_setpoint_triplet_updated() { _pos_sp_triplet_updated = true; }
+
+ /**
+ * Getters
+ */
+ struct vehicle_status_s* get_vstatus() { return &_vstatus; }
+ struct vehicle_control_mode_s* get_control_mode() { return &_control_mode; }
+ struct vehicle_global_position_s* get_global_position() { return &_global_pos; }
+ struct vehicle_gps_position_s* get_gps_position() { return &_gps_pos; }
+ struct sensor_combined_s* get_sensor_combined() { return &_sensor_combined; }
+ struct home_position_s* get_home_position() { return &_home_pos; }
+ struct position_setpoint_triplet_s* get_position_setpoint_triplet() { return &_pos_sp_triplet; }
+ struct mission_result_s* get_mission_result() { return &_mission_result; }
+ struct vehicle_attitude_setpoint_s* get_att_sp() { return &_att_sp; }
+
+ int get_onboard_mission_sub() { return _onboard_mission_sub; }
+ int get_offboard_mission_sub() { return _offboard_mission_sub; }
+ Geofence& get_geofence() { return _geofence; }
+ bool get_can_loiter_at_sp() { return _can_loiter_at_sp; }
+ float get_loiter_radius() { return _param_loiter_radius.get(); }
+ float get_acceptance_radius() { return _param_acceptance_radius.get(); }
+ int get_mavlink_fd() { return _mavlink_fd; }
+
+private:
+
+ bool _task_should_exit; /**< if true, sensor task should exit */
+ int _navigator_task; /**< task handle for sensor task */
+
+ int _mavlink_fd; /**< the file descriptor to send messages over mavlink */
+
+ int _global_pos_sub; /**< global position subscription */
+ int _gps_pos_sub; /**< gps position subscription */
+ int _sensor_combined_sub; /**< sensor combined subscription */
+ int _home_pos_sub; /**< home position subscription */
+ int _vstatus_sub; /**< vehicle status subscription */
+ int _capabilities_sub; /**< notification of vehicle capabilities updates */
+ int _control_mode_sub; /**< vehicle control mode subscription */
+ int _onboard_mission_sub; /**< onboard mission subscription */
+ int _offboard_mission_sub; /**< offboard mission subscription */
+ int _param_update_sub; /**< param update subscription */
+
+ orb_advert_t _pos_sp_triplet_pub; /**< publish position setpoint triplet */
+ orb_advert_t _mission_result_pub;
+ orb_advert_t _att_sp_pub; /**< publish att sp
+ used only in very special failsafe modes
+ when pos control is deactivated */
+
+ vehicle_status_s _vstatus; /**< vehicle status */
+ vehicle_control_mode_s _control_mode; /**< vehicle control mode */
+ vehicle_global_position_s _global_pos; /**< global vehicle position */
+ vehicle_gps_position_s _gps_pos; /**< gps position */
+ sensor_combined_s _sensor_combined; /**< sensor values */
+ home_position_s _home_pos; /**< home position for RTL */
+ mission_item_s _mission_item; /**< current mission item */
+ navigation_capabilities_s _nav_caps; /**< navigation capabilities */
+ position_setpoint_triplet_s _pos_sp_triplet; /**< triplet of position setpoints */
+
+ mission_result_s _mission_result;
+ vehicle_attitude_setpoint_s _att_sp;
+
+ bool _mission_item_valid; /**< flags if the current mission item is valid */
+
+ perf_counter_t _loop_perf; /**< loop performance counter */
+
+ Geofence _geofence; /**< class that handles the geofence */
+ bool _geofence_violation_warning_sent; /**< prevents spaming to mavlink */
+
+ bool _inside_fence; /**< vehicle is inside fence */
+
+ NavigatorMode *_navigation_mode; /**< abstract pointer to current navigation mode class */
+ Mission _mission; /**< class that handles the missions */
+ Loiter _loiter; /**< class that handles loiter */
+ RTL _rtl; /**< class that handles RTL */
+ RCLoss _rcLoss; /**< class that handles RTL according to
+ OBC rules (rc loss mode) */
+ DataLinkLoss _dataLinkLoss; /**< class that handles the OBC datalink loss mode */
+ EngineFailure _engineFailure; /**< class that handles the engine failure mode
+ (FW only!) */
+ GpsFailure _gpsFailure; /**< class that handles the OBC gpsfailure loss mode */
+
+ NavigatorMode *_navigation_mode_array[NAVIGATOR_MODE_ARRAY_SIZE]; /**< array of navigation modes */
+
+ bool _can_loiter_at_sp; /**< flags if current position SP can be used to loiter */
+ bool _pos_sp_triplet_updated; /**< flags if position SP triplet needs to be published */
+ bool _pos_sp_triplet_published_invalid_once; /**< flags if position SP triplet has been published once to UORB */
+
+ control::BlockParamFloat _param_loiter_radius; /**< loiter radius for fixedwing */
+ control::BlockParamFloat _param_acceptance_radius; /**< acceptance for takeoff */
+ control::BlockParamInt _param_datalinkloss_obc; /**< if true: obc mode on data link loss enabled */
+ control::BlockParamInt _param_rcloss_obc; /**< if true: obc mode on rc loss enabled */
+ /**
+ * Retrieve global position
+ */
+ void global_position_update();
+
+ /**
+ * Retrieve gps position
+ */
+ void gps_position_update();
+
+ /**
+ * Retrieve sensor values
+ */
+ void sensor_combined_update();
+
+ /**
+ * Retrieve home position
+ */
+ void home_position_update();
+
+ /**
+ * Retreive navigation capabilities
+ */
+ void navigation_capabilities_update();
+
+ /**
+ * Retrieve vehicle status
+ */
+ void vehicle_status_update();
+
+ /**
+ * Retrieve vehicle control mode
+ */
+ void vehicle_control_mode_update();
+
+ /**
+ * Update parameters
+ */
+ void params_update();
+
+ /**
+ * Shim for calling task_main from task_create.
+ */
+ static void task_main_trampoline(int argc, char *argv[]);
+
+ /**
+ * Main task.
+ */
+ void task_main();
+
+ /**
+ * Translate mission item to a position setpoint.
+ */
+ void mission_item_to_position_setpoint(const mission_item_s *item, position_setpoint_s *sp);
+
+ /**
+ * Publish a new position setpoint triplet for position controllers
+ */
+ void publish_position_setpoint_triplet();
+
+ /* this class has ptr data members, so it should not be copied,
+ * consequently the copy constructors are private.
+ */
+ Navigator(const Navigator&);
+ Navigator operator=(const Navigator&);
+};
+#endif
diff --git a/src/modules/navigator/navigator_main.cpp b/src/modules/navigator/navigator_main.cpp
index 06e0c5904..df620e5e7 100644
--- a/src/modules/navigator/navigator_main.cpp
+++ b/src/modules/navigator/navigator_main.cpp
@@ -32,18 +32,19 @@
****************************************************************************/
/**
* @file navigator_main.cpp
- * Implementation of the main navigation state machine.
*
- * Handles missions, geo fencing and failsafe navigation behavior.
- * Published the mission item triplet for the position controller.
+ * Handles mission items, geo fencing and failsafe navigation behavior.
+ * Published the position setpoint triplet for the position controller.
*
* @author Lorenz Meier <lm@inf.ethz.ch>
* @author Jean Cyr <jean.m.cyr@gmail.com>
- * @author Julian Oes <joes@student.ethz.ch>
+ * @author Julian Oes <julian@oes.ch>
* @author Anton Babushkin <anton.babushkin@me.com>
+ * @author Thomas Gubler <thomasgubler@gmail.com>
*/
#include <nuttx/config.h>
+
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -54,42 +55,29 @@
#include <poll.h>
#include <time.h>
#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
#include <drivers/device/device.h>
#include <drivers/drv_hrt.h>
#include <arch/board/board.h>
+
#include <uORB/uORB.h>
-#include <uORB/topics/vehicle_global_position.h>
#include <uORB/topics/home_position.h>
-#include <uORB/topics/position_setpoint_triplet.h>
#include <uORB/topics/vehicle_status.h>
-#include <uORB/topics/vehicle_control_mode.h>
-#include <uORB/topics/parameter_update.h>
#include <uORB/topics/mission.h>
#include <uORB/topics/fence.h>
#include <uORB/topics/navigation_capabilities.h>
-#include <systemlib/param/param.h>
+#include <drivers/drv_baro.h>
+
#include <systemlib/err.h>
-#include <systemlib/state_table.h>
-#include <systemlib/perf_counter.h>
#include <systemlib/systemlib.h>
#include <geo/geo.h>
-#include <mathlib/mathlib.h>
#include <dataman/dataman.h>
+#include <mathlib/mathlib.h>
#include <mavlink/mavlink_log.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include "navigator_state.h"
-#include "navigator_mission.h"
-#include "mission_feasibility_checker.h"
-#include "geofence.h"
-
-
-/* oddly, ERROR is not defined for c++ */
-#ifdef ERROR
-# undef ERROR
-#endif
-static const int ERROR = -1;
+#include "navigator.h"
/**
* navigator app start / stop handling function
@@ -98,342 +86,73 @@ static const int ERROR = -1;
*/
extern "C" __EXPORT int navigator_main(int argc, char *argv[]);
-class Navigator : public StateTable
-{
-public:
- /**
- * Constructor
- */
- Navigator();
-
- /**
- * Destructor, also kills the navigators task.
- */
- ~Navigator();
-
- /**
- * Start the navigator task.
- *
- * @return OK on success.
- */
- int start();
-
- /**
- * Display the navigator status.
- */
- void status();
-
- /**
- * Add point to geofence
- */
- void add_fence_point(int argc, char *argv[]);
-
- /**
- * Load fence from file
- */
- void load_fence_from_file(const char *filename);
-
-private:
-
- bool _task_should_exit; /**< if true, sensor task should exit */
- int _navigator_task; /**< task handle for sensor task */
-
- int _mavlink_fd;
-
- int _global_pos_sub; /**< global position subscription */
- int _home_pos_sub; /**< home position subscription */
- int _vstatus_sub; /**< vehicle status subscription */
- int _params_sub; /**< notification of parameter updates */
- int _offboard_mission_sub; /**< notification of offboard mission updates */
- int _onboard_mission_sub; /**< notification of onboard mission updates */
- int _capabilities_sub; /**< notification of vehicle capabilities updates */
- int _control_mode_sub; /**< vehicle control mode subscription */
-
- orb_advert_t _pos_sp_triplet_pub; /**< publish position setpoint triplet */
- orb_advert_t _mission_result_pub; /**< publish mission result topic */
-
- struct vehicle_status_s _vstatus; /**< vehicle status */
- struct vehicle_control_mode_s _control_mode; /**< vehicle control mode */
- struct vehicle_global_position_s _global_pos; /**< global vehicle position */
- struct home_position_s _home_pos; /**< home position for RTL */
- struct position_setpoint_triplet_s _pos_sp_triplet; /**< triplet of position setpoints */
- struct mission_result_s _mission_result; /**< mission result for commander/mavlink */
- struct mission_item_s _mission_item; /**< current mission item */
-
- perf_counter_t _loop_perf; /**< loop performance counter */
-
- Geofence _geofence;
- bool _geofence_violation_warning_sent;
-
- bool _fence_valid; /**< flag if fence is valid */
- bool _inside_fence; /**< vehicle is inside fence */
-
- struct navigation_capabilities_s _nav_caps;
-
- class Mission _mission;
-
- bool _mission_item_valid; /**< current mission item valid */
- bool _global_pos_valid; /**< track changes of global_position */
- bool _reset_loiter_pos; /**< if true then loiter position should be set to current position */
- bool _waypoint_position_reached;
- bool _waypoint_yaw_reached;
- uint64_t _time_first_inside_orbit;
- bool _need_takeoff; /**< if need to perform vertical takeoff before going to waypoint (only for MISSION mode and VTOL vehicles) */
- bool _do_takeoff; /**< vertical takeoff state, current mission item is generated by navigator (only for MISSION mode and VTOL vehicles) */
-
- MissionFeasibilityChecker missionFeasiblityChecker;
-
- uint64_t _set_nav_state_timestamp; /**< timestamp of last handled navigation state request */
-
- bool _pos_sp_triplet_updated;
-
- const char *nav_states_str[NAV_STATE_MAX];
-
- struct {
- float min_altitude;
- float acceptance_radius;
- float loiter_radius;
- int onboard_mission_enabled;
- float takeoff_alt;
- float land_alt;
- float rtl_alt;
- float rtl_land_delay;
- } _parameters; /**< local copies of parameters */
-
- struct {
- param_t min_altitude;
- param_t acceptance_radius;
- param_t loiter_radius;
- param_t onboard_mission_enabled;
- param_t takeoff_alt;
- param_t land_alt;
- param_t rtl_alt;
- param_t rtl_land_delay;
- } _parameter_handles; /**< handles for parameters */
-
- enum Event {
- EVENT_NONE_REQUESTED,
- EVENT_READY_REQUESTED,
- EVENT_LOITER_REQUESTED,
- EVENT_MISSION_REQUESTED,
- EVENT_RTL_REQUESTED,
- EVENT_LAND_REQUESTED,
- EVENT_MISSION_CHANGED,
- EVENT_HOME_POSITION_CHANGED,
- MAX_EVENT
- };
-
- /**
- * State machine transition table
- */
- static StateTable::Tran const myTable[NAV_STATE_MAX][MAX_EVENT];
-
- enum RTLState {
- RTL_STATE_NONE = 0,
- RTL_STATE_CLIMB,
- RTL_STATE_RETURN,
- RTL_STATE_DESCEND
- };
-
- enum RTLState _rtl_state;
-
- /**
- * Update our local parameter cache.
- */
- void parameters_update();
-
- /**
- * Retrieve global position
- */
- void global_position_update();
-
- /**
- * Retrieve home position
- */
- void home_position_update();
-
- /**
- * Retreive navigation capabilities
- */
- void navigation_capabilities_update();
-
- /**
- * Retrieve offboard mission.
- */
- void offboard_mission_update(bool isrotaryWing);
-
- /**
- * Retrieve onboard mission.
- */
- void onboard_mission_update();
-
- /**
- * Retrieve vehicle status
- */
- void vehicle_status_update();
-
- /**
- * Retrieve vehicle control mode
- */
- void vehicle_control_mode_update();
-
- /**
- * Shim for calling task_main from task_create.
- */
- static void task_main_trampoline(int argc, char *argv[]);
-
- /**
- * Main task.
- */
- void task_main();
-
- void publish_safepoints(unsigned points);
-
- /**
- * Functions that are triggered when a new state is entered.
- */
- void start_none();
- void start_ready();
- void start_loiter();
- void start_mission();
- void start_rtl();
- void start_land();
- void start_land_home();
-
- /**
- * Fork for state transitions
- */
- void request_loiter_or_ready();
- void request_mission_if_available();
-
- /**
- * Guards offboard mission
- */
- bool offboard_mission_available(unsigned relative_index);
-
- /**
- * Guards onboard mission
- */
- bool onboard_mission_available(unsigned relative_index);
-
- /**
- * Reset all "reached" flags.
- */
- void reset_reached();
-
- /**
- * Check if current mission item has been reached.
- */
- bool check_mission_item_reached();
-
- /**
- * Perform actions when current mission item reached.
- */
- void on_mission_item_reached();
-
- /**
- * Move to next waypoint
- */
- void set_mission_item();
-
- /**
- * Switch to next RTL state
- */
- void set_rtl_item();
-
- /**
- * Set position setpoint for mission item
- */
- void position_setpoint_from_mission_item(position_setpoint_s *sp, mission_item_s *item);
-
- /**
- * Helper function to get a takeoff item
- */
- void get_takeoff_setpoint(position_setpoint_s *pos_sp);
-
- /**
- * Publish a new mission item triplet for position controller
- */
- void publish_position_setpoint_triplet();
-};
+#define GEOFENCE_CHECK_INTERVAL 200000
namespace navigator
{
-/* oddly, ERROR is not defined for c++ */
-#ifdef ERROR
-# undef ERROR
-#endif
-static const int ERROR = -1;
-
Navigator *g_navigator;
}
Navigator::Navigator() :
-
-/* state machine transition table */
- StateTable(&myTable[0][0], NAV_STATE_MAX, MAX_EVENT),
-
+ SuperBlock(NULL, "NAV"),
_task_should_exit(false),
_navigator_task(-1),
_mavlink_fd(-1),
-
-/* subscriptions */
_global_pos_sub(-1),
+ _gps_pos_sub(-1),
_home_pos_sub(-1),
_vstatus_sub(-1),
- _params_sub(-1),
- _offboard_mission_sub(-1),
- _onboard_mission_sub(-1),
_capabilities_sub(-1),
_control_mode_sub(-1),
-
-/* publications */
+ _onboard_mission_sub(-1),
+ _offboard_mission_sub(-1),
+ _param_update_sub(-1),
_pos_sp_triplet_pub(-1),
-
-/* performance counters */
+ _mission_result_pub(-1),
+ _att_sp_pub(-1),
+ _vstatus{},
+ _control_mode{},
+ _global_pos{},
+ _gps_pos{},
+ _sensor_combined{},
+ _home_pos{},
+ _mission_item{},
+ _nav_caps{},
+ _pos_sp_triplet{},
+ _mission_result{},
+ _att_sp{},
+ _mission_item_valid(false),
_loop_perf(perf_alloc(PC_ELAPSED, "navigator")),
-
+ _geofence{},
_geofence_violation_warning_sent(false),
- _fence_valid(false),
_inside_fence(true),
- _mission(),
- _mission_item_valid(false),
- _global_pos_valid(false),
- _reset_loiter_pos(true),
- _waypoint_position_reached(false),
- _waypoint_yaw_reached(false),
- _time_first_inside_orbit(0),
- _need_takeoff(true),
- _do_takeoff(false),
- _set_nav_state_timestamp(0),
+ _navigation_mode(nullptr),
+ _mission(this, "MIS"),
+ _loiter(this, "LOI"),
+ _rtl(this, "RTL"),
+ _rcLoss(this, "RCL"),
+ _dataLinkLoss(this, "DLL"),
+ _engineFailure(this, "EF"),
+ _gpsFailure(this, "GPSF"),
+ _can_loiter_at_sp(false),
_pos_sp_triplet_updated(false),
-/* states */
- _rtl_state(RTL_STATE_NONE)
+ _pos_sp_triplet_published_invalid_once(false),
+ _param_loiter_radius(this, "LOITER_RAD"),
+ _param_acceptance_radius(this, "ACC_RAD"),
+ _param_datalinkloss_obc(this, "DLL_OBC"),
+ _param_rcloss_obc(this, "RCL_OBC")
{
- _parameter_handles.min_altitude = param_find("NAV_MIN_ALT");
- _parameter_handles.acceptance_radius = param_find("NAV_ACCEPT_RAD");
- _parameter_handles.loiter_radius = param_find("NAV_LOITER_RAD");
- _parameter_handles.onboard_mission_enabled = param_find("NAV_ONB_MIS_EN");
- _parameter_handles.takeoff_alt = param_find("NAV_TAKEOFF_ALT");
- _parameter_handles.land_alt = param_find("NAV_LAND_ALT");
- _parameter_handles.rtl_alt = param_find("NAV_RTL_ALT");
- _parameter_handles.rtl_land_delay = param_find("NAV_RTL_LAND_T");
-
- memset(&_pos_sp_triplet, 0, sizeof(struct position_setpoint_triplet_s));
- memset(&_mission_item, 0, sizeof(struct mission_item_s));
-
- memset(&nav_states_str, 0, sizeof(nav_states_str));
- nav_states_str[0] = "NONE";
- nav_states_str[1] = "READY";
- nav_states_str[2] = "LOITER";
- nav_states_str[3] = "MISSION";
- nav_states_str[4] = "RTL";
- nav_states_str[5] = "LAND";
-
- /* Initialize state machine */
- myState = NAV_STATE_NONE;
- start_none();
+ /* Create a list of our possible navigation types */
+ _navigation_mode_array[0] = &_mission;
+ _navigation_mode_array[1] = &_loiter;
+ _navigation_mode_array[2] = &_rtl;
+ _navigation_mode_array[3] = &_dataLinkLoss;
+ _navigation_mode_array[4] = &_engineFailure;
+ _navigation_mode_array[5] = &_gpsFailure;
+ _navigation_mode_array[6] = &_rcLoss;
+
+ updateParams();
}
Navigator::~Navigator()
@@ -462,92 +181,33 @@ Navigator::~Navigator()
}
void
-Navigator::parameters_update()
-{
- /* read from param to clear updated flag */
- struct parameter_update_s update;
- orb_copy(ORB_ID(parameter_update), _params_sub, &update);
-
- param_get(_parameter_handles.min_altitude, &(_parameters.min_altitude));
- param_get(_parameter_handles.acceptance_radius, &(_parameters.acceptance_radius));
- param_get(_parameter_handles.loiter_radius, &(_parameters.loiter_radius));
- param_get(_parameter_handles.onboard_mission_enabled, &(_parameters.onboard_mission_enabled));
- param_get(_parameter_handles.takeoff_alt, &(_parameters.takeoff_alt));
- param_get(_parameter_handles.land_alt, &(_parameters.land_alt));
- param_get(_parameter_handles.rtl_alt, &(_parameters.rtl_alt));
- param_get(_parameter_handles.rtl_land_delay, &(_parameters.rtl_land_delay));
-
- _mission.set_onboard_mission_allowed((bool)_parameter_handles.onboard_mission_enabled);
-
- _geofence.updateParams();
-}
-
-void
Navigator::global_position_update()
{
orb_copy(ORB_ID(vehicle_global_position), _global_pos_sub, &_global_pos);
}
void
-Navigator::home_position_update()
+Navigator::gps_position_update()
{
- orb_copy(ORB_ID(home_position), _home_pos_sub, &_home_pos);
+ orb_copy(ORB_ID(vehicle_gps_position), _gps_pos_sub, &_gps_pos);
}
void
-Navigator::navigation_capabilities_update()
+Navigator::sensor_combined_update()
{
- orb_copy(ORB_ID(navigation_capabilities), _capabilities_sub, &_nav_caps);
+ orb_copy(ORB_ID(sensor_combined), _sensor_combined_sub, &_sensor_combined);
}
-
void
-Navigator::offboard_mission_update(bool isrotaryWing)
+Navigator::home_position_update()
{
- struct mission_s offboard_mission;
-
- if (orb_copy(ORB_ID(offboard_mission), _offboard_mission_sub, &offboard_mission) == OK) {
-
- /* Check mission feasibility, for now do not handle the return value,
- * however warnings are issued to the gcs via mavlink from inside the MissionFeasiblityChecker */
- dm_item_t dm_current;
-
- if (offboard_mission.dataman_id == 0) {
- dm_current = DM_KEY_WAYPOINTS_OFFBOARD_0;
-
- } else {
- dm_current = DM_KEY_WAYPOINTS_OFFBOARD_1;
- }
-
- missionFeasiblityChecker.checkMissionFeasible(isrotaryWing, dm_current, (size_t)offboard_mission.count, _geofence, _home_pos.alt);
-
- _mission.set_offboard_dataman_id(offboard_mission.dataman_id);
-
- _mission.set_offboard_mission_count(offboard_mission.count);
- _mission.set_current_offboard_mission_index(offboard_mission.current_index);
-
- } else {
- _mission.set_offboard_mission_count(0);
- _mission.set_current_offboard_mission_index(0);
- }
-
- _mission.publish_mission_result();
+ orb_copy(ORB_ID(home_position), _home_pos_sub, &_home_pos);
}
void
-Navigator::onboard_mission_update()
+Navigator::navigation_capabilities_update()
{
- struct mission_s onboard_mission;
-
- if (orb_copy(ORB_ID(onboard_mission), _onboard_mission_sub, &onboard_mission) == OK) {
-
- _mission.set_onboard_mission_count(onboard_mission.count);
- _mission.set_current_onboard_mission_index(onboard_mission.current_index);
-
- } else {
- _mission.set_onboard_mission_count(0);
- _mission.set_current_onboard_mission_index(0);
- }
+ orb_copy(ORB_ID(navigation_capabilities), _capabilities_sub, &_nav_caps);
}
void
@@ -570,6 +230,13 @@ Navigator::vehicle_control_mode_update()
}
void
+Navigator::params_update()
+{
+ parameter_update_s param_update;
+ orb_copy(ORB_ID(parameter_update), _param_update_sub, &param_update);
+}
+
+void
Navigator::task_main_trampoline(int argc, char *argv[])
{
navigator::g_navigator->task_main();
@@ -580,16 +247,13 @@ Navigator::task_main()
{
/* inform about start */
warnx("Initializing..");
- fflush(stdout);
_mavlink_fd = open(MAVLINK_LOG_DEVICE, 0);
-
- mavlink_log_info(_mavlink_fd, "[navigator] started");
+ _geofence.setMavlinkFd(_mavlink_fd);
/* Try to load the geofence:
* if /fs/microsd/etc/geofence.txt load from this file
- * else clear geofence data in datamanager
- */
+ * else clear geofence data in datamanager */
struct stat buffer;
if (stat(GEOFENCE_FILENAME, &buffer) == 0) {
@@ -597,38 +261,39 @@ Navigator::task_main()
_geofence.loadFromFile(GEOFENCE_FILENAME);
} else {
+ mavlink_log_critical(_mavlink_fd, "#audio: No geofence file");
if (_geofence.clearDm() > 0)
warnx("Geofence cleared");
else
warnx("Could not clear geofence");
}
- /*
- * do subscriptions
- */
+ /* do subscriptions */
_global_pos_sub = orb_subscribe(ORB_ID(vehicle_global_position));
- _offboard_mission_sub = orb_subscribe(ORB_ID(offboard_mission));
- _onboard_mission_sub = orb_subscribe(ORB_ID(onboard_mission));
+ _gps_pos_sub = orb_subscribe(ORB_ID(vehicle_gps_position));
+ _sensor_combined_sub = orb_subscribe(ORB_ID(sensor_combined));
_capabilities_sub = orb_subscribe(ORB_ID(navigation_capabilities));
_vstatus_sub = orb_subscribe(ORB_ID(vehicle_status));
_control_mode_sub = orb_subscribe(ORB_ID(vehicle_control_mode));
- _params_sub = orb_subscribe(ORB_ID(parameter_update));
_home_pos_sub = orb_subscribe(ORB_ID(home_position));
+ _onboard_mission_sub = orb_subscribe(ORB_ID(onboard_mission));
+ _offboard_mission_sub = orb_subscribe(ORB_ID(offboard_mission));
+ _param_update_sub = orb_subscribe(ORB_ID(parameter_update));
/* copy all topics first time */
vehicle_status_update();
vehicle_control_mode_update();
- parameters_update();
global_position_update();
+ gps_position_update();
+ sensor_combined_update();
home_position_update();
navigation_capabilities_update();
- offboard_mission_update(_vstatus.is_rotary_wing);
- onboard_mission_update();
+ params_update();
- /* rate limit position updates to 50 Hz */
+ /* rate limit position and sensor updates to 50 Hz */
orb_set_interval(_global_pos_sub, 20);
+ orb_set_interval(_sensor_combined_sub, 20);
- unsigned prevState = NAV_STATE_NONE;
hrt_abstime mavlink_open_time = 0;
const hrt_abstime mavlink_open_interval = 500000;
@@ -636,21 +301,21 @@ Navigator::task_main()
struct pollfd fds[8];
/* Setup of loop */
- fds[0].fd = _params_sub;
+ fds[0].fd = _global_pos_sub;
fds[0].events = POLLIN;
- fds[1].fd = _global_pos_sub;
+ fds[1].fd = _home_pos_sub;
fds[1].events = POLLIN;
- fds[2].fd = _home_pos_sub;
+ fds[2].fd = _capabilities_sub;
fds[2].events = POLLIN;
- fds[3].fd = _capabilities_sub;
+ fds[3].fd = _vstatus_sub;
fds[3].events = POLLIN;
- fds[4].fd = _offboard_mission_sub;
+ fds[4].fd = _control_mode_sub;
fds[4].events = POLLIN;
- fds[5].fd = _onboard_mission_sub;
+ fds[5].fd = _param_update_sub;
fds[5].events = POLLIN;
- fds[6].fd = _vstatus_sub;
+ fds[6].fd = _sensor_combined_sub;
fds[6].events = POLLIN;
- fds[7].fd = _control_mode_sub;
+ fds[7].fd = _gps_pos_sub;
fds[7].events = POLLIN;
while (!_task_should_exit) {
@@ -658,13 +323,12 @@ Navigator::task_main()
/* wait for up to 100ms for data */
int pret = poll(&fds[0], (sizeof(fds) / sizeof(fds[0])), 100);
- /* timed out - periodic check for _task_should_exit, etc. */
if (pret == 0) {
+ /* timed out - periodic check for _task_should_exit, etc. */
continue;
- }
- /* this is undesirable but not much we can do - might want to flag unhappy status */
- if (pret < 0) {
+ } else if (pret < 0) {
+ /* this is undesirable but not much we can do - might want to flag unhappy status */
warn("poll error %d, %d", pret, errno);
continue;
}
@@ -677,162 +341,157 @@ Navigator::task_main()
_mavlink_fd = open(MAVLINK_LOG_DEVICE, 0);
}
- /* vehicle control mode updated */
+ static bool have_geofence_position_data = false;
+
+ /* gps updated */
if (fds[7].revents & POLLIN) {
- vehicle_control_mode_update();
+ gps_position_update();
+ if (_geofence.getSource() == Geofence::GF_SOURCE_GPS) {
+ have_geofence_position_data = true;
+ }
}
- /* vehicle status updated */
+ /* sensors combined updated */
if (fds[6].revents & POLLIN) {
- vehicle_status_update();
-
- /* evaluate state requested by commander */
- if (_control_mode.flag_armed && _control_mode.flag_control_auto_enabled) {
- /* publish position setpoint triplet on each status update if navigator active */
- _pos_sp_triplet_updated = true;
-
- if (_vstatus.set_nav_state_timestamp != _set_nav_state_timestamp) {
- /* commander requested new navigation mode, try to set it */
- switch (_vstatus.set_nav_state) {
- case NAV_STATE_NONE:
- /* nothing to do */
- break;
-
- case NAV_STATE_LOITER:
- request_loiter_or_ready();
- break;
-
- case NAV_STATE_MISSION:
- request_mission_if_available();
- break;
-
- case NAV_STATE_RTL:
- if (!(_rtl_state == RTL_STATE_DESCEND &&
- (myState == NAV_STATE_LAND || myState == NAV_STATE_LOITER)) &&
- _vstatus.condition_home_position_valid) {
- dispatch(EVENT_RTL_REQUESTED);
- }
-
- break;
-
- case NAV_STATE_LAND:
- dispatch(EVENT_LAND_REQUESTED);
-
- break;
-
- default:
- warnx("ERROR: Requested navigation state not supported");
- break;
- }
-
- } else {
- /* on first switch to AUTO try mission by default, if none is available fallback to loiter */
- if (myState == NAV_STATE_NONE) {
- request_mission_if_available();
- }
- }
-
- /* check if waypoint has been reached in MISSION, RTL and LAND modes */
- if (myState == NAV_STATE_MISSION || myState == NAV_STATE_RTL || myState == NAV_STATE_LAND) {
- if (check_mission_item_reached()) {
- on_mission_item_reached();
- }
- }
-
- } else {
- /* navigator shouldn't act */
- dispatch(EVENT_NONE_REQUESTED);
- }
-
- _set_nav_state_timestamp = _vstatus.set_nav_state_timestamp;
+ sensor_combined_update();
}
/* parameters updated */
- if (fds[0].revents & POLLIN) {
- parameters_update();
- /* note that these new parameters won't be in effect until a mission triplet is published again */
+ if (fds[5].revents & POLLIN) {
+ params_update();
+ updateParams();
}
- /* navigation capabilities updated */
- if (fds[3].revents & POLLIN) {
- navigation_capabilities_update();
+ /* vehicle control mode updated */
+ if (fds[4].revents & POLLIN) {
+ vehicle_control_mode_update();
}
- /* offboard mission updated */
- if (fds[4].revents & POLLIN) {
- offboard_mission_update(_vstatus.is_rotary_wing);
- // XXX check if mission really changed
- dispatch(EVENT_MISSION_CHANGED);
+ /* vehicle status updated */
+ if (fds[3].revents & POLLIN) {
+ vehicle_status_update();
}
- /* onboard mission updated */
- if (fds[5].revents & POLLIN) {
- onboard_mission_update();
- // XXX check if mission really changed
- dispatch(EVENT_MISSION_CHANGED);
+ /* navigation capabilities updated */
+ if (fds[2].revents & POLLIN) {
+ navigation_capabilities_update();
}
/* home position updated */
- if (fds[2].revents & POLLIN) {
+ if (fds[1].revents & POLLIN) {
home_position_update();
- // XXX check if home position really changed
- dispatch(EVENT_HOME_POSITION_CHANGED);
}
/* global position updated */
- if (fds[1].revents & POLLIN) {
+ if (fds[0].revents & POLLIN) {
global_position_update();
-
- if (_control_mode.flag_armed && _control_mode.flag_control_auto_enabled) {
- /* publish position setpoint triplet on each position update if navigator active */
- _pos_sp_triplet_updated = true;
-
- if (myState == NAV_STATE_LAND && !_global_pos_valid) {
- /* got global position when landing, update setpoint */
- start_land();
- }
-
- /* check if waypoint has been reached in MISSION, RTL and LAND modes */
- if (myState == NAV_STATE_MISSION || myState == NAV_STATE_RTL || myState == NAV_STATE_LAND) {
- if (check_mission_item_reached()) {
- on_mission_item_reached();
- }
- }
+ if (_geofence.getSource() == Geofence::GF_SOURCE_GLOBALPOS) {
+ have_geofence_position_data = true;
}
+ }
- /* Check geofence violation */
- if (!_geofence.inside(&_global_pos)) {
- //xxx: publish geofence violation here (or change local flag depending on which app handles the flight termination)
+ /* Check geofence violation */
+ static hrt_abstime last_geofence_check = 0;
+ if (have_geofence_position_data && hrt_elapsed_time(&last_geofence_check) > GEOFENCE_CHECK_INTERVAL) {
+ bool inside = _geofence.inside(_global_pos, _gps_pos, _sensor_combined.baro_alt_meter);
+ last_geofence_check = hrt_absolute_time();
+ have_geofence_position_data = false;
+ if (!inside) {
+ /* inform other apps via the mission result */
+ _mission_result.geofence_violated = true;
+ publish_mission_result();
/* Issue a warning about the geofence violation once */
if (!_geofence_violation_warning_sent) {
mavlink_log_critical(_mavlink_fd, "#audio: Geofence violation");
_geofence_violation_warning_sent = true;
}
-
} else {
+ /* inform other apps via the mission result */
+ _mission_result.geofence_violated = false;
+ publish_mission_result();
/* Reset the _geofence_violation_warning_sent field */
_geofence_violation_warning_sent = false;
}
}
- _global_pos_valid = _vstatus.condition_global_position_valid;
+ /* Do stuff according to navigation state set by commander */
+ switch (_vstatus.nav_state) {
+ case NAVIGATION_STATE_MANUAL:
+ case NAVIGATION_STATE_ACRO:
+ case NAVIGATION_STATE_ALTCTL:
+ case NAVIGATION_STATE_POSCTL:
+ case NAVIGATION_STATE_LAND:
+ case NAVIGATION_STATE_TERMINATION:
+ case NAVIGATION_STATE_OFFBOARD:
+ _navigation_mode = nullptr;
+ _can_loiter_at_sp = false;
+ break;
+ case NAVIGATION_STATE_AUTO_MISSION:
+ _pos_sp_triplet_published_invalid_once = false;
+ _navigation_mode = &_mission;
+ break;
+ case NAVIGATION_STATE_AUTO_LOITER:
+ _pos_sp_triplet_published_invalid_once = false;
+ _navigation_mode = &_loiter;
+ break;
+ case NAVIGATION_STATE_AUTO_RCRECOVER:
+ _pos_sp_triplet_published_invalid_once = false;
+ if (_param_rcloss_obc.get() != 0) {
+ _navigation_mode = &_rcLoss;
+ } else {
+ _navigation_mode = &_rtl;
+ }
+ break;
+ case NAVIGATION_STATE_AUTO_RTL:
+ _pos_sp_triplet_published_invalid_once = false;
+ _navigation_mode = &_rtl;
+ break;
+ case NAVIGATION_STATE_AUTO_RTGS:
+ /* Use complex data link loss mode only when enabled via param
+ * otherwise use rtl */
+ _pos_sp_triplet_published_invalid_once = false;
+ if (_param_datalinkloss_obc.get() != 0) {
+ _navigation_mode = &_dataLinkLoss;
+ } else {
+ _navigation_mode = &_rtl;
+ }
+ break;
+ case NAVIGATION_STATE_AUTO_LANDENGFAIL:
+ _pos_sp_triplet_published_invalid_once = false;
+ _navigation_mode = &_engineFailure;
+ break;
+ case NAVIGATION_STATE_AUTO_LANDGPSFAIL:
+ _pos_sp_triplet_published_invalid_once = false;
+ _navigation_mode = &_gpsFailure;
+ break;
+ default:
+ _navigation_mode = nullptr;
+ _can_loiter_at_sp = false;
+ break;
+ }
- /* publish position setpoint triplet if updated */
- if (_pos_sp_triplet_updated) {
- _pos_sp_triplet_updated = false;
- publish_position_setpoint_triplet();
+ /* iterate through navigation modes and set active/inactive for each */
+ for(unsigned int i = 0; i < NAVIGATOR_MODE_ARRAY_SIZE; i++) {
+ _navigation_mode_array[i]->run(_navigation_mode == _navigation_mode_array[i]);
}
- /* notify user about state changes */
- if (myState != prevState) {
- mavlink_log_info(_mavlink_fd, "#audio: navigation state: %s", nav_states_str[myState]);
- prevState = myState;
+ /* if nothing is running, set position setpoint triplet invalid once */
+ if (_navigation_mode == nullptr && !_pos_sp_triplet_published_invalid_once) {
+ _pos_sp_triplet_published_invalid_once = true;
+ _pos_sp_triplet.previous.valid = false;
+ _pos_sp_triplet.current.valid = false;
+ _pos_sp_triplet.next.valid = false;
+ _pos_sp_triplet_updated = true;
+ }
+
+ if (_pos_sp_triplet_updated) {
+ publish_position_setpoint_triplet();
+ _pos_sp_triplet_updated = false;
}
perf_end(_loop_perf);
}
-
warnx("exiting.");
_navigator_task = -1;
@@ -847,8 +506,8 @@ Navigator::start()
/* start the task */
_navigator_task = task_spawn_cmd("navigator",
SCHED_DEFAULT,
- SCHED_PRIORITY_MAX - 5,
- 2000,
+ SCHED_PRIORITY_DEFAULT + 20,
+ 1800,
(main_t)&Navigator::task_main_trampoline,
nullptr);
@@ -863,767 +522,41 @@ Navigator::start()
void
Navigator::status()
{
- warnx("Global position: %svalid", _global_pos_valid ? "" : "in");
-
- if (_global_pos_valid) {
- warnx("Longitude %5.5f degrees, latitude %5.5f degrees", _global_pos.lon, _global_pos.lat);
- warnx("Altitude %5.5f meters, altitude above home %5.5f meters",
- (double)_global_pos.alt, (double)(_global_pos.alt - _home_pos.alt));
- warnx("Ground velocity in m/s, N %5.5f, E %5.5f, D %5.5f",
- (double)_global_pos.vel_n, (double)_global_pos.vel_e, (double)_global_pos.vel_d);
- warnx("Compass heading in degrees %5.5f", (double)(_global_pos.yaw * M_RAD_TO_DEG_F));
- }
+ /* TODO: add this again */
+ // warnx("Global position is %svalid", _global_pos_valid ? "" : "in");
- if (_fence_valid) {
+ // if (_global_pos.global_valid) {
+ // warnx("Longitude %5.5f degrees, latitude %5.5f degrees", _global_pos.lon, _global_pos.lat);
+ // warnx("Altitude %5.5f meters, altitude above home %5.5f meters",
+ // (double)_global_pos.alt, (double)(_global_pos.alt - _home_pos.alt));
+ // warnx("Ground velocity in m/s, N %5.5f, E %5.5f, D %5.5f",
+ // (double)_global_pos.vel_n, (double)_global_pos.vel_e, (double)_global_pos.vel_d);
+ // warnx("Compass heading in degrees %5.5f", (double)(_global_pos.yaw * M_RAD_TO_DEG_F));
+ // }
+
+ if (_geofence.valid()) {
warnx("Geofence is valid");
+ /* TODO: needed? */
// warnx("Vertex longitude latitude");
// for (unsigned i = 0; i < _fence.count; i++)
// warnx("%6u %9.5f %8.5f", i, (double)_fence.vertices[i].lon, (double)_fence.vertices[i].lat);
} else {
- warnx("Geofence not set");
- }
-
- switch (myState) {
- case NAV_STATE_NONE:
- warnx("State: None");
- break;
-
- case NAV_STATE_LOITER:
- warnx("State: Loiter");
- break;
-
- case NAV_STATE_MISSION:
- warnx("State: Mission");
- break;
-
- case NAV_STATE_RTL:
- warnx("State: RTL");
- break;
-
- default:
- warnx("State: Unknown");
- break;
+ warnx("Geofence not set (no /etc/geofence.txt on microsd) or not valid");
}
}
-StateTable::Tran const Navigator::myTable[NAV_STATE_MAX][MAX_EVENT] = {
- {
- /* NAV_STATE_NONE */
- /* EVENT_NONE_REQUESTED */ {NO_ACTION, NAV_STATE_NONE},
- /* EVENT_READY_REQUESTED */ {ACTION(&Navigator::start_ready), NAV_STATE_READY},
- /* EVENT_LOITER_REQUESTED */ {ACTION(&Navigator::start_loiter), NAV_STATE_LOITER},
- /* EVENT_MISSION_REQUESTED */ {ACTION(&Navigator::start_mission), NAV_STATE_MISSION},
- /* EVENT_RTL_REQUESTED */ {ACTION(&Navigator::start_rtl), NAV_STATE_RTL},
- /* EVENT_LAND_REQUESTED */ {ACTION(&Navigator::start_land), NAV_STATE_LAND},
- /* EVENT_MISSION_CHANGED */ {NO_ACTION, NAV_STATE_NONE},
- /* EVENT_HOME_POSITION_CHANGED */ {NO_ACTION, NAV_STATE_NONE},
- },
- {
- /* NAV_STATE_READY */
- /* EVENT_NONE_REQUESTED */ {ACTION(&Navigator::start_none), NAV_STATE_NONE},
- /* EVENT_READY_REQUESTED */ {NO_ACTION, NAV_STATE_READY},
- /* EVENT_LOITER_REQUESTED */ {NO_ACTION, NAV_STATE_READY},
- /* EVENT_MISSION_REQUESTED */ {ACTION(&Navigator::start_mission), NAV_STATE_MISSION},
- /* EVENT_RTL_REQUESTED */ {NO_ACTION, NAV_STATE_READY},
- /* EVENT_LAND_REQUESTED */ {NO_ACTION, NAV_STATE_READY},
- /* EVENT_MISSION_CHANGED */ {NO_ACTION, NAV_STATE_READY},
- /* EVENT_HOME_POSITION_CHANGED */ {NO_ACTION, NAV_STATE_READY},
- },
- {
- /* NAV_STATE_LOITER */
- /* EVENT_NONE_REQUESTED */ {ACTION(&Navigator::start_none), NAV_STATE_NONE},
- /* EVENT_READY_REQUESTED */ {NO_ACTION, NAV_STATE_LOITER},
- /* EVENT_LOITER_REQUESTED */ {NO_ACTION, NAV_STATE_LOITER},
- /* EVENT_MISSION_REQUESTED */ {ACTION(&Navigator::start_mission), NAV_STATE_MISSION},
- /* EVENT_RTL_REQUESTED */ {ACTION(&Navigator::start_rtl), NAV_STATE_RTL},
- /* EVENT_LAND_REQUESTED */ {ACTION(&Navigator::start_land), NAV_STATE_LAND},
- /* EVENT_MISSION_CHANGED */ {NO_ACTION, NAV_STATE_LOITER},
- /* EVENT_HOME_POSITION_CHANGED */ {NO_ACTION, NAV_STATE_LOITER},
- },
- {
- /* NAV_STATE_MISSION */
- /* EVENT_NONE_REQUESTED */ {ACTION(&Navigator::start_none), NAV_STATE_NONE},
- /* EVENT_READY_REQUESTED */ {ACTION(&Navigator::start_ready), NAV_STATE_READY},
- /* EVENT_LOITER_REQUESTED */ {ACTION(&Navigator::start_loiter), NAV_STATE_LOITER},
- /* EVENT_MISSION_REQUESTED */ {NO_ACTION, NAV_STATE_MISSION},
- /* EVENT_RTL_REQUESTED */ {ACTION(&Navigator::start_rtl), NAV_STATE_RTL},
- /* EVENT_LAND_REQUESTED */ {ACTION(&Navigator::start_land), NAV_STATE_LAND},
- /* EVENT_MISSION_CHANGED */ {ACTION(&Navigator::start_mission), NAV_STATE_MISSION},
- /* EVENT_HOME_POSITION_CHANGED */ {NO_ACTION, NAV_STATE_MISSION},
- },
- {
- /* NAV_STATE_RTL */
- /* EVENT_NONE_REQUESTED */ {ACTION(&Navigator::start_none), NAV_STATE_NONE},
- /* EVENT_READY_REQUESTED */ {ACTION(&Navigator::start_ready), NAV_STATE_READY},
- /* EVENT_LOITER_REQUESTED */ {ACTION(&Navigator::start_loiter), NAV_STATE_LOITER},
- /* EVENT_MISSION_REQUESTED */ {ACTION(&Navigator::start_mission), NAV_STATE_MISSION},
- /* EVENT_RTL_REQUESTED */ {NO_ACTION, NAV_STATE_RTL},
- /* EVENT_LAND_REQUESTED */ {ACTION(&Navigator::start_land_home), NAV_STATE_LAND},
- /* EVENT_MISSION_CHANGED */ {NO_ACTION, NAV_STATE_RTL},
- /* EVENT_HOME_POSITION_CHANGED */ {ACTION(&Navigator::start_rtl), NAV_STATE_RTL}, // TODO need to reset rtl_state
- },
- {
- /* NAV_STATE_LAND */
- /* EVENT_NONE_REQUESTED */ {ACTION(&Navigator::start_none), NAV_STATE_NONE},
- /* EVENT_READY_REQUESTED */ {ACTION(&Navigator::start_ready), NAV_STATE_READY},
- /* EVENT_LOITER_REQUESTED */ {ACTION(&Navigator::start_loiter), NAV_STATE_LOITER},
- /* EVENT_MISSION_REQUESTED */ {ACTION(&Navigator::start_mission), NAV_STATE_MISSION},
- /* EVENT_RTL_REQUESTED */ {ACTION(&Navigator::start_rtl), NAV_STATE_RTL},
- /* EVENT_LAND_REQUESTED */ {NO_ACTION, NAV_STATE_LAND},
- /* EVENT_MISSION_CHANGED */ {NO_ACTION, NAV_STATE_LAND},
- /* EVENT_HOME_POSITION_CHANGED */ {NO_ACTION, NAV_STATE_LAND},
- },
-};
-
-void
-Navigator::start_none()
-{
- reset_reached();
-
- _pos_sp_triplet.previous.valid = false;
- _pos_sp_triplet.current.valid = false;
- _pos_sp_triplet.next.valid = false;
- _mission_item_valid = false;
-
- _reset_loiter_pos = true;
- _do_takeoff = false;
- _rtl_state = RTL_STATE_NONE;
-
- _pos_sp_triplet_updated = true;
-}
-
-void
-Navigator::start_ready()
-{
- reset_reached();
-
- _pos_sp_triplet.previous.valid = false;
- _pos_sp_triplet.current.valid = true;
- _pos_sp_triplet.next.valid = false;
-
- _pos_sp_triplet.current.type = SETPOINT_TYPE_IDLE;
-
- _mission_item_valid = false;
-
- _reset_loiter_pos = true;
- _do_takeoff = false;
-
- if (_rtl_state != RTL_STATE_DESCEND) {
- /* reset RTL state if landed not at home */
- _rtl_state = RTL_STATE_NONE;
- }
-
- _pos_sp_triplet_updated = true;
-}
-
-void
-Navigator::start_loiter()
-{
- reset_reached();
-
- _do_takeoff = false;
-
- /* set loiter position if needed */
- if (_reset_loiter_pos || !_pos_sp_triplet.current.valid) {
- _reset_loiter_pos = false;
-
- _pos_sp_triplet.current.lat = _global_pos.lat;
- _pos_sp_triplet.current.lon = _global_pos.lon;
- _pos_sp_triplet.current.yaw = NAN; // NAN means to use current yaw
-
- float min_alt_amsl = _parameters.min_altitude + _home_pos.alt;
-
- /* use current altitude if above min altitude set by parameter */
- if (_global_pos.alt < min_alt_amsl && !_vstatus.is_rotary_wing) {
- _pos_sp_triplet.current.alt = min_alt_amsl;
- mavlink_log_info(_mavlink_fd, "#audio: loiter %.1fm higher", (double)(min_alt_amsl - _global_pos.alt));
-
- } else {
- _pos_sp_triplet.current.alt = _global_pos.alt;
- mavlink_log_info(_mavlink_fd, "#audio: loiter at current altitude");
- }
-
- }
- _pos_sp_triplet.current.type = SETPOINT_TYPE_LOITER;
- _pos_sp_triplet.current.loiter_radius = _parameters.loiter_radius;
- _pos_sp_triplet.current.loiter_direction = 1;
- _pos_sp_triplet.previous.valid = false;
- _pos_sp_triplet.current.valid = true;
- _pos_sp_triplet.next.valid = false;
- _mission_item_valid = false;
-
- _pos_sp_triplet_updated = true;
-}
-
-void
-Navigator::start_mission()
-{
- _need_takeoff = true;
-
- set_mission_item();
-}
-
-void
-Navigator::set_mission_item()
-{
- reset_reached();
-
- /* copy current mission to previous item */
- memcpy(&_pos_sp_triplet.previous, &_pos_sp_triplet.current, sizeof(position_setpoint_s));
-
- _reset_loiter_pos = true;
- _do_takeoff = false;
-
- int ret;
- bool onboard;
- unsigned index;
-
- ret = _mission.get_current_mission_item(&_mission_item, &onboard, &index);
-
- if (ret == OK) {
- _mission.report_current_offboard_mission_item();
-
- _mission_item_valid = true;
- position_setpoint_from_mission_item(&_pos_sp_triplet.current, &_mission_item);
-
- if (_mission_item.nav_cmd != NAV_CMD_RETURN_TO_LAUNCH &&
- _mission_item.nav_cmd != NAV_CMD_LOITER_TIME_LIMIT &&
- _mission_item.nav_cmd != NAV_CMD_LOITER_TURN_COUNT &&
- _mission_item.nav_cmd != NAV_CMD_LOITER_UNLIMITED) {
- /* don't reset RTL state on RTL or LOITER items */
- _rtl_state = RTL_STATE_NONE;
- }
-
- if (_vstatus.is_rotary_wing) {
- if (_need_takeoff && (
- _mission_item.nav_cmd == NAV_CMD_TAKEOFF ||
- _mission_item.nav_cmd == NAV_CMD_WAYPOINT ||
- _mission_item.nav_cmd == NAV_CMD_RETURN_TO_LAUNCH ||
- _mission_item.nav_cmd == NAV_CMD_LOITER_TIME_LIMIT ||
- _mission_item.nav_cmd == NAV_CMD_LOITER_TURN_COUNT ||
- _mission_item.nav_cmd == NAV_CMD_LOITER_UNLIMITED
- )) {
- /* do special TAKEOFF handling for VTOL */
- _need_takeoff = false;
-
- /* calculate desired takeoff altitude AMSL */
- float takeoff_alt_amsl = _pos_sp_triplet.current.alt;
-
- if (_vstatus.condition_landed) {
- /* takeoff to at least NAV_TAKEOFF_ALT from ground if landed */
- takeoff_alt_amsl = fmaxf(takeoff_alt_amsl, _global_pos.alt + _parameters.takeoff_alt);
- }
-
- /* check if we really need takeoff */
- if (_vstatus.condition_landed || _global_pos.alt < takeoff_alt_amsl - _mission_item.acceptance_radius) {
- /* force TAKEOFF if landed or waypoint altitude is more than current */
- _do_takeoff = true;
-
- /* move current position setpoint to next */
- memcpy(&_pos_sp_triplet.next, &_pos_sp_triplet.current, sizeof(position_setpoint_s));
-
- /* set current setpoint to takeoff */
-
- _pos_sp_triplet.current.lat = _global_pos.lat;
- _pos_sp_triplet.current.lon = _global_pos.lon;
- _pos_sp_triplet.current.alt = takeoff_alt_amsl;
- _pos_sp_triplet.current.yaw = NAN;
- _pos_sp_triplet.current.type = SETPOINT_TYPE_TAKEOFF;
- }
-
- } else if (_mission_item.nav_cmd == NAV_CMD_LAND) {
- /* will need takeoff after landing */
- _need_takeoff = true;
- }
- }
-
- if (_do_takeoff) {
- mavlink_log_info(_mavlink_fd, "#audio: takeoff to %.1fm above home", (double)(_pos_sp_triplet.current.alt - _home_pos.alt));
-
- } else {
- if (onboard) {
- mavlink_log_info(_mavlink_fd, "#audio: heading to onboard WP %d", index);
-
- } else {
- mavlink_log_info(_mavlink_fd, "#audio: heading to offboard WP %d", index);
- }
- }
-
- } else {
- /* since a mission is not advanced without WPs available, this is not supposed to happen */
- _mission_item_valid = false;
- _pos_sp_triplet.current.valid = false;
- warnx("ERROR: current WP can't be set");
- }
-
- if (!_do_takeoff) {
- mission_item_s item_next;
- ret = _mission.get_next_mission_item(&item_next);
-
- if (ret == OK) {
- position_setpoint_from_mission_item(&_pos_sp_triplet.next, &item_next);
-
- } else {
- /* this will fail for the last WP */
- _pos_sp_triplet.next.valid = false;
- }
- }
-
- _pos_sp_triplet_updated = true;
-}
-
-void
-Navigator::start_rtl()
-{
- _do_takeoff = false;
-
- /* decide if we need climb */
- if (_rtl_state == RTL_STATE_NONE) {
- if (_global_pos.alt < _home_pos.alt + _parameters.rtl_alt) {
- _rtl_state = RTL_STATE_CLIMB;
-
- } else {
- _rtl_state = RTL_STATE_RETURN;
- }
- }
-
- /* if switching directly to return state, reset altitude setpoint */
- if (_rtl_state == RTL_STATE_RETURN) {
- _mission_item.altitude_is_relative = false;
- _mission_item.altitude = _global_pos.alt;
- }
-
- _reset_loiter_pos = true;
- set_rtl_item();
-}
-
-void
-Navigator::start_land()
-{
- reset_reached();
-
- /* this state can be requested by commander even if no global position available,
- * in his case controller must perform landing without position control */
- _do_takeoff = false;
- _reset_loiter_pos = true;
-
- memcpy(&_pos_sp_triplet.previous, &_pos_sp_triplet.current, sizeof(position_setpoint_s));
-
- _mission_item_valid = true;
-
- _mission_item.lat = _global_pos.lat;
- _mission_item.lon = _global_pos.lon;
- _mission_item.altitude_is_relative = false;
- _mission_item.altitude = _global_pos.alt;
- _mission_item.yaw = NAN;
- _mission_item.loiter_radius = _parameters.loiter_radius;
- _mission_item.loiter_direction = 1;
- _mission_item.nav_cmd = NAV_CMD_LAND;
- _mission_item.acceptance_radius = _parameters.acceptance_radius;
- _mission_item.time_inside = 0.0f;
- _mission_item.pitch_min = 0.0f;
- _mission_item.autocontinue = true;
- _mission_item.origin = ORIGIN_ONBOARD;
-
- position_setpoint_from_mission_item(&_pos_sp_triplet.current, &_mission_item);
-
- _pos_sp_triplet.next.valid = false;
-}
-
-void
-Navigator::start_land_home()
-{
- reset_reached();
-
- /* land to home position, should be called when hovering above home, from RTL state */
- _do_takeoff = false;
- _reset_loiter_pos = true;
-
- memcpy(&_pos_sp_triplet.previous, &_pos_sp_triplet.current, sizeof(position_setpoint_s));
-
- _mission_item_valid = true;
-
- _mission_item.lat = _home_pos.lat;
- _mission_item.lon = _home_pos.lon;
- _mission_item.altitude_is_relative = false;
- _mission_item.altitude = _home_pos.alt;
- _mission_item.yaw = NAN;
- _mission_item.loiter_radius = _parameters.loiter_radius;
- _mission_item.loiter_direction = 1;
- _mission_item.nav_cmd = NAV_CMD_LAND;
- _mission_item.acceptance_radius = _parameters.acceptance_radius;
- _mission_item.time_inside = 0.0f;
- _mission_item.pitch_min = 0.0f;
- _mission_item.autocontinue = true;
- _mission_item.origin = ORIGIN_ONBOARD;
-
- position_setpoint_from_mission_item(&_pos_sp_triplet.current, &_mission_item);
-
- _pos_sp_triplet.next.valid = false;
-}
-
-void
-Navigator::set_rtl_item()
-{
- reset_reached();
-
- switch (_rtl_state) {
- case RTL_STATE_CLIMB: {
- memcpy(&_pos_sp_triplet.previous, &_pos_sp_triplet.current, sizeof(position_setpoint_s));
-
- float climb_alt = _home_pos.alt + _parameters.rtl_alt;
-
- if (_vstatus.condition_landed) {
- climb_alt = fmaxf(climb_alt, _global_pos.alt + _parameters.rtl_alt);
- }
-
- _mission_item_valid = true;
-
- _mission_item.lat = _global_pos.lat;
- _mission_item.lon = _global_pos.lon;
- _mission_item.altitude_is_relative = false;
- _mission_item.altitude = climb_alt;
- _mission_item.yaw = NAN;
- _mission_item.loiter_radius = _parameters.loiter_radius;
- _mission_item.loiter_direction = 1;
- _mission_item.nav_cmd = NAV_CMD_WAYPOINT;
- _mission_item.acceptance_radius = _parameters.acceptance_radius;
- _mission_item.time_inside = 0.0f;
- _mission_item.pitch_min = 0.0f;
- _mission_item.autocontinue = true;
- _mission_item.origin = ORIGIN_ONBOARD;
-
- position_setpoint_from_mission_item(&_pos_sp_triplet.current, &_mission_item);
-
- _pos_sp_triplet.next.valid = false;
-
- mavlink_log_info(_mavlink_fd, "#audio: RTL: climb to %.1fm above home", (double)(climb_alt - _home_pos.alt));
- break;
- }
-
- case RTL_STATE_RETURN: {
- memcpy(&_pos_sp_triplet.previous, &_pos_sp_triplet.current, sizeof(position_setpoint_s));
-
- _mission_item_valid = true;
-
- _mission_item.lat = _home_pos.lat;
- _mission_item.lon = _home_pos.lon;
- // don't change altitude
- if (_pos_sp_triplet.previous.valid) {
- /* if previous setpoint is valid then use it to calculate heading to home */
- _mission_item.yaw = get_bearing_to_next_waypoint(_pos_sp_triplet.previous.lat, _pos_sp_triplet.previous.lon, _mission_item.lat, _mission_item.lon);
-
- } else {
- /* else use current position */
- _mission_item.yaw = get_bearing_to_next_waypoint(_global_pos.lat, _global_pos.lon, _mission_item.lat, _mission_item.lon);
- }
- _mission_item.loiter_radius = _parameters.loiter_radius;
- _mission_item.loiter_direction = 1;
- _mission_item.nav_cmd = NAV_CMD_WAYPOINT;
- _mission_item.acceptance_radius = _parameters.acceptance_radius;
- _mission_item.time_inside = 0.0f;
- _mission_item.pitch_min = 0.0f;
- _mission_item.autocontinue = true;
- _mission_item.origin = ORIGIN_ONBOARD;
-
- position_setpoint_from_mission_item(&_pos_sp_triplet.current, &_mission_item);
-
- _pos_sp_triplet.next.valid = false;
-
- mavlink_log_info(_mavlink_fd, "#audio: RTL: return at %.1fm above home", (double)(_mission_item.altitude - _home_pos.alt));
- break;
- }
-
- case RTL_STATE_DESCEND: {
- memcpy(&_pos_sp_triplet.previous, &_pos_sp_triplet.current, sizeof(position_setpoint_s));
-
- _mission_item_valid = true;
-
- _mission_item.lat = _home_pos.lat;
- _mission_item.lon = _home_pos.lon;
- _mission_item.altitude_is_relative = false;
- _mission_item.altitude = _home_pos.alt + _parameters.land_alt;
- _mission_item.yaw = NAN;
- _mission_item.loiter_radius = _parameters.loiter_radius;
- _mission_item.loiter_direction = 1;
- _mission_item.nav_cmd = NAV_CMD_LOITER_TIME_LIMIT;
- _mission_item.acceptance_radius = _parameters.acceptance_radius;
- _mission_item.time_inside = _parameters.rtl_land_delay < 0.0f ? 0.0f : _parameters.rtl_land_delay;
- _mission_item.pitch_min = 0.0f;
- _mission_item.autocontinue = _parameters.rtl_land_delay > -0.001f;
- _mission_item.origin = ORIGIN_ONBOARD;
-
- position_setpoint_from_mission_item(&_pos_sp_triplet.current, &_mission_item);
-
- _pos_sp_triplet.next.valid = false;
-
- mavlink_log_info(_mavlink_fd, "#audio: RTL: descend to %.1fm above home", (double)(_mission_item.altitude - _home_pos.alt));
- break;
- }
-
- default: {
- mavlink_log_critical(_mavlink_fd, "#audio: [navigator] error: unknown RTL state: %d", _rtl_state);
- start_loiter();
- break;
- }
- }
-
- _pos_sp_triplet_updated = true;
-}
-
-void
-Navigator::request_loiter_or_ready()
-{
- /* XXX workaround: no landing detector for fixedwing yet */
- if (_vstatus.condition_landed && _vstatus.is_rotary_wing) {
- dispatch(EVENT_READY_REQUESTED);
-
- } else {
- dispatch(EVENT_LOITER_REQUESTED);
- }
-}
-
-void
-Navigator::request_mission_if_available()
-{
- if (_mission.current_mission_available()) {
- dispatch(EVENT_MISSION_REQUESTED);
-
- } else {
- request_loiter_or_ready();
- }
-}
-
-void
-Navigator::position_setpoint_from_mission_item(position_setpoint_s *sp, mission_item_s *item)
-{
- sp->valid = true;
-
- if (item->nav_cmd == NAV_CMD_RETURN_TO_LAUNCH) {
- /* set home position for RTL item */
- sp->lat = _home_pos.lat;
- sp->lon = _home_pos.lon;
- sp->alt = _home_pos.alt + _parameters.rtl_alt;
-
- if (_pos_sp_triplet.previous.valid) {
- /* if previous setpoint is valid then use it to calculate heading to home */
- sp->yaw = get_bearing_to_next_waypoint(_pos_sp_triplet.previous.lat, _pos_sp_triplet.previous.lon, sp->lat, sp->lon);
-
- } else {
- /* else use current position */
- sp->yaw = get_bearing_to_next_waypoint(_global_pos.lat, _global_pos.lon, sp->lat, sp->lon);
- }
- sp->loiter_radius = _parameters.loiter_radius;
- sp->loiter_direction = 1;
- sp->pitch_min = 0.0f;
-
- } else {
- sp->lat = item->lat;
- sp->lon = item->lon;
- sp->alt = item->altitude_is_relative ? item->altitude + _home_pos.alt : item->altitude;
- sp->yaw = item->yaw;
- sp->loiter_radius = item->loiter_radius;
- sp->loiter_direction = item->loiter_direction;
- sp->pitch_min = item->pitch_min;
- }
-
- if (item->nav_cmd == NAV_CMD_TAKEOFF) {
- sp->type = SETPOINT_TYPE_TAKEOFF;
-
- } else if (item->nav_cmd == NAV_CMD_LAND) {
- sp->type = SETPOINT_TYPE_LAND;
-
- } else if (item->nav_cmd == NAV_CMD_LOITER_TIME_LIMIT ||
- item->nav_cmd == NAV_CMD_LOITER_TURN_COUNT ||
- item->nav_cmd == NAV_CMD_LOITER_UNLIMITED) {
- sp->type = SETPOINT_TYPE_LOITER;
-
- } else {
- sp->type = SETPOINT_TYPE_NORMAL;
- }
-}
-
-bool
-Navigator::check_mission_item_reached()
-{
- /* only check if there is actually a mission item to check */
- if (!_mission_item_valid) {
- return false;
- }
-
- if (_mission_item.nav_cmd == NAV_CMD_LAND) {
- return _vstatus.condition_landed;
- }
-
- /* XXX TODO count turns */
- if ((_mission_item.nav_cmd == NAV_CMD_LOITER_TURN_COUNT ||
- _mission_item.nav_cmd == NAV_CMD_LOITER_UNLIMITED) &&
- _mission_item.loiter_radius > 0.01f) {
-
- return false;
- }
-
- uint64_t now = hrt_absolute_time();
-
- if (!_waypoint_position_reached) {
- float acceptance_radius;
-
- if (_mission_item.nav_cmd == NAV_CMD_WAYPOINT && _mission_item.acceptance_radius > 0.01f) {
- acceptance_radius = _mission_item.acceptance_radius;
-
- } else {
- acceptance_radius = _parameters.acceptance_radius;
- }
-
- if (_do_takeoff) {
- /* require only altitude for takeoff */
- if (_global_pos.alt > _pos_sp_triplet.current.alt - acceptance_radius) {
- _waypoint_position_reached = true;
- }
-
- } else {
- float dist = -1.0f;
- float dist_xy = -1.0f;
- float dist_z = -1.0f;
-
- /* calculate AMSL altitude for this waypoint */
- float wp_alt_amsl = _mission_item.altitude;
-
- if (_mission_item.altitude_is_relative)
- wp_alt_amsl += _home_pos.alt;
-
- dist = get_distance_to_point_global_wgs84(_mission_item.lat, _mission_item.lon, wp_alt_amsl,
- (double)_global_pos.lat, (double)_global_pos.lon, _global_pos.alt,
- &dist_xy, &dist_z);
-
- if (dist >= 0.0f && dist <= acceptance_radius) {
- _waypoint_position_reached = true;
- }
- }
- }
-
- if (_waypoint_position_reached && !_waypoint_yaw_reached) {
- if (_vstatus.is_rotary_wing && !_do_takeoff && isfinite(_mission_item.yaw)) {
- /* check yaw if defined only for rotary wing except takeoff */
- float yaw_err = _wrap_pi(_mission_item.yaw - _global_pos.yaw);
-
- if (fabsf(yaw_err) < 0.2f) { /* XXX get rid of magic number */
- _waypoint_yaw_reached = true;
- }
-
- } else {
- _waypoint_yaw_reached = true;
- }
- }
-
- /* check if the current waypoint was reached */
- if (_waypoint_position_reached && _waypoint_yaw_reached) {
- if (_time_first_inside_orbit == 0) {
- _time_first_inside_orbit = now;
-
- if (_mission_item.time_inside > 0.01f) {
- mavlink_log_info(_mavlink_fd, "#audio: waypoint reached, wait for %.1fs", (double)_mission_item.time_inside);
- }
- }
-
- /* check if the MAV was long enough inside the waypoint orbit */
- if ((now - _time_first_inside_orbit >= (uint64_t)_mission_item.time_inside * 1e6)
- || _mission_item.nav_cmd == NAV_CMD_TAKEOFF) {
- reset_reached();
- return true;
- }
- }
-
- return false;
-
-}
-
-void
-Navigator::reset_reached()
-{
- _time_first_inside_orbit = 0;
- _waypoint_position_reached = false;
- _waypoint_yaw_reached = false;
-
-}
-
-void
-Navigator::on_mission_item_reached()
-{
- if (myState == NAV_STATE_MISSION) {
-
- _mission.report_mission_item_reached();
-
- if (_do_takeoff) {
- /* takeoff completed */
- _do_takeoff = false;
- mavlink_log_info(_mavlink_fd, "#audio: takeoff completed");
-
- } else {
- /* advance by one mission item */
- _mission.move_to_next();
- }
-
- if (_mission.current_mission_available()) {
- if (_mission_item.autocontinue) {
- /* continue mission */
- set_mission_item();
-
- } else {
- /* autocontinue disabled for this item */
- request_loiter_or_ready();
- }
-
- } else {
- /* if no more mission items available then finish mission */
- /* loiter at last waypoint */
- _reset_loiter_pos = false;
- mavlink_log_info(_mavlink_fd, "[navigator] mission completed");
- request_loiter_or_ready();
- }
-
- } else if (myState == NAV_STATE_RTL) {
- /* RTL completed */
- if (_rtl_state == RTL_STATE_DESCEND) {
- /* hovering above home position, land if needed or loiter */
- mavlink_log_info(_mavlink_fd, "[navigator] RTL completed");
-
- if (_mission_item.autocontinue) {
- dispatch(EVENT_LAND_REQUESTED);
-
- } else {
- _reset_loiter_pos = false;
- dispatch(EVENT_LOITER_REQUESTED);
- }
-
- } else {
- /* next RTL step */
- _rtl_state = (RTLState)(_rtl_state + 1);
- set_rtl_item();
- }
-
- } else if (myState == NAV_STATE_LAND) {
- /* landing completed */
- mavlink_log_info(_mavlink_fd, "[navigator] landing completed");
- dispatch(EVENT_READY_REQUESTED);
- }
- _mission.publish_mission_result();
-}
-
void
Navigator::publish_position_setpoint_triplet()
{
/* update navigation state */
- _pos_sp_triplet.nav_state = static_cast<nav_state_t>(myState);
+ _pos_sp_triplet.nav_state = _vstatus.nav_state;
/* lazily publish the position setpoint triplet only once available */
if (_pos_sp_triplet_pub > 0) {
- /* publish the position setpoint triplet */
orb_publish(ORB_ID(position_setpoint_triplet), _pos_sp_triplet_pub, &_pos_sp_triplet);
} else {
- /* advertise and publish */
_pos_sp_triplet_pub = orb_advertise(ORB_ID(position_setpoint_triplet), &_pos_sp_triplet);
}
}
@@ -1693,3 +626,31 @@ int navigator_main(int argc, char *argv[])
return 0;
}
+
+void
+Navigator::publish_mission_result()
+{
+ /* lazily publish the mission result only once available */
+ if (_mission_result_pub > 0) {
+ /* publish mission result */
+ orb_publish(ORB_ID(mission_result), _mission_result_pub, &_mission_result);
+
+ } else {
+ /* advertise and publish */
+ _mission_result_pub = orb_advertise(ORB_ID(mission_result), &_mission_result);
+ }
+}
+
+void
+Navigator::publish_att_sp()
+{
+ /* lazily publish the attitude sp only once available */
+ if (_att_sp_pub > 0) {
+ /* publish att sp*/
+ orb_publish(ORB_ID(vehicle_attitude_setpoint), _att_sp_pub, &_att_sp);
+
+ } else {
+ /* advertise and publish */
+ _att_sp_pub = orb_advertise(ORB_ID(vehicle_attitude_setpoint), &_att_sp);
+ }
+}
diff --git a/src/modules/navigator/navigator_mission.cpp b/src/modules/navigator/navigator_mission.cpp
deleted file mode 100644
index 49fc62785..000000000
--- a/src/modules/navigator/navigator_mission.cpp
+++ /dev/null
@@ -1,319 +0,0 @@
-/****************************************************************************
- *
- * Copyright (c) 2013 PX4 Development Team. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * 3. Neither the name PX4 nor the names of its contributors may be
- * used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
- * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- *
- ****************************************************************************/
-/**
- * @file navigator_mission.cpp
- * Helper class to access missions
- *
- * @author Julian Oes <joes@student.ethz.ch>
- */
-
-#include <string.h>
-#include <stdlib.h>
-#include <dataman/dataman.h>
-#include <systemlib/err.h>
-#include <uORB/uORB.h>
-#include <uORB/topics/mission_result.h>
-#include "navigator_mission.h"
-
-/* oddly, ERROR is not defined for c++ */
-#ifdef ERROR
-# undef ERROR
-#endif
-static const int ERROR = -1;
-
-
-Mission::Mission() :
-
- _offboard_dataman_id(-1),
- _current_offboard_mission_index(0),
- _current_onboard_mission_index(0),
- _offboard_mission_item_count(0),
- _onboard_mission_item_count(0),
- _onboard_mission_allowed(false),
- _current_mission_type(MISSION_TYPE_NONE),
- _mission_result_pub(-1)
-{
- memset(&_mission_result, 0, sizeof(struct mission_result_s));
-}
-
-Mission::~Mission()
-{
-
-}
-
-void
-Mission::set_offboard_dataman_id(int new_id)
-{
- _offboard_dataman_id = new_id;
-}
-
-void
-Mission::set_current_offboard_mission_index(int new_index)
-{
- if (new_index != -1) {
- warnx("specifically set to %d", new_index);
- _current_offboard_mission_index = (unsigned)new_index;
- } else {
-
- /* if less WPs available, reset to first WP */
- if (_current_offboard_mission_index >= _offboard_mission_item_count) {
- _current_offboard_mission_index = 0;
- }
- }
- report_current_offboard_mission_item();
-}
-
-void
-Mission::set_current_onboard_mission_index(int new_index)
-{
- if (new_index != -1) {
- _current_onboard_mission_index = (unsigned)new_index;
- } else {
-
- /* if less WPs available, reset to first WP */
- if (_current_onboard_mission_index >= _onboard_mission_item_count) {
- _current_onboard_mission_index = 0;
- }
- }
- // TODO: implement this for onboard missions as well
- // report_current_mission_item();
-}
-
-void
-Mission::set_offboard_mission_count(unsigned new_count)
-{
- _offboard_mission_item_count = new_count;
-}
-
-void
-Mission::set_onboard_mission_count(unsigned new_count)
-{
- _onboard_mission_item_count = new_count;
-}
-
-void
-Mission::set_onboard_mission_allowed(bool allowed)
-{
- _onboard_mission_allowed = allowed;
-}
-
-bool
-Mission::current_mission_available()
-{
- return (current_onboard_mission_available() || current_offboard_mission_available());
-
-}
-
-bool
-Mission::next_mission_available()
-{
- return (next_onboard_mission_available() || next_offboard_mission_available());
-}
-
-int
-Mission::get_current_mission_item(struct mission_item_s *new_mission_item, bool *onboard, unsigned *index)
-{
- /* try onboard mission first */
- if (current_onboard_mission_available()) {
-
- const ssize_t len = sizeof(struct mission_item_s);
-
- if (dm_read(DM_KEY_WAYPOINTS_ONBOARD, _current_onboard_mission_index, new_mission_item, len) != len) {
- /* not supposed to happen unless the datamanager can't access the SD card, etc. */
- return ERROR;
- }
-
- _current_mission_type = MISSION_TYPE_ONBOARD;
- *onboard = true;
- *index = _current_onboard_mission_index;
-
- /* otherwise fallback to offboard */
-
- } else if (current_offboard_mission_available()) {
-
- dm_item_t dm_current;
-
- if (_offboard_dataman_id == 0) {
- dm_current = DM_KEY_WAYPOINTS_OFFBOARD_0;
-
- } else {
- dm_current = DM_KEY_WAYPOINTS_OFFBOARD_1;
- }
-
- const ssize_t len = sizeof(struct mission_item_s);
-
- if (dm_read(dm_current, _current_offboard_mission_index, new_mission_item, len) != len) {
- /* not supposed to happen unless the datamanager can't access the SD card, etc. */
- _current_mission_type = MISSION_TYPE_NONE;
- return ERROR;
- }
-
- _current_mission_type = MISSION_TYPE_OFFBOARD;
- *onboard = false;
- *index = _current_offboard_mission_index;
-
- } else {
- /* happens when no more mission items can be added as a next item */
- _current_mission_type = MISSION_TYPE_NONE;
- return ERROR;
- }
-
- return OK;
-}
-
-int
-Mission::get_next_mission_item(struct mission_item_s *new_mission_item)
-{
- /* try onboard mission first */
- if (next_onboard_mission_available()) {
-
- const ssize_t len = sizeof(struct mission_item_s);
-
- if (dm_read(DM_KEY_WAYPOINTS_ONBOARD, _current_onboard_mission_index + 1, new_mission_item, len) != len) {
- /* not supposed to happen unless the datamanager can't access the SD card, etc. */
- return ERROR;
- }
-
- /* otherwise fallback to offboard */
-
- } else if (next_offboard_mission_available()) {
-
- dm_item_t dm_current;
-
- if (_offboard_dataman_id == 0) {
- dm_current = DM_KEY_WAYPOINTS_OFFBOARD_0;
-
- } else {
- dm_current = DM_KEY_WAYPOINTS_OFFBOARD_1;
- }
-
- const ssize_t len = sizeof(struct mission_item_s);
-
- if (dm_read(dm_current, _current_offboard_mission_index + 1, new_mission_item, len) != len) {
- /* not supposed to happen unless the datamanager can't access the SD card, etc. */
- return ERROR;
- }
-
- } else {
- /* happens when no more mission items can be added as a next item */
- return ERROR;
- }
-
- return OK;
-}
-
-
-bool
-Mission::current_onboard_mission_available()
-{
- return _onboard_mission_item_count > _current_onboard_mission_index && _onboard_mission_allowed;
-}
-
-bool
-Mission::current_offboard_mission_available()
-{
- return _offboard_mission_item_count > _current_offboard_mission_index;
-}
-
-bool
-Mission::next_onboard_mission_available()
-{
- unsigned next = 0;
-
- if (_current_mission_type != MISSION_TYPE_ONBOARD) {
- next = 1;
- }
-
- return _onboard_mission_item_count > (_current_onboard_mission_index + next) && _onboard_mission_allowed;
-}
-
-bool
-Mission::next_offboard_mission_available()
-{
- unsigned next = 0;
-
- if (_current_mission_type != MISSION_TYPE_OFFBOARD) {
- next = 1;
- }
-
- return _offboard_mission_item_count > (_current_offboard_mission_index + next);
-}
-
-void
-Mission::move_to_next()
-{
- switch (_current_mission_type) {
- case MISSION_TYPE_ONBOARD:
- _current_onboard_mission_index++;
- break;
-
- case MISSION_TYPE_OFFBOARD:
- _current_offboard_mission_index++;
- break;
-
- case MISSION_TYPE_NONE:
- default:
- break;
- }
-}
-
-void
-Mission::report_mission_item_reached()
-{
- if (_current_mission_type == MISSION_TYPE_OFFBOARD) {
- _mission_result.mission_reached = true;
- _mission_result.mission_index_reached = _current_offboard_mission_index;
- }
-}
-
-void
-Mission::report_current_offboard_mission_item()
-{
- _mission_result.index_current_mission = _current_offboard_mission_index;
-}
-
-void
-Mission::publish_mission_result()
-{
- /* lazily publish the mission result only once available */
- if (_mission_result_pub > 0) {
- /* publish mission result */
- orb_publish(ORB_ID(mission_result), _mission_result_pub, &_mission_result);
-
- } else {
- /* advertise and publish */
- _mission_result_pub = orb_advertise(ORB_ID(mission_result), &_mission_result);
- }
- /* reset reached bool */
- _mission_result.mission_reached = false;
-}
diff --git a/src/modules/navigator/navigator_mission.h b/src/modules/navigator/navigator_mission.h
deleted file mode 100644
index b0f88e016..000000000
--- a/src/modules/navigator/navigator_mission.h
+++ /dev/null
@@ -1,105 +0,0 @@
-/****************************************************************************
- *
- * Copyright (c) 2013 PX4 Development Team. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * 3. Neither the name PX4 nor the names of its contributors may be
- * used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
- * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- *
- ****************************************************************************/
-/**
- * @file navigator_mission.h
- * Helper class to access missions
- *
- * @author Julian Oes <joes@student.ethz.ch>
- */
-
-#ifndef NAVIGATOR_MISSION_H
-#define NAVIGATOR_MISSION_H
-
-#include <uORB/topics/mission.h>
-#include <uORB/topics/mission_result.h>
-
-
-class __EXPORT Mission
-{
-public:
- /**
- * Constructor
- */
- Mission();
-
- /**
- * Destructor, also kills the sensors task.
- */
- ~Mission();
-
- void set_offboard_dataman_id(int new_id);
- void set_current_offboard_mission_index(int new_index);
- void set_current_onboard_mission_index(int new_index);
- void set_offboard_mission_count(unsigned new_count);
- void set_onboard_mission_count(unsigned new_count);
-
- void set_onboard_mission_allowed(bool allowed);
-
- bool current_mission_available();
- bool next_mission_available();
-
- int get_current_mission_item(struct mission_item_s *mission_item, bool *onboard, unsigned *index);
- int get_next_mission_item(struct mission_item_s *mission_item);
-
- void move_to_next();
-
- void report_mission_item_reached();
- void report_current_offboard_mission_item();
- void publish_mission_result();
-
-private:
- bool current_onboard_mission_available();
- bool current_offboard_mission_available();
- bool next_onboard_mission_available();
- bool next_offboard_mission_available();
-
- int _offboard_dataman_id;
- unsigned _current_offboard_mission_index;
- unsigned _current_onboard_mission_index;
- unsigned _offboard_mission_item_count; /** number of offboard mission items available */
- unsigned _onboard_mission_item_count; /** number of onboard mission items available */
-
- bool _onboard_mission_allowed;
-
- enum {
- MISSION_TYPE_NONE,
- MISSION_TYPE_ONBOARD,
- MISSION_TYPE_OFFBOARD,
- } _current_mission_type;
-
- int _mission_result_pub;
-
- struct mission_result_s _mission_result;
-};
-
-#endif
diff --git a/src/modules/navigator/navigator_mode.cpp b/src/modules/navigator/navigator_mode.cpp
new file mode 100644
index 000000000..3807c5ea8
--- /dev/null
+++ b/src/modules/navigator/navigator_mode.cpp
@@ -0,0 +1,98 @@
+/****************************************************************************
+ *
+ * Copyright (c) 2014 PX4 Development Team. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+/**
+ * @file navigator_mode.cpp
+ *
+ * Base class for different modes in navigator
+ *
+ * @author Julian Oes <julian@oes.ch>
+ * @author Anton Babushkin <anton.babushkin@me.com>
+ */
+
+#include "navigator_mode.h"
+#include "navigator.h"
+
+NavigatorMode::NavigatorMode(Navigator *navigator, const char *name) :
+ SuperBlock(navigator, name),
+ _navigator(navigator),
+ _first_run(true)
+{
+ /* load initial params */
+ updateParams();
+ /* set initial mission items */
+ on_inactive();
+}
+
+NavigatorMode::~NavigatorMode()
+{
+}
+
+void
+NavigatorMode::run(bool active) {
+ if (active) {
+ if (_first_run) {
+ /* first run */
+ _first_run = false;
+ /* Reset stay in failsafe flag */
+ _navigator->get_mission_result()->stay_in_failsafe = false;
+ _navigator->publish_mission_result();
+ on_activation();
+
+ } else {
+ /* periodic updates when active */
+ on_active();
+ }
+
+ } else {
+ /* periodic updates when inactive */
+ _first_run = true;
+ on_inactive();
+ }
+}
+
+void
+NavigatorMode::on_inactive()
+{
+}
+
+void
+NavigatorMode::on_activation()
+{
+ /* invalidate position setpoint by default */
+ _navigator->get_position_setpoint_triplet()->current.valid = false;
+}
+
+void
+NavigatorMode::on_active()
+{
+}
diff --git a/src/modules/navigator/navigator_mode.h b/src/modules/navigator/navigator_mode.h
new file mode 100644
index 000000000..de5545dcb
--- /dev/null
+++ b/src/modules/navigator/navigator_mode.h
@@ -0,0 +1,99 @@
+/****************************************************************************
+ *
+ * Copyright (c) 2014 PX4 Development Team. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+/**
+ * @file navigator_mode.h
+ *
+ * Base class for different modes in navigator
+ *
+ * @author Julian Oes <julian@oes.ch>
+ * @author Anton Babushkin <anton.babushkin@me.com>
+ */
+
+#ifndef NAVIGATOR_MODE_H
+#define NAVIGATOR_MODE_H
+
+#include <drivers/drv_hrt.h>
+
+#include <controllib/blocks.hpp>
+#include <controllib/block/BlockParam.hpp>
+
+#include <dataman/dataman.h>
+
+#include <uORB/topics/position_setpoint_triplet.h>
+
+class Navigator;
+
+class NavigatorMode : public control::SuperBlock
+{
+public:
+ /**
+ * Constructor
+ */
+ NavigatorMode(Navigator *navigator, const char *name);
+
+ /**
+ * Destructor
+ */
+ virtual ~NavigatorMode();
+
+ void run(bool active);
+
+ /**
+ * This function is called while the mode is inactive
+ */
+ virtual void on_inactive();
+
+ /**
+ * This function is called one time when mode become active, poos_sp_triplet must be initialized here
+ */
+ virtual void on_activation();
+
+ /**
+ * This function is called while the mode is active
+ */
+ virtual void on_active();
+
+protected:
+ Navigator *_navigator;
+
+private:
+ bool _first_run;
+
+ /* this class has ptr data members, so it should not be copied,
+ * consequently the copy constructors are private.
+ */
+ NavigatorMode(const NavigatorMode&);
+ NavigatorMode operator=(const NavigatorMode&);
+};
+
+#endif
diff --git a/src/modules/navigator/navigator_params.c b/src/modules/navigator/navigator_params.c
index 5139283b6..1f40e634e 100644
--- a/src/modules/navigator/navigator_params.c
+++ b/src/modules/navigator/navigator_params.c
@@ -1,6 +1,6 @@
/****************************************************************************
*
- * Copyright (c) 2013 PX4 Development Team. All rights reserved.
+ * Copyright (c) 2014 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -17,7 +17,7 @@
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * AS IS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
@@ -34,104 +34,87 @@
/**
* @file navigator_params.c
*
- * Parameters defined by the navigator task.
+ * Parameters for navigator in general
*
- * @author Lorenz Meier <lm@inf.ethz.ch>
- * @author Julian Oes <joes@student.ethz.ch>
- * @author Anton Babushkin <anton.babushkin@me.com>
+ * @author Julian Oes <julian@oes.ch>
+ * @author Thomas Gubler <thomasgubler@gmail.com>
*/
#include <nuttx/config.h>
#include <systemlib/param/param.h>
-/*
- * Navigator parameters, accessible via MAVLink
- */
-
/**
- * Minimum altitude (fixed wing only)
+ * Loiter radius (FW only)
*
- * Minimum altitude above home for LOITER.
- *
- * @unit meters
- * @group Navigation
- */
-PARAM_DEFINE_FLOAT(NAV_MIN_ALT, 50.0f);
-
-/**
- * Waypoint acceptance radius
- *
- * Default value of acceptance radius (if not specified in mission item).
+ * Default value of loiter radius for missions, loiter, RTL, etc. (fixedwing only).
*
* @unit meters
* @min 0.0
- * @group Navigation
+ * @group Mission
*/
-PARAM_DEFINE_FLOAT(NAV_ACCEPT_RAD, 10.0f);
+PARAM_DEFINE_FLOAT(NAV_LOITER_RAD, 50.0f);
/**
- * Loiter radius (fixed wing only)
+ * Acceptance Radius
*
- * Default value of loiter radius (if not specified in mission item).
+ * Default acceptance radius, overridden by acceptance radius of waypoint if set.
*
* @unit meters
- * @min 0.0
- * @group Navigation
- */
-PARAM_DEFINE_FLOAT(NAV_LOITER_RAD, 50.0f);
-
-/**
- * Enable onboard mission
- *
- * @group Navigation
+ * @min 1.0
+ * @group Mission
*/
-PARAM_DEFINE_INT32(NAV_ONB_MIS_EN, 0);
+PARAM_DEFINE_FLOAT(NAV_ACC_RAD, 25.0f);
/**
- * Take-off altitude
+ * Set OBC mode for data link loss
*
- * Even if first waypoint has altitude less then NAV_TAKEOFF_ALT above home position, system will climb to NAV_TAKEOFF_ALT on takeoff, then go to waypoint.
+ * If set to 1 the behaviour on data link loss is set to a mode according to the OBC rules
*
- * @unit meters
- * @group Navigation
+ * @min 0
+ * @group Mission
*/
-PARAM_DEFINE_FLOAT(NAV_TAKEOFF_ALT, 10.0f);
+PARAM_DEFINE_INT32(NAV_DLL_OBC, 0);
/**
- * Landing altitude
+ * Set OBC mode for rc loss
*
- * Stay at this altitude above home position after RTL descending. Land (i.e. slowly descend) from this altitude if autolanding allowed.
+ * If set to 1 the behaviour on data link loss is set to a mode according to the OBC rules
*
- * @unit meters
- * @group Navigation
+ * @min 0
+ * @group Mission
*/
-PARAM_DEFINE_FLOAT(NAV_LAND_ALT, 5.0f);
+PARAM_DEFINE_INT32(NAV_RCL_OBC, 0);
/**
- * Return-To-Launch altitude
+ * Airfield home Lat
*
- * Minimum altitude above home position for going home in RTL mode.
+ * Latitude of airfield home waypoint
*
- * @unit meters
- * @group Navigation
+ * @unit degrees * 1e7
+ * @min 0.0
+ * @group DLL
*/
-PARAM_DEFINE_FLOAT(NAV_RTL_ALT, 30.0f);
+PARAM_DEFINE_INT32(NAV_AH_LAT, -265847810);
/**
- * Return-To-Launch delay
+ * Airfield home Lon
*
- * Delay after descend before landing in RTL mode.
- * If set to -1 the system will not land but loiter at NAV_LAND_ALT.
+ * Longitude of airfield home waypoint
*
- * @unit seconds
- * @group Navigation
+ * @unit degrees * 1e7
+ * @min 0.0
+ * @group DLL
*/
-PARAM_DEFINE_FLOAT(NAV_RTL_LAND_T, -1.0f);
+PARAM_DEFINE_INT32(NAV_AH_LON, 1518423250);
/**
- * Enable parachute deployment
+ * Airfield home alt
*
- * @group Navigation
+ * Altitude of airfield home waypoint
+ *
+ * @unit m
+ * @min 0.0
+ * @group DLL
*/
-PARAM_DEFINE_INT32(NAV_PARACHUTE_EN, 0);
+PARAM_DEFINE_FLOAT(NAV_AH_ALT, 600.0f);
diff --git a/src/modules/navigator/rcloss.cpp b/src/modules/navigator/rcloss.cpp
new file mode 100644
index 000000000..42392e739
--- /dev/null
+++ b/src/modules/navigator/rcloss.cpp
@@ -0,0 +1,184 @@
+/****************************************************************************
+ *
+ * Copyright (c) 2013-2014 PX4 Development Team. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+/**
+ * @file rcloss.cpp
+ * Helper class for RC Loss Mode according to the OBC rules
+ *
+ * @author Thomas Gubler <thomasgubler@gmail.com>
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+#include <fcntl.h>
+
+#include <mavlink/mavlink_log.h>
+#include <systemlib/err.h>
+#include <geo/geo.h>
+
+#include <uORB/uORB.h>
+#include <uORB/topics/mission.h>
+#include <uORB/topics/home_position.h>
+
+#include "navigator.h"
+#include "datalinkloss.h"
+
+#define DELAY_SIGMA 0.01f
+
+RCLoss::RCLoss(Navigator *navigator, const char *name) :
+ MissionBlock(navigator, name),
+ _param_loitertime(this, "LT"),
+ _rcl_state(RCL_STATE_NONE)
+{
+ /* load initial params */
+ updateParams();
+ /* initial reset */
+ on_inactive();
+}
+
+RCLoss::~RCLoss()
+{
+}
+
+void
+RCLoss::on_inactive()
+{
+ /* reset RCL state only if setpoint moved */
+ if (!_navigator->get_can_loiter_at_sp()) {
+ _rcl_state = RCL_STATE_NONE;
+ }
+}
+
+void
+RCLoss::on_activation()
+{
+ _rcl_state = RCL_STATE_NONE;
+ updateParams();
+ advance_rcl();
+ set_rcl_item();
+}
+
+void
+RCLoss::on_active()
+{
+ if (is_mission_item_reached()) {
+ updateParams();
+ advance_rcl();
+ set_rcl_item();
+ }
+}
+
+void
+RCLoss::set_rcl_item()
+{
+ struct position_setpoint_triplet_s *pos_sp_triplet = _navigator->get_position_setpoint_triplet();
+
+ set_previous_pos_setpoint();
+ _navigator->set_can_loiter_at_sp(false);
+
+ switch (_rcl_state) {
+ case RCL_STATE_LOITER: {
+ _mission_item.lat = _navigator->get_global_position()->lat;
+ _mission_item.lon = _navigator->get_global_position()->lon;
+ _mission_item.altitude = _navigator->get_global_position()->alt;
+ _mission_item.altitude_is_relative = false;
+ _mission_item.yaw = NAN;
+ _mission_item.loiter_radius = _navigator->get_loiter_radius();
+ _mission_item.loiter_direction = 1;
+ _mission_item.nav_cmd = NAV_CMD_LOITER_TIME_LIMIT;
+ _mission_item.acceptance_radius = _navigator->get_acceptance_radius();
+ _mission_item.time_inside = _param_loitertime.get() < 0.0f ? 0.0f : _param_loitertime.get();
+ _mission_item.pitch_min = 0.0f;
+ _mission_item.autocontinue = true;
+ _mission_item.origin = ORIGIN_ONBOARD;
+
+ _navigator->set_can_loiter_at_sp(true);
+ break;
+ }
+ case RCL_STATE_TERMINATE: {
+ /* Request flight termination from the commander */
+ _navigator->get_mission_result()->flight_termination = true;
+ _navigator->publish_mission_result();
+ warnx("rc not recovered: request flight termination");
+ pos_sp_triplet->previous.valid = false;
+ pos_sp_triplet->current.valid = false;
+ pos_sp_triplet->next.valid = false;
+ break;
+ }
+ default:
+ break;
+ }
+
+ reset_mission_item_reached();
+
+ /* convert mission item to current position setpoint and make it valid */
+ mission_item_to_position_setpoint(&_mission_item, &pos_sp_triplet->current);
+ pos_sp_triplet->next.valid = false;
+
+ _navigator->set_position_setpoint_triplet_updated();
+}
+
+void
+RCLoss::advance_rcl()
+{
+ switch (_rcl_state) {
+ case RCL_STATE_NONE:
+ if (_param_loitertime.get() > 0.0f) {
+ warnx("RC loss, OBC mode, loiter");
+ mavlink_log_info(_navigator->get_mavlink_fd(), "#audio: rc loss, loitering");
+ _rcl_state = RCL_STATE_LOITER;
+ } else {
+ warnx("RC loss, OBC mode, slip loiter, terminate");
+ mavlink_log_info(_navigator->get_mavlink_fd(), "#audio: rc loss, terminating");
+ _rcl_state = RCL_STATE_TERMINATE;
+ _navigator->get_mission_result()->stay_in_failsafe = true;
+ _navigator->publish_mission_result();
+ reset_mission_item_reached();
+ }
+ break;
+ case RCL_STATE_LOITER:
+ _rcl_state = RCL_STATE_TERMINATE;
+ warnx("time is up, no RC regain, terminating");
+ mavlink_log_info(_navigator->get_mavlink_fd(), "#audio: RC not regained, terminating");
+ _navigator->get_mission_result()->stay_in_failsafe = true;
+ _navigator->publish_mission_result();
+ reset_mission_item_reached();
+ break;
+ case RCL_STATE_TERMINATE:
+ warnx("rcl end");
+ _rcl_state = RCL_STATE_END;
+ break;
+ default:
+ break;
+ }
+}
diff --git a/src/modules/navigator/rcloss.h b/src/modules/navigator/rcloss.h
new file mode 100644
index 000000000..bcb74d877
--- /dev/null
+++ b/src/modules/navigator/rcloss.h
@@ -0,0 +1,88 @@
+/***************************************************************************
+ *
+ * Copyright (c) 2013-2014 PX4 Development Team. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+/**
+ * @file rcloss.h
+ * Helper class for RC Loss Mode acording to the OBC rules
+ *
+ * @author Thomas Gubler <thomasgubler@gmail.com>
+ */
+
+#ifndef NAVIGATOR_RCLOSS_H
+#define NAVIGATOR_RCLOSS_H
+
+#include <controllib/blocks.hpp>
+#include <controllib/block/BlockParam.hpp>
+
+#include <uORB/Subscription.hpp>
+
+#include "navigator_mode.h"
+#include "mission_block.h"
+
+class Navigator;
+
+class RCLoss : public MissionBlock
+{
+public:
+ RCLoss(Navigator *navigator, const char *name);
+
+ ~RCLoss();
+
+ virtual void on_inactive();
+
+ virtual void on_activation();
+
+ virtual void on_active();
+
+private:
+ /* Params */
+ control::BlockParamFloat _param_loitertime;
+
+ enum RCLState {
+ RCL_STATE_NONE = 0,
+ RCL_STATE_LOITER = 1,
+ RCL_STATE_TERMINATE = 2,
+ RCL_STATE_END = 3
+ } _rcl_state;
+
+ /**
+ * Set the RCL item
+ */
+ void set_rcl_item();
+
+ /**
+ * Move to next RCL item
+ */
+ void advance_rcl();
+
+};
+#endif
diff --git a/src/modules/navigator/rcloss_params.c b/src/modules/navigator/rcloss_params.c
new file mode 100644
index 000000000..f1a01c73b
--- /dev/null
+++ b/src/modules/navigator/rcloss_params.c
@@ -0,0 +1,60 @@
+/****************************************************************************
+ *
+ * Copyright (c) 2014 PX4 Development Team. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * AS IS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/**
+ * @file rcloss_params.c
+ *
+ * Parameters for RC Loss (OBC)
+ *
+ * @author Thomas Gubler <thomasgubler@gmail.com>
+ */
+
+#include <nuttx/config.h>
+
+#include <systemlib/param/param.h>
+
+/*
+ * OBC RC Loss mode parameters, accessible via MAVLink
+ */
+
+/**
+ * Loiter Time
+ *
+ * The amount of time in seconds the system should loiter at current position before termination
+ * Set to -1 to make the system skip loitering
+ *
+ * @unit seconds
+ * @min -1.0
+ * @group RCL
+ */
+PARAM_DEFINE_FLOAT(NAV_RCL_LT, 120.0f);
diff --git a/src/modules/navigator/rtl.cpp b/src/modules/navigator/rtl.cpp
new file mode 100644
index 000000000..b6c4b8fdf
--- /dev/null
+++ b/src/modules/navigator/rtl.cpp
@@ -0,0 +1,317 @@
+/****************************************************************************
+ *
+ * Copyright (c) 2013-2014 PX4 Development Team. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+/**
+ * @file navigator_rtl.cpp
+ * Helper class to access RTL
+ * @author Julian Oes <julian@oes.ch>
+ * @author Anton Babushkin <anton.babushkin@me.com>
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+#include <fcntl.h>
+
+#include <mavlink/mavlink_log.h>
+#include <systemlib/err.h>
+#include <geo/geo.h>
+
+#include <uORB/uORB.h>
+#include <uORB/topics/mission.h>
+#include <uORB/topics/home_position.h>
+
+#include "navigator.h"
+#include "rtl.h"
+
+#define DELAY_SIGMA 0.01f
+
+RTL::RTL(Navigator *navigator, const char *name) :
+ MissionBlock(navigator, name),
+ _rtl_state(RTL_STATE_NONE),
+ _param_return_alt(this, "RTL_RETURN_ALT", false),
+ _param_descend_alt(this, "RTL_DESCEND_ALT", false),
+ _param_land_delay(this, "RTL_LAND_DELAY", false)
+{
+ /* load initial params */
+ updateParams();
+ /* initial reset */
+ on_inactive();
+}
+
+RTL::~RTL()
+{
+}
+
+void
+RTL::on_inactive()
+{
+ /* reset RTL state only if setpoint moved */
+ if (!_navigator->get_can_loiter_at_sp()) {
+ _rtl_state = RTL_STATE_NONE;
+ }
+}
+
+void
+RTL::on_activation()
+{
+ /* decide where to enter the RTL procedure when we switch into it */
+ if (_rtl_state == RTL_STATE_NONE) {
+ /* for safety reasons don't go into RTL if landed */
+ if (_navigator->get_vstatus()->condition_landed) {
+ _rtl_state = RTL_STATE_LANDED;
+ mavlink_log_info(_navigator->get_mavlink_fd(), "#audio: no RTL when landed");
+
+ /* if lower than return altitude, climb up first */
+ } else if (_navigator->get_global_position()->alt < _navigator->get_home_position()->alt
+ + _param_return_alt.get()) {
+ _rtl_state = RTL_STATE_CLIMB;
+
+ /* otherwise go straight to return */
+ } else {
+ /* set altitude setpoint to current altitude */
+ _rtl_state = RTL_STATE_RETURN;
+ _mission_item.altitude_is_relative = false;
+ _mission_item.altitude = _navigator->get_global_position()->alt;
+ }
+ }
+
+ set_rtl_item();
+}
+
+void
+RTL::on_active()
+{
+ if (_rtl_state != RTL_STATE_LANDED && is_mission_item_reached()) {
+ advance_rtl();
+ set_rtl_item();
+ }
+}
+
+void
+RTL::set_rtl_item()
+{
+ struct position_setpoint_triplet_s *pos_sp_triplet = _navigator->get_position_setpoint_triplet();
+
+ /* make sure we have the latest params */
+ updateParams();
+
+ set_previous_pos_setpoint();
+ _navigator->set_can_loiter_at_sp(false);
+
+ switch (_rtl_state) {
+ case RTL_STATE_CLIMB: {
+ float climb_alt = _navigator->get_home_position()->alt + _param_return_alt.get();
+
+ _mission_item.lat = _navigator->get_global_position()->lat;
+ _mission_item.lon = _navigator->get_global_position()->lon;
+ _mission_item.altitude_is_relative = false;
+ _mission_item.altitude = climb_alt;
+ _mission_item.yaw = NAN;
+ _mission_item.loiter_radius = _navigator->get_loiter_radius();
+ _mission_item.loiter_direction = 1;
+ _mission_item.nav_cmd = NAV_CMD_WAYPOINT;
+ _mission_item.acceptance_radius = _navigator->get_acceptance_radius();
+ _mission_item.time_inside = 0.0f;
+ _mission_item.pitch_min = 0.0f;
+ _mission_item.autocontinue = true;
+ _mission_item.origin = ORIGIN_ONBOARD;
+
+ mavlink_log_info(_navigator->get_mavlink_fd(), "#audio: RTL: climb to %d meters above home",
+ (int)(climb_alt - _navigator->get_home_position()->alt));
+ break;
+ }
+
+ case RTL_STATE_RETURN: {
+ _mission_item.lat = _navigator->get_home_position()->lat;
+ _mission_item.lon = _navigator->get_home_position()->lon;
+ // don't change altitude
+
+ if (pos_sp_triplet->previous.valid) {
+ /* if previous setpoint is valid then use it to calculate heading to home */
+ _mission_item.yaw = get_bearing_to_next_waypoint(
+ pos_sp_triplet->previous.lat, pos_sp_triplet->previous.lon,
+ _mission_item.lat, _mission_item.lon);
+
+ } else {
+ /* else use current position */
+ _mission_item.yaw = get_bearing_to_next_waypoint(
+ _navigator->get_global_position()->lat, _navigator->get_global_position()->lon,
+ _mission_item.lat, _mission_item.lon);
+ }
+ _mission_item.loiter_radius = _navigator->get_loiter_radius();
+ _mission_item.loiter_direction = 1;
+ _mission_item.nav_cmd = NAV_CMD_WAYPOINT;
+ _mission_item.acceptance_radius = _navigator->get_acceptance_radius();
+ _mission_item.time_inside = 0.0f;
+ _mission_item.pitch_min = 0.0f;
+ _mission_item.autocontinue = true;
+ _mission_item.origin = ORIGIN_ONBOARD;
+
+ mavlink_log_info(_navigator->get_mavlink_fd(), "#audio: RTL: return at %d meters above home",
+ (int)(_mission_item.altitude - _navigator->get_home_position()->alt));
+ break;
+ }
+
+ case RTL_STATE_DESCEND: {
+ _mission_item.lat = _navigator->get_home_position()->lat;
+ _mission_item.lon = _navigator->get_home_position()->lon;
+ _mission_item.altitude_is_relative = false;
+ _mission_item.altitude = _navigator->get_home_position()->alt + _param_descend_alt.get();
+ _mission_item.yaw = NAN;
+ _mission_item.loiter_radius = _navigator->get_loiter_radius();
+ _mission_item.loiter_direction = 1;
+ _mission_item.nav_cmd = NAV_CMD_LOITER_TIME_LIMIT;
+ _mission_item.acceptance_radius = _navigator->get_acceptance_radius();
+ _mission_item.time_inside = 0.0f;
+ _mission_item.pitch_min = 0.0f;
+ _mission_item.autocontinue = false;
+ _mission_item.origin = ORIGIN_ONBOARD;
+
+ mavlink_log_info(_navigator->get_mavlink_fd(), "#audio: RTL: descend to %d meters above home",
+ (int)(_mission_item.altitude - _navigator->get_home_position()->alt));
+ break;
+ }
+
+ case RTL_STATE_LOITER: {
+ bool autoland = _param_land_delay.get() > -DELAY_SIGMA;
+
+ _mission_item.lat = _navigator->get_home_position()->lat;
+ _mission_item.lon = _navigator->get_home_position()->lon;
+ _mission_item.altitude_is_relative = false;
+ _mission_item.altitude = _navigator->get_home_position()->alt + _param_descend_alt.get();
+ _mission_item.yaw = NAN;
+ _mission_item.loiter_radius = _navigator->get_loiter_radius();
+ _mission_item.loiter_direction = 1;
+ _mission_item.nav_cmd = autoland ? NAV_CMD_LOITER_TIME_LIMIT : NAV_CMD_LOITER_UNLIMITED;
+ _mission_item.acceptance_radius = _navigator->get_acceptance_radius();
+ _mission_item.time_inside = _param_land_delay.get() < 0.0f ? 0.0f : _param_land_delay.get();
+ _mission_item.pitch_min = 0.0f;
+ _mission_item.autocontinue = autoland;
+ _mission_item.origin = ORIGIN_ONBOARD;
+
+ _navigator->set_can_loiter_at_sp(true);
+
+ if (autoland) {
+ mavlink_log_info(_navigator->get_mavlink_fd(), "#audio: RTL: loiter %.1fs", (double)_mission_item.time_inside);
+
+ } else {
+ mavlink_log_info(_navigator->get_mavlink_fd(), "#audio: RTL: completed, loiter");
+ }
+ break;
+ }
+
+ case RTL_STATE_LAND: {
+ _mission_item.lat = _navigator->get_home_position()->lat;
+ _mission_item.lon = _navigator->get_home_position()->lon;
+ _mission_item.altitude_is_relative = false;
+ _mission_item.altitude = _navigator->get_home_position()->alt;
+ _mission_item.yaw = NAN;
+ _mission_item.loiter_radius = _navigator->get_loiter_radius();
+ _mission_item.loiter_direction = 1;
+ _mission_item.nav_cmd = NAV_CMD_LAND;
+ _mission_item.acceptance_radius = _navigator->get_acceptance_radius();
+ _mission_item.time_inside = 0.0f;
+ _mission_item.pitch_min = 0.0f;
+ _mission_item.autocontinue = true;
+ _mission_item.origin = ORIGIN_ONBOARD;
+
+ mavlink_log_info(_navigator->get_mavlink_fd(), "#audio: RTL: land at home");
+ break;
+ }
+
+ case RTL_STATE_LANDED: {
+ _mission_item.lat = _navigator->get_home_position()->lat;
+ _mission_item.lon = _navigator->get_home_position()->lon;
+ _mission_item.altitude_is_relative = false;
+ _mission_item.altitude = _navigator->get_home_position()->alt;
+ _mission_item.yaw = NAN;
+ _mission_item.loiter_radius = _navigator->get_loiter_radius();
+ _mission_item.loiter_direction = 1;
+ _mission_item.nav_cmd = NAV_CMD_IDLE;
+ _mission_item.acceptance_radius = _navigator->get_acceptance_radius();
+ _mission_item.time_inside = 0.0f;
+ _mission_item.pitch_min = 0.0f;
+ _mission_item.autocontinue = true;
+ _mission_item.origin = ORIGIN_ONBOARD;
+
+ mavlink_log_info(_navigator->get_mavlink_fd(), "#audio: RTL: completed, landed");
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ reset_mission_item_reached();
+
+ /* convert mission item to current position setpoint and make it valid */
+ mission_item_to_position_setpoint(&_mission_item, &pos_sp_triplet->current);
+ pos_sp_triplet->next.valid = false;
+
+ _navigator->set_position_setpoint_triplet_updated();
+}
+
+void
+RTL::advance_rtl()
+{
+ switch (_rtl_state) {
+ case RTL_STATE_CLIMB:
+ _rtl_state = RTL_STATE_RETURN;
+ break;
+
+ case RTL_STATE_RETURN:
+ _rtl_state = RTL_STATE_DESCEND;
+ break;
+
+ case RTL_STATE_DESCEND:
+ /* only go to land if autoland is enabled */
+ if (_param_land_delay.get() < -DELAY_SIGMA || _param_land_delay.get() > DELAY_SIGMA) {
+ _rtl_state = RTL_STATE_LOITER;
+
+ } else {
+ _rtl_state = RTL_STATE_LAND;
+ }
+ break;
+
+ case RTL_STATE_LOITER:
+ _rtl_state = RTL_STATE_LAND;
+ break;
+
+ case RTL_STATE_LAND:
+ _rtl_state = RTL_STATE_LANDED;
+ break;
+
+ default:
+ break;
+ }
+}
diff --git a/src/modules/navigator/rtl.h b/src/modules/navigator/rtl.h
new file mode 100644
index 000000000..5928f1f07
--- /dev/null
+++ b/src/modules/navigator/rtl.h
@@ -0,0 +1,96 @@
+/***************************************************************************
+ *
+ * Copyright (c) 2013-2014 PX4 Development Team. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+/**
+ * @file navigator_rtl.h
+ * Helper class for RTL
+ *
+ * @author Julian Oes <julian@oes.ch>
+ * @author Anton Babushkin <anton.babushkin@me.com>
+ */
+
+#ifndef NAVIGATOR_RTL_H
+#define NAVIGATOR_RTL_H
+
+#include <controllib/blocks.hpp>
+#include <controllib/block/BlockParam.hpp>
+
+#include <uORB/topics/mission.h>
+#include <uORB/topics/mission.h>
+#include <uORB/topics/home_position.h>
+#include <uORB/topics/vehicle_global_position.h>
+
+#include "navigator_mode.h"
+#include "mission_block.h"
+
+class Navigator;
+
+class RTL : public MissionBlock
+{
+public:
+ RTL(Navigator *navigator, const char *name);
+
+ ~RTL();
+
+ virtual void on_inactive();
+
+ virtual void on_activation();
+
+ virtual void on_active();
+
+private:
+ /**
+ * Set the RTL item
+ */
+ void set_rtl_item();
+
+ /**
+ * Move to next RTL item
+ */
+ void advance_rtl();
+
+ enum RTLState {
+ RTL_STATE_NONE = 0,
+ RTL_STATE_CLIMB,
+ RTL_STATE_RETURN,
+ RTL_STATE_DESCEND,
+ RTL_STATE_LOITER,
+ RTL_STATE_LAND,
+ RTL_STATE_LANDED,
+ } _rtl_state;
+
+ control::BlockParamFloat _param_return_alt;
+ control::BlockParamFloat _param_descend_alt;
+ control::BlockParamFloat _param_land_delay;
+};
+
+#endif
diff --git a/src/modules/navigator/rtl_params.c b/src/modules/navigator/rtl_params.c
new file mode 100644
index 000000000..bfe6ce7e1
--- /dev/null
+++ b/src/modules/navigator/rtl_params.c
@@ -0,0 +1,98 @@
+/****************************************************************************
+ *
+ * Copyright (c) 2014 PX4 Development Team. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * AS IS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/**
+ * @file rtl_params.c
+ *
+ * Parameters for RTL
+ *
+ * @author Julian Oes <julian@oes.ch>
+ */
+
+#include <nuttx/config.h>
+
+#include <systemlib/param/param.h>
+
+/*
+ * RTL parameters, accessible via MAVLink
+ */
+
+/**
+ * Loiter radius after RTL (FW only)
+ *
+ * Default value of loiter radius after RTL (fixedwing only).
+ *
+ * @unit meters
+ * @min 0.0
+ * @group RTL
+ */
+PARAM_DEFINE_FLOAT(RTL_LOITER_RAD, 50.0f);
+
+/**
+ * RTL altitude
+ *
+ * Altitude to fly back in RTL in meters
+ *
+ * @unit meters
+ * @min 0
+ * @max 1
+ * @group RTL
+ */
+PARAM_DEFINE_FLOAT(RTL_RETURN_ALT, 100);
+
+
+/**
+ * RTL loiter altitude
+ *
+ * Stay at this altitude above home position after RTL descending.
+ * Land (i.e. slowly descend) from this altitude if autolanding allowed.
+ *
+ * @unit meters
+ * @min 0
+ * @max 100
+ * @group RTL
+ */
+PARAM_DEFINE_FLOAT(RTL_DESCEND_ALT, 20);
+
+/**
+ * RTL delay
+ *
+ * Delay after descend before landing in RTL mode.
+ * If set to -1 the system will not land but loiter at NAV_LAND_ALT.
+ *
+ * @unit seconds
+ * @min -1
+ * @max
+ * @group RTL
+ */
+PARAM_DEFINE_FLOAT(RTL_LAND_DELAY, -1.0f);
diff --git a/src/modules/position_estimator_inav/position_estimator_inav_main.c b/src/modules/position_estimator_inav/position_estimator_inav_main.c
index f908d7a3b..e736a86d7 100644
--- a/src/modules/position_estimator_inav/position_estimator_inav_main.c
+++ b/src/modules/position_estimator_inav/position_estimator_inav_main.c
@@ -49,6 +49,7 @@
#include <sys/prctl.h>
#include <termios.h>
#include <math.h>
+#include <float.h>
#include <uORB/uORB.h>
#include <uORB/topics/parameter_update.h>
#include <uORB/topics/actuator_controls.h>
@@ -58,6 +59,7 @@
#include <uORB/topics/vehicle_local_position.h>
#include <uORB/topics/vehicle_global_position.h>
#include <uORB/topics/vehicle_gps_position.h>
+#include <uORB/topics/vision_position_estimate.h>
#include <uORB/topics/home_position.h>
#include <uORB/topics/optical_flow.h>
#include <mavlink/mavlink_log.h>
@@ -79,6 +81,7 @@ static bool thread_running = false; /**< Deamon status flag */
static int position_estimator_inav_task; /**< Handle of deamon task / thread */
static bool verbose_mode = false;
+static const hrt_abstime vision_topic_timeout = 500000; // Vision topic timeout = 0.5s
static const hrt_abstime gps_topic_timeout = 500000; // GPS topic timeout = 0.5s
static const hrt_abstime flow_topic_timeout = 1000000; // optical flow topic timeout = 1s
static const hrt_abstime sonar_timeout = 150000; // sonar timeout = 150ms
@@ -158,7 +161,7 @@ int position_estimator_inav_main(int argc, char *argv[])
thread_should_exit = true;
} else {
- warnx("app not started");
+ warnx("not started");
}
exit(0);
@@ -166,10 +169,10 @@ int position_estimator_inav_main(int argc, char *argv[])
if (!strcmp(argv[1], "status")) {
if (thread_running) {
- warnx("app is running");
+ warnx("is running");
} else {
- warnx("app not started");
+ warnx("not started");
}
exit(0);
@@ -179,15 +182,21 @@ int position_estimator_inav_main(int argc, char *argv[])
exit(1);
}
-void write_debug_log(const char *msg, float dt, float x_est[2], float y_est[2], float z_est[2], float x_est_prev[2], float y_est_prev[2], float z_est_prev[2], float acc[3], float corr_gps[3][2], float w_xy_gps_p, float w_xy_gps_v)
+static void write_debug_log(const char *msg, float dt, float x_est[2], float y_est[2], float z_est[2], float x_est_prev[2], float y_est_prev[2], float z_est_prev[2], float acc[3], float corr_gps[3][2], float w_xy_gps_p, float w_xy_gps_v)
{
FILE *f = fopen("/fs/microsd/inav.log", "a");
if (f) {
char *s = malloc(256);
- unsigned n = snprintf(s, 256, "%llu %s\n\tdt=%.5f x_est=[%.5f %.5f] y_est=[%.5f %.5f] z_est=[%.5f %.5f] x_est_prev=[%.5f %.5f] y_est_prev=[%.5f %.5f] z_est_prev=[%.5f %.5f]\n", hrt_absolute_time(), msg, dt, x_est[0], x_est[1], y_est[0], y_est[1], z_est[0], z_est[1], x_est_prev[0], x_est_prev[1], y_est_prev[0], y_est_prev[1], z_est_prev[0], z_est_prev[1]);
+ unsigned n = snprintf(s, 256, "%llu %s\n\tdt=%.5f x_est=[%.5f %.5f] y_est=[%.5f %.5f] z_est=[%.5f %.5f] x_est_prev=[%.5f %.5f] y_est_prev=[%.5f %.5f] z_est_prev=[%.5f %.5f]\n",
+ hrt_absolute_time(), msg, (double)dt,
+ (double)x_est[0], (double)x_est[1], (double)y_est[0], (double)y_est[1], (double)z_est[0], (double)z_est[1],
+ (double)x_est_prev[0], (double)x_est_prev[1], (double)y_est_prev[0], (double)y_est_prev[1], (double)z_est_prev[0], (double)z_est_prev[1]);
fwrite(s, 1, n, f);
- n = snprintf(s, 256, "\tacc=[%.5f %.5f %.5f] gps_pos_corr=[%.5f %.5f %.5f] gps_vel_corr=[%.5f %.5f %.5f] w_xy_gps_p=%.5f w_xy_gps_v=%.5f\n", acc[0], acc[1], acc[2], corr_gps[0][0], corr_gps[1][0], corr_gps[2][0], corr_gps[0][1], corr_gps[1][1], corr_gps[2][1], w_xy_gps_p, w_xy_gps_v);
+ n = snprintf(s, 256, "\tacc=[%.5f %.5f %.5f] gps_pos_corr=[%.5f %.5f %.5f] gps_vel_corr=[%.5f %.5f %.5f] w_xy_gps_p=%.5f w_xy_gps_v=%.5f\n",
+ (double)acc[0], (double)acc[1], (double)acc[2],
+ (double)corr_gps[0][0], (double)corr_gps[1][0], (double)corr_gps[2][0], (double)corr_gps[0][1], (double)corr_gps[1][1], (double)corr_gps[2][1],
+ (double)w_xy_gps_p, (double)w_xy_gps_v);
fwrite(s, 1, n, f);
free(s);
}
@@ -201,10 +210,8 @@ void write_debug_log(const char *msg, float dt, float x_est[2], float y_est[2],
****************************************************************************/
int position_estimator_inav_thread_main(int argc, char *argv[])
{
- warnx("started");
int mavlink_fd;
mavlink_fd = open(MAVLINK_LOG_DEVICE, 0);
- mavlink_log_info(mavlink_fd, "[inav] started");
float x_est[2] = { 0.0f, 0.0f }; // pos, vel
float y_est[2] = { 0.0f, 0.0f }; // pos, vel
@@ -226,6 +233,9 @@ int position_estimator_inav_thread_main(int argc, char *argv[])
float eph_flow = 1.0f;
+ float eph_vision = 0.5f;
+ float epv_vision = 0.5f;
+
float x_est_prev[2], y_est_prev[2], z_est_prev[2];
memset(x_est_prev, 0, sizeof(x_est_prev));
memset(y_est_prev, 0, sizeof(y_est_prev));
@@ -261,9 +271,6 @@ int position_estimator_inav_thread_main(int argc, char *argv[])
hrt_abstime t_prev = 0;
- /* acceleration in NED frame */
- float accel_NED[3] = { 0.0f, 0.0f, -CONSTANTS_ONE_G };
-
/* store error when sensor updates, but correct on each time step to avoid jumps in estimated value */
float acc[] = { 0.0f, 0.0f, 0.0f }; // N E D
float acc_bias[] = { 0.0f, 0.0f, 0.0f }; // body frame
@@ -275,6 +282,13 @@ int position_estimator_inav_thread_main(int argc, char *argv[])
};
float w_gps_xy = 1.0f;
float w_gps_z = 1.0f;
+
+ float corr_vision[3][2] = {
+ { 0.0f, 0.0f }, // N (pos, vel)
+ { 0.0f, 0.0f }, // E (pos, vel)
+ { 0.0f, 0.0f }, // D (pos, vel)
+ };
+
float corr_sonar = 0.0f;
float corr_sonar_filtered = 0.0f;
@@ -285,12 +299,12 @@ int position_estimator_inav_thread_main(int argc, char *argv[])
hrt_abstime flow_prev = 0; // time of last flow measurement
hrt_abstime sonar_time = 0; // time of last sonar measurement (not filtered)
hrt_abstime sonar_valid_time = 0; // time of last sonar measurement used for correction (filtered)
- hrt_abstime xy_src_time = 0; // time of last available position data
bool gps_valid = false; // GPS is valid
bool sonar_valid = false; // sonar is valid
bool flow_valid = false; // flow is valid
bool flow_accurate = false; // flow should be accurate (this flag not updated if flow_valid == false)
+ bool vision_valid = false;
/* declare and safely initialize all structs */
struct actuator_controls_s actuator;
@@ -309,6 +323,8 @@ int position_estimator_inav_thread_main(int argc, char *argv[])
memset(&local_pos, 0, sizeof(local_pos));
struct optical_flow_s flow;
memset(&flow, 0, sizeof(flow));
+ struct vision_position_estimate vision;
+ memset(&vision, 0, sizeof(vision));
struct vehicle_global_position_s global_pos;
memset(&global_pos, 0, sizeof(global_pos));
@@ -320,6 +336,7 @@ int position_estimator_inav_thread_main(int argc, char *argv[])
int vehicle_attitude_sub = orb_subscribe(ORB_ID(vehicle_attitude));
int optical_flow_sub = orb_subscribe(ORB_ID(optical_flow));
int vehicle_gps_position_sub = orb_subscribe(ORB_ID(vehicle_gps_position));
+ int vision_position_estimate_sub = orb_subscribe(ORB_ID(vision_position_estimate));
int home_position_sub = orb_subscribe(ORB_ID(home_position));
/* advertise */
@@ -370,8 +387,8 @@ int position_estimator_inav_thread_main(int argc, char *argv[])
} else {
wait_baro = false;
baro_offset /= (float) baro_init_cnt;
- warnx("baro offs: %.2f", baro_offset);
- mavlink_log_info(mavlink_fd, "[inav] baro offs: %.2f", baro_offset);
+ warnx("baro offs: %d", (int)baro_offset);
+ mavlink_log_info(mavlink_fd, "[inav] baro offs: %d", (int)baro_offset);
local_pos.z_valid = true;
local_pos.v_z_valid = true;
}
@@ -475,7 +492,11 @@ int position_estimator_inav_thread_main(int argc, char *argv[])
float flow_dt = flow_prev > 0 ? (flow.flow_timestamp - flow_prev) * 1e-6f : 0.1f;
flow_prev = flow.flow_timestamp;
- if (flow.ground_distance_m > 0.31f && flow.ground_distance_m < 4.0f && att.R[2][2] > 0.7 && flow.ground_distance_m != sonar_prev) {
+ if ((flow.ground_distance_m > 0.31f) &&
+ (flow.ground_distance_m < 4.0f) &&
+ (att.R[2][2] > 0.7f) &&
+ (fabsf(flow.ground_distance_m - sonar_prev) > FLT_EPSILON)) {
+
sonar_time = t;
sonar_prev = flow.ground_distance_m;
corr_sonar = flow.ground_distance_m + surface_offset + z_est[0];
@@ -497,7 +518,7 @@ int position_estimator_inav_thread_main(int argc, char *argv[])
sonar_valid_time = t;
sonar_valid = true;
local_pos.surface_bottom_timestamp = t;
- mavlink_log_info(mavlink_fd, "[inav] new surface level: %.2f", surface_offset);
+ mavlink_log_info(mavlink_fd, "[inav] new surface level: %d", (int)surface_offset);
}
} else {
@@ -510,7 +531,7 @@ int position_estimator_inav_thread_main(int argc, char *argv[])
float flow_q = flow.quality / 255.0f;
float dist_bottom = - z_est[0] - surface_offset;
- if (dist_bottom > 0.3f && flow_q > params.flow_q_min && (t < sonar_valid_time + sonar_valid_timeout) && att.R[2][2] > 0.7) {
+ if (dist_bottom > 0.3f && flow_q > params.flow_q_min && (t < sonar_valid_time + sonar_valid_timeout) && att.R[2][2] > 0.7f) {
/* distance to surface */
float flow_dist = dist_bottom / att.R[2][2];
/* check if flow if too large for accurate measurements */
@@ -558,7 +579,7 @@ int position_estimator_inav_thread_main(int argc, char *argv[])
}
/* under ideal conditions, on 1m distance assume EPH = 10cm */
- eph_flow = 0.1 / w_flow;
+ eph_flow = 0.1f / w_flow;
flow_valid = true;
@@ -609,6 +630,46 @@ int position_estimator_inav_thread_main(int argc, char *argv[])
}
}
+
+ /* check no vision circuit breaker is set */
+ if (params.no_vision != CBRK_NO_VISION_KEY) {
+ /* vehicle vision position */
+ orb_check(vision_position_estimate_sub, &updated);
+
+ if (updated) {
+ orb_copy(ORB_ID(vision_position_estimate), vision_position_estimate_sub, &vision);
+
+ /* reset position estimate on first vision update */
+ if (!vision_valid) {
+ x_est[0] = vision.x;
+ x_est[1] = vision.vx;
+ y_est[0] = vision.y;
+ y_est[1] = vision.vy;
+ /* only reset the z estimate if the z weight parameter is not zero */
+ if (params.w_z_vision_p > MIN_VALID_W)
+ {
+ z_est[0] = vision.z;
+ z_est[1] = vision.vz;
+ }
+
+ vision_valid = true;
+ warnx("VISION estimate valid");
+ mavlink_log_info(mavlink_fd, "[inav] VISION estimate valid");
+ }
+
+ /* calculate correction for position */
+ corr_vision[0][0] = vision.x - x_est[0];
+ corr_vision[1][0] = vision.y - y_est[0];
+ corr_vision[2][0] = vision.z - z_est[0];
+
+ /* calculate correction for velocity */
+ corr_vision[0][1] = vision.vx - x_est[1];
+ corr_vision[1][1] = vision.vy - y_est[1];
+ corr_vision[2][1] = vision.vz - z_est[1];
+
+ }
+ }
+
/* vehicle GPS position */
orb_check(vehicle_gps_position_sub, &updated);
@@ -619,13 +680,13 @@ int position_estimator_inav_thread_main(int argc, char *argv[])
/* hysteresis for GPS quality */
if (gps_valid) {
- if (gps.eph_m > max_eph_epv || gps.epv_m > max_eph_epv || gps.fix_type < 3) {
+ if (gps.eph > max_eph_epv || gps.epv > max_eph_epv || gps.fix_type < 3) {
gps_valid = false;
mavlink_log_info(mavlink_fd, "[inav] GPS signal lost");
}
} else {
- if (gps.eph_m < max_eph_epv * 0.7f && gps.epv_m < max_eph_epv * 0.7f && gps.fix_type >= 3) {
+ if (gps.eph < max_eph_epv * 0.7f && gps.epv < max_eph_epv * 0.7f && gps.fix_type >= 3) {
gps_valid = true;
reset_est = true;
mavlink_log_info(mavlink_fd, "[inav] GPS signal found");
@@ -644,25 +705,23 @@ int position_estimator_inav_thread_main(int argc, char *argv[])
} else if (t > ref_init_start + ref_init_delay) {
ref_inited = true;
- /* update baro offset */
- baro_offset -= z_est[0];
/* set position estimate to (0, 0, 0), use GPS velocity for XY */
x_est[0] = 0.0f;
x_est[1] = gps.vel_n_m_s;
y_est[0] = 0.0f;
y_est[1] = gps.vel_e_m_s;
- z_est[0] = 0.0f;
local_pos.ref_lat = lat;
local_pos.ref_lon = lon;
- local_pos.ref_alt = alt;
+ local_pos.ref_alt = alt + z_est[0];
local_pos.ref_timestamp = t;
/* initialize projection */
map_projection_init(&ref, lat, lon);
- warnx("init ref: lat=%.7f, lon=%.7f, alt=%.2f", lat, lon, alt);
- mavlink_log_info(mavlink_fd, "[inav] init ref: lat=%.7f, lon=%.7f, alt=%.2f", lat, lon, alt);
+ // XXX replace this print
+ warnx("init ref: lat=%.7f, lon=%.7f, alt=%8.4f", (double)lat, (double)lon, (double)alt);
+ mavlink_log_info(mavlink_fd, "[inav] init ref: %.7f, %.7f, %8.4f", (double)lat, (double)lon, (double)alt);
}
}
@@ -705,8 +764,8 @@ int position_estimator_inav_thread_main(int argc, char *argv[])
/* save rotation matrix at this moment */
memcpy(R_gps, R_buf[est_i], sizeof(R_gps));
- w_gps_xy = min_eph_epv / fmaxf(min_eph_epv, gps.eph_m);
- w_gps_z = min_eph_epv / fmaxf(min_eph_epv, gps.epv_m);
+ w_gps_xy = min_eph_epv / fmaxf(min_eph_epv, gps.eph);
+ w_gps_z = min_eph_epv / fmaxf(min_eph_epv, gps.epv);
}
} else {
@@ -728,14 +787,21 @@ int position_estimator_inav_thread_main(int argc, char *argv[])
}
/* check for timeout on GPS topic */
- if (gps_valid && t > gps.timestamp_position + gps_topic_timeout) {
+ if (gps_valid && (t > (gps.timestamp_position + gps_topic_timeout))) {
gps_valid = false;
warnx("GPS timeout");
mavlink_log_info(mavlink_fd, "[inav] GPS timeout");
}
+ /* check for timeout on vision topic */
+ if (vision_valid && (t > (vision.timestamp_boot + vision_topic_timeout))) {
+ vision_valid = false;
+ warnx("VISION timeout");
+ mavlink_log_info(mavlink_fd, "[inav] VISION timeout");
+ }
+
/* check for sonar measurement timeout */
- if (sonar_valid && t > sonar_time + sonar_timeout) {
+ if (sonar_valid && (t > (sonar_time + sonar_timeout))) {
corr_sonar = 0.0f;
sonar_valid = false;
}
@@ -746,24 +812,22 @@ int position_estimator_inav_thread_main(int argc, char *argv[])
/* increase EPH/EPV on each step */
if (eph < max_eph_epv) {
- eph *= 1.0 + dt;
+ eph *= 1.0f + dt;
}
if (epv < max_eph_epv) {
- epv += 0.005 * dt; // add 1m to EPV each 200s (baro drift)
+ epv += 0.005f * dt; // add 1m to EPV each 200s (baro drift)
}
/* use GPS if it's valid and reference position initialized */
bool use_gps_xy = ref_inited && gps_valid && params.w_xy_gps_p > MIN_VALID_W;
bool use_gps_z = ref_inited && gps_valid && params.w_z_gps_p > MIN_VALID_W;
+ /* use VISION if it's valid and has a valid weight parameter */
+ bool use_vision_xy = vision_valid && params.w_xy_vision_p > MIN_VALID_W;
+ bool use_vision_z = vision_valid && params.w_z_vision_p > MIN_VALID_W;
/* use flow if it's valid and (accurate or no GPS available) */
bool use_flow = flow_valid && (flow_accurate || !use_gps_xy);
- /* try to estimate position during some time after position sources lost */
- if (use_gps_xy || use_flow) {
- xy_src_time = t;
- }
-
- bool can_estimate_xy = (eph < max_eph_epv) || use_gps_xy || use_flow;
+ bool can_estimate_xy = (eph < max_eph_epv) || use_gps_xy || use_flow || use_vision_xy;
bool dist_bottom_valid = (t < sonar_valid_time + sonar_valid_timeout);
@@ -782,6 +846,10 @@ int position_estimator_inav_thread_main(int argc, char *argv[])
float w_xy_gps_v = params.w_xy_gps_v * w_gps_xy;
float w_z_gps_p = params.w_z_gps_p * w_gps_z;
+ float w_xy_vision_p = params.w_xy_vision_p;
+ float w_xy_vision_v = params.w_xy_vision_v;
+ float w_z_vision_p = params.w_z_vision_p;
+
/* reduce GPS weight if optical flow is good */
if (use_flow && flow_accurate) {
w_xy_gps_p *= params.w_gps_flow;
@@ -822,6 +890,35 @@ int position_estimator_inav_thread_main(int argc, char *argv[])
}
}
+ /* accelerometer bias correction for VISION (use buffered rotation matrix) */
+ accel_bias_corr[0] = 0.0f;
+ accel_bias_corr[1] = 0.0f;
+ accel_bias_corr[2] = 0.0f;
+
+ if (use_vision_xy) {
+ accel_bias_corr[0] -= corr_vision[0][0] * w_xy_vision_p * w_xy_vision_p;
+ accel_bias_corr[0] -= corr_vision[0][1] * w_xy_vision_v;
+ accel_bias_corr[1] -= corr_vision[1][0] * w_xy_vision_p * w_xy_vision_p;
+ accel_bias_corr[1] -= corr_vision[1][1] * w_xy_vision_v;
+ }
+
+ if (use_vision_z) {
+ accel_bias_corr[2] -= corr_vision[2][0] * w_z_vision_p * w_z_vision_p;
+ }
+
+ /* transform error vector from NED frame to body frame */
+ for (int i = 0; i < 3; i++) {
+ float c = 0.0f;
+
+ for (int j = 0; j < 3; j++) {
+ c += att.R[j][i] * accel_bias_corr[j];
+ }
+
+ if (isfinite(c)) {
+ acc_bias[i] += c * params.w_acc_bias * dt;
+ }
+ }
+
/* accelerometer bias correction for flow and baro (assume that there is no delay) */
accel_bias_corr[0] = 0.0f;
accel_bias_corr[1] = 0.0f;
@@ -859,15 +956,21 @@ int position_estimator_inav_thread_main(int argc, char *argv[])
inertial_filter_correct(corr_baro, dt, z_est, 0, params.w_z_baro);
if (use_gps_z) {
- epv = fminf(epv, gps.epv_m);
+ epv = fminf(epv, gps.epv);
inertial_filter_correct(corr_gps[2][0], dt, z_est, 0, w_z_gps_p);
}
+ if (use_vision_z) {
+ epv = fminf(epv, epv_vision);
+ inertial_filter_correct(corr_vision[2][0], dt, z_est, 0, w_z_vision_p);
+ }
+
if (!(isfinite(z_est[0]) && isfinite(z_est[1]))) {
write_debug_log("BAD ESTIMATE AFTER Z CORRECTION", dt, x_est, y_est, z_est, x_est_prev, y_est_prev, z_est_prev, acc, corr_gps, w_xy_gps_p, w_xy_gps_v);
memcpy(z_est, z_est_prev, sizeof(z_est));
memset(corr_gps, 0, sizeof(corr_gps));
+ memset(corr_vision, 0, sizeof(corr_vision));
corr_baro = 0;
} else {
@@ -894,7 +997,7 @@ int position_estimator_inav_thread_main(int argc, char *argv[])
}
if (use_gps_xy) {
- eph = fminf(eph, gps.eph_m);
+ eph = fminf(eph, gps.eph);
inertial_filter_correct(corr_gps[0][0], dt, x_est, 0, w_xy_gps_p);
inertial_filter_correct(corr_gps[1][0], dt, y_est, 0, w_xy_gps_p);
@@ -905,17 +1008,34 @@ int position_estimator_inav_thread_main(int argc, char *argv[])
}
}
+ if (use_vision_xy) {
+ eph = fminf(eph, eph_vision);
+
+ inertial_filter_correct(corr_vision[0][0], dt, x_est, 0, w_xy_vision_p);
+ inertial_filter_correct(corr_vision[1][0], dt, y_est, 0, w_xy_vision_p);
+
+ if (w_xy_vision_v > MIN_VALID_W) {
+ inertial_filter_correct(corr_vision[0][1], dt, x_est, 1, w_xy_vision_v);
+ inertial_filter_correct(corr_vision[1][1], dt, y_est, 1, w_xy_vision_v);
+ }
+ }
+
if (!(isfinite(x_est[0]) && isfinite(x_est[1]) && isfinite(y_est[0]) && isfinite(y_est[1]))) {
write_debug_log("BAD ESTIMATE AFTER CORRECTION", dt, x_est, y_est, z_est, x_est_prev, y_est_prev, z_est_prev, acc, corr_gps, w_xy_gps_p, w_xy_gps_v);
memcpy(x_est, x_est_prev, sizeof(x_est));
memcpy(y_est, y_est_prev, sizeof(y_est));
memset(corr_gps, 0, sizeof(corr_gps));
+ memset(corr_vision, 0, sizeof(corr_vision));
memset(corr_flow, 0, sizeof(corr_flow));
} else {
memcpy(x_est_prev, x_est, sizeof(x_est));
memcpy(y_est_prev, y_est, sizeof(y_est));
}
+ } else {
+ /* gradually reset xy velocity estimates */
+ inertial_filter_correct(-x_est[1], dt, x_est, 1, params.w_xy_res_v);
+ inertial_filter_correct(-y_est[1], dt, y_est, 1, params.w_xy_res_v);
}
/* detect land */
@@ -931,6 +1051,9 @@ int position_estimator_inav_thread_main(int argc, char *argv[])
landed = false;
landed_time = 0;
}
+ /* reset xy velocity estimates when landed */
+ x_est[1] = 0.0f;
+ y_est[1] = 0.0f;
} else {
if (alt_disp2 < land_disp2 && thrust < params.land_thr) {
@@ -955,11 +1078,11 @@ int position_estimator_inav_thread_main(int argc, char *argv[])
float updates_dt = (t - updates_counter_start) * 0.000001f;
warnx(
"updates rate: accelerometer = %.1f/s, baro = %.1f/s, gps = %.1f/s, attitude = %.1f/s, flow = %.1f/s",
- accel_updates / updates_dt,
- baro_updates / updates_dt,
- gps_updates / updates_dt,
- attitude_updates / updates_dt,
- flow_updates / updates_dt);
+ (double)(accel_updates / updates_dt),
+ (double)(baro_updates / updates_dt),
+ (double)(gps_updates / updates_dt),
+ (double)(attitude_updates / updates_dt),
+ (double)(flow_updates / updates_dt));
updates_counter_start = t;
accel_updates = 0;
baro_updates = 0;
diff --git a/src/modules/position_estimator_inav/position_estimator_inav_params.c b/src/modules/position_estimator_inav/position_estimator_inav_params.c
index d88419c25..cc0fb4155 100644
--- a/src/modules/position_estimator_inav/position_estimator_inav_params.c
+++ b/src/modules/position_estimator_inav/position_estimator_inav_params.c
@@ -1,7 +1,6 @@
/****************************************************************************
*
- * Copyright (C) 2013 Anton Babushkin. All rights reserved.
- * Author: Anton Babushkin <rk3dov@gmail.com>
+ * Copyright (c) 2013, 2014 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -35,36 +34,261 @@
/*
* @file position_estimator_inav_params.c
*
+ * @author Anton Babushkin <rk3dov@gmail.com>
+ *
* Parameters for position_estimator_inav
*/
#include "position_estimator_inav_params.h"
+/**
+ * Z axis weight for barometer
+ *
+ * Weight (cutoff frequency) for barometer altitude measurements.
+ *
+ * @min 0.0
+ * @max 10.0
+ * @group Position Estimator INAV
+ */
PARAM_DEFINE_FLOAT(INAV_W_Z_BARO, 0.5f);
+
+/**
+ * Z axis weight for GPS
+ *
+ * Weight (cutoff frequency) for GPS altitude measurements. GPS altitude data is very noisy and should be used only as slow correction for baro offset.
+ *
+ * @min 0.0
+ * @max 10.0
+ * @group Position Estimator INAV
+ */
PARAM_DEFINE_FLOAT(INAV_W_Z_GPS_P, 0.005f);
+
+/**
+ * Z axis weight for vision
+ *
+ * Weight (cutoff frequency) for vision altitude measurements. vision altitude data is very noisy and should be used only as slow correction for baro offset.
+ *
+ * @min 0.0
+ * @max 10.0
+ * @group Position Estimator INAV
+ */
+PARAM_DEFINE_FLOAT(INAV_W_Z_VIS_P, 0.5f);
+
+/**
+ * Z axis weight for sonar
+ *
+ * Weight (cutoff frequency) for sonar measurements.
+ *
+ * @min 0.0
+ * @max 10.0
+ * @group Position Estimator INAV
+ */
PARAM_DEFINE_FLOAT(INAV_W_Z_SONAR, 3.0f);
+
+/**
+ * XY axis weight for GPS position
+ *
+ * Weight (cutoff frequency) for GPS position measurements.
+ *
+ * @min 0.0
+ * @max 10.0
+ * @group Position Estimator INAV
+ */
PARAM_DEFINE_FLOAT(INAV_W_XY_GPS_P, 1.0f);
+
+/**
+ * XY axis weight for GPS velocity
+ *
+ * Weight (cutoff frequency) for GPS velocity measurements.
+ *
+ * @min 0.0
+ * @max 10.0
+ * @group Position Estimator INAV
+ */
PARAM_DEFINE_FLOAT(INAV_W_XY_GPS_V, 2.0f);
+
+/**
+ * XY axis weight for vision position
+ *
+ * Weight (cutoff frequency) for vision position measurements.
+ *
+ * @min 0.0
+ * @max 10.0
+ * @group Position Estimator INAV
+ */
+PARAM_DEFINE_FLOAT(INAV_W_XY_VIS_P, 5.0f);
+
+/**
+ * XY axis weight for vision velocity
+ *
+ * Weight (cutoff frequency) for vision velocity measurements.
+ *
+ * @min 0.0
+ * @max 10.0
+ * @group Position Estimator INAV
+ */
+PARAM_DEFINE_FLOAT(INAV_W_XY_VIS_V, 0.0f);
+
+/**
+ * XY axis weight for optical flow
+ *
+ * Weight (cutoff frequency) for optical flow (velocity) measurements.
+ *
+ * @min 0.0
+ * @max 10.0
+ * @group Position Estimator INAV
+ */
PARAM_DEFINE_FLOAT(INAV_W_XY_FLOW, 5.0f);
+
+/**
+ * XY axis weight for resetting velocity
+ *
+ * When velocity sources lost slowly decrease estimated horizontal velocity with this weight.
+ *
+ * @min 0.0
+ * @max 10.0
+ * @group Position Estimator INAV
+ */
+PARAM_DEFINE_FLOAT(INAV_W_XY_RES_V, 0.5f);
+
+/**
+ * XY axis weight factor for GPS when optical flow available
+ *
+ * When optical flow data available, multiply GPS weights (for position and velocity) by this factor.
+ *
+ * @min 0.0
+ * @max 1.0
+ * @group Position Estimator INAV
+ */
PARAM_DEFINE_FLOAT(INAV_W_GPS_FLOW, 0.1f);
+
+/**
+ * Accelerometer bias estimation weight
+ *
+ * Weight (cutoff frequency) for accelerometer bias estimation. 0 to disable.
+ *
+ * @min 0.0
+ * @max 0.1
+ * @group Position Estimator INAV
+ */
PARAM_DEFINE_FLOAT(INAV_W_ACC_BIAS, 0.05f);
+
+/**
+ * Optical flow scale factor
+ *
+ * Factor to convert raw optical flow (in pixels) to radians [rad/px].
+ *
+ * @min 0.0
+ * @max 1.0
+ * @unit rad/px
+ * @group Position Estimator INAV
+ */
PARAM_DEFINE_FLOAT(INAV_FLOW_K, 0.15f);
+
+/**
+ * Minimal acceptable optical flow quality
+ *
+ * 0 - lowest quality, 1 - best quality.
+ *
+ * @min 0.0
+ * @max 1.0
+ * @group Position Estimator INAV
+ */
PARAM_DEFINE_FLOAT(INAV_FLOW_Q_MIN, 0.5f);
+
+/**
+ * Weight for sonar filter
+ *
+ * Sonar filter detects spikes on sonar measurements and used to detect new surface level.
+ *
+ * @min 0.0
+ * @max 1.0
+ * @group Position Estimator INAV
+ */
PARAM_DEFINE_FLOAT(INAV_SONAR_FILT, 0.05f);
+
+/**
+ * Sonar maximal error for new surface
+ *
+ * If sonar measurement error is larger than this value it skiped (spike) or accepted as new surface level (if offset is stable).
+ *
+ * @min 0.0
+ * @max 1.0
+ * @unit m
+ * @group Position Estimator INAV
+ */
PARAM_DEFINE_FLOAT(INAV_SONAR_ERR, 0.5f);
+
+/**
+ * Land detector time
+ *
+ * Vehicle assumed landed if no altitude changes happened during this time on low throttle.
+ *
+ * @min 0.0
+ * @max 10.0
+ * @unit s
+ * @group Position Estimator INAV
+ */
PARAM_DEFINE_FLOAT(INAV_LAND_T, 3.0f);
+
+/**
+ * Land detector altitude dispersion threshold
+ *
+ * Dispersion threshold for triggering land detector.
+ *
+ * @min 0.0
+ * @max 10.0
+ * @unit m
+ * @group Position Estimator INAV
+ */
PARAM_DEFINE_FLOAT(INAV_LAND_DISP, 0.7f);
+
+/**
+ * Land detector throttle threshold
+ *
+ * Value should be lower than minimal hovering thrust. Half of it is good choice.
+ *
+ * @min 0.0
+ * @max 1.0
+ * @group Position Estimator INAV
+ */
PARAM_DEFINE_FLOAT(INAV_LAND_THR, 0.2f);
+
+/**
+ * GPS delay
+ *
+ * GPS delay compensation
+ *
+ * @min 0.0
+ * @max 1.0
+ * @unit s
+ * @group Position Estimator INAV
+ */
PARAM_DEFINE_FLOAT(INAV_DELAY_GPS, 0.2f);
+/**
+ * Disable vision input
+ *
+ * Set to the appropriate key (328754) to disable vision input.
+ *
+ * @min 0.0
+ * @max 1.0
+ * @group Position Estimator INAV
+ */
+PARAM_DEFINE_INT32(CBRK_NO_VISION, 0);
+
int parameters_init(struct position_estimator_inav_param_handles *h)
{
h->w_z_baro = param_find("INAV_W_Z_BARO");
h->w_z_gps_p = param_find("INAV_W_Z_GPS_P");
+ h->w_z_vision_p = param_find("INAV_W_Z_VIS_P");
h->w_z_sonar = param_find("INAV_W_Z_SONAR");
h->w_xy_gps_p = param_find("INAV_W_XY_GPS_P");
h->w_xy_gps_v = param_find("INAV_W_XY_GPS_V");
+ h->w_xy_vision_p = param_find("INAV_W_XY_VIS_P");
+ h->w_xy_vision_v = param_find("INAV_W_XY_VIS_V");
h->w_xy_flow = param_find("INAV_W_XY_FLOW");
+ h->w_xy_res_v = param_find("INAV_W_XY_RES_V");
h->w_gps_flow = param_find("INAV_W_GPS_FLOW");
h->w_acc_bias = param_find("INAV_W_ACC_BIAS");
h->flow_k = param_find("INAV_FLOW_K");
@@ -74,6 +298,7 @@ int parameters_init(struct position_estimator_inav_param_handles *h)
h->land_t = param_find("INAV_LAND_T");
h->land_disp = param_find("INAV_LAND_DISP");
h->land_thr = param_find("INAV_LAND_THR");
+ h->no_vision = param_find("CBRK_NO_VISION");
h->delay_gps = param_find("INAV_DELAY_GPS");
return OK;
@@ -83,10 +308,14 @@ int parameters_update(const struct position_estimator_inav_param_handles *h, str
{
param_get(h->w_z_baro, &(p->w_z_baro));
param_get(h->w_z_gps_p, &(p->w_z_gps_p));
+ param_get(h->w_z_vision_p, &(p->w_z_vision_p));
param_get(h->w_z_sonar, &(p->w_z_sonar));
param_get(h->w_xy_gps_p, &(p->w_xy_gps_p));
param_get(h->w_xy_gps_v, &(p->w_xy_gps_v));
+ param_get(h->w_xy_vision_p, &(p->w_xy_vision_p));
+ param_get(h->w_xy_vision_v, &(p->w_xy_vision_v));
param_get(h->w_xy_flow, &(p->w_xy_flow));
+ param_get(h->w_xy_res_v, &(p->w_xy_res_v));
param_get(h->w_gps_flow, &(p->w_gps_flow));
param_get(h->w_acc_bias, &(p->w_acc_bias));
param_get(h->flow_k, &(p->flow_k));
@@ -96,6 +325,7 @@ int parameters_update(const struct position_estimator_inav_param_handles *h, str
param_get(h->land_t, &(p->land_t));
param_get(h->land_disp, &(p->land_disp));
param_get(h->land_thr, &(p->land_thr));
+ param_get(h->no_vision, &(p->no_vision));
param_get(h->delay_gps, &(p->delay_gps));
return OK;
diff --git a/src/modules/position_estimator_inav/position_estimator_inav_params.h b/src/modules/position_estimator_inav/position_estimator_inav_params.h
index df1cfaa71..d0a65e42e 100644
--- a/src/modules/position_estimator_inav/position_estimator_inav_params.h
+++ b/src/modules/position_estimator_inav/position_estimator_inav_params.h
@@ -1,7 +1,6 @@
/****************************************************************************
*
- * Copyright (C) 2013 Anton Babushkin. All rights reserved.
- * Author: Anton Babushkin <rk3dov@gmail.com>
+ * Copyright (c) 2013, 2014 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -33,9 +32,11 @@
****************************************************************************/
/*
- * @file position_estimator_inav_params.h
+ * @file position_estimator_inav_params.c
*
- * Parameters for Position Estimator
+ * @author Anton Babushkin <rk3dov@gmail.com>
+ *
+ * Parameters definition for position_estimator_inav
*/
#include <systemlib/param/param.h>
@@ -43,10 +44,14 @@
struct position_estimator_inav_params {
float w_z_baro;
float w_z_gps_p;
+ float w_z_vision_p;
float w_z_sonar;
float w_xy_gps_p;
float w_xy_gps_v;
+ float w_xy_vision_p;
+ float w_xy_vision_v;
float w_xy_flow;
+ float w_xy_res_v;
float w_gps_flow;
float w_acc_bias;
float flow_k;
@@ -56,16 +61,21 @@ struct position_estimator_inav_params {
float land_t;
float land_disp;
float land_thr;
+ int32_t no_vision;
float delay_gps;
};
struct position_estimator_inav_param_handles {
param_t w_z_baro;
param_t w_z_gps_p;
+ param_t w_z_vision_p;
param_t w_z_sonar;
param_t w_xy_gps_p;
param_t w_xy_gps_v;
+ param_t w_xy_vision_p;
+ param_t w_xy_vision_v;
param_t w_xy_flow;
+ param_t w_xy_res_v;
param_t w_gps_flow;
param_t w_acc_bias;
param_t flow_k;
@@ -75,9 +85,12 @@ struct position_estimator_inav_param_handles {
param_t land_t;
param_t land_disp;
param_t land_thr;
+ param_t no_vision;
param_t delay_gps;
};
+#define CBRK_NO_VISION_KEY 328754
+
/**
* Initialize all parameter handles and values
*
diff --git a/src/modules/px4iofirmware/controls.c b/src/modules/px4iofirmware/controls.c
index 62e6b12cb..58c9429b6 100644
--- a/src/modules/px4iofirmware/controls.c
+++ b/src/modules/px4iofirmware/controls.c
@@ -41,21 +41,74 @@
#include <stdbool.h>
#include <drivers/drv_hrt.h>
+#include <drivers/drv_rc_input.h>
#include <systemlib/perf_counter.h>
#include <systemlib/ppm_decode.h>
+#include <rc/st24.h>
#include "px4io.h"
#define RC_FAILSAFE_TIMEOUT 2000000 /**< two seconds failsafe timeout */
-#define RC_CHANNEL_HIGH_THRESH 5000
-#define RC_CHANNEL_LOW_THRESH -5000
+#define RC_CHANNEL_HIGH_THRESH 5000 /* 75% threshold */
+#define RC_CHANNEL_LOW_THRESH -8000 /* 10% threshold */
static bool ppm_input(uint16_t *values, uint16_t *num_values, uint16_t *frame_len);
+static bool dsm_port_input(uint16_t *rssi, bool *dsm_updated, bool *st24_updated);
static perf_counter_t c_gather_dsm;
static perf_counter_t c_gather_sbus;
static perf_counter_t c_gather_ppm;
+static int _dsm_fd;
+
+static uint16_t rc_value_override = 0;
+
+bool dsm_port_input(uint16_t *rssi, bool *dsm_updated, bool *st24_updated)
+{
+ perf_begin(c_gather_dsm);
+ uint16_t temp_count = r_raw_rc_count;
+ uint8_t n_bytes = 0;
+ uint8_t *bytes;
+ *dsm_updated = dsm_input(r_raw_rc_values, &temp_count, &n_bytes, &bytes);
+ if (*dsm_updated) {
+ r_raw_rc_count = temp_count & 0x7fff;
+ if (temp_count & 0x8000)
+ r_raw_rc_flags |= PX4IO_P_RAW_RC_FLAGS_RC_DSM11;
+ else
+ r_raw_rc_flags &= ~PX4IO_P_RAW_RC_FLAGS_RC_DSM11;
+
+ r_raw_rc_flags &= ~(PX4IO_P_RAW_RC_FLAGS_FRAME_DROP);
+ r_raw_rc_flags &= ~(PX4IO_P_RAW_RC_FLAGS_FAILSAFE);
+
+ }
+ perf_end(c_gather_dsm);
+
+ /* get data from FD and attempt to parse with DSM and ST24 libs */
+ uint8_t st24_rssi, rx_count;
+ uint16_t st24_channel_count = 0;
+
+ *st24_updated = false;
+
+ for (unsigned i = 0; i < n_bytes; i++) {
+ /* set updated flag if one complete packet was parsed */
+ st24_rssi = RC_INPUT_RSSI_MAX;
+ *st24_updated |= (OK == st24_decode(bytes[i], &st24_rssi, &rx_count,
+ &st24_channel_count, r_raw_rc_values, PX4IO_RC_INPUT_CHANNELS));
+ }
+
+ if (*st24_updated) {
+
+ *rssi = st24_rssi;
+ r_raw_rc_count = st24_channel_count;
+
+ r_status_flags |= PX4IO_P_STATUS_FLAGS_RC_ST24;
+ r_raw_rc_flags &= ~(PX4IO_P_RAW_RC_FLAGS_FRAME_DROP);
+ r_raw_rc_flags &= ~(PX4IO_P_RAW_RC_FLAGS_FAILSAFE);
+ }
+
+ return (*dsm_updated | *st24_updated);
+}
+
void
controls_init(void)
{
@@ -65,7 +118,7 @@ controls_init(void)
system_state.rc_channels_timestamp_valid = 0;
/* DSM input (USART1) */
- dsm_init("/dev/ttyS0");
+ _dsm_fd = dsm_init("/dev/ttyS0");
/* S.bus input (USART3) */
sbus_init("/dev/ttyS2");
@@ -116,19 +169,13 @@ controls_tick() {
#endif
perf_begin(c_gather_dsm);
- uint16_t temp_count = r_raw_rc_count;
- bool dsm_updated = dsm_input(r_raw_rc_values, &temp_count);
+ bool dsm_updated, st24_updated;
+ (void)dsm_port_input(&rssi, &dsm_updated, &st24_updated);
if (dsm_updated) {
- r_raw_rc_flags |= PX4IO_P_STATUS_FLAGS_RC_DSM;
- r_raw_rc_count = temp_count & 0x7fff;
- if (temp_count & 0x8000)
- r_raw_rc_flags |= PX4IO_P_RAW_RC_FLAGS_RC_DSM11;
- else
- r_raw_rc_flags &= ~PX4IO_P_RAW_RC_FLAGS_RC_DSM11;
-
- r_raw_rc_flags &= ~(PX4IO_P_RAW_RC_FLAGS_FRAME_DROP);
- r_raw_rc_flags &= ~(PX4IO_P_RAW_RC_FLAGS_FAILSAFE);
-
+ r_status_flags |= PX4IO_P_STATUS_FLAGS_RC_DSM;
+ }
+ if (st24_updated) {
+ r_status_flags |= PX4IO_P_STATUS_FLAGS_RC_ST24;
}
perf_end(c_gather_dsm);
@@ -191,7 +238,7 @@ controls_tick() {
/*
* If we received a new frame from any of the RC sources, process it.
*/
- if (dsm_updated || sbus_updated || ppm_updated) {
+ if (dsm_updated || sbus_updated || ppm_updated || st24_updated) {
/* record a bitmask of channels assigned */
unsigned assigned_channels = 0;
@@ -278,6 +325,9 @@ controls_tick() {
r_rc_values[mapped] = SIGNED_TO_REG(scaled);
assigned_channels |= (1 << mapped);
+ } else if (mapped == PX4IO_P_RC_CONFIG_ASSIGNMENT_MODESWITCH) {
+ /* pick out override channel, indicated by special mapping */
+ rc_value_override = SIGNED_TO_REG(scaled);
}
}
}
@@ -371,15 +421,24 @@ controls_tick() {
* requested override.
*
*/
- if ((r_status_flags & PX4IO_P_STATUS_FLAGS_RC_OK) && (REG_TO_SIGNED(r_rc_values[4]) < RC_CHANNEL_LOW_THRESH))
+ if ((r_status_flags & PX4IO_P_STATUS_FLAGS_RC_OK) && (REG_TO_SIGNED(rc_value_override) < RC_CHANNEL_LOW_THRESH))
override = true;
+ /*
+ if the FMU is dead then enable override if we have a
+ mixer and OVERRIDE_IMMEDIATE is set
+ */
+ if (!(r_status_flags & PX4IO_P_STATUS_FLAGS_FMU_OK) &&
+ (r_setup_arming & PX4IO_P_SETUP_ARMING_OVERRIDE_IMMEDIATE) &&
+ (r_status_flags & PX4IO_P_STATUS_FLAGS_MIXER_OK))
+ override = true;
+
if (override) {
r_status_flags |= PX4IO_P_STATUS_FLAGS_OVERRIDE;
/* mix new RC input control values to servos */
- if (dsm_updated || sbus_updated || ppm_updated)
+ if (dsm_updated || sbus_updated || ppm_updated || st24_updated)
mixer_tick();
} else {
diff --git a/src/modules/px4iofirmware/dsm.c b/src/modules/px4iofirmware/dsm.c
index d3f365822..6e57c9ec7 100644
--- a/src/modules/px4iofirmware/dsm.c
+++ b/src/modules/px4iofirmware/dsm.c
@@ -452,10 +452,12 @@ dsm_decode(hrt_abstime frame_time, uint16_t *values, uint16_t *num_values)
*
* @param[out] values pointer to per channel array of decoded values
* @param[out] num_values pointer to number of raw channel values returned, high order bit 0:10 bit data, 1:11 bit data
+ * @param[out] n_butes number of bytes read
+ * @param[out] bytes pointer to the buffer of read bytes
* @return true=decoded raw channel values updated, false=no update
*/
bool
-dsm_input(uint16_t *values, uint16_t *num_values)
+dsm_input(uint16_t *values, uint16_t *num_values, uint8_t *n_bytes, uint8_t **bytes)
{
ssize_t ret;
hrt_abstime now;
@@ -478,8 +480,12 @@ dsm_input(uint16_t *values, uint16_t *num_values)
ret = read(dsm_fd, &dsm_frame[dsm_partial_frame_count], DSM_FRAME_SIZE - dsm_partial_frame_count);
/* if the read failed for any reason, just give up here */
- if (ret < 1)
+ if (ret < 1) {
return false;
+ } else {
+ *n_bytes = ret;
+ *bytes = &dsm_frame[dsm_partial_frame_count];
+ }
dsm_last_rx_time = now;
diff --git a/src/modules/px4iofirmware/i2c.c b/src/modules/px4iofirmware/i2c.c
index 79b6546b3..6d1d1fc2d 100644
--- a/src/modules/px4iofirmware/i2c.c
+++ b/src/modules/px4iofirmware/i2c.c
@@ -64,12 +64,15 @@
#define rCCR REG(STM32_I2C_CCR_OFFSET)
#define rTRISE REG(STM32_I2C_TRISE_OFFSET)
+void i2c_reset(void);
static int i2c_interrupt(int irq, void *context);
static void i2c_rx_setup(void);
static void i2c_tx_setup(void);
static void i2c_rx_complete(void);
static void i2c_tx_complete(void);
+#ifdef DEBUG
static void i2c_dump(void);
+#endif
static DMA_HANDLE rx_dma;
static DMA_HANDLE tx_dma;
@@ -331,6 +334,7 @@ i2c_tx_complete(void)
i2c_tx_setup();
}
+#ifdef DEBUG
static void
i2c_dump(void)
{
@@ -339,3 +343,4 @@ i2c_dump(void)
debug("CCR 0x%08x TRISE 0x%08x", rCCR, rTRISE);
debug("SR1 0x%08x SR2 0x%08x", rSR1, rSR2);
}
+#endif
diff --git a/src/modules/px4iofirmware/mixer.cpp b/src/modules/px4iofirmware/mixer.cpp
index 2f721bf1e..c0b8ac358 100644
--- a/src/modules/px4iofirmware/mixer.cpp
+++ b/src/modules/px4iofirmware/mixer.cpp
@@ -58,14 +58,7 @@ extern "C" {
/*
* Maximum interval in us before FMU signal is considered lost
*/
-#define FMU_INPUT_DROP_LIMIT_US 200000
-
-/* XXX need to move the RC_CHANNEL_FUNCTION out of rc_channels.h and into systemlib */
-#define ROLL 0
-#define PITCH 1
-#define YAW 2
-#define THROTTLE 3
-#define OVERRIDE 4
+#define FMU_INPUT_DROP_LIMIT_US 500000
/* current servo arm/disarm state */
static bool mixer_servos_armed = false;
@@ -98,7 +91,8 @@ mixer_tick(void)
{
/* check that we are receiving fresh data from the FMU */
- if (hrt_elapsed_time(&system_state.fmu_data_received_time) > FMU_INPUT_DROP_LIMIT_US) {
+ if ((system_state.fmu_data_received_time == 0) ||
+ hrt_elapsed_time(&system_state.fmu_data_received_time) > FMU_INPUT_DROP_LIMIT_US) {
/* too long without FMU input, time to go to failsafe */
if (r_status_flags & PX4IO_P_STATUS_FLAGS_FMU_OK) {
@@ -109,9 +103,12 @@ mixer_tick(void)
} else {
r_status_flags |= PX4IO_P_STATUS_FLAGS_FMU_OK;
+
+ /* this flag is never cleared once OK */
+ r_status_flags |= PX4IO_P_STATUS_FLAGS_FMU_INITIALIZED;
}
- /* default to failsafe mixing */
+ /* default to failsafe mixing - it will be forced below if flag is set */
source = MIX_FAILSAFE;
/*
@@ -139,7 +136,9 @@ mixer_tick(void)
(r_status_flags & PX4IO_P_STATUS_FLAGS_RC_OK) &&
(r_status_flags & PX4IO_P_STATUS_FLAGS_MIXER_OK) &&
!(r_setup_arming & PX4IO_P_SETUP_ARMING_RC_HANDLING_DISABLED) &&
- !(r_status_flags & PX4IO_P_STATUS_FLAGS_FMU_OK)) {
+ !(r_status_flags & PX4IO_P_STATUS_FLAGS_FMU_OK) &&
+ /* do not enter manual override if we asked for termination failsafe and FMU is lost */
+ !(r_setup_arming & PX4IO_P_SETUP_ARMING_TERMINATION_FAILSAFE)) {
/* if allowed, mix from RC inputs directly */
source = MIX_OVERRIDE;
@@ -155,22 +154,11 @@ mixer_tick(void)
}
/*
- * Set failsafe status flag depending on mixing source
- */
- if (source == MIX_FAILSAFE) {
- r_status_flags |= PX4IO_P_STATUS_FLAGS_FAILSAFE;
- } else {
- r_status_flags &= ~(PX4IO_P_STATUS_FLAGS_FAILSAFE);
- }
-
- /*
* Decide whether the servos should be armed right now.
*
* We must be armed, and we must have a PWM source; either raw from
* FMU or from the mixer.
*
- * XXX correct behaviour for failsafe may require an additional case
- * here.
*/
should_arm = (
/* IO initialised without error */ (r_status_flags & PX4IO_P_STATUS_FLAGS_INIT_OK)
@@ -188,6 +176,38 @@ mixer_tick(void)
&& (r_status_flags & PX4IO_P_STATUS_FLAGS_FMU_OK);
/*
+ * Check if failsafe termination is set - if yes,
+ * set the force failsafe flag once entering the first
+ * failsafe condition.
+ */
+ if ( /* if we have requested flight termination style failsafe (noreturn) */
+ (r_setup_arming & PX4IO_P_SETUP_ARMING_TERMINATION_FAILSAFE) &&
+ /* and we ended up in a failsafe condition */
+ (source == MIX_FAILSAFE) &&
+ /* and we should be armed, so we intended to provide outputs */
+ should_arm &&
+ /* and FMU is initialized */
+ (r_status_flags & PX4IO_P_STATUS_FLAGS_FMU_INITIALIZED)) {
+ r_setup_arming |= PX4IO_P_SETUP_ARMING_FORCE_FAILSAFE;
+ }
+
+ /*
+ * Check if we should force failsafe - and do it if we have to
+ */
+ if (r_setup_arming & PX4IO_P_SETUP_ARMING_FORCE_FAILSAFE) {
+ source = MIX_FAILSAFE;
+ }
+
+ /*
+ * Set failsafe status flag depending on mixing source
+ */
+ if (source == MIX_FAILSAFE) {
+ r_status_flags |= PX4IO_P_STATUS_FLAGS_FAILSAFE;
+ } else {
+ r_status_flags &= ~(PX4IO_P_STATUS_FLAGS_FAILSAFE);
+ }
+
+ /*
* Run the mixers.
*/
if (source == MIX_FAILSAFE) {
diff --git a/src/modules/px4iofirmware/module.mk b/src/modules/px4iofirmware/module.mk
index 01869569f..eb99e8a96 100644
--- a/src/modules/px4iofirmware/module.mk
+++ b/src/modules/px4iofirmware/module.mk
@@ -14,7 +14,8 @@ SRCS = adc.c \
../systemlib/mixer/mixer_group.cpp \
../systemlib/mixer/mixer_multirotor.cpp \
../systemlib/mixer/mixer_simple.cpp \
- ../systemlib/pwm_limit/pwm_limit.c
+ ../systemlib/pwm_limit/pwm_limit.c \
+ ../../lib/rc/st24.c
ifeq ($(BOARD),px4io-v1)
SRCS += i2c.c
diff --git a/src/modules/px4iofirmware/protocol.h b/src/modules/px4iofirmware/protocol.h
index 7471faec7..c7e9ae3eb 100644
--- a/src/modules/px4iofirmware/protocol.h
+++ b/src/modules/px4iofirmware/protocol.h
@@ -1,6 +1,6 @@
/****************************************************************************
*
- * Copyright (c) 2012, 2013 PX4 Development Team. All rights reserved.
+ * Copyright (c) 2012-2014 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -111,6 +111,8 @@
#define PX4IO_P_STATUS_FLAGS_INIT_OK (1 << 10) /* initialisation of the IO completed without error */
#define PX4IO_P_STATUS_FLAGS_FAILSAFE (1 << 11) /* failsafe is active */
#define PX4IO_P_STATUS_FLAGS_SAFETY_OFF (1 << 12) /* safety is off */
+#define PX4IO_P_STATUS_FLAGS_FMU_INITIALIZED (1 << 13) /* FMU was initialized and OK once */
+#define PX4IO_P_STATUS_FLAGS_RC_ST24 (1 << 14) /* ST24 input is valid */
#define PX4IO_P_STATUS_ALARMS 3 /* alarm flags - alarms latch, write 1 to a bit to clear it */
#define PX4IO_P_STATUS_ALARMS_VBATT_LOW (1 << 0) /* [1] VBatt is very close to regulator dropout */
@@ -179,6 +181,9 @@
#define PX4IO_P_SETUP_ARMING_ALWAYS_PWM_ENABLE (1 << 5) /* Output of PWM right after startup enabled to help ESCs initialize and prevent them from beeping */
#define PX4IO_P_SETUP_ARMING_RC_HANDLING_DISABLED (1 << 6) /* Disable the IO-internal evaluation of the RC */
#define PX4IO_P_SETUP_ARMING_LOCKDOWN (1 << 7) /* If set, the system operates normally, but won't actuate any servos */
+#define PX4IO_P_SETUP_ARMING_FORCE_FAILSAFE (1 << 8) /* If set, the system will always output the failsafe values */
+#define PX4IO_P_SETUP_ARMING_TERMINATION_FAILSAFE (1 << 9) /* If set, the system will never return from a failsafe, but remain in failsafe once triggered. */
+#define PX4IO_P_SETUP_ARMING_OVERRIDE_IMMEDIATE (1 << 10) /* If set then on FMU failure override is immediate. Othewise it waits for the mode switch to go past the override thrshold */
#define PX4IO_P_SETUP_PWM_RATES 2 /* bitmask, 0 = low rate, 1 = high rate */
#define PX4IO_P_SETUP_PWM_DEFAULTRATE 3 /* 'low' PWM frame output rate in Hz */
@@ -217,6 +222,7 @@ enum { /* DSM bind states */
hence index 12 can safely be used. */
#define PX4IO_P_SETUP_RC_THR_FAILSAFE_US 13 /**< the throttle failsafe pulse length in microseconds */
+#define PX4IO_P_SETUP_FORCE_SAFETY_ON 14 /* force safety switch into 'disarmed' (PWM disabled state) */
#define PX4IO_FORCE_SAFETY_MAGIC 22027 /* required argument for force safety (random) */
/* autopilot control values, -10000..10000 */
@@ -242,6 +248,7 @@ enum { /* DSM bind states */
#define PX4IO_P_RC_CONFIG_MAX 2 /**< highest input value */
#define PX4IO_P_RC_CONFIG_DEADZONE 3 /**< band around center that is ignored */
#define PX4IO_P_RC_CONFIG_ASSIGNMENT 4 /**< mapped input value */
+#define PX4IO_P_RC_CONFIG_ASSIGNMENT_MODESWITCH 100 /**< magic value for mode switch */
#define PX4IO_P_RC_CONFIG_OPTIONS 5 /**< channel options bitmask */
#define PX4IO_P_RC_CONFIG_OPTIONS_ENABLED (1 << 0)
#define PX4IO_P_RC_CONFIG_OPTIONS_REVERSE (1 << 1)
@@ -253,6 +260,10 @@ enum { /* DSM bind states */
/* PWM failsafe values - zero disables the output */
#define PX4IO_PAGE_FAILSAFE_PWM 55 /**< 0..CONFIG_ACTUATOR_COUNT-1 */
+/* PWM failsafe values - zero disables the output */
+#define PX4IO_PAGE_SENSORS 56 /**< Sensors connected to PX4IO */
+#define PX4IO_P_SENSORS_ALTITUDE 0 /**< Altitude of an external sensor (HoTT or S.BUS2) */
+
/* Debug and test page - not used in normal operation */
#define PX4IO_PAGE_TEST 127
#define PX4IO_P_TEST_LED 0 /**< set the amber LED on/off */
@@ -312,7 +323,7 @@ struct IOPacket {
#define PKT_COUNT(_p) ((_p).count_code & PKT_COUNT_MASK)
#define PKT_CODE(_p) ((_p).count_code & PKT_CODE_MASK)
-#define PKT_SIZE(_p) ((uint8_t *)&((_p).regs[PKT_COUNT(_p)]) - ((uint8_t *)&(_p)))
+#define PKT_SIZE(_p) ((size_t)((uint8_t *)&((_p).regs[PKT_COUNT(_p)]) - ((uint8_t *)&(_p))))
static const uint8_t crc8_tab[256] __attribute__((unused)) =
{
diff --git a/src/modules/px4iofirmware/px4io.c b/src/modules/px4iofirmware/px4io.c
index d4c25911e..14ee9cb40 100644
--- a/src/modules/px4iofirmware/px4io.c
+++ b/src/modules/px4iofirmware/px4io.c
@@ -37,6 +37,7 @@
*/
#include <nuttx/config.h>
+#include <nuttx/arch.h>
#include <stdio.h> // required for task_create
#include <stdbool.h>
@@ -84,6 +85,9 @@ static volatile uint8_t msg_next_out, msg_next_in;
#define NUM_MSG 2
static char msg[NUM_MSG][40];
+static void heartbeat_blink(void);
+static void ring_blink(void);
+
/*
* add a debug message to be printed on the console
*/
@@ -125,6 +129,65 @@ heartbeat_blink(void)
LED_BLUE(heartbeat = !heartbeat);
}
+static void
+ring_blink(void)
+{
+#ifdef GPIO_LED4
+
+ if (/* IO armed */ (r_status_flags & PX4IO_P_STATUS_FLAGS_SAFETY_OFF)
+ /* and FMU is armed */ && (r_setup_arming & PX4IO_P_SETUP_ARMING_FMU_ARMED)) {
+ LED_RING(1);
+ return;
+ }
+
+ // XXX this led code does have
+ // intentionally a few magic numbers.
+ const unsigned max_brightness = 118;
+
+ static unsigned counter = 0;
+ static unsigned brightness = max_brightness;
+ static unsigned brightness_counter = 0;
+ static unsigned on_counter = 0;
+
+ if (brightness_counter < max_brightness) {
+
+ bool on = ((on_counter * 100) / brightness_counter+1) <= ((brightness * 100) / max_brightness+1);
+
+ // XXX once led is PWM driven,
+ // remove the ! in the line below
+ // to return to the proper breathe
+ // animation / pattern (currently inverted)
+ LED_RING(!on);
+ brightness_counter++;
+
+ if (on) {
+ on_counter++;
+ }
+
+ } else {
+
+ if (counter >= 62) {
+ counter = 0;
+ }
+
+ int n;
+
+ if (counter < 32) {
+ n = counter;
+
+ } else {
+ n = 62 - counter;
+ }
+
+ brightness = (n * n) / 8;
+ brightness_counter = 0;
+ on_counter = 0;
+ counter++;
+ }
+
+#endif
+}
+
static uint64_t reboot_time;
/**
@@ -190,6 +253,9 @@ user_start(int argc, char *argv[])
LED_AMBER(false);
LED_BLUE(false);
LED_SAFETY(false);
+#ifdef GPIO_LED4
+ LED_RING(false);
+#endif
/* turn on servo power (if supported) */
#ifdef POWER_SERVO
@@ -293,6 +359,8 @@ user_start(int argc, char *argv[])
heartbeat_blink();
}
+ ring_blink();
+
check_reboot();
/* check for debug activity (default: none) */
@@ -303,14 +371,12 @@ user_start(int argc, char *argv[])
*/
if (hrt_absolute_time() - last_debug_time > (1000 * 1000)) {
- struct mallinfo minfo = mallinfo();
-
isr_debug(1, "d:%u s=0x%x a=0x%x f=0x%x m=%u",
(unsigned)r_page_setup[PX4IO_P_SETUP_SET_DEBUG],
(unsigned)r_status_flags,
(unsigned)r_setup_arming,
(unsigned)r_setup_features,
- (unsigned)minfo.mxordblk);
+ (unsigned)mallinfo().mxordblk);
last_debug_time = hrt_absolute_time();
}
}
diff --git a/src/modules/px4iofirmware/px4io.h b/src/modules/px4iofirmware/px4io.h
index ca175bfbc..93a33490f 100644
--- a/src/modules/px4iofirmware/px4io.h
+++ b/src/modules/px4iofirmware/px4io.h
@@ -140,6 +140,7 @@ extern pwm_limit_t pwm_limit;
#define LED_BLUE(_s) stm32_gpiowrite(GPIO_LED1, !(_s))
#define LED_AMBER(_s) stm32_gpiowrite(GPIO_LED2, !(_s))
#define LED_SAFETY(_s) stm32_gpiowrite(GPIO_LED3, !(_s))
+#define LED_RING(_s) stm32_gpiowrite(GPIO_LED4, (_s))
#ifdef CONFIG_ARCH_BOARD_PX4IO_V1
@@ -215,12 +216,12 @@ extern uint16_t adc_measure(unsigned channel);
extern void controls_init(void);
extern void controls_tick(void);
extern int dsm_init(const char *device);
-extern bool dsm_input(uint16_t *values, uint16_t *num_values);
+extern bool dsm_input(uint16_t *values, uint16_t *num_values, uint8_t *n_bytes, uint8_t **bytes);
extern void dsm_bind(uint16_t cmd, int pulses);
extern int sbus_init(const char *device);
extern bool sbus_input(uint16_t *values, uint16_t *num_values, bool *sbus_failsafe, bool *sbus_frame_drop, uint16_t max_channels);
-extern bool sbus1_output(uint16_t *values, uint16_t num_values);
-extern bool sbus2_output(uint16_t *values, uint16_t num_values);
+extern void sbus1_output(uint16_t *values, uint16_t num_values);
+extern void sbus2_output(uint16_t *values, uint16_t num_values);
/** global debug level for isr_debug() */
extern volatile uint8_t debug_level;
diff --git a/src/modules/px4iofirmware/registers.c b/src/modules/px4iofirmware/registers.c
index db1836f4a..fbfdd35db 100644
--- a/src/modules/px4iofirmware/registers.c
+++ b/src/modules/px4iofirmware/registers.c
@@ -1,6 +1,6 @@
/****************************************************************************
*
- * Copyright (c) 2012, 2013 PX4 Development Team. All rights reserved.
+ * Copyright (c) 2012-2014 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -119,7 +119,6 @@ uint16_t r_page_raw_rc_input[] =
[PX4IO_P_RAW_RC_DATA] = 0,
[PX4IO_P_RAW_FRAME_COUNT] = 0,
[PX4IO_P_RAW_LOST_FRAME_COUNT] = 0,
- [PX4IO_P_RAW_RC_DATA] = 0,
[PX4IO_P_RAW_RC_BASE ... (PX4IO_P_RAW_RC_BASE + PX4IO_RC_INPUT_CHANNELS)] = 0
};
@@ -190,7 +189,10 @@ volatile uint16_t r_page_setup[] =
PX4IO_P_SETUP_ARMING_FAILSAFE_CUSTOM | \
PX4IO_P_SETUP_ARMING_ALWAYS_PWM_ENABLE | \
PX4IO_P_SETUP_ARMING_RC_HANDLING_DISABLED | \
- PX4IO_P_SETUP_ARMING_LOCKDOWN)
+ PX4IO_P_SETUP_ARMING_LOCKDOWN | \
+ PX4IO_P_SETUP_ARMING_FORCE_FAILSAFE | \
+ PX4IO_P_SETUP_ARMING_TERMINATION_FAILSAFE | \
+ PX4IO_P_SETUP_ARMING_OVERRIDE_IMMEDIATE)
#define PX4IO_P_SETUP_RATES_VALID ((1 << PX4IO_SERVO_COUNT) - 1)
#define PX4IO_P_SETUP_RELAYS_VALID ((1 << PX4IO_RELAY_CHANNELS) - 1)
@@ -285,7 +287,9 @@ registers_set(uint8_t page, uint8_t offset, const uint16_t *values, unsigned num
while ((offset < PX4IO_CONTROL_CHANNELS) && (num_values > 0)) {
/* XXX range-check value? */
- r_page_servos[offset] = *values;
+ if (*values != PWM_IGNORE_THIS_CHANNEL) {
+ r_page_servos[offset] = *values;
+ }
offset++;
num_values--;
@@ -516,6 +520,19 @@ registers_set_one(uint8_t page, uint8_t offset, uint16_t value)
r_status_flags |= PX4IO_P_STATUS_FLAGS_INIT_OK;
}
+ /*
+ * If the failsafe termination flag is set, do not allow the autopilot to unset it
+ */
+ value |= (r_setup_arming & PX4IO_P_SETUP_ARMING_TERMINATION_FAILSAFE);
+
+ /*
+ * If failsafe termination is enabled and force failsafe bit is set, do not allow
+ * the autopilot to clear it.
+ */
+ if (r_setup_arming & PX4IO_P_SETUP_ARMING_TERMINATION_FAILSAFE) {
+ value |= (r_setup_arming & PX4IO_P_SETUP_ARMING_FORCE_FAILSAFE);
+ }
+
r_setup_arming = value;
break;
@@ -587,6 +604,12 @@ registers_set_one(uint8_t page, uint8_t offset, uint16_t value)
dsm_bind(value & 0x0f, (value >> 4) & 0xF);
break;
+ case PX4IO_P_SETUP_FORCE_SAFETY_ON:
+ if (value == PX4IO_FORCE_SAFETY_MAGIC) {
+ r_status_flags &= ~PX4IO_P_STATUS_FLAGS_SAFETY_OFF;
+ }
+ break;
+
case PX4IO_P_SETUP_FORCE_SAFETY_OFF:
if (value == PX4IO_FORCE_SAFETY_MAGIC) {
r_status_flags |= PX4IO_P_STATUS_FLAGS_SAFETY_OFF;
@@ -670,7 +693,8 @@ registers_set_one(uint8_t page, uint8_t offset, uint16_t value)
if (conf[PX4IO_P_RC_CONFIG_ASSIGNMENT] == UINT8_MAX) {
disabled = true;
- } else if ((int)(conf[PX4IO_P_RC_CONFIG_ASSIGNMENT]) < 0 || conf[PX4IO_P_RC_CONFIG_ASSIGNMENT] >= PX4IO_RC_MAPPED_CONTROL_CHANNELS) {
+ } else if ((conf[PX4IO_P_RC_CONFIG_ASSIGNMENT] >= PX4IO_RC_MAPPED_CONTROL_CHANNELS) &&
+ (conf[PX4IO_P_RC_CONFIG_ASSIGNMENT] != PX4IO_P_RC_CONFIG_ASSIGNMENT_MODESWITCH)) {
count++;
}
@@ -712,7 +736,7 @@ registers_get(uint8_t page, uint8_t offset, uint16_t **values, unsigned *num_val
{
#define SELECT_PAGE(_page_name) \
do { \
- *values = &_page_name[0]; \
+ *values = (uint16_t *)&_page_name[0]; \
*num_values = sizeof(_page_name) / sizeof(_page_name[0]); \
} while(0)
@@ -739,30 +763,19 @@ registers_get(uint8_t page, uint8_t offset, uint16_t **values, unsigned *num_val
{
/*
* Coefficients here derived by measurement of the 5-16V
- * range on one unit:
+ * range on one unit, validated on sample points of another unit
*
- * V counts
- * 5 1001
- * 6 1219
- * 7 1436
- * 8 1653
- * 9 1870
- * 10 2086
- * 11 2303
- * 12 2522
- * 13 2738
- * 14 2956
- * 15 3172
- * 16 3389
+ * Data in Tools/tests-host/data folder.
*
- * slope = 0.0046067
- * intercept = 0.3863
+ * measured slope = 0.004585267878277 (int: 4585)
+ * nominal theoretic slope: 0.00459340659 (int: 4593)
+ * intercept = 0.016646394188076 (int: 16646)
+ * nominal theoretic intercept: 0.00 (int: 0)
*
- * Intercept corrected for best results @ 12V.
*/
unsigned counts = adc_measure(ADC_VBATT);
if (counts != 0xffff) {
- unsigned mV = (4150 + (counts * 46)) / 10 - 200;
+ unsigned mV = (166460 + (counts * 45934)) / 10000;
unsigned corrected = (mV * r_page_setup[PX4IO_P_SETUP_VBATT_SCALE]) / 10000;
r_page_status[PX4IO_P_STATUS_VBATT] = corrected;
diff --git a/src/modules/px4iofirmware/sbus.c b/src/modules/px4iofirmware/sbus.c
index 0e7dc621c..d76ec55f0 100644
--- a/src/modules/px4iofirmware/sbus.c
+++ b/src/modules/px4iofirmware/sbus.c
@@ -57,11 +57,12 @@
#define SBUS_FLAGS_BYTE 23
#define SBUS_FAILSAFE_BIT 3
#define SBUS_FRAMELOST_BIT 2
+#define SBUS1_FRAME_DELAY 14000
/*
Measured values with Futaba FX-30/R6108SB:
-+100% on TX: PCM 1.100/1.520/1.950ms -> SBus raw values: 350/1024/1700 (100% ATV)
- -+140% on TX: PCM 0.930/1.520/2.112ms -> SBus raw values: 78/1024/1964 (140% ATV)
+ -+140% on TX: PCM 0.930/1.520/2.112ms -> SBus raw values: 78/1024/1964 (140% ATV)
-+152% on TX: PCM 0.884/1.520/2.160ms -> SBus raw values: 1/1024/2047 (140% ATV plus dirty tricks)
*/
@@ -80,6 +81,7 @@ static int sbus_fd = -1;
static hrt_abstime last_rx_time;
static hrt_abstime last_frame_time;
+static hrt_abstime last_txframe_time = 0;
static uint8_t frame[SBUS_FRAME_SIZE];
@@ -87,13 +89,15 @@ static unsigned partial_frame_count;
unsigned sbus_frame_drops;
-static bool sbus_decode(hrt_abstime frame_time, uint16_t *values, uint16_t *num_values, bool *sbus_failsafe, bool *sbus_frame_drop, uint16_t max_channels);
+static bool sbus_decode(hrt_abstime frame_time, uint16_t *values, uint16_t *num_values, bool *sbus_failsafe,
+ bool *sbus_frame_drop, uint16_t max_channels);
int
sbus_init(const char *device)
{
- if (sbus_fd < 0)
+ if (sbus_fd < 0) {
sbus_fd = open(device, O_RDWR | O_NONBLOCK);
+ }
if (sbus_fd >= 0) {
struct termios t;
@@ -113,19 +117,54 @@ sbus_init(const char *device)
} else {
debug("S.Bus: open failed");
}
+
return sbus_fd;
}
-bool
+void
sbus1_output(uint16_t *values, uint16_t num_values)
{
- write(sbus_fd, 'A', 1);
-}
+ uint8_t byteindex = 1; /*Data starts one byte into the sbus frame. */
+ uint8_t offset = 0;
+ uint16_t value;
+ hrt_abstime now;
-bool
+ now = hrt_absolute_time();
+
+ if ((now - last_txframe_time) > SBUS1_FRAME_DELAY) {
+ last_txframe_time = now;
+ uint8_t oframe[SBUS_FRAME_SIZE] = { 0x0f };
+
+ /* 16 is sbus number of servos/channels minus 2 single bit channels.
+ * currently ignoring single bit channels. */
+
+ for (unsigned i = 0; (i < num_values) && (i < 16); ++i) {
+ value = (uint16_t)(((values[i] - SBUS_SCALE_OFFSET) / SBUS_SCALE_FACTOR) + .5f);
+
+ /*protect from out of bounds values and limit to 11 bits*/
+ if (value > 0x07ff ) {
+ value = 0x07ff;
+ }
+
+ while (offset >= 8) {
+ ++byteindex;
+ offset -= 8;
+ }
+
+ oframe[byteindex] |= (value << (offset)) & 0xff;
+ oframe[byteindex + 1] |= (value >> (8 - offset)) & 0xff;
+ oframe[byteindex + 2] |= (value >> (16 - offset)) & 0xff;
+ offset += 11;
+ }
+
+ write(sbus_fd, oframe, SBUS_FRAME_SIZE);
+ }
+}
+void
sbus2_output(uint16_t *values, uint16_t num_values)
{
- write(sbus_fd, 'B', 1);
+ char b = 'B';
+ write(sbus_fd, &b, 1);
}
bool
@@ -165,8 +204,9 @@ sbus_input(uint16_t *values, uint16_t *num_values, bool *sbus_failsafe, bool *sb
ret = read(sbus_fd, &frame[partial_frame_count], SBUS_FRAME_SIZE - partial_frame_count);
/* if the read failed for any reason, just give up here */
- if (ret < 1)
+ if (ret < 1) {
return false;
+ }
last_rx_time = now;
@@ -178,8 +218,9 @@ sbus_input(uint16_t *values, uint16_t *num_values, bool *sbus_failsafe, bool *sb
/*
* If we don't have a full frame, return
*/
- if (partial_frame_count < SBUS_FRAME_SIZE)
+ if (partial_frame_count < SBUS_FRAME_SIZE) {
return false;
+ }
/*
* Great, it looks like we might have a frame. Go ahead and
@@ -226,7 +267,8 @@ static const struct sbus_bit_pick sbus_decoder[SBUS_INPUT_CHANNELS][3] = {
};
static bool
-sbus_decode(hrt_abstime frame_time, uint16_t *values, uint16_t *num_values, bool *sbus_failsafe, bool *sbus_frame_drop, uint16_t max_values)
+sbus_decode(hrt_abstime frame_time, uint16_t *values, uint16_t *num_values, bool *sbus_failsafe, bool *sbus_frame_drop,
+ uint16_t max_values)
{
/* check frame boundary markers to avoid out-of-sync cases */
if ((frame[0] != 0x0f)) {
@@ -235,23 +277,27 @@ sbus_decode(hrt_abstime frame_time, uint16_t *values, uint16_t *num_values, bool
}
switch (frame[24]) {
- case 0x00:
+ case 0x00:
/* this is S.BUS 1 */
break;
- case 0x03:
+
+ case 0x03:
/* S.BUS 2 SLOT0: RX battery and external voltage */
break;
- case 0x83:
+
+ case 0x83:
/* S.BUS 2 SLOT1 */
break;
- case 0x43:
- case 0xC3:
- case 0x23:
- case 0xA3:
- case 0x63:
- case 0xE3:
+
+ case 0x43:
+ case 0xC3:
+ case 0x23:
+ case 0xA3:
+ case 0x63:
+ case 0xE3:
break;
- default:
+
+ default:
/* we expect one of the bits above, but there are some we don't know yet */
break;
}
@@ -281,7 +327,7 @@ sbus_decode(hrt_abstime frame_time, uint16_t *values, uint16_t *num_values, bool
/* convert 0-2048 values to 1000-2000 ppm encoding in a not too sloppy fashion */
- values[channel] = (uint16_t)(value * SBUS_SCALE_FACTOR +.5f) + SBUS_SCALE_OFFSET;
+ values[channel] = (uint16_t)(value * SBUS_SCALE_FACTOR + .5f) + SBUS_SCALE_OFFSET;
}
/* decode switch channels if data fields are wide enough */
@@ -302,16 +348,17 @@ sbus_decode(hrt_abstime frame_time, uint16_t *values, uint16_t *num_values, bool
/* report that we failed to read anything valid off the receiver */
*sbus_failsafe = true;
*sbus_frame_drop = true;
- }
- else if (frame[SBUS_FLAGS_BYTE] & (1 << SBUS_FRAMELOST_BIT)) { /* a frame was lost */
+
+ } else if (frame[SBUS_FLAGS_BYTE] & (1 << SBUS_FRAMELOST_BIT)) { /* a frame was lost */
/* set a special warning flag
- *
- * Attention! This flag indicates a skipped frame only, not a total link loss! Handling this
- * condition as fail-safe greatly reduces the reliability and range of the radio link,
+ *
+ * Attention! This flag indicates a skipped frame only, not a total link loss! Handling this
+ * condition as fail-safe greatly reduces the reliability and range of the radio link,
* e.g. by prematurely issueing return-to-launch!!! */
*sbus_failsafe = false;
*sbus_frame_drop = true;
+
} else {
*sbus_failsafe = false;
*sbus_frame_drop = false;
diff --git a/src/modules/sdlog2/module.mk b/src/modules/sdlog2/module.mk
index a28d43e72..d4a00af39 100644
--- a/src/modules/sdlog2/module.mk
+++ b/src/modules/sdlog2/module.mk
@@ -43,3 +43,5 @@ SRCS = sdlog2.c \
logbuffer.c
MODULE_STACKSIZE = 1200
+
+MAXOPTIMIZATION = -Os
diff --git a/src/modules/sdlog2/sdlog2.c b/src/modules/sdlog2/sdlog2.c
index 577cadfbb..af580f1f7 100644
--- a/src/modules/sdlog2/sdlog2.c
+++ b/src/modules/sdlog2/sdlog2.c
@@ -74,7 +74,9 @@
#include <uORB/topics/vehicle_global_position.h>
#include <uORB/topics/position_setpoint_triplet.h>
#include <uORB/topics/vehicle_gps_position.h>
+#include <uORB/topics/satellite_info.h>
#include <uORB/topics/vehicle_vicon_position.h>
+#include <uORB/topics/vision_position_estimate.h>
#include <uORB/topics/vehicle_global_velocity_setpoint.h>
#include <uORB/topics/optical_flow.h>
#include <uORB/topics/battery_status.h>
@@ -84,8 +86,11 @@
#include <uORB/topics/esc_status.h>
#include <uORB/topics/telemetry_status.h>
#include <uORB/topics/estimator_status.h>
+#include <uORB/topics/tecs_status.h>
#include <uORB/topics/system_power.h>
#include <uORB/topics/servorail_status.h>
+#include <uORB/topics/wind_estimate.h>
+#include <uORB/topics/encoders.h>
#include <systemlib/systemlib.h>
#include <systemlib/param/param.h>
@@ -139,6 +144,8 @@ PARAM_DEFINE_INT32(SDLOG_EXT, -1);
fds[fdsc_count].events = POLLIN; \
fdsc_count++;
+#define MIN(X,Y) ((X) < (Y) ? (X) : (Y))
+
static bool main_thread_should_exit = false; /**< Deamon exit flag */
static bool thread_running = false; /**< Deamon status flag */
static int deamon_task; /**< Handle of deamon task / thread */
@@ -622,6 +629,9 @@ void sdlog2_start_log()
perf_print_all(perf_fd);
close(perf_fd);
+ /* reset performance counters to get in-flight min and max values in post flight log */
+ perf_reset_all();
+
logging_enabled = true;
}
@@ -929,6 +939,7 @@ int sdlog2_thread_main(int argc, char *argv[])
struct vehicle_global_position_s global_pos;
struct position_setpoint_triplet_s triplet;
struct vehicle_vicon_position_s vicon_pos;
+ struct vision_position_estimate vision_pos;
struct optical_flow_s flow;
struct rc_channels_s rc;
struct differential_pressure_s diff_pres;
@@ -939,8 +950,12 @@ int sdlog2_thread_main(int argc, char *argv[])
struct telemetry_status_s telemetry;
struct range_finder_report range_finder;
struct estimator_status_report estimator_status;
+ struct tecs_status_s tecs_status;
struct system_power_s system_power;
struct servorail_status_s servorail_status;
+ struct satellite_info_s sat_info;
+ struct wind_estimate_s wind_estimate;
+ struct encoders_s encoders;
} buf;
memset(&buf, 0, sizeof(buf));
@@ -971,14 +986,19 @@ int sdlog2_thread_main(int argc, char *argv[])
struct log_GVSP_s log_GVSP;
struct log_BATT_s log_BATT;
struct log_DIST_s log_DIST;
- struct log_TELE_s log_TELE;
- struct log_ESTM_s log_ESTM;
+ struct log_TEL_s log_TEL;
+ struct log_EST0_s log_EST0;
+ struct log_EST1_s log_EST1;
struct log_PWR_s log_PWR;
struct log_VICN_s log_VICN;
+ struct log_VISN_s log_VISN;
struct log_GS0A_s log_GS0A;
struct log_GS0B_s log_GS0B;
struct log_GS1A_s log_GS1A;
struct log_GS1B_s log_GS1B;
+ struct log_TECS_s log_TECS;
+ struct log_WIND_s log_WIND;
+ struct log_ENCD_s log_ENCD;
} body;
} log_msg = {
LOG_PACKET_HEADER_INIT(0)
@@ -1000,18 +1020,23 @@ int sdlog2_thread_main(int argc, char *argv[])
int global_pos_sub;
int triplet_sub;
int gps_pos_sub;
+ int sat_info_sub;
int vicon_pos_sub;
+ int vision_pos_sub;
int flow_sub;
int rc_sub;
int airspeed_sub;
int esc_sub;
int global_vel_sp_sub;
int battery_sub;
- int telemetry_sub;
+ int telemetry_subs[TELEMETRY_STATUS_ORB_ID_NUM];
int range_finder_sub;
int estimator_status_sub;
+ int tecs_status_sub;
int system_power_sub;
int servorail_status_sub;
+ int wind_sub;
+ int encoders_sub;
} subs;
subs.cmd_sub = orb_subscribe(ORB_ID(vehicle_command));
@@ -1028,17 +1053,40 @@ int sdlog2_thread_main(int argc, char *argv[])
subs.global_pos_sub = orb_subscribe(ORB_ID(vehicle_global_position));
subs.triplet_sub = orb_subscribe(ORB_ID(position_setpoint_triplet));
subs.vicon_pos_sub = orb_subscribe(ORB_ID(vehicle_vicon_position));
+ subs.vision_pos_sub = orb_subscribe(ORB_ID(vision_position_estimate));
subs.flow_sub = orb_subscribe(ORB_ID(optical_flow));
subs.rc_sub = orb_subscribe(ORB_ID(rc_channels));
subs.airspeed_sub = orb_subscribe(ORB_ID(airspeed));
subs.esc_sub = orb_subscribe(ORB_ID(esc_status));
subs.global_vel_sp_sub = orb_subscribe(ORB_ID(vehicle_global_velocity_setpoint));
subs.battery_sub = orb_subscribe(ORB_ID(battery_status));
- subs.telemetry_sub = orb_subscribe(ORB_ID(telemetry_status));
subs.range_finder_sub = orb_subscribe(ORB_ID(sensor_range_finder));
subs.estimator_status_sub = orb_subscribe(ORB_ID(estimator_status));
+ subs.tecs_status_sub = orb_subscribe(ORB_ID(tecs_status));
subs.system_power_sub = orb_subscribe(ORB_ID(system_power));
subs.servorail_status_sub = orb_subscribe(ORB_ID(servorail_status));
+ subs.wind_sub = orb_subscribe(ORB_ID(wind_estimate));
+ /* we need to rate-limit wind, as we do not need the full update rate */
+ orb_set_interval(subs.wind_sub, 90);
+ subs.encoders_sub = orb_subscribe(ORB_ID(encoders));
+
+ /* add new topics HERE */
+
+
+ for (int i = 0; i < TELEMETRY_STATUS_ORB_ID_NUM; i++) {
+ subs.telemetry_subs[i] = orb_subscribe(telemetry_status_orb_id[i]);
+ }
+
+ if (_extended_logging) {
+ subs.sat_info_sub = orb_subscribe(ORB_ID(satellite_info));
+ }
+
+ /* close non-needed fd's */
+
+ /* close stdin */
+ close(0);
+ /* close stdout */
+ close(1);
thread_running = true;
@@ -1052,12 +1100,21 @@ int sdlog2_thread_main(int argc, char *argv[])
hrt_abstime magnetometer_timestamp = 0;
hrt_abstime barometer_timestamp = 0;
hrt_abstime differential_pressure_timestamp = 0;
+ hrt_abstime gyro1_timestamp = 0;
+ hrt_abstime accelerometer1_timestamp = 0;
+ hrt_abstime magnetometer1_timestamp = 0;
+ hrt_abstime gyro2_timestamp = 0;
+ hrt_abstime accelerometer2_timestamp = 0;
+ hrt_abstime magnetometer2_timestamp = 0;
+
+ /* initialize calculated mean SNR */
+ float snr_mean = 0.0f;
/* enable logging on start if needed */
if (log_on_start) {
/* check GPS topic to get GPS time */
if (log_name_timestamp) {
- if (copy_if_updated(ORB_ID(vehicle_gps_position), subs.gps_pos_sub, &buf_gps_pos)) {
+ if (!orb_copy(ORB_ID(vehicle_gps_position), subs.gps_pos_sub, &buf_gps_pos)) {
gps_time = buf_gps_pos.time_gps_usec;
}
}
@@ -1105,7 +1162,7 @@ int sdlog2_thread_main(int argc, char *argv[])
log_msg.msg_type = LOG_STAT_MSG;
log_msg.body.log_STAT.main_state = (uint8_t) buf_status.main_state;
log_msg.body.log_STAT.arming_state = (uint8_t) buf_status.arming_state;
- log_msg.body.log_STAT.failsafe_state = (uint8_t) buf_status.failsafe_state;
+ log_msg.body.log_STAT.failsafe_state = (uint8_t) buf_status.failsafe;
log_msg.body.log_STAT.battery_remaining = buf_status.battery_remaining;
log_msg.body.log_STAT.battery_warning = (uint8_t) buf_status.battery_warning;
log_msg.body.log_STAT.landed = (uint8_t) buf_status.condition_landed;
@@ -1115,19 +1172,11 @@ int sdlog2_thread_main(int argc, char *argv[])
/* --- GPS POSITION - UNIT #1 --- */
if (gps_pos_updated) {
- float snr_mean = 0.0f;
-
- for (unsigned i = 0; i < buf_gps_pos.satellites_visible; i++) {
- snr_mean += buf_gps_pos.satellite_snr[i];
- }
-
- snr_mean /= buf_gps_pos.satellites_visible;
-
log_msg.msg_type = LOG_GPS_MSG;
log_msg.body.log_GPS.gps_time = buf_gps_pos.time_gps_usec;
log_msg.body.log_GPS.fix_type = buf_gps_pos.fix_type;
- log_msg.body.log_GPS.eph = buf_gps_pos.eph_m;
- log_msg.body.log_GPS.epv = buf_gps_pos.epv_m;
+ log_msg.body.log_GPS.eph = buf_gps_pos.eph;
+ log_msg.body.log_GPS.epv = buf_gps_pos.epv;
log_msg.body.log_GPS.lat = buf_gps_pos.lat;
log_msg.body.log_GPS.lon = buf_gps_pos.lon;
log_msg.body.log_GPS.alt = buf_gps_pos.alt * 0.001f;
@@ -1135,44 +1184,55 @@ int sdlog2_thread_main(int argc, char *argv[])
log_msg.body.log_GPS.vel_e = buf_gps_pos.vel_e_m_s;
log_msg.body.log_GPS.vel_d = buf_gps_pos.vel_d_m_s;
log_msg.body.log_GPS.cog = buf_gps_pos.cog_rad;
- log_msg.body.log_GPS.sats = buf_gps_pos.satellites_visible;
+ log_msg.body.log_GPS.sats = buf_gps_pos.satellites_used;
log_msg.body.log_GPS.snr_mean = snr_mean;
log_msg.body.log_GPS.noise_per_ms = buf_gps_pos.noise_per_ms;
log_msg.body.log_GPS.jamming_indicator = buf_gps_pos.jamming_indicator;
LOGBUFFER_WRITE_AND_COUNT(GPS);
+ }
+
+ /* --- SATELLITE INFO - UNIT #1 --- */
+ if (_extended_logging) {
+
+ if (copy_if_updated(ORB_ID(satellite_info), subs.sat_info_sub, &buf.sat_info)) {
- if (_extended_logging) {
/* log the SNR of each satellite for a detailed view of signal quality */
- unsigned gps_msg_max_snr = sizeof(buf_gps_pos.satellite_snr) / sizeof(buf_gps_pos.satellite_snr[0]);
+ unsigned sat_info_count = MIN(buf.sat_info.count, sizeof(buf.sat_info.snr) / sizeof(buf.sat_info.snr[0]));
unsigned log_max_snr = sizeof(log_msg.body.log_GS0A.satellite_snr) / sizeof(log_msg.body.log_GS0A.satellite_snr[0]);
log_msg.msg_type = LOG_GS0A_MSG;
memset(&log_msg.body.log_GS0A, 0, sizeof(log_msg.body.log_GS0A));
- /* fill set A */
- for (unsigned i = 0; i < gps_msg_max_snr; i++) {
+ snr_mean = 0.0f;
+
+ /* fill set A and calculate mean SNR */
+ for (unsigned i = 0; i < sat_info_count; i++) {
+
+ snr_mean += buf.sat_info.snr[i];
- int satindex = buf_gps_pos.satellite_prn[i] - 1;
+ int satindex = buf.sat_info.svid[i] - 1;
/* handles index exceeding and wraps to to arithmetic errors */
if ((satindex >= 0) && (satindex < (int)log_max_snr)) {
/* map satellites by their ID so that logs from two receivers can be compared */
- log_msg.body.log_GS0A.satellite_snr[satindex] = buf_gps_pos.satellite_snr[i];
+ log_msg.body.log_GS0A.satellite_snr[satindex] = buf.sat_info.snr[i];
}
}
LOGBUFFER_WRITE_AND_COUNT(GS0A);
+ snr_mean /= sat_info_count;
log_msg.msg_type = LOG_GS0B_MSG;
memset(&log_msg.body.log_GS0B, 0, sizeof(log_msg.body.log_GS0B));
+
/* fill set B */
- for (unsigned i = 0; i < gps_msg_max_snr; i++) {
+ for (unsigned i = 0; i < sat_info_count; i++) {
/* get second bank of satellites, thus deduct bank size from index */
- int satindex = buf_gps_pos.satellite_prn[i] - 1 - log_max_snr;
+ int satindex = buf.sat_info.svid[i] - 1 - log_max_snr;
/* handles index exceeding and wraps to to arithmetic errors */
if ((satindex >= 0) && (satindex < (int)log_max_snr)) {
/* map satellites by their ID so that logs from two receivers can be compared */
- log_msg.body.log_GS0B.satellite_snr[satindex] = buf_gps_pos.satellite_snr[i];
+ log_msg.body.log_GS0B.satellite_snr[satindex] = buf.sat_info.snr[i];
}
}
LOGBUFFER_WRITE_AND_COUNT(GS0B);
@@ -1182,6 +1242,8 @@ int sdlog2_thread_main(int argc, char *argv[])
/* --- SENSOR COMBINED --- */
if (copy_if_updated(ORB_ID(sensor_combined), subs.sensor_sub, &buf.sensor)) {
bool write_IMU = false;
+ bool write_IMU1 = false;
+ bool write_IMU2 = false;
bool write_SENS = false;
if (buf.sensor.timestamp != gyro_timestamp) {
@@ -1233,6 +1295,64 @@ int sdlog2_thread_main(int argc, char *argv[])
LOGBUFFER_WRITE_AND_COUNT(SENS);
}
+ if (buf.sensor.accelerometer1_timestamp != accelerometer1_timestamp) {
+ accelerometer1_timestamp = buf.sensor.accelerometer1_timestamp;
+ write_IMU1 = true;
+ }
+
+ if (buf.sensor.gyro1_timestamp != gyro1_timestamp) {
+ gyro1_timestamp = buf.sensor.gyro1_timestamp;
+ write_IMU1 = true;
+ }
+
+ if (buf.sensor.magnetometer1_timestamp != magnetometer1_timestamp) {
+ magnetometer1_timestamp = buf.sensor.magnetometer1_timestamp;
+ write_IMU1 = true;
+ }
+
+ if (write_IMU1) {
+ log_msg.msg_type = LOG_IMU1_MSG;
+ log_msg.body.log_IMU.gyro_x = buf.sensor.gyro1_rad_s[0];
+ log_msg.body.log_IMU.gyro_y = buf.sensor.gyro1_rad_s[1];
+ log_msg.body.log_IMU.gyro_z = buf.sensor.gyro1_rad_s[2];
+ log_msg.body.log_IMU.acc_x = buf.sensor.accelerometer1_m_s2[0];
+ log_msg.body.log_IMU.acc_y = buf.sensor.accelerometer1_m_s2[1];
+ log_msg.body.log_IMU.acc_z = buf.sensor.accelerometer1_m_s2[2];
+ log_msg.body.log_IMU.mag_x = buf.sensor.magnetometer1_ga[0];
+ log_msg.body.log_IMU.mag_y = buf.sensor.magnetometer1_ga[1];
+ log_msg.body.log_IMU.mag_z = buf.sensor.magnetometer1_ga[2];
+ LOGBUFFER_WRITE_AND_COUNT(IMU);
+ }
+
+ if (buf.sensor.accelerometer2_timestamp != accelerometer2_timestamp) {
+ accelerometer2_timestamp = buf.sensor.accelerometer2_timestamp;
+ write_IMU2 = true;
+ }
+
+ if (buf.sensor.gyro2_timestamp != gyro2_timestamp) {
+ gyro2_timestamp = buf.sensor.gyro2_timestamp;
+ write_IMU2 = true;
+ }
+
+ if (buf.sensor.magnetometer2_timestamp != magnetometer2_timestamp) {
+ magnetometer2_timestamp = buf.sensor.magnetometer2_timestamp;
+ write_IMU2 = true;
+ }
+
+ if (write_IMU2) {
+ log_msg.msg_type = LOG_IMU2_MSG;
+ log_msg.body.log_IMU.gyro_x = buf.sensor.gyro2_rad_s[0];
+ log_msg.body.log_IMU.gyro_y = buf.sensor.gyro2_rad_s[1];
+ log_msg.body.log_IMU.gyro_z = buf.sensor.gyro2_rad_s[2];
+ log_msg.body.log_IMU.acc_x = buf.sensor.accelerometer2_m_s2[0];
+ log_msg.body.log_IMU.acc_y = buf.sensor.accelerometer2_m_s2[1];
+ log_msg.body.log_IMU.acc_z = buf.sensor.accelerometer2_m_s2[2];
+ log_msg.body.log_IMU.mag_x = buf.sensor.magnetometer2_ga[0];
+ log_msg.body.log_IMU.mag_y = buf.sensor.magnetometer2_ga[1];
+ log_msg.body.log_IMU.mag_z = buf.sensor.magnetometer2_ga[2];
+ LOGBUFFER_WRITE_AND_COUNT(IMU);
+ }
+
}
/* --- ATTITUDE --- */
@@ -1334,22 +1454,30 @@ int sdlog2_thread_main(int argc, char *argv[])
log_msg.body.log_GPOS.vel_d = buf.global_pos.vel_d;
log_msg.body.log_GPOS.eph = buf.global_pos.eph;
log_msg.body.log_GPOS.epv = buf.global_pos.epv;
+ if (buf.global_pos.terrain_alt_valid) {
+ log_msg.body.log_GPOS.terrain_alt = buf.global_pos.terrain_alt;
+ } else {
+ log_msg.body.log_GPOS.terrain_alt = -1.0f;
+ }
LOGBUFFER_WRITE_AND_COUNT(GPOS);
}
/* --- GLOBAL POSITION SETPOINT --- */
if (copy_if_updated(ORB_ID(position_setpoint_triplet), subs.triplet_sub, &buf.triplet)) {
- log_msg.msg_type = LOG_GPSP_MSG;
- log_msg.body.log_GPSP.nav_state = buf.triplet.nav_state;
- log_msg.body.log_GPSP.lat = (int32_t)(buf.triplet.current.lat * 1e7d);
- log_msg.body.log_GPSP.lon = (int32_t)(buf.triplet.current.lon * 1e7d);
- log_msg.body.log_GPSP.alt = buf.triplet.current.alt;
- log_msg.body.log_GPSP.yaw = buf.triplet.current.yaw;
- log_msg.body.log_GPSP.type = buf.triplet.current.type;
- log_msg.body.log_GPSP.loiter_radius = buf.triplet.current.loiter_radius;
- log_msg.body.log_GPSP.loiter_direction = buf.triplet.current.loiter_direction;
- log_msg.body.log_GPSP.pitch_min = buf.triplet.current.pitch_min;
- LOGBUFFER_WRITE_AND_COUNT(GPSP);
+
+ if (buf.triplet.current.valid) {
+ log_msg.msg_type = LOG_GPSP_MSG;
+ log_msg.body.log_GPSP.nav_state = buf.triplet.nav_state;
+ log_msg.body.log_GPSP.lat = (int32_t)(buf.triplet.current.lat * 1e7d);
+ log_msg.body.log_GPSP.lon = (int32_t)(buf.triplet.current.lon * 1e7d);
+ log_msg.body.log_GPSP.alt = buf.triplet.current.alt;
+ log_msg.body.log_GPSP.yaw = buf.triplet.current.yaw;
+ log_msg.body.log_GPSP.type = buf.triplet.current.type;
+ log_msg.body.log_GPSP.loiter_radius = buf.triplet.current.loiter_radius;
+ log_msg.body.log_GPSP.loiter_direction = buf.triplet.current.loiter_direction;
+ log_msg.body.log_GPSP.pitch_min = buf.triplet.current.pitch_min;
+ LOGBUFFER_WRITE_AND_COUNT(GPSP);
+ }
}
/* --- VICON POSITION --- */
@@ -1364,6 +1492,22 @@ int sdlog2_thread_main(int argc, char *argv[])
LOGBUFFER_WRITE_AND_COUNT(VICN);
}
+ /* --- VISION POSITION --- */
+ if (copy_if_updated(ORB_ID(vision_position_estimate), subs.vision_pos_sub, &buf.vision_pos)) {
+ log_msg.msg_type = LOG_VISN_MSG;
+ log_msg.body.log_VISN.x = buf.vision_pos.x;
+ log_msg.body.log_VISN.y = buf.vision_pos.y;
+ log_msg.body.log_VISN.z = buf.vision_pos.z;
+ log_msg.body.log_VISN.vx = buf.vision_pos.vx;
+ log_msg.body.log_VISN.vy = buf.vision_pos.vy;
+ log_msg.body.log_VISN.vz = buf.vision_pos.vz;
+ log_msg.body.log_VISN.qx = buf.vision_pos.q[0];
+ log_msg.body.log_VISN.qy = buf.vision_pos.q[1];
+ log_msg.body.log_VISN.qz = buf.vision_pos.q[2];
+ log_msg.body.log_VISN.qw = buf.vision_pos.q[3];
+ LOGBUFFER_WRITE_AND_COUNT(VISN);
+ }
+
/* --- FLOW --- */
if (copy_if_updated(ORB_ID(optical_flow), subs.flow_sub, &buf.flow)) {
log_msg.msg_type = LOG_FLOW_MSG;
@@ -1381,8 +1525,8 @@ int sdlog2_thread_main(int argc, char *argv[])
if (copy_if_updated(ORB_ID(rc_channels), subs.rc_sub, &buf.rc)) {
log_msg.msg_type = LOG_RC_MSG;
/* Copy only the first 8 channels of 14 */
- memcpy(log_msg.body.log_RC.channel, buf.rc.chan, sizeof(log_msg.body.log_RC.channel));
- log_msg.body.log_RC.channel_count = buf.rc.chan_count;
+ memcpy(log_msg.body.log_RC.channel, buf.rc.channels, sizeof(log_msg.body.log_RC.channel));
+ log_msg.body.log_RC.channel_count = buf.rc.channel_count;
log_msg.body.log_RC.signal_lost = buf.rc.signal_lost;
LOGBUFFER_WRITE_AND_COUNT(RC);
}
@@ -1454,16 +1598,19 @@ int sdlog2_thread_main(int argc, char *argv[])
}
/* --- TELEMETRY --- */
- if (copy_if_updated(ORB_ID(telemetry_status), subs.telemetry_sub, &buf.telemetry)) {
- log_msg.msg_type = LOG_TELE_MSG;
- log_msg.body.log_TELE.rssi = buf.telemetry.rssi;
- log_msg.body.log_TELE.remote_rssi = buf.telemetry.remote_rssi;
- log_msg.body.log_TELE.noise = buf.telemetry.noise;
- log_msg.body.log_TELE.remote_noise = buf.telemetry.remote_noise;
- log_msg.body.log_TELE.rxerrors = buf.telemetry.rxerrors;
- log_msg.body.log_TELE.fixed = buf.telemetry.fixed;
- log_msg.body.log_TELE.txbuf = buf.telemetry.txbuf;
- LOGBUFFER_WRITE_AND_COUNT(TELE);
+ for (int i = 0; i < TELEMETRY_STATUS_ORB_ID_NUM; i++) {
+ if (copy_if_updated(telemetry_status_orb_id[i], subs.telemetry_subs[i], &buf.telemetry)) {
+ log_msg.msg_type = LOG_TEL0_MSG + i;
+ log_msg.body.log_TEL.rssi = buf.telemetry.rssi;
+ log_msg.body.log_TEL.remote_rssi = buf.telemetry.remote_rssi;
+ log_msg.body.log_TEL.noise = buf.telemetry.noise;
+ log_msg.body.log_TEL.remote_noise = buf.telemetry.remote_noise;
+ log_msg.body.log_TEL.rxerrors = buf.telemetry.rxerrors;
+ log_msg.body.log_TEL.fixed = buf.telemetry.fixed;
+ log_msg.body.log_TEL.txbuf = buf.telemetry.txbuf;
+ log_msg.body.log_TEL.heartbeat_time = buf.telemetry.heartbeat_time;
+ LOGBUFFER_WRITE_AND_COUNT(TEL);
+ }
}
/* --- BOTTOM DISTANCE --- */
@@ -1477,15 +1624,61 @@ int sdlog2_thread_main(int argc, char *argv[])
/* --- ESTIMATOR STATUS --- */
if (copy_if_updated(ORB_ID(estimator_status), subs.estimator_status_sub, &buf.estimator_status)) {
- log_msg.msg_type = LOG_ESTM_MSG;
- unsigned maxcopy = (sizeof(buf.estimator_status.states) < sizeof(log_msg.body.log_ESTM.s)) ? sizeof(buf.estimator_status.states) : sizeof(log_msg.body.log_ESTM.s);
- memset(&(log_msg.body.log_ESTM.s), 0, sizeof(log_msg.body.log_ESTM.s));
- memcpy(&(log_msg.body.log_ESTM.s), buf.estimator_status.states, maxcopy);
- log_msg.body.log_ESTM.n_states = buf.estimator_status.n_states;
- log_msg.body.log_ESTM.states_nan = buf.estimator_status.states_nan;
- log_msg.body.log_ESTM.covariance_nan = buf.estimator_status.covariance_nan;
- log_msg.body.log_ESTM.kalman_gain_nan = buf.estimator_status.kalman_gain_nan;
- LOGBUFFER_WRITE_AND_COUNT(ESTM);
+ log_msg.msg_type = LOG_EST0_MSG;
+ unsigned maxcopy0 = (sizeof(buf.estimator_status.states) < sizeof(log_msg.body.log_EST0.s)) ? sizeof(buf.estimator_status.states) : sizeof(log_msg.body.log_EST0.s);
+ memset(&(log_msg.body.log_EST0.s), 0, sizeof(log_msg.body.log_EST0.s));
+ memcpy(&(log_msg.body.log_EST0.s), buf.estimator_status.states, maxcopy0);
+ log_msg.body.log_EST0.n_states = buf.estimator_status.n_states;
+ log_msg.body.log_EST0.nan_flags = buf.estimator_status.nan_flags;
+ log_msg.body.log_EST0.health_flags = buf.estimator_status.health_flags;
+ log_msg.body.log_EST0.timeout_flags = buf.estimator_status.timeout_flags;
+ LOGBUFFER_WRITE_AND_COUNT(EST0);
+
+ log_msg.msg_type = LOG_EST1_MSG;
+ unsigned maxcopy1 = ((sizeof(buf.estimator_status.states) - maxcopy0) < sizeof(log_msg.body.log_EST1.s)) ? (sizeof(buf.estimator_status.states) - maxcopy0) : sizeof(log_msg.body.log_EST1.s);
+ memset(&(log_msg.body.log_EST1.s), 0, sizeof(log_msg.body.log_EST1.s));
+ memcpy(&(log_msg.body.log_EST1.s), buf.estimator_status.states + maxcopy0, maxcopy1);
+ LOGBUFFER_WRITE_AND_COUNT(EST1);
+ }
+
+ /* --- TECS STATUS --- */
+ if (copy_if_updated(ORB_ID(tecs_status), subs.tecs_status_sub, &buf.tecs_status)) {
+ log_msg.msg_type = LOG_TECS_MSG;
+ log_msg.body.log_TECS.altitudeSp = buf.tecs_status.altitudeSp;
+ log_msg.body.log_TECS.altitudeFiltered = buf.tecs_status.altitude_filtered;
+ log_msg.body.log_TECS.flightPathAngleSp = buf.tecs_status.flightPathAngleSp;
+ log_msg.body.log_TECS.flightPathAngle = buf.tecs_status.flightPathAngle;
+ log_msg.body.log_TECS.flightPathAngleFiltered = buf.tecs_status.flightPathAngleFiltered;
+ log_msg.body.log_TECS.airspeedSp = buf.tecs_status.airspeedSp;
+ log_msg.body.log_TECS.airspeedFiltered = buf.tecs_status.airspeed_filtered;
+ log_msg.body.log_TECS.airspeedDerivativeSp = buf.tecs_status.airspeedDerivativeSp;
+ log_msg.body.log_TECS.airspeedDerivative = buf.tecs_status.airspeedDerivative;
+ log_msg.body.log_TECS.totalEnergyRateSp = buf.tecs_status.totalEnergyRateSp;
+ log_msg.body.log_TECS.totalEnergyRate = buf.tecs_status.totalEnergyRate;
+ log_msg.body.log_TECS.energyDistributionRateSp = buf.tecs_status.energyDistributionRateSp;
+ log_msg.body.log_TECS.energyDistributionRate = buf.tecs_status.energyDistributionRate;
+ log_msg.body.log_TECS.mode = (uint8_t)buf.tecs_status.mode;
+ LOGBUFFER_WRITE_AND_COUNT(TECS);
+ }
+
+ /* --- WIND ESTIMATE --- */
+ if (copy_if_updated(ORB_ID(wind_estimate), subs.wind_sub, &buf.wind_estimate)) {
+ log_msg.msg_type = LOG_WIND_MSG;
+ log_msg.body.log_WIND.x = buf.wind_estimate.windspeed_north;
+ log_msg.body.log_WIND.y = buf.wind_estimate.windspeed_east;
+ log_msg.body.log_WIND.cov_x = buf.wind_estimate.covariance_north;
+ log_msg.body.log_WIND.cov_y = buf.wind_estimate.covariance_east;
+ LOGBUFFER_WRITE_AND_COUNT(WIND);
+ }
+
+ /* --- ENCODERS --- */
+ if (copy_if_updated(ORB_ID(encoders), subs.encoders_sub, &buf.encoders)) {
+ log_msg.msg_type = LOG_ENCD_MSG;
+ log_msg.body.log_ENCD.cnt0 = buf.encoders.counts[0];
+ log_msg.body.log_ENCD.vel0 = buf.encoders.velocity[0];
+ log_msg.body.log_ENCD.cnt1 = buf.encoders.counts[1];
+ log_msg.body.log_ENCD.vel1 = buf.encoders.velocity[1];
+ LOGBUFFER_WRITE_AND_COUNT(ENCD);
}
/* signal the other thread new data, but not yet unlock */
diff --git a/src/modules/sdlog2/sdlog2_format.h b/src/modules/sdlog2/sdlog2_format.h
index dc5e6c8bd..aff0e3f48 100644
--- a/src/modules/sdlog2/sdlog2_format.h
+++ b/src/modules/sdlog2/sdlog2_format.h
@@ -91,6 +91,14 @@ struct log_format_s {
.labels = _labels \
}
+#define LOG_FORMAT_S(_name, _struct_name, _format, _labels) { \
+ .type = LOG_##_name##_MSG, \
+ .length = sizeof(struct log_##_struct_name##_s) + LOG_PACKET_HEADER_LEN, \
+ .name = #_name, \
+ .format = _format, \
+ .labels = _labels \
+ }
+
#define LOG_FORMAT_MSG 0x80
#define LOG_PACKET_SIZE(_name) LOG_PACKET_HEADER_LEN + sizeof(struct log_##_name##_s)
diff --git a/src/modules/sdlog2/sdlog2_messages.h b/src/modules/sdlog2/sdlog2_messages.h
index f4d88f079..fa9bdacb8 100644
--- a/src/modules/sdlog2/sdlog2_messages.h
+++ b/src/modules/sdlog2/sdlog2_messages.h
@@ -73,6 +73,8 @@ struct log_ATSP_s {
/* --- IMU - IMU SENSORS --- */
#define LOG_IMU_MSG 4
+#define LOG_IMU1_MSG 22
+#define LOG_IMU2_MSG 23
struct log_IMU_s {
float acc_x;
float acc_y;
@@ -218,6 +220,7 @@ struct log_GPOS_s {
float vel_d;
float eph;
float epv;
+ float terrain_alt;
};
/* --- GPSP - GLOBAL POSITION SETPOINT --- */
@@ -238,15 +241,15 @@ struct log_GPSP_s {
#define LOG_ESC_MSG 18
struct log_ESC_s {
uint16_t counter;
- uint8_t esc_count;
- uint8_t esc_connectiontype;
- uint8_t esc_num;
+ uint8_t esc_count;
+ uint8_t esc_connectiontype;
+ uint8_t esc_num;
uint16_t esc_address;
uint16_t esc_version;
- uint16_t esc_voltage;
- uint16_t esc_current;
- uint16_t esc_rpm;
- uint16_t esc_temperature;
+ float esc_voltage;
+ float esc_current;
+ int32_t esc_rpm;
+ float esc_temperature;
float esc_setpoint;
uint16_t esc_setpoint_raw;
};
@@ -276,27 +279,8 @@ struct log_DIST_s {
uint8_t flags;
};
-/* --- TELE - TELEMETRY STATUS --- */
-#define LOG_TELE_MSG 22
-struct log_TELE_s {
- uint8_t rssi;
- uint8_t remote_rssi;
- uint8_t noise;
- uint8_t remote_noise;
- uint16_t rxerrors;
- uint16_t fixed;
- uint8_t txbuf;
-};
+/* LOG IMU1 and IMU2 MSGs consume IDs 22 and 23 */
-/* --- ESTM - ESTIMATOR STATUS --- */
-#define LOG_ESTM_MSG 23
-struct log_ESTM_s {
- float s[10];
- uint8_t n_states;
- uint8_t states_nan;
- uint8_t covariance_nan;
- uint8_t kalman_gain_nan;
-};
/* --- PWR - ONBOARD POWER SYSTEM --- */
#define LOG_PWR_MSG 24
@@ -346,6 +330,93 @@ struct log_GS1B_s {
uint8_t satellite_snr[16]; /**< dBHz, Signal to noise ratio of satellite C/N0, range 0..99 */
};
+/* --- TECS - TECS STATUS --- */
+#define LOG_TECS_MSG 30
+struct log_TECS_s {
+ float altitudeSp;
+ float altitudeFiltered;
+ float flightPathAngleSp;
+ float flightPathAngle;
+ float flightPathAngleFiltered;
+ float airspeedSp;
+ float airspeedFiltered;
+ float airspeedDerivativeSp;
+ float airspeedDerivative;
+
+ float totalEnergyRateSp;
+ float totalEnergyRate;
+ float energyDistributionRateSp;
+ float energyDistributionRate;
+
+ uint8_t mode;
+};
+
+/* --- WIND - WIND ESTIMATE --- */
+#define LOG_WIND_MSG 31
+struct log_WIND_s {
+ float x;
+ float y;
+ float cov_x;
+ float cov_y;
+};
+
+/* --- EST0 - ESTIMATOR STATUS --- */
+#define LOG_EST0_MSG 32
+struct log_EST0_s {
+ float s[12];
+ uint8_t n_states;
+ uint8_t nan_flags;
+ uint8_t health_flags;
+ uint8_t timeout_flags;
+};
+
+/* --- EST1 - ESTIMATOR STATUS --- */
+#define LOG_EST1_MSG 33
+struct log_EST1_s {
+ float s[16];
+};
+
+/* --- TEL0..3 - TELEMETRY STATUS --- */
+#define LOG_TEL0_MSG 34
+#define LOG_TEL1_MSG 35
+#define LOG_TEL2_MSG 36
+#define LOG_TEL3_MSG 37
+struct log_TEL_s {
+ uint8_t rssi;
+ uint8_t remote_rssi;
+ uint8_t noise;
+ uint8_t remote_noise;
+ uint16_t rxerrors;
+ uint16_t fixed;
+ uint8_t txbuf;
+ uint64_t heartbeat_time;
+};
+
+/* --- VISN - VISION POSITION --- */
+#define LOG_VISN_MSG 38
+struct log_VISN_s {
+ float x;
+ float y;
+ float z;
+ float vx;
+ float vy;
+ float vz;
+ float qx;
+ float qy;
+ float qz;
+ float qw;
+};
+
+/* --- ENCODERS - ENCODER DATA --- */
+#define LOG_ENCD_MSG 39
+struct log_ENCD_s {
+ int64_t cnt0;
+ float vel0;
+ int64_t cnt1;
+ float vel1;
+};
+
+
/********** SYSTEM MESSAGES, ID > 0x80 **********/
/* --- TIME - TIME STAMP --- */
@@ -375,7 +446,9 @@ static const struct log_format_s log_formats[] = {
/* business-level messages, ID < 0x80 */
LOG_FORMAT(ATT, "fffffffff", "Roll,Pitch,Yaw,RollRate,PitchRate,YawRate,GX,GY,GZ"),
LOG_FORMAT(ATSP, "ffff", "RollSP,PitchSP,YawSP,ThrustSP"),
- LOG_FORMAT(IMU, "fffffffff", "AccX,AccY,AccZ,GyroX,GyroY,GyroZ,MagX,MagY,MagZ"),
+ LOG_FORMAT_S(IMU, IMU, "fffffffff", "AccX,AccY,AccZ,GyroX,GyroY,GyroZ,MagX,MagY,MagZ"),
+ LOG_FORMAT_S(IMU1, IMU, "fffffffff", "AccX,AccY,AccZ,GyroX,GyroY,GyroZ,MagX,MagY,MagZ"),
+ LOG_FORMAT_S(IMU2, IMU, "fffffffff", "AccX,AccY,AccZ,GyroX,GyroY,GyroZ,MagX,MagY,MagZ"),
LOG_FORMAT(SENS, "fffff", "BaroPres,BaroAlt,BaroTemp,DiffPres,DiffPresFilt"),
LOG_FORMAT(LPOS, "ffffffffLLfBBBff", "X,Y,Z,Dist,DistR,VX,VY,VZ,RLat,RLon,RAlt,PFlg,LFlg,GFlg,EPH,EPV"),
LOG_FORMAT(LPSP, "ffff", "X,Y,Z,Yaw"),
@@ -387,20 +460,28 @@ static const struct log_format_s log_formats[] = {
LOG_FORMAT(AIRS, "fff", "IndSpeed,TrueSpeed,AirTemp"),
LOG_FORMAT(ARSP, "fff", "RollRateSP,PitchRateSP,YawRateSP"),
LOG_FORMAT(FLOW, "hhfffBB", "RawX,RawY,CompX,CompY,Dist,Q,SensID"),
- LOG_FORMAT(GPOS, "LLffffff", "Lat,Lon,Alt,VelN,VelE,VelD,EPH,EPV"),
+ LOG_FORMAT(GPOS, "LLfffffff", "Lat,Lon,Alt,VelN,VelE,VelD,EPH,EPV,TALT"),
LOG_FORMAT(GPSP, "BLLffBfbf", "NavState,Lat,Lon,Alt,Yaw,Type,LoitR,LoitDir,PitMin"),
- LOG_FORMAT(ESC, "HBBBHHHHHHfH", "count,nESC,Conn,N,Ver,Adr,Volt,Amp,RPM,Temp,SetP,SetPRAW"),
+ LOG_FORMAT(ESC, "HBBBHHffiffH", "count,nESC,Conn,N,Ver,Adr,Volt,Amp,RPM,Temp,SetP,SetPRAW"),
LOG_FORMAT(GVSP, "fff", "VX,VY,VZ"),
LOG_FORMAT(BATT, "ffff", "V,VFilt,C,Discharged"),
LOG_FORMAT(DIST, "ffB", "Bottom,BottomRate,Flags"),
- LOG_FORMAT(TELE, "BBBBHHB", "RSSI,RemRSSI,Noise,RemNoise,RXErr,Fixed,TXBuf"),
- LOG_FORMAT(ESTM, "ffffffffffBBBB", "s0,s1,s2,s3,s4,s5,s6,s7,s8,s9,nStat,statNaN,covNaN,kGainNaN"),
+ LOG_FORMAT_S(TEL0, TEL, "BBBBHHBQ", "RSSI,RemRSSI,Noise,RemNoise,RXErr,Fixed,TXBuf,HbTime"),
+ LOG_FORMAT_S(TEL1, TEL, "BBBBHHBQ", "RSSI,RemRSSI,Noise,RemNoise,RXErr,Fixed,TXBuf,HbTime"),
+ LOG_FORMAT_S(TEL2, TEL, "BBBBHHBQ", "RSSI,RemRSSI,Noise,RemNoise,RXErr,Fixed,TXBuf,HbTime"),
+ LOG_FORMAT_S(TEL3, TEL, "BBBBHHBQ", "RSSI,RemRSSI,Noise,RemNoise,RXErr,Fixed,TXBuf,HbTime"),
+ LOG_FORMAT(EST0, "ffffffffffffBBBB", "s0,s1,s2,s3,s4,s5,s6,s7,s8,s9,s10,s11,nStat,fNaN,fHealth,fTOut"),
+ LOG_FORMAT(EST1, "ffffffffffffffff", "s12,s13,s14,s15,s16,s17,s18,s19,s20,s21,s22,s23,s24,s25,s26,s27"),
LOG_FORMAT(PWR, "fffBBBBB", "Periph5V,Servo5V,RSSI,UsbOk,BrickOk,ServoOk,PeriphOC,HipwrOC"),
LOG_FORMAT(VICN, "ffffff", "X,Y,Z,Roll,Pitch,Yaw"),
+ LOG_FORMAT(VISN, "ffffffffff", "X,Y,Z,VX,VY,VZ,QuatX,QuatY,QuatZ,QuatW"),
LOG_FORMAT(GS0A, "BBBBBBBBBBBBBBBB", "s0,s1,s2,s3,s4,s5,s6,s7,s8,s9,s10,s11,s12,s13,s14,s15"),
LOG_FORMAT(GS0B, "BBBBBBBBBBBBBBBB", "s0,s1,s2,s3,s4,s5,s6,s7,s8,s9,s10,s11,s12,s13,s14,s15"),
LOG_FORMAT(GS1A, "BBBBBBBBBBBBBBBB", "s0,s1,s2,s3,s4,s5,s6,s7,s8,s9,s10,s11,s12,s13,s14,s15"),
LOG_FORMAT(GS1B, "BBBBBBBBBBBBBBBB", "s0,s1,s2,s3,s4,s5,s6,s7,s8,s9,s10,s11,s12,s13,s14,s15"),
+ LOG_FORMAT(TECS, "fffffffffffffB", "ASP,AF,FSP,F,FF,AsSP,AsF,AsDSP,AsD,TERSP,TER,EDRSP,EDR,M"),
+ LOG_FORMAT(WIND, "ffff", "X,Y,CovX,CovY"),
+ LOG_FORMAT(ENCD, "qfqf", "cnt0,vel0,cnt1,vel1"),
/* system-level messages, ID >= 0x80 */
/* FMT: don't write format of format message, it's useless */
diff --git a/src/modules/sensors/module.mk b/src/modules/sensors/module.mk
index 5b1bc5e86..dfbba92d9 100644
--- a/src/modules/sensors/module.mk
+++ b/src/modules/sensors/module.mk
@@ -42,3 +42,5 @@ SRCS = sensors.cpp \
sensor_params.c
MODULE_STACKSIZE = 1200
+
+MAXOPTIMIZATION = -Os
diff --git a/src/modules/sensors/sensor_params.c b/src/modules/sensors/sensor_params.c
index 999cf8bb3..229bfe3ce 100644
--- a/src/modules/sensors/sensor_params.c
+++ b/src/modules/sensors/sensor_params.c
@@ -194,16 +194,35 @@ PARAM_DEFINE_FLOAT(SENS_ACC_ZSCALE, 1.0f);
/**
* Differential pressure sensor offset
*
+ * The offset (zero-reading) in Pascal
+ *
* @group Sensor Calibration
*/
PARAM_DEFINE_FLOAT(SENS_DPRES_OFF, 0.0f);
/**
- * Differential pressure sensor analog enabled
+ * Differential pressure sensor analog scaling
+ *
+ * Pick the appropriate scaling from the datasheet.
+ * this number defines the (linear) conversion from voltage
+ * to Pascal (pa). For the MPXV7002DP this is 1000.
+ *
+ * NOTE: If the sensor always registers zero, try switching
+ * the static and dynamic tubes.
*
* @group Sensor Calibration
*/
-PARAM_DEFINE_INT32(SENS_DPRES_ANA, 0);
+PARAM_DEFINE_FLOAT(SENS_DPRES_ANSC, 0);
+
+/**
+ * QNH for barometer
+ *
+ * @min 500
+ * @max 1500
+ * @group Sensor Calibration
+ * @unit hPa
+ */
+PARAM_DEFINE_FLOAT(SENS_BARO_QNH, 1013.25f);
/**
@@ -243,6 +262,36 @@ PARAM_DEFINE_INT32(SENS_DPRES_ANA, 0);
PARAM_DEFINE_INT32(SENS_BOARD_ROT, 0);
/**
+ * Board rotation Y (Pitch) offset
+ *
+ * This parameter defines a rotational offset in degrees around the Y (Pitch) axis. It allows the user
+ * to fine tune the board offset in the event of misalignment.
+ *
+ * @group Sensor Calibration
+ */
+ PARAM_DEFINE_FLOAT(SENS_BOARD_Y_OFF, 0.0f);
+
+/**
+ * Board rotation X (Roll) offset
+ *
+ * This parameter defines a rotational offset in degrees around the X (Roll) axis It allows the user
+ * to fine tune the board offset in the event of misalignment.
+ *
+ * @group Sensor Calibration
+ */
+PARAM_DEFINE_FLOAT(SENS_BOARD_X_OFF, 0.0f);
+
+/**
+ * Board rotation Z (YAW) offset
+ *
+ * This parameter defines a rotational offset in degrees around the Z (Yaw) axis. It allows the user
+ * to fine tune the board offset in the event of misalignment.
+ *
+ * @group Sensor Calibration
+ */
+PARAM_DEFINE_FLOAT(SENS_BOARD_Z_OFF, 0.0f);
+
+/**
* External magnetometer rotation
*
* This parameter defines the rotation of the external magnetometer relative
@@ -253,6 +302,19 @@ PARAM_DEFINE_INT32(SENS_BOARD_ROT, 0);
*/
PARAM_DEFINE_INT32(SENS_EXT_MAG_ROT, 0);
+/**
+* Set usage of external magnetometer
+*
+* * Set to 0 (default) to auto-detect (will try to get the external as primary)
+* * Set to 1 to force the external magnetometer as primary
+* * Set to 2 to force the internal magnetometer as primary
+*
+* @min 0
+* @max 2
+* @group Sensor Calibration
+*/
+PARAM_DEFINE_INT32(SENS_EXT_MAG, 0);
+
/**
* RC Channel 1 Minimum
@@ -635,6 +697,15 @@ PARAM_DEFINE_INT32(RC_MAP_LOITER_SW, 0);
PARAM_DEFINE_INT32(RC_MAP_ACRO_SW, 0);
/**
+ * Offboard switch channel mapping.
+ *
+ * @min 0
+ * @max 18
+ * @group Radio Calibration
+ */
+PARAM_DEFINE_INT32(RC_MAP_OFFB_SW, 0);
+
+/**
* Flaps channel mapping.
*
* @min 0
@@ -781,3 +852,20 @@ PARAM_DEFINE_FLOAT(RC_LOITER_TH, 0.5f);
*
*/
PARAM_DEFINE_FLOAT(RC_ACRO_TH, 0.5f);
+
+
+/**
+ * Threshold for selecting offboard mode
+ *
+ * min:-1
+ * max:+1
+ *
+ * 0-1 indicate where in the full channel range the threshold sits
+ * 0 : min
+ * 1 : max
+ * sign indicates polarity of comparison
+ * positive : true when channel>th
+ * negative : true when channel<th
+ *
+ */
+PARAM_DEFINE_FLOAT(RC_OFFB_TH, 0.5f);
diff --git a/src/modules/sensors/sensors.cpp b/src/modules/sensors/sensors.cpp
index b268b1b36..cdcb428dd 100644
--- a/src/modules/sensors/sensors.cpp
+++ b/src/modules/sensors/sensors.cpp
@@ -133,7 +133,7 @@
#endif
#define BATT_V_LOWPASS 0.001f
-#define BATT_V_IGNORE_THRESHOLD 3.5f
+#define BATT_V_IGNORE_THRESHOLD 4.8f
/**
* HACK - true temperature is much less than indicated temperature in baro,
@@ -143,6 +143,12 @@
#define STICK_ON_OFF_LIMIT 0.75f
+/* oddly, ERROR is not defined for c++ */
+#ifdef ERROR
+# undef ERROR
+#endif
+static const int ERROR = -1;
+
/**
* Sensor app start / stop handling function
*
@@ -199,9 +205,15 @@ private:
bool _hil_enabled; /**< if true, HIL is active */
bool _publishing; /**< if true, we are publishing sensor data */
- int _gyro_sub; /**< raw gyro data subscription */
- int _accel_sub; /**< raw accel data subscription */
- int _mag_sub; /**< raw mag data subscription */
+ int _gyro_sub; /**< raw gyro0 data subscription */
+ int _accel_sub; /**< raw accel0 data subscription */
+ int _mag_sub; /**< raw mag0 data subscription */
+ int _gyro1_sub; /**< raw gyro1 data subscription */
+ int _accel1_sub; /**< raw accel1 data subscription */
+ int _mag1_sub; /**< raw mag1 data subscription */
+ int _gyro2_sub; /**< raw gyro2 data subscription */
+ int _accel2_sub; /**< raw accel2 data subscription */
+ int _mag2_sub; /**< raw mag2 data subscription */
int _rc_sub; /**< raw rc channels data subscription */
int _baro_sub; /**< raw baro data subscription */
int _airspeed_sub; /**< airspeed subscription */
@@ -248,11 +260,13 @@ private:
float accel_offset[3];
float accel_scale[3];
float diff_pres_offset_pa;
- float diff_pres_analog_enabled;
+ float diff_pres_analog_scale;
int board_rotation;
int external_mag_rotation;
+ float board_offset[3];
+
int rc_map_roll;
int rc_map_pitch;
int rc_map_yaw;
@@ -264,6 +278,7 @@ private:
int rc_map_posctl_sw;
int rc_map_loiter_sw;
int rc_map_acro_sw;
+ int rc_map_offboard_sw;
int rc_map_flaps;
@@ -280,16 +295,20 @@ private:
float rc_return_th;
float rc_loiter_th;
float rc_acro_th;
+ float rc_offboard_th;
bool rc_assist_inv;
bool rc_auto_inv;
bool rc_posctl_inv;
bool rc_return_inv;
bool rc_loiter_inv;
bool rc_acro_inv;
+ bool rc_offboard_inv;
float battery_voltage_scaling;
float battery_current_scaling;
+ float baro_qnh;
+
} _parameters; /**< local copies of interesting parameters */
struct {
@@ -306,7 +325,7 @@ private:
param_t mag_offset[3];
param_t mag_scale[3];
param_t diff_pres_offset_pa;
- param_t diff_pres_analog_enabled;
+ param_t diff_pres_analog_scale;
param_t rc_map_roll;
param_t rc_map_pitch;
@@ -319,6 +338,7 @@ private:
param_t rc_map_posctl_sw;
param_t rc_map_loiter_sw;
param_t rc_map_acro_sw;
+ param_t rc_map_offboard_sw;
param_t rc_map_flaps;
@@ -335,6 +355,7 @@ private:
param_t rc_return_th;
param_t rc_loiter_th;
param_t rc_acro_th;
+ param_t rc_offboard_th;
param_t battery_voltage_scaling;
param_t battery_current_scaling;
@@ -342,6 +363,10 @@ private:
param_t board_rotation;
param_t external_mag_rotation;
+ param_t board_offset[3];
+
+ param_t baro_qnh;
+
} _parameter_handles; /**< handles for interesting parameters */
@@ -447,12 +472,6 @@ private:
namespace sensors
{
-/* oddly, ERROR is not defined for c++ */
-#ifdef ERROR
-# undef ERROR
-#endif
-static const int ERROR = -1;
-
Sensors *g_sensors = nullptr;
}
@@ -469,6 +488,12 @@ Sensors::Sensors() :
_gyro_sub(-1),
_accel_sub(-1),
_mag_sub(-1),
+ _gyro1_sub(-1),
+ _accel1_sub(-1),
+ _mag1_sub(-1),
+ _gyro2_sub(-1),
+ _accel2_sub(-1),
+ _mag2_sub(-1),
_rc_sub(-1),
_baro_sub(-1),
_vcontrol_mode_sub(-1),
@@ -492,6 +517,7 @@ Sensors::Sensors() :
_battery_current_timestamp(0)
{
memset(&_rc, 0, sizeof(_rc));
+ memset(&_diff_pres, 0, sizeof(_diff_pres));
/* basic r/c parameters */
for (unsigned i = 0; i < _rc_max_chan_count; i++) {
@@ -536,6 +562,7 @@ Sensors::Sensors() :
_parameter_handles.rc_map_posctl_sw = param_find("RC_MAP_POSCTL_SW");
_parameter_handles.rc_map_loiter_sw = param_find("RC_MAP_LOITER_SW");
_parameter_handles.rc_map_acro_sw = param_find("RC_MAP_ACRO_SW");
+ _parameter_handles.rc_map_offboard_sw = param_find("RC_MAP_OFFB_SW");
_parameter_handles.rc_map_aux1 = param_find("RC_MAP_AUX1");
_parameter_handles.rc_map_aux2 = param_find("RC_MAP_AUX2");
@@ -551,6 +578,7 @@ Sensors::Sensors() :
_parameter_handles.rc_return_th = param_find("RC_RETURN_TH");
_parameter_handles.rc_loiter_th = param_find("RC_LOITER_TH");
_parameter_handles.rc_acro_th = param_find("RC_ACRO_TH");
+ _parameter_handles.rc_offboard_th = param_find("RC_OFFB_TH");
/* gyro offsets */
_parameter_handles.gyro_offset[0] = param_find("SENS_GYRO_XOFF");
@@ -579,7 +607,7 @@ Sensors::Sensors() :
/* Differential pressure offset */
_parameter_handles.diff_pres_offset_pa = param_find("SENS_DPRES_OFF");
- _parameter_handles.diff_pres_analog_enabled = param_find("SENS_DPRES_ANA");
+ _parameter_handles.diff_pres_analog_scale = param_find("SENS_DPRES_ANSC");
_parameter_handles.battery_voltage_scaling = param_find("BAT_V_SCALING");
_parameter_handles.battery_current_scaling = param_find("BAT_C_SCALING");
@@ -588,6 +616,14 @@ Sensors::Sensors() :
_parameter_handles.board_rotation = param_find("SENS_BOARD_ROT");
_parameter_handles.external_mag_rotation = param_find("SENS_EXT_MAG_ROT");
+ /* rotation offsets */
+ _parameter_handles.board_offset[0] = param_find("SENS_BOARD_X_OFF");
+ _parameter_handles.board_offset[1] = param_find("SENS_BOARD_Y_OFF");
+ _parameter_handles.board_offset[2] = param_find("SENS_BOARD_Z_OFF");
+
+ /* Barometer QNH */
+ _parameter_handles.baro_qnh = param_find("SENS_BARO_QNH");
+
/* fetch initial parameter values */
parameters_update();
}
@@ -640,7 +676,7 @@ Sensors::parameters_update()
if (!isfinite(tmpScaleFactor) ||
(tmpRevFactor < 0.000001f) ||
(tmpRevFactor > 0.2f)) {
- warnx("RC chan %u not sane, scaling: %8.6f, rev: %d", i, tmpScaleFactor, (int)(_parameters.rev[i]));
+ warnx("RC chan %u not sane, scaling: %8.6f, rev: %d", i, (double)tmpScaleFactor, (int)(_parameters.rev[i]));
/* scaling factors do not make sense, lock them down */
_parameters.scaling_factor[i] = 0.0f;
rc_valid = false;
@@ -698,6 +734,10 @@ Sensors::parameters_update()
warnx("%s", paramerr);
}
+ if (param_get(_parameter_handles.rc_map_offboard_sw, &(_parameters.rc_map_offboard_sw)) != OK) {
+ warnx("%s", paramerr);
+ }
+
if (param_get(_parameter_handles.rc_map_flaps, &(_parameters.rc_map_flaps)) != OK) {
warnx("%s", paramerr);
}
@@ -726,6 +766,9 @@ Sensors::parameters_update()
param_get(_parameter_handles.rc_acro_th, &(_parameters.rc_acro_th));
_parameters.rc_acro_inv = (_parameters.rc_acro_th < 0);
_parameters.rc_acro_th = fabs(_parameters.rc_acro_th);
+ param_get(_parameter_handles.rc_offboard_th, &(_parameters.rc_offboard_th));
+ _parameters.rc_offboard_inv = (_parameters.rc_offboard_th < 0);
+ _parameters.rc_offboard_th = fabs(_parameters.rc_offboard_th);
/* update RC function mappings */
_rc.function[THROTTLE] = _parameters.rc_map_throttle - 1;
@@ -738,6 +781,7 @@ Sensors::parameters_update()
_rc.function[POSCTL] = _parameters.rc_map_posctl_sw - 1;
_rc.function[LOITER] = _parameters.rc_map_loiter_sw - 1;
_rc.function[ACRO] = _parameters.rc_map_acro_sw - 1;
+ _rc.function[OFFBOARD] = _parameters.rc_map_offboard_sw - 1;
_rc.function[FLAPS] = _parameters.rc_map_flaps - 1;
@@ -774,7 +818,7 @@ Sensors::parameters_update()
/* Airspeed offset */
param_get(_parameter_handles.diff_pres_offset_pa, &(_parameters.diff_pres_offset_pa));
- param_get(_parameter_handles.diff_pres_analog_enabled, &(_parameters.diff_pres_analog_enabled));
+ param_get(_parameter_handles.diff_pres_analog_scale, &(_parameters.diff_pres_analog_scale));
/* scaling of ADC ticks to battery voltage */
if (param_get(_parameter_handles.battery_voltage_scaling, &(_parameters.battery_voltage_scaling)) != OK) {
@@ -792,6 +836,36 @@ Sensors::parameters_update()
get_rot_matrix((enum Rotation)_parameters.board_rotation, &_board_rotation);
get_rot_matrix((enum Rotation)_parameters.external_mag_rotation, &_external_mag_rotation);
+ param_get(_parameter_handles.board_offset[0], &(_parameters.board_offset[0]));
+ param_get(_parameter_handles.board_offset[1], &(_parameters.board_offset[1]));
+ param_get(_parameter_handles.board_offset[2], &(_parameters.board_offset[2]));
+
+ /** fine tune board offset on parameter update **/
+ math::Matrix<3, 3> board_rotation_offset;
+ board_rotation_offset.from_euler( M_DEG_TO_RAD_F * _parameters.board_offset[0],
+ M_DEG_TO_RAD_F * _parameters.board_offset[1],
+ M_DEG_TO_RAD_F * _parameters.board_offset[2]);
+
+ _board_rotation = _board_rotation * board_rotation_offset;
+
+ /* update barometer qnh setting */
+ param_get(_parameter_handles.baro_qnh, &(_parameters.baro_qnh));
+ int fd;
+ fd = open(BARO_DEVICE_PATH, 0);
+ if (fd < 0) {
+ warn("%s", BARO_DEVICE_PATH);
+ errx(1, "FATAL: no barometer found");
+
+ } else {
+ int ret = ioctl(fd, BAROIOCSMSLPRESSURE, (unsigned long)(_parameters.baro_qnh * 100));
+ if (ret) {
+ warnx("qnh could not be set");
+ close(fd);
+ return ERROR;
+ }
+ close(fd);
+ }
+
return OK;
}
@@ -967,7 +1041,7 @@ Sensors::accel_poll(struct sensor_combined_s &raw)
if (accel_updated) {
struct accel_report accel_report;
- orb_copy(ORB_ID(sensor_accel), _accel_sub, &accel_report);
+ orb_copy(ORB_ID(sensor_accel0), _accel_sub, &accel_report);
math::Vector<3> vect(accel_report.x, accel_report.y, accel_report.z);
vect = _board_rotation * vect;
@@ -982,6 +1056,48 @@ Sensors::accel_poll(struct sensor_combined_s &raw)
raw.accelerometer_timestamp = accel_report.timestamp;
}
+
+ orb_check(_accel1_sub, &accel_updated);
+
+ if (accel_updated) {
+ struct accel_report accel_report;
+
+ orb_copy(ORB_ID(sensor_accel1), _accel_sub, &accel_report);
+
+ math::Vector<3> vect(accel_report.x, accel_report.y, accel_report.z);
+ vect = _board_rotation * vect;
+
+ raw.accelerometer1_m_s2[0] = vect(0);
+ raw.accelerometer1_m_s2[1] = vect(1);
+ raw.accelerometer1_m_s2[2] = vect(2);
+
+ raw.accelerometer1_raw[0] = accel_report.x_raw;
+ raw.accelerometer1_raw[1] = accel_report.y_raw;
+ raw.accelerometer1_raw[2] = accel_report.z_raw;
+
+ raw.accelerometer1_timestamp = accel_report.timestamp;
+ }
+
+ orb_check(_accel2_sub, &accel_updated);
+
+ if (accel_updated) {
+ struct accel_report accel_report;
+
+ orb_copy(ORB_ID(sensor_accel2), _accel_sub, &accel_report);
+
+ math::Vector<3> vect(accel_report.x, accel_report.y, accel_report.z);
+ vect = _board_rotation * vect;
+
+ raw.accelerometer2_m_s2[0] = vect(0);
+ raw.accelerometer2_m_s2[1] = vect(1);
+ raw.accelerometer2_m_s2[2] = vect(2);
+
+ raw.accelerometer2_raw[0] = accel_report.x_raw;
+ raw.accelerometer2_raw[1] = accel_report.y_raw;
+ raw.accelerometer2_raw[2] = accel_report.z_raw;
+
+ raw.accelerometer2_timestamp = accel_report.timestamp;
+ }
}
void
@@ -993,7 +1109,7 @@ Sensors::gyro_poll(struct sensor_combined_s &raw)
if (gyro_updated) {
struct gyro_report gyro_report;
- orb_copy(ORB_ID(sensor_gyro), _gyro_sub, &gyro_report);
+ orb_copy(ORB_ID(sensor_gyro0), _gyro_sub, &gyro_report);
math::Vector<3> vect(gyro_report.x, gyro_report.y, gyro_report.z);
vect = _board_rotation * vect;
@@ -1008,6 +1124,48 @@ Sensors::gyro_poll(struct sensor_combined_s &raw)
raw.timestamp = gyro_report.timestamp;
}
+
+ orb_check(_gyro1_sub, &gyro_updated);
+
+ if (gyro_updated) {
+ struct gyro_report gyro_report;
+
+ orb_copy(ORB_ID(sensor_gyro1), _gyro1_sub, &gyro_report);
+
+ math::Vector<3> vect(gyro_report.x, gyro_report.y, gyro_report.z);
+ vect = _board_rotation * vect;
+
+ raw.gyro1_rad_s[0] = vect(0);
+ raw.gyro1_rad_s[1] = vect(1);
+ raw.gyro1_rad_s[2] = vect(2);
+
+ raw.gyro1_raw[0] = gyro_report.x_raw;
+ raw.gyro1_raw[1] = gyro_report.y_raw;
+ raw.gyro1_raw[2] = gyro_report.z_raw;
+
+ raw.gyro1_timestamp = gyro_report.timestamp;
+ }
+
+ orb_check(_gyro2_sub, &gyro_updated);
+
+ if (gyro_updated) {
+ struct gyro_report gyro_report;
+
+ orb_copy(ORB_ID(sensor_gyro2), _gyro_sub, &gyro_report);
+
+ math::Vector<3> vect(gyro_report.x, gyro_report.y, gyro_report.z);
+ vect = _board_rotation * vect;
+
+ raw.gyro2_rad_s[0] = vect(0);
+ raw.gyro2_rad_s[1] = vect(1);
+ raw.gyro2_rad_s[2] = vect(2);
+
+ raw.gyro2_raw[0] = gyro_report.x_raw;
+ raw.gyro2_raw[1] = gyro_report.y_raw;
+ raw.gyro2_raw[2] = gyro_report.z_raw;
+
+ raw.gyro2_timestamp = gyro_report.timestamp;
+ }
}
void
@@ -1019,10 +1177,12 @@ Sensors::mag_poll(struct sensor_combined_s &raw)
if (mag_updated) {
struct mag_report mag_report;
- orb_copy(ORB_ID(sensor_mag), _mag_sub, &mag_report);
+ orb_copy(ORB_ID(sensor_mag0), _mag_sub, &mag_report);
math::Vector<3> vect(mag_report.x, mag_report.y, mag_report.z);
+ // XXX we need device-id based handling here
+
if (_mag_is_external) {
vect = _external_mag_rotation * vect;
@@ -1050,7 +1210,7 @@ Sensors::baro_poll(struct sensor_combined_s &raw)
if (baro_updated) {
- orb_copy(ORB_ID(sensor_baro), _baro_sub, &_barometer);
+ orb_copy(ORB_ID(sensor_baro0), _baro_sub, &_barometer);
raw.baro_pres_mbar = _barometer.pressure; // Pressure in mbar
raw.baro_alt_meter = _barometer.altitude; // Altitude in meters
@@ -1069,16 +1229,18 @@ Sensors::diff_pres_poll(struct sensor_combined_s &raw)
if (updated) {
orb_copy(ORB_ID(differential_pressure), _diff_pres_sub, &_diff_pres);
- raw.differential_pressure_pa = _diff_pres.differential_pressure_pa;
+ raw.differential_pressure_pa = _diff_pres.differential_pressure_raw_pa;
raw.differential_pressure_timestamp = _diff_pres.timestamp;
raw.differential_pressure_filtered_pa = _diff_pres.differential_pressure_filtered_pa;
float air_temperature_celsius = (_diff_pres.temperature > -300.0f) ? _diff_pres.temperature : (raw.baro_temp_celcius - PCB_TEMP_ESTIMATE_DEG);
_airspeed.timestamp = _diff_pres.timestamp;
- _airspeed.indicated_airspeed_m_s = calc_indicated_airspeed(_diff_pres.differential_pressure_filtered_pa);
- _airspeed.true_airspeed_m_s = calc_true_airspeed(_diff_pres.differential_pressure_filtered_pa + raw.baro_pres_mbar * 1e2f,
- raw.baro_pres_mbar * 1e2f, air_temperature_celsius);
+
+ /* don't risk to feed negative airspeed into the system */
+ _airspeed.indicated_airspeed_m_s = math::max(0.0f, calc_indicated_airspeed(_diff_pres.differential_pressure_filtered_pa));
+ _airspeed.true_airspeed_m_s = math::max(0.0f, calc_true_airspeed(_diff_pres.differential_pressure_filtered_pa + raw.baro_pres_mbar * 1e2f,
+ raw.baro_pres_mbar * 1e2f, air_temperature_celsius));
_airspeed.air_temperature_celsius = air_temperature_celsius;
/* announce the airspeed if needed, just publish else */
@@ -1202,9 +1364,9 @@ Sensors::parameter_update_poll(bool forced)
}
#if 0
- printf("CH0: RAW MAX: %d MIN %d S: %d MID: %d FUNC: %d\n", (int)_parameters.max[0], (int)_parameters.min[0], (int)(_rc.chan[0].scaling_factor * 10000), (int)(_rc.chan[0].mid), (int)_rc.function[0]);
- printf("CH1: RAW MAX: %d MIN %d S: %d MID: %d FUNC: %d\n", (int)_parameters.max[1], (int)_parameters.min[1], (int)(_rc.chan[1].scaling_factor * 10000), (int)(_rc.chan[1].mid), (int)_rc.function[1]);
- printf("MAN: %d %d\n", (int)(_rc.chan[0].scaled * 100), (int)(_rc.chan[1].scaled * 100));
+ printf("CH0: RAW MAX: %d MIN %d S: %d MID: %d FUNC: %d\n", (int)_parameters.max[0], (int)_parameters.min[0], (int)(_rc.channels[0].scaling_factor * 10000), (int)(_rc.channels[0].mid), (int)_rc.function[0]);
+ printf("CH1: RAW MAX: %d MIN %d S: %d MID: %d FUNC: %d\n", (int)_parameters.max[1], (int)_parameters.min[1], (int)(_rc.channels[1].scaling_factor * 10000), (int)(_rc.channels[1].mid), (int)_rc.function[1]);
+ printf("MAN: %d %d\n", (int)(_rc.channels[0] * 100), (int)(_rc.channels[1] * 100));
fflush(stdout);
usleep(5000);
#endif
@@ -1287,22 +1449,21 @@ Sensors::adc_poll(struct sensor_combined_s &raw)
} else if (ADC_AIRSPEED_VOLTAGE_CHANNEL == buf_adc[i].am_channel) {
/* calculate airspeed, raw is the difference from */
- float voltage = (float)(buf_adc[i].am_data) * 3.3f / 4096.0f * 2.0f; //V_ref/4096 * (voltage divider factor)
+ float voltage = (float)(buf_adc[i].am_data) * 3.3f / 4096.0f * 2.0f; // V_ref/4096 * (voltage divider factor)
/**
* The voltage divider pulls the signal down, only act on
* a valid voltage from a connected sensor. Also assume a non-
* zero offset from the sensor if its connected.
*/
- if (voltage > 0.4f && _parameters.diff_pres_analog_enabled) {
+ if (voltage > 0.4f && (_parameters.diff_pres_analog_scale > 0.0f)) {
- float diff_pres_pa = voltage * 1000.0f - _parameters.diff_pres_offset_pa; //for MPXV7002DP sensor
+ float diff_pres_pa_raw = voltage * _parameters.diff_pres_analog_scale - _parameters.diff_pres_offset_pa;
_diff_pres.timestamp = t;
- _diff_pres.differential_pressure_pa = diff_pres_pa;
- _diff_pres.differential_pressure_filtered_pa = diff_pres_pa;
+ _diff_pres.differential_pressure_raw_pa = diff_pres_pa_raw;
+ _diff_pres.differential_pressure_filtered_pa = (_diff_pres.differential_pressure_filtered_pa * 0.9f) + (diff_pres_pa_raw * 0.1f);
_diff_pres.temperature = -1000.0f;
- _diff_pres.voltage = voltage;
/* announce the airspeed if needed, just publish else */
if (_diff_pres_pub > 0) {
@@ -1334,7 +1495,7 @@ float
Sensors::get_rc_value(enum RC_CHANNELS_FUNCTION func, float min_value, float max_value)
{
if (_rc.function[func] >= 0) {
- float value = _rc.chan[_rc.function[func]].scaled;
+ float value = _rc.channels[_rc.function[func]];
if (value < min_value) {
return min_value;
@@ -1355,7 +1516,7 @@ switch_pos_t
Sensors::get_rc_sw3pos_position(enum RC_CHANNELS_FUNCTION func, float on_th, bool on_inv, float mid_th, bool mid_inv)
{
if (_rc.function[func] >= 0) {
- float value = 0.5f * _rc.chan[_rc.function[func]].scaled + 0.5f;
+ float value = 0.5f * _rc.channels[_rc.function[func]] + 0.5f;
if (on_inv ? value < on_th : value > on_th) {
return SWITCH_POS_ON;
@@ -1376,7 +1537,7 @@ switch_pos_t
Sensors::get_rc_sw2pos_position(enum RC_CHANNELS_FUNCTION func, float on_th, bool on_inv)
{
if (_rc.function[func] >= 0) {
- float value = 0.5f * _rc.chan[_rc.function[func]].scaled + 0.5f;
+ float value = 0.5f * _rc.channels[_rc.function[func]] + 0.5f;
if (on_inv ? value < on_th : value > on_th) {
return SWITCH_POS_ON;
@@ -1468,25 +1629,25 @@ Sensors::rc_poll()
* DO NOT REMOVE OR ALTER STEP 1!
*/
if (rc_input.values[i] > (_parameters.trim[i] + _parameters.dz[i])) {
- _rc.chan[i].scaled = (rc_input.values[i] - _parameters.trim[i] - _parameters.dz[i]) / (float)(_parameters.max[i] - _parameters.trim[i] - _parameters.dz[i]);
+ _rc.channels[i] = (rc_input.values[i] - _parameters.trim[i] - _parameters.dz[i]) / (float)(_parameters.max[i] - _parameters.trim[i] - _parameters.dz[i]);
} else if (rc_input.values[i] < (_parameters.trim[i] - _parameters.dz[i])) {
- _rc.chan[i].scaled = (rc_input.values[i] - _parameters.trim[i] + _parameters.dz[i]) / (float)(_parameters.trim[i] - _parameters.min[i] - _parameters.dz[i]);
+ _rc.channels[i] = (rc_input.values[i] - _parameters.trim[i] + _parameters.dz[i]) / (float)(_parameters.trim[i] - _parameters.min[i] - _parameters.dz[i]);
} else {
/* in the configured dead zone, output zero */
- _rc.chan[i].scaled = 0.0f;
+ _rc.channels[i] = 0.0f;
}
- _rc.chan[i].scaled *= _parameters.rev[i];
+ _rc.channels[i] *= _parameters.rev[i];
/* handle any parameter-induced blowups */
- if (!isfinite(_rc.chan[i].scaled)) {
- _rc.chan[i].scaled = 0.0f;
+ if (!isfinite(_rc.channels[i])) {
+ _rc.channels[i] = 0.0f;
}
}
- _rc.chan_count = rc_input.channel_count;
+ _rc.channel_count = rc_input.channel_count;
_rc.rssi = rc_input.rssi;
_rc.signal_lost = signal_lost;
_rc.timestamp = rc_input.timestamp_last_signal;
@@ -1524,6 +1685,7 @@ Sensors::rc_poll()
manual.return_switch = get_rc_sw2pos_position(RETURN, _parameters.rc_return_th, _parameters.rc_return_inv);
manual.loiter_switch = get_rc_sw2pos_position(LOITER, _parameters.rc_loiter_th, _parameters.rc_loiter_inv);
manual.acro_switch = get_rc_sw2pos_position(ACRO, _parameters.rc_acro_th, _parameters.rc_acro_inv);
+ manual.offboard_switch = get_rc_sw2pos_position(OFFBOARD, _parameters.rc_offboard_th, _parameters.rc_offboard_inv);
/* publish manual_control_setpoint topic */
if (_manual_control_pub > 0) {
@@ -1579,11 +1741,17 @@ Sensors::task_main()
/*
* do subscriptions
*/
- _gyro_sub = orb_subscribe(ORB_ID(sensor_gyro));
- _accel_sub = orb_subscribe(ORB_ID(sensor_accel));
- _mag_sub = orb_subscribe(ORB_ID(sensor_mag));
+ _gyro_sub = orb_subscribe(ORB_ID(sensor_gyro0));
+ _accel_sub = orb_subscribe(ORB_ID(sensor_accel0));
+ _mag_sub = orb_subscribe(ORB_ID(sensor_mag0));
+ _gyro1_sub = orb_subscribe(ORB_ID(sensor_gyro1));
+ _accel1_sub = orb_subscribe(ORB_ID(sensor_accel1));
+ _mag1_sub = orb_subscribe(ORB_ID(sensor_mag1));
+ _gyro2_sub = orb_subscribe(ORB_ID(sensor_gyro2));
+ _accel2_sub = orb_subscribe(ORB_ID(sensor_accel2));
+ _mag2_sub = orb_subscribe(ORB_ID(sensor_mag2));
_rc_sub = orb_subscribe(ORB_ID(input_rc));
- _baro_sub = orb_subscribe(ORB_ID(sensor_baro));
+ _baro_sub = orb_subscribe(ORB_ID(sensor_baro0));
_diff_pres_sub = orb_subscribe(ORB_ID(differential_pressure));
_vcontrol_mode_sub = orb_subscribe(ORB_ID(vehicle_control_mode));
_params_sub = orb_subscribe(ORB_ID(parameter_update));
diff --git a/src/modules/systemlib/board_serial.c b/src/modules/systemlib/board_serial.c
index ad8c2bf83..182fd15c6 100644
--- a/src/modules/systemlib/board_serial.c
+++ b/src/modules/systemlib/board_serial.c
@@ -44,11 +44,11 @@
#include "board_config.h"
#include "board_serial.h"
-int get_board_serial(char *serialid)
+int get_board_serial(uint8_t *serialid)
{
- const volatile unsigned *udid_ptr = (const unsigned *)UDID_START;
+ const volatile uint32_t *udid_ptr = (const uint32_t *)UDID_START;
union udid id;
- val_read((unsigned *)&id, udid_ptr, sizeof(id));
+ val_read((uint32_t *)&id, udid_ptr, sizeof(id));
/* Copy the serial from the chips non-write memory and swap endianess */
@@ -57,4 +57,4 @@ int get_board_serial(char *serialid)
serialid[8] = id.data[11]; serialid[9] = id.data[10]; serialid[10] = id.data[9]; serialid[11] = id.data[8];
return 0;
-} \ No newline at end of file
+}
diff --git a/src/modules/systemlib/board_serial.h b/src/modules/systemlib/board_serial.h
index b14bb4376..873d9657b 100644
--- a/src/modules/systemlib/board_serial.h
+++ b/src/modules/systemlib/board_serial.h
@@ -44,6 +44,6 @@
__BEGIN_DECLS
-__EXPORT int get_board_serial(char *serialid);
+__EXPORT int get_board_serial(uint8_t *serialid);
__END_DECLS
diff --git a/src/modules/systemlib/circuit_breaker.c b/src/modules/systemlib/circuit_breaker.c
new file mode 100644
index 000000000..12187d70e
--- /dev/null
+++ b/src/modules/systemlib/circuit_breaker.c
@@ -0,0 +1,146 @@
+/****************************************************************************
+ *
+ * Copyright (c) 2014 PX4 Development Team. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/*
+ * @file circuit_breaker.c
+ *
+ * Circuit breaker parameters.
+ * Analog to real aviation circuit breakers these parameters
+ * allow to disable subsystems. They are not supported as standard
+ * operation procedure and are only provided for development purposes.
+ * To ensure they are not activated accidentally, the associated
+ * parameter needs to set to the key (magic).
+ */
+
+#include <systemlib/param/param.h>
+#include <systemlib/circuit_breaker.h>
+
+/**
+ * Circuit breaker for power supply check
+ *
+ * Setting this parameter to 894281 will disable the power valid
+ * checks in the commander.
+ * WARNING: ENABLING THIS CIRCUIT BREAKER IS AT OWN RISK
+ *
+ * @min 0
+ * @max 894281
+ * @group Circuit Breaker
+ */
+PARAM_DEFINE_INT32(CBRK_SUPPLY_CHK, 0);
+
+/**
+ * Circuit breaker for rate controller output
+ *
+ * Setting this parameter to 140253 will disable the rate
+ * controller uORB publication.
+ * WARNING: ENABLING THIS CIRCUIT BREAKER IS AT OWN RISK
+ *
+ * @min 0
+ * @max 140253
+ * @group Circuit Breaker
+ */
+PARAM_DEFINE_INT32(CBRK_RATE_CTRL, 0);
+
+/**
+ * Circuit breaker for IO safety
+ *
+ * Setting this parameter to 894281 will disable IO safety.
+ * WARNING: ENABLING THIS CIRCUIT BREAKER IS AT OWN RISK
+ *
+ * @min 0
+ * @max 22027
+ * @group Circuit Breaker
+ */
+PARAM_DEFINE_INT32(CBRK_IO_SAFETY, 0);
+
+/**
+ * Circuit breaker for airspeed sensor
+ *
+ * Setting this parameter to 162128 will disable the check for an airspeed sensor.
+ * WARNING: ENABLING THIS CIRCUIT BREAKER IS AT OWN RISK
+ *
+ * @min 0
+ * @max 162128
+ * @group Circuit Breaker
+ */
+PARAM_DEFINE_INT32(CBRK_AIRSPD_CHK, 0);
+
+/**
+ * Circuit breaker for flight termination
+ *
+ * Setting this parameter to 121212 will disable the flight termination action.
+ * --> The IO driver will not do flight termination if requested by the FMU
+ * WARNING: ENABLING THIS CIRCUIT BREAKER IS AT OWN RISK
+ *
+ * @min 0
+ * @max 121212
+ * @group Circuit Breaker
+ */
+PARAM_DEFINE_INT32(CBRK_FLIGHTTERM, 121212);
+
+/**
+ * Circuit breaker for engine failure detection
+ *
+ * Setting this parameter to 284953 will disable the engine failure detection.
+ * If the aircraft is in engine failure mode the enine failure flag will be
+ * set to healthy
+ * WARNING: ENABLING THIS CIRCUIT BREAKER IS AT OWN RISK
+ *
+ * @min 0
+ * @max 284953
+ * @group Circuit Breaker
+ */
+PARAM_DEFINE_INT32(CBRK_ENGINEFAIL, 284953);
+
+/**
+ * Circuit breaker for gps failure detection
+ *
+ * Setting this parameter to 240024 will disable the gps failure detection.
+ * If the aircraft is in gps failure mode the gps failure flag will be
+ * set to healthy
+ * WARNING: ENABLING THIS CIRCUIT BREAKER IS AT OWN RISK
+ *
+ * @min 0
+ * @max 240024
+ * @group Circuit Breaker
+ */
+PARAM_DEFINE_INT32(CBRK_GPSFAIL, 240024);
+
+bool circuit_breaker_enabled(const char* breaker, int32_t magic)
+{
+ int32_t val;
+ (void)param_get(param_find(breaker), &val);
+
+ return (val == magic);
+}
+
diff --git a/src/modules/systemlib/circuit_breaker.h b/src/modules/systemlib/circuit_breaker.h
new file mode 100644
index 000000000..b3431722f
--- /dev/null
+++ b/src/modules/systemlib/circuit_breaker.h
@@ -0,0 +1,68 @@
+/****************************************************************************
+ *
+ * Copyright (c) 2014 PX4 Development Team. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/*
+ * @file circuit_breaker.h
+ *
+ * Circuit breaker functionality.
+ */
+
+#ifndef CIRCUIT_BREAKER_H_
+#define CIRCUIT_BREAKER_H_
+
+/* SAFETY WARNING -- SAFETY WARNING -- SAFETY WARNING
+ *
+ * OBEY THE DOCUMENTATION FOR ALL CIRCUIT BREAKERS HERE,
+ * ENSURE TO READ CAREFULLY ALL SAFETY WARNINGS.
+ * http://pixhawk.org/dev/circuit_breakers
+ *
+ * CIRCUIT BREAKERS ARE NOT PART OF THE STANDARD OPERATION PROCEDURE
+ * AND MAY DISABLE CHECKS THAT ARE VITAL FOR SAFE FLIGHT.
+ */
+#define CBRK_SUPPLY_CHK_KEY 894281
+#define CBRK_RATE_CTRL_KEY 140253
+#define CBRK_IO_SAFETY_KEY 22027
+#define CBRK_AIRSPD_CHK_KEY 162128
+#define CBRK_FLIGHTTERM_KEY 121212
+#define CBRK_ENGINEFAIL_KEY 284953
+#define CBRK_GPSFAIL_KEY 240024
+
+#include <stdbool.h>
+
+__BEGIN_DECLS
+
+__EXPORT bool circuit_breaker_enabled(const char* breaker, int32_t magic);
+
+__END_DECLS
+
+#endif /* CIRCUIT_BREAKER_H_ */
diff --git a/src/modules/systemlib/cpuload.c b/src/modules/systemlib/cpuload.c
index ccc516f39..7aa2f3a5f 100644
--- a/src/modules/systemlib/cpuload.c
+++ b/src/modules/systemlib/cpuload.c
@@ -67,7 +67,7 @@ __EXPORT void sched_note_switch(FAR struct tcb_s *pFromTcb, FAR struct tcb_s *pT
__EXPORT struct system_load_s system_load;
-extern FAR struct _TCB *sched_gettcb(pid_t pid);
+extern FAR struct tcb_s *sched_gettcb(pid_t pid);
void cpuload_initialize_once()
{
diff --git a/src/modules/systemlib/err.c b/src/modules/systemlib/err.c
index 6c0e876d1..998b5ac7d 100644
--- a/src/modules/systemlib/err.c
+++ b/src/modules/systemlib/err.c
@@ -86,7 +86,7 @@ warnerr_core(int errcode, const char *fmt, va_list args)
fprintf(stderr, "\n");
#elif CONFIG_ARCH_LOWPUTC
lowsyslog("%s: ", getprogname());
- lowvyslog(fmt, args);
+ lowvsyslog(fmt, args);
/* convenience as many parts of NuttX use negative errno */
if (errcode < 0)
diff --git a/src/modules/systemlib/hx_stream.c b/src/modules/systemlib/hx_stream.c
index 8e9c2bfcf..52ae77de5 100644
--- a/src/modules/systemlib/hx_stream.c
+++ b/src/modules/systemlib/hx_stream.c
@@ -63,7 +63,7 @@ struct hx_stream {
/* TX state */
int fd;
bool tx_error;
- uint8_t *tx_buf;
+ const uint8_t *tx_buf;
unsigned tx_resid;
uint32_t tx_crc;
enum {
diff --git a/src/modules/systemlib/mcu_version.c b/src/modules/systemlib/mcu_version.c
new file mode 100644
index 000000000..4bcf95784
--- /dev/null
+++ b/src/modules/systemlib/mcu_version.c
@@ -0,0 +1,109 @@
+/****************************************************************************
+ *
+ * Copyright (c) 2014 PX4 Development Team. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/**
+ * @file mcu_version.c
+ *
+ * Read out the microcontroller version from the board
+ *
+ * @author Lorenz Meier <lorenz@px4.io>
+ *
+ */
+
+#include "mcu_version.h"
+
+#include <nuttx/config.h>
+
+#ifdef CONFIG_ARCH_CHIP_STM32
+#include <up_arch.h>
+
+#define DBGMCU_IDCODE 0xE0042000
+
+#define STM32F40x_41x 0x413
+#define STM32F42x_43x 0x419
+
+#define REVID_MASK 0xFFFF0000
+#define DEVID_MASK 0xFFF
+
+#endif
+
+
+
+int mcu_version(char* rev, char** revstr)
+{
+#ifdef CONFIG_ARCH_CHIP_STM32
+ uint32_t abc = getreg32(DBGMCU_IDCODE);
+
+ int32_t chip_version = abc & DEVID_MASK;
+ enum MCU_REV revid = (abc & REVID_MASK) >> 16;
+
+ switch (chip_version) {
+ case STM32F40x_41x:
+ *revstr = "STM32F40x";
+ break;
+ case STM32F42x_43x:
+ *revstr = "STM32F42x";
+ break;
+ default:
+ *revstr = "STM32F???";
+ break;
+ }
+
+ switch (revid) {
+
+ case MCU_REV_STM32F4_REV_A:
+ *rev = 'A';
+ break;
+ case MCU_REV_STM32F4_REV_Z:
+ *rev = 'Z';
+ break;
+ case MCU_REV_STM32F4_REV_Y:
+ *rev = 'Y';
+ break;
+ case MCU_REV_STM32F4_REV_1:
+ *rev = '1';
+ break;
+ case MCU_REV_STM32F4_REV_3:
+ *rev = '3';
+ break;
+ default:
+ *rev = '?';
+ revid = -1;
+ break;
+ }
+
+ return revid;
+#else
+ return -1;
+#endif
+}
diff --git a/src/modules/mavlink/util.h b/src/modules/systemlib/mcu_version.h
index 5ca9a085d..1b3d0aba9 100644
--- a/src/modules/mavlink/util.h
+++ b/src/modules/systemlib/mcu_version.h
@@ -1,6 +1,6 @@
/****************************************************************************
*
- * Copyright (c) 2012-2014 PX4 Development Team. All rights reserved.
+ * Copyright (c) 2014 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -31,23 +31,22 @@
*
****************************************************************************/
-/**
- * @file util.h
- * Utility and helper functions and data.
- */
-
#pragma once
-/** MAVLink communications channel */
-extern uint8_t chan;
-
-/** Shutdown marker */
-extern volatile bool thread_should_exit;
-
-/** Waypoint storage */
-extern mavlink_wpm_storage *wpm;
+/* magic numbers from reference manual */
+enum MCU_REV {
+ MCU_REV_STM32F4_REV_A = 0x1000,
+ MCU_REV_STM32F4_REV_Z = 0x1001,
+ MCU_REV_STM32F4_REV_Y = 0x1003,
+ MCU_REV_STM32F4_REV_1 = 0x1007,
+ MCU_REV_STM32F4_REV_3 = 0x2001
+};
/**
- * Translate the custom state into standard mavlink modes and state.
+ * Reports the microcontroller version of the main CPU.
+ *
+ * @param rev The silicon revision character
+ * @param revstr The full chip name string
+ * @return The silicon revision / version number as integer
*/
-extern void get_mavlink_mode_and_state(uint8_t *mavlink_state, uint8_t *mavlink_base_mode, uint32_t *mavlink_custom_mode);
+__EXPORT int mcu_version(char* rev, char** revstr);
diff --git a/src/modules/systemlib/mixer/mixer.h b/src/modules/systemlib/mixer/mixer.h
index 225570fa4..17989558e 100644
--- a/src/modules/systemlib/mixer/mixer.h
+++ b/src/modules/systemlib/mixer/mixer.h
@@ -130,6 +130,9 @@
#include <nuttx/config.h>
#include "drivers/drv_mixer.h"
+
+#include <uORB/topics/multirotor_motor_limits.h>
+
#include "mixer_load.h"
/**
@@ -231,6 +234,10 @@ protected:
static const char * skipline(const char *buf, unsigned &buflen);
private:
+
+ /* do not allow to copy due to prt data members */
+ Mixer(const Mixer&);
+ Mixer& operator=(const Mixer&);
};
/**
@@ -307,6 +314,10 @@ public:
private:
Mixer *_first; /**< linked list of mixers */
+
+ /* do not allow to copy due to pointer data members */
+ MixerGroup(const MixerGroup&);
+ MixerGroup operator=(const MixerGroup&);
};
/**
@@ -424,6 +435,10 @@ private:
mixer_scaler_s &scaler,
uint8_t &control_group,
uint8_t &control_index);
+
+ /* do not allow to copy due to ptr data members */
+ SimpleMixer(const SimpleMixer&);
+ SimpleMixer operator=(const SimpleMixer&);
};
/**
@@ -519,9 +534,15 @@ private:
float _yaw_scale;
float _idle_speed;
+ orb_advert_t _limits_pub;
+ multirotor_motor_limits_s _limits;
+
unsigned _rotor_count;
const Rotor *_rotors;
+ /* do not allow to copy due to ptr data members */
+ MultirotorMixer(const MultirotorMixer&);
+ MultirotorMixer operator=(const MultirotorMixer&);
};
#endif
diff --git a/src/modules/systemlib/mixer/mixer_load.c b/src/modules/systemlib/mixer/mixer_load.c
index bf3428a50..0d629d610 100644
--- a/src/modules/systemlib/mixer/mixer_load.c
+++ b/src/modules/systemlib/mixer/mixer_load.c
@@ -91,6 +91,7 @@ int load_mixer_file(const char *fname, char *buf, unsigned maxlen)
/* if the line is too long to fit in the buffer, bail */
if ((strlen(line) + strlen(buf) + 1) >= maxlen) {
warnx("line too long");
+ fclose(fp);
return -1;
}
@@ -98,6 +99,7 @@ int load_mixer_file(const char *fname, char *buf, unsigned maxlen)
strcat(buf, line);
}
+ fclose(fp);
return 0;
}
diff --git a/src/modules/systemlib/mixer/mixer_multirotor.cpp b/src/modules/systemlib/mixer/mixer_multirotor.cpp
index 092c0e2b0..57e17b67d 100644
--- a/src/modules/systemlib/mixer/mixer_multirotor.cpp
+++ b/src/modules/systemlib/mixer/mixer_multirotor.cpp
@@ -36,7 +36,8 @@
*
* Multi-rotor mixers.
*/
-
+#include <uORB/uORB.h>
+#include <uORB/topics/multirotor_motor_limits.h>
#include <nuttx/config.h>
#include <sys/types.h>
@@ -208,7 +209,6 @@ MultirotorMixer::from_text(Mixer::ControlCallback control_cb, uintptr_t cb_handl
char geomname[8];
int s[4];
int used;
- const char *end = buf + buflen;
/* enforce that the mixer ends with space or a new line */
for (int i = buflen - 1; i >= 0; i--) {
@@ -302,7 +302,11 @@ MultirotorMixer::mix(float *outputs, unsigned space)
//lowsyslog("thrust: %d, get_control3: %d\n", (int)(thrust), (int)(get_control(0, 3)));
float min_out = 0.0f;
float max_out = 0.0f;
- float scale_yaw = 1.0f;
+
+ _limits.roll_pitch = false;
+ _limits.yaw = false;
+ _limits.throttle_upper = false;
+ _limits.throttle_lower = false;
/* perform initial mix pass yielding unbounded outputs, ignore yaw */
for (unsigned i = 0; i < _rotor_count; i++) {
@@ -313,6 +317,7 @@ MultirotorMixer::mix(float *outputs, unsigned space)
/* limit yaw if it causes outputs clipping */
if (out >= 0.0f && out < -yaw * _rotors[i].yaw_scale) {
yaw = -out / _rotors[i].yaw_scale;
+ _limits.yaw = true;
}
/* calculate min and max output values */
@@ -327,13 +332,14 @@ MultirotorMixer::mix(float *outputs, unsigned space)
}
/* scale down roll/pitch controls if some outputs are negative, don't add yaw, keep total thrust */
- if (min_out < 0.0) {
+ if (min_out < 0.0f) {
float scale_in = thrust / (thrust - min_out);
/* mix again with adjusted controls */
for (unsigned i = 0; i < _rotor_count; i++) {
outputs[i] = scale_in * (roll * _rotors[i].roll_scale + pitch * _rotors[i].pitch_scale) + thrust;
}
+ _limits.roll_pitch = true;
} else {
/* roll/pitch mixed without limiting, add yaw control */
@@ -346,6 +352,7 @@ MultirotorMixer::mix(float *outputs, unsigned space)
float scale_out;
if (max_out > 1.0f) {
scale_out = 1.0f / max_out;
+ _limits.throttle_upper = true;
} else {
scale_out = 1.0f;
@@ -353,9 +360,20 @@ MultirotorMixer::mix(float *outputs, unsigned space)
/* scale outputs to range _idle_speed..1, and do final limiting */
for (unsigned i = 0; i < _rotor_count; i++) {
+ if (outputs[i] < _idle_speed) {
+ _limits.throttle_lower = true;
+ }
outputs[i] = constrain(_idle_speed + (outputs[i] * (1.0f - _idle_speed) * scale_out), _idle_speed, 1.0f);
}
+#if defined(CONFIG_ARCH_BOARD_PX4FMU_V1) || defined(CONFIG_ARCH_BOARD_PX4FMU_V2)
+ /* publish/advertise motor limits if running on FMU */
+ if (_limits_pub > 0) {
+ orb_publish(ORB_ID(multirotor_motor_limits), _limits_pub, &_limits);
+ } else {
+ _limits_pub = orb_advertise(ORB_ID(multirotor_motor_limits), &_limits);
+ }
+#endif
return _rotor_count;
}
diff --git a/src/modules/systemlib/module.mk b/src/modules/systemlib/module.mk
index 3953b757d..fe8b7e75a 100644
--- a/src/modules/systemlib/module.mk
+++ b/src/modules/systemlib/module.mk
@@ -1,6 +1,6 @@
############################################################################
#
-# Copyright (C) 2012 PX4 Development Team. All rights reserved.
+# Copyright (c) 2012-2014 PX4 Development Team. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
@@ -52,5 +52,8 @@ SRCS = err.c \
rc_check.c \
otp.c \
board_serial.c \
- pwm_limit/pwm_limit.c
+ pwm_limit/pwm_limit.c \
+ circuit_breaker.c \
+ mcu_version.c
+MAXOPTIMIZATION = -Os
diff --git a/src/modules/systemlib/otp.c b/src/modules/systemlib/otp.c
index 695574fdc..0548a9f7d 100644
--- a/src/modules/systemlib/otp.c
+++ b/src/modules/systemlib/otp.c
@@ -133,7 +133,7 @@ int lock_otp(void)
// COMPLETE, BUSY, or other flash error?
-int F_GetStatus(void)
+static int F_GetStatus(void)
{
int fs = F_COMPLETE;
diff --git a/src/modules/systemlib/otp.h b/src/modules/systemlib/otp.h
index f10e129d8..273b064f0 100644
--- a/src/modules/systemlib/otp.h
+++ b/src/modules/systemlib/otp.h
@@ -125,7 +125,7 @@ struct otp_lock {
#pragma pack(push, 1)
union udid {
uint32_t serial[3];
- char data[12];
+ uint8_t data[12];
};
#pragma pack(pop)
diff --git a/src/modules/systemlib/param/param.c b/src/modules/systemlib/param/param.c
index 7a499ca72..6b8d0e634 100644
--- a/src/modules/systemlib/param/param.c
+++ b/src/modules/systemlib/param/param.c
@@ -96,8 +96,6 @@ ORB_DEFINE(parameter_update, struct parameter_update_s);
/** parameter update topic handle */
static orb_advert_t param_topic = -1;
-static sem_t param_sem = { .semcount = 1 };
-
/** lock the parameter store */
static void
param_lock(void)
@@ -324,7 +322,8 @@ param_get_value_ptr(param_t param)
v = &param_info_base[param].val;
}
- if (param_type(param) == PARAM_TYPE_STRUCT) {
+ if (param_type(param) >= PARAM_TYPE_STRUCT
+ && param_type(param) <= PARAM_TYPE_STRUCT_MAX) {
result = v->p;
} else {
diff --git a/src/modules/systemlib/param/param.h b/src/modules/systemlib/param/param.h
index 084cd931a..dc73b37e9 100644
--- a/src/modules/systemlib/param/param.h
+++ b/src/modules/systemlib/param/param.h
@@ -307,7 +307,7 @@ __EXPORT int param_load_default(void);
struct param_info_s __param__##_name = { \
#_name, \
PARAM_TYPE_STRUCT + sizeof(_default), \
- .val.p = &_default; \
+ .val.p = &_default \
}
/**
diff --git a/src/modules/systemlib/perf_counter.h b/src/modules/systemlib/perf_counter.h
index 6835ee4a2..668d9dfdf 100644
--- a/src/modules/systemlib/perf_counter.h
+++ b/src/modules/systemlib/perf_counter.h
@@ -75,7 +75,7 @@ __EXPORT extern void perf_free(perf_counter_t handle);
/**
* Count a performance event.
*
- * This call only affects counters that take single events; PC_COUNT etc.
+ * This call only affects counters that take single events; PC_COUNT, PC_INTERVAL etc.
*
* @param handle The handle returned from perf_alloc.
*/
diff --git a/src/modules/systemlib/pwm_limit/pwm_limit.c b/src/modules/systemlib/pwm_limit/pwm_limit.c
index ea5ba9e52..c733a53d7 100644
--- a/src/modules/systemlib/pwm_limit/pwm_limit.c
+++ b/src/modules/systemlib/pwm_limit/pwm_limit.c
@@ -97,7 +97,6 @@ void pwm_limit_calc(const bool armed, const unsigned num_channels, const uint16_
}
unsigned progress;
- uint16_t temp_pwm;
/* then set effective_pwm based on state */
switch (limit->state) {
diff --git a/src/modules/systemlib/rc_check.c b/src/modules/systemlib/rc_check.c
index c0c1a5cb4..b35b333af 100644
--- a/src/modules/systemlib/rc_check.c
+++ b/src/modules/systemlib/rc_check.c
@@ -42,6 +42,7 @@
#include <stdio.h>
#include <fcntl.h>
+#include <systemlib/err.h>
#include <systemlib/rc_check.h>
#include <systemlib/param/param.h>
#include <mavlink/mavlink_log.h>
@@ -98,32 +99,32 @@ int rc_calibration_check(int mavlink_fd) {
/* assert min..center..max ordering */
if (param_min < 500) {
count++;
- mavlink_log_critical(mavlink_fd, "ERR: RC_%d_MIN < 500", i+1);
+ mavlink_log_critical(mavlink_fd, "#audio ERR: RC_%d_MIN < 500", i+1);
/* give system time to flush error message in case there are more */
usleep(100000);
}
if (param_max > 2500) {
count++;
- mavlink_log_critical(mavlink_fd, "ERR: RC_%d_MAX > 2500", i+1);
+ mavlink_log_critical(mavlink_fd, "#audio ERR: RC_%d_MAX > 2500", i+1);
/* give system time to flush error message in case there are more */
usleep(100000);
}
if (param_trim < param_min) {
count++;
- mavlink_log_critical(mavlink_fd, "ERR: RC_%d_TRIM < MIN (%d/%d)", i+1, (int)param_trim, (int)param_min);
+ mavlink_log_critical(mavlink_fd, "#audio ERR: RC_%d_TRIM < MIN (%d/%d)", i+1, (int)param_trim, (int)param_min);
/* give system time to flush error message in case there are more */
usleep(100000);
}
if (param_trim > param_max) {
count++;
- mavlink_log_critical(mavlink_fd, "ERR: RC_%d_TRIM > MAX (%d/%d)", i+1, (int)param_trim, (int)param_max);
+ mavlink_log_critical(mavlink_fd, "#audio ERR: RC_%d_TRIM > MAX (%d/%d)", i+1, (int)param_trim, (int)param_max);
/* give system time to flush error message in case there are more */
usleep(100000);
}
/* assert deadzone is sane */
if (param_dz > 500) {
- mavlink_log_critical(mavlink_fd, "ERR: RC_%d_DZ > 500", i+1);
+ mavlink_log_critical(mavlink_fd, "#audio ERR: RC_%d_DZ > 500", i+1);
/* give system time to flush error message in case there are more */
usleep(100000);
count++;
@@ -139,7 +140,7 @@ int rc_calibration_check(int mavlink_fd) {
/* sanity checks pass, enable channel */
if (count) {
- mavlink_log_critical(mavlink_fd, "ERROR: %d config error(s) for RC channel %d.", count, (i + 1));
+ mavlink_log_critical(mavlink_fd, "#audio ERROR: %d config error(s) for RC channel %d.", count, (i + 1));
warnx("ERROR: %d config error(s) for RC channel %d.", count, (i + 1));
usleep(100000);
}
diff --git a/src/modules/systemlib/state_table.h b/src/modules/systemlib/state_table.h
index f2709d29f..e6011fdef 100644
--- a/src/modules/systemlib/state_table.h
+++ b/src/modules/systemlib/state_table.h
@@ -1,6 +1,6 @@
/****************************************************************************
*
- * Copyright (C) 2012 PX4 Development Team. All rights reserved.
+ * Copyright (C) 2013-2014 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -33,8 +33,9 @@
/**
* @file state_table.h
- *
+ *
* Finite-State-Machine helper class for state table
+ * @author: Julian Oes <julian@oes.ch>
*/
#ifndef __SYSTEMLIB_STATE_TABLE_H
@@ -48,22 +49,28 @@ public:
Action action;
unsigned nextState;
};
-
+
StateTable(Tran const *table, unsigned nStates, unsigned nSignals)
: myTable(table), myNsignals(nSignals), myNstates(nStates) {}
-
+
#define NO_ACTION &StateTable::doNothing
- #define ACTION(_target) static_cast<StateTable::Action>(_target)
+ #define ACTION(_target) StateTable::Action(_target)
virtual ~StateTable() {}
-
+
void dispatch(unsigned const sig) {
- register Tran const *t = myTable + myState*myNsignals + sig;
- (this->*(t->action))();
+ /* get transition using state table */
+ Tran const *t = myTable + myState*myNsignals + sig;
+ /* accept new state */
myState = t->nextState;
+
+ /* */
+ (this->*(t->action))();
+ }
+ void doNothing() {
+ return;
}
- void doNothing() {}
protected:
unsigned myState;
private:
@@ -72,4 +79,4 @@ private:
unsigned myNstates;
};
-#endif \ No newline at end of file
+#endif
diff --git a/src/modules/systemlib/systemlib.c b/src/modules/systemlib/systemlib.c
index 57a751e1c..90d8dd77c 100644
--- a/src/modules/systemlib/systemlib.c
+++ b/src/modules/systemlib/systemlib.c
@@ -54,6 +54,9 @@
#include "systemlib.h"
+// Didn't seem right to include up_internal.h, so direct extern instead.
+extern void up_systemreset(void) noreturn_function;
+
void
systemreset(bool to_bootloader)
{
@@ -64,6 +67,9 @@ systemreset(bool to_bootloader)
*(uint32_t *)0x40002850 = 0xb007b007;
}
up_systemreset();
+
+ /* lock up here */
+ while(true);
}
static void kill_task(FAR struct tcb_s *tcb, FAR void *arg);
diff --git a/src/modules/uORB/Publication.cpp b/src/modules/uORB/Publication.cpp
index e9398a625..fd5c511ef 100644
--- a/src/modules/uORB/Publication.cpp
+++ b/src/modules/uORB/Publication.cpp
@@ -47,6 +47,7 @@
#include "topics/vehicle_rates_setpoint.h"
#include "topics/actuator_outputs.h"
#include "topics/encoders.h"
+#include "topics/tecs_status.h"
namespace uORB {
@@ -76,5 +77,6 @@ template class __EXPORT Publication<vehicle_attitude_setpoint_s>;
template class __EXPORT Publication<vehicle_rates_setpoint_s>;
template class __EXPORT Publication<actuator_outputs_s>;
template class __EXPORT Publication<encoders_s>;
+template class __EXPORT Publication<tecs_status_s>;
}
diff --git a/src/modules/uORB/Subscription.cpp b/src/modules/uORB/Subscription.cpp
index 1a4867c85..69969307e 100644
--- a/src/modules/uORB/Subscription.cpp
+++ b/src/modules/uORB/Subscription.cpp
@@ -40,6 +40,7 @@
#include "topics/parameter_update.h"
#include "topics/actuator_controls.h"
#include "topics/vehicle_gps_position.h"
+#include "topics/satellite_info.h"
#include "topics/sensor_combined.h"
#include "topics/vehicle_attitude.h"
#include "topics/vehicle_global_position.h"
@@ -80,6 +81,7 @@ T Subscription<T>::getData() {
template class __EXPORT Subscription<parameter_update_s>;
template class __EXPORT Subscription<actuator_controls_s>;
template class __EXPORT Subscription<vehicle_gps_position_s>;
+template class __EXPORT Subscription<satellite_info_s>;
template class __EXPORT Subscription<sensor_combined_s>;
template class __EXPORT Subscription<vehicle_attitude_s>;
template class __EXPORT Subscription<vehicle_global_position_s>;
diff --git a/src/modules/uORB/module.mk b/src/modules/uORB/module.mk
index 0c29101fe..9385ce253 100644
--- a/src/modules/uORB/module.mk
+++ b/src/modules/uORB/module.mk
@@ -44,3 +44,5 @@ SRCS = uORB.cpp \
objects_common.cpp \
Publication.cpp \
Subscription.cpp
+
+MAXOPTIMIZATION = -Os
diff --git a/src/modules/uORB/objects_common.cpp b/src/modules/uORB/objects_common.cpp
index 90675bb2e..b91a00c1e 100644
--- a/src/modules/uORB/objects_common.cpp
+++ b/src/modules/uORB/objects_common.cpp
@@ -46,16 +46,23 @@
#include <drivers/drv_orb_dev.h>
#include <drivers/drv_mag.h>
-ORB_DEFINE(sensor_mag, struct mag_report);
+ORB_DEFINE(sensor_mag0, struct mag_report);
+ORB_DEFINE(sensor_mag1, struct mag_report);
+ORB_DEFINE(sensor_mag2, struct mag_report);
#include <drivers/drv_accel.h>
-ORB_DEFINE(sensor_accel, struct accel_report);
+ORB_DEFINE(sensor_accel0, struct accel_report);
+ORB_DEFINE(sensor_accel1, struct accel_report);
+ORB_DEFINE(sensor_accel2, struct accel_report);
#include <drivers/drv_gyro.h>
-ORB_DEFINE(sensor_gyro, struct gyro_report);
+ORB_DEFINE(sensor_gyro0, struct gyro_report);
+ORB_DEFINE(sensor_gyro1, struct gyro_report);
+ORB_DEFINE(sensor_gyro2, struct gyro_report);
#include <drivers/drv_baro.h>
-ORB_DEFINE(sensor_baro, struct baro_report);
+ORB_DEFINE(sensor_baro0, struct baro_report);
+ORB_DEFINE(sensor_baro1, struct baro_report);
#include <drivers/drv_range_finder.h>
ORB_DEFINE(sensor_range_finder, struct range_finder_report);
@@ -75,6 +82,9 @@ ORB_DEFINE(sensor_combined, struct sensor_combined_s);
#include "topics/vehicle_gps_position.h"
ORB_DEFINE(vehicle_gps_position, struct vehicle_gps_position_s);
+#include "topics/satellite_info.h"
+ORB_DEFINE(satellite_info, struct satellite_info_s);
+
#include "topics/home_position.h"
ORB_DEFINE(home_position, struct home_position_s);
@@ -182,8 +192,17 @@ ORB_DEFINE(actuator_outputs_1, struct actuator_outputs_s);
ORB_DEFINE(actuator_outputs_2, struct actuator_outputs_s);
ORB_DEFINE(actuator_outputs_3, struct actuator_outputs_s);
+#include "topics/multirotor_motor_limits.h"
+ORB_DEFINE(multirotor_motor_limits, struct multirotor_motor_limits_s);
+
#include "topics/telemetry_status.h"
-ORB_DEFINE(telemetry_status, struct telemetry_status_s);
+ORB_DEFINE(telemetry_status_0, struct telemetry_status_s);
+ORB_DEFINE(telemetry_status_1, struct telemetry_status_s);
+ORB_DEFINE(telemetry_status_2, struct telemetry_status_s);
+ORB_DEFINE(telemetry_status_3, struct telemetry_status_s);
+
+#include "topics/test_motor.h"
+ORB_DEFINE(test_motor, struct test_motor_s);
#include "topics/debug_key_value.h"
ORB_DEFINE(debug_key_value, struct debug_key_value_s);
@@ -199,3 +218,15 @@ ORB_DEFINE(encoders, struct encoders_s);
#include "topics/estimator_status.h"
ORB_DEFINE(estimator_status, struct estimator_status_report);
+
+#include "topics/vision_position_estimate.h"
+ORB_DEFINE(vision_position_estimate, struct vision_position_estimate);
+
+#include "topics/vehicle_force_setpoint.h"
+ORB_DEFINE(vehicle_force_setpoint, struct vehicle_force_setpoint_s);
+
+#include "topics/tecs_status.h"
+ORB_DEFINE(tecs_status, struct tecs_status_s);
+
+#include "topics/wind_estimate.h"
+ORB_DEFINE(wind_estimate, struct wind_estimate_s);
diff --git a/src/modules/uORB/topics/actuator_armed.h b/src/modules/uORB/topics/actuator_armed.h
index a98d3fc3a..0f6c9aca1 100644
--- a/src/modules/uORB/topics/actuator_armed.h
+++ b/src/modules/uORB/topics/actuator_armed.h
@@ -56,6 +56,7 @@ struct actuator_armed_s {
bool armed; /**< Set to true if system is armed */
bool ready_to_arm; /**< Set to true if system is ready to be armed */
bool lockdown; /**< Set to true if actuators are forced to being disabled (due to emergency or HIL) */
+ bool force_failsafe; /**< Set to true if the actuators are forced to the failsafe position */
};
/**
@@ -65,4 +66,4 @@ struct actuator_armed_s {
/* register this as object request broker structure */
ORB_DECLARE(actuator_armed);
-#endif \ No newline at end of file
+#endif
diff --git a/src/modules/uORB/topics/differential_pressure.h b/src/modules/uORB/topics/differential_pressure.h
index 01e14cda9..7342fcf04 100644
--- a/src/modules/uORB/topics/differential_pressure.h
+++ b/src/modules/uORB/topics/differential_pressure.h
@@ -54,11 +54,9 @@
struct differential_pressure_s {
uint64_t timestamp; /**< Microseconds since system boot, needed to integrate */
uint64_t error_count; /**< Number of errors detected by driver */
- float differential_pressure_pa; /**< Differential pressure reading */
float differential_pressure_raw_pa; /**< Raw differential pressure reading (may be negative) */
float differential_pressure_filtered_pa; /**< Low pass filtered differential pressure reading */
float max_differential_pressure_pa; /**< Maximum differential pressure reading */
- float voltage; /**< Voltage from analog airspeed sensors (voltage divider already compensated) */
float temperature; /**< Temperature provided by sensor, -1000.0f if unknown */
};
diff --git a/src/modules/uORB/topics/esc_status.h b/src/modules/uORB/topics/esc_status.h
index 628824efa..b45c49788 100644
--- a/src/modules/uORB/topics/esc_status.h
+++ b/src/modules/uORB/topics/esc_status.h
@@ -78,6 +78,7 @@ enum ESC_CONNECTION_TYPE {
/**
* Electronic speed controller status.
+ * Unsupported float fields should be assigned NaN.
*/
struct esc_status_s {
/* use of a counter and timestamp recommended (but not necessary) */
@@ -89,17 +90,17 @@ struct esc_status_s {
enum ESC_CONNECTION_TYPE esc_connectiontype; /**< how ESCs connected to the system */
struct {
- uint16_t esc_address; /**< Address of current ESC (in most cases 1-8 / must be set by driver) */
enum ESC_VENDOR esc_vendor; /**< Vendor of current ESC */
- uint16_t esc_version; /**< Version of current ESC - if supported */
- uint16_t esc_voltage; /**< Voltage measured from current ESC - if supported */
- uint16_t esc_current; /**< Current measured from current ESC (100mA steps) - if supported */
- uint16_t esc_rpm; /**< RPM measured from current ESC - if supported */
- uint16_t esc_temperature; /**< Temperature measured from current ESC - if supported */
- float esc_setpoint; /**< setpoint of current ESC */
+ uint32_t esc_errorcount; /**< Number of reported errors by ESC - if supported */
+ int32_t esc_rpm; /**< Motor RPM, negative for reverse rotation [RPM] - if supported */
+ float esc_voltage; /**< Voltage measured from current ESC [V] - if supported */
+ float esc_current; /**< Current measured from current ESC [A] - if supported */
+ float esc_temperature; /**< Temperature measured from current ESC [degC] - if supported */
+ float esc_setpoint; /**< setpoint of current ESC */
uint16_t esc_setpoint_raw; /**< setpoint of current ESC (Value sent to ESC) */
- uint16_t esc_state; /**< State of ESC - depend on Vendor */
- uint16_t esc_errorcount; /**< Number of reported errors by ESC - if supported */
+ uint16_t esc_address; /**< Address of current ESC (in most cases 1-8 / must be set by driver) */
+ uint16_t esc_version; /**< Version of current ESC - if supported */
+ uint16_t esc_state; /**< State of ESC - depend on Vendor */
} esc[CONNECTED_ESC_MAX];
};
diff --git a/src/modules/uORB/topics/estimator_status.h b/src/modules/uORB/topics/estimator_status.h
index 5530cdb21..7f26b505b 100644
--- a/src/modules/uORB/topics/estimator_status.h
+++ b/src/modules/uORB/topics/estimator_status.h
@@ -64,9 +64,9 @@ struct estimator_status_report {
uint64_t timestamp; /**< Timestamp in microseconds since boot */
float states[32]; /**< Internal filter states */
float n_states; /**< Number of states effectively used */
- bool states_nan; /**< If set to true, one of the states is NaN */
- bool covariance_nan; /**< If set to true, the covariance matrix went NaN */
- bool kalman_gain_nan; /**< If set to true, the Kalman gain matrix went NaN */
+ uint8_t nan_flags; /**< Bitmask to indicate NaN states */
+ uint8_t health_flags; /**< Bitmask to indicate sensor health states (vel, pos, hgt) */
+ uint8_t timeout_flags; /**< Bitmask to indicate timeout flags (vel, pos, hgt) */
};
diff --git a/src/modules/uORB/topics/home_position.h b/src/modules/uORB/topics/home_position.h
index 08d11abae..70071130d 100644
--- a/src/modules/uORB/topics/home_position.h
+++ b/src/modules/uORB/topics/home_position.h
@@ -59,10 +59,13 @@ struct home_position_s
{
uint64_t timestamp; /**< Timestamp (microseconds since system boot) */
- //bool altitude_is_relative; // TODO what means home relative altitude? we need clear definition of reference altitude then
double lat; /**< Latitude in degrees */
double lon; /**< Longitude in degrees */
float alt; /**< Altitude in meters */
+
+ float x; /**< X coordinate in meters */
+ float y; /**< Y coordinate in meters */
+ float z; /**< Z coordinate in meters */
};
/**
diff --git a/src/modules/uORB/topics/manual_control_setpoint.h b/src/modules/uORB/topics/manual_control_setpoint.h
index 910b8a623..50b7bd9e5 100644
--- a/src/modules/uORB/topics/manual_control_setpoint.h
+++ b/src/modules/uORB/topics/manual_control_setpoint.h
@@ -47,7 +47,7 @@
* Switch position
*/
typedef enum {
- SWITCH_POS_NONE = 0, /**< switch is not mapped */
+ SWITCH_POS_NONE = 0, /**< switch is not mapped */
SWITCH_POS_ON, /**< switch activated (value = 1) */
SWITCH_POS_MIDDLE, /**< middle position (value = 0) */
SWITCH_POS_OFF /**< switch not activated (value = -1) */
@@ -93,12 +93,13 @@ struct manual_control_setpoint_s {
float aux4; /**< default function: camera roll */
float aux5; /**< default function: payload drop */
- switch_pos_t mode_switch; /**< main mode 3 position switch (mandatory): _MANUAL_, ASSIST, AUTO */
- switch_pos_t return_switch; /**< return to launch 2 position switch (mandatory): _NORMAL_, RTL */
- switch_pos_t posctl_switch; /**< position control 2 position switch (optional): _ALTCTL_, POSCTL */
- switch_pos_t loiter_switch; /**< loiter 2 position switch (optional): _MISSION_, LOITER */
- switch_pos_t acro_switch; /**< acro 2 position switch (optional): _MANUAL_, ACRO */
-}; /**< manual control inputs */
+ switch_pos_t mode_switch; /**< main mode 3 position switch (mandatory): _MANUAL_, ASSIST, AUTO */
+ switch_pos_t return_switch; /**< return to launch 2 position switch (mandatory): _NORMAL_, RTL */
+ switch_pos_t posctl_switch; /**< position control 2 position switch (optional): _ALTCTL_, POSCTL */
+ switch_pos_t loiter_switch; /**< loiter 2 position switch (optional): _MISSION_, LOITER */
+ switch_pos_t acro_switch; /**< acro 2 position switch (optional): _MANUAL_, ACRO */
+ switch_pos_t offboard_switch; /**< offboard 2 position switch (optional): _NORMAL_, OFFBOARD */
+};
/**
* @}
diff --git a/src/modules/uORB/topics/mission.h b/src/modules/uORB/topics/mission.h
index ef4bc1def..e4b72f87c 100644
--- a/src/modules/uORB/topics/mission.h
+++ b/src/modules/uORB/topics/mission.h
@@ -1,9 +1,6 @@
/****************************************************************************
*
* Copyright (C) 2012 PX4 Development Team. All rights reserved.
- * Author: @author Thomas Gubler <thomasgubler@student.ethz.ch>
- * @author Julian Oes <joes@student.ethz.ch>
- * @author Lorenz Meier <lm@inf.ethz.ch>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -37,6 +34,9 @@
/**
* @file mission.h
* Definition of a mission consisting of mission items.
+ * @author Thomas Gubler <thomasgubler@student.ethz.ch>
+ * @author Julian Oes <joes@student.ethz.ch>
+ * @author Lorenz Meier <lm@inf.ethz.ch>
*/
#ifndef TOPIC_MISSION_H_
@@ -50,6 +50,7 @@
/* compatible to mavlink MAV_CMD */
enum NAV_CMD {
+ NAV_CMD_IDLE=0,
NAV_CMD_WAYPOINT=16,
NAV_CMD_LOITER_UNLIMITED=17,
NAV_CMD_LOITER_TURN_COUNT=18,
@@ -58,7 +59,8 @@ enum NAV_CMD {
NAV_CMD_LAND=21,
NAV_CMD_TAKEOFF=22,
NAV_CMD_ROI=80,
- NAV_CMD_PATHPLANNING=81
+ NAV_CMD_PATHPLANNING=81,
+ NAV_CMD_DO_JUMP=177
};
enum ORIGIN {
@@ -91,13 +93,20 @@ struct mission_item_s {
float pitch_min; /**< minimal pitch angle for fixed wing takeoff waypoints */
bool autocontinue; /**< true if next waypoint should follow after this one */
enum ORIGIN origin; /**< where the waypoint has been generated */
+ int do_jump_mission_index; /**< index where the do jump will go to */
+ unsigned do_jump_repeat_count; /**< how many times do jump needs to be done */
+ unsigned do_jump_current_count; /**< count how many times the jump has been done */
};
+/**
+ * This topic used to notify navigator about mission changes, mission itself and new mission state
+ * must be stored in dataman before publication.
+ */
struct mission_s
{
- int dataman_id; /**< default -1, there are two offboard storage places in the dataman: 0 or 1 */
- unsigned count; /**< count of the missions stored in the datamanager */
- int current_index; /**< default -1, start at the one changed latest */
+ int dataman_id; /**< default 0, there are two offboard storage places in the dataman: 0 or 1 */
+ unsigned count; /**< count of the missions stored in the dataman */
+ int current_seq; /**< default -1, start at the one changed latest */
};
/**
diff --git a/src/modules/uORB/topics/mission_result.h b/src/modules/uORB/topics/mission_result.h
index 7c3921198..c7d25d1f0 100644
--- a/src/modules/uORB/topics/mission_result.h
+++ b/src/modules/uORB/topics/mission_result.h
@@ -53,9 +53,13 @@
struct mission_result_s
{
- bool mission_reached; /**< true if mission has been reached */
- unsigned mission_index_reached; /**< index of the mission which has been reached */
- unsigned index_current_mission; /**< index of the current mission */
+ unsigned seq_reached; /**< Sequence of the mission item which has been reached */
+ unsigned seq_current; /**< Sequence of the current mission item */
+ bool reached; /**< true if mission has been reached */
+ bool finished; /**< true if mission has been completed */
+ bool stay_in_failsafe; /**< true if the commander should not switch out of the failsafe mode*/
+ bool geofence_violated; /**< true if the geofence is violated */
+ bool flight_termination; /**< true if the navigator demands a flight termination from the commander app */
};
/**
diff --git a/src/modules/uORB/topics/multirotor_motor_limits.h b/src/modules/uORB/topics/multirotor_motor_limits.h
new file mode 100644
index 000000000..44c51e4d9
--- /dev/null
+++ b/src/modules/uORB/topics/multirotor_motor_limits.h
@@ -0,0 +1,69 @@
+/****************************************************************************
+ *
+ * Copyright (C) 2012-2013 PX4 Development Team. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/**
+ * @file multirotor_motor_limits.h
+ *
+ * Definition of multirotor_motor_limits topic
+ */
+
+#ifndef MULTIROTOR_MOTOR_LIMITS_H_
+#define MULTIROTOR_MOTOR_LIMITS_H_
+
+#include "../uORB.h"
+#include <stdint.h>
+
+/**
+ * @addtogroup topics
+ * @{
+ */
+
+/**
+ * Motor limits
+ */
+struct multirotor_motor_limits_s {
+ uint8_t roll_pitch : 1; // roll/pitch limit reached
+ uint8_t yaw : 1; // yaw limit reached
+ uint8_t throttle_lower : 1; // lower throttle limit reached
+ uint8_t throttle_upper : 1; // upper throttle limit reached
+ uint8_t reserved : 4;
+};
+
+/**
+ * @}
+ */
+
+/* register this as object request broker structure */
+ORB_DECLARE(multirotor_motor_limits);
+
+#endif
diff --git a/src/modules/uORB/topics/offboard_control_setpoint.h b/src/modules/uORB/topics/offboard_control_setpoint.h
index 68d3e494b..72a28e501 100644
--- a/src/modules/uORB/topics/offboard_control_setpoint.h
+++ b/src/modules/uORB/topics/offboard_control_setpoint.h
@@ -53,11 +53,42 @@ enum OFFBOARD_CONTROL_MODE {
OFFBOARD_CONTROL_MODE_DIRECT = 0,
OFFBOARD_CONTROL_MODE_DIRECT_RATES = 1,
OFFBOARD_CONTROL_MODE_DIRECT_ATTITUDE = 2,
- OFFBOARD_CONTROL_MODE_DIRECT_VELOCITY = 3,
- OFFBOARD_CONTROL_MODE_DIRECT_POSITION = 4,
- OFFBOARD_CONTROL_MODE_ATT_YAW_RATE = 5,
- OFFBOARD_CONTROL_MODE_ATT_YAW_POS = 6,
- OFFBOARD_CONTROL_MODE_MULTIROTOR_SIMPLE = 7, /**< roll / pitch rotated aligned to the takeoff orientation, throttle stabilized, yaw pos */
+ OFFBOARD_CONTROL_MODE_DIRECT_LOCAL_NED = 3,
+ OFFBOARD_CONTROL_MODE_DIRECT_LOCAL_OFFSET_NED = 4,
+ OFFBOARD_CONTROL_MODE_DIRECT_BODY_NED = 5,
+ OFFBOARD_CONTROL_MODE_DIRECT_BODY_OFFSET_NED = 6,
+ OFFBOARD_CONTROL_MODE_DIRECT_GLOBAL = 7,
+ OFFBOARD_CONTROL_MODE_DIRECT_FORCE = 8,
+ OFFBOARD_CONTROL_MODE_ATT_YAW_RATE = 9,
+ OFFBOARD_CONTROL_MODE_ATT_YAW_POS = 10,
+ OFFBOARD_CONTROL_MODE_MULTIROTOR_SIMPLE = 11 /**< roll / pitch rotated aligned to the takeoff orientation, throttle stabilized, yaw pos */
+};
+
+enum OFFBOARD_CONTROL_FRAME {
+ OFFBOARD_CONTROL_FRAME_LOCAL_NED = 0,
+ OFFBOARD_CONTROL_FRAME_LOCAL_OFFSET_NED = 1,
+ OFFBOARD_CONTROL_FRAME_BODY_NED = 2,
+ OFFBOARD_CONTROL_FRAME_BODY_OFFSET_NED = 3,
+ OFFBOARD_CONTROL_FRAME_GLOBAL = 4
+};
+
+/* mappings for the ignore bitmask */
+enum {OFB_IGN_BIT_POS_X,
+ OFB_IGN_BIT_POS_Y,
+ OFB_IGN_BIT_POS_Z,
+ OFB_IGN_BIT_VEL_X,
+ OFB_IGN_BIT_VEL_Y,
+ OFB_IGN_BIT_VEL_Z,
+ OFB_IGN_BIT_ACC_X,
+ OFB_IGN_BIT_ACC_Y,
+ OFB_IGN_BIT_ACC_Z,
+ OFB_IGN_BIT_BODYRATE_X,
+ OFB_IGN_BIT_BODYRATE_Y,
+ OFB_IGN_BIT_BODYRATE_Z,
+ OFB_IGN_BIT_ATT,
+ OFB_IGN_BIT_THRUST,
+ OFB_IGN_BIT_YAW,
+ OFB_IGN_BIT_YAWRATE,
};
/**
@@ -69,11 +100,22 @@ struct offboard_control_setpoint_s {
uint64_t timestamp;
enum OFFBOARD_CONTROL_MODE mode; /**< The current control inputs mode */
- bool armed; /**< Armed flag set, yes / no */
- float p1; /**< ailerons roll / roll rate input */
- float p2; /**< elevator / pitch / pitch rate */
- float p3; /**< rudder / yaw rate / yaw */
- float p4; /**< throttle / collective thrust / altitude */
+
+ double position[3]; /**< lat, lon, alt / x, y, z */
+ float velocity[3]; /**< x vel, y vel, z vel */
+ float acceleration[3]; /**< x acc, y acc, z acc */
+ float attitude[4]; /**< attitude of vehicle (quaternion) */
+ float attitude_rate[3]; /**< body angular rates (x, y, z) */
+ float thrust; /**< thrust */
+ float yaw; /**< yaw: this is the yaw from the position_target message
+ (not from the full attitude_target message) */
+ float yaw_rate; /**< yaw rate: this is the yaw from the position_target message
+ (not from the full attitude_target message) */
+
+ uint16_t ignore; /**< if field i is set to true, the value should be ignored, see definition at top of file
+ for mapping */
+
+ bool isForceSetpoint; /**< the acceleration vector should be interpreted as force */
float override_mode_switch;
@@ -87,6 +129,147 @@ struct offboard_control_setpoint_s {
* @}
*/
+/**
+ * Returns true if the position setpoint at index should be ignored
+ */
+inline bool offboard_control_sp_ignore_position(const struct offboard_control_setpoint_s &offboard_control_sp, int index) {
+ return (bool)(offboard_control_sp.ignore & (1 << (OFB_IGN_BIT_POS_X + index)));
+}
+
+/**
+ * Returns true if all position setpoints should be ignored
+ */
+inline bool offboard_control_sp_ignore_position_all(const struct offboard_control_setpoint_s &offboard_control_sp) {
+ for (int i = 0; i < 3; i++) {
+ if (!offboard_control_sp_ignore_position(offboard_control_sp, i)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+/**
+ * Returns true if some position setpoints should be ignored
+ */
+inline bool offboard_control_sp_ignore_position_some(const struct offboard_control_setpoint_s &offboard_control_sp) {
+ for (int i = 0; i < 3; i++) {
+ if (offboard_control_sp_ignore_position(offboard_control_sp, i)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Returns true if the velocity setpoint at index should be ignored
+ */
+inline bool offboard_control_sp_ignore_velocity(const struct offboard_control_setpoint_s &offboard_control_sp, int index) {
+ return (bool)(offboard_control_sp.ignore & (1 << (OFB_IGN_BIT_VEL_X + index)));
+}
+
+/**
+ * Returns true if all velocity setpoints should be ignored
+ */
+inline bool offboard_control_sp_ignore_velocity_all(const struct offboard_control_setpoint_s &offboard_control_sp) {
+ for (int i = 0; i < 3; i++) {
+ if (!offboard_control_sp_ignore_velocity(offboard_control_sp, i)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+/**
+ * Returns true if some velocity setpoints should be ignored
+ */
+inline bool offboard_control_sp_ignore_velocity_some(const struct offboard_control_setpoint_s &offboard_control_sp) {
+ for (int i = 0; i < 3; i++) {
+ if (offboard_control_sp_ignore_velocity(offboard_control_sp, i)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Returns true if the acceleration setpoint at index should be ignored
+ */
+inline bool offboard_control_sp_ignore_acceleration(const struct offboard_control_setpoint_s &offboard_control_sp, int index) {
+ return (bool)(offboard_control_sp.ignore & (1 << (OFB_IGN_BIT_ACC_X + index)));
+}
+
+/**
+ * Returns true if all acceleration setpoints should be ignored
+ */
+inline bool offboard_control_sp_ignore_acceleration_all(const struct offboard_control_setpoint_s &offboard_control_sp) {
+ for (int i = 0; i < 3; i++) {
+ if (!offboard_control_sp_ignore_acceleration(offboard_control_sp, i)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+/**
+ * Returns true if some acceleration setpoints should be ignored
+ */
+inline bool offboard_control_sp_ignore_acceleration_some(const struct offboard_control_setpoint_s &offboard_control_sp) {
+ for (int i = 0; i < 3; i++) {
+ if (offboard_control_sp_ignore_acceleration(offboard_control_sp, i)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Returns true if the bodyrate setpoint at index should be ignored
+ */
+inline bool offboard_control_sp_ignore_bodyrates(const struct offboard_control_setpoint_s &offboard_control_sp, int index) {
+ return (bool)(offboard_control_sp.ignore & (1 << (OFB_IGN_BIT_BODYRATE_X + index)));
+}
+
+/**
+ * Returns true if some of the bodyrate setpoints should be ignored
+ */
+inline bool offboard_control_sp_ignore_bodyrates_some(const struct offboard_control_setpoint_s &offboard_control_sp) {
+ for (int i = 0; i < 3; i++) {
+ if (offboard_control_sp_ignore_bodyrates(offboard_control_sp, i)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Returns true if the attitude setpoint should be ignored
+ */
+inline bool offboard_control_sp_ignore_attitude(const struct offboard_control_setpoint_s &offboard_control_sp) {
+ return (bool)(offboard_control_sp.ignore & (1 << OFB_IGN_BIT_ATT));
+}
+
+/**
+ * Returns true if the thrust setpoint should be ignored
+ */
+inline bool offboard_control_sp_ignore_thrust(const struct offboard_control_setpoint_s &offboard_control_sp) {
+ return (bool)(offboard_control_sp.ignore & (1 << OFB_IGN_BIT_THRUST));
+}
+
+/**
+ * Returns true if the yaw setpoint should be ignored
+ */
+inline bool offboard_control_sp_ignore_yaw(const struct offboard_control_setpoint_s &offboard_control_sp) {
+ return (bool)(offboard_control_sp.ignore & (1 << OFB_IGN_BIT_YAW));
+}
+
+/**
+ * Returns true if the yaw rate setpoint should be ignored
+ */
+inline bool offboard_control_sp_ignore_yawrate(const struct offboard_control_setpoint_s &offboard_control_sp) {
+ return (bool)(offboard_control_sp.ignore & (1 << OFB_IGN_BIT_YAWRATE));
+}
+
+
/* register this as object request broker structure */
ORB_DECLARE(offboard_control_setpoint);
diff --git a/src/modules/uORB/topics/position_setpoint_triplet.h b/src/modules/uORB/topics/position_setpoint_triplet.h
index 34aaa30dd..cb2262534 100644
--- a/src/modules/uORB/topics/position_setpoint_triplet.h
+++ b/src/modules/uORB/topics/position_setpoint_triplet.h
@@ -1,9 +1,6 @@
/****************************************************************************
*
* Copyright (C) 2013 PX4 Development Team. All rights reserved.
- * Author: @author Thomas Gubler <thomasgubler@student.ethz.ch>
- * @author Julian Oes <joes@student.ethz.ch>
- * @author Lorenz Meier <lm@inf.ethz.ch>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -37,6 +34,10 @@
/**
* @file mission_item_triplet.h
* Definition of the global WGS84 position setpoint uORB topic.
+ *
+ * @author Thomas Gubler <thomasgubler@student.ethz.ch>
+ * @author Julian Oes <joes@student.ethz.ch>
+ * @author Lorenz Meier <lm@inf.ethz.ch>
*/
#ifndef TOPIC_MISSION_ITEM_TRIPLET_H_
@@ -45,7 +46,6 @@
#include <stdint.h>
#include <stdbool.h>
#include "../uORB.h"
-#include <navigator/navigator_state.h>
/**
* @addtogroup topics
@@ -54,24 +54,42 @@
enum SETPOINT_TYPE
{
- SETPOINT_TYPE_NORMAL = 0, /**< normal setpoint */
- SETPOINT_TYPE_LOITER, /**< loiter setpoint */
- SETPOINT_TYPE_TAKEOFF, /**< takeoff setpoint */
- SETPOINT_TYPE_LAND, /**< land setpoint, altitude must be ignored, vehicle must descend until landing */
- SETPOINT_TYPE_IDLE, /**< do nothing, switch off motors or keep at idle speed (MC) */
+ SETPOINT_TYPE_POSITION = 0, /**< position setpoint */
+ SETPOINT_TYPE_VELOCITY, /**< velocity setpoint */
+ SETPOINT_TYPE_LOITER, /**< loiter setpoint */
+ SETPOINT_TYPE_TAKEOFF, /**< takeoff setpoint */
+ SETPOINT_TYPE_LAND, /**< land setpoint, altitude must be ignored, descend until landing */
+ SETPOINT_TYPE_IDLE, /**< do nothing, switch off motors or keep at idle speed (MC) */
+ SETPOINT_TYPE_OFFBOARD, /**< setpoint in NED frame (x, y, z, vx, vy, vz) set by offboard */
};
struct position_setpoint_s
{
bool valid; /**< true if setpoint is valid */
enum SETPOINT_TYPE type; /**< setpoint type to adjust behavior of position controller */
+ float x; /**< local position setpoint in m in NED */
+ float y; /**< local position setpoint in m in NED */
+ float z; /**< local position setpoint in m in NED */
+ bool position_valid; /**< true if local position setpoint valid */
+ float vx; /**< local velocity setpoint in m/s in NED */
+ float vy; /**< local velocity setpoint in m/s in NED */
+ float vz; /**< local velocity setpoint in m/s in NED */
+ bool velocity_valid; /**< true if local velocity setpoint valid */
double lat; /**< latitude, in deg */
double lon; /**< longitude, in deg */
float alt; /**< altitude AMSL, in m */
float yaw; /**< yaw (only for multirotors), in rad [-PI..PI), NaN = hold current yaw */
+ bool yaw_valid; /**< true if yaw setpoint valid */
+ float yawspeed; /**< yawspeed (only for multirotors, in rad/s) */
+ bool yawspeed_valid; /**< true if yawspeed setpoint valid */
float loiter_radius; /**< loiter radius (only for fixed wing), in m */
int8_t loiter_direction; /**< loiter direction: 1 = CW, -1 = CCW */
float pitch_min; /**< minimal pitch angle for fixed wing takeoff waypoints */
+ float a_x; //**< acceleration x setpoint */
+ float a_y; //**< acceleration y setpoint */
+ float a_z; //**< acceleration z setpoint */
+ bool acceleration_valid; //*< true if acceleration setpoint is valid/should be used */
+ bool acceleration_is_force; //*< interprete acceleration as force */
};
/**
@@ -85,7 +103,7 @@ struct position_setpoint_triplet_s
struct position_setpoint_s current;
struct position_setpoint_s next;
- nav_state_t nav_state; /**< navigation state */
+ unsigned nav_state; /**< report the navigation state */
};
/**
diff --git a/src/modules/uORB/topics/rc_channels.h b/src/modules/uORB/topics/rc_channels.h
index 370c54442..16916cc4d 100644
--- a/src/modules/uORB/topics/rc_channels.h
+++ b/src/modules/uORB/topics/rc_channels.h
@@ -1,6 +1,6 @@
/****************************************************************************
*
- * Copyright (c) 2012, 2013 PX4 Development Team. All rights reserved.
+ * Copyright (c) 2012-2014 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -34,6 +34,8 @@
/**
* @file rc_channels.h
* Definition of the rc_channels uORB topic.
+ *
+ * @deprecated DO NOT USE FOR NEW CODE
*/
#ifndef RC_CHANNELS_H_
@@ -43,59 +45,45 @@
#include "../uORB.h"
/**
- * The number of RC channel inputs supported.
- * Current (Q4/2013) radios support up to 18 channels,
- * leaving at a sane value of 16.
- * This number can be greater then number of RC channels,
- * because single RC channel can be mapped to multiple
- * functions, e.g. for various mode switches.
- */
-#define RC_CHANNELS_MAPPED_MAX 16
-
-/**
* This defines the mapping of the RC functions.
* The value assigned to the specific function corresponds to the entry of
- * the channel array chan[].
+ * the channel array channels[].
*/
enum RC_CHANNELS_FUNCTION {
THROTTLE = 0,
- ROLL = 1,
- PITCH = 2,
- YAW = 3,
- MODE = 4,
- RETURN = 5,
- POSCTL = 6,
- LOITER = 7,
- OFFBOARD_MODE = 8,
- ACRO = 9,
- FLAPS = 10,
- AUX_1 = 11,
- AUX_2 = 12,
- AUX_3 = 13,
- AUX_4 = 14,
- AUX_5 = 15,
- RC_CHANNELS_FUNCTION_MAX /**< indicates the number of functions. There can be more functions than RC channels. */
+ ROLL,
+ PITCH,
+ YAW,
+ MODE,
+ RETURN,
+ POSCTL,
+ LOITER,
+ OFFBOARD,
+ ACRO,
+ FLAPS,
+ AUX_1,
+ AUX_2,
+ AUX_3,
+ AUX_4,
+ AUX_5
};
+// MAXIMUM FUNCTIONS IS != MAXIMUM RC INPUT CHANNELS
+
+#define RC_CHANNELS_FUNCTION_MAX 18
+
/**
* @addtogroup topics
* @{
*/
-
struct rc_channels_s {
-
- uint64_t timestamp; /**< In microseconds since boot time. */
- uint64_t timestamp_last_valid; /**< timestamp of last valid RC signal. */
- struct {
- float scaled; /**< Scaled to -1..1 (throttle: 0..1) */
- } chan[RC_CHANNELS_MAPPED_MAX];
- uint8_t chan_count; /**< number of valid channels */
-
- /*String array to store the names of the functions*/
- char function_name[RC_CHANNELS_FUNCTION_MAX][20];
- int8_t function[RC_CHANNELS_FUNCTION_MAX];
- uint8_t rssi; /**< Overall receive signal strength */
- bool signal_lost; /**< control signal lost, should be checked together with topic timeout */
+ uint64_t timestamp; /**< Timestamp in microseconds since boot time */
+ uint64_t timestamp_last_valid; /**< Timestamp of last valid RC signal */
+ float channels[RC_CHANNELS_FUNCTION_MAX]; /**< Scaled to -1..1 (throttle: 0..1) */
+ uint8_t channel_count; /**< Number of valid channels */
+ int8_t function[RC_CHANNELS_FUNCTION_MAX]; /**< Functions mapping */
+ uint8_t rssi; /**< Receive signal strength index */
+ bool signal_lost; /**< Control signal lost, should be checked together with topic timeout */
}; /**< radio control channels. */
/**
diff --git a/src/modules/uORB/topics/satellite_info.h b/src/modules/uORB/topics/satellite_info.h
new file mode 100644
index 000000000..37c2faa96
--- /dev/null
+++ b/src/modules/uORB/topics/satellite_info.h
@@ -0,0 +1,89 @@
+/****************************************************************************
+ *
+ * Copyright (C) 2014 PX4 Development Team. All rights reserved.
+ * Author: @author Thomas Gubler <thomasgubler@student.ethz.ch>
+ * @author Julian Oes <joes@student.ethz.ch>
+ * @author Lorenz Meier <lm@inf.ethz.ch>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/**
+ * @file satellite_info.h
+ * Definition of the GNSS satellite info uORB topic.
+ */
+
+#ifndef TOPIC_SAT_INFO_H_
+#define TOPIC_SAT_INFO_H_
+
+#include <stdint.h>
+#include "../uORB.h"
+
+/**
+ * @addtogroup topics
+ * @{
+ */
+
+/**
+ * GNSS Satellite Info.
+ */
+
+#define SAT_INFO_MAX_SATELLITES 20
+
+struct satellite_info_s {
+ uint64_t timestamp; /**< Timestamp of satellite info */
+ uint8_t count; /**< Number of satellites in satellite info */
+ uint8_t svid[SAT_INFO_MAX_SATELLITES]; /**< Space vehicle ID [1..255], see scheme below */
+ uint8_t used[SAT_INFO_MAX_SATELLITES]; /**< 0: Satellite not used, 1: used for navigation */
+ uint8_t elevation[SAT_INFO_MAX_SATELLITES]; /**< Elevation (0: right on top of receiver, 90: on the horizon) of satellite */
+ uint8_t azimuth[SAT_INFO_MAX_SATELLITES]; /**< Direction of satellite, 0: 0 deg, 255: 360 deg. */
+ uint8_t snr[SAT_INFO_MAX_SATELLITES]; /**< dBHz, Signal to noise ratio of satellite C/N0, range 0..99, zero when not tracking this satellite. */
+};
+
+/**
+ * NAV_SVINFO space vehicle ID (svid) scheme according to u-blox protocol specs
+ * u-bloxM8-V15_ReceiverDescriptionProtocolSpec_Public_(UBX-13003221).pdf
+ *
+ * GPS 1-32
+ * SBAS 120-158
+ * Galileo 211-246
+ * BeiDou 159-163, 33-64
+ * QZSS 193-197
+ * GLONASS 65-96, 255
+ *
+ */
+
+/**
+ * @}
+ */
+
+/* register this as object request broker structure */
+ORB_DECLARE(satellite_info);
+
+#endif
diff --git a/src/modules/uORB/topics/sensor_combined.h b/src/modules/uORB/topics/sensor_combined.h
index fa3367de9..06dfcdab3 100644
--- a/src/modules/uORB/topics/sensor_combined.h
+++ b/src/modules/uORB/topics/sensor_combined.h
@@ -95,6 +95,30 @@ struct sensor_combined_s {
float magnetometer_cuttoff_freq_hz; /**< Internal analog low pass frequency of sensor */
uint64_t magnetometer_timestamp; /**< Magnetometer timestamp */
+ int16_t gyro1_raw[3]; /**< Raw sensor values of angular velocity */
+ float gyro1_rad_s[3]; /**< Angular velocity in radian per seconds */
+ uint64_t gyro1_timestamp; /**< Gyro timestamp */
+
+ int16_t accelerometer1_raw[3]; /**< Raw acceleration in NED body frame */
+ float accelerometer1_m_s2[3]; /**< Acceleration in NED body frame, in m/s^2 */
+ uint64_t accelerometer1_timestamp; /**< Accelerometer timestamp */
+
+ int16_t magnetometer1_raw[3]; /**< Raw magnetic field in NED body frame */
+ float magnetometer1_ga[3]; /**< Magnetic field in NED body frame, in Gauss */
+ uint64_t magnetometer1_timestamp; /**< Magnetometer timestamp */
+
+ int16_t gyro2_raw[3]; /**< Raw sensor values of angular velocity */
+ float gyro2_rad_s[3]; /**< Angular velocity in radian per seconds */
+ uint64_t gyro2_timestamp; /**< Gyro timestamp */
+
+ int16_t accelerometer2_raw[3]; /**< Raw acceleration in NED body frame */
+ float accelerometer2_m_s2[3]; /**< Acceleration in NED body frame, in m/s^2 */
+ uint64_t accelerometer2_timestamp; /**< Accelerometer timestamp */
+
+ int16_t magnetometer2_raw[3]; /**< Raw magnetic field in NED body frame */
+ float magnetometer2_ga[3]; /**< Magnetic field in NED body frame, in Gauss */
+ uint64_t magnetometer2_timestamp; /**< Magnetometer timestamp */
+
float baro_pres_mbar; /**< Barometric pressure, already temp. comp. */
float baro_alt_meter; /**< Altitude, already temp. comp. */
float baro_temp_celcius; /**< Temperature in degrees celsius */
diff --git a/src/modules/uORB/topics/tecs_status.h b/src/modules/uORB/topics/tecs_status.h
new file mode 100644
index 000000000..ddca19d61
--- /dev/null
+++ b/src/modules/uORB/topics/tecs_status.h
@@ -0,0 +1,94 @@
+/****************************************************************************
+ *
+ * Copyright (c) 2012-2014 PX4 Development Team. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/**
+ * @file vehicle_global_position.h
+ * Definition of the global fused WGS84 position uORB topic.
+ *
+ * @author Thomas Gubler <thomasgubler@student.ethz.ch>
+ */
+
+#ifndef TECS_STATUS_T_H_
+#define TECS_STATUS_T_H_
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "../uORB.h"
+
+/**
+ * @addtogroup topics
+ * @{
+ */
+
+typedef enum {
+ TECS_MODE_NORMAL = 0,
+ TECS_MODE_UNDERSPEED,
+ TECS_MODE_TAKEOFF,
+ TECS_MODE_LAND,
+ TECS_MODE_LAND_THROTTLELIM,
+ TECS_MODE_BAD_DESCENT,
+ TECS_MODE_CLIMBOUT
+} tecs_mode;
+
+ /**
+ * Internal values of the (m)TECS fixed wing speed alnd altitude control system
+ */
+struct tecs_status_s {
+ uint64_t timestamp; /**< timestamp, in microseconds since system start */
+
+ float altitudeSp;
+ float altitude_filtered;
+ float flightPathAngleSp;
+ float flightPathAngle;
+ float flightPathAngleFiltered;
+ float airspeedSp;
+ float airspeed_filtered;
+ float airspeedDerivativeSp;
+ float airspeedDerivative;
+
+ float totalEnergyRateSp;
+ float totalEnergyRate;
+ float energyDistributionRateSp;
+ float energyDistributionRate;
+
+ tecs_mode mode;
+};
+
+/**
+ * @}
+ */
+
+/* register this as object request broker structure */
+ORB_DECLARE(tecs_status);
+
+#endif
diff --git a/src/modules/uORB/topics/telemetry_status.h b/src/modules/uORB/topics/telemetry_status.h
index 76693c46e..93193f32b 100644
--- a/src/modules/uORB/topics/telemetry_status.h
+++ b/src/modules/uORB/topics/telemetry_status.h
@@ -57,6 +57,8 @@ enum TELEMETRY_STATUS_RADIO_TYPE {
struct telemetry_status_s {
uint64_t timestamp;
+ uint64_t heartbeat_time; /**< Time of last received heartbeat from remote system */
+ uint64_t telem_time; /**< Time of last received telemetry status packet, 0 for none */
enum TELEMETRY_STATUS_RADIO_TYPE type; /**< type of the radio hardware */
uint8_t rssi; /**< local signal strength */
uint8_t remote_rssi; /**< remote signal strength */
@@ -65,12 +67,36 @@ struct telemetry_status_s {
uint8_t noise; /**< background noise level */
uint8_t remote_noise; /**< remote background noise level */
uint8_t txbuf; /**< how full the tx buffer is as a percentage */
+ uint8_t system_id; /**< system id of the remote system */
+ uint8_t component_id; /**< component id of the remote system */
};
/**
* @}
*/
-ORB_DECLARE(telemetry_status);
+ORB_DECLARE(telemetry_status_0);
+ORB_DECLARE(telemetry_status_1);
+ORB_DECLARE(telemetry_status_2);
+ORB_DECLARE(telemetry_status_3);
+
+#define TELEMETRY_STATUS_ORB_ID_NUM 4
+
+static const struct orb_metadata *telemetry_status_orb_id[TELEMETRY_STATUS_ORB_ID_NUM] = {
+ ORB_ID(telemetry_status_0),
+ ORB_ID(telemetry_status_1),
+ ORB_ID(telemetry_status_2),
+ ORB_ID(telemetry_status_3),
+};
+
+// This is a hack to quiet an unused-variable warning for when telemetry_status.h is
+// included but telemetry_status_orb_id is not referenced. The inline works if you
+// choose to use it, but you can continue to just directly index into the array as well.
+// If you don't use the inline this ends up being a no-op with no additional code emitted.
+extern inline const struct orb_metadata *telemetry_status_orb_id_lookup(size_t index);
+extern inline const struct orb_metadata *telemetry_status_orb_id_lookup(size_t index)
+{
+ return telemetry_status_orb_id[index];
+}
#endif /* TOPIC_TELEMETRY_STATUS_H */
diff --git a/src/modules/uORB/topics/test_motor.h b/src/modules/uORB/topics/test_motor.h
new file mode 100644
index 000000000..2dd90e69d
--- /dev/null
+++ b/src/modules/uORB/topics/test_motor.h
@@ -0,0 +1,70 @@
+/****************************************************************************
+ *
+ * Copyright (C) 2014 PX4 Development Team. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/**
+ * @file test_motor.h
+ *
+ * Test a single drive motor while the vehicle is disarmed
+ *
+ * Publishing values to this topic causes a single motor to spin, e.g. for
+ * ground test purposes. This topic will be ignored while the vehicle is armed.
+ *
+ */
+
+#ifndef TOPIC_TEST_MOTOR_H
+#define TOPIC_TEST_MOTOR_H
+
+#include <stdint.h>
+#include "../uORB.h"
+
+#define NUM_MOTOR_OUTPUTS 8
+
+/**
+ * @addtogroup topics
+ * @{
+ */
+
+struct test_motor_s {
+ uint64_t timestamp; /**< output timestamp in us since system boot */
+ unsigned motor_number; /**< number of motor to spin */
+ float value; /**< output power, range [0..1] */
+};
+
+/**
+ * @}
+ */
+
+/* actuator output sets; this list can be expanded as more drivers emerge */
+ORB_DECLARE(test_motor);
+
+#endif
diff --git a/src/modules/uORB/topics/vehicle_attitude_setpoint.h b/src/modules/uORB/topics/vehicle_attitude_setpoint.h
index d526a8ff2..8446e9c6e 100644
--- a/src/modules/uORB/topics/vehicle_attitude_setpoint.h
+++ b/src/modules/uORB/topics/vehicle_attitude_setpoint.h
@@ -72,6 +72,8 @@ struct vehicle_attitude_setpoint_s {
float thrust; /**< Thrust in Newton the power system should generate */
bool roll_reset_integral; /**< Reset roll integral part (navigation logic change) */
+ bool pitch_reset_integral; /**< Reset pitch integral part (navigation logic change) */
+ bool yaw_reset_integral; /**< Reset yaw integral part (navigation logic change) */
};
diff --git a/src/modules/uORB/topics/vehicle_command.h b/src/modules/uORB/topics/vehicle_command.h
index c21a29b13..f264accbb 100644
--- a/src/modules/uORB/topics/vehicle_command.h
+++ b/src/modules/uORB/topics/vehicle_command.h
@@ -1,9 +1,6 @@
/****************************************************************************
*
- * Copyright (C) 2008-2012 PX4 Development Team. All rights reserved.
- * Author: @author Thomas Gubler <thomasgubler@student.ethz.ch>
- * @author Julian Oes <joes@student.ethz.ch>
- * @author Lorenz Meier <lm@inf.ethz.ch>
+ * Copyright (C) 2012-2014 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -37,6 +34,10 @@
/**
* @file vehicle_command.h
* Definition of the vehicle command uORB topic.
+ *
+ * @author Thomas Gubler <thomasgubler@student.ethz.ch>
+ * @author Julian Oes <joes@student.ethz.ch>
+ * @author Lorenz Meier <lm@inf.ethz.ch>
*/
#ifndef TOPIC_VEHICLE_COMMAND_H_
@@ -52,6 +53,9 @@
* but can contain additional ones.
*/
enum VEHICLE_CMD {
+ VEHICLE_CMD_CUSTOM_0 = 0, /* test command */
+ VEHICLE_CMD_CUSTOM_1 = 1, /* test command */
+ VEHICLE_CMD_CUSTOM_2 = 2, /* test command */
VEHICLE_CMD_NAV_WAYPOINT = 16, /* Navigate to MISSION. |Hold time in decimal seconds. (ignored by fixed wing, time to stay at MISSION for rotary wing)| Acceptance radius in meters (if the sphere with this radius is hit, the MISSION counts as reached)| 0 to pass through the WP, if > 0 radius in meters to pass by WP. Positive value for clockwise orbit, negative value for counter-clockwise orbit. Allows trajectory control.| Desired yaw angle at MISSION (rotary wing)| Latitude| Longitude| Altitude| */
VEHICLE_CMD_NAV_LOITER_UNLIM = 17, /* Loiter around this MISSION an unlimited amount of time |Empty| Empty| Radius around MISSION, in meters. If positive loiter clockwise, else counter-clockwise| Desired yaw angle.| Latitude| Longitude| Altitude| */
VEHICLE_CMD_NAV_LOITER_TURNS = 18, /* Loiter around this MISSION for X turns |Turns| Empty| Radius around MISSION, in meters. If positive loiter clockwise, else counter-clockwise| Desired yaw angle.| Latitude| Longitude| Altitude| */
@@ -61,6 +65,9 @@ enum VEHICLE_CMD {
VEHICLE_CMD_NAV_TAKEOFF = 22, /* Takeoff from ground / hand |Minimum pitch (if airspeed sensor present), desired pitch without sensor| Empty| Empty| Yaw angle (if magnetometer present), ignored without magnetometer| Latitude| Longitude| Altitude| */
VEHICLE_CMD_NAV_ROI = 80, /* Sets the region of interest (ROI) for a sensor set or the vehicle itself. This can then be used by the vehicles control system to control the vehicle attitude and the attitude of various sensors such as cameras. |Region of intereset mode. (see MAV_ROI enum)| MISSION index/ target ID. (see MAV_ROI enum)| ROI index (allows a vehicle to manage multiple ROI's)| Empty| x the location of the fixed ROI (see MAV_FRAME)| y| z| */
VEHICLE_CMD_NAV_PATHPLANNING = 81, /* Control autonomous path planning on the MAV. |0: Disable local obstacle avoidance / local path planning (without resetting map), 1: Enable local path planning, 2: Enable and reset local path planning| 0: Disable full path planning (without resetting map), 1: Enable, 2: Enable and reset map/occupancy grid, 3: Enable and reset planned route, but not occupancy grid| Empty| Yaw angle at goal, in compass degrees, [0..360]| Latitude/X of goal| Longitude/Y of goal| Altitude/Z of goal| */
+ VEHICLE_CMD_NAV_GUIDED_LIMITS=90, /* set limits for external control |timeout - maximum time (in seconds) that external controller will be allowed to control vehicle. 0 means no timeout| absolute altitude min (in meters, WGS84) - if vehicle moves below this alt, the command will be aborted and the mission will continue. 0 means no lower altitude limit| absolute altitude max (in meters)- if vehicle moves above this alt, the command will be aborted and the mission will continue. 0 means no upper altitude limit| horizontal move limit (in meters, WGS84) - if vehicle moves more than this distance from it's location at the moment the command was executed, the command will be aborted and the mission will continue. 0 means no horizontal altitude limit| Empty| Empty| Empty| */
+ VEHICLE_CMD_NAV_GUIDED_MASTER=91, /* set id of master controller |System ID| Component ID| Empty| Empty| Empty| Empty| Empty| */
+ VEHICLE_CMD_NAV_GUIDED_ENABLE=92, /* hand control over to an external controller |On / Off (> 0.5f on)| Empty| Empty| Empty| Empty| Empty| Empty| */
VEHICLE_CMD_NAV_LAST = 95, /* NOP - This command is only used to mark the upper limit of the NAV/ACTION commands in the enumeration |Empty| Empty| Empty| Empty| Empty| Empty| Empty| */
VEHICLE_CMD_CONDITION_DELAY = 112, /* Delay mission state machine. |Delay in seconds (decimal)| Empty| Empty| Empty| Empty| Empty| Empty| */
VEHICLE_CMD_CONDITION_CHANGE_ALT = 113, /* Ascend/descend at rate. Delay mission state machine until desired altitude reached. |Descent / Ascend rate (m/s)| Empty| Empty| Empty| Empty| Empty| Finish Altitude| */
@@ -76,6 +83,7 @@ enum VEHICLE_CMD {
VEHICLE_CMD_DO_REPEAT_RELAY = 182, /* Cycle a relay on and off for a desired number of cyles with a desired period. |Relay number| Cycle count| Cycle time (seconds, decimal)| Empty| Empty| Empty| Empty| */
VEHICLE_CMD_DO_SET_SERVO = 183, /* Set a servo to a desired PWM value. |Servo number| PWM (microseconds, 1000 to 2000 typical)| Empty| Empty| Empty| Empty| Empty| */
VEHICLE_CMD_DO_REPEAT_SERVO = 184, /* Cycle a between its nominal setting and a desired PWM for a desired number of cycles with a desired period. |Servo number| PWM (microseconds, 1000 to 2000 typical)| Cycle count| Cycle time (seconds)| Empty| Empty| Empty| */
+ VEHICLE_CMD_DO_FLIGHTTERMINATION=185, /* Terminate flight immediately |Flight termination activated if > 0.5| Empty| Empty| Empty| Empty| Empty| Empty| */
VEHICLE_CMD_DO_CONTROL_VIDEO = 200, /* Control onboard camera system. |Camera ID (-1 for all)| Transmission: 0: disabled, 1: enabled compressed, 2: enabled raw| Transmission mode: 0: video stream, >0: single images every n seconds (decimal)| Recording: 0: disabled, 1: enabled compressed, 2: enabled raw| Empty| Empty| Empty| */
VEHICLE_CMD_DO_LAST = 240, /* NOP - This command is only used to mark the upper limit of the DO commands in the enumeration |Empty| Empty| Empty| Empty| Empty| Empty| Empty| */
VEHICLE_CMD_PREFLIGHT_CALIBRATION = 241, /* Trigger calibration. This command will be only accepted if in pre-flight mode. |Gyro calibration: 0: no, 1: yes| Magnetometer calibration: 0: no, 1: yes| Ground pressure: 0: no, 1: yes| Radio calibration: 0: no, 1: yes| Accelerometer calibration: 0: no, 1: yes| Empty| Empty| */
@@ -86,7 +94,8 @@ enum VEHICLE_CMD {
VEHICLE_CMD_MISSION_START = 300, /* start running a mission |first_item: the first mission item to run| last_item: the last mission item to run (after this item is run, the mission ends)| */
VEHICLE_CMD_COMPONENT_ARM_DISARM = 400, /* Arms / Disarms a component |1 to arm, 0 to disarm| */
VEHICLE_CMD_START_RX_PAIR = 500, /* Starts receiver pairing |0:Spektrum| 0:Spektrum DSM2, 1:Spektrum DSMX| */
- VEHICLE_CMD_ENUM_END = 501, /* | */
+ VEHICLE_CMD_PAYLOAD_PREPARE_DEPLOY = 30001, /**< Prepare a payload deployment in the flight plan */
+ VEHICLE_CMD_PAYLOAD_CONTROL_DEPLOY = 30002 /**< Control a pre-programmed payload deployment */
};
/**
@@ -114,8 +123,8 @@ struct vehicle_command_s {
float param2; /**< Parameter 2, as defined by MAVLink VEHICLE_CMD enum. */
float param3; /**< Parameter 3, as defined by MAVLink VEHICLE_CMD enum. */
float param4; /**< Parameter 4, as defined by MAVLink VEHICLE_CMD enum. */
- float param5; /**< Parameter 5, as defined by MAVLink VEHICLE_CMD enum. */
- float param6; /**< Parameter 6, as defined by MAVLink VEHICLE_CMD enum. */
+ double param5; /**< Parameter 5, as defined by MAVLink VEHICLE_CMD enum. */
+ double param6; /**< Parameter 6, as defined by MAVLink VEHICLE_CMD enum. */
float param7; /**< Parameter 7, as defined by MAVLink VEHICLE_CMD enum. */
enum VEHICLE_CMD command; /**< Command ID, as defined MAVLink by VEHICLE_CMD enum. */
uint8_t target_system; /**< System which should execute the command */
diff --git a/src/modules/uORB/topics/vehicle_control_mode.h b/src/modules/uORB/topics/vehicle_control_mode.h
index ea554006f..ca7705456 100644
--- a/src/modules/uORB/topics/vehicle_control_mode.h
+++ b/src/modules/uORB/topics/vehicle_control_mode.h
@@ -74,8 +74,10 @@ struct vehicle_control_mode_s {
bool flag_control_manual_enabled; /**< true if manual input is mixed in */
bool flag_control_auto_enabled; /**< true if onboard autopilot should act */
+ bool flag_control_offboard_enabled; /**< true if offboard control should be used */
bool flag_control_rates_enabled; /**< true if rates are stabilized */
bool flag_control_attitude_enabled; /**< true if attitude stabilization is mixed in */
+ bool flag_control_force_enabled; /**< true if force control is mixed in */
bool flag_control_velocity_enabled; /**< true if horizontal velocity (implies direction) is controlled */
bool flag_control_position_enabled; /**< true if position is controlled */
bool flag_control_altitude_enabled; /**< true if altitude is controlled */
diff --git a/src/modules/uORB/topics/vehicle_force_setpoint.h b/src/modules/uORB/topics/vehicle_force_setpoint.h
new file mode 100644
index 000000000..e3a7360b2
--- /dev/null
+++ b/src/modules/uORB/topics/vehicle_force_setpoint.h
@@ -0,0 +1,65 @@
+/****************************************************************************
+ *
+ * Copyright (C) 2014 PX4 Development Team. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/**
+ * @file vehicle_force_setpoint.h
+ * @author Thomas Gubler <thomasgubler@gmail.com>
+ * Definition of force (NED) setpoint uORB topic. Typically this can be used
+ * by a position control app together with an attitude control app.
+ */
+
+#ifndef TOPIC_VEHICLE_FORCE_SETPOINT_H_
+#define TOPIC_VEHICLE_FORCE_SETPOINT_H_
+
+#include "../uORB.h"
+
+/**
+ * @addtogroup topics
+ * @{
+ */
+
+struct vehicle_force_setpoint_s {
+ float x; /**< in N NED */
+ float y; /**< in N NED */
+ float z; /**< in N NED */
+ float yaw; /**< right-hand rotation around downward axis (rad, equivalent to Tait-Bryan yaw) */
+}; /**< Desired force in NED frame */
+
+/**
+ * @}
+ */
+
+/* register this as object request broker structure */
+ORB_DECLARE(vehicle_force_setpoint);
+
+#endif
diff --git a/src/modules/uORB/topics/vehicle_global_position.h b/src/modules/uORB/topics/vehicle_global_position.h
index 4897ca737..c3bb3b893 100644
--- a/src/modules/uORB/topics/vehicle_global_position.h
+++ b/src/modules/uORB/topics/vehicle_global_position.h
@@ -36,7 +36,7 @@
* Definition of the global fused WGS84 position uORB topic.
*
* @author Thomas Gubler <thomasgubler@student.ethz.ch>
- * @author Julian Oes <joes@student.ethz.ch>
+ * @author Julian Oes <julian@oes.ch>
* @author Lorenz Meier <lm@inf.ethz.ch>
*/
@@ -61,18 +61,19 @@
* e.g. control inputs of the vehicle in a Kalman-filter implementation.
*/
struct vehicle_global_position_s {
- uint64_t timestamp; /**< Time of this estimate, in microseconds since system start */
-
- uint64_t time_gps_usec; /**< GPS timestamp in microseconds */
+ uint64_t timestamp; /**< Time of this estimate, in microseconds since system start */
+ uint64_t time_gps_usec; /**< GPS timestamp in microseconds */
double lat; /**< Latitude in degrees */
double lon; /**< Longitude in degrees */
float alt; /**< Altitude AMSL in meters */
- float vel_n; /**< Ground north velocity, m/s */
- float vel_e; /**< Ground east velocity, m/s */
- float vel_d; /**< Ground downside velocity, m/s */
+ float vel_n; /**< Ground north velocity, m/s */
+ float vel_e; /**< Ground east velocity, m/s */
+ float vel_d; /**< Ground downside velocity, m/s */
float yaw; /**< Yaw in radians -PI..+PI. */
- float eph;
- float epv;
+ float eph; /**< Standard deviation of position estimate horizontally */
+ float epv; /**< Standard deviation of position vertically */
+ float terrain_alt; /**< Terrain altitude in m, WGS84 */
+ bool terrain_alt_valid; /**< Terrain altitude estimate is valid */
};
/**
diff --git a/src/modules/uORB/topics/vehicle_gps_position.h b/src/modules/uORB/topics/vehicle_gps_position.h
index 5924a324d..31e616f4f 100644
--- a/src/modules/uORB/topics/vehicle_gps_position.h
+++ b/src/modules/uORB/topics/vehicle_gps_position.h
@@ -61,12 +61,11 @@ struct vehicle_gps_position_s {
uint64_t timestamp_variance;
float s_variance_m_s; /**< speed accuracy estimate m/s */
- float p_variance_m; /**< position accuracy estimate m */
float c_variance_rad; /**< course accuracy estimate rad */
- uint8_t fix_type; /**< 0-1: no fix, 2: 2D fix, 3: 3D fix. Some applications will not use the value of this field unless it is at least two, so always correctly fill in the fix. */
+ uint8_t fix_type; /**< 0-1: no fix, 2: 2D fix, 3: 3D fix, 4: RTCM code differential, 5: Real-Time Kinematic, float, 6: Real-Time Kinematic, fixed, 8: Extrapolated. Some applications will not use the value of this field unless it is at least two, so always correctly fill in the fix. */
- float eph_m; /**< GPS HDOP horizontal dilution of position in m */
- float epv_m; /**< GPS VDOP horizontal dilution of position in m */
+ float eph; /**< GPS HDOP horizontal dilution of position in m */
+ float epv; /**< GPS VDOP horizontal dilution of position in m */
unsigned noise_per_ms; /**< */
unsigned jamming_indicator; /**< */
@@ -82,14 +81,7 @@ struct vehicle_gps_position_s {
uint64_t timestamp_time; /**< Timestamp for time information */
uint64_t time_gps_usec; /**< Timestamp (microseconds in GPS format), this is the timestamp which comes from the gps module */
- uint64_t timestamp_satellites; /**< Timestamp for sattelite information */
- uint8_t satellites_visible; /**< Number of satellites visible. If unknown, set to 255 */
- uint8_t satellite_prn[20]; /**< Global satellite ID */
- uint8_t satellite_used[20]; /**< 0: Satellite not used, 1: used for localization */
- uint8_t satellite_elevation[20]; /**< Elevation (0: right on top of receiver, 90: on the horizon) of satellite */
- uint8_t satellite_azimuth[20]; /**< Direction of satellite, 0: 0 deg, 255: 360 deg. */
- uint8_t satellite_snr[20]; /**< dBHz, Signal to noise ratio of satellite C/N0, range 0..99, zero when not tracking this satellite. */
- bool satellite_info_available; /**< 0 for no info, 1 for info available */
+ uint8_t satellites_used; /**< Number of satellites used */
};
/**
diff --git a/src/modules/uORB/topics/vehicle_local_position_setpoint.h b/src/modules/uORB/topics/vehicle_local_position_setpoint.h
index 8988a0330..6766bb58a 100644
--- a/src/modules/uORB/topics/vehicle_local_position_setpoint.h
+++ b/src/modules/uORB/topics/vehicle_local_position_setpoint.h
@@ -50,11 +50,12 @@
*/
struct vehicle_local_position_setpoint_s {
+ uint64_t timestamp; /**< timestamp of the setpoint */
float x; /**< in meters NED */
float y; /**< in meters NED */
float z; /**< in meters NED */
float yaw; /**< in radians NED -PI..+PI */
-}; /**< Local position in NED frame to go to */
+}; /**< Local position in NED frame */
/**
* @}
diff --git a/src/modules/uORB/topics/vehicle_status.h b/src/modules/uORB/topics/vehicle_status.h
index ea20a317a..91491c148 100644
--- a/src/modules/uORB/topics/vehicle_status.h
+++ b/src/modules/uORB/topics/vehicle_status.h
@@ -1,10 +1,6 @@
/****************************************************************************
*
- * Copyright (C) 2012 PX4 Development Team. All rights reserved.
- * Author: @author Lorenz Meier <lm@inf.ethz.ch>
- * @author Petri Tanskanen <petri.tanskanen@inf.ethz.ch>
- * @author Thomas Gubler <thomasgubler@student.ethz.ch>
- * @author Julian Oes <joes@student.ethz.ch>
+ * Copyright (C) 2012 - 2014 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -45,6 +41,11 @@
* All apps should write to subsystem_info:
*
* (any app) --> subsystem_info (published) --> (commander app state machine) --> vehicle_status --> (mavlink app)
+ *
+ * @author Lorenz Meier <lm@inf.ethz.ch>
+ * @author Petri Tanskanen <petri.tanskanen@inf.ethz.ch>
+ * @author Thomas Gubler <thomasgubler@student.ethz.ch>
+ * @author Julian Oes <julian@oes.ch>
*/
#ifndef VEHICLE_STATUS_H_
@@ -54,19 +55,22 @@
#include <stdbool.h>
#include "../uORB.h"
-#include <navigator/navigator_state.h>
-
/**
* @addtogroup topics @{
*/
-/* main state machine */
+/**
+ * Main state, i.e. what user wants. Controlled by RC or from ground station via telemetry link.
+ */
typedef enum {
MAIN_STATE_MANUAL = 0,
MAIN_STATE_ALTCTL,
MAIN_STATE_POSCTL,
- MAIN_STATE_AUTO,
+ MAIN_STATE_AUTO_MISSION,
+ MAIN_STATE_AUTO_LOITER,
+ MAIN_STATE_AUTO_RTL,
MAIN_STATE_ACRO,
+ MAIN_STATE_OFFBOARD,
MAIN_STATE_MAX
} main_state_t;
@@ -80,7 +84,7 @@ typedef enum {
ARMING_STATE_STANDBY_ERROR,
ARMING_STATE_REBOOT,
ARMING_STATE_IN_AIR_RESTORE,
- ARMING_STATE_MAX
+ ARMING_STATE_MAX,
} arming_state_t;
typedef enum {
@@ -88,13 +92,27 @@ typedef enum {
HIL_STATE_ON
} hil_state_t;
+/**
+ * Navigation state, i.e. "what should vehicle do".
+ */
typedef enum {
- FAILSAFE_STATE_NORMAL = 0, /**< Normal operation */
- FAILSAFE_STATE_RTL, /**< Return To Launch */
- FAILSAFE_STATE_LAND, /**< Land without position control */
- FAILSAFE_STATE_TERMINATION, /**< Disable motors and use parachute, can't be recovered */
- FAILSAFE_STATE_MAX
-} failsafe_state_t;
+ NAVIGATION_STATE_MANUAL = 0, /**< Manual mode */
+ NAVIGATION_STATE_ALTCTL, /**< Altitude control mode */
+ NAVIGATION_STATE_POSCTL, /**< Position control mode */
+ NAVIGATION_STATE_AUTO_MISSION, /**< Auto mission mode */
+ NAVIGATION_STATE_AUTO_LOITER, /**< Auto loiter mode */
+ NAVIGATION_STATE_AUTO_RTL, /**< Auto return to launch mode */
+ NAVIGATION_STATE_AUTO_RCRECOVER, /**< RC recover mode */
+ NAVIGATION_STATE_AUTO_RTGS, /**< Auto return to groundstation on data link loss */
+ NAVIGATION_STATE_AUTO_LANDENGFAIL, /**< Auto land on engine failure */
+ NAVIGATION_STATE_AUTO_LANDGPSFAIL, /**< Auto land on gps failure (e.g. open loop loiter down) */
+ NAVIGATION_STATE_ACRO, /**< Acro mode */
+ NAVIGATION_STATE_LAND, /**< Land mode */
+ NAVIGATION_STATE_DESCEND, /**< Descend mode (no position control) */
+ NAVIGATION_STATE_TERMINATION, /**< Termination mode */
+ NAVIGATION_STATE_OFFBOARD,
+ NAVIGATION_STATE_MAX,
+} navigation_state_t;
enum VEHICLE_MODE_FLAG {
VEHICLE_MODE_FLAG_SAFETY_ARMED = 128,
@@ -154,12 +172,11 @@ struct vehicle_status_s {
uint16_t counter; /**< incremented by the writing thread everytime new data is stored */
uint64_t timestamp; /**< in microseconds since system start, is set whenever the writing thread stores new data */
- main_state_t main_state; /**< main state machine */
- unsigned int set_nav_state; /**< set navigation state machine to specified value */
- uint64_t set_nav_state_timestamp; /**< timestamp of latest change of set_nav_state */
+ main_state_t main_state; /**< main state machine */
+ navigation_state_t nav_state; /**< set navigation state machine to specified value */
arming_state_t arming_state; /**< current arming state */
- hil_state_t hil_state; /**< current hil state */
- failsafe_state_t failsafe_state; /**< current failsafe state */
+ hil_state_t hil_state; /**< current hil state */
+ bool failsafe; /**< true if system is in failsafe state */
int32_t system_type; /**< system type, inspired by MAVLink's VEHICLE_TYPE enum */
int32_t system_id; /**< system id, inspired by MAVLink's system ID field */
@@ -172,22 +189,37 @@ struct vehicle_status_s {
bool condition_system_sensors_initialized;
bool condition_system_returned_to_home;
bool condition_auto_mission_available;
- bool condition_global_position_valid; /**< set to true by the commander app if the quality of the gps signal is good enough to use it in the position estimator */
+ bool condition_global_position_valid; /**< set to true by the commander app if the quality of the position estimate is good enough to use it for navigation */
bool condition_launch_position_valid; /**< indicates a valid launch position */
bool condition_home_position_valid; /**< indicates a valid home position (a valid home position is not always a valid launch) */
bool condition_local_position_valid;
bool condition_local_altitude_valid;
bool condition_airspeed_valid; /**< set to true by the commander app if there is a valid airspeed measurement available */
bool condition_landed; /**< true if vehicle is landed, always true if disarmed */
+ bool condition_power_input_valid; /**< set if input power is valid */
+ float avionics_power_rail_voltage; /**< voltage of the avionics power rail */
bool rc_signal_found_once;
bool rc_signal_lost; /**< true if RC reception lost */
+ bool rc_signal_lost_cmd; /**< true if RC lost mode is commanded */
bool rc_input_blocked; /**< set if RC input should be ignored */
+ bool data_link_lost; /**< datalink to GCS lost */
+ bool data_link_lost_cmd; /**< datalink to GCS lost mode commanded */
+ uint8_t data_link_lost_counter; /**< counts unique data link lost events */
+ bool engine_failure; /** Set to true if an engine failure is detected */
+ bool engine_failure_cmd; /** Set to true if an engine failure mode is commanded */
+ bool gps_failure; /** Set to true if a gps failure is detected */
+ bool gps_failure_cmd; /** Set to true if a gps failure mode is commanded */
+
+ bool barometer_failure; /** Set to true if a barometer failure is detected */
+
bool offboard_control_signal_found_once;
bool offboard_control_signal_lost;
bool offboard_control_signal_weak;
uint64_t offboard_control_signal_lost_interval; /**< interval in microseconds without an offboard control message */
+ bool offboard_control_set_by_command; /**< true if the offboard mode was set by a mavlink command
+ and should not be overridden by RC */
/* see SYS_STATUS mavlink message for the following */
uint32_t onboard_control_sensors_present;
@@ -206,6 +238,11 @@ struct vehicle_status_s {
uint16_t errors_count2;
uint16_t errors_count3;
uint16_t errors_count4;
+
+ bool circuit_breaker_engaged_power_check;
+ bool circuit_breaker_engaged_airspd_check;
+ bool circuit_breaker_engaged_enginefailure_check;
+ bool circuit_breaker_engaged_gpsfailure_check;
};
/**
diff --git a/src/modules/uORB/topics/vision_position_estimate.h b/src/modules/uORB/topics/vision_position_estimate.h
new file mode 100644
index 000000000..74c96cf2e
--- /dev/null
+++ b/src/modules/uORB/topics/vision_position_estimate.h
@@ -0,0 +1,82 @@
+/****************************************************************************
+ *
+ * Copyright (c) 2014 PX4 Development Team. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/**
+ * @file vision_position_estimate.h
+ * Vision based position estimate
+ */
+
+#ifndef TOPIC_VISION_POSITION_ESTIMATE_H_
+#define TOPIC_VISION_POSITION_ESTIMATE_H_
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "../uORB.h"
+
+/**
+ * @addtogroup topics
+ * @{
+ */
+
+/**
+ * Vision based position estimate in NED frame
+ */
+struct vision_position_estimate {
+
+ unsigned id; /**< ID of the estimator, commonly the component ID of the incoming message */
+
+ uint64_t timestamp_boot; /**< time of this estimate, in microseconds since system start */
+ uint64_t timestamp_computer; /**< timestamp provided by the companion computer, in us */
+
+ float x; /**< X position in meters in NED earth-fixed frame */
+ float y; /**< Y position in meters in NED earth-fixed frame */
+ float z; /**< Z position in meters in NED earth-fixed frame (negative altitude) */
+
+ float vx; /**< X velocity in meters per second in NED earth-fixed frame */
+ float vy; /**< Y velocity in meters per second in NED earth-fixed frame */
+ float vz; /**< Z velocity in meters per second in NED earth-fixed frame */
+
+ float q[4]; /**< Estimated attitude as quaternion */
+
+ // XXX Add covariances here
+
+};
+
+/**
+ * @}
+ */
+
+/* register this as object request broker structure */
+ORB_DECLARE(vision_position_estimate);
+
+#endif /* TOPIC_VISION_POSITION_ESTIMATE_H_ */
diff --git a/src/modules/uORB/topics/wind_estimate.h b/src/modules/uORB/topics/wind_estimate.h
new file mode 100644
index 000000000..58333a64f
--- /dev/null
+++ b/src/modules/uORB/topics/wind_estimate.h
@@ -0,0 +1,68 @@
+/****************************************************************************
+ *
+ * Copyright (c) 2014 PX4 Development Team. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/**
+ * @file wind_estimate.h
+ *
+ * Wind estimate topic topic
+ *
+ */
+
+#ifndef TOPIC_WIND_ESTIMATE_H
+#define TOPIC_WIND_ESTIMATE_H
+
+#include <stdint.h>
+#include "../uORB.h"
+
+/**
+ * @addtogroup topics
+ * @{
+ */
+
+/** Wind estimate */
+struct wind_estimate_s {
+
+ uint64_t timestamp; /**< Microseconds since system boot */
+ float windspeed_north; /**< Wind component in north / X direction */
+ float windspeed_east; /**< Wind component in east / Y direction */
+ float covariance_north; /**< Uncertainty - set to zero (no uncertainty) if not estimated */
+ float covariance_east; /**< Uncertainty - set to zero (no uncertainty) if not estimated */
+};
+
+/**
+ * @}
+ */
+
+ORB_DECLARE(wind_estimate);
+
+#endif \ No newline at end of file
diff --git a/src/modules/uavcan/.gitignore b/src/modules/uavcan/.gitignore
new file mode 100644
index 000000000..24fbf171f
--- /dev/null
+++ b/src/modules/uavcan/.gitignore
@@ -0,0 +1 @@
+./dsdlc_generated/
diff --git a/src/modules/uavcan/actuators/esc.cpp b/src/modules/uavcan/actuators/esc.cpp
new file mode 100644
index 000000000..1d23099f3
--- /dev/null
+++ b/src/modules/uavcan/actuators/esc.cpp
@@ -0,0 +1,175 @@
+/****************************************************************************
+ *
+ * Copyright (C) 2014 PX4 Development Team. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/**
+ * @file esc.cpp
+ *
+ * @author Pavel Kirienko <pavel.kirienko@gmail.com>
+ */
+
+#include "esc.hpp"
+#include <systemlib/err.h>
+
+
+#define MOTOR_BIT(x) (1<<(x))
+
+UavcanEscController::UavcanEscController(uavcan::INode &node) :
+ _node(node),
+ _uavcan_pub_raw_cmd(node),
+ _uavcan_sub_status(node),
+ _orb_timer(node)
+{
+}
+
+UavcanEscController::~UavcanEscController()
+{
+ perf_free(_perfcnt_invalid_input);
+ perf_free(_perfcnt_scaling_error);
+}
+
+int UavcanEscController::init()
+{
+ // ESC status subscription
+ int res = _uavcan_sub_status.start(StatusCbBinder(this, &UavcanEscController::esc_status_sub_cb));
+ if (res < 0)
+ {
+ warnx("ESC status sub failed %i", res);
+ return res;
+ }
+
+ // ESC status will be relayed from UAVCAN bus into ORB at this rate
+ _orb_timer.setCallback(TimerCbBinder(this, &UavcanEscController::orb_pub_timer_cb));
+ _orb_timer.startPeriodic(uavcan::MonotonicDuration::fromMSec(1000 / ESC_STATUS_UPDATE_RATE_HZ));
+
+ return res;
+}
+
+void UavcanEscController::update_outputs(float *outputs, unsigned num_outputs)
+{
+ if ((outputs == nullptr) || (num_outputs > uavcan::equipment::esc::RawCommand::FieldTypes::cmd::MaxSize)) {
+ perf_count(_perfcnt_invalid_input);
+ return;
+ }
+
+ /*
+ * Rate limiting - we don't want to congest the bus
+ */
+ const auto timestamp = _node.getMonotonicTime();
+ if ((timestamp - _prev_cmd_pub).toUSec() < (1000000 / MAX_RATE_HZ)) {
+ return;
+ }
+ _prev_cmd_pub = timestamp;
+
+ /*
+ * Fill the command message
+ * If unarmed, we publish an empty message anyway
+ */
+ uavcan::equipment::esc::RawCommand msg;
+
+ static const int cmd_max = uavcan::equipment::esc::RawCommand::FieldTypes::cmd::RawValueType::max();
+
+ for (unsigned i = 0; i < num_outputs; i++) {
+ if (_armed_mask & MOTOR_BIT(i)) {
+ float scaled = (outputs[i] + 1.0F) * 0.5F * cmd_max;
+ if (scaled < 1.0F) {
+ scaled = 1.0F; // Since we're armed, we don't want to stop it completely
+ }
+
+ if (scaled > cmd_max) {
+ scaled = cmd_max;
+ perf_count(_perfcnt_scaling_error);
+ }
+
+ msg.cmd.push_back(static_cast<int>(scaled));
+
+ _esc_status.esc[i].esc_setpoint_raw = abs(static_cast<int>(scaled));
+ } else {
+ msg.cmd.push_back(static_cast<unsigned>(0));
+ }
+ }
+
+ /*
+ * Publish the command message to the bus
+ * Note that for a quadrotor it takes one CAN frame
+ */
+ (void)_uavcan_pub_raw_cmd.broadcast(msg);
+}
+
+void UavcanEscController::arm_all_escs(bool arm)
+{
+ if (arm) {
+ _armed_mask = -1;
+ } else {
+ _armed_mask = 0;
+ }
+}
+
+void UavcanEscController::arm_single_esc(int num, bool arm)
+{
+ if (arm) {
+ _armed_mask = MOTOR_BIT(num);
+ } else {
+ _armed_mask = 0;
+ }
+}
+
+void UavcanEscController::esc_status_sub_cb(const uavcan::ReceivedDataStructure<uavcan::equipment::esc::Status> &msg)
+{
+ if (msg.esc_index < CONNECTED_ESC_MAX) {
+ _esc_status.esc_count = uavcan::max<int>(_esc_status.esc_count, msg.esc_index + 1);
+ _esc_status.timestamp = msg.getMonotonicTimestamp().toUSec();
+
+ auto &ref = _esc_status.esc[msg.esc_index];
+
+ ref.esc_address = msg.getSrcNodeID().get();
+
+ ref.esc_voltage = msg.voltage;
+ ref.esc_current = msg.current;
+ ref.esc_temperature = msg.temperature;
+ ref.esc_setpoint = msg.power_rating_pct;
+ ref.esc_rpm = msg.rpm;
+ ref.esc_errorcount = msg.error_count;
+ }
+}
+
+void UavcanEscController::orb_pub_timer_cb(const uavcan::TimerEvent&)
+{
+ _esc_status.counter += 1;
+ _esc_status.esc_connectiontype = ESC_CONNECTION_TYPE_CAN;
+
+ if (_esc_status_pub > 0) {
+ (void)orb_publish(ORB_ID(esc_status), _esc_status_pub, &_esc_status);
+ } else {
+ _esc_status_pub = orb_advertise(ORB_ID(esc_status), &_esc_status);
+ }
+}
diff --git a/src/modules/uavcan/actuators/esc.hpp b/src/modules/uavcan/actuators/esc.hpp
new file mode 100644
index 000000000..12c035542
--- /dev/null
+++ b/src/modules/uavcan/actuators/esc.hpp
@@ -0,0 +1,112 @@
+/****************************************************************************
+ *
+ * Copyright (C) 2014 PX4 Development Team. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/**
+ * @file esc.hpp
+ *
+ * UAVCAN <--> ORB bridge for ESC messages:
+ * uavcan.equipment.esc.RawCommand
+ * uavcan.equipment.esc.RPMCommand
+ * uavcan.equipment.esc.Status
+ *
+ * @author Pavel Kirienko <pavel.kirienko@gmail.com>
+ */
+
+#pragma once
+
+#include <uavcan/uavcan.hpp>
+#include <uavcan/equipment/esc/RawCommand.hpp>
+#include <uavcan/equipment/esc/Status.hpp>
+#include <systemlib/perf_counter.h>
+#include <uORB/topics/esc_status.h>
+
+
+class UavcanEscController
+{
+public:
+ UavcanEscController(uavcan::INode& node);
+ ~UavcanEscController();
+
+ int init();
+
+ void update_outputs(float *outputs, unsigned num_outputs);
+
+ void arm_all_escs(bool arm);
+ void arm_single_esc(int num, bool arm);
+
+private:
+ /**
+ * ESC status message reception will be reported via this callback.
+ */
+ void esc_status_sub_cb(const uavcan::ReceivedDataStructure<uavcan::equipment::esc::Status> &msg);
+
+ /**
+ * ESC status will be published to ORB from this callback (fixed rate).
+ */
+ void orb_pub_timer_cb(const uavcan::TimerEvent &event);
+
+
+ static constexpr unsigned MAX_RATE_HZ = 200; ///< XXX make this configurable
+ static constexpr unsigned ESC_STATUS_UPDATE_RATE_HZ = 10;
+
+ typedef uavcan::MethodBinder<UavcanEscController*,
+ void (UavcanEscController::*)(const uavcan::ReceivedDataStructure<uavcan::equipment::esc::Status>&)>
+ StatusCbBinder;
+
+ typedef uavcan::MethodBinder<UavcanEscController*, void (UavcanEscController::*)(const uavcan::TimerEvent&)>
+ TimerCbBinder;
+
+ bool _armed = false;
+ esc_status_s _esc_status = {};
+ orb_advert_t _esc_status_pub = -1;
+
+ /*
+ * libuavcan related things
+ */
+ uavcan::MonotonicTime _prev_cmd_pub; ///< rate limiting
+ uavcan::INode &_node;
+ uavcan::Publisher<uavcan::equipment::esc::RawCommand> _uavcan_pub_raw_cmd;
+ uavcan::Subscriber<uavcan::equipment::esc::Status, StatusCbBinder> _uavcan_sub_status;
+ uavcan::TimerEventForwarder<TimerCbBinder> _orb_timer;
+
+ /*
+ * ESC states
+ */
+ uint32_t _armed_mask = 0;
+
+ /*
+ * Perf counters
+ */
+ perf_counter_t _perfcnt_invalid_input = perf_alloc(PC_COUNT, "uavcan_esc_invalid_input");
+ perf_counter_t _perfcnt_scaling_error = perf_alloc(PC_COUNT, "uavcan_esc_scaling_error");
+};
diff --git a/src/modules/uavcan/module.mk b/src/modules/uavcan/module.mk
new file mode 100644
index 000000000..f92bc754f
--- /dev/null
+++ b/src/modules/uavcan/module.mk
@@ -0,0 +1,79 @@
+############################################################################
+#
+# Copyright (C) 2013 PX4 Development Team. All rights reserved.
+# Author: Pavel Kirienko <pavel.kirienko@gmail.com>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# 3. Neither the name PX4 nor the names of its contributors may be
+# used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+############################################################################
+
+#
+# UAVCAN <--> uORB bridge
+#
+
+MODULE_COMMAND = uavcan
+
+MAXOPTIMIZATION = -Os
+
+# Main
+SRCS += uavcan_main.cpp \
+ uavcan_clock.cpp \
+ uavcan_params.c
+
+# Actuators
+SRCS += actuators/esc.cpp
+
+# Sensors
+SRCS += sensors/sensor_bridge.cpp \
+ sensors/gnss.cpp \
+ sensors/mag.cpp \
+ sensors/baro.cpp
+
+#
+# libuavcan
+#
+include $(UAVCAN_DIR)/libuavcan/include.mk
+SRCS += $(LIBUAVCAN_SRC)
+INCLUDE_DIRS += $(LIBUAVCAN_INC)
+# Since actual compiler mode is C++11, the library will default to UAVCAN_CPP11, but it will fail to compile
+# because this platform lacks most of the standard library and STL. Hence we need to force C++03 mode.
+override EXTRADEFINES := $(EXTRADEFINES) -DUAVCAN_CPP_VERSION=UAVCAN_CPP03 -DUAVCAN_NO_ASSERTIONS
+
+#
+# libuavcan drivers for STM32
+#
+include $(UAVCAN_DIR)/libuavcan_drivers/stm32/driver/include.mk
+SRCS += $(LIBUAVCAN_STM32_SRC)
+INCLUDE_DIRS += $(LIBUAVCAN_STM32_INC)
+override EXTRADEFINES := $(EXTRADEFINES) -DUAVCAN_STM32_NUTTX -DUAVCAN_STM32_NUM_IFACES=2
+
+#
+# Invoke DSDL compiler
+#
+$(info $(shell $(LIBUAVCAN_DSDLC) $(UAVCAN_DSDL_DIR)))
+INCLUDE_DIRS += dsdlc_generated
diff --git a/src/modules/uavcan/sensors/baro.cpp b/src/modules/uavcan/sensors/baro.cpp
new file mode 100644
index 000000000..8741ae20d
--- /dev/null
+++ b/src/modules/uavcan/sensors/baro.cpp
@@ -0,0 +1,113 @@
+/****************************************************************************
+ *
+ * Copyright (C) 2014 PX4 Development Team. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/**
+ * @author Pavel Kirienko <pavel.kirienko@gmail.com>
+ */
+
+#include "baro.hpp"
+#include <cmath>
+
+static const orb_id_t BARO_TOPICS[2] = {
+ ORB_ID(sensor_baro0),
+ ORB_ID(sensor_baro1)
+};
+
+const char *const UavcanBarometerBridge::NAME = "baro";
+
+UavcanBarometerBridge::UavcanBarometerBridge(uavcan::INode& node) :
+UavcanCDevSensorBridgeBase("uavcan_baro", "/dev/uavcan/baro", BARO_DEVICE_PATH, BARO_TOPICS),
+_sub_air_data(node)
+{
+}
+
+int UavcanBarometerBridge::init()
+{
+ int res = device::CDev::init();
+ if (res < 0) {
+ return res;
+ }
+
+ res = _sub_air_data.start(AirDataCbBinder(this, &UavcanBarometerBridge::air_data_sub_cb));
+ if (res < 0) {
+ log("failed to start uavcan sub: %d", res);
+ return res;
+ }
+ return 0;
+}
+
+int UavcanBarometerBridge::ioctl(struct file *filp, int cmd, unsigned long arg)
+{
+ switch (cmd) {
+ case BAROIOCSMSLPRESSURE: {
+ if ((arg < 80000) || (arg > 120000)) {
+ return -EINVAL;
+ } else {
+ log("new msl pressure %u", _msl_pressure);
+ _msl_pressure = arg;
+ return OK;
+ }
+ }
+ case BAROIOCGMSLPRESSURE: {
+ return _msl_pressure;
+ }
+ default: {
+ return CDev::ioctl(filp, cmd, arg);
+ }
+ }
+}
+
+void UavcanBarometerBridge::air_data_sub_cb(const uavcan::ReceivedDataStructure<uavcan::equipment::air_data::StaticAirData> &msg)
+{
+ auto report = ::baro_report();
+
+ report.timestamp = msg.getMonotonicTimestamp().toUSec();
+ report.temperature = msg.static_temperature;
+ report.pressure = msg.static_pressure / 100.0F; // Convert to millibar
+
+ /*
+ * Altitude computation
+ * Refer to the MS5611 driver for details
+ */
+ const double T1 = 15.0 + 273.15; // temperature at base height in Kelvin
+ const double a = -6.5 / 1000; // temperature gradient in degrees per metre
+ const double g = 9.80665; // gravity constant in m/s/s
+ const double R = 287.05; // ideal gas constant in J/kg/K
+
+ const double p1 = _msl_pressure / 1000.0; // current pressure at MSL in kPa
+ const double p = double(msg.static_pressure) / 1000.0; // measured pressure in kPa
+
+ report.altitude = (((std::pow((p / p1), (-(a * R) / g))) * T1) - T1) / a;
+
+ publish(msg.getSrcNodeID().get(), &report);
+}
diff --git a/src/modules/uavcan/sensors/baro.hpp b/src/modules/uavcan/sensors/baro.hpp
new file mode 100644
index 000000000..9d470219e
--- /dev/null
+++ b/src/modules/uavcan/sensors/baro.hpp
@@ -0,0 +1,68 @@
+/****************************************************************************
+ *
+ * Copyright (C) 2014 PX4 Development Team. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/**
+ * @author Pavel Kirienko <pavel.kirienko@gmail.com>
+ */
+
+#pragma once
+
+#include "sensor_bridge.hpp"
+#include <drivers/drv_baro.h>
+
+#include <uavcan/equipment/air_data/StaticAirData.hpp>
+
+class UavcanBarometerBridge : public UavcanCDevSensorBridgeBase
+{
+public:
+ static const char *const NAME;
+
+ UavcanBarometerBridge(uavcan::INode& node);
+
+ const char *get_name() const override { return NAME; }
+
+ int init() override;
+
+private:
+ int ioctl(struct file *filp, int cmd, unsigned long arg) override;
+
+ void air_data_sub_cb(const uavcan::ReceivedDataStructure<uavcan::equipment::air_data::StaticAirData> &msg);
+
+ typedef uavcan::MethodBinder<UavcanBarometerBridge*,
+ void (UavcanBarometerBridge::*)
+ (const uavcan::ReceivedDataStructure<uavcan::equipment::air_data::StaticAirData>&)>
+ AirDataCbBinder;
+
+ uavcan::Subscriber<uavcan::equipment::air_data::StaticAirData, AirDataCbBinder> _sub_air_data;
+ unsigned _msl_pressure = 101325;
+};
diff --git a/src/modules/uavcan/sensors/gnss.cpp b/src/modules/uavcan/sensors/gnss.cpp
new file mode 100644
index 000000000..a375db37f
--- /dev/null
+++ b/src/modules/uavcan/sensors/gnss.cpp
@@ -0,0 +1,173 @@
+/****************************************************************************
+ *
+ * Copyright (C) 2014 PX4 Development Team. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/**
+ * @file gnss.cpp
+ *
+ * @author Pavel Kirienko <pavel.kirienko@gmail.com>
+ * @author Andrew Chambers <achamber@gmail.com>
+ *
+ */
+
+#include "gnss.hpp"
+#include <systemlib/err.h>
+#include <mathlib/mathlib.h>
+
+const char *const UavcanGnssBridge::NAME = "gnss";
+
+UavcanGnssBridge::UavcanGnssBridge(uavcan::INode &node) :
+_node(node),
+_sub_fix(node),
+_report_pub(-1)
+{
+}
+
+int UavcanGnssBridge::init()
+{
+ int res = _sub_fix.start(FixCbBinder(this, &UavcanGnssBridge::gnss_fix_sub_cb));
+ if (res < 0)
+ {
+ warnx("GNSS fix sub failed %i", res);
+ return res;
+ }
+ return res;
+}
+
+unsigned UavcanGnssBridge::get_num_redundant_channels() const
+{
+ return (_receiver_node_id < 0) ? 0 : 1;
+}
+
+void UavcanGnssBridge::print_status() const
+{
+ printf("RX errors: %d, receiver node id: ", _sub_fix.getFailureCount());
+ if (_receiver_node_id < 0) {
+ printf("N/A\n");
+ } else {
+ printf("%d\n", _receiver_node_id);
+ }
+}
+
+void UavcanGnssBridge::gnss_fix_sub_cb(const uavcan::ReceivedDataStructure<uavcan::equipment::gnss::Fix> &msg)
+{
+ // This bridge does not support redundant GNSS receivers yet.
+ if (_receiver_node_id < 0) {
+ _receiver_node_id = msg.getSrcNodeID().get();
+ warnx("GNSS receiver node ID: %d", _receiver_node_id);
+ } else {
+ if (_receiver_node_id != msg.getSrcNodeID().get()) {
+ return; // This GNSS receiver is the redundant one, ignore it.
+ }
+ }
+
+ auto report = ::vehicle_gps_position_s();
+
+ report.timestamp_position = msg.getMonotonicTimestamp().toUSec();
+ report.lat = msg.latitude_deg_1e8 / 10;
+ report.lon = msg.longitude_deg_1e8 / 10;
+ report.alt = msg.height_msl_mm;
+
+ report.timestamp_variance = report.timestamp_position;
+
+
+ // Check if the msg contains valid covariance information
+ const bool valid_position_covariance = !msg.position_covariance.empty();
+ const bool valid_velocity_covariance = !msg.velocity_covariance.empty();
+
+ if (valid_position_covariance) {
+ float pos_cov[9];
+ msg.position_covariance.unpackSquareMatrix(pos_cov);
+
+ // Horizontal position uncertainty
+ const float horizontal_pos_variance = math::max(pos_cov[0], pos_cov[4]);
+ report.eph = (horizontal_pos_variance > 0) ? sqrtf(horizontal_pos_variance) : -1.0F;
+
+ // Vertical position uncertainty
+ report.epv = (pos_cov[8] > 0) ? sqrtf(pos_cov[8]) : -1.0F;
+ } else {
+ report.eph = -1.0F;
+ report.epv = -1.0F;
+ }
+
+ if (valid_velocity_covariance) {
+ float vel_cov[9];
+ msg.velocity_covariance.unpackSquareMatrix(vel_cov);
+ report.s_variance_m_s = math::max(math::max(vel_cov[0], vel_cov[4]), vel_cov[8]);
+
+ /* There is a nonlinear relationship between the velocity vector and the heading.
+ * Use Jacobian to transform velocity covariance to heading covariance
+ *
+ * Nonlinear equation:
+ * heading = atan2(vel_e_m_s, vel_n_m_s)
+ * For math, see http://en.wikipedia.org/wiki/Atan2#Derivative
+ *
+ * To calculate the variance of heading from the variance of velocity,
+ * cov(heading) = J(velocity)*cov(velocity)*J(velocity)^T
+ */
+ float vel_n = msg.ned_velocity[0];
+ float vel_e = msg.ned_velocity[1];
+ float vel_n_sq = vel_n * vel_n;
+ float vel_e_sq = vel_e * vel_e;
+ report.c_variance_rad =
+ (vel_e_sq * vel_cov[0] +
+ -2 * vel_n * vel_e * vel_cov[1] + // Covariance matrix is symmetric
+ vel_n_sq* vel_cov[4]) / ((vel_n_sq + vel_e_sq) * (vel_n_sq + vel_e_sq));
+
+ } else {
+ report.s_variance_m_s = -1.0F;
+ report.c_variance_rad = -1.0F;
+ }
+
+ report.fix_type = msg.status;
+
+ report.timestamp_velocity = report.timestamp_position;
+ report.vel_n_m_s = msg.ned_velocity[0];
+ report.vel_e_m_s = msg.ned_velocity[1];
+ report.vel_d_m_s = msg.ned_velocity[2];
+ report.vel_m_s = sqrtf(report.vel_n_m_s * report.vel_n_m_s + report.vel_e_m_s * report.vel_e_m_s + report.vel_d_m_s * report.vel_d_m_s);
+ report.cog_rad = atan2f(report.vel_e_m_s, report.vel_n_m_s);
+ report.vel_ned_valid = true;
+
+ report.timestamp_time = report.timestamp_position;
+ report.time_gps_usec = uavcan::UtcTime(msg.gnss_timestamp).toUSec(); // Convert to microseconds
+
+ report.satellites_used = msg.sats_used;
+
+ if (_report_pub > 0) {
+ orb_publish(ORB_ID(vehicle_gps_position), _report_pub, &report);
+
+ } else {
+ _report_pub = orb_advertise(ORB_ID(vehicle_gps_position), &report);
+ }
+
+}
diff --git a/src/modules/uavcan/sensors/gnss.hpp b/src/modules/uavcan/sensors/gnss.hpp
new file mode 100644
index 000000000..2111ff80b
--- /dev/null
+++ b/src/modules/uavcan/sensors/gnss.hpp
@@ -0,0 +1,85 @@
+/****************************************************************************
+ *
+ * Copyright (C) 2014 PX4 Development Team. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/**
+ * @file gnss.hpp
+ *
+ * UAVCAN --> ORB bridge for GNSS messages:
+ * uavcan.equipment.gnss.Fix
+ *
+ * @author Pavel Kirienko <pavel.kirienko@gmail.com>
+ * @author Andrew Chambers <achamber@gmail.com>
+ */
+
+#pragma once
+
+#include <uORB/uORB.h>
+#include <uORB/topics/vehicle_gps_position.h>
+
+#include <uavcan/uavcan.hpp>
+#include <uavcan/equipment/gnss/Fix.hpp>
+
+#include "sensor_bridge.hpp"
+
+class UavcanGnssBridge : public IUavcanSensorBridge
+{
+public:
+ static const char *const NAME;
+
+ UavcanGnssBridge(uavcan::INode& node);
+
+ const char *get_name() const override { return NAME; }
+
+ int init() override;
+
+ unsigned get_num_redundant_channels() const override;
+
+ void print_status() const override;
+
+private:
+ /**
+ * GNSS fix message will be reported via this callback.
+ */
+ void gnss_fix_sub_cb(const uavcan::ReceivedDataStructure<uavcan::equipment::gnss::Fix> &msg);
+
+ typedef uavcan::MethodBinder<UavcanGnssBridge*,
+ void (UavcanGnssBridge::*)(const uavcan::ReceivedDataStructure<uavcan::equipment::gnss::Fix>&)>
+ FixCbBinder;
+
+ uavcan::INode &_node;
+ uavcan::Subscriber<uavcan::equipment::gnss::Fix, FixCbBinder> _sub_fix;
+ int _receiver_node_id = -1;
+
+ orb_advert_t _report_pub; ///< uORB pub for gnss position
+
+};
diff --git a/src/modules/uavcan/sensors/mag.cpp b/src/modules/uavcan/sensors/mag.cpp
new file mode 100644
index 000000000..35ebee542
--- /dev/null
+++ b/src/modules/uavcan/sensors/mag.cpp
@@ -0,0 +1,150 @@
+/****************************************************************************
+ *
+ * Copyright (C) 2014 PX4 Development Team. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/**
+ * @author Pavel Kirienko <pavel.kirienko@gmail.com>
+ */
+
+#include "mag.hpp"
+
+#include <systemlib/err.h>
+
+static const orb_id_t MAG_TOPICS[3] = {
+ ORB_ID(sensor_mag0),
+ ORB_ID(sensor_mag1),
+ ORB_ID(sensor_mag2)
+};
+
+const char *const UavcanMagnetometerBridge::NAME = "mag";
+
+UavcanMagnetometerBridge::UavcanMagnetometerBridge(uavcan::INode& node) :
+UavcanCDevSensorBridgeBase("uavcan_mag", "/dev/uavcan/mag", MAG_DEVICE_PATH, MAG_TOPICS),
+_sub_mag(node)
+{
+ _device_id.devid_s.devtype = DRV_MAG_DEVTYPE_HMC5883;
+
+ _scale.x_scale = 1.0F;
+ _scale.y_scale = 1.0F;
+ _scale.z_scale = 1.0F;
+}
+
+int UavcanMagnetometerBridge::init()
+{
+ int res = device::CDev::init();
+ if (res < 0) {
+ return res;
+ }
+
+ res = _sub_mag.start(MagCbBinder(this, &UavcanMagnetometerBridge::mag_sub_cb));
+ if (res < 0) {
+ log("failed to start uavcan sub: %d", res);
+ return res;
+ }
+ return 0;
+}
+
+ssize_t UavcanMagnetometerBridge::read(struct file *filp, char *buffer, size_t buflen)
+{
+ static uint64_t last_read = 0;
+ struct mag_report *mag_buf = reinterpret_cast<struct mag_report *>(buffer);
+
+ /* buffer must be large enough */
+ unsigned count = buflen / sizeof(struct mag_report);
+ if (count < 1) {
+ return -ENOSPC;
+ }
+
+ if (last_read < _report.timestamp) {
+ /* copy report */
+ lock();
+ *mag_buf = _report;
+ last_read = _report.timestamp;
+ unlock();
+ return sizeof(struct mag_report);
+ } else {
+ /* no new data available, warn caller */
+ return -EAGAIN;
+ }
+}
+
+int UavcanMagnetometerBridge::ioctl(struct file *filp, int cmd, unsigned long arg)
+{
+ switch (cmd) {
+ case SENSORIOCSQUEUEDEPTH: {
+ return OK; // Pretend that this stuff is supported to keep APM happy
+ }
+ case MAGIOCSSCALE: {
+ std::memcpy(&_scale, reinterpret_cast<const void*>(arg), sizeof(_scale));
+ return 0;
+ }
+ case MAGIOCGSCALE: {
+ std::memcpy(reinterpret_cast<void*>(arg), &_scale, sizeof(_scale));
+ return 0;
+ }
+ case MAGIOCSELFTEST: {
+ return 0; // Nothing to do
+ }
+ case MAGIOCGEXTERNAL: {
+ return 1; // declare it external rise it's priority and to allow for correct orientation compensation
+ }
+ case MAGIOCSSAMPLERATE: {
+ return 0; // Pretend that this stuff is supported to keep the sensor app happy
+ }
+ case MAGIOCCALIBRATE:
+ case MAGIOCGSAMPLERATE:
+ case MAGIOCSRANGE:
+ case MAGIOCGRANGE:
+ case MAGIOCSLOWPASS:
+ case MAGIOCEXSTRAP:
+ case MAGIOCGLOWPASS: {
+ return -EINVAL;
+ }
+ default: {
+ return CDev::ioctl(filp, cmd, arg);
+ }
+ }
+}
+
+void UavcanMagnetometerBridge::mag_sub_cb(const uavcan::ReceivedDataStructure<uavcan::equipment::ahrs::Magnetometer> &msg)
+{
+ lock();
+ _report.range_ga = 1.3F; // Arbitrary number, doesn't really mean anything
+ _report.timestamp = msg.getMonotonicTimestamp().toUSec();
+
+ _report.x = (msg.magnetic_field[0] - _scale.x_offset) * _scale.x_scale;
+ _report.y = (msg.magnetic_field[1] - _scale.y_offset) * _scale.y_scale;
+ _report.z = (msg.magnetic_field[2] - _scale.z_offset) * _scale.z_scale;
+ unlock();
+
+ publish(msg.getSrcNodeID().get(), &_report);
+}
diff --git a/src/modules/uavcan/sensors/mag.hpp b/src/modules/uavcan/sensors/mag.hpp
new file mode 100644
index 000000000..74077d883
--- /dev/null
+++ b/src/modules/uavcan/sensors/mag.hpp
@@ -0,0 +1,70 @@
+/****************************************************************************
+ *
+ * Copyright (C) 2014 PX4 Development Team. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/**
+ * @author Pavel Kirienko <pavel.kirienko@gmail.com>
+ */
+
+#pragma once
+
+#include "sensor_bridge.hpp"
+#include <drivers/drv_mag.h>
+
+#include <uavcan/equipment/ahrs/Magnetometer.hpp>
+
+class UavcanMagnetometerBridge : public UavcanCDevSensorBridgeBase
+{
+public:
+ static const char *const NAME;
+
+ UavcanMagnetometerBridge(uavcan::INode& node);
+
+ const char *get_name() const override { return NAME; }
+
+ int init() override;
+
+private:
+ ssize_t read(struct file *filp, char *buffer, size_t buflen);
+ int ioctl(struct file *filp, int cmd, unsigned long arg) override;
+
+ void mag_sub_cb(const uavcan::ReceivedDataStructure<uavcan::equipment::ahrs::Magnetometer> &msg);
+
+ typedef uavcan::MethodBinder<UavcanMagnetometerBridge*,
+ void (UavcanMagnetometerBridge::*)
+ (const uavcan::ReceivedDataStructure<uavcan::equipment::ahrs::Magnetometer>&)>
+ MagCbBinder;
+
+ uavcan::Subscriber<uavcan::equipment::ahrs::Magnetometer, MagCbBinder> _sub_mag;
+ mag_scale _scale = {};
+ mag_report _report = {};
+};
diff --git a/src/modules/uavcan/sensors/sensor_bridge.cpp b/src/modules/uavcan/sensors/sensor_bridge.cpp
new file mode 100644
index 000000000..0999938fc
--- /dev/null
+++ b/src/modules/uavcan/sensors/sensor_bridge.cpp
@@ -0,0 +1,161 @@
+/****************************************************************************
+ *
+ * Copyright (C) 2014 PX4 Development Team. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/**
+ * @author Pavel Kirienko <pavel.kirienko@gmail.com>
+ */
+
+#include "sensor_bridge.hpp"
+#include <cassert>
+
+#include "gnss.hpp"
+#include "mag.hpp"
+#include "baro.hpp"
+
+/*
+ * IUavcanSensorBridge
+ */
+void IUavcanSensorBridge::make_all(uavcan::INode &node, List<IUavcanSensorBridge*> &list)
+{
+ list.add(new UavcanBarometerBridge(node));
+ list.add(new UavcanMagnetometerBridge(node));
+ list.add(new UavcanGnssBridge(node));
+}
+
+/*
+ * UavcanCDevSensorBridgeBase
+ */
+UavcanCDevSensorBridgeBase::~UavcanCDevSensorBridgeBase()
+{
+ for (unsigned i = 0; i < _max_channels; i++) {
+ if (_channels[i].node_id >= 0) {
+ (void)unregister_class_devname(_class_devname, _channels[i].class_instance);
+ }
+ }
+ delete [] _orb_topics;
+ delete [] _channels;
+}
+
+void UavcanCDevSensorBridgeBase::publish(const int node_id, const void *report)
+{
+ assert(report != nullptr);
+
+ Channel *channel = nullptr;
+
+ // Checking if such channel already exists
+ for (unsigned i = 0; i < _max_channels; i++) {
+ if (_channels[i].node_id == node_id) {
+ channel = _channels + i;
+ break;
+ }
+ }
+
+ // No such channel - try to create one
+ if (channel == nullptr) {
+ if (_out_of_channels) {
+ return; // Give up immediately - saves some CPU time
+ }
+
+ log("adding channel %d...", node_id);
+
+ // Search for the first free channel
+ for (unsigned i = 0; i < _max_channels; i++) {
+ if (_channels[i].node_id < 0) {
+ channel = _channels + i;
+ break;
+ }
+ }
+
+ // No free channels left
+ if (channel == nullptr) {
+ _out_of_channels = true;
+ log("out of channels");
+ return;
+ }
+
+ // update device id as we now know our device node_id
+ _device_id.devid_s.address = static_cast<uint8_t>(node_id);
+
+ // Ask the CDev helper which class instance we can take
+ const int class_instance = register_class_devname(_class_devname);
+ if (class_instance < 0 || class_instance >= int(_max_channels)) {
+ _out_of_channels = true;
+ log("out of class instances");
+ (void)unregister_class_devname(_class_devname, class_instance);
+ return;
+ }
+
+ // Publish to the appropriate topic, abort on failure
+ channel->orb_id = _orb_topics[class_instance];
+ channel->node_id = node_id;
+ channel->class_instance = class_instance;
+
+ channel->orb_advert = orb_advertise(channel->orb_id, report);
+ if (channel->orb_advert < 0) {
+ log("ADVERTISE FAILED");
+ (void)unregister_class_devname(_class_devname, class_instance);
+ *channel = Channel();
+ return;
+ }
+
+ log("channel %d class instance %d ok", channel->node_id, channel->class_instance);
+ }
+ assert(channel != nullptr);
+
+ (void)orb_publish(channel->orb_id, channel->orb_advert, report);
+}
+
+unsigned UavcanCDevSensorBridgeBase::get_num_redundant_channels() const
+{
+ unsigned out = 0;
+ for (unsigned i = 0; i < _max_channels; i++) {
+ if (_channels[i].node_id >= 0) {
+ out += 1;
+ }
+ }
+ return out;
+}
+
+void UavcanCDevSensorBridgeBase::print_status() const
+{
+ printf("devname: %s\n", _class_devname);
+
+ for (unsigned i = 0; i < _max_channels; i++) {
+ if (_channels[i].node_id >= 0) {
+ printf("channel %d: node id %d --> class instance %d\n",
+ i, _channels[i].node_id, _channels[i].class_instance);
+ } else {
+ printf("channel %d: empty\n", i);
+ }
+ }
+}
diff --git a/src/modules/uavcan/sensors/sensor_bridge.hpp b/src/modules/uavcan/sensors/sensor_bridge.hpp
new file mode 100644
index 000000000..e31960537
--- /dev/null
+++ b/src/modules/uavcan/sensors/sensor_bridge.hpp
@@ -0,0 +1,133 @@
+/****************************************************************************
+ *
+ * Copyright (C) 2014 PX4 Development Team. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/**
+ * @author Pavel Kirienko <pavel.kirienko@gmail.com>
+ */
+
+#pragma once
+
+#include <containers/List.hpp>
+#include <uavcan/uavcan.hpp>
+#include <drivers/device/device.h>
+#include <drivers/drv_orb_dev.h>
+
+/**
+ * A sensor bridge class must implement this interface.
+ */
+class IUavcanSensorBridge : uavcan::Noncopyable, public ListNode<IUavcanSensorBridge*>
+{
+public:
+ static constexpr unsigned MAX_NAME_LEN = 20;
+
+ virtual ~IUavcanSensorBridge() { }
+
+ /**
+ * Returns ASCII name of the bridge.
+ */
+ virtual const char *get_name() const = 0;
+
+ /**
+ * Starts the bridge.
+ * @return Non-negative value on success, negative on error.
+ */
+ virtual int init() = 0;
+
+ /**
+ * Returns number of active redundancy channels.
+ */
+ virtual unsigned get_num_redundant_channels() const = 0;
+
+ /**
+ * Prints current status in a human readable format to stdout.
+ */
+ virtual void print_status() const = 0;
+
+ /**
+ * Sensor bridge factory.
+ * Creates a bridge object by its ASCII name, e.g. "gnss", "mag".
+ * @return nullptr if such bridge can't be created.
+ */
+ static void make_all(uavcan::INode &node, List<IUavcanSensorBridge*> &list);
+};
+
+/**
+ * This is the base class for redundant sensors with an independent ORB topic per each redundancy channel.
+ * For example, sensor_mag0, sensor_mag1, etc.
+ */
+class UavcanCDevSensorBridgeBase : public IUavcanSensorBridge, public device::CDev
+{
+ struct Channel
+ {
+ int node_id = -1;
+ orb_id_t orb_id = nullptr;
+ orb_advert_t orb_advert = -1;
+ int class_instance = -1;
+ };
+
+ const unsigned _max_channels;
+ const char *const _class_devname;
+ orb_id_t *const _orb_topics;
+ Channel *const _channels;
+ bool _out_of_channels = false;
+
+protected:
+ template <unsigned MaxChannels>
+ UavcanCDevSensorBridgeBase(const char *name, const char *devname, const char *class_devname,
+ const orb_id_t (&orb_topics)[MaxChannels]) :
+ device::CDev(name, devname),
+ _max_channels(MaxChannels),
+ _class_devname(class_devname),
+ _orb_topics(new orb_id_t[MaxChannels]),
+ _channels(new Channel[MaxChannels])
+ {
+ memcpy(_orb_topics, orb_topics, sizeof(orb_id_t) * MaxChannels);
+ _device_id.devid_s.bus_type = DeviceBusType_UAVCAN;
+ _device_id.devid_s.bus = 0;
+ }
+
+ /**
+ * Sends one measurement into appropriate ORB topic.
+ * New redundancy channels will be registered automatically.
+ * @param node_id Sensor's Node ID
+ * @param report Pointer to ORB message object
+ */
+ void publish(const int node_id, const void *report);
+
+public:
+ virtual ~UavcanCDevSensorBridgeBase();
+
+ unsigned get_num_redundant_channels() const override;
+
+ void print_status() const override;
+};
diff --git a/src/modules/mavlink/mavlink_commands.cpp b/src/modules/uavcan/uavcan_clock.cpp
index fccd4d9a5..fe8ba406a 100644
--- a/src/modules/mavlink/mavlink_commands.cpp
+++ b/src/modules/uavcan/uavcan_clock.cpp
@@ -31,40 +31,51 @@
*
****************************************************************************/
+#include <uavcan_stm32/uavcan_stm32.hpp>
+#include <drivers/drv_hrt.h>
+
/**
- * @file mavlink_commands.cpp
- * Mavlink commands stream implementation.
+ * @file uavcan_clock.cpp
+ *
+ * Implements a clock for the CAN node.
*
- * @author Anton Babushkin <anton.babushkin@me.com>
+ * @author Pavel Kirienko <pavel.kirienko@gmail.com>
*/
-#include "mavlink_commands.h"
+namespace uavcan_stm32
+{
+namespace clock
+{
-MavlinkCommandsStream::MavlinkCommandsStream(Mavlink *mavlink, mavlink_channel_t channel) : _channel(channel), _cmd_time(0)
+uavcan::MonotonicTime getMonotonic()
{
- _cmd_sub = mavlink->add_orb_subscription(ORB_ID(vehicle_command));
+ return uavcan::MonotonicTime::fromUSec(hrt_absolute_time());
}
-void
-MavlinkCommandsStream::update(const hrt_abstime t)
+uavcan::UtcTime getUtc()
{
- struct vehicle_command_s cmd;
+ return uavcan::UtcTime();
+}
- if (_cmd_sub->update(&_cmd_time, &cmd)) {
- /* only send commands for other systems/components */
- if (cmd.target_system != mavlink_system.sysid || cmd.target_component != mavlink_system.compid) {
- mavlink_msg_command_long_send(_channel,
- cmd.target_system,
- cmd.target_component,
- cmd.command,
- cmd.confirmation,
- cmd.param1,
- cmd.param2,
- cmd.param3,
- cmd.param4,
- cmd.param5,
- cmd.param6,
- cmd.param7);
- }
- }
+void adjustUtc(uavcan::UtcDuration adjustment)
+{
+ (void)adjustment;
}
+
+uavcan::uint64_t getUtcUSecFromCanInterrupt();
+
+uavcan::uint64_t getUtcUSecFromCanInterrupt()
+{
+ return 0;
+}
+
+} // namespace clock
+
+SystemClock &SystemClock::instance()
+{
+ static SystemClock inst;
+ return inst;
+}
+
+}
+
diff --git a/src/modules/uavcan/uavcan_main.cpp b/src/modules/uavcan/uavcan_main.cpp
new file mode 100644
index 000000000..2c543462e
--- /dev/null
+++ b/src/modules/uavcan/uavcan_main.cpp
@@ -0,0 +1,647 @@
+/****************************************************************************
+ *
+ * Copyright (c) 2014 PX4 Development Team. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <cstdlib>
+#include <cstring>
+#include <fcntl.h>
+#include <systemlib/err.h>
+#include <systemlib/systemlib.h>
+#include <systemlib/param/param.h>
+#include <systemlib/mixer/mixer.h>
+#include <systemlib/board_serial.h>
+#include <systemlib/scheduling_priorities.h>
+#include <version/version.h>
+#include <arch/board/board.h>
+#include <arch/chip/chip.h>
+
+#include <drivers/drv_hrt.h>
+#include <drivers/drv_pwm_output.h>
+
+#include "uavcan_main.hpp"
+
+/**
+ * @file uavcan_main.cpp
+ *
+ * Implements basic functinality of UAVCAN node.
+ *
+ * @author Pavel Kirienko <pavel.kirienko@gmail.com>
+ */
+
+/*
+ * UavcanNode
+ */
+UavcanNode *UavcanNode::_instance;
+
+UavcanNode::UavcanNode(uavcan::ICanDriver &can_driver, uavcan::ISystemClock &system_clock) :
+ CDev("uavcan", UAVCAN_DEVICE_PATH),
+ _node(can_driver, system_clock),
+ _node_mutex(),
+ _esc_controller(_node)
+{
+ _control_topics[0] = ORB_ID(actuator_controls_0);
+ _control_topics[1] = ORB_ID(actuator_controls_1);
+ _control_topics[2] = ORB_ID(actuator_controls_2);
+ _control_topics[3] = ORB_ID(actuator_controls_3);
+
+ const int res = pthread_mutex_init(&_node_mutex, nullptr);
+ if (res < 0) {
+ std::abort();
+ }
+}
+
+UavcanNode::~UavcanNode()
+{
+ if (_task != -1) {
+ /* tell the task we want it to go away */
+ _task_should_exit = true;
+
+ unsigned i = 10;
+
+ do {
+ /* wait 5ms - it should wake every 10ms or so worst-case */
+ usleep(5000);
+
+ /* if we have given up, kill it */
+ if (--i == 0) {
+ task_delete(_task);
+ break;
+ }
+
+ } while (_task != -1);
+ }
+
+ /* clean up the alternate device node */
+ // unregister_driver(PWM_OUTPUT_DEVICE_PATH);
+
+ ::close(_armed_sub);
+
+ // Removing the sensor bridges
+ auto br = _sensor_bridges.getHead();
+ while (br != nullptr) {
+ auto next = br->getSibling();
+ delete br;
+ br = next;
+ }
+
+ _instance = nullptr;
+}
+
+int UavcanNode::start(uavcan::NodeID node_id, uint32_t bitrate)
+{
+ if (_instance != nullptr) {
+ warnx("Already started");
+ return -1;
+ }
+
+ /*
+ * GPIO config.
+ * Forced pull up on CAN2 is required for Pixhawk v1 where the second interface lacks a transceiver.
+ * If no transceiver is connected, the RX pin will float, occasionally causing CAN controller to
+ * fail during initialization.
+ */
+ stm32_configgpio(GPIO_CAN1_RX);
+ stm32_configgpio(GPIO_CAN1_TX);
+ stm32_configgpio(GPIO_CAN2_RX | GPIO_PULLUP);
+ stm32_configgpio(GPIO_CAN2_TX);
+
+ /*
+ * CAN driver init
+ */
+ static CanInitHelper can;
+ static bool can_initialized = false;
+
+ if (!can_initialized) {
+ const int can_init_res = can.init(bitrate);
+
+ if (can_init_res < 0) {
+ warnx("CAN driver init failed %i", can_init_res);
+ return can_init_res;
+ }
+
+ can_initialized = true;
+ }
+
+ /*
+ * Node init
+ */
+ _instance = new UavcanNode(can.driver, uavcan_stm32::SystemClock::instance());
+
+ if (_instance == nullptr) {
+ warnx("Out of memory");
+ return -1;
+ }
+
+ const int node_init_res = _instance->init(node_id);
+
+ if (node_init_res < 0) {
+ delete _instance;
+ _instance = nullptr;
+ warnx("Node init failed %i", node_init_res);
+ return node_init_res;
+ }
+
+ /*
+ * Start the task. Normally it should never exit.
+ */
+ static auto run_trampoline = [](int, char *[]) {return UavcanNode::_instance->run();};
+ _instance->_task = task_spawn_cmd("uavcan", SCHED_DEFAULT, SCHED_PRIORITY_ACTUATOR_OUTPUTS, StackSize,
+ static_cast<main_t>(run_trampoline), nullptr);
+
+ if (_instance->_task < 0) {
+ warnx("start failed: %d", errno);
+ return -errno;
+ }
+
+ return OK;
+}
+
+void UavcanNode::fill_node_info()
+{
+ /* software version */
+ uavcan::protocol::SoftwareVersion swver;
+
+ // Extracting the first 8 hex digits of FW_GIT and converting them to int
+ char fw_git_short[9] = {};
+ std::memmove(fw_git_short, FW_GIT, 8);
+ assert(fw_git_short[8] == '\0');
+ char *end = nullptr;
+ swver.vcs_commit = std::strtol(fw_git_short, &end, 16);
+ swver.optional_field_mask |= swver.OPTIONAL_FIELD_MASK_VCS_COMMIT;
+
+ warnx("SW version vcs_commit: 0x%08x", unsigned(swver.vcs_commit));
+
+ _node.setSoftwareVersion(swver);
+
+ /* hardware version */
+ uavcan::protocol::HardwareVersion hwver;
+
+ if (!std::strncmp(HW_ARCH, "PX4FMU_V1", 9)) {
+ hwver.major = 1;
+ } else if (!std::strncmp(HW_ARCH, "PX4FMU_V2", 9)) {
+ hwver.major = 2;
+ } else {
+ ; // All other values of HW_ARCH resolve to zero
+ }
+
+ uint8_t udid[12] = {}; // Someone seems to love magic numbers
+ get_board_serial(udid);
+ uavcan::copy(udid, udid + sizeof(udid), hwver.unique_id.begin());
+
+ _node.setHardwareVersion(hwver);
+}
+
+int UavcanNode::init(uavcan::NodeID node_id)
+{
+ int ret = -1;
+
+ // Do regular cdev init
+ ret = CDev::init();
+
+ if (ret != OK) {
+ return ret;
+ }
+
+ _node.setName("org.pixhawk.pixhawk");
+
+ _node.setNodeID(node_id);
+
+ fill_node_info();
+
+ // Actuators
+ ret = _esc_controller.init();
+ if (ret < 0) {
+ return ret;
+ }
+
+ // Sensor bridges
+ IUavcanSensorBridge::make_all(_node, _sensor_bridges);
+ auto br = _sensor_bridges.getHead();
+ while (br != nullptr) {
+ ret = br->init();
+ if (ret < 0) {
+ warnx("cannot init sensor bridge '%s' (%d)", br->get_name(), ret);
+ return ret;
+ }
+ warnx("sensor bridge '%s' init ok", br->get_name());
+ br = br->getSibling();
+ }
+
+ return _node.start();
+}
+
+void UavcanNode::node_spin_once()
+{
+ const int spin_res = _node.spin(uavcan::MonotonicTime());
+ if (spin_res < 0) {
+ warnx("node spin error %i", spin_res);
+ }
+}
+
+int UavcanNode::run()
+{
+ (void)pthread_mutex_lock(&_node_mutex);
+
+ const unsigned PollTimeoutMs = 50;
+
+ // XXX figure out the output count
+ _output_count = 2;
+
+ _armed_sub = orb_subscribe(ORB_ID(actuator_armed));
+ _test_motor_sub = orb_subscribe(ORB_ID(test_motor));
+
+ actuator_outputs_s outputs;
+ memset(&outputs, 0, sizeof(outputs));
+
+ const int busevent_fd = ::open(uavcan_stm32::BusEvent::DevName, 0);
+ if (busevent_fd < 0)
+ {
+ warnx("Failed to open %s", uavcan_stm32::BusEvent::DevName);
+ _task_should_exit = true;
+ }
+
+ /*
+ * XXX Mixing logic/subscriptions shall be moved into UavcanEscController::update();
+ * IO multiplexing shall be done here.
+ */
+
+ _node.setStatusOk();
+
+ /*
+ * This event is needed to wake up the thread on CAN bus activity (RX/TX/Error).
+ * Please note that with such multiplexing it is no longer possible to rely only on
+ * the value returned from poll() to detect whether actuator control has timed out or not.
+ * Instead, all ORB events need to be checked individually (see below).
+ */
+ _poll_fds_num = 0;
+ _poll_fds[_poll_fds_num] = ::pollfd();
+ _poll_fds[_poll_fds_num].fd = busevent_fd;
+ _poll_fds[_poll_fds_num].events = POLLIN;
+ _poll_fds_num += 1;
+
+ while (!_task_should_exit) {
+ // update actuator controls subscriptions if needed
+ if (_groups_subscribed != _groups_required) {
+ subscribe();
+ _groups_subscribed = _groups_required;
+ }
+
+ // Mutex is unlocked while the thread is blocked on IO multiplexing
+ (void)pthread_mutex_unlock(&_node_mutex);
+
+ const int poll_ret = ::poll(_poll_fds, _poll_fds_num, PollTimeoutMs);
+
+ (void)pthread_mutex_lock(&_node_mutex);
+
+ node_spin_once(); // Non-blocking
+
+ // this would be bad...
+ if (poll_ret < 0) {
+ log("poll error %d", errno);
+ continue;
+ } else {
+ // get controls for required topics
+ bool controls_updated = false;
+ unsigned poll_id = 1;
+ for (unsigned i = 0; i < NUM_ACTUATOR_CONTROL_GROUPS; i++) {
+ if (_control_subs[i] > 0) {
+ if (_poll_fds[poll_id].revents & POLLIN) {
+ controls_updated = true;
+ orb_copy(_control_topics[i], _control_subs[i], &_controls[i]);
+ }
+ poll_id++;
+ }
+ }
+
+ // can we mix?
+ if (_test_in_progress) {
+ float test_outputs[NUM_ACTUATOR_OUTPUTS] = {};
+ test_outputs[_test_motor.motor_number] = _test_motor.value*2.0f-1.0f;
+
+ // Output to the bus
+ _esc_controller.update_outputs(test_outputs, NUM_ACTUATOR_OUTPUTS);
+ } else if (controls_updated && (_mixers != nullptr)) {
+
+ // XXX one output group has 8 outputs max,
+ // but this driver could well serve multiple groups.
+ unsigned num_outputs_max = 8;
+
+ // Do mixing
+ outputs.noutputs = _mixers->mix(&outputs.output[0], num_outputs_max);
+ outputs.timestamp = hrt_absolute_time();
+
+ // iterate actuators
+ for (unsigned i = 0; i < outputs.noutputs; i++) {
+ // last resort: catch NaN, INF and out-of-band errors
+ if (!isfinite(outputs.output[i])) {
+ /*
+ * Value is NaN, INF or out of band - set to the minimum value.
+ * This will be clearly visible on the servo status and will limit the risk of accidentally
+ * spinning motors. It would be deadly in flight.
+ */
+ outputs.output[i] = -1.0f;
+ }
+
+ // limit outputs to valid range
+
+ // never go below min
+ if (outputs.output[i] < -1.0f) {
+ outputs.output[i] = -1.0f;
+ }
+
+ // never go below max
+ if (outputs.output[i] > 1.0f) {
+ outputs.output[i] = 1.0f;
+ }
+ }
+
+ // Output to the bus
+ _esc_controller.update_outputs(outputs.output, outputs.noutputs);
+ }
+ }
+
+ // Check motor test state
+ bool updated = false;
+ orb_check(_test_motor_sub, &updated);
+
+ if (updated) {
+ orb_copy(ORB_ID(test_motor), _test_motor_sub, &_test_motor);
+
+ // Update the test status and check that we're not locked down
+ _test_in_progress = (_test_motor.value > 0);
+ _esc_controller.arm_single_esc(_test_motor.motor_number, _test_in_progress);
+ }
+
+ // Check arming state
+ orb_check(_armed_sub, &updated);
+
+ if (updated) {
+ orb_copy(ORB_ID(actuator_armed), _armed_sub, &_armed);
+
+ // Update the armed status and check that we're not locked down and motor
+ // test is not running
+ bool set_armed = _armed.armed && !_armed.lockdown && !_test_in_progress;
+
+ arm_actuators(set_armed);
+ }
+ }
+
+ teardown();
+ warnx("exiting.");
+
+ exit(0);
+}
+
+int
+UavcanNode::control_callback(uintptr_t handle, uint8_t control_group, uint8_t control_index, float &input)
+{
+ const actuator_controls_s *controls = (actuator_controls_s *)handle;
+
+ input = controls[control_group].control[control_index];
+ return 0;
+}
+
+int
+UavcanNode::teardown()
+{
+ for (unsigned i = 0; i < NUM_ACTUATOR_CONTROL_GROUPS; i++) {
+ if (_control_subs[i] > 0) {
+ ::close(_control_subs[i]);
+ _control_subs[i] = -1;
+ }
+ }
+ return ::close(_armed_sub);
+}
+
+int
+UavcanNode::arm_actuators(bool arm)
+{
+ _is_armed = arm;
+ _esc_controller.arm_all_escs(arm);
+ return OK;
+}
+
+void
+UavcanNode::subscribe()
+{
+ // Subscribe/unsubscribe to required actuator control groups
+ uint32_t sub_groups = _groups_required & ~_groups_subscribed;
+ uint32_t unsub_groups = _groups_subscribed & ~_groups_required;
+ // the first fd used by CAN
+ _poll_fds_num = 1;
+ for (unsigned i = 0; i < NUM_ACTUATOR_CONTROL_GROUPS; i++) {
+ if (sub_groups & (1 << i)) {
+ warnx("subscribe to actuator_controls_%d", i);
+ _control_subs[i] = orb_subscribe(_control_topics[i]);
+ }
+ if (unsub_groups & (1 << i)) {
+ warnx("unsubscribe from actuator_controls_%d", i);
+ ::close(_control_subs[i]);
+ _control_subs[i] = -1;
+ }
+
+ if (_control_subs[i] > 0) {
+ _poll_fds[_poll_fds_num].fd = _control_subs[i];
+ _poll_fds[_poll_fds_num].events = POLLIN;
+ _poll_fds_num++;
+ }
+ }
+}
+
+int
+UavcanNode::ioctl(file *filp, int cmd, unsigned long arg)
+{
+ int ret = OK;
+
+ lock();
+
+ switch (cmd) {
+ case PWM_SERVO_ARM:
+ arm_actuators(true);
+ break;
+
+ case PWM_SERVO_SET_ARM_OK:
+ case PWM_SERVO_CLEAR_ARM_OK:
+ case PWM_SERVO_SET_FORCE_SAFETY_OFF:
+ // these are no-ops, as no safety switch
+ break;
+
+ case PWM_SERVO_DISARM:
+ arm_actuators(false);
+ break;
+
+ case MIXERIOCGETOUTPUTCOUNT:
+ *(unsigned *)arg = _output_count;
+ break;
+
+ case MIXERIOCRESET:
+ if (_mixers != nullptr) {
+ delete _mixers;
+ _mixers = nullptr;
+ _groups_required = 0;
+ }
+
+ break;
+
+ case MIXERIOCLOADBUF: {
+ const char *buf = (const char *)arg;
+ unsigned buflen = strnlen(buf, 1024);
+
+ if (_mixers == nullptr)
+ _mixers = new MixerGroup(control_callback, (uintptr_t)_controls);
+
+ if (_mixers == nullptr) {
+ _groups_required = 0;
+ ret = -ENOMEM;
+
+ } else {
+
+ ret = _mixers->load_from_buf(buf, buflen);
+
+ if (ret != 0) {
+ warnx("mixer load failed with %d", ret);
+ delete _mixers;
+ _mixers = nullptr;
+ _groups_required = 0;
+ ret = -EINVAL;
+ } else {
+
+ _mixers->groups_required(_groups_required);
+ }
+ }
+
+ break;
+ }
+
+ default:
+ ret = -ENOTTY;
+ break;
+ }
+
+ unlock();
+
+ if (ret == -ENOTTY) {
+ ret = CDev::ioctl(filp, cmd, arg);
+ }
+
+ return ret;
+}
+
+void
+UavcanNode::print_info()
+{
+ if (!_instance) {
+ warnx("not running, start first");
+ }
+
+ (void)pthread_mutex_lock(&_node_mutex);
+
+ // ESC mixer status
+ printf("ESC actuators control groups: sub: %u / req: %u / fds: %u\n",
+ (unsigned)_groups_subscribed, (unsigned)_groups_required, _poll_fds_num);
+ printf("ESC mixer: %s\n", (_mixers == nullptr) ? "NONE" : "OK");
+
+ // Sensor bridges
+ auto br = _sensor_bridges.getHead();
+ while (br != nullptr) {
+ printf("Sensor '%s':\n", br->get_name());
+ br->print_status();
+ printf("\n");
+ br = br->getSibling();
+ }
+
+ (void)pthread_mutex_unlock(&_node_mutex);
+}
+
+/*
+ * App entry point
+ */
+static void print_usage()
+{
+ warnx("usage: \n"
+ "\tuavcan {start|status|stop}");
+}
+
+extern "C" __EXPORT int uavcan_main(int argc, char *argv[]);
+
+int uavcan_main(int argc, char *argv[])
+{
+ if (argc < 2) {
+ print_usage();
+ ::exit(1);
+ }
+
+ if (!std::strcmp(argv[1], "start")) {
+ if (UavcanNode::instance()) {
+ errx(1, "already started");
+ }
+
+ // Node ID
+ int32_t node_id = 0;
+ (void)param_get(param_find("UAVCAN_NODE_ID"), &node_id);
+
+ if (node_id < 0 || node_id > uavcan::NodeID::Max || !uavcan::NodeID(node_id).isUnicast()) {
+ warnx("Invalid Node ID %i", node_id);
+ ::exit(1);
+ }
+
+ // CAN bitrate
+ int32_t bitrate = 0;
+ (void)param_get(param_find("UAVCAN_BITRATE"), &bitrate);
+
+ // Start
+ warnx("Node ID %u, bitrate %u", node_id, bitrate);
+ return UavcanNode::start(node_id, bitrate);
+ }
+
+ /* commands below require the app to be started */
+ UavcanNode *const inst = UavcanNode::instance();
+
+ if (!inst) {
+ errx(1, "application not running");
+ }
+
+ if (!std::strcmp(argv[1], "status") || !std::strcmp(argv[1], "info")) {
+ inst->print_info();
+ ::exit(0);
+ }
+
+ if (!std::strcmp(argv[1], "stop")) {
+ delete inst;
+ ::exit(0);
+ }
+
+ print_usage();
+ ::exit(1);
+}
diff --git a/src/modules/uavcan/uavcan_main.hpp b/src/modules/uavcan/uavcan_main.hpp
new file mode 100644
index 000000000..274321f0d
--- /dev/null
+++ b/src/modules/uavcan/uavcan_main.hpp
@@ -0,0 +1,130 @@
+/****************************************************************************
+ *
+ * Copyright (c) 2014 PX4 Development Team. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+#pragma once
+
+#include <nuttx/config.h>
+
+#include <uavcan_stm32/uavcan_stm32.hpp>
+#include <drivers/device/device.h>
+
+#include <uORB/topics/actuator_controls.h>
+#include <uORB/topics/actuator_outputs.h>
+#include <uORB/topics/actuator_armed.h>
+#include <uORB/topics/test_motor.h>
+
+#include "actuators/esc.hpp"
+#include "sensors/sensor_bridge.hpp"
+
+/**
+ * @file uavcan_main.hpp
+ *
+ * Defines basic functinality of UAVCAN node.
+ *
+ * @author Pavel Kirienko <pavel.kirienko@gmail.com>
+ */
+
+#define NUM_ACTUATOR_CONTROL_GROUPS_UAVCAN 4
+#define UAVCAN_DEVICE_PATH "/dev/uavcan/esc"
+
+/**
+ * A UAVCAN node.
+ */
+class UavcanNode : public device::CDev
+{
+ static constexpr unsigned MemPoolSize = 10752;
+ static constexpr unsigned RxQueueLenPerIface = 64;
+ static constexpr unsigned StackSize = 3000;
+
+public:
+ typedef uavcan::Node<MemPoolSize> Node;
+ typedef uavcan_stm32::CanInitHelper<RxQueueLenPerIface> CanInitHelper;
+
+ UavcanNode(uavcan::ICanDriver &can_driver, uavcan::ISystemClock &system_clock);
+
+ virtual ~UavcanNode();
+
+ virtual int ioctl(file *filp, int cmd, unsigned long arg);
+
+ static int start(uavcan::NodeID node_id, uint32_t bitrate);
+
+ Node& get_node() { return _node; }
+
+ // TODO: move the actuator mixing stuff into the ESC controller class
+ static int control_callback(uintptr_t handle, uint8_t control_group, uint8_t control_index, float &input);
+
+ void subscribe();
+
+ int teardown();
+ int arm_actuators(bool arm);
+
+ void print_info();
+
+ static UavcanNode* instance() { return _instance; }
+
+private:
+ void fill_node_info();
+ int init(uavcan::NodeID node_id);
+ void node_spin_once();
+ int run();
+
+ int _task = -1; ///< handle to the OS task
+ bool _task_should_exit = false; ///< flag to indicate to tear down the CAN driver
+ int _armed_sub = -1; ///< uORB subscription of the arming status
+ actuator_armed_s _armed; ///< the arming request of the system
+ bool _is_armed = false; ///< the arming status of the actuators on the bus
+
+ int _test_motor_sub = -1; ///< uORB subscription of the test_motor status
+ test_motor_s _test_motor;
+ bool _test_in_progress = false;
+
+ unsigned _output_count = 0; ///< number of actuators currently available
+
+ static UavcanNode *_instance; ///< singleton pointer
+ Node _node; ///< library instance
+ pthread_mutex_t _node_mutex;
+
+ UavcanEscController _esc_controller;
+
+ List<IUavcanSensorBridge*> _sensor_bridges; ///< List of active sensor bridges
+
+ MixerGroup *_mixers = nullptr;
+
+ uint32_t _groups_required = 0;
+ uint32_t _groups_subscribed = 0;
+ int _control_subs[NUM_ACTUATOR_CONTROL_GROUPS_UAVCAN] = {};
+ actuator_controls_s _controls[NUM_ACTUATOR_CONTROL_GROUPS_UAVCAN] = {};
+ orb_id_t _control_topics[NUM_ACTUATOR_CONTROL_GROUPS_UAVCAN] = {};
+ pollfd _poll_fds[NUM_ACTUATOR_CONTROL_GROUPS_UAVCAN + 1] = {}; ///< +1 for /dev/uavcan/busevent
+ unsigned _poll_fds_num = 0;
+};
diff --git a/src/modules/uavcan/uavcan_params.c b/src/modules/uavcan/uavcan_params.c
new file mode 100644
index 000000000..e6ea8a8fb
--- /dev/null
+++ b/src/modules/uavcan/uavcan_params.c
@@ -0,0 +1,73 @@
+/****************************************************************************
+ *
+ * Copyright (C) 2014 PX4 Development Team. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. Neither the name PX4 nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/**
+ * @author Pavel Kirienko <pavel.kirienko@gmail.com>
+ */
+
+#include <nuttx/config.h>
+#include <systemlib/param/param.h>
+
+/**
+ * Enable UAVCAN.
+ *
+ * Enables support for UAVCAN-interfaced actuators and sensors.
+ *
+ * @min 0
+ * @max 1
+ * @group UAVCAN
+ */
+PARAM_DEFINE_INT32(UAVCAN_ENABLE, 0);
+
+/**
+ * UAVCAN Node ID.
+ *
+ * Read the specs at http://uavcan.org to learn more about Node ID.
+ *
+ * @min 1
+ * @max 125
+ * @group UAVCAN
+ */
+PARAM_DEFINE_INT32(UAVCAN_NODE_ID, 1);
+
+/**
+ * UAVCAN CAN bus bitrate.
+ *
+ * @min 20000
+ * @max 1000000
+ * @group UAVCAN
+ */
+PARAM_DEFINE_INT32(UAVCAN_BITRATE, 1000000);
+
+
+
diff --git a/src/modules/unit_test/module.mk b/src/modules/unit_test/module.mk
index f00b0f592..5000790a5 100644
--- a/src/modules/unit_test/module.mk
+++ b/src/modules/unit_test/module.mk
@@ -37,3 +37,4 @@
SRCS = unit_test.cpp
+MAXOPTIMIZATION = -Os
diff --git a/src/modules/unit_test/unit_test.cpp b/src/modules/unit_test/unit_test.cpp
index 02d1af481..b7568ce3a 100644
--- a/src/modules/unit_test/unit_test.cpp
+++ b/src/modules/unit_test/unit_test.cpp
@@ -36,7 +36,11 @@
#include <systemlib/err.h>
-UnitTest::UnitTest()
+UnitTest::UnitTest() :
+ _tests_run(0),
+ _tests_failed(0),
+ _tests_passed(0),
+ _assertions(0)
{
}
@@ -44,15 +48,22 @@ UnitTest::~UnitTest()
{
}
-void UnitTest::printResults(void)
+void UnitTest::print_results(void)
{
- warnx(mu_tests_failed() ? "SOME TESTS FAILED" : "ALL TESTS PASSED");
- warnx(" Tests passed : %d", mu_tests_passed());
- warnx(" Tests failed : %d", mu_tests_failed());
- warnx(" Assertions : %d", mu_assertion());
+ warnx(_tests_failed ? "SOME TESTS FAILED" : "ALL TESTS PASSED");
+ warnx(" Tests passed : %d", _tests_passed);
+ warnx(" Tests failed : %d", _tests_failed);
+ warnx(" Assertions : %d", _assertions);
}
-void UnitTest::printAssert(const char* msg, const char* test, const char* file, int line)
+/// @brief Used internally to the ut_assert macro to print assert failures.
+void UnitTest::_print_assert(const char* msg, const char* test, const char* file, int line)
{
- warnx("Assertion failed: %s - %s (%s:%d)", msg, test, file, line);
+ warnx("Assertion failed: %s - %s (%s:%d)", msg, test, file, line);
+}
+
+/// @brief Used internally to the ut_compare macro to print assert failures.
+void UnitTest::_print_compare(const char* msg, const char *v1_text, int v1, const char *v2_text, int v2, const char* file, int line)
+{
+ warnx("Compare failed: %s - (%s:%d) (%s:%d) (%s:%d)", msg, v1_text, v1, v2_text, v2, file, line);
}
diff --git a/src/modules/unit_test/unit_test.h b/src/modules/unit_test/unit_test.h
index 32eb8c308..8ed4efadf 100644
--- a/src/modules/unit_test/unit_test.h
+++ b/src/modules/unit_test/unit_test.h
@@ -37,50 +37,85 @@
#include <systemlib/err.h>
+/// @brief Base class to be used for unit tests.
class __EXPORT UnitTest
{
-
public:
-#define INLINE_GLOBAL(type,func) inline type& func() { static type x; return x; }
-
-INLINE_GLOBAL(int, mu_tests_run)
-INLINE_GLOBAL(int, mu_tests_failed)
-INLINE_GLOBAL(int, mu_tests_passed)
-INLINE_GLOBAL(int, mu_assertion)
-INLINE_GLOBAL(int, mu_line)
-INLINE_GLOBAL(const char*, mu_last_test)
UnitTest();
- virtual ~UnitTest();
-
- virtual void runTests(void) = 0;
- void printResults(void);
-
- void printAssert(const char* msg, const char* test, const char* file, int line);
-
-#define ut_assert(message, test) \
- do { \
- if (!(test)) { \
- printAssert(message, #test, __FILE__, __LINE__); \
- return false; \
- } else { \
- mu_assertion()++; \
- } \
- } while (0)
-
-#define ut_run_test(test) \
- do { \
- warnx("RUNNING TEST: %s", #test); \
- mu_tests_run()++; \
- if (!test()) { \
- warnx("TEST FAILED: %s", #test); \
- mu_tests_failed()++; \
- } else { \
- warnx("TEST PASSED: %s", #test); \
- mu_tests_passed()++; \
- } \
- } while (0)
+ virtual ~UnitTest();
+
+ /// @brief Override to run your unit tests. Unit tests should be called using ut_run_test macro.
+ /// @return true: all unit tests succeeded, false: one or more unit tests failed
+ virtual bool run_tests(void) = 0;
+
+ /// @brief Prints results from running of unit tests.
+ void print_results(void);
+
+/// @brief Macro to create a function which will run a unit test class and print results.
+#define ut_declare_test(test_function, test_class) \
+ bool test_function(void) \
+ { \
+ test_class* test = new test_class(); \
+ bool success = test->run_tests(); \
+ test->print_results(); \
+ return success; \
+ }
+
+protected:
+
+/// @brief Runs a single unit test. Unit tests must have the function signature of bool test(void). The unit
+/// test should return true if it succeeded, false for fail.
+#define ut_run_test(test) \
+ do { \
+ warnx("RUNNING TEST: %s", #test); \
+ _tests_run++; \
+ _init(); \
+ if (!test()) { \
+ warnx("TEST FAILED: %s", #test); \
+ _tests_failed++; \
+ } else { \
+ warnx("TEST PASSED: %s", #test); \
+ _tests_passed++; \
+ } \
+ _cleanup(); \
+ } while (0)
+
+/// @brief Used to assert a value within a unit test.
+#define ut_assert(message, test) \
+ do { \
+ if (!(test)) { \
+ _print_assert(message, #test, __FILE__, __LINE__); \
+ return false; \
+ } else { \
+ _assertions++; \
+ } \
+ } while (0)
+
+/// @brief Used to compare two integer values within a unit test. If possible use ut_compare instead of ut_assert
+/// since it will give you better error reporting of the actual values being compared.
+#define ut_compare(message, v1, v2) \
+ do { \
+ int _v1 = v1; \
+ int _v2 = v2; \
+ if (_v1 != _v2) { \
+ _print_compare(message, #v1, _v1, #v2, _v2, __FILE__, __LINE__); \
+ return false; \
+ } else { \
+ _assertions++; \
+ } \
+ } while (0)
+
+ virtual void _init(void) { }; ///< Run before each unit test. Override to provide custom behavior.
+ virtual void _cleanup(void) { }; ///< Run after each unit test. Override to provide custom behavior.
+
+ void _print_assert(const char* msg, const char* test, const char* file, int line);
+ void _print_compare(const char* msg, const char *v1_text, int v1, const char *v2_text, int v2, const char* file, int line);
+ int _tests_run; ///< The number of individual unit tests run
+ int _tests_failed; ///< The number of unit tests which failed
+ int _tests_passed; ///< The number of unit tests which passed
+ int _assertions; ///< Total number of assertions tested by all unit tests
};
#endif /* UNIT_TEST_H_ */