def __init__(self, dataset: katdal.DataSet, common: CommonStats, target: katpoint.Target) -> None: self.common = common self.target = target # Status string for each channel self.status: List[str] = ['masked'] * common.channels for channel in common.output_channels: self.status[channel] = 'failed' # Peak per channel (NaN where missing) self.peak: u.Quantity = [math.nan] * common.channels * (u.Jy / u.beam) # Total flux density per (polarization, channel) (NaN where missing) self.totals: u.Quantity = { pol: [math.nan] * common.channels * u.Jy for pol in 'IQUV' } # Noise per channel (NaN where missing) self.noise: u.Quantity = [math.nan] * common.channels * (u.Jy / u.beam) # Estimate noise from weights (NaN where missing) self.weights_noise: u.Quantity = [math.nan] * common.channels * (u.Jy / u.beam) # Increase in noise due to imaging weights self.normalized_noise = [math.nan] * common.channels self.plots: Dict[str, str] = {} # Divs to insert for plots returned by make_plots self.uv_coverage = '' self.frequency_range = bokeh.models.Range1d( self.common.frequencies[0].to_value(FREQUENCY_PLOT_UNIT), self.common.frequencies[-1].to_value(FREQUENCY_PLOT_UNIT), bounds='auto' ) self.channel_range = bokeh.models.Range1d(0, self.common.channels - 1, bounds='auto') self.time_on_target = katsdpimager.metadata.time_on_target(dataset, target) self.model_natural_noise: Optional[u.Quantity] = None if self.common.sefd is not None and len(self.common.antennas) > 1: n = len(self.common.antennas) # Correlator efficiency is already folded in to self.common.sefd denom = math.sqrt(2 * n * (n - 1) * self.time_on_target * self.common.channel_width) self.model_natural_noise = self.common.sefd / denom / u.beam mask = katsdpimager.metadata.target_mask(dataset, target) self.timestamps = dataset.timestamps[mask] self.time_range = bokeh.models.Range1d( datetime.fromtimestamp(self.timestamps[0] - 0.5 * dataset.dump_period, timezone.utc), datetime.fromtimestamp(self.timestamps[-1] + 0.5 * dataset.dump_period, timezone.utc) ) # Find contiguous time intervals on target delta = np.diff(mask, prepend=0, append=0) starts = np.nonzero(delta == 1)[0] ends = np.nonzero(delta == -1)[0] - 1 self.time_intervals = list(zip(dataset.timestamps[starts] - 0.5 * dataset.dump_period, dataset.timestamps[ends] + 0.5 * dataset.dump_period)) self.array_ant = dataset.sensor['Antennas/array/antenna'][0] self.ants = dataset.ants self.elevation = target.azel(timestamp=self.timestamps, antenna=self.array_ant)[1] << u.rad self.parallactic_angle = target.parallactic_angle( timestamp=self.timestamps, antenna=self.array_ant) << u.rad
class TestVirtualSensors: def setup(self): self.target = Target('PKS1934-638, radec, 19:39, -63:42') self.antennas = [ Antenna('m000, -30:42:39.8, 21:26:38.0, 1086.6, 13.5, ' '-8.264 -207.29 8.5965'), Antenna('m063, -30:42:39.8, 21:26:38.0, 1086.6, 13.5, ' '-3419.5845 -1840.48 16.3825') ] corrprods = [('m000h', 'm000h'), ('m000v', 'm000v'), ('m063h', 'm063h'), ('m063v', 'm063v'), ('m000h', 'm063h'), ('m000v', 'm063v')] subarray = Subarray(self.antennas, corrprods) spw = SpectralWindow(centre_freq=1284e6, channel_width=0, num_chans=16, sideband=1, bandwidth=856e6) # Pick a time when the source is up as that seems more realistic self.timestamps = 1234667890.0 + 1.0 * np.arange(10) self.dataset = MinimalDataSet(self.target, subarray, spw, self.timestamps) self.array_ant = self.dataset.sensor.get('Antennas/array/antenna')[0] def test_timestamps(self): mjd = Timestamp(self.timestamps[0]).to_mjd() assert_equal(self.dataset.mjd[0], mjd) lst = self.array_ant.local_sidereal_time(self.timestamps) # Convert LST from radians (katpoint) to hours (katdal) assert_array_equal(self.dataset.lst, lst * (12 / np.pi)) def test_pointing(self): az, el = self.target.azel(self.timestamps, self.antennas[1]) assert_array_equal(self.dataset.az[:, 1], rad2deg(az)) assert_array_equal(self.dataset.el[:, 1], rad2deg(el)) ra, dec = self.target.radec(self.timestamps, self.antennas[0]) assert_array_almost_equal(self.dataset.ra[:, 0], rad2deg(ra), decimal=5) assert_array_almost_equal(self.dataset.dec[:, 0], rad2deg(dec), decimal=5) angle = self.target.parallactic_angle(self.timestamps, self.antennas[0]) # TODO: Check why this is so poor... see SR-1882 for progress on this assert_array_almost_equal(self.dataset.parangle[:, 0], rad2deg(angle), decimal=0) x, y = self.target.sphere_to_plane(az, el, self.timestamps, self.antennas[1]) assert_array_equal(self.dataset.target_x[:, 1], rad2deg(x)) assert_array_equal(self.dataset.target_y[:, 1], rad2deg(y)) def test_uvw(self): u0, v0, w0 = self.target.uvw(self.antennas[0], self.timestamps, self.array_ant) u1, v1, w1 = self.target.uvw(self.antennas[1], self.timestamps, self.array_ant) u = u0 - u1 v = v0 - v1 w = w0 - w1 assert_array_equal(self.dataset.u[:, 4], u) assert_array_equal(self.dataset.v[:, 4], v) assert_array_equal(self.dataset.w[:, 4], w) # Check that both H and V polarisations have the same (u, v, w) assert_array_equal(self.dataset.u[:, 5], u) assert_array_equal(self.dataset.v[:, 5], v) assert_array_equal(self.dataset.w[:, 5], w)