You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
286 lines
6.9 KiB
286 lines
6.9 KiB
// -*- tab-width: 4; Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- |
|
|
|
|
|
|
|
#include <AP_Common.h> |
|
#include <AP_Math.h> |
|
#include <AP_HAL.h> |
|
#include <AP_Notify.h> |
|
#include "GPS.h" |
|
|
|
extern const AP_HAL::HAL& hal; |
|
|
|
#define GPS_DEBUGGING 0 |
|
|
|
#if GPS_DEBUGGING |
|
# define Debug(fmt, args ...) do {hal.console->printf("%s:%d: " fmt "\n", __FUNCTION__, __LINE__, ## args); hal.scheduler->delay(0); } while(0) |
|
#else |
|
# define Debug(fmt, args ...) |
|
#endif |
|
|
|
GPS::GPS(void) : |
|
// ensure all the inherited fields are zeroed |
|
time_week_ms(0), |
|
time_week(0), |
|
latitude(0), |
|
longitude(0), |
|
altitude_cm(0), |
|
ground_speed_cm(0), |
|
ground_course_cd(0), |
|
speed_3d_cm(0), |
|
hdop(0), |
|
num_sats(0), |
|
new_data(false), |
|
fix(FIX_NONE), |
|
valid_read(false), |
|
last_fix_time(0), |
|
_have_raw_velocity(false), |
|
_last_gps_time(0), |
|
_idleTimer(0), |
|
_status(GPS::NO_FIX), |
|
_last_ground_speed_cm(0), |
|
_velocity_north(0), |
|
_velocity_east(0), |
|
_velocity_down(0) |
|
{ |
|
} |
|
|
|
void |
|
GPS::update(void) |
|
{ |
|
bool result; |
|
uint32_t tnow; |
|
|
|
// call the GPS driver to process incoming data |
|
result = read(); |
|
|
|
tnow = hal.scheduler->millis(); |
|
|
|
// if we did not get a message, and the idle timer of 1.2 seconds has expired, re-init |
|
if (!result) { |
|
if ((tnow - _idleTimer) > 1200) { |
|
Debug("gps read timeout %lu %lu", (unsigned long)tnow, (unsigned long)_idleTimer); |
|
_status = NO_GPS; |
|
|
|
init(_port, _nav_setting); |
|
// reset the idle timer |
|
_idleTimer = tnow; |
|
} |
|
} else { |
|
// we got a message, update our status correspondingly |
|
if (fix == FIX_3D) { |
|
_status = GPS_OK_FIX_3D; |
|
}else if (fix == FIX_2D) { |
|
_status = GPS_OK_FIX_2D; |
|
}else{ |
|
_status = NO_FIX; |
|
} |
|
|
|
valid_read = true; |
|
new_data = true; |
|
|
|
// reset the idle timer |
|
_idleTimer = tnow; |
|
|
|
if (_status >= GPS_OK_FIX_2D) { |
|
last_fix_time = _idleTimer; |
|
_last_ground_speed_cm = ground_speed_cm; |
|
|
|
if (_have_raw_velocity) { |
|
// the GPS is able to give us velocity numbers directly |
|
_velocity_north = _vel_north * 0.01f; |
|
_velocity_east = _vel_east * 0.01f; |
|
_velocity_down = _vel_down * 0.01f; |
|
} else { |
|
float gps_heading = ToRad(ground_course_cd * 0.01f); |
|
float gps_speed = ground_speed_cm * 0.01f; |
|
float sin_heading, cos_heading; |
|
|
|
cos_heading = cosf(gps_heading); |
|
sin_heading = sinf(gps_heading); |
|
|
|
_velocity_north = gps_speed * cos_heading; |
|
_velocity_east = gps_speed * sin_heading; |
|
|
|
// no good way to get descent rate |
|
_velocity_down = 0; |
|
} |
|
} |
|
} |
|
|
|
// update notify with gps status |
|
AP_Notify::flags.gps_status = _status; |
|
} |
|
|
|
void |
|
GPS::setHIL(uint64_t _time_epoch_ms, float _latitude, float _longitude, float _altitude, |
|
float _ground_speed, float _ground_course, float _speed_3d, uint8_t _num_sats) |
|
{ |
|
} |
|
|
|
// XXX this is probably the wrong way to do it, too |
|
void |
|
GPS::_error(const char *msg) |
|
{ |
|
hal.console->println(msg); |
|
} |
|
|
|
/// |
|
/// write a block of configuration data to a GPS |
|
/// |
|
void GPS::_write_progstr_block(AP_HAL::UARTDriver *_fs, const prog_char *pstr, uint8_t size) |
|
{ |
|
while (size--) { |
|
_fs->write(pgm_read_byte(pstr++)); |
|
} |
|
} |
|
|
|
/* |
|
a prog_char block queue, used to send out config commands to a GPS |
|
in 16 byte chunks. This saves us having to have a 128 byte GPS send |
|
buffer, while allowing us to avoid a long delay in sending GPS init |
|
strings while waiting for the GPS auto detection to happen |
|
*/ |
|
|
|
// maximum number of pending progstrings |
|
#define PROGSTR_QUEUE_SIZE 3 |
|
|
|
struct progstr_queue { |
|
const prog_char *pstr; |
|
uint8_t ofs, size; |
|
}; |
|
|
|
static struct { |
|
AP_HAL::UARTDriver *fs; |
|
uint8_t queue_size; |
|
uint8_t idx, next_idx; |
|
struct progstr_queue queue[PROGSTR_QUEUE_SIZE]; |
|
} progstr_state; |
|
|
|
void GPS::_send_progstr(AP_HAL::UARTDriver *_fs, const prog_char *pstr, uint8_t size) |
|
{ |
|
progstr_state.fs = _fs; |
|
struct progstr_queue *q = &progstr_state.queue[progstr_state.next_idx]; |
|
q->pstr = pstr; |
|
q->size = size; |
|
q->ofs = 0; |
|
progstr_state.next_idx++; |
|
if (progstr_state.next_idx == PROGSTR_QUEUE_SIZE) { |
|
progstr_state.next_idx = 0; |
|
} |
|
} |
|
|
|
void GPS::_update_progstr(void) |
|
{ |
|
struct progstr_queue *q = &progstr_state.queue[progstr_state.idx]; |
|
// quick return if nothing to do |
|
if (q->size == 0 || progstr_state.fs->tx_pending()) { |
|
return; |
|
} |
|
uint8_t nbytes = q->size - q->ofs; |
|
if (nbytes > 16) { |
|
nbytes = 16; |
|
} |
|
//hal.console->printf_P(PSTR("writing %u bytes\n"), (unsigned)nbytes); |
|
_write_progstr_block(progstr_state.fs, q->pstr+q->ofs, nbytes); |
|
q->ofs += nbytes; |
|
if (q->ofs == q->size) { |
|
q->size = 0; |
|
progstr_state.idx++; |
|
if (progstr_state.idx == PROGSTR_QUEUE_SIZE) { |
|
progstr_state.idx = 0; |
|
} |
|
} |
|
} |
|
|
|
int32_t GPS::_swapl(const void *bytes) const |
|
{ |
|
const uint8_t *b = (const uint8_t *)bytes; |
|
union { |
|
int32_t v; |
|
uint8_t b[4]; |
|
} u; |
|
|
|
u.b[0] = b[3]; |
|
u.b[1] = b[2]; |
|
u.b[2] = b[1]; |
|
u.b[3] = b[0]; |
|
|
|
return(u.v); |
|
} |
|
|
|
int16_t GPS::_swapi(const void *bytes) const |
|
{ |
|
const uint8_t *b = (const uint8_t *)bytes; |
|
union { |
|
int16_t v; |
|
uint8_t b[2]; |
|
} u; |
|
|
|
u.b[0] = b[1]; |
|
u.b[1] = b[0]; |
|
|
|
return(u.v); |
|
} |
|
|
|
/** |
|
current time since the unix epoch in microseconds |
|
|
|
This costs about 60 usec on AVR2560 |
|
*/ |
|
uint64_t GPS::time_epoch_usec(void) |
|
{ |
|
if (_last_gps_time == 0) { |
|
return 0; |
|
} |
|
const uint64_t ms_per_week = 7000ULL*86400ULL; |
|
const uint64_t unix_offset = 17000ULL*86400ULL + 52*10*7000ULL*86400ULL - 15000ULL; |
|
uint64_t fix_time_ms = unix_offset + time_week*ms_per_week + time_week_ms; |
|
// add in the milliseconds since the last fix |
|
return (fix_time_ms + (hal.scheduler->millis() - _last_gps_time)) * 1000ULL; |
|
} |
|
|
|
|
|
/** |
|
fill in time_week_ms and time_week from BCD date and time components |
|
assumes MTK19 millisecond form of bcd_time |
|
|
|
This function takes about 340 usec on the AVR2560 |
|
*/ |
|
void GPS::_make_gps_time(uint32_t bcd_date, uint32_t bcd_milliseconds) |
|
{ |
|
uint8_t year, mon, day, hour, min, sec; |
|
uint16_t msec; |
|
|
|
year = bcd_date % 100; |
|
mon = (bcd_date / 100) % 100; |
|
day = bcd_date / 10000; |
|
msec = bcd_milliseconds % 1000; |
|
|
|
uint32_t v = bcd_milliseconds; |
|
msec = v % 1000; v /= 1000; |
|
sec = v % 100; v /= 100; |
|
min = v % 100; v /= 100; |
|
hour = v % 100; v /= 100; |
|
|
|
int8_t rmon = mon - 2; |
|
if (0 >= rmon) { |
|
rmon += 12; |
|
year -= 1; |
|
} |
|
|
|
// get time in seconds since unix epoch |
|
uint32_t ret = (year/4) - 15 + 367*rmon/12 + day; |
|
ret += year*365 + 10501; |
|
ret = ret*24 + hour; |
|
ret = ret*60 + min; |
|
ret = ret*60 + sec; |
|
|
|
// convert to time since GPS epoch |
|
ret -= 272764785UL; |
|
|
|
// get GPS week and time |
|
time_week = ret / (7*86400UL); |
|
time_week_ms = (ret % (7*86400UL)) * 1000; |
|
time_week_ms += msec; |
|
}
|
|
|