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