def test_phot(self): """ Test data to compare with generated by running make testphot which runs the equivalent Perl script with the same data. """ fits_filename = self.get_abs_path("data/1616681p22.fits") x_in = 560.06 y_in = 406.51 ap = 4 insky = 11 outsky = 15 maxcount = 30000.0 exptime = 1.0 swidth = outsky - insky apcor = 0.0 hdu = daophot.phot(fits_filename, x_in, y_in, aperture=ap, sky=insky, swidth=swidth, apcor=apcor, maxcount=maxcount, exptime=exptime) def get_first(param): value_list = hdu[param] self.assertEqual(len(value_list), 1) return value_list[0] xcen = get_first("XCENTER") ycen = get_first("YCENTER") mag = get_first("MAG") magerr = get_first("MERR") self.assertAlmostEqual(xcen, 560.0, 1) self.assertAlmostEqual(ycen, 406.6, 1) self.assertAlmostEqual(mag, 24.64, 2) self.assertAlmostEqual(magerr, 0.223, 2)
def test_phot(self): """ Test data to compare with generated by running make testphot which runs the equivalent Perl script with the same data. """ fits_filename = self.get_abs_path("data/1616681p22.fits") x_in = 560.06 y_in = 406.51 ap = 4 insky = 11 outsky = 15 maxcount = 30000.0 exptime = 1.0 swidth = outsky - insky apcor = 0.0 hdu = daophot.phot(fits_filename, x_in, y_in, aperture=ap, sky=insky, swidth=swidth, apcor=apcor, maxcount=maxcount, exptime=exptime) def get_first(param): value_list = hdu["data"][param] assert_that(value_list, has_length(1)) return value_list[0] xcen = get_first("X") ycen = get_first("Y") mag = get_first("MAG") magerr = get_first("MERR") assert_that(xcen, close_to(560.000, DELTA)) assert_that(ycen, close_to(406.600, DELTA)) assert_that(mag, close_to(24.769, DELTA)) # NOTE: minor difference in magnitude error: 0.290 vs 0.291 assert_that(magerr, close_to(0.290, 0.0011))
def align(expnums, ccd, version='s', dry_run=False): """Create a 'shifts' file that transforms the space/flux/time scale of all images to the first image. This function relies on the .fwhm, .trans.jmp, .phot and .zeropoint.used files for inputs. The scaling we are computing here is for use in planting sources into the image at the same sky/flux locations while accounting for motions of sources with time. :param expnums: list of MegaPrime exposure numbers to add artificial KBOs to, the first frame in the list is the reference. :param ccd: which ccd to work on. :param version: Add sources to the 'o', 'p' or 's' images :param dry_run: don't push results to VOSpace. """ # Get the images and supporting files that we need from the VOSpace area # get_image and get_file check if the image/file is already on disk. # re-computed fluxes from the PSF stars and then recompute x/y/flux scaling. # some dictionaries to hold the various scale pos = {} apcor = {} mags = {} zmag = {} mjdates = {} for expnum in expnums: filename = storage.get_image(expnum, ccd=ccd, version=version) zmag[expnum] = storage.get_zeropoint(expnum, ccd, prefix=None, version=version) mjdates[expnum] = float(fits.open(filename)[0].header.get('MJD-OBS')) apcor[expnum] = [ float(x) for x in open( storage.get_file( expnum, ccd=ccd, version=version, ext=storage.APCOR_EXT)).read().split() ] keys = ['crval1', 'cd1_1', 'cd1_2', 'crval2', 'cd2_1', 'cd2_2'] # load the .trans.jmp values into a 'wcs' like dictionary. # .trans.jmp maps current frame to reference frame in pixel coordinates. # the reference frame of all the frames supplied must be the same. shifts = dict( zip(keys, [ float(x) for x in open( storage.get_file( expnum, ccd=ccd, version=version, ext='trans.jmp')).read().split() ])) shifts['crpix1'] = 0.0 shifts['crpix2'] = 0.0 # now create a wcs object based on those transforms, this wcs links the current frame's # pixel coordinates to the reference frame's pixel coordinates. w = get_wcs(shifts) # get the PHOT file that was produced by the mkpsf routine phot = ascii.read(storage.get_file(expnum, ccd=ccd, version=version, ext='phot'), format='daophot') # compute the small-aperture magnitudes of the stars used in the PSF mags[expnum] = daophot.phot(filename, phot['XCENTER'], phot['YCENTER'], aperture=apcor[expnum][0], sky=apcor[expnum][1] + 1, swidth=apcor[expnum][0], zmag=zmag[expnum]) # covert the x/y positions to positions in Frame 1 based on the trans.jmp values. (x, y) = w.wcs_pix2world(mags[expnum]["XCENTER"], mags[expnum]["YCENTER"], 1) pos[expnum] = numpy.transpose([x, y]) # match this exposures PSF stars position against those in the first image of the set. idx1, idx2 = util.match_lists(pos[expnums[0]], pos[expnum]) # compute the magnitdue offset between the current frame and the reference. dmags = numpy.ma.array(mags[expnums[0]]["MAG"] - apcor[expnums[0]][2] - (mags[expnum]["MAG"][idx1] - apcor[expnum][2]), mask=idx1.mask) dmags.sort() # compute the median and determine if that shift is small compared to the scatter. dmag = dmags[int(len(dmags) / 2.0)] if math.fabs(dmag) > 3 * (dmags.std() + 0.01): logging.warn( "Magnitude shift {} between {} and {} is large: {}".format( dmag, expnums[0], expnum, shifts[expnum])) shifts['dmag'] = dmag shifts['emag'] = dmags.std() shifts['nmag'] = len(dmags.mask) - dmags.mask.sum() shifts['dmjd'] = mjdates[expnums[0]] - mjdates[expnum] shift_file = os.path.basename( storage.get_uri(expnum, ccd, version, '.shifts')) fh = open(shift_file, 'w') fh.write( json.dumps(shifts, sort_keys=True, indent=4, separators=(',', ': '))) fh.write('\n') fh.close() if not dry_run: storage.copy( shift_file, os.path.basename( storage.get_uri(expnum, ccd, version, '.shifts')))
def align(expnums, ccd, version='s', prefix='', dry_run=False, force=True): """Create a 'shifts' file that transforms the space/flux/time scale of all images to the first image. This function relies on the .fwhm, .trans.jmp, .phot and .zeropoint.used files for inputs. The scaling we are computing here is for use in planting sources into the image at the same sky/flux locations while accounting for motions of sources with time. :param expnums: list of MegaPrime exposure numbers to add artificial KBOs to, the first frame in the list is the reference. :param ccd: which ccd to work on. :param prefix: put this string in front of expnum when looking for exposure, normally '' or 'fk' :param force: When true run task even if this task is recorded as having succeeded :param version: Add sources to the 'o', 'p' or 's' images :param dry_run: don't push results to VOSpace. """ message = storage.SUCCESS if storage.get_status(task, prefix, expnums[0], version, ccd) and not force: logging.info("{} completed successfully for {} {} {} {}".format( task, prefix, expnums[0], version, ccd)) return # Get the images and supporting files that we need from the VOSpace area # get_image and get_file check if the image/file is already on disk. # re-computed fluxes from the PSF stars and then recompute x/y/flux scaling. # some dictionaries to hold the various scale pos = {} apcor = {} mags = {} zmag = {} mjdates = {} with storage.LoggingManager(task, prefix, expnums[0], ccd, version, dry_run): try: for expnum in expnums: filename = storage.get_image(expnum, ccd=ccd, version=version) zmag[expnum] = storage.get_zeropoint(expnum, ccd, prefix=None, version=version) mjdates[expnum] = float( fits.open(filename)[0].header.get('MJD-OBS')) apcor[expnum] = [ float(x) for x in open( storage.get_file( expnum, ccd=ccd, version=version, ext=storage.APCOR_EXT)).read().split() ] keys = ['crval1', 'cd1_1', 'cd1_2', 'crval2', 'cd2_1', 'cd2_2'] # load the .trans.jmp values into a 'wcs' like dictionary. # .trans.jmp maps current frame to reference frame in pixel coordinates. # the reference frame of all the frames supplied must be the same. shifts = dict( list( zip(keys, [ float(x) for x in open( storage.get_file( expnum, ccd=ccd, version=version, ext='trans.jmp')).read().split() ]))) shifts['crpix1'] = 0.0 shifts['crpix2'] = 0.0 # now create a wcs object based on those transforms, this wcs links the current frame's # pixel coordinates to the reference frame's pixel coordinates. w = get_wcs(shifts) # get the PHOT file that was produced by the mkpsf routine logging.debug("Reading .phot file {}".format(expnum)) phot = ascii.read(storage.get_file(expnum, ccd=ccd, version=version, ext='phot'), format='daophot') # compute the small-aperture magnitudes of the stars used in the PSF logging.debug("Running phot on {}".format(filename)) mags[expnum] = daophot.phot(filename, phot['XCENTER'], phot['YCENTER'], aperture=apcor[expnum][0], sky=apcor[expnum][1] + 1, swidth=apcor[expnum][0], zmag=zmag[expnum]) # covert the x/y positions to positions in Frame 1 based on the trans.jmp values. logging.debug( "Doing the XY translation to refrence frame: {}".format(w)) (x, y) = w.wcs_pix2world(mags[expnum]["XCENTER"], mags[expnum]["YCENTER"], 1) pos[expnum] = numpy.transpose([x, y]) # match this exposures PSF stars position against those in the first image of the set. logging.debug("Matching lists") idx1, idx2 = util.match_lists(pos[expnums[0]], pos[expnum]) # compute the magnitdue offset between the current frame and the reference. dmags = numpy.ma.array( mags[expnums[0]]["MAG"] - apcor[expnums[0]][2] - (mags[expnum]["MAG"][idx1] - apcor[expnum][2]), mask=idx1.mask) dmags.sort() # logging.debug("Computed dmags between input and reference: {}".format(dmags)) error_count = 0 error_count += 1 logging.debug("{}".format(error_count)) # compute the median and determine if that shift is small compared to the scatter. try: midx = int( numpy.sum(numpy.any([~dmags.mask], axis=0)) / 2.0) dmag = float(dmags[midx]) logging.debug("Computed a mag delta of: {}".format(dmag)) except Exception as e: logging.error(str(e)) logging.error( "Failed to compute mag offset between plant and found using: {}" .format(dmags)) dmag = 99.99 error_count += 1 logging.debug("{}".format(error_count)) try: if math.fabs(dmag) > 3 * (dmags.std() + 0.01): logging.warning( "Magnitude shift {} between {} and {} is large: {}" .format(dmag, expnums[0], expnum, shifts)) except Exception as e: logging.error(str(e)) error_count += 1 logging.debug("{}".format(error_count)) shifts['dmag'] = dmag shifts['emag'] = dmags.std() shifts['nmag'] = len(dmags.mask) - dmags.mask.sum() shifts['dmjd'] = mjdates[expnums[0]] - mjdates[expnum] shift_file = os.path.basename( storage.get_uri(expnum, ccd, version, '.shifts')) error_count += 1 logging.debug("{}".format(error_count)) try: fh = open(shift_file, 'w') fh.write( json.dumps(shifts, sort_keys=True, indent=4, separators=(',', ': '), cls=NpEncoder)) fh.write('\n') fh.close() except Exception as e: logging.error( "Creation of SHIFTS file failed while trying to write: {}" .format(shifts)) raise e error_count += 1 logging.debug("{}".format(error_count)) if not dry_run: storage.copy( shift_file, storage.get_uri(expnum, ccd, version, '.shifts')) logging.info(message) except Exception as ex: message = str(ex) logging.error(message) if not dry_run: storage.set_status(task, prefix, expnum, version, ccd, status=message)
def app(): parser = argparse.ArgumentParser() parser.add_argument('image_filenames', nargs='+', help="image to measure the source location on") parser.add_argument('name', help="name of the object being measured") args = parser.parse_args() imdisplay = DS9() for key in ds9_init.keys(): cmd = key.replace("_", " ") value = ds9_init[key] imdisplay.set('{} {}'.format(cmd, value)) imdisplay.set('scale zscale') imdisplay.set('cmap invert') imdisplay.set("frame delete all") ast_filename = get_filename(args.name, exists=True) if os.access(ast_filename, os.R_OK): orbit = BKOrbit(None, ast_filename=ast_filename) print(orbit.summarize()) else: orbit = None images = [] # Load images into DS9 for filename in args.image_filenames: images.append(Image(filename, timekw='UTC-OBS')) print(images[-1].filename) imdisplay.set('frame new') imdisplay.set('scale zscale') imdisplay.set_pyfits(images[-1].hdulist) if orbit is not None: date = images[-1].mid_exposure_time orbit.predict(date.iso, minimum_delta=1 * units.second) imdisplay.set( "regions", "fk5; circle({},{},{}) # colour red".format( orbit.coordinate.ra.degree, orbit.coordinate.dec.degree, 0.0003)) imdisplay.set('frame match wcs') # Measure RA/DEC of images. new_ast_filename = get_filename(args.name) print("Writing measurements to {}".format(new_ast_filename)) observations = {} if orbit is not None: for obs in orbit.observations: observations[date_key(obs)] = obs for idx in range(len(images)): imdisplay.set("frame {}".format(idx + 1)) while True: result = imdisplay.get('iexam key coordinate wcs fk5 degrees') c, ra, dec = result.split() if c in ['h', 'x', 'r', 'q']: break if c == 'q': break if c == 'r': obs = Observation(provisional_name=args.name, null_observation=True, frame=os.path.splitext(images[idx].filename)[0], ra=float(ra), dec=float(dec), mag=None, date=Time(images[idx].mid_exposure_time, precision=5).mpc) observations[date_key(obs)] = obs continue x, y = images[idx].wcs.all_world2pix(float(ra), float(dec), 1) # c, x, y = imdisplay.get('iexam key coordinate image').split() if c != 'h': phot_result = daophot.phot( images[idx].filename, float(x), float(y), aperture=4, sky=20, swidth=4, exptime=images[idx].exptime.to('second').value, datamin=-5 * images[idx].header['SKY_STD'], apcor=0.3, zmag=images[idx].header.get('PHOTZP', None), extno=images[idx].data_extno) mag = phot_result['MAG'][0] x = phot_result['XCENTER'][0] y = phot_result['YCENTER'][0] else: mag = None ra, dec = images[idx].wcs.all_pix2world(float(x), float(y), 1) obs = Observation(provisional_name=args.name, frame=os.path.splitext(images[idx].filename)[0], xpos=x + float(images[idx].header['XCEN']), ypos=y + float(images[idx].header['YCEN']), ra=float(ra), dec=float(dec), mag=mag, date=Time(images[idx].mid_exposure_time, precision=5)) observations[date_key(obs)] = obs with open(new_ast_filename, 'w') as ast: keys = sorted(observations.keys()) for key in keys: ast.write("{}\n".format(observations[key]))