import{readVerniers,dualDifference,wrap}from'./angles.mjs';export{readVerniers,dualDifference,wrap};export const MODEL_VERSION='1.0.0',EXPERIMENT_ID='course-04/prism-refractive-index';export const DEFAULTS=Object.freeze({apex_deg:60,refractive_index:1.5,incidence_deg:48.590377890729144,zero_deg:10,eccentricity_arcmin:5}); export function validateParameters(p){for(const[k,l,h]of[['apex_deg',40,70],['refractive_index',1.3,1.7],['incidence_deg',0,85],['zero_deg',0,360],['eccentricity_arcmin',0,10]])if(!Number.isFinite(p[k])||p[k]h)throw new RangeError(`${k}须在${l}–${h}`);return{...p};} export function minimum(p){validateParameters(p);const a=p.apex_deg*Math.PI/180,arg=p.refractive_index*Math.sin(a/2);if(arg>=1)throw new RangeError('没有透射最小偏向光路');const incidence=Math.asin(arg)*180/Math.PI;return{incidence_deg:incidence,deviation_deg:2*incidence-p.apex_deg};} export function deviationAt(incidence_deg,p){validateParameters(p);if(!Number.isFinite(incidence_deg)||incidence_deg<0||incidence_deg>85)throw new RangeError('入射角0–85°');const a=p.apex_deg*Math.PI/180,i=incidence_deg*Math.PI/180,r1=Math.asin(Math.sin(i)/p.refractive_index),r2=a-r1;if(r2<0||r2>=Math.PI/2||p.refractive_index*Math.sin(r2)>=1)throw new RangeError('当前第二面全反射,没有出射光线');return incidence_deg+Math.asin(p.refractive_index*Math.sin(r2))*180/Math.PI-p.apex_deg;} export function forward(p){validateParameters(p);return{reference_deg:0,target_deg:deviationAt(p.incidence_deg,p),true_value:p.refractive_index,unit:'1',apex_deg:p.apex_deg};} export function inverse(delta,p){validateParameters(p);return Math.sin((p.apex_deg+Math.abs(delta))*Math.PI/360)/Math.sin(p.apex_deg*Math.PI/360);} export function measurement(p,source='ideal-model',start=null,end=null){validateParameters(p);if(!['ideal-model','manual-telescope'].includes(source))throw new RangeError('记录身份无效');const f=forward(p),a=source==='ideal-model'?readVerniers(f.reference_deg,p):start,b=source==='ideal-model'?readVerniers(f.target_deg,p):end;if(!a||!b)throw new RangeError('先保存基准与目标方向');const diff=dualDifference(a,b);return{experiment_id:EXPERIMENT_ID,model_version:MODEL_VERSION,source,parameters:{...p},start:a,end:b,...diff,estimate:inverse(diff.average_difference_deg,p),expected:f.true_value,unit:f.unit,minimum_incidence_deg:minimum(p).incidence_deg,at_minimum:Math.abs(p.incidence_deg-minimum(p).incidence_deg)<1e-8};}