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