Пример #1
0
    def get_4piconv(self, ch, theta, phi, psi, horn_pointing=False):
        l.info('Computing dipole temperature with 4pi convolver')
        vel = qarray.amplitude(self.satellite_v).flatten()
        beta = vel / physcon.c
        gamma = 1./np.sqrt(1-beta**2)
        unit_vel = self.satellite_v/vel[:,None]
        if horn_pointing: # psi comes from the S channel, so there is no need 
        # to remove psi_pol
            psi_nopol = psi
        else:
            # remove psi_pol
            psi_nopol = psi - np.radians(ch.get_instrument_db_field("psi_pol"))
        # rotate vel to ecliptic
        # phi around z
        #ecl_rotation = qarray.rotation([0,0,1], -phi)
        # theta around y
        ecl_rotation = qarray.norm( qarray.mult(
            qarray.rotation([0,0,1], -psi_nopol) ,
            qarray.mult(
                        qarray.rotation([0,1,0], -theta) , 
                        qarray.rotation([0,0,1], -phi)
                       )
            ))
        # psi around z
        #ecl_rotation = qarray.mult(qarray.rotation([0,0,1], -psi) , ecl_rotation)
        # vel in beam ref frame
        vel_rad = qarray.rotate(ecl_rotation, unit_vel)

        cosdir = qarray.arraylist_dot(vel_rad, self.beam_sum[ch.tag]).flatten()
        #return beta * cosdir * T_CMB
        return (1. / ( gamma * (1 - beta * cosdir ) ) - 1) * T_CMB
Пример #2
0
def angles2siam(theta, phi, psi):
    mat_spin2boresight=qarray.rotation([0,1,0], np.pi/2-SPIN2BORESIGHT)

    mat_theta_phi = qarray.rotation([-math.sin(phi),math.cos(phi),0], theta)
    mat_psi = qarray.rotation([0,0,1], psi)
    # detector points to X axis
    total = qarray.mult(mat_spin2boresight, qarray.mult(mat_theta_phi, mat_psi))
    # siam is defined as pointing to Z axis
    return np.dot(qarray.to_rotmat(total[0]), np.array([[0,0,1],[0,1,0],[1,0,0]]))
Пример #3
0
def boresight_sim(nsim=1000, qprec=None, samplerate=23.0, spinperiod=10.0, spinangle=30.0, precperiod=93.0, precangle=65.0):

    spinrate = 1.0 / (60.0 * spinperiod)
    spinangle = spinangle * np.pi / 180.0
    precrate = 1.0 / (60.0 * precperiod)
    precangle = precangle * np.pi / 180.0

    xaxis = np.array([1,0,0], dtype=np.float64)
    yaxis = np.array([0,1,0], dtype=np.float64)
    zaxis = np.array([0,0,1], dtype=np.float64)

    satrot = None
    if qprec is None:
        satrot = np.tile(qa.rotation(np.array([0.0, 1.0, 0.0]), np.pi/2), nsim).reshape(-1,4)
    elif qprec.flatten().shape[0] == 4:
        satrot = np.tile(qprec, nsim).reshape(-1,4)
    elif qprec.shape == (nsim, 4):
        satrot = qprec
    else:
        raise RuntimeError("qprec has wrong dimensions")

    # Time-varying rotation about precession axis.  
    # Increment per sample is
    # (2pi radians) X (precrate) / (samplerate)
    # Construct quaternion from axis / angle form.
    precang = np.arange(nsim, dtype=np.float64)
    precang *= 2.0 * np.pi * precrate / samplerate

    # (zaxis, precang)
    cang = np.cos(0.5 * precang)
    sang = np.sin(0.5 * precang)
    precaxis = np.multiply(sang.reshape(-1,1), np.tile(zaxis, nsim).reshape(-1,3))
    precrot = np.concatenate((precaxis, cang.reshape(-1,1)), axis=1)

    # Rotation which performs the precession opening angle
    precopen = qa.rotation(np.array([1.0, 0.0, 0.0]), precangle)

    # Time-varying rotation about spin axis.  Increment 
    # per sample is
    # (2pi radians) X (spinrate) / (samplerate)
    # Construct quaternion from axis / angle form.
    spinang = np.arange(nsim, dtype=np.float64)
    spinang *= 2.0 * np.pi * spinrate / samplerate

    cang = np.cos(0.5 * spinang)
    sang = np.sin(0.5 * spinang)
    spinaxis = np.multiply(sang.reshape(-1,1), np.tile(zaxis, nsim).reshape(-1,3))
    spinrot = np.concatenate((spinaxis, cang.reshape(-1,1)), axis=1)

    # Rotation which performs the spin axis opening angle
    spinopen = qa.rotation(np.array([1.0, 0.0, 0.0]), spinangle)

    # compose final rotation
    boresight = qa.mult(satrot, qa.mult(precrot, qa.mult(precopen, qa.mult(spinrot, spinopen))))

    return boresight
Пример #4
0
def ptcor(obt, ptcorfile):
    # Boresight rotation of 85 degrees in order to get in inscan-xscan reference frame
    q_str_LOS = qarray.rotation(np.array([0,1,0]), np.radians(90-85))

    # read variable correction for current OD from file
    delta_inscan, delta_xscan = read_ptcor(obt, ptcorfile)

    # rotation in inscan-xscan reference frame
    qcor = qarray.mult(
            qarray.rotation(np.array([0,1,0]), delta_xscan),
            qarray.rotation(np.array([1,0,0]), delta_inscan)
            )

    qcor_tot = qarray.mult(q_str_LOS, qarray.mult(qcor, qarray.inv(q_str_LOS)))
    return qcor_tot
Пример #5
0
def quaternion_ecl2gal(qsat):
    '''Convert array of quaternions from Ecliptic to Galactic'''
    l.info('Rotating to Galactic frame')
    qsatgal = qarray.mult(QECL2GAL ,qsat)
    # renormalizing to unity
    qarray.norm_inplace(qsatgal)
    return qsatgal
Пример #6
0
 def process_message(self, msg):
     # read a Dragonfly message
     msg_type = msg.GetHeader().msg_type
     dest_mod_id = msg.GetHeader().dest_mod_id
     if msg_type == MT_EXIT:
         if (dest_mod_id == 0) or (dest_mod_id == self.mod.GetModuleID()):
             print 'Received MT_EXIT, disconnecting...'
             self.mod.SendSignal(rc.MT_EXIT_ACK)
             self.mod.DisconnectFromMMM()
             return
     elif msg_type == rc.MT_PING:
         respond_to_ping(self.mod, msg, 'PlotHead')
     elif msg_type == rc.MT_POLARIS_POSITION:
         in_mdf = rc.MDF_POLARIS_POSITION()
         copy_from_msg(in_mdf, msg)
         positions = np.asarray(in_mdf.xyz[:])
         orientations = self.shuffle_q(np.asarray(in_mdf.ori[:]))
         if in_mdf.tool_id == (self.pointer + 1):
             Qf = qa.norm(orientations)
             Qr = qa.mult(Qf, qa.inv(self.pointer_Qi)).flatten()
             #find_nans(self.store_head, Qr, 'Qr')
             Tk = positions
             #find_nans(self.store_head, Tk, 'Tk')
             tip_pos = (qa.rotate(Qr, self.pointer_Xi) + Tk).flatten()
             self.pointer_position = np.append(self.pointer_position,
                                               (tip_pos[np.newaxis, :]),
                                               axis=0)
             #self.pl.reset(x=self.pointer_position[:,0], y=self.pointer_position[:,1], z=self.pointer_position[:,2])
             print("old=", tip_pos)
             print("new=", self.tp.get_pos(orientations, positions)[0])
Пример #7
0
def ang_to_quat(offsets):
    """Convert cartesian angle offsets and rotation into quaternions.

    Each offset contains two angles specifying the distance from the Z axis
    in orthogonal directions (called "X" and "Y").  The third angle is the
    rotation about the Z axis.  A quaternion is computed that first rotates
    about the Z axis and then rotates this axis to the specified X/Y angle
    location.

    Args:
        offsets (list of arrays):  Each item of the list has 3 elements for
            the X / Y angle offsets in radians and the rotation in radians
            about the Z axis.

    Returns:
        (list): List of quaternions, one for each item in the input list.

    """
    out = list()

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

    for off in offsets:
        angrot = qa.rotation(zaxis, off[2])
        wx = np.sin(off[0])
        wy = np.sin(off[1])
        wz = np.sqrt(1.0 - (wx * wx + wy * wy))
        wdir = np.array([wx, wy, wz])
        posrot = qa.from_vectors(zaxis, wdir)
        out.append(qa.mult(posrot, angrot))

    return out
Пример #8
0
def wobble(obt, wobble_psi2_model=get_wobble_psi2_maris, offset=0):
    """Gets array of OBT and returns an array of quaternions"""

    R_psi1 = qarray.inv(qarray.rotation([0,0,1], private.WOBBLE_DX7['psi1_ref']))
    R_psi2 = qarray.inv(qarray.rotation([0,1,0], private.WOBBLE_DX7['psi2_ref']))

    psi2 = wobble_psi2_model(obt) - offset
    R_psi2T = qarray.rotation([0,1,0], psi2)

    wobble_rotation = qarray.mult(qarray.inv(R_psi1),
                            qarray.mult(R_psi2T , 
                                qarray.mult(R_psi2 , R_psi1)
                            )
                        )

    #debug_here()
    return wobble_rotation
Пример #9
0
    def process_message(self, in_msg):

        msg_type = in_msg.GetHeader().msg_type
        if msg_type == rc.MT_POLARIS_POSITION:
            # handling input message
            in_mdf = rc.MDF_POLARIS_POSITION()
            copy_from_msg(in_mdf, in_msg)
            positions = np.array(in_mdf.xyz[:])
            orientations = self.shuffle_q(np.array(in_mdf.ori[:]))

            # np.testing.assert_array_equal(positions[:,0], orientations[:,0], err_msg='Samples are not aligned')

            if self.calibrated:
                if in_mdf.tool_id == (self.marker + 1):
                    # calculating output
                    self.Qk = qa.norm(
                        orientations
                    )  # need to find a way to discriminate the tools files in the messages???
                    Qr = qa.mult(self.Qk, qa.inv(self.Qi)).flatten()
                    Tk = positions
                    hotspot_position = (qa.rotate(Qr, self.Xi) + Tk).flatten()
                    hotspot_vector_head = qa.rotate(Qr, plate_vector)
                    if np.any(np.isnan(hotspot_position)) == True:
                        print "x",
                        # print '         *****nan present, check coil is within frame!*****'

                    # creating output message
                    out_mdf = rc.MDF_HOTSPOT_POSITION()
                    out_mdf.xyz[:] = hotspot_position
                    out_mdf.ori[:3] = hotspot_vector_head  # Qk - coil active orientation
                    out_mdf.sample_header = in_mdf.sample_header
                    msg = CMessage(rc.MT_HOTSPOT_POSITION)
                    copy_to_msg(out_mdf, msg)
                    self.mod.SendMessage(msg)
                    sys.stdout.write("o")

            else:
                if np.any(np.isnan(positions)) == True:
                    raise Exception, "nan present"
                if np.any(np.isnan(orientations)) == True:
                    raise Exception, "nan present"
                if (
                    (self.store_plate >= self.store_plate_pos.shape[0])
                    & (self.store_plate >= self.store_plate_ori.shape[0])
                    & (self.store_coil >= self.store_coil_pos.shape[0])
                    & (self.store_coil >= self.store_coil_ori.shape[0])
                ):
                    self.calibrating = False
                    self.make_calibration_vector()
                elif in_mdf.tool_id == (self.marker + 1):
                    self.store_coil_pos[self.store_coil, :] = positions
                    self.store_coil_ori[self.store_coil, :] = orientations
                    self.store_coil += 1
                elif in_mdf.tool_id == (self.plate + 1):
                    self.store_plate_pos[self.store_plate, :] = positions
                    self.store_plate_ori[self.store_plate, :] = orientations
                    self.store_plate += 1
Пример #10
0
def ahf_wobble(obt):
    """Pointing period by pointing period correction for psi1 and psi2 from
    the AHF observation files"""

    R_psi1 = qarray.inv(qarray.rotation([0,0,1], private.WOBBLE['psi1_ref']))
    R_psi2 = qarray.inv(qarray.rotation([0,1,0], private.WOBBLE['psi2_ref']))

    psi1, psi2 = get_ahf_wobble(obt)

    R_psi2T = qarray.rotation([0,1,0], psi2)
    R_psi1T = qarray.rotation([0,0,1], psi1)

    wobble_rotation = qarray.mult(R_psi1T,
                            qarray.mult(R_psi2T , 
                                qarray.mult(R_psi2 , R_psi1)
                            )
                        )

    return wobble_rotation
Пример #11
0
 def interp_get(self, rad):
     '''Interpolation after rotation to gal frame'''
     from Quaternion import Quat
     l.info('Rotating to detector %s' % rad)
     siam_quat = Quat(self.siam.get(rad)).q
     totquat = qarray.mult(self.qsatgal_interp, siam_quat)
     totquat_interp = qarray.nlerp(self.obt, self.ahfobt, totquat)
     x = np.array([1, 0, 0])
     vec = qarray.rotate(totquat_interp, x)
     l.info('Rotated to detector %s' % rad)
     return vec
Пример #12
0
def sim2(fp, freq, borequats, hwpang, hits, alps, inpp=None, hwprate=88.0, outdir = ''):

    nsim = borequats.shape[0]
    nhpix = hits.shape[0]
    nside = int(np.sqrt(nhpix / 12))

    if nhpix != 12*nside*nside:
        raise RuntimeError('invalid healpix nside value')
    if hwpang.shape[0] != borequats.shape[0]:
        raise RuntimeError('HWP angle vector must be same length as boresight quaternions')
    if inpp is not None:
        if inpp.shape[0] != nhpix:
            raise RuntimeError('N_pp^-1 number of pixels must match N_hits')
        if inpp.shape[1] != 6:
            raise RuntimeError('N_pp^-1 must have 6 elements per pixel')

    xaxis = np.array([1,0,0], dtype=np.float64)
    yaxis = np.array([0,1,0], dtype=np.float64)
    zaxis = np.array([0,0,1], dtype=np.float64)

    # generate hitcount map and alpha
    for i, det in enumerate(fp.detectors(freq=freq)):

        detrot = qa.mult(borequats, fp.quat(det))
        detdir = qa.rotate(detrot, np.tile(zaxis, nsim).reshape(-1,3))
        dettheta, detphi = hp.vec2ang(detdir)
        detpix = hp.vec2pix(nside, detdir[:,0], detdir[:,1], detdir[:,2])
        detbinned = np.bincount(detpix)
        hits[0:detbinned.shape[0]] += detbinned[:]

        outfile = os.path.join(outdir, 'theta.bin')
        with open(outfile, 'wb') as f:
            dettheta.tofile(f)
        outfile = os.path.join(outdir, 'phi.bin')
        with open(outfile, 'wb') as f:
            detphi.tofile(f)
        outfile = os.path.join(outdir, 'pix.bin')
        with open(outfile, 'wb') as f:
            detpix.tofile(f)

        if np.mod(i,2)!=1: 
            alpdir = qa.rotate(detrot, np.tile(xaxis, nsim).reshape(-1,3))
            x = alpdir[:,0]*detdir[:,1] - alpdir[:,1]*detdir[:,0]
            y = alpdir[:,0]*(-detdir[:,2]*detdir[:,0]) + alpdir[:,1]*(-detdir[:,2]*detdir[:,1]) + alpdir[:,2]*(detdir[:,0]*detdir[:,0]+detdir[:,1]*detdir[:,1])        
            angle = np.arctan2(y,x)

            outfile = os.path.join(outdir, 'angle.bin')
            with open(outfile, 'wb') as f:
                angle.tofile(f)
Пример #13
0
    def get_4piconv_dx10(self, ch, theta, phi, psi):
        l.info('Computing dipole temperature with 4pi convolver')
        rel_vel = self.satellite_v/physcon.c
        # remove psi_pol
        psi_nopol = psi - np.radians(ch.get_instrument_db_field("psi_pol"))
        # rotate vel to horn reference frame
        tohorn_rotation = qarray.norm( qarray.mult(
            qarray.rotation([0,0,1], -psi_nopol) ,
            qarray.mult(
                        qarray.rotation([0,1,0], -theta) , 
                        qarray.rotation([0,0,1], -phi)
                       )
            ))
        # vel in beam ref frame
        vel_rad = qarray.rotate(tohorn_rotation, rel_vel)

        dipole_amplitude = self.get_fourpi_prod(vel_rad, ["S100", "S010", "S001"], ch)

        # relative corrections
        dipole_amplitude += vel_rad[:,0] * self.get_fourpi_prod(vel_rad, ["S200", "S110", "S101"], ch)/2
        dipole_amplitude += vel_rad[:,1] * self.get_fourpi_prod(vel_rad, ["S110", "S020", "S011"], ch)/2
        dipole_amplitude += vel_rad[:,2] * self.get_fourpi_prod(vel_rad, ["S101", "S011", "S002"], ch)/2

        return dipole_amplitude * T_CMB
Пример #14
0
def triangle(npos, width, rotate=None):
    """Compute positions in an equilateral triangle layout.
        
        Args:
        npos (int): The number of positions packed onto wafer=3
        width (float): distance between tubes in degrees
        rotate (array, optional): Optional array of rotation angles in degrees
        to apply to each position.
        
        Returns:
        (array): Array of quaternions for the positions.
        
        """
    zaxis = np.array([0, 0, 1], dtype=np.float64)
    sixty = np.pi / 3.0
    thirty = np.pi / 6.0
    rtthree = np.sqrt(3.0)
    rtthreebytwo = 0.5 * rtthree

    tubedist = width * np.pi / 180.0
    result = np.zeros((npos, 4), dtype=np.float64)
    posangarr = np.array([sixty * 3.0 + thirty, -thirty, thirty * 3.0])
    for pos in range(npos):
        posang = posangarr[pos]
        posdist = tubedist / rtthree

        posx = np.sin(posdist) * np.cos(posang)
        posy = np.sin(posdist) * np.sin(posang)
        posz = np.cos(posdist)
        posdir = np.array([posx, posy, posz], dtype=np.float64)
        norm = np.sqrt(np.dot(posdir, posdir))
        posdir /= norm
        posrot = qa.from_vectors(zaxis, posdir)

        if rotate is None:
            result[pos] = posrot
        else:
            prerot = qa.rotation(zaxis, rotate[pos] * np.pi / 180.0)
            result[pos] = qa.mult(posrot, prerot)

    return result
            # Rotation is a rotation with respect to the `z` axis

            # In[ ]:

            rotation_speed = np.radians(-1 * 360/60)
            az = rotation_speed * (target_ut_h * 3600.) % (2*np.pi)


            q_rotation = qa.rotation(z, az)


            # We compose the rotations

            direction = qa.rotate(
                qa.mult(qfull, qa.mult(q_rotation, q_elev)),
                        z)


            lon, lat= hp.vec2dir(direction[:,0], direction[:,1], direction[:,2], lonlat=True)


            # ### Hitmap

            pix = hp.vec2pix(NSIDE,direction[:,0], direction[:,1], direction[:,2] )

            hit += hp.ma(pix2map(pix, NSIDE))

        hit.mask = hit == 0
        hp.write_map('hitmap_%s_%d_opening.fits' % (LOCATION, OPENING_ANGLE),hit)
Пример #16
0
    dets = ["1A", "1B", "2A", "2B"]
    detstring = dets2detstring(dets)

    ndet = len(dets)

    spin_period_seconds = 60

    x_axis, y_axis, z_axis = np.eye(3)

    spin_ang_speed = 2 * np.pi / spin_period_seconds

    spin_angles = (timestamps * spin_ang_speed) % (2 * np.pi)

    rot_opening = qa.rotation(z_axis, -np.radians(10))

    rot_spin = qa.mult(qa.rotation(x_axis, spin_angles), rot_opening)
    bore_v = qa.rotate(rot_spin, x_axis)
    pix_1det = hp.vec2pix(nside,
                          bore_v[:, 0],
                          bore_v[:, 1],
                          bore_v[:, 2],
                          nest=True)

    pixels = np.tile(pix_1det, ndet)
    del pix_1det, bore_v, rot_spin

    pars = {}
    pars["base_first"] = 60.0
    pars["fsample"] = fsample
    pars["nside_map"] = nside
    pars["nside_cross"] = nside // 2
Пример #17
0
 def get_pos(self, Qk, Tk):
     Qk = qa.norm(Qk)
     Qr = (qa.mult(Qk, qa.inv(self.Qi))).flatten()
     pos = (qa.rotate(Qr, self.Xi)).flatten() + Tk
     return pos, Qr
Пример #18
0
    def __init__(
        self,
        obt,
        coord="G",
        horn_pointing=False,
        deaberration=True,
        wobble=True,
        interp="slerp",
        siamfile=None,
        wobble_offset=0,
        ptcorfile=None,
        Pxx=False,
        instrument_db=None,
    ):
        """
        nointerp to use the AHF OBT stamps"""
        l.warning("Pointing setup, coord:%s, deab:%s, wobble:%s" % (coord, deaberration, wobble))
        # get ahf limits
        self.Pxx = Pxx
        self.deaberration = deaberration
        self.wobble = wobble

        filenames = AHF_btw_OBT(obt)
        files = [pycfitsio.open(f) for f in filenames]
        l.debug("reading files %s" % str(files))
        AHF_data_iter = [f[0] for f in files]

        l.debug("reading files")

        ahf_obt = np.concatenate([h.read_column("OBT_SPL") for h in AHF_data_iter])
        ahf_obt /= 2.0 ** 16
        i_start = max(ahf_obt.searchsorted(obt[0]) - 1, 0)
        i_end = min(ahf_obt.searchsorted(obt[-1]) + 1, len(ahf_obt) - 1)
        ahf_obt = ahf_obt[i_start:i_end]

        ahf_quat = np.empty((len(ahf_obt), 4))
        for i, c in enumerate(self.comp):
            ahf_quat[:, i] = np.concatenate([h.read_column("QUATERNION_" + c) for h in AHF_data_iter])[i_start:i_end]

        # debug_here()
        if self.wobble:
            # ahf_quat = qarray.mult(ahf_quat, correction.wobble(ahf_obt,offset=wobble_offset))
            # DX8 wobble angle correction
            wob = correction.ahf_wobble(ahf_obt)
            ahf_quat = qarray.mult(ahf_quat, wob)
            # print(wob[17320:17335])
            # print(ahf_obt[17329])
            # 34690:34705
            qarray.norm_inplace(ahf_quat)

        if ptcorfile == True:
            ptcorfile = private.ptcorfile
        if ptcorfile:
            ahf_quat = qarray.mult(ahf_quat, correction.ptcor(ahf_obt, ptcorfile))

        if coord == "G":
            ahf_quat = quaternion_ecl2gal(ahf_quat)

        if interp is None:
            self.qsatgal_interp = ahf_quat
            # save AHF obt for later interpolation
            self.ahf_obt = ahf_obt
        else:
            l.info("Interpolating quaternions with %s" % interp)
            interpfunc = getattr(qarray, interp)
            self.qsatgal_interp = interpfunc(obt, ahf_obt, ahf_quat)

        # if self.wobble:
        #    self.qsatgal_interp = qarray.mult(self.qsatgal_interp, correction.wobble(obt))
        #    qarray.norm_inplace(self.qsatgal_interp)

        l.info("Quaternions interpolated")
        self.siam = IDBSiam(instrument_db, obt, self.Pxx)

        self.obt = obt
        self.coord = coord

        l.debug("Closing AHF files")
        for f in files:
            f.close()
    x_axis, y_axis, z_axis = np.eye(3)

    # Earth

    angle_each_day = np.radians(360 / 365.25)
    angles = timestamps * angle_each_day / 3600 / 24
    rot_earth_orbit = qa.rotation(z_axis, angles)

    # Precession

    prec_period_seconds = 1 * 3600
    prec_ang_speed = 2 * np.pi / prec_period_seconds
    rot_prec_opening = qa.rotation(z_axis, -np.radians(40))
    prec_angles = (timestamps * prec_ang_speed) % (2 * np.pi)
    rot_prec = qa.mult(qa.rotation(x_axis, prec_angles), rot_prec_opening)

    # Spin

    spin_period_seconds = 60
    spin_ang_speed = 2 * np.pi / spin_period_seconds
    spin_angles = (timestamps * spin_ang_speed) % (2 * np.pi)
    rot_opening = qa.rotation(z_axis, -np.radians(10))
    rot_spin = qa.mult(qa.rotation(x_axis, spin_angles), rot_opening)

    # Total quaternions to boresight

    bore_quat = qa.norm(qa.mult(rot_earth_orbit, qa.mult(rot_prec, rot_spin)))

    bore_v = qa.rotate(bore_quat, x_axis)
    pix_1det = hp.vec2pix(nside,
 def test_mult_onequaternion(self):
     my_mult_result = qarray.mult(self.q1, self.q2)
     self.assertEquals( my_mult_result.shape[0], 1)
     self.assertEquals( my_mult_result.shape[1], 4)
     np.testing.assert_array_almost_equal(my_mult_result , self.mult_result)
Пример #21
0
def sim_telescope_detectors(hw, tele, tubes=None):
    """Generate detector properties for a telescope.

    Given a Hardware model, generate all detector properties for the specified
    telescope and optionally a subset of optics tubes (for the LAT).

    Args:
        hw (Hardware): The hardware object to use.
        tele (str): The telescope name.
        tubes (list, optional): The optional list of tubes to include.

    Returns:
        (OrderedDict): The properties of all selected detectors.

    """
    zaxis = np.array([0, 0, 1], dtype=np.float64)
    thirty = np.pi / 6.0
    # The properties of this telescope
    teleprops = hw.data["telescopes"][tele]
    platescale = teleprops["platescale"]
    fwhm = teleprops["fwhm"]

    # The tubes
    alltubes = teleprops["tubes"]
    ntube = len(alltubes)
    if tubes is None:
        tubes = alltubes
    else:
        for t in tubes:
            if t not in alltubes:
                raise RuntimeError(
                    "Invalid tube '{}' for telescope '{}'".format(t, tele))

    alldets = OrderedDict()
    if ntube == 1:
        # This is a SAT.  We have one tube at the center.
        tubeprops = hw.data["tubes"][tubes[0]]
        waferspace = tubeprops["waferspace"]

        shift = waferspace * platescale * np.pi / 180.0
        wcenters = [
            np.array([0.0, 0.0, 0.0]),
            np.array([shift * np.cos(thirty), shift * np.sin(thirty), 0.0]),
            np.array([0.0, shift, 0.0]),
            np.array([-shift * np.cos(thirty), shift * np.sin(thirty), 0.0]),
            np.array([-shift * np.cos(thirty), -shift * np.sin(thirty), 0.0]),
            np.array([0.0, -shift, 0.0]),
            np.array([shift * np.cos(thirty), -shift * np.sin(thirty), 0.0])
        ]
        centers = ang_to_quat(wcenters)

        windx = 0
        for wafer in tubeprops["wafers"]:
            dets = sim_wafer_detectors(hw,
                                       wafer,
                                       platescale,
                                       fwhm,
                                       center=centers[windx])
            alldets.update(dets)
            windx += 1
    else:
        # This is the LAT.  Compute the tube centers.
        # Rotate each tube by 90 degrees, so that it is pointed "down".
        tubespace = teleprops["tubespace"]
        tuberot = 90.0 * np.ones(19, dtype=np.float64)
        tcenters = hex_layout(19, 4 * (tubespace * platescale), rotate=tuberot)

        tindx = 0
        for tube in tubes:
            tubeprops = hw.data["tubes"][tube]
            waferspace = tubeprops["waferspace"]
            location = tubeprops["location"]

            wradius = 0.5 * (waferspace * platescale * np.pi / 180.0)
            wcenters = [
                np.array([np.tan(thirty) * wradius, wradius, 0.0]),
                np.array([-wradius / np.cos(thirty), 0.0, 0.0]),
                np.array([np.tan(thirty) * wradius, -wradius, 0.0])
            ]
            qwcenters = ang_to_quat(wcenters)
            centers = list()
            for qwc in qwcenters:
                centers.append(qa.mult(tcenters[location], qwc))

            windx = 0
            for wafer in tubeprops["wafers"]:
                dets = sim_wafer_detectors(hw,
                                           wafer,
                                           platescale,
                                           fwhm,
                                           center=centers[windx])
                alldets.update(dets)
                windx += 1
            tindx += 1
    return alldets
 def test_mult_qarray(self):
     dim = (3, 1)
     qarray1 = np.tile(self.q1, dim)
     qarray2 = np.tile(self.q2, dim)
     my_mult_result = qarray.mult(qarray1, qarray2)
     np.testing.assert_array_almost_equal(my_mult_result , np.tile(self.mult_result,dim))
Пример #23
0
def sim_wafer_detectors(hw,
                        wafer,
                        platescale,
                        fwhm,
                        band=None,
                        center=np.array([0, 0, 0, 1], dtype=np.float64)):
    """Generate detector properties for a wafer.

    Given a Hardware configuration, generate all detector properties for
    the specified wafer and optionally only the specified band.

    Args:
        hw (Hardware): The hardware properties.
        wafer (str): The wafer name.
        platescale (float): The plate scale in degrees / mm.
        fwhm (dict): Dictionary of nominal FWHM values in arcminutes for
            each band.
        band (str, optional): Optionally only use this band.
        center (array, optional): The quaternion offset of the center.

    Returns:
        (OrderedDict): The properties of all selected detectors.

    """
    # The properties of this wafer
    wprops = hw.data["wafers"][wafer]
    # The readout card and its properties
    card = wprops["card"]
    cardprops = hw.data["cards"][card]
    # The bands
    bands = wprops["bands"]
    if band is not None:
        if band in bands:
            bands = [band]
        else:
            raise RuntimeError("band '{}' not valid for wafer '{}'".format(
                band, wafer))

    # Lay out the pixel locations depending on the wafer type.  Also
    # compute the polarization orientation rotation, as well as the A/B
    # handedness for the Sinuous detectors.

    npix = wprops["npixel"]
    pixsep = platescale * wprops["pixsize"]
    layout_A = None
    layout_B = None
    handed = None
    kill = []
    if wprops["packing"] == "F":
        # Feedhorn (NIST style)
        gap = platescale * wprops["rhombusgap"]
        nrhombus = npix // 3
        # This dim is also the number of pixels along the short axis.
        dim = rhomb_dim(nrhombus)
        # This is the center-center distance along the short axis
        width = (dim - 1) * pixsep
        # The orientation within each rhombus alternates between zero and 45
        # degrees.  However there is an offset.  We choose this arbitrarily
        # for the nominal rhombus position, and then the rotation of the
        # other 2 rhombi will naturally modulate this.
        pol_A = np.zeros(nrhombus, dtype=np.float64)
        pol_B = np.zeros(nrhombus, dtype=np.float64)
        poloff = 22.5
        for p in range(nrhombus):
            # get the row / col of the pixel
            row, col = rhomb_row_col(nrhombus, p)
            if np.mod(row, 2) == 0:
                pol_A[p] = 0.0 + poloff
            else:
                pol_A[p] = 45.0 + poloff
            pol_B[p] = 90.0 + pol_A[p]
        # We are going to remove 2 pixels for mechanical reasons
        kf = dim * (dim - 1) // 2
        kill = [kf, kf + dim - 2]
        layout_A = rhombus_hex_layout(nrhombus,
                                      width,
                                      gap,
                                      rhombus_rotate=pol_A,
                                      killpix=kill)
        layout_B = rhombus_hex_layout(nrhombus,
                                      width,
                                      gap,
                                      rhombus_rotate=pol_B,
                                      killpix=kill)
    elif wprops["packing"] == "S":
        # Sinuous (Berkeley style)
        # This is the center-center distance along the vertex-vertex axis
        width = (2 * (hex_nring(npix) - 1)) * pixsep
        # The sinuous handedness is chosen so that A/B pairs of pixels have the
        # same nominal orientation but trail each other along the
        # vertex-vertex axis of the hexagon.  The polarization orientation
        # changes every other column
        handed = list()
        pol_A = np.zeros(npix, dtype=np.float64)
        pol_B = np.zeros(npix, dtype=np.float64)
        for p in range(npix):
            row, col = hex_row_col(npix, p)
            if np.mod(col, 2) == 0:
                handed.append("L")
            else:
                handed.append("R")
            if np.mod(col, 4) < 2:
                pol_A[p] = 0.0
            else:
                pol_A[p] = 45.0
            pol_B[p] = 90.0 + pol_A[p]
        layout_A = hex_layout(npix, width, rotate=pol_A)
        layout_B = hex_layout(npix, width, rotate=pol_B)
    else:
        raise RuntimeError("Unknown wafer packing '{}'".format(
            wprops["packing"]))

    # Now we go through each pixel and create the orthogonal detectors for
    # each band.
    dets = OrderedDict()

    chan_per_coax = cardprops["nchannel"] // cardprops["ncoax"]
    chan_per_bias = cardprops["nchannel"] // cardprops["nbias"]

    doff = 0
    p = 0
    idoff = int(wafer) * 10000
    for px in range(npix):
        if px in kill:
            continue
        pstr = "{:03d}".format(p)
        for b in bands:
            for pl, layout in zip(["A", "B"], [layout_A, layout_B]):
                dprops = OrderedDict()
                dprops["wafer"] = wafer
                dprops["ID"] = idoff + doff
                dprops["pixel"] = pstr
                dprops["band"] = b
                dprops["fwhm"] = fwhm[b]
                dprops["pol"] = pl
                if handed is not None:
                    dprops["handed"] = handed[p]
                # Made-up assignment to readout channels
                dprops["card"] = card
                dprops["channel"] = doff
                dprops["coax"] = doff // chan_per_coax
                dprops["bias"] = doff // chan_per_bias
                # Layout quaternion offset is from the origin.  Now we apply
                # the rotation of the wafer center.
                dprops["quat"] = qa.mult(center, layout[p]).flatten()
                dname = "{}_{}_{}_{}".format(wafer, pstr, b, pl)
                dets[dname] = dprops
                doff += 1
        p += 1

    return dets
Пример #24
0
def rhombus_hex_layout(rhombus_npos,
                       rhombus_width,
                       gap,
                       rhombus_rotate=None,
                       killpix=None):
    """
    Construct a hexagon from 3 rhombi.

    Args:
        rhombus_npos (int): The number of positions in one rhombus.
        rhombus_width (float): The angle (in degrees) subtended by the
            width of one rhombus along the X axis.
        gap (float): The gap between the edges of the rhombi, in degrees.
        rhombus_rotate (array, optional): An additional angle rotation of
            each position on each rhombus before the rhombus is rotated
            into place.
        killpix (list, optional): Pixel indices to remove for mechanical
            reasons.

    Returns:
        (dict): Keys are the hexagon position and values are quaternions.

    """
    sixty = np.pi / 3.0
    thirty = np.pi / 6.0

    # rhombus dim
    dim = rhomb_dim(rhombus_npos)

    # width in radians
    radwidth = rhombus_width * np.pi / 180.0

    # First layout one rhombus
    rquat = rhombus_layout(rhombus_npos, rhombus_width, rotate=rhombus_rotate)

    # angular separation of rhombi
    gap *= np.pi / 180.0

    # half-width of rhombus in radians
    halfwidth = 0.5 * radwidth

    # width of one pixel
    pixwidth = radwidth / (dim - 1)

    # Compute the individual rhombus centers.  This is the shift of origin
    # in the X direction for the "vertical" rhombus.
    shift = halfwidth + (0.5 * pixwidth) + ((0.5 * gap) / np.cos(thirty))

    centers = [
        np.array([shift, 0.0, 0.0]),
        np.array([-shift * np.cos(sixty), shift * np.sin(sixty), 2 * sixty]),
        np.array([-shift * np.cos(sixty), -shift * np.sin(sixty), 4 * sixty])
    ]
    qcenters = ang_to_quat(centers)

    nkill = len(killpix)
    result = np.zeros((3 * rhombus_npos - nkill, 4), dtype=np.float64)

    off = 0
    px = 0
    for qc in qcenters:
        for p in range(rhombus_npos):
            if px not in killpix:
                result[off] = qa.mult(qc, rquat[p])
                off += 1
            px += 1

    return result
Пример #25
0
def rhombus_layout(npos, width, rotate=None):
    """Compute positions in a hexagon layout.

    This particular rhombus geometry is essentially a third of a
    hexagon.  In other words the aspect ratio of the rhombus is
    constrained to have the long dimension be sqrt(3) times the short
    dimension.

    The rhombus is projected on the sphere and centered on the Z axis.
    The X axis is along the short direction.  The Y axis is along the longer
    direction.  For example::

                          O
        Y ^              O O
        |               O O O
        |              O O O O
        +--> X          O O O
                         O O
                          O

    Each position is numbered 0..npos-1.  The first position is at the
    "top", and then the positions are numbered moving downward and left to
    right.

    The extent of the rhombus is directly specified by the width parameter
    which is the angular extent along the X direction.

    Args:
        npos (int): The number of positions in the rhombus.
        width (float): The angle (in degrees) subtended by the width along
            the X axis.
        rotate (array, optional): Optional array of rotation angles in degrees
            to apply to each position.

    Returns:
        (array): Array of quaternions for the positions.

    """
    zaxis = np.array([0, 0, 1], dtype=np.float64)
    rtthree = np.sqrt(3.0)

    angwidth = width * np.pi / 180.0
    dim = rhomb_dim(npos)

    # find the angular packing size of one detector
    posdiam = angwidth / (dim - 1)

    result = np.zeros((npos, 4), dtype=np.float64)

    for pos in range(npos):
        posrow, poscol = rhomb_row_col(npos, pos)

        rowang = 0.5 * rtthree * ((dim - 1) - posrow) * posdiam
        relrow = posrow
        if posrow >= dim:
            relrow = (2 * dim - 2) - posrow
        colang = (float(poscol) - float(relrow) / 2.0) * posdiam
        distang = np.sqrt(rowang**2 + colang**2)
        zang = np.cos(distang)
        posdir = np.array([colang, rowang, zang], dtype=np.float64)
        norm = np.sqrt(np.dot(posdir, posdir))
        posdir /= norm

        posrot = qa.from_vectors(zaxis, posdir)

        if rotate is None:
            result[pos] = posrot
        else:
            prerot = qa.rotation(zaxis, rotate[pos] * np.pi / 180.0)
            result[pos] = qa.mult(posrot, prerot)

    return result
Пример #26
0
def sim_telescope_detectors(hw, tele, tubes=None):
    """Generate detector properties for a telescope.

    Given a Hardware model, generate all detector properties for the specified
    telescope and optionally a subset of optics tubes (for the LAT).

    Args:
        hw (Hardware): The hardware object to use.
        tele (str): The telescope name.
        tubes (list, optional): The optional list of tubes to include.

    Returns:
        (OrderedDict): The properties of all selected detectors.

    """
    zaxis = np.array([0, 0, 1], dtype=np.float64)
    thirty = np.pi / 6.0
    # The properties of this telescope
    teleprops = hw.data["telescopes"][tele]
    tele_platescale = teleprops["platescale"]
    fwhm = teleprops["fwhm"]

    # The tubes
    alltubes = teleprops["tubes"]
    ntube = len(alltubes)
    if tubes is None:
        tubes = alltubes
    else:
        for t in tubes:
            if t not in alltubes:
                raise RuntimeError(
                    "Invalid tube '{}' for telescope '{}'".format(t, tele))

    alldets = OrderedDict()
    if ntube == 3:
        # This is a SAT.  We have three tubes.
        tubespace = teleprops["tubespace"]
        #tuberot = 0.0 * np.ones(7, dtype=np.float64)
        #tcenters = hex_layout(7, 2 * (tubespace * tele_platescale), rotate=tuberot)
        # tuberot = 90.0 * np.ones(3, dtype=np.float64)
        # tcenters = triangle(3, (tubespace * tele_platescale), rotate=tuberot)

        tindx = 0
        for tube in tubes:
            tubeprops = hw.data["tubes"][tube]
            waferspace = tubeprops["waferspace"]
            platescale = tubeprops["platescale"]
            location = tubeprops["location"]
            type = tubeprops["type"]
            if type == "HFS":
                tuberot = 90.0 * np.ones(7, dtype=np.float64)
                tcenters = hex_layout(7,
                                      2 * (tubespace * tele_platescale),
                                      rotate=tuberot)
                srad = waferspace * platescale * np.pi / 180.0
                wcenters = [
                    np.array([-srad / (2. * np.cos(thirty)), 0.0, 0.0]),
                    np.array([srad / (4. * np.cos(thirty)), -srad / 2., 0.0]),
                    np.array([srad / (4. * np.cos(thirty)), srad / 2., 0.0]),
                    np.array([srad / (np.cos(thirty)), 0.0, 0.0]),
                    np.array([srad / (np.cos(thirty)), srad, 10 * thirty]),
                    np.array(
                        [srad / (4. * np.cos(thirty)), srad / 2. + srad, 0.0]),
                    np.array([-srad / (2. * np.cos(thirty)), srad, 0.0]),
                    np.array([
                        -5. * srad / (4. * np.cos(thirty)), srad / 2.,
                        2 * thirty
                    ]),
                    np.array([
                        -5. * srad / (4. * np.cos(thirty)), -srad / 2.,
                        4 * thirty
                    ]),
                    np.array([-srad / (2. * np.cos(thirty)), -srad, 0.0]),
                    np.array([
                        srad / (4. * np.cos(thirty)), -srad / 2. - srad,
                        6 * thirty
                    ]),
                    np.array([srad / (np.cos(thirty)), -srad, -4 * thirty]),
                ]
                qwcenters = ang_to_quat(wcenters)
                centers = list()
                for qwc in qwcenters:
                    centers.append(qa.mult(tcenters[location], qwc))

                windx = 0
                for wafer in tubeprops["wafers"]:
                    if windx == 4:
                        partial_type = "half"
                    elif windx == 5:
                        partial_type = "half"
                    elif windx == 7:
                        partial_type = "half"
                    elif windx == 8:
                        partial_type = "half"
                    elif windx == 10:
                        partial_type = "half"
                    elif windx == 11:
                        partial_type = "half"
                    else:
                        partial_type = None
                    dets = sim_wafer_detectors(
                        hw,
                        wafer,
                        platescale,
                        fwhm,
                        center=centers[windx],
                        partial_type=partial_type,
                    )
                    alldets.update(dets)
                    windx += 1
                tindx += 1
            else:
                tuberot = 0.0 * np.ones(7, dtype=np.float64)
                tcenters = hex_layout(7,
                                      2 * (tubespace * tele_platescale),
                                      rotate=tuberot)
                shift = waferspace * platescale * np.pi / 180.0
                wcenters = [
                    np.array([-shift / (2. * np.cos(thirty)), 0.0, 0.0]),
                    np.array([shift / (4. * np.cos(thirty)), -shift / 2.,
                              0.0]),
                    np.array([shift / (4. * np.cos(thirty)), shift / 2., 0.0]),
                    np.array([shift / (np.cos(thirty)), 0.0, 0.0]),
                    np.array([shift / (np.cos(thirty)), shift, 0.0]),
                    np.array([
                        shift / (4. * np.cos(thirty)), shift / 2. + shift, 0.0
                    ]),
                    np.array([-shift / (2. * np.cos(thirty)), shift, 0.0]),
                    np.array(
                        [-5. * shift / (4. * np.cos(thirty)), shift / 2.,
                         0.0]),
                    np.array([
                        -5. * shift / (4. * np.cos(thirty)), -shift / 2., 0.0
                    ]),
                    np.array([-shift / (2. * np.cos(thirty)), -shift, 0.0]),
                    np.array([
                        shift / (4. * np.cos(thirty)), -shift / 2. - shift, 0.0
                    ]),
                    np.array([shift / (np.cos(thirty)), -shift, 0.0]),
                ]
                qwcenters = ang_to_quat(wcenters)
                centers = list()
                for qwc in qwcenters:
                    centers.append(qa.mult(tcenters[location], qwc))

                windx = 0
                for wafer in tubeprops["wafers"]:
                    partial_type = None
                    dets = sim_wafer_detectors(
                        hw,
                        wafer,
                        platescale,
                        fwhm,
                        center=centers[windx],
                        partial_type=partial_type,
                    )
                    alldets.update(dets)
                    windx += 1
                tindx += 1
    else:
        # This is the LAT.  Compute the tube centers.
        # Rotate each tube by 90 degrees, so that it is pointed "down".
        tubespace = teleprops["tubespace"]
        tuberot = 90.0 * np.ones(91, dtype=np.float64)
        tcenters = hex_layout(91,
                              10 * (tubespace * tele_platescale),
                              rotate=tuberot)

        tindx = 0
        for tube in tubes:
            tubeprops = hw.data["tubes"][tube]
            waferspace = tubeprops["waferspace"]
            platescale = tubeprops["platescale"]
            location = tubeprops["location"]

            wradius = 0.5 * (waferspace * platescale * np.pi / 180.0)
            # get centers and rotations for arrays
            wcenters = [np.array([0.0, 0.0, 0.0])]
            qwcenters = ang_to_quat(wcenters)
            centers = list()
            for qwc in qwcenters:
                centers.append(qa.mult(tcenters[location], qwc))

            windx = 0
            for wafer in tubeprops["wafers"]:
                # For first three wafers, use whole wafers, then construct partial wafers
                dets = sim_wafer_detectors(
                    hw,
                    wafer,
                    platescale,
                    fwhm,
                    center=centers[windx],
                    partial_type=None,
                    no_gap=None,
                )
                alldets.update(dets)
                #windx += 1
            tindx += 1
    return alldets
Пример #27
0
def hex_layout(npos, width, rotate=None):
    """Compute positions in a hexagon layout.

    Place the given number of positions in a hexagonal layout projected on
    the sphere and centered at z axis.  The width specifies the angular
    extent from vertex to vertex along the "X" axis.  For example::

        Y ^             O O O
        |              O O O O
        |             O O + O O
        +--> X         O O O O
                        O O O

    Each position is numbered 0..npos-1.  The first position is at the center,
    and then the positions are numbered moving outward in rings.

    Args:
        npos (int): The number of positions packed onto wafer.
        width (float): The angle (in degrees) subtended by the width along
            the X axis.
        rotate (array, optional): Optional array of rotation angles in degrees
            to apply to each position.

    Returns:
        (array): Array of quaternions for the positions.

    """
    zaxis = np.array([0, 0, 1], dtype=np.float64)
    nullquat = np.array([0, 0, 0, 1], dtype=np.float64)
    sixty = np.pi / 3.0
    thirty = np.pi / 6.0
    rtthree = np.sqrt(3.0)
    rtthreebytwo = 0.5 * rtthree

    angdiameter = width * np.pi / 180.0

    # find the angular packing size of one detector
    nrings = hex_nring(npos)
    posdiam = angdiameter / (2 * nrings - 2)

    result = np.zeros((npos, 4), dtype=np.float64)

    for pos in range(npos):
        if pos == 0:
            # center position has no offset
            posrot = nullquat
        else:
            # Not at the center, find ring for this position
            test = pos - 1
            ring = 1
            while (test - 6 * ring) >= 0:
                test -= 6 * ring
                ring += 1
            sectors = int(test / ring)
            sectorsteps = np.mod(test, ring)

            # Convert angular steps around the ring into the angle and distance
            # in polar coordinates.  Each "sector" of 60 degrees is essentially
            # an equilateral triangle, and each step is equally spaced along
            # the edge opposite the vertex:
            #
            #          O
            #         O O (step 2)
            #        O   O (step 1)
            #       X O O O (step 0)
            #
            # For a given ring, "R" (center is R=0), there are R steps along
            # the sector edge.  The line from the origin to the opposite edge
            # that bisects this triangle has length R*sqrt(3)/2.  For each
            # equally-spaced step, we use the right triangle formed with this
            # bisection line to compute the angle and radius within this
            # sector.

            # The distance from the origin to the midpoint of the opposite
            # side.
            midline = rtthreebytwo * float(ring)

            # the distance along the opposite edge from the midpoint (positive
            # or negative)
            edgedist = float(sectorsteps) - 0.5 * float(ring)

            # the angle relative to the midpoint line (positive or negative)
            relang = np.arctan2(edgedist, midline)

            # total angle is based on number of sectors we have and the angle
            # within the final sector.
            posang = sectors * sixty + thirty + relang

            posdist = rtthreebytwo * posdiam * float(ring) / np.cos(relang)

            posx = np.sin(posdist) * np.cos(posang)
            posy = np.sin(posdist) * np.sin(posang)
            posz = np.cos(posdist)
            posdir = np.array([posx, posy, posz], dtype=np.float64)
            norm = np.sqrt(np.dot(posdir, posdir))
            posdir /= norm

            posrot = qa.from_vectors(zaxis, posdir)

        if rotate is None:
            result[pos] = posrot
        else:
            prerot = qa.rotation(zaxis, rotate[pos] * np.pi / 180.0)
            result[pos] = qa.mult(posrot, prerot)

    return result