2016-10-24 02:46:07 -03:00
/*
This program is free software : you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
# include "AP_Beacon.h"
2022-01-27 20:09:43 -04:00
# if AP_BEACON_ENABLED
2016-10-24 02:46:07 -03:00
# include "AP_Beacon_Backend.h"
# include "AP_Beacon_Pozyx.h"
2017-04-03 17:53:50 -03:00
# include "AP_Beacon_Marvelmind.h"
2020-09-11 06:16:38 -03:00
# include "AP_Beacon_Nooploop.h"
2016-10-24 02:46:07 -03:00
# include "AP_Beacon_SITL.h"
2019-01-01 23:26:29 -04:00
# include <AP_Common/Location.h>
2022-03-03 21:31:25 -04:00
# include <AP_Logger/AP_Logger.h>
2019-01-01 23:26:29 -04:00
2016-10-24 02:46:07 -03:00
extern const AP_HAL : : HAL & hal ;
2024-08-25 02:30:12 -03:00
# ifndef AP_BEACON_MINIMUM_FENCE_BEACONS
# define AP_BEACON_MINIMUM_FENCE_BEACONS 3
# endif
2016-10-24 02:46:07 -03:00
// table of user settable parameters
const AP_Param : : GroupInfo AP_Beacon : : var_info [ ] = {
// @Param: _TYPE
// @DisplayName: Beacon based position estimation device type
// @Description: What type of beacon based position estimation device is connected
2020-09-11 06:16:38 -03:00
// @Values: 0:None,1:Pozyx,2:Marvelmind,3:Nooploop,10:SITL
2016-10-24 02:46:07 -03:00
// @User: Advanced
2021-08-08 18:26:48 -03:00
AP_GROUPINFO_FLAGS ( " _TYPE " , 0 , AP_Beacon , _type , 0 , AP_PARAM_FLAG_ENABLE ) ,
2016-10-24 02:46:07 -03:00
// @Param: _LATITUDE
// @DisplayName: Beacon origin's latitude
// @Description: Beacon origin's latitude
2017-05-02 10:41:50 -03:00
// @Units: deg
2016-10-24 02:46:07 -03:00
// @Increment: 0.000001
// @Range: -90 90
// @User: Advanced
AP_GROUPINFO ( " _LATITUDE " , 1 , AP_Beacon , origin_lat , 0 ) ,
// @Param: _LONGITUDE
// @DisplayName: Beacon origin's longitude
// @Description: Beacon origin's longitude
2017-05-02 10:41:50 -03:00
// @Units: deg
2016-10-24 02:46:07 -03:00
// @Increment: 0.000001
// @Range: -180 180
// @User: Advanced
AP_GROUPINFO ( " _LONGITUDE " , 2 , AP_Beacon , origin_lon , 0 ) ,
// @Param: _ALT
// @DisplayName: Beacon origin's altitude above sealevel in meters
// @Description: Beacon origin's altitude above sealevel in meters
2017-05-02 10:41:50 -03:00
// @Units: m
2016-10-24 02:46:07 -03:00
// @Increment: 1
// @Range: 0 10000
// @User: Advanced
AP_GROUPINFO ( " _ALT " , 3 , AP_Beacon , origin_alt , 0 ) ,
// @Param: _ORIENT_YAW
// @DisplayName: Beacon systems rotation from north in degrees
// @Description: Beacon systems rotation from north in degrees
2017-05-02 10:41:50 -03:00
// @Units: deg
2016-10-24 02:46:07 -03:00
// @Increment: 1
// @Range: -180 +180
// @User: Advanced
AP_GROUPINFO ( " _ORIENT_YAW " , 4 , AP_Beacon , orient_yaw , 0 ) ,
AP_GROUPEND
} ;
2022-05-31 23:12:32 -03:00
AP_Beacon : : AP_Beacon ( )
2016-10-24 02:46:07 -03:00
{
2019-05-22 03:01:49 -03:00
# if CONFIG_HAL_BOARD == HAL_BOARD_SITL
if ( _singleton ! = nullptr ) {
AP_HAL : : panic ( " Fence must be singleton " ) ;
}
# endif
_singleton = this ;
2016-10-24 02:46:07 -03:00
AP_Param : : setup_object_defaults ( this , var_info ) ;
}
// initialise the AP_Beacon class
void AP_Beacon : : init ( void )
{
if ( _driver ! = nullptr ) {
// init called a 2nd time?
return ;
}
// create backend
2024-06-23 22:22:18 -03:00
switch ( ( Type ) _type ) {
case Type : : Pozyx :
2024-05-26 22:24:09 -03:00
_driver = NEW_NOTHROW AP_Beacon_Pozyx ( * this ) ;
2024-06-23 22:22:18 -03:00
break ;
case Type : : Marvelmind :
2024-05-26 22:24:09 -03:00
_driver = NEW_NOTHROW AP_Beacon_Marvelmind ( * this ) ;
2024-06-23 22:22:18 -03:00
break ;
case Type : : Nooploop :
2024-05-26 22:24:09 -03:00
_driver = NEW_NOTHROW AP_Beacon_Nooploop ( * this ) ;
2024-06-23 22:22:18 -03:00
break ;
# if AP_BEACON_SITL_ENABLED
case Type : : SITL :
2024-05-26 22:24:09 -03:00
_driver = NEW_NOTHROW AP_Beacon_SITL ( * this ) ;
2024-06-23 22:22:18 -03:00
break ;
2016-10-24 02:46:07 -03:00
# endif
2024-06-23 22:22:18 -03:00
case Type : : None :
break ;
}
2016-10-24 02:46:07 -03:00
}
// return true if beacon feature is enabled
2020-11-08 21:27:27 -04:00
bool AP_Beacon : : enabled ( void ) const
2016-10-24 02:46:07 -03:00
{
2024-06-23 22:22:18 -03:00
return ( _type ! = Type : : None ) ;
2016-10-24 02:46:07 -03:00
}
// return true if sensor is basically healthy (we are receiving data)
2020-11-08 21:27:27 -04:00
bool AP_Beacon : : healthy ( void ) const
2016-10-24 02:46:07 -03:00
{
2016-12-09 13:33:25 -04:00
if ( ! device_ready ( ) ) {
2016-10-24 02:46:07 -03:00
return false ;
}
return _driver - > healthy ( ) ;
}
// update state. This should be called often from the main loop
void AP_Beacon : : update ( void )
{
2016-12-09 13:33:25 -04:00
if ( ! device_ready ( ) ) {
2016-10-24 02:46:07 -03:00
return ;
}
_driver - > update ( ) ;
2017-06-08 03:58:15 -03:00
// update boundary for fence
update_boundary_points ( ) ;
2016-10-24 02:46:07 -03:00
}
// return origin of position estimate system
bool AP_Beacon : : get_origin ( Location & origin_loc ) const
{
2016-12-09 13:33:25 -04:00
if ( ! device_ready ( ) ) {
2016-10-24 02:46:07 -03:00
return false ;
}
2017-05-08 12:34:55 -03:00
// check for un-initialised origin
2016-10-24 02:46:07 -03:00
if ( is_zero ( origin_lat ) & & is_zero ( origin_lon ) & & is_zero ( origin_alt ) ) {
return false ;
}
// return origin
2019-01-01 23:26:29 -04:00
origin_loc = { } ;
2019-04-04 19:06:55 -03:00
origin_loc . lat = origin_lat * 1.0e7 f ;
origin_loc . lng = origin_lon * 1.0e7 f ;
2016-10-24 02:46:07 -03:00
origin_loc . alt = origin_alt * 100 ;
return true ;
}
2017-05-08 12:34:55 -03:00
// return position in NED from position estimate system's origin in meters
2016-10-24 02:46:07 -03:00
bool AP_Beacon : : get_vehicle_position_ned ( Vector3f & position , float & accuracy_estimate ) const
{
2016-12-09 13:33:25 -04:00
if ( ! device_ready ( ) ) {
2016-10-24 02:46:07 -03:00
return false ;
}
// check for timeout
if ( AP_HAL : : millis ( ) - veh_pos_update_ms > AP_BEACON_TIMEOUT_MS ) {
return false ;
}
// return position
position = veh_pos_ned ;
accuracy_estimate = veh_pos_accuracy ;
return true ;
}
// return the number of beacons
uint8_t AP_Beacon : : count ( ) const
{
2016-12-09 13:33:25 -04:00
if ( ! device_ready ( ) ) {
2016-10-24 02:46:07 -03:00
return 0 ;
}
return num_beacons ;
}
// return all beacon data
bool AP_Beacon : : get_beacon_data ( uint8_t beacon_instance , struct BeaconState & state ) const
{
2016-12-09 13:33:25 -04:00
if ( ! device_ready ( ) | | beacon_instance > = num_beacons ) {
2016-10-24 02:46:07 -03:00
return false ;
}
state = beacon_state [ beacon_instance ] ;
return true ;
}
// return individual beacon's id
uint8_t AP_Beacon : : beacon_id ( uint8_t beacon_instance ) const
{
if ( beacon_instance > = num_beacons ) {
return 0 ;
}
return beacon_state [ beacon_instance ] . id ;
}
// return beacon health
bool AP_Beacon : : beacon_healthy ( uint8_t beacon_instance ) const
{
if ( beacon_instance > = num_beacons ) {
return false ;
}
return beacon_state [ beacon_instance ] . healthy ;
}
// return distance to beacon in meters
float AP_Beacon : : beacon_distance ( uint8_t beacon_instance ) const
{
2021-08-10 19:35:43 -03:00
if ( beacon_instance > = num_beacons | | ! beacon_state [ beacon_instance ] . healthy ) {
2016-10-24 02:46:07 -03:00
return 0.0f ;
}
return beacon_state [ beacon_instance ] . distance ;
}
2017-05-08 12:34:55 -03:00
// return beacon position in meters
2016-10-24 02:46:07 -03:00
Vector3f AP_Beacon : : beacon_position ( uint8_t beacon_instance ) const
{
2016-12-09 13:33:25 -04:00
if ( ! device_ready ( ) | | beacon_instance > = num_beacons ) {
2016-10-24 02:46:07 -03:00
Vector3f temp = { } ;
return temp ;
}
return beacon_state [ beacon_instance ] . position ;
}
2017-05-08 12:34:55 -03:00
// return last update time from beacon in milliseconds
2016-10-24 02:46:07 -03:00
uint32_t AP_Beacon : : beacon_last_update_ms ( uint8_t beacon_instance ) const
{
2024-06-23 22:22:18 -03:00
if ( _type = = Type : : None | | beacon_instance > = num_beacons ) {
2016-10-24 02:46:07 -03:00
return 0 ;
}
return beacon_state [ beacon_instance ] . distance_update_ms ;
}
2016-12-09 13:33:25 -04:00
2017-06-08 03:58:15 -03:00
// create fence boundary points
void AP_Beacon : : update_boundary_points ( )
{
// we need three beacons at least to create boundary fence.
// update boundary fence if number of beacons changes
if ( ! device_ready ( ) | | num_beacons < AP_BEACON_MINIMUM_FENCE_BEACONS | | boundary_num_beacons = = num_beacons ) {
return ;
}
// record number of beacons so we do not repeat calculations
boundary_num_beacons = num_beacons ;
// accumulate beacon points
Vector2f beacon_points [ AP_BEACON_MAX_BEACONS ] ;
for ( uint8_t index = 0 ; index < num_beacons ; index + + ) {
const Vector3f & point_3d = beacon_position ( index ) ;
beacon_points [ index ] . x = point_3d . x ;
beacon_points [ index ] . y = point_3d . y ;
}
// create polygon around boundary points using the following algorithm
// set the "current point" as the first boundary point
// loop through all the boundary points looking for the point which creates a vector (from the current point to this new point) with the lowest angle
// check if point is already in boundary
// - no: add to boundary, move current point to this new point and repeat the above
// - yes: we've completed the bounding box, delete any boundary points found earlier than the duplicate
Vector2f boundary_points [ AP_BEACON_MAX_BEACONS + 1 ] ; // array of boundary points
uint8_t curr_boundary_idx = 0 ; // index into boundary_sorted index. always points to the highest filled in element of the array
uint8_t curr_beacon_idx = 0 ; // index into beacon_point array. point indexed is same point as curr_boundary_idx's
// initialise first point of boundary_sorted with first beacon's position (this point may be removed later if it is found to not be on the outer boundary)
boundary_points [ curr_boundary_idx ] = beacon_points [ curr_beacon_idx ] ;
bool boundary_success = false ; // true once the boundary has been successfully found
bool boundary_failure = false ; // true if we fail to build the boundary
2020-04-24 17:06:34 -03:00
float start_angle = 0.0f ; // starting angle used when searching for next boundary point, on each iteration this climbs but never climbs past PI * 2
2017-06-08 03:58:15 -03:00
while ( ! boundary_success & & ! boundary_failure ) {
// look for next outer point
uint8_t next_idx ;
float next_angle ;
if ( get_next_boundary_point ( beacon_points , num_beacons , curr_beacon_idx , start_angle , next_idx , next_angle ) ) {
// add boundary point to boundary_sorted array
curr_boundary_idx + + ;
boundary_points [ curr_boundary_idx ] = beacon_points [ next_idx ] ;
curr_beacon_idx = next_idx ;
start_angle = next_angle ;
// check if we have a complete boundary by looking for duplicate points within the boundary_sorted
uint8_t dup_idx = 0 ;
bool dup_found = false ;
while ( dup_idx < curr_boundary_idx & & ! dup_found ) {
dup_found = ( boundary_points [ dup_idx ] = = boundary_points [ curr_boundary_idx ] ) ;
if ( ! dup_found ) {
dup_idx + + ;
}
}
// if duplicate is found, remove all boundary points before the duplicate because they are inner points
if ( dup_found ) {
2019-05-24 01:42:42 -03:00
// note that the closing/duplicate point is not
// included in the boundary points.
const uint8_t num_pts = curr_boundary_idx - dup_idx ;
if ( num_pts > = AP_BEACON_MINIMUM_FENCE_BEACONS ) { // we consider three points to be a polygon
2017-06-08 03:58:15 -03:00
// success, copy boundary points to boundary array and convert meters to cm
for ( uint8_t j = 0 ; j < num_pts ; j + + ) {
boundary [ j ] = boundary_points [ j + dup_idx ] * 100.0f ;
}
boundary_num_points = num_pts ;
boundary_success = true ;
} else {
// boundary has too few points
boundary_failure = true ;
}
}
} else {
// failed to create boundary - give up!
boundary_failure = true ;
}
}
// clear boundary on failure
if ( boundary_failure ) {
boundary_num_points = 0 ;
}
}
// find next boundary point from an array of boundary points given the current index into that array
// returns true if a next point can be found
// current_index should be an index into the boundary_pts array
// start_angle is an angle (in radians), the search will sweep clockwise from this angle
// the index of the next point is returned in the next_index argument
// the angle to the next point is returned in the next_angle argument
bool AP_Beacon : : get_next_boundary_point ( const Vector2f * boundary_pts , uint8_t num_points , uint8_t current_index , float start_angle , uint8_t & next_index , float & next_angle )
{
// sanity check
if ( boundary_pts = = nullptr | | current_index > = num_points ) {
return false ;
}
// get current point
Vector2f curr_point = boundary_pts [ current_index ] ;
// search through all points for next boundary point in a clockwise direction
float lowest_angle = M_PI_2 ;
float lowest_angle_relative = M_PI_2 ;
bool lowest_found = false ;
2017-06-20 04:34:41 -03:00
uint8_t lowest_index = 0 ;
2017-06-08 03:58:15 -03:00
for ( uint8_t i = 0 ; i < num_points ; i + + ) {
if ( i ! = current_index ) {
Vector2f vec = boundary_pts [ i ] - curr_point ;
if ( ! vec . is_zero ( ) ) {
float angle = wrap_2PI ( atan2f ( vec . y , vec . x ) ) ;
float angle_relative = wrap_2PI ( angle - start_angle ) ;
if ( ( angle_relative < lowest_angle_relative ) | | ! lowest_found ) {
lowest_angle = angle ;
lowest_angle_relative = angle_relative ;
lowest_index = i ;
lowest_found = true ;
}
}
}
}
// return results
if ( lowest_found ) {
next_index = lowest_index ;
next_angle = lowest_angle ;
}
return lowest_found ;
}
2017-06-08 03:57:24 -03:00
// return fence boundary array
const Vector2f * AP_Beacon : : get_boundary_points ( uint16_t & num_points ) const
{
if ( ! device_ready ( ) ) {
num_points = 0 ;
return nullptr ;
}
2017-06-14 21:36:27 -03:00
num_points = boundary_num_points ;
2017-06-08 03:57:24 -03:00
return boundary ;
}
2016-12-09 13:33:25 -04:00
// check if the device is ready
bool AP_Beacon : : device_ready ( void ) const
{
2024-06-23 22:22:18 -03:00
return ( ( _driver ! = nullptr ) & & ( _type ! = Type : : None ) ) ;
2016-12-09 13:33:25 -04:00
}
2019-05-22 03:01:49 -03:00
2023-07-13 21:58:05 -03:00
# if HAL_LOGGING_ENABLED
2022-03-03 21:31:25 -04:00
// Write beacon sensor (position) data
void AP_Beacon : : log ( )
{
if ( ! enabled ( ) ) {
return ;
}
// position
Vector3f pos ;
float accuracy = 0.0f ;
get_vehicle_position_ned ( pos , accuracy ) ;
const struct log_Beacon pkt_beacon {
LOG_PACKET_HEADER_INIT ( LOG_BEACON_MSG ) ,
time_us : AP_HAL : : micros64 ( ) ,
health : ( uint8_t ) healthy ( ) ,
count : ( uint8_t ) count ( ) ,
dist0 : beacon_distance ( 0 ) ,
dist1 : beacon_distance ( 1 ) ,
dist2 : beacon_distance ( 2 ) ,
dist3 : beacon_distance ( 3 ) ,
posx : pos . x ,
posy : pos . y ,
posz : pos . z
} ;
AP : : logger ( ) . WriteBlock ( & pkt_beacon , sizeof ( pkt_beacon ) ) ;
}
2023-07-13 21:58:05 -03:00
# endif
2019-05-22 03:01:49 -03:00
// singleton instance
AP_Beacon * AP_Beacon : : _singleton ;
namespace AP {
AP_Beacon * beacon ( )
{
return AP_Beacon : : get_singleton ( ) ;
}
}
2022-01-27 20:09:43 -04:00
# endif