30179df8e6
Remove the fixed pseudorange start offset, preserve per-PRN navigation assistance across RF loss and power cycles, and seed coherent navigation measurement phase on reacquisition. Add full LNAV almanac visibility scheduling, power-safe QSPI journaling, RF-domain reset isolation, and PPS Gray CDC hardening with Linux, sanitizer, RTL, and end-to-end coverage.
176 lines
5.8 KiB
C
176 lines
5.8 KiB
C
#include <assert.h>
|
|
#include <math.h>
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include "gps/gps_assistance_data.h"
|
|
|
|
static gps_ephemeris_t make_ephemeris(
|
|
unsigned int prn,
|
|
int current_week,
|
|
double toe_sec,
|
|
unsigned int issue)
|
|
{
|
|
gps_ephemeris_t ephemeris;
|
|
memset(&ephemeris, 0, sizeof(ephemeris));
|
|
ephemeris.WN = (unsigned int)current_week & 0x3ffu;
|
|
ephemeris.toe.sec = toe_sec;
|
|
ephemeris.toc.sec = toe_sec;
|
|
ephemeris.sqrta = 5153.795;
|
|
ephemeris.A = ephemeris.sqrta * ephemeris.sqrta;
|
|
ephemeris.ecc = 0.01;
|
|
ephemeris.iodc = issue;
|
|
ephemeris.iode = (unsigned char)issue;
|
|
ephemeris.SVID = (unsigned char)prn;
|
|
ephemeris.ephemeris_valid = true;
|
|
ephemeris.abs_sample = 123456789.25;
|
|
return ephemeris;
|
|
}
|
|
|
|
static void test_fresh_mixed_ephemeris_restore_and_true_remaining_age(void)
|
|
{
|
|
const gpstime_t saved_time = {.week = 2300, .sec = 100000.0};
|
|
gps_ephemeris_cache_t source;
|
|
gps_ephemeris_cache_init(&source, GPS_EPHEMERIS_CACHE_MAX_AGE_TICKS);
|
|
for(unsigned int prn = 0u ; prn < 7u ; prn++)
|
|
{
|
|
double age_sec = (prn == 6u) ? 3.0 * 3600.0 :
|
|
(double)(prn + 1u) * 600.0;
|
|
gps_ephemeris_t ephemeris = make_ephemeris(
|
|
prn, saved_time.week, saved_time.sec - age_sec,
|
|
40u + prn);
|
|
assert(gps_ephemeris_cache_store(
|
|
&source, prn, &ephemeris, 100u));
|
|
}
|
|
gps_almanac_cache_t source_almanac;
|
|
gps_almanac_cache_init(
|
|
&source_almanac, GPS_ALMANAC_CACHE_MAX_AGE_TICKS);
|
|
gps_assistance_payload_t payload;
|
|
gps_assistance_payload_build(
|
|
&payload, &source, &source_almanac, NULL, NULL, saved_time);
|
|
assert(payload.ephemeris[0].abs_sample == 0.0);
|
|
|
|
gps_ephemeris_cache_t restored_cache;
|
|
gps_ephemeris_cache_init(
|
|
&restored_cache, GPS_EPHEMERIS_CACHE_MAX_AGE_TICKS);
|
|
gps_almanac_cache_t restored_almanac;
|
|
gps_almanac_cache_init(
|
|
&restored_almanac, GPS_ALMANAC_CACHE_MAX_AGE_TICKS);
|
|
gps_assistance_restore_result_t result;
|
|
const uint64_t boot_now = 10u;
|
|
assert(gps_assistance_restore(
|
|
&payload, saved_time, boot_now,
|
|
&restored_cache, &restored_almanac, &result));
|
|
assert(result.ephemeris_mask == UINT32_C(0x3f));
|
|
assert(!restored_cache.entry[0].time_anchor.valid);
|
|
|
|
gps_ephemeris_t output;
|
|
assert(gps_ephemeris_cache_restore(
|
|
&restored_cache, 0u, boot_now, &output));
|
|
/* PRN0 was already 10 minutes old. It has 110 minutes, not a newly
|
|
* granted two hours, after reboot. */
|
|
uint64_t remaining_ticks = GPS_EPHEMERIS_CACHE_MAX_AGE_TICKS -
|
|
600u * GPS_SYSTICK_HZ;
|
|
assert(gps_ephemeris_cache_restore(
|
|
&restored_cache, 0u, boot_now + remaining_ticks, &output));
|
|
assert(!gps_ephemeris_cache_restore(
|
|
&restored_cache, 0u, boot_now + remaining_ticks + 1u, &output));
|
|
}
|
|
|
|
static void test_issue_check_is_per_satellite_and_per_subframe(void)
|
|
{
|
|
gps_ephemeris_t ephemeris = make_ephemeris(3u, 2300, 90000.0, 0x155u);
|
|
unsigned int words[10] = {0};
|
|
words[2] = ((ephemeris.iodc >> 8) & 0x03u) << 6;
|
|
words[7] = (ephemeris.iodc & 0xffu) << 22;
|
|
assert(gps_assistance_ephemeris_issue_matches(
|
|
&ephemeris, 1u, words));
|
|
words[7] ^= UINT32_C(1) << 22;
|
|
assert(!gps_assistance_ephemeris_issue_matches(
|
|
&ephemeris, 1u, words));
|
|
|
|
memset(words, 0, sizeof(words));
|
|
words[2] = (uint32_t)ephemeris.iode << 22;
|
|
assert(gps_assistance_ephemeris_issue_matches(
|
|
&ephemeris, 2u, words));
|
|
words[2] += UINT32_C(1) << 22;
|
|
assert(!gps_assistance_ephemeris_issue_matches(
|
|
&ephemeris, 2u, words));
|
|
assert(gps_assistance_ephemeris_issue_matches(
|
|
&ephemeris, 4u, words));
|
|
}
|
|
|
|
static void test_iono_position_and_almanac_independent_staleness(void)
|
|
{
|
|
const gpstime_t saved_time = {.week = 2400, .sec = 200000.0};
|
|
gps_ephemeris_cache_t source;
|
|
gps_ephemeris_cache_init(&source, GPS_EPHEMERIS_CACHE_MAX_AGE_TICKS);
|
|
gps_almanac_cache_t almanac;
|
|
gps_almanac_cache_init(&almanac, GPS_ALMANAC_CACHE_MAX_AGE_TICKS);
|
|
almanac.reference_valid = true;
|
|
almanac.reference_week_mod256 = (unsigned int)saved_time.week & 0xffu;
|
|
almanac.reference_toa_sec = saved_time.sec - 86400.0;
|
|
almanac.reference_tick = 50u;
|
|
ionoutc_t ionoutc = {.enable = true, .vflg = true, .dtls = 18};
|
|
gps_assistance_position_t position = {
|
|
.ecef_m = {4200000.0, 2800000.0, 3900000.0},
|
|
.latitude_deg = 41.0,
|
|
.longitude_deg = 29.0,
|
|
.height_m = 100.0,
|
|
.gps_time = saved_time,
|
|
.valid = true
|
|
};
|
|
gps_assistance_payload_t payload;
|
|
gps_assistance_payload_build(
|
|
&payload, &source, &almanac, &ionoutc, &position, saved_time);
|
|
|
|
gps_ephemeris_cache_t output_eph;
|
|
gps_almanac_cache_t output_almanac;
|
|
gps_ephemeris_cache_init(
|
|
&output_eph, GPS_EPHEMERIS_CACHE_MAX_AGE_TICKS);
|
|
gps_almanac_cache_init(
|
|
&output_almanac, GPS_ALMANAC_CACHE_MAX_AGE_TICKS);
|
|
gps_assistance_restore_result_t result;
|
|
gpstime_t current = saved_time;
|
|
current.sec += 2.0 * 86400.0;
|
|
assert(gps_assistance_restore(
|
|
&payload, current, 20u,
|
|
&output_eph, &output_almanac, &result));
|
|
assert(result.almanac_restored);
|
|
assert(result.ionoutc_restored);
|
|
assert(result.position_restored);
|
|
assert(result.ionoutc.dtls == 18);
|
|
|
|
/* Eight days rejects almanac and iono, while a coarse position remains a
|
|
* safe acquisition/WLS seed under the separate 30-day policy. */
|
|
current = saved_time;
|
|
current.week += 1;
|
|
current.sec += 86400.0;
|
|
if(current.sec >= 604800.0)
|
|
{
|
|
current.sec -= 604800.0;
|
|
current.week++;
|
|
}
|
|
gps_ephemeris_cache_init(
|
|
&output_eph, GPS_EPHEMERIS_CACHE_MAX_AGE_TICKS);
|
|
gps_almanac_cache_init(
|
|
&output_almanac, GPS_ALMANAC_CACHE_MAX_AGE_TICKS);
|
|
assert(gps_assistance_restore(
|
|
&payload, current, 20u,
|
|
&output_eph, &output_almanac, &result));
|
|
assert(!result.almanac_restored);
|
|
assert(!result.ionoutc_restored);
|
|
assert(result.position_restored);
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
test_fresh_mixed_ephemeris_restore_and_true_remaining_age();
|
|
test_issue_check_is_per_satellite_and_per_subframe();
|
|
test_iono_position_and_almanac_independent_staleness();
|
|
puts("gps assistance freshness/issue tests passed");
|
|
return 0;
|
|
}
|