Compute Latency

This example demonstrates how to use direct function calls of the low-level TAT-C library to perform latency analysis.

Similar to the Collect Observations and Compute Coverage examples, the first steps are to define the satellites for the mission. This example again uses the NOAA-20 satellite with a two-line elements model from July 2022 and a VIIRS instrument with field of regard computed based on a 834km altitude and 3000km swath width. Points are distributed over the globe with a 5000 km characteristic distance and observations are collected over a 30 day period starting on July 14, 2022 at noon UTC.

from tatc import utils
from tatc.schemas import Instrument, Satellite, TwoLineElements

viirs = Instrument(
    name="VIIRS",
    field_of_regard=utils.swath_width_to_field_of_regard(834000, 3000000),
    req_target_sunlit=True,
)
noaa20 = Satellite(
    name="NOAA 20",
    orbit=TwoLineElements(
        tle=[
            "1 43013U 17073A   22195.78278435  .00000038  00000+0  38919-4 0  9996",
            "2 43013  98.7169 133.9110 0001202  63.8768 296.2532 14.19561306241107",
        ]
    ),
    instruments=[viirs],
)

from tatc.generation import generate_equally_spaced_points

points_df = generate_equally_spaced_points(5000e3)

from tatc.schemas import Point

points = points_df.apply(
    lambda r: Point(id=r.point_id, latitude=r.geometry.y, longitude=r.geometry.x),
    axis=1,
)

from datetime import datetime, timedelta, timezone

start = datetime(year=2022, month=7, day=14, hour=12, tzinfo=timezone.utc)
end = start + timedelta(days=30)

from tatc.analysis import collect_observations
from joblib import Parallel, delayed
import pandas as pd

observations_list = Parallel(n_jobs=-1)(
    delayed(collect_observations)(point, noaa20, start, end) for point in points
)
observations = pd.concat(observations_list, ignore_index=True)
display(observations)
point_id geometry satellite instrument start end epoch sat_alt sat_az
0 0 POINT Z (-157.51699 -67.51699 0) NOAA 20 VIIRS 2022-07-26 23:05:45.258746+00:00 2022-07-26 23:08:44.580526+00:00 2022-07-26 23:07:14.919636+00:00 23.232376 96.383771
1 0 POINT Z (-157.51699 -67.51699 0) NOAA 20 VIIRS 2022-07-30 23:30:00.700453+00:00 2022-07-30 23:35:11.668269+00:00 2022-07-30 23:32:36.184361+00:00 29.980441 90.571477
2 0 POINT Z (-157.51699 -67.51699 0) NOAA 20 VIIRS 2022-07-31 01:09:44.380087+00:00 2022-07-31 01:17:23.069693+00:00 2022-07-31 01:13:33.724890+00:00 83.932517 71.659563
3 0 POINT Z (-157.51699 -67.51699 0) NOAA 20 VIIRS 2022-07-31 23:11:40.551119+00:00 2022-07-31 23:15:24.047070+00:00 2022-07-31 23:13:32.299094500+00:00 24.749767 94.883549
4 0 POINT Z (-157.51699 -67.51699 0) NOAA 20 VIIRS 2022-08-01 00:50:55.502645+00:00 2022-08-01 00:58:28.482647+00:00 2022-08-01 00:54:41.992646+00:00 70.126386 72.613305
... ... ... ... ... ... ... ... ... ...
2169 31 POINT Z (157.24514 67.38106 0) NOAA 20 VIIRS 2022-08-12 18:28:51.557986+00:00 2022-08-12 18:34:04.353232+00:00 2022-08-12 18:31:27.955609+00:00 31.258697 324.067710
2170 31 POINT Z (157.24514 67.38106 0) NOAA 20 VIIRS 2022-08-12 21:48:45.630367+00:00 2022-08-12 21:49:49.415577+00:00 2022-08-12 21:49:17.522972+00:00 21.115155 12.621024
2171 31 POINT Z (157.24514 67.38106 0) NOAA 20 VIIRS 2022-08-12 23:25:37.442375+00:00 2022-08-12 23:31:05.304568+00:00 2022-08-12 23:28:21.373471500+00:00 32.779326 36.304442
2172 31 POINT Z (157.24514 67.38106 0) NOAA 20 VIIRS 2022-08-13 01:04:22.215027+00:00 2022-08-13 01:11:48.221137+00:00 2022-08-13 01:08:05.218082+00:00 73.798507 55.756224
2173 31 POINT Z (157.24514 67.38106 0) NOAA 20 VIIRS 2022-08-13 02:45:33.011228+00:00 2022-08-13 02:51:56.674083+00:00 2022-08-13 02:48:44.842655500+00:00 40.285445 264.921794

2174 rows × 9 columns

Latency analysis measures the interval between observations and downlink to the first ground station. The following specifies a ground station at Hoboken with a 10-degree minimum elevaiton angle for downlink.

from tatc.schemas import GroundStation

hoboken = GroundStation(
    name="Hoboken", latitude=40.74259, longitude=-74.02686, min_elevation_angle=10
)

from tatc.analysis import collect_downlinks

downlinks = collect_downlinks(hoboken, noaa20, start, end)
display(downlinks)
station geometry satellite start end epoch
0 Hoboken POINT Z (-74.02686 40.74259 0) NOAA 20 2022-07-14 17:12:43.763296+00:00 2022-07-14 17:22:57.456044+00:00 2022-07-14 17:17:50.609670+00:00
1 Hoboken POINT Z (-74.02686 40.74259 0) NOAA 20 2022-07-14 18:54:04.803537+00:00 2022-07-14 19:02:25.062501+00:00 2022-07-14 18:58:14.933019+00:00
2 Hoboken POINT Z (-74.02686 40.74259 0) NOAA 20 2022-07-15 05:32:06.654997+00:00 2022-07-15 05:39:35.858756+00:00 2022-07-15 05:35:51.256876500+00:00
3 Hoboken POINT Z (-74.02686 40.74259 0) NOAA 20 2022-07-15 07:11:08.223574+00:00 2022-07-15 07:21:37.089606+00:00 2022-07-15 07:16:22.656590+00:00
4 Hoboken POINT Z (-74.02686 40.74259 0) NOAA 20 2022-07-15 16:54:27.877548+00:00 2022-07-15 17:03:52.170605+00:00 2022-07-15 16:59:10.024076500+00:00
... ... ... ... ... ... ...
115 Hoboken POINT Z (-74.02686 40.74259 0) NOAA 20 2022-08-12 08:28:09.529139+00:00 2022-08-12 08:32:42.603014+00:00 2022-08-12 08:30:26.066076500+00:00
116 Hoboken POINT Z (-74.02686 40.74259 0) NOAA 20 2022-08-12 16:30:12.255865+00:00 2022-08-12 16:37:46.514194+00:00 2022-08-12 16:33:59.385029500+00:00
117 Hoboken POINT Z (-74.02686 40.74259 0) NOAA 20 2022-08-12 18:08:28.080213+00:00 2022-08-12 18:19:02.956149+00:00 2022-08-12 18:13:45.518181+00:00
118 Hoboken POINT Z (-74.02686 40.74259 0) NOAA 20 2022-08-13 06:26:51.204464+00:00 2022-08-13 06:37:31.285805+00:00 2022-08-13 06:32:11.245134500+00:00
119 Hoboken POINT Z (-74.02686 40.74259 0) NOAA 20 2022-08-13 08:08:18.072998+00:00 2022-08-13 08:15:29.539014+00:00 2022-08-13 08:11:53.806006+00:00

120 rows × 6 columns

Finally, latencies are computed by comparing the observations and downlink opportunities.

from tatc.analysis import compute_latencies

latencies = compute_latencies(observations, downlinks)
display(latencies)
point_id geometry satellite instrument sat_alt sat_az station downlinked latency observed
0 20 POINT Z (22.34708 22.41505 0) NOAA 20 VIIRS 46.686116 260.777813 Hoboken 2022-07-14 17:17:50.609670+00:00 0 days 05:10:34.144994500 2022-07-14 12:07:16.464675500+00:00
1 28 POINT Z (22.34708 67.38106 0) NOAA 20 VIIRS 29.024717 271.880297 Hoboken 2022-07-14 17:17:50.609670+00:00 0 days 04:57:46.494320 2022-07-14 12:20:04.115350+00:00
2 27 POINT Z (-22.61894 67.38106 0) NOAA 20 VIIRS 48.704552 49.346995 Hoboken 2022-07-14 17:17:50.609670+00:00 0 days 04:56:13.129769 2022-07-14 12:21:37.479901+00:00
3 25 POINT Z (-112.55097 67.38106 0) NOAA 20 VIIRS 31.323022 324.299192 Hoboken 2022-07-14 17:17:50.609670+00:00 0 days 04:47:32.626919500 2022-07-14 12:30:17.982750500+00:00
4 24 POINT Z (-157.51699 67.38106 0) NOAA 20 VIIRS 55.694847 103.325667 Hoboken 2022-07-14 17:17:50.609670+00:00 0 days 04:44:55.809464 2022-07-14 12:32:54.800206+00:00
... ... ... ... ... ... ... ... ... ... ...
2169 26 POINT Z (-67.58495 67.38106 0) NOAA 20 VIIRS 28.848897 327.376449 None NaT NaT 2022-08-13 09:44:37.403851500+00:00
2170 5 POINT Z (67.3131 -67.51699 0) NOAA 20 VIIRS 78.725002 245.320118 None NaT NaT 2022-08-13 10:37:41.608547500+00:00
2171 20 POINT Z (22.34708 22.41505 0) NOAA 20 VIIRS 35.605265 70.967792 None NaT NaT 2022-08-13 11:04:24.936363+00:00
2172 28 POINT Z (22.34708 67.38106 0) NOAA 20 VIIRS 56.654314 258.438321 None NaT NaT 2022-08-13 11:16:22.849990500+00:00
2173 27 POINT Z (-22.61894 67.38106 0) NOAA 20 VIIRS 31.043210 33.884307 None NaT NaT 2022-08-13 11:19:02.608528500+00:00

2174 rows × 10 columns

Similar to Coverage Analysis, latency analysis can also be reduced to compute descriptive statistics for each observation point.

from tatc.analysis import reduce_latencies

reduced_results = reduce_latencies(latencies)
display(reduced_results)
point_id geometry latency samples
0 0 POINT Z (-157.51699 -67.51699 0) 0 days 05:10:59.268689 31
1 1 POINT Z (-112.55097 -67.51699 0) 0 days 08:14:18.836280 31
2 2 POINT Z (-67.58495 -67.51699 0) 0 days 04:21:48.654757 30
3 3 POINT Z (-22.61894 -67.51699 0) 0 days 01:17:44.394353 31
4 4 POINT Z (22.34708 -67.51699 0) 0 days 04:08:15.422021 31
5 5 POINT Z (67.3131 -67.51699 0) 0 days 08:42:06.877307 31
6 6 POINT Z (112.27912 -67.51699 0) 0 days 02:43:07.838991 33
7 7 POINT Z (157.24514 -67.51699 0) 0 days 02:19:26.312887 34
8 8 POINT Z (-157.51699 -22.55097 0) 0 days 05:44:31.317841 34
9 9 POINT Z (-112.55097 -22.55097 0) 0 days 08:37:46.021444 34
10 10 POINT Z (-67.58495 -22.55097 0) 0 days 00:17:46.170450 32
11 11 POINT Z (-22.61894 -22.55097 0) 0 days 01:35:26.242661 32
12 12 POINT Z (22.34708 -22.55097 0) 0 days 04:33:05.882867 32
13 13 POINT Z (67.3131 -22.55097 0) 0 days 09:05:11.024889 34
14 14 POINT Z (112.27912 -22.55097 0) 0 days 00:45:24.121858 32
15 15 POINT Z (157.24514 -22.55097 0) 0 days 02:52:47.346982 32
16 16 POINT Z (-157.51699 22.41505 0) 0 days 06:21:54.644226 32
17 17 POINT Z (-112.55097 22.41505 0) 0 days 09:24:22.656684 34
18 18 POINT Z (-67.58495 22.41505 0) 0 days 06:12:15.863428 32
19 19 POINT Z (-22.61894 22.41505 0) 0 days 02:11:37.441686 34
20 20 POINT Z (22.34708 22.41505 0) 0 days 06:27:50.003063 34
21 21 POINT Z (67.3131 22.41505 0) 0 days 07:34:34.545599 32
22 22 POINT Z (112.27912 22.41505 0) 0 days 00:32:56.099903 34
23 23 POINT Z (157.24514 22.41505 0) 0 days 03:11:43.033986 32
24 24 POINT Z (-157.51699 67.38106 0) 0 days 05:48:32.308626 173
25 25 POINT Z (-112.55097 67.38106 0) 0 days 06:31:10.233941 173
26 26 POINT Z (-67.58495 67.38106 0) 0 days 04:58:06.133920 171
27 27 POINT Z (-22.61894 67.38106 0) 0 days 03:01:50.017806 176
28 28 POINT Z (22.34708 67.38106 0) 0 days 04:34:58.875476 174
29 29 POINT Z (67.3131 67.38106 0) 0 days 04:11:56.901332 174
30 30 POINT Z (112.27912 67.38106 0) 0 days 04:35:16.325513 177
31 31 POINT Z (157.24514 67.38106 0) 0 days 05:24:20.336656 178

Finally, GeoPlot can visualize the geospatial data.

import matplotlib.pyplot as plt
import cartopy.crs as ccrs

reduced_results["latency_hr"] = reduced_results.latency / timedelta(hours=1)

fig, ax = plt.subplots(figsize=(8, 6), subplot_kw={"projection": ccrs.PlateCarree()})
reduced_results.plot(
    column="latency_hr",
    legend=True,
    legend_kwds={"label": "Latency (hr)", "orientation": "horizontal"},
    ax=ax,
    transform=ccrs.PlateCarree()
)
ax.coastlines()
ax.set_global()
plt.show()
../_images/125b9fca514686166f5d23a3c45ce31c334682f4d294a57cbe0927235c33aa5a.png

Similar to Coverage Analysis, cells can also be used to aggregate descriptive statistics. Note that, when working on the reduced results, the revisit aggregation function must perform a weighted average based on the number of samples for each point.

from tatc.generation.cells import generate_equally_spaced_cells

cells_df = generate_equally_spaced_cells(5000e3)

import numpy as np

grid_results = (
    cells_df.sjoin(reduced_results, how="inner", predicate="contains")
    .dissolve(
        by="cell_id",
        aggfunc={
            "samples": "sum",
            "latency_hr": lambda r: np.average(
                r, weights=reduced_results.loc[r.index, "samples"]
            ),
        },
    )
    .reset_index()
)
display(grid_results)
cell_id geometry samples latency_hr
0 0 POLYGON Z ((-135.03398 -90 0, -135.03398 -45.0... 31 5.183130
1 1 POLYGON Z ((-90.06796 -90 0, -90.06796 -45.033... 31 8.238566
2 2 POLYGON Z ((-45.10194 -90 0, -45.10194 -45.033... 30 4.363515
3 3 POLYGON Z ((-0.13593 -90 0, -0.13593 -45.03398... 31 1.295665
4 4 POLYGON Z ((44.83009 -90 0, 44.83009 -45.03398... 31 4.137617
5 5 POLYGON Z ((89.79611 -90 0, 89.79611 -45.03398... 31 8.701910
6 6 POLYGON Z ((134.76213 -90 0, 134.76213 -45.033... 33 2.718844
7 7 POLYGON Z ((179.72815 -90 0, 179.72815 -45.033... 34 2.323976
8 8 POLYGON Z ((-135.03398 -45.03398 0, -135.03398... 34 5.742033
9 9 POLYGON Z ((-90.06796 -45.03398 0, -90.06796 -... 34 8.629450
10 10 POLYGON Z ((-45.10194 -45.03398 0, -45.10194 -... 32 0.296158
11 11 POLYGON Z ((-0.13593 -45.03398 0, -0.13593 -0.... 32 1.590623
12 12 POLYGON Z ((44.83009 -45.03398 0, 44.83009 -0.... 32 4.551634
13 13 POLYGON Z ((89.79611 -45.03398 0, 89.79611 -0.... 34 9.086396
14 14 POLYGON Z ((134.76213 -45.03398 0, 134.76213 -... 32 0.756701
15 15 POLYGON Z ((179.72815 -45.03398 0, 179.72815 -... 32 2.879819
16 16 POLYGON Z ((-135.03398 -0.06796 0, -135.03398 ... 32 6.365179
17 17 POLYGON Z ((-90.06796 -0.06796 0, -90.06796 44... 34 9.406294
18 18 POLYGON Z ((-45.10194 -0.06796 0, -45.10194 44... 32 6.204407
19 19 POLYGON Z ((-0.13593 -0.06796 0, -0.13593 44.8... 34 2.193734
20 20 POLYGON Z ((44.83009 -0.06796 0, 44.83009 44.8... 34 6.463890
21 21 POLYGON Z ((89.79611 -0.06796 0, 89.79611 44.8... 32 7.576263
22 22 POLYGON Z ((134.76213 -0.06796 0, 134.76213 44... 34 0.548917
23 23 POLYGON Z ((179.72815 -0.06796 0, 179.72815 44... 32 3.195287
24 24 POLYGON Z ((-135.03398 44.89806 0, -135.03398 ... 173 5.808975
25 25 POLYGON Z ((-90.06796 44.89806 0, -90.06796 89... 173 6.519509
26 26 POLYGON Z ((-45.10194 44.89806 0, -45.10194 89... 171 4.968371
27 27 POLYGON Z ((-0.13593 44.89806 0, -0.13593 89.8... 176 3.030561
28 28 POLYGON Z ((44.83009 44.89806 0, 44.83009 89.8... 174 4.583021
29 29 POLYGON Z ((89.79611 44.89806 0, 89.79611 89.8... 174 4.199139
30 30 POLYGON Z ((134.76213 44.89806 0, 134.76213 89... 177 4.587868
31 31 POLYGON Z ((179.72815 44.89806 0, 179.72815 89... 178 5.405649

Finally, plotting can illustrate the latency over global regions.

import matplotlib.pyplot as plt
import cartopy.crs as ccrs

fig, ax = plt.subplots(figsize=(8, 6), subplot_kw={"projection": ccrs.PlateCarree()})
grid_results.plot(
    column="latency_hr",
    cmap="viridis",
    edgecolor="k",
    legend=True,
    legend_kwds={"label": "Latency (hr)", "orientation": "horizontal"},
    ax=ax,
)
ax.coastlines()
ax.set_global()
plt.show()
../_images/62997784a2d23770476879015ce127c671f4692f360866d39bfd54b1111eb112.png