def plot_azel(bvis_list, plot_file=None, **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 = calculate_blockvisibility_hourangles(bvis).value az, el = calculate_blockvisibility_azel(bvis) if ibvis == 0: plt.plot(ha, az.deg, '.', color='r', label='Azimuth (deg)') plt.plot(ha, el.deg, '.', color='b', label='Elevation (deg)') else: plt.plot(ha, az.deg, '.', color='r') plt.plot(ha, el.deg, '.', color='b') plt.xlabel('HA (hours)') plt.ylabel('Angle') plt.legend() plt.title('Azimuth and elevation vs hour angle') if plot_file is not None: plt.savefig(plot_file) plt.show(block=False)
def plot_pa(bvis_list, plot_file=None, **kwargs): """ Standard plot of parallactic angle coverage :param bvis_list: :param plot_file: :param kwargs: :return: """ plt.clf() for ibvis, bvis in enumerate(bvis_list): ha = calculate_blockvisibility_hourangles(bvis).value pa = calculate_blockvisibility_parallactic_angles(bvis) if ibvis == 0: plt.plot(ha, pa.deg, '.', color='r', label='PA (deg)') else: plt.plot(ha, pa.deg, '.', color='r') plt.xlabel('HA (hours)') plt.ylabel('Parallactic Angle') plt.legend() plt.title('Parallactic angle vs hour angle') if plot_file is not None: plt.savefig(plot_file) plt.show(block=False)
def test_hourangle(self): ha = calculate_blockvisibility_hourangles(self.bvis) numpy.testing.assert_array_almost_equal(ha[0].deg, -88.487126)
def create_gaintable_from_screen(vis, sc, screen, height=3e5, vis_slices=None, scale=1.0, r0=5e3, type_atmosphere='ionosphere', **kwargs): """ Create gaintables from a screen calculated using ARatmospy Screen axes are ['XX', 'YY', 'TIME', 'FREQ'] :param vis: :param sc: Sky components for which pierce points are needed :param screen: :param height: Height (in m) of screen above telescope e.g. 3e5 :param r0: r0 in meters :param type_atmosphere: 'ionosphere' or 'troposphere' :param scale: Multiply the screen by this factor :return: """ assert isinstance(vis, BlockVisibility) station_locations = vis.configuration.xyz scale = numpy.power(r0 / 5000.0, -5.0 / 3.0) if type_atmosphere == 'troposphere': # In troposphere files, the units are phase in radians. screen_to_phase = scale else: # In the ionosphere file, the units are dTEC. screen_to_phase = -scale * 8.44797245e9 / numpy.array(vis.frequency) nant = station_locations.shape[0] t2r = numpy.pi / 43200.0 gaintables = [ create_gaintable_from_blockvisibility(vis, **kwargs) for i in sc ] number_bad = 0 number_good = 0 ha_zero = numpy.average(calculate_blockvisibility_hourangles(vis)) 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) - ha_zero).to('rad').value for icomp, comp in enumerate(sc): pp = find_pierce_points(station_locations, (comp.direction.ra.rad + t2r * ha) * units.rad, comp.direction.dec, height=height, phasecentre=vis.phasecentre) scr = numpy.zeros([nant]) for ant in range(nant): pp0 = pp[ant][0:2] # Using narrow band approach - we should loop over frequency try: worldloc = [ pp0[0], pp0[1], ha, numpy.average(vis.frequency) ] pixloc = screen.wcs.wcs_world2pix([worldloc], 0)[0].astype('int') if type_atmosphere == 'troposphere': pixloc[3] = 0 scr[ant] = screen_to_phase * screen.data[ pixloc[3], pixloc[2], pixloc[1], pixloc[0]] number_good += 1 except (ValueError, IndexError): number_bad += 1 scr[ant] = 0.0 # axes of gaintable.gain are time, ant, nchan, nrec gaintables[icomp].gain[iha, :, :, :] = numpy.exp( 1j * scr)[..., numpy.newaxis, numpy.newaxis, numpy.newaxis] gaintables[icomp].phasecentre = comp.direction assert number_good > 0, "create_gaintable_from_screen: There are no pierce points inside the atmospheric screen image" if number_bad > 0: log.warning( "create_gaintable_from_screen: %d pierce points are inside the atmospheric screen image" % (number_good)) log.warning( "create_gaintable_from_screen: %d pierce points are outside the atmospheric screen image" % (number_bad)) return gaintables
def grid_gaintable_to_screen(vis, gaintables, screen, height=3e5, gaintable_slices=None, scale=1.0, r0=5e3, type_atmosphere='ionosphere', vis_slices=None, **kwargs): """ Grid a gaintable to a screen image Screen axes are ['XX', 'YY', 'TIME', 'FREQ'] The phases are just averaged per grid cell, no phase unwrapping is performed. :param vis: :param gaintables: input gaintables :param screen: :param height: Height (in m) of screen above telescope e.g. 3e5 :param r0: r0 in meters :param type_atmosphere: 'ionosphere' or 'troposphere' :param scale: Multiply the screen by this factor :return: gridded screen image, weights image """ assert isinstance(vis, BlockVisibility) station_locations = vis.configuration.xyz nant = station_locations.shape[0] t2r = numpy.pi / 43200.0 newscreen = create_empty_image_like(screen) weights = create_empty_image_like(screen) nchan, ntimes, ny, nx = screen.shape number_no_weight = 0 for gaintable in gaintables: ha_zero = numpy.average(calculate_blockvisibility_hourangles(vis)) 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) - ha_zero).to('rad').value pp = find_pierce_points(station_locations, (gaintable.phasecentre.ra.rad + t2r * ha) * units.rad, gaintable.phasecentre.dec, height=height, phasecentre=vis.phasecentre) scr = numpy.angle(gaintable.gain[0, :, 0, 0, 0]) wt = gaintable.weight[0, :, 0, 0, 0] for ant in range(nant): pp0 = pp[ant][0:2] for freq in vis.frequency: scale = numpy.power(r0 / 5000.0, -5.0 / 3.0) if type_atmosphere == 'troposphere': # In troposphere files, the units are phase in radians. screen_to_phase = scale else: # In the ionosphere file, the units are dTEC. screen_to_phase = -scale * 8.44797245e9 / freq worldloc = [pp0[0], pp0[1], ha, freq] pixloc = newscreen.wcs.wcs_world2pix([worldloc], 0)[0].astype('int') assert pixloc[0] >= 0 assert pixloc[0] < nx assert pixloc[1] >= 0 assert pixloc[1] < ny pixloc[3] = 0 newscreen.data[ pixloc[3], pixloc[2], pixloc[1], pixloc[0]] += wt[ant] * scr[ant] / screen_to_phase weights.data[pixloc[3], pixloc[2], pixloc[1], pixloc[0]] += wt[ant] if wt[ant] == 0.0: number_no_weight += 1 if number_no_weight > 0: log.warning( "grid_gaintable_to_screen: %d pierce points are have no weight" % (number_no_weight)) assert numpy.max(weights.data) > 0.0, "No points were gridded" newscreen.data[weights.data > 0.0] = newscreen.data[ weights.data > 0.0] / weights.data[weights.data > 0.0] return newscreen, weights
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