Commit 21fe0c02 authored by Andrey Filippov's avatar Andrey Filippov

CLAUDE: T2 - in-loop 16-bit feeder replaces the 9.7 GiB float preload

Real per-scene file I/O inside the measured loop (the honest RT test, and the
production shape: 4 reader threads = the 4xSATA-2 device groups of 4
consecutive sensors). New CuasSceneFeeder parses the single-strip 16-bit TIFF
layout ONCE from file #1 (strip@557, 656640 B, big-endian, telemetry row 0 -
verified constant over the whole segment and bit-exact vs the production
ImagejJp4Tiff reader), then preads raw strip bytes straight into the native
pinned u16 staging (PosixIo/libc - the kernel's read IS the pinned write,
half the pinned-write bytes of the float path). The GPU converts (byte-swap)
+ applies the borrowed per-scene Row/Col (prior scales snapshotted at
creation, exact double chain) + two-map conditions in ONE kernel
(condition_lwir_u16, native 84d7ab6) - bit-identical to the preload path by
the u16_cond case, so the Done-line byte-identity gate carries over.

Cold-read honesty: posix_fadvise(DONTNEED) after every file - reruns never
replay the page cache (the production RAM-based dual-port SATA feeder always
serves NEW frames). Cold bench: 4 threads 4.4/5.4/7.3 ms/scene p50/p95/max -
hidden under the ~12 ms DP entry via the D5 overlap, which now awaits the
in-flight read (kicked one scene ahead at collect-safe points, keeping the
one-scene staging slack contract).

Arrival moderation (Andrey 07/18): in paced (pose_pace) mode the feeder is
armed with the same 60 Hz epoch grid - the DATA, not just the loop, arrives
at 60 Hz; the overlap then fires only when the next read already landed
(never parks inside the DP window), i.e. exactly when running behind.

New saved checkbox curt.pose_feed16 (default ON) selects the feeder; OFF =
the legacy float preload (A/B). CPU-oracle fallback (heap reads + the exact
preload float chain) covers cond_gpu OFF and any native failure. Stage0
42/42; mvn package PASS.
Co-authored-by: 's avatarClaude Fable 5 <claude-fable-5@anthropic.com>
parent a8ffb2f8
...@@ -578,6 +578,129 @@ public class CuasConditioning { ...@@ -578,6 +578,129 @@ public class CuasConditioning {
// 3-B rung B0: one-shot conditioning-stage split (see above). By Claude on 07/16/2026. // 3-B rung B0: one-shot conditioning-stage split (see above). By Claude on 07/16/2026.
private static boolean b0_condition_reported = false; private static boolean b0_condition_reported = false;
// ---- T2 16-bit feeder (07/18/2026): in-loop reads + GPU u16 conditioning.
// The pinned staging already holds the scene's raw strip bytes (the feeder
// pread them there); conditioning = enqueue per-sensor async H2D + the
// per-scene Row/Col block + ONE condition_lwir_u16 launch, all on the copy
// stream. Bit-identical to the preload float path by the u16_cond case.
// By Claude on 07/18/2026, from Andrey's T2 design. ----
private static boolean feeder_path_reported = false; // once-per-program "path active" note
/** Upload the resident linear calibration maps for the feeder harness (the
* preload twin). Returns false when the CPU oracle must be used. */
public static boolean prepareFeederConditioning(
final QuadCLT scene,
final CuasSceneFeeder feeder,
final Config cfg) {
if ((scene == null) || (feeder == null) || (cfg == null)) return false;
return prepareGpuConditioning(
scene.getGPUQuad(), feeder.getNumSensors(), feeder.getNumPixels(),
scene.getLwirScales(), scene.getLwirOffsets(), scene.getLwirScales2(), scene.getFPN(), cfg);
}
/**
* Serial (non-overlap) feeder conditioning of one scene into the ACTIVE image
* set: await the staged read, then u16 H2D + Row/Col + condition in one async
* pass. CPU fallback (heap mode / native failure) rebuilds the exact preload
* floats and takes the legacy float upload. False = input failed (caller coasts).
*/
public static boolean conditionFeederSceneToGpu(
final QuadCLT scene,
final CuasSceneFeeder feeder,
final int nscene,
final float [][] cpu_raw, // scratch [sensor][pixels] for the CPU fallback
final float [][] cpu_scratch, // scratch for the CPU-conditioned output
final Config cfg) {
if ((scene == null) || (feeder == null)) return false;
final Config use_cfg = (cfg != null) ? cfg : new Config();
if (use_cfg.rowcol_enable) {
throw new IllegalArgumentException("Feeder Row/Col is applied in-kernel, not by the timed Config");
}
if (use_cfg.zero_dc && Double.isNaN(use_cfg.dc_level)) {
throw new IllegalArgumentException("Feeder conditioning requires a fixed sequence DC level");
}
if (!feeder.awaitStaged(nscene)) return false;
final GpuQuad gpuQuad = scene.getGPUQuad();
if (feeder.isPinned() && prepareGpuConditioning(
gpuQuad, feeder.getNumSensors(), feeder.getNumPixels(),
scene.getLwirScales(), scene.getLwirOffsets(), scene.getLwirScales2(), scene.getFPN(), use_cfg)) {
if (enqueueFeederScene(scene, feeder, nscene, gpuQuad)) {
if (!feeder_path_reported) {
feeder_path_reported = true;
System.out.println("CuasConditioning: 16-bit feeder path active (in-loop read -> pinned u16 H2D"+
" -> one-pass GPU convert+"+(feeder.isRowColEnabled() ? "Row/Col+" : "")+"condition)");
}
return true;
}
System.out.println("CuasConditioning: u16 conditioning failed for scene "+nscene+
" - CPU fallback for this scene");
}
// CPU oracle: exact preload floats -> linear conditioning -> legacy upload
if ((cpu_raw == null) || (feeder.readSceneFloats(nscene, cpu_raw) == null)) return false;
conditionLinearToFloat(cpu_raw, cpu_scratch, scene.getLwirScales(), scene.getLwirOffsets(),
scene.getLwirScales2(), scene.getFPN(), use_cfg);
scene.saveQuadClt();
scene.setHasNewImageData(false);
gpuQuad.setBayerImages(cpu_scratch, true);
return true;
}
/**
* D5 overlap variant: condition one FEEDER scene into ring set img_set while
* the previous scene's DP chain runs. The caller has already verified the
* staged read (never parks inside the DP window). GPU-only by construction.
*/
public static boolean conditionFeederSceneToGpuOverlap(
final QuadCLT scene,
final CuasSceneFeeder feeder,
final int nscene,
final Config cfg,
final int img_set,
final int restore_set) {
if ((scene == null) || (feeder == null) || !feeder.isPinned() || !feeder.isStaged(nscene)) return false;
final Config use_cfg = (cfg != null) ? cfg : new Config();
if (use_cfg.rowcol_enable || (use_cfg.zero_dc && Double.isNaN(use_cfg.dc_level))) return false;
final GpuQuad gpuQuad = scene.getGPUQuad();
if (!prepareGpuConditioning(
gpuQuad, feeder.getNumSensors(), feeder.getNumPixels(),
scene.getLwirScales(), scene.getLwirOffsets(), scene.getLwirScales2(), scene.getFPN(), use_cfg)) {
return false;
}
if (!gpuQuad.selectImageSet(img_set)) return false;
boolean ok = false;
try {
ok = enqueueFeederScene(scene, feeder, nscene, gpuQuad);
} finally {
if (!ok) {
gpuQuad.selectImageSet(restore_set); // never leave a half-filled set active
}
}
return ok;
}
/** Common enqueue: per-sensor u16 H2D + per-scene Row/Col block + the one-pass
* convert/condition kernel, all async on the copy stream (records the active
* set's ready event - the D5a ONE-event contract). */
private static boolean enqueueFeederScene(
final QuadCLT scene,
final CuasSceneFeeder feeder,
final int nscene,
final GpuQuad gpuQuad) {
final int slot = feeder.getSlot(nscene);
if (slot < 0) return false;
scene.saveQuadClt();
scene.setHasNewImageData(false);
for (int cam = 0; cam < feeder.getNumSensors(); cam++) {
if (!gpuQuad.setImageStagedU16(cam, slot)) return false;
}
if (feeder.isRowColEnabled()) {
final com.sun.jna.Pointer rc = gpuQuad.rowcolStaging(slot);
if ((rc == null) || !feeder.writeRowcolStaging(nscene, rc)) return false;
if (!gpuQuad.setRowcolStaged(slot)) return false;
}
return gpuQuad.execConditioningU16(feeder.isBigEndian(), feeder.isRowColEnabled());
}
/** /**
* D5 overlap variant of the preloaded upload (design ratified 07/17/2026): * D5 overlap variant of the preloaded upload (design ratified 07/17/2026):
* condition ONE scene into device image-set ring slot img_set while the * condition ONE scene into device image-set ring slot img_set while the
......
...@@ -2582,9 +2582,20 @@ public class CuasPoseRT { ...@@ -2582,9 +2582,20 @@ public class CuasPoseRT {
final QuadCLT [] quadCLTs, final QuadCLT [] quadCLTs,
final CuasScenePreload scenePreload, final CuasScenePreload scenePreload,
final int debugLevel) { final int debugLevel) {
return testPoseSequence(clt_parameters, center_CLT, quadCLTs, scenePreload, null, debugLevel);
}
/** Pose sequence using either the float preload or the in-loop 16-bit feeder (T2). */
public static double [][][] testPoseSequence(
final CLTParameters clt_parameters,
final QuadCLT center_CLT,
final QuadCLT [] quadCLTs,
final CuasScenePreload scenePreload,
final CuasSceneFeeder sceneFeeder, // By Claude on 07/18/2026 (T2)
final int debugLevel) {
final boolean [] saved_calib = new boolean [1]; final boolean [] saved_calib = new boolean [1];
double [][][] fitted = poseSequencePass(clt_parameters, center_CLT, quadCLTs, double [][][] fitted = poseSequencePass(clt_parameters, center_CLT, quadCLTs,
scenePreload, scenePreload, sceneFeeder,
saved_calib, // out: set when this pass generated+saved the tile calibration saved_calib, // out: set when this pass generated+saved the tile calibration
debugLevel); debugLevel);
if (saved_calib[0]) { // bootstrap: calibration was just generated - re-run filtered if (saved_calib[0]) { // bootstrap: calibration was just generated - re-run filtered
...@@ -2592,7 +2603,7 @@ public class CuasPoseRT { ...@@ -2592,7 +2603,7 @@ public class CuasPoseRT {
" was just generated; re-running the sequence with the filtered selection"+ " was just generated; re-running the sequence with the filtered selection"+
" (all outputs are overwritten by the filtered run)"); " (all outputs are overwritten by the filtered run)");
fitted = poseSequencePass(clt_parameters, center_CLT, quadCLTs, fitted = poseSequencePass(clt_parameters, center_CLT, quadCLTs,
scenePreload, scenePreload, sceneFeeder,
null, // saved_calib: a filtered pass never writes the calibration null, // saved_calib: a filtered pass never writes the calibration
debugLevel); debugLevel);
} }
...@@ -2606,6 +2617,7 @@ public class CuasPoseRT { ...@@ -2606,6 +2617,7 @@ public class CuasPoseRT {
final QuadCLT center_CLT, final QuadCLT center_CLT,
final QuadCLT [] quadCLTs, final QuadCLT [] quadCLTs,
final CuasScenePreload scenePreload, final CuasScenePreload scenePreload,
final CuasSceneFeeder sceneFeeder, // in-loop 16-bit feeder (T2) or null // By Claude on 07/18/2026
final boolean [] saved_calib, // out (may be null): [0]=true if the tile calibration was saved final boolean [] saved_calib, // out (may be null): [0]=true if the tile calibration was saved
final int debugLevel) { final int debugLevel) {
final double min_str = clt_parameters.curt.pose_str; // e.g. 1.0 final double min_str = clt_parameters.curt.pose_str; // e.g. 1.0
...@@ -2768,8 +2780,10 @@ public class CuasPoseRT { ...@@ -2768,8 +2780,10 @@ public class CuasPoseRT {
} }
} }
CuasScenePreload activePreload = scenePreload; CuasScenePreload activePreload = scenePreload;
if ((activePreload != null) && Double.isNaN(cond_cfg.dc_level)) { final CuasSceneFeeder activeFeeder = sceneFeeder; // T2 in-loop 16-bit feeder // By Claude on 07/18/2026
System.out.println("CuasPoseRT: ERROR - preloaded RT path requires fixed sequence DC; pose RT measurement NOT RUN because RT-STATE/manual dc_center is unavailable"); if (activeFeeder != null) activePreload = null; // mutually exclusive; the feeder supersedes
if (((activePreload != null) || (activeFeeder != null)) && Double.isNaN(cond_cfg.dc_level)) {
System.out.println("CuasPoseRT: ERROR - preloaded/feeder RT path requires fixed sequence DC; pose RT measurement NOT RUN because RT-STATE/manual dc_center is unavailable");
return null; return null;
} }
final float [][] preloadCpuScratch; final float [][] preloadCpuScratch;
...@@ -2786,6 +2800,24 @@ public class CuasPoseRT { ...@@ -2786,6 +2800,24 @@ public class CuasPoseRT {
} else { } else {
preloadCpuScratch = null; preloadCpuScratch = null;
} }
// T2 feeder (Andrey 07/18): real per-scene file I/O INSIDE the measured loop
// (the honest RT test; also the production 4xSATA shape). Row/Col moves into
// the conversion kernel (per-scene borrowed profiles, prior scales snapshotted
// at feeder creation), so the timed Config stays strictly linear like the
// preload contract. By Claude on 07/18/2026.
final float [][] feederCpuRaw;
final float [][] feederCpuScratch;
if (activeFeeder != null) {
cond_cfg.rowcol_enable = false;
feederCpuRaw = new float [activeFeeder.getNumSensors()][activeFeeder.getNumPixels()];
feederCpuScratch = new float [activeFeeder.getNumSensors()][activeFeeder.getNumPixels()];
System.out.println("CuasPoseRT: 16-BIT FEEDER input active (in-loop reads, "+
(activeFeeder.isRowColEnabled() ? "borrowed Row/Col in-kernel" : "Row/Col bypassed")+
", timed conditioning="+(cond_cfg.gpu_enable ? "GPU u16" : "CPU")+")");
} else {
feederCpuRaw = null;
feederCpuScratch = null;
}
// D5 overlap state (design ratified 07/17/2026): dp_overlap_scene[0] = the // D5 overlap state (design ratified 07/17/2026): dp_overlap_scene[0] = the
// scene index already conditioned into the ACTIVE device image-set ring slot // scene index already conditioned into the ACTIVE device image-set ring slot
// during the previous scene's DP entry (-1 = none); dp_ring_set[0] = the ring // during the previous scene's DP entry (-1 = none); dp_ring_set[0] = the ring
...@@ -2932,14 +2964,35 @@ public class CuasPoseRT { ...@@ -2932,14 +2964,35 @@ public class CuasPoseRT {
RtPoseProfile rtProfile = null; RtPoseProfile rtProfile = null;
com.elphel.imagej.common.PerfDaemon.Hold rtPerfHold = null; // 'measure' clock hold for the timed run // By Claude on 07/17/2026 com.elphel.imagej.common.PerfDaemon.Hold rtPerfHold = null; // 'measure' clock hold for the timed run // By Claude on 07/17/2026
RT_POSE_PROFILE.remove(); RT_POSE_PROFILE.remove();
if (activePreload != null) { if ((activePreload != null) || (activeFeeder != null)) { // feeder harness: same RT instrumentation // By Claude on 07/18/2026
int rtInputs = 0; int rtInputs = 0;
for (int i = earliest; i < quadCLTs.length; i++) if (quadCLTs[i] != null) rtInputs++; for (int i = earliest; i < quadCLTs.length; i++) if (quadCLTs[i] != null) rtInputs++;
if (activePreload != null) {
if (cond_cfg.gpu_enable && !CuasConditioning.preparePreloadedConditioning( if (cond_cfg.gpu_enable && !CuasConditioning.preparePreloadedConditioning(
quadCLTs[earliest], activePreload.getScene(earliest), cond_cfg)) { quadCLTs[earliest], activePreload.getScene(earliest), cond_cfg)) {
System.out.println("CuasPoseRT: WARNING - requested GPU linear conditioning is unavailable for this configuration; timed preload path will use the CPU oracle"); System.out.println("CuasPoseRT: WARNING - requested GPU linear conditioning is unavailable for this configuration; timed preload path will use the CPU oracle");
} }
} else {
// T2: prepare the resident maps, then switch the feeder to pinned mode
// (preads land straight in the native u16 staging). Failure of either
// leaves the CPU-oracle heap mode - functional, honest, just slower.
// By Claude on 07/18/2026.
activeFeeder.reset(); // bootstrap second pass / rerun: drain + clean staging state
final boolean feeder_gpu = cond_cfg.gpu_enable &&
CuasConditioning.prepareFeederConditioning(quadCLTs[earliest], activeFeeder, cond_cfg) &&
activeFeeder.enablePinned(quadCLTs[earliest].getGPUQuad());
if (cond_cfg.gpu_enable && !feeder_gpu) {
System.out.println("CuasPoseRT: WARNING - GPU u16 conditioning unavailable for this configuration; feeder falls back to the CPU oracle (heap reads)");
}
}
rtTiming = new RtPoseTiming(clt_parameters.curt.pose_pace, rtInputs); rtTiming = new RtPoseTiming(clt_parameters.curt.pose_pace, rtInputs);
if (activeFeeder != null) {
// Arrival moderation (Andrey 07/18): in paced mode the DATA - not just
// the loop - is released on the 60 Hz epoch grid; scene k's read starts
// no earlier than epoch + k*period. Then kick scene 0's read.
if (clt_parameters.curt.pose_pace) activeFeeder.arm(rtTiming.epochNs, rtTiming.periodNs);
activeFeeder.requestNext();
}
final int profileCycles = (clt_parameters.curt.pose_cycles > 0) ? final int profileCycles = (clt_parameters.curt.pose_cycles > 0) ?
clt_parameters.curt.pose_cycles : clt_parameters.imp.max_cycles; clt_parameters.curt.pose_cycles : clt_parameters.imp.max_cycles;
rtProfile = new RtPoseProfile(rtInputs, profileCycles); rtProfile = new RtPoseProfile(rtInputs, profileCycles);
...@@ -3001,6 +3054,18 @@ public class CuasPoseRT { ...@@ -3001,6 +3054,18 @@ public class CuasPoseRT {
// previous scene's DP entry - nothing to do here. By Claude on 07/17/2026. // previous scene's DP entry - nothing to do here. By Claude on 07/17/2026.
dp_overlap_scene[0] = -1; dp_overlap_scene[0] = -1;
raw_ok = true; raw_ok = true;
// T2 safe point: everything through the previous chain has collected,
// so the next staging slot's H2D is at least one full scene behind
// (the float path's one-scene-of-slack contract) - kick the next read.
// By Claude on 07/18/2026.
if (activeFeeder != null) activeFeeder.requestNext();
} else if (activeFeeder != null) {
// T2 serial path: await this scene's staged read, enqueue u16 H2D +
// convert/condition, then kick the next read (same safe point).
// By Claude on 07/18/2026.
raw_ok = CuasConditioning.conditionFeederSceneToGpu(
quadCLTs[nscene], activeFeeder, nscene, feederCpuRaw, feederCpuScratch, cond_cfg);
activeFeeder.requestNext();
} else { } else {
raw_ok = (activePreload != null) ? raw_ok = (activePreload != null) ?
CuasConditioning.conditionPreloadedSceneToGpu( CuasConditioning.conditionPreloadedSceneToGpu(
...@@ -3159,8 +3224,10 @@ public class CuasPoseRT { ...@@ -3159,8 +3224,10 @@ public class CuasPoseRT {
// as before. Any failure restores the previous set and disables the // as before. Any failure restores the previous set and disables the
// overlap for the rest of the run (fail-safe). By Claude on 07/17/2026. // overlap for the rest of the run (fail-safe). By Claude on 07/17/2026.
Runnable dp_overlap_work = null; Runnable dp_overlap_work = null;
final CuasScenePreload overlapPreload = activePreload;
if (dp_overlap_ok[0] && clt_parameters.curt.pose_h2d_overlap && if (dp_overlap_ok[0] && clt_parameters.curt.pose_h2d_overlap &&
clt_parameters.curt.pose_raw && (activePreload != null) && clt_parameters.curt.pose_raw &&
((activePreload != null) || ((activeFeeder != null) && activeFeeder.isPinned())) && // T2: feeder overlap (file I/O IS the overlap now) // By Claude on 07/18/2026
center_CLT.getGPUQuad().supportsPoseSceneDpOverlap()) { center_CLT.getGPUQuad().supportsPoseSceneDpOverlap()) {
int nn = -1; int nn = -1;
for (int j = nscene + 1; j < quadCLTs.length; j++) { for (int j = nscene + 1; j < quadCLTs.length; j++) {
...@@ -3169,9 +3236,34 @@ public class CuasPoseRT { ...@@ -3169,9 +3236,34 @@ public class CuasPoseRT {
if (nn >= 0) { if (nn >= 0) {
final int next_scene = nn; final int next_scene = nn;
dp_overlap_work = () -> { dp_overlap_work = () -> {
if (activeFeeder != null) {
// T2: paced mode never parks inside the DP window - the overlap
// only fires when the next scene's read already landed (i.e.
// when running behind arrival, exactly when throughput is
// needed); when caught up the serial order has idle margin.
// Unpaced (capacity) mode awaits the in-flight read - it was
// kicked a full scene ago and hides under the ~12 ms DP entry.
// A failed read just skips (the serial path coasts that scene);
// it does NOT disable the overlap. By Claude on 07/18/2026.
if (activeFeeder.isPaced() ? !activeFeeder.isStaged(next_scene) :
!activeFeeder.awaitStaged(next_scene)) {
return;
}
final int target_set = dp_ring_set[0] ^ 1;
if (CuasConditioning.conditionFeederSceneToGpuOverlap(
quadCLTs[next_scene], activeFeeder, next_scene,
cond_cfg, target_set, dp_ring_set[0])) {
dp_ring_set[0] = target_set;
dp_overlap_scene[0] = next_scene;
} else {
dp_overlap_ok[0] = false;
System.out.println("CuasPoseRT: D5 overlap conditioning DISABLED for this run - serial order resumes");
}
return;
}
final int target_set = dp_ring_set[0] ^ 1; final int target_set = dp_ring_set[0] ^ 1;
if (CuasConditioning.conditionPreloadedSceneToGpuOverlap( if (CuasConditioning.conditionPreloadedSceneToGpuOverlap(
quadCLTs[next_scene], activePreload.getScene(next_scene), quadCLTs[next_scene], overlapPreload.getScene(next_scene),
preloadCpuScratch, cond_cfg, target_set, dp_ring_set[0])) { preloadCpuScratch, cond_cfg, target_set, dp_ring_set[0])) {
dp_ring_set[0] = target_set; dp_ring_set[0] = target_set;
dp_overlap_scene[0] = next_scene; dp_overlap_scene[0] = next_scene;
......
...@@ -88,8 +88,22 @@ public class CuasRT { ...@@ -88,8 +88,22 @@ public class CuasRT {
// calibratePhotometric(): borrowed Row/Col profiles are expressed under the prior // calibratePhotometric(): borrowed Row/Col profiles are expressed under the prior
// corr-xml scales still attached to the scenes here. By Codex on 07/15/2026. // corr-xml scales still attached to the scenes here. By Codex on 07/15/2026.
CuasScenePreload scenePreload = null; CuasScenePreload scenePreload = null;
CuasSceneFeeder sceneFeeder = null; // T2 in-loop 16-bit feeder // By Claude on 07/18/2026
if (clt_parameters.curt.pose_test && clt_parameters.curt.pose_raw && if (clt_parameters.curt.pose_test && clt_parameters.curt.pose_raw &&
"none".equals(clt_parameters.curt.kernel_test)) { "none".equals(clt_parameters.curt.kernel_test)) {
if (clt_parameters.curt.pose_feed16) {
// T2 (Andrey 07/18): drop the ~9.7 GiB float preload - real per-scene
// 16-bit reads inside the measured loop (honest RT test, production
// 4xSATA shape). Created HERE for the same reason as the preload: the
// borrowed Row/Col prior scales must be snapshotted before
// calibratePhotometric() refines them. By Claude on 07/18/2026.
sceneFeeder = CuasSceneFeeder.create(
quadCLTs, clt_parameters.curt.rowcol_en, debugLevel - 2);
if (sceneFeeder == null) {
System.out.println("CuasRT: ERROR - 16-bit feeder setup failed; pose RT measurement NOT RUN (uncheck curt_pose_feed16 for the float preload)");
return;
}
} else {
scenePreload = CuasScenePreload.load( scenePreload = CuasScenePreload.load(
quadCLTs, clt_parameters.curt.rowcol_en, quadCLTs, clt_parameters.curt.rowcol_en,
ImageDtt.THREADS_MAX, debugLevel - 2); ImageDtt.THREADS_MAX, debugLevel - 2);
...@@ -98,12 +112,15 @@ public class CuasRT { ...@@ -98,12 +112,15 @@ public class CuasRT {
return; return;
} }
} }
}
calibratePhotometric(clt_parameters, master_CLT, quadCLTs, quadCLT_main, ref_index, debugLevel); calibratePhotometric(clt_parameters, master_CLT, quadCLTs, quadCLT_main, ref_index, debugLevel);
appropriateCenter(clt_parameters, master_CLT, debugLevel); appropriateCenter(clt_parameters, master_CLT, debugLevel);
// ---- 2. DIAGNOSTICS (optional, exclusive - replace production and return) ---- // ---- 2. DIAGNOSTICS (optional, exclusive - replace production and return) ----
if (diagnostics(clt_parameters, master_CLT, quadCLTs, scenePreload, debugLevel)) { if (diagnostics(clt_parameters, master_CLT, quadCLTs, scenePreload, sceneFeeder, debugLevel)) {
if (sceneFeeder != null) sceneFeeder.shutdown(); // By Claude on 07/18/2026
return; return;
} }
if (sceneFeeder != null) sceneFeeder.shutdown(); // By Claude on 07/18/2026
// ---- 3. PRODUCTION: per-scene processing + target detection ---- // ---- 3. PRODUCTION: per-scene processing + target detection ----
detect(clt_parameters, cuasRanging, master_CLT, imp_targets, uasLogReader, batch_mode, debugLevel); detect(clt_parameters, cuasRanging, master_CLT, imp_targets, uasLogReader, batch_mode, debugLevel);
} }
...@@ -367,6 +384,7 @@ public class CuasRT { ...@@ -367,6 +384,7 @@ public class CuasRT {
final QuadCLT master_CLT, final QuadCLT master_CLT,
final QuadCLT [] quadCLTs, final QuadCLT [] quadCLTs,
final CuasScenePreload scenePreload, final CuasScenePreload scenePreload,
final CuasSceneFeeder sceneFeeder, // T2 in-loop 16-bit feeder (or null) // By Claude on 07/18/2026
final int debugLevel) { final int debugLevel) {
boolean ran = false; boolean ran = false;
if (!"none".equals(clt_parameters.curt.kernel_test)) { if (!"none".equals(clt_parameters.curt.kernel_test)) {
...@@ -389,6 +407,7 @@ public class CuasRT { ...@@ -389,6 +407,7 @@ public class CuasRT {
master_CLT, // QuadCLT center_CLT, master_CLT, // QuadCLT center_CLT,
quadCLTs, // QuadCLT [] quadCLTs, quadCLTs, // QuadCLT [] quadCLTs,
scenePreload, // CuasScenePreload preloaded raw scenes (or null) scenePreload, // CuasScenePreload preloaded raw scenes (or null)
sceneFeeder, // CuasSceneFeeder in-loop 16-bit feeder (or null) // By Claude on 07/18/2026
debugLevel); // int debugLevel debugLevel); // int debugLevel
ran = true; ran = true;
} }
......
/**
** CuasSceneFeeder.java - in-loop 16-bit raw scene feeder for CUAS RT
**
** Copyright (C) 2026 Elphel, Inc.
**
** -----------------------------------------------------------------------------**
**
** CuasSceneFeeder.java is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** CuasSceneFeeder.java is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with CuasSceneFeeder.java. If not, see <http://www.gnu.org/licenses/>.
** -----------------------------------------------------------------------------**
**
*/
package com.elphel.imagej.cuas.rt;
import java.io.File;
import java.io.RandomAccessFile;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.LockSupport;
import com.elphel.imagej.tileprocessor.QuadCLT;
import com.sun.jna.Pointer;
/**
* In-loop 16-bit TIFF scene feeder (T2, Andrey's 07/18/2026 ruling): replaces
* the ~9.7 GiB float all-scene preload with real per-scene file I/O inside the
* measured loop - the HONEST RT test, and the code shape that stays valid for
* the production feeder (4 external RAM-based dual-port SATA devices, 4
* consecutive sensors each; this class reads with 4 threads in exactly those
* sensor groups).
*
* Fast fixed-offset reader: the single-strip 16-bit TIFF layout (byte order,
* dimensions, strip offset/size, telemetry row count) is parsed ONCE from the
* first file and applied to every other file (verified by equal file length).
* The Boson-640 telemetry row (strip top) is skipped at the read offset; Exif
* parsing is not needed in the loop (DumpTiff oracle 07/18: the production
* ImagejJp4Tiff output is bit-exactly (float)u16 of strip rows 1..512 - the
* gains cancel exactly).
*
* In pinned mode the reader preads raw strip bytes STRAIGHT into the native
* pinned u16 staging slot (PosixIo - the kernel's read IS the pinned write),
* the GPU does byte-swap + borrowed Row/Col + two-map conditioning in one
* kernel (condition_lwir_u16, bit-exact vs the preload path by the u16_cond
* case). posix_fadvise(DONTNEED) after each read keeps reruns honestly cold:
* the production device's frames are always new, page-cache replay would fake
* the I/O cost.
*
* Arrival moderation (60 Hz emulation): arm() gives the feeder the same epoch
* grid the paced harness uses; a scene's read then starts no earlier than its
* arrival time, so with the preload gone the DATA - not just the loop - is
* released at 60 Hz.
*
* Borrowed preparation (to be replaced by the slow background calibration per
* plan): per-scene Row/Col profiles stay CPU-resident on the scenes; their
* prior-calibration inverse scales are snapshotted at create() - before
* calibratePhotometric() refines the live scales - exactly like the preload.
*
* By Claude on 07/18/2026, from Andrey's T2 design.
*/
public final class CuasSceneFeeder {
private static final int READER_THREADS = 4; // production shape: one per SATA device group
private final QuadCLT [] quadCLTs;
private final String [][] scenePaths; // [scene][sensor] or null scene
private final double [] invScales; // 1/prior lwir_scales, snapshotted at create // [sensor]
private final boolean rowcolEnable;
private final int numSensors;
private final int width;
private final int height; // image rows (telemetry excluded)
private final boolean bigEndian;
private final long dataOffset; // strip offset + telemetry rows
private final long fileLength; // every file must match (fixed-offset contract)
private final int debugLevel;
private final ThreadPoolExecutor pool;
// per-scene staging state (single-threaded control from the pose loop)
private static final class Job {
final int nscene;
final int slot;
final long arrivalNs; // Long.MIN_VALUE = immediate
final CountDownLatch done = new CountDownLatch(READER_THREADS);
final AtomicBoolean ok = new AtomicBoolean(true);
short [][] heap; // heap mode only: [sensor][pixels], raw file byte order
Job(int nscene, int slot, long arrivalNs) {
this.nscene = nscene; this.slot = slot; this.arrivalNs = arrivalNs;
}
}
private final Job [] jobs; // by scene index
private int requestCount = 0; // slot = requestCount & 1 at request time
private int nextToRequest = 0; // next scene index to consider
private Job lastRequested = null;
private Job prevRequested = null;
// pinned mode (set by the harness after GPU conditioning is prepared)
private com.elphel.imagej.gpu.GpuQuad gpuQuad = null;
private final Pointer [] u16Staging = new Pointer [2];
// paced arrival grid (armed per pass)
private long epochNs = 0;
private long periodNs = 0;
private boolean paced = false;
private CuasSceneFeeder(
final QuadCLT [] quadCLTs,
final String [][] scenePaths,
final double [] invScales,
final boolean rowcolEnable,
final int numSensors,
final int width,
final int height,
final boolean bigEndian,
final long dataOffset,
final long fileLength,
final int debugLevel) {
this.quadCLTs = quadCLTs;
this.scenePaths = scenePaths;
this.invScales = invScales;
this.rowcolEnable = rowcolEnable;
this.numSensors = numSensors;
this.width = width;
this.height = height;
this.bigEndian = bigEndian;
this.dataOffset = dataOffset;
this.fileLength = fileLength;
this.debugLevel = debugLevel;
this.jobs = new Job [quadCLTs.length];
this.pool = new ThreadPoolExecutor(READER_THREADS, READER_THREADS,
60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(),
r -> { Thread t = new Thread(r, "CuasSceneFeeder"); t.setDaemon(true); return t; });
}
/**
* Build the feeder: per-scene sensor->file mapping (the readRawImageData
* rule), fixed-offset layout from file #1, uniform-length verification of
* EVERY file, and the same borrowed Row/Col provenance guards as the
* preload. Any incomplete scene aborts as a unit (null), exactly like
* CuasScenePreload - a configuration error must not silently coast.
*/
public static CuasSceneFeeder create(
final QuadCLT [] quadCLTs,
final boolean rowcol_enable,
final int debugLevel) {
if ((quadCLTs == null) || (quadCLTs.length == 0)) return null;
QuadCLT first = null;
for (QuadCLT q : quadCLTs) if (q != null) { first = q; break; }
if (first == null) return null;
final int num_sens = first.getNumSensors();
final int [] wh = first.getGeometryCorrection().getSensorWH();
final String [][] scenePaths = new String [quadCLTs.length][];
final String [] sourceFiles = first.correctionsParameters.getSourcePaths();
int mapped_scenes = 0;
for (int nscene = 0; nscene < quadCLTs.length; nscene++) {
final QuadCLT scene = quadCLTs[nscene];
if (scene == null) continue;
final String set_name = scene.getImageName();
final String [] paths = new String [num_sens];
int found = 0;
for (int nFile = 0; nFile < sourceFiles.length; nFile++) {
if ((sourceFiles[nFile] == null) || (sourceFiles[nFile].length() <= 1)) continue;
if (!set_name.equals(scene.correctionsParameters.getNameFromSourceTiff(sourceFiles[nFile]))) continue;
final int [] channels = scene.fileChannelToSensorChannels(
scene.correctionsParameters.getChannelFromSourceTiff(sourceFiles[nFile]));
if (channels == null) continue;
for (int ch : channels) if ((ch >= 0) && (ch < num_sens) && scene.eyesisCorrections.isChannelEnabled(ch)) {
if (paths[ch] == null) found++;
paths[ch] = sourceFiles[nFile];
}
}
if (found != num_sens) {
System.out.println("CuasSceneFeeder: scene "+nscene+" ("+set_name+") has "+found+
" of "+num_sens+" source files - feeder NOT built");
return null;
}
scenePaths[nscene] = paths;
mapped_scenes++;
}
if (mapped_scenes == 0) return null;
// Borrowed Row/Col provenance (the preload rules): prior scales2 must be
// zero (a quadratic term cannot be mapped back to raw) and every scene
// must carry complete, dimension-consistent profiles when enabled.
// Inverse PRIOR scales are snapshotted now - calibratePhotometric() will
// refine the live scales after this point.
double [] inv_scales = null;
if (rowcol_enable) {
inv_scales = new double [num_sens];
final double [] scales = first.getLwirScales();
final double [] scales2 = first.getLwirScales2();
if (scales2 != null) for (int s = 0; s < scales2.length; s++) {
if (scales2[s] != 0.0) {
System.out.println("CuasSceneFeeder: nonzero prior scales2["+s+"]="+scales2[s]+
" cannot map calibrated Row/Col profiles back to raw - feeder NOT built");
return null;
}
}
for (int s = 0; s < num_sens; s++) {
final double sc = ((scales != null) && (s < scales.length)) ? scales[s] : 0.0;
if (!Double.isFinite(sc) || (sc == 0.0)) {
System.out.println("CuasSceneFeeder: invalid prior calibration scale for sensor "+s+
": "+sc+" - feeder NOT built");
return null;
}
inv_scales[s] = 1.0 / sc;
}
for (int nscene = 0; nscene < quadCLTs.length; nscene++) {
final QuadCLT scene = quadCLTs[nscene];
if (scene == null) continue;
final double [][] row = scene.getRowCorr();
final double [][] col = scene.getColCorr();
if ((row == null) || (col == null)) {
System.out.println("CuasSceneFeeder: scene "+nscene+" has no borrowed Row/Col profiles - feeder NOT built");
return null;
}
for (int s = 0; s < num_sens; s++) {
if ((s >= row.length) || (s >= col.length) || (row[s] == null) || (col[s] == null) ||
(row[s].length != wh[0])) {
System.out.println("CuasSceneFeeder: incomplete Row/Col for scene "+nscene+
" sensor "+s+" - feeder NOT built");
return null;
}
}
}
}
// Fixed-offset layout from file #1 + uniform file length everywhere.
final String firstPath = scenePaths[firstSceneIndex(scenePaths)][0];
final TiffLayout layout = parseTiffLayout(firstPath);
if (layout == null) return null; // message printed
if (layout.width != wh[0]) {
System.out.println("CuasSceneFeeder: TIFF width "+layout.width+" != sensor width "+wh[0]+
" - feeder NOT built");
return null;
}
final int image_h = wh[1];
final int telemetry_rows = layout.stripRows - image_h;
if ((telemetry_rows < 0) || (telemetry_rows > 1)) {
System.out.println("CuasSceneFeeder: strip rows "+layout.stripRows+" vs image height "+image_h+
" (telemetry rows "+telemetry_rows+") unsupported - feeder NOT built");
return null;
}
final long file_len = new File(firstPath).length();
long checked = 0;
for (String [] paths : scenePaths) {
if (paths == null) continue;
for (String path : paths) {
if (new File(path).length() != file_len) {
System.out.println("CuasSceneFeeder: "+path+" length differs from "+file_len+
" - fixed-offset contract broken, feeder NOT built");
return null;
}
checked++;
}
}
final long data_offset = layout.stripOffset + (long) telemetry_rows * layout.width * 2;
System.out.println(String.format(
"CuasSceneFeeder: READY %d scenes x %d sensors, %dx%d %s-endian, strip@%d (+%d telemetry row%s),"+
" %d files verified @%d bytes; %d reader threads (4-sensor groups, the 4xSATA production shape);"+
" Row/Col %s; reads are IN the measured loop (fadvise keeps them cold)",
mapped_scenes, num_sens, layout.width, image_h, layout.bigEndian ? "big" : "little",
layout.stripOffset, telemetry_rows, (telemetry_rows == 1) ? "" : "s",
checked, file_len, READER_THREADS,
rowcol_enable ? "borrowed profiles applied in-kernel (prior scales snapshotted)" : "bypassed"));
return new CuasSceneFeeder(quadCLTs, scenePaths, inv_scales, rowcol_enable,
num_sens, layout.width, image_h, layout.bigEndian, data_offset, file_len, debugLevel);
}
private static int firstSceneIndex(final String [][] scenePaths) {
for (int i = 0; i < scenePaths.length; i++) if (scenePaths[i] != null) return i;
return -1;
}
private static final class TiffLayout {
boolean bigEndian;
int width, stripRows;
long stripOffset;
}
/** Minimal single-strip 16-bit TIFF IFD parse (file #1 only - the feeder contract). */
private static TiffLayout parseTiffLayout(final String path) {
try (RandomAccessFile raf = new RandomAccessFile(path, "r")) {
final byte [] head = new byte [4096];
final int n = raf.read(head);
if (n < 16) { System.out.println("CuasSceneFeeder: "+path+" too short"); return null; }
final boolean big = (head[0] == 'M') && (head[1] == 'M');
if (!big && !((head[0] == 'I') && (head[1] == 'I'))) {
System.out.println("CuasSceneFeeder: "+path+" is not a TIFF"); return null;
}
final TiffLayout lt = new TiffLayout();
lt.bigEndian = big;
final long ifd = get32(head, 4, big);
if (ifd + 2 > n) { System.out.println("CuasSceneFeeder: "+path+" IFD beyond header read"); return null; }
final int entries = get16(head, (int) ifd, big);
long strip_bytes = -1;
int bits = -1, compression = 1, spp = 1, height = -1;
for (int i = 0; i < entries; i++) {
final int e = (int) ifd + 2 + 12*i;
if (e + 12 > n) { System.out.println("CuasSceneFeeder: "+path+" IFD entry beyond header read"); return null; }
final int tag = get16(head, e, big);
final int typ = get16(head, e + 2, big);
final long cnt = get32(head, e + 4, big);
final long val = (typ == 3) ? get16(head, e + 8, big) : get32(head, e + 8, big);
switch (tag) {
case 256: lt.width = (int) val; break;
case 257: height = (int) val; break;
case 258: bits = (int) val; break;
case 259: compression = (int) val; break;
case 273: if (cnt != 1) { System.out.println("CuasSceneFeeder: "+path+" multi-strip - unsupported"); return null; }
lt.stripOffset = val; break;
case 277: spp = (int) val; break;
case 279: if (cnt != 1) { System.out.println("CuasSceneFeeder: "+path+" multi-strip - unsupported"); return null; }
strip_bytes = val; break;
}
}
if ((bits != 16) || (compression != 1) || (spp != 1) || (lt.width <= 0) || (height <= 0) ||
(lt.stripOffset <= 0) || (strip_bytes != 2L * lt.width * height)) {
System.out.println("CuasSceneFeeder: "+path+" unsupported layout (bits="+bits+
" comp="+compression+" spp="+spp+" "+lt.width+"x"+height+
" strip@"+lt.stripOffset+" bytes="+strip_bytes+")");
return null;
}
lt.stripRows = height;
return lt;
} catch (Exception e) {
System.out.println("CuasSceneFeeder: cannot parse "+path+": "+e);
return null;
}
}
private static int get16(final byte [] b, final int off, final boolean big) {
return big ? (((b[off] & 0xff) << 8) | (b[off+1] & 0xff)) :
(((b[off+1] & 0xff) << 8) | (b[off] & 0xff));
}
private static long get32(final byte [] b, final int off, final boolean big) {
return big ?
(((long)(b[off] & 0xff) << 24) | ((b[off+1] & 0xff) << 16) | ((b[off+2] & 0xff) << 8) | (b[off+3] & 0xff)) :
(((long)(b[off+3] & 0xff) << 24) | ((b[off+2] & 0xff) << 16) | ((b[off+1] & 0xff) << 8) | (b[off] & 0xff));
}
public int getNumSensors() { return numSensors; }
public int getWidth() { return width; }
public int getHeight() { return height; }
public int getNumPixels() { return width * height; }
public boolean isBigEndian() { return bigEndian; }
public boolean isRowColEnabled() { return rowcolEnable; }
public boolean isPaced() { return paced; }
/**
* Switch to pinned mode: reads go straight into the native u16 staging.
* Call after GPU conditioning is prepared; false leaves heap mode (CPU oracle).
*/
public boolean enablePinned(final com.elphel.imagej.gpu.GpuQuad gpu) {
if ((gpu == null) || !gpu.supportsU16Conditioning()) return false;
if ((gpu.getImageWidth() != width) || (gpu.getImageHeight() != height)) {
System.out.println("CuasSceneFeeder: GPU image "+gpu.getImageWidth()+"x"+gpu.getImageHeight()+
" != feeder "+width+"x"+height+" - staying in heap mode");
return false;
}
for (int slot = 0; slot < 2; slot++) {
u16Staging[slot] = gpu.u16Staging(slot);
if (u16Staging[slot] == null) return false;
}
gpuQuad = gpu;
return true;
}
public boolean isPinned() { return gpuQuad != null; }
/** Arm the 60 Hz arrival grid (paced harness): scene k's read starts no earlier
* than epochNs + k*periodNs (k = request order over non-null scenes). */
public void arm(final long epochNs, final long periodNs) {
this.epochNs = epochNs;
this.periodNs = periodNs;
this.paced = true;
}
/** Bootstrap/rerun support: drain in-flight reads, clear all staging state. */
public void reset() {
if (lastRequested != null) try { lastRequested.done.await(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
if (prevRequested != null) try { prevRequested.done.await(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
java.util.Arrays.fill(jobs, null);
requestCount = 0;
nextToRequest = 0;
lastRequested = null;
prevRequested = null;
paced = false;
}
public void shutdown() { pool.shutdown(); }
/**
* Kick the asynchronous read of the NEXT unrequested scene. Call sites are the
* loop's safe points (all chains through scene N-1 collected), which guarantees
* the target staging slot's previous H2D has completed - the same one-scene
* slack argument as the float staging path.
*/
public void requestNext() {
while ((nextToRequest < quadCLTs.length) && (scenePaths[nextToRequest] == null)) nextToRequest++;
if (nextToRequest >= quadCLTs.length) return;
final int nscene = nextToRequest++;
final int slot = requestCount & 1;
final long arrival = paced ? (epochNs + requestCount * periodNs) : Long.MIN_VALUE;
requestCount++;
final Job job = new Job(nscene, slot, arrival);
if (!isPinned()) job.heap = new short [numSensors][];
jobs[nscene] = job;
prevRequested = lastRequested;
lastRequested = job;
final int group_size = (numSensors + READER_THREADS - 1) / READER_THREADS;
for (int g = 0; g < READER_THREADS; g++) {
final int s0 = g * group_size;
final int s1 = Math.min(numSensors, s0 + group_size);
pool.execute(() -> readGroup(job, s0, s1));
}
}
public boolean isStaged(final int nscene) {
final Job job = jobs[nscene];
return (job != null) && (job.done.getCount() == 0) && job.ok.get();
}
public boolean isRequested(final int nscene) { return jobs[nscene] != null; }
public int getSlot(final int nscene) {
final Job job = jobs[nscene];
return (job == null) ? -1 : job.slot;
}
/** Block until scene nscene is staged (false = read failed - caller coasts). */
public boolean awaitStaged(final int nscene) {
Job job = jobs[nscene];
if (job == null) {
// out-of-order request (should not happen in the pose loop) - serve inline
while ((nextToRequest <= nscene) && (nextToRequest < quadCLTs.length)) requestNext();
job = jobs[nscene];
if (job == null) return false;
}
try { job.done.await(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); return false; }
return job.ok.get();
}
/** One reader thread's share: sensors [s0, s1) - a production SATA device group. */
private void readGroup(final Job job, final int s0, final int s1) {
try {
if (job.arrivalNs != Long.MIN_VALUE) {
for (;;) {
final long remain = job.arrivalNs - System.nanoTime();
if (remain <= 0) break;
LockSupport.parkNanos(remain);
}
}
final String [] paths = scenePaths[job.nscene];
final long count = 2L * width * height;
for (int s = s0; s < s1; s++) {
if (!job.ok.get()) break; // another group already failed
if (isPinned() && PosixIo.isAvailable()) {
final int fd = PosixIo.open(paths[s], PosixIo.O_RDONLY);
if (fd < 0) { fail(job, paths[s], "open failed"); break; }
try {
final Pointer dst = u16Staging[job.slot].share((long) s * width * height * 2);
long got = 0;
while (got < count) {
final long r = PosixIo.pread(fd, dst.share(got), count - got, dataOffset + got);
if (r <= 0) { fail(job, paths[s], "pread returned "+r+" at "+got); break; }
got += r;
}
if (got < count) break;
PosixIo.posix_fadvise(fd, 0, 0, PosixIo.POSIX_FADV_DONTNEED); // keep reruns cold
} finally {
PosixIo.close(fd);
}
} else {
// heap read (CPU oracle mode / PosixIo unavailable)
final byte [] buf = new byte [(int) count];
try (RandomAccessFile raf = new RandomAccessFile(paths[s], "r")) {
raf.seek(dataOffset);
raf.readFully(buf);
} catch (Exception e) { fail(job, paths[s], e.toString()); break; }
if (isPinned()) {
// pinned staging ALWAYS holds raw file bytes - the kernel swaps
u16Staging[job.slot].write((long) s * width * height * 2, buf, 0, buf.length);
} else {
// heap staging holds DECODED values (no GPU swap downstream)
final short [] px = new short [width * height];
for (int i = 0; i < px.length; i++) {
px[i] = bigEndian ?
(short) (((buf[2*i] & 0xff) << 8) | (buf[2*i + 1] & 0xff)) :
(short) (((buf[2*i + 1] & 0xff) << 8) | (buf[2*i] & 0xff));
}
job.heap[s] = px;
}
}
}
} catch (Throwable t) {
fail(job, "group "+s0+"-"+(s1-1), t.toString());
} finally {
job.done.countDown();
}
}
private void fail(final Job job, final String what, final String why) {
if (job.ok.getAndSet(false)) {
System.out.println("CuasSceneFeeder: scene "+job.nscene+" read FAILED ("+what+": "+why+")");
}
}
/**
* Fill the pinned Row/Col staging block for one scene: rows [cam][width] |
* cols [cam][height] | inv_scales [cam], the condition_lwir_u16 layout.
* The profiles are the scene's borrowed CPU-resident arrays; the inverse
* scales are the create()-time prior-calibration snapshot.
*/
public boolean writeRowcolStaging(final int nscene, final Pointer rc) {
if (!rowcolEnable || (rc == null)) return false;
final QuadCLT scene = quadCLTs[nscene];
if (scene == null) return false;
final double [][] row = scene.getRowCorr();
final double [][] col = scene.getColCorr();
if ((row == null) || (col == null)) return false;
long off = 0;
for (int s = 0; s < numSensors; s++) {
if ((row[s] == null) || (row[s].length != width)) return false;
rc.write(off, row[s], 0, width);
off += 8L * width;
}
for (int s = 0; s < numSensors; s++) {
if ((col[s] == null) || (col[s].length != height)) return false;
rc.write(off, col[s], 0, height);
off += 8L * height;
}
rc.write(off, invScales, 0, numSensors);
return true;
}
/**
* CPU oracle: the staged u16 scene as conditioned-input floats - byte swap,
* borrowed Row/Col in double (the exact applyCalibratedRowColToRaw chain),
* ONE float cast. Bit-identical to what CuasScenePreload.getScene() held.
*/
public float [][] readSceneFloats(final int nscene, final float [][] out) {
final Job job = jobs[nscene];
if ((job == null) || (job.done.getCount() != 0) || !job.ok.get()) return null;
final QuadCLT scene = quadCLTs[nscene];
final double [][] row = rowcolEnable ? scene.getRowCorr() : null;
final double [][] col = rowcolEnable ? scene.getColCorr() : null;
final int npix = width * height;
final short [] pinned_buf = isPinned() ? new short [npix] : null;
for (int s = 0; s < numSensors; s++) {
final short [] px;
if (isPinned()) {
u16Staging[job.slot].read((long) s * npix * 2, pinned_buf, 0, npix);
px = pinned_buf;
} else {
px = job.heap[s];
}
if ((px == null) || (out[s] == null) || (out[s].length != npix)) return null;
final double inv = rowcolEnable ? invScales[s] : 0.0;
// pinned staging holds raw file bytes (host short read = LE view -> swap
// when the file is big-endian); heap staging holds decoded values
final boolean swap = isPinned() && bigEndian;
for (int i = 0; i < npix; i++) {
final int v = (swap ? Short.reverseBytes(px[i]) : px[i]) & 0xffff;
double d = v;
if (rowcolEnable) {
d -= (row[s][i % width] + col[s][i / width]) * inv;
}
out[s][i] = (float) d;
}
}
return out;
}
}
/**
** PosixIo.java - minimal libc file I/O for the CUAS RT 16-bit scene feeder.
**
** Copyright (C) 2026 Elphel, Inc.
**
** -----------------------------------------------------------------------------**
**
** PosixIo.java is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** PosixIo.java is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with PosixIo.java. If not, see <http://www.gnu.org/licenses/>.
** -----------------------------------------------------------------------------**
**
*/
package com.elphel.imagej.cuas.rt;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
/**
* Direct-mapped libc calls for the T2 16-bit feeder: pread() straight into the
* pinned u16 staging buffer (no Java heap copy - the kernel's read() IS the
* pinned write), and posix_fadvise(DONTNEED) so every run's in-loop reads stay
* honestly cold (the production feeder is a RAM-based dual-port SATA device
* whose frames are always new - page-cache replay would fake the I/O cost).
* Linux/glibc only, like the JNA tile-processor backend it feeds.
* By Claude on 07/18/2026, from Andrey's T2 design.
*/
public final class PosixIo {
public static final int O_RDONLY = 0;
public static final int POSIX_FADV_DONTNEED = 4;
public static native int open(String path, int flags);
public static native long pread(int fd, Pointer buf, long count, long offset);
public static native int close(int fd);
public static native int posix_fadvise(int fd, long offset, long len, int advice);
private static boolean available = false;
static {
try {
Native.register(PosixIo.class, "c");
available = true;
} catch (Throwable t) {
System.out.println("PosixIo: libc direct mapping unavailable ("+t.getMessage()+
") - the 16-bit feeder will fall back to heap reads");
}
}
public static boolean isAvailable() { return available; }
private PosixIo() {}
}
...@@ -3156,6 +3156,35 @@ public class GpuQuad{ // quad camera description ...@@ -3156,6 +3156,35 @@ public class GpuQuad{ // quad camera description
public boolean imageSetReadyRecord() { public boolean imageSetReadyRecord() {
return false; return false;
} }
// ---- T2 16-bit feeder (07/18/2026): pinned u16 staging + one-pass GPU
// uint16->float + Row/Col + two-map conditioning. Base/JCuda: unsupported -
// the float preload/direct paths remain. By Claude on 07/18/2026. ----
public boolean supportsU16Conditioning() {
return false;
}
/** Pinned host u16 staging for slot (0/1) - the feeder preads raw TIFF strip
* bytes straight into it. Null = unsupported/failed. */
public com.sun.jna.Pointer u16Staging(int slot) {
return null;
}
/** Pinned host staging for the borrowed per-scene Row/Col doubles:
* rows [cam][width] | cols [cam][height] | inv_scales [cam]. */
public com.sun.jna.Pointer rowcolStaging(int slot) {
return null;
}
/** Enqueue the async H2D of one sensor's u16 image from staging slot (copy stream). */
public boolean setImageStagedU16(int cam, int slot) {
return false;
}
/** Enqueue the async H2D of the rowcol staging block (copy stream). */
public boolean setRowcolStaged(int slot) {
return false;
}
/** Convert+condition the staged u16 scene into the ACTIVE image set on the copy
* stream (async; records the set's ready event - ONE event = uploaded+conditioned). */
public boolean execConditioningU16(boolean swapBytes, boolean rowcolEnable) {
return false;
}
/** D5b launch half of execPoseSceneDp: same inputs, no readbacks - enqueue the whole /** D5b launch half of execPoseSceneDp: same inputs, no readbacks - enqueue the whole
* per-scene chain and return immediately (want_trace/want_mstats fix what the later * per-scene chain and return immediately (want_trace/want_mstats fix what the later
* collect may read). Returns 1 (launched) or a negative. */ * collect may read). Returns 1 (launched) or a negative. */
......
...@@ -982,6 +982,60 @@ public class GpuQuadJna extends GpuQuad { ...@@ -982,6 +982,60 @@ public class GpuQuadJna extends GpuQuad {
return false; return false;
} }
} }
// ---- T2 16-bit feeder (07/18/2026): pinned u16 staging + one-pass GPU
// uint16->float + Row/Col + two-map conditioning. By Claude on 07/18/2026. ----
@Override public boolean supportsU16Conditioning() { return !rectilinear; }
@Override public Pointer u16Staging(int slot) {
try {
final Pointer ptr = lib.tp_proc_bayer_staging_u16(proc, slot);
if (ptr == null) System.out.println("GpuQuadJna.u16Staging("+slot+"): "+lib.tp_last_error());
return ptr;
} catch (UnsatisfiedLinkError e) {
System.out.println("GpuQuadJna.u16Staging(): native missing ("+e.getMessage()+")");
return null;
}
}
@Override public Pointer rowcolStaging(int slot) {
try {
final Pointer ptr = lib.tp_proc_rowcol_staging(proc, slot);
if (ptr == null) System.out.println("GpuQuadJna.rowcolStaging("+slot+"): "+lib.tp_last_error());
return ptr;
} catch (UnsatisfiedLinkError e) {
System.out.println("GpuQuadJna.rowcolStaging(): native missing ("+e.getMessage()+")");
return null;
}
}
@Override public boolean setImageStagedU16(int cam, int slot) {
try {
final int rc = lib.tp_proc_set_image_staged_u16(proc, cam, slot);
if (rc != 0) System.out.println("GpuQuadJna.setImageStagedU16("+cam+","+slot+"): rc="+rc+" ("+lib.tp_last_error()+")");
return rc == 0;
} catch (UnsatisfiedLinkError e) {
System.out.println("GpuQuadJna.setImageStagedU16(): native missing ("+e.getMessage()+")");
return false;
}
}
@Override public boolean setRowcolStaged(int slot) {
try {
final int rc = lib.tp_proc_set_rowcol_staged(proc, slot);
if (rc != 0) System.out.println("GpuQuadJna.setRowcolStaged("+slot+"): rc="+rc+" ("+lib.tp_last_error()+")");
return rc == 0;
} catch (UnsatisfiedLinkError e) {
System.out.println("GpuQuadJna.setRowcolStaged(): native missing ("+e.getMessage()+")");
return false;
}
}
@Override public boolean execConditioningU16(boolean swapBytes, boolean rowcolEnable) {
try {
final int rc = lib.tp_proc_exec_conditioning_u16(proc, swapBytes ? 1 : 0, rowcolEnable ? 1 : 0);
if (rc != 0) System.out.println("GpuQuadJna.execConditioningU16(): rc="+rc+" ("+lib.tp_last_error()+")");
if (rc == 0) jna_bayer_set = true; // conditioned images are now resident, same as an upload
return rc == 0;
} catch (UnsatisfiedLinkError e) {
System.out.println("GpuQuadJna.execConditioningU16(): native missing ("+e.getMessage()+")");
return false;
}
}
@Override public int execPoseSceneDpLaunch( @Override public int execPoseSceneDpLaunch(
final IntersceneLmaFloat.Camera reference, final IntersceneLmaFloat.Camera reference,
final IntersceneLmaFloat.Camera scene, final IntersceneLmaFloat.Camera scene,
......
...@@ -10,7 +10,7 @@ import com.sun.jna.Pointer; ...@@ -10,7 +10,7 @@ import com.sun.jna.Pointer;
* com.elphel.imagej.gpu.jna.Stage0 [kernel_src_dir] [libcudadevrt.a] * com.elphel.imagej.gpu.jna.Stage0 [kernel_src_dir] [libcudadevrt.a]
*/ */
public class Stage0 { public class Stage0 {
private static final int EXPECTED_KERNELS = 41; // 40 (through rung D4) + erase_clt_task_tiles (margin rung M1: erase-by-task-list in the pose chain) // By Claude on 07/17/2026 private static final int EXPECTED_KERNELS = 42; // 41 (through margin rung M1) + condition_lwir_u16 (T2 16-bit feeder) // By Claude on 07/18/2026
public static void main(String[] args) { public static void main(String[] args) {
String srcdir = (args.length > 0) ? args[0] : "/home/elphel/git/tile_processor_gpu/src"; String srcdir = (args.length > 0) ? args[0] : "/home/elphel/git/tile_processor_gpu/src";
String devrt = (args.length > 1) ? args[1] : "/usr/local/cuda/targets/x86_64-linux/lib/libcudadevrt.a"; String devrt = (args.length > 1) ? args[1] : "/usr/local/cuda/targets/x86_64-linux/lib/libcudadevrt.a";
......
...@@ -81,6 +81,22 @@ public interface TpJna extends Library { ...@@ -81,6 +81,22 @@ public interface TpJna extends Library {
* return immediately (per-sensor copy/compute pipelining). Image consumers fence * return immediately (per-sensor copy/compute pipelining). Image consumers fence
* natively, so a partial upload is never visible to kernels or readbacks. */ * natively, so a partial upload is never visible to kernels or readbacks. */
int tp_proc_set_image_staged(Pointer proc, int cam, int slot); int tp_proc_set_image_staged(Pointer proc, int cam, int slot);
// ---- T2 16-bit feeder (07/18/2026): pinned u16 staging + one-pass GPU
// uint16->float + Row/Col + two-map conditioning. By Claude on 07/18/2026. ----
/** Pinned host u16 staging for slot (0/1): num_cams*img_w*img_h uint16 - the feeder
* preads raw TIFF strip bytes straight into it (no heap copy). Null on failure. */
Pointer tp_proc_bayer_staging_u16(Pointer proc, int slot);
/** Pinned host staging for the borrowed per-scene Row/Col doubles:
* rows [cam][img_w] | cols [cam][img_h] | inv_scales [cam]. Null on failure. */
Pointer tp_proc_rowcol_staging(Pointer proc, int slot);
/** Enqueue the async H2D of ONE sensor's u16 image from staging slot into the device
* u16 scratch (copy stream; consumed by tp_proc_exec_conditioning_u16). */
int tp_proc_set_image_staged_u16(Pointer proc, int cam, int slot);
/** Enqueue the async H2D of the whole rowcol staging block (copy stream). */
int tp_proc_set_rowcol_staged(Pointer proc, int slot);
/** Convert+condition the staged u16 scene into the ACTIVE image set on the copy
* stream (async; records the set's ready event - the D5a ONE-event contract). */
int tp_proc_exec_conditioning_u16(Pointer proc, int swapBytes, int rowcolEnable);
/** De-pitch one resident source image into out[width*height]. */ /** De-pitch one resident source image into out[width*height]. */
int tp_proc_get_image(Pointer proc, int cam, float[] out); int tp_proc_get_image(Pointer proc, int cam, float[] out);
/** Resident production calibration maps, contiguous [camera][height][width]. */ /** Resident production calibration maps, contiguous [camera][height][width]. */
......
...@@ -25,6 +25,7 @@ public class CuasRtParameters { ...@@ -25,6 +25,7 @@ public class CuasRtParameters {
public int pose_shrink = 4; // tile-selection interior gate (3-A3, Andrey's rulings 07/13/2026): exclude tiles within this many tiles of the tile-array BORDER before selection (pure ring, NO morphological shrink - the calibration is NaN wherever a tile was not strength-selected, ~21% finite density, so a blind shrink annihilated the selection to ~2 tiles in the first attempt). The OOB-missing band is geometrically confined to the frame border (margin-dropped ring 1-2 tiles + partial 16-sensor OOB soft band 12 px = 1.5 tiles); ring 4 = hard+soft+slack, guaranteeing all-16-interior tiles at every scene pose - the GPU pose chain can then bypass the consolidation OOB pass with a FIXED per-sequence tile list. 0 = off (legacy selection). // By Claude on 07/13/2026 public int pose_shrink = 4; // tile-selection interior gate (3-A3, Andrey's rulings 07/13/2026): exclude tiles within this many tiles of the tile-array BORDER before selection (pure ring, NO morphological shrink - the calibration is NaN wherever a tile was not strength-selected, ~21% finite density, so a blind shrink annihilated the selection to ~2 tiles in the first attempt). The OOB-missing band is geometrically confined to the frame border (margin-dropped ring 1-2 tiles + partial 16-sensor OOB soft band 12 px = 1.5 tiles); ring 4 = hard+soft+slack, guaranteeing all-16-interior tiles at every scene pose - the GPU pose chain can then bypass the consolidation OOB pass with a FIXED per-sequence tile list. 0 = off (legacy selection). // By Claude on 07/13/2026
public boolean pose_raw = false; // phase A2/RT ingest: preload all RAW /jp4/ scenes before the measured loop; apply borrowed Row/Col profiles there when enabled; then condition with the current scale/offset/FPN/DC and force-upload. Bypasses prepared image_data and excludes file I/O from RT timing. // By Claude on 07/05/2026, preload Codex 07/15/2026 public boolean pose_raw = false; // phase A2/RT ingest: preload all RAW /jp4/ scenes before the measured loop; apply borrowed Row/Col profiles there when enabled; then condition with the current scale/offset/FPN/DC and force-upload. Bypasses prepared image_data and excludes file I/O from RT timing. // By Claude on 07/05/2026, preload Codex 07/15/2026
public boolean pose_pace = false; // preloaded pose RT harness: false = maximum-throughput run; true = release every input at exactly 60 Hz, process every scene with no skipping, and report queue/latency/deadline statistics. Saved as curt_pose_pace for run provenance. // By Codex on 07/15/2026 public boolean pose_pace = false; // preloaded pose RT harness: false = maximum-throughput run; true = release every input at exactly 60 Hz, process every scene with no skipping, and report queue/latency/deadline statistics. Saved as curt_pose_pace for run provenance. // By Codex on 07/15/2026
public boolean pose_feed16 = true; // T2 (Andrey 07/18): replace the ~9.7 GiB float preload with the in-loop 16-bit feeder - real per-scene fixed-offset TIFF reads (4 threads = the production 4xSATA sensor groups) pread straight into pinned u16 staging, GPU does byte-swap + borrowed Row/Col + conditioning in one kernel (bit-exact vs the preload, case u16_cond). fadvise keeps reruns honestly cold; paced mode also gates the DATA at 60 Hz arrival. OFF = the legacy preload. // By Claude on 07/18/2026
public boolean pose_lean = false; // phase B measurement engine: TD-average the 16 sensors (CuasTD, CPU bridge) then ONE conj-multiply vs the virtual-center TD -> FZ-normalize -> PD -> argmax+eigen (getMaxXYCmEig) -> 3-angle LMA. All existing GPU kernels. v1: NO motion-blur compensation - compare vs the NOMB baseline. // By Claude on 07/05/2026 public boolean pose_lean = false; // phase B measurement engine: TD-average the 16 sensors (CuasTD, CPU bridge) then ONE conj-multiply vs the virtual-center TD -> FZ-normalize -> PD -> argmax+eigen (getMaxXYCmEig) -> 3-angle LMA. All existing GPU kernels. v1: NO motion-blur compensation - compare vs the NOMB baseline. // By Claude on 07/05/2026
public boolean pose_full = false; // DEBUG: use ALL strength-selected tiles (~1074) - the -POSE-RT-TILE-CALIB calibration is neither read nor written (temporary bypass of the 150-tile filter for measurement debugging). // By Claude on 07/04/2026 public boolean pose_full = false; // DEBUG: use ALL strength-selected tiles (~1074) - the -POSE-RT-TILE-CALIB calibration is neither read nor written (temporary bypass of the 150-tile filter for measurement debugging). // By Claude on 07/04/2026
public boolean pose_corr_save = false; // DEBUG: save the per-scene 2D correlations vs the virtual center in the pixel domain (-POSE-RT-CORR2D: z=scenes, tile grid of 16x16 cells, last LMA cycle) - lean engine only. // By Claude on 07/04/2026 public boolean pose_corr_save = false; // DEBUG: save the per-scene 2D correlations vs the virtual center in the pixel domain (-POSE-RT-CORR2D: z=scenes, tile grid of 16x16 cells, last LMA cycle) - lean engine only. // By Claude on 07/04/2026
...@@ -142,6 +143,8 @@ public class CuasRtParameters { ...@@ -142,6 +143,8 @@ public class CuasRtParameters {
"Preload every RAW /jp4/ scene before timing; optionally apply borrowed Row/Col profiles during setup, then condition with current scale/offset/FPN/DC and force-upload. File I/O is excluded from RT timing."); "Preload every RAW /jp4/ scene before timing; optionally apply borrowed Row/Col profiles during setup, then condition with current scale/offset/FPN/DC and force-upload. File I/O is excluded from RT timing.");
gd.addCheckbox ("Pace preloaded pose test at 60 FPS", this.pose_pace, // By Codex on 07/15/2026 gd.addCheckbox ("Pace preloaded pose test at 60 FPS", this.pose_pace, // By Codex on 07/15/2026
"OFF = maximum-throughput run. ON = release all inputs at 60 Hz without skipping and report bounded-queue, latency and deadline statistics. Applies only when the all-scene preload succeeds."); "OFF = maximum-throughput run. ON = release all inputs at 60 Hz without skipping and report bounded-queue, latency and deadline statistics. Applies only when the all-scene preload succeeds.");
gd.addCheckbox ("In-loop 16-bit feeder (no preload)", this.pose_feed16, // By Claude on 07/18/2026
"Read the 16-bit scene TIFFs INSIDE the measured loop (4 reader threads = the production 4xSATA sensor groups) straight into pinned staging; the GPU converts+conditions in one kernel, bit-exact vs the preload. Honest RT I/O; with 60 FPS pacing the data arrival is gated too. OFF = the legacy ~9.7 GiB float preload.");
gd.addCheckbox ("Pose test lean correlation (B)", this.pose_lean, // By Claude on 07/05/2026 gd.addCheckbox ("Pose test lean correlation (B)", this.pose_lean, // By Claude on 07/05/2026
"Phase B measurement: TD-average the 16 sensors, then ONE conj-multiply vs the virtual-center TD -> FZ-normalize -> PD -> argmax+eigen -> 3-angle LMA. v1 has NO motion-blur compensation (compare vs the NOMB baseline)."); "Phase B measurement: TD-average the 16 sensors, then ONE conj-multiply vs the virtual-center TD -> FZ-normalize -> PD -> argmax+eigen -> 3-angle LMA. v1 has NO motion-blur compensation (compare vs the NOMB baseline).");
...@@ -358,6 +361,7 @@ public class CuasRtParameters { ...@@ -358,6 +361,7 @@ public class CuasRtParameters {
this.pose_shrink = (int) gd.getNextNumber(); // By Claude on 07/13/2026 this.pose_shrink = (int) gd.getNextNumber(); // By Claude on 07/13/2026
this.pose_raw = gd.getNextBoolean(); // By Claude on 07/05/2026 this.pose_raw = gd.getNextBoolean(); // By Claude on 07/05/2026
this.pose_pace = gd.getNextBoolean(); // By Codex on 07/15/2026 this.pose_pace = gd.getNextBoolean(); // By Codex on 07/15/2026
this.pose_feed16 = gd.getNextBoolean(); // By Claude on 07/18/2026
this.pose_lean = gd.getNextBoolean(); // By Claude on 07/05/2026 this.pose_lean = gd.getNextBoolean(); // By Claude on 07/05/2026
this.pose_full = gd.getNextBoolean(); // By Claude on 07/04/2026 this.pose_full = gd.getNextBoolean(); // By Claude on 07/04/2026
this.pose_corr_save = gd.getNextBoolean(); // By Claude on 07/04/2026 this.pose_corr_save = gd.getNextBoolean(); // By Claude on 07/04/2026
...@@ -469,6 +473,7 @@ public class CuasRtParameters { ...@@ -469,6 +473,7 @@ public class CuasRtParameters {
properties.setProperty(prefix+"pose_shrink", this.pose_shrink+""); // int // By Claude on 07/13/2026 properties.setProperty(prefix+"pose_shrink", this.pose_shrink+""); // int // By Claude on 07/13/2026
properties.setProperty(prefix+"pose_raw", this.pose_raw+""); // boolean // By Claude on 07/05/2026 properties.setProperty(prefix+"pose_raw", this.pose_raw+""); // boolean // By Claude on 07/05/2026
properties.setProperty(prefix+"pose_pace", this.pose_pace+""); // boolean // By Codex on 07/15/2026 properties.setProperty(prefix+"pose_pace", this.pose_pace+""); // boolean // By Codex on 07/15/2026
properties.setProperty(prefix+"pose_feed16", this.pose_feed16+""); // boolean // By Claude on 07/18/2026
properties.setProperty(prefix+"pose_lean", this.pose_lean+""); // boolean // By Claude on 07/05/2026 properties.setProperty(prefix+"pose_lean", this.pose_lean+""); // boolean // By Claude on 07/05/2026
properties.setProperty(prefix+"pose_full", this.pose_full+""); // boolean // By Claude on 07/04/2026 properties.setProperty(prefix+"pose_full", this.pose_full+""); // boolean // By Claude on 07/04/2026
properties.setProperty(prefix+"pose_corr_save", this.pose_corr_save+""); // boolean // By Claude on 07/04/2026 properties.setProperty(prefix+"pose_corr_save", this.pose_corr_save+""); // boolean // By Claude on 07/04/2026
...@@ -580,6 +585,7 @@ public class CuasRtParameters { ...@@ -580,6 +585,7 @@ public class CuasRtParameters {
if (properties.getProperty(prefix+"pose_shrink")!=null) this.pose_shrink=Integer.parseInt(properties.getProperty(prefix+"pose_shrink")); // By Claude on 07/13/2026 if (properties.getProperty(prefix+"pose_shrink")!=null) this.pose_shrink=Integer.parseInt(properties.getProperty(prefix+"pose_shrink")); // By Claude on 07/13/2026
if (properties.getProperty(prefix+"pose_raw")!=null) this.pose_raw=Boolean.parseBoolean(properties.getProperty(prefix+"pose_raw")); // By Claude on 07/05/2026 if (properties.getProperty(prefix+"pose_raw")!=null) this.pose_raw=Boolean.parseBoolean(properties.getProperty(prefix+"pose_raw")); // By Claude on 07/05/2026
if (properties.getProperty(prefix+"pose_pace")!=null) this.pose_pace=Boolean.parseBoolean(properties.getProperty(prefix+"pose_pace")); // By Codex on 07/15/2026 if (properties.getProperty(prefix+"pose_pace")!=null) this.pose_pace=Boolean.parseBoolean(properties.getProperty(prefix+"pose_pace")); // By Codex on 07/15/2026
if (properties.getProperty(prefix+"pose_feed16")!=null) this.pose_feed16=Boolean.parseBoolean(properties.getProperty(prefix+"pose_feed16")); // By Claude on 07/18/2026
if (properties.getProperty(prefix+"pose_lean")!=null) this.pose_lean=Boolean.parseBoolean(properties.getProperty(prefix+"pose_lean")); // By Claude on 07/05/2026 if (properties.getProperty(prefix+"pose_lean")!=null) this.pose_lean=Boolean.parseBoolean(properties.getProperty(prefix+"pose_lean")); // By Claude on 07/05/2026
if (properties.getProperty(prefix+"pose_full")!=null) this.pose_full=Boolean.parseBoolean(properties.getProperty(prefix+"pose_full")); // By Claude on 07/04/2026 if (properties.getProperty(prefix+"pose_full")!=null) this.pose_full=Boolean.parseBoolean(properties.getProperty(prefix+"pose_full")); // By Claude on 07/04/2026
if (properties.getProperty(prefix+"pose_corr_save")!=null) this.pose_corr_save=Boolean.parseBoolean(properties.getProperty(prefix+"pose_corr_save")); // By Claude on 07/04/2026 if (properties.getProperty(prefix+"pose_corr_save")!=null) this.pose_corr_save=Boolean.parseBoolean(properties.getProperty(prefix+"pose_corr_save")); // By Claude on 07/04/2026
...@@ -694,6 +700,7 @@ public class CuasRtParameters { ...@@ -694,6 +700,7 @@ public class CuasRtParameters {
cp.pose_shrink = this.pose_shrink; // By Claude on 07/13/2026 cp.pose_shrink = this.pose_shrink; // By Claude on 07/13/2026
cp.pose_raw = this.pose_raw; cp.pose_raw = this.pose_raw;
cp.pose_pace = this.pose_pace; // By Codex on 07/15/2026 cp.pose_pace = this.pose_pace; // By Codex on 07/15/2026
cp.pose_feed16 = this.pose_feed16; // By Claude on 07/18/2026
cp.pose_lean = this.pose_lean; cp.pose_lean = this.pose_lean;
cp.pose_full = this.pose_full; // By Claude on 07/04/2026 cp.pose_full = this.pose_full; // By Claude on 07/04/2026
cp.pose_corr_save = this.pose_corr_save; // By Claude on 07/04/2026 cp.pose_corr_save = this.pose_corr_save; // By Claude on 07/04/2026
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment