Coverage Report

Created: 2026-07-30 06:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/sofars-0.6.1/src/astro/starpv.rs
Line
Count
Source
1
use crate::{
2
    consts::{DAU, DAYSEC, DC, DJY, DR2AS},
3
    vm::{pdp, pm, pmp, pn, ppp, s2pv, sxp, zp},
4
};
5
6
///  Convert star catalog coordinates to position+velocity vector.
7
///
8
///  This function is part of the International Astronomical Union's
9
///  SOFA (Standards of Fundamental Astronomy) software collection.
10
///
11
///  Status:  support function.
12
///
13
///  Given (Note 1):
14
///     ra     double        right ascension (radians)
15
///     dec    double        declination (radians)
16
///     pmr    double        RA proper motion (radians/year)
17
///     pmd    double        Dec proper motion (radians/year)
18
///     px     double        parallax (arcseconds)
19
///     rv     double        radial velocity (km/s, positive = receding)
20
///
21
///  Returned (Note 2):
22
///     pv     double[2][3]  pv-vector (au, au/day)
23
///
24
///  Returned (function value):
25
///            int           status:
26
///                              0 = no warnings
27
///                              1 = distance overridden (Note 6)
28
///                              2 = excessive speed (Note 7)
29
///                              4 = solution didn't converge (Note 8)
30
///                           else = binary logical OR of the above
31
///
32
///  Notes:
33
///
34
///  1) The star data accepted by this function are "observables" for an
35
///     imaginary observer at the solar-system barycenter.  Proper motion
36
///     and radial velocity are, strictly, in terms of barycentric
37
///     coordinate time, TCB.  For most practical applications, it is
38
///     permissible to neglect the distinction between TCB and ordinary
39
///     "proper" time on Earth (TT/TAI).  The result will, as a rule, be
40
///     limited by the intrinsic accuracy of the proper-motion and
41
///     radial-velocity data;  moreover, the pv-vector is likely to be
42
///     merely an intermediate result, so that a change of time unit
43
///     would cancel out overall.
44
///
45
///     In accordance with normal star-catalog conventions, the object's
46
///     right ascension and declination are freed from the effects of
47
///     secular aberration.  The frame, which is aligned to the catalog
48
///     equator and equinox, is Lorentzian and centered on the SSB.
49
///
50
///  2) The resulting position and velocity pv-vector is with respect to
51
///     the same frame and, like the catalog coordinates, is freed from
52
///     the effects of secular aberration.  Should the "coordinate
53
///     direction", where the object was located at the catalog epoch, be
54
///     required, it may be obtained by calculating the magnitude of the
55
///     position vector pv[0][0-2] dividing by the speed of light in
56
///     au/day to give the light-time, and then multiplying the space
57
///     velocity pv[1][0-2] by this light-time and adding the result to
58
///     pv[0][0-2].
59
///
60
///     Summarizing, the pv-vector returned is for most stars almost
61
///     identical to the result of applying the standard geometrical
62
///     "space motion" transformation.  The differences, which are the
63
///     subject of the Stumpff paper referenced below, are:
64
///
65
///     (i) In stars with significant radial velocity and proper motion,
66
///     the constantly changing light-time distorts the apparent proper
67
///     motion.  Note that this is a classical, not a relativistic,
68
///     effect.
69
///
70
///     (ii) The transformation complies with special relativity.
71
///
72
///  3) Care is needed with units.  The star coordinates are in radians
73
///     and the proper motions in radians per Julian year, but the
74
///     parallax is in arcseconds; the radial velocity is in km/s, but
75
///     the pv-vector result is in au and au/day.
76
///
77
///  4) The RA proper motion is in terms of coordinate angle, not true
78
///     angle.  If the catalog uses arcseconds for both RA and Dec proper
79
///     motions, the RA proper motion will need to be divided by cos(Dec)
80
///     before use.
81
///
82
///  5) Straight-line motion at constant speed, in the inertial frame,
83
///     is assumed.
84
///
85
///  6) An extremely small (or zero or negative) parallax is interpreted
86
///     to mean that the object is on the "celestial sphere", the radius
87
///     of which is an arbitrary (large) value (see the constant PXMIN).
88
///     When the distance is overridden in this way, the status,
89
///     initially zero, has 1 added to it.
90
///
91
///  7) If the space velocity is a significant fraction of c (see the
92
///     constant VMAX), it is arbitrarily set to zero.  When this action
93
///     occurs, 2 is added to the status.
94
///
95
///  8) The relativistic adjustment involves an iterative calculation.
96
///     If the process fails to converge within a set number (IMAX) of
97
///     iterations, 4 is added to the status.
98
///
99
///  9) The inverse transformation is performed by the function
100
///     iauPvstar.
101
///
102
///  Called:
103
///     iauS2pv      spherical coordinates to pv-vector
104
///     iauPm        modulus of p-vector
105
///     iauZp        zero p-vector
106
///     iauPn        decompose p-vector into modulus and direction
107
///     iauPdp       scalar product of two p-vectors
108
///     iauSxp       multiply p-vector by scalar
109
///     iauPmp       p-vector minus p-vector
110
///     iauPpp       p-vector plus p-vector
111
///
112
///  Reference:
113
///
114
///     Stumpff, P., 1985, Astron.Astrophys. 144, 232-240.
115
///
116
///  This revision:  2023 May 4
117
///
118
///  SOFA release 2023-10-11
119
///
120
///  Copyright (C) 2023 IAU SOFA Board.  See notes at end.
121
///
122
0
pub fn starpv(
123
0
    ra: f64,
124
0
    dec: f64,
125
0
    pmr: f64,
126
0
    pmd: f64,
127
0
    px: f64,
128
0
    rv: f64,
129
0
) -> ([[f64; 3]; 2], i32) {
130
    /* Smallest allowed parallax */
131
    const PXMIN: f64 = 1e-7;
132
133
    /* Largest allowed speed (fraction of c) */
134
    const VMAX: f64 = 0.5;
135
136
    /* Maximum number of iterations for relativistic solution */
137
    const IMAX: i32 = 100;
138
139
0
    let mut i: i32 = 0;
140
    let mut iwarn: i32;
141
    let mut w: f64;
142
    let (r, rd, rad, decd, v, vsr, vst): (f64, f64, f64, f64, f64, f64, f64);
143
    let (betst, betsr, mut bett, mut betr, mut dd, mut ddel): (f64, f64, f64, f64, f64, f64);
144
145
0
    let (mut d, mut del, mut odd, mut oddel, mut od, mut odel) = (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
146
147
    // Distance (au).
148
0
    if px >= PXMIN {
149
0
        w = px;
150
0
        iwarn = 0;
151
0
    } else {
152
0
        w = PXMIN;
153
0
        iwarn = 1;
154
0
    }
155
0
    r = DR2AS / w;
156
157
    // Radial speed (au/day).
158
0
    rd = DAYSEC * rv * 1e3 / DAU;
159
160
    // Proper motion (radian/day).
161
0
    rad = pmr / DJY;
162
0
    decd = pmd / DJY;
163
164
    // To pv-vector (au, au/day).
165
0
    let mut pv = s2pv(ra, dec, r, rad, decd, rd);
166
167
    // If excessive velocity, arbitrarily set it to zero.
168
0
    v = pm(pv[1]);
169
0
    if v / DC > VMAX {
170
0
        zp(&mut pv[1]);
171
0
        iwarn += 2;
172
0
    }
173
174
    // Isolate the radial component of the velocity (au/day).
175
0
    let (_, pu) = pn(&pv[0]);
176
0
    vsr = pdp(&pu, &pv[1]);
177
0
    let usr = sxp(vsr, &pu);
178
179
    // Isolate the transverse component of the velocity (au/day).
180
0
    let ust = pmp(&pv[1], &usr);
181
0
    vst = pm(ust);
182
183
    // Special-relativity dimensionless parameters.
184
0
    betsr = vsr / DC;
185
0
    betst = vst / DC;
186
187
    // Determine the observed-to-inertial correction terms.
188
0
    bett = betst;
189
0
    betr = betsr;
190
0
    while i < IMAX {
191
0
        d = 1.0 + betr;
192
0
        w = betr * betr + bett * bett;
193
0
        del = -w / ((1.0 - w).sqrt() + 1.0);
194
0
        betr = d * betsr + del;
195
0
        bett = d * betst;
196
0
        if i > 0 {
197
0
            dd = (d - od).abs();
198
0
            ddel = (del - odel).abs();
199
0
            if i > 1 && dd >= odd && ddel >= oddel {
200
0
                break;
201
0
            }
202
0
            odd = dd;
203
0
            oddel = ddel;
204
0
        }
205
0
        od = d;
206
0
        odel = del;
207
0
        i += 1;
208
    }
209
0
    if i >= IMAX {
210
0
        iwarn += 4;
211
0
    }
212
213
    // Scale observed tangential velocity vector into inertial (au/d).
214
0
    let ut = sxp(d, &ust);
215
216
    // Compute inertial radial velocity vector (au/d).
217
0
    let ur = sxp(DC * (d * betsr + del), &pu);
218
219
    // Combine the two to obtain the inertial space velocity vector.
220
0
    pv[1] = ppp(&ur, &ut);
221
222
0
    (pv, iwarn)
223
0
}