tabsim 0.0.1__py3-none-any.whl
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/__init__.py +0 -0
- tabsim/config.py +904 -0
- tabsim/dask/__init__.py +0 -0
- tabsim/dask/coordinates.py +469 -0
- tabsim/dask/extras.py +60 -0
- tabsim/dask/interferometry.py +572 -0
- tabsim/dask/observation.py +969 -0
- tabsim/data/EDA2.itrf.txt +256 -0
- tabsim/data/MWA.itrf.txt +128 -0
- tabsim/data/MeerKAT.itrf.txt +64 -0
- tabsim/data/__init__.py +0 -0
- tabsim/data/aux_data/calibrator.skymodel +2 -0
- tabsim/data/aux_data/norad_satellite.rfimodel +61711 -0
- tabsim/data/aux_data/satellite.ole +3 -0
- tabsim/data/aux_data/satellite.rfimodel +3 -0
- tabsim/data/aux_data/stationary.loc +4 -0
- tabsim/data/aux_data/stationary.rfimodel +4 -0
- tabsim/data/config_files/sim_config_base.yaml +172 -0
- tabsim/data/config_files/tab_config_base.yaml +86 -0
- tabsim/jax/__init__.py +0 -0
- tabsim/jax/coordinates.py +1082 -0
- tabsim/jax/interferometry.py +667 -0
- tabsim/jax/utils.py +90 -0
- tabsim/plot.py +323 -0
- tabsim/scripts/__init__.py +1 -0
- tabsim/scripts/ast_region.py +56 -0
- tabsim/scripts/flag_data.py +195 -0
- tabsim/scripts/sim_vis.py +136 -0
- tabsim/scripts/tle_sat_region.py +191 -0
- tabsim/sky.py +137 -0
- tabsim/tle.py +753 -0
- tabsim/tools.py +111 -0
- tabsim/write.py +715 -0
- tabsim-0.0.1.dist-info/METADATA +991 -0
- tabsim-0.0.1.dist-info/RECORD +39 -0
- tabsim-0.0.1.dist-info/WHEEL +5 -0
- tabsim-0.0.1.dist-info/entry_points.txt +5 -0
- tabsim-0.0.1.dist-info/licenses/LICENSE +674 -0
- tabsim-0.0.1.dist-info/top_level.txt +1 -0
tabsim/config.py
ADDED
|
@@ -0,0 +1,904 @@
|
|
|
1
|
+
import yaml
|
|
2
|
+
import re
|
|
3
|
+
import os
|
|
4
|
+
import sys
|
|
5
|
+
import shutil
|
|
6
|
+
|
|
7
|
+
import collections.abc
|
|
8
|
+
from typing import Tuple
|
|
9
|
+
from datetime import datetime
|
|
10
|
+
|
|
11
|
+
import dask.array as da
|
|
12
|
+
from dask.array.core import Array
|
|
13
|
+
import xarray as xr
|
|
14
|
+
import numpy as np
|
|
15
|
+
import pandas as pd
|
|
16
|
+
|
|
17
|
+
from tabsim.dask.observation import Observation
|
|
18
|
+
from tabsim.sky import generate_random_sky
|
|
19
|
+
from tabsim.plot import plot_uv, plot_src_alt, plot_angular_seps
|
|
20
|
+
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
|
+
|
|
24
|
+
from daskms import xds_from_ms
|
|
25
|
+
|
|
26
|
+
from tqdm import tqdm
|
|
27
|
+
from importlib.resources import files
|
|
28
|
+
|
|
29
|
+
config_dir = files("tabsim.data").joinpath("config_files").__str__()
|
|
30
|
+
|
|
31
|
+
sim_base_config_path = os.path.join(config_dir, "sim_config_base.yaml")
|
|
32
|
+
tab_base_config_path = os.path.join(config_dir, "tab_config_base.yaml")
|
|
33
|
+
# extract_base_config_path = os.path.join(config_dir, "extract_config_base.yaml")
|
|
34
|
+
# pow_spec_base_config_path = os.path.join(
|
|
35
|
+
# config_dir, "extract_pow_spec_config_base.yaml"
|
|
36
|
+
# )
|
|
37
|
+
|
|
38
|
+
JD0 = 2459997.079914223 # 2023-02-21 13:55:04.589 UTC => GMSA = 0
|
|
39
|
+
MJD0 = JD0 - 2400000.5
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def deep_update(d: dict, u: dict) -> dict:
|
|
43
|
+
"""Recursively update a dictionary which includes subdictionaries.
|
|
44
|
+
|
|
45
|
+
Parameters
|
|
46
|
+
----------
|
|
47
|
+
d : dict
|
|
48
|
+
Base dictionary to update.
|
|
49
|
+
u : dict
|
|
50
|
+
Update dictionary.
|
|
51
|
+
|
|
52
|
+
Returns
|
|
53
|
+
-------
|
|
54
|
+
dict
|
|
55
|
+
Updated dictionary.
|
|
56
|
+
"""
|
|
57
|
+
for k, v in u.items():
|
|
58
|
+
if isinstance(v, collections.abc.Mapping):
|
|
59
|
+
d[k] = deep_update(d.get(k, {}), v)
|
|
60
|
+
else:
|
|
61
|
+
d[k] = v
|
|
62
|
+
return d
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
loader = yaml.SafeLoader
|
|
66
|
+
loader.add_implicit_resolver(
|
|
67
|
+
"tag:yaml.org,2002:float",
|
|
68
|
+
re.compile(
|
|
69
|
+
"""^(?:
|
|
70
|
+
[-+]?(?:[0-9][0-9_]*)\\.[0-9_]*(?:[eE][-+]?[0-9]+)?
|
|
71
|
+
|[-+]?(?:[0-9][0-9_]*)(?:[eE][-+]?[0-9]+)
|
|
72
|
+
|\\.[0-9_]+(?:[eE][-+][0-9]+)?
|
|
73
|
+
|[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\\.[0-9_]*
|
|
74
|
+
|[-+]?\\.(?:inf|Inf|INF)
|
|
75
|
+
|\\.(?:nan|NaN|NAN))$""",
|
|
76
|
+
re.X,
|
|
77
|
+
),
|
|
78
|
+
list("-+0123456789."),
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def yaml_load(path):
|
|
83
|
+
config = yaml.load(open(path), Loader=loader)
|
|
84
|
+
return config
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class Tee(object):
|
|
88
|
+
"""https://stackoverflow.com/questions/17866724/python-logging-print-statements-while-having-them-print-to-stdout"""
|
|
89
|
+
|
|
90
|
+
def __init__(self, *files):
|
|
91
|
+
self.files = files
|
|
92
|
+
|
|
93
|
+
def write(self, obj):
|
|
94
|
+
for f in self.files:
|
|
95
|
+
f.write(obj)
|
|
96
|
+
|
|
97
|
+
def flush(self):
|
|
98
|
+
pass
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def load_config(path: str, config_type: str = "sim") -> dict:
|
|
102
|
+
"""Load a configuration file and populate default parameters where needed.
|
|
103
|
+
|
|
104
|
+
Parameters
|
|
105
|
+
----------
|
|
106
|
+
path : str
|
|
107
|
+
Path to the yaml config file.
|
|
108
|
+
config_type : str, optional
|
|
109
|
+
Type of configuration file, by default "sim". Options are {"sim", "tab", "extract", "pow_spec"}.
|
|
110
|
+
|
|
111
|
+
Returns
|
|
112
|
+
-------
|
|
113
|
+
dict
|
|
114
|
+
Configuration dictionary.
|
|
115
|
+
"""
|
|
116
|
+
|
|
117
|
+
config = yaml_load(path)
|
|
118
|
+
if config_type == "sim":
|
|
119
|
+
base_config = yaml_load(sim_base_config_path)
|
|
120
|
+
elif config_type == "tab":
|
|
121
|
+
base_config = yaml_load(tab_base_config_path)
|
|
122
|
+
elif config_type == "extract":
|
|
123
|
+
base_config = yaml_load(extract_base_config_path)
|
|
124
|
+
elif config_type == "pow_spec":
|
|
125
|
+
base_config = yaml_load(pow_spec_base_config_path)
|
|
126
|
+
else:
|
|
127
|
+
ValueError(
|
|
128
|
+
"A config type must be specified. Options are {sim, tab, extract, pow_spec}."
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
return deep_update(base_config, config)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def load_sky_model(file_path: str, freqs: Array, src_type: str) -> tuple:
|
|
135
|
+
"""Read in a skymodel file and return parameters to be used in tabascal simulator.
|
|
136
|
+
A skymodel file is a csv file with the following columns:
|
|
137
|
+
(src_id, I, Q, U, V, ra, dec, spec_idx, ref_freq, major, minor, post_ang)
|
|
138
|
+
Depending on the 'src_type' the last 3 columns may be required. src_id, Q, U, and V
|
|
139
|
+
are not used.
|
|
140
|
+
|
|
141
|
+
Parameters
|
|
142
|
+
----------
|
|
143
|
+
file_path : str
|
|
144
|
+
The file path to the skymodel file.
|
|
145
|
+
freqs : Array
|
|
146
|
+
Array of observation frequencies used to build a sky model
|
|
147
|
+
src_type : str
|
|
148
|
+
The source type to return. Options are {'point', 'gauss', 'exp'}.
|
|
149
|
+
|
|
150
|
+
Returns
|
|
151
|
+
-------
|
|
152
|
+
tuple
|
|
153
|
+
A tuple of dask arrays including at least (I, ra, dec) and some of
|
|
154
|
+
(major, minor, pos_ang).
|
|
155
|
+
|
|
156
|
+
Raises
|
|
157
|
+
------
|
|
158
|
+
KeyError
|
|
159
|
+
The error is raised when one of the accepted source types is not chosen.
|
|
160
|
+
"""
|
|
161
|
+
|
|
162
|
+
freqs = da.atleast_1d(freqs)
|
|
163
|
+
|
|
164
|
+
def calc_S_nu(I: Array, f0: Array, si: Array):
|
|
165
|
+
return (
|
|
166
|
+
I[:, None, None]
|
|
167
|
+
* da.ones((len(I), 1, len(freqs)))
|
|
168
|
+
* (freqs[None, None, :] / f0[:, None, None]) ** si[:, None, None]
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
src_list = [
|
|
172
|
+
da.atleast_1d(x) for x in np.loadtxt(file_path, skiprows=1, delimiter=",").T
|
|
173
|
+
]
|
|
174
|
+
|
|
175
|
+
if src_type == "point":
|
|
176
|
+
src_id, I, Q, U, V, si, f0, ra, dec = src_list[:9]
|
|
177
|
+
S = calc_S_nu(I, f0, si)
|
|
178
|
+
return S, ra, dec
|
|
179
|
+
elif src_type == "gauss":
|
|
180
|
+
src_id, I, Q, U, V, si, f0, ra, dec, major, minor, pos_ang = src_list
|
|
181
|
+
S = calc_S_nu(I, f0, si)
|
|
182
|
+
return S, ra, dec, major, minor, pos_ang
|
|
183
|
+
elif src_type == "exp":
|
|
184
|
+
src_id, I, Q, U, V, si, f0, ra, dec, major = src_list[:10]
|
|
185
|
+
S = calc_S_nu(I, f0, si)
|
|
186
|
+
return S, ra, dec, major
|
|
187
|
+
else:
|
|
188
|
+
raise KeyError("'src_type' must be one of {'point', 'gauss', 'exp'}.")
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def sigma_value(value: str | float | int, obs: Observation):
|
|
192
|
+
try:
|
|
193
|
+
if isinstance(value, str):
|
|
194
|
+
sigma = (np.mean(obs.noise_std) / np.sqrt(obs.n_time * obs.n_bl)).compute()
|
|
195
|
+
n_sig = float(value.replace("sigma", ""))
|
|
196
|
+
value = n_sig * sigma
|
|
197
|
+
return value
|
|
198
|
+
elif isinstance(value, float) or isinstance(value, int):
|
|
199
|
+
return float(value)
|
|
200
|
+
except:
|
|
201
|
+
raise ValueError()
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def load_obs(obs_spec: dict) -> Observation:
|
|
205
|
+
|
|
206
|
+
tel_ = obs_spec["telescope"]
|
|
207
|
+
obs_ = obs_spec["observation"]
|
|
208
|
+
dask_ = obs_spec["dask"]
|
|
209
|
+
|
|
210
|
+
def arange(start: float, delta: float, n: int):
|
|
211
|
+
x = da.arange(start, start + n * delta, delta)[:n]
|
|
212
|
+
return x
|
|
213
|
+
|
|
214
|
+
if obs_["start_time_jd"]:
|
|
215
|
+
from tabascal.jax.coordinates import jd_to_mjd
|
|
216
|
+
|
|
217
|
+
start_time_mjd = jd_to_mjd(obs_["start_time_jd"])
|
|
218
|
+
elif obs_["start_time_isot"]:
|
|
219
|
+
from astropy.time import Time
|
|
220
|
+
from tabascal.jax.coordinates import jd_to_mjd
|
|
221
|
+
|
|
222
|
+
start_time_mjd = jd_to_mjd(
|
|
223
|
+
Time(obs_["start_time_isot"], format="isot", scale="ut1").jd
|
|
224
|
+
)
|
|
225
|
+
elif obs_["start_time_lha"] is not None:
|
|
226
|
+
gsa = obs_["start_time_lha"] - tel_["longitude"] + obs_["ra"]
|
|
227
|
+
start_time_mjd = MJD0 + (gsa / 360)
|
|
228
|
+
elif obs_["start_time"] is not None:
|
|
229
|
+
start_time = obs_["start_time"]
|
|
230
|
+
start_time_mjd = MJD0 + start_time / (24 * 3600)
|
|
231
|
+
else:
|
|
232
|
+
ValueError(
|
|
233
|
+
"A start time must be given in either the 'start_time', 'start_time_jd', 'start_time_isot' or 'start_time_lha'"
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
time_range = arange(0, obs_["int_time"], obs_["n_time"])
|
|
237
|
+
times_mjd = start_time_mjd + time_range / (24 * 3600)
|
|
238
|
+
freqs = arange(obs_["start_freq"], obs_["chan_width"], obs_["n_freq"])
|
|
239
|
+
|
|
240
|
+
obs = Observation(
|
|
241
|
+
latitude=tel_["latitude"],
|
|
242
|
+
longitude=tel_["longitude"],
|
|
243
|
+
elevation=tel_["elevation"],
|
|
244
|
+
ra=obs_["ra"],
|
|
245
|
+
dec=obs_["dec"],
|
|
246
|
+
times_mjd=times_mjd,
|
|
247
|
+
freqs=freqs,
|
|
248
|
+
int_time=obs_["int_time"],
|
|
249
|
+
chan_width=obs_["chan_width"],
|
|
250
|
+
SEFD=obs_["SEFD"],
|
|
251
|
+
ENU_path=tel_["enu_path"],
|
|
252
|
+
ITRF_path=tel_["itrf_path"],
|
|
253
|
+
n_ant=tel_["n_ant"],
|
|
254
|
+
dish_d=tel_["dish_d"],
|
|
255
|
+
random_seed=obs_["random_seed"],
|
|
256
|
+
auto_corrs=obs_["auto_corrs"],
|
|
257
|
+
no_w=obs_["no_w"],
|
|
258
|
+
n_int_samples=obs_["n_int"],
|
|
259
|
+
tel_name=tel_["name"],
|
|
260
|
+
target_name=obs_["target_name"],
|
|
261
|
+
max_chunk_MB=dask_["max_chunk_MB"],
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
print()
|
|
265
|
+
print(f"Theoretical synthesized beam width : {3600*obs.syn_bw:.0f} arcsec")
|
|
266
|
+
|
|
267
|
+
return obs
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def add_power_spectrum_sources(obs: Observation, ps_rand: dict) -> None:
|
|
271
|
+
|
|
272
|
+
from tge import simulate_sky, Cl, Pk, beam_constants, lm_to_radec
|
|
273
|
+
|
|
274
|
+
const = beam_constants(
|
|
275
|
+
D=obs.dish_d.compute(), freq=obs.freqs[0].compute(), dBdT=None, f=1
|
|
276
|
+
)
|
|
277
|
+
beam = lambda x: 1
|
|
278
|
+
cl_ps_keys = ["A", "beta"]
|
|
279
|
+
cl_ps_args = {key: value for key, value in ps_rand.items() if key in cl_ps_keys}
|
|
280
|
+
pk_ps_keys = ["Po", "k0", "gamma"]
|
|
281
|
+
pk_ps_args = {key: value for key, value in ps_rand.items() if key in pk_ps_keys}
|
|
282
|
+
non_ps_keys = ["n_side", "fov_f", "type", "random_seed"]
|
|
283
|
+
# ps_args = {key: value for key, value in ps_rand.items() if key not in non_ps_keys}
|
|
284
|
+
|
|
285
|
+
if ps_rand["type"] == "Cl":
|
|
286
|
+
I, lxy = simulate_sky(
|
|
287
|
+
N_side=ps_rand["n_side"],
|
|
288
|
+
fov=ps_rand["fov_f"] * const["thetaFWHM"],
|
|
289
|
+
Cl=Cl,
|
|
290
|
+
PS_args=cl_ps_args,
|
|
291
|
+
beam=beam,
|
|
292
|
+
seed=ps_rand["random_seed"],
|
|
293
|
+
)
|
|
294
|
+
elif ps_rand["type"] == "Pk":
|
|
295
|
+
I, lxy = simulate_sky(
|
|
296
|
+
N_side=ps_rand["n_side"],
|
|
297
|
+
fov=ps_rand["fov_f"] * const["thetaFWHM"],
|
|
298
|
+
Pk=Pk,
|
|
299
|
+
PS_args=pk_ps_args,
|
|
300
|
+
beam=beam,
|
|
301
|
+
seed=ps_rand["random_seed"],
|
|
302
|
+
)
|
|
303
|
+
else:
|
|
304
|
+
ValueError("Keyword 'type' in section pow_spec.random must be one of {Cl, Pk}")
|
|
305
|
+
|
|
306
|
+
ra, dec = lm_to_radec(lxy, obs.ra, obs.dec).T
|
|
307
|
+
I = const["dBdT"] * da.asarray(I.reshape(-1, 1))
|
|
308
|
+
obs.addAstro(I[:, None, :], ra, dec)
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
def add_astro_sources(obs: Observation, obs_spec: dict) -> None:
|
|
312
|
+
"""Add astronomical sources from the simulation config file to the observation object.
|
|
313
|
+
|
|
314
|
+
Parameters
|
|
315
|
+
----------
|
|
316
|
+
obs : Observation
|
|
317
|
+
Observation object instance.
|
|
318
|
+
obs_spec : dict
|
|
319
|
+
Simulation config dictionary.
|
|
320
|
+
"""
|
|
321
|
+
|
|
322
|
+
methods = {
|
|
323
|
+
"point": obs.addAstro,
|
|
324
|
+
"gauss": obs.addAstroGauss,
|
|
325
|
+
"exp": obs.addAstroExp,
|
|
326
|
+
}
|
|
327
|
+
ast_ = obs_spec["ast_sources"]
|
|
328
|
+
|
|
329
|
+
if ast_["pow_spec"]["random"]["type"]:
|
|
330
|
+
add_power_spectrum_sources(obs, ast_["pow_spec"]["random"])
|
|
331
|
+
|
|
332
|
+
for key in ast_.keys():
|
|
333
|
+
|
|
334
|
+
path = ast_[key]["path"]
|
|
335
|
+
if path is not None:
|
|
336
|
+
params = load_sky_model(path, obs.freqs, key)
|
|
337
|
+
print()
|
|
338
|
+
print(f"Adding {len(params[0])} {key} sources from {path} ...")
|
|
339
|
+
methods[key](*params)
|
|
340
|
+
|
|
341
|
+
if "n_src" in ast_[key]["random"]:
|
|
342
|
+
if ast_[key]["random"]["n_src"] > 0:
|
|
343
|
+
rand_ = ast_[key]["random"]
|
|
344
|
+
n_beam = rand_["n_beam"]
|
|
345
|
+
max_beam = rand_["max_sep"] / 3600 / n_beam
|
|
346
|
+
beam_width = np.min([obs.syn_bw, max_beam])
|
|
347
|
+
fov = np.min([obs.fov, 180.0])
|
|
348
|
+
print()
|
|
349
|
+
print(
|
|
350
|
+
f"Generating {rand_['n_src']} sources within {fov:.2f} deg FoV ({fov/2:.2f} radius) ..."
|
|
351
|
+
)
|
|
352
|
+
print(f"Minimum {n_beam*beam_width*3600:.1f} arcsec separation ...")
|
|
353
|
+
|
|
354
|
+
I, d_ra, d_dec = generate_random_sky(
|
|
355
|
+
n_src=rand_["n_src"],
|
|
356
|
+
min_I=sigma_value(rand_["min_I"], obs),
|
|
357
|
+
max_I=sigma_value(rand_["max_I"], obs),
|
|
358
|
+
freqs=obs.freqs,
|
|
359
|
+
fov=fov,
|
|
360
|
+
beam_width=beam_width,
|
|
361
|
+
random_seed=rand_["random_seed"],
|
|
362
|
+
n_beam=n_beam,
|
|
363
|
+
)
|
|
364
|
+
ra = (obs.ra + d_ra) % 360
|
|
365
|
+
dec = obs.dec + d_dec
|
|
366
|
+
dec = np.where(dec > 90, 180 - dec, dec)
|
|
367
|
+
dec = np.where(dec < -90, -180 - dec, dec)
|
|
368
|
+
|
|
369
|
+
if key == "point":
|
|
370
|
+
print()
|
|
371
|
+
print(f"Adding {rand_['n_src']} random {key} sources ...")
|
|
372
|
+
methods[key](I[:, None, :], ra, dec)
|
|
373
|
+
elif key == "gauss":
|
|
374
|
+
rng = np.random.default_rng(rand_["random_seed"] + 1)
|
|
375
|
+
pos_ang = rng.uniform(low=0.0, high=360.0, size=(rand_["n_src"],))
|
|
376
|
+
major_ = np.abs(
|
|
377
|
+
rng.normal(
|
|
378
|
+
loc=rand_["major_mean"],
|
|
379
|
+
scale=rand_["major_std"],
|
|
380
|
+
size=(rand_["n_src"],),
|
|
381
|
+
)
|
|
382
|
+
)
|
|
383
|
+
minor_ = np.abs(
|
|
384
|
+
rng.normal(
|
|
385
|
+
loc=rand_["minor_mean"],
|
|
386
|
+
scale=rand_["minor_std"],
|
|
387
|
+
size=(rand_["n_src"],),
|
|
388
|
+
)
|
|
389
|
+
)
|
|
390
|
+
major = np.where(major_ > minor_, major_, minor_)
|
|
391
|
+
minor = np.where(major_ < minor_, major_, minor_)
|
|
392
|
+
print()
|
|
393
|
+
print(f"Adding {rand_['n_src']} random {key} sources ...")
|
|
394
|
+
methods[key](
|
|
395
|
+
I[:, None, :],
|
|
396
|
+
major,
|
|
397
|
+
minor,
|
|
398
|
+
pos_ang,
|
|
399
|
+
ra,
|
|
400
|
+
dec,
|
|
401
|
+
)
|
|
402
|
+
elif key == "exp":
|
|
403
|
+
rng = np.random.default_rng(rand_["random_seed"] + 1)
|
|
404
|
+
shape = np.abs(
|
|
405
|
+
rng.normal(
|
|
406
|
+
loc=rand_["size_mean"],
|
|
407
|
+
scale=rand_["size_std"],
|
|
408
|
+
size=(rand_["n_src"],),
|
|
409
|
+
)
|
|
410
|
+
)
|
|
411
|
+
print()
|
|
412
|
+
print(f"Adding {rand_['n_src']} random {key} sources ...")
|
|
413
|
+
methods[key](I[:, None, :], shape, ra, dec)
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
def gauss(A: float, mean: float, sigma: float, x: Array) -> Array:
|
|
417
|
+
return A * da.exp(-0.5 * ((x - mean) / sigma) ** 2)
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
def generate_spectra(spec_df: pd.DataFrame, freqs: Array, id_key: str) -> tuple:
|
|
421
|
+
|
|
422
|
+
spectra = []
|
|
423
|
+
ids = []
|
|
424
|
+
for i, spec in spec_df.iterrows():
|
|
425
|
+
if spec["sig_type"].lower() == "gauss":
|
|
426
|
+
ids.append(spec[id_key])
|
|
427
|
+
spectra.append(
|
|
428
|
+
gauss(spec["power"], spec["freq"], spec["band_width"] / 2, freqs)
|
|
429
|
+
)
|
|
430
|
+
else:
|
|
431
|
+
print(f"sig_type : {spec['sig_type']} not supported.")
|
|
432
|
+
|
|
433
|
+
return np.array(ids), da.atleast_2d(spectra)
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
def add_satellite_sources(obs: Observation, obs_spec: dict) -> None:
|
|
437
|
+
|
|
438
|
+
sat_ = obs_spec["rfi_sources"]["satellite"]
|
|
439
|
+
|
|
440
|
+
# Circular path based Satellites
|
|
441
|
+
if len(sat_["sat_ids"]) > 0:
|
|
442
|
+
if sat_["circ_path"] is not None:
|
|
443
|
+
oles = pd.read_csv(sat_["circ_path"])
|
|
444
|
+
oles = oles[oles["sat_id"].isin(sat_["sat_ids"])]
|
|
445
|
+
path_ids = oles["sat_id"].values
|
|
446
|
+
else:
|
|
447
|
+
raise ValueError(
|
|
448
|
+
"'circ_path' must be populated to include satellites with circular orbits."
|
|
449
|
+
)
|
|
450
|
+
|
|
451
|
+
sat_spec = pd.read_csv(sat_["spec_model"])
|
|
452
|
+
sat_spec = sat_spec[sat_spec["sat_id"].isin(path_ids)]
|
|
453
|
+
|
|
454
|
+
if len(sat_spec) > 0:
|
|
455
|
+
spec_ids, spectra = generate_spectra(sat_spec, obs.freqs, "sat_id")
|
|
456
|
+
uids = np.unique(spec_ids)
|
|
457
|
+
for uid in uids:
|
|
458
|
+
Pv = (
|
|
459
|
+
sat_["power_scale"]
|
|
460
|
+
* da.sum(spectra[spec_ids == uid], axis=0)[None, None, :]
|
|
461
|
+
* da.ones((1, 1, obs.n_freq))
|
|
462
|
+
)
|
|
463
|
+
ole = oles[oles["sat_id"] == uid]
|
|
464
|
+
if len(ole) == 1:
|
|
465
|
+
print()
|
|
466
|
+
print("Adding satellite RFI source ...")
|
|
467
|
+
obs.addSatelliteRFI(
|
|
468
|
+
Pv,
|
|
469
|
+
ole["elevation"].values,
|
|
470
|
+
ole["inclination"].values,
|
|
471
|
+
ole["lon_asc_node"].values,
|
|
472
|
+
ole["periapsis"].values,
|
|
473
|
+
)
|
|
474
|
+
else:
|
|
475
|
+
print()
|
|
476
|
+
print(f"sat_id: {uid} multiply-defined.")
|
|
477
|
+
else:
|
|
478
|
+
print("No 'sat_ids' matching in 'spec_model' file given.")
|
|
479
|
+
|
|
480
|
+
|
|
481
|
+
def add_tle_satellite_sources(
|
|
482
|
+
obs: Observation, obs_spec: dict, spacetrack_path: str
|
|
483
|
+
) -> None:
|
|
484
|
+
|
|
485
|
+
sat_ = obs_spec["rfi_sources"]["tle_satellite"]
|
|
486
|
+
|
|
487
|
+
# TLE path based Satellites
|
|
488
|
+
tle_cond = [
|
|
489
|
+
sat_["norad_ids_path"],
|
|
490
|
+
len(sat_["norad_ids"]) > 0,
|
|
491
|
+
len(sat_["sat_names"]) > 0,
|
|
492
|
+
]
|
|
493
|
+
if np.any(tle_cond):
|
|
494
|
+
|
|
495
|
+
if spacetrack_path:
|
|
496
|
+
st_ = yaml_load(spacetrack_path)
|
|
497
|
+
else:
|
|
498
|
+
raise ValueError(
|
|
499
|
+
"Space-Track login details must be given to simulate TLE based satellites"
|
|
500
|
+
)
|
|
501
|
+
|
|
502
|
+
if sat_["norad_ids_path"] is None:
|
|
503
|
+
norad_ids = sat_["norad_ids"]
|
|
504
|
+
else:
|
|
505
|
+
norad_ids = np.concatenate(
|
|
506
|
+
[sat_["norad_ids"], np.loadtxt(sat_["norad_ids_path"], usecols=0)]
|
|
507
|
+
)
|
|
508
|
+
|
|
509
|
+
from astropy.time import Time
|
|
510
|
+
|
|
511
|
+
jd_step = sat_["vis_step"] / (24 * 60)
|
|
512
|
+
times_check = Time(
|
|
513
|
+
np.arange(obs.times_mjd[0], obs.times_mjd[-1] + jd_step, jd_step),
|
|
514
|
+
format="mjd",
|
|
515
|
+
)
|
|
516
|
+
|
|
517
|
+
norad_ids, tles = get_visible_satellite_tles(
|
|
518
|
+
st_["username"],
|
|
519
|
+
st_["password"],
|
|
520
|
+
times_check,
|
|
521
|
+
obs.latitude,
|
|
522
|
+
obs.longitude,
|
|
523
|
+
obs.elevation,
|
|
524
|
+
obs.ra,
|
|
525
|
+
obs.dec,
|
|
526
|
+
sat_["max_ang_sep"],
|
|
527
|
+
sat_["min_alt"],
|
|
528
|
+
sat_["sat_names"],
|
|
529
|
+
norad_ids,
|
|
530
|
+
sat_["tle_dir"],
|
|
531
|
+
)
|
|
532
|
+
|
|
533
|
+
print(f"NORAD IDs includeed : {norad_ids}")
|
|
534
|
+
|
|
535
|
+
sat_spec = pd.read_csv(sat_["norad_spec_model"])
|
|
536
|
+
sat_spec = sat_spec[sat_spec["norad_id"].isin(norad_ids)]
|
|
537
|
+
|
|
538
|
+
print(f"Spectral models for {len(sat_spec)} TLE satellites found.")
|
|
539
|
+
|
|
540
|
+
if len(sat_spec) > 0:
|
|
541
|
+
print()
|
|
542
|
+
print("Adding TLE-based satellite RFI sources ...")
|
|
543
|
+
ids, spectra = generate_spectra(sat_spec, obs.freqs, "norad_id")
|
|
544
|
+
uids = np.unique(ids)
|
|
545
|
+
for uid in tqdm(uids[: sat_["max_n_sat"]]):
|
|
546
|
+
Pv = (
|
|
547
|
+
sat_["power_scale"]
|
|
548
|
+
* da.sum(spectra[ids == uid], axis=0)[None, None, :]
|
|
549
|
+
* da.ones((1, 1, obs.n_freq))
|
|
550
|
+
)
|
|
551
|
+
tle = tles[norad_ids == uid]
|
|
552
|
+
if len(tle) == 1:
|
|
553
|
+
obs.addTLESatelliteRFI(Pv, [uid], tle)
|
|
554
|
+
else:
|
|
555
|
+
print()
|
|
556
|
+
print(f"norad_id: {uid} multiply-defined.")
|
|
557
|
+
else:
|
|
558
|
+
print("No NORAD IDs matching in 'norad_spec_model' file given.")
|
|
559
|
+
|
|
560
|
+
|
|
561
|
+
def add_stationary_sources(obs: Observation, obs_spec: dict) -> None:
|
|
562
|
+
|
|
563
|
+
stat_ = obs_spec["rfi_sources"]["stationary"]
|
|
564
|
+
if len(stat_["loc_ids"]) > 0:
|
|
565
|
+
ids = stat_["loc_ids"]
|
|
566
|
+
path_ids = []
|
|
567
|
+
if stat_["geo_path"] is not None:
|
|
568
|
+
geos = pd.read_csv(stat_["geo_path"])
|
|
569
|
+
geos = geos[geos["loc_id"].isin(ids)]
|
|
570
|
+
path_ids.append(geos["loc_id"].values)
|
|
571
|
+
else:
|
|
572
|
+
raise ValueError(
|
|
573
|
+
"'geo_path' must be populated to include stationary RFI sources."
|
|
574
|
+
)
|
|
575
|
+
|
|
576
|
+
if len(path_ids) == 0:
|
|
577
|
+
print("No location IDs matching in 'geo_path' file given.")
|
|
578
|
+
else:
|
|
579
|
+
path_ids = np.concatenate(path_ids)
|
|
580
|
+
|
|
581
|
+
stat_spec = pd.read_csv(stat_["spec_model"])
|
|
582
|
+
stat_spec = stat_spec[stat_spec["loc_id"].isin(ids)]
|
|
583
|
+
|
|
584
|
+
if len(stat_spec) > 0:
|
|
585
|
+
ids, spectra = generate_spectra(stat_spec, obs.freqs, "loc_id")
|
|
586
|
+
uids = np.unique(ids)
|
|
587
|
+
for uid in uids:
|
|
588
|
+
Pv = (
|
|
589
|
+
stat_["power_scale"]
|
|
590
|
+
* da.sum(spectra[ids == uid], axis=0)[None, None, :]
|
|
591
|
+
* da.ones((1, 1, obs.n_freq))
|
|
592
|
+
)
|
|
593
|
+
geo = geos[geos["loc_id"] == uid]
|
|
594
|
+
if len(geo) == 1:
|
|
595
|
+
print()
|
|
596
|
+
print("Adding stationary RFI source ...")
|
|
597
|
+
obs.addStationaryRFI(
|
|
598
|
+
Pv,
|
|
599
|
+
geo["latitude"].values,
|
|
600
|
+
geo["longitude"].values,
|
|
601
|
+
geo["elevation"].values,
|
|
602
|
+
)
|
|
603
|
+
else:
|
|
604
|
+
print()
|
|
605
|
+
print(f"loc_id: {uid} multiply-defined.")
|
|
606
|
+
else:
|
|
607
|
+
print("No locations IDs matching in 'spec_model' file given.")
|
|
608
|
+
|
|
609
|
+
|
|
610
|
+
def add_gains(obs: Observation, obs_spec: dict) -> None:
|
|
611
|
+
|
|
612
|
+
gains_ = obs_spec["gains"]
|
|
613
|
+
gain_offset = gains_["G0_mean"] != 1 or gains_["G0_std"] != 0
|
|
614
|
+
gain_var = gains_["Gt_std_amp"] != 0 or gains_["Gt_std_phase"] != 0
|
|
615
|
+
if gain_offset or gain_var:
|
|
616
|
+
print()
|
|
617
|
+
print("Adding gains ...")
|
|
618
|
+
obs.addGains(
|
|
619
|
+
gains_["G0_mean"],
|
|
620
|
+
gains_["G0_std"],
|
|
621
|
+
gains_["Gt_std_amp"],
|
|
622
|
+
gains_["Gt_std_phase"],
|
|
623
|
+
gains_["Gt_corr_amp"],
|
|
624
|
+
gains_["Gt_corr_phase"],
|
|
625
|
+
gains_["random_seed"],
|
|
626
|
+
)
|
|
627
|
+
else:
|
|
628
|
+
print()
|
|
629
|
+
print("No gains added ...")
|
|
630
|
+
|
|
631
|
+
|
|
632
|
+
def plot_diagnostics(obs: Observation, obs_spec: dict, save_path: str) -> None:
|
|
633
|
+
|
|
634
|
+
diag_ = obs_spec["diagnostics"]
|
|
635
|
+
|
|
636
|
+
if diag_["src_alt"]:
|
|
637
|
+
print()
|
|
638
|
+
print("Plotting source altitude ...")
|
|
639
|
+
plot_src_alt(obs, save_path)
|
|
640
|
+
if diag_["uv_cov"]:
|
|
641
|
+
print()
|
|
642
|
+
print("Plotting uv coverage ...")
|
|
643
|
+
plot_uv(obs, save_path)
|
|
644
|
+
if diag_["rfi_seps"]:
|
|
645
|
+
print()
|
|
646
|
+
print("Plotting RFI angular separations ...")
|
|
647
|
+
plot_angular_seps(obs, save_path)
|
|
648
|
+
|
|
649
|
+
|
|
650
|
+
def print_signal_specs(
|
|
651
|
+
vis_rfi: Array, vis_ast: Array, noise_data: Array, flags: Array
|
|
652
|
+
) -> None:
|
|
653
|
+
|
|
654
|
+
rfi_amp = da.mean(da.abs(vis_rfi)).compute()
|
|
655
|
+
ast_amp = da.mean(da.abs(vis_ast)).compute()
|
|
656
|
+
noise = da.std(noise_data.real).compute()
|
|
657
|
+
flag_rate = 100 * da.mean(flags).compute()
|
|
658
|
+
|
|
659
|
+
print()
|
|
660
|
+
print(f"Mean RFI Amp. : {rfi_amp:.2f} Jy")
|
|
661
|
+
print(f"Mean AST Amp. : {ast_amp:.2f} Jy")
|
|
662
|
+
print(f"Vis Noise Amp. : {noise:.2f} Jy")
|
|
663
|
+
print(f"Flag Rate : {flag_rate:.1f} %")
|
|
664
|
+
|
|
665
|
+
|
|
666
|
+
def write_to_ms(xds: xr.Dataset, ms_path: str, overwrite: bool) -> None:
|
|
667
|
+
start = datetime.now()
|
|
668
|
+
print()
|
|
669
|
+
print(f"Writing visibilities to MS ...")
|
|
670
|
+
write_ms(xds, ms_path, overwrite)
|
|
671
|
+
end = datetime.now()
|
|
672
|
+
print(f"MS Write Time : {end - start}")
|
|
673
|
+
|
|
674
|
+
|
|
675
|
+
def write_to_zarr(obs: Observation, zarr_path: str, overwrite: bool) -> None:
|
|
676
|
+
start = datetime.now()
|
|
677
|
+
print()
|
|
678
|
+
print(f"Writing visibilities to zarr ...")
|
|
679
|
+
obs.write_to_zarr(zarr_path, overwrite)
|
|
680
|
+
end = datetime.now()
|
|
681
|
+
print(f"zarr Write Time : {end - start}")
|
|
682
|
+
|
|
683
|
+
|
|
684
|
+
def save_data(obs: Observation, obs_spec: dict, zarr_path: str, ms_path: str) -> None:
|
|
685
|
+
|
|
686
|
+
if obs_spec["output"]["zarr"] or obs_spec["output"]["ms"]:
|
|
687
|
+
print()
|
|
688
|
+
print("Calculating visibilities ...")
|
|
689
|
+
obs.calculate_vis(flags=obs_spec["output"]["flag_data"])
|
|
690
|
+
|
|
691
|
+
overwrite = obs_spec["output"]["overwrite"]
|
|
692
|
+
|
|
693
|
+
if obs_spec["output"]["zarr"] and obs_spec["output"]["ms"]:
|
|
694
|
+
write_to_zarr(obs, zarr_path, overwrite)
|
|
695
|
+
xds = xr.open_zarr(zarr_path)
|
|
696
|
+
write_to_ms(xds, ms_path, overwrite)
|
|
697
|
+
print_signal_specs(
|
|
698
|
+
xds.vis_rfi.data, xds.vis_ast.data, xds.noise_data.data, xds.flags.data
|
|
699
|
+
)
|
|
700
|
+
elif obs_spec["output"]["zarr"]:
|
|
701
|
+
write_to_zarr(obs, zarr_path, overwrite)
|
|
702
|
+
xds = xr.open_zarr(zarr_path)
|
|
703
|
+
print_signal_specs(
|
|
704
|
+
xds.vis_rfi.data, xds.vis_ast.data, xds.noise_data.data, xds.flags.data
|
|
705
|
+
)
|
|
706
|
+
elif obs_spec["output"]["ms"]:
|
|
707
|
+
write_to_ms(obs.dataset, ms_path, overwrite)
|
|
708
|
+
xds = xds_from_ms(ms_path)
|
|
709
|
+
print_signal_specs(
|
|
710
|
+
xds.RFI_MODEL_DATA.data,
|
|
711
|
+
xds.AST_MODEL_DATA.data,
|
|
712
|
+
xds.NOISE_DATA.data,
|
|
713
|
+
xds.FLAG.data,
|
|
714
|
+
)
|
|
715
|
+
else:
|
|
716
|
+
ValueError(
|
|
717
|
+
"No output format has been chosen. output: zarr: or output: ms: must be True."
|
|
718
|
+
)
|
|
719
|
+
|
|
720
|
+
|
|
721
|
+
def save_inputs(obs: Observation, obs_spec: dict, save_path: str) -> None:
|
|
722
|
+
|
|
723
|
+
for key in ["enu_path", "itrf_path"]:
|
|
724
|
+
path = obs_spec["telescope"][key]
|
|
725
|
+
if path is not None:
|
|
726
|
+
shutil.copy(path, save_path)
|
|
727
|
+
|
|
728
|
+
for key in obs_spec["ast_sources"].keys():
|
|
729
|
+
path = obs_spec["ast_sources"][key]["path"]
|
|
730
|
+
if path is not None:
|
|
731
|
+
shutil.copy(path, save_path)
|
|
732
|
+
|
|
733
|
+
key = (
|
|
734
|
+
2
|
|
735
|
+
* [
|
|
736
|
+
"tle_satellite",
|
|
737
|
+
]
|
|
738
|
+
+ 2
|
|
739
|
+
* [
|
|
740
|
+
"satellite",
|
|
741
|
+
]
|
|
742
|
+
+ 2
|
|
743
|
+
* [
|
|
744
|
+
"stationary",
|
|
745
|
+
]
|
|
746
|
+
)
|
|
747
|
+
subkey = [
|
|
748
|
+
"norad_ids_path",
|
|
749
|
+
"norad_spec_model",
|
|
750
|
+
"circ_path",
|
|
751
|
+
"spec_model",
|
|
752
|
+
"geo_path",
|
|
753
|
+
"spec_model",
|
|
754
|
+
]
|
|
755
|
+
for key1, key2 in zip(key, subkey):
|
|
756
|
+
path = obs_spec["rfi_sources"][key1][key2]
|
|
757
|
+
if path is not None:
|
|
758
|
+
shutil.copy(path, save_path)
|
|
759
|
+
|
|
760
|
+
np.savetxt(os.path.join(save_path, "norad_ids.yaml"), obs.norad_ids, fmt="%i")
|
|
761
|
+
|
|
762
|
+
with open(os.path.join(save_path, "sim_config.yaml"), "w") as fp:
|
|
763
|
+
yaml.dump(obs_spec, fp)
|
|
764
|
+
|
|
765
|
+
|
|
766
|
+
def print_fringe_freq_sat(obs: Observation):
|
|
767
|
+
if len(obs.times_fine) == obs.n_int_samples:
|
|
768
|
+
n_int = int(obs.n_int_samples) - 1
|
|
769
|
+
elif obs.times[-1] - obs.times[0] > 120:
|
|
770
|
+
n_int = int(60 / obs.int_time) * int(obs.n_int_samples)
|
|
771
|
+
else:
|
|
772
|
+
n_int = obs.n_int_samples
|
|
773
|
+
fringe_params = [
|
|
774
|
+
{
|
|
775
|
+
"times": obs.times_fine[::n_int].compute(),
|
|
776
|
+
"freq": obs.freqs.max().compute(),
|
|
777
|
+
"rfi_xyz": da.concatenate(obs.rfi_satellite_xyz, axis=0)[
|
|
778
|
+
i, ::n_int
|
|
779
|
+
].compute(),
|
|
780
|
+
"ants_itrf": obs.ITRF.compute(),
|
|
781
|
+
"ants_u": obs.ants_uvw[::n_int, :, 0].compute(),
|
|
782
|
+
"dec": obs.dec.compute(),
|
|
783
|
+
}
|
|
784
|
+
for i in range(obs.n_rfi_satellite)
|
|
785
|
+
]
|
|
786
|
+
fringe_freq = [calculate_fringe_frequency(**f_params) for f_params in fringe_params]
|
|
787
|
+
f_sample = (
|
|
788
|
+
np.pi
|
|
789
|
+
* np.max(np.abs(fringe_freq))
|
|
790
|
+
* np.max(obs.rfi_satellite_A_app)
|
|
791
|
+
/ np.sqrt(6 * obs.noise_std.mean()).compute()
|
|
792
|
+
)
|
|
793
|
+
n_int = int(np.ceil(obs.int_time * f_sample))
|
|
794
|
+
|
|
795
|
+
print()
|
|
796
|
+
print(f"Maximum Fringe Frequency is : {np.max(np.abs(fringe_freq)):.2f} Hz")
|
|
797
|
+
print(f"Maximum sampling rate is : {f_sample:.2f} Hz")
|
|
798
|
+
print(f"Recommended n_int is >= : {n_int:.0f} ({obs.n_int_samples} used)")
|
|
799
|
+
|
|
800
|
+
|
|
801
|
+
def print_fringe_freq_tle_sat(obs: Observation):
|
|
802
|
+
if len(obs.times_fine) == obs.n_int_samples:
|
|
803
|
+
n_int = obs.n_int_samples - 1
|
|
804
|
+
elif obs.times[-1] - obs.times[0] > 120:
|
|
805
|
+
n_int = int(60 / obs.int_time) * obs.n_int_samples
|
|
806
|
+
else:
|
|
807
|
+
n_int = obs.n_int_samples
|
|
808
|
+
fringe_params = [
|
|
809
|
+
{
|
|
810
|
+
"times_mjd": obs.times_mjd_fine[::n_int].compute(),
|
|
811
|
+
"freq": obs.freqs.max().compute(),
|
|
812
|
+
"rfi_xyz": da.concatenate(obs.rfi_tle_satellite_xyz, axis=0)[
|
|
813
|
+
i, ::n_int
|
|
814
|
+
].compute(),
|
|
815
|
+
"ants_itrf": obs.ITRF.compute(),
|
|
816
|
+
"ants_u": obs.ants_uvw[::n_int, :, 0].compute(),
|
|
817
|
+
"dec": obs.dec.compute(),
|
|
818
|
+
}
|
|
819
|
+
for i in range(obs.n_rfi_tle_satellite)
|
|
820
|
+
]
|
|
821
|
+
fringe_freq = [calculate_fringe_frequency(**f_params) for f_params in fringe_params]
|
|
822
|
+
f_sample = (
|
|
823
|
+
np.pi
|
|
824
|
+
* np.max(np.abs(fringe_freq))
|
|
825
|
+
* np.max(obs.rfi_tle_satellite_A_app)
|
|
826
|
+
/ np.sqrt(6 * obs.noise_std.mean()).compute()
|
|
827
|
+
)
|
|
828
|
+
n_int = int(np.ceil(obs.int_time * f_sample))
|
|
829
|
+
|
|
830
|
+
print()
|
|
831
|
+
print(f"Maximum Fringe Frequency is : {np.max(np.abs(fringe_freq)):.2f} Hz")
|
|
832
|
+
print(
|
|
833
|
+
f"Maximum RFI Amplitude : {np.max(obs.rfi_tle_satellite_A_app):.5f} sqrt(Jy)"
|
|
834
|
+
)
|
|
835
|
+
print(f"Maximum sampling rate is : {f_sample:.2f} Hz")
|
|
836
|
+
print(f"Recommended n_int is >= : {n_int:.0f} ({obs.n_int_samples} used)")
|
|
837
|
+
|
|
838
|
+
|
|
839
|
+
def run_sim_config(
|
|
840
|
+
obs_spec: dict = None, config_path: str = None, spacetrack_path: str = None
|
|
841
|
+
) -> Tuple[Observation, str]:
|
|
842
|
+
|
|
843
|
+
from tabsim.tle import id_generator
|
|
844
|
+
|
|
845
|
+
log_path = f"log_sim_{id_generator()}.txt"
|
|
846
|
+
log = open(log_path, "w")
|
|
847
|
+
backup = sys.stdout
|
|
848
|
+
sys.stdout = Tee(sys.stdout, log)
|
|
849
|
+
|
|
850
|
+
start = datetime.now()
|
|
851
|
+
print(datetime.now())
|
|
852
|
+
|
|
853
|
+
if config_path is not None:
|
|
854
|
+
obs_spec = load_config(config_path, config_type="sim")
|
|
855
|
+
elif obs_spec is None:
|
|
856
|
+
print("obs_spec or path must be defined.")
|
|
857
|
+
return None
|
|
858
|
+
|
|
859
|
+
obs = load_obs(obs_spec)
|
|
860
|
+
add_astro_sources(obs, obs_spec)
|
|
861
|
+
add_satellite_sources(obs, obs_spec)
|
|
862
|
+
if obs_spec["rfi_sources"]["tle_satellite"]["max_n_sat"] != 0:
|
|
863
|
+
add_tle_satellite_sources(obs, obs_spec, spacetrack_path)
|
|
864
|
+
add_stationary_sources(obs, obs_spec)
|
|
865
|
+
add_gains(obs, obs_spec)
|
|
866
|
+
|
|
867
|
+
# Change this for calculating all RFI sources
|
|
868
|
+
if obs.n_rfi_satellite > 0:
|
|
869
|
+
print_fringe_freq_sat(obs)
|
|
870
|
+
if obs.n_rfi_tle_satellite > 0:
|
|
871
|
+
print_fringe_freq_tle_sat(obs)
|
|
872
|
+
|
|
873
|
+
print(obs)
|
|
874
|
+
|
|
875
|
+
obs_name = mk_obs_name(
|
|
876
|
+
obs_spec["output"]["prefix"], obs, obs_spec["output"]["suffix"]
|
|
877
|
+
)
|
|
878
|
+
save_path, zarr_path, ms_path = mk_obs_dir(
|
|
879
|
+
obs_spec["output"]["path"], obs_name, obs_spec["output"]["overwrite"]
|
|
880
|
+
)
|
|
881
|
+
|
|
882
|
+
input_path = os.path.join(save_path, "input_data")
|
|
883
|
+
|
|
884
|
+
os.makedirs(input_path, exist_ok=True)
|
|
885
|
+
save_inputs(obs, obs_spec, input_path)
|
|
886
|
+
|
|
887
|
+
print()
|
|
888
|
+
print(f"Writing data to : {save_path}")
|
|
889
|
+
|
|
890
|
+
plot_diagnostics(obs, obs_spec, save_path)
|
|
891
|
+
save_data(obs, obs_spec, zarr_path, ms_path)
|
|
892
|
+
|
|
893
|
+
end = datetime.now()
|
|
894
|
+
print()
|
|
895
|
+
print(f"Total simulation time : {end - start}")
|
|
896
|
+
print()
|
|
897
|
+
print(datetime.now())
|
|
898
|
+
|
|
899
|
+
log.close()
|
|
900
|
+
shutil.copy(log_path, save_path)
|
|
901
|
+
os.remove(log_path)
|
|
902
|
+
sys.stdout = backup
|
|
903
|
+
|
|
904
|
+
return obs, save_path
|