Skip to content

Commit 58cbc63

Browse files
committed
vectorized
1 parent 2c24a28 commit 58cbc63

3 files changed

Lines changed: 43 additions & 61 deletions

File tree

pyorbital/orbital.py

Lines changed: 41 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import logging
55
import warnings
66
from functools import partial
7-
from typing import Optional
87

98
import numpy as np
109
from scipy import optimize
@@ -95,6 +94,16 @@ def compute_azimuth_elevation(top_s, top_e, top_z, rg_):
9594

9695
return np.rad2deg(az_), np.rad2deg(el_)
9796

97+
def _look_from_ecef(pos, opos, lon_rad, lat_rad, theta):
98+
"""Compute azimuth/elevation from two ECEF positions."""
99+
rx = pos[0] - opos[0]
100+
ry = pos[1] - opos[1]
101+
rz = pos[2] - opos[2]
102+
rg_ = np.sqrt(rx * rx + ry * ry + rz * rz)
103+
104+
top_s, top_e, top_z = ecef_to_topocentric(rx, ry, rz, lat_rad, lon_rad, theta)
105+
return compute_azimuth_elevation(top_s, top_e, top_z, rg_)
106+
98107
def get_observer_look(sat_lon, sat_lat, sat_alt, utc_time, lon, lat, alt):
99108
"""Calculate observer's look angle to a satellite.
100109
@@ -109,27 +118,24 @@ def get_observer_look(sat_lon, sat_lat, sat_alt, utc_time, lon, lat, alt):
109118
:param alt: Observer altitude in km
110119
:return: (Azimuth, Elevation) in degrees
111120
"""
112-
# Get satellite and observer ECEF positions
113-
(pos_x, pos_y, pos_z), _ = astronomy.observer_position(utc_time, sat_lon, sat_lat, sat_alt)
114-
(opos_x, opos_y, opos_z), _ = astronomy.observer_position(utc_time, lon, lat, alt)
115-
116-
lon = np.deg2rad(lon)
117-
lat = np.deg2rad(lat)
118-
119-
# Compute local sidereal time
120-
theta = (astronomy.gmst(utc_time) + lon) % (2 * np.pi)
121-
122-
# Vector from observer to satellite
123-
rx = pos_x - opos_x
124-
ry = pos_y - opos_y
125-
rz = pos_z - opos_z
126-
rg_ = np.sqrt(rx * rx + ry * ry + rz * rz)
127-
128-
# Convert to topocentric coordinates
129-
top_s, top_e, top_z = ecef_to_topocentric(rx, ry, rz, lat, lon, theta)
130-
131-
# Compute azimuth and elevation
132-
return compute_azimuth_elevation(top_s, top_e, top_z, rg_)
121+
(pos_x, pos_y, pos_z), _ = astronomy.observer_position(
122+
utc_time, sat_lon, sat_lat, sat_alt
123+
)
124+
(opos_x, opos_y, opos_z), _ = astronomy.observer_position(
125+
utc_time, lon, lat, alt
126+
)
127+
128+
lon_rad = np.deg2rad(lon)
129+
lat_rad = np.deg2rad(lat)
130+
theta = (astronomy.gmst(utc_time) + lon_rad) % (2 * np.pi)
131+
132+
return _look_from_ecef(
133+
(pos_x, pos_y, pos_z),
134+
(opos_x, opos_y, opos_z),
135+
lon_rad,
136+
lat_rad,
137+
theta
138+
)
133139

134140

135141
class Orbital:
@@ -257,7 +263,7 @@ def _find_single_crossing(
257263
horizon: float,
258264
is_aos: bool,
259265
max_search_min: float = 1440.0
260-
) -> Optional[dt.datetime]:
266+
) -> dt.datetime | None:
261267
"""Internal helper to find the single next horizon crossing time (AOS or AOL)."""
262268
elev_func = partial(self._elevation, utc_time, lon, lat, alt, horizon)
263269

@@ -298,7 +304,7 @@ def find_aos(
298304
lat: float,
299305
alt: float = 0,
300306
horizon: float = 0
301-
) -> Optional[dt.datetime]:
307+
) -> dt.datetime | None:
302308
"""Find the time of the next Acquisition of Signal (AOS) after utc_time (while rising)."""
303309
return self._find_single_crossing(utc_time, lon, lat, alt, horizon, is_aos=True)
304310

@@ -309,7 +315,7 @@ def find_aol(
309315
lat: float,
310316
alt: float = 0,
311317
horizon: float = 0
312-
) -> Optional[dt.datetime]:
318+
) -> dt.datetime | None:
313319
"""Find the time of the next Acquisition of Signal (AOL) after utc_time (while setting)."""
314320
return self._find_single_crossing(utc_time, lon, lat, alt, horizon, is_aos=False)
315321

@@ -331,19 +337,17 @@ def get_observer_look(self, utc_time, lon, lat, alt):
331337
(pos_x, pos_y, pos_z), _ = self.get_position(time_ref, normalize=False)
332338
(opos_x, opos_y, opos_z), _ = astronomy.observer_position(time_ref, lon, lat, alt)
333339

334-
rx = pos_x - opos_x
335-
ry = pos_y - opos_y
336-
rz = pos_z - opos_z
337-
rg_ = np.sqrt(rx * rx + ry * ry + rz * rz)
338-
339340
lon_rad = np.deg2rad(lon)
340341
lat_rad = np.deg2rad(lat)
341-
342342
theta = (astronomy.gmst(time_ref) + lon_rad) % (2 * np.pi)
343343

344-
top_s, top_e, top_z = ecef_to_topocentric(rx, ry, rz, lat_rad, lon_rad, theta)
345-
346-
return compute_azimuth_elevation(top_s, top_e, top_z, rg_)
344+
return _look_from_ecef(
345+
(pos_x, pos_y, pos_z),
346+
(opos_x, opos_y, opos_z),
347+
lon_rad,
348+
lat_rad,
349+
theta
350+
)
347351

348352
def get_orbit_number(self, utc_time, tbus_style=False, as_float=False):
349353
"""Calculate orbit number at specified time.
@@ -420,8 +424,8 @@ def get_next_passes(
420424
zcs = np.where(np.diff(np.sign(elev)))[0]
421425

422426
res: list[tuple[dt.datetime, dt.datetime, dt.datetime]] = []
423-
risetime: Optional[dt.datetime] = None
424-
risemins: Optional[float] = None
427+
risetime: dt.datetime | None = None
428+
risemins: float | None = None
425429

426430
elev_func = partial(self._elevation, utc_time, lon, lat, alt, horizon)
427431
elev_inv_func = partial(self._elevation_inv, utc_time, lon, lat, alt, horizon)
@@ -435,7 +439,7 @@ def get_next_passes(
435439
risemins = horizon_mins
436440
else:
437441
falltime = horizon_time
438-
fallmins: Optional[float] = horizon_mins
442+
fallmins: float | None = horizon_mins
439443

440444
if risetime is None or risemins is None or fallmins is None:
441445
continue

pyorbital/tests/test_orbital.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,8 @@ def test_orbit_num_equator(self):
9090
t2 = dt.datetime(2013, 3, 2, 22, 3, 00)
9191
on1 = sat.get_orbit_number(t1)
9292
on2 = sat.get_orbit_number(t2)
93-
assert on2 >= on1
94-
assert on2 - on1 <= 1
93+
assert on1 == 6973
94+
assert on2 == 6974
9595
pos1, vel1 = sat.get_position(t1, normalize=False)
9696
pos2, vel2 = sat.get_position(t2, normalize=False)
9797
del vel1, vel2

pyorbital/tests/test_utils.py

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,3 @@
1-
#!/usr/bin/env python
2-
# -*- coding: utf-8 -*-
3-
4-
# Copyright (c) 2012-2024 Pytroll Community
5-
6-
# Author(s):
7-
8-
# Martin Raspaud <[email protected]>
9-
10-
# This program is free software: you can redistribute it and/or modify
11-
# it under the terms of the GNU General Public License as published by
12-
# the Free Software Foundation, either version 3 of the License, or
13-
# (at your option) any later version.
14-
15-
# This program is distributed in the hope that it will be useful,
16-
# but WITHOUT ANY WARRANTY; without even the implied warranty of
17-
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18-
# GNU General Public License for more details.
19-
20-
# You should have received a copy of the GNU General Public License
21-
# along with this program. If not, see <http://www.gnu.org/licenses/>.
22-
231
"""Test suite for utils in pyorbital.orbital."""
242

253
import numpy as np

0 commit comments

Comments
 (0)