1 /* Copyright (c) 2012-2017, 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_NDEBUG 0 31 #define LOG_TAG "LocSvc_nmea" 32 #include <loc_nmea.h> 33 #include <math.h> 34 #include <platform_lib_includes.h> 35 36 #define GLONASS_SV_ID_OFFSET 64 37 #define MAX_SATELLITES_IN_USE 12 38 39 // GNSS system id according to NMEA spec 40 #define SYSTEM_ID_GPS 1 41 #define SYSTEM_ID_GLONASS 2 42 #define SYSTEM_ID_GALILEO 3 43 // Extended systems 44 #define SYSTEM_ID_BEIDOU 4 45 #define SYSTEM_ID_QZSS 5 46 47 typedef struct loc_nmea_sv_meta_s 48 { 49 char talker[3]; 50 LocGnssConstellationType svType; 51 uint32_t mask; 52 uint32_t svCount; 53 uint32_t svIdOffset; 54 uint32_t systemId; 55 } loc_nmea_sv_meta; 56 57 typedef struct loc_sv_cache_info_s 58 { 59 uint32_t gps_used_mask; 60 uint32_t glo_used_mask; 61 uint32_t gal_used_mask; 62 uint32_t qzss_used_mask; 63 uint32_t bds_used_mask; 64 uint32_t gps_count; 65 uint32_t glo_count; 66 uint32_t gal_count; 67 uint32_t qzss_count; 68 uint32_t bds_count; 69 float hdop; 70 float pdop; 71 float vdop; 72 } loc_sv_cache_info; 73 74 static loc_sv_cache_info sv_cache_info; 75 76 /*=========================================================================== 77 FUNCTION loc_nmea_sv_meta_init 78 79 DESCRIPTION 80 Init loc_nmea_sv_meta passed in 81 82 DEPENDENCIES 83 NONE 84 85 RETURN VALUE 86 Pointer to loc_nmea_sv_meta 87 88 SIDE EFFECTS 89 N/A 90 91 ===========================================================================*/ 92 static loc_nmea_sv_meta* loc_nmea_sv_meta_init(loc_nmea_sv_meta& sv_meta, 93 GnssSvType svType, 94 bool needCombine) 95 { 96 memset(&sv_meta, 0, sizeof(sv_meta)); 97 sv_meta.svType = svType; 98 99 switch (svType) 100 { 101 case GNSS_SV_TYPE_GPS: 102 sv_meta.talker[0] = 'G'; 103 sv_meta.talker[1] = 'P'; 104 sv_meta.mask = sv_cache_info.gps_used_mask; 105 sv_meta.svCount = sv_cache_info.gps_count; 106 sv_meta.systemId = SYSTEM_ID_GPS; 107 break; 108 case GNSS_SV_TYPE_GLONASS: 109 sv_meta.talker[0] = 'G'; 110 sv_meta.talker[1] = 'L'; 111 sv_meta.mask = sv_cache_info.glo_used_mask; 112 sv_meta.svCount = sv_cache_info.glo_count; 113 // GLONASS SV ids are from 65-96 114 sv_meta.svIdOffset = GLONASS_SV_ID_OFFSET; 115 sv_meta.systemId = SYSTEM_ID_GLONASS; 116 break; 117 case GNSS_SV_TYPE_GALILEO: 118 sv_meta.talker[0] = 'G'; 119 sv_meta.talker[1] = 'A'; 120 sv_meta.mask = sv_cache_info.gal_used_mask; 121 sv_meta.svCount = sv_cache_info.gal_count; 122 sv_meta.systemId = SYSTEM_ID_GALILEO; 123 break; 124 case GNSS_SV_TYPE_QZSS: 125 sv_meta.talker[0] = 'P'; 126 sv_meta.talker[1] = 'Q'; 127 sv_meta.mask = sv_cache_info.qzss_used_mask; 128 sv_meta.svCount = sv_cache_info.qzss_count; 129 // QZSS SV ids are from 193-197. So keep svIdOffset 0 130 sv_meta.systemId = SYSTEM_ID_QZSS; 131 break; 132 case GNSS_SV_TYPE_BEIDOU: 133 sv_meta.talker[0] = 'P'; 134 sv_meta.talker[1] = 'Q'; 135 sv_meta.mask = sv_cache_info.bds_used_mask; 136 sv_meta.svCount = sv_cache_info.bds_count; 137 // BDS SV ids are from 201-235. So keep svIdOffset 0 138 sv_meta.systemId = SYSTEM_ID_BEIDOU; 139 break; 140 default: 141 LOC_LOGE("NMEA Error unknow constellation type: %d", svType); 142 return NULL; 143 } 144 if (needCombine && 145 (sv_cache_info.gps_used_mask ? 1 : 0) + 146 (sv_cache_info.glo_used_mask ? 1 : 0) + 147 (sv_cache_info.gal_used_mask ? 1 : 0) + 148 (sv_cache_info.qzss_used_mask ? 1 : 0) + 149 (sv_cache_info.bds_used_mask ? 1 : 0) > 1) 150 { 151 // If GPS, GLONASS, Galileo, QZSS, BDS etc. are combined 152 // to obtain the reported position solution, 153 // talker shall be set to GN, to indicate that 154 // the satellites are used in a combined solution 155 sv_meta.talker[0] = 'G'; 156 sv_meta.talker[1] = 'N'; 157 } 158 return &sv_meta; 159 } 160 161 /*=========================================================================== 162 FUNCTION loc_nmea_put_checksum 163 164 DESCRIPTION 165 Generate NMEA sentences generated based on position report 166 167 DEPENDENCIES 168 NONE 169 170 RETURN VALUE 171 Total length of the nmea sentence 172 173 SIDE EFFECTS 174 N/A 175 176 ===========================================================================*/ 177 static int loc_nmea_put_checksum(char *pNmea, int maxSize) 178 { 179 uint8_t checksum = 0; 180 int length = 0; 181 if(NULL == pNmea) 182 return 0; 183 184 pNmea++; //skip the $ 185 while (*pNmea != '\0') 186 { 187 checksum ^= *pNmea++; 188 length++; 189 } 190 191 // length now contains nmea sentence string length not including $ sign. 192 int checksumLength = snprintf(pNmea,(maxSize-length-1),"*%02X\r\n", checksum); 193 194 // total length of nmea sentence is length of nmea sentence inc $ sign plus 195 // length of checksum (+1 is to cover the $ character in the length). 196 return (length + checksumLength + 1); 197 } 198 199 /*=========================================================================== 200 FUNCTION loc_nmea_generate_GSA 201 202 DESCRIPTION 203 Generate NMEA GSA sentences generated based on position report 204 Currently below sentences are generated: 205 - $GPGSA : GPS DOP and active SVs 206 - $GLGSA : GLONASS DOP and active SVs 207 - $GAGSA : GALILEO DOP and active SVs 208 - $GNGSA : GNSS DOP and active SVs 209 210 DEPENDENCIES 211 NONE 212 213 RETURN VALUE 214 Number of SVs used 215 216 SIDE EFFECTS 217 N/A 218 219 ===========================================================================*/ 220 static uint32_t loc_nmea_generate_GSA(const GpsLocationExtended &locationExtended, 221 char* sentence, 222 int bufSize, 223 loc_nmea_sv_meta* sv_meta_p, 224 std::vector<std::string> &nmeaArraystr) 225 { 226 if (!sentence || bufSize <= 0 || !sv_meta_p) 227 { 228 LOC_LOGE("NMEA Error invalid arguments."); 229 return 0; 230 } 231 232 char* pMarker = sentence; 233 int lengthRemaining = bufSize; 234 int length = 0; 235 236 uint32_t svUsedCount = 0; 237 uint32_t svUsedList[32] = {0}; 238 239 char fixType = '\0'; 240 241 const char* talker = sv_meta_p->talker; 242 uint32_t svIdOffset = sv_meta_p->svIdOffset; 243 uint32_t mask = sv_meta_p->mask; 244 245 for (uint8_t i = 1; mask > 0 && svUsedCount < 32; i++) 246 { 247 if (mask & 1) 248 svUsedList[svUsedCount++] = i + svIdOffset; 249 mask = mask >> 1; 250 } 251 252 if (svUsedCount == 0 && GNSS_SV_TYPE_GPS != sv_meta_p->svType) 253 return 0; 254 255 if (svUsedCount == 0) 256 fixType = '1'; // no fix 257 else if (svUsedCount <= 3) 258 fixType = '2'; // 2D fix 259 else 260 fixType = '3'; // 3D fix 261 262 // Start printing the sentence 263 // Format: $--GSA,a,x,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,xx,p.p,h.h,v.v*cc 264 // a : Mode : A : Automatic, allowed to automatically switch 2D/3D 265 // x : Fixtype : 1 (no fix), 2 (2D fix), 3 (3D fix) 266 // xx : 12 SV ID 267 // p.p : Position DOP (Dilution of Precision) 268 // h.h : Horizontal DOP 269 // v.v : Vertical DOP 270 // cc : Checksum value 271 length = snprintf(pMarker, lengthRemaining, "$%sGSA,A,%c,", talker, fixType); 272 273 if (length < 0 || length >= lengthRemaining) 274 { 275 LOC_LOGE("NMEA Error in string formatting"); 276 return 0; 277 } 278 pMarker += length; 279 lengthRemaining -= length; 280 281 // Add first 12 satellite IDs 282 for (uint8_t i = 0; i < 12; i++) 283 { 284 if (i < svUsedCount) 285 length = snprintf(pMarker, lengthRemaining, "%02d,", svUsedList[i]); 286 else 287 length = snprintf(pMarker, lengthRemaining, ","); 288 289 if (length < 0 || length >= lengthRemaining) 290 { 291 LOC_LOGE("NMEA Error in string formatting"); 292 return 0; 293 } 294 pMarker += length; 295 lengthRemaining -= length; 296 } 297 298 // Add the position/horizontal/vertical DOP values 299 if (locationExtended.flags & GPS_LOCATION_EXTENDED_HAS_DOP) 300 { 301 length = snprintf(pMarker, lengthRemaining, "%.1f,%.1f,%.1f,", 302 locationExtended.pdop, 303 locationExtended.hdop, 304 locationExtended.vdop); 305 } 306 else 307 { // no dop 308 length = snprintf(pMarker, lengthRemaining, ",,,"); 309 } 310 pMarker += length; 311 lengthRemaining -= length; 312 313 // system id 314 length = snprintf(pMarker, lengthRemaining, "%d", sv_meta_p->systemId); 315 pMarker += length; 316 lengthRemaining -= length; 317 318 /* Sentence is ready, add checksum and broadcast */ 319 length = loc_nmea_put_checksum(sentence, bufSize); 320 nmeaArraystr.push_back(sentence); 321 322 return svUsedCount; 323 } 324 325 /*=========================================================================== 326 FUNCTION loc_nmea_generate_GSV 327 328 DESCRIPTION 329 Generate NMEA GSV sentences generated based on sv report 330 Currently below sentences are generated: 331 - $GPGSV: GPS Satellites in View 332 - $GNGSV: GLONASS Satellites in View 333 - $GAGSV: GALILEO Satellites in View 334 335 DEPENDENCIES 336 NONE 337 338 RETURN VALUE 339 NONE 340 341 SIDE EFFECTS 342 N/A 343 344 ===========================================================================*/ 345 static void loc_nmea_generate_GSV(const GnssSvNotification &svNotify, 346 char* sentence, 347 int bufSize, 348 loc_nmea_sv_meta* sv_meta_p, 349 std::vector<std::string> &nmeaArraystr) 350 { 351 if (!sentence || bufSize <= 0) 352 { 353 LOC_LOGE("NMEA Error invalid argument."); 354 return; 355 } 356 357 char* pMarker = sentence; 358 int lengthRemaining = bufSize; 359 int length = 0; 360 int sentenceCount = 0; 361 int sentenceNumber = 1; 362 size_t svNumber = 1; 363 364 const char* talker = sv_meta_p->talker; 365 uint32_t svIdOffset = sv_meta_p->svIdOffset; 366 int svCount = sv_meta_p->svCount; 367 368 if (svCount <= 0) 369 { 370 // no svs in view, so just send a blank $--GSV sentence 371 snprintf(sentence, lengthRemaining, "$%sGSV,1,1,0,", talker); 372 length = loc_nmea_put_checksum(sentence, bufSize); 373 nmeaArraystr.push_back(sentence); 374 return; 375 } 376 377 svNumber = 1; 378 sentenceNumber = 1; 379 sentenceCount = svCount / 4 + (svCount % 4 != 0); 380 381 while (sentenceNumber <= sentenceCount) 382 { 383 pMarker = sentence; 384 lengthRemaining = bufSize; 385 386 length = snprintf(pMarker, lengthRemaining, "$%sGSV,%d,%d,%02d", 387 talker, sentenceCount, sentenceNumber, svCount); 388 389 if (length < 0 || length >= lengthRemaining) 390 { 391 LOC_LOGE("NMEA Error in string formatting"); 392 return; 393 } 394 pMarker += length; 395 lengthRemaining -= length; 396 397 for (int i=0; (svNumber <= svNotify.count) && (i < 4); svNumber++) 398 { 399 if (sv_meta_p->svType == svNotify.gnssSvs[svNumber - 1].type) 400 { 401 length = snprintf(pMarker, lengthRemaining,",%02d,%02d,%03d,", 402 svNotify.gnssSvs[svNumber - 1].svId + svIdOffset, 403 (int)(0.5 + svNotify.gnssSvs[svNumber - 1].elevation), //float to int 404 (int)(0.5 + svNotify.gnssSvs[svNumber - 1].azimuth)); //float to int 405 406 if (length < 0 || length >= lengthRemaining) 407 { 408 LOC_LOGE("NMEA Error in string formatting"); 409 return; 410 } 411 pMarker += length; 412 lengthRemaining -= length; 413 414 if (svNotify.gnssSvs[svNumber - 1].cN0Dbhz > 0) 415 { 416 length = snprintf(pMarker, lengthRemaining,"%02d", 417 (int)(0.5 + svNotify.gnssSvs[svNumber - 1].cN0Dbhz)); //float to int 418 419 if (length < 0 || length >= lengthRemaining) 420 { 421 LOC_LOGE("NMEA Error in string formatting"); 422 return; 423 } 424 pMarker += length; 425 lengthRemaining -= length; 426 } 427 428 i++; 429 } 430 431 } 432 433 // The following entries are specific to QZSS and BDS 434 if ((sv_meta_p->svType == GNSS_SV_TYPE_QZSS) || 435 (sv_meta_p->svType == GNSS_SV_TYPE_BEIDOU)) 436 { 437 // last one is System id and second last is Signal Id which is always zero 438 length = snprintf(pMarker, lengthRemaining,",%d,%d",0,sv_meta_p->systemId); 439 pMarker += length; 440 lengthRemaining -= length; 441 } 442 443 length = loc_nmea_put_checksum(sentence, bufSize); 444 nmeaArraystr.push_back(sentence); 445 sentenceNumber++; 446 447 } //while 448 } 449 450 /*=========================================================================== 451 FUNCTION loc_nmea_generate_pos 452 453 DESCRIPTION 454 Generate NMEA sentences generated based on position report 455 Currently below sentences are generated within this function: 456 - $GPGSA : GPS DOP and active SVs 457 - $GLGSA : GLONASS DOP and active SVs 458 - $GAGSA : GALILEO DOP and active SVs 459 - $GNGSA : GNSS DOP and active SVs 460 - $--VTG : Track made good and ground speed 461 - $--RMC : Recommended minimum navigation information 462 - $--GGA : Time, position and fix related data 463 464 DEPENDENCIES 465 NONE 466 467 RETURN VALUE 468 0 469 470 SIDE EFFECTS 471 N/A 472 473 ===========================================================================*/ 474 void loc_nmea_generate_pos(const UlpLocation &location, 475 const GpsLocationExtended &locationExtended, 476 unsigned char generate_nmea, 477 std::vector<std::string> &nmeaArraystr) 478 { 479 ENTRY_LOG(); 480 time_t utcTime(location.gpsLocation.timestamp/1000); 481 tm * pTm = gmtime(&utcTime); 482 if (NULL == pTm) { 483 LOC_LOGE("gmtime failed"); 484 return; 485 } 486 487 char sentence[NMEA_SENTENCE_MAX_LENGTH] = {0}; 488 char* pMarker = sentence; 489 int lengthRemaining = sizeof(sentence); 490 int length = 0; 491 int utcYear = pTm->tm_year % 100; // 2 digit year 492 int utcMonth = pTm->tm_mon + 1; // tm_mon starts at zero 493 int utcDay = pTm->tm_mday; 494 int utcHours = pTm->tm_hour; 495 int utcMinutes = pTm->tm_min; 496 int utcSeconds = pTm->tm_sec; 497 int utcMSeconds = (location.gpsLocation.timestamp)%1000; 498 499 if (generate_nmea) { 500 char talker[3] = {'G', 'P', '\0'}; 501 uint32_t svUsedCount = 0; 502 uint32_t count = 0; 503 loc_nmea_sv_meta sv_meta; 504 // ------------------- 505 // ---$GPGSA/$GNGSA--- 506 // ------------------- 507 508 count = loc_nmea_generate_GSA(locationExtended, sentence, sizeof(sentence), 509 loc_nmea_sv_meta_init(sv_meta, GNSS_SV_TYPE_GPS, true), nmeaArraystr); 510 if (count > 0) 511 { 512 svUsedCount += count; 513 talker[0] = sv_meta.talker[0]; 514 talker[1] = sv_meta.talker[1]; 515 } 516 517 // ------------------- 518 // ---$GLGSA/$GNGSA--- 519 // ------------------- 520 521 count = loc_nmea_generate_GSA(locationExtended, sentence, sizeof(sentence), 522 loc_nmea_sv_meta_init(sv_meta, GNSS_SV_TYPE_GLONASS, true), nmeaArraystr); 523 if (count > 0) 524 { 525 svUsedCount += count; 526 talker[0] = sv_meta.talker[0]; 527 talker[1] = sv_meta.talker[1]; 528 } 529 530 // ------------------- 531 // ---$GAGSA/$GNGSA--- 532 // ------------------- 533 534 count = loc_nmea_generate_GSA(locationExtended, sentence, sizeof(sentence), 535 loc_nmea_sv_meta_init(sv_meta, GNSS_SV_TYPE_GALILEO, true), nmeaArraystr); 536 if (count > 0) 537 { 538 svUsedCount += count; 539 talker[0] = sv_meta.talker[0]; 540 talker[1] = sv_meta.talker[1]; 541 } 542 543 // -------------------------- 544 // ---$PQGSA/$GNGSA (QZSS)--- 545 // -------------------------- 546 547 count = loc_nmea_generate_GSA(locationExtended, sentence, sizeof(sentence), 548 loc_nmea_sv_meta_init(sv_meta, GNSS_SV_TYPE_QZSS, false), nmeaArraystr); 549 if (count > 0) 550 { 551 svUsedCount += count; 552 // talker should be default "GP". If GPS, GLO etc is used, it should be "GN" 553 } 554 555 // ---------------------------- 556 // ---$PQGSA/$GNGSA (BEIDOU)--- 557 // ---------------------------- 558 count = loc_nmea_generate_GSA(locationExtended, sentence, sizeof(sentence), 559 loc_nmea_sv_meta_init(sv_meta, GNSS_SV_TYPE_BEIDOU, false), nmeaArraystr); 560 if (count > 0) 561 { 562 svUsedCount += count; 563 // talker should be default "GP". If GPS, GLO etc is used, it should be "GN" 564 } 565 566 // ------------------- 567 // ------$--VTG------- 568 // ------------------- 569 570 pMarker = sentence; 571 lengthRemaining = sizeof(sentence); 572 573 if (location.gpsLocation.flags & LOC_GPS_LOCATION_HAS_BEARING) 574 { 575 float magTrack = location.gpsLocation.bearing; 576 if (locationExtended.flags & GPS_LOCATION_EXTENDED_HAS_MAG_DEV) 577 { 578 float magTrack = location.gpsLocation.bearing - locationExtended.magneticDeviation; 579 if (magTrack < 0.0) 580 magTrack += 360.0; 581 else if (magTrack > 360.0) 582 magTrack -= 360.0; 583 } 584 585 length = snprintf(pMarker, lengthRemaining, "$%sVTG,%.1lf,T,%.1lf,M,", talker, location.gpsLocation.bearing, magTrack); 586 } 587 else 588 { 589 length = snprintf(pMarker, lengthRemaining, "$%sVTG,,T,,M,", talker); 590 } 591 592 if (length < 0 || length >= lengthRemaining) 593 { 594 LOC_LOGE("NMEA Error in string formatting"); 595 return; 596 } 597 pMarker += length; 598 lengthRemaining -= length; 599 600 if (location.gpsLocation.flags & LOC_GPS_LOCATION_HAS_SPEED) 601 { 602 float speedKnots = location.gpsLocation.speed * (3600.0/1852.0); 603 float speedKmPerHour = location.gpsLocation.speed * 3.6; 604 605 length = snprintf(pMarker, lengthRemaining, "%.1lf,N,%.1lf,K,", speedKnots, speedKmPerHour); 606 } 607 else 608 { 609 length = snprintf(pMarker, lengthRemaining, ",N,,K,"); 610 } 611 612 if (length < 0 || length >= lengthRemaining) 613 { 614 LOC_LOGE("NMEA Error in string formatting"); 615 return; 616 } 617 pMarker += length; 618 lengthRemaining -= length; 619 620 if (!(location.gpsLocation.flags & LOC_GPS_LOCATION_HAS_LAT_LONG)) 621 // N means no fix 622 length = snprintf(pMarker, lengthRemaining, "%c", 'N'); 623 else if (LOC_NAV_MASK_SBAS_CORRECTION_IONO & locationExtended.navSolutionMask) 624 // D means differential 625 length = snprintf(pMarker, lengthRemaining, "%c", 'D'); 626 else if (LOC_POS_TECH_MASK_SENSORS == locationExtended.tech_mask) 627 // E means estimated (dead reckoning) 628 length = snprintf(pMarker, lengthRemaining, "%c", 'E'); 629 else // A means autonomous 630 length = snprintf(pMarker, lengthRemaining, "%c", 'A'); 631 632 length = loc_nmea_put_checksum(sentence, sizeof(sentence)); 633 nmeaArraystr.push_back(sentence); 634 635 // ------------------- 636 // ------$--RMC------- 637 // ------------------- 638 639 pMarker = sentence; 640 lengthRemaining = sizeof(sentence); 641 642 length = snprintf(pMarker, lengthRemaining, "$%sRMC,%02d%02d%02d.%02d,A," , 643 talker, utcHours, utcMinutes, utcSeconds,utcMSeconds/10); 644 645 if (length < 0 || length >= lengthRemaining) 646 { 647 LOC_LOGE("NMEA Error in string formatting"); 648 return; 649 } 650 pMarker += length; 651 lengthRemaining -= length; 652 653 if (location.gpsLocation.flags & LOC_GPS_LOCATION_HAS_LAT_LONG) 654 { 655 double latitude = location.gpsLocation.latitude; 656 double longitude = location.gpsLocation.longitude; 657 char latHemisphere; 658 char lonHemisphere; 659 double latMinutes; 660 double lonMinutes; 661 662 if (latitude > 0) 663 { 664 latHemisphere = 'N'; 665 } 666 else 667 { 668 latHemisphere = 'S'; 669 latitude *= -1.0; 670 } 671 672 if (longitude < 0) 673 { 674 lonHemisphere = 'W'; 675 longitude *= -1.0; 676 } 677 else 678 { 679 lonHemisphere = 'E'; 680 } 681 682 latMinutes = fmod(latitude * 60.0 , 60.0); 683 lonMinutes = fmod(longitude * 60.0 , 60.0); 684 685 length = snprintf(pMarker, lengthRemaining, "%02d%09.6lf,%c,%03d%09.6lf,%c,", 686 (uint8_t)floor(latitude), latMinutes, latHemisphere, 687 (uint8_t)floor(longitude),lonMinutes, lonHemisphere); 688 } 689 else 690 { 691 length = snprintf(pMarker, lengthRemaining,",,,,"); 692 } 693 694 if (length < 0 || length >= lengthRemaining) 695 { 696 LOC_LOGE("NMEA Error in string formatting"); 697 return; 698 } 699 pMarker += length; 700 lengthRemaining -= length; 701 702 if (location.gpsLocation.flags & LOC_GPS_LOCATION_HAS_SPEED) 703 { 704 float speedKnots = location.gpsLocation.speed * (3600.0/1852.0); 705 length = snprintf(pMarker, lengthRemaining, "%.1lf,", speedKnots); 706 } 707 else 708 { 709 length = snprintf(pMarker, lengthRemaining, ","); 710 } 711 712 if (length < 0 || length >= lengthRemaining) 713 { 714 LOC_LOGE("NMEA Error in string formatting"); 715 return; 716 } 717 pMarker += length; 718 lengthRemaining -= length; 719 720 if (location.gpsLocation.flags & LOC_GPS_LOCATION_HAS_BEARING) 721 { 722 length = snprintf(pMarker, lengthRemaining, "%.1lf,", location.gpsLocation.bearing); 723 } 724 else 725 { 726 length = snprintf(pMarker, lengthRemaining, ","); 727 } 728 729 if (length < 0 || length >= lengthRemaining) 730 { 731 LOC_LOGE("NMEA Error in string formatting"); 732 return; 733 } 734 pMarker += length; 735 lengthRemaining -= length; 736 737 length = snprintf(pMarker, lengthRemaining, "%2.2d%2.2d%2.2d,", 738 utcDay, utcMonth, utcYear); 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 if (locationExtended.flags & GPS_LOCATION_EXTENDED_HAS_MAG_DEV) 749 { 750 float magneticVariation = locationExtended.magneticDeviation; 751 char direction; 752 if (magneticVariation < 0.0) 753 { 754 direction = 'W'; 755 magneticVariation *= -1.0; 756 } 757 else 758 { 759 direction = 'E'; 760 } 761 762 length = snprintf(pMarker, lengthRemaining, "%.1lf,%c,", 763 magneticVariation, direction); 764 } 765 else 766 { 767 length = snprintf(pMarker, lengthRemaining, ",,"); 768 } 769 770 if (length < 0 || length >= lengthRemaining) 771 { 772 LOC_LOGE("NMEA Error in string formatting"); 773 return; 774 } 775 pMarker += length; 776 lengthRemaining -= length; 777 778 if (!(location.gpsLocation.flags & LOC_GPS_LOCATION_HAS_LAT_LONG)) 779 // N means no fix 780 length = snprintf(pMarker, lengthRemaining, "%c", 'N'); 781 else if (LOC_NAV_MASK_SBAS_CORRECTION_IONO & locationExtended.navSolutionMask) 782 // D means differential 783 length = snprintf(pMarker, lengthRemaining, "%c", 'D'); 784 else if (LOC_POS_TECH_MASK_SENSORS == locationExtended.tech_mask) 785 // E means estimated (dead reckoning) 786 length = snprintf(pMarker, lengthRemaining, "%c", 'E'); 787 else // A means autonomous 788 length = snprintf(pMarker, lengthRemaining, "%c", 'A'); 789 790 length = loc_nmea_put_checksum(sentence, sizeof(sentence)); 791 nmeaArraystr.push_back(sentence); 792 793 // ------------------- 794 // ------$--GGA------- 795 // ------------------- 796 797 pMarker = sentence; 798 lengthRemaining = sizeof(sentence); 799 800 length = snprintf(pMarker, lengthRemaining, "$%sGGA,%02d%02d%02d.%02d," , 801 talker, utcHours, utcMinutes, utcSeconds, utcMSeconds/10); 802 803 if (length < 0 || length >= lengthRemaining) 804 { 805 LOC_LOGE("NMEA Error in string formatting"); 806 return; 807 } 808 pMarker += length; 809 lengthRemaining -= length; 810 811 if (location.gpsLocation.flags & LOC_GPS_LOCATION_HAS_LAT_LONG) 812 { 813 double latitude = location.gpsLocation.latitude; 814 double longitude = location.gpsLocation.longitude; 815 char latHemisphere; 816 char lonHemisphere; 817 double latMinutes; 818 double lonMinutes; 819 820 if (latitude > 0) 821 { 822 latHemisphere = 'N'; 823 } 824 else 825 { 826 latHemisphere = 'S'; 827 latitude *= -1.0; 828 } 829 830 if (longitude < 0) 831 { 832 lonHemisphere = 'W'; 833 longitude *= -1.0; 834 } 835 else 836 { 837 lonHemisphere = 'E'; 838 } 839 840 latMinutes = fmod(latitude * 60.0 , 60.0); 841 lonMinutes = fmod(longitude * 60.0 , 60.0); 842 843 length = snprintf(pMarker, lengthRemaining, "%02d%09.6lf,%c,%03d%09.6lf,%c,", 844 (uint8_t)floor(latitude), latMinutes, latHemisphere, 845 (uint8_t)floor(longitude),lonMinutes, lonHemisphere); 846 } 847 else 848 { 849 length = snprintf(pMarker, lengthRemaining,",,,,"); 850 } 851 852 if (length < 0 || length >= lengthRemaining) 853 { 854 LOC_LOGE("NMEA Error in string formatting"); 855 return; 856 } 857 pMarker += length; 858 lengthRemaining -= length; 859 860 char gpsQuality; 861 if (!(location.gpsLocation.flags & LOC_GPS_LOCATION_HAS_LAT_LONG)) 862 gpsQuality = '0'; // 0 means no fix 863 else if (LOC_NAV_MASK_SBAS_CORRECTION_IONO & locationExtended.navSolutionMask) 864 gpsQuality = '2'; // 2 means DGPS fix 865 else if (LOC_POS_TECH_MASK_SENSORS == locationExtended.tech_mask) 866 gpsQuality = '6'; // 6 means estimated (dead reckoning) 867 else 868 gpsQuality = '1'; // 1 means GPS fix 869 870 // Number of satellites in use, 00-12 871 if (svUsedCount > MAX_SATELLITES_IN_USE) 872 svUsedCount = MAX_SATELLITES_IN_USE; 873 if (locationExtended.flags & GPS_LOCATION_EXTENDED_HAS_DOP) 874 { 875 length = snprintf(pMarker, lengthRemaining, "%c,%02d,%.1f,", 876 gpsQuality, svUsedCount, locationExtended.hdop); 877 } 878 else 879 { // no hdop 880 length = snprintf(pMarker, lengthRemaining, "%c,%02d,,", 881 gpsQuality, svUsedCount); 882 } 883 884 if (length < 0 || length >= lengthRemaining) 885 { 886 LOC_LOGE("NMEA Error in string formatting"); 887 return; 888 } 889 pMarker += length; 890 lengthRemaining -= length; 891 892 if (locationExtended.flags & GPS_LOCATION_EXTENDED_HAS_ALTITUDE_MEAN_SEA_LEVEL) 893 { 894 length = snprintf(pMarker, lengthRemaining, "%.1lf,M,", 895 locationExtended.altitudeMeanSeaLevel); 896 } 897 else 898 { 899 length = snprintf(pMarker, lengthRemaining,",,"); 900 } 901 902 if (length < 0 || length >= lengthRemaining) 903 { 904 LOC_LOGE("NMEA Error in string formatting"); 905 return; 906 } 907 pMarker += length; 908 lengthRemaining -= length; 909 910 if ((location.gpsLocation.flags & LOC_GPS_LOCATION_HAS_ALTITUDE) && 911 (locationExtended.flags & GPS_LOCATION_EXTENDED_HAS_ALTITUDE_MEAN_SEA_LEVEL)) 912 { 913 length = snprintf(pMarker, lengthRemaining, "%.1lf,M,,", 914 location.gpsLocation.altitude - locationExtended.altitudeMeanSeaLevel); 915 } 916 else 917 { 918 length = snprintf(pMarker, lengthRemaining,",,,"); 919 } 920 921 length = loc_nmea_put_checksum(sentence, sizeof(sentence)); 922 nmeaArraystr.push_back(sentence); 923 924 // clear the cache so they can't be used again 925 sv_cache_info.gps_used_mask = 0; 926 sv_cache_info.glo_used_mask = 0; 927 sv_cache_info.gal_used_mask = 0; 928 sv_cache_info.qzss_used_mask = 0; 929 sv_cache_info.bds_used_mask = 0; 930 } 931 //Send blank NMEA reports for non-final fixes 932 else { 933 strlcpy(sentence, "$GPGSA,A,1,,,,,,,,,,,,,,,", sizeof(sentence)); 934 length = loc_nmea_put_checksum(sentence, sizeof(sentence)); 935 nmeaArraystr.push_back(sentence); 936 937 strlcpy(sentence, "$GNGSA,A,1,,,,,,,,,,,,,,,", sizeof(sentence)); 938 length = loc_nmea_put_checksum(sentence, sizeof(sentence)); 939 nmeaArraystr.push_back(sentence); 940 941 strlcpy(sentence, "$PQGSA,A,1,,,,,,,,,,,,,,,", sizeof(sentence)); 942 length = loc_nmea_put_checksum(sentence, sizeof(sentence)); 943 nmeaArraystr.push_back(sentence); 944 945 strlcpy(sentence, "$GPVTG,,T,,M,,N,,K,N", sizeof(sentence)); 946 length = loc_nmea_put_checksum(sentence, sizeof(sentence)); 947 nmeaArraystr.push_back(sentence); 948 949 strlcpy(sentence, "$GPRMC,,V,,,,,,,,,,N", sizeof(sentence)); 950 length = loc_nmea_put_checksum(sentence, sizeof(sentence)); 951 nmeaArraystr.push_back(sentence); 952 953 strlcpy(sentence, "$GPGGA,,,,,,0,,,,,,,,", sizeof(sentence)); 954 length = loc_nmea_put_checksum(sentence, sizeof(sentence)); 955 nmeaArraystr.push_back(sentence); 956 } 957 958 EXIT_LOG(%d, 0); 959 } 960 961 962 963 /*=========================================================================== 964 FUNCTION loc_nmea_generate_sv 965 966 DESCRIPTION 967 Generate NMEA sentences generated based on sv report 968 969 DEPENDENCIES 970 NONE 971 972 RETURN VALUE 973 0 974 975 SIDE EFFECTS 976 N/A 977 978 ===========================================================================*/ 979 void loc_nmea_generate_sv(const GnssSvNotification &svNotify, 980 std::vector<std::string> &nmeaArraystr) 981 { 982 ENTRY_LOG(); 983 984 char sentence[NMEA_SENTENCE_MAX_LENGTH] = {0}; 985 char* pMarker = sentence; 986 int lengthRemaining = sizeof(sentence); 987 int length = 0; 988 int svCount = svNotify.count; 989 int sentenceCount = 0; 990 int sentenceNumber = 1; 991 int svNumber = 1; 992 993 //Count GPS SVs for saparating GPS from GLONASS and throw others 994 995 sv_cache_info.gps_used_mask = 0; 996 sv_cache_info.glo_used_mask = 0; 997 sv_cache_info.gal_used_mask = 0; 998 sv_cache_info.qzss_used_mask = 0; 999 sv_cache_info.bds_used_mask = 0; 1000 1001 sv_cache_info.gps_count = 0; 1002 sv_cache_info.glo_count = 0; 1003 sv_cache_info.gal_count = 0; 1004 sv_cache_info.qzss_count = 0; 1005 sv_cache_info.bds_count = 0; 1006 for(svNumber=1; svNumber <= svCount; svNumber++) { 1007 if (GNSS_SV_TYPE_GPS == svNotify.gnssSvs[svNumber - 1].type) 1008 { 1009 // cache the used in fix mask, as it will be needed to send $GPGSA 1010 // during the position report 1011 if (GNSS_SV_OPTIONS_USED_IN_FIX_BIT == 1012 (svNotify.gnssSvs[svNumber - 1].gnssSvOptionsMask & 1013 GNSS_SV_OPTIONS_USED_IN_FIX_BIT)) 1014 { 1015 sv_cache_info.gps_used_mask |= (1 << (svNotify.gnssSvs[svNumber - 1].svId - 1)); 1016 } 1017 sv_cache_info.gps_count++; 1018 } 1019 else if (GNSS_SV_TYPE_GLONASS == svNotify.gnssSvs[svNumber - 1].type) 1020 { 1021 // cache the used in fix mask, as it will be needed to send $GNGSA 1022 // during the position report 1023 if (GNSS_SV_OPTIONS_USED_IN_FIX_BIT == 1024 (svNotify.gnssSvs[svNumber - 1].gnssSvOptionsMask & 1025 GNSS_SV_OPTIONS_USED_IN_FIX_BIT)) 1026 { 1027 sv_cache_info.glo_used_mask |= (1 << (svNotify.gnssSvs[svNumber - 1].svId - 1)); 1028 } 1029 sv_cache_info.glo_count++; 1030 } 1031 else if (GNSS_SV_TYPE_GALILEO == svNotify.gnssSvs[svNumber - 1].type) 1032 { 1033 // cache the used in fix mask, as it will be needed to send $GAGSA 1034 // during the position report 1035 if (GNSS_SV_OPTIONS_USED_IN_FIX_BIT == 1036 (svNotify.gnssSvs[svNumber - 1].gnssSvOptionsMask & 1037 GNSS_SV_OPTIONS_USED_IN_FIX_BIT)) 1038 { 1039 sv_cache_info.gal_used_mask |= (1 << (svNotify.gnssSvs[svNumber - 1].svId - 1)); 1040 } 1041 sv_cache_info.gal_count++; 1042 } 1043 else if (GNSS_SV_TYPE_QZSS == svNotify.gnssSvs[svNumber - 1].type) 1044 { 1045 // cache the used in fix mask, as it will be needed to send $PQGSA 1046 // during the position report 1047 if (GNSS_SV_OPTIONS_USED_IN_FIX_BIT == 1048 (svNotify.gnssSvs[svNumber - 1].gnssSvOptionsMask & 1049 GNSS_SV_OPTIONS_USED_IN_FIX_BIT)) 1050 { 1051 sv_cache_info.qzss_used_mask |= (1 << (svNotify.gnssSvs[svNumber - 1].svId - 1)); 1052 } 1053 sv_cache_info.qzss_count++; 1054 } 1055 else if (GNSS_SV_TYPE_BEIDOU == svNotify.gnssSvs[svNumber - 1].type) 1056 { 1057 // cache the used in fix mask, as it will be needed to send $PQGSA 1058 // during the position report 1059 if (GNSS_SV_OPTIONS_USED_IN_FIX_BIT == 1060 (svNotify.gnssSvs[svNumber - 1].gnssSvOptionsMask & 1061 GNSS_SV_OPTIONS_USED_IN_FIX_BIT)) 1062 { 1063 sv_cache_info.bds_used_mask |= (1 << (svNotify.gnssSvs[svNumber - 1].svId - 1)); 1064 } 1065 sv_cache_info.bds_count++; 1066 } 1067 } 1068 1069 loc_nmea_sv_meta sv_meta; 1070 // ------------------ 1071 // ------$GPGSV------ 1072 // ------------------ 1073 1074 loc_nmea_generate_GSV(svNotify, sentence, sizeof(sentence), 1075 loc_nmea_sv_meta_init(sv_meta, GNSS_SV_TYPE_GPS, false), nmeaArraystr); 1076 1077 // ------------------ 1078 // ------$GLGSV------ 1079 // ------------------ 1080 1081 loc_nmea_generate_GSV(svNotify, sentence, sizeof(sentence), 1082 loc_nmea_sv_meta_init(sv_meta, GNSS_SV_TYPE_GLONASS, false), nmeaArraystr); 1083 1084 // ------------------ 1085 // ------$GAGSV------ 1086 // ------------------ 1087 1088 loc_nmea_generate_GSV(svNotify, sentence, sizeof(sentence), 1089 loc_nmea_sv_meta_init(sv_meta, GNSS_SV_TYPE_GALILEO, false), nmeaArraystr); 1090 1091 // ------------------------- 1092 // ------$PQGSV (QZSS)------ 1093 // ------------------------- 1094 1095 loc_nmea_generate_GSV(svNotify, sentence, sizeof(sentence), 1096 loc_nmea_sv_meta_init(sv_meta, GNSS_SV_TYPE_QZSS, false), nmeaArraystr); 1097 1098 // --------------------------- 1099 // ------$PQGSV (BEIDOU)------ 1100 // --------------------------- 1101 1102 loc_nmea_generate_GSV(svNotify, sentence, sizeof(sentence), 1103 loc_nmea_sv_meta_init(sv_meta, GNSS_SV_TYPE_BEIDOU, false), nmeaArraystr); 1104 1105 EXIT_LOG(%d, 0); 1106 } 1107