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    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/*===========================================================================
68FUNCTION    loc_eng_nmea_put_checksum
69
70DESCRIPTION
71   Generate NMEA sentences generated based on position report
72
73DEPENDENCIES
74   NONE
75
76RETURN VALUE
77   Total length of the nmea sentence
78
79SIDE EFFECTS
80   N/A
81
82===========================================================================*/
83int 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/*===========================================================================
100FUNCTION    loc_eng_nmea_generate_pos
101
102DESCRIPTION
103   Generate NMEA sentences generated based on position report
104
105DEPENDENCIES
106   NONE
107
108RETURN VALUE
109   0
110
111SIDE EFFECTS
112   N/A
113
114===========================================================================*/
115void 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/*===========================================================================
590FUNCTION    loc_eng_nmea_generate_sv
591
592DESCRIPTION
593   Generate NMEA sentences generated based on sv report
594
595DEPENDENCIES
596   NONE
597
598RETURN VALUE
599   0
600
601SIDE EFFECTS
602   N/A
603
604===========================================================================*/
605void 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