def prepare(filenames, obsparam, header_update, keep_wcs=False,
            flipx=False, flipy=False, rotate=0, man_ra=None,
            man_dec=None, diagnostics=False, display=False):
    """
    prepare wrapper
    output: diagnostic properties
    """

    # start logging
    logging.info('preparing data with parameters: %s' %
                 (', '.join([('%s: %s' % (var, str(val))) for
                             var, val in list(locals().items())])))

    # change FITS file extensions to .fits
    for idx, filename in enumerate(filenames):
        if filename.split('.')[-1] in ['fts', 'FTS', 'FITS', 'fit', 'FIT']:
            os.rename(filename, '.'.join(filename.split('.')[:-1])+'.fits')
            filenames[idx] = '.'.join(filename.split('.')[:-1])+'.fits'
            logging.info('change filename from "%s" to "%s"' %
                         (filename, filenames[idx]))

    # identify keywords for GENERIC telescopes

    # open one sample image file
    hdulist = fits.open(filenames[0], verify='ignore',
                        ignore_missing_end='True')
    header = hdulist[0].header

    # check if this is a single-extension FITS file
    if float(header['NAXIS']) > 2.:
        logging.error('This is not a single-extension FITS file. Please '
                      'extract individual extensions and run them '
                      'individually.')
        sys.exit()
    
    # keywords that have to be implanted into each image
    implants = {}

    # if GENERIC telescope, ask user for header keywords
    if obsparam['telescope_keyword'] is 'GENERIC':
        keywords = {'pixel scale in arcsec/px before binning': 'secpix',
                    'binning factor in both axes': 'binning',
                    'image center RA (keyword or degrees)': 'ra',
                    'image center DEC (keyword or degrees)': 'dec',
                    'filter used (clear if none was used)': 'filter',
                    'observation midtime': 'date_keyword',
                    'exposure time (seconds)': 'exptime'}

        for description, keyword in list(keywords.items()):

            try:
                if obsparam[keyword] in header:
                    continue
            except:
                pass

            inp = input('%s? > ' % description)

            if keyword is 'secpix':
                obsparam[keyword] = (float(inp), float(inp))
            if keyword is 'binning':
                implants['BINX'] = (float(inp), 'PP: user-defined')
                implants['BINY'] = (float(inp), 'PP: user-defined')
            if keyword is 'ra':
                try:
                    implants['OBJCTRA'] = (float(inp), 'PP: user_defined')
                    obsparam['radec_separator'] = 'XXX'
                except TypeError:
                    obsparam['ra'] = inp
                # # check for separator
                # try:
                #     dummy = float(header[inp])
                #     obsparam['radec_separator'] = 'XXX'
                # except ValueError:
                #     if ':' in header[inp]:
                #         obsparam['radec_separator'] = ':'
                #     if ' ' in header[inp].strip():
                #         obsparam['radec_separator'] = ' '
            if keyword is 'dec':
                try:
                    implants['OBJCTDEC'] = (float(inp), 'PP: user_defined')
                    obsparam['radec_separator'] = 'XXX'
                except TypeError:
                    obsparam['dec'] = inp
            if keyword is 'filter':
                implants[obsparam['filter']] = (inp, 'PP: user-defined')
            if keyword is 'date_keyword':
                obsparam['date_keyword'] = inp
            if keyword is 'exptime':
                implants['EXPTIME'] = (float(inp), 'PP: user-defined')

        implants['INSTRUME'] = ('GENERIC', 'PP: manually set')

    # prepare image headers for photometry pipeline

    for filename in filenames:

        if display:
            print('preparing', filename)

        # open image file
        hdulist = fits.open(filename, mode='update', verify='silentfix',
                            ignore_missing_end=True)
        header = hdulist[0].header

        # add other headers, if available
        if len(hdulist) > 1:
            for i in range(len(hdulist)):
                try:
                    header += hdulist[i].header
                except:
                    pass

        # account for flips and rotation in telescope configuration
        # if instrument has several chips...
        if 'chip_id' in obsparam:
            chip_id = header[obsparam['chip_id']]
            this_flipx = obsparam['flipx'][chip_id]
            this_flipy = obsparam['flipy'][chip_id]
            this_rotate = obsparam['rotate'][chip_id]
        # if not...
        else:
            this_flipx = obsparam['flipx']
            this_flipy = obsparam['flipy']
            this_rotate = obsparam['rotate']

        if flipx:
            this_flipx = numpy.invert(this_flipx)
        if flipy:
            this_flipy = numpy.invert(this_flipy)
        if rotate > 0:
            this_rotate += rotate

        # read image data
        imdata = hdulist[0].data

        # check if image is a cube, or a single frame put into a cube
        if len(imdata.shape) > 2:
            # this image is a cube
            if imdata.shape[0] == 1:
                # this is a single image put into a cube
                # turn this into a single-frame fits file
                imdata = imdata[0]
            else:
                # this is really a cube; don't know what to do
                raise TypeError(("%s is a cube FITS file; don't know how to "
                                 "handle this file...") % filename)

        # add header keywords for Source Extractor
        if 'EPOCH' not in header:
            header['EPOCH'] = (2000, 'PP: required for registration')
        # if 'EQUINOX' not in header:
        #     header['EQUINOX'] = (2000, 'PP: required for registration')

        # add header keywords for SCAMP
        header['PHOTFLAG'] = ('F', 'PP: data is not photometric (SCAMP)')
        header['PHOT_K'] = (0.05, 'PP: assumed extinction coefficient')

        
        if not keep_wcs:

            # remove keywords that might collide with fake wcs
            for key in list(header.keys()):
                if re.match('^CD[1,2]_[1,2]', key) is not None:
                    # if key not in obsparam.values():
                    #     header.remove(key)
                    if not toolbox.if_val_in_dict(key, obsparam):
                        header.remove(key)
                elif 'PV' in key and '_' in key:
                    header.remove(key)
                elif key in ['CTYPE1', 'CRPIX1', 'CRVAL1', 'CROTA1',
                             'CROTA2', 'CFINT1', 'CTYPE2', 'CRPIX2',
                             'CRVAL2', 'CFINT2', 'LTM1_1', 'LTM2_2',
                             'WAT0_001', 'LTV1', 'LTV2', 'PIXXMIT',
                             'PIXOFFST', 'PC1_1', 'PC1_2', 'PC2_1', 'PC2_2',
                             #'CUNIT1', 'CUNIT2',
                             'A_ORDER', 'A_0_0',
                             'A_0_1', 'A_0_2', 'A_1_0', 'A_1_1', 'A_2_0',
                             'B_ORDER', 'B_0_0', 'B_0_1', 'B_0_2', 'B_1_0',
                             'B_1_1', 'B_2_0', 'AP_ORDER', 'AP_0_0',
                             'AP_0_1', 'AP_0_2', 'AP_1_0', 'AP_1_1',
                             'AP_2_0', 'BP_ORDER', 'BP_0_0', 'BP_0_1',
                             'BP_0_2', 'BP_1_0', 'BP_1_1', 'BP_2_0',
                             'CDELT1', 'CDELT2', 'CRDELT1', 'CRDELT2']:
                    if not toolbox.if_val_in_dict(key, obsparam):
                        header.remove(key)

            # normalize CUNIT keywords
            try:
                if 'degree' in header['CUNIT1'].lower():
                    header['CUNIT1'] = ('deg')
                if 'degree' in header['CUNIT2'].lower():
                    header['CUNIT2'] = ('deg')
            except KeyError:
                pass
                    
            # if GENERIC telescope, add implants to header
            if obsparam['telescope_keyword'] is 'GENERIC':
                for key, val in list(implants.items()):
                    header[key] = (val[0], val[1])

        # read out image binning mode
        binning = toolbox.get_binning(header, obsparam)

        # add pixel resolution keyword
        header['SECPIXX'] = (obsparam['secpix'][0]*binning[0],
                             'PP: x pixscale after binning')
        header['SECPIXY'] = (obsparam['secpix'][1]*binning[1],
                             'PP: y pixscale after binning')

        # create observation midtime jd 
        if not keep_wcs or 'MIDTIMJD' not in header:
            if obsparam['date_keyword'].find('|') == -1:
                header['MIDTIMJD'] = \
                    (toolbox.dateobs_to_jd(header[obsparam['date_keyword']]) +
                     float(header[obsparam['exptime']])/2./86400.,
                     'PP: obs midtime')
            else:
                datetime = (header[obsparam['date_keyword'].split('|')[0]] +
                        'T' + header[obsparam['date_keyword'].split('|')[1]])
                datetime = datetime.replace('/', '-')
                header['MIDTIMJD'] = (toolbox.dateobs_to_jd(datetime) +
                                float(header[obsparam['exptime']])/2./86400.,
                                      'PP: obs midtime')

        # other keywords
        header['TELINSTR'] = (obsparam['telescope_instrument'],
                              'PP: tel/instr name')
        header['TEL_KEYW'] = (obsparam['telescope_keyword'],
                              'PP: tel/instr keyword')
        header['FILTER'] = (header[obsparam['filter']], 'PP:copied')
        header['EXPTIME'] = (header[obsparam['exptime']], 'PP: copied')
        if obsparam['airmass'] in header:
            header['AIRMASS'] = (header[obsparam['airmass']], 'PP: copied')
        else:
            header['AIRMASS'] = (1, 'PP: fake airmass')

        # check if filter can be translated by PP
        try:
            obsparam['filter_translations'][header[obsparam['filter']]]
        except KeyError:
            logging.warning('cannot translate filter keyword \"' +
                          header[obsparam['filter']] +
                          '\"; assume clear filter')
            header[obsparam['filter']] = 'clear'
        header['FILTER'] = (header[obsparam['filter']], 'PP:copied')


            
        # perform header update
        for key, value in list(header_update.items()):
            if key in header:
                header['_'+key[:6]] = (header[key],
                                       'PP: old value for %s' % key)
            header[key] = (value, 'PP: manually updated')

        # check if OBJECT keyword is available
        if 'OBJECT' not in header:
            header['OBJECT'] = 'None'
        elif len(header['OBJECT'].strip()) == 0:
            header['OBJECT'] = 'None'
            
        # # check if RA, Dec, airmass headers are available;
        # #   else: query horizons
        # # to get approximate information
        # if (obsparam['ra'] not in header or
        #     obsparam['dec'] not in header or
        #     obsparam['airmass'] not in header):

        #     logging.info('Either RA, Dec, or airmass missing from image ' +
        #                  'header; pull approximate information for Horizons')

        #     # obtain approximate ra and dec (and airmass) from JPL Horizons
        #     eph = callhorizons.query(header[obsparam['object']].
        #                              replace('_', ' '))
        #     eph.set_discreteepochs(header['MIDTIMJD'])
        #     try:
        #         n = eph.get_ephemerides(obsparam['observatory_code'])
        #     except ValueError:
        #         logging.warning('Target (%s) is not an asteroid' %
        #                         header[obsparam['object']])
        #         n = None

        #     if n is None:
        #         raise KeyError((('%s is not an asteroid known '
        #                          'to JPL Horizons') %
        #                          header[obsparam['object']]))

        #     header[obsparam['ra']] = (eph['RA'][0],
        #                               'PP: queried from Horizons')
        #     header[obsparam['dec']] = (eph['DEC'][0],
        #                                'PP: queried from Horizons')
        #     header[obsparam['airmass']] = (eph['airmass'][0],
        #                                    'PP: queried from Horizons')

        # add fake wcs information that is necessary to run SCAMP

        
        # read out ra and dec from header
        if obsparam['radec_separator'] == 'XXX':
            ra_deg = float(header[obsparam['ra']])
            dec_deg = float(header[obsparam['dec']])
        else:
            ra_string = header[obsparam['ra']].split(
                obsparam['radec_separator'])
            dec_string = header[obsparam['dec']].split(
                obsparam['radec_separator'])
            ra_deg = 15.*(float(ra_string[0]) +
                          old_div(float(ra_string[1]), 60.) +
                          old_div(float(ra_string[2]), 3600.))
            dec_deg = (abs(float(dec_string[0])) +
                       old_div(float(dec_string[1]), 60.) +
                       old_div(float(dec_string[2]), 3600.))
            if dec_string[0].find('-') > -1:
                dec_deg = -1 * dec_deg

        # transform to equinox J2000, if necessary
        if 'EQUINOX' in header:
            equinox = float(header['EQUINOX'])
            if equinox != 2000.:
                anyeq = SkyCoord(ra=ra_deg*u.deg, dec=dec_deg*u.deg,
                                 frame=FK5,
                                 equinox=Time(equinox, format='jyear',
                                              scale='utc'))
                coo = anyeq.transform_to(ICRS)
                ra_deg = coo.ra.deg
                dec_deg = coo.dec.deg
                header['EQUINOX'] = (2000.0, 'PP: normalized to ICRS')
                
        if man_ra is not None and man_dec is not None:
            ra_deg = float(man_ra)
            dec_deg = float(man_dec)

        # special treatment for UKIRT/WFCAM
        if obsparam['telescope_keyword'] == 'UKIRTWFCAM':
            ra_deg = (float(header['TELRA'])/24.*360. -
                      old_div(float(header['JITTER_X']), 3600.))
            dec_deg = (float(header['TELDEC']) -
                       old_div(float(header['JITTER_Y']), 3600.))

        # apply flips
        xnorm, ynorm = 1, 1
        if this_flipx:
            xnorm = -1
        if this_flipy:
            ynorm = -1

        # check if instrument has a chip offset
        ra_offset, dec_offset = 0, 0
        if (man_ra is None or man_dec is None) and \
           'chip_offset_fixed' in obsparam:
            cid = header[obsparam['chip_id']]
            ra_offset = float(obsparam['chip_offset_fixed'][cid][0])
            dec_offset = float(obsparam['chip_offset_fixed'][cid][1])

        if not keep_wcs:
            # create fake header
            header['RADECSYS'] = ('FK5', 'PP: fake wcs coordinates')
            header['RADESYS'] = ('FK5', 'PP: fake wcs coordinates')
            header['CTYPE1'] = ('RA---TAN', 'PP: fake Coordinate type')
            header['CTYPE2'] = ('DEC--TAN', 'PP: fake Coordinate type')
            header['CRVAL1'] = (ra_deg+ra_offset,
                                'PP: fake Coordinate reference value')
            header['CRVAL2'] = (dec_deg+dec_offset,
                                'PP: fake Coordinate reference value')
            header['CRPIX1'] = (int(old_div(
                float(header[obsparam['extent'][0]]), 2)),
                                'PP: fake Coordinate reference pixel')
            header['CRPIX2'] = (int(old_div(
                float(header[obsparam['extent'][1]]), 2)),
                                'PP: fake Coordinate reference pixel')


            # plugin default distortion parameters, if available
            if 'distort' in obsparam:
                if 'functionof' in obsparam['distort']:
                    pv_dict = obsparam['distort'][header[obsparam['distort']
                                                         ['functionof']]]
                else:
                    pv_dict = obsparam['distort']

                try:
                    for pv_key, pv_val in pv_dict.items():
                        header[pv_key] = (pv_val, 'PP: default distortion')
                except KeyError:
                    logging.error(('No distortion coefficients available for '
                                '%s %s') % (obsparam['distort']['functionof'],
                                            header[obsparam['distort']
                                                   ['functionof']]))
        
            header['CD1_1'] = (xnorm * numpy.cos(this_rotate/180.*numpy.pi) *
                               obsparam['secpix'][0]*binning[0]/3600.,
                               'PP: fake Coordinate matrix')
            header['CD1_2'] = (ynorm * -numpy.sin(this_rotate/180.*numpy.pi) *
                               obsparam['secpix'][1]*binning[1]/3600.,
                               'PP: fake Coordinate matrix')
            header['CD2_1'] = (xnorm * numpy.sin(this_rotate/180.*numpy.pi) *
                               obsparam['secpix'][0]*binning[0]/3600.,
                               'PP: fake Coordinate matrix')
            header['CD2_2'] = (ynorm * numpy.cos(this_rotate/180.*numpy.pi) *
                               obsparam['secpix'][1]*binning[1]/3600.,
                               'PP: fake Coordinate matrix')

        # crop center from LOWELL42 frames
        if obsparam['telescope_keyword'] == 'LOWELL42':
            imdata = imdata[100:-100, 100:-100]
            logging.info('cropping LOWELL42 data')

        # overwrite imdata in case something has been modified
        hdulist[0].data = imdata

        hdulist.flush()
        hdulist.close()

        logging.info('created fake wcs information for image %s' % filename)

    # create diagnostics
    if diagnostics:
        diag.create_index(filenames, os.getcwd(), obsparam, display)

    logging.info('Done! -----------------------------------------------------')

    return None
Beispiel #2
0
def create_index(filenames, directory, obsparam, display=False):
    """
    create index.html
    diagnostic root website for one pipeline process
    """

    if display:
        print('create frame index table and frame images')
    logging.info('create frame index table and frame images')

    # obtain filtername from first image file
    refheader = fits.open(filenames[0], ignore_missing_end=True)[0].header
    filtername = obsparam['filter_translations'][refheader[obsparam['filter']]]

    del (refheader)

    html = "<H2>data directory: %s</H2>\n" % directory

    html += ("<H1>%s/%s-band - Diagnostic Output</H1>\n" + \
               "%d frames total, see full pipeline " + \
               "<A HREF=\"%s\">log</A> for more information\n") % \
               (obsparam['telescope_instrument'], filtername,
                len(filenames),
                '.diagnostics/' +
                _pp_conf.log_filename.split('.diagnostics/')[1])

    ### create frame information table
    html += "<P><TABLE BORDER=\"1\">\n<TR>\n"
    html += "<TH>Idx</TH><TH>Filename</TH><TH>Midtime (JD)</TH>" + \
            "<TH>Objectname</TH><TH>Filter</TH>" + \
            "<TH>Airmass</TH><TH>Exptime (s)</TH>" + \
            "<TH>FoV (arcmin)</TH>\n</TR>\n"

    # fill table and create frames
    filename = filenames
    for idx, filename in enumerate(filenames):

        ### fill table
        hdulist = fits.open(filename, ignore_missing_end=True)
        header = hdulist[0].header

        # read out image binning mode
        binning = toolbox.get_binning(header, obsparam)

        #framefilename = _pp_conf.diagroot + '/' + filename + '.png'
        framefilename = '.diagnostics/' + filename + '.png'

        try:
            objectname = header[obsparam['object']]
        except KeyError:
            objectname = 'Unknown Target'

        html += ("<TR><TD>%d</TD><TD><A HREF=\"%s\">%s</A></TD>" + \
                 "<TD>%16.8f</TD><TD>%s</TD>" + \
                 "<TD>%s</TD><TD>%4.2f</TD><TD>%.1f</TD>" + \
                 "<TD>%.1f x %.1f</TD>\n</TR>\n") % \
            (idx+1, framefilename, filename, header["MIDTIMJD"],
             objectname,
             header[obsparam['filter']],
             float(header[obsparam['airmass']]),
             float(header[obsparam['exptime']]),
             header[obsparam['extent'][0]]*obsparam['secpix'][0]*binning[0]/60.,
             header[obsparam['extent'][1]]*obsparam['secpix'][1]*binning[1]/60.)

        ### create frame image
        imgdat = hdulist[0].data
        imgdat = imresize(imgdat,
                          min(1., 1000. / numpy.max(imgdat.shape)),
                          interp='nearest')
        # resize image larger than 1000px on one side

        median = numpy.median(
            imgdat[int(imgdat.shape[1] * 0.25):int(imgdat.shape[1] * 0.75),
                   int(imgdat.shape[0] * 0.25):int(imgdat.shape[0] * 0.75)])
        std = numpy.std(
            imgdat[int(imgdat.shape[1] * 0.25):int(imgdat.shape[1] * 0.75),
                   int(imgdat.shape[0] * 0.25):int(imgdat.shape[0] * 0.75)])

        plt.figure(figsize=(5, 5))

        img = plt.imshow(imgdat,
                         cmap='gray',
                         vmin=median - 1.5 * std,
                         vmax=median + 1.5 * std,
                         origin='lower')
        # remove axes
        plt.axis('off')
        img.axes.get_xaxis().set_visible(False)
        img.axes.get_yaxis().set_visible(False)

        plt.savefig(framefilename,
                    format='png',
                    bbox_inches='tight',
                    pad_inches=0,
                    dpi=200)

        plt.close()
        hdulist.close()
        del (imgdat)

    html += '</TABLE>\n'

    create_website(_pp_conf.index_filename, html)

    ### add to summary website, if requested
    if _pp_conf.use_diagnostics_summary:
        add_to_summary(header[obsparam['object']], filtername, len(filenames))

    return None
def prepare(filenames, obsparam, header_update, keep_wcs=False,
            flipx=False, flipy=False, rotate=0, man_ra=None,
            man_dec=None, diagnostics=False, display=False):
    """
    prepare wrapper
    output: diagnostic properties
    """

    # start logging
    logging.info('preparing data with parameters: %s' %
                 (', '.join([('%s: %s' % (var, str(val))) for
                             var, val in list(locals().items())])))

    # change FITS file extensions to .fits
    for idx, filename in enumerate(filenames):
        if filename.split('.')[-1] in ['fts', 'FTS', 'FITS', 'fit', 'FIT']:
            os.rename(filename, '.'.join(filename.split('.')[:-1])+'.fits')
            filenames[idx] = '.'.join(filename.split('.')[:-1])+'.fits'
            logging.info('change filename from "%s" to "%s"' %
                         (filename, filenames[idx]))

    # identify keywords for GENERIC telescopes

    # open one sample image file
    hdulist = fits.open(filenames[0], verify='ignore',
                        ignore_missing_end='True')
    header = hdulist[0].header

    # check if this is a single-extension FITS file
    if float(header['NAXIS']) > 2.:
        logging.error('This is not a single-extension FITS file. Please '
                      'extract individual extensions and run them '
                      'individually.')
        sys.exit()

    # keywords that have to be implanted into each image
    implants = {}

    # if GENERIC telescope, ask user for header keywords
    if obsparam['telescope_keyword'] is 'GENERIC':
        keywords = {'pixel scale in arcsec/px before binning': 'secpix',
                    'binning factor in both axes': 'binning',
                    'image center RA (keyword or degrees)': 'ra',
                    'image center DEC (keyword or degrees)': 'dec',
                    'filter used (clear if none was used)': 'filter',
                    'observation midtime': 'date_keyword',
                    'exposure time (seconds)': 'exptime'}

        for description, keyword in list(keywords.items()):

            try:
                if obsparam[keyword] in header:
                    continue
            except:
                pass

            inp = input('%s? > ' % description)

            if keyword is 'secpix':
                obsparam[keyword] = (float(inp), float(inp))
            if keyword is 'binning':
                implants['BINX'] = (float(inp), 'PP: user-defined')
                implants['BINY'] = (float(inp), 'PP: user-defined')
            if keyword is 'ra':
                try:
                    implants['OBJCTRA'] = (float(inp), 'PP: user_defined')
                    obsparam['radec_separator'] = 'XXX'
                except TypeError:
                    obsparam['ra'] = inp
                # # check for separator
                # try:
                #     dummy = float(header[inp])
                #     obsparam['radec_separator'] = 'XXX'
                # except ValueError:
                #     if ':' in header[inp]:
                #         obsparam['radec_separator'] = ':'
                #     if ' ' in header[inp].strip():
                #         obsparam['radec_separator'] = ' '
            if keyword is 'dec':
                try:
                    implants['OBJCTDEC'] = (float(inp), 'PP: user_defined')
                    obsparam['radec_separator'] = 'XXX'
                except TypeError:
                    obsparam['dec'] = inp
            if keyword is 'filter':
                implants[obsparam['filter']] = (inp, 'PP: user-defined')
            if keyword is 'date_keyword':
                obsparam['date_keyword'] = inp
            if keyword is 'exptime':
                implants['EXPTIME'] = (float(inp), 'PP: user-defined')

        implants['INSTRUME'] = ('GENERIC', 'PP: manually set')

    # prepare image headers for photometry pipeline

    for filename in filenames:

        if display:
            print('preparing', filename)

        # open image file
        hdulist = fits.open(filename, mode='update', verify='silentfix',
                            ignore_missing_end=True)
        header = hdulist[0].header

        # add other headers, if available
        if len(hdulist) > 1:
            for i in range(len(hdulist)):
                try:
                    header += hdulist[i].header
                except:
                    pass

        # account for flips and rotation in telescope configuration
        # if instrument has several chips...
        if 'chip_id' in obsparam:
            chip_id = header[obsparam['chip_id']]
            this_flipx = obsparam['flipx'][chip_id]
            this_flipy = obsparam['flipy'][chip_id]
            this_rotate = obsparam['rotate'][chip_id]
        # if not...
        else:
            this_flipx = obsparam['flipx']
            this_flipy = obsparam['flipy']
            this_rotate = obsparam['rotate']

        if flipx:
            this_flipx = numpy.invert(this_flipx)
        if flipy:
            this_flipy = numpy.invert(this_flipy)
        if rotate > 0:
            this_rotate += rotate

        # read image data
        imdata = hdulist[0].data

        # check if image is a cube, or a single frame put into a cube
        if len(imdata.shape) > 2:
            # this image is a cube
            if imdata.shape[0] == 1:
                # this is a single image put into a cube
                # turn this into a single-frame fits file
                imdata = imdata[0]
            else:
                # this is really a cube; don't know what to do
                raise TypeError(("%s is a cube FITS file; don't know how to "
                                 "handle this file...") % filename)

        # add header keywords for Source Extractor
        if 'EPOCH' not in header:
            header['EPOCH'] = (2000, 'PP: required for registration')

        # add header keywords for SCAMP
        header['PHOTFLAG'] = ('F', 'PP: data is not photometric (SCAMP)')
        header['PHOT_K'] = (0.05, 'PP: assumed extinction coefficient')

        if not keep_wcs:

            # remove keywords that might collide with fake wcs
            for key in list(header.keys()):
                if re.match('^CD[1,2]_[1,2]', key) is not None:
                    # if key not in obsparam.values():
                    #     header.remove(key)
                    if not toolbox.if_val_in_dict(key, obsparam):
                        header.remove(key)
                elif 'PV' in key and '_' in key:
                    header.remove(key)
                elif key in (['CTYPE1', 'CRPIX1', 'CRVAL1', 'CROTA1',
                              'CROTA2', 'CFINT1', 'CTYPE2', 'CRPIX2',
                              'CRVAL2', 'CFINT2', 'LTM1_1', 'LTM2_2',
                              'WAT0_001', 'LTV1', 'LTV2', 'PIXXMIT',
                              'PIXOFFST', 'PC1_1', 'PC1_2', 'PC2_1', 'PC2_2',
                              'A_ORDER', 'A_0_0',
                              'A_0_1', 'A_0_2', 'A_1_0', 'A_1_1', 'A_2_0',
                              'B_ORDER', 'B_0_0', 'B_0_1', 'B_0_2', 'B_1_0',
                              'B_1_1', 'B_2_0', 'AP_ORDER', 'AP_0_0',
                              'AP_0_1', 'AP_0_2', 'AP_1_0', 'AP_1_1',
                              'AP_2_0', 'BP_ORDER', 'BP_0_0', 'BP_0_1',
                              'BP_0_2', 'BP_1_0', 'BP_1_1', 'BP_2_0',
                              'CDELT1', 'CDELT2', 'CRDELT1', 'CRDELT2'] +
                             ['TR{}_{}'.format(i, j)
                              for i in range(1, 3)
                              for j in range(15)]):
                    if not toolbox.if_val_in_dict(key, obsparam):
                        header.remove(key)

            # normalize CUNIT keywords
            try:
                if 'degree' in header['CUNIT1'].lower():
                    header['CUNIT1'] = ('deg')
                if 'degree' in header['CUNIT2'].lower():
                    header['CUNIT2'] = ('deg')
            except KeyError:
                pass

            # if GENERIC telescope, add implants to header
            if obsparam['telescope_keyword'] is 'GENERIC':
                for key, val in list(implants.items()):
                    header[key] = (val[0], val[1])

        # read out image binning mode
        binning = toolbox.get_binning(header, obsparam)

        # add pixel resolution keyword
        header['SECPIXX'] = (obsparam['secpix'][0]*binning[0],
                             'PP: x pixscale after binning')
        header['SECPIXY'] = (obsparam['secpix'][1]*binning[1],
                             'PP: y pixscale after binning')

        # create observation midtime jd
        if not keep_wcs or 'MIDTIMJD' not in header:
            if obsparam['date_keyword'].find('|') == -1:
                header['MIDTIMJD'] = \
                    (toolbox.dateobs_to_jd(header[obsparam['date_keyword']]) +
                     float(header[obsparam['exptime']])/2./86400.,
                     'PP: obs midtime')
            else:
                datetime = (header[obsparam['date_keyword'].split('|')[0]] +
                            'T' + header[obsparam['date_keyword'].split('|')[1]])
                datetime = datetime.replace('/', '-')
                header['MIDTIMJD'] = (toolbox.dateobs_to_jd(datetime) +
                                      float(
                                          header[obsparam['exptime']])/2./86400.,
                                      'PP: obs midtime')

        # other keywords
        header['TELINSTR'] = (obsparam['telescope_instrument'],
                              'PP: tel/instr name')
        header['TEL_KEYW'] = (obsparam['telescope_keyword'],
                              'PP: tel/instr keyword')

        header[obsparam['filter']] = header[obsparam['filter']].strip()
        header['FILTER'] = (header[obsparam['filter']], 'PP:copied')
        header['EXPTIME'] = (header[obsparam['exptime']], 'PP: copied')
        if obsparam['airmass'] in header:
            header['AIRMASS'] = (header[obsparam['airmass']], 'PP: copied')
        else:
            header['AIRMASS'] = (1, 'PP: fake airmass')

        # check if filter can be translated by PP
        try:
            obsparam['filter_translations'][header[obsparam['filter']]]
        except KeyError:
            logging.warning('cannot translate filter keyword \"' +
                            header[obsparam['filter']] +
                            '\"')
            # header[obsparam['filter']] = 'clear'
        header['FILTER'] = (header[obsparam['filter']], 'PP:copied')

        # perform header update
        for key, value in list(header_update.items()):
            if key in header:
                header['_'+key[:6]] = (header[key],
                                       'PP: old value for %s' % key)
            header[key] = (value, 'PP: manually updated')

        # check if OBJECT keyword is available
        if 'OBJECT' not in header:
            header['OBJECT'] = 'None'
        elif len(header['OBJECT'].strip()) == 0:
            header['OBJECT'] = 'None'

        # add fake wcs information that is necessary to run SCAMP

        # read out ra and dec from header
        if obsparam['radec_separator'] == 'XXX':
            ra_deg = float(header[obsparam['ra']])
            dec_deg = float(header[obsparam['dec']])
        else:
            ra_string = header[obsparam['ra']].split(
                obsparam['radec_separator'])
            dec_string = header[obsparam['dec']].split(
                obsparam['radec_separator'])
            ra_deg = 15.*(float(ra_string[0]) +
                          float(ra_string[1]) / 60. +
                          float(ra_string[2]) / 3600.)
            dec_deg = (abs(float(dec_string[0])) +
                       float(dec_string[1]) / 60. +
                       float(dec_string[2]) / 3600.)
            if dec_string[0].find('-') > -1:
                dec_deg = -1 * dec_deg

        # transform to equinox J2000, if necessary
        if 'EQUINOX' in header:
            equinox = float(header['EQUINOX'])
            if equinox != 2000.:
                anyeq = SkyCoord(ra=ra_deg*u.deg, dec=dec_deg*u.deg,
                                 frame=FK5,
                                 equinox=Time(equinox, format='jyear',
                                              scale='utc'))
                coo = anyeq.transform_to(ICRS)
                ra_deg = coo.ra.deg
                dec_deg = coo.dec.deg
                header['EQUINOX'] = (2000.0, 'PP: normalized to ICRS')
        else:
            header['EQUINOX'] = (2000, 'added by PP')

        if man_ra is not None and man_dec is not None:
            ra_deg = float(man_ra)
            dec_deg = float(man_dec)

        # special treatment for UKIRT/WFCAM
        if obsparam['telescope_keyword'] == 'UKIRTWFCAM':
            try:
                ra_deg = (float(header['TELRA'])/24.*360. -
                          float(header['JITTER_X'])/3600)
                dec_deg = (float(header['TELDEC']) -
                           float(header['JITTER_Y'])/3600)
            except KeyError:
                # JITTER keywords not in combined images
                pass

        # apply flips
        xnorm, ynorm = 1, 1
        if this_flipx:
            xnorm = -1
        if this_flipy:
            ynorm = -1

        # check if instrument has a chip offset
        ra_offset, dec_offset = 0, 0
        if (man_ra is None or man_dec is None) and \
           'chip_offset_fixed' in obsparam:
            cid = header[obsparam['chip_id']]
            ra_offset = float(obsparam['chip_offset_fixed'][cid][0])
            dec_offset = float(obsparam['chip_offset_fixed'][cid][1])

        if not keep_wcs:
            # create fake header
            header['RADECSYS'] = ('FK5', 'PP: fake wcs coordinates')
            header['RADESYS'] = ('FK5', 'PP: fake wcs coordinates')
            header['CTYPE1'] = ('RA---TAN', 'PP: fake Coordinate type')
            header['CTYPE2'] = ('DEC--TAN', 'PP: fake Coordinate type')
            header['CRVAL1'] = (ra_deg+ra_offset,
                                'PP: fake Coordinate reference value')
            header['CRVAL2'] = (dec_deg+dec_offset,
                                'PP: fake Coordinate reference value')
            header['CRPIX1'] = (int(float(header[obsparam['extent'][0]])/2),
                                'PP: fake Coordinate reference pixel')
            header['CRPIX2'] = (int(float(header[obsparam['extent'][1]])/2),
                                'PP: fake Coordinate reference pixel')

            # plugin default distortion parameters, if available
            if 'distort' in obsparam:
                if 'functionof' in obsparam['distort']:
                    pv_dict = obsparam['distort'][header[obsparam['distort']
                                                         ['functionof']]]
                else:
                    pv_dict = obsparam['distort']

                try:
                    for pv_key, pv_val in pv_dict.items():
                        header[pv_key] = (pv_val, 'PP: default distortion')
                except KeyError:
                    logging.error(('No distortion coefficients available for '
                                   '%s %s') % (obsparam['distort']['functionof'],
                                               header[obsparam['distort']
                                                      ['functionof']]))

            header['CD1_1'] = (xnorm * numpy.cos(this_rotate/180.*numpy.pi) *
                               obsparam['secpix'][0]*binning[0]/3600.,
                               'PP: fake Coordinate matrix')
            header['CD1_2'] = (ynorm * -numpy.sin(this_rotate/180.*numpy.pi) *
                               obsparam['secpix'][1]*binning[1]/3600.,
                               'PP: fake Coordinate matrix')
            header['CD2_1'] = (xnorm * numpy.sin(this_rotate/180.*numpy.pi) *
                               obsparam['secpix'][0]*binning[0]/3600.,
                               'PP: fake Coordinate matrix')
            header['CD2_2'] = (ynorm * numpy.cos(this_rotate/180.*numpy.pi) *
                               obsparam['secpix'][1]*binning[1]/3600.,
                               'PP: fake Coordinate matrix')

        # crop center from LOWELL42 frames
        if obsparam['telescope_keyword'] == 'LOWELL42':
            imdata = imdata[100:-100, 100:-100]
            logging.info('cropping LOWELL42 data')

        # overwrite imdata in case something has been modified
        hdulist[0].data = imdata

        hdulist.flush()
        hdulist.close()

        logging.info('created fake wcs information for image %s' % filename)

    # create diagnostics
    if diagnostics:
        if display:
            print('creating diagnostic output')
        logging.info(' ~~~~~~~~~ creating diagnostic output')
        diag.add_index(filenames, os.getcwd(), obsparam)

    logging.info('Done! -----------------------------------------------------')

    return None