rsspice 0.1.0

Pure Rust port of the SPICE Toolkit for space geometry
Documentation
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
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
//
// GENERATED FILE
//

use super::*;
use crate::SpiceContext;
use f2rust_std::*;

const CTRSIZ: i32 = 2;
const REF: &[u8] = b"J2000";
const SUN: i32 = 10;
const MAXL: i32 = 36;

struct SaveVars {
    SVCTR1: StackArray<i32, 2>,
    SVBODY: Vec<u8>,
    SVIDCD: i32,
    SVFND1: bool,
    FIRST: bool,
}

impl SaveInit for SaveVars {
    fn new() -> Self {
        let mut SVCTR1 = StackArray::<i32, 2>::new(1..=CTRSIZ);
        let mut SVBODY = vec![b' '; MAXL as usize];
        let mut SVIDCD: i32 = 0;
        let mut SVFND1: bool = false;
        let mut FIRST: bool = false;

        FIRST = true;

        Self {
            SVCTR1,
            SVBODY,
            SVIDCD,
            SVFND1,
            FIRST,
        }
    }
}

/// Longitude of the sun, planetocentric
///
/// Compute L_s, the planetocentric longitude of the sun, as seen
/// from a specified body.
///
/// # Required Reading
///
/// * [NAIF_IDS](crate::required_reading::naif_ids)
/// * [PCK](crate::required_reading::pck)
/// * [TIME](crate::required_reading::time)
/// * [SPK](crate::required_reading::spk)
///
/// # Brief I/O
///
/// ```text
///  VARIABLE  I/O  DESCRIPTION
///  --------  ---  --------------------------------------------------
///  BODY       I   Name of central body.
///  ET         I   Epoch in seconds past J2000 TDB.
///  ABCORR     I   Aberration correction.
///
///  The function returns the value of L_s for the specified body
///  at the specified time.
/// ```
///
/// # Detailed Input
///
/// ```text
///  BODY     is the name of the central body, typically a planet.
///
///  ET       is the epoch at which the longitude of the sun (L_s)
///           is to be computed. ET is expressed as seconds past
///           J2000 TDB (Barycentric Dynamical Time).
///
///  ABCORR   indicates the aberration corrections to be applied
///           when computing the longitude of the sun. ABCORR may
///           be any of the following.
///
///              'NONE'     Apply no correction.
///
///              'LT'       Correct the position of the sun,
///                         relative to the central body, for
///                         planetary (light time) aberration.
///
///              'LT+S'     Correct the position of the sun,
///                         relative to the central body, for
///                         planetary and stellar aberrations.
/// ```
///
/// # Detailed Output
///
/// ```text
///  The function returns the planetocentric longitude of the Sun,
///  often called "L_s," for the specified body at the specified time.
///  This is the longitude of the body-Sun vector in a right-handed
///  frame whose basis vectors are defined as follows:
///
///  -  The positive Z direction is given by the instantaneous
///     angular velocity vector of the orbit of the body about
///     the Sun.
///
///  -  The positive X direction is that of the cross product of the
///     instantaneous north spin axis of the body with the positive
///     Z direction.
///
///  -  The positive Y direction is Z x X.
///
///  Units are radians; the range is 0 to 2*pi. Longitudes are
///  positive to the east.
/// ```
///
/// # Exceptions
///
/// ```text
///  1)  If the input body name cannot be translated to an ID code,
///      and if the name is not a string representation of an integer
///      (for example, '399'), the error SPICE(NOTRANSLATION) is
///      signaled.
///
///  2)  If no SPK (ephemeris) file has been loaded prior to calling
///      this routine, or if the SPK data has insufficient coverage, an
///      error is signaled by a routine in the call
///      tree of this routine.
///
///  3)  If a PCK file containing rotational elements for the central
///      body has not been loaded prior to calling this routine, an
///      error is signaled by a routine in the call tree of this
///      routine.
///
///  4)  If the instantaneous angular velocity and spin axis of BODY
///      are parallel, an error is signaled by a
///      routine in the call tree of this routine.
/// ```
///
/// # Files
///
/// ```text
///  Appropriate SPICE kernels must be loaded by the calling program
///  before this routine is called.
///
///  The following data are required:
///
///  -  An SPK file (or files) containing ephemeris data sufficient to
///     compute the geometric state of the central body relative to
///     the sun at ET must be loaded before this routine is called. If
///     light time correction is used, data must be available that
///     enable computation of the state the sun relative to the solar
///     system barycenter at the light-time corrected epoch. If
///     stellar aberration correction is used, data must be available
///     that enable computation of the state the central body relative
///     to the solar system barycenter at ET.
///
///  -  A PCK file containing rotational elements for the central body
///     must be loaded before this routine is called.
/// ```
///
/// # Particulars
///
/// ```text
///  The direction of the vernal equinox for the central body is
///  determined from the instantaneous equatorial and orbital planes
///  of the central body. This equinox definition is specified in
///  reference [1]. The "instantaneous orbital plane" is interpreted
///  in this routine as the plane normal to the cross product of the
///  position and velocity of the central body relative to the sun.
///  The geometric state of the central body relative to the sun is
///  used for this normal vector computation. The "instantaneous
///  equatorial plane" is normal to the central body's north pole
///  at the requested epoch. The pole direction is determined from
///  rotational elements loaded via a PCK file.
///
///  The result returned by this routine will depend on the
///  ephemeris data and rotational elements used. The result may
///  differ from that given in any particular version of the
///  Astronomical Almanac, due to differences in these input data,
///  and due to differences in precision of the computations.
/// ```
///
/// # Examples
///
/// ```text
///  The numerical results shown for this example may differ across
///  platforms. The results depend on the SPICE kernels used as
///  input, the compiler and supporting libraries, and the machine
///  specific arithmetic implementation.
///
///  1) A simple program that computes L_s for a body and time
///     supplied interactively. The geometric state of the Sun is
///     used.
///
///     Example code begins here.
///
///
///           PROGRAM LSPCN_EX1
///           IMPLICIT NONE
///
///           DOUBLE PRECISION      DPR
///           DOUBLE PRECISION      LSPCN
///
///           CHARACTER*(*)         ABCORR
///           PARAMETER           ( ABCORR = 'NONE' )
///
///           INTEGER               FILSIZ
///           PARAMETER           ( FILSIZ = 255 )
///
///           INTEGER               NAMLEN
///           PARAMETER           ( NAMLEN = 36 )
///
///           INTEGER               TIMLEN
///           PARAMETER           ( TIMLEN = 40 )
///
///           CHARACTER*(NAMLEN)    BODY
///           CHARACTER*(FILSIZ)    LSK
///           CHARACTER*(FILSIZ)    PCK
///           CHARACTER*(FILSIZ)    SPK
///           CHARACTER*(TIMLEN)    TIMSTR
///
///           DOUBLE PRECISION      ET
///           DOUBLE PRECISION      LON
///
///
///           CALL PROMPT ( 'Enter name of LSK file > ', LSK )
///           CALL PROMPT ( 'Enter name of PCK file > ', PCK )
///           CALL PROMPT ( 'Enter name of SPK file > ', SPK )
///
///           CALL FURNSH ( LSK )
///           CALL FURNSH ( PCK )
///           CALL FURNSH ( SPK )
///
///           WRITE (*,*) ' '
///           WRITE (*,*) 'Kernels have been loaded.'
///           WRITE (*,*) ' '
///
///           CALL PROMPT ( 'Enter name of central body       > ',
///          .               BODY                                  )
///           CALL PROMPT ( 'Enter calendar, JD, or DOY time  > ',
///          .               TIMSTR                                )
///
///           CALL STR2ET ( TIMSTR, ET )
///
///     C
///     C     Convert longitude to degrees.
///     C
///           LON = DPR() * LSPCN ( BODY, ET, ABCORR )
///
///           WRITE (*,*) ' '
///           WRITE (*,*) 'Central body              = ',  BODY
///           WRITE (*,*) 'Time                      = ',  TIMSTR
///           WRITE (*,*) 'Planetocentric L_s (deg.) = ',  LON
///           WRITE (*,*) ' '
///
///           END
///
///
///     When this program was executed on a Mac/Intel/gfortran/64-bit
///     platform, using the LSK file named naif0012.tls, the PCK file
///     named pck00010.tpc, the SPK file named de421.bsp; the 'EARTH'
///     as central body and the time string '2018 Mar 8  17:59 UTC',
///     the output was:
///
///
///     Enter name of LSK file > naif0012.tls
///     Enter name of PCK file > pck00010.tpc
///     Enter name of SPK file > de421.bsp
///
///      Kernels have been loaded.
///
///     Enter name of central body       > EARTH
///     Enter calendar, JD, or DOY time  > 2018 Mar 8  17:59 UTC
///
///      Central body              = EARTH
///      Time                      = 2018 Mar 8  17:59 UTC
///      Planetocentric L_s (deg.) =    348.11593978317080
/// ```
///
/// # Literature References
///
/// ```text
///  [1]  "The Astronomical Almanac for the Year 2005," page L9,
///       United States Naval Observatory, U.S. Government Printing
///       Office, Washington, D.C., 2004.
/// ```
///
/// # Author and Institution
///
/// ```text
///  N.J. Bachman       (JPL)
///  J. Diaz del Rio    (ODC Space)
///  B.V. Semenov       (JPL)
/// ```
///
/// # Version
///
/// ```text
/// -    SPICELIB Version 1.1.1, 25-AUG-2021 (JDR)
///
///         Edited the header to comply with NAIF standard. Removed WHILE
///         loop from example code, and added solution.
///
/// -    SPICELIB Version 1.1.0, 19-SEP-2013 (BVS)
///
///         Updated to save the input body name and ZZBODTRN state
///         counter and to do name-ID conversion only if the counter
///         has changed.
///
/// -    SPICELIB Version 1.0.0, 07-JAN-2005 (NJB)
/// ```
///
/// # Revisions
///
/// ```text
/// -    SPICELIB Version 1.1.0, 19-SEP-2013 (BVS)
///
///         Updated to save the input body name and ZZBODTRN state
///         counter and to do name-ID conversion only if the counter
///         has changed.
/// ```
pub fn lspcn(ctx: &mut SpiceContext, body: &str, et: f64, abcorr: &str) -> crate::Result<f64> {
    let ret = LSPCN(body.as_bytes(), et, abcorr.as_bytes(), ctx.raw_context())?;
    ctx.handle_errors()?;
    Ok(ret)
}

//$Procedure LSPCN  ( Longitude of the sun, planetocentric )
pub fn LSPCN(BODY: &[u8], ET: f64, ABCORR: &[u8], ctx: &mut Context) -> f2rust_std::Result<f64> {
    let save = ctx.get_vars::<SaveVars>();
    let save = &mut *save.borrow_mut();

    let mut LSPCN: f64 = 0.0;
    let mut UAVEL = StackArray::<f64, 3>::new(1..=3);
    let mut LAT: f64 = 0.0;
    let mut LT: f64 = 0.0;
    let mut NPOLE = StackArray::<f64, 3>::new(1..=3);
    let mut POS = StackArray::<f64, 3>::new(1..=3);
    let mut RADIUS: f64 = 0.0;
    let mut BSTATE = StackArray::<f64, 6>::new(1..=6);
    let mut SSTATE = StackArray::<f64, 6>::new(1..=6);
    let mut TIPM = StackArray2D::<f64, 9>::new(1..=3, 1..=3);
    let mut TRANS = StackArray2D::<f64, 9>::new(1..=3, 1..=3);
    let mut IDCODE: i32 = 0;
    let mut FOUND: bool = false;

    //
    // SPICELIB functions
    //

    //
    // Local parameters
    //

    //
    // Saved body name length.
    //

    //
    // Local variables
    //

    //
    // Saved name/ID item declarations.
    //

    //
    // Saved name/ID items.
    //

    //
    // Initial values.
    //

    //
    // Give the function an initial value.
    //
    LSPCN = 0.0;

    //
    // Standard SPICE error handling.
    //
    if RETURN(ctx) {
        return Ok(LSPCN);
    }

    CHKIN(b"LSPCN", ctx)?;

    //
    // Initialization.
    //
    if save.FIRST {
        //
        // Initialize counters
        //
        ZZCTRUIN(save.SVCTR1.as_slice_mut(), ctx);

        save.FIRST = false;
    }

    //
    // Map the body name to an ID code.
    //
    ZZBODS2C(
        save.SVCTR1.as_slice_mut(),
        &mut save.SVBODY,
        &mut save.SVIDCD,
        &mut save.SVFND1,
        BODY,
        &mut IDCODE,
        &mut FOUND,
        ctx,
    )?;

    if !FOUND {
        SETMSG(b"The body name # could not be translated to a NAIF ID code.  The cause of this problem may be that you need an updated version of the SPICE Toolkit.", ctx);
        ERRCH(b"#", BODY, ctx);
        SIGERR(b"SPICE(NOTRANSLATION)", ctx)?;
        CHKOUT(b"LSPCN", ctx)?;
        return Ok(LSPCN);
    }

    //
    // Look up the direction of the North pole of the central body.
    // Note that TIPBOD does make use of binary PCK data if available.
    //
    TIPBOD(REF, IDCODE, ET, TIPM.as_slice_mut(), ctx)?;

    for I in 1..=3 {
        NPOLE[I] = TIPM[[3, I]];
    }

    //
    // Get the geometric state of the body relative to the sun.
    //
    SPKGEO(IDCODE, ET, REF, SUN, BSTATE.as_slice_mut(), &mut LT, ctx)?;

    //
    // Get the unit direction vector parallel to the angular velocity
    // vector of the orbit.  This is just the unitized cross product of
    // position and velocity.
    //
    UCRSS(BSTATE.as_slice(), BSTATE.subarray(4), UAVEL.as_slice_mut());

    //
    // We want to create a transformation matrix that maps vectors from
    // basis REF to the following frame:

    //    Z  =  UAVEL
    //
    //    X  =  NPOLE x UAVEL
    //
    //    Y  =  Z x X
    //
    // This is a "two-vector" frame with the unit orbital
    // angular velocity vector UAVEL as the primary vector and the
    // spin axis NPOLE as the secondary vector.  The primary
    // vector is associated with the +Z axis; the secondary vector
    // is associated with the +Y axis.
    //
    TWOVEC(
        UAVEL.as_slice(),
        3,
        NPOLE.as_slice(),
        2,
        TRANS.as_slice_mut(),
        ctx,
    )?;

    if FAILED(ctx) {
        CHKOUT(b"LSPCN", ctx)?;
        return Ok(LSPCN);
    }

    //
    // We'll find the position of the Sun relative to this frame.
    //
    // Get the state of the sun in frame REF.  Since we may be using
    // aberration corrections, this is not necessarily the negative of
    // the state we've just found.
    //
    SPKEZR(
        b"SUN",
        ET,
        REF,
        ABCORR,
        BODY,
        SSTATE.as_slice_mut(),
        &mut LT,
        ctx,
    )?;

    //
    // Now transform the position of the Sun into the "orbit plane
    // and equinox" frame.
    //
    MXV(TRANS.as_slice(), SSTATE.as_slice(), POS.as_slice_mut());

    //
    // Let RECRAD find the longitude LS for us.  RECRAD performs
    // the same coordinate transformation as the more commonly used
    // RECLAT, but the range of right ascension is 0:2*pi, which is
    // what we want for Ls.
    //
    RECRAD(POS.as_slice(), &mut RADIUS, &mut LSPCN, &mut LAT, ctx);

    CHKOUT(b"LSPCN", ctx)?;
    Ok(LSPCN)
}