Files
gps/soc/software/fusion/tests/gps_reacquisition_test.c
T

179 lines
5.5 KiB
C

#include "fusion/fusion_engine.h"
#include <math.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#define TEST_TIMEBASE_HZ 1000000u
#define TEST_IMU_HZ 52u
#define TEST_GPS_PERIOD_TICKS 200000u
static fusion_imu_measurement_t stationary_imu(uint64_t timestamp_ticks,
double gravity_mps2)
{
fusion_imu_measurement_t imu;
memset(&imu, 0, sizeof(imu));
imu.timestamp_ticks = timestamp_ticks;
imu.accel_mps2[2] = gravity_mps2;
imu.temperature_c = 25.0;
imu.vehicle_at_rest = true;
return imu;
}
static fusion_position_measurement_t gps_position(
uint64_t timestamp_ticks,
double east_m,
double north_m,
double up_m)
{
fusion_position_measurement_t gps;
memset(&gps, 0, sizeof(gps));
gps.timestamp_ticks = timestamp_ticks;
gps.position_enu_m[0] = east_m;
gps.position_enu_m[1] = north_m;
gps.position_enu_m[2] = up_m;
gps.covariance_enu_m2[0] = 4.0;
gps.covariance_enu_m2[4] = 4.0;
gps.covariance_enu_m2[8] = 16.0;
gps.source_quality = 100.0;
gps.source_valid = true;
return gps;
}
static bool calibrate_at_origin(fusion_engine_t* engine,
fusion_engine_config_t* config)
{
fusion_engine_default_config(config, TEST_TIMEBASE_HZ);
if(!fusion_engine_init(engine, config))
{
return false;
}
fusion_position_measurement_t origin = gps_position(1u, 0.0, 0.0, 0.0);
if(!fusion_engine_push_position(engine, &origin, NULL, NULL))
{
return false;
}
for(uint64_t sample = 0u ; sample <= 3u * TEST_IMU_HZ ; sample++)
{
uint64_t ticks = sample * TEST_TIMEBASE_HZ / TEST_IMU_HZ;
fusion_imu_measurement_t imu = stationary_imu(ticks, config->eskf.gravity_mps2);
if(!fusion_engine_push_imu(engine, &imu))
{
return false;
}
}
return engine->state == FUSION_ENGINE_RUNNING;
}
static int startup_offset_recovers(uint64_t gps_period_ticks)
{
static fusion_engine_t engine;
fusion_engine_config_t config;
if(!calibrate_at_origin(&engine, &config))
{
fprintf(stderr, "FAIL: calibration did not complete\n");
return 1;
}
/* Models a poor first LLH origin followed by the stable, correct fixes seen
* on UART. ENU remains relative to the first origin, so the correct fixes
* legitimately appear far from the initialized ESKF position. */
const double expected[3] = {80.0, 40.0, -170.0};
uint64_t next_gps_ticks = 3200000u;
for(uint64_t sample = 3u * TEST_IMU_HZ + 1u ;
sample <= 12u * TEST_IMU_HZ ; sample++)
{
uint64_t ticks = sample * TEST_TIMEBASE_HZ / TEST_IMU_HZ;
fusion_imu_measurement_t imu = stationary_imu(ticks, config.eskf.gravity_mps2);
if(!fusion_engine_push_imu(&engine, &imu))
{
fprintf(stderr, "FAIL: IMU propagation failed\n");
return 1;
}
while(next_gps_ticks <= ticks)
{
fusion_position_measurement_t gps = gps_position(
next_gps_ticks, expected[0], expected[1], expected[2]);
(void)fusion_engine_push_position(&engine, &gps, NULL, NULL);
next_gps_ticks += gps_period_ticks;
}
}
double error = sqrt(
pow(engine.current.filter.position_m[0] - expected[0], 2.0) +
pow(engine.current.filter.position_m[1] - expected[1], 2.0) +
pow(engine.current.filter.position_m[2] - expected[2], 2.0));
printf("startup offset GPS=%4llu ms: error=%.3f m accepted=%llu "
"rejected=%llu recovery=%llu\n",
(unsigned long long)(gps_period_ticks / 1000u), error,
(unsigned long long)engine.diagnostics.position_accepted,
(unsigned long long)engine.diagnostics.position_nis_rejected,
(unsigned long long)engine.diagnostics.position_recovery_count);
if((error > 5.0) || (engine.diagnostics.position_recovery_count != 1u))
{
fprintf(stderr, "FAIL: consistent GPS did not recover the wrong startup position\n");
return 1;
}
return 0;
}
static int isolated_outlier_does_not_reset(void)
{
static fusion_engine_t engine;
fusion_engine_config_t config;
if(!calibrate_at_origin(&engine, &config))
{
fprintf(stderr, "FAIL: outlier scenario calibration did not complete\n");
return 1;
}
for(uint64_t gps_ticks = 3200000u ; gps_ticks <= 7000000u ;
gps_ticks += TEST_GPS_PERIOD_TICKS)
{
uint64_t imu_ticks = gps_ticks + 10000u;
fusion_imu_measurement_t imu = stationary_imu(
imu_ticks, config.eskf.gravity_mps2);
if(!fusion_engine_push_imu(&engine, &imu))
{
fprintf(stderr, "FAIL: outlier scenario IMU propagation failed\n");
return 1;
}
bool outlier = (gps_ticks == 5000000u);
fusion_position_measurement_t gps = gps_position(
gps_ticks, outlier ? 150.0 : 0.0, outlier ? -120.0 : 0.0,
outlier ? 80.0 : 0.0);
(void)fusion_engine_push_position(&engine, &gps, NULL, NULL);
}
double distance = sqrt(
pow(engine.current.filter.position_m[0], 2.0) +
pow(engine.current.filter.position_m[1], 2.0) +
pow(engine.current.filter.position_m[2], 2.0));
printf("single outlier: error=%.3f m rejected=%llu recovery=%llu\n",
distance,
(unsigned long long)engine.diagnostics.position_nis_rejected,
(unsigned long long)engine.diagnostics.position_recovery_count);
if((distance > 1.0) || (engine.diagnostics.position_nis_rejected == 0u) ||
(engine.diagnostics.position_recovery_count != 0u))
{
fprintf(stderr, "FAIL: isolated GPS outlier moved/reset the ESKF\n");
return 1;
}
return 0;
}
int main(void)
{
int failures = startup_offset_recovers(100000u);
failures += startup_offset_recovers(200000u);
failures += startup_offset_recovers(1000000u);
failures += isolated_outlier_does_not_reset();
if(failures == 0)
{
puts("GPS reacquisition tests passed");
}
return failures == 0 ? 0 : 1;
}