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