예제 #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
    );
  }
}
예제 #2
0
파일: from_fix.cpp 프로젝트: chadrockey/enu
void initialize_datum(double datum_ecef[3],
                      const sensor_msgs::NavSatFixConstPtr fix_ptr,
                      const ros::Publisher& pub_datum)
{
  ros::NodeHandle n("~");
  sensor_msgs::NavSatFix datum_msg(*fix_ptr);

  // Local ENU coordinates are with respect to a plane which is 
  // perpendicular to a particular lat/lon. This logic decides 
  // whether to use a specific passed-in point (typical for 
  // repeated tests in a locality) or just an arbitrary starting
  // point (more ad-hoc type scenarios).
  if (n.hasParam("datum_latitude") &&
      n.hasParam("datum_longitude") && 
      n.hasParam("datum_altitude")) {
    n.getParam("datum_latitude", datum_msg.latitude);  
    n.getParam("datum_longitude", datum_msg.longitude);  
    n.getParam("datum_altitude", datum_msg.altitude);  
    ROS_INFO("Using datum provided by node parameters.");
  } else {
    ROS_INFO("Using initial position fix as datum.");
  }
  pub_datum.publish(datum_msg);

  // The datum point is stored as an ECEF, for mathematical reasons.
  // We convert it here, using the appropriate function from
  // libswiftnav.
  double llh[3] = { datum_msg.latitude * TO_RADIANS,
                    datum_msg.longitude * TO_RADIANS,
                    datum_msg.altitude };
  wgsllh2ecef(llh, datum_ecef);
}
예제 #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
  );
}
예제 #4
0
static void system_monitor_thread(void *arg)
{
  (void)arg;
  chRegSetThreadName("system monitor");

  systime_t time = chVTGetSystemTime();

  bool ant_status = 0;

  while (TRUE) {

    if (ant_status != frontend_ant_status()) {
      ant_status = frontend_ant_status();
      if (ant_status && frontend_ant_setting() == AUTO) {
        log_info("Now using external antenna.");
      }
      else if (frontend_ant_setting() == AUTO) {
        log_info("Now using patch antenna.");
      }
    }
    u32 status_flags = ant_status << 31 | SBP_MAJOR_VERSION << 16 | SBP_MINOR_VERSION << 8;
    sbp_send_msg(SBP_MSG_HEARTBEAT, sizeof(status_flags), (u8 *)&status_flags);

    /* If we are in base station mode then broadcast our known location. */
    if (broadcast_surveyed_position && position_quality == POSITION_FIX) {
      double tmp[3];
      double base_ecef[3];
      double base_distance;

      llhdeg2rad(base_llh, tmp);
      wgsllh2ecef(tmp, base_ecef);

      vector_subtract(3, base_ecef, position_solution.pos_ecef, tmp);
      base_distance = vector_norm(3, tmp);

      if (base_distance > BASE_STATION_DISTANCE_THRESHOLD) {
        log_warn("Invalid surveyed position coordinates\n");
      } else {
        sbp_send_msg(SBP_MSG_BASE_POS_ECEF, sizeof(msg_base_pos_ecef_t), (u8 *)&base_ecef);
      }
    }

    msg_iar_state_t iar_state;
    if (simulation_enabled_for(SIMULATION_MODE_RTK)) {
      iar_state.num_hyps = 1;
    } else {
      iar_state.num_hyps = dgnss_iar_num_hyps();
    }
    sbp_send_msg(SBP_MSG_IAR_STATE, sizeof(msg_iar_state_t), (u8 *)&iar_state);
    
    DO_EVERY(2, 
     send_thread_states(); 
    );

    sleep_until(&time, MS2ST(heartbeat_period_milliseconds));
  }
예제 #5
0
파일: from_fix.cpp 프로젝트: chadrockey/enu
static void handle_fix(const sensor_msgs::NavSatFixConstPtr fix_ptr,
                       const ros::Publisher& pub_enu,
                       const ros::Publisher& pub_datum)
{
  static double ecef_datum[3];
  static bool have_datum = false;

  if (!have_datum) {
    initialize_datum(ecef_datum, fix_ptr, pub_datum);
    have_datum = true;
  }

  // Prepare the appropriate input vector to convert the input latlon
  // to an ECEF triplet.
  double llh[3] = { fix_ptr->latitude * TO_RADIANS,
                    fix_ptr->longitude * TO_RADIANS,
                    fix_ptr->altitude };
  double ecef[3];
  wgsllh2ecef(llh, ecef);
  
  // ECEF triplet is converted to north-east-down (NED), by combining it
  // with the ECEF-formatted datum point.
  double ned[3];
  wgsecef2ned_d(ecef, ecef_datum, ned);

  nav_msgs::Odometry odom_msg;
  odom_msg.header.stamp = fix_ptr->header.stamp;
  odom_msg.pose.pose.position.x = ned[1];
  odom_msg.pose.pose.position.y = ned[0];
  odom_msg.pose.pose.position.z = -ned[2];

  // We only need to populate the diagonals of the covariance matrix; the
  // rest initialize to zero automatically, which is correct as the
  // dimensions of the state are independent.
  odom_msg.pose.covariance[0] = fix_ptr->position_covariance[0];
  odom_msg.pose.covariance[7] = fix_ptr->position_covariance[4];
  odom_msg.pose.covariance[14] = fix_ptr->position_covariance[8];
  
  // Do not use/trust orientation dimensions from GPS.
  odom_msg.pose.covariance[21] = 1e6;
  odom_msg.pose.covariance[28] = 1e6;
  odom_msg.pose.covariance[35] = 1e6;

  // Orientation of GPS is pointing upward (as far as we know).
  odom_msg.pose.pose.orientation.w = 1;

  pub_enu.publish(odom_msg); 
}
예제 #6
0
/** SBP callback for when the base station sends us a message containing its
 * known location.
 * Stores the base station position in the global #base_pos_ecef variable and
 * sets #base_pos_known to `true`.
 */
static void base_pos_callback(u16 sender_id, u8 len, u8 msg[], void* context)
{
  (void) context; (void) len; (void) msg; (void) sender_id;

  double llh_degrees[3];
  double llh[3];
  memcpy(llh_degrees, msg, 3*sizeof(double));

  llh[0] = llh_degrees[0] * D2R;
  llh[1] = llh_degrees[1] * D2R;
  llh[2] = llh_degrees[2];

  chMtxLock(&base_pos_lock);
  wgsllh2ecef(llh, base_pos_ecef);
  base_pos_known = true;
  chMtxUnlock();
}
예제 #7
0
END_TEST

START_TEST(test_wgsllh2ecef)
{
  double ecef[3];

  wgsllh2ecef(llhs[_i], ecef);

  for (int n=0; n<3; n++) {
    fail_unless(!isnan(ecef[n]), "NaN in output from wgsllh2ecef.");
    double err = fabs(ecef[n] - ecefs[_i][n]);
    fail_unless(err < MAX_DIST_ERROR_M,
      "Conversion from WGS84 LLH to ECEF has >1e-6m error:\n"
      "LLH: %f, %f, %f\n"
      "X error (mm): %g\nY error (mm): %g\nZ error (mm): %g",
      llhs[_i][0]*R2D, llhs[_i][1]*R2D, llhs[_i][2],
      (ecef[0] - ecefs[_i][0])*1e3,
      (ecef[1] - ecefs[_i][1])*1e3,
      (ecef[2] - ecefs[_i][2])*1e3
    );
  }
}
예제 #8
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]
  );
}
예제 #9
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]
  );
}
예제 #10
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);

        }

    }
}