181 lines
6.0 KiB
C++
181 lines
6.0 KiB
C++
#include "core/ir_frame.hpp"
|
|
|
|
#include <cassert>
|
|
#include <cstdint>
|
|
#include <vector>
|
|
|
|
namespace {
|
|
|
|
void test_parse_valid_frame() {
|
|
// 2x1 frame: marker + counter + len + type + shutter + 2 pixels + trailing marker + tail
|
|
std::vector<uint8_t> data(0x38 + 4, 0xaa);
|
|
auto w32 = [&](size_t off, uint32_t v) {
|
|
data[off] = static_cast<uint8_t>(v);
|
|
data[off + 1] = static_cast<uint8_t>(v >> 8);
|
|
data[off + 2] = static_cast<uint8_t>(v >> 16);
|
|
data[off + 3] = static_cast<uint8_t>(v >> 24);
|
|
};
|
|
w32(0x00, 0x1bb1b11b);
|
|
w32(0x04, 42);
|
|
w32(0x08, 4); // data_length
|
|
w32(0x0c, 1); // raw frame type
|
|
w32(0x10, 1000); // shutter
|
|
data[0x1c] = 0x34;
|
|
data[0x1d] = 0x12;
|
|
data[0x1e] = 0x78;
|
|
data[0x1f] = 0x56;
|
|
w32(0x1c + 4, 0x1bb1b11c);
|
|
|
|
mag160c::core::IrFrameHeader h{};
|
|
const uint16_t* pixels = nullptr;
|
|
assert(mag160c::core::parse_ir_frame(data.data(), data.size(), &h, &pixels) == MAG160C_OK);
|
|
assert(h.marker == 0x1bb1b11b);
|
|
assert(h.frame_counter == 42);
|
|
assert(h.data_length == 4);
|
|
assert(h.frame_type == 1);
|
|
assert(h.period_shutter == 1000);
|
|
assert(pixels[0] == 0x1234);
|
|
assert(pixels[1] == 0x5678);
|
|
}
|
|
|
|
void test_parse_rejects_bad_marker() {
|
|
std::vector<uint8_t> data(0x40, 0);
|
|
data[0] = 0xab;
|
|
|
|
mag160c::core::IrFrameHeader h{};
|
|
const uint16_t* pixels = nullptr;
|
|
assert(mag160c::core::parse_ir_frame(data.data(), data.size(), &h, &pixels) ==
|
|
MAG160C_ERR_PROTOCOL_UNKNOWN);
|
|
}
|
|
|
|
void test_parse_rejects_bad_type_and_trailer() {
|
|
std::vector<uint8_t> data(0x40, 0);
|
|
auto w32 = [&](size_t off, uint32_t v) {
|
|
data[off] = static_cast<uint8_t>(v);
|
|
data[off + 1] = static_cast<uint8_t>(v >> 8);
|
|
data[off + 2] = static_cast<uint8_t>(v >> 16);
|
|
data[off + 3] = static_cast<uint8_t>(v >> 24);
|
|
};
|
|
w32(0x00, 0x1bb1b11b);
|
|
w32(0x08, 4);
|
|
w32(0x0c, 2); // invalid type
|
|
mag160c::core::IrFrameHeader h{};
|
|
const uint16_t* pixels = nullptr;
|
|
assert(mag160c::core::parse_ir_frame(data.data(), data.size(), &h, &pixels) ==
|
|
MAG160C_ERR_PROTOCOL_UNKNOWN);
|
|
|
|
w32(0x0c, 0);
|
|
w32(0x1c + 4, 0xdeadbeef); // bad trailing marker
|
|
assert(mag160c::core::parse_ir_frame(data.data(), data.size(), &h, &pixels) ==
|
|
MAG160C_ERR_PROTOCOL_UNKNOWN);
|
|
}
|
|
|
|
void test_calibrate_flat_band() {
|
|
// 4 pixels, 2 bands (1 search threshold), threshold = {5} per pixel
|
|
const std::vector<uint16_t> frame{1000, 2000, 3000, 4000};
|
|
const std::vector<int16_t> thresholds{5, 5, 5, 5};
|
|
// per band per pixel {coeff, offset}: band0 = {0, 100}, band1 = {0, 200}
|
|
const std::vector<uint16_t> coeff{
|
|
0, 100, 0, 100, 0, 100, 0, 100,
|
|
0, 200, 0, 200, 0, 200, 0, 200,
|
|
};
|
|
|
|
mag160c::core::IrCalibrationTables tables{};
|
|
tables.thresholds = thresholds.data();
|
|
tables.coeff = coeff.data();
|
|
tables.pixel_count = 4;
|
|
tables.band_count = 2;
|
|
tables.baseline = nullptr;
|
|
tables.has_baseline = false;
|
|
|
|
std::vector<uint16_t> out(4);
|
|
mag160c::core::calibrate_frame(frame.data(), tables, out.data());
|
|
|
|
// diff = frame >> 1 = 500..2000, all > 5 → band 1 → offset 200
|
|
for (auto v : out) {
|
|
assert(v == 200);
|
|
}
|
|
}
|
|
|
|
void test_calibrate_interpolation_and_clamp() {
|
|
// 2 pixels, 2 bands, threshold = {500}
|
|
const std::vector<uint16_t> frame{2000, 0x8000};
|
|
const std::vector<uint16_t> baseline{0, 0x100};
|
|
const std::vector<int16_t> thresholds{500, 500};
|
|
// band0: {coeff=0x1000 (4096), offset=1000}; band1: {coeff=0x100, offset=2000}
|
|
const std::vector<uint16_t> coeff{
|
|
0x1000, 1000, 0x1000, 1000,
|
|
0x0100, 2000, 0x0100, 2000,
|
|
};
|
|
|
|
mag160c::core::IrCalibrationTables tables{};
|
|
tables.thresholds = thresholds.data();
|
|
tables.coeff = coeff.data();
|
|
tables.pixel_count = 2;
|
|
tables.band_count = 2;
|
|
tables.baseline = baseline.data();
|
|
tables.has_baseline = true;
|
|
|
|
std::vector<uint16_t> out(2);
|
|
mag160c::core::calibrate_frame(frame.data(), tables, out.data());
|
|
|
|
// pixel 0: diff = (2000-0)>>1 = 1000 > 500 → band1: 2000 + (1000*0x100 >> 12) = 2000 + 62
|
|
assert(out[0] == 2062);
|
|
// pixel 1: diff = (0x8000-0x100)>>1 = 0x3f80 (16256) > 500 → band1: 2000 + (16256*256 >> 12)
|
|
// = 2000 + 1016 = 3016
|
|
assert(out[1] == 3016);
|
|
}
|
|
|
|
void test_calibrate_negative_diff_zero() {
|
|
const std::vector<uint16_t> frame{100};
|
|
const std::vector<uint16_t> baseline{200};
|
|
const std::vector<int16_t> thresholds{5};
|
|
const std::vector<uint16_t> coeff{0x1000, 1000, 0x1000, 1000};
|
|
|
|
mag160c::core::IrCalibrationTables tables{};
|
|
tables.thresholds = thresholds.data();
|
|
tables.coeff = coeff.data();
|
|
tables.pixel_count = 1;
|
|
tables.band_count = 2;
|
|
tables.baseline = baseline.data();
|
|
tables.has_baseline = true;
|
|
|
|
std::vector<uint16_t> out(1);
|
|
mag160c::core::calibrate_frame(frame.data(), tables, out.data());
|
|
|
|
// diff = (int16)(100-200) >> 1 = -50; negative coeff*offset path clamps to >= 0:
|
|
// band0 (diff <= 5): 1000 + (-50 * 4096 >> 12) = 1000 - 50 = 950
|
|
assert(out[0] == 950);
|
|
}
|
|
|
|
void test_parse_rejects_truncated_frame() {
|
|
std::vector<uint8_t> data(0x38 + 4, 0);
|
|
auto w32 = [&](size_t off, uint32_t v) {
|
|
data[off] = static_cast<uint8_t>(v);
|
|
data[off + 1] = static_cast<uint8_t>(v >> 8);
|
|
data[off + 2] = static_cast<uint8_t>(v >> 16);
|
|
data[off + 3] = static_cast<uint8_t>(v >> 24);
|
|
};
|
|
w32(0x00, 0x1bb1b11b);
|
|
w32(0x08, 4);
|
|
|
|
mag160c::core::IrFrameHeader h{};
|
|
const uint16_t* pixels = nullptr;
|
|
data.resize(data.size() - 1); // truncate
|
|
assert(mag160c::core::parse_ir_frame(data.data(), data.size(), &h, &pixels) ==
|
|
MAG160C_ERR_PROTOCOL_UNKNOWN);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
int main() {
|
|
test_parse_valid_frame();
|
|
test_parse_rejects_bad_marker();
|
|
test_parse_rejects_bad_type_and_trailer();
|
|
test_parse_rejects_truncated_frame();
|
|
test_calibrate_flat_band();
|
|
test_calibrate_interpolation_and_clamp();
|
|
test_calibrate_negative_diff_zero();
|
|
return 0;
|
|
}
|