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