-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathFlightComputer.cs
456 lines (397 loc) · 19.9 KB
/
FlightComputer.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
namespace RemoteTech
{
public class FlightComputer
{
double torqueRAvailable, torquePYAvailable, torqueThrustPYAvailable;
private int[] rapidToggleX = { 0, 0, 0, 0 }; //Array size determines how many toggles required to trigger the axis rest, init array to 0s
private int[] rapidToggleY = { 0, 0, 0, 0 }; //Negative values indicate the vector was at the minimum threshold
private int[] rapidToggleZ = { 0, 0, 0, 0 };
private int rapidRestX = 0; //How long the axis should rest for if rapid toggle is detected
private int rapidRestY = 0;
private int rapidRestZ = 0;
private int rapidToggleRestFactor = 8; //Factor the axis is divided by if the rapid toggle resting is activated
private Vector3d integral = Vector3d.zero;
private Vector3d[] a_avg_act = new Vector3d[2];
Vector3d prev_err;
public float Kp = 120.0F;
public Quaternion Target;
public AttitudeReference Reference;
bool attitideActive = false;
bool throttleActive = false;
float throttle;
double attitudeError;
RemoteCore core;
Vessel vessel
{
get
{
return core.vessel;
}
}
public FlightComputer(RemoteCore coreIn)
{
this.core = coreIn;
}
public double AttitudeError
{
get
{
return this.attitudeError;
}
}
public Quaternion Direction(Vector3d d, AttitudeReference referer)
{
double ang_diff = Math.Abs(Vector3d.Angle(attitudeGetReferenceRotation(referer) * Target * Vector3d.forward, attitudeGetReferenceRotation(referer) * d));
Vector3 up, dir = d;
if ((ang_diff > 45))
{
up = attitudeWorldToReference(-vessel.ReferenceTransform.forward, referer);
}
else
{
up = attitudeWorldToReference(attitudeReferenceToWorld(Target * Vector3d.up, Reference), referer);
}
Vector3.OrthoNormalize(ref dir, ref up);
return Quaternion.LookRotation(dir, up);
}
public void ShutDown()
{
attitideActive = throttleActive = roverActive = false;
}
public bool roverActive = false;
public double altitude;
public RoverState roverState;
RoverPidController throttlePID;
public void setRover(RoverState StateIn)
{
throttlePID = new RoverPidController(10, 1e-5F, 1e-5F, 50, 1);
this.roverState = StateIn;
altitude = Vector3d.Distance(core.vessel.mainBody.position, core.vessel.transform.position);
roverActive = true;
core.vessel.ActionGroups.SetGroup(KSPActionGroup.Brakes, false);
}
public void SetThrottle(float throttlein)
{
if (throttlein > 0)
this.throttleActive = true;
this.throttle = throttlein;
}
public void SetMode(AttitudeMode mode, AttitudeButtonState input)
{
this.attitideActive = input.Active;
if (!input.Active)
{
return;
}
switch (mode)
{
case AttitudeMode.MANEUVERNODE:
if (input.MN != Quaternion.identity)
{
Target = input.MN;
Reference = AttitudeReference.INERTIAL;
}
else
{
Target = Quaternion.LookRotation(vessel.ReferenceTransform.up, -vessel.ReferenceTransform.forward);
Reference = AttitudeReference.INERTIAL;
}
break;
case AttitudeMode.KILLROT:
Target = Quaternion.LookRotation(vessel.ReferenceTransform.up, -vessel.ReferenceTransform.forward);
Reference = AttitudeReference.INERTIAL;
break;
case AttitudeMode.PROGRADE:
Target = Direction(Vector3d.forward, AttitudeReference.ORBIT);
Reference = AttitudeReference.ORBIT;
break;
case AttitudeMode.RETROGRADE:
Target = Direction(Vector3d.back, AttitudeReference.ORBIT);
Reference = AttitudeReference.ORBIT;
break;
case AttitudeMode.SRF_PROGRADE:
Target = Direction(Vector3d.forward, AttitudeReference.SURFACE_VELOCITY);
Reference = AttitudeReference.SURFACE_VELOCITY;
break;
case AttitudeMode.SRF_RETROGRADE:
Target = Direction(Vector3d.back, AttitudeReference.SURFACE_VELOCITY);
Reference = AttitudeReference.SURFACE_VELOCITY;
break;
case AttitudeMode.NORMAL_PLUS:
Target = Direction(Vector3d.left, AttitudeReference.ORBIT);
Reference = AttitudeReference.ORBIT;
break;
case AttitudeMode.NORMAL_MINUS:
Target = Direction(Vector3d.right, AttitudeReference.ORBIT);
Reference = AttitudeReference.ORBIT;
break;
case AttitudeMode.RADIAL_PLUS:
Target = Direction(Vector3d.up, AttitudeReference.ORBIT);
Reference = AttitudeReference.ORBIT;
break;
case AttitudeMode.RADIAL_MINUS:
Target = Direction(Vector3d.down, AttitudeReference.ORBIT);
Reference = AttitudeReference.ORBIT;
break;
case AttitudeMode.SURFACE:
if (input.USEROL)
Target = Quaternion.AngleAxis(input.HDG, Vector3.up) * Quaternion.AngleAxis(-input.PIT, Vector3.right) * Quaternion.AngleAxis(-input.ROL, Vector3.forward);
else
Target = Direction(Quaternion.AngleAxis(input.HDG, Vector3.up) * Quaternion.AngleAxis(-input.PIT, Vector3.right) * Vector3d.forward, AttitudeReference.SURFACE_NORTH);
Reference = AttitudeReference.SURFACE_NORTH;
break;
}
}
public bool AttitudeActive
{
get
{
return this.attitideActive;
}
}
public void drive(FlightCtrlState s)
{
if (attitideActive)
{
updateAvailableTorque();
attitudeError = Math.Abs(Vector3d.Angle(attitudeGetReferenceRotation(Reference) * Target * Vector3d.forward, vessel.ReferenceTransform.up));
// Used in the drive_limit calculation
double precision = Math.Max(0.5, Math.Min(10.0, (torquePYAvailable + torqueThrustPYAvailable * s.mainThrottle) * 20.0 / MoI.magnitude));
// Direction we want to be facing
Quaternion target = attitudeGetReferenceRotation(Reference) * Target;
Quaternion delta = Quaternion.Inverse(Quaternion.Euler(90, 0, 0) * Quaternion.Inverse(vessel.ReferenceTransform.rotation) * target);
Vector3d deltaEuler = new Vector3d(
(delta.eulerAngles.x > 180) ? (delta.eulerAngles.x - 360.0F) : delta.eulerAngles.x,
-((delta.eulerAngles.y > 180) ? (delta.eulerAngles.y - 360.0F) : delta.eulerAngles.y),
(delta.eulerAngles.z > 180) ? (delta.eulerAngles.z - 360.0F) : delta.eulerAngles.z
);
Vector3d torque = new Vector3d(
torquePYAvailable + torqueThrustPYAvailable * s.mainThrottle,
torqueRAvailable,
torquePYAvailable + torqueThrustPYAvailable * s.mainThrottle
);
Vector3d inertia = Vector3d.Scale(
RTUtils.Sign(angularMomentum) * 1.8f,
Vector3d.Scale(
Vector3d.Scale(angularMomentum, angularMomentum),
RTUtils.Invert(Vector3d.Scale(torque, MoI))
)
);
// Determine the current level of error, this is then used to determine what act to apply
Vector3d err = deltaEuler * Math.PI / 180.0F;
err += RTUtils.Reorder(inertia, 132);
err.Scale(Vector3d.Scale(MoI, RTUtils.Invert(torque)));
prev_err = err;
// Make sure we are taking into account the correct timeframe we are working with
integral += err * TimeWarp.fixedDeltaTime;
// The inital value of act
// Act is ultimately what determines what the pitch, yaw and roll will be set to
// We make alterations to act between here and passing it into the pod controls
Vector3d act = Mathf.Clamp((float)attitudeError, 1.0F, 3.0F) * Kp * err;
//+ Ki * integral + Kd * deriv; //Ki and Kd are always 0 so they have been commented out
// The maximum value the controls may be
float drive_limit = Mathf.Clamp01((float)(err.magnitude * 450 / precision));
// Reduce z by reduceZfactor, z seems to act far too high in relation to x and y
act.z = act.z / 18.0F;
// Reduce all 3 axis to a maximum of drive_limit
act.x = Mathf.Clamp((float)act.x, drive_limit * -1, drive_limit);
act.y = Mathf.Clamp((float)act.y, drive_limit * -1, drive_limit);
act.z = Mathf.Clamp((float)act.z, drive_limit * -1, drive_limit);
// Met is the time in seconds from take off
double met = Planetarium.GetUniversalTime() - vessel.launchTime;
// Reduce effects of controls after launch and returns them gradually
// This helps to reduce large wobbles experienced in the first few seconds
if (met < 3.0F)
{
act = act * ((met / 3.0F) * (met / 3.0F));
}
// Averages out the act with the last few results, determined by the size of the a_avg_act array
act = RTUtils.averageVector3d(a_avg_act, act);
// Looks for rapid toggling of each axis and if found, then rest that axis for a while
// This helps prevents wobbles from getting larger
rapidRestX = RTUtils.restForPeriod(rapidToggleX, act.x, rapidRestX);
rapidRestY = RTUtils.restForPeriod(rapidToggleY, act.y, rapidRestY);
rapidRestZ = RTUtils.restForPeriod(rapidToggleZ, act.z, rapidRestZ);
// Reduce axis by rapidToggleRestFactor if rapid toggle rest has been triggered
if (rapidRestX > 0) act.x = act.x / rapidToggleRestFactor;
if (rapidRestY > 0) act.y = act.y / rapidToggleRestFactor;
if (rapidRestZ > 0) act.z = act.z / rapidToggleRestFactor;
// Sets the SetFlightCtrlState for pitch, yaw and roll
if (!double.IsNaN(act.z)) s.roll = Mathf.Clamp((float)(act.z), -1, 1);
if (!double.IsNaN(act.x)) s.pitch = Mathf.Clamp((float)(act.x), -1, 1);
if (!double.IsNaN(act.y)) s.yaw = Mathf.Clamp((float)(act.y), -1, 1);
}
if (throttleActive)
{
s.mainThrottle = throttle;
if (throttle == 0)
throttleActive = false;
}
if (roverActive)
{
if (roverState.Steer)
{
if (Quaternion.Angle(roverState.roverRotation, core.vessel.ReferenceTransform.rotation) < roverState.Target)
{
s.wheelThrottle = roverState.reverse ? -throttlePID.Control(roverState.Speed - (float)vessel.horizontalSrfSpeed) : throttlePID.Control(roverState.Speed - (float)vessel.horizontalSrfSpeed);
s.wheelSteer = roverState.Steering;
}
else
{
s.wheelThrottle = 0;
s.wheelSteer = 0;
roverActive = false;
core.vessel.ActionGroups.SetGroup(KSPActionGroup.Brakes, true);
}
}
else
{
if ((float)Vector3d.Distance(core.vessel.mainBody.position + altitude * core.vessel.mainBody.GetSurfaceNVector(roverState.latitude, roverState.longitude), core.vessel.transform.position) < roverState.Target)
{
s.wheelThrottle = roverState.reverse ? -throttlePID.Control(roverState.Speed - (float)vessel.horizontalSrfSpeed) : throttlePID.Control(roverState.Speed - (float)vessel.horizontalSrfSpeed);
s.wheelSteer = roverState.Steering;
}
else
{
s.wheelThrottle = 0;
s.wheelSteer = 0;
roverActive = false;
core.vessel.ActionGroups.SetGroup(KSPActionGroup.Brakes, true);
}
}
}
}
public Quaternion attitudeGetReferenceRotation(AttitudeReference reference)
{
Quaternion rotRef = Quaternion.identity;
switch (reference)
{
case AttitudeReference.ORBIT:
rotRef = Quaternion.LookRotation(velocityVesselOrbitNorm, up);
break;
case AttitudeReference.ORBIT_HORIZONTAL:
rotRef = Quaternion.LookRotation(Vector3d.Exclude(up, velocityVesselOrbitNorm), up);
break;
case AttitudeReference.SURFACE_NORTH:
rotRef = rotationSurface;
break;
case AttitudeReference.SURFACE_VELOCITY:
rotRef = Quaternion.LookRotation(velocityVesselSurfaceUnit, up);
break;
case AttitudeReference.CURFRAME:
if(vessel.altitude > vessel.mainBody.maxAtmosphereAltitude)
rotRef = Quaternion.LookRotation(velocityVesselOrbitNorm, up);
else
rotRef = Quaternion.LookRotation(velocityVesselSurfaceUnit, up);
break;
}
return rotRef;
}
public Vector3d attitudeWorldToReference(Vector3d vector, AttitudeReference reference)
{
return Quaternion.Inverse(attitudeGetReferenceRotation(reference)) * vector;
}
public Vector3d attitudeReferenceToWorld(Vector3d vector, AttitudeReference reference)
{
return attitudeGetReferenceRotation(reference) * vector;
}
public Vector3d velocityVesselSurfaceUnit
{
get
{
return (vessel.srf_velocity).normalized;
}
}
public Vector3d velocityVesselOrbitNorm
{
get
{
return vessel.orbit.GetVel().normalized;
}
}
public Vector3d up
{
get
{
return (CoM - vessel.mainBody.position).normalized;
}
}
public Vector3d CoM
{
get
{
return vessel.findWorldCenterOfMass();
}
}
public Quaternion rotationSurface
{
get
{
return Quaternion.LookRotation(Vector3d.Exclude(up, (vessel.mainBody.position + vessel.mainBody.transform.up * (float)vessel.mainBody.Radius) - CoM).normalized, up);
}
}
public Vector3d MoI;
public Vector3d angularMomentum
{
get
{
return new Vector3d((Quaternion.Inverse(vessel.ReferenceTransform.rotation) * vessel.rigidbody.angularVelocity).x * MoI.x, (Quaternion.Inverse(vessel.ReferenceTransform.rotation) * vessel.rigidbody.angularVelocity).y * MoI.y, (Quaternion.Inverse(vessel.ReferenceTransform.rotation) * vessel.rigidbody.angularVelocity).z * MoI.z);
}
}
public void updateAvailableTorque()
{
MoI = vessel.findLocalMOI(CoM);
torqueRAvailable = torquePYAvailable = torqueThrustPYAvailable = 0;
foreach (Part p in vessel.parts)
{
MoI += p.Rigidbody.inertiaTensor;
if (((p.State == PartStates.ACTIVE) || ((Staging.CurrentStage > Staging.lastStage) && (p.inverseStage == Staging.lastStage))))
{
if (p is LiquidEngine && p.RequestFuel(p, 0, Part.getFuelReqId()) && ((LiquidEngine)p).thrustVectoringCapable)
{
torqueThrustPYAvailable += Math.Sin(Math.Abs(((LiquidEngine)p).gimbalRange) * Math.PI / 180) * ((LiquidEngine)p).maxThrust * (p.Rigidbody.worldCenterOfMass - CoM).magnitude;
}
else if (p is LiquidFuelEngine && p.RequestFuel(p, 0, Part.getFuelReqId()) && ((LiquidFuelEngine)p).thrustVectoringCapable)
{
torqueThrustPYAvailable += Math.Sin(Math.Abs(((LiquidFuelEngine)p).gimbalRange) * Math.PI / 180) * ((LiquidFuelEngine)p).maxThrust * (p.Rigidbody.worldCenterOfMass - CoM).magnitude;
}
else if (p is AtmosphericEngine && p.RequestFuel(p, 0, Part.getFuelReqId()) && ((AtmosphericEngine)p).thrustVectoringCapable)
{
torqueThrustPYAvailable += Math.Sin(Math.Abs(((AtmosphericEngine)p).gimbalRange) * Math.PI / 180) * ((AtmosphericEngine)p).maximumEnginePower * ((AtmosphericEngine)p).totalEfficiency * (p.Rigidbody.worldCenterOfMass - CoM).magnitude;
}
if (p.Modules.Contains("ModuleGimbal") && p.Modules.Contains("ModuleEngines"))
{
torqueThrustPYAvailable += Math.Sin(Math.Abs(((ModuleGimbal)p.Modules["ModuleGimbal"]).gimbalRange) * Math.PI / 180) * ((ModuleEngines)p.Modules["ModuleEngines"]).maxThrust * (p.Rigidbody.worldCenterOfMass - CoM).magnitude;
}
}
if (core.RCSoverride && (p is RCSModule || p.Modules.Contains("ModuleRCS")))
{
double maxT = 0;
for (int i = 0; i < 6; i++)
{
if (p is RCSModule && ((RCSModule)p).thrustVectors[i] != Vector3.zero)
{
maxT = Math.Max(maxT, ((RCSModule)p).thrusterPowers[i]);
}
}
torquePYAvailable += maxT * (p.Rigidbody.worldCenterOfMass - CoM).magnitude;
foreach (PartModule m in p.Modules)
{
if (m is ModuleRCS)
torquePYAvailable += ((ModuleRCS)m).thrusterPower * (p.Rigidbody.worldCenterOfMass - CoM).magnitude;
}
}
if (p is CommandPod)
{
torqueRAvailable += Math.Abs(((CommandPod)p).rotPower);
torquePYAvailable += Math.Abs(((CommandPod)p).rotPower);
}
}
}
}
}