tabsim 0.0.4__tar.gz → 0.0.5__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.4 → tabsim-0.0.5}/PKG-INFO +4 -4
- {tabsim-0.0.4 → tabsim-0.0.5}/pyproject.toml +5 -4
- {tabsim-0.0.4 → tabsim-0.0.5}/tabsim/config.py +37 -8
- {tabsim-0.0.4 → tabsim-0.0.5}/tabsim/dask/observation.py +19 -6
- {tabsim-0.0.4 → tabsim-0.0.5}/tabsim/data/config/sim_config_base.yaml +2 -0
- {tabsim-0.0.4 → tabsim-0.0.5}/tabsim/jax/coordinates.py +55 -0
- tabsim-0.0.5/tabsim/read_ms.py +137 -0
- tabsim-0.0.5/tabsim/scripts/write_config_from_ms.py +40 -0
- {tabsim-0.0.4 → tabsim-0.0.5}/tabsim/write.py +57 -23
- {tabsim-0.0.4 → tabsim-0.0.5}/tabsim.egg-info/PKG-INFO +4 -4
- {tabsim-0.0.4 → tabsim-0.0.5}/tabsim.egg-info/SOURCES.txt +2 -0
- {tabsim-0.0.4 → tabsim-0.0.5}/tabsim.egg-info/entry_points.txt +1 -0
- {tabsim-0.0.4 → tabsim-0.0.5}/tabsim.egg-info/requires.txt +2 -2
- {tabsim-0.0.4 → tabsim-0.0.5}/LICENSE +0 -0
- {tabsim-0.0.4 → tabsim-0.0.5}/README.md +0 -0
- {tabsim-0.0.4 → tabsim-0.0.5}/docs/conf.py +0 -0
- {tabsim-0.0.4 → tabsim-0.0.5}/setup.cfg +0 -0
- {tabsim-0.0.4 → tabsim-0.0.5}/tabsim/__init__.py +0 -0
- {tabsim-0.0.4 → tabsim-0.0.5}/tabsim/dask/__init__.py +0 -0
- {tabsim-0.0.4 → tabsim-0.0.5}/tabsim/dask/coordinates.py +0 -0
- {tabsim-0.0.4 → tabsim-0.0.5}/tabsim/dask/extras.py +0 -0
- {tabsim-0.0.4 → tabsim-0.0.5}/tabsim/dask/interferometry.py +0 -0
- {tabsim-0.0.4 → tabsim-0.0.5}/tabsim/data/__init__.py +0 -0
- {tabsim-0.0.4 → tabsim-0.0.5}/tabsim/data/config/tab_config_base.yaml +0 -0
- {tabsim-0.0.4 → tabsim-0.0.5}/tabsim/data/rfi/norad_satellite.rfimodel +0 -0
- {tabsim-0.0.4 → tabsim-0.0.5}/tabsim/data/rfi/satellite.ole +0 -0
- {tabsim-0.0.4 → tabsim-0.0.5}/tabsim/data/rfi/satellite.rfimodel +0 -0
- {tabsim-0.0.4 → tabsim-0.0.5}/tabsim/data/rfi/stationary.loc +0 -0
- {tabsim-0.0.4 → tabsim-0.0.5}/tabsim/data/rfi/stationary.rfimodel +0 -0
- {tabsim-0.0.4 → tabsim-0.0.5}/tabsim/data/rfi/tles/2023-02-21-J8HE0T.json +0 -0
- {tabsim-0.0.4 → tabsim-0.0.5}/tabsim/data/rfi/tles/2023-02-21-navstar.json +0 -0
- {tabsim-0.0.4 → tabsim-0.0.5}/tabsim/data/sky/calibrator.skymodel +0 -0
- {tabsim-0.0.4 → tabsim-0.0.5}/tabsim/data/telescopes/EDA2.itrf.txt +0 -0
- {tabsim-0.0.4 → tabsim-0.0.5}/tabsim/data/telescopes/MWA.itrf.txt +0 -0
- {tabsim-0.0.4 → tabsim-0.0.5}/tabsim/data/telescopes/MeerKAT.itrf.txt +0 -0
- {tabsim-0.0.4 → tabsim-0.0.5}/tabsim/data/telescopes/_telescopes.yaml +0 -0
- {tabsim-0.0.4 → tabsim-0.0.5}/tabsim/jax/__init__.py +0 -0
- {tabsim-0.0.4 → tabsim-0.0.5}/tabsim/jax/interferometry.py +0 -0
- {tabsim-0.0.4 → tabsim-0.0.5}/tabsim/jax/utils.py +0 -0
- {tabsim-0.0.4 → tabsim-0.0.5}/tabsim/plot.py +0 -0
- {tabsim-0.0.4 → tabsim-0.0.5}/tabsim/scripts/__init__.py +0 -0
- {tabsim-0.0.4 → tabsim-0.0.5}/tabsim/scripts/ast_region.py +0 -0
- {tabsim-0.0.4 → tabsim-0.0.5}/tabsim/scripts/flag_data.py +0 -0
- {tabsim-0.0.4 → tabsim-0.0.5}/tabsim/scripts/sim_vis.py +0 -0
- {tabsim-0.0.4 → tabsim-0.0.5}/tabsim/scripts/tle_sat_region.py +0 -0
- {tabsim-0.0.4 → tabsim-0.0.5}/tabsim/sky.py +0 -0
- {tabsim-0.0.4 → tabsim-0.0.5}/tabsim/tle.py +0 -0
- {tabsim-0.0.4 → tabsim-0.0.5}/tabsim/tools.py +0 -0
- {tabsim-0.0.4 → tabsim-0.0.5}/tabsim.egg-info/dependency_links.txt +0 -0
- {tabsim-0.0.4 → tabsim-0.0.5}/tabsim.egg-info/top_level.txt +0 -0
- {tabsim-0.0.4 → tabsim-0.0.5}/tests/test_basic.py +0 -0
- {tabsim-0.0.4 → tabsim-0.0.5}/tests/test_sim-vis.py +0 -0
- {tabsim-0.0.4 → tabsim-0.0.5}/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.5
|
|
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,11 +680,11 @@ 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:
|
|
683
|
+
Requires-Python: >=3.9
|
|
684
684
|
Description-Content-Type: text/markdown
|
|
685
685
|
License-File: LICENSE
|
|
686
686
|
Requires-Dist: jax
|
|
687
|
-
Requires-Dist: dask
|
|
687
|
+
Requires-Dist: dask
|
|
688
688
|
Requires-Dist: xarray
|
|
689
689
|
Requires-Dist: zarr<3.0.0
|
|
690
690
|
Requires-Dist: dask-ms>=0.2.21
|
|
@@ -694,7 +694,7 @@ Requires-Dist: matplotlib
|
|
|
694
694
|
Requires-Dist: astropy
|
|
695
695
|
Requires-Dist: spacetrack
|
|
696
696
|
Requires-Dist: skyfield
|
|
697
|
-
Requires-Dist: numpy
|
|
697
|
+
Requires-Dist: numpy
|
|
698
698
|
Provides-Extra: gpu
|
|
699
699
|
Requires-Dist: jax[cuda12]; extra == "gpu"
|
|
700
700
|
Provides-Extra: sat
|
|
@@ -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.5"
|
|
9
9
|
license = {file = "LICENSE"}
|
|
10
10
|
readme = "README.md"
|
|
11
11
|
# dynamic = ["version", "readme"]
|
|
@@ -17,11 +17,11 @@ maintainers = [
|
|
|
17
17
|
]
|
|
18
18
|
description = "Trajectory-based Radio Frequency Interference (RFI) simulations for radio interferometeric data."
|
|
19
19
|
|
|
20
|
-
requires-python = ">=3.9
|
|
20
|
+
requires-python = ">=3.9"
|
|
21
21
|
dependencies = [
|
|
22
|
-
"jax", "dask
|
|
22
|
+
"jax", "dask", "xarray", "zarr<3.0.0",
|
|
23
23
|
"dask-ms>=0.2.21", "scipy", "tqdm", "matplotlib",
|
|
24
|
-
"astropy", "spacetrack", "skyfield", "numpy
|
|
24
|
+
"astropy", "spacetrack", "skyfield", "numpy",
|
|
25
25
|
]
|
|
26
26
|
|
|
27
27
|
[project.optional-dependencies]
|
|
@@ -51,3 +51,4 @@ tle-region = "tabsim.scripts.tle_sat_region:main"
|
|
|
51
51
|
ast-region = "tabsim.scripts.ast_region:main"
|
|
52
52
|
flag-data = "tabsim.scripts.flag_data:main"
|
|
53
53
|
sim-vis = "tabsim.scripts.sim_vis:main"
|
|
54
|
+
write-config = "tabsim.scripts.write_config_from_ms:main"
|
|
@@ -18,7 +18,6 @@ from astropy.time import Time
|
|
|
18
18
|
|
|
19
19
|
from tabsim.dask.observation import Observation
|
|
20
20
|
from tabsim.sky import generate_random_sky
|
|
21
|
-
from tabsim.plot import plot_uv, plot_src_alt, plot_angular_seps
|
|
22
21
|
from tabsim.write import write_ms, mk_obs_name, mk_obs_dir
|
|
23
22
|
from tabsim.jax.coordinates import calculate_fringe_frequency, jd_to_mjd
|
|
24
23
|
from tabsim.tle import get_visible_satellite_tles, id_generator
|
|
@@ -256,9 +255,7 @@ def load_obs(obs_spec: dict) -> Observation:
|
|
|
256
255
|
start_time_mjd = jd_to_mjd(obs_["start_time_jd"])
|
|
257
256
|
elif obs_["start_time_isot"]:
|
|
258
257
|
|
|
259
|
-
start_time_mjd = jd_to_mjd(
|
|
260
|
-
Time(obs_["start_time_isot"], format="isot", scale="ut1").jd
|
|
261
|
-
)
|
|
258
|
+
start_time_mjd = jd_to_mjd(Time(obs_["start_time_isot"], format="isot").jd)
|
|
262
259
|
elif obs_["start_time_lha"] is not None:
|
|
263
260
|
gsa = obs_["start_time_lha"] - tel_["longitude"] + obs_["ra"]
|
|
264
261
|
start_time_mjd = MJD0 + (gsa / 360)
|
|
@@ -668,6 +665,8 @@ def add_gains(obs: Observation, obs_spec: dict) -> None:
|
|
|
668
665
|
|
|
669
666
|
def plot_diagnostics(obs: Observation, obs_spec: dict, save_path: str) -> None:
|
|
670
667
|
|
|
668
|
+
from tabsim.plot import plot_uv, plot_src_alt, plot_angular_seps
|
|
669
|
+
|
|
671
670
|
diag_ = obs_spec["diagnostics"]
|
|
672
671
|
|
|
673
672
|
if diag_["src_alt"]:
|
|
@@ -808,11 +807,14 @@ def print_fringe_freq_sat(obs: Observation):
|
|
|
808
807
|
for i in range(obs.n_rfi_satellite)
|
|
809
808
|
]
|
|
810
809
|
fringe_freq = [calculate_fringe_frequency(**f_params) for f_params in fringe_params]
|
|
810
|
+
noise_std = obs.noise_std.mean().compute()
|
|
811
|
+
if noise_std == 0:
|
|
812
|
+
noise_std = 1.0
|
|
811
813
|
f_sample = (
|
|
812
814
|
np.pi
|
|
813
815
|
* np.max(np.abs(fringe_freq))
|
|
814
816
|
* np.max(obs.rfi_satellite_A_app)
|
|
815
|
-
/ np.sqrt(6 *
|
|
817
|
+
/ np.sqrt(6 * noise_std)
|
|
816
818
|
)
|
|
817
819
|
n_int = int(np.ceil(obs.int_time * f_sample))
|
|
818
820
|
|
|
@@ -843,11 +845,14 @@ def print_fringe_freq_tle_sat(obs: Observation):
|
|
|
843
845
|
for i in range(obs.n_rfi_tle_satellite)
|
|
844
846
|
]
|
|
845
847
|
fringe_freq = [calculate_fringe_frequency(**f_params) for f_params in fringe_params]
|
|
848
|
+
noise_std = obs.noise_std.mean().compute()
|
|
849
|
+
if noise_std == 0:
|
|
850
|
+
noise_std = 1.0
|
|
846
851
|
f_sample = (
|
|
847
852
|
np.pi
|
|
848
853
|
* np.max(np.abs(fringe_freq))
|
|
849
854
|
* np.max(obs.rfi_tle_satellite_A_app)
|
|
850
|
-
/ np.sqrt(6 *
|
|
855
|
+
/ np.sqrt(6 * noise_std)
|
|
851
856
|
)
|
|
852
857
|
n_int = int(np.ceil(obs.int_time * f_sample))
|
|
853
858
|
|
|
@@ -933,9 +938,26 @@ def run_sim_config(
|
|
|
933
938
|
print()
|
|
934
939
|
print(f"Writing data to : {save_path}")
|
|
935
940
|
|
|
936
|
-
|
|
941
|
+
plot_cond = np.array(
|
|
942
|
+
[
|
|
943
|
+
obs_spec["diagnostics"]["rfi_seps"],
|
|
944
|
+
obs_spec["diagnostics"]["src_alt"],
|
|
945
|
+
obs_spec["diagnostics"]["uv_cov"],
|
|
946
|
+
]
|
|
947
|
+
)
|
|
948
|
+
if np.any(plot_cond):
|
|
949
|
+
plot_diagnostics(obs, obs_spec, save_path)
|
|
950
|
+
else:
|
|
951
|
+
print("\nNo diagnostic plots.")
|
|
952
|
+
|
|
937
953
|
save_data(obs, obs_spec, zarr_path, ms_path)
|
|
938
954
|
|
|
955
|
+
if obs_spec["output"]["accumulate_ms"] is not None:
|
|
956
|
+
from tabsim.write import add_to_ms
|
|
957
|
+
|
|
958
|
+
xds = xr.open_zarr(zarr_path)
|
|
959
|
+
add_to_ms(xds, obs_spec["output"]["accumulate_ms"])
|
|
960
|
+
|
|
939
961
|
end = datetime.now()
|
|
940
962
|
print()
|
|
941
963
|
print(f"Total simulation time : {end - start}")
|
|
@@ -947,4 +969,11 @@ def run_sim_config(
|
|
|
947
969
|
os.remove(log_path)
|
|
948
970
|
sys.stdout = backup
|
|
949
971
|
|
|
950
|
-
|
|
972
|
+
if (
|
|
973
|
+
not obs_spec["output"]["keep_sim"]
|
|
974
|
+
and obs_spec["output"]["accumulate_ms"] is not None
|
|
975
|
+
):
|
|
976
|
+
shutil.rmtree(save_path)
|
|
977
|
+
return obs, save_path
|
|
978
|
+
else:
|
|
979
|
+
return obs, save_path
|
|
@@ -290,11 +290,17 @@ class Observation(Telescope):
|
|
|
290
290
|
self.n_int_samples // 2, self.n_time_fine, self.n_int_samples
|
|
291
291
|
).rechunk(self.time_chunk)
|
|
292
292
|
|
|
293
|
+
# self.gsa = da.asarray(
|
|
294
|
+
# Time(self.times_mjd_fine.compute(), format="mjd")
|
|
295
|
+
# .sidereal_time("mean", "greenwich")
|
|
296
|
+
# .hour
|
|
297
|
+
# * 15,
|
|
298
|
+
# chunks=(self.time_fine_chunk,),
|
|
299
|
+
# )
|
|
300
|
+
from tabsim.jax.coordinates import mjd_to_gast
|
|
301
|
+
|
|
293
302
|
self.gsa = da.asarray(
|
|
294
|
-
|
|
295
|
-
.sidereal_time("mean", "greenwich")
|
|
296
|
-
.hour
|
|
297
|
-
* 15,
|
|
303
|
+
mjd_to_gast(self.times_mjd_fine.compute()),
|
|
298
304
|
chunks=(self.time_fine_chunk,),
|
|
299
305
|
)
|
|
300
306
|
self.gha = (self.gsa - self.ra) % 360
|
|
@@ -313,7 +319,9 @@ class Observation(Telescope):
|
|
|
313
319
|
self.dish_d = da.asarray(dish_d)
|
|
314
320
|
self.fov = beam_size(dish_d, freqs.max(), fwhp=False)
|
|
315
321
|
|
|
316
|
-
self.ants_uvw =
|
|
322
|
+
self.ants_uvw = da.asarray(
|
|
323
|
+
np.array(ITRF_to_UVW(self.ITRF, self.gha, self.dec).compute())
|
|
324
|
+
)
|
|
317
325
|
|
|
318
326
|
if no_w:
|
|
319
327
|
self.ants_uvw[:, :, -1] = 0.0
|
|
@@ -725,6 +733,11 @@ Number of stationary RFI : {n_stat}"""
|
|
|
725
733
|
get_satellite_positions(tles, mjd_to_jd(self.times_mjd_fine.compute())),
|
|
726
734
|
chunks=(-1, self.time_fine_chunk, 3),
|
|
727
735
|
)
|
|
736
|
+
# from tabsim.jax.coordinates import kepler_orbit_many
|
|
737
|
+
# from tabsim.tle import
|
|
738
|
+
# rfi_xyz = da.asarray(
|
|
739
|
+
# kepler_orbit_many(mjd_to_jd(self.times_mjd_fine.compute()), )
|
|
740
|
+
# )
|
|
728
741
|
tles = da.asarray(da.atleast_2d(tles), chunks=(-1,))
|
|
729
742
|
# rfi_xyz is shape (n_src,n_time_fine,3)
|
|
730
743
|
# self.ants_xyz is shape (n_time_fine,n_ant,3)
|
|
@@ -938,7 +951,7 @@ Number of stationary RFI : {n_stat}"""
|
|
|
938
951
|
else:
|
|
939
952
|
self.flags = (
|
|
940
953
|
da.abs(self.vis_cal - self.vis_ast)
|
|
941
|
-
> 3.0 * da.std(self.
|
|
954
|
+
> 3.0 * da.std(self.vis_ast, axis=0)[None, ...]
|
|
942
955
|
)
|
|
943
956
|
else:
|
|
944
957
|
self.flags = da.zeros(shape=self.vis_cal.shape, dtype=bool)
|
|
@@ -122,6 +122,8 @@ observation:
|
|
|
122
122
|
output:
|
|
123
123
|
zarr: True # Optional - Whether to save the output to a zarr file. This contains almost all inputs ot the simulation
|
|
124
124
|
ms: True # Optional - Whether to save the output to a Measurement Set file
|
|
125
|
+
accumulate_ms: null # Optional - Whether to add the RFI visibilities to a provided MS file. This should be the MS file path to add to.
|
|
126
|
+
keep_sim: True # Optional - Whether to keep the simulation files generated by tabsim after adding to RFI visibilities to accumulate_ms. This includes all data including generated MS files.
|
|
125
127
|
flag_data: True # Optional - Whether to apply ideal 3sigma flags
|
|
126
128
|
overwrite: False # Optional - Whether to overwrite a previous simulation with the same name
|
|
127
129
|
path: ./ # Optional - Path to where the simulation should be saved
|
|
@@ -386,6 +386,61 @@ def jd_from_gmst(gmst: float) -> float:
|
|
|
386
386
|
return jd
|
|
387
387
|
|
|
388
388
|
|
|
389
|
+
def mjd_to_gmst(mjd: float) -> float:
|
|
390
|
+
"""Taken from OSKAR simulator"""
|
|
391
|
+
|
|
392
|
+
sec2rad = 7.2722052166430399038487e-5
|
|
393
|
+
two_pi = 6.28318530717958647693
|
|
394
|
+
|
|
395
|
+
# Days since J2000
|
|
396
|
+
jd2000 = mjd - 51544.5
|
|
397
|
+
|
|
398
|
+
# Centuries from J2000.0
|
|
399
|
+
t = jd2000 / 36525.0
|
|
400
|
+
|
|
401
|
+
# GMST at this time
|
|
402
|
+
gmst = (mjd % 1.0) * two_pi + (
|
|
403
|
+
24110.54841 + (8640184.812866 + (0.093104 - 6.2e-6 * t) * t) * t
|
|
404
|
+
) * sec2rad
|
|
405
|
+
|
|
406
|
+
# Restrict to 2 pi
|
|
407
|
+
x = (gmst % two_pi) / two_pi * 360
|
|
408
|
+
|
|
409
|
+
return x
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
def equation_of_equinoxes(mjd: float) -> float:
|
|
413
|
+
"""Taken from OSKAR simulator"""
|
|
414
|
+
|
|
415
|
+
deg2rad = 0.0174532925199432957692
|
|
416
|
+
hour2rad = 0.261799387799149436539
|
|
417
|
+
# Days from J2000.0
|
|
418
|
+
jd2000 = mjd - 51544.5
|
|
419
|
+
|
|
420
|
+
# Longitude of ascending node of the Moon
|
|
421
|
+
omega = (125.04 - 0.052954 * jd2000) * deg2rad
|
|
422
|
+
|
|
423
|
+
# Mean Longitude of the Sun
|
|
424
|
+
L = (280.47 + 0.98565 * jd2000) * deg2rad
|
|
425
|
+
|
|
426
|
+
# eqeq = delta_psi * cos(epsilon)
|
|
427
|
+
delta_psi = -0.000319 * jnp.sin(omega) - 0.000024 * jnp.sin(2.0 * L)
|
|
428
|
+
epsilon = (23.4393 - 0.0000004 * jd2000) * deg2rad
|
|
429
|
+
|
|
430
|
+
# Return equation of equinoxes in radians
|
|
431
|
+
eqeq = delta_psi * jnp.cos(epsilon) * hour2rad
|
|
432
|
+
|
|
433
|
+
return eqeq
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
def mjd_to_gast(mjd: float) -> float:
|
|
437
|
+
"""Taken from OSKAR simulator"""
|
|
438
|
+
|
|
439
|
+
gast = mjd_to_gmst(mjd) + equation_of_equinoxes(mjd)
|
|
440
|
+
|
|
441
|
+
return gast
|
|
442
|
+
|
|
443
|
+
|
|
389
444
|
def jd_to_mjd(jd):
|
|
390
445
|
|
|
391
446
|
mjd = jd - 2400000.5
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
from daskms import xds_from_table, xds_from_ms
|
|
2
|
+
import numpy as np
|
|
3
|
+
from astropy.time import Time
|
|
4
|
+
from astropy.coordinates import EarthLocation
|
|
5
|
+
|
|
6
|
+
from typing import Union
|
|
7
|
+
import os
|
|
8
|
+
import yaml
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def write_antenna_itrf_from_ms(
|
|
12
|
+
ms_path: str, tel_name: str = "telescope", save_dir: str = "./"
|
|
13
|
+
) -> tuple[str, int, float]:
|
|
14
|
+
|
|
15
|
+
xds_ant = xds_from_table(ms_path + "::ANTENNA")[0]
|
|
16
|
+
|
|
17
|
+
itrf = xds_ant.POSITION.data.compute()
|
|
18
|
+
n_ant = len(itrf)
|
|
19
|
+
|
|
20
|
+
dish_d = float(xds_ant.DISH_DIAMETER.data[0].compute())
|
|
21
|
+
|
|
22
|
+
itrf_path = os.path.abspath(os.path.join(save_dir, f"{tel_name}.itrf.txt"))
|
|
23
|
+
|
|
24
|
+
np.savetxt(itrf_path, itrf, fmt="%.6f")
|
|
25
|
+
|
|
26
|
+
return itrf_path, n_ant, dish_d
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def read_tel_location_from_ms(ms_path: str) -> tuple[float, float, float]:
|
|
30
|
+
|
|
31
|
+
xds_ant = xds_from_table(ms_path + "::ANTENNA")[0]
|
|
32
|
+
|
|
33
|
+
tel_itrf = xds_ant.POSITION.data.mean(axis=0).compute()
|
|
34
|
+
|
|
35
|
+
tel_loc = EarthLocation(x=tel_itrf[0], y=tel_itrf[1], z=tel_itrf[2], unit="m")
|
|
36
|
+
|
|
37
|
+
lat, lon, elevation = (
|
|
38
|
+
float(tel_loc.lat.deg),
|
|
39
|
+
float(tel_loc.lon.deg),
|
|
40
|
+
float(tel_loc.height.value),
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
return lat, lon, elevation
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def read_time_data_from_ms(ms_path: str) -> tuple[str, float, int]:
|
|
47
|
+
|
|
48
|
+
xds_ms = xds_from_ms(ms_path)[0]
|
|
49
|
+
|
|
50
|
+
times = np.unique(xds_ms.TIME.data.compute())
|
|
51
|
+
|
|
52
|
+
n_time = len(times)
|
|
53
|
+
int_time = float(np.diff(np.sort(times)[:2])[0])
|
|
54
|
+
|
|
55
|
+
start_time_isot = Time(np.min(times) / (24 * 3600), format="mjd").isot
|
|
56
|
+
|
|
57
|
+
return start_time_isot, int_time, n_time
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def read_freq_data_from_ms(
|
|
61
|
+
ms_path: str,
|
|
62
|
+
) -> tuple[Union[float, None], Union[float, None], Union[int, None]]:
|
|
63
|
+
|
|
64
|
+
try:
|
|
65
|
+
xds_spec = xds_from_table(ms_path + "::SPECTRAL_WINDOW")[0]
|
|
66
|
+
|
|
67
|
+
freqs = xds_spec.CHAN_FREQ.data[0].compute()
|
|
68
|
+
start_freq = float(freqs[0])
|
|
69
|
+
n_freq = len(freqs)
|
|
70
|
+
|
|
71
|
+
chan_width = float(xds_spec.CHAN_WIDTH.data[0, 0].compute())
|
|
72
|
+
except:
|
|
73
|
+
print("Frequency information could not be red from MS file.")
|
|
74
|
+
start_freq, chan_width, n_freq = None, None, None
|
|
75
|
+
|
|
76
|
+
return start_freq, chan_width, n_freq
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def read_phase_centre_from_ms(
|
|
80
|
+
ms_path: str,
|
|
81
|
+
) -> tuple[Union[float, None], Union[float, None]]:
|
|
82
|
+
|
|
83
|
+
try:
|
|
84
|
+
xds_src = xds_from_table(ms_path + "::SOURCE")[0]
|
|
85
|
+
|
|
86
|
+
ra, dec = [float(x) for x in np.rad2deg(xds_src.DIRECTION.data[0]).compute()]
|
|
87
|
+
except:
|
|
88
|
+
print("Phase centre could not be read from MS file.")
|
|
89
|
+
ra, dec = None, None
|
|
90
|
+
|
|
91
|
+
return ra, dec
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def construct_config_from_ms(
|
|
95
|
+
ms_path: str, tel_name: str = "telescope", save_dir: str = "./"
|
|
96
|
+
) -> tuple[str, dict]:
|
|
97
|
+
|
|
98
|
+
itrf_path, n_ant, dish_d = write_antenna_itrf_from_ms(ms_path, tel_name, save_dir)
|
|
99
|
+
lat, lon, elevation = read_tel_location_from_ms(ms_path)
|
|
100
|
+
ra, dec = read_phase_centre_from_ms(ms_path)
|
|
101
|
+
start_time_isot, int_time, n_time = read_time_data_from_ms(ms_path)
|
|
102
|
+
start_freq, chan_width, n_freq = read_freq_data_from_ms(ms_path)
|
|
103
|
+
|
|
104
|
+
config = {
|
|
105
|
+
"telescope": {
|
|
106
|
+
"name": tel_name,
|
|
107
|
+
"latitude": lat,
|
|
108
|
+
"longitude": lon,
|
|
109
|
+
"elevation": elevation,
|
|
110
|
+
"itrf_path": itrf_path,
|
|
111
|
+
"n_ant": n_ant,
|
|
112
|
+
# "dish_d": dish_d,
|
|
113
|
+
},
|
|
114
|
+
"observation": {
|
|
115
|
+
"start_time_isot": start_time_isot,
|
|
116
|
+
"int_time": int_time,
|
|
117
|
+
"n_time": n_time,
|
|
118
|
+
"start_freq": start_freq,
|
|
119
|
+
"chan_width": chan_width,
|
|
120
|
+
"n_freq": n_freq,
|
|
121
|
+
},
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if ra is not None:
|
|
125
|
+
config["observation"].update(
|
|
126
|
+
{
|
|
127
|
+
"target_name": f"pointing_{ra:.3f}_{dec:.3f}",
|
|
128
|
+
"ra": ra,
|
|
129
|
+
"dec": dec,
|
|
130
|
+
}
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
config_path = os.path.abspath(os.path.join(save_dir, f"{tel_name}_sim_config.yaml"))
|
|
134
|
+
|
|
135
|
+
yaml.dump(config, open(config_path, "w"))
|
|
136
|
+
|
|
137
|
+
return config_path, config
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from tabsim.read_ms import construct_config_from_ms
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def main():
|
|
5
|
+
|
|
6
|
+
import argparse
|
|
7
|
+
|
|
8
|
+
parser = argparse.ArgumentParser(
|
|
9
|
+
description="Extract simulation configuration parameters from an MS file and write to disk."
|
|
10
|
+
)
|
|
11
|
+
parser.add_argument(
|
|
12
|
+
"-ms",
|
|
13
|
+
"--ms_path",
|
|
14
|
+
required=True,
|
|
15
|
+
help="File path to the MS file.",
|
|
16
|
+
)
|
|
17
|
+
parser.add_argument(
|
|
18
|
+
"-t",
|
|
19
|
+
"--tel_name",
|
|
20
|
+
default="telescope",
|
|
21
|
+
help="Telescope name.",
|
|
22
|
+
)
|
|
23
|
+
parser.add_argument(
|
|
24
|
+
"-s",
|
|
25
|
+
"--save_dir",
|
|
26
|
+
default="./",
|
|
27
|
+
help="Path to directory where config and antenna coordinates should be saved.",
|
|
28
|
+
)
|
|
29
|
+
args = parser.parse_args()
|
|
30
|
+
|
|
31
|
+
config_path, config = construct_config_from_ms(
|
|
32
|
+
args.ms_path, args.tel_name, args.save_dir
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
print(f"Simulation configuration file save to : {config_path}")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
if __name__ == "__main__":
|
|
39
|
+
|
|
40
|
+
main()
|
|
@@ -383,7 +383,37 @@ def get_stationary_rfi_data(obs: Observation):
|
|
|
383
383
|
return rfi_stat
|
|
384
384
|
|
|
385
385
|
|
|
386
|
-
def
|
|
386
|
+
def construct_rfi_mscol(vis_rfi: Array, data: Array):
|
|
387
|
+
|
|
388
|
+
n_corr = data.shape[-1]
|
|
389
|
+
|
|
390
|
+
if n_corr == 1:
|
|
391
|
+
vis_rfi_ms = xr.DataArray(
|
|
392
|
+
vis_rfi[:, :, None],
|
|
393
|
+
dims=("row", "chan", "corr"),
|
|
394
|
+
)
|
|
395
|
+
elif n_corr == 2:
|
|
396
|
+
vis_rfi_ms = xr.DataArray(
|
|
397
|
+
da.stack([vis_rfi, vis_rfi], axis=2),
|
|
398
|
+
dims=("row", "chan", "corr"),
|
|
399
|
+
)
|
|
400
|
+
elif n_corr == 4:
|
|
401
|
+
vis_rfi_ms = xr.DataArray(
|
|
402
|
+
da.stack(
|
|
403
|
+
[vis_rfi, da.zeros_like(vis_rfi), da.zeros_like(vis_rfi), vis_rfi],
|
|
404
|
+
axis=2,
|
|
405
|
+
),
|
|
406
|
+
dims=("row", "chan", "corr"),
|
|
407
|
+
)
|
|
408
|
+
else:
|
|
409
|
+
raise ValueError("Correlation axis in target MS file is non-standard")
|
|
410
|
+
|
|
411
|
+
return vis_rfi_ms
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
def add_to_ms(
|
|
415
|
+
ds: xr.Dataset, ms_path: str, flags: Optional[Array] = None, data_col: str = "DATA"
|
|
416
|
+
):
|
|
387
417
|
|
|
388
418
|
n_time = ds.attrs["n_time"]
|
|
389
419
|
n_freq = ds.attrs["n_freq"]
|
|
@@ -396,24 +426,27 @@ def add_to_ms(ds: Dataset, ms_path: str, flags: Optional[Array] = None):
|
|
|
396
426
|
dims = ["row", "chan", "corr"]
|
|
397
427
|
chunks = {k: v for k, v in xds_ms.chunks.items() if k in dims}
|
|
398
428
|
|
|
399
|
-
|
|
400
|
-
vis_rfi
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
),
|
|
404
|
-
|
|
429
|
+
# vis_rfi_ms = construct_rfi_mscol(
|
|
430
|
+
# ds.vis_rfi.data.reshape(n_row, n_freq), xds_ms[data_col]
|
|
431
|
+
# )
|
|
432
|
+
vis_rfi_ms = construct_rfi_mscol(
|
|
433
|
+
np.conjugate(ds.vis_rfi.data.reshape(n_row, n_freq)), xds_ms[data_col]
|
|
434
|
+
) # Needed for OSKAR simulations compatibility
|
|
435
|
+
|
|
436
|
+
xds_ms = xds_ms.assign({data_col: xds_ms[data_col] + vis_rfi_ms.chunk(chunks)})
|
|
437
|
+
cols = data_col
|
|
438
|
+
|
|
439
|
+
# if flags:
|
|
440
|
+
# flags = xr.DataArray(
|
|
441
|
+
# ds.flags.data.reshape(n_row, n_freq, n_corr), dims=("row", "chan", "corr")
|
|
442
|
+
# )
|
|
443
|
+
# xds_ms = xds_ms.assign(FLAG=flags)
|
|
444
|
+
# cols.append("FLAG")
|
|
445
|
+
|
|
446
|
+
print(
|
|
447
|
+
f"Writing tabsim generated RFI visibilities to {data_col} column in {ms_path}"
|
|
405
448
|
)
|
|
406
449
|
|
|
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
450
|
dask.compute(xds_to_table([xds_ms], ms_path, cols))
|
|
418
451
|
|
|
419
452
|
|
|
@@ -479,6 +512,7 @@ def construct_ms_data_table(
|
|
|
479
512
|
|
|
480
513
|
row_times = da.asarray(
|
|
481
514
|
(ds.coords["time_mjd"].data[:, None] * da.ones(shape=(1, n_bl))).flatten()
|
|
515
|
+
* (24 * 3600)
|
|
482
516
|
)
|
|
483
517
|
ant1 = (
|
|
484
518
|
(ds.antenna1.data[None, :] * da.ones(shape=(n_time, 1)))
|
|
@@ -515,10 +549,10 @@ def construct_ms_data_table(
|
|
|
515
549
|
"ARRAY_ID": (("row"), a_id),
|
|
516
550
|
"FLAG": (("row", "chan", "corr"), flags),
|
|
517
551
|
"INTERVAL": (("row"), interval),
|
|
518
|
-
"FLAG_CATEGORY": (
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
),
|
|
552
|
+
# "FLAG_CATEGORY": (
|
|
553
|
+
# ("row", "chan", "corr", "flagcat"),
|
|
554
|
+
# da.expand_dims(flags, axis=3),
|
|
555
|
+
# ),
|
|
522
556
|
}
|
|
523
557
|
|
|
524
558
|
if extras:
|
|
@@ -677,7 +711,7 @@ def construct_ms_spectral_window_table(ds: Dataset, ms_path: str):
|
|
|
677
711
|
"CHAN_FREQ": (("row", "chan"), chan_freq),
|
|
678
712
|
"CHAN_WIDTH": (("row", "chan"), chan_width),
|
|
679
713
|
"EFFECTIVE_BW": (("row", "chan"), chan_width),
|
|
680
|
-
"EFFECTIVE_BW": (("row", "chan"), n_freq * chan_width),
|
|
714
|
+
# "EFFECTIVE_BW": (("row", "chan"), n_freq * chan_width),
|
|
681
715
|
"FLAG_ROW": (("row"), zero),
|
|
682
716
|
"FREQ_GROUP": (("row"), zero),
|
|
683
717
|
# 'FREQ_GROUP_NAME': (('row'), zero),
|
|
@@ -685,7 +719,7 @@ def construct_ms_spectral_window_table(ds: Dataset, ms_path: str):
|
|
|
685
719
|
"MEAS_FREQ_REF": (("row"), 5 * one),
|
|
686
720
|
# 'NAME': (('row'), zero),
|
|
687
721
|
"NET_SIDEBAND": (("row"), one),
|
|
688
|
-
"NUM_CHAN": (("row"), one),
|
|
722
|
+
"NUM_CHAN": (("row"), n_freq * one),
|
|
689
723
|
"REF_FREQUENCY": (("row"), ref_freq),
|
|
690
724
|
"RESOLUTION": (("row", "chan"), chan_width),
|
|
691
725
|
"TOTAL_BANDWIDTH": (("row"), total_bw),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tabsim
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.5
|
|
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,11 +680,11 @@ 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:
|
|
683
|
+
Requires-Python: >=3.9
|
|
684
684
|
Description-Content-Type: text/markdown
|
|
685
685
|
License-File: LICENSE
|
|
686
686
|
Requires-Dist: jax
|
|
687
|
-
Requires-Dist: dask
|
|
687
|
+
Requires-Dist: dask
|
|
688
688
|
Requires-Dist: xarray
|
|
689
689
|
Requires-Dist: zarr<3.0.0
|
|
690
690
|
Requires-Dist: dask-ms>=0.2.21
|
|
@@ -694,7 +694,7 @@ Requires-Dist: matplotlib
|
|
|
694
694
|
Requires-Dist: astropy
|
|
695
695
|
Requires-Dist: spacetrack
|
|
696
696
|
Requires-Dist: skyfield
|
|
697
|
-
Requires-Dist: numpy
|
|
697
|
+
Requires-Dist: numpy
|
|
698
698
|
Provides-Extra: gpu
|
|
699
699
|
Requires-Dist: jax[cuda12]; extra == "gpu"
|
|
700
700
|
Provides-Extra: sat
|
|
@@ -5,6 +5,7 @@ docs/conf.py
|
|
|
5
5
|
tabsim/__init__.py
|
|
6
6
|
tabsim/config.py
|
|
7
7
|
tabsim/plot.py
|
|
8
|
+
tabsim/read_ms.py
|
|
8
9
|
tabsim/sky.py
|
|
9
10
|
tabsim/tle.py
|
|
10
11
|
tabsim/tools.py
|
|
@@ -44,6 +45,7 @@ tabsim/scripts/ast_region.py
|
|
|
44
45
|
tabsim/scripts/flag_data.py
|
|
45
46
|
tabsim/scripts/sim_vis.py
|
|
46
47
|
tabsim/scripts/tle_sat_region.py
|
|
48
|
+
tabsim/scripts/write_config_from_ms.py
|
|
47
49
|
tests/test_basic.py
|
|
48
50
|
tests/test_sim-vis.py
|
|
49
51
|
tests/test_version.py
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|