| /* Copyright (C) 2007-2015 The Android Open Source Project |
| ** |
| ** This software is licensed under the terms of the GNU General Public |
| ** License version 2, as published by the Free Software Foundation, and |
| ** may be copied, distributed, and modified under those terms. |
| ** |
| ** 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. |
| */ |
| #include "android/gps.h" |
| #include "android/utils/debug.h" |
| #include "android/utils/stralloc.h" |
| |
| #include <string.h> |
| #include <time.h> |
| #include <stdio.h> |
| |
| CSerialLine* android_gps_serial_line; |
| // Set to true to ping guest for location updates every few seconds |
| static bool s_enable_passive_location_update = true; |
| |
| #define D(...) VERBOSE_PRINT(gps,__VA_ARGS__) |
| |
| void |
| android_gps_send_nmea( const char* sentence ) |
| { |
| if (sentence == NULL) |
| return; |
| |
| D("sending '%s'", sentence); |
| |
| if (android_gps_serial_line == NULL) { |
| D("missing GPS channel, ignored"); |
| return; |
| } |
| |
| android_serialline_write( android_gps_serial_line, (const void*)sentence, strlen(sentence) ); |
| android_serialline_write( android_gps_serial_line, (const void*)"\n", 1 ); |
| } |
| |
| void |
| android_gps_send_gnss( const char* sentence ) |
| { |
| if (sentence == NULL) |
| return; |
| |
| if (android_gps_serial_line == NULL) { |
| D("missing GPS channel, ignored"); |
| return; |
| } |
| |
| D("sending '%s'", sentence); |
| |
| char temp[1024]; |
| snprintf(temp, sizeof(temp), "$GPGNSSv1,%s", sentence); |
| |
| android_serialline_write( android_gps_serial_line, (const void*)temp, strlen(temp) ); |
| android_serialline_write( android_gps_serial_line, (const void*)"\n", 1 ); |
| } |
| |
| //////////////////////////////////////////////////////////// |
| // |
| // android_gps_send_location |
| // |
| // Send a GPS location to the AVD using an NMEA sentence |
| // |
| // Inputs: latitude: Degrees |
| // longitude: Degrees |
| // metersElevation: Meters above sea level |
| // speedKnots: Speed in knots |
| // headingDegrees: Heading -180..+360, 0=north, 90=east |
| // nSatellites: Number of satellites used |
| // time: UTC, in the format provided |
| // by gettimeofday() |
| |
| void |
| android_gps_send_location(double latitude, double longitude, |
| double metersElevation, |
| double speedKnots, double headingDegrees, |
| int nSatellites, |
| const struct timeval *time) |
| { |
| STRALLOC_DEFINE(msgStr); |
| STRALLOC_DEFINE(elevationStr); |
| char* elevStrPtr; |
| |
| int latDeg, latMin, latFraction; |
| int lngDeg, lngMin, lngFraction; |
| char hemiNS, hemiEW; |
| int hh = 0, mm = 0, ss = 0; |
| |
| // GPGGA format overview: |
| // time of fix 123519 12:35:19 UTC |
| // latitude 4807.038 48 degrees, 07.038 minutes |
| // north/south N or S |
| // longitude 01131.000 11 degrees, 31. minutes |
| // east/west E or W |
| // fix quality 1 standard GPS fix |
| // satellites 1 to 12 number of satellites being tracked |
| // HDOP <dontcare> horizontal dilution |
| // altitude 546. altitude above sea-level |
| // altitude units M to indicate meters |
| // diff <dontcare> height of sea-level above ellipsoid |
| // diff units M to indicate meters (should be <dontcare>) |
| // dgps age <dontcare> time in seconds since last DGPS fix |
| // dgps sid <dontcare> DGPS station id |
| |
| // time->tv_sec is elapsed seconds since epoch, UTC |
| hh = (int) (time->tv_sec / (60 * 60)) % 24; |
| mm = (int) (time->tv_sec / 60 ) % 60; |
| ss = (int) (time->tv_sec ) % 60; |
| |
| stralloc_add_format( msgStr, "$GPGGA,%02d%02d%02d", hh, mm, ss); |
| |
| // Latitude |
| hemiNS = 'N'; |
| if (latitude < 0) { |
| hemiNS = 'S'; |
| latitude = -latitude; |
| } |
| latDeg = (int) latitude; |
| latitude = 60*(latitude - latDeg); |
| latMin = (int) latitude; |
| latFraction = 10000*(latitude - latMin); |
| stralloc_add_format(msgStr, ",%02d%02d.%04d,%c", latDeg, latMin, latFraction, hemiNS); |
| |
| // Longitude |
| hemiEW = 'E'; |
| if (longitude < 0) { |
| hemiEW = 'W'; |
| longitude = -longitude; |
| } |
| lngDeg = (int) longitude; |
| longitude = 60*(longitude - lngDeg); |
| lngMin = (int) longitude; |
| lngFraction = 10000*(longitude - lngMin); |
| stralloc_add_format(msgStr, ",%02d%02d.%04d,%c", lngDeg, lngMin, lngFraction, hemiEW); |
| |
| // Bogus fix quality (1), satellite count, and bogus dilution |
| stralloc_add_format( msgStr, ",1,6,"); |
| |
| // Altitude (to 0.1 meter precision) + bogus diff |
| // Make sure elevation is formatted with a decimal point instead of comma. |
| // setlocale isn't used because of thread safety concerns. |
| stralloc_add_format( elevationStr, "%.1f", metersElevation ); |
| for (elevStrPtr = stralloc_cstr(elevationStr); *elevStrPtr; ++elevStrPtr) { |
| if (*elevStrPtr == ',') { |
| *elevStrPtr = '.'; |
| break; |
| } |
| } |
| stralloc_add_format( msgStr, ",%s,M,0.,M", stralloc_cstr(elevationStr) ); |
| stralloc_reset(elevationStr); |
| |
| // Bogus rest and checksum |
| stralloc_add_str( msgStr, ",,,*47" ); |
| |
| // Send it |
| android_gps_send_nmea( stralloc_cstr(msgStr) ); |
| |
| // Free it |
| stralloc_reset(msgStr); |
| |
| // GPRMC format overview: |
| // GPRMC,220516,A,5133.82,N,00042.24,W,173.8,231.8,130694,004.2,W*70 |
| // 1 2 3 4 5 6 7 8 9 10 11 12 |
| // 1 220516 Time Stamp |
| // 2 A validity - A-ok, V-invalid |
| // 3 5133.82 current Latitude |
| // 4 N North/South |
| // 5 00042.24 current Longitude |
| // 6 W East/West |
| // 7 173.8 Speed in knots |
| // 8 231.8 True course |
| // 9 130694 Date Stamp (13 June 1994) |
| // 10 004.2 Variation |
| // 11 W East/West |
| // 12 *70 checksum |
| |
| STRALLOC_DEFINE(rmcStr); |
| |
| // Time |
| stralloc_add_format(rmcStr, "$GPRMC,%02d%02d%02d,A", hh, mm, ss); |
| |
| // Latitude |
| stralloc_add_format(rmcStr, ",%02d%02d.%04d,%c", latDeg, latMin, latFraction, hemiNS); |
| |
| // Longitude |
| stralloc_add_format(rmcStr, ",%02d%02d.%04d,%c", lngDeg, lngMin, lngFraction, hemiEW); |
| |
| // Speed in knots, course |
| if (headingDegrees < 0.0) headingDegrees += 360.0; |
| stralloc_add_format(rmcStr, ",%.2f,%.2f", speedKnots, headingDegrees); |
| |
| // Date |
| time_t ttTime = time->tv_sec; |
| struct tm *pTm = gmtime(&ttTime); |
| stralloc_add_format(rmcStr, ",%02d%02d%02d", pTm->tm_mday, pTm->tm_mon+1, (pTm->tm_year)%100); |
| |
| // Magnetic variation, cksum (both bogus) |
| stralloc_add_format(rmcStr, ",0.0,W*47"); |
| |
| // Send it |
| android_gps_send_nmea( stralloc_cstr(rmcStr) ); |
| |
| // Free it |
| stralloc_reset(rmcStr); |
| } |
| |
| int |
| android_gps_get_location(double* outLatitude, double* outLongitude, |
| double* outMetersElevation, |
| double* outVelocityKnots, double* outHeading, |
| int* outNSatellites) |
| { |
| // TODO: This should use 'adb shell dumpsys location' and parse the result. |
| // It must ignore parameters the caller does not want (null pointers). |
| return 0; |
| } |
| |
| void |
| android_gps_set_passive_update(bool enable) |
| { |
| s_enable_passive_location_update = enable; |
| } |
| |
| bool |
| android_gps_get_passive_update() |
| { |
| return s_enable_passive_location_update; |
| } |