XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFEEDToolBox

【WinUI】XFE精英危险工具箱

公开
关注 0 Fork 0 Star 0
UTF-8
namespace XFEEDToolBox.Core.Navigation;

/// <summary>
/// Bounds supplied by a separately qualified original ordinary-supercruise
/// controller. Merely reading these numbers does not qualify a brake response.
/// Speeds are m/s, accelerations m/s², distances m and times seconds.
/// </summary>
public sealed record ScoBrakingInput
{
    public bool OriginalParametersVerified{get;init;}
    public bool OrdinaryArrivalControllerVerified{get;init;}
    public bool OverspeedBrakeBoundVerified{get;init;}
    public double RemainingDistanceMetres{get;init;}=double.NaN;
    public double CurrentSpeedMetresPerSecond{get;init;}=double.NaN;
    public double OriginalBaseMaximumSpeedMetresPerSecond{get;init;}=double.NaN;
    public double OrdinarySpeedLimitUpperMetresPerSecond{get;init;}=double.NaN;
    public double OriginalBaseOverspeedBrakeAcceleration{get;init;}=double.NaN;
    public int OriginalBrakeScalingMode{get;init;}=-1;
    // Modes 0/1 get weaker as the ordinary scale falls. Their lower bound
    // must cover the whole exit, not just the current provider snapshot.
    public double OriginalOrdinaryBrakeScaleLower{get;init;}=double.NaN;
    public double OriginalExtraBrakeFactorLower{get;init;}=double.NaN;
    public double OriginalArrivalWindowSeconds{get;init;}=double.NaN;
    public double StopResponseSecondsUpper{get;init;}=double.NaN;
    public double ActiveAccelerationMetresPerSecondSquaredUpper{get;init;}=double.NaN;
    public double PositionUncertaintyMetres{get;init;}=double.NaN;
    public double SafeDropDistanceMetres{get;init;}=double.NaN;
}

public sealed record ScoBrakingForecast(bool Known,string Reason)
{
    public bool ContinueOvercharge{get;init;}
    public double ResponseDistanceMetres{get;init;}=double.NaN;
    public double SpeedAtOriginalStopMetresPerSecond{get;init;}=double.NaN;
    public double OverspeedDistanceMetres{get;init;}=double.NaN;
    public double OverspeedSeconds{get;init;}=double.NaN;
    public double OrdinaryEntrySpeedMetresPerSecond{get;init;}=double.NaN;
    public double OrdinaryArrivalEnvelopeMetres{get;init;}=double.NaN;
    public double StopDistanceMetres{get;init;}=double.NaN;
    public bool CoolingDragUsed=>false;
}

/// <summary>
/// Conditional integrals and an explicitly estimated, unconnected exit model.
/// Predict requires an independently established effective brake factor: the
/// native .25 extra brake is NOT an unconditional ordinary-supercruise force.
/// Cooling's additional -lambda*v force is deliberately ignored until its
/// duration is qualified; the current SCO speed is never held for the whole
/// approach. This is an opt-in mathematical forecast, not live qualification.
/// </summary>
public static class ScoBrakingPredictor
{
    /// <summary>
    /// An estimate for a bounded trial, never a live qualification. Cooling gets
    /// no braking credit. Once Off and fully assisted, native mode 2 supplies
    /// A(v,U) >= (A_base/V_base)*v ONLY WHILE v>U. The forward servo request has
    /// the same lower bound above its bias-dependent floor; jerk starts at zero.
    /// The budget maximises D+window*v across possible ordinary handoff speeds.
    /// That union is not a physical exponential continued below the ordinary cap.
    /// </summary>
    public static ScoExitEstimate EstimateExit(ScoExitEstimateInput input)
    {
        if(!input.OriginalParametersVerified)return EstimateUnknown("original-parameters-unverified");
        if(!input.ForwardServoDomainVerified)return EstimateUnknown("forward-servo-domain-unverified");
        if(!input.OrdinaryArrivalControllerVerified)return EstimateUnknown("ordinary-arrival-controller-unverified");
        if(input.OriginalBrakeScalingMode!=2)return EstimateUnknown("mode-2-required");
        var v=input.CurrentSpeedMetresPerSecond;var maximum=input.OriginalBaseMaximumSpeedMetresPerSecond;
        var brake=input.OriginalBaseBrakeAcceleration;var jerk=input.OriginalJerkMetresPerSecondCubed;
        var throttle=input.ForwardThrottle;var bias=input.OriginalForwardTargetBiasMetresPerSecond;
        var response=input.StopResponseSecondsUpper;var settle=input.EstimatedCoolingAndAssistSettleSeconds;
        var physicsStep=input.MaximumPhysicsStepSeconds;var window=input.OriginalArrivalWindowSeconds;
        if(!Positive(v)||!Positive(maximum)||v>=maximum||!Positive(brake)||!Positive(jerk)
            ||!Nonnegative(throttle)||throttle>.75||!Nonnegative(bias)||!Positive(input.OriginalResponseTimeSeconds)
            ||!Positive(input.CurrentOrdinarySpeedLimitMetresPerSecond)||input.CurrentOrdinarySpeedLimitMetresPerSecond>maximum
            ||!Nonnegative(response)||response>1||!Nonnegative(settle)||settle>30
            ||!Positive(physicsStep)||physicsStep>1||!Nonnegative(input.AdditionalResponseMarginSeconds)
            ||!Nonnegative(input.ActiveAccelerationUpper)||!Positive(window)||window>60
            ||!Positive(input.MinimumOrdinaryHandoffSpeedMetresPerSecond)
            ||!Nonnegative(input.DistanceUncertaintyMetres)||!Nonnegative(input.SafeDropDistanceMetres)
            ||!Nonnegative(input.RelativeDistanceMargin)||input.RelativeDistanceMargin>1
            ||!Positive(input.RemainingDistanceMetres))return EstimateUnknown("invalid-exit-estimate-input");

        var initial=v+input.ActiveAccelerationUpper*response;
        if(!Positive(initial)||initial>=maximum)return EstimateUnknown("below-base-speed-domain-exceeded");
        if(v<=input.CurrentOrdinarySpeedLimitMetresPerSecond)return EstimateUnknown("forward-overspeed-domain-required");
        var tauUpper=Math.Max(Math.Max(.5,input.OriginalResponseTimeSeconds),physicsStep);
        var sigma=(1-throttle)/tauUpper;
        // If the native clamp's rate is too large for the bias servo, half of
        // sigma is a mathematical lower bound above 2*bias, not a fitted brake.
        var rate=brake/maximum;
        if(rate>=sigma)rate=sigma/2;
        var floor=Math.Max(input.MinimumOrdinaryHandoffSpeedMetresPerSecond,bias*sigma/(sigma-rate));
        if(!Positive(rate)||!Positive(floor)||floor>=initial
            ||input.CurrentOrdinarySpeedLimitMetresPerSecond<floor)return EstimateUnknown("ordinary-handoff-floor-unavailable");

        var path=CreatePath(initial,rate,jerk,floor);
        if(path is null)return EstimateUnknown("exit-budget-overflow");
        // For any actual earlier handoff the budget is D(t)+W*v(t).
        // In the exponential domain its derivative is v*(1-W*rate).
        // In the jerk ramp it is v-W*J*t and has one positive root.
        var selected=path.At(0);var selectedTime=0d;
        void Consider(double time)
        {
            var point=path.At(time);
            if(point.Distance+window*point.Speed>selected.Distance+window*selected.Speed)
            {selected=point;selectedTime=time;}
        }
        Consider(path.FloorSeconds);
        var critical=2*initial/jerk/(Math.Sqrt(window*window+2*initial/jerk)+window);
        if(critical>0&&critical<Math.Min(path.RampSeconds,path.FloorSeconds))Consider(critical);

        var responseDistance=v*response+.5*input.ActiveAccelerationUpper*response*response;
        var noBrakeDelay=settle+physicsStep+input.AdditionalResponseMarginSeconds;
        var transitionDistance=responseDistance+initial*noBrakeDelay;
        var ordinaryEnvelope=window*selected.Speed;
        var overspeedBudget=selected.Distance+ordinaryEnvelope;
        var uncertainty=input.DistanceUncertaintyMetres+input.SafeDropDistanceMetres+input.RelativeDistanceMargin*overspeedBudget;
        var total=Math.BitIncrement(transitionDistance+overspeedBudget+uncertainty);
        if(!Positive(total))return EstimateUnknown("exit-budget-overflow");
        return new(true,"estimated-no-cooling-credit-jerk-and-native-mode-2-handoff-union")
        {
            RemainingDistanceExceedsEstimate=input.RemainingDistanceMetres>total,
            EstimatedStopDistanceMetres=total,NoBrakeTransitionDistanceMetres=transitionDistance,
            OverspeedBudgetDistanceMetres=selected.Distance,OrdinaryArrivalEnvelopeMetres=ordinaryEnvelope,
            MarginDistanceMetres=uncertainty,OffBudgetInitialSpeedMetresPerSecond=initial,
            DecayRatePerSecond=rate,ServoDomainFloorMetresPerSecond=floor,
            JerkRampSeconds=path.RampSeconds,DomainFloorSeconds=path.FloorSeconds,
            BudgetHandoffSeconds=selectedTime,BudgetHandoffSpeedMetresPerSecond=selected.Speed,
            NoBrakeDelaySeconds=response+noBrakeDelay,AssistSettleDeadlineSeconds=response+settle
        };
    }

    /// <summary>
    /// Guards a future bounded trial using fresh, independently verified native
    /// observations. It sends no command. A real cap crossing ends this model;
    /// ordinary cruise may then accelerate and remains the arrival controller's
    /// job. A missed settle deadline or a slower brake invalidates the estimate.
    /// </summary>
    public static ScoExitEstimateCheck CheckExitEstimate(ScoExitEstimateInput input,ScoExitEstimate estimate,ScoExitEstimateObservation value)
    {
        if(!estimate.Known)return new(false,false,"estimate-unavailable");
        if(!value.FreshBindingAndSourceGatesVerified)return new(false,false,"native-binding-or-source-gates-changed");
        if(!Nonnegative(value.ElapsedSeconds)||!Nonnegative(value.SpeedMetresPerSecond)
            ||!Positive(value.OrdinarySpeedLimitMetresPerSecond)||value.OrdinarySpeedLimitMetresPerSecond>input.OriginalBaseMaximumSpeedMetresPerSecond
            ||!Positive(value.NativeStepSeconds)
            ||value.NativeStepSeconds>input.MaximumPhysicsStepSeconds
            ||!Nonnegative(value.FlightAssistBlend)||value.FlightAssistBlend>1
            ||!Nonnegative(value.AllowedSpeedResidualFraction)||value.AllowedSpeedResidualFraction>1
            ||!Nonnegative(value.AllowedSpeedResidualMetresPerSecond))
            return new(false,false,"invalid-exit-observation");
        if(value.ElapsedSeconds>input.StopResponseSecondsUpper&&value.ScoState is not 0 and not 3)
            return new(false,false,"original-stop-not-observed");
        if(value.FlightAssistBlend<1&&value.SpeedMetresPerSecond<value.OrdinarySpeedLimitMetresPerSecond)
            return new(false,false,"mixed-assist-below-ordinary-cap");
        if(value.ScoState==0&&value.FlightAssistBlend==1&&value.SpeedMetresPerSecond<=value.OrdinarySpeedLimitMetresPerSecond)
            return new(true,true,"actual-ordinary-cap-handoff");
        if(value.ElapsedSeconds>estimate.AssistSettleDeadlineSeconds&&(value.ScoState!=0||value.FlightAssistBlend!=1))
            return new(false,false,"estimated-cooling-or-assist-settle-overrun");
        if(value.SpeedMetresPerSecond<estimate.ServoDomainFloorMetresPerSecond
            ||value.OrdinarySpeedLimitMetresPerSecond<estimate.ServoDomainFloorMetresPerSecond)
            return new(false,false,"actual-ordinary-handoff-required-at-domain-floor");
        var path=CreatePath(estimate.OffBudgetInitialSpeedMetresPerSecond,estimate.DecayRatePerSecond,
            input.OriginalJerkMetresPerSecondCubed,estimate.ServoDomainFloorMetresPerSecond);
        if(path is null)return new(false,false,"exit-budget-overflow");
        var elapsed=Math.Max(0,value.ElapsedSeconds-estimate.NoBrakeDelaySeconds);
        if(elapsed>path.FloorSeconds)return new(false,false,"overspeed-domain-time-exceeded");
        var upper=path.At(elapsed).Speed;
        upper+=upper*value.AllowedSpeedResidualFraction+value.AllowedSpeedResidualMetresPerSecond;
        return value.SpeedMetresPerSecond>upper
            ?new(false,false,"observed-brake-slower-than-estimate")
            :new(true,false,"within-estimated-overspeed-domain");
    }

    /// <summary>
    /// One scalar original-physics replay step. Caller must supply the actual
    /// physics dt and current cap; sparse sample dt values cannot be summed to
    /// reconstruct unobserved frames. No quarter-brake shortcut is used.
    /// Valid for full FA, or mixed FA while forward speed stays above cap and
    /// below base maximum (so the unassisted forward/extra-brake terms are zero).
    /// </summary>
    public static ScoServoStepResult StepOriginalServo(ScoServoParameters parameters,ScoServoStepInput value)
    {
        var speed=value.SpeedMetresPerSecond;var maximum=parameters.BaseMaximumSpeedMetresPerSecond;
        var cap=value.OrdinaryPhysicsMaximumSpeedMetresPerSecond;var blend=value.FlightAssistBlend;
        if(!parameters.PinnedSourceDomainVerified)return ServoUnknown("pinned-scalar-source-domain-unverified");
        if(!Positive(maximum)||!Positive(parameters.BaseForwardAcceleration)||!Positive(parameters.BaseBrakeAcceleration)
            ||!Positive(parameters.ResponseTimeSeconds)||!Positive(parameters.JerkMetresPerSecondCubed)
            ||!Nonnegative(parameters.ForwardTargetBiasMetresPerSecond)||!Nonnegative(parameters.ForwardThrottle)||parameters.ForwardThrottle>.75
            ||!Nonnegative(speed)||speed>=maximum||!Positive(cap)||cap>maximum
            ||!Nonnegative(blend)||blend>1||!Nonnegative(value.PreviousServoAccelerationNorm)
            ||!Nonnegative(value.CoolingDragPerSecond)||!Positive(value.NativeStepSeconds)||value.NativeStepSeconds>1
            ||(blend<1&&speed<cap))return ServoUnknown("outside-original-scalar-domain");
        var scale=cap/maximum;var ratio=speed/maximum;
        var brake=parameters.BaseBrakeAcceleration*Math.Max(scale,ratio*Math.Max(1,Math.Sqrt(ratio/scale)));
        var target=(cap-parameters.ForwardTargetBiasMetresPerSecond)*parameters.ForwardThrottle+parameters.ForwardTargetBiasMetresPerSecond;
        var tau=Math.Max(.5,parameters.ResponseTimeSeconds*Math.Max(Math.Clamp(ratio,0,1)*4/3-1d/3,0));
        var requested=(target-speed)/Math.Max(tau,value.NativeStepSeconds);
        var clamped=Math.Clamp(requested,-brake,parameters.BaseForwardAcceleration*scale);
        var norm=Math.Min(Math.Abs(clamped),value.PreviousServoAccelerationNorm+parameters.JerkMetresPerSecondCubed*value.NativeStepSeconds);
        var acceleration=Math.CopySign(norm,clamped)*blend;
        // The original lambda force is inside if(B>0), not multiplied by B.
        if(value.Cooling&&blend>0)acceleration-=value.CoolingDragPerSecond*speed;
        var next=Math.Max(0,speed+acceleration*value.NativeStepSeconds);
        return new(true,"original-forward-servo-scalar-step")
        {NextSpeedMetresPerSecond=next,NextServoAccelerationNorm=norm,AppliedAcceleration=acceleration,
            NativeBrakeClampAcceleration=brake,TargetSpeedMetresPerSecond=target,EffectiveResponseTimeSeconds=tau,
            ApproximateDistanceMetres=(speed+next)*.5*value.NativeStepSeconds};
    }

    private static ScoExitEstimate EstimateUnknown(string reason)=>new(false,reason);
    private static ScoServoStepResult ServoUnknown(string reason)=>new(false,reason);
    private sealed record BudgetPoint(double Speed,double Distance);
    private sealed record BudgetPath(double Initial,double Rate,double Jerk,double Floor,double RampSeconds,double RampEndSpeed,double FloorSeconds)
    {
        public BudgetPoint At(double seconds)
        {
            if(seconds<=RampSeconds)return new(Initial-.5*Jerk*seconds*seconds,Initial*seconds-Jerk*seconds*seconds*seconds/6);
            var speed=RampEndSpeed*Math.Exp(-Rate*(seconds-RampSeconds));
            return new(speed,Initial*RampSeconds-Jerk*RampSeconds*RampSeconds*RampSeconds/6+(RampEndSpeed-speed)/Rate);
        }
    }
    private static BudgetPath? CreatePath(double initial,double rate,double jerk,double floor)
    {
        // J*t = rate*(v0-.5*J*t²), evaluated without subtracting close roots.
        var ramp=2*rate*initial/jerk/(1+Math.Sqrt(1+2*rate*rate*initial/jerk));
        var end=initial-.5*jerk*ramp*ramp;
        var floorTime=floor>=end?Math.Sqrt(2*(initial-floor)/jerk):ramp+Math.Log(end/floor)/rate;
        return Positive(ramp)&&Positive(end)&&Nonnegative(floorTime)
            ?new(initial,rate,jerk,floor,ramp,end,floorTime):null;
    }
    public static ScoBrakingForecast Predict(ScoBrakingInput value)
    {
        if(!value.OriginalParametersVerified)return Unknown("original-parameters-unverified");
        if(!value.OrdinaryArrivalControllerVerified)return Unknown("ordinary-arrival-controller-unverified");
        if(!value.OverspeedBrakeBoundVerified)return Unknown("ordinary-overspeed-brake-bound-unverified");
        var remaining=value.RemainingDistanceMetres;var current=value.CurrentSpeedMetresPerSecond;
        var maximum=value.OriginalBaseMaximumSpeedMetresPerSecond;var ordinary=value.OrdinarySpeedLimitUpperMetresPerSecond;
        var acceleration=value.OriginalBaseOverspeedBrakeAcceleration;var factor=value.OriginalExtraBrakeFactorLower;
        var window=value.OriginalArrivalWindowSeconds;var response=value.StopResponseSecondsUpper;
        var activeAcceleration=value.ActiveAccelerationMetresPerSecondSquaredUpper;
        if(!Positive(remaining)||!Nonnegative(current)||!Positive(maximum)||!Positive(ordinary)||ordinary>maximum
            ||!Positive(acceleration)||!Positive(factor)||factor>.25||value.OriginalBrakeScalingMode is <0 or >2
            ||!Positive(window)||window>60||!Nonnegative(response)||response>1||!Nonnegative(activeAcceleration)
            ||!Nonnegative(value.PositionUncertaintyMetres)||!Nonnegative(value.SafeDropDistanceMetres))return Unknown("invalid-original-braking-input");
        var speed=current+activeAcceleration*response;
        var responseDistance=current*response+.5*activeAcceleration*response*response;
        if(!Nonnegative(speed)||!Nonnegative(responseDistance))return Unknown("response-overflow");
        var scale=ordinary/maximum;
        if(!Positive(scale)||scale>1)return Unknown("ordinary-scale-underflow");
        if(value.OriginalBrakeScalingMode is 0 or 1
            &&(!Positive(value.OriginalOrdinaryBrakeScaleLower)||value.OriginalOrdinaryBrakeScaleLower>scale))
            return Unknown("ordinary-brake-scale-lower-bound-unverified");
        var entry=Math.Min(speed,ordinary);var overspeedDistance=0d;var overspeedSeconds=0d;
        if(speed>ordinary)
        {
            var delta=speed-ordinary;
            if(value.OriginalBrakeScalingMode==2)
            {
                // Above Vordinary, the original mode-2 maximum selects
                // a(v)=factor*A_base*(v/V_base)^(3/2)/sqrt(scale).
                // Its exact integrals avoid both time stepping and treating
                // the much higher SCO speed as a constant coast speed.
                var rootScale=Math.Sqrt(scale);var rootHigh=Math.Sqrt(speed/maximum);
                var denominator=factor*acceleration*(rootHigh+rootScale);
                overspeedDistance=2*maximum*rootScale*delta/denominator;
                overspeedSeconds=2*delta/(denominator*rootHigh);
            }
            else
            {
                var lower=value.OriginalOrdinaryBrakeScaleLower;
                var deceleration=factor*acceleration*(value.OriginalBrakeScalingMode==0?lower:Math.Sqrt(lower));
                overspeedDistance=delta*(speed+ordinary)/(2*deceleration);
                overspeedSeconds=delta/deceleration;
            }
        }
        var ordinaryEnvelope=entry*window;
        var total=responseDistance+overspeedDistance+ordinaryEnvelope+value.PositionUncertaintyMetres+value.SafeDropDistanceMetres;
        if(!Nonnegative(overspeedDistance)||!Nonnegative(overspeedSeconds)||!Nonnegative(ordinaryEnvelope)||!Positive(total))return Unknown("braking-integration-overflow");
        // Round away from a later stop when summing very different magnitudes.
        total=Math.BitIncrement(total);
        if(!Positive(total))return Unknown("braking-integration-overflow");
        return new(true,"original-overspeed-integral-and-ordinary-arrival-envelope")
        {
            ContinueOvercharge=remaining>total,ResponseDistanceMetres=responseDistance,SpeedAtOriginalStopMetresPerSecond=speed,
            OverspeedDistanceMetres=overspeedDistance,OverspeedSeconds=overspeedSeconds,OrdinaryEntrySpeedMetresPerSecond=entry,
            OrdinaryArrivalEnvelopeMetres=ordinaryEnvelope,StopDistanceMetres=total
        };
    }
    private static ScoBrakingForecast Unknown(string reason)=>new(false,reason);
    private static bool Positive(double value)=>double.IsFinite(value)&&value>0;
    private static bool Nonnegative(double value)=>double.IsFinite(value)&&value>=0;
}

/// <summary>
/// All units SI. Verified domain means fresh pinned identity/mode 2, straight
/// positive local-Z, u<=.75, unit translation scale, disabled external motion
/// and target-velocity correction, full negative clamp, and an ordinary arrival
/// controller ready to accept actual v<=cap. Future Cooling/assist settlement
/// is deliberately an estimate with an online deadline, never a proven bound.
/// </summary>
public sealed record ScoExitEstimateInput
{
    public bool OriginalParametersVerified{get;init;}
    public bool ForwardServoDomainVerified{get;init;}
    public bool OrdinaryArrivalControllerVerified{get;init;}
    public int OriginalBrakeScalingMode{get;init;}=-1;
    public double RemainingDistanceMetres{get;init;}=double.NaN;
    public double CurrentSpeedMetresPerSecond{get;init;}=double.NaN;
    public double CurrentOrdinarySpeedLimitMetresPerSecond{get;init;}=double.NaN;
    public double OriginalBaseMaximumSpeedMetresPerSecond{get;init;}=double.NaN;
    public double OriginalBaseBrakeAcceleration{get;init;}=double.NaN;
    public double OriginalResponseTimeSeconds{get;init;}=double.NaN;
    public double OriginalJerkMetresPerSecondCubed{get;init;}=double.NaN;
    public double OriginalForwardTargetBiasMetresPerSecond{get;init;}=double.NaN;
    public double ForwardThrottle{get;init;}=double.NaN;
    public double StopResponseSecondsUpper{get;init;}=double.NaN;
    public double ActiveAccelerationUpper{get;init;}=double.NaN;
    public double EstimatedCoolingAndAssistSettleSeconds{get;init;}=double.NaN;
    public double MaximumPhysicsStepSeconds{get;init;}=double.NaN;
    public double AdditionalResponseMarginSeconds{get;init;}=double.NaN;
    public double MinimumOrdinaryHandoffSpeedMetresPerSecond{get;init;}=double.NaN;
    public double OriginalArrivalWindowSeconds{get;init;}=double.NaN;
    public double RelativeDistanceMargin{get;init;}=double.NaN;
    public double DistanceUncertaintyMetres{get;init;}=double.NaN;
    public double SafeDropDistanceMetres{get;init;}=double.NaN;
}
public sealed record ScoExitEstimate(bool Known,string Reason)
{
    public bool Estimated=>true;
    public bool QualifiedForControl=>false;
    public bool CoolingDragUsed=>false;
    public bool RequiresActualOrdinaryHandoff=>true;
    public bool RemainingDistanceExceedsEstimate{get;init;}
    public double EstimatedStopDistanceMetres{get;init;}=double.NaN;
    public double NoBrakeTransitionDistanceMetres{get;init;}=double.NaN;
    public double OverspeedBudgetDistanceMetres{get;init;}=double.NaN;
    public double OrdinaryArrivalEnvelopeMetres{get;init;}=double.NaN;
    public double MarginDistanceMetres{get;init;}=double.NaN;
    public double OffBudgetInitialSpeedMetresPerSecond{get;init;}=double.NaN;
    public double DecayRatePerSecond{get;init;}=double.NaN;
    public double ServoDomainFloorMetresPerSecond{get;init;}=double.NaN;
    public double JerkRampSeconds{get;init;}=double.NaN;
    public double DomainFloorSeconds{get;init;}=double.NaN;
    public double BudgetHandoffSeconds{get;init;}=double.NaN;
    public double BudgetHandoffSpeedMetresPerSecond{get;init;}=double.NaN;
    public double NoBrakeDelaySeconds{get;init;}=double.NaN;
    public double AssistSettleDeadlineSeconds{get;init;}=double.NaN;
}
public sealed record ScoExitEstimateObservation
{
    public bool FreshBindingAndSourceGatesVerified{get;init;}
    public double ElapsedSeconds{get;init;}=double.NaN;
    public double SpeedMetresPerSecond{get;init;}=double.NaN;
    public double OrdinarySpeedLimitMetresPerSecond{get;init;}=double.NaN;
    public double NativeStepSeconds{get;init;}=double.NaN;
    public double FlightAssistBlend{get;init;}=double.NaN;
    public int ScoState{get;init;}=-1;
    public double AllowedSpeedResidualFraction{get;init;}=double.NaN;
    public double AllowedSpeedResidualMetresPerSecond{get;init;}=double.NaN;
}
public sealed record ScoExitEstimateCheck(bool WithinEstimatedDomain,bool OrdinaryHandoffReady,string Reason)
{
    public bool QualifiedForControl=>false;
}
public sealed record ScoServoParameters
{
    public bool PinnedSourceDomainVerified{get;init;}
    public double BaseMaximumSpeedMetresPerSecond{get;init;}=double.NaN;
    public double BaseForwardAcceleration{get;init;}=double.NaN;
    public double BaseBrakeAcceleration{get;init;}=double.NaN;
    public double ResponseTimeSeconds{get;init;}=double.NaN;
    public double JerkMetresPerSecondCubed{get;init;}=double.NaN;
    public double ForwardTargetBiasMetresPerSecond{get;init;}=double.NaN;
    public double ForwardThrottle{get;init;}=double.NaN;
}
public sealed record ScoServoStepInput
{
    public double SpeedMetresPerSecond{get;init;}=double.NaN;
    public double OrdinaryPhysicsMaximumSpeedMetresPerSecond{get;init;}=double.NaN;
    public double PreviousServoAccelerationNorm{get;init;}=double.NaN;
    public double FlightAssistBlend{get;init;}=double.NaN;
    public bool Cooling{get;init;}
    public double CoolingDragPerSecond{get;init;}=double.NaN;
    public double NativeStepSeconds{get;init;}=double.NaN;
}
public sealed record ScoServoStepResult(bool Known,string Reason)
{
    public bool QualifiedForControl=>false;
    public double NextSpeedMetresPerSecond{get;init;}=double.NaN;
    public double NextServoAccelerationNorm{get;init;}=double.NaN;
    public double AppliedAcceleration{get;init;}=double.NaN;
    public double NativeBrakeClampAcceleration{get;init;}=double.NaN;
    public double TargetSpeedMetresPerSecond{get;init;}=double.NaN;
    public double EffectiveResponseTimeSeconds{get;init;}=double.NaN;
    public double ApproximateDistanceMetres{get;init;}=double.NaN;
}