/* mag160c_linux_demo.c - Linux-oriented reference app. * * Uses the platform-independent mag160c_render API. On Windows this * builds/runs too (no GDI): output is written to thermal.bmp every 30 * frames. On Linux the same code path is used; only the display * backend differs (fbdev/DRM/LVGL - see DISPLAY_BACKEND below). * * Build (Windows, same toolchain as demo3): * gcc -O2 -w -DMAG160C_STATIC -I"csdk\third_party\libusb\win64" * -I"csdk\include" -I"csdk\src" -o build-artifacts\mag160c_linux_demo.exe * csdk\tools\mag160c_linux_demo.c csdk\src\mag160c_render.c * csdk\src\mag160c_ir.c csdk\src\mag160c_frame.c csdk\src\mag160c_temp.c * csdk\src\mag160c_tcm.c csdk\src\mag160c_error.c csdk\src\mag160c_display.c * csdk\third_party\libusb\win64\libusb-1.0.x64.a * Build (Linux): * gcc -O2 -Wall csdk/tools/mag160c_linux_demo.c csdk/src/mag160c_render.c * csdk/src/mag160c_ir.c csdk/src/mag160c_frame.c csdk/src/mag160c_temp.c * csdk/src/mag160c_tcm.c csdk/src/mag160c_error.c csdk/src/mag160c_display.c * -lusb-1.0 -lpthread -lm -o mag160c_linux_demo */ #define _WIN32_WINNT 0x0601 #include #include #include #include #ifdef _WIN32 #include #define SLEEP_MS(ms) Sleep(ms) #else #include #include #define SLEEP_MS(ms) usleep((ms) * 1000) #endif #include #include "mag160c/mag160c.h" #include "mag160c/mag160c_render.h" #include "mag160c/mag160c_display.h" #define W 160 #define H 120 #define OUT_W 320 #define OUT_H 240 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 void ffc_cb(void *user, int param) { (void)user; if (g_h) sendcmd(0x6bb6b672, (unsigned)param, 8); } /* ---- display backend abstraction ---- * Windows: no-op (terminal stats). Linux: implement one of: * A. fbdev: open("/dev/fb0"), mmap, memcpy RGB (convert to RGB565) * B. DRM/KMS: drmModeSetCrtc + dumb buffer * C. LVGL: lv_img_set_src + lv_obj_invalidate (best for UI/animation) */ static void display_rgb(const uint8_t *rgb, int w, int h) { (void)rgb; (void)w; (void)h; /* Linux example (fbdev RGB565): * static int fd = -1; static unsigned char *fb; static int fbw, fbh; * ... open/mmap once, then: * for (y) for (x) { u16 v = ((rgb[y*w*3+x*3]>>3)<<11) | * ((rgb[y*w*3+x*3+1]>>2)<<5) | * (rgb[y*w*3+x*3+2]>>3); * *(u16*)(fb + y*fbw*2 + x*2) = v; } */ } static void save_bmp(const char *path, const uint8_t *rgb, int w, int h) { uint32_t sz = 54 + (uint32_t)w * h * 3; uint8_t bmp[54]; memset(bmp, 0, 54); bmp[0] = 'B'; bmp[1] = 'M'; bmp[2] = (uint8_t)sz; bmp[3] = (uint8_t)(sz >> 8); bmp[4] = (uint8_t)(sz >> 16); bmp[5] = (uint8_t)(sz >> 24); bmp[10] = 54; bmp[14] = 40; bmp[18] = (uint8_t)w; bmp[19] = (uint8_t)(w >> 8); bmp[20] = (uint8_t)(w >> 16); bmp[21] = (uint8_t)(w >> 24); bmp[22] = (uint8_t)h; bmp[23] = (uint8_t)(h >> 8); bmp[24] = (uint8_t)(h >> 16); bmp[25] = (uint8_t)(h >> 24); bmp[26] = 1; bmp[28] = 24; FILE *f = fopen(path, "wb"); if (!f) return; fwrite(bmp, 1, 54, f); for (int y = h - 1; y >= 0; --y) { const uint8_t *row = rgb + (size_t)y * w * 3; for (int x = 0; x < w; ++x) { uint8_t bgr[3] = { row[x * 3 + 2], row[x * 3 + 1], row[x * 3] }; fwrite(bgr, 1, 3, f); } } fclose(f); } int main(int argc, char **argv) { const char *ddt = argc > 1 ? argv[1] : "mag160c_official.ddt"; libusb_init(&g_ctx); mag160c_render_cfg_t cfg; memset(&cfg, 0, sizeof(cfg)); cfg.width = W; cfg.height = H; cfg.ddt_path = ddt; cfg.ffc_period = 1800; cfg.ffc_drift = 250; cfg.startup_force_ffc = 1; cfg.warm_frames = 20; cfg.cdf_pivot_75 = 1; cfg.ffc_cb = ffc_cb; mag160c_render_t *render = NULL; if (mag160c_render_init(&render, &cfg) != MAG160C_OK) { fprintf(stderr, "render init failed (need %s)\n", ddt); return 1; } int ok = 0; for (int attempt = 0; attempt < 4 && !ok; ++attempt) { if (attempt > 0) { if (g_h) libusb_reset_device(g_h); if (g_h) { libusb_close(g_h); g_h = NULL; } SLEEP_MS(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_MS(100); if (sendcmd(0x6bb6b672, 0, 8)) continue; SLEEP_MS(300); if (sendcmd(0x6bb6b673, 0, 4)) continue; SLEEP_MS(700); ok = 1; } if (!ok) { fprintf(stderr, "camera init failed\n"); mag160c_render_destroy(render); libusb_exit(g_ctx); return 1; } static uint8_t rgb[OUT_W * OUT_H * 3]; static uint8_t frame[40000]; unsigned prev = 0xffffffff; unsigned long nframe = 0; for (;;) { uint8_t hdr[64]; int xfer = 0; if (libusb_bulk_transfer(g_h, 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 == prev) continue; prev = c; if (libusb_bulk_transfer(g_h, 0x81, frame, sizeof(frame), &xfer, 500) || xfer < 38400) continue; nframe++; /* assemble the full frame (28B header + pixels + tail) for the * renderer: the second bulk read carries 38400 px + 28B tail */ static uint8_t full[28 + 40000]; memcpy(full, hdr, 28); memcpy(full + 28, frame, 38400); if (xfer > 38400) memcpy(full + 28 + 38400, frame + 38400, (size_t)(xfer - 38400)); /* feed the platform-independent pipeline */ mag160c_error_t rc = mag160c_render_frame(render, full, 1, rgb); if (rc != MAG160C_OK) continue; /* FFC window / startup: frozen */ display_rgb(rgb, OUT_W, OUT_H); if (nframe % 30 == 0) { char path[64]; snprintf(path, sizeof(path), "thermal_%05lu.bmp", nframe); save_bmp(path, rgb, OUT_W, OUT_H); int lo, hi, fmin, fmax, mean, std; mag160c_render_get_stats(render, &lo, &hi, &fmin, &fmax, &mean, &std); printf("f=%lu shutter=%d sel=%d win=[%d,%d] stats=(%d..%d m%d s%d) ref=%d\n", nframe, mag160c_render_get_shutter(render), mag160c_render_get_endpoint(render), lo, hi, fmin, fmax, mean, std, mag160c_render_has_reference(render)); fflush(stdout); } } mag160c_render_destroy(render); libusb_close(g_h); libusb_exit(g_ctx); return 0; }