From 730abcc3d4354badb431ec99ca10011b29d5eccd Mon Sep 17 00:00:00 2001 From: Wei Chen Date: Fri, 19 Oct 2018 15:52:40 -0700 Subject: [PATCH] GPS NMEA: generate proper nmea during leap second transition During leap second transition, the hour, minute and seconds displayed need to be in the format of 23:59:60 to denote the extra leap second Change-Id: Ice7c6f934b1251990ba26e2b7e399554a2862eb4 CRs-fixed: 2335188 --- gnss/GnssAdapter.cpp | 26 ++++++++--- utils/loc_nmea.cpp | 100 ++++++++++++++++++++++++++++++++++++++++++- utils/loc_nmea.h | 1 + 3 files changed, 120 insertions(+), 7 deletions(-) diff --git a/gnss/GnssAdapter.cpp b/gnss/GnssAdapter.cpp index fc59c224..6b3e8463 100644 --- a/gnss/GnssAdapter.cpp +++ b/gnss/GnssAdapter.cpp @@ -1920,9 +1920,6 @@ GnssAdapter::updateClientsEventMask() mask |= LOC_API_ADAPTER_BIT_NMEA_1HZ_REPORT; updateNmeaMask(mNmeaMask | LOC_NMEA_MASK_DEBUG_V02); } - if (it->second.locationSystemInfoCb != nullptr) { - mask |= LOC_API_ADAPTER_BIT_LOC_SYSTEM_INFO; - } } /* @@ -1952,6 +1949,9 @@ GnssAdapter::updateClientsEventMask() mask |= LOC_API_ADAPTER_BIT_REQUEST_WIFI; } + // need to register for leap second info + // for proper nmea generation + mask |= LOC_API_ADAPTER_BIT_LOC_SYSTEM_INFO; updateEvtMask(mask, LOC_REGISTRATION_MASK_SET); } @@ -3012,7 +3012,8 @@ GnssAdapter::reportPosition(const UlpLocation& ulpLocation, (LOC_RELIABILITY_NOT_SET == locationExtended.horizontal_reliability)); uint8_t generate_nmea = (reported && status != LOC_SESS_FAILURE && !blank_fix); std::vector nmeaArraystr; - loc_nmea_generate_pos(ulpLocation, locationExtended, generate_nmea, nmeaArraystr); + loc_nmea_generate_pos(ulpLocation, locationExtended, mLocSystemInfo, + generate_nmea, nmeaArraystr); stringstream ss; for (auto sentence : nmeaArraystr) { ss << sentence; @@ -3292,7 +3293,22 @@ GnssAdapter::reportLocationSystemInfo(const LocationSystemInfo & locationSystemI // may come at different time if (locationSystemInfo.systemInfoMask & LOCATION_SYS_INFO_LEAP_SECOND) { mLocSystemInfo.systemInfoMask |= LOCATION_SYS_INFO_LEAP_SECOND; - mLocSystemInfo.leapSecondSysInfo = locationSystemInfo.leapSecondSysInfo; + + const LeapSecondSystemInfo &srcLeapSecondSysInfo = locationSystemInfo.leapSecondSysInfo; + LeapSecondSystemInfo &dstLeapSecondSysInfo = mLocSystemInfo.leapSecondSysInfo; + if (srcLeapSecondSysInfo.leapSecondInfoMask & + LEAP_SECOND_SYS_INFO_CURRENT_LEAP_SECONDS_BIT) { + dstLeapSecondSysInfo.leapSecondInfoMask |= + LEAP_SECOND_SYS_INFO_CURRENT_LEAP_SECONDS_BIT; + dstLeapSecondSysInfo.leapSecondCurrent = srcLeapSecondSysInfo.leapSecondCurrent; + } + // once leap second change event is complete, modem may send up event invalidate the leap second + // change info while AP is still processing report during leap second transition + // so, we choose to keep this info around even though it is old + if (srcLeapSecondSysInfo.leapSecondInfoMask & LEAP_SECOND_SYS_INFO_LEAP_SECOND_CHANGE_BIT) { + dstLeapSecondSysInfo.leapSecondInfoMask |= LEAP_SECOND_SYS_INFO_LEAP_SECOND_CHANGE_BIT; + dstLeapSecondSysInfo.leapSecondChangeInfo = srcLeapSecondSysInfo.leapSecondChangeInfo; + } } // we received new info, inform client of the newly received info diff --git a/utils/loc_nmea.cpp b/utils/loc_nmea.cpp index 5ef4277a..b4de6dba 100644 --- a/utils/loc_nmea.cpp +++ b/utils/loc_nmea.cpp @@ -36,6 +36,8 @@ #define GLONASS_SV_ID_OFFSET 64 #define MAX_SATELLITES_IN_USE 12 +#define MSEC_IN_ONE_WEEK 604800000ULL +#define UTC_GPS_OFFSET_MSECS 315964800000ULL // GNSS system id according to NMEA spec #define SYSTEM_ID_GPS 1 @@ -605,6 +607,79 @@ static void loc_nmea_generate_GSV(const GnssSvNotification &svNotify, } //while } +/*=========================================================================== +FUNCTION getUtcTimeWithLeapSecondTransition + +DESCRIPTION + This function returns true if the position report is generated during + leap second transition period. If not, then the utc timestamp returned + will be set to the timestamp in the position report. If it is, + then the utc timestamp returned will need to take into account + of the leap second transition so that proper calendar year/month/date + can be calculated from the returned utc timestamp. + +DEPENDENCIES + NONE + +RETURN VALUE + true: position report is generated in leap second transition period. + +SIDE EFFECTS + N/A + +===========================================================================*/ +bool getUtcTimeWithLeapSecondTransition(const UlpLocation &location, + const GpsLocationExtended &locationExtended, + const LocationSystemInfo &systemInfo, + LocGpsUtcTime &utcPosTimestamp) { + bool inTransition = false; + + // position report is not generated during leap second transition, + // we can use the UTC timestamp from position report as is + utcPosTimestamp = location.gpsLocation.timestamp; + + // Check whether we are in leap second transition. + // If so, per NMEA spec, we need to display the extra second in format of 23:59:60 + // with year/month/date not getting advanced. + if ((locationExtended.flags & GPS_LOCATION_EXTENDED_HAS_GPS_TIME) && + ((systemInfo.systemInfoMask & LOCATION_SYS_INFO_LEAP_SECOND) && + (systemInfo.leapSecondSysInfo.leapSecondInfoMask & + LEAP_SECOND_SYS_INFO_LEAP_SECOND_CHANGE_BIT))) { + + const LeapSecondChangeInfo &leapSecondChangeInfo = + systemInfo.leapSecondSysInfo.leapSecondChangeInfo; + const GnssSystemTimeStructType &gpsTimestampLsChange = + leapSecondChangeInfo.gpsTimestampLsChange; + + uint64_t gpsTimeLsChange = gpsTimestampLsChange.systemWeek * MSEC_IN_ONE_WEEK + + gpsTimestampLsChange.systemMsec; + uint64_t gpsTimePosReport = locationExtended.gpsTime.gpsWeek * MSEC_IN_ONE_WEEK + + locationExtended.gpsTime.gpsTimeOfWeekMs; + // we are only dealing with positive leap second change, as negative + // leap second change has never occurred and should not occur in future + if (leapSecondChangeInfo.leapSecondsAfterChange > + leapSecondChangeInfo.leapSecondsBeforeChange) { + // leap second adjustment is always 1 second at a time. It can happen + // every quarter end and up to four times per year. + if ((gpsTimePosReport >= gpsTimeLsChange) && + (gpsTimePosReport < (gpsTimeLsChange + 1000))) { + inTransition = true; + utcPosTimestamp = gpsTimeLsChange + UTC_GPS_OFFSET_MSECS - + leapSecondChangeInfo.leapSecondsBeforeChange * 1000; + + // we substract 1000 milli-seconds from UTC timestmap in order to calculate the + // proper year, month and date during leap second transtion. + // Let us give an example, assuming leap second transition is scheduled on 2019, + // Dec 31st mid night. When leap second transition is happening, + // instead of outputting the time as 2020, Jan, 1st, 00 hour, 00 min, and 00 sec. + // The time need to be displayed as 2019, Dec, 31st, 23 hour, 59 min and 60 sec. + utcPosTimestamp -= 1000; + } + } + } + return inTransition; +} + /*=========================================================================== FUNCTION loc_nmea_generate_pos @@ -631,11 +706,19 @@ SIDE EFFECTS ===========================================================================*/ void loc_nmea_generate_pos(const UlpLocation &location, const GpsLocationExtended &locationExtended, + const LocationSystemInfo &systemInfo, unsigned char generate_nmea, std::vector &nmeaArraystr) { ENTRY_LOG(); - time_t utcTime(location.gpsLocation.timestamp/1000); + + LocGpsUtcTime utcPosTimestamp = 0; + bool inLsTransition = false; + + inLsTransition = getUtcTimeWithLeapSecondTransition + (location, locationExtended, systemInfo, utcPosTimestamp); + + time_t utcTime(utcPosTimestamp/1000); tm * pTm = gmtime(&utcTime); if (NULL == pTm) { LOC_LOGE("gmtime failed"); @@ -653,7 +736,19 @@ void loc_nmea_generate_pos(const UlpLocation &location, int utcMinutes = pTm->tm_min; int utcSeconds = pTm->tm_sec; int utcMSeconds = (location.gpsLocation.timestamp)%1000; - loc_sv_cache_info sv_cache_info = {}; + + if (inLsTransition) { + // During leap second transition, we need to display the extra + // leap second of hour, minute, second as (23:59:60) + utcHours = 23; + utcMinutes = 59; + utcSeconds = 60; + // As UTC timestamp is freezing during leap second transition, + // retrieve milli-seconds portion from GPS timestamp. + utcMSeconds = locationExtended.gpsTime.gpsTimeOfWeekMs % 1000; + } + + loc_sv_cache_info sv_cache_info = {}; if (GPS_LOCATION_EXTENDED_HAS_GNSS_SV_USED_DATA & locationExtended.flags) { sv_cache_info.gps_used_mask = @@ -667,6 +762,7 @@ void loc_nmea_generate_pos(const UlpLocation &location, sv_cache_info.bds_used_mask = (uint32_t)locationExtended.gnss_sv_used_ids.qzss_sv_used_ids_mask; } + if (generate_nmea) { char talker[3] = {'G', 'P', '\0'}; uint32_t svUsedCount = 0; diff --git a/utils/loc_nmea.h b/utils/loc_nmea.h index 585a9c13..fbf88a74 100644 --- a/utils/loc_nmea.h +++ b/utils/loc_nmea.h @@ -40,6 +40,7 @@ void loc_nmea_generate_sv(const GnssSvNotification &svNotify, void loc_nmea_generate_pos(const UlpLocation &location, const GpsLocationExtended &locationExtended, + const LocationSystemInfo &systemInfo, unsigned char generate_nmea, std::vector &nmeaArraystr);