def plot_azel(bvis_list, plot_file='azel.png', **kwargs): """ Standard plot of az el coverage :param bvis_list: :param plot_file: :param kwargs: :return: """ plt.clf() r2d = 180.0 / numpy.pi for ibvis, bvis in enumerate(bvis_list): ha = numpy.pi * bvis.time / 43200.0 dec = bvis.phasecentre.dec.rad latitude = bvis.configuration.location.lat.rad az, el = hadec_to_azel(ha, dec, latitude) if ibvis == 0: plt.plot(bvis.time, r2d * az, '.', color='r', label='Azimuth (deg)') plt.plot(bvis.time, r2d * el, '.', color='b', label='Elevation (deg)') else: plt.plot(bvis.time, r2d * az, '.', color='r') plt.plot(bvis.time, r2d * el, '.', color='b') plt.xlabel('HA (s)') plt.ylabel('Angle') plt.legend() plt.title('Azimuth and elevation vs hour angle') plt.savefig(plot_file) plt.show(block=False)
def find_vp(band, vis): ha = calculate_blockvisibility_hourangles(vis).to('rad').value dec = vis.phasecentre.dec.rad latitude = vis.configuration.location.lat.rad az, el = hadec_to_azel(ha, dec, latitude) el_deg = el * 180.0 / numpy.pi el_table = max( 0.0, min( 90.1, elevation_sampling * ((el_deg + elevation_sampling / 2.0) // elevation_sampling))) return get_band_vp(band, el_table)
def find_vp(band, vis): ha = numpy.pi * numpy.average(vis.time) / 43200.0 dec = vis.phasecentre.dec.rad latitude = vis.configuration.location.lat.rad az, el = hadec_to_azel(ha, dec, latitude) el_deg = el * 180.0 / numpy.pi el_table = max( 0.0, min( 90.1, elevation_sampling * ((el_deg + elevation_sampling / 2.0) // elevation_sampling))) return get_band_vp(band, el_table)
def valid_elevation(time, location, phasecentre): ha = numpy.pi * time / 43200.0 dec = phasecentre.dec.rad az, el = hadec_to_azel(ha, dec, location.lat.rad) return el > elevation_limit * numpy.pi / 180.0
def simulate_gaintable_from_zernikes(vis, sc, vp_list, vp_coeffs, vis_slices=None, order=3, use_radec=False, elevation_limit=15.0 * numpy.pi / 180.0, **kwargs): """ Create gaintables for a set of zernikes :param vis: :param sc: Sky components for which pierce points are needed :param vp: List of Voltage patterns in AZELGEO frame :param vp_coeffs: Fractional contribution [nants, nvp] :param order: order of spline (default is 3) :return: """ ntimes, nant = vis.vis.shape[0:2] vp_coeffs = numpy.array(vp_coeffs) gaintables = [ create_gaintable_from_blockvisibility(vis, **kwargs) for i in sc ] if not use_radec: assert isinstance(vis, BlockVisibility) assert vis.configuration.mount[ 0] == 'azel', "Mount %s not supported yet" % vis.configuration.mount[ 0] # The time in the Visibility is hour angle in seconds! number_bad = 0 number_good = 0 # Cache the splines, one per voltage pattern real_splines = list() imag_splines = list() for ivp, vp in enumerate(vp_list): assert vp.wcs.wcs.ctype[0] == 'AZELGEO long', vp.wcs.wcs.ctype[0] assert vp.wcs.wcs.ctype[1] == 'AZELGEO lati', vp.wcs.wcs.ctype[1] nchan, npol, ny, nx = vp.data.shape real_splines.append( RectBivariateSpline(range(ny), range(nx), vp.data[0, 0, ...].real, kx=order, ky=order)) imag_splines.append( RectBivariateSpline(range(ny), range(nx), vp.data[0, 0, ...].imag, kx=order, ky=order)) latitude = vis.configuration.location.lat.rad r2d = 180.0 / numpy.pi s2r = numpy.pi / 43200.0 # For each hourangle, we need to calculate the location of a component # in AZELGEO. With that we can then look up the relevant gain from the # voltage pattern for iha, rows in enumerate( vis_timeslice_iter(vis, vis_slices=vis_slices)): v = create_visibility_from_rows(vis, rows) ha = numpy.average( calculate_blockvisibility_hourangles(v).to('rad').value) # Calculate the az el for this hourangle and the phasecentre declination utc_time = Time([numpy.average(v.time) / 86400.0], format='mjd', scale='utc') azimuth_centre, elevation_centre = calculate_azel( v.configuration.location, utc_time, vis.phasecentre) azimuth_centre = azimuth_centre[0].to('deg').value elevation_centre = elevation_centre[0].to('deg').value for icomp, comp in enumerate(sc): if elevation_centre >= elevation_limit: antgain = numpy.zeros([nant], dtype='complex') # Calculate the location of the component in AZELGEO, then add the pointing offset # for each antenna hacomp = comp.direction.ra.rad - vis.phasecentre.ra.rad + ha deccomp = comp.direction.dec.rad azimuth_comp, elevation_comp = hadec_to_azel( hacomp, deccomp, latitude) for ant in range(nant): for ivp, vp in enumerate(vp_list): nchan, npol, ny, nx = vp.data.shape wcs_azel = vp.wcs.deepcopy() # We use WCS sensible coordinate handling by labelling the axes misleadingly wcs_azel.wcs.crval[0] = azimuth_centre wcs_azel.wcs.crval[1] = elevation_centre wcs_azel.wcs.ctype[0] = 'RA---SIN' wcs_azel.wcs.ctype[1] = 'DEC--SIN' worldloc = [ azimuth_comp * r2d, elevation_comp * r2d, vp.wcs.wcs.crval[2], vp.wcs.wcs.crval[3] ] try: pixloc = wcs_azel.sub(2).wcs_world2pix( [worldloc[:2]], 1)[0] assert pixloc[0] > 2 assert pixloc[0] < nx - 3 assert pixloc[1] > 2 assert pixloc[1] < ny - 3 gain = real_splines[ivp].ev(pixloc[1], pixloc[0]) \ + 1j * imag_splines[ivp](pixloc[1], pixloc[0]) antgain[ant] += vp_coeffs[ant, ivp] * gain number_good += 1 except (ValueError, AssertionError): number_bad += 1 antgain[ant] = 1.0 antgain[ant] = 1.0 / antgain[ant] gaintables[icomp].gain[ iha, :, :, :] = antgain[:, numpy.newaxis, numpy.newaxis, numpy.newaxis] gaintables[icomp].phasecentre = comp.direction else: gaintables[icomp].gain[...] = 1.0 + 0.0j gaintables[icomp].phasecentre = comp.direction number_bad += nant else: assert isinstance(vis, BlockVisibility) number_bad = 0 number_good = 0 # Cache the splines, one per voltage pattern real_splines = list() imag_splines = list() for ivp, vp in enumerate(vp_list): nchan, npol, ny, nx = vp.data.shape real_splines.append( RectBivariateSpline(range(ny), range(nx), vp.data[0, 0, ...].real, kx=order, ky=order)) imag_splines.append( RectBivariateSpline(range(ny), range(nx), vp.data[0, 0, ...].imag, kx=order, ky=order)) for iha, rows in enumerate( vis_timeslice_iter(vis, vis_slices=vis_slices)): # The time in the Visibility is hour angle in seconds! r2d = 180.0 / numpy.pi # For each hourangle, we need to calculate the location of a component # in AZELGEO. With that we can then look up the relevant gain from the # voltage pattern v = create_visibility_from_rows(vis, rows) ha = numpy.average(calculate_blockvisibility_hourangles(v)) for icomp, comp in enumerate(sc): antgain = numpy.zeros([nant], dtype='complex') antwt = numpy.zeros([nant]) ra_comp = comp.direction.ra.rad dec_comp = comp.direction.dec.rad for ant in range(nant): for ivp, vp in enumerate(vp_list): assert vp.wcs.wcs.ctype[ 0] == 'RA---SIN', vp.wcs.wcs.ctype[0] assert vp.wcs.wcs.ctype[ 1] == 'DEC--SIN', vp.wcs.wcs.ctype[1] worldloc = [ ra_comp * r2d, dec_comp * r2d, vp.wcs.wcs.crval[2], vp.wcs.wcs.crval[3] ] nchan, npol, ny, nx = vp.data.shape try: pixloc = vp.wcs.sub(2).wcs_world2pix( [worldloc[:2]], 1)[0] assert pixloc[0] > 2 assert pixloc[0] < nx - 3 assert pixloc[1] > 2 assert pixloc[1] < ny - 3 gain = real_splines[ivp].ev(pixloc[1], pixloc[0]) \ + 1j * imag_splines[ivp](pixloc[1], pixloc[0]) antgain[ant] += vp_coeffs[ant, ivp] * gain antwt[ant] = 1.0 number_good += 1 except (ValueError, AssertionError): number_bad += 1 antgain[ant] = 1e15 antwt[ant] = 0.0 antgain[ant] = 1.0 / antgain[ant] gaintables[icomp].gain[ iha, :, :, :] = antgain[:, numpy.newaxis, numpy.newaxis, numpy.newaxis] gaintables[icomp].weight[ iha, :, :, :] = antwt[:, numpy.newaxis, numpy.newaxis, numpy.newaxis] gaintables[icomp].phasecentre = comp.direction if number_bad > 0: log.warning( "simulate_gaintable_from_zernikes: %d points are inside the voltage pattern image" % (number_good)) log.warning( "simulate_gaintable_from_zernikes: %d points are outside the voltage pattern image" % (number_bad)) return gaintables
def create_visibility(config: Configuration, times: numpy.array, frequency: numpy.array, channel_bandwidth, phasecentre: SkyCoord, weight: float, polarisation_frame=PolarisationFrame('stokesI'), integration_time=1.0, zerow=False, elevation_limit=15.0 * numpy.pi / 180.0, source='unknown', meta=None) -> Visibility: """ Create a Visibility from Configuration, hour angles, and direction of source Note that we keep track of the integration time for BDA purposes :param config: Configuration of antennas :param times: hour angles in radians :param frequency: frequencies (Hz] [nchan] :param weight: weight of a single sample :param phasecentre: phasecentre of observation :param channel_bandwidth: channel bandwidths: (Hz] [nchan] :param integration_time: Integration time ('auto' or value in s) :param polarisation_frame: PolarisationFrame('stokesI') :return: Visibility """ assert phasecentre is not None, "Must specify phase centre" if polarisation_frame is None: polarisation_frame = correlate_polarisation(config.receptor_frame) latitude = config.location.geodetic[1].to('rad').value nch = len(frequency) ants_xyz = config.data['xyz'] nants = len(config.data['names']) nbaselines = int(nants * (nants - 1) / 2) ntimes = 0 for iha, ha in enumerate(times): # Calculate the positions of the antennas as seen for this hour angle # and declination _, elevation = hadec_to_azel(ha, phasecentre.dec.rad, latitude) if elevation_limit is None or (elevation > elevation_limit): ntimes +=1 npol = polarisation_frame.npol nrows = nbaselines * ntimes * nch nrowsperintegration = nbaselines * nch rvis = numpy.zeros([nrows, npol], dtype='complex') rweight = weight * numpy.ones([nrows, npol]) rtimes = numpy.zeros([nrows]) rfrequency = numpy.zeros([nrows]) rchannel_bandwidth = numpy.zeros([nrows]) rantenna1 = numpy.zeros([nrows], dtype='int') rantenna2 = numpy.zeros([nrows], dtype='int') ruvw = numpy.zeros([nrows, 3]) n_flagged = 0 # Do each hour angle in turn row = 0 for iha, ha in enumerate(times): # Calculate the positions of the antennas as seen for this hour angle # and declination _, elevation = hadec_to_azel(ha, phasecentre.dec.rad, latitude) if elevation_limit is None or (elevation > elevation_limit): rtimes[row:row + nrowsperintegration] = ha * 43200.0 / numpy.pi # TODO: optimise loop # Loop over all pairs of antennas. Note that a2>a1 ant_pos = xyz_to_uvw(ants_xyz, ha, phasecentre.dec.rad) for a1 in range(nants): for a2 in range(a1 + 1, nants): rantenna1[row:row + nch] = a1 rantenna2[row:row + nch] = a2 rweight[row:row+nch,...] = 1.0 # Loop over all frequencies and polarisations for ch in range(nch): # noinspection PyUnresolvedReferences k = frequency[ch] / constants.c.value ruvw[row, :] = (ant_pos[a2, :] - ant_pos[a1, :]) * k rfrequency[row] = frequency[ch] rchannel_bandwidth[row] = channel_bandwidth[ch] row += 1 if zerow: ruvw[..., 2] = 0.0 assert row == nrows rintegration_time = numpy.full_like(rtimes, integration_time) vis = Visibility(uvw=ruvw, time=rtimes, antenna1=rantenna1, antenna2=rantenna2, frequency=rfrequency, vis=rvis, weight=rweight, imaging_weight=rweight, integration_time=rintegration_time, channel_bandwidth=rchannel_bandwidth, polarisation_frame=polarisation_frame, source=source, meta=meta) vis.phasecentre = phasecentre vis.configuration = config log.info("create_visibility: %s" % (vis_summary(vis))) assert isinstance(vis, Visibility), "vis is not a Visibility: %r" % vis if elevation_limit is not None: log.info('create_visibility: flagged %d/%d visibilities below elevation limit %f (rad)' % (n_flagged, vis.nvis, elevation_limit)) else: log.info('create_visibility: created %d visibilities' % (vis.nvis)) return vis
def create_blockvisibility(config: Configuration, times: numpy.array, frequency: numpy.array, phasecentre: SkyCoord, weight: float = 1.0, polarisation_frame: PolarisationFrame = None, integration_time=1.0, channel_bandwidth=1e6, zerow=False, elevation_limit=None, source='unknown', meta=None, **kwargs) -> BlockVisibility: """ Create a BlockVisibility from Configuration, hour angles, and direction of source Note that we keep track of the integration time for BDA purposes :param config: Configuration of antennas :param times: hour angles in radians :param frequency: frequencies (Hz] [nchan] :param weight: weight of a single sample :param phasecentre: phasecentre of observation :param channel_bandwidth: channel bandwidths: (Hz] [nchan] :param integration_time: Integration time ('auto' or value in s) :param polarisation_frame: :return: BlockVisibility """ assert phasecentre is not None, "Must specify phase centre" if polarisation_frame is None: polarisation_frame = correlate_polarisation(config.receptor_frame) latitude = config.location.geodetic[1].to('rad').value nch = len(frequency) ants_xyz = config.data['xyz'] nants = len(config.data['names']) ntimes = 0 n_flagged = 0 for iha, ha in enumerate(times): # Calculate the positions of the antennas as seen for this hour angle # and declination _, elevation = hadec_to_azel(ha, phasecentre.dec.rad, latitude) if elevation_limit is None or (elevation > elevation_limit): ntimes +=1 else: n_flagged += 1 assert ntimes > 0, "No unflagged points" if elevation_limit is not None: log.info('create_visibility: flagged %d/%d times below elevation limit %f (rad)' % (n_flagged, ntimes, elevation_limit)) else: log.info('create_visibility: created %d times' % (ntimes)) npol = polarisation_frame.npol visshape = [ntimes, nants, nants, nch, npol] rvis = numpy.zeros(visshape, dtype='complex') rweight = weight * numpy.ones(visshape) rimaging_weight = numpy.ones(visshape) rtimes = numpy.zeros([ntimes]) ruvw = numpy.zeros([ntimes, nants, nants, 3]) # Do each hour angle in turn itime = 0 for iha, ha in enumerate(times): # Calculate the positions of the antennas as seen for this hour angle # and declination ant_pos = xyz_to_uvw(ants_xyz, ha, phasecentre.dec.rad) _, elevation = hadec_to_azel(ha, phasecentre.dec.rad, latitude) if elevation_limit is None or (elevation > elevation_limit): rtimes[itime] = ha * 43200.0 / numpy.pi rweight[itime, ...] = 1.0 # Loop over all pairs of antennas. Note that a2>a1 for a1 in range(nants): for a2 in range(a1 + 1, nants): ruvw[itime, a2, a1, :] = (ant_pos[a2, :] - ant_pos[a1, :]) ruvw[itime, a1, a2, :] = (ant_pos[a1, :] - ant_pos[a2, :]) itime += 1 rintegration_time = numpy.full_like(rtimes, integration_time) rchannel_bandwidth = channel_bandwidth if zerow: ruvw[..., 2] = 0.0 vis = BlockVisibility(uvw=ruvw, time=rtimes, frequency=frequency, vis=rvis, weight=rweight, imaging_weight=rimaging_weight, integration_time=rintegration_time, channel_bandwidth=rchannel_bandwidth, polarisation_frame=polarisation_frame, source=source, meta=meta) vis.phasecentre = phasecentre vis.configuration = config log.info("create_blockvisibility: %s" % (vis_summary(vis))) assert isinstance(vis, BlockVisibility), "vis is not a BlockVisibility: %r" % vis return vis
def create_pointingtable_from_blockvisibility(vis: BlockVisibility, pointing_frame='azel', timeslice=None, frequencyslice: float = None, **kwargs) -> PointingTable: """ Create pointing table from visibility. This makes an empty pointing table consistent with the BlockVisibility. :param vis: BlockVisibilty :param timeslice: Time interval between solutions (s) :param frequency_width: Frequency solution width (Hz) :return: PointingTable """ assert isinstance( vis, BlockVisibility), "vis is not a BlockVisibility: %r" % vis nants = vis.nants if timeslice is None or timeslice == 'auto': utimes = numpy.unique(vis.time) ntimes = len(utimes) pointing_interval = numpy.zeros([ntimes]) if ntimes > 1: pointing_interval[:-1] = utimes[1:] - utimes[0:-1] pointing_interval[-1] = utimes[-1] - utimes[-2] else: pointing_interval[...] = 1.0 else: ntimes = numpy.ceil((numpy.max(vis.time) - numpy.min(vis.time)) / timeslice).astype('int') utimes = numpy.linspace(numpy.min(vis.time), numpy.max(vis.time), ntimes) pointing_interval = timeslice * numpy.ones([ntimes]) # log.debug('create_pointingtable_from_blockvisibility: times are %s' % str(utimes)) # log.debug('create_pointingtable_from_blockvisibility: intervals are %s' % str(pointing_interval)) ntimes = len(utimes) ufrequency = numpy.unique(vis.frequency) nfrequency = len(ufrequency) receptor_frame = ReceptorFrame(vis.polarisation_frame.type) nrec = receptor_frame.nrec pointingshape = [ntimes, nants, nfrequency, nrec, 2] pointing = numpy.zeros(pointingshape) if nrec > 1: pointing[..., 0, 0] = 0.0 pointing[..., 1, 0] = 0.0 pointing[..., 0, 1] = 0.0 pointing[..., 1, 1] = 0.0 ha = numpy.pi * vis.time / 43200.0 dec = vis.phasecentre.dec.rad latitude = vis.configuration.location.lat.rad az, el = hadec_to_azel(ha, dec, latitude) pointing_nominal = numpy.zeros([ntimes, nants, nfrequency, nrec, 2]) pointing_nominal[..., 0] = az[:, numpy.newaxis, numpy.newaxis, numpy.newaxis] pointing_nominal[..., 1] = el[:, numpy.newaxis, numpy.newaxis, numpy.newaxis] pointing_weight = numpy.ones(pointingshape) pointing_time = utimes pointing_frequency = ufrequency pointing_residual = numpy.zeros([ntimes, nfrequency, nrec, 2]) pointing_frame = pointing_frame pt = PointingTable(pointing=pointing, nominal=pointing_nominal, time=pointing_time, interval=pointing_interval, weight=pointing_weight, residual=pointing_residual, frequency=pointing_frequency, receptor_frame=receptor_frame, pointing_frame=pointing_frame, pointingcentre=vis.phasecentre, configuration=vis.configuration) assert isinstance(pt, PointingTable), "pt is not a PointingTable: %r" % pt return pt