Exemplo n.º 1
0
def star_tracktable(srcname, mjd1=None, mjd2=None):
    '''Generate a tracktable for the given Starburst source (flare star).
       This is just three lines giving the RA, Dec coordinates and times.
       Important: aipy is used to precess the coordinates to the current epoch
       from J2000 - this can be an effect of a couple tenths of a degree.
    '''
    if mjd1 is None:
        d = util.datime()
        mjd1 = int(d.get())
        mjd2 = mjd1 + 1.

    src = import_source(srcname)

    if type(src) is str:
        return 'Source ' + srcname + ' not found'
    else:
        aa = eovsa_array()
        aa.compute_pressure()
        cat = aipy.phs.SrcCatalog(src)
        tbl = ''
        dt = 0.5 / 24.
        mjd = mjd1 - 2 * dt
        while mjd < (mjd2 + 4 * dt):
            jultime = mjd + 2400000.5
            aa.set_jultime(jultime)
            cat.compute(aa)
            msec = round((mjd % 1) * 86400.) * 1000.
            tbl = tbl + '{0:7d} {1:7d} {2:5d} {3:8d}\n'.format(
                int(cat[srcname].ra * 1800000. / pi),
                int(cat[srcname].dec * 1800000. / pi), int(mjd), int(msec))
            mjd = mjd + dt
        return tbl
Exemplo n.º 2
0
def star_tracktable(srcname,mjd1=None,mjd2=None):
    '''Generate a tracktable for the given Starburst source (flare star).
       This is just three lines giving the RA, Dec coordinates and times.
       Important: aipy is used to precess the coordinates to the current epoch
       from J2000 - this can be an effect of a couple tenths of a degree.
    '''
    if mjd1 is None:
        d = util.datime()
        mjd1 = int(d.get())
        mjd2 = mjd1 + 1.

    src = import_source(srcname)

    if type(src) is str:
        return 'Source '+srcname+' not found'
    else:
        aa = eovsa_array()
        aa.compute_pressure()
        cat = aipy.phs.SrcCatalog(src)
        tbl = ''
        dt = 0.5/24.
        mjd = mjd1 - 2*dt
        while mjd < (mjd2+4*dt):
            jultime = mjd + 2400000.5
            aa.set_jultime(jultime)
            cat.compute(aa)
            msec = round((mjd % 1)*86400.)*1000.
            tbl = tbl+'{0:7d} {1:7d} {2:5d} {3:8d}\n'.format(int(cat[srcname].ra*1800000./pi),
                                                 int(cat[srcname].dec*1800000./pi),
                                                 int(mjd),int(msec))
            mjd = mjd + dt
        return tbl
Exemplo n.º 3
0
def calibrator_tracktable(srcname,mjd1=None,mjd2=None):
    '''Generate a tracktable for the given calibrator source (quasar).
       This is just three lines giving the precessed RA, Dec coordinates.
    '''
    if mjd1 is None:
        d = util.datime()
        mjd1 = int(d.get())
        mjd2 = mjd1 + 1.

    cal = readvlacaldb()
    for c in cal:
        if c.name.find(srcname) == 0:
            break

    if srcname in c.name:
        aa = eovsa_array()
        aa.compute_pressure()
        src = aipy.phs.RadioFixedBody(c.ra[0],c.dec[0],name=c.name,epoch='2000')
        cat = aipy.phs.SrcCatalog(src)
        tbl = ''
        dt = 0.5/24.
        mjd = mjd1 - 2*dt
        while mjd < (mjd2+4*dt):
            jultime = mjd + 2400000.5
            aa.set_jultime(jultime)
            cat.compute(aa)
            msec = round((mjd % 1)*86400.)*1000.
            tbl = tbl+'{:7d} {:7d} {:5d} {:8d}\n'.format(int(cat[c.name].ra*1800000./pi),
                                                 int(cat[c.name].dec*1800000./pi),
                                                 int(mjd),int(msec))
            mjd = mjd + dt

        return tbl
    else:
        return 'Source '+srcname+' not found'
Exemplo n.º 4
0
def solar_tracktable(mjd1=None,mjd2=None):
    '''Generate a solar tracktable with 1 h separation of solar coordinates
       between lines, from the time indicated by mjd1 to 1 hour past the time
       indicated by mjd2.  If called without arguments, a tracktable for the
       entire current day is generated.
    '''
    if mjd1 is None:
        d = util.datime()
        mjd1 = int(d.get())
        mjd2 = mjd1 + 1.
    aa = eovsa_array()
    aa.compute_pressure()
    srcs = aipy.phs.RadioSpecial('Sun')
    cat = aipy.phs.SrcCatalog(srcs)
    tbl = ''
    dt = 1/24.
    mjd = mjd1 - 2*dt
    while mjd < (mjd2+4*dt):
        jultime = mjd + 2400000.5
        aa.set_jultime(jultime)
        cat.compute(aa)
        msec = round((mjd % 1)*86400.)*1000.
        tbl = tbl+'{:7d} {:7d} {:5d} {:8d}\n'.format(int(cat['Sun'].ra*1800000./pi),
                                                     int(cat['Sun'].dec*1800000./pi),
                                                     int(mjd),int(msec))
        mjd = mjd + dt

    return tbl
Exemplo n.º 5
0
def solar_tracktable(mjd1=None, mjd2=None):
    '''Generate a solar tracktable with 1 h separation of solar coordinates
       between lines, from the time indicated by mjd1 to 1 hour past the time
       indicated by mjd2.  If called without arguments, a tracktable for the
       entire current day is generated.
    '''
    if mjd1 is None:
        d = util.datime()
        mjd1 = int(d.get())
        mjd2 = mjd1 + 1.
    aa = eovsa_array()
    aa.compute_pressure()
    srcs = aipy.phs.RadioSpecial('Sun')
    cat = aipy.phs.SrcCatalog(srcs)
    tbl = ''
    dt = 1 / 24.
    mjd = mjd1 - 2 * dt
    while mjd < (mjd2 + 4 * dt):
        jultime = mjd + 2400000.5
        aa.set_jultime(jultime)
        cat.compute(aa)
        msec = round((mjd % 1) * 86400.) * 1000.
        tbl = tbl + '{:7d} {:7d} {:5d} {:8d}\n'.format(
            int(cat['Sun'].ra * 1800000. / pi),
            int(cat['Sun'].dec * 1800000. / pi), int(mjd), int(msec))
        mjd = mjd + dt

    return tbl
Exemplo n.º 6
0
def eovsa_array_with_cat():
    ''' Return an aa object created by the eovsa_array module but with a source
        catalog in the .cat attribute.
    '''
    aa = eovsa_array()
    cat = load_cat()
    cat.compute(aa)
    aa.cat = cat
    return aa
Exemplo n.º 7
0
def eovsa_array_with_cat():
    ''' Return an aa object created by the eovsa_array module but with a source
        catalog in the .cat attribute.
    '''
    aa = eovsa_array()
    cat = load_cat()
    cat.compute(aa)
    aa.cat = cat
    return aa
Exemplo n.º 8
0
def eovsa_lst(tin=None):
    ''' Input is a Time() object (or None to use current time).
        Returns local sidereal time for EOVSA. NB: Now returns LST in radians,
        not as an RA_Angle()
    '''
    if tin is None:
        tin = Time.now()
    aa = eovsa_array()
    aa.set_jultime(tin.jd)
    return aa.sidereal_time()
Exemplo n.º 9
0
def eovsa_lst(tin=None):
    ''' Input is a Time() object (or None to use current time).
        Returns local sidereal time for EOVSA. NB: Now returns LST in radians,
        not as an RA_Angle()
    '''
    if tin is None:
        tin = Time.now()
    aa = eovsa_array()
    aa.set_jultime(tin.jd)
    return aa.sidereal_time()
Exemplo n.º 10
0
def findcal(cal, band='C', qual='P', t=None, dtheta=[20, 40]):
    '''Searches the VLA calibrator database [cal list returned by
       a call to readvlacaldb()] to find sources matching the band
       and quality codes provided:
           band = 'L', 'C', 'X', 'U', 'Q' (default 'C')
           qual = 'P', 'S', 'X', 'W' (default 'P')
       If t and dtheta are provided, only sources within an annulus 
       from dtheta[0] to dtheta[1] degrees from the Sun are selected, 
       for the time specified in Time() object t. Default is 20-40 deg.
       Returns a list of ephem source objects.  If t is not provided, 
       the current time is used.  In all cases, the first source in the 
       list is the Sun.
    '''
    srclist = []
    dtor = pi / 180.
    if t is None:
        time = Time.now().iso.replace('-', '/')[:16]
    else:
        time = t.iso.replace('-', '/')[:16]
    aa = eovsa_array()
    aa.set_ephemtime(time)
    aa.compute_pressure()
    sun = ephem.Sun()
    sun.compute(aa)
    ## Add sun to source list in first location
    srclist.append(sun)

    for i in range(len(cal)):
        if band in cal[i].band:
            j = (array(cal[i].band) == band) & (array(cal[i].qual) == qual)
            if j.any():
                flux = str(array(cal[i].flux)[j][0])
                name = cal[i].name
                ra = cal[i].ra[0]
                dec = cal[i].dec[0]
                src = ephem.readdb(name + ',f,' + ra + ',' + dec + ',' + flux +
                                   ',2000')
                src.compute(aa)
                if t is None:
                    # No selection based on distance to Sun, so append
                    srclist.append(src)
                else:
                    sep = ephem.separation(sun, src)
                    if sep > dtheta[0] * dtor and sep < dtheta[1] * dtor:
                        # This source qualifies so append it to list
                        srclist.append(src)

    return srclist, aa
Exemplo n.º 11
0
def findcal(cal, band='C', qual='P', t=None, dtheta=[20,40]):
    '''Searches the VLA calibrator database [cal list returned by
       a call to readvlacaldb()] to find sources matching the band
       and quality codes provided:
           band = 'L', 'C', 'X', 'U', 'Q' (default 'C')
           qual = 'P', 'S', 'X', 'W' (default 'P')
       If t and dtheta are provided, only sources within an annulus 
       from dtheta[0] to dtheta[1] degrees from the Sun are selected, 
       for the time specified in Time() object t. Default is 20-40 deg.
       Returns a list of ephem source objects.  If t is not provided, 
       the current time is used.  In all cases, the first source in the 
       list is the Sun.
    '''
    srclist = []
    dtor = pi/180.
    if t is None:
        time = Time.now().iso.replace('-','/')[:16]
    else:
        time = t.iso.replace('-','/')[:16]
    aa = eovsa_array()
    aa.set_ephemtime(time)
    aa.compute_pressure()
    sun = ephem.Sun()
    sun.compute(aa)
    ## Add sun to source list in first location
    srclist.append(sun)
        
    for i in range(len(cal)):
        if band in cal[i].band:
            j = (array(cal[i].band) == band) & (array(cal[i].qual) == qual)
            if j.any():
                flux = str(array(cal[i].flux)[j][0])
                name = cal[i].name
                ra = cal[i].ra[0]
                dec = cal[i].dec[0]
                src = ephem.readdb(name+',f,'+ra+','+dec+','+flux+',2000')
                src.compute(aa)
                if t is None:
                    # No selection based on distance to Sun, so append
                    srclist.append(src)
                else:
                    sep = ephem.separation(sun,src)
                    if sep > dtheta[0]*dtor and sep < dtheta[1]*dtor:
                        # This source qualifies so append it to list
                        srclist.append(src)

    return srclist, aa
Exemplo n.º 12
0
def compute_cal(cal, t):
    '''Given a cal object representing one of the VLA calibrators, 
       convert to an ephem object observed at OVSA for the time in 
       the given Time() object, t.  Returns the computed source
       and the antenna array object.
    '''
    time = t.iso.replace('-', '/')[:16]
    aa = eovsa_array()
    aa.set_ephemtime(time)
    aa.compute_pressure()
    flux = '1.0'  # Just a dummy flux since no band is specified
    name = cal.name
    ra = cal.ra[0]
    dec = cal.dec[0]
    src = ephem.readdb(name + ',f,' + ra + ',' + dec + ',' + flux + ',2000')
    src.compute(aa)
    return src, aa
Exemplo n.º 13
0
def compute_cal(cal,t):
    '''Given a cal object representing one of the VLA calibrators, 
       convert to an ephem object observed at OVSA for the time in 
       the given Time() object, t.  Returns the computed source
       and the antenna array object.
    '''
    time = t.iso.replace('-','/')[:16]
    aa = eovsa_array()
    aa.set_ephemtime(time)
    aa.compute_pressure()
    flux = '1.0'   # Just a dummy flux since no band is specified
    name = cal.name
    ra = cal.ra[0]
    dec = cal.dec[0]
    src = ephem.readdb(name+',f,'+ra+','+dec+','+flux+',2000')
    src.compute(aa)
    return src, aa
Exemplo n.º 14
0
def geosat_tracktable(srcname,mjd1=None,mjd2=None):
    '''Generate a tracktable for the given geostationary satellite.
       If MDJ1 and MJD2 are not set, the tracktable covers just over
       one day (the current UT day) with entries 1/2 hour apart. 
    '''
    if mjd1 is None:
        d = util.datime()
        mjd1 = int(d.get())
        mjd2 = mjd1 + 1.

    # Retrieve TLE file for geostationary satellites from Celestrak site.
    f = urllib2.urlopen('http://www.celestrak.com/NORAD/elements/geo.txt')
    lines = f.readlines()
    f.close()

    # Names in schedule have underscore ('_') in place of spaces, so we have
    # to convert back to spaces
    name = srcname.replace('_',' ')

    # Find the source name in the GEOSAT TLE file
    for i,line in enumerate(lines):
        if line.find(name) == 0:
            break

    if name in line:
        aa = eovsa_array()
        aa.compute_pressure()
        src = ephem.readtle(lines[i], lines[i+1], lines[i+2])
        tbl = ''
        dt = 0.5/24.
        mjd = mjd1 - 2*dt
        while mjd < (mjd2+4*dt):
            jultime = mjd + 2400000.5
            aa.set_jultime(jultime)
            src.compute(aa)
            msec = round((mjd % 1)*86400.)*1000.
            tbl = tbl+'{:7d} {:7d} {:5d} {:8d}\n'.format(int(src.ra*1800000./pi),
                                                 int(src.dec*1800000./pi),
                                                 int(mjd),int(msec))
            #print src.alt, src.az
            mjd = mjd + dt

        return tbl
    else:
        return 'Source '+name+' not found'
Exemplo n.º 15
0
def geosat_tracktable(srcname, mjd1=None, mjd2=None):
    '''Generate a tracktable for the given geostationary satellite.
       If MDJ1 and MJD2 are not set, the tracktable covers just over
       one day (the current UT day) with entries 1/2 hour apart. 
    '''
    if mjd1 is None:
        d = util.datime()
        mjd1 = int(d.get())
        mjd2 = mjd1 + 1.

    # Retrieve TLE file for geostationary satellites from Celestrak site.
    f = urllib2.urlopen('http://www.celestrak.com/NORAD/elements/geo.txt')
    lines = f.readlines()
    f.close()

    # Names in schedule have underscore ('_') in place of spaces, so we have
    # to convert back to spaces
    name = srcname.replace('_', ' ')

    # Find the source name in the GEOSAT TLE file
    for i, line in enumerate(lines):
        if line.find(name) == 0:
            break

    if name in line:
        aa = eovsa_array()
        aa.compute_pressure()
        src = ephem.readtle(lines[i], lines[i + 1], lines[i + 2])
        tbl = ''
        dt = 0.5 / 24.
        mjd = mjd1 - 2 * dt
        while mjd < (mjd2 + 4 * dt):
            jultime = mjd + 2400000.5
            aa.set_jultime(jultime)
            src.compute(aa)
            msec = round((mjd % 1) * 86400.) * 1000.
            tbl = tbl + '{:7d} {:7d} {:5d} {:8d}\n'.format(
                int(src.ra * 1800000. / pi), int(src.dec * 1800000. / pi),
                int(mjd), int(msec))
            #print src.alt, src.az
            mjd = mjd + dt

        return tbl
    else:
        return 'Source ' + name + ' not found'
Exemplo n.º 16
0
def calibrator_tracktable(srcname, mjd1=None, mjd2=None):
    '''Generate a tracktable for the given calibrator source (quasar).
       This is just three lines giving the precessed RA, Dec coordinates.
    '''
    if mjd1 is None:
        d = util.datime()
        mjd1 = int(d.get())
        mjd2 = mjd1 + 1.

    cal = readvlacaldb()
    for c in cal:
        if c.name.find(srcname) == 0:
            break

    if srcname in c.name:
        aa = eovsa_array()
        aa.compute_pressure()
        src = aipy.phs.RadioFixedBody(c.ra[0],
                                      c.dec[0],
                                      name=c.name,
                                      epoch='2000')
        cat = aipy.phs.SrcCatalog(src)
        tbl = ''
        dt = 0.5 / 24.
        mjd = mjd1 - 2 * dt
        while mjd < (mjd2 + 4 * dt):
            jultime = mjd + 2400000.5
            aa.set_jultime(jultime)
            cat.compute(aa)
            msec = round((mjd % 1) * 86400.) * 1000.
            tbl = tbl + '{:7d} {:7d} {:5d} {:8d}\n'.format(
                int(cat[c.name].ra * 1800000. / pi),
                int(cat[c.name].dec * 1800000. / pi), int(mjd), int(msec))
            mjd = mjd + dt

        return tbl
    else:
        return 'Source ' + srcname + ' not found'
Exemplo n.º 17
0
def starobs2dxeldel(filename=None,hadec=False):
    """Takes the star observations (calculated RA,Dec and measured RA,Dec) and
        calculates the differences in (Az, El).  The expected file format has the
        first three lines, below, and then a series of lines like the third:

        Num   Name        RA(J2000)    Dec(J2000)    RA(Meas)      Dec(Meas)  Time(Meas)
        ==== ========== ============ ============= ============ ============= ==========
          37 BD-18   14  0:12:10.000 -17:56:18.000  0:15:54.000 -18:35:23.000  02:38:00
        """

    if filename is None:
        filename = '/home/dgary/Dropbox/Python/Star Pointing/2012Jan09_starobs.txt'

    f = open(filename, 'r')
    lines = f.readlines()
    f.close()
    idx = filename.find('solutions')
    outfile = filename[0:idx]+'reduction'+filename[idx+9:]
    o = open(outfile,'w')
    line = lines[0]
    didx = filename.find('.txt')
    datstr = filename[didx-10:didx]
    # Loop over the lines in the file
    for line in lines[2:]:
        if line == '': break
        num = int(line[0:4])
        name = line[5:15]
        RA_J2000 = RA_Angle(line[16:28].strip(),'hms')
        Dec_J2000 = Dec_Angle(line[29:42].strip(),'dms')
        RA_Obs = RA_Angle(line[43:55].strip(),'hms')
        Dec_Obs = Dec_Angle(line[56:69].strip(),'dms')
        timstr = line[71:79]
        t = Time(datstr+' '+timstr)
        # Make a couple of fake sources, one for the nominal position, and one for the observed position
        srcnom = ephem.readdb('Nominal'+',f,'+line[16:28].strip()+','+line[29:42].strip()+','+'1.0,2000')
        srcobs = ephem.readdb('Observed'+',f,'+line[43:55].strip()+','+line[56:69].strip()+','+'1.0,2000')
        # Compute sources for EOVSA array at current time
        aa = eovsa_array()
        aa.set_jultime(t.jd)
        srcnom.compute(aa)
        srcobs.compute(aa)
        HA_Obs = eovsa_ha(srcnom,t)  # Updates srcnom for OVRO location and time Time_Obs)
        HA_Obs = eovsa_ha(srcobs,t)  # Updates srcobs for OVRO location and time Time_Obs)
        if hadec:
            ha1 = HA_Obs
            dec1 = Dec_Obs.get()
        az0 = srcnom.az
        el0 = srcnom.alt
        az1 = srcobs.az
        el1 = srcobs.alt

        # Calculate differences (in radians)
        d_xel = (az1 - az0) * cos(el1)
        d_el = (el1 - el0)

        # Write out a new file in the style of KSRBL (for now)
        timesec = int((t.mjd-int(t.mjd))*86400)
        dra = (srcobs.a_ra - srcnom.a_ra)*180/pi # Degrees
        ddec = (srcobs.a_dec - srcnom.a_dec)*180/pi # Degrees
        fmt = '{0:<8} {1:5d} {2:7.3f} {3:7.3f} {4:7.3f} {5:7.3f} {6:6.3f} {7:6.3f} {8:8.3f} {9:7.3f} {10:6.3f} {11:6.3f}'
        if hadec:
            # If the hadec switch is set, write out HA and Dec coordinates (ha1 and dec1)
            print fmt.format(name[0:8],timesec,srcnom.a_ra*180/pi, srcnom.a_dec*180/pi,
                        srcobs.a_ra*180/pi, srcobs.a_dec*180/pi, dra, ddec,
                        ha1*180/pi, dec1*180/pi, d_xel*180/pi, d_el*180/pi)
            o.write(fmt.format(name[0:8],timesec,srcnom.a_ra*180/pi, srcnom.a_dec*180/pi,
                        srcobs.a_ra*180/pi, srcobs.a_dec*180/pi, dra, ddec,
                        ha1*180/pi, dec1*180/pi, d_xel*180/pi, d_el*180/pi)+'\n')
        else:
            # Otherwise, write out AZ and EL coordinates (az1 and el1)
            print fmt.format(name[0:8],timesec,srcnom.a_ra*180/pi, srcnom.a_dec*180/pi,
                        srcobs.a_ra*180/pi, srcobs.a_dec*180/pi, dra, ddec,
                        az1*180/pi, el1*180/pi, d_xel*180/pi, d_el*180/pi)
            o.write(fmt.format(name[0:8],timesec,srcnom.a_ra*180/pi, srcnom.a_dec*180/pi,
                        srcobs.a_ra*180/pi, srcobs.a_dec*180/pi, dra, ddec,
                        az1*180/pi, el1*180/pi, d_xel*180/pi, d_el*180/pi)+'\n')
    o.close()
Exemplo n.º 18
0
def starobs2dxeldel(filename=None, hadec=False):
    """Takes the star observations (calculated RA,Dec and measured RA,Dec) and
        calculates the differences in (Az, El).  The expected file format has the
        first three lines, below, and then a series of lines like the third:

        Num   Name        RA(J2000)    Dec(J2000)    RA(Meas)      Dec(Meas)  Time(Meas)
        ==== ========== ============ ============= ============ ============= ==========
          37 BD-18   14  0:12:10.000 -17:56:18.000  0:15:54.000 -18:35:23.000  02:38:00
        """

    if filename is None:
        filename = '/home/dgary/Dropbox/Python/Star Pointing/2012Jan09_starobs.txt'

    f = open(filename, 'r')
    lines = f.readlines()
    f.close()
    idx = filename.find('solutions')
    outfile = filename[0:idx] + 'reduction' + filename[idx + 9:]
    o = open(outfile, 'w')
    line = lines[0]
    didx = filename.find('.txt')
    datstr = filename[didx - 10:didx]
    # Loop over the lines in the file
    for line in lines[2:]:
        if line == '': break
        num = int(line[0:4])
        name = line[5:15]
        RA_J2000 = RA_Angle(line[16:28].strip(), 'hms')
        Dec_J2000 = Dec_Angle(line[29:42].strip(), 'dms')
        RA_Obs = RA_Angle(line[43:55].strip(), 'hms')
        Dec_Obs = Dec_Angle(line[56:69].strip(), 'dms')
        timstr = line[71:79]
        t = Time(datstr + ' ' + timstr)
        # Make a couple of fake sources, one for the nominal position, and one for the observed position
        srcnom = ephem.readdb('Nominal' + ',f,' + line[16:28].strip() + ',' +
                              line[29:42].strip() + ',' + '1.0,2000')
        srcobs = ephem.readdb('Observed' + ',f,' + line[43:55].strip() + ',' +
                              line[56:69].strip() + ',' + '1.0,2000')
        # Compute sources for EOVSA array at current time
        aa = eovsa_array()
        aa.set_jultime(t.jd)
        srcnom.compute(aa)
        srcobs.compute(aa)
        HA_Obs = eovsa_ha(
            srcnom, t)  # Updates srcnom for OVRO location and time Time_Obs)
        HA_Obs = eovsa_ha(
            srcobs, t)  # Updates srcobs for OVRO location and time Time_Obs)
        if hadec:
            ha1 = HA_Obs
            dec1 = Dec_Obs.get()
        az0 = srcnom.az
        el0 = srcnom.alt
        az1 = srcobs.az
        el1 = srcobs.alt

        # Calculate differences (in radians)
        d_xel = (az1 - az0) * cos(el1)
        d_el = (el1 - el0)

        # Write out a new file in the style of KSRBL (for now)
        timesec = int((t.mjd - int(t.mjd)) * 86400)
        dra = (srcobs.a_ra - srcnom.a_ra) * 180 / pi  # Degrees
        ddec = (srcobs.a_dec - srcnom.a_dec) * 180 / pi  # Degrees
        fmt = '{0:<8} {1:5d} {2:7.3f} {3:7.3f} {4:7.3f} {5:7.3f} {6:6.3f} {7:6.3f} {8:8.3f} {9:7.3f} {10:6.3f} {11:6.3f}'
        if hadec:
            # If the hadec switch is set, write out HA and Dec coordinates (ha1 and dec1)
            print fmt.format(name[0:8], timesec, srcnom.a_ra * 180 / pi,
                             srcnom.a_dec * 180 / pi, srcobs.a_ra * 180 / pi,
                             srcobs.a_dec * 180 / pi, dra, ddec,
                             ha1 * 180 / pi, dec1 * 180 / pi, d_xel * 180 / pi,
                             d_el * 180 / pi)
            o.write(
                fmt.format(name[0:8], timesec, srcnom.a_ra * 180 /
                           pi, srcnom.a_dec * 180 / pi, srcobs.a_ra * 180 /
                           pi, srcobs.a_dec * 180 / pi, dra, ddec, ha1 * 180 /
                           pi, dec1 * 180 / pi, d_xel * 180 / pi, d_el * 180 /
                           pi) + '\n')
        else:
            # Otherwise, write out AZ and EL coordinates (az1 and el1)
            print fmt.format(name[0:8], timesec, srcnom.a_ra * 180 / pi,
                             srcnom.a_dec * 180 / pi, srcobs.a_ra * 180 / pi,
                             srcobs.a_dec * 180 / pi, dra, ddec,
                             az1 * 180 / pi, el1 * 180 / pi, d_xel * 180 / pi,
                             d_el * 180 / pi)
            o.write(
                fmt.format(name[0:8], timesec, srcnom.a_ra * 180 /
                           pi, srcnom.a_dec * 180 / pi, srcobs.a_ra * 180 /
                           pi, srcobs.a_dec * 180 / pi, dra, ddec, az1 * 180 /
                           pi, el1 * 180 / pi, d_xel * 180 / pi, d_el * 180 /
                           pi) + '\n')
    o.close()
Exemplo n.º 19
0
def scan_header(sh_dict, datfile='/tmp/scan_header.dat'):
    '''Writes the state frame header file from the scan header dictionary 
       created by the schedule. Returns file names datfile and xmlfile 
       corresponding to the output files in the /tmp directory that are 
       created by this routine and are updated at the start of each scan.  
       The format string fmt can be used with struct.unpack() to read 
       the data file /tmp/scan_header.dat, although that usage is not 
       anticipated except for testing.

       This routine does something sensible even if the supplied
       dictionary sh_dict is empty (i.e. is {}).
    '''
    dtor = pi / 180.
    xmlfile = datfile[:-4] + '.xml'
    f = open(datfile, 'wb')
    xml = open(xmlfile, 'w')
    xml.write('<Cluster>\n')
    xml.write('<Name>Scan_Header</Name>')
    xml.write('<NumElts>54</NumElts>')

    fmt = ''
    buf = ''

    # Scan Header Timestamp (double) [s, in LabVIEW format]
    # To be compatible with other timestamps in the stateframe, this
    # will be in LabVIEW format, which is s since 1904/01/01 (don't ask).
    item = sh_dict.get('timestamp', 0.0)
    fmt = '<d'
    buf = struct.pack('d', item)
    f.write(buf)
    xml.write('<DBL>\n')
    xml.write('<Name>Timestamp</Name>\n')
    xml.write('<Val></Val>\n')
    xml.write('</DBL>\n')

    # Schedule version (double) [N/A]
    # Version of the schedule/scan header stateframe.
    item = sh_dict.get('Version', 0.4)
    fmt += 'd'
    buf = struct.pack('d', item)
    f.write(buf)
    xml.write('<DBL>\n')
    xml.write('<Name>Version</Name>\n')
    xml.write('<Val>' + str(item) + '</Val>\n')
    xml.write('</DBL>\n')

    # Project (ascii, length 32 string)
    # Purpose of observations (correlator testing, routine observations)
    # Project is always the first thing added, so open the file
    # Default is 'NormalObserving'
    item = sh_dict.get('project', 'NormalObserving')
    fmt += 'I32s'
    buf = struct.pack('I32s', 32, item)
    f.write(buf)
    xml.write('<Array>\n')
    xml.write('<Name>Project</Name>\n')
    xml.write(
        '<Dimsize>32</Dimsize>\n<U8>\n<Name></Name>\n<Val></Val>\n</U8>\n')
    xml.write('</Array>\n')

    # Operator (ascii, length 16 string)
    # Default is 'Kjell Nelin'
    item = sh_dict.get('operator', 'Kjell Nelin')
    fmt += 'I16s'
    buf = struct.pack('I16s', 16, item)
    f.write(buf)
    xml.write('<Array>\n')
    xml.write('<Name>Operator</Name>\n')
    xml.write(
        '<Dimsize>16</Dimsize>\n<U8>\n<Name></Name>\n<Val></Val>\n</U8>\n')
    xml.write('</Array>\n')

    # Operator comments/log (ascii, length 120 string)
    # Default is 'None'
    item = sh_dict.get('comments', 'None')
    fmt += 'I120s'
    buf = struct.pack('I120s', 120, item)
    f.write(buf)
    xml.write('<Array>\n')
    xml.write('<Name>Comments</Name>\n')
    xml.write(
        '<Dimsize>120</Dimsize>\n<U8>\n<Name></Name>\n<Val></Val>\n</U8>\n')
    xml.write('</Array>\n')

    # Version (ascii, 2 length 8 strings)
    # Current hardware/software versions
    # Default is 1.0.0 and 1.0.0
    item = sh_dict.get('version', ['1.0.0', '1.0.0'])
    fmt += 'I8sI8s'
    buf = struct.pack('I8sI8s', 8, item[0], 8, item[1])
    f.write(buf)
    xml.write('<Array>\n')
    xml.write('<Name>HVersion</Name>\n')
    xml.write(
        '<Dimsize>8</Dimsize>\n<U8>\n<Name></Name>\n<Val></Val>\n</U8>\n')
    xml.write('</Array>\n')
    xml.write('<Array>\n')
    xml.write('<Name>SVersion</Name>\n')
    xml.write(
        '<Dimsize>8</Dimsize>\n<U8>\n<Name></Name>\n<Val></Val>\n</U8>\n')
    xml.write('</Array>\n')

    # Nants (unsigned integer)
    # Number of active antennas
    # Default is 16
    item = sh_dict.get('nants', 16)
    fmt += 'I'
    buf = struct.pack('I', item)
    f.write(buf)
    xml.write('<U32>')
    xml.write('<Name>Nants</Name>')
    xml.write('<Val></Val>')
    xml.write('</U32>')

    # Active antenna list (list of unsigned integers, length 16)
    item = sh_dict.get('antlist', range(1, 17))
    nants = len(item)
    fmt += 'I16I'
    buf = struct.pack('I', 16)
    for i in range(16):
        buf += struct.pack('I', item[i])
    f.write(buf)
    xml.write('<Array>\n')
    xml.write('<Name>Antlist</Name>\n')
    xml.write(
        '<Dimsize>16</Dimsize>\n<U32>\n<Name></Name>\n<Val></Val>\n</U32>\n')
    xml.write('</Array>\n')

    # Antpos (3 x 16 FP array) [ns]
    # Antenna equatorial coordinates
    # Default is nominal EOVSA array positions
    item = sh_dict.get('antpos', eovsa_array())
    fmt += '2I'
    buf = struct.pack('2I', 3, 16)
    for i in range(16):
        p = item.ants[i].pos
        fmt += '3d'
        buf += struct.pack('3d', p[0], p[1], p[2])
    f.write(buf)
    xml.write('<Array>\n')
    xml.write('<Name>Antpos</Name>\n')
    xml.write(
        '<Dimsize>3</Dimsize>\n<Dimsize>16</Dimsize><DBL>\n<Name></Name>\n<Val></Val>\n</DBL>\n'
    )
    xml.write('</Array>\n')

    # Pbfwhm (16-element FP array) [arcsec]
    # Primary beam FWHM at 1 GHz
    # Default is nominal primary beam for 13 2.1m dishes, 2 27m dishes, and 0.0 (bare-feed)
    item = sh_dict.get('pbfwhm', [1.22 * 30 * 180 * 3600. / 210. / pi] * 13 +
                       [1.22 * 30 * 180 * 3600. / 2700. / pi] * 2 + [0.0])
    fmt += 'I'
    buf = struct.pack('I', 16)
    fmt += '16f'
    for i in range(16):
        buf += struct.pack('f', item[i])
    f.write(buf)
    xml.write('<Array>\n')
    xml.write('<Name>PrimaryBeam</Name>\n')
    xml.write(
        '<Dimsize>16</Dimsize><SGL>\n<Name></Name>\n<Val></Val>\n</SGL>\n')
    xml.write('</Array>\n')

    # Mount (16-element signed integer array)
    # Type of antenna mounts
    # -1 = rf, 0 = bare-feed, 1 = 2m-azel, 2 = 27m-eq, 3 = 2m-eq
    # Default is list of 13 2m-azel, 2 27m-eq, and 1 bare-feed
    item = sh_dict.get('mount', [1] * 13 + [2] * 2 + [0])
    fmt += 'I16i'
    buf = struct.pack('I', 16)
    for i in range(16):
        buf += struct.pack('i', item[i])
    f.write(buf)
    xml.write('<Array>\n')
    xml.write('<Name>Mount</Name>\n')
    xml.write(
        '<Dimsize>16</Dimsize><I32>\n<Name></Name>\n<Val></Val>\n</I32>\n')
    xml.write('</Array>\n')

    # SCAN_ID (ascii, length 12)
    # A unique string that identifies the scan (yymmddhhmmss)
    # Default is current date/time
    dt = util.Time.now()
    item = sh_dict.get(
        'scan_id', dt.iso[2:19].replace('-', '').replace(':',
                                                         '').replace(' ', ''))
    fmt += 'I12s'
    buf = struct.pack('I12s', 12, item)
    f.write(buf)
    xml.write('<Array>\n')
    xml.write('<Name>ScanID</Name>\n')
    xml.write(
        '<Dimsize>12</Dimsize>\n<U8>\n<Name></Name>\n<Val></Val>\n</U8>\n')
    xml.write('</Array>\n')

    # SCAN_TYPE (ascii, length 12)
    # solar, calibration, etc
    # Default is 'test'
    item = sh_dict.get('scan_type', 'test')
    fmt += 'I12s'
    buf = struct.pack('I12s', 12, item)
    f.write(buf)
    xml.write('<Array>\n')
    xml.write('<Name>ScanType</Name>\n')
    xml.write(
        '<Dimsize>12</Dimsize>\n<U8>\n<Name></Name>\n<Val></Val>\n</U8>\n')
    xml.write('</Array>\n')

    # SOURCE_ID (ascii, length 12)
    # Name of source (e.g. 3C84, Sun, 0321+123, etc.)
    # Default is 'None'
    item = sh_dict.get('source_id', 'None')
    fmt += 'I12s'
    buf = struct.pack('I12s', 12, item)
    f.write(buf)
    xml.write('<Array>\n')
    xml.write('<Name>SourceID</Name>\n')
    xml.write(
        '<Dimsize>12</Dimsize>\n<U8>\n<Name></Name>\n<Val></Val>\n</U8>\n')
    xml.write('</Array>\n')

    # Coordinate type (ascii, length 6)
    # 'RADEC ' => fixed, use RA, Dec;
    # 'FIXED ' => geostationary, use HA, Dec;
    # 'PLANET' => planetary, use EPHEM
    # 'SATELL' => satellite tracking, use SAT Ephem
    # Default is 'PLANET'
    item = sh_dict.get('track_mode', 'PLANET')
    fmt += 'I6s'
    buf = struct.pack('I6s', 6, item)
    f.write(buf)
    xml.write('<Array>\n')
    xml.write('<Name>TrackMode</Name>\n')
    xml.write(
        '<Dimsize>6</Dimsize>\n<U8>\n<Name></Name>\n<Val></Val>\n</U8>\n')
    xml.write('</Array>\n')

    # epoch of source coordinates (ascii, length 4)
    # '2000' => J2000
    # 'DATE' => current date/time
    item = sh_dict.get('epoch', 'DATE')
    fmt += 'I4s'
    buf = struct.pack('I4s', 4, item)
    f.write(buf)
    xml.write('<Array>\n')
    xml.write('<Name>Epoch</Name>\n')
    xml.write(
        '<Dimsize>4</Dimsize>\n<U8>\n<Name></Name>\n<Val></Val>\n</U8>\n')
    xml.write('</Array>\n')

    # Right Ascension (double) [radians]
    # Default is current LST
    item = sh_dict.get('ra', eovsa_lst(dt))
    fmt += 'd'
    buf = struct.pack('d', item)
    f.write(buf)
    xml.write('<DBL>\n')
    xml.write('<Name>RA</Name>\n')
    xml.write('<Val></Val>\n')
    xml.write('</DBL>\n')

    # Declination (double) [radians]
    # Default is 0 Dec
    item = sh_dict.get('dec', 0.0)
    fmt += 'd'
    buf = struct.pack('d', item)
    f.write(buf)
    xml.write('<DBL>\n')
    xml.write('<Name>Dec</Name>\n')
    xml.write('<Val></Val>\n')
    xml.write('</DBL>\n')

    # Right Ascension Offset of phase center (double) [radians]
    # Default is 0
    item = sh_dict.get('dra', 0.0)
    fmt += 'd'
    buf = struct.pack('d', item)
    f.write(buf)
    xml.write('<DBL>\n')
    xml.write('<Name>dRA</Name>\n')
    xml.write('<Val></Val>\n')
    xml.write('</DBL>\n')

    # Declination Offset of phase center (double) [radians]
    # Default is 0
    item = sh_dict.get('ddec', 0.0)
    fmt += 'd'
    buf = struct.pack('d', item)
    f.write(buf)
    xml.write('<DBL>\n')
    xml.write('<Name>dDec</Name>\n')
    xml.write('<Val></Val>\n')
    xml.write('</DBL>\n')

    # Hour angle for geostationary sources (double) [radians]
    # Default is 0
    item = sh_dict.get('ha', 0.0)
    fmt += 'd'
    buf = struct.pack('d', item)
    f.write(buf)
    xml.write('<DBL>\n')
    xml.write('<Name>HA</Name>\n')
    xml.write('<Val></Val>\n')
    xml.write('</DBL>\n')

    # Hour angle offset for geostationary sources (double) [radians]
    # Default is 0
    item = sh_dict.get('dha', 0.0)
    fmt += 'd'
    buf = struct.pack('d', item)
    f.write(buf)
    xml.write('<DBL>\n')
    xml.write('<Name>dHA</Name>\n')
    xml.write('<Val></Val>\n')
    xml.write('</DBL>\n')

    aa = eovsa_array()
    aa.date = str(aa.date)[:11] + '00:00'  # Set date to 0 UT
    mjd0 = aa.date + 15019.5

    # Ephemeris for planetary source ([T, RA, Dec] x 3, double) [mjd, radians, radians]
    # Coordinates are geocentric, times are UTC, and apparent coords must be calculated from them
    # Default is the ephemeris for the Sun for the current date
    item = sh_dict.get('ephem')
    if item is None:
        # Generate default ephemeris, which is that for the Sun for current date
        item = []
        aa = eovsa_array()
        aa.date = str(aa.date)[:11] + '00:00'  # Set date to 0 UT
        mjd0 = aa.date + 15019.5
        sun = ephem.Sun()
        for i in range(3):
            sun.compute(aa)
            mjd = aa.date + 15019.5
            item.append([mjd, sun.g_ra, sun.g_dec])  # Geocentric coordinates
            aa.date += 1
    fmt += 'II' + '3d' * 3
    buf = struct.pack('2I', 3, 3)
    for i in range(3):
        for j in range(3):
            buf += struct.pack('d', item[i][j])
    f.write(buf)
    xml.write('<Array>\n')
    xml.write('<Name>Ephem</Name>\n')
    xml.write(
        '<Dimsize>3</Dimsize>\n<Dimsize>3</Dimsize><DBL>\n<Name></Name>\n<Val></Val>\n</DBL>\n'
    )
    xml.write('</Array>\n')

    # Calculate solar P, B0 and R (FP x 3) [radians, radians, arcsec]
    # Item is mjd of beginning of scan
    # Default is to calculate P, B0 and R for the current date
    item = sh_dict.get('sun_info', dt.mjd)
    fmt += 'Ifff'
    p, b0, r = get_pb0r(item, arcsec=True)
    buf = struct.pack('If', 3, p * dtor)
    buf += struct.pack('f', b0 * dtor)
    buf += struct.pack('f', r)
    f.write(buf)
    xml.write('<Array>\n')
    xml.write('<Name>SunInfo</Name>\n')
    xml.write(
        '<Dimsize>3</Dimsize>\n<SGL>\n<Name></Name>\n<Val></Val>\n</SGL>\n')
    xml.write('</Array>\n')

    # Satellite ephemeris data ([T, RA, Dec] x nlines, double) [mjd, radians, radians]
    # This is tricky, since we want a fixed stateframe length, so we
    # have to specify as many lines as the maximum we might use.  For
    # now, set to 20 lines.
    # Default is all zero
    item = sh_dict.get('sat_ephem', [[0.0, 0.0, 0.0]] * 20)
    nlines = len(item)
    fmt += 'II' + '3d' * 20
    buf = struct.pack('2I', 3, 20)
    for i in range(nlines):
        for j in range(3):
            buf += struct.pack('d', item[i][j])
    if nlines < 20:
        for i in range(nlines, 20):
            for j in range(3):
                buf += struct.pack('d', 0.0)
    f.write(buf)
    xml.write('<Array>\n')
    xml.write('<Name>SatEphem</Name>\n')
    xml.write(
        '<Dimsize>3</Dimsize>\n<Dimsize>20</Dimsize><DBL>\n<Name></Name>\n<Val></Val>\n</DBL>\n'
    )
    xml.write('</Array>\n')

    # Polarization list (Miriad definition) (signed int)
    #     1: Stokes I
    #     2: Stokes Q
    #     3: Stokes U
    #     4: Stokes V
    #    -1: Circular RR
    #    -2: Circular LL
    #    -3: Circular RL
    #    -4: Circular LR
    #    -5: Linear XX
    #    -6: Linear YY
    #    -7: Linear XY
    #    -8: Linear YX
    #     0: Not used
    # Zero-filled list of 4
    # Default is RR, LL
    item = sh_dict.get('pol', [-1, -2, 0, 0])
    fmt += '4i'
    buf = ''
    for i in range(4):
        buf += struct.pack('i', item[i])
    f.write(buf)
    xml.write('<I32>\n')
    xml.write('<Name>Pol1</Name>\n')
    xml.write('<Val></Val>\n')
    xml.write('</I32>\n')
    xml.write('<I32>\n')
    xml.write('<Name>Pol2</Name>\n')
    xml.write('<Val></Val>\n')
    xml.write('</I32>\n')
    xml.write('<I32>\n')
    xml.write('<Name>Pol3</Name>\n')
    xml.write('<Val></Val>\n')
    xml.write('</I32>\n')
    xml.write('<I32>\n')
    xml.write('<Name>Pol4</Name>\n')
    xml.write('<Val></Val>\n')
    xml.write('</I32>\n')

    # Timing offset (UT1-UTC) (double) [fraction of day]
    # Default is to read IERS bulletin and use (UT1-UTC) for current date
    item = sh_dict.get('ut1-utc', util.UT1_UTC(mjd0))
    fmt += 'd'
    buf = struct.pack('d', item)
    f.write(buf)
    xml.write('<DBL>\n')
    xml.write('<Name>UT1UTC</Name>\n')
    xml.write('<Val></Val>\n')
    xml.write('</DBL>\n')

    # Maximum IDB filesize (unsigned int) [MB]
    # DPP will break IDB scan data into separate files none
    # of which will exceed this size
    # Default is 100 (MB)
    item = sh_dict.get('max_file_size', 100)
    fmt += 'I'
    buf = struct.pack('I', item)
    f.write(buf)
    xml.write('<U32>\n')
    xml.write('<Name>IDBMaxFileSize</Name>\n')
    xml.write('<Val></Val>\n')
    xml.write('</U32>\n')

    # IDB filename stem (length 14 string of form yyyymmddhhmmss)
    # Value of item passed in is Time() object
    # Creates IDB filename stem value from Time() object.
    # Filename will be extended as needed to accommodate multiple files per scan.
    # (Note: truncated at integer second, since all times should happen at
    # 1pps boundary)
    # Default is current date/time
    item = sh_dict.get('date2IDB_stem', dt)
    fmt += 'I14s'
    datestr = item.iso.replace('-', '').replace(' ', '').replace(':', '')
    buf = struct.pack('I14s', 14, datestr[:14])
    f.write(buf)
    xml.write('<Array>\n')
    xml.write('<Name>IDBStem</Name>\n')
    xml.write(
        '<Dimsize>14</Dimsize>\n<U8>\n<Name></Name>\n<Val></Val>\n</U8>\n')
    xml.write('</Array>\n')

    # Nominal start time of scan (length 14 string of form yyyymmddhhmmss)
    # (Note: truncated at integer second, since all times should happen at
    # 1pps boundary)
    fmt += 'I14s'
    buf = struct.pack('I14s', 14, datestr[:14])
    f.write(buf)
    xml.write('<Array>\n')
    xml.write('<Name>IDBStart</Name>\n')
    xml.write(
        '<Dimsize>14</Dimsize>\n<U8>\n<Name></Name>\n<Val></Val>\n</U8>\n')
    xml.write('</Array>\n')

    # Time conversion factor (double) [mjd]
    # (i.e., Relates the absolute time to the accumulation number for the
    # packets).  This is the time of the next 1 pps after the ARM signal to
    # the correlator.
    # Default is current date/time, which is an error
    item = sh_dict.get('time_at_acc0', dt)
    fmt += 'd'
    buf = struct.pack('d', item.mjd)
    f.write(buf)
    xml.write('<DBL>\n')
    xml.write('<Name>TimeAtAcc0</Name>\n')
    xml.write('<Val></Val>\n')
    xml.write('</DBL>\n')

    # Duration of each spectral frame. Nominally 1 s. (unsigned int) [ms]
    # Default is 1000 [ms]
    item = sh_dict.get('dur_spec_frame', 1000)
    fmt += 'I'
    buf = struct.pack('I', item)
    f.write(buf)
    xml.write('<U32>\n')
    xml.write('<Name>DurSpecFrame</Name>\n')
    xml.write('<Val></Val>\n')
    xml.write('</U32>\n')

    # Intval Integration time (unsigned int) [ms]
    # Default is 20 [ms]
    item = sh_dict.get('intval', 20)
    fmt += 'I'
    buf = struct.pack('I', item)
    f.write(buf)
    xml.write('<U32>\n')
    xml.write('<Name>Intval</Name>\n')
    xml.write('<Val></Val>\n')
    xml.write('</U32>\n')

    # Number of integration intervals in each spectral frame. (unsigned int)
    # Default is 50 [per second].
    item = sh_dict.get('nintval', 50)
    fmt += 'I'
    buf = struct.pack('I', item)
    f.write(buf)
    xml.write('<U32>\n')
    xml.write('<Name>NIntval</Name>\n')
    xml.write('<Val></Val>\n')
    xml.write('</U32>\n')

    # List of frequencies of 600 MHz band start AFTER reversal (length 50 double) [GHz]
    # Input is integer band number corresponding to base frequency for
    # each integration interval, where 1 = 1 GHz, 2 = 1.5 GHz, etc.
    # (50 unsigned ints). These are the 500 MHz IF bands.  The integer
    # values n are converted to GHz as follows:
    #     21 + n/2.0 is LO frequency [GHz].
    #     LO frequency - 19.95 is frequency at low end of 600 MHz band
    #     After reversal of bins, subtract another 0.6 GHz
    #     Complete expression is equivalent to 0.45 + n/2.
    # The Hittite tuning list is passed in and parsed with comma ','
    # Default is nominal solar sequence
    #*******This needs modification for 800 MHz clock*******
    item = sh_dict.get(
        'fsequence', '1, 2, 3, 4, 5, 6, 7, 8, 9,10,' +
        '1, 2, 3, 4,11,12,13,14,15,16,' + '1, 2, 3, 4,17,18,19,20,21,22,' +
        '1, 2, 3, 4,23,24,25,26,27,28,' + '1, 2, 3, 4,29,30,31,32,33,34')
    # print item
    fseqlist = item.rsplit(',')
    nintval = len(fseqlist)
    fmt += 'I50d'
    buf = struct.pack('I', 50)
    for n in fseqlist:
        val = 0.45 + float(n) / 2.0
        buf += struct.pack('d', val)
    if nintval < 50:
        # Fewer than 50 values sent in, so zero-fill to 50
        for n in range(nintval, 50):
            buf += struct.pack('d', 0.0)
    f.write(buf)
    xml.write('<Array>\n')
    xml.write('<Name>FSeqList</Name>\n')
    xml.write(
        '<Dimsize>50</Dimsize><DBL>\n<Name></Name>\n<Val></Val>\n</DBL>\n')
    xml.write('</Array>\n')

    # Subband channel width [GHz]
    # This is fixed by the system to 0.6 GHz/4096.
    # Default is 0.6/4096
    item = sh_dict.get('subbw', 0.6 / 4096)
    fmt += 'd'
    buf = struct.pack('d', item)
    f.write(buf)
    xml.write('<DBL>\n')
    xml.write('<Name>SubBW</Name>\n')
    xml.write('<Val></Val>\n')
    xml.write('</DBL>\n')

    # Number of 'wide' spectral channels (unsigned int)
    # Default 504, max 511
    item = sh_dict.get('nchan', cu.tot_scichan())
    fmt += 'I'
    buf = struct.pack('I', item)
    f.write(buf)
    xml.write('<U32>\n')
    xml.write('<Name>Nchan</Name>\n')
    xml.write('<Val></Val>\n')
    xml.write('</U32>\n')

    # Wide spectral channel lower value--nominally 504 values (nchan doubles) [GHz]
    # Default is the default set of 504 frequencies valid for solar observing
    # (zero-filled to 511)
    item = sh_dict.get('fGHz')
    if item is None:
        item = []
        for band in range(1, 35):
            item += cu.start_freq(band)
    nchan = len(item)
    fmt += 'I511d'
    buf = struct.pack('I', 511)
    for i in item:
        buf += struct.pack('d', i)
    if nchan < 511:
        # Fewer than 511 values sent in, so zero-fill to 511
        for n in range(nchan, 511):
            buf += struct.pack('d', 0.0)
    f.write(buf)
    xml.write('<Array>\n')
    xml.write('<Name>fGHz</Name>\n')
    xml.write(
        '<Dimsize>511</Dimsize><DBL>\n<Name></Name>\n<Val></Val>\n</DBL>\n')
    xml.write('</Array>\n')

    # Wide spectral channel widths--nominally 504 values (nchan doubles) [GHz]
    # Default is the list of widths valid for solar observing (zero-filled to 511)
    item = sh_dict.get('chan_widths')
    if item is None:
        item = []
        for band in range(1, 35):
            item += cu.sci_bw(band)
    nchan = len(item)
    fmt += 'I511d'
    buf = struct.pack('I', 511)
    for i in item:
        buf += struct.pack('d', i)
    if nchan < 511:
        # Fewer than 511 values sent in, so zero-fill to 511
        for n in range(nchan, 511):
            buf += struct.pack('d', 0.0)
    f.write(buf)
    xml.write('<Array>\n')
    xml.write('<Name>ChanWidths</Name>\n')
    xml.write(
        '<Dimsize>511</Dimsize><DBL>\n<Name></Name>\n<Val></Val>\n</DBL>\n')
    xml.write('</Array>\n')

    # Science channel assignments (4096 x 50 array of 2-byte integers)
    #   0             = subband is not used.
    #   (1-500)       = science channel to assign subband.
    #   512 + (1-500) = integration should not be time-averaged.
    # Multiple samples <512 within a spectral frame are averaged.
    # Multiple samples >512 are not averaged and are treated
    # as separate channels.)
    # Restrictions:
    #   1. There are a maximum of 511 science channels.
    #   2. A given subband cannot be associated with more than one
    #      science channel.
    #   3. All subbands associated with a given science channel must
    #      be in the same 20 ms integration.
    item = sh_dict.get('chan2wide')
    if item is None:
        # Create default channel assignments for the list of frequencies (bands) in fseqlist
        item = []
        for band in fseqlist:
            ch = cu.chan_asmt(int(band))
            #ch[0] = 0  # Assign first subband to purgatory (unused subband)
            item += ch
    nsubchan = len(item)
    fmt += 'II204800H'
    buf = struct.pack('2I', 4096, 50)
    for i in item:
        buf += struct.pack('H', i)
    if nsubchan < 204800:
        # Fewer than 4096*50 values sent in, so zero-fill to 204800
        for n in range(nsubchan, 204800):
            buf += struct.pack('H', 0)
    f.write(buf)
    xml.write('<Array>\n')
    xml.write('<Name>Chan2Wide</Name>\n')
    xml.write(
        '<Dimsize>4096</Dimsize><Dimsize>50</Dimsize><U16>\n<Name></Name>\n<Val></Val>\n</U16>\n'
    )
    xml.write('</Array>\n')

    # Mask of RFI subbands (4096 x 50 array of 1-byte flags).
    #    0 = Subband is presumed contaminated
    #    1 = otherwise
    # Default is all 1 (good values)
    item = sh_dict.get('chanmask', np.array([1] * 204800, 'byte'))
    nsubchan = len(item)
    fmt += 'II204800B'
    buf = struct.pack('2I', 4096, 50)
    buf += struct.pack(str(nsubchan) + 'B', *item)
    if nsubchan < 204800:
        # Fewer than 4096*50 values sent in, so zero-fill to 204800
        for n in range(nsubchan, 204800):
            buf += struct.pack('B', 0)
    f.write(buf)
    xml.write('<Array>\n')
    xml.write('<Name>Chanmask</Name>\n')
    xml.write(
        '<Dimsize>4096</Dimsize><Dimsize>50</Dimsize><B8>\n<Name></Name>\n<Val></Val>\n</B8>\n'
    )
    xml.write('</Array>\n')

    # RFI guard band (4-byte int)
    # Number of subbands to reject adjacent to kurtosis-flagged channels.
    #    0 = do not reject any additional subbands
    # Default is 0
    item = sh_dict.get('sk_guard_width', 0)
    fmt += 'I'
    buf = struct.pack('I', item)
    f.write(buf)
    xml.write('<U32>\n')
    xml.write('<Name>SKGuard</Name>\n')
    xml.write('<Val></Val>\n')
    xml.write('</U32>\n')

    # Spectral kurtosis strategy (4-byte int) (Details TBD.)
    # Default 0 => Standard strategy
    item = sh_dict.get('sk_mode', 0)
    fmt += 'I'
    buf = struct.pack('I', item)
    f.write(buf)
    xml.write('<U32>\n')
    xml.write('<Name>SKMode</Name>\n')
    xml.write('<Val></Val>\n')
    xml.write('</U32>\n')

    # Kurtosis lower and upper limits (two doubles)
    # Min, max acceptable SK thresholds
    # Default is 0.0, 0.0 -- Must be changed when calculated correctly
    item = sh_dict.get('sk_lims', [0.0, 0.0])
    fmt += '2d'
    buf = struct.pack('2d', item[0], item[1])
    f.write(buf)
    xml.write('<DBL>\n')
    xml.write('<Name>SKLimLo</Name>\n')
    xml.write('<Val></Val>\n')
    xml.write('</DBL>\n')
    xml.write('<DBL>\n')
    xml.write('<Name>SKLimHi</Name>\n')
    xml.write('<Val></Val>\n')
    xml.write('</DBL>\n')

    # Reference complex gains (511 x 16 x 2 complex array) [Jy/unit]
    # These are time-independent values that correspond to the
    # 'base attenuator state'  which includes the effects of the
    # preset front-end and back-end attenuators. (Amplitude units
    # are nominally Jy, but may be modified by attenuator and
    # time-variable gain factors)
    # Note that Struct has no complex format, so buffer is written
    # with each complex value as a pair of floats, real,imag.
    # Default 1 + 0j
    item = sh_dict.get('gains', [1.0 + 0j] * 16352)
    fmt += 'IIII16352d'
    buf = struct.pack('4I', 2, 511, 16, 2)
    for i in item:
        buf += struct.pack('2d', np.real(i), np.imag(i))
    f.write(buf)
    xml.write('<Array>\n')
    xml.write('<Name>GainTable</Name>\n')
    xml.write(
        '<Dimsize>2</Dimsize><Dimsize>511</Dimsize><Dimsize>16</Dimsize><Dimsize>2</Dimsize><DBL>\n<Name></Name>\n<Val></Val>\n</DBL>\n'
    )
    xml.write('</Array>\n')

    # Time variable gain factors (511 x 16 x 2 complex array) [Jy]
    # Antenna-based complex gains corresponding to time-variable
    # factors (presumably determined by a recent phase calibration).
    # Defaults to 1 + 0j if not present.
    item = sh_dict.get('gain_update', [1.0 + 0j] * 16352)
    fmt += 'IIII16352d'
    buf = struct.pack('4I', 2, 511, 16, 2)
    for i in item:
        buf += struct.pack('2d', np.real(i), np.imag(i))
    f.write(buf)
    xml.write('<Array>\n')
    xml.write('<Name>GainUpdate</Name>\n')
    xml.write(
        '<Dimsize>2</Dimsize><Dimsize>511</Dimsize><Dimsize>16</Dimsize><Dimsize>2</Dimsize><DBL>\n<Name></Name>\n<Val></Val>\n</DBL>\n'
    )
    xml.write('</Array>\n')

    # Base attenuator value (4-byte int) [dB]
    # attenuator value corresponding to the base attenuator state
    # Default 0
    item = sh_dict.get('attn0', 0)
    fmt += 'I'
    buf = struct.pack('I', item)
    f.write(buf)
    xml.write('<U32>\n')
    xml.write('<Name>Attn0</Name>\n')
    xml.write('<Val></Val>\n')
    xml.write('</U32>\n')

    # Attenuator step size (4-byte int) [dB]
    # Nominal step size for time-dependent variable attenuation
    # 2-byte integer (presumably = 3 or 5 db)
    # Default 3 [dB]
    item = sh_dict.get('attn_step', 3)
    fmt += 'I'
    buf = struct.pack('I', item)
    f.write(buf)
    xml.write('<U32>\n')
    xml.write('<Name>AttnStep</Name>\n')
    xml.write('<Val></Val>\n')
    xml.write('</U32>\n')

    # Attenuator gain table (500 x 16 x 2 x N complex array)
    # complex gains (one for each science channel, polarization and antenna).
    # The values are relative to the 'base' attenuator settings, which represents
    # both the front end and back end attenuation.  Each of the N steps
    # corresponds to 3 or 5 db increments applied to all antennas.
    #item = sh_dict.get('attn_table')

    # XML encapsulation of KatADC info (array of 8)
    xml.write('<Array>\n')
    xml.write('<Name>KatADC</Name>\n')
    xml.write('<Dimsize>8</Dimsize>\n')
    # Put array dimension into data
    fmt += 'I'
    buf = struct.pack('I', 8)

    # This is an array, but the definitions only go in once
    # Here are the definitions for the 8 elements in the cluster
    xml.write('<Cluster>\n')
    xml.write('<Name/>\n')
    xml.write('<NumElts>6</NumElts>\n')
    xml.write('<U32>\n')
    xml.write('<Name>Status</Name>\n')
    xml.write('<Val></Val>\n')
    xml.write('</U32>\n')
    xml.write('<SGL>\n')
    xml.write('<Name>Temp.adc0</Name>\n')
    xml.write('<Val></Val>\n')
    xml.write('</SGL>\n')
    xml.write('<SGL>\n')
    xml.write('<Name>Temp.adc1</Name>\n')
    xml.write('<Val></Val>\n')
    xml.write('</SGL>\n')
    xml.write('<SGL>\n')
    xml.write('<Name>Temp.ambient0</Name>\n')
    xml.write('<Val></Val>\n')
    xml.write('</SGL>\n')
    xml.write('<SGL>\n')
    xml.write('<Name>Temp.ambient1</Name>\n')
    xml.write('<Val></Val>\n')
    xml.write('</SGL>\n')
    xml.write('<SGL>\n')
    xml.write('<Name>BoardClock</Name>\n')
    xml.write('<Val></Val>\n')
    xml.write('</SGL>\n')
    # End KatADC Cluster
    xml.write('</Cluster>\n')

    # Get the KatADC array of dictionaries (defaults to eight empty dicts)
    katadc = sh_dict.get('katadc', [{}, {}, {}, {}, {}, {}, {}, {}])
    brd_clk = sh_dict.get('roach_brd_clk',
                          [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0])
    # The actual data has to go in eight times
    for i in range(8):
        # Handle case of empty dictionary
        if katadc[i] == {}:
            fmt += 'Iffff'
            buf += struct.pack('I', 0)
            for j in range(4):
                buf += struct.pack('f', 0.0)

        else:
            # Status (int) [bit list]
            # Each bit signifies whether corresponding sensor is nominal [0] or in error [1]
            # Order is alphabetical, msb to lsb.
            # Default is zero
            status = 0
            keys = sorted(katadc[i].keys())
            for key in keys:
                if key.find('status') != -1:
                    if katadc[i][key] == 'nominal':
                        status = status << 1
                    else:
                        status = (status << 1) + 1
            item = status
            fmt += 'I'
            buf += struct.pack('I', item)

            # Just add the sorted list of sensors
            # Default is zero
            for key in keys:
                if key.find('status') == -1:
                    item = katadc[i][key]
                    fmt += 'f'
                    buf += struct.pack('f', item)

        fmt += 'f'
        buf += struct.pack('f', brd_clk[i])

    # End KatADC Array
    xml.write('</Array>\n')

    f.write(buf)
    f.close()
    xml.write('</Cluster>\n')
    xml.close()

    # Connect to ACC /parm directory and transfer scan_header files
    try:
        acc = FTP('acc.solar.pvt')
        acc.login('admin', 'observer')
        acc.cwd('parm')
        # Send XML file to ACC
        f = open(xmlfile, 'r')
        acc.storlines('STOR scan_header.xml', f)
        f.close()
        # Send DAT file to ACC
        g = open(datfile, 'rb')
        acc.storbinary('STOR ' + datfile[5:], g)
        acc.close()
        g.close()
        sys.stdout.write(
            'Successfully transferred scan_header files to ACC.\n')
    except:
        sys.stdout.write(
            'Could not transfer scan_header files.  ACC is down?\n')

    return fmt, datfile, xmlfile
Exemplo n.º 20
0
def scan_header(sh_dict,datfile='/tmp/scan_header.dat'):
    '''Writes the state frame header file from the scan header dictionary 
       created by the schedule. Returns file names datfile and xmlfile 
       corresponding to the output files in the /tmp directory that are 
       created by this routine and are updated at the start of each scan.  
       The format string fmt can be used with struct.unpack() to read 
       the data file /tmp/scan_header.dat, although that usage is not 
       anticipated except for testing.

       This routine does something sensible even if the supplied
       dictionary sh_dict is empty (i.e. is {}).
    '''
    dtor = pi/180.
    xmlfile = datfile[:-4]+'.xml'
    f = open(datfile,'wb')
    xml = open(xmlfile,'w')
    xml.write('<Cluster>\n')
    xml.write('<Name>Scan_Header</Name>')
    xml.write('<NumElts>54</NumElts>')

    fmt = ''
    buf = ''
    
    # Scan Header Timestamp (double) [s, in LabVIEW format]
    # To be compatible with other timestamps in the stateframe, this
    # will be in LabVIEW format, which is s since 1904/01/01 (don't ask).
    item = sh_dict.get('timestamp',0.0)
    fmt = '<d'
    buf = struct.pack('d',item)
    f.write(buf)
    xml.write('<DBL>\n')
    xml.write('<Name>Timestamp</Name>\n')
    xml.write('<Val></Val>\n')
    xml.write('</DBL>\n')
    
    # Schedule version (double) [N/A]
    # Version of the schedule/scan header stateframe.
    item = sh_dict.get('Version',0.4)
    fmt += 'd'
    buf = struct.pack('d',item)
    f.write(buf)
    xml.write('<DBL>\n')
    xml.write('<Name>Version</Name>\n')
    xml.write('<Val>'+str(item)+'</Val>\n')
    xml.write('</DBL>\n')

    # Project (ascii, length 32 string)
    # Purpose of observations (correlator testing, routine observations)
    # Project is always the first thing added, so open the file
    # Default is 'NormalObserving'
    item = sh_dict.get('project','NormalObserving')
    fmt += 'I32s'
    buf = struct.pack('I32s',32,item)
    f.write(buf)
    xml.write('<Array>\n')
    xml.write('<Name>Project</Name>\n')
    xml.write('<Dimsize>32</Dimsize>\n<U8>\n<Name></Name>\n<Val></Val>\n</U8>\n')
    xml.write('</Array>\n')

    # Operator (ascii, length 16 string)
    # Default is 'Kjell Nelin'
    item = sh_dict.get('operator','Kjell Nelin')
    fmt += 'I16s'
    buf = struct.pack('I16s',16,item)
    f.write(buf)
    xml.write('<Array>\n')
    xml.write('<Name>Operator</Name>\n')
    xml.write('<Dimsize>16</Dimsize>\n<U8>\n<Name></Name>\n<Val></Val>\n</U8>\n')
    xml.write('</Array>\n')

    # Operator comments/log (ascii, length 120 string)
    # Default is 'None'
    item = sh_dict.get('comments','None')
    fmt += 'I120s'
    buf = struct.pack('I120s',120,item)
    f.write(buf)
    xml.write('<Array>\n')
    xml.write('<Name>Comments</Name>\n')
    xml.write('<Dimsize>120</Dimsize>\n<U8>\n<Name></Name>\n<Val></Val>\n</U8>\n')
    xml.write('</Array>\n')

    # Version (ascii, 2 length 8 strings)
    # Current hardware/software versions
    # Default is 1.0.0 and 1.0.0 
    item = sh_dict.get('version',['1.0.0','1.0.0'])
    fmt += 'I8sI8s'
    buf = struct.pack('I8sI8s',8,item[0],8,item[1])
    f.write(buf)
    xml.write('<Array>\n')
    xml.write('<Name>HVersion</Name>\n')
    xml.write('<Dimsize>8</Dimsize>\n<U8>\n<Name></Name>\n<Val></Val>\n</U8>\n')
    xml.write('</Array>\n')
    xml.write('<Array>\n')
    xml.write('<Name>SVersion</Name>\n')
    xml.write('<Dimsize>8</Dimsize>\n<U8>\n<Name></Name>\n<Val></Val>\n</U8>\n')
    xml.write('</Array>\n')

    # Nants (unsigned integer)
    # Number of active antennas
    # Default is 16
    item = sh_dict.get('nants',16)
    fmt += 'I'
    buf = struct.pack('I',item)
    f.write(buf)
    xml.write('<U32>')
    xml.write('<Name>Nants</Name>')
    xml.write('<Val></Val>')
    xml.write('</U32>')

    # Active antenna list (list of unsigned integers, length 16)
    item = sh_dict.get('antlist',range(1,17))
    nants = len(item)
    fmt += 'I16I'
    buf = struct.pack('I',16)
    for i in range(16):
        buf += struct.pack('I',item[i])
    f.write(buf)
    xml.write('<Array>\n')
    xml.write('<Name>Antlist</Name>\n')
    xml.write('<Dimsize>16</Dimsize>\n<U32>\n<Name></Name>\n<Val></Val>\n</U32>\n')
    xml.write('</Array>\n')

    # Antpos (3 x 16 FP array) [ns]
    # Antenna equatorial coordinates
    # Default is nominal EOVSA array positions
    item = sh_dict.get('antpos',eovsa_array())
    fmt += '2I'
    buf = struct.pack('2I',3,16)
    for i in range(16):
        p = item.ants[i].pos
        fmt += '3d'
        buf += struct.pack('3d',p[0],p[1],p[2])
    f.write(buf)
    xml.write('<Array>\n')
    xml.write('<Name>Antpos</Name>\n')
    xml.write('<Dimsize>3</Dimsize>\n<Dimsize>16</Dimsize><DBL>\n<Name></Name>\n<Val></Val>\n</DBL>\n')
    xml.write('</Array>\n')

    # Pbfwhm (16-element FP array) [arcsec]
    # Primary beam FWHM at 1 GHz
    # Default is nominal primary beam for 13 2.1m dishes, 2 27m dishes, and 0.0 (bare-feed)
    item = sh_dict.get('pbfwhm',[1.22*30*180*3600./210./pi]*13 + [1.22*30*180*3600./2700./pi]*2 + [0.0])
    fmt += 'I'
    buf = struct.pack('I',16)
    fmt += '16f'
    for i in range(16):
        buf += struct.pack('f',item[i])
    f.write(buf)
    xml.write('<Array>\n')
    xml.write('<Name>PrimaryBeam</Name>\n')
    xml.write('<Dimsize>16</Dimsize><SGL>\n<Name></Name>\n<Val></Val>\n</SGL>\n')
    xml.write('</Array>\n')

    # Mount (16-element signed integer array)
    # Type of antenna mounts
    # -1 = rf, 0 = bare-feed, 1 = 2m-azel, 2 = 27m-eq, 3 = 2m-eq
    # Default is list of 13 2m-azel, 2 27m-eq, and 1 bare-feed
    item = sh_dict.get('mount',[1]*13 + [2]*2 + [0])
    fmt += 'I16i'
    buf = struct.pack('I',16)
    for i in range(16):
        buf += struct.pack('i',item[i])
    f.write(buf)
    xml.write('<Array>\n')
    xml.write('<Name>Mount</Name>\n')
    xml.write('<Dimsize>16</Dimsize><I32>\n<Name></Name>\n<Val></Val>\n</I32>\n')
    xml.write('</Array>\n')

    # SCAN_ID (ascii, length 12) 
    # A unique string that identifies the scan (yymmddhhmmss)
    # Default is current date/time
    dt = util.Time.now()
    item = sh_dict.get('scan_id',dt.iso[2:19].replace('-','').replace(':','').replace(' ',''))
    fmt += 'I12s'
    buf = struct.pack('I12s',12,item)
    f.write(buf)
    xml.write('<Array>\n')
    xml.write('<Name>ScanID</Name>\n')
    xml.write('<Dimsize>12</Dimsize>\n<U8>\n<Name></Name>\n<Val></Val>\n</U8>\n')
    xml.write('</Array>\n')

    # SCAN_TYPE (ascii, length 12)
    # solar, calibration, etc
    # Default is 'test'
    item = sh_dict.get('scan_type','test')
    fmt += 'I12s'
    buf = struct.pack('I12s',12,item)
    f.write(buf)
    xml.write('<Array>\n')
    xml.write('<Name>ScanType</Name>\n')
    xml.write('<Dimsize>12</Dimsize>\n<U8>\n<Name></Name>\n<Val></Val>\n</U8>\n')
    xml.write('</Array>\n')

    # SOURCE_ID (ascii, length 12)
    # Name of source (e.g. 3C84, Sun, 0321+123, etc.)
    # Default is 'None'
    item = sh_dict.get('source_id','None')
    fmt += 'I12s'
    buf = struct.pack('I12s',12,item)
    f.write(buf)
    xml.write('<Array>\n')
    xml.write('<Name>SourceID</Name>\n')
    xml.write('<Dimsize>12</Dimsize>\n<U8>\n<Name></Name>\n<Val></Val>\n</U8>\n')
    xml.write('</Array>\n')

    # Coordinate type (ascii, length 6)
    # 'RADEC ' => fixed, use RA, Dec;
    # 'FIXED ' => geostationary, use HA, Dec;
    # 'PLANET' => planetary, use EPHEM
    # 'SATELL' => satellite tracking, use SAT Ephem
    # Default is 'PLANET'
    item = sh_dict.get('track_mode','PLANET')
    fmt += 'I6s'
    buf = struct.pack('I6s',6,item)
    f.write(buf)
    xml.write('<Array>\n')
    xml.write('<Name>TrackMode</Name>\n')
    xml.write('<Dimsize>6</Dimsize>\n<U8>\n<Name></Name>\n<Val></Val>\n</U8>\n')
    xml.write('</Array>\n')

    # epoch of source coordinates (ascii, length 4)
    # '2000' => J2000
    # 'DATE' => current date/time
    item = sh_dict.get('epoch','DATE')
    fmt += 'I4s'
    buf = struct.pack('I4s',4,item)
    f.write(buf)
    xml.write('<Array>\n')
    xml.write('<Name>Epoch</Name>\n')
    xml.write('<Dimsize>4</Dimsize>\n<U8>\n<Name></Name>\n<Val></Val>\n</U8>\n')
    xml.write('</Array>\n')

    # Right Ascension (double) [radians]
    # Default is current LST
    item = sh_dict.get('ra',eovsa_lst(dt))
    fmt += 'd'
    buf = struct.pack('d',item)
    f.write(buf)
    xml.write('<DBL>\n')
    xml.write('<Name>RA</Name>\n')
    xml.write('<Val></Val>\n')
    xml.write('</DBL>\n')

    # Declination (double) [radians]
    # Default is 0 Dec
    item = sh_dict.get('dec',0.0)
    fmt += 'd'
    buf = struct.pack('d',item)
    f.write(buf)
    xml.write('<DBL>\n')
    xml.write('<Name>Dec</Name>\n')
    xml.write('<Val></Val>\n')
    xml.write('</DBL>\n')

    # Right Ascension Offset of phase center (double) [radians]
    # Default is 0
    item = sh_dict.get('dra',0.0)
    fmt += 'd'
    buf = struct.pack('d',item)
    f.write(buf)
    xml.write('<DBL>\n')
    xml.write('<Name>dRA</Name>\n')
    xml.write('<Val></Val>\n')
    xml.write('</DBL>\n')

    # Declination Offset of phase center (double) [radians]
    # Default is 0
    item = sh_dict.get('ddec',0.0)
    fmt += 'd'
    buf = struct.pack('d',item)
    f.write(buf)
    xml.write('<DBL>\n')
    xml.write('<Name>dDec</Name>\n')
    xml.write('<Val></Val>\n')
    xml.write('</DBL>\n')

    # Hour angle for geostationary sources (double) [radians]
    # Default is 0
    item = sh_dict.get('ha',0.0)
    fmt += 'd'
    buf = struct.pack('d',item)
    f.write(buf)
    xml.write('<DBL>\n')
    xml.write('<Name>HA</Name>\n')
    xml.write('<Val></Val>\n')
    xml.write('</DBL>\n')

    # Hour angle offset for geostationary sources (double) [radians]
    # Default is 0
    item = sh_dict.get('dha',0.0)
    fmt += 'd'
    buf = struct.pack('d',item)
    f.write(buf)
    xml.write('<DBL>\n')
    xml.write('<Name>dHA</Name>\n')
    xml.write('<Val></Val>\n')
    xml.write('</DBL>\n')

    aa = eovsa_array()
    aa.date = str(aa.date)[:11]+'00:00'  # Set date to 0 UT
    mjd0 = aa.date + 15019.5

    # Ephemeris for planetary source ([T, RA, Dec] x 3, double) [mjd, radians, radians]
    # Coordinates are geocentric, times are UTC, and apparent coords must be calculated from them
    # Default is the ephemeris for the Sun for the current date
    item = sh_dict.get('ephem')
    if item is None:
        # Generate default ephemeris, which is that for the Sun for current date
        item = []
        aa = eovsa_array()
        aa.date = str(aa.date)[:11]+'00:00'  # Set date to 0 UT
        mjd0 = aa.date + 15019.5
        sun = ephem.Sun()
        for i in range(3):
            sun.compute(aa)
            mjd = aa.date + 15019.5
            item.append([mjd, sun.g_ra, sun.g_dec])  # Geocentric coordinates
            aa.date += 1
    fmt += 'II' + '3d'*3
    buf = struct.pack('2I',3,3)
    for i in range(3):
        for j in range(3):
            buf += struct.pack('d',item[i][j])
    f.write(buf)
    xml.write('<Array>\n')
    xml.write('<Name>Ephem</Name>\n')
    xml.write('<Dimsize>3</Dimsize>\n<Dimsize>3</Dimsize><DBL>\n<Name></Name>\n<Val></Val>\n</DBL>\n')
    xml.write('</Array>\n')

    # Calculate solar P, B0 and R (FP x 3) [radians, radians, arcsec]
    # Item is mjd of beginning of scan
    # Default is to calculate P, B0 and R for the current date
    item = sh_dict.get('sun_info',dt.mjd)
    fmt += 'Ifff'
    p, b0, r = get_pb0r(item,arcsec=True)
    buf = struct.pack('If',3,p*dtor)
    buf += struct.pack('f',b0*dtor)
    buf += struct.pack('f',r)
    f.write(buf)
    xml.write('<Array>\n')
    xml.write('<Name>SunInfo</Name>\n')
    xml.write('<Dimsize>3</Dimsize>\n<SGL>\n<Name></Name>\n<Val></Val>\n</SGL>\n')
    xml.write('</Array>\n')

    # Satellite ephemeris data ([T, RA, Dec] x nlines, double) [mjd, radians, radians]
    # This is tricky, since we want a fixed stateframe length, so we
    # have to specify as many lines as the maximum we might use.  For
    # now, set to 20 lines.
    # Default is all zero
    item = sh_dict.get('sat_ephem',[[0.0,0.0,0.0]]*20)
    nlines = len(item)
    fmt += 'II' + '3d'*20
    buf = struct.pack('2I',3,20)
    for i in range(nlines):
        for j in range(3):
            buf += struct.pack('d',item[i][j])
    if nlines < 20:
        for i in range(nlines,20):
            for j in range(3):
                buf += struct.pack('d',0.0)            
    f.write(buf)
    xml.write('<Array>\n')
    xml.write('<Name>SatEphem</Name>\n')
    xml.write('<Dimsize>3</Dimsize>\n<Dimsize>20</Dimsize><DBL>\n<Name></Name>\n<Val></Val>\n</DBL>\n')
    xml.write('</Array>\n')
    
    # Polarization list (Miriad definition) (signed int)
    #     1: Stokes I
    #     2: Stokes Q
    #     3: Stokes U
    #     4: Stokes V
    #    -1: Circular RR
    #    -2: Circular LL
    #    -3: Circular RL
    #    -4: Circular LR
    #    -5: Linear XX
    #    -6: Linear YY
    #    -7: Linear XY
    #    -8: Linear YX
    #     0: Not used
    # Zero-filled list of 4
    # Default is RR, LL
    item = sh_dict.get('pol',[-1, -2, 0, 0])
    fmt += '4i'
    buf = ''
    for i in range(4):
       buf += struct.pack('i',item[i])
    f.write(buf)
    xml.write('<I32>\n')
    xml.write('<Name>Pol1</Name>\n')
    xml.write('<Val></Val>\n')
    xml.write('</I32>\n')
    xml.write('<I32>\n')
    xml.write('<Name>Pol2</Name>\n')
    xml.write('<Val></Val>\n')
    xml.write('</I32>\n')
    xml.write('<I32>\n')
    xml.write('<Name>Pol3</Name>\n')
    xml.write('<Val></Val>\n')
    xml.write('</I32>\n')
    xml.write('<I32>\n')
    xml.write('<Name>Pol4</Name>\n')
    xml.write('<Val></Val>\n')
    xml.write('</I32>\n')

    # Timing offset (UT1-UTC) (double) [fraction of day]
    # Default is to read IERS bulletin and use (UT1-UTC) for current date
    item = sh_dict.get('ut1-utc',util.UT1_UTC(mjd0))
    fmt += 'd'
    buf = struct.pack('d',item)
    f.write(buf)
    xml.write('<DBL>\n')
    xml.write('<Name>UT1UTC</Name>\n')
    xml.write('<Val></Val>\n')
    xml.write('</DBL>\n')

    # Maximum IDB filesize (unsigned int) [MB]
    # DPP will break IDB scan data into separate files none 
    # of which will exceed this size
    # Default is 100 (MB)
    item = sh_dict.get('max_file_size',100)
    fmt += 'I'
    buf = struct.pack('I',item)
    f.write(buf)
    xml.write('<U32>\n')
    xml.write('<Name>IDBMaxFileSize</Name>\n')
    xml.write('<Val></Val>\n')
    xml.write('</U32>\n')

    # IDB filename stem (length 14 string of form yyyymmddhhmmss)
    # Value of item passed in is Time() object
    # Creates IDB filename stem value from Time() object.  
    # Filename will be extended as needed to accommodate multiple files per scan.
    # (Note: truncated at integer second, since all times should happen at
    # 1pps boundary)
    # Default is current date/time
    item = sh_dict.get('date2IDB_stem',dt)
    fmt += 'I14s'
    datestr = item.iso.replace('-','').replace(' ','').replace(':','')
    buf = struct.pack('I14s',14,datestr[:14])
    f.write(buf)
    xml.write('<Array>\n')
    xml.write('<Name>IDBStem</Name>\n')
    xml.write('<Dimsize>14</Dimsize>\n<U8>\n<Name></Name>\n<Val></Val>\n</U8>\n')
    xml.write('</Array>\n')

    # Nominal start time of scan (length 14 string of form yyyymmddhhmmss)
    # (Note: truncated at integer second, since all times should happen at
    # 1pps boundary)
    fmt += 'I14s'
    buf = struct.pack('I14s',14,datestr[:14])
    f.write(buf)
    xml.write('<Array>\n')
    xml.write('<Name>IDBStart</Name>\n')
    xml.write('<Dimsize>14</Dimsize>\n<U8>\n<Name></Name>\n<Val></Val>\n</U8>\n')
    xml.write('</Array>\n')

    # Time conversion factor (double) [mjd] 
    # (i.e., Relates the absolute time to the accumulation number for the 
    # packets).  This is the time of the next 1 pps after the ARM signal to 
    # the correlator.
    # Default is current date/time, which is an error
    item = sh_dict.get('time_at_acc0',dt)
    fmt += 'd'
    buf = struct.pack('d',item.mjd)
    f.write(buf)
    xml.write('<DBL>\n')
    xml.write('<Name>TimeAtAcc0</Name>\n')
    xml.write('<Val></Val>\n')
    xml.write('</DBL>\n')

    # Duration of each spectral frame. Nominally 1 s. (unsigned int) [ms]
    # Default is 1000 [ms]
    item = sh_dict.get('dur_spec_frame',1000)
    fmt += 'I'
    buf = struct.pack('I',item)
    f.write(buf)
    xml.write('<U32>\n')
    xml.write('<Name>DurSpecFrame</Name>\n')
    xml.write('<Val></Val>\n')
    xml.write('</U32>\n')

    # Intval Integration time (unsigned int) [ms]
    # Default is 20 [ms]
    item = sh_dict.get('intval',20)
    fmt += 'I'
    buf = struct.pack('I',item)
    f.write(buf)
    xml.write('<U32>\n')
    xml.write('<Name>Intval</Name>\n')
    xml.write('<Val></Val>\n')
    xml.write('</U32>\n')

    # Number of integration intervals in each spectral frame. (unsigned int)
    # Default is 50 [per second].
    item = sh_dict.get('nintval',50)
    fmt += 'I'
    buf = struct.pack('I',item)
    f.write(buf)
    xml.write('<U32>\n')
    xml.write('<Name>NIntval</Name>\n')
    xml.write('<Val></Val>\n')
    xml.write('</U32>\n')

    # List of frequencies of 600 MHz band start AFTER reversal (length 50 double) [GHz]
    # Input is integer band number corresponding to base frequency for
    # each integration interval, where 1 = 1 GHz, 2 = 1.5 GHz, etc.
    # (50 unsigned ints). These are the 500 MHz IF bands.  The integer
    # values n are converted to GHz as follows:
    #     21 + n/2.0 is LO frequency [GHz].
    #     LO frequency - 19.95 is frequency at low end of 600 MHz band
    #     After reversal of bins, subtract another 0.6 GHz
    #     Complete expression is equivalent to 0.45 + n/2.
    # The Hittite tuning list is passed in and parsed with comma ','
    # Default is nominal solar sequence
    #*******This needs modification for 800 MHz clock*******
    item = sh_dict.get('fsequence',
                        '1, 2, 3, 4, 5, 6, 7, 8, 9,10,'+
                        '1, 2, 3, 4,11,12,13,14,15,16,'+
                        '1, 2, 3, 4,17,18,19,20,21,22,'+
                        '1, 2, 3, 4,23,24,25,26,27,28,'+
                        '1, 2, 3, 4,29,30,31,32,33,34')
    # print item
    fseqlist = item.rsplit(',')
    nintval = len(fseqlist)
    fmt += 'I50d'
    buf = struct.pack('I',50)
    for n in fseqlist:
        val = 0.45 + float(n)/2.0
        buf += struct.pack('d',val)
    if nintval < 50:
        # Fewer than 50 values sent in, so zero-fill to 50
        for n in range(nintval,50):
            buf += struct.pack('d',0.0)
    f.write(buf)
    xml.write('<Array>\n')
    xml.write('<Name>FSeqList</Name>\n')
    xml.write('<Dimsize>50</Dimsize><DBL>\n<Name></Name>\n<Val></Val>\n</DBL>\n')
    xml.write('</Array>\n')

    # Subband channel width [GHz]
    # This is fixed by the system to 0.6 GHz/4096.
    # Default is 0.6/4096
    item = sh_dict.get('subbw',0.6/4096)
    fmt += 'd'
    buf = struct.pack('d',item)
    f.write(buf)
    xml.write('<DBL>\n')
    xml.write('<Name>SubBW</Name>\n')
    xml.write('<Val></Val>\n')
    xml.write('</DBL>\n')

    # Number of 'wide' spectral channels (unsigned int)
    # Default 504, max 511
    item = sh_dict.get('nchan',cu.tot_scichan())
    fmt += 'I'
    buf = struct.pack('I',item)
    f.write(buf)
    xml.write('<U32>\n')
    xml.write('<Name>Nchan</Name>\n')
    xml.write('<Val></Val>\n')
    xml.write('</U32>\n')

    # Wide spectral channel lower value--nominally 504 values (nchan doubles) [GHz]
    # Default is the default set of 504 frequencies valid for solar observing 
    # (zero-filled to 511)
    item = sh_dict.get('fGHz')
    if item is None:
        item = []
        for band in range(1,35):
            item += cu.start_freq(band)
    nchan = len(item)
    fmt += 'I511d'
    buf = struct.pack('I',511)
    for i in item:
        buf += struct.pack('d',i)
    if nchan < 511:
        # Fewer than 511 values sent in, so zero-fill to 511
        for n in range(nchan,511):
            buf += struct.pack('d',0.0)        
    f.write(buf)
    xml.write('<Array>\n')
    xml.write('<Name>fGHz</Name>\n')
    xml.write('<Dimsize>511</Dimsize><DBL>\n<Name></Name>\n<Val></Val>\n</DBL>\n')
    xml.write('</Array>\n')

    # Wide spectral channel widths--nominally 504 values (nchan doubles) [GHz]
    # Default is the list of widths valid for solar observing (zero-filled to 511)
    item = sh_dict.get('chan_widths')
    if item is None:
        item = []
        for band in range(1,35):
            item += cu.sci_bw(band)
    nchan = len(item)
    fmt += 'I511d'
    buf = struct.pack('I',511)
    for i in item:
        buf += struct.pack('d',i)
    if nchan < 511:
        # Fewer than 511 values sent in, so zero-fill to 511
        for n in range(nchan,511):
            buf += struct.pack('d',0.0)        
    f.write(buf)
    xml.write('<Array>\n')
    xml.write('<Name>ChanWidths</Name>\n')
    xml.write('<Dimsize>511</Dimsize><DBL>\n<Name></Name>\n<Val></Val>\n</DBL>\n')
    xml.write('</Array>\n')
    
    # Science channel assignments (4096 x 50 array of 2-byte integers)
    #   0             = subband is not used.  
    #   (1-500)       = science channel to assign subband.
    #   512 + (1-500) = integration should not be time-averaged.
    # Multiple samples <512 within a spectral frame are averaged.
    # Multiple samples >512 are not averaged and are treated
    # as separate channels.)
    # Restrictions: 
    #   1. There are a maximum of 511 science channels.
    #   2. A given subband cannot be associated with more than one
    #      science channel. 
    #   3. All subbands associated with a given science channel must
    #      be in the same 20 ms integration.
    item = sh_dict.get('chan2wide')
    if item is None:
        # Create default channel assignments for the list of frequencies (bands) in fseqlist
        item = []
        for band in fseqlist:
            ch = cu.chan_asmt(int(band))
            #ch[0] = 0  # Assign first subband to purgatory (unused subband)
            item += ch
    nsubchan = len(item)
    fmt += 'II204800H'
    buf = struct.pack('2I',4096,50)
    for i in item:
        buf += struct.pack('H',i)
    if nsubchan < 204800:
        # Fewer than 4096*50 values sent in, so zero-fill to 204800
        for n in range(nsubchan,204800):
            buf += struct.pack('H',0)        
    f.write(buf)
    xml.write('<Array>\n')
    xml.write('<Name>Chan2Wide</Name>\n')
    xml.write('<Dimsize>4096</Dimsize><Dimsize>50</Dimsize><U16>\n<Name></Name>\n<Val></Val>\n</U16>\n')
    xml.write('</Array>\n')

    # Mask of RFI subbands (4096 x 50 array of 1-byte flags).   
    #    0 = Subband is presumed contaminated
    #    1 = otherwise
    # Default is all 1 (good values)
    item = sh_dict.get('chanmask',np.array([1]*204800,'byte'))
    nsubchan = len(item)
    fmt += 'II204800B'
    buf = struct.pack('2I',4096,50)
    buf += struct.pack(str(nsubchan)+'B',*item)
    if nsubchan < 204800:
        # Fewer than 4096*50 values sent in, so zero-fill to 204800
        for n in range(nsubchan,204800):
            buf += struct.pack('B',0)        
    f.write(buf)
    xml.write('<Array>\n')
    xml.write('<Name>Chanmask</Name>\n')
    xml.write('<Dimsize>4096</Dimsize><Dimsize>50</Dimsize><B8>\n<Name></Name>\n<Val></Val>\n</B8>\n')
    xml.write('</Array>\n')

    # RFI guard band (4-byte int)
    # Number of subbands to reject adjacent to kurtosis-flagged channels.
    #    0 = do not reject any additional subbands
    # Default is 0
    item = sh_dict.get('sk_guard_width',0)
    fmt += 'I'
    buf = struct.pack('I',item)
    f.write(buf)
    xml.write('<U32>\n')
    xml.write('<Name>SKGuard</Name>\n')
    xml.write('<Val></Val>\n')
    xml.write('</U32>\n')

    # Spectral kurtosis strategy (4-byte int) (Details TBD.)
    # Default 0 => Standard strategy
    item = sh_dict.get('sk_mode',0)
    fmt += 'I'
    buf = struct.pack('I',item)
    f.write(buf)
    xml.write('<U32>\n')
    xml.write('<Name>SKMode</Name>\n')
    xml.write('<Val></Val>\n')
    xml.write('</U32>\n')

    # Kurtosis lower and upper limits (two doubles)
    # Min, max acceptable SK thresholds
    # Default is 0.0, 0.0 -- Must be changed when calculated correctly
    item = sh_dict.get('sk_lims',[0.0,0.0])
    fmt += '2d'
    buf = struct.pack('2d',item[0],item[1])
    f.write(buf)
    xml.write('<DBL>\n')
    xml.write('<Name>SKLimLo</Name>\n')
    xml.write('<Val></Val>\n')
    xml.write('</DBL>\n')
    xml.write('<DBL>\n')
    xml.write('<Name>SKLimHi</Name>\n')
    xml.write('<Val></Val>\n')
    xml.write('</DBL>\n')

    # Reference complex gains (511 x 16 x 2 complex array) [Jy/unit]
    # These are time-independent values that correspond to the
    # 'base attenuator state'  which includes the effects of the
    # preset front-end and back-end attenuators. (Amplitude units
    # are nominally Jy, but may be modified by attenuator and
    # time-variable gain factors)
    # Note that Struct has no complex format, so buffer is written
    # with each complex value as a pair of floats, real,imag.
    # Default 1 + 0j
    item = sh_dict.get('gains',[1.0+0j]*16352)
    fmt += 'IIII16352d'
    buf = struct.pack('4I',2,511,16,2)
    for i in item:
        buf += struct.pack('2d',np.real(i),np.imag(i))
    f.write(buf)
    xml.write('<Array>\n')
    xml.write('<Name>GainTable</Name>\n')
    xml.write('<Dimsize>2</Dimsize><Dimsize>511</Dimsize><Dimsize>16</Dimsize><Dimsize>2</Dimsize><DBL>\n<Name></Name>\n<Val></Val>\n</DBL>\n')
    xml.write('</Array>\n')

    # Time variable gain factors (511 x 16 x 2 complex array) [Jy]
    # Antenna-based complex gains corresponding to time-variable
    # factors (presumably determined by a recent phase calibration).
    # Defaults to 1 + 0j if not present.
    item = sh_dict.get('gain_update',[1.0+0j]*16352)
    fmt += 'IIII16352d'
    buf = struct.pack('4I',2,511,16,2)
    for i in item:
        buf += struct.pack('2d',np.real(i),np.imag(i))
    f.write(buf)
    xml.write('<Array>\n')
    xml.write('<Name>GainUpdate</Name>\n')
    xml.write('<Dimsize>2</Dimsize><Dimsize>511</Dimsize><Dimsize>16</Dimsize><Dimsize>2</Dimsize><DBL>\n<Name></Name>\n<Val></Val>\n</DBL>\n')
    xml.write('</Array>\n')

    # Base attenuator value (4-byte int) [dB]
    # attenuator value corresponding to the base attenuator state
    # Default 0
    item = sh_dict.get('attn0',0)
    fmt += 'I'
    buf = struct.pack('I',item)
    f.write(buf)
    xml.write('<U32>\n')
    xml.write('<Name>Attn0</Name>\n')
    xml.write('<Val></Val>\n')
    xml.write('</U32>\n')

    # Attenuator step size (4-byte int) [dB]
    # Nominal step size for time-dependent variable attenuation 
    # 2-byte integer (presumably = 3 or 5 db)
    # Default 3 [dB]
    item = sh_dict.get('attn_step',3)
    fmt += 'I'
    buf = struct.pack('I',item)
    f.write(buf)
    xml.write('<U32>\n')
    xml.write('<Name>AttnStep</Name>\n')
    xml.write('<Val></Val>\n')
    xml.write('</U32>\n')

    # Attenuator gain table (500 x 16 x 2 x N complex array) 
    # complex gains (one for each science channel, polarization and antenna).
    # The values are relative to the 'base' attenuator settings, which represents
    # both the front end and back end attenuation.  Each of the N steps
    # corresponds to 3 or 5 db increments applied to all antennas.
    #item = sh_dict.get('attn_table')

    # XML encapsulation of KatADC info (array of 8)
    xml.write('<Array>\n')
    xml.write('<Name>KatADC</Name>\n')
    xml.write('<Dimsize>8</Dimsize>\n')
    # Put array dimension into data
    fmt += 'I'
    buf = struct.pack('I',8)

    # This is an array, but the definitions only go in once
    # Here are the definitions for the 8 elements in the cluster
    xml.write('<Cluster>\n')
    xml.write('<Name/>\n')
    xml.write('<NumElts>6</NumElts>\n')
    xml.write('<U32>\n')
    xml.write('<Name>Status</Name>\n')
    xml.write('<Val></Val>\n')
    xml.write('</U32>\n')
    xml.write('<SGL>\n')
    xml.write('<Name>Temp.adc0</Name>\n')
    xml.write('<Val></Val>\n')
    xml.write('</SGL>\n')
    xml.write('<SGL>\n')
    xml.write('<Name>Temp.adc1</Name>\n')
    xml.write('<Val></Val>\n')
    xml.write('</SGL>\n')
    xml.write('<SGL>\n')
    xml.write('<Name>Temp.ambient0</Name>\n')
    xml.write('<Val></Val>\n')
    xml.write('</SGL>\n')
    xml.write('<SGL>\n')
    xml.write('<Name>Temp.ambient1</Name>\n')
    xml.write('<Val></Val>\n')
    xml.write('</SGL>\n')
    xml.write('<SGL>\n')
    xml.write('<Name>BoardClock</Name>\n')
    xml.write('<Val></Val>\n')
    xml.write('</SGL>\n')
    # End KatADC Cluster
    xml.write('</Cluster>\n')

    # Get the KatADC array of dictionaries (defaults to eight empty dicts)
    katadc = sh_dict.get('katadc',[{},{},{},{},{},{},{},{}])
    brd_clk = sh_dict.get('roach_brd_clk',[0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])
    # The actual data has to go in eight times
    for i in range(8):
        # Handle case of empty dictionary
        if katadc[i] == {}:
            fmt += 'Iffff'
            buf += struct.pack('I',0)
            for j in range(4):
                buf += struct.pack('f',0.0)
        
        else:
            # Status (int) [bit list]
            # Each bit signifies whether corresponding sensor is nominal [0] or in error [1]
            # Order is alphabetical, msb to lsb.
            # Default is zero
            status = 0
            keys = sorted(katadc[i].keys())
            for key in keys:
                if key.find('status') != -1:
                    if katadc[i][key] == 'nominal':
                        status = status<<1
                    else:
                        status = (status<<1) + 1
            item = status
            fmt += 'I'
            buf += struct.pack('I',item)

            # Just add the sorted list of sensors
            # Default is zero
            for key in keys:
                if key.find('status') == -1:
                    item = katadc[i][key]
                    fmt += 'f'
                    buf += struct.pack('f',item)

        fmt += 'f'
        buf += struct.pack('f',brd_clk[i])
        
    # End KatADC Array
    xml.write('</Array>\n')

    f.write(buf)
    f.close()
    xml.write('</Cluster>\n')
    xml.close()

    # Connect to ACC /parm directory and transfer scan_header files
    try:
        acc = FTP('acc.solar.pvt')
        acc.login('admin','observer')
        acc.cwd('parm')
        # Send XML file to ACC
        f = open(xmlfile,'r')
        acc.storlines('STOR scan_header.xml',f)
        f.close()
        # Send DAT file to ACC    
        g = open(datfile,'rb')
        acc.storbinary('STOR '+datfile[5:],g)
        acc.close()
        g.close()
        sys.stdout.write('Successfully transferred scan_header files to ACC.\n')
    except:
        sys.stdout.write('Could not transfer scan_header files.  ACC is down?\n')

    return fmt, datfile, xmlfile