1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.location.cts.common;
18 
19 import static org.junit.Assert.assertNotNull;
20 
21 import android.content.Context;
22 import android.content.pm.PackageManager;
23 import android.location.CorrelationVector;
24 import android.location.GnssClock;
25 import android.location.GnssMeasurement;
26 import android.location.GnssMeasurementsEvent;
27 import android.location.GnssNavigationMessage;
28 import android.location.GnssStatus;
29 import android.location.LocationManager;
30 import android.location.SatellitePvt;
31 import android.util.Log;
32 
33 import com.google.common.collect.Range;
34 
35 import java.util.ArrayList;
36 import java.util.Arrays;
37 import java.util.Collection;
38 import java.util.HashMap;
39 import java.util.HashSet;
40 import java.util.List;
41 import java.util.Map;
42 import java.util.Set;
43 import java.util.concurrent.TimeUnit;
44 
45 /**
46  * Helper class for GnssMeasurement Tests.
47  */
48 public final class TestMeasurementUtil {
49 
50     private static final String TAG = "TestMeasurementUtil";
51 
52     private static final long NSEC_IN_SEC = 1000_000_000L;
53     // Generally carrier phase quality prr's have uncertainties around 0.001-0.05 m/s, vs.
54     // doppler energy quality prr's closer to 0.25-10 m/s.  Threshold is chosen between those
55     // typical ranges.
56     private static final float THRESHOLD_FOR_CARRIER_PRR_UNC_METERS_PER_SEC = 0.15F;
57 
58     // For gpsTimeInNs >= 1.14 * 10^18 (year 2016+)
59     private static final long GPS_TIME_YEAR_2016_IN_NSEC = 1_140_000_000L * NSEC_IN_SEC;
60 
61     // Error message for GnssMeasurements Registration.
62     public static final String REGISTRATION_ERROR_MESSAGE = "Registration of GnssMeasurements" +
63             " listener has failed, this indicates a platform bug. Please report the issue with" +
64             " a full bugreport.";
65 
66     private static final Range<Double> GPS_L5_QZSS_J5_FREQ_RANGE_HZ =
67             Range.closed(1.164e9, 1.189e9);
68     private static final Range<Double> GAL_E1_FREQ_RANGE_HZ =
69             Range.closed(1.559e9, 1.591e9);
70     private static final Range<Double> GAL_E5_FREQ_RANGE_HZ =
71             Range.closed(1.164e9, 1.218e9);
72 
73     private enum GnssBand {
74         GNSS_L1,
75         GNSS_L2,
76         GNSS_L5,
77         GNSS_E6
78     }
79 
80     // The valid Gnss navigation message type as listed in
81     // android/hardware/libhardware/include/hardware/gps.h
82     public static final Set<Integer> GNSS_NAVIGATION_MESSAGE_TYPE =
83             new HashSet<Integer>(Arrays.asList(
84                     GnssNavigationMessage.TYPE_UNKNOWN,
85                     GnssNavigationMessage.TYPE_GPS_L1CA,
86                     GnssNavigationMessage.TYPE_GPS_L2CNAV,
87                     GnssNavigationMessage.TYPE_GPS_L5CNAV,
88                     GnssNavigationMessage.TYPE_GPS_CNAV2,
89                     GnssNavigationMessage.TYPE_SBS,
90                     GnssNavigationMessage.TYPE_GLO_L1CA,
91                     GnssNavigationMessage.TYPE_QZS_L1CA,
92                     GnssNavigationMessage.TYPE_BDS_D1,
93                     GnssNavigationMessage.TYPE_BDS_D2,
94                     GnssNavigationMessage.TYPE_BDS_CNAV1,
95                     GnssNavigationMessage.TYPE_BDS_CNAV2,
96                     GnssNavigationMessage.TYPE_GAL_I,
97                     GnssNavigationMessage.TYPE_GAL_F,
98                     GnssNavigationMessage.TYPE_IRN_L5CA
99             ));
100 
101     /**
102      * Check if test can be run on the current device.
103      *
104      * @param  testLocationManager TestLocationManager
105      * @return true if Build.VERSION &gt;= {@code androidSdkVersionCode} and Location GPS present on
106      *         device.
107      */
canTestRunOnCurrentDevice(TestLocationManager testLocationManager, String testTag)108     public static boolean canTestRunOnCurrentDevice(TestLocationManager testLocationManager,
109             String testTag) {
110         // If device does not have a GPS, skip the test.
111         if (!TestUtils.deviceHasGpsFeature(testLocationManager.getContext())) {
112             Log.i(TAG, "Skip the test since GPS is not supported on the device.");
113             return false;
114         }
115 
116         boolean gpsProviderEnabled = testLocationManager.getLocationManager()
117                 .isProviderEnabled(LocationManager.GPS_PROVIDER);
118         SoftAssert.failOrWarning(true, " GPS location disabled on the device. "
119                 + "Enable location in settings to continue test.", gpsProviderEnabled);
120         return gpsProviderEnabled;
121     }
122 
123     /**
124      * Check if current device is an Android Automotive OS device.
125      */
isAutomotiveDevice(Context context)126     public static boolean isAutomotiveDevice(Context context) {
127         return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
128     }
129 
130     /**
131      * Check if pseudorange rate uncertainty in Gnss Measurement is in the expected range.
132      * See field description in {@code gps.h}.
133      *
134      * @param measurement GnssMeasurement
135      * @return true if this measurement has prr uncertainty in a range indicative of carrier phase
136      */
gnssMeasurementHasCarrierPhasePrr(GnssMeasurement measurement)137     public static boolean gnssMeasurementHasCarrierPhasePrr(GnssMeasurement measurement) {
138       return (measurement.getPseudorangeRateUncertaintyMetersPerSecond() <
139               THRESHOLD_FOR_CARRIER_PRR_UNC_METERS_PER_SEC);
140     }
141 
142     /**
143      * Assert all mandatory fields in Gnss Clock are in expected range.
144      * See mandatory fields in {@code gps.h}.
145      *
146      * @param clock       GnssClock
147      * @param softAssert  custom SoftAssert
148      * @param timeInNs    event time in ns
149      */
assertGnssClockFields(GnssClock clock, SoftAssert softAssert, long timeInNs)150     public static void assertGnssClockFields(GnssClock clock,
151                                              SoftAssert softAssert,
152                                              long timeInNs) {
153         softAssert.assertTrue("time_ns: clock value",
154                 timeInNs,
155                 "X >= 0",
156                 String.valueOf(timeInNs),
157                 timeInNs >= 0L);
158 
159         // If full bias is valid and accurate within one sec. verify its sign & magnitude
160         if (clock.hasFullBiasNanos() &&
161                 ((!clock.hasBiasUncertaintyNanos()) ||
162                         (clock.getBiasUncertaintyNanos() < NSEC_IN_SEC))) {
163             long gpsTimeInNs = timeInNs - clock.getFullBiasNanos();
164             softAssert.assertTrue("TimeNanos - FullBiasNanos = GpsTimeNanos: clock value",
165                     gpsTimeInNs,
166                     "gpsTimeInNs >= 1.14 * 10^18 (year 2016+)",
167                     String.valueOf(gpsTimeInNs),
168                     gpsTimeInNs >= GPS_TIME_YEAR_2016_IN_NSEC);
169         }
170     }
171 
172     /**
173      * Asserts the same FullBiasNanos of multiple GnssMeasurementEvents at the same time epoch.
174      *
175      * <p>FullBiasNanos denotes the receiver clock bias calculated by the GNSS chipset. If multiple
176      * GnssMeasurementEvents are tagged with the same time epoch, their FullBiasNanos should be the
177      * same.
178      *
179      * @param softAssert custom SoftAssert
180      * @param events     GnssMeasurementEvents. Each event includes one GnssClock with a
181      *                   fullBiasNanos.
182      */
assertGnssClockHasConsistentFullBiasNanos(SoftAssert softAssert, List<GnssMeasurementsEvent> events)183     public static void assertGnssClockHasConsistentFullBiasNanos(SoftAssert softAssert,
184             List<GnssMeasurementsEvent> events) {
185         Map<Long, List<Long>> timeToFullBiasList = new HashMap<>();
186         for (GnssMeasurementsEvent event : events) {
187             long timeNanos = event.getClock().getTimeNanos();
188             long fullBiasNanos = event.getClock().getFullBiasNanos();
189 
190             timeToFullBiasList.putIfAbsent(timeNanos, new ArrayList<>());
191             List<Long> fullBiasNanosList = timeToFullBiasList.get(timeNanos);
192             fullBiasNanosList.add(fullBiasNanos);
193         }
194 
195         for (Map.Entry<Long, List<Long>> entry : timeToFullBiasList.entrySet()) {
196             long timeNanos = entry.getKey();
197             List<Long> fullBiasNanosList = entry.getValue();
198             if (fullBiasNanosList.size() < 2) {
199                 continue;
200             }
201             long fullBiasNanos = fullBiasNanosList.get(0);
202             for (int i = 1; i < fullBiasNanosList.size(); i++) {
203                 softAssert.assertTrue("FullBiasNanos are the same at the same timeNanos",
204                         timeNanos,
205                         "fullBiasNanosList.get(i) - fullBiasNanosList.get(0) == 0",
206                         String.valueOf(fullBiasNanosList.get(i) - fullBiasNanos),
207                         fullBiasNanosList.get(i) - fullBiasNanos == 0);
208             }
209         }
210     }
211 
212     /**
213      * Assert all mandatory fields in Gnss Measurement are in expected range.
214      * See mandatory fields in {@code gps.h}.
215      *
216      * @param testLocationManager TestLocationManager
217      * @param measurement GnssMeasurement
218      * @param softAssert  custom SoftAssert
219      * @param timeInNs    event time in ns
220      */
assertAllGnssMeasurementMandatoryFields( TestLocationManager testLocationManager, GnssMeasurement measurement, SoftAssert softAssert, long timeInNs)221     public static void assertAllGnssMeasurementMandatoryFields(
222         TestLocationManager testLocationManager, GnssMeasurement measurement,
223         SoftAssert softAssert, long timeInNs) {
224 
225         verifySvid(measurement, softAssert, timeInNs);
226         verifyReceivedSatelliteVehicleTimeInNs(measurement, softAssert, timeInNs);
227         verifyAccumulatedDeltaRanges(measurement, softAssert, timeInNs);
228 
229         int state = measurement.getState();
230         softAssert.assertTrue("state: Satellite code sync state",
231                 timeInNs,
232                 "X >= 0",
233                 String.valueOf(state),
234                 state >= 0);
235 
236         // Check received_gps_tow_uncertainty_ns
237         softAssert.assertTrueAsWarning("received_gps_tow_uncertainty_ns:" +
238                         " Uncertainty of received GPS Time-of-Week in ns",
239                 timeInNs,
240                 "X > 0",
241                 String.valueOf(measurement.getReceivedSvTimeUncertaintyNanos()),
242                 measurement.getReceivedSvTimeUncertaintyNanos() > 0L);
243 
244         long timeOffsetInSec = TimeUnit.NANOSECONDS.toSeconds(
245                 (long) measurement.getTimeOffsetNanos());
246         softAssert.assertTrue("time_offset_ns: Time offset",
247                 timeInNs,
248                 "-100 seconds < X < +10 seconds",
249                 String.valueOf(measurement.getTimeOffsetNanos()),
250                 (-100 < timeOffsetInSec) && (timeOffsetInSec < 10));
251 
252         softAssert.assertTrue("c_n0_dbhz: Carrier-to-noise density",
253                 timeInNs,
254                 "0.0 >= X <=63",
255                 String.valueOf(measurement.getCn0DbHz()),
256                 measurement.getCn0DbHz() >= 0.0 &&
257                         measurement.getCn0DbHz() <= 63.0);
258 
259         softAssert.assertTrue("pseudorange_rate_uncertainty_mps: " +
260                         "Pseudorange Rate Uncertainty in m/s",
261                 timeInNs,
262                 "X > 0.0",
263                 String.valueOf(
264                         measurement.getPseudorangeRateUncertaintyMetersPerSecond()),
265                 measurement.getPseudorangeRateUncertaintyMetersPerSecond() > 0.0);
266 
267         verifyGnssCarrierFrequency(softAssert, testLocationManager,
268                 measurement.hasCarrierFrequencyHz(),
269                 measurement.hasCarrierFrequencyHz() ? measurement.getCarrierFrequencyHz() : 0F);
270 
271         // Check carrier_phase.
272         if (measurement.hasCarrierPhase()) {
273             softAssert.assertTrue("carrier_phase: Carrier phase",
274                     timeInNs,
275                     "0.0 >= X <= 1.0",
276                     String.valueOf(measurement.getCarrierPhase()),
277                     measurement.getCarrierPhase() >= 0.0 && measurement.getCarrierPhase() <= 1.0);
278         }
279 
280         // Check carrier_phase_uncertainty..
281         if (measurement.hasCarrierPhaseUncertainty()) {
282             softAssert.assertTrue("carrier_phase_uncertainty: 1-Sigma uncertainty of the " +
283                             "carrier-phase",
284                     timeInNs,
285                     "X > 0.0",
286                     String.valueOf(measurement.getCarrierPhaseUncertainty()),
287                     measurement.getCarrierPhaseUncertainty() > 0.0);
288         }
289 
290         // Check GNSS Measurement's multipath_indicator.
291         softAssert.assertTrue("multipath_indicator: GNSS Measurement's multipath indicator",
292                 timeInNs,
293                 "0 >= X <= 2",
294                 String.valueOf(measurement.getMultipathIndicator()),
295                 measurement.getMultipathIndicator() >= 0
296                         && measurement.getMultipathIndicator() <= 2);
297 
298 
299         // Check Signal-to-Noise ratio (SNR).
300         if (measurement.hasSnrInDb()) {
301             softAssert.assertTrue("snr: Signal-to-Noise ratio (SNR) in dB",
302                     timeInNs,
303                     "0.0 >= X <= 63",
304                     String.valueOf(measurement.getSnrInDb()),
305                     measurement.getSnrInDb() >= 0.0 && measurement.getSnrInDb() <= 63);
306         }
307 
308         if (measurement.hasAutomaticGainControlLevelDb()) {
309             softAssert.assertTrue("Automatic Gain Control level in dB",
310                 timeInNs,
311                 "-100 >= X <= 100",
312                 String.valueOf(measurement.getAutomaticGainControlLevelDb()),
313                 measurement.getAutomaticGainControlLevelDb() >= -100
314                     && measurement.getAutomaticGainControlLevelDb() <= 100);
315         }
316     }
317 
318     /**
319      * Assert all SystemApi fields in Gnss Measurement are in expected range.
320      *
321      * @param measurement GnssMeasurement
322      * @param softAssert  custom SoftAssert
323      * @param timeInNs    event time in ns
324      */
assertAllGnssMeasurementSystemFields(GnssMeasurement measurement, SoftAssert softAssert, long timeInNs)325     public static void assertAllGnssMeasurementSystemFields(GnssMeasurement measurement,
326         SoftAssert softAssert, long timeInNs) {
327 
328         if (measurement.hasCorrelationVectors()) {
329             verifyCorrelationVectors(measurement, softAssert, timeInNs);
330         }
331 
332         if (measurement.hasSatellitePvt()) {
333             verifySatellitePvt(measurement, softAssert, timeInNs);
334         }
335     }
336 
337     /**
338      * Verify correlation vectors are in expected range.
339      *
340      * @param measurement GnssMeasurement
341      * @param softAssert  custom SoftAssert
342      * @param timeInNs    event time in ns
343      */
verifyCorrelationVectors(GnssMeasurement measurement, SoftAssert softAssert, long timeInNs)344     private static void verifyCorrelationVectors(GnssMeasurement measurement,
345         SoftAssert softAssert, long timeInNs) {
346         Collection<CorrelationVector> correlationVectors =
347                 measurement.getCorrelationVectors();
348         assertNotNull("CorrelationVectors cannot be null.", correlationVectors);
349         softAssert.assertTrue("CorrelationVectors count",
350                 timeInNs,
351                 "X > 0",
352                 String.valueOf(correlationVectors.size()),
353                 correlationVectors.size() > 0);
354         for (CorrelationVector correlationVector : correlationVectors) {
355             assertNotNull("CorrelationVector cannot be null.", correlationVector);
356             int[] magnitude = correlationVector.getMagnitude();
357             softAssert.assertTrue("frequency_offset_mps : "
358                     + "Frequency offset from reported pseudorange rate "
359                     + "for this CorrelationVector",
360                     timeInNs,
361                     "X >= 0.0",
362                     String.valueOf(correlationVector.getFrequencyOffsetMetersPerSecond()),
363                     correlationVector.getFrequencyOffsetMetersPerSecond() >= 0.0);
364             softAssert.assertTrue("sampling_width_m : "
365                     + "The space between correlation samples in meters",
366                     timeInNs,
367                     "X > 0.0",
368                     String.valueOf(correlationVector.getSamplingWidthMeters()),
369                     correlationVector.getSamplingWidthMeters() > 0.0);
370             softAssert.assertTrue("Magnitude count",
371                     timeInNs,
372                     "X > 0",
373                     String.valueOf(magnitude.length),
374                 magnitude.length > 0);
375             for (int value : magnitude) {
376                 softAssert.assertTrue("magnitude : Data representing normalized "
377                         + "correlation magnitude values",
378                         timeInNs,
379                         "-32768 <= X < 32767",
380                         String.valueOf(value),
381                         value >= -32768 && value < 32767);
382             }
383         }
384     }
385 
386     /**
387      * Verify accumulated delta ranges are in expected range.
388      *
389      * @param measurement GnssMeasurement
390      * @param softAssert  custom SoftAssert
391      * @param timeInNs    event time in ns
392      */
verifyAccumulatedDeltaRanges(GnssMeasurement measurement, SoftAssert softAssert, long timeInNs)393     private static void verifyAccumulatedDeltaRanges(GnssMeasurement measurement,
394         SoftAssert softAssert, long timeInNs) {
395 
396         int accumulatedDeltaRangeState = measurement.getAccumulatedDeltaRangeState();
397         softAssert.assertTrue("accumulated_delta_range_state: " +
398                 "Accumulated delta range state",
399                 timeInNs,
400                 "X & ~ADR_STATE_ALL == 0",
401                 String.valueOf(accumulatedDeltaRangeState),
402                 (accumulatedDeltaRangeState & ~GnssMeasurement.ADR_STATE_ALL) == 0);
403         softAssert.assertTrue("accumulated_delta_range_state: " +
404                 "Accumulated delta range state",
405                 timeInNs,
406                 "ADR_STATE_HALF_CYCLE_REPORTED, or !ADR_STATE_HALF_CYCLE_RESOLVED",
407                 String.valueOf(accumulatedDeltaRangeState),
408                 ((accumulatedDeltaRangeState &
409                   GnssMeasurement.ADR_STATE_HALF_CYCLE_REPORTED) != 0) ||
410                  (accumulatedDeltaRangeState &
411                   GnssMeasurement.ADR_STATE_HALF_CYCLE_RESOLVED) == 0);
412         if ((accumulatedDeltaRangeState & GnssMeasurement.ADR_STATE_VALID) != 0) {
413             double accumulatedDeltaRangeInMeters =
414                     measurement.getAccumulatedDeltaRangeMeters();
415             softAssert.assertTrue("accumulated_delta_range_m: " +
416                     "Accumulated delta range in meter",
417                     timeInNs,
418                     "X != 0.0",
419                     String.valueOf(accumulatedDeltaRangeInMeters),
420                     accumulatedDeltaRangeInMeters != 0.0);
421             double accumulatedDeltaRangeUncertainty =
422                     measurement.getAccumulatedDeltaRangeUncertaintyMeters();
423             softAssert.assertTrue("accumulated_delta_range_uncertainty_m: " +
424                     "Accumulated delta range uncertainty in meter",
425                     timeInNs,
426                     "X > 0.0",
427                     String.valueOf(accumulatedDeltaRangeUncertainty),
428                     accumulatedDeltaRangeUncertainty > 0.0);
429         }
430     }
431 
432     /**
433      * Verify svid's are in expected range.
434      *
435      * @param measurement GnssMeasurement
436      * @param softAssert  custom SoftAssert
437      * @param timeInNs    event time in ns
438      */
verifySvid(GnssMeasurement measurement, SoftAssert softAssert, long timeInNs)439     private static void verifySvid(GnssMeasurement measurement, SoftAssert softAssert,
440         long timeInNs) {
441 
442         int constellationType = measurement.getConstellationType();
443         int svid = measurement.getSvid();
444         validateSvidSub(softAssert, timeInNs, constellationType, svid);
445     }
446 
validateSvidSub(SoftAssert softAssert, Long timeInNs, int constellationType, int svid)447     public static void validateSvidSub(SoftAssert softAssert, Long timeInNs,
448         int constellationType, int svid) {
449 
450         String svidValue = String.valueOf(svid);
451 
452         switch (constellationType) {
453             case GnssStatus.CONSTELLATION_GPS:
454                 softAssert.assertTrue("svid: Space Vehicle ID. Constellation type " +
455                                 "= CONSTELLATION_GPS",
456                         timeInNs,
457                         "[1, 32]",
458                         svidValue,
459                         svid > 0 && svid <= 32);
460                 break;
461             case GnssStatus.CONSTELLATION_SBAS:
462                 softAssert.assertTrue("svid: Space Vehicle ID. Constellation type " +
463                                 "= CONSTELLATION_SBAS",
464                         timeInNs,
465                         "120 <= X <= 192",
466                         svidValue,
467                         svid >= 120 && svid <= 192);
468                 break;
469             case GnssStatus.CONSTELLATION_GLONASS:
470                 softAssert.assertTrue("svid: Slot ID, or if unknown, Frequency + 100 (93-106). " +
471                                 "Constellation type = CONSTELLATION_GLONASS",
472                         timeInNs,
473                         "1 <= svid <= 24 || 93 <= svid <= 106",
474                         svidValue,
475                         (svid >= 1 && svid <= 24) || (svid >= 93 && svid <= 106));
476                 break;
477             case GnssStatus.CONSTELLATION_QZSS:
478                 softAssert.assertTrue("svid: Space Vehicle ID. Constellation type " +
479                                 "= CONSTELLATION_QZSS",
480                         timeInNs,
481                         "193 <= X <= 200",
482                         svidValue,
483                         svid >= 193 && svid <= 200);
484                 break;
485             case GnssStatus.CONSTELLATION_BEIDOU:
486                 softAssert.assertTrue("svid: Space Vehicle ID. Constellation type " +
487                                 "= CONSTELLATION_BEIDOU",
488                         timeInNs,
489                         "1 <= X <= 63",
490                         svidValue,
491                         svid >= 1 && svid <= 63);
492                 break;
493             case GnssStatus.CONSTELLATION_GALILEO:
494                 softAssert.assertTrue("svid: Space Vehicle ID. Constellation type " +
495                                 "= CONSTELLATION_GALILEO",
496                         timeInNs,
497                         "1 <= X <= 36",
498                         String.valueOf(svid),
499                         svid >= 1 && svid <= 36);
500                 break;
501             case GnssStatus.CONSTELLATION_IRNSS:
502                 softAssert.assertTrue("svid: Space Vehicle ID. Constellation type " +
503                                 "= CONSTELLATION_IRNSS",
504                         timeInNs,
505                         "1 <= X <= 14",
506                         String.valueOf(svid),
507                         svid >= 1 && svid <= 14);
508                 break;
509             default:
510                 // Explicit fail if did not receive valid constellation type.
511                 softAssert.assertTrue("svid: Space Vehicle ID. Did not receive any valid " +
512                                 "constellation type.",
513                         timeInNs,
514                         "Valid constellation type.",
515                         svidValue,
516                         false);
517                 break;
518         }
519     }
520 
521     /**
522      * Verify sv times are in expected range.
523      *
524      * @param measurement GnssMeasurement
525      * @param softAssert  custom SoftAssert
526      * @param timeInNs    event time in ns
527      * */
verifyReceivedSatelliteVehicleTimeInNs(GnssMeasurement measurement, SoftAssert softAssert, long timeInNs)528     private static void verifyReceivedSatelliteVehicleTimeInNs(GnssMeasurement measurement,
529         SoftAssert softAssert, long timeInNs) {
530 
531         int constellationType = measurement.getConstellationType();
532         int state = measurement.getState();
533         long received_sv_time_ns = measurement.getReceivedSvTimeNanos();
534         double sv_time_ms = TimeUnit.NANOSECONDS.toMillis(received_sv_time_ns);
535         double sv_time_sec = TimeUnit.NANOSECONDS.toSeconds(received_sv_time_ns);
536         double sv_time_days = TimeUnit.NANOSECONDS.toDays(received_sv_time_ns);
537 
538         // Check ranges for received_sv_time_ns for given Gps State
539         if (state == 0) {
540             softAssert.assertTrue("received_sv_time_ns:" +
541                             " Received SV Time-of-Week in ns." +
542                             " GNSS_MEASUREMENT_STATE_UNKNOWN.",
543                     timeInNs,
544                     "X == 0",
545                     String.valueOf(received_sv_time_ns),
546                     sv_time_ms == 0);
547         }
548 
549         switch (constellationType) {
550             case GnssStatus.CONSTELLATION_GPS:
551                 verifyGpsQzssSvTimes(measurement, softAssert, timeInNs, state, "CONSTELLATION_GPS");
552                 break;
553             case GnssStatus.CONSTELLATION_QZSS:
554                 verifyGpsQzssSvTimes(measurement, softAssert, timeInNs, state,
555                         "CONSTELLATION_QZSS");
556                 break;
557             case GnssStatus.CONSTELLATION_SBAS:
558                 if ((state & GnssMeasurement.STATE_SBAS_SYNC)
559                         == GnssMeasurement.STATE_SBAS_SYNC) {
560                     softAssert.assertTrue(getReceivedSvTimeNsLogMessage(
561                                     "GNSS_MEASUREMENT_STATE_SBAS_SYNC",
562                                     "GnssStatus.CONSTELLATION_SBAS"),
563                             timeInNs,
564                             "0s >= X <= 1s",
565                             String.valueOf(sv_time_sec),
566                             sv_time_sec >= 0 && sv_time_sec <= 1);
567                 } else if ((state & GnssMeasurement.STATE_SYMBOL_SYNC)
568                         == GnssMeasurement.STATE_SYMBOL_SYNC) {
569                     softAssert.assertTrue(getReceivedSvTimeNsLogMessage(
570                                     "GNSS_MEASUREMENT_STATE_SYMBOL_SYNC",
571                                     "GnssStatus.CONSTELLATION_SBAS"),
572                             timeInNs,
573                             "0ms >= X <= 2ms",
574                             String.valueOf(sv_time_ms),
575                             sv_time_ms >= 0 && sv_time_ms <= 2);
576                 } else if ((state & GnssMeasurement.STATE_CODE_LOCK)
577                         == GnssMeasurement.STATE_CODE_LOCK) {
578                     softAssert.assertTrue(getReceivedSvTimeNsLogMessage(
579                                     "GNSS_MEASUREMENT_STATE_CODE_LOCK",
580                                     "GnssStatus.CONSTELLATION_SBAS"),
581                             timeInNs,
582                             "0ms >= X <= 1ms",
583                             String.valueOf(sv_time_ms),
584                             sv_time_ms >= 0 && sv_time_ms <= 1);
585                 }
586                 break;
587             case GnssStatus.CONSTELLATION_GLONASS:
588                 if ((state & GnssMeasurement.STATE_GLO_TOD_DECODED)
589                         == GnssMeasurement.STATE_GLO_TOD_DECODED) {
590                     softAssert.assertTrue(getReceivedSvTimeNsLogMessage(
591                                     "GNSS_MEASUREMENT_STATE_GLO_TOD_DECODED",
592                                     "GnssStatus.CONSTELLATION_GLONASS"),
593                             timeInNs,
594                             "0 day >= X <= 1 day",
595                             String.valueOf(sv_time_days),
596                             sv_time_days >= 0 && sv_time_days <= 1);
597                 } else if ((state & GnssMeasurement.STATE_GLO_TOD_KNOWN)
598                          == GnssMeasurement.STATE_GLO_TOD_KNOWN) {
599                     softAssert.assertTrue(getReceivedSvTimeNsLogMessage(
600                                     "GNSS_MEASUREMENT_STATE_GLO_TOD_KNOWN",
601                                     "GnssStatus.CONSTELLATION_GLONASS"),
602                             timeInNs,
603                             "0 day >= X <= 1 day",
604                             String.valueOf(sv_time_days),
605                             sv_time_days >= 0 && sv_time_days <= 1);
606                 } else if ((state & GnssMeasurement.STATE_GLO_STRING_SYNC)
607                         == GnssMeasurement.STATE_GLO_STRING_SYNC) {
608                     softAssert.assertTrue(getReceivedSvTimeNsLogMessage(
609                                     "GNSS_MEASUREMENT_STATE_GLO_STRING_SYNC",
610                                     "GnssStatus.CONSTELLATION_GLONASS"),
611                             timeInNs,
612                             "0s >= X <= 2s",
613                             String.valueOf(sv_time_sec),
614                             sv_time_sec >= 0 && sv_time_sec <= 2);
615                 } else if ((state & GnssMeasurement.STATE_BIT_SYNC)
616                         == GnssMeasurement.STATE_BIT_SYNC) {
617                     softAssert.assertTrue(getReceivedSvTimeNsLogMessage(
618                                     "GNSS_MEASUREMENT_STATE_BIT_SYNC",
619                                     "GnssStatus.CONSTELLATION_GLONASS"),
620                             timeInNs,
621                             "0ms >= X <= 20ms",
622                             String.valueOf(sv_time_ms),
623                             sv_time_ms >= 0 && sv_time_ms <= 20);
624                 } else if ((state & GnssMeasurement.STATE_SYMBOL_SYNC)
625                         == GnssMeasurement.STATE_SYMBOL_SYNC) {
626                     softAssert.assertTrue(getReceivedSvTimeNsLogMessage(
627                                     "GNSS_MEASUREMENT_STATE_SYMBOL_SYNC",
628                                     "GnssStatus.CONSTELLATION_GLONASS"),
629                             timeInNs,
630                             "0ms >= X <= 10ms",
631                             String.valueOf(sv_time_ms),
632                             sv_time_ms >= 0 && sv_time_ms <= 10);
633                 } else if ((state & GnssMeasurement.STATE_CODE_LOCK)
634                         == GnssMeasurement.STATE_CODE_LOCK) {
635                     softAssert.assertTrue(getReceivedSvTimeNsLogMessage(
636                                     "GNSS_MEASUREMENT_STATE_CODE_LOCK",
637                                     "GnssStatus.CONSTELLATION_GLONASS"),
638                             timeInNs,
639                             "0ms >= X <= 1ms",
640                             String.valueOf(sv_time_ms),
641                             sv_time_ms >= 0 && sv_time_ms <= 1);
642                 }
643                 break;
644             case GnssStatus.CONSTELLATION_GALILEO:
645                 if ((state & GnssMeasurement.STATE_TOW_DECODED)
646                         == GnssMeasurement.STATE_TOW_DECODED) {
647                     softAssert.assertTrue(getReceivedSvTimeNsLogMessage(
648                                     "GNSS_MEASUREMENT_STATE_TOW_DECODED",
649                                     "GnssStatus.CONSTELLATION_GALILEO"),
650                             timeInNs,
651                             "0 >= X <= 7 days",
652                             String.valueOf(sv_time_days),
653                             sv_time_days >= 0 && sv_time_days <= 7);
654                 } else if ((state & GnssMeasurement.STATE_TOW_KNOWN)
655                               == GnssMeasurement.STATE_TOW_KNOWN) {
656                     softAssert.assertTrue(getReceivedSvTimeNsLogMessage(
657                                     "GNSS_MEASUREMENT_STATE_TOW_DECODED",
658                                     "GnssStatus.CONSTELLATION_GALILEO"),
659                         timeInNs,
660                         "0 >= X <= 7 days",
661                         String.valueOf(sv_time_days),
662                         sv_time_days >= 0 && sv_time_days <= 7);
663                 } else if ((state & GnssMeasurement.STATE_GAL_E1B_PAGE_SYNC)
664                         == GnssMeasurement.STATE_GAL_E1B_PAGE_SYNC) {
665                     softAssert.assertTrue(getReceivedSvTimeNsLogMessage(
666                                     "GNSS_MEASUREMENT_STATE_GAL_E1B_PAGE_SYNC",
667                                     "GnssStatus.CONSTELLATION_GALILEO"),
668                             timeInNs,
669                             "0s >= X <= 2s",
670                             String.valueOf(sv_time_sec),
671                             sv_time_sec >= 0 && sv_time_sec <= 2);
672                 } else if ((state & GnssMeasurement.STATE_GAL_E1C_2ND_CODE_LOCK)
673                         == GnssMeasurement.STATE_GAL_E1C_2ND_CODE_LOCK) {
674                     softAssert.assertTrue(getReceivedSvTimeNsLogMessage(
675                                     "GNSS_MEASUREMENT_STATE_GAL_E1C_2ND_CODE_LOCK",
676                                     "GnssStatus.CONSTELLATION_GALILEO"),
677                             timeInNs,
678                             "0ms >= X <= 100ms",
679                             String.valueOf(sv_time_ms),
680                             sv_time_ms >= 0 && sv_time_ms <= 100);
681                 } else if ((state & GnssMeasurement.STATE_GAL_E1BC_CODE_LOCK)
682                         == GnssMeasurement.STATE_GAL_E1BC_CODE_LOCK) {
683                     softAssert.assertTrue(getReceivedSvTimeNsLogMessage(
684                                     "GNSS_MEASUREMENT_STATE_GAL_E1BC_CODE_LOCK",
685                                     "GnssStatus.CONSTELLATION_GALILEO"),
686                             timeInNs,
687                             "0ms >= X <= 4ms",
688                             String.valueOf(sv_time_ms),
689                             sv_time_ms >= 0 && sv_time_ms <= 4);
690                 }
691                 break;
692             case GnssStatus.CONSTELLATION_BEIDOU:
693                 if ((state & GnssMeasurement.STATE_TOW_DECODED)
694                         == GnssMeasurement.STATE_TOW_DECODED) {
695                     softAssert.assertTrue(getReceivedSvTimeNsLogMessage(
696                                     "GNSS_MEASUREMENT_STATE_TOW_DECODED",
697                                     "GnssStatus.CONSTELLATION_BEIDOU"),
698                             timeInNs,
699                             "0 >= X <= 7 days",
700                             String.valueOf(sv_time_days),
701                             sv_time_days >= 0 && sv_time_days <= 7);
702                 } else if ((state & GnssMeasurement.STATE_TOW_KNOWN)
703                         == GnssMeasurement.STATE_TOW_KNOWN) {
704                     softAssert.assertTrue(getReceivedSvTimeNsLogMessage(
705                                     "GNSS_MEASUREMENT_STATE_TOW_KNOWN",
706                                     "GnssStatus.CONSTELLATION_BEIDOU"),
707                             timeInNs,
708                             "0 >= X <= 7 days",
709                             String.valueOf(sv_time_days),
710                             sv_time_days >= 0 && sv_time_days <= 7);
711                 } else if ((state & GnssMeasurement.STATE_SUBFRAME_SYNC)
712                         == GnssMeasurement.STATE_SUBFRAME_SYNC) {
713                     softAssert.assertTrue(getReceivedSvTimeNsLogMessage(
714                                     "GNSS_MEASUREMENT_STATE_SUBFRAME_SYNC",
715                                     "GnssStatus.CONSTELLATION_BEIDOU"),
716                             timeInNs,
717                             "0s >= X <= 6s",
718                             String.valueOf(sv_time_sec),
719                             sv_time_sec >= 0 && sv_time_sec <= 6);
720                 } else if ((state & GnssMeasurement.STATE_BDS_D2_SUBFRAME_SYNC)
721                         == GnssMeasurement.STATE_BDS_D2_SUBFRAME_SYNC) {
722                     softAssert.assertTrue(getReceivedSvTimeNsLogMessage(
723                                     "GNSS_MEASUREMENT_STATE_BDS_D2_SUBFRAME_SYNC",
724                                     "GnssStatus.CONSTELLATION_BEIDOU"),
725                             timeInNs,
726                             "0ms >= X <= 600ms (0.6sec)",
727                             String.valueOf(sv_time_ms),
728                             sv_time_ms >= 0 && sv_time_ms <= 600);
729                 } else if ((state & GnssMeasurement.STATE_BIT_SYNC)
730                         == GnssMeasurement.STATE_BIT_SYNC) {
731                     softAssert.assertTrue(getReceivedSvTimeNsLogMessage(
732                                     "GNSS_MEASUREMENT_STATE_BIT_SYNC",
733                                     "GnssStatus.CONSTELLATION_BEIDOU"),
734                             timeInNs,
735                             "0ms >= X <= 20ms",
736                             String.valueOf(sv_time_ms),
737                             sv_time_ms >= 0 && sv_time_ms <= 20);
738                 } else if ((state & GnssMeasurement.STATE_BDS_D2_BIT_SYNC)
739                         == GnssMeasurement.STATE_BDS_D2_BIT_SYNC) {
740                     softAssert.assertTrue(getReceivedSvTimeNsLogMessage(
741                                     "GNSS_MEASUREMENT_STATE_BDS_D2_BIT_SYNC",
742                                     "GnssStatus.CONSTELLATION_BEIDOU"),
743                             timeInNs,
744                             "0ms >= X <= 2ms",
745                             String.valueOf(sv_time_ms),
746                             sv_time_ms >= 0 && sv_time_ms <= 2);
747                 } else if ((state & GnssMeasurement.STATE_CODE_LOCK)
748                         == GnssMeasurement.STATE_CODE_LOCK) {
749                     softAssert.assertTrue(getReceivedSvTimeNsLogMessage(
750                                     "GNSS_MEASUREMENT_STATE_CODE_LOCK",
751                                     "GnssStatus.CONSTELLATION_BEIDOU"),
752                             timeInNs,
753                             "0ms >= X <= 1ms",
754                             String.valueOf(sv_time_ms),
755                             sv_time_ms >= 0 && sv_time_ms <= 1);
756                 }
757                 break;
758         }
759     }
760 
getReceivedSvTimeNsLogMessage(String state, String constellationType)761     private static String getReceivedSvTimeNsLogMessage(String state, String constellationType) {
762         return "received_sv_time_ns: Received SV Time-of-Week in ns. Constellation type = "
763                 + constellationType + ". State = " + state;
764     }
765 
766     /**
767      * Verify sv times are in expected range for given constellation type.
768      * This is common check for CONSTELLATION_GPS & CONSTELLATION_QZSS.
769      *
770      * @param measurement GnssMeasurement
771      * @param softAssert  custom SoftAssert
772      * @param timeInNs    event time in ns
773      * @param state       GnssMeasurement State
774      * @param constellationType Gnss Constellation type
775      */
verifyGpsQzssSvTimes(GnssMeasurement measurement, SoftAssert softAssert, long timeInNs, int state, String constellationType)776     private static void verifyGpsQzssSvTimes(GnssMeasurement measurement,
777         SoftAssert softAssert, long timeInNs, int state, String constellationType) {
778 
779         long received_sv_time_ns = measurement.getReceivedSvTimeNanos();
780         double sv_time_ms = TimeUnit.NANOSECONDS.toMillis(received_sv_time_ns);
781         double sv_time_sec = TimeUnit.NANOSECONDS.toSeconds(received_sv_time_ns);
782         double sv_time_days = TimeUnit.NANOSECONDS.toDays(received_sv_time_ns);
783 
784         if ((state & GnssMeasurement.STATE_TOW_DECODED)
785                 == GnssMeasurement.STATE_TOW_DECODED) {
786             softAssert.assertTrue(getReceivedSvTimeNsLogMessage(
787                             "GNSS_MEASUREMENT_STATE_TOW_DECODED",
788                             constellationType),
789                     timeInNs,
790                     "0 >= X <= 7 days",
791                     String.valueOf(sv_time_days),
792                     sv_time_days >= 0 && sv_time_days <= 7);
793         } else if ((state & GnssMeasurement.STATE_TOW_KNOWN)
794                 == GnssMeasurement.STATE_TOW_KNOWN) {
795             softAssert.assertTrue(getReceivedSvTimeNsLogMessage(
796                             "GNSS_MEASUREMENT_STATE_TOW_KNOWN",
797                             constellationType),
798                     timeInNs,
799                     "0 >= X <= 7 days",
800                     String.valueOf(sv_time_days),
801                     sv_time_days >= 0 && sv_time_days <= 7);
802         } else if ((state & GnssMeasurement.STATE_SUBFRAME_SYNC)
803                 == GnssMeasurement.STATE_SUBFRAME_SYNC) {
804             softAssert.assertTrue(getReceivedSvTimeNsLogMessage(
805                             "GNSS_MEASUREMENT_STATE_SUBFRAME_SYNC",
806                             constellationType),
807                     timeInNs,
808                     "0s >= X <= 6s",
809                     String.valueOf(sv_time_sec),
810                     sv_time_sec >= 0 && sv_time_sec <= 6);
811         } else if ((state & GnssMeasurement.STATE_BIT_SYNC)
812                 == GnssMeasurement.STATE_BIT_SYNC) {
813             softAssert.assertTrue(getReceivedSvTimeNsLogMessage(
814                     "GNSS_MEASUREMENT_STATE_BIT_SYNC",
815                     constellationType),
816                     timeInNs,
817                     "0ms >= X <= 20ms",
818                     String.valueOf(sv_time_ms),
819                     sv_time_ms >= 0 && sv_time_ms <= 20);
820 
821         } else if ((state & GnssMeasurement.STATE_2ND_CODE_LOCK)
822                 == GnssMeasurement.STATE_2ND_CODE_LOCK) {
823             int maxReceivedSvTimeMs = -1;
824             if (isGpsL5OrQzssJ5I(measurement)) {
825                 maxReceivedSvTimeMs = 10;
826             } else if (isGpsL5OrQzssJ5Q(measurement)) {
827                 maxReceivedSvTimeMs = 20;
828             } else if (isGalileoE1C(measurement)) {
829                 maxReceivedSvTimeMs = 100;
830             } else if (isGalileoE5AQ(measurement)) {
831                 maxReceivedSvTimeMs = 100;
832             } else {
833                 softAssert.assertTrue(
834                         "Signal type does not have secondary code but has have "
835                                 + "STATE_2ND_CODE_LOCK state set. constellation="
836                                 + measurement.getConstellationType()
837                                 + ", carrierFrequencyHz=" + measurement.getCarrierFrequencyHz()
838                                 + ", codeType=" + measurement.getCodeType()
839                         , false);
840             }
841 
842             softAssert.assertTrue(getReceivedSvTimeNsLogMessage(
843                     "GNSS_MEASUREMENT_STATE_2ND_CODE_LOCK",
844                     constellationType),
845                     timeInNs,
846                     "0ms >= X <= " + maxReceivedSvTimeMs + "ms",
847                     String.valueOf(sv_time_ms),
848                     sv_time_ms >= 0 && sv_time_ms <= maxReceivedSvTimeMs);
849         } else if ((state & GnssMeasurement.STATE_CODE_LOCK)
850                 == GnssMeasurement.STATE_CODE_LOCK) {
851             softAssert.assertTrue(getReceivedSvTimeNsLogMessage(
852                             "GNSS_MEASUREMENT_STATE_CODE_LOCK",
853                             constellationType),
854                     timeInNs,
855                     "0ms >= X <= 1ms",
856                     String.valueOf(sv_time_ms),
857                     sv_time_ms >= 0 && sv_time_ms <= 1);
858         }
859     }
860 
861 
862     /**
863      * Get a unique string for the SV including the constellation and the default L1 band.
864      *
865      * @param constellationType Gnss Constellation type
866      * @param svId Gnss Sv Identifier
867      */
getUniqueSvStringId(int constellationType, int svId)868     public static String getUniqueSvStringId(int constellationType, int svId) {
869         return getUniqueSvStringId(constellationType, svId, GnssBand.GNSS_L1);
870     }
871 
872     /**
873      * Get a unique string for the SV including the constellation and the band.
874      *
875      * @param constellationType Gnss Constellation type
876      * @param svId Gnss Sv Identifier
877      * @param carrierFrequencyHz Carrier Frequency for Sv in Hz
878      */
getUniqueSvStringId(int constellationType, int svId, float carrierFrequencyHz)879     public static String getUniqueSvStringId(int constellationType, int svId,
880         float carrierFrequencyHz) {
881         return getUniqueSvStringId(constellationType, svId,
882             frequencyToGnssBand(carrierFrequencyHz));
883     }
884 
getUniqueSvStringId(int constellationType, int svId, GnssBand gnssBand)885     private static String getUniqueSvStringId(int constellationType, int svId, GnssBand gnssBand) {
886         return gnssBand.toString() + "." + constellationType + "." + svId;
887     }
888 
889     /**
890      * Assert all mandatory fields in Gnss Navigation Message are in expected range.
891      * See mandatory fields in {@code gps.h}.
892      *
893      * @param testLocationManager TestLocationManager
894      * @param events GnssNavigationMessageEvents
895      */
verifyGnssNavMessageMandatoryField(TestLocationManager testLocationManager, List<GnssNavigationMessage> events)896     public static void verifyGnssNavMessageMandatoryField(TestLocationManager testLocationManager,
897                                                           List<GnssNavigationMessage> events) {
898         // Verify mandatory GnssNavigationMessage field values.
899         SoftAssert softAssert = new SoftAssert(TAG);
900         for (GnssNavigationMessage message : events) {
901             int type = message.getType();
902             softAssert.assertTrue("Gnss Navigation Message Type:expected [" +
903                 getGnssNavMessageTypes() + "] actual = " + type,
904                     GNSS_NAVIGATION_MESSAGE_TYPE.contains(type));
905 
906             int messageType = message.getType();
907             softAssert.assertTrue("Message ID cannot be 0", message.getMessageId() != 0);
908             if (messageType == GnssNavigationMessage.TYPE_GAL_I) {
909                 softAssert.assertTrue("Sub Message ID can not be negative.",
910                     message.getSubmessageId() >= 0);
911             } else {
912                 softAssert.assertTrue("Sub Message ID has to be greater than 0.",
913                     message.getSubmessageId() > 0);
914             }
915 
916             // if message type == TYPE_L1CA, verify PRN & Data Size.
917             if (messageType == GnssNavigationMessage.TYPE_GPS_L1CA) {
918                 int svid = message.getSvid();
919                 softAssert.assertTrue("Space Vehicle ID : expected = [1, 32], actual = " +
920                                 svid,
921                         svid >= 1 && svid <= 32);
922                 int dataSize = message.getData().length;
923                 softAssert.assertTrue("Data size: expected = 40, actual = " + dataSize,
924                         dataSize == 40);
925             } else {
926                 Log.i(TAG, "GnssNavigationMessage (type = " + messageType
927                         + ") skipped for verification.");
928             }
929         }
930         softAssert.assertAll();
931     }
932 
933     /**
934      * Asserts presence of CarrierFrequency and the values are in expected range.
935      * As per CDD 7.3.3 / C-3-3 Year 2107+ should have Carrier Frequency present
936      * As of 2018, per http://www.navipedia.net/index.php/GNSS_signal, all known GNSS bands
937      * lie within 2 frequency ranges [1100-1300] & [1500-1700].
938      *
939      * @param softAssert custom SoftAssert
940      * @param testLocationManager TestLocationManager
941      * @param hasCarrierFrequency Whether carrierFrequency is present
942      * @param carrierFrequencyHz Value of carrier frequency in Hz if hasCarrierFrequency is true.
943      *                              It is ignored when hasCarrierFrequency is false.
944      */
verifyGnssCarrierFrequency(SoftAssert softAssert, TestLocationManager testLocationManager, boolean hasCarrierFrequency, float carrierFrequencyHz)945     public static void verifyGnssCarrierFrequency(SoftAssert softAssert,
946         TestLocationManager testLocationManager,
947         boolean hasCarrierFrequency, float carrierFrequencyHz) {
948 
949         if (hasCarrierFrequency) {
950             float frequencyMhz = carrierFrequencyHz/1e6F;
951             softAssert.assertTrue("carrier_frequency_mhz: Carrier frequency in Mhz",
952                 "1100 < X < 1300 || 1500 < X < 1700",
953                 String.valueOf(frequencyMhz),
954                 (frequencyMhz > 1100.0 && frequencyMhz < 1300.0) ||
955                     (frequencyMhz > 1500.0 && frequencyMhz < 1700.0));
956         }
957     }
958 
getGnssNavMessageTypes()959     private static String getGnssNavMessageTypes() {
960         StringBuilder typesStr = new StringBuilder();
961         for (int type : GNSS_NAVIGATION_MESSAGE_TYPE) {
962             typesStr.append(String.format("0x%04X", type));
963             typesStr.append(", ");
964         }
965 
966         return typesStr.length() > 2 ? typesStr.substring(0, typesStr.length() - 2) : "";
967     }
968 
969     /**
970      * The band information is as of 2018, per http://www.navipedia.net/index.php/GNSS_signal
971      * Bands are combined for simplicity as the constellation is also tracked.
972      *
973      * @param frequencyHz Frequency in Hz
974      * @return GnssBand where the frequency lies.
975      */
frequencyToGnssBand(float frequencyHz)976     private static GnssBand frequencyToGnssBand(float frequencyHz) {
977         float frequencyMhz = frequencyHz/1e6F;
978         if (frequencyMhz >= 1151 && frequencyMhz <= 1214) {
979             return GnssBand.GNSS_L5;
980         }
981         if (frequencyMhz > 1214 && frequencyMhz <= 1255) {
982             return GnssBand.GNSS_L2;
983         }
984         if (frequencyMhz > 1255 && frequencyMhz <= 1300) {
985             return GnssBand.GNSS_E6;
986         }
987         return GnssBand.GNSS_L1; // default to L1 band
988     }
989 
990     /**
991      * Assert most of the fields in Satellite PVT are in expected range.
992      *
993      * @param measurement GnssMeasurement
994      * @param softAssert  custom SoftAssert
995      * @param timeInNs    event time in ns
996      */
verifySatellitePvt(GnssMeasurement measurement, SoftAssert softAssert, long timeInNs)997     private static void verifySatellitePvt(GnssMeasurement measurement,
998         SoftAssert softAssert, long timeInNs) {
999         SatellitePvt satellitePvt = measurement.getSatellitePvt();
1000         assertNotNull("SatellitePvt cannot be null when HAS_SATELLITE_PVT is true.", satellitePvt);
1001 
1002         if (satellitePvt.hasPositionVelocityClockInfo()){
1003             assertNotNull("PositionEcef cannot be null when "
1004                     + "HAS_POSITION_VELOCITY_CLOCK_INFO is true.", satellitePvt.getPositionEcef());
1005             assertNotNull("VelocityEcef cannot be null when "
1006                     + "HAS_POSITION_VELOCITY_CLOCK_INFO is true.", satellitePvt.getVelocityEcef());
1007             assertNotNull("ClockInfo cannot be null when "
1008                     + "HAS_POSITION_VELOCITY_CLOCK_INFO is true.", satellitePvt.getClockInfo());
1009             softAssert.assertTrue("x_meters : "
1010                     + "Satellite position X in WGS84 ECEF (meters)",
1011                     timeInNs,
1012                     "-43000000 <= X <= 43000000",
1013                     String.valueOf(satellitePvt.getPositionEcef().getXMeters()),
1014                     satellitePvt.getPositionEcef().getXMeters() >= -43000000 &&
1015                         satellitePvt.getPositionEcef().getXMeters() <= 43000000);
1016             softAssert.assertTrue("y_meters : "
1017                     + "Satellite position Y in WGS84 ECEF (meters)",
1018                     timeInNs,
1019                     "-43000000 <= X <= 43000000",
1020                     String.valueOf(satellitePvt.getPositionEcef().getYMeters()),
1021                     satellitePvt.getPositionEcef().getYMeters() >= -43000000 &&
1022                         satellitePvt.getPositionEcef().getYMeters() <= 43000000);
1023             softAssert.assertTrue("z_meters : "
1024                     + "Satellite position Z in WGS84 ECEF (meters)",
1025                     timeInNs,
1026                     "-43000000 <= X <= 43000000",
1027                     String.valueOf(satellitePvt.getPositionEcef().getZMeters()),
1028                     satellitePvt.getPositionEcef().getZMeters() >= -43000000 &&
1029                         satellitePvt.getPositionEcef().getZMeters() <= 43000000);
1030             softAssert.assertTrue("ure_meters : "
1031                     + "The Signal in Space User Range Error (URE) (meters)",
1032                     timeInNs,
1033                     "X > 0",
1034                     String.valueOf(satellitePvt.getPositionEcef().getUreMeters()),
1035                     satellitePvt.getPositionEcef().getUreMeters() > 0);
1036             softAssert.assertTrue("x_mps : "
1037                     + "Satellite velocity X in WGS84 ECEF (meters per second)",
1038                     timeInNs,
1039                     "-4000 <= X <= 4000",
1040                     String.valueOf(satellitePvt.getVelocityEcef().getXMetersPerSecond()),
1041                     satellitePvt.getVelocityEcef().getXMetersPerSecond() >= -4000 &&
1042                         satellitePvt.getVelocityEcef().getXMetersPerSecond() <= 4000);
1043             softAssert.assertTrue("y_mps : "
1044                     + "Satellite velocity Y in WGS84 ECEF (meters per second)",
1045                     timeInNs,
1046                     "-4000 <= X <= 4000",
1047                     String.valueOf(satellitePvt.getVelocityEcef().getYMetersPerSecond()),
1048                     satellitePvt.getVelocityEcef().getYMetersPerSecond() >= -4000 &&
1049                         satellitePvt.getVelocityEcef().getYMetersPerSecond() <= 4000);
1050             softAssert.assertTrue("z_mps : "
1051                     + "Satellite velocity Z in WGS84 ECEF (meters per second)",
1052                     timeInNs,
1053                     "-4000 <= X <= 4000",
1054                     String.valueOf(satellitePvt.getVelocityEcef().getZMetersPerSecond()),
1055                     satellitePvt.getVelocityEcef().getZMetersPerSecond() >= -4000 &&
1056                         satellitePvt.getVelocityEcef().getZMetersPerSecond() <= 4000);
1057             softAssert.assertTrue("ure_rate_mps : "
1058                     + "The Signal in Space User Range Error Rate (URE Rate) (meters per second)",
1059                     timeInNs,
1060                     "X > 0",
1061                     String.valueOf(satellitePvt.getVelocityEcef().getUreRateMetersPerSecond()),
1062                     satellitePvt.getVelocityEcef().getUreRateMetersPerSecond() > 0);
1063             softAssert.assertTrue("hardware_code_bias_meters : "
1064                     + "The satellite hardware code bias of the reported code type "
1065                     + "w.r.t ionosphere-free measurement in meters.",
1066                     timeInNs,
1067                     "-17.869 < X < 17.729",
1068                     String.valueOf(satellitePvt.getClockInfo().getHardwareCodeBiasMeters()),
1069                     satellitePvt.getClockInfo().getHardwareCodeBiasMeters() > -17.869 &&
1070                     satellitePvt.getClockInfo().getHardwareCodeBiasMeters() < 17.729);
1071             softAssert.assertTrue("time_correction_meters : "
1072                     + "The satellite time correction for ionospheric-free signal measurement "
1073                     + "(meters)",
1074                     timeInNs,
1075                     "-3e6 < X < 3e6",
1076                     String.valueOf(satellitePvt.getClockInfo().getTimeCorrectionMeters()),
1077                     satellitePvt.getClockInfo().getTimeCorrectionMeters() > -3e6 &&
1078                     satellitePvt.getClockInfo().getTimeCorrectionMeters() < 3e6);
1079             softAssert.assertTrue("clock_drift_mps : "
1080                     + "The satellite clock drift (meters per second)",
1081                     timeInNs,
1082                     "-1.117 < X < 1.117",
1083                     String.valueOf(satellitePvt.getClockInfo().getClockDriftMetersPerSecond()),
1084                     satellitePvt.getClockInfo().getClockDriftMetersPerSecond() > -1.117 &&
1085                     satellitePvt.getClockInfo().getClockDriftMetersPerSecond() < 1.117);
1086         }
1087 
1088         if (satellitePvt.hasIono()){
1089             softAssert.assertTrue("iono_delay_meters : "
1090                     + "The ionospheric delay in meters",
1091                     timeInNs,
1092                     "0 < X < 100",
1093                     String.valueOf(satellitePvt.getIonoDelayMeters()),
1094                     satellitePvt.getIonoDelayMeters() > 0 &&
1095                     satellitePvt.getIonoDelayMeters() < 100);
1096         }
1097 
1098         if (satellitePvt.hasTropo()){
1099             softAssert.assertTrue("tropo_delay_meters : "
1100                     + "The tropospheric delay in meters",
1101                     timeInNs,
1102                     "0 < X < 100",
1103                     String.valueOf(satellitePvt.getTropoDelayMeters()),
1104                     satellitePvt.getTropoDelayMeters() > 0 &&
1105                     satellitePvt.getTropoDelayMeters() < 100);
1106         }
1107     }
1108 
isGpsL5OrQzssJ5I(GnssMeasurement measurement)1109     private static boolean isGpsL5OrQzssJ5I(GnssMeasurement measurement) {
1110         return (measurement.getConstellationType() == GnssStatus.CONSTELLATION_GPS ||
1111                 measurement.getConstellationType() == GnssStatus.CONSTELLATION_QZSS)
1112                 && GPS_L5_QZSS_J5_FREQ_RANGE_HZ.contains(
1113                 (double) measurement.getCarrierFrequencyHz())
1114                 && measurement.hasCodeType()
1115                 && "I".equals(measurement.getCodeType());
1116     }
1117 
isGpsL5OrQzssJ5Q(GnssMeasurement measurement)1118     private static boolean isGpsL5OrQzssJ5Q(GnssMeasurement measurement) {
1119         return (measurement.getConstellationType() == GnssStatus.CONSTELLATION_GPS ||
1120                 measurement.getConstellationType() == GnssStatus.CONSTELLATION_QZSS)
1121                 && GPS_L5_QZSS_J5_FREQ_RANGE_HZ.contains(
1122                 (double) measurement.getCarrierFrequencyHz())
1123                 && measurement.hasCodeType()
1124                 && "Q".equals(measurement.getCodeType());
1125     }
1126 
isGalileoE1C(GnssMeasurement measurement)1127     private static boolean isGalileoE1C(GnssMeasurement measurement) {
1128         return measurement.getConstellationType() == GnssStatus.CONSTELLATION_GALILEO
1129                 && GAL_E1_FREQ_RANGE_HZ.contains((double) measurement.getCarrierFrequencyHz())
1130                 && measurement.hasCodeType()
1131                 && "C".equals(measurement.getCodeType());
1132     }
1133 
isGalileoE5AQ(GnssMeasurement measurement)1134     private static boolean isGalileoE5AQ(GnssMeasurement measurement) {
1135         return measurement.getConstellationType() == GnssStatus.CONSTELLATION_GALILEO
1136                 && GAL_E5_FREQ_RANGE_HZ.contains((double) measurement.getCarrierFrequencyHz())
1137                 && measurement.hasCodeType()
1138                 && "Q".equals(measurement.getCodeType());
1139     }
1140 }
1141