def create_random_pointings( center, npointings, dtheta, date_obs=None, sampling_period=None, latitude=None, longitude=None ): """ Return pointings randomly and uniformly distributed in a spherical cap. Parameters ---------- center : 2-tuple The R.A. and declination of the center of the FOV, in degrees. npointings : int The number of requested pointings dtheta : float The maximum angular distance to the center. date_obs : str or astropy.time.Time, optional The starting date of the observation (UTC). sampling_period : float, optional The sampling period of the pointings, in seconds. latitude : float, optional The observer's latitude [degrees]. Default is DOMEC's. longitude : float, optional The observer's longitude [degrees]. Default is DOMEC's. """ cosdtheta = np.cos(np.radians(dtheta)) theta = np.degrees(np.arccos(cosdtheta + (1 - cosdtheta) * randomu(npointings))) phi = randomu(npointings) * 360 pitch = randomu(npointings) * 360 p = QubicPointing.zeros( npointings, date_obs=date_obs, sampling_period=sampling_period, latitude=latitude, longitude=longitude ) time = p.date_obs + TimeDelta(p.time, format="sec") rotation = ( Cartesian2SphericalOperator("azimuth,elevation", degrees=True) * CartesianEquatorial2HorizontalOperator("NE", time, p.latitude, p.longitude) * Rotation3dOperator("ZY'", center[0], 90 - center[1], degrees=True) * Spherical2CartesianOperator("zenith,azimuth", degrees=True) ) coords = rotation(np.asarray([theta.T, phi.T]).T) p.azimuth = coords[..., 0] p.elevation = coords[..., 1] p.pitch = pitch return p
def create_sweeping_pointings( center, duration, sampling_period, angspeed, delta_az, nsweeps_per_elevation, angspeed_psi, maxpsi, date_obs=None, latitude=None, longitude=None, return_hor=True, decrange=0.0, decspeed=2.0, recenter=False, ): """ Return pointings according to the sweeping strategy: Sweep around the tracked FOV center azimuth at a fixed elevation, an update elevation towards the FOV center at discrete times. Parameters ---------- center : array-like of size 2 The R.A. and Declination of the center of the FOV. duration : float The duration of the observation, in hours. sampling_period : float The sampling period of the pointings, in seconds. angspeed : float The pointing angular speed, in deg / s. delta_az : float The sweeping extent in degrees. nsweeps_per_elevation : int The number of sweeps during a phase of constant elevation. angspeed_psi : float The pitch angular speed, in deg / s. maxpsi : float The maximum pitch angle, in degrees. latitude : float, optional The observer's latitude [degrees]. Default is DOMEC's. longitude : float, optional The observer's longitude [degrees]. Default is DOMEC's. date_obs : str or astropy.time.Time, optional The starting date of the observation (UTC). return_hor : bool, optional Obsolete keyword. Returns ------- pointings : QubicPointing Structured array containing the azimuth, elevation and pitch angles, in degrees. """ nsamples = int(np.ceil(duration * 3600 / sampling_period)) out = QubicPointing.zeros( nsamples, date_obs=date_obs, sampling_period=sampling_period, latitude=latitude, longitude=longitude ) racenter = center[0] deccenter = center[1] + decrange * np.cos(decspeed * 2 * np.pi * out.time / (np.max(out.time) - np.min(out.time))) backforthdt = delta_az / angspeed * 2 jd = (out.date_obs + TimeDelta(out.time, format="sec")).jd tsamples = _gst2lst(_jd2gst(jd), out.longitude) # compute the sweep number isweeps = np.floor(out.time / backforthdt).astype(int) # azimuth/elevation of the center of the field as a function of time azcenter, elcenter = _equ2hor(racenter, deccenter, out.latitude, tsamples) # compute azimuth offset for all time samples daz = out.time * angspeed daz = daz % (delta_az * 2) mask = daz > delta_az daz[mask] = -daz[mask] + 2 * delta_az daz -= delta_az / 2 # elevation is kept constant during nsweeps_per_elevation elcst = np.zeros(nsamples) azcst = np.zeros(nsamples) ielevations = isweeps // nsweeps_per_elevation nelevations = ielevations[-1] + 1 for i in xrange(nelevations): mask = ielevations == i elcst[mask] = np.mean(elcenter[mask]) azcst[mask] = ((np.mean((azcenter[mask] - azcenter[mask][-1]) % 360)) + azcenter[mask][-1]) % 360 # azimuth and elevations to use for pointing if recenter: azptg = azcenter + daz else: azptg = azcst + daz elptg = elcst ### scan psi as well pitch = out.time * angspeed_psi pitch = pitch % (4 * maxpsi) mask = pitch > (2 * maxpsi) pitch[mask] = -pitch[mask] + 4 * maxpsi pitch -= maxpsi if not return_hor: # convert them to RA, Dec raptg, decptg = _hor2equ(azptg, elptg, out.latitude, tsamples) theta = 90 - decptg phi = raptg pointings = np.array([theta, phi, pitch]).T return pointings out.azimuth = azptg out.elevation = elptg out.pitch = pitch return out