Пример #1
0
def get_guidetelemetry(night, verbose=False):
    '''
    Retrieve guider telemetry data for a given night.

    Parameters
    ----------
    night : int
        night in form 20210320 for 20 March 2021
    verbose : bool
        print verbose output?

    Returns
    -------
    guide : record array
        guide telemetry stream for night
    '''

    # connect to telemetry database
    conn = psycopg2.connect(database="desi_dev", user="******")

    # set query limits as 3 hours before/after twilight at beginning/end of night
    twi1, twi2 = utils.get_twilights(night)
    twibeg = Time( Time(twi1, format='mjd'), format='datetime')
    twiend = Time( Time(twi2, format='mjd'), format='datetime')
    query_start = twibeg - (3./24.)
    query_stop = twiend + (3./24.)

    # query for guider data
    guidedf = pd.read_sql_query(f"SELECT time_recorded,seeing,meanx,meany FROM guider_summary WHERE time_recorded >= '{query_start}' AND time_recorded < '{query_stop}'", conn)
    guide = guidedf.to_records()

    return guide
Пример #2
0
def get_teltelemetry(night, verbose=False):
    '''
    Retrieve telescope telemetry data for a given night.

    Parameters
    ----------
    night : int
        night in form 20210320 for 20 March 2021
    verbose : bool
        print verbose output?

    Returns
    -------
    ccd : record array
        ccd telemetry stream for night
    '''

    # connect to telemetry database
    conn = psycopg2.connect(database="desi_dev", user="******")

    # set query limits as 3 hours before/after twilight at beginning/end of night
    twi1, twi2 = utils.get_twilights(night)
    twibeg = Time( Time(twi1, format='mjd'), format='datetime')
    twiend = Time( Time(twi2, format='mjd'), format='datetime')
    query_start = twibeg - (3./24.)
    query_stop = twiend + (3./24.)

    # Telescope data:
    teldf = pd.read_sql_query(f"SELECT * FROM tcs_info WHERE time_recorded >= '{query_start}' AND time_recorded < '{query_stop}'", conn)
    tel = teldf.to_records()

    # Convert dome_timestamp to a Time object in MJD
    tel['time_recorded'] = np.array([Time(t).mjd for t in tel['time_recorded']])

    return tel
Пример #3
0
def get_dometelemetry(night, verbose=False):
    '''
    Retrieve dome telemetry data for a given night.

    Parameters
    ----------
    night : int
        night in form 20210320 for 20 March 2021
    verbose : bool
        print verbose output?

    Returns
    -------
    dome : record array
        dome telemetry stream for night
    '''

    # connect to telemetry database
    conn = psycopg2.connect(database="desi_dev", user="******")

    # set query limits as 3 hours before/after twilight at beginning/end of night
    twi1, twi2 = utils.get_twilights(night)
    twibeg = Time( Time(twi1, format='mjd'), format='datetime')
    twiend = Time( Time(twi2, format='mjd'), format='datetime')
    query_start = twibeg - (3./24.)
    query_stop = twiend + (3./24.)

    # query for dome data
    domedf = pd.read_sql_query(f"SELECT dome_timestamp,shutter_upper,mirror_cover FROM environmentmonitor_dome WHERE time_recorded >= '{query_start}' AND time_recorded < '{query_stop}'", conn)
    dome = domedf.to_records()

    # convert dome_timestamp to a Time object in MJD
    dome['dome_timestamp'] = np.array([Time(t).mjd for t in dome['dome_timestamp']])

    return dome
Пример #4
0
def get_scidata(expidlist, night):
    '''
    Determine start and lengths of all science frames associated with expidlist

    Parameters
    ----------
    expidlist : list
        list of expids 
    night : int
        night in form 20210320 for 20 March 2021

    Returns
    -------
    spec_start : list
        start times of each spec exposure in UT hours
    spec_width : list
        lengths of each spec exposure in UT hours
    spec_expid : list
        expid of each spec exposure
    '''

    try: 
        datadir = utils.get_rawdatadir()
    except RuntimeError:
        exit(1)
    nightdir = os.path.join(datadir, str(night)) 
    twibeg_mjd, twiend_mjd = utils.get_twilights(int(night))
    startdate = int(twibeg_mjd)

    spec_start = []
    spec_width = []
    spec_expid = []

    # Identify expids that corresponds to science exposures:
    for expid in expidlist:
        tmpdir = os.path.join(nightdir, expid.zfill(8))
        scifiles = (glob(tmpdir + "/desi*"))
        if len(scifiles) > 0:
            hhh = fits.open(scifiles[0])
            tt = Time(''.join(hhh['SPECTCONS'].data['DATE-OBS'][0])).mjd
            try:
                flavor = hhh[1].header['FLAVOR']
                program = hhh[1].header['PROGRAM']
                obstype = hhh[1].header['OBSTYPE']
                exptime = hhh[1].header['EXPTIME']
            except:
                continue
            if obstype == 'SCIENCE' and flavor == 'science' and 'Dither' not in program and exptime > 10.:
                #spec_expids.append(expid)
                spec_start.append((tt - startdate)*24 )
                spec_width.append(hhh['SPEC'].header['EXPTIME']/3600.)
                spec_expid.append(expid)

    return spec_start, spec_width, spec_expid
Пример #5
0
def get_guidedata(expidlist, night, acqonly=False, startonly=False):
    '''
    Determine start and lengths of all sky frames associated with expidlist

    Parameters
    ----------
    expidlist : list
        list of expids 
    night : int
        night in form 20210320 for 20 March 2021
    acqonly : bool 
        report only the times for the acquisition images
    startonly : bool 
        report only the start of guiding

    Returns
    -------
    guide_start : list
        start times of each guide exposure in UT hours
    guide_width : list
        lengths of each guide exposure in UT hours
    '''

    try: 
        datadir = utils.get_rawdatadir()
    except RuntimeError:
        exit(1)
    nightdir = os.path.join(datadir, str(night)) 
    twibeg_mjd, twiend_mjd = utils.get_twilights(int(night))
    startdate = int(twibeg_mjd)

    guide_start = []
    guide_width = []
    for expid in expidlist:
        tmpdir = os.path.join(nightdir, expid.zfill(8))
        guidefiles = (glob(tmpdir + "/guide-" + expid.zfill(8) + ".fits.fz"))
        if len(guidefiles) > 0:
            ghh = fits.open(guidefiles[0])
            if acqonly:
                tt = Time(ghh['GUIDE0T'].data[0]['DATE-OBS']).mjd
                guide_start.append((tt - startdate)*24 )
                guide_width.append(ghh['GUIDE0T'].data[0]['EXPTIME']/3600.)
            elif startonly:
                tt = Time(ghh['GUIDE0T'].data[1]['DATE-OBS']).mjd
                guide_start.append((tt - startdate)*24 )
                guide_width.append(ghh['GUIDE0T'].data[1]['EXPTIME']/3600.)
            else:
                for i in range(len(ghh['GUIDE0T'].data)):
                    tt = Time(ghh['GUIDE0T'].data[i]['DATE-OBS']).mjd
                    guide_start.append((tt - startdate)*24 )
                    guide_width.append(ghh['GUIDE0T'].data[i]['EXPTIME']/3600.)
    return guide_start, guide_width
Пример #6
0
def calc_science(night, verbose=False):
    '''
    Calculate quantities about science exposures for a given night


    Parameters
    ----------
    night : int
        night in form 20210320 for 20 March 2021
    verbose : bool
        print verbose output?

    Returns
    -------
    science_first : float
        start time of first spectroscopic exposure in MJD
    science_last : float
        end time of last spectroscopic exposure in MJD
    scitwi_hours : float
        number of hours of spectroscopic exposures between twilights in UT hours 
    scitot_hours : float
        total number of hours of spectroscopic exposures in UT hours (not limited by twilight)
    '''

    twibeg_mjd, twiend_mjd = utils.get_twilights(int(night))
    startdate = int(twibeg_mjd)
    twibeg_hours = 24.*(twibeg_mjd - startdate)
    twiend_hours = 24.*(twiend_mjd - startdate)
    twitot_hours = twiend_hours - twibeg_hours

    science_start, science_width, dither_start, dither_width, guide_start, guide_width = calc_obstimes(night)

    # Science hours between twilights 
    scitwi_hours = get_totobs(science_start, science_width, twibeg_hours, twiend_hours)

    # Calculate MJD of start of first science exposure:
    if len(science_start) == 0:
        return 0., 0., 0., 0.
    science_first = min(science_start)/24. + startdate
    indx = np.where( np.asarray(science_start) == max(science_start))[0][0]
    science_last = (science_start[indx] + science_width[indx])/24. + startdate

    # Science hours total
    scitot_hours = np.sum(science_width) 

    # Open shutter time between twilights
    fracscitwi = scitwi_hours/(twiend_hours - twibeg_hours)

    return science_first, science_last, scitwi_hours, scitot_hours
Пример #7
0
def get_fvcdata(expidlist, night, verbose=False):
    '''
    Determine start and lengths of all FVC frames associated with expidlist

    Parameters
    ----------
    expidlist : list
        list of expids 
    night : int
        night in form 20210320 for 20 March 2021

    Returns
    -------
    fvc_start : list
        start times of each FVC exposure in UT hours
    fvc_width : list
        lengths of each FVC exposure in UT hours
    '''

    try: 
        datadir = utils.get_rawdatadir()
    except RuntimeError:
        exit(1)
    nightdir = os.path.join(datadir, str(night)) 
    twibeg_mjd, twiend_mjd = utils.get_twilights(int(night))
    startdate = int(twibeg_mjd)

    fvc_start = []
    fvc_width = []
    for expid in expidlist:
        tmpdir = os.path.join(nightdir, expid.zfill(8))
        fvcfiles = (glob(tmpdir + "/fvc-" + expid.zfill(8) + ".fits.fz"))
        if len(fvcfiles) > 0:
            if verbose:
                print(fvcfiles[0])
            fhh = fits.open(fvcfiles[0])
            tt = Time(fhh['F0000'].header['DATE']).mjd
            fvc_start.append((tt - startdate)*24 )
            fvc_width.append(fhh['F0000'].header['EXPTIME']/3600.)
            try:   # Splits do not have a second FVC image
                tt = Time(fhh['F0001'].header['DATE']).mjd
                fvc_start.append((tt - startdate)*24 )
                fvc_width.append(fhh['F0001'].header['EXPTIME']/3600.)
            except KeyError: 
                continue
    return fvc_start, fvc_width
Пример #8
0
def get_skydata(expidlist, night, verbose=False):
    '''
    Determine start and lengths of all sky frames associated with expidlist

    Parameters
    ----------
    expidlist : list
        list of expids 
    night : int
        night in form 20210320 for 20 March 2021
    verbose : bool
        print verbose output?

    Returns
    -------
    sky_start : list
        start times of each sky exposure in UT hours
    sky_width : list
        lengths of each sky exposure in UT hours
    '''

    try: 
        datadir = utils.get_rawdatadir()
    except RuntimeError:
        exit(1)
    nightdir = os.path.join(datadir, str(night)) 
    twibeg_mjd, twiend_mjd = utils.get_twilights(int(night))
    startdate = int(twibeg_mjd)

    if verbose:
        print(night, startdate, twibeg_mjd, nightdir)

    sky_start = []
    sky_width = []
    for expid in expidlist:
        tmpdir = os.path.join(nightdir, expid.zfill(8))
        skyfiles = (glob(tmpdir + "/sky-" + expid.zfill(8) + ".fits.fz"))
        if len(skyfiles) > 0:
            shh = fits.open(skyfiles[0])
            for i in range(len(shh['SKYCAM0T'].data)):
                tt = Time(shh['SKYCAM0T'].data[i]['DATE-OBS']).mjd
                sky_start.append((tt - startdate)*24 )
                sky_width.append(shh['SKYCAM0T'].data[i]['EXPTIME']/3600.)
    return sky_start, sky_width
Пример #9
0
def calc_row(night):
    '''
    Calculate information about observing efficiency for a single night

    Parameters
    ----------
    night : int
        night in form 20210320 for 20 March 2021
    verbose : bool
        provide verbose output

    Returns
    -------
    rowdata : list
        info to update row of summary table for the night
    '''

    twibeg_mjd, twiend_mjd = utils.get_twilights(night) 
    firstopen, lastclose, fractwiopen, twiopen_hrs = od.calc_domevals(night)
    science_first, science_last, scitwi_hrs, scitot_hrs = od.calc_science(night) 
    print(twiopen_hrs)
    if twiopen_hrs > 0.: 
        fracscitwi = scitwi_hrs/twiopen_hrs
    else:
        fracscitwi = 0.

    # names=('NIGHT', 'TWIBEG', 'TWIEND', 'DOMEOPEN', 'DOMECLOSE', 'DOMEFRAC', 'DOMEHRS', 'SCSTART', 'SCSTOP', 'SCTWTOT', 'SCTOT', 'SCTWFRAC'),
    c0 = np.array([night], dtype=np.int32)
    c1 = np.array([twibeg_mjd], dtype=float)
    c2 = np.array([twiend_mjd], dtype=float)
    c3 = np.array([firstopen], dtype=float)
    c4 = np.array([lastclose], dtype=float)
    c5 = np.array([fractwiopen], dtype=float)
    c6 = np.array([twiopen_hrs], dtype=float)
    c7 = np.array([science_first], dtype=float)
    c8 = np.array([science_last], dtype=float)
    c9 = np.array([scitwi_hrs], dtype=float)
    c10 = np.array([scitot_hrs], dtype=float)
    c11 = np.array([fracscitwi], dtype=float)

    newrow = [c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11]

    return newrow
Пример #10
0
def calc_guidelist(night, clobber=False, verbose=False): 
    '''
    Create json files with start times, exptimes, and other information 
    about guiding for a single night. Outputs have the form guidedata20201224.json. 

    Parameters
    ----------
    night : int
        night in form 20210320 for 20 March 2021
    clobber : bool
        overwrite existing file
    verbose : bool
        provide verbose output

    Returns
    -------
    none
    '''

    try:
        outdir = utils.get_outdir()
    except RuntimeError:
        exit(1)

    try: 
        datadir = utils.get_rawdatadir()
    except RuntimeError:
        exit(1)

    nightdir = os.path.join(datadir, str(night)) 
    twibeg_mjd, twiend_mjd = utils.get_twilights(int(night))
        
    # Names for output json file: 
    filename = "guidedata" + str(night) + ".json"
    guidedatafile = os.path.join(outdir, 'NightlyData', filename) 


    # See if json file for this night already exists: 
    if not os.path.isfile(guidedatafile) or clobber: 
        # List of all directories and expids: 
        expdirs = glob(nightdir + "/*")
        expids = expdirs.copy()
        for i in range(len(expdirs)):
            expids[i] = expdirs[i][expdirs[i].find('000')::]

        # Print warning if there are no data
        if len(expids) == 0: 
            print("Warning: No observations found") 

        guidedata = {}
        for expid in expids:
            # tmpdir = os.path.join(nightdir, expid, "guide-" + expid + ".fits.fz")
            # guidefiles = (glob(tmpdir + "/guide-" + expid + ".fits.fz"))
            # guidefile = tmpdir + "/guide-" + expid + ".fits.fz"
            guidefile = os.path.join(nightdir, expid, "guide-" + expid + ".fits.fz")
            #if len(guidefiles) > 0:
            if os.path.isfile(guidefile):
                try:
                    hhh = fits.open(guidefile)
                except OSError: 
                    if verbose: 
                        print("OSError with {}".format(guidefile))
                    continue
                guidedata[expid] = {}
                t1 = Time( utils.find_dateobs(hhh) ).mjd
                try: 
                    t2 = Time(hhh['GUIDE0T'].data[-1]['DATE-OBS']).mjd
                except KeyError:
                    t2 = t1  # Need to fix this
                guidedata[expid]['GUIDE-START'] = t1
                guidedata[expid]['GUIDE-STOP'] = t2
                try:
                    guidedata[expid]['DOMSHUTU'] = hhh[0].header['DOMSHUTU']
                except KeyError:
                    guidedata[expid]['DOMSHUTU'] = 'None'
                try:
                    guidedata[expid]['PMCOVER'] = hhh[0].header['PMCOVER']
                except KeyError:
                    guidedata[expid]['PMCOVER'] = 'UNKNOWN'
                try:
                    guidedata[expid]['OBSTYPE'] = hhh[0].header['OBSTYPE']
                except KeyError:
                    guidedata[expid]['OBSTYPE'] = 'None'
                try:
                    guidedata[expid]['FLAVOR'] = hhh[0].header['FLAVOR']
                except KeyError:
                    guidedata[expid]['FLAVOR'] = 'None'
                try:
                    guidedata[expid]['EXPTIME'] = hhh[0].header['EXPTIME']
                except KeyError:
                    guidedata[expid]['EXPTIME'] = 0.
        with open(guidedatafile, 'w') as fp:
            json.dump(guidedata, fp)

        if verbose: 
            print("Wrote", guidedatafile) 
Пример #11
0
def calc_interexp(night, minexptime=300., clobber=False, verbose=False):
    '''
    Calculate the interexposure times

    Parameters
    ----------
    night : int
        night in form 20210320 for 20 March 2021
    minexptime : float
        minimum science exposure time to consider in calculation
    verbose : bool
        print verbose output?
    clobber : bool
        overwrite existing file

    Returns
    -------
    interexp : 2-d float array
        columns are DATE-OBS in MJD, DATE-OBS in UT hrs, EXPTIME in s, EXPID, interexposure time in s
    '''

    # Read in the data
    try:
        outdir = utils.get_outdir()
    except RuntimeError:
        exit(1)

    specfilename = "specdata" + str(night) + ".json"
    specdatafile = os.path.join(outdir, 'NightlyData', specfilename)
    if os.path.isfile(specdatafile) and not clobber:
        specdata = utils.read_json(specdatafile)
    else:
        print("Note: {} not found ... creating it".format(specdatafile))
        calc_sciencelist(night)
        specdata = utils.read_json(specdatafile)

    # Take about the dict
    dateobs_mjd = []
    exptime = []
    expid = []
    for i, key in enumerate(specdata.keys()):
        if specdata[key]['EXPTIME'] > minexptime:
            dateobs_mjd.append(specdata[key]['DATE-OBS'])
            exptime.append(specdata[key]['EXPTIME'])
            expid.append(key)

    twibeg_mjd, twiend_mjd = utils.get_twilights(int(night))
    startdate = int(twibeg_mjd)

    scidata = np.zeros([len(dateobs_mjd), 5])
    for i in range(len(dateobs_mjd)):
        dateobs_hrs = (dateobs_mjd[i] - startdate)*24.
        scidata[i] = np.asarray([dateobs_mjd[i], dateobs_hrs, exptime[i], expid[i], 0.])
            
    # Sort by expid:
    columnIndex = 3
    sortdata = scidata[scidata[:,columnIndex].argsort()]

    # Calculate interexposure time between each successive pair
    secperday = 24.*3600.
    interexp = []
    for i in range(len(sortdata) - 1):
        sortdata[i][4] = secperday*(sortdata[i+1][0] - sortdata[i][0] - sortdata[i][1]/secperday)
        # interexp.append(secperday*(sortdata[i+1][0] - sortdata[i][0] - sortdata[i][1]/secperday ))

    return sortdata[:-1]
Пример #12
0
def calc_obstimes(night, verbose=False, clobber=False):
    '''
    Calculate start and lengths of science, dither, and guide exposures 

    Parameters
    ----------
    night : int
        night in form 20210320 for 20 March 2021
    verbose : bool
        print verbose output?
    clobber : bool
        overwrite existing file

    Returns
    -------
    science_start : float
        start times of science exposures in UT hours
    science_width : float
        length of science exposures in UT hours
    dither_start : float
        start times of dither exposures in UT hours
    dither_width : float
        length of dither exposures in UT hours
    guide_start : float
        start times of guide exposures in UT hours
    guide_width : float
        length of guide exposures in UT hours
    '''

    try:
        outdir = utils.get_outdir()
    except RuntimeError:
        exit(1)

    # Read in the data
    specfilename = "specdata" + str(night) + ".json"
    specdatafile = os.path.join(outdir, 'NightlyData', specfilename)
    if os.path.isfile(specdatafile) and not clobber:
        specdata = utils.read_json(specdatafile)
    else:
        print("Note: {} not found ... creating it".format(specdatafile))
        calc_sciencelist(night)
        specdata = utils.read_json(specdatafile)

    guidefilename = "guidedata" + str(night) + ".json"
    guidedatafile = os.path.join(outdir, 'NightlyData', guidefilename)
    if os.path.isfile(guidedatafile) and not clobber:
        guidedata = utils.read_json(guidedatafile)
    else:
        print("Note: {} not found ... creating it".format(guidedatafile))
        calc_guidelist(night)
        guidedata = utils.read_json(guidedatafile)

    twibeg_mjd, twiend_mjd = utils.get_twilights(int(night))
    startdate = int(twibeg_mjd)
    
    # Calculate the start and duration for the science observations:
    science_start = []
    science_width = []
    for item in specdata:
        if specdata[item]['OBSTYPE'] == 'SCIENCE' and specdata[item]['FLAVOR'] == 'science' and 'Dither' not in specdata[item]['PROGRAM'] and specdata[item]['DOMSHUTU'] == 'open' and specdata[item]['PMCOVER'] == 'open':
            science_start.append( (specdata[item]['DATE-OBS'] - startdate)*24. )
            science_width.append( specdata[item]['EXPTIME']/3600. )
    
    # Separately account for time spent on dither tests
    dither_start = []
    dither_width = []
    for item in specdata:
        if specdata[item]['OBSTYPE'] == 'SCIENCE' and specdata[item]['FLAVOR'] == 'science' and 'Dither' in specdata[item]['PROGRAM']:
            dither_start.append( (specdata[item]['DATE-OBS'] - startdate)*24. )
            dither_width.append( specdata[item]['EXPTIME']/3600. )
    
    # Times for guiding:
    guide_start = []
    guide_width = []
    for item in guidedata:
        # if guidedata[item]['OBSTYPE'] == 'SCIENCE' and guidedata[item]['FLAVOR'] == 'science' and guidedata[item]['PMCOVER'] == 'open' and guidedata[item]['DOMSHUTU'] == 'open':
        if guidedata[item]['OBSTYPE'] == 'SCIENCE' and guidedata[item]['FLAVOR'] == 'science' and guidedata[item]['DOMSHUTU'] == 'open':
            guide_start.append( (guidedata[item]['GUIDE-START'] - startdate)*24. )
            guide_width.append( (guidedata[item]['GUIDE-STOP'] - guidedata[item]['GUIDE-START'])*24. )
    
    return science_start, science_width, dither_start, dither_width, guide_start, guide_width
Пример #13
0
def calc_domevals(night, verbose=False):
    '''
    Calculate basic dome parameters for a given night.

    Parameters
    ----------
    night : int
        night in form 20210320 for 20 March 2021
    verbose : bool
        print verbose output?

    Returns
    -------
    dome_firstopen : float
        first time the dome opened (MJD)
    dome_lastclose : float
        last time dome closed (MJD)
    fractwiopen : float
        fraction of the time the dome was open between 18 deg twilights 
    twiopen_hrs : float
        hours the dome was open between 18 deg twilights
    '''

    twibeg_mjd, twiend_mjd = utils.get_twilights(int(night))
    startdate = int(twibeg_mjd)
    twibeg_hours = 24.*(twibeg_mjd - startdate)
    twiend_hours = 24.*(twiend_mjd - startdate)
    twitot_hours = twiend_hours - twibeg_hours

    # Get dome status:
    dome = get_dometelemetry(night)
    dometime = np.array([ (t - startdate)*24 for t in dome['dome_timestamp']])
    dome_open = np.array([t for t in dome['shutter_upper']], dtype=bool)

    # Compute total time with dome open between twilights:
    nmask = dometime >= twibeg_hours
    nmask = nmask*(dometime <= twiend_hours) # time between twilights
    dmask = nmask*dome_open # dome open
    open_fraction = np.sum(dmask)/np.sum(nmask)
    fractwiopen = open_fraction

    # Total time dome was open between twilights
    twiopen_hrs = open_fraction*twitot_hours

    # get times the dome first opened and last closed 
    # shutter_upper == 1 means dome was open
    open_indices = np.where(dome_open)[0]

    if verbose: 
        print("len(open_indices) = ", len(open_indices))

    # check if dome didn't open at all
    if len(open_indices) == 0:
        return 0., 0., 0., 0.

    # time stamps when dome first opened and last closed 
    firstopen = dome['dome_timestamp'][open_indices[0]]
    lastclose = dome['dome_timestamp'][open_indices[-1]]

    if verbose: 
        print("MJD for dome first open, last close:", firstopen, lastclose)
        print("UT for dome first open, last close:", Time(firstopen, format='mjd').datetime, Time(lastclose, format='mjd').datetime )
        print("Dome open: {0:.3f} Dome close: {1:.3f} Fraction of twilight with dome open = {2:.2f}".format(firstopen, lastclose, fractwiopen) )

    return firstopen, lastclose, fractwiopen, twiopen_hrs