After a decoupler split the new vehicle starts with the thruster data of the whole vehicle, so its flight computer Angle Deadband jumps

Maxi

Member
Messages
197
Reaction score
146
(post mostly written by claude, verified be me)

Game Version
v2026.11.10.5801 (reproduced with stock parts only); first found on v2026.10.25.5707

Operating System
Windows 11

What Happened
When a vehicle with RCS thrusters is split by a decoupler, the flight computer of the new (separated) vehicle computes its first RCS parameters from the thruster data of the whole vehicle before the split. In that first step the rate bit on the pitch and yaw axes is far too large, and the flight computer raises the Angle Deadband from it. The deadband is only ever raised, so the wrong value stays.

Stock vehicle on v2026.11.10.5801 (details under Reproduction Steps), Balanced profile (1.000 deg) before the split:
  • 2 carrier tanks (10.2 t in total, probe 3.3 t): Angle Deadband of the probe 1.463 deg after the split. Pitch and yaw rate bit in its first step 7.317 deg/s, from the second step on 0.738 deg/s.
  • 6 carrier tanks (24.1 t in total, the same 3.3 t probe): Angle Deadband of the probe 4.634 deg after the split, Rate Limit 23.17 deg/s. Pitch and yaw rate bit in its first step 23.17 deg/s, from the second step on 0.738 deg/s.
In both cases the value is set in the first flight computer step of the new vehicle and is unchanged 1 s later. The Balanced profile button no longer shows as selected. Clicking Balanced sets the deadband to 1 deg, and the next step raises it to 1.391 deg. That is the correct value for this probe, which comes from its own roll rate bit (6.956 deg/s) and is the same before and after the reset. So the 4.634 deg value is more than three times what the probe's own thrusters need.

On v2026.10.25.5707 I saw larger values on two smaller probes (the carrier had 1.000 deg on Balanced):
  • 21.065 deg for a 129 kg probe with two RCS pods (23 N each)
  • 4.891 deg for a 503 kg probe
With 21 degrees the attitude hold is bad. In my test a 30 degree turn wandered between 2 and 14 degrees at about 0.3 deg/s and never settled to the usual 0.5 degree. After a click on Balanced, the same pods turned 30 degrees in about 5 s and held with a mean error of 0.46 degrees.

What Was Expected
The new vehicle uses the thruster data of its own parts from its first flight computer step. It keeps the attitude profile (Balanced, 1 degree), and the deadband grows only when the thrusters of this vehicle need it.

Reproduction Steps
  1. In the editor, start the vehicle with the tank CoreFuelTankA_Prefab_MP1W1HA, so that this carrier tank is the root part and the probe is the part that separates. Add five more CoreFuelTankA_Prefab_MP1W1HA below it (with only one more tank the effect is there but small).
  2. On top of the first tank add CoreCouplingA_Prefab_Decoupler1WA, then CoreFuelTankA_Prefab_MP1WHalfHA, then the probe core CoreCommandC_Prefab_ProbeCoreSmallA.
  3. Put four CorePropulsionB_Prefab_RCSSmallFourHornV1A on the side of the half-height MP tank, 90 degrees apart. Put the decoupler in a stage of its own.
  4. Launch, and put the vehicle in orbit (for example with Debug > Show Set Orbit Debug). Leave the flight computer on the Balanced profile and RCS on. Nothing else is needed: the attitude mode can stay Manual.
  5. Open Debug > Show Flight Computer Debug. "Angle Deadband" shows 1 (degrees).
  6. Press the staging key to fire the decoupler, and switch to the new probe vehicle. "Angle Deadband" shows about 4.63 and "Rate Limit" about 23.2, and the Balanced profile button is not shown as selected.
  7. Click Balanced. "Angle Deadband" goes to about 1.39 and stays there.
Reproduction Rate
Always. Every split of a vehicle with RCS thrusters on the separated part.

Additional Info
I found this with mods loaded, while I tested my own part mods with automated flight tests. On v2026.11.10.5801 I reproduced it with stock parts only: the vehicle above is built from Core parts, and the test runs the normal game code headless (no content mods; only my test harness and a small test plugin that spawns the vehicle, presses the staging key and reads the values). The test also read the thruster state of the new vehicle at the end of Vehicle.Split, before its first flight computer step. This confirms the cause below.

Technical analysis (decompiled 2026.11.10.5801)
Where the value is set:
  • FlightComputer.UpdateRcsParams computes RateBit = thrusterAuthority.MinRotationalImpulse.Hadamard(inverse inertia) and then raises the deadband with AngleDeadband = MathEx.MaxIfRhsFinite(AngleDeadband, 2 / MaxFlightComputerRate * RateBit) for each axis. It only raises it and never lowers it. RateLimit is raised the same way with RateDeadband. ComputeControl calls UpdateRcsParams in every step, also in the Manual attitude mode.
  • The only code that sets it back is FlightComputer.SetAttitudeProfile, which is called by the profile buttons. FlightComputer.HasAttitudeProfile compares AngleDeadband and RateLimit with the profile values for equality, so after the raise no profile looks selected. PhysicsBubble.ResolveRateLimitForCurrentProfile has the same equality test.
  • With MaxFlightComputerRate = 10, a rate bit of 23.17 deg/s gives 2 / 10 * 23.17 = 4.634 deg, which is the measured value.
Why the first update of the new vehicle gets a wrong rate bit:
  • Vehicle.Split calls PartTree.Split, which moves the parts to a new PartTree through TransferPartSubtreeTo and TransferPartSubtree(part, States), and then part.Modules.AddStatesInto(States, prevStates).
  • That ends in ModuleStateful<...>.StateList.AddFrom(modules, oldStates). When the new list is empty it does _globalState = oldStates._globalState. For the thrusters the global state is ThrusterControllerGlobalState, which holds the cached Authority (TotalTorque, MinRotationalImpulse), MaxThrustFromRotation and the cache key (CachedCenterOfMassAsmb, CachedMass, CachedCtrl2Body). So the new vehicle starts with the thruster authority of the whole vehicle, computed about the centre of mass of the whole vehicle.
  • Measured at the end of Vehicle.Split (6 carrier tanks): the new 3302.5 kg probe vehicle had CachedMass 24101.1 kg, CachedCenterOfMassAsmb X = -2.04 m and MinRotationalImpulse (100.04, 232.37, 232.37) N m s. These are exactly the values of the whole vehicle before the split. After its first step the cache was rebuilt: CachedMass 3302.5 kg, centre of mass X = 0.855 m, MinRotationalImpulse (100.04, 7.398, 7.398) N m s.
  • The new vehicle gets a new FlightComputer in the Vehicle constructor (Balanced, 1 deg). Its first FlightComputer.ComputeControl reads outputs.Thrusters.GlobalState.Authority. The inverse inertia is already the one of the probe, but MinRotationalImpulse is still the value of the whole vehicle. It was built from torque arms about the old centre of mass, about 3 m away from the probe thrusters, and with ThrusterAuthority.ConservativeWith it is the maximum over both directions. The product is a pitch and yaw rate bit about 31 times too large (23.17 against 0.738 deg/s), and UpdateRcsParams stores it in AngleDeadband and RateLimit for good.
  • The stale value is used only once. Rocket.UpdateRockets calls Rocket.UpdateThrusterCache after the flight computer step, and ThrusterControllerGlobalState.IsCacheValid fails because the centre of mass and the mass differ. From the second step the authority is correct. One step is enough, because of the ratchet.
  • The roll axis is not affected in this layout, because the torque arm about the stack axis does not change with the centre of mass along the stack. The error grows with the distance from the old centre of mass to the thrusters of the new vehicle, so a long carrier and a light probe give the largest values.
Suggested fix
Either of these removes the problem. The first one is the root cause:
  1. In StateList.AddFrom (or in Vehicle.Split), do not copy the global state of ThrusterControllerGlobalState to the new tree. Start it from ThrusterControllerGlobalState.Zero with an invalid cache key, so that the first UpdateThrusterCache computes it before the flight computer reads it. The other global states that are copied this way should be checked too.
  2. Make the rate-bit part of the deadband non-persistent. Keep the profile deadband and rate limit as the stored values, and compute the effective values in each update as max(profile value, 2 / MaxFlightComputerRate * RateBit). Then one wrong update cannot stay forever, and HasAttitudeProfile keeps working.
The thruster, mass and torque numbers of the game are correct after the first step, so no part data needs a change.
 
Upvote 0
Back
Top