diff --git a/gnss/aidl/default/Gnss.cpp b/gnss/aidl/default/Gnss.cpp index c31f991b9d..94d4d000ac 100644 --- a/gnss/aidl/default/Gnss.cpp +++ b/gnss/aidl/default/Gnss.cpp @@ -162,12 +162,13 @@ ScopedAStatus Gnss::close() { return ScopedAStatus::ok(); } -void Gnss::reportLocation(const GnssLocation& location) const { +void Gnss::reportLocation(const GnssLocation& location) { std::unique_lock lock(mMutex); if (sGnssCallback == nullptr) { ALOGE("%s: GnssCallback is null.", __func__); return; } + mLastLocation = std::make_shared(location); auto status = sGnssCallback->gnssLocationCb(location); if (!status.isOk()) { ALOGE("%s: Unable to invoke gnssLocationCb", __func__); @@ -359,7 +360,6 @@ ScopedAStatus Gnss::getExtensionGnssNavigationMessage( ndk::ScopedAStatus Gnss::getExtensionGnssDebug(std::shared_ptr* iGnssDebug) { ALOGD("Gnss::getExtensionGnssDebug"); - *iGnssDebug = SharedRefBase::make(); return ndk::ScopedAStatus::ok(); } @@ -398,4 +398,8 @@ void Gnss::setGnssMeasurementInterval(const long intervalMs) { mGnssMeasurementIntervalMs = intervalMs; } +std::shared_ptr Gnss::getLastLocation() const { + return mLastLocation; +} + } // namespace aidl::android::hardware::gnss diff --git a/gnss/aidl/default/Gnss.h b/gnss/aidl/default/Gnss.h index 245d60785f..73085ef591 100644 --- a/gnss/aidl/default/Gnss.h +++ b/gnss/aidl/default/Gnss.h @@ -87,18 +87,19 @@ class Gnss : public BnGnss { void reportSvStatus() const; void setGnssMeasurementEnabled(const bool enabled); void setGnssMeasurementInterval(const long intervalMs); + std::shared_ptr getLastLocation() const; std::shared_ptr mGnssConfiguration; std::shared_ptr mGnssPowerIndication; std::shared_ptr mGnssMeasurementInterface; private: - void reportLocation(const GnssLocation&) const; + void reportLocation(const GnssLocation&); void reportSvStatus(const std::vector& svInfoList) const; + void reportGnssStatusValue(const IGnssCallback::GnssStatusValue gnssStatusValue) const; + void reportNmea() const; std::vector filterBlocklistedSatellites( std::vector gnssSvInfoList) const; - void reportGnssStatusValue(const IGnssCallback::GnssStatusValue gnssStatusValue) const; std::unique_ptr getLocationFromHW(); - void reportNmea() const; static std::shared_ptr sGnssCallback; @@ -109,6 +110,7 @@ class Gnss : public BnGnss { std::atomic mIsNmeaActive; std::atomic mFirstFixReceived; std::atomic mGnssMeasurementEnabled; + std::shared_ptr mLastLocation; std::thread mThread; ::android::hardware::gnss::common::ThreadBlocker mThreadBlocker; diff --git a/gnss/aidl/default/GnssDebug.cpp b/gnss/aidl/default/GnssDebug.cpp index f40c0bcef0..5ae6edd098 100644 --- a/gnss/aidl/default/GnssDebug.cpp +++ b/gnss/aidl/default/GnssDebug.cpp @@ -18,10 +18,15 @@ #include "GnssDebug.h" #include +#include +#include "Constants.h" +#include "Gnss.h" #include "MockLocation.h" namespace aidl::android::hardware::gnss { +using ::android::hardware::gnss::common::kMockTimestamp; + ndk::ScopedAStatus GnssDebug::getDebugData(DebugData* debugData) { ALOGD("GnssDebug::getDebugData"); @@ -36,10 +41,94 @@ ndk::ScopedAStatus GnssDebug::getDebugData(DebugData* debugData) { .speedAccuracyMetersPerSecond = 1, .bearingAccuracyDegrees = 90, .ageSeconds = 0.99}; - TimeDebug timeDebug = {.timeEstimateMs = 1519930775453L, + TimeDebug timeDebug = {.timeEstimateMs = static_cast( + kMockTimestamp + ::android::elapsedRealtimeNano() / 1e6), .timeUncertaintyNs = 1000, - .frequencyUncertaintyNsPerSec = 5.0e4}; - std::vector satelliteDataArrayDebug = {}; + .frequencyUncertaintyNsPerSec = 800}; + SatelliteData satelliteData1 = { + .svid = 3, + .constellation = GnssConstellationType::GPS, + .ephemerisType = SatelliteEphemerisType::EPHEMERIS, + .ephemerisSource = SatellitePvt::SatelliteEphemerisSource::SERVER_LONG_TERM, + .ephemerisHealth = SatelliteEphemerisHealth::GOOD, + .ephemerisAgeSeconds = 12, + .serverPredictionIsAvailable = true, + .serverPredictionAgeSeconds = 30}; + SatelliteData satelliteData2 = { + .svid = 5, + .constellation = GnssConstellationType::GPS, + .ephemerisType = SatelliteEphemerisType::EPHEMERIS, + .ephemerisSource = SatellitePvt::SatelliteEphemerisSource::SERVER_LONG_TERM, + .ephemerisHealth = SatelliteEphemerisHealth::GOOD, + .ephemerisAgeSeconds = 12, + .serverPredictionIsAvailable = true, + .serverPredictionAgeSeconds = 30}; + SatelliteData satelliteData3 = { + .svid = 17, + .constellation = GnssConstellationType::GPS, + .ephemerisType = SatelliteEphemerisType::EPHEMERIS, + .ephemerisSource = SatellitePvt::SatelliteEphemerisSource::SERVER_LONG_TERM, + .ephemerisHealth = SatelliteEphemerisHealth::GOOD, + .ephemerisAgeSeconds = 12, + .serverPredictionIsAvailable = true, + .serverPredictionAgeSeconds = 30}; + SatelliteData satelliteData4 = { + .svid = 26, + .constellation = GnssConstellationType::GPS, + .ephemerisType = SatelliteEphemerisType::EPHEMERIS, + .ephemerisSource = SatellitePvt::SatelliteEphemerisSource::SERVER_LONG_TERM, + .ephemerisHealth = SatelliteEphemerisHealth::GOOD, + .ephemerisAgeSeconds = 12, + .serverPredictionIsAvailable = true, + .serverPredictionAgeSeconds = 30}; + SatelliteData satelliteData5 = { + .svid = 5, + .constellation = GnssConstellationType::GLONASS, + .ephemerisType = SatelliteEphemerisType::EPHEMERIS, + .ephemerisSource = SatellitePvt::SatelliteEphemerisSource::SERVER_LONG_TERM, + .ephemerisHealth = SatelliteEphemerisHealth::GOOD, + .ephemerisAgeSeconds = 12, + .serverPredictionIsAvailable = true, + .serverPredictionAgeSeconds = 30}; + SatelliteData satelliteData6 = { + .svid = 17, + .constellation = GnssConstellationType::GLONASS, + .ephemerisType = SatelliteEphemerisType::EPHEMERIS, + .ephemerisSource = SatellitePvt::SatelliteEphemerisSource::SERVER_LONG_TERM, + .ephemerisHealth = SatelliteEphemerisHealth::GOOD, + .ephemerisAgeSeconds = 12, + .serverPredictionIsAvailable = true, + .serverPredictionAgeSeconds = 30}; + SatelliteData satelliteData7 = { + .svid = 18, + .constellation = GnssConstellationType::GLONASS, + .ephemerisType = SatelliteEphemerisType::EPHEMERIS, + .ephemerisSource = SatellitePvt::SatelliteEphemerisSource::SERVER_LONG_TERM, + .ephemerisHealth = SatelliteEphemerisHealth::GOOD, + .ephemerisAgeSeconds = 12, + .serverPredictionIsAvailable = true, + .serverPredictionAgeSeconds = 30}; + SatelliteData satelliteData8 = { + .svid = 10, + .constellation = GnssConstellationType::GLONASS, + .ephemerisType = SatelliteEphemerisType::EPHEMERIS, + .ephemerisSource = SatellitePvt::SatelliteEphemerisSource::SERVER_LONG_TERM, + .ephemerisHealth = SatelliteEphemerisHealth::GOOD, + .ephemerisAgeSeconds = 12, + .serverPredictionIsAvailable = true, + .serverPredictionAgeSeconds = 30}; + SatelliteData satelliteData9 = { + .svid = 3, + .constellation = GnssConstellationType::IRNSS, + .ephemerisType = SatelliteEphemerisType::EPHEMERIS, + .ephemerisSource = SatellitePvt::SatelliteEphemerisSource::SERVER_LONG_TERM, + .ephemerisHealth = SatelliteEphemerisHealth::GOOD, + .ephemerisAgeSeconds = 12, + .serverPredictionIsAvailable = true, + .serverPredictionAgeSeconds = 30}; + std::vector satelliteDataArrayDebug = { + satelliteData1, satelliteData2, satelliteData3, satelliteData4, satelliteData5, + satelliteData6, satelliteData7, satelliteData8, satelliteData9}; debugData->position = positionDebug; debugData->time = timeDebug; debugData->satelliteDataArray = satelliteDataArrayDebug; diff --git a/gnss/aidl/default/GnssDebug.h b/gnss/aidl/default/GnssDebug.h index 001d47cf5c..b6844b3113 100644 --- a/gnss/aidl/default/GnssDebug.h +++ b/gnss/aidl/default/GnssDebug.h @@ -20,6 +20,8 @@ namespace aidl::android::hardware::gnss { +class Gnss; + struct GnssDebug : public BnGnssDebug { public: ndk::ScopedAStatus getDebugData(DebugData* debugData) override; diff --git a/gnss/aidl/vts/gnss_hal_test_cases.cpp b/gnss/aidl/vts/gnss_hal_test_cases.cpp index 430918720b..f7408d8dd6 100644 --- a/gnss/aidl/vts/gnss_hal_test_cases.cpp +++ b/gnss/aidl/vts/gnss_hal_test_cases.cpp @@ -1149,40 +1149,139 @@ TEST_P(GnssHalTest, GnssDebugValuesSanityTest) { sp iGnssDebug; auto status = aidl_gnss_hal_->getExtensionGnssDebug(&iGnssDebug); ASSERT_TRUE(status.isOk()); - - if (!IsAutomotiveDevice()) { - ASSERT_TRUE(iGnssDebug != nullptr); - - IGnssDebug::DebugData data; - auto status = iGnssDebug->getDebugData(&data); - ASSERT_TRUE(status.isOk()); - - if (data.position.valid) { - ASSERT_TRUE(data.position.latitudeDegrees >= -90 && - data.position.latitudeDegrees <= 90); - ASSERT_TRUE(data.position.longitudeDegrees >= -180 && - data.position.longitudeDegrees <= 180); - ASSERT_TRUE(data.position.altitudeMeters >= -1000 && // Dead Sea: -414m - data.position.altitudeMeters <= 20000); // Mount Everest: 8850m - ASSERT_TRUE(data.position.speedMetersPerSec >= 0 && - data.position.speedMetersPerSec <= 600); - ASSERT_TRUE(data.position.bearingDegrees >= -360 && - data.position.bearingDegrees <= 360); - ASSERT_TRUE(data.position.horizontalAccuracyMeters > 0 && - data.position.horizontalAccuracyMeters <= 20000000); - ASSERT_TRUE(data.position.verticalAccuracyMeters > 0 && - data.position.verticalAccuracyMeters <= 20000); - ASSERT_TRUE(data.position.speedAccuracyMetersPerSecond > 0 && - data.position.speedAccuracyMetersPerSecond <= 500); - ASSERT_TRUE(data.position.bearingAccuracyDegrees > 0 && - data.position.bearingAccuracyDegrees <= 180); - ASSERT_TRUE(data.position.ageSeconds >= 0); - } - ASSERT_TRUE(data.time.timeEstimateMs >= 1483228800000); // Jan 01 2017 00:00:00 GMT. - ASSERT_TRUE(data.time.timeUncertaintyNs > 0); - ASSERT_TRUE(data.time.frequencyUncertaintyNsPerSec > 0 && - data.time.frequencyUncertaintyNsPerSec <= 2.0e5); // 200 ppm + if (IsAutomotiveDevice()) { + return; } + ASSERT_TRUE(iGnssDebug != nullptr); + + IGnssDebug::DebugData data; + status = iGnssDebug->getDebugData(&data); + ASSERT_TRUE(status.isOk()); + Utils::checkPositionDebug(data); + + // Additional GnssDebug tests for AIDL version >= 4 (launched in Android 15(V)+) + if (aidl_gnss_hal_->getInterfaceVersion() <= 3) { + return; + } + + // Start location and check the consistency between SvStatus and DebugData + aidl_gnss_cb_->location_cbq_.reset(); + aidl_gnss_cb_->sv_info_list_cbq_.reset(); + StartAndCheckLocations(/* count= */ 2); + int location_called_count = aidl_gnss_cb_->location_cbq_.calledCount(); + ALOGD("Observed %d GnssSvStatus, while awaiting 2 locations (%d received)", + aidl_gnss_cb_->sv_info_list_cbq_.size(), location_called_count); + + // Wait for up to kNumSvInfoLists events for kTimeoutSeconds for each event. + int kTimeoutSeconds = 2; + int kNumSvInfoLists = 4; + std::list> sv_info_lists; + std::vector last_sv_info_list; + + do { + EXPECT_GT(aidl_gnss_cb_->sv_info_list_cbq_.retrieve(sv_info_lists, kNumSvInfoLists, + kTimeoutSeconds), + 0); + if (!sv_info_lists.empty()) { + last_sv_info_list = sv_info_lists.back(); + ALOGD("last_sv_info size = %d", (int)last_sv_info_list.size()); + } + } while (!sv_info_lists.empty() && last_sv_info_list.size() == 0); + + StopAndClearLocations(); + + status = iGnssDebug->getDebugData(&data); + Utils::checkPositionDebug(data); + + // Validate SatelliteEphemerisType, SatelliteEphemerisSource, SatelliteEphemerisHealth + for (auto sv_info : last_sv_info_list) { + if ((sv_info.svFlag & static_cast(IGnssCallback::GnssSvFlags::USED_IN_FIX)) == 0) { + continue; + } + ALOGD("Found usedInFix const: %d, svid: %d", static_cast(sv_info.constellation), + sv_info.svid); + bool foundDebugData = false; + for (auto satelliteData : data.satelliteDataArray) { + if (satelliteData.constellation == sv_info.constellation && + satelliteData.svid == sv_info.svid) { + foundDebugData = true; + ALOGD("Found GnssDebug data for this sv."); + EXPECT_TRUE(satelliteData.serverPredictionIsAvailable || + satelliteData.ephemerisType == + IGnssDebug::SatelliteEphemerisType::EPHEMERIS); + // for satellites with ephType=0, they need ephHealth=0 if used-in-fix + if (satelliteData.ephemerisType == IGnssDebug::SatelliteEphemerisType::EPHEMERIS) { + EXPECT_TRUE(satelliteData.ephemerisHealth == + IGnssDebug::SatelliteEphemerisHealth::GOOD); + } + break; + } + } + // Every Satellite where GnssStatus says it is used-in-fix has a valid ephemeris - i.e. it's + // it shows either a serverPredAvail: 1, or a ephType=0 + EXPECT_TRUE(foundDebugData); + } + + bool hasServerPredictionAvailable = false; + bool hasNoneZeroServerPredictionAgeSeconds = false; + bool hasNoneDemodEphSource = false; + for (auto satelliteData : data.satelliteDataArray) { + // for satellites with serverPredAvail: 1, the serverPredAgeSec: is not 0 for all + // satellites (at least not on 2 fixes in a row - it could get lucky once) + if (satelliteData.serverPredictionIsAvailable) { + hasServerPredictionAvailable = true; + if (satelliteData.serverPredictionAgeSeconds != 0) { + hasNoneZeroServerPredictionAgeSeconds = true; + } + } + // for satellites with ephType=0, they need ephSource 0-3 + if (satelliteData.ephemerisType == IGnssDebug::SatelliteEphemerisType::EPHEMERIS) { + EXPECT_TRUE(satelliteData.ephemerisSource >= + SatellitePvt::SatelliteEphemerisSource::DEMODULATED && + satelliteData.ephemerisSource <= + SatellitePvt::SatelliteEphemerisSource::OTHER); + if (satelliteData.ephemerisSource != + SatellitePvt::SatelliteEphemerisSource::DEMODULATED) { + hasNoneDemodEphSource = true; + } + } + } + if (hasNoneDemodEphSource && hasServerPredictionAvailable) { + EXPECT_TRUE(hasNoneZeroServerPredictionAgeSeconds); + } + + /** + - Gnss Location Data:: should show some valid information, ideally reasonably close (+/-1km) to + the Location output - at least after the 2nd valid location output (maybe in general, wait + for 2 good Location outputs before checking this, in case they don't update the assistance + until after they output the Location) + */ + double distanceM = + Utils::distanceMeters(data.position.latitudeDegrees, data.position.longitudeDegrees, + aidl_gnss_cb_->last_location_.latitudeDegrees, + aidl_gnss_cb_->last_location_.longitudeDegrees); + ALOGD("distance between debug position and last position: %.2lf", distanceM); + EXPECT_LT(distanceM, 1000.0); // 1km + + /** + - Gnss Time Data:: timeEstimate should be reasonably close to the current GPS time. + - Gnss Time Data:: timeUncertaintyNs should always be > 0 and < 5e9 (could be large due + to solve-for-time type solutions) + - Gnss Time Data:: frequencyUncertaintyNsPerSec: should always be > 0 and < 1000 (1000 ns/s + corresponds to roughly a 300 m/s speed error, which should be pretty rare) + */ + ALOGD("debug time: %" PRId64 ", position time: %" PRId64, data.time.timeEstimateMs, + aidl_gnss_cb_->last_location_.timestampMillis); + // Allowing 5s between the last location time and the current GPS time + EXPECT_LT(abs(data.time.timeEstimateMs - aidl_gnss_cb_->last_location_.timestampMillis), 5000); + + ALOGD("debug time uncertainty: %f ns", data.time.timeUncertaintyNs); + EXPECT_GT(data.time.timeUncertaintyNs, 0); + EXPECT_LT(data.time.timeUncertaintyNs, 5e9); + + ALOGD("debug freq uncertainty: %f ns/s", data.time.frequencyUncertaintyNsPerSec); + EXPECT_GT(data.time.frequencyUncertaintyNsPerSec, 0); + EXPECT_LT(data.time.frequencyUncertaintyNsPerSec, 1000); } /* diff --git a/gnss/common/utils/vts/Utils.cpp b/gnss/common/utils/vts/Utils.cpp index 69e2b346b0..e3ff0f3d24 100644 --- a/gnss/common/utils/vts/Utils.cpp +++ b/gnss/common/utils/vts/Utils.cpp @@ -20,6 +20,7 @@ #include "gtest/gtest.h" #include +#include #include namespace android { @@ -58,6 +59,31 @@ void Utils::checkLocationElapsedRealtime(const android::hardware::gnss::GnssLoca checkElapsedRealtime(location.elapsedRealtime); } +void Utils::checkPositionDebug(android::hardware::gnss::IGnssDebug::DebugData data) { + if (data.position.valid) { + ASSERT_TRUE(data.position.latitudeDegrees >= -90 && data.position.latitudeDegrees <= 90); + ASSERT_TRUE(data.position.longitudeDegrees >= -180 && + data.position.longitudeDegrees <= 180); + ASSERT_TRUE(data.position.altitudeMeters >= -1000 && // Dead Sea: -414m + data.position.altitudeMeters <= 20000); // Mount Everest: 8850m + ASSERT_TRUE(data.position.speedMetersPerSec >= 0 && data.position.speedMetersPerSec <= 600); + ASSERT_TRUE(data.position.bearingDegrees >= -360 && data.position.bearingDegrees <= 360); + ASSERT_TRUE(data.position.horizontalAccuracyMeters > 0 && + data.position.horizontalAccuracyMeters <= 20000000); + ASSERT_TRUE(data.position.verticalAccuracyMeters > 0 && + data.position.verticalAccuracyMeters <= 20000); + ASSERT_TRUE(data.position.speedAccuracyMetersPerSecond > 0 && + data.position.speedAccuracyMetersPerSecond <= 500); + ASSERT_TRUE(data.position.bearingAccuracyDegrees > 0 && + data.position.bearingAccuracyDegrees <= 180); + ASSERT_TRUE(data.position.ageSeconds >= 0); + } + ASSERT_TRUE(data.time.timeEstimateMs >= 1483228800000); // Jan 01 2017 00:00:00 GMT. + ASSERT_TRUE(data.time.timeUncertaintyNs > 0); + ASSERT_TRUE(data.time.frequencyUncertaintyNsPerSec > 0 && + data.time.frequencyUncertaintyNsPerSec <= 2.0e5); // 200 ppm +} + void Utils::checkElapsedRealtime(const ElapsedRealtime& elapsedRealtime) { ASSERT_TRUE(elapsedRealtime.flags >= 0 && elapsedRealtime.flags <= (ElapsedRealtime::HAS_TIMESTAMP_NS | @@ -282,6 +308,17 @@ bool Utils::isAutomotiveDevice() { return strncmp(buffer, "automotive", PROPERTY_VALUE_MAX) == 0; } +double Utils::distanceMeters(double lat1, double lon1, double lat2, double lon2) { + double R = 6378.137; // Radius of earth in KM + double dLat = lat2 * M_PI / 180 - lat1 * M_PI / 180; + double dLon = lon2 * M_PI / 180 - lon1 * M_PI / 180; + double a = sin(dLat / 2) * sin(dLat / 2) + + cos(lat1 * M_PI / 180) * cos(lat2 * M_PI / 180) * sin(dLon / 2) * sin(dLon / 2); + double c = 2 * atan2(sqrt(a), sqrt(1 - a)); + double d = R * c; + return d * 1000; // meters +} + } // namespace common } // namespace gnss } // namespace hardware diff --git a/gnss/common/utils/vts/include/Utils.h b/gnss/common/utils/vts/include/Utils.h index 7b890781db..62d409ac66 100644 --- a/gnss/common/utils/vts/include/Utils.h +++ b/gnss/common/utils/vts/include/Utils.h @@ -43,6 +43,7 @@ struct Utils { static void checkElapsedRealtime( const android::hardware::gnss::ElapsedRealtime& elapsedRealtime); + static void checkPositionDebug(android::hardware::gnss::IGnssDebug::DebugData data); static const android::hardware::gnss::GnssLocation getMockLocation( double latitudeDegrees, double longitudeDegrees, double horizontalAccuracyMeters); @@ -57,6 +58,7 @@ struct Utils { V2_0::GnssConstellationType constellation); static bool isAutomotiveDevice(); + static double distanceMeters(double lat1, double lon1, double lat2, double lon2); private: template