feature:support save lvx file for lidar;

This commit is contained in:
Livox-SDK
2019-05-06 12:33:55 +08:00
parent 2c5b72a589
commit a490a93b20
907 changed files with 10205 additions and 5141 deletions
+13 -7
View File
@@ -59,16 +59,16 @@ char broadcast_code_list[BROADCAST_CODE_LIST_SIZE][kBroadcastCodeSize] = {
"00000000000001"
};*/
void GetLidarData(uint8_t handle, LivoxEthPacket *data, uint32_t data_num) {
void GetLidarData(uint8_t handle, LivoxEthPacket *data, uint32_t data_num, void *client_data) {
static uint32_t receive_packet_count = 0;
if (data) {
++receive_packet_count;
if (0 == (receive_packet_count % 10000)) {
printf("receive packet count %d %d\n", data->id, receive_packet_count);
/** Parsing the timestamp and the point cloud data. */
uint64_t cur_timestamp = *((uint64_t *)(data->timestamp));
LivoxRawPoint *p_point_data = (LivoxRawPoint *)data->data;
/** Parsing the timestamp and the point cloud data. */
uint64_t cur_timestamp = *((uint64_t *)(data->timestamp));
LivoxRawPoint *p_point_data = (LivoxRawPoint *)data->data;
}
printf("receive packet from %d \n", (uint16_t) HubGetLidarHandle(data->slot, data->id));
}
@@ -198,7 +198,7 @@ void OnDeviceBroadcast(const BroadcastDeviceInfo *info) {
uint8_t handle = 0;
result = AddHubToConnect(info->broadcast_code, &handle);
if (result == kStatusSuccess && handle < kMaxLidarCount) {
SetDataCallback(handle, GetLidarData);
SetDataCallback(handle, GetLidarData, NULL);
devices[handle].handle = handle;
devices[handle].device_state = kDeviceStateDisconnect;
}
@@ -221,8 +221,14 @@ int SetProgramOption(int argc, const char *argv[]) {
int optch = 0;
const char *optarg = NULL;
apr_initialize();
apr_pool_create(&mp, NULL);
if (apr_initialize() != APR_SUCCESS) {
return -1;
}
if (apr_pool_create(&mp, NULL) != APR_SUCCESS) {
return -1;
}
rv = apr_getopt_init(&opt, mp, argc, argv);
if (rv != APR_SUCCESS) {
printf("Program options initialization failed.\n");
+8
View File
@@ -0,0 +1,8 @@
cmake_minimum_required(VERSION 3.0)
set(DEMO_NAME hub_lvx_sample)
add_executable(${DEMO_NAME} main.cpp lvx_file.cpp)
target_link_libraries(${DEMO_NAME}
PRIVATE
${PROJECT_NAME}_static
)
+155
View File
@@ -0,0 +1,155 @@
//
// The MIT License (MIT)
//
// Copyright (c) 2019 Livox. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
#include <time.h>
#include <math.h>
#include "lvx_file.h"
#define WRITE_BUFFER_LEN 1024 * 1024
#define MAGIC_CODE (0xac0ea767)
#define PACK_POINT_NUM 100
#define M_PI 3.14159265358979323846
LvxFileHandle::LvxFileHandle() : cur_offset_(0), cur_frame_index_(0) {
}
bool LvxFileHandle::InitLvxFile() {
time_t curtime = time(nullptr);
char filename[30] = { 0 };
tm* local_time = localtime(&curtime);
sprintf(filename, "%d-%02d-%02d_%02d-%02d-%02d.lvx", local_time->tm_year + 1900,
local_time->tm_mon + 1,
local_time->tm_mday,
local_time->tm_hour,
local_time->tm_min,
local_time->tm_sec);
lvx_file_.open(filename, std::ios::out | std::ios::binary);
if (!lvx_file_.is_open()) {
return false;
}
return true;
}
void LvxFileHandle::InitLvxFileHeader() {
LvxFileHeader lvx_file_header = { 0 };
std::unique_ptr<char[]> write_buffer(new char[WRITE_BUFFER_LEN]);
std::string signature = "livox_tech";
memcpy(lvx_file_header.signature, signature.c_str(), signature.size());
lvx_file_header.version[0] = 1;
lvx_file_header.version[1] = 0;
lvx_file_header.version[2] = 0;
lvx_file_header.version[3] = 0;
lvx_file_header.magic_code = MAGIC_CODE;
memcpy(write_buffer.get() + cur_offset_, (void *)&lvx_file_header, sizeof(LvxFileHeader));
cur_offset_ += sizeof(LvxFileHeader);
uint8_t device_count = static_cast<uint8_t>(device_info_list_.size());
memcpy(write_buffer.get() + cur_offset_, (void *)&device_count, sizeof(uint8_t));
cur_offset_ += sizeof(uint8_t);
for (int i = 0; i < device_count; i++) {
memcpy(write_buffer.get() + cur_offset_, (void *)&device_info_list_[i], sizeof(LvxDeviceInfo));
cur_offset_ += sizeof(LvxDeviceInfo);
}
lvx_file_.write((char *)write_buffer.get(), cur_offset_);
}
void LvxFileHandle::SaveFrameToLvxFile(std::list<LvxBasePackDetail> &point_packet_list_temp) {
uint64_t cur_pos = 0;
FrameHeader frame_header = { 0 };
std::unique_ptr<char[]> write_buffer(new char[WRITE_BUFFER_LEN]);
int pack_num = point_packet_list_temp.size();
frame_header.current_offset = cur_offset_;
frame_header.next_offset = cur_offset_ + (int64_t)pack_num * sizeof(LvxBasePackDetail) + sizeof(FrameHeader);
frame_header.package_count = pack_num;
frame_header.frame_index = cur_frame_index_;
memcpy(write_buffer.get() + cur_pos, (void*)&frame_header, sizeof(FrameHeader));
cur_pos += sizeof(FrameHeader);
auto iter = point_packet_list_temp.begin();
for (; iter != point_packet_list_temp.end(); iter++) {
if (cur_pos + sizeof(LvxBasePackDetail) >= WRITE_BUFFER_LEN) {
lvx_file_.write((char*)write_buffer.get(), cur_pos);
cur_pos = 0;
memcpy(write_buffer.get() + cur_pos, (void*)&(*iter), sizeof(LvxBasePackDetail));
cur_pos += sizeof(LvxBasePackDetail);
}
else {
memcpy(write_buffer.get() + cur_pos, (void*)&(*iter), sizeof(LvxBasePackDetail));
cur_pos += sizeof(LvxBasePackDetail);
}
}
lvx_file_.write((char*)write_buffer.get(), cur_pos);
cur_offset_ = frame_header.next_offset;
cur_frame_index_++;
}
void LvxFileHandle::CloseLvxFile() {
if (lvx_file_.is_open())
lvx_file_.close();
}
void LvxFileHandle::BasePointsHandle(LivoxEthPacket *data, LvxBasePackDetail &packet) {
packet.version = data->version;
packet.port_id = data->slot;
packet.lidar_index = data->id;
packet.rsvd = data->rsvd;
packet.error_code = data->err_code;
packet.timestamp_type = data->timestamp_type;
packet.data_type = data->data_type;
memcpy(packet.timestamp, data->timestamp, 8 * sizeof(uint8_t));
if (packet.data_type == 0) {
LivoxRawPoint tmp[PACK_POINT_NUM];
memcpy(tmp, (void *)data->data, PACK_POINT_NUM * sizeof(LivoxRawPoint));
for (int i = 0; i < PACK_POINT_NUM; i++) {
packet.point[i].x = static_cast<float>(tmp[i].x / 1000.0);
packet.point[i].y = static_cast<float>(tmp[i].y / 1000.0);
packet.point[i].z = static_cast<float>(tmp[i].z / 1000.0);
packet.point[i].reflectivity = tmp[i].reflectivity;
}
}
else if (packet.data_type == 1) {
LivoxSpherPoint tmp[PACK_POINT_NUM];
memcpy(tmp, (void *)data->data, PACK_POINT_NUM * sizeof(LivoxSpherPoint));
for (int i = 0; i < PACK_POINT_NUM; i++) {
packet.point[i].x = static_cast<float>(tmp[i].depth / 1000.0);
packet.point[i].y = static_cast<float>(tmp[i].theta);
packet.point[i].z = static_cast<float>(tmp[i].phi);
packet.point[i].reflectivity = tmp[i].reflectivity;
LivoxPoint temp = { packet.point[i].x * sin(packet.point[i].y) * cos(packet.point[i].z), packet.point[i].x * sin(packet.point[i].y) * sin(packet.point[i].z), packet.point[i].x * cos(packet.point[i].y) };;
packet.point[i] = temp;
}
}
}
+110
View File
@@ -0,0 +1,110 @@
//
// The MIT License (MIT)
//
// Copyright (c) 2019 Livox. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
#include <apr_thread_cond.h>
#include <apr_thread_mutex.h>
#include <apr_thread_proc.h>
#include <condition_variable>
#include <memory>
#include <fstream>
#include <list>
#include <vector>
#include <mutex>
#include "livox_sdk.h"
typedef enum {
kDeviceStateDisconnect = 0,
kDeviceStateConnect = 1,
kDeviceStateSampling = 2,
} DeviceState;
typedef struct {
uint8_t handle;
DeviceState device_state;
DeviceInfo info;
} DeviceItem;
#pragma pack(1)
typedef struct {
uint8_t signature[16];
uint8_t version[4];
uint32_t magic_code;
}LvxFileHeader;
typedef struct {
uint8_t lidar_broadcast_code[16];
uint8_t hub_broadcast_code[16];
uint8_t device_index;
uint8_t device_type;
float roll;
float pitch;
float yaw;
float x;
float y;
float z;
} LvxDeviceInfo;
typedef struct {
uint8_t device_index;
uint8_t version;
uint8_t port_id;
uint8_t lidar_index;
uint8_t rsvd;
uint32_t error_code;
uint8_t timestamp_type;
uint8_t data_type;
uint8_t timestamp[8];
LivoxPoint point[100];
}LvxBasePackDetail;
typedef struct {
uint64_t current_offset;
uint64_t next_offset;
uint64_t frame_index;
uint64_t package_count;
}FrameHeader;
#pragma pack()
class LvxFileHandle {
public:
LvxFileHandle();
bool InitLvxFile();
void InitLvxFileHeader();
void SaveFrameToLvxFile(std::list<LvxBasePackDetail> &point_packet_list_temp);
void CloseLvxFile();
void AddDeviceInfo(LvxDeviceInfo &info) { device_info_list_.push_back(info); };
int GetDeviceInfoListSize() { return device_info_list_.size(); }
void BasePointsHandle(LivoxEthPacket *data, LvxBasePackDetail &packet);
private:
std::ofstream lvx_file_;
std::vector<LvxDeviceInfo> device_info_list_;
uint32_t cur_frame_index_;
uint64_t cur_offset_;
};
+354
View File
@@ -0,0 +1,354 @@
//
// The MIT License (MIT)
//
// Copyright (c) 2019 Livox. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
#ifdef WIN32
#include <windows.h>
#else
#include <unistd.h>
#endif
#include <apr_general.h>
#include <apr_getopt.h>
#include <string.h>
#include <algorithm>
#include "lvx_file.h"
DeviceItem devices[kMaxLidarCount];
LvxFileHandle lvx_file_handler;
std::list<LvxBasePackDetail> point_packet_list;
std::condition_variable condition_variable;
std::mutex mtx;
int lidar_units_index[32];
int lvx_file_save_time = 10;
bool is_finish_extrinsic_parameter = false;
#define FRAME_RATE 20
/** Connect the first broadcast hub in default and connect specific device when use program options or broadcast_code_list is not empty. */
std::vector<std::string> broadcast_code_list = {
//"000000000000001"
};
/** Receiving point cloud data from Livox Hub. */
void GetHubData(uint8_t handle, LivoxEthPacket *data, uint32_t data_num, void *client_data) {
if (data) {
if (is_finish_extrinsic_parameter) {
std::lock_guard<std::mutex> lock(mtx);
LvxBasePackDetail packet;
packet.device_index = lidar_units_index[HubGetLidarHandle(data->slot, data->id)];
lvx_file_handler.BasePointsHandle(data, packet);
point_packet_list.push_back(packet);
if (point_packet_list.size() % (50 * broadcast_code_list.size()) == 0) {
condition_variable.notify_one();
}
}
}
}
/** Callback function of starting sampling. */
void OnSampleCallback(uint8_t status, uint8_t handle, uint8_t response, void *data) {
printf("OnSampleCallback statue %d handle %d response %d \n", status, handle, response);
if (status == kStatusSuccess) {
if (response != 0) {
devices[handle].device_state = kDeviceStateConnect;
}
} else if (status == kStatusTimeout) {
devices[handle].device_state = kDeviceStateConnect;
}
}
/** Callback function of stopping sampling. */
void OnStopSampleCallback(uint8_t status, uint8_t handle, uint8_t response, void *data) {
}
/** Callback function of get LiDAR units' extrinsic parameter. */
void OnGetLidarUnitsExtrinsicParameter(uint8_t status, uint8_t handle, HubGetExtrinsicParameterResponse *response, void *data) {
if (status == kStatusSuccess) {
if (response != 0) {
printf("OnGetLidarUnitsExtrinsicParameter statue %d handle %d response %d \n", status, handle, response->ret_code);
std::lock_guard<std::mutex> lock(mtx);
LvxDeviceInfo lidar_info;
for (int i = 0; i < response->count; i++) {
ExtrinsicParameterResponseItem temp;
memcpy(&temp, (void *)(response->parameter_list + i), sizeof(ExtrinsicParameterResponseItem));
strncpy((char *)lidar_info.lidar_broadcast_code, temp.broadcast_code, kBroadcastCodeSize);
strncpy((char *)lidar_info.hub_broadcast_code, broadcast_code_list[0].c_str(), kBroadcastCodeSize);
std::unique_ptr<DeviceInfo[]> device_list(new DeviceInfo[kMaxLidarCount]);
std::unique_ptr<uint8_t> size(new uint8_t);
GetConnectedDevices(device_list.get(), size.get());
for (int j = 0; j < kMaxLidarCount; j++) {
if (strncmp(device_list[j].broadcast_code, temp.broadcast_code, kBroadcastCodeSize) == 0) {
lidar_units_index[device_list[j].handle] = i;
lidar_info.device_index = i;
break;
}
}
lidar_info.device_type = devices[handle].info.type;
lidar_info.pitch = temp.pitch;
lidar_info.roll = temp.roll;
lidar_info.yaw = temp.yaw;
lidar_info.x = static_cast<float>(temp.x / 1000.0);
lidar_info.y = static_cast<float>(temp.y / 1000.0);
lidar_info.z = static_cast<float>(temp.z / 1000.0);
lvx_file_handler.AddDeviceInfo(lidar_info);
}
is_finish_extrinsic_parameter = true;
condition_variable.notify_one();
}
}
else if (status == kStatusTimeout) {
printf("GetLidarUnitsExtrinsicParameter timeout! \n");
}
}
void OnHubLidarInfo(uint8_t status, uint8_t handle, HubQueryLidarInformationResponse *response, void *client_data) {
if (status != kStatusSuccess) {
printf("Device Query Informations Failed %d\n", status);
}
if (response) {
int i = 0;
for (i = 0; i < response->count; ++i) {
printf("Hub Lidar Info broadcast code %s id %d slot %d \n ",
response->device_info_list[i].broadcast_code,
response->device_info_list[i].id,
response->device_info_list[i].slot);
}
}
}
/** Callback function of changing of device state. */
void OnDeviceChange(const DeviceInfo *info, DeviceEvent type) {
if (info == nullptr) {
return;
}
printf("OnDeviceChange broadcast code %s update type %d\n", info->broadcast_code, type);
uint8_t handle = info->handle;
if (handle >= kMaxLidarCount) {
return;
}
if (type == kEventConnect) {
HubQueryLidarInformation(OnHubLidarInfo, nullptr);
if (devices[handle].device_state == kDeviceStateDisconnect) {
devices[handle].device_state = kDeviceStateConnect;
devices[handle].info = *info;
}
} else if (type == kEventDisconnect) {
devices[handle].device_state = kDeviceStateDisconnect;
} else if (type == kEventStateChange) {
devices[handle].info = *info;
}
if (devices[handle].device_state == kDeviceStateConnect) {
printf("Device State error_code %d\n", devices[handle].info.status.status_code);
printf("Device State working state %d\n", devices[handle].info.state);
printf("Device feature %d\n", devices[handle].info.feature);
if (devices[handle].info.state == kLidarStateNormal) {
if (devices[handle].info.type == kDeviceTypeHub) {
HubGetExtrinsicParameter(OnGetLidarUnitsExtrinsicParameter, nullptr);
HubStartSampling(OnSampleCallback, nullptr);
devices[handle].device_state = kDeviceStateSampling;
}
}
}
}
/** Callback function when broadcast message received.
* You need to add listening device broadcast code and set the point cloud data callback in this function.
*/
void OnDeviceBroadcast(const BroadcastDeviceInfo *info) {
if (info == nullptr) {
return;
}
printf("Receive Broadcast Code %s\n", info->broadcast_code);
if (broadcast_code_list.size() > 0) {
bool found = false;
uint8_t i = 0;
for (i = 0; i < broadcast_code_list.size(); ++i) {
if (strncmp(info->broadcast_code, broadcast_code_list[i].c_str(), kBroadcastCodeSize) == 0) {
found = true;
break;
}
}
if (!found) {
return;
}
}
else {
broadcast_code_list.push_back(info->broadcast_code);
return;
}
bool result = false;
uint8_t handle = 0;
result = AddHubToConnect(info->broadcast_code, &handle);
if (result == kStatusSuccess) {
SetDataCallback(handle, GetHubData, nullptr);
devices[handle].handle = handle;
devices[handle].device_state = kDeviceStateDisconnect;
}
}
/** Set the program options.
* You can input the registered device broadcast code and decide whether to save the log file.
*/
int SetProgramOption(int argc, const char *argv[]) {
apr_status_t rv;
apr_pool_t *mp = nullptr;
static const apr_getopt_option_t opt_option[] = {
/** Long-option, short-option, has-arg flag, description */
{ "code", 'c', 1, "Register device broadcast code" },
{ "log", 'l', 0, "Save the log file" },
{ "time", 't', 1, "Time to save point cloud to the lvx file" },
{ "help", 'h', 0, "Show help" },
{ nullptr, 0, 0, nullptr },
};
apr_getopt_t *opt = nullptr;
int optch = 0;
const char *optarg = nullptr;
if (apr_initialize() != APR_SUCCESS) {
return -1;
}
if (apr_pool_create(&mp, NULL) != APR_SUCCESS) {
return -1;
}
rv = apr_getopt_init(&opt, mp, argc, argv);
if (rv != APR_SUCCESS) {
printf("Program options initialization failed.\n");
return -1;
}
/** Parse the all options based on opt_option[] */
bool is_help = false;
while ((rv = apr_getopt_long(opt, opt_option, &optch, &optarg)) == APR_SUCCESS) {
switch (optch) {
case 'c': {
printf("Register broadcast code: %s\n", optarg);
broadcast_code_list.push_back(optarg);
break;
}
case 'l': {
printf("Save the log file.\n");
SaveLoggerFile();
break;
}
case 't': {
printf("Time to save point cloud to the lvx file:%s.\n", optarg);
lvx_file_save_time = atoi(optarg);
break;
}
case 'h': {
printf(
" [-c] Register device broadcast code\n"
" [-l] Save the log file\n"
" [-t] Time to save point cloud to the lvx file\n"
" [-h] Show help\n"
);
is_help = true;
break;
}
}
}
if (rv != APR_EOF) {
printf("Invalid options.\n");
}
apr_pool_destroy(mp);
mp = nullptr;
if (is_help)
return 1;
return 0;
}
int main(int argc, const char *argv[]) {
/** Set the program options. */
if (SetProgramOption(argc, argv))
return 0;
printf("Livox SDK initializing.\n");
/** Initialize Livox-SDK. */
if (!Init()) {
return -1;
}
printf("Livox SDK has been initialized.\n");
LivoxSdkVersion _sdkversion;
GetLivoxSdkVersion(&_sdkversion);
printf("Livox SDK version %d.%d.%d .\n", _sdkversion.major, _sdkversion.minor, _sdkversion.patch);
memset(devices, 0, sizeof(devices));
/** Set the callback function receiving broadcast message from Livox LiDAR. */
SetBroadcastCallback(OnDeviceBroadcast);
/** Set the callback function called when device state change,
* which means connection/disconnection and changing of LiDAR state.
*/
SetDeviceStateUpdateCallback(OnDeviceChange);
/** Start the device discovering routine. */
if (!Start()) {
Uninit();
return -1;
}
printf("Start discovering device.\n");
{
std::unique_lock<std::mutex> lock(mtx);
condition_variable.wait(lock);
}
printf("Start initialize lvx file.\n");
if (!lvx_file_handler.InitLvxFile()) {
Uninit();
return -1;
}
lvx_file_handler.InitLvxFileHeader();
int i = 0;
for (i = 0; i < lvx_file_save_time * FRAME_RATE; ++i) {
std::list<LvxBasePackDetail> point_packet_list_temp;
{
std::unique_lock<std::mutex> lock(mtx);
condition_variable.wait(lock);
point_packet_list_temp.swap(point_packet_list);
}
printf("Finish save %d frame to lvx file.\n", i);
lvx_file_handler.SaveFrameToLvxFile(point_packet_list_temp);
}
lvx_file_handler.CloseLvxFile();
HubStopSampling(OnStopSampleCallback, NULL);
printf("stop sample\n");
Uninit();
}
+13 -7
View File
@@ -63,15 +63,15 @@ char broadcast_code_list[kMaxLidarCount][kBroadcastCodeSize] = {
};*/
/** Receiving point cloud data from Livox LiDAR. */
void GetLidarData(uint8_t handle, LivoxEthPacket *data, uint32_t data_num) {
void GetLidarData(uint8_t handle, LivoxEthPacket *data, uint32_t data_num, void *client_data) {
if (data) {
data_recveive_count[handle] += data_num;
if (data_recveive_count[handle] % 10000 == 0) {
printf("receive packet count %d %d\n", handle, data_recveive_count[handle]);
/** Parsing the timestamp and the point cloud data. */
uint64_t cur_timestamp = *((uint64_t *)(data->timestamp));
LivoxRawPoint *p_point_data = (LivoxRawPoint *)data->data;
/** Parsing the timestamp and the point cloud data. */
uint64_t cur_timestamp = *((uint64_t *)(data->timestamp));
LivoxRawPoint *p_point_data = (LivoxRawPoint *)data->data;
}
}
}
@@ -172,7 +172,7 @@ void OnDeviceBroadcast(const BroadcastDeviceInfo *info) {
result = AddLidarToConnect(info->broadcast_code, &handle);
if (result == kStatusSuccess) {
/** Set the point cloud data for a specific Livox LiDAR. */
SetDataCallback(handle, GetLidarData);
SetDataCallback(handle, GetLidarData, NULL);
devices[handle].handle = handle;
devices[handle].device_state = kDeviceStateDisconnect;
}
@@ -195,8 +195,14 @@ int SetProgramOption(int argc, const char *argv[]) {
int optch = 0;
const char *optarg = NULL;
apr_initialize();
apr_pool_create(&mp, NULL);
if (apr_initialize() != APR_SUCCESS) {
return -1;
}
if (apr_pool_create(&mp, NULL) != APR_SUCCESS) {
return -1;
}
rv = apr_getopt_init(&opt, mp, argc, argv);
if (rv != APR_SUCCESS) {
printf("Program options initialization failed.\n");
+8
View File
@@ -0,0 +1,8 @@
cmake_minimum_required(VERSION 3.0)
set(DEMO_NAME lidar_lvx_sample)
add_executable(${DEMO_NAME} main.cpp lvx_file.cpp)
target_link_libraries(${DEMO_NAME}
PRIVATE
${PROJECT_NAME}_static
)
+208
View File
@@ -0,0 +1,208 @@
//
// The MIT License (MIT)
//
// Copyright (c) 2019 Livox. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
#include <time.h>
#include <math.h>
#include "lvx_file.h"
#include "third_party/rapidxml/rapidxml.hpp"
#include "third_party/rapidxml/rapidxml_utils.hpp"
#define WRITE_BUFFER_LEN 1024 * 1024
#define MAGIC_CODE (0xac0ea767)
#define PACK_POINT_NUM 100
#define M_PI 3.14159265358979323846
LvxFileHandle::LvxFileHandle() : cur_offset_(0), cur_frame_index_(0) {
}
bool LvxFileHandle::InitLvxFile() {
time_t curtime = time(nullptr);
char filename[30] = { 0 };
tm* local_time = localtime(&curtime);
sprintf(filename, "%d-%02d-%02d_%02d-%02d-%02d.lvx", local_time->tm_year + 1900,
local_time->tm_mon + 1,
local_time->tm_mday,
local_time->tm_hour,
local_time->tm_min,
local_time->tm_sec);
lvx_file_.open(filename, std::ios::out | std::ios::binary);
if (!lvx_file_.is_open()) {
return false;
}
return true;
}
void LvxFileHandle::InitLvxFileHeader() {
LvxFileHeader lvx_file_header = { 0 };
std::unique_ptr<char[]> write_buffer(new char[WRITE_BUFFER_LEN]);
std::string signature = "livox_tech";
memcpy(lvx_file_header.signature, signature.c_str(), signature.size());
lvx_file_header.version[0] = 1;
lvx_file_header.version[1] = 0;
lvx_file_header.version[2] = 0;
lvx_file_header.version[3] = 0;
lvx_file_header.magic_code = MAGIC_CODE;
memcpy(write_buffer.get() + cur_offset_, (void *)&lvx_file_header, sizeof(LvxFileHeader));
cur_offset_ += sizeof(LvxFileHeader);
uint8_t device_count = static_cast<uint8_t>(device_info_list_.size());
memcpy(write_buffer.get() + cur_offset_, (void *)&device_count, sizeof(uint8_t));
cur_offset_ += sizeof(uint8_t);
for (int i = 0; i < device_count; i++) {
memcpy(write_buffer.get() + cur_offset_, (void *)&device_info_list_[i], sizeof(LvxDeviceInfo));
cur_offset_ += sizeof(LvxDeviceInfo);
}
lvx_file_.write((char *)write_buffer.get(), cur_offset_);
}
void LvxFileHandle::SaveFrameToLvxFile(std::list<LvxBasePackDetail> &point_packet_list_temp) {
uint64_t cur_pos = 0;
FrameHeader frame_header = { 0 };
std::unique_ptr<char[]> write_buffer(new char[WRITE_BUFFER_LEN]);
int pack_num = point_packet_list_temp.size();
frame_header.current_offset = cur_offset_;
frame_header.next_offset = cur_offset_ + (int64_t)pack_num * sizeof(LvxBasePackDetail) + sizeof(FrameHeader);
frame_header.package_count = pack_num;
frame_header.frame_index = cur_frame_index_;
memcpy(write_buffer.get() + cur_pos, (void*)&frame_header, sizeof(FrameHeader));
cur_pos += sizeof(FrameHeader);
auto iter = point_packet_list_temp.begin();
for (; iter != point_packet_list_temp.end(); iter++) {
if (cur_pos + sizeof(LvxBasePackDetail) >= WRITE_BUFFER_LEN) {
lvx_file_.write((char*)write_buffer.get(), cur_pos);
cur_pos = 0;
memcpy(write_buffer.get() + cur_pos, (void*)&(*iter), sizeof(LvxBasePackDetail));
cur_pos += sizeof(LvxBasePackDetail);
}
else {
memcpy(write_buffer.get() + cur_pos, (void*)&(*iter), sizeof(LvxBasePackDetail));
cur_pos += sizeof(LvxBasePackDetail);
}
}
lvx_file_.write((char*)write_buffer.get(), cur_pos);
cur_offset_ = frame_header.next_offset;
cur_frame_index_++;
}
void LvxFileHandle::CloseLvxFile() {
if (lvx_file_.is_open())
lvx_file_.close();
}
void LvxFileHandle::BasePointsHandle(LivoxEthPacket *data, LvxBasePackDetail &packet) {
packet.version = data->version;
packet.port_id = data->slot;
packet.lidar_index = data->id;
packet.rsvd = data->rsvd;
packet.error_code = data->err_code;
packet.timestamp_type = data->timestamp_type;
packet.data_type = data->data_type;
memcpy(packet.timestamp, data->timestamp, 8 * sizeof(uint8_t));
if (packet.data_type == 0) {
LivoxRawPoint tmp[PACK_POINT_NUM];
memcpy(tmp, (void *)data->data, PACK_POINT_NUM * sizeof(LivoxRawPoint));
for (int i = 0; i < PACK_POINT_NUM; i++) {
packet.point[i].x = static_cast<float>(tmp[i].x / 1000.0);
packet.point[i].y = static_cast<float>(tmp[i].y / 1000.0);
packet.point[i].z = static_cast<float>(tmp[i].z / 1000.0);
packet.point[i].reflectivity = tmp[i].reflectivity;
}
}
else if (packet.data_type == 1) {
LivoxSpherPoint tmp[PACK_POINT_NUM];
memcpy(tmp, (void *)data->data, PACK_POINT_NUM * sizeof(LivoxSpherPoint));
for (int i = 0; i < PACK_POINT_NUM; i++) {
packet.point[i].x = static_cast<float>(tmp[i].depth / 1000.0);
packet.point[i].y = static_cast<float>(tmp[i].theta);
packet.point[i].z = static_cast<float>(tmp[i].phi);
packet.point[i].reflectivity = tmp[i].reflectivity;
}
}
}
void LvxFileHandle::CalcExtrinsicPoints(LvxBasePackDetail &packet) {
LvxDeviceInfo info = device_info_list_[packet.device_index];
info.roll = static_cast<float>(info.roll * M_PI / 180.0);
info.pitch = static_cast<float>(info.pitch * M_PI / 180.0);
info.yaw = static_cast<float>(info.yaw * M_PI / 180.0);
float rotate[3][3] = { { cos(info.pitch) * cos(info.yaw), sin(info.roll) * sin(info.pitch) * cos(info.yaw) - cos(info.roll) * sin(info.yaw), cos(info.roll) * sin(info.pitch) * cos(info.yaw) + sin(info.roll) * sin(info.yaw) },
{ cos(info.pitch) * sin(info.yaw), sin(info.roll) * sin(info.pitch) * sin(info.yaw) + cos(info.roll) * cos(info.yaw), cos(info.roll) * sin(info.pitch) * sin(info.yaw) - sin(info.roll) * cos(info.yaw) },
{ -sin(info.pitch), sin(info.roll) * cos(info.pitch), cos(info.roll) * cos(info.pitch) } };
float trans[3] = { static_cast<float>(info.x), static_cast<float>(info.y), static_cast<float>(info.z) };
if (packet.data_type == 0) {
for (int i = 0; i < PACK_POINT_NUM; i++) {
LivoxPoint temp = packet.point[i];
packet.point[i].x = temp.x * rotate[0][0] + temp.y * rotate[0][1] + temp.z * rotate[0][2] + trans[0];
packet.point[i].y = temp.x * rotate[1][0] + temp.y * rotate[1][1] + temp.z * rotate[1][2] + trans[1];
packet.point[i].z = temp.x * rotate[2][0] + temp.y * rotate[2][1] + temp.z * rotate[2][2] + trans[2];
}
}
else {
for (int i = 0; i < PACK_POINT_NUM; i++) {
LivoxPoint temp = { packet.point[i].x * sin(packet.point[i].y) * cos(packet.point[i].z), packet.point[i].x * sin(packet.point[i].y) * sin(packet.point[i].z), packet.point[i].x * cos(packet.point[i].y) };;
packet.point[i].x = temp.x * rotate[0][0] + temp.y * rotate[0][1] + temp.z * rotate[0][2] + trans[0];
packet.point[i].y = temp.x * rotate[1][0] + temp.y * rotate[1][1] + temp.z * rotate[1][2] + trans[1];
packet.point[i].z = temp.x * rotate[2][0] + temp.y * rotate[2][1] + temp.z * rotate[2][2] + trans[2];
}
}
}
void ParseExtrinsicXml(DeviceItem &item, LvxDeviceInfo &info) {
rapidxml::file<> extrinsic_param("extrinsic.xml");
rapidxml::xml_document<> doc;
doc.parse<0>(extrinsic_param.data());
rapidxml::xml_node<>* root = doc.first_node();
if ("Livox" == (std::string)root->name()) {
for (rapidxml::xml_node<>* device = root->first_node(); device; device = device->next_sibling()) {
if ("Device" == (std::string)device->name() && (strncmp(item.info.broadcast_code, device->value(), kBroadcastCodeSize) == 0)) {
memcpy(info.lidar_broadcast_code, device->value(), kBroadcastCodeSize);
memset(info.hub_broadcast_code, 0, kBroadcastCodeSize);
info.device_type = item.info.type;
info.device_index = item.handle;
for (rapidxml::xml_attribute<>* param = device->first_attribute(); param; param = param->next_attribute()) {
if ("roll" == (std::string)param->name()) info.roll = static_cast<float>(atof(param->value()));
if ("pitch" == (std::string)param->name()) info.pitch = static_cast<float>(atof(param->value()));
if ("yaw" == (std::string)param->name()) info.yaw = static_cast<float>(atof(param->value()));
if ("x" == (std::string)param->name()) info.x = static_cast<float>(atof(param->value()));
if ("y" == (std::string)param->name()) info.y = static_cast<float>(atof(param->value()));
if ("z" == (std::string)param->name()) info.z = static_cast<float>(atof(param->value()));
}
}
}
}
}
+113
View File
@@ -0,0 +1,113 @@
//
// The MIT License (MIT)
//
// Copyright (c) 2019 Livox. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
#include <apr_thread_cond.h>
#include <apr_thread_mutex.h>
#include <apr_thread_proc.h>
#include <condition_variable>
#include <memory>
#include <fstream>
#include <list>
#include <vector>
#include <mutex>
#include "livox_sdk.h"
typedef enum {
kDeviceStateDisconnect = 0,
kDeviceStateConnect = 1,
kDeviceStateSampling = 2,
} DeviceState;
typedef struct {
uint8_t handle;
DeviceState device_state;
DeviceInfo info;
} DeviceItem;
#pragma pack(1)
typedef struct {
uint8_t signature[16];
uint8_t version[4];
uint32_t magic_code;
}LvxFileHeader;
typedef struct {
uint8_t lidar_broadcast_code[16];
uint8_t hub_broadcast_code[16];
uint8_t device_index;
uint8_t device_type;
float roll;
float pitch;
float yaw;
float x;
float y;
float z;
} LvxDeviceInfo;
typedef struct {
uint8_t device_index;
uint8_t version;
uint8_t port_id;
uint8_t lidar_index;
uint8_t rsvd;
uint32_t error_code;
uint8_t timestamp_type;
uint8_t data_type;
uint8_t timestamp[8];
LivoxPoint point[100];
}LvxBasePackDetail;
typedef struct {
uint64_t current_offset;
uint64_t next_offset;
uint64_t frame_index;
uint64_t package_count;
}FrameHeader;
#pragma pack()
class LvxFileHandle {
public:
LvxFileHandle();
bool InitLvxFile();
void InitLvxFileHeader();
void SaveFrameToLvxFile(std::list<LvxBasePackDetail> &point_packet_list_temp);
void CloseLvxFile();
void AddDeviceInfo(LvxDeviceInfo &info) { device_info_list_.push_back(info); };
int GetDeviceInfoListSize() { return device_info_list_.size(); }
void BasePointsHandle(LivoxEthPacket *data, LvxBasePackDetail &packet);
void CalcExtrinsicPoints(LvxBasePackDetail &packet);
private:
std::ofstream lvx_file_;
std::vector<LvxDeviceInfo> device_info_list_;
uint32_t cur_frame_index_;
uint64_t cur_offset_;
};
void ParseExtrinsicXml(DeviceItem &item, LvxDeviceInfo &info);
+395
View File
@@ -0,0 +1,395 @@
//
// The MIT License (MIT)
//
// Copyright (c) 2019 Livox. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
#ifdef WIN32
#include <windows.h>
#else
#include <unistd.h>
#endif
#include <apr_general.h>
#include <apr_getopt.h>
#include <algorithm>
#include <string.h>
#include "lvx_file.h"
DeviceItem devices[kMaxLidarCount];
LvxFileHandle lvx_file_handler;
std::list<LvxBasePackDetail> point_packet_list;
std::vector<std::string> broadcast_code_rev;
std::condition_variable condition_variable;
std::mutex mtx;
int lvx_file_save_time = 10;
bool is_finish_extrinsic_parameter = false;
bool is_read_extrinsic_from_xml = false;
#define FRAME_RATE 20
/** Connect all the broadcast device in default and connect specific device when use program options or broadcast_code_list is not empty. */
std::vector<std::string> broadcast_code_list = {
//"000000000000002",
//"000000000000003",
//"000000000000004"
};
/** Receiving point cloud data from Livox LiDAR. */
void GetLidarData(uint8_t handle, LivoxEthPacket *data, uint32_t data_num, void *client_data) {
if (data) {
if (handle < broadcast_code_list.size() && is_finish_extrinsic_parameter) {
std::lock_guard<std::mutex> lock(mtx);
LvxBasePackDetail packet;
packet.device_index = handle;
lvx_file_handler.BasePointsHandle(data, packet);
lvx_file_handler.CalcExtrinsicPoints(packet);
point_packet_list.push_back(packet);
if (point_packet_list.size() % (50 * broadcast_code_list.size()) == 0) {
condition_variable.notify_one();
}
}
}
}
/** Callback function of starting sampling. */
void OnSampleCallback(uint8_t status, uint8_t handle, uint8_t response, void *data) {
printf("OnSampleCallback statue %d handle %d response %d \n", status, handle, response);
if (status == kStatusSuccess) {
if (response != 0) {
devices[handle].device_state = kDeviceStateConnect;
}
} else if (status == kStatusTimeout) {
devices[handle].device_state = kDeviceStateConnect;
}
}
/** Callback function of stopping sampling. */
void OnStopSampleCallback(uint8_t status, uint8_t handle, uint8_t response, void *data) {
}
/** Callback function of get LiDARs' extrinsic parameter. */
void OnGetLidarExtrinsicParameter(uint8_t status, uint8_t handle, LidarGetExtrinsicParameterResponse *response, void *data) {
if (status == kStatusSuccess) {
if (response != 0) {
printf("OnGetLidarExtrinsicParameter statue %d handle %d response %d \n", status, handle, response->ret_code);
std::lock_guard<std::mutex> lock(mtx);
LvxDeviceInfo lidar_info;
strncpy((char *)lidar_info.lidar_broadcast_code, devices[handle].info.broadcast_code, kBroadcastCodeSize);
memset(lidar_info.hub_broadcast_code, 0, kBroadcastCodeSize);
lidar_info.device_index = handle;
lidar_info.device_type = devices[handle].info.type;
lidar_info.pitch = response->pitch;
lidar_info.roll = response->roll;
lidar_info.yaw = response->yaw;
lidar_info.x = static_cast<float>(response->x / 1000.0);
lidar_info.y = static_cast<float>(response->y / 1000.0);
lidar_info.z = static_cast<float>(response->z / 1000.0);
lvx_file_handler.AddDeviceInfo(lidar_info);
if (lvx_file_handler.GetDeviceInfoListSize() == broadcast_code_list.size()) {
is_finish_extrinsic_parameter = true;
condition_variable.notify_one();
}
}
}
else if (status == kStatusTimeout) {
printf("GetLidarExtrinsicParameter timeout! \n");
}
}
/** Get LiDARs' extrinsic parameter from file named "extrinsic.xml". */
void LidarGetExtrinsicFromXml(uint8_t handle) {
LvxDeviceInfo lidar_info;
ParseExtrinsicXml(devices[handle], lidar_info);
lvx_file_handler.AddDeviceInfo(lidar_info);
if (lvx_file_handler.GetDeviceInfoListSize() == broadcast_code_list.size()) {
is_finish_extrinsic_parameter = true;
condition_variable.notify_one();
}
}
/** Query the firmware version of Livox LiDAR. */
void OnDeviceInformation(uint8_t status, uint8_t handle, DeviceInformationResponse *ack, void *data) {
if (status != kStatusSuccess) {
printf("Device Query Informations Failed %d\n", status);
}
if (ack) {
printf("firm ver: %d.%d.%d.%d\n",
ack->firmware_version[0],
ack->firmware_version[1],
ack->firmware_version[2],
ack->firmware_version[3]);
}
}
/** Callback function of changing of device state. */
void OnDeviceChange(const DeviceInfo *info, DeviceEvent type) {
if (info == nullptr) {
return;
}
printf("OnDeviceChange broadcast code %s update type %d\n", info->broadcast_code, type);
uint8_t handle = info->handle;
if (handle >= kMaxLidarCount) {
return;
}
if (type == kEventConnect) {
QueryDeviceInformation(handle, OnDeviceInformation, nullptr);
if (devices[handle].device_state == kDeviceStateDisconnect) {
devices[handle].device_state = kDeviceStateConnect;
devices[handle].info = *info;
}
} else if (type == kEventDisconnect) {
devices[handle].device_state = kDeviceStateDisconnect;
} else if (type == kEventStateChange) {
devices[handle].info = *info;
}
if (devices[handle].device_state == kDeviceStateConnect) {
printf("Device State error_code %d\n", devices[handle].info.status.status_code);
printf("Device State working state %d\n", devices[handle].info.state);
printf("Device feature %d\n", devices[handle].info.feature);
if (devices[handle].info.state == kLidarStateNormal) {
if (devices[handle].info.type != kDeviceTypeHub) {
if (!is_read_extrinsic_from_xml) {
LidarGetExtrinsicParameter(handle, OnGetLidarExtrinsicParameter, nullptr);
}
else {
LidarGetExtrinsicFromXml(handle);
}
LidarStartSampling(handle, OnSampleCallback, nullptr);
devices[handle].device_state = kDeviceStateSampling;
}
}
}
}
/** Callback function when broadcast message received.
* You need to add listening device broadcast code and set the point cloud data callback in this function.
*/
void OnDeviceBroadcast(const BroadcastDeviceInfo *info) {
if (info == nullptr) {
return;
}
printf("Receive Broadcast Code %s\n", info->broadcast_code);
if (broadcast_code_list.size() > 0) {
bool found = false;
uint8_t i = 0;
for (i = 0; i < broadcast_code_list.size(); ++i) {
if (strncmp(info->broadcast_code, broadcast_code_list[i].c_str(), kBroadcastCodeSize) == 0) {
found = true;
break;
}
}
if (!found) {
return;
}
}
else {
if ((broadcast_code_rev.size() == 0) || (std::find(broadcast_code_rev.begin(), broadcast_code_rev.end(), info->broadcast_code) == broadcast_code_rev.end()))
broadcast_code_rev.push_back(info->broadcast_code);
return;
}
bool result = false;
uint8_t handle = 0;
result = AddLidarToConnect(info->broadcast_code, &handle);
if (result == kStatusSuccess) {
/** Set the point cloud data for a specific Livox LiDAR. */
SetDataCallback(handle, GetLidarData, nullptr);
devices[handle].handle = handle;
devices[handle].device_state = kDeviceStateDisconnect;
}
}
/** Set the program options.
* You can input the registered device broadcast code and decide whether to save the log file.
*/
int SetProgramOption(int argc, const char *argv[]) {
apr_status_t rv;
apr_pool_t *mp = nullptr;
static const apr_getopt_option_t opt_option[] = {
/** Long-option, short-option, has-arg flag, description */
{ "code", 'c', 1, "Register device broadcast code" },
{ "log", 'l', 0, "Save the log file" },
{ "time", 't', 1, "Time to save point cloud to the lvx file" },
{ "param", 'p', 0, "Get the extrinsic parameter from extrinsic.xml file" },
{ "help", 'h', 0, "Show help" },
{ nullptr, 0, 0, nullptr },
};
apr_getopt_t *opt = nullptr;
int optch = 0;
const char *optarg = nullptr;
if (apr_initialize() != APR_SUCCESS) {
return -1;
}
if (apr_pool_create(&mp, NULL) != APR_SUCCESS) {
return -1;
}
rv = apr_getopt_init(&opt, mp, argc, argv);
if (rv != APR_SUCCESS) {
printf("Program options initialization failed.\n");
return -1;
}
/** Parse the all options based on opt_option[] */
bool is_help = false;
while ((rv = apr_getopt_long(opt, opt_option, &optch, &optarg)) == APR_SUCCESS) {
switch (optch) {
case 'c': {
printf("Register broadcast code: %s\n", optarg);
char *sn_list = (char *)malloc(sizeof(char)*(strlen(optarg) + 1));
strncpy(sn_list, optarg, sizeof(char)*(strlen(optarg) + 1));
char *sn_list_head = sn_list;
sn_list = strtok(sn_list, "&");
int i = 0;
broadcast_code_list.clear();
while (sn_list) {
broadcast_code_list.push_back(sn_list);
sn_list = strtok(nullptr, "&");
i++;
}
free(sn_list_head);
sn_list_head = nullptr;
break;
}
case 'l': {
printf("Save the log file.\n");
SaveLoggerFile();
break;
}
case 't': {
printf("Time to save point cloud to the lvx file:%s.\n", optarg);
lvx_file_save_time = atoi(optarg);
break;
}
case 'p': {
printf("Get the extrinsic parameter from extrinsic.xml file.\n");
is_read_extrinsic_from_xml = true;
break;
}
case 'h': {
printf(
" [-c] Register device broadcast code\n"
" [-l] Save the log file\n"
" [-t] Time to save point cloud to the lvx file\n"
" [-p] Get the extrinsic parameter from extrinsic.xml file\n"
" [-h] Show help\n"
);
is_help = true;
break;
}
}
}
if (rv != APR_EOF) {
printf("Invalid options.\n");
}
apr_pool_destroy(mp);
mp = nullptr;
if (is_help)
return 1;
return 0;
}
int main(int argc, const char *argv[]) {
/** Set the program options. */
if (SetProgramOption(argc, argv))
return 0;
printf("Livox SDK initializing.\n");
/** Initialize Livox-SDK. */
if (!Init()) {
return -1;
}
printf("Livox SDK has been initialized.\n");
LivoxSdkVersion _sdkversion;
GetLivoxSdkVersion(&_sdkversion);
printf("Livox SDK version %d.%d.%d .\n", _sdkversion.major, _sdkversion.minor, _sdkversion.patch);
memset(devices, 0, sizeof(devices));
/** Set the callback function receiving broadcast message from Livox LiDAR. */
SetBroadcastCallback(OnDeviceBroadcast);
/** Set the callback function called when device state change,
* which means connection/disconnection and changing of LiDAR state.
*/
SetDeviceStateUpdateCallback(OnDeviceChange);
/** Start the device discovering routine. */
if (!Start()) {
Uninit();
return -1;
}
printf("Start discovering device.\n");
#ifdef WIN32
Sleep(2000);
#else
sleep(2);
#endif
if (broadcast_code_rev.size() != 0)
broadcast_code_list = broadcast_code_rev;
{
std::unique_lock<std::mutex> lock(mtx);
condition_variable.wait(lock);
}
printf("Start initialize lvx file.\n");
if (!lvx_file_handler.InitLvxFile()) {
Uninit();
return -1;
}
lvx_file_handler.InitLvxFileHeader();
int i = 0;
for (i = 0; i < lvx_file_save_time * FRAME_RATE; ++i) {
std::list<LvxBasePackDetail> point_packet_list_temp;
{
std::unique_lock<std::mutex> lock(mtx);
condition_variable.wait(lock);
point_packet_list_temp.swap(point_packet_list);
}
printf("Finish save %d frame to lvx file.\n", i);
lvx_file_handler.SaveFrameToLvxFile(point_packet_list_temp);
}
lvx_file_handler.CloseLvxFile();
for (i = 0; i < kMaxLidarCount; ++i) {
if (devices[i].device_state == kDeviceStateSampling) {
/** Stop the sampling of Livox LiDAR. */
LidarStopSampling(devices[i].handle, OnStopSampleCallback, nullptr);
}
}
/** Uninitialize Livox-SDK. */
Uninit();
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,174 @@
#ifndef RAPIDXML_ITERATORS_HPP_INCLUDED
#define RAPIDXML_ITERATORS_HPP_INCLUDED
// Copyright (C) 2006, 2009 Marcin Kalicinski
// Version 1.13
// Revision $DateTime: 2009/05/13 01:46:17 $
//! \file rapidxml_iterators.hpp This file contains rapidxml iterators
#include "rapidxml.hpp"
namespace rapidxml
{
//! Iterator of child nodes of xml_node
template<class Ch>
class node_iterator
{
public:
typedef typename xml_node<Ch> value_type;
typedef typename xml_node<Ch> &reference;
typedef typename xml_node<Ch> *pointer;
typedef std::ptrdiff_t difference_type;
typedef std::bidirectional_iterator_tag iterator_category;
node_iterator()
: m_node(0)
{
}
node_iterator(xml_node<Ch> *node)
: m_node(node->first_node())
{
}
reference operator *() const
{
assert(m_node);
return *m_node;
}
pointer operator->() const
{
assert(m_node);
return m_node;
}
node_iterator& operator++()
{
assert(m_node);
m_node = m_node->next_sibling();
return *this;
}
node_iterator operator++(int)
{
node_iterator tmp = *this;
++this;
return tmp;
}
node_iterator& operator--()
{
assert(m_node && m_node->previous_sibling());
m_node = m_node->previous_sibling();
return *this;
}
node_iterator operator--(int)
{
node_iterator tmp = *this;
++this;
return tmp;
}
bool operator ==(const node_iterator<Ch> &rhs)
{
return m_node == rhs.m_node;
}
bool operator !=(const node_iterator<Ch> &rhs)
{
return m_node != rhs.m_node;
}
private:
xml_node<Ch> *m_node;
};
//! Iterator of child attributes of xml_node
template<class Ch>
class attribute_iterator
{
public:
typedef typename xml_attribute<Ch> value_type;
typedef typename xml_attribute<Ch> &reference;
typedef typename xml_attribute<Ch> *pointer;
typedef std::ptrdiff_t difference_type;
typedef std::bidirectional_iterator_tag iterator_category;
attribute_iterator()
: m_attribute(0)
{
}
attribute_iterator(xml_node<Ch> *node)
: m_attribute(node->first_attribute())
{
}
reference operator *() const
{
assert(m_attribute);
return *m_attribute;
}
pointer operator->() const
{
assert(m_attribute);
return m_attribute;
}
attribute_iterator& operator++()
{
assert(m_attribute);
m_attribute = m_attribute->next_attribute();
return *this;
}
attribute_iterator operator++(int)
{
attribute_iterator tmp = *this;
++this;
return tmp;
}
attribute_iterator& operator--()
{
assert(m_attribute && m_attribute->previous_attribute());
m_attribute = m_attribute->previous_attribute();
return *this;
}
attribute_iterator operator--(int)
{
attribute_iterator tmp = *this;
++this;
return tmp;
}
bool operator ==(const attribute_iterator<Ch> &rhs)
{
return m_attribute == rhs.m_attribute;
}
bool operator !=(const attribute_iterator<Ch> &rhs)
{
return m_attribute != rhs.m_attribute;
}
private:
xml_attribute<Ch> *m_attribute;
};
}
#endif
@@ -0,0 +1,421 @@
#ifndef RAPIDXML_PRINT_HPP_INCLUDED
#define RAPIDXML_PRINT_HPP_INCLUDED
// Copyright (C) 2006, 2009 Marcin Kalicinski
// Version 1.13
// Revision $DateTime: 2009/05/13 01:46:17 $
//! \file rapidxml_print.hpp This file contains rapidxml printer implementation
#include "rapidxml.hpp"
// Only include streams if not disabled
#ifndef RAPIDXML_NO_STREAMS
#include <ostream>
#include <iterator>
#endif
namespace rapidxml
{
///////////////////////////////////////////////////////////////////////
// Printing flags
const int print_no_indenting = 0x1; //!< Printer flag instructing the printer to suppress indenting of XML. See print() function.
///////////////////////////////////////////////////////////////////////
// Internal
//! \cond internal
namespace internal
{
///////////////////////////////////////////////////////////////////////////
// Internal character operations
// Copy characters from given range to given output iterator
template<class OutIt, class Ch>
inline OutIt copy_chars(const Ch *begin, const Ch *end, OutIt out)
{
while (begin != end)
*out++ = *begin++;
return out;
}
// Copy characters from given range to given output iterator and expand
// characters into references (&lt; &gt; &apos; &quot; &amp;)
template<class OutIt, class Ch>
inline OutIt copy_and_expand_chars(const Ch *begin, const Ch *end, Ch noexpand, OutIt out)
{
while (begin != end)
{
if (*begin == noexpand)
{
*out++ = *begin; // No expansion, copy character
}
else
{
switch (*begin)
{
case Ch('<'):
*out++ = Ch('&'); *out++ = Ch('l'); *out++ = Ch('t'); *out++ = Ch(';');
break;
case Ch('>'):
*out++ = Ch('&'); *out++ = Ch('g'); *out++ = Ch('t'); *out++ = Ch(';');
break;
case Ch('\''):
*out++ = Ch('&'); *out++ = Ch('a'); *out++ = Ch('p'); *out++ = Ch('o'); *out++ = Ch('s'); *out++ = Ch(';');
break;
case Ch('"'):
*out++ = Ch('&'); *out++ = Ch('q'); *out++ = Ch('u'); *out++ = Ch('o'); *out++ = Ch('t'); *out++ = Ch(';');
break;
case Ch('&'):
*out++ = Ch('&'); *out++ = Ch('a'); *out++ = Ch('m'); *out++ = Ch('p'); *out++ = Ch(';');
break;
default:
*out++ = *begin; // No expansion, copy character
}
}
++begin; // Step to next character
}
return out;
}
// Fill given output iterator with repetitions of the same character
template<class OutIt, class Ch>
inline OutIt fill_chars(OutIt out, int n, Ch ch)
{
for (int i = 0; i < n; ++i)
*out++ = ch;
return out;
}
// Find character
template<class Ch, Ch ch>
inline bool find_char(const Ch *begin, const Ch *end)
{
while (begin != end)
if (*begin++ == ch)
return true;
return false;
}
///////////////////////////////////////////////////////////////////////////
// Internal printing operations
// Print node
template<class OutIt, class Ch>
inline OutIt print_node(OutIt out, const xml_node<Ch> *node, int flags, int indent)
{
// Print proper node type
switch (node->type())
{
// Document
case node_document:
out = print_children(out, node, flags, indent);
break;
// Element
case node_element:
out = print_element_node(out, node, flags, indent);
break;
// Data
case node_data:
out = print_data_node(out, node, flags, indent);
break;
// CDATA
case node_cdata:
out = print_cdata_node(out, node, flags, indent);
break;
// Declaration
case node_declaration:
out = print_declaration_node(out, node, flags, indent);
break;
// Comment
case node_comment:
out = print_comment_node(out, node, flags, indent);
break;
// Doctype
case node_doctype:
out = print_doctype_node(out, node, flags, indent);
break;
// Pi
case node_pi:
out = print_pi_node(out, node, flags, indent);
break;
// Unknown
default:
assert(0);
break;
}
// If indenting not disabled, add line break after node
if (!(flags & print_no_indenting))
*out = Ch('\n'), ++out;
// Return modified iterator
return out;
}
// Print children of the node
template<class OutIt, class Ch>
inline OutIt print_children(OutIt out, const xml_node<Ch> *node, int flags, int indent)
{
for (xml_node<Ch> *child = node->first_node(); child; child = child->next_sibling())
out = print_node(out, child, flags, indent);
return out;
}
// Print attributes of the node
template<class OutIt, class Ch>
inline OutIt print_attributes(OutIt out, const xml_node<Ch> *node, int flags)
{
for (xml_attribute<Ch> *attribute = node->first_attribute(); attribute; attribute = attribute->next_attribute())
{
if (attribute->name() && attribute->value())
{
// Print attribute name
*out = Ch(' '), ++out;
out = copy_chars(attribute->name(), attribute->name() + attribute->name_size(), out);
*out = Ch('='), ++out;
// Print attribute value using appropriate quote type
if (find_char<Ch, Ch('"')>(attribute->value(), attribute->value() + attribute->value_size()))
{
*out = Ch('\''), ++out;
out = copy_and_expand_chars(attribute->value(), attribute->value() + attribute->value_size(), Ch('"'), out);
*out = Ch('\''), ++out;
}
else
{
*out = Ch('"'), ++out;
out = copy_and_expand_chars(attribute->value(), attribute->value() + attribute->value_size(), Ch('\''), out);
*out = Ch('"'), ++out;
}
}
}
return out;
}
// Print data node
template<class OutIt, class Ch>
inline OutIt print_data_node(OutIt out, const xml_node<Ch> *node, int flags, int indent)
{
assert(node->type() == node_data);
if (!(flags & print_no_indenting))
out = fill_chars(out, indent, Ch('\t'));
out = copy_and_expand_chars(node->value(), node->value() + node->value_size(), Ch(0), out);
return out;
}
// Print data node
template<class OutIt, class Ch>
inline OutIt print_cdata_node(OutIt out, const xml_node<Ch> *node, int flags, int indent)
{
assert(node->type() == node_cdata);
if (!(flags & print_no_indenting))
out = fill_chars(out, indent, Ch('\t'));
*out = Ch('<'); ++out;
*out = Ch('!'); ++out;
*out = Ch('['); ++out;
*out = Ch('C'); ++out;
*out = Ch('D'); ++out;
*out = Ch('A'); ++out;
*out = Ch('T'); ++out;
*out = Ch('A'); ++out;
*out = Ch('['); ++out;
out = copy_chars(node->value(), node->value() + node->value_size(), out);
*out = Ch(']'); ++out;
*out = Ch(']'); ++out;
*out = Ch('>'); ++out;
return out;
}
// Print element node
template<class OutIt, class Ch>
inline OutIt print_element_node(OutIt out, const xml_node<Ch> *node, int flags, int indent)
{
assert(node->type() == node_element);
// Print element name and attributes, if any
if (!(flags & print_no_indenting))
out = fill_chars(out, indent, Ch('\t'));
*out = Ch('<'), ++out;
out = copy_chars(node->name(), node->name() + node->name_size(), out);
out = print_attributes(out, node, flags);
// If node is childless
if (node->value_size() == 0 && !node->first_node())
{
// Print childless node tag ending
*out = Ch('/'), ++out;
*out = Ch('>'), ++out;
}
else
{
// Print normal node tag ending
*out = Ch('>'), ++out;
// Test if node contains a single data node only (and no other nodes)
xml_node<Ch> *child = node->first_node();
if (!child)
{
// If node has no children, only print its value without indenting
out = copy_and_expand_chars(node->value(), node->value() + node->value_size(), Ch(0), out);
}
else if (child->next_sibling() == 0 && child->type() == node_data)
{
// If node has a sole data child, only print its value without indenting
out = copy_and_expand_chars(child->value(), child->value() + child->value_size(), Ch(0), out);
}
else
{
// Print all children with full indenting
if (!(flags & print_no_indenting))
*out = Ch('\n'), ++out;
out = print_children(out, node, flags, indent + 1);
if (!(flags & print_no_indenting))
out = fill_chars(out, indent, Ch('\t'));
}
// Print node end
*out = Ch('<'), ++out;
*out = Ch('/'), ++out;
out = copy_chars(node->name(), node->name() + node->name_size(), out);
*out = Ch('>'), ++out;
}
return out;
}
// Print declaration node
template<class OutIt, class Ch>
inline OutIt print_declaration_node(OutIt out, const xml_node<Ch> *node, int flags, int indent)
{
// Print declaration start
if (!(flags & print_no_indenting))
out = fill_chars(out, indent, Ch('\t'));
*out = Ch('<'), ++out;
*out = Ch('?'), ++out;
*out = Ch('x'), ++out;
*out = Ch('m'), ++out;
*out = Ch('l'), ++out;
// Print attributes
out = print_attributes(out, node, flags);
// Print declaration end
*out = Ch('?'), ++out;
*out = Ch('>'), ++out;
return out;
}
// Print comment node
template<class OutIt, class Ch>
inline OutIt print_comment_node(OutIt out, const xml_node<Ch> *node, int flags, int indent)
{
assert(node->type() == node_comment);
if (!(flags & print_no_indenting))
out = fill_chars(out, indent, Ch('\t'));
*out = Ch('<'), ++out;
*out = Ch('!'), ++out;
*out = Ch('-'), ++out;
*out = Ch('-'), ++out;
out = copy_chars(node->value(), node->value() + node->value_size(), out);
*out = Ch('-'), ++out;
*out = Ch('-'), ++out;
*out = Ch('>'), ++out;
return out;
}
// Print doctype node
template<class OutIt, class Ch>
inline OutIt print_doctype_node(OutIt out, const xml_node<Ch> *node, int flags, int indent)
{
assert(node->type() == node_doctype);
if (!(flags & print_no_indenting))
out = fill_chars(out, indent, Ch('\t'));
*out = Ch('<'), ++out;
*out = Ch('!'), ++out;
*out = Ch('D'), ++out;
*out = Ch('O'), ++out;
*out = Ch('C'), ++out;
*out = Ch('T'), ++out;
*out = Ch('Y'), ++out;
*out = Ch('P'), ++out;
*out = Ch('E'), ++out;
*out = Ch(' '), ++out;
out = copy_chars(node->value(), node->value() + node->value_size(), out);
*out = Ch('>'), ++out;
return out;
}
// Print pi node
template<class OutIt, class Ch>
inline OutIt print_pi_node(OutIt out, const xml_node<Ch> *node, int flags, int indent)
{
assert(node->type() == node_pi);
if (!(flags & print_no_indenting))
out = fill_chars(out, indent, Ch('\t'));
*out = Ch('<'), ++out;
*out = Ch('?'), ++out;
out = copy_chars(node->name(), node->name() + node->name_size(), out);
*out = Ch(' '), ++out;
out = copy_chars(node->value(), node->value() + node->value_size(), out);
*out = Ch('?'), ++out;
*out = Ch('>'), ++out;
return out;
}
}
//! \endcond
///////////////////////////////////////////////////////////////////////////
// Printing
//! Prints XML to given output iterator.
//! \param out Output iterator to print to.
//! \param node Node to be printed. Pass xml_document to print entire document.
//! \param flags Flags controlling how XML is printed.
//! \return Output iterator pointing to position immediately after last character of printed text.
template<class OutIt, class Ch>
inline OutIt print(OutIt out, const xml_node<Ch> &node, int flags = 0)
{
return internal::print_node(out, &node, flags, 0);
}
#ifndef RAPIDXML_NO_STREAMS
//! Prints XML to given output stream.
//! \param out Output stream to print to.
//! \param node Node to be printed. Pass xml_document to print entire document.
//! \param flags Flags controlling how XML is printed.
//! \return Output stream.
template<class Ch>
inline std::basic_ostream<Ch> &print(std::basic_ostream<Ch> &out, const xml_node<Ch> &node, int flags = 0)
{
print(std::ostream_iterator<Ch>(out), node, flags);
return out;
}
//! Prints formatted XML to given output stream. Uses default printing flags. Use print() function to customize printing process.
//! \param out Output stream to print to.
//! \param node Node to be printed.
//! \return Output stream.
template<class Ch>
inline std::basic_ostream<Ch> &operator <<(std::basic_ostream<Ch> &out, const xml_node<Ch> &node)
{
return print(out, node);
}
#endif
}
#endif
@@ -0,0 +1,122 @@
#ifndef RAPIDXML_UTILS_HPP_INCLUDED
#define RAPIDXML_UTILS_HPP_INCLUDED
// Copyright (C) 2006, 2009 Marcin Kalicinski
// Version 1.13
// Revision $DateTime: 2009/05/13 01:46:17 $
//! \file rapidxml_utils.hpp This file contains high-level rapidxml utilities that can be useful
//! in certain simple scenarios. They should probably not be used if maximizing performance is the main objective.
#include "rapidxml.hpp"
#include <vector>
#include <string>
#include <fstream>
#include <stdexcept>
namespace rapidxml
{
//! Represents data loaded from a file
template<class Ch = char>
class file
{
public:
//! Loads file into the memory. Data will be automatically destroyed by the destructor.
//! \param filename Filename to load.
file(const char *filename)
{
using namespace std;
// Open stream
basic_ifstream<Ch> stream(filename, ios::binary);
if (!stream)
throw runtime_error(string("cannot open file ") + filename);
stream.unsetf(ios::skipws);
// Determine stream size
stream.seekg(0, ios::end);
size_t size = stream.tellg();
stream.seekg(0);
// Load data and add terminating 0
m_data.resize(size + 1);
stream.read(&m_data.front(), static_cast<streamsize>(size));
m_data[size] = 0;
}
//! Loads file into the memory. Data will be automatically destroyed by the destructor
//! \param stream Stream to load from
file(std::basic_istream<Ch> &stream)
{
using namespace std;
// Load data and add terminating 0
stream.unsetf(ios::skipws);
m_data.assign(istreambuf_iterator<Ch>(stream), istreambuf_iterator<Ch>());
if (stream.fail() || stream.bad())
throw runtime_error("error reading stream");
m_data.push_back(0);
}
//! Gets file data.
//! \return Pointer to data of file.
Ch *data()
{
return &m_data.front();
}
//! Gets file data.
//! \return Pointer to data of file.
const Ch *data() const
{
return &m_data.front();
}
//! Gets file data size.
//! \return Size of file data, in characters.
std::size_t size() const
{
return m_data.size();
}
private:
std::vector<Ch> m_data; // File data
};
//! Counts children of node. Time complexity is O(n).
//! \return Number of children of node
template<class Ch>
inline std::size_t count_children(xml_node<Ch> *node)
{
xml_node<Ch> *child = node->first_node();
std::size_t count = 0;
while (child)
{
++count;
child = child->next_sibling();
}
return count;
}
//! Counts attributes of node. Time complexity is O(n).
//! \return Number of attributes of node
template<class Ch>
inline std::size_t count_attributes(xml_node<Ch> *node)
{
xml_attribute<Ch> *attr = node->first_attribute();
std::size_t count = 0;
while (attr)
{
++count;
attr = attr->next_attribute();
}
return count;
}
}
#endif