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