// 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 = `HEL=${helPower.value}kW · VIS=${atmVis.value}km · Cn²=${atmCn2.value}/10 · AO=${aoQ.value}/10 · DET=${detHz.value}Hz`; } };