Skip to content

Commit

Permalink
optional astropy
Browse files Browse the repository at this point in the history
test pandas and xarray time conversion

remove pypy from travis since travis pypy3 is old
  • Loading branch information
scivision committed Sep 7, 2018
1 parent edb5d62 commit ebf33ab
Show file tree
Hide file tree
Showing 11 changed files with 105 additions and 37 deletions.
1 change: 0 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ git:
python:
- 3.6
- 3.5
- pypy3

os:
- linux
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ Includes some relevant
## Prerequisites

* Python ≥ 3.5 or PyPy3
* [AstroPy](http://www.astropy.org/) (optional, used for ECI coordinates)

References to AstroPy are optional, algorithms from Vallado and Meeus are used if AstroPy is not present.


## Install
Expand All @@ -39,7 +40,7 @@ pip install pymap3d
or for the latest development code:
```sh
git clone https://github.com/scivision/pymap3d

cd pymap3d
pip install -e .
```

Expand Down
10 changes: 6 additions & 4 deletions pymap3d/aer.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ def aer2geodetic(az: float, el: float, srange: float,

def eci2aer(eci: Tuple[float, float, float],
lat0: float, lon0: float, h0: float,
t: datetime) -> Tuple[float, float, float]:
t: datetime,
useastropy: bool=True) -> Tuple[float, float, float]:
"""
Observer => Point
Expand All @@ -95,14 +96,15 @@ def eci2aer(eci: Tuple[float, float, float],
azimuth, elevation (degrees/radians) [0,360),[0,90]
slant range [meters] [0,Infinity)
"""
ecef = np.atleast_2d(eci2ecef(eci, t))
ecef = np.atleast_2d(eci2ecef(eci, t, useastropy))

return ecef2aer(ecef[:, 0], ecef[:, 1], ecef[:, 2], lat0, lon0, h0)


def aer2eci(az: float, el: float, srange: float,
lat0: float, lon0: float, h0: float, t: datetime,
ell=None, deg: bool=True) -> np.ndarray:
ell=None, deg: bool=True,
useastropy: bool=True) -> np.ndarray:
"""
input
Expand All @@ -120,7 +122,7 @@ def aer2eci(az: float, el: float, srange: float,
"""
x, y, z = aer2ecef(az, el, srange, lat0, lon0, h0, ell, deg)

return ecef2eci(np.column_stack((x, y, z)), t)
return ecef2eci(np.column_stack((x, y, z)), t, useastropy)


def aer2ecef(az: float, el: float, srange: float,
Expand Down
5 changes: 3 additions & 2 deletions pymap3d/azelradec.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from datetime import datetime
import numpy as np
from .vallado import azel2radec as vazel2radec, radec2azel as vradec2azel
from .timeconv import str2dt # astropy can't handle xarray times (yet)
try:
from astropy.time import Time
from astropy import units as u
Expand Down Expand Up @@ -36,7 +37,7 @@ def azel2radec(az_deg: float, el_deg: float,

obs = EarthLocation(lat=lat_deg * u.deg, lon=lon_deg * u.deg)

direc = AltAz(location=obs, obstime=Time(time),
direc = AltAz(location=obs, obstime=Time(str2dt(time)),
az=az_deg * u.deg, alt=el_deg * u.deg)

sky = SkyCoord(direc.transform_to(ICRS()))
Expand Down Expand Up @@ -77,6 +78,6 @@ def radec2azel(ra_deg: float, dec_deg: float,
Angle(dec, unit=u.deg),
equinox='J2000.0')

altaz = points.transform_to(AltAz(location=obs, obstime=Time(time)))
altaz = points.transform_to(AltAz(location=obs, obstime=Time(str2dt(time))))

return altaz.az.degree, altaz.alt.degree
5 changes: 3 additions & 2 deletions pymap3d/ecef.py
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,8 @@ def uvw2enu(u: float, v: float, w: float,
return East, North, Up


def eci2geodetic(eci: np.ndarray, t: datetime) -> Tuple[float, float, float]:
def eci2geodetic(eci: np.ndarray, t: datetime,
useastropy: bool=True) -> Tuple[float, float, float]:
"""
convert ECI to geodetic coordinates
Expand All @@ -257,7 +258,7 @@ def eci2geodetic(eci: np.ndarray, t: datetime) -> Tuple[float, float, float]:
eci2geodetic() a.k.a. eci2lla()
"""
ecef = np.atleast_2d(eci2ecef(eci, t))
ecef = np.atleast_2d(eci2ecef(eci, t, useastropy))

return np.asarray(ecef2geodetic(ecef[:, 0], ecef[:, 1], ecef[:, 2])).squeeze()

Expand Down
25 changes: 17 additions & 8 deletions pymap3d/eci.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
from datetime import datetime
import numpy as np
from .datetime2hourangle import datetime2sidereal
try:
from astropy.time import Time
except ImportError as e:
Time = None


def eci2ecef(eci: np.ndarray,
time: datetime) -> np.ndarray:
time: datetime,
useastropy: bool=True) -> np.ndarray:
"""
Observer => Point
Expand All @@ -20,10 +22,13 @@ def eci2ecef(eci: np.ndarray,
------
x,y,z [meters] target ECEF location [0,Infinity)
"""
if Time is None:
raise ImportError('eci2ecef requires Numpy and AstroPy')
useastropy = useastropy and Time

if useastropy:
gst = Time(time).sidereal_time('apparent', 'greenwich').radian
else:
gst = datetime2sidereal(time, 0.)

gst = Time(time).sidereal_time('apparent', 'greenwich').radian
gst = np.atleast_1d(gst)
assert gst.ndim == 1 and isinstance(gst[0], float) # must be in radians!

Expand All @@ -43,7 +48,8 @@ def eci2ecef(eci: np.ndarray,


def ecef2eci(ecef: np.ndarray,
time: datetime) -> np.ndarray:
time: datetime,
useastropy: bool=True) -> np.ndarray:
"""
Point => Point
Expand All @@ -57,10 +63,13 @@ def ecef2eci(ecef: np.ndarray,
------
eci x,y,z (meters)
"""
if Time is None:
raise ImportError('ecef2eci requires AstroPy')
useastropy = useastropy and Time

if useastropy:
gst = Time(time).sidereal_time('apparent', 'greenwich').radian
else:
gst = datetime2sidereal(time, 0.)

gst = Time(time).sidereal_time('apparent', 'greenwich').radian
gst = np.atleast_1d(gst)
assert gst.ndim == 1 and isinstance(gst[0], float) # must be in radians!

Expand Down
9 changes: 5 additions & 4 deletions pymap3d/timeconv.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ def str2dt(time: datetime) -> np.ndarray:
return time
elif isinstance(time, str):
return parse(time)
elif isinstance(time[0], str):
return [parse(t) for t in time]
else:
return time.values.astype('datetime64[us]').astype(datetime)
else: # some sort of iterable
try:
return [parse(t) for t in time]
except TypeError:
return time.values.astype('datetime64[us]').astype(datetime)
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[metadata]
name = pymap3d
version = 1.7.9
version = 1.7.10
author = Michael Hirsch, Ph.D.
author_email = [email protected]
description = pure Python coordinate conversions, following convention of several popular Matlab routines.
Expand Down
35 changes: 33 additions & 2 deletions tests/test_astropy.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def test_anglesep_meeus():
assert pmh.anglesep_meeus(35, 23, 84, 20) == approx(ha)


def test_eci():
def test_eci_astropy():
pytest.importorskip('astropy')

t = '2013-01-15T12:00:05'
Expand All @@ -60,7 +60,7 @@ def test_eci():
pm.aer2eci(aer1[0], aer1[1], -1, 42, -100, 0, t)


def test_eci_times():
def test_eci_times_astropy():
pytest.importorskip('astropy')

with pytest.raises(AssertionError):
Expand All @@ -73,5 +73,36 @@ def test_eci_times():
assert pm.ecef2eci(pm.eci2ecef(eci0s, [t0] * 2), [t0] * 2) == approx(eci0s)


def test_eci_vallado():
t = '2013-01-15T12:00:05'
lla = pm.eci2geodetic(eci0, t, useastropy=False)
assert lla == approx(lla0, rel=0.2)

eci1 = pm.eci2ecef(eci0, t, useastropy=False)
assert eci1 == approx([649012.04640917, -4697980.55129606, 4250818.82815207], rel=0.001)

assert pm.ecef2eci(eci1, t, useastropy=False) == approx(eci0, rel=0.001)

aer1 = pm.eci2aer(eci0, 42, -100, 0, t, useastropy=False)
assert aer1 == approx([83.73050, -6.614478, 1.473510e6], rel=0.001)

assert pm.aer2eci(*aer1, 42, -100, 0, t, useastropy=False) == approx(eci0, rel=0.001)

with pytest.raises(ValueError):
pm.aer2eci(aer1[0], aer1[1], -1, 42, -100, 0, t, useastropy=False)


def test_eci_times_vallado():
with pytest.raises(AssertionError):
pm.eci2ecef(eci0, [t0, t0], useastropy=False)

with pytest.raises(AssertionError):
pm.ecef2eci(eci0, [t0, t0], useastropy=False)

eci0s = np.stack((eci0, eci0))
assert pm.ecef2eci(pm.eci2ecef(eci0s, [t0] * 2, useastropy=False),
[t0] * 2, useastropy=False) == approx(eci0s, rel=0.001)


if __name__ == '__main__':
pytest.main(['-xrsv', __file__])
11 changes: 0 additions & 11 deletions tests/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,8 @@
"""
import pytest
from pytest import approx
from datetime import datetime
import numpy as np
import pymap3d as pm
from pymap3d.timeconv import str2dt

pi = np.pi
nan = np.nan
Expand Down Expand Up @@ -46,15 +44,6 @@ def test_ellipsoid():
assert pm.ecef2geodetic(*xyz0, ell=pm.Ellipsoid('moon')) == approx([41.808706, -82., 4.630807e6])


def test_str2dt():

assert str2dt(datetime(2014, 4, 6, 8)) == datetime(2014, 4, 6, 8) # passthrough
assert str2dt('2014-04-06T08:00:00') == datetime(2014, 4, 6, 8)
ti = [str2dt('2014-04-06T08:00:00'), str2dt('2014-04-06T08:01:02')]
to = [datetime(2014, 4, 6, 8), datetime(2014, 4, 6, 8, 1, 2)]
assert ti == to # even though ti is numpy array of datetime and to is list of datetime


def test_losint():

pytest.importorskip('pytest', minversion='3.5')
Expand Down
34 changes: 34 additions & 0 deletions tests/test_time.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#!/usr/bin/env python
import pytest
from pymap3d.timeconv import str2dt
from datetime import datetime

t0 = datetime(2014, 4, 6, 8)


def test_str2dt():
assert str2dt(t0) == t0 # passthrough
assert str2dt('2014-04-06T08:00:00') == t0
ti = [str2dt('2014-04-06T08:00:00'), str2dt('2014-04-06T08:01:02')]
to = [t0, datetime(2014, 4, 6, 8, 1, 2)]
assert ti == to # even though ti is numpy array of datetime and to is list of datetime


def test_xarray_time():
xarray = pytest.importorskip('xarray')

t = {'time': t0}

ds = xarray.Dataset(t)
assert str2dt(ds['time']) == t0


def test_pandas_time():
pandas = pytest.importorskip('pandas')

t = pandas.Series(t0)
assert str2dt(t) == t0


if __name__ == '__main__':
pytest.main([__file__])

0 comments on commit ebf33ab

Please sign in to comment.