Skip to content

Commit d285e98

Browse files
committed
Add pure parallactic correction option
1 parent 278bcd8 commit d285e98

8 files changed

+176
-4
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
nenupy.astro.beam\_correction.compute\_projection\_corrections
2+
==============================================================
3+
4+
.. currentmodule:: nenupy.astro.beam_correction
5+
6+
.. autofunction:: compute_projection_corrections

docs/source/_autosummary/nenupy.astro.beam_correction.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
nenupy.astro.beam\_correction
1+
nenupy.astro.beam\_correction
22
=============================
33

44
.. automodule:: nenupy.astro.beam_correction
@@ -15,6 +15,7 @@ nenupy.astro.beam\_correction
1515
:toctree:
1616

1717
compute_jones_matrices
18+
compute_projection_corrections
1819
convert_to_mueller
1920
matrices_to_hdf5
2021

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
nenupy.schedule.open\_time.NenuEvent
2+
====================================
3+
4+
.. currentmodule:: nenupy.schedule.open_time
5+
6+
.. autoclass:: NenuEvent
7+
:members:
8+
:show-inheritance:
9+
:inherited-members:
10+
11+
12+
13+
.. automethod:: __init__
14+
15+
16+
.. rubric:: Methods
17+
18+
.. autosummary::
19+
20+
~NenuEvent.__init__
21+
~NenuEvent.connect_to_plot_events
22+
~NenuEvent.contains
23+
~NenuEvent.draw
24+
25+
26+
27+
28+
29+

docs/source/_autosummary/nenupy.schedule.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
nenupy.schedule.geneticalgo
3434
nenupy.schedule.obsblocks
3535
nenupy.schedule.open_time
36+
nenupy.schedule.satellite_position
3637
nenupy.schedule.schedule
3738
nenupy.schedule.targets
3839

nenupy/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
__copyright__ = "Copyright 2023, nenupy"
66
__credits__ = ["Alan Loh"]
77
__license__ = "MIT"
8-
__version__ = "2.7.15"
8+
__version__ = "2.7.16"
99
__maintainer__ = "Alan Loh"
1010
__email__ = "alan.loh@obspm.fr"
1111

nenupy/astro/beam_correction.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
__all__ = [
55
"compute_jones_matrices",
6-
"compute_projection_correction",
6+
"compute_projection_corrections",
77
"convert_to_mueller",
88
"matrices_to_hdf5"
99
]

nenupy/io/tf.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,55 @@ def wrapper_task(
361361
repeatable=False,
362362
)
363363

364+
@classmethod
365+
def correct_parallactic_rotation(cls):
366+
""":class:`~nenupy.io.tf.TFTask` calling :func:`~nenupy.io.tf_utils.correct_parallactic`.
367+
This allows for parallactic angle correction by aplying the inverse rotation.
368+
369+
Warning
370+
-------
371+
This task must be computed early in the pipeline process as it involves full Jones operations.
372+
373+
"""
374+
375+
def wrapper_task(
376+
time_unix,
377+
frequency_hz,
378+
data,
379+
dt,
380+
channels,
381+
dreambeam_dt,
382+
dreambeam_skycoord,
383+
):
384+
# DreamBeam correction (beam gain + parallactic angle)
385+
if (
386+
(dreambeam_dt is None)
387+
or (dreambeam_skycoord is None)
388+
):
389+
return time_unix, frequency_hz, data
390+
data = utils.correct_parallactic(
391+
time_unix=time_unix,
392+
frequency_hz=frequency_hz,
393+
data=data,
394+
dt_sec=dt.to_value(u.s),
395+
time_step_sec=dreambeam_dt.to_value(u.s),
396+
n_channels=channels,
397+
skycoord=dreambeam_skycoord
398+
)
399+
return time_unix, frequency_hz, data
400+
401+
return cls(
402+
"Parallactic corection",
403+
wrapper_task,
404+
[
405+
"channels",
406+
"dt",
407+
"dreambeam_skycoord",
408+
"dreambeam_dt",
409+
],
410+
repeatable=False,
411+
)
412+
364413
@classmethod
365414
def correct_faraday_rotation(cls):
366415
""":class:`~nenupy.io.tf.TFTask` calling :func:`~nenupy.io.tf_utils.de_faraday_data` to correct for Faraday rotation for a given ``'rotation_measure'`` set in :attr:`~nenupy.io.tf.TFPipeline.parameters`.

nenupy/io/tf_utils.py

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,17 @@
3232

3333
log = logging.getLogger(__name__)
3434

35-
from nenupy.astro import dispersion_delay, faraday_angle
35+
from nenupy.astro import dispersion_delay, faraday_angle, parallactic_angle
3636
from nenupy.astro.beam_correction import compute_jones_matrices, compute_projection_corrections
3737

3838
__all__ = [
39+
"apply_dreambeam_corrections",
3940
"blocks_to_tf_data",
4041
"compute_spectra_frequencies",
4142
"compute_spectra_time",
4243
"compute_stokes_parameters",
4344
"correct_bandpass",
45+
"correct_parallactic",
4446
"crop_subband_edges",
4547
"de_disperse_array",
4648
"de_faraday_data",
@@ -580,6 +582,90 @@ def correct_bandpass(data: np.ndarray, n_channels: int) -> np.ndarray:
580582
return data.reshape((n_times, n_freqs, 2, 2))
581583

582584

585+
# ============================================================= #
586+
# -------------------- correct_parallactic -------------------- #
587+
def correct_parallactic(
588+
time_unix: np.ndarray,
589+
frequency_hz: np.ndarray,
590+
data: np.ndarray,
591+
dt_sec: float,
592+
time_step_sec: float,
593+
n_channels: int,
594+
skycoord: SkyCoord
595+
) -> np.ndarray:
596+
597+
# Basic checks to make sure the dimensions are correct
598+
freq_size = frequency_hz.size
599+
time_size = time_unix.size
600+
if time_size != data.shape[0]:
601+
raise ValueError("There is a problem in the time dimension!")
602+
if (freq_size != data.shape[1]) or (freq_size % n_channels != 0):
603+
raise ValueError("There is a problem in the frequency dimension!")
604+
n_subbands = int(freq_size / n_channels)
605+
606+
# Compute the number of time samples that will be corrected together
607+
time_group_size = int(np.round(time_step_sec / dt_sec))
608+
log.debug(
609+
f"\tGroups of {time_group_size} time blocks will be corrected altogether ({dt_sec*time_group_size} sec resolution)."
610+
)
611+
n_time_groups = time_size // time_group_size
612+
leftover_time_samples = time_size % time_group_size
613+
614+
# Build the time array for the Jones solutions
615+
start_time = Time(time_unix[0], format="unix", precision=7)
616+
jones_times = start_time + np.arange(time_group_size) * TimeDelta(time_group_size * dt_sec, format="sec")
617+
jones_unix = jones_times.unix
618+
619+
# Reshape the data at the time and frequency resolutions
620+
# Take into account leftover times
621+
data_leftover = data[-leftover_time_samples:, ...].reshape(
622+
(leftover_time_samples, n_subbands, n_channels, 2, 2)
623+
)
624+
data = data[: time_size - leftover_time_samples, ...].reshape(
625+
(n_time_groups, time_group_size, n_subbands, n_channels, 2, 2)
626+
)
627+
628+
# Do the same with the time
629+
group_start_time = time_unix[: time_size - leftover_time_samples].reshape(
630+
(n_time_groups, time_group_size)
631+
)[:, 0]
632+
time_start_idx = np.argmax(jones_unix >= group_start_time[0])
633+
time_stop_idx = time_start_idx + n_time_groups - 1
634+
635+
par_angle = parallactic_angle(coordinates=skycoord, time=jones_times).rad
636+
jones_parallactic = np.array([
637+
[np.cos(par_angle), - np.sin(par_angle)],
638+
[np.sin(par_angle), np.cos(par_angle)]
639+
])
640+
jones_parallactic = np.swapaxes(jones_parallactic, 2, 0)
641+
jones_parallactic = np.linalg.inv(jones_parallactic)
642+
643+
jones = jones_parallactic[
644+
time_start_idx : time_stop_idx + 1, :, :
645+
][:, None, None, None, :, :]
646+
jones_leftover = jones_parallactic[
647+
-1, :, :
648+
][None, None, None, :, :]
649+
650+
# Compute the Hermitian matrices
651+
jones_transpose = np.swapaxes(jones, -2, -1)
652+
jones_leftover_transpose = np.swapaxes(jones_leftover, -2, -1)
653+
jones_hermitian = np.conjugate(jones_transpose)
654+
jones_leftover_hermitian = np.conjugate(jones_leftover_transpose)
655+
656+
return np.concatenate(
657+
(
658+
np.matmul(jones, np.matmul(data, jones_hermitian)).reshape(
659+
(time_size - leftover_time_samples, freq_size, 2, 2)
660+
),
661+
np.matmul(
662+
jones_leftover, np.matmul(data_leftover, jones_leftover_hermitian)
663+
).reshape((leftover_time_samples, freq_size, 2, 2)),
664+
),
665+
axis=0
666+
)
667+
668+
583669
# ============================================================= #
584670
# -------------------- crop_subband_edges --------------------- #
585671
def crop_subband_edges(

0 commit comments

Comments
 (0)