tabsim 0.0.2__tar.gz → 0.0.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {tabsim-0.0.2 → tabsim-0.0.3}/PKG-INFO +10 -2
- {tabsim-0.0.2 → tabsim-0.0.3}/README.md +5 -0
- tabsim-0.0.3/docs/conf.py +14 -0
- {tabsim-0.0.2 → tabsim-0.0.3}/pyproject.toml +3 -2
- {tabsim-0.0.2 → tabsim-0.0.3}/tabsim/config.py +11 -9
- {tabsim-0.0.2 → tabsim-0.0.3}/tabsim/jax/coordinates.py +255 -1
- {tabsim-0.0.2 → tabsim-0.0.3}/tabsim/scripts/sim_vis.py +3 -1
- {tabsim-0.0.2 → tabsim-0.0.3}/tabsim/sky.py +3 -1
- {tabsim-0.0.2 → tabsim-0.0.3}/tabsim/tle.py +6 -4
- {tabsim-0.0.2 → tabsim-0.0.3}/tabsim/write.py +39 -3
- {tabsim-0.0.2 → tabsim-0.0.3}/tabsim.egg-info/PKG-INFO +10 -2
- {tabsim-0.0.2 → tabsim-0.0.3}/tabsim.egg-info/SOURCES.txt +1 -0
- {tabsim-0.0.2 → tabsim-0.0.3}/tabsim.egg-info/requires.txt +4 -0
- {tabsim-0.0.2 → tabsim-0.0.3}/tabsim.egg-info/top_level.txt +1 -0
- {tabsim-0.0.2 → tabsim-0.0.3}/LICENSE +0 -0
- {tabsim-0.0.2 → tabsim-0.0.3}/setup.cfg +0 -0
- {tabsim-0.0.2 → tabsim-0.0.3}/tabsim/__init__.py +0 -0
- {tabsim-0.0.2 → tabsim-0.0.3}/tabsim/dask/__init__.py +0 -0
- {tabsim-0.0.2 → tabsim-0.0.3}/tabsim/dask/coordinates.py +0 -0
- {tabsim-0.0.2 → tabsim-0.0.3}/tabsim/dask/extras.py +0 -0
- {tabsim-0.0.2 → tabsim-0.0.3}/tabsim/dask/interferometry.py +0 -0
- {tabsim-0.0.2 → tabsim-0.0.3}/tabsim/dask/observation.py +0 -0
- {tabsim-0.0.2 → tabsim-0.0.3}/tabsim/data/EDA2.itrf.txt +0 -0
- {tabsim-0.0.2 → tabsim-0.0.3}/tabsim/data/MWA.itrf.txt +0 -0
- {tabsim-0.0.2 → tabsim-0.0.3}/tabsim/data/MeerKAT.itrf.txt +0 -0
- {tabsim-0.0.2 → tabsim-0.0.3}/tabsim/data/__init__.py +0 -0
- {tabsim-0.0.2 → tabsim-0.0.3}/tabsim/data/aux_data/calibrator.skymodel +0 -0
- {tabsim-0.0.2 → tabsim-0.0.3}/tabsim/data/aux_data/norad_satellite.rfimodel +0 -0
- {tabsim-0.0.2 → tabsim-0.0.3}/tabsim/data/aux_data/satellite.ole +0 -0
- {tabsim-0.0.2 → tabsim-0.0.3}/tabsim/data/aux_data/satellite.rfimodel +0 -0
- {tabsim-0.0.2 → tabsim-0.0.3}/tabsim/data/aux_data/stationary.loc +0 -0
- {tabsim-0.0.2 → tabsim-0.0.3}/tabsim/data/aux_data/stationary.rfimodel +0 -0
- {tabsim-0.0.2 → tabsim-0.0.3}/tabsim/data/aux_data/tles/2023-02-21-J8HE0T.json +0 -0
- {tabsim-0.0.2 → tabsim-0.0.3}/tabsim/data/config_files/sim_config_base.yaml +0 -0
- {tabsim-0.0.2 → tabsim-0.0.3}/tabsim/data/config_files/tab_config_base.yaml +0 -0
- {tabsim-0.0.2 → tabsim-0.0.3}/tabsim/jax/__init__.py +0 -0
- {tabsim-0.0.2 → tabsim-0.0.3}/tabsim/jax/interferometry.py +0 -0
- {tabsim-0.0.2 → tabsim-0.0.3}/tabsim/jax/utils.py +0 -0
- {tabsim-0.0.2 → tabsim-0.0.3}/tabsim/plot.py +0 -0
- {tabsim-0.0.2 → tabsim-0.0.3}/tabsim/scripts/__init__.py +0 -0
- {tabsim-0.0.2 → tabsim-0.0.3}/tabsim/scripts/ast_region.py +0 -0
- {tabsim-0.0.2 → tabsim-0.0.3}/tabsim/scripts/flag_data.py +0 -0
- {tabsim-0.0.2 → tabsim-0.0.3}/tabsim/scripts/tle_sat_region.py +0 -0
- {tabsim-0.0.2 → tabsim-0.0.3}/tabsim/tools.py +0 -0
- {tabsim-0.0.2 → tabsim-0.0.3}/tabsim.egg-info/dependency_links.txt +0 -0
- {tabsim-0.0.2 → tabsim-0.0.3}/tabsim.egg-info/entry_points.txt +0 -0
- {tabsim-0.0.2 → tabsim-0.0.3}/tests/test_basic.py +0 -0
- {tabsim-0.0.2 → tabsim-0.0.3}/tests/test_sim-vis.py +0 -0
- {tabsim-0.0.2 → tabsim-0.0.3}/tests/test_version.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tabsim
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.3
|
|
4
4
|
Summary: Trajectory-based Radio Frequency Interference (RFI) simulations for radio interferometeric data.
|
|
5
5
|
Author-email: Chris Finlay <christopher.finlay@epfl.ch>
|
|
6
6
|
Maintainer-email: Chris Finlay <christopher.finlay@epfl.ch>
|
|
@@ -680,7 +680,7 @@ License: GNU GENERAL PUBLIC LICENSE
|
|
|
680
680
|
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
|
681
681
|
|
|
682
682
|
Project-URL: Repository, https://github.com/chrisfinlay/tab-sim.git
|
|
683
|
-
Requires-Python: <3.13,>=3.
|
|
683
|
+
Requires-Python: <3.13,>=3.9
|
|
684
684
|
Description-Content-Type: text/markdown
|
|
685
685
|
License-File: LICENSE
|
|
686
686
|
Requires-Dist: jax
|
|
@@ -704,12 +704,16 @@ Requires-Dist: ipython; extra == "ipy"
|
|
|
704
704
|
Requires-Dist: ipykernel; extra == "ipy"
|
|
705
705
|
Provides-Extra: test
|
|
706
706
|
Requires-Dist: pytest; extra == "test"
|
|
707
|
+
Provides-Extra: docs
|
|
708
|
+
Requires-Dist: sphinx; extra == "docs"
|
|
709
|
+
Requires-Dist: sphinx-rtd-theme; extra == "docs"
|
|
707
710
|
Dynamic: license-file
|
|
708
711
|
|
|
709
712
|
# tab-sim
|
|
710
713
|
|
|
711
714
|
[](https://doi.org/10.1093/mnras/stad1979)
|
|
712
715
|
[](https://doi.org/10.48550/arXiv.2502.00106)
|
|
716
|
+
[](https://tab-sim.readthedocs.io/en/latest/?badge=latest)
|
|
713
717
|
|
|
714
718
|
**T**r**A**jectory **B**ased RFI **Sim**ulations of radio interferometry data.
|
|
715
719
|
A source to visibility model for RFI sources including certain near-field effects.
|
|
@@ -717,6 +721,10 @@ A source to visibility model for RFI sources including certain near-field effect
|
|
|
717
721
|
`tab-sim` is written in [JAX](https://jax.readthedocs.io/en/latest/notebooks/quickstart.html)
|
|
718
722
|
and [Dask](https://www.dask.org) and can therefore use GPUs and/or CPUs to perform larger than memory computation.
|
|
719
723
|
|
|
724
|
+
## Documentation
|
|
725
|
+
|
|
726
|
+
[https://tab-sim.readthedocs.io/](https://tab-sim.readthedocs.io/)
|
|
727
|
+
|
|
720
728
|
## Installation
|
|
721
729
|
|
|
722
730
|
The following instructions are expected to work on Linux machine. If you are running Windows it is recommended to use WSL. If you are running Mac then it is best to start with a [conda](https://docs.conda.io/en/latest/)/[mamba](https://mamba.readthedocs.io/en/latest/index.html) environment and install `python-casacore` before continuing with the `pip` install method. If all else fails there is the [Docker install](#docker).
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://doi.org/10.1093/mnras/stad1979)
|
|
4
4
|
[](https://doi.org/10.48550/arXiv.2502.00106)
|
|
5
|
+
[](https://tab-sim.readthedocs.io/en/latest/?badge=latest)
|
|
5
6
|
|
|
6
7
|
**T**r**A**jectory **B**ased RFI **Sim**ulations of radio interferometry data.
|
|
7
8
|
A source to visibility model for RFI sources including certain near-field effects.
|
|
@@ -9,6 +10,10 @@ A source to visibility model for RFI sources including certain near-field effect
|
|
|
9
10
|
`tab-sim` is written in [JAX](https://jax.readthedocs.io/en/latest/notebooks/quickstart.html)
|
|
10
11
|
and [Dask](https://www.dask.org) and can therefore use GPUs and/or CPUs to perform larger than memory computation.
|
|
11
12
|
|
|
13
|
+
## Documentation
|
|
14
|
+
|
|
15
|
+
[https://tab-sim.readthedocs.io/](https://tab-sim.readthedocs.io/)
|
|
16
|
+
|
|
12
17
|
## Installation
|
|
13
18
|
|
|
14
19
|
The following instructions are expected to work on Linux machine. If you are running Windows it is recommended to use WSL. If you are running Mac then it is best to start with a [conda](https://docs.conda.io/en/latest/)/[mamba](https://mamba.readthedocs.io/en/latest/index.html) environment and install `python-casacore` before continuing with the `pip` install method. If all else fails there is the [Docker install](#docker).
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
sys.path.insert(0, os.path.abspath('..'))
|
|
4
|
+
|
|
5
|
+
project = 'tab‑sim'
|
|
6
|
+
author = 'Chris Finlay'
|
|
7
|
+
extensions = [
|
|
8
|
+
'sphinx.ext.autodoc',
|
|
9
|
+
'sphinx.ext.napoleon',
|
|
10
|
+
'sphinx.ext.viewcode',
|
|
11
|
+
]
|
|
12
|
+
templates_path = ['_templates']
|
|
13
|
+
exclude_patterns = []
|
|
14
|
+
html_theme = 'sphinx_rtd_theme'
|
|
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
|
|
|
5
5
|
|
|
6
6
|
[project]
|
|
7
7
|
name = "tabsim"
|
|
8
|
-
version = "0.0.
|
|
8
|
+
version = "0.0.3"
|
|
9
9
|
license = {file = "LICENSE"}
|
|
10
10
|
readme = "README.md"
|
|
11
11
|
# dynamic = ["version", "readme"]
|
|
@@ -17,7 +17,7 @@ maintainers = [
|
|
|
17
17
|
]
|
|
18
18
|
description = "Trajectory-based Radio Frequency Interference (RFI) simulations for radio interferometeric data."
|
|
19
19
|
|
|
20
|
-
requires-python = ">=3.
|
|
20
|
+
requires-python = ">=3.9,<3.13"
|
|
21
21
|
dependencies = [
|
|
22
22
|
"jax", "dask<=2024.10.0", "xarray", "zarr<3.0.0",
|
|
23
23
|
"dask-ms>=0.2.21", "scipy", "tqdm", "matplotlib",
|
|
@@ -32,6 +32,7 @@ sat = ["regions"]
|
|
|
32
32
|
# all_cpu = ["regions", "bdsf", "TGEpy @ git+https://github.com/chrisfinlay/TGEpy.git"]
|
|
33
33
|
ipy = ["ipython", "ipykernel"]
|
|
34
34
|
test = ["pytest"]
|
|
35
|
+
docs = ["sphinx", "sphinx-rtd-theme"]
|
|
35
36
|
|
|
36
37
|
[project.urls]
|
|
37
38
|
Repository = "https://github.com/chrisfinlay/tab-sim.git"
|
|
@@ -5,7 +5,8 @@ import sys
|
|
|
5
5
|
import shutil
|
|
6
6
|
|
|
7
7
|
import collections.abc
|
|
8
|
-
from typing import Tuple
|
|
8
|
+
from typing import Tuple, Union, Optional
|
|
9
|
+
|
|
9
10
|
from datetime import datetime
|
|
10
11
|
|
|
11
12
|
import dask.array as da
|
|
@@ -13,13 +14,14 @@ from dask.array.core import Array
|
|
|
13
14
|
import xarray as xr
|
|
14
15
|
import numpy as np
|
|
15
16
|
import pandas as pd
|
|
17
|
+
from astropy.time import Time
|
|
16
18
|
|
|
17
19
|
from tabsim.dask.observation import Observation
|
|
18
20
|
from tabsim.sky import generate_random_sky
|
|
19
21
|
from tabsim.plot import plot_uv, plot_src_alt, plot_angular_seps
|
|
20
22
|
from tabsim.write import write_ms, mk_obs_name, mk_obs_dir
|
|
21
|
-
from tabsim.jax.coordinates import calculate_fringe_frequency
|
|
22
|
-
from tabsim.tle import get_visible_satellite_tles
|
|
23
|
+
from tabsim.jax.coordinates import calculate_fringe_frequency, jd_to_mjd
|
|
24
|
+
from tabsim.tle import get_visible_satellite_tles, id_generator
|
|
23
25
|
|
|
24
26
|
from daskms import xds_from_ms
|
|
25
27
|
|
|
@@ -188,7 +190,7 @@ def load_sky_model(file_path: str, freqs: Array, src_type: str) -> tuple:
|
|
|
188
190
|
raise KeyError("'src_type' must be one of {'point', 'gauss', 'exp'}.")
|
|
189
191
|
|
|
190
192
|
|
|
191
|
-
def sigma_value(value: str
|
|
193
|
+
def sigma_value(value: Union[str, float, int], obs: Observation):
|
|
192
194
|
try:
|
|
193
195
|
if isinstance(value, str):
|
|
194
196
|
sigma = (np.mean(obs.noise_std) / np.sqrt(obs.n_time * obs.n_bl)).compute()
|
|
@@ -212,12 +214,9 @@ def load_obs(obs_spec: dict) -> Observation:
|
|
|
212
214
|
return x
|
|
213
215
|
|
|
214
216
|
if obs_["start_time_jd"]:
|
|
215
|
-
from tabascal.jax.coordinates import jd_to_mjd
|
|
216
217
|
|
|
217
218
|
start_time_mjd = jd_to_mjd(obs_["start_time_jd"])
|
|
218
219
|
elif obs_["start_time_isot"]:
|
|
219
|
-
from astropy.time import Time
|
|
220
|
-
from tabascal.jax.coordinates import jd_to_mjd
|
|
221
220
|
|
|
222
221
|
start_time_mjd = jd_to_mjd(
|
|
223
222
|
Time(obs_["start_time_isot"], format="isot", scale="ut1").jd
|
|
@@ -837,11 +836,14 @@ def print_fringe_freq_tle_sat(obs: Observation):
|
|
|
837
836
|
|
|
838
837
|
|
|
839
838
|
def run_sim_config(
|
|
840
|
-
obs_spec: dict = None,
|
|
839
|
+
obs_spec: Optional[dict] = None,
|
|
840
|
+
config_path: Optional[str] = None,
|
|
841
|
+
spacetrack_path: Optional[str] = None,
|
|
841
842
|
) -> Tuple[Observation, str]:
|
|
842
843
|
|
|
843
844
|
from tabsim.tle import id_generator
|
|
844
845
|
|
|
846
|
+
|
|
845
847
|
log_path = f"log_sim_{id_generator()}.txt"
|
|
846
848
|
log = open(log_path, "w")
|
|
847
849
|
backup = sys.stdout
|
|
@@ -859,7 +861,7 @@ def run_sim_config(
|
|
|
859
861
|
obs = load_obs(obs_spec)
|
|
860
862
|
add_astro_sources(obs, obs_spec)
|
|
861
863
|
add_satellite_sources(obs, obs_spec)
|
|
862
|
-
if obs_spec["rfi_sources"]["tle_satellite"]["max_n_sat"] != 0:
|
|
864
|
+
if obs_spec["rfi_sources"]["tle_satellite"]["max_n_sat"] != 0 and spacetrack_path:
|
|
863
865
|
add_tle_satellite_sources(obs, obs_spec, spacetrack_path)
|
|
864
866
|
add_stationary_sources(obs, obs_spec)
|
|
865
867
|
add_gains(obs, obs_spec)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import jax.numpy as jnp
|
|
2
2
|
from jax import jit, vmap, jacrev, config, Array
|
|
3
3
|
from tabsim.jax.utils import jit_with_doc
|
|
4
|
+
from jax.lax import scan
|
|
4
5
|
|
|
5
6
|
config.update("jax_enable_x64", True)
|
|
6
7
|
|
|
@@ -690,7 +691,7 @@ def calculate_fringe_frequency(
|
|
|
690
691
|
r_ecef = xyz_to_itrf(rfi_xyz, gsa)
|
|
691
692
|
s_ecef = r_ecef - jnp.mean(ants_itrf, axis=0)
|
|
692
693
|
s_hat_ecef = s_ecef / jnp.linalg.norm(s_ecef, axis=-1, keepdims=True)
|
|
693
|
-
s_hat_dot = jnp.gradient(s_hat_ecef, times, axis=0)
|
|
694
|
+
s_hat_dot = jnp.gradient(s_hat_ecef, jnp.diff(times[:2])[0], axis=0)
|
|
694
695
|
|
|
695
696
|
a1, a2 = jnp.triu_indices(len(ants_itrf), 1)
|
|
696
697
|
bl_ecef = ants_itrf[a1] - ants_itrf[a2]
|
|
@@ -1080,3 +1081,256 @@ def orbit_fisher(times: Array, orbit_params: Array, RIC_std: Array) -> Array:
|
|
|
1080
1081
|
fisher = vmap(propagate, in_axes=(0, None))(J, F).sum(axis=0)
|
|
1081
1082
|
|
|
1082
1083
|
return fisher
|
|
1084
|
+
|
|
1085
|
+
|
|
1086
|
+
# New Orbit Model with Keplerian Elements
|
|
1087
|
+
|
|
1088
|
+
|
|
1089
|
+
def R_z(angle):
|
|
1090
|
+
c, s = jnp.cos(angle), jnp.sin(angle)
|
|
1091
|
+
return jnp.array([[c, -s, 0], [s, c, 0], [0, 0, 1]])
|
|
1092
|
+
|
|
1093
|
+
|
|
1094
|
+
def R_x(angle):
|
|
1095
|
+
c, s = jnp.cos(angle), jnp.sin(angle)
|
|
1096
|
+
return jnp.array([[1, 0, 0], [0, c, -s], [0, s, c]])
|
|
1097
|
+
|
|
1098
|
+
|
|
1099
|
+
def solve_kepler(M, e, max_iter=100):
|
|
1100
|
+
"""Newton-Raphson solver for Kepler's equation."""
|
|
1101
|
+
|
|
1102
|
+
def newton_step(E, _):
|
|
1103
|
+
f = E - e * jnp.sin(E) - M
|
|
1104
|
+
f_prime = 1 - e * jnp.cos(E)
|
|
1105
|
+
E_new = E - f / f_prime
|
|
1106
|
+
return E_new, E_new
|
|
1107
|
+
|
|
1108
|
+
E0 = M
|
|
1109
|
+
E_final, _ = scan(newton_step, E0, None, length=max_iter)
|
|
1110
|
+
|
|
1111
|
+
return E_final
|
|
1112
|
+
|
|
1113
|
+
|
|
1114
|
+
def kepler_orbit(times, t_epoch, params):
|
|
1115
|
+
"""
|
|
1116
|
+
Compute orbit position over time for a single satellite.
|
|
1117
|
+
|
|
1118
|
+
times: (T,)
|
|
1119
|
+
params: (6,) = [a, e, i, Ω, ω, M0]
|
|
1120
|
+
Returns: (T, 3)
|
|
1121
|
+
"""
|
|
1122
|
+
a, e, i_deg, Ω_deg, ω_deg, M0_deg = params
|
|
1123
|
+
|
|
1124
|
+
mu = 398600.4418 # km^3/s^2
|
|
1125
|
+
|
|
1126
|
+
n = jnp.sqrt(mu / jnp.abs(a) ** 3) * 86400
|
|
1127
|
+
|
|
1128
|
+
i = jnp.radians(i_deg)
|
|
1129
|
+
Ω = jnp.radians(Ω_deg)
|
|
1130
|
+
ω = jnp.radians(ω_deg)
|
|
1131
|
+
M_0 = jnp.radians(M0_deg)
|
|
1132
|
+
|
|
1133
|
+
M = M_0 + n * (times - t_epoch) # Mean anomaly
|
|
1134
|
+
|
|
1135
|
+
E = vmap(solve_kepler, (0, None))(jnp.atleast_1d(M), e)
|
|
1136
|
+
|
|
1137
|
+
nu = 2 * jnp.arctan2(
|
|
1138
|
+
jnp.sqrt(1 + e) * jnp.sin(E / 2), jnp.sqrt(1 - e) * jnp.cos(E / 2)
|
|
1139
|
+
)
|
|
1140
|
+
|
|
1141
|
+
r = a * (1 - e * jnp.cos(E))
|
|
1142
|
+
x_orb = r * jnp.cos(nu)
|
|
1143
|
+
y_orb = r * jnp.sin(nu)
|
|
1144
|
+
z_orb = jnp.zeros_like(x_orb)
|
|
1145
|
+
|
|
1146
|
+
R = R_z(Ω) @ R_x(i) @ R_z(ω)
|
|
1147
|
+
|
|
1148
|
+
pos_orb = jnp.stack([x_orb, y_orb, z_orb], axis=1) # (T, 3)
|
|
1149
|
+
pos_inertial = pos_orb @ R.T # (T, 3)
|
|
1150
|
+
|
|
1151
|
+
return pos_inertial * 1e3
|
|
1152
|
+
|
|
1153
|
+
|
|
1154
|
+
def kepler_orbit_many(
|
|
1155
|
+
times_jd: Array,
|
|
1156
|
+
epoch_jd: Array,
|
|
1157
|
+
elements: Array,
|
|
1158
|
+
# semi_major: Array,
|
|
1159
|
+
# eccentricity: Array,
|
|
1160
|
+
# inclination: Array,
|
|
1161
|
+
# ra_of_asc_node: Array,
|
|
1162
|
+
# arg_of_peri: Array,
|
|
1163
|
+
# mean_anomaly: Array,
|
|
1164
|
+
):
|
|
1165
|
+
"""Propagate Keplerian orbits for a set of satellites using orbital elements as defined in a TLE.
|
|
1166
|
+
|
|
1167
|
+
Parameters
|
|
1168
|
+
----------
|
|
1169
|
+
times_jd : Array (n_time,)
|
|
1170
|
+
Propagation times in Julian Date.
|
|
1171
|
+
epoch_jd : Array (n_sat,)
|
|
1172
|
+
Epoch of the TLE in Julian Date.
|
|
1173
|
+
semi_major : Array (n_sat,)
|
|
1174
|
+
Semi major axis of the orbit in km.
|
|
1175
|
+
eccentricity : Array (n_sat,)
|
|
1176
|
+
Eccentricity of the orbit.
|
|
1177
|
+
inclination : Array (n_sat,)
|
|
1178
|
+
Inclination of the orbit in degrees.
|
|
1179
|
+
ra_of_asc_node : Array (n_sat,)
|
|
1180
|
+
Right ascension of the ascending node in degrees.
|
|
1181
|
+
arg_of_peri : Array (n_sat,)
|
|
1182
|
+
Argument of the pericentre in degrees.
|
|
1183
|
+
mean_anomaly : Array (n_sat,)
|
|
1184
|
+
Mean anomaly of the orbit at the epoch time in degrees.
|
|
1185
|
+
"""
|
|
1186
|
+
|
|
1187
|
+
# elements = [
|
|
1188
|
+
# jnp.atleast_1d(x)
|
|
1189
|
+
# for x in [
|
|
1190
|
+
# semi_major,
|
|
1191
|
+
# eccentricity,
|
|
1192
|
+
# inclination,
|
|
1193
|
+
# ra_of_asc_node,
|
|
1194
|
+
# arg_of_peri,
|
|
1195
|
+
# mean_anomaly,
|
|
1196
|
+
# ]
|
|
1197
|
+
# ]
|
|
1198
|
+
|
|
1199
|
+
pos = vmap(kepler_orbit, (None, 0, 0))(times_jd, epoch_jd, elements)
|
|
1200
|
+
|
|
1201
|
+
return pos
|
|
1202
|
+
|
|
1203
|
+
|
|
1204
|
+
def kepler_orbit_velocity(times_jd: Array, epoch_jd: float, elements: Array) -> Array:
|
|
1205
|
+
|
|
1206
|
+
vel_vmap = vmap(jacrev(kepler_orbit, argnums=(0)), in_axes=(0, None, None))
|
|
1207
|
+
velocity = vel_vmap(times_jd, epoch_jd, elements).reshape(len(times_jd), 3)
|
|
1208
|
+
|
|
1209
|
+
return velocity
|
|
1210
|
+
|
|
1211
|
+
|
|
1212
|
+
def R_uvw(
|
|
1213
|
+
times_jd: Array,
|
|
1214
|
+
epoch_jd: float,
|
|
1215
|
+
elements: Array,
|
|
1216
|
+
) -> Array:
|
|
1217
|
+
"""
|
|
1218
|
+
Calculate the rotation matrices at each time step to transform from an Earth
|
|
1219
|
+
centric reference frame (ECI) to a satellite centric frame given the
|
|
1220
|
+
parameters of its (circular) orbit.
|
|
1221
|
+
|
|
1222
|
+
Parameters
|
|
1223
|
+
----------
|
|
1224
|
+
times: ndarray (n_time,)
|
|
1225
|
+
Times at which to evaluate the rotation matrices.
|
|
1226
|
+
elevation: float
|
|
1227
|
+
Elevation/Altitude of the orbit in metres.
|
|
1228
|
+
inclination: float
|
|
1229
|
+
Inclination angle of the orbit relative to the equatorial plane.
|
|
1230
|
+
lon_asc_node: float
|
|
1231
|
+
Longitude of the ascending node of the orbit. This is the longitude of
|
|
1232
|
+
when the orbit crosses the equator from the south to the north.
|
|
1233
|
+
periapsis: float
|
|
1234
|
+
Perisapsis of the orbit. This is the angular starting point of the orbit
|
|
1235
|
+
at t = 0.
|
|
1236
|
+
|
|
1237
|
+
Returns
|
|
1238
|
+
-------
|
|
1239
|
+
R: ndarray (3, 3)
|
|
1240
|
+
The rotation matrix to orient to a satellite centric (RIC) frame defined
|
|
1241
|
+
by the Radial, In-track, Cross-track components.
|
|
1242
|
+
"""
|
|
1243
|
+
times_jd = jnp.asarray(times_jd)
|
|
1244
|
+
elements = jnp.asarray(elements)
|
|
1245
|
+
|
|
1246
|
+
position = kepler_orbit(times_jd, epoch_jd, elements)
|
|
1247
|
+
velocity = kepler_orbit_velocity(times_jd, epoch_jd, elements)
|
|
1248
|
+
|
|
1249
|
+
vel_hat = velocity / jnp.linalg.norm(velocity, axis=-1, keepdims=True)
|
|
1250
|
+
u_hat = position / jnp.linalg.norm(position, axis=-1, keepdims=True)
|
|
1251
|
+
w_hat = jnp.cross(u_hat, vel_hat)
|
|
1252
|
+
v_hat = jnp.cross(w_hat, u_hat)
|
|
1253
|
+
R = jnp.stack([u_hat.T, v_hat.T, w_hat.T])
|
|
1254
|
+
|
|
1255
|
+
return R
|
|
1256
|
+
|
|
1257
|
+
|
|
1258
|
+
def kepler_RIC_dev(
|
|
1259
|
+
times_jd: Array,
|
|
1260
|
+
epoch_jd: float,
|
|
1261
|
+
true_elements: Array,
|
|
1262
|
+
estimated_elements: Array,
|
|
1263
|
+
) -> Array:
|
|
1264
|
+
"""
|
|
1265
|
+
Calculate the Radial (R), In-track (I) and Cross-track (C) deviations
|
|
1266
|
+
between two circular orbits given their orbit parameters at many time steps.
|
|
1267
|
+
|
|
1268
|
+
Parameters
|
|
1269
|
+
----------
|
|
1270
|
+
times: array (n_time,)
|
|
1271
|
+
Times at which to evaluate the RIC deviations.
|
|
1272
|
+
true_orb_params: ndarray (4,)
|
|
1273
|
+
Orbit parameters (elevation, inclination, lon_asc_node, periapsis) of
|
|
1274
|
+
the object of most interest. The more accurate measurement when
|
|
1275
|
+
comparing the same object.
|
|
1276
|
+
est_orb_params: array (elevation, inclination, lon_asc_node, periapsis)
|
|
1277
|
+
Estimated orbit parameters.
|
|
1278
|
+
|
|
1279
|
+
Returns
|
|
1280
|
+
-------
|
|
1281
|
+
RIC: ndarray (n_time, 3)
|
|
1282
|
+
Radial, In-track and Cross-track coordinates, in metres, of the orbiting
|
|
1283
|
+
body relative to the orbit defined by 'true_orbit_params'.
|
|
1284
|
+
"""
|
|
1285
|
+
times_jd = jnp.asarray(times_jd)
|
|
1286
|
+
true_elements = jnp.asarray(true_elements)
|
|
1287
|
+
estimated_elements = jnp.asarray(estimated_elements)
|
|
1288
|
+
|
|
1289
|
+
true_xyz = kepler_orbit(times_jd, epoch_jd, true_elements)
|
|
1290
|
+
est_xyz = kepler_orbit(times_jd, epoch_jd, estimated_elements)
|
|
1291
|
+
|
|
1292
|
+
R = R_uvw(times_jd, epoch_jd, true_elements)
|
|
1293
|
+
true_uvw = vmap(lambda x, y: x @ y, in_axes=(-1, 0))(R, true_xyz)
|
|
1294
|
+
est_uvw = vmap(lambda x, y: x @ y, in_axes=(-1, 0))(R, est_xyz)
|
|
1295
|
+
|
|
1296
|
+
return est_uvw - true_uvw
|
|
1297
|
+
|
|
1298
|
+
|
|
1299
|
+
def kepler_orbit_fisher(
|
|
1300
|
+
times_jd: Array, epoch_jd: float, elements: Array, RIC_std: Array
|
|
1301
|
+
) -> Array:
|
|
1302
|
+
"""
|
|
1303
|
+
Calculate the inverse covariance (Fisher) matrix in orbital elements
|
|
1304
|
+
induced by errors in the RIC frame of an orbiting object. This is
|
|
1305
|
+
essentially linear uncertainty propagation assuming Gaussian errors.
|
|
1306
|
+
|
|
1307
|
+
Parameters
|
|
1308
|
+
----------
|
|
1309
|
+
times: ndarray (n_time,)
|
|
1310
|
+
Times at which the RIC covariance/standard deviations is defined.
|
|
1311
|
+
orbit_params: ndarray (4,)
|
|
1312
|
+
Orbit parameters (elevation, inclination, lon_asc_node, periapsis) for
|
|
1313
|
+
the orbit defining the origin of the RIC frame.
|
|
1314
|
+
RIC_std: ndarray (3,)
|
|
1315
|
+
The standard deviation in the Radial, In-track and Cross-track
|
|
1316
|
+
directions.
|
|
1317
|
+
|
|
1318
|
+
Returns
|
|
1319
|
+
-------
|
|
1320
|
+
fisher: ndarray (6, 6)
|
|
1321
|
+
The inverse covariance (Fisher) matrix for the orbit parameters induced
|
|
1322
|
+
by the orbit uncertainties in the RIC frame.
|
|
1323
|
+
"""
|
|
1324
|
+
times_jd = jnp.asarray(times_jd)
|
|
1325
|
+
elements = jnp.asarray(elements)
|
|
1326
|
+
RIC_std = jnp.asarray(RIC_std)
|
|
1327
|
+
|
|
1328
|
+
J = jacrev(kepler_RIC_dev, argnums=(3,))(times_jd, epoch_jd, elements, elements)[0]
|
|
1329
|
+
F = jnp.diag(1.0 / RIC_std**2)
|
|
1330
|
+
|
|
1331
|
+
def propagate(J, F):
|
|
1332
|
+
return J.T @ F @ J
|
|
1333
|
+
|
|
1334
|
+
fisher = vmap(propagate, in_axes=(0, None))(J, F).sum(axis=0)
|
|
1335
|
+
|
|
1336
|
+
return fisher
|
|
@@ -5,8 +5,10 @@ from pathlib import Path
|
|
|
5
5
|
|
|
6
6
|
from tabsim.config import load_config, run_sim_config
|
|
7
7
|
|
|
8
|
+
from typing import Union
|
|
8
9
|
|
|
9
|
-
|
|
10
|
+
|
|
11
|
+
def get_abs_path(rel_path: Union[str, None], work_dir: str) -> Union[str, None]:
|
|
10
12
|
if rel_path:
|
|
11
13
|
return os.path.abspath(os.path.join(work_dir, rel_path))
|
|
12
14
|
else:
|
|
@@ -2,8 +2,10 @@ import numpy as np
|
|
|
2
2
|
import jax.numpy as jnp
|
|
3
3
|
import dask.array as da
|
|
4
4
|
|
|
5
|
+
from typing import Optional
|
|
5
6
|
|
|
6
|
-
|
|
7
|
+
|
|
8
|
+
def uniform_points_disk(radius: float, n_src: int, random_seed: Optional[int] = None):
|
|
7
9
|
"""
|
|
8
10
|
Generate uniformly distributed random points on a disk.
|
|
9
11
|
|
|
@@ -21,8 +21,10 @@ from glob import glob
|
|
|
21
21
|
|
|
22
22
|
from importlib.resources import files
|
|
23
23
|
|
|
24
|
+
from typing import Optional
|
|
24
25
|
|
|
25
|
-
|
|
26
|
+
|
|
27
|
+
def make_tle_dir(tle_dir: Optional[str]):
|
|
26
28
|
|
|
27
29
|
if tle_dir:
|
|
28
30
|
tle_dir = os.path.abspath(tle_dir)
|
|
@@ -89,7 +91,7 @@ def get_tles_by_id(
|
|
|
89
91
|
epoch_jd: float,
|
|
90
92
|
window_days: float = 1.0,
|
|
91
93
|
limit: int = 2000,
|
|
92
|
-
tle_dir: str
|
|
94
|
+
tle_dir: Optional[str] = None,
|
|
93
95
|
) -> pd.DataFrame:
|
|
94
96
|
|
|
95
97
|
tle_dir = make_tle_dir(tle_dir)
|
|
@@ -166,7 +168,7 @@ def get_tles_by_name(
|
|
|
166
168
|
epoch_jd: float,
|
|
167
169
|
window_days: float = 1.0,
|
|
168
170
|
limit: int = 10000,
|
|
169
|
-
tle_dir: str
|
|
171
|
+
tle_dir: Optional[str] = None,
|
|
170
172
|
) -> pd.DataFrame:
|
|
171
173
|
|
|
172
174
|
tle_dir = make_tle_dir(tle_dir)
|
|
@@ -291,7 +293,7 @@ def get_visible_satellite_tles(
|
|
|
291
293
|
min_elevation: float,
|
|
292
294
|
names: ArrayLike = [],
|
|
293
295
|
norad_ids: ArrayLike = [],
|
|
294
|
-
tle_dir: str
|
|
296
|
+
tle_dir: Optional[str] = None,
|
|
295
297
|
) -> tuple:
|
|
296
298
|
"""Get the TLEs corresponding to satellites that satisfy the conditions given.
|
|
297
299
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from typing import TYPE_CHECKING
|
|
3
|
+
from typing import TYPE_CHECKING, Optional
|
|
4
4
|
|
|
5
5
|
if TYPE_CHECKING:
|
|
6
6
|
from tabsim.dask.observation import Observation
|
|
@@ -12,9 +12,11 @@ import dask
|
|
|
12
12
|
import dask.array as da
|
|
13
13
|
import numpy as np
|
|
14
14
|
import xarray as xr
|
|
15
|
-
from daskms import Dataset, xds_to_table
|
|
15
|
+
from daskms import Dataset, xds_to_table, xds_from_ms
|
|
16
16
|
import numpy as np
|
|
17
17
|
|
|
18
|
+
from dask.array.core import Array
|
|
19
|
+
|
|
18
20
|
|
|
19
21
|
def rm_dir(path: str, overwrite: bool = True):
|
|
20
22
|
"""Remove a directory but check for existence and overwrite flag before.
|
|
@@ -73,7 +75,7 @@ def mk_obs_dir(output_path: str, obs_name: str, overwrite: bool = True) -> tuple
|
|
|
73
75
|
return save_path, zarr_path, ms_path
|
|
74
76
|
|
|
75
77
|
|
|
76
|
-
def mk_obs_name(prefix: str, obs: Observation, suffix: str = None) -> str:
|
|
78
|
+
def mk_obs_name(prefix: str, obs: Observation, suffix: Optional[str] = None) -> str:
|
|
77
79
|
"""Construct an observation name based on the parameters of the observation and an additional prefix.
|
|
78
80
|
|
|
79
81
|
Parameters
|
|
@@ -381,6 +383,40 @@ def get_stationary_rfi_data(obs: Observation):
|
|
|
381
383
|
return rfi_stat
|
|
382
384
|
|
|
383
385
|
|
|
386
|
+
def add_to_ms(ds: Dataset, ms_path: str, flags: Optional[Array] = None):
|
|
387
|
+
|
|
388
|
+
n_time = ds.attrs["n_time"]
|
|
389
|
+
n_freq = ds.attrs["n_freq"]
|
|
390
|
+
n_corr = 1
|
|
391
|
+
n_bl = ds.attrs["n_bl"]
|
|
392
|
+
n_row = n_time * n_bl
|
|
393
|
+
|
|
394
|
+
xds_ms = xds_from_ms(ms_path)[0]
|
|
395
|
+
|
|
396
|
+
dims = ["row", "chan", "corr"]
|
|
397
|
+
chunks = {k: v for k, v in xds_ms.chunks.items() if k in dims}
|
|
398
|
+
|
|
399
|
+
vis_rfi = ds.vis_rfi.data.reshape(n_row, n_freq, n_corr)
|
|
400
|
+
vis_rfi = xr.DataArray(
|
|
401
|
+
da.stack(
|
|
402
|
+
[vis_rfi, da.zeros_like(vis_rfi), da.zeros_like(vis_rfi), vis_rfi], axis=2
|
|
403
|
+
),
|
|
404
|
+
dims=("row", "chan", "corr"),
|
|
405
|
+
)
|
|
406
|
+
|
|
407
|
+
xds_ms = xds_ms.assign(DATA=xds_ms.DATA + vis_rfi)
|
|
408
|
+
cols = "DATA"
|
|
409
|
+
|
|
410
|
+
if flags:
|
|
411
|
+
flags = xr.DataArray(
|
|
412
|
+
ds.flags.data.reshape(n_row, n_freq, n_corr), dims=("row", "chan", "corr")
|
|
413
|
+
)
|
|
414
|
+
xds_ms = xds_ms.assign(FLAG=flags)
|
|
415
|
+
cols.append("FLAG")
|
|
416
|
+
|
|
417
|
+
dask.compute(xds_to_table([xds_ms], ms_path, cols))
|
|
418
|
+
|
|
419
|
+
|
|
384
420
|
def write_ms(
|
|
385
421
|
ds: Dataset,
|
|
386
422
|
ms_path: str,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tabsim
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.3
|
|
4
4
|
Summary: Trajectory-based Radio Frequency Interference (RFI) simulations for radio interferometeric data.
|
|
5
5
|
Author-email: Chris Finlay <christopher.finlay@epfl.ch>
|
|
6
6
|
Maintainer-email: Chris Finlay <christopher.finlay@epfl.ch>
|
|
@@ -680,7 +680,7 @@ License: GNU GENERAL PUBLIC LICENSE
|
|
|
680
680
|
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
|
681
681
|
|
|
682
682
|
Project-URL: Repository, https://github.com/chrisfinlay/tab-sim.git
|
|
683
|
-
Requires-Python: <3.13,>=3.
|
|
683
|
+
Requires-Python: <3.13,>=3.9
|
|
684
684
|
Description-Content-Type: text/markdown
|
|
685
685
|
License-File: LICENSE
|
|
686
686
|
Requires-Dist: jax
|
|
@@ -704,12 +704,16 @@ Requires-Dist: ipython; extra == "ipy"
|
|
|
704
704
|
Requires-Dist: ipykernel; extra == "ipy"
|
|
705
705
|
Provides-Extra: test
|
|
706
706
|
Requires-Dist: pytest; extra == "test"
|
|
707
|
+
Provides-Extra: docs
|
|
708
|
+
Requires-Dist: sphinx; extra == "docs"
|
|
709
|
+
Requires-Dist: sphinx-rtd-theme; extra == "docs"
|
|
707
710
|
Dynamic: license-file
|
|
708
711
|
|
|
709
712
|
# tab-sim
|
|
710
713
|
|
|
711
714
|
[](https://doi.org/10.1093/mnras/stad1979)
|
|
712
715
|
[](https://doi.org/10.48550/arXiv.2502.00106)
|
|
716
|
+
[](https://tab-sim.readthedocs.io/en/latest/?badge=latest)
|
|
713
717
|
|
|
714
718
|
**T**r**A**jectory **B**ased RFI **Sim**ulations of radio interferometry data.
|
|
715
719
|
A source to visibility model for RFI sources including certain near-field effects.
|
|
@@ -717,6 +721,10 @@ A source to visibility model for RFI sources including certain near-field effect
|
|
|
717
721
|
`tab-sim` is written in [JAX](https://jax.readthedocs.io/en/latest/notebooks/quickstart.html)
|
|
718
722
|
and [Dask](https://www.dask.org) and can therefore use GPUs and/or CPUs to perform larger than memory computation.
|
|
719
723
|
|
|
724
|
+
## Documentation
|
|
725
|
+
|
|
726
|
+
[https://tab-sim.readthedocs.io/](https://tab-sim.readthedocs.io/)
|
|
727
|
+
|
|
720
728
|
## Installation
|
|
721
729
|
|
|
722
730
|
The following instructions are expected to work on Linux machine. If you are running Windows it is recommended to use WSL. If you are running Mac then it is best to start with a [conda](https://docs.conda.io/en/latest/)/[mamba](https://mamba.readthedocs.io/en/latest/index.html) environment and install `python-casacore` before continuing with the `pip` install method. If all else fails there is the [Docker install](#docker).
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|