CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
Ardupilot

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.

GitHub Repository: Ardupilot/ardupilot
Path: blob/master/libraries/AP_Beacon/AP_Beacon.cpp
Views: 1798
1
/*
2
This program is free software: you can redistribute it and/or modify
3
it under the terms of the GNU General Public License as published by
4
the Free Software Foundation, either version 3 of the License, or
5
(at your option) any later version.
6
7
This program is distributed in the hope that it will be useful,
8
but WITHOUT ANY WARRANTY; without even the implied warranty of
9
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
GNU General Public License for more details.
11
12
You should have received a copy of the GNU General Public License
13
along with this program. If not, see <http://www.gnu.org/licenses/>.
14
*/
15
16
#include "AP_Beacon.h"
17
18
#if AP_BEACON_ENABLED
19
20
#include "AP_Beacon_Backend.h"
21
#include "AP_Beacon_Pozyx.h"
22
#include "AP_Beacon_Marvelmind.h"
23
#include "AP_Beacon_Nooploop.h"
24
#include "AP_Beacon_SITL.h"
25
26
#include <AP_Common/Location.h>
27
#include <AP_Logger/AP_Logger.h>
28
29
extern const AP_HAL::HAL &hal;
30
31
#ifndef AP_BEACON_MINIMUM_FENCE_BEACONS
32
#define AP_BEACON_MINIMUM_FENCE_BEACONS 3
33
#endif
34
35
// table of user settable parameters
36
const AP_Param::GroupInfo AP_Beacon::var_info[] = {
37
38
// @Param: _TYPE
39
// @DisplayName: Beacon based position estimation device type
40
// @Description: What type of beacon based position estimation device is connected
41
// @Values: 0:None,1:Pozyx,2:Marvelmind,3:Nooploop,10:SITL
42
// @User: Advanced
43
AP_GROUPINFO_FLAGS("_TYPE", 0, AP_Beacon, _type, 0, AP_PARAM_FLAG_ENABLE),
44
45
// @Param: _LATITUDE
46
// @DisplayName: Beacon origin's latitude
47
// @Description: Beacon origin's latitude
48
// @Units: deg
49
// @Increment: 0.000001
50
// @Range: -90 90
51
// @User: Advanced
52
AP_GROUPINFO("_LATITUDE", 1, AP_Beacon, origin_lat, 0),
53
54
// @Param: _LONGITUDE
55
// @DisplayName: Beacon origin's longitude
56
// @Description: Beacon origin's longitude
57
// @Units: deg
58
// @Increment: 0.000001
59
// @Range: -180 180
60
// @User: Advanced
61
AP_GROUPINFO("_LONGITUDE", 2, AP_Beacon, origin_lon, 0),
62
63
// @Param: _ALT
64
// @DisplayName: Beacon origin's altitude above sealevel in meters
65
// @Description: Beacon origin's altitude above sealevel in meters
66
// @Units: m
67
// @Increment: 1
68
// @Range: 0 10000
69
// @User: Advanced
70
AP_GROUPINFO("_ALT", 3, AP_Beacon, origin_alt, 0),
71
72
// @Param: _ORIENT_YAW
73
// @DisplayName: Beacon systems rotation from north in degrees
74
// @Description: Beacon systems rotation from north in degrees
75
// @Units: deg
76
// @Increment: 1
77
// @Range: -180 +180
78
// @User: Advanced
79
AP_GROUPINFO("_ORIENT_YAW", 4, AP_Beacon, orient_yaw, 0),
80
81
AP_GROUPEND
82
};
83
84
AP_Beacon::AP_Beacon()
85
{
86
#if CONFIG_HAL_BOARD == HAL_BOARD_SITL
87
if (_singleton != nullptr) {
88
AP_HAL::panic("Fence must be singleton");
89
}
90
#endif
91
_singleton = this;
92
AP_Param::setup_object_defaults(this, var_info);
93
}
94
95
// initialise the AP_Beacon class
96
void AP_Beacon::init(void)
97
{
98
if (_driver != nullptr) {
99
// init called a 2nd time?
100
return;
101
}
102
103
// create backend
104
switch ((Type)_type) {
105
case Type::Pozyx:
106
_driver = NEW_NOTHROW AP_Beacon_Pozyx(*this);
107
break;
108
case Type::Marvelmind:
109
_driver = NEW_NOTHROW AP_Beacon_Marvelmind(*this);
110
break;
111
case Type::Nooploop:
112
_driver = NEW_NOTHROW AP_Beacon_Nooploop(*this);
113
break;
114
#if AP_BEACON_SITL_ENABLED
115
case Type::SITL:
116
_driver = NEW_NOTHROW AP_Beacon_SITL(*this);
117
break;
118
#endif
119
case Type::None:
120
break;
121
}
122
}
123
124
// return true if beacon feature is enabled
125
bool AP_Beacon::enabled(void) const
126
{
127
return (_type != Type::None);
128
}
129
130
// return true if sensor is basically healthy (we are receiving data)
131
bool AP_Beacon::healthy(void) const
132
{
133
if (!device_ready()) {
134
return false;
135
}
136
return _driver->healthy();
137
}
138
139
// update state. This should be called often from the main loop
140
void AP_Beacon::update(void)
141
{
142
if (!device_ready()) {
143
return;
144
}
145
_driver->update();
146
147
// update boundary for fence
148
update_boundary_points();
149
}
150
151
// return origin of position estimate system
152
bool AP_Beacon::get_origin(Location &origin_loc) const
153
{
154
if (!device_ready()) {
155
return false;
156
}
157
158
// check for un-initialised origin
159
if (is_zero(origin_lat) && is_zero(origin_lon) && is_zero(origin_alt)) {
160
return false;
161
}
162
163
// return origin
164
origin_loc = {};
165
origin_loc.lat = origin_lat * 1.0e7f;
166
origin_loc.lng = origin_lon * 1.0e7f;
167
origin_loc.alt = origin_alt * 100;
168
169
return true;
170
}
171
172
// return position in NED from position estimate system's origin in meters
173
bool AP_Beacon::get_vehicle_position_ned(Vector3f &position, float& accuracy_estimate) const
174
{
175
if (!device_ready()) {
176
return false;
177
}
178
179
// check for timeout
180
if (AP_HAL::millis() - veh_pos_update_ms > AP_BEACON_TIMEOUT_MS) {
181
return false;
182
}
183
184
// return position
185
position = veh_pos_ned;
186
accuracy_estimate = veh_pos_accuracy;
187
return true;
188
}
189
190
// return the number of beacons
191
uint8_t AP_Beacon::count() const
192
{
193
if (!device_ready()) {
194
return 0;
195
}
196
return num_beacons;
197
}
198
199
// return all beacon data
200
bool AP_Beacon::get_beacon_data(uint8_t beacon_instance, struct BeaconState& state) const
201
{
202
if (!device_ready() || beacon_instance >= num_beacons) {
203
return false;
204
}
205
state = beacon_state[beacon_instance];
206
return true;
207
}
208
209
// return individual beacon's id
210
uint8_t AP_Beacon::beacon_id(uint8_t beacon_instance) const
211
{
212
if (beacon_instance >= num_beacons) {
213
return 0;
214
}
215
return beacon_state[beacon_instance].id;
216
}
217
218
// return beacon health
219
bool AP_Beacon::beacon_healthy(uint8_t beacon_instance) const
220
{
221
if (beacon_instance >= num_beacons) {
222
return false;
223
}
224
return beacon_state[beacon_instance].healthy;
225
}
226
227
// return distance to beacon in meters
228
float AP_Beacon::beacon_distance(uint8_t beacon_instance) const
229
{
230
if ( beacon_instance >= num_beacons || !beacon_state[beacon_instance].healthy) {
231
return 0.0f;
232
}
233
return beacon_state[beacon_instance].distance;
234
}
235
236
// return beacon position in meters
237
Vector3f AP_Beacon::beacon_position(uint8_t beacon_instance) const
238
{
239
if (!device_ready() || beacon_instance >= num_beacons) {
240
Vector3f temp = {};
241
return temp;
242
}
243
return beacon_state[beacon_instance].position;
244
}
245
246
// return last update time from beacon in milliseconds
247
uint32_t AP_Beacon::beacon_last_update_ms(uint8_t beacon_instance) const
248
{
249
if (_type == Type::None || beacon_instance >= num_beacons) {
250
return 0;
251
}
252
return beacon_state[beacon_instance].distance_update_ms;
253
}
254
255
// create fence boundary points
256
void AP_Beacon::update_boundary_points()
257
{
258
// we need three beacons at least to create boundary fence.
259
// update boundary fence if number of beacons changes
260
if (!device_ready() || num_beacons < AP_BEACON_MINIMUM_FENCE_BEACONS || boundary_num_beacons == num_beacons) {
261
return;
262
}
263
264
// record number of beacons so we do not repeat calculations
265
boundary_num_beacons = num_beacons;
266
267
// accumulate beacon points
268
Vector2f beacon_points[AP_BEACON_MAX_BEACONS];
269
for (uint8_t index = 0; index < num_beacons; index++) {
270
const Vector3f& point_3d = beacon_position(index);
271
beacon_points[index].x = point_3d.x;
272
beacon_points[index].y = point_3d.y;
273
}
274
275
// create polygon around boundary points using the following algorithm
276
// set the "current point" as the first boundary point
277
// 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
278
// check if point is already in boundary
279
// - no: add to boundary, move current point to this new point and repeat the above
280
// - yes: we've completed the bounding box, delete any boundary points found earlier than the duplicate
281
282
Vector2f boundary_points[AP_BEACON_MAX_BEACONS+1]; // array of boundary points
283
uint8_t curr_boundary_idx = 0; // index into boundary_sorted index. always points to the highest filled in element of the array
284
uint8_t curr_beacon_idx = 0; // index into beacon_point array. point indexed is same point as curr_boundary_idx's
285
286
// 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)
287
boundary_points[curr_boundary_idx] = beacon_points[curr_beacon_idx];
288
289
bool boundary_success = false; // true once the boundary has been successfully found
290
bool boundary_failure = false; // true if we fail to build the boundary
291
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
292
while (!boundary_success && !boundary_failure) {
293
// look for next outer point
294
uint8_t next_idx;
295
float next_angle;
296
if (get_next_boundary_point(beacon_points, num_beacons, curr_beacon_idx, start_angle, next_idx, next_angle)) {
297
// add boundary point to boundary_sorted array
298
curr_boundary_idx++;
299
boundary_points[curr_boundary_idx] = beacon_points[next_idx];
300
curr_beacon_idx = next_idx;
301
start_angle = next_angle;
302
303
// check if we have a complete boundary by looking for duplicate points within the boundary_sorted
304
uint8_t dup_idx = 0;
305
bool dup_found = false;
306
while (dup_idx < curr_boundary_idx && !dup_found) {
307
dup_found = (boundary_points[dup_idx] == boundary_points[curr_boundary_idx]);
308
if (!dup_found) {
309
dup_idx++;
310
}
311
}
312
// if duplicate is found, remove all boundary points before the duplicate because they are inner points
313
if (dup_found) {
314
// note that the closing/duplicate point is not
315
// included in the boundary points.
316
const uint8_t num_pts = curr_boundary_idx - dup_idx;
317
if (num_pts >= AP_BEACON_MINIMUM_FENCE_BEACONS) { // we consider three points to be a polygon
318
// success, copy boundary points to boundary array and convert meters to cm
319
for (uint8_t j = 0; j < num_pts; j++) {
320
boundary[j] = boundary_points[j+dup_idx] * 100.0f;
321
}
322
boundary_num_points = num_pts;
323
boundary_success = true;
324
} else {
325
// boundary has too few points
326
boundary_failure = true;
327
}
328
}
329
} else {
330
// failed to create boundary - give up!
331
boundary_failure = true;
332
}
333
}
334
335
// clear boundary on failure
336
if (boundary_failure) {
337
boundary_num_points = 0;
338
}
339
}
340
341
// find next boundary point from an array of boundary points given the current index into that array
342
// returns true if a next point can be found
343
// current_index should be an index into the boundary_pts array
344
// start_angle is an angle (in radians), the search will sweep clockwise from this angle
345
// the index of the next point is returned in the next_index argument
346
// the angle to the next point is returned in the next_angle argument
347
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)
348
{
349
// sanity check
350
if (boundary_pts == nullptr || current_index >= num_points) {
351
return false;
352
}
353
354
// get current point
355
Vector2f curr_point = boundary_pts[current_index];
356
357
// search through all points for next boundary point in a clockwise direction
358
float lowest_angle = M_PI_2;
359
float lowest_angle_relative = M_PI_2;
360
bool lowest_found = false;
361
uint8_t lowest_index = 0;
362
for (uint8_t i=0; i < num_points; i++) {
363
if (i != current_index) {
364
Vector2f vec = boundary_pts[i] - curr_point;
365
if (!vec.is_zero()) {
366
float angle = wrap_2PI(atan2f(vec.y, vec.x));
367
float angle_relative = wrap_2PI(angle - start_angle);
368
if ((angle_relative < lowest_angle_relative) || !lowest_found) {
369
lowest_angle = angle;
370
lowest_angle_relative = angle_relative;
371
lowest_index = i;
372
lowest_found = true;
373
}
374
}
375
}
376
}
377
378
// return results
379
if (lowest_found) {
380
next_index = lowest_index;
381
next_angle = lowest_angle;
382
}
383
return lowest_found;
384
}
385
386
// return fence boundary array
387
const Vector2f* AP_Beacon::get_boundary_points(uint16_t& num_points) const
388
{
389
if (!device_ready()) {
390
num_points = 0;
391
return nullptr;
392
}
393
394
num_points = boundary_num_points;
395
return boundary;
396
}
397
398
// check if the device is ready
399
bool AP_Beacon::device_ready(void) const
400
{
401
return ((_driver != nullptr) && (_type != Type::None));
402
}
403
404
#if HAL_LOGGING_ENABLED
405
// Write beacon sensor (position) data
406
void AP_Beacon::log()
407
{
408
if (!enabled()) {
409
return;
410
}
411
// position
412
Vector3f pos;
413
float accuracy = 0.0f;
414
get_vehicle_position_ned(pos, accuracy);
415
416
const struct log_Beacon pkt_beacon{
417
LOG_PACKET_HEADER_INIT(LOG_BEACON_MSG),
418
time_us : AP_HAL::micros64(),
419
health : (uint8_t)healthy(),
420
count : (uint8_t)count(),
421
dist0 : beacon_distance(0),
422
dist1 : beacon_distance(1),
423
dist2 : beacon_distance(2),
424
dist3 : beacon_distance(3),
425
posx : pos.x,
426
posy : pos.y,
427
posz : pos.z
428
};
429
AP::logger().WriteBlock(&pkt_beacon, sizeof(pkt_beacon));
430
}
431
#endif
432
433
// singleton instance
434
AP_Beacon *AP_Beacon::_singleton;
435
436
namespace AP {
437
438
AP_Beacon *beacon()
439
{
440
return AP_Beacon::get_singleton();
441
}
442
443
}
444
445
#endif
446
447