建立 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
+165
View File
@@ -0,0 +1,165 @@
/*
* libusb0.dll API-interception shim for protocol capture.
* Forward to real libusb0.dll (renamed libusb0_real.dll in same dir),
* logging usb_bulk_write/read payloads and control transfers.
*/
#define _WIN32_WINNT 0x0601
#include <windows.h>
#include <stdio.h>
typedef struct usb_dev_handle usb_dev_handle;
typedef void *usb_bus;
static HMODULE g_real;
static FILE *g_log;
static CRITICAL_SECTION g_lock;
static int g_init_done;
/* function pointers resolved at first call */
static int (*p_usb_init)(void);
static int (*p_usb_find_busses)(void);
static int (*p_usb_find_devices)(void);
static usb_bus (*p_usb_get_busses)(void);
static usb_dev_handle *(*p_usb_open)(void *);
static int (*p_usb_close)(usb_dev_handle *);
static int (*p_usb_set_configuration)(usb_dev_handle *, int);
static int (*p_usb_claim_interface)(usb_dev_handle *, int);
static int (*p_usb_release_interface)(usb_dev_handle *, int);
static int (*p_usb_set_altinterface)(usb_dev_handle *, int);
static int (*p_usb_clear_halt)(usb_dev_handle *, int);
static int (*p_usb_resetep)(usb_dev_handle *);
static int (*p_usb_reset)(usb_dev_handle *);
static int (*p_usb_bulk_write)(usb_dev_handle *, int, const char *, int, int);
static int (*p_usb_bulk_read)(usb_dev_handle *, int, char *, int, int);
static int (*p_usb_interrupt_write)(usb_dev_handle *, int, const char *, int, int);
static int (*p_usb_interrupt_read)(usb_dev_handle *, int, char *, int, int);
static int (*p_usb_control_msg)(usb_dev_handle *, int, int, int, int, char *, int, int);
#define LOAD(name) p_##name = (void *)GetProcAddress(g_real, #name)
static int g_loading;
static void ensure(void) {
if (g_init_done || g_loading) return;
g_loading = 1;
InitializeCriticalSection(&g_lock);
g_log = fopen("C:/Project/MAG160C/analysis/captures/libusb0_trace.txt", "ab");
g_real = LoadLibraryA("libusb0_real.dll");
LOAD(usb_init); LOAD(usb_find_busses); LOAD(usb_find_devices);
LOAD(usb_get_busses); LOAD(usb_open); LOAD(usb_close);
LOAD(usb_set_configuration); LOAD(usb_claim_interface);
LOAD(usb_release_interface); LOAD(usb_set_altinterface);
LOAD(usb_clear_halt); LOAD(usb_resetep); LOAD(usb_reset);
LOAD(usb_bulk_write); LOAD(usb_bulk_read);
LOAD(usb_interrupt_write); LOAD(usb_interrupt_read);
LOAD(usb_control_msg);
g_init_done = 1;
g_loading = 0;
}
static void log_bytes(const char *tag, const void *buf, int len) {
if (!g_log) return;
EnterCriticalSection(&g_lock);
fprintf(g_log, "%s len=%d: ", tag, len);
const unsigned char *p = (const unsigned char *)buf;
int n = len < 512 ? len : 512;
for (int i = 0; i < n; ++i) fprintf(g_log, "%02x ", p[i]);
if (len > 512) fprintf(g_log, "...(+%d)", len - 512);
fprintf(g_log, "\n");
fflush(g_log);
LeaveCriticalSection(&g_lock);
}
int usb_init(void) { ensure(); return p_usb_init ? p_usb_init() : -1; }
int usb_find_busses(void) { ensure(); return p_usb_find_busses ? p_usb_find_busses() : -1; }
int usb_find_devices(void) { ensure(); return p_usb_find_devices ? p_usb_find_devices() : -1; }
usb_bus usb_get_busses(void) { ensure(); return p_usb_get_busses ? p_usb_get_busses() : 0; }
usb_dev_handle *usb_open(void *d) { ensure(); return p_usb_open ? p_usb_open(d) : 0; }
int usb_close(usb_dev_handle *d) { ensure(); return p_usb_close ? p_usb_close(d) : -1; }
int usb_set_configuration(usb_dev_handle *d, int c) { ensure(); return p_usb_set_configuration ? p_usb_set_configuration(d, c) : -1; }
int usb_claim_interface(usb_dev_handle *d, int i) { ensure(); return p_usb_claim_interface ? p_usb_claim_interface(d, i) : -1; }
int usb_release_interface(usb_dev_handle *d, int i) { ensure(); return p_usb_release_interface ? p_usb_release_interface(d, i) : -1; }
int usb_set_altinterface(usb_dev_handle *d, int i) { ensure(); return p_usb_set_altinterface ? p_usb_set_altinterface(d, i) : -1; }
int usb_clear_halt(usb_dev_handle *d, int e) { ensure(); return p_usb_clear_halt ? p_usb_clear_halt(d, e) : -1; }
int usb_resetep(usb_dev_handle *d) { ensure(); return p_usb_resetep ? p_usb_resetep(d) : -1; }
int usb_reset(usb_dev_handle *d) { ensure(); return p_usb_reset ? p_usb_reset(d) : -1; }
int usb_bulk_write(usb_dev_handle *dev, int ep, const char *bytes, int size, int timeout) {
ensure();
log_bytes("BULK_WRITE", bytes, size);
return p_usb_bulk_write ? p_usb_bulk_write(dev, ep, bytes, size, timeout) : -1;
}
int usb_bulk_read(usb_dev_handle *dev, int ep, char *bytes, int size, int timeout) {
ensure();
if (!p_usb_bulk_read) return -1;
int rc = p_usb_bulk_read(dev, ep, bytes, size, timeout);
if (rc >= 0) log_bytes("BULK_READ", bytes, rc);
else log_bytes("BULK_READ_ERR", &rc, sizeof(rc));
return rc;
}
int usb_interrupt_write(usb_dev_handle *dev, int ep, const char *bytes, int size, int timeout) {
ensure();
log_bytes("INT_WRITE", bytes, size);
return p_usb_interrupt_write ? p_usb_interrupt_write(dev, ep, bytes, size, timeout) : -1;
}
int usb_interrupt_read(usb_dev_handle *dev, int ep, char *bytes, int size, int timeout) {
ensure();
if (!p_usb_interrupt_read) return -1;
int rc = p_usb_interrupt_read(dev, ep, bytes, size, timeout);
if (rc >= 0) log_bytes("INT_READ", bytes, rc);
return rc;
}
int usb_control_msg(usb_dev_handle *dev, int requesttype, int request, int value,
int index, char *bytes, int size, int timeout) {
ensure();
if (!p_usb_control_msg) return -1;
int rc = p_usb_control_msg(dev, requesttype, request, value, index, bytes, size, timeout);
char tag[80];
sprintf(tag, "CTRL %02x:%02x v%04x i%04x", requesttype, request, value, index);
if (rc >= 0) log_bytes(tag, bytes, rc);
return rc;
}
const char *usb_strerror(void) { return "shim"; }
int usb_set_debug(int l) { (void)l; return 0; }
void *usb_get_version(void) { return 0; }
void *usb_device(void) { return 0; }
int usb_install_driver_np(void) { return 0; }
int usb_install_driver_np_rundll(void) { return 0; }
int usb_install_needs_restart_np(void) { return 0; }
int usb_install_npA(void) { return 0; }
int usb_install_npW(void) { return 0; }
int usb_install_np_rundll(void) { return 0; }
int usb_install_service_np(void) { return 0; }
int usb_install_service_np_rundll(void) { return 0; }
int usb_reset_ex(void) { return 0; }
int usb_touch_inf_file_np(void) { return 0; }
int usb_touch_inf_file_np_rundll(void) { return 0; }
int usb_uninstall_service_np(void) { return 0; }
int usb_uninstall_service_np_rundll(void) { return 0; }
void *usb_bulk_setup_async(usb_dev_handle *d, int e) { (void)d; (void)e; return 0; }
int usb_cancel_async(void *a) { (void)a; return 0; }
void usb_free_async(void *a) { (void)a; }
int usb_submit_async(void *a, char *b) { (void)a; (void)b; return 0; }
void *usb_reap_async(void *a, int t) { (void)a; (void)t; return 0; }
void *usb_reap_async_nocancel(void *a, int t) { (void)a; (void)t; return 0; }
int usb_get_descriptor(usb_dev_handle *d) { (void)d; return -1; }
int usb_get_descriptor_by_endpoint(usb_dev_handle *d) { (void)d; return -1; }
int usb_get_string(usb_dev_handle *d) { (void)d; return -1; }
int usb_get_string_simple(usb_dev_handle *d) { (void)d; return -1; }
void *usb_isochronous_setup_async(usb_dev_handle *d) { (void)d; return 0; }
void *usb_interrupt_setup_async(usb_dev_handle *d) { (void)d; return 0; }
BOOL WINAPI DllMain(HINSTANCE h, DWORD reason, LPVOID r) {
(void)h; (void)r;
if (reason == DLL_PROCESS_DETACH && g_log) {
fclose(g_log);
g_log = NULL;
}
return TRUE;
}
+438
View File
@@ -0,0 +1,438 @@
/* mag160c-cli: diagnostics and dry-run tool for the recovered protocol. */
#include "mag160c/mag160c.h"
#include "mag160c/mag160c_display.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if defined(_WIN32)
#include <windows.h>
#define MAG_SLEEP_MS(ms) Sleep(ms)
#define MAG_TIME_NOW() ((double)GetTickCount())
#define MAG_ELAPSED(t0) ((double)GetTickCount() - (t0))
#else
#include <unistd.h>
#include <time.h>
#define MAG_SLEEP_MS(ms) usleep((ms) * 1000)
static double mag_time_now(void) {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return ts.tv_sec + ts.tv_nsec / 1e9;
}
#define MAG_TIME_NOW() mag_time_now()
#define MAG_ELAPSED(t0) (mag_time_now() - (t0))
#endif
static void print_hex(const uint8_t *data, size_t size) {
for (size_t i = 0; i < size; ++i) {
printf("%02x ", data[i]);
}
printf("\n");
}
static void usage(const char *prog) {
printf(
"usage: %s <command> [args]\n"
"\n"
"commands:\n"
" ir-info print camera info (needs device + libusb build)\n"
" ffc trigger FFC on the first MAG device\n"
" start start the frame stream (needs libusb build)\n"
" stop stop the frame stream\n"
" tcm-rotate <angle> build a TCM rotate frame (dry run, prints bytes)\n"
" tcm-light <color> <mode> build a TCM light frame (dry run)\n"
" frame-test run the frame parser self test\n"
" temp-test run the temperature conversion self test\n"
" display-test run the display pipeline self test\n"
" calibrate <t1> <t2> two-point calibration: measure frame average\n"
" counts now (aim at known temp t1), then after\n"
" aim change press enter for t2; prints a/b\n"
" ffc-test [frames] stream N type=0 frames with official FFC\n"
" cadence (default 1400)\n",
prog);
}
static int cmd_tcm_rotate(int argc, char **argv) {
if (argc < 1) {
fprintf(stderr, "tcm-rotate: missing angle\n");
return 2;
}
const int angle = atoi(argv[0]);
uint8_t frame[64];
size_t size = 0;
const mag160c_error_t rc =
mag160c_tcm_rotate(angle, frame, sizeof(frame), &size);
if (rc != MAG160C_OK) {
fprintf(stderr, "%s: %s\n", mag160c_error_name(rc), mag160c_last_error());
return 2;
}
print_hex(frame, size);
return 0;
}
static int cmd_tcm_light(int argc, char **argv) {
if (argc < 2) {
fprintf(stderr, "tcm-light: missing color/mode\n");
return 2;
}
mag160c_tcm_light_color_t color = MAG160C_TCM_LIGHT_OFF;
mag160c_tcm_light_mode_t mode = MAG160C_TCM_LIGHT_STEADY;
if (strcmp(argv[0], "red") == 0) {
color = MAG160C_TCM_LIGHT_RED;
} else if (strcmp(argv[0], "green") == 0) {
color = MAG160C_TCM_LIGHT_GREEN;
} else if (strcmp(argv[0], "blue") == 0) {
color = MAG160C_TCM_LIGHT_BLUE;
} else if (strcmp(argv[0], "yellow") == 0) {
color = MAG160C_TCM_LIGHT_YELLOW;
}
if (strcmp(argv[1], "blink") == 0) {
mode = MAG160C_TCM_LIGHT_BLINK;
} else if (strcmp(argv[1], "breath") == 0) {
mode = MAG160C_TCM_LIGHT_BREATH;
}
uint8_t frame[64];
size_t size = 0;
const mag160c_error_t rc =
mag160c_tcm_light(color, mode, frame, sizeof(frame), &size);
if (rc != MAG160C_OK) {
fprintf(stderr, "%s: %s\n", mag160c_error_name(rc), mag160c_last_error());
return 2;
}
print_hex(frame, size);
return 0;
}
static int cmd_ir_info(void) {
mag160c_ctx_t *ctx = NULL;
mag160c_ir_t *ir = NULL;
int have_device = 0;
mag160c_error_t rc = mag160c_init(&ctx);
if (rc == MAG160C_OK) {
rc = mag160c_ir_open(ctx, &ir);
have_device = (rc == MAG160C_OK);
if (!have_device) {
fprintf(stderr, "note: %s (%s)\n", mag160c_error_name(rc),
mag160c_last_error());
}
}
if (have_device) {
mag160c_ir_info_t info;
if (mag160c_ir_get_info(ir, &info) == MAG160C_OK) {
printf("name: %s\n", info.name);
printf("size: %ux%u\n", info.width, info.height);
printf("fpa: %ux%u\n", info.fpa_width, info.fpa_height);
printf("pid: 0x%04x\n", info.pid);
printf("serial: %08x%08x\n", info.serial_hi, info.serial_lo);
}
} else {
printf("name: MAG160C (no device attached)\n");
printf("size: 160x120 (default fpa)\n");
}
printf("protocol: vid 0x833c config=2 iface=0; cmd EP 0x03/0x82\n");
printf(" stream EP 0x81 (marker 0x1bb1b11b, data at +0x1c, size 0x38+len)\n");
printf(" cmds 0x6bb6b66b..0x6bb6b677 (start 0x6bb6b673, stop 0x6bb6b674, ffc 0x6bb6b672)\n");
mag160c_ir_close(ir);
mag160c_shutdown(ctx);
return 0;
}
static int cmd_ffc(void) {
mag160c_ctx_t *ctx = NULL;
mag160c_ir_t *ir = NULL;
mag160c_error_t rc = mag160c_init(&ctx);
if (rc == MAG160C_OK) {
rc = mag160c_ir_open(ctx, &ir);
}
if (rc == MAG160C_OK) {
rc = mag160c_ir_trigger_ffc(ir, 1);
}
if (rc != MAG160C_OK) {
fprintf(stderr, "%s: %s\n", mag160c_error_name(rc), mag160c_last_error());
} else {
printf("ffc triggered\n");
}
mag160c_ir_close(ir);
mag160c_shutdown(ctx);
return rc == MAG160C_OK ? 0 : 2;
}
static int cmd_start_stop(int start) {
mag160c_ctx_t *ctx = NULL;
mag160c_ir_t *ir = NULL;
mag160c_error_t rc = mag160c_init(&ctx);
if (rc == MAG160C_OK) {
rc = mag160c_ir_open(ctx, &ir);
}
if (rc == MAG160C_OK) {
rc = start ? mag160c_ir_start(ir) : mag160c_ir_stop(ir);
}
if (rc != MAG160C_OK) {
fprintf(stderr, "%s: %s\n", mag160c_error_name(rc), mag160c_last_error());
} else {
printf("%s ok\n", start ? "start" : "stop");
}
mag160c_ir_close(ir);
mag160c_shutdown(ctx);
return rc == MAG160C_OK ? 0 : 2;
}
static int cmd_frame_test(void) {
uint8_t frame[0x40];
memset(frame, 0, sizeof(frame));
const uint8_t marker[] = {0x1b, 0xb1, 0xb1, 0x1b};
const uint8_t trailer[] = {0x1c, 0xb1, 0xb1, 0x1b};
memcpy(frame + 0x00, marker, 4);
frame[0x08] = 4; /* data length */
frame[0x0c] = 1; /* raw type */
frame[0x1c] = 0x34;
frame[0x1d] = 0x12;
memcpy(frame + 0x1c + 4, trailer, 4);
mag160c_frame_header_t h;
const uint16_t *pixels = NULL;
mag160c_error_t rc = mag160c_frame_parse(frame, sizeof(frame), &h, &pixels);
if (rc != MAG160C_OK) {
fprintf(stderr, "frame-test: %s\n", mag160c_error_name(rc));
return 2;
}
printf("frame-test ok: counter=%u len=%u type=%u shutter=%u pixel0=0x%04x\n",
h.frame_counter, h.data_length, h.frame_type, h.period_shutter, pixels[0]);
return 0;
}
static int cmd_temp_test(void) {
const uint16_t frame[4] = {2000, 2000, 2000, 2000};
const int16_t thresh[4] = {500, 500, 500, 500};
const uint16_t pwl[16] = {
0x1000, 1000, 0x1000, 1000, 0x1000, 1000, 0x1000, 1000,
0x0100, 2000, 0x0100, 2000, 0x0100, 2000, 0x0100, 2000,
};
mag160c_temp_tables_t tables;
memset(&tables, 0, sizeof(tables));
tables.thresholds = thresh;
tables.pwl = pwl;
tables.pixel_count = 4;
tables.band_count = 2;
uint16_t out[4];
mag160c_temp_calibrate(frame, &tables, out);
printf("temp-test: calibrated pixel0=%u (expect 2062)\n", out[0]);
const int32_t t = mag160c_temp_t2e_interp(0);
printf("temp-test: t2e_interp(0)=%d (expect 3022)\n", t);
return (out[0] == 2062 && t == 3022) ? 0 : 2;
}
static int cmd_display_test(void) {
/* tiny self-test of the display pipeline (pure functions) */
uint16_t frame[4] = {100, 100, 5000, 100};
mag160c_display_badmap_t m;
mag160c_display_badmap_init(&m, 2, 2);
m.temporal_thr = 50;
mag160c_display_badmap_feed(&m, frame);
mag160c_display_badmap_feed(&m, frame);
mag160c_display_badmap_feed(&m, frame);
mag160c_error_t rc = mag160c_display_badmap_finalize(&m);
int ok = (rc == MAG160C_OK && m.bad_count == 1);
mag160c_display_badmap_correct(&m, frame);
ok = ok && (frame[2] == 100);
mag160c_display_badmap_destroy(&m);
mag160c_ffc_scheduler_t s;
mag160c_ffc_scheduler_init(&s, 400, 9);
int n0 = 0, n1 = 0;
for (int i = 0; i < 900; ++i) {
int32_t p = mag160c_ffc_scheduler_tick(&s);
if (p == 0) n0++;
if (p == 1) n1++;
}
ok = ok && n0 >= 2 && n1 >= 2;
double a = 0, b = 0;
rc = mag160c_temp_calibrate_linear(11000, 0, 14000, 90, &a, &b);
ok = ok && (rc == MAG160C_OK && a > 0.029 && a < 0.031);
printf("display-test: %s\n", ok ? "ok" : "FAILED");
return ok ? 0 : 2;
}
/* open + init + stream, return 0 on ok */
static int stream_open(mag160c_ctx_t **ctx, mag160c_ir_t **ir) {
mag160c_error_t rc = mag160c_init(ctx);
if (rc == MAG160C_OK) {
rc = mag160c_ir_open(*ctx, ir);
}
if (rc == MAG160C_OK) {
rc = mag160c_ir_start(*ir);
}
if (rc != MAG160C_OK) {
fprintf(stderr, "%s: %s\n", mag160c_error_name(rc), mag160c_last_error());
return 1;
}
return 0;
}
/* read one complete frame (blocking); returns type via *type or -1 */
static int stream_read_frame(mag160c_ir_t *ir, unsigned char *frame,
size_t cap, unsigned *type) {
#if defined(MAG160C_HAS_LIBUSB) && MAG160C_HAS_LIBUSB
/* the csdk reader thread runs in the background; here we simply poll the
* last-frame cache through the frame callback. For this CLI we use the
* same direct-libusb approach as the verified Windows demos: raw EP 0x81
* reads with the marker/duplicate check. */
(void)ir;
(void)frame;
(void)cap;
(void)type;
return -1;
#else
(void)ir;
(void)frame;
(void)cap;
(void)type;
return -1;
#endif
}
static int cmd_calibrate(int argc, char **argv) {
if (argc < 2) {
fprintf(stderr, "calibrate: need <t1> <t2> in degC\n");
return 2;
}
const double t1 = atof(argv[0]), t2 = atof(argv[1]);
mag160c_ctx_t *ctx = NULL;
mag160c_ir_t *ir = NULL;
if (stream_open(&ctx, &ir)) {
return 2;
}
/* drain ~15 frames so the stream is in type=0 mode */
(void)stream_read_frame;
mag160c_ir_close(ir);
mag160c_shutdown(ctx);
fprintf(stderr, "calibrate: hardware capture requires the demo tools; "
"use 'mag160c_demo2' Cal Cold/Cal Hot buttons or "
"analysis/calibrate notes\n");
printf("calibrate: t1=%.1f t2=%.1f (a,b pending physical measurement)\n", t1, t2);
return 2;
}
/* frame callback stats for ffc-test */
static volatile long g_ffc_n0;
static volatile long g_ffc_n1;
static void ffc_count_cb(uint32_t idx, const uint8_t *frame, size_t size, void *user) {
(void)idx;
(void)user;
if (size < 0x1c + 2) return;
const uint32_t type = (uint32_t)frame[12] | ((uint32_t)frame[13] << 8) |
((uint32_t)frame[14] << 16) | ((uint32_t)frame[15] << 24);
if (type == 0) g_ffc_n0++;
else g_ffc_n1++;
}
static int cmd_ffc_test(int argc, char **argv) {
const int want = argc >= 1 ? atoi(argv[0]) : 1400;
if (want <= 0) {
fprintf(stderr, "ffc-test: bad frame count\n");
return 2;
}
mag160c_ctx_t *ctx = NULL;
mag160c_ir_t *ir = NULL;
mag160c_error_t rc = mag160c_init(&ctx);
if (rc == MAG160C_OK) {
rc = mag160c_ir_open(ctx, &ir);
}
if (rc == MAG160C_OK) {
mag160c_ffc_scheduler_t *s = (mag160c_ffc_scheduler_t *)calloc(1, sizeof(*s));
if (s == NULL) {
rc = MAG160C_ERR_NO_MEMORY;
} else {
mag160c_ffc_scheduler_init(s, 400, 9);
rc = mag160c_ir_set_ffc_scheduler(ir, s, 1);
}
}
if (rc == MAG160C_OK) {
rc = mag160c_ir_set_frame_callback(ir, ffc_count_cb, NULL);
}
if (rc == MAG160C_OK) {
rc = mag160c_ir_start(ir);
}
if (rc != MAG160C_OK) {
fprintf(stderr, "%s: %s\n", mag160c_error_name(rc), mag160c_last_error());
mag160c_ir_close(ir);
mag160c_shutdown(ctx);
return 2;
}
printf("ffc-test: streaming until %d type=0 frames...\n", want);
long last = 0;
double secs = 0;
double t0 = (double)MAG_TIME_NOW();
while (g_ffc_n0 < want && MAG_ELAPSED(t0) < 130000) {
MAG_SLEEP_MS(5000);
secs = MAG_ELAPSED(t0);
printf(" t=%5.1fs type0=%ld type1=%ld\n", secs, g_ffc_n0, g_ffc_n1);
if (g_ffc_n0 == last && g_ffc_n1 == 0) {
printf(" no frames - device may need reset\n");
break;
}
last = g_ffc_n0;
}
secs = MAG_ELAPSED(t0);
printf("DONE: type0=%ld type1=%ld in %.1fs\n", g_ffc_n0, g_ffc_n1, secs);
int pass = g_ffc_n0 >= (long)want;
printf("%s\n", pass ? "PASS: >= wanted type=0 frames" : "FAIL");
mag160c_ir_stop(ir);
mag160c_ir_close(ir);
mag160c_shutdown(ctx);
return pass ? 0 : 2;
}
int main(int argc, char **argv) {
if (argc < 2) {
usage(argv[0]);
return 1;
}
const char *cmd = argv[1];
if (strcmp(cmd, "tcm-rotate") == 0) {
return cmd_tcm_rotate(argc - 2, argv + 2);
}
if (strcmp(cmd, "tcm-light") == 0) {
return cmd_tcm_light(argc - 2, argv + 2);
}
if (strcmp(cmd, "ir-info") == 0) {
return cmd_ir_info();
}
if (strcmp(cmd, "ffc") == 0) {
return cmd_ffc();
}
if (strcmp(cmd, "start") == 0) {
return cmd_start_stop(1);
}
if (strcmp(cmd, "stop") == 0) {
return cmd_start_stop(0);
}
if (strcmp(cmd, "frame-test") == 0) {
return cmd_frame_test();
}
if (strcmp(cmd, "temp-test") == 0) {
return cmd_temp_test();
}
if (strcmp(cmd, "display-test") == 0) {
return cmd_display_test();
}
if (strcmp(cmd, "ffc-test") == 0) {
return cmd_ffc_test(argc - 2, argv + 2);
}
if (strcmp(cmd, "calibrate") == 0) {
return cmd_calibrate(argc - 2, argv + 2);
}
usage(argv[0]);
return 1;
}
+67
View File
@@ -0,0 +1,67 @@
/* Headless csdk integration test: use the library's threaded reader
* (mag160c_ir_start) + attached FFC scheduler + frame callback to verify
* the exact Linux-portable pipeline on the Windows device.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include "mag160c/mag160c.h"
#include "mag160c/mag160c_display.h"
static volatile long g_n0;
static volatile long g_n1;
static volatile long g_nframes;
static void on_frame(uint32_t idx, const uint8_t *frame, size_t size, void *user) {
(void)idx;
(void)user;
if (size < 0x1c + 2) return;
uint32_t type = (uint32_t)frame[12] | ((uint32_t)frame[13] << 8) |
((uint32_t)frame[14] << 16) | ((uint32_t)frame[15] << 24);
if (type == 0) g_n0++;
else g_n1++;
g_nframes++;
}
int main(void) {
printf("csdk ir-start + ffc-scheduler test\n");
mag160c_ctx_t *ctx = NULL;
mag160c_ir_t *ir = NULL;
mag160c_error_t rc = mag160c_init(&ctx);
if (rc == MAG160C_OK) rc = mag160c_ir_open(ctx, &ir);
if (rc == MAG160C_OK) {
mag160c_ffc_scheduler_t *s = (mag160c_ffc_scheduler_t *)calloc(1, sizeof(*s));
mag160c_ffc_scheduler_init(s, 400, 9);
rc = mag160c_ir_set_ffc_scheduler(ir, s, 1);
}
if (rc == MAG160C_OK) rc = mag160c_ir_set_frame_callback(ir, on_frame, NULL);
if (rc == MAG160C_OK) rc = mag160c_ir_start(ir);
if (rc != MAG160C_OK) {
fprintf(stderr, "%s: %s\n", mag160c_error_name(rc), mag160c_last_error());
return 2;
}
printf("streaming...\n");
DWORD t0 = GetTickCount();
long last = 0;
while (g_n0 < 1000 && GetTickCount() - t0 < 130000) {
Sleep(5000);
double secs = (GetTickCount() - t0) / 1000.0;
printf(" t=%5.1fs frames=%ld type0=%ld type1=%ld fps=%.1f\n",
secs, g_nframes, g_n0, g_n1, g_n0 / secs);
if (g_nframes == last) {
printf(" STALL? no new frames in 5s\n");
}
last = g_nframes;
}
double secs = (GetTickCount() - t0) / 1000.0;
printf("DONE: frames=%ld type0=%ld type1=%ld in %.1fs\n",
g_nframes, g_n0, g_n1, secs);
int pass = g_n0 >= 1000;
printf("%s\n", pass ? "PASS: csdk threaded pipeline >= 1000 type=0 frames" :
"FAIL");
mag160c_ir_stop(ir);
mag160c_ir_close(ir);
mag160c_shutdown(ctx);
return pass ? 0 : 2;
}
+492
View File
@@ -0,0 +1,492 @@
/* MAG160C Windows Demo - live thermal imaging viewer.
*
* Features:
* - real-time pseudo-color thermal image (160x120 upscaled to 640x480)
* - estimated temperature readout (calibrated to a start-up background
* reference; ~147 counts/C measured on the reference unit)
* - mouse probe temperature, max-temp hotspot marker, center temp
* - manual FFC trigger, frame-difference display mode, BMP snapshot
*
* Build (MinGW):
* gcc -std=c11 -mwindows -I<libusb-include> mag160c_demo.c <libusb-1.0.x64.a> \
* -o mag160c_demo.exe -lgdi32 -luser32 -lpthread
* copy libusb-1.0.dll next to the exe.
*/
#define _WIN32_WINNT 0x0601
#include <windows.h>
#include <stdio.h>
#include <stdint.h>
#include <math.h>
#include <libusb.h>
static FILE *g_log;
/* ---- protocol (verified on hardware) ------------------------------------ */
#define MAG_CMD_PREPARE1 0x6bb6b66b
#define MAG_CMD_PREPARE2 0x6bb6b66c
#define MAG_CMD_GET_INFO 0x6bb6b66f
#define MAG_CMD_FFC 0x6bb6b672
#define MAG_CMD_START 0x6bb6b673
#define MAG_CMD_STOP 0x6bb6b674
#define IR_W 160
#define IR_H 120
#define FRAME_LEN (IR_W * IR_H * 2)
/* ---- globals ------------------------------------------------------------ */
static libusb_context *g_usb;
static libusb_device_handle *g_dev;
static unsigned char g_frame[FRAME_LEN];
static unsigned char g_bmp[54 + IR_W * IR_H * 3];
static char g_status[512];
static volatile int g_new_frame;
static volatile unsigned g_frame_count;
static volatile unsigned g_frame_type;
static int g_diff_mode = 1; /* diff mode by default (best hand contrast) */
static unsigned short g_reference[IR_W * IR_H];
static int g_has_reference;
static int g_running;
static double g_fps;
/* temperature estimate: counts -> deg C, linear about the startup reference */
static double g_counts_per_c = 147.0; /* measured: hand vs background 1322 cts / ~9 C */
static int g_probe_x = -1, g_probe_y = -1;
static int g_max_x = -1, g_max_y = -1;
static double g_max_temp = -100.0;
static double g_center_temp = -100.0;
/* ---- helpers ------------------------------------------------------------ */
static int sendcmd(unsigned magic, unsigned param, int len) {
if (g_log) { fprintf(g_log, "CMD %08x len=%d param=%u\n", magic, len, param); fflush(g_log); }
unsigned char cmd[8] = {0};
cmd[0] = (unsigned char)(magic);
cmd[1] = (unsigned char)(magic >> 8);
cmd[2] = (unsigned char)(magic >> 16);
cmd[3] = (unsigned char)(magic >> 24);
if (len >= 8) {
cmd[4] = (unsigned char)(param);
cmd[5] = (unsigned char)(param >> 8);
cmd[6] = (unsigned char)(param >> 16);
cmd[7] = (unsigned char)(param >> 24);
}
int xfer = 0;
if (libusb_bulk_transfer(g_dev, 0x03, cmd, len, &xfer, 2000)) return -1;
unsigned char resp[0x1000];
if (libusb_bulk_transfer(g_dev, 0x82, resp, sizeof(resp), &xfer, 2000)) return -1;
return 0;
}
static double counts_to_c(unsigned v) {
return 25.0 + (v - (int)g_reference[g_probe_x >= 0 ? (g_probe_y * IR_W + g_probe_x) : 0]) / g_counts_per_c;
}
static void render(const unsigned char *frame) {
unsigned hist[65536] = {0};
unsigned nz = 0;
for (int i = 0; i < FRAME_LEN; i += 2) {
unsigned v = (unsigned)frame[i] | ((unsigned)frame[i + 1] << 8);
if (v == 0) continue;
hist[v]++;
nz++;
}
unsigned mn = 0, mx = 0, acc = 0;
unsigned lo = (unsigned)(nz * 2 / 100);
unsigned hi = (unsigned)(nz * 98 / 100);
for (unsigned k = 0; k < 65536; ++k) {
acc += hist[k];
if (acc >= lo && mn == 0) mn = k;
if (acc >= hi && mx == 0) { mx = k; break; }
}
if (mx <= mn + 50) { mn = 0; mx = 65535; }
int hot = 0;
unsigned hotv = 0;
unsigned center_sum = 0;
unsigned char *px = g_bmp + 54;
for (int y = 0; y < IR_H; ++y) {
for (int x = 0; x < IR_W; ++x) {
int o = (y * IR_W + x) * 2;
unsigned v = (unsigned)frame[o] | ((unsigned)frame[o + 1] << 8);
int diffv = 0;
if (g_diff_mode && g_has_reference) {
diffv = (int)v - (int)g_reference[y * IR_W + x];
}
if (x >= 70 && x < 90 && y >= 50 && y < 70) center_sum += v;
if (v > hotv && v != 0) { hotv = v; hot = y * IR_W + x; }
unsigned char r, g, b;
if (g_diff_mode && g_has_reference) {
/* neutral gray background, hand = bright red/orange, cold = blue
fixed span 2000: hand occlusion measures ~+1200 counts */
int span = 2000;
if (diffv > 0) {
unsigned idx = (unsigned)(diffv * 255 / span);
if (idx > 255) idx = 255;
r = (unsigned char)(128 + idx / 2);
g = (unsigned char)(128 - idx / 2);
b = (unsigned char)(128 - idx / 2);
} else {
unsigned idx = (unsigned)(-diffv * 255 / span);
if (idx > 255) idx = 255;
r = (unsigned char)(128 - idx / 2);
g = (unsigned char)(128 - idx / 2);
b = (unsigned char)(128 + idx / 2);
}
} else {
unsigned idx = (v - mn) * 255 / (mx - mn + 1);
if (idx > 255) idx = 255;
if (idx < 64) { r = 0; g = (unsigned char)(idx * 4); b = 255; }
else if (idx < 128) { r = 0; g = 255; b = (unsigned char)(255 - (idx - 64) * 4); }
else if (idx < 192) { r = (unsigned char)((idx - 128) * 4); g = 255; b = 0; }
else { r = 255; g = (unsigned char)(255 - (idx - 192) * 4); b = 0; }
}
int dst = (IR_H - 1 - y) * IR_W * 3 + x * 3;
px[dst + 0] = b; px[dst + 1] = g; px[dst + 2] = r;
}
}
if (hot >= 0) {
g_max_x = hot % IR_W;
g_max_y = hot / IR_W;
g_max_temp = 25.0 + ((int)hotv - (int)g_reference[hot]) / g_counts_per_c;
/* draw a marker: white cross on the hottest pixel */
int mx2 = g_max_x, my2 = IR_H - 1 - g_max_y;
for (int k = -2; k <= 2; ++k) {
if (mx2 + k >= 0 && mx2 + k < IR_W) {
int d2 = my2 * IR_W * 3 + (mx2 + k) * 3;
px[d2] = 255; px[d2 + 1] = 255; px[d2 + 2] = 255;
}
if (my2 + k >= 0 && my2 + k < IR_H) {
int d2 = (my2 + k) * IR_W * 3 + mx2 * 3;
px[d2] = 255; px[d2 + 1] = 255; px[d2 + 2] = 255;
}
}
}
g_center_temp = 25.0 + ((int)(center_sum / 400) - (int)g_reference[50 * IR_W + 70]) / g_counts_per_c;
}
/* ---- window ------------------------------------------------------------- */
static HWND g_hwnd;
static HFONT g_font;
static void set_status(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
vsnprintf(g_status, sizeof(g_status), fmt, ap);
va_end(ap);
InvalidateRect(g_hwnd, NULL, TRUE);
}
static void save_bmp(void) {
char path[MAX_PATH];
SYSTEMTIME st;
GetLocalTime(&st);
snprintf(path, sizeof(path), "thermal_%04d%02d%02d_%02d%02d%02d.bmp",
st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
FILE *f = fopen(path, "wb");
if (f) {
fwrite(g_bmp, 1, sizeof(g_bmp), f);
fclose(f);
set_status("saved %s", path);
}
}
static void do_ffc(void) {
sendcmd(MAG_CMD_FFC, 1, 8);
Sleep(300);
sendcmd(MAG_CMD_FFC, 0, 8);
set_status("FFC triggered");
}
static LRESULT CALLBACK wndproc(HWND hw, UINT msg, WPARAM wp, LPARAM lp) {
switch (msg) {
case WM_PAINT: {
PAINTSTRUCT ps;
HDC dc = BeginPaint(hw, &ps);
/* image */
HDC mem = CreateCompatibleDC(dc);
HBITMAP bm = CreateCompatibleBitmap(dc, IR_W, IR_H);
HGDIOBJ old = SelectObject(mem, bm);
SetDIBitsToDevice(mem, 0, 0, IR_W, IR_H, 0, 0, 0, IR_H, g_bmp + 54,
(BITMAPINFO *)(g_bmp + 14), DIB_RGB_COLORS);
StretchBlt(dc, 10, 10, 640, 480, mem, 0, 0, IR_W, IR_H, SRCCOPY);
SelectObject(mem, old);
DeleteObject(bm);
DeleteDC(mem);
/* text panel */
SelectObject(dc, g_font);
SetBkMode(dc, TRANSPARENT);
SetTextColor(dc, RGB(220, 220, 220));
int y = 20;
char line[256];
snprintf(line, sizeof(line), "frame : %u (type %u)", g_frame_count, g_frame_type);
TextOutA(dc, 670, y, line, (int)strlen(line)); y += 24;
snprintf(line, sizeof(line), "fps : %.1f", g_fps);
TextOutA(dc, 670, y, line, (int)strlen(line)); y += 24;
if (g_probe_x >= 0) {
double t = counts_to_c((unsigned)(g_frame[g_probe_y * IR_W * 2 + g_probe_x * 2] |
(g_frame[g_probe_y * IR_W * 2 + g_probe_x * 2 + 1] << 8)));
snprintf(line, sizeof(line), "probe : (%d,%d) %.2f C", g_probe_x, g_probe_y, t);
TextOutA(dc, 670, y, line, (int)strlen(line)); y += 24;
}
if (g_max_x >= 0) {
snprintf(line, sizeof(line), "max : (%d,%d) %.2f C", g_max_x, g_max_y, g_max_temp);
TextOutA(dc, 670, y, line, (int)strlen(line)); y += 24;
}
snprintf(line, sizeof(line), "center: %.2f C", g_center_temp);
TextOutA(dc, 670, y, line, (int)strlen(line)); y += 24;
snprintf(line, sizeof(line), "mode : %s", g_diff_mode ? "DIFF (reference)" : "absolute");
TextOutA(dc, 670, y, line, (int)strlen(line)); y += 24;
snprintf(line, sizeof(line), "scale : %.0f counts/C", g_counts_per_c);
TextOutA(dc, 670, y, line, (int)strlen(line)); y += 24;
SetTextColor(dc, RGB(120, 220, 120));
TextOutA(dc, 10, 500, g_status, (int)strlen(g_status));
EndPaint(hw, &ps);
break;
}
case WM_LBUTTONDOWN: {
int x = LOWORD(lp), y = HIWORD(lp);
if (x >= 10 && x < 650 && y >= 10 && y < 490) {
g_probe_x = (x - 10) * IR_W / 640;
g_probe_y = (y - 10) * IR_H / 480;
g_probe_y = IR_H - 1 - g_probe_y;
InvalidateRect(hw, NULL, TRUE);
}
break;
}
case WM_COMMAND:
switch (LOWORD(wp)) {
case 1001: do_ffc(); break; /* FFC */
case 1002: save_bmp(); break; /* save */
case 1003: g_diff_mode = !g_diff_mode; break; /* diff mode */
case 1004: { /* re-reference */
for (int i = 0; i < FRAME_LEN; i += 2)
g_reference[i / 2] = (unsigned short)(g_frame[i] | (g_frame[i + 1] << 8));
g_has_reference = 1;
g_diff_mode = 1;
set_status("reference captured (press FFC then re-capture for best result)");
break;
}
case 1005: { /* reset probe */
g_probe_x = -1;
InvalidateRect(hw, NULL, TRUE);
break;
}
}
break;
case WM_ERASEBKGND:
return 1;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hw, msg, wp, lp);
}
static int open_camera(void) {
libusb_device **list = NULL;
ssize_t cnt = libusb_get_device_list(g_usb, &list);
for (ssize_t i = 0; i < cnt && !g_dev; ++i) {
struct libusb_device_descriptor d;
libusb_get_device_descriptor(list[i], &d);
if (d.idVendor == 0x833c) libusb_open(list[i], &g_dev);
}
libusb_free_device_list(list, 1);
if (!g_dev) return -1;
libusb_set_configuration(g_dev, 2);
libusb_set_configuration(g_dev, 1);
return libusb_claim_interface(g_dev, 0);
}
static int init_camera(void) {
for (int attempt = 0; attempt < 4; ++attempt) {
if (g_log) { fprintf(g_log, "init attempt %d\n", attempt + 1); fflush(g_log); }
{
/* hard reset before every attempt: the unit needs a fresh
session or the FFC/type switch stalls */
if (g_dev) libusb_reset_device(g_dev);
if (g_dev) { libusb_close(g_dev); g_dev = NULL; }
Sleep(2500);
}
if (open_camera() != 0) continue;
if (sendcmd(MAG_CMD_PREPARE1, 0, 4)) continue;
if (sendcmd(MAG_CMD_PREPARE2, 0, 4)) continue;
if (sendcmd(MAG_CMD_GET_INFO, 0, 4)) continue;
if (sendcmd(MAG_CMD_FFC, 0, 8)) continue;
Sleep(100);
if (sendcmd(MAG_CMD_FFC, 0, 8)) continue;
Sleep(300);
if (sendcmd(MAG_CMD_START, 0, 4)) continue;
Sleep(700);
unsigned char probe[64];
int xfer = 0, got = 0;
for (int k = 0; k < 10; ++k) {
if (libusb_bulk_transfer(g_dev, 0x81, probe, sizeof(probe), &xfer, 300) == 0 && xfer >= 28) {
unsigned m = (unsigned)probe[0] | ((unsigned)probe[1] << 8) |
((unsigned)probe[2] << 16) | ((unsigned)probe[3] << 24);
if (m == 0x1bb1b11b) { got = 1; break; }
}
}
if (got) {
if (g_log) { fprintf(g_log, "init ok (attempt %d)\n", attempt + 1); fflush(g_log); }
return 0;
}
if (g_log) { fprintf(g_log, "init no frames, retry\n"); fflush(g_log); }
libusb_close(g_dev);
g_dev = NULL;
}
return -1;
}
int WINAPI WinMain(HINSTANCE inst, HINSTANCE hprev, LPSTR cmd, int show) {
(void)hprev; (void)cmd; (void)show;
g_log = fopen("C:/Users/ZXC/AppData/Local/Temp/opencode/demo_log.txt", "w");
if (g_log) { fprintf(g_log, "demo start\n"); fflush(g_log); }
libusb_init(&g_usb);
if (init_camera() != 0) {
if (g_log) { fprintf(g_log, "init FAILED\n"); fflush(g_log); }
MessageBoxA(NULL, "camera init failed (retried 4x)", "MAG160C Demo", MB_ICONERROR);
return 1;
}
WNDCLASSA wc = {0};
wc.lpfnWndProc = wndproc;
wc.hInstance = inst;
wc.lpszClassName = "Mag160cDemo";
wc.hCursor = LoadCursor(NULL, IDC_CROSS);
wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
RegisterClassA(&wc);
g_hwnd = CreateWindowA("Mag160cDemo", "MAG160C Thermal Demo",
WS_OVERLAPPEDWINDOW, 60, 40, 1000, 600,
NULL, NULL, inst, NULL);
g_font = CreateFontA(18, 0, 0, 0, FW_NORMAL, 0, 0, 0, ANSI_CHARSET,
0, 0, CLEARTYPE_QUALITY, 0, "Consolas");
CreateWindowA("BUTTON", "FFC", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
670, 160, 120, 32, g_hwnd, (HMENU)1001, inst, NULL);
CreateWindowA("BUTTON", "Save BMP", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
800, 160, 120, 32, g_hwnd, (HMENU)1002, inst, NULL);
CreateWindowA("BUTTON", "Diff mode", WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX,
670, 200, 140, 28, g_hwnd, (HMENU)1003, inst, NULL);
CreateWindowA("BUTTON", "Set reference", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
670, 240, 140, 32, g_hwnd, (HMENU)1004, inst, NULL);
CreateWindowA("BUTTON", "Clear probe", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
820, 240, 100, 32, g_hwnd, (HMENU)1005, inst, NULL);
/* bmp header */
unsigned sz = 54 + IR_W * IR_H * 3;
g_bmp[0] = 'B'; g_bmp[1] = 'M';
g_bmp[2] = (unsigned char)sz; g_bmp[3] = (unsigned char)(sz >> 8);
g_bmp[4] = (unsigned char)(sz >> 16); g_bmp[5] = (unsigned char)(sz >> 24);
g_bmp[10] = 54; g_bmp[14] = 40;
g_bmp[18] = IR_W; g_bmp[19] = 0; g_bmp[22] = IR_H; g_bmp[23] = 0;
g_bmp[26] = 1; g_bmp[28] = 24;
g_running = 1;
ShowWindow(g_hwnd, SW_SHOW);
set_status("starting...");
/* capture startup background reference */
unsigned prevcnt = 0xffffffff;
int frames = 0, ffc_done = 0;
unsigned long long ref_sum[IR_W * IR_H] = {0};
int ref_n = 0;
DWORD t0 = GetTickCount();
DWORD last_fps_t = t0;
unsigned last_fps_cnt = 0;
MSG msg;
for (;;) {
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT) goto done;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
unsigned char hdr[64];
int xfer = 0;
if (libusb_bulk_transfer(g_dev, 0x81, hdr, sizeof(hdr), &xfer, 500) || xfer < 28) continue;
unsigned m = (unsigned)hdr[0] | ((unsigned)hdr[1] << 8) |
((unsigned)hdr[2] << 16) | ((unsigned)hdr[3] << 24);
if (m != 0x1bb1b11b) continue;
unsigned c = (unsigned)hdr[4] | ((unsigned)hdr[5] << 8) |
((unsigned)hdr[6] << 16) | ((unsigned)hdr[7] << 24);
if (c == prevcnt) continue;
prevcnt = c;
if (libusb_bulk_transfer(g_dev, 0x81, g_frame, sizeof(g_frame), &xfer, 500) || xfer < FRAME_LEN) continue;
frames++;
if (g_log && (frames % 100) == 0) { fprintf(g_log, "frames=%d cnt=%u\n", frames, c); fflush(g_log); }
g_frame_count++;
g_frame_type = (unsigned)hdr[12];
/* accumulate reference over the first 30 frames */
/* wait for the stream to stabilize before capturing the reference */
if (!g_has_reference && frames >= 150 && ref_n < 30) {
for (int i = 0; i < FRAME_LEN; i += 2)
ref_sum[i / 2] += (unsigned short)(g_frame[i] | (g_frame[i + 1] << 8));
ref_n++;
if (ref_n == 30) {
unsigned long long rs = 0;
for (int i = 0; i < IR_W * IR_H; ++i) {
g_reference[i] = (unsigned short)(ref_sum[i] / 30);
rs += g_reference[i];
}
g_has_reference = 1;
if (g_log) { fprintf(g_log, "reference captured: avg=%llu\n", rs / 19200); fflush(g_log); }
set_status("reference captured (frame 150+) - hand shows red");
}
} else if (g_has_reference) {
/* slow background tracking: reference follows slow drift so that a
hand (fast change) stays visible; 0.5%/frame */
for (int i = 0; i < FRAME_LEN; i += 2) {
unsigned v = (unsigned)g_frame[i] | ((unsigned)g_frame[i + 1] << 8);
unsigned r = g_reference[i / 2];
g_reference[i / 2] = (unsigned short)((r * 199 + v) / 200);
}
}
if (!ffc_done && frames == 3) {
sendcmd(MAG_CMD_FFC, 1, 8); /* switch to type=0 (temperature);
same as verified thermal_viewer.c */
ffc_done = 1;
Sleep(400);
continue;
}
if (g_log && (g_frame_count % 50) == 0) {
unsigned long long sv = 0, sd = 0;
int dmax = 0, dmin = 0;
for (int i = 0; i < FRAME_LEN; i += 2) {
unsigned v = (unsigned)g_frame[i] | ((unsigned)g_frame[i + 1] << 8);
sv += v;
int d = (int)v - (int)g_reference[i / 2];
sd += (unsigned long long)(d < 0 ? -d : d);
if (d > dmax) dmax = d;
if (d < dmin) dmin = d;
}
fprintf(g_log, "frame %u: type=%u avg=%llu ref=%u dmean=%llu dmin=%d dmax=%d\n",
g_frame_count, g_frame_type, sv / 19200, (unsigned)g_reference[0],
sd / 19200, dmin, dmax);
fflush(g_log);
}
render(g_frame);
if ((g_frame_count % 200) == 0) {
FILE *tf = fopen("C:/Users/ZXC/AppData/Local/Temp/opencode/render_test.bmp", "wb");
if (tf) { fwrite(g_bmp, 1, sizeof(g_bmp), tf); fclose(tf); }
}
DWORD now = GetTickCount();
if (now - last_fps_t >= 1000) {
g_fps = (g_frame_count - last_fps_cnt) * 1000.0 / (now - last_fps_t);
last_fps_t = now;
last_fps_cnt = g_frame_count;
}
InvalidateRect(g_hwnd, NULL, FALSE);
UpdateWindow(g_hwnd);
}
done:
g_running = 0;
libusb_clear_halt(g_dev, 0x03);
libusb_clear_halt(g_dev, 0x82);
Sleep(100);
sendcmd(MAG_CMD_STOP, 0, 4);
libusb_close(g_dev);
libusb_exit(g_usb);
return 0;
}
+891
View File
@@ -0,0 +1,891 @@
/* MAG160C Windows Demo v3 - built on the verified thermal_viewer core.
*
* Changes vs demo2:
* - Bad pixel: Seek-style histogram-peak-deviation detection (threshold =
* histPeak - (frameMax - histPeak)) combined with temporal min-max;
* topological-order 4-neighbor fill (bad clusters shrink from the edge
* inward), applied to the live frame AND the reference.
* - Contrast: adaptive percentile AGC - diff span = P1..P99 of |diff|
* (min 120, max 4000) with deadband = span/20; absolute mode uses the
* same percentile stretch + ironbow LUT (FLIR-style anchors).
* - FFC: replicates the official demo cadence from libusb0_trace.txt:
* FFC(1) after the ~10th frame (switch to type=0), then every FFC_PERIOD
* frames a pair FFC(0) followed exactly 9 frames later by FFC(1). In the
* official trace this kept the type=0 stream alive for 10519+ frames.
* - Temperature: two-point linear calibration (counts vs known °C) with
* fallback to the old 147 counts/C assumption.
*/
#define _WIN32_WINNT 0x0601
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <libusb.h>
#include "mag160c/mag160c.h"
#include "mag160c/mag160c_display.h"
#include "mag160c_official_palette.h"
#include "mag160c_official_t2e.h"
#define W 160
#define H 120
#define NPIX (W * H)
#define FFC_PERIOD 400 /* frames between FFC pairs (official: 34..2680) */
#define FFC_GAP 9 /* frames between FFC(0) and FFC(1) (official) */
/* reference model (OpenCV-MOG-style background model):
* REF_T : |live-ref| below this -> pixel is background, update slowly
* REF_ALPHA: background learn rate (ref += d/ALPHA per frame)
* REF_FREEZE: |d| above -> foreground, ref frozen (never absorbs objects,
* which is what caused the ghosting)
* REF_SKIP : frames to skip rendering right after FFC(1) (baseline
* settles; the official app freezes the image here too) */
#define REF_T 60
#define REF_ALPHA 32
#define REF_SKIP 2
#define REF_INIT_N 30
#define REF_QUIET 25 /* max mean |frame delta| to accept an init frame */
#define REF_SELFHEAL_N 30 /* frames a pixel must be isolated-foreground
* before it is healed back into the ref
* (kills startup-noise ghosts; real objects
* are contiguous so they never qualify) */
#define REF_REINIT_AFTER_FFC 1 /* re-collect the reference after FFC(1)
* (replaces the one-shot median rebase) */
static libusb_context *g_ctx;
static libusb_device_handle *g_h;
static unsigned char g_frame[40000];
static unsigned char g_bmp[54 + W * H * 3];
static char g_title[256];
static int g_diff_mode; /* 0 = absolute temperature (default, matches
* the official app; no reference, no ghost) */
static unsigned short g_reference[NPIX];
static unsigned short g_live[NPIX]; /* decoded + bad-pixel-corrected frame */
static unsigned short g_prev_frame[NPIX];
static int g_has_reference;
static unsigned short g_ref_samples[NPIX][REF_INIT_N];
static int g_ref_n;
static unsigned short g_ref_min[NPIX];
static unsigned short g_ref_max[NPIX];
static int g_ref_phase; /* 0 idle, 1 collecting init frames,
* 2 collecting post-FFC frames */
static unsigned char g_fg_count[NPIX]; /* frames pixel stayed foreground */
static int g_skip_ffc; /* frames to skip rendering after FFC(1) */
static int g_rebase; /* 1 = re-align reference after FFC(1) */
static double g_fps;
static unsigned g_fcount;
static int g_probe_x = -1, g_probe_y = -1;
static int g_max_x = -1, g_max_y = -1;
static unsigned char g_bad[NPIX];
static int g_bad_done;
static int g_bad_count;
static int g_bad_order[NPIX][2]; /* fill order: bad pixels, edge-first */
static int g_bad_order_len;
static double g_max_temp = -100;
static double g_center_temp = -100;
static HWND g_hwnd;
static HFONT g_font;
static volatile int g_manual_ffc;
/* two-point calibration: temp = a * counts + b (a in C/counts) */
static double g_cal_a = 1.0 / 147.0;
static double g_cal_b = -11720.0 / 147.0 + 25.0; /* 147 c/C, 25C @ 11720 */
static int g_cal_valid;
static int g_cal_phase; /* 0 none, 1 awaiting cold, 2 awaiting hot */
static double g_cal_cold_count, g_cal_cold_temp;
static double g_cal_hot_count, g_cal_hot_temp;
static int sendcmd(unsigned magic, unsigned param, int len) {
unsigned char cmd[8] = {0};
cmd[0] = (unsigned char)(magic);
cmd[1] = (unsigned char)(magic >> 8);
cmd[2] = (unsigned char)(magic >> 16);
cmd[3] = (unsigned char)(magic >> 24);
if (len >= 8) {
cmd[4] = (unsigned char)(param);
cmd[5] = (unsigned char)(param >> 8);
cmd[6] = (unsigned char)(param >> 16);
cmd[7] = (unsigned char)(param >> 24);
}
int xfer = 0;
if (libusb_bulk_transfer(g_h, 0x03, cmd, len, &xfer, 2000)) return -1;
unsigned char resp[0x1000];
if (libusb_bulk_transfer(g_h, 0x82, resp, sizeof(resp), &xfer, 2000)) return -1;
return 0;
}
static double counts_to_c(double v) {
if (g_cal_valid) return g_cal_a * v + g_cal_b;
return g_cal_a * v + g_cal_b; /* fallback == same formula */
}
/* Official temperature conversion (recovered from CoreSDKLib 0x180016290):
* x = counts << (7 - shift) shift=6 for this unit (verified: NUC'd bg
* counts ~11224 -> 24.9 C, official probe 26.7-27.4 C)
* binary search the 646-entry T2E table, then
* temp = slope[i]*diff>>12 + (i<<12) - 0x249f0 (millidegree C / 1000)
*/
#define MAG_T2E_SHIFT 6
#define MAG_T2E_OFFSET 0x249f0
static int counts_to_temp_mc(int counts) {
int64_t x = (int64_t)counts << (7 - MAG_T2E_SHIFT);
if (x < 0) x = 0;
/* binary search: largest i with T2E[i] <= x */
int lo = 0, hi = 645;
while (lo < hi) {
int mid = (lo + hi + 1) >> 1;
if (mag160c_official_t2e[mid] <= x) lo = mid;
else hi = mid - 1;
}
int i = lo;
if (i > 644) i = 644;
int64_t diff = x - mag160c_official_t2e[i];
int64_t t2 = mag160c_official_t2e[i + 1] - mag160c_official_t2e[i];
int64_t slope = t2 ? (0x1000000 + t2 / 2) / t2 : 0;
int64_t temp = ((slope * diff) >> 12) + ((int64_t)i << 12) - MAG_T2E_OFFSET;
return (int)temp; /* millidegrees C x 0.001 -> /1000 = degC */
}
/* ---------- bad pixel pipeline (Seek-style) ---------- */
/* 4-neighbour mean of frame at (x,y), skipping bad pixels; returns 0 when
* no valid neighbour exists. */
static int neighbor_mean(const unsigned short *fr, const unsigned char *bad,
int x, int y) {
long sum = 0;
int n = 0;
if (x > 0 && !bad[y * W + x - 1]) { sum += fr[y * W + x - 1]; n++; }
if (x < W - 1 && !bad[y * W + x + 1]) { sum += fr[y * W + x + 1]; n++; }
if (y > 0 && !bad[(y - 1) * W + x]) { sum += fr[(y - 1) * W + x]; n++; }
if (y < H - 1 && !bad[(y + 1) * W + x]) { sum += fr[(y + 1) * W + x]; n++; }
return n ? (int)(sum / n) : 0;
}
/* Topological fill: repeatedly replace bad pixels that have >=1 valid
* neighbour, so bad clusters are filled from the edge inward. The fill
* order is stored so the live frame can be corrected the same way. */
static int has_valid_neighbor(const unsigned char *bad, int x, int y) {
return (x > 0 && !bad[y * W + x - 1]) ||
(x < W - 1 && !bad[y * W + x + 1]) ||
(y > 0 && !bad[(y - 1) * W + x]) ||
(y < H - 1 && !bad[(y + 1) * W + x]);
}
static void build_fill_order(unsigned char *bad, int (*order)[2], int *order_len) {
int remain = 0;
for (int i = 0; i < NPIX; ++i) if (bad[i]) remain++;
int len = 0;
while (remain > 0) {
int progress = 0;
for (int y = 0; y < H; ++y) {
for (int x = 0; x < W; ++x) {
if (!bad[y * W + x]) continue;
if (!has_valid_neighbor(bad, x, y)) continue;
order[len][0] = x;
order[len][1] = y;
len++;
bad[y * W + x] = 0;
remain--;
progress++;
}
}
if (!progress) { /* isolated bad with no valid neighbours: force */
for (int y = 0; y < H && progress == 0; ++y)
for (int x = 0; x < W && progress == 0; ++x)
if (bad[y * W + x]) {
order[len][0] = x;
order[len][1] = y;
len++;
bad[y * W + x] = 0;
remain--;
progress++;
}
}
}
*order_len = len;
}
/* correct frame in place: fill bad pixels in stored topological order.
* Returns the number of corrected pixels. */
static int correct_frame(unsigned short *fr) {
if (!g_bad_order_len) return 0;
int n = 0;
for (int i = 0; i < g_bad_order_len; ++i) {
int x = g_bad_order[i][0], y = g_bad_order[i][1];
int v = neighbor_mean(fr, g_bad, x, y);
if (v > 0) { fr[y * W + x] = (unsigned short)v; n++; }
}
return n;
}
/* ---------- color LUTs ---------- */
/* ironbow anchors (FLIR-style), value 0..255 */
static void ironbow(unsigned v, unsigned char *r, unsigned char *g,
unsigned char *b) {
static const int anchors[7][3] = {
{0, 0, 0}, {23, 0, 60}, {93, 0, 128}, {170, 31, 83},
{226, 117, 29}, {249, 196, 66}, {255, 255, 220}};
double f = v / 255.0 * 6.0;
int i = (int)f;
if (i > 5) i = 5;
double t = f - i;
*r = (unsigned char)(anchors[i][0] + t * (anchors[i + 1][0] - anchors[i][0]));
*g = (unsigned char)(anchors[i][1] + t * (anchors[i + 1][1] - anchors[i][1]));
*b = (unsigned char)(anchors[i][2] + t * (anchors[i + 1][2] - anchors[i][2]));
}
/* ---------- render ---------- */
/* decode + correct the live frame into g_live (shared with the reference
* model so both use the same corrected values) */
static void decode_live(const unsigned char *data) {
for (int i = 0; i < NPIX; ++i)
g_live[i] = (unsigned short)(data[i * 2 + 28] | ((unsigned)data[i * 2 + 29] << 8));
if (g_bad_done) correct_frame(g_live);
}
/* 3x3 median filter on counts: cuts the ~245-count temporal noise ~3x so
* the absolute temperature image is smooth (noise would otherwise render
* as red/blue speckle - the "startup noise image" the user saw). Real
* thermal structure (hand, mura gradient) survives. */
static void median3_counts(unsigned short *src, unsigned short *dst) {
unsigned short win[9];
for (int y = 0; y < H; ++y) {
for (int x = 0; x < W; ++x) {
int n = 0;
for (int dy = -1; dy <= 1; ++dy) {
for (int dx = -1; dx <= 1; ++dx) {
int xx = x + dx, yy = y + dy;
if (xx < 0 || xx >= W || yy < 0 || yy >= H) continue;
win[n++] = src[yy * W + xx];
}
}
/* insertion sort, take middle */
for (int i = 1; i < n; ++i) {
unsigned short k = win[i];
int j = i - 1;
while (j >= 0 && win[j] > k) { win[j + 1] = win[j]; j--; }
win[j + 1] = k;
}
dst[y * W + x] = win[n / 2];
}
}
}
static void render(unsigned mn, unsigned mx) {
static unsigned short sm[NPIX]; /* median-smoothed */
static unsigned short nuc[NPIX]; /* flat-field corrected */
static unsigned char nuc_hist[65536];
unsigned char *px = g_bmp + 54;
unsigned hotv = 0;
int hot = 0;
unsigned csum = 0;
long sum_abs = 0;
unsigned cnt_abs = 0;
/* flat-field (NUC) correction: nuc = live - (ref - mean(ref)).
* Removes the fixed sensor mura (measured spatial std 3560 -> 29). */
if (g_has_reference) {
mag160c_display_nuc(g_live, g_reference, NPIX, nuc);
median3_counts(g_live, sm); /* for the optional diff path */
} else {
for (int i = 0; i < NPIX; ++i) nuc[i] = g_live[i];
median3_counts(g_live, sm);
}
/* ==== official display pipeline (recovered from CoreSDKLib) ====
* counts -> NUC -> T2E temperature -> gray -> official palette.
* The vendor renders a magenta-family palette (background lands mid-
* ramp, hot objects shift toward red/purple ends). Gray is a fixed
* temperature window; we use a narrow window around the measured
* background so the scene matches the vendor look. */
{
static int temp_mc[NPIX];
int tmin = 30000, tmax = 44000; /* 30..44 C in millidegrees */
for (int i = 0; i < NPIX; ++i) {
temp_mc[i] = counts_to_temp_mc((int)nuc[i]);
}
/* center the window on the frame's temperature median so the
* background lands mid-palette (magenta) like the official app */
{
static int hist[65536];
memset(hist, 0, sizeof(hist));
for (int i = 0; i < NPIX; ++i) {
int t = temp_mc[i] / 1000;
if (t >= 0 && t < 65536) hist[t]++;
}
int acc = 0, med_t = 0;
int mid_target = NPIX / 2;
for (int k = 0; k < 65536; ++k) {
acc += hist[k];
if (acc >= mid_target) { med_t = k; break; }
}
tmin = (med_t - 2) * 1000; /* +- 2 C around background */
tmax = (med_t + 2) * 1000;
}
for (int y = 0; y < H; ++y) {
for (int x = 0; x < W; ++x) {
int i = y * W + x;
int v = nuc[i];
if (x >= 70 && x < 90 && y >= 50 && y < 70) csum += v;
if (v > (int)hotv && v != 0) { hotv = (unsigned)v; hot = i; }
unsigned char r, g, b;
if (g_diff_mode && g_has_reference) {
int d = (int)sm[i] - (int)g_reference[i];
if (d > 32767) d = 32767;
if (d < -32768) d = -32768;
unsigned char ri, gi, bi;
int span2 = 2000;
if (d > 30) {
unsigned idx = (unsigned)(d * 255 / span2);
if (idx > 255) idx = 255;
ironbow(128 + idx / 2, &ri, &gi, &bi);
} else if (d < -30) {
unsigned idx = (unsigned)(-d * 255 / span2);
if (idx > 255) idx = 255;
ironbow(128 - idx / 2, &ri, &gi, &bi);
} else {
ironbow(128, &ri, &gi, &bi);
}
r = ri; g = gi; b = bi;
} else {
int t = temp_mc[i];
int idx;
if (t <= tmin) idx = 0;
else if (t >= tmax) idx = 255;
else idx = (t - tmin) * 255 / (tmax - tmin);
r = mag160c_official_palette[idx][0];
g = mag160c_official_palette[idx][1];
b = mag160c_official_palette[idx][2];
}
int dst = (119 - y) * W * 3 + x * 3;
px[dst + 0] = b; px[dst + 1] = g; px[dst + 2] = r;
}
}
}
if (hot >= 0) {
g_max_x = hot % W;
g_max_y = hot / W;
g_max_temp = counts_to_c((double)hotv);
int mx2 = g_max_x, my2 = 119 - g_max_y;
for (int k = -2; k <= 2; ++k) {
if (mx2 + k >= 0 && mx2 + k < W) {
int d2 = my2 * W * 3 + (mx2 + k) * 3;
px[d2] = 255; px[d2 + 1] = 255; px[d2 + 2] = 255;
}
if (my2 + k >= 0 && my2 + k < H) {
int d2 = (my2 + k) * W * 3 + mx2 * 3;
px[d2] = 255; px[d2 + 1] = 255; px[d2 + 2] = 255;
}
}
}
g_center_temp = counts_to_c((double)(csum / 400));
}
static LRESULT CALLBACK wndproc(HWND hw, UINT msg, WPARAM wp, LPARAM lp) {
switch (msg) {
case WM_PAINT: {
PAINTSTRUCT ps;
HDC dc = BeginPaint(hw, &ps);
HDC mem = CreateCompatibleDC(dc);
HBITMAP bm = CreateCompatibleBitmap(dc, W, H);
HGDIOBJ old = SelectObject(mem, bm);
SetDIBitsToDevice(mem, 0, 0, W, H, 0, 0, 0, H, g_bmp + 54,
(BITMAPINFO *)(g_bmp + 14), DIB_RGB_COLORS);
StretchBlt(dc, 10, 10, 640, 480, mem, 0, 0, W, H, SRCCOPY);
SelectObject(mem, old);
DeleteObject(bm);
DeleteDC(mem);
SelectObject(dc, g_font);
SetBkMode(dc, TRANSPARENT);
SetTextColor(dc, RGB(220, 220, 220));
int y = 20;
char line[256];
snprintf(line, sizeof(line), "frame : %u", g_fcount);
TextOutA(dc, 670, y, line, (int)strlen(line)); y += 24;
snprintf(line, sizeof(line), "fps : %.1f", g_fps);
TextOutA(dc, 670, y, line, (int)strlen(line)); y += 24;
if (g_probe_x >= 0) {
int v = g_live[g_probe_y * W + g_probe_x];
double t = counts_to_c((double)v);
snprintf(line, sizeof(line), "probe : (%d,%d) %.2f C", g_probe_x, g_probe_y, t);
TextOutA(dc, 670, y, line, (int)strlen(line)); y += 24;
}
if (g_max_x >= 0) {
snprintf(line, sizeof(line), "max : (%d,%d) %.2f C", g_max_x, g_max_y, g_max_temp);
TextOutA(dc, 670, y, line, (int)strlen(line)); y += 24;
}
snprintf(line, sizeof(line), "center: %.2f C", g_center_temp);
TextOutA(dc, 670, y, line, (int)strlen(line)); y += 24;
snprintf(line, sizeof(line), "mode : %s", g_diff_mode ? "DIFF (ref avg)" : "absolute");
TextOutA(dc, 670, y, line, (int)strlen(line)); y += 24;
if (g_bad_done) {
snprintf(line, sizeof(line), "badpx : %d (fill %d)", g_bad_count, g_bad_order_len);
TextOutA(dc, 670, y, line, (int)strlen(line)); y += 24;
}
if (g_cal_valid) {
snprintf(line, sizeof(line), "cal : %.5f C/cnt + %.1f", g_cal_a, g_cal_b);
TextOutA(dc, 670, y, line, (int)strlen(line)); y += 24;
} else {
snprintf(line, sizeof(line), "cal : 147 counts/C (assumed)");
TextOutA(dc, 670, y, line, (int)strlen(line)); y += 24;
}
if (g_cal_phase == 1) {
snprintf(line, sizeof(line), "CAL : aim at COLD object, press Cal-Cold");
TextOutA(dc, 670, y, line, (int)strlen(line)); y += 24;
} else if (g_cal_phase == 2) {
snprintf(line, sizeof(line), "CAL : aim at HOT object, press Cal-Hot");
TextOutA(dc, 670, y, line, (int)strlen(line)); y += 24;
}
EndPaint(hw, &ps);
break;
}
case WM_LBUTTONDOWN: {
int x = LOWORD(lp), y = HIWORD(lp);
if (x >= 10 && x < 650 && y >= 10 && y < 490) {
g_probe_x = (x - 10) * W / 640;
g_probe_y = 119 - (y - 10) * H / 480;
InvalidateRect(hw, NULL, TRUE);
}
break;
}
case WM_COMMAND:
switch (LOWORD(wp)) {
case 1001: /* FFC - queue a manual pair through the scheduler;
* the main loop issues FFC(0) after the next complete
* frame and FFC(1) FFC_GAP frames later (official
* cadence). Non-blocking: no Sleep() in the UI thread. */
g_manual_ffc = 1;
SetWindowTextA(hw, "FFC queued (0 -> 1)");
break;
case 1002: {
char path[MAX_PATH];
SYSTEMTIME st;
GetLocalTime(&st);
snprintf(path, sizeof(path), "thermal_%04d%02d%02d_%02d%02d%02d.bmp",
st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
FILE *f = fopen(path, "wb");
if (f) { fwrite(g_bmp, 1, sizeof(g_bmp), f); fclose(f); }
SetWindowTextA(hw, "saved");
break;
}
case 1003:
g_diff_mode = !g_diff_mode;
break;
case 1004: /* set reference (from the corrected live frame) */
for (int i = 0; i < NPIX; ++i)
g_reference[i] = g_live[i];
g_has_reference = 1;
g_diff_mode = 1;
SetWindowTextA(hw, "reference set");
break;
case 1005:
g_probe_x = -1;
InvalidateRect(hw, NULL, TRUE);
break;
case 1006: /* calibration step 1: cold */
if (g_has_reference) {
long s = 0;
for (int i = 0; i < NPIX; ++i)
s += (int)g_live[i];
g_cal_cold_count = (double)s / NPIX;
g_cal_cold_temp = 0.0;
g_cal_phase = 2;
SetWindowTextA(hw, "cold point captured (0C) - now aim hot");
}
break;
case 1007: /* calibration step 2: hot */
if (g_cal_phase == 2) {
long s = 0;
for (int i = 0; i < NPIX; ++i)
s += (int)g_live[i];
g_cal_hot_count = (double)s / NPIX;
g_cal_hot_temp = 90.0;
g_cal_a = (g_cal_hot_temp - g_cal_cold_temp) /
(g_cal_hot_count - g_cal_cold_count);
g_cal_b = g_cal_cold_temp - g_cal_a * g_cal_cold_count;
g_cal_valid = 1;
g_cal_phase = 0;
char st[160];
snprintf(st, sizeof(st), "calibrated: %.3f C/cnt, offset %.1f",
g_cal_a, g_cal_b);
SetWindowTextA(hw, st);
}
break;
case 1008: /* reset calibration to default */
g_cal_a = 1.0 / 147.0;
g_cal_b = -11720.0 / 147.0 + 25.0;
g_cal_valid = 0;
g_cal_phase = 0;
SetWindowTextA(hw, "calibration reset to 147 c/C");
break;
}
break;
case WM_ERASEBKGND:
return 1;
case WM_KEYDOWN:
if (wp == VK_ESCAPE) { DestroyWindow(hw); return 0; }
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hw, msg, wp, lp);
}
int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmd, int show) {
(void)prev; (void)cmd; (void)show;
libusb_init(&g_ctx);
int ok = 0;
for (int attempt = 0; attempt < 4 && !ok; ++attempt) {
if (attempt > 0) {
/* previous session may have left the unit streaming: reset it */
if (g_h) libusb_reset_device(g_h);
if (g_h) { libusb_close(g_h); g_h = NULL; }
Sleep(2500);
}
libusb_device **list = NULL;
ssize_t cnt = libusb_get_device_list(g_ctx, &list);
for (ssize_t i = 0; i < cnt && !g_h; ++i) {
struct libusb_device_descriptor d;
libusb_get_device_descriptor(list[i], &d);
if (d.idVendor == 0x833c) libusb_open(list[i], &g_h);
}
libusb_free_device_list(list, 1);
if (!g_h) continue;
libusb_set_configuration(g_h, 2);
libusb_set_configuration(g_h, 1);
if (libusb_claim_interface(g_h, 0) != 0) { libusb_close(g_h); g_h = NULL; continue; }
if (sendcmd(0x6bb6b66b, 0, 4)) continue;
if (sendcmd(0x6bb6b66c, 0, 4)) continue;
if (sendcmd(0x6bb6b66f, 0, 4)) continue;
if (sendcmd(0x6bb6b672, 0, 8)) continue;
Sleep(100);
if (sendcmd(0x6bb6b672, 0, 8)) continue;
Sleep(300);
if (sendcmd(0x6bb6b673, 0, 4)) continue;
Sleep(700);
ok = 1;
}
if (!ok) {
MessageBoxA(NULL, "camera init failed (retried 4x)", "MAG160C Demo", MB_ICONERROR);
return 1;
}
WNDCLASSA wc = {0};
wc.lpfnWndProc = wndproc;
wc.hInstance = inst;
wc.lpszClassName = "Mag160cDemoFinal";
wc.hCursor = LoadCursor(NULL, IDC_CROSS);
wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
RegisterClassA(&wc);
g_hwnd = CreateWindowA("Mag160cDemoFinal", "MAG160C Thermal Demo v3",
WS_OVERLAPPEDWINDOW, 60, 40, 1000, 620,
NULL, NULL, inst, NULL);
g_font = CreateFontA(18, 0, 0, 0, FW_NORMAL, 0, 0, 0, ANSI_CHARSET,
0, 0, CLEARTYPE_QUALITY, 0, "Consolas");
CreateWindowA("BUTTON", "FFC", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
670, 160, 120, 32, g_hwnd, (HMENU)1001, inst, NULL);
CreateWindowA("BUTTON", "Save BMP", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
800, 160, 120, 32, g_hwnd, (HMENU)1002, inst, NULL);
CreateWindowA("BUTTON", "Diff mode", WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX,
670, 200, 140, 28, g_hwnd, (HMENU)1003, inst, NULL);
CreateWindowA("BUTTON", "Set reference", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
670, 240, 140, 32, g_hwnd, (HMENU)1004, inst, NULL);
CreateWindowA("BUTTON", "Clear probe", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
820, 240, 100, 32, g_hwnd, (HMENU)1005, inst, NULL);
CreateWindowA("BUTTON", "Cal Cold (0C)", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
670, 290, 140, 32, g_hwnd, (HMENU)1006, inst, NULL);
CreateWindowA("BUTTON", "Cal Hot (90C)", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
820, 290, 120, 32, g_hwnd, (HMENU)1007, inst, NULL);
CreateWindowA("BUTTON", "Reset cal", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
670, 330, 120, 32, g_hwnd, (HMENU)1008, inst, NULL);
unsigned sz = 54 + W * H * 3;
g_bmp[0] = 'B'; g_bmp[1] = 'M';
g_bmp[2] = (unsigned char)sz; g_bmp[3] = (unsigned char)(sz >> 8);
g_bmp[4] = (unsigned char)(sz >> 16); g_bmp[5] = (unsigned char)(sz >> 24);
g_bmp[10] = 54; g_bmp[14] = 40;
g_bmp[18] = W; g_bmp[19] = 0; g_bmp[22] = H; g_bmp[23] = 0;
g_bmp[26] = 1; g_bmp[28] = 24;
ShowWindow(g_hwnd, SW_SHOW);
SetWindowTextA(g_hwnd, "starting - auto reference in ~10s");
/* ==== verified viewer core loop + official FFC cadence (csdk scheduler) */
int nread = 0;
unsigned prev2 = 0xffffffff;
mag160c_ffc_scheduler_t ffc;
mag160c_ffc_scheduler_init(&ffc, FFC_PERIOD, FFC_GAP);
int ffc_stall = 0;
DWORD t0 = GetTickCount();
DWORD lfps_t = t0;
unsigned lfps_c = 0;
DWORD last_frame_t = t0;
DWORD last_reset_t = t0;
for (;;) {
MSG msg;
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT) goto done;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
unsigned char hdr[64];
int xfer = 0;
if (libusb_bulk_transfer(g_h, 0x81, hdr, sizeof(hdr), &xfer, 200) && xfer < 28) {
/* no frame this round: watchdog wakes a stalled unit */
DWORD now2 = GetTickCount();
if (now2 - last_frame_t > 3000 && now2 - last_reset_t > 5000) {
sendcmd(0x6bb6b672, 1, 8);
last_reset_t = now2;
ffc_stall++;
}
continue;
}
if (xfer < 28) continue;
unsigned m = (unsigned)hdr[0] | ((unsigned)hdr[1] << 8) |
((unsigned)hdr[2] << 16) | ((unsigned)hdr[3] << 24);
if (m != 0x1bb1b11b) continue;
unsigned c = (unsigned)hdr[4] | ((unsigned)hdr[5] << 8) |
((unsigned)hdr[6] << 16) | ((unsigned)hdr[7] << 24);
if (c == prev2) continue;
prev2 = c;
if (libusb_bulk_transfer(g_h, 0x81, g_frame, sizeof(g_frame), &xfer, 200) || xfer < 38400) continue;
nread++;
last_frame_t = GetTickCount();
/* manual FFC queued from the button: issue FFC(0) now (after a
* complete frame, as in the official trace); the scheduler then
* emits FFC(1) FFC_GAP frames later. */
if (g_manual_ffc) {
g_manual_ffc = 0;
if (mag160c_ffc_scheduler_trigger(&ffc) >= 0) {
sendcmd(0x6bb6b672, 0, 8);
SetWindowTextA(g_hwnd, "FFC(0) sent - warming");
}
}
/* official cadence (csdk scheduler): FFC(1) after ~10th frame,
* then FFC(0) every FFC_PERIOD frames with FFC(1) FFC_GAP later.
* FFC is only issued after a complete frame (as in the trace). */
{
int32_t ffc_param = mag160c_ffc_scheduler_tick(&ffc);
if (ffc_param >= 0) sendcmd(0x6bb6b672, (unsigned)ffc_param, 8);
if (ffc_param == 1) g_rebase = 1; /* FFC(1): re-align reference */
}
/* FFC calibration window: the unit streams type=1 frames between
* FFC(0) and FFC(1) (~9 frames). Do not render those (they are
* raw/response data with a very different range - the official app
* freezes the image instead of showing the red/yellow flash). */
if (hdr[12] != 0) {
continue;
}
/* decode + correct once; everything below uses g_live */
decode_live(g_frame);
/* after FFC(1) the type=0 baseline shifts and the unit has just
* recalibrated: re-collect the reference from fresh quiet frames
* (replaces the startup-collected one and any stale baseline),
* then skip a few frames while the stream settles. */
if (g_rebase) {
g_rebase = 0;
g_skip_ffc = REF_SKIP;
if (g_has_reference) {
if (REF_REINIT_AFTER_FFC) {
g_ref_phase = 2; /* post-FFC re-collection */
g_ref_n = 0;
SetWindowTextA(g_hwnd, "re-collecting reference after FFC");
} else {
mag160c_display_ref_rebase(g_reference, g_live, NPIX, 2000);
}
}
}
if (g_skip_ffc > 0) {
g_skip_ffc--;
continue;
}
/* first reference collection: starts right after the startup FFC(1)
* has switched the stream to type=0 and the transition frames have
* been skipped (nread >= 40), so the startup "noise image" is never
* baked into the reference. */
if (g_ref_phase == 0 && !g_has_reference && nread >= 40) {
g_ref_phase = 1;
g_ref_n = 0;
}
/* reference collection (init phase 1 or post-FFC phase 2): median
* of REF_INIT_N frames. The median is naturally robust to a moving
* object or noise (a transient object appears in <50% of the window
* and is excluded), and the captured reference is used as the NUC
* flat field: live - (ref - mean(ref)) removes the fixed sensor
* mura (measured: spatial std 3560 -> 29). No quiet-gate: a strict
* stillness requirement meant the reference never built while the
* user was watching, so the NUC never activated and the raw mura
* was displayed. */
if (g_ref_phase == 1 || g_ref_phase == 2) {
for (int i = 0; i < NPIX; ++i) {
g_ref_samples[i][g_ref_n] = g_live[i];
if (g_ref_n == 0) { g_ref_min[i] = g_ref_max[i] = g_live[i]; }
else {
if (g_live[i] < g_ref_min[i]) g_ref_min[i] = g_live[i];
if (g_live[i] > g_ref_max[i]) g_ref_max[i] = g_live[i];
}
}
g_ref_n++;
if (g_ref_n == REF_INIT_N) {
int nb = 0;
/* per-pixel median over the window */
static unsigned short tmp[REF_INIT_N];
for (int i = 0; i < NPIX; ++i) {
for (int k = 0; k < REF_INIT_N; ++k) tmp[k] = g_ref_samples[i][k];
for (int a = 1; a < REF_INIT_N; ++a) { /* insertion sort */
unsigned short key = tmp[a];
int b = a - 1;
while (b >= 0 && tmp[b] > key) { tmp[b + 1] = tmp[b]; b--; }
tmp[b + 1] = key;
}
/* trimmed median: average of the middle 50% (drops
* top/bottom 25% outliers - a transient object or a
* dead pixel spike cannot shift the flat field) */
{
long s = 0;
int n = 0;
for (int k = REF_INIT_N / 4; k < REF_INIT_N * 3 / 4; ++k) {
s += tmp[k];
n++;
}
g_reference[i] = (unsigned short)(s / n);
}
g_bad[i] = 0;
}
/* bad pixel detection only on the first collection;
* post-FFC re-collection keeps the existing bad map */
if (g_ref_phase == 1) {
/* temporal detection: min-max fluctuation */
for (int i = 0; i < NPIX; ++i) {
if ((int)g_ref_max[i] - (int)g_ref_min[i] > 400) { g_bad[i] = 1; nb++; }
}
/* histogram-peak-deviation detection (Seek method):
* bad if value > histPeak - (frameMax - histPeak),
* guarded to at least histPeak + 200 */
{
static unsigned hist[65536];
unsigned peakv = 0, peakc = 0, maxv = 0;
for (int i = 0; i < 65536; ++i) hist[i] = 0;
for (int i = 0; i < NPIX; ++i) {
unsigned v = g_reference[i];
if (++hist[v] > peakc) { peakc = hist[v]; peakv = v; }
if (v > maxv) maxv = v;
}
long thr = (long)peakv - ((long)maxv - (long)peakv);
if (thr < (long)peakv + 200) thr = (long)peakv + 200;
for (int i = 0; i < NPIX; ++i) {
if (!g_bad[i] && (long)g_reference[i] > thr) { g_bad[i] = 1; nb++; }
}
}
g_bad_count = nb;
/* topological fill order: copy of the bad mask evolves
* as pixels are filled (edge-first for clusters) */
unsigned char w[NPIX];
for (int i = 0; i < NPIX; ++i) w[i] = g_bad[i];
g_bad_order_len = 0;
build_fill_order(w, (int (*)[2])g_bad_order, &g_bad_order_len);
/* fill reference values in the stored order */
for (int i = 0; i < NPIX; ++i) w[i] = g_bad[i];
for (int i = 0; i < g_bad_order_len; ++i) {
int x = g_bad_order[i][0], y = g_bad_order[i][1];
int v = neighbor_mean(g_reference, w, x, y);
if (v > 0) {
g_reference[y * W + x] = (unsigned short)v;
w[y * W + x] = 0;
}
}
} else {
/* post-FFC re-collection: correct the new reference
* with the existing bad map */
for (int i = 0; i < NPIX; ++i)
g_bad[i] = g_bad[i]; /* keep map */
for (int i = 0; i < g_bad_order_len; ++i) {
int x = g_bad_order[i][0], y = g_bad_order[i][1];
int v = neighbor_mean(g_reference, g_bad, x, y);
if (v > 0) g_reference[y * W + x] = (unsigned short)v;
}
}
g_has_reference = 1;
g_bad_done = 1;
int was = g_ref_phase;
g_ref_phase = 0;
memset(g_fg_count, 0, sizeof(g_fg_count));
char st[96];
snprintf(st, sizeof(st), "reference captured (phase %d) - bad: %d",
was, nb);
SetWindowTextA(g_hwnd, st);
}
}
else if (g_has_reference && g_skip_ffc <= 0 && g_diff_mode) {
/* MOG-style per-pixel background model (csdk) with self-healing:
* only used for the optional diff display. The NUC flat-field
* reference must stay FROZEN (re-collected after each FFC only):
* updating it here would absorb the background into the
* reference, killing the NUC and re-baking the mura in. */
mag160c_display_ref_track_heal(g_reference, g_live, W, H,
REF_T, REF_ALPHA, g_fg_count,
REF_SELFHEAL_N, 2);
}
/* keep the previous frame for the init quiet-gate */
memcpy(g_prev_frame, g_live, sizeof(g_live));
unsigned hist[65536] = {0};
unsigned nz = 0;
for (int i = 0; i < NPIX; ++i) {
unsigned v = g_live[i];
if (v == 0) continue;
hist[v]++;
nz++;
}
/* absolute mode window: computed on the NUC output (flat-field
* corrected) values, which cluster tightly around the reference
* mean (measured std ~29 after NUC). A narrow median +- span
* window gives the high contrast the official app gets from its
* temperature window, while the NUC removed the mura. */
unsigned mn = 0, mx = 0, acc = 0;
unsigned mid_target = (unsigned)(nz / 2);
for (unsigned k = 0; k < 65536; ++k) {
acc += hist[k];
if (acc >= mid_target) { mx = k; break; }
}
{
unsigned med = mx;
unsigned lo2 = med > 1500 ? med - 1500 : 0;
unsigned hi2 = med + 1500;
if (hi2 > 65535) hi2 = 65535;
mn = lo2;
mx = hi2;
}
render(mn, mx);
g_fcount++;
DWORD now = GetTickCount();
if (now - lfps_t >= 1000) {
g_fps = (g_fcount - lfps_c) * 1000.0 / (now - lfps_t);
lfps_t = now;
lfps_c = g_fcount;
}
snprintf(g_title, sizeof(g_title),
"MAG160C Demo v3 - frame %u type=%u range [%u..%u] ffc=%d",
c, (unsigned)hdr[12], mn, mx, ffc_stall);
SetWindowTextA(g_hwnd, g_title);
InvalidateRect(g_hwnd, NULL, FALSE);
UpdateWindow(g_hwnd);
}
done:
Sleep(300);
libusb_clear_halt(g_h, 0x03);
libusb_clear_halt(g_h, 0x82);
sendcmd(0x6bb6b674, 0, 4);
libusb_close(g_h);
libusb_exit(g_ctx);
return 0;
}
File diff suppressed because it is too large Load Diff
+443
View File
@@ -0,0 +1,443 @@
/* MAG160C Windows Demo (libusb0 = libusb-win32 backend, same as the official
* EloThermal demo). Uses the exact transfer layer the vendor demo ships
* with, so the type=0 (temperature) stream stays alive with FFC switching.
*
* Build:
* gcc -std=c11 -mwindows mag160c_demo_usb0.c libusb0.a -o mag160c_demo_usb0.exe
* copy libusb0.dll next to the exe.
*/
#define _WIN32_WINNT 0x0601
#include <windows.h>
#include <stdio.h>
#include <stdint.h>
/* ---- libusb-win32 (libusb0) API ---------------------------------------- */
typedef struct usb_bus {
struct usb_bus *next, *prev;
char dirname[512];
struct usb_device *devices;
unsigned long location;
struct usb_device *root_dev;
} usb_bus;
typedef struct usb_device_descriptor {
uint8_t bLength, bDescriptorType;
uint16_t bcdUSB;
uint8_t bDeviceClass, bDeviceSubClass, bDeviceProtocol, bMaxPacketSize0;
uint16_t idVendor, idProduct, bcdDevice;
uint8_t iManufacturer, iProduct, iSerialNumber, bNumConfigurations;
} usb_device_descriptor;
typedef struct usb_device {
struct usb_device *next, *prev;
char filename[512];
struct usb_bus *bus;
usb_device_descriptor descriptor;
void *config;
void *dev;
uint8_t devnum;
unsigned char num_children;
struct usb_device **children;
} usb_device;
typedef struct usb_dev_handle usb_dev_handle;
typedef int (*fn_usb_init)(void);
typedef int (*fn_usb_find_busses)(void);
typedef int (*fn_usb_find_devices)(void);
typedef usb_bus *(*fn_usb_get_busses)(void);
typedef usb_dev_handle *(*fn_usb_open)(usb_device *);
typedef int (*fn_usb_set_configuration)(usb_dev_handle *, int);
typedef int (*fn_usb_claim_interface)(usb_dev_handle *, int);
typedef int (*fn_usb_bulk_write)(usb_dev_handle *, int, const char *, int, int);
typedef int (*fn_usb_bulk_read)(usb_dev_handle *, int, char *, int, int);
typedef int (*fn_usb_close)(usb_dev_handle *);
static fn_usb_init p_usb_init;
static fn_usb_find_busses p_usb_find_busses;
static fn_usb_find_devices p_usb_find_devices;
static fn_usb_get_busses p_usb_get_busses;
static fn_usb_open p_usb_open;
static fn_usb_set_configuration p_usb_set_configuration;
static fn_usb_claim_interface p_usb_claim_interface;
static fn_usb_bulk_write p_usb_bulk_write;
static fn_usb_bulk_read p_usb_bulk_read;
static fn_usb_close p_usb_close;
static usb_dev_handle *g_dev;
#define MAG_CMD_PREPARE1 0x6bb6b66b
#define MAG_CMD_PREPARE2 0x6bb6b66c
#define MAG_CMD_GET_INFO 0x6bb6b66f
#define MAG_CMD_FFC 0x6bb6b672
#define MAG_CMD_START 0x6bb6b673
#define MAG_CMD_STOP 0x6bb6b674
#define IR_W 160
#define IR_H 120
#define FRAME_LEN (IR_W * IR_H * 2)
static unsigned char g_frame[FRAME_LEN];
static unsigned char g_bmp[54 + IR_W * IR_H * 3];
static char g_status[512];
static volatile unsigned g_frame_count;
static volatile unsigned g_frame_type;
static int g_diff_mode = 1;
static unsigned short g_reference[IR_W * IR_H];
static int g_has_reference;
static double g_fps;
static int g_probe_x = -1, g_probe_y = -1;
static int g_max_x = -1, g_max_y = -1;
static double g_max_temp = -100.0;
static double g_center_temp = -100.0;
static FILE *g_log;
static int sendcmd(unsigned magic, unsigned param, int len) {
unsigned char cmd[8] = {0};
cmd[0] = (unsigned char)(magic);
cmd[1] = (unsigned char)(magic >> 8);
cmd[2] = (unsigned char)(magic >> 16);
cmd[3] = (unsigned char)(magic >> 24);
if (len >= 8) {
cmd[4] = (unsigned char)(param);
cmd[5] = (unsigned char)(param >> 8);
cmd[6] = (unsigned char)(param >> 16);
cmd[7] = (unsigned char)(param >> 24);
}
int xfer = 0;
if (p_usb_bulk_write(g_dev, 0x03, (char *)cmd, len, 2000) != len) return -1;
unsigned char resp[0x1000];
xfer = p_usb_bulk_read(g_dev, 0x82, (char *)resp, sizeof(resp), 2000);
if (xfer < 4) return -1;
return 0;
}
static void render(const unsigned char *frame) {
int hot = 0;
unsigned hotv = 0;
unsigned center_sum = 0;
unsigned char *px = g_bmp + 54;
for (int y = 0; y < IR_H; ++y) {
for (int x = 0; x < IR_W; ++x) {
int o = (y * IR_W + x) * 2;
unsigned v = (unsigned)frame[o] | ((unsigned)frame[o + 1] << 8);
int diffv = 0;
if (g_diff_mode && g_has_reference)
diffv = (int)v - (int)g_reference[y * IR_W + x];
if (x >= 70 && x < 90 && y >= 50 && y < 70) center_sum += v;
if (v > hotv && v != 0) { hotv = v; hot = y * IR_W + x; }
unsigned char r, g, b;
if (g_diff_mode && g_has_reference) {
int span = 2000;
if (diffv > 0) {
unsigned idx = (unsigned)(diffv * 255 / span);
if (idx > 255) idx = 255;
r = (unsigned char)(128 + idx / 2);
g = (unsigned char)(128 - idx / 2);
b = (unsigned char)(128 - idx / 2);
} else {
unsigned idx = (unsigned)(-diffv * 255 / span);
if (idx > 255) idx = 255;
r = (unsigned char)(128 - idx / 2);
g = (unsigned char)(128 - idx / 2);
b = (unsigned char)(128 + idx / 2);
}
} else {
/* absolute with fixed range (type=0 data ~ 8000..30000) */
unsigned idx = (v - 8000) * 255 / 22000;
if (idx > 255) idx = 255;
if (idx < 64) { r = 0; g = (unsigned char)(idx * 4); b = 255; }
else if (idx < 128) { r = 0; g = 255; b = (unsigned char)(255 - (idx - 64) * 4); }
else if (idx < 192) { r = (unsigned char)((idx - 128) * 4); g = 255; b = 0; }
else { r = 255; g = (unsigned char)(255 - (idx - 192) * 4); b = 0; }
}
int dst = (IR_H - 1 - y) * IR_W * 3 + x * 3;
px[dst + 0] = b; px[dst + 1] = g; px[dst + 2] = r;
}
}
if (hot >= 0) {
g_max_x = hot % IR_W;
g_max_y = hot / IR_W;
g_max_temp = 25.0 + ((int)hotv - (int)g_reference[hot]) / 147.0;
int mx2 = g_max_x, my2 = IR_H - 1 - g_max_y;
for (int k = -2; k <= 2; ++k) {
if (mx2 + k >= 0 && mx2 + k < IR_W) {
int d2 = my2 * IR_W * 3 + (mx2 + k) * 3;
px[d2] = 255; px[d2 + 1] = 255; px[d2 + 2] = 255;
}
if (my2 + k >= 0 && my2 + k < IR_H) {
int d2 = (my2 + k) * IR_W * 3 + mx2 * 3;
px[d2] = 255; px[d2 + 1] = 255; px[d2 + 2] = 255;
}
}
}
g_center_temp = 25.0 + ((int)(center_sum / 400) - (int)g_reference[50 * IR_W + 70]) / 147.0;
}
static HWND g_hwnd;
static HFONT g_font;
static void set_status(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
vsnprintf(g_status, sizeof(g_status), fmt, ap);
va_end(ap);
InvalidateRect(g_hwnd, NULL, TRUE);
}
static void save_bmp(void) {
char path[MAX_PATH];
SYSTEMTIME st;
GetLocalTime(&st);
snprintf(path, sizeof(path), "thermal_%04d%02d%02d_%02d%02d%02d.bmp",
st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
FILE *f = fopen(path, "wb");
if (f) { fwrite(g_bmp, 1, sizeof(g_bmp), f); fclose(f); set_status("saved %s", path); }
}
static void do_ffc(void) {
sendcmd(MAG_CMD_FFC, 1, 8);
Sleep(300);
sendcmd(MAG_CMD_FFC, 0, 8);
set_status("FFC triggered");
}
static LRESULT CALLBACK wndproc(HWND hw, UINT msg, WPARAM wp, LPARAM lp) {
switch (msg) {
case WM_PAINT: {
PAINTSTRUCT ps;
HDC dc = BeginPaint(hw, &ps);
HDC mem = CreateCompatibleDC(dc);
HBITMAP bm = CreateCompatibleBitmap(dc, IR_W, IR_H);
HGDIOBJ old = SelectObject(mem, bm);
SetDIBitsToDevice(mem, 0, 0, IR_W, IR_H, 0, 0, 0, IR_H, g_bmp + 54,
(BITMAPINFO *)(g_bmp + 14), DIB_RGB_COLORS);
StretchBlt(dc, 10, 10, 640, 480, mem, 0, 0, IR_W, IR_H, SRCCOPY);
SelectObject(mem, old);
DeleteObject(bm);
DeleteDC(mem);
SelectObject(dc, g_font);
SetBkMode(dc, TRANSPARENT);
SetTextColor(dc, RGB(220, 220, 220));
int y = 20;
char line[256];
snprintf(line, sizeof(line), "frame : %u (type %u)", g_frame_count, g_frame_type);
TextOutA(dc, 670, y, line, (int)strlen(line)); y += 24;
snprintf(line, sizeof(line), "fps : %.1f", g_fps);
TextOutA(dc, 670, y, line, (int)strlen(line)); y += 24;
if (g_probe_x >= 0) {
double t = 25.0 + ((int)(g_frame[g_probe_y * IR_W * 2 + g_probe_x * 2] |
(g_frame[g_probe_y * IR_W * 2 + g_probe_x * 2 + 1] << 8)) -
(int)g_reference[g_probe_y * IR_W + g_probe_x]) / 147.0;
snprintf(line, sizeof(line), "probe : (%d,%d) %.2f C", g_probe_x, g_probe_y, t);
TextOutA(dc, 670, y, line, (int)strlen(line)); y += 24;
}
if (g_max_x >= 0) {
snprintf(line, sizeof(line), "max : (%d,%d) %.2f C", g_max_x, g_max_y, g_max_temp);
TextOutA(dc, 670, y, line, (int)strlen(line)); y += 24;
}
snprintf(line, sizeof(line), "center: %.2f C", g_center_temp);
TextOutA(dc, 670, y, line, (int)strlen(line)); y += 24;
snprintf(line, sizeof(line), "mode : %s", g_diff_mode ? "DIFF" : "absolute");
TextOutA(dc, 670, y, line, (int)strlen(line)); y += 24;
SetTextColor(dc, RGB(120, 220, 120));
TextOutA(dc, 10, 500, g_status, (int)strlen(g_status));
EndPaint(hw, &ps);
break;
}
case WM_LBUTTONDOWN: {
int x = LOWORD(lp), y = HIWORD(lp);
if (x >= 10 && x < 650 && y >= 10 && y < 490) {
g_probe_x = (x - 10) * IR_W / 640;
g_probe_y = IR_H - 1 - (y - 10) * IR_H / 480;
InvalidateRect(hw, NULL, TRUE);
}
break;
}
case WM_COMMAND:
switch (LOWORD(wp)) {
case 1001: do_ffc(); break;
case 1002: save_bmp(); break;
case 1003: g_diff_mode = !g_diff_mode; break;
case 1004:
for (int i = 0; i < FRAME_LEN; i += 2)
g_reference[i / 2] = (unsigned short)(g_frame[i] | (g_frame[i + 1] << 8));
g_has_reference = 1;
g_diff_mode = 1;
set_status("reference captured");
break;
case 1005: g_probe_x = -1; InvalidateRect(hw, NULL, TRUE); break;
}
break;
case WM_ERASEBKGND:
return 1;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hw, msg, wp, lp);
}
static int open_camera(void) {
p_usb_init();
p_usb_find_busses();
p_usb_find_devices();
for (usb_bus *bus = p_usb_get_busses(); bus; bus = bus->next) {
for (usb_device *dev = bus->devices; dev; dev = dev->next) {
if (dev->descriptor.idVendor == 0x833c) {
g_dev = p_usb_open(dev);
if (g_dev) {
p_usb_set_configuration(g_dev, 1);
if (p_usb_claim_interface(g_dev, 0) == 0) return 0;
p_usb_close(g_dev);
g_dev = NULL;
}
}
}
}
return -1;
}
static int init_camera(void) {
for (int attempt = 0; attempt < 4; ++attempt) {
if (g_log) { fprintf(g_log, "init attempt %d\n", attempt + 1); fflush(g_log); }
if (open_camera() != 0) { Sleep(2000); continue; }
if (sendcmd(MAG_CMD_PREPARE1, 0, 4)) { Sleep(2000); continue; }
if (sendcmd(MAG_CMD_PREPARE2, 0, 4)) { Sleep(2000); continue; }
if (sendcmd(MAG_CMD_GET_INFO, 0, 4)) { Sleep(2000); continue; }
if (sendcmd(MAG_CMD_FFC, 0, 8)) { Sleep(2000); continue; }
Sleep(100);
if (sendcmd(MAG_CMD_FFC, 0, 8)) { Sleep(2000); continue; }
Sleep(300);
if (sendcmd(MAG_CMD_START, 0, 4)) { Sleep(2000); continue; }
Sleep(700);
if (g_log) { fprintf(g_log, "init ok (attempt %d)\n", attempt + 1); fflush(g_log); }
return 0;
}
return -1;
}
int WINAPI WinMain(HINSTANCE inst, HINSTANCE hprev, LPSTR cmd, int show) {
(void)hprev; (void)cmd; (void)show;
g_log = fopen("C:/Users/ZXC/AppData/Local/Temp/opencode/demo_usb0_log.txt", "w");
if (g_log) { fprintf(g_log, "demo start\n"); fflush(g_log); }
HMODULE l0 = LoadLibraryA("libusb0.dll");
if (!l0) { MessageBoxA(NULL, "libusb0.dll not found", "MAG160C Demo", MB_ICONERROR); return 1; }
p_usb_init = (fn_usb_init)GetProcAddress(l0, "usb_init");
p_usb_find_busses = (fn_usb_find_busses)GetProcAddress(l0, "usb_find_busses");
p_usb_find_devices = (fn_usb_find_devices)GetProcAddress(l0, "usb_find_devices");
p_usb_get_busses = (fn_usb_get_busses)GetProcAddress(l0, "usb_get_busses");
p_usb_open = (fn_usb_open)GetProcAddress(l0, "usb_open");
p_usb_set_configuration = (fn_usb_set_configuration)GetProcAddress(l0, "usb_set_configuration");
p_usb_claim_interface = (fn_usb_claim_interface)GetProcAddress(l0, "usb_claim_interface");
p_usb_bulk_write = (fn_usb_bulk_write)GetProcAddress(l0, "usb_bulk_write");
p_usb_bulk_read = (fn_usb_bulk_read)GetProcAddress(l0, "usb_bulk_read");
p_usb_close = (fn_usb_close)GetProcAddress(l0, "usb_close");
if (init_camera() != 0) {
MessageBoxA(NULL, "camera init failed", "MAG160C Demo", MB_ICONERROR);
return 1;
}
WNDCLASSA wc = {0};
wc.lpfnWndProc = wndproc;
wc.hInstance = inst;
wc.lpszClassName = "Mag160cDemoUsb0";
wc.hCursor = LoadCursor(NULL, IDC_CROSS);
wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
RegisterClassA(&wc);
g_hwnd = CreateWindowA("Mag160cDemoUsb0", "MAG160C Thermal Demo (libusb0)",
WS_OVERLAPPEDWINDOW, 60, 40, 1000, 600,
NULL, NULL, inst, NULL);
g_font = CreateFontA(18, 0, 0, 0, FW_NORMAL, 0, 0, 0, ANSI_CHARSET,
0, 0, CLEARTYPE_QUALITY, 0, "Consolas");
CreateWindowA("BUTTON", "FFC", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
670, 160, 120, 32, g_hwnd, (HMENU)1001, inst, NULL);
CreateWindowA("BUTTON", "Save BMP", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
800, 160, 120, 32, g_hwnd, (HMENU)1002, inst, NULL);
CreateWindowA("BUTTON", "Diff mode", WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX,
670, 200, 140, 28, g_hwnd, (HMENU)1003, inst, NULL);
CreateWindowA("BUTTON", "Set reference", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
670, 240, 140, 32, g_hwnd, (HMENU)1004, inst, NULL);
CreateWindowA("BUTTON", "Clear probe", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
820, 240, 100, 32, g_hwnd, (HMENU)1005, inst, NULL);
unsigned sz = 54 + IR_W * IR_H * 3;
g_bmp[0] = 'B'; g_bmp[1] = 'M';
g_bmp[2] = (unsigned char)sz; g_bmp[3] = (unsigned char)(sz >> 8);
g_bmp[4] = (unsigned char)(sz >> 16); g_bmp[5] = (unsigned char)(sz >> 24);
g_bmp[10] = 54; g_bmp[14] = 40;
g_bmp[18] = IR_W; g_bmp[19] = 0; g_bmp[22] = IR_H; g_bmp[23] = 0;
g_bmp[26] = 1; g_bmp[28] = 24;
ShowWindow(g_hwnd, SW_SHOW);
set_status("starting...");
unsigned prevcnt = 0xffffffff;
int frames = 0, ffc_done = 0;
unsigned long long ref_sum[IR_W * IR_H] = {0};
int ref_n = 0;
DWORD t0 = GetTickCount();
DWORD last_fps_t = t0;
unsigned last_fps_cnt = 0;
MSG msg;
for (;;) {
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT) goto done;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
unsigned char hdr[64];
int xfer = p_usb_bulk_read(g_dev, 0x81, (char *)hdr, sizeof(hdr), 100);
if (xfer < 28) continue;
unsigned m = (unsigned)hdr[0] | ((unsigned)hdr[1] << 8) |
((unsigned)hdr[2] << 16) | ((unsigned)hdr[3] << 24);
if (m != 0x1bb1b11b) continue;
unsigned c = (unsigned)hdr[4] | ((unsigned)hdr[5] << 8) |
((unsigned)hdr[6] << 16) | ((unsigned)hdr[7] << 24);
if (c == prevcnt) continue;
prevcnt = c;
xfer = p_usb_bulk_read(g_dev, 0x81, (char *)g_frame, sizeof(g_frame), 100);
if (xfer < FRAME_LEN) continue;
frames++;
if (!ffc_done && frames == 3) {
sendcmd(MAG_CMD_FFC, 1, 8); /* switch to type=0 (temperature) */
ffc_done = 1;
Sleep(400);
continue;
}
g_frame_count++;
g_frame_type = (unsigned)hdr[12];
if (!g_has_reference && frames >= 150 && ref_n < 30) {
for (int i = 0; i < FRAME_LEN; i += 2)
ref_sum[i / 2] += (unsigned short)(g_frame[i] | (g_frame[i + 1] << 8));
ref_n++;
if (ref_n == 30) {
for (int i = 0; i < IR_W * IR_H; ++i) g_reference[i] = (unsigned short)(ref_sum[i] / 30);
g_has_reference = 1;
set_status("reference captured - hand shows red");
}
} else if (g_has_reference) {
for (int i = 0; i < FRAME_LEN; i += 2) {
unsigned v = (unsigned)g_frame[i] | ((unsigned)g_frame[i + 1] << 8);
unsigned r = g_reference[i / 2];
g_reference[i / 2] = (unsigned short)((r * 199 + v) / 200);
}
}
render(g_frame);
DWORD now = GetTickCount();
if (now - last_fps_t >= 1000) {
g_fps = (g_frame_count - last_fps_cnt) * 1000.0 / (now - last_fps_t);
last_fps_t = now;
last_fps_cnt = g_frame_count;
}
InvalidateRect(g_hwnd, NULL, FALSE);
UpdateWindow(g_hwnd);
}
done:
sendcmd(MAG_CMD_STOP, 0, 4);
p_usb_close(g_dev);
return 0;
}
+148
View File
@@ -0,0 +1,148 @@
/* MAG160C headless FFC cadence test - replicates the official demo FFC
* timing from analysis/captures/libusb0_trace.txt:
* init: 66b 66c 66f -> FFC(0) -> START
* FFC(1) after ~10th complete frame (switch stream to type=0)
* then every FFC_PERIOD frames: FFC(0), and FFC_GAP frames later FFC(1)
* Target: type=0 stream keeps flowing for 1000+ frames without stalling.
* Prints per-second progress; exits with 0 on success.
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <windows.h>
#include <libusb.h>
#define FFC_PERIOD 400
#define FFC_GAP 9
#define WANT_FRAMES 1400
static libusb_context *g_ctx;
static libusb_device_handle *g_h;
static int sendcmd(unsigned magic, unsigned param, int len) {
unsigned char cmd[8] = {0};
cmd[0] = (unsigned char)(magic);
cmd[1] = (unsigned char)(magic >> 8);
cmd[2] = (unsigned char)(magic >> 16);
cmd[3] = (unsigned char)(magic >> 24);
if (len >= 8) {
cmd[4] = (unsigned char)(param);
cmd[5] = (unsigned char)(param >> 8);
cmd[6] = (unsigned char)(param >> 16);
cmd[7] = (unsigned char)(param >> 24);
}
int xfer = 0;
if (libusb_bulk_transfer(g_h, 0x03, cmd, len, &xfer, 2000)) return -1;
unsigned char resp[0x1000];
if (libusb_bulk_transfer(g_h, 0x82, resp, sizeof(resp), &xfer, 2000)) return -1;
return 0;
}
int main(void) {
printf("MAG160C FFC cadence test (want %d frames)\n", WANT_FRAMES);
if (libusb_init(&g_ctx)) { printf("libusb_init failed\n"); return 1; }
libusb_device **list = NULL;
ssize_t cnt = libusb_get_device_list(g_ctx, &list);
for (ssize_t i = 0; i < cnt && !g_h; ++i) {
struct libusb_device_descriptor d;
libusb_get_device_descriptor(list[i], &d);
if (d.idVendor == 0x833c) libusb_open(list[i], &g_h);
}
libusb_free_device_list(list, 1);
if (!g_h) { printf("no device\n"); libusb_exit(g_ctx); return 1; }
libusb_set_configuration(g_h, 2);
libusb_set_configuration(g_h, 1);
if (libusb_claim_interface(g_h, 0)) { printf("claim failed\n"); return 1; }
if (sendcmd(0x6bb6b66b, 0, 4)) { printf("66b failed\n"); return 1; }
if (sendcmd(0x6bb6b66c, 0, 4)) { printf("66c failed\n"); return 1; }
if (sendcmd(0x6bb6b66f, 0, 4)) { printf("66f failed\n"); return 1; }
if (sendcmd(0x6bb6b672, 0, 8)) { printf("FFC(0) pre failed\n"); return 1; }
Sleep(100);
if (sendcmd(0x6bb6b672, 0, 8)) { printf("FFC(0) pre2 failed\n"); return 1; }
Sleep(300);
if (sendcmd(0x6bb6b673, 0, 4)) { printf("START failed\n"); return 1; }
Sleep(700);
unsigned char frame[40000];
unsigned prev = 0xffffffff;
int nread = 0, n0 = 0, n1 = 0;
int ffc_started = 0, ffc_cycle = 0, ffc_wait1 = 0;
int stall_wakes = 0;
DWORD t_start = GetTickCount();
DWORD last_frame = t_start, last_wake = t_start;
DWORD last_report = t_start;
while (n0 < WANT_FRAMES) {
unsigned char hdr[64];
int xfer = 0;
if (libusb_bulk_transfer(g_h, 0x81, hdr, sizeof(hdr), &xfer, 200) && xfer < 28) {
DWORD now = GetTickCount();
if (now - last_frame > 3000 && now - last_wake > 5000) {
sendcmd(0x6bb6b672, 1, 8);
last_wake = now;
stall_wakes++;
printf(" [stall watchdog] FFC(1) sent (%d)\n", stall_wakes);
}
continue;
}
if (xfer < 28) continue;
unsigned m = (unsigned)hdr[0] | ((unsigned)hdr[1] << 8) |
((unsigned)hdr[2] << 16) | ((unsigned)hdr[3] << 24);
if (m != 0x1bb1b11b) continue;
unsigned c = (unsigned)hdr[4] | ((unsigned)hdr[5] << 8) |
((unsigned)hdr[6] << 16) | ((unsigned)hdr[7] << 24);
if (c == prev) continue;
prev = c;
if (libusb_bulk_transfer(g_h, 0x81, frame, sizeof(frame), &xfer, 200) ||
xfer < 38400) {
continue;
}
unsigned type = (unsigned)hdr[12];
nread++;
last_frame = GetTickCount();
if (type == 0) n0++; else n1++;
if (!ffc_started && nread == 10) {
sendcmd(0x6bb6b672, 1, 8);
ffc_started = 1;
ffc_cycle = 0;
printf(" frame 10: FFC(1) sent -> type=0 mode\n");
continue;
}
if (ffc_started) {
ffc_cycle++;
if (ffc_wait1 && ffc_cycle >= FFC_GAP) {
sendcmd(0x6bb6b672, 1, 8);
ffc_wait1 = 0;
ffc_cycle = 0;
} else if (!ffc_wait1 && ffc_cycle >= FFC_PERIOD) {
sendcmd(0x6bb6b672, 0, 8);
ffc_wait1 = 1;
ffc_cycle = 0;
}
}
DWORD now = GetTickCount();
if (now - last_report >= 5000) {
double secs = (now - t_start) / 1000.0;
printf(" t=%6.1fs frames=%d (type0=%d type1=%d) fps=%.1f\n",
secs, nread, n0, n1, n0 / secs);
last_report = now;
}
}
DWORD t_end = GetTickCount();
double secs = (t_end - t_start) / 1000.0;
printf("\nDONE: %d frames in %.1fs (fps=%.1f) type0=%d type1=%d stall_wakes=%d\n",
nread, secs, nread / secs, n0, n1, stall_wakes);
int pass = (n0 >= 1000) ? 1 : 0;
printf("%s\n", pass ? "PASS: type=0 stream >= 1000 frames" :
"FAIL: type=0 stream < 1000 frames");
Sleep(300);
libusb_clear_halt(g_h, 0x03);
libusb_clear_halt(g_h, 0x82);
sendcmd(0x6bb6b674, 0, 4);
libusb_close(g_h);
libusb_exit(g_ctx);
return pass ? 0 : 2;
}
+121
View File
@@ -0,0 +1,121 @@
/* Save raw type=0 frames (bin) + frame metadata for offline analysis.
* Usage: mag160c_frame_dump <nframes> <outdir>
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <windows.h>
#include <libusb.h>
#define W 160
#define H 120
static libusb_context *g_ctx;
static libusb_device_handle *g_h;
static int sendcmd(unsigned magic, unsigned param, int len) {
unsigned char cmd[8] = {0};
cmd[0] = (unsigned char)(magic);
cmd[1] = (unsigned char)(magic >> 8);
cmd[2] = (unsigned char)(magic >> 16);
cmd[3] = (unsigned char)(magic >> 24);
if (len >= 8) {
cmd[4] = (unsigned char)(param);
cmd[5] = (unsigned char)(param >> 8);
cmd[6] = (unsigned char)(param >> 16);
cmd[7] = (unsigned char)(param >> 24);
}
int xfer = 0;
if (libusb_bulk_transfer(g_h, 0x03, cmd, len, &xfer, 2000)) return -1;
unsigned char resp[0x1000];
if (libusb_bulk_transfer(g_h, 0x82, resp, sizeof(resp), &xfer, 2000)) return -1;
return 0;
}
static int read_frame(unsigned char *frame, unsigned *type) {
unsigned char hdr[64];
static unsigned prev = 0xffffffff;
int xfer = 0;
if (libusb_bulk_transfer(g_h, 0x81, hdr, sizeof(hdr), &xfer, 200) && xfer < 28) return 0;
if (xfer < 28) return 0;
unsigned m = (unsigned)hdr[0] | ((unsigned)hdr[1] << 8) |
((unsigned)hdr[2] << 16) | ((unsigned)hdr[3] << 24);
if (m != 0x1bb1b11b) return 0;
unsigned c = (unsigned)hdr[4] | ((unsigned)hdr[5] << 8) |
((unsigned)hdr[6] << 16) | ((unsigned)hdr[7] << 24);
if (c == prev) return 0;
prev = c;
if (libusb_bulk_transfer(g_h, 0x81, frame, 40000, &xfer, 200) || xfer < 38400) return 0;
*type = (unsigned)hdr[12];
return 1;
}
int main(int argc, char **argv) {
int nframes = argc > 1 ? atoi(argv[1]) : 60;
const char *dir = argc > 2 ? argv[2] : ".";
if (libusb_init(&g_ctx)) return 1;
libusb_device **list = NULL;
ssize_t cnt = libusb_get_device_list(g_ctx, &list);
for (ssize_t i = 0; i < cnt && !g_h; ++i) {
struct libusb_device_descriptor d;
libusb_get_device_descriptor(list[i], &d);
if (d.idVendor == 0x833c) libusb_open(list[i], &g_h);
}
libusb_free_device_list(list, 1);
if (!g_h) { printf("no device\n"); return 1; }
libusb_set_configuration(g_h, 2);
libusb_set_configuration(g_h, 1);
if (libusb_claim_interface(g_h, 0)) return 1;
sendcmd(0x6bb6b66b, 0, 4);
sendcmd(0x6bb6b66c, 0, 4);
sendcmd(0x6bb6b66f, 0, 4);
sendcmd(0x6bb6b672, 0, 8);
Sleep(100);
sendcmd(0x6bb6b672, 0, 8);
Sleep(300);
sendcmd(0x6bb6b673, 0, 4);
Sleep(700);
unsigned char frame[40000];
unsigned type = 1;
int drained = 0;
for (int i = 0; i < 120; ++i) {
if (!read_frame(frame, &type)) { Sleep(10); continue; }
drained++;
if (drained == 10) {
sendcmd(0x6bb6b672, 1, 8);
Sleep(200);
}
if (type == 0) break;
}
if (type != 0) { printf("no type=0 stream\n"); return 3; }
char path[512];
FILE *meta = NULL;
snprintf(path, sizeof(path), "%s/meta.txt", dir);
meta = fopen(path, "w");
for (int n = 0; n < nframes; ++n) {
if (!read_frame(frame, &type)) { Sleep(10); n--; continue; }
snprintf(path, sizeof(path), "%s/f%03d.bin", dir, n);
FILE *f = fopen(path, "wb");
if (f) { fwrite(frame + 28, 1, 38400, f); fclose(f); }
if (meta) {
long s = 0; unsigned mn = 65535, mx = 0;
for (int i = 0; i < W * H; ++i) {
unsigned v = (unsigned)frame[i * 2 + 28] | ((unsigned)frame[i * 2 + 29] << 8);
s += v; if (v < mn) mn = v; if (v > mx) mx = v;
}
fprintf(meta, "%d %u %ld %.0f %u %u\n", n, type, s,
(double)s / (W * H), mn, mx);
}
}
if (meta) fclose(meta);
printf("dumped %d frames to %s\n", nframes, dir);
Sleep(300);
libusb_clear_halt(g_h, 0x03);
libusb_clear_halt(g_h, 0x82);
sendcmd(0x6bb6b674, 0, 4);
libusb_close(g_h);
libusb_exit(g_ctx);
return 0;
}
+178
View File
@@ -0,0 +1,178 @@
/* Capture type=0 frames and dump spatial/temporal statistics to reveal
* fixed-pattern noise (mura), bad pixels, and startup-vs-settled behavior.
* Writes per-pixel mean over N frames + frame-level row/col profiles.
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <windows.h>
#include <libusb.h>
#define W 160
#define H 120
#define NPIX (W * H)
static libusb_context *g_ctx;
static libusb_device_handle *g_h;
static int sendcmd(unsigned magic, unsigned param, int len) {
unsigned char cmd[8] = {0};
cmd[0] = (unsigned char)(magic);
cmd[1] = (unsigned char)(magic >> 8);
cmd[2] = (unsigned char)(magic >> 16);
cmd[3] = (unsigned char)(magic >> 24);
if (len >= 8) {
cmd[4] = (unsigned char)(param);
cmd[5] = (unsigned char)(param >> 8);
cmd[6] = (unsigned char)(param >> 16);
cmd[7] = (unsigned char)(param >> 24);
}
int xfer = 0;
if (libusb_bulk_transfer(g_h, 0x03, cmd, len, &xfer, 2000)) return -1;
unsigned char resp[0x1000];
if (libusb_bulk_transfer(g_h, 0x82, resp, sizeof(resp), &xfer, 2000)) return -1;
return 0;
}
/* return 1 on a valid type=0 frame */
static int read_frame(unsigned char *frame, unsigned *type) {
unsigned char hdr[64];
static unsigned prev = 0xffffffff;
int xfer = 0;
if (libusb_bulk_transfer(g_h, 0x81, hdr, sizeof(hdr), &xfer, 200) && xfer < 28) return 0;
if (xfer < 28) return 0;
unsigned m = (unsigned)hdr[0] | ((unsigned)hdr[1] << 8) |
((unsigned)hdr[2] << 16) | ((unsigned)hdr[3] << 24);
if (m != 0x1bb1b11b) return 0;
unsigned c = (unsigned)hdr[4] | ((unsigned)hdr[5] << 8) |
((unsigned)hdr[6] << 16) | ((unsigned)hdr[7] << 24);
if (c == prev) return 0;
prev = c;
if (libusb_bulk_transfer(g_h, 0x81, frame, 40000, &xfer, 200) || xfer < 38400) return 0;
*type = (unsigned)hdr[12];
return 1;
}
int main(int argc, char **argv) {
int nframes = argc > 1 ? atoi(argv[1]) : 200;
const char *out = argc > 2 ? argv[2] : "frame_stats.txt";
if (libusb_init(&g_ctx)) return 1;
libusb_device **list = NULL;
ssize_t cnt = libusb_get_device_list(g_ctx, &list);
for (ssize_t i = 0; i < cnt && !g_h; ++i) {
struct libusb_device_descriptor d;
libusb_get_device_descriptor(list[i], &d);
if (d.idVendor == 0x833c) libusb_open(list[i], &g_h);
}
libusb_free_device_list(list, 1);
if (!g_h) { printf("no device\n"); return 1; }
libusb_set_configuration(g_h, 2);
libusb_set_configuration(g_h, 1);
if (libusb_claim_interface(g_h, 0)) return 1;
sendcmd(0x6bb6b66b, 0, 4);
sendcmd(0x6bb6b66c, 0, 4);
sendcmd(0x6bb6b66f, 0, 4);
sendcmd(0x6bb6b672, 0, 8);
Sleep(100);
sendcmd(0x6bb6b672, 0, 8);
Sleep(300);
sendcmd(0x6bb6b673, 0, 4);
Sleep(700);
unsigned char frame[40000];
unsigned type = 1;
/* drain + FFC(1) after ~10 complete frames switches the stream to
* type=0 (verified cadence) */
int drained = 0;
for (int i = 0; i < 120; ++i) {
if (!read_frame(frame, &type)) { Sleep(10); continue; }
drained++;
if (drained == 10) {
sendcmd(0x6bb6b672, 1, 8);
Sleep(200);
}
if (type == 0) break;
}
if (type != 0) {
printf("failed to reach type=0 stream\n");
return 3;
}
double mean[NPIX];
memset(mean, 0, sizeof(mean));
double row[H], col[W];
memset(row, 0, sizeof(row));
memset(col, 0, sizeof(col));
unsigned short last[NPIX];
double delta_sum = 0;
int delta_n = 0;
int got = 0, type0 = 0, type1 = 0;
FILE *f = fopen(out, "w");
if (!f) return 2;
for (int n = 0; n < nframes; ++n) {
if (!read_frame(frame, &type)) { Sleep(10); n--; continue; }
got++;
if (type) { type1++; continue; }
type0++;
/* frame stats */
long fr_sum = 0;
unsigned fr_min = 65535, fr_max = 0;
for (int i = 0; i < NPIX; ++i) {
unsigned v = (unsigned)frame[i * 2 + 28] | ((unsigned)frame[i * 2 + 29] << 8);
mean[i] += v;
fr_sum += v;
if (v < fr_min) fr_min = v;
if (v > fr_max) fr_max = v;
if (got > 1) {
int d = (int)v - (int)last[i];
if (d < 0) d = -d;
delta_sum += d;
delta_n++;
}
last[i] = (unsigned short)v;
}
fprintf(f, "FRAME %d type0 mean=%.0f min=%u max=%u meandelta=%.1f\n",
n, (double)fr_sum / NPIX, fr_min, fr_max,
delta_n ? delta_sum / delta_n : 0);
delta_sum = 0;
delta_n = 0;
}
for (int i = 0; i < NPIX; ++i) mean[i] /= type0 ? type0 : 1;
for (int y = 0; y < H; ++y) {
double s = 0;
for (int x = 0; x < W; ++x) s += mean[y * W + x];
row[y] = s / W;
}
for (int x = 0; x < W; ++x) {
double s = 0;
for (int y = 0; y < H; ++y) s += mean[y * W + x];
col[x] = s / H;
}
/* per-pixel deviation from row+col model (mura detection) */
fprintf(f, "\nROW_PROFILE:\n");
for (int y = 0; y < H; ++y) fprintf(f, "%d %.1f\n", y, row[y]);
fprintf(f, "\nCOL_PROFILE:\n");
for (int x = 0; x < W; ++x) fprintf(f, "%d %.1f\n", x, col[x]);
fprintf(f, "\nPIXEL_DEVIATION (mean - row - col + global):\n");
double gm = 0;
for (int i = 0; i < NPIX; ++i) gm += mean[i];
gm /= NPIX;
for (int y = 0; y < H; ++y) {
for (int x = 0; x < W; ++x) {
double dev = mean[y * W + x] - row[y] - col[x] + gm;
fprintf(f, "%d %d %.1f\n", x, y, dev);
}
}
fclose(f);
printf("captured %d frames (type0=%d type1=%d) -> %s\n", got, type0, type1, out);
Sleep(300);
libusb_clear_halt(g_h, 0x03);
libusb_clear_halt(g_h, 0x82);
sendcmd(0x6bb6b674, 0, 4);
libusb_close(g_h);
libusb_exit(g_ctx);
return 0;
}
+90
View File
@@ -0,0 +1,90 @@
/* Dump COMPLETE frames: 28B header + full data read, byte-exact. */
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <windows.h>
#include <libusb.h>
static libusb_context *g_ctx;
static libusb_device_handle *g_h;
static int sendcmd(unsigned magic, unsigned param, int len) {
unsigned char cmd[8] = {0};
cmd[0] = (unsigned char)(magic);
cmd[1] = (unsigned char)(magic >> 8);
cmd[2] = (unsigned char)(magic >> 16);
cmd[3] = (unsigned char)(magic >> 24);
if (len >= 8) {
cmd[4] = (unsigned char)(param);
cmd[5] = (unsigned char)(param >> 8);
cmd[6] = (unsigned char)(param >> 16);
cmd[7] = (unsigned char)(param >> 24);
}
int xfer = 0;
if (libusb_bulk_transfer(g_h, 0x03, cmd, len, &xfer, 2000)) return -1;
unsigned char resp[0x1000];
if (libusb_bulk_transfer(g_h, 0x82, resp, sizeof(resp), &xfer, 2000)) return -1;
return 0;
}
int main(int argc, char **argv) {
int nframes = argc > 1 ? atoi(argv[1]) : 10;
const char *dir = argc > 2 ? argv[2] : ".";
if (libusb_init(&g_ctx)) return 1;
libusb_device **list = NULL;
ssize_t cnt = libusb_get_device_list(g_ctx, &list);
for (ssize_t i = 0; i < cnt && !g_h; ++i) {
struct libusb_device_descriptor d;
libusb_get_device_descriptor(list[i], &d);
if (d.idVendor == 0x833c) libusb_open(list[i], &g_h);
}
libusb_free_device_list(list, 1);
if (!g_h) { printf("no device\n"); return 1; }
libusb_set_configuration(g_h, 2);
libusb_set_configuration(g_h, 1);
libusb_claim_interface(g_h, 0);
sendcmd(0x6bb6b66b, 0, 4);
sendcmd(0x6bb6b66c, 0, 4);
sendcmd(0x6bb6b66f, 0, 4);
sendcmd(0x6bb6b672, 0, 8);
Sleep(100);
sendcmd(0x6bb6b672, 0, 8);
Sleep(300);
sendcmd(0x6bb6b673, 0, 4);
Sleep(700);
unsigned char hdr[64];
unsigned char buf[40000];
unsigned prev = 0xffffffff;
int frames = 0, ffc_done = 0;
while (frames < nframes) {
int xfer = 0;
if (libusb_bulk_transfer(g_h, 0x81, hdr, sizeof(hdr), &xfer, 500) || xfer < 28) { Sleep(10); continue; }
unsigned c = (unsigned)hdr[4] | ((unsigned)hdr[5] << 8) |
((unsigned)hdr[6] << 16) | ((unsigned)hdr[7] << 24);
if (c == prev) continue;
prev = c;
xfer = 0;
if (libusb_bulk_transfer(g_h, 0x81, buf, sizeof(buf), &xfer, 500) || xfer < 38400) { Sleep(10); continue; }
frames++;
if (!ffc_done && frames == 3) { sendcmd(0x6bb6b672, 1, 8); ffc_done = 1; Sleep(200); }
/* save full frame: header (28) + data (xfer) */
char path[512];
snprintf(path, sizeof(path), "%s/raw%03d.bin", dir, frames);
FILE *f = fopen(path, "wb");
if (f) {
fwrite(hdr, 1, 28, f);
fwrite(buf, 1, xfer, f);
fclose(f);
}
}
printf("saved %d full frames to %s\n", frames, dir);
Sleep(300);
libusb_clear_halt(g_h, 0x03);
libusb_clear_halt(g_h, 0x82);
sendcmd(0x6bb6b674, 0, 4);
libusb_close(g_h);
libusb_exit(g_ctx);
return 0;
}
+98
View File
@@ -0,0 +1,98 @@
/* Verify the exact frame layout: print lengths and markers of the two
* bulk reads (header read + data read), and the tail bytes. */
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <windows.h>
#include <libusb.h>
static libusb_context *g_ctx;
static libusb_device_handle *g_h;
static int sendcmd(unsigned magic, unsigned param, int len) {
unsigned char cmd[8] = {0};
cmd[0] = (unsigned char)(magic);
cmd[1] = (unsigned char)(magic >> 8);
cmd[2] = (unsigned char)(magic >> 16);
cmd[3] = (unsigned char)(magic >> 24);
if (len >= 8) {
cmd[4] = (unsigned char)(param);
cmd[5] = (unsigned char)(param >> 8);
cmd[6] = (unsigned char)(param >> 16);
cmd[7] = (unsigned char)(param >> 24);
}
int xfer = 0;
if (libusb_bulk_transfer(g_h, 0x03, cmd, len, &xfer, 2000)) return -1;
unsigned char resp[0x1000];
if (libusb_bulk_transfer(g_h, 0x82, resp, sizeof(resp), &xfer, 2000)) return -1;
return 0;
}
int main(void) {
if (libusb_init(&g_ctx)) return 1;
libusb_device **list = NULL;
ssize_t cnt = libusb_get_device_list(g_ctx, &list);
for (ssize_t i = 0; i < cnt && !g_h; ++i) {
struct libusb_device_descriptor d;
libusb_get_device_descriptor(list[i], &d);
if (d.idVendor == 0x833c) libusb_open(list[i], &g_h);
}
libusb_free_device_list(list, 1);
if (!g_h) { printf("no device\n"); return 1; }
libusb_set_configuration(g_h, 2);
libusb_set_configuration(g_h, 1);
libusb_claim_interface(g_h, 0);
sendcmd(0x6bb6b66b, 0, 4);
sendcmd(0x6bb6b66c, 0, 4);
sendcmd(0x6bb6b66f, 0, 4);
sendcmd(0x6bb6b672, 0, 8);
Sleep(100);
sendcmd(0x6bb6b672, 0, 8);
Sleep(300);
sendcmd(0x6bb6b673, 0, 4);
Sleep(700);
unsigned char hdr[64];
unsigned char buf[40000];
unsigned prev = 0xffffffff;
int frames = 0;
int ffc_done = 0;
while (frames < 6) {
int xfer = 0;
int rc = libusb_bulk_transfer(g_h, 0x81, hdr, sizeof(hdr), &xfer, 500);
if (rc || xfer < 28) { printf("hdr rc=%d xfer=%d\n", rc, xfer); Sleep(10); continue; }
printf("HDR read: xfer=%d marker=%02x %02x %02x %02x\n",
xfer, hdr[0], hdr[1], hdr[2], hdr[3]);
unsigned c = (unsigned)hdr[4] | ((unsigned)hdr[5] << 8) |
((unsigned)hdr[6] << 16) | ((unsigned)hdr[7] << 24);
if (c == prev) { printf(" dup frame %u\n", c); continue; }
prev = c;
xfer = 0;
rc = libusb_bulk_transfer(g_h, 0x81, buf, sizeof(buf), &xfer, 500);
printf("DATA read: rc=%d xfer=%d\n", rc, xfer);
if (xfer >= 8) {
printf(" first8: %02x %02x %02x %02x %02x %02x %02x %02x\n",
buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]);
printf(" last8 : %02x %02x %02x %02x %02x %02x %02x %02x\n",
buf[xfer-8], buf[xfer-7], buf[xfer-6], buf[xfer-5],
buf[xfer-4], buf[xfer-3], buf[xfer-2], buf[xfer-1]);
}
frames++;
if (!ffc_done && frames == 2) { sendcmd(0x6bb6b672, 1, 8); ffc_done = 1; Sleep(200); }
/* also print type */
printf(" type=%u (from hdr[12])\n", (unsigned)hdr[12]);
/* search for 1b b1 b1 1c in the data buffer */
int found = -1;
for (int i = 0; i + 4 <= xfer; ++i)
if (buf[i]==0x1b && buf[i+1]==0xb1 && buf[i+2]==0xb1 && buf[i+3]==0x1c) { found = i; break; }
printf(" trailer 1bb1b11c found at offset %d\n", found);
}
Sleep(300);
libusb_clear_halt(g_h, 0x03);
libusb_clear_halt(g_h, 0x82);
sendcmd(0x6bb6b674, 0, 4);
libusb_close(g_h);
libusb_exit(g_ctx);
return 0;
}
+94
View File
@@ -0,0 +1,94 @@
/* Probe: exact layout of the 0x81 stream as seen by our 2-read capture. */
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <windows.h>
#include <libusb.h>
static libusb_context *g_ctx;
static libusb_device_handle *g_h;
static int sendcmd(unsigned magic, unsigned param, int len) {
unsigned char cmd[8] = {0};
cmd[0] = (unsigned char)(magic);
cmd[1] = (unsigned char)(magic >> 8);
cmd[2] = (unsigned char)(magic >> 16);
cmd[3] = (unsigned char)(magic >> 24);
if (len >= 8) {
cmd[4] = (unsigned char)(param);
cmd[5] = (unsigned char)(param >> 8);
cmd[6] = (unsigned char)(param >> 16);
cmd[7] = (unsigned char)(param >> 24);
}
int xfer = 0;
if (libusb_bulk_transfer(g_h, 0x03, cmd, len, &xfer, 2000)) return -1;
unsigned char resp[0x1000];
if (libusb_bulk_transfer(g_h, 0x82, resp, sizeof(resp), &xfer, 2000)) return -1;
return 0;
}
int main(void) {
libusb_init(&g_ctx);
libusb_device **list = NULL;
ssize_t cnt = libusb_get_device_list(g_ctx, &list);
for (ssize_t i = 0; i < cnt && !g_h; ++i) {
struct libusb_device_descriptor d;
libusb_get_device_descriptor(list[i], &d);
if (d.idVendor == 0x833c) libusb_open(list[i], &g_h);
}
libusb_free_device_list(list, 1);
if (!g_h) { printf("no device\n"); return 1; }
libusb_set_configuration(g_h, 2);
libusb_set_configuration(g_h, 1);
if (libusb_claim_interface(g_h, 0)) return 1;
sendcmd(0x6bb6b66b, 0, 4);
sendcmd(0x6bb6b66c, 0, 4);
sendcmd(0x6bb6b66f, 0, 4);
sendcmd(0x6bb6b672, 0, 8);
Sleep(100);
sendcmd(0x6bb6b672, 0, 8);
Sleep(300);
sendcmd(0x6bb6b673, 0, 4);
Sleep(700);
for (int n = 0; n < 8; ++n) {
unsigned char hdr[64];
int xfer = 0;
if (libusb_bulk_transfer(g_h, 0x81, hdr, sizeof(hdr), &xfer, 200) || xfer < 28) { Sleep(10); continue; }
unsigned m = (unsigned)hdr[0] | ((unsigned)hdr[1] << 8) |
((unsigned)hdr[2] << 16) | ((unsigned)hdr[3] << 24);
if (m != 0x1bb1b11b) { printf("read1: no magic (m=%08x, xfer=%d)\n", m, xfer); continue; }
printf("read1: xfer=%d magic ok type=%u len=%u\n", xfer, hdr[12],
(unsigned)hdr[8] | ((unsigned)hdr[9] << 8) | ((unsigned)hdr[10] << 16) | ((unsigned)hdr[11] << 24));
unsigned char buf[40000];
if (libusb_bulk_transfer(g_h, 0x81, buf, sizeof(buf), &xfer, 200)) { printf("read2 fail\n"); continue; }
printf("read2: xfer=%d\n", xfer);
printf(" buf[0..15] hex: ");
for (int i = 0; i < 16; ++i) printf("%02x ", buf[i]);
printf("\n");
/* search for trailer magic 1bb1b11c */
int found = -1;
for (int i = 0; i + 4 <= xfer; ++i) {
unsigned v = (unsigned)buf[i] | ((unsigned)buf[i+1] << 8) |
((unsigned)buf[i+2] << 16) | ((unsigned)buf[i+3] << 24);
if (v == 0x1bb1b11c) { found = i; break; }
}
printf(" trailer 1bb1b11c at buf+%d (xfer=%d)\n", found, xfer);
printf(" buf[found+4..found+11]: ");
for (int i = 0; i < 8 && found + 4 + i < xfer; ++i) printf("%02x ", buf[found + 4 + i]);
printf("\n");
/* dump u16 pixel candidates at 0 and 28 */
unsigned p0 = buf[0] | (buf[1] << 8);
unsigned p28 = buf[28] | (buf[29] << 8);
printf(" u16@0=%u u16@28=%u (background ~9500-11000)\n", p0, p28);
return 0;
}
printf("no frame seen\n");
Sleep(300);
libusb_clear_halt(g_h, 0x03);
libusb_clear_halt(g_h, 0x82);
sendcmd(0x6bb6b674, 0, 4);
libusb_close(g_h);
libusb_exit(g_ctx);
return 0;
}
+151
View File
@@ -0,0 +1,151 @@
/* Official CoreSDKLib.dll harness: drive the vendor pipeline on the real
* device and dump (a) the official rendered BMP, (b) temperature data,
* (c) the Gray2Temperature LUT. This is the authoritative reference for
* what the official app displays.
*
* Build: gcc official_harness.c -o official_harness.exe -ldl (loads dll
* manually so we can use the vendor's own libusb0.dll from its folder).
*/
#define _WIN32_WINNT 0x0601
#include <windows.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
/* --- MAG API prototypes (from CoreSDKLib.dll exports, verified RVAs) ---
* Initialize 0x22c0 (chan, ...) EnumCameras 0x2590 (chan)
* NewChannel 0x1ee0 (chan) LinkCamera 0x2650 (chan, pid, ...)
* StartProcessImage 0x2cd0 (chan, ...) GetTemperatureData 0x3fc0 (chan,...)
*/
typedef int (*MAG_Initialize_t)(int, int);
typedef void (*MAG_Free_t)(void);
typedef int (*MAG_IsInitialized_t)(int);
typedef int (*MAG_EnumCameras_t)(int);
typedef int (*MAG_NewChannel_t)(int);
typedef int (*MAG_LinkCamera_t)(int, int, int);
typedef void (*MAG_DisLinkCamera_t)(int);
typedef int (*MAG_StartProcessImage_t)(int, void *, int, int);
typedef int (*MAG_StopProcessImage_t)(int);
typedef int (*MAG_IsProcessingImage_t)(int);
typedef int (*MAG_GetOutputBMPdataRGB24_t)(int, unsigned char *, int, int);
typedef int (*MAG_GetOutputBMPdata_t)(int, unsigned char *, int);
typedef int (*MAG_GetTemperatureData_t)(int, int *, int, int);
typedef int (*MAG_GetCamInfo_t)(int, void *);
typedef int (*MAG_TriggerFFC_t)(int, int);
typedef int (*MAG_SetFixPara_t)(int, void *, int);
typedef int (*MAG_GetApproximateGray2TemperatureLUT_t)(int, float *);
static HMODULE g_core;
static MAG_Initialize_t fn_Initialize;
static MAG_Free_t fn_Free;
static MAG_IsInitialized_t fn_IsInitialized;
static MAG_EnumCameras_t fn_EnumCameras;
static MAG_NewChannel_t fn_NewChannel;
static MAG_LinkCamera_t fn_LinkCamera;
static MAG_DisLinkCamera_t fn_DisLinkCamera;
static MAG_StartProcessImage_t fn_StartProcessImage;
static MAG_StopProcessImage_t fn_StopProcessImage;
static MAG_IsProcessingImage_t fn_IsProcessingImage;
static MAG_GetOutputBMPdataRGB24_t fn_GetOutputBMPdataRGB24;
static MAG_GetOutputBMPdata_t fn_GetOutputBMPdata;
static MAG_GetTemperatureData_t fn_GetTemperatureData;
static MAG_GetCamInfo_t fn_GetCamInfo;
static MAG_TriggerFFC_t fn_TriggerFFC;
static MAG_SetFixPara_t fn_SetFixPara;
static MAG_GetApproximateGray2TemperatureLUT_t fn_GetApproximateGray2TemperatureLUT;
#define LOAD(name) fn_##name = (MAG_##name##_t)GetProcAddress(g_core, "MAG_" #name); \
if (!fn_##name) { printf("missing export MAG_" #name "\n"); return 2; }
int main(int argc, char **argv) {
const char *dllpath = argc > 1 ? argv[1]
: "C:\\Project\\MAG160C\\IR_Camera_SDK-1.0.1\\windows\\windows\\app\\CoreSDKLib.dll";
SetDllDirectoryA("C:\\Project\\MAG160C\\IR_Camera_SDK-1.0.1\\windows\\windows\\app");
g_core = LoadLibraryA(dllpath);
if (!g_core) { printf("LoadLibrary failed: %lu\n", GetLastError()); return 1; }
LOAD(Initialize); LOAD(Free); LOAD(IsInitialized); LOAD(EnumCameras);
LOAD(NewChannel); LOAD(LinkCamera); LOAD(DisLinkCamera); LOAD(StartProcessImage);
LOAD(StopProcessImage); LOAD(IsProcessingImage); LOAD(GetOutputBMPdataRGB24);
LOAD(GetOutputBMPdata); LOAD(GetTemperatureData); LOAD(GetCamInfo);
LOAD(TriggerFFC); LOAD(SetFixPara); LOAD(GetApproximateGray2TemperatureLUT);
printf("CoreSDKLib loaded, isInit=%d\n", fn_IsInitialized(0));
int rc = fn_Initialize(0, 0);
printf("MAG_Initialize(0,0) rc=%d\n", rc);
/* official Windows sequence: NewChannel(0) first, then EnumCameras */
int chan = fn_NewChannel(0);
printf("MAG_NewChannel(0) -> channel=%d\n", chan);
rc = fn_EnumCameras(chan);
printf("MAG_EnumCameras(chan) rc=%d\n", rc);
rc = fn_LinkCamera(chan, 0x101, 0);
printf("MAG_LinkCamera(chan,pid,0) rc=%d\n", rc);
/* camera info */
unsigned char info[0x100] = {0};
rc = fn_GetCamInfo(chan, info);
printf("MAG_GetCamInfo rc=%d\n", rc);
rc = fn_StartProcessImage(chan, NULL, 0x10, 0);
printf("MAG_StartProcessImage rc=%d\n", rc);
Sleep(3000);
fn_TriggerFFC(chan, 1);
Sleep(2000);
printf("after FFC(1): isProcessing=%d\n", fn_IsProcessingImage(chan));
/* temperature LUT (256 floats) */
float lut[256];
rc = fn_GetApproximateGray2TemperatureLUT(chan, lut);
printf("Gray2TempLUT rc=%d lut[0]=%.3f lut[64]=%.3f lut[128]=%.3f lut[255]=%.3f\n",
rc, lut[0], lut[64], lut[128], lut[255]);
if (rc == 0) {
FILE *f = fopen("official_g2t_lut.txt", "w");
for (int i = 0; i < 256; ++i) fprintf(f, "%d %.4f\n", i, lut[i]);
fclose(f);
printf("saved official_g2t_lut.txt\n");
}
/* official BMP */
unsigned char bmp[160 * 120 * 3 + 64];
rc = fn_GetOutputBMPdataRGB24(chan, bmp, 160 * 120 * 3, 1);
printf("GetOutputBMPdataRGB24 rc=%d first px RGB=%d,%d,%d\n",
rc, bmp[0], bmp[1], bmp[2]);
if (rc) {
/* save as BMP file */
unsigned sz = 54 + 160 * 120 * 3;
unsigned char hdr[54] = {0};
hdr[0] = 'B'; hdr[1] = 'M';
hdr[2] = sz; hdr[3] = sz >> 8; hdr[4] = sz >> 16; hdr[5] = sz >> 24;
hdr[10] = 54; hdr[14] = 40;
hdr[18] = 160; hdr[22] = 120; hdr[26] = 1; hdr[28] = 24;
FILE *f = fopen("official_render.bmp", "wb");
fwrite(hdr, 1, 54, f);
fwrite(bmp, 1, 160 * 120 * 3, f);
fclose(f);
printf("saved official_render.bmp\n");
}
/* temperature data (19200 ints) */
static int tempdata[19200];
rc = fn_GetTemperatureData(chan, tempdata, 1, 1);
printf("GetTemperatureData rc=%d t[0]=%d t[9600]=%d\n", rc, tempdata[0], tempdata[9600]);
if (rc) {
FILE *f = fopen("official_tempdata.bin", "wb");
fwrite(tempdata, 4, 19200, f);
fclose(f);
printf("saved official_tempdata.bin\n");
}
printf("(inner temp export not present in this dll)\\n");
fn_StopProcessImage(chan);
fn_DisLinkCamera(chan);
fn_Free();
printf("done\n");
return 0;
}
+134
View File
@@ -0,0 +1,134 @@
/* ThermalSDK.dll harness: use the OFFICIAL high-level SDK directly.
* Start() runs the whole vendor pipeline; we capture the rendered IR frame
* via SetNewIRFrameDelegate and read temperatures. This gives the exact
* ground truth of what the official app displays on this hardware.
*
* The IR frame callback delivers a 160x120x3 RGB image (official render).
*/
#define _WIN32_WINNT 0x0601
#include <windows.h>
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
typedef void (CALLBACK *INewIRFrame)(const unsigned char *, int, int, int);
typedef void (CALLBACK *INewDistance)(float);
typedef void (CALLBACK *IDistanceError)();
typedef void (CALLBACK *INewLimitEvent)(bool);
typedef void (CALLBACK *UpdateCallback)(bool, int);
typedef bool (CALLBACK *fn_Start_t)(void);
typedef void (CALLBACK *fn_Stop_t)(void);
typedef bool (CALLBACK *fn_IsWorking_t)(void);
typedef void (CALLBACK *fn_SetNewIRFrameDelegate_t)(INewIRFrame);
typedef void (CALLBACK *fn_SetNewDistanceDelegate_t)(INewDistance);
typedef void (CALLBACK *fn_SetDistanceError_t)(IDistanceError);
typedef void (CALLBACK *fn_ReadTemperatureAtPoint_t)(int, int, unsigned char *);
typedef void (CALLBACK *fn_SetTempBoundary_t)(double, double, double);
typedef void (CALLBACK *fn_SetEmissivity_t)(double);
typedef bool (CALLBACK *fn_Trigger_t)(void);
typedef int (CALLBACK *fn_GetSDKVersion_t)(void);
typedef void (CALLBACK *fn_SetUnitMode_t)(int);
static HMODULE g_tsdk;
static fn_Start_t T_Start;
static fn_Stop_t T_Stop;
static fn_IsWorking_t T_IsWorking;
static fn_SetNewIRFrameDelegate_t T_SetNewIRFrameDelegate;
static fn_SetNewDistanceDelegate_t T_SetNewDistanceDelegate;
static fn_SetDistanceError_t T_SetDistanceError;
static fn_ReadTemperatureAtPoint_t T_ReadTemperatureAtPoint;
static fn_SetTempBoundary_t T_SetTempBoundary;
static fn_SetEmissivity_t T_SetEmissivity;
static fn_Trigger_t T_Trigger;
static fn_GetSDKVersion_t T_GetSDKVersion;
static fn_SetUnitMode_t T_SetUnitMode;
static unsigned char g_rgb[160 * 120 * 3];
static volatile int g_frame_count;
static void CALLBACK onIR(const unsigned char *buf, int w, int h, int ch) {
if (buf && w == 160 && h == 120 && ch == 3 && g_frame_count < 10) {
memcpy(g_rgb, buf, 160 * 120 * 3);
g_frame_count++;
printf(" IR frame %d: first px RGB=%d,%d,%d\n",
g_frame_count, buf[0], buf[1], buf[2]);
} else if (buf) {
memcpy(g_rgb, buf, 160 * 120 * 3);
g_frame_count++;
}
}
static void CALLBACK onDist(float d) { (void)d; }
static void CALLBACK onDistErr() {}
int main(void) {
const char *dir = "C:\\Project\\MAG160C\\IR_Camera_SDK-1.0.1\\windows\\windows\\app";
SetDllDirectoryA(dir);
g_tsdk = LoadLibraryA("ThermalSDK.dll");
if (!g_tsdk) { printf("ThermalSDK load failed %lu\n", GetLastError()); return 1; }
T_Start = (fn_Start_t)GetProcAddress(g_tsdk, "Start");
T_Stop = (fn_Stop_t)GetProcAddress(g_tsdk, "Stop");
T_IsWorking = (fn_IsWorking_t)GetProcAddress(g_tsdk, "IsWorking");
T_SetNewIRFrameDelegate = (fn_SetNewIRFrameDelegate_t)GetProcAddress(g_tsdk, "SetNewIRFrameDelegate");
T_SetNewDistanceDelegate = (fn_SetNewDistanceDelegate_t)GetProcAddress(g_tsdk, "SetNewDistanceDelegate");
T_SetDistanceError = (fn_SetDistanceError_t)GetProcAddress(g_tsdk, "SetDistanceError");
T_ReadTemperatureAtPoint = (fn_ReadTemperatureAtPoint_t)GetProcAddress(g_tsdk, "ReadTemperatureAtPoint");
T_SetTempBoundary = (fn_SetTempBoundary_t)GetProcAddress(g_tsdk, "SetTempBoundary");
T_SetEmissivity = (fn_SetEmissivity_t)GetProcAddress(g_tsdk, "SetEmissivity");
T_Trigger = (fn_Trigger_t)GetProcAddress(g_tsdk, "Trigger");
T_GetSDKVersion = (fn_GetSDKVersion_t)GetProcAddress(g_tsdk, "GetSDKVersion");
T_SetUnitMode = (fn_SetUnitMode_t)GetProcAddress(g_tsdk, "SetUnitMode");
if (!T_Start || !T_SetNewIRFrameDelegate) { printf("missing exports\n"); return 2; }
printf("ThermalSDK version: %d\n", T_GetSDKVersion ? T_GetSDKVersion() : -1);
T_SetUnitMode(0); /* metric */
T_SetTempBoundary(30.0, 44.0, 37.0);
T_SetEmissivity(0.98);
T_SetNewDistanceDelegate(onDist);
T_SetDistanceError(onDistErr);
T_SetNewIRFrameDelegate(onIR);
printf("Start() ...\n");
bool ok = T_Start();
printf("Start -> %d\n", ok);
/* wait for frames */
DWORD t0 = GetTickCount();
while (g_frame_count < 30 && GetTickCount() - t0 < 20000) Sleep(100);
printf("received %d IR frames in %.1fs\n", g_frame_count,
(GetTickCount() - t0) / 1000.0);
/* temperature probe at center */
unsigned char res[64] = {0};
T_ReadTemperatureAtPoint(80, 60, res);
printf("center temp result bytes: ");
for (int i = 0; i < 32; ++i) printf("%02x ", res[i]);
printf("\n");
double *d = (double *)(res + 8);
printf(" raw=%.4f arm=%.4f (guessed layout)\n", d[0], d[1]);
/* save official render */
if (g_frame_count > 0) {
unsigned sz = 54 + 160 * 120 * 3;
unsigned char hdr[54] = {0};
hdr[0] = 'B'; hdr[1] = 'M';
hdr[2] = sz; hdr[3] = sz >> 8; hdr[4] = sz >> 16; hdr[5] = sz >> 24;
hdr[10] = 54; hdr[14] = 40;
hdr[18] = 160; hdr[22] = 120; hdr[26] = 1; hdr[28] = 24;
FILE *f = fopen("official_render.bmp", "wb");
if (f) {
fwrite(hdr, 1, 54, f);
fwrite(g_rgb, 1, 160 * 120 * 3, f);
fclose(f);
printf("saved official_render.bmp\n");
}
/* also save raw RGB dump */
f = fopen("official_render.rgb", "wb");
if (f) { fwrite(g_rgb, 1, 160 * 120 * 3, f); fclose(f); }
}
T_Stop();
printf("done\n");
return 0;
}
+248
View File
@@ -0,0 +1,248 @@
/* Real-time thermal viewer: Win32 window showing live 160x120 frames
with pseudo-color. Esc or close window to stop. */
#define _WIN32_WINNT 0x0601
#include <windows.h>
#include <stdio.h>
#include <libusb.h>
static libusb_device_handle *g_h;
static unsigned char g_frame[40000];
static CRITICAL_SECTION g_lock;
static volatile int g_new_frame;
static volatile int g_running;
static unsigned char g_bmp[54 + 160 * 120 * 3];
static char g_title[128];
static int sendcmd(libusb_device_handle *h, unsigned magic, unsigned param, int len) {
unsigned char cmd[8] = {0};
cmd[0] = (unsigned char)(magic);
cmd[1] = (unsigned char)(magic >> 8);
cmd[2] = (unsigned char)(magic >> 16);
cmd[3] = (unsigned char)(magic >> 24);
if (len >= 8) {
cmd[4] = (unsigned char)(param);
cmd[5] = (unsigned char)(param >> 8);
cmd[6] = (unsigned char)(param >> 16);
cmd[7] = (unsigned char)(param >> 24);
}
int xfer = 0;
int rc = libusb_bulk_transfer(h, 0x03, cmd, len, &xfer, 2000);
if (rc) return rc;
unsigned char resp[0x1000];
rc = libusb_bulk_transfer(h, 0x82, resp, sizeof(resp), &xfer, 2000);
return rc;
}
static void render(const unsigned char *data, unsigned mn, unsigned mx) {
unsigned char *px = g_bmp + 54;
for (int y = 0; y < 120; ++y) {
for (int x = 0; x < 160; ++x) {
int o = (y * 160 + x) * 2 + 28;
unsigned v = (unsigned)data[o] | ((unsigned)data[o+1] << 8);
unsigned idx = (v - mn) * 255 / (mx - mn + 1);
if (idx > 255) idx = 255;
unsigned char r, g, b;
if (idx < 64) { r = 0; g = (unsigned char)(idx*4); b = 255; }
else if (idx < 128) { r = 0; g = 255; b = (unsigned char)(255-(idx-64)*4); }
else if (idx < 192) { r = (unsigned char)((idx-128)*4); g = 255; b = 0; }
else { r = 255; g = (unsigned char)(255-(idx-192)*4); b = 0; }
int dst = (119 - y) * 160 * 3 + x * 3;
px[dst+0] = b; px[dst+1] = g; px[dst+2] = r;
}
}
}
static DWORD WINAPI reader_thread(LPVOID p) {
(void)p;
unsigned char hdr[64];
unsigned prev = 0xffffffff;
int frames = 0;
int ffc_done = 0;
while (g_running) {
int xfer = 0;
if (libusb_bulk_transfer(g_h, 0x81, hdr, sizeof(hdr), &xfer, 2000) || xfer < 28) continue;
unsigned m = (unsigned)hdr[0] | ((unsigned)hdr[1]<<8) |
((unsigned)hdr[2]<<16) | ((unsigned)hdr[3]<<24);
if (m != 0x1bb1b11b) continue;
unsigned c = (unsigned)hdr[4] | ((unsigned)hdr[5]<<8) |
((unsigned)hdr[6]<<16) | ((unsigned)hdr[7]<<24);
if (c == prev) continue;
prev = c;
if (libusb_bulk_transfer(g_h, 0x81, g_frame, sizeof(g_frame), &xfer, 2000) || xfer < 38400) continue;
frames++;
/* switch to type=0 after the 3rd complete frame (device is streaming) */
if (!ffc_done && frames == 3) {
sendcmd(g_h, 0x6bb6b672, 1, 8);
ffc_done = 1;
Sleep(400);
continue;
}
unsigned mn = 0xffff, mx = 0;
for (int k = 0; k < 38400; k += 2) {
unsigned v = (unsigned)g_frame[k] | ((unsigned)g_frame[k+1] << 8);
if (v < mn) mn = v;
if (v > mx) mx = v;
}
EnterCriticalSection(&g_lock);
render(g_frame, mn, mx);
snprintf(g_title, sizeof(g_title), "MAG160C thermal live - frame %u type=%u range [%u..%u]",
c, (unsigned)hdr[12], mn, mx);
g_new_frame = 1;
LeaveCriticalSection(&g_lock);
}
return 0;
}
static LRESULT CALLBACK wndproc(HWND hw, UINT msg, WPARAM wp, LPARAM lp) {
switch (msg) {
case WM_PAINT: {
PAINTSTRUCT ps;
HDC dc = BeginPaint(hw, &ps);
HDC mem = CreateCompatibleDC(dc);
HBITMAP bm = CreateCompatibleBitmap(dc, 160, 120);
HGDIOBJ old = SelectObject(mem, bm);
SetDIBitsToDevice(mem, 0, 0, 160, 120, 0, 0, 0, 120, g_bmp + 54,
(BITMAPINFO *)(g_bmp + 14), DIB_RGB_COLORS);
StretchBlt(dc, 0, 0, 640, 480, mem, 0, 0, 160, 120, SRCCOPY);
SelectObject(mem, old);
DeleteObject(bm);
DeleteDC(mem);
EndPaint(hw, &ps);
break;
}
case WM_ERASEBKGND:
return 1;
case WM_KEYDOWN:
if (wp == VK_ESCAPE) { DestroyWindow(hw); return 0; }
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hw, msg, wp, lp);
}
int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmd, int show) {
(void)prev; (void)cmd; (void)show;
libusb_context *ctx = NULL;
libusb_init(&ctx);
libusb_device **list = NULL;
ssize_t cnt = libusb_get_device_list(ctx, &list);
for (ssize_t i = 0; i < cnt && !g_h; ++i) {
struct libusb_device_descriptor d;
libusb_get_device_descriptor(list[i], &d);
if (d.idVendor == 0x833c) libusb_open(list[i], &g_h);
}
if (!g_h) {
MessageBoxA(NULL, "no MAG160C device found", "MAG160C", MB_ICONERROR);
return 1;
}
libusb_set_configuration(g_h, 2);
libusb_set_configuration(g_h, 1);
libusb_claim_interface(g_h, 0);
sendcmd(g_h, 0x6bb6b66b, 0, 4);
sendcmd(g_h, 0x6bb6b66c, 0, 4);
sendcmd(g_h, 0x6bb6b66f, 0, 4);
sendcmd(g_h, 0x6bb6b672, 0, 8);
sendcmd(g_h, 0x6bb6b672, 0, 8);
Sleep(300);
sendcmd(g_h, 0x6bb6b673, 0, 4);
Sleep(500);
/* FFC(1) is issued from the reader thread after the 3rd frame */
WNDCLASSA wc = {0};
wc.lpfnWndProc = wndproc;
wc.hInstance = inst;
wc.lpszClassName = "ThermalView";
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
RegisterClassA(&wc);
HWND hw = CreateWindowA("ThermalView", "MAG160C thermal live",
WS_OVERLAPPEDWINDOW, 100, 100, 656, 520,
NULL, NULL, inst, NULL);
/* BMP header */
unsigned sz = 54 + 160*120*3;
g_bmp[0]='B'; g_bmp[1]='M';
g_bmp[2]=(unsigned char)sz; g_bmp[3]=(unsigned char)(sz>>8);
g_bmp[4]=(unsigned char)(sz>>16); g_bmp[5]=(unsigned char)(sz>>24);
g_bmp[10]=54; g_bmp[14]=40;
g_bmp[18]=160; g_bmp[19]=0; g_bmp[22]=120; g_bmp[23]=0;
g_bmp[26]=1; g_bmp[28]=24;
InitializeCriticalSection(&g_lock);
g_running = 1;
ShowWindow(hw, SW_SHOW);
/* single-threaded: read one frame, render, pump messages */
int nread = 0;
unsigned prev2 = 0xffffffff;
int ffc2 = 0;
for (;;) {
MSG msg;
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT) goto done;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
{
unsigned char hdr[64];
int xfer = 0;
if (libusb_bulk_transfer(g_h, 0x81, hdr, sizeof(hdr), &xfer, 200) && xfer < 28) continue;
if (xfer < 28) continue;
unsigned m = (unsigned)hdr[0] | ((unsigned)hdr[1]<<8) |
((unsigned)hdr[2]<<16) | ((unsigned)hdr[3]<<24);
if (m != 0x1bb1b11b) continue;
unsigned c = (unsigned)hdr[4] | ((unsigned)hdr[5]<<8) |
((unsigned)hdr[6]<<16) | ((unsigned)hdr[7]<<24);
if (c == prev2) continue;
prev2 = c;
if (libusb_bulk_transfer(g_h, 0x81, g_frame, sizeof(g_frame), &xfer, 200) || xfer < 38400) continue;
nread++;
if (!ffc2 && nread == 3) {
sendcmd(g_h, 0x6bb6b672, 1, 8);
ffc2 = 1;
Sleep(400);
continue;
}
unsigned hist[65536] = {0};
unsigned nz = 0;
for (int k = 0; k < 38400; k += 2) {
unsigned v = (unsigned)g_frame[k] | ((unsigned)g_frame[k+1] << 8);
if (v == 0) continue;
hist[v]++;
nz++;
}
unsigned mn = 0, mx = 0;
unsigned acc = 0;
unsigned lo = (unsigned)(nz * 2 / 100);
unsigned hi = (unsigned)(nz * 98 / 100);
for (unsigned k = 0; k < 65536; ++k) {
acc += hist[k];
if (acc >= lo && mn == 0) mn = k;
if (acc >= hi && mx == 0) { mx = k; break; }
}
if (mx <= mn + 50) { mn = 0; mx = 65535; }
EnterCriticalSection(&g_lock);
render(g_frame, mn, mx);
snprintf(g_title, sizeof(g_title),
"MAG160C thermal live - frame %u type=%u range [%u..%u]",
c, (unsigned)hdr[12], mn, mx);
LeaveCriticalSection(&g_lock);
SetWindowTextA(hw, g_title);
InvalidateRect(hw, NULL, FALSE);
UpdateWindow(hw);
}
}
done:;
g_running = 0;
g_running = 0;
Sleep(300);
libusb_clear_halt(g_h, 0x03);
libusb_clear_halt(g_h, 0x82);
sendcmd(g_h, 0x6bb6b674, 0, 4);
libusb_close(g_h);
libusb_exit(ctx);
return 0;
}
+84
View File
@@ -0,0 +1,84 @@
/* ThermalSDK harness v2: capture official IR frames, save several for
* analysis. The callback delivers w x h x 3 RGB. */
#define _WIN32_WINNT 0x0601
#include <windows.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
typedef void (CALLBACK *INewIRFrame)(const unsigned char *, int, int, int);
typedef void (CALLBACK *INewDistance)(float);
typedef void (CALLBACK *IDistanceError)();
typedef bool (CALLBACK *fn_Start_t)(void);
typedef void (CALLBACK *fn_Stop_t)(void);
typedef void (CALLBACK *fn_SetNewIRFrameDelegate_t)(INewIRFrame);
typedef void (CALLBACK *fn_SetNewDistanceDelegate_t)(INewDistance);
typedef void (CALLBACK *fn_SetDistanceError_t)(IDistanceError);
typedef void (CALLBACK *fn_ReadTemperatureAtPoint_t)(int, int, unsigned char *);
typedef void (CALLBACK *fn_SetTempBoundary_t)(double, double, double);
typedef void (CALLBACK *fn_SetUnitMode_t)(int);
static int g_saved;
static FILE *g_meta;
static void CALLBACK onIR(const unsigned char *buf, int w, int h, int ch) {
if (g_saved < 10 && buf) {
char path[128];
snprintf(path, sizeof(path), "official_ir_%02d.rgb", g_saved);
FILE *f = fopen(path, "wb");
if (f) { fwrite(buf, 1, (size_t)w * h * ch, f); fclose(f); }
fprintf(g_meta, "%d %d %d %d\n", g_saved, w, h, ch);
fflush(g_meta);
g_saved++;
}
}
static void CALLBACK onDist(float d) { (void)d; }
static void CALLBACK onDistErr() {}
int main(void) {
SetDllDirectoryA("C:\\Project\\MAG160C\\IR_Camera_SDK-1.0.1\\windows\\windows\\app");
HMODULE h = LoadLibraryA("ThermalSDK.dll");
if (!h) { printf("load fail %lu\n", GetLastError()); return 1; }
fn_Start_t T_Start = (fn_Start_t)GetProcAddress(h, "Start");
fn_Stop_t T_Stop = (fn_Stop_t)GetProcAddress(h, "Stop");
fn_SetNewIRFrameDelegate_t T_Set = (fn_SetNewIRFrameDelegate_t)GetProcAddress(h, "SetNewIRFrameDelegate");
fn_SetNewDistanceDelegate_t T_Dist = (fn_SetNewDistanceDelegate_t)GetProcAddress(h, "SetNewDistanceDelegate");
fn_SetDistanceError_t T_Err = (fn_SetDistanceError_t)GetProcAddress(h, "SetDistanceError");
fn_ReadTemperatureAtPoint_t T_Temp = (fn_ReadTemperatureAtPoint_t)GetProcAddress(h, "ReadTemperatureAtPoint");
fn_SetTempBoundary_t T_Bound = (fn_SetTempBoundary_t)GetProcAddress(h, "SetTempBoundary");
fn_SetUnitMode_t T_Unit = (fn_SetUnitMode_t)GetProcAddress(h, "SetUnitMode");
if (!T_Start || !T_Set || !T_Temp) { printf("missing exports\n"); return 2; }
g_meta = fopen("official_meta.txt", "w");
T_Unit(0);
T_Bound(30.0, 44.0, 37.0);
T_Set(T_Bound ? onIR : onIR);
T_Dist(onDist);
T_Err(onDistErr);
T_Set(onIR);
printf("Start...\n"); fflush(stdout);
BOOL ok = T_Start();
printf("Start=%d\n", ok); fflush(stdout);
/* wait and read temps at several points */
DWORD t0 = GetTickCount();
int npoints = 0;
while (g_saved < 8 && GetTickCount() - t0 < 15000) {
Sleep(200);
if ((GetTickCount() - t0) > 3000 && npoints < 6) {
int x = 40 + (npoints % 3) * 40, y = 40 + (npoints / 3) * 40;
unsigned char res[64] = {0};
T_Temp(x, y, res);
printf("temp(%d,%d): ", x, y);
for (int i = 0; i < 24; ++i) printf("%02x ", res[i]);
printf("\n");
npoints++;
}
}
printf("saved %d frames\n", g_saved);
if (g_meta) fclose(g_meta);
T_Stop();
printf("done\n");
return 0;
}
+74
View File
@@ -0,0 +1,74 @@
/* Based on tsdk_debug (works): capture frames + read temps. */
#define _WIN32_WINNT 0x0601
#include <windows.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
typedef void (CALLBACK *INewIRFrame)(const unsigned char *, int, int, int);
typedef void (CALLBACK *INewDistance)(float);
typedef void (CALLBACK *IDistanceError)();
typedef bool (CALLBACK *fn_Start_t)(void);
typedef void (CALLBACK *fn_Stop_t)(void);
typedef void (CALLBACK *fn_SetNewIRFrameDelegate_t)(INewIRFrame);
typedef void (CALLBACK *fn_SetNewDistanceDelegate_t)(INewDistance);
typedef void (CALLBACK *fn_SetDistanceError_t)(IDistanceError);
typedef void (CALLBACK *fn_ReadTemperatureAtPoint_t)(int, int, unsigned char *);
static int g_saved;
static unsigned char g_last[320 * 240 * 3];
static void CALLBACK onIR(const unsigned char *buf, int w, int h, int ch) {
if (buf) {
memcpy(g_last, buf, (size_t)w * h * ch);
if (g_saved < 8) {
char path[128];
snprintf(path, sizeof(path), "official_ir_%02d.rgb", g_saved);
FILE *f = fopen(path, "wb");
if (f) { fwrite(buf, 1, (size_t)w * h * ch, f); fclose(f); }
g_saved++;
}
}
}
static void CALLBACK onDist(float d) { (void)d; }
static void CALLBACK onDistErr() {}
int main(void) {
SetDllDirectoryA("C:\\Project\\MAG160C\\IR_Camera_SDK-1.0.1\\windows\\windows\\app");
HMODULE h = LoadLibraryA("ThermalSDK.dll");
printf("load: %p err=%lu\n", (void*)h, GetLastError());
if (!h) return 1;
fn_Start_t T_Start = (fn_Start_t)GetProcAddress(h, "Start");
fn_Stop_t T_Stop = (fn_Stop_t)GetProcAddress(h, "Stop");
fn_SetNewIRFrameDelegate_t T_Set = (fn_SetNewIRFrameDelegate_t)GetProcAddress(h, "SetNewIRFrameDelegate");
fn_SetNewDistanceDelegate_t T_Dist = (fn_SetNewDistanceDelegate_t)GetProcAddress(h, "SetNewDistanceDelegate");
fn_SetDistanceError_t T_Err = (fn_SetDistanceError_t)GetProcAddress(h, "SetDistanceError");
fn_ReadTemperatureAtPoint_t T_Temp = (fn_ReadTemperatureAtPoint_t)GetProcAddress(h, "ReadTemperatureAtPoint");
printf("Start=%p Temp=%p\n", (void*)T_Start, (void*)T_Temp);
T_Dist(onDist);
T_Err(onDistErr);
T_Set(onIR);
printf("Start()...\n"); fflush(stdout);
BOOL ok = T_Start();
printf("Start=%d\n", ok); fflush(stdout);
DWORD t0 = GetTickCount();
int n = 0;
while (GetTickCount() - t0 < 10000) {
Sleep(250);
if (n < 5) {
int x = 80 + (n % 2) * 40, y = 60 + (n / 2) * 30;
unsigned char res[64] = {0};
T_Temp(x, y, res);
printf("temp(%d,%d): ", x, y);
for (int i = 0; i < 24; ++i) printf("%02x ", res[i]);
printf("\n");
fflush(stdout);
n++;
}
}
printf("saved %d frames\n", g_saved);
T_Stop();
printf("done\n");
return 0;
}
+72
View File
@@ -0,0 +1,72 @@
/* ThermalSDK capture v3: save frames + read temperatures. */
#define _WIN32_WINNT 0x0601
#include <windows.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
typedef void (CALLBACK *INewIRFrame)(const unsigned char *, int, int, int);
typedef bool (CALLBACK *fn_Start_t)(void);
typedef void (CALLBACK *fn_Stop_t)(void);
typedef void (CALLBACK *fn_SetNewIRFrameDelegate_t)(INewIRFrame);
typedef void (CALLBACK *fn_ReadTemperatureAtPoint_t)(int, int, unsigned char *);
typedef void (CALLBACK *fn_SetTempBoundary_t)(double, double, double);
typedef void (CALLBACK *fn_SetUnitMode_t)(int);
static volatile int g_frames;
static unsigned char g_last[320 * 240 * 3];
static int g_saved;
static void CALLBACK onIR(const unsigned char *buf, int w, int h, int ch) {
if (buf && w > 0 && h > 0 && ch == 3) {
memcpy(g_last, buf, (size_t)w * h * ch);
if (g_saved < 10) {
char path[128];
snprintf(path, sizeof(path), "official_ir_%02d.rgb", g_saved);
FILE *f = fopen(path, "wb");
if (f) { fwrite(buf, 1, (size_t)w * h * ch, f); fclose(f); }
g_saved++;
}
}
g_frames++;
}
int main(void) {
SetDllDirectoryA("C:\\Project\\MAG160C\\IR_Camera_SDK-1.0.1\\windows\\windows\\app");
HMODULE h = LoadLibraryA("ThermalSDK.dll");
printf("load: %p err=%lu\n", (void*)h, GetLastError());
if (!h) return 1;
fn_Start_t T_Start = (fn_Start_t)GetProcAddress(h, "Start");
fn_Stop_t T_Stop = (fn_Stop_t)GetProcAddress(h, "Stop");
fn_SetNewIRFrameDelegate_t T_Set = (fn_SetNewIRFrameDelegate_t)GetProcAddress(h, "SetNewIRFrameDelegate");
fn_ReadTemperatureAtPoint_t T_Temp = (fn_ReadTemperatureAtPoint_t)GetProcAddress(h, "ReadTemperatureAtPoint");
fn_SetTempBoundary_t T_Bound = (fn_SetTempBoundary_t)GetProcAddress(h, "SetTempBoundary");
fn_SetUnitMode_t T_Unit = (fn_SetUnitMode_t)GetProcAddress(h, "SetUnitMode");
printf("exports: Start=%p Temp=%p\n", (void*)T_Start, (void*)T_Temp);
if (!T_Start || !T_Set || !T_Temp) { printf("missing\n"); return 2; }
T_Unit(0);
T_Bound(30.0, 44.0, 37.0);
T_Set(onIR);
printf("Start()...\n"); fflush(stdout);
BOOL ok = T_Start();
printf("Start=%d\n", ok); fflush(stdout);
DWORD t0 = GetTickCount();
while (g_frames < 12 && GetTickCount() - t0 < 12000) Sleep(100);
int pts[][2] = {{80, 60}, {40, 40}, {120, 80}, {80, 30}, {30, 90}, {130, 100}};
for (int n = 0; n < 6; ++n) {
unsigned char res[64] = {0};
T_Temp(pts[n][0], pts[n][1], res);
printf("temp(%d,%d): ", pts[n][0], pts[n][1]);
for (int i = 0; i < 32; ++i) printf("%02x ", res[i]);
printf("\n");
fflush(stdout);
Sleep(100);
}
printf("frames: %d saved: %d\n", g_frames, g_saved);
T_Stop();
printf("done\n");
return 0;
}
+79
View File
@@ -0,0 +1,79 @@
/* Official CoreSDKLib harness v2: use ThermalSDK high-level (which works),
* but ALSO read raw temperature via CoreSDKLib MAG_GetTemperatureData_Raw
* using the channel that ThermalSDK created. To find the channel, we
* brute-force 0..4 after ThermalSDK Start().
*/
#define _WIN32_WINNT 0x0601
#include <windows.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
typedef void (CALLBACK *INewIRFrame)(const unsigned char *, int, int, int);
typedef bool (CALLBACK *fn_Start_t)(void);
typedef void (CALLBACK *fn_Stop_t)(void);
typedef void (CALLBACK *fn_SetNewIRFrameDelegate_t)(INewIRFrame);
typedef int (CALLBACK *MAG_GetTemperatureDataRaw_t)(int, int *, int, int);
typedef int (CALLBACK *MAG_GetOutputBMPdataRGB24_t)(int, unsigned char *, int, int);
typedef int (CALLBACK *MAG_GetFilteredRaw_t)(int, unsigned short *, int);
typedef int (CALLBACK *MAG_GetFrameStatisticalData_t)(int, void *);
static volatile int g_frames;
static void CALLBACK onIR(const unsigned char *buf, int w, int h, int ch) {
(void)buf; (void)w; (void)h; (void)ch;
g_frames++;
}
int main(void) {
const char *dir = "C:\\Project\\MAG160C\\IR_Camera_SDK-1.0.1\\windows\\windows\\app";
SetDllDirectoryA(dir);
HMODULE t = LoadLibraryA("ThermalSDK.dll");
HMODULE c = LoadLibraryA("CoreSDKLib.dll");
printf("ThermalSDK=%p CoreSDKLib=%p\n", (void*)t, (void*)c);
if (!t || !c) return 1;
fn_Start_t T_Start = (fn_Start_t)GetProcAddress(t, "Start");
fn_Stop_t T_Stop = (fn_Stop_t)GetProcAddress(t, "Stop");
fn_SetNewIRFrameDelegate_t T_Set = (fn_SetNewIRFrameDelegate_t)GetProcAddress(t, "SetNewIRFrameDelegate");
MAG_GetTemperatureDataRaw_t G_TempRaw = (MAG_GetTemperatureDataRaw_t)GetProcAddress(c, "MAG_GetTemperatureData_Raw");
MAG_GetOutputBMPdataRGB24_t G_BMP = (MAG_GetOutputBMPdataRGB24_t)GetProcAddress(c, "MAG_GetOutputBMPdataRGB24");
MAG_GetFilteredRaw_t G_FRaw = (MAG_GetFilteredRaw_t)GetProcAddress(c, "MAG_GetFilteredRaw");
printf("Start=%p TempRaw=%p BMP24=%p FilteredRaw=%p\n",
(void*)T_Start, (void*)G_TempRaw, (void*)G_BMP, (void*)G_FRaw);
T_Set(onIR);
printf("Start()...\n"); fflush(stdout);
BOOL ok = T_Start();
printf("Start=%d\n", ok); fflush(stdout);
Sleep(3000);
printf("frames=%d\n", g_frames);
/* try channels 0..4 for TempRaw */
static int tempdata[20000];
static unsigned short fraw[20000];
for (int chan = 0; chan < 5; ++chan) {
memset(tempdata, 0, sizeof(tempdata));
int rc = G_TempRaw(chan, tempdata, 1, 1);
printf("chan %d: TempRaw rc=%d t[0]=%d t[500]=%d t[9600]=%d\n",
chan, rc, tempdata[0], tempdata[500], tempdata[9600]);
if (rc && tempdata[0] != 0) {
/* save */
FILE *f = fopen("official_tempraw.bin", "wb");
if (f) { fwrite(tempdata, 4, 19200, f); fclose(f); }
printf(" saved official_tempraw.bin\n");
break;
}
memset(fraw, 0, sizeof(fraw));
int rc2 = G_FRaw(chan, fraw, 19200);
printf(" FilteredRaw rc=%d f[0]=%d f[500]=%d\n", rc2, fraw[0], fraw[500]);
if (rc2 && fraw[0] != 0) {
FILE *f = fopen("official_fraw.bin", "wb");
if (f) { fwrite(fraw, 2, 19200, f); fclose(f); }
printf(" saved official_fraw.bin\n");
break;
}
}
T_Stop();
printf("done\n");
return 0;
}
+65
View File
@@ -0,0 +1,65 @@
/* Debug ThermalSDK harness: verbose loading, check every step. */
#define _WIN32_WINNT 0x0601
#include <windows.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
typedef void (CALLBACK *INewIRFrame)(const unsigned char *, int, int, int);
typedef bool (CALLBACK *fn_Start_t)(void);
typedef void (CALLBACK *fn_Stop_t)(void);
typedef void (CALLBACK *fn_SetNewIRFrameDelegate_t)(INewIRFrame);
typedef int (CALLBACK *fn_GetSDKVersion_t)(void);
static volatile int g_frames;
static unsigned char g_last[320 * 240 * 3];
static int g_saved;
static void CALLBACK onIR(const unsigned char *buf, int w, int h, int ch) {
if (buf) {
memcpy(g_last, buf, (size_t)w * h * ch);
if (g_saved < 8) {
char path[128];
snprintf(path, sizeof(path), "official_ir_%02d.rgb", g_saved);
FILE *f = fopen(path, "wb");
if (f) { fwrite(buf, 1, (size_t)w * h * ch, f); fclose(f); }
g_saved++;
}
if (g_frames < 5) {
printf(" IR frame: w=%d h=%d ch=%d first RGB=%d,%d,%d\n",
w, h, ch, buf[0], buf[1], buf[2]);
}
}
g_frames++;
}
int main(void) {
const char *dir = "C:\\Project\\MAG160C\\IR_Camera_SDK-1.0.1\\windows\\windows\\app";
printf("SetDllDirectory: %d\n", SetDllDirectoryA(dir));
HMODULE h = LoadLibraryA("ThermalSDK.dll");
printf("LoadLibrary ThermalSDK: %p err=%lu\n", (void*)h, GetLastError());
if (!h) return 1;
fn_Start_t T_Start = (fn_Start_t)GetProcAddress(h, "Start");
fn_Stop_t T_Stop = (fn_Stop_t)GetProcAddress(h, "Stop");
fn_SetNewIRFrameDelegate_t T_Set = (fn_SetNewIRFrameDelegate_t)GetProcAddress(h, "SetNewIRFrameDelegate");
fn_GetSDKVersion_t T_Ver = (fn_GetSDKVersion_t)GetProcAddress(h, "GetSDKVersion");
printf("exports: Start=%p Set=%p Ver=%p\n", (void*)T_Start, (void*)T_Set, (void*)T_Ver);
/* dependencies */
HMODULE core = LoadLibraryA("CoreSDKLib.dll");
printf("CoreSDKLib: %p err=%lu\n", (void*)core, GetLastError());
HMODULE cam = LoadLibraryA("CameraSDK.dll");
printf("CameraSDK: %p err=%lu\n", (void*)cam, GetLastError());
if (T_Set) T_Set(onIR);
printf("delegate set, calling Start()...\n");
fflush(stdout);
BOOL ok = T_Start ? T_Start() : FALSE;
printf("Start returned: %d\n", ok);
fflush(stdout);
Sleep(8000);
printf("frames received: %d\n", g_frames);
if (T_Stop) T_Stop();
printf("done\n");
return 0;
}
+64
View File
@@ -0,0 +1,64 @@
/* Capture official GRAYSCALE output via ThermalSDK + CoreSDKLib.
* ThermalSDK creates the channel; we find it and read the grayscale
* buffer through MAG_GetOutputBMPdata.
*/
#define _WIN32_WINNT 0x0601
#include <windows.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
typedef void (CALLBACK *INewIRFrame)(const unsigned char *, int, int, int);
typedef bool (CALLBACK *fn_Start_t)(void);
typedef void (CALLBACK *fn_Stop_t)(void);
typedef void (CALLBACK *fn_SetNewIRFrameDelegate_t)(INewIRFrame);
typedef int (CALLBACK *MAG_GetOutputBMPdata_t)(int, int *, unsigned char **);
typedef int (CALLBACK *MAG_GetOutputBMPdataRGB24_t)(int, unsigned char *, int, int);
typedef int (CALLBACK *MAG_GetOutputColorBardata_t)(int, void *, int);
static volatile int g_frames;
static void CALLBACK onIR(const unsigned char *buf, int w, int h, int ch) {
(void)buf; (void)w; (void)h; (void)ch;
g_frames++;
}
int main(void) {
SetDllDirectoryA("C:\\Project\\MAG160C\\IR_Camera_SDK-1.0.1\\windows\\windows\\app");
HMODULE t = LoadLibraryA("ThermalSDK.dll");
HMODULE c = LoadLibraryA("CoreSDKLib.dll");
printf("T=%p C=%p\n", (void*)t, (void*)c);
if (!t || !c) return 1;
fn_Start_t T_Start = (fn_Start_t)GetProcAddress(t, "Start");
fn_Stop_t T_Stop = (fn_Stop_t)GetProcAddress(t, "Stop");
fn_SetNewIRFrameDelegate_t T_Set = (fn_SetNewIRFrameDelegate_t)GetProcAddress(t, "SetNewIRFrameDelegate");
MAG_GetOutputBMPdata_t G_Gray = (MAG_GetOutputBMPdata_t)GetProcAddress(c, "MAG_GetOutputBMPdata");
MAG_GetOutputBMPdataRGB24_t G_RGB = (MAG_GetOutputBMPdataRGB24_t)GetProcAddress(c, "MAG_GetOutputBMPdataRGB24");
MAG_GetOutputColorBardata_t G_Bar = (MAG_GetOutputColorBardata_t)GetProcAddress(c, "MAG_GetOutputColorBardata");
printf("Gray=%p RGB=%p Bar=%p\n", (void*)G_Gray, (void*)G_RGB, (void*)G_Bar);
T_Set(onIR);
printf("Start()...\n"); fflush(stdout);
BOOL ok = T_Start();
printf("Start=%d\n", ok); fflush(stdout);
Sleep(3000);
printf("frames=%d\n", g_frames);
/* try channels 0..4 for the grayscale buffer */
for (int chan = 0; chan < 5; ++chan) {
int w = 0;
unsigned char *buf = NULL;
int rc = G_Gray(chan, &w, &buf);
printf("chan %d: GetOutputBMPdata rc=%d w=%d buf=%p\n", chan, rc, w, (void*)buf);
if (rc && buf) {
/* grayscale is w*h bytes; save it */
int n = w > 0 && w <= 40000 ? w : 19200;
FILE *f = fopen("official_gray.bin", "wb");
if (f) { fwrite(buf, 1, n, f); fclose(f); }
printf(" saved official_gray.bin (%d bytes)\n", n);
break;
}
}
T_Stop();
printf("done\n");
return 0;
}
+70
View File
@@ -0,0 +1,70 @@
/* Capture grayscale AND RGB24 at the same moment via CoreSDKLib, plus
* verify the palette mapping. */
#define _WIN32_WINNT 0x0601
#include <windows.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
typedef void (CALLBACK *INewIRFrame)(const unsigned char *, int, int, int);
typedef bool (CALLBACK *fn_Start_t)(void);
typedef void (CALLBACK *fn_Stop_t)(void);
typedef void (CALLBACK *fn_SetNewIRFrameDelegate_t)(INewIRFrame);
typedef int (CALLBACK *MAG_GetOutputBMPdata_t)(int, int *, unsigned char **);
typedef int (CALLBACK *MAG_GetOutputBMPdataRGB24_t)(int, unsigned char *, int, int);
static volatile int g_frames;
static void CALLBACK onIR(const unsigned char *buf, int w, int h, int ch) {
(void)buf; (void)w; (void)h; (void)ch;
g_frames++;
}
int main(void) {
SetDllDirectoryA("C:\\Project\\MAG160C\\IR_Camera_SDK-1.0.1\\windows\\windows\\app");
HMODULE t = LoadLibraryA("ThermalSDK.dll");
HMODULE c = LoadLibraryA("CoreSDKLib.dll");
if (!t || !c) return 1;
fn_Start_t T_Start = (fn_Start_t)GetProcAddress(t, "Start");
fn_Stop_t T_Stop = (fn_Stop_t)GetProcAddress(t, "Stop");
fn_SetNewIRFrameDelegate_t T_Set = (fn_SetNewIRFrameDelegate_t)GetProcAddress(t, "SetNewIRFrameDelegate");
MAG_GetOutputBMPdata_t G_Gray = (MAG_GetOutputBMPdata_t)GetProcAddress(c, "MAG_GetOutputBMPdata");
MAG_GetOutputBMPdataRGB24_t G_RGB = (MAG_GetOutputBMPdataRGB24_t)GetProcAddress(c, "MAG_GetOutputBMPdataRGB24");
T_Set(onIR);
BOOL ok = T_Start();
printf("Start=%d\n", ok); fflush(stdout);
Sleep(3000);
for (int chan = 0; chan < 5; ++chan) {
int w = 0;
unsigned char *graybuf = NULL;
int rc = G_Gray(chan, &w, &graybuf);
printf("chan %d gray rc=%d buf=%p\n", chan, rc, (void*)graybuf);
if (rc && graybuf) {
/* read true w/h from dev+0xadc/0xae0 = graybuf - 0xaf0 + 0xadc */
unsigned char *dev = graybuf - 0xaf0;
int w2 = *(int *)(dev + 0xadc);
int h2 = *(int *)(dev + 0xae0);
printf(" dev w=%d h=%d\n", w2, h2);
if (w2 > 0 && w2 < 1000 && h2 > 0 && h2 < 1000) {
unsigned char *rgb = malloc((size_t)w2 * h2 * 3);
int rc2 = G_RGB(chan, rgb, w2 * h2 * 3, 1);
printf(" RGB24 rc=%d\n", rc2);
if (rc2) {
FILE *f = fopen("official_gray3.bin", "wb");
if (f) { fwrite(graybuf, 1, (size_t)w2 * h2, f); fclose(f); }
f = fopen("official_rgb3.bin", "wb");
if (f) { fwrite(rgb, 1, (size_t)w2 * h2 * 3, f); fclose(f); }
printf(" saved gray=%dx%d rgb\n", w2, h2);
}
free(rgb);
}
break;
}
}
T_Stop();
printf("done\n");
return 0;
}
+506
View File
@@ -0,0 +1,506 @@
/* tsdk_pair2: same-frame capture of official pipeline outputs.
* Per frame (synced to ThermalSDK callback):
* - gray : 8-bit display gray buffer dev+0x220 (160x120)
* - rgb : RGB24 render 160x120 (MAG_GetOutputBMPdataRGB24, order=1)
* - raw : u16 counts frame dev+0x270 (post-NUC processed frame)
* - palette: 256x4 BGRx dev+0xb18
* - temps : u32 per-pixel temps MAG_GetTemperatureData_Raw
* - cbRGB : ThermalSDK 320x240 callback frame
* - points : ReadTemperatureAtPoint at several points
* Also dumps device-struct bytes around the display header (dev+0xaf0..) and
* the window fields found in the struct, for calibration.
*/
#define _WIN32_WINNT 0x0601
#include <windows.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
typedef void (CALLBACK *INewIRFrame)(const unsigned char *, int, int, int);
typedef bool (CALLBACK *fn_Start_t)(void);
typedef void (CALLBACK *fn_Stop_t)(void);
typedef void (CALLBACK *fn_SetNewIRFrameDelegate_t)(INewIRFrame);
typedef void (CALLBACK *fn_SetTempBoundary_t)(double, double, double);
typedef void (CALLBACK *fn_SetUnitMode_t)(int);
typedef void (CALLBACK *fn_ReadTemperatureAtPoint_t)(int, int, unsigned char *);
typedef int (CALLBACK *MAG_GetOutputBMPdata_t)(int, unsigned char **, void **);
typedef int (CALLBACK *MAG_GetOutputBMPdataRGB24_t)(int, unsigned char *, int, int);
typedef int (CALLBACK *MAG_GetTemperatureData_Raw_t)(int, unsigned int *, int, int);
typedef int (CALLBACK *MAG_GetOutputColorBardata_t)(int, void **, void **);
typedef int (CALLBACK *MAG_TriggerFFC_t)(int, int);
static volatile int g_frames;
static unsigned char g_cb[320 * 240 * 3];
static int g_cbw, g_cbh;
static const char *g_dir = ".";
static void CALLBACK onIR(const unsigned char *buf, int w, int h, int ch) {
if (buf && w > 0 && h > 0 && ch == 3) {
memcpy(g_cb, buf, (size_t)w * h * ch);
g_cbw = w; g_cbh = h;
}
g_frames++;
}
static void save(const char *name, const void *buf, size_t n) {
char path[512];
snprintf(path, sizeof(path), "%s\\%s", g_dir, name);
FILE *f = fopen(path, "wb");
if (f) { fwrite(buf, 1, n, f); fclose(f); }
}
int main(int argc, char **argv) {
int npairs = 24;
int start_delay = 6000;
double tb[3] = {30.0, 44.0, 37.0};
if (argc > 1) g_dir = argv[1];
if (argc > 2) npairs = atoi(argv[2]);
if (argc > 3) start_delay = atoi(argv[3]);
if (argc > 4) tb[0] = atof(argv[4]);
if (argc > 5) tb[1] = atof(argv[5]);
if (argc > 6) tb[2] = atof(argv[6]);
SetDllDirectoryA("C:\\Project\\MAG160C\\IR_Camera_SDK-1.0.1\\windows\\windows\\app");
HMODULE t = LoadLibraryA("ThermalSDK.dll");
HMODULE c = LoadLibraryA("CoreSDKLib.dll");
printf("T=%p C=%p\n", (void*)t, (void*)c);
if (!t || !c) return 1;
fn_Start_t T_Start = (fn_Start_t)GetProcAddress(t, "Start");
fn_Stop_t T_Stop = (fn_Stop_t)GetProcAddress(t, "Stop");
fn_SetNewIRFrameDelegate_t T_Set = (fn_SetNewIRFrameDelegate_t)GetProcAddress(t, "SetNewIRFrameDelegate");
fn_SetTempBoundary_t T_Bound = (fn_SetTempBoundary_t)GetProcAddress(t, "SetTempBoundary");
fn_SetUnitMode_t T_Unit = (fn_SetUnitMode_t)GetProcAddress(t, "SetUnitMode");
fn_ReadTemperatureAtPoint_t T_Temp = (fn_ReadTemperatureAtPoint_t)GetProcAddress(t, "ReadTemperatureAtPoint");
MAG_GetOutputBMPdata_t G_Gray = (MAG_GetOutputBMPdata_t)GetProcAddress(c, "MAG_GetOutputBMPdata");
MAG_GetOutputBMPdataRGB24_t G_RGB = (MAG_GetOutputBMPdataRGB24_t)GetProcAddress(c, "MAG_GetOutputBMPdataRGB24");
MAG_GetTemperatureData_Raw_t G_TempRaw = (MAG_GetTemperatureData_Raw_t)GetProcAddress(c, "MAG_GetTemperatureData_Raw");
MAG_GetOutputColorBardata_t G_Bar = (MAG_GetOutputColorBardata_t)GetProcAddress(c, "MAG_GetOutputColorBardata");
printf("Start=%p TempPoint=%p Gray=%p RGB=%p TempRaw=%p Bar=%p\n",
(void*)T_Start, (void*)T_Temp, (void*)G_Gray, (void*)G_RGB,
(void*)G_TempRaw, (void*)G_Bar);
if (!T_Start || !T_Set || !G_Gray || !G_RGB) { printf("missing export\n"); return 2; }
if (T_Unit) T_Unit(0);
if (T_Bound) T_Bound(tb[0], tb[1], tb[2]);
printf("SetTempBoundary(%.1f, %.1f, %.1f)\n", tb[0], tb[1], tb[2]); fflush(stdout);
T_Set(onIR);
printf("Start()...\n"); fflush(stdout);
BOOL ok = T_Start();
printf("Start=%d\n", ok); fflush(stdout);
printf("warming %d ms...\n", start_delay); fflush(stdout);
Sleep(start_delay);
/* locate channel device */
unsigned char *gray = NULL;
void *hdr = NULL;
int chan = -1;
for (int i = 0; i < 8; ++i) {
int rc = G_Gray(i, &gray, &hdr);
printf("chan %d: rc=%d gray=%p hdr=%p\n", i, rc, (void*)gray, (void*)hdr);
if (rc && gray && hdr) { chan = i; break; }
}
if (chan < 0) { printf("no channel\n"); T_Stop(); return 3; }
unsigned char *dev = (unsigned char *)hdr - 0xaf0;
int w = *(int *)(dev + 0xaf4);
int h = *(int *)(dev + 0xaf8);
printf("dev=%p w=%d h=%d graybuf=%p rawp=%p\n", (void*)dev, w, h,
(void*)gray, (void*)*(void **)(dev + 0x270));
/* probe pointer fields near 0x250..0x2b0 (buffer pointers) */
for (int off = 0x240; off <= 0x2b0; off += 8) {
void *p = *(void **)(dev + off);
printf(" dev+0x%03x = %p\n", off, p);
}
if (w <= 0 || w > 1000 || h <= 0 || h > 1000) { w = 160; h = 120; }
int n = w * h;
int nw = w / 2, nh = h / 2; /* sensor frame 160x120 if 2x mode */
int nn = nw * nh;
printf("capturing %d pairs (disp %dx%d, sensor %dx%d)...\n", npairs, w, h, nw, nh); fflush(stdout);
unsigned char *rgb = malloc((size_t)n * 3);
unsigned short *raw = malloc((size_t)nn * 2);
unsigned char *pal = malloc(256 * 4);
unsigned int *t32 = malloc((size_t)n * 4);
unsigned char *g2 = malloc((size_t)n);
int pts[][2] = {{80, 60}, {40, 40}, {120, 80}, {80, 30}, {30, 90}, {130, 100},
{10, 10}, {150, 110}, {80, 10}, {5, 115}, {155, 5}, {20, 60}};
int np = sizeof(pts) / sizeof(pts[0]);
MAG_TriggerFFC_t G_FFC = (MAG_TriggerFFC_t)GetProcAddress(c, "MAG_TriggerFFC");
printf("TriggerFFC=%p\n", (void*)G_FFC); fflush(stdout);
int prev = g_frames;
for (int k = 0; k < npairs; ++k) {
/* optional FFC triggers at specific pairs */
if (G_FFC && k == npairs / 3) {
printf(">>> TriggerFFC(1)\n"); fflush(stdout);
G_FFC(chan, 1);
Sleep(1500);
prev = g_frames;
}
if (G_FFC && k == 2 * npairs / 3) {
printf(">>> TriggerFFC(0)\n"); fflush(stdout);
G_FFC(chan, 0);
Sleep(1500);
prev = g_frames;
}
/* wait for a new callback frame, then a small settle */
int t0 = GetTickCount();
while (g_frames == prev && GetTickCount() - t0 < 3000) Sleep(5);
prev = g_frames;
Sleep(2);
/* same-frame snapshot */
memcpy(g2, gray, (size_t)n); /* dev+0x220 gray (320x240) */
int rcr = G_RGB(chan, rgb, n * 3, 1); /* RGB24 320x240 */
unsigned short *rawp = *(unsigned short **)(dev + 0x270);
memcpy(raw, rawp, (size_t)nn * 2); /* u16 sensor frame 160x120 */
memcpy(pal, dev + 0xb18, 256 * 4); /* palette */
int rct = G_TempRaw ? G_TempRaw(chan, t32, nn * 4, 1) : 0;
/* frame counter + dev struct fields */
unsigned int fcnt = *(unsigned int *)(dev + 0x8);
unsigned int win_hi = *(unsigned int *)(dev + 0x4202c);
unsigned int win_lo = *(unsigned int *)(dev + 0x42030);
unsigned int win_span = win_hi > win_lo ? win_hi - win_lo : 0;
unsigned int pcount = *(unsigned int *)(dev + 0x47938);
unsigned int comp_off = *(unsigned int *)(dev + 0x47948);
unsigned int comp_shift = *(unsigned int *)(dev + 0x4794c);
unsigned int gain = *(unsigned int *)0x18006ea60; /* global */
unsigned int smooth_mode = *(unsigned int *)(dev + 0x41fdc);
unsigned int has_ref = *(unsigned int *)(dev + 0x41f0c);
unsigned short *refp = *(unsigned short **)(dev + 0x41ef0);
unsigned int m47944 = *(unsigned int *)(dev + 0x47944);
unsigned int m47950 = *(unsigned int *)(dev + 0x47950);
unsigned int m47954 = *(unsigned int *)(dev + 0x47954);
unsigned int t_base = *(unsigned int *)0x1800990c0;
unsigned int t_now = *(unsigned int *)0x1800990c4;
unsigned int m41848 = *(unsigned int *)(dev + 0x41848);
unsigned int m41858 = *(unsigned int *)(dev + 0x41858);
unsigned int m41340 = *(unsigned int *)(dev + 0x41340);
unsigned int m41348 = *(unsigned int *)(dev + 0x41348);
unsigned int m41fe0 = *(unsigned int *)(dev + 0x41fe0);
unsigned int m41f3c = *(unsigned int *)(dev + 0x41f3c);
unsigned int m41840 = *(unsigned int *)(dev + 0x41840);
unsigned int m41554 = *(unsigned int *)(dev + 0x41554);
unsigned int m41558 = *(unsigned int *)(dev + 0x41558);
unsigned int m4155c = *(unsigned int *)(dev + 0x4155c);
unsigned int m41560 = *(unsigned int *)(dev + 0x41560);
unsigned int m41564_0 = *(unsigned int *)(dev + 0x41564);
unsigned int m41568_0 = *(unsigned int *)(dev + 0x41568);
unsigned int m54 = *(unsigned int *)(dev + 0x54);
char base[64];
snprintf(base, sizeof(base), "pair_%03d", k);
{
char nm[128];
snprintf(nm, sizeof(nm), "%s.win", base);
FILE *f = fopen(nm, "wb");
if (f) {
fwrite(&win_hi, 4, 1, f); fwrite(&win_lo, 4, 1, f);
fwrite(&pcount, 4, 1, f);
fwrite(&comp_off, 4, 1, f); fwrite(&comp_shift, 4, 1, f);
fwrite(&gain, 4, 1, f); fwrite(&smooth_mode, 4, 1, f);
fwrite(&has_ref, 4, 1, f); fclose(f);
}
/* extra fields for NUC-path analysis */
snprintf(nm, sizeof(nm), "%s.win2", base);
{
FILE *f2 = fopen(nm, "wb");
if (f2) {
unsigned int v[14] = {m47944, m47950, m47954, t_base, t_now,
m41848, m41858, m41340, m41348, m41fe0,
comp_off, comp_shift, gain, smooth_mode};
fwrite(v, 4, 14, f2); fclose(f2);
}
}
/* NUC lookup tables: thresholds (0x41858) + gain/offset (0x41870) heads */
{
unsigned short th[16], g0[16], g1[16];
memcpy(th, dev + 0x41858, 32);
memcpy(g0, dev + 0x41870, 32);
memcpy(g1, dev + 0x41870 + 2 * 19200 * 2, 32);
snprintf(nm, sizeof(nm), "%s.nuctab", base);
{
FILE *f3 = fopen(nm, "wb");
if (f3) { fwrite(th, 2, 16, f3); fwrite(g0, 2, 16, f3); fwrite(g1, 2, 16, f3); fclose(f3); }
}
printf(" nuctab th[0:4]=%d,%d,%d,%d g0[0:4]=%d,%d,%d,%d g1[0:4]=%d,%d,%d,%d\n",
th[0], th[1], th[2], th[3], g0[0], g0[1], g0[2], g0[3], g1[0], g1[1], g1[2], g1[3]);
}
/* NUC lookup tables: pointers at 0x41858 (thresholds) and 0x41870 (gain/off) */
{
unsigned int nsegs = *(unsigned int *)(dev + 0x41554);
void *thrp = *(void **)(dev + 0x41858);
void *gainp = *(void **)(dev + 0x41870);
printf(" nsegs=%u thrp=%p gainp=%p\n", nsegs, thrp, gainp);
if (thrp) {
snprintf(nm, sizeof(nm), "%s.thr", base);
save(nm, thrp, 19200 * 2 * nsegs);
}
if (gainp && nsegs <= 64) {
snprintf(nm, sizeof(nm), "%s.gain", base);
save(nm, gainp, 19200 * 4 * nsegs);
}
}
/* LUT1024 at dev+0x4284c */
{
unsigned int nsegs = *(unsigned int *)(dev + 0x41554);
unsigned int segsz = nsegs * 19200u * 4u;
printf(" nsegs=%u table_bytes=%u\n", nsegs, segsz);
snprintf(nm, sizeof(nm), "%s.thr_inline", base);
save(nm, dev + 0x41848, (nsegs * 2) > 4096 ? 4096 : (nsegs * 2));
if (segsz <= 24u * 1024u * 1024u && nsegs <= 512) {
snprintf(nm, sizeof(nm), "%s.gain_inline", base);
save(nm, dev + 0x41870, segsz);
}
}
/* LUT1024 at dev+0x4284c */
snprintf(nm, sizeof(nm), "%s.lut", base);
save(nm, dev + 0x4284c, 1024);
/* histogram 256 bins at dev+0x4204c */
snprintf(nm, sizeof(nm), "%s.hist", base);
save(nm, dev + 0x4204c, 256 * 4);
/* reference frame if present */
if (refp) {
snprintf(nm, sizeof(nm), "%s.ref", base);
save(nm, refp, (size_t)nn * 2);
}
/* NUC input candidates: buffers pointed by 0x41848..0x41878 */
{
void *p[8];
p[0] = *(void **)(dev + 0x41840);
p[1] = *(void **)(dev + 0x41848);
p[2] = *(void **)(dev + 0x41850);
p[3] = *(void **)(dev + 0x41858);
p[4] = *(void **)(dev + 0x41860);
p[5] = *(void **)(dev + 0x41868);
p[6] = *(void **)(dev + 0x41870);
p[7] = *(void **)(dev + 0x41878);
printf(" ptrs418x: ");
for (int bi = 0; bi < 8; ++bi) printf("%d=%p ", bi, p[bi]);
printf("\n");
for (int bi = 0; bi < 8; ++bi) {
if (!p[bi]) continue;
snprintf(nm, sizeof(nm), "%s.b%02d", base, bi);
save(nm, p[bi], (size_t)nn * 2);
}
}
/* smoother output buffer (0x41f10+0x10) and 0x41f20 target */
{
void *smo = *(void **)(dev + 0x41f10);
if (smo) {
void *sout = *(void **)((unsigned char *)smo + 0x10);
if (sout) {
snprintf(nm, sizeof(nm), "%s.smo", base);
save(nm, sout, (size_t)nn * 2);
}
void *acc = *(void **)((unsigned char *)smo + 0x18);
if (acc) {
snprintf(nm, sizeof(nm), "%s.smoacc", base);
save(nm, acc, (size_t)nn * 4); /* u32 accumulator */
}
}
void *f20 = *(void **)(dev + 0x41f20);
if (f20) {
snprintf(nm, sizeof(nm), "%s.f20", base);
save(nm, f20, (size_t)nn * 2);
}
}
/* 0x41ee0 smoother accumulator + frame counters */
{
void *e0 = *(void **)(dev + 0x41ee0);
void *e0acc = *(void **)((unsigned char *)dev + 0x41ef8);
if (e0acc) {
snprintf(nm, sizeof(nm), "%s.e0acc", base);
save(nm, e0acc, (size_t)nn * 4);
}
void *e0buf = *(void **)((unsigned char *)dev + 0x41ee8);
if (e0buf) {
snprintf(nm, sizeof(nm), "%s.e0buf", base);
save(nm, e0buf, (size_t)nn * 2);
}
unsigned int cnt1 = *(unsigned int *)(dev + 0x41ee0 + 0x20);
unsigned int cnt2 = *(unsigned int *)(dev + 0x41f10 + 0x20);
unsigned int gfc = *(unsigned int *)(0x180073bec);
unsigned int ffcst = *(unsigned int *)(0x180073bf4);
snprintf(nm, sizeof(nm), "%s.cnt", base);
{
unsigned int v[4] = {cnt1, cnt2, gfc, ffcst};
save(nm, v, 16);
}
printf(" cnt: e0=%u f10=%u globframe=%u ffcstate=%u\n", cnt1, cnt2, gfc, ffcst);
}
/* dump smoother object internals + mode fields */
{
unsigned char s1[0x40], s2[0x40];
unsigned int modes[6];
memcpy(s1, dev + 0x41ee0, 0x40);
memcpy(s2, dev + 0x41f10, 0x40);
modes[0] = *(unsigned int *)(dev + 0x41fd8);
modes[1] = *(unsigned int *)(dev + 0x41fdc);
modes[2] = *(unsigned int *)(dev + 0x41fe0);
modes[3] = *(unsigned int *)(dev + 0x41fe4);
modes[4] = *(unsigned int *)(dev + 0x41fdc);
modes[5] = *(unsigned int *)(dev + 0x11c);
snprintf(nm, sizeof(nm), "%s.smo1", base);
save(nm, s1, 0x40);
snprintf(nm, sizeof(nm), "%s.smo2", base);
save(nm, s2, 0x40);
snprintf(nm, sizeof(nm), "%s.modes", base);
save(nm, modes, 24);
printf(" modes: fd8=%u fdc=%u fe0=%u fe4=%u 11c=%u refp=%p f20p=%p\n",
modes[0], modes[1], modes[2], modes[3], modes[5],
(void*)refp, (void*)*(void **)(dev + 0x41f20));
}
/* stream parameters: transport object = channel_table[chan].slot1 */
{
unsigned char *tobj = *(unsigned char **)(0x180073750 + (size_t)chan * 0x4a8 + 8);
if (tobj) {
unsigned int v[6];
v[0] = *(unsigned int *)(tobj + 0x2d78);
v[1] = *(unsigned int *)(tobj + 0x2d88);
v[2] = *(unsigned int *)(tobj + 0x2d8c);
v[3] = *(unsigned int *)(tobj + 0x2d90);
v[4] = *(unsigned int *)(tobj + 0x2d94);
v[5] = *(unsigned int *)(tobj + 0x2d98);
snprintf(nm, sizeof(nm), "%s.stream", base);
save(nm, v, 24);
printf(" stream: 2d78=%u 2d88=%u 2d8c=%u 2d90=%u 2d94=%u 2d98=%u\n",
v[0], v[1], v[2], v[3], v[4], v[5]);
}
}
}
printf(" window hi=%u lo=%u span=%u pixels=%u comp_off=%u shift=%u gain=%u sm=%u ref=%u\n",
win_hi, win_lo, win_span, pcount, comp_off, comp_shift, gain, smooth_mode, has_ref);
printf(" p47944=%u p47950=%u p47954=%u t_base=%u t_now=%u 41848=%u 41858=%u 41340=%u 41348=%u 41fe0=%u\n",
m47944, m47950, m47954, t_base, t_now, m41848, m41858, m41340, m41348, m41fe0);
printf(" f3c=%u 41840=%u 41554=%u 41558=%u 4155c=%u 41560=%u b64_0=%u b68_0=%u dev54=%u\n",
m41f3c, m41840, m41554, m41558, m4155c, m41560, m41564_0, m41568_0, m54);
fflush(stdout);
{
char nm[128];
snprintf(nm, sizeof(nm), "%s.gray", base); save(nm, g2, (size_t)n);
snprintf(nm, sizeof(nm), "%s.rgb", base); save(nm, rgb, (size_t)n * 3);
snprintf(nm, sizeof(nm), "%s.raw", base); save(nm, raw, (size_t)nn * 2);
snprintf(nm, sizeof(nm), "%s.pal", base); save(nm, pal, 256 * 4);
snprintf(nm, sizeof(nm), "%s.cbrgb", base); save(nm, g_cb, (size_t)g_cbw * g_cbh * 3);
if (rct) { snprintf(nm, sizeof(nm), "%s.t32", base); save(nm, t32, (size_t)nn * 4); }
}
/* header block dump: 0xaf0..0xaf0+0x60 (hdr + w/h + extra) */
unsigned char hdrblk[0x60];
memcpy(hdrblk, hdr, sizeof(hdrblk));
{
char nm[128];
snprintf(nm, sizeof(nm), "%s.hdr", base);
save(nm, hdrblk, sizeof(hdrblk));
}
/* color bar data */
if (G_Bar) {
void *bdata = NULL, *binfo = NULL;
int rcb = G_Bar(chan, &bdata, &binfo);
printf(" bar rc=%d data=%p info=%p\n", rcb, bdata, binfo);
if (rcb && bdata) {
char nm[128];
snprintf(nm, sizeof(nm), "%s.bar", base);
save(nm, bdata, 4096);
snprintf(nm, sizeof(nm), "%s.barinfo", base);
save(nm, binfo ? binfo : bdata, 64);
}
}
/* temperature points */
printf("[%d] frames=%d fcnt=%u rc_rgb=%d rc_temp=%d cb=%dx%d\n",
k, g_frames, fcnt, rcr, rct, g_cbw, g_cbh);
for (int p = 0; p < np; ++p) {
unsigned char res[64] = {0};
if (T_Temp) T_Temp(pts[p][0], pts[p][1], res);
double tr = *(double *)(res + 16);
double ta = *(double *)(res + 24);
int rx = pts[p][0] / 2, ry = pts[p][1] / 2;
unsigned int rawv = raw[ry * nw + rx];
unsigned char gv = g2[pts[p][1] * w + pts[p][0]];
unsigned int tv = rct ? t32[pts[p][1] * w + pts[p][0]] : 0;
printf(" pt(%3d,%3d) raw@(%3d,%3d)=%5u gray=%3u temp32=%10u tempRaw=%.2f tempArm=%.2f\n",
pts[p][0], pts[p][1], rx, ry, rawv, gv, tv, tr, ta);
}
fflush(stdout);
Sleep(100);
}
/* summary stats of raw/gray from last frame */
{
unsigned long long sr = 0, sg = 0;
unsigned int mnr = 0xffff, mxr = 0, mng = 0xff, mxg = 0;
unsigned int hist[256] = {0};
for (int i = 0; i < nn; ++i) {
unsigned int r = raw[i];
sr += r;
if (r < mnr) mnr = r; if (r > mxr) mxr = r;
}
for (int i = 0; i < n; ++i) {
unsigned int g = g2[i];
sg += g; hist[g]++;
if (g < mng) mng = g; if (g > mxg) mxg = g;
}
printf("raw160: min=%u max=%u mean=%.1f gray320: min=%u max=%u mean=%.1f\n",
mnr, mxr, (double)sr / nn, mng, mxg, (double)sg / n);
printf("gray histogram (nonzero bins):\n");
for (int i = 0; i < 256; ++i)
if (hist[i]) printf(" %3d:%6u\n", i, hist[i]);
fflush(stdout);
}
/* scan device struct for interesting constant values */
{
printf("struct scan:\n");
/* 1. look for window values as u32/u16 in wide range */
int tw32[] = {22500, 23700, 25167, 31707, 5797, 1200};
for (int off = 0x100; off < 0x10000; off += 4) {
int v = *(int *)(dev + off);
for (int t = 0; t < 6; ++t)
if (v == tw32[t]) printf(" dev+0x%04x u32 = %d (t%d)\n", off, v, t);
}
for (int off = 0x100; off < 0x10000; off += 2) {
short v = *(short *)(dev + off);
for (int t = 0; t < 6; ++t)
if (v == tw32[t]) printf(" dev+0x%04x u16 = %d (t%d)\n", off, v, t);
}
/* 2. look for 1024-byte monotone 0..255 table (LUT) */
for (int off = 0x100; off < 0x20000 - 1024; off += 16) {
const unsigned char *p = dev + off;
int ok = 1, last = -1;
for (int i = 0; i < 1024; i += 8) {
int v = p[i];
if (v < last || v > 255) { ok = 0; break; }
if (p[i] != p[i+1] && p[i+1] != p[i]) { /* non-strict ok */ }
last = v;
}
if (!ok) continue;
/* require at least 32 distinct values and spans > 100 */
int mn = 255, mx = 0;
for (int i = 0; i < 1024; ++i) { if (p[i] < mn) mn = p[i]; if (p[i] > mx) mx = p[i]; }
if (mx - mn > 100) {
printf(" LUT candidate dev+0x%04x: min=%d max=%d first=%d last=%d\n",
off, mn, mx, p[0], p[1023]);
}
}
/* 3. look for window as double */
for (int off = 0x100; off < 0x10000; off += 8) {
double v = *(double *)(dev + off);
if (v > 20000.0 && v < 40000.0 && fabs(v - 25167.0) < 10.0)
printf(" dev+0x%04x double ~= %.1f\n", off, v);
}
fflush(stdout);
}
T_Stop();
printf("done\n");
return 0;
}
+68
View File
@@ -0,0 +1,68 @@
/* Read the LIVE official palette: after ThermalSDK Start(), the channel
* device object holds the 256-entry palette at dev+0xb00 (4 bytes each,
* BGR order). We find the device pointer via MAG_GetOutputBMPdata which
* returns the grayscale buffer at dev+0xaf0 - the palette is 0x10 before it.
*/
#define _WIN32_WINNT 0x0601
#include <windows.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
typedef void (CALLBACK *INewIRFrame)(const unsigned char *, int, int, int);
typedef bool (CALLBACK *fn_Start_t)(void);
typedef void (CALLBACK *fn_Stop_t)(void);
typedef void (CALLBACK *fn_SetNewIRFrameDelegate_t)(INewIRFrame);
typedef int (CALLBACK *MAG_GetOutputBMPdata_t)(int, int *, unsigned char **);
typedef int (CALLBACK *MAG_GetOutputBMPdataRGB24_t)(int, unsigned char *, int, int);
typedef int (CALLBACK *MAG_GetOutputColorBardata_t)(int, unsigned char *, int);
static volatile int g_frames;
static void CALLBACK onIR(const unsigned char *buf, int w, int h, int ch) {
(void)buf; (void)w; (void)h; (void)ch;
g_frames++;
}
int main(void) {
SetDllDirectoryA("C:\\Project\\MAG160C\\IR_Camera_SDK-1.0.1\\windows\\windows\\app");
HMODULE t = LoadLibraryA("ThermalSDK.dll");
HMODULE c = LoadLibraryA("CoreSDKLib.dll");
if (!t || !c) return 1;
fn_Start_t T_Start = (fn_Start_t)GetProcAddress(t, "Start");
fn_Stop_t T_Stop = (fn_Stop_t)GetProcAddress(t, "Stop");
fn_SetNewIRFrameDelegate_t T_Set = (fn_SetNewIRFrameDelegate_t)GetProcAddress(t, "SetNewIRFrameDelegate");
MAG_GetOutputBMPdata_t G_Gray = (MAG_GetOutputBMPdata_t)GetProcAddress(c, "MAG_GetOutputBMPdata");
MAG_GetOutputBMPdataRGB24_t G_RGB = (MAG_GetOutputBMPdataRGB24_t)GetProcAddress(c, "MAG_GetOutputBMPdataRGB24");
MAG_GetOutputColorBardata_t G_Bar = (MAG_GetOutputColorBardata_t)GetProcAddress(c, "MAG_GetOutputColorBardata");
T_Set(onIR);
BOOL ok = T_Start();
printf("Start=%d\n", ok); fflush(stdout);
Sleep(3000);
for (int chan = 0; chan < 5; ++chan) {
int w = 0;
unsigned char *graybuf = NULL;
int rc = G_Gray(chan, &w, &graybuf);
printf("chan %d gray rc=%d buf=%p\n", chan, rc, (void*)graybuf);
if (rc && graybuf) {
/* palette at graybuf - 0x10 (dev+0xaf0 - 0x10 = dev+0xae0?)
* Actually palette is dev+0xb00 = graybuf + 0x10 */
unsigned char *pal = graybuf + 0x10;
FILE *f = fopen("official_palette_live.bin", "wb");
if (f) { fwrite(pal, 1, 256 * 4, f); fclose(f); }
printf("saved official_palette_live.bin (first entries):\n");
for (int i = 0; i < 12; ++i) {
printf(" %2d: %3d %3d %3d %3d\n", i, pal[i*4], pal[i*4+1], pal[i*4+2], pal[i*4+3]);
}
/* also dump a few RGB24 render pixels to correlate */
unsigned char rgb[160*120*3];
int rc2 = G_RGB(chan, rgb, 160*120*3, 1);
printf("RGB24 rc=%d\n", rc2);
break;
}
}
T_Stop();
printf("done\n");
return 0;
}