270 lines
11 KiB
C
270 lines
11 KiB
C
/*
|
|
* MAG160C thermal camera SDK - pure C implementation.
|
|
*
|
|
* Protocol recovered from the vendor binaries:
|
|
* - Linux libmagcore.so.2.1.1 (x86-64) : link/config/command/frame-stream
|
|
* - Android libcoresdk.so (ARM64) : temperature conversion (CFunctions)
|
|
* - Windows CoreSDKLib.dll : cross-validation of all magic codes
|
|
* Full details in analysis/protocol_spec.md.
|
|
*
|
|
* USB topology (recovered):
|
|
* VID 0x833C, config value 2, interface 0
|
|
* EP OUT 0x03 : commands EP IN 0x82 : command responses
|
|
* EP IN 0x81 : frame stream EP IN 0x84 : bulk/large reads
|
|
*
|
|
* Commands are 8-byte packets {u32 magic, u32 param} (or 0x3c-byte with a
|
|
* 0x38-byte payload for DDT). Response magic codes: 0x5BB5B55B..0x5BB5B57B.
|
|
*/
|
|
#ifndef MAG160C_H
|
|
#define MAG160C_H
|
|
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
#if defined(MAG160C_STATIC)
|
|
# define MAG160C_API
|
|
#elif defined(_WIN32)
|
|
# if defined(MAG160C_BUILDING_LIBRARY)
|
|
# define MAG160C_API __declspec(dllexport)
|
|
# else
|
|
# define MAG160C_API __declspec(dllimport)
|
|
# endif
|
|
#else
|
|
# define MAG160C_API
|
|
#endif
|
|
|
|
typedef enum mag160c_error_t {
|
|
MAG160C_OK = 0,
|
|
MAG160C_ERR_INVALID_ARGUMENT = 1,
|
|
MAG160C_ERR_NO_MEMORY = 2,
|
|
MAG160C_ERR_NOT_READY = 3, /* vendor 0xe4ae0003 */
|
|
MAG160C_ERR_NOT_INITIALIZED = 4, /* vendor 0xe4ae0004 */
|
|
MAG160C_ERR_NOT_OPEN = 5, /* vendor 0xe4ae0005 */
|
|
MAG160C_ERR_NOT_SUPPORTED = 6, /* vendor 0xe4ae0006 */
|
|
MAG160C_ERR_FAILED = 7, /* vendor 0xe4ae0007 */
|
|
MAG160C_ERR_USB = 8,
|
|
MAG160C_ERR_TIMEOUT = 9,
|
|
MAG160C_ERR_CHECKSUM = 10,
|
|
MAG160C_ERR_BAD_FRAME = 11,
|
|
MAG160C_ERR_INTERNAL = 12
|
|
} mag160c_error_t;
|
|
|
|
/* ---- IR camera identity / info ---------------------------------------- */
|
|
|
|
#define MAG160C_IR_VENDOR_ID 0x833c
|
|
#define MAG160C_IR_CONFIG_VALUE 2
|
|
#define MAG160C_IR_INTERFACE_NUMBER 0
|
|
#define MAG160C_IR_EP_CMD_OUT 0x03
|
|
#define MAG160C_IR_EP_CMD_IN 0x82
|
|
#define MAG160C_IR_EP_STREAM_IN 0x81
|
|
#define MAG160C_IR_EP_BULK_IN 0x84
|
|
|
|
typedef struct mag160c_ir_info_t {
|
|
uint16_t width; /* 0xa0 = 160 */
|
|
uint16_t height; /* 0x78 = 120 */
|
|
uint16_t fpa_width;
|
|
uint16_t fpa_height;
|
|
uint32_t pid;
|
|
uint32_t serial_lo; /* info block +0x08 */
|
|
uint32_t serial_hi;
|
|
uint32_t max_fps;
|
|
char name[64];
|
|
} mag160c_ir_info_t;
|
|
|
|
/* ---- vendor magic codes (recovered) ------------------------------------ */
|
|
|
|
#define MAG160C_MAG_CMD_PREPARE1 0x6bb6b66b
|
|
#define MAG160C_MAG_CMD_PREPARE2 0x6bb6b66c
|
|
#define MAG160C_MAG_CMD_DDT_WRITE 0x6bb6b66d
|
|
#define MAG160C_MAG_CMD_DDT_READ 0x6bb6b66e
|
|
#define MAG160C_MAG_CMD_GET_INFO 0x6bb6b66f
|
|
#define MAG160C_MAG_CMD_GET_VERSION 0x6bb6b670
|
|
#define MAG160C_MAG_CMD_FFC 0x6bb6b672
|
|
#define MAG160C_MAG_CMD_START 0x6bb6b673
|
|
#define MAG160C_MAG_CMD_STOP 0x6bb6b674
|
|
#define MAG160C_MAG_CMD_SET_A 0x6bb6b676
|
|
#define MAG160C_MAG_CMD_SET_B 0x6bb6b677
|
|
|
|
#define MAG160C_MAG_RSP_INFO_0 0x5bb5b55b
|
|
#define MAG160C_MAG_RSP_INFO_1 0x5bb5b55c
|
|
#define MAG160C_MAG_RSP_INFO_2 0x5bb5b55d
|
|
#define MAG160C_MAG_RSP_PAIR 0x5bb5b55e
|
|
#define MAG160C_MAG_RSP_INFO_3 0x5bb5b55f
|
|
#define MAG160C_MAG_RSP_FILE 0x5bb5b57b
|
|
|
|
/* ---- frame stream ------------------------------------------------------- */
|
|
|
|
#define MAG160C_FRAME_MARKER 0x1bb1b11b
|
|
#define MAG160C_FRAME_TRAILING_MARKER 0x1bb1b11c
|
|
#define MAG160C_FRAME_DATA_OFFSET 0x1c
|
|
#define MAG160C_FRAME_OVERHEAD 0x38
|
|
#define MAG160C_FRAME_MAX_PIXELS 0x10000 /* 160x120 and 384x288 both fit */
|
|
|
|
typedef enum mag160c_frame_type_t {
|
|
MAG160C_FRAME_RESPONSE = 0,
|
|
MAG160C_FRAME_RAW = 1
|
|
} mag160c_frame_type_t;
|
|
|
|
typedef struct mag160c_frame_header_t {
|
|
uint32_t marker;
|
|
uint32_t frame_counter;
|
|
uint32_t data_length; /* bytes of pixel data at +0x1c */
|
|
uint32_t frame_type;
|
|
uint32_t period_shutter;
|
|
uint32_t trailing_marker;
|
|
} mag160c_frame_header_t;
|
|
|
|
/* ---- temperature pipeline (recovered CFunctions) ------------------------ */
|
|
|
|
typedef struct mag160c_temp_tables_t {
|
|
const int16_t *thresholds; /* [pixels][bands-1] int16, or NULL */
|
|
const uint16_t *pwl; /* [band][pixel] {coeff,offset} pairs, or NULL */
|
|
uint32_t pixel_count; /* 160*120 = 19200 */
|
|
uint32_t band_count; /* thresholds per pixel + 1 */
|
|
const uint16_t *baseline; /* optional, or NULL */
|
|
uint32_t has_baseline;
|
|
} mag160c_temp_tables_t;
|
|
|
|
/* ---- device handles ----------------------------------------------------- */
|
|
|
|
typedef struct mag160c_ctx_t mag160c_ctx_t;
|
|
typedef struct mag160c_ir_t mag160c_ir_t;
|
|
|
|
/* ---- lifecycle ---------------------------------------------------------- */
|
|
|
|
MAG160C_API mag160c_error_t mag160c_init(mag160c_ctx_t **out_ctx);
|
|
MAG160C_API void mag160c_shutdown(mag160c_ctx_t *ctx);
|
|
MAG160C_API const char *mag160c_last_error(void);
|
|
MAG160C_API const char *mag160c_error_name(mag160c_error_t code);
|
|
|
|
/* ---- IR camera session --------------------------------------------------- */
|
|
|
|
MAG160C_API mag160c_error_t mag160c_ir_open(mag160c_ctx_t *ctx, mag160c_ir_t **out_ir);
|
|
MAG160C_API void mag160c_ir_close(mag160c_ir_t *ir);
|
|
MAG160C_API mag160c_error_t mag160c_ir_is_linked(mag160c_ir_t *ir);
|
|
MAG160C_API mag160c_error_t mag160c_ir_get_info(mag160c_ir_t *ir, mag160c_ir_info_t *out_info);
|
|
|
|
/* Start: allocates stream buffers, starts reader + dispatcher threads,
|
|
* waits 50 ms, sends MAG_CMD_START. */
|
|
MAG160C_API mag160c_error_t mag160c_ir_start(mag160c_ir_t *ir);
|
|
MAG160C_API mag160c_error_t mag160c_ir_stop(mag160c_ir_t *ir);
|
|
MAG160C_API mag160c_error_t mag160c_ir_trigger_ffc(mag160c_ir_t *ir, uint32_t param);
|
|
MAG160C_API mag160c_error_t mag160c_ir_set_ffc_mode(mag160c_ir_t *ir, uint32_t mode);
|
|
|
|
/* Attach an FFC scheduler (mag160c/mag160c_display.h). The reader thread
|
|
* ticks it after every complete frame and sends the requested FFC command,
|
|
* replicating the official cadence that keeps the type=0 stream alive.
|
|
* Pass own=1 to have the IR session free a heap-allocated scheduler. */
|
|
struct mag160c_ffc_scheduler_t;
|
|
MAG160C_API mag160c_error_t mag160c_ir_set_ffc_scheduler(mag160c_ir_t *ir,
|
|
struct mag160c_ffc_scheduler_t *sched,
|
|
int own);
|
|
MAG160C_API mag160c_error_t mag160c_ir_prepare(mag160c_ir_t *ir);
|
|
MAG160C_API mag160c_error_t mag160c_ir_reset(mag160c_ir_t *ir);
|
|
|
|
/* Poll a freshly parsed frame from the dispatcher. out_frame must be at
|
|
* least data_length + FRAME_OVERHEAD bytes. Returns MAG160C_OK with
|
|
* *out_length = 0 when no complete frame is buffered yet. */
|
|
typedef void (*mag160c_frame_cb_t)(uint32_t frame_index, const uint8_t *data,
|
|
size_t data_len, void *user);
|
|
|
|
MAG160C_API mag160c_error_t mag160c_ir_set_frame_callback(mag160c_ir_t *ir,
|
|
mag160c_frame_cb_t cb,
|
|
void *user);
|
|
|
|
/* Read latest converted temperature for a probe position (x, y) in
|
|
* millidegrees Celsius scaled by 100 (i.e. value/100000 = °C is NOT used;
|
|
* see protocol spec section 7.5). Uses window average then ReviseTemperature
|
|
* + CorrectTemperature when calibration tables are installed. */
|
|
MAG160C_API mag160c_error_t mag160c_ir_read_temperature(mag160c_ir_t *ir,
|
|
uint32_t x, uint32_t y,
|
|
int32_t *out_temp);
|
|
|
|
/* ---- frame parse (pure, no USB needed) ----------------------------------- */
|
|
|
|
MAG160C_API mag160c_error_t mag160c_frame_parse(const uint8_t *data, size_t size,
|
|
mag160c_frame_header_t *out_hdr,
|
|
const uint16_t **out_pixels);
|
|
|
|
/* Streaming assembler mirroring the vendor reader thread. */
|
|
typedef struct mag160c_frame_stream_t {
|
|
uint8_t *buf; /* 2*max_frame_len + 0x470 */
|
|
size_t cap;
|
|
size_t len;
|
|
size_t aligned;
|
|
} mag160c_frame_stream_t;
|
|
|
|
MAG160C_API void mag160c_frame_stream_init(mag160c_frame_stream_t *s, size_t max_frame_len);
|
|
MAG160C_API void mag160c_frame_stream_destroy(mag160c_frame_stream_t *s);
|
|
MAG160C_API mag160c_error_t mag160c_frame_stream_push(mag160c_frame_stream_t *s,
|
|
const uint8_t *data, size_t size,
|
|
uint8_t *out_frame, size_t out_cap,
|
|
size_t *out_len);
|
|
|
|
/* ---- temperature conversion (pure, no USB needed) ------------------------ */
|
|
|
|
/* CFunctions::Calibration - per-pixel piecewise-linear map with baseline. */
|
|
MAG160C_API void mag160c_temp_calibrate(const uint16_t *frame,
|
|
const mag160c_temp_tables_t *tables,
|
|
uint16_t *out);
|
|
|
|
/* CFunctions::ConvertResponse2Temperature - auto-gain linear map. */
|
|
typedef struct mag160c_temp_gain_t {
|
|
uint32_t gain; /* this+0x76e0 */
|
|
uint32_t shift; /* this+0x76e4 */
|
|
int32_t coeff; /* auto-adjusted gain (global 0x4028a4) */
|
|
const uint16_t *baseline;
|
|
} mag160c_temp_gain_t;
|
|
|
|
MAG160C_API uint32_t mag160c_temp_convert_response(const uint16_t *frame,
|
|
uint32_t pixel_count,
|
|
const mag160c_temp_gain_t *cfg,
|
|
uint16_t *out);
|
|
|
|
/* T2E piecewise-linear curve evaluation with Q13 band interpolation
|
|
* (ReviseTemperature/CorrectTemperature core). */
|
|
MAG160C_API int32_t mag160c_temp_t2e_interp(int32_t value);
|
|
|
|
/* ---- TCM board ------------------------------------------------------------ */
|
|
|
|
typedef enum mag160c_tcm_light_color_t {
|
|
MAG160C_TCM_LIGHT_OFF = 0,
|
|
MAG160C_TCM_LIGHT_RED = 1,
|
|
MAG160C_TCM_LIGHT_GREEN = 2,
|
|
MAG160C_TCM_LIGHT_BLUE = 3,
|
|
MAG160C_TCM_LIGHT_YELLOW = 4
|
|
} mag160c_tcm_light_color_t;
|
|
|
|
typedef enum mag160c_tcm_light_mode_t {
|
|
MAG160C_TCM_LIGHT_STEADY = 0,
|
|
MAG160C_TCM_LIGHT_BLINK = 1,
|
|
MAG160C_TCM_LIGHT_BREATH = 2
|
|
} mag160c_tcm_light_mode_t;
|
|
|
|
/* FTDICommand framing: 0x7e header + BE body length + header checksum +
|
|
* main/sub/frame-id + payload + body checksum. */
|
|
MAG160C_API mag160c_error_t mag160c_tcm_encode(uint8_t main_cmd, uint8_t sub_cmd,
|
|
uint16_t frame_id,
|
|
const uint8_t *payload, size_t payload_size,
|
|
uint8_t *out, size_t out_cap, size_t *out_size);
|
|
MAG160C_API mag160c_error_t mag160c_tcm_decode(const uint8_t *data, size_t size,
|
|
uint8_t *out_main, uint8_t *out_sub,
|
|
uint16_t *out_frame_id,
|
|
uint8_t *out_payload, size_t out_payload_cap,
|
|
size_t *out_payload_size);
|
|
MAG160C_API mag160c_error_t mag160c_tcm_rotate(int angle,
|
|
uint8_t *out, size_t out_cap, size_t *out_size);
|
|
MAG160C_API mag160c_error_t mag160c_tcm_light(mag160c_tcm_light_color_t color,
|
|
mag160c_tcm_light_mode_t mode,
|
|
uint8_t *out, size_t out_cap, size_t *out_size);
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif /* MAG160C_H */
|