Skip to content

File hesai_packet.hpp

File List > decoders > hesai_packet.hpp

Go to the documentation of this file

// Copyright 2024 TIER IV, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#pragma once

#include <cstddef>
#include <cstdint>
#include <ctime>
#include <stdexcept>
namespace nebula::drivers::hesai_packet
{

// FIXME(mojomex) This is a workaround for the compiler being pedantic about casting `enum class`s
// to their underlying type
namespace return_mode
{
enum ReturnMode {
  SINGLE_FIRST = 0x33,
  SINGLE_SECOND = 0x34,
  SINGLE_STRONGEST = 0x37,
  SINGLE_LAST = 0x38,
  DUAL_LAST_STRONGEST = 0x39,
  DUAL_FIRST_SECOND = 0x3a,
  DUAL_FIRST_LAST = 0x3b,
  DUAL_FIRST_STRONGEST = 0x3c,
  TRIPLE_FIRST_LAST_STRONGEST = 0x3d,
  DUAL_STRONGEST_SECONDSTRONGEST = 0x3e,
};
}  // namespace return_mode

#pragma pack(push, 1)

template <int YearOffset>
struct DateTime
{
  uint8_t year;
  uint8_t month;
  uint8_t day;
  uint8_t hour;
  uint8_t minute;
  uint8_t second;

  [[nodiscard]] uint64_t getSeconds() const
  {
    std::tm tm{};
    tm.tm_year = year - 1900 + YearOffset;
    tm.tm_mon = month - 1;  // starts from 0 in C
    tm.tm_mday = day;
    tm.tm_hour = hour;
    tm.tm_min = minute;
    tm.tm_sec = second;
    return timegm(&tm);
  }
};

struct SecondsSinceEpoch
{
  uint8_t zero;
  uint8_t seconds[5];

  [[nodiscard]] uint64_t getSeconds() const
  {
    uint64_t seconds = 0;
    for (unsigned char second : this->seconds) {
      seconds = (seconds << 8) | second;
    }
    return seconds;
  }
};

struct FunctionalSafety
{
  uint8_t fs_version;
  uint8_t lidar_state;
  uint8_t fault_code_id;
  uint16_t fault_code;
  uint8_t reserved1[8];
  uint32_t crc_fs;
};

struct Header12B
{
  uint16_t sop;
  uint8_t protocol_major;
  uint8_t protocol_minor;
  uint8_t reserved1[2];

  uint8_t laser_num;
  uint8_t block_num;
  uint8_t reserved2;
  uint8_t dis_unit;
  uint8_t return_num;
  uint8_t flags;
};

struct Header8B
{
  uint16_t sop;

  uint8_t laser_num;
  uint8_t block_num;
  uint8_t reserved1;
  uint8_t dis_unit;
  uint8_t reserved2[2];
};

struct Unit3B
{
  uint16_t distance;
  uint8_t reflectivity;
};

struct Unit4B
{
  uint16_t distance;
  uint8_t reflectivity;
  uint8_t confidence_or_reserved;
};

template <typename UnitT, size_t UnitN>
struct Block
{
  uint16_t azimuth;
  UnitT units[UnitN];
  using unit_t = UnitT;

  [[nodiscard]] uint32_t getAzimuth() const { return azimuth; }
};

template <typename UnitT, size_t UnitN>
struct FineAzimuthBlock
{
  using unit_t = UnitT;
  uint16_t azimuth;
  uint8_t fine_azimuth;
  UnitT units[UnitN];

  [[nodiscard]] uint32_t getAzimuth() const { return (azimuth << 8) + fine_azimuth; }
};

template <typename UnitT, size_t UnitN>
struct SOBBlock
{
  using unit_t = UnitT;

  uint16_t sob;
  uint16_t azimuth;
  UnitT units[UnitN];

  [[nodiscard]] uint32_t getAzimuth() const { return azimuth; }
};

template <typename BlockT, size_t BlockN>
struct Body
{
  using block_t = BlockT;
  BlockT blocks[BlockN];
};

template <size_t nBlocks, size_t nChannels, size_t maxReturns, size_t degreeSubdivisions>
struct PacketBase
{
  static constexpr size_t N_BLOCKS = nBlocks;
  static constexpr size_t N_CHANNELS = nChannels;
  static constexpr size_t MAX_RETURNS = maxReturns;
  static constexpr size_t DEGREE_SUBDIVISIONS = degreeSubdivisions;
};

#pragma pack(pop)

inline int get_n_returns(uint8_t return_mode)
{
  switch (return_mode) {
    case return_mode::SINGLE_FIRST:
    case return_mode::SINGLE_SECOND:
    case return_mode::SINGLE_STRONGEST:
    case return_mode::SINGLE_LAST:
      return 1;
    case return_mode::DUAL_LAST_STRONGEST:
    case return_mode::DUAL_FIRST_SECOND:
    case return_mode::DUAL_FIRST_LAST:
    case return_mode::DUAL_FIRST_STRONGEST:
    case return_mode::DUAL_STRONGEST_SECONDSTRONGEST:
      return 2;
    case return_mode::TRIPLE_FIRST_LAST_STRONGEST:
      return 3;
    default:
      throw std::runtime_error("Unknown return mode");
  }
}

template <typename PacketT>
uint64_t get_timestamp_ns(const PacketT & packet)
{
  return packet.tail.date_time.getSeconds() * 1000000000 + packet.tail.timestamp * 1000;
}

template <typename PacketT>
double get_dis_unit(const PacketT & packet)
{
  // Packets define distance unit in millimeters, convert to meters here
  return packet.header.dis_unit / 1000.;
}

}  // namespace nebula::drivers::hesai_packet