def write_movie(photon_file, band, tranges, stepsz=30, overwrite=False, pixsz=0.000416666666666667): """This function creates FITS cubes based on a photon event .csv file.""" fitsfilename = photon_file.replace('.csv', '-{s}s.fits'.format(s=int(stepsz))) if not os.path.exists(fitsfilename) or overwrite: events = calibrate_photons(photon_file, band) for trange in tranges: print_inline(trange) image, wcs = make_image(photon_file, band, trange=trange, events=events, pixsz=pixsz) try: movie = np.append(movie, [image], axis=0) except: movie = [image] hdu = pyfits.PrimaryHDU(movie) hdulist = pyfits.HDUList([hdu]) hdulist.writeto(fitsfilename)
def globalcount_shuttered(band, trange, verbose=0, timestamplist=False): """ Global event counts over the time range, exluding shuttered periods (due to no non-NULL data). :param band: The band to use, either 'FUV' or 'NUV'. :type band: str :param trange: Minimum and maximum time (in GALEX time) to consider. :type trange: list :param verbose: Verbosity level, a value of 0 is minimum verbosity. :type verbose: int :param timestamplist: Global event time stamps. :type timestamplist: list :returns: int -- Total global counts excluding shuttered periods. """ try: t = (timestamplist if np.array(timestamplist).any() else np.array(gQuery.getArray(gQuery.uniquetimes( band, trange[0], trange[1], flag=True), verbose=verbose), dtype='float64')[:, 0] / gQuery.tscale) except IndexError: # Shutter this whole time range. if verbose: print_inline('No data in {t0},{t1}'.format(t0=trange[0], t1=trange[1])) return 0 times = np.sort(np.unique(np.append(t, trange))) tranges = distinct_tranges(times, maxgap=0.05) nonnullevents, nullevents = 0, 0 for trange in tranges: nullevents += gQuery.getValue(gQuery.deadtime2(band, trange[0], trange[1]), verbose=verbose) nonnullevents += gQuery.getValue(gQuery.deadtime1( band, trange[0], trange[1]), verbose=verbose) return nullevents + nonnullevents
def globalcount_shuttered(band, trange, verbose=0, timestamplist=False): """ Global event counts over the time range, exluding shuttered periods (due to no non-NULL data). :param band: The band to use, either 'FUV' or 'NUV'. :type band: str :param trange: Minimum and maximum time (in GALEX time) to consider. :type trange: list :param verbose: Verbosity level, a value of 0 is minimum verbosity. :type verbose: int :param timestamplist: Global event time stamps. :type timestamplist: list :returns: int -- Total global counts excluding shuttered periods. """ try: t = (timestamplist if np.array(timestamplist).any() else np.array(gQuery.getArray(gQuery.uniquetimes(band, trange[0], trange[1], flag=True), verbose=verbose), dtype='float64')[:, 0]/gQuery.tscale) except IndexError: # Shutter this whole time range. if verbose: print_inline('No data in {t0},{t1}'.format(t0=trange[0], t1=trange[1])) return 0 times = np.sort(np.unique(np.append(t, trange))) tranges = distinct_tranges(times, maxgap=0.05) nonnullevents, nullevents = 0, 0 for trange in tranges: nullevents += gQuery.getValue( gQuery.deadtime2(band, trange[0], trange[1]), verbose=verbose) nonnullevents += gQuery.getValue(gQuery.deadtime1(band, trange[0], trange[1]), verbose=verbose) return nullevents+nonnullevents
def stimcount_shuttered(band, trange, verbose=0, timestamplist=False): """ Returns the stim count over a time range, excluding periods that the detector is considered shuttered (because of no non-NULL data). :param band: The band to use, either 'FUV' or 'NUV'. :type band: str :param trange: Minimum and maximum time (in GALEX time) to consider. :type trange: list :param verbose: Verbosity level, a value of 0 is minimum verbosity. :type verbose: int :param timestamplist: Global detector event timestamps. :type timestamplist: list :returns: int -- Total stim counts excluding shuttered time ranges. """ try: t = (timestamplist if np.array(timestamplist).any() else np.array(gQuery.getArray(gQuery.uniquetimes(band, trange[0], trange[1]), verbose=verbose), dtype='float64')[:, 0]/gQuery.tscale) except IndexError: # Shutter this whole time range. if verbose: print_inline('No data in {t0},{t1}'.format(t0=trange[0], t1=trange[1])) return 0 times = np.sort(np.unique(np.append(t, trange))) tranges = distinct_tranges(times, maxgap=0.05) stimcount = 0 for trange in tranges: stimcount += (gQuery.getValue(gQuery.stimcount(band, trange[0], trange[1]), verbose=verbose) + gQuery.getValue(gQuery.stimcount(band, trange[0], trange[1], null=False), verbose=verbose)) return stimcount
def stimcount_shuttered(band, trange, verbose=0, timestamplist=False): """ Returns the stim count over a time range, excluding periods that the detector is considered shuttered (because of no non-NULL data). :param band: The band to use, either 'FUV' or 'NUV'. :type band: str :param trange: Minimum and maximum time (in GALEX time) to consider. :type trange: list :param verbose: Verbosity level, a value of 0 is minimum verbosity. :type verbose: int :param timestamplist: Global detector event timestamps. :type timestamplist: list :returns: int -- Total stim counts excluding shuttered time ranges. """ try: t = (timestamplist if np.array(timestamplist).any() else np.array( gQuery.getArray(gQuery.uniquetimes(band, trange[0], trange[1]), verbose=verbose), dtype='float64')[:, 0] / gQuery.tscale) except IndexError: # Shutter this whole time range. if verbose: print_inline('No data in {t0},{t1}'.format(t0=trange[0], t1=trange[1])) return 0 times = np.sort(np.unique(np.append(t, trange))) tranges = distinct_tranges(times, maxgap=0.05) stimcount = 0 for trange in tranges: stimcount += (gQuery.getValue( gQuery.stimcount(band, trange[0], trange[1]), verbose=verbose) + gQuery.getValue(gQuery.stimcount( band, trange[0], trange[1], null=False), verbose=verbose)) return stimcount
def exposure(band, trange, verbose=0): """ Calculate the effective exposure time in a period, in seconds, accounting for shutter and deadtime. Does not account for actual sky coverage of the telescope during the time period queried (see: compute_exptime() below). :param band: The band to use, either 'FUV' or 'NUV'. :type band: str :param trange: Minimum and maximum time (in GALEX time) to consider. :type trange: list :param verbose: Verbosity level, a value of 0 is minimum verbosity. :type verbose: int :returns: float -- The effective exposure time, in seconds. """ rawexpt = trange[1] - trange[0] if rawexpt == 0.: return 0. try: t = (np.array(gQuery.getArray(gQuery.uniquetimes( band, trange[0], trange[1], flag=True), verbose=verbose), dtype='float64')[:, 0] / gQuery.tscale) except IndexError: # Shutter this whole time range. if verbose: print_inline('No data in {t0},{t1}'.format(t0=trange[0], t1=trange[1])) return 0. shutter = compute_shutter(band, trange, verbose=verbose, timestamplist=t) deadtime = empirical_deadtime(band, trange, verbose=verbose, timestamplist=t) return (rawexpt - shutter) * (1. - deadtime)
def exposure(band, trange, verbose=0): """ Calculate the effective exposure time in a period, in seconds, accounting for shutter and deadtime. Does not account for actual sky coverage of the telescope during the time period queried (see: compute_exptime() below). :param band: The band to use, either 'FUV' or 'NUV'. :type band: str :param trange: Minimum and maximum time (in GALEX time) to consider. :type trange: list :param verbose: Verbosity level, a value of 0 is minimum verbosity. :type verbose: int :returns: float -- The effective exposure time, in seconds. """ rawexpt = trange[1]-trange[0] if rawexpt == 0.: return 0. try: t = (np.array(gQuery.getArray( gQuery.uniquetimes(band, trange[0], trange[1], flag=True), verbose=verbose), dtype='float64')[:, 0]/gQuery.tscale) except IndexError: # Shutter this whole time range. if verbose: print_inline('No data in {t0},{t1}'.format(t0=trange[0], t1=trange[1])) return 0. shutter = compute_shutter(band, trange, verbose=verbose, timestamplist=t) deadtime = empirical_deadtime(band, trange, verbose=verbose, timestamplist=t) return (rawexpt-shutter)*(1.-deadtime)
def mcat_skybg(band, skypos, radius, verbose=0, trange=None, mcat=None, searchradius=0.1): """ Estimate the sky background using the MCAT 'skybg' for nearby sources. :param band: The band to use, either 'FUV' or 'NUV'. :type band: str :param skypos: The right ascension and declination, in degrees. :type skypos: list :param radius: The radius in which to search for MCAT sources in degrees. :type radius: float :param verbose: Verbosity level, a value of 0 is minimum verbosity. :type verbose: int :param trange: Minimum and maximum time (in GALEX time) to consider. :type trange: list :returns: float -- The estimated sky background in the photometric aperture, in counts per second. """ # Search the visit-level MCAT for nearby detections. # Unless the MCAT data has already been handed off for detection purposes. if not mcat: mcat = get_mcat_data(skypos, searchradius) try: # Find the distance to each source. dist = np.array([angularSeparation(skypos[0], skypos[1], a[0], a[1]) for a in zip(mcat[band]['ra'], mcat[band]['dec'])]) except TypeError: print_inline( 'No {b} MCAT sources within {r} degrees of {p}'.format( b=band, r=searchradius, p=skypos)) return np.nan # Find visits that overlap in time. if not trange: tix = (np.array(list(range(len(mcat[band]['mag']))), dtype='int32'),) else: tix = np.where( ((trange[0] >= mcat[band]['t0']) & (trange[0] <= mcat[band]['t1'])) | ((trange[1] >= mcat[band]['t0']) & (trange[1] <= mcat[band]['t1'])) | ((trange[0] <= mcat[band]['t0']) & (trange[1] >= mcat[band]['t1']))) if not len(tix[0]): print_inline('No concurrent {b} MCAT source nearby.'.format(b=band)) return np.nan # Might not be the preferred behavior here. ix = np.where(dist[tix] == min(dist[tix])) skybg = mcat[band]['skybg'][tix][ix] # This should rarely happen, but sometimes there's a duplicate entry in # the visit-level MCAT. if len(skybg) > 1: # If the skybg array is all the same value, it's a duplicate. if np.all(skybg == skybg[0]): skybg = np.asarray([skybg[0]]) else: skybg = np.asarray([np.median(skybg)]) return skybg[0]*area(radius*60.*60.)
def fGetTimeRanges(band, skypos, trange=None, detsize=1.1, verbose=0, maxgap=1., minexp=1., skyrange=None, maxgap_override=False, minexp_override = False): """ Find the contiguous time ranges within a time range at a specific location. :param band: The band to use, either 'FUV' or 'NUV'. :type band: str :param skypos: The right ascension and declination, in degrees. :type skypos: list :param trange: Minimum and maximum time (in GALEX time) to consider. :type trange: list :param detsize: The effective detector diameter, in degrees. :type detsize: float :param verbose: Verbosity level, a value of 0 is minimum verbosity. :type verbose: int :param maxgap: Maximum gap size, in seconds, for data to be considered contiguous. :type maxgap: float :param minexp: Minimum gap size, in seconds, for data to be considered contiguous. :type minexp: float :param skyrange: Values in degrees RA and Dec of a box around skypos that defines the extent of the region of interest. :type skyrange: list :param maxgap_override: Enables an experimental feature where maxgap can be less than one second. :type maxgap_override: bool :returns: numpy.ndarray -- A valid set of time ranges, accounting for minimum exposure lengths and maximum gaps. """ if trange is not None: trange_buffer = (trange[0]-1,trange[1]+1) times = get_valid_times(band, skypos, trange=trange_buffer, detsize=detsize, verbose=verbose, skyrange=skyrange) if len(times): times = np.append(times,times[-1]+1) if times[0]<trange[0]: times[0]=trange[0] if times[-1]>trange[1]: times[-1]=trange[1] else: times = get_valid_times(band, skypos, trange=None, detsize=detsize, verbose=verbose, skyrange=skyrange) if len(times): times = np.append(times,times[-1]+1) if not len(times): return np.array([[]], dtype='float64') if verbose: print_inline('Parsing ~'+str(len(times)-1)+' seconds of raw exposure.') # NOTE: The minimum meaningful maxgap is 1 second. if maxgap < 1 and not maxgap_override: raise ValueError('maxgap must be >=1 second') tranges = distinct_tranges(times, maxgap=maxgap) if not minexp_override: ix = np.where(np.array(tranges)[:, 1]-np.array(tranges)[:, 0] >= minexp) tranges = np.array(tranges)[ix].tolist() return np.array(tranges, dtype='float64')
def photonpipe(outbase, band, raw6file=None, scstfile=None, aspfile=None, ssdfile=None, nullfile=None, verbose=0, retries=20, eclipse=None): """ Apply static and sky calibrations to -raw6 GALEX data, producing fully aspect-corrected and time-tagged photon list files. :param raw6file: Name of the raw6 file to use. :type raw6file: str :param scstfile: Spacecraft state file to use. :type scstfile: str :param band: Name of the band to use, either 'FUV' or 'NUV'. :type band: str :param outbase: Base of the output file names. :type outbase: str :param aspfile: Name of aspect file to use. :type aspfile: int :param ssdfile: Name of Stim Separation Data file to use. :type ssdfile: int :param nullfile: Name of output file to record NULL lines. :type nullfile: int :param verbose: Note used. Included for consistency with other tools. :type verbose: int :param retries: Number of query retries to attempt before giving up. :type retries: int """ startt = time.time() # Scale factor for the time column in the output csv so that it # can be recorded as an int in the database. dbscale = 1000 # This number determines the size of the chunks that gPhoton reads # in from the raw6 for processing. Even if your machine has a lot # of memory, making this number bigger is unlikely to improve the # processing time much because so much is eaten up by the .csv write. chunksz = 1000000 # These are just constants for the mission. detsize = 1.25 # Detector size in degrees pltscl = 68.754932 # Plate scale aspum = pltscl / 1000.0 arcsecperpixel = 1.5 xi_xsc, xi_ysc, eta_xsc, eta_ysc = 0., 1., 1., 0. # Determine the eclipse number from the raw6 header. if not raw6file: if not eclipse: raise ValueError('Must specifiy eclipse if no raw6file.') else: raw6file = download_data(eclipse, band, 'raw6', datadir=os.path.dirname(outbase)) if raw6file == None: print('Unable to retrieve raw6 file for this eclipse.') return hdulist = pyfits.open(raw6file) hdr = hdulist[0].header hdulist.close() if eclipse and (eclipse != hdr['eclipse']): # just a consistency check print("Warning: eclipse mismatch {e0} vs. {e1} (header)".format( e0=eclipse, e1=hdr['eclipse'])) eclipse = hdr['eclipse'] print("Processing eclipse " + str(eclipse) + ".") # Returns detector constants. print("Band is " + band + ".") (xclk, yclk, xcen, ycen, xscl, yscl, xslp, yslp) = clk_cen_scl_slp(band, eclipse) # This determines the values for the post-CSP detector stim scaling # and detector constant corrections. Mx, Bx, My, By, stimsep = 1, 0, 1, 0, 0 if eclipse > 37460: (Mx, Bx, My, By, stimsep, yactbl) = compute_stimstats(raw6file, band, eclipse) wig2, wig2data, wlk2, wlk2data, clk2, clk2data = post_csp_caldata() print("Loading wiggle files...") wiggle_x, _ = cal.wiggle(band, 'x') wiggle_y, _ = cal.wiggle(band, 'y') print("Loading walk files...") walk_x, _ = cal.walk(band, 'x') walk_y, _ = cal.walk(band, 'y') print("Loading linearity files...") linearity_x, _ = cal.linearity(band, 'x') linearity_y, _ = cal.linearity(band, 'y') # This is for the post-CSP stim distortion corrections. print("Loading distortion files...") if eclipse > 37460: print(" Using stim separation of :" + str(stimsep)) distortion_x, disthead = cal.distortion(band, 'x', eclipse, stimsep) distortion_y, _ = cal.distortion(band, 'y', eclipse, stimsep) (cube_x0, cube_dx, cube_y0, cube_dy, cube_d0, cube_dd, cube_nd, cube_nc, cube_nr) = (disthead['DC_X0'], disthead['DC_DX'], disthead['DC_Y0'], disthead['DC_DY'], disthead['DC_D0'], disthead['DC_DD'], disthead['NAXIS3'], disthead['NAXIS1'], disthead['NAXIS2']) if band == 'FUV': if not scstfile: if not eclipse: raise ValueError('Must specifiy eclipse if no scstfile.') else: scstfile = download_data(eclipse, band, 'scst', datadir=os.path.dirname(outbase)) if scstfile == None: print('Unable to retrieve SCST file for this eclipse.') return xoffset, yoffset = find_fuv_offset(scstfile) else: xoffset, yoffset = 0., 0. if os.path.isfile(str(ssdfile)): print("SSD file provided: " + str(ssdfile)) stim_coef0, stim_coef1 = get_stim_coefs(ssdfile) elif ssdfile: print("SSD file requested: " + str(ssdfile)) stim_coef0, stim_coef1 = create_ssd(raw6file, band, eclipse, ssdfile) else: print("No SSD file provided or requested.") stim_coef0, stim_coef1 = create_ssd(raw6file, band, eclipse) print(" stim_coef0, stim_coef1 = " + str(stim_coef0) + ", " + str(stim_coef1)) print("Loading mask file...") mask, maskinfo = cal.mask(band) npixx = mask.shape[0] npixy = mask.shape[1] pixsz = maskinfo['CDELT2'] maskfill = detsize / (npixx * pixsz) print("Loading aspect data...") if aspfile: (aspra, aspdec, asptwist, asptime, aspheader, aspflags) = load_aspect(aspfile) else: (aspra, aspdec, asptwist, asptime, aspheader, aspflags) = web_query_aspect(eclipse, retries=retries) minasp, maxasp = min(asptime), max(asptime) trange = [minasp, maxasp] print(" trange= ( {t0} , {t1} )".format(t0=trange[0], t1=trange[1])) ra0, dec0, roll0 = aspheader['RA'], aspheader['DEC'], aspheader['ROLL'] print(" [avgRA, avgDEC, avgROLL] = [{RA}, {DEC}, {ROLL}]".format( RA=aspra.mean(), DEC=aspdec.mean(), ROLL=asptwist.mean())) # This projects the aspect solutions onto the MPS field centers. print("Computing aspect vectors...") (xi_vec, eta_vec) = gnomfwd_simple(aspra, aspdec, ra0, dec0, -asptwist, 1.0 / 36000.0, 0.) print("Loading raw6 file...") raw6hdulist = pyfits.open(raw6file, memmap=1) raw6htab = raw6hdulist[1].header nphots = raw6htab['NAXIS2'] print(" " + str(nphots) + " events") cnt = 0 outfile = outbase + '.csv' print("Preparing output file " + outfile) spreadsheet = csv.writer(open(outfile, 'w'), delimiter=',', quotechar='|', quoting=csv.QUOTE_MINIMAL) # If specified, dump lines with NULLS into a separate csv file. if nullfile: nullfile = outbase + '_NULL.csv' print("Preparing output file " + nullfile) NULLspreadsheet = csv.writer(open(nullfile, 'w'), delimiter=',', quotechar='|', quoting=csv.QUOTE_MINIMAL) print("") for i in range(int(nphots / chunksz) + 1): a = time.time() csvrows = [] chunkbeg, chunkend = i * chunksz, (i + 1) * chunksz if chunkend > nphots: chunkend = nphots chunkid = " " + str(i + 1) + " of " + str(int(nphots / chunksz) + 1) + ": " print_inline(chunkid + "Unpacking raw6 data...") t = np.array(raw6hdulist[1].data.field('t')[chunkbeg:chunkend]) phb1 = np.array(raw6hdulist[1].data.field('phb1')[chunkbeg:chunkend], dtype='int64') phb2 = np.array(raw6hdulist[1].data.field('phb2')[chunkbeg:chunkend], dtype='int64') phb3 = np.array(raw6hdulist[1].data.field('phb3')[chunkbeg:chunkend], dtype='int64') phb4 = np.array(raw6hdulist[1].data.field('phb4')[chunkbeg:chunkend], dtype='int64') phb5 = np.array(raw6hdulist[1].data.field('phb5')[chunkbeg:chunkend], dtype='int64') # Bitwise "decoding" of the raw6 telemetry. q = ((phb4 & 3) << 3) + ((phb5 & 224) >> 5) xb = phb1 >> 5 xamc = (np.array(((phb1 & 31) << 7), dtype='int16') + np.array( ((phb2 & 254) >> 1), dtype='int16') - np.array( ((phb1 & 16) << 8), dtype='int16')) yb = ((phb2 & 1) << 2) + ((phb3 & 192) >> 6) yamc = (np.array(((phb3 & 63) << 6), dtype='int16') + np.array( ((phb4 & 252) >> 2), dtype='int16') - np.array( ((phb3 & 32) << 7), dtype='int16')) xa = ((phb5 & 16) >> 4) + ((phb5 & 3) << 3) + ((phb5 & 12) >> 1) xraw0 = xb * xclk + xamc yraw0 = yb * yclk + yamc ya = np.array( ((((yraw0 / (2 * yclk) - xraw0 / (2 * xclk)) + 10) * 32) + xa), dtype='int64') % 32 xraw = xraw0 + np.array((((xa + 7) % 32) - 16), dtype='int64') * xslp yraw = yraw0 + np.array((((ya + 7) % 32) - 16), dtype='int64') * yslp # Centering and scaling. x = (xraw - xcen) * xscl y = (yraw - ycen) * yscl # Post-CSP 'yac' corrections. if eclipse > 37460: x = Mx * x + Bx y = My * y + By yac = rtaph_yac(yactbl, ya, yb, yamc, eclipse) y = y - yac yac = rtaph_yac2(q, xb, yb, ya, y, aspum, wig2, wig2data, wlk2, wlk2data, clk2, clk2data) y = y + yac # [Future] This and other ugly lines like it below are for the purpose # of memory management. There is likely a more Pythonic way. (phb1, phb2, phb3, phb4, phb5, xb, xamc, yb, yamc, xraw0, yraw0, xraw, yraw) = ([], [], [], [], [], [], [], [], [], [], [], [], []) flags = np.zeros(len(t)) print_inline(chunkid + "Applying wiggle correction...") x_as = x * aspum y_as = y * aspum fptrx = x_as / 10. + 240. fptry = y_as / 10. + 240. x_as, y_as = [], [] # This and other lines like it below are to verify that the # event is still on the detector. cut = ((fptrx > 0.) & (fptrx < 479.) & (fptry > 0.) & (fptry < 479.) & (flags == 0)) flags[np.where(cut == False)[0]] = 8 ix = np.where(cut == True)[0] blt = fptrx - np.array(fptrx, dtype='int64') blu = fptry - np.array(fptry, dtype='int64') wigx, wigy = np.zeros(len(t)), np.zeros(len(t)) wigx[ix] = ( (1 - blt[ix]) * (wiggle_x[xa[ix], np.array(fptrx[ix], dtype='int64')]) + (blt[ix]) * (wiggle_x[xa[ix], np.array(fptrx[ix], dtype='int64') + 1])) wigy[ix] = ( (1 - blu[ix]) * (wiggle_y[ya[ix], np.array(fptry[ix], dtype='int64')]) + (blu[ix]) * (wiggle_y[ya[ix], np.array(fptry[ix], dtype='int64') + 1])) xdig = x + wigx / (10. * aspum) ydig = y + wigy / (10. * aspum) wigx, wigy = [], [] print_inline(chunkid + "Applying walk correction...") xdig_as = xdig * aspum ydig_as = ydig * aspum fptrx = xdig_as / 10. + 240. fptry = ydig_as / 10. + 240. xdig_as, ydig_as = [], [] cut = ((fptrx > 0.) & (fptrx < 479.) & (fptry > 0.) & (fptry < 479.) & (flags == 0)) flags[np.where(cut == False)[0]] = 9 ix = np.where(cut == True)[0] cut[ix] = ((walk_x[q[ix], np.array(fptry[ix], dtype='int64'), np.array(fptrx[ix], dtype='int64')] != -999) | (walk_x[q[ix], np.array(fptry[ix], dtype='int64'), np.array(fptrx[ix], dtype='int64') + 1] != -999) | (walk_x[q[ix], np.array(fptry[ix], dtype='int64') + 1, np.array(fptrx[ix], dtype='int64')] != -999) | (walk_x[q[ix], np.array(fptry[ix], dtype='int64') + 1, np.array(fptrx[ix], dtype='int64') + 1] != -999) | (walk_y[q[ix], np.array(fptry[ix], dtype='int64'), np.array(fptrx[ix], dtype='int64')] != -999) | (walk_y[q[ix], np.array(fptry[ix], dtype='int64'), np.array(fptrx[ix], dtype='int64') + 1] != -999) | (walk_y[q[ix], np.array(fptry[ix], dtype='int64') + 1, np.array(fptrx[ix], dtype='int64')] != -999) | (walk_y[q[ix], np.array(fptry[ix], dtype='int64') + 1, np.array(fptrx[ix], dtype='int64') + 1] != -999)) flags[np.where(cut == False)[0]] = 9 ix = np.where(cut == True)[0] blt = fptrx - np.array(fptrx, dtype='int64') blu = fptry - np.array(fptry, dtype='int64') walkx, walky = np.zeros(len(t)), np.zeros(len(t)) walkx[ix] = ((1 - blt[ix]) * (1 - blu[ix]) * (walk_x[q[ix], np.array(fptry[ix], dtype='int64'), np.array(fptrx[ix], dtype='int64')]) + (blt[ix]) * (1 - blu[ix]) * (walk_x[q[ix], np.array(fptry[ix], dtype='int64'), np.array(fptrx[ix], dtype='int64') + 1]) + (1 - blt[ix]) * (blu[ix]) * (walk_x[q[ix], np.array(fptry[ix], dtype='int64') + 1, np.array(fptrx[ix], dtype='int64')]) + (blt[ix]) * (blu[ix]) * (walk_x[q[ix], np.array(fptry[ix], dtype='int64') + 1, np.array(fptrx[ix], dtype='int64') + 1])) walky[ix] = ((1 - blt[ix]) * (1 - blu[ix]) * (walk_y[q[ix], np.array(fptry[ix], dtype='int64'), np.array(fptrx[ix], dtype='int64')]) + (blt[ix]) * (1 - blu[ix]) * (walk_y[q[ix], np.array(fptry[ix], dtype='int64'), np.array(fptrx[ix], dtype='int64') + 1]) + (1 - blt[ix]) * (blu[ix]) * (walk_y[q[ix], np.array(fptry[ix], dtype='int64') + 1, np.array(fptrx[ix], dtype='int64')]) + (blt[ix]) * (blu[ix]) * (walk_y[q[ix], np.array(fptry[ix], dtype='int64') + 1, np.array(fptrx[ix], dtype='int64') + 1])) print_inline(chunkid + "Applying spatial non-linearity correction...") xp = xdig - walkx yp = ydig - walky xp_as = xp * aspum yp_as = yp * aspum fptrx = xp_as / 10. + 240. fptry = yp_as / 10. + 240. xp, yp = [], [] walkx, walky = [], [] cut = ((fptrx > 0.) & (fptrx < 479.) & (fptry > 0.) & (fptry < 479.) & (flags == 0)) flags[np.where(cut == False)[0]] = 10 ix = np.where(cut == True)[0] blt = fptrx - np.array(fptrx, dtype='int64') blu = fptry - np.array(fptry, dtype='int64') dx, dy = np.zeros(len(t)), np.zeros(len(t)) dx[ix] = ( (1 - blt[ix]) * (1 - blu[ix]) * linearity_x[np.array(fptry[ix], dtype='int64'), np.array(fptrx[ix], dtype='int64')] + (blt[ix]) * (1 - blu[ix]) * linearity_x[np.array(fptry[ix], dtype='int64'), np.array(fptrx[ix], dtype='int64') + 1] + (1 - blt[ix]) * (blu[ix]) * linearity_x[np.array(fptry[ix], dtype='int64') + 1, np.array(fptrx[ix], dtype='int64')] + (blt[ix]) * (blu[ix]) * linearity_x[np.array(fptry[ix], dtype='int64') + 1, np.array(fptrx[ix], dtype='int64') + 1]) dy[ix] = ( (1 - blt[ix]) * (1 - blu[ix]) * linearity_y[np.array(fptry[ix], dtype='int64'), np.array(fptrx[ix], dtype='int64')] + (blt[ix]) * (1 - blu[ix]) * linearity_y[np.array(fptry[ix], dtype='int64'), np.array(fptrx[ix], dtype='int64') + 1] + (1 - blt[ix]) * (blu[ix]) * linearity_y[np.array(fptry[ix], dtype='int64') + 1, np.array(fptrx[ix], dtype='int64')] + (blt[ix]) * (blu[ix]) * linearity_y[np.array(fptry[ix], dtype='int64') + 1, np.array(fptrx[ix], dtype='int64') + 1]) print_inline(chunkid + "Applying stim distortion correction...") ss = stim_coef0 + (t * stim_coef1) # stim separation col, row, depth = np.zeros(len(t)), np.zeros(len(t)), np.zeros(len(t)) col[ix] = (xp_as[ix] - cube_x0) / cube_dx row[ix] = (yp_as[ix] - cube_y0) / cube_dy depth[ix] = (ss[ix] - cube_d0) / cube_dd # [Future]: This throws an error sometimes like the following, may need # fixing... """PhotonPipe.py:262: RuntimeWarning: invalid value encountered in less depth[((depth < 0)).nonzero()[0]] = 0. PhotonPipe.py:263: RuntimeWarning: invalid value encountered in greater_equal depth[((depth >= cube_nd)).nonzero()[0]] = -1. ERROR: IndexError: index -9223372036854775808 is out of bounds for axis 0 with size 17 [PhotonPipe] depth[((depth < 0)).nonzero()[0]] = 0. depth[((depth >= cube_nd)).nonzero()[0]] = -1. """ cut = ((col > -1) & (col < cube_nc) & (row > -1) & (row < cube_nr) & (flags == 0) & (np.array(depth, dtype='int64') < 18)) flags[np.where(cut == False)[0]] = 11 ix = np.where(cut == True)[0] xshift, yshift = np.zeros(len(t)), np.zeros(len(t)) xshift[ix] = distortion_x[np.array(depth[ix], dtype='int64'), np.array(row[ix], dtype='int64'), np.array(col[ix], dtype='int64')] yshift[ix] = distortion_y[np.array(depth[ix], dtype='int64'), np.array(row[ix], dtype='int64'), np.array(col[ix], dtype='int64')] xshift = (xshift * arcsecperpixel) + xoffset yshift = (yshift * arcsecperpixel) + yoffset print_inline(chunkid + "Applying hotspot mask...") # The detectors aren't oriented the same way. flip = 1. if band == 'FUV': flip = -1. xi = (xi_xsc * (flip * (yp_as + dy + yshift) * 10.) + xi_ysc * (flip * (xp_as + dx + xshift) * 10.)) eta = (eta_xsc * (flip * (yp_as + dy + yshift) * 10.) + eta_ysc * (flip * (xp_as + dx + xshift) * 10.)) xp_as, yp_as, depth, ss = [], [], [], [] dx, dy, xshift, yshift = [], [], [], [] col = (((xi / 36000.) / (detsize / 2.) * maskfill + 1.) / 2. * npixx) row = (((eta / 36000.) / (detsize / 2.) * maskfill + 1.) / 2. * npixy) # This mask out data that is not on the detector at all cut = ((col > 0.) & (col < 799.) & (row > 0.) & (row < 799.) & (flags == 0)) flags[np.where(cut == False)[0]] = 6 ix = np.where(cut == True)[0] # This masks out hotspot regions and is experimentally deprecated. #cut[ix] = ( # (mask[np.array(col[ix], dtype='int64'), # np.array(row[ix], dtype='int64')] == 1.)) #flags[np.where(cut == False)[0]] = 6 #ix = np.where(cut == True)[0] col, row = [], [] # This gives the index of the aspect time that comes _before_ # each photon time. Without the '-1' it will give the index # of the aspect time _after_ the photon time. print_inline(chunkid + "Mapping photon times to aspect times...") aspix = np.digitize(t, asptime) - 1 print_inline(chunkid + "Applying dither correction...") # Use only photons that are bracketed by valid aspect solutions # and have been not themselves been flagged as invalid. cut = ((aspix > 0) & (aspix < (len(asptime) - 1)) & ((flags == 0) | (flags == 6))) flags[np.where(cut == False)[0]] = 7 ix = np.where(cut == True)[0] print_inline(chunkid + "Interpolating aspect solutions...") dxi, deta = np.zeros(len(t)), np.zeros(len(t)) dxi[ix] = ((xi_vec[aspix[ix] + 1] - xi_vec[aspix[ix]]) * (t[ix] - asptime[aspix[ix]]) / (asptime[aspix[ix] + 1] - asptime[aspix[ix]])) deta[ix] = ((eta_vec[aspix[ix] + 1] - eta_vec[aspix[ix]]) * (t[ix] - asptime[aspix[ix]]) / (asptime[aspix[ix] + 1] - asptime[aspix[ix]])) print_inline(chunkid + "Mapping to sky...") ra, dec = np.zeros(len(t)), np.zeros(len(t)) ra[ix], dec[ix] = gnomrev_simple(xi[ix] + dxi[ix], eta[ix] + deta[ix], aspra[aspix[ix]], aspdec[aspix[ix]], -asptwist[aspix[ix]], 1 / 36000., 0.) cut = (((asptime[aspix[ix] + 1] - asptime[aspix[ix]]) == 1) & (aspflags[aspix[ix]] % 2 == 0) & (aspflags[aspix[ix] + 1] % 2 == 0) & (aspflags[aspix[ix] - 1] % 2 == 0) & (flags[ix] == 0) & (flags[ix] != 7)) flags[np.where(cut == False)[0]] = 12 # NOTE: If you wish to add a hook that filters the gPhoton output # (like perhaps by sky position or time range) then add it here. # I reccomend that you use the "ix = np.where" technique used above. # [Future]: Preprogram a (commented out) filter on RA/Dec. print_inline(chunkid + "Writing to spreadsheet...") # The issue is that we need to recombine the data into rows to # feed to csv.writerow, hence the loop. # It might be possible to do without a loop. One way would be # with numpy.column_stack except that this requires stupid # amounts of memory and therefore takes longer to run than the # loop iffen it manages to complete at all without a memory # error or segmentation fault. for i in range(len(t)): cnt += 1 # To avoid repeat indexing of flags... thisflag = flags[i] # This substitutes empty strings for RA and Dec # values so that when they're dumped into the database # they are correctly recorded as NULL if (thisflag == 2 or thisflag == 5 or thisflag == 7 or thisflag == 8 or thisflag == 9 or thisflag == 10 or thisflag == 11 or thisflag == 12): # Should be: #if ((thisflag == 3) or (thisflag == 6) or (thisflag == 8) or # (thisflag == 9) or (thisflag == 10) or # (thisflag == 11) or (thisflag = 12):) if nullfile: NULLspreadsheet.writerow([ int(t[i] * dbscale), x[i], y[i], xa[i], ya[i], q[i], xi[i], eta[i], "", "", flags[i] ]) else: spreadsheet.writerow([ int(t[i] * dbscale), x[i], y[i], xa[i], ya[i], q[i], xi[i], eta[i], "", "", flags[i] ]) else: spreadsheet.writerow([ int(t[i] * dbscale), x[i], y[i], xa[i], ya[i], q[i], xi[i], eta[i], ra[i], dec[i], flags[i] ]) raw6hdulist.close() stopt = time.time() print_inline("") print("") print("Runtime statistics:") print(" runtime = {seconds} sec. = ({minutes} min.)".format( seconds=stopt - startt, minutes=(stopt - startt) / 60.)) print(" processed = " + str(cnt) + " of " + str(nphots) + " events.") if cnt < nphots: print(" WARNING: MISSING EVENTS!") print(" rate = " + str(nphots / (stopt - startt)) + " photons/sec.") print("") return
def photonpipe(outbase, band, raw6file=None, scstfile=None, aspfile=None, ssdfile=None, nullfile=None, verbose=0, retries=20, eclipse=None): """ Apply static and sky calibrations to -raw6 GALEX data, producing fully aspect-corrected and time-tagged photon list files. :param raw6file: Name of the raw6 file to use. :type raw6file: str :param scstfile: Spacecraft state file to use. :type scstfile: str :param band: Name of the band to use, either 'FUV' or 'NUV'. :type band: str :param outbase: Base of the output file names. :type outbase: str :param aspfile: Name of aspect file to use. :type aspfile: int :param ssdfile: Name of Stim Separation Data file to use. :type ssdfile: int :param nullfile: Name of output file to record NULL lines. :type nullfile: int :param verbose: Note used. Included for consistency with other tools. :type verbose: int :param retries: Number of query retries to attempt before giving up. :type retries: int """ startt = time.time() # Scale factor for the time column in the output csv so that it # can be recorded as an int in the database. dbscale = 1000 # This number determines the size of the chunks that gPhoton reads # in from the raw6 for processing. Even if your machine has a lot # of memory, making this number bigger is unlikely to improve the # processing time much because so much is eaten up by the .csv write. chunksz = 1000000 # These are just constants for the mission. detsize = 1.25 # Detector size in degrees pltscl = 68.754932 # Plate scale aspum = pltscl/1000.0 arcsecperpixel = 1.5 xi_xsc, xi_ysc, eta_xsc, eta_ysc = 0., 1., 1., 0. # Determine the eclipse number from the raw6 header. if not raw6file: if not eclipse: raise ValueError('Must specifiy eclipse if no raw6file.') else: raw6file = download_data( eclipse,band,'raw6',datadir=os.path.dirname(outbase)) if raw6file==None: print('Unable to retrieve raw6 file for this eclipse.') return hdulist = pyfits.open(raw6file) hdr = hdulist[0].header hdulist.close() if eclipse and (eclipse!=hdr['eclipse']): # just a consistency check print("Warning: eclipse mismatch {e0} vs. {e1} (header)".format( e0=eclipse,e1=hdr['eclipse'])) eclipse = hdr['eclipse'] print("Processing eclipse "+str(eclipse)+".") # Returns detector constants. print("Band is "+band+".") (xclk, yclk, xcen, ycen, xscl, yscl, xslp, yslp) = clk_cen_scl_slp(band, eclipse) # This determines the values for the post-CSP detector stim scaling # and detector constant corrections. Mx, Bx, My, By, stimsep = 1, 0, 1, 0, 0 if eclipse > 37460: (Mx, Bx, My, By, stimsep, yactbl) = compute_stimstats(raw6file, band, eclipse) wig2, wig2data, wlk2, wlk2data, clk2, clk2data = post_csp_caldata() print("Loading wiggle files...") wiggle_x, _ = cal.wiggle(band, 'x') wiggle_y, _ = cal.wiggle(band, 'y') print("Loading walk files...") walk_x, _ = cal.walk(band, 'x') walk_y, _ = cal.walk(band, 'y') print("Loading linearity files...") linearity_x, _ = cal.linearity(band, 'x') linearity_y, _ = cal.linearity(band, 'y') # This is for the post-CSP stim distortion corrections. print("Loading distortion files...") if eclipse > 37460: print(" Using stim separation of :"+str(stimsep)) distortion_x, disthead = cal.distortion(band, 'x', eclipse, stimsep) distortion_y, _ = cal.distortion(band, 'y', eclipse, stimsep) (cube_x0, cube_dx, cube_y0, cube_dy, cube_d0, cube_dd, cube_nd, cube_nc, cube_nr) = (disthead['DC_X0'], disthead['DC_DX'], disthead['DC_Y0'], disthead['DC_DY'], disthead['DC_D0'], disthead['DC_DD'], disthead['NAXIS3'], disthead['NAXIS1'], disthead['NAXIS2']) if band == 'FUV': if not scstfile: if not eclipse: raise ValueError('Must specifiy eclipse if no scstfile.') else: scstfile = download_data( eclipse,band,'scst',datadir=os.path.dirname(outbase)) if scstfile==None: print('Unable to retrieve SCST file for this eclipse.') return xoffset, yoffset = find_fuv_offset(scstfile) else: xoffset, yoffset = 0., 0. if os.path.isfile(str(ssdfile)): print("SSD file provided: "+str(ssdfile)) stim_coef0, stim_coef1 = get_stim_coefs(ssdfile) elif ssdfile: print("SSD file requested: "+str(ssdfile)) stim_coef0, stim_coef1 = create_ssd(raw6file, band, eclipse, ssdfile) else: print("No SSD file provided or requested.") stim_coef0, stim_coef1 = create_ssd(raw6file, band, eclipse) print(" stim_coef0, stim_coef1 = "+str(stim_coef0)+", "+str(stim_coef1)) print("Loading mask file...") mask, maskinfo = cal.mask(band) npixx = mask.shape[0] npixy = mask.shape[1] pixsz = maskinfo['CDELT2'] maskfill = detsize/(npixx*pixsz) print("Loading aspect data...") if aspfile: (aspra, aspdec, asptwist, asptime, aspheader, aspflags) = load_aspect(aspfile) else: (aspra, aspdec, asptwist, asptime, aspheader, aspflags) = web_query_aspect(eclipse, retries=retries) minasp, maxasp = min(asptime), max(asptime) trange = [minasp, maxasp] print(" trange= ( {t0} , {t1} )".format(t0=trange[0], t1=trange[1])) ra0, dec0, roll0 = aspheader['RA'], aspheader['DEC'], aspheader['ROLL'] print(" [avgRA, avgDEC, avgROLL] = [{RA}, {DEC}, {ROLL}]".format( RA=aspra.mean(), DEC=aspdec.mean(), ROLL=asptwist.mean())) # This projects the aspect solutions onto the MPS field centers. print("Computing aspect vectors...") (xi_vec, eta_vec) = gnomfwd_simple(aspra, aspdec, ra0, dec0, -asptwist, 1.0/36000.0, 0.) print("Loading raw6 file...") raw6hdulist = pyfits.open(raw6file, memmap=1) raw6htab = raw6hdulist[1].header nphots = raw6htab['NAXIS2'] print(" "+str(nphots)+" events") cnt = 0 outfile = outbase+'.csv' print("Preparing output file "+outfile) spreadsheet = csv.writer(open(outfile, 'w'), delimiter=',', quotechar='|', quoting=csv.QUOTE_MINIMAL) # If specified, dump lines with NULLS into a separate csv file. if nullfile: nullfile = outbase+'_NULL.csv' print("Preparing output file "+nullfile) NULLspreadsheet = csv.writer(open(nullfile, 'w'), delimiter=',', quotechar='|', quoting=csv.QUOTE_MINIMAL) print("") for i in range(int(nphots/chunksz)+1): a = time.time() # Start the timer. csvrows = [] chunkbeg, chunkend = i*chunksz, min(nphots,(i+1)*chunksz) chunkid = " "+str(i+1)+" of "+str(int(nphots/chunksz)+1)+": " print_inline(chunkid+"Unpacking raw6 data...") t = np.array(raw6hdulist[1].data.field('t')[chunkbeg:chunkend]) phb1 = np.array( raw6hdulist[1].data.field('phb1')[chunkbeg:chunkend],dtype='int64') phb2 = np.array( raw6hdulist[1].data.field('phb2')[chunkbeg:chunkend],dtype='int64') phb3 = np.array( raw6hdulist[1].data.field('phb3')[chunkbeg:chunkend],dtype='int64') phb4 = np.array( raw6hdulist[1].data.field('phb4')[chunkbeg:chunkend],dtype='int64') phb5 = np.array( raw6hdulist[1].data.field('phb5')[chunkbeg:chunkend],dtype='int64') # Bitwise "decoding" of the raw6 telemetry. q = ((phb4 & 3) << 3) + ((phb5 & 224) >> 5) xb = phb1 >> 5 xamc = ( np.array(((phb1 & 31) << 7), dtype='int16') + np.array(((phb2 & 254) >> 1), dtype='int16') - np.array(((phb1 & 16) << 8), dtype='int16')) yb = ((phb2 & 1) << 2) + ((phb3 & 192) >> 6) yamc = ( np.array(((phb3 & 63) << 6), dtype='int16') + np.array(((phb4 & 252) >> 2), dtype='int16') - np.array(((phb3 & 32) << 7), dtype='int16')) xa = ((phb5 & 16) >> 4) + ((phb5 & 3) << 3) + ((phb5 & 12) >> 1) xraw0 = xb*xclk + xamc yraw0 = yb*yclk + yamc ya = np.array(((((yraw0/(2*yclk) - xraw0/(2*xclk)) + 10)*32) + xa), dtype='int64') % 32 xraw = xraw0 + np.array((((xa+7) % 32) - 16), dtype='int64') * xslp yraw = yraw0 + np.array((((ya+7) % 32) - 16), dtype='int64') * yslp # Centering and scaling. x = (xraw - xcen)*xscl y = (yraw - ycen)*yscl # Post-CSP 'yac' corrections. if eclipse > 37460: x = Mx*x+Bx y = My*y+By yac = rtaph_yac(yactbl, ya, yb, yamc, eclipse) y = y-yac yac = rtaph_yac2(q, xb, yb, ya, y, aspum, wig2, wig2data, wlk2, wlk2data, clk2, clk2data) y = y + yac # Empty these variables for memory management purposes. phb1,phb2,phb3,phb4,phb5,xb,xamc,yb,yamc,xraw0,yraw0,xraw,yraw=[[]]*13 flags = np.zeros(len(t)) # initialized print_inline(chunkid+"Applying wiggle correction...") x_as = x*aspum y_as = y*aspum fptrx = x_as/10. + 240. fptry = y_as/10. + 240. x_as, y_as = [[]]*2 # Memory management. # This and other lines like it below are to verify that the # event is still on the detector. cut = ((fptrx > 0.) & (fptrx < 479.) & (fptry > 0.) & (fptry < 479.)) flags[np.where((cut == False) & (flags == 0))[0]] = 8 ix = np.where(cut == True)[0] fptrx_ix = np.array(fptrx, dtype='int64') fptry_ix = np.array(fptry, dtype='int64') blt = fptrx-fptrx_ix blu = fptry-fptry_ix wigx, wigy = np.zeros(len(t)), np.zeros(len(t)) wigx[ix] = ( (1-blt[ix])*(wiggle_x[xa[ix], fptrx_ix[ix]]) + (blt[ix])*(wiggle_x[xa[ix], fptrx_ix[ix]+1])) wigy[ix] = ( (1-blu[ix])*(wiggle_y[ya[ix], fptry_ix[ix]]) + (blu[ix])*(wiggle_y[ya[ix], fptry_ix[ix]+1])) xdig = x + wigx/(10.*aspum) ydig = y + wigy/(10.*aspum) wigx, wigy = [[]]*2 # Memory management. print_inline(chunkid+"Applying walk correction...") xdig_as = xdig*aspum ydig_as = ydig*aspum fptrx = xdig_as/10. + 240. fptry = ydig_as/10. + 240. xdig_as, ydig_as = [[]]*2 # Memory management. cut = ((fptrx > 0.) & (fptrx < 479.) & (fptry > 0.) & (fptry < 479.)) flags[np.where((cut == False) & (flags == 0))[0]] = 9 ix = np.where(cut == True)[0] fptrx_ix = np.array(fptrx, dtype='int64') fptry_ix = np.array(fptry, dtype='int64') cut[ix] = ((walk_x[q[ix],fptry_ix[ix],fptrx_ix[ix]] != -999) | (walk_x[q[ix],fptry_ix[ix],fptrx_ix[ix]+1] != -999) | (walk_x[q[ix],fptry_ix[ix]+1,fptrx_ix[ix]] != -999) | (walk_x[q[ix],fptry_ix[ix]+1,fptrx_ix[ix]+1] != -999) | (walk_y[q[ix],fptry_ix[ix],fptrx_ix[ix]] != -999) | (walk_y[q[ix],fptry_ix[ix],fptrx_ix[ix]+1] != -999) | (walk_y[q[ix],fptry_ix[ix]+1,fptrx_ix[ix]] != -999) | (walk_y[q[ix],fptry_ix[ix]+1,fptrx_ix[ix]+1] != -999)) flags[np.where((cut == False) & (flags == 0))] = 9 ix = np.where(cut == True)[0] fptrx_ix = np.array(fptrx, dtype='int64') fptry_ix = np.array(fptry, dtype='int64') blt = fptrx-fptrx_ix blu = fptry-fptry_ix walkx, walky = np.zeros(len(t)), np.zeros(len(t)) walkx[ix] = ( (1-blt[ix])*(1-blu[ix])*(walk_x[q[ix],fptry_ix[ix],fptrx_ix[ix]])+ (blt[ix])*(1-blu[ix])*(walk_x[q[ix],fptry_ix[ix],fptrx_ix[ix]+1])+ (1-blt[ix])*(blu[ix])*(walk_x[q[ix],fptry_ix[ix]+1,fptrx_ix[ix]]) + (blt[ix])*(blu[ix])*(walk_x[q[ix],fptry_ix[ix]+1,fptrx_ix[ix]+1])) walky[ix] = ( (1-blt[ix])*(1-blu[ix])*(walk_y[q[ix],fptry_ix[ix],fptrx_ix[ix]]) + (blt[ix])*(1-blu[ix])*(walk_y[q[ix],fptry_ix[ix],fptrx_ix[ix]+1]) + (1-blt[ix])*(blu[ix])*(walk_y[q[ix],fptry_ix[ix]+1,fptrx_ix[ix]]) + (blt[ix])*(blu[ix])*(walk_y[q[ix],fptry_ix[ix]+1,fptrx_ix[ix]+1])) print_inline(chunkid+"Applying spatial non-linearity correction...") xp = xdig - walkx yp = ydig - walky xp_as = xp*aspum yp_as = yp*aspum fptrx = xp_as/10. + 240. fptry = yp_as/10. + 240. xp, yp, walkx, walky = [[]]*4 # Memory management. cut = ((fptrx > 0.) & (fptrx < 479.) & (fptry > 0.) & (fptry < 479.)) flags[np.where((cut == False) & (flags == 0))] = 10 ix = np.where(cut == True)[0] fptrx_ix = np.array(fptrx, dtype='int64') fptry_ix = np.array(fptry, dtype='int64') blt = fptrx-fptrx_ix blu = fptry-fptry_ix dx, dy = np.zeros(len(t)), np.zeros(len(t)) dx[ix] = ( (1-blt[ix])*(1-blu[ix])*linearity_x[fptry_ix[ix],fptrx_ix[ix]]+ (blt[ix])*(1-blu[ix])*linearity_x[fptry_ix[ix],fptrx_ix[ix]+1]+ (1-blt[ix])*(blu[ix])*linearity_x[fptry_ix[ix]+1,fptrx_ix[ix]]+ (blt[ix])*(blu[ix])*linearity_x[fptry_ix[ix]+1,fptrx_ix[ix]+1]) dy[ix] = ( (1-blt[ix])*(1-blu[ix])*linearity_y[fptry_ix[ix],fptrx_ix[ix]]+ (blt[ix])*(1-blu[ix])*linearity_y[fptry_ix[ix],fptrx_ix[ix]+1]+ (1-blt[ix])*(blu[ix])*linearity_y[fptry_ix[ix]+1,fptrx_ix[ix]]+ (blt[ix])*(blu[ix])*linearity_y[fptry_ix[ix]+1,fptrx_ix[ix]+1]) print_inline(chunkid+"Applying stim distortion correction...") ss = stim_coef0 + (t * stim_coef1) # stim separation col, row, depth = np.zeros(len(t)), np.zeros(len(t)), np.zeros(len(t)) col[ix] = (xp_as[ix] - cube_x0) / cube_dx row[ix] = (yp_as[ix] - cube_y0) / cube_dy depth[ix] = (ss[ix] - cube_d0) / cube_dd # [Future]: This throws an error sometimes like the following, may need # fixing... """PhotonPipe.py:262: RuntimeWarning: invalid value encountered in less depth[((depth < 0)).nonzero()[0]] = 0. PhotonPipe.py:263: RuntimeWarning: invalid value encountered in greater_equal depth[((depth >= cube_nd)).nonzero()[0]] = -1. ERROR: IndexError: index -9223372036854775808 is out of bounds for axis 0 with size 17 [PhotonPipe] depth[((depth < 0)).nonzero()[0]] = 0. depth[((depth >= cube_nd)).nonzero()[0]] = -1. """ cut = ((col > -1) & (col < cube_nc) & (row > -1) & (row < cube_nr) & (flags == 0) & (np.array(depth,dtype='int64')<18)) flags[np.where(cut == False)[0]] = 11 ix = np.where(cut == True)[0] xshift, yshift = np.zeros(len(t)), np.zeros(len(t)) xshift[ix] = distortion_x[np.array(depth[ix], dtype='int64'), np.array(row[ix], dtype='int64'),np.array(col[ix], dtype='int64')] yshift[ix] = distortion_y[np.array(depth[ix], dtype='int64'), np.array(row[ix], dtype='int64'),np.array(col[ix], dtype='int64')] xshift = (xshift*arcsecperpixel)+xoffset yshift = (yshift*arcsecperpixel)+yoffset print_inline(chunkid+"Applying hotspot mask...") # The detectors aren't oriented the same way. flip = -1. if band=='FUV' else 1. xi = ( xi_xsc*(flip*(yp_as + dy + yshift)*10.) + xi_ysc*(flip*(xp_as + dx + xshift)*10.)) eta = ( eta_xsc*(flip*(yp_as + dy + yshift)*10.) + eta_ysc*(flip*(xp_as + dx + xshift)*10.)) xp_as,yp_as,depth,ss,dx,dy,xshift,yshift=[[]]*8 # Memory management. col = (((xi/36000.)/(detsize/2.)*maskfill + 1.)/2. * npixx) row = (((eta/36000.)/(detsize/2.)*maskfill + 1.)/2. * npixy) # Off detector at hotspot mask. Should mostly be stims. cut = ((col > 0.) & (col < 799.) & (row > 0.) & (row < 799.)) flags[np.where((cut == False))] = 5 # Clobbers some earlier flags. ix = np.where(cut == True)[0] # Hotspot masks. Must use ix to prevent out-of-bounds error. cut[ix] = (mask[np.array(col, dtype='int64')[ix], np.array(row, dtype='int64')[ix]] == 1.) flags[np.where((cut == False) & (flags == 0))] = 6 ix = np.where(cut == True)[0] col, row = [[]]*2 # Memory management. # This gives the index of the aspect time that comes _before_ # each photon time. Without the '-1' it will give the index # of the aspect time _after_ the photon time. print_inline(chunkid+"Mapping photon times to aspect times...") aspix = np.digitize(t, asptime)-1 print_inline(chunkid+"Applying dither correction...") # Sky-project all data that are bracketed by aspect timestamps. cut = ((aspix > 0) & (aspix < (len(asptime)-1))) flags[np.where((cut == False) & ((flags==0) | (flags == 6)))] = 7 ix = np.where(cut == True)[0] print_inline(chunkid+"Interpolating aspect solutions...") dxi, deta = np.zeros(len(t)), np.zeros(len(t)) dxi[ix] = ( (xi_vec[aspix[ix]+1]-xi_vec[aspix[ix]])* (t[ix]-asptime[aspix[ix]])/ (asptime[aspix[ix]+1]-asptime[aspix[ix]])) deta[ix] = ( (eta_vec[aspix[ix]+1]-eta_vec[aspix[ix]])* (t[ix]-asptime[aspix[ix]])/ (asptime[aspix[ix]+1]-asptime[aspix[ix]])) print_inline(chunkid+"Mapping to sky...") ra, dec = np.zeros(len(t)), np.zeros(len(t)) ra[ix], dec[ix] = gnomrev_simple(xi[ix]+dxi[ix], eta[ix]+deta[ix], aspra[aspix[ix]],aspdec[aspix[ix]], -asptwist[aspix[ix]], 1/36000.,0.) # Flag data that do not have "good" aspect flags on both sides. # "Good" aspect solutions are divisible by 2. cut[ix] = ( ((asptime[aspix[ix]+1]-asptime[aspix[ix]]) == 1) & (aspflags[aspix[ix]]%2 == 0) & (aspflags[aspix[ix]+1]%2 == 0) & (aspflags[aspix[ix]-1]%2 == 0) & (flags[ix] == 0) & (flags[ix] != 7) & (flags[ix] !=6)) flags[np.where((cut == False) & (flags == 0))[0]] = 12 # NOTE: If you wish to add a hook that filters the gPhoton output # (like perhaps by sky position or time range) then add it here. # [Future]: Preprogram a (commented out) filter on RA/Dec. print_inline(chunkid+"Writing to spreadsheet...") # There is a loop here because: # we need to recombine the data into rows to feed to csv.writerow... # numpy.column_stack requires a ton of memory and therefore takes # longer to run than the loop... # so there might be a more Pythonic way, but I don't know it. (cm) for i in range(len(t)): cnt += 1 # To avoid repeat indexing of flags... thisflag = flags[i] # This substitutes empty strings for RA and Dec # values so that when they're dumped into the database # they are correctly recorded as NULL if thisflag in [2,5,7,8,9,10,11,12]: if nullfile: NULLspreadsheet.writerow( [int(t[i]*dbscale), x[i], y[i],xa[i], ya[i], q[i], xi[i], eta[i], "", "", flags[i]]) else: spreadsheet.writerow( [int(t[i]*dbscale), x[i], y[i], xa[i], ya[i], q[i], xi[i], eta[i], "", "", flags[i]]) else: spreadsheet.writerow( [int(t[i]*dbscale), x[i], y[i], xa[i], ya[i], q[i], xi[i], eta[i], ra[i], dec[i], flags[i]]) raw6hdulist.close() stopt = time.time() print_inline("") print("") print("Runtime statistics:") print(" runtime = {seconds} sec. = ({minutes} min.)".format( seconds=stopt-startt, minutes=(stopt-startt)/60.)) print(" processed = "+str(cnt)+" of "+str(nphots)+" events.") if cnt < nphots: print(" WARNING: MISSING EVENTS!") # This should never happen. print(" rate = "+str(nphots/(stopt-startt))+" photons/sec.") print("") return
def mcat_skybg(band, skypos, radius, verbose=0, trange=None, mcat=None, searchradius=0.1): """ Estimate the sky background using the MCAT 'skybg' for nearby sources. :param band: The band to use, either 'FUV' or 'NUV'. :type band: str :param skypos: The right ascension and declination, in degrees. :type skypos: list :param radius: The radius in which to search for MCAT sources in degrees. :type radius: float :param verbose: Verbosity level, a value of 0 is minimum verbosity. :type verbose: int :param trange: Minimum and maximum time (in GALEX time) to consider. :type trange: list :returns: float -- The estimated sky background in the photometric aperture, in counts per second. """ # Search the visit-level MCAT for nearby detections. # Unless the MCAT data has already been handed off for detection purposes. if not mcat: mcat = get_mcat_data(skypos, searchradius) try: # Find the distance to each source. dist = np.array([ angularSeparation(skypos[0], skypos[1], a[0], a[1]) for a in zip(mcat[band]['ra'], mcat[band]['dec']) ]) except TypeError: print_inline('No {b} MCAT sources within {r} degrees of {p}'.format( b=band, r=searchradius, p=skypos)) return np.nan # Find visits that overlap in time. if not trange: tix = (np.array(list(range(len(mcat[band]['mag']))), dtype='int32'), ) else: tix = np.where(( (trange[0] >= mcat[band]['t0']) & (trange[0] <= mcat[band]['t1'])) | ((trange[1] >= mcat[band]['t0']) & (trange[1] <= mcat[band]['t1'])) | ((trange[0] <= mcat[band]['t0']) & (trange[1] >= mcat[band]['t1']))) if not len(tix[0]): print_inline('No concurrent {b} MCAT source nearby.'.format(b=band)) return np.nan # Might not be the preferred behavior here. ix = np.where(dist[tix] == min(dist[tix])) skybg = mcat[band]['skybg'][tix][ix] # This should rarely happen, but sometimes there's a duplicate entry in # the visit-level MCAT. if len(skybg) > 1: # If the skybg array is all the same value, it's a duplicate. if np.all(skybg == skybg[0]): skybg = np.asarray([skybg[0]]) else: skybg = np.asarray([np.median(skybg)]) return skybg[0] * area(radius * 60. * 60.)
def fGetTimeRanges(band, skypos, trange=None, detsize=1.1, verbose=0, maxgap=1., minexp=1., skyrange=None, maxgap_override=False, minexp_override=False): """ Find the contiguous time ranges within a time range at a specific location. :param band: The band to use, either 'FUV' or 'NUV'. :type band: str :param skypos: The right ascension and declination, in degrees. :type skypos: list :param trange: Minimum and maximum time (in GALEX time) to consider. :type trange: list :param detsize: The effective detector diameter, in degrees. :type detsize: float :param verbose: Verbosity level, a value of 0 is minimum verbosity. :type verbose: int :param maxgap: Maximum gap size, in seconds, for data to be considered contiguous. :type maxgap: float :param minexp: Minimum gap size, in seconds, for data to be considered contiguous. :type minexp: float :param skyrange: Values in degrees RA and Dec of a box around skypos that defines the extent of the region of interest. :type skyrange: list :param maxgap_override: Enables an experimental feature where maxgap can be less than one second. :type maxgap_override: bool :returns: numpy.ndarray -- A valid set of time ranges, accounting for minimum exposure lengths and maximum gaps. """ if trange is not None: trange_buffer = (trange[0] - 1, trange[1] + 1) times = get_valid_times(band, skypos, trange=trange_buffer, detsize=detsize, verbose=verbose, skyrange=skyrange) if len(times): times = np.append(times, times[-1] + 1) if times[0] < trange[0]: times[0] = trange[0] if times[-1] > trange[1]: times[-1] = trange[1] else: times = get_valid_times(band, skypos, trange=None, detsize=detsize, verbose=verbose, skyrange=skyrange) if len(times): times = np.append(times, times[-1] + 1) if not len(times): return np.array([[]], dtype='float64') if verbose: print_inline('Parsing ~' + str(len(times) - 1) + ' seconds of raw exposure.') # NOTE: The minimum meaningful maxgap is 1 second. if maxgap < 1 and not maxgap_override: raise ValueError('maxgap must be >=1 second') tranges = distinct_tranges(times, maxgap=maxgap) if not minexp_override: ix = np.where( np.array(tranges)[:, 1] - np.array(tranges)[:, 0] >= minexp) tranges = np.array(tranges)[ix].tolist() return np.array(tranges, dtype='float64')
def raw6_to_stims(raw6file, band, eclipse, margin=90.001): """ Extracts stim events from a raw6 file. :param raw6file: The name of the raw6 FITS file to read. :type raw6file: str :param band: The band to return the stim data for, either 'FUV' or 'NUV'. :type band: str :param eclipse: The eclipse number to return the stim data for. :type eclipse: int :param margin: +/- extent in arcseconds defining stim search box :type margin: float :returns: tuple -- A four-element tuple containing data from each stim. The data from each stim are stored in dicts. """ print("Extracting stim data from ", raw6file, " ...") print(" Using a search box with sides of ", margin, " arcseconds.") # This is unscoped for some reason... so I'm just coding it. (xclk, yclk, xcen, ycen, xscl, yscl, xslp, yslp) = clk_cen_scl_slp(band, eclipse) chunksz = 1000000 print("Loading raw6 file...") raw6hdulist = pyfits.open(raw6file, memmap=1) raw6htab = raw6hdulist[1].header nphots = raw6htab['NAXIS2'] stim1 = ( {'t':np.array([]), 'q':np.array([]), 'xb':np.array([]), 'xamc':np.array([]), 'yamc':np.array([]), 'xa':np.array([]), 'ya':np.array([]), 'x':np.array([]), 'y':np.array([]), 'yb':np.array([]), 'yap':np.array([])} ) stim2 = ( {'t':np.array([]), 'q':np.array([]), 'xb':np.array([]), 'xamc':np.array([]), 'yamc':np.array([]), 'xa':np.array([]), 'ya':np.array([]), 'x':np.array([]), 'y':np.array([]), 'yb':np.array([]), 'yap':np.array([])} ) stim3 = ( {'t':np.array([]), 'q':np.array([]), 'xb':np.array([]), 'xamc':np.array([]), 'yamc':np.array([]), 'xa':np.array([]), 'ya':np.array([]), 'x':np.array([]), 'y':np.array([]), 'yb':np.array([]), 'yap':np.array([])} ) stim4 = ( {'t':np.array([]), 'q':np.array([]), 'xb':np.array([]), 'xamc':np.array([]), 'yamc':np.array([]), 'xa':np.array([]), 'ya':np.array([]), 'x':np.array([]), 'y':np.array([]), 'yb':np.array([]), 'yap':np.array([])} ) print("") for i in range(int(nphots/chunksz)+1): csvrows = [] chunkbeg, chunkend = i*chunksz, (i+1)*chunksz-1 if chunkend > nphots: chunkend = nphots-1 chunkid = " "+str(i+1)+" of "+str(int(nphots/chunksz)+1)+": " print_inline(chunkid+"Unpacking raw6 data...") t = np.array(raw6hdulist[1].data.field('t')[chunkbeg:chunkend]) phb1 = np.array( raw6hdulist[1].data.field('phb1')[chunkbeg:chunkend], dtype='int64') phb2 = np.array( raw6hdulist[1].data.field('phb2')[chunkbeg:chunkend], dtype='int64') phb3 = np.array( raw6hdulist[1].data.field('phb3')[chunkbeg:chunkend], dtype='int64') phb4 = np.array( raw6hdulist[1].data.field('phb4')[chunkbeg:chunkend], dtype='int64') phb5 = np.array( raw6hdulist[1].data.field('phb5')[chunkbeg:chunkend], dtype='int64') q = ((phb4 & 3) << 3) + ((phb5 & 224) >> 5) xb = phb1 >> 5 xamc = (np.array(((phb1 & 31) << 7), dtype='int16') + np.array(((phb2 & 254) >> 1), dtype='int16') - np.array(((phb1 & 16) << 8), dtype='int16')) yb = ((phb2 & 1) << 2) + ((phb3 & 192) >> 6) yamc = (np.array(((phb3 & 63) << 6), dtype='int16') + np.array(((phb4 & 252) >> 2), dtype='int16') - np.array(((phb3 & 32) << 7), dtype='int16')) xa = ((phb5 & 16) >> 4) + ((phb5 & 3) << 3) + ((phb5 & 12) >> 1) xraw0 = xb*xclk + xamc yraw0 = yb*yclk + yamc ya = (np.array(((((yraw0/(2*yclk) - xraw0/(2*xclk)) + 10)*32) + xa), dtype='int64') % 32) xraw = (xraw0 + np.array((((xa+7) % 32) - 16), dtype='int64') * xslp) yraw = (yraw0 + np.array((((ya+7) % 32) - 16), dtype='int64') * yslp) x = (xraw - xcen)*xscl y = (yraw - ycen)*yscl index1, index2, index3, index4 = find_stims_index(x, y, band, eclipse, margin) # [Future] There may well be a better way to do these assignments. stim1['t'] = np.append(stim1['t'], t[index1]) stim1['x'] = np.append(stim1['x'], x[index1]) stim1['y'] = np.append(stim1['y'], y[index1]) stim1['q'] = np.append(stim1['q'], q[index1]) stim1['xa'] = np.append(stim1['xa'], xa[index1]) stim1['xb'] = np.append(stim1['xb'], ya[index1]) stim1['ya'] = np.append(stim1['ya'], ya[index1]) stim1['yb'] = np.append(stim1['yb'], yb[index1]) stim1['xamc'] = np.append(stim1['xamc'], xamc[index1]) stim1['yamc'] = np.append(stim1['yamc'], yamc[index1]) stim1['yap'] = np.append(stim1['yap'], rtaph_yap(ya[index1], yb[index1], yamc[index1])) stim2['t'] = np.append(stim2['t'], t[index2]) stim2['x'] = np.append(stim2['x'], x[index2]) stim2['y'] = np.append(stim2['y'], y[index2]) stim2['q'] = np.append(stim2['q'], q[index2]) stim2['xa'] = np.append(stim2['xa'], xa[index2]) stim2['xb'] = np.append(stim2['xb'], ya[index2]) stim2['ya'] = np.append(stim2['ya'], ya[index2]) stim2['yb'] = np.append(stim2['yb'], yb[index2]) stim2['xamc'] = np.append(stim2['xamc'], xamc[index2]) stim2['yamc'] = np.append(stim2['yamc'], yamc[index2]) stim2['yap'] = np.append(stim2['yap'], rtaph_yap(ya[index2], yb[index2], yamc[index2])) stim3['t'] = np.append(stim3['t'], t[index3]) stim3['x'] = np.append(stim3['x'], x[index3]) stim3['y'] = np.append(stim3['y'], y[index3]) stim3['q'] = np.append(stim3['q'], q[index3]) stim3['xa'] = np.append(stim3['xa'], xa[index3]) stim3['xb'] = np.append(stim3['xb'], ya[index3]) stim3['ya'] = np.append(stim3['ya'], ya[index3]) stim3['yb'] = np.append(stim3['yb'], yb[index3]) stim3['xamc'] = np.append(stim3['xamc'], xamc[index3]) stim3['yamc'] = np.append(stim3['yamc'], yamc[index3]) stim3['yap'] = np.append(stim3['yap'], rtaph_yap(ya[index3], yb[index3], yamc[index3])) stim4['t'] = np.append(stim4['t'], t[index4]) stim4['x'] = np.append(stim4['x'], x[index4]) stim4['y'] = np.append(stim4['y'], y[index4]) stim4['q'] = np.append(stim4['q'], q[index4]) stim4['xa'] = np.append(stim4['xa'], xa[index4]) stim4['xb'] = np.append(stim4['xb'], ya[index4]) stim4['ya'] = np.append(stim4['ya'], ya[index4]) stim4['yb'] = np.append(stim4['yb'], yb[index4]) stim4['xamc'] = np.append(stim4['xamc'], xamc[index4]) stim4['yamc'] = np.append(stim4['yamc'], yamc[index4]) stim4['yap'] = np.append(stim4['yap'], rtaph_yap(ya[index4], yb[index4], yamc[index4])) print_inline(" Done.") return stim1, stim2, stim3, stim4
def make_lightcurve(photon_file, band, stepsz=30., skypos=(24.76279, -17.94948), aper=gt.aper2deg(7), fixed_t0=False, makefile=False, quiet=False, filetag='', lc_filename=None): """ Generate a light curve of a specific target. """ if lc_filename is None: lc_filename = photon_file.replace( '.csv', '-{stepsz}s{filetag}.csv'.format(stepsz=int(stepsz), filetag=filetag)) if os.path.exists(lc_filename) and not makefile: if not quiet: print_inline(' Pre-exists, reading in file...') return pd.read_csv(lc_filename) else: if not quiet: print_inline('Generating {fn}'.format(fn=lc_filename)) events = calibrate_photons(photon_file, band) # Below is a calculation of the re-centering, if desired. skypos_recentered = recenter(events, skypos=skypos) c1 = SkyCoord(ra=skypos[0] * u.degree, dec=skypos[1] * u.degree) c2 = SkyCoord(ra=skypos_recentered[0] * u.degree, dec=skypos_recentered[1] * u.degree) if not quiet: print('Recentering aperture on [{ra}, {dec}]'.format( ra=skypos_recentered[0], dec=skypos_recentered[1])) print("Recenter shift (arcsec): " + str(c1.separation(c2).arcsec)) ix = aper_photons(events, skypos=skypos_recentered, aper=aper) if len(ix[0]) == 0: return [], [], [], [] trange = [ np.floor(np.array(events['t'])[ix].min()), np.ceil(np.array(events['t'])[ix].max()) ] if fixed_t0: # Use this to force NUV and FUV to have the same bins trange[0] = fixed_t0 expt = compute_exptime_array(np.array(events['t'].values), band, trange, stepsz, np.array(events['flags'].values)) counts, tbins, detrads = [], [], [] col, row = np.array(events['col']), np.array(events['row']) detrad = np.sqrt((col - 400)**2 + (row - 400)**2) for t0 in np.arange(trange[0], trange[1], stepsz): tix = np.where((np.array(events['t'])[ix] >= t0) & (np.array(events['t']) < t0 + stepsz)[ix] & (np.array(events['flags'], dtype='int16')[ix] == 0)) tbins += [t0] detrads += [detrad[ix][tix].mean()] if len(tix[0]) == 0: counts += [0.] else: counts += [np.array(events['response'])[ix][tix].sum()] cps = np.array(counts) / np.array(expt) cps_err = np.sqrt(counts) / np.array(expt) lc = pd.DataFrame({ 't0': tbins, 't1': list(np.array(tbins) + stepsz), 'cps': cps, 'cps_err': cps_err, 'flux': gt.counts2flux(cps, band), 'flux_err': gt.counts2flux(cps_err, band), 'counts': counts, 'expt': expt, 'detrad': detrads }) lc['cps_apcorrected'] = apcorrect_cps(lc, band, aper=aper) lc['flux_apcorrected'] = gt.counts2flux(lc['cps_apcorrected'], band) lc.to_csv(lc_filename) return lc