Home | History | Annotate | Download | only in libloc_api_50001
      1 /* Copyright (c) 2012, The Linux Foundation. All rights reserved.
      2  *
      3  * Redistribution and use in source and binary forms, with or without
      4  * modification, are permitted provided that the following conditions are
      5  * met:
      6  *     * Redistributions of source code must retain the above copyright
      7  *       notice, this list of conditions and the following disclaimer.
      8  *     * Redistributions in binary form must reproduce the above
      9  *       copyright notice, this list of conditions and the following
     10  *       disclaimer in the documentation and/or other materials provided
     11  *       with the distribution.
     12  *     * Neither the name of The Linux Foundation nor the names of its
     13  *       contributors may be used to endorse or promote products derived
     14  *       from this software without specific prior written permission.
     15  *
     16  * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
     17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
     18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
     19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
     20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
     23  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     24  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
     25  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
     26  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27  *
     28  */
     29 
     30 #define LOG_NDDEBUG 0
     31 #define LOG_TAG "LocSvc_eng_nmea"
     32 
     33 #include <loc_eng.h>
     34 #include <loc_eng_nmea.h>
     35 #include <math.h>
     36 #include "log_util.h"
     37 
     38 /*===========================================================================
     39 FUNCTION    loc_eng_nmea_send
     40 
     41 DESCRIPTION
     42    send out NMEA sentence
     43 
     44 DEPENDENCIES
     45    NONE
     46 
     47 RETURN VALUE
     48    Total length of the nmea sentence
     49 
     50 SIDE EFFECTS
     51    N/A
     52 
     53 ===========================================================================*/
     54 void loc_eng_nmea_send(char *pNmea, int length, loc_eng_data_s_type *loc_eng_data_p)
     55 {
     56     struct timeval tv;
     57     gettimeofday(&tv, (struct timezone *) NULL);
     58     int64_t now = tv.tv_sec * 1000LL + tv.tv_usec / 1000;
     59     CALLBACK_LOG_CALLFLOW("nmea_cb", %p, pNmea);
     60     loc_eng_data_p->nmea_cb(now, pNmea, length);
     61     LOC_LOGD("NMEA <%s", pNmea);
     62 }
     63 
     64 /*===========================================================================
     65 FUNCTION    loc_eng_nmea_put_checksum
     66 
     67 DESCRIPTION
     68    Generate NMEA sentences generated based on position report
     69 
     70 DEPENDENCIES
     71    NONE
     72 
     73 RETURN VALUE
     74    Total length of the nmea sentence
     75 
     76 SIDE EFFECTS
     77    N/A
     78 
     79 ===========================================================================*/
     80 int loc_eng_nmea_put_checksum(char *pNmea, int maxSize)
     81 {
     82     uint8_t checksum = 0;
     83     int length = 0;
     84 
     85     pNmea++; //skip the $
     86     while (*pNmea != '\0')
     87     {
     88         checksum ^= *pNmea++;
     89         length++;
     90     }
     91 
     92     int checksumLength = snprintf(pNmea,(maxSize-length-1),"*%02X\r\n", checksum);
     93     return (length + checksumLength);
     94 }
     95 
     96 /*===========================================================================
     97 FUNCTION    loc_eng_nmea_generate_pos
     98 
     99 DESCRIPTION
    100    Generate NMEA sentences generated based on position report
    101 
    102 DEPENDENCIES
    103    NONE
    104 
    105 RETURN VALUE
    106    0
    107 
    108 SIDE EFFECTS
    109    N/A
    110 
    111 ===========================================================================*/
    112 void loc_eng_nmea_generate_pos(loc_eng_data_s_type *loc_eng_data_p,
    113                                const UlpLocation &location,
    114                                const GpsLocationExtended &locationExtended,
    115                                unsigned char generate_nmea)
    116 {
    117     ENTRY_LOG();
    118 
    119     char sentence[NMEA_SENTENCE_MAX_LENGTH] = {0};
    120     char* pMarker = sentence;
    121     int lengthRemaining = sizeof(sentence);
    122     int length = 0;
    123 
    124     time_t utcTime(location.gpsLocation.timestamp/1000);
    125     tm * pTm = gmtime(&utcTime);
    126     int utcYear = pTm->tm_year % 100; // 2 digit year
    127     int utcMonth = pTm->tm_mon + 1; // tm_mon starts at zero
    128     int utcDay = pTm->tm_mday;
    129     int utcHours = pTm->tm_hour;
    130     int utcMinutes = pTm->tm_min;
    131     int utcSeconds = pTm->tm_sec;
    132 
    133     if (generate_nmea) {
    134         // ------------------
    135         // ------$GPGSA------
    136         // ------------------
    137 
    138         uint32_t svUsedCount = 0;
    139         uint32_t svUsedList[32] = {0};
    140         uint32_t mask = loc_eng_data_p->sv_used_mask;
    141         for (uint8_t i = 1; mask > 0 && svUsedCount < 32; i++)
    142         {
    143             if (mask & 1)
    144                 svUsedList[svUsedCount++] = i;
    145             mask = mask >> 1;
    146         }
    147         // clear the cache so they can't be used again
    148         loc_eng_data_p->sv_used_mask = 0;
    149 
    150         char fixType;
    151         if (svUsedCount == 0)
    152             fixType = '1'; // no fix
    153         else if (svUsedCount <= 3)
    154             fixType = '2'; // 2D fix
    155         else
    156             fixType = '3'; // 3D fix
    157 
    158         length = snprintf(pMarker, lengthRemaining, "$GPGSA,A,%c,", fixType);
    159 
    160         if (length < 0 || length >= lengthRemaining)
    161         {
    162             LOC_LOGE("NMEA Error in string formatting");
    163             return;
    164         }
    165         pMarker += length;
    166         lengthRemaining -= length;
    167 
    168         for (uint8_t i = 0; i < 12; i++) // only the first 12 sv go in sentence
    169         {
    170             if (i < svUsedCount)
    171                 length = snprintf(pMarker, lengthRemaining, "%02d,", svUsedList[i]);
    172             else
    173                 length = snprintf(pMarker, lengthRemaining, ",");
    174 
    175             if (length < 0 || length >= lengthRemaining)
    176             {
    177                 LOC_LOGE("NMEA Error in string formatting");
    178                 return;
    179             }
    180             pMarker += length;
    181             lengthRemaining -= length;
    182         }
    183 
    184         if (locationExtended.flags & GPS_LOCATION_EXTENDED_HAS_DOP)
    185         {   // dop is in locationExtended, (QMI)
    186             length = snprintf(pMarker, lengthRemaining, "%.1f,%.1f,%.1f",
    187                               locationExtended.pdop,
    188                               locationExtended.hdop,
    189                               locationExtended.vdop);
    190         }
    191         else if (loc_eng_data_p->pdop > 0 && loc_eng_data_p->hdop > 0 && loc_eng_data_p->vdop > 0)
    192         {   // dop was cached from sv report (RPC)
    193             length = snprintf(pMarker, lengthRemaining, "%.1f,%.1f,%.1f",
    194                               loc_eng_data_p->pdop,
    195                               loc_eng_data_p->hdop,
    196                               loc_eng_data_p->vdop);
    197         }
    198         else
    199         {   // no dop
    200             length = snprintf(pMarker, lengthRemaining, ",,");
    201         }
    202 
    203         length = loc_eng_nmea_put_checksum(sentence, sizeof(sentence));
    204         loc_eng_nmea_send(sentence, length, loc_eng_data_p);
    205 
    206         // ------------------
    207         // ------$GPVTG------
    208         // ------------------
    209 
    210         pMarker = sentence;
    211         lengthRemaining = sizeof(sentence);
    212 
    213         if (location.gpsLocation.flags & GPS_LOCATION_HAS_BEARING)
    214         {
    215             float magTrack = location.gpsLocation.bearing;
    216             if (locationExtended.flags & GPS_LOCATION_EXTENDED_HAS_MAG_DEV)
    217             {
    218                 float magTrack = location.gpsLocation.bearing - locationExtended.magneticDeviation;
    219                 if (magTrack < 0.0)
    220                     magTrack += 360.0;
    221                 else if (magTrack > 360.0)
    222                     magTrack -= 360.0;
    223             }
    224 
    225             length = snprintf(pMarker, lengthRemaining, "$GPVTG,%.1lf,T,%.1lf,M,", location.gpsLocation.bearing, magTrack);
    226         }
    227         else
    228         {
    229             length = snprintf(pMarker, lengthRemaining, "$GPVTG,,T,,M,");
    230         }
    231 
    232         if (length < 0 || length >= lengthRemaining)
    233         {
    234             LOC_LOGE("NMEA Error in string formatting");
    235             return;
    236         }
    237         pMarker += length;
    238         lengthRemaining -= length;
    239 
    240         if (location.gpsLocation.flags & GPS_LOCATION_HAS_SPEED)
    241         {
    242             float speedKnots = location.gpsLocation.speed * (3600.0/1852.0);
    243             float speedKmPerHour = location.gpsLocation.speed * 3.6;
    244 
    245             length = snprintf(pMarker, lengthRemaining, "%.1lf,N,%.1lf,K,", speedKnots, speedKmPerHour);
    246         }
    247         else
    248         {
    249             length = snprintf(pMarker, lengthRemaining, ",N,,K,");
    250         }
    251 
    252         if (length < 0 || length >= lengthRemaining)
    253         {
    254             LOC_LOGE("NMEA Error in string formatting");
    255             return;
    256         }
    257         pMarker += length;
    258         lengthRemaining -= length;
    259 
    260         if (!(location.gpsLocation.flags & GPS_LOCATION_HAS_LAT_LONG))
    261             length = snprintf(pMarker, lengthRemaining, "%c", 'N'); // N means no fix
    262         else if (LOC_POSITION_MODE_STANDALONE == loc_eng_data_p->adapter->getPositionMode().mode)
    263             length = snprintf(pMarker, lengthRemaining, "%c", 'A'); // A means autonomous
    264         else
    265             length = snprintf(pMarker, lengthRemaining, "%c", 'D'); // D means differential
    266 
    267         length = loc_eng_nmea_put_checksum(sentence, sizeof(sentence));
    268         loc_eng_nmea_send(sentence, length, loc_eng_data_p);
    269 
    270         // ------------------
    271         // ------$GPRMC------
    272         // ------------------
    273 
    274         pMarker = sentence;
    275         lengthRemaining = sizeof(sentence);
    276 
    277         length = snprintf(pMarker, lengthRemaining, "$GPRMC,%02d%02d%02d,A," ,
    278                           utcHours, utcMinutes, utcSeconds);
    279 
    280         if (length < 0 || length >= lengthRemaining)
    281         {
    282             LOC_LOGE("NMEA Error in string formatting");
    283             return;
    284         }
    285         pMarker += length;
    286         lengthRemaining -= length;
    287 
    288         if (location.gpsLocation.flags & GPS_LOCATION_HAS_LAT_LONG)
    289         {
    290             double latitude = location.gpsLocation.latitude;
    291             double longitude = location.gpsLocation.longitude;
    292             char latHemisphere;
    293             char lonHemisphere;
    294             double latMinutes;
    295             double lonMinutes;
    296 
    297             if (latitude > 0)
    298             {
    299                 latHemisphere = 'N';
    300             }
    301             else
    302             {
    303                 latHemisphere = 'S';
    304                 latitude *= -1.0;
    305             }
    306 
    307             if (longitude < 0)
    308             {
    309                 lonHemisphere = 'W';
    310                 longitude *= -1.0;
    311             }
    312             else
    313             {
    314                 lonHemisphere = 'E';
    315             }
    316 
    317             latMinutes = fmod(latitude * 60.0 , 60.0);
    318             lonMinutes = fmod(longitude * 60.0 , 60.0);
    319 
    320             length = snprintf(pMarker, lengthRemaining, "%02d%09.6lf,%c,%03d%09.6lf,%c,",
    321                               (uint8_t)floor(latitude), latMinutes, latHemisphere,
    322                               (uint8_t)floor(longitude),lonMinutes, lonHemisphere);
    323         }
    324         else
    325         {
    326             length = snprintf(pMarker, lengthRemaining,",,,,");
    327         }
    328 
    329         if (length < 0 || length >= lengthRemaining)
    330         {
    331             LOC_LOGE("NMEA Error in string formatting");
    332             return;
    333         }
    334         pMarker += length;
    335         lengthRemaining -= length;
    336 
    337         if (location.gpsLocation.flags & GPS_LOCATION_HAS_SPEED)
    338         {
    339             float speedKnots = location.gpsLocation.speed * (3600.0/1852.0);
    340             length = snprintf(pMarker, lengthRemaining, "%.1lf,", speedKnots);
    341         }
    342         else
    343         {
    344             length = snprintf(pMarker, lengthRemaining, ",");
    345         }
    346 
    347         if (length < 0 || length >= lengthRemaining)
    348         {
    349             LOC_LOGE("NMEA Error in string formatting");
    350             return;
    351         }
    352         pMarker += length;
    353         lengthRemaining -= length;
    354 
    355         if (location.gpsLocation.flags & GPS_LOCATION_HAS_BEARING)
    356         {
    357             length = snprintf(pMarker, lengthRemaining, "%.1lf,", location.gpsLocation.bearing);
    358         }
    359         else
    360         {
    361             length = snprintf(pMarker, lengthRemaining, ",");
    362         }
    363 
    364         if (length < 0 || length >= lengthRemaining)
    365         {
    366             LOC_LOGE("NMEA Error in string formatting");
    367             return;
    368         }
    369         pMarker += length;
    370         lengthRemaining -= length;
    371 
    372         length = snprintf(pMarker, lengthRemaining, "%2.2d%2.2d%2.2d,",
    373                           utcDay, utcMonth, utcYear);
    374 
    375         if (length < 0 || length >= lengthRemaining)
    376         {
    377             LOC_LOGE("NMEA Error in string formatting");
    378             return;
    379         }
    380         pMarker += length;
    381         lengthRemaining -= length;
    382 
    383         if (locationExtended.flags & GPS_LOCATION_EXTENDED_HAS_MAG_DEV)
    384         {
    385             float magneticVariation = locationExtended.magneticDeviation;
    386             char direction;
    387             if (magneticVariation < 0.0)
    388             {
    389                 direction = 'W';
    390                 magneticVariation *= -1.0;
    391             }
    392             else
    393             {
    394                 direction = 'E';
    395             }
    396 
    397             length = snprintf(pMarker, lengthRemaining, "%.1lf,%c,",
    398                               magneticVariation, direction);
    399         }
    400         else
    401         {
    402             length = snprintf(pMarker, lengthRemaining, ",,");
    403         }
    404 
    405         if (length < 0 || length >= lengthRemaining)
    406         {
    407             LOC_LOGE("NMEA Error in string formatting");
    408             return;
    409         }
    410         pMarker += length;
    411         lengthRemaining -= length;
    412 
    413         if (!(location.gpsLocation.flags & GPS_LOCATION_HAS_LAT_LONG))
    414             length = snprintf(pMarker, lengthRemaining, "%c", 'N'); // N means no fix
    415         else if (LOC_POSITION_MODE_STANDALONE == loc_eng_data_p->adapter->getPositionMode().mode)
    416             length = snprintf(pMarker, lengthRemaining, "%c", 'A'); // A means autonomous
    417         else
    418             length = snprintf(pMarker, lengthRemaining, "%c", 'D'); // D means differential
    419 
    420         length = loc_eng_nmea_put_checksum(sentence, sizeof(sentence));
    421         loc_eng_nmea_send(sentence, length, loc_eng_data_p);
    422 
    423         // ------------------
    424         // ------$GPGGA------
    425         // ------------------
    426 
    427         pMarker = sentence;
    428         lengthRemaining = sizeof(sentence);
    429 
    430         length = snprintf(pMarker, lengthRemaining, "$GPGGA,%02d%02d%02d," ,
    431                           utcHours, utcMinutes, utcSeconds);
    432 
    433         if (length < 0 || length >= lengthRemaining)
    434         {
    435             LOC_LOGE("NMEA Error in string formatting");
    436             return;
    437         }
    438         pMarker += length;
    439         lengthRemaining -= length;
    440 
    441         if (location.gpsLocation.flags & GPS_LOCATION_HAS_LAT_LONG)
    442         {
    443             double latitude = location.gpsLocation.latitude;
    444             double longitude = location.gpsLocation.longitude;
    445             char latHemisphere;
    446             char lonHemisphere;
    447             double latMinutes;
    448             double lonMinutes;
    449 
    450             if (latitude > 0)
    451             {
    452                 latHemisphere = 'N';
    453             }
    454             else
    455             {
    456                 latHemisphere = 'S';
    457                 latitude *= -1.0;
    458             }
    459 
    460             if (longitude < 0)
    461             {
    462                 lonHemisphere = 'W';
    463                 longitude *= -1.0;
    464             }
    465             else
    466             {
    467                 lonHemisphere = 'E';
    468             }
    469 
    470             latMinutes = fmod(latitude * 60.0 , 60.0);
    471             lonMinutes = fmod(longitude * 60.0 , 60.0);
    472 
    473             length = snprintf(pMarker, lengthRemaining, "%02d%09.6lf,%c,%03d%09.6lf,%c,",
    474                               (uint8_t)floor(latitude), latMinutes, latHemisphere,
    475                               (uint8_t)floor(longitude),lonMinutes, lonHemisphere);
    476         }
    477         else
    478         {
    479             length = snprintf(pMarker, lengthRemaining,",,,,");
    480         }
    481 
    482         if (length < 0 || length >= lengthRemaining)
    483         {
    484             LOC_LOGE("NMEA Error in string formatting");
    485             return;
    486         }
    487         pMarker += length;
    488         lengthRemaining -= length;
    489 
    490         char gpsQuality;
    491         if (!(location.gpsLocation.flags & GPS_LOCATION_HAS_LAT_LONG))
    492             gpsQuality = '0'; // 0 means no fix
    493         else if (LOC_POSITION_MODE_STANDALONE == loc_eng_data_p->adapter->getPositionMode().mode)
    494             gpsQuality = '1'; // 1 means GPS fix
    495         else
    496             gpsQuality = '2'; // 2 means DGPS fix
    497 
    498         if (locationExtended.flags & GPS_LOCATION_EXTENDED_HAS_DOP)
    499         {   // dop is in locationExtended, (QMI)
    500             length = snprintf(pMarker, lengthRemaining, "%c,%02d,%.1f,",
    501                               gpsQuality, svUsedCount, locationExtended.hdop);
    502         }
    503         else if (loc_eng_data_p->pdop > 0 && loc_eng_data_p->hdop > 0 && loc_eng_data_p->vdop > 0)
    504         {   // dop was cached from sv report (RPC)
    505             length = snprintf(pMarker, lengthRemaining, "%c,%02d,%.1f,",
    506                               gpsQuality, svUsedCount, loc_eng_data_p->hdop);
    507         }
    508         else
    509         {   // no hdop
    510             length = snprintf(pMarker, lengthRemaining, "%c,%02d,,",
    511                               gpsQuality, svUsedCount);
    512         }
    513 
    514         if (length < 0 || length >= lengthRemaining)
    515         {
    516             LOC_LOGE("NMEA Error in string formatting");
    517             return;
    518         }
    519         pMarker += length;
    520         lengthRemaining -= length;
    521 
    522         if (locationExtended.flags & GPS_LOCATION_EXTENDED_HAS_ALTITUDE_MEAN_SEA_LEVEL)
    523         {
    524             length = snprintf(pMarker, lengthRemaining, "%.1lf,M,",
    525                               locationExtended.altitudeMeanSeaLevel);
    526         }
    527         else
    528         {
    529             length = snprintf(pMarker, lengthRemaining,",,");
    530         }
    531 
    532         if (length < 0 || length >= lengthRemaining)
    533         {
    534             LOC_LOGE("NMEA Error in string formatting");
    535             return;
    536         }
    537         pMarker += length;
    538         lengthRemaining -= length;
    539 
    540         if ((location.gpsLocation.flags & GPS_LOCATION_HAS_ALTITUDE) &&
    541             (locationExtended.flags & GPS_LOCATION_EXTENDED_HAS_ALTITUDE_MEAN_SEA_LEVEL))
    542         {
    543             length = snprintf(pMarker, lengthRemaining, "%.1lf,M,,",
    544                               location.gpsLocation.altitude - locationExtended.altitudeMeanSeaLevel);
    545         }
    546         else
    547         {
    548             length = snprintf(pMarker, lengthRemaining,",,,");
    549         }
    550 
    551         length = loc_eng_nmea_put_checksum(sentence, sizeof(sentence));
    552         loc_eng_nmea_send(sentence, length, loc_eng_data_p);
    553 
    554     }
    555     //Send blank NMEA reports for non-final fixes
    556     else {
    557         strlcpy(sentence, "$GPGSA,A,1,,,,,,,,,,,,,,,", sizeof(sentence));
    558         length = loc_eng_nmea_put_checksum(sentence, sizeof(sentence));
    559         loc_eng_nmea_send(sentence, length, loc_eng_data_p);
    560 
    561         strlcpy(sentence, "$GPVTG,,T,,M,,N,,K,N", sizeof(sentence));
    562         length = loc_eng_nmea_put_checksum(sentence, sizeof(sentence));
    563         loc_eng_nmea_send(sentence, length, loc_eng_data_p);
    564 
    565         strlcpy(sentence, "$GPRMC,,V,,,,,,,,,,N", sizeof(sentence));
    566         length = loc_eng_nmea_put_checksum(sentence, sizeof(sentence));
    567         loc_eng_nmea_send(sentence, length, loc_eng_data_p);
    568 
    569         strlcpy(sentence, "$GPGGA,,,,,,0,,,,,,,,", sizeof(sentence));
    570         length = loc_eng_nmea_put_checksum(sentence, sizeof(sentence));
    571         loc_eng_nmea_send(sentence, length, loc_eng_data_p);
    572     }
    573     // clear the dop cache so they can't be used again
    574     loc_eng_data_p->pdop = 0;
    575     loc_eng_data_p->hdop = 0;
    576     loc_eng_data_p->vdop = 0;
    577 
    578     EXIT_LOG(%d, 0);
    579 }
    580 
    581 
    582 
    583 /*===========================================================================
    584 FUNCTION    loc_eng_nmea_generate_sv
    585 
    586 DESCRIPTION
    587    Generate NMEA sentences generated based on sv report
    588 
    589 DEPENDENCIES
    590    NONE
    591 
    592 RETURN VALUE
    593    0
    594 
    595 SIDE EFFECTS
    596    N/A
    597 
    598 ===========================================================================*/
    599 void loc_eng_nmea_generate_sv(loc_eng_data_s_type *loc_eng_data_p,
    600                               const GpsSvStatus &svStatus, const GpsLocationExtended &locationExtended)
    601 {
    602     ENTRY_LOG();
    603 
    604     char sentence[NMEA_SENTENCE_MAX_LENGTH] = {0};
    605     char* pMarker = sentence;
    606     int lengthRemaining = sizeof(sentence);
    607     int length = 0;
    608 
    609     // ------------------
    610     // ------$GPGSV------
    611     // ------------------
    612 
    613     if (svStatus.num_svs <= 0)
    614     {
    615         // no svs in view, so just send a blank $GPGSV sentence
    616         strlcpy(sentence, "$GPGSV,1,1,0,", sizeof(sentence));
    617         length = loc_eng_nmea_put_checksum(sentence, sizeof(sentence));
    618         loc_eng_nmea_send(sentence, length, loc_eng_data_p);
    619     }
    620     else
    621     {
    622         int svCount = svStatus.num_svs;
    623         int sentenceCount = svCount / 4;
    624         if (svStatus.num_svs % 4)
    625             sentenceCount++;
    626         int sentenceNumber = 1;
    627         int svNumber = 1;
    628 
    629         while (sentenceNumber <= sentenceCount)
    630         {
    631             pMarker = sentence;
    632             lengthRemaining = sizeof(sentence);
    633 
    634             length = snprintf(pMarker, lengthRemaining, "$GPGSV,%d,%d,%02d",
    635                           sentenceCount, sentenceNumber, svCount);
    636 
    637             if (length < 0 || length >= lengthRemaining)
    638             {
    639                 LOC_LOGE("NMEA Error in string formatting");
    640                 return;
    641             }
    642             pMarker += length;
    643             lengthRemaining -= length;
    644 
    645             for (int i=0; (svNumber <= svCount) && (i < 4); i++, svNumber++)
    646             {
    647                 length = snprintf(pMarker, lengthRemaining,",%02d,%02d,%03d,",
    648                                   svStatus.sv_list[svNumber-1].prn,
    649                                   (int)(0.5 + svStatus.sv_list[svNumber-1].elevation), //float to int
    650                                   (int)(0.5 + svStatus.sv_list[svNumber-1].azimuth)); //float to int
    651 
    652                 if (length < 0 || length >= lengthRemaining)
    653                 {
    654                     LOC_LOGE("NMEA Error in string formatting");
    655                     return;
    656                 }
    657                 pMarker += length;
    658                 lengthRemaining -= length;
    659 
    660                 if (svStatus.sv_list[svNumber-1].snr > 0)
    661                 {
    662                     length = snprintf(pMarker, lengthRemaining,"%02d",
    663                                      (int)(0.5 + svStatus.sv_list[svNumber-1].snr)); //float to int
    664 
    665                     if (length < 0 || length >= lengthRemaining)
    666                     {
    667                         LOC_LOGE("NMEA Error in string formatting");
    668                         return;
    669                     }
    670                     pMarker += length;
    671                     lengthRemaining -= length;
    672                 }
    673             }
    674 
    675             length = loc_eng_nmea_put_checksum(sentence, sizeof(sentence));
    676             loc_eng_nmea_send(sentence, length, loc_eng_data_p);
    677             sentenceNumber++;
    678 
    679         }
    680     }
    681 
    682     if (svStatus.used_in_fix_mask == 0)
    683     {   // No sv used, so there will be no position report, so send
    684         // blank NMEA sentences
    685         strlcpy(sentence, "$GPGSA,A,1,,,,,,,,,,,,,,,", sizeof(sentence));
    686         length = loc_eng_nmea_put_checksum(sentence, sizeof(sentence));
    687         loc_eng_nmea_send(sentence, length, loc_eng_data_p);
    688 
    689         strlcpy(sentence, "$GPVTG,,T,,M,,N,,K,N", sizeof(sentence));
    690         length = loc_eng_nmea_put_checksum(sentence, sizeof(sentence));
    691         loc_eng_nmea_send(sentence, length, loc_eng_data_p);
    692 
    693         strlcpy(sentence, "$GPRMC,,V,,,,,,,,,,N", sizeof(sentence));
    694         length = loc_eng_nmea_put_checksum(sentence, sizeof(sentence));
    695         loc_eng_nmea_send(sentence, length, loc_eng_data_p);
    696 
    697         strlcpy(sentence, "$GPGGA,,,,,,0,,,,,,,,", sizeof(sentence));
    698         length = loc_eng_nmea_put_checksum(sentence, sizeof(sentence));
    699         loc_eng_nmea_send(sentence, length, loc_eng_data_p);
    700     }
    701     else
    702     {   // cache the used in fix mask, as it will be needed to send $GPGSA
    703         // during the position report
    704         loc_eng_data_p->sv_used_mask = svStatus.used_in_fix_mask;
    705 
    706         // For RPC, the DOP are sent during sv report, so cache them
    707         // now to be sent during position report.
    708         // For QMI, the DOP will be in position report.
    709         if (locationExtended.flags & GPS_LOCATION_EXTENDED_HAS_DOP)
    710         {
    711             loc_eng_data_p->pdop = locationExtended.pdop;
    712             loc_eng_data_p->hdop = locationExtended.hdop;
    713             loc_eng_data_p->vdop = locationExtended.vdop;
    714         }
    715         else
    716         {
    717             loc_eng_data_p->pdop = 0;
    718             loc_eng_data_p->hdop = 0;
    719             loc_eng_data_p->vdop = 0;
    720         }
    721 
    722     }
    723 
    724     EXIT_LOG(%d, 0);
    725 }
    726