AP_ADSB.cpp 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925
  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. This program is distributed in the hope that it will be useful,
  7. but WITHOUT ANY WARRANTY; without even the implied warranty of
  8. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  9. GNU General Public License for more details.
  10. You should have received a copy of the GNU General Public License
  11. along with this program. If not, see <http://www.gnu.org/licenses/>.
  12. */
  13. /*
  14. AP_ADSB.cpp
  15. ADS-B RF based collision avoidance module
  16. https://en.wikipedia.org/wiki/Automatic_dependent_surveillance_%E2%80%93_broadcast
  17. */
  18. #include <AP_HAL/AP_HAL.h>
  19. #include "AP_ADSB.h"
  20. #include <GCS_MAVLink/GCS_MAVLink.h>
  21. #include <stdio.h> // for sprintf
  22. #include <limits.h>
  23. #include <AP_Vehicle/AP_Vehicle.h>
  24. #include <GCS_MAVLink/GCS.h>
  25. #include <AP_Logger/AP_Logger.h>
  26. #include <AP_GPS/AP_GPS.h>
  27. #include <AP_Baro/AP_Baro.h>
  28. #include <AP_AHRS/AP_AHRS.h>
  29. #define VEHICLE_TIMEOUT_MS 5000 // if no updates in this time, drop it from the list
  30. #define ADSB_VEHICLE_LIST_SIZE_DEFAULT 25
  31. #define ADSB_VEHICLE_LIST_SIZE_MAX 100
  32. #define ADSB_CHAN_TIMEOUT_MS 15000
  33. #define ADSB_SQUAWK_OCTAL_DEFAULT 1200
  34. #define ADSB_BITBASK_RF_CAPABILITIES_UAT_IN (1 << 0)
  35. #define ADSB_BITBASK_RF_CAPABILITIES_1090ES_IN (1 << 1)
  36. #if APM_BUILD_TYPE(APM_BUILD_ArduPlane)
  37. #define ADSB_LIST_RADIUS_DEFAULT 10000 // in meters
  38. #else // APM_BUILD_TYPE(APM_BUILD_ArduCopter), Rover, Boat
  39. #define ADSB_LIST_RADIUS_DEFAULT 2000 // in meters
  40. #endif
  41. extern const AP_HAL::HAL& hal;
  42. // table of user settable parameters
  43. const AP_Param::GroupInfo AP_ADSB::var_info[] = {
  44. // @Param: ENABLE
  45. // @DisplayName: Enable ADSB
  46. // @Description: Enable ADS-B
  47. // @Values: 0:Disabled,1:Enabled
  48. // @User: Standard
  49. AP_GROUPINFO_FLAGS("ENABLE", 0, AP_ADSB, _enabled, 0, AP_PARAM_FLAG_ENABLE),
  50. // index 1 is reserved - was BEHAVIOR
  51. // @Param: LIST_MAX
  52. // @DisplayName: ADSB vehicle list size
  53. // @Description: ADSB list size of nearest vehicles. Longer lists take longer to refresh with lower SRx_ADSB values.
  54. // @Range: 1 100
  55. // @User: Advanced
  56. AP_GROUPINFO("LIST_MAX", 2, AP_ADSB, in_state.list_size_param, ADSB_VEHICLE_LIST_SIZE_DEFAULT),
  57. // @Param: LIST_RADIUS
  58. // @DisplayName: ADSB vehicle list radius filter
  59. // @Description: ADSB vehicle list radius filter. Vehicles detected outside this radius will be completely ignored. They will not show up in the SRx_ADSB stream to the GCS and will not be considered in any avoidance calculations. A value of 0 will disable this filter.
  60. // @Range: 0 100000
  61. // @User: Advanced
  62. // @Units: m
  63. AP_GROUPINFO("LIST_RADIUS", 3, AP_ADSB, in_state.list_radius, ADSB_LIST_RADIUS_DEFAULT),
  64. // @Param: ICAO_ID
  65. // @DisplayName: ICAO_ID vehicle identification number
  66. // @Description: ICAO_ID unique vehicle identification number of this aircraft. This is a integer limited to 24bits. If set to 0 then one will be randomly generated. If set to -1 then static information is not sent, transceiver is assumed pre-programmed.
  67. // @Range: -1 16777215
  68. // @User: Advanced
  69. AP_GROUPINFO("ICAO_ID", 4, AP_ADSB, out_state.cfg.ICAO_id_param, 0),
  70. // @Param: EMIT_TYPE
  71. // @DisplayName: Emitter type
  72. // @Description: ADSB classification for the type of vehicle emitting the transponder signal. Default value is 14 (UAV).
  73. // @Values: 0:NoInfo,1:Light,2:Small,3:Large,4:HighVortexlarge,5:Heavy,6:HighlyManuv,7:Rotocraft,8:RESERVED,9:Glider,10:LightAir,11:Parachute,12:UltraLight,13:RESERVED,14:UAV,15:Space,16:RESERVED,17:EmergencySurface,18:ServiceSurface,19:PointObstacle
  74. // @User: Advanced
  75. AP_GROUPINFO("EMIT_TYPE", 5, AP_ADSB, out_state.cfg.emitterType, ADSB_EMITTER_TYPE_UAV),
  76. // @Param: LEN_WIDTH
  77. // @DisplayName: Aircraft length and width
  78. // @Description: Aircraft length and width dimension options in Length and Width in meters. In most cases, use a value of 1 for smallest size.
  79. // @Values: 0:NO_DATA,1:L15W23,2:L25W28P5,3:L25W34,4:L35W33,5:L35W38,6:L45W39P5,7:L45W45,8:L55W45,9:L55W52,10:L65W59P5,11:L65W67,12:L75W72P5,13:L75W80,14:L85W80,15:L85W90
  80. // @User: Advanced
  81. AP_GROUPINFO("LEN_WIDTH", 6, AP_ADSB, out_state.cfg.lengthWidth, UAVIONIX_ADSB_OUT_CFG_AIRCRAFT_SIZE_L15M_W23M),
  82. // @Param: OFFSET_LAT
  83. // @DisplayName: GPS antenna lateral offset
  84. // @Description: GPS antenna lateral offset. This describes the physical location offest from center of the GPS antenna on the aircraft.
  85. // @Values: 0:NoData,1:Left2m,2:Left4m,3:Left6m,4:Center,5:Right2m,6:Right4m,7:Right6m
  86. // @User: Advanced
  87. AP_GROUPINFO("OFFSET_LAT", 7, AP_ADSB, out_state.cfg.gpsLatOffset, UAVIONIX_ADSB_OUT_CFG_GPS_OFFSET_LAT_RIGHT_0M),
  88. // @Param: OFFSET_LON
  89. // @DisplayName: GPS antenna longitudinal offset
  90. // @Description: GPS antenna longitudinal offset. This is usually set to 1, Applied By Sensor
  91. // @Values: 0:NO_DATA,1:AppliedBySensor
  92. // @User: Advanced
  93. AP_GROUPINFO("OFFSET_LON", 8, AP_ADSB, out_state.cfg.gpsLonOffset, UAVIONIX_ADSB_OUT_CFG_GPS_OFFSET_LON_APPLIED_BY_SENSOR),
  94. // @Param: RF_SELECT
  95. // @DisplayName: Transceiver RF selection
  96. // @Description: Transceiver RF selection for Rx enable and/or Tx enable. This only effects devices that can Tx and Rx. Rx-only devices override this to always be Rx-only.
  97. // @Values: 0:Disabled,1:Rx-Only,2:Tx-Only,3:Rx and Tx Enabled
  98. // @User: Advanced
  99. AP_GROUPINFO("RF_SELECT", 9, AP_ADSB, out_state.cfg.rfSelect, UAVIONIX_ADSB_OUT_RF_SELECT_RX_ENABLED),
  100. // @Param: SQUAWK
  101. // @DisplayName: Squawk code
  102. // @Description: VFR squawk (Mode 3/A) code is a pre-programmed default code when the pilot is flying VFR and not in contact with ATC. In the USA, the VFR squawk code is octal 1200 (hex 0x280, decimal 640) and in most parts of Europe the VFR squawk code is octal 7000. If an invalid octal number is set then it will be reset to 1200.
  103. // @Range: 0 7777
  104. // @Units: octal
  105. // @User: Advanced
  106. AP_GROUPINFO("SQUAWK", 10, AP_ADSB, out_state.cfg.squawk_octal_param, ADSB_SQUAWK_OCTAL_DEFAULT),
  107. // @Param: RF_CAPABLE
  108. // @DisplayName: RF capabilities
  109. // @Description: Describes your hardware RF In/Out capabilities.
  110. // @Values: 0:Unknown,1:Rx UAT only,3:Rx UAT and 1090ES,7:Rx&Tx UAT and 1090ES
  111. // @Bitmask: 0:UAT_in,1:1090ES_in,2:UAT_out,3:1090ES_out
  112. // @User: Advanced
  113. AP_GROUPINFO("RF_CAPABLE", 11, AP_ADSB, out_state.cfg.rf_capable, 0),
  114. // @Param: LIST_ALT
  115. // @DisplayName: ADSB vehicle list altitude filter
  116. // @Description: ADSB vehicle list altitude filter. Vehicles detected above this altitude will be completely ignored. They will not show up in the SRx_ADSB stream to the GCS and will not be considered in any avoidance calculations. A value of 0 will disable this filter.
  117. // @Range: 0 32767
  118. // @User: Advanced
  119. // @Units: m
  120. AP_GROUPINFO("LIST_ALT", 12, AP_ADSB, in_state.list_altitude, 0),
  121. // @Param: ICAO_SPECL
  122. // @DisplayName: ICAO_ID of special vehicle
  123. // @Description: ICAO_ID of special vehicle that ignores ADSB_LIST_RADIUS and ADSB_LIST_ALT. The vehicle is always tracked. Use 0 to disable.
  124. // @User: Advanced
  125. AP_GROUPINFO("ICAO_SPECL", 13, AP_ADSB, _special_ICAO_target, 0),
  126. // @Param: LOG
  127. // @DisplayName: ADS-B logging
  128. // @Description: 0: no logging, 1: log only special ID, 2:log all
  129. // @Values: 0:no logging,1:log only special ID,2:log all
  130. // @User: Advanced
  131. AP_GROUPINFO("LOG", 14, AP_ADSB, _log, 1),
  132. AP_GROUPEND
  133. };
  134. /*
  135. * Initialize variables and allocate memory for array
  136. */
  137. void AP_ADSB::init(void)
  138. {
  139. // in_state
  140. in_state.vehicle_count = 0;
  141. if (in_state.vehicle_list == nullptr) {
  142. if (in_state.list_size_param != constrain_int16(in_state.list_size_param, 1, ADSB_VEHICLE_LIST_SIZE_MAX)) {
  143. in_state.list_size_param.set_and_notify(ADSB_VEHICLE_LIST_SIZE_DEFAULT);
  144. in_state.list_size_param.save();
  145. }
  146. in_state.list_size = in_state.list_size_param;
  147. in_state.vehicle_list = new adsb_vehicle_t[in_state.list_size];
  148. if (in_state.vehicle_list == nullptr) {
  149. // dynamic RAM allocation of _vehicle_list[] failed, disable gracefully
  150. hal.console->printf("Unable to initialize ADS-B vehicle list\n");
  151. _enabled.set_and_notify(0);
  152. }
  153. }
  154. furthest_vehicle_distance = 0;
  155. furthest_vehicle_index = 0;
  156. // out_state
  157. set_callsign("PING1234", false);
  158. }
  159. /*
  160. * de-initialize and free up some memory
  161. */
  162. void AP_ADSB::deinit(void)
  163. {
  164. in_state.vehicle_count = 0;
  165. if (in_state.vehicle_list != nullptr) {
  166. delete [] in_state.vehicle_list;
  167. in_state.vehicle_list = nullptr;
  168. }
  169. }
  170. /*
  171. * periodic update to handle vehicle timeouts and trigger collision detection
  172. */
  173. void AP_ADSB::update(void)
  174. {
  175. // update _my_loc
  176. if (!AP::ahrs().get_position(_my_loc)) {
  177. _my_loc.zero();
  178. }
  179. if (!_enabled) {
  180. if (in_state.vehicle_list != nullptr) {
  181. deinit();
  182. }
  183. // nothing to do
  184. return;
  185. } else if (in_state.vehicle_list == nullptr) {
  186. init();
  187. return;
  188. } else if (in_state.list_size != in_state.list_size_param) {
  189. deinit();
  190. return;
  191. }
  192. const uint32_t now = AP_HAL::millis();
  193. // check current list for vehicles that time out
  194. uint16_t index = 0;
  195. while (index < in_state.vehicle_count) {
  196. // check list and drop stale vehicles. When disabled, the list will get flushed
  197. if (now - in_state.vehicle_list[index].last_update_ms > VEHICLE_TIMEOUT_MS) {
  198. // don't increment index, we want to check this same index again because the contents changed
  199. // also, if we're disabled then clear the list
  200. delete_vehicle(index);
  201. } else {
  202. index++;
  203. }
  204. }
  205. if (_my_loc.is_zero()) {
  206. // if we don't have a GPS lock then there's nothing else to do
  207. return;
  208. }
  209. if (out_state.chan < 0) {
  210. // if there's no transceiver detected then do not set ICAO and do not service the transceiver
  211. return;
  212. }
  213. if (out_state.cfg.squawk_octal_param != out_state.cfg.squawk_octal) {
  214. // param changed, check that it's a valid octal
  215. if (!is_valid_octal(out_state.cfg.squawk_octal_param)) {
  216. // invalid, reset it to default
  217. out_state.cfg.squawk_octal_param = ADSB_SQUAWK_OCTAL_DEFAULT;
  218. }
  219. out_state.cfg.squawk_octal = (uint16_t)out_state.cfg.squawk_octal_param;
  220. }
  221. // ensure it's positive 24bit but allow -1
  222. if (out_state.cfg.ICAO_id_param <= -1 || out_state.cfg.ICAO_id_param > 0x00FFFFFF) {
  223. // icao param of -1 means static information is not sent, transceiver is assumed pre-programmed.
  224. // reset timer constantly so it never reaches 10s so it never sends
  225. out_state.last_config_ms = now;
  226. } else if (out_state.cfg.ICAO_id == 0 ||
  227. out_state.cfg.ICAO_id_param_prev != out_state.cfg.ICAO_id_param) {
  228. // if param changed then regenerate. This allows the param to be changed back to zero to trigger a re-generate
  229. if (out_state.cfg.ICAO_id_param == 0) {
  230. out_state.cfg.ICAO_id = genICAO(_my_loc);
  231. } else {
  232. out_state.cfg.ICAO_id = out_state.cfg.ICAO_id_param;
  233. }
  234. out_state.cfg.ICAO_id_param_prev = out_state.cfg.ICAO_id_param;
  235. set_callsign("PING", true);
  236. gcs().send_text(MAV_SEVERITY_INFO, "ADSB: Using ICAO_id %d and Callsign %s", (int)out_state.cfg.ICAO_id, out_state.cfg.callsign);
  237. out_state.last_config_ms = 0; // send now
  238. }
  239. // send static configuration data to transceiver, every 5s
  240. if (out_state.chan_last_ms > 0 && now - out_state.chan_last_ms > ADSB_CHAN_TIMEOUT_MS) {
  241. // haven't gotten a heartbeat health status packet in a while, assume hardware failure
  242. // TODO: reset out_state.chan
  243. out_state.chan = -1;
  244. gcs().send_text(MAV_SEVERITY_ERROR, "ADSB: Transceiver heartbeat timed out");
  245. } else if (out_state.chan < MAVLINK_COMM_NUM_BUFFERS) {
  246. const mavlink_channel_t chan = (mavlink_channel_t)(MAVLINK_COMM_0 + out_state.chan);
  247. if (now - out_state.last_config_ms >= 5000 && HAVE_PAYLOAD_SPACE(chan, UAVIONIX_ADSB_OUT_CFG)) {
  248. out_state.last_config_ms = now;
  249. send_configure(chan);
  250. } // last_config_ms
  251. // send dynamic data to transceiver at 5Hz
  252. if (now - out_state.last_report_ms >= 200 && HAVE_PAYLOAD_SPACE(chan, UAVIONIX_ADSB_OUT_DYNAMIC)) {
  253. out_state.last_report_ms = now;
  254. send_dynamic_out(chan);
  255. } // last_report_ms
  256. } // chan_last_ms
  257. }
  258. /*
  259. * determine index and distance of furthest vehicle. This is
  260. * used to bump it off when a new closer aircraft is detected
  261. */
  262. void AP_ADSB::determine_furthest_aircraft(void)
  263. {
  264. float max_distance = 0;
  265. uint16_t max_distance_index = 0;
  266. for (uint16_t index = 0; index < in_state.vehicle_count; index++) {
  267. if (is_special_vehicle(in_state.vehicle_list[index].info.ICAO_address)) {
  268. continue;
  269. }
  270. const float distance = _my_loc.get_distance(get_location(in_state.vehicle_list[index]));
  271. if (max_distance < distance || index == 0) {
  272. max_distance = distance;
  273. max_distance_index = index;
  274. }
  275. } // for index
  276. furthest_vehicle_index = max_distance_index;
  277. furthest_vehicle_distance = max_distance;
  278. }
  279. /*
  280. * Convert/Extract a Location from a vehicle
  281. */
  282. Location AP_ADSB::get_location(const adsb_vehicle_t &vehicle) const
  283. {
  284. const Location loc = Location(
  285. vehicle.info.lat,
  286. vehicle.info.lon,
  287. vehicle.info.altitude * 0.1f,
  288. Location::AltFrame::ABSOLUTE);
  289. return loc;
  290. }
  291. /*
  292. * delete a vehicle by copying last vehicle to
  293. * current index then decrementing count
  294. */
  295. void AP_ADSB::delete_vehicle(const uint16_t index)
  296. {
  297. if (index >= in_state.vehicle_count) {
  298. // index out of range
  299. return;
  300. }
  301. // if the vehicle is the furthest, invalidate it. It has been bumped
  302. if (index == furthest_vehicle_index && furthest_vehicle_distance > 0) {
  303. furthest_vehicle_distance = 0;
  304. furthest_vehicle_index = 0;
  305. }
  306. if (index != (in_state.vehicle_count-1)) {
  307. in_state.vehicle_list[index] = in_state.vehicle_list[in_state.vehicle_count-1];
  308. }
  309. // TODO: is memset needed? When we decrement the index we essentially forget about it
  310. memset(&in_state.vehicle_list[in_state.vehicle_count-1], 0, sizeof(adsb_vehicle_t));
  311. in_state.vehicle_count--;
  312. }
  313. /*
  314. * Search _vehicle_list for the given vehicle. A match
  315. * depends on ICAO_address. Returns true if match found
  316. * and index is populated. otherwise, return false.
  317. */
  318. bool AP_ADSB::find_index(const adsb_vehicle_t &vehicle, uint16_t *index) const
  319. {
  320. for (uint16_t i = 0; i < in_state.vehicle_count; i++) {
  321. if (in_state.vehicle_list[i].info.ICAO_address == vehicle.info.ICAO_address) {
  322. *index = i;
  323. return true;
  324. }
  325. }
  326. return false;
  327. }
  328. /*
  329. * Update the vehicle list. If the vehicle is already in the
  330. * list then it will update it, otherwise it will be added.
  331. */
  332. void AP_ADSB::handle_vehicle(const mavlink_message_t &packet)
  333. {
  334. if (in_state.vehicle_list == nullptr) {
  335. // We are only null when disabled. Updating is inhibited.
  336. return;
  337. }
  338. uint16_t index = in_state.list_size + 1; // initialize with invalid index
  339. adsb_vehicle_t vehicle {};
  340. mavlink_msg_adsb_vehicle_decode(&packet, &vehicle.info);
  341. const Location vehicle_loc = AP_ADSB::get_location(vehicle);
  342. const bool my_loc_is_zero = _my_loc.is_zero();
  343. const float my_loc_distance_to_vehicle = _my_loc.get_distance(vehicle_loc);
  344. const bool is_special = is_special_vehicle(vehicle.info.ICAO_address);
  345. const bool out_of_range = in_state.list_radius > 0 && !my_loc_is_zero && my_loc_distance_to_vehicle > in_state.list_radius && !is_special;
  346. const bool out_of_range_alt = in_state.list_altitude > 0 && !my_loc_is_zero && abs(vehicle_loc.alt - _my_loc.alt) > in_state.list_altitude*100 && !is_special;
  347. const bool is_tracked_in_list = find_index(vehicle, &index);
  348. const uint32_t now = AP_HAL::millis();
  349. // note the last time the receiver got a packet from the aircraft
  350. vehicle.last_update_ms = now - (vehicle.info.tslc * 1000);
  351. const uint16_t required_flags_position = ADSB_FLAGS_VALID_COORDS | ADSB_FLAGS_VALID_ALTITUDE;
  352. const bool detected_ourself = (out_state.cfg.ICAO_id != 0) && ((uint32_t)out_state.cfg.ICAO_id == vehicle.info.ICAO_address);
  353. if (vehicle_loc.is_zero() ||
  354. out_of_range ||
  355. out_of_range_alt ||
  356. detected_ourself ||
  357. (vehicle.info.ICAO_address > 0x00FFFFFF) || // ICAO address is 24bits, so ignore higher values.
  358. !(vehicle.info.flags & required_flags_position) ||
  359. now - vehicle.last_update_ms > VEHICLE_TIMEOUT_MS) {
  360. // vehicle is out of range or invalid lat/lng. If we're tracking it, delete from list. Otherwise ignore it.
  361. if (is_tracked_in_list) {
  362. delete_vehicle(index);
  363. }
  364. return;
  365. } else if (is_tracked_in_list) {
  366. // found, update it
  367. set_vehicle(index, vehicle);
  368. } else if (in_state.vehicle_count < in_state.list_size) {
  369. // not found and there's room, add it to the end of the list
  370. set_vehicle(in_state.vehicle_count, vehicle);
  371. in_state.vehicle_count++;
  372. } else {
  373. // buffer is full. if new vehicle is closer than furthest, replace furthest with new
  374. if (my_loc_is_zero) {
  375. // nothing else to do
  376. furthest_vehicle_distance = 0;
  377. furthest_vehicle_index = 0;
  378. } else {
  379. if (furthest_vehicle_distance <= 0) {
  380. // ensure this is populated
  381. determine_furthest_aircraft();
  382. }
  383. if (my_loc_distance_to_vehicle < furthest_vehicle_distance) { // is closer than the furthest
  384. // replace with the furthest vehicle
  385. set_vehicle(furthest_vehicle_index, vehicle);
  386. // furthest_vehicle_index is now invalid because the vehicle was overwritten, need
  387. // to run determine_furthest_aircraft() to determine a new one next time
  388. furthest_vehicle_distance = 0;
  389. furthest_vehicle_index = 0;
  390. }
  391. }
  392. } // if buffer full
  393. const uint16_t required_flags_avoidance =
  394. ADSB_FLAGS_VALID_COORDS |
  395. ADSB_FLAGS_VALID_ALTITUDE |
  396. ADSB_FLAGS_VALID_HEADING |
  397. ADSB_FLAGS_VALID_VELOCITY;
  398. if (vehicle.info.flags & required_flags_avoidance) {
  399. push_sample(vehicle); // note that set_vehicle modifies vehicle
  400. }
  401. }
  402. /*
  403. * Copy a vehicle's data into the list
  404. */
  405. void AP_ADSB::set_vehicle(const uint16_t index, const adsb_vehicle_t &vehicle)
  406. {
  407. if (index >= in_state.list_size) {
  408. // out of range
  409. return;
  410. }
  411. in_state.vehicle_list[index] = vehicle;
  412. write_log(vehicle);
  413. }
  414. void AP_ADSB::send_adsb_vehicle(const mavlink_channel_t chan)
  415. {
  416. if (in_state.vehicle_list == nullptr || in_state.vehicle_count == 0) {
  417. return;
  418. }
  419. uint32_t now = AP_HAL::millis();
  420. if (in_state.send_index[chan] >= in_state.vehicle_count) {
  421. // we've finished a list
  422. if (now - in_state.send_start_ms[chan] < 1000) {
  423. // too soon to start a new one
  424. return;
  425. } else {
  426. // start new list
  427. in_state.send_start_ms[chan] = now;
  428. in_state.send_index[chan] = 0;
  429. }
  430. }
  431. if (in_state.send_index[chan] < in_state.vehicle_count) {
  432. mavlink_adsb_vehicle_t vehicle = in_state.vehicle_list[in_state.send_index[chan]].info;
  433. in_state.send_index[chan]++;
  434. mavlink_msg_adsb_vehicle_send(chan,
  435. vehicle.ICAO_address,
  436. vehicle.lat,
  437. vehicle.lon,
  438. vehicle.altitude_type,
  439. vehicle.altitude,
  440. vehicle.heading,
  441. vehicle.hor_velocity,
  442. vehicle.ver_velocity,
  443. vehicle.callsign,
  444. vehicle.emitter_type,
  445. vehicle.tslc,
  446. vehicle.flags,
  447. vehicle.squawk);
  448. }
  449. }
  450. void AP_ADSB::send_dynamic_out(const mavlink_channel_t chan)
  451. {
  452. const AP_GPS &gps = AP::gps();
  453. const Vector3f &gps_velocity = gps.velocity();
  454. const int32_t latitude = _my_loc.lat;
  455. const int32_t longitude = _my_loc.lng;
  456. const int32_t altGNSS = _my_loc.alt * 10; // convert cm to mm
  457. const int16_t velVert = gps_velocity.z * 1E2; // convert m/s to cm/s
  458. const int16_t nsVog = gps_velocity.x * 1E2; // convert m/s to cm/s
  459. const int16_t ewVog = gps_velocity.y * 1E2; // convert m/s to cm/s
  460. const uint8_t fixType = gps.status(); // this lines up perfectly with our enum
  461. const uint8_t emStatus = 0; // TODO: implement this ENUM. no emergency = 0
  462. const uint8_t numSats = gps.num_sats();
  463. const uint16_t squawk = out_state.cfg.squawk_octal;
  464. uint32_t accHoriz = UINT_MAX;
  465. float accHoriz_f;
  466. if (gps.horizontal_accuracy(accHoriz_f)) {
  467. accHoriz = accHoriz_f * 1E3; // convert m to mm
  468. }
  469. uint16_t accVert = USHRT_MAX;
  470. float accVert_f;
  471. if (gps.vertical_accuracy(accVert_f)) {
  472. accVert = accVert_f * 1E2; // convert m to cm
  473. }
  474. uint16_t accVel = USHRT_MAX;
  475. float accVel_f;
  476. if (gps.speed_accuracy(accVel_f)) {
  477. accVel = accVel_f * 1E3; // convert m/s to mm/s
  478. }
  479. uint16_t state = 0;
  480. if (out_state._is_in_auto_mode) {
  481. state |= UAVIONIX_ADSB_OUT_DYNAMIC_STATE_AUTOPILOT_ENABLED;
  482. }
  483. if (!out_state.is_flying) {
  484. state |= UAVIONIX_ADSB_OUT_DYNAMIC_STATE_ON_GROUND;
  485. }
  486. // TODO: confirm this sets utcTime correctly
  487. const uint64_t gps_time = gps.time_epoch_usec();
  488. const uint32_t utcTime = gps_time / 1000000ULL;
  489. const AP_Baro &baro = AP::baro();
  490. int32_t altPres = INT_MAX;
  491. if (baro.healthy()) {
  492. // Altitude difference between sea level pressure and current pressure. Result in millimeters
  493. altPres = baro.get_altitude_difference(SSL_AIR_PRESSURE, baro.get_pressure()) * 1E3; // convert m to mm;
  494. }
  495. mavlink_msg_uavionix_adsb_out_dynamic_send(
  496. chan,
  497. utcTime,
  498. latitude,
  499. longitude,
  500. altGNSS,
  501. fixType,
  502. numSats,
  503. altPres,
  504. accHoriz,
  505. accVert,
  506. accVel,
  507. velVert,
  508. nsVog,
  509. ewVog,
  510. emStatus,
  511. state,
  512. squawk);
  513. }
  514. /*
  515. * To expand functionality in their HW, uAvionix has extended a few of the unused MAVLink bits to pack in more new features
  516. * This function will override the MSB byte of the 24bit ICAO address. To ensure an invalid >24bit ICAO is never broadcasted,
  517. * this function is used to create the encoded verison without ever writing to the actual ICAO number. It's created on-demand
  518. */
  519. uint32_t AP_ADSB::get_encoded_icao(void)
  520. {
  521. // utilize the upper unused 8bits of the icao with special flags.
  522. // This encoding is required for uAvionix devices that break the MAVLink spec.
  523. // ensure the user assignable icao is 24 bits
  524. uint32_t encoded_icao = (uint32_t)out_state.cfg.ICAO_id & 0x00FFFFFF;
  525. encoded_icao &= ~0x20000000; // useGnssAltitude should always be FALSE
  526. encoded_icao |= 0x10000000; // csidLogic should always be TRUE
  527. //SIL/SDA are special fields that should be set to 0 with only expert user adjustment
  528. encoded_icao &= ~0x03000000; // SDA should always be FALSE
  529. encoded_icao &= ~0x0C000000; // SIL should always be FALSE
  530. return encoded_icao;
  531. }
  532. /*
  533. * To expand functionality in their HW, uAvionix has extended a few of the unused MAVLink bits to pack in more new features
  534. * This function will override the usually-null ending char of the callsign. It always encodes the last byte [8], even if
  535. * the callsign string is less than 9 chars and there are other zero-padded nulls.
  536. */
  537. uint8_t AP_ADSB::get_encoded_callsign_null_char()
  538. {
  539. // Encoding of the 8bit null char
  540. // (LSB) - knots
  541. // bit.1 - knots
  542. // bit.2 - knots
  543. // bit.3 - (unused)
  544. // bit.4 - flag - ADSB_BITBASK_RF_CAPABILITIES_1090ES_IN
  545. // bit.5 - flag - ADSB_BITBASK_RF_CAPABILITIES_UAT_IN
  546. // bit.6 - flag - 0 = callsign is treated as callsign, 1 = callsign is treated as flightPlanID/Squawk
  547. // (MSB) - (unused)
  548. uint8_t encoded_null = 0;
  549. if (out_state.cfg.maxAircraftSpeed_knots <= 0) {
  550. // not set or unknown. no bits set
  551. } else if (out_state.cfg.maxAircraftSpeed_knots <= 75) {
  552. encoded_null |= 0x01;
  553. } else if (out_state.cfg.maxAircraftSpeed_knots <= 150) {
  554. encoded_null |= 0x02;
  555. } else if (out_state.cfg.maxAircraftSpeed_knots <= 300) {
  556. encoded_null |= 0x03;
  557. } else if (out_state.cfg.maxAircraftSpeed_knots <= 600) {
  558. encoded_null |= 0x04;
  559. } else if (out_state.cfg.maxAircraftSpeed_knots <= 1200) {
  560. encoded_null |= 0x05;
  561. } else {
  562. encoded_null |= 0x06;
  563. }
  564. if (out_state.cfg.rf_capable & ADSB_BITBASK_RF_CAPABILITIES_1090ES_IN) {
  565. encoded_null |= 0x10;
  566. }
  567. if (out_state.cfg.rf_capable & ADSB_BITBASK_RF_CAPABILITIES_UAT_IN) {
  568. encoded_null |= 0x20;
  569. }
  570. /*
  571. If the user has an 8 digit flightPlanID assigned from a filed flight plan, this should be assigned to FlightPlanID, (assigned by remote app)
  572. else if the user has an assigned squawk code from ATC this should be converted from 4 digit octal to 4 character alpha string and assigned to FlightPlanID,
  573. else if a tail number is known it should be set to the tail number of the aircraft, (assigned by remote app)
  574. else it should be left blank (all 0's)
  575. */
  576. // using the above logic, we must always assign the squawk. once we get configured
  577. // externally then get_encoded_callsign_null_char() stops getting called
  578. snprintf(out_state.cfg.callsign, 5, "%04d", unsigned(out_state.cfg.squawk_octal) & 0x1FFF);
  579. memset(&out_state.cfg.callsign[4], 0, 5); // clear remaining 5 chars
  580. encoded_null |= 0x40;
  581. return encoded_null;
  582. }
  583. /*
  584. * handle incoming packet UAVIONIX_ADSB_OUT_CFG.
  585. * This allows a GCS to send cfg info through the autopilot to the ADSB hardware.
  586. * This is done indirectly by reading and storing the packet and then another mechanism sends it out periodically
  587. */
  588. void AP_ADSB::handle_out_cfg(const mavlink_message_t &msg)
  589. {
  590. mavlink_uavionix_adsb_out_cfg_t packet {};
  591. mavlink_msg_uavionix_adsb_out_cfg_decode(&msg, &packet);
  592. out_state.cfg.was_set_externally = true;
  593. out_state.cfg.ICAO_id = packet.ICAO;
  594. out_state.cfg.ICAO_id_param = out_state.cfg.ICAO_id_param_prev = packet.ICAO & 0x00FFFFFFFF;
  595. // May contain a non-null value at the end so accept it as-is with memcpy instead of strcpy
  596. memcpy(out_state.cfg.callsign, packet.callsign, sizeof(out_state.cfg.callsign));
  597. out_state.cfg.emitterType = packet.emitterType;
  598. out_state.cfg.lengthWidth = packet.aircraftSize;
  599. out_state.cfg.gpsLatOffset = packet.gpsOffsetLat;
  600. out_state.cfg.gpsLonOffset = packet.gpsOffsetLon;
  601. out_state.cfg.rfSelect = packet.rfSelect;
  602. out_state.cfg.stall_speed_cm = packet.stallSpeed;
  603. // guard against string with non-null end char
  604. const char c = out_state.cfg.callsign[MAVLINK_MSG_UAVIONIX_ADSB_OUT_CFG_FIELD_CALLSIGN_LEN-1];
  605. out_state.cfg.callsign[MAVLINK_MSG_UAVIONIX_ADSB_OUT_CFG_FIELD_CALLSIGN_LEN-1] = 0;
  606. gcs().send_text(MAV_SEVERITY_INFO, "ADSB: Using ICAO_id %d and Callsign %s", (int)out_state.cfg.ICAO_id, out_state.cfg.callsign);
  607. out_state.cfg.callsign[MAVLINK_MSG_UAVIONIX_ADSB_OUT_CFG_FIELD_CALLSIGN_LEN-1] = c;
  608. // send now
  609. out_state.last_config_ms = 0;
  610. }
  611. /*
  612. * populate and send MAVLINK_MSG_UAVIONIX_ADSB_OUT_CFG
  613. */
  614. void AP_ADSB::send_configure(const mavlink_channel_t chan)
  615. {
  616. // MAVLink spec says the 9 byte callsign field is 8 byte string with 9th byte as null.
  617. // Here we temporarily set some flags in that null char to signify the callsign
  618. // may be a flightplanID instead
  619. int8_t callsign[sizeof(out_state.cfg.callsign)];
  620. uint32_t icao;
  621. memcpy(callsign, out_state.cfg.callsign, sizeof(out_state.cfg.callsign));
  622. if (out_state.cfg.was_set_externally) {
  623. // take values as-is
  624. icao = out_state.cfg.ICAO_id;
  625. } else {
  626. callsign[MAVLINK_MSG_UAVIONIX_ADSB_OUT_CFG_FIELD_CALLSIGN_LEN-1] = (int8_t)get_encoded_callsign_null_char();
  627. icao = get_encoded_icao();
  628. }
  629. mavlink_msg_uavionix_adsb_out_cfg_send(
  630. chan,
  631. icao,
  632. (const char*)callsign,
  633. (uint8_t)out_state.cfg.emitterType,
  634. (uint8_t)out_state.cfg.lengthWidth,
  635. (uint8_t)out_state.cfg.gpsLatOffset,
  636. (uint8_t)out_state.cfg.gpsLonOffset,
  637. out_state.cfg.stall_speed_cm,
  638. (uint8_t)out_state.cfg.rfSelect);
  639. }
  640. /*
  641. * this is a message from the transceiver reporting it's health. Using this packet
  642. * we determine which channel is on so we don't have to send out_state to all channels
  643. */
  644. void AP_ADSB::handle_transceiver_report(const mavlink_channel_t chan, const mavlink_message_t &msg)
  645. {
  646. mavlink_uavionix_adsb_transceiver_health_report_t packet {};
  647. mavlink_msg_uavionix_adsb_transceiver_health_report_decode(&msg, &packet);
  648. if (out_state.chan != chan) {
  649. gcs().send_text(MAV_SEVERITY_DEBUG, "ADSB: Found transceiver on channel %d", chan);
  650. }
  651. out_state.chan_last_ms = AP_HAL::millis();
  652. out_state.chan = chan;
  653. out_state.status = (UAVIONIX_ADSB_RF_HEALTH)packet.rfHealth;
  654. }
  655. /*
  656. @brief Generates pseudorandom ICAO from gps time, lat, and lon.
  657. Reference: DO282B, 2.2.4.5.1.3.2
  658. Note gps.time is the number of seconds since UTC midnight
  659. */
  660. uint32_t AP_ADSB::genICAO(const Location &loc)
  661. {
  662. // gps_time is not seconds since UTC midnight, but it is an equivalent random number
  663. // TODO: use UTC time instead of GPS time
  664. const AP_GPS &gps = AP::gps();
  665. const uint64_t gps_time = gps.time_epoch_usec();
  666. uint32_t timeSum = 0;
  667. const uint32_t M3 = 4096 * (loc.lat & 0x00000FFF) + (loc.lng & 0x00000FFF);
  668. for (uint8_t i=0; i<24; i++) {
  669. timeSum += (((gps_time & 0x00FFFFFF)>> i) & 0x00000001);
  670. }
  671. return( (timeSum ^ M3) & 0x00FFFFFF);
  672. }
  673. // assign a string to out_state.cfg.callsign but ensure it's null terminated
  674. void AP_ADSB::set_callsign(const char* str, const bool append_icao)
  675. {
  676. bool zero_char_pad = false;
  677. // clean slate
  678. memset(out_state.cfg.callsign, 0, sizeof(out_state.cfg.callsign));
  679. // copy str to cfg.callsign but we can't use strncpy because we need
  680. // to restrict values to only 'A' - 'Z' and '0' - '9' and pad
  681. for (uint8_t i=0; i<sizeof(out_state.cfg.callsign)-1; i++) {
  682. if (!str[i] || zero_char_pad) {
  683. // finish early. Either pad the rest with zero char or null terminate and call it a day
  684. if ((append_icao && i<4) || zero_char_pad) {
  685. out_state.cfg.callsign[i] = '0';
  686. zero_char_pad = true;
  687. } else {
  688. // already null terminated via memset so just stop
  689. break;
  690. }
  691. } else if (('A' <= str[i] && str[i] <= 'Z') ||
  692. ('0' <= str[i] && str[i] <= '9')) {
  693. // valid as-is
  694. // spaces are also allowed but are handled in the last else
  695. out_state.cfg.callsign[i] = str[i];
  696. } else if ('a' <= str[i] && str[i] <= 'z') {
  697. // toupper()
  698. out_state.cfg.callsign[i] = str[i] - ('a' - 'A');
  699. } else if (i == 0) {
  700. // invalid, pad to char zero because first index can't be space
  701. out_state.cfg.callsign[i] = '0';
  702. } else {
  703. // invalid, pad with space
  704. out_state.cfg.callsign[i] = ' ';
  705. }
  706. } // for i
  707. if (append_icao) {
  708. snprintf(&out_state.cfg.callsign[4], 5, "%04X", unsigned(out_state.cfg.ICAO_id % 0x10000));
  709. }
  710. }
  711. void AP_ADSB::push_sample(adsb_vehicle_t &vehicle)
  712. {
  713. samples.push_back(vehicle);
  714. }
  715. bool AP_ADSB::next_sample(adsb_vehicle_t &vehicle)
  716. {
  717. return samples.pop_front(vehicle);
  718. }
  719. void AP_ADSB::handle_message(const mavlink_channel_t chan, const mavlink_message_t &msg)
  720. {
  721. switch (msg.msgid) {
  722. case MAVLINK_MSG_ID_ADSB_VEHICLE:
  723. handle_vehicle(msg);
  724. break;
  725. case MAVLINK_MSG_ID_UAVIONIX_ADSB_TRANSCEIVER_HEALTH_REPORT:
  726. handle_transceiver_report(chan, msg);
  727. break;
  728. case MAVLINK_MSG_ID_UAVIONIX_ADSB_OUT_CFG:
  729. handle_out_cfg(msg);
  730. break;
  731. case MAVLINK_MSG_ID_UAVIONIX_ADSB_OUT_DYNAMIC:
  732. // unhandled, this is an outbound packet only
  733. default:
  734. break;
  735. }
  736. }
  737. // If that ICAO is found in the database then return true with a fully populated vehicle
  738. bool AP_ADSB::get_vehicle_by_ICAO(const uint32_t icao, adsb_vehicle_t &vehicle) const
  739. {
  740. adsb_vehicle_t temp_vehicle;
  741. temp_vehicle.info.ICAO_address = icao;
  742. uint16_t i;
  743. if (find_index(temp_vehicle, &i)) {
  744. // vehicle is tracked in list.
  745. // we must memcpy it because the database may reorganize itself and we don't
  746. // want the reference to magically start pointing at a different vehicle
  747. memcpy(&vehicle, &in_state.vehicle_list[i], sizeof(adsb_vehicle_t));
  748. return true;
  749. }
  750. return false;
  751. }
  752. /*
  753. * Write vehicle to log
  754. */
  755. void AP_ADSB::write_log(const adsb_vehicle_t &vehicle)
  756. {
  757. switch (_log) {
  758. case logging::SPECIAL_ONLY:
  759. if (!is_special_vehicle(vehicle.info.ICAO_address)) {
  760. return;
  761. }
  762. case logging::ALL:
  763. break;
  764. case logging::NONE:
  765. default:
  766. return;
  767. }
  768. struct log_ADSB pkt = {
  769. LOG_PACKET_HEADER_INIT(LOG_ADSB_MSG),
  770. time_us : AP_HAL::micros64(),
  771. ICAO_address : vehicle.info.ICAO_address,
  772. lat : vehicle.info.lat,
  773. lng : vehicle.info.lon,
  774. alt : vehicle.info.altitude,
  775. heading : vehicle.info.heading,
  776. hor_velocity : vehicle.info.hor_velocity,
  777. ver_velocity : vehicle.info.ver_velocity,
  778. squawk : vehicle.info.squawk,
  779. };
  780. AP::logger().WriteBlock(&pkt, sizeof(pkt));
  781. }