Skip to content

Commit 8f61817

Browse files
LuEdRaMoPerezHz
andauthored
WIP: New Horizons API (#32)
* First draft * New APIs + new tests * Suggestions by @PerezHz * Update README.md * Minor fixes * Fix vec_tbl tests * Update common.jl * Breaking change warning * Minor fix --------- Co-authored-by: Jorge Pérez <[email protected]>
1 parent 9f1b4af commit 8f61817

File tree

9 files changed

+685
-281
lines changed

9 files changed

+685
-281
lines changed

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,7 @@
22
*.jl.*.cov
33
*.jl.mem
44
/deps/libexjl.so
5-
.vscode/
5+
.vscode/
6+
*.bsp
7+
*.tls
8+
Manifest.toml

Project.toml

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,22 @@
11
name = "HORIZONS"
22
uuid = "5a3ac768-beb4-554a-9c98-3342fe3377f5"
33
repo = "https://github.com/PerezHz/HORIZONS.jl.git"
4-
version = "0.3.1"
4+
version = "0.4.0"
55

66
[deps]
7+
Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"
78
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
8-
DelimitedFiles = "8bb1440f-4735-579b-a4ab-409b98df4dab"
9-
Expect = "6a31a4e8-6e70-5a2d-b005-bc2d500d80a5"
10-
FTPClient = "01fcc997-4f28-56b8-8a06-30002c134abb"
9+
HTTP = "cd3eb016-35fb-5094-929b-558a96fad6f3"
10+
JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
1111

1212
[compat]
13-
Expect = "0.3.1"
14-
FTPClient = "1.1.0"
13+
HTTP = "1.10"
14+
JSON = "0.21"
1515
julia = "1"
1616

1717
[extras]
18-
Expect = "6a31a4e8-6e70-5a2d-b005-bc2d500d80a5"
1918
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
2019
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
2120

2221
[targets]
23-
test = ["Test", "Expect", "Pkg"]
22+
test = ["Test", "Pkg"]

README.md

Lines changed: 50 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
[![codecov](https://codecov.io/gh/PerezHz/HORIZONS.jl/branch/master/graph/badge.svg)](https://codecov.io/gh/PerezHz/HORIZONS.jl) [![Coverage Status](https://coveralls.io/repos/github/PerezHz/HORIZONS.jl/badge.svg?branch=master)](https://coveralls.io/github/PerezHz/HORIZONS.jl?branch=master)
66

7-
An interface to NASA-JPL [HORIZONS](https://ssd.jpl.nasa.gov/?horizons) system in
7+
An interface to JPL Solar System Dynamics [APIs](https://ssd.jpl.nasa.gov/api.html) in
88
[Julia](http://julialang.org).
99

1010
## Author
@@ -17,16 +17,17 @@ Comments, suggestions, and improvements are welcome and appreciated.
1717
## Installation
1818

1919
`HORIZONS.jl` is a registered Julia package and may be installed
20-
from the Julia REPL doing `import Pkg; Pkg.add("HORIZONS")`. Current stable
21-
release is `v0.3.0`, which is compatible with Julia 1.0, 1.3 and 1.4.
22-
23-
## External dependencies
24-
25-
Connection to the HORIZONS machine is done via the `telnet` command line
26-
utility, which should be locally installed and enabled. File downloading is done via `ftp`.
20+
from the Julia REPL doing
21+
```
22+
] add HORIZONS
23+
```
24+
Current stable
25+
release is `v0.4.0`, which is compatible with Julia >= 1.6.
2726

2827
## Usage examples
2928

29+
### Horizons API
30+
3031
The `horizons()` function is a shortcut to the HORIZONS `telnet` interface
3132
prompt from the Julia REPL:
3233

@@ -41,36 +42,39 @@ System news updated June 08, 2020
4142

4243
Horizons>
4344
```
45+
To run this function, the `telnet` command line utility should be locally installed and enabled.
46+
4447

45-
`HORIZONS.jl` also has Julia functions which for some of the scripts authored by
48+
49+
`HORIZONS.jl` also has Julia functions for some of the scripts authored by
4650
Jon D. Giorgini for automated generation of small-body binary SPK files and tables.
4751
These scripts were originally written in `expect`, and can be found at the
4852
JPL's Solar System Dynamics group ftp server `ftp://ssd.jpl.nasa.gov/pub/ssd/SCRIPTS/`.
4953
Below, we describe the functions `smb_spk`, `smb_spk_ele` and `vec_tbl`.
5054

51-
### `smb_spk`
55+
#### `smb_spk`
5256

5357
The `smb_spk` function automates generation and downloading of Solar System
5458
small-bodies binary SPK files from HORIZONS:
5559
```julia
5660
using HORIZONS, Dates
5761

58-
# generate a binary SPK file for asteroid 99942 Apophis covering from 2021 to 2029
59-
ftp_name, local_file = smb_spk("b", "DES= 2099942;", DateTime(2021,Jan,1), DateTime(2029,Apr,13))
62+
# Generate a binary SPK file for asteroid 99942 Apophis covering from 2021 to 2029
63+
local_file = smb_spk("DES = 20099942;", DateTime(2021,Jan,1), DateTime(2029,Apr,13))
6064

61-
isfile(local_file) # check that the binary SPK file `local_file` exists
65+
isfile(local_file) # Check that the binary SPK file `local_file` exists
6266
```
6367
Binary SPK files (i.e., extension `.bsp`) can be read using e.g.
6468
[`SPICE.jl`](https://github.com/JuliaAstro/SPICE.jl):
6569
```julia
66-
# import Pkg; Pkg.add("SPICE") # uncomment this line to add `SPICE.jl` to current environment
70+
# ] add SPICE" # uncomment this line to add `SPICE.jl` to current environment
6771
using SPICE, Dates
6872
furnsh(local_file)
6973
et = 86400*(datetime2julian(DateTime(2024,3,1)) - 2.451545e6)
70-
pv = spkgeo(2099942, et, "J2000", 0)
74+
pv = spkgeo(20099942, et, "J2000", 0)
7175
```
7276

73-
### `smb_spk_ele`
77+
#### `smb_spk_ele`
7478

7579
`HORIZONS.jl` function `smb_spk_ele` generates `.bsp` binary SPK files for
7680
small-bodies from a set of osculating orbital elements at a given epoch:
@@ -88,120 +92,77 @@ inc = 24.4225258251465 # Inclination
8892
start_time = DateTime(2021,Jan,1)
8993
stop_time = DateTime(2022,Jan,1)
9094

91-
# generate a binary SPK file for asteroid 1990 MU at `epoch`
92-
ftp_name, local_file = smb_spk_ele("b", "1990 MU", start_time, stop_time, epoch, ec, qr, tp, om, w, inc)
95+
# Generate a binary SPK file for asteroid 1990 MU at `epoch`
96+
local_file = smb_spk_ele("1990 MU", start_time, stop_time, epoch, ec, qr, tp, om, w, inc)
9397

94-
isfile(local_file) # check that the binary SPK was downloaded
98+
isfile(local_file) # Check that the binary SPK was downloaded
9599
```
96100

97-
### `vec_tbl`
101+
#### `vec_tbl`
98102

99103
`HORIZONS.jl` function `vec_tbl` allows the user to generate vector tables for
100104
designated objects and save the output into a file:
101105

102106
```julia
103-
# date variables for start and stop times
107+
# Date variables for start and stop times
104108
t_start = DateTime(2029,4,13)
105109
t_stop = Date(2029,4,14)
106110

107-
# step size (allowed types: Period, Int, String)
111+
# Step size (allowed types: Period, Int, String)
108112
δt = Hour(1) # 1 hour step size
109113

110-
# generate tables and save output to Apophis.txt in current directory:
111-
vec_tbl("Apophis", "Apophis.txt", t_start, t_stop, δt; CENTER="@ssb", REF_PLANE="FRAME", OUT_UNITS=2, CSV_FORMAT=true, VEC_TABLE=2)
114+
# Generate tables and save output to Apophis.txt in current directory:
115+
vec_tbl("Apophis", t_start, t_stop, δt; FILENAME = "Apophis.txt", CENTER = "@ssb", REF_PLANE = "FRAME", OUT_UNITS = "AU-D", CSV_FORMAT = true, VEC_TABLE = 2)
112116
```
113117

114118
Note that `CENTER`, `REF_PLANE`, etc., are keyword arguments. If they are omitted
115119
from the `vec_tbl` call, then they will take default values:
116120

117121
```julia
118-
δt = 1 #return only one step
122+
δt = 1 # Return only one step
119123

120-
# generate tables with default values and save output to Apophis.txt in current directory:
124+
# Generate tables with default values and save output to Apophis.txt in current directory:
121125

122-
vec_tbl("Apophis", "Apophis.txt", t_start, t_stop, δt)
126+
vec_tbl("Apophis", t_start, t_stop, δt; FILENAME = "Apophis.txt")
123127
```
124128

125129
More details about default values of keyword arguments are available in the
126130
`vec_tbl` docstrings.
127131

128132
If the output file is not specified, then `vec_tbl` returns the output as a
129-
string, which may be then used for further processing within Julia:
133+
`String`, which may be then used for further processing within Julia:
130134

131135
```julia
132136
δt = "2 hours" # 2 hour step size
133137

134-
# save into `apophisvt::String` the output from HORIZONS
138+
# Save into `apophisvt::String` the output from HORIZONS
135139
apophisvt = vec_tbl("Apophis", t_start, t_stop, δt)
136140

137-
# do stuff with `apophisvt` inside julia...
141+
# Do stuff with `apophisvt` inside julia...
138142
```
139143

140-
Julia's broadcasting allows the user to get many vector tables at once:
141-
142-
```julia
143-
julia> using HORIZONS
144-
145-
julia> IDs = string.([99942, 90000033])
146-
2-element Array{String,1}:
147-
"99942"
148-
"90000033"
144+
### Small-Body DataBase API
149145

150-
julia> local_files = string.(IDs,".txt")
151-
2-element Array{String,1}:
152-
"99942.txt"
153-
"90000033.txt"
154-
155-
julia> vec_tbl.(IDs, local_files, t_start, t_stop, δt) #save output to local files 99942.txt and 90000033.txt in current folder
156-
2-element Array{Void,1}:
157-
nothing
158-
nothing
159-
160-
julia>
161-
```
162-
163-
Additionally, the `vec_tbl_csv` function returns HORIZONS output both as an
164-
`Array{Any,2}` and a CSV-formatted `String`, which
165-
can in turn be used to construct a `DataFrame` (requires
166-
[DataFrames.jl](https://github.com/JuliaData/DataFrames.jl) to be installed):
146+
`HORIZONS.jl` function `sbdb` fetchs data for a specific small-body in JPL's Small-Body DataBase (SBDB) and returns the output as a `Dict{String, Any}`:
167147

168148
```julia
169-
using HORIZONS, DataFrames
170-
171-
dt0 = Date(2000)
172-
dtmax = Date(2015)
173-
δt = Year(1)
149+
# Fetch data of asteroid 433 Eros
150+
sbdb("sstr" => "Eros")
174151

175-
#tbl is an Array{Any,2}; str is a String with CSV format
176-
tbl, str = vec_tbl_csv("1950 DA", dt0, dtmax, δt;
177-
VEC_TABLE = "2", REF_PLANE="F", CENTER="coord", COORD_TYPE="C", SITE_COORD="1,45,45");
178-
179-
mydataframe = readtable(IOBuffer(str))
152+
# Fetch data of asteroid 99942 Apophis, including close-approach information
153+
sbdb("sstr" => "Apophis", "ca-data" => "true")
180154
```
181155

182-
Then, `mydataframe` is a 16×8 `DataFrame`:
156+
### Small-Body Radar Astrometry API
183157

184-
```
185-
# mydataframe:
186-
# 16×8 DataFrames.DataFrame
187-
│ Row │ JDTDB │ Calendar_Date_TDB │ X │ Y │ Z │ VX │ VY │ VZ │
188-
├─────┼───────────┼──────────────────────────────────┼────────────┼────────────┼────────────┼─────────┼──────────┼──────────┤
189-
│ 1 │ 2.45154e6 │ "A.D. 2000-Jan-01 00:00:00.0000" │ 3.49475e8 │ 2.10629e7 │ 5.71688e7 │ 25.2192 │ 15.1321 │ 9.42222 │
190-
│ 2 │ 2.45191e6 │ "A.D. 2001-Jan-01 00:00:00.0000" │ -6.98285e7 │ 2.58022e7 │ 5.45238e7 │ 14.9524 │ -12.6021 │ -10.6881 │
191-
│ 3 │ 2.45228e6 │ "A.D. 2002-Jan-01 00:00:00.0000" │ 3.61348e8 │ -5.69666e7 │ 1.54172e6 │ 31.3711 │ 17.5209 │ 11.1536 │
192-
│ 4 │ 2.45264e6 │ "A.D. 2003-Jan-01 00:00:00.0000" │ 4.38864e7 │ 1.05596e8 │ 1.13413e8 │ 12.2543 │ -1.86915 │ -2.97705 │
193-
│ 5 │ 2.45301e6 │ "A.D. 2004-Jan-01 00:00:00.0000" │ 3.22054e8 │ -1.46042e8 │ -6.27119e7 │ 39.9381 │ 18.0432 │ 11.7154 │
194-
│ 6 │ 2.45337e6 │ "A.D. 2005-Jan-01 00:00:00.0000" │ 1.58117e8 │ 1.26817e8 │ 1.30187e8 │ 14.1172 │ 5.18222 │ 2.03615 │
195-
│ 7 │ 2.45374e6 │ "A.D. 2006-Jan-01 00:00:00.0000" │ 2.16183e8 │ -2.27991e8 │ -1.22995e8 │ 52.494 │ 15.0644 │ 9.69931 │
196-
│ 8 │ 2.4541e6 │ "A.D. 2007-Jan-01 00:00:00.0000" │ 2.52251e8 │ 1.08971e8 │ 1.18844e8 │ 17.5583 │ 9.77493 │ 5.43963 │
197-
│ 9 │ 2.45447e6 │ "A.D. 2008-Jan-01 00:00:00.0000" │ 7.88944e6 │ -2.43067e8 │ -1.36722e8 │ 65.0567 │ -6.41305 │ -5.42335 │
198-
│ 10 │ 2.45483e6 │ "A.D. 2009-Jan-01 00:00:00.0000" │ 3.21987e8 │ 6.3783e7 │ 8.74408e7 │ 21.7692 │ 13.586 │ 8.1631 │
199-
│ 11 │ 2.4552e6 │ "A.D. 2010-Jan-01 00:00:00.0000" │ -1.15663e8 │ -7.63649e7 │ -1.92427e7 │ 27.1975 │ -22.6347 │ -17.6561 │
200-
│ 12 │ 2.45556e6 │ "A.D. 2011-Jan-01 00:00:00.0000" │ 3.57936e8 │ -3.91115e6 │ 3.95854e7 │ 27.1418 │ 16.0684 │ 10.0908 │
201-
│ 13 │ 2.45593e6 │ "A.D. 2012-Jan-01 00:00:00.0000" │ -3.42864e7 │ 6.17015e7 │ 8.08374e7 │ 13.0119 │ -8.54587 │ -7.70992 │
202-
│ 14 │ 2.45629e6 │ "A.D. 2013-Jan-01 00:00:00.0000" │ 3.55506e8 │ -8.52031e7 │ -1.86717e7 │ 33.8279 │ 18.0591 │ 11.5473 │
203-
│ 15 │ 2.45666e6 │ "A.D. 2014-Jan-01 00:00:00.0000" │ 8.32588e7 │ 1.17897e8 │ 1.22693e8 │ 12.6344 │ 0.803698 │ -1.08723 │
204-
│ 16 │ 2.45702e6 │ "A.D. 2015-Jan-01 00:00:00.0000" │ 2.96116e8 │ -1.75053e8 │ -8.37231e7 │ 43.4907 │ 17.7757 │ 11.5517 │
158+
`HORIZONS.jl` function `sbradar` searches for radar astrometry of asteroids/commets and returns the output as a `Dict{String, Any}`:
159+
160+
```julia
161+
# Search Apophis' radar astrometry
162+
sbradar("spk" => "20099942")
163+
164+
# Add observer information
165+
sbradar("spk" => "20099942", "observer" => "true")
205166
```
206167

207168
## License
@@ -226,11 +187,9 @@ The [HORIZONS](https://ssd.jpl.nasa.gov/?horizons) system itself is the work of
226187
- Paul Chodas (some subroutines)
227188
- The NAIF group (SPICELIB) (esp. Chuck Acton, Bill Taber, Nat Bachman)
228189

229-
Translation from the original `expect` scripts to Julia was done using the
230-
[Expect.jl](https://gitlab.com/wavexx/Expect.jl) package.
231-
232190
## References
233191

192+
* [JPL Solar System Dynamics APIs](https://ssd.jpl.nasa.gov/api.html)
234193
* [HORIZONS documentation (HTML)](https://ssd.jpl.nasa.gov/?horizons_doc)
235194
* Giorgini, J.D., Yeomans, D.K., Chamberlin, A.B., Chodas, P.W.,
236195
Jacobson, R.A., Keesey, M.S., Lieske, J.H., Ostro, S.J.,

src/HORIZONS.jl

Lines changed: 22 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,40 +3,38 @@
33

44
module HORIZONS
55

6-
using Expect, FTPClient
6+
using HTTP, JSON, Base64, Dates
7+
using HTTP: Messages.Response
78

8-
using DelimitedFiles, Dates
9+
export horizons, smb_spk, smb_spk_ele, vec_tbl, sbdb, sbradar
910

10-
export HORIZONS_MACHINE, horizons, vec_tbl, vec_tbl_csv,
11-
smb_spk, smb_spk_ele
11+
@doc raw"""
12+
horizons()
1213
13-
const HORIZONS_MACHINE = "ssd.jpl.nasa.gov"
14-
const HORIZONS_FTP_DIR = "pub/ssd/"
15-
const HORIZONS_DATE_FORMAT = "yyyy-u-dd HH:MM:SS.sss"
16-
const ObjectName = Union{Int, String}
17-
const StartStopTime = Union{DateTime, Date, String}
18-
const StepSize = Union{Period, Int, String}
19-
const VecTable = Union{Int, String}
14+
Connect to JPL HORIZONS `telnet` interface
2015
16+
`telnet horizons.jpl.nasa.gov 6775`
17+
18+
!!! warning
19+
To run this function, the `telnet` command line utility should be locally installed and enabled.
2120
"""
22-
`horizons()`
21+
horizons() = run(ignorestatus(`telnet horizons.jpl.nasa.gov 6775`))
2322

24-
Connect to JPL HORIZONS telnet interface
23+
include("common.jl")
24+
include("horizonsapi.jl")
25+
include("sbdb.jl")
26+
include("sbradar.jl")
2527

26-
`telnet horizons.jpl.nasa.gov 6775`
28+
function __init__()
2729

28-
"""
29-
function horizons()
30-
run(ignorestatus(`telnet horizons.jpl.nasa.gov 6775`))
31-
end
30+
# Breaking change warning added in v0.4.0; to be deleted in next minor version (v0.5.0)
31+
@warn("""\n
32+
# Breaking change
33+
Starting from v0.4.0 HORIZONS.jl connects to JPL via a HTTP API.
34+
Previous versions used the telnet command line utility as an external dependency.
35+
""")
3236

33-
#auxiliary function which translates a ::Bool to a "YES" or "NO" string
34-
function yesornostring(yesorno::Bool)
35-
yesorno ? "YES" : "NO"
3637
end
3738

38-
include("vec_tbl.jl")
39-
include("smb_spk.jl")
40-
include("smb_spk_ele.jl")
4139

4240
end # module

0 commit comments

Comments
 (0)