Commit 98e4ee98 authored by Andrey Filippov's avatar Andrey Filippov

CLAUDE: 3-C rung 1 - pose_corr kernel-test export (checkpoint 1 = raw TD corr...

CLAUDE: 3-C rung 1 - pose_corr kernel-test export (checkpoint 1 = raw TD corr after correlate2D_inter)

curt.kernel_test dropdown entry pose_corr: runs the normal lean pose sequence
with a static armed capture (PoseCorrExport) that records, for the FIRST
fitted scene, every LMA iteration's task streams exactly as the GPU received
them (pre-offsets, task|511) and as the consolidation flattened them
(post-offsets, disp_dist tail zero), the conditioned 16-sensor images exactly
as uploaded (hook in conditionSceneToGpuInner - the data never lands in
QuadCLT), geometry/correction-vector floats, aberration-kernel flatten, the
four LPF/HPF constant payloads, the resident center TD (gpu_clt_ref cam 0)
and the expected packed corr indices + raw TD correlation (captured right
after execCorr2D_inter_TD, before FZ-normalize = the rung-2 boundary).
The sequence stops early once the case is written; the TILE-CALIB save is
additionally gated on !captureDone() so a truncated max-over-scenes
calibration can never be written, and kernelTest refuses to run without an
existing -POSE-RT-TILE-CALIB (the bootstrap FULL pass must never be capture-
truncated). Requires pose_lean+pose_raw ON, pose_stored/pose_full OFF.
C++ side: tile_processor_gpu tests_bin/test_pose_corr --data <case> --tol 0
(newly exercises calculate_tiles_offsets + correlate2D_inter; results/ are
saved reordered into oracle index order for the Java round-trip compare).
Co-Authored-By: 's avatarClaude Fable 5 <noreply@anthropic.com>
parent 2f0611e3
......@@ -281,6 +281,10 @@ public class CuasConditioning {
scene.saveQuadClt(); // bind this scene (conditional; resets bayer guard on switch)
scene.setHasNewImageData(false); // the guard's other re-pull trigger - must be clear
gpuQuad.setBayerImages(data, true); // force H2D; sets bayer_set -> pipeline skips its pull
// pose_corr kernel-test capture: the conditioned data is uploaded directly (never
// stored in the QuadCLT), so the exporter takes its bit-exact copy here. No-op
// unless the curt.kernel_test=pose_corr run armed it. By Claude on 07/13/2026.
com.elphel.imagej.gpu.testdata.PoseCorrExport.offerImages(scene, data);
return true;
}
......
......@@ -32,6 +32,7 @@ import java.util.Arrays;
import com.elphel.imagej.cameras.CLTParameters;
import com.elphel.imagej.gpu.GpuQuad;
import com.elphel.imagej.gpu.TpTask;
import com.elphel.imagej.gpu.testdata.PoseCorrExport; // pose_corr kernel-test capture // By Claude on 07/13/2026
import com.elphel.imagej.tileprocessor.Correlation2d;
import com.elphel.imagej.tileprocessor.ErsCorrection;
import com.elphel.imagej.tileprocessor.ImageDtt;
......@@ -335,6 +336,10 @@ public class CuasPoseRT {
sortTasksByTile(tp_tasks2[0]); // fixed task-stream order (3-A3, GPU static shape) // By Claude on 07/13/2026
sortTasksByTile(tp_tasks2[1]);
cons_tasks = tp_tasks2; // By Claude on 07/12/2026
// pose_corr kernel-test capture (checkpoint 1, roadmap 3-C rung 1): the task
// streams exactly as handed to the GPU, BEFORE setTasks/updateTasks mutate them.
// No-op unless the curt.kernel_test=pose_corr run armed it. By Claude on 07/13/2026.
PoseCorrExport.iterTasksPre(clt_parameters, image_dtt, gpuQuad, scene, cons_tasks);
image_dtt.interCorrTDMotionBlur(
clt_parameters.img_dtt,
1, // erase_clt_first: NaN outside the task set
......@@ -370,6 +375,7 @@ public class CuasPoseRT {
}
sortTasksByTile(tp_tasks); // fixed task-stream order (3-A3, GPU static shape) // By Claude on 07/13/2026
cons_tasks = new TpTask [][] {tp_tasks, null}; // no-MB: single set, [1] = null // By Claude on 07/12/2026
PoseCorrExport.iterTasksPre(clt_parameters, image_dtt, gpuQuad, scene, cons_tasks); // pose_corr capture (armed only) // By Claude on 07/13/2026
// 2. convert only: LPFs + tasks + offsets + convert_direct, NO correlation (mask=0)
image_dtt.interCorrTD(
clt_parameters.img_dtt,
......@@ -405,6 +411,9 @@ public class CuasPoseRT {
// dominated, not fz-dominated (NOMB oracle 3.58 ~ lean 3.22 at 16x different
// amplitude); the fat zero regularizes usefully at the average's operating point.
// By Claude on 07/04/2026, falsified-and-reverted per LEAN-07.
// pose_corr capture: the SAME arrays after updateTasks() filled the GPU-computed
// offsets - the exact stream the consolidation flattens. By Claude on 07/13/2026.
PoseCorrExport.iterTasksPost(scene, cons_tasks);
CuasTD.consolidateToSlot0(gpuQuad, cons_tasks, // By Claude on 07/12/2026
clt_parameters.curt.oob_soft, clt_parameters.curt.oob_hard);
// DEBUG (img_out): render the composite the correlator sees (the weighted average)
......@@ -419,6 +428,9 @@ public class CuasPoseRT {
1.0/(1.0+clt_parameters.corr_red+clt_parameters.corr_blue)});
gpuQuad.setSensorMaskInter(1);
gpuQuad.execCorr2D_inter_TD(col_weights);
// pose_corr capture: checkpoint 1 = the raw TD correlation + packed indices right
// here, BEFORE fat-zero normalization (rung 2's boundary). By Claude on 07/13/2026.
PoseCorrExport.iterCorr(gpuQuad, scene, col_weights);
// 5. TD tiles -> FZ-normalize -> pixel domain (the JNA-validated TDCorrTile path)
final TDCorrTile [] td_tiles = TDCorrTile.getFromGpu(gpuQuad);
final double [][] corr_pd = TDCorrTile.convertTDtoPD(
......@@ -637,7 +649,10 @@ public class CuasPoseRT {
center_disparity, pXpYD_center, selection,
scene_xyzatr0[0], scene_xyzatr0[1], margin,
mb_vectors, mb_tau, mb_max_gain, corr_pd_out, img_out, debugLevel);
if (cm == null) return null;
if (cm == null) {
PoseCorrExport.sceneFailed(scene); // drop a partial pose_corr capture (armed only) // By Claude on 07/13/2026
return null;
}
final double [][] eigen_masked = clt_parameters.imp.eig_xy_lma ? null : cm[2];
intersceneLma.prepareLMA(
scene_xyzatr0, // scene_xyzatr0
......@@ -681,6 +696,7 @@ public class CuasPoseRT {
lma_debug); // curt.pose_lma_debug boost (inner solver only) // By Claude on 07/12/2026
if (lmaResult < 0) {
System.out.println("leanFitScene(): LMA failed, lmaResult="+lmaResult);
PoseCorrExport.sceneFailed(scene); // drop a partial pose_corr capture (armed only) // By Claude on 07/13/2026
return null;
}
scene_xyzatr0 = intersceneLma.getSceneXYZATR(false);
......@@ -746,6 +762,9 @@ public class CuasPoseRT {
System.out.println("Adjusted interscene, iteration="+nlma+", last RMS = "+
intersceneLma.getLastRms()[0]+" (lean)");
}
// pose_corr capture complete: all measure<->solve iterations of this scene are
// recorded - write the test case (armed only). By Claude on 07/13/2026.
PoseCorrExport.sceneDone(scene);
return scene_xyzatr0;
}
......@@ -1340,6 +1359,14 @@ public class CuasPoseRT {
prev_pose = predicted;
prev_ts = ts;
predicted = new double [][] {fitted[nscene][0].clone(), fitted[nscene][1].clone()};
// pose_corr kernel-test run (roadmap 3-C rung 1): the case is written once the
// first fitted scene completes - the exclusive diagnostic needs no more scenes
// (this run's CSV/products are partial and disposable). By Claude on 07/13/2026.
if (PoseCorrExport.captureDone()) {
System.out.println("CuasPoseRT: pose_corr kernel-test case captured after scene "+
nscene+" ("+ts_name+") - stopping the sequence early");
break;
}
}
// ---- Output ----
......@@ -1364,7 +1391,8 @@ public class CuasPoseRT {
// not selected/measured. Saved only from a FULL-selection run so a filtered run
// never shrinks the calibration coverage. The derived boolean selection is also
// saved (-POSE-RT-RELIABLE-FILT) for visualization.
if (full_selection && !clt_parameters.curt.pose_full && !hyper_scenes.isEmpty()) { // pose_full: never write the calibration // By Claude on 07/04/2026
if (full_selection && !clt_parameters.curt.pose_full && !hyper_scenes.isEmpty()
&& !PoseCorrExport.captureDone()) { // pose_full: never write the calibration // By Claude on 07/04/2026; a pose_corr capture stops the sequence early - a truncated max-over-scenes calibration must never be saved // By Claude on 07/13/2026
final float [] fmax = new float [num_pix];
Arrays.fill(fmax, Float.NaN);
for (int i = 0; i < num_pix; i++) if (reliable_ref[i]) {
......
......@@ -28,6 +28,7 @@ import java.util.Arrays;
import com.elphel.imagej.cuas.CuasMotion;
import com.elphel.imagej.gpu.testdata.AvgTdExport; // By Claude on 07/12/2026
import com.elphel.imagej.gpu.testdata.PoseCorrExport; // pose_corr kernel test (roadmap 3-C rung 1) // By Claude on 07/13/2026
import com.elphel.imagej.cuas.CuasRanging;
import com.elphel.imagej.gpu.GpuQuad;
import com.elphel.imagej.gpu.TpTask;
......@@ -358,7 +359,7 @@ public class CuasRT {
// exports ONE kernel's testdata case; nothing else runs, so a saved config
// can't accidentally mix a kernel test into other diagnostics/production.
// By Claude on 07/12/2026, from Andrey's design.
kernelTest(clt_parameters, master_CLT, debugLevel);
kernelTest(clt_parameters, master_CLT, quadCLTs, debugLevel); // quadCLTs: pose_corr runs the pose sequence // By Claude on 07/13/2026
return true;
}
if (clt_parameters.curt.pose_test) {
......@@ -431,6 +432,7 @@ public class CuasRT {
private static void kernelTest(
final CLTParameters clt_parameters,
final QuadCLT master_CLT,
final QuadCLT [] quadCLTs, // scene sequence (pose_corr runs the pose flow) // By Claude on 07/13/2026
final int debugLevel) {
final String kernel = clt_parameters.curt.kernel_test;
System.out.println("===== CUAS RT GPU-kernel test (curt_kernel_test): "+kernel+" =====");
......@@ -475,6 +477,42 @@ public class CuasRT {
dir_path, phys.getImageName(), debugLevel);
break;
}
case "pose_corr": {
// Lean pose chain CHECKPOINT 1 (roadmap 3-C rung 1): run the NORMAL pose
// sequence with the PoseCorrExport capture armed - the first fitted scene's
// per-LMA-iteration task streams, the resident center TD and the expected raw
// TD correlation (after correlate2D_inter, before FZ-normalize) become the
// case; the sequence then stops early (products of this run are disposable).
// C++ side: tests_bin/test_pose_corr (newly exercises calculate_tiles_offsets
// + correlate2D_inter @ tol 0). By Claude on 07/13/2026, from Andrey's plan.
if (!clt_parameters.curt.pose_lean || !clt_parameters.curt.pose_raw ||
clt_parameters.curt.pose_stored || clt_parameters.curt.pose_full) {
System.out.println("kernelTest(pose_corr): requires pose_lean + pose_raw ON, "+
"pose_stored + pose_full OFF (the production lean chain) - skipping");
return;
}
if (quadCLTs == null) {
System.out.println("kernelTest(pose_corr): no scene sequence - skipping");
return;
}
// the tile calibration must exist: a missing file would trigger the
// FULL-selection bootstrap pass, which the capture's early stop would truncate
if (master_CLT.readImagePlusFromModelDirectory(CuasPoseRT.TILE_CALIB_SUFFIX) == null) {
System.out.println("kernelTest(pose_corr): "+CuasPoseRT.TILE_CALIB_SUFFIX+
" not found - run a normal pose_test once (TILE-CALIB bootstrap), then re-run");
return;
}
PoseCorrExport.arm(dir_path, debugLevel);
try {
CuasPoseRT.testPoseSequence(clt_parameters, master_CLT, quadCLTs, debugLevel);
} finally {
if (!PoseCorrExport.captureDone()) {
System.out.println("kernelTest(pose_corr): capture did NOT complete - no case written");
}
PoseCorrExport.disarm();
}
break;
}
default:
System.out.println("kernelTest(): unknown kernel test '"+kernel+
"' - update CuasRT.kernelTest() to match CuasRtParameters.KERNEL_TESTS");
......@@ -482,6 +520,9 @@ public class CuasRT {
}
if (new File(dir_path, "results").isDirectory()) { // C++ results from a previous run of THIS case
System.out.println("kernelTest("+kernel+"): NOTE - results/ predate this export if the case just changed");
if ("pose_corr".equals(kernel)) { // per-iteration corr_td_it<k> results // By Claude on 07/13/2026
PoseCorrExport.compareResults(dir_path, debugLevel);
} else
AvgTdExport.compareResults(dir_path, debugLevel);
} else {
System.out.println("kernelTest("+kernel+"): now run (tile_processor_gpu): tests_bin/test_"+
......
/**
**
** PoseCorrExport.java - export a complete standalone test case for the lean
** pose-chain CHECKPOINT 1 (roadmap 3-C rung 1): the raw transform-domain
** correlation after correlate2D_inter, for ONE scene of the CUAS RT pose
** sequence. The case replays the per-LMA-iteration GPU chain
** calculate_tiles_offsets -> convert_direct (MB pair: SET + SUBTRACT) ->
** consolidation (index_consolidate/consolidate_oob/clt_average_sensors_list)
** -> correlate2D_inter vs the resident virtual-center TD (gpu_clt_ref cam 0)
** with everything data-driven: the 16 conditioned images exactly as uploaded,
** geometry-correction/correction-vector float arrays, aberration kernels, LPF
** constants, the per-iteration task streams exactly as handed to the GPU
** (pre-offsets) and as received by the consolidation (post-offsets), and the
** expected corr indices + raw TD correlation tiles (the oracle outputs).
** The C++ side is tile_processor_gpu/src/tests/test_pose_corr.cu (tol 0,
** NSight/cuda-gdb debuggable; newly exercises calculate_tiles_offsets and
** correlate2D_inter - convert_direct and the consolidation chain are already
** covered by test_convert_direct / test_avg_td_oob).
**
** CAPTURE MODEL: static armed-capture hooks called from the PRODUCTION lean
** chain (CuasPoseRT.leanMeasure / leanFitScene, CuasConditioning ingest).
** All hooks are no-ops unless arm() was called (the curt.kernel_test
** "pose_corr" dropdown entry, one Java run = one case). The capture binds to
** the first scene leanMeasure processes, records every LMA iteration of that
** scene, writes the case when the scene's fit completes, and the pose loop
** exits early (kernel tests are exclusive diagnostics - no products needed).
**
** Copyright (C) 2026 Elphel, Inc.
**
** -----------------------------------------------------------------------------**
**
** PoseCorrExport.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.
**
** This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
** -----------------------------------------------------------------------------**
**
** By Claude on 07/13/2026
*/
package com.elphel.imagej.gpu.testdata;
import java.io.IOException;
import java.util.ArrayList;
import com.elphel.imagej.gpu.GPUTileProcessor;
import com.elphel.imagej.gpu.GpuQuad;
import com.elphel.imagej.gpu.TpTask;
import com.elphel.imagej.cameras.CLTParameters;
import com.elphel.imagej.tileprocessor.GeometryCorrection;
import com.elphel.imagej.tileprocessor.ImageDtt;
import com.elphel.imagej.tileprocessor.QuadCLT;
public class PoseCorrExport {
// ---- armed-capture state (single-threaded RT diagnostic path; all static) ----
private static boolean armed = false;
private static boolean done = false; // case written - the pose loop may stop
private static String dir_path = null;
private static int debug = 0;
private static String bound_scene = null; // image name of the scene being captured
// conditioned images: the LAST upload offered while armed (overwritten per scene
// until the capture binds; conditionSceneToGpu runs BEFORE leanMeasure)
private static float [][] images = null; // [num_sens][img_h*img_w], exactly as H2D'd
private static String images_scene = null;
// once-per-case statics
private static float [] fgc, fcv; // geometry corr + correction vector floats
private static float [] lpf_data, lpf_corr, lpf_rb_corr, log_corr; // __constant__ payloads
private static float [] clt_ref0 = null; // resident center TD, cam-0 slice of gpu_clt_ref
private static float [][] fkernels, foffsets; // per-cam aberration kernels (GPU flatten)
private static float [] scales3; // correlate2D_inter color scales triplet
private static int sel_sensors, task_size, kernels_hor, kernels_vert;
private static double soft_margin, hard_margin;
private static int num_sens, colors, tilesx, tilesy, img_w, img_h;
private static boolean has_mb;
private static boolean statics_ok = false;
// per-LMA-iteration streams
private static final ArrayList<float []> tasks0_pre = new ArrayList<float []>(); // as handed to the GPU (pre-offsets)
private static final ArrayList<float []> tasks1_pre = new ArrayList<float []>();
private static final ArrayList<float []> tasks0_post = new ArrayList<float []>(); // as the consolidation received (post-offsets)
private static final ArrayList<float []> tasks1_post = new ArrayList<float []>();
private static final ArrayList<int []> corr_indices = new ArrayList<int []>(); // packed (tile<<8)|sensor, 0xff=sum
private static final ArrayList<float []> corr_td = new ArrayList<float []>(); // raw TD, [n_corr*256]
/** Arm the capture (CuasRT.kernelTest "pose_corr"). One case per arm. */
public static synchronized void arm(String dir, int debugLevel) {
disarm();
armed = true;
dir_path = dir;
debug = debugLevel;
}
/** Disarm + drop all captured state (safety net after the export run). */
public static synchronized void disarm() {
armed = false;
done = false;
dir_path = null;
bound_scene = null;
images = null;
images_scene = null;
clt_ref0 = null;
statics_ok = false;
resetIterations();
}
public static boolean isArmed() { return armed; }
/** True once the case has been written - the pose scene loop may break early. */
public static boolean captureDone() { return done; }
private static void resetIterations() {
tasks0_pre.clear(); tasks1_pre.clear();
tasks0_post.clear(); tasks1_post.clear();
corr_indices.clear(); corr_td.clear();
}
/**
* Hook (CuasConditioning.conditionSceneToGpuInner): the conditioned image data
* is uploaded straight to the GPU and never stored in the scene's QuadCLT, so
* the exporter takes its copy here - same channel combine + double->float cast
* as GpuQuad/GpuQuadJna.setBayerImages (bit-exact vs the upload).
*/
public static synchronized void offerImages(QuadCLT scene, double [][][] data) {
if (!armed || done) return;
if ((bound_scene != null) && !bound_scene.equals(scene.getImageName())) return; // capture already bound elsewhere
final float [][] f = new float [data.length][];
for (int ncam = 0; ncam < data.length; ncam++) {
final double [][] chans = data[ncam];
if ((chans == null) || (chans[0] == null)) return; // incomplete - cannot capture
f[ncam] = new float [chans[0].length];
for (int i = 0; i < f[ncam].length; i++) {
double s = chans[0][i];
for (int j = 1; j < chans.length; j++) s += chans[j][i];
f[ncam][i] = (float) s;
}
}
images = f;
images_scene = scene.getImageName();
}
/**
* Hook (CuasPoseRT.leanMeasure, per LMA iteration, BEFORE interCorrTD*): the
* task streams exactly as the GPU receives them - setTasks ORs 511 into the
* task word before flattening, replicated here so the capture precedes the
* in-place mutation. tp_tasks[1] == null = no-MB single-set mode.
*/
public static synchronized void iterTasksPre(
CLTParameters clt_parameters,
ImageDtt image_dtt,
GpuQuad gpuQuad,
QuadCLT scene,
TpTask [][] tp_tasks) {
if (!armed || done) return;
if (bound_scene == null) { // bind to this scene
if ((images_scene == null) || !images_scene.equals(scene.getImageName())) {
System.out.println("PoseCorrExport: no conditioned-image capture for scene "+
scene.getImageName()+" (curt.pose_raw ingest required) - export DISARMED");
disarm();
return;
}
bound_scene = scene.getImageName();
if (!captureStatics(clt_parameters, image_dtt, gpuQuad, scene, tp_tasks)) {
disarm();
return;
}
} else if (!bound_scene.equals(scene.getImageName())) {
return; // another scene (bound scene failed mid-fit and was reported) - keep what we have
}
tasks0_pre.add(flattenTasks(tp_tasks[0], true));
tasks1_pre.add((tp_tasks.length > 1) && (tp_tasks[1] != null) ? flattenTasks(tp_tasks[1], true) : null);
}
/**
* Hook (CuasPoseRT.leanMeasure, after interCorrTD* / before consolidateToSlot0):
* the SAME task arrays after updateTasks() replaced them with the GPU-computed
* offsets - the exact stream execConsolidateSensorsTD flattens (plain
* asFloatArray: disp_dist tail stays zero, matching production).
*/
public static synchronized void iterTasksPost(QuadCLT scene, TpTask [][] tp_tasks) {
if (!armed || done || (bound_scene == null) || !bound_scene.equals(scene.getImageName())) return;
tasks0_post.add(flattenTasks(tp_tasks[0], false));
tasks1_post.add((tp_tasks.length > 1) && (tp_tasks[1] != null) ? flattenTasks(tp_tasks[1], false) : null);
}
/**
* Hook (CuasPoseRT.leanMeasure, right after execCorr2D_inter_TD): the expected
* output of checkpoint 1 - packed corr indices + raw TD correlation tiles
* (BEFORE fat-zero normalization). First call also snapshots the resident
* center reference TD (gpu_clt_ref cam 0 - the only slice sensor mask 1 reads)
* and the correlate scales triplet.
*/
public static synchronized void iterCorr(GpuQuad gpuQuad, QuadCLT scene, double [] col_weights) {
if (!armed || done || (bound_scene == null) || !bound_scene.equals(scene.getImageName())) return;
if (clt_ref0 == null) {
clt_ref0 = gpuQuad.getCltData(true)[0];
sel_sensors = gpuQuad.getSensorMaskInter();
// same triplet reduction as GpuQuadJna.scaleTriplet (monochrome -> {1,0,0})
final int ncol = Math.min(colors, 3);
scales3 = new float [] {
(ncol > 1) ? (float) col_weights[0] : 1f,
(ncol > 1) ? (float) col_weights[1] : 0f,
(ncol > 2) ? (float) col_weights[2] : 0f};
}
corr_indices.add(gpuQuad.getCorrIndices());
corr_td.add(gpuQuad.getCorrTdData());
}
/**
* Hook (CuasPoseRT.leanFitScene, after the measure<->solve cycles succeeded):
* all iterations of the bound scene are captured - write the case.
*/
public static synchronized void sceneDone(QuadCLT scene) {
if (!armed || done || (bound_scene == null) || !bound_scene.equals(scene.getImageName())) return;
try {
writeCase();
done = true;
} catch (IOException e) {
System.out.println("PoseCorrExport: FAILED to write "+dir_path+": "+e.getMessage());
e.printStackTrace();
disarm();
}
}
/**
* Hook (CuasPoseRT.leanFitScene, fit failed / measurement null): drop the
* partial capture and let the next scene bind instead.
*/
public static synchronized void sceneFailed(QuadCLT scene) {
if (!armed || done || (bound_scene == null) || !bound_scene.equals(scene.getImageName())) return;
System.out.println("PoseCorrExport: scene "+bound_scene+" fit failed mid-capture - rebinding to the next scene");
bound_scene = null;
clt_ref0 = null;
statics_ok = false;
resetIterations();
}
// flatten a task stream in the exact GPU struct layout; or511 replicates the
// task-word mutation setTasks applies before its own flatten (pre-offsets
// stream); post-offsets streams are flattened plain, as execConsolidateSensorsTD
private static float [] flattenTasks(TpTask [] tasks, boolean or511) {
final int ts = tasks[0].getSize();
final float [] ftasks = new float [tasks.length * ts];
for (int i = 0; i < tasks.length; i++) {
tasks[i].asFloatArray(ftasks, i, false);
if (or511) {
ftasks[i * ts] = Float.intBitsToFloat(Float.floatToIntBits(ftasks[i * ts]) | 511);
}
}
return ftasks;
}
// once-per-case inputs that do not change across iterations
private static boolean captureStatics(
CLTParameters clt_parameters,
ImageDtt image_dtt,
GpuQuad gpuQuad,
QuadCLT scene,
TpTask [][] tp_tasks) {
final QuadCLT bound = gpuQuad.getQuadCLT(); // the scene the GPU geometry/kernels come from
if ((bound == null) || !bound.getImageName().equals(scene.getImageName())) {
System.out.println("PoseCorrExport: GPU-bound scene "+
((bound == null) ? "null" : bound.getImageName())+" != measured scene "+
scene.getImageName()+" - cannot capture");
return false;
}
num_sens = gpuQuad.num_cams;
colors = gpuQuad.num_colors;
img_w = gpuQuad.img_width;
img_h = gpuQuad.img_height;
tilesx = img_w / GPUTileProcessor.DTT_SIZE;
tilesy = img_h / GPUTileProcessor.DTT_SIZE;
kernels_hor = gpuQuad.kernels_hor;
kernels_vert = gpuQuad.kernels_vert;
task_size = tp_tasks[0][0].getSize();
has_mb = (tp_tasks.length > 1) && (tp_tasks[1] != null);
soft_margin = clt_parameters.curt.oob_soft;
hard_margin = clt_parameters.curt.oob_hard;
// geometry: same float arrays GpuQuad/GpuQuadJna upload (execSetTilesOffsets)
final GeometryCorrection gc = bound.getGeometryCorrection().expandSensors(GPUTileProcessor.MAX_NUM_CAMS);
fgc = gc.toFloatArray();
final double [] dcv = gc.getCorrVector().toFullRollArray();
fcv = new float [dcv.length];
for (int i = 0; i < dcv.length; i++) fcv[i] = (float) dcv[i];
// LPF/HPF __constant__ payloads: the exact arrays interCorrTD*/MotionBlur uploads
final boolean mono = scene.isMonochrome();
final float [][] lpf_rgb = new float [][] {
image_dtt.floatGetCltLpfFd(clt_parameters.gpu_sigma_r),
image_dtt.floatGetCltLpfFd(clt_parameters.gpu_sigma_b),
image_dtt.floatGetCltLpfFd(clt_parameters.gpu_sigma_g),
image_dtt.floatGetCltLpfFd(clt_parameters.gpu_sigma_m)};
lpf_data = new float [lpf_rgb.length * lpf_rgb[0].length]; // same flatten as setLpfRbg
for (int i = 0; i < lpf_rgb.length; i++) {
System.arraycopy(lpf_rgb[i], 0, lpf_data, i * lpf_rgb[0].length, lpf_rgb[i].length);
}
lpf_corr = image_dtt.floatGetCltLpfFd(clt_parameters.getGpuCorrSigma(mono));
lpf_rb_corr = image_dtt.floatGetCltLpfFd(mono ? 1.0 : clt_parameters.gpu_sigma_rb_corr);
log_corr = image_dtt.floatGetCltHpfFd(clt_parameters.getGpuCorrLoGSigma(mono));
// aberration kernels: the exact per-cam flatten of GpuQuad/GpuQuadJna.setConvolutionKernels
final double [][][][][][] clt_kernels = bound.getCLTKernels();
if (clt_kernels == null) {
System.out.println("PoseCorrExport: scene "+scene.getImageName()+" has no CLT kernels - cannot capture");
return false;
}
final int num_kernels = clt_kernels[0][0].length * clt_kernels[0][0][0].length * clt_kernels[0].length;
final int kernel_length = num_kernels * 4 * GPUTileProcessor.DTT_SIZE * GPUTileProcessor.DTT_SIZE;
fkernels = new float [num_sens][];
foffsets = new float [num_sens][];
for (int ncam = 0; ncam < num_sens; ncam++) {
final float [] fkernel = new float [kernel_length];
final float [] foffset = new float [num_kernels * 8];
int indx = 0;
for (int ty = 0; ty < clt_kernels[ncam][0].length; ty++)
for (int tx = 0; tx < clt_kernels[ncam][0][ty].length; tx++)
for (int col = 0; col < clt_kernels[ncam].length; col++)
for (int p = 0; p < 4; p++) {
final double [] pa = clt_kernels[ncam][col][ty][tx][p];
for (int i0 = 0; i0 < 64; i0++) {
final int i = ((i0 & 7) << 3) + ((i0 >> 3) & 7); // transpose, as production
fkernel[indx++] = (float) pa[i];
}
}
indx = 0;
for (int ty = 0; ty < clt_kernels[ncam][0].length; ty++)
for (int tx = 0; tx < clt_kernels[ncam][0][ty].length; tx++)
for (int col = 0; col < clt_kernels[ncam].length; col++) {
final double [] pa = clt_kernels[ncam][col][ty][tx][4];
for (int i = 0; i < pa.length; i++) foffset[indx++] = (float) pa[i];
}
fkernels[ncam] = fkernel;
foffsets[ncam] = foffset;
}
statics_ok = true;
return true;
}
private static void writeCase() throws IOException {
if (!statics_ok || (clt_ref0 == null) || tasks0_pre.isEmpty() ||
(tasks0_pre.size() != tasks0_post.size()) || (tasks0_pre.size() != corr_td.size())) {
throw new IOException("incomplete capture: iters pre/post/corr = "+
tasks0_pre.size()+"/"+tasks0_post.size()+"/"+corr_td.size());
}
final int num_iters = tasks0_pre.size();
final int chunk = 4 * GPUTileProcessor.DTT_SIZE * GPUTileProcessor.DTT_SIZE; // 256
try (TestDataWriter tdw = new TestDataWriter(dir_path, "pose_corr test case: scene "+bound_scene+
" ("+num_iters+" LMA iterations, checkpoint 1 = raw TD correlation after correlate2D_inter)")) {
tdw.prm("num_sensors", num_sens);
tdw.prm("colors", colors);
tdw.prm("tilesx", tilesx);
tdw.prm("tilesy", tilesy);
tdw.prm("img_width", img_w);
tdw.prm("img_height", img_h);
tdw.prm("kernels_hor", kernels_hor);
tdw.prm("kernels_vert", kernels_vert);
tdw.prm("task_size", task_size);
tdw.prm("num_iters", num_iters);
tdw.prm("has_mb", has_mb ? 1 : 0);
tdw.prm("sel_sensors", sel_sensors);
tdw.prm("soft_margin", soft_margin);
tdw.prm("hard_margin", hard_margin);
tdw.prm("scale0", scales3[0]);
tdw.prm("scale1", scales3[1]);
tdw.prm("scale2", scales3[2]);
final float [] images_flat = new float [num_sens * img_h * img_w];
for (int ncam = 0; ncam < num_sens; ncam++) {
System.arraycopy(images[ncam], 0, images_flat, ncam * img_h * img_w, img_h * img_w);
}
tdw.buf("images", images_flat, num_sens, img_h, img_w);
final float [] kern_flat = new float [num_sens * fkernels[0].length];
final float [] offs_flat = new float [num_sens * foffsets[0].length];
for (int ncam = 0; ncam < num_sens; ncam++) {
System.arraycopy(fkernels[ncam], 0, kern_flat, ncam * fkernels[0].length, fkernels[0].length);
System.arraycopy(foffsets[ncam], 0, offs_flat, ncam * foffsets[0].length, foffsets[0].length);
}
tdw.buf("kernels", kern_flat, num_sens, fkernels[0].length);
tdw.buf("kernel_offsets", offs_flat, num_sens, foffsets[0].length);
tdw.buf("geometry_correction", fgc, fgc.length);
tdw.buf("correction_vector", fcv, fcv.length);
tdw.buf("lpf_data", lpf_data, 4, lpf_data.length / 4);
tdw.buf("lpf_corr", lpf_corr, lpf_corr.length);
tdw.buf("lpf_rb_corr", lpf_rb_corr, lpf_rb_corr.length);
tdw.buf("log_corr", log_corr, log_corr.length);
tdw.buf("clt_ref_cam0", clt_ref0, tilesy, tilesx, colors, chunk);
for (int k = 0; k < num_iters; k++) {
tdw.buf("ftasks0_it"+k, tasks0_pre.get(k), tasks0_pre.get(k).length / task_size, task_size);
tdw.buf("expected_tasks0_it"+k, tasks0_post.get(k), tasks0_post.get(k).length / task_size, task_size);
if (has_mb) {
tdw.buf("ftasks1_it"+k, tasks1_pre.get(k), tasks1_pre.get(k).length / task_size, task_size);
tdw.buf("expected_tasks1_it"+k, tasks1_post.get(k), tasks1_post.get(k).length / task_size, task_size);
}
tdw.buf("expected_corr_indices_it"+k, corr_indices.get(k), corr_indices.get(k).length);
tdw.buf("expected_corr_td_it"+k, corr_td.get(k), corr_td.get(k).length / chunk, chunk);
}
if (debug > -3) {
System.out.println("PoseCorrExport: wrote "+tdw.getDir()+" (scene "+bound_scene+", "+
num_iters+" iterations, "+(tasks0_pre.get(0).length / task_size)+" tiles/set, "+
(corr_td.get(0).length / chunk)+" corr tiles/iteration, sel_sensors="+sel_sensors+")");
}
}
}
/**
* Import the C++ test results (results/corr_td_it<k>, already reordered by the
* test into the expected index order) and compare against the oracle reference
* (Java-side round trip; the C++ test already self-checks at tol 0).
* @return max abs difference over finite elements (NaN mismatch = +inf)
*/
public static double compareResults(String dir, int debugLevel) throws IOException {
final TestDataReader expected = new TestDataReader(dir);
final TestDataReader results = TestDataReader.results(dir);
double max_diff = 0.0;
for (int k = 0; expected.has("expected_corr_td_it"+k); k++) {
if (!results.has("corr_td_it"+k)) {
System.out.println("PoseCorrExport.compareResults: results/corr_td_it"+k+" missing");
continue;
}
final float [] r = results.getFloats("corr_td_it"+k);
final float [] e = expected.getFloats("expected_corr_td_it"+k);
double md = 0.0;
int nan_mismatch = 0;
final int n = Math.min(r.length, e.length);
for (int i = 0; i < n; i++) {
final boolean rn = Float.isNaN(r[i]), en = Float.isNaN(e[i]);
if (rn || en) {
if (rn != en) nan_mismatch++;
continue;
}
md = Math.max(md, Math.abs((double) r[i] - (double) e[i]));
}
if ((r.length != e.length) || (nan_mismatch > 0)) md = Double.POSITIVE_INFINITY;
if (debugLevel > -3) {
System.out.println("PoseCorrExport.compareResults: corr_td_it"+k+" max|diff|="+md+
", NaN-mismatch="+nan_mismatch);
}
max_diff = Math.max(max_diff, md);
}
return max_diff;
}
}
......@@ -37,7 +37,7 @@ public class CuasRtParameters {
public boolean rend_test = false; // RT full-render product (CuasRender.testRenderSequence): per scene raw /jp4/ -> conditionSceneToGpu -> virtual-grid render at BORROWED stored pose+ERS rates -> -CUAS-RT-RENDER hyperstack [s00..s15, merged][scenes], comparable to -CUAS-INDIVIDUAL/MERGED-CUAS-DBG. NON-exclusive: runs AFTER the pose stage when both are ON (the pose stage is part of the RT cycle - Andrey 07/06/2026). // By Claude on 07/05/2026
public boolean log_test = false; // LoG A/B with the RT render (CuasRender): also save -CUAS-RT-RENDER-LOG-ORACLE (product convolved with the EXISTING pre-DNN pixel LoG, LINEAR alpha=1) and -CUAS-RT-RENDER-LOG-FOLDED (same render, LoG folded into the GPU aberration kernels via CuasLogFold - no pixel convolution; originals restored). The aberration+LoG kernel-fold validation (RT-seed step 1b). // By Claude on 07/06/2026
public boolean log_ident = false; // LoG ISOLATION test with the RT render (CuasRender): render with IDENTITY kernels (-CUAS-RT-RENDER-ID, aberrations skipped), pixel-LoG it (-ID-LOG-ORACLE), and render with PURE-LoG kernels (-LOG-ONLY). If -LOG-ONLY vs -ID-LOG-ORACLE residual persists, the fold/L0 is wrong; if it collapses to the wrap floor, the aberration-kernel interaction was responsible (Andrey 07/06/2026). // By Claude on 07/06/2026
public static final String[] KERNEL_TESTS = {"none", "avg_td", "avg_td_oob"}; // GPU-kernel test registry: one dropdown entry per CUDA kernel under oracle validation (roadmap kernel ladder); add one name here + one case in CuasRT.kernelTest() per new kernel. // By Claude on 07/12/2026
public static final String[] KERNEL_TESTS = {"none", "avg_td", "avg_td_oob", "pose_corr"}; // GPU-kernel test registry: one dropdown entry per CUDA kernel under oracle validation (roadmap kernel ladder); add one name here + one case in CuasRT.kernelTest() per new kernel. pose_corr = lean pose-chain checkpoint 1 (roadmap 3-C rung 1): per-LMA-iteration task streams + resident center TD + expected raw TD correlation after correlate2D_inter, captured from the first fitted scene of a normal pose run (requires pose_lean + pose_raw, existing -POSE-RT-TILE-CALIB, pose_stored OFF). // By Claude on 07/12/2026, pose_corr 07/13/2026
public String kernel_test = KERNEL_TESTS[0]; // GPU-kernel test to run this pass (EXCLUSIVE diagnostic, ONE kernel per Java run - Andrey 07/12/2026): run the kernel's CPU oracle on live GPU data and export a self-describing testdata case for the standalone NSight-debuggable CUDA test (tile_processor_gpu tests_bin/test_<kernel> --data <case> --tol 0). "none" = normal operation. // By Claude on 07/12/2026
public boolean show_kernels = false; // show selected convolution kernel
public boolean dbg_fpixels = true; // save the ORACLE debug renders during ingest (prepareFpixels): -CUAS-INDIVIDUAL-CUAS-DBG (an EXTRA per-sensor render pass, ~11GB) + -CUAS-MERGED-CUAS-DBG (save only - the merged render is the production input regardless). Was hardcoded. Turn OFF to save the render time and disk once the RT chain (-CUAS-RT-RENDER) is the trusted full render. // By Claude on 07/05/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