/* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "AP_NavEKF_Source.h" #include #include #include #include extern const AP_HAL::HAL& hal; const AP_Param::GroupInfo AP_NavEKF_Source::var_info[] = { // @Param: 1_POSXY // @DisplayName: Position Horizontal Source (Primary) // @Description: Position Horizontal Source (Primary) // @Values: 0:None, 3:GPS, 4:Beacon, 6:ExternalNav // @User: Advanced AP_GROUPINFO("1_POSXY", 1, AP_NavEKF_Source, _source_set[0].posxy, (int8_t)AP_NavEKF_Source::SourceXY::GPS), // @Param: 1_VELXY // @DisplayName: Velocity Horizontal Source // @Description: Velocity Horizontal Source // @Values: 0:None, 3:GPS, 4:Beacon, 5:OpticalFlow, 6:ExternalNav, 7:WheelEncoder // @User: Advanced AP_GROUPINFO("1_VELXY", 2, AP_NavEKF_Source, _source_set[0].velxy, (int8_t)AP_NavEKF_Source::SourceXY::GPS), // @Param: 1_POSZ // @DisplayName: Position Vertical Source // @Description: Position Vertical Source // @Values: 0:None, 1:Baro, 2:RangeFinder, 3:GPS, 4:Beacon, 6:ExternalNav // @User: Advanced AP_GROUPINFO("1_POSZ", 3, AP_NavEKF_Source, _source_set[0].posz, (int8_t)AP_NavEKF_Source::SourceZ::BARO), // @Param: 1_VELZ // @DisplayName: Velocity Vertical Source // @Description: Velocity Vertical Source // @Values: 0:None, 3:GPS, 4:Beacon, 6:ExternalNav // @User: Advanced AP_GROUPINFO("1_VELZ", 4, AP_NavEKF_Source, _source_set[0].velz, (int8_t)AP_NavEKF_Source::SourceZ::GPS), // @Param: 1_YAW // @DisplayName: Yaw Source // @Description: Yaw Source // @Values: 0:None, 1:Compass, 2:GPS, 3:GPS with Compass Fallback, 6:ExternalNav, 8:GSF // @User: Advanced AP_GROUPINFO("1_YAW", 5, AP_NavEKF_Source, _source_set[0].yaw, (int8_t)AP_NavEKF_Source::SourceYaw::COMPASS), #if AP_NAKEKF_SOURCE_SET_MAX >= 2 // @Param: 2_POSXY // @DisplayName: Position Horizontal Source (Secondary) // @Description: Position Horizontal Source (Secondary) // @Values: 0:None, 3:GPS, 4:Beacon, 6:ExternalNav // @User: Advanced AP_GROUPINFO("2_POSXY", 6, AP_NavEKF_Source, _source_set[1].posxy, (int8_t)AP_NavEKF_Source::SourceXY::NONE), // @Param: 2_VELXY // @DisplayName: Velocity Horizontal Source (Secondary) // @Description: Velocity Horizontal Source (Secondary) // @Values: 0:None, 3:GPS, 4:Beacon, 5:OpticalFlow, 6:ExternalNav, 7:WheelEncoder // @User: Advanced AP_GROUPINFO("2_VELXY", 7, AP_NavEKF_Source, _source_set[1].velxy, (int8_t)AP_NavEKF_Source::SourceXY::NONE), // @Param: 2_POSZ // @DisplayName: Position Vertical Source (Secondary) // @Description: Position Vertical Source (Secondary) // @Values: 0:None, 1:Baro, 2:RangeFinder, 3:GPS, 4:Beacon, 6:ExternalNav // @User: Advanced AP_GROUPINFO("2_POSZ", 8, AP_NavEKF_Source, _source_set[1].posz, (int8_t)AP_NavEKF_Source::SourceZ::BARO), // @Param: 2_VELZ // @DisplayName: Velocity Vertical Source (Secondary) // @Description: Velocity Vertical Source (Secondary) // @Values: 0:None, 3:GPS, 4:Beacon, 6:ExternalNav // @User: Advanced AP_GROUPINFO("2_VELZ", 9, AP_NavEKF_Source, _source_set[1].velz, (int8_t)AP_NavEKF_Source::SourceZ::NONE), // @Param: 2_YAW // @DisplayName: Yaw Source (Secondary) // @Description: Yaw Source (Secondary) // @Values: 0:None, 1:Compass, 2:GPS, 3:GPS with Compass Fallback, 6:ExternalNav, 8:GSF // @User: Advanced AP_GROUPINFO("2_YAW", 10, AP_NavEKF_Source, _source_set[1].yaw, (int8_t)AP_NavEKF_Source::SourceYaw::NONE), #endif #if AP_NAKEKF_SOURCE_SET_MAX >= 3 // @Param: 3_POSXY // @DisplayName: Position Horizontal Source (Tertiary) // @Description: Position Horizontal Source (Tertiary) // @Values: 0:None, 3:GPS, 4:Beacon, 6:ExternalNav // @User: Advanced AP_GROUPINFO("3_POSXY", 11, AP_NavEKF_Source, _source_set[2].posxy, (int8_t)AP_NavEKF_Source::SourceXY::NONE), // @Param: 3_VELXY // @DisplayName: Velocity Horizontal Source (Tertiary) // @Description: Velocity Horizontal Source (Tertiary) // @Values: 0:None, 3:GPS, 4:Beacon, 5:OpticalFlow, 6:ExternalNav, 7:WheelEncoder // @User: Advanced AP_GROUPINFO("3_VELXY", 12, AP_NavEKF_Source, _source_set[2].velxy, (int8_t)AP_NavEKF_Source::SourceXY::NONE), // @Param: 3_POSZ // @DisplayName: Position Vertical Source (Tertiary) // @Description: Position Vertical Source (Tertiary) // @Values: 0:None, 1:Baro, 2:RangeFinder, 3:GPS, 4:Beacon, 6:ExternalNav // @User: Advanced AP_GROUPINFO("3_POSZ", 13, AP_NavEKF_Source, _source_set[2].posz, (int8_t)AP_NavEKF_Source::SourceZ::BARO), // @Param: 3_VELZ // @DisplayName: Velocity Vertical Source (Tertiary) // @Description: Velocity Vertical Source (Tertiary) // @Values: 0:None, 3:GPS, 4:Beacon, 6:ExternalNav // @User: Advanced AP_GROUPINFO("3_VELZ", 14, AP_NavEKF_Source, _source_set[2].velz, (int8_t)AP_NavEKF_Source::SourceZ::NONE), // @Param: 3_YAW // @DisplayName: Yaw Source (Tertiary) // @Description: Yaw Source (Tertiary) // @Values: 0:None, 1:Compass, 2:GPS, 3:GPS with Compass Fallback, 6:ExternalNav, 8:GSF // @User: Advanced AP_GROUPINFO("3_YAW", 15, AP_NavEKF_Source, _source_set[2].yaw, (int8_t)AP_NavEKF_Source::SourceYaw::NONE), #endif // @Param: _OPTIONS // @DisplayName: EKF Source Options // @Description: EKF Source Options // @Bitmask: 0:FuseAllVelocities // @User: Advanced AP_GROUPINFO("_OPTIONS", 16, AP_NavEKF_Source, _options, (int16_t)SourceOptions::FUSE_ALL_VELOCITIES), AP_GROUPEND }; AP_NavEKF_Source::AP_NavEKF_Source() { AP_Param::setup_object_defaults(this, var_info); } // set position, velocity and yaw sources to either 0=primary, 1=secondary, 2=tertiary void AP_NavEKF_Source::setPosVelYawSourceSet(uint8_t source_set_idx) { // sanity check source idx if (source_set_idx < AP_NAKEKF_SOURCE_SET_MAX) { active_source_set = source_set_idx; static const LogEvent evt[AP_NAKEKF_SOURCE_SET_MAX] { LogEvent::EK3_SOURCES_SET_TO_PRIMARY, LogEvent::EK3_SOURCES_SET_TO_SECONDARY, LogEvent::EK3_SOURCES_SET_TO_TERTIARY, }; AP::logger().Write_Event(evt[active_source_set]); } } // true/false of whether velocity source should be used bool AP_NavEKF_Source::useVelXYSource(SourceXY velxy_source) const { if (velxy_source == _source_set[active_source_set].velxy) { return true; } // check for fuse all velocities if (_options.get() & (uint16_t)(SourceOptions::FUSE_ALL_VELOCITIES)) { for (uint8_t i=0; ienabled()) { return; } // consider aligning XY position: bool align_posxy = false; if ((getPosXYSource() == SourceXY::GPS) || (getPosXYSource() == SourceXY::BEACON)) { // only align position if active source is GPS or Beacon for (uint8_t i=0; ialign_position_to_ahrs(align_posxy, align_posz); #endif } // sensor specific helper functions bool AP_NavEKF_Source::usingGPS() const { return getPosXYSource() == SourceXY::GPS || getPosZSource() == SourceZ::GPS || getVelXYSource() == SourceXY::GPS || getVelZSource() == SourceZ::GPS || getYawSource() == SourceYaw::GSF; } // true if some parameters have been configured (used during parameter conversion) bool AP_NavEKF_Source::configured() { if (_configured) { return true; } // first source parameter is used to determine if configured or not _configured = _source_set[0].posxy.configured(); return _configured; } // mark parameters as configured (used to ensure parameter conversion is only done once) void AP_NavEKF_Source::mark_configured() { // save first parameter's current value to mark as configured return _source_set[0].posxy.save(true); } // returns false if we fail arming checks, in which case the buffer will be populated with a failure message // requires_position should be true if vertical or horizontal position configuration should be checked bool AP_NavEKF_Source::pre_arm_check(bool requires_position, char *failure_msg, uint8_t failure_msg_len) const { auto &dal = AP::dal(); bool baro_required = false; bool beacon_required = false; bool compass_required = false; bool gps_required = false; bool rangefinder_required = false; bool visualodom_required = false; bool optflow_required = false; bool wheelencoder_required = false; // check source params are valid for (uint8_t i=0; isnprintf(failure_msg, failure_msg_len, "Check EK3_SRC%d_POSXY", (int)i+1); return false; } // check velxy switch ((SourceXY)_source_set[i].velxy.get()) { case SourceXY::NONE: break; case SourceXY::GPS: gps_required = true; break; case SourceXY::OPTFLOW: optflow_required = true; break; case SourceXY::EXTNAV: visualodom_required = true; break; case SourceXY::WHEEL_ENCODER: wheelencoder_required = true; break; case SourceXY::BEACON: default: // invalid velxy value hal.util->snprintf(failure_msg, failure_msg_len, "Check EK3_SRC%d_VELXY", (int)i+1); return false; } // check posz switch ((SourceZ)_source_set[i].posz.get()) { case SourceZ::BARO: baro_required = true; break; case SourceZ::RANGEFINDER: rangefinder_required = true; break; case SourceZ::GPS: gps_required = true; break; case SourceZ::BEACON: beacon_required = true; break; case SourceZ::EXTNAV: visualodom_required = true; break; case SourceZ::NONE: break; default: // invalid posz value hal.util->snprintf(failure_msg, failure_msg_len, "Check EK3_SRC%d_POSZ", (int)i+1); return false; } // check velz switch ((SourceZ)_source_set[i].velz.get()) { case SourceZ::NONE: break; case SourceZ::GPS: gps_required = true; break; case SourceZ::EXTNAV: visualodom_required = true; break; case SourceZ::BARO: case SourceZ::RANGEFINDER: case SourceZ::BEACON: default: // invalid velz value hal.util->snprintf(failure_msg, failure_msg_len, "Check EK3_SRC%d_VELZ", (int)i+1); return false; } } // check yaw switch ((SourceYaw)_source_set[i].yaw.get()) { case SourceYaw::NONE: case SourceYaw::GPS: // valid yaw value break; case SourceYaw::COMPASS: // skip compass check for easier user setup of compass-less operation break; case SourceYaw::GPS_COMPASS_FALLBACK: compass_required = true; break; case SourceYaw::EXTNAV: visualodom_required = true; break; case SourceYaw::GSF: gps_required = true; break; default: // invalid yaw value hal.util->snprintf(failure_msg, failure_msg_len, "Check EK3_SRC%d_YAW", (int)i+1); return false; } } // check all required sensors are available const char* ekf_requires_msg = "EK3 sources require %s"; if (baro_required && (dal.baro().num_instances() == 0)) { hal.util->snprintf(failure_msg, failure_msg_len, ekf_requires_msg, "Baro"); return false; } if (beacon_required) { #if AP_BEACON_ENABLED const bool beacon_available = (dal.beacon() != nullptr && dal.beacon()->enabled()); #else const bool beacon_available = false; #endif if (!beacon_available) { hal.util->snprintf(failure_msg, failure_msg_len, ekf_requires_msg, "Beacon"); return false; } } if (compass_required && (dal.compass().get_num_enabled() == 0)) { hal.util->snprintf(failure_msg, failure_msg_len, ekf_requires_msg, "Compass"); return false; } if (gps_required && (dal.gps().num_sensors() == 0)) { hal.util->snprintf(failure_msg, failure_msg_len, ekf_requires_msg, "GPS"); return false; } if (optflow_required && !dal.opticalflow_enabled()) { hal.util->snprintf(failure_msg, failure_msg_len, ekf_requires_msg, "OpticalFlow"); return false; } if (rangefinder_required && (dal.rangefinder() == nullptr || !dal.rangefinder()->has_orientation(ROTATION_PITCH_270))) { hal.util->snprintf(failure_msg, failure_msg_len, ekf_requires_msg, "RangeFinder"); return false; } if (visualodom_required) { bool visualodom_available = false; #if HAL_VISUALODOM_ENABLED auto *vo = AP::dal().visualodom(); visualodom_available = vo && vo->enabled(); #endif if (!visualodom_available) { hal.util->snprintf(failure_msg, failure_msg_len, ekf_requires_msg, "VisualOdom"); return false; } } if (wheelencoder_required && !dal.wheelencoder_enabled()) { hal.util->snprintf(failure_msg, failure_msg_len, ekf_requires_msg, "WheelEncoder"); return false; } return true; } // return true if ext nav is enabled on any source bool AP_NavEKF_Source::ext_nav_enabled(void) const { for (uint8_t i=0; i