Files
paparazzi/sw/airborne/subsystems/gps.c
T
2016-02-05 15:23:48 -08:00

386 lines
11 KiB
C

/*
* Copyright (C) 2008-2011 The Paparazzi Team
*
* This file is part of paparazzi.
*
* paparazzi 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 2, or (at your option)
* any later version.
*
* paparazzi 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 paparazzi; see the file COPYING. If not, write to
* the Free Software Foundation, 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/** @file gps.c
* @brief Device independent GPS code
*
*/
#include "subsystems/abi.h"
#include "subsystems/gps.h"
#include "led.h"
#include "subsystems/settings.h"
#include "generated/settings.h"
#ifdef GPS_POWER_GPIO
#include "mcu_periph/gpio.h"
#ifndef GPS_POWER_GPIO_ON
#define GPS_POWER_GPIO_ON gpio_set
#endif
#endif
#define MSEC_PER_WEEK (1000*60*60*24*7)
struct GpsState gps;
struct GpsTimeSync gps_time_sync;
#ifndef PrimaryGpsImpl
#warning "PrimaryGpsImpl not set!"
#else
PRINT_CONFIG_VAR(PrimaryGpsImpl)
#endif
#ifdef USE_MULTI_GPS
#ifndef SecondaryGpsImpl
#warning "SecondaryGpsImpl not set!"
#else
PRINT_CONFIG_VAR(SecondaryGpsImpl)
#endif
static uint8_t current_gps_id = 0;
#endif /*USE_MULTI_GPS*/
#define __PrimaryGpsRegister(_x) _x ## _gps_register()
#define _PrimaryGpsRegister(_x) __PrimaryGpsRegister(_x)
#define PrimaryGpsRegister() _PrimaryGpsRegister(PrimaryGpsImpl)
#define __SecondaryGpsRegister(_x) _x ## _gps_register()
#define _SecondaryGpsRegister(_x) __SecondaryGpsRegister(_x)
#define SecondaryGpsRegister() _SecondaryGpsRegister(SecondaryGpsImpl)
#define TIME_TO_SWITCH 5000 //ten s in ms
uint8_t multi_gps_mode;
/* gps structs */
struct GpsInstance {;
ImplGpsInit init;
ImplGpsEvent event;
uint8_t id;
};
struct GpsInstance GpsInstances[GPS_NUM_INSTANCES];
#if PERIODIC_TELEMETRY
#include "subsystems/datalink/telemetry.h"
static void send_svinfo_id(struct transport_tx *trans, struct link_device *dev,
uint8_t svid)
{
if (svid < GPS_NB_CHANNELS) {
pprz_msg_send_SVINFO(trans, dev, AC_ID, &svid,
&gps.svinfos[svid].svid, &gps.svinfos[svid].flags,
&gps.svinfos[svid].qi, &gps.svinfos[svid].cno,
&gps.svinfos[svid].elev, &gps.svinfos[svid].azim);
}
}
/** send SVINFO message (regardless of state) */
static void send_svinfo(struct transport_tx *trans, struct link_device *dev)
{
static uint8_t i = 0;
if (i == gps.nb_channels) { i = 0; }
send_svinfo_id(trans, dev, i);
i++;
}
/** send SVINFO message if updated.
* send SVINFO for all satellites while no GPS fix,
* after 3D fix, send avialable sats only when there is new information
*/
static inline void send_svinfo_available(struct transport_tx *trans, struct link_device *dev)
{
static uint8_t i = 0;
static uint8_t last_cnos[GPS_NB_CHANNELS];
if (i >= gps.nb_channels) { i = 0; }
// send SVINFO for all satellites while no GPS fix,
// after 3D fix, send avialable sats if they were updated
if (gps.fix < GPS_FIX_3D) {
send_svinfo_id(trans, dev, i);
} else if (gps.svinfos[i].cno != last_cnos[i]) {
send_svinfo_id(trans, dev, i);
last_cnos[i] = gps.svinfos[i].cno;
}
i++;
}
static void send_gps(struct transport_tx *trans, struct link_device *dev)
{
uint8_t zero = 0;
int16_t climb = -gps.ned_vel.z;
int16_t course = (DegOfRad(gps.course) / ((int32_t)1e6));
struct UtmCoor_i utm = utm_int_from_gps(&gps, 0);
pprz_msg_send_GPS(trans, dev, AC_ID, &gps.fix,
&utm.east, &utm.north,
&course, &gps.hmsl, &gps.gspeed, &climb,
&gps.week, &gps.tow, &utm.zone, &zero);
// send SVINFO for available satellites that have new data
send_svinfo_available(trans, dev);
}
static void send_gps_int(struct transport_tx *trans, struct link_device *dev)
{
pprz_msg_send_GPS_INT(trans, dev, AC_ID,
&gps.ecef_pos.x, &gps.ecef_pos.y, &gps.ecef_pos.z,
&gps.lla_pos.lat, &gps.lla_pos.lon, &gps.lla_pos.alt,
&gps.hmsl,
&gps.ecef_vel.x, &gps.ecef_vel.y, &gps.ecef_vel.z,
&gps.pacc, &gps.sacc,
&gps.tow,
&gps.pdop,
&gps.num_sv,
&gps.fix,
&gps.comp_id);
// send SVINFO for available satellites that have new data
send_svinfo_available(trans, dev);
}
static void send_gps_lla(struct transport_tx *trans, struct link_device *dev)
{
uint8_t err = 0;
int16_t climb = -gps.ned_vel.z;
int16_t course = (DegOfRad(gps.course) / ((int32_t)1e6));
pprz_msg_send_GPS_LLA(trans, dev, AC_ID,
&gps.lla_pos.lat, &gps.lla_pos.lon, &gps.lla_pos.alt,
&gps.hmsl, &course, &gps.gspeed, &climb,
&gps.week, &gps.tow,
&gps.fix, &err);
}
static void send_gps_sol(struct transport_tx *trans, struct link_device *dev)
{
pprz_msg_send_GPS_SOL(trans, dev, AC_ID, &gps.pacc, &gps.sacc, &gps.pdop, &gps.num_sv);
}
#endif
void gps_periodic_check(void)
{
if (sys_time.nb_sec - gps.last_msg_time > GPS_TIMEOUT) {
gps.fix = GPS_FIX_NONE;
}
}
#ifdef USE_MULTI_GPS
static uint8_t gps_multi_switch(struct GpsState *gps_s) {
static uint32_t time_since_last_gps_switch = 0;
if (multi_gps_mode == GPS_MODE_PRIMARY){
return GpsInstances[PRIMARY_GPS_INSTANCE].id;
} else if (multi_gps_mode == GPS_MODE_SECONDARY){
return GpsInstances[SECONDARY_GPS_INSTANCE].id;
} else{
if (gps_s->fix > gps.fix){
return gps_s->comp_id;
} else if (gps.fix > gps_s->fix){
return gps.comp_id;
} else{
if (get_sys_time_msec() - time_since_last_gps_switch > TIME_TO_SWITCH) {
if (gps_s->num_sv > gps.num_sv) {
current_gps_id = gps_s->comp_id;
time_since_last_gps_switch = get_sys_time_msec();
} else if (gps.num_sv > gps_s->num_sv) {
current_gps_id = gps.comp_id;
time_since_last_gps_switch = get_sys_time_msec();
}
}
}
}
return current_gps_id;
}
#endif /*USE_MULTI_GPS*/
static abi_event gps_ev;
static void gps_cb(uint8_t sender_id,
uint32_t stamp __attribute__((unused)),
struct GpsState *gps_s)
{
if (sender_id == GPS_MULTI_ID) {
return;
}
uint32_t now_ts = get_sys_time_usec();
#ifdef USE_MULTI_GPS
current_gps_id = gps_multi_switch(gps_s);
if (gps_s->comp_id == current_gps_id) {
gps = *gps_s;
AbiSendMsgGPS(GPS_MULTI_ID, now_ts, gps_s);
}
#else
gps = *gps_s;
AbiSendMsgGPS(GPS_MULTI_ID, now_ts, gps_s);
#endif
}
/*
* handle gps switching and updating gps instances
*/
void GpsEvent(void) {
// run each gps event
uint8_t i;
for ( i = 0 ; i < GPS_NUM_INSTANCES ; i++) {
GpsInstances[i].event();
}
}
/*
* register gps structs for callback
*/
void gps_register_impl(ImplGpsInit init, ImplGpsEvent event, uint8_t id, int8_t instance)
{
GpsInstances[instance].init = init;
GpsInstances[instance].event = event;
GpsInstances[instance].id = id;
GpsInstances[instance].init();
}
void gps_init(void)
{
// #ifdef USE_MULTI_GPS
multi_gps_mode = MULTI_GPS_MODE;
// #endif
gps.valid_fields = 0;
gps.fix = GPS_FIX_NONE;
gps.week = 0;
gps.tow = 0;
gps.cacc = 0;
gps.last_3dfix_ticks = 0;
gps.last_3dfix_time = 0;
gps.last_msg_ticks = 0;
gps.last_msg_time = 0;
#ifdef GPS_POWER_GPIO
gpio_setup_output(GPS_POWER_GPIO);
GPS_POWER_GPIO_ON(GPS_POWER_GPIO);
#endif
#ifdef GPS_LED
LED_OFF(GPS_LED);
#endif
#ifdef PrimaryGpsImpl
PrimaryGpsRegister();
#endif
#ifdef SecondaryGpsImpl
SecondaryGpsRegister();
#endif
AbiBindMsgGPS(ABI_BROADCAST, &gps_ev, gps_cb);
#if PERIODIC_TELEMETRY
register_periodic_telemetry(DefaultPeriodic, PPRZ_MSG_ID_GPS, send_gps);
register_periodic_telemetry(DefaultPeriodic, PPRZ_MSG_ID_GPS_INT, send_gps_int);
register_periodic_telemetry(DefaultPeriodic, PPRZ_MSG_ID_GPS_LLA, send_gps_lla);
register_periodic_telemetry(DefaultPeriodic, PPRZ_MSG_ID_GPS_SOL, send_gps_sol);
register_periodic_telemetry(DefaultPeriodic, PPRZ_MSG_ID_SVINFO, send_svinfo);
#endif
}
uint32_t gps_tow_from_sys_ticks(uint32_t sys_ticks)
{
uint32_t clock_delta;
uint32_t time_delta;
uint32_t itow_now;
if (sys_ticks < gps_time_sync.t0_ticks) {
clock_delta = (0xFFFFFFFF - sys_ticks) + gps_time_sync.t0_ticks + 1;
} else {
clock_delta = sys_ticks - gps_time_sync.t0_ticks;
}
time_delta = msec_of_sys_time_ticks(clock_delta);
itow_now = gps_time_sync.t0_tow + time_delta;
if (itow_now > MSEC_PER_WEEK) {
itow_now %= MSEC_PER_WEEK;
}
return itow_now;
}
/**
* Default parser for GPS injected data
*/
void WEAK gps_inject_data(uint8_t packet_id __attribute__((unused)), uint8_t length __attribute__((unused)), uint8_t *data __attribute__((unused))){
}
/**
* Convenience function to get utm position from GPS state
*/
struct UtmCoor_f utm_float_from_gps(struct GpsState *gps_s, uint8_t zone)
{
struct UtmCoor_f utm;
utm.alt = 0.f;
if (bit_is_set(gps_s->valid_fields, GPS_VALID_POS_UTM_BIT)) {
// A real UTM position is available, use the correct zone
utm.zone = gps_s->utm_pos.zone;
utm.east = gps_s->utm_pos.east / 100.0f;
utm.north = gps_s->utm_pos.north / 100.0f;
}
else {
struct LlaCoor_f lla;
LLA_FLOAT_OF_BFP(lla, gps_s->lla_pos);
// Check if zone should be computed
if (zone > 0) {
utm.zone = zone;
} else {
utm.zone = (gps_s->lla_pos.lon / 1e7 + 180) / 6 + 1;
}
/* Recompute UTM coordinates in this zone */
utm_of_lla_f(&utm, &lla);
}
return utm;
}
struct UtmCoor_i utm_int_from_gps(struct GpsState *gps_s, uint8_t zone)
{
struct UtmCoor_i utm;
utm.alt = 0;
if (bit_is_set(gps_s->valid_fields, GPS_VALID_POS_UTM_BIT)) {
// A real UTM position is available, use the correct zone
utm.zone = gps_s->utm_pos.zone;
utm.east = gps_s->utm_pos.east;
utm.north = gps_s->utm_pos.north;
}
else {
struct LlaCoor_f lla;
LLA_FLOAT_OF_BFP(lla, gps_s->lla_pos);
// Check if zone should be computed
if (zone > 0) {
utm.zone = zone;
} else {
utm.zone = (gps_s->lla_pos.lon / 1e7 + 180) / 6 + 1;
}
/* Recompute UTM coordinates in this zone */
struct UtmCoor_f utm_f;
utm_f.zone = utm.zone;
utm_of_lla_f(&utm_f, &lla);
/* convert to fixed point in cm */
utm.east = utm_f.east * 100;
utm.north = utm_f.north * 100;
}
return utm;
}