Created
March 4, 2024 09:06
-
-
Save 404mskiran/10a55f66344b42c5ba6ba09b5093da96 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| ** Copyright 2006, The Android Open Source Project | |
| ** Copyright 2009, Michael Trimarchi <michael@panicking.kicks-ass.org> | |
| ** Copyright 2015, Keith Conger <keith.conger@gmail.com> | |
| ** | |
| ** 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 2, 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, write to the Free Software Foundation, Inc., 59 | |
| ** Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |
| **/ | |
| #include <errno.h> | |
| #include <pthread.h> | |
| #include <termios.h> | |
| #include <fcntl.h> | |
| #include <sys/epoll.h> | |
| #include <math.h> | |
| #include <time.h> | |
| #ifndef __aarch64__ | |
| #include <time64.h> | |
| #endif | |
| #include <signal.h> | |
| #include <unistd.h> | |
| #include <ctype.h> | |
| #define LOG_TAG "gps_serial" | |
| #include <cutils/log.h> | |
| #include <cutils/sockets.h> | |
| #include <cutils/properties.h> | |
| #include <hardware/gps.h> | |
| #include "gpslib.h" | |
| static GpsState _gps_state[1]; | |
| static int id_in_gp_fixed[12]; | |
| static int id_in_gl_fixed[12]; | |
| #define GPS_DEBUG 1 | |
| #define DFR(...) ALOGD(__VA_ARGS__) | |
| #if GPS_DEBUG | |
| #define D(...) ALOGD(__VA_ARGS__) | |
| static int xz = 0; | |
| #else | |
| #define D(...) ((void)0) | |
| #endif | |
| #define GPS_DEV_SLOW_UPDATE_RATE (10) | |
| #define GPS_DEV_HIGH_UPDATE_RATE (1) | |
| static void gps_dev_init(int fd); | |
| static void gps_dev_deinit(int fd); | |
| static void gps_dev_start(int fd); | |
| static void gps_dev_stop(int fd); | |
| /*****************************************************************/ | |
| /*****************************************************************/ | |
| /***** *****/ | |
| /***** N M E A T O K E N I Z E R *****/ | |
| /***** *****/ | |
| /*****************************************************************/ | |
| /*****************************************************************/ | |
| #define MAX_NMEA_TOKENS 32 | |
| typedef struct { | |
| const char* p; | |
| const char* end; | |
| } Token; | |
| typedef struct { | |
| int count; | |
| Token tokens[ MAX_NMEA_TOKENS ]; | |
| } NmeaTokenizer; | |
| static int | |
| nmea_tokenizer_init( NmeaTokenizer* t, const char* p, const char* end ) | |
| { | |
| int count = 0; | |
| char* q; | |
| // the initial '$' is optional | |
| if (p < end && p[0] == '$') | |
| p += 1; | |
| // remove trailing newline | |
| if (end > p && end[-1] == '\n') { | |
| end -= 1; | |
| if (end > p && end[-1] == '\r') | |
| end -= 1; | |
| } | |
| // get rid of checksum at the end of the sentence | |
| if (end >= p+3 && end[-3] == '*') { | |
| end -= 3; | |
| } | |
| while (p < end) { | |
| const char* q = p; | |
| q = memchr(p, ',', end-p); | |
| if (q == NULL) | |
| q = end; | |
| if (count < MAX_NMEA_TOKENS) { | |
| t->tokens[count].p = p; | |
| t->tokens[count].end = q; | |
| count += 1; | |
| } | |
| if (q < end) | |
| q += 1; | |
| p = q; | |
| } | |
| t->count = count; | |
| return count; | |
| } | |
| static Token | |
| nmea_tokenizer_get( NmeaTokenizer* t, int index ) | |
| { | |
| Token tok; | |
| if (index < 0 || index >= t->count || index >= MAX_NMEA_TOKENS) | |
| tok.p = tok.end = ""; | |
| else | |
| tok = t->tokens[index]; | |
| return tok; | |
| } | |
| static int | |
| str2int( const char* p, const char* end ) | |
| { | |
| int result = 0; | |
| int len = end - p; | |
| for ( ; len > 0; len--, p++ ) { | |
| int c; | |
| if (p >= end) | |
| goto Fail; | |
| c = *p - '0'; | |
| if ((unsigned)c >= 10) | |
| goto Fail; | |
| result = result*10 + c; | |
| } | |
| return result; | |
| Fail: | |
| return -1; | |
| } | |
| static double | |
| str2float( const char* p, const char* end ) | |
| { | |
| size_t len = end - p; | |
| char temp[16]; | |
| if (len >= sizeof(temp)) | |
| return 0.; | |
| memcpy( temp, p, len ); | |
| temp[len] = '\0'; | |
| return strtod( temp, NULL ); | |
| } | |
| /*****************************************************************/ | |
| /*****************************************************************/ | |
| /***** *****/ | |
| /***** N M E A P A R S E R *****/ | |
| /***** *****/ | |
| /*****************************************************************/ | |
| /*****************************************************************/ | |
| #define NMEA_MAX_SIZE 255 | |
| typedef struct { | |
| int pos; | |
| int overflow; | |
| int utc_year; | |
| int utc_mon; | |
| int utc_day; | |
| int utc_diff; | |
| GpsLocation fix; | |
| GnssSvStatus sv_gl_status; | |
| GnssSvStatus sv_gp_status; | |
| GnssSvStatus sv_gns_status; | |
| gps_location_callback callback; | |
| char in[ NMEA_MAX_SIZE+1 ]; | |
| int update; | |
| } NmeaReader; | |
| void update_gps_status(GpsStatusValue val) | |
| { | |
| GpsState* state = _gps_state; | |
| //Should be made thread safe... | |
| state->status.status=val; | |
| if (state->callbacks->status_cb) | |
| state->callbacks->status_cb(&state->status); | |
| } | |
| void update_gns_svstatus(GnssSvStatus *val) | |
| { | |
| GpsState* state = _gps_state; | |
| //Should be made thread safe... | |
| if (state->callbacks->gnss_sv_status_cb) | |
| state->callbacks->gnss_sv_status_cb(val); | |
| } | |
| void update_gps_location(GpsLocation *fix) | |
| { | |
| GpsState* state = _gps_state; | |
| //Should be made thread safe... | |
| if (state->callbacks->location_cb) | |
| state->callbacks->location_cb(fix); | |
| } | |
| static void | |
| nmea_reader_update_utc_diff( NmeaReader* r ) | |
| { | |
| time_t now = time(NULL); | |
| struct tm tm_local; | |
| struct tm tm_utc; | |
| long time_local, time_utc; | |
| gmtime_r( &now, &tm_utc ); | |
| localtime_r( &now, &tm_local ); | |
| time_local = tm_local.tm_sec + | |
| 60*(tm_local.tm_min + | |
| 60*(tm_local.tm_hour + | |
| 24*(tm_local.tm_yday + | |
| 365*tm_local.tm_year))); | |
| time_utc = tm_utc.tm_sec + | |
| 60*(tm_utc.tm_min + | |
| 60*(tm_utc.tm_hour + | |
| 24*(tm_utc.tm_yday + | |
| 365*tm_utc.tm_year))); | |
| r->utc_diff = time_utc - time_local; | |
| } | |
| static void | |
| nmea_reader_init( NmeaReader* r ) | |
| { | |
| memset( r, 0, sizeof(*r) ); | |
| r->pos = 0; | |
| r->overflow = 0; | |
| r->utc_year = -1; | |
| r->utc_mon = -1; | |
| r->utc_day = -1; | |
| r->callback = NULL; | |
| r->fix.size = sizeof(r->fix); | |
| r->update = 0; | |
| nmea_reader_update_utc_diff( r ); | |
| } | |
| static void | |
| nmea_reader_set_callback( NmeaReader* r, gps_location_callback cb ) | |
| { | |
| r->callback = cb; | |
| if (cb != NULL && r->fix.flags != 0) { | |
| D("%s: sending latest fix to new callback", __FUNCTION__); | |
| r->callback( &r->fix ); | |
| r->fix.flags = 0; | |
| } | |
| } | |
| static int | |
| nmea_reader_update_time( NmeaReader* r, Token tok ) | |
| { | |
| int hour, minute; | |
| double seconds; | |
| double milliseconds; | |
| struct tm tm; | |
| #ifdef __aarch64__ | |
| time_t fix_time; | |
| #else | |
| time64_t fix_time; | |
| #endif | |
| if (tok.p + 6 > tok.end) | |
| return -1; | |
| if (r->utc_year < 0) { | |
| // no date yet, get current one | |
| time_t now = time(NULL); | |
| gmtime_r(&now, &tm); | |
| r->utc_year = tm.tm_year + 1900; | |
| r->utc_mon = tm.tm_mon + 1; | |
| r->utc_day = tm.tm_mday; | |
| } | |
| hour = str2int(tok.p, tok.p + 2); | |
| minute = str2int(tok.p + 2, tok.p + 4); | |
| seconds = str2float(tok.p + 4, tok.end); | |
| milliseconds = seconds * 1000.0; /* This is seconds + milliseconds */ | |
| /* initialize all values inside the tm-struct to 0, | |
| otherwise mktime(&tm) will return -1 */ | |
| memset(&tm, 0, sizeof(tm)); | |
| tm.tm_hour = hour; | |
| tm.tm_min = minute; | |
| tm.tm_sec = 0; /* Don't set seconds; we will add them later (together with milliseconds) */ | |
| tm.tm_year = r->utc_year - 1900; | |
| tm.tm_mon = r->utc_mon - 1; | |
| tm.tm_mday = r->utc_day; | |
| /* We use the timegm64() here, as there is no timegm() in Bionic. | |
| * The timesteamp field in the fix is 64-bit anyway, so we don't need | |
| * to convert back to 32-bit after this (which would be tricky to get safe). | |
| * timegm64() returns seconds since epoch. | |
| * | |
| * We immediately convert to milliseconds, as that is what is needed | |
| * in location fix struct. | |
| */ | |
| #ifdef __aarch64__ | |
| fix_time = timegm(&tm) * 1000LL; | |
| #else | |
| fix_time = timegm64(&tm) * 1000LL; | |
| #endif | |
| /* Now add the seconds+millliseconds */ | |
| fix_time = fix_time + ((long) milliseconds); | |
| /* Assign calculated value to fix */ | |
| #ifdef __aarch64__ | |
| r->fix.timestamp = fix_time; | |
| #else | |
| r->fix.timestamp = (time64_t) fix_time; | |
| #endif | |
| return 0; | |
| } | |
| static int | |
| nmea_reader_update_date( NmeaReader* r, Token date, Token time ) | |
| { | |
| Token tok = date; | |
| int day, mon, year; | |
| if (tok.p + 6 != tok.end) { | |
| D("Date not properly formatted: '%.*s'", tok.end-tok.p, tok.p); | |
| return -1; | |
| } | |
| day = str2int(tok.p, tok.p+2); | |
| mon = str2int(tok.p+2, tok.p+4); | |
| year = str2int(tok.p+4, tok.p+6) + 2000; | |
| if ((day|mon|year) < 0) { | |
| D("Date not properly formatted: '%.*s'", tok.end-tok.p, tok.p); | |
| return -1; | |
| } | |
| r->utc_year = year; | |
| r->utc_mon = mon; | |
| r->utc_day = day; | |
| return nmea_reader_update_time( r, time ); | |
| } | |
| static double | |
| convert_from_hhmm( Token tok ) | |
| { | |
| double val = str2float(tok.p, tok.end); | |
| int degrees = (int)(floor(val) / 100); | |
| double minutes = val - degrees*100.; | |
| double dcoord = degrees + minutes / 60.0; | |
| return dcoord; | |
| } | |
| static int | |
| nmea_reader_update_latlong( NmeaReader* r, | |
| Token latitude, | |
| char latitudeHemi, | |
| Token longitude, | |
| char longitudeHemi ) | |
| { | |
| double lat, lon; | |
| Token tok; | |
| tok = latitude; | |
| if (tok.p + 6 > tok.end) { | |
| D("Latitude is too short: '%.*s'", tok.end-tok.p, tok.p); | |
| return -1; | |
| } | |
| lat = convert_from_hhmm(tok); | |
| if (latitudeHemi == 'S') | |
| lat = -lat; | |
| tok = longitude; | |
| if (tok.p + 6 > tok.end) { | |
| D("Longitude is too short: '%.*s'", tok.end-tok.p, tok.p); | |
| return -1; | |
| } | |
| lon = convert_from_hhmm(tok); | |
| if (longitudeHemi == 'W') | |
| lon = -lon; | |
| r->fix.flags |= GPS_LOCATION_HAS_LAT_LONG; | |
| r->fix.latitude = lat; | |
| r->fix.longitude = lon; | |
| return 0; | |
| } | |
| static int | |
| nmea_reader_update_altitude( NmeaReader* r, | |
| Token altitude, | |
| Token units ) | |
| { | |
| double alt; | |
| Token tok = altitude; | |
| if (tok.p >= tok.end) | |
| return -1; | |
| r->fix.flags |= GPS_LOCATION_HAS_ALTITUDE; | |
| r->fix.altitude = str2float(tok.p, tok.end); | |
| return 0; | |
| } | |
| static int nmea_reader_update_accuracy( NmeaReader* r, | |
| Token accuracy ) | |
| { | |
| double acc; | |
| Token tok = accuracy; | |
| if (tok.p >= tok.end) | |
| return -1; | |
| r->fix.accuracy = str2float(tok.p, tok.end); | |
| if (r->fix.accuracy == 99.99){ | |
| return 0; | |
| } | |
| r->fix.flags |= GPS_LOCATION_HAS_ACCURACY; | |
| return 0; | |
| } | |
| static int | |
| nmea_reader_update_bearing( NmeaReader* r, | |
| Token bearing ) | |
| { | |
| double alt; | |
| Token tok = bearing; | |
| if (tok.p >= tok.end) | |
| return -1; | |
| r->fix.flags |= GPS_LOCATION_HAS_BEARING; | |
| r->fix.bearing = str2float(tok.p, tok.end); | |
| return 0; | |
| } | |
| static int | |
| nmea_reader_update_speed( NmeaReader* r, | |
| Token speed ) | |
| { | |
| double alt; | |
| Token tok = speed; | |
| if (tok.p >= tok.end) | |
| return -1; | |
| r->fix.flags |= GPS_LOCATION_HAS_SPEED; | |
| r->fix.speed = str2float(tok.p, tok.end) * 1.852 / 3.6; | |
| return 0; | |
| } | |
| static int | |
| nmea_reader_update_svs_gl( NmeaReader* r, int inview, int num, int i, Token prn, Token elevation, Token azimuth, Token snr ) | |
| { | |
| int o; | |
| int prnid; | |
| int sid, svid; | |
| i = (num - 1)*4 + i; | |
| if (i < inview) { | |
| sid = str2int(prn.p,prn.end); | |
| svid = sid - 64; | |
| r->sv_gl_status.gnss_sv_list[i].constellation = GNSS_CONSTELLATION_GLONASS; | |
| r->sv_gl_status.gnss_sv_list[i].svid=svid; | |
| r->sv_gl_status.gnss_sv_list[i].elevation=str2int(elevation.p,elevation.end); | |
| r->sv_gl_status.gnss_sv_list[i].azimuth=str2int(azimuth.p,azimuth.end); | |
| r->sv_gl_status.gnss_sv_list[i].c_n0_dbhz=str2int(snr.p,snr.end); | |
| r->sv_gl_status.gnss_sv_list[i].flags = GNSS_SV_FLAGS_NONE; | |
| for (o=0;o<12;o++){ | |
| if (id_in_gl_fixed[o]==sid){ | |
| r->sv_gl_status.gnss_sv_list[i].flags |= GNSS_SV_FLAGS_USED_IN_FIX; | |
| break; | |
| } | |
| } | |
| } | |
| return 0; | |
| } | |
| static int | |
| nmea_reader_update_svs_gp( NmeaReader* r, int inview, int num, int i, Token prn, Token elevation, Token azimuth, Token snr ) | |
| { | |
| int o; | |
| int prnid; | |
| int sid; | |
| i = (num - 1)*4 + i; | |
| if (i < inview) { | |
| sid = str2int(prn.p,prn.end); | |
| r->sv_gp_status.gnss_sv_list[i].constellation = GNSS_CONSTELLATION_GPS; | |
| r->sv_gp_status.gnss_sv_list[i].svid=sid; | |
| r->sv_gp_status.gnss_sv_list[i].elevation=str2int(elevation.p,elevation.end); | |
| r->sv_gp_status.gnss_sv_list[i].azimuth=str2int(azimuth.p,azimuth.end); | |
| r->sv_gp_status.gnss_sv_list[i].c_n0_dbhz=str2int(snr.p,snr.end); | |
| r->sv_gp_status.gnss_sv_list[i].flags = GNSS_SV_FLAGS_NONE; | |
| for ( o = 0; o < 12; o++) { | |
| if (id_in_gp_fixed[o]==sid){ | |
| r->sv_gp_status.gnss_sv_list[i].flags |= GNSS_SV_FLAGS_USED_IN_FIX; | |
| break; | |
| } | |
| } | |
| } | |
| return 0; | |
| } | |
| static void | |
| nmea_reader_parse( NmeaReader* r ) | |
| { | |
| /* we received a complete sentence, now parse it to generate | |
| * a new GPS fix... | |
| */ | |
| NmeaTokenizer tzer[1]; | |
| Token tok; | |
| struct timeval tv; | |
| D("Received: '%.*s'", r->pos, r->in); | |
| if (r->pos < 9) { | |
| D("Too short. discarded."); | |
| return; | |
| } | |
| gettimeofday(&tv, NULL); | |
| if (_gps_state->init) | |
| _gps_state->callbacks->nmea_cb(tv.tv_sec*1000+tv.tv_usec/1000, r->in, r->pos); | |
| nmea_tokenizer_init(tzer, r->in, r->in + r->pos); | |
| #if GPS_DEBUG | |
| { | |
| int n; | |
| D("Found %d tokens", tzer->count); | |
| for (n = 0; n < tzer->count; n++) { | |
| Token tok = nmea_tokenizer_get(tzer,n); | |
| D("%2d: '%.*s'", n, tok.end-tok.p, tok.p); | |
| } | |
| } | |
| #endif | |
| tok = nmea_tokenizer_get(tzer, 0); | |
| if (tok.p + 5 > tok.end) { | |
| D("Sentence id '%.*s' too short, ignored.", tok.end-tok.p, tok.p); | |
| return; | |
| } | |
| // ignore first two characters. | |
| tok.p += 2; | |
| if ( !memcmp(tok.p, "GGA", 3) ) { | |
| // GPS fix | |
| Token tok_time = nmea_tokenizer_get(tzer,1); | |
| Token tok_latitude = nmea_tokenizer_get(tzer,2); | |
| Token tok_latitudeHemi = nmea_tokenizer_get(tzer,3); | |
| Token tok_longitude = nmea_tokenizer_get(tzer,4); | |
| Token tok_longitudeHemi = nmea_tokenizer_get(tzer,5); | |
| Token tok_accuracy = nmea_tokenizer_get(tzer,8); | |
| Token tok_altitude = nmea_tokenizer_get(tzer,9); | |
| Token tok_altitudeUnits = nmea_tokenizer_get(tzer,10); | |
| nmea_reader_update_time(r, tok_time); | |
| nmea_reader_update_latlong(r, tok_latitude, | |
| tok_latitudeHemi.p[0], | |
| tok_longitude, | |
| tok_longitudeHemi.p[0]); | |
| nmea_reader_update_altitude(r, tok_altitude, tok_altitudeUnits); | |
| nmea_reader_update_accuracy(r, tok_accuracy); | |
| } else if ( !memcmp(tok.p, "GSA", 3) ) { | |
| /* | |
| 1 = Mode: | |
| M=Manual, forced to operate in 2D or 3D | |
| A=Automatic, 3D/2D | |
| 2 = Mode: | |
| 1=Fix not available | |
| 2=2D | |
| 3=3D | |
| 3-14 = IDs of SVs used in position fix (null for unused fields) | |
| 15 = PDOP | |
| 16 = HDOP | |
| 17 = VDOP | |
| */ | |
| Token tok_mode = nmea_tokenizer_get(tzer,1); | |
| Token tok_fix = nmea_tokenizer_get(tzer,2); | |
| Token tok_id = nmea_tokenizer_get(tzer,3); | |
| Token tok_pdop = nmea_tokenizer_get(tzer,15); | |
| Token tok_hdop = nmea_tokenizer_get(tzer,16); | |
| Token tok_vdop = nmea_tokenizer_get(tzer,17); | |
| nmea_reader_update_accuracy(r, tok_hdop); | |
| int sid; | |
| sid = str2int(tok_id.p,tok_id.end); | |
| int i; | |
| for ( i=0; i<12; i++ ) { | |
| Token tok_id = nmea_tokenizer_get(tzer,3+i); | |
| if ( tok_id.end > tok_id.p ){ | |
| if (sid > 64) { | |
| id_in_gl_fixed[i] = str2int(tok_id.p,tok_id.end); | |
| } else { | |
| id_in_gp_fixed[i] = str2int(tok_id.p,tok_id.end); | |
| } | |
| D("Satellite used '%.*s'", tok_id.end-tok_id.p, tok_id.p); | |
| } | |
| } | |
| } else if ( !memcmp(tok.p, "GSV", 3) ) { | |
| /* | |
| 1 = Total number of messages of this type in this cycle | |
| 2 = Message number | |
| 3 = Total number of SVs in view | |
| 4 = SV PRN number | |
| 5 = Elevation in degrees, 90 maximum | |
| 6 = Azimuth, degrees from true north, 000 to 359 | |
| 7 = SNR, 00-99 dB (null when not tracking) | |
| 8-11 = Information about second SV, same as field 4-7 | |
| 12-15= Information about third SV, same as field 4-7 | |
| 16-19= Information about fourth SV, same as field 4-7 | |
| */ | |
| //Satellites are handled by RPC-side code. | |
| Token tok_num_messages = nmea_tokenizer_get(tzer,1); | |
| Token tok_msg_number = nmea_tokenizer_get(tzer,2); | |
| Token tok_svs_inview = nmea_tokenizer_get(tzer,3); | |
| Token tok_sv1_prn_num = nmea_tokenizer_get(tzer,4); | |
| Token tok_sv1_elevation = nmea_tokenizer_get(tzer,5); | |
| Token tok_sv1_azimuth = nmea_tokenizer_get(tzer,6); | |
| Token tok_sv1_snr = nmea_tokenizer_get(tzer,7); | |
| Token tok_sv2_prn_num = nmea_tokenizer_get(tzer,8); | |
| Token tok_sv2_elevation = nmea_tokenizer_get(tzer,9); | |
| Token tok_sv2_azimuth = nmea_tokenizer_get(tzer,10); | |
| Token tok_sv2_snr = nmea_tokenizer_get(tzer,11); | |
| Token tok_sv3_prn_num = nmea_tokenizer_get(tzer,12); | |
| Token tok_sv3_elevation = nmea_tokenizer_get(tzer,13); | |
| Token tok_sv3_azimuth = nmea_tokenizer_get(tzer,14); | |
| Token tok_sv3_snr = nmea_tokenizer_get(tzer,15); | |
| Token tok_sv4_prn_num = nmea_tokenizer_get(tzer,16); | |
| Token tok_sv4_elevation = nmea_tokenizer_get(tzer,17); | |
| Token tok_sv4_azimuth = nmea_tokenizer_get(tzer,18); | |
| Token tok_sv4_snr = nmea_tokenizer_get(tzer,19); | |
| int num_messages = str2int(tok_num_messages.p,tok_num_messages.end); | |
| int msg_number = str2int(tok_msg_number.p,tok_msg_number.end); | |
| int svs_inview = str2int(tok_svs_inview.p,tok_svs_inview.end); | |
| D("GSV %d %d %d", num_messages, msg_number, svs_inview ); | |
| int sid = str2int(tok_sv1_prn_num.p,tok_sv1_prn_num.end); | |
| if (sid > 64) { | |
| nmea_reader_update_svs_gl( r, svs_inview, msg_number, 0, tok_sv1_prn_num, tok_sv1_elevation, tok_sv1_azimuth, tok_sv1_snr ); | |
| nmea_reader_update_svs_gl( r, svs_inview, msg_number, 1, tok_sv2_prn_num, tok_sv2_elevation, tok_sv2_azimuth, tok_sv2_snr ); | |
| nmea_reader_update_svs_gl( r, svs_inview, msg_number, 2, tok_sv3_prn_num, tok_sv3_elevation, tok_sv3_azimuth, tok_sv3_snr ); | |
| nmea_reader_update_svs_gl( r, svs_inview, msg_number, 3, tok_sv4_prn_num, tok_sv4_elevation, tok_sv4_azimuth, tok_sv4_snr ); | |
| r->sv_gl_status.num_svs=svs_inview; | |
| r->sv_gl_status.size = sizeof(r->sv_gl_status); | |
| } else { | |
| nmea_reader_update_svs_gp( r, svs_inview, msg_number, 0, tok_sv1_prn_num, tok_sv1_elevation, tok_sv1_azimuth, tok_sv1_snr ); | |
| nmea_reader_update_svs_gp( r, svs_inview, msg_number, 1, tok_sv2_prn_num, tok_sv2_elevation, tok_sv2_azimuth, tok_sv2_snr ); | |
| nmea_reader_update_svs_gp( r, svs_inview, msg_number, 2, tok_sv3_prn_num, tok_sv3_elevation, tok_sv3_azimuth, tok_sv3_snr ); | |
| nmea_reader_update_svs_gp( r, svs_inview, msg_number, 3, tok_sv4_prn_num, tok_sv4_elevation, tok_sv4_azimuth, tok_sv4_snr ); | |
| r->sv_gp_status.num_svs=svs_inview; | |
| r->sv_gp_status.size = sizeof(r->sv_gp_status); | |
| } | |
| if (num_messages==msg_number){ | |
| int k = 0; | |
| #if GPS_DEBUG | |
| xz++; | |
| #endif | |
| for (int i=0; i < GNSS_MAX_SVS; i++){ | |
| if (r->sv_gp_status.gnss_sv_list[i].svid > 0){ | |
| r->sv_gns_status.gnss_sv_list[k] = r->sv_gp_status.gnss_sv_list[i]; | |
| k++; | |
| } | |
| } | |
| for (int i=0; i < GNSS_MAX_SVS; i++){ | |
| if (r->sv_gl_status.gnss_sv_list[i].svid > 0){ | |
| r->sv_gns_status.gnss_sv_list[k] = r->sv_gl_status.gnss_sv_list[i]; | |
| k++; | |
| } | |
| } | |
| #if GPS_DEBUG | |
| if (xz == 30) { | |
| for (int i=0; i < GNSS_MAX_SVS; i++) { | |
| if (r->sv_gns_status.gnss_sv_list[i].svid > 0) { | |
| ALOGI("%d svid %d, constellation %d, flags %d",i, r->sv_gns_status.gnss_sv_list[i].svid, r->sv_gns_status.gnss_sv_list[i].constellation, r->sv_gns_status.gnss_sv_list[i].flags); | |
| } | |
| } | |
| ALOGI("total %d",k); | |
| xz = 0; | |
| } | |
| #endif | |
| r->sv_gns_status.num_svs=k; | |
| r->sv_gns_status.size = sizeof(r->sv_gns_status); | |
| update_gns_svstatus(&r->sv_gns_status); | |
| } | |
| } else if (!memcmp(tok.p, "GLL", 3)) { | |
| Token tok_fixStatus = nmea_tokenizer_get(tzer, 6); | |
| if (tok_fixStatus.p[0] == 'A') { | |
| Token tok_time = nmea_tokenizer_get(tzer, 5); | |
| Token tok_fixStatus = nmea_tokenizer_get(tzer, 6); | |
| Token tok_latitude = nmea_tokenizer_get(tzer, 1); | |
| Token tok_latitudeHemi = nmea_tokenizer_get(tzer, 2); | |
| Token tok_longitude = nmea_tokenizer_get(tzer, 3); | |
| Token tok_longitudeHemi = nmea_tokenizer_get(tzer, 4); | |
| D("in GGL, fixStatus=%c", tok_fixStatus.p[0]); | |
| if (tok_fixStatus.p[0] == 'A') { | |
| nmea_reader_update_latlong(r, tok_latitude, | |
| tok_latitudeHemi.p[0], | |
| tok_longitude, | |
| tok_longitudeHemi.p[0]); | |
| r->update = 1; | |
| } | |
| } | |
| } else if ( !memcmp(tok.p, "RMC", 3) ) { | |
| Token tok_time = nmea_tokenizer_get(tzer,1); | |
| Token tok_fixStatus = nmea_tokenizer_get(tzer,2); | |
| Token tok_latitude = nmea_tokenizer_get(tzer,3); | |
| Token tok_latitudeHemi = nmea_tokenizer_get(tzer,4); | |
| Token tok_longitude = nmea_tokenizer_get(tzer,5); | |
| Token tok_longitudeHemi = nmea_tokenizer_get(tzer,6); | |
| Token tok_speed = nmea_tokenizer_get(tzer,7); | |
| Token tok_bearing = nmea_tokenizer_get(tzer,8); | |
| Token tok_date = nmea_tokenizer_get(tzer,9); | |
| D("in RMC, fixStatus=%c", tok_fixStatus.p[0]); | |
| if (tok_fixStatus.p[0] == 'A') { | |
| nmea_reader_update_date( r, tok_date, tok_time ); | |
| nmea_reader_update_latlong( r, tok_latitude, | |
| tok_latitudeHemi.p[0], | |
| tok_longitude, | |
| tok_longitudeHemi.p[0] ); | |
| nmea_reader_update_bearing( r, tok_bearing ); | |
| nmea_reader_update_speed ( r, tok_speed ); | |
| r->update = 1; | |
| } | |
| } else if ( !memcmp(tok.p, "VTG", 3) ) { | |
| Token tok_fixStatus = nmea_tokenizer_get(tzer,9); | |
| if (tok_fixStatus.p[0] != '\0' && tok_fixStatus.p[0] != 'N') { | |
| Token tok_bearing = nmea_tokenizer_get(tzer,1); | |
| Token tok_speed = nmea_tokenizer_get(tzer,5); | |
| nmea_reader_update_bearing( r, tok_bearing ); | |
| nmea_reader_update_speed ( r, tok_speed ); | |
| } | |
| } else { | |
| tok.p -= 2; | |
| D("Unknown sentence '%.*s", tok.end-tok.p, tok.p); | |
| } | |
| #if GPS_DEBUG | |
| if (r->fix.flags) { | |
| char temp[256]; | |
| char* p = temp; | |
| char* end = p + sizeof(temp); | |
| struct tm utc; | |
| p += snprintf( p, end-p, "Sending fix" ); | |
| if (r->fix.flags & GPS_LOCATION_HAS_LAT_LONG) { | |
| p += snprintf(p, end-p, " lat=%g lon=%g", r->fix.latitude, r->fix.longitude); | |
| } | |
| if (r->fix.flags & GPS_LOCATION_HAS_ALTITUDE) { | |
| p += snprintf(p, end-p, " altitude=%g", r->fix.altitude); | |
| } | |
| if (r->fix.flags & GPS_LOCATION_HAS_SPEED) { | |
| p += snprintf(p, end-p, " speed=%g", r->fix.speed); | |
| } | |
| if (r->fix.flags & GPS_LOCATION_HAS_BEARING) { | |
| p += snprintf(p, end-p, " bearing=%g", r->fix.bearing); | |
| } | |
| if (r->fix.flags & GPS_LOCATION_HAS_ACCURACY) { | |
| p += snprintf(p,end-p, " accuracy=%g", r->fix.accuracy); | |
| } | |
| gmtime_r( (time_t*) &r->fix.timestamp, &utc ); | |
| p += snprintf(p, end-p, " time=%s", asctime( &utc ) ); | |
| D("%s\n", temp); | |
| } | |
| #endif | |
| if (r->fix.flags & GPS_LOCATION_HAS_ACCURACY && r->update) { | |
| if (_gps_state->callbacks->location_cb) { | |
| _gps_state->callbacks->location_cb( &r->fix ); | |
| r->fix.flags = 0; | |
| r->update = 0; | |
| } else { | |
| D("No callback, keeping data until needed !"); | |
| } | |
| } | |
| } | |
| static void | |
| nmea_reader_addc( NmeaReader* r, int c ) | |
| { | |
| if (r->overflow) { | |
| r->overflow = (c != '\n'); | |
| return; | |
| } | |
| if (r->pos >= (int) sizeof(r->in)-1 ) { | |
| r->overflow = 1; | |
| r->pos = 0; | |
| return; | |
| } | |
| r->in[r->pos] = (char)c; | |
| r->pos += 1; | |
| if (c == '\n') { | |
| nmea_reader_parse( r ); | |
| r->pos = 0; | |
| } | |
| } | |
| /*****************************************************************/ | |
| /*****************************************************************/ | |
| /***** *****/ | |
| /***** C O N N E C T I O N S T A T E *****/ | |
| /***** *****/ | |
| /*****************************************************************/ | |
| /*****************************************************************/ | |
| /* commands sent to the gps thread */ | |
| enum { | |
| CMD_QUIT = 0, | |
| CMD_START = 1, | |
| CMD_STOP = 2 | |
| }; | |
| static void | |
| gps_state_done( GpsState* s ) | |
| { | |
| // tell the thread to quit, and wait for it | |
| char cmd = CMD_QUIT; | |
| void* dummy; | |
| write( s->control[0], &cmd, 1 ); | |
| pthread_join(s->thread, &dummy); | |
| // close the control socket pair | |
| close( s->control[0] ); s->control[0] = -1; | |
| close( s->control[1] ); s->control[1] = -1; | |
| // close connection to the QEMU GPS daemon | |
| close( s->fd ); s->fd = -1; | |
| s->init = 0; | |
| } | |
| static void | |
| gps_state_start( GpsState* s ) | |
| { | |
| char cmd = CMD_START; | |
| int ret; | |
| do { | |
| ret = write( s->control[0], &cmd, 1 ); | |
| } while (ret < 0 && errno == EINTR); | |
| if (ret != 1) { | |
| D("%s: could not send CMD_START command: ret=%d: %s", | |
| __FUNCTION__, ret, strerror(errno)); | |
| gps_state_init(s, s->callbacks); | |
| } | |
| } | |
| static void | |
| gps_state_stop( GpsState* s ) | |
| { | |
| char cmd = CMD_STOP; | |
| int ret; | |
| do { ret=write( s->control[0], &cmd, 1 ); } | |
| while (ret < 0 && errno == EINTR); | |
| if (ret != 1) | |
| D("%s: could not send CMD_STOP command: ret=%d: %s", | |
| __FUNCTION__, ret, strerror(errno)); | |
| } | |
| static int | |
| epoll_register( int epoll_fd, int fd ) | |
| { | |
| struct epoll_event ev; | |
| int ret, flags; | |
| /* important: make the fd non-blocking */ | |
| flags = fcntl(fd, F_GETFL); | |
| fcntl(fd, F_SETFL, flags | O_NONBLOCK); | |
| ev.events = EPOLLIN; | |
| ev.data.fd = fd; | |
| do { | |
| ret = epoll_ctl( epoll_fd, EPOLL_CTL_ADD, fd, &ev ); | |
| } while (ret < 0 && errno == EINTR); | |
| return ret; | |
| } | |
| static int | |
| epoll_deregister( int epoll_fd, int fd ) | |
| { | |
| int ret; | |
| do { | |
| ret = epoll_ctl( epoll_fd, EPOLL_CTL_DEL, fd, NULL ); | |
| } while (ret < 0 && errno == EINTR); | |
| return ret; | |
| } | |
| /* this is the main thread, it waits for commands from gps_state_start/stop and, | |
| * when started, messages from the QEMU GPS daemon. these are simple NMEA sentences | |
| * that must be parsed to be converted into GPS fixes sent to the framework | |
| */ | |
| static void | |
| gps_state_thread( void* arg ) | |
| { | |
| GpsState* state = (GpsState*) arg; | |
| NmeaReader reader[1]; | |
| int epoll_fd = epoll_create(2); | |
| int started = 0; | |
| int gps_fd = state->fd; | |
| int control_fd = state->control[1]; | |
| nmea_reader_init( reader ); | |
| // register control file descriptors for polling | |
| epoll_register( epoll_fd, control_fd ); | |
| epoll_register( epoll_fd, gps_fd ); | |
| D("GPS thread running"); | |
| // now loop | |
| for (;;) { | |
| struct epoll_event events[2]; | |
| int ne, nevents; | |
| nevents = epoll_wait( epoll_fd, events, 2, -1 ); | |
| if (nevents < 0) { | |
| if (errno != EINTR) | |
| ALOGE("epoll_wait() unexpected error: %s", strerror(errno)); | |
| continue; | |
| } | |
| for (ne = 0; ne < nevents; ne++) { | |
| if ((events[ne].events & (EPOLLERR|EPOLLHUP)) != 0) { | |
| ALOGE("EPOLLERR or EPOLLHUP after epoll_wait() !?"); | |
| close(gps_fd); | |
| usleep(5000*1000); | |
| gps_state_init(state, state->callbacks); | |
| return; | |
| } | |
| if ((events[ne].events & EPOLLIN) != 0) { | |
| int fd = events[ne].data.fd; | |
| if (fd == control_fd) { | |
| char cmd = 255; | |
| int ret; | |
| D("GPS control fd event"); | |
| do { | |
| ret = read( fd, &cmd, 1 ); | |
| } while (ret < 0 && errno == EINTR); | |
| if (cmd == CMD_QUIT) { | |
| D("GPS thread quitting on demand"); | |
| return; | |
| } else if (cmd == CMD_START) { | |
| if (!started) { | |
| D("GPS thread starting location_cb=%p", state->callbacks->location_cb); | |
| started = 1; | |
| update_gps_status(GPS_STATUS_SESSION_BEGIN); | |
| } | |
| } else if (cmd == CMD_STOP) { | |
| if (started) { | |
| D("GPS thread stopping"); | |
| started = 0; | |
| update_gps_status(GPS_STATUS_SESSION_END); | |
| } | |
| } | |
| } else if (fd == gps_fd) { | |
| char buff[32]; | |
| for (;;) { | |
| int nn, ret; | |
| ret = read( fd, buff, sizeof(buff) ); | |
| if (ret < 0) { | |
| if (errno == EINTR) | |
| continue; | |
| if (errno != EWOULDBLOCK) | |
| ALOGE("Error while reading from GPS daemon socket: %s:", strerror(errno)); | |
| break; | |
| } | |
| for (nn = 0; nn < ret; nn++) | |
| nmea_reader_addc( reader, buff[nn] ); | |
| } | |
| } else { | |
| ALOGE("epoll_wait() returned unkown fd %d ?", fd); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| void gps_state_init( GpsState* state, GpsCallbacks* callbacks ) | |
| { | |
| char prop[PROPERTY_VALUE_MAX]; | |
| char baud[PROPERTY_VALUE_MAX]; | |
| char device[256]; | |
| int ret; | |
| int pos = 0; | |
| int len = 0; | |
| int cnt; | |
| int cmp; | |
| struct sigevent tmr_event; | |
| state->init = 1; | |
| state->control[0] = -1; | |
| state->control[1] = -1; | |
| state->fd = -1; | |
| state->callbacks = callbacks; | |
| D("gps_state_init"); | |
| int vid, pid = 0; | |
| property_get("ro.gps.interface", prop, "uart"); | |
| if(strncmp(prop, "uart", 4)==0) { | |
| ALOGI("uart gps mode !!!"); | |
| property_get("ro.gps.port", prop, "ttyUSB0"); | |
| char path[64] = "/dev/"; | |
| strcat(path, prop); | |
| ALOGI("open uart gps : %s ", path); | |
| state->fd = open(path, O_RDONLY); | |
| if (state->fd > 0) { | |
| ALOGI("found gps!!!"); | |
| goto found; | |
| } | |
| ALOGE("could not open gps serial device %s: %s", device, strerror(errno) ); | |
| return; | |
| } | |
| property_get("ro.gps.id.vendor", prop, "046D"); | |
| vid = strtol(prop, NULL, 16); | |
| memset(prop, '\0', PROPERTY_VALUE_MAX); | |
| property_get("ro.gps.id.product", prop, "08E4"); | |
| pid = strtol(prop, NULL, 16); | |
| memset(prop, '\0', PROPERTY_VALUE_MAX); | |
| property_get("ro.kernel.android.gps", prop, "ttyUSB0"); | |
| ALOGI("ro.kernel.android.gps = %s", prop); | |
| int i = 0; | |
| char buf[64]; | |
| char path[64] = "/dev/"; | |
| FILE *fp = NULL; | |
| strcat(path, prop); | |
| strcat(path, "%d"); | |
| do { | |
| sprintf(buf, path, i); | |
| ALOGI("buf = %s", buf); | |
| state->fd = open(buf, O_RDONLY); | |
| if (state->fd > 0) { | |
| sprintf(buf, "/sys/class/tty/%s%d/device/uevent", prop, i); | |
| ALOGI("USB GPS node path = %s", buf); | |
| fp = fopen(buf, "r"); | |
| if (fp != NULL) { | |
| char line[128]; | |
| while(fgets(line, sizeof(line), fp) != NULL) { | |
| if (strncmp(line, "PRODUCT=", 8) == 0) { | |
| char *ptr = strchr(line, '=') + 1; | |
| char *tr = strtok(ptr, "/"); | |
| int j = 0; | |
| int value[3]; | |
| while (tr != NULL) { | |
| value[j] = strtol(tr, NULL, 16); | |
| tr = strtok(NULL, "/"); | |
| j++; | |
| } | |
| if (value[0] == vid) { | |
| fclose(fp); | |
| ALOGI("found !!!"); | |
| goto found; | |
| } | |
| } | |
| } | |
| } | |
| fclose(fp); | |
| } | |
| i++; | |
| close(state->fd); | |
| state->fd = -1; | |
| usleep(50*1000); | |
| } while(i < 10); | |
| found: | |
| if (state->fd < 0) { | |
| ALOGE("could not open gps serial device %s: %s", device, strerror(errno) ); | |
| return; | |
| } | |
| D("GPS will read from %s", device); | |
| // Disable echo on serial lines | |
| if ( isatty( state->fd ) ) { | |
| struct termios ios; | |
| tcgetattr( state->fd, &ios ); | |
| ios.c_lflag = 0; /* disable ECHO, ICANON, etc... */ | |
| ios.c_oflag &= (~ONLCR); /* Stop \n -> \r\n translation on output */ | |
| ios.c_iflag &= (~(ICRNL | INLCR)); /* Stop \r -> \n & \n -> \r translation on input */ | |
| ios.c_iflag |= (IGNCR | IXOFF); /* Ignore \r & XON/XOFF on input */ | |
| // Set baud rate and other flags | |
| property_get("ro.kernel.android.gps.speed",baud,"4800"); | |
| if (strcmp(baud, "4800") == 0) { | |
| ALOGE("Setting gps baud rate to 4800"); | |
| ios.c_cflag = B4800 | CRTSCTS | CS8 | CLOCAL | CREAD; | |
| } else if (strcmp(baud, "9600") == 0) { | |
| ALOGE("Setting gps baud rate to 9600"); | |
| ios.c_cflag = B9600 | CRTSCTS | CS8 | CLOCAL | CREAD; | |
| } else if (strcmp(baud, "19200") == 0) { | |
| ALOGE("Setting gps baud rate to 19200"); | |
| ios.c_cflag = B19200 | CRTSCTS | CS8 | CLOCAL | CREAD; | |
| } else if (strcmp(baud, "38400") == 0) { | |
| ALOGE("Setting gps baud rate to 38400"); | |
| ios.c_cflag = B38400 | CRTSCTS | CS8 | CLOCAL | CREAD; | |
| } else if (strcmp(baud, "57600") == 0) { | |
| ALOGE("Setting gps baud rate to 57600"); | |
| ios.c_cflag = B57600 | CRTSCTS | CS8 | CLOCAL | CREAD; | |
| } else if (strcmp(baud, "115200") == 0) { | |
| ALOGE("Setting gps baud rate to 115200"); | |
| ios.c_cflag = B115200 | CRTSCTS | CS8 | CLOCAL | CREAD; | |
| } else { | |
| ALOGE("GPS baud rate unknown: '%s'", baud); | |
| return; | |
| } | |
| tcsetattr( state->fd, TCSANOW, &ios ); | |
| } | |
| if ( socketpair( AF_LOCAL, SOCK_STREAM, 0, state->control ) < 0 ) { | |
| ALOGE("Could not create thread control socket pair: %s", strerror(errno)); | |
| goto Fail; | |
| } | |
| state->thread = callbacks->create_thread_cb( "gps_state_thread", gps_state_thread, state ); | |
| if ( !state->thread ) { | |
| ALOGE("Could not create GPS thread: %s", strerror(errno)); | |
| goto Fail; | |
| } | |
| D("GPS state initialized"); | |
| return; | |
| Fail: | |
| gps_state_done( state ); | |
| } | |
| /*****************************************************************/ | |
| /*****************************************************************/ | |
| /***** *****/ | |
| /***** I N T E R F A C E *****/ | |
| /***** *****/ | |
| /*****************************************************************/ | |
| /*****************************************************************/ | |
| static int | |
| serial_gps_init(GpsCallbacks* callbacks) | |
| { | |
| D("serial_gps_init"); | |
| GpsState* s = _gps_state; | |
| // Always retry | |
| //if (!s->init) | |
| gps_state_init(s, callbacks); | |
| if (s->fd < 0) | |
| return -1; | |
| return 0; | |
| } | |
| static void | |
| serial_gps_cleanup(void) | |
| { | |
| GpsState* s = _gps_state; | |
| if (s->init) | |
| gps_state_done(s); | |
| } | |
| static int | |
| serial_gps_start() | |
| { | |
| GpsState* s = _gps_state; | |
| if (!s->init) { | |
| DFR("%s: called with uninitialized state !!", __FUNCTION__); | |
| return -1; | |
| } | |
| D("%s: called", __FUNCTION__); | |
| gps_state_start(s); | |
| return 0; | |
| } | |
| static int | |
| serial_gps_stop() | |
| { | |
| GpsState* s = _gps_state; | |
| if (!s->init) { | |
| DFR("%s: called with uninitialized state !!", __FUNCTION__); | |
| return -1; | |
| } | |
| D("%s: called", __FUNCTION__); | |
| gps_state_stop(s); | |
| return 0; | |
| } | |
| static int | |
| serial_gps_inject_time(GpsUtcTime time, int64_t timeReference, int uncertainty) | |
| { | |
| return 0; | |
| } | |
| static int | |
| serial_gps_inject_location(double latitude, double longitude, float accuracy) | |
| { | |
| return 0; | |
| } | |
| static void | |
| serial_gps_delete_aiding_data(GpsAidingData flags) | |
| { | |
| } | |
| static int serial_gps_set_position_mode(GpsPositionMode mode, GpsPositionRecurrence recurrence, | |
| uint32_t min_interval, uint32_t preferred_accuracy, uint32_t preferred_time) | |
| { | |
| GpsState* s = _gps_state; | |
| if (!s->init) { | |
| D("%s: called with uninitialized state !!", __FUNCTION__); | |
| return -1; | |
| } | |
| D("set_position_mode: mode=%d recurrence=%d min_interval=%d preferred_accuracy=%d preferred_time=%d", | |
| mode, recurrence, min_interval, preferred_accuracy, preferred_time); | |
| return 0; | |
| } | |
| static const void* | |
| serial_gps_get_extension(const char* name) | |
| { | |
| return NULL; | |
| } | |
| static const GpsInterface serialGpsInterface = { | |
| sizeof(GpsInterface), | |
| serial_gps_init, | |
| serial_gps_start, | |
| serial_gps_stop, | |
| serial_gps_cleanup, | |
| serial_gps_inject_time, | |
| serial_gps_inject_location, | |
| serial_gps_delete_aiding_data, | |
| serial_gps_set_position_mode, | |
| serial_gps_get_extension, | |
| }; | |
| const GpsInterface* gps_get_hardware_interface() | |
| { | |
| D("GPS dev get_hardware_interface"); | |
| return &serialGpsInterface; | |
| } | |
| /*****************************************************************/ | |
| /*****************************************************************/ | |
| /***** *****/ | |
| /***** D E V I C E *****/ | |
| /***** *****/ | |
| /*****************************************************************/ | |
| /*****************************************************************/ | |
| static void gps_dev_power(int state) | |
| { | |
| return; | |
| } | |
| static void gps_dev_send(int fd, char *msg) | |
| { | |
| int i, n, ret; | |
| i = strlen(msg); | |
| n = 0; | |
| do { | |
| ret = write(fd, msg + n, i - n); | |
| if (ret < 0 && errno == EINTR) { | |
| continue; | |
| } | |
| n += ret; | |
| } while (n < i); | |
| } | |
| static unsigned char gps_dev_calc_nmea_csum(char *msg) | |
| { | |
| unsigned char csum = 0; | |
| int i; | |
| for (i = 1; msg[i] != '*'; ++i) { | |
| csum ^= msg[i]; | |
| } | |
| return csum; | |
| } | |
| static void gps_dev_set_nmea_message_rate(int fd, char *msg, int rate) | |
| { | |
| char buff[50]; | |
| int i; | |
| sprintf(buff, "$PUBX,40,%s,%d,%d,%d,0*", msg, rate, rate, rate); | |
| i = strlen(buff); | |
| sprintf((buff + i), "%02x\r\n", gps_dev_calc_nmea_csum(buff)); | |
| gps_dev_send(fd, buff); | |
| D("GPS sent to device: %s", buff); | |
| } | |
| static void gps_dev_set_baud_rate(int fd, int baud) | |
| { | |
| char buff[50]; | |
| int i, u; | |
| for (u = 0; u < 3; ++u) { | |
| sprintf(buff, "$PUBX,41,%d,0003,0003,%d,0*", u, baud); | |
| i = strlen(buff); | |
| sprintf((buff + i), "%02x\r\n", gps_dev_calc_nmea_csum(buff)); | |
| gps_dev_send(fd, buff); | |
| D("Sent to device: %s", buff); | |
| } | |
| } | |
| static void gps_dev_set_message_rate(int fd, int rate) | |
| { | |
| unsigned int i; | |
| char *msg[] = { | |
| "GGA", "GLL", "VTG", | |
| "GSA", "GSV", "RMC" | |
| }; | |
| for (i = 0; i < sizeof(msg)/sizeof(msg[0]); ++i) { | |
| gps_dev_set_nmea_message_rate(fd, msg[i], rate); | |
| } | |
| return; | |
| } | |
| static void gps_dev_init(int fd) | |
| { | |
| gps_dev_power(1); | |
| return; | |
| } | |
| static void gps_dev_deinit(int fd) | |
| { | |
| gps_dev_power(0); | |
| } | |
| static void gps_dev_start(int fd) | |
| { | |
| // Set full message rate | |
| gps_dev_set_message_rate(fd, GPS_DEV_HIGH_UPDATE_RATE); | |
| D("GPS dev start initiated"); | |
| } | |
| static void gps_dev_stop(int fd) | |
| { | |
| // Set slow message rate | |
| gps_dev_set_message_rate(fd, GPS_DEV_SLOW_UPDATE_RATE); | |
| D("GPS dev stop initiated"); | |
| } | |
| static int open_gps(const struct hw_module_t* module, char const* name, struct hw_device_t** device) | |
| { | |
| D("GPS dev open_gps"); | |
| struct gps_device_t *dev = malloc(sizeof(struct gps_device_t)); | |
| memset(dev, 0, sizeof(*dev)); | |
| dev->common.tag = HARDWARE_DEVICE_TAG; | |
| dev->common.version = 0; | |
| dev->common.module = (struct hw_module_t*)module; | |
| dev->get_gps_interface = gps_get_hardware_interface; | |
| *device = &dev->common; | |
| return 0; | |
| } | |
| static struct hw_module_methods_t gps_module_methods = { | |
| .open = open_gps | |
| }; | |
| struct hw_module_t HAL_MODULE_INFO_SYM = { | |
| .tag = HARDWARE_MODULE_TAG, | |
| .version_major = 1, | |
| .version_minor = 0, | |
| .id = GPS_HARDWARE_MODULE_ID, | |
| .name = "Serial GPS Module", | |
| .author = "VooDiK", | |
| .methods = &gps_module_methods, | |
| }; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment