def atd6(): """ Verify that a MosaicAD method can create a block from a given extension name. The test creates a mosaic ndarray from the MosaicAD method mosaic_image_data with the block parameter value (0,0), indicating to output the lower left block. NOTE: Having one amp per block, the actual extension data is not the same as the block since it would be trim by the DATASEC image section. gmos_file='../data/gS20120420S0033.fits' gsaoi_file='../data/guS20120413S0048.fits' """ from astrodata import AstroData from gempy.adlibrary.mosaicAD import MosaicAD # This is the default Mosaic function from gempy.mosaic.gemMosaicFunction import gemini_mosaic_function print '\n atd6 REQUIREMENT.......' print ('***** From a given AstroData object, the system shall create a block from ' 'a given extension name') gmos_file='../data/gS20120420S0033.fits' gsaoi_file='../data/guS20120413S0048.fits' for file in [gmos_file,gsaoi_file]: ad = AstroData(file) print 'Instrument: ',ad.instrument() mo = MosaicAD(ad, gemini_mosaic_function) # Now use the mosaic_image_data method to generate # an output block by using the parameter block and # value as a tuple (col,row) (0-based) of the block # you want returned. For GMOS the block values are # (0,0), (1,0), (2,0). block=(1,0) block_data = mo.mosaic_image_data(block=block,tile=True) # Get the shape: (height, width) in pixels. print 'block_data.shape:',block_data.shape extn = block[0] + block[1]*mo.geometry.mosaic_grid[0] + 1 print 'Input shape for 1-amp per detector:',ad['SCI',extn].data.shape # Check values of the lower 2x2 pixels print 'Output block [0:2,0:2] pixels:\n',block_data[:2,:2] if ad.instrument() == 'GSAOI': # GSAOI FITS extension 1 correspond to block (1,0) # and extension 2 to block (0,0). DETSEC image section # indicates this. extn = [2,1,3,4][extn-1] # To get the correct segment we need to look at # DATASEC, in case the data is trimm -as it appears in data_list. x1,x2,y1,y2 = ad.data_section().as_dict()['SCI',extn] print 'Input amp DATASEC[x1:x1+2 pixels:\n',\ ad['SCI',extn].data[x1:x1+2,y1:y1+2] print '\n'
def atd1(): """ With a GMOS AstroData object, the test instantiates a MosaicAD object containing 'coords' as one of the attributes. The test verify that coords['amp_mosaic_coord'] and ad['SCI'].detector_array.as_dict() values match. """ print '\n atd1 REQUIREMENT.......' print ('*****MosaicAD shall instantiate an object from a supported AstroData objec') gmos_file = '../data/gS20120420S0033.fits' gsaoi_file = '../data/guS20110324S0146.fits' nongem_file='../data/kp620765.fits' from astrodata import AstroData from gempy.adlibrary.mosaicAD import MosaicAD # This is the default Mosaic function from gempy.mosaic.gemMosaicFunction import gemini_mosaic_function # Success Criteria 1. (GMOS data) # The tester should provide her/his own GMOS file. for file in [gmos_file,gsaoi_file,nongem_file]: ad = AstroData(file) print '\n ...........CASE for:',file,ad.instrument() # Create the MosaicAD object mo = MosaicAD(ad,gemini_mosaic_function) # print DETECTOR values for all the 'SCI' extensions as # a dictionary. print ad['SCI'].detector_section().as_dict() # print the 'amp_mosaic_coord' key value from the 'coords' # attribute. This list is in increasing order of extver. print mo.coords['amp_mosaic_coord']
class AcquisitionImage(object): def __init__(self, filename, mosmask=None, mdfdir=None): self.ad = AstroData(filename) self.mosmask = mosmask self.mdfdir = mdfdir # Determine extension nsci = len(self.ad) debug("...nsci = ", nsci) if nsci > 1: l_sci_ext = 1 else: l_sci_ext = 0 debug("...using extension [" + str(l_sci_ext) + "]") overscan_dv = self.ad[l_sci_ext].overscan_section() if self.is_mos_mode(): self.box_coords = parse_box_coords(self, self.get_mdf_filename()) self.box_mosaic = BoxMosaic(self, self.box_coords) self.scidata = self.box_mosaic.get_science_data() elif self.is_new_gmosn_ccd(): # tile the 2 center parts of the new GMOS image self.scidata = gmultiamp(self.ad) elif not overscan_dv.is_none(): # remove the overscan so we don't have to take it into account when guessing the slit location self.scidata = subtract_overscan(self.ad[l_sci_ext]) # it still affects the center of rotation however ox1, ox2, oy1, oy2 = overscan_dv.as_list() correction = np.array([ox2 - ox1, 0]) center = self.get_binned_data_center() - correction self.fieldcenter = center * self.detector_y_bin() else: self.scidata = self.ad[l_sci_ext].data @cache def instrument(self): return str(self.ad.instrument()) def is_new_gmosn_ccd(self): header = self.ad.phu.header if "DETECTOR" not in header: return False if header["DETECTOR"] == "GMOS + e2v DD CCD42-90": return True return False def get_science_data(self): assert self.scidata is not None return self.scidata @cache def unbinned_pixel_scale(self): return float(self.ad.pixel_scale()) / self.detector_y_bin() @cache def binned_pixel_scale(self): return float(self.ad.pixel_scale()) def _check_binning(self): if int(self.ad.detector_x_bin()) != int(self.ad.detector_y_bin()): error("ERROR: incorrect binning!") error("Sorry about that, better luck next time.") sys.exit(1) @cache def detector_x_bin(self): self._check_binning() return int(self.ad.detector_x_bin()) @cache def detector_y_bin(self): self._check_binning() return int(self.ad.detector_y_bin()) @cache def program_id(self): return str(self.ad.program_id()) @cache def observation_id(self): return str(self.ad.observation_id()) @cache def saturation_level(self): dv = self.ad.saturation_level() return min(dv.as_list()) @cache def focal_plane_mask(self): return str(self.ad.focal_plane_mask()) @cache def grating(self): return str(self.ad.grating()) def get_detector_size(self): # mos mode acquisitions don't necessarily have the entire # field of view in their data sections, so we have to rely on # other tricks to figure out the center of rotation. detsize = self.ad.phu_get_key_value("DETSIZE") xmin, xdim, ymin, ydim = extract_dimensions(detsize) # adjust for chip gaps nccds = int(self.ad.phu_get_key_value("NCCDS")) xdim += ((nccds - 1) * _obtain_unbinned_arraygap(self.ad)) # adjust for un-illuminated pixels if self.is_gmos(): ydim -= 36 # magic number that should be replaced with a lookup table later return xdim, ydim def get_field_center(self): """ The center of rotation in pixels. """ if hasattr(self, "fieldcenter"): return self.fieldcenter if self.is_mos_mode(): xdim, ydim = self.get_detector_size() return np.array([float(xdim) / 2.0, float(ydim) / 2.0]) return self.get_data_center() def get_data_center(self): ydim, xdim = self.get_science_data().shape return np.array([float(xdim) / 2.0, float(ydim) / 2.0]) * self.detector_y_bin() def get_binned_data_center(self): return self.get_data_center() / self.detector_y_bin() def set_goal_center(self, center): self.goal_center = np.array(center) def get_goal_center(self): default = self.get_data_center() return getattr(self, "goal_center", default) def set_binned_custom_center(self, center): self.set_binned_goal_center(center) self.custom_center = True def has_custom_center(self): return getattr(self, "custom_center", False) def get_binned_goal_center(self): return self.get_goal_center() / self.detector_y_bin() def set_binned_goal_center(self, center): center = np.array(center) * self.detector_y_bin() self.set_goal_center(center) def get_mask_width(self): debug("...finding slit dimensions...") slitxbin = self.detector_x_bin() slitybin = self.detector_y_bin() debug("...slit image binning = ", slitxbin, " x ", slitybin) if slitxbin > 1 or slitybin > 1: warning("! WARNING: Slit image is binned " + slitxbin + " x " + slitybin) slitmask = self.focal_plane_mask() return float(slitmask.replace("arcsec", "")) def get_mask_width_in_pixels(self): return self.get_mask_width() / self.unbinned_pixel_scale() def get_slit_size_in_pixels(self): xsize = self.get_mask_width_in_pixels() ysize = self.get_science_data().shape[0] return xsize, ysize def get_expected_slit_tilt(self): if self.is_gmos(): return 0.0 error("Instrument is not supported, need to know an expected slit tilt") sys.exit(1) @property def phu(self): return self.ad.phu @property def filename(self): return self.ad.filename def get_program_id_parts(self): gemprgid = str(self.ad.program_id()) parts = gemprgid.split("-") if len(parts) != 4: msg = "Cannot parse program id '%s'" % gemprgid error(msg) raise ValueError(msg) observatory, semester, prgtype, queuenum = parts return observatory, semester, prgtype, int(queuenum) def get_semester(self): """ Return something in the form of '2006B' """ observatory, semester, prgtype, queuenum = self.get_program_id_parts() return semester def get_observatory_prefix(self): """ Return something in the form of 'GN' """ observatory, semester, prgtype, queuenum = self.get_program_id_parts() return observatory def is_mos_mode(self): return self.mosmask is not None or self.has_mos_mask() @cache def has_mos_mask(self): if not self.is_gmos() and not self.is_f2(): return False maskname = self.focal_plane_mask() if ("Q" in maskname or # Queue program "C" in maskname or # Classical program "D" in maskname or # DD program "V" in maskname): # SV program xbin = self.detector_x_bin() ybin = self.detector_y_bin() if xbin != 1 or ybin != 1: error ("MOS acquisition image binning must be 1x1, found %ix%i binning." % (xbin, ybin)) clean() return True return False def has_mask_in_beam(self): maskname = self.focal_plane_mask().lower() if "imag" in maskname: return False slitmask = self.focal_plane_mask() if self.is_gmos() and "arcsec" in slitmask: return True if self.is_gnirs() and "arcsec" in slitmask: acqmir = slitimage_ad.phu.header["ACQMIR"] debug("...acqmir = ", acqmir) if acqmir == "In": return True if self.is_niri() and "cam" not in slitmask: return True if self.is_f2() and "slit" in slitmask: return True if self.is_f2() and "mos" in slitmask: return True return self.has_mos_mask() def get_mdf_filename(self): if hasattr(self, "mdffile"): return self.mdffile self.mdffile = self._get_mdf_filename() return self.mdffile def _get_mdf_filename(self): if self.mosmask is not None: mdffile = self.mosmask # Expand the MOS mask number if is_number(self.mosmask): observatory, semester, prgtype, queuenum = self.get_program_id_parts() mdffile = "%s%s%s%03i-%02i" % (observatory, semester, prgtype, queuenum, int(self.mosmask)) debug("...mosmask =", mdffile) else: mdffile = self.focal_plane_mask() mdffile = fits_filename(mdffile) #----------------------------------------------------------------------- # Start searching around willy nilly for the MDF file if os.path.exists(mdffile): return mdffile # note, the order in which directories are added to this list gives priority dirs = [] if self.mdfdir is not None: dirs.append(self.mdfdir) dname = os.path.dirname(self.filename) if dname and dname != ".": dirs.append(dname) dirs.append(os.getcwd()) # search through semester directories as well semester_dir = self.get_observatory_prefix() + self.get_semester() directories_to_search = [] for dname in dirs: directories_to_search.append(dname) dname = os.path.join(dname, semester_dir) directories_to_search.append(dname) # now search through the directories for dname in directories_to_search: fname = os.path.join(dname, mdffile) debug("...trying", fname) if os.path.exists(fname): return fname raise ValueError("Unable to find MDF file named '%s'" % mdffile) def get_num_mos_boxes(self): return self.box_mosaic.get_num_mos_boxes() def get_mos_boxes(self): return self.box_mosaic.get_boxes() def get_mos_box_borders(self): for border in self.box_mosaic.get_box_borders(): yield border @cache def get_min_slitsize(self): mdffile_ad = AstroData(self.get_mdf_filename()) xsize = Ellipsis ysize = Ellipsis for row in mdffile_ad["MDF"].data: # select the alignment boxes, designated by priority 0 if row["priority"] not in ["1", "2", "3"]: continue xsize = min(xsize, row["slitsize_x"]) ysize = min(ysize, row["slitsize_y"]) return xsize, ysize def get_extensions(self): for ext in self.ad: yield ext def _get_lazy_detector_section_finder(self): if not hasattr(self, "detsec_finder"): self.detsec_finder = DetectorSectionFinder(self) return self.detsec_finder def get_box_size(self): return self._get_lazy_detector_section_finder().get_box_size() def find_detector_section(self, point): return self._get_lazy_detector_section_finder().find_detector_section(point) def get_full_field_of_view(self): return self._get_lazy_detector_section_finder().get_full_field_of_view() def is_altair(self): aofold = self.ad.phu.header["AOFOLD"] if aofold == "IN": return True return False def is_south_port(self): inportnum = int(self.ad.phu.header["INPORT"]) if inportnum == 1: return True return False def is_type(self, typename): return self.ad.is_type(typename) def is_gmos(self): return self.is_type("GMOS") def is_gmosn(self): return self.is_type("GMOS_N") def is_gmoss(self): return self.is_type("GMOS_S") def is_gnirs(self): return self.is_type("GNIRS") def is_f2(self): return self.is_type("F2") def is_nifs(self): return self.is_type("NIFS") def is_niri(self): return self.is_type("NIRI")
if __name__ == '__main__': """ Testing in the unix shell """ from astrodata import AstroData import time f2='/data2/ed/data/fS20120104S0070.fits' gnirs = '/data2/ed/data/nN20101215S0475_comb.fits' gmos = '/data2/ed/data/mgS20100113S0110.fits' t1=time.time() for ff in [gmos,f2,gnirs]: ad = AstroData(ff) print 'MAIN:>>>>>>>>>>>>>>>>>',ad.instrument(),ad.filename adout = trace_footprints(ad,debug=False) print adout.info() t2 = time.time() print '.....trace_footprints:','(%.2f curr: %.1f)'%(t2-t1,t2-t1) cl = CutFootprints(adout,debug=False) t4=time.time() cl.cut_regions() t5=time.time() print '.....cut_regions:','(%.2f curr: %.1f)'%(t5-t4,t5-t1) adcut=cl.as_astrodata() t6=time.time() print '...cl.as_astrodata:','(%.2f curr: %.1f)'%(t6-t5,t6-t1) #adcut.filename='adcut.fits' #adcut.write(clobber=True)
import sys sys.path.append("/opt/gemini_python") from astrodata import AstroData import urllib2, urllib # This is a GMOS_N imaging science dataset ad = AstroData("/home/callen/SVN-AD/gemini_python/test_data/calsearch/N20110531S0114.fits") desc_dict = {'instrument':ad.instrument().for_db(), 'observation_type': ad.observation_type().for_db(), 'data_label':ad.data_label().for_db(), 'detector_x_bin':ad.detector_x_bin().for_db(), 'detector_y_bin':ad.detector_y_bin().for_db(), 'read_speed_setting':ad.read_speed_setting().for_db(), 'gain_setting':ad.gain_setting().for_db(), 'amp_read_area':ad.amp_read_area().for_db(), 'ut_datetime':ad.ut_datetime().for_db(), 'exposure_time':ad.exposure_time().for_db(), 'object': ad.object().for_db(), 'filter_name':ad.filter_name().for_db(), 'focal_plane_mask':ad.focal_plane_mask().for_db(), } print repr(desc_dict) type_list = ad.types ad.close() sequence = [('descriptors', desc_dict), ('types', type_list)] postdata = urllib.urlencode(sequence) #postdata = urllib.urlencode({"hello":1.})
def atd5(): """ Verify that mosaicAD gives the correct WCS information for the mosaiced data. Given a GMOS input file, the MosaicAD object method as_astrodata creates an output AstroData object. This object 'SCI' header have the CRPIX1 and CPRIX2 for the reference extension header. The value CRPIX1 should match the value explained in the Success Criteria section. The value CRPIX2 is unchanged. Resources: gmos_file='../data/gS20120420S0033.fits' gsaoi_file='../data/guS20120413S0048.fits' ds9 running """ import pywcs try: from stsci.numdisplay import display except ImportError: from numdisplay import display print '\n atd5 REQUIREMENT.......' print ('***** Given an AstroData object, the system shall update the header keywords ' ' CRPIX1 and CRPIX2 in the output mosaiced AD object to match the requested ' 'transformations') gmos_file='../data/gS20120420S0033.fits' gsaoi_file='../data/guS20120413S0048.fits' from astrodata import AstroData from gempy.adlibrary.mosaicAD import MosaicAD # This is the default Mosaic function from gempy.mosaic.gemMosaicFunction import gemini_mosaic_function ad = AstroData(gmos_file) # Creates a mosaicAD object using the input ad and the # default mosaic function name gemini_mosaic_function. # 'SCI' is the default extname. mo = MosaicAD(ad, gemini_mosaic_function) # outad = mo.as_astrodata() # NOTE: The ref_ext is the left most amplifier in # reference block. For GMOS the reference block # (2,1). E.G. for a 6-amp GMOS exposure the left # most exposure is 3. refblk = mo.geometry.ref_block col,row = refblk[0], refblk[1] amp_per_block = mo._amps_per_block ref_ext = col*amp_per_block+1 ocrpix1 = ad['SCI',ref_ext].header['CRPIX1'] xgap = mo.geometry.gap_dict['transform_gaps'][col,row][0] blksz_x,blksz_y = mo.geometry.blocksize # Success Criteria 1. # Get the x2 value from coords['amp_mosaic_coord'][refblk] xoff = mo.coords['amp_mosaic_coord'][max(0,col-1)][1] print ocrpix1 + xoff + xgap, 'should match: ' print outad['SCI',1].header['CRPIX1'] # Success Criteria 2. # For a GSAOI file, ad = AstroData(gsaoi_file) if ad.instrument() != 'GSAOI': print '******** file is not GSAOI ************' mo = MosaicAD(ad, gemini_mosaic_function) outad = mo.as_astrodata() outhdr = outad['SCI'].header inhdr = ad['SCI',2].header # The values should be the same. print 'Crpix1 values (in,out):',inhdr["CRPIX1"],outhdr['CRPIX1'] print 'Crpix2 values (in,out):',inhdr["CRPIX2"],outhdr['CRPIX2'] # Success Criteria 3. # For a GMOS file in extension #2 hdr = ad['SCI',2].header wcs = pywcs.WCS(hdr) import pysao # Bring up a ds9 display by instantiating the pysao object ds9 = pysao.ds9() # Display the image ds9.view(ad['SCI',2].data, frame=1) # display(ad['SCI',2].data,frame=1) print 'Click on any object:' X,Y,f,k = ds9.readcursor() # Get X,Y values from an object on the ds9 display # Get ra,dec ra,dec = wcs.wcs_pix2sky(X,Y,1) # Generate the mosaic_data for this ad using # as_astrodata method. # Display the mosaic mosaic_data for the 'SCI' extension mosaic_data = mo.mosaic_image_data() # Display the mosaic ds9.view(mosaic_data,frame=2) # display(ad['SCI',2].data,frame=1) print 'Click on the same object:' MX,MY,f,k = ds9.readcursor() #display(mosaic_data,frame=2) # Measure X,Y of the same object, named this MX,MY # Get the wcs from the mosaic header mhdr = outad['SCI'].header mwcs = pywcs.WCS(mhdr) mra,mdec = mwcs.wcs_pix2sky(MX,MY,1) print 'These RA,DEC should be pretty close:',(ra[0],mra[0]),(dec[0],mdec[0])
def demo(test_data_path): """ Edge Detection demo script. The input parameter is the pathname to the 'test_data' directory of the gemini_python SVN repository. It will display images in DS9 and plot in your terminal. Please make sure ds9 is running already. Using Python shell: >>> import demo >>> demo.demo('/data1/gemini_python/test_data') Using Unix shell: demo.py --tdir=<test_data_path> run ' """ import os #from gempy.science import extract as extr import extract as extr from astrodata import AstroData # Set test_dir path to the test files; they are available in the # test_data directory in the SVN repository: # http://chara.hi.gemini.edu/svn/DRSoftware/gemini_python/test_data/edge_detection/ if not os.access(test_data_path,os.F_OK): print "\n >>>> ERROR in test_data_path",test_data_path return test_data_path = os.path.join(test_data_path, 'edge_detection') if test_data_path == None: print "..... ERROR: Please edit this script and set the 'test_data_path'" gnirs_file = os.path.join(test_data_path, 'nN20101215S0475_comb.fits') f2_file = os.path.join(test_data_path, 'fS20100220S0035_comb.fits') gmos_file = os.path.join(test_data_path, 'mgS20100113S0110.fits') # EDIT the for loop to include gmos. GMOS is SLOW demo (~4 mins) #for fname in [gnirs_file,f2_file,gmos_file]: print "Starting time:",time.asctime() pylab.interactive(True) for fname in [gnirs_file,f2_file]: ad = AstroData(fname) print ".... 1) Finding slit edges for",ad.filename,' (',ad.instrument(),')' adout = extr.trace_slits(ad,debug=True) print ".... 2) Cutting slits... \n" ad_cuts = extr.cut_slits(adout,debug=True) bname = os.path.basename(fname) # Writing to FITS file. ad_cuts[0].filename='cuts_'+bname ad_cuts[0].write(clobber=True) print ".... 3) Writing FITS file: ",ad_cuts[0].filename," .. in your working directory." print ".... 4) Displayinig the whole frame in frame: 1" print "Ending time:",time.asctime()