建立 MAG160C 逆向工程交接仓库
This commit is contained in:
@@ -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 */
|
||||
@@ -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 */
|
||||
Reference in New Issue
Block a user