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