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.
221 lines
6.5 KiB
221 lines
6.5 KiB
// -*- tab-width: 4; Mode: C++; c-basic-offset: 4; indent-tabs-mode: t -*- |
|
|
|
/// @file AP_GPS_Auto.cpp |
|
/// @brief Simple GPS auto-detection logic. |
|
|
|
#include <FastSerial.h> |
|
#include <AP_Common.h> |
|
|
|
#include "AP_GPS.h" // includes AP_GPS_Auto.h |
|
|
|
// Define this to add NMEA to the auto-detection cycle. |
|
// |
|
// Note that there is a potential race where NMEA data may overlap with |
|
// the commands that switch a GPS out of NMEA mode that can cause |
|
// the GPS to switch to binary mode at the same time that this code |
|
// detects it as being in NMEA mode. |
|
// |
|
//#define WITH_NMEA_MODE 1 |
|
|
|
static unsigned int baudrates[] = {38400U, 57600U, 9600U, 4800U}; |
|
|
|
const prog_char AP_GPS_Auto::_mtk_set_binary[] PROGMEM = MTK_SET_BINARY; |
|
const prog_char AP_GPS_Auto::_ublox_set_binary[] PROGMEM = UBLOX_SET_BINARY; |
|
const prog_char AP_GPS_Auto::_sirf_set_binary[] PROGMEM = SIRF_SET_BINARY; |
|
|
|
|
|
AP_GPS_Auto::AP_GPS_Auto(FastSerial *s, GPS **gps) : |
|
GPS(s), |
|
_fs(s), |
|
_gps(gps) |
|
{ |
|
} |
|
|
|
// Do nothing at init time - it may be too early to try detecting the GPS |
|
// |
|
void |
|
AP_GPS_Auto::init(void) |
|
{ |
|
idleTimeout = 1200; |
|
if (callback == NULL) callback = delay; |
|
} |
|
|
|
// Called the first time that a client tries to kick the GPS to update. |
|
// |
|
// We detect the real GPS, then update the pointer we have been called through |
|
// and return. |
|
// |
|
/// @todo This routine spends a long time trying to detect a GPS. That's not strictly |
|
/// desirable; it might be a good idea to rethink the logic here to make it |
|
/// more asynchronous, so that other parts of the system can get a chance |
|
/// to run while GPS detection is in progress. |
|
/// |
|
bool |
|
AP_GPS_Auto::read(void) |
|
{ |
|
GPS *gps; |
|
uint8_t i; |
|
unsigned long then; |
|
|
|
// Loop through possible baudrates trying to detect a GPS at one of them. |
|
// |
|
// Note that we need to have a FastSerial rather than a Stream here because |
|
// Stream has no idea of line speeds. FastSerial is quite OK with us calling |
|
// ::begin any number of times. |
|
// |
|
for (i = 0; i < (sizeof(baudrates) / sizeof(baudrates[0])); i++) { |
|
|
|
_fs->begin(baudrates[i]); |
|
if (NULL != (gps = _detect())) { |
|
|
|
// configure the detected GPS and give it a chance to listen to its device |
|
gps->init(); |
|
then = millis(); |
|
while ((millis() - then) < 1200) { |
|
// if we get a successful update from the GPS, we are done |
|
gps->new_data = false; |
|
gps->update(); |
|
if (gps->new_data) { |
|
Serial.println_P(PSTR("OK")); |
|
*_gps = gps; |
|
return true; |
|
} |
|
} |
|
// GPS driver failed to parse any data from GPS, |
|
// delete the driver and continue the process. |
|
Serial.println_P(PSTR("failed, retrying")); |
|
delete gps; |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
// |
|
// Perform one iteration of the auto-detection process. |
|
// |
|
GPS * |
|
AP_GPS_Auto::_detect(void) |
|
{ |
|
unsigned long then; |
|
int fingerprint[4]; |
|
int tries; |
|
GPS *gps; |
|
|
|
// |
|
// Loop attempting to detect a recognized GPS |
|
// |
|
Serial.print('G'); |
|
gps = NULL; |
|
for (tries = 0; tries < 2; tries++) { |
|
|
|
// |
|
// Empty the serial buffer and wait for 50ms of quiet. |
|
// |
|
// XXX We can detect babble by counting incoming characters, but |
|
// what would we do about it? |
|
// |
|
_port->flush(); |
|
then = millis(); |
|
do { |
|
callback(1); |
|
if (_port->available()) { |
|
then = millis(); |
|
_port->read(); |
|
} |
|
} while ((millis() - then) < 50); |
|
|
|
// |
|
// Collect four characters to fingerprint a device |
|
// |
|
// If we take more than 1200ms to receive four characters, abort. |
|
// This will normally only be the case where there is no GPS attached. |
|
// |
|
while (_port->available() < 4) { |
|
callback(1); |
|
if ((millis() - then) > 1200) { |
|
Serial.print('!'); |
|
return NULL; |
|
} |
|
} |
|
fingerprint[0] = _port->read(); |
|
fingerprint[1] = _port->read(); |
|
fingerprint[2] = _port->read(); |
|
fingerprint[3] = _port->read(); |
|
|
|
// |
|
// ublox or MTK in DIYD binary mode (whose smart idea was |
|
// it to make the MTK look sort-of like it was talking UBX?) |
|
// |
|
if ((0xb5 == fingerprint[0]) && |
|
(0x62 == fingerprint[1]) && |
|
(0x01 == fingerprint[2])) { |
|
|
|
// message 5 is MTK pretending to talk UBX |
|
if (0x05 == fingerprint[3]) { |
|
gps = new AP_GPS_MTK(_port); |
|
Serial.print_P(PSTR(" MTK1.4 ")); |
|
break; |
|
} |
|
|
|
// any other message is ublox |
|
gps = new AP_GPS_UBLOX(_port); |
|
Serial.print_P(PSTR(" ublox ")); |
|
break; |
|
} |
|
|
|
// |
|
// MTK v1.6 |
|
// |
|
if ((0xd0 == fingerprint[0]) && |
|
(0xdd == fingerprint[1]) && |
|
(0x20 == fingerprint[2])) { |
|
gps = new AP_GPS_MTK16(_port); |
|
Serial.print_P(PSTR(" MTK1.6 ")); |
|
break; |
|
} |
|
|
|
// |
|
// SIRF in binary mode |
|
// |
|
if ((0xa0 == fingerprint[0]) && |
|
(0xa2 == fingerprint[1])) { |
|
gps = new AP_GPS_SIRF(_port); |
|
Serial.print_P(PSTR(" SiRF ")); |
|
break; |
|
} |
|
|
|
// |
|
// If we haven't spammed the various init strings, send them now |
|
// and retry to avoid a false-positive on the NMEA detector. |
|
// |
|
if (0 == tries) { |
|
Serial.print('*'); |
|
// use the FastSerial port handle so that we can use PROGMEM strings |
|
_fs->println_P((const prog_char_t *)_mtk_set_binary); |
|
_fs->println_P((const prog_char_t *)_ublox_set_binary); |
|
_fs->println_P((const prog_char_t *)_sirf_set_binary); |
|
|
|
// give the GPS time to react to the settings |
|
callback(100); |
|
continue; |
|
} else { |
|
Serial.print('?'); |
|
} |
|
|
|
#if WITH_NMEA_MODE |
|
// |
|
// Something talking NMEA |
|
// |
|
if (('$' == fingerprint[0]) && |
|
(('G' == fingerprint[1]) || ('P' == fingerprint[1]))) { |
|
|
|
// XXX this may be a bit presumptive, might want to give the GPS a couple of |
|
// iterations around the loop to react to init strings? |
|
gps = new AP_GPS_NMEA(_port); |
|
break; |
|
} |
|
#endif |
|
} |
|
return(gps); |
|
} |
|
|
|
|