/**
 ** CuasRtParameters.java - CUAS real-time (curt_*) parameters, extracted from
 ** IntersceneMatchParameters (2026-07-05) to keep it under the 64KB method-bytecode
 ** limit and group all curt_* in one navigable place. Held as
 ** IntersceneMatchParameters.curt; persisted with the same property keys
 ** (prefix+"curt_"+short) so old .corr-xml files still load. GPLv3 (Elphel norm).
 ** By Claude on 07/05/2026, from Andrey's design.
 */
package com.elphel.imagej.tileprocessor;

import java.awt.Rectangle;
import java.util.Properties;

import com.elphel.imagej.common.GenericJTabbedDialogMcp;

public class CuasRtParameters {
	public boolean  en =              true; // enable cuas rt calculation (not needed with a separate button)
	public boolean  calib =           false; // first step of the CUAS RT processing flow: per-sensor photometric (re)calibration - fit a+b*x over safe (weak/far) tiles, fold into the 16+16 lwir offsets/scales, apply + save (reference scene INTERFRAME corr-xml, quadCLT_main -> main config). Bypass when off. // By Claude on 07/03/2026
	public boolean  cond_test =       false; // conditioning/calibration isolation test inside the en branch: build the QuadCLT instances (borrowed calibration) then print per-sensor average spread (CuasMotion.perSensorAveragesFromTD) instead of normal RT detection; well-calibrated -> 16 sensor averages match (spread ~0). // By Claude on 07/01/2026
	public boolean  pose_test =       false; // RT pose-adjustment prototype (phase A) inside the en branch: re-generate per-scene 3-angle poses against the virtual-center reference, ascending, prediction-seeded (CuasPoseRT.testPoseSequence) instead of RT detection; writes -POSE-RT-TEST.csv + fitted-vs-stored summary. // By Claude on 07/03/2026
	public double   pose_str =        1.0;   // reliable-tile strength threshold over the combo-DSI strength for the pose test tile selection (1.0 ~ old getReliableTiles population). // By Claude on 07/03/2026
	public double   pose_dxy_k =      0.75;  // tile-selection outlier gate: keep tiles with max-over-scenes residual <= median + k*NMAD of the finite per-tile maxes (scale-free - adapts to footage quality/sequence length; NaN-in-any-scene = +inf, always rejected). <=0 - skip the gate. // By Claude on 07/04/2026
	public int      pose_num_tiles =  150;   // tile-selection compute budget: after the gate, keep this many BEST (smallest max-residual) tiles; threshold-free rank - always yields the best available population. <=0 - no cap. // By Claude on 07/04/2026
	public boolean  pose_recalc =     false; // force a FULL-selection pose run that regenerates the -POSE-RT-TILE-CALIB calibration even if it exists. Default (false): automatic FPN-style reuse - if -POSE-RT-TILE-CALIB exists, derive the selection from it (NMAD gate + rank-N) and use it; if not, run full and generate it. // By Claude on 07/04/2026
	public boolean  pose_raw =        false; // phase A2 ingest: per scene read RAW /jp4/, condition with the current calibration (CuasConditioning: rowcol + photometric + FPN) and FORCE-upload straight to the GPU, bypassing the QuadCLT prepared image_data (which carries the old broken Photogrammetric Calibration). Judge by own dstored quality, not agreement with phase A. // 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_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_img_save =   false; // DEBUG: save the composite scenes actually correlated - imclt render of the consolidated 16-sensor TD average at the converged pose (-POSE-RT-COMPOSITE: z=scenes; NaN outside task tiles) + the virtual-center reference render (-POSE-RT-CENTER-REF). Lean engine only. // By Claude on 07/04/2026
	public boolean  pose_stored =     false; // DEBUG: decouple rendering/measurement from the LMA - use the STORED (oracle vintage) pose for every scene, no adjustment; one leanMeasure per scene feeds -POSE-RT-HYPER/-CORR2D/-COMPOSITE. The composite must then match the oracle CUAS-MERGED-CUAS-DBG renders tile-for-tile. Lean engine only. // By Claude on 07/04/2026
	public boolean  rend_test =       false; // MINIMAL RT ingest+render test (CuasRender.testRenderSequence, takes precedence over pose test): 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. Certify THIS before the pose test. // By Claude on 07/05/2026
	public double   psf_radius =      1.0;   // sensor PSF radius for LoG pre-filter
	public double   n_sigma =         4.0;   // cutoff LoG kernel array, number of sigmas
	public int      pyramid =         7;     // temporal pyramid levels
	public int      pix_decimate =    1;     // decimate pixel resolution
	public int      vel_decimate =    4;     // decimate velocities
	public int      vel_radius =      5;     // velocities radius in decimated samples
	public boolean  vel_thresh_en =   true;  // zero out per-(sub)pixel velocities, if they have stronger neighbors in 4D space
	public int      vel_suppr_rad =   3;   // suppress weaker in this radius
	public double   vel_athresh =     1.0;   // absolute threshold: discard this pixel-velocity if it has immediate neighbor (of 80=81-1) stronger by this value 
	public double   vel_rthresh =     0.1;   // relative threshold: discard this pixel-velocity if it has immediate neighbor stronger by this value times stronger value 
	public double   vel_dbgmin =      0.0;   // For testing recurrent with strong targets, zero out lower values 
	public double[] temp_weights =    {1,1,1,1,1}; // historic weights for 5D convolution ([0] is the latest sample)
	public boolean  recur_en =        true;  // enable recurrent accumulation buffer (Layer 2) // By Claude on 06/09/2026
	public int      recur_period =    4;     // time units between buffer updates (= vel_decimate for integer-pixel shifts) // By Claude on 06/09/2026
	public double   recur_w =         0.25;  // measurement weight w: buf = leak*(1-w)*shift(buf) + w*conditioned(meas) // By Claude on 06/09/2026
	public double   recur_leak =      1.0;   // extra decay of the shifted prediction per update (gap decay = leak*(1-w) for fixed w) // By Claude on 06/09/2026
	public double   recur_wthresh =   0.0;   // if >0: adaptive per-pixel weight w_eff=w*tanh(strength/wthresh); 0 - fixed w // By Claude on 06/09/2026
	public double   recur_spread =    0.5;   // predict-step velocity diffusion: 3-tap (spread,1,spread) normalized, lets the max migrate between velocity cells; <=0 - off // By Claude on 06/10/2026
	public int      recur_max_clust = 5;     // max 4D-connected survivor cluster size treated as one target (half-cosine bump fit); larger - linear pass-through // By Claude on 06/10/2026
	public double   recur_fit_rat =   2.0;   // max disagreement of per-survivor peak estimates (max/min) for the bump fit; failed fit - per-survivor single-point bumps // By Claude on 06/10/2026
	public double   recur_rs_frac =   0.5;   // re-sharpen blob growth threshold as fraction of the blob max; <=0 - re-sharpening off // By Claude on 06/10/2026
	public double   recur_rs_min =    1.0;   // re-sharpen minimal blob maximum to re-fit; weaker content passes through as residue // By Claude on 06/10/2026
	public int []   c5_levels =       {};    // pyramid levels to process (empty - all); skips the heavy DNN front-end on others; retained as the DNN level gate // By Claude on 06/13/2026
	public String   dnn_model =       "";    // C5P DNN front-end model (ONNX): empty = disabled; local path, scp user@host:path, or http(s) URL fetched to cache; overrides bundled resource (mirrors tile_processor_gpu) // By Claude on 06/13/2026
	public String   stage2_model =    "";    // C5P DNN v2 Stage-2 (learned Hough-vote refine) ONNX: empty = Stage-1 reg field only; when set + a reg Stage-1 model, the reg branch votes (tail T=P-V*(N-1), s^2-weighted) + refines -> saves -VOTE heatmap + -REFINED detection views. By Claude on 06/18/2026
	public double   dnn_thresh =      0.0;   // VISUALIZATION-ONLY s-threshold: NaN's the -OFFSET Vx,Vy,dx,dy where s < this (0 = show all) so velocity shows only at detections. Does NOT gate Layer 2 / the recurrent (always full field) nor the -RECT/-HYPER-RECT data - do NOT use for critical computation. // By Claude on 06/13/2026, viz-only 06/20/2026
	public int      dnn_stride =      1;     // DNN temporal output stride (per pyramid-level slice): 1 = every step (testing, watch each temporal unit); 4 = production (50% overlap, matches the convolver's integer-pixel-shift cadence = recur_period) // By Claude on 06/14/2026
	public double   dnn_vmax =        1.4;   // "ghostbuster": trained DNN velocity limit (px/frame); zero velocity-grid cells beyond this radius (cell-R > vmax*vel_decimate) and discard pixels whose peak lands there (s=0) - the untrained corner cells emit spurious sidelobes that would confuse the recurrent. Set to match the loaded model's training vmax_px (PM models=1.4, base=1.0); <=0 disables // By Claude on 06/15/2026
	public int      dnn_patch =       24;    // DNN receptive-field / training patch size (px): inferROI extracts this PxP patch per ROI pixel (half=P/2 = the output pixel). MUST match the loaded model (24 = base/PM models, 32 = larger-attention model) // By Claude on 06/16/2026
	public boolean  dnn_remote =      false; // run the DNN front-end on the remote DGX server (CuasDnnRemote) instead of local CPU ORT: upload the conditioned stack once, DGX builds the pyramid + full-res shift-and-stitch, returns full-frame {dx,dy,s,Vx,Vy} (-OFFSET) + ROI 121-cell softmax*s (-RECT/-HYPER-RECT). Local CPU path + recurrent feed untouched when off. // By Claude on 06/20/2026
	public String   dnn_remote_host = "192.168.0.62:5577"; // DGX inference server host:port for dnn_remote (see attic/imagej-elphel-internal/c5p_dnn/infer_server.py) // By Claude on 06/20/2026
	public String   dnn_remote_model = "runs/weighted9_pm_s"; // DGX-side run dir (with model.pt) the auto-launched server loads (passed as RUN=) // By Claude on 06/20/2026
	public String   dnn_remote_srcdir = ""; // server-scripts override dir: empty = bundled jar resource (cuas_dnn/), set = local dir - same default-vs-override scheme as the GPU kernels (cuda_project_directory) // By Claude on 06/20/2026
	public boolean  dnn_local =       false; // run the DNN front-end in-process via native LibTorch (CuasDnnLocal/libtpdnn.so) instead of the remote Python server - identical L1+L2 result, no DGX/server. Overrides dnn_remote when set. // By Claude on 06/27/2026
	public String   dnn_local_dir =   ""; // override dir holding <model>/model.ts.pt (+.meta.json) for dnn_local: empty = bundled jar resource (cuas_dnn/) - same default-vs-override scheme as the kernels. // By Claude on 06/27/2026
	public boolean  dnn_l2 =          false; // run the trained Layer-2 (track-before-detect ConvGRU) on the DGX after L1: -OFFSET then carries L2 {det,Vx,Vy} (L1's full-res, NON-ghostbusted field fed in, recurrence over the scene/time axis). Off = L1 offset5 as before (re-run with this off to inspect L1). Requires dnn_remote. // By Claude on 06/22/2026
	public String   dnn_l2_model =    "runs/l2_v1"; // DGX-side Layer-2 run dir (with model.pt) the auto-launched server loads (passed as RUN2=) // By Claude on 06/22/2026
	public boolean  dnn_recur_splat = false; // when feeding the DNN field to the recurrent layer: false = feed per-pixel field as-is; true = splat each pixel's velocity vector to its fractional offset (px+dx,py+dy) so neighbours reinforce in one sub-pixel bin // By Claude on 06/14/2026
	public double   dnn_recur_scale = 10.0;  // multiply the DNN field (softmax*s, peaks ~0.1) by this before the recurrent feed, to reach the recurrent's tuned scale (rs_min=1.0); ~10 -> peak ~1.0. Alternative to lowering recur_rs_min // By Claude on 06/14/2026
	public boolean  synth_src =       true;  // default set for the synthetic B-measurement experiment (set false for real-data runs); reads *-CUAS-SYNTHETIC-CUAS.tiff, output titles get -SYNTH // By Claude on 06/12/2026
	public double   synth_scale =     5.0;   // synthetic target peak, counts (synthetic file is peak-1 normalized; scaled at load) // By Claude on 06/12/2026
	public int      synth_bg_avg =    1;     // synth quick-run: average this many real frames per output frame (=2^LEV noise floor, e.g. 8=LEV3) + force single level; 1=off // By Claude on 06/20/2026
	public boolean  synth_bg =        true;  // add the real *-CUAS-MERGED-CUAS.tiff scene under the synthetic targets (label-matched frames); false - clean targets only // By Claude on 06/12/2026
	public boolean  subtract_avg =    false; // subtract the input temporal average (over the whole sequence) before LoG - removes static background (treeline edge -> kills its tile-grid streak); moving targets survive. NOTE: uses the whole sequence (not realtime); realtime would use a prior-run average // By Claude on 06/14/2026
	public double   min_frac =        0.0;   // default 0 for linear synthetic/B runs (production value ~0.1); zeroes conv5d outputs where the newest frame contributes less than this fraction (NONLINEAR dark-frame gate) // By Claude on 06/12/2026
	public boolean  save_c5full =     false; // save fine velocities [direction][scene][subpixels]
	public boolean  save_c5rect =     true;  // save fine velocities for selected rectangle only [scene][flattened image]
	public boolean  save_c5hyper =    true;  // save per-velocity HYPER-RECT hyperstacks (bottom slider=velocity, second=time) for C5P and posterior outputs - watch a low-SNR target evolve at fixed velocity // By Claude on 06/13/2026
	public Rectangle save_select =    new Rectangle(98, 265, 82, 24); // Example selection for 1773135527_803834
	public double   time_from =      -1.0;  // timing ROI start (timestamp seconds): <=0 open; 0<v<10000 = last-4 of seconds (expanded from the file's range, rollover-aware); >=10000 explicit. Clips saved RECT/HYPER/DNN/C5P stacks to the time window // By Claude on 06/14/2026
	public double   time_to =        -1.0;  // timing ROI end (timestamp seconds): same convention as time_from; the whole 'to' second is included // By Claude on 06/14/2026

	public void dialogQuestions(GenericJTabbedDialogMcp gd) {
		gd.addCheckbox ("CUAS realtime enable",                      this.en,
				"Enable testing of the realtime CUAS detection.");
		gd.addCheckbox ("CUAS RT photometric calibration",           this.calib, // By Claude on 07/03/2026
				"First step of the RT processing flow: per-sensor photometric (re)calibration - fit a+b*x over safe (weak/far) tiles, fold into the 16+16 lwir offsets/scales, apply and save (reference scene INTERFRAME corr-xml, quadCLT_main for the main configuration). Bypass when off.");
		gd.addCheckbox ("CUAS RT conditioning test",                 this.cond_test,
				"Isolation test inside the en branch: build QuadCLT instances (borrowed calibration), then print per-sensor average spread (CuasMotion.perSensorAveragesFromTD) instead of normal RT detection. Well-calibrated -> the 16 sensor averages match (spread ~0).");
		gd.addCheckbox ("CUAS RT pose test",                         this.pose_test, // By Claude on 07/03/2026
				"RT pose-adjustment prototype (phase A): re-generate per-scene 3-angle poses against the virtual-center reference (ascending, prediction-seeded) instead of RT detection. Writes -POSE-RT-TEST.csv and a fitted-vs-stored summary.");
		gd.addNumericField("Pose test reliable strength",            this.pose_str, 5,7,"", // By Claude on 07/03/2026
				"Tile selection for the pose test: combo-DSI strength threshold (1.0 gives ~ the old getReliableTiles population).");
		gd.addNumericField("Pose test outlier gate k (NMAD)",        this.pose_dxy_k, 5,7,"", // By Claude on 07/04/2026
				"Keep tiles with max-over-scenes residual <= median + k*NMAD of finite per-tile maxes (scale-free; NaN in any scene always rejected). <=0 - skip the gate.");
		gd.addNumericField("Pose test number of best tiles",         this.pose_num_tiles, 0,7,"", // By Claude on 07/04/2026
				"After the gate keep this many best (smallest max-residual) tiles - the RT compute budget. <=0 - no cap.");
		gd.addCheckbox ("Pose test force recalc calibration",        this.pose_recalc, // By Claude on 07/04/2026
				"Force a FULL pose run that regenerates -POSE-RT-TILE-CALIB even if it exists. Default OFF = automatic reuse: use the calibration if present (filtered run), else run full and generate it.");
		gd.addCheckbox ("Pose test raw-jp4 ingest (A2)",             this.pose_raw, // By Claude on 07/05/2026
				"Per scene: read RAW /jp4/, condition with the CURRENT calibration (rowcol+photometric+FPN, CuasConditioning) and force-upload straight to the GPU, bypassing the prepared image_data (old broken Photogrammetric Calibration).");
		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).");
		gd.addCheckbox ("Pose test ALL tiles (ignore calibration)",   this.pose_full, // By Claude on 07/04/2026
				"DEBUG: use all strength-selected tiles (~1074); the tile-selection calibration is neither read nor written.");
		gd.addCheckbox ("Pose test save 2D correlations",            this.pose_corr_save, // By Claude on 07/04/2026
				"DEBUG: save per-scene pixel-domain 2D correlations vs the virtual center (-POSE-RT-CORR2D, z=scenes, last LMA cycle). Lean engine only.");
		gd.addCheckbox ("Pose test save composite scenes",           this.pose_img_save, // By Claude on 07/04/2026
				"DEBUG: save the rendered composite (TD-averaged, grid-transformed) scenes correlated against the virtual center (-POSE-RT-COMPOSITE, z=scenes) + the center reference render (-POSE-RT-CENTER-REF). Lean engine only.");
		gd.addCheckbox ("Pose test stored poses (no LMA)",           this.pose_stored, // By Claude on 07/04/2026
				"DEBUG: measure/render every scene at its STORED pose, skip the LMA entirely - decouples the task/render/correlation chain from the solver. Lean engine only.");
		gd.addCheckbox ("CUAS RT render test (ingest+render)",       this.rend_test, // By Claude on 07/05/2026
				"MINIMAL standalone test (precedence over pose test): raw jp4 -> conditioning -> virtual-grid render at borrowed stored poses/ERS; saves -CUAS-RT-RENDER [s00..s15+merged][scenes] to compare against the -CUAS-*-DBG oracle products.");

		gd.addMessage("=== LoG prefilter ===");
		gd.addNumericField("Optical PSF radius",                     this.psf_radius, 6,8,"pix",
				"Sensor optical  PSF radius for the LoG pre-filter.");
		gd.addNumericField("N-sigmas for LoG cutoff",                this.n_sigma, 6,8,"x",
				"Cutoff LoG kernel array, number of sigmas.");
		// rleak0 dialog field removed 2026-06-20 (LReLU conditioning now LINEAR, alpha=1.0); predecessor code at git tag cuas-layer1. // By Claude on 06/20/2026

		gd.addMessage("=== Temporal binary pyramid ===");
		gd.addNumericField("Temporal pyramid levels",                this.pyramid, 0,3,"",
				"Number of binary temporal pyramid levels.");
		// rleak_pyr dialog field removed 2026-06-20 (pyramid averaging now LINEAR, alpha=1.0); predecessor code at git tag cuas-layer1. // By Claude on 06/20/2026

		// 3d3 coarse-velocity path removed 2026-06-20; predecessor code at git tag cuas-layer1. // By Claude on 06/20/2026
		gd.addMessage("=== Fine velocities layer ===");
		gd.addNumericField("Pixel decimate",                         this.pix_decimate, 0,3,"",
				"Decimate pixels (increase resolution).");
		gd.addNumericField("Velocity decimate",                      this.vel_decimate, 0,3,"",
				"Decimate velocities (increase velocity resolution).");
		gd.addNumericField("Velocities radius",                      this.vel_radius, 0,3,"",
				"Velocities radius in decimated samples.");
		gd.addCheckbox ("Filter by stronger 4d space neighbors",     this.vel_thresh_en,
				"Zero-out velocity components if they have stronger neighbors");
		gd.addNumericField("Velocities suppress radius",            this.vel_suppr_rad, 0,3,"",
				"Suppress weaker in this radius.");
		gd.addNumericField("Absolute threshold",                     this.vel_athresh, 6,8,"",
				"Discard this pixel-velocity if it has immediate neighbor (of 80=81-1) stronger by this valuediscard this pixel-velocity if it has immediate neighbor (of 80=81-1) stronger by this value.");
		gd.addNumericField("Relative thershold",                     this.vel_rthresh, 6,8,"",
				"Discard this pixel-velocity if it has immediate neighbor stronger by this value times stronger value.");

		gd.addNumericField("Remove weaker velocities",               this.vel_dbgmin, 6,8,"",
				"Debug feature for testing recurrent with strong targets - zero out weaker (set 10.0).");
		
		gd.addStringField ("Historic weight for 5D convolution",  IntersceneMatchParameters.doublesToString(temp_weights), 80,
				"Historic weights for 5D convolution ([0] is the latest sample). Will be normalized");

		gd.addMessage("=== Layer 2 recurrent accumulation buffer ==="); // By Claude on 06/09/2026
		gd.addCheckbox ("Enable recurrent buffer",                   this.recur_en, // By Claude on 06/09/2026
				"Maintain a recurrent 4D (pixel, velocity) accumulation buffer over the selected rectangle."); // By Claude on 06/09/2026
		gd.addNumericField("Update period",                          this.recur_period, 0,3,"time units", // By Claude on 06/09/2026
				"Time units between buffer updates. Equal to velocity decimation for integer-pixel shifts (1 px per minimum velocity)."); // By Claude on 06/09/2026
		gd.addNumericField("Measurement weight",                     this.recur_w, 6,8,"", // By Claude on 06/09/2026
				"Update rule: buf = leak*(1-w)*shift(buf) + w*conditioned(measurement). Gap decay = leak*(1-w) with fixed w."); // By Claude on 06/09/2026
		gd.addNumericField("Prediction leak",                        this.recur_leak, 6,8,"", // By Claude on 06/09/2026
				"Extra decay of the velocity-shifted prediction per update. Keep leak^n_gap >= 0.2-0.3 for expected dark gaps."); // By Claude on 06/09/2026
		gd.addNumericField("Adaptive weight threshold",              this.recur_wthresh, 6,8,"", // By Claude on 06/09/2026
				"If >0: per-pixel adaptive weight w_eff = w*tanh(strength/wthresh), dark frames give w_eff~0 (pure prediction). 0 - fixed w."); // By Claude on 06/09/2026
		gd.addNumericField("Velocity diffusion weight",              this.recur_spread, 6,8,"", // By Claude on 06/10/2026
				"Predict-step process noise: 3-tap (spread,1,spread) normalized diffusion over vx,vy after the shift. Lets the buffer maximum migrate between velocity cells (half-grid velocities). <=0 - off."); // By Claude on 06/10/2026
		gd.addNumericField("Max target cluster size",                this.recur_max_clust, 0,3,"", // By Claude on 06/09/2026
				"Survivor clusters up to this size are one target: fit a 4D half-cosine bump (3-pix half-period, half-pixel-grid center) and inject its full grid sampling. Larger clusters (weak linear signal) pass through unchanged."); // By Claude on 06/10/2026
		gd.addNumericField("Bump fit max peak ratio",                this.recur_fit_rat, 6,8,"", // By Claude on 06/10/2026
				"Reject the single-bump fit when per-survivor peak estimates (value/model_weight) disagree by more than this ratio; then each survivor injects its own single-point bump."); // By Claude on 06/10/2026
		gd.addNumericField("Re-sharpen blob threshold fraction",     this.recur_rs_frac, 6,8,"", // By Claude on 06/10/2026
				"After merging the measurement, peel buffer blobs (4D-connected cells above this fraction of the blob max) and re-fit each as a canonical half-cosine at its fractional centroid, conserving total energy. <=0 - off."); // By Claude on 06/10/2026
		gd.addNumericField("Re-sharpen min blob max",                this.recur_rs_min, 6,8,"", // By Claude on 06/10/2026
				"Stop peeling when the strongest remaining cell is below this (filter-output units); sub-threshold residue passes through unchanged."); // By Claude on 06/10/2026
		// C5P matched-filter / Bayes-posterior / whitening dialog fields removed 2026-06-20; predecessor code at git tag cuas-layer1. // By Claude on 06/20/2026
		gd.addStringField ("C5P/DNN pyramid levels",                 IntersceneMatchParameters.intsToString(this.c5_levels), 40, // By Claude on 06/13/2026
				"Process the DNN front-end only on these pyramid levels (space/comma separated, e.g. \"2 3\"). Empty - all levels. Skips the heavy work on others - main speedup. Pyramid frames still build for all levels up to the max selected."); // By Claude on 06/13/2026
		gd.addStringField ("C5P DNN model (ONNX)",                   this.dnn_model, 60, // By Claude on 06/13/2026
				"Trained DNN front-end model location. Empty = disabled (use matched-filter/posterior path). Local path, scp user@host:/path (fetched to cache), or http(s) URL. Overrides any bundled resource - same default-vs-override scheme as the GPU kernel sources."); // By Claude on 06/13/2026
		gd.addStringField ("C5P Stage-2 vote-refine (ONNX)",         this.stage2_model, 60, // By Claude on 06/18/2026
				"v2 Stage-2 learned Hough-vote refine model. Empty = Stage-1 reg field only. When set (with a reg Stage-1 model) the reg branch adds the vote heatmap (-VOTE) and refined detection (-REFINED) views.");
		gd.addCheckbox    ("DNN remote (run on DGX)",                this.dnn_remote, // grouped with the model fields // By Claude on 06/20/2026
				"Run the DNN on the remote DGX inference server instead of local CPU: upload the conditioned stack once, the DGX builds the pyramid + full-res shift-and-stitch, returns full-frame {dx,dy,s,Vx,Vy} (-OFFSET) and ROI 121-cell softmax*s (-RECT/-HYPER-RECT). Local CPU path (+ recurrent feed) used when off."); // By Claude on 06/20/2026
		gd.addStringField ("DNN remote host:port",                   this.dnn_remote_host, 24, // By Claude on 06/20/2026
				"DGX inference server address for 'DNN remote' (default 192.168.0.62:5577)."); // By Claude on 06/20/2026
		gd.addStringField ("DNN remote model (DGX run dir)",         this.dnn_remote_model, 24, // By Claude on 06/20/2026
				"DGX-side run directory (containing model.pt) the auto-launched server loads, e.g. runs/weighted9_pm_s.");
		gd.addStringField ("DNN remote server src (empty=bundled)",  this.dnn_remote_srcdir, 40, // By Claude on 06/20/2026
				"Override dir for the DGX server scripts (infer_server.py / run_infer_server.sh): empty = bundled jar resource (cuas_dnn/); set = local dir. Same default-vs-override (bundled resource vs local repo) scheme as the GPU kernels - bundled is the working version, refresh it after server-script dev.");
		gd.addCheckbox    ("DNN local (in-process LibTorch)",        this.dnn_local, // By Claude on 06/27/2026
				"Run the DNN front-end in-process via native LibTorch (libtpdnn.so / JNA) instead of the remote Python server - identical L1+L2 result, no DGX/server. When set, overrides 'DNN remote'. Needs -Djna.library.path pointing at libtpdnn.so.");
		gd.addStringField ("DNN local model dir (empty=bundled)",    this.dnn_local_dir, 40, // By Claude on 06/27/2026
				"Override dir holding <model>/model.ts.pt (+ .meta.json) for 'DNN local': empty = bundled jar resource (cuas_dnn/). Same default-vs-override scheme as the kernels. Uses the same model names as the remote fields.");
		gd.addCheckbox    ("DNN Layer-2 (run on DGX)",               this.dnn_l2, // By Claude on 06/22/2026
				"Run the trained Layer-2 track-before-detect ConvGRU on the DGX after L1 (requires 'DNN remote'). -OFFSET then shows L2 {det,Vx,Vy} (L1's full-res non-ghostbusted field fed in, recurrence over time), titled -DNN-L2-. Uncheck to re-run the old L1 way."); // By Claude on 06/22/2026
		gd.addStringField ("DNN Layer-2 model (DGX run dir)",        this.dnn_l2_model, 24, // By Claude on 06/22/2026
				"DGX-side Layer-2 run directory (containing model.pt) the auto-launched server loads as RUN2=, e.g. runs/l2_v1.");
		gd.addNumericField("DNN s-threshold (VIZ ONLY)",             this.dnn_thresh, 6,8,"", // By Claude on 06/13/2026, viz-only 06/20/2026
				"VISUALIZATION ONLY - NaN's the -OFFSET Vx,Vy,dx,dy where s < this (0 = show all) so velocity shows only at detections. Does NOT gate Layer 2 (the recurrent always sees the full field) nor the -RECT/-HYPER-RECT data. Do NOT use for critical computation - it is a display mask.");
		gd.addCheckbox ("DNN recurrent feed: offset-splat",          this.dnn_recur_splat, // By Claude on 06/14/2026
				"Feeding the DNN field to the recurrent layer: unchecked = feed per-pixel field as-is; checked = splat each pixel to its fractional (px+dx,py+dy) so neighbours reinforce in one sub-pixel bin. Output gets a -SPLAT mark."); // By Claude on 06/14/2026
		gd.addNumericField("DNN recurrent feed scale",               this.dnn_recur_scale, 4,8,"", // By Claude on 06/14/2026
				"Multiply the DNN field (peaks ~0.1) by this before the recurrent feed so it reaches the recurrent's tuned scale (rs_min=1.0); ~10 -> peak ~1.0. Set 1 and instead lower recur_rs_min if you prefer."); // By Claude on 06/14/2026
		gd.addNumericField("DNN ghostbuster vmax",                   this.dnn_vmax, 4,8,"pix/frame", // By Claude on 06/15/2026
				"Zero DNN velocities beyond this radius (untrained corner cells) and discard pixels peaking there (s=0); match the model's training vmax_px (PM=1.4, base=1.0); <=0 disables.");
		// dnn_t8frac dialog field removed 2026-06-20; predecessor code at git tag cuas-layer1. // By Claude on 06/20/2026
		gd.addNumericField("DNN patch size (RF)",                    this.dnn_patch, 0,3,"pix", // By Claude on 06/16/2026
				"Receptive-field / training patch the model was built at (24 = base/PM, 32 = larger-attention). MUST match the loaded model.");
		gd.addNumericField("DNN temporal stride",                    this.dnn_stride, 0,3,"slices", // By Claude on 06/14/2026
				"DNN output stride along the pyramid-level time axis: 1 = a fresh inference at every slice (testing, watch each temporal step); 4 = production (50% overlap of the window, matches the convolver's integer-pixel-shift cadence)."); // By Claude on 06/14/2026
		gd.addCheckbox ("Use synthetic input",                       this.synth_src, // By Claude on 06/11/2026
				"Read *-CUAS-SYNTHETIC-CUAS.tiff (generated test targets) instead of *-CUAS-MERGED-CUAS.tiff from the same model directory; all output titles get a -SYNTH mark."); // By Claude on 06/11/2026
		gd.addNumericField("Synthetic target peak",                   this.synth_scale, 6,8,"counts", // By Claude on 06/12/2026
				"Scale applied to the (peak-1 normalized) synthetic targets at load = target peak in counts. Reference: birds ~3, background sigma ~1.7, strong targets >30."); // By Claude on 06/12/2026
		gd.addNumericField("Synthetic bg frame averaging",            this.synth_bg_avg, 0,3,"frames", // By Claude on 06/20/2026
				"Synth quick-run: average this many consecutive REAL frames per output frame (LEV noise floor = 2^LEV: 2=LEV1, 4=LEV2, 8=LEV3) and force a single level. 1 = off (full pyramid)."); // By Claude on 06/20/2026
		gd.addCheckbox ("Subtract input temporal average",          this.subtract_avg, // By Claude on 06/14/2026
				"Subtract the per-pixel temporal average of the input (whole sequence) before LoG. Removes the static treeline edge and its tile-grid streak; moving targets are not in the average so they survive. Uses the whole sequence (not realtime - realtime would use a prior-run average)."); // By Claude on 06/14/2026
		gd.addCheckbox ("Synthetic over real background",            this.synth_bg, // By Claude on 06/12/2026
				"Add the real *-CUAS-MERGED-CUAS.tiff scene under the scaled synthetic targets (frames matched by timestamp label). Unchecked - clean targets on zero background (linear B measurement)."); // By Claude on 06/12/2026
		gd.addNumericField("Min newest-frame fraction",              this.min_frac, 6,8,"", // By Claude on 06/12/2026
				"Zero fine-velocity outputs where the newest frame contributes less than this fraction of the accumulated response (dark-frame feedforward gate). NONLINEAR - punches holes into ridge maxima; set 0 for linear/synthetic B-measurement runs."); // By Claude on 06/12/2026

		gd.addMessage("=== Debug, saving images ===");
		gd.addCheckbox ("Save full velocities hyperstack",           this.save_c5full,
				"Save fine velocities [direction][scene][subpixels].");
		gd.addCheckbox ("Save velocities for rectangle",             this.save_c5rect,
				"Save fine velocities for selected rectangle only [scene][flattened image].");
		gd.addCheckbox ("Save per-velocity hyperstacks",             this.save_c5hyper, // By Claude on 06/13/2026
				"HYPER-RECT views (bottom slider = velocity, second slider/scrollwheel = time) for the C5P output and one per posterior lambda - watch a low-SNR target evolve at a fixed velocity."); // By Claude on 06/13/2026
		gd.addStringField ("Output selection Rectangle",             IntersceneMatchParameters.rectangleToString(save_select), 40, "Selection Rectangle:left, top, width,height");
		gd.addNumericField("Time ROI from (s)",                      this.time_from, 3,12,"s", // 3 decimals (fixed) - stable round-trip on OK // By Claude on 06/14/2026
				"Timing ROI start: clips saved RECT/HYPER/DNN/C5P stacks to this time window. -1 = open. Convenience: enter the last 4 digits of the seconds (0<v<10000) and the high digits are guessed from the file's timestamp range (rollover-aware); >=10000 = explicit seconds."); // By Claude on 06/14/2026
		gd.addNumericField("Time ROI to (s)",                        this.time_to, 3,12,"s", // 3 decimals (fixed) - stable round-trip on OK // By Claude on 06/14/2026
				"Timing ROI end: same convention as 'from'. The whole 'to' second is included. -1 = open."); // By Claude on 06/14/2026
	}

	public void dialogAnswers(GenericJTabbedDialogMcp gd) {
		this.en =                  gd.getNextBoolean();
		this.calib =               gd.getNextBoolean(); // By Claude on 07/03/2026
		this.cond_test =           gd.getNextBoolean();
		this.pose_test =           gd.getNextBoolean(); // By Claude on 07/03/2026
		this.pose_str =            gd.getNextNumber();  // By Claude on 07/03/2026
		this.pose_dxy_k =          gd.getNextNumber();       // By Claude on 07/04/2026
		this.pose_num_tiles =(int) gd.getNextNumber();       // By Claude on 07/04/2026
		this.pose_recalc =         gd.getNextBoolean();      // By Claude on 07/04/2026
		this.pose_raw =            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_corr_save =      gd.getNextBoolean();      // By Claude on 07/04/2026
		this.pose_img_save =       gd.getNextBoolean();      // By Claude on 07/04/2026
		this.pose_stored =         gd.getNextBoolean();      // By Claude on 07/04/2026
		this.rend_test =           gd.getNextBoolean();      // By Claude on 07/05/2026

		this.psf_radius =          gd.getNextNumber();
		this.n_sigma =             gd.getNextNumber();
		// rleak0 getNext removed 2026-06-20 (LReLU now LINEAR); predecessor code at git tag cuas-layer1. // By Claude on 06/20/2026

		this.pyramid =       (int) gd.getNextNumber();
		// rleak_pyr getNext removed 2026-06-20 (LReLU now LINEAR); predecessor code at git tag cuas-layer1. // By Claude on 06/20/2026

		// 3d3 coarse-velocity path removed 2026-06-20; predecessor code at git tag cuas-layer1. // By Claude on 06/20/2026
		this.pix_decimate =  (int) gd.getNextNumber();
		this.vel_decimate =  (int) gd.getNextNumber();
		this.vel_radius =    (int) gd.getNextNumber();
		
		this.vel_thresh_en =       gd.getNextBoolean();
		this.vel_suppr_rad = (int) gd.getNextNumber();
		this.vel_athresh =         gd.getNextNumber();
		this.vel_rthresh =         gd.getNextNumber();
		this.vel_dbgmin =          gd.getNextNumber();
	    this.temp_weights = IntersceneMatchParameters.StringToDoubles(gd.getNextString(), 0);

		this.recur_en =            gd.getNextBoolean();     // By Claude on 06/09/2026
		this.recur_period =  (int) gd.getNextNumber();      // By Claude on 06/09/2026
		this.recur_w =             gd.getNextNumber();      // By Claude on 06/09/2026
		this.recur_leak =          gd.getNextNumber();      // By Claude on 06/09/2026
		this.recur_wthresh =       gd.getNextNumber();      // By Claude on 06/09/2026
		this.recur_spread =        gd.getNextNumber();      // By Claude on 06/09/2026
		this.recur_max_clust=(int) gd.getNextNumber();      // By Claude on 06/09/2026
		this.recur_fit_rat =       gd.getNextNumber();      // By Claude on 06/10/2026
		this.recur_rs_frac =       gd.getNextNumber();      // By Claude on 06/10/2026
		this.recur_rs_min =        gd.getNextNumber();      // By Claude on 06/10/2026
		// C5P matched-filter / Bayes-posterior / whitening getNext* removed 2026-06-20; predecessor code at git tag cuas-layer1. // By Claude on 06/20/2026
		this.c5_levels =           IntersceneMatchParameters.StringToInts(gd.getNextString()); // By Claude on 06/13/2026
		this.dnn_model =           gd.getNextString().trim(); // By Claude on 06/13/2026
		this.stage2_model =        gd.getNextString().trim(); // By Claude on 06/18/2026
		this.dnn_remote =          gd.getNextBoolean();     // grouped with the model fields // By Claude on 06/20/2026
		this.dnn_remote_host =     gd.getNextString().trim(); // By Claude on 06/20/2026
		this.dnn_remote_model =    gd.getNextString().trim(); // By Claude on 06/20/2026
		this.dnn_remote_srcdir =   gd.getNextString().trim(); // By Claude on 06/20/2026
		this.dnn_local =           gd.getNextBoolean();     // By Claude on 06/27/2026
		this.dnn_local_dir =       gd.getNextString().trim(); // By Claude on 06/27/2026
		this.dnn_l2 =              gd.getNextBoolean();     // By Claude on 06/22/2026
		this.dnn_l2_model =        gd.getNextString().trim(); // By Claude on 06/22/2026
		this.dnn_thresh =          gd.getNextNumber();      // By Claude on 06/13/2026
		this.dnn_recur_splat =     gd.getNextBoolean();     // By Claude on 06/14/2026
		this.dnn_recur_scale =     gd.getNextNumber();      // By Claude on 06/14/2026
		this.dnn_vmax =            gd.getNextNumber();      // By Claude on 06/15/2026
		// dnn_t8frac getNext removed 2026-06-20; predecessor code at git tag cuas-layer1. // By Claude on 06/20/2026
		this.dnn_patch =     (int) gd.getNextNumber();      // By Claude on 06/16/2026
		this.dnn_stride =    (int) gd.getNextNumber();      // By Claude on 06/14/2026
		this.synth_src =           gd.getNextBoolean();     // By Claude on 06/11/2026
		this.synth_scale =         gd.getNextNumber();      // By Claude on 06/12/2026
		this.synth_bg_avg =        (int) gd.getNextNumber(); // By Claude on 06/20/2026
		this.subtract_avg =        gd.getNextBoolean();     // By Claude on 06/14/2026
		this.synth_bg =            gd.getNextBoolean();     // By Claude on 06/12/2026
		this.min_frac =            gd.getNextNumber();      // By Claude on 06/12/2026

		this.save_c5full =         gd.getNextBoolean();
		this.save_c5rect =         gd.getNextBoolean();
		this.save_c5hyper =        gd.getNextBoolean();     // By Claude on 06/13/2026
		this.save_select =         IntersceneMatchParameters.stringToRectangle(gd.getNextString());// Rectangle
		this.time_from =           gd.getNextNumber();      // By Claude on 06/14/2026
		this.time_to =             gd.getNextNumber();      // By Claude on 06/14/2026
	}

	public void setProperties(String prefix, Properties properties){
		properties.setProperty(prefix+"en",              this.en+"");             // boolean
		properties.setProperty(prefix+"calib",           this.calib+"");          // boolean // By Claude on 07/03/2026
		properties.setProperty(prefix+"cond_test",       this.cond_test+"");      // boolean
		properties.setProperty(prefix+"pose_test",       this.pose_test+"");      // boolean // By Claude on 07/03/2026
		properties.setProperty(prefix+"pose_str",        this.pose_str+"");       // double  // By Claude on 07/03/2026
		properties.setProperty(prefix+"pose_dxy_k",      this.pose_dxy_k+"");     // double  // By Claude on 07/04/2026
		properties.setProperty(prefix+"pose_num_tiles",  this.pose_num_tiles+""); // int     // By Claude on 07/04/2026
		properties.setProperty(prefix+"pose_recalc",     this.pose_recalc+"");    // boolean // By Claude on 07/04/2026
		properties.setProperty(prefix+"pose_raw",        this.pose_raw+"");       // 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_corr_save",  this.pose_corr_save+""); // boolean // By Claude on 07/04/2026
		properties.setProperty(prefix+"pose_img_save",   this.pose_img_save+"");  // boolean // By Claude on 07/04/2026
		properties.setProperty(prefix+"pose_stored",     this.pose_stored+"");    // boolean // By Claude on 07/04/2026
		properties.setProperty(prefix+"rend_test",       this.rend_test+"");      // boolean // By Claude on 07/05/2026

		properties.setProperty(prefix+"psf_radius",      this.psf_radius+"");     // double
		properties.setProperty(prefix+"n_sigma",         this.n_sigma+"");        // double
		// rleak0 setProperty removed 2026-06-20 (LReLU now LINEAR); predecessor code at git tag cuas-layer1. // By Claude on 06/20/2026

		properties.setProperty(prefix+"pyramid",         this.pyramid+"");        // int
		// rleak_pyr setProperty removed 2026-06-20 (LReLU now LINEAR); predecessor code at git tag cuas-layer1. // By Claude on 06/20/2026

		// 3d3 coarse-velocity path removed 2026-06-20; predecessor code at git tag cuas-layer1. // By Claude on 06/20/2026

		properties.setProperty(prefix+"pix_decimate",    this.pix_decimate+"");   // int
		properties.setProperty(prefix+"vel_decimate",    this.vel_decimate+"");   // int
		properties.setProperty(prefix+"vel_radius",      this.vel_radius+"");     // int
		properties.setProperty(prefix+"vel_thresh_en",   this.vel_thresh_en+"");  // boolean
		properties.setProperty(prefix+"vel_suppr_rad",   this.vel_suppr_rad+"");  // int
		properties.setProperty(prefix+"vel_athresh",     this.vel_athresh+"");    // double
		properties.setProperty(prefix+"vel_rthresh",     this.vel_rthresh+"");    // double
		properties.setProperty(prefix+"vel_dbgmin",      this.vel_dbgmin+"");     // double
		properties.setProperty(prefix+"temp_weights", IntersceneMatchParameters.doublesToString(this.temp_weights));

		properties.setProperty(prefix+"recur_en",        this.recur_en+"");       // boolean // By Claude on 06/09/2026
		properties.setProperty(prefix+"recur_period",    this.recur_period+"");   // int     // By Claude on 06/09/2026
		properties.setProperty(prefix+"recur_w",         this.recur_w+"");        // double  // By Claude on 06/09/2026
		properties.setProperty(prefix+"recur_leak",      this.recur_leak+"");     // double  // By Claude on 06/09/2026
		properties.setProperty(prefix+"recur_wthresh",   this.recur_wthresh+"");  // double  // By Claude on 06/09/2026
		properties.setProperty(prefix+"recur_spread",    this.recur_spread+"");   // double  // By Claude on 06/09/2026
		properties.setProperty(prefix+"recur_max_clust", this.recur_max_clust+"");// int     // By Claude on 06/09/2026
		properties.setProperty(prefix+"recur_fit_rat",   this.recur_fit_rat+"");  // double  // By Claude on 06/10/2026
		properties.setProperty(prefix+"recur_rs_frac",   this.recur_rs_frac+"");  // double  // By Claude on 06/10/2026
		properties.setProperty(prefix+"recur_rs_min",    this.recur_rs_min+"");   // double  // By Claude on 06/10/2026
		// C5P matched-filter / Bayes-posterior / whitening setProperty removed 2026-06-20; predecessor code at git tag cuas-layer1. // By Claude on 06/20/2026
		properties.setProperty(prefix+"c5_levels",       IntersceneMatchParameters.intsToString(this.c5_levels)); // int [] // By Claude on 06/13/2026
		properties.setProperty(prefix+"dnn_model",       this.dnn_model);         // String // By Claude on 06/13/2026
		properties.setProperty(prefix+"stage2_model",    this.stage2_model);      // String // By Claude on 06/18/2026
		properties.setProperty(prefix+"dnn_thresh",      this.dnn_thresh+"");     // double // By Claude on 06/13/2026
		properties.setProperty(prefix+"dnn_recur_splat", this.dnn_recur_splat+""); // boolean // By Claude on 06/14/2026
		properties.setProperty(prefix+"dnn_recur_scale", this.dnn_recur_scale+""); // double // By Claude on 06/14/2026
		properties.setProperty(prefix+"dnn_stride",      this.dnn_stride+"");     // int // By Claude on 06/14/2026
		properties.setProperty(prefix+"dnn_vmax",        this.dnn_vmax+"");       // double // By Claude on 06/15/2026
		// dnn_t8frac setProperty removed 2026-06-20; predecessor code at git tag cuas-layer1. // By Claude on 06/20/2026
		properties.setProperty(prefix+"dnn_patch",       this.dnn_patch+"");      // int // By Claude on 06/16/2026
		properties.setProperty(prefix+"dnn_remote",      this.dnn_remote+"");     // boolean // By Claude on 06/20/2026
		properties.setProperty(prefix+"dnn_remote_host", this.dnn_remote_host);   // String // By Claude on 06/20/2026
		properties.setProperty(prefix+"dnn_remote_model", this.dnn_remote_model); // String // By Claude on 06/20/2026
		properties.setProperty(prefix+"dnn_remote_srcdir", this.dnn_remote_srcdir); // String // By Claude on 06/20/2026
		properties.setProperty(prefix+"dnn_local",       this.dnn_local+"");      // boolean // By Claude on 06/27/2026
		properties.setProperty(prefix+"dnn_local_dir",   this.dnn_local_dir);     // String // By Claude on 06/27/2026
		properties.setProperty(prefix+"dnn_l2",          this.dnn_l2+"");         // boolean // By Claude on 06/22/2026
		properties.setProperty(prefix+"dnn_l2_model",    this.dnn_l2_model);      // String // By Claude on 06/22/2026
		properties.setProperty(prefix+"synth_src",       this.synth_src+"");      // boolean // By Claude on 06/11/2026
		properties.setProperty(prefix+"synth_scale",     this.synth_scale+"");    // double  // By Claude on 06/12/2026
		properties.setProperty(prefix+"synth_bg_avg",    this.synth_bg_avg+"");   // int     // By Claude on 06/20/2026
		properties.setProperty(prefix+"subtract_avg",    this.subtract_avg+"");   // boolean // By Claude on 06/14/2026
		properties.setProperty(prefix+"synth_bg",        this.synth_bg+"");       // boolean // By Claude on 06/12/2026
		properties.setProperty(prefix+"min_frac",        this.min_frac+"");       // double  // By Claude on 06/12/2026

		properties.setProperty(prefix+"save_c5full",     this.save_c5full+"");    // boolean
		properties.setProperty(prefix+"save_c5rect",     this.save_c5rect+"");    // boolean
		properties.setProperty(prefix+"save_c5hyper",    this.save_c5hyper+"");   // boolean // By Claude on 06/13/2026
		properties.setProperty(prefix+"save_select",     IntersceneMatchParameters.rectangleToString(save_select)+"");  // Rectangle
		properties.setProperty(prefix+"time_from",       this.time_from+"");      // double // By Claude on 06/14/2026
		properties.setProperty(prefix+"time_to",         this.time_to+"");        // double // By Claude on 06/14/2026
	}

	public void getProperties(String prefix, Properties properties){
		if (properties.getProperty(prefix+"en")!=null)              this.en=Boolean.parseBoolean(properties.getProperty(prefix+"en"));
		if (properties.getProperty(prefix+"calib")!=null)           this.calib=Boolean.parseBoolean(properties.getProperty(prefix+"calib")); // By Claude on 07/03/2026
		if (properties.getProperty(prefix+"cond_test")!=null)       this.cond_test=Boolean.parseBoolean(properties.getProperty(prefix+"cond_test"));
		if (properties.getProperty(prefix+"pose_test")!=null)       this.pose_test=Boolean.parseBoolean(properties.getProperty(prefix+"pose_test")); // By Claude on 07/03/2026
		if (properties.getProperty(prefix+"pose_str")!=null)        this.pose_str=Double.parseDouble(properties.getProperty(prefix+"pose_str"));    // By Claude on 07/03/2026
		if (properties.getProperty(prefix+"pose_dxy_k")!=null)      this.pose_dxy_k=Double.parseDouble(properties.getProperty(prefix+"pose_dxy_k"));           // By Claude on 07/04/2026
		if (properties.getProperty(prefix+"pose_num_tiles")!=null)  this.pose_num_tiles=Integer.parseInt(properties.getProperty(prefix+"pose_num_tiles"));     // By Claude on 07/04/2026
		if (properties.getProperty(prefix+"pose_recalc")!=null)     this.pose_recalc=Boolean.parseBoolean(properties.getProperty(prefix+"pose_recalc"));       // By Claude on 07/04/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_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_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_img_save")!=null)   this.pose_img_save=Boolean.parseBoolean(properties.getProperty(prefix+"pose_img_save"));   // By Claude on 07/04/2026
		if (properties.getProperty(prefix+"pose_stored")!=null)     this.pose_stored=Boolean.parseBoolean(properties.getProperty(prefix+"pose_stored"));       // By Claude on 07/04/2026
		if (properties.getProperty(prefix+"rend_test")!=null)       this.rend_test=Boolean.parseBoolean(properties.getProperty(prefix+"rend_test"));           // By Claude on 07/05/2026

		if (properties.getProperty(prefix+"psf_radius")!=null)      this.psf_radius=Double.parseDouble(properties.getProperty(prefix+"psf_radius"));
		if (properties.getProperty(prefix+"n_sigma")!=null)         this.n_sigma=Double.parseDouble(properties.getProperty(prefix+"n_sigma"));
		// rleak0 getProperty removed 2026-06-20 (LReLU now LINEAR); predecessor code at git tag cuas-layer1. // By Claude on 06/20/2026

		if (properties.getProperty(prefix+"pyramid")!=null)         this.pyramid=Integer.parseInt(properties.getProperty(prefix+"pyramid"));
		// rleak_pyr getProperty removed 2026-06-20 (LReLU now LINEAR); predecessor code at git tag cuas-layer1. // By Claude on 06/20/2026

		// 3d3 coarse-velocity path removed 2026-06-20; predecessor code at git tag cuas-layer1. // By Claude on 06/20/2026

		if (properties.getProperty(prefix+"pix_decimate")!=null)    this.pix_decimate=Integer.parseInt(properties.getProperty(prefix+"pix_decimate"));
		if (properties.getProperty(prefix+"vel_decimate")!=null)    this.vel_decimate=Integer.parseInt(properties.getProperty(prefix+"vel_decimate"));
		if (properties.getProperty(prefix+"vel_radius")!=null)      this.vel_radius=Integer.parseInt(properties.getProperty(prefix+"vel_radius"));
		if (properties.getProperty(prefix+"vel_thresh_en")!=null)   this.vel_thresh_en=Boolean.parseBoolean(properties.getProperty(prefix+"vel_thresh_en"));
		if (properties.getProperty(prefix+"vel_suppr_rad")!=null)   this.vel_suppr_rad=Integer.parseInt(properties.getProperty(prefix+"vel_suppr_rad"));
		if (properties.getProperty(prefix+"vel_athresh")!=null)     this.vel_athresh=Double.parseDouble(properties.getProperty(prefix+"vel_athresh"));
		if (properties.getProperty(prefix+"vel_rthresh")!=null)     this.vel_rthresh=Double.parseDouble(properties.getProperty(prefix+"vel_rthresh"));
		if (properties.getProperty(prefix+"vel_dbgmin")!=null)      this.vel_dbgmin=Double.parseDouble(properties.getProperty(prefix+"vel_dbgmin"));
		if (properties.getProperty(prefix+"temp_weights")!=null) this.temp_weights= IntersceneMatchParameters.StringToDoubles(properties.getProperty(prefix+"temp_weights"),0); // use 0

		if (properties.getProperty(prefix+"recur_en")!=null)        this.recur_en=Boolean.parseBoolean(properties.getProperty(prefix+"recur_en"));         // By Claude on 06/09/2026
		if (properties.getProperty(prefix+"recur_period")!=null)    this.recur_period=Integer.parseInt(properties.getProperty(prefix+"recur_period"));     // By Claude on 06/09/2026
		if (properties.getProperty(prefix+"recur_w")!=null)         this.recur_w=Double.parseDouble(properties.getProperty(prefix+"recur_w"));             // By Claude on 06/09/2026
		if (properties.getProperty(prefix+"recur_leak")!=null)      this.recur_leak=Double.parseDouble(properties.getProperty(prefix+"recur_leak"));       // By Claude on 06/09/2026
		if (properties.getProperty(prefix+"recur_wthresh")!=null)   this.recur_wthresh=Double.parseDouble(properties.getProperty(prefix+"recur_wthresh")); // By Claude on 06/09/2026
		if (properties.getProperty(prefix+"recur_spread")!=null)    this.recur_spread=Double.parseDouble(properties.getProperty(prefix+"recur_spread"));   // By Claude on 06/09/2026
		if (properties.getProperty(prefix+"recur_max_clust")!=null) this.recur_max_clust=Integer.parseInt(properties.getProperty(prefix+"recur_max_clust")); // By Claude on 06/09/2026
		if (properties.getProperty(prefix+"recur_fit_rat")!=null)   this.recur_fit_rat=Double.parseDouble(properties.getProperty(prefix+"recur_fit_rat"));   // By Claude on 06/10/2026
		if (properties.getProperty(prefix+"recur_rs_frac")!=null)   this.recur_rs_frac=Double.parseDouble(properties.getProperty(prefix+"recur_rs_frac"));   // By Claude on 06/10/2026
		if (properties.getProperty(prefix+"recur_rs_min")!=null)    this.recur_rs_min=Double.parseDouble(properties.getProperty(prefix+"recur_rs_min"));     // By Claude on 06/10/2026

		// C5P matched-filter / Bayes-posterior / whitening getProperty removed 2026-06-20; predecessor code at git tag cuas-layer1. // By Claude on 06/20/2026
		if (properties.getProperty(prefix+"c5_levels")!=null)       this.c5_levels= IntersceneMatchParameters.StringToInts(properties.getProperty(prefix+"c5_levels")); // By Claude on 06/13/2026
		if (properties.getProperty(prefix+"dnn_model")!=null)       this.dnn_model=(String) properties.getProperty(prefix+"dnn_model"); // By Claude on 06/13/2026
		if (properties.getProperty(prefix+"stage2_model")!=null)    this.stage2_model=(String) properties.getProperty(prefix+"stage2_model"); // By Claude on 06/18/2026
		if (properties.getProperty(prefix+"dnn_thresh")!=null)      this.dnn_thresh=Double.parseDouble(properties.getProperty(prefix+"dnn_thresh")); // By Claude on 06/13/2026
		if (properties.getProperty(prefix+"dnn_recur_splat")!=null) this.dnn_recur_splat=Boolean.parseBoolean(properties.getProperty(prefix+"dnn_recur_splat")); // By Claude on 06/14/2026
		if (properties.getProperty(prefix+"dnn_recur_scale")!=null) this.dnn_recur_scale=Double.parseDouble(properties.getProperty(prefix+"dnn_recur_scale")); // By Claude on 06/14/2026
		if (properties.getProperty(prefix+"dnn_stride")!=null)      this.dnn_stride=Integer.parseInt(properties.getProperty(prefix+"dnn_stride")); // By Claude on 06/14/2026
		if (properties.getProperty(prefix+"dnn_vmax")!=null)        this.dnn_vmax=Double.parseDouble(properties.getProperty(prefix+"dnn_vmax")); // By Claude on 06/15/2026
		// dnn_t8frac getProperty removed 2026-06-20; predecessor code at git tag cuas-layer1. // By Claude on 06/20/2026
		if (properties.getProperty(prefix+"dnn_patch")!=null)       this.dnn_patch=Integer.parseInt(properties.getProperty(prefix+"dnn_patch")); // By Claude on 06/16/2026
		if (properties.getProperty(prefix+"dnn_remote")!=null)      this.dnn_remote=Boolean.parseBoolean(properties.getProperty(prefix+"dnn_remote")); // By Claude on 06/20/2026
		if (properties.getProperty(prefix+"dnn_remote_host")!=null) this.dnn_remote_host=(String) properties.getProperty(prefix+"dnn_remote_host"); // By Claude on 06/20/2026
		if (properties.getProperty(prefix+"dnn_remote_model")!=null)  this.dnn_remote_model=(String) properties.getProperty(prefix+"dnn_remote_model"); // By Claude on 06/20/2026
		if (properties.getProperty(prefix+"dnn_remote_srcdir")!=null) this.dnn_remote_srcdir=(String) properties.getProperty(prefix+"dnn_remote_srcdir"); // By Claude on 06/20/2026
		if (properties.getProperty(prefix+"dnn_local")!=null)       this.dnn_local=Boolean.parseBoolean(properties.getProperty(prefix+"dnn_local")); // By Claude on 06/27/2026
		if (properties.getProperty(prefix+"dnn_local_dir")!=null)   this.dnn_local_dir=(String) properties.getProperty(prefix+"dnn_local_dir"); // By Claude on 06/27/2026
		if (properties.getProperty(prefix+"dnn_l2")!=null)         this.dnn_l2=Boolean.parseBoolean(properties.getProperty(prefix+"dnn_l2")); // By Claude on 06/22/2026
		if (properties.getProperty(prefix+"dnn_l2_model")!=null)   this.dnn_l2_model=(String) properties.getProperty(prefix+"dnn_l2_model"); // By Claude on 06/22/2026

		if (properties.getProperty(prefix+"synth_src")!=null)       this.synth_src=Boolean.parseBoolean(properties.getProperty(prefix+"synth_src"));         // By Claude on 06/11/2026
		if (properties.getProperty(prefix+"synth_scale")!=null)     this.synth_scale=Double.parseDouble(properties.getProperty(prefix+"synth_scale"));       // By Claude on 06/12/2026
		if (properties.getProperty(prefix+"synth_bg_avg")!=null)    this.synth_bg_avg=Integer.parseInt(properties.getProperty(prefix+"synth_bg_avg"));      // By Claude on 06/20/2026
		if (properties.getProperty(prefix+"subtract_avg")!=null)    this.subtract_avg=Boolean.parseBoolean(properties.getProperty(prefix+"subtract_avg")); // By Claude on 06/14/2026
		if (properties.getProperty(prefix+"synth_bg")!=null)        this.synth_bg=Boolean.parseBoolean(properties.getProperty(prefix+"synth_bg"));           // By Claude on 06/12/2026
		if (properties.getProperty(prefix+"min_frac")!=null)        this.min_frac=Double.parseDouble(properties.getProperty(prefix+"min_frac"));             // By Claude on 06/12/2026

		if (properties.getProperty(prefix+"save_c5full")!=null)     this.save_c5full=Boolean.parseBoolean(properties.getProperty(prefix+"save_c5full"));
		if (properties.getProperty(prefix+"save_c5rect")!=null)     this.save_c5rect=Boolean.parseBoolean(properties.getProperty(prefix+"save_c5rect"));
		if (properties.getProperty(prefix+"save_c5hyper")!=null)    this.save_c5hyper=Boolean.parseBoolean(properties.getProperty(prefix+"save_c5hyper"));   // By Claude on 06/13/2026
		if (properties.getProperty(prefix+"save_select")!=null)     this.save_select=IntersceneMatchParameters.stringToRectangle((String) properties.getProperty(prefix+"save_select"));
		if (properties.getProperty(prefix+"time_from")!=null)       this.time_from=Double.parseDouble(properties.getProperty(prefix+"time_from")); // By Claude on 06/14/2026
		if (properties.getProperty(prefix+"time_to")!=null)         this.time_to=Double.parseDouble(properties.getProperty(prefix+"time_to")); // By Claude on 06/14/2026
	}

	public CuasRtParameters clone() throws CloneNotSupportedException {
		CuasRtParameters cp = new CuasRtParameters();
		cp.en = this.en;
		cp.calib = this.calib;
		cp.cond_test = this.cond_test;
		cp.pose_test = this.pose_test;
		cp.pose_str = this.pose_str;
		cp.pose_dxy_k = this.pose_dxy_k;
		cp.pose_num_tiles = this.pose_num_tiles;
		cp.pose_recalc = this.pose_recalc;
		cp.pose_raw = this.pose_raw;
		cp.pose_lean = this.pose_lean;
		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_img_save = this.pose_img_save; // By Claude on 07/04/2026
		cp.pose_stored = this.pose_stored; // By Claude on 07/04/2026
		cp.rend_test = this.rend_test; // By Claude on 07/05/2026
		cp.psf_radius = this.psf_radius;
		cp.n_sigma = this.n_sigma;
		cp.pyramid = this.pyramid;
		cp.pix_decimate = this.pix_decimate;
		cp.vel_decimate = this.vel_decimate;
		cp.vel_radius = this.vel_radius;
		cp.vel_thresh_en = this.vel_thresh_en;
		cp.vel_suppr_rad = this.vel_suppr_rad;
		cp.vel_athresh = this.vel_athresh;
		cp.vel_rthresh = this.vel_rthresh;
		cp.vel_dbgmin = this.vel_dbgmin;
		cp.temp_weights = (this.temp_weights == null) ? null : this.temp_weights.clone();
		cp.recur_en = this.recur_en;
		cp.recur_period = this.recur_period;
		cp.recur_w = this.recur_w;
		cp.recur_leak = this.recur_leak;
		cp.recur_wthresh = this.recur_wthresh;
		cp.recur_spread = this.recur_spread;
		cp.recur_max_clust = this.recur_max_clust;
		cp.recur_fit_rat = this.recur_fit_rat;
		cp.recur_rs_frac = this.recur_rs_frac;
		cp.recur_rs_min = this.recur_rs_min;
		cp.c5_levels = (this.c5_levels == null) ? null : this.c5_levels.clone();
		cp.dnn_model = this.dnn_model;
		cp.stage2_model = this.stage2_model;
		cp.dnn_thresh = this.dnn_thresh;
		cp.dnn_stride = this.dnn_stride;
		cp.dnn_vmax = this.dnn_vmax;
		cp.dnn_patch = this.dnn_patch;
		cp.dnn_remote = this.dnn_remote;
		cp.dnn_remote_host = this.dnn_remote_host;
		cp.dnn_remote_model = this.dnn_remote_model;
		cp.dnn_remote_srcdir = this.dnn_remote_srcdir;
		cp.dnn_local = this.dnn_local;
		cp.dnn_local_dir = this.dnn_local_dir;
		cp.dnn_l2 = this.dnn_l2;
		cp.dnn_l2_model = this.dnn_l2_model;
		cp.dnn_recur_splat = this.dnn_recur_splat;
		cp.dnn_recur_scale = this.dnn_recur_scale;
		cp.synth_src = this.synth_src;
		cp.synth_scale = this.synth_scale;
		cp.synth_bg_avg = this.synth_bg_avg;
		cp.synth_bg = this.synth_bg;
		cp.subtract_avg = this.subtract_avg;
		cp.min_frac = this.min_frac;
		cp.save_c5full = this.save_c5full;
		cp.save_c5rect = this.save_c5rect;
		cp.save_c5hyper = this.save_c5hyper;
		cp.save_select = (this.save_select == null) ? null : new Rectangle(this.save_select);
		cp.time_from = this.time_from;
		cp.time_to = this.time_to;
		return cp;
	}
}
