Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/nebula/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ if (BUILD_TESTING)
foreach(MODEL Pandar40P Pandar64 PandarQT64 PandarQT128 Pandar128E4X PandarAT128 PandarXT16 PandarXT32 PandarXT32M ARS548 SRR520 VLP16 VLP32 VLS128 Helios Bpearl)
string(TOLOWER ${MODEL}_smoke_test test_name)
add_ros_test(
test/smoke_test.py
tests/smoke_test.py
TARGET ${test_name}
ARGS "launch_file_path:=${PROJECT_SOURCE_DIR}/launch/nebula_launch.py" "sensor_model:=${MODEL}"
TIMEOUT "10"
Expand Down
File renamed without changes.
44 changes: 43 additions & 1 deletion src/nebula_core/nebula_core_common/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,53 @@ if(BUILD_TESTING)
ament_lint_auto_find_test_dependencies()

ament_add_gtest(test_rate_checker
test/test_rate_checker.cpp
tests/test_rate_checker.cpp
)
target_link_libraries(test_rate_checker
nebula_core_common
)

ament_add_gtest(test_ring_buffer
tests/test_ring_buffer.cpp
)
target_link_libraries(test_ring_buffer
nebula_core_common
)

ament_add_gtest(test_bitfield
tests/test_bitfield.cpp
)
target_link_libraries(test_bitfield
nebula_core_common
)

ament_add_gtest(test_expected
tests/test_expected.cpp
)
target_link_libraries(test_expected
nebula_core_common
)

ament_add_gtest(test_rate_limiter
tests/test_rate_limiter.cpp
)
target_link_libraries(test_rate_limiter
nebula_core_common
)

ament_add_gtest(test_string_conversions
tests/test_string_conversions.cpp
)
target_link_libraries(test_string_conversions
nebula_core_common
)

ament_add_gtest(test_nebula_common
tests/test_nebula_common.cpp
)
target_link_libraries(test_nebula_common
nebula_core_common
)
endif()

install(TARGETS nebula_core_common EXPORT export_nebula_core_common)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@

#pragma once

#include <cstdint>
#include <cstddef>
#include <limits>

namespace nebula::util
{

Expand All @@ -38,12 +40,14 @@ namespace nebula::util
* @param storage The variable acting as the storage for the bitfield
* @return constexpr OutT The extracted value
*/
template <typename OutT, uint8_t LowBit, uint8_t HighBit, typename InT>
template <typename OutT, size_t LowBit, size_t HighBit, typename InT>
constexpr OutT get_bitfield(const InT & storage)
{
constexpr uint8_t n_bits = HighBit - LowBit + 1;
constexpr size_t n_bits = HighBit - LowBit + 1;
constexpr InT all_ones = ~static_cast<InT>(0);
constexpr InT mask = ~(all_ones << n_bits);
constexpr InT mask = (n_bits < std::numeric_limits<InT>::digits)
? static_cast<InT>(~static_cast<InT>(all_ones << n_bits))
: all_ones;

InT raw_value = (storage >> LowBit) & mask;
return static_cast<OutT>(raw_value);
Expand Down
144 changes: 144 additions & 0 deletions src/nebula_core/nebula_core_common/tests/test_bitfield.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
// Copyright 2025 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.

#include "nebula_core_common/util/bitfield.hpp"

#include <gtest/gtest.h>

#include <cstdint>

using nebula::util::get_bitfield;

TEST(BitfieldTest, SingleBitExtraction)
{
uint8_t value = 0b10101010;

EXPECT_EQ((get_bitfield<uint8_t, 0, 0>(value)), 0);
EXPECT_EQ((get_bitfield<uint8_t, 1, 1>(value)), 1);
EXPECT_EQ((get_bitfield<uint8_t, 2, 2>(value)), 0);
EXPECT_EQ((get_bitfield<uint8_t, 3, 3>(value)), 1);
EXPECT_EQ((get_bitfield<uint8_t, 4, 4>(value)), 0);
EXPECT_EQ((get_bitfield<uint8_t, 5, 5>(value)), 1);
EXPECT_EQ((get_bitfield<uint8_t, 6, 6>(value)), 0);
EXPECT_EQ((get_bitfield<uint8_t, 7, 7>(value)), 1);
}

TEST(BitfieldTest, MultiBitExtraction)
{
uint8_t value = 0b11010110; // = 214

// Extract bits [0, 3] = 0b0110 = 6
EXPECT_EQ((get_bitfield<uint8_t, 0, 3>(value)), 6);

// Extract bits [4, 7] = 0b1101 = 13
EXPECT_EQ((get_bitfield<uint8_t, 4, 7>(value)), 13);

// Extract bits [2, 5] = 0b0101 = 5
EXPECT_EQ((get_bitfield<uint8_t, 2, 5>(value)), 5);
}

TEST(BitfieldTest, FullWidthExtraction)
{
uint8_t value8 = 0xAB;
EXPECT_EQ((get_bitfield<uint8_t, 0, 7>(value8)), 0xAB);

uint16_t value16 = 0xABCD;
EXPECT_EQ((get_bitfield<uint16_t, 0, 15>(value16)), 0xABCD);

uint32_t value32 = 0x1234'5678;
EXPECT_EQ((get_bitfield<uint32_t, 0, 31>(value32)), 0x1234'5678);
}

TEST(BitfieldTest, Uint16Extraction)
{
uint16_t value = 0xABCD; // 1010101111001101

// Extract lower byte
EXPECT_EQ((get_bitfield<uint8_t, 0, 7>(value)), 0xCD);

// Extract upper byte
EXPECT_EQ((get_bitfield<uint8_t, 8, 15>(value)), 0xAB);

// Extract middle bits [4, 11]
EXPECT_EQ((get_bitfield<uint8_t, 4, 11>(value)), 0xBC);
}

TEST(BitfieldTest, Uint32Extraction)
{
uint32_t value = 0x12345678;

// Extract each byte
EXPECT_EQ((get_bitfield<uint8_t, 0, 7>(value)), 0x78);
EXPECT_EQ((get_bitfield<uint8_t, 8, 15>(value)), 0x56);
EXPECT_EQ((get_bitfield<uint8_t, 16, 23>(value)), 0x34);
EXPECT_EQ((get_bitfield<uint8_t, 24, 31>(value)), 0x12);

// Extract 16-bit values
EXPECT_EQ((get_bitfield<uint16_t, 0, 15>(value)), 0x5678);
EXPECT_EQ((get_bitfield<uint16_t, 16, 31>(value)), 0x1234);
}

enum class TestEnum : uint8_t { VALUE_0 = 0, VALUE_1 = 1, VALUE_5 = 5, VALUE_15 = 15 };

TEST(BitfieldTest, EnumOutput)
{
uint8_t value = 0b01010001; // bits[0,3] = 1, bits[4,7] = 5

EXPECT_EQ((get_bitfield<TestEnum, 0, 3>(value)), TestEnum::VALUE_1);
EXPECT_EQ((get_bitfield<TestEnum, 4, 7>(value)), TestEnum::VALUE_5);
}

TEST(BitfieldTest, ZeroValue)
{
uint32_t value = 0;

EXPECT_EQ((get_bitfield<uint8_t, 0, 7>(value)), 0);
EXPECT_EQ((get_bitfield<uint16_t, 8, 23>(value)), 0);
EXPECT_EQ((get_bitfield<uint32_t, 0, 31>(value)), 0);
}

TEST(BitfieldTest, AllOnesValue)
{
uint8_t value8 = 0xFF;
EXPECT_EQ((get_bitfield<uint8_t, 0, 3>(value8)), 0x0F);
EXPECT_EQ((get_bitfield<uint8_t, 4, 7>(value8)), 0x0F);

uint16_t value16 = 0xFFFF;
EXPECT_EQ((get_bitfield<uint8_t, 0, 7>(value16)), 0xFF);
EXPECT_EQ((get_bitfield<uint16_t, 0, 15>(value16)), 0xFFFF);
}

TEST(BitfieldTest, MacroAccessor)
{
struct TestStruct
{
uint16_t storage;

BITFIELD_ACCESSOR(uint8_t, low_nibble, 0, 3, storage)
BITFIELD_ACCESSOR(uint8_t, high_nibble, 4, 7, storage)
BITFIELD_ACCESSOR(uint8_t, upper_byte, 8, 15, storage)
};

TestStruct s{0xABCD};

EXPECT_EQ(s.low_nibble(), 0x0D);
EXPECT_EQ(s.high_nibble(), 0x0C);
EXPECT_EQ(s.upper_byte(), 0xAB);
}

int main(int argc, char ** argv)
{
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
Loading
Loading