1. 10 Jul, 2026 5 commits
    • Andrey Filippov's avatar
      CLAUDE: renderNadirSequence(): dump per-scene sampling grids as -NADIR-SCENE-PXPYD · 8157473b
      Andrey Filippov authored
      Save the actual scene-frame pXpYD fed to renderGPUFromDSI (after
      transformToScenePxPyD and optional undistort) as a pX/pY/D hyperstack at
      tile resolution, one frame per scene, suffix -NADIR-SCENE-PXPYD[-RECTILINEAR].
      For offline verification of the nadir render geometry: the grid should be
      near-uniform (tile centers + ERS displacement); any residual rotation
      proportional to scene roll would explain the halved in-plane rotation
      observed in the 2026-07-10 graveyard COLMAP test.
      Co-authored-by: 's avatarClaude <claude@elphel.com>
      8157473b
    • Andrey Filippov's avatar
      CLAUDE: Fix renderNadirSequence(): missing ref->scene transform of pXpYD · a3f665e2
      Andrey Filippov authored
      getNadirDisparityMaps() returns pXpYD in the SEGMENT REFERENCE frame
      (Cuas.transformFromVirtual() output), but renderNadirSequence() passed it
      to renderGPUFromDSI() with ZERO3 pose and the scene as its own reference,
      so each scene image was sampled at reference-frame coordinates. Each nadir
      frame thus carried an extra ref->scene terrain warp: effective camera
      poses vs the segment reference were ~doubled (ground translation 2x, small
      rotations ~2x), and the identity transform also canceled ERS compensation.
      On flat terrain the extra plane homography mimics a valid pose change, so
      COLMAP converged self-consistently but disagreed with the exported poses
      (Sim(3)/free monocular scale absorbs the 2x translation; rotations do not
      scale, explaining the large angular residuals, worst on high-rotation-rate
      sequences).
      
      Now transformToScenePxPyD() maps the reference-frame pXpYD into each
      scene's own frame (pose + ERS, same machinery as the verified
      frozen-terrain path) before rendering with ZERO3/self, where the identity
      pass is an exact pass-through. Output becomes a global-shutter virtual
      view at each scene's nominal pose. Also converts disparity to the scene
      frame for 16-sensor parallax compensation, and drops the defensive clone
      (the transform returns new arrays). Docs updated to state the frame
      conventions.
      Co-authored-by: 's avatarClaude <claude@elphel.com>
      a3f665e2
    • Andrey Filippov's avatar
    • Andrey Filippov's avatar
      CLAUDE: TestDataView: manifest-shaped ImageJ hyperstack viewer for test-case/results buffers · c9acd835
      Andrey Filippov authored
      Kills the manual raw-import papercut: dims come from manifest.txt, TD
      buffers (trailing 256) render as tile grids with presentCltData quadrant
      placement, per-tile planes as tilesX x tilesY images, generic fallback
      otherwise. viewCase/viewResults/showAll; verified shapes on the avg_td
      round-trip case (80x64 x16 TD grid, 5x4 counts).
      Co-Authored-By: 's avatarClaude Fable 5 <noreply@anthropic.com>
      c9acd835
    • Andrey Filippov's avatar
      CLAUDE: gpu.testdata package: standalone kernel-test export/import (oracle contract) · 1acb4341
      Andrey Filippov authored
      New package com.elphel.imagej.gpu.testdata implementing the Java side of
      tile_processor_gpu/src/tests/testdata_format.md:
      - TestDataWriter: raw little-endian arrays + manifest.txt (prm/buf records)
      - TestDataReader: parse manifest or C++ results back for ImageJ comparison
      - AvgTdExport: full clt_average_sensors test case (per-sensor TD from
        GpuQuad, CuasTD.consolidateSensorsTD expected outputs + counts, optional
        TpTask per-sensor XY for the OOB stage) + compareResults round-trip check
      
      Round-trip verified bit-exact (max|diff|=0): TestDataWriter -> CUDA
      test_avg_td on GPU -> TestDataReader.
      Co-Authored-By: 's avatarClaude Fable 5 <noreply@anthropic.com>
      1acb4341
  2. 08 Jul, 2026 5 commits
    • Andrey Filippov's avatar
      CLAUDE: robust average v2: NO NaN frame - all contributors + median/IQR rejection · 6a274e46
      Andrey Filippov authored
      Andrey's correction: the eroded average left a wide NaN frame (and all
      its descendants - EVAL diffs). The average virtual image must be defined
      wherever ANY scene provides (a single bad first/last row/col at most):
      combine ALL available contributors, let the count taper naturally near
      the edges, and reject junk providers per pixel as OUTLIERS vs the MEDIAN
      with an IQR-based robust sigma (immune to heavy contamination, unlike
      mean/sigma). Fewer than 4 providers - plain mean; zero - NaN. Geometric
      erosion (erodeDefined/erodedMasks/AVG_EDGE_ERODE) removed.
      
      Next (Andrey): per-TILE outlier rejection in the 16-sensor TD
      consolidation, anticipating the GPU migration (candidate indicator:
      CC(0,0) flux, possibly + quadrant energy).
      Co-Authored-By: 's avatarClaude Fable 5 <noreply@anthropic.com>
      6a274e46
    • Andrey Filippov's avatar
      CLAUDE: -CUAS-RT-RENDER-EVAL: log / log-diff / diff hyperstack (Andrey's spec) · 8726cc75
      Andrey Filippov authored
      New evaluation product with the RT render (replaces the manual
      run-UM-in-ImageJ workflow): merged component only, z = robust all-scenes
      average + each scene, t ordered for ImageJ auto-range: 1) pixel LoG (the
      pre-DNN kernel, LINEAR) of the image, 2) LoG of (scene - average),
      3) plain (scene - average); t2/t3 are 0 for the average frame. The
      average is the new robust one (edge-eroded + outlier-rejected),
      extracted into shared helpers (erodedMasks/robustAverage) used by both
      saveRenderHyper and the EVAL builder.
      Co-Authored-By: 's avatarClaude Fable 5 <noreply@anthropic.com>
      8726cc75
    • Andrey Filippov's avatar
      CLAUDE: RT-RENDER average slices: per-scene edge erosion + outlier rejection · c8cd84bd
      Andrey Filippov authored
      The temporal averages accumulated per-scene edge junk into stable margin
      artifacts (ROWCOL-04: worst columns x=12,20,28 = 8-px tile pitch -
      partially covered virtual tiles at the warped-coverage edge; the wobble
      sweeps which tiles are partial). For the two AVERAGE slices only
      (individual scenes stay liberal/untouched): each scene's contribution is
      box-eroded 24 px (3 tile pitches, AVG_EDGE_ERODE - clears the
      partial-tile ring) and outlier-rejected (3 sigma vs the first-pass mean
      when >= 4 providers, per Andrey's >1-non-NaN-provider rule); zero eroded
      providers -> honest NaN.
      Co-Authored-By: 's avatarClaude Fable 5 <noreply@anthropic.com>
      c8cd84bd
    • Andrey Filippov's avatar
      CLAUDE: RT rowcol: untrusted-line guard (inlier fraction) + physical cap · c0096034
      Andrey Filippov authored
      ROWCOL-04 localization: RT rows regression (18.0 vs 10.6 pre-rework) is
      CONTENT BIAS, zero in the sky and +10..22 counts in terrain rows - with
      dense high-contrast content nearly every |UM residual| exceeds max_abs,
      all pixels get the outlier weight and the weighted mean degenerates to
      the row's content mean (the small weights cancel), stamping bias bands.
      lineAverage now zeroes a line's correction when its inlier fraction is
      below rowcol_min_inlier (default 0.33 - content-dominated lines cannot
      yield a line-noise measurement) or when |estimate| > max_abs (line noise
      cannot physically exceed the gate scale). Both guards are no-ops with
      the gate off (max_abs INF - every pixel is an inlier).
      Co-Authored-By: 's avatarClaude Fable 5 <noreply@anthropic.com>
      c0096034
    • Andrey Filippov's avatar
      CLAUDE: ROWCOL frame-edge margin + physical line cap; RT: single high-pass (QC/ROWCOL-03 findings) · 8db6269b
      Andrey Filippov authored
      ROWCOL-03 QC FAIL (rms 52.9 unchanged by erosion) proved the junk edge
      columns are FULLY covered with DEFINED-BUT-WRONG back-projection values
      (left-edge convert_direct artifact class) - erosion cannot see them:
      - exclude a 16px frame-edge band from the content-excluded estimator by
        geometry (ROWCOL_BP_EDGE_MARGIN); those lines take the legacy fallback;
      - physical cap in the per-line merge: a content-excluded line value over
        max_abs cannot be line noise - use legacy (also blunts any future
        ROWCOL-01-class estimator failure).
      
      RT conditioning: profile HPF now applies ONLY when rowcol_um_sigma==0.
      Stacking it on the UM measurement double-high-passed the row-offset
      estimate ((1-G10)(1-G8)): removed fraction at period ~20 rows dropped
      96% -> 91%, residual mid-band line noise x2 - measured merged RT-RENDER
      rows metric 10.60 (ROWCOL-02, pre-rework) -> 18.73 (ROWCOL-03). Single
      sigma-10 high-pass removes strictly more noise at every period than the
      old sigma-8-only path and protects the LoG band at least as well.
      Co-Authored-By: 's avatarClaude Fable 5 <noreply@anthropic.com>
      8db6269b
  3. 07 Jul, 2026 13 commits
    • Andrey Filippov's avatar
      CLAUDE: disparity conditioning: fill small foreground clusters with background (Laplacian) · 66bd669a
      Andrey Filippov authored
      Where a small foreground object (the 3-tile roadside sign, disp ~22)
      stands in front of a distant background both disparities are legitimate,
      but for CUAS the background matters: a drone flying exactly behind the
      sign must stay detectable. conditionDisparityCuas now detects connected
      clusters of local near-outliers (disparity above the local 7x7 median by
      curt.disp_fg_over, default 2 px) up to curt.disp_fg_tiles (default 8,
      0 = off) tiles and re-fills them with zero-curvature background
      disparity (TileProcessor.fillNaNs Laplacian relaxation). All three RT
      call sites (render, pose, appropriation) share the one conditioned
      source as before.
      Co-Authored-By: 's avatarClaude Fable 5 <noreply@anthropic.com>
      66bd669a
    • Andrey Filippov's avatar
      CLAUDE: ROWCOL edge erosion + RT rowcol rework (order/UM/gate) + QC harness · 275f812b
      Andrey Filippov authored
      1. calculateRowColBackProject: ERODE the coverage mask (blurred mask >=
         ROWCOL_BP_ERODE=0.95 ~ 16px inside at sigma 10) before the masked UM
         and averaging - the back-projection is unreliable near its own
         boundary and boundary garbage dominated the partially-covered EDGE
         columns in v013-ROWCOL-02 (per-x maxima ~1000 for x<14, interior
         clean at 13-39 counts). Excluded edge lines drop below MIN_COVER and
         take the legacy per-line fallback.
      
      2. RT conditioning row/col (problem 2, borrowed from the oracle path,
         /jp4/ input unchanged):
         - REORDER: row/col now AFTER photometric+FPN. Measuring on raw
           captured the FPN's static line structure (rms ~6/12 counts) which
           the later FPN subtraction removed AGAIN - negative FPN line
           structure was injected into the output.
         - UM before averaging (new curt.rowcol_um_sigma, default 10, 0=off):
           profiles measured on an unsharp-masked copy, subtracted from the
           original - content no longer biases the averages and the max_abs
           gate acts on residual counts.
         - rowcol_max_abs default 50 (calibrated counts; configs saved before
           this change carry 0 = no gate - set 50 to enable).
      
      3. CuasQC (Andrey's regression-protection design): per-product health
         statistics with sequence-independent pass bands, appended per run to
         <center>-QC.json; FAIL prints loudly, never aborts. Increment 1:
         ROWCOL vectors (rms<=25, scene-max p95<=500 - both of today's
         defects would have failed) + FPN (rms, row/col line-structure
         energy), hooked after calculation AND reuse in CorrectionFPN.
      Co-Authored-By: 's avatarClaude Fable 5 <noreply@anthropic.com>
      275f812b
    • Andrey Filippov's avatar
      CLAUDE: fix ROWCOL back-project baseline mismatch (v013-ROWCOL-01 junk) · 947c199c
      Andrey Filippov authored
      The first calculateRowColBackProject mixed two baselines in one image:
      content-excluded residual (~0, regression baseline) inside the
      back-projection coverage, full raw-level image-FPN outside it. Whole
      lines fell outside the max_abs gate and the outlier-weighted average
      degenerated to the content mean: vectors reached ~2300 counts (legacy
      ~340) and stamped junk bands at the coverage-window margins into all
      per-sensor images (v013-ROWCOL-01 MERGED-DBG slices 141/408 etc).
      
      Now the two baselines never mix: masked UM (blur(value*mask)/blur(mask))
      high-passes the residual within coverage only; NaN-aware line averages
      use covered pixels only (getLineAvgNaN); lines with under 10% coverage
      (ROWCOL_BP_MIN_COVER) fall back to the pure legacy UM+gating estimate
      per line (mergeLineProfiles). Console reports the line-fallback share.
      Co-Authored-By: 's avatarClaude Fable 5 <noreply@anthropic.com>
      947c199c
    • Andrey Filippov's avatar
      CLAUDE: fix MCP dialog values silently discarded on submit · b70a9f6b
      Andrey Filippov authored
      The registry entry is cleared by submitCurrent() (and again by
      showDialog()) BEFORE applyCltDialog() runs the getNext*() reads, so
      getValueOrDefault() - which looked the session up via the registry -
      always saw null and fell back to every field's default: any value set
      via POST /mcp/dialog/values was silently lost and OK re-applied the
      loaded config. The dialog now keeps its OWN session reference for the
      reads and the await; the registry cleanup clears only its own session
      (covers the timeout path without clobbering a newer dialog).
      Co-Authored-By: 's avatarClaude Fable 5 <noreply@anthropic.com>
      b70a9f6b
    • Andrey Filippov's avatar
      CLAUDE: remove cuas_calc_fpn/cuas_calc_rowcol - file existence is THE recalc trigger · b056e1a5
      Andrey Filippov authored
      Delete-the-file rule (Andrey): FPN and row/col noise recalculate ONLY
      when <center>-FPN.tiff / <center>-ROWCOL.tiff are missing in the model
      directory. The two force-recalc checkboxes are removed from the dialog
      and the config (stale _cuas_calc_* keys in old corr-xml are ignored on
      load). Reuse printlns name the exact file to delete; the dialog shows
      addMessage() notes in place of the removed checkboxes.
      Co-Authored-By: 's avatarClaude Fable 5 <noreply@anthropic.com>
      b056e1a5
    • Andrey Filippov's avatar
      CLAUDE: Row/Col correction (RT step 2): curt rowcol_* dialog params +... · 3cc0997b
      Andrey Filippov authored
      CLAUDE: Row/Col correction (RT step 2): curt rowcol_* dialog params + content-excluded noise calculation
      
      2a: CuasConditioning rowcol_* Config now dialog-driven (curt.rowcol_en/
      rowcol_hpf/rowcol_hpf_sigma/rowcol_max_abs/rowcol_outlier, "CUAS RT" tab)
      via CuasConditioning.configFromCurt(); RT Row/Col is optional/bypassable
      (turn rowcol_en OFF when the FLIR internal correction is verified OK).
      Defaults reproduce the previous hardcoded Config bit-exactly
      (rowcol_max_abs 0 = the old INFINITY no-gate).
      
      2b: new CorrectionFPN.calculateRowColBackProject() (gated by NEW
      imp.cuas_backprop_rowcol, default ON): subtract the back-projected
      (pose-transformed, photometric-matched) virtual center from each scene
      BEFORE the UM + line averaging, so bright content no longer biases the
      row/col averages (the dark horizontal/vertical line artifact). Falls
      back to (image - FPN) where the back-projection is undefined; serial
      scene loop (GPU back-projection per scene) - borrowed/non-RT path only.
      Co-Authored-By: 's avatarClaude Fable 5 <noreply@anthropic.com>
      3cc0997b
    • Andrey Filippov's avatar
    • Andrey Filippov's avatar
      CLAUDE: test 7 - windowed-LoG TD product (correlation machinery): broadband... · 284f6ae1
      Andrey Filippov authored
      CLAUDE: test 7 - windowed-LoG TD product (correlation machinery): broadband exact, crease bands UNCHANGED
      
      Andrey's separate-LoG proposal prototyped on the CPU oracle: LoG as a windowed
      16x16 DATA tile (taps pre-divided by the sine window AND cosAtten so the
      effective filter is the exact flux-normalized LoG), full 16-mult quad product
      per tile (GPU correlateAccumulateTiles table == convolve_tile(conj(data),k) -
      verified term-by-term), dttt_iie back (mode-TRANSPOSED = GPU dttii_2d),
      corr_unfold_tile, windowed OLA with sum(w^2) normalization.
      
      RESULT: impulse fidelity 2.0e-3 (no aberration-kernel cross-term - a separate
      pass), but the ramp crease bands are IDENTICAL to the per-bin fold (+/-0.546):
      the DTT symmetric-extension reflection is only attenuated by the window, not
      removed - the crease is BASIS-INHERENT to any single-tile TD product. The
      per-bin fold is the degenerate (symmetric-kernel) case of this same product.
      Band-free LoG requires pixel-domain convolution on the assembled image.
      
      Calibrated conventions along the way (documented in code): correlation product
      table, dttii mode transpose, corr_unfold patch mapping (+1 offset, no flip),
      global gain 4.000, window and cos tap precompensation.
      Co-Authored-By: 's avatarClaude Fable 5 <noreply@anthropic.com>
      284f6ae1
    • Andrey Filippov's avatar
      CLAUDE: test 6 extension - crease bands per kernel type (Andrey's symmetry argument quantified) · da5c157f
      Andrey Filippov authored
      Same ramp, same chain: identity 0.000; aberration-like kernel 0.624 counts
      (band/output 0.18% - present in every render, invisible under content);
      pure LoG 0.547 (176% of its near-zero output); folded 0.682. The aberration
      kernels DO band identically in absolute counts - the LoG only unmasks them
      by removing the content. Corollary: the CURRENT pixel-LoG conditioning also
      reveals the render's own crease bands (the oracle product bands too, with a
      different pattern).
      Co-Authored-By: 's avatarClaude Fable 5 <noreply@anthropic.com>
      da5c157f
    • Andrey Filippov's avatar
      CLAUDE: test 6 - sky-band root cause reproduced: symmetric-extension crease... · b70a65c5
      Andrey Filippov authored
      CLAUDE: test 6 - sky-band root cause reproduced: symmetric-extension crease response to LINEAR content
      
      Constant -> exactly 0; ramp 5/row -> 8-row-periodic band pattern
      (0,-0.008,-0.297,-0.547,+0.547,+0.297,+0.008,0) = the LOG-01 run's measured sky
      bands scaled by the gradient (2.5/row -> -0.14,-0.30,+0.35,+0.16). Pure double
      CPU chain -> not float32, not GPU, not a kernel coefficient: pixel LoG of a ramp
      is 0 (odd symmetry) but the per-tile symmetric convolution sees the mirrored
      extension's slope crease at tile boundaries. Inherent to any per-bin TD multiply
      (present under aberration kernels too, masked by content - Andrey's LPF-era
      artifact class, confirmed in substance).
      Co-Authored-By: 's avatarClaude Fable 5 <noreply@anthropic.com>
      b70a65c5
    • Andrey Filippov's avatar
      CLAUDE: LoG isolation test (curt.log_ident) - identity vs pure-LoG kernels, aberrations skipped · 7b6d0284
      Andrey Filippov authored
      Andrey's discriminator for the fold-vs-kernel-interaction question: with
      rend_test + new 'RT render LoG isolation (identity krnl)' ON, saves
      -CUAS-RT-RENDER-ID (identity kernels: q0=1, q1..3=0, CltExtra=0 - aberration
      correction AND fractional offsets skipped), -ID-LOG-ORACLE (pixel LoG of -ID),
      and -LOG-ONLY (pure-LoG kernels, CuasLogFold.uniformKernels(log_td)).
      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 is responsible.
      Refactor: pixel-LoG application extracted to applyPixelLog(); log_test and
      log_ident share the kernel fetch/restore (finally).
      
      Evidence so far (offline, this evening's run): per-sensor high-bin tilt varies
      s07 +1.3% .. s01 +2.4% with identical scene content => kernel-dependent;
      per-quadrant deviation maps identical (no single-coefficient signature);
      kx=0 column regionally incoherent (row-structure channel, separate issue).
      Co-Authored-By: 's avatarClaude Fable 5 <noreply@anthropic.com>
      7b6d0284
    • Andrey Filippov's avatar
      CLAUDE: RT-RENDER* hyperstacks organized like -INDIVIDUAL-CUAS-DBG (merged first, 2 average frames) · c9b0452a
      Andrey Filippov authored
      t (channels): merged FIRST then s00..s15; z (frames): 'average' (all scenes) +
      'average-f:l' (middle-half, renderSceneSequence default range) then scenes -
      same dimensions as -CUAS-INDIVIDUAL-CUAS-DBG (17 x 499 x H x W) so ImageJ can
      subtract the stacks directly. NaN-tolerant per-pixel averages. Applies to all
      three products (RT-RENDER, -LOG-ORACLE, -LOG-FOLDED). Per Andrey 07/06/2026.
      Co-Authored-By: 's avatarClaude Fable 5 <noreply@anthropic.com>
      c9b0452a
    • Andrey Filippov's avatar
      CLAUDE: RT-seed step 1b wiring - LoG A/B products in the RT render (curt.log_test) · 0fe8f5c1
      Andrey Filippov authored
      With 'CUAS RT render' + new 'RT render LoG A/B (folded kernels)' checkboxes ON,
      testRenderSequence saves two more hyperstacks comparable to -CUAS-RT-RENDER:
      - -CUAS-RT-RENDER-LOG-ORACLE: the product convolved with the EXISTING pre-DNN
        pixel-domain LoG (same CuasRTUtils.convolve2DLReLU instance construction as
        CuasDetectRT.detectTargets, LINEAR alpha=1);
      - -CUAS-RT-RENDER-LOG-FOLDED: the same render chain re-run with the LoG folded
        into the GPU aberration kernels (CuasLogFold.getLogTd tap-precompensated +
        foldKernels, setConvolutionKernels force re-upload) - NO pixel convolution;
        original kernels restored in a finally block.
      Render loop extracted verbatim into renderPass(), hyperstack save into
      saveRenderHyper() so both passes share the exact chain. Offline verdict:
      attic/CLAUDE/compare_cuda_versions.py --only LOG-
      Co-Authored-By: 's avatarClaude Fable 5 <noreply@anthropic.com>
      0fe8f5c1
  4. 06 Jul, 2026 10 commits
    • Andrey Filippov's avatar
      CLAUDE: CuasLogFold - LoG-into-kernel fold + synthetic impulse/grating... · cfc93456
      Andrey Filippov authored
      CLAUDE: CuasLogFold - LoG-into-kernel fold + synthetic impulse/grating self-test (roadmap RT-seed step 1)
      
      Findings (all validated to stated precision by the self-test, exit 0):
      - Per-bin TD quad-multiply == pixel convolution with cos(pi*dx/16)*cos(pi*dy/16)
        ATTENUATED taps (matches norm_sym_weights in calculateCLTKernel; shift-tap
        error exactly 1-cos(pi/16) before compensation). getLogTd() therefore
        pre-compensates LoG taps by 1/cosAtten so the EFFECTIVE filter is the true
        pixel LoG (machine-exact at non-bin gratings).
      - Residual floors: symmetric-extension wrap ~0.5-2.5% (inherent to the lapped
        structure); folded-vs-pixel-LoG A/B cross-term ~7% on a pessimistic spread
        kernel (test 5 - predicts the full-run A/B floor).
      - clt_symmetrize_kernel quadrant-3 (AA) sign pattern looks like a latent
        copy-paste of the AS expression: pure-AA kernel test - math sign 2.6e-3,
        code sign 1.9e-1. Production kernel files likely carry the wrong AA sign.
      Co-Authored-By: 's avatarClaude Fable 5 <noreply@anthropic.com>
      cfc93456
    • Andrey Filippov's avatar
      CLAUDE: FPN iteration - composite rebuilt center over original (NaN-hole fix) · 953f5efd
      Andrey Filippov authored
      POSE-13 forensics (from files, run 02:18-02:24): the iteration executed but dG
      came out defined on only 5% of the frame (right-edge strip + terrain patches),
      so fpn2 = fpn1 over 95% - the ghost survived. Chain: rebuild accumulation was
      healthy (494/494 scenes, ~75% per scene, ~85% union) but a single-sequence
      new-average center keeps NaN holes in the weak sky (no parent cumulative, no
      fillNewGaps), and convert_direct of a NaN-holed center image NaN-poisons
      nearly all back-projected tiles (task inversion + overlap spread).
      
      Fix: composite before the second back-propagation - keep rebuilt values where
      defined, fall back to the original center in the holes. There dG measures the
      pass-1 residual (~0), so it degrades to "no correction where not rebuilt"
      instead of NaN. Adds a coverage printout and a cuas_debug save of the
      composited center (-FPN-CENTER2-ITERn) so the next run is file-verifiable.
      Co-Authored-By: 's avatarClaude Fable 5 <noreply@anthropic.com>
      953f5efd
    • Andrey Filippov's avatar
      CLAUDE: FPN second back-propagation iteration (imp.cuas_fpn_iters, default 1 = old behavior) · 3b1049d6
      Andrey Filippov authored
      FPN = mean(scene - backProject(center)) is a fixed point that preserves the
      center's own content residual (the rotating ghost): recomputing against the
      same contaminated center reproduces the same ghost regardless of rot_period
      or photometric vintage (POSE-12 confirmed: 43.1/92.0 unchanged).
      
      Iterate per Andrey's design (internal handoff 2026-07-06):
        1. subtract fpn_k from scenes in memory (setApplyFPN, delta-aware)
        2. rebuild the center IMAGE from the corrected scenes over integer
           rotations - new Cuas.rebuildCenterImageInMemory(): new-average only
           (no parent cumulative fold), NOTHING persisted, center_clt and
           image_center untouched (the .cuas cumulative is upstream state)
        3. back-propagate against the rebuilt center image (swapped in/restored
           around the measurement) -> residual dG = the ghost
        4. fpn_{k+1} = fpn_k + dG in NEW arrays (scenes hold fpn_k references as
           applied-FPN for delta re-apply); prints |dG| RMS/max per iteration
        5. cuas_debug saves per-iteration -FPN-DG-ITERn
      
      Gated by new dialog field "FPN back-propagation iterations" next to
      "Rotation period"; default 1 keeps the POSE-12 baseline bit-reproducible.
      Verify by RT-RENDER horizon/sky pk-pk (43.1/92.0 -> temporal-noise floor).
      
      Data path verified: applyFPN sets hasNewImageData, honored by both
      GpuQuad.setBayerImages and GpuQuadJna, so corrected pixels reach the GPU
      for the rebuild and the second back-propagation.
      Co-Authored-By: 's avatarClaude Fable 5 <noreply@anthropic.com>
      3b1049d6
    • Andrey Filippov's avatar
      CLAUDE: retire curt.pose_recalc - delete-the-file is THE force method; reuse printlns say so · 53fe5f5c
      Andrey Filippov authored
      Andrey's clutter rule: calibration force-rerun = erase the file, not a
      config checkbox. pose_recalc removed everywhere (field/dialog/properties/
      copy; old configs' leftover key is ignored harmlessly), poseSequencePass
      lost its allow_recalc parameter. Every file-gated reuse now prints the
      delete-to-recalculate hint:
      - TILE-CALIB: 'reusing ... delete <name>-POSE-RT-TILE-CALIB.tiff to recalculate'
      - RT-STATE dc_center: 'delete <name>-RT-STATE.json (or its dc_center field)'
      - FPN (CorrectionFPN): 'reusing saved <name>-FPN.tiff - delete it to
        recalculate (verify Rotation period first - a mismatch bakes an
        azimuth-sector content ghost into the FPN)'
      Co-Authored-By: 's avatarClaude Fable 5 <noreply@anthropic.com>
      53fe5f5c
    • Andrey Filippov's avatar
      CLAUDE: pose stage non-exclusive with the render product (pose = part of the RT cycle) · 2c6751b8
      Andrey Filippov authored
      Andrey's ruling: the pose stage normally ALWAYS runs (it is part of the RT
      cycle, not a mutually-exclusive test) - the last run had rend_test ON which
      silently skipped the pose stage (old precedence) and left TILE-CALIB unbuilt.
      Now: pose_test runs FIRST, rend_test render follows when also ON, cond_test
      stays standalone (only when nothing else ran). Dialog/tooltips updated
      ('CUAS RT render (full product)').
      
      Verified meanwhile (POSE-11 renders): frozen per-sequence DC cut the
      RT-RENDER horizon wobble 234 -> 42.4 counts pk-pk (5.5x), exactly to the
      oracle DBG floor - the residual at 1-rev period is the shared FPN ghost;
      FPN.tiff still Jun-1 vintage because cuas_calc_fpn was OFF (the '(Re)calculate
      FPN' checkbox) - the rot_period=164.9 recompute has not actually run yet.
      Co-Authored-By: 's avatarClaude Fable 5 <noreply@anthropic.com>
      2c6751b8
    • Andrey Filippov's avatar
      CLAUDE: per-SEQUENCE zero-DC level - fix rotation-synced DC flicker (the rotating ghost) · 689efd90
      Andrey Filippov authored
      POSE-10 forensics: RT-RENDER merged horizon pk-pk 234 counts (sky 187) with
      the dominant period 165.7 scenes = EXACTLY one camera revolution (21.83 rpm
      @ 60 fps -> 164.9); POSE-RT-COMPOSITE real slices the same. Cause: the
      per-SCENE zero-DC sky median (Config.dc_level=NaN auto) oscillates with
      azimuth as warm terrain sweeps through the sky half; downstream temporal
      averages convert the wobble into content-correlated residuals - the faint
      rotating ghost ('real image treated as FPN'). Oracle MERGED-DBG shows a
      separate, 3-6x smaller wobble at the same period (pre-existing, prepared
      path - not addressed here).
      
      Fix (Andrey's rule: one constant per sequence, from the average scene):
      - CuasRT.sequenceDcLevel(): manual curt.dc_center override when nonzero,
        else the RT-STATE value derived by appropriateCenter() from the borrowed
        center (= the sequence average) - scenes and center shift by the
        IDENTICAL constant.
      - CuasPoseRT (A2 ingest) + CuasRender (render test) now pass a Config with
        that dc_level instead of null; NaN fallback (per-scene) kept but warns
        loudly. Config.dc_level doc updated.
      Co-Authored-By: 's avatarClaude Fable 5 <noreply@anthropic.com>
      689efd90
    • Andrey Filippov's avatar
      CLAUDE: curt.dbg_fpixels - parameterize the oracle DBG renders (was hardcoded, ~11GB/run) · 424ac6f9
      Andrey Filippov authored
      New checkbox 'Save oracle DBG renders (11GB)' (default ON - current
      behavior preserved): gates the EXTRA per-sensor -CUAS-INDIVIDUAL-CUAS-DBG
      render pass and the -CUAS-MERGED-CUAS-DBG save in CuasRanging.prepareFpixels
      (the merged render itself stays - it is the production imp_targets input).
      Turn OFF to save render time and disk once -CUAS-RT-RENDER (rend_test, the
      RT-chain full render) is the trusted full product.
      Also refreshed the stale dc_center GUI tooltip (RT-STATE.json semantics).
      Co-Authored-By: 's avatarClaude Fable 5 <noreply@anthropic.com>
      424ac6f9
    • Andrey Filippov's avatar
      CLAUDE: TILE-CALIB bootstrap + per-sequence RT-STATE.json + INTERFRAME poses-only · af6ef9ec
      Andrey Filippov authored
      Missing-file rule (Andrey): calibrations are FILE-gated; missing -> rebuild
      (slow first run), present -> reuse, erase -> force. Config keeps parameters
      only.
      
      - TILE-CALIB bootstrap: testPoseSequence = wrapper over poseSequencePass;
        missing -POSE-RT-TILE-CALIB (or pose_recalc ON) -> FULL pass generates and
        saves it, then AUTOMATICALLY re-runs filtered (~150 cluster tiles) - first
        run on a v013_min copy ends in the warmed-system state, per sequence
        (mixed-state batches self-serve). Bootstrap re-run ignores pose_recalc;
        pose_full still never reads/writes the calibration.
      - NEW CuasRtState (-RT-STATE.json, Gson): per-sequence RT-only persistent
        variables in the CENTER model dir; global params stay with quadCLT_main /
        corr-xml (GUI save + compare/combine). Data-pure (paths, no QuadCLT),
        forward-compatible, failure-safe load. First tenant: dc_center - AUTO now
        reuses/derives-and-stores per sequence; the corr-xml config is never
        mutated (nonzero = manual override).
      - INTERFRAME is QuadCLT-workflow/POSES ONLY (Andrey): rtPhotometricCalibration
        no longer writes the ref INTERFRAME; photometric persists via the explicit
        corr-xml save after a calib-ON run (quadCLT_main carries the values).
      Co-Authored-By: 's avatarClaude Fable 5 <noreply@anthropic.com>
      af6ef9ec
    • Andrey Filippov's avatar
      CLAUDE: CuasRT - single RT entry point + borrow-boundary appropriation + calib fixes · 22d49ee1
      Andrey Filippov authored
      - NEW cuas/rt/CuasRT.java: the ENTIRE CUAS RT mode moved out of
        OpticalFlow.buildSeries (curt_en branch) into one readable top method:
        1. INITIALIZATION (ingest, photometric calibration, center appropriation)
        2. DIAGNOSTICS (rend/pose/cond tests - exclusive, never grow the top)
        3. PRODUCTION (detection; the MVP per-scene jp4 loop will replace the
           bulk oracle ingest without changing this structure).
      - Borrow boundary (Andrey ruling): all transforms of borrowed QuadCLT data
        happen at CuasRT initialization ONLY. appropriateCenter() zeroes the
        center CLT IN MEMORY (setCenterClt; -CLT-UPDATED is saved upstream, so
        the zeroed copy never persists) - the pose test drops its per-run GPU
        appropriation and uploads an already-zeroed reference. The pure math is
        CuasConditioning.zeroDcClt(fclt, pattern, level) - knows no QuadCLT.
      - FIX (POSE-09 silent no-op): the DC-pattern uniform image must be set on
        gpuQuad.getQuadCLT() - what both backends actually read (GpuQuadJna
        setBayerImages(center=true), JCuda base, CorrectionFPN recipe) - not on
        center_CLT; plus an all-zero-pattern guard (warn loudly, never fake-offset).
      - FIX (POSE-09 1470-count per-sensor sky spread vs 17 calibrated):
        rtPhotometricCalibration applies the fit to the ALREADY-SPAWNED per-scene
        instances too (created by ingest before the calib step). The recalibrated
        photometric persists only via ref INTERFRAME + a config save after a
        calib-ON run - all current configs still carry the stale values.
      Co-Authored-By: 's avatarClaude Fable 5 <noreply@anthropic.com>
      22d49ee1
    • Andrey Filippov's avatar
      CLAUDE: appropriateCenterDC AUTO level from the reference buffer render · 194d87a8
      Andrey Filippov authored
      getCenterAverage() is null in the pose flow, so the auto DC level silently
      no-op'd (composite virtual sky stayed at -4511). Derive the level from the
      reference buffer render itself - perSensorImagesFromTD(gpuQuad, true)[0]
      sky-half median - i.e. the exact data being zeroed, right after
      setReferenceGPU uploads it. Failure-safe warnings preserved; explicit
      curt.dc_center and adoption/persistence semantics unchanged.
      
      Verify: pose run -> console 'AUTO center DC level', composite virtual sky ~0.
      Co-Authored-By: 's avatarClaude Fable 5 <noreply@anthropic.com>
      194d87a8
  5. 05 Jul, 2026 7 commits
    • Andrey Filippov's avatar
      CLAUDE: RT zero-DC conditioning + decoupled fat zero + borrowed-center DC appropriation · 024b3bda
      Andrey Filippov authored
      Three coupled improvements from tonight's design discussion with Andrey:
      
      1. ZERO-DC in CuasConditioning (Config default ON): LWIR microbolometers are
         ADDITIVE (fixed tau=8ms exposure, F#~1 for NETD, ~100 counts/K) - day/night
         and seasons SHIFT the level, never scale it; the MCLT has no clean DC/AC
         separation, so a large DC (current ~-3500 counts under ~300 counts AC)
         spreads over many TD points and costs float32 mantissa bits. Subtract ONE
         common constant from all 16 sensors (pure shift, never rescale). AUTO level
         = MEDIAN OF THE TOP (SKY) HALF, robust to the snowy-ground-vs-summer-
         calibration mismatch and to targets.
      
      2. RT-DECOUPLED FAT ZERO (curt.fz_inter=10000, curt.fz_intra=2000 reserved):
         the lean code erroneously used the INTRA getGpuFatZero (2000) instead of
         the INTER value - Andrey's FZ sweep (200/1000/3000/10000: corrRMS
         .54/.39/.37/.31, roll bias +.80/+.62/+.50/+.39) independently rediscovered
         the oracle's INTER operating point (10000). RT now owns its FZ pair, no
         AUX switch; legacy gpu_fatz*/FOPEN configs untouched. Sweep recorded as
         characterization: FZ tolerant over a broad range; revisit only together
         with the argmax method redesign (sharp peaks starve the centroid).
      
      3. BORROWED-CENTER APPROPRIATION (curt.dc_center, 0=auto): the restored
         CenterClt reflects its own footage vintage - offset it ONCE at the borrow
         boundary (firewall: never re-compensate downstream) to match the zero-sky
         scenes. Auto level = sky-half median of the borrowed center average,
         ADOPTED into curt.dc_center (persisted with the config - borrowable later
         like the photometric calibration). Mechanics: DC pattern = CLT of a
         uniform image via the existing back-propagation convert (setImageCenter +
         no_kernels + use_center_image, the CorrectionFPN recipe); failure-safe
         (any missing piece leaves the reference unchanged, with a warning).
      
      Roadmap recorded: per-pixel offset (recalibrated per-sensor + FPN) and
      per-pixel scale maps to the GPU; the average stored in TD AFTER LoG
      (aberration+LoG combined kernels) for fpixels subtraction - possibly for
      poses too; center-average window length trades reference stability vs
      extremely-slow-target detectability.
      
      Verified: mvn -DskipTests clean package OK.
      Co-Authored-By: 's avatarClaude Fable 5 <noreply@anthropic.com>
      024b3bda
    • Andrey Filippov's avatar
      CLAUDE: pose test rebased on the certified CuasRender chain (lean v2: MB + all-defined disparity) · 0a8d2415
      Andrey Filippov authored
      Per Andrey: restore the pose test using the new (certified) data.
      
      - Disparity: ONE source for all of RT - the plain DSI slice through
        CuasRender.conditionDisparityCuas (defined for ALL tiles, sky=infinity,
        negatives clamped). The use_lma_dsi gate removed from the pose test (the
        LMA slice is NaN over the sky and silently shrank the usable set).
      - leanMeasure/leanFitScene: MB compensation (lean v2) - the same certified
        machinery as CuasRender: setInterTasksMotionBlur two task sets + the
        erase-first interCorrTDMotionBlur double convert (undefined tiles NaN for
        the consolidation), gated by imp.mb_en, vectors from the existing per-scene
        getMotionBlur block (previously computed but unused by lean).
      - pose_stored branch gets the same MB treatment.
      
      Expected in the next lean run (LMA, 150-cluster selection, mb_en ON):
      az/tilt wobble (v*tau, 0.5 px rotating) collapses toward the MBEN oracle
      (0.029/0.021 mrad), corr RMS toward ~0.08; roll bias = the remaining open
      question (FPN-mask lead).
      
      Verified: mvn -DskipTests clean package OK.
      Co-Authored-By: 's avatarClaude Fable 5 <noreply@anthropic.com>
      0a8d2415
    • Andrey Filippov's avatar
      CLAUDE: CuasRender - disparity defined for ALL tiles (conditionDisparityCuas) · e4e65e23
      Andrey Filippov authored
      Per Andrey: the sky has nothing to match but is the BACKGROUND FOR DRONES -
      it must render (at infinity), NaN acceptable only outside the image.
      
      Root of the NaN tiles in -CUAS-RT-RENDER: use_lma_dsi=true selected the LMA
      DSI slice, which is NaN wherever the LMA did not fit (the whole sky) ->
      tiles dropped from the task list. The plain slice has no NaN but carries
      garbage (crazy < -1 tiles in the sky, negatives near the sign post).
      
      Fix: NEW static QuadCLT-free conditionDisparityCuas() (the legacy
      conditionInitialDS takes a QuadCLT instance - out per the RT rules):
      plain slice 0, NaN -> 0.0 (infinity), negatives -> 0.0. testRenderSequence
      now uses it unconditionally (not gated by use_lma_dsi - that gate is for
      the pose measurement, not rendering).
      
      Verified: mvn -DskipTests clean package OK.
      Co-Authored-By: 's avatarClaude Fable 5 <noreply@anthropic.com>
      e4e65e23
    • Andrey Filippov's avatar
      CLAUDE: MB convert erase-first overload - undefined tiles reach consolidation as NaN · 48c93f00
      Andrey Filippov authored
      Per Andrey: undefined tiles (= not in the task list; the fixed-length 5120
      arrays represent them as nulls) must be handled by the channel consolidation
      - weighted-by-existing average, all-NaN tile when no sensor has it (now Java
      CuasTD.consolidateSensorsTD, later the CUDA clt_average_sensors kernel).
      The consolidation detects missing by NaN, but the MB double convert ran with
      erase=-1, leaving STALE (valid-looking) data at undefined tiles.
      
      - ImageDtt.interCorrTDMotionBlur: new erase-capable overload (erase_clt_first:
        >0 NaN, 0 zeros, <0 legacy no-erase) - erases before the FIRST (SET) convert
        only; the SECOND (negative-scale SUBTRACT) never erases. Legacy signature
        delegates with -1: oracle/SfM callers byte-identical.
      - CuasRender MB path uses erase_clt_first=1: the GPU TD buffer's NaN pattern
        now mirrors the task-list membership, so consolidation + renders see
        undefined tiles as NaN - never the previous scene.
      
      Verified: mvn -DskipTests clean package OK.
      Co-Authored-By: 's avatarClaude Fable 5 <noreply@anthropic.com>
      48c93f00
    • Andrey Filippov's avatar
      CLAUDE: CuasRender MB compensation + conditionSceneToGpuCuas motion wrapper (ERS omegas/velocities) · 72fd0677
      Andrey Filippov authored
      Per Andrey: of the 3 expected render-vs-oracle mismatch causes, the legitimate
      one is the better photometric calibration; the other two - ERS and motion
      blur - must be implemented in the RT chain:
      
      - ERS: NEW CuasConditioning.conditionSceneToGpuCuas(scene, cfg, omegas,
        velocities, ...) - the ONE place per-scene motion enters the ingest. Sets
        the scene ERS rates (fully-preserved ErsCorrection path); velocities =
        provision ({0,0,0}/null for the rotation-only camera). In production the
        omegas come analytically from wobble radius + RPM; the GPU corr_vector
        imu_rot/imu_move feed is the future upgrade (after the double-application
        check vs CPU pXpYD ERS). Legacy conditionSceneToGpu delegates (unchanged
        behavior for the pose loop).
      - MB: renderSceneVirtual takes mb_vectors/mb_tau/mb_max_gain; when enabled
        uses the ORACLE machinery (setInterTasksMotionBlur two-task-set +
        interCorrTDMotionBlur double convert: positive original + negative shifted
        scale pair summing to 1, ratio from 1-pix-time/tau, offset stretched when
        scales would exceed mb_max_gain). No erased re-convert in MB mode (would
        destroy the accumulation) - comparisons must mask to task tiles.
      - testRenderSequence: MB gated by imp.mb_en (matching the oracle DBG
        products: ON -> compare vs MB DBG, OFF -> vs -NOMB DBG); vectors from the
        same OpticalFlow.getMotionBlur at borrowed pose + stored rates; ingest via
        the new wrapper.
      
      Verified: mvn -DskipTests clean package OK.
      Co-Authored-By: 's avatarClaude Fable 5 <noreply@anthropic.com>
      72fd0677
    • Andrey Filippov's avatar
      CLAUDE: CuasRender - dedicated CUAS RT renderer + MINIMAL ingest+render certification test · 29c7239b
      Andrey Filippov authored
      Per Andrey (2026-07-05): today's problems came from jumping over the standalone
      certification of conditionSceneToGpu (single caller inside the pose test, never
      image-tested by itself). Test and debug the ingest+render chain FIRST, only then
      return to the pose test.
      
      NEW class cuas/rt/CuasRender (the start of the dedicated renderer per the spec
      in imagej-elphel-internal handoffs/2026-07-05_cuas_rt_dataflow_and_grids.md par.6:
      virtual uniform grid always, raw center DSI disparity, jp4+CuasConditioning
      pixels, QuadCLT borrow-only, static methods):
      - renderSceneVirtual(): tasks at pose (ALL valid-disparity tiles) -> convert
        (erase=1, NaN outside tasks) -> 16 per-sensor renders + consolidated (NaN-aware
        weighted average) merged render. No correlation.
      - testRenderSequence(): per scene raw /jp4/ -> conditionSceneToGpu -> render at
        BORROWED stored pose + stored ERS rates (the exact sources the oracle DBG
        renders use) -> saves <center>-CUAS-RT-RENDER.tiff hyperstack
        [t: s00..s15, merged][z: scenes], slice-by-slice comparable to
        -CUAS-INDIVIDUAL-CUAS-DBG / -CUAS-MERGED-CUAS-DBG.
      
      New checkbox "CUAS RT render test (ingest+render)" (curt.rend_test), takes
      precedence over the pose test in the curt_en branch.
      
      Verified: mvn -DskipTests clean package OK.
      Co-Authored-By: 's avatarClaude Fable 5 <noreply@anthropic.com>
      29c7239b
    • Andrey Filippov's avatar
      CLAUDE: REVERT lean sum scaling - falsified by LEAN-07 (peaks blur-dominated, not fz-dominated) · 6eaa8bf4
      Andrey Filippov authored
      LEAN-07 (stored poses, all tiles, sum-scaled input): sqrt_l0 3.22 -> 5.58
      (wider, not the predicted ~2.1), corr RMS 0.82 -> 1.005. At 16x signal-to-
      fat-zero the normalize amplifies the blur-decorrelated high frequencies -
      the fat zero was regularizing usefully. Root of the misdiagnosis: the width
      comparison used the MB-COMPENSATED oracles (FILT150A 2.08, MBEN 2.75); the
      correct no-MB control (NOMB oracle) has sqrt_l0 3.58 ~ lean's 3.22 - lean
      peak shapes were normal all along, and Andrey's "there was no 16x
      difference" was right where it mattered.
      
      Consequences: (1) correlator input back to the true weighted average (the
      same data as the -POSE-RT-COMPOSITE debug render - single upload again);
      (2) the "fz-broadened peak content pull" roll-bias mechanism is falsified -
      the +0.52/+0.33 mrad roll bias needs a new suspect (remaining engine
      differences vs the NOMB oracle: neighbor consolidation min_str_neib,
      min_confidence gates, peak-extraction parameters); (3) MB compensation in
      lean (v2) is the main quality lever - MBEN sharpens peaks 3.6 -> 2.8 and
      removes the 0.5 px wobble.
      
      Verified: mvn -DskipTests clean package OK.
      Co-Authored-By: 's avatarClaude Fable 5 <noreply@anthropic.com>
      6eaa8bf4