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
This commit is contained in:
Wei Chen 2018-10-19 15:52:40 -07:00
parent 9b0abcc59d
commit 730abcc3d4
3 changed files with 120 additions and 7 deletions

View file

@ -1920,9 +1920,6 @@ GnssAdapter::updateClientsEventMask()
mask |= LOC_API_ADAPTER_BIT_NMEA_1HZ_REPORT; mask |= LOC_API_ADAPTER_BIT_NMEA_1HZ_REPORT;
updateNmeaMask(mNmeaMask | LOC_NMEA_MASK_DEBUG_V02); 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; 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); updateEvtMask(mask, LOC_REGISTRATION_MASK_SET);
} }
@ -3012,7 +3012,8 @@ GnssAdapter::reportPosition(const UlpLocation& ulpLocation,
(LOC_RELIABILITY_NOT_SET == locationExtended.horizontal_reliability)); (LOC_RELIABILITY_NOT_SET == locationExtended.horizontal_reliability));
uint8_t generate_nmea = (reported && status != LOC_SESS_FAILURE && !blank_fix); uint8_t generate_nmea = (reported && status != LOC_SESS_FAILURE && !blank_fix);
std::vector<std::string> nmeaArraystr; std::vector<std::string> nmeaArraystr;
loc_nmea_generate_pos(ulpLocation, locationExtended, generate_nmea, nmeaArraystr); loc_nmea_generate_pos(ulpLocation, locationExtended, mLocSystemInfo,
generate_nmea, nmeaArraystr);
stringstream ss; stringstream ss;
for (auto sentence : nmeaArraystr) { for (auto sentence : nmeaArraystr) {
ss << sentence; ss << sentence;
@ -3292,7 +3293,22 @@ GnssAdapter::reportLocationSystemInfo(const LocationSystemInfo & locationSystemI
// may come at different time // may come at different time
if (locationSystemInfo.systemInfoMask & LOCATION_SYS_INFO_LEAP_SECOND) { if (locationSystemInfo.systemInfoMask & LOCATION_SYS_INFO_LEAP_SECOND) {
mLocSystemInfo.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 // we received new info, inform client of the newly received info

View file

@ -36,6 +36,8 @@
#define GLONASS_SV_ID_OFFSET 64 #define GLONASS_SV_ID_OFFSET 64
#define MAX_SATELLITES_IN_USE 12 #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 // GNSS system id according to NMEA spec
#define SYSTEM_ID_GPS 1 #define SYSTEM_ID_GPS 1
@ -605,6 +607,79 @@ static void loc_nmea_generate_GSV(const GnssSvNotification &svNotify,
} //while } //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 FUNCTION loc_nmea_generate_pos
@ -631,11 +706,19 @@ SIDE EFFECTS
===========================================================================*/ ===========================================================================*/
void loc_nmea_generate_pos(const UlpLocation &location, void loc_nmea_generate_pos(const UlpLocation &location,
const GpsLocationExtended &locationExtended, const GpsLocationExtended &locationExtended,
const LocationSystemInfo &systemInfo,
unsigned char generate_nmea, unsigned char generate_nmea,
std::vector<std::string> &nmeaArraystr) std::vector<std::string> &nmeaArraystr)
{ {
ENTRY_LOG(); 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); tm * pTm = gmtime(&utcTime);
if (NULL == pTm) { if (NULL == pTm) {
LOC_LOGE("gmtime failed"); LOC_LOGE("gmtime failed");
@ -653,7 +736,19 @@ void loc_nmea_generate_pos(const UlpLocation &location,
int utcMinutes = pTm->tm_min; int utcMinutes = pTm->tm_min;
int utcSeconds = pTm->tm_sec; int utcSeconds = pTm->tm_sec;
int utcMSeconds = (location.gpsLocation.timestamp)%1000; 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) { if (GPS_LOCATION_EXTENDED_HAS_GNSS_SV_USED_DATA & locationExtended.flags) {
sv_cache_info.gps_used_mask = sv_cache_info.gps_used_mask =
@ -667,6 +762,7 @@ void loc_nmea_generate_pos(const UlpLocation &location,
sv_cache_info.bds_used_mask = sv_cache_info.bds_used_mask =
(uint32_t)locationExtended.gnss_sv_used_ids.qzss_sv_used_ids_mask; (uint32_t)locationExtended.gnss_sv_used_ids.qzss_sv_used_ids_mask;
} }
if (generate_nmea) { if (generate_nmea) {
char talker[3] = {'G', 'P', '\0'}; char talker[3] = {'G', 'P', '\0'};
uint32_t svUsedCount = 0; uint32_t svUsedCount = 0;

View file

@ -40,6 +40,7 @@ void loc_nmea_generate_sv(const GnssSvNotification &svNotify,
void loc_nmea_generate_pos(const UlpLocation &location, void loc_nmea_generate_pos(const UlpLocation &location,
const GpsLocationExtended &locationExtended, const GpsLocationExtended &locationExtended,
const LocationSystemInfo &systemInfo,
unsigned char generate_nmea, unsigned char generate_nmea,
std::vector<std::string> &nmeaArraystr); std::vector<std::string> &nmeaArraystr);