android: 统一APP工程骨架 + 官方渲染管线Kotlin移植(与C参考逐字节一致)

This commit is contained in:
ZXCLI
2026-09-06 18:44:02 +08:00
parent dd52469377
commit 5b41a6aa9b
27 changed files with 2184 additions and 0 deletions
+115
View File
@@ -0,0 +1,115 @@
#!/usr/bin/env python3
"""Generate Kotlin tables from csdk C headers (palette256, t2e)."""
import re, sys, io
CSDK = r"C:\Project\MAG160C\csdk\src"
OUT = r"C:\Project\MAG160C\android\app\src\main\kotlin\com\mag160c\thermal\core\OfficialTables.kt"
def read(fn):
with open(fn, encoding="utf-8") as f:
return f.read()
def parse_matrix(text, rows=256):
vals = []
for m in re.finditer(r"\{\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\}", text):
vals.append((int(m.group(3)), int(m.group(2)), int(m.group(1)))) # R,G,B from (B,G,R,0)
if len(vals) == rows:
break
return vals
def parse_int32(text):
body = text[text.index("{"):]
return [int(x) for x in re.findall(r"-?\d+", body)]
pal_txt = read(rf"{CSDK}\mag160c_official_palette256.h")
t2e_txt = read(rf"{CSDK}\mag160c_official_t2e.h")
tables_txt = read(rf"{CSDK}\mag160c_tables.h")
pal = parse_matrix(pal_txt)
assert len(pal) == 256, f"palette entries: {len(pal)}"
t2e = parse_int32(t2e_txt)
assert len(t2e) == 646, f"t2e entries: {len(t2e)}"
# mag160c_t2e (274 entries, hex) + mag160c_e2t_acc_q10 (274 entries)
def parse_hex_array(text, name):
m = re.search(rf"static const uint32_t {name}\[MAG160C_TEMP_CURVE_ENTRIES\] = \{{(.*?)\}};", text, re.S)
assert m, name
return [int(x, 16) for x in re.findall(r"0x[0-9a-fA-F]+", m.group(1))]
t2e274 = parse_hex_array(tables_txt, "mag160c_t2e")
assert len(t2e274) == 274, f"t2e274: {len(t2e274)}"
e2t = parse_hex_array(tables_txt, "mag160c_e2t_acc_q10")
assert len(e2t) == 274, f"e2t: {len(e2t)}"
# palette ARGB int per gray index
lines = []
lines.append("package com.mag160c.thermal.core")
lines.append("")
lines.append("/** Generated from csdk C headers by analysis/gen_kotlin_tables.py. DO NOT EDIT. */")
lines.append("object OfficialTables {")
lines.append(" /** Official default palette (256 gray levels -> ARGB int), source: CoreSDKLib dev+0xb18. */")
lines.append(" val PALETTE256_ARGB = intArrayBuilder {")
for i, (r, g, b) in enumerate(pal):
argb = (0xFF << 24) | (r << 16) | (g << 8) | b
lines.append(f" add({argb}) // {i}")
lines.append(" }")
lines.append("")
lines.append(" /** Official T2E table (646 int32), temp = slope*diff>>12 + (i<<12) - 0x249f0. */")
lines.append(" val T2E = intArrayBuilder {")
for i in range(0, len(t2e), 8):
chunk = t2e[i:i+8]
lines.append(" add(" + ", ".join(str(v) for v in chunk) + (")" if i + 8 >= len(t2e) else ""))
lines.append(" }")
lines.append("}")
kt = "\n".join(lines) + "\n"
# intArrayBuilder trick is overkill; use direct arrays with chunked lines instead.
argb_lines = []
def signed(v):
return v if v < 2**31 else v - 2**32
for i in range(0, len(pal), 6):
row = ", ".join(str(signed((0xFF << 24) | (r << 16) | (g << 8) | b)) for (r, g, b) in pal[i:i+6])
argb_lines.append(" " + row + ",")
t2e_lines = []
for i in range(0, len(t2e), 8):
row = ", ".join(str(v) for v in t2e[i:i+8])
t2e_lines.append(" " + row + ("," if i + 8 < len(t2e) else ""))
t2e274_lines = []
for i in range(0, len(t2e274), 8):
row = ", ".join(f"0x{v:08x}" for v in t2e274[i:i+8])
t2e274_lines.append(" " + row + ("," if i + 8 < len(t2e274) else ""))
e2t_lines = []
for i in range(0, len(e2t), 8):
row = ", ".join(f"0x{v:08x}" for v in e2t[i:i+8])
e2t_lines.append(" " + row + ("," if i + 8 < len(e2t) else ""))
kt = f"""package com.mag160c.thermal.core
/** Generated from csdk C headers by analysis/gen_kotlin_tables.py. DO NOT EDIT. */
object OfficialTables {{
/** Official default palette: 256 gray levels -> ARGB int (from CoreSDKLib dev+0xb18, B,G,R order swapped). */
val PALETTE256_ARGB = intArrayOf(
{chr(10).join(argb_lines)}
)
/** Official T2E table (646 int32): temp = slope*diff>>12 + (i<<12) - 0x249f0. */
val T2E = intArrayOf(
{chr(10).join(t2e_lines)}
)
/** Vendor T2E curve from libcoresdk.so ARM64 @0x402010 (274 uint32 entries). */
val T2E274 = intArrayOf(
{chr(10).join(t2e274_lines)}
)
/** Vendor E2TAccQ10 curve from libcoresdk.so ARM64 @0x40245c (274 uint32 entries). */
val E2T_ACC_Q10 = intArrayOf(
{chr(10).join(e2t_lines)}
)
}}
"""
with open(OUT, "w", encoding="utf-8", newline="\n") as f:
f.write(kt)
print(f"written {OUT}: palette {len(pal)} entries, t2e {len(t2e)} entries")
+69
View File
@@ -0,0 +1,69 @@
/* render_offline.c - PC reference runner for the official mag160c pipeline.
* Usage: render_offline.exe <ddt> <frames.bin> <out.rgb> <stats.txt>
* frames.bin = sequence of complete USB frames (0x38 + 38400 bytes each).
* Writes per-frame 320x240x3 RGB24 + one stats line per frame.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "mag160c/mag160c_render.h"
static unsigned rd32(const unsigned char *p) {
return p[0] | (p[1] << 8) | (p[2] << 16) | ((unsigned)p[3] << 24);
}
static void ffc_cb(void *user, int param) { (void)user; (void)param; }
int main(int argc, char **argv) {
if (argc < 5) { fprintf(stderr, "usage: %s ddt frames out.rgb stats\n", argv[0]); return 1; }
FILE *ff = fopen(argv[2], "rb");
if (!ff) { fprintf(stderr, "open frames fail\n"); return 1; }
fseek(ff, 0, SEEK_END);
long fsz = ftell(ff);
fseek(ff, 0, SEEK_SET);
unsigned char *buf = malloc(fsz);
if (fread(buf, 1, fsz, ff) != (size_t)fsz) { fclose(ff); return 1; }
fclose(ff);
const unsigned frame_len = 38400;
const unsigned total = 0x38 + frame_len;
if (fsz < (long)total) { fprintf(stderr, "frames too small\n"); return 1; }
FILE *out = fopen(argv[3], "wb");
FILE *st = fopen(argv[4], "w");
if (!out || !st) { fprintf(stderr, "open out fail\n"); return 1; }
mag160c_render_t *r = NULL;
mag160c_render_cfg_t cfg;
memset(&cfg, 0, sizeof(cfg));
cfg.width = 160; cfg.height = 120;
cfg.ddt_path = argv[1];
cfg.ffc_cb = ffc_cb;
cfg.startup_force_ffc = 1;
cfg.cdf_pivot_75 = 1;
if (mag160c_render_init(&r, &cfg) != MAG160C_OK) {
fprintf(stderr, "render init fail\n");
return 1;
}
unsigned char *rgb = malloc(320 * 240 * 3);
int nframes = fsz / total;
for (int f = 0; f < nframes; ++f) {
unsigned char *fr = buf + (long)f * total;
/* sanity: marker + len */
if (rd32(fr) != 0x1bb1b11b) { fprintf(stderr, "bad marker at frame %d\n", f); return 1; }
if (rd32(fr + 8) != frame_len) { fprintf(stderr, "bad len at frame %d\n", f); return 1; }
if (rd32(fr + 0x1c + frame_len) != 0x1bb1b11c) { fprintf(stderr, "bad trailer at frame %d\n", f); return 1; }
mag160c_error_t rc = mag160c_render_frame(r, fr, 1, rgb);
if (rc == MAG160C_OK) {
fwrite(rgb, 1, 320 * 240 * 3, out);
int lo, hi, mn, mx, mean, sd;
mag160c_render_get_stats(r, &lo, &hi, &mn, &mx, &mean, &sd);
fprintf(st, "%d %d %d %d %d %d %d\n", f, lo, hi, mn, mx, mean, sd);
} else {
fprintf(st, "%d HIDDEN\n", f);
}
}
fclose(out); fclose(st);
return 0;
}
+60
View File
@@ -0,0 +1,60 @@
0 HIDDEN
1 HIDDEN
2 HIDDEN
3 HIDDEN
4 HIDDEN
5 HIDDEN
6 HIDDEN
7 HIDDEN
8 HIDDEN
9 HIDDEN
10 HIDDEN
11 HIDDEN
12 HIDDEN
13 HIDDEN
14 HIDDEN
15 HIDDEN
16 HIDDEN
17 HIDDEN
18 HIDDEN
19 HIDDEN
20 HIDDEN
21 HIDDEN
22 HIDDEN
23 HIDDEN
24 HIDDEN
25 HIDDEN
26 HIDDEN
27 HIDDEN
28 HIDDEN
29 HIDDEN
30 HIDDEN
31 HIDDEN
32 6961 7961 7118 7668 7461 103
33 6962 7962 7098 7663 7462 103
34 6962 7962 7116 7675 7462 103
35 6961 7961 7106 7672 7461 103
36 6961 7961 7106 7659 7461 103
37 6961 7961 7116 7664 7461 103
38 6961 7961 7121 7671 7461 103
39 6962 7962 7117 7673 7462 103
40 6962 7962 7132 7667 7462 103
41 6962 7962 7105 7664 7462 103
42 6961 7961 7118 7671 7461 103
43 6961 7961 7130 7671 7461 103
44 6962 7962 7122 7661 7462 103
45 6961 7961 7096 7669 7461 103
46 6961 7961 7096 7671 7461 103
47 6962 7962 7122 7675 7462 103
48 6961 7961 7094 7667 7461 103
49 6962 7962 7099 7664 7462 103
50 6962 7962 7134 7669 7462 103
51 6962 7962 7133 7662 7462 103
52 6961 7961 7129 7671 7461 103
53 6962 7962 7126 7671 7462 103
54 6961 7961 7119 7669 7461 103
55 6961 7961 7096 7673 7461 103
56 6961 7961 7135 7661 7461 103
57 6962 7962 7099 7662 7462 103
58 6961 7961 7122 7659 7461 103
59 6962 7962 7116 7669 7462 103