From a3409617b658f1e732b154802d0a88dcee83912e Mon Sep 17 00:00:00 2001 From: Dante Russo Date: Tue, 7 Aug 2012 10:29:30 -0700 Subject: [PATCH] gps: AP NMEA generation Generate NMEA sentences on the AP Change-Id: Ifc9e9263868ab4dec450bef35c53c441c881a072 --- .../src/LocApiRpcAdapter.cpp | 32 +- loc_api/libloc_api_50001/Android.mk | 3 +- loc_api/libloc_api_50001/LocApiAdapter.cpp | 8 +- loc_api/libloc_api_50001/LocApiAdapter.h | 7 +- loc_api/libloc_api_50001/loc_eng.cpp | 26 +- loc_api/libloc_api_50001/loc_eng.h | 15 +- loc_api/libloc_api_50001/loc_eng_msg.h | 45 +- loc_api/libloc_api_50001/loc_eng_nmea.cpp | 703 ++++++++++++++++++ loc_api/libloc_api_50001/loc_eng_nmea.h | 42 ++ loc_api/loc_api_v02/LocApiV02Adapter.cpp | 32 +- 10 files changed, 896 insertions(+), 17 deletions(-) create mode 100644 loc_api/libloc_api_50001/loc_eng_nmea.cpp create mode 100644 loc_api/libloc_api_50001/loc_eng_nmea.h diff --git a/loc_api/libloc_api-rpc-50001/libloc_api-rpc-glue/src/LocApiRpcAdapter.cpp b/loc_api/libloc_api-rpc-50001/libloc_api-rpc-glue/src/LocApiRpcAdapter.cpp index 843a8ae9..5754a4c9 100644 --- a/loc_api/libloc_api-rpc-50001/libloc_api-rpc-glue/src/LocApiRpcAdapter.cpp +++ b/loc_api/libloc_api-rpc-50001/libloc_api-rpc-glue/src/LocApiRpcAdapter.cpp @@ -1,4 +1,4 @@ -/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2011-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 @@ -625,8 +625,10 @@ LocApiRpcAdapter::deleteAidingData(GpsAidingData bits) void LocApiRpcAdapter::reportPosition(const rpc_loc_parsed_position_s_type *location_report_ptr) { GpsLocation location = {0}; + GpsLocationExtended locationExtended = {0}; location.size = sizeof(location); + locationExtended.size = sizeof(locationExtended); if (location_report_ptr->valid_mask & RPC_LOC_POS_VALID_SESSION_STATUS) { // Process the position from final and intermediate reports @@ -681,8 +683,22 @@ void LocApiRpcAdapter::reportPosition(const rpc_loc_parsed_position_s_type *loca //Mark the location source as from GNSS location.flags |= LOCATION_HAS_SOURCE_INFO; location.position_source = ULP_LOCATION_IS_FROM_GNSS; + + if (location_report_ptr->valid_mask & RPC_LOC_POS_VALID_ALTITUDE_WRT_MEAN_SEA_LEVEL) + { + locationExtended.flags |= GPS_LOCATION_EXTENDED_HAS_MAG_DEV; + locationExtended.magneticDeviation = location_report_ptr->altitude_wrt_mean_sea_level; + } + + if (location_report_ptr->valid_mask & RPC_LOC_POS_VALID_MAGNETIC_VARIATION ) + { + locationExtended.flags |= GPS_LOCATION_EXTENDED_HAS_MAG_DEV; + locationExtended.magneticDeviation = location_report_ptr->magnetic_deviation; + } + LOC_LOGV("reportPosition: fire callback\n"); LocApiAdapter::reportPosition(location, + locationExtended, locEngHandle.extPosInfo((void*)location_report_ptr), (location_report_ptr->session_status == RPC_LOC_SESS_STATUS_IN_PROGESS ? LOC_SESS_INTERMEDIATE : LOC_SESS_SUCCESS)); @@ -691,6 +707,7 @@ void LocApiRpcAdapter::reportPosition(const rpc_loc_parsed_position_s_type *loca else { LocApiAdapter::reportPosition(location, + locationExtended, NULL, LOC_SESS_FAILURE); LOC_LOGV("loc_eng_report_position: ignore position report when session status = %d\n", location_report_ptr->session_status); @@ -705,6 +722,8 @@ void LocApiRpcAdapter::reportPosition(const rpc_loc_parsed_position_s_type *loca void LocApiRpcAdapter::reportSv(const rpc_loc_gnss_info_s_type *gnss_report_ptr) { GpsSvStatus SvStatus = {0}; + GpsLocationExtended locationExtended = {0}; + locationExtended.size = sizeof(locationExtended); int num_svs_max = 0; const rpc_loc_sv_info_s_type *sv_info_ptr; @@ -788,9 +807,20 @@ void LocApiRpcAdapter::reportSv(const rpc_loc_gnss_info_s_type *gnss_report_ptr) } } + if ((gnss_report_ptr->valid_mask & RPC_LOC_GNSS_INFO_VALID_POS_DOP) && + (gnss_report_ptr->valid_mask & RPC_LOC_GNSS_INFO_VALID_HOR_DOP) && + (gnss_report_ptr->valid_mask & RPC_LOC_GNSS_INFO_VALID_VERT_DOP)) + { + locationExtended.flags |= GPS_LOCATION_EXTENDED_HAS_DOP; + locationExtended.pdop = gnss_report_ptr->position_dop; + locationExtended.hdop = gnss_report_ptr->horizontal_dop; + locationExtended.vdop = gnss_report_ptr->vertical_dop; + } + if (SvStatus.num_svs >= 0) { LocApiAdapter::reportSv(SvStatus, + locationExtended, locEngHandle.extSvInfo((void*)gnss_report_ptr)); } } diff --git a/loc_api/libloc_api_50001/Android.mk b/loc_api/libloc_api_50001/Android.mk index 4e5973d6..0bf889ef 100644 --- a/loc_api/libloc_api_50001/Android.mk +++ b/loc_api/libloc_api_50001/Android.mk @@ -65,7 +65,8 @@ LOCAL_SRC_FILES += \ loc_eng_agps.cpp \ loc_eng_xtra.cpp \ loc_eng_ni.cpp \ - loc_eng_log.cpp + loc_eng_log.cpp \ + loc_eng_nmea.cpp ifeq ($(FEATURE_GNSS_BIT_API), true) LOCAL_CFLAGS += -DFEATURE_GNSS_BIT_API diff --git a/loc_api/libloc_api_50001/LocApiAdapter.cpp b/loc_api/libloc_api_50001/LocApiAdapter.cpp index 1bf7a8e0..6bd79bbe 100755 --- a/loc_api/libloc_api_50001/LocApiAdapter.cpp +++ b/loc_api/libloc_api_50001/LocApiAdapter.cpp @@ -1,4 +1,4 @@ -/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2011-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 @@ -113,12 +113,14 @@ int LocApiAdapter::decodeAddress(char *addr_string, int string_size, } void LocApiAdapter::reportPosition(GpsLocation &location, + GpsLocationExtended &locationExtended, void* locationExt, enum loc_sess_status status, LocPosTechMask loc_technology_mask ) { loc_eng_msg_report_position *msg(new loc_eng_msg_report_position(locEngHandle.owner, location, + locationExtended, locationExt, status, loc_technology_mask)); @@ -129,9 +131,9 @@ void LocApiAdapter::reportPosition(GpsLocation &location, } } -void LocApiAdapter::reportSv(GpsSvStatus &svStatus, void* svExt) +void LocApiAdapter::reportSv(GpsSvStatus &svStatus, GpsLocationExtended &locationExtended, void* svExt) { - loc_eng_msg_report_sv *msg(new loc_eng_msg_report_sv(locEngHandle.owner, svStatus, svExt)); + loc_eng_msg_report_sv *msg(new loc_eng_msg_report_sv(locEngHandle.owner, svStatus, locationExtended, svExt)); //We want to send SV info to ULP to help it in determining GNSS signal strength //ULP will forward the SV reports to HAL without any modifications diff --git a/loc_api/libloc_api_50001/LocApiAdapter.h b/loc_api/libloc_api_50001/LocApiAdapter.h index 59caaf62..80a675d8 100755 --- a/loc_api/libloc_api_50001/LocApiAdapter.h +++ b/loc_api/libloc_api_50001/LocApiAdapter.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2011-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 @@ -121,10 +121,13 @@ public: const char *data, int data_size); void reportPosition(GpsLocation &location, + GpsLocationExtended &locationExtended, void* locationExt, enum loc_sess_status status, LocPosTechMask loc_technology_mask = LOC_POS_TECH_MASK_DEFAULT); - void reportSv(GpsSvStatus &svStatus, void* svExt); + void reportSv(GpsSvStatus &svStatus, + GpsLocationExtended &locationExtended, + void* svExt); void reportStatus(GpsStatusValue status); void reportNmea(const char* nmea, int length); void reportAgpsStatus(AGpsStatus &agpsStatus); diff --git a/loc_api/libloc_api_50001/loc_eng.cpp b/loc_api/libloc_api_50001/loc_eng.cpp index f7161da8..5075311e 100755 --- a/loc_api/libloc_api_50001/loc_eng.cpp +++ b/loc_api/libloc_api_50001/loc_eng.cpp @@ -1,4 +1,4 @@ -/* Copyright (c) 2009-2012 Code Aurora Forum. All rights reserved. +/* Copyright (c) 2009-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 @@ -57,6 +57,7 @@ #include #include #include +#include #include #include @@ -83,6 +84,7 @@ static loc_param_s_type loc_parameter_table[] = {"INTERMEDIATE_POS", &gps_conf.INTERMEDIATE_POS, NULL, 'n'}, {"ACCURACY_THRES", &gps_conf.ACCURACY_THRES, NULL, 'n'}, {"ENABLE_WIPER", &gps_conf.ENABLE_WIPER, NULL, 'n'}, + {"NMEA_PROVIDER", &gps_conf.NMEA_PROVIDER, NULL, 'n'}, {"SUPL_VER", &gps_conf.SUPL_VER, NULL, 'n'}, {"CAPABILITIES", &gps_conf.CAPABILITIES, NULL, 'n'}, {"GYRO_BIAS_RANDOM_WALK", &gps_conf.GYRO_BIAS_RANDOM_WALK, &gps_conf.GYRO_BIAS_RANDOM_WALK_VALID, 'f'}, @@ -111,6 +113,7 @@ static void loc_default_parameters(void) gps_conf.INTERMEDIATE_POS = 0; gps_conf.ACCURACY_THRES = 0; gps_conf.ENABLE_WIPER = 0; + gps_conf.NMEA_PROVIDER = 0; gps_conf.SUPL_VER = 0x10000; gps_conf.CAPABILITIES = 0x7; @@ -303,6 +306,16 @@ int loc_eng_init(loc_eng_data_s_type &loc_eng_data, LocCallbacks* callbacks, // loc_eng_data.fix_session_status -- GPS_STATUS_NONE; // loc_eng_data.mute_session_state -- LOC_MUTE_SESS_NONE; + if ((event & LOC_API_ADAPTER_BIT_NMEA_1HZ_REPORT) && (gps_conf.NMEA_PROVIDER == NMEA_PROVIDER_AP)) + { + event = event ^ LOC_API_ADAPTER_BIT_NMEA_1HZ_REPORT; // unregister for modem NMEA report + loc_eng_data.generateNmea = true; + } + else + { + loc_eng_data.generateNmea = false; + } + LocEng locEngHandle(&loc_eng_data, event, loc_eng_data.acquire_wakelock_cb, loc_eng_data.release_wakelock_cb, loc_eng_msg_sender, loc_external_msg_sender, callbacks->location_ext_parser, callbacks->sv_ext_parser); @@ -1523,6 +1536,11 @@ static void loc_eng_deferred_action_thread(void* arg) loc_eng_data_p->client_handle->setInSession(false); } + if (loc_eng_data_p->generateNmea && rpMsg->location.position_source == ULP_LOCATION_IS_FROM_GNSS) + { + loc_eng_nmea_generate_pos(loc_eng_data_p, rpMsg->location, rpMsg->locationExtended); + } + // Free the allocated memory for rawData GpsLocation* gp = (GpsLocation*)&(rpMsg->location); if (gp != NULL && gp->rawData != NULL) @@ -1543,6 +1561,12 @@ static void loc_eng_deferred_action_thread(void* arg) loc_eng_data_p->sv_status_cb((GpsSvStatus*)&(rsMsg->svStatus), (void*)rsMsg->svExt); } + + if (loc_eng_data_p->generateNmea) + { + loc_eng_nmea_generate_sv(loc_eng_data_p, rsMsg->svStatus, rsMsg->locationExtended); + } + } break; diff --git a/loc_api/libloc_api_50001/loc_eng.h b/loc_api/libloc_api_50001/loc_eng.h index a14f5bd1..1b9dcf58 100644 --- a/loc_api/libloc_api_50001/loc_eng.h +++ b/loc_api/libloc_api_50001/loc_eng.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2009-2012 Code Aurora Forum. All rights reserved. +/* Copyright (c) 2009-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 @@ -68,6 +68,11 @@ typedef unsigned char boolean; #define FAILURE FALSE #define INVALID_ATL_CONNECTION_HANDLE -1 +enum loc_nmea_provider_e_type { + NMEA_PROVIDER_AP = 0, // Application Processor Provider of NMEA + NMEA_PROVIDER_MP // Modem Processor Provider of NMEA +}; + enum loc_mute_session_e_type { LOC_MUTE_SESS_NONE = 0, LOC_MUTE_SESS_WAIT, @@ -128,6 +133,13 @@ typedef struct // For muting session broadcast loc_mute_session_e_type mute_session_state; + // For nmea generation + boolean generateNmea; + uint32_t sv_used_mask; + float hdop; + float pdop; + float vdop; + // Address buffers, for addressing setting before init int supl_host_set; char supl_host_buf[101]; @@ -149,6 +161,7 @@ typedef struct loc_gps_cfg_s unsigned long INTERMEDIATE_POS; unsigned long ACCURACY_THRES; unsigned long ENABLE_WIPER; + uint8_t NMEA_PROVIDER; unsigned long SUPL_VER; unsigned long CAPABILITIES; uint8_t GYRO_BIAS_RANDOM_WALK_VALID; diff --git a/loc_api/libloc_api_50001/loc_eng_msg.h b/loc_api/libloc_api_50001/loc_eng_msg.h index d846a1f3..9dffc280 100755 --- a/loc_api/libloc_api_50001/loc_eng_msg.h +++ b/loc_api/libloc_api_50001/loc_eng_msg.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2011,2012 Code Aurora Forum. All rights reserved. +/* Copyright (c) 2011-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 @@ -98,6 +98,35 @@ struct LocPosMode } }; +/** Flags to indicate which values are valid in a GpsLocationExtended. */ +typedef uint16_t GpsLocationExtendedFlags; +/** GpsLocationExtended has valid pdop, hdop, vdop. */ +#define GPS_LOCATION_EXTENDED_HAS_DOP 0x0001 +/** GpsLocationExtended has valid altitude mean sea level. */ +#define GPS_LOCATION_EXTENDED_HAS_ALTITUDE_MEAN_SEA_LEVEL 0x0002 +/** GpsLocation has valid magnetic deviation. */ +#define GPS_LOCATION_EXTENDED_HAS_MAG_DEV 0x0004 +/** GpsLocation has valid mode indicator. */ +#define GPS_LOCATION_EXTENDED_HAS_MODE_IND 0x0008 + +/** Represents gps location extended. */ +typedef struct { + /** set to sizeof(GpsLocationExtended) */ + size_t size; + /** Contains GpsLocationExtendedFlags bits. */ + uint16_t flags; + /** Contains the Altitude wrt mean sea level */ + float altitudeMeanSeaLevel; + /** Contains Position Dilusion of Precision. */ + float pdop; + /** Contains Horizontal Dilusion of Precision. */ + float hdop; + /** Contains Vertical Dilusion of Precision. */ + float vdop; + /** Contains Magnetic Deviation. */ + float magneticDeviation; +} GpsLocationExtended; + typedef enum { LOC_ENG_IF_REQUEST_TYPE_SUPL = 0, LOC_ENG_IF_REQUEST_TYPE_WIFI, @@ -317,23 +346,24 @@ struct loc_eng_msg_delete_aiding_data : public loc_eng_msg { struct loc_eng_msg_report_position : public loc_eng_msg { const GpsLocation location; + const GpsLocationExtended locationExtended; const void* locationExt; const enum loc_sess_status status; const LocPosTechMask technology_mask; - inline loc_eng_msg_report_position(void* instance, GpsLocation &loc, void* locExt, + inline loc_eng_msg_report_position(void* instance, GpsLocation &loc, GpsLocationExtended &locExtended, void* locExt, enum loc_sess_status st) : loc_eng_msg(instance, LOC_ENG_MSG_REPORT_POSITION), - location(loc), locationExt(locExt), status(st), technology_mask(LOC_POS_TECH_MASK_DEFAULT) + location(loc), locationExtended(locExtended), locationExt(locExt), status(st), technology_mask(LOC_POS_TECH_MASK_DEFAULT) { LOC_LOGV("flags: %d\n source: %d\n latitude: %f\n longitude: %f\n altitude: %f\n speed: %f\n bearing: %f\n accuracy: %f\n timestamp: %lld\n rawDataSize: %d\n rawData: %p\n Session status: %d\n Technology mask: %u", location.flags, location.position_source, location.latitude, location.longitude, location.altitude, location.speed, location.bearing, location.accuracy, location.timestamp, location.rawDataSize, location.rawData,status,technology_mask); } - inline loc_eng_msg_report_position(void* instance, GpsLocation &loc, void* locExt, + inline loc_eng_msg_report_position(void* instance, GpsLocation &loc, GpsLocationExtended &locExtended, void* locExt, enum loc_sess_status st, LocPosTechMask technology) : loc_eng_msg(instance, LOC_ENG_MSG_REPORT_POSITION), - location(loc), locationExt(locExt), status(st), technology_mask(technology) + location(loc), locationExtended(locExtended), locationExt(locExt), status(st), technology_mask(technology) { LOC_LOGV("flags: %d\n source: %d\n latitude: %f\n longitude: %f\n altitude: %f\n speed: %f\n bearing: %f\n accuracy: %f\n timestamp: %lld\n rawDataSize: %d\n rawData: %p\n Session status: %d\n Technology mask: %u", location.flags, location.position_source, location.latitude, location.longitude, @@ -344,9 +374,10 @@ struct loc_eng_msg_report_position : public loc_eng_msg { struct loc_eng_msg_report_sv : public loc_eng_msg { const GpsSvStatus svStatus; + const GpsLocationExtended locationExtended; const void* svExt; - inline loc_eng_msg_report_sv(void* instance, GpsSvStatus &sv, void* ext) : - loc_eng_msg(instance, LOC_ENG_MSG_REPORT_SV), svStatus(sv), svExt(ext) + inline loc_eng_msg_report_sv(void* instance, GpsSvStatus &sv, GpsLocationExtended &locExtended, void* ext) : + loc_eng_msg(instance, LOC_ENG_MSG_REPORT_SV), svStatus(sv), locationExtended(locExtended), svExt(ext) { LOC_LOGV("num sv: %d\n ephemeris mask: %dxn almanac mask: %x\n used in fix mask: %x\n sv: prn snr elevation azimuth", svStatus.num_svs, svStatus.ephemeris_mask, svStatus.almanac_mask, svStatus.used_in_fix_mask); diff --git a/loc_api/libloc_api_50001/loc_eng_nmea.cpp b/loc_api/libloc_api_50001/loc_eng_nmea.cpp new file mode 100644 index 00000000..a0a3f202 --- /dev/null +++ b/loc_api/libloc_api_50001/loc_eng_nmea.cpp @@ -0,0 +1,703 @@ +/* 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 Code Aurora Forum, Inc. 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" + +#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); + 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,"*%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 GpsLocation &location, const GpsLocationExtended &locationExtended) +{ + ENTRY_LOG(); + + char sentence[NMEA_SENTENCE_MAX_LENGTH] = {0}; + char* pMarker = sentence; + int lengthRemaining = sizeof(sentence); + int length = 0; + + time_t utcTime(location.timestamp/1000); + tm * pTm = gmtime(&utcTime); + 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; + + // ------------------ + // ------$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.flags & GPS_LOCATION_HAS_BEARING) + { + float magTrack = location.bearing; + if (locationExtended.flags & GPS_LOCATION_EXTENDED_HAS_MAG_DEV) + { + float magTrack = location.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.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.flags & GPS_LOCATION_HAS_SPEED) + { + float speedKnots = location.speed * (3600.0/1852.0); + float speedKmPerHour = location.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.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->client_handle->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.flags & GPS_LOCATION_HAS_LAT_LONG) + { + double latitude = location.latitude; + double longitude = location.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.flags & GPS_LOCATION_HAS_SPEED) + { + float speedKnots = location.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.flags & GPS_LOCATION_HAS_BEARING) + { + length = snprintf(pMarker, lengthRemaining, "%.1lf,", location.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.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->client_handle->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.flags & GPS_LOCATION_HAS_LAT_LONG) + { + double latitude = location.latitude; + double longitude = location.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.flags & GPS_LOCATION_HAS_LAT_LONG)) + gpsQuality = '0'; // 0 means no fix + else if (LOC_POSITION_MODE_STANDALONE == loc_eng_data_p->client_handle->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.flags & GPS_LOCATION_HAS_ALTITUDE) && + (locationExtended.flags & GPS_LOCATION_EXTENDED_HAS_ALTITUDE_MEAN_SEA_LEVEL)) + { + length = snprintf(pMarker, lengthRemaining, "%.1lf,M,,", + location.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); + + // 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; + + // ------------------ + // ------$GPGSV------ + // ------------------ + + if (svStatus.num_svs <= 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 + { + int svCount = svStatus.num_svs; + int sentenceCount = svCount / 4; + if (svStatus.num_svs % 4) + sentenceCount++; + int sentenceNumber = 1; + int svNumber = 1; + + while (sentenceNumber <= sentenceCount) + { + pMarker = sentence; + lengthRemaining = sizeof(sentence); + + length = snprintf(pMarker, lengthRemaining, "$GPGSV,%d,%d,%02d", + sentenceCount, sentenceNumber, svCount); + + 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); i++, svNumber++) + { + 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; + } + } + + length = loc_eng_nmea_put_checksum(sentence, sizeof(sentence)); + loc_eng_nmea_send(sentence, length, loc_eng_data_p); + sentenceNumber++; + + } + } + + 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); +} diff --git a/loc_api/libloc_api_50001/loc_eng_nmea.h b/loc_api/libloc_api_50001/loc_eng_nmea.h new file mode 100644 index 00000000..5da7d5dd --- /dev/null +++ b/loc_api/libloc_api_50001/loc_eng_nmea.h @@ -0,0 +1,42 @@ +/* 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 Code Aurora Forum, Inc. 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. + * + */ + +#ifndef LOC_ENG_NMEA_H +#define LOC_ENG_NMEA_H + +#include + +#define NMEA_SENTENCE_MAX_LENGTH 200 + +void loc_eng_nmea_send(char *pNmea, int length, loc_eng_data_s_type *loc_eng_data_p); +int loc_eng_nmea_put_checksum(char *pNmea, int maxSize); +void loc_eng_nmea_generate_sv(loc_eng_data_s_type *loc_eng_data_p, const GpsSvStatus &svStatus, const GpsLocationExtended &locationExtended); +void loc_eng_nmea_generate_pos(loc_eng_data_s_type *loc_eng_data_p, const GpsLocation &location, const GpsLocationExtended &locationExtended); + +#endif // LOC_ENG_NMEA_H diff --git a/loc_api/loc_api_v02/LocApiV02Adapter.cpp b/loc_api/loc_api_v02/LocApiV02Adapter.cpp index 68057bcd..9345aedf 100755 --- a/loc_api/loc_api_v02/LocApiV02Adapter.cpp +++ b/loc_api/loc_api_v02/LocApiV02Adapter.cpp @@ -1,4 +1,4 @@ -/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2011-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 @@ -1554,6 +1554,9 @@ void LocApiV02Adapter :: reportPosition ( LOC_LOGD("Reporting postion from V2 Adapter\n"); memset(&location, 0, sizeof (GpsLocation)); location.size = sizeof(location); + GpsLocationExtended locationExtended; + memset(&locationExtended, 0, sizeof (GpsLocationExtended)); + locationExtended.size = sizeof(locationExtended); // Process the position from final and intermediate reports if( (location_report_ptr->sessionStatus == eQMI_LOC_SESS_STATUS_SUCCESS_V02) || @@ -1615,7 +1618,29 @@ void LocApiV02Adapter :: reportPosition ( //Mark the location source as from GNSS location.flags |= LOCATION_HAS_SOURCE_INFO; location.position_source = ULP_LOCATION_IS_FROM_GNSS; + + if (location_report_ptr->magneticDeviation_valid) + { + locationExtended.flags |= GPS_LOCATION_EXTENDED_HAS_MAG_DEV; + locationExtended.magneticDeviation = location_report_ptr->magneticDeviation; + } + + if (location_report_ptr->DOP_valid) + { + locationExtended.flags |= GPS_LOCATION_EXTENDED_HAS_DOP; + locationExtended.pdop = location_report_ptr->DOP.PDOP; + locationExtended.hdop = location_report_ptr->DOP.HDOP; + locationExtended.vdop = location_report_ptr->DOP.VDOP; + } + + if (location_report_ptr->altitudeWrtMeanSeaLevel_valid) + { + locationExtended.flags |= GPS_LOCATION_EXTENDED_HAS_ALTITUDE_MEAN_SEA_LEVEL; + locationExtended.altitudeMeanSeaLevel = location_report_ptr->altitudeWrtMeanSeaLevel; + } + LocApiAdapter::reportPosition( location, + locationExtended, locEngHandle.extPosInfo((void*)location_report_ptr), (location_report_ptr->sessionStatus == eQMI_LOC_SESS_STATUS_IN_PROGRESS_V02 ? @@ -1626,6 +1651,7 @@ void LocApiV02Adapter :: reportPosition ( else { LocApiAdapter::reportPosition(location, + locationExtended, NULL, LOC_SESS_FAILURE); @@ -1642,6 +1668,7 @@ void LocApiV02Adapter :: reportSv ( const qmiLocEventGnssSvInfoIndMsgT_v02 *gnss_report_ptr) { GpsSvStatus SvStatus; + GpsLocationExtended locationExtended; int num_svs_max, i; const qmiLocSvInfoStructT_v02 *sv_info_ptr; @@ -1650,6 +1677,8 @@ void LocApiV02Adapter :: reportSv ( num_svs_max = 0; memset (&SvStatus, 0, sizeof (GpsSvStatus)); + memset(&locationExtended, 0, sizeof (GpsLocationExtended)); + locationExtended.size = sizeof(locationExtended); if(gnss_report_ptr->svList_valid == 1) { num_svs_max = gnss_report_ptr->svList_len; @@ -1740,6 +1769,7 @@ void LocApiV02Adapter :: reportSv ( { LOC_LOGV ("%s:%d]: firing SV callback\n", __func__, __LINE__); LocApiAdapter::reportSv(SvStatus, + locationExtended, locEngHandle.extSvInfo((void*)gnss_report_ptr)); } }