LCOV - code coverage report
Current view: top level - SITL - SIM_PS_RPLidar.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 154 211 73.0 %
Date: 2026-09-09 11:02:08 Functions: 7 8 87.5 %

          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             :   Simulator for the RPLidar proximity sensors
      17             : */
      18             : 
      19             : #include "SIM_PS_RPLidar.h"
      20             : 
      21             : #if AP_SIM_PS_RPLIDARA2_ENABLED || AP_SIM_PS_RPLIDARA1_ENABLED || AP_SIM_PS_RPLIDARS2_ENABLED
      22             : 
      23             : #include <GCS_MAVLink/GCS.h>
      24             : #include <stdio.h>
      25             : #include <errno.h>
      26             : #include <math.h>
      27             : 
      28             : using namespace SITL;
      29             : 
      30           0 : uint32_t PS_RPLidar::packet_for_location(const Location &location,
      31             :                                            uint8_t *data,
      32             :                                            uint8_t buflen)
      33             : {
      34           0 :     return 0;
      35             : }
      36             : 
      37      158677 : void PS_RPLidar::move_preamble_in_buffer()
      38             : {
      39             :     uint8_t i;
      40      158677 :     for (i=0; i<_buflen; i++) {
      41           6 :         if ((uint8_t)_buffer[i] == PREAMBLE) {
      42           6 :             break;
      43             :         }
      44             :     }
      45      158677 :     if (i == 0) {
      46      158677 :         return;
      47             :     }
      48           0 :     memmove(_buffer, &_buffer[i], _buflen-i);
      49           0 :     _buflen = _buflen - i;
      50             : }
      51             : 
      52      158677 : void PS_RPLidar::update_input()
      53             : {
      54      158677 :     const ssize_t n = read_from_autopilot(&_buffer[_buflen], ARRAY_SIZE(_buffer) - _buflen - 1);
      55      158677 :     if (n < 0) {
      56             :         // TODO: do better here
      57           0 :         if (errno != EAGAIN && errno != EWOULDBLOCK && errno != 0) {
      58           0 :             AP_HAL::panic("Failed to read from autopilot");
      59             :         }
      60             :     } else {
      61      158677 :         _buflen += n;
      62             :     }
      63             : 
      64      158677 :     switch (_inputstate) {
      65      158677 :     case InputState::WAITING_FOR_PREAMBLE:
      66      158677 :         move_preamble_in_buffer();
      67      158677 :         if (_buflen == 0) {
      68      158671 :             return;
      69             :         }
      70           6 :         set_inputstate(InputState::GOT_PREAMBLE);
      71             :         // consume the preamble:
      72           6 :         memmove(_buffer, &_buffer[1], _buflen-1);
      73           6 :         _buflen--;
      74             :         FALLTHROUGH;
      75           6 :     case InputState::GOT_PREAMBLE:
      76           6 :         if (_buflen == 0) {
      77           0 :             return;
      78             :         }
      79           6 :         switch ((Command)_buffer[0]) {
      80           0 :         case Command::STOP:
      81             :             // consume the command:
      82           0 :             memmove(_buffer, &_buffer[1], _buflen-1);
      83           0 :             _buflen--;
      84           0 :             set_inputstate(InputState::WAITING_FOR_PREAMBLE);
      85           0 :             set_state(State::IDLE);
      86           0 :             _scan_mode = ScanMode::SCAN;
      87           0 :             return;
      88           2 :         case Command::SCAN:
      89             :             // 5-byte scan mode
      90           2 :             memmove(_buffer, &_buffer[1], _buflen-1);
      91           2 :             _buflen--;
      92           2 :             send_response_descriptor(0x05, SendMode::SRMR, DataType::Unknown81);
      93           2 :             set_inputstate(InputState::WAITING_FOR_PREAMBLE);
      94           2 :             set_state(State::SCANNING);
      95           2 :             _scan_mode = ScanMode::SCAN;
      96           2 :             last_scan_output_time_ms = 0;
      97           2 :             last_degrees_bf = 0.0f;
      98           2 :             return;
      99           1 :         case Command::EXPRESS_SCAN:
     100             :             // Dense Express mode (40 samples / packet)
     101             :             //
     102             :             // Request is: A5 82 <payload_size=0x05> <5-byte payload> <checksum>
     103             :             // Descriptor: A5 5A 54 00 00 40 85  (len=0x54=84 bytes, type=0x85)
     104           1 :             memmove(_buffer, &_buffer[1], _buflen-1);
     105           1 :             _buflen--;
     106           1 :             if (_buflen >= 1) {
     107           1 :                 const uint8_t payload_size = (uint8_t)_buffer[0];
     108           1 :                 const uint8_t total_to_discard = 1 + payload_size + 1; // size + payload + checksum
     109           1 :                 if (_buflen >= total_to_discard) {
     110           1 :                     memmove(_buffer, &_buffer[total_to_discard], _buflen - total_to_discard);
     111           1 :                     _buflen -= total_to_discard;
     112             :                 } else {
     113           0 :                     _buflen = 0;
     114             :                 }
     115             :             } else {
     116           0 :                 _buflen = 0;
     117             :             }
     118           1 :             send_response_descriptor(0x54, SendMode::SRMR, DataType::Unknown85);
     119           1 :             set_inputstate(InputState::WAITING_FOR_PREAMBLE);
     120           1 :             set_state(State::SCANNING);
     121           1 :             _scan_mode = ScanMode::EXPRESS_SCAN_DENSE;
     122           1 :             _express_w_i_deg = 0.0f;
     123           1 :             last_scan_output_time_ms = 0;
     124           1 :             return;
     125           0 :         case Command::GET_HEALTH: {
     126             :             // consume the command:
     127           0 :             memmove(_buffer, &_buffer[1], _buflen-1);
     128           0 :             _buflen--;
     129           0 :             send_response_descriptor(0x03, SendMode::SRSR, DataType::Unknown06);
     130             :             // now send the health:
     131           0 :             const uint8_t health[3] {}; // all zeros fine for now
     132           0 :             const ssize_t ret = write_to_autopilot((const char*)health, ARRAY_SIZE(health));
     133           0 :             if (ret != ARRAY_SIZE(health)) {
     134           0 :                 abort();
     135             :             }
     136           0 :             set_inputstate(InputState::WAITING_FOR_PREAMBLE);
     137           0 :             return;
     138             :         }
     139           3 :         case Command::GET_DEVICE_INFO: {
     140             :             // consume the command:
     141           3 :             memmove(_buffer, &_buffer[1], _buflen-1);
     142           3 :             _buflen--;
     143           3 :             send_response_descriptor(0x14, SendMode::SRSR, DataType::Unknown04);
     144             :             // now send the device info:
     145             :             struct PACKED _device_info {
     146             :                 uint8_t model;
     147             :                 uint8_t firmware_minor;
     148             :                 uint8_t firmware_major;
     149             :                 uint8_t hardware;
     150             :                 uint8_t serial[16];
     151             :             } device_info;
     152           3 :             device_info.model = device_info_model();
     153           3 :             device_info.firmware_minor = 17;
     154           3 :             device_info.firmware_major = 42;
     155           3 :             device_info.hardware = 6;
     156           3 :             const ssize_t ret = write_to_autopilot((const char*)&device_info, sizeof(device_info));
     157           3 :             if (ret != sizeof(device_info)) {
     158           0 :                 abort();
     159             :             }
     160           3 :             set_inputstate(InputState::WAITING_FOR_PREAMBLE);
     161           3 :             return;
     162             :         }
     163           0 :         case Command::FORCE_SCAN:
     164           0 :             abort();
     165           0 :         case Command::RESET:
     166             :             // consume the command:
     167           0 :             memmove(_buffer, &_buffer[1], _buflen-1);
     168           0 :             _buflen--;
     169           0 :             set_inputstate(InputState::RESETTING_START);
     170           0 :             return;
     171           0 :         default:
     172           0 :             const uint8_t bad = static_cast<uint8_t>(_buffer[0]);
     173           0 :             ::fprintf(stderr, "SIM_RPLidar: unknown command 0x%02x, ignoring\n", bad);
     174             :             // consume this byte and resync:
     175           0 :             memmove(_buffer, &_buffer[1], _buflen-1);
     176           0 :             _buflen--;
     177           0 :             set_inputstate(InputState::WAITING_FOR_PREAMBLE);
     178           0 :             return;
     179             :         }
     180           0 :     case InputState::RESETTING_START:
     181           0 :         _firmware_info_offset = 0;
     182           0 :         set_inputstate(InputState::RESETTING_SEND_FIRMWARE_INFO);
     183             :         FALLTHROUGH;
     184           0 :     case InputState::RESETTING_SEND_FIRMWARE_INFO: {
     185           0 :         const ssize_t written = write_to_autopilot(&FIRMWARE_INFO[_firmware_info_offset], strlen(FIRMWARE_INFO) - _firmware_info_offset);
     186           0 :         if (written <= 0) {
     187           0 :             AP_HAL::panic("Failed to write to autopilot");
     188             :         }
     189           0 :         _firmware_info_offset += written;
     190           0 :         if (_firmware_info_offset < strlen(FIRMWARE_INFO)) {
     191           0 :             return;
     192             :         }
     193           0 :         set_inputstate(InputState::WAITING_FOR_PREAMBLE);
     194           0 :         return;
     195             :     }
     196             :     }
     197             : }
     198             : 
     199       99027 : void PS_RPLidar::update_output_scan(const Location &location)
     200             : {
     201       99027 :     const uint32_t now = AP_HAL::millis();
     202       99027 :     if (last_scan_output_time_ms == 0) {
     203           2 :         last_scan_output_time_ms = now;
     204           2 :         return;
     205             :     }
     206       99025 :     const uint32_t time_delta = (now - last_scan_output_time_ms);
     207             : 
     208       99025 :     const uint32_t samples_per_second = 1000;
     209       99025 :     const float samples_per_ms = samples_per_second / 1000.0f;
     210       99025 :     const uint32_t sample_count = time_delta / samples_per_ms;
     211       99025 :     const float degrees_per_ms = 3600 / 1000.0f;
     212       99025 :     const float degrees_per_sample = degrees_per_ms / samples_per_ms;
     213             : 
     214             : //    ::fprintf(stderr, "Packing %u samples in for %ums interval (%f degrees/sample)\n", sample_count, time_delta, degrees_per_sample);
     215             : 
     216       99025 :     last_scan_output_time_ms += sample_count/samples_per_ms;
     217             : 
     218      181513 :     for (uint32_t i=0; i<sample_count; i++) {
     219       82488 :         const float current_degrees_bf = fmodf((last_degrees_bf + degrees_per_sample), 360.0f);
     220       82488 :         const uint8_t quality = 17; // random number
     221       82488 :         const uint16_t angle_q6 = current_degrees_bf * 64;
     222       82488 :         const bool is_start_packet = current_degrees_bf < last_degrees_bf;
     223       82488 :         last_degrees_bf = current_degrees_bf;
     224             : 
     225             : 
     226       82488 :         float distance = measure_distance_at_angle_bf(location, current_degrees_bf);
     227             :         // ::fprintf(stderr, "SIM: %f=%fm\n", current_degrees_bf, distance);
     228       82488 :         if (distance > max_range()) {
     229             :             // sensor returns zero for out-of-range
     230       34416 :             distance = 0.0f;
     231             :         }
     232       82488 :         const uint16_t distance_q2 = (distance*1000 * 4); // m->mm and *4
     233             : 
     234             :         struct PACKED  {
     235             :             uint8_t startbit      : 1;            ///< on the first revolution 1 else 0
     236             :             uint8_t not_startbit  : 1;            ///< complementary to startbit
     237             :             uint8_t quality       : 6;            ///< Related the reflected laser pulse strength
     238             :             uint8_t checkbit      : 1;            ///< always set to 1
     239             :             uint16_t angle_q6     : 15;           ///< Actual heading = angle_q6/64.0 Degree
     240             :             uint16_t distance_q2  : 16;           ///< Actual Distance = distance_q2/4.0 mm
     241             :         } send_buffer;
     242       82488 :         send_buffer.startbit = is_start_packet;
     243       82488 :         send_buffer.not_startbit = !is_start_packet;
     244       82488 :         send_buffer.quality = quality;
     245       82488 :         send_buffer.checkbit = 1;
     246       82488 :         send_buffer.angle_q6 = angle_q6;
     247       82488 :         send_buffer.distance_q2 = distance_q2;
     248             : 
     249             :         static_assert(sizeof(send_buffer) == 5, "send_buffer correct size");
     250             : 
     251       82488 :         const ssize_t ret = write_to_autopilot((const char*)&send_buffer, sizeof(send_buffer));
     252       82488 :         if (ret != sizeof(send_buffer)) {
     253           0 :             abort();
     254             :         }
     255             :     }
     256             : }
     257             : 
     258       48808 : void PS_RPLidar::update_output_express_dense(const Location &location)
     259             : {
     260       48808 :     const uint32_t now = AP_HAL::millis();
     261       48808 :     if (last_scan_output_time_ms == 0) {
     262           1 :         last_scan_output_time_ms = now;
     263           1 :         return;
     264             :     }
     265             : 
     266             :     // Number of dense packets per second (tunable).
     267             :     // 80 packets/s -> 3200 samples/s (40 samples/packet).
     268       48807 :     const uint32_t packets_per_second = 80;
     269       48807 :     const uint32_t packet_period_ms = 1000 / packets_per_second;
     270             : 
     271       48807 :     const uint32_t time_delta = (now - last_scan_output_time_ms);
     272       48807 :     const uint32_t packet_count = time_delta / packet_period_ms;
     273             : 
     274       48807 :     if (packet_count == 0) {
     275       45419 :         return;
     276             :     }
     277             : 
     278        3388 :     last_scan_output_time_ms += packet_count * packet_period_ms;
     279             : 
     280             :     // 0.1125 degree per sample -> 4.5 degrees per packet
     281        3388 :     const float sample_delta_deg = 0.1125f;
     282        3388 :     const float packet_delta_deg = 40.0f * sample_delta_deg;
     283             : 
     284        6776 :     for (uint32_t p = 0; p < packet_count; p++) {
     285        3388 :         uint8_t packet[84] {};
     286             :         // Start angle in degrees for this packet
     287        3388 :         const float w_i_deg = _express_w_i_deg;
     288             : 
     289             :         // start_angle_q6 is in 1/64 degrees
     290        3388 :         const uint16_t start_angle_q6 = (uint16_t)lrintf(w_i_deg * 64.0f);
     291             : 
     292        3388 :         packet[2] = start_angle_q6 & 0xFF;
     293        3388 :         packet[3] = (start_angle_q6 >> 8) & 0xFF;
     294             : 
     295             :         // cabins: 40 distances (2 bytes each, little-endian)
     296        3388 :         uint8_t* cab = &packet[4];
     297      138908 :         for (uint32_t k = 0; k < 40; k++) {
     298             :             // angle of sample k
     299      135520 :             float angle_deg = w_i_deg + sample_delta_deg * k;
     300             :             // wrap to [0, 360)
     301      135520 :             if (angle_deg >= 360.0f) {
     302           0 :                 angle_deg = fmodf(angle_deg, 360.0f);
     303             :             }
     304             : 
     305      135520 :             float distance = measure_distance_at_angle_bf(location, angle_deg);
     306      135520 :             if (distance > max_range()) {
     307       22798 :                 distance = 0.0f;
     308             :             }
     309             : 
     310      135520 :             const uint16_t dist_mm = (uint16_t)lrintf(distance * 1000.0f);
     311      135520 :             cab[0] = dist_mm & 0xFF;
     312      135520 :             cab[1] = (dist_mm >> 8) & 0xFF;
     313      135520 :             cab += 2;
     314             :         }
     315             : 
     316             :         // checksum: XOR of bytes [2..83]
     317        3388 :         uint8_t checksum = 0;
     318      281204 :         for (uint32_t i = 2; i < sizeof(packet); i++) {
     319      277816 :             checksum ^= packet[i];
     320             :         }
     321             : 
     322             :         // sync1/sync2 and checksum nibbles (dense format)
     323        3388 :         const uint8_t sync1 = 0x0A;
     324        3388 :         const uint8_t sync2 = 0x05;
     325             : 
     326        3388 :         packet[0] = uint8_t((sync1 << 4) | (checksum & 0x0F));
     327        3388 :         packet[1] = uint8_t((sync2 << 4) | ((checksum >> 4) & 0x0F));
     328             : 
     329        3388 :         const ssize_t ret = write_to_autopilot((const char*)packet, sizeof(packet));
     330        3388 :         if (ret != (ssize_t)sizeof(packet)) {
     331           0 :             abort();
     332             :         }
     333             : 
     334             :         // advance start angle for next packet
     335        3388 :         _express_w_i_deg += packet_delta_deg;
     336        3388 :         if (_express_w_i_deg >= 360.0f) {
     337          42 :             _express_w_i_deg = fmodf(_express_w_i_deg, 360.0f);
     338             :         }
     339             :     }
     340             : }
     341             : 
     342      158677 : void PS_RPLidar::update_output(const Location &location)
     343             : {
     344      158677 :     switch (_state) {
     345       10842 :     case State::IDLE:
     346       10842 :         return;
     347      147835 :     case State::SCANNING:
     348      147835 :         if (_scan_mode == ScanMode::SCAN) {
     349       99027 :             update_output_scan(location);
     350             :         } else {
     351       48808 :             update_output_express_dense(location);
     352             :         }
     353      147835 :         return;
     354             :     }
     355             : }
     356             : 
     357      158677 : void PS_RPLidar::update(const Location &location)
     358             : {
     359      158677 :     update_input();
     360      158677 :     update_output(location);
     361      158677 : }
     362             : 
     363             : 
     364           6 : void PS_RPLidar::send_response_descriptor(uint32_t data_response_length, SendMode sendmode, DataType datatype)
     365             : {
     366           6 :     const uint8_t send_buffer[] = {
     367             :         0xA5,
     368             :         0x5A,
     369             :         uint8_t((data_response_length >> 0) & 0xff),
     370           6 :         uint8_t((data_response_length >>  8) & 0xff),
     371           6 :         uint8_t((data_response_length >> 16) & 0xff),
     372           6 :         uint8_t(((data_response_length >> 24) & 0xff) | (uint8_t) sendmode),
     373             :         (uint8_t)datatype
     374           6 :     };
     375             :     static_assert(ARRAY_SIZE(send_buffer) == 7, "send_buffer correct size");
     376             : 
     377           6 :     const ssize_t ret = write_to_autopilot((const char*)send_buffer, ARRAY_SIZE(send_buffer));
     378           6 :     if (ret != ARRAY_SIZE(send_buffer)) {
     379           0 :         abort();
     380             :     }
     381           6 : }
     382             : 
     383             : #endif  // AP_SIM_PS_RPLIDARA2_ENABLED || AP_SIM_PS_RPLIDARA1_ENABLED || AP_SIM_PS_RPLIDARS2_ENABLED

Generated by: LCOV version 1.14