建立 MAG160C 逆向工程交接仓库

This commit is contained in:
ZXCLI
2026-08-11 19:08:44 +08:00
commit 8409b27ba3
3135 changed files with 534408 additions and 0 deletions
+269
View File
@@ -0,0 +1,269 @@
/*
* 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 */
+118
View File
@@ -0,0 +1,118 @@
/*
* MAG160C display pipeline - pure C, no OS/USB dependencies.
* See mag160c_display.c for the pipeline documentation.
*/
#ifndef MAG160C_DISPLAY_H
#define MAG160C_DISPLAY_H
#include "mag160c/mag160c.h"
#ifdef __cplusplus
extern "C" {
#endif
/* ---- bad pixel map ------------------------------------------------------ */
typedef struct mag160c_display_badmap_t {
uint32_t width;
uint32_t height;
uint32_t pixels;
uint32_t feed_count; /* frames fed to mag160c_display_badmap_feed */
uint32_t temporal_thr; /* min/max fluctuation threshold (default 400) */
uint32_t hist_min_dev; /* histogram peak min deviation (default 200) */
uint32_t bad_count; /* detected bad pixels */
uint32_t order_len; /* pixels in the topological fill order */
int ready; /* set by finalize() */
uint8_t *bad; /* pixels bad mask */
uint32_t *order_x; /* fill order x */
uint32_t *order_y; /* fill order y */
uint64_t *ref_sum; /* reference accumulator */
uint16_t *ref_min;
uint16_t *ref_max;
uint16_t *ref; /* averaged reference (dead pixels filled) */
} mag160c_display_badmap_t;
MAG160C_API void mag160c_display_badmap_init(mag160c_display_badmap_t *m,
uint32_t w, uint32_t h);
MAG160C_API void mag160c_display_badmap_destroy(mag160c_display_badmap_t *m);
MAG160C_API mag160c_error_t mag160c_display_badmap_feed(mag160c_display_badmap_t *m,
const uint16_t *frame);
MAG160C_API mag160c_error_t mag160c_display_badmap_finalize(mag160c_display_badmap_t *m);
MAG160C_API mag160c_error_t mag160c_display_badmap_correct(const mag160c_display_badmap_t *m,
uint16_t *frame);
/* MOG-style per-pixel reference tracking (anti-ghost): background pixels
* (|live-ref| < thr) track drift slowly (ref += d/alpha); foreground pixels
* are frozen so a static object is never absorbed into the reference. */
MAG160C_API void mag160c_display_ref_track(uint16_t *ref, const uint16_t *live,
uint32_t n, int32_t thr, int32_t alpha);
/* MOG tracking with self-healing: an *isolated* pixel stuck as foreground
* for heal_frames (fewer than min_nbr foreground 8-neighbors) is a
* reference error (startup noise, bad pixel) and is reset to the live
* value; contiguous objects are never healed. */
MAG160C_API void mag160c_display_ref_track_heal(uint16_t *ref, const uint16_t *live,
uint32_t w, uint32_t h,
int32_t thr, int32_t alpha,
uint8_t *fg_count,
uint32_t heal_frames,
uint32_t min_nbr);
/* Flat-field (NUC) correction: out = live - (ref - mean(ref)). Removes the
* fixed sensor mura so the absolute image is smooth; the reference must be
* the fixed pattern only (object-free capture + frozen tracking). */
MAG160C_API mag160c_error_t mag160c_display_nuc(const uint16_t *live,
const uint16_t *ref,
uint32_t n, uint16_t *out);
/* Re-align the reference after FFC(1): applies the median of (live-ref)
* clamped to max_shift as a global offset (baseline shifts reach +1000). */
MAG160C_API mag160c_error_t mag160c_display_ref_rebase(uint16_t *ref,
const uint16_t *live,
uint32_t n,
int32_t max_shift);
/* ---- AGC ---------------------------------------------------------------- */
MAG160C_API mag160c_error_t mag160c_display_agc_range(const uint16_t *frame, uint32_t n,
uint32_t lo_pct, uint32_t hi_pct,
uint32_t *out_lo, uint32_t *out_hi);
MAG160C_API mag160c_error_t mag160c_display_diff_span(const int32_t *diff, uint32_t n,
uint32_t min_span, uint32_t max_span,
uint32_t *out_span);
/* ---- pseudo color ------------------------------------------------------- */
MAG160C_API void mag160c_display_ironbow(uint32_t v, uint8_t *r, uint8_t *g, uint8_t *b);
/* ---- FFC scheduler (official demo cadence) ------------------------------ */
typedef struct mag160c_ffc_scheduler_t {
uint32_t period; /* frames between FFC pairs (default 400) */
uint32_t gap; /* frames FFC(0) -> FFC(1) (default 9) */
uint32_t frames; /* frames since last FFC event */
uint32_t started; /* first FFC(1) already sent */
uint32_t wait1; /* FFC(0) sent, waiting to send FFC(1) */
} mag160c_ffc_scheduler_t;
MAG160C_API void mag160c_ffc_scheduler_init(mag160c_ffc_scheduler_t *s,
uint32_t period, uint32_t gap);
/* Returns -1 (nothing) or an FFC param (0/1) to send after this frame. */
MAG160C_API int32_t mag160c_ffc_scheduler_tick(mag160c_ffc_scheduler_t *s);
/* Force an immediate FFC pair: returns 0 (send FFC(0) now), then tick()
* returns 1 (send FFC(1)) after `gap` frames. */
MAG160C_API int32_t mag160c_ffc_scheduler_trigger(mag160c_ffc_scheduler_t *s);
/* ---- two-point linear temperature calibration --------------------------- */
/* counts = a*temp + b from two known (counts, tempC) points. */
MAG160C_API mag160c_error_t mag160c_temp_calibrate_linear(double counts0, double temp0,
double counts1, double temp1,
double *out_a, double *out_b);
MAG160C_API double mag160c_temp_apply_linear(double counts, double a, double b);
#ifdef __cplusplus
}
#endif
#endif /* MAG160C_DISPLAY_H */