def read_tess_fitslc(lcfits,
                     headerkeys=LCHEADERKEYS,
                     datakeys=LCDATAKEYS,
                     sapkeys=LCSAPKEYS,
                     pdckeys=LCPDCKEYS,
                     topkeys=LCTOPKEYS,
                     apkeys=LCAPERTUREKEYS,
                     normalize=False,
                     appendto=None,
                     filterqualityflags=False,
                     nanfilter=None,
                     timestoignore=None):
    '''This extracts the light curve from a single TESS .lc.fits file.

    This works on the light curves available at MAST.

    TODO: look at:

    https://archive.stsci.edu/missions/tess/doc/EXP-TESS-ARC-ICD-TM-0014.pdf

    for details on the column descriptions and to fill in any other info we
    need.

    Parameters
    ----------

    lcfits : str
        The filename of a MAST Kepler/K2 light curve FITS file.

    headerkeys : list
        A list of FITS header keys that will be extracted from the FITS light
        curve file. These describe the observations. The default value for this
        is given in `LCHEADERKEYS` above.

    datakeys : list
        A list of FITS column names that correspond to the auxiliary
        measurements in the light curve. The default is `LCDATAKEYS` above.

    sapkeys : list
        A list of FITS column names that correspond to the SAP flux
        measurements in the light curve. The default is `LCSAPKEYS` above.

    pdckeys : list
        A list of FITS column names that correspond to the PDC flux
        measurements in the light curve. The default is `LCPDCKEYS` above.

    topkeys : list
        A list of FITS header keys that describe the object in the light
        curve. The default is `LCTOPKEYS` above.

    apkeys : list
        A list of FITS header keys that describe the flux measurement apertures
        used by the TESS pipeline. The default is `LCAPERTUREKEYS` above.

    normalize : bool
        If True, then the light curve's SAP_FLUX and PDCSAP_FLUX measurements
        will be normalized to 1.0 by dividing out the median flux for the
        component light curve.

    appendto : lcdict or None
        If appendto is an `lcdict`, will append measurements of this `lcdict` to
        that `lcdict`. This is used for consolidating light curves for the same
        object across different files (sectors/cameras/CCDs?). The appending
        does not care about the time order. To consolidate light curves in time
        order, use `consolidate_tess_fitslc` below.

    filterqualityflags : bool
        If True, will remove any measurements that have non-zero quality flags
        present. This usually indicates an issue with the instrument or
        spacecraft.

    nanfilter : {'sap','pdc','sap,pdc'} or None
        Indicates the flux measurement type(s) to apply the filtering to.

    timestoignore : list of tuples or None
        This is of the form::

            [(time1_start, time1_end), (time2_start, time2_end), ...]

        and indicates the start and end times to mask out of the final
        lcdict. Use this to remove anything that wasn't caught by the quality
        flags.

    Returns
    -------

    lcdict
        Returns an `lcdict` (this is useable by most astrobase functions for LC
        processing).

    '''

    # read the fits file
    hdulist = pyfits.open(lcfits)
    lchdr, lcdata = hdulist[1].header, hdulist[1].data
    lctophdr, lcaperturehdr, lcaperturedata = (hdulist[0].header,
                                               hdulist[2].header,
                                               hdulist[2].data)
    hdulist.close()

    hdrinfo = {}

    # now get the values we want from the header
    for key in headerkeys:
        if key in lchdr and lchdr[key] is not None:
            hdrinfo[key.lower()] = lchdr[key]
        else:
            hdrinfo[key.lower()] = None

    # get the number of detections
    ndet = lchdr['NAXIS2']

    # get the info from the topheader
    for key in topkeys:
        if key in lctophdr and lctophdr[key] is not None:
            hdrinfo[key.lower()] = lctophdr[key]
        else:
            hdrinfo[key.lower()] = None

    # get the info from the lcaperturehdr
    for key in lcaperturehdr:
        if key in lcaperturehdr and lcaperturehdr[key] is not None:
            hdrinfo[key.lower()] = lcaperturehdr[key]
        else:
            hdrinfo[key.lower()] = None


    # if we're appending to another lcdict
    if appendto and isinstance(appendto, dict):

        lcdict = appendto

        # update lcinfo
        lcdict['lcinfo']['timesys'].append(hdrinfo['timesys'])
        lcdict['lcinfo']['bjdoffset'].append(
            hdrinfo['bjdrefi'] + hdrinfo['bjdreff']
        )
        lcdict['lcinfo']['lcaperture'].append(lcaperturedata)
        lcdict['lcinfo']['aperpix_used'].append(hdrinfo['npixsap'])
        lcdict['lcinfo']['aperpix_unused'].append(hdrinfo['npixmiss'])
        lcdict['lcinfo']['pixarcsec'].append(
            (np.abs(hdrinfo['cdelt1']) +
             np.abs(hdrinfo['cdelt2']))*3600.0/2.0
        )
        lcdict['lcinfo']['ndet'].append(ndet)
        lcdict['lcinfo']['exptime'].append(hdrinfo['exposure'])
        lcdict['lcinfo']['sector'].append(hdrinfo['sector'])
        lcdict['lcinfo']['camera'].append(hdrinfo['camera'])
        lcdict['lcinfo']['ccd'].append(hdrinfo['ccd'])

        lcdict['lcinfo']['date_obs_start'].append(hdrinfo['date-obs'])
        lcdict['lcinfo']['date_obs_end'].append(hdrinfo['date-end'])
        lcdict['lcinfo']['pixel_table_id'].append(hdrinfo['pxtable'])
        lcdict['lcinfo']['origin'].append(hdrinfo['origin'])
        lcdict['lcinfo']['datarelease'].append(hdrinfo['data_rel'])
        lcdict['lcinfo']['procversion'].append(hdrinfo['procver'])


        lcdict['lcinfo']['tic_version'].append(hdrinfo['ticver'])
        lcdict['lcinfo']['cr_mitigation'].append(hdrinfo['crmiten'])
        lcdict['lcinfo']['cr_blocksize'].append(hdrinfo['crblksz'])
        lcdict['lcinfo']['cr_spocclean'].append(hdrinfo['crspoc'])

        # update the varinfo for this light curve
        lcdict['varinfo']['cdpp0_5'].append(hdrinfo['cdpp0_5'])
        lcdict['varinfo']['cdpp1_0'].append(hdrinfo['cdpp1_0'])
        lcdict['varinfo']['cdpp2_0'].append(hdrinfo['cdpp2_0'])
        lcdict['varinfo']['pdcvar'].append(hdrinfo['pdcvar'])
        lcdict['varinfo']['pdcmethod'].append(hdrinfo['pdcmethd'])
        lcdict['varinfo']['target_flux_total_flux_ratio_in_aper'].append(
            hdrinfo['crowdsap']
        )
        lcdict['varinfo']['target_flux_fraction_in_aper'].append(
            hdrinfo['flfrcsap']
        )

        # update the light curve columns now
        for key in datakeys:

            if key.lower() in lcdict:
                lcdict[key.lower()] = (
                    np.concatenate((lcdict[key.lower()], lcdata[key]))
                )

        for key in sapkeys:

            if key.lower() in lcdict['sap']:

                sapflux_median = np.nanmedian(lcdata['SAP_FLUX'])

                # normalize the current flux measurements if needed
                if normalize and key == 'SAP_FLUX':
                    thislcdata = lcdata[key] / sapflux_median
                elif normalize and key == 'SAP_FLUX_ERR':
                    thislcdata = lcdata[key] / sapflux_median
                elif normalize and key == 'SAP_BKG':
                    thislcdata = lcdata[key] / sapflux_median
                elif normalize and key == 'SAP_BKG_ERR':
                    thislcdata = lcdata[key] / sapflux_median
                else:
                    thislcdata = lcdata[key]

                lcdict['sap'][key.lower()] = (
                    np.concatenate((lcdict['sap'][key.lower()], thislcdata))
                )

        for key in pdckeys:

            if key.lower() in lcdict['pdc']:

                pdcsap_flux_median = np.nanmedian(lcdata['PDCSAP_FLUX'])

                # normalize the current flux measurements if needed
                if normalize and key == 'PDCSAP_FLUX':
                    thislcdata = lcdata[key] / pdcsap_flux_median
                elif normalize and key == 'PDCSAP_FLUX_ERR':
                    thislcdata = lcdata[key] / pdcsap_flux_median
                else:
                    thislcdata = lcdata[key]

                lcdict['pdc'][key.lower()] = (
                    np.concatenate((lcdict['pdc'][key.lower()], thislcdata))
                )


        # append some of the light curve information into existing numpy arrays
        # so we can sort on them later
        lcdict['exptime'] = np.concatenate(
            (lcdict['exptime'],
             np.full_like(lcdata['TIME'],
                          hdrinfo['exposure'],
                          dtype=np.float64))
        )
        lcdict['sector'] = np.concatenate(
            (lcdict['sector'],
             np.full_like(lcdata['TIME'],
                          hdrinfo['sector'],
                          dtype=np.int64))
        )
        lcdict['camera'] = np.concatenate(
            (lcdict['camera'],
             np.full_like(lcdata['TIME'],
                          hdrinfo['camera'],
                          dtype=np.int64))
        )
        lcdict['ccd'] = np.concatenate(
            (lcdict['ccd'],
             np.full_like(lcdata['TIME'],
                          hdrinfo['ccd'],
                          dtype=np.int64))
        )

        lcdict['pixel_table_id'] = np.concatenate(
            (lcdict['pixel_table_id'],
             np.full_like(lcdata['TIME'],
                          hdrinfo['pxtable'],
                          dtype=np.int64))
        )
        lcdict['origin'] = np.concatenate(
            (lcdict['origin'],
             np.full_like(lcdata['TIME'],
                          hdrinfo['origin'],
                          dtype='U100'))
        )
        lcdict['date_obs_start'] = np.concatenate(
            (lcdict['date_obs_start'],
             np.full_like(lcdata['TIME'],
                          hdrinfo['date-obs'],
                          dtype='U100'))
        )
        lcdict['date_obs_end'] = np.concatenate(
            (lcdict['date_obs_end'],
             np.full_like(lcdata['TIME'],
                          hdrinfo['date-end'],
                          dtype='U100'))
        )
        lcdict['procversion'] = np.concatenate(
            (lcdict['procversion'],
             np.full_like(lcdata['TIME'],
                          hdrinfo['procver'],
                          dtype='U255'))
        )
        lcdict['datarelease'] = np.concatenate(
            (lcdict['datarelease'],
             np.full_like(lcdata['TIME'],
                          hdrinfo['data_rel'],
                          dtype=np.int64))
        )


    # otherwise, this is a new lcdict
    else:

        # form the lcdict
        # the metadata is one-elem arrays because we might add on to them later
        lcdict = {
            'objectid':hdrinfo['object'],
            'lcinfo':{
                'timesys':[hdrinfo['timesys']],
                'bjdoffset':[hdrinfo['bjdrefi'] + hdrinfo['bjdreff']],
                'exptime':[hdrinfo['exposure']],
                'lcaperture':[lcaperturedata],
                'aperpix_used':[hdrinfo['npixsap']],
                'aperpix_unused':[hdrinfo['npixmiss']],
                'pixarcsec':[(np.abs(hdrinfo['cdelt1']) +
                              np.abs(hdrinfo['cdelt2']))*3600.0/2.0],
                'ndet':[ndet],
                'origin':[hdrinfo['origin']],
                'procversion':[hdrinfo['procver']],
                'datarelease':[hdrinfo['data_rel']],
                'sector':[hdrinfo['sector']],
                'camera':[hdrinfo['camera']],
                'ccd':[hdrinfo['ccd']],
                'pixel_table_id':[hdrinfo['pxtable']],
                'date_obs_start':[hdrinfo['date-obs']],
                'date_obs_end':[hdrinfo['date-end']],
                'tic_version':[hdrinfo['ticver']],
                'cr_mitigation':[hdrinfo['crmiten']],
                'cr_blocksize':[hdrinfo['crblksz']],
                'cr_spocclean':[hdrinfo['crspoc']],
            },
            'objectinfo':{
                'objectid':hdrinfo['object'],
                'ticid':hdrinfo['ticid'],
                'tessmag':hdrinfo['tessmag'],
                'ra':hdrinfo['ra_obj'],
                'decl':hdrinfo['dec_obj'],
                'pmra':hdrinfo['pmra'],
                'pmdecl':hdrinfo['pmdec'],
                'pmtotal':hdrinfo['pmtotal'],
                'star_teff':hdrinfo['teff'],
                'star_logg':hdrinfo['logg'],
                'star_mh':hdrinfo['mh'],
                'star_radius':hdrinfo['radius'],
                'observatory':'TESS',
                'telescope':'TESS photometer',
            },
            'varinfo':{
                'cdpp0_5':[hdrinfo['cdpp0_5']],
                'cdpp1_0':[hdrinfo['cdpp1_0']],
                'cdpp2_0':[hdrinfo['cdpp2_0']],
                'pdcvar':[hdrinfo['pdcvar']],
                'pdcmethod':[hdrinfo['pdcmethd']],
                'target_flux_total_flux_ratio_in_aper':[hdrinfo['crowdsap']],
                'target_flux_fraction_in_aper':[hdrinfo['flfrcsap']],
            },
            'sap':{},
            'pdc':{},
        }

        # get the LC columns
        for key in datakeys:
            lcdict[key.lower()] = lcdata[key]
        for key in sapkeys:
            lcdict['sap'][key.lower()] = lcdata[key]
        for key in pdckeys:
            lcdict['pdc'][key.lower()] = lcdata[key]

        # turn some of the light curve information into numpy arrays so we can
        # sort on them later
        lcdict['exptime'] = np.full_like(lcdict['time'],
                                         lcdict['lcinfo']['exptime'][0],
                                         dtype=np.float64)
        lcdict['sector'] = np.full_like(lcdict['time'],
                                        lcdict['lcinfo']['sector'][0],
                                        dtype=np.int64)
        lcdict['camera'] = np.full_like(lcdict['time'],
                                        lcdict['lcinfo']['camera'][0],
                                        dtype=np.int64)
        lcdict['ccd'] = np.full_like(lcdict['time'],
                                     lcdict['lcinfo']['ccd'][0],
                                     dtype=np.int64)

        lcdict['pixel_table_id'] = np.full_like(
            lcdict['time'],
            lcdict['lcinfo']['pixel_table_id'][0],
            dtype=np.int64,
        )
        lcdict['origin'] = np.full_like(
            lcdict['time'],
            lcdict['lcinfo']['origin'][0],
            dtype='U100',
        )
        lcdict['date_obs_start'] = np.full_like(
            lcdict['time'],
            lcdict['lcinfo']['date_obs_start'][0],
            dtype='U100',
        )
        lcdict['date_obs_end'] = np.full_like(
            lcdict['time'],
            lcdict['lcinfo']['date_obs_end'][0],
            dtype='U100',
        )
        lcdict['procversion'] = np.full_like(
            lcdict['time'],
            lcdict['lcinfo']['procversion'][0],
            dtype='U255',
        )
        lcdict['datarelease'] = np.full_like(
            lcdict['time'],
            lcdict['lcinfo']['datarelease'][0],
            dtype=np.int64,
        )


        # normalize the SAP and PDCSAP fluxes, errs, and backgrounds if needed
        if normalize:

            sapflux_median = np.nanmedian(lcdict['sap']['sap_flux'])
            pdcsap_flux_median = np.nanmedian(lcdict['pdc']['pdcsap_flux'])

            lcdict['sap']['sap_flux'] = (
                lcdict['sap']['sap_flux'] /
                sapflux_median
            )
            lcdict['sap']['sap_flux_err'] = (
                lcdict['sap']['sap_flux_err'] /
                sapflux_median
            )

            lcdict['sap']['sap_bkg'] = (
                lcdict['sap']['sap_bkg'] /
                sapflux_median
            )
            lcdict['sap']['sap_bkg_err'] = (
                lcdict['sap']['sap_bkg_err'] /
                sapflux_median
            )

            lcdict['pdc']['pdcsap_flux'] = (
                lcdict['pdc']['pdcsap_flux'] /
                pdcsap_flux_median
            )
            lcdict['pdc']['pdcsap_flux_err'] = (
                lcdict['pdc']['pdcsap_flux_err'] /
                pdcsap_flux_median
            )

    ## END OF LIGHT CURVE CONSTRUCTION ##

    # update the lcdict columns with the actual columns
    lcdict['columns'] = (
        [x.lower() for x in datakeys] +
        ['sap.%s' % x.lower() for x in sapkeys] +
        ['pdc.%s' % x.lower() for x in pdckeys] +
        ['exptime','sector','camera','ccd', 'pixel_table_id',
         'origin', 'date_obs_start', 'date_obs_end',
         'procversion', 'datarelease']
    )

    # update the ndet key in the objectinfo with the sum of all observations
    lcdict['objectinfo']['ndet'] = sum(lcdict['lcinfo']['ndet'])

    # filter the LC dict if requested
    if (filterqualityflags is not False or
        nanfilter is not None or
        timestoignore is not None):
        lcdict = filter_tess_lcdict(lcdict,
                                    filterqualityflags,
                                    nanfilter=nanfilter,
                                    timestoignore=timestoignore)

    # return the lcdict at the end
    return lcdict
def read_tess_fitslc(lcfits,
                     headerkeys=LCHEADERKEYS,
                     datakeys=LCDATAKEYS,
                     sapkeys=LCSAPKEYS,
                     pdckeys=LCPDCKEYS,
                     topkeys=LCTOPKEYS,
                     apkeys=LCAPERTUREKEYS,
                     normalize=False,
                     appendto=None,
                     filterqualityflags=False,
                     nanfilter=None,
                     timestoignore=None):
    '''This extracts the light curve from a single TESS .lc.fits file.

    This works on the light curves available at MAST.

    TODO: look at:

    https://archive.stsci.edu/missions/tess/doc/EXP-TESS-ARC-ICD-TM-0014.pdf

    for details on the column descriptions and to fill in any other info we
    need.

    Parameters
    ----------

    lcfits : str
        The filename of a MAST Kepler/K2 light curve FITS file.

    headerkeys : list
        A list of FITS header keys that will be extracted from the FITS light
        curve file. These describe the observations. The default value for this
        is given in `LCHEADERKEYS` above.

    datakeys : list
        A list of FITS column names that correspond to the auxiliary
        measurements in the light curve. The default is `LCDATAKEYS` above.

    sapkeys : list
        A list of FITS column names that correspond to the SAP flux
        measurements in the light curve. The default is `LCSAPKEYS` above.

    pdckeys : list
        A list of FITS column names that correspond to the PDC flux
        measurements in the light curve. The default is `LCPDCKEYS` above.

    topkeys : list
        A list of FITS header keys that describe the object in the light
        curve. The default is `LCTOPKEYS` above.

    apkeys : list
        A list of FITS header keys that describe the flux measurement apertures
        used by the TESS pipeline. The default is `LCAPERTUREKEYS` above.

    normalize : bool
        If True, then the light curve's SAP_FLUX and PDCSAP_FLUX measurements
        will be normalized to 1.0 by dividing out the median flux for the
        component light curve.

    appendto : lcdict or None
        If appendto is an `lcdict`, will append measurements of this `lcdict` to
        that `lcdict`. This is used for consolidating light curves for the same
        object across different files (sectors/cameras/CCDs?). The appending
        does not care about the time order. To consolidate light curves in time
        order, use `consolidate_tess_fitslc` below.

    filterqualityflags : bool
        If True, will remove any measurements that have non-zero quality flags
        present. This usually indicates an issue with the instrument or
        spacecraft.

    nanfilter : {'sap','pdc','sap,pdc'} or None
        Indicates the flux measurement type(s) to apply the filtering to.

    timestoignore : list of tuples or None
        This is of the form::

            [(time1_start, time1_end), (time2_start, time2_end), ...]

        and indicates the start and end times to mask out of the final
        lcdict. Use this to remove anything that wasn't caught by the quality
        flags.

    Returns
    -------

    lcdict
        Returns an `lcdict` (this is useable by most astrobase functions for LC
        processing).

    '''

    # read the fits file
    hdulist = pyfits.open(lcfits)
    lchdr, lcdata = hdulist[1].header, hdulist[1].data
    lctophdr, lcaperturehdr, lcaperturedata = (hdulist[0].header,
                                               hdulist[2].header,
                                               hdulist[2].data)
    hdulist.close()

    hdrinfo = {}

    # now get the values we want from the header
    for key in headerkeys:
        if key in lchdr and lchdr[key] is not None:
            hdrinfo[key.lower()] = lchdr[key]
        else:
            hdrinfo[key.lower()] = None

    # get the number of detections
    ndet = lchdr['NAXIS2']

    # get the info from the topheader
    for key in topkeys:
        if key in lctophdr and lctophdr[key] is not None:
            hdrinfo[key.lower()] = lctophdr[key]
        else:
            hdrinfo[key.lower()] = None

    # get the info from the lcaperturehdr
    for key in lcaperturehdr:
        if key in lcaperturehdr and lcaperturehdr[key] is not None:
            hdrinfo[key.lower()] = lcaperturehdr[key]
        else:
            hdrinfo[key.lower()] = None


    # if we're appending to another lcdict
    if appendto and isinstance(appendto, dict):

        lcdict = appendto

        # update lcinfo
        lcdict['lcinfo']['timesys'].append(hdrinfo['timesys'])
        lcdict['lcinfo']['bjdoffset'].append(
            hdrinfo['bjdrefi'] + hdrinfo['bjdreff']
        )
        lcdict['lcinfo']['lcaperture'].append(lcaperturedata)
        lcdict['lcinfo']['aperpix_used'].append(hdrinfo['npixsap'])
        lcdict['lcinfo']['aperpix_unused'].append(hdrinfo['npixmiss'])
        lcdict['lcinfo']['pixarcsec'].append(
            (np.abs(hdrinfo['cdelt1']) +
             np.abs(hdrinfo['cdelt2']))*3600.0/2.0
        )
        lcdict['lcinfo']['ndet'].append(ndet)
        lcdict['lcinfo']['exptime'].append(hdrinfo['exposure'])
        lcdict['lcinfo']['sector'].append(hdrinfo['sector'])
        lcdict['lcinfo']['camera'].append(hdrinfo['camera'])
        lcdict['lcinfo']['ccd'].append(hdrinfo['ccd'])

        lcdict['lcinfo']['date_obs_start'].append(hdrinfo['date-obs'])
        lcdict['lcinfo']['date_obs_end'].append(hdrinfo['date-end'])
        lcdict['lcinfo']['pixel_table_id'].append(hdrinfo['pxtable'])
        lcdict['lcinfo']['origin'].append(hdrinfo['origin'])
        lcdict['lcinfo']['datarelease'].append(hdrinfo['data_rel'])
        lcdict['lcinfo']['procversion'].append(hdrinfo['procver'])


        lcdict['lcinfo']['tic_version'].append(hdrinfo['ticver'])
        lcdict['lcinfo']['cr_mitigation'].append(hdrinfo['crmiten'])
        lcdict['lcinfo']['cr_blocksize'].append(hdrinfo['crblksz'])
        lcdict['lcinfo']['cr_spocclean'].append(hdrinfo['crspoc'])

        # update the varinfo for this light curve
        lcdict['varinfo']['cdpp0_5'].append(hdrinfo['cdpp0_5'])
        lcdict['varinfo']['cdpp1_0'].append(hdrinfo['cdpp1_0'])
        lcdict['varinfo']['cdpp2_0'].append(hdrinfo['cdpp2_0'])
        lcdict['varinfo']['pdcvar'].append(hdrinfo['pdcvar'])
        lcdict['varinfo']['pdcmethod'].append(hdrinfo['pdcmethd'])
        lcdict['varinfo']['target_flux_total_flux_ratio_in_aper'].append(
            hdrinfo['crowdsap']
        )
        lcdict['varinfo']['target_flux_fraction_in_aper'].append(
            hdrinfo['flfrcsap']
        )

        # update the light curve columns now
        for key in datakeys:

            if key.lower() in lcdict:
                lcdict[key.lower()] = (
                    np.concatenate((lcdict[key.lower()], lcdata[key]))
                )

        for key in sapkeys:

            if key.lower() in lcdict['sap']:

                sapflux_median = np.nanmedian(lcdata['SAP_FLUX'])

                # normalize the current flux measurements if needed
                if normalize and key == 'SAP_FLUX':
                    thislcdata = lcdata[key] / sapflux_median
                elif normalize and key == 'SAP_FLUX_ERR':
                    thislcdata = lcdata[key] / sapflux_median
                elif normalize and key == 'SAP_BKG':
                    thislcdata = lcdata[key] / sapflux_median
                elif normalize and key == 'SAP_BKG_ERR':
                    thislcdata = lcdata[key] / sapflux_median
                else:
                    thislcdata = lcdata[key]

                lcdict['sap'][key.lower()] = (
                    np.concatenate((lcdict['sap'][key.lower()], thislcdata))
                )

        for key in pdckeys:

            if key.lower() in lcdict['pdc']:

                pdcsap_flux_median = np.nanmedian(lcdata['PDCSAP_FLUX'])

                # normalize the current flux measurements if needed
                if normalize and key == 'PDCSAP_FLUX':
                    thislcdata = lcdata[key] / pdcsap_flux_median
                elif normalize and key == 'PDCSAP_FLUX_ERR':
                    thislcdata = lcdata[key] / pdcsap_flux_median
                else:
                    thislcdata = lcdata[key]

                lcdict['pdc'][key.lower()] = (
                    np.concatenate((lcdict['pdc'][key.lower()], thislcdata))
                )


        # append some of the light curve information into existing numpy arrays
        # so we can sort on them later
        lcdict['exptime'] = np.concatenate(
            (lcdict['exptime'],
             np.full_like(lcdata['TIME'],
                          hdrinfo['exposure'],
                          dtype=np.float64))
        )
        lcdict['sector'] = np.concatenate(
            (lcdict['sector'],
             np.full_like(lcdata['TIME'],
                          hdrinfo['sector'],
                          dtype=np.int64))
        )
        lcdict['camera'] = np.concatenate(
            (lcdict['camera'],
             np.full_like(lcdata['TIME'],
                          hdrinfo['camera'],
                          dtype=np.int64))
        )
        lcdict['ccd'] = np.concatenate(
            (lcdict['ccd'],
             np.full_like(lcdata['TIME'],
                          hdrinfo['ccd'],
                          dtype=np.int64))
        )

        lcdict['pixel_table_id'] = np.concatenate(
            (lcdict['pixel_table_id'],
             np.full_like(lcdata['TIME'],
                          hdrinfo['pxtable'],
                          dtype=np.int64))
        )
        lcdict['origin'] = np.concatenate(
            (lcdict['origin'],
             np.full_like(lcdata['TIME'],
                          hdrinfo['origin'],
                          dtype='U100'))
        )
        lcdict['date_obs_start'] = np.concatenate(
            (lcdict['date_obs_start'],
             np.full_like(lcdata['TIME'],
                          hdrinfo['date-obs'],
                          dtype='U100'))
        )
        lcdict['date_obs_end'] = np.concatenate(
            (lcdict['date_obs_end'],
             np.full_like(lcdata['TIME'],
                          hdrinfo['date-end'],
                          dtype='U100'))
        )
        lcdict['procversion'] = np.concatenate(
            (lcdict['procversion'],
             np.full_like(lcdata['TIME'],
                          hdrinfo['procver'],
                          dtype='U255'))
        )
        lcdict['datarelease'] = np.concatenate(
            (lcdict['datarelease'],
             np.full_like(lcdata['TIME'],
                          hdrinfo['data_rel'],
                          dtype=np.int64))
        )


    # otherwise, this is a new lcdict
    else:

        # form the lcdict
        # the metadata is one-elem arrays because we might add on to them later
        lcdict = {
            'objectid':hdrinfo['object'],
            'lcinfo':{
                'timesys':[hdrinfo['timesys']],
                'bjdoffset':[hdrinfo['bjdrefi'] + hdrinfo['bjdreff']],
                'exptime':[hdrinfo['exposure']],
                'lcaperture':[lcaperturedata],
                'aperpix_used':[hdrinfo['npixsap']],
                'aperpix_unused':[hdrinfo['npixmiss']],
                'pixarcsec':[(np.abs(hdrinfo['cdelt1']) +
                              np.abs(hdrinfo['cdelt2']))*3600.0/2.0],
                'ndet':[ndet],
                'origin':[hdrinfo['origin']],
                'procversion':[hdrinfo['procver']],
                'datarelease':[hdrinfo['data_rel']],
                'sector':[hdrinfo['sector']],
                'camera':[hdrinfo['camera']],
                'ccd':[hdrinfo['ccd']],
                'pixel_table_id':[hdrinfo['pxtable']],
                'date_obs_start':[hdrinfo['date-obs']],
                'date_obs_end':[hdrinfo['date-end']],
                'tic_version':[hdrinfo['ticver']],
                'cr_mitigation':[hdrinfo['crmiten']],
                'cr_blocksize':[hdrinfo['crblksz']],
                'cr_spocclean':[hdrinfo['crspoc']],
            },
            'objectinfo':{
                'objectid':hdrinfo['object'],
                'ticid':hdrinfo['ticid'],
                'tessmag':hdrinfo['tessmag'],
                'ra':hdrinfo['ra_obj'],
                'decl':hdrinfo['dec_obj'],
                'pmra':hdrinfo['pmra'],
                'pmdecl':hdrinfo['pmdec'],
                'pmtotal':hdrinfo['pmtotal'],
                'star_teff':hdrinfo['teff'],
                'star_logg':hdrinfo['logg'],
                'star_mh':hdrinfo['mh'],
                'star_radius':hdrinfo['radius'],
                'observatory':'TESS',
                'telescope':'TESS photometer',
            },
            'varinfo':{
                'cdpp0_5':[hdrinfo['cdpp0_5']],
                'cdpp1_0':[hdrinfo['cdpp1_0']],
                'cdpp2_0':[hdrinfo['cdpp2_0']],
                'pdcvar':[hdrinfo['pdcvar']],
                'pdcmethod':[hdrinfo['pdcmethd']],
                'target_flux_total_flux_ratio_in_aper':[hdrinfo['crowdsap']],
                'target_flux_fraction_in_aper':[hdrinfo['flfrcsap']],
            },
            'sap':{},
            'pdc':{},
        }

        # get the LC columns
        for key in datakeys:
            lcdict[key.lower()] = lcdata[key]
        for key in sapkeys:
            lcdict['sap'][key.lower()] = lcdata[key]
        for key in pdckeys:
            lcdict['pdc'][key.lower()] = lcdata[key]

        # turn some of the light curve information into numpy arrays so we can
        # sort on them later
        lcdict['exptime'] = np.full_like(lcdict['time'],
                                         lcdict['lcinfo']['exptime'][0],
                                         dtype=np.float64)
        lcdict['sector'] = np.full_like(lcdict['time'],
                                        lcdict['lcinfo']['sector'][0],
                                        dtype=np.int64)
        lcdict['camera'] = np.full_like(lcdict['time'],
                                        lcdict['lcinfo']['camera'][0],
                                        dtype=np.int64)
        lcdict['ccd'] = np.full_like(lcdict['time'],
                                     lcdict['lcinfo']['ccd'][0],
                                     dtype=np.int64)

        lcdict['pixel_table_id'] = np.full_like(
            lcdict['time'],
            lcdict['lcinfo']['pixel_table_id'][0],
            dtype=np.int64,
        )
        lcdict['origin'] = np.full_like(
            lcdict['time'],
            lcdict['lcinfo']['origin'][0],
            dtype='U100',
        )
        lcdict['date_obs_start'] = np.full_like(
            lcdict['time'],
            lcdict['lcinfo']['date_obs_start'][0],
            dtype='U100',
        )
        lcdict['date_obs_end'] = np.full_like(
            lcdict['time'],
            lcdict['lcinfo']['date_obs_end'][0],
            dtype='U100',
        )
        lcdict['procversion'] = np.full_like(
            lcdict['time'],
            lcdict['lcinfo']['procversion'][0],
            dtype='U255',
        )
        lcdict['datarelease'] = np.full_like(
            lcdict['time'],
            lcdict['lcinfo']['datarelease'][0],
            dtype=np.int64,
        )


        # normalize the SAP and PDCSAP fluxes, errs, and backgrounds if needed
        if normalize:

            sapflux_median = np.nanmedian(lcdict['sap']['sap_flux'])
            pdcsap_flux_median = np.nanmedian(lcdict['pdc']['pdcsap_flux'])

            lcdict['sap']['sap_flux'] = (
                lcdict['sap']['sap_flux'] /
                sapflux_median
            )
            lcdict['sap']['sap_flux_err'] = (
                lcdict['sap']['sap_flux_err'] /
                sapflux_median
            )

            lcdict['sap']['sap_bkg'] = (
                lcdict['sap']['sap_bkg'] /
                sapflux_median
            )
            lcdict['sap']['sap_bkg_err'] = (
                lcdict['sap']['sap_bkg_err'] /
                sapflux_median
            )

            lcdict['pdc']['pdcsap_flux'] = (
                lcdict['pdc']['pdcsap_flux'] /
                pdcsap_flux_median
            )
            lcdict['pdc']['pdcsap_flux_err'] = (
                lcdict['pdc']['pdcsap_flux_err'] /
                pdcsap_flux_median
            )

    ## END OF LIGHT CURVE CONSTRUCTION ##

    # update the lcdict columns with the actual columns
    lcdict['columns'] = (
        [x.lower() for x in datakeys] +
        ['sap.%s' % x.lower() for x in sapkeys] +
        ['pdc.%s' % x.lower() for x in pdckeys] +
        ['exptime','sector','camera','ccd', 'pixel_table_id',
         'origin', 'date_obs_start', 'date_obs_end',
         'procversion', 'datarelease']
    )

    # update the ndet key in the objectinfo with the sum of all observations
    lcdict['objectinfo']['ndet'] = sum(lcdict['lcinfo']['ndet'])

    # filter the LC dict if requested
    if (filterqualityflags is not False or
        nanfilter is not None or
        timestoignore is not None):
        lcdict = filter_tess_lcdict(lcdict,
                                    filterqualityflags,
                                    nanfilter=nanfilter,
                                    timestoignore=timestoignore)

    # return the lcdict at the end
    return lcdict
def read_tess_fitslc ( lcfits , headerkeys = LCHEADERKEYS , datakeys = LCDATAKEYS , sapkeys = LCSAPKEYS , pdckeys = LCPDCKEYS , topkeys = LCTOPKEYS , apkeys = LCAPERTUREKEYS , normalize = False , appendto = None , filterqualityflags = False , nanfilter = None , timestoignore = None ) : # read the fits file hdulist = pyfits . open ( lcfits ) lchdr , lcdata = hdulist [ 1 ] . header , hdulist [ 1 ] . data lctophdr , lcaperturehdr , lcaperturedata = ( hdulist [ 0 ] . header , hdulist [ 2 ] . header , hdulist [ 2 ] . data ) hdulist . close ( ) hdrinfo = { } # now get the values we want from the header for key in headerkeys : if key in lchdr and lchdr [ key ] is not None : hdrinfo [ key . lower ( ) ] = lchdr [ key ] else : hdrinfo [ key . lower ( ) ] = None # get the number of detections ndet = lchdr [ 'NAXIS2' ] # get the info from the topheader for key in topkeys : if key in lctophdr and lctophdr [ key ] is not None : hdrinfo [ key . lower ( ) ] = lctophdr [ key ] else : hdrinfo [ key . lower ( ) ] = None # get the info from the lcaperturehdr for key in lcaperturehdr : if key in lcaperturehdr and lcaperturehdr [ key ] is not None : hdrinfo [ key . lower ( ) ] = lcaperturehdr [ key ] else : hdrinfo [ key . lower ( ) ] = None # if we're appending to another lcdict if appendto and isinstance ( appendto , dict ) : lcdict = appendto # update lcinfo lcdict [ 'lcinfo' ] [ 'timesys' ] . append ( hdrinfo [ 'timesys' ] ) lcdict [ 'lcinfo' ] [ 'bjdoffset' ] . append ( hdrinfo [ 'bjdrefi' ] + hdrinfo [ 'bjdreff' ] ) lcdict [ 'lcinfo' ] [ 'lcaperture' ] . append ( lcaperturedata ) lcdict [ 'lcinfo' ] [ 'aperpix_used' ] . append ( hdrinfo [ 'npixsap' ] ) lcdict [ 'lcinfo' ] [ 'aperpix_unused' ] . append ( hdrinfo [ 'npixmiss' ] ) lcdict [ 'lcinfo' ] [ 'pixarcsec' ] . append ( ( np . abs ( hdrinfo [ 'cdelt1' ] ) + np . abs ( hdrinfo [ 'cdelt2' ] ) ) * 3600.0 / 2.0 ) lcdict [ 'lcinfo' ] [ 'ndet' ] . append ( ndet ) lcdict [ 'lcinfo' ] [ 'exptime' ] . append ( hdrinfo [ 'exposure' ] ) lcdict [ 'lcinfo' ] [ 'sector' ] . append ( hdrinfo [ 'sector' ] ) lcdict [ 'lcinfo' ] [ 'camera' ] . append ( hdrinfo [ 'camera' ] ) lcdict [ 'lcinfo' ] [ 'ccd' ] . append ( hdrinfo [ 'ccd' ] ) lcdict [ 'lcinfo' ] [ 'date_obs_start' ] . append ( hdrinfo [ 'date-obs' ] ) lcdict [ 'lcinfo' ] [ 'date_obs_end' ] . append ( hdrinfo [ 'date-end' ] ) lcdict [ 'lcinfo' ] [ 'pixel_table_id' ] . append ( hdrinfo [ 'pxtable' ] ) lcdict [ 'lcinfo' ] [ 'origin' ] . append ( hdrinfo [ 'origin' ] ) lcdict [ 'lcinfo' ] [ 'datarelease' ] . append ( hdrinfo [ 'data_rel' ] ) lcdict [ 'lcinfo' ] [ 'procversion' ] . append ( hdrinfo [ 'procver' ] ) lcdict [ 'lcinfo' ] [ 'tic_version' ] . append ( hdrinfo [ 'ticver' ] ) lcdict [ 'lcinfo' ] [ 'cr_mitigation' ] . append ( hdrinfo [ 'crmiten' ] ) lcdict [ 'lcinfo' ] [ 'cr_blocksize' ] . append ( hdrinfo [ 'crblksz' ] ) lcdict [ 'lcinfo' ] [ 'cr_spocclean' ] . append ( hdrinfo [ 'crspoc' ] ) # update the varinfo for this light curve lcdict [ 'varinfo' ] [ 'cdpp0_5' ] . append ( hdrinfo [ 'cdpp0_5' ] ) lcdict [ 'varinfo' ] [ 'cdpp1_0' ] . append ( hdrinfo [ 'cdpp1_0' ] ) lcdict [ 'varinfo' ] [ 'cdpp2_0' ] . append ( hdrinfo [ 'cdpp2_0' ] ) lcdict [ 'varinfo' ] [ 'pdcvar' ] . append ( hdrinfo [ 'pdcvar' ] ) lcdict [ 'varinfo' ] [ 'pdcmethod' ] . append ( hdrinfo [ 'pdcmethd' ] ) lcdict [ 'varinfo' ] [ 'target_flux_total_flux_ratio_in_aper' ] . append ( hdrinfo [ 'crowdsap' ] ) lcdict [ 'varinfo' ] [ 'target_flux_fraction_in_aper' ] . append ( hdrinfo [ 'flfrcsap' ] ) # update the light curve columns now for key in datakeys : if key . lower ( ) in lcdict : lcdict [ key . lower ( ) ] = ( np . concatenate ( ( lcdict [ key . lower ( ) ] , lcdata [ key ] ) ) ) for key in sapkeys : if key . lower ( ) in lcdict [ 'sap' ] : sapflux_median = np . nanmedian ( lcdata [ 'SAP_FLUX' ] ) # normalize the current flux measurements if needed if normalize and key == 'SAP_FLUX' : thislcdata = lcdata [ key ] / sapflux_median elif normalize and key == 'SAP_FLUX_ERR' : thislcdata = lcdata [ key ] / sapflux_median elif normalize and key == 'SAP_BKG' : thislcdata = lcdata [ key ] / sapflux_median elif normalize and key == 'SAP_BKG_ERR' : thislcdata = lcdata [ key ] / sapflux_median else : thislcdata = lcdata [ key ] lcdict [ 'sap' ] [ key . lower ( ) ] = ( np . concatenate ( ( lcdict [ 'sap' ] [ key . lower ( ) ] , thislcdata ) ) ) for key in pdckeys : if key . lower ( ) in lcdict [ 'pdc' ] : pdcsap_flux_median = np . nanmedian ( lcdata [ 'PDCSAP_FLUX' ] ) # normalize the current flux measurements if needed if normalize and key == 'PDCSAP_FLUX' : thislcdata = lcdata [ key ] / pdcsap_flux_median elif normalize and key == 'PDCSAP_FLUX_ERR' : thislcdata = lcdata [ key ] / pdcsap_flux_median else : thislcdata = lcdata [ key ] lcdict [ 'pdc' ] [ key . lower ( ) ] = ( np . concatenate ( ( lcdict [ 'pdc' ] [ key . lower ( ) ] , thislcdata ) ) ) # append some of the light curve information into existing numpy arrays # so we can sort on them later lcdict [ 'exptime' ] = np . concatenate ( ( lcdict [ 'exptime' ] , np . full_like ( lcdata [ 'TIME' ] , hdrinfo [ 'exposure' ] , dtype = np . float64 ) ) ) lcdict [ 'sector' ] = np . concatenate ( ( lcdict [ 'sector' ] , np . full_like ( lcdata [ 'TIME' ] , hdrinfo [ 'sector' ] , dtype = np . int64 ) ) ) lcdict [ 'camera' ] = np . concatenate ( ( lcdict [ 'camera' ] , np . full_like ( lcdata [ 'TIME' ] , hdrinfo [ 'camera' ] , dtype = np . int64 ) ) ) lcdict [ 'ccd' ] = np . concatenate ( ( lcdict [ 'ccd' ] , np . full_like ( lcdata [ 'TIME' ] , hdrinfo [ 'ccd' ] , dtype = np . int64 ) ) ) lcdict [ 'pixel_table_id' ] = np . concatenate ( ( lcdict [ 'pixel_table_id' ] , np . full_like ( lcdata [ 'TIME' ] , hdrinfo [ 'pxtable' ] , dtype = np . int64 ) ) ) lcdict [ 'origin' ] = np . concatenate ( ( lcdict [ 'origin' ] , np . full_like ( lcdata [ 'TIME' ] , hdrinfo [ 'origin' ] , dtype = 'U100' ) ) ) lcdict [ 'date_obs_start' ] = np . concatenate ( ( lcdict [ 'date_obs_start' ] , np . full_like ( lcdata [ 'TIME' ] , hdrinfo [ 'date-obs' ] , dtype = 'U100' ) ) ) lcdict [ 'date_obs_end' ] = np . concatenate ( ( lcdict [ 'date_obs_end' ] , np . full_like ( lcdata [ 'TIME' ] , hdrinfo [ 'date-end' ] , dtype = 'U100' ) ) ) lcdict [ 'procversion' ] = np . concatenate ( ( lcdict [ 'procversion' ] , np . full_like ( lcdata [ 'TIME' ] , hdrinfo [ 'procver' ] , dtype = 'U255' ) ) ) lcdict [ 'datarelease' ] = np . concatenate ( ( lcdict [ 'datarelease' ] , np . full_like ( lcdata [ 'TIME' ] , hdrinfo [ 'data_rel' ] , dtype = np . int64 ) ) ) # otherwise, this is a new lcdict else : # form the lcdict # the metadata is one-elem arrays because we might add on to them later lcdict = { 'objectid' : hdrinfo [ 'object' ] , 'lcinfo' : { 'timesys' : [ hdrinfo [ 'timesys' ] ] , 'bjdoffset' : [ hdrinfo [ 'bjdrefi' ] + hdrinfo [ 'bjdreff' ] ] , 'exptime' : [ hdrinfo [ 'exposure' ] ] , 'lcaperture' : [ lcaperturedata ] , 'aperpix_used' : [ hdrinfo [ 'npixsap' ] ] , 'aperpix_unused' : [ hdrinfo [ 'npixmiss' ] ] , 'pixarcsec' : [ ( np . abs ( hdrinfo [ 'cdelt1' ] ) + np . abs ( hdrinfo [ 'cdelt2' ] ) ) * 3600.0 / 2.0 ] , 'ndet' : [ ndet ] , 'origin' : [ hdrinfo [ 'origin' ] ] , 'procversion' : [ hdrinfo [ 'procver' ] ] , 'datarelease' : [ hdrinfo [ 'data_rel' ] ] , 'sector' : [ hdrinfo [ 'sector' ] ] , 'camera' : [ hdrinfo [ 'camera' ] ] , 'ccd' : [ hdrinfo [ 'ccd' ] ] , 'pixel_table_id' : [ hdrinfo [ 'pxtable' ] ] , 'date_obs_start' : [ hdrinfo [ 'date-obs' ] ] , 'date_obs_end' : [ hdrinfo [ 'date-end' ] ] , 'tic_version' : [ hdrinfo [ 'ticver' ] ] , 'cr_mitigation' : [ hdrinfo [ 'crmiten' ] ] , 'cr_blocksize' : [ hdrinfo [ 'crblksz' ] ] , 'cr_spocclean' : [ hdrinfo [ 'crspoc' ] ] , } , 'objectinfo' : { 'objectid' : hdrinfo [ 'object' ] , 'ticid' : hdrinfo [ 'ticid' ] , 'tessmag' : hdrinfo [ 'tessmag' ] , 'ra' : hdrinfo [ 'ra_obj' ] , 'decl' : hdrinfo [ 'dec_obj' ] , 'pmra' : hdrinfo [ 'pmra' ] , 'pmdecl' : hdrinfo [ 'pmdec' ] , 'pmtotal' : hdrinfo [ 'pmtotal' ] , 'star_teff' : hdrinfo [ 'teff' ] , 'star_logg' : hdrinfo [ 'logg' ] , 'star_mh' : hdrinfo [ 'mh' ] , 'star_radius' : hdrinfo [ 'radius' ] , 'observatory' : 'TESS' , 'telescope' : 'TESS photometer' , } , 'varinfo' : { 'cdpp0_5' : [ hdrinfo [ 'cdpp0_5' ] ] , 'cdpp1_0' : [ hdrinfo [ 'cdpp1_0' ] ] , 'cdpp2_0' : [ hdrinfo [ 'cdpp2_0' ] ] , 'pdcvar' : [ hdrinfo [ 'pdcvar' ] ] , 'pdcmethod' : [ hdrinfo [ 'pdcmethd' ] ] , 'target_flux_total_flux_ratio_in_aper' : [ hdrinfo [ 'crowdsap' ] ] , 'target_flux_fraction_in_aper' : [ hdrinfo [ 'flfrcsap' ] ] , } , 'sap' : { } , 'pdc' : { } , } # get the LC columns for key in datakeys : lcdict [ key . lower ( ) ] = lcdata [ key ] for key in sapkeys : lcdict [ 'sap' ] [ key . lower ( ) ] = lcdata [ key ] for key in pdckeys : lcdict [ 'pdc' ] [ key . lower ( ) ] = lcdata [ key ] # turn some of the light curve information into numpy arrays so we can # sort on them later lcdict [ 'exptime' ] = np . full_like ( lcdict [ 'time' ] , lcdict [ 'lcinfo' ] [ 'exptime' ] [ 0 ] , dtype = np . float64 ) lcdict [ 'sector' ] = np . full_like ( lcdict [ 'time' ] , lcdict [ 'lcinfo' ] [ 'sector' ] [ 0 ] , dtype = np . int64 ) lcdict [ 'camera' ] = np . full_like ( lcdict [ 'time' ] , lcdict [ 'lcinfo' ] [ 'camera' ] [ 0 ] , dtype = np . int64 ) lcdict [ 'ccd' ] = np . full_like ( lcdict [ 'time' ] , lcdict [ 'lcinfo' ] [ 'ccd' ] [ 0 ] , dtype = np . int64 ) lcdict [ 'pixel_table_id' ] = np . full_like ( lcdict [ 'time' ] , lcdict [ 'lcinfo' ] [ 'pixel_table_id' ] [ 0 ] , dtype = np . int64 , ) lcdict [ 'origin' ] = np . full_like ( lcdict [ 'time' ] , lcdict [ 'lcinfo' ] [ 'origin' ] [ 0 ] , dtype = 'U100' , ) lcdict [ 'date_obs_start' ] = np . full_like ( lcdict [ 'time' ] , lcdict [ 'lcinfo' ] [ 'date_obs_start' ] [ 0 ] , dtype = 'U100' , ) lcdict [ 'date_obs_end' ] = np . full_like ( lcdict [ 'time' ] , lcdict [ 'lcinfo' ] [ 'date_obs_end' ] [ 0 ] , dtype = 'U100' , ) lcdict [ 'procversion' ] = np . full_like ( lcdict [ 'time' ] , lcdict [ 'lcinfo' ] [ 'procversion' ] [ 0 ] , dtype = 'U255' , ) lcdict [ 'datarelease' ] = np . full_like ( lcdict [ 'time' ] , lcdict [ 'lcinfo' ] [ 'datarelease' ] [ 0 ] , dtype = np . int64 , ) # normalize the SAP and PDCSAP fluxes, errs, and backgrounds if needed if normalize : sapflux_median = np . nanmedian ( lcdict [ 'sap' ] [ 'sap_flux' ] ) pdcsap_flux_median = np . nanmedian ( lcdict [ 'pdc' ] [ 'pdcsap_flux' ] ) lcdict [ 'sap' ] [ 'sap_flux' ] = ( lcdict [ 'sap' ] [ 'sap_flux' ] / sapflux_median ) lcdict [ 'sap' ] [ 'sap_flux_err' ] = ( lcdict [ 'sap' ] [ 'sap_flux_err' ] / sapflux_median ) lcdict [ 'sap' ] [ 'sap_bkg' ] = ( lcdict [ 'sap' ] [ 'sap_bkg' ] / sapflux_median ) lcdict [ 'sap' ] [ 'sap_bkg_err' ] = ( lcdict [ 'sap' ] [ 'sap_bkg_err' ] / sapflux_median ) lcdict [ 'pdc' ] [ 'pdcsap_flux' ] = ( lcdict [ 'pdc' ] [ 'pdcsap_flux' ] / pdcsap_flux_median ) lcdict [ 'pdc' ] [ 'pdcsap_flux_err' ] = ( lcdict [ 'pdc' ] [ 'pdcsap_flux_err' ] / pdcsap_flux_median ) ## END OF LIGHT CURVE CONSTRUCTION ## # update the lcdict columns with the actual columns lcdict [ 'columns' ] = ( [ x . lower ( ) for x in datakeys ] + [ 'sap.%s' % x . lower ( ) for x in sapkeys ] + [ 'pdc.%s' % x . lower ( ) for x in pdckeys ] + [ 'exptime' , 'sector' , 'camera' , 'ccd' , 'pixel_table_id' , 'origin' , 'date_obs_start' , 'date_obs_end' , 'procversion' , 'datarelease' ] ) # update the ndet key in the objectinfo with the sum of all observations lcdict [ 'objectinfo' ] [ 'ndet' ] = sum ( lcdict [ 'lcinfo' ] [ 'ndet' ] ) # filter the LC dict if requested if ( filterqualityflags is not False or nanfilter is not None or timestoignore is not None ) : lcdict = filter_tess_lcdict ( lcdict , filterqualityflags , nanfilter = nanfilter , timestoignore = timestoignore ) # return the lcdict at the end return lcdict
This extracts the light curve from a single TESS .lc.fits file.

    This works on the light curves available at MAST.

    TODO: look at:

    https://archive.stsci.edu/missions/tess/doc/EXP-TESS-ARC-ICD-TM-0014.pdf

    for details on the column descriptions and to fill in any other info we
    need.

    Parameters
    ----------

    lcfits : str
        The filename of a MAST Kepler/K2 light curve FITS file.

    headerkeys : list
        A list of FITS header keys that will be extracted from the FITS light
        curve file. These describe the observations. The default value for this
        is given in `LCHEADERKEYS` above.

    datakeys : list
        A list of FITS column names that correspond to the auxiliary
        measurements in the light curve. The default is `LCDATAKEYS` above.

    sapkeys : list
        A list of FITS column names that correspond to the SAP flux
        measurements in the light curve. The default is `LCSAPKEYS` above.

    pdckeys : list
        A list of FITS column names that correspond to the PDC flux
        measurements in the light curve. The default is `LCPDCKEYS` above.

    topkeys : list
        A list of FITS header keys that describe the object in the light
        curve. The default is `LCTOPKEYS` above.

    apkeys : list
        A list of FITS header keys that describe the flux measurement apertures
        used by the TESS pipeline. The default is `LCAPERTUREKEYS` above.

    normalize : bool
        If True, then the light curve's SAP_FLUX and PDCSAP_FLUX measurements
        will be normalized to 1.0 by dividing out the median flux for the
        component light curve.

    appendto : lcdict or None
        If appendto is an `lcdict`, will append measurements of this `lcdict` to
        that `lcdict`. This is used for consolidating light curves for the same
        object across different files (sectors/cameras/CCDs?). The appending
        does not care about the time order. To consolidate light curves in time
        order, use `consolidate_tess_fitslc` below.

    filterqualityflags : bool
        If True, will remove any measurements that have non-zero quality flags
        present. This usually indicates an issue with the instrument or
        spacecraft.

    nanfilter : {'sap','pdc','sap,pdc'} or None
        Indicates the flux measurement type(s) to apply the filtering to.

    timestoignore : list of tuples or None
        This is of the form::

            [(time1_start, time1_end), (time2_start, time2_end), ...]

        and indicates the start and end times to mask out of the final
        lcdict. Use this to remove anything that wasn't caught by the quality
        flags.

    Returns
    -------

    lcdict
        Returns an `lcdict` (this is useable by most astrobase functions for LC
        processing).
This extracts the light curve from a single TESS . lc . fits file .