@@ -10,22 +10,6 @@ const CelestialBody = constants.CelestialBody;
10
10
11
11
const Spacecraft = @This ();
12
12
13
- /// State Vector - Used for position and velocity knowledge
14
- const StateV = [6 ]f64 ;
15
-
16
- /// Contains time and state vector to be used during propagation
17
- const StateTime = struct {
18
- time : f64 ,
19
- state : StateV ,
20
- };
21
-
22
- // an easy win would be to combine this with StateTime to form a *spacecraftState* struct to deal with all of this
23
- /// Responsible for spacecraft orientation
24
- const AttitudeState = struct {
25
- quaternion : [4 ]f64 ,
26
- angularVelocity : [3 ]f64 ,
27
- };
28
-
29
13
/// Satellite details used in calculations
30
14
pub const SatelliteParameters = struct {
31
15
drag : f64 ,
@@ -47,16 +31,6 @@ pub const Impulse = struct {
47
31
} = null ,
48
32
};
49
33
50
- /// Needed for propagation.
51
- pub const OrbitalElements = struct {
52
- a : f64 ,
53
- e : f64 ,
54
- i : f64 ,
55
- raan : f64 ,
56
- argPeriapsis : f64 ,
57
- trueAnomaly : f64 ,
58
- };
59
-
60
34
/// Determines the values in the SatelliteParameters struct
61
35
pub const SatelliteSize = enum {
62
36
Cube ,
@@ -108,7 +82,7 @@ inertiaTensor: [3][3]f64,
108
82
bodyVectors : [2 ][3 ]f64 ,
109
83
referenceVectors : [2 ][3 ]f64 ,
110
84
orbitingObject : CelestialBody = constants .earth ,
111
- orbitPredictions : std .ArrayList (StateTime ),
85
+ orbitPredictions : std .ArrayList (calculations . StateTime ),
112
86
allocator : std.mem.Allocator ,
113
87
114
88
pub fn init (name : []const u8 , tle : Tle , mass : f64 , size : SatelliteSize , orbitingObject : ? CelestialBody , allocator : std.mem.Allocator ) Spacecraft {
@@ -133,7 +107,7 @@ pub fn init(name: []const u8, tle: Tle, mass: f64, size: SatelliteSize, orbiting
133
107
.{ 0.0 , 1.0 , 0.0 },
134
108
},
135
109
.orbitingObject = orbitingObject .? ,
136
- .orbitPredictions = std .ArrayList (StateTime ).init (allocator ),
110
+ .orbitPredictions = std .ArrayList (calculations . StateTime ).init (allocator ),
137
111
.allocator = allocator ,
138
112
};
139
113
}
@@ -143,12 +117,12 @@ pub fn deinit(self: *Spacecraft) void {
143
117
}
144
118
145
119
pub fn updateAttitude (self : * Spacecraft ) void {
146
- const attitudeMatrix = triad (self .bodyVectors [0 ], self .bodyVectors [1 ], self .referenceVectors [0 ], self .referenceVectors [1 ]);
147
- self .quaternion = matrixToQuaternion (attitudeMatrix );
120
+ const attitudeMatrix = calculations . triad (self .bodyVectors [0 ], self .bodyVectors [1 ], self .referenceVectors [0 ], self .referenceVectors [1 ]);
121
+ self .quaternion = calculations . matrixToQuaternion (attitudeMatrix );
148
122
}
149
123
150
124
pub fn propagateAttitude (self : * Spacecraft , dt : f64 ) void {
151
- const state = AttitudeState { .quaternion = self .quaternion , .angularVelocity = self .angularVelocity };
125
+ const state = calculations. AttitudeState { .quaternion = self .quaternion , .angularVelocity = self .angularVelocity };
152
126
const newState = rk4Attitude (self , state , dt );
153
127
self .quaternion = newState .quaternion ;
154
128
self .angularVelocity = newState .angularVelocity ;
@@ -166,7 +140,7 @@ pub fn propagate(self: *Spacecraft, t0: f64, days: f64, h: f64, impulseList: ?[]
166
140
167
141
log .info ("Initial energy established: {d}\n " , .{initialEnergy });
168
142
169
- try self .orbitPredictions .append (StateTime { .time = t , .state = y });
143
+ try self .orbitPredictions .append (calculations. StateTime { .time = t , .state = y });
170
144
var step : usize = 0 ;
171
145
var impulseIndex : usize = 0 ;
172
146
@@ -177,12 +151,12 @@ pub fn propagate(self: *Spacecraft, t0: f64, days: f64, h: f64, impulseList: ?[]
177
151
if (dt > 0 ) {
178
152
y = self .rk4 (y , dt );
179
153
t += dt ;
180
- try self .orbitPredictions .append (StateTime { .time = t , .state = y });
154
+ try self .orbitPredictions .append (calculations. StateTime { .time = t , .state = y });
181
155
}
182
156
183
157
switch (impulses [impulseIndex ].mode ) {
184
158
.Absolute = > {
185
- y = impulse (y , impulses [impulseIndex ].deltaV );
159
+ y = calculations . impulse (y , impulses [impulseIndex ].deltaV );
186
160
log .info ("Impulse completed at t={d:.2}s" , .{t });
187
161
},
188
162
.Prograde = > {
@@ -193,7 +167,7 @@ pub fn propagate(self: *Spacecraft, t0: f64, days: f64, h: f64, impulseList: ?[]
193
167
y [4 ] / velocityMagnitude * deltaVMagnitude ,
194
168
y [5 ] / velocityMagnitude * deltaVMagnitude ,
195
169
};
196
- y = impulse (y , deltaV );
170
+ y = calculations . impulse (y , deltaV );
197
171
log .info ("Prograde impulse completed at t={d:.2}s" , .{t });
198
172
},
199
173
.Phase = > {
@@ -207,27 +181,27 @@ pub fn propagate(self: *Spacecraft, t0: f64, days: f64, h: f64, impulseList: ?[]
207
181
y [4 ] / velocityMagnitude * deltaVMagnitude ,
208
182
y [5 ] / velocityMagnitude * deltaVMagnitude ,
209
183
};
210
- y = impulse (y , deltaV );
184
+ y = calculations . impulse (y , deltaV );
211
185
212
186
const period = 2 * std .math .pi * @sqrt (std .math .pow (f64 , r , 3 ) / self .orbitingObject .mu );
213
187
const secondImpulseTime = t + period * transferOrbits ;
214
188
while (t < secondImpulseTime ) : (t += h ) {
215
189
y = self .rk4 (y , h );
216
- try self .orbitPredictions .append (StateTime { .time = t + h , .state = y });
190
+ try self .orbitPredictions .append (calculations. StateTime { .time = t + h , .state = y });
217
191
}
218
- y = impulse (y , .{ - deltaV [0 ], - deltaV [1 ], - deltaV [2 ] });
219
- try self .orbitPredictions .append (StateTime { .time = t , .state = y });
192
+ y = calculations . impulse (y , .{ - deltaV [0 ], - deltaV [1 ], - deltaV [2 ] });
193
+ try self .orbitPredictions .append (calculations. StateTime { .time = t , .state = y });
220
194
log .info ("Phase change completed at t={d:.2}s" , .{t });
221
195
},
222
196
.PlaneChange = > {
223
197
const planeChange = impulses [impulseIndex ];
224
198
y = self .applyPlaneChange (y , planeChange );
225
- try self .orbitPredictions .append (StateTime { .time = t , .state = y });
199
+ try self .orbitPredictions .append (calculations. StateTime { .time = t , .state = y });
226
200
log .info ("Plane change completed at t={d:.2}s" , .{t });
227
201
},
228
202
}
229
203
230
- try self .orbitPredictions .append (StateTime { .time = t , .state = y });
204
+ try self .orbitPredictions .append (calculations. StateTime { .time = t , .state = y });
231
205
232
206
impulseIndex += 1 ;
233
207
}
@@ -240,7 +214,7 @@ pub fn propagate(self: *Spacecraft, t0: f64, days: f64, h: f64, impulseList: ?[]
240
214
const r = @sqrt (y [0 ] * y [0 ] + y [1 ] * y [1 ] + y [2 ] * y [2 ]);
241
215
const energy = self .calculateEnergy (y );
242
216
243
- try self .orbitPredictions .append (StateTime { .time = t , .state = y });
217
+ try self .orbitPredictions .append (calculations. StateTime { .time = t , .state = y });
244
218
245
219
if (energy > 0 or std .math .isNan (energy ) or r > 100_000 ) {
246
220
log .warn ("Abnormal orbit detected at step {d}" , .{step });
@@ -253,126 +227,33 @@ pub fn propagate(self: *Spacecraft, t0: f64, days: f64, h: f64, impulseList: ?[]
253
227
}
254
228
}
255
229
256
- fn rk4 (self : Spacecraft , y : StateV , h : f64 ) StateV {
257
- const k1 = scalarMultiply (h , self .derivative (y ));
258
- const k2 = scalarMultiply (h , self .derivative (vectorAdd (y , scalarMultiply (0.5 , k1 ))));
259
- const k3 = scalarMultiply (h , self .derivative (vectorAdd (y , scalarMultiply (0.5 , k2 ))));
260
- const k4 = scalarMultiply (h , self .derivative (vectorAdd (y , k3 )));
261
- return vectorAdd (
230
+ fn rk4 (self : Spacecraft , y : calculations. StateV , h : f64 ) calculations. StateV {
231
+ const k1 = calculations . scalarMultiply (h , self .derivative (y ));
232
+ const k2 = calculations . scalarMultiply (h , self .derivative (calculations . vectorAdd (y , calculations . scalarMultiply (0.5 , k1 ))));
233
+ const k3 = calculations . scalarMultiply (h , self .derivative (calculations . vectorAdd (y , calculations . scalarMultiply (0.5 , k2 ))));
234
+ const k4 = calculations . scalarMultiply (h , self .derivative (calculations . vectorAdd (y , k3 )));
235
+ return calculations . vectorAdd (
262
236
y ,
263
- scalarMultiply (
237
+ calculations . scalarMultiply (
264
238
1.0 / 6.0 ,
265
- vectorAdd (
266
- vectorAdd (k1 , scalarMultiply (2 , k2 )),
267
- vectorAdd (scalarMultiply (2 , k3 ), k4 ),
239
+ calculations . vectorAdd (
240
+ calculations . vectorAdd (k1 , calculations . scalarMultiply (2 , k2 )),
241
+ calculations . vectorAdd (calculations . scalarMultiply (2 , k3 ), k4 ),
268
242
),
269
243
),
270
244
);
271
245
}
272
246
273
- fn triad (v1_body : [3 ]f64 , v2_body : [3 ]f64 , v1_ref : [3 ]f64 , v2_ref : [3 ]f64 ) [3 ][3 ]f64 {
274
- const t1Body = normalize (v1_body );
275
- const t2Body = normalize (cross (v1_body , v2_body ));
276
- const t3Body = cross (t1Body , t2Body );
277
-
278
- const t1Ref = normalize (v1_ref );
279
- const t2Ref = normalize (cross (v1_ref , v2_ref ));
280
- const t3Ref = cross (t1Ref , t2Ref );
281
-
282
- const bodyMatrix = [3 ][3 ]f64 {
283
- .{ t1Body [0 ], t2Body [0 ], t3Body [0 ] },
284
- .{ t1Body [1 ], t2Body [1 ], t3Body [1 ] },
285
- .{ t1Body [2 ], t2Body [2 ], t3Body [2 ] },
286
- };
287
-
288
- const refMatrix = [3 ][3 ]f64 {
289
- .{ t1Ref [0 ], t2Ref [0 ], t3Ref [0 ] },
290
- .{ t1Ref [1 ], t2Ref [1 ], t3Ref [1 ] },
291
- .{ t1Ref [2 ], t2Ref [2 ], t3Ref [2 ] },
292
- };
293
-
294
- return multiplyMatrices (bodyMatrix , transposeMatrix (refMatrix ));
295
- }
296
-
297
- fn normalize (v : [3 ]f64 ) [3 ]f64 {
298
- const magni = @sqrt (v [0 ] * v [0 ] + v [1 ] * v [1 ] + v [2 ] * v [2 ]);
299
- return .{ v [0 ] / magni , v [1 ] / magni , v [2 ] / magni };
300
- }
301
-
302
- fn cross (a : [3 ]f64 , b : [3 ]f64 ) [3 ]f64 {
303
- return .{
304
- a [1 ] * b [2 ] - a [2 ] * b [1 ],
305
- a [2 ] * b [0 ] - a [0 ] * b [2 ],
306
- a [0 ] * b [1 ] - a [1 ] * b [0 ],
307
- };
308
- }
309
-
310
- pub fn multiplyMatrices (a : [3 ][3 ]f64 , b : [3 ][3 ]f64 ) [3 ][3 ]f64 {
311
- var result : [3 ][3 ]f64 = undefined ;
312
- for (0.. 3) | i | {
313
- for (0.. 3) | j | {
314
- result [i ][j ] = 0 ;
315
- for (0.. 3) | k | {
316
- result [i ][j ] += a [i ][k ] * b [k ][j ];
317
- }
318
- }
319
- }
320
- return result ;
321
- }
322
-
323
- fn transposeMatrix (m : [3 ][3 ]f64 ) [3 ][3 ]f64 {
324
- return .{
325
- .{ m [0 ][0 ], m [1 ][0 ], m [2 ][0 ] },
326
- .{ m [0 ][1 ], m [1 ][1 ], m [2 ][1 ] },
327
- .{ m [0 ][2 ], m [1 ][2 ], m [2 ][2 ] },
328
- };
329
- }
330
-
331
- fn matrixToQuaternion (m : [3 ][3 ]f64 ) [4 ]f64 {
332
- var q : [4 ]f64 = undefined ;
333
- const trace = m [0 ][0 ] + m [1 ][1 ] + m [2 ][2 ];
334
-
335
- if (trace > 0 ) {
336
- const s = 0.5 / @sqrt (trace + 1.0 );
337
- q [0 ] = 0.25 / s ;
338
- q [1 ] = (m [2 ][1 ] - m [1 ][2 ]) * s ;
339
- q [2 ] = (m [0 ][2 ] - m [2 ][0 ]) * s ;
340
- q [3 ] = (m [1 ][0 ] - m [0 ][1 ]) * s ;
341
- } else {
342
- if (m [0 ][0 ] > m [1 ][1 ] and m [0 ][0 ] > m [2 ][2 ]) {
343
- const s = 2.0 * @sqrt (1.0 + m [0 ][0 ] - m [1 ][1 ] - m [2 ][2 ]);
344
- q [0 ] = (m [2 ][1 ] - m [1 ][2 ]) / s ;
345
- q [1 ] = 0.25 * s ;
346
- q [2 ] = (m [0 ][1 ] + m [1 ][0 ]) / s ;
347
- q [3 ] = (m [0 ][2 ] + m [2 ][0 ]) / s ;
348
- } else if (m [1 ][1 ] > m [2 ][2 ]) {
349
- const s = 2.0 * @sqrt (1.0 + m [1 ][1 ] - m [0 ][0 ] - m [2 ][2 ]);
350
- q [0 ] = (m [0 ][2 ] - m [2 ][0 ]) / s ;
351
- q [1 ] = (m [0 ][1 ] + m [1 ][0 ]) / s ;
352
- q [2 ] = 0.25 * s ;
353
- q [3 ] = (m [1 ][2 ] + m [2 ][1 ]) / s ;
354
- } else {
355
- const s = 2.0 * @sqrt (1.0 + m [2 ][2 ] - m [0 ][0 ] - m [1 ][1 ]);
356
- q [0 ] = (m [1 ][0 ] - m [0 ][1 ]) / s ;
357
- q [1 ] = (m [0 ][2 ] + m [2 ][0 ]) / s ;
358
- q [2 ] = (m [1 ][2 ] + m [2 ][1 ]) / s ;
359
- q [3 ] = 0.25 * s ;
360
- }
361
- }
362
-
363
- return q ;
364
- }
365
-
366
- fn rk4Attitude (spacecraft : * Spacecraft , state : AttitudeState , dt : f64 ) AttitudeState {
247
+ fn rk4Attitude (spacecraft : * Spacecraft , state : calculations.AttitudeState , dt : f64 ) calculations.AttitudeState {
367
248
const k1 = attitudeDerivative (spacecraft , state );
368
- const k2 = attitudeDerivative (spacecraft , addScaledAttitudeState (state , k1 , 0.5 * dt ));
369
- const k3 = attitudeDerivative (spacecraft , addScaledAttitudeState (state , k2 , 0.5 * dt ));
370
- const k4 = attitudeDerivative (spacecraft , addScaledAttitudeState (state , k3 , dt ));
249
+ const k2 = attitudeDerivative (spacecraft , calculations . addScaledAttitudeState (state , k1 , 0.5 * dt ));
250
+ const k3 = attitudeDerivative (spacecraft , calculations . addScaledAttitudeState (state , k2 , 0.5 * dt ));
251
+ const k4 = attitudeDerivative (spacecraft , calculations . addScaledAttitudeState (state , k3 , dt ));
371
252
372
- return addScaledAttitudeState (state , addAttitudeStates (addAttitudeStates (k1 , scaleAttitudeState (k2 , 2 )), addAttitudeStates (scaleAttitudeState (k3 , 2 ), k4 )), dt / 6.0 );
253
+ return calculations . addScaledAttitudeState (state , calculations . addAttitudeStates (calculations . addAttitudeStates (k1 , calculations . scaleAttitudeState (k2 , 2 )), calculations . addAttitudeStates (calculations . scaleAttitudeState (k3 , 2 ), k4 )), dt / 6.0 );
373
254
}
374
255
375
- fn attitudeDerivative (spacecraft : * Spacecraft , state : AttitudeState ) AttitudeState {
256
+ fn attitudeDerivative (spacecraft : * Spacecraft , state : calculations. AttitudeState ) calculations. AttitudeState {
376
257
const q = state .quaternion ;
377
258
const w = state .angularVelocity ;
378
259
@@ -396,59 +277,7 @@ fn attitudeDerivative(spacecraft: *Spacecraft, state: AttitudeState) AttitudeSta
396
277
};
397
278
}
398
279
399
- fn addAttitudeStates (a : AttitudeState , b : AttitudeState ) AttitudeState {
400
- return .{
401
- .quaternion = .{
402
- a .quaternion [0 ] + b .quaternion [0 ],
403
- a .quaternion [1 ] + b .quaternion [1 ],
404
- a .quaternion [2 ] + b .quaternion [2 ],
405
- a .quaternion [3 ] + b .quaternion [3 ],
406
- },
407
- .angularVelocity = .{
408
- a .angularVelocity [0 ] + b .angularVelocity [0 ],
409
- a .angularVelocity [1 ] + b .angularVelocity [1 ],
410
- a .angularVelocity [2 ] + b .angularVelocity [2 ],
411
- },
412
- };
413
- }
414
-
415
- fn scaleAttitudeState (state : AttitudeState , scalar : f64 ) AttitudeState {
416
- return .{
417
- .quaternion = .{
418
- state .quaternion [0 ] * scalar ,
419
- state .quaternion [1 ] * scalar ,
420
- state .quaternion [2 ] * scalar ,
421
- state .quaternion [3 ] * scalar ,
422
- },
423
- .angularVelocity = .{
424
- state .angularVelocity [0 ] * scalar ,
425
- state .angularVelocity [1 ] * scalar ,
426
- state .angularVelocity [2 ] * scalar ,
427
- },
428
- };
429
- }
430
-
431
- fn addScaledAttitudeState (state : AttitudeState , delta : AttitudeState , scalar : f64 ) AttitudeState {
432
- return addAttitudeStates (state , scaleAttitudeState (delta , scalar ));
433
- }
434
-
435
- fn vectorAdd (a : StateV , b : StateV ) StateV {
436
- var result : StateV = undefined ;
437
- for (0.. 6) | i | {
438
- result [i ] = a [i ] + b [i ];
439
- }
440
- return result ;
441
- }
442
-
443
- fn scalarMultiply (scalar : f64 , vector : StateV ) StateV {
444
- var result : StateV = undefined ;
445
- for (0.. 6) | i | {
446
- result [i ] = scalar * vector [i ];
447
- }
448
- return result ;
449
- }
450
-
451
- fn derivative (self : Spacecraft , state : StateV ) StateV {
280
+ fn derivative (self : Spacecraft , state : calculations.StateV ) calculations.StateV {
452
281
const x = state [0 ];
453
282
const y = state [1 ];
454
283
const z = state [2 ];
@@ -488,7 +317,7 @@ fn atmosphericDensity(self: Spacecraft, altitude: f64) f64 {
488
317
return self .orbitingObject .seaLevelDensity * std .math .exp (- altitude / self .orbitingObject .scaleHeight );
489
318
}
490
319
491
- fn calculateEnergy (self : Spacecraft , state : StateV ) f64 {
320
+ fn calculateEnergy (self : Spacecraft , state : calculations. StateV ) f64 {
492
321
const r = @sqrt (state [0 ] * state [0 ] + state [1 ] * state [1 ] + state [2 ] * state [2 ]);
493
322
const vSquared = state [3 ] * state [3 ] + state [4 ] * state [4 ] + state [5 ] * state [5 ];
494
323
return 0.5 * vSquared - self .orbitingObject .mu / r ;
@@ -506,13 +335,6 @@ fn applyPlaneChange(self: *Spacecraft, y: [6]f64, plane_change: Impulse) [6]f64
506
335
return calculations .orbitalElementsToStateVector (elements , mu );
507
336
}
508
337
509
- fn impulse (state : StateV , delta_v : [3 ]f64 ) StateV {
510
- return .{
511
- state [0 ], state [1 ], state [2 ],
512
- state [3 ] + delta_v [0 ], state [4 ] + delta_v [1 ], state [5 ] + delta_v [2 ],
513
- };
514
- }
515
-
516
338
fn calculatePhaseChange (self : Spacecraft , radius : f64 , phase_change : f64 , transfer_orbits : f64 ) f64 {
517
339
const vCircular = @sqrt (self .orbitingObject .mu / radius );
518
340
const period = 2 * std .math .pi * @sqrt (std .math .pow (f64 , radius , 3 ) / self .orbitingObject .mu );
0 commit comments