← Back to ratslair.com
aboutsummaryrefslogtreecommitdiff
path: root/LIDAR/sdk/src/sl_lidar_driver.cpp
diff options
context:
space:
mode:
authorEdvin <[email protected]>2025-04-10 11:35:25 +0200
committerEdvin <[email protected]>2025-04-10 11:35:25 +0200
commitdcd2dd87ce8034a46e4150a89dc69f6ba1a22d8d (patch)
treebce00ff5029550f3bde1e83542d688dc3188e256 /LIDAR/sdk/src/sl_lidar_driver.cpp
parent1a837cbe411b4522582d323454a03ef02e9dd292 (diff)
Implemented code for Slamtec rplidar C1
Diffstat (limited to 'LIDAR/sdk/src/sl_lidar_driver.cpp')
-rw-r--r--LIDAR/sdk/src/sl_lidar_driver.cpp1702
1 files changed, 1702 insertions, 0 deletions
diff --git a/LIDAR/sdk/src/sl_lidar_driver.cpp b/LIDAR/sdk/src/sl_lidar_driver.cpp
new file mode 100644
index 0000000..4fd719c
--- /dev/null
+++ b/LIDAR/sdk/src/sl_lidar_driver.cpp
@@ -0,0 +1,1702 @@
+/*
+ * Slamtec LIDAR SDK
+ *
+ * Copyright (c) 2014 - 2020 Shanghai Slamtec Co., Ltd.
+ * http://www.slamtec.com
+ *
+ */
+ /*
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "sdkcommon.h"
+#include "hal/abs_rxtx.h"
+#include "hal/thread.h"
+#include "hal/types.h"
+#include "hal/assert.h"
+#include "hal/locker.h"
+#include "hal/socket.h"
+#include "hal/event.h"
+#include "hal/waiter.h"
+#include "hal/byteorder.h"
+#include "sl_lidar_driver.h"
+#include "sl_crc.h"
+#include <algorithm>
+#include <memory>
+#include <atomic>
+#include <deque>
+
+#include "dataunpacker/dataunpacker.h"
+#include "sl_async_transceiver.h"
+#include "sl_lidarprotocol_codec.h"
+
+
+
+#ifdef _WIN32
+#define NOMINMAX
+#undef min
+#undef max
+#endif
+
+#if defined(__cplusplus) && __cplusplus >= 201103L
+#ifndef _GXX_NULLPTR_T
+#define _GXX_NULLPTR_T
+typedef decltype(nullptr) nullptr_t;
+#endif
+#endif /* C++11. */
+
+namespace sl {
+ static void printDeprecationWarn(const char* fn, const char* replacement)
+ {
+ fprintf(stderr, "*WARN* YOU ARE USING DEPRECATED API: %s, PLEASE MOVE TO %s\n", fn, replacement);
+ }
+
+ static void convert(const sl_lidar_response_measurement_node_t& from, sl_lidar_response_measurement_node_hq_t& to)
+ {
+ to.angle_z_q14 = (((from.angle_q6_checkbit) >> SL_LIDAR_RESP_MEASUREMENT_ANGLE_SHIFT) << 8) / 90; //transfer to q14 Z-angle
+ to.dist_mm_q2 = from.distance_q2;
+ to.flag = (from.sync_quality & SL_LIDAR_RESP_MEASUREMENT_SYNCBIT); // trasfer syncbit to HQ flag field
+ to.quality = (from.sync_quality >> SL_LIDAR_RESP_MEASUREMENT_QUALITY_SHIFT) << SL_LIDAR_RESP_MEASUREMENT_QUALITY_SHIFT; //remove the last two bits and then make quality from 0-63 to 0-255
+ }
+
+ static void convert(const sl_lidar_response_measurement_node_hq_t& from, sl_lidar_response_measurement_node_t& to)
+ {
+ to.sync_quality = (from.flag & SL_LIDAR_RESP_MEASUREMENT_SYNCBIT) | ((from.quality >> SL_LIDAR_RESP_MEASUREMENT_QUALITY_SHIFT) << SL_LIDAR_RESP_MEASUREMENT_QUALITY_SHIFT);
+ to.angle_q6_checkbit = 1 | (((from.angle_z_q14 * 90) >> 8) << SL_LIDAR_RESP_MEASUREMENT_ANGLE_SHIFT);
+ to.distance_q2 = from.dist_mm_q2 > sl_u16(-1) ? sl_u16(0) : sl_u16(from.dist_mm_q2);
+ }
+
+
+ static inline float getAngle(const sl_lidar_response_measurement_node_t& node)
+ {
+ return (node.angle_q6_checkbit >> SL_LIDAR_RESP_MEASUREMENT_ANGLE_SHIFT) / 64.f;
+ }
+
+ static inline void setAngle(sl_lidar_response_measurement_node_t& node, float v)
+ {
+ sl_u16 checkbit = node.angle_q6_checkbit & SL_LIDAR_RESP_MEASUREMENT_CHECKBIT;
+ node.angle_q6_checkbit = (((sl_u16)(v * 64.0f)) << SL_LIDAR_RESP_MEASUREMENT_ANGLE_SHIFT) | checkbit;
+ }
+
+ static inline float getAngle(const sl_lidar_response_measurement_node_hq_t& node)
+ {
+ return node.angle_z_q14 * 90.f / 16384.f;
+ }
+
+ static inline void setAngle(sl_lidar_response_measurement_node_hq_t& node, float v)
+ {
+ node.angle_z_q14 = sl_u32(v * 16384.f / 90.f);
+ }
+
+ static inline sl_u16 getDistanceQ2(const sl_lidar_response_measurement_node_t& node)
+ {
+ return node.distance_q2;
+ }
+
+ static inline sl_u32 getDistanceQ2(const sl_lidar_response_measurement_node_hq_t& node)
+ {
+ return node.dist_mm_q2;
+ }
+
+ template <class TNode>
+ static bool angleLessThan(const TNode& a, const TNode& b)
+ {
+ return getAngle(a) < getAngle(b);
+ }
+
+ template < class TNode >
+ static sl_result ascendScanData_(TNode * nodebuffer, size_t count)
+ {
+ float inc_origin_angle = 360.f / count;
+ size_t i = 0;
+
+ //Tune head
+ for (i = 0; i < count; i++) {
+ if (getDistanceQ2(nodebuffer[i]) == 0) {
+ continue;
+ }
+ else {
+ while (i != 0) {
+ i--;
+ float expect_angle = getAngle(nodebuffer[i + 1]) - inc_origin_angle;
+ if (expect_angle < 0.0f) expect_angle = 0.0f;
+ setAngle(nodebuffer[i], expect_angle);
+ }
+ break;
+ }
+ }
+
+ // all the data is invalid
+ if (i == count) return SL_RESULT_OPERATION_FAIL;
+
+ //Tune tail
+ for (i = count - 1; i < count; i--) {
+ // To avoid array overruns, use the i < count condition
+ if (getDistanceQ2(nodebuffer[i]) == 0) {
+ continue;
+ }
+ else {
+ while (i != (count - 1)) {
+ i++;
+ float expect_angle = getAngle(nodebuffer[i - 1]) + inc_origin_angle;
+ if (expect_angle > 360.0f) expect_angle -= 360.0f;
+ setAngle(nodebuffer[i], expect_angle);
+ }
+ break;
+ }
+ }
+
+ //Fill invalid angle in the scan
+ float frontAngle = getAngle(nodebuffer[0]);
+ for (i = 1; i < count; i++) {
+ if (getDistanceQ2(nodebuffer[i]) == 0) {
+ float expect_angle = frontAngle + i * inc_origin_angle;
+ if (expect_angle > 360.0f) expect_angle -= 360.0f;
+ setAngle(nodebuffer[i], expect_angle);
+ }
+ }
+
+ // Reorder the scan according to the angle value
+ std::sort(nodebuffer, nodebuffer + count, &angleLessThan<TNode>);
+
+ return SL_RESULT_OK;
+ }
+
+ template<typename T>
+ class RawSampleNodeHolder
+ {
+ public:
+ RawSampleNodeHolder(size_t maxcount = 8192)
+ : _max_count(maxcount)
+ {
+
+ }
+ void clear()
+ {
+ rp::hal::AutoLocker l(_locker);
+ _data_waiter.set(false);
+ _data_queue.clear();
+ }
+
+ void pushNode(_u64 timestamp_uS, const T* node)
+ {
+ rp::hal::AutoLocker l(_locker);
+ _data_queue.push_back(*node);
+ if (_data_queue.size() > _max_count) {
+ _data_queue.pop_front();
+ }
+ _data_waiter.set();
+ }
+
+ size_t waitAndFetch(T* node, size_t maxcount, _u32 timeout)
+ {
+ if (_data_waiter.wait(timeout) == rp::hal::Event::EVENT_OK)
+ {
+ rp::hal::AutoLocker l(_locker);
+
+ size_t copiedCount = 0;
+
+ while (maxcount--) {
+ node[copiedCount++] = _data_queue.front();
+ _data_queue.pop_front();
+ }
+
+ return copiedCount;
+ }
+ return 0;
+ }
+ protected:
+ size_t _max_count;
+ rp::hal::Locker _locker;
+ rp::hal::Event _data_waiter;
+ std::deque<T> _data_queue;
+
+ };
+
+ template<typename T>
+ class ScanDataHolder
+ {
+ public:
+ ScanDataHolder(size_t maxcount = 8192)
+ : _scan_node_buffer_size(maxcount)
+ , _scan_node_available_id(-1)
+ , _new_scan_ready(false)
+ {
+ _scanbuffer[0].reserve(_scan_node_buffer_size);
+ _scanbuffer[1].reserve(_scan_node_buffer_size);
+
+ memset(_scan_begin_timestamp_uS, 0, sizeof(_scan_begin_timestamp_uS));
+ }
+
+ size_t getMaxCacheCount() const {
+ return _scan_node_buffer_size;
+ }
+
+
+ void reset() {
+ rp::hal::AutoLocker l(_locker);
+ _scan_node_available_id = -1;
+ _new_scan_ready = false;
+ _scanbuffer[0].clear();
+ _scanbuffer[1].clear();
+ _data_waiter.set(false);
+ memset(_scan_begin_timestamp_uS, 0, sizeof(_scan_begin_timestamp_uS));
+ }
+
+ bool checkNewScanSignalAndReset()
+ {
+ return _new_scan_ready.exchange(false);
+ }
+
+ void pushScanNodeData(_u64 currentSampleTsUs, const T* hqNode)
+ {
+ rp::hal::AutoLocker l(_locker);
+
+ int operationBufID = _getOperationBufferID_locked();
+ auto operationalBuf = &_scanbuffer[operationBufID];
+
+ if (hqNode->flag & RPLIDAR_RESP_HQ_FLAG_SYNCBIT) {
+ if (operationalBuf->size()) {
+ operationBufID = _finishCurrentScanAndSwap_locked();
+ operationalBuf = &_scanbuffer[operationBufID];
+
+ // publish the available scan
+ _new_scan_ready = true;
+ _data_waiter.set();
+
+ }
+
+ assert(operationalBuf->size() == 0);
+
+ //store the timestamp info
+ _scan_begin_timestamp_uS[operationBufID] = currentSampleTsUs;
+ }
+ else {
+ if (operationalBuf->size() == 0) {
+ //discard the data, do not form partial scan
+ return;
+ }
+ }
+
+ if (operationalBuf->size() >= _scan_node_buffer_size) {
+ //replace the last entry if buffer is full
+ operationalBuf->at(operationalBuf->size() - 1) = *hqNode;
+ }
+ else {
+ operationalBuf->push_back(*hqNode);
+ }
+
+ }
+
+ void rewindCurrentScanData() {
+ rp::hal::AutoLocker l(_locker);
+ _getOperationalBuffer_locked().clear();
+ }
+
+ std::vector<T>* waitAndLockAvailableScan(_u32 timeout, _u64 * out_timestamp_uS = nullptr)
+ {
+ if (_data_waiter.wait(timeout) == rp::hal::Event::EVENT_OK)
+ {
+ _locker.lock();
+ assert(_scan_node_available_id >= 0);
+ _new_scan_ready = false;
+ if (out_timestamp_uS) {
+ *out_timestamp_uS = _scan_begin_timestamp_uS[_scan_node_available_id];
+ }
+ return &_scanbuffer[_scan_node_available_id];
+ }
+ else {
+ return nullptr;
+ }
+ }
+
+ void unlockScan(std::vector<T>* scan) {
+ if (scan) {
+ _locker.unlock();
+ }
+ }
+
+ protected:
+ int _finishCurrentScanAndSwap_locked() {
+ _scan_node_available_id = _getOperationBufferID_locked();
+ int newOperationalID = 1 - _scan_node_available_id;
+
+ _scanbuffer[newOperationalID].clear();
+ return newOperationalID;
+ }
+
+ int _getOperationBufferID_locked() {
+ if (_scan_node_available_id < 0) return 0;
+ return 1 - _scan_node_available_id;
+ }
+
+ std::vector<T>& _getOperationalBuffer_locked()
+ {
+ return _scanbuffer[_getOperationBufferID_locked()];
+ }
+
+
+ rp::hal::Locker _locker;
+ rp::hal::Event _data_waiter;
+
+
+
+ _u64 _scan_begin_timestamp_uS[2];
+ size_t _scan_node_buffer_size;
+ int _scan_node_available_id;
+ std::atomic<bool> _new_scan_ready;
+
+ std::vector<T> _scanbuffer[2];
+ };
+
+ class SlamtecLidarDriver :
+ public ILidarDriver, internal::IProtocolMessageListener, internal::LIDARSampleDataListener
+ {
+ public:
+ enum {
+ MAX_SCANNODE_CACHE_COUNT = 8192,
+ };
+
+ enum {
+ A2A3_LIDAR_MINUM_MAJOR_ID = 2,
+ BUILTIN_MOTORCTL_MINUM_MAJOR_ID = 6,
+ };
+
+
+ enum {
+ TOF_C_SERIAL_MINUM_MAJOR_ID = 4,
+ TOF_S_SERIAL_MINUM_MAJOR_ID = 6,
+ TOF_T_SERIAL_MINUM_MAJOR_ID = 9,
+ TOF_M_SERIAL_MINUM_MAJOR_ID = 12,
+
+
+ NEWDESIGN_MINUM_MAJOR_ID = TOF_C_SERIAL_MINUM_MAJOR_ID,
+ };
+
+ public:
+ SlamtecLidarDriver()
+ : _isConnected(false)
+ , _isSupportingMotorCtrl(MotorCtrlSupportNone)
+ , _op_locker(true)
+ , _scanHolder(MAX_SCANNODE_CACHE_COUNT)
+ , _rawSampleNodeHolder(MAX_SCANNODE_CACHE_COUNT)
+ , _waiting_packet_type(0)
+ {
+ _protocolHandler = std::make_shared< internal::RPLidarProtocolCodec>();
+ _transeiver = std::make_shared< internal::AsyncTransceiver>(*_protocolHandler);
+ _dataunpacker.reset(internal::LIDARSampleDataUnpacker::CreateInstance(*this));
+
+ _protocolHandler->setMessageListener(this);
+
+ memset(&_cached_DevInfo, 0, sizeof(_cached_DevInfo));
+ }
+
+
+ virtual ~SlamtecLidarDriver()
+ {
+ disconnect();
+ _protocolHandler->setMessageListener(nullptr);
+ }
+
+
+ LIDARTechnologyType getLIDARTechnologyType(const sl_lidar_response_device_info_t* devInfo)
+ {
+ rp::hal::AutoLocker l(_op_locker);
+ if (!devInfo) {
+ devInfo = &_cached_DevInfo;
+ }
+
+ return ParseLIDARTechnologyTypeByModelID(devInfo->model);
+ }
+
+ LIDARMajorType getLIDARMajorType(const sl_lidar_response_device_info_t* devInfo)
+ {
+ rp::hal::AutoLocker l(_op_locker);
+ if (!devInfo) {
+ devInfo = &_cached_DevInfo;
+ }
+
+ return ParseLIDARMajorTypeByModelID(devInfo->model);
+
+ }
+
+ sl_result getModelNameDescriptionString(std::string& out_description, bool fetchAliasName, const sl_lidar_response_device_info_t* devInfo, sl_u32 timeout)
+ {
+ rp::hal::AutoLocker l(_op_locker);
+ u_result ans;
+ // fetch alias (commerical) name if asked:
+ if (fetchAliasName) {
+ std::vector<_u8> replyData;
+ ans = getLidarConf(SL_LIDAR_CONF_MODEL_NAME_ALIAS, replyData, nullptr, 0, timeout);
+ if (IS_OK(ans) && replyData.size()) {
+ out_description.resize(replyData.size() + 1);
+ memcpy(&out_description[0], &replyData[0], replyData.size());
+ out_description[replyData.size()] = '\0';
+ if (out_description != "") {
+ return SL_RESULT_OK;
+ }
+ }
+ }
+
+
+ if (!devInfo) {
+ devInfo = &_cached_DevInfo;
+ }
+
+
+ out_description = GetModelNameStringByModelID(devInfo->model);
+
+ return SL_RESULT_OK;
+ }
+
+ sl_result connect(IChannel* channel)
+ {
+ rp::hal::AutoLocker l(_op_locker);
+ if (!channel) return SL_RESULT_OPERATION_FAIL;
+ if (isConnected()) return SL_RESULT_ALREADY_DONE;
+
+ _rawSampleNodeHolder.clear();
+
+ sl_result ans;
+
+
+ ans = (sl_result)_transeiver->openChannelAndBind(channel);
+
+ if (IS_OK(ans)) {
+ _isConnected = true;
+ // the first dev info local cache will be taken here
+ checkMotorCtrlSupport(_isSupportingMotorCtrl, 500);
+ }
+
+ return ans;
+ }
+
+ void disconnect()
+ {
+ rp::hal::AutoLocker l(_op_locker);
+ if (_isConnected) {
+ _disableDataGrabbing();
+
+ _transeiver->unbindAndClose();
+ _isConnected = false;
+ }
+ }
+
+ bool isConnected()
+ {
+ return _isConnected;
+ }
+
+ sl_result reset(sl_u32 timeoutInMs = DEFAULT_TIMEOUT)
+ {
+ rp::hal::AutoLocker l(_op_locker);
+ // send reset message
+ return (sl_result)_sendCommandWithoutResponse(SL_LIDAR_CMD_RESET);
+ }
+
+ sl_result getAllSupportedScanModes(std::vector<LidarScanMode>& outModes, sl_u32 timeoutInMs = DEFAULT_TIMEOUT)
+ {
+ rp::hal::AutoLocker l(_op_locker);
+
+ if (!isConnected()) return SL_RESULT_OPERATION_NOT_SUPPORT;
+
+ Result<nullptr_t> ans = SL_RESULT_OK;
+ bool confProtocolSupported = false;
+ ans = checkSupportConfigCommands(confProtocolSupported, timeoutInMs);
+ if (!ans) return SL_RESULT_INVALID_DATA;
+
+ if (confProtocolSupported) {
+ // 1. get scan mode count
+ sl_u16 modeCount;
+ ans = getScanModeCount(modeCount, timeoutInMs);
+ if (!ans) return ans;
+ // 2. for loop to get all fields of each scan mode
+ for (sl_u16 i = 0; i < modeCount; i++) {
+ LidarScanMode scanModeInfoTmp;
+ memset(&scanModeInfoTmp, 0, sizeof(scanModeInfoTmp));
+ scanModeInfoTmp.id = i;
+ ans = getLidarSampleDuration(scanModeInfoTmp.us_per_sample, i, timeoutInMs);
+ if (!ans) return ans;
+ ans = getMaxDistance(scanModeInfoTmp.max_distance, i, timeoutInMs);
+ if (!ans) return ans;
+ ans = getScanModeAnsType(scanModeInfoTmp.ans_type, i, timeoutInMs);
+ if (!ans) return ans;
+ ans = getScanModeName(scanModeInfoTmp.scan_mode, sizeof(scanModeInfoTmp.scan_mode), i, timeoutInMs);
+ if (!ans) return ans;
+ outModes.push_back(scanModeInfoTmp);
+
+ }
+ return ans;
+ }
+
+ return ans;
+ }
+
+ sl_result getTypicalScanMode(sl_u16& outMode, sl_u32 timeoutInMs = DEFAULT_TIMEOUT)
+ {
+ rp::hal::AutoLocker l(_op_locker);
+ if (!isConnected()) return SL_RESULT_OPERATION_NOT_SUPPORT;
+
+ Result<nullptr_t> ans = SL_RESULT_OK;
+ std::vector<sl_u8> answer;
+ bool lidarSupportConfigCmds = false;
+ ans = checkSupportConfigCommands(lidarSupportConfigCmds);
+ if (!ans) return ans;
+
+ if (lidarSupportConfigCmds) {
+ ans = getLidarConf(SL_LIDAR_CONF_SCAN_MODE_TYPICAL, answer, nullptr, 0, timeoutInMs);
+ if (!ans) return ans;
+ if (answer.size() < sizeof(sl_u16)) {
+ return SL_RESULT_INVALID_DATA;
+ }
+ const sl_u16 *p_answer = reinterpret_cast<const sl_u16*>(&answer[0]);
+ outMode = *p_answer;
+ return ans;
+ }
+ //old version of triangle lidar
+ else {
+ outMode = SL_LIDAR_CONF_SCAN_COMMAND_EXPRESS;
+ return ans;
+ }
+ return ans;
+
+ }
+
+ sl_result startScan(bool force, bool useTypicalScan, sl_u32 options = 0, LidarScanMode* outUsedScanMode = nullptr)
+ {
+ rp::hal::AutoLocker l(_op_locker);
+ if (!isConnected()) return SL_RESULT_OPERATION_NOT_SUPPORT;
+
+ Result<nullptr_t> ans = SL_RESULT_OK;
+ bool ifSupportLidarConf = false;
+ LidarScanMode localMode;
+
+ if (!isConnected()) return SL_RESULT_OPERATION_FAIL;
+ stop();
+
+ if (!outUsedScanMode) outUsedScanMode = &localMode;
+
+
+ ans = checkSupportConfigCommands(ifSupportLidarConf);
+ if (!ans) return ans;
+ if (useTypicalScan){
+ sl_u16 typicalMode;
+ ans = getTypicalScanMode(typicalMode);
+ if (!ans) return ans;
+
+ //call startScanExpress to do the job
+ return startScanExpress(false, typicalMode, 0, outUsedScanMode);
+ }
+
+ // 'useTypicalScan' is false, just use normal scan mode
+
+
+ return startScanNormal_commonpath(force, ifSupportLidarConf , *outUsedScanMode, DEFAULT_TIMEOUT);
+ }
+
+
+ // this path make sure the working mode has always been retrieved
+ sl_result startScanNormal_commonpath(bool force, bool ifSupportLidarConf, LidarScanMode& outUsedScanMode, sl_u32 timeout)
+ {
+
+ Result<nullptr_t> ans = SL_RESULT_OK;
+
+ if (ifSupportLidarConf) {
+
+ outUsedScanMode.id = SL_LIDAR_CONF_SCAN_COMMAND_STD;
+ ans = getLidarSampleDuration(outUsedScanMode.us_per_sample, outUsedScanMode.id);
+ if (!ans) return ans;
+ ans = getMaxDistance(outUsedScanMode.max_distance, outUsedScanMode.id);
+ if (!ans) return ans;
+ ans = getScanModeAnsType(outUsedScanMode.ans_type, outUsedScanMode.id);
+ if (!ans) return ans;
+ ans = getScanModeName(outUsedScanMode.scan_mode, sizeof(outUsedScanMode.scan_mode), outUsedScanMode.id);
+ if (!ans) return ans;
+
+ }
+ else {
+ // a legacy device
+ rplidar_response_sample_rate_t sampleRateTmp;
+ ans = _getLegacySampleDuration_uS(sampleRateTmp, timeout);
+
+ if (!ans) return SL_RESULT_INVALID_DATA;
+ outUsedScanMode.us_per_sample = sampleRateTmp.std_sample_duration_us;
+ outUsedScanMode.max_distance = 16;
+ outUsedScanMode.ans_type = SL_LIDAR_ANS_TYPE_MEASUREMENT;
+ strcpy(outUsedScanMode.scan_mode, "Standard");
+ }
+
+
+ _updateTimingDesc(_cached_DevInfo, outUsedScanMode.us_per_sample);
+
+ startMotor();
+
+ _scanHolder.reset();
+ _dataunpacker->enable();
+
+ ans = _sendCommandWithoutResponse(force ? SL_LIDAR_CMD_FORCE_SCAN : SL_LIDAR_CMD_SCAN, nullptr, 0, true);
+ if (ans) delay(10); // wait rplidar to handle it
+ return ans;
+ }
+
+
+ sl_result startScanNormal(bool force, sl_u32 timeout = DEFAULT_TIMEOUT)
+ {
+ rp::hal::AutoLocker l(_op_locker);
+ if (!isConnected()) return SL_RESULT_OPERATION_NOT_SUPPORT;
+
+
+ LidarScanMode localMode;
+ bool ifSupportLidarConf;
+
+ if (!isConnected()) return SL_RESULT_OPERATION_FAIL;
+ stop();
+
+ Result<nullptr_t> ans = checkSupportConfigCommands(ifSupportLidarConf);
+ if (!ans) return ans;
+
+ return startScanNormal_commonpath(force, ifSupportLidarConf, localMode, timeout);
+ }
+
+ sl_result startScanExpress(bool force, sl_u16 scanMode, sl_u32 options = 0, LidarScanMode* outUsedScanMode = nullptr, sl_u32 timeout = DEFAULT_TIMEOUT)
+ {
+ rp::hal::AutoLocker l(_op_locker);
+ if (!isConnected()) return SL_RESULT_OPERATION_NOT_SUPPORT;
+
+
+ Result<nullptr_t> ans = SL_RESULT_OK;
+ if (!isConnected()) return SL_RESULT_OPERATION_FAIL;
+ stop(); //force the previous operation to stop
+
+ LidarScanMode localMode;
+
+ if (!outUsedScanMode) outUsedScanMode = &localMode;
+
+ bool ifSupportLidarConf = false;
+ ans = checkSupportConfigCommands(ifSupportLidarConf);
+ if (!ans) return SL_RESULT_INVALID_DATA;
+
+
+
+ outUsedScanMode->id = scanMode;
+ if (ifSupportLidarConf) {
+ ans = getLidarSampleDuration(outUsedScanMode->us_per_sample, outUsedScanMode->id);
+ if (!ans) return SL_RESULT_INVALID_DATA;
+
+ ans = getMaxDistance(outUsedScanMode->max_distance, outUsedScanMode->id);
+ if (!ans) return SL_RESULT_INVALID_DATA;
+
+ ans = getScanModeAnsType(outUsedScanMode->ans_type, outUsedScanMode->id);
+ if (!ans) return SL_RESULT_INVALID_DATA;
+
+ ans = getScanModeName(outUsedScanMode->scan_mode, sizeof(outUsedScanMode->scan_mode), outUsedScanMode->id);
+ if (!ans) return SL_RESULT_INVALID_DATA;
+ }
+ else {
+ // legacy device support
+ if (scanMode != RPLIDAR_CONF_SCAN_COMMAND_STD) {
+ rplidar_response_sample_rate_t sampleRateTmp;
+ ans = _getLegacySampleDuration_uS(sampleRateTmp, timeout);
+ if (!ans) return RESULT_INVALID_DATA;
+
+ outUsedScanMode->us_per_sample = sampleRateTmp.express_sample_duration_us;
+ outUsedScanMode->max_distance = 16;
+ outUsedScanMode->ans_type = SL_LIDAR_ANS_TYPE_MEASUREMENT_CAPSULED;
+ strcpy(outUsedScanMode->scan_mode, "Express");
+ }
+ else {
+ outUsedScanMode->ans_type = SL_LIDAR_ANS_TYPE_MEASUREMENT;
+ }
+ }
+
+ if (outUsedScanMode->ans_type == SL_LIDAR_ANS_TYPE_MEASUREMENT)
+ {
+ // redirect to the correct function...
+ return startScanNormal(force, timeout);
+ }
+
+ _updateTimingDesc(_cached_DevInfo, outUsedScanMode->us_per_sample);
+ startMotor();
+
+ _scanHolder.reset();
+ _dataunpacker->enable();
+
+ sl_lidar_payload_express_scan_t scanReq;
+ memset(&scanReq, 0, sizeof(scanReq));
+
+ if (!ifSupportLidarConf) {
+ if (scanMode != SL_LIDAR_CONF_SCAN_COMMAND_STD && scanMode != SL_LIDAR_CONF_SCAN_COMMAND_EXPRESS)
+ scanReq.working_mode = sl_u8(scanMode);
+ }
+ else
+ scanReq.working_mode = sl_u8(scanMode);
+
+ scanReq.working_flags = options;
+
+ ans = _sendCommandWithoutResponse(SL_LIDAR_CMD_EXPRESS_SCAN, &scanReq, sizeof(scanReq), true);
+ if (ans) delay(10); // wait rplidar to handle it
+ return ans;
+
+ }
+
+ sl_result stop(sl_u32 timeout = DEFAULT_TIMEOUT)
+ {
+ rp::hal::AutoLocker l(_op_locker);
+
+
+ u_result ans = SL_RESULT_OK;
+ ans = _sendCommandWithoutResponse(SL_LIDAR_CMD_STOP);
+ _disableDataGrabbing();
+
+ if (IS_FAIL(ans)) return ans;
+
+
+ delay(100);
+
+ if(_isSupportingMotorCtrl == MotorCtrlSupportPwm)
+ setMotorSpeed(0);
+
+ return SL_RESULT_OK;
+ }
+
+ sl_result grabScanDataHqWithTimeStamp(sl_lidar_response_measurement_node_hq_t* nodebuffer, size_t& count, sl_u64& timestamp_uS, sl_u32 timeout = DEFAULT_TIMEOUT)
+ {
+ rp::hal::AutoLocker l(_op_locker);
+
+ if (!nodebuffer)
+ return SL_RESULT_INVALID_DATA;
+
+ auto availBuffer = _scanHolder.waitAndLockAvailableScan(timeout, &timestamp_uS);
+ if (!availBuffer) return SL_RESULT_OPERATION_TIMEOUT;
+
+ count = std::min<size_t>(count, availBuffer->size());
+
+ std::copy(availBuffer->begin(), availBuffer->begin() + count, nodebuffer);
+
+ _scanHolder.unlockScan(availBuffer);
+
+ return RESULT_OK;
+ }
+
+ sl_result grabScanDataHq(sl_lidar_response_measurement_node_hq_t* nodebuffer, size_t& count, sl_u32 timeout = DEFAULT_TIMEOUT)
+ {
+ _u64 localTS;
+ return grabScanDataHqWithTimeStamp(nodebuffer, count, localTS, timeout);
+ }
+
+ sl_result getDeviceInfo(sl_lidar_response_device_info_t& info, sl_u32 timeout = DEFAULT_TIMEOUT)
+ {
+ rp::hal::AutoLocker l(_op_locker);
+ if (!isConnected()) return SL_RESULT_OPERATION_NOT_SUPPORT;
+
+
+ u_result ans;
+ internal::message_autoptr_t ans_frame;
+
+ ans = _sendCommandWithResponse(SL_LIDAR_CMD_GET_DEVICE_INFO, SL_LIDAR_ANS_TYPE_DEVINFO, ans_frame, timeout);
+
+ if (IS_FAIL(ans)) return ans;
+ if (ans_frame->getPayloadSize() < sizeof(rplidar_response_device_info_t))
+ {
+ return RESULT_INVALID_DATA;
+ }
+ info = *(rplidar_response_device_info_t*)ans_frame->getDataBuf();
+#ifdef _CPU_ENDIAN_BIG
+ info.firmware_version = le16_to_cpu(info.firmware_version);
+#endif
+
+ _cached_DevInfo = info;
+ return (sl_result)ans;
+ }
+
+ sl_result checkMotorCtrlSupport(MotorCtrlSupport & support, sl_u32 timeout = DEFAULT_TIMEOUT)
+ {
+ rp::hal::AutoLocker l(_op_locker);
+ if (!isConnected()) return SL_RESULT_OPERATION_NOT_SUPPORT;
+
+ Result<nullptr_t> ans = SL_RESULT_OK;
+ support = MotorCtrlSupportNone;
+ _disableDataGrabbing();
+
+ {
+ sl_lidar_response_device_info_t devInfo;
+ ans = getDeviceInfo(devInfo, 500);
+ if (!ans) return ans;
+ sl_u8 majorId = devInfo.model >> 4;
+ if (majorId >= BUILTIN_MOTORCTL_MINUM_MAJOR_ID) {
+ support = MotorCtrlSupportRpm;
+ return ans;
+ }
+ else if(majorId >= A2A3_LIDAR_MINUM_MAJOR_ID){
+
+ rp::hal::AutoLocker l(_op_locker);
+ sl_lidar_payload_acc_board_flag_t flag;
+ flag.reserved = 0;
+ internal::message_autoptr_t ans_frame;
+
+ ans = _sendCommandWithResponse(SL_LIDAR_CMD_GET_ACC_BOARD_FLAG, SL_LIDAR_ANS_TYPE_ACC_BOARD_FLAG, ans_frame, timeout, &flag, sizeof(flag));
+ if (!ans) return ans;
+
+ if (ans_frame->getPayloadSize() < sizeof(rplidar_response_acc_board_flag_t))
+ {
+ return RESULT_INVALID_DATA;
+ }
+
+ const sl_lidar_response_acc_board_flag_t* acc_board_flag
+ = reinterpret_cast<const sl_lidar_response_acc_board_flag_t*>(ans_frame->getDataBuf());
+
+ if (acc_board_flag->support_flag & SL_LIDAR_RESP_ACC_BOARD_FLAG_MOTOR_CTRL_SUPPORT_MASK) {
+ support = MotorCtrlSupportPwm;
+ }
+ return ans;
+ }
+
+ }
+ return SL_RESULT_OK;
+
+ }
+
+ sl_result getFrequency(const LidarScanMode& scanMode, const sl_lidar_response_measurement_node_hq_t* nodes, size_t count, float& frequency)
+ {
+ float sample_duration = scanMode.us_per_sample;
+ frequency = 1000000.0f / (count * sample_duration);
+ return SL_RESULT_OK;
+ }
+
+ sl_result setLidarIpConf(const sl_lidar_ip_conf_t& conf, sl_u32 timeout)
+ {
+ rp::hal::AutoLocker l(_op_locker);
+ if (!isConnected()) return SL_RESULT_OPERATION_NOT_SUPPORT;
+
+ sl_result ans = setLidarConf(SL_LIDAR_CONF_LIDAR_STATIC_IP_ADDR, &conf, sizeof(sl_lidar_ip_conf_t), timeout);
+ return ans;
+ }
+
+ sl_result getLidarIpConf(sl_lidar_ip_conf_t& conf, sl_u32 timeout)
+ {
+ rp::hal::AutoLocker l(_op_locker);
+ if (!isConnected()) return SL_RESULT_OPERATION_NOT_SUPPORT;
+
+
+ Result<nullptr_t> ans = SL_RESULT_OK;
+ std::vector<sl_u8> reserve(2); //keep backward compatibility
+
+ std::vector<sl_u8> answer;
+ ans = getLidarConf(SL_LIDAR_CONF_LIDAR_STATIC_IP_ADDR, answer, &reserve[0], 2, timeout);
+ size_t len = answer.size();
+ if (0 == len) return SL_RESULT_INVALID_DATA;
+ memcpy(&conf, &answer[0], len);
+ return ans;
+ }
+
+ sl_result getHealth(sl_lidar_response_device_health_t& health, sl_u32 timeout = DEFAULT_TIMEOUT)
+ {
+ rp::hal::AutoLocker l(_op_locker);
+ if (!isConnected()) return SL_RESULT_OPERATION_NOT_SUPPORT;
+
+
+ u_result ans;
+ internal::message_autoptr_t ans_frame;
+
+ ans = _sendCommandWithResponse(SL_LIDAR_CMD_GET_DEVICE_HEALTH, SL_LIDAR_ANS_TYPE_DEVHEALTH, ans_frame, timeout);
+
+ if (IS_FAIL(ans)) return ans;
+ if (ans_frame->getPayloadSize() < sizeof(rplidar_response_device_health_t))
+ {
+ return SL_RESULT_INVALID_DATA;
+ }
+ health = *(rplidar_response_device_health_t*)ans_frame->getDataBuf();
+#ifdef _CPU_ENDIAN_BIG
+ health.error_code = le16_to_cpu(health.error_code);
+#endif
+
+ return ans;
+ }
+
+ sl_result getDeviceMacAddr(sl_u8* macAddrArray, sl_u32 timeoutInMs)
+ {
+ rp::hal::AutoLocker l(_op_locker);
+ if (!isConnected()) return SL_RESULT_OPERATION_NOT_SUPPORT;
+
+
+ u_result ans;
+
+ std::vector<_u8> answer(6, 0);
+ ans = getLidarConf(SL_LIDAR_CONF_LIDAR_MAC_ADDR, answer, NULL, 0, timeoutInMs);
+ if (IS_FAIL(ans))
+ {
+ return ans;
+ }
+ size_t len = answer.size();
+ if (0 == len) return SL_RESULT_INVALID_DATA;
+ memcpy(macAddrArray, &answer[0], len);
+ return ans;
+ }
+
+ sl_result ascendScanData(sl_lidar_response_measurement_node_hq_t * nodebuffer, size_t count)
+ {
+ return ascendScanData_<sl_lidar_response_measurement_node_hq_t>(nodebuffer, count);
+ }
+
+ sl_result getScanDataWithIntervalHq(sl_lidar_response_measurement_node_hq_t * nodebuffer, size_t & count)
+ {
+ count = _rawSampleNodeHolder.waitAndFetch(nodebuffer, count, 0);
+ return SL_RESULT_OK;
+ }
+
+ sl_result setMotorSpeed(sl_u16 speed = DEFAULT_MOTOR_SPEED)
+ {
+ rp::hal::AutoLocker l(_op_locker);
+ if (!isConnected()) return SL_RESULT_OPERATION_NOT_SUPPORT;
+
+
+ Result<nullptr_t> ans = SL_RESULT_OK;
+
+ if(speed == DEFAULT_MOTOR_SPEED){
+ sl_lidar_response_desired_rot_speed_t desired_speed;
+ ans = getDesiredSpeed(desired_speed);
+ if (ans) {
+ if (_isSupportingMotorCtrl == MotorCtrlSupportPwm)
+ speed = desired_speed.pwm_ref;
+ else
+ speed = desired_speed.rpm;
+ }
+ else {
+ //set a dummy default value
+ speed = 600;
+ }
+ }
+ switch (_isSupportingMotorCtrl)
+ {
+ case MotorCtrlSupportNone:
+ if (_transeiver->getBindedChannel()->getChannelType() == CHANNEL_TYPE_SERIALPORT) {
+ ISerialPortChannel* serialChanel = (ISerialPortChannel*)_transeiver->getBindedChannel();
+ if (!speed) {
+ serialChanel->setDTR(true);
+ }else{
+ serialChanel->setDTR(false);
+ }
+ }
+ break;
+ case MotorCtrlSupportPwm:
+ sl_lidar_payload_motor_pwm_t motor_pwm;
+ motor_pwm.pwm_value = speed;
+
+
+ ans = _sendCommandWithoutResponse(SL_LIDAR_CMD_SET_MOTOR_PWM, &motor_pwm, sizeof(motor_pwm), true);
+ if (!ans) return ans;
+ delay(10);
+ break;
+ case MotorCtrlSupportRpm:
+ sl_lidar_payload_motor_pwm_t motor_rpm;
+ motor_rpm.pwm_value = speed;
+
+ ans = _sendCommandWithoutResponse(SL_LIDAR_CMD_HQ_MOTOR_SPEED_CTRL, &motor_rpm, sizeof(motor_rpm), true);
+ if (!ans) return ans;
+ delay(10);
+ break;
+ }
+ return SL_RESULT_OK;
+ }
+
+ sl_result getMotorInfo(LidarMotorInfo &motorInfo, sl_u32 timeoutInMs)
+ {
+ Result<nullptr_t> ans = SL_RESULT_OK;
+ rp::hal::AutoLocker l(_op_locker);
+ if (!isConnected()) return SL_RESULT_OPERATION_NOT_SUPPORT;
+
+
+ {
+ std::vector<sl_u8> answer;
+
+ ans = getLidarConf(SL_LIDAR_CONF_MIN_ROT_FREQ, answer);
+ if (!ans) return ans;
+
+ const sl_u16 *min_answer = reinterpret_cast<const sl_u16*>(&answer[0]);
+ motorInfo.min_speed = *min_answer;
+
+
+ ans = getLidarConf(SL_LIDAR_CONF_MAX_ROT_FREQ, answer);
+ if (!ans) return ans;
+
+ const sl_u16 *max_answer = reinterpret_cast<const sl_u16*>(&answer[0]);
+ motorInfo.max_speed = *max_answer;
+
+ sl_lidar_response_desired_rot_speed_t desired_speed;
+ ans = getDesiredSpeed(desired_speed);
+ if (!ans) return ans;
+ if(motorInfo.motorCtrlSupport == MotorCtrlSupportPwm)
+ motorInfo.desired_speed = desired_speed.pwm_ref;
+ else
+ motorInfo.desired_speed = desired_speed.rpm;
+
+ }
+ return SL_RESULT_OK;
+ }
+
+ sl_result negotiateSerialBaudRate(sl_u32 requiredBaudRate, sl_u32* baudRateDetected)
+ {
+ // ask the LIDAR to stop working first...
+ stop();
+
+ rp::hal::AutoLocker l(_op_locker);
+
+ IChannel* cachedChannel = _transeiver->getBindedChannel();
+ if (!cachedChannel) return SL_RESULT_OPERATION_FAIL;
+ if (cachedChannel->getChannelType() != CHANNEL_TYPE_SERIALPORT)
+ {
+ // only works for UART connection
+ return RESULT_OPERATION_NOT_SUPPORT;
+ }
+
+ // disable the transeiver as it may interrupt the operation...
+ _transeiver->unbindAndClose();
+
+ sl_result ans = SL_RESULT_OK;
+
+ do {
+ // reopen the channel...
+
+ if (!cachedChannel->open()) {
+ // failed to reopen
+ // try to revert back...
+ ans = SL_RESULT_OPERATION_FAIL;
+ break;
+ }
+
+ cachedChannel->flush();
+
+ // wait for a while
+ delay(10);
+ cachedChannel->clearReadCache();
+
+ // sending magic byte to let the target LIDAR start baudrate measurement
+ // More than 100 bytes per second datarate is required to trigger the measurements
+ {
+
+
+ sl_u8 magicByteSeq[16];
+
+ memset(magicByteSeq, SL_LIDAR_AUTOBAUD_MAGICBYTE, sizeof(magicByteSeq));
+
+ sl_u64 startTS = getms();
+
+ while (getms() - startTS < 1500) //lasting for 1.5sec
+ {
+ if (cachedChannel->write(magicByteSeq, sizeof(magicByteSeq)) < 0)
+ {
+ ans = SL_RESULT_OPERATION_FAIL;
+ break;
+ }
+
+ size_t dataCountGot;
+ if (cachedChannel->waitForData(1, 1, &dataCountGot)) {
+ //got reply, stop
+ ans = SL_RESULT_OK;
+ break;
+ }
+ }
+ }
+
+ if (IS_FAIL(ans)) break;
+
+ // getback the bps measured
+ _u32 bpsDetected = 0;
+ size_t dataCountGot;
+ if (cachedChannel->waitForData(4, 500, &dataCountGot)) {
+ //got reply, stop
+ cachedChannel->read(&bpsDetected, 4);
+ if (baudRateDetected) *baudRateDetected = bpsDetected;
+
+
+ cachedChannel->close();
+ // restart the transiever
+ ans = _transeiver->openChannelAndBind(cachedChannel);
+ if (IS_FAIL(ans)) return ans;
+
+
+ // send a confirmation to the LIDAR, otherwise, the previous baudrate will be reverted back
+ sl_lidar_payload_new_bps_confirmation_t confirmation;
+ confirmation.flag = 0x5F5F;
+ confirmation.required_bps = requiredBaudRate;
+ confirmation.param = 0;
+
+
+ ans = _sendCommandWithoutResponse(SL_LIDAR_CMD_NEW_BAUDRATE_CONFIRM, &confirmation, sizeof(confirmation));
+
+ return ans;
+ }
+ } while (0);
+
+ _transeiver->openChannelAndBind(cachedChannel);
+
+ return ans;
+ }
+
+ protected:
+ sl_result startMotor()
+ {
+ return setMotorSpeed(DEFAULT_MOTOR_SPEED);
+ }
+
+ u_result getDesiredSpeed(sl_lidar_response_desired_rot_speed_t & motorSpeed, sl_u32 timeoutInMs = DEFAULT_TIMEOUT)
+ {
+ u_result ans;
+ std::vector<sl_u8> answer;
+ ans = getLidarConf(SL_LIDAR_CONF_DESIRED_ROT_FREQ, answer, nullptr, 0, timeoutInMs);
+
+ if (IS_FAIL(ans)) return ans;
+
+ const sl_lidar_response_desired_rot_speed_t *p_answer = reinterpret_cast<const sl_lidar_response_desired_rot_speed_t*>(&answer[0]);
+ motorSpeed = *p_answer;
+ return RESULT_OK;
+ }
+
+ u_result checkSupportConfigCommands(bool& outSupport, sl_u32 timeoutInMs = DEFAULT_TIMEOUT)
+ {
+ u_result ans;
+ rplidar_response_device_info_t devinfo;
+ ans = getDeviceInfo(devinfo, timeoutInMs);
+ if (IS_FAIL(ans)) {
+ outSupport = false;
+ return ans;
+ }
+
+
+ if (_checkNDMagicNumber(devinfo.model)) {
+
+ outSupport = true;
+ }
+ else {
+ // if lidar firmware >= 1.24
+ outSupport = (devinfo.firmware_version >= ((0x1 << 8) | 24));
+ }
+ return RESULT_OK;
+ }
+
+
+ u_result getScanModeCount(sl_u16& modeCount, sl_u32 timeoutInMs = DEFAULT_TIMEOUT)
+ {
+ u_result ans;
+ std::vector<_u8> answer;
+ ans = getLidarConf(SL_LIDAR_CONF_SCAN_MODE_COUNT, answer);
+ if (IS_FAIL(ans)) {
+ return ans;
+ }
+ if (answer.size() < sizeof(_u16)) {
+ return RESULT_INVALID_DATA;
+ }
+ const _u16* p_answer = reinterpret_cast<const _u16*>(&answer[0]);
+ modeCount = *p_answer;
+ return ans;
+ }
+
+ u_result setLidarConf(_u32 type, const void* payload, size_t payloadSize, _u32 timeout)
+ {
+ if (type < 0x00010000 || type >0x0001FFFF)
+ return SL_RESULT_INVALID_DATA;
+
+
+ std::vector<sl_u8> requestPkt;
+ requestPkt.resize(sizeof(sl_lidar_payload_set_scan_conf_t) + payloadSize);
+ if (!payload) payloadSize = 0;
+ sl_lidar_payload_set_scan_conf_t* query = reinterpret_cast<sl_lidar_payload_set_scan_conf_t*>(&requestPkt[0]);
+
+ query->type = type;
+
+ if (payloadSize)
+ memcpy(&query[1], payload, payloadSize);
+
+ sl_result ans;
+ internal::message_autoptr_t ans_frame;
+ ans = _sendCommandWithResponse(SL_LIDAR_CMD_SET_LIDAR_CONF, SL_LIDAR_ANS_TYPE_SET_LIDAR_CONF, ans_frame, timeout, &requestPkt[0], requestPkt.size());
+
+ if (IS_FAIL(ans)) {
+ return ans;
+ }
+
+ //check if returned size is even less than sizeof(type)
+ if (ans_frame->getPayloadSize() < sizeof(rplidar_response_set_lidar_conf_t)) {
+ return RESULT_INVALID_DATA;
+ }
+
+ const rplidar_response_set_lidar_conf_t* response =
+ reinterpret_cast<const rplidar_response_set_lidar_conf_t*>(ans_frame->getDataBuf());
+
+
+ if (ans_frame->getPayloadSize() == 4) {
+ // legacy device?
+ return *(const u_result*)(ans_frame->getDataBuf());
+ }
+ else {
+ if (response->type != type) {
+ return RESULT_INVALID_DATA;
+ }
+
+ return (u_result)response->result;
+ }
+ }
+
+ u_result getLidarConf(_u32 type, std::vector<_u8>& outputBuf, const void* payload = NULL, size_t payloadSize = 0, _u32 timeout = DEFAULT_TIMEOUT)
+ {
+ std::vector<_u8> requestPkt;
+
+ if (!payload) payloadSize = 0;
+ requestPkt.resize(sizeof(rplidar_payload_get_scan_conf_t) + payloadSize);
+ rplidar_payload_get_scan_conf_t* query = reinterpret_cast<rplidar_payload_get_scan_conf_t*>(&requestPkt[0]);
+
+ query->type = type;
+
+ if (payloadSize)
+ memcpy(&query[1], payload, payloadSize);
+
+ u_result ans;
+ internal::message_autoptr_t ans_frame;
+ ans = _sendCommandWithResponse(SL_LIDAR_CMD_GET_LIDAR_CONF, SL_LIDAR_ANS_TYPE_GET_LIDAR_CONF, ans_frame, timeout, &requestPkt[0], requestPkt.size());
+ if (IS_FAIL(ans)) {
+ return ans;
+ }
+ //check if returned size is even less than sizeof(type)
+ if (ans_frame->getPayloadSize() < offsetof(rplidar_response_get_lidar_conf_t, payload)) {
+ return SL_RESULT_INVALID_DATA;
+ }
+
+ //check if returned type is same as asked type
+ const rplidar_response_get_lidar_conf_t* replied =
+ reinterpret_cast<const rplidar_response_get_lidar_conf_t*>(ans_frame->getDataBuf());
+
+
+ if (replied->type != type) {
+ return SL_RESULT_INVALID_DATA;
+ }
+ //copy all the payload into &outputBuf
+ int payLoadLen = (int)ans_frame->getPayloadSize() - (int)offsetof(rplidar_response_get_lidar_conf_t, payload);
+ //do consistency check
+ if (payLoadLen < 0) {
+ return SL_RESULT_INVALID_DATA;
+ }
+ //copy all payLoadLen bytes to outputBuf
+ outputBuf.resize(payLoadLen);
+ if (payLoadLen)
+ memcpy(&outputBuf[0], replied->payload, payLoadLen);
+ return ans;
+ }
+
+ u_result getLidarSampleDuration(float& sampleDurationRes, sl_u16 scanModeID, sl_u32 timeoutInMs = DEFAULT_TIMEOUT)
+ {
+ u_result ans;
+
+ std::vector<_u8> answer;
+ ans = getLidarConf(SL_LIDAR_CONF_SCAN_MODE_US_PER_SAMPLE, answer, &scanModeID, sizeof(_u16), timeoutInMs);
+ if (IS_FAIL(ans))
+ {
+ return ans;
+ }
+ if (answer.size() < sizeof(_u32))
+ {
+ return SL_RESULT_INVALID_DATA;
+ }
+ const _u32* result = reinterpret_cast<const _u32*>(&answer[0]);
+ sampleDurationRes = (float)(*result / 256.0);
+ return ans;
+ }
+
+ u_result getMaxDistance(float &maxDistance, sl_u16 scanModeID, sl_u32 timeoutInMs = DEFAULT_TIMEOUT)
+ {
+ u_result ans;
+
+
+ std::vector<_u8> answer;
+ ans = getLidarConf(SL_LIDAR_CONF_SCAN_MODE_MAX_DISTANCE, answer, &scanModeID, sizeof(_u16), timeoutInMs);
+ if (IS_FAIL(ans))
+ {
+ return ans;
+ }
+ if (answer.size() < sizeof(_u32))
+ {
+ return SL_RESULT_INVALID_DATA;
+ }
+ const _u32* result = reinterpret_cast<const _u32*>(&answer[0]);
+ maxDistance = (float)(*result >> 8);
+ return ans;
+ }
+
+ u_result getScanModeAnsType(sl_u8 &ansType, sl_u16 scanModeID, sl_u32 timeoutInMs = DEFAULT_TIMEOUT)
+ {
+ u_result ans;
+
+ std::vector<_u8> answer;
+ ans = getLidarConf(SL_LIDAR_CONF_SCAN_MODE_ANS_TYPE, answer, &scanModeID, sizeof(_u16), timeoutInMs);
+ if (IS_FAIL(ans))
+ {
+ return ans;
+ }
+ if (answer.size() < sizeof(_u8))
+ {
+ return SL_RESULT_INVALID_DATA;
+ }
+ const _u8* result = reinterpret_cast<const _u8*>(&answer[0]);
+ ansType = *result;
+ return ans;
+ }
+
+ u_result getScanModeName(char* modeName, size_t stringSize, _u16 scanModeID, _u32 timeoutInMs = DEFAULT_TIMEOUT)
+ {
+ u_result ans;
+
+ std::vector<_u8> answer;
+ ans = getLidarConf(SL_LIDAR_CONF_SCAN_MODE_NAME, answer, &scanModeID, sizeof(_u16), timeoutInMs);
+ if (IS_FAIL(ans))
+ {
+ return ans;
+ }
+ size_t len = std::min<size_t>(answer.size(), stringSize);
+ if (0 == len) return SL_RESULT_INVALID_DATA;
+
+ memcpy(modeName, &answer[0], len);
+ return ans;
+ }
+
+
+ static LIDARTechnologyType ParseLIDARTechnologyTypeByModelID(_u8 modelID)
+ {
+ _u8 majorModelID = (modelID >> 4);
+ // FIXME: stupid implementation here
+ if (majorModelID < NEWDESIGN_MINUM_MAJOR_ID) {
+ return LIDAR_TECHNOLOGY_TRIANGULATION;
+ }
+ else {
+ return LIDAR_TECHNOLOGY_DTOF;
+ }
+ }
+
+ static LIDARMajorType ParseLIDARMajorTypeByModelID(_u8 modelID)
+ {
+ _u8 majorModelID = (modelID >> 4);
+
+
+ if (majorModelID >= TOF_M_SERIAL_MINUM_MAJOR_ID) {
+ return LIDAR_MAJOR_TYPE_M_SERIES;
+ }
+ else if (majorModelID >= TOF_T_SERIAL_MINUM_MAJOR_ID) {
+ return LIDAR_MAJOR_TYPE_T_SERIES;
+ }
+ else if (majorModelID >= TOF_S_SERIAL_MINUM_MAJOR_ID) {
+ return LIDAR_MAJOR_TYPE_S_SERIES;
+ }
+ else if (majorModelID >= TOF_C_SERIAL_MINUM_MAJOR_ID) {
+ return LIDAR_MAJOR_TYPE_C_SERIES;
+ }
+ else {
+ return LIDAR_MAJOR_TYPE_A_SERIES;
+ }
+ }
+
+ static std::string GetModelNameStringByModelID(_u8 modelID)
+ {
+
+ char stringBuffer[100];
+ auto majorType = ParseLIDARMajorTypeByModelID(modelID);
+
+
+ switch (majorType) {
+ case LIDAR_MAJOR_TYPE_A_SERIES:
+ sprintf(stringBuffer, "A%dM%d", (modelID >> 4), (modelID & 0xF));
+
+ break;
+
+ case LIDAR_MAJOR_TYPE_S_SERIES:
+ sprintf(stringBuffer, "S%dM%d", (modelID >> 4) - (TOF_S_SERIAL_MINUM_MAJOR_ID)+1, (modelID & 0xF));
+
+ break;
+
+ case LIDAR_MAJOR_TYPE_T_SERIES:
+ sprintf(stringBuffer, "T%dM%d", (modelID >> 4) - (TOF_T_SERIAL_MINUM_MAJOR_ID)+1, (modelID & 0xF));
+
+ break;
+
+ case LIDAR_MAJOR_TYPE_M_SERIES:
+ sprintf(stringBuffer, "M%dM%d", (modelID >> 4) - (TOF_M_SERIAL_MINUM_MAJOR_ID)+1, (modelID & 0xF));
+
+ break;
+
+ case LIDAR_MAJOR_TYPE_C_SERIES:
+ sprintf(stringBuffer, "C%dM%d", (modelID >> 4) - (TOF_C_SERIAL_MINUM_MAJOR_ID)+1, (modelID & 0xF));
+
+ break;
+
+
+ default:
+ sprintf(stringBuffer, "unknown(%x)", modelID);
+ }
+
+ return std::string(stringBuffer);
+ }
+
+ protected:
+
+ void _disableDataGrabbing()
+ {
+ _dataunpacker->disable();
+ _protocolHandler->exitLoopMode(); // exit loop mode
+ }
+
+
+
+ bool _checkNDMagicNumber(_u8 model)
+ {
+ return ((model >> 4) >= NEWDESIGN_MINUM_MAJOR_ID);
+ }
+
+
+
+
+ u_result _detectLIDARNativeInterfaceType(LIDARInterfaceType & outputType, const rplidar_response_device_info_t& devInfo, sl_u32 timeout = DEFAULT_TIMEOUT)
+ {
+
+ LIDARMajorType majorType = ParseLIDARMajorTypeByModelID(devInfo.model);
+
+ switch (majorType) {
+ case LIDAR_MAJOR_TYPE_A_SERIES:
+ case LIDAR_MAJOR_TYPE_M_SERIES:
+ case LIDAR_MAJOR_TYPE_C_SERIES:
+
+ outputType = LIDAR_INTERFACE_UART;
+ return SL_RESULT_OK;
+
+
+ case LIDAR_MAJOR_TYPE_T_SERIES:
+ outputType = LIDAR_INTERFACE_ETHERNET;
+ return SL_RESULT_OK;
+
+ case LIDAR_MAJOR_TYPE_S_SERIES:
+ {
+ // ethernet version exists, check whether it is
+ _u8 macAddr[6];
+ u_result ans = getDeviceMacAddr(macAddr, timeout);
+ if (IS_FAIL(ans)) {
+ // cannot retrieve the device mac address, consider a UART interface version
+ outputType = LIDAR_INTERFACE_UART;
+ }
+ else {
+ outputType = LIDAR_INTERFACE_ETHERNET;
+ }
+ return SL_RESULT_OK;
+ }
+
+
+ case LIDAR_MAJOR_TYPE_UNKNOWN:
+ default:
+ outputType = LIDAR_INTERFACE_UNKNOWN;
+ return SL_RESULT_OK;
+ }
+ }
+
+ _u32 _getNativeBaudRate(const rplidar_response_device_info_t & devInfo)
+ {
+ _u8 majorModelID = (devInfo.model >> 4);
+ switch (majorModelID)
+ {
+ case 1:
+ case 2:
+ case 3: //A1..A3 series
+ return (devInfo.hardware_version >= 6) ? 256000 : 115200;
+ case 4: //C series
+ return 460800;
+ case 6: //model ID of S1
+ return 256000;
+ case 7: //model ID of S2
+ case 8: //model ID of S3
+ if (devInfo.model == (0x82)) return 460800;
+ return 1000000;
+ default:
+ return 0; //0 as unknown
+ }
+ }
+
+ bool _updateTimingDesc(const rplidar_response_device_info_t& devInfo, float selectedSampleDuration)
+ {
+ _timing_desc.native_baudrate = _getNativeBaudRate(devInfo);
+ _detectLIDARNativeInterfaceType(_timing_desc.native_interface_type, devInfo, 500);
+
+ _timing_desc.sample_duration_uS = (_u64)(selectedSampleDuration + 0.5f);
+
+ //FIXME: will be changed in future releases
+ _timing_desc.native_timestamp_support = false;
+ _timing_desc.linkage_delay_uS = 0;
+
+
+ // notify the data unpacker
+ _dataunpacker->updateUnpackerContext(internal::LIDARSampleDataUnpacker::UNPACKER_CONTEXT_TYPE_LIDAR_TIMING ,&_timing_desc, sizeof(_timing_desc));
+ return true;
+
+ }
+
+ u_result _getLegacySampleDuration_uS(rplidar_response_sample_rate_t& rateInfo, _u32 timeout)
+ {
+
+ static const _u32 LEGACY_SAMPLE_DURATION = 476;
+
+ rplidar_response_device_info_t devinfo;
+ // 1. fetch the device version first...
+ u_result ans = getDeviceInfo(devinfo, timeout);
+
+ rateInfo.express_sample_duration_us = LEGACY_SAMPLE_DURATION;
+ rateInfo.std_sample_duration_us = LEGACY_SAMPLE_DURATION;
+
+ if (IS_FAIL(ans)) {
+ return ans;
+ }
+
+ if (getLIDARMajorType(&devinfo) == LIDAR_MAJOR_TYPE_A_SERIES) {
+ if (devinfo.firmware_version < ((0x1 << 8) | 17)) {
+ // very very rare and old model found!!
+ return SL_RESULT_OK;
+ }
+ }
+
+
+ internal::message_autoptr_t ans_frame;
+
+ ans = _sendCommandWithResponse(SL_LIDAR_CMD_GET_SAMPLERATE, SL_LIDAR_ANS_TYPE_SAMPLE_RATE, ans_frame, timeout);
+
+ if (IS_FAIL(ans)) return ans;
+ if (ans_frame->getPayloadSize() < sizeof(rplidar_response_sample_rate_t))
+ {
+ return RESULT_INVALID_DATA;
+ }
+ memcpy(&rateInfo, ans_frame->getDataBuf(), sizeof(rateInfo));
+
+#ifdef _CPU_ENDIAN_BIG
+ rateInfo.express_sample_duration_us = le16_to_cpu(rateInfo.express_sample_duration_us);
+ rateInfo.std_sample_duration_us = le16_to_cpu(rateInfo.std_sample_duration_us);
+#endif
+
+ return ans;
+ }
+
+
+ u_result _sendCommandWithoutResponse(_u8 cmd, const void* payload = NULL, size_t payloadsize = 0, bool noForceStop = false)
+ {
+ if (!noForceStop) {
+ _disableDataGrabbing();
+ }
+ _response_waiter.set(false);
+
+ internal::message_autoptr_t message(new internal::ProtocolMessage(cmd, (const _u8*)payload, payloadsize));
+ return _transeiver->sendMessage(message);
+
+ }
+
+ u_result _sendCommandWithResponse(_u8 cmd, _u8 responseType, internal::message_autoptr_t& ansPkt, _u32 timeout = DEFAULT_TIMEOUT, const void* payload = NULL, size_t payloadsize = 0)
+ {
+ u_result ans;
+
+ _data_locker.lock();
+
+ internal::message_autoptr_t message(new internal::ProtocolMessage(cmd, (const _u8*)payload, payloadsize));
+ _disableDataGrabbing();
+ _waiting_packet_type = responseType;
+ _response_waiter.set(false);
+ _data_locker.unlock();
+
+ ans = _transeiver->sendMessage(message);
+
+ if (IS_FAIL(ans)) return ans;
+
+ do {
+ switch (_response_waiter.wait(timeout)) {
+ case rp::hal::Event::EVENT_TIMEOUT:
+ return RESULT_OPERATION_TIMEOUT;
+ case rp::hal::Event::EVENT_OK:
+ _data_locker.lock();
+ ansPkt = _lastAnsPkt;
+ _data_locker.unlock();
+ return RESULT_OK;
+ default:
+ return RESULT_OPERATION_FAIL;
+ }
+ } while (1);
+ }
+
+ public:
+
+ virtual void onHQNodeDecoded(_u64 timestamp_uS, const rplidar_response_measurement_node_hq_t* node)
+ {
+ _scanHolder.pushScanNodeData(timestamp_uS, node);
+ _rawSampleNodeHolder.pushNode(timestamp_uS, node);
+ }
+
+ virtual void onHQNodeScanResetReq() {
+ _scanHolder.rewindCurrentScanData();
+ }
+
+ virtual void onProtocolMessageDecoded(const internal::ProtocolMessage& msg)
+ {
+ internal::message_autoptr_t message = std::make_shared<internal::ProtocolMessage>(msg);
+
+ if (_dataunpacker->onSampleData(message->cmd, message->getDataBuf(), message->getPayloadSize()))
+ {
+ return;
+ }
+
+ if (message->cmd == _waiting_packet_type) {
+ _data_locker.lock();
+ _lastAnsPkt = message;
+ _response_waiter.setResult(message->cmd);
+ _data_locker.unlock();
+ }
+
+
+ }
+ private:
+
+ std::shared_ptr<internal::RPLidarProtocolCodec> _protocolHandler;
+ std::shared_ptr<internal::AsyncTransceiver> _transeiver;
+ std::shared_ptr<internal::LIDARSampleDataUnpacker> _dataunpacker;
+
+ bool _isConnected;
+
+ MotorCtrlSupport _isSupportingMotorCtrl;
+
+
+ rp::hal::Locker _op_locker;
+ rp::hal::Locker _data_locker;
+ rp::hal::Waiter<_u32> _response_waiter;
+
+ ScanDataHolder<sl_lidar_response_measurement_node_hq_t> _scanHolder;
+ RawSampleNodeHolder<sl_lidar_response_measurement_node_hq_t> _rawSampleNodeHolder;
+ _u32 _waiting_packet_type;
+ internal::message_autoptr_t _lastAnsPkt;
+
+ sl_lidar_response_device_info_t _cached_DevInfo;
+ SlamtecLidarTimingDesc _timing_desc;
+
+ };
+
+ Result<ILidarDriver*> createLidarDriver()
+ {
+ return new SlamtecLidarDriver();
+ }
+} \ No newline at end of file