Files
MAG160C/analysis/history/progress_20260810.md
T
ZXCLI 0bfb926892 完成官方管线全量逆向与 demo3 v5 复刻,清理仓库
- 逆向:Ghidra/IDA 全量反编译 CoreSDKLib.dll/ThermalSDK.dll/libthermalSDK.so/
  libcoresdk.so(ARM64)/libmagcore.so,导出 analysis/ida/export/
- 解码官方渲染管线:DDT 校准表加载->快门端点选择->Q12 插值->ref(4x type1 帧
  均值)->NUC 查表->盲元补偿->窗口->LUT1024 重建->2x 升采样->调色板
- 逐像素验证:NUC+盲元 0/19200、插值 0 误差、2x 0/76800、窗口一致
- demo3 v5:完整复刻官方管线(含 DDT 解析、FFC 状态机、快门温度驱动),
  修复 load_ddt 表错位导致的零像素问题
- 鬼影根因分析写入 analysis/reverse_20260813_full.md
- 心跳/恢复机制:analysis/session_state.md + tools/resume_rev.ps1
- 新增 tsdk_pair3 增强采集工具;历史工具归档 csdk/tools/legacy/;
  根目录抓帧残留删除,历史文档归档 analysis/history/
- csdk/README.md 完整使用文档;.gitignore/.gitattributes 补 LFS 规则
2026-08-13 23:16:12 +08:00

303 lines
23 KiB
Markdown

# MAG160C Reverse Engineering Progress
## 2026-07-12
- Started task.
- Read relevant process skills: using-superpowers, brainstorming, planning-with-files, test-driven-development, systematic-debugging, verification-before-completion.
- Confirmed current directory is not a git repository.
- Ran initial file inventory with rg --files; found Windows C# examples, native SDK headers/DLLs, Android demo/AARs, native libraries, and libuvc/libusb source.
- Created persistent planning files.
- Extracted SDK PDF text with Python/pdfplumber.
- Read Windows headers and C# wrappers.
- Read Android demo source and AAR/class signatures using javap.
- Extracted thermallib-release.aar to .reverse/thermallib_aar for analysis.
- Confirmed IR camera VID/PID 0x833C/0x0001 from Hardware Guide.
- Confirmed Android native libcoresdk.so exports MAG_* APIs and uses libusb.
- Attempted to install capstone/pyelftools into .tools/python-revlibs; failed because network/proxy is unavailable. No new reverse-engineering software installed.
- Tried PowerShell Get-PnpDevice for attached hardware; access denied in current environment.
- Tried MinGW objdump on ARM64 libcoresdk.so; unsupported architecture.
- Resumed from planning files and continued Android bytecode inspection with javap.
- Found detailed FTDI/TCM command framing in FTDICommand and confirmed Android TCM bulk communication auto-detects an interface with bulk IN + bulk OUT endpoints.
- Expanded TCM command table for motor, proximity, light, power, audio, schedule task, and firmware update commands.
- Extracted MagDevice JNI method surface and core native data structures from coresdk.jar.
- Inspected Windows ThermalSDK.dll, CameraSDK.dll, and CoreSDKLib.dll exports/imports/strings.
- Confirmed ThermalSDK.dll wraps CoreSDKLib.dll MAG_* APIs, CoreSDKLib.dll imports libusb0 bulk read/write APIs, and Windows TCM logs match the Android TCM command frame model.
- Marked reverse-engineering evidence phases 3 and 4 complete; moved into design approval phase.
- User selected Linux deliverables: C/C++ library plus CLI sample, and Python-callable tooling/bindings.
- User approved implementation approach 1: C++ core library + stable C ABI + CLI + Python ctypes/cffi-style wrapper.
- Wrote design spec at docs/superpowers/specs/2026-07-12-mag160c-linux-sdk-design.md and ran a placeholder scan/self-review.
- Did not commit the design spec because the workspace is not a git repository; progress remains tracked in markdown files.
- User approved the design/spec and requested the implementation plan.
- Wrote implementation plan at docs/superpowers/plans/2026-07-12-mag160c-linux-sdk.md.
- Ran implementation-plan self-review/placeholder scan; no red-flag placeholders were found.
- Implemented Task 1 scaffold: CMake, public C ABI lifecycle, error names, and lifecycle tests.
- Task 1 verification note: cmake is unavailable in PATH, so substituted direct MinGW g++ compile/run verification with MAG160C_STATIC and MAG160C_HAS_LIBUSB=0; test executable passed with exit code 0.
- Implemented Task 2 TCM frame codec with byte-exact encode/decode tests and C ABI utility functions.
- Task 2 verification note: cmake remains unavailable in PATH, so substituted direct MinGW g++ compile/run verification for test_c_api and test_tcm_frame; both passed with exit code 0.
- Implemented Task 3 TCM command builder, dry-run C ABI helpers, and command payload tests.
- Task 3 verification note: cmake remains unavailable in PATH, so substituted direct MinGW g++ compile/run verification for test_c_api, test_tcm_frame, and test_tcm_device; all passed with exit code 0.
- Implemented Task 4 USB device model, endpoint-pair selection, and C ABI device listing/freeing.
- Task 4 verification note: cmake remains unavailable in PATH, so substituted direct MinGW g++ compile/run verification for test_c_api and test_device_model; both passed with exit code 0.
## 2026-07-14 Handoff
- User asked to stop and then requested a compact context handoff for a new conversation.
- Updated `task_plan.md`, `findings.md`, and `progress.md` with a short resume-ready summary.
- Current implementation state to remember:
- design and plan are already approved
- Tasks 1-4 are implemented and verified in the no-libusb fallback path
- remaining work is the live Linux runtime path, CLI sample, and Python wrapper integration
- Important stop condition:
- do not continue execution unless the user explicitly asks to resume work
## 2026-07-14 Resume
- User explicitly resumed the task and added a Linux SDK under `IR_Camera_SDK-1.0.1/linux`.
- Restored context from `task_plan.md`, `findings.md`, and `progress.md`.
- Attempted planning-with-files session catchup via `.claude` skill path; it failed because `C:\Users\ZXC\.claude\skills\planning-with-files\scripts\session-catchup.py` does not exist in this Codex install.
- Verified the workspace is still not a git repository, so implementation continues in-place with markdown progress tracking.
- Began Linux SDK inventory. Found versioned Linux packages for 1.0.0, 1.0.1, 1.2.1, and 1.2.2; 1.2.2 is extracted with Qt demo sources, headers, Linux shared libraries, and firmware.
- Read Linux 1.2.2 `CHANGELOG`, `Coding Tips`, `ThermalSDK.h`, `CameraSDK.h`, `Def.h`, and Qt project file.
- Inspected Linux `libthermalSDK.so` and `libmagcore.so.2.1.1` ELF symbols/strings. Confirmed `libmagcore` exports `MAG_*` plus bundled `libusb_*`; confirmed Linux TCM implementation uses `SerialPort`/`TCMDevice::open(std::string)` classes around the same FTDI command concept.
- Extracted text from Linux `SDK Guide.pdf` and `Quick Start.pdf` after two PowerShell quoting failures; root cause was Bash-style heredoc/Python newline quoting in PowerShell, not PDF tooling.
- Read Linux Qt demo `basewidget.cpp`, `testwidget.cpp`, `expertwidget.cpp`, and `smartwidget.cpp`; recorded startup/callback/calibration usage in findings.
- Disassembled small Linux x86-64 slices of `MAG_LinkCamera`, `MAG_LinkCameraEx`, `MAG_StartProcessImage`, `MAG_TriggerFFC`, `MAG_EnumCameras`, `MAG_GetCamInfo`, `MAG_SetFFCMode`, `EloMotorDevice::rotate`, and `TCMVirtualDevice::sendData` to extract interoperability facts only.
- New low-level Linux evidence recorded: IR endpoints `0x03` OUT, `0x81/0x82/0x84` IN; raw stream markers `0x1bb1b11b/0x1bb1b11c`; `MAG_LinkCamera` default VID `0x833c`; Linux rotate clamp `[-250,250]` while Android evidence had `[-128,128]`.
- Implemented Task 5 IR API skeleton with public `mag160c_ir_info_t`, `mag160c_ir_open_first`, `mag160c_ir_close`, `mag160c_ir_get_info`, `mag160c_ir_trigger_ffc`, and `mag160c_ir_read_raw_once`.
- Task 5 RED verification: direct MinGW compile of `tests/cpp/test_ir_skeleton.cpp` failed as expected because `mag160c_ir_*` and `mag160c_ir_info_t` were not declared.
- Task 5 GREEN verification: direct MinGW compile and run of `build_task5_ir_skeleton_test.exe` passed with exit code 0.
- CMake is still unavailable in PATH (`cmake --version` failed), so full Task 5 verification used direct MinGW no-libusb builds.
- Full no-libusb direct verification after Task 5 passed: `test_c_api`, `test_tcm_frame`, `test_tcm_device`, `test_device_model`, and `test_ir_skeleton` all compiled and ran; output ended with `ALL_DIRECT_TESTS_PASSED`.
- Implemented Task 6 `mag160c-cli` with `probe`, `tcm-rotate --dry-run`, `tcm-light --dry-run`, and `ir-info`.
- Task 6 RED verification: direct MinGW compile of `tools/mag160c_cli.cpp` failed as expected because the CLI source file did not exist.
- Task 6 CLI verification: `build_task6_mag160c_cli.exe` no-args printed usage and exited 1; `tcm-rotate --dry-run 5` printed `7e 00 07 85 02 77 00 01 00 05 7f`; `tcm-light --dry-run green blink` printed `7e 00 09 87 02 32 00 01 01 00 ff 00 35`; `ir-info` printed MAG160C skeleton info and known Linux endpoint evidence.
- Full no-libusb direct verification after Task 6 passed again for `test_c_api`, `test_tcm_frame`, `test_tcm_device`, `test_device_model`, and `test_ir_skeleton`; output ended with `ALL_DIRECT_TESTS_PASSED`.
## 2026-08-10 Deep Disassembly: Full Protocol Recovery
- Network became available; pip-installed capstone 5.0.7 + pyelftools 0.33 into `.tools/python-revlibs` (verified: r2pipe/angr/keystone/unicorn also installed). Built reusable `analysis/revtools.py` (ELF loader, ARM64 PLT/GOT resolver, lite C++ demangler, xref scan) and used MinGW objdump/readelf on the x86-64 Linux `libmagcore.so.2.1.1` and Windows `CoreSDKLib.dll`.
- Recovered the complete vendor USB protocol from libmagcore (was the #1 open question):
- Link: VID 0x833C, set_configuration(2), claim_interface(0), auto-detach on, optional pid/bus/addr filters.
- Command channel: EP OUT 0x03 + EP IN 0x82 (0x1000, 2000ms); 8-byte packets `{u32 magic, u32 param}`; 0x3c-byte packets for DDT.
- Command magic table: 0x6bb6b66b/66c prepare, 66d/66e DDT read/write (0x38-byte payload), 66f camera info, 670 version, 671 (Windows-only), 672 FFC, 673 start (after 50ms + thread setup), 674 stop, 676/677 set params (Linux-only).
- Response magic table: 0x5bb5b55b/55c/55d/55f = 0x38-byte info blocks (55b: pid@0, width@0x10, height@0x14), 55e = 16-byte qword pair, 57b = saved-file header.
- Frame stream EP 0x81: marker 0x1bb1b11b@0, frame counter@4, data length@8, type@0xc (0/1), shutter@0x10, pixel data@0x1c (uint16 LE), trailing 0x1bb1b11c@0x1c+len, total 0x38+len, width 160 or 384. Reader thread 0x170a4 + dispatcher 0x16fb5.
- Windows CoreSDKLib.dll cross-validates: same magic codes (66b/66c/66f/670/671/672/673/674), same frame markers with 0x1c offset, same 50ms-then-start flow, same 0xe4ae error domain, same response dispatch shape (0x5bb5b55b..0x5bb5b560).
- Recovered temperature conversion math from ARM64 libcoresdk.so exported CFunctions:
- Calibration (0x6a41c): per-pixel PWL - diff=(raw-baseline)>>1, band search on per-pixel threshold table (stride bands-1), v=off[i][p]+(diff*coeff[i][p]>>12), clamp [0,0xffff].
- ConvertResponse2Temperature (0x73124): base=(50000-gain)>>shift + diff*coeff, auto-gain decrement when >0x101 pixels saturate.
- CAccumulator::PushFrame (0x62548): baseline copy/avg accumulation with restart state machine.
- GetTemperature (0x7252c): window average (radius 1..3) -> ReviseTemperature (0x6cdb8, T2E PWL 0x112 entries + emissivity float mixing + inverse-map binary search) -> CorrectTemperature (0x6d6b0, offset segmentation k=(v-15000)/100 clamped 0..6, float gains +0xc4/+0xcc/+0xd0).
- T2E table symbol exported (`T2E` @ 0x402010), monotonic 1000..737212, dumped to analysis/t2e_table.json; float constants 1000.0/0.01/0.99; units consistent with millidegree-C scale.
- DDT saved file: 0x5bb5b57b header + aligned payload + 0x5bb5b55b/55c/55d/55e/55f typed blocks.
- Wrote complete spec to `analysis/protocol_spec.md`; updated `findings.md` with the 2026-08-10 session summary.
- Implementation updates (all passing direct MinGW builds, same method as before):
- include/mag160c/mag160c.h: frame header struct, all magic constants, temp-conversion constants.
- src/core/ir_frame.{hpp,cpp}: recovered frame parser (parse_ir_frame) + Calibration piecewise-linear converter (calibrate_frame).
- tests/cpp/test_ir_frame.cpp: 7 tests (frame parse valid/bad marker/bad type/bad trailer/truncated, calibration band select/interpolation+clamp/negative diff).
- ir_device.cpp + CLI ir-info now report the recovered protocol; skeleton errors changed from PROTOCOL_UNKNOWN to UNSUPPORTED; CMakeLists updated.
- Full suite green: test_c_api, test_tcm_frame, test_tcm_device, test_device_model, test_ir_skeleton, test_ir_frame all EXIT=0; mag160c-cli ir-info shows the recovered protocol line.
## 2026-08-10 Windows Test Prep + USB Capture Tooling
- Organized workspace: disassembly outputs moved to `analysis/disasm/`, legacy
C++ exe artifacts moved to `analysis/legacy-cpp/build/`, temp files removed.
- Hardware check: no VID 0x833C device currently attached (present USB
devices: FFFF/FFFF, 0CF2/7750, 258A/010C, Logitech receiver). User will plug
the camera in for Windows testing.
- Installed USB capture tooling via winget:
- USBPcap 1.5.4.0 (`desowin.USBPcap`) - driver + USBPcapCMD.exe
- Wireshark 4.6.7 (`WiresharkFoundation.Wireshark`)
- Reboot needed for the USBPcap driver to load.
- Windows libusb-1.0 build support:
- Found a 64-bit `libusb-1.0.dll` in TI CCS
(`C:\ti\ccs2050\ccs\ccs_base\common\bin\`); openocd's copy is 32-bit
(pei-i386), which crashed with 0xC000007B on a 64-bit exe.
- Generated MinGW import library with `gendef` + `dlltool -k` (kill-at so
symbols match the undecorated x64 calls) into
`csdk/third_party/libusb/win64/` (libusb.h + libusb-1.0.dll +
libusb-1.0.x64.a + original .def).
- CMakeLists now falls back to this bundled libusb on Windows when
pkg-config finds nothing, and copies the DLL next to the CLI.
- Verified full libusb+threads build on Windows:
- `csdk_cli_libusb.exe` runs; tcm/frame/temp tests pass; `ir-info` reports
"no device with VID 0x833c" (libusb enumeration works); `ffc` returns
NOT_OPEN cleanly.
- Wrote `csdk/WINDOWS_TESTING.md`: build commands, hardware test flow, USBPcap
capture procedure, and the pcap verification checklist against the recovered
protocol (magic bytes per command/response/frame).
## 2026-08-10 Pure-C SDK Rewrite (csdk/)
- User requested a C SDK faithful to the reverse-engineered protocol; the
previous C++ implementation (fake `ir_open`, dynamic endpoint discovery,
missing temperature math) was replaced. Old tree archived to
`analysis/legacy-cpp/`.
- New pure-C tree in `csdk/`:
- `include/mag160c/mag160c.h` - C ABI: vendor error domain, all magic
command/response codes, frame format constants, temperature pipeline API,
TCM framing API.
- `src/mag160c_error.c` - TLS last-error.
- `src/mag160c_frame.c` - 0x1bb1b11b frame parse + streaming assembler
(marker align, length check, trailing marker, 0x38+len consumption).
- `src/mag160c_temp.c` - Calibration (per-pixel PWL w/ baseline),
ConvertResponse2Temperature (auto-gain + saturation counting),
T2E Q13 interpolation using the dumped tables.
- `src/mag160c_tcm.c` - FTDICommand 0x7e framing; NOTE: checksum is
additive mod-256 (0x7e+0x07=0x85), byte-exact vs. the old C++ tests.
- `src/mag160c_ir.c` - libusb session: VID 0x833C, config 2, iface 0,
EP 0x03/0x82 command channel, EP 0x81 frame reader thread, 50 ms then
START, STOP, FFC; compiles in libusb/threads/no-libusb modes.
- `src/mag160c_tables.h` - T2E + E2TAccQ10 tables generated from the
ARM64 binary dump.
- `tools/mag160c_cli.c` - ir-info / ffc / start / stop / tcm-rotate /
tcm-light / frame-test / temp-test.
- `tests/` - test_frame, test_temp, test_tcm, test_api.
- Verification (mingw gcc, no-libusb mode): all 4 tests pass
(ALL_CSDK_TESTS_PASSED); threads mode passes; libusb+threads mode compiles
clean (no libusb .lib on this machine to link).
- CLI output verified: `tcm-rotate 5` ->
`7e 00 07 85 02 77 00 01 00 05 7f` (byte-exact), `tcm-light green blink` ->
`7e 00 09 87 02 32 00 01 01 00 ff 00 35`, frame-test/temp-test pass,
ir-info prints the recovered protocol.
## 2026-07-14 Workspace Organization
- Organized generated and analysis files without deleting source or vendor SDK material.
- Moved root `build_*.exe` binaries into `build-artifacts/`.
- Moved `.reverse/` to `analysis/reverse-cache/`.
- Moved `reverse_tools/` to `analysis/reverse_tools/`.
- Moved root `【模块文档】MAG160CORE.pdf` to `vendor-docs/【模块文档】MAG160CORE.pdf`.
- Left vendor SDK/app directories and active source directories in place: `IR_Camera_SDK-1.0.1/`, `app/`, `src/`, `include/`, `tests/`, `tools/`, and `docs/`.
## 2026-08-10 Live Hardware Protocol Verification (WinUSB/libusb-win32)
User connected the actual device (Magnity Thermal Camera, VID 0x833C PID 0x0001,
serial 160043865). Full protocol verified live against the official Windows demo
(EloThermal.exe, which shows a real thermal image).
### Driver / libusb matrix (important)
- Device ships without a driver; SDK's ThermoCube_stream.inf fails signature
validation on Win10/11 (2012 cat). Zadig installed WinUSB, then
libusb-win32 for the official demo (demo links libusb0.dll directly).
- Working combo: **TI libusb-1.0.dll + libusb-win32 driver** (all commands/stream
work; harmless "monotonic clock" warnings). Official libusb-1.0.27 MinGW build
crashes on bulk transfer with the libusb-win32 driver; official DLL + WinUSB
crashes in libusb_control_transfer; TI DLL + WinUSB fails FFC writes.
### Protocol verified on hardware (differs from Linux-SDK assumptions)
- Commands are 4-byte packets (magic only); only FFC (0x6bb6b672) uses the
8-byte {magic, param} form.
- Init: 66b(4B) -> 66c(4B) -> 66f(4B). Response 0x5bb5b55b from 66b carries the
camera info (width@+0x10=160, height@+0x14=120, fps@+0x18=15). The 0x5bb5b55c
block (from 66c) is a DIFFERENT layout and must not be parsed as info.
- START: FFC(0) x2, sleep 300ms, then 73(4B). This switches the unit into live
imaging mode (frames with type field 0/1). FFC(param=1) toggles type 1->0,
FFC(param=0) toggles 0->1; the official demo alternates FFC every ~10 frames.
- STOP: clear_halt(0x03/0x82) then 74(4B) works.
- Frame stream: 28-byte header (marker 0x1bb1b11b, counter, len=0x9600,
type, shutter) + 38400-byte payload + 28-byte tail (0x1bb1b11c trailer), at
15 fps. Verified: 4 s capture = 60 frames.
- Thermal response: hand vs background delta ~1322 counts (whole-FOV hand)
=> roughly 147 counts/C with a plausible 34C vs 25C assumption.
### csdk fixes made from live testing
- send_cmd4 for 66b/66c/66f/73/74; FFC stays 8-byte with param.
- ir_open sequence 66b,66c,66f (was sending 670 in third slot).
- FFC(0) x2 before START in ir_start.
- Keep libusb_context alive for handle lifetime (was libusb_exit'ing it).
- Only 0x5bb5b55b parsed as info (0x5bb5b55c corrupted width/height).
- GUI live thermal viewer (thermal_viewer.c) shows real-time pseudo-color
imagery; single-threaded read/render loop required (TI libusb DLL is
unreliable with a reader thread + GUI thread).
### Artifacts
- official demo traffic capture: analysis/captures/libusb0_trace.txt
(26k lines: init, periodic FFC, 10k+ frames) via a libusb0.dll API shim.
- ETW capture experiments (USB-UCX has no payload data; etl2pcapng is
network-only) - kept as notes in csdk/WINDOWS_TESTING.md.
## 2026-08-10 Post-reboot USBPcap Verification
- System rebooted; USBPcap driver now bound to the root hubs. Device is on
\\\\.\\USBPcap4 (AMD USB 3.x hub on this machine).
- Captured the csdk's own live traffic with USBPcap (no shim needed anymore):
analysis/captures/csdk_verified.pcap.
- Sequence verified identical to the official demo: 66b -> 66c -> 66f ->
FFC(0) x2 -> START -> 61 frames @ 15 fps -> STOP.
- csdk frame callback test: 60 frames in 4 s (type=1). All tests green.
## 2026-08-10 (evening)
- Task 1: researched open-source thermal display pipelines (OpenThermal/libseek-thermal
SeekCam.cpp, MLX90640 API, FLIR Lepton AGC/LUT). Key learnings: bad pixel = histogram
peak-deviation threshold (thr = peak - (max - peak)) + temporal min/max, topological
4-neighbour fill; AGC = percentile stretch + ironbow LUT; diff AGC = adaptive mean-based span.
- Task 2: rewrote tools/mag160c_demo2.c v3: Seek-style bad pixel detection + topological
fill order applied to live frame and reference; adaptive diff AGC (span=2*mean|d|, deadband);
ironbow LUT; two-point calibration buttons (Cal Cold 0C / Cal Hot 90C / Reset).
- Task 3: decoded official FFC cadence from libusb0_trace.txt (26092 lines):
FFC(1) after ~frame 10, then FFC(0) -> 9 frames -> FFC(1) -> variable gap (34..2680).
Built mag160c_ffc_test.c with period 400 + gap 9: PASS 1400 type=0 frames / 95s /
15.1 fps / 0 stalls (twice). 44-frame period failed (fps drop + 17.7% type=1).
- Task 4: added two-point linear calibration (mag160c_temp_calibrate_linear) to csdk +
demo2 GUI buttons; physical measurement (ice water / boiling water) still TODO by user.
- Task 5: new csdk module src/mag160c_display.c + include/mag160c/mag160c_display.h
(badmap, AGC range/diff span, ironbow, FFC scheduler, linear cal); mag160c_ir.c now
supports mag160c_ir_set_ffc_scheduler() ticking in the reader thread. test_display.c
added (20/20 pass); all old tests still pass; CLI gained display-test/ffc-test/calibrate.
- Hardware validation: csdk_stream_test.exe (threaded reader + scheduler) PASS 1030
type=0 frames / 14.7 fps; csdk_cli_libusb.exe ffc-test 1000 PASS. demo2 GUI verified
rendering type=0 with bad-pixel red/blue down to ~0.02% and no saturated defects.
- Evidence: analysis/demo2_v3_verified_20260810.png (GUI screenshot).
## 2026-08-10 (late night, third fix round)
- User reported: FFC flash fixed but (1) startup noise image (red/yellow dots)
persists and overlays the normal view, (2) ghosting still present.
- Root cause: MOG permanent-freeze defect - reference collected before the
first auto-FFC cycle baked startup noise in; frozen pixels (|d|>=60) could
never heal, so noise + wrong reference persisted forever.
- Fixes in mag160c_demo2.c:
* first reference collection moved to right after startup FFC(1) settling
(nread>=40, not >=150)
* reference re-collected after EVERY FFC(1) (quiet-frame median, keeps bad
map) instead of one-shot median rebase - any stale reference self-heals
within one FFC cycle (~27s)
* MOG self-healing: isolated foreground pixels (8-neighbor fg < 2) stuck
for >=30 frames are reference errors (startup noise/bad pixel), reset to
live value; contiguous objects never healed/absorbed
- csdk: new mag160c_display_ref_track_heal() (w,h variant with fg counters);
demo2 uses it directly. test_display now 30/30.
- Verified on hardware: startup noise 0.12% -> 0.10% over 2157 frames
(5+ FFC cycles), no growth, no overlay; red content all real scene blobs;
no FFC flash (red stable, blue 0.01%); stream never stalls.
- Evidence: analysis/demo2_v4_selfheal_verified_20260810.png
## 2026-08-10 (late night, fourth fix round - root cause found)
- User reported startup noise image and ghosting STILL present after the
MOG self-heal fix. Started a data-driven investigation instead of more
reference-model tweaks.
- Captured 60 real type=0 frames (tools/mag160c_frame_dump.c) and analyzed:
* temporal noise std ~248 counts per pixel (99.9% of pixels) -> diff mode
with adaptive span ~400 amplifies this into visible red/blue speckle =
the "startup noise image"
* fixed sensor mura: row profile span 15594 (row1 constant +5000), col
span 8714, FFT shows ~10% energy on row/col axes
* mura drifts ~350 counts per session and jumps after FFC -> reference
diff shows old pattern = ghosting
* flat-field correction live-(ref-mean(ref)) reduces mura std 3557->379
- Decided to abandon the scene-diff approach (which caused both problems)
and adopt the official pipeline: raw -> NUC flat-field correction ->
absolute temperature display (median-centered window + ironbow) + 3x3
median smoothing (cuts temporal noise ~3x).
- csdk: new mag160c_display_nuc(); reference capture now rejects frames
where >5% pixels deviate from the running average (persistent objects
never enter the flat field) - the anti-ghost guarantee. diff mode kept
as optional.
- Verified on hardware: speckle residual 2.2-2.7 (smooth), pure blue 0%,
red content all real scene blobs (1-1.4%), 5+ FFC cycles with no flash
and no pattern growth, stream 2268+ frames. csdk tests 32/32.
- Evidence: analysis/demo2_v5_nuc_verified_20260810.png