清理仓库:删除验证期中间产物(约 225MB)

- 删除 analysis/ 下 pairs_*(采集对比数据)、reverse-cache*、captures
  (pcap)、usbnoise、ourdump、legacy-cpp 等逆向验证中间产物
- 保留:analysis/ida(.i64 逆向数据库+二进制)、disasm、history、
  reverse_tools、关键文档(reverse_20260813_full/session_state/protocol)
- 保留:IR_Camera_SDK-1.0.1(官方 SDK 参考)、app/(官方安装包)、
  vendor-docs/(官方手册)、.tools/(capstone/pyelftools)
This commit is contained in:
ZXCLI
2026-08-19 15:50:35 +08:00
parent 0dcb2830c6
commit 0d83c625d1
2394 changed files with 0 additions and 132866 deletions
Binary file not shown.
File diff suppressed because it is too large Load Diff
Binary file not shown.
Binary file not shown.
-91
View File
@@ -1,91 +0,0 @@
cmake_minimum_required(VERSION 3.16)
project(mag160c LANGUAGES C CXX)
option(MAG160C_BUILD_TESTS "Build MAG160C tests" ON)
option(MAG160C_BUILD_CLI "Build MAG160C CLI tools" ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
find_package(PkgConfig QUIET)
if(PkgConfig_FOUND)
pkg_check_modules(LIBUSB QUIET libusb-1.0)
endif()
if(MAG160C_BUILD_CLI)
add_executable(mag160c-cli tools/mag160c_cli.cpp)
target_link_libraries(mag160c-cli PRIVATE mag160c_core)
endif()
add_library(mag160c_core SHARED
src/core/error.cpp
src/core/context.cpp
src/core/tcm_frame.cpp
src/core/tcm_device.cpp
src/core/device.cpp
src/core/ir_device.cpp
src/core/ir_frame.cpp
src/c_api.cpp
)
target_include_directories(mag160c_core
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/include
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src
)
target_compile_definitions(mag160c_core PRIVATE MAG160C_BUILDING_LIBRARY)
if(LIBUSB_FOUND)
set(MAG160C_HAS_LIBUSB_VALUE 1)
target_compile_definitions(mag160c_core PRIVATE MAG160C_HAS_LIBUSB=1)
target_include_directories(mag160c_core PRIVATE ${LIBUSB_INCLUDE_DIRS})
target_link_directories(mag160c_core PRIVATE ${LIBUSB_LIBRARY_DIRS})
target_link_libraries(mag160c_core PRIVATE ${LIBUSB_LIBRARIES})
target_compile_options(mag160c_core PRIVATE ${LIBUSB_CFLAGS_OTHER})
else()
set(MAG160C_HAS_LIBUSB_VALUE 0)
target_compile_definitions(mag160c_core PRIVATE MAG160C_HAS_LIBUSB=0)
endif()
if(MAG160C_BUILD_TESTS)
enable_testing()
add_executable(test_c_api tests/cpp/test_c_api.cpp)
target_link_libraries(test_c_api PRIVATE mag160c_core)
target_compile_definitions(test_c_api PRIVATE MAG160C_HAS_LIBUSB=${MAG160C_HAS_LIBUSB_VALUE})
add_test(NAME test_c_api COMMAND test_c_api)
add_executable(test_tcm_frame tests/cpp/test_tcm_frame.cpp)
target_link_libraries(test_tcm_frame PRIVATE mag160c_core)
target_include_directories(test_tcm_frame PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
add_test(NAME test_tcm_frame COMMAND test_tcm_frame)
add_executable(test_tcm_device tests/cpp/test_tcm_device.cpp)
target_link_libraries(test_tcm_device PRIVATE mag160c_core)
target_include_directories(test_tcm_device PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
add_test(NAME test_tcm_device COMMAND test_tcm_device)
add_executable(test_device_model tests/cpp/test_device_model.cpp)
target_link_libraries(test_device_model PRIVATE mag160c_core)
target_include_directories(test_device_model PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
add_test(NAME test_device_model COMMAND test_device_model)
add_executable(test_ir_skeleton tests/cpp/test_ir_skeleton.cpp)
target_link_libraries(test_ir_skeleton PRIVATE mag160c_core)
add_test(NAME test_ir_skeleton COMMAND test_ir_skeleton)
add_executable(test_ir_frame tests/cpp/test_ir_frame.cpp)
target_link_libraries(test_ir_frame PRIVATE mag160c_core)
target_include_directories(test_ir_frame PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
add_test(NAME test_ir_frame COMMAND test_ir_frame)
endif()
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
-340
View File
@@ -1,340 +0,0 @@
#include "mag160c/mag160c.h"
#include "core/context.hpp"
#include "core/device.hpp"
#include "core/error.hpp"
#include "core/ir_device.hpp"
#include "core/tcm_device.hpp"
#include "core/tcm_frame.hpp"
#include <new>
#include <stdexcept>
#include <vector>
namespace {
constexpr size_t MAX_TCM_PAYLOAD_SIZE = 0xffffU - 5U;
} // namespace
struct mag160c_context_t {
mag160c::core::Context context;
};
struct mag160c_ir_device_t {
mag160c::core::IrDevice device;
};
mag160c_error_t mag160c_init(mag160c_context_t** out_ctx) {
if (out_ctx == nullptr) {
mag160c::core::set_last_error("mag160c_init: out_ctx must not be null");
return MAG160C_ERR_INVALID_ARGUMENT;
}
*out_ctx = nullptr;
try {
*out_ctx = new mag160c_context_t{};
mag160c::core::clear_last_error();
return MAG160C_OK;
} catch (const std::bad_alloc&) {
mag160c::core::set_last_error("mag160c_init: failed to allocate context");
return MAG160C_ERR_INTERNAL;
} catch (...) {
mag160c::core::set_last_error("mag160c_init: unexpected context construction failure");
return MAG160C_ERR_INTERNAL;
}
}
void mag160c_shutdown(mag160c_context_t* ctx) {
delete ctx;
}
const char* mag160c_last_error(void) {
return mag160c::core::last_error();
}
const char* mag160c_error_name(mag160c_error_t code) {
return mag160c::core::error_name(code);
}
mag160c_error_t mag160c_list_devices(
mag160c_context_t* ctx,
mag160c_device_info_t** out_devices,
size_t* out_count
) {
if (ctx == nullptr || out_devices == nullptr || out_count == nullptr) {
mag160c::core::set_last_error(
"mag160c_list_devices: ctx, out_devices, and out_count must not be null"
);
return MAG160C_ERR_INVALID_ARGUMENT;
}
*out_devices = nullptr;
*out_count = 0;
std::vector<mag160c::core::DeviceInfo> devices;
const mag160c_error_t rc = mag160c::core::list_devices(&devices);
if (rc != MAG160C_OK) {
return rc;
}
if (devices.empty()) {
mag160c::core::clear_last_error();
return MAG160C_OK;
}
mag160c_device_info_t* raw = new (std::nothrow) mag160c_device_info_t[devices.size()];
if (raw == nullptr) {
mag160c::core::set_last_error("mag160c_list_devices: allocation failed");
return MAG160C_ERR_INTERNAL;
}
for (size_t i = 0; i < devices.size(); ++i) {
raw[i] = devices[i].c_info;
}
*out_devices = raw;
*out_count = devices.size();
mag160c::core::clear_last_error();
return MAG160C_OK;
}
void mag160c_free_device_list(mag160c_device_info_t* devices) {
delete[] devices;
}
mag160c_error_t mag160c_ir_open_first(
mag160c_context_t* ctx,
mag160c_ir_device_t** out_device
) {
if (ctx == nullptr || out_device == nullptr) {
mag160c::core::set_last_error(
"mag160c_ir_open_first: ctx and out_device must not be null"
);
return MAG160C_ERR_INVALID_ARGUMENT;
}
*out_device = nullptr;
mag160c_ir_device_t* device = new (std::nothrow) mag160c_ir_device_t{};
if (device == nullptr) {
mag160c::core::set_last_error("mag160c_ir_open_first: allocation failed");
return MAG160C_ERR_INTERNAL;
}
*out_device = device;
mag160c::core::clear_last_error();
return MAG160C_OK;
}
void mag160c_ir_close(mag160c_ir_device_t* device) {
delete device;
}
mag160c_error_t mag160c_ir_get_info(
mag160c_ir_device_t* device,
mag160c_ir_info_t* out_info
) {
if (device == nullptr || out_info == nullptr) {
mag160c::core::set_last_error(
"mag160c_ir_get_info: device and out_info must not be null"
);
return MAG160C_ERR_INVALID_ARGUMENT;
}
*out_info = device->device.info();
mag160c::core::clear_last_error();
return MAG160C_OK;
}
mag160c_error_t mag160c_ir_trigger_ffc(mag160c_ir_device_t* device) {
if (device == nullptr) {
mag160c::core::set_last_error("mag160c_ir_trigger_ffc: device must not be null");
return MAG160C_ERR_INVALID_ARGUMENT;
}
return device->device.trigger_ffc();
}
mag160c_error_t mag160c_ir_read_raw_once(
mag160c_ir_device_t* device,
uint8_t* out_bytes,
size_t out_capacity,
size_t* out_size,
int timeout_ms
) {
if (device == nullptr) {
if (out_size != nullptr) {
*out_size = 0;
}
mag160c::core::set_last_error("mag160c_ir_read_raw_once: device must not be null");
return MAG160C_ERR_INVALID_ARGUMENT;
}
return device->device.read_raw_once(out_bytes, out_capacity, out_size, timeout_ms);
}
mag160c_error_t mag160c_tcm_encode_frame(
uint8_t main_cmd,
uint8_t sub_cmd,
uint16_t frame_id,
const uint8_t* payload,
size_t payload_size,
uint8_t* out_bytes,
size_t out_capacity,
size_t* out_size
) {
if (out_size == nullptr) {
mag160c::core::set_last_error("mag160c_tcm_encode_frame: out_size must not be null");
return MAG160C_ERR_INVALID_ARGUMENT;
}
if (payload_size > MAX_TCM_PAYLOAD_SIZE) {
*out_size = 0;
mag160c::core::set_last_error("mag160c_tcm_encode_frame: TCM payload is too large");
return MAG160C_ERR_INVALID_ARGUMENT;
}
if (payload_size != 0 && payload == nullptr) {
*out_size = 0;
mag160c::core::set_last_error(
"mag160c_tcm_encode_frame: payload must not be null when payload_size is nonzero"
);
return MAG160C_ERR_INVALID_ARGUMENT;
}
std::vector<uint8_t> payload_vec;
if (payload_size != 0) {
payload_vec.assign(payload, payload + payload_size);
}
std::vector<uint8_t> encoded;
try {
encoded = mag160c::core::encode_tcm_frame(main_cmd, sub_cmd, frame_id, payload_vec);
} catch (const std::length_error&) {
*out_size = 0;
mag160c::core::set_last_error("mag160c_tcm_encode_frame: TCM payload is too large");
return MAG160C_ERR_INVALID_ARGUMENT;
}
*out_size = encoded.size();
if (out_bytes == nullptr || out_capacity < encoded.size()) {
mag160c::core::set_last_error("mag160c_tcm_encode_frame: output buffer is too small");
return MAG160C_ERR_INVALID_ARGUMENT;
}
for (size_t i = 0; i < encoded.size(); ++i) {
out_bytes[i] = encoded[i];
}
mag160c::core::clear_last_error();
return MAG160C_OK;
}
mag160c_error_t mag160c_tcm_decode_header(
const uint8_t* data,
size_t size,
uint8_t* out_main_cmd,
uint8_t* out_sub_cmd,
uint16_t* out_frame_id,
size_t* out_payload_size
) {
if (out_main_cmd == nullptr || out_sub_cmd == nullptr || out_frame_id == nullptr ||
out_payload_size == nullptr) {
mag160c::core::set_last_error(
"mag160c_tcm_decode_header: all output pointers must not be null"
);
return MAG160C_ERR_INVALID_ARGUMENT;
}
mag160c::core::TcmFrame frame;
const mag160c_error_t rc = mag160c::core::decode_tcm_frame(data, size, &frame);
if (rc != MAG160C_OK) {
return rc;
}
*out_main_cmd = frame.main_cmd;
*out_sub_cmd = frame.sub_cmd;
*out_frame_id = frame.frame_id;
*out_payload_size = frame.payload.size();
mag160c::core::clear_last_error();
return MAG160C_OK;
}
namespace {
mag160c_error_t copy_frame_to_output(
const std::vector<uint8_t>& frame,
uint8_t* out_bytes,
size_t out_capacity,
size_t* out_size
) {
if (out_size == nullptr) {
mag160c::core::set_last_error("TCM frame builder: out_size must not be null");
return MAG160C_ERR_INVALID_ARGUMENT;
}
*out_size = frame.size();
if (out_bytes == nullptr || out_capacity < frame.size()) {
mag160c::core::set_last_error("TCM frame builder: output buffer is too small");
return MAG160C_ERR_INVALID_ARGUMENT;
}
for (size_t i = 0; i < frame.size(); ++i) {
out_bytes[i] = frame[i];
}
mag160c::core::clear_last_error();
return MAG160C_OK;
}
} // namespace
mag160c_error_t mag160c_tcm_build_rotate_frame(
int angle,
uint8_t* out_bytes,
size_t out_capacity,
size_t* out_size
) {
mag160c::core::TcmCommandBuilder builder;
return copy_frame_to_output(builder.build_rotate_frame(angle), out_bytes, out_capacity, out_size);
}
mag160c_error_t mag160c_tcm_build_light_frame(
mag160c_tcm_light_color_t color,
mag160c_tcm_light_mode_t mode,
uint8_t* out_bytes,
size_t out_capacity,
size_t* out_size
) {
switch (color) {
case MAG160C_TCM_LIGHT_OFF:
case MAG160C_TCM_LIGHT_RED:
case MAG160C_TCM_LIGHT_GREEN:
case MAG160C_TCM_LIGHT_BLUE:
case MAG160C_TCM_LIGHT_YELLOW:
break;
default:
if (out_size != nullptr) {
*out_size = 0;
}
mag160c::core::set_last_error("mag160c_tcm_build_light_frame: invalid color");
return MAG160C_ERR_INVALID_ARGUMENT;
}
switch (mode) {
case MAG160C_TCM_LIGHT_STEADY:
case MAG160C_TCM_LIGHT_BLINK:
case MAG160C_TCM_LIGHT_BREATH:
break;
default:
if (out_size != nullptr) {
*out_size = 0;
}
mag160c::core::set_last_error("mag160c_tcm_build_light_frame: invalid mode");
return MAG160C_ERR_INVALID_ARGUMENT;
}
mag160c::core::TcmCommandBuilder builder;
return copy_frame_to_output(builder.build_light_frame(color, mode), out_bytes, out_capacity, out_size);
}
-17
View File
@@ -1,17 +0,0 @@
#include "core/context.hpp"
#ifndef MAG160C_HAS_LIBUSB
#define MAG160C_HAS_LIBUSB 0
#endif
namespace mag160c::core {
Context::Context()
: has_libusb_(MAG160C_HAS_LIBUSB != 0) {}
bool Context::has_libusb() const noexcept {
return has_libusb_;
}
} // namespace mag160c::core
-19
View File
@@ -1,19 +0,0 @@
#ifndef MAG160C_CORE_CONTEXT_HPP
#define MAG160C_CORE_CONTEXT_HPP
namespace mag160c::core {
class Context {
public:
Context();
bool has_libusb() const noexcept;
private:
bool has_libusb_;
};
} // namespace mag160c::core
#endif
-185
View File
@@ -1,185 +0,0 @@
#include "core/device.hpp"
#include "core/error.hpp"
#include <cstring>
#if MAG160C_HAS_LIBUSB
#include <libusb.h>
#endif
namespace mag160c::core {
bool is_in_endpoint(uint8_t address) {
return (address & 0x80U) != 0;
}
bool find_bulk_pair(const std::vector<EndpointDescriptor>& endpoints, EndpointPair* out) {
if (out == nullptr) {
return false;
}
EndpointPair pair{};
for (const EndpointDescriptor& endpoint : endpoints) {
if (endpoint.type != EndpointType::Bulk) {
continue;
}
if (is_in_endpoint(endpoint.address) && pair.bulk_in == 0) {
pair.bulk_in = endpoint.address;
} else if (!is_in_endpoint(endpoint.address) && pair.bulk_out == 0) {
pair.bulk_out = endpoint.address;
}
}
if (pair.bulk_in == 0 || pair.bulk_out == 0) {
return false;
}
*out = pair;
return true;
}
#if MAG160C_HAS_LIBUSB
namespace {
EndpointType endpoint_type_from_libusb(uint8_t attributes) {
switch (attributes & LIBUSB_TRANSFER_TYPE_MASK) {
case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
return EndpointType::Isochronous;
case LIBUSB_TRANSFER_TYPE_BULK:
return EndpointType::Bulk;
case LIBUSB_TRANSFER_TYPE_INTERRUPT:
return EndpointType::Interrupt;
default:
return EndpointType::Other;
}
}
void copy_string_descriptor(
libusb_device_handle* handle,
uint8_t index,
char* out,
size_t out_size
) {
if (out == nullptr || out_size == 0) {
return;
}
out[0] = '\0';
if (handle == nullptr || index == 0) {
return;
}
unsigned char buffer[128] = {};
const int rc = libusb_get_string_descriptor_ascii(handle, index, buffer, sizeof(buffer));
if (rc <= 0) {
return;
}
const size_t n = static_cast<size_t>(rc) < out_size - 1 ? static_cast<size_t>(rc) : out_size - 1;
std::memcpy(out, buffer, n);
out[n] = '\0';
}
} // namespace
#endif
mag160c_error_t list_devices(std::vector<DeviceInfo>* out) {
if (out == nullptr) {
set_last_error("list_devices: out must not be null");
return MAG160C_ERR_INVALID_ARGUMENT;
}
out->clear();
#if !MAG160C_HAS_LIBUSB
set_last_error("list_devices: libusb-1.0 was not available at build time");
return MAG160C_ERR_UNSUPPORTED;
#else
libusb_context* ctx = nullptr;
int rc = libusb_init(&ctx);
if (rc != 0) {
set_last_error("list_devices: libusb_init failed");
return MAG160C_ERR_USB;
}
libusb_device** list = nullptr;
const ssize_t count = libusb_get_device_list(ctx, &list);
if (count < 0) {
libusb_exit(ctx);
set_last_error("list_devices: libusb_get_device_list failed");
return MAG160C_ERR_USB;
}
for (ssize_t i = 0; i < count; ++i) {
libusb_device* device = list[i];
libusb_device_descriptor desc{};
if (libusb_get_device_descriptor(device, &desc) != 0) {
continue;
}
if (desc.idVendor != MAG_IR_VENDOR_ID || desc.idProduct != MAG_IR_PRODUCT_ID) {
continue;
}
libusb_config_descriptor* config = nullptr;
if (libusb_get_active_config_descriptor(device, &config) != 0 || config == nullptr) {
continue;
}
bool added = false;
for (uint8_t iface_index = 0; iface_index < config->bNumInterfaces && !added; ++iface_index) {
const libusb_interface& iface = config->interface[iface_index];
for (int alt_index = 0; alt_index < iface.num_altsetting && !added; ++alt_index) {
const libusb_interface_descriptor& alt = iface.altsetting[alt_index];
std::vector<EndpointDescriptor> endpoints;
endpoints.reserve(alt.bNumEndpoints);
for (uint8_t ep_index = 0; ep_index < alt.bNumEndpoints; ++ep_index) {
const libusb_endpoint_descriptor& ep = alt.endpoint[ep_index];
endpoints.push_back({ep.bEndpointAddress, endpoint_type_from_libusb(ep.bmAttributes)});
}
EndpointPair pair{};
if (!find_bulk_pair(endpoints, &pair)) {
continue;
}
DeviceInfo info{};
info.c_info.vendor_id = desc.idVendor;
info.c_info.product_id = desc.idProduct;
info.c_info.bus = static_cast<uint8_t>(libusb_get_bus_number(device));
info.c_info.address = static_cast<uint8_t>(libusb_get_device_address(device));
info.c_info.interface_number = alt.bInterfaceNumber;
info.c_info.bulk_in_endpoint = pair.bulk_in;
info.c_info.bulk_out_endpoint = pair.bulk_out;
libusb_device_handle* handle = nullptr;
if (libusb_open(device, &handle) == 0) {
copy_string_descriptor(handle, desc.iProduct, info.c_info.product, sizeof(info.c_info.product));
copy_string_descriptor(
handle,
desc.iManufacturer,
info.c_info.manufacturer,
sizeof(info.c_info.manufacturer)
);
copy_string_descriptor(handle, desc.iSerialNumber, info.c_info.serial, sizeof(info.c_info.serial));
libusb_close(handle);
}
out->push_back(info);
added = true;
}
}
libusb_free_config_descriptor(config);
}
libusb_free_device_list(list, 1);
libusb_exit(ctx);
clear_last_error();
return MAG160C_OK;
#endif
}
} // namespace mag160c::core
-41
View File
@@ -1,41 +0,0 @@
#ifndef MAG160C_CORE_DEVICE_HPP
#define MAG160C_CORE_DEVICE_HPP
#include "mag160c/mag160c.h"
#include <cstdint>
#include <vector>
namespace mag160c::core {
constexpr uint16_t MAG_IR_VENDOR_ID = 0x833c;
constexpr uint16_t MAG_IR_PRODUCT_ID = 0x0001;
enum class EndpointType {
Other,
Bulk,
Interrupt,
Isochronous
};
struct EndpointDescriptor {
uint8_t address = 0;
EndpointType type = EndpointType::Other;
};
struct EndpointPair {
uint8_t bulk_in = 0;
uint8_t bulk_out = 0;
};
struct DeviceInfo {
mag160c_device_info_t c_info{};
};
bool is_in_endpoint(uint8_t address);
bool find_bulk_pair(const std::vector<EndpointDescriptor>& endpoints, EndpointPair* out);
mag160c_error_t list_devices(std::vector<DeviceInfo>* out);
} // namespace mag160c::core
#endif
-51
View File
@@ -1,51 +0,0 @@
#include "core/error.hpp"
#include <utility>
namespace mag160c::core {
namespace {
thread_local std::string g_last_error;
} // namespace
const char* error_name(mag160c_error_t code) noexcept {
switch (code) {
case MAG160C_OK:
return "MAG160C_OK";
case MAG160C_ERR_INVALID_ARGUMENT:
return "MAG160C_ERR_INVALID_ARGUMENT";
case MAG160C_ERR_NO_DEVICE:
return "MAG160C_ERR_NO_DEVICE";
case MAG160C_ERR_PERMISSION:
return "MAG160C_ERR_PERMISSION";
case MAG160C_ERR_USB:
return "MAG160C_ERR_USB";
case MAG160C_ERR_TIMEOUT:
return "MAG160C_ERR_TIMEOUT";
case MAG160C_ERR_CHECKSUM:
return "MAG160C_ERR_CHECKSUM";
case MAG160C_ERR_PROTOCOL_UNKNOWN:
return "MAG160C_ERR_PROTOCOL_UNKNOWN";
case MAG160C_ERR_UNSUPPORTED:
return "MAG160C_ERR_UNSUPPORTED";
case MAG160C_ERR_INTERNAL:
return "MAG160C_ERR_INTERNAL";
default:
return "MAG160C_ERR_UNKNOWN_CODE";
}
}
const char* last_error() noexcept {
return g_last_error.c_str();
}
void clear_last_error() {
g_last_error.clear();
}
void set_last_error(std::string message) {
g_last_error = std::move(message);
}
} // namespace mag160c::core
-18
View File
@@ -1,18 +0,0 @@
#ifndef MAG160C_CORE_ERROR_HPP
#define MAG160C_CORE_ERROR_HPP
#include "mag160c/mag160c.h"
#include <string>
namespace mag160c::core {
const char* error_name(mag160c_error_t code) noexcept;
const char* last_error() noexcept;
void clear_last_error();
void set_last_error(std::string message);
} // namespace mag160c::core
#endif
@@ -1,65 +0,0 @@
#include "core/ir_device.hpp"
#include "core/error.hpp"
#include <cstring>
namespace mag160c::core {
namespace {
void copy_literal(char* out, size_t out_size, const char* value) {
if (out == nullptr || out_size == 0) {
return;
}
std::strncpy(out, value, out_size - 1);
out[out_size - 1] = '\0';
}
} // namespace
IrDevice::IrDevice() {
info_.width = 160;
info_.height = 120;
info_.output_width = 160;
info_.output_height = 120;
info_.max_fps = 25;
info_.current_fps = 0;
copy_literal(info_.name, sizeof(info_.name), "MAG160C");
copy_literal(info_.type, sizeof(info_.type), "vendor-bulk-ir");
}
const mag160c_ir_info_t& IrDevice::info() const noexcept {
return info_;
}
mag160c_error_t IrDevice::trigger_ffc() const {
set_last_error(
"IR FFC protocol recovered from libmagcore.so.2.1.1: write {u32 0x6bb6b672, u32 param} "
"on bulk endpoint OUT 0x03, then read response on IN 0x82 (0x1000 max, 2000 ms timeout); "
"live USB transport is not available in this build"
);
return MAG160C_ERR_UNSUPPORTED;
}
mag160c_error_t IrDevice::read_raw_once(
unsigned char* /*out_bytes*/,
size_t /*out_capacity*/,
size_t* out_size,
int /*timeout_ms*/
) const {
if (out_size == nullptr) {
set_last_error("mag160c_ir_read_raw_once: out_size must not be null");
return MAG160C_ERR_INVALID_ARGUMENT;
}
*out_size = 0;
set_last_error(
"IR raw read protocol recovered: start stream with {0x6bb6b673} on OUT 0x03, then read "
"frame stream on IN 0x81 (markers 0x1bb1b11b/0x1bb1b11c, data at +0x1c, size 0x38+len); "
"live USB transport is not available in this build"
);
return MAG160C_ERR_UNSUPPORTED;
}
} // namespace mag160c::core
@@ -1,27 +0,0 @@
#ifndef MAG160C_CORE_IR_DEVICE_HPP
#define MAG160C_CORE_IR_DEVICE_HPP
#include "mag160c/mag160c.h"
namespace mag160c::core {
class IrDevice {
public:
IrDevice();
const mag160c_ir_info_t& info() const noexcept;
mag160c_error_t trigger_ffc() const;
mag160c_error_t read_raw_once(
unsigned char* out_bytes,
size_t out_capacity,
size_t* out_size,
int timeout_ms
) const;
private:
mag160c_ir_info_t info_{};
};
} // namespace mag160c::core
#endif
-107
View File
@@ -1,107 +0,0 @@
#include "core/ir_frame.hpp"
namespace mag160c::core {
mag160c_error_t parse_ir_frame(
const uint8_t* data,
size_t size,
IrFrameHeader* out_header,
const uint16_t** out_pixels
) {
if (data == nullptr || out_header == nullptr || out_pixels == nullptr) {
return MAG160C_ERR_INVALID_ARGUMENT;
}
if (size < MAG160C_IR_FRAME_DATA_OFFSET + 4) {
return MAG160C_ERR_PROTOCOL_UNKNOWN;
}
const uint32_t marker =
static_cast<uint32_t>(data[0]) |
(static_cast<uint32_t>(data[1]) << 8) |
(static_cast<uint32_t>(data[2]) << 16) |
(static_cast<uint32_t>(data[3]) << 24);
if (marker != MAG160C_IR_FRAME_MARKER) {
return MAG160C_ERR_PROTOCOL_UNKNOWN;
}
auto rd32 = [&](size_t off) {
return static_cast<uint32_t>(data[off]) |
(static_cast<uint32_t>(data[off + 1]) << 8) |
(static_cast<uint32_t>(data[off + 2]) << 16) |
(static_cast<uint32_t>(data[off + 3]) << 24);
};
IrFrameHeader h{};
h.frame_counter = rd32(4);
h.data_length = rd32(8);
h.frame_type = rd32(12);
h.period_shutter = rd32(16);
if (h.frame_type > 1) {
return MAG160C_ERR_PROTOCOL_UNKNOWN;
}
if (size < MAG160C_IR_FRAME_OVERHEAD + h.data_length) {
return MAG160C_ERR_PROTOCOL_UNKNOWN;
}
const size_t trailing_offset = MAG160C_IR_FRAME_DATA_OFFSET + h.data_length;
if (rd32(trailing_offset) != MAG160C_IR_FRAME_TRAILING_MARKER) {
return MAG160C_ERR_PROTOCOL_UNKNOWN;
}
h.marker = marker;
*out_header = h;
*out_pixels = reinterpret_cast<const uint16_t*>(data + MAG160C_IR_FRAME_DATA_OFFSET);
return MAG160C_OK;
}
void calibrate_frame(
const uint16_t* frame,
const IrCalibrationTables& tables,
uint16_t* out
) {
if (frame == nullptr || tables.coeff == nullptr || tables.thresholds == nullptr ||
out == nullptr || tables.pixel_count == 0) {
return;
}
const uint32_t pixels = tables.pixel_count;
const uint32_t bands = tables.band_count == 0 ? 1 : tables.band_count;
const uint32_t search_bands = bands - 1;
for (uint32_t p = 0; p < pixels; ++p) {
int32_t diff;
if (tables.has_baseline && tables.baseline != nullptr) {
diff = static_cast<int32_t>(static_cast<int16_t>(
frame[p] - tables.baseline[p]));
} else {
diff = static_cast<int32_t>(frame[p]);
}
diff >>= 1;
uint32_t band = 0;
if (search_bands > 0) {
for (uint32_t i = 0; i < search_bands; ++i) {
if (diff <= tables.thresholds[p * search_bands + i]) {
band = i;
break;
}
band = i + 1;
}
}
const size_t entry = (static_cast<size_t>(band) * pixels + p) * 2;
const uint32_t coeff = tables.coeff[entry];
const uint32_t offset = tables.coeff[entry + 1];
int32_t v = static_cast<int32_t>(offset) + ((diff * static_cast<int32_t>(coeff)) >> 12);
if (v < 0) {
v = 0;
}
if (v > 0xffff) {
v = 0xffff;
}
out[p] = static_cast<uint16_t>(v);
}
}
} // namespace mag160c::core
-57
View File
@@ -1,57 +0,0 @@
#ifndef MAG160C_CORE_IR_FRAME_HPP
#define MAG160C_CORE_IR_FRAME_HPP
#include "mag160c/mag160c.h"
#include <cstddef>
#include <cstdint>
namespace mag160c::core {
struct IrFrameHeader {
uint32_t marker;
uint32_t frame_counter;
uint32_t data_length;
uint32_t frame_type;
uint32_t period_shutter;
};
struct IrCalibrationTables {
const int16_t* thresholds; /* per-pixel band thresholds, [bands] per pixel */
const uint16_t* coeff; /* per-pixel per-band {coeff, offset} pairs, 2*bands per pixel */
uint32_t pixel_count; /* 160*120 = 19200 */
uint32_t band_count; /* thresholds per pixel (band_count-1 searched) */
const uint16_t* baseline; /* optional baseline frame (may be null) */
bool has_baseline;
};
/*
* Parse a frame stream chunk recovered from libmagcore.so.2.1.1:
* marker 0x1bb1b11b @0, frame counter @4, data length @8, type @0xc, shutter @0x10,
* pixel data @0x1c, trailing marker 0x1bb1b11c @0x1c+data_length, total 0x38+data_length.
* Returns MAG160C_OK when a complete, valid frame is present.
*/
mag160c_error_t parse_ir_frame(
const uint8_t* data,
size_t size,
IrFrameHeader* out_header,
const uint16_t** out_pixels
);
/*
* Recovered CFunctions::Calibration piecewise-linear map (libcoresdk.so 0x6a41c):
* diff = (int16)(frame[p] - baseline[p]) >> 1
* band = first index i where diff <= thresholds[p*bands+i] (linear scan, max bands-1)
* v = coeff[(band*pixels + p)*2 + 1] + ((diff * coeff[(band*pixels + p)*2]) >> 12)
* out[p] = clamp(v, 0, 0xffff)
* When no baseline is set, diff = frame[p] >> 1.
*/
void calibrate_frame(
const uint16_t* frame,
const IrCalibrationTables& tables,
uint16_t* out
);
} // namespace mag160c::core
#endif
@@ -1,70 +0,0 @@
#include "core/tcm_device.hpp"
#include <algorithm>
namespace mag160c::core {
namespace {
constexpr uint8_t MAIN_TCM = 0x02;
constexpr uint8_t SUB_ROTATE = 0x77;
constexpr uint8_t SUB_LIGHT_STEADY = 0x31;
constexpr uint8_t SUB_LIGHT_BLINK = 0x32;
constexpr uint8_t SUB_LIGHT_BREATH = 0x33;
uint8_t light_subcommand(mag160c_tcm_light_mode_t mode) {
switch (mode) {
case MAG160C_TCM_LIGHT_STEADY:
return SUB_LIGHT_STEADY;
case MAG160C_TCM_LIGHT_BLINK:
return SUB_LIGHT_BLINK;
case MAG160C_TCM_LIGHT_BREATH:
return SUB_LIGHT_BREATH;
}
return SUB_LIGHT_STEADY;
}
std::vector<uint8_t> light_payload(mag160c_tcm_light_color_t color) {
switch (color) {
case MAG160C_TCM_LIGHT_OFF:
return {0x00, 0x00, 0x00, 0x00};
case MAG160C_TCM_LIGHT_RED:
return {0x01, 0xff, 0x00, 0x00};
case MAG160C_TCM_LIGHT_GREEN:
return {0x01, 0x00, 0xff, 0x00};
case MAG160C_TCM_LIGHT_BLUE:
return {0x01, 0x00, 0x00, 0xff};
case MAG160C_TCM_LIGHT_YELLOW:
return {0x01, 0xff, 0xff, 0x00};
}
return {0x00, 0x00, 0x00, 0x00};
}
} // namespace
TcmCommandBuilder::TcmCommandBuilder() : frame_id_(1) {}
uint16_t TcmCommandBuilder::next_frame_id() {
const uint16_t current = frame_id_;
++frame_id_;
if (frame_id_ >= 0x8000) {
frame_id_ = 1;
}
return current;
}
std::vector<uint8_t> TcmCommandBuilder::build_rotate_frame(int angle) {
angle = std::clamp(angle, -128, 128);
const uint8_t direction = angle < 0 ? 0x01 : 0x00;
const uint8_t magnitude = static_cast<uint8_t>(angle < 0 ? -angle : angle);
return encode_tcm_frame(MAIN_TCM, SUB_ROTATE, next_frame_id(), {direction, magnitude});
}
std::vector<uint8_t> TcmCommandBuilder::build_light_frame(
mag160c_tcm_light_color_t color,
mag160c_tcm_light_mode_t mode
) {
return encode_tcm_frame(MAIN_TCM, light_subcommand(mode), next_frame_id(), light_payload(color));
}
} // namespace mag160c::core
@@ -1,30 +0,0 @@
#ifndef MAG160C_CORE_TCM_DEVICE_HPP
#define MAG160C_CORE_TCM_DEVICE_HPP
#include "core/tcm_frame.hpp"
#include "mag160c/mag160c.h"
#include <cstdint>
#include <vector>
namespace mag160c::core {
class TcmCommandBuilder {
public:
TcmCommandBuilder();
std::vector<uint8_t> build_rotate_frame(int angle);
std::vector<uint8_t> build_light_frame(
mag160c_tcm_light_color_t color,
mag160c_tcm_light_mode_t mode
);
private:
uint16_t next_frame_id();
uint16_t frame_id_;
};
} // namespace mag160c::core
#endif
-101
View File
@@ -1,101 +0,0 @@
#include "core/tcm_frame.hpp"
#include "core/error.hpp"
#include <stdexcept>
namespace mag160c::core {
namespace {
constexpr size_t MAX_TCM_PAYLOAD_SIZE = 0xffffU - 5U;
} // namespace
uint8_t checksum(const uint8_t* data, size_t begin, size_t end) {
if (data == nullptr || begin > end) {
return 0;
}
uint8_t sum = 0;
for (size_t i = begin; i < end; ++i) {
sum = static_cast<uint8_t>(sum + data[i]);
}
return sum;
}
std::vector<uint8_t> encode_tcm_frame(
uint8_t main_cmd,
uint8_t sub_cmd,
uint16_t frame_id,
const std::vector<uint8_t>& payload
) {
if (payload.size() > MAX_TCM_PAYLOAD_SIZE) {
throw std::length_error("TCM payload is too large");
}
const uint16_t body_length = static_cast<uint16_t>(payload.size() + 5U);
std::vector<uint8_t> out(static_cast<size_t>(body_length) + 4U, 0);
out[0] = 0x7e;
out[1] = static_cast<uint8_t>((body_length >> 8) & 0xff);
out[2] = static_cast<uint8_t>(body_length & 0xff);
out[3] = checksum(out.data(), 0, 3);
out[4] = main_cmd;
out[5] = sub_cmd;
out[6] = static_cast<uint8_t>((frame_id >> 8) & 0xff);
out[7] = static_cast<uint8_t>(frame_id & 0xff);
for (size_t i = 0; i < payload.size(); ++i) {
out[8 + i] = payload[i];
}
out[out.size() - 1] = checksum(out.data(), 4, out.size() - 1);
return out;
}
mag160c_error_t decode_tcm_frame(const uint8_t* data, size_t size, TcmFrame* out) {
if (data == nullptr || out == nullptr) {
set_last_error("decode_tcm_frame: data and out must not be null");
return MAG160C_ERR_INVALID_ARGUMENT;
}
if (size < 9) {
set_last_error("decode_tcm_frame: packet shorter than minimum TCM frame");
return MAG160C_ERR_INVALID_ARGUMENT;
}
if (data[0] != 0x7e) {
set_last_error("decode_tcm_frame: missing 0x7e header byte");
return MAG160C_ERR_CHECKSUM;
}
if (data[3] != checksum(data, 0, 3)) {
set_last_error("decode_tcm_frame: invalid header checksum");
return MAG160C_ERR_CHECKSUM;
}
const uint16_t body_length =
static_cast<uint16_t>((static_cast<uint16_t>(data[1]) << 8) | data[2]);
const size_t expected_size = static_cast<size_t>(body_length) + 4U;
if (size != expected_size) {
set_last_error("decode_tcm_frame: packet size does not match encoded body length");
return MAG160C_ERR_INVALID_ARGUMENT;
}
if (data[size - 1] != checksum(data, 4, size - 1)) {
set_last_error("decode_tcm_frame: invalid body checksum");
return MAG160C_ERR_CHECKSUM;
}
out->main_cmd = data[4];
out->sub_cmd = data[5];
out->frame_id = static_cast<uint16_t>((static_cast<uint16_t>(data[6]) << 8) | data[7]);
out->payload.assign(data + 8, data + size - 1);
clear_last_error();
return MAG160C_OK;
}
} // namespace mag160c::core
@@ -1,30 +0,0 @@
#ifndef MAG160C_CORE_TCM_FRAME_HPP
#define MAG160C_CORE_TCM_FRAME_HPP
#include "mag160c/mag160c.h"
#include <cstddef>
#include <cstdint>
#include <vector>
namespace mag160c::core {
struct TcmFrame {
uint8_t main_cmd = 0;
uint8_t sub_cmd = 0;
uint16_t frame_id = 0;
std::vector<uint8_t> payload;
};
uint8_t checksum(const uint8_t* data, size_t begin, size_t end);
std::vector<uint8_t> encode_tcm_frame(
uint8_t main_cmd,
uint8_t sub_cmd,
uint16_t frame_id,
const std::vector<uint8_t>& payload
);
mag160c_error_t decode_tcm_frame(const uint8_t* data, size_t size, TcmFrame* out);
} // namespace mag160c::core
#endif
@@ -1,20 +0,0 @@
#ifndef MAG160C_CORE_TRANSPORT_HPP
#define MAG160C_CORE_TRANSPORT_HPP
#include "mag160c/mag160c.h"
#include <cstdint>
#include <vector>
namespace mag160c::core {
class Transport {
public:
virtual ~Transport() = default;
virtual mag160c_error_t write(const std::vector<uint8_t>& bytes) = 0;
virtual mag160c_error_t read(std::vector<uint8_t>* out, int timeout_ms) = 0;
};
} // namespace mag160c::core
#endif
@@ -1,106 +0,0 @@
#include "mag160c/mag160c.h"
#include <cassert>
#include <cstdint>
#include <cstring>
#include <vector>
int main() {
assert(std::strcmp(mag160c_error_name(MAG160C_OK), "MAG160C_OK") == 0);
assert(std::strcmp(mag160c_error_name(MAG160C_ERR_PROTOCOL_UNKNOWN),
"MAG160C_ERR_PROTOCOL_UNKNOWN") == 0);
assert(std::strcmp(mag160c_error_name(static_cast<mag160c_error_t>(9999)),
"MAG160C_ERR_UNKNOWN_CODE") == 0);
assert(mag160c_init(nullptr) == MAG160C_ERR_INVALID_ARGUMENT);
assert(std::strstr(mag160c_last_error(), "out_ctx") != nullptr);
mag160c_context_t* ctx = nullptr;
assert(mag160c_init(&ctx) == MAG160C_OK);
assert(ctx != nullptr);
assert(std::strcmp(mag160c_last_error(), "") == 0);
mag160c_shutdown(ctx);
mag160c_shutdown(nullptr);
assert(mag160c_list_devices(nullptr, nullptr, nullptr) == MAG160C_ERR_INVALID_ARGUMENT);
ctx = nullptr;
assert(mag160c_init(&ctx) == MAG160C_OK);
mag160c_device_info_t* devices = reinterpret_cast<mag160c_device_info_t*>(0x1);
size_t device_count = 42;
#if MAG160C_HAS_LIBUSB
assert(mag160c_list_devices(ctx, &devices, &device_count) == MAG160C_OK);
mag160c_free_device_list(devices);
#else
assert(mag160c_list_devices(ctx, &devices, &device_count) == MAG160C_ERR_UNSUPPORTED);
assert(devices == nullptr);
assert(device_count == 0);
assert(std::strstr(mag160c_last_error(), "libusb") != nullptr);
mag160c_free_device_list(devices);
#endif
mag160c_shutdown(ctx);
const uint8_t payload[] = {0x00, 0x05};
const uint8_t expected[] = {
0x7e, 0x00, 0x07, 0x85, 0x02, 0x77, 0x00, 0x01, 0x00, 0x05, 0x7f};
size_t encoded_size = 0;
assert(mag160c_tcm_encode_frame(0x02, 0x77, 0x0001, payload, sizeof(payload), nullptr, 0,
&encoded_size) == MAG160C_ERR_INVALID_ARGUMENT);
assert(encoded_size == sizeof(expected));
uint8_t encoded[sizeof(expected)] = {};
assert(mag160c_tcm_encode_frame(0x02, 0x77, 0x0001, payload, sizeof(payload), encoded,
sizeof(encoded), &encoded_size) == MAG160C_OK);
assert(encoded_size == sizeof(expected));
assert(std::memcmp(encoded, expected, sizeof(expected)) == 0);
uint8_t main_cmd = 0;
uint8_t sub_cmd = 0;
uint16_t frame_id = 0;
size_t payload_size = 0;
assert(mag160c_tcm_decode_header(encoded, encoded_size, &main_cmd, &sub_cmd, &frame_id,
&payload_size) == MAG160C_OK);
assert(main_cmd == 0x02);
assert(sub_cmd == 0x77);
assert(frame_id == 0x0001);
assert(payload_size == sizeof(payload));
size_t rotate_size = 0;
assert(mag160c_tcm_build_rotate_frame(5, nullptr, 0, &rotate_size) ==
MAG160C_ERR_INVALID_ARGUMENT);
assert(rotate_size == sizeof(expected));
uint8_t rotate_frame[sizeof(expected)] = {};
assert(mag160c_tcm_build_rotate_frame(5, rotate_frame, sizeof(rotate_frame), &rotate_size) ==
MAG160C_OK);
assert(rotate_size == sizeof(expected));
assert(std::memcmp(rotate_frame, expected, sizeof(expected)) == 0);
const uint8_t expected_green_blink[] = {
0x7e, 0x00, 0x09, 0x87, 0x02, 0x32, 0x00, 0x01, 0x01, 0x00, 0xff, 0x00, 0x35};
size_t light_size = 0;
uint8_t light_frame[sizeof(expected_green_blink)] = {};
assert(mag160c_tcm_build_light_frame(MAG160C_TCM_LIGHT_GREEN, MAG160C_TCM_LIGHT_BLINK,
light_frame, sizeof(light_frame), &light_size) ==
MAG160C_OK);
assert(light_size == sizeof(expected_green_blink));
assert(std::memcmp(light_frame, expected_green_blink, sizeof(expected_green_blink)) == 0);
assert(mag160c_tcm_build_light_frame(static_cast<mag160c_tcm_light_color_t>(99),
MAG160C_TCM_LIGHT_BLINK, light_frame,
sizeof(light_frame), &light_size) ==
MAG160C_ERR_INVALID_ARGUMENT);
assert(std::strstr(mag160c_last_error(), "color") != nullptr);
const std::vector<uint8_t> oversized_payload(65531U, 0xaa);
encoded_size = 123U;
assert(mag160c_tcm_encode_frame(0x02, 0x77, 0x0001, oversized_payload.data(),
oversized_payload.size(), nullptr, 0, &encoded_size) ==
MAG160C_ERR_INVALID_ARGUMENT);
assert(encoded_size == 0);
assert(std::strstr(mag160c_last_error(), "too large") != nullptr);
return 0;
}
@@ -1,34 +0,0 @@
#include "core/device.hpp"
#include <cassert>
#include <vector>
int main() {
using mag160c::core::EndpointDescriptor;
using mag160c::core::EndpointPair;
using mag160c::core::EndpointType;
const std::vector<EndpointDescriptor> endpoints = {
{0x01, EndpointType::Interrupt},
{0x82, EndpointType::Bulk},
{0x03, EndpointType::Bulk},
};
EndpointPair pair{};
assert(mag160c::core::find_bulk_pair(endpoints, &pair));
assert(pair.bulk_in == 0x82);
assert(pair.bulk_out == 0x03);
const std::vector<EndpointDescriptor> missing_out = {
{0x82, EndpointType::Bulk},
};
pair = {};
assert(!mag160c::core::find_bulk_pair(missing_out, &pair));
assert(!mag160c::core::find_bulk_pair(endpoints, nullptr));
assert(mag160c::core::is_in_endpoint(0x82));
assert(!mag160c::core::is_in_endpoint(0x03));
return 0;
}
@@ -1,180 +0,0 @@
#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;
}
@@ -1,46 +0,0 @@
#include "mag160c/mag160c.h"
#include <cassert>
#include <cstring>
int main() {
assert(mag160c_ir_open_first(nullptr, nullptr) == MAG160C_ERR_INVALID_ARGUMENT);
assert(std::strstr(mag160c_last_error(), "ctx") != nullptr);
mag160c_ir_close(nullptr);
assert(mag160c_ir_get_info(nullptr, nullptr) == MAG160C_ERR_INVALID_ARGUMENT);
assert(std::strstr(mag160c_last_error(), "device") != nullptr);
mag160c_context_t* ctx = nullptr;
assert(mag160c_init(&ctx) == MAG160C_OK);
mag160c_ir_device_t* ir = nullptr;
assert(mag160c_ir_open_first(ctx, &ir) == MAG160C_OK);
assert(ir != nullptr);
mag160c_ir_info_t info{};
assert(mag160c_ir_get_info(ir, &info) == MAG160C_OK);
assert(info.width == 160);
assert(info.height == 120);
assert(info.output_width == 160);
assert(info.output_height == 120);
assert(info.max_fps == 25);
assert(info.current_fps == 0);
assert(std::strcmp(info.name, "MAG160C") == 0);
assert(std::strcmp(info.type, "vendor-bulk-ir") == 0);
assert(mag160c_ir_trigger_ffc(ir) == MAG160C_ERR_UNSUPPORTED);
assert(std::strstr(mag160c_last_error(), "0x6bb6b672") != nullptr);
assert(std::strstr(mag160c_last_error(), "0x03") != nullptr);
size_t raw_size = 123;
assert(mag160c_ir_read_raw_once(ir, nullptr, 0, &raw_size, 100) ==
MAG160C_ERR_UNSUPPORTED);
assert(raw_size == 0);
assert(std::strstr(mag160c_last_error(), "0x1bb1b11b") != nullptr);
mag160c_ir_close(ir);
mag160c_shutdown(ctx);
return 0;
}
@@ -1,49 +0,0 @@
#include "core/tcm_device.hpp"
#include <cassert>
#include <cstdint>
#include <vector>
namespace {
void test_rotate_positive_payload() {
mag160c::core::TcmCommandBuilder builder;
const std::vector<uint8_t> expected{
0x7e, 0x00, 0x07, 0x85, 0x02, 0x77, 0x00, 0x01, 0x00, 0x05, 0x7f};
const auto encoded = builder.build_rotate_frame(5);
assert(encoded == expected);
}
void test_rotate_negative_payload_and_frame_increment() {
mag160c::core::TcmCommandBuilder builder;
(void)builder.build_rotate_frame(5);
const std::vector<uint8_t> expected{
0x7e, 0x00, 0x07, 0x85, 0x02, 0x77, 0x00, 0x02, 0x01, 0x03, 0x7f};
const auto encoded = builder.build_rotate_frame(-3);
assert(encoded == expected);
}
void test_green_blink_payload() {
mag160c::core::TcmCommandBuilder builder;
const std::vector<uint8_t> expected{
0x7e, 0x00, 0x09, 0x87, 0x02, 0x32, 0x00, 0x01, 0x01, 0x00, 0xff, 0x00, 0x35};
const auto encoded = builder.build_light_frame(MAG160C_TCM_LIGHT_GREEN, MAG160C_TCM_LIGHT_BLINK);
assert(encoded == expected);
}
} // namespace
int main() {
test_rotate_positive_payload();
test_rotate_negative_payload_and_frame_increment();
test_green_blink_payload();
return 0;
}
@@ -1,64 +0,0 @@
#include "core/tcm_frame.hpp"
#include <cassert>
#include <cstdint>
#include <stdexcept>
#include <vector>
namespace {
void test_rotate_frame_encoding() {
const std::vector<uint8_t> payload{0x00, 0x05};
const std::vector<uint8_t> expected{
0x7e, 0x00, 0x07, 0x85, 0x02, 0x77, 0x00, 0x01, 0x00, 0x05, 0x7f};
const auto encoded = mag160c::core::encode_tcm_frame(0x02, 0x77, 0x0001, payload);
assert(encoded == expected);
}
void test_decode_rejects_bad_header_checksum() {
auto encoded = mag160c::core::encode_tcm_frame(0x02, 0x77, 0x0001, {0x00, 0x05});
encoded[3] ^= 0xff;
mag160c::core::TcmFrame decoded;
assert(mag160c::core::decode_tcm_frame(encoded.data(), encoded.size(), &decoded) ==
MAG160C_ERR_CHECKSUM);
}
void test_decode_roundtrip() {
const std::vector<uint8_t> payload{0x10, 0x20, 0x30};
const auto encoded = mag160c::core::encode_tcm_frame(0xa1, 0xb2, 0xc3d4, payload);
mag160c::core::TcmFrame decoded;
assert(mag160c::core::decode_tcm_frame(encoded.data(), encoded.size(), &decoded) == MAG160C_OK);
assert(decoded.main_cmd == 0xa1);
assert(decoded.sub_cmd == 0xb2);
assert(decoded.frame_id == 0xc3d4);
assert(decoded.payload == payload);
}
void test_encode_rejects_oversized_payload() {
const std::vector<uint8_t> payload(65531U, 0xaa);
bool threw_length_error = false;
try {
(void)mag160c::core::encode_tcm_frame(0x02, 0x77, 0x0001, payload);
} catch (const std::length_error&) {
threw_length_error = true;
}
assert(threw_length_error);
}
} // namespace
int main() {
test_rotate_frame_encoding();
test_decode_rejects_bad_header_checksum();
test_decode_roundtrip();
test_encode_rejects_oversized_payload();
return 0;
}
-232
View File
@@ -1,232 +0,0 @@
#include "mag160c/mag160c.h"
#include <cctype>
#include <cstdlib>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <string>
namespace {
void print_usage() {
std::cout << "usage:\n"
<< " mag160c-cli probe\n"
<< " mag160c-cli tcm-rotate --dry-run ANGLE\n"
<< " mag160c-cli tcm-light --dry-run COLOR MODE\n"
<< " mag160c-cli ir-info\n";
}
std::string lower(std::string value) {
for (char& ch : value) {
ch = static_cast<char>(std::tolower(static_cast<unsigned char>(ch)));
}
return value;
}
void print_hex(const uint8_t* bytes, size_t size) {
std::ios old_state(nullptr);
old_state.copyfmt(std::cout);
for (size_t i = 0; i < size; ++i) {
if (i != 0) {
std::cout << ' ';
}
std::cout << std::hex << std::setfill('0') << std::setw(2)
<< static_cast<unsigned int>(bytes[i]);
}
std::cout << '\n';
std::cout.copyfmt(old_state);
}
int parse_int(const char* text, int* out) {
if (text == nullptr || out == nullptr) {
return 0;
}
char* end = nullptr;
const long value = std::strtol(text, &end, 10);
if (end == text || *end != '\0') {
return 0;
}
*out = static_cast<int>(value);
return 1;
}
int command_tcm_rotate(int argc, char** argv) {
if (argc != 4 || std::string(argv[2]) != "--dry-run") {
print_usage();
return 1;
}
int angle = 0;
if (!parse_int(argv[3], &angle)) {
std::cerr << "invalid angle\n";
return 1;
}
uint8_t frame[64] = {};
size_t frame_size = 0;
const mag160c_error_t rc = mag160c_tcm_build_rotate_frame(
angle, frame, sizeof(frame), &frame_size
);
if (rc != MAG160C_OK) {
std::cerr << mag160c_error_name(rc) << ": " << mag160c_last_error() << '\n';
return 2;
}
print_hex(frame, frame_size);
return 0;
}
int parse_color(const std::string& value, mag160c_tcm_light_color_t* out) {
const std::string v = lower(value);
if (v == "off") { *out = MAG160C_TCM_LIGHT_OFF; return 1; }
if (v == "red") { *out = MAG160C_TCM_LIGHT_RED; return 1; }
if (v == "green") { *out = MAG160C_TCM_LIGHT_GREEN; return 1; }
if (v == "blue") { *out = MAG160C_TCM_LIGHT_BLUE; return 1; }
if (v == "yellow") { *out = MAG160C_TCM_LIGHT_YELLOW; return 1; }
return 0;
}
int parse_mode(const std::string& value, mag160c_tcm_light_mode_t* out) {
const std::string v = lower(value);
if (v == "steady") { *out = MAG160C_TCM_LIGHT_STEADY; return 1; }
if (v == "blink") { *out = MAG160C_TCM_LIGHT_BLINK; return 1; }
if (v == "breath") { *out = MAG160C_TCM_LIGHT_BREATH; return 1; }
return 0;
}
int command_tcm_light(int argc, char** argv) {
if (argc != 5 || std::string(argv[2]) != "--dry-run") {
print_usage();
return 1;
}
mag160c_tcm_light_color_t color = MAG160C_TCM_LIGHT_OFF;
mag160c_tcm_light_mode_t mode = MAG160C_TCM_LIGHT_STEADY;
if (!parse_color(argv[3], &color)) {
std::cerr << "invalid color\n";
return 1;
}
if (!parse_mode(argv[4], &mode)) {
std::cerr << "invalid mode\n";
return 1;
}
uint8_t frame[64] = {};
size_t frame_size = 0;
const mag160c_error_t rc = mag160c_tcm_build_light_frame(
color, mode, frame, sizeof(frame), &frame_size
);
if (rc != MAG160C_OK) {
std::cerr << mag160c_error_name(rc) << ": " << mag160c_last_error() << '\n';
return 2;
}
print_hex(frame, frame_size);
return 0;
}
int command_ir_info() {
mag160c_context_t* ctx = nullptr;
mag160c_ir_device_t* ir = nullptr;
mag160c_ir_info_t info{};
mag160c_error_t rc = mag160c_init(&ctx);
if (rc != MAG160C_OK) {
std::cerr << mag160c_error_name(rc) << ": " << mag160c_last_error() << '\n';
return 2;
}
rc = mag160c_ir_open_first(ctx, &ir);
if (rc == MAG160C_OK) {
rc = mag160c_ir_get_info(ir, &info);
}
if (rc != MAG160C_OK) {
std::cerr << mag160c_error_name(rc) << ": " << mag160c_last_error() << '\n';
mag160c_ir_close(ir);
mag160c_shutdown(ctx);
return 2;
}
std::cout << "name: " << info.name << '\n'
<< "type: " << info.type << '\n'
<< "size: " << info.width << "x" << info.height << '\n'
<< "output: " << info.output_width << "x" << info.output_height << '\n'
<< "protocol: vid 0x833c config=2 iface=0; cmd EP OUT 0x03 / IN 0x82;\n"
<< " stream EP IN 0x81 (marker 0x1bb1b11b, data at +0x1c, size 0x38+len);\n"
<< " cmds 0x6bb6b66b..0x6bb6b677 (start 0x6bb6b673, stop 0x6bb6b674,\n"
<< " ffc 0x6bb6b672); responses 0x5bb5b55b..0x5bb5b57b\n";
mag160c_ir_close(ir);
mag160c_shutdown(ctx);
return 0;
}
int command_probe() {
mag160c_context_t* ctx = nullptr;
mag160c_error_t rc = mag160c_init(&ctx);
if (rc != MAG160C_OK) {
std::cerr << mag160c_error_name(rc) << ": " << mag160c_last_error() << '\n';
return 2;
}
mag160c_device_info_t* devices = nullptr;
size_t count = 0;
rc = mag160c_list_devices(ctx, &devices, &count);
if (rc != MAG160C_OK) {
std::cerr << mag160c_error_name(rc) << ": " << mag160c_last_error() << '\n';
mag160c_shutdown(ctx);
return 2;
}
std::cout << "devices: " << count << '\n';
std::ios old_state(nullptr);
old_state.copyfmt(std::cout);
for (size_t i = 0; i < count; ++i) {
const mag160c_device_info_t& d = devices[i];
std::cout << "[" << i << "] vid:pid "
<< std::hex << std::setfill('0') << std::setw(4) << d.vendor_id
<< ':' << std::setw(4) << d.product_id
<< std::dec << " bus " << static_cast<unsigned int>(d.bus)
<< " address " << static_cast<unsigned int>(d.address)
<< " interface " << static_cast<unsigned int>(d.interface_number)
<< " bulk-in 0x" << std::hex << static_cast<unsigned int>(d.bulk_in_endpoint)
<< " bulk-out 0x" << static_cast<unsigned int>(d.bulk_out_endpoint)
<< '\n';
}
std::cout.copyfmt(old_state);
mag160c_free_device_list(devices);
mag160c_shutdown(ctx);
return 0;
}
} // namespace
int main(int argc, char** argv) {
if (argc < 2) {
print_usage();
return 1;
}
const std::string command = argv[1];
if (command == "probe") {
return command_probe();
}
if (command == "tcm-rotate") {
return command_tcm_rotate(argc, argv);
}
if (command == "tcm-light") {
return command_tcm_light(argc, argv);
}
if (command == "ir-info") {
return command_ir_info();
}
print_usage();
return 1;
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
-3
View File
@@ -1,3 +0,0 @@
0 0 208192682 10843 0 45340
1 0 208110489 10839 0 45340
2 0 208188334 10843 0 45340
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More