/* Copyright (c) 2012, The Linux Foundation. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of The Linux Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #define LOG_NDDEBUG 0 #define LOG_TAG "LocSvc_eng_nmea" #define GPS_PRN_START 1 #define GPS_PRN_END 32 #define GLONASS_PRN_START 65 #define GLONASS_PRN_END 96 #include #include #include #include "log_util.h" /*=========================================================================== FUNCTION loc_eng_nmea_send DESCRIPTION send out NMEA sentence DEPENDENCIES NONE RETURN VALUE Total length of the nmea sentence SIDE EFFECTS N/A ===========================================================================*/ void loc_eng_nmea_send(char *pNmea, int length, loc_eng_data_s_type *loc_eng_data_p) { struct timeval tv; gettimeofday(&tv, (struct timezone *) NULL); int64_t now = tv.tv_sec * 1000LL + tv.tv_usec / 1000; CALLBACK_LOG_CALLFLOW("nmea_cb", %p, pNmea); if (loc_eng_data_p->nmea_cb != NULL) loc_eng_data_p->nmea_cb(now, pNmea, length); LOC_LOGD("NMEA <%s", pNmea); } /*=========================================================================== FUNCTION loc_eng_nmea_put_checksum DESCRIPTION Generate NMEA sentences generated based on position report DEPENDENCIES NONE RETURN VALUE Total length of the nmea sentence SIDE EFFECTS N/A ===========================================================================*/ int loc_eng_nmea_put_checksum(char *pNmea, int maxSize) { uint8_t checksum = 0; int length = 0; pNmea++; //skip the $ while (*pNmea != '\0') { checksum ^= *pNmea++; length++; } int checksumLength = snprintf(pNmea,(maxSize-length-1),"*%02X\r\n", checksum); return (length + checksumLength); } /*=========================================================================== FUNCTION loc_eng_nmea_generate_pos DESCRIPTION Generate NMEA sentences generated based on position report DEPENDENCIES NONE RETURN VALUE 0 SIDE EFFECTS N/A ===========================================================================*/ void loc_eng_nmea_generate_pos(loc_eng_data_s_type *loc_eng_data_p, const UlpLocation &location, const GpsLocationExtended &locationExtended, unsigned char generate_nmea) { ENTRY_LOG(); time_t utcTime(location.gpsLocation.timestamp/1000); tm * pTm = gmtime(&utcTime); if (NULL == pTm) { LOC_LOGE("gmtime failed"); return; } char sentence[NMEA_SENTENCE_MAX_LENGTH] = {0}; char* pMarker = sentence; int lengthRemaining = sizeof(sentence); int length = 0; int utcYear = pTm->tm_year % 100; // 2 digit year int utcMonth = pTm->tm_mon + 1; // tm_mon starts at zero int utcDay = pTm->tm_mday; int utcHours = pTm->tm_hour; int utcMinutes = pTm->tm_min; int utcSeconds = pTm->tm_sec; if (generate_nmea) { // ------------------ // ------$GPGSA------ // ------------------ uint32_t svUsedCount = 0; uint32_t svUsedList[32] = {0}; uint32_t mask = loc_eng_data_p->sv_used_mask; for (uint8_t i = 1; mask > 0 && svUsedCount < 32; i++) { if (mask & 1) svUsedList[svUsedCount++] = i; mask = mask >> 1; } // clear the cache so they can't be used again loc_eng_data_p->sv_used_mask = 0; char fixType; if (svUsedCount == 0) fixType = '1'; // no fix else if (svUsedCount <= 3) fixType = '2'; // 2D fix else fixType = '3'; // 3D fix length = snprintf(pMarker, lengthRemaining, "$GPGSA,A,%c,", fixType); if (length < 0 || length >= lengthRemaining) { LOC_LOGE("NMEA Error in string formatting"); return; } pMarker += length; lengthRemaining -= length; for (uint8_t i = 0; i < 12; i++) // only the first 12 sv go in sentence { if (i < svUsedCount) length = snprintf(pMarker, lengthRemaining, "%02d,", svUsedList[i]); else length = snprintf(pMarker, lengthRemaining, ","); if (length < 0 || length >= lengthRemaining) { LOC_LOGE("NMEA Error in string formatting"); return; } pMarker += length; lengthRemaining -= length; } if (locationExtended.flags & GPS_LOCATION_EXTENDED_HAS_DOP) { // dop is in locationExtended, (QMI) length = snprintf(pMarker, lengthRemaining, "%.1f,%.1f,%.1f", locationExtended.pdop, locationExtended.hdop, locationExtended.vdop); } else if (loc_eng_data_p->pdop > 0 && loc_eng_data_p->hdop > 0 && loc_eng_data_p->vdop > 0) { // dop was cached from sv report (RPC) length = snprintf(pMarker, lengthRemaining, "%.1f,%.1f,%.1f", loc_eng_data_p->pdop, loc_eng_data_p->hdop, loc_eng_data_p->vdop); } else { // no dop length = snprintf(pMarker, lengthRemaining, ",,"); } length = loc_eng_nmea_put_checksum(sentence, sizeof(sentence)); loc_eng_nmea_send(sentence, length, loc_eng_data_p); // ------------------ // ------$GPVTG------ // ------------------ pMarker = sentence; lengthRemaining = sizeof(sentence); if (location.gpsLocation.flags & GPS_LOCATION_HAS_BEARING) { float magTrack = location.gpsLocation.bearing; if (locationExtended.flags & GPS_LOCATION_EXTENDED_HAS_MAG_DEV) { float magTrack = location.gpsLocation.bearing - locationExtended.magneticDeviation; if (magTrack < 0.0) magTrack += 360.0; else if (magTrack > 360.0) magTrack -= 360.0; } length = snprintf(pMarker, lengthRemaining, "$GPVTG,%.1lf,T,%.1lf,M,", location.gpsLocation.bearing, magTrack); } else { length = snprintf(pMarker, lengthRemaining, "$GPVTG,,T,,M,"); } if (length < 0 || length >= lengthRemaining) { LOC_LOGE("NMEA Error in string formatting"); return; } pMarker += length; lengthRemaining -= length; if (location.gpsLocation.flags & GPS_LOCATION_HAS_SPEED) { float speedKnots = location.gpsLocation.speed * (3600.0/1852.0); float speedKmPerHour = location.gpsLocation.speed * 3.6; length = snprintf(pMarker, lengthRemaining, "%.1lf,N,%.1lf,K,", speedKnots, speedKmPerHour); } else { length = snprintf(pMarker, lengthRemaining, ",N,,K,"); } if (length < 0 || length >= lengthRemaining) { LOC_LOGE("NMEA Error in string formatting"); return; } pMarker += length; lengthRemaining -= length; if (!(location.gpsLocation.flags & GPS_LOCATION_HAS_LAT_LONG)) length = snprintf(pMarker, lengthRemaining, "%c", 'N'); // N means no fix else if (LOC_POSITION_MODE_STANDALONE == loc_eng_data_p->adapter->getPositionMode().mode) length = snprintf(pMarker, lengthRemaining, "%c", 'A'); // A means autonomous else length = snprintf(pMarker, lengthRemaining, "%c", 'D'); // D means differential length = loc_eng_nmea_put_checksum(sentence, sizeof(sentence)); loc_eng_nmea_send(sentence, length, loc_eng_data_p); // ------------------ // ------$GPRMC------ // ------------------ pMarker = sentence; lengthRemaining = sizeof(sentence); length = snprintf(pMarker, lengthRemaining, "$GPRMC,%02d%02d%02d,A," , utcHours, utcMinutes, utcSeconds); if (length < 0 || length >= lengthRemaining) { LOC_LOGE("NMEA Error in string formatting"); return; } pMarker += length; lengthRemaining -= length; if (location.gpsLocation.flags & GPS_LOCATION_HAS_LAT_LONG) { double latitude = location.gpsLocation.latitude; double longitude = location.gpsLocation.longitude; char latHemisphere; char lonHemisphere; double latMinutes; double lonMinutes; if (latitude > 0) { latHemisphere = 'N'; } else { latHemisphere = 'S'; latitude *= -1.0; } if (longitude < 0) { lonHemisphere = 'W'; longitude *= -1.0; } else { lonHemisphere = 'E'; } latMinutes = fmod(latitude * 60.0 , 60.0); lonMinutes = fmod(longitude * 60.0 , 60.0); length = snprintf(pMarker, lengthRemaining, "%02d%09.6lf,%c,%03d%09.6lf,%c,", (uint8_t)floor(latitude), latMinutes, latHemisphere, (uint8_t)floor(longitude),lonMinutes, lonHemisphere); } else { length = snprintf(pMarker, lengthRemaining,",,,,"); } if (length < 0 || length >= lengthRemaining) { LOC_LOGE("NMEA Error in string formatting"); return; } pMarker += length; lengthRemaining -= length; if (location.gpsLocation.flags & GPS_LOCATION_HAS_SPEED) { float speedKnots = location.gpsLocation.speed * (3600.0/1852.0); length = snprintf(pMarker, lengthRemaining, "%.1lf,", speedKnots); } else { length = snprintf(pMarker, lengthRemaining, ","); } if (length < 0 || length >= lengthRemaining) { LOC_LOGE("NMEA Error in string formatting"); return; } pMarker += length; lengthRemaining -= length; if (location.gpsLocation.flags & GPS_LOCATION_HAS_BEARING) { length = snprintf(pMarker, lengthRemaining, "%.1lf,", location.gpsLocation.bearing); } else { length = snprintf(pMarker, lengthRemaining, ","); } if (length < 0 || length >= lengthRemaining) { LOC_LOGE("NMEA Error in string formatting"); return; } pMarker += length; lengthRemaining -= length; length = snprintf(pMarker, lengthRemaining, "%2.2d%2.2d%2.2d,", utcDay, utcMonth, utcYear); if (length < 0 || length >= lengthRemaining) { LOC_LOGE("NMEA Error in string formatting"); return; } pMarker += length; lengthRemaining -= length; if (locationExtended.flags & GPS_LOCATION_EXTENDED_HAS_MAG_DEV) { float magneticVariation = locationExtended.magneticDeviation; char direction; if (magneticVariation < 0.0) { direction = 'W'; magneticVariation *= -1.0; } else { direction = 'E'; } length = snprintf(pMarker, lengthRemaining, "%.1lf,%c,", magneticVariation, direction); } else { length = snprintf(pMarker, lengthRemaining, ",,"); } if (length < 0 || length >= lengthRemaining) { LOC_LOGE("NMEA Error in string formatting"); return; } pMarker += length; lengthRemaining -= length; if (!(location.gpsLocation.flags & GPS_LOCATION_HAS_LAT_LONG)) length = snprintf(pMarker, lengthRemaining, "%c", 'N'); // N means no fix else if (LOC_POSITION_MODE_STANDALONE == loc_eng_data_p->adapter->getPositionMode().mode) length = snprintf(pMarker, lengthRemaining, "%c", 'A'); // A means autonomous else length = snprintf(pMarker, lengthRemaining, "%c", 'D'); // D means differential length = loc_eng_nmea_put_checksum(sentence, sizeof(sentence)); loc_eng_nmea_send(sentence, length, loc_eng_data_p); // ------------------ // ------$GPGGA------ // ------------------ pMarker = sentence; lengthRemaining = sizeof(sentence); length = snprintf(pMarker, lengthRemaining, "$GPGGA,%02d%02d%02d," , utcHours, utcMinutes, utcSeconds); if (length < 0 || length >= lengthRemaining) { LOC_LOGE("NMEA Error in string formatting"); return; } pMarker += length; lengthRemaining -= length; if (location.gpsLocation.flags & GPS_LOCATION_HAS_LAT_LONG) { double latitude = location.gpsLocation.latitude; double longitude = location.gpsLocation.longitude; char latHemisphere; char lonHemisphere; double latMinutes; double lonMinutes; if (latitude > 0) { latHemisphere = 'N'; } else { latHemisphere = 'S'; latitude *= -1.0; } if (longitude < 0) { lonHemisphere = 'W'; longitude *= -1.0; } else { lonHemisphere = 'E'; } latMinutes = fmod(latitude * 60.0 , 60.0); lonMinutes = fmod(longitude * 60.0 , 60.0); length = snprintf(pMarker, lengthRemaining, "%02d%09.6lf,%c,%03d%09.6lf,%c,", (uint8_t)floor(latitude), latMinutes, latHemisphere, (uint8_t)floor(longitude),lonMinutes, lonHemisphere); } else { length = snprintf(pMarker, lengthRemaining,",,,,"); } if (length < 0 || length >= lengthRemaining) { LOC_LOGE("NMEA Error in string formatting"); return; } pMarker += length; lengthRemaining -= length; char gpsQuality; if (!(location.gpsLocation.flags & GPS_LOCATION_HAS_LAT_LONG)) gpsQuality = '0'; // 0 means no fix else if (LOC_POSITION_MODE_STANDALONE == loc_eng_data_p->adapter->getPositionMode().mode) gpsQuality = '1'; // 1 means GPS fix else gpsQuality = '2'; // 2 means DGPS fix if (locationExtended.flags & GPS_LOCATION_EXTENDED_HAS_DOP) { // dop is in locationExtended, (QMI) length = snprintf(pMarker, lengthRemaining, "%c,%02d,%.1f,", gpsQuality, svUsedCount, locationExtended.hdop); } else if (loc_eng_data_p->pdop > 0 && loc_eng_data_p->hdop > 0 && loc_eng_data_p->vdop > 0) { // dop was cached from sv report (RPC) length = snprintf(pMarker, lengthRemaining, "%c,%02d,%.1f,", gpsQuality, svUsedCount, loc_eng_data_p->hdop); } else { // no hdop length = snprintf(pMarker, lengthRemaining, "%c,%02d,,", gpsQuality, svUsedCount); } if (length < 0 || length >= lengthRemaining) { LOC_LOGE("NMEA Error in string formatting"); return; } pMarker += length; lengthRemaining -= length; if (locationExtended.flags & GPS_LOCATION_EXTENDED_HAS_ALTITUDE_MEAN_SEA_LEVEL) { length = snprintf(pMarker, lengthRemaining, "%.1lf,M,", locationExtended.altitudeMeanSeaLevel); } else { length = snprintf(pMarker, lengthRemaining,",,"); } if (length < 0 || length >= lengthRemaining) { LOC_LOGE("NMEA Error in string formatting"); return; } pMarker += length; lengthRemaining -= length; if ((location.gpsLocation.flags & GPS_LOCATION_HAS_ALTITUDE) && (locationExtended.flags & GPS_LOCATION_EXTENDED_HAS_ALTITUDE_MEAN_SEA_LEVEL)) { length = snprintf(pMarker, lengthRemaining, "%.1lf,M,,", location.gpsLocation.altitude - locationExtended.altitudeMeanSeaLevel); } else { length = snprintf(pMarker, lengthRemaining,",,,"); } length = loc_eng_nmea_put_checksum(sentence, sizeof(sentence)); loc_eng_nmea_send(sentence, length, loc_eng_data_p); } //Send blank NMEA reports for non-final fixes else { strlcpy(sentence, "$GPGSA,A,1,,,,,,,,,,,,,,,", sizeof(sentence)); length = loc_eng_nmea_put_checksum(sentence, sizeof(sentence)); loc_eng_nmea_send(sentence, length, loc_eng_data_p); strlcpy(sentence, "$GPVTG,,T,,M,,N,,K,N", sizeof(sentence)); length = loc_eng_nmea_put_checksum(sentence, sizeof(sentence)); loc_eng_nmea_send(sentence, length, loc_eng_data_p); strlcpy(sentence, "$GPRMC,,V,,,,,,,,,,N", sizeof(sentence)); length = loc_eng_nmea_put_checksum(sentence, sizeof(sentence)); loc_eng_nmea_send(sentence, length, loc_eng_data_p); strlcpy(sentence, "$GPGGA,,,,,,0,,,,,,,,", sizeof(sentence)); length = loc_eng_nmea_put_checksum(sentence, sizeof(sentence)); loc_eng_nmea_send(sentence, length, loc_eng_data_p); } // clear the dop cache so they can't be used again loc_eng_data_p->pdop = 0; loc_eng_data_p->hdop = 0; loc_eng_data_p->vdop = 0; EXIT_LOG(%d, 0); } /*=========================================================================== FUNCTION loc_eng_nmea_generate_sv DESCRIPTION Generate NMEA sentences generated based on sv report DEPENDENCIES NONE RETURN VALUE 0 SIDE EFFECTS N/A ===========================================================================*/ void loc_eng_nmea_generate_sv(loc_eng_data_s_type *loc_eng_data_p, const GpsSvStatus &svStatus, const GpsLocationExtended &locationExtended) { ENTRY_LOG(); char sentence[NMEA_SENTENCE_MAX_LENGTH] = {0}; char* pMarker = sentence; int lengthRemaining = sizeof(sentence); int length = 0; int svCount = svStatus.num_svs; int sentenceCount = 0; int sentenceNumber = 1; int svNumber = 1; int gpsCount = 0; int glnCount = 0; //Count GPS SVs for saparating GPS from GLONASS and throw others for(svNumber=1; svNumber <= svCount; svNumber++) { if( (svStatus.sv_list[svNumber-1].prn >= GPS_PRN_START)&& (svStatus.sv_list[svNumber-1].prn <= GPS_PRN_END) ) { gpsCount++; } else if( (svStatus.sv_list[svNumber-1].prn >= GLONASS_PRN_START) && (svStatus.sv_list[svNumber-1].prn <= GLONASS_PRN_END) ) { glnCount++; } } // ------------------ // ------$GPGSV------ // ------------------ if (gpsCount <= 0) { // no svs in view, so just send a blank $GPGSV sentence strlcpy(sentence, "$GPGSV,1,1,0,", sizeof(sentence)); length = loc_eng_nmea_put_checksum(sentence, sizeof(sentence)); loc_eng_nmea_send(sentence, length, loc_eng_data_p); } else { svNumber = 1; sentenceNumber = 1; sentenceCount = gpsCount/4 + (gpsCount % 4 != 0); while (sentenceNumber <= sentenceCount) { pMarker = sentence; lengthRemaining = sizeof(sentence); length = snprintf(pMarker, lengthRemaining, "$GPGSV,%d,%d,%02d", sentenceCount, sentenceNumber, gpsCount); if (length < 0 || length >= lengthRemaining) { LOC_LOGE("NMEA Error in string formatting"); return; } pMarker += length; lengthRemaining -= length; for (int i=0; (svNumber <= svCount) && (i < 4); svNumber++) { if( (svStatus.sv_list[svNumber-1].prn >= GPS_PRN_START) && (svStatus.sv_list[svNumber-1].prn <= GPS_PRN_END) ) { length = snprintf(pMarker, lengthRemaining,",%02d,%02d,%03d,", svStatus.sv_list[svNumber-1].prn, (int)(0.5 + svStatus.sv_list[svNumber-1].elevation), //float to int (int)(0.5 + svStatus.sv_list[svNumber-1].azimuth)); //float to int if (length < 0 || length >= lengthRemaining) { LOC_LOGE("NMEA Error in string formatting"); return; } pMarker += length; lengthRemaining -= length; if (svStatus.sv_list[svNumber-1].snr > 0) { length = snprintf(pMarker, lengthRemaining,"%02d", (int)(0.5 + svStatus.sv_list[svNumber-1].snr)); //float to int if (length < 0 || length >= lengthRemaining) { LOC_LOGE("NMEA Error in string formatting"); return; } pMarker += length; lengthRemaining -= length; } i++; } } length = loc_eng_nmea_put_checksum(sentence, sizeof(sentence)); loc_eng_nmea_send(sentence, length, loc_eng_data_p); sentenceNumber++; } //while } //if // ------------------ // ------$GLGSV------ // ------------------ if (glnCount <= 0) { // no svs in view, so just send a blank $GLGSV sentence strlcpy(sentence, "$GLGSV,1,1,0,", sizeof(sentence)); length = loc_eng_nmea_put_checksum(sentence, sizeof(sentence)); loc_eng_nmea_send(sentence, length, loc_eng_data_p); } else { svNumber = 1; sentenceNumber = 1; sentenceCount = glnCount/4 + (glnCount % 4 != 0); while (sentenceNumber <= sentenceCount) { pMarker = sentence; lengthRemaining = sizeof(sentence); length = snprintf(pMarker, lengthRemaining, "$GLGSV,%d,%d,%02d", sentenceCount, sentenceNumber, glnCount); if (length < 0 || length >= lengthRemaining) { LOC_LOGE("NMEA Error in string formatting"); return; } pMarker += length; lengthRemaining -= length; for (int i=0; (svNumber <= svCount) && (i < 4); svNumber++) { if( (svStatus.sv_list[svNumber-1].prn >= GLONASS_PRN_START) && (svStatus.sv_list[svNumber-1].prn <= GLONASS_PRN_END) ) { length = snprintf(pMarker, lengthRemaining,",%02d,%02d,%03d,", svStatus.sv_list[svNumber-1].prn, (int)(0.5 + svStatus.sv_list[svNumber-1].elevation), //float to int (int)(0.5 + svStatus.sv_list[svNumber-1].azimuth)); //float to int if (length < 0 || length >= lengthRemaining) { LOC_LOGE("NMEA Error in string formatting"); return; } pMarker += length; lengthRemaining -= length; if (svStatus.sv_list[svNumber-1].snr > 0) { length = snprintf(pMarker, lengthRemaining,"%02d", (int)(0.5 + svStatus.sv_list[svNumber-1].snr)); //float to int if (length < 0 || length >= lengthRemaining) { LOC_LOGE("NMEA Error in string formatting"); return; } pMarker += length; lengthRemaining -= length; } i++; } } length = loc_eng_nmea_put_checksum(sentence, sizeof(sentence)); loc_eng_nmea_send(sentence, length, loc_eng_data_p); sentenceNumber++; } //while }//if if (svStatus.used_in_fix_mask == 0) { // No sv used, so there will be no position report, so send // blank NMEA sentences strlcpy(sentence, "$GPGSA,A,1,,,,,,,,,,,,,,,", sizeof(sentence)); length = loc_eng_nmea_put_checksum(sentence, sizeof(sentence)); loc_eng_nmea_send(sentence, length, loc_eng_data_p); strlcpy(sentence, "$GPVTG,,T,,M,,N,,K,N", sizeof(sentence)); length = loc_eng_nmea_put_checksum(sentence, sizeof(sentence)); loc_eng_nmea_send(sentence, length, loc_eng_data_p); strlcpy(sentence, "$GPRMC,,V,,,,,,,,,,N", sizeof(sentence)); length = loc_eng_nmea_put_checksum(sentence, sizeof(sentence)); loc_eng_nmea_send(sentence, length, loc_eng_data_p); strlcpy(sentence, "$GPGGA,,,,,,0,,,,,,,,", sizeof(sentence)); length = loc_eng_nmea_put_checksum(sentence, sizeof(sentence)); loc_eng_nmea_send(sentence, length, loc_eng_data_p); } else { // cache the used in fix mask, as it will be needed to send $GPGSA // during the position report loc_eng_data_p->sv_used_mask = svStatus.used_in_fix_mask; // For RPC, the DOP are sent during sv report, so cache them // now to be sent during position report. // For QMI, the DOP will be in position report. if (locationExtended.flags & GPS_LOCATION_EXTENDED_HAS_DOP) { loc_eng_data_p->pdop = locationExtended.pdop; loc_eng_data_p->hdop = locationExtended.hdop; loc_eng_data_p->vdop = locationExtended.vdop; } else { loc_eng_data_p->pdop = 0; loc_eng_data_p->hdop = 0; loc_eng_data_p->vdop = 0; } } EXIT_LOG(%d, 0); }