diff --git a/quartical/calibration/solver.py b/quartical/calibration/solver.py index ddf3452f..e36586bf 100644 --- a/quartical/calibration/solver.py +++ b/quartical/calibration/solver.py @@ -229,7 +229,8 @@ def solver_wrapper( corr_mode ) else: - jhj = np.zeros(getattr(active_spec, "pshape", active_spec.shape)) + pshape, gshape = active_spec.pshape, active_spec.shape + jhj = np.zeros(pshape if term.is_parameterized else gshape) conv_iter, conv_perc = 0, 1 # If reweighting is enabled, do it when the epoch changes, except diff --git a/quartical/config/gain_schema.yaml b/quartical/config/gain_schema.yaml index 266c23b7..2bc4ff57 100644 --- a/quartical/config/gain_schema.yaml +++ b/quartical/config/gain_schema.yaml @@ -17,6 +17,7 @@ gain: - crosshand_phase_null_v - leakage - parallactic_angle + - feed_flip info: Type of gain to solve for. diff --git a/quartical/gains/__init__.py b/quartical/gains/__init__.py index e652419d..e7d6fc55 100644 --- a/quartical/gains/__init__.py +++ b/quartical/gains/__init__.py @@ -10,6 +10,7 @@ from quartical.gains.leakage import Leakage from quartical.gains.delay_and_tec import DelayAndTec from quartical.gains.parallactic_angle import ParallacticAngle +from quartical.gains.feed_flip import FeedFlip TERM_TYPES = { @@ -26,5 +27,6 @@ "crosshand_phase_null_v": CrosshandPhaseNullV, "leakage": Leakage, "delay_and_tec": DelayAndTec, - "parallactic_angle": ParallacticAngle + "parallactic_angle": ParallacticAngle, + "feed_flip": FeedFlip } diff --git a/quartical/gains/feed_flip/__init__.py b/quartical/gains/feed_flip/__init__.py new file mode 100644 index 00000000..00089918 --- /dev/null +++ b/quartical/gains/feed_flip/__init__.py @@ -0,0 +1,47 @@ +import numpy as np +from quartical.gains.gain import Gain +from quartical.gains.conversion import amp_trig_to_complex + + +class FeedFlip(Gain): + + solver = None # This is not a solvable term. + # Conversion functions required for interpolation NOTE: Non-parameterised + # gains will always be reinterpreted and parameterised in amplitude and + # phase for the sake of simplicity. + # TODO: Make this a real valued term - took simple approach for now. + native_to_converted = ( + (0, (np.abs,)), + (0, (np.angle, np.cos)), + (1, (np.angle, np.sin)) + ) + converted_to_native = ( + (3, amp_trig_to_complex), + ) + converted_dtype = np.float64 + native_dtype = np.complex128 + + def __init__(self, term_name, term_opts): + + super().__init__(term_name, term_opts) + + self.time_interval = 0 + self.freq_interval = 0 + + def init_term(self, term_spec, ref_ant, ms_kwargs, term_kwargs, meta=None): + """Initialise the gains (and parameters).""" + + (_, _, gain_shape, _) = term_spec + + gains = np.ones(gain_shape, dtype=np.complex128) + + if gain_shape[-1] == 4: + gains[..., (0, 3)] = 0 # 2-by-2 antidiagonal. + else: + raise ValueError( + "Feed flip unsupported for less than four correlations" + ) + + gain_flags = np.zeros(gains.shape[:-1], dtype=np.int8) + + return gains, gain_flags