LCOV - code coverage report
Current view: top level - AP_Proximity - AP_Proximity_MAV.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 113 127 89.0 %
Date: 2026-09-09 11:02:08 Functions: 6 6 100.0 %

          Line data    Source code
       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_Proximity_config.h"
      17             : 
      18             : #if AP_PROXIMITY_MAV_ENABLED
      19             : 
      20             : #include "AP_Proximity_MAV.h"
      21             : #include <AP_HAL/AP_HAL.h>
      22             : #include <ctype.h>
      23             : #include <stdio.h>
      24             : 
      25             : extern const AP_HAL::HAL& hal;
      26             : 
      27             : #define PROXIMITY_MAV_TIMEOUT_MS    500 // distance messages must arrive within this many milliseconds
      28             : #define PROXIMITY_TIMESTAMP_MSG_TIMEOUT_MS  50  // obstacles will be transferred from temp boundary to actual boundary if mavlink message does not arrive within this many milliseconds
      29             : 
      30             : // update the state of the sensor
      31       45093 : void AP_Proximity_MAV::update(void)
      32             : {
      33             :     // check for timeout and set health status
      34       71909 :     if ((_last_update_ms == 0 || (AP_HAL::millis() - _last_update_ms > PROXIMITY_MAV_TIMEOUT_MS)) &&
      35       26816 :         (_last_upward_update_ms == 0 || (AP_HAL::millis() - _last_upward_update_ms > PROXIMITY_MAV_TIMEOUT_MS))) {
      36       26816 :         set_status(AP_Proximity::Status::NoData);
      37             :     } else {
      38       18277 :         set_status(AP_Proximity::Status::Good);
      39             :     }
      40       45093 : }
      41             : 
      42             : // get distance upwards in meters. returns true on success
      43       14105 : bool AP_Proximity_MAV::get_upward_distance(float &distance) const
      44             : {
      45       14105 :     if ((_last_upward_update_ms != 0) && (AP_HAL::millis() - _last_upward_update_ms <= PROXIMITY_MAV_TIMEOUT_MS)) {
      46           0 :         distance = _distance_upward;
      47           0 :         return true;
      48             :     }
      49       14105 :     return false;
      50             : }
      51             : 
      52             : // handle mavlink messages
      53         460 : void AP_Proximity_MAV::handle_msg(const mavlink_message_t &msg)
      54             : {   
      55         460 :     switch (msg.msgid) {
      56          81 :         case (MAVLINK_MSG_ID_DISTANCE_SENSOR):
      57          81 :             handle_distance_sensor_msg(msg);
      58          81 :             break;
      59             : 
      60           3 :         case (MAVLINK_MSG_ID_OBSTACLE_DISTANCE):
      61           3 :             handle_obstacle_distance_msg(msg);
      62           3 :             break;
      63             : 
      64         376 :         case (MAVLINK_MSG_ID_OBSTACLE_DISTANCE_3D):
      65         376 :             handle_obstacle_distance_3d_msg(msg);
      66         376 :             break;    
      67             :     }
      68         460 : }
      69             : 
      70             : // handle mavlink DISTANCE_SENSOR messages
      71          81 : void AP_Proximity_MAV::handle_distance_sensor_msg(const mavlink_message_t &msg)
      72             : {
      73             :     mavlink_distance_sensor_t packet;
      74          81 :     mavlink_msg_distance_sensor_decode(&msg, &packet);
      75             : 
      76             :     // store distance to appropriate sector based on orientation field
      77          81 :     if (packet.orientation <= MAV_SENSOR_ROTATION_YAW_315) {
      78          81 :         const uint32_t previous_sys_time = _last_update_ms;
      79          81 :         _last_update_ms = AP_HAL::millis();
      80             : 
      81             :         // time_diff will check if the new message arrived significantly later than the last message
      82          81 :         const uint32_t time_diff = _last_update_ms - previous_sys_time;
      83             : 
      84          81 :         const uint32_t previous_msg_timestamp = _last_msg_update_timestamp_ms;
      85          81 :         _last_msg_update_timestamp_ms = packet.time_boot_ms;
      86             : 
      87             :         // we will add on to the last fence if the time stamp is the same
      88             :         // provided we got the new obstacle in less than PROXIMITY_TIMESTAMP_MSG_TIMEOUT_MS
      89          81 :         if ((previous_msg_timestamp != _last_msg_update_timestamp_ms) || (time_diff > PROXIMITY_TIMESTAMP_MSG_TIMEOUT_MS)) {
      90             :             // push data from temp boundary to the main 3-D proximity boundary
      91          67 :             temp_boundary.update_3D_boundary(state.instance, frontend.boundary);
      92             :             // clear temp boundary for new data
      93          67 :             temp_boundary.reset();
      94             :         }
      95             :         // store in meters
      96          81 :         const float distance = packet.current_distance * 0.01f;
      97          81 :         const uint8_t sector = packet.orientation;
      98             :         // get the face for this sector
      99          81 :         const float yaw_angle_deg = sector * 45;
     100          81 :         const AP_Proximity_Boundary_3D::Face face = frontend.boundary.get_face(yaw_angle_deg);
     101          81 :         _distance_min = packet.min_distance * 0.01f;
     102          81 :         _distance_max = packet.max_distance * 0.01f;
     103          81 :         const bool in_range = distance <= _distance_max && distance >= _distance_min;
     104          81 :         if (in_range && !ignore_reading(yaw_angle_deg, distance, false)) {
     105          81 :             temp_boundary.add_distance(face, yaw_angle_deg, distance);
     106             :             // update OA database
     107          81 :             database_push(yaw_angle_deg, distance);
     108             :         }
     109             :     }
     110             : 
     111             :     // store upward distance
     112          81 :     if (packet.orientation == MAV_SENSOR_ROTATION_PITCH_90) {
     113           0 :         _distance_upward = packet.current_distance * 0.01f;
     114           0 :         _last_upward_update_ms = AP_HAL::millis();
     115             :     }
     116          81 :     return;
     117             : }
     118             : 
     119             : // handle mavlink OBSTACLE_DISTANCE messages
     120           3 : void AP_Proximity_MAV::handle_obstacle_distance_msg(const mavlink_message_t &msg)
     121             : {
     122             :     mavlink_obstacle_distance_t packet;
     123           3 :     mavlink_msg_obstacle_distance_decode(&msg, &packet);
     124             : 
     125             :     // check increment (message's sector width)
     126             :     float increment;
     127           3 :     if (!is_zero(packet.increment_f)) {
     128             :         // use increment float
     129           3 :         increment = packet.increment_f;
     130           0 :     } else if (packet.increment != 0) {
     131             :         // use increment uint8_t
     132           0 :         increment = packet.increment;
     133             :     } else {
     134             :         // invalid increment
     135           0 :         return;
     136             :     }
     137             : 
     138           3 :     const uint8_t total_distances = MIN(((360.0f / fabsf(increment)) + 0.5f), MAVLINK_MSG_OBSTACLE_DISTANCE_FIELD_DISTANCES_LEN); // usually 72
     139             : 
     140             :     // set distance min and max
     141           3 :     _distance_min = packet.min_distance * 0.01f;
     142           3 :     _distance_max = packet.max_distance * 0.01f;
     143           3 :     _last_update_ms = AP_HAL::millis();
     144             : 
     145             :     // get user configured yaw correction from front end
     146           3 :     const float param_yaw_offset = constrain_float(params.yaw_correction, -360.0f, +360.0f);
     147           3 :     const float yaw_correction = wrap_360(param_yaw_offset + packet.angle_offset);
     148           3 :     if (params.orientation != 0) {
     149           0 :         increment *= -1;
     150             :     }
     151             : 
     152           3 :     Vector3f current_pos;
     153           3 :     Matrix3f body_to_ned;
     154           3 :     const bool database_ready = database_prepare_for_push(current_pos, body_to_ned);
     155             : 
     156             :     // variables to calculate closest angle and distance for each face
     157           3 :     AP_Proximity_Boundary_3D::Face face;
     158           3 :     float face_distance = FLT_MAX;
     159           3 :     float face_yaw_deg = 0.0f;
     160           3 :     bool face_distance_valid = false;
     161             : 
     162             :     // reset this  boundary to fill with new data
     163           3 :     frontend.boundary.reset();
     164             : 
     165             :     // iterate over message's sectors
     166         147 :     for (uint8_t j = 0; j < total_distances; j++) {
     167         144 :         const uint16_t distance_cm = packet.distances[j];
     168         144 :         const float packet_distance_m = distance_cm * 0.01f;
     169         144 :         const float mid_angle = wrap_360((float)j * increment + yaw_correction);
     170             : 
     171         288 :         const bool range_check = distance_cm == 0 || distance_cm == 65535 || distance_cm < packet.min_distance ||
     172         144 :                                  distance_cm > packet.max_distance;
     173         144 :         if (range_check || ignore_reading(mid_angle, packet_distance_m, false)) {
     174             :             // sanity check failed, ignore this distance value
     175          66 :             continue;
     176             :         }
     177             : 
     178             :         // get face for this latest reading
     179          78 :         AP_Proximity_Boundary_3D::Face latest_face = frontend.boundary.get_face(mid_angle);
     180          78 :         if (latest_face != face) {
     181             :             // store previous face
     182           5 :             if (face_distance_valid) {
     183           2 :                 frontend.boundary.set_face_attributes(face, face_yaw_deg, face_distance, state.instance);
     184             :             } else {
     185           3 :                 frontend.boundary.reset_face(face, state.instance);
     186             :             }
     187             :             // init for latest face
     188           5 :             face = latest_face;
     189           5 :             face_distance_valid = false;
     190             :         }
     191             : 
     192             :         // update minimum distance found so far
     193          78 :         if (!face_distance_valid || (packet_distance_m < face_distance)) {
     194          40 :             face_yaw_deg = mid_angle;
     195          40 :             face_distance = packet_distance_m;
     196          40 :             face_distance_valid = true;
     197             :         }
     198             : 
     199             :         // update Object Avoidance database with Earth-frame point
     200          78 :         if (database_ready) {
     201           0 :             database_push(mid_angle, packet_distance_m, _last_update_ms, current_pos, body_to_ned);
     202             :         }
     203             :     }
     204             : 
     205             :     // process the last face
     206           3 :     if (face_distance_valid) {
     207           3 :         frontend.boundary.set_face_attributes(face, face_yaw_deg, face_distance, state.instance);
     208             :     } else {
     209           0 :         frontend.boundary.reset_face(face, state.instance);
     210             :     }
     211           3 :     return;
     212             : }
     213             : 
     214             : // handle mavlink OBSTACLE_DISTANCE_3D messages
     215         376 : void AP_Proximity_MAV::handle_obstacle_distance_3d_msg(const mavlink_message_t &msg)
     216             : {
     217             :     mavlink_obstacle_distance_3d_t packet;
     218         376 :     mavlink_msg_obstacle_distance_3d_decode(&msg, &packet);
     219             : 
     220         376 :     const uint32_t previous_sys_time = _last_update_ms;
     221         376 :     _last_update_ms = AP_HAL::millis();
     222             : 
     223             :     // time_diff will check if the new message arrived significantly later than the last message
     224         376 :     const uint32_t time_diff = _last_update_ms - previous_sys_time;
     225             : 
     226         376 :     const uint32_t previous_msg_timestamp = _last_msg_update_timestamp_ms;
     227         376 :     _last_msg_update_timestamp_ms = packet.time_boot_ms;
     228             : 
     229         376 :     if (packet.frame != MAV_FRAME_BODY_FRD) {
     230             :         // we do not support this frame of reference yet
     231           0 :         return;
     232             :     }
     233             : 
     234         376 :     if ((previous_msg_timestamp != _last_msg_update_timestamp_ms) || (time_diff > PROXIMITY_TIMESTAMP_MSG_TIMEOUT_MS)) {
     235             :         // push data from temp boundary to the main 3-D proximity boundary because a new timestamp has arrived
     236         376 :         temp_boundary.update_3D_boundary(state.instance, frontend.boundary);
     237             :         // clear temp boundary for new data
     238         376 :         temp_boundary.reset();
     239             :     }
     240             : 
     241         376 :     _distance_min = packet.min_distance;
     242         376 :     _distance_max = packet.max_distance;
     243             : 
     244         376 :     Vector3f current_pos;
     245         376 :     Matrix3f body_to_ned;
     246         376 :     const bool database_ready = database_prepare_for_push(current_pos, body_to_ned);
     247             : 
     248         376 :     const Vector3f obstacle_FRD(packet.x, packet.y, packet.z);
     249         376 :     const float obstacle_distance = obstacle_FRD.length();
     250         376 :     if (obstacle_distance < _distance_min || obstacle_distance > _distance_max || is_zero(obstacle_distance)) {
     251             :         // message isn't healthy
     252           0 :         return;
     253             :     }
     254             : 
     255             :     // convert to FRU
     256         376 :     const Vector3f obstacle(obstacle_FRD.x, obstacle_FRD.y, obstacle_FRD.z * -1.0f);
     257             : 
     258             :     // extract yaw and pitch from Obstacle Vector
     259         376 :     const float yaw = wrap_360(degrees(atan2f(obstacle.y, obstacle.x)));
     260         376 :     const float pitch = wrap_180(degrees(M_PI_2 - atan2f(obstacle.xy().length(), obstacle.z))); 
     261             : 
     262         376 :     if (ignore_reading(pitch, yaw, obstacle_distance, false)) {
     263             :         // obstacle is probably near ground or out of range
     264           0 :         return;
     265             :     }
     266             : 
     267             :     // allot to correct layer and sector based on calculated pitch and yaw
     268         376 :     const AP_Proximity_Boundary_3D::Face face = frontend.boundary.get_face(pitch, yaw);
     269         376 :     temp_boundary.add_distance(face, pitch, yaw, obstacle.length());
     270             : 
     271         376 :     if (database_ready) {
     272           0 :         database_push(yaw, pitch, obstacle.length(),_last_update_ms, current_pos, body_to_ned);
     273             :     }
     274         376 :     return;
     275             : }
     276             : 
     277             : #endif // AP_PROXIMITY_MAV_ENABLED

Generated by: LCOV version 1.14