LCOV - code coverage report
Current view: top level - AP_Proximity - AP_Proximity_LightWareSF45B.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 77 82 93.9 %
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             :    The Lightware SF45B serial interface is described on this wiki page
      16             :    http://support.lightware.co.za/sf45/#/commands
      17             :  */
      18             : 
      19             : #include "AP_Proximity_config.h"
      20             : 
      21             : #if AP_PROXIMITY_LIGHTWARE_SF45B_ENABLED
      22             : 
      23             : #include "AP_Proximity_LightWareSF45B.h"
      24             : 
      25             : #include <AP_Common/AP_Common.h>
      26             : #include <AP_HAL/AP_HAL.h>
      27             : #include <AP_HAL/utility/sparse-endian.h>
      28             : 
      29             : extern const AP_HAL::HAL& hal;
      30             : 
      31             : static const uint32_t PROXIMITY_SF45B_TIMEOUT_MS = 200;
      32             : static const uint32_t PROXIMITY_SF45B_REINIT_INTERVAL_MS = 5000;    // re-initialise sensor after this many milliseconds
      33             : static const float PROXIMITY_SF45B_COMBINE_READINGS_DEG = 5.0f;     // combine readings from within this many degrees to improve efficiency
      34             : static const uint32_t PROXIMITY_SF45B_STREAM_DISTANCE_DATA_CM = 5;
      35             : static const uint8_t PROXIMITY_SF45B_DESIRED_UPDATE_RATE = 6;       // 1:48hz, 2:55hz, 3:64hz, 4:77hz, 5:97hz, 6:129hz, 7:194hz, 8:388hz
      36             : static const uint32_t PROXIMITY_SF45B_DESIRED_FIELDS = ((uint32_t)1 << 0 | (uint32_t)1 << 8);   // first return (unfiltered), yaw angle
      37             : static const uint16_t PROXIMITY_SF45B_DESIRED_FIELD_COUNT = 2;      // DISTANCE_DATA_CM message should contain two fields
      38             : 
      39             : // update the state of the sensor
      40        8248 : void AP_Proximity_LightWareSF45B::update(void)
      41             : {
      42        8248 :     if (_uart == nullptr) {
      43           0 :         return;
      44             :     }
      45             : 
      46             :     // initialise sensor if necessary
      47        8248 :     initialise();
      48             : 
      49             :     // process incoming messages
      50        8248 :     process_replies();
      51             : 
      52             :     // check for timeout and set health status
      53        8248 :     if ((_last_distance_received_ms == 0) || ((AP_HAL::millis() - _last_distance_received_ms) > PROXIMITY_SF45B_TIMEOUT_MS)) {
      54         514 :         set_status(AP_Proximity::Status::NoData);
      55             :     } else {
      56        7734 :         set_status(AP_Proximity::Status::Good);
      57             :     }
      58             : }
      59             : 
      60             : // initialise sensor
      61        8248 : void AP_Proximity_LightWareSF45B::initialise()
      62             : {
      63             :     // check sensor is configured correctly
      64       24230 :     _init_complete = (_sensor_state.stream_data_type == PROXIMITY_SF45B_STREAM_DISTANCE_DATA_CM) &&
      65       15982 :                      (_sensor_state.update_rate == PROXIMITY_SF45B_DESIRED_UPDATE_RATE) &&
      66        7734 :                      (_sensor_state.streaming_fields == PROXIMITY_SF45B_DESIRED_FIELDS);
      67             : 
      68             :     // exit if initialisation requests have been sent within the last few seconds
      69        8248 :     uint32_t now_ms = AP_HAL::millis();
      70        8248 :     if ((now_ms - _last_init_ms) < PROXIMITY_SF45B_REINIT_INTERVAL_MS) {
      71        8240 :         return;
      72             :     }
      73           8 :     _last_init_ms = now_ms;
      74             : 
      75             :     // request stream rate and contents
      76           8 :     request_stream_start();
      77             : }
      78             : 
      79             : // request start of streaming of distances
      80           8 : void AP_Proximity_LightWareSF45B::request_stream_start()
      81             : {
      82             :     // request output rate
      83           8 :     send_message((uint8_t)MessageID::UPDATE_RATE, true, &PROXIMITY_SF45B_DESIRED_UPDATE_RATE, sizeof(PROXIMITY_SF45B_DESIRED_UPDATE_RATE));
      84             : 
      85             :     // request first return (unfiltered), and yaw angle
      86           8 :     send_message((uint8_t)MessageID::DISTANCE_OUTPUT, true, (const uint8_t*)&PROXIMITY_SF45B_DESIRED_FIELDS, sizeof(PROXIMITY_SF45B_DESIRED_FIELDS));
      87             : 
      88             :     // request start streaming of DISTANCE_DATA_CM messages
      89           8 :     send_message((uint8_t)MessageID::STREAM, true, (const uint8_t*)&PROXIMITY_SF45B_STREAM_DISTANCE_DATA_CM, sizeof(PROXIMITY_SF45B_STREAM_DISTANCE_DATA_CM));
      90           8 : }
      91             : 
      92             : // check for replies from sensor
      93        8248 : void AP_Proximity_LightWareSF45B::process_replies()
      94             : {
      95        8248 :     if (_uart == nullptr) {
      96           0 :         return;
      97             :     }
      98             : 
      99             :     // process up to 1K of characters per iteration
     100        8248 :     uint32_t nbytes = MIN(_uart->available(), 1024U);
     101       68124 :     while (nbytes-- > 0) {
     102             :         uint8_t c;
     103       59876 :         if (!_uart->read(c)) {
     104           0 :             continue;
     105             :         }
     106       59876 :         if (parse_byte(c)) {
     107        5990 :             process_message();
     108             :         }
     109             :     }
     110             : }
     111             : 
     112             : // process the latest message held in the _msg structure
     113        5990 : void AP_Proximity_LightWareSF45B::process_message()
     114             : {
     115             :     // process payload
     116        5990 :     switch ((MessageID)_msg.msgid) {
     117             : 
     118           8 :     case MessageID::DISTANCE_OUTPUT:
     119           8 :         if (_payload_recv == sizeof(uint32_t)) {
     120           8 :             _sensor_state.streaming_fields = UINT32_VALUE(_msg.payload[3], _msg.payload[2], _msg.payload[1], _msg.payload[0]);
     121             :         }
     122           8 :         break;
     123             : 
     124           8 :     case MessageID::STREAM:
     125           8 :         if (_payload_recv == sizeof(uint32_t)) {
     126           8 :             _sensor_state.stream_data_type = UINT32_VALUE(_msg.payload[3], _msg.payload[2], _msg.payload[1], _msg.payload[0]);
     127             :         }
     128           8 :         break;
     129             : 
     130           8 :     case MessageID::UPDATE_RATE:
     131           8 :         if (_payload_recv == 1) {
     132           8 :             _sensor_state.update_rate = _msg.payload[0];
     133             :         }
     134           8 :         break;
     135             : 
     136        5966 :     case MessageID::DISTANCE_DATA_CM: {
     137             :         // ignore distance messages until initialisation is complete
     138        5966 :         if (!_init_complete || (_payload_recv != (PROXIMITY_SF45B_DESIRED_FIELD_COUNT * 2))) {
     139             :             break;
     140             :         }
     141        5524 :         _last_distance_received_ms = AP_HAL::millis();
     142        5524 :         const float distance_m = _distance_filt.apply((int16_t)UINT16_VALUE(_msg.payload[1], _msg.payload[0])) * 0.01f;
     143        5524 :         const float angle_deg = correct_angle_for_orientation((int16_t)UINT16_VALUE(_msg.payload[3], _msg.payload[2]) * 0.01f);
     144             : 
     145             :         // if distance is from a new face then update distance, angle and boundary for previous face
     146             :         // get face from 3D boundary based on yaw angle to the object
     147        5524 :         const AP_Proximity_Boundary_3D::Face face = frontend.boundary.get_face(angle_deg);
     148        5524 :         if (face != _face) {
     149         382 :             if (_face_distance_valid) {
     150         232 :                 frontend.boundary.set_face_attributes(_face, _face_yaw_deg, _face_distance, state.instance);
     151             :             } else {
     152             :                 // mark previous face invalid
     153         150 :                 frontend.boundary.reset_face(_face, state.instance);
     154             :             }
     155             :             // record updated face
     156         382 :             _face = face;
     157         382 :             _face_yaw_deg = 0;
     158         382 :             _face_distance = INT16_MAX;
     159         382 :             _face_distance_valid = false;
     160             :         }
     161             : 
     162             :         // if distance is from a new minisector then update obstacle database using angle and distance from previous minisector
     163        5524 :         const uint8_t minisector = convert_angle_to_minisector(angle_deg);
     164        5524 :         if (minisector != _minisector) {
     165        3238 :             if ((_minisector != UINT8_MAX) && _minisector_distance_valid) {
     166        1849 :                 database_push(_minisector_angle, _minisector_distance);
     167             :             }
     168             :             // init mini sector
     169        3238 :             _minisector = minisector;
     170        3238 :             _minisector_angle = 0;
     171        3238 :             _minisector_distance = INT16_MAX;
     172        3238 :             _minisector_distance_valid = false;
     173             :         }
     174             : 
     175             :         // check reading is valid
     176        5524 :         if (!ignore_reading(angle_deg, distance_m) && (distance_m >= distance_min_m()) && (distance_m <= distance_max_m())) {
     177             :             // update shortest distance for this face
     178        3085 :             if (!_face_distance_valid || (distance_m < _face_distance)) {
     179         643 :                 _face_yaw_deg = angle_deg;
     180         643 :                 _face_distance = distance_m;
     181         643 :                 _face_distance_valid = true;
     182             :             }
     183             : 
     184             :             // update shortest distance for this mini sector
     185        3085 :             if (distance_m < _minisector_distance) {
     186        2097 :                 _minisector_angle = angle_deg;
     187        2097 :                 _minisector_distance = distance_m;
     188        2097 :                 _minisector_distance_valid = true;
     189             :             }
     190             :         }
     191        5524 :         break;
     192             :     }
     193             : 
     194           0 :     default:
     195             :         // ignore unsupported messages
     196           0 :         break;
     197             :     }
     198        5990 : }
     199             : 
     200             : // convert an angle (in degrees) to a mini sector number
     201        5524 : uint8_t AP_Proximity_LightWareSF45B::convert_angle_to_minisector(float angle_deg) const
     202             : {
     203        5524 :     return wrap_360(angle_deg + (PROXIMITY_SF45B_COMBINE_READINGS_DEG * 0.5f)) / PROXIMITY_SF45B_COMBINE_READINGS_DEG;
     204             : }
     205             : 
     206             : #endif // AP_PROXIMITY_LIGHTWARE_SF45B_ENABLED

Generated by: LCOV version 1.14