/* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* Simulator for the RPLidarA2 proximity sensor */ #include "SIM_PS_LightWare_SF45B.h" #if HAL_SIM_PS_LIGHTWARE_SF45B_ENABLED #include #include #include using namespace SITL; uint32_t PS_LightWare_SF45B::packet_for_location(const Location &location, uint8_t *data, uint8_t buflen) { return 0; } void PS_LightWare_SF45B::move_preamble_in_buffer(uint8_t search_start_pos) { uint8_t i; for (i=search_start_pos; i<_buflen; i++) { if ((uint8_t)_msg.buffer[i] == PREAMBLE) { break; } } if (i == 0) { return; } memmove(_msg.buffer, &_msg.buffer[i], _buflen-i); _buflen = _buflen - i; } // handle a valid message in _msg.buffer void PS_LightWare_SF45B::send(const char *data, uint32_t len) { const ssize_t ret = write_to_autopilot(data, len); if (ret < 0 || (uint32_t)ret != len) { // abort(); } } void PS_LightWare_SF45B::handle_message() { // GCS_SEND_TEXT(MAV_SEVERITY_INFO, "Got message %u", (unsigned)_msg.packed_msgstream.msg.msgid); switch (MessageID(_msg.packed_msgstream.msg.msgid)) { case MessageID::STREAM: { stream = _msg.packed_msgstream.msg.stream; send_response.stream = true; return; } case MessageID::DISTANCE_OUTPUT: { desired_fields = _msg.packed_distance_output.msg.desired_fields; send_response.desired_fields = true; return; } case MessageID::UPDATE_RATE: { update_rate = _msg.packed_update_rate.msg.rate; send_response.update_rate = true; return; } case MessageID::DISTANCE_DATA_CM: { ::fprintf(stderr, "Got a distance data. Weird.\n"); return; } } // AP_HAL::panic("Unrecognised message (%u)", _msg.common.msgid); ::fprintf(stderr, "Unrecognised message (%u)\n", _msg.packed_msgstream.msg.msgid); } void PS_LightWare_SF45B::update_input() { const ssize_t n = read_from_autopilot(&_msg.buffer[_buflen], ARRAY_SIZE(_msg.buffer) - _buflen - 1); if (n < 0) { // TODO: do better here if (errno != EAGAIN && errno != EWOULDBLOCK && errno != 0) { AP_HAL::panic("Failed to read from autopilot"); } } else { _buflen += n; } switch (_inputstate) { case InputState::WANT_PREAMBLE: move_preamble_in_buffer(); if (_buflen == 0) { return; } set_inputstate(InputState::WANT_FLAGS); FALLTHROUGH; case InputState::WANT_FLAGS: if (_buflen < 4) { // 1 preamble, 2 flags, 1 msg return; } set_inputstate(InputState::WANT_PAYLOAD); FALLTHROUGH; case InputState::WANT_PAYLOAD: { // 1 preamble, 2 flags, 1 msg + payload const uint8_t want_len = 4 + payload_length(); if (_buflen < want_len) { return; } set_inputstate(InputState::WANT_CRC); FALLTHROUGH; } case InputState::WANT_CRC: { // 1 preamble, 2 flags, 1 msg + payload + 2 crc const uint8_t want_len = 4 + payload_length() + 2; if (_buflen < want_len) { return; } const uint16_t got_checksum = UINT16_VALUE(uint8_t(_msg.buffer[want_len-1]), uint8_t(_msg.buffer[want_len-2])); const uint16_t calc_checksum = msg_checksum(); if (calc_checksum == got_checksum) { // GCS_SEND_TEXT(MAV_SEVERITY_INFO, "sim-SF45B: Got one (%u)!", _msg.common.msgid); // see if this was a read or a write request.. handle_message(); } else { GCS_SEND_TEXT(MAV_SEVERITY_INFO, "sim-SF45B: Bad checksum"); } // consume these bytes move_preamble_in_buffer(want_len-1); set_inputstate(InputState::WANT_PREAMBLE); return; } } } void PS_LightWare_SF45B::update(const Location &location) { update_input(); update_output(location); } void PS_LightWare_SF45B::update_output(const Location &location) { switch (_state) { case State::SCANNING: update_output_responses(); update_output_scan(location); return; } } void PS_LightWare_SF45B::update_output_responses() { if (send_response.stream) { send_response.stream = false; PackedMessage packed { MsgStream(stream), 0x1 }; packed.update_checksum(); send((char*)&packed, sizeof(packed)); } if (send_response.update_rate) { send_response.update_rate = false; PackedMessage packed { UpdateRate(update_rate), 0x1 }; packed.update_checksum(); send((char*)&packed, sizeof(packed)); } if (send_response.desired_fields) { send_response.desired_fields = false; PackedMessage packed { DistanceOutput(desired_fields), 0x1 }; packed.update_checksum(); send((char*)&packed, sizeof(packed)); } } void PS_LightWare_SF45B::update_output_scan(const Location &location) { const uint32_t now = AP_HAL::millis(); if (last_scan_output_time_ms == 0) { last_scan_output_time_ms = now; return; } const uint32_t time_delta = (now - last_scan_output_time_ms); const uint32_t samples_per_second = 1000; const float samples_per_ms = samples_per_second / 1000.0f; const uint32_t sample_count = time_delta / samples_per_ms; const float degrees_per_ms = 1000 / 1000.0f; // Randy reports 1 degree increments const float degrees_per_sample = degrees_per_ms / samples_per_ms; // ::fprintf(stderr, "Packing %u samples in for %ums interval (%f degrees/sample)\n", sample_count, time_delta, degrees_per_sample); last_scan_output_time_ms += sample_count/samples_per_ms; for (uint32_t i=0; i MAX_RANGE) { // sensor returns zero for out-of-range distance = 0.0f; } PackedMessage packed_distance_data { DistanceDataCM( uint16_t(distance*100.0), wrap_180(current_degrees_bf) * 100 ), 0x1 }; packed_distance_data.update_checksum(); send((char*)&packed_distance_data, sizeof(packed_distance_data)); } } #endif // HAL_SIM_PS_LIGHTWARE_SF45B_ENABLED