Exemple #1
0
END_TEST

START_TEST(test_wgsecef2llh2ecef)
{
  double llh[3];
  double ecef[3];

  wgsecef2llh(ecefs[_i], llh);
  wgsllh2ecef(llh, ecef);

  for (int n=0; n<3; n++) {
    fail_unless(!isnan(llh[n]), "NaN in LLH output from conversion.");
    fail_unless(!isnan(ecef[n]), "NaN in ECEF output from conversion.");
  }

  for (int n=0; n<3; n++) {
    double err = fabs(ecef[n] - ecefs[_i][n]);
    fail_unless(err < MAX_DIST_ERROR_M,
      "Converting WGS84 ECEF to LLH and back again does not return the "
      "original values.\n"
      "Initial ECEF: %f, %f, %f\n"
      "LLH: %f, %f, %f\n"
      "Final ECEF: %f, %f, %f\n"
      "X error (mm): %g\nY error (mm): %g\nZ error (mm): %g",
      ecefs[_i][0], ecefs[_i][1], ecefs[_i][2],
      R2D*llh[0], R2D*llh[1], llh[2],
      ecef[0], ecef[1], ecef[2],
      (ecef[0] - ecefs[_i][0])*1e3,
      (ecef[1] - ecefs[_i][1])*1e3,
      (ecef[2] - ecefs[_i][2])*1e3
    );
  }
}
Exemple #2
0
END_TEST

START_TEST(test_wgsecef2llh)
{
  double llh[3];

  wgsecef2llh(ecefs[_i], llh);

  for (int n=0; n<3; n++) {
    fail_unless(!isnan(llh[n]), "NaN in output from wgsecef2llh.");
  }

  double lat_err = fabs(llh[0] - llhs[_i][0]);
  double lon_err = fabs(llh[1] - llhs[_i][1]);
  double hgt_err = fabs(llh[2] - llhs[_i][2]);
  fail_unless((lat_err < MAX_ANGLE_ERROR_RAD) &&
              (lon_err < MAX_ANGLE_ERROR_RAD) &&
              (hgt_err < MAX_DIST_ERROR_M),
    "Conversion from WGS84 ECEF to LLH has >1e-6 {rad, m} error:\n"
    "ECEF: %f, %f, %f\n"
    "Lat error (arc sec): %g\nLon error (arc sec): %g\nH error (mm): %g",
    ecefs[_i][0], ecefs[_i][1], ecefs[_i][2],
    (llh[0] - llhs[_i][0])*(R2D*3600),
    (llh[1] - llhs[_i][1])*(R2D*3600),
    (llh[2] - llhs[_i][2])*1e3
  );
}
Exemple #3
0
END_TEST

START_TEST(test_wgsllh2ecef2llh)
{
  double ecef[3];
  double llh[3];

  wgsllh2ecef(llhs[_i], ecef);
  wgsecef2llh(ecef, llh);

  for (int n=0; n<3; n++) {
    fail_unless(!isnan(llh[n]), "NaN in LLH output from conversion.");
    fail_unless(!isnan(ecef[n]), "NaN in ECEF output from conversion.");
  }

  double lat_err = fabs(llh[0] - llhs[_i][0]);
  double lon_err = fabs(llh[1] - llhs[_i][1]);
  double hgt_err = fabs(llh[2] - llhs[_i][2]);
  fail_unless((lat_err < MAX_ANGLE_ERROR_RAD) &&
              (lon_err < MAX_ANGLE_ERROR_RAD) &&
              (hgt_err < MAX_DIST_ERROR_M),
    "Converting WGS84 LLH to ECEF and back again does not return the "
    "original values.\n"
    "Initial LLH: %f, %f, %f\n"
    "ECEF: %f, %f, %f\n"
    "Final LLH: %f, %f, %f\n"
    "Lat error (arc sec): %g\nLon error (arc sec): %g\nH error (mm): %g",
    R2D*llhs[_i][0], R2D*llhs[_i][1], llhs[_i][2],
    ecef[0], ecef[1], ecef[2],
    R2D*llh[0], R2D*llh[1], llh[2],
    (llh[0] - llhs[_i][0])*(R2D*3600),
    (llh[1] - llhs[_i][1])*(R2D*3600),
    (llh[2] - llhs[_i][2])*1e3
  );
}
Exemple #4
0
/**
* Performs a simulation step for the given duration, by moving
* our simulated position in a circle at a given radius and speed
* around the simulation's center point.
*/
void simulation_step_position_in_circle(double elapsed)
{

  /* Update the angle, making a small angle approximation. */
  sim_state.angle += (sim_settings.speed * elapsed) / sim_settings.radius;
  if (sim_state.angle > 2*M_PI) {
    sim_state.angle = 0;
  }

  double pos_ned[3] = {
    sim_settings.radius * sin(sim_state.angle),
    sim_settings.radius * cos(sim_state.angle),
    0
  };

  /* Fill out position simulation's gnss_solution pos_ECEF, pos_LLH structures */
  wgsned2ecef_d(pos_ned,
    sim_settings.base_ecef,
    sim_state.pos);

  /* Calculate an accurate baseline for simulating RTK */
  vector_subtract(3,
    sim_state.pos,
    sim_settings.base_ecef,
    sim_state.baseline);

  /* Add gaussian noise to PVT position */
  double* pos_ecef = sim_state.noisy_solution.pos_ecef;
  double pos_variance = sim_settings.pos_sigma * sim_settings.pos_sigma;
  pos_ecef[0] = sim_state.pos[0] + rand_gaussian(pos_variance);
  pos_ecef[1] = sim_state.pos[1] + rand_gaussian(pos_variance);
  pos_ecef[2] = sim_state.pos[2] + rand_gaussian(pos_variance);

  wgsecef2llh(sim_state.noisy_solution.pos_ecef, sim_state.noisy_solution.pos_llh);

  /* Calculate Velocity vector tangent to the sphere */
  double noisy_speed = sim_settings.speed +
                       rand_gaussian(sim_settings.speed_sigma *
                                     sim_settings.speed_sigma);

  sim_state.noisy_solution.vel_ned[0] = noisy_speed * cos(sim_state.angle);
  sim_state.noisy_solution.vel_ned[1] = noisy_speed * -1.0 * sin(sim_state.angle);
  sim_state.noisy_solution.vel_ned[2] = 0.0;

  wgsned2ecef(sim_state.noisy_solution.vel_ned,
    sim_state.noisy_solution.pos_ecef,
    sim_state.noisy_solution.vel_ecef);
}
Exemple #5
0
s8 calc_PVT(const u8 n_used,
            const navigation_measurement_t const nav_meas[n_used],
            gnss_solution *soln,
            dops_t *dops)
{
  /* Initial state is the center of the Earth with zero velocity and zero
   * clock error, if we have some a priori position estimate we could use
   * that here to speed convergence a little on the first iteration.
   *
   *  rx_state format:
   *    pos[3], clock error, vel[3], intermediate freq error
   */
  static double rx_state[8];

  double H[4][4];

  soln->valid = 0;

  soln->n_used = n_used; // Keep track of number of working channels

  /* reset state to zero !? */
  for(u8 i=4; i<8; i++) {
    rx_state[i] = 0;
  }

  double update;
  u8 iters;
  /* Newton-Raphson iteration. */
  for (iters=0; iters<PVT_MAX_ITERATIONS; iters++) {
    if ((update = pvt_solve(rx_state, n_used, nav_meas, H)) > 0) {
      break;
    }
  }

  /* Compute various dilution of precision metrics. */
  compute_dops((const double(*)[4])H, rx_state, dops);
  soln->err_cov[6] = dops->gdop;

  /* Populate error covariances according to layout in definition
   * of gnss_solution struct.
   */
  soln->err_cov[0] = H[0][0];
  soln->err_cov[1] = H[0][1];
  soln->err_cov[2] = H[0][2];
  soln->err_cov[3] = H[1][1];
  soln->err_cov[4] = H[1][2];
  soln->err_cov[5] = H[2][2];

  if (iters >= PVT_MAX_ITERATIONS) {
    /* Reset state if solution fails */
    rx_state[0] = 0;
    rx_state[1] = 0;
    rx_state[2] = 0;
    return -4;
  }

  /* Save as x, y, z. */
  for (u8 i=0; i<3; i++) {
    soln->pos_ecef[i] = rx_state[i];
    soln->vel_ecef[i] = rx_state[4+i];
  }

  wgsecef2ned(soln->vel_ecef, soln->pos_ecef, soln->vel_ned);

  /* Convert to lat, lon, hgt. */
  wgsecef2llh(rx_state, soln->pos_llh);

  soln->clock_offset = rx_state[3] / GPS_C;
  soln->clock_bias = rx_state[7] / GPS_C;

  /* Time at receiver is TOT plus time of flight. Time of flight is eqaul to
   * the pseudorange minus the clock bias. */
  soln->time = nav_meas[0].tot;
  soln->time.tow += nav_meas[0].pseudorange / GPS_C;
  /* Subtract clock offset. */
  soln->time.tow -= rx_state[3] / GPS_C;
  soln->time = normalize_gps_time(soln->time);

  u8 ret;
  if ((ret = filter_solution(soln, dops))) {
    memset(soln, 0, sizeof(*soln));
    /* Reset state if solution fails */
    rx_state[0] = 0;
    rx_state[1] = 0;
    rx_state[2] = 0;
    return -ret;
  }

  soln->valid = 1;
  return 0;
}
Exemple #6
0
END_TEST

START_TEST(test_random_wgsecef2llh2ecef)
{
  double ecef_init[3];
  double llh[3];
  double ecef[3];

  seed_rng();

  ecef_init[0] = frand(-4*EARTH_A, 4*EARTH_A);
  ecef_init[1] = frand(-4*EARTH_A, 4*EARTH_A);
  ecef_init[2] = frand(-4*EARTH_A, 4*EARTH_A);

  wgsecef2llh(ecef_init, llh);
  wgsllh2ecef(llh, ecef);

  for (int n=0; n<3; n++) {
    fail_unless(!isnan(llh[n]), "NaN in LLH output from conversion.");
    fail_unless(!isnan(ecef[n]), "NaN in ECEF output from conversion.");
  }

  for (int n=0; n<3; n++) {
    double err = fabs(ecef[n] - ecef_init[n]);
    fail_unless(err < MAX_DIST_ERROR_M,
      "Converting random WGS84 ECEF to LLH and back again does not return the "
      "original values.\n"
      "Initial ECEF: %f, %f, %f\n"
      "LLH: %f, %f, %f\n"
      "Final ECEF: %f, %f, %f\n"
      "X error (mm): %g\nY error (mm): %g\nZ error (mm): %g",
      ecef_init[0], ecef_init[1], ecef_init[2],
      R2D*llh[0], R2D*llh[1], llh[2],
      ecef[0], ecef[1], ecef[2],
      (ecef[0] - ecef_init[0])*1e3,
      (ecef[1] - ecef_init[1])*1e3,
      (ecef[2] - ecef_init[2])*1e3
    );
  }

  fail_unless((R2D*llh[0] >= -90) && (R2D*llh[0] <= 90),
      "Converting random WGS84 ECEF gives latitude out of bounds.\n"
      "Initial ECEF: %f, %f, %f\n"
      "LLH: %f, %f, %f\n",
      ecef_init[0], ecef_init[1], ecef_init[2],
      R2D*llh[0], R2D*llh[1], llh[2]
  );

  fail_unless((R2D*llh[1] >= -180) && (R2D*llh[1] <= 180),
      "Converting random WGS84 ECEF gives longitude out of bounds.\n"
      "Initial ECEF: %f, %f, %f\n"
      "LLH: %f, %f, %f\n",
      ecef_init[0], ecef_init[1], ecef_init[2],
      R2D*llh[0], R2D*llh[1], llh[2]
  );

  fail_unless(llh[2] > -EARTH_A,
      "Converting random WGS84 ECEF gives height out of bounds.\n"
      "Initial ECEF: %f, %f, %f\n"
      "LLH: %f, %f, %f\n",
      ecef_init[0], ecef_init[1], ecef_init[2],
      R2D*llh[0], R2D*llh[1], llh[2]
  );
}
Exemple #7
0
END_TEST

START_TEST(test_random_wgsllh2ecef2llh)
{
  double ecef[3];
  double llh_init[3];
  double llh[3];

  seed_rng();

  llh_init[0] = D2R*frand(-90, 90);
  llh_init[1] = D2R*frand(-180, 180);
  llh_init[2] = frand(-0.5 * EARTH_A, 4 * EARTH_A);

  wgsllh2ecef(llh_init, ecef);
  wgsecef2llh(ecef, llh);

  for (int n=0; n<3; n++) {
    fail_unless(!isnan(llh[n]), "NaN in LLH output from conversion.");
    fail_unless(!isnan(ecef[n]), "NaN in ECEF output from conversion.");
  }

  double lat_err = fabs(llh[0] - llh_init[0]);
  double lon_err = fabs(llh[1] - llh_init[1]);
  double hgt_err = fabs(llh[2] - llh_init[2]);
  fail_unless((lat_err < MAX_ANGLE_ERROR_RAD) &&
              (lon_err < MAX_ANGLE_ERROR_RAD) &&
              (hgt_err < MAX_DIST_ERROR_M),
    "Converting random WGS84 LLH to ECEF and back again does not return the "
    "original values.\n"
    "Initial LLH: %f, %f, %f\n"
    "ECEF: %f, %f, %f\n"
    "Final LLH: %f, %f, %f\n"
    "Lat error (arc sec): %g\nLon error (arc sec): %g\nH error (mm): %g",
    R2D*llh_init[0], R2D*llh_init[1], llh_init[2],
    ecef[0], ecef[1], ecef[2],
    R2D*llh[0], R2D*llh[1], llh[2],
    (llh[0] - llh_init[0])*(R2D*3600),
    (llh[1] - llh_init[1])*(R2D*3600),
    (llh[2] - llh_init[2])*1e3
  );

  fail_unless((R2D*llh[0] >= -90) && (R2D*llh[0] <= 90),
      "Converting random WGS84 ECEF gives latitude out of bounds.\n"
      "ECEF: %f, %f, %f\n"
      "LLH: %f, %f, %f\n",
      ecef[0], ecef[1], ecef[2],
      R2D*llh[0], R2D*llh[1], llh[2]
  );

  fail_unless((R2D*llh[1] >= -180) && (R2D*llh[1] <= 180),
      "Converting random WGS84 ECEF gives longitude out of bounds.\n"
      "ECEF: %f, %f, %f\n"
      "LLH: %f, %f, %f\n",
      ecef[0], ecef[1], ecef[2],
      R2D*llh[0], R2D*llh[1], llh[2]
  );

  fail_unless(llh[2] > -EARTH_A,
      "Converting random WGS84 ECEF gives height out of bounds.\n"
      "ECEF: %f, %f, %f\n"
      "LLH: %f, %f, %f\n",
      ecef[0], ecef[1], ecef[2],
      R2D*llh[0], R2D*llh[1], llh[2]
  );
}
Exemple #8
0
static void test_wgs_conversion_functions(void)
{

    #define D2R DEG_TO_RAD_DOUBLE

    /* Maximum allowable error in quantities with units of length (in meters). */
    #define MAX_DIST_ERROR_M 1e-6
    /* Maximum allowable error in quantities with units of angle (in sec of arc).
     * 1 second of arc on the equator is ~31 meters. */
    #define MAX_ANGLE_ERROR_SEC 1e-7
    #define MAX_ANGLE_ERROR_RAD (MAX_ANGLE_ERROR_SEC*(D2R/3600.0))

    /* Semi-major axis. */
    #define EARTH_A 6378137.0
    /* Semi-minor axis. */
    #define EARTH_B 6356752.31424517929553985595703125


    #define NUM_COORDS 10
    Vector3d llhs[NUM_COORDS];
    llhs[0] = Vector3d(0, 0, 0);        /* On the Equator and Prime Meridian. */
    llhs[1] = Vector3d(0, 180*D2R, 0);  /* On the Equator. */
    llhs[2] = Vector3d(0, 90*D2R, 0);   /* On the Equator. */
    llhs[3] = Vector3d(0, -90*D2R, 0);  /* On the Equator. */
    llhs[4] = Vector3d(90*D2R, 0, 0);   /* North pole. */
    llhs[5] = Vector3d(-90*D2R, 0, 0);  /* South pole. */
    llhs[6] = Vector3d(90*D2R, 0, 22);  /* 22m above the north pole. */
    llhs[7] = Vector3d(-90*D2R, 0, 22); /* 22m above the south pole. */
    llhs[8] = Vector3d(0, 0, 22);       /* 22m above the Equator and Prime Meridian. */
    llhs[9] = Vector3d(0, 180*D2R, 22); /* 22m above the Equator. */

    Vector3d ecefs[NUM_COORDS];
    ecefs[0] = Vector3d(EARTH_A, 0, 0);
    ecefs[1] = Vector3d(-EARTH_A, 0, 0);
    ecefs[2] = Vector3d(0, EARTH_A, 0);
    ecefs[3] = Vector3d(0, -EARTH_A, 0);
    ecefs[4] = Vector3d(0, 0, EARTH_B);
    ecefs[5] = Vector3d(0, 0, -EARTH_B);
    ecefs[6] = Vector3d(0, 0, (EARTH_B+22));
    ecefs[7] = Vector3d(0, 0, -(EARTH_B+22));
    ecefs[8] = Vector3d((22+EARTH_A), 0, 0);
    ecefs[9] = Vector3d(-(22+EARTH_A), 0, 0);

    hal.console->printf("TESTING wgsllh2ecef\n");
    for (int i = 0; i < NUM_COORDS; i++) {

        Vector3d ecef;
        wgsllh2ecef(llhs[i], ecef);

        double x_err = fabs(ecef[0] - ecefs[i][0]);
        double y_err = fabs(ecef[1] - ecefs[i][1]);
        double z_err = fabs(ecef[2] - ecefs[i][2]);
        if ((x_err < MAX_DIST_ERROR_M) &&
                  (y_err < MAX_DIST_ERROR_M) &&
                  (z_err < MAX_DIST_ERROR_M)) {
            hal.console->printf("passing llh to ecef test %d\n", i);
        } else {
            hal.console->printf("failed llh to ecef test %d: ", i);
            hal.console->printf("(%f - %f) (%f - %f) (%f - %f) => %.10f %.10f %.10f\n", ecef[0], ecefs[i][0], ecef[1], ecefs[i][1], ecef[2], ecefs[i][2], x_err, y_err, z_err);
        }

    }

    hal.console->printf("TESTING wgsecef2llh\n");
    for (int i = 0; i < NUM_COORDS; i++) {

        Vector3d llh;
        wgsecef2llh(ecefs[i], llh);

        double lat_err = fabs(llh[0] - llhs[i][0]);
        double lon_err = fabs(llh[1] - llhs[i][1]);
        double hgt_err = fabs(llh[2] - llhs[i][2]);
        if ((lat_err < MAX_ANGLE_ERROR_RAD) &&
                  (lon_err < MAX_ANGLE_ERROR_RAD) &&
                  (hgt_err < MAX_DIST_ERROR_M)) {
            hal.console->printf("passing exef to llh test %d\n", i);
        } else {
            hal.console->printf("failed ecef to llh test %d: ", i);
            hal.console->printf("%.10f %.10f %.10f\n", lat_err, lon_err, hgt_err);

        }

    }
}
Exemple #9
0
/** Try to calculate a single point gps solution
 *
 * \param n_used number of measurments
 * \param nav_meas array of measurements
 * \param disable_raim passing True will omit raim check/repair functionality
 * \param soln output solution struct
 * \param dops output doppler information
 * \return Non-negative values indicate a valid solution.
 *   -  `2`: Solution converged but RAIM unavailable or disabled
 *   -  `1`: Solution converged, failed RAIM but was successfully repaired
 *   -  `0`: Solution converged and verified by RAIM
 *   - `-1`: PDOP is too high to yield a good solution.
 *   - `-2`: Altitude is unreasonable.
 *   - `-3`: Velocity is greater than or equal to 1000 kts.
 *   - `-4`: RAIM check failed and repair was unsuccessful
 *   - `-5`: RAIM check failed and repair was impossible (not enough measurements)
 *   - `-6`: pvt_iter didn't converge
 *   - `-7`: < 4 measurements
 */
s8 calc_PVT(const u8 n_used,
            const navigation_measurement_t nav_meas[n_used],
            bool disable_raim,
            gnss_solution *soln,
            dops_t *dops)
{
  /* Initial state is the center of the Earth with zero velocity and zero
   * clock error, if we have some a priori position estimate we could use
   * that here to speed convergence a little on the first iteration.
   *
   *  rx_state format:
   *    pos[3], clock error, vel[3], intermediate freq error
   */
  static double rx_state[8];

  double H[4][4];

  if (n_used < 4) {
    return -7;
  }

  soln->valid = 0;
  soln->n_used = n_used; // Keep track of number of working channels

  gnss_signal_t removed_sid;
  s8 raim_flag = pvt_solve_raim(rx_state, n_used, nav_meas, disable_raim,
                                H, &removed_sid, 0);

  if (raim_flag < 0) {
    /* Didn't converge or least squares integrity check failed. */
    return raim_flag - 3;
  }

  /* Initial solution failed, but repair was successful. */
  if (raim_flag == 1) {
    soln->n_used--;
  }

  /* Compute various dilution of precision metrics. */
  compute_dops((const double(*)[4])H, rx_state, dops);
  soln->err_cov[6] = dops->gdop;

  /* Populate error covariances according to layout in definition
   * of gnss_solution struct.
   */
  soln->err_cov[0] = H[0][0];
  soln->err_cov[1] = H[0][1];
  soln->err_cov[2] = H[0][2];
  soln->err_cov[3] = H[1][1];
  soln->err_cov[4] = H[1][2];
  soln->err_cov[5] = H[2][2];

  /* Save as x, y, z. */
  for (u8 i=0; i<3; i++) {
    soln->pos_ecef[i] = rx_state[i];
    soln->vel_ecef[i] = rx_state[4+i];
  }

  wgsecef2ned(soln->vel_ecef, soln->pos_ecef, soln->vel_ned);

  /* Convert to lat, lon, hgt. */
  wgsecef2llh(rx_state, soln->pos_llh);

  soln->clock_offset = rx_state[3] / GPS_C;
  soln->clock_bias = rx_state[7] / GPS_C;

  /* Time at receiver is TOT plus time of flight. Time of flight is eqaul to
   * the pseudorange minus the clock bias. */
  soln->time = nav_meas[0].tot;
  soln->time.tow += nav_meas[0].pseudorange / GPS_C;
  /* Subtract clock offset. */
  soln->time.tow -= rx_state[3] / GPS_C;
  soln->time = normalize_gps_time(soln->time);

  u8 ret;
  if ((ret = filter_solution(soln, dops))) {
    memset(soln, 0, sizeof(*soln));
    /* Reset position elements of state if solution fails. */
    rx_state[0] = 0;
    rx_state[1] = 0;
    rx_state[2] = 0;
    return -ret;
  }

  soln->valid = 1;

  return raim_flag;
}