def vis_marker_received(self, markerInfo): markerId, position, orientation = markerInfo.id, markerInfo.pose.position, markerInfo.pose.orientation # if int(markerId) not in self.allowed: return # print markerInfo.id px, py, pz = tuple([position.x, position.y, position.z]) # print distFromCamera position.z = np.cos(self.angle) * pz - np.sin(self.angle) * py position.y = np.sin(self.angle) * pz + np.cos(self.angle) * py q = (orientation.x, orientation.y, orientation.z, orientation.w) if q in self.cache: ori = self.cache[q] markerInfo.pose.orientation.x = ori[0] markerInfo.pose.orientation.y = ori[1] markerInfo.pose.orientation.z = ori[2] markerInfo.pose.orientation.w = ori[3] else: originalQuat = Q.Quat(Q.normalize(list(q))) quatOffsetFix = Q.Quat([0, 0, -1 * self.angle]) fixed = originalQuat * quatOffsetFix x, y, z, w = tuple(fixed._get_q()) markerInfo.pose.orientation.x = x markerInfo.pose.orientation.y = y markerInfo.pose.orientation.z = z markerInfo.pose.orientation.w = w self.cache[q] = [x, y, z, w] self.markerSeen = markerInfo
def calc_earth_vis(p_chandra_eci, chandra_att, planes=make_taco(), p_radiators=make_radiator(), earth_surface_grid=sphere_grid(4)): """Calculate the relative Earth visibility for the ACIS radiator given the Chandra orbit position ``p_chandra_eci`` and attitude ``chandra_att``. The relative visibility is normalized so that 1.0 represents the entire radiator seeing the full Earth at 100000 km. :param p_chandra_eci: Chandra orbital position [x, y, z] (meters) :param chandra_att: Chandra attitude [ra, dec, roll] (deg) :returns: relative visibility """ # Calculate position of earth in ECI and Chandra body coords # For T = attitude transformation matrix then p_body = T^-1 p_eci q_att = Quaternion.Quat(chandra_att) p_earth_body = np.dot(q_att.transform.transpose(), -p_chandra_eci) p_earth_surfaces = (p_earth_body + earth_surface_grid * Rad_Earth) # points that are visible to ACIS radiator behind_earth = [] visible = [] blocked = [] illum = 0. for p_radiator in p_radiators: for p_earth_surface in p_earth_surfaces: line = Line(p_radiator, p_earth_surface) if nearest_sphere_intersect(line.u, p_earth_body, Rad_Earth, line.len): for plane in planes: if plane_line_intersect(plane, line): # Blocked by SIM structure (aka space taco) blocked.append(p_earth_surface) break else: visible.append(p_earth_surface) # calculate projection of radiator normal [0,0,1] onto the vector # from radiator to p_earth_surface. illum += abs(line.u[2]) / line.len**2 # Do I need another cos(theta) term for Lambert's law?? YES else: behind_earth.append(p_earth_surface) illum *= 10000.**2 / (len(p_radiators) * len(p_earth_surfaces)) / 0.038 return visible, blocked, behind_earth, illum
def plot(obsid, mp_dir=None): sc = get_starcheck_catalog(obsid, mp_dir) quat = Quaternion.Quat((sc['obs']['point_ra'], sc['obs']['point_dec'], sc['obs']['point_roll'])) field = agasc.get_agasc_cone(sc['obs']['point_ra'], sc['obs']['point_dec'], radius=1.5, date=DateTime( sc['obs']['mp_starcat_time']).date) fig = plot_stars(catalog=sc['cat'], attitude=quat, stars=field, title="RA %.2f Dec %.2f" % (sc['obs']['point_ra'], sc['obs']['point_dec'])) return fig, sc['cat'], sc['obs']
def plot_stars(attitude, catalog=None, stars=None, title=None, starcat_time=None, red_mag_lim=None, quad_bound=True, grid=True, bad_stars=None, plot_keepout=False, ax=None, duration=0): """ Plot a catalog, a star field, or both in a matplotlib figure. If supplying a star field, an attitude must also be supplied. :param attitude: A Quaternion compatible attitude for the pointing :param catalog: Records describing catalog. Must be astropy table compatible. Required fields are ['idx', 'type', 'yang', 'zang', 'halfw'] :param stars: astropy table compatible set of agasc records of stars Required fields are ['RA_PMCORR', 'DEC_PMCORR', 'MAG_ACA', 'MAG_ACA_ERR']. If bad_acq_stars will be called (bad_stars is None), additional required fields ['CLASS', 'ASPQ1', 'ASPQ2', 'ASPQ3', 'VAR', 'POS_ERR'] If stars is None, stars will be fetched from the AGASC for the supplied attitude. :param title: string to be used as suptitle for the figure :param starcat_time: DateTime-compatible time. Used in ACASC fetch for proper motion correction. Not used if stars is not None. :param red_mag_lim: faint limit for field star plotting. :param quad_bound: boolean, plot inner quadrant boundaries :param grid: boolean, plot axis grid :param bad_stars: boolean mask on 'stars' of those that don't meet minimum requirements to be selected as acq stars. If None, bad_stars will be set by a call to bad_acq_stars(). :param plot_keepout: plot CCD area to be avoided in star selection (default=False) :param ax: matplotlib axes object to use (optional) :param duration: duration (starting at ``starcat_time``) for plotting planets (secs, default=0) :returns: matplotlib figure """ if stars is None: quat = Quaternion.Quat(attitude) stars = agasc.get_agasc_cone(quat.ra, quat.dec, radius=1.5, date=starcat_time) if bad_stars is None: bad_stars = bad_acq_stars(stars) if ax is None: fig = plt.figure(figsize=(5.325, 5.325)) fig.subplots_adjust(top=0.95) # Make an empty plot in row, col space ax = fig.add_subplot(1, 1, 1) else: fig = ax.get_figure() ax.set_aspect('equal') lim0, lim1 = -580, 590 plt.xlim(lim0, lim1) # Matches -2900, 2900 arcsec roughly plt.ylim(lim0, lim1) # plot the box and set the labels b1hw = 512 box1 = plt.Rectangle((b1hw, -b1hw), -2 * b1hw, 2 * b1hw, fill=False) ax.add_patch(box1) b2w = 520 box2 = plt.Rectangle((b2w, -b1hw), -4 + -2 * b2w, 2 * b1hw, fill=False) ax.add_patch(box2) ax.scatter(np.array([-2700, -2700, -2700, -2700, -2700]) / -5, np.array([2400, 2100, 1800, 1500, 1200]) / 5, c='orange', edgecolors='none', s=symsize(np.array([10.0, 9.0, 8.0, 7.0, 6.0]))) # Manually set ticks and grid to specified yag/zag values yz_ticks = [-2000, -1000, 0, 1000, 2000] zeros = [0, 0, 0, 0, 0] r, c = yagzag_to_pixels(yz_ticks, zeros) ax.set_xticks(r) ax.set_xticklabels(yz_ticks) r, c = yagzag_to_pixels(zeros, yz_ticks) ax.set_yticks(c) ax.set_yticklabels(yz_ticks) ax.grid() ax.set_xlabel("Yag (arcsec)") ax.set_ylabel("Zag (arcsec)") [label.set_rotation(90) for label in ax.get_yticklabels()] if quad_bound: ax.plot([-511, 511], [0, 0], color='magenta', alpha=0.4) ax.plot([0, 0], [-511, 511], color='magenta', alpha=0.4) if plot_keepout: # Plot grey area showing effective keep-out zones for stars. Back off on # outer limits by one pixel to improve rendered PNG slightly. row_pad = 15 col_pad = 8 box = plt.Rectangle((-511, -511), 1022, 1022, edgecolor='none', facecolor='black', alpha=0.2, zorder=-1000) ax.add_patch(box) box = plt.Rectangle((-512 + row_pad, -512 + col_pad), 1024 - row_pad * 2, 1024 - col_pad * 2, edgecolor='none', facecolor='white', zorder=-999) ax.add_patch(box) # Plot stars _plot_field_stars(ax, stars, attitude=attitude, bad_stars=bad_stars, red_mag_lim=red_mag_lim) # plot starcheck catalog if catalog is not None: _plot_catalog_items(ax, catalog) # Planets _plot_planets(ax, attitude, starcat_time, duration, lim0, lim1) if title is not None: ax.set_title(title, fontsize='small') return fig
def _plot_field_stars(ax, stars, attitude, red_mag_lim=None, bad_stars=None): """ Plot plot field stars in yang and zang on the supplied axes object in place. :param ax: matplotlib axes :param stars: astropy.table compatible set of records of agasc entries of stars :param attitude: Quaternion-compatible attitude :param red_mag_lim: faint limit :param bad_stars: boolean mask of stars to be plotted in red """ stars = Table(stars) quat = Quaternion.Quat(attitude) if bad_stars is None: bad_stars = np.zeros(len(stars), dtype=bool) if 'yang' not in stars.colnames or 'zang' not in stars.colnames: # Add star Y angle and Z angle in arcsec to the stars table. # radec2yagzag returns degrees. yags, zags = radec2yagzag(stars['RA_PMCORR'], stars['DEC_PMCORR'], quat) stars['yang'] = yags * 3600 stars['zang'] = zags * 3600 # Update table to include row/col values corresponding to yag/zag rows, cols = yagzag_to_pixels(stars['yang'], stars['zang'], allow_bad=True) stars['row'] = rows stars['col'] = cols # Initialize array of colors for the stars, default is black. Use 'object' # type to not worry in advance about string length and also for Py2/3 compat. colors = np.zeros(len(stars), dtype='object') colors[:] = 'black' colors[bad_stars] = BAD_STAR_COLOR if red_mag_lim: # Mark stars with the FAINT_STAR_COLOR if they have MAG_ACA # that is fainter than red_mag_lim but brighter than red_mag_lim # plus a rough mag error. The rough mag error calculation is # based on the SAUSAGE acq stage 1 check, which uses nsigma of # 3.0, a mag low limit of 1.5, and a random error of 0.26. nsigma = 3.0 mag_error_low_limit = 1.5 randerr = 0.26 caterr = stars['MAG_ACA_ERR'] / 100. error = nsigma * np.sqrt(randerr**2 + caterr**2) error = error.clip(mag_error_low_limit) # Faint and bad stars will keep their BAD_STAR_COLOR # Only use the faint mask on stars that are not bad colors[(stars['MAG_ACA'] >= red_mag_lim) & (stars['MAG_ACA'] < red_mag_lim + error) & ~bad_stars] = FAINT_STAR_COLOR # Don't plot those for which MAG_ACA is fainter than red_mag_lim + error # This overrides any that may be 'bad' colors[stars['MAG_ACA'] >= red_mag_lim + error] = 'none' size = symsize(stars['MAG_ACA']) # scatter() does not take an array of alphas, and rgba is # awkward for color='none', so plot these in a loop. for color, alpha in [(FAINT_STAR_COLOR, FAINT_STAR_ALPHA), (BAD_STAR_COLOR, BAD_STAR_ALPHA), ('black', 1.0)]: colormatch = colors == color ax.scatter(stars[colormatch]['row'], stars[colormatch]['col'], c=color, s=size[colormatch], edgecolor='none', alpha=alpha)
# Else there were no None's in the Q's so Calculate pitch and roll # Calculate the pitch and roll values from the specified Maneuver Quaternions # # First create an array of the 4 quaternions man_quat_array = np.array( [float(args.q1), float(args.q2), float(args.q3), float(args.q4)]) # Be sure the q's are normalized normd_q_list = qt.normalize(man_quat_array) # Create the Quat instance # Give it a try; if fail then set pitch and roll to 0.0 try: man_quat = qt.Quat(normd_q_list) # Worked ok so now calculate the pitch and roll pitch = Ska.Sun.pitch(man_quat.ra, man_quat.dec, str(args.event_time)) nom_roll = Ska.Sun.nominal_roll(man_quat.ra, man_quat.dec, str(args.event_time)) except ValueError: pitch = 0.0 nom_roll = 0.0 print "WARNING: The Quaternion set you gave me is not normalized. Can't use it. I've set the resultant pitch and roll to 0.0.\n BE SURE you do not run a model until you've entered reasonable values." # Append the header to the file eventfile.write( "#*******************************************************************************" ) eventfile.write("\n# Type: " + args.event_type)
def main(): global opt opt, args = get_options() tstart = DateTime(opt.tstart).secs tstop = DateTime(opt.tstop).secs # Get orbital ephemeris in requested time range print('Fetching ephemeris') ephem_x = fetch.MSID('orbitephem0_x', opt.tstart, opt.tstop) ephem_y = fetch.MSID('orbitephem0_y', opt.tstart, opt.tstop) ephem_z = fetch.MSID('orbitephem0_z', opt.tstart, opt.tstop) ephem_times = ephem_x.times.copy() # Get spacecraft attitude in requested time range at the same sampling as ephemeris print('Fetching attitude telemetry between {0} and {1}'.format( opt.tstart, opt.tstop)) qatts = fetch.MSIDset(['aoattqt1', 'aoattqt2', 'aoattqt3', 'aoattqt4'], opt.tstart, opt.tstop) #cols, atts = fetch(start=ephem.Time[0], stop=ephem.Time[-1], dt=dt, time_format='secs', # colspecs=) # atts = np.rec.fromrecords(atts, names=cols) q1s = qatts['aoattqt1'].vals[::opt.sample] q2s = qatts['aoattqt2'].vals[::opt.sample] q3s = qatts['aoattqt3'].vals[::opt.sample] q4s = qatts['aoattqt4'].vals[::opt.sample] q_times = qatts['aoattqt1'].times[::opt.sample] ephem_x_vals = Ska.Numpy.interpolate(ephem_x.vals, ephem_times, q_times) ephem_y_vals = Ska.Numpy.interpolate(ephem_y.vals, ephem_times, q_times) ephem_z_vals = Ska.Numpy.interpolate(ephem_z.vals, ephem_times, q_times) chandra_ecis = np.array([ephem_x_vals, ephem_y_vals, ephem_z_vals]).copy().transpose() if opt.movie: if len(atts) > 250: print "Error: movie option will produce more than 250 images. Change code if needed." sys.exit(0) if not os.path.exists(opt.out): os.makedirs(opt.out) # Divy up calculations amongst the n-processors i0s = range(0, len(q1s), len(q1s) // opt.nproc + 1) i1s = i0s[1:] + [len(q1s)] t0 = time.time() # Calculate illumination in a separate process over each sub-interval queues = [] procs = [] for iproc, i0, i1 in zip(itertools.count(), i0s, i1s): queue = Queue() proc = Process(target=calc_vis_values, args=(queue, iproc, q_times[i0:i1], chandra_ecis[i0:i1], q1s[i0:i1], q2s[i0:i1], q3s[i0:i1], q4s[i0:i1])) proc.start() procs.append(proc) queues.append(queue) # Join the results from each processor at the end outvals = [] for proc, queue in zip(procs, queues): outvals.extend(queue.get()) proc.join() print print 'calc_esa:', time.time() - t0 t0 = time.time() esa_directs = np.ndarray(len(q_times)) esa_refls = np.ndarray(len(q_times)) for i, t, q1, q2, q3, q4, x, y, z in zip(itertools.count(), q_times, q1s, q2s, q3s, q4s, ephem_x_vals, ephem_y_vals, ephem_z_vals): direct, refl, total = Chandra.acis_esa.earth_solid_angle( Quaternion.Quat([q1, q2, q3, q4]), np.array([x, y, z])) esa_directs[i] = direct esa_refls[i] = refl print 'calc_esa:', time.time() - t0 # Plot illumination versus date fig = plt.figure(1, figsize=(6, 4)) plt.clf() illum = np.rec.fromrecords( outvals, names=['time', 'direct', 'reflect', 'alt', 'q1', 'q2', 'q3', 'q4']) ticklocs, fig, ax = plot_cxctime(illum.time, illum.direct + illum.reflect, '-b') # plot_cxctime(illum.time, illum.reflect, '-r') # plot_cxctime(illum.time, illum.direct, '-r') # plot_cxctime(q_times, esa_directs, '-c') # plot_cxctime(q_times, esa_refls, '-m') plot_cxctime(q_times, esa_directs + esa_refls, '-r') ax.set_title('ACIS radiator illumination') ax.set_ylabel('Illumination (steradians)') filename = opt.out + '.png' fig.savefig(filename) print 'Create image file', filename # Write results to FITS table filename = opt.out + '.fits' Ska.Table.write_fits_table(opt.out + '.fits', illum) print 'Created FITS table', filename if opt.movie: print 'To make a movie run the following command:' print 'convert -delay 30 %s/*.png -loop 0 %s.gif' % (opt.out, opt.out)
def get_xray_data(obsids): for obsid in obsids: obsdir = "%s/auto/obs%05d" % (projdir, obsid) if not os.path.exists(obsdir): os.makedirs(obsdir) src_file = os.path.join(obsdir, 'picked_src.dat') obs_info = sqlaca.fetchone( "select * from observations where obsid = %d" % obsid) if obs_info is None: continue if not os.path.exists(src_file): src = find_obsid_src(obsid, obs_info) if src is None: continue else: src.write(src_file, format='ascii.tab') else: src = Table.read(src_file, format='ascii.tab') # cut-out region with a point source for this obsid point = '%s/point_source.fits' % obsdir #print point if (not os.path.exists(point) or ((os.stat(point).st_mtime < MTIME) and obs_info['instrume'] == 'HRC') or REDO): extract_point(obs_info, src, obsdir, point) obsid_src = point # periscope tilt telemetry if not os.path.exists(os.path.join(obsdir, 'tilt.pkl')) or REDO: obs = obs_info msids = [ 'OOBAGRD3', 'OOBAGRD6', 'OHRTHR42', 'OHRTHR43', 'OOBTHR39', 'OHRTHR24', '4RT702T', 'OHRTHR24', 'AACBPPT', 'AACH1T', 'AACCCDPT', 'AACBPRT' ] telem = fetch.MSIDset(msids, obs['tstart'] - 1000, obs['tstop'] + 1000) telemtime = telem['OOBAGRD3'].times tilt = dict(telem) tilt.update( dict(time=telemtime, tilt_axial=telem['OOBAGRD3'].vals, tilt_diam=telem['OOBAGRD6'].vals)) tilt_pick = open(os.path.join(obsdir, 'tilt.pkl'), 'w') cPickle.dump(tilt, tilt_pick) tilt_pick.close() #print os.path.join(obsdir, 'tilt.pkl') # position data if (not os.path.exists(os.path.join(obsdir, 'released_pos.pkl')) or (os.stat(os.path.join(obsdir, 'released_pos.pkl')).st_mtime < os.stat(point).st_mtime) or REDO): obs = obs_info print "making released_pos.pkl for {}".format(obs['obsid']) print obs evts = Table.read(obsid_src) q = Quaternion.Quat( [obs['ra_nom'], obs['dec_nom'], obs['roll_nom']]) # only use the first aspect interval of obsid 14457 if obsid == 14457: evts = evts[evts['time'] < 490232479.878] y, z = Ska.quatutil.radec2yagzag(evts['RA'], evts['DEC'], q) pos = dict(time=np.array(evts['time']), yag=np.array(y * 3600), zag=np.array(z * 3600)) pos_pick = open(os.path.join(obsdir, 'released_pos.pkl'), 'w') cPickle.dump(pos, pos_pick) pos_pick.close() GRADIENTS = dict(OOBAGRD3=dict( yag=6.98145650e-04, zag=9.51578351e-05, ), OOBAGRD6=dict( yag=-1.67009240e-03, zag=-2.79084775e-03, )) # position data if (not os.path.exists(os.path.join(obsdir, 'pos.pkl')) or (os.stat(os.path.join(obsdir, 'pos.pkl')).st_mtime < os.stat(point).st_mtime) or REDO): obs = obs_info print "making pos.pkl for {}".format(obs['obsid']) evts = Table.read(obsid_src) q = Quaternion.Quat( [obs['ra_nom'], obs['dec_nom'], obs['roll_nom']]) # only use the first aspect interval of obsid 14457 if obsid == 14457: evts = evts[evts['time'] < 490232479.878] y, z = Ska.quatutil.radec2yagzag(evts['RA'], evts['DEC'], q) # retrieve gradient telemetry tstart = evts['time'][0] tstop = evts['time'][-1] gradients = fetch.MSIDset(GRADIENTS.keys(), tstart - 100, tstop + 100) for msid in gradients: # filter bad telemetry in place filter_bad_telem(gradients[msid]) times = gradients[msid].times evt_idx = np.searchsorted(times, evts['time']) # find a mean gradient, because this calibration is relative to mean mean_gradient = np.mean(gradients[msid].vals[evt_idx]) # and smooth the telemetry to deal with slow changes and large step sizes.. smooth_gradient = smooth(gradients[msid].vals) y += (smooth_gradient[evt_idx] - mean_gradient) * GRADIENTS[msid]['yag'] z += (smooth_gradient[evt_idx] - mean_gradient) * GRADIENTS[msid]['zag'] pos = dict(time=np.array(evts['time']), yag=np.array(y * 3600), zag=np.array(z * 3600)) pos_pick = open(os.path.join(obsdir, 'pos.pkl'), 'w') cPickle.dump(pos, pos_pick) pos_pick.close() pos_files = glob("auto/obs*/released_pos.pkl") print "Retrieved sources for {} observations".format(len(pos_files))