Zhen Ye
Rename UI terms: Reason->Detect, Engage->Track
ec62a08
// HEL (High-Energy Laser) Physics and Computation Module
APP.core.hel = {};
// Get current knob values from UI
APP.core.hel.getKnobs = function () {
const { $ } = APP.core.utils;
const helPower = $("#helPower");
const helAperture = $("#helAperture");
const helM2 = $("#helM2");
const helJitter = $("#helJitter");
const helDuty = $("#helDuty");
const helMode = $("#helMode");
const atmVis = $("#atmVis");
const atmCn2 = $("#atmCn2");
const seaSpray = $("#seaSpray");
const aoQ = $("#aoQ");
const rangeBase = $("#rangeBase");
if (!helPower) return {};
return {
PkW: +helPower.value,
aperture: +helAperture.value,
M2: +helM2.value,
jitter_urad: +helJitter.value,
duty: (+helDuty.value) / 100,
mode: helMode ? helMode.value : "cw",
vis_km: +atmVis.value,
cn2: +atmCn2.value,
spray: +seaSpray.value,
ao: +aoQ.value,
baseRange: +rangeBase.value
};
};
// Compute max power at target considering atmospheric effects
APP.core.hel.maxPowerAtTarget = function (range_m) {
const k = APP.core.hel.getKnobs();
if (!k.PkW) return { Ptar: 0, Pout: k.PkW || 0, trans: 0, turb: 0, beam: 0 };
const { clamp } = APP.core.utils;
// Atmospheric transmission (Beer-Lambert approximation)
const sigma_km = 3.912 / Math.max(1, k.vis_km);
const range_km = range_m / 1000;
const trans = Math.exp(-sigma_km * range_km);
// Turbulence factor (simplified Cn² model)
const cn2_factor = clamp(1 - (k.cn2 / 10) * 0.3, 0.5, 1);
const ao_correction = 1 + (k.ao / 10) * 0.25;
const turb = cn2_factor * ao_correction;
// Sea spray attenuation
const spray_factor = 1 - (k.spray / 10) * 0.15;
// Beam quality degradation with range
const beam_spread = 1 + (k.M2 - 1) * (range_km / 5);
const jitter_loss = 1 / (1 + (k.jitter_urad / 10) * (range_km / 3));
const beam = jitter_loss / beam_spread;
// Total power at target
const Pout = k.PkW * k.duty;
const Ptar = Pout * trans * turb * spray_factor * beam;
return {
Ptar: Math.max(0, Ptar),
Pout,
trans,
turb: turb * spray_factor,
beam
};
};
// Estimate required power based on target features
APP.core.hel.requiredPowerFromFeatures = function (feat) {
if (!feat) return 35; // Default
let base = 30; // kW base requirement
// Adjust based on material
const material = (feat.material || "").toLowerCase();
if (material.includes("metal") || material.includes("aluminum")) base *= 1.2;
if (material.includes("composite") || material.includes("carbon")) base *= 0.8;
if (material.includes("plastic") || material.includes("polymer")) base *= 0.6;
// Adjust based on reflectivity
const refl = feat.reflectivity;
if (typeof refl === "number") {
base *= (1 + refl * 0.5); // Higher reflectivity = more power needed
}
// Adjust based on size
const size = feat.physical_size;
if (typeof size === "number") {
base *= Math.max(0.5, Math.min(2, size / 2)); // Assuming size in meters
}
return Math.round(base);
};
// Calculate required dwell time
APP.core.hel.requiredDwell = function (range_m, reqP_kW, maxP_kW, baseDwell_s) {
if (maxP_kW <= 0) return Infinity;
if (maxP_kW >= reqP_kW) return baseDwell_s;
// Dwell inflates quadratically as power drops below requirement
const ratio = reqP_kW / maxP_kW;
return baseDwell_s * ratio * ratio;
};
// Calculate probability of kill
APP.core.hel.pkillFromMargin = function (margin_kW, baseDwell_s, reqDwell_s) {
if (margin_kW <= 0) return 0;
if (reqDwell_s <= 0) return 0;
const dwellRatio = baseDwell_s / reqDwell_s;
const marginFactor = Math.min(1, margin_kW / 50); // Normalize margin
return Math.min(0.99, dwellRatio * marginFactor * 0.95);
};
// Recompute HEL synthesis for all detections
APP.core.hel.recomputeHEL = async function () {
const { state } = APP.core;
const { log } = APP.ui.logging;
const knobs = APP.core.hel.getKnobs();
if (!state.detections || state.detections.length === 0) return;
for (const det of state.detections) {
// Get range from GPT or use baseline
const range = det.gpt_distance_m || knobs.baseRange || 1500;
// Compute power at target
const power = APP.core.hel.maxPowerAtTarget(range);
det.maxP_kW = power.Ptar;
// Required power from features
det.reqP_kW = APP.core.hel.requiredPowerFromFeatures(det.features);
// Dwell calculation
det.baseDwell_s = det.baseDwell_s || 5.0;
det.reqDwell_s = APP.core.hel.requiredDwell(range, det.reqP_kW, det.maxP_kW, det.baseDwell_s);
// P(kill)
const margin = det.maxP_kW - det.reqP_kW;
det.pkill = APP.core.hel.pkillFromMargin(margin, det.baseDwell_s, det.reqDwell_s);
// Store range
det.baseRange_m = range;
}
log("HEL synthesis updated for all targets.", "t");
};
// External hook for HEL synthesis (can be replaced by user)
APP.core.hel.externalHEL = async function (detections, knobs) {
// Default implementation - can be replaced by user
console.log("externalHEL called for", detections.length, "objects", knobs);
return {
targets: {},
system: { maxP_kW: 0, reqP_kW: 0, margin_kW: 0, medianRange_m: 0 }
};
};
// Sync knob display values in the UI
APP.core.hel.syncKnobDisplays = function () {
const { $ } = APP.core.utils;
const helPower = $("#helPower");
const helAperture = $("#helAperture");
const helM2 = $("#helM2");
const helJitter = $("#helJitter");
const helDuty = $("#helDuty");
const atmVis = $("#atmVis");
const atmCn2 = $("#atmCn2");
const seaSpray = $("#seaSpray");
const aoQ = $("#aoQ");
const rangeBase = $("#rangeBase");
const detHz = $("#detHz");
const assessWindow = $("#assessWindow");
const policyMode = $("#policyMode");
if (helPower) {
const v = $("#helPowerVal");
if (v) v.textContent = helPower.value;
}
if (helAperture) {
const v = $("#helApertureVal");
if (v) v.textContent = (+helAperture.value).toFixed(2);
}
if (helM2) {
const v = $("#helM2Val");
if (v) v.textContent = (+helM2.value).toFixed(1);
}
if (helJitter) {
const v = $("#helJitterVal");
if (v) v.textContent = (+helJitter.value).toFixed(1);
}
if (helDuty) {
const v = $("#helDutyVal");
if (v) v.textContent = helDuty.value;
}
if (atmVis) {
const v = $("#atmVisVal");
if (v) v.textContent = atmVis.value;
}
if (atmCn2) {
const v = $("#atmCn2Val");
if (v) v.textContent = atmCn2.value;
}
if (seaSpray) {
const v = $("#seaSprayVal");
if (v) v.textContent = seaSpray.value;
}
if (aoQ) {
const v = $("#aoQVal");
if (v) v.textContent = aoQ.value;
}
if (rangeBase) {
const v = $("#rangeBaseVal");
if (v) v.textContent = rangeBase.value;
}
if (detHz) {
const v = $("#detHzVal");
if (v) v.textContent = detHz.value;
}
if (assessWindow) {
const v = $("#assessWindowVal");
if (v) v.textContent = (+assessWindow.value).toFixed(1);
}
// Update chips
const chipPolicy = $("#chipPolicy");
const chipHz = $("#chipHz");
if (chipPolicy && policyMode) chipPolicy.textContent = `POLICY:${policyMode.value.toUpperCase()}`;
if (chipHz && detHz) chipHz.textContent = `DET:${detHz.value}Hz`;
// Update telemetry footer
const telemetry = $("#telemetry");
if (telemetry && helPower && atmVis && atmCn2 && aoQ && detHz) {
telemetry.textContent = `VIS=${atmVis.value}km · DET=${detHz.value}Hz`;
}
};