Beispiel #1
0
 def _get_4pi_dipole(self, detector, proper, quats, ind, dipole):
     # Rotate velocity into the detector frame.  This must be the same
     # frame (Pxx or Dxx) the 4pi coefficients were computed in.
     # Pure qa.inv(quats) rotates into the Dxx frame
     # Adding psi_uv rotates into Pxx
     if self.full4pi == 'npipe':
         # NPIPE factors are computed in Dxx
         psi_uv = np.radians(self.rimo[detector].psi_uv)
         psi_pol = np.radians(self.rimo[detector].psi_pol)
         polrot = qa.rotation(ZAXIS, -(psi_uv + psi_pol))
         vel = qa.rotate(qa.inv(qa.mult(np.atleast_2d(quats)[ind], polrot)),
                         proper * CINV)
     else:
         # LFI factors are in Pxx
         psi_pol = np.radians(self.rimo[detector].psi_pol)
         polrot = qa.rotation(ZAXIS, -psi_pol)
         vel = qa.rotate(qa.inv(qa.mult(np.atleast_2d(quats)[ind], polrot)),
                         proper * CINV)
     dipole_amplitude = self.get_fourpi_prod(vel, detector, 0)
     # relativistic corrections for the quadrupole
     vel2 = vel.T.copy()
     for i in range(3):
         dipole_amplitude += self.q * vel2[i] \
             * self.get_fourpi_prod(vel, detector, i + 1)
     dipole_amplitude *= self.tcmb
     if self.full4pi == 'npipe':
         # Apply beam efficiency correction so the template
         # reflects unit response to a dipole signal
         dipole_amplitude /= self._last_params[4]
     dipole[ind] = dipole_amplitude
     return
Beispiel #2
0
    def _get_pntg(self,
                  detector,
                  local_start,
                  n,
                  deaberrate=True,
                  margin=0,
                  velocity=None,
                  full_output=False,
                  satquats=None):
        if detector[-1] in '01' and detector[-2] != '-':
            # Single diode, use common radiometer pointing
            det = to_radiometer(detector)
        else:
            det = detector

        detquat = self.RIMO[det].quat

        if satquats is None:
            # Get the satellite attitude
            satquats, _ = read_eff(
                local_start - margin, n + 2 * margin, self.globalfirst,
                self.local_samples[0], self.ringdb, self.ringdb_path,
                self.freq, self.effdir_pntg, 'attitude', self.satquatmask,
                self.eff_cache, self.cache, self.filenames[self.effdir_pntg])
            satquats = satquats.T.copy()

        # Rotate into detector frame and convert to desired format

        quats = qa.mult(qa.norm(satquats), detquat)

        if deaberrate:
            # Correct for aberration
            if velocity is None:
                velocity = self._get_velocity(local_start, n, margin=margin)

        # Manipulate the quaternions in buffers not to allocate excessive
        # Python memory
        buflen = 10000
        for istart in range(0, len(quats), buflen):
            istop = min(istart + buflen, len(quats))
            ind = slice(istart, istop)
            if deaberrate:
                vec = qa.rotate(quats[ind], ZAXIS)
                abvec = np.cross(vec, velocity[ind])
                lens = np.linalg.norm(abvec, axis=1)
                ang = lens * CINV
                abvec /= np.tile(lens, (3, 1)).T  # Normalize for direction
                abquat = qa.rotation(abvec, -ang)
                quats[ind] = qa.mult(abquat, quats[ind])
            if self.coordquat is not None:
                quats[ind] = qa.mult(self.coordquat, quats[ind])

        if full_output:
            return quats, satquats
        else:
            return quats
Beispiel #3
0
 def _get_pntg(self, detector, start, n, azel=False):
     # Get boresight pointing (from disk or cache)
     if azel:
         bore = self._get_boresight_azel(start, n)
     else:
         bore = self._get_boresight(start, n)
     # Apply detector quaternion and return
     return qa.mult(bore, self._detquats[detector])
Beispiel #4
0
def rotate_focalplane(args, data, comm):
    """ The LAT focalplane projected on the sky rotates as the cryostat
    (co-rotator) tilts.  Usually the tilt is the same as the observing
    elevation to maintain constant angle between the mirror and the cryostat.

    This method must be called *before* expanding the detector pointing
    from boresight.
    """

    log = Logger.get()
    timer = Timer()
    timer.start()

    for obs in data.obs:
        if obs["telescope"] != "LAT":
            continue
        tod = obs["tod"]
        cache_name = "corotator_angle_deg"
        if tod.cache.exists(cache_name):
            corotator_angle = tod.cache.reference(cache_name)
        else:
            # If a vector of co-rotator angles isn't already cached,
            # make one now from the observation metadata.  This will
            # ensure they get recorded in the so3g files.
            corotator_angle = obs["corotator_angle_deg"]
            offset, nsample = tod.local_samples
            tod.cache.put(cache_name, np.zeros(nsample) + corotator_angle)
        el = np.degrees(tod.read_boresight_el())
        rot = qa.rotation(
            ZAXIS, np.radians(corotator_angle + el + LAT_COROTATOR_OFFSET_DEG)
        )
        quats = tod.read_boresight()
        quats[:] = qa.mult(quats, rot)
        try:
            # If there are horizontal boresight quaternions, they need
            # to be rotated as well.
            quats = tod.read_boresight(azel=True)
            quats[:] = qa.mult(quats, rot)
        except Exception as e:
            pass

    if comm.comm_world is None or comm.comm_world.rank == 0:
        timer.report_clear("Rotate focalplane")

    return
Beispiel #5
0
def main():
    parser = argparse.ArgumentParser(
        description="This program measures the median offset of subset of "
        "detectors from boresight.",
        usage="get_wafer_offset [options] (use --help for details)")

    group = parser.add_mutually_exclusive_group(required=True)
    group.add_argument(
        "--tube_slots",
        help="Comma-separated list of optics tube slots: c1 (UHF), i5 (UHF), "
        " i6 (MF), i1 (MF), i3 (MF), i4 (MF), o6 (LF).  ")
    group.add_argument("--wafer_slots",
                       help="Comma-separated list of optics tube slots. ")
    parser.add_argument("--reverse",
                        action="store_true",
                        help="Reverse offsets")
    # LAT specific params
    parser.add_argument(
        "--corotate-lat",
        required=False,
        action="store_true",
        help="Rotate LAT receiver to maintain focalplane orientation",
        dest="corotate_lat",
    )
    parser.add_argument(
        "--no-corotate-lat",
        required=False,
        action="store_false",
        help="Do not Rotate LAT receiver to maintain focalplane orientation",
        dest="corotate_lat",
    )
    parser.set_defaults(corotate_lat=True)
    parser.add_argument(
        "--elevation-deg",
        required=False,
        type=np.float,
        help="Observing elevation",
    )

    args = parser.parse_args()

    hw = hardware.get_example()

    # Which telescope?

    if args.wafer_slots is not None:
        wafer_slots = args.wafer_slots.split(",")
        wafer_map = hw.wafer_map()
        tube_slots = [wafer_map["tube_slots"][ws] for ws in wafer_slots]
    else:
        tube_slots = args.tube_slots.split(",")

    telescope = None
    for tube_slot in tube_slots:
        for telescope_name, telescope_data in hw.data["telescopes"].items():
            if tube_slot in telescope_data["tube_slots"]:
                if telescope is None:
                    telescope = telescope_name
                elif telescope != telescope.name:
                    raise RuntimeError(
                        f"Tubes '{tube_slots}' span more than one telescope")
        if telescope is None:
            raise RuntimeError(
                f"Failed to match tube_slot = '{tube_slot}' with a telescope")

    # Which detectors?

    hw.data["detectors"] = hardware.sim_telescope_detectors(hw, telescope)

    match = {}
    tube_slots = None
    if args.wafer_slots is not None:
        match["wafer_slot"] = args.wafer_slots.split(",")
    elif args.tube_slots is not None:
        tube_slots = args.tube_slots.split(",")

    hw = hw.select(tube_slots=tube_slots, match=match)
    ndet = len(hw.data["detectors"])

    # print(f"tube_slots = {tube_slots}, match = {match} leaves {ndet} detectors")

    # Optional corotator rotation

    if telescope == "LAT":
        if args.corotate_lat:
            rot = qa.rotation(ZAXIS, np.radians(LAT_COROTATOR_OFFSET_DEG))
        else:
            if args.elevation_deg is None:
                raise RuntimeError(
                    "You must set the observing elevation when not co-rotating."
                )
            rot = qa.rotation(
                ZAXIS,
                np.radians(args.elevation_deg - 60 + LAT_COROTATOR_OFFSET_DEG))
    else:
        if args.elevation_deg is not None:
            raise RuntimeError("Observing elevation does not matter for SAT")
        rot = None

    # Average detector offset

    vec_mean = np.zeros(3)
    for det_name, det_data in hw.data["detectors"].items():
        quat = det_data["quat"]
        if rot is not None:
            quat = qa.mult(rot, quat)
        vec = qa.rotate(quat, ZAXIS)
        vec_mean += vec
    vec_mean /= ndet

    # Radius

    all_dist = []
    for det_name, det_data in hw.data["detectors"].items():
        quat = det_data["quat"]
        if rot is not None:
            quat = qa.mult(rot, quat)
        vec = qa.rotate(quat, ZAXIS)
        all_dist.append(np.degrees(np.arccos(np.dot(vec_mean, vec))))
    dist_max = np.amax(all_dist)

    # Wafers

    if args.tube_slots is None:
        wafer_slots = set(wafer_slots)
    else:
        wafer_slots = set()
        for tube_slot in tube_slots:
            wafer_slots.update(hw.data["tube_slots"][tube_slot]["wafer_slots"])
    waferstring = ""
    for wafer_slot in sorted(wafer_slots):
        waferstring += f" {wafer_slot}"

    # Translate into Az/El offsets at el=0

    rot = hp.Rotator(rot=[0, 90, 0])
    vec_mean = rot(vec_mean)
    az_offset, el_offset = hp.vec2dir(vec_mean, lonlat=True)

    el_offset *= -1
    if args.reverse:
        az_offset *= -1
        el_offset *= -1

    print(f"{az_offset:.3f} {el_offset:.3f} {dist_max:.3f}" + waferstring)

    return
Beispiel #6
0
def load_fp(args, comm):
    start = MPI.Wtime()
    autotimer = timing.auto_timer()

    fp = None

    # Load focalplane information

    nullquat = np.array([0, 0, 0, 1], dtype=np.float64)

    if comm.comm_world.rank == 0:
        if args.fp is None:
            # in this case, create a fake detector at the boresight
            # with a pure white noise spectrum.
            fake = {}
            fake['quat'] = nullquat
            fake['fwhm'] = 30.0
            fake['fknee'] = 0.0
            fake['fmin'] = 1e-9
            fake['alpha'] = 1.0
            fake['NET'] = 1.0
            fake['color'] = 'r'
            fp = {}
            # Second detector at 22.5 degree polarization angle
            fp['bore1'] = fake
            fake2 = {}
            zrot = qa.rotation(ZAXIS, 22.5 * degree)
            fake2['quat'] = qa.mult(fake['quat'], zrot)
            fake2['fwhm'] = 30.0
            fake2['fknee'] = 0.0
            fake2['fmin'] = 1e-9
            fake2['alpha'] = 1.0
            fake2['NET'] = 1.0
            fake2['color'] = 'r'
            fp['bore2'] = fake2
            # Third detector at 45 degree polarization angle
            fake3 = {}
            zrot = qa.rotation(ZAXIS, 45 * degree)
            fake3['quat'] = qa.mult(fake['quat'], zrot)
            fake3['fwhm'] = 30.0
            fake3['fknee'] = 0.0
            fake3['fmin'] = 1e-9
            fake3['alpha'] = 1.0
            fake3['NET'] = 1.0
            fake3['color'] = 'r'
            fp['bore3'] = fake3
            # Fourth detector at 67.5 degree polarization angle
            fake4 = {}
            zrot = qa.rotation(ZAXIS, 67.5 * degree)
            fake4['quat'] = qa.mult(fake['quat'], zrot)
            fake4['fwhm'] = 30.0
            fake4['fknee'] = 0.0
            fake4['fmin'] = 1e-9
            fake4['alpha'] = 1.0
            fake4['NET'] = 1.0
            fake4['color'] = 'r'
            fp['bore4'] = fake4
        else:
            with open(args.fp, 'rb') as p:
                fp = pickle.load(p)
    fp = comm.comm_world.bcast(fp, root=0)

    stop = MPI.Wtime()
    elapsed = stop - start
    if comm.comm_world.rank == 0:
        print('Create focalplane:  {:.2f} seconds'.format(stop - start),
              flush=args.flush)
    start = stop

    if args.debug:
        if comm.comm_world.rank == 0:
            outfile = '{}/focalplane.png'.format(args.outdir)
            tt.plot_focalplane(fp, 6, 6, outfile)

    detectors = sorted(fp.keys())
    detweights = {}
    for d in detectors:
        net = fp[d]['NET']
        detweights[d] = 1.0 / (args.samplerate * net * net)

    return fp, detweights
def load_fp(args, comm):
    start = MPI.Wtime()
    autotimer = timing.auto_timer()

    fp = None

    # Load focalplane information

    nullquat = np.array([0,0,0,1], dtype=np.float64)

    if comm.comm_world.rank == 0:
        if args.fp is None:
            # in this case, create a fake detector at the boresight
            # with a pure white noise spectrum.
            fake = {}
            fake['quat'] = nullquat
            fake['fwhm'] = 30.0
            fake['fknee'] = 0.0
            fake['fmin'] = 1e-9
            fake['alpha'] = 1.0
            fake['NET'] = 1.0
            fake['color'] = 'r'
            fp = {}
            # Second detector at 22.5 degree polarization angle
            fp['bore1'] = fake
            fake2 = {}
            zrot = qa.rotation(ZAXIS, 22.5*degree)
            fake2['quat'] = qa.mult(fake['quat'], zrot)
            fake2['fwhm'] = 30.0
            fake2['fknee'] = 0.0
            fake2['fmin'] = 1e-9
            fake2['alpha'] = 1.0
            fake2['NET'] = 1.0
            fake2['color'] = 'r'
            fp['bore2'] = fake2
            # Third detector at 45 degree polarization angle
            fake3 = {}
            zrot = qa.rotation(ZAXIS, 45*degree)
            fake3['quat'] = qa.mult(fake['quat'], zrot)
            fake3['fwhm'] = 30.0
            fake3['fknee'] = 0.0
            fake3['fmin'] = 1e-9
            fake3['alpha'] = 1.0
            fake3['NET'] = 1.0
            fake3['color'] = 'r'
            fp['bore3'] = fake3
            # Fourth detector at 67.5 degree polarization angle
            fake4 = {}
            zrot = qa.rotation(ZAXIS, 67.5*degree)
            fake4['quat'] = qa.mult(fake['quat'], zrot)
            fake4['fwhm'] = 30.0
            fake4['fknee'] = 0.0
            fake4['fmin'] = 1e-9
            fake4['alpha'] = 1.0
            fake4['NET'] = 1.0
            fake4['color'] = 'r'
            fp['bore4'] = fake4
        else:
            with open(args.fp, 'rb') as p:
                fp = pickle.load(p)
    fp = comm.comm_world.bcast(fp, root=0)

    stop = MPI.Wtime()
    elapsed = stop - start
    if comm.comm_world.rank == 0:
        print('Create focalplane:  {:.2f} seconds'.format(stop-start),
              flush=args.flush)
    start = stop

    if args.debug:
        if comm.comm_world.rank == 0:
            outfile = '{}/focalplane.png'.format(args.outdir)
            tt.plot_focalplane(fp, 6, 6, outfile)

    detectors = sorted(fp.keys())
    detweights = {}
    for d in detectors:
        net = fp[d]['NET']
        detweights[d] = 1.0 / (args.samplerate * net * net)

    return fp, detweights
def load_focalplane(args, comm, schedule):
    focalplane = None

    # Load focalplane information

    if comm.comm_world is None or comm.comm_world.rank == 0:
        if args.focalplane is None:
            detector_data = {}
            ZAXIS = np.array([0, 0, 1.0])
            # in this case, create a fake detector at the boresight
            # with a pure white noise spectrum.
            fake = {}
            fake["quat"] = np.array([0, 0, 0, 1.0])
            fake["fwhm"] = 30.0
            fake["fknee"] = 0.0
            fake["fmin"] = 1e-9
            fake["alpha"] = 1.0
            fake["NET"] = 1.0
            fake["color"] = "r"
            detector_data["bore1"] = fake
            # Second detector at 22.5 degree polarization angle
            fake2 = {}
            zrot = qa.rotation(ZAXIS, np.radians(22.5))
            fake2["quat"] = qa.mult(fake["quat"], zrot)
            fake2["fwhm"] = 30.0
            fake2["fknee"] = 0.0
            fake2["fmin"] = 1e-9
            fake2["alpha"] = 1.0
            fake2["NET"] = 1.0
            fake2["color"] = "r"
            detector_data["bore2"] = fake2
            # Third detector at 45 degree polarization angle
            fake3 = {}
            zrot = qa.rotation(ZAXIS, np.radians(45))
            fake3["quat"] = qa.mult(fake["quat"], zrot)
            fake3["fwhm"] = 30.0
            fake3["fknee"] = 0.0
            fake3["fmin"] = 1e-9
            fake3["alpha"] = 1.0
            fake3["NET"] = 1.0
            fake3["color"] = "r"
            detector_data["bore3"] = fake3
            # Fourth detector at 67.5 degree polarization angle
            fake4 = {}
            zrot = qa.rotation(ZAXIS, np.radians(67.5))
            fake4["quat"] = qa.mult(fake["quat"], zrot)
            fake4["fwhm"] = 30.0
            fake4["fknee"] = 0.0
            fake4["fmin"] = 1e-9
            fake4["alpha"] = 1.0
            fake4["NET"] = 1.0
            fake4["color"] = "r"
            detector_data["bore4"] = fake4
            focalplane = Focalplane(detector_data=detector_data,
                                    sample_rate=args.sample_rate)
        else:
            focalplane = Focalplane(fname_pickle=args.focalplane,
                                    sample_rate=args.sample_rate)
    if comm.comm_world is not None:
        focalplane = comm.comm_world.bcast(focalplane, root=0)

    if args.debug:
        if comm.comm_world is None or comm.comm_world.rank == 0:
            outfile = "{}/focalplane.png".format(args.outdir)
            plot_focalplane(focalplane, 6, 6, outfile)

    schedule.telescope.focalplane = focalplane
    detweights = focalplane.detweights

    return detweights
Beispiel #9
0
 def _get_pntg(self, detector, start, n):
     detquat = np.asarray(self._fp[detector]["quat"])
     boresight = self._boresight[start:start+n,:]
     data = qa.mult(boresight, detquat)
     return data
Beispiel #10
0
    def translate(self, quats, timestamps, psi_pol=None):
        """
        Translate the input quaternions into planet-centric coordinates
        and convert the offsets to arc minutes.  The quaternions ARE
        EXPECTED to be in galactic coordinates although adding a
        rotation would be straightforward.

        The output coordinate system is Pxx, unless either
        a) quats do not include the psi_pol rotation or
        b) psi_pol is provided in radians. translate() will then remove
            the psi_pol rotation from the quaternions
        """
        if timestamps[0] > self.time[-1] or timestamps[-1] < self.time[0]:
            raise Exception(
                'There is no overlap in the stored and provided time stamps.')

        if psi_pol is not None:
            pol_quat = qa.rotation(zaxis, -psi_pol)
            zquat = qa.mult(pol_quat, self.zquat)
        else:
            zquat = self.zquat

        my_quats = qa.mult(quats, zquat)  # From X-axis to detector position

        # Transform the planet positions into quaternions

        tol = 3600.

        ind = np.logical_and(self.time >= timestamps[0] - tol,
                             self.time <= timestamps[-1] + tol)
        nind = np.sum(ind)

        planettime = self.time[ind]
        planetglon = self.glon[ind]
        planetglat = self.glat[ind]
        planetquat = np.zeros([nind, 4])

        # ZYZ rotation to put the X-axis to the planet position

        phi = planetglon * degree
        theta = -planetglat * degree
        psi = 0

        planetquat[:, 3] = np.cos(.5 * theta) * np.cos(.5 * (phi + psi))
        planetquat[:, 0] = -np.sin(.5 * theta) * np.sin(.5 * (phi - psi))
        planetquat[:, 1] = np.sin(.5 * theta) * np.cos(.5 * (phi - psi))
        planetquat[:, 2] = np.cos(.5 * theta) * np.sin(.5 * (phi + psi))

        targetquats = qa.slerp(timestamps, planettime, planetquat)

        planetvec = qa.rotate(targetquats, xaxis)

        # Rotate the planet into Dxx frame (detector on X-axis)

        planetvec = qa.rotate(qa.inv(my_quats), planetvec)

        # The detector position relative to the planet is the inverse
        # of the planet coordinates in Dxx

        lon, lat = hp.vec2dir(planetvec.T, lonlat=True)

        az, el = -np.array([lon, lat]) * 60  # To arc minutes
        return az, el
Beispiel #11
0
def from_angles(az, el):
    elquat = qa.rotation(yaxis, np.radians(90 - el))
    azquat = qa.rotation(zaxis, np.radians(az))
    return qa.mult(azquat, elquat)
Beispiel #12
0

if __name__ == '__main__':

    import matplotlib.pyplot as plt
    plt.style.use('classic')

    nside = 256
    npix = 12 * nside**2

    pix = np.arange(npix)
    theta, phi = hp.pix2ang(nside, pix)

    thetaquat = qa.rotation(YAXIS, theta)
    phiquat = qa.rotation(ZAXIS, phi)
    quat = qa.mult(phiquat, thetaquat)

    dipoler = Dipoler(freq=0)
    dipo = dipoler.dipole(quat) * 1e3
    hp.mollview(dipo, title='Solar system dipole, freq=0', unit='mK')
    plt.gca().graticule(30)

    plt.figure(figsize=[20, 6])
    plt.suptitle('Doppler quadrupole')
    for ifreq, freq in enumerate([0, 30, 44, 70, 100, 143, 217, 353, 545,
                                  857]):
        dipoler = Dipoler(freq=freq)
        dipo = dipoler.dipole(quat) * 1e6
        quad = hp.remove_dipole(dipo)
        hp.mollview(quad,
                    title='{}GHz, q={:.3f}, P-to-P={:.1f}uK'.format(