def predict_skycomponent_visibility( vis: Union[Visibility, BlockVisibility], sc: Union[Skycomponent, List[Skycomponent]] ) -> Union[Visibility, BlockVisibility]: """Predict the visibility from a Skycomponent, add to existing visibility, for Visibility or BlockVisibility :param vis: Visibility or BlockVisibility :param sc: Skycomponent or list of SkyComponents :return: Visibility or BlockVisibility """ if sc is None: return vis if not isinstance(sc, collections.Iterable): sc = [sc] if isinstance(vis, Visibility): _, im_nchan = list(get_frequency_map(vis, None)) for comp in sc: assert isinstance(comp, Skycomponent), comp assert_same_chan_pol(vis, comp) l, m, n = skycoord_to_lmn(comp.direction, vis.phasecentre) phasor = simulate_point(vis.uvw, l, m) comp = comp.flux[im_nchan, :] vis.data['vis'][...] += comp[:, :] * phasor[:, numpy.newaxis] elif isinstance(vis, BlockVisibility): ntimes, nant, _, nchan, npol = vis.vis.shape k = numpy.array(vis.frequency) / constants.c.to('m s^-1').value for comp in sc: # assert isinstance(comp, Skycomponent), comp assert_same_chan_pol(vis, comp) flux = comp.flux if comp.polarisation_frame != vis.polarisation_frame: flux = convert_pol_frame(flux, comp.polarisation_frame, vis.polarisation_frame) l, m, n = skycoord_to_lmn(comp.direction, vis.phasecentre) uvw = vis.uvw[..., numpy.newaxis] * k phasor = numpy.ones([ntimes, nant, nant, nchan, npol], dtype='complex') for chan in range(nchan): phasor[:, :, :, chan, :] = simulate_point(uvw[..., chan], l, m)[..., numpy.newaxis] vis.data['vis'][..., :, :] += flux[:, :] * phasor[..., :] return vis
def phaserotate_visibility(vis: Visibility, newphasecentre: SkyCoord, tangent=True, inverse=False) -> Visibility: """ Phase rotate from the current phase centre to a new phase centre If tangent is False the uvw are recomputed and the visibility phasecentre is updated. Otherwise only the visibility phases are adjusted :param vis: Visibility to be rotated :param newphasecentre: :param tangent: Stay on the same tangent plane? (True) :param inverse: Actually do the opposite :return: Visibility """ assert isinstance(vis, Visibility), "vis is not a Visibility: %r" % vis l, m, n = skycoord_to_lmn(newphasecentre, vis.phasecentre) # No significant change? if numpy.abs(n) > 1e-15: # Make a new copy newvis = copy_visibility(vis) phasor = simulate_point(newvis.uvw, l, m) if len(newvis.vis.shape) > len(phasor.shape): phasor = phasor[:, numpy.newaxis] if inverse: newvis.data['vis'] *= phasor else: newvis.data['vis'] *= numpy.conj(phasor) # To rotate UVW, rotate into the global XYZ coordinate system and back. We have the option of # staying on the tangent plane or not. If we stay on the tangent then the raster will # join smoothly at the edges. If we change the tangent then we will have to reproject to get # the results on the same image, in which case overlaps or gaps are difficult to deal with. if not tangent: if inverse: xyz = uvw_to_xyz(vis.data['uvw'], ha=-newvis.phasecentre.ra.rad, dec=newvis.phasecentre.dec.rad) newvis.data['uvw'][...] = \ xyz_to_uvw(xyz, ha=-newphasecentre.ra.rad, dec=newphasecentre.dec.rad)[...] else: # This is the original (non-inverse) code xyz = uvw_to_xyz(newvis.data['uvw'], ha=-newvis.phasecentre.ra.rad, dec=newvis.phasecentre.dec.rad) newvis.data['uvw'][...] = xyz_to_uvw( xyz, ha=-newphasecentre.ra.rad, dec=newphasecentre.dec.rad)[...] newvis.phasecentre = newphasecentre return newvis else: return vis
def simulate_rfi_block(bvis, emitter_location, emitter_power=5e3, attenuation=1.0): """ Simulate RFI block :param config: ARL telescope Configuration :param times: observation times (hour angles) :param frequency: frequencies :param phasecentre: :param emitter_location: EarthLocation of emitter :param emitter_power: Power of emitter :param attenuation: Attenuation to be applied to signal :return: """ # Calculate the power spectral density of the DTV station: Watts/Hz emitter = simulate_Noise(bvis.frequency, bvis.time, power=emitter_power, timevariable=False) # Calculate the propagators for signals from Perth to the stations in low # These are fixed in time but vary with frequency. The ad hoc attenuation # is set to produce signal roughly equal to noise at LOW propagators = create_propagators(bvis.configuration, emitter_location, frequency=bvis.frequency, attenuation=attenuation) # Now calculate the RFI at the stations, based on the emitter and the propagators rfi_at_station = calculate_rfi_at_station(propagators, emitter) # Calculate the rfi correlation using the fringe rotation and the rfi at the station # [ntimes, nants, nants, nchan, npol] bvis.data['vis'][...] = calculate_station_correlation_rfi(rfi_at_station) ntimes, nant, _, nchan, npol = bvis.vis.shape # Observatory Hour angle & Declination pole = SkyCoord(ra=+0.0 * u.deg, dec=-26.0 * u.deg, frame='icrs', equinox='J2000') # Calculate phasor needed to shift from the phasecentre to the pole l, m, n = skycoord_to_lmn(pole, bvis.phasecentre) k = numpy.array(bvis.frequency) / constants.c.to('m s^-1').value uvw = bvis.uvw[..., numpy.newaxis] * k phasor = numpy.ones([ntimes, nant, nant, nchan, npol], dtype='complex') for chan in range(nchan): phasor[:, :, :, chan, :] = simulate_point(uvw[..., chan], l, m)[..., numpy.newaxis] # Now fill this into the BlockVisibility bvis.data['vis'] = bvis.data['vis'] * phasor return bvis
def calculate_station_fringe_rotation(ants_xyz, times, frequency, phasecentre, pole): # Time corresponds to hour angle uvw = xyz_to_uvw(ants_xyz, times, phasecentre.dec.rad) nants, nuvw = uvw.shape ntimes = len(times) uvw = uvw.reshape([nants, ntimes, 3]) lmn = skycoord_to_lmn(phasecentre, pole) delay = numpy.dot(uvw, lmn) nchan = len(frequency) phase = numpy.zeros([nants, ntimes, nchan]) for ant in range(nants): for chan in range(nchan): phase[ant, :, chan] = delay[ant] * frequency[chan] / constants.c.value return numpy.exp(2.0 * numpy.pi * 1j * phase)
def find_pierce_points(station_locations, ha, dec, phasecentre, height): """Find the pierce points for a flat screen at specified height :param station_locations: All station locations [:3] :param ha: Hour angle :param dec: Declination :param phasecentre: Phase centre :param height: Height of screen :return: """ source_direction = SkyCoord(ra=ha, dec=dec, frame='icrs', equinox='J2000') local_locations = xyz_to_uvw(station_locations, ha, dec) local_locations -= numpy.average(local_locations, axis=0) lmn = numpy.array(skycoord_to_lmn(source_direction, phasecentre)) lmn[2] += 1.0 pierce_points = local_locations + height * numpy.array(lmn) return pierce_points
def sum_visibility(vis: Visibility, direction: SkyCoord) -> numpy.array: """ Direct Fourier summation in a given direction :param vis: Visibility to be summed :param direction: Direction of summation :return: flux[nch,npol], weight[nch,pol] """ # TODO: Convert to Visibility or remove? assert isinstance(vis, Visibility) or isinstance(vis, BlockVisibility), vis svis = copy_visibility(vis) l, m, n = skycoord_to_lmn(direction, svis.phasecentre) phasor = numpy.conjugate(simulate_point(svis.uvw, l, m)) # Need to put correct mapping here _, frequency = get_frequency_map(svis, None) frequency = list(frequency) nchan = max(frequency) + 1 npol = svis.polarisation_frame.npol flux = numpy.zeros([nchan, npol]) weight = numpy.zeros([nchan, npol]) coords = svis.vis, svis.weight, phasor, list(frequency) for v, wt, p, ic in zip(*coords): for pol in range(npol): flux[ic, pol] += numpy.real(wt[pol] * v[pol] * p) weight[ic, pol] += wt[pol] flux[weight > 0.0] = flux[weight > 0.0] / weight[weight > 0.0] flux[weight <= 0.0] = 0.0 return flux, weight
def simulate_rfi_block(bvis, emitter_location, emitter_power=5e4, attenuation=1.0, use_pole=False): """ Simulate RFI block :param config: ARL telescope Configuration :param times: observation times (hour angles) :param frequency: frequencies :param phasecentre: :param emitter_location: EarthLocation of emitter :param emitter_power: Power of emitter :param attenuation: Attenuation to be applied to signal :param use_pole: Set the emitter to nbe at the southern celestial pole :return: """ # Calculate the power spectral density of the DTV station: Watts/Hz emitter = simulate_DTV(bvis.frequency, bvis.time, power=emitter_power, timevariable=False) # Calculate the propagators for signals from Perth to the stations in low # These are fixed in time but vary with frequency. The ad hoc attenuation # is set to produce signal roughly equal to noise at LOW propagators = create_propagators(bvis.configuration, emitter_location, frequency=bvis.frequency, attenuation=attenuation) # Now calculate the RFI at the stations, based on the emitter and the propagators rfi_at_station = calculate_rfi_at_station(propagators, emitter) # Calculate the rfi correlation using the fringe rotation and the rfi at the station # [ntimes, nants, nants, nchan, npol] bvis.data['vis'][...] = calculate_station_correlation_rfi(rfi_at_station) ntimes, nant, _, nchan, npol = bvis.vis.shape s2r = numpy.pi / 43200.0 k = numpy.array(bvis.frequency) / constants.c.to('m s^-1').value uvw = bvis.uvw[..., numpy.newaxis] * k pole = SkyCoord(ra=+0.0 * u.deg, dec=-90.0 * u.deg, frame='icrs', equinox='J2000') if use_pole: # Calculate phasor needed to shift from the phasecentre to the pole l, m, n = skycoord_to_lmn(pole, bvis.phasecentre) phasor = numpy.ones([ntimes, nant, nant, nchan, npol], dtype='complex') for chan in range(nchan): phasor[:, :, :, chan, :] = simulate_point(uvw[..., chan], l, m)[..., numpy.newaxis] # Now fill this into the BlockVisibility bvis.data['vis'] = bvis.data['vis'] * phasor else: # We know where the emitter is. Calculate the bearing to the emitter from # the site, generate az, el, and convert to ha, dec. ha, dec is static. site = bvis.configuration.location site_tup = (site.lat.deg, site.lon.deg) emitter_tup = (emitter_location.lat.deg, emitter_location.lon.deg) az = -calculate_initial_compass_bearing(site_tup, emitter_tup) * numpy.pi / 180.0 el = 0.0 hadec = azel_to_hadec(az, el, site.lat.rad) # Now step through the time stamps, calculating the effective # sky position for the emitter, and performing phase rotation # appropriately for itime, time in enumerate(bvis.time): ra = -hadec[0] + s2r * time dec = hadec[1] emitter_sky = SkyCoord(ra * u.rad, dec * u.rad) l, m, n = skycoord_to_lmn(emitter_sky, bvis.phasecentre) phasor = numpy.ones([nant, nant, nchan, npol], dtype='complex') for chan in range(nchan): phasor[:, :, chan, :] = simulate_point(uvw[itime, ..., chan], l, m)[..., numpy.newaxis] # Now fill this into the BlockVisibility bvis.data['vis'][itime, ...] = bvis.data['vis'][itime, ...] * phasor return bvis
def fit_visibility(vis, sc, tol=1e-6, niter=20, verbose=False, method='trust-exact', **kwargs): """Fit a single component to a visibility Uses the scipy.optimize.minimize function. :param vis: :param sc: Initial component :param tol: Tolerance of fit :param niter: Number of iterations :param verbose: :param method: 'CG', 'BFGS', 'Powell', 'trust-ncg', 'trust-exact', 'trust-krylov': default 'trust-exact' :param kwargs: :return: component, convergence info as a dictionary """ assert vis.polarisation_frame.type == 'stokesI', "Currently restricted to stokesI" # These derivative have been calculated using sympy. See visibility_fitting_sympy.py def J(params): # Params are flux, l, m S = params[0] l = params[1] m = params[2] u = vis.u[:, numpy.newaxis] v = vis.v[:, numpy.newaxis] vobs = vis.vis p = numpy.exp(-2j * numpy.pi * (u * l + v * m)) vres = vobs - S * p J = numpy.sum(vis.weight * (vres * numpy.conjugate(vres)).real) return J def Jboth(params): # Params are flux, l, m S = params[0] l = params[1] m = params[2] u = vis.u[:, numpy.newaxis] v = vis.v[:, numpy.newaxis] vobs = vis.vis p = numpy.exp(-2j * numpy.pi * (u * l + v * m)) vres = vobs - S * p Vrp = vres * numpy.conjugate(p) * vis.weight J = numpy.sum(vis.weight * (vres * numpy.conjugate(vres)).real) gradJ = numpy.array([ -2.0 * numpy.sum(Vrp.real), +4.0 * numpy.pi * S * numpy.sum(u * Vrp.imag), +4.0 * numpy.pi * S * numpy.sum(v * Vrp.imag) ]) return J, gradJ def hessian(params): S = params[0] l = params[1] m = params[2] u = vis.u[:, numpy.newaxis] v = vis.v[:, numpy.newaxis] w = vis.w[:, numpy.newaxis] wt = vis.weight vobs = vis.vis p = numpy.exp(-2j * numpy.pi * (u * l + v * m)) vres = vobs - S * p Vrp = vres * numpy.conjugate(p) hess = numpy.zeros([3, 3]) hess[0, 0] = 2.0 * numpy.sum(wt) hess[0, 1] = 4.0 * numpy.pi * numpy.sum(wt * u * Vrp.imag) hess[0, 2] = 4.0 * numpy.pi * numpy.sum(wt * v * Vrp.imag) hess[1, 1] = 8.0 * numpy.pi**2 * S * numpy.sum(wt * u**2 * (S + Vrp.real)) hess[1, 2] = 8.0 * numpy.pi**2 * S * numpy.sum(wt * u * v * (S + Vrp.real)) hess[2, 2] = 8.0 * numpy.pi**2 * S * numpy.sum(wt * v**2 * (S + Vrp.real)) hess[1, 0] = hess[0, 1] hess[2, 0] = hess[0, 2] hess[2, 1] = hess[1, 2] return hess # Initialize l,m,n to be in the direction of the component as defined in the frame of # visibility phasecentre l, m, n = skycoord_to_lmn(sc.direction, vis.phasecentre) x0 = numpy.array([sc.flux[0, 0], l, m]) bounds = ((None, None), (-0.1, -0.1), (-0.1, 0.1)) options = {'maxiter': niter, 'disp': verbose} res = {} import time start = time.time() if method == 'BFGS' or method == 'CG' or method == 'Powell': res = minimize(J, x0, method=method, options=options, tol=tol) elif method == 'Nelder-Mead': res = minimize(Jboth, x0, method=method, options=options, tol=tol) elif method == 'L-BFGS-B': res = minimize(Jboth, x0, method=method, jac=True, bounds=bounds, options=options, tol=tol) else: res = minimize(Jboth, x0, method=method, jac=True, hess=hessian, options=options, tol=tol) if verbose: print("Solution for %s took %.6f seconds" % (method, time.time() - start)) print("Solution = %s" % str(res.x)) print(res) sc.flux[...] = res.x[0] lmn = (res.x[1], res.x[2], 0.0) sc.direction = lmn_to_skycoord(lmn, vis.phasecentre) return sc, res
def phaserotate_visibility(vis: Visibility, newphasecentre: SkyCoord, tangent=True, inverse=False) -> Visibility: """ Phase rotate from the current phase centre to a new phase centre If tangent is False the uvw are recomputed and the visibility phasecentre is updated. Otherwise only the visibility phases are adjusted :param vis: Visibility to be rotated :param newphasecentre: :param tangent: Stay on the same tangent plane? (True) :param inverse: Actually do the opposite :return: Visibility """ l, m, n = skycoord_to_lmn(newphasecentre, vis.phasecentre) # No significant change? if numpy.abs(n) < 1e-15: return vis # Make a new copy newvis = copy_visibility(vis) if isinstance(vis, Visibility): phasor = simulate_point(newvis.uvw, l, m) if len(newvis.vis.shape) > len(phasor.shape): phasor = phasor[:, numpy.newaxis] if inverse: newvis.data['vis'] *= phasor else: newvis.data['vis'] *= numpy.conj(phasor) # To rotate UVW, rotate into the global XYZ coordinate system and back. We have the option of # staying on the tangent plane or not. If we stay on the tangent then the raster will # join smoothly at the edges. If we change the tangent then we will have to reproject to get # the results on the same image, in which case overlaps or gaps are difficult to deal with. if not tangent: if inverse: xyz = uvw_to_xyz(vis.data['uvw'], ha=-newvis.phasecentre.ra.rad, dec=newvis.phasecentre.dec.rad) newvis.data['uvw'][...] = \ xyz_to_uvw(xyz, ha=-newphasecentre.ra.rad, dec=newphasecentre.dec.rad)[...] else: # This is the original (non-inverse) code xyz = uvw_to_xyz(newvis.data['uvw'], ha=-newvis.phasecentre.ra.rad, dec=newvis.phasecentre.dec.rad) newvis.data['uvw'][...] = xyz_to_uvw(xyz, ha=-newphasecentre.ra.rad, dec=newphasecentre.dec.rad)[ ...] newvis.phasecentre = newphasecentre return newvis elif isinstance(vis, BlockVisibility): k = numpy.array(vis.frequency) / constants.c.to('m s^-1').value uvw = vis.uvw[..., numpy.newaxis] * k phasor = numpy.ones_like(vis.vis, dtype='complex') _, _, _, nchan, npol = vis.vis.shape for chan in range(nchan): phasor[:, :, :, chan, :] = simulate_point(uvw[..., chan], l, m)[..., numpy.newaxis] if inverse: newvis.data['vis'] *= phasor else: newvis.data['vis'] *= numpy.conj(phasor) # To rotate UVW, rotate into the global XYZ coordinate system and back. We have the option of # staying on the tangent plane or not. If we stay on the tangent then the raster will # join smoothly at the edges. If we change the tangent then we will have to reproject to get # the results on the same image, in which case overlaps or gaps are difficult to deal with. if not tangent: # UVW is shape [nants, nants, 3], we want [nants * nants, 3] nrows, nants, _, _ = vis.uvw.shape uvw_linear = vis.uvw.reshape([nrows * nants * nants, 3]) if inverse: xyz = uvw_to_xyz(uvw_linear, ha=-newvis.phasecentre.ra.rad, dec=newvis.phasecentre.dec.rad) uvw_linear = \ xyz_to_uvw(xyz, ha=-newphasecentre.ra.rad, dec=newphasecentre.dec.rad)[...] else: # This is the original (non-inverse) code xyz = uvw_to_xyz(uvw_linear, ha=-newvis.phasecentre.ra.rad, dec=newvis.phasecentre.dec.rad) uvw_linear = \ xyz_to_uvw(xyz, ha=-newphasecentre.ra.rad, dec=newphasecentre.dec.rad)[...] newvis.phasecentre = newphasecentre newvis.data['uvw'][...] = uvw_linear.reshape([nrows, nants, nants, 3]) return newvis else: raise ValueError("vis argument neither Visibility or BlockVisibility")
def test_skycoord_to_lmn(self): center = SkyCoord(ra=0, dec=0, unit=u.deg) north = SkyCoord(ra=0, dec=90, unit=u.deg) south = SkyCoord(ra=0, dec=-90, unit=u.deg) east = SkyCoord(ra=90, dec=0, unit=u.deg) west = SkyCoord(ra=-90, dec=0, unit=u.deg) assert_allclose(skycoord_to_lmn(center, center), (0, 0, 0)) assert_allclose(skycoord_to_lmn(north, center), (0, 1, -1)) assert_allclose(skycoord_to_lmn(south, center), (0, -1, -1)) assert_allclose(skycoord_to_lmn(south, north), (0, 0, -2), atol=1e-14) assert_allclose(skycoord_to_lmn(east, center), (1, 0, -1)) assert_allclose(skycoord_to_lmn(west, center), (-1, 0, -1)) assert_allclose(skycoord_to_lmn(center, west), (1, 0, -1)) assert_allclose(skycoord_to_lmn(north, west), (0, 1, -1), atol=1e-14) assert_allclose(skycoord_to_lmn(south, west), (0, -1, -1), atol=1e-14) assert_allclose(skycoord_to_lmn(north, east), (0, 1, -1), atol=1e-14) assert_allclose(skycoord_to_lmn(south, east), (0, -1, -1), atol=1e-14)