#include "Location.h"
#ifndef HAL_BOOTLOADER_BUILD
#include <AP_AHRS/AP_AHRS.h>
#include <AP_Terrain/AP_Terrain.h>
const Location definitely_zero{};
bool Location::is_zero(void) const
{
return !memcmp(this, &definitely_zero, sizeof(*this));
}
void Location::zero(void)
{
memset(this, 0, sizeof(*this));
}
Location::Location(int32_t latitude, int32_t longitude, int32_t alt_in_cm, AltFrame frame)
{
ASSERT_STORAGE_SIZE(Location, 16);
zero();
lat = latitude;
lng = longitude;
set_alt_cm(alt_in_cm, frame);
}
#if AP_AHRS_ENABLED
Location::Location(const Vector3f &ekf_offset_neu_cm, AltFrame frame)
{
zero();
set_alt_cm(ekf_offset_neu_cm.z, frame);
Location ekf_origin;
if (AP::ahrs().get_origin(ekf_origin)) {
lat = ekf_origin.lat;
lng = ekf_origin.lng;
offset(ekf_offset_neu_cm.x * 0.01, ekf_offset_neu_cm.y * 0.01);
}
}
Location::Location(const Vector3d &ekf_offset_neu_cm, AltFrame frame)
{
zero();
set_alt_cm(ekf_offset_neu_cm.z, frame);
Location ekf_origin;
if (AP::ahrs().get_origin(ekf_origin)) {
lat = ekf_origin.lat;
lng = ekf_origin.lng;
offset(ekf_offset_neu_cm.x * 0.01, ekf_offset_neu_cm.y * 0.01);
}
}
Location Location::from_ekf_offset_NED_m(const Vector3f& ekf_offset_ned_m, AltFrame frame)
{
const Vector3f ekf_offset_neu_cm(ekf_offset_ned_m.x * 100.0, ekf_offset_ned_m.y * 100.0, -ekf_offset_ned_m.z * 100.0);
return Location(ekf_offset_neu_cm, frame);
}
Location Location::from_ekf_offset_NED_m(const Vector3d& ekf_offset_ned_m, AltFrame frame)
{
const Vector3d ekf_offset_neu_cm(ekf_offset_ned_m.x * 100.0, ekf_offset_ned_m.y * 100.0, -ekf_offset_ned_m.z * 100.0);
return Location(ekf_offset_neu_cm, frame);
}
#endif
void Location::set_alt_cm(int32_t alt_cm, AltFrame frame)
{
alt = alt_cm;
relative_alt = false;
terrain_alt = false;
origin_alt = false;
switch (frame) {
case AltFrame::ABSOLUTE:
break;
case AltFrame::ABOVE_HOME:
relative_alt = true;
break;
case AltFrame::ABOVE_ORIGIN:
origin_alt = true;
break;
case AltFrame::ABOVE_TERRAIN:
relative_alt = true;
terrain_alt = true;
break;
}
}
bool Location::change_alt_frame(AltFrame desired_frame)
{
int32_t new_alt_cm;
if (!get_alt_cm(desired_frame, new_alt_cm)) {
return false;
}
set_alt_cm(new_alt_cm, desired_frame);
return true;
}
void Location::copy_alt_from(const Location &other)
{
alt = other.alt;
relative_alt = other.relative_alt;
terrain_alt = other.terrain_alt;
origin_alt = other.origin_alt;
}
Location::AltFrame Location::get_alt_frame() const
{
if (terrain_alt) {
#if CONFIG_HAL_BOARD == HAL_BOARD_SITL
if (!relative_alt) {
AP_HAL::panic("terrain loc must be relative_alt1");
}
#endif
return AltFrame::ABOVE_TERRAIN;
}
if (origin_alt) {
return AltFrame::ABOVE_ORIGIN;
}
if (relative_alt) {
return AltFrame::ABOVE_HOME;
}
return AltFrame::ABSOLUTE;
}
bool Location::get_alt_cm(AltFrame desired_frame, int32_t &ret_alt_cm) const
{
#if CONFIG_HAL_BOARD == HAL_BOARD_SITL
if (!initialised()) {
AP_HAL::panic("Should not be called on invalid location: Location cannot be (0, 0, 0)");
}
if (terrain_alt && !relative_alt) {
AP_HAL::panic("terrain loc must be relative_alt2");
}
#endif
Location::AltFrame frame = get_alt_frame();
if (desired_frame == frame) {
ret_alt_cm = alt;
return true;
}
float alt_terr_cm = 0;
if (frame == AltFrame::ABOVE_TERRAIN || desired_frame == AltFrame::ABOVE_TERRAIN) {
#if AP_TERRAIN_AVAILABLE
AP_Terrain *terrain = AP::terrain();
if (terrain == nullptr) {
return false;
}
if (!terrain->height_amsl(*this, alt_terr_cm)) {
return false;
}
alt_terr_cm *= 100.0f;
#else
return false;
#endif
}
int32_t alt_abs = 0;
switch (frame) {
case AltFrame::ABSOLUTE:
alt_abs = alt;
break;
case AltFrame::ABOVE_HOME:
#if AP_AHRS_ENABLED
if (!AP::ahrs().home_is_set()) {
return false;
}
alt_abs = alt + AP::ahrs().get_home().alt;
#else
return false;
#endif
break;
case AltFrame::ABOVE_ORIGIN:
#if AP_AHRS_ENABLED
{
Location ekf_origin;
if (!AP::ahrs().get_origin(ekf_origin)) {
return false;
}
alt_abs = alt + ekf_origin.alt;
}
break;
#else
return false;
#endif
case AltFrame::ABOVE_TERRAIN:
alt_abs = alt + alt_terr_cm;
break;
}
switch (desired_frame) {
case AltFrame::ABSOLUTE:
ret_alt_cm = alt_abs;
return true;
case AltFrame::ABOVE_HOME:
#if AP_AHRS_ENABLED
if (!AP::ahrs().home_is_set()) {
return false;
}
ret_alt_cm = alt_abs - AP::ahrs().get_home().alt;
#else
return false;
#endif
return true;
case AltFrame::ABOVE_ORIGIN:
#if AP_AHRS_ENABLED
{
Location ekf_origin;
if (!AP::ahrs().get_origin(ekf_origin)) {
return false;
}
ret_alt_cm = alt_abs - ekf_origin.alt;
return true;
}
#else
return false;
#endif
case AltFrame::ABOVE_TERRAIN:
ret_alt_cm = alt_abs - alt_terr_cm;
return true;
}
return false;
}
bool Location::get_alt_m(AltFrame desired_frame, float &ret_alt) const
{
int32_t ret_alt_cm;
if (!get_alt_cm(desired_frame, ret_alt_cm)) {
return false;
}
ret_alt = ret_alt_cm * 0.01;
return true;
}
#if AP_AHRS_ENABLED
template<typename T>
bool Location::get_vector_xy_from_origin_NE_cm(T &vec_ne) const
{
Location ekf_origin;
if (!AP::ahrs().get_origin(ekf_origin)) {
return false;
}
vec_ne.x = (lat-ekf_origin.lat) * LATLON_TO_CM;
vec_ne.y = diff_longitude(lng,ekf_origin.lng) * LATLON_TO_CM * longitude_scale((lat+ekf_origin.lat)/2);
return true;
}
template bool Location::get_vector_xy_from_origin_NE_cm<Vector2f>(Vector2f &vec_ne) const;
#if HAL_WITH_POSTYPE_DOUBLE
template bool Location::get_vector_xy_from_origin_NE_cm<Vector2p>(Vector2p &vec_ne) const;
#endif
template<typename T>
bool Location::get_vector_from_origin_NEU_cm(T &vec_neu) const
{
int32_t alt_above_origin_cm = 0;
if (!get_alt_cm(AltFrame::ABOVE_ORIGIN, alt_above_origin_cm)) {
return false;
}
if (!get_vector_xy_from_origin_NE_cm(vec_neu.xy())) {
return false;
}
vec_neu.z = alt_above_origin_cm;
return true;
}
template<typename T>
bool Location::get_vector_from_origin_NEU(T &vec_neu) const
{
return get_vector_from_origin_NEU_cm(vec_neu);
}
template bool Location::get_vector_from_origin_NEU_cm<Vector3f>(Vector3f &vec_neu) const;
template bool Location::get_vector_from_origin_NEU<Vector3f>(Vector3f &vec_neu) const;
#if HAL_WITH_POSTYPE_DOUBLE
template bool Location::get_vector_from_origin_NEU_cm<Vector3p>(Vector3p &vec_neu) const;
template bool Location::get_vector_from_origin_NEU<Vector3p>(Vector3p &vec_neu) const;
#endif
template<typename T>
bool Location::get_vector_xy_from_origin_NE_m(T &vec_ne) const
{
if (!get_vector_xy_from_origin_NE_cm(vec_ne)) {
return false;
}
vec_ne *= 0.01;
return true;
}
template bool Location::get_vector_xy_from_origin_NE_m<Vector2f>(Vector2f &vec_ne) const;
#if HAL_WITH_POSTYPE_DOUBLE
template bool Location::get_vector_xy_from_origin_NE_m<Vector2p>(Vector2p &vec_ne) const;
#endif
template<typename T>
bool Location::get_vector_from_origin_NED_m(T &vec_ned) const
{
if (!get_vector_from_origin_NEU_cm(vec_ned)) {
return false;
}
vec_ned *= 0.01;
vec_ned.z *= -1.0;
return true;
}
template bool Location::get_vector_from_origin_NED_m<Vector3f>(Vector3f &vec_ned) const;
#if HAL_WITH_POSTYPE_DOUBLE
template bool Location::get_vector_from_origin_NED_m<Vector3p>(Vector3p &vec_ned) const;
#endif
template<typename T>
bool Location::get_vector_from_origin_NEU_m(T &vec_neu) const
{
if (!get_vector_from_origin_NEU_cm(vec_neu)) {
return false;
}
vec_neu *= 0.01;
return true;
}
template bool Location::get_vector_from_origin_NEU_m<Vector3f>(Vector3f &vec_neu) const;
#if HAL_WITH_POSTYPE_DOUBLE
template bool Location::get_vector_from_origin_NEU_m<Vector3p>(Vector3p &vec_neu) const;
#endif
#endif
ftype Location::get_distance(const Location &loc2) const
{
ftype dlat = (ftype)(loc2.lat - lat);
ftype dlng = ((ftype)diff_longitude(loc2.lng,lng)) * longitude_scale((lat+loc2.lat)/2);
return norm(dlat, dlng) * LOCATION_SCALING_FACTOR;
}
bool Location::get_height_above(const Location &loc2, ftype &distance) const
{
if (get_alt_frame() == loc2.get_alt_frame()) {
switch (get_alt_frame()) {
case AltFrame::ABSOLUTE:
case AltFrame::ABOVE_HOME:
case AltFrame::ABOVE_ORIGIN:
distance = (alt - loc2.alt) * 0.01;
return true;
case AltFrame::ABOVE_TERRAIN:
break;
}
}
int32_t alt1, alt2;
if (!get_alt_cm(AltFrame::ABSOLUTE, alt1) || !loc2.get_alt_cm(AltFrame::ABSOLUTE, alt2)) {
return false;
}
distance = (alt1 - alt2) * 0.01;
return true;
}
Vector2f Location::get_distance_NE(const Location &loc2) const
{
return Vector2f((loc2.lat - lat) * LOCATION_SCALING_FACTOR,
diff_longitude(loc2.lng,lng) * LOCATION_SCALING_FACTOR * longitude_scale((loc2.lat+lat)/2));
}
Vector3f Location::get_distance_NED(const Location &loc2) const
{
return Vector3f((loc2.lat - lat) * LOCATION_SCALING_FACTOR,
diff_longitude(loc2.lng,lng) * LOCATION_SCALING_FACTOR * longitude_scale((lat+loc2.lat)/2),
(alt - loc2.alt) * 0.01);
}
Vector3p Location::get_distance_NED_postype(const Location &loc2) const
{
return Vector3p((loc2.lat - lat) * LOCATION_SCALING_FACTOR,
diff_longitude(loc2.lng,lng) * LOCATION_SCALING_FACTOR * longitude_scale((lat+loc2.lat)/2),
(alt - loc2.alt) * 0.01);
}
Vector3d Location::get_distance_NED_double(const Location &loc2) const
{
return Vector3d((loc2.lat - lat) * double(LOCATION_SCALING_FACTOR),
diff_longitude(loc2.lng,lng) * LOCATION_SCALING_FACTOR * longitude_scale((lat+loc2.lat)/2),
(alt - loc2.alt) * 0.01);
}
Vector3f Location::get_distance_NED_alt_frame(const Location &loc2) const
{
int32_t alt1, alt2 = 0;
if (!get_alt_cm(AltFrame::ABSOLUTE, alt1) || !loc2.get_alt_cm(AltFrame::ABSOLUTE, alt2)) {
alt1 = 0, alt2 = 0;
}
return Vector3f((loc2.lat - lat) * LOCATION_SCALING_FACTOR,
diff_longitude(loc2.lng,lng) * LOCATION_SCALING_FACTOR * longitude_scale((loc2.lat+lat)/2),
(alt1 - alt2) * 0.01);
}
Vector2d Location::get_distance_NE_double(const Location &loc2) const
{
return Vector2d((loc2.lat - lat) * double(LOCATION_SCALING_FACTOR),
diff_longitude(loc2.lng,lng) * double(LOCATION_SCALING_FACTOR) * longitude_scale((lat+loc2.lat)/2));
}
Vector2p Location::get_distance_NE_postype(const Location &loc2) const
{
return Vector2p((loc2.lat - lat) * double(LOCATION_SCALING_FACTOR),
diff_longitude(loc2.lng,lng) * double(LOCATION_SCALING_FACTOR) * longitude_scale((lat+loc2.lat)/2));
}
Vector2F Location::get_distance_NE_ftype(const Location &loc2) const
{
return Vector2F((loc2.lat - lat) * ftype(LOCATION_SCALING_FACTOR),
diff_longitude(loc2.lng,lng) * ftype(LOCATION_SCALING_FACTOR) * longitude_scale((lat+loc2.lat)/2));
}
void Location::offset_latlng(int32_t &lat, int32_t &lng, ftype ofs_north, ftype ofs_east)
{
const int32_t dlat = ofs_north * LOCATION_SCALING_FACTOR_INV;
const int64_t dlng = (ofs_east * LOCATION_SCALING_FACTOR_INV) / longitude_scale(lat+dlat/2);
lat += dlat;
lat = limit_lattitude(lat);
lng = wrap_longitude(dlng+lng);
}
void Location::offset(ftype ofs_north, ftype ofs_east)
{
offset_latlng(lat, lng, ofs_north, ofs_east);
}
void Location::offset(const Vector3p &ofs_ned)
{
offset_latlng(lat, lng, ofs_ned.x, ofs_ned.y);
alt += -ofs_ned.z * 100;
}
void Location::offset_bearing(ftype bearing_deg, ftype distance)
{
const ftype ofs_north = cosF(radians(bearing_deg)) * distance;
const ftype ofs_east = sinF(radians(bearing_deg)) * distance;
offset(ofs_north, ofs_east);
}
void Location::offset_bearing_and_pitch(ftype bearing_deg, ftype pitch_deg, ftype distance)
{
const ftype ofs_north = cosF(radians(pitch_deg)) * cosF(radians(bearing_deg)) * distance;
const ftype ofs_east = cosF(radians(pitch_deg)) * sinF(radians(bearing_deg)) * distance;
offset(ofs_north, ofs_east);
const int32_t dalt = sinF(radians(pitch_deg)) * distance *100.0f;
alt += dalt;
}
ftype Location::longitude_scale(int32_t lat)
{
ftype scale = cosF(lat * (1.0e-7 * DEG_TO_RAD));
return MAX(scale, 0.01);
}
bool Location::sanitize(const Location &defaultLoc)
{
bool has_changed = false;
if (lat == 0 && lng == 0) {
lat = defaultLoc.lat;
lng = defaultLoc.lng;
has_changed = true;
}
if (alt == 0 && relative_alt) {
int32_t defaultLoc_alt;
if (defaultLoc.get_alt_cm(get_alt_frame(), defaultLoc_alt)) {
alt = defaultLoc_alt;
has_changed = true;
}
}
if (!check_latlng()) {
lat = defaultLoc.lat;
lng = defaultLoc.lng;
has_changed = true;
}
return has_changed;
}
ftype Location::get_bearing(const Location &loc2) const
{
const int32_t off_x = diff_longitude(loc2.lng,lng);
const int32_t off_y = (loc2.lat - lat) / loc2.longitude_scale((lat+loc2.lat)/2);
ftype bearing = (M_PI*0.5) + atan2F(-off_y, off_x);
if (bearing < 0) {
bearing += 2*M_PI;
}
return bearing;
}
bool Location::same_latlon_as(const Location &loc2) const
{
return (lat == loc2.lat) && (lng == loc2.lng);
}
bool Location::same_alt_as(const Location &loc2) const
{
if (this->get_alt_frame() == loc2.get_alt_frame()) {
return this->alt == loc2.alt;
}
ftype alt_diff;
bool have_diff = this->get_height_above(loc2, alt_diff);
const ftype tolerance = FLT_EPSILON;
return have_diff && (fabsF(alt_diff) < tolerance);
}
bool Location::check_latlng() const
{
return check_lat(lat) && check_lng(lng);
}
bool Location::past_interval_finish_line(const Location &point1, const Location &point2) const
{
return this->line_path_proportion(point1, point2) >= 1.0f;
}
float Location::line_path_proportion(const Location &point1, const Location &point2) const
{
const Vector2f vec1 = point1.get_distance_NE(point2);
const Vector2f vec2 = point1.get_distance_NE(*this);
const ftype dsquared = sq(vec1.x) + sq(vec1.y);
if (dsquared < 0.001f) {
return 1.0f;
}
return (vec1 * vec2) / dsquared;
}
int32_t Location::wrap_longitude(int64_t lon)
{
if (lon > 1800000000L) {
lon = int32_t(lon-3600000000LL);
} else if (lon < -1800000000L) {
lon = int32_t(lon+3600000000LL);
}
return int32_t(lon);
}
int32_t Location::diff_longitude(int32_t lon1, int32_t lon2)
{
if ((lon1 & 0x80000000) == (lon2 & 0x80000000)) {
return lon1 - lon2;
}
int64_t dlon = int64_t(lon1)-int64_t(lon2);
if (dlon > 1800000000LL) {
dlon -= 3600000000LL;
} else if (dlon < -1800000000LL) {
dlon += 3600000000LL;
}
return int32_t(dlon);
}
int32_t Location::limit_lattitude(int32_t lat)
{
if (lat > 900000000L) {
lat = 1800000000LL - lat;
} else if (lat < -900000000L) {
lat = -(1800000000LL + lat);
}
return lat;
}
void Location::linearly_interpolate_alt(const Location &point1, const Location &point2)
{
set_alt_cm(point1.alt + (point2.alt - point1.alt) * constrain_float(line_path_proportion(point1, point2), 0.0f, 1.0f), point2.get_alt_frame());
}
#endif