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