Skip to content

Commit eb5d0f2

Browse files
committed
Add a command line script for ephemeris generation.
1 parent 1315915 commit eb5d0f2

File tree

2 files changed

+225
-3
lines changed

2 files changed

+225
-3
lines changed

sbpy/data/ephem.py

Lines changed: 222 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,26 @@
11
# Licensed under a 3-clause BSD style license - see LICENSE.rst
22
"""
33
======================
4-
sbpy data.Ephem Module
4+
sbpy.data.ephem Module
55
======================
66
7-
Class for storing and querying ephemerides
7+
Class for storing and querying ephemerides.
8+
9+
Ephemerides may be generated by this module via command line:
10+
11+
:code:
12+
python3 -m sbpy.data.ephem
13+
14+
Or with the ``sbpy-ephem`` command line script.
815
916
created on June 04, 2017
17+
1018
"""
19+
1120
import os
21+
import argparse
1222
from warnings import warn
23+
from typing import Union
1324

1425
import numpy as np
1526
from numpy import ndarray, hstack, iterable
@@ -525,7 +536,7 @@ def from_miriade(cls, targetids, objtype='asteroid',
525536
* For detailed explanations of the queried fields, refer to
526537
`astroquery.imcce.MiriadeClass.get_ephemerides` and the
527538
`Miriade documentation
528-
<http://vo.imcce.fr/webservices/miriade/?documentation>`_.
539+
<https://vo.imcce.fr/webservices/miriade/?documentation>`_.
529540
* By default, all properties are provided in the J2000.0 reference
530541
system. Different settings can be chosen using
531542
additional keyword arguments as used by
@@ -863,3 +874,211 @@ def from_oo(cls, orbit, epochs=None, location='500', scope='full',
863874
ephem.table.remove_column('MJD')
864875

865876
return ephem
877+
878+
879+
@requires("astroquery")
880+
def main():
881+
"""Command line script for ephemeris generation."""
882+
883+
from .names import Names, TargetNameParseError
884+
885+
today: Time = Time(Time.now().iso[:10])
886+
887+
parser: argparse.ArgumentParser = argparse.ArgumentParser()
888+
parser.add_argument(
889+
"service",
890+
choices=("horizons", "miriade", "mpc"),
891+
help="online service to use for ephemeris",
892+
)
893+
parser.add_argument(
894+
"target",
895+
)
896+
parser.add_argument(
897+
"--location",
898+
"-l",
899+
default="500",
900+
help="observer location",
901+
)
902+
parser.add_argument(
903+
"--start",
904+
default=today,
905+
type=Time,
906+
help="start time (UTC)",
907+
)
908+
parser.add_argument(
909+
"--stop", default=today + 30 * u.day, type=Time, help="stop time (UTC)"
910+
)
911+
step_mutex = parser.add_mutually_exclusive_group()
912+
step_mutex.add_argument(
913+
"--step",
914+
default="1 d",
915+
type=u.Quantity,
916+
help="ephemeris step size with units or number of steps",
917+
)
918+
parser.add_argument(
919+
"--radec",
920+
"-r",
921+
default="hmsdms",
922+
choices=("hmsdms", "deg"),
923+
help="RA, Dec coordinate format",
924+
)
925+
926+
horizons: argparse._ArgumentGroup = parser.add_argument_group(
927+
"JPL Horizons options"
928+
)
929+
horizons.add_argument(
930+
"--id-type",
931+
"-i",
932+
default=None,
933+
choices=(
934+
"smallbody",
935+
"designation",
936+
"name",
937+
"asteroid_name",
938+
"comet_name",
939+
),
940+
help=(
941+
"target identifer type; automatically set to 'designation' for "
942+
"target names that appear to be a cometary designation"
943+
),
944+
)
945+
horizons.add_argument(
946+
"--quantities",
947+
"-q",
948+
default="1,3,9,19,20,23,24,27,33",
949+
help="table quantities to return as a comma-separated list of integers",
950+
)
951+
952+
miriade: argparse._ArgumentGroup = parser.add_argument_group(
953+
"IMCCE Miriade options"
954+
)
955+
miriade.add_argument(
956+
"--type",
957+
"-t",
958+
default="asteroid",
959+
choices=(
960+
"asteroid",
961+
"comet",
962+
"dwarf planet",
963+
"planet",
964+
"satellite",
965+
),
966+
help="target identifer type",
967+
)
968+
969+
args: argparse.Namespace = parser.parse_args()
970+
971+
eph: Ephem
972+
epochs: dict = {
973+
"start": args.start,
974+
"stop": args.stop,
975+
}
976+
if args.service == "mpc":
977+
if args.step.unit == u.dimensionless_unscaled:
978+
epochs["number"] = int(args.step)
979+
else:
980+
epochs["step"] = args.step
981+
982+
eph = Ephem.from_mpc(
983+
args.target,
984+
epochs=epochs,
985+
location=args.location,
986+
)
987+
988+
# simplify output
989+
for k in ["Targetname"]:
990+
del eph.table[k]
991+
elif args.service == "horizons":
992+
epochs["step"] = (
993+
int(args.step) if args.step.unit == u.dimensionless_unscaled else args.step
994+
)
995+
996+
# comet specific options: always avoid multiple fragment matches, always
997+
# return closest apparition
998+
closest_apparition: bool = False
999+
no_fragments: bool = False
1000+
id_type: Union[str, None] = args.id_type
1001+
try:
1002+
Names.parse_comet(args.target)
1003+
closest_apparition = True
1004+
no_fragments = True
1005+
id_type = "designation" if id_type is None else id_type
1006+
except TargetNameParseError:
1007+
pass
1008+
1009+
eph = Ephem.from_horizons(
1010+
args.target,
1011+
id_type=id_type,
1012+
closest_apparition=closest_apparition,
1013+
no_fragments=no_fragments,
1014+
epochs=epochs,
1015+
location=args.location,
1016+
quantities=args.quantities,
1017+
)
1018+
1019+
# simplify output
1020+
eph["epoch"].format = "iso"
1021+
eph["ra"] = eph["ra"].unmasked
1022+
eph["dec"] = eph["dec"].unmasked
1023+
for k in ["M1", "k1", "solar_presence", "lunar_presence", "targetname"]:
1024+
del eph.table[k]
1025+
elif args.service == "miriade":
1026+
if args.step.unit == u.dimensionless_unscaled:
1027+
epochs["number"] = int(args.step)
1028+
else:
1029+
epochs["step"] = args.step
1030+
1031+
eph = Ephem.from_miriade(
1032+
args.target,
1033+
objtype=args.type,
1034+
epochs=epochs,
1035+
location=args.location,
1036+
coordtype=1,
1037+
)
1038+
1039+
# simplify output
1040+
eph["epoch"].format = "iso"
1041+
for k in ["target"]:
1042+
del eph.table[k]
1043+
for k in eph.field_names:
1044+
if hasattr(eph[k], "unmasked"):
1045+
eph[k] = eph[k].unmasked
1046+
1047+
# convert RA and Dec to Angle in units of degree
1048+
eph["ra"] = Angle(eph["ra"], eph["ra"].unit).to("deg")
1049+
eph["dec"] = Angle(eph["dec"], eph["dec"].unit).to("deg")
1050+
1051+
if args.radec == "hmsdms":
1052+
eph["ra"].info.format = lambda x: x.to_string(
1053+
sep=":", precision=2, unit="hourangle"
1054+
)
1055+
eph["dec"].info.format = lambda x: x.to_string(sep=":", precision=1)
1056+
else:
1057+
eph["ra"].info.format = lambda x: x.to_string()
1058+
eph["dec"].info.format = lambda x: x.to_string()
1059+
1060+
# unified output order for most common columns
1061+
fields = eph.field_names
1062+
for k in eph._translate_columns(
1063+
[
1064+
"RA*cos(Dec)_rate",
1065+
"DEC_rate",
1066+
"elong",
1067+
"phase",
1068+
"delta",
1069+
"rh",
1070+
"dec",
1071+
"ra",
1072+
"epoch",
1073+
],
1074+
ignore_missing=True,
1075+
):
1076+
if k not in fields:
1077+
continue
1078+
fields.insert(0, fields.pop(fields.index(k)))
1079+
1080+
eph[fields].table.pprint_all()
1081+
1082+
1083+
if __name__ == "__main__":
1084+
main()

setup.cfg

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ docs =
6767
ginga.rv.plugins =
6868
cometaryenhancements = sbpy.ginga_plugins:setup_cometaryenhancements
6969

70+
console_scripts =
71+
sbpy-ephem = sbpy.data.ephem:main
72+
7073
[tool:pytest]
7174
minversion = 7.0
7275
testpaths = "sbpy" "docs"

0 commit comments

Comments
 (0)