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