Commit 3dfe70ad authored by Andrey Filippov's avatar Andrey Filippov

CLAUDE: CUAS RT mode-0 path + L2 age/noise/flight-log (pre-JNA-migration checkpoint)

Checkpoint of the CUAS real-time work before the JCuda->JNA GPU-layer migration:
- OpticalFlow.buildSeries mode-0 curt_en fork: generate the merged-CUAS stack via
  CuasRanging.prepareFpixels() (GPU, explicit) then run the CUDA-free CuasDetectRT;
  coexists with the oracle (oracle gated off when curt_en).
- CuasDetectRT: file + in-memory(ImagePlus) entries via shared ingest(); -OFFSET gains
  an L2 "age" slice (5->6 ch), per-level noise scale, -LEV0 uniform naming, -OFFSET-<model> suffix.
- infer_server.py: L2 track-age (masked 5x5 max-pool, AGE_THR=0.2/AGE_K=0.5),
  per-level noise normalization (sqrt(2)^(L-3) default, Java-sent scale), nch + noise_scale
  + CMD_STATUS protocol additions; auto model-switch in CuasDnnRemote.ensureServer.
- cuasSynth + cuasNoise list SET keys (shared synth dir / inline per-level scales).
- CuasRanging.saveUasFlightLogCsv: per-frame UAS truth -> <name>-UAS_DATA.tsv (mode-0 only).
Co-Authored-By: 's avatarClaude Opus 4.8 (1M context) <noreply@anthropic.com>
parent 95e25fcc
......@@ -68,7 +68,9 @@ public class EyesisCorrectionParameters {
"resultsDirectory", // 6
"cuasSeed", // 7
"uasLogs", // 8
"skyMask"}; // 9
"skyMask", // 9
"cuasSynth", // 10 shared synthetic-grid dir (curt_synth_src), valid for all sequences. By Claude on 06/24/2026
"cuasNoise"}; // 11 INLINE per-level L2 noise-scale numbers (NOT a path); empty -> sqrt default. By Claude on 06/24/2026
public static final int KEY_INDEX_ROOT_DIRECTORY = 0;
public static final int KEY_INDEX_SOURCE_DIRECTORY = 1;
public static final int KEY_INDEX_LINKED_MODELS = 2;
......@@ -79,6 +81,8 @@ public class EyesisCorrectionParameters {
public static final int KEY_INDEX_CUAS_SEED = 7;
public static final int KEY_INDEX_UAS_LOGS = 8;
public static final int KEY_INDEX_SKY_MASK = 9;
public static final int KEY_INDEX_CUAS_SYNTH = 10; // By Claude on 06/24/2026
public static final int KEY_INDEX_CUAS_NOISE = 11; // inline per-level noise scales. By Claude on 06/24/2026
public static final String AUX_PREFIX = "AUX-";
public boolean swapSubchannels01= true; // false; // (false: 0-1-2, true - 1-0-2)
......@@ -129,6 +133,8 @@ public class EyesisCorrectionParameters {
public String cuasSeedDir= "";
public boolean useCuasSeedDir= false;
public String cuasSkyMask = ""; // TIFF image 640x512 where 1.0 - sky, 0.0 - ground, blurred with GB (now sigma==2.0)
public String cuasSynth = ""; // shared dir holding the synthetic-grid TIFF (curt_synth_src) - valid for ALL sequences, resolved from the list SET key "cuasSynth" (relative to rootDirectory), like cuasSkyMask; empty -> per-sequence model dir (old behavior). By Claude on 06/24/2026
public String cuasNoise = ""; // INLINE per-level L2 noise-scale numbers from the list SET key "cuasNoise" (e.g. "0.354,0.5,0.707,1.0,1.414,2.0"); NOT a path. Empty -> theoretical sqrt(2)^(L-3) default (computed in CuasDetectRT). By Claude on 06/24/2026
public String cuasUasLogs = ""; // json file path containing UAS logs
public double cuasUasTimeStamp = 0.0; // timestamp corresponding to the UAS time 0.0
public double [] cuasCameraATR = {0, 0, 0};
......@@ -322,6 +328,8 @@ public class EyesisCorrectionParameters {
cp.useCuasSeedDir= this.useCuasSeedDir;
cp.cuasSkyMask = this.cuasSkyMask;
cp.cuasSynth = this.cuasSynth;
cp.cuasNoise = this.cuasNoise;
cp.cuasUasLogs = this.cuasUasLogs;
cp.cuasUasTimeStamp = this.cuasUasTimeStamp;
cp.cuasCameraATR = this.cuasCameraATR.clone();
......@@ -1833,7 +1841,7 @@ public class EyesisCorrectionParameters {
if (dir_map.get(KEY_DIRS[i]).length() > 0){
Path dir_path=base_path.resolve(Paths.get(dir_map.get(KEY_DIRS[i])));
File dir_file = new File(dir_path.toString());
if ((i != KEY_INDEX_UAS_LOGS) && (i != KEY_INDEX_SKY_MASK)) { // cuasUasLogs, cuasSkyMask are files, not directories
if ((i != KEY_INDEX_UAS_LOGS) && (i != KEY_INDEX_SKY_MASK) && (i != KEY_INDEX_CUAS_SYNTH) && (i != KEY_INDEX_CUAS_NOISE)) { // cuasUasLogs/cuasSkyMask=files; cuasSynth=input dir; cuasNoise=inline numbers (not a path) - don't auto-create. By Claude on 06/24/2026
if (!dir_file.exists()) {
if (MKDIRS_ALLOW) {
dir_file.mkdirs();
......@@ -1902,6 +1910,19 @@ public class EyesisCorrectionParameters {
this.cuasSkyMask = dir_string; // dir_path.toString();
System.out.println("this.cuasSkyMask=" + this.cuasSkyMask);
break;
case KEY_INDEX_CUAS_SYNTH: // 10: shared synthetic-grid dir (curt_synth_src), all sequences. By Claude on 06/24/2026
this.cuasSynth = dir_string;
System.out.println("this.cuasSynth=" + this.cuasSynth);
break;
case KEY_INDEX_CUAS_NOISE: // 11: INLINE per-level noise scales (numbers, NOT a path). By Claude on 06/24/2026
{
StringBuilder nsb = new StringBuilder(dir_map.get(KEY_DIRS[i])); // first number
ArrayList<String> nx = extra_map.get(KEY_DIRS[i]); // remaining numbers
if (nx != null) for (String v : nx) nsb.append(",").append(v);
this.cuasNoise = nsb.toString();
}
System.out.println("this.cuasNoise=" + this.cuasNoise);
break;
}
}
......
......@@ -2881,6 +2881,64 @@ public class CuasRanging {
}
}
/** Standalone UAS flight-log extraction for the mode-0 RT path (NO CuasMotion / targets array): project the
* DJI flight log to per-scene px,py,range using center_CLT pose + uasLogReader, accumulate a TSV (same columns
* + UAS_DATA_SUFFIX as addUasData), save via the QuadCLT model dir. Timestamps = imp_targets slice labels
* (skip leading non-digit "average" slices, == CuasDetectRT.ingest). Needs QuadCLT pose, so mode-0 only (not
* the mode=3 file path). By Claude on 06/24/2026 */
public void saveUasFlightLogCsv(UasLogReader uasLogReader, ImagePlus imp_targets) {
if (uasLogReader == null) {
System.out.println("saveUasFlightLogCsv(): no UAS log reader - skipping flight-log CSV");
return;
}
if ((imp_targets == null) || (imp_targets.getStackSize() < 1)) {
System.out.println("saveUasFlightLogCsv(): no imp_targets - skipping flight-log CSV");
return;
}
// camera reference LLA from center_CLT (mirror addUasData)
if ((getCenter_CLT() != null) && getCenter_CLT().hasIns()) {
double [] cameraLla = getCenter_CLT().getLla();
if ((cameraLla != null) && ((cameraLla[0] != 0.0) || (cameraLla[1] != 0.0))) {
uasLogReader.setCameraLLA(cameraLla);
}
}
double [] cam_atr = uasLogReader.getCameraATR();
int tilesX = getCenter_CLT().getTileProcessor().getTilesX();
int tilesY = getCenter_CLT().getTileProcessor().getTilesY();
ij.ImageStack stack = imp_targets.getStack();
int num_slices = stack.getSize();
int first_slice = 1;
for (; (first_slice <= num_slices) && !Character.isDigit(stack.getSliceLabel(first_slice).charAt(0)); first_slice++);
StringBuffer sb = new StringBuffer();
sb.append("seq\tts\tstatus\tpx\tpy\ttile_x\ttile_y\trange\tlat\tlon\talt\tnorth\teast\tdown\tcam_az\tcam_tilt\tcam_roll\n");
int nseq = 0;
for (int slice = first_slice; slice <= num_slices; slice++, nseq++) {
// slice labels can carry a suffix (e.g. "1773135457.547099-0"); extract the bare timestamp the
// same way as QuadCLT.getTimeStamp (regex \d{5,10}\.\d{6}), but keep it a STRING - the double
// round-trip would lose microsecond precision at this magnitude. By Claude on 06/24/2026
String norm = stack.getSliceLabel(slice).replace("_", ".");
java.util.regex.Matcher tm = java.util.regex.Pattern.compile("\\d{5,10}\\.\\d{6}").matcher(norm);
String timestamp = tm.find() ? norm.substring(tm.start(), tm.end()) : norm;
double [] uas = uasLogReader.getUasPxPyDRange(timestamp); // px, py, disparity, range
double [] llaned = uasLogReader.getUasLlaNed(timestamp); // lat, lon, alt, N, E, D
if (uas != null) {
double px = uas[0], py = uas[1], range = uas[3];
int tileX = (int) (px / GPUTileProcessor.DTT_SIZE);
int tileY = (int) (py / GPUTileProcessor.DTT_SIZE);
String status = ((tileX >= 0) && (tileY >= 0) && (tileX < tilesX) && (tileY < tilesY)) ? "IN FoV" : "OUT OF FoV";
sb.append(nseq+"\t"+timestamp+"\t"+status+"\t"+px+"\t"+py+"\t"+tileX+"\t"+tileY+"\t"+range+"\t"+
llaned[0]+"\t"+llaned[1]+"\t"+llaned[2]+"\t"+llaned[3]+"\t"+llaned[4]+"\t"+llaned[5]+"\t"+
cam_atr[0]+"\t"+cam_atr[1]+"\t"+cam_atr[2]+"\n");
} else {
sb.append(nseq+"\t"+timestamp+"\tno entry\t\t\t\t\t\t"+
((llaned != null) ? (llaned[0]+"\t"+llaned[1]+"\t"+llaned[2]) : "\t\t")+
"\t\t\t\t"+cam_atr[0]+"\t"+cam_atr[1]+"\t"+cam_atr[2]+"\n");
}
}
getCenter_CLT().saveStringInModelDirectory(sb.toString(), UAS_DATA_SUFFIX, false);
System.out.println("saveUasFlightLogCsv(): wrote UAS flight log ("+nseq+" scenes) -> "+getCenter_CLT().getImageName()+UAS_DATA_SUFFIX);
}
// relies on calcMatchingTargetsLengths(.., true,...) called from recalcOmegas() to set [RSLT_GLOBAL]
public void saveTargetStats(
final double [][][] targets_single) {
......
......@@ -29,7 +29,7 @@ import java.nio.file.Files;
* BYE : cmd=0
*/
public class CuasDnnRemote implements AutoCloseable {
private static final int CMD_BYE = 0, CMD_UPLOAD = 1, CMD_INFER = 2, CMD_READBACK = 3;
private static final int CMD_BYE = 0, CMD_UPLOAD = 1, CMD_INFER = 2, CMD_READBACK = 3, CMD_STATUS = 4;
private final Socket sock;
private final DataInputStream in;
private final DataOutputStream out;
......@@ -73,27 +73,30 @@ public class CuasDnnRemote implements AutoCloseable {
* the GPU) + the total pure-GPU compute ms (continuous = production throughput). */
public static class BatchResult {
public double gpuMs;
public int H, W, count, nvel, rh, rw;
public float [][][] offset5; // [count][5][H*W]
public int H, W, count, nvel, rh, rw, nch; // nch = offset channels: 5 {dx,dy,s,Vx,Vy} or 6 (+L2 age). By Claude 06/24/2026
public float [][][] offset5; // [count][nch][H*W]
public float [][][] roiField; // [count][rh*rw][nvel]
}
/** Infer `count` scenes of a level in one round-trip (newest_s = start + s*stride). rmaxCells>0
* enables the on-GPU ghostbuster (== CuasDetectRT.dnnGhostbust). Keep `count` modest so the
* reply byte[] stays < 2GB (count*5*H*W*4): count<=64 is ~419MB at 640x512. */
public BatchResult inferBatch(int level, int start, int count, int stride, Rectangle roi, double rmaxCells) throws Exception {
public BatchResult inferBatch(int level, int start, int count, int stride, Rectangle roi, double rmaxCells,
boolean l2Enable, boolean l2Reset, double noiseScale) throws Exception {
out.writeInt(CMD_INFER); out.writeInt(level); out.writeInt(start); out.writeInt(count); out.writeInt(stride);
out.writeInt(roi.x); out.writeInt(roi.y); out.writeInt(roi.width); out.writeInt(roi.height);
out.writeDouble(rmaxCells);
out.writeInt(l2Enable ? 1 : 0); out.writeInt(l2Reset ? 1 : 0); // run Layer-2 on the DGX; reset hidden state at a level's first chunk. By Claude 06/22/2026
out.writeDouble(noiseScale); // per-level L1-input noise scale (Java is the source of truth; <=0 -> server sqrt fallback). By Claude 06/24/2026
out.flush();
BatchResult r = new BatchResult();
r.gpuMs = in.readDouble(); r.H = in.readInt(); r.W = in.readInt();
r.count = in.readInt(); r.nvel = in.readInt(); r.rh = in.readInt(); r.rw = in.readInt();
r.count = in.readInt(); r.nch = in.readInt(); r.nvel = in.readInt(); r.rh = in.readInt(); r.rw = in.readInt(); // +nch. By Claude 06/24/2026
int hw = r.H * r.W, rn = r.rh * r.rw;
byte [] ob = new byte [r.count * 5 * hw * 4]; in.readFully(ob);
byte [] ob = new byte [r.count * r.nch * hw * 4]; in.readFully(ob);
ByteBuffer obb = ByteBuffer.wrap(ob);
r.offset5 = new float [r.count][5][hw];
for (int s = 0; s < r.count; s++) for (int c = 0; c < 5; c++) for (int p = 0; p < hw; p++) r.offset5[s][c][p] = obb.getFloat();
r.offset5 = new float [r.count][r.nch][hw];
for (int s = 0; s < r.count; s++) for (int c = 0; c < r.nch; c++) for (int p = 0; p < hw; p++) r.offset5[s][c][p] = obb.getFloat();
byte [] rb = new byte [r.count * rn * r.nvel * 4]; in.readFully(rb);
ByteBuffer rbb = ByteBuffer.wrap(rb);
r.roiField = new float [r.count][rn][r.nvel];
......@@ -148,21 +151,70 @@ public class CuasDnnRemote implements AutoCloseable {
if (p.waitFor() != 0) throw new Exception("deploy " + name + " to " + sshTarget + ":" + dest + " failed");
}
/** Ensure the DGX server is reachable at hostPort; if not, deploy the (bundled/override) scripts and
* ssh-launch run_infer_server.sh with RUN=model, then poll until it accepts connections. No manual step. */
public static void ensureServer(String hostPort, String model, String srcdir) throws Exception {
/** Query the running server for its loaded {L1 model, L2 model} paths (L2="" if L1-only), or null
* if unreachable or the server is too old to answer CMD_STATUS. A short read timeout keeps an old
* server (which ignores the opcode and never replies) from hanging the client. By Claude 06/24/2026 */
private static String [] queryModels(String host, int port) {
try (Socket s = new Socket()) {
s.connect(new InetSocketAddress(host, port), 1500);
s.setSoTimeout(2500);
DataOutputStream o = new DataOutputStream(s.getOutputStream());
DataInputStream i = new DataInputStream (s.getInputStream());
o.writeInt(CMD_STATUS); o.flush();
int n1 = i.readInt(); byte [] b1 = new byte [n1]; i.readFully(b1);
int n2 = i.readInt(); byte [] b2 = new byte [n2]; i.readFully(b2);
try { o.writeInt(CMD_BYE); o.flush(); } catch (Exception e) { /* ignore */ }
return new String [] { new String(b1, java.nio.charset.StandardCharsets.UTF_8),
new String(b2, java.nio.charset.StandardCharsets.UTF_8) };
} catch (Exception e) {
return null; // unreachable, or old server (status read timed out)
}
}
/** ssh-stop the DGX server (docker rm -f) so the next launch can load a different model. By Claude 06/24/2026 */
private static void stopServer(String sshTarget, String code, int port) throws Exception {
ProcessBuilder pb = new ProcessBuilder("ssh", sshTarget,
"cd " + code + " && PORT=" + port + " ./run_infer_server.sh stop");
pb.inheritIO();
pb.start().waitFor();
}
/** Ensure the DGX server is up at hostPort WITH the requested L1+L2 models; if a different model is
* loaded (or the server is an old build that can't report), tear it down and relaunch. Then deploy the
* (bundled/override) scripts and ssh-launch run_infer_server.sh with RUN=model, polling until it accepts
* connections. No manual step. All decisions logged to System.out (-> ImageJ log file). By Claude 06/24/2026 */
public static void ensureServer(String hostPort, String model, String l2model, String srcdir) throws Exception {
String [] hp = hostPort.split(":");
String host = hp[0].trim();
int port = (hp.length > 1) ? Integer.parseInt(hp[1].trim()) : 5577;
if (canConnect(host, port, 1500)) return; // already up
String sshTarget = "elphel@" + host; // DGX login user
String code = "/home/elphel/c5p_dnn"; // DGX dir (model.py + runs/ live here)
System.out.println("CuasDnnRemote.ensureServer(): no server at " + host + ":" + port
+ " - deploying scripts + launching on " + sshTarget + " (model=" + model + ")");
String wantL2 = (l2model == null) ? "" : l2model; // normalize (server reports "" for L1-only)
if (canConnect(host, port, 1500)) { // a server is listening - is it the right one?
String [] cur = queryModels(host, port);
if ((cur != null) && cur[0].equals(model) && cur[1].equals(wantL2)) {
System.out.println("CuasDnnRemote.ensureServer(): server at " + host + ":" + port
+ " already loaded with matching models (L1=" + model + ", L2=" + (wantL2.isEmpty()?"off":wantL2) + ") - reusing");
return;
}
if (cur == null) {
System.out.println("CuasDnnRemote.ensureServer(): server at " + host + ":" + port
+ " up but did not report models (old build) - restarting for L1=" + model + ", L2=" + (wantL2.isEmpty()?"off":wantL2));
} else {
System.out.println("CuasDnnRemote.ensureServer(): server model MISMATCH at " + host + ":" + port
+ " (has L1=" + cur[0] + ", L2=" + (cur[1].isEmpty()?"off":cur[1])
+ "; want L1=" + model + ", L2=" + (wantL2.isEmpty()?"off":wantL2) + ") - restarting");
}
stopServer(sshTarget, code, port); // free the port so the relaunch loads the new model
}
String run2 = !wantL2.isEmpty() ? (" RUN2=" + wantL2) : ""; // optional Layer-2. By Claude 06/22/2026
System.out.println("CuasDnnRemote.ensureServer(): launching server on " + sshTarget
+ " (model=" + model + ", l2=" + (run2.isEmpty()?"off":wantL2) + ")");
deployScript("infer_server.py", srcdir, sshTarget, code + "/infer_server.py");
deployScript("layer2.py", srcdir, sshTarget, code + "/layer2.py"); // Layer-2 model module (Layer2Net). By Claude 06/22/2026
deployScript("run_infer_server.sh", srcdir, sshTarget, code + "/run_infer_server.sh");
ProcessBuilder pb = new ProcessBuilder("ssh", sshTarget,
"cd " + code + " && chmod +x run_infer_server.sh && RUN=" + model + " PORT=" + port + " ./run_infer_server.sh start");
"cd " + code + " && chmod +x run_infer_server.sh && RUN=" + model + run2 + " PORT=" + port + " ./run_infer_server.sh start");
pb.inheritIO();
pb.start().waitFor();
long deadline = System.currentTimeMillis() + 90000; // model load + warm-up can take a bit
......
......@@ -1168,6 +1168,8 @@ min_str_neib_fpn 0.35
public String curt_dnn_remote_host = "192.168.0.62:5577"; // DGX inference server host:port for curt_dnn_remote (see attic/imagej-elphel-internal/c5p_dnn/infer_server.py) // By Claude on 06/20/2026
public String curt_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 curt_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 curt_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 curt_dnn_remote. // By Claude on 06/22/2026
public String curt_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 curt_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 curt_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 curt_recur_rs_min // By Claude on 06/14/2026
public boolean curt_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
......@@ -2702,8 +2704,8 @@ min_str_neib_fpn 0.35
"Maximal gain for motion blur correction (if needed more for 1 pixel, increase offset). Will be forced fro the last adjustment");
gd.addNumericField("Maximal gain pose", this.mb_max_gain_inter, 5,7,"x",
"Maximal gain for motion blur correction during interscene correlation. Will be used for all but the last adjustment.");
gd.addTab("CUAS","CUAS Parameters");
gd.addCheckbox ("Enable targets processing", this.cuas_targets_en,
gd.addTab("CUAS Oracle","CUAS OracleParameters");
gd.addCheckbox ("Enable Oracle targets processing", this.cuas_targets_en,
"Enable extraction and processing targets.");
gd.addCheckbox ("Re-calculate center CLT", this.cuas_update_existing,
"Re-create center_CLT if it exists (FIXME: accumulates errors - need fixing).");
......@@ -3478,6 +3480,10 @@ min_str_neib_fpn 0.35
"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.curt_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 Layer-2 (run on DGX)", this.curt_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.curt_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.curt_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.curt_dnn_recur_splat, // By Claude on 06/14/2026
......@@ -5026,6 +5032,8 @@ min_str_neib_fpn 0.35
this.curt_dnn_remote_host = gd.getNextString().trim(); // By Claude on 06/20/2026
this.curt_dnn_remote_model = gd.getNextString().trim(); // By Claude on 06/20/2026
this.curt_dnn_remote_srcdir = gd.getNextString().trim(); // By Claude on 06/20/2026
this.curt_dnn_l2 = gd.getNextBoolean(); // By Claude on 06/22/2026
this.curt_dnn_l2_model = gd.getNextString().trim(); // By Claude on 06/22/2026
this.curt_dnn_thresh = gd.getNextNumber(); // By Claude on 06/13/2026
this.curt_dnn_recur_splat = gd.getNextBoolean(); // By Claude on 06/14/2026
this.curt_dnn_recur_scale = gd.getNextNumber(); // By Claude on 06/14/2026
......@@ -6393,6 +6401,8 @@ min_str_neib_fpn 0.35
properties.setProperty(prefix+"curt_dnn_remote_host", this.curt_dnn_remote_host); // String // By Claude on 06/20/2026
properties.setProperty(prefix+"curt_dnn_remote_model", this.curt_dnn_remote_model); // String // By Claude on 06/20/2026
properties.setProperty(prefix+"curt_dnn_remote_srcdir", this.curt_dnn_remote_srcdir); // String // By Claude on 06/20/2026
properties.setProperty(prefix+"curt_dnn_l2", this.curt_dnn_l2+""); // boolean // By Claude on 06/22/2026
properties.setProperty(prefix+"curt_dnn_l2_model", this.curt_dnn_l2_model); // String // By Claude on 06/22/2026
properties.setProperty(prefix+"curt_synth_src", this.curt_synth_src+""); // boolean // By Claude on 06/11/2026
properties.setProperty(prefix+"curt_synth_scale", this.curt_synth_scale+""); // double // By Claude on 06/12/2026
properties.setProperty(prefix+"curt_synth_bg_avg", this.curt_synth_bg_avg+""); // int // By Claude on 06/20/2026
......@@ -6788,6 +6798,8 @@ min_str_neib_fpn 0.35
if (properties.getProperty(prefix+"curt_dnn_remote_host")!=null) this.curt_dnn_remote_host=(String) properties.getProperty(prefix+"curt_dnn_remote_host"); // By Claude on 06/20/2026
if (properties.getProperty(prefix+"curt_dnn_remote_model")!=null) this.curt_dnn_remote_model=(String) properties.getProperty(prefix+"curt_dnn_remote_model"); // By Claude on 06/20/2026
if (properties.getProperty(prefix+"curt_dnn_remote_srcdir")!=null) this.curt_dnn_remote_srcdir=(String) properties.getProperty(prefix+"curt_dnn_remote_srcdir"); // By Claude on 06/20/2026
if (properties.getProperty(prefix+"curt_dnn_l2")!=null) this.curt_dnn_l2=Boolean.parseBoolean(properties.getProperty(prefix+"curt_dnn_l2")); // By Claude on 06/22/2026
if (properties.getProperty(prefix+"curt_dnn_l2_model")!=null) this.curt_dnn_l2_model=(String) properties.getProperty(prefix+"curt_dnn_l2_model"); // By Claude on 06/22/2026
if (properties.getProperty(prefix+"curt_synth_src")!=null) this.curt_synth_src=Boolean.parseBoolean(properties.getProperty(prefix+"curt_synth_src")); // By Claude on 06/11/2026
if (properties.getProperty(prefix+"curt_synth_scale")!=null) this.curt_synth_scale=Double.parseDouble(properties.getProperty(prefix+"curt_synth_scale")); // By Claude on 06/12/2026
......@@ -9065,6 +9077,8 @@ min_str_neib_fpn 0.35
imp.curt_dnn_remote_host = this.curt_dnn_remote_host; // By Claude on 06/20/2026
imp.curt_dnn_remote_model = this.curt_dnn_remote_model; // By Claude on 06/20/2026
imp.curt_dnn_remote_srcdir = this.curt_dnn_remote_srcdir; // By Claude on 06/20/2026
imp.curt_dnn_l2 = this.curt_dnn_l2; // By Claude on 06/22/2026
imp.curt_dnn_l2_model = this.curt_dnn_l2_model; // By Claude on 06/22/2026
imp.curt_synth_src = this.curt_synth_src; // By Claude on 06/11/2026
imp.curt_synth_scale = this.curt_synth_scale; // By Claude on 06/12/2026
imp.curt_synth_bg_avg = this.curt_synth_bg_avg; // By Claude on 06/20/2026
......
......@@ -7239,9 +7239,9 @@ java.lang.NullPointerException
// Moved to the very end, after 3D
// boolean test_vegetation = true;
if (master_CLT.hasCenterClt() && clt_parameters.imp.cuas_targets_en) { // cuas mode
if (master_CLT.hasCenterClt() && clt_parameters.imp.cuas_targets_en && !clt_parameters.imp.curt_en) { // cuas mode
if (debugLevel >-3) {
System.out.println("===== Running CUAS ranging. =====");
System.out.println("===== Running CUAS ranging in Oracle mode. =====");
}
CuasRanging cuasRanging = new CuasRanging (
clt_parameters, // CLTParameters clt_parameters,
......@@ -7260,6 +7260,33 @@ java.lang.NullPointerException
}
}
// CUAS RT (our code) - coexists with the oracle CuasRanging above (separate buttons/modes, no interference).
// Generate the merged-CUAS stack on the GPU EXPLICITLY here (CuasRanging.prepareFpixels() uses the CUDA
// tile-processor kernels - may be incompatible with a future CUDA), then hand the plain ImagePlus to the
// CUDA-free CuasDetectRT. By Claude on 06/24/2026
if (clt_parameters.imp.curt_en && master_CLT.hasCenterClt()) {
System.out.println("===== Running CUAS RT detection (curt_en). =====");
CuasRanging cuasRangingRT = new CuasRanging(
clt_parameters, // CLTParameters clt_parameters,
master_CLT, // QuadCLT center_CLT,
quadCLTs, // QuadCLT [] scenes,
debugLevel);
ImagePlus imp_targets = cuasRangingRT.prepareFpixels(); // GPU generator (explicit, CUDA-sensitive)
cuasRangingRT.saveUasFlightLogCsv(uasLogReader, imp_targets); // UAS flight-log truth -> <name>-UAS_DATA.tsv (mode-0 only; needs QuadCLT pose). By Claude on 06/24/2026
new CuasDetectRT(
clt_parameters, // CLTParameters clt_parameters,
uasLogReader, // UasLogReader uasLogReader,
imp_targets, // ImagePlus imp_targets (no GPU inside CuasDetectRT)
master_CLT.getX3dDirectory(), // String model_directory (outputs land like oracle)
master_CLT.getImageName(), // String core_base_name
master_CLT.correctionsParameters.cuasSynth, // String cuas_synth_dir (shared, list SET; "" -> model_directory)
master_CLT.correctionsParameters.cuasNoise, // String cuas_noise (inline per-level scales, list SET; "" -> sqrt default). By Claude on 06/24/2026
debugLevel).detectTargets(
clt_parameters, // CLTParameters clt_parameters,
batch_mode, // boolean batch_mode,
debugLevel); // int debugLevel
}
if (generate_mapped || reuse_video) { // modifies combo_dsn_final ?
int tilesX = master_CLT.getTileProcessor().getTilesX();
int tilesY = master_CLT.getTileProcessor().getTilesY();
......
......@@ -9241,6 +9241,8 @@ if (debugLevel > -100) return true; // temporarily !
clt_parameters, // CLTParameters clt_parameters,
uasLogReader, // UasLogReader uasLogReader,
model_paths[i], // String model_directory) {
quadCLT_main.correctionsParameters.cuasSynth, // String cuas_synth_dir (shared, list SET; "" -> model_directory). By Claude on 06/24/2026
quadCLT_main.correctionsParameters.cuasNoise, // String cuas_noise (inline per-level scales, list SET; "" -> sqrt default). By Claude on 06/24/2026
debugLevel); // int debugLevel)
CuasMotion cuasMotion= cuasDetectRT.detectTargets(
clt_parameters, // CLTParameters clt_parameters,
......
This diff is collapsed.
This diff is collapsed.
#!/usr/bin/env bash
# Start/stop the CUAS DGX inference server (PyTorch RawFCN, cuDNN) in the NGC container.
# By Claude on 06/20/2026. Run on the DGX (elphel@192.168.0.62).
# start|stop|logs|status env: RUN=runs/<model> PORT=5577
# start|stop|logs|status env: RUN=runs/<model> RUN2=runs/<l2> PORT=5577
set -euo pipefail
NAME=cuas_infer
IMG=nvcr.io/nvidia/pytorch:25.10-py3
CODE=/home/elphel/c5p_dnn
RUN="${RUN:-runs/weighted9_pm_s}"
RUN2="${RUN2:-}" # optional Layer-2 run dir; empty -> L1-only. By Claude 06/22/2026
PORT="${PORT:-5577}"
case "${1:-start}" in
start)
docker rm -f "$NAME" >/dev/null 2>&1 || true
L2ARG=""; [ -n "$RUN2" ] && L2ARG="--l2run $RUN2"
docker run -d --name "$NAME" --gpus all --network host \
-v "$CODE":/work -w /work "$IMG" \
python infer_server.py --run "$RUN" --port "$PORT" >/dev/null
echo "started $NAME (run=$RUN port=$PORT)"; sleep 3; docker logs "$NAME"
python infer_server.py --run "$RUN" $L2ARG --port "$PORT" >/dev/null
echo "started $NAME (run=$RUN l2=${RUN2:-off} port=$PORT)"; sleep 3; docker logs "$NAME"
;;
stop) docker rm -f "$NAME" >/dev/null 2>&1 && echo "stopped" || echo "not running" ;;
logs) docker logs --tail 60 "$NAME" ;;
status) docker ps --filter "name=$NAME" --format "{{.Names}} {{.Status}}" ;;
*) echo "usage: $0 {start|stop|logs|status} (env: RUN=, PORT=)"; exit 1 ;;
*) echo "usage: $0 {start|stop|logs|status} (env: RUN=, RUN2=, PORT=)"; exit 1 ;;
esac
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment