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