#pragma once
#include "AP_GPS_config.h"
#if AP_GPS_ENABLED
#include <AP_HAL/AP_HAL.h>
#include <inttypes.h>
#include <AP_Common/AP_Common.h>
#include <AP_Common/Location.h>
#include <AP_Param/AP_Param.h>
#include "GPS_detect_state.h"
#include <AP_Math/AP_Math.h>
#include <AP_MSP/msp.h>
#include <AP_ExternalAHRS/AP_ExternalAHRS.h>
#include <SITL/SIM_GPS.h>
#include <GCS_MAVLink/GCS_MAVLink.h>
#define GPS_UNKNOWN_DOP UINT16_MAX
#define GPS_LEAPSECONDS_MILLIS 18000ULL
#define UNIX_OFFSET_MSEC (17000ULL * 86400ULL + 52ULL * 10ULL * AP_MSEC_PER_WEEK - GPS_LEAPSECONDS_MILLIS)
#ifndef GPS_MOVING_BASELINE
#define GPS_MOVING_BASELINE GPS_MAX_RECEIVERS>1
#endif
#if GPS_MOVING_BASELINE
#include "MovingBase.h"
#endif
class AP_GPS_Backend;
class RTCM3_Parser;
class AP_GPS
{
friend class AP_GPS_Blended;
friend class AP_GPS_ERB;
friend class AP_GPS_GSOF;
friend class AP_GPS_MAV;
friend class AP_GPS_MSP;
friend class AP_GPS_ExternalAHRS;
friend class AP_GPS_NMEA;
friend class AP_GPS_NOVA;
friend class AP_GPS_PX4;
friend class AP_GPS_SBF;
friend class AP_GPS_SBP;
friend class AP_GPS_SBP2;
friend class AP_GPS_SIRF;
friend class AP_GPS_UBLOX;
friend class AP_GPS_Backend;
friend class AP_GPS_DroneCAN;
public:
AP_GPS();
CLASS_NO_COPY(AP_GPS);
static AP_GPS *get_singleton() {
return _singleton;
}
HAL_Semaphore &get_semaphore(void) {
return rsem;
}
enum GPS_Type {
GPS_TYPE_NONE = 0,
GPS_TYPE_AUTO = 1,
GPS_TYPE_UBLOX = 2,
GPS_TYPE_NMEA = 5,
GPS_TYPE_SIRF = 6,
GPS_TYPE_HIL = 7,
GPS_TYPE_SBP = 8,
GPS_TYPE_UAVCAN = 9,
GPS_TYPE_SBF = 10,
GPS_TYPE_GSOF = 11,
GPS_TYPE_ERB = 13,
GPS_TYPE_MAV = 14,
GPS_TYPE_NOVA = 15,
GPS_TYPE_HEMI = 16,
GPS_TYPE_UBLOX_RTK_BASE = 17,
GPS_TYPE_UBLOX_RTK_ROVER = 18,
GPS_TYPE_MSP = 19,
GPS_TYPE_ALLYSTAR = 20,
GPS_TYPE_EXTERNAL_AHRS = 21,
GPS_TYPE_UAVCAN_RTK_BASE = 22,
GPS_TYPE_UAVCAN_RTK_ROVER = 23,
GPS_TYPE_UNICORE_NMEA = 24,
GPS_TYPE_UNICORE_MOVINGBASE_NMEA = 25,
GPS_TYPE_SBF_DUAL_ANTENNA = 26,
#if AP_SIM_GPS_ENABLED
GPS_TYPE_SITL = 100,
#endif
};
bool is_rtk_base(uint8_t instance) const;
bool is_rtk_rover(uint8_t instance) const;
class Params {
public:
Params(void);
AP_Enum<GPS_Type> type;
AP_Int8 gnss_mode;
AP_Int16 rate_ms;
AP_Vector3f antenna_offset;
AP_Int16 delay_ms;
AP_Int8 com_port;
#if HAL_ENABLE_DRONECAN_DRIVERS
AP_Int32 node_id;
AP_Int32 override_node_id;
#endif
#if GPS_MOVING_BASELINE
MovingBase mb_params;
#endif
static const struct AP_Param::GroupInfo var_info[];
};
enum GPS_Status {
NO_GPS = 0,
NO_FIX = 1,
GPS_OK_FIX_2D = 2,
GPS_OK_FIX_3D = 3,
GPS_OK_FIX_3D_DGPS = 4,
GPS_OK_FIX_3D_RTK_FLOAT = 5,
GPS_OK_FIX_3D_RTK_FIXED = 6,
};
enum GPS_Engine_Setting {
GPS_ENGINE_NONE = -1,
GPS_ENGINE_PORTABLE = 0,
GPS_ENGINE_STATIONARY = 2,
GPS_ENGINE_PEDESTRIAN = 3,
GPS_ENGINE_AUTOMOTIVE = 4,
GPS_ENGINE_SEA = 5,
GPS_ENGINE_AIRBORNE_1G = 6,
GPS_ENGINE_AIRBORNE_2G = 7,
GPS_ENGINE_AIRBORNE_4G = 8
};
enum GPS_Role {
GPS_ROLE_NORMAL,
GPS_ROLE_MB_BASE,
GPS_ROLE_MB_ROVER,
};
enum class CovarianceType : uint8_t {
UNKNOWN = 0,
APPROXIMATED = 1,
DIAGONAL_KNOWN = 2,
KNOWN = 3,
};
struct GPS_State {
uint8_t instance;
GPS_Status status;
uint32_t time_week_ms;
uint16_t time_week;
Location location;
float ground_speed;
float ground_course;
float gps_yaw;
uint32_t gps_yaw_time_ms;
bool gps_yaw_configured;
uint16_t hdop;
uint16_t vdop;
uint8_t num_sats;
Vector3f velocity;
float speed_accuracy;
float horizontal_accuracy;
float vertical_accuracy;
float gps_yaw_accuracy;
bool have_vertical_velocity;
bool have_speed_accuracy;
bool have_horizontal_accuracy;
bool have_vertical_accuracy;
bool have_gps_yaw;
bool have_gps_yaw_accuracy;
float undulation;
bool have_undulation;
uint32_t last_gps_time_ms;
bool announced_detection;
uint64_t last_corrected_gps_time_us;
bool corrected_timestamp_updated;
uint32_t lagged_sample_count;
uint32_t rtk_time_week_ms;
uint16_t rtk_week_number;
uint32_t rtk_age_ms;
uint8_t rtk_num_sats;
uint8_t rtk_baseline_coords_type;
int32_t rtk_baseline_x_mm;
int32_t rtk_baseline_y_mm;
int32_t rtk_baseline_z_mm;
uint32_t rtk_accuracy;
int32_t rtk_iar_num_hypotheses;
float relPosHeading;
float relPosLength;
float relPosD;
float accHeading;
uint32_t relposheading_ts;
};
void init();
void set_default_type_for_gps1(uint8_t default_type) {
params[0].type.set_default(default_type);
}
void update(void);
void handle_msg(mavlink_channel_t chan, const mavlink_message_t &msg);
#if HAL_MSP_GPS_ENABLED
void handle_msp(const MSP::msp_gps_data_message_t &pkt);
#endif
#if AP_EXTERNAL_AHRS_ENABLED
bool get_first_external_instance(uint8_t& instance) const WARN_IF_UNUSED;
void handle_external(const AP_ExternalAHRS::gps_data_message_t &pkt, const uint8_t instance);
#endif
uint8_t num_sensors(void) const;
uint8_t primary_sensor(void) const {
return primary_instance;
}
GPS_Status status(uint8_t instance) const {
if (_force_disable_gps && state[instance].status > NO_FIX) {
return NO_FIX;
}
return state[instance].status;
}
GPS_Status status(void) const {
return status(primary_instance);
}
char status_onechar(void) const {
switch (status()) {
case AP_GPS::NO_GPS:
return ' ';
case AP_GPS::NO_FIX:
return '-';
case AP_GPS::GPS_OK_FIX_2D:
return '2';
case AP_GPS::GPS_OK_FIX_3D:
return '3';
case AP_GPS::GPS_OK_FIX_3D_DGPS:
return '4';
case AP_GPS::GPS_OK_FIX_3D_RTK_FLOAT:
return '5';
case AP_GPS::GPS_OK_FIX_3D_RTK_FIXED:
return '6';
}
return '?';
}
GPS_Status highest_supported_status(uint8_t instance) const WARN_IF_UNUSED;
const Location &location(uint8_t instance) const {
return state[instance].location;
}
const Location &location() const {
return location(primary_instance);
}
bool get_undulation(uint8_t instance, float &undulation) const;
bool get_undulation(float &undulation) const {
return get_undulation(primary_instance, undulation);
}
bool speed_accuracy(uint8_t instance, float &sacc) const;
bool speed_accuracy(float &sacc) const {
return speed_accuracy(primary_instance, sacc);
}
bool horizontal_accuracy(uint8_t instance, float &hacc) const;
bool horizontal_accuracy(float &hacc) const {
return horizontal_accuracy(primary_instance, hacc);
}
bool vertical_accuracy(uint8_t instance, float &vacc) const;
bool vertical_accuracy(float &vacc) const {
return vertical_accuracy(primary_instance, vacc);
}
CovarianceType position_covariance(const uint8_t instance, Matrix3f& cov) const WARN_IF_UNUSED;
const Vector3f &velocity(uint8_t instance) const {
return state[instance].velocity;
}
const Vector3f &velocity() const {
return velocity(primary_instance);
}
float ground_speed(uint8_t instance) const {
return state[instance].ground_speed;
}
float ground_speed() const {
return ground_speed(primary_instance);
}
float ground_course(uint8_t instance) const {
return state[instance].ground_course;
}
float ground_course() const {
return ground_course(primary_instance);
}
int32_t ground_course_cd(uint8_t instance) const {
return ground_course(instance) * 100;
}
int32_t ground_course_cd() const {
return ground_course_cd(primary_instance);
}
bool gps_yaw_deg(uint8_t instance, float &yaw_deg, float &accuracy_deg, uint32_t &time_ms) const;
bool gps_yaw_deg(float &yaw_deg, float &accuracy_deg, uint32_t &time_ms) const {
return gps_yaw_deg(primary_instance, yaw_deg, accuracy_deg, time_ms);
}
uint8_t num_sats(uint8_t instance) const {
return state[instance].num_sats;
}
uint8_t num_sats() const {
return num_sats(primary_instance);
}
uint32_t time_week_ms(uint8_t instance) const {
return state[instance].time_week_ms;
}
uint32_t time_week_ms() const {
return time_week_ms(primary_instance);
}
uint16_t time_week(uint8_t instance) const {
return state[instance].time_week;
}
uint16_t time_week() const {
return time_week(primary_instance);
}
uint16_t get_hdop(uint8_t instance) const {
return state[instance].hdop;
}
uint16_t get_hdop() const {
return get_hdop(primary_instance);
}
uint16_t get_vdop(uint8_t instance) const {
return state[instance].vdop;
}
uint16_t get_vdop() const {
return get_vdop(primary_instance);
}
uint32_t last_fix_time_ms(uint8_t instance) const {
return timing[instance].last_fix_time_ms;
}
uint32_t last_fix_time_ms(void) const {
return last_fix_time_ms(primary_instance);
}
uint32_t last_message_time_ms(uint8_t instance) const {
return timing[instance].last_message_time_ms;
}
uint32_t last_message_time_ms(void) const {
return last_message_time_ms(primary_instance);
}
uint16_t last_message_delta_time_ms(uint8_t instance) const {
return timing[instance].delta_time_ms;
}
uint16_t last_message_delta_time_ms(void) const {
return last_message_delta_time_ms(primary_instance);
}
bool have_vertical_velocity(uint8_t instance) const {
return state[instance].have_vertical_velocity;
}
bool have_vertical_velocity(void) const {
return have_vertical_velocity(primary_instance);
}
bool have_gps_yaw(uint8_t instance) const {
return !_force_disable_gps_yaw && state[instance].have_gps_yaw;
}
bool have_gps_yaw(void) const {
return have_gps_yaw(primary_instance);
}
bool have_gps_yaw_configured(uint8_t instance) const {
return state[instance].gps_yaw_configured;
}
bool get_lag(uint8_t instance, float &lag_sec) const;
bool get_lag(float &lag_sec) const {
return get_lag(primary_instance, lag_sec);
}
const Vector3f &get_antenna_offset(uint8_t instance) const;
void lock_port(uint8_t instance, bool locked);
void send_mavlink_gps_raw(mavlink_channel_t chan);
void send_mavlink_gps2_raw(mavlink_channel_t chan);
void send_mavlink_gps_rtk(mavlink_channel_t chan, uint8_t inst);
bool first_unconfigured_gps(uint8_t &instance) const WARN_IF_UNUSED;
void broadcast_first_configuration_failure_reason(void) const;
bool all_consistent(float &distance) const;
void send_blob_start(uint8_t instance);
void send_blob_start(uint8_t instance, const char *_blob, uint16_t size);
void send_blob_update(uint8_t instance);
uint64_t time_epoch_usec(uint8_t instance) const;
uint64_t time_epoch_usec(void) const {
return time_epoch_usec(primary_instance);
}
uint64_t last_message_epoch_usec(uint8_t instance) const;
uint64_t last_message_epoch_usec() const {
return last_message_epoch_usec(primary_instance);
}
static uint64_t istate_time_to_epoch_ms(uint16_t gps_week, uint32_t gps_ms);
static const struct AP_Param::GroupInfo var_info[];
#if HAL_LOGGING_ENABLED
void Write_AP_Logger_Log_Startup_messages();
#endif
void set_log_gps_bit(uint32_t bit) { _log_gps_bit = bit; }
bool is_healthy(uint8_t instance) const;
bool is_healthy(void) const { return is_healthy(primary_instance); }
bool prepare_for_arming(void);
bool pre_arm_checks(char failure_msg[], uint16_t failure_msg_len);
bool logging_failed(void) const;
bool logging_present(void) const { return _raw_data != 0; }
bool logging_enabled(void) const { return _raw_data != 0; }
void force_disable(bool disable) {
_force_disable_gps = disable;
}
void set_force_disable_yaw(bool disable) {
_force_disable_gps_yaw = disable;
}
void handle_gps_rtcm_fragment(uint8_t flags, const uint8_t *data, uint8_t len);
GPS_Type get_type(uint8_t instance) const {
return instance>=ARRAY_SIZE(params) ? GPS_Type::GPS_TYPE_NONE : params[instance].type;
}
uint32_t get_itow(uint8_t instance) const;
bool get_error_codes(uint8_t instance, uint32_t &error_codes) const;
bool get_error_codes(uint32_t &error_codes) const { return get_error_codes(primary_instance, error_codes); }
enum class SBAS_Mode : int8_t {
Disabled = 0,
Enabled = 1,
DoNotChange = 2,
};
#if GPS_MOVING_BASELINE
void inject_MBL_data(uint8_t* data, uint16_t length);
bool get_RelPosHeading(uint32_t ×tamp, float &relPosHeading, float &relPosLength, float &relPosD, float &accHeading) WARN_IF_UNUSED;
bool get_RTCMV3(const uint8_t *&bytes, uint16_t &len);
void clear_RTCMV3();
#endif
#if !AP_GPS_BLENDED_ENABLED
uint8_t get_auto_switch_type() const { return _auto_switch; }
#endif
void inject_data(const uint8_t *data, uint16_t len);
protected:
Params params[GPS_MAX_INSTANCES];
AP_Int8 _navfilter;
AP_Int8 _auto_switch;
AP_Int16 _sbp_logmask;
AP_Int8 _inject_to;
uint32_t _last_instance_swap_ms;
AP_Enum<SBAS_Mode> _sbas_mode;
AP_Int8 _min_elevation;
AP_Int8 _raw_data;
AP_Int8 _save_config;
AP_Int8 _auto_config;
AP_Int8 _blend_mask;
AP_Int16 _driver_options;
AP_Int8 _primary;
uint32_t _log_gps_bit = -1;
enum DriverOptions : int16_t {
UBX_MBUseUart2 = (1U << 0U),
SBF_UseBaseForYaw = (1U << 1U),
UBX_Use115200 = (1U << 2U),
UAVCAN_MBUseDedicatedBus = (1 << 3U),
HeightEllipsoid = (1U << 4),
GPSL5HealthOverride = (1U << 5),
AlwaysRTCMDecode = (1U << 6),
DisableRTCMDecode = (1U << 7),
};
bool option_set(const DriverOptions option) const {
return (uint8_t(_driver_options.get()) & uint8_t(option)) != 0;
}
private:
static AP_GPS *_singleton;
HAL_Semaphore rsem;
uint16_t get_rate_ms(uint8_t instance) const;
struct GPS_timing {
uint32_t last_fix_time_ms;
uint32_t last_message_time_ms;
uint16_t delta_time_ms;
uint8_t delayed_count;
float average_delta_ms;
};
GPS_timing timing[GPS_MAX_INSTANCES];
GPS_State state[GPS_MAX_INSTANCES];
AP_GPS_Backend *drivers[GPS_MAX_INSTANCES];
AP_HAL::UARTDriver *_port[GPS_MAX_RECEIVERS];
uint8_t primary_instance;
uint8_t num_instances;
uint8_t locked_ports;
struct detect_state {
uint32_t last_baud_change_ms;
uint8_t current_baud;
uint32_t probe_baud;
bool auto_detected_baud;
#if AP_GPS_UBLOX_ENABLED
struct UBLOX_detect_state ublox_detect_state;
#endif
#if AP_GPS_SIRF_ENABLED
struct SIRF_detect_state sirf_detect_state;
#endif
#if AP_GPS_NMEA_ENABLED
struct NMEA_detect_state nmea_detect_state;
#endif
#if AP_GPS_SBP_ENABLED
struct SBP_detect_state sbp_detect_state;
#endif
#if AP_GPS_SBP2_ENABLED
struct SBP2_detect_state sbp2_detect_state;
#endif
#if AP_GPS_ERB_ENABLED
struct ERB_detect_state erb_detect_state;
#endif
} detect_state[GPS_MAX_RECEIVERS];
struct {
const char *blob;
uint16_t remaining;
} initblob_state[GPS_MAX_RECEIVERS];
static const uint32_t _baudrates[];
static const char _initialisation_blob[];
static const char _initialisation_raw_blob[];
void detect_instance(uint8_t instance);
AP_GPS_Backend *_detect_instance(uint8_t instance);
void update_instance(uint8_t instance);
struct rtcm_buffer {
uint8_t fragments_received;
uint8_t sequence;
uint8_t fragment_count;
uint16_t total_length;
uint8_t buffer[MAVLINK_MSG_GPS_RTCM_DATA_FIELD_DATA_LEN*4];
} *rtcm_buffer;
struct {
uint16_t fragments_used;
uint16_t fragments_discarded;
} rtcm_stats;
void handle_gps_rtcm_data(mavlink_channel_t chan, const mavlink_message_t &msg);
void handle_gps_inject(const mavlink_message_t &msg);
void inject_data(uint8_t instance, const uint8_t *data, uint16_t len);
#if AP_GPS_BLENDED_ENABLED
bool _output_is_blended;
#endif
bool should_log() const;
bool needs_uart(GPS_Type type) const;
#if GPS_MAX_RECEIVERS > 1
void update_primary(void);
#endif
uint16_t gps_yaw_cdeg(uint8_t instance) const;
enum GPS_AUTO_CONFIG {
GPS_AUTO_CONFIG_DISABLE = 0,
GPS_AUTO_CONFIG_ENABLE_SERIAL_ONLY = 1,
GPS_AUTO_CONFIG_ENABLE_ALL = 2,
};
enum class GPSAutoSwitch {
NONE = 0,
USE_BEST = 1,
BLEND = 2,
USE_PRIMARY_IF_3D_FIX = 4,
};
bool _force_disable_gps;
bool _force_disable_gps_yaw;
void Write_GPS(uint8_t instance);
#if AP_GPS_RTCM_DECODE_ENABLED
struct {
RTCM3_Parser *parsers[MAVLINK_COMM_NUM_BUFFERS];
uint32_t sent_crc[32];
uint8_t sent_idx;
uint16_t seen_mav_channels;
} rtcm;
bool parse_rtcm_injection(mavlink_channel_t chan, const mavlink_gps_rtcm_data_t &pkt);
#endif
void convert_parameters();
};
namespace AP {
AP_GPS &gps();
};
#endif