Exemple #1
0
def pshift2pcenter(pshift, ms):
    ''' Convert phaseshift in heliocentric X,Y to RA, Dec using phase center in ms file

        pshift is a 2-element list of floats, e.g. [0.0, 0.0]

        output is the formatted RA Dec of the phase center, for use as phasecenter input in TCLEAN.
    '''
    from astropy.coordinates import SkyCoord
    from astropy import units as u
    from sunpy.coordinates import sun
    asec2rad = np.pi / (3600. * 180.)

    mdata = ms.metadata()
    pcenter = mdata.phasecenter()
    # Phase center in radians
    ra0, dec0 = (pcenter['m0']['value'], pcenter['m1']['value'])
    trange = mdata.timerangeforobs(0)
    tstart = Time(trange['begin']['m0']['value'],
                  format='mjd')  # Start time of file
    p0rad = sun.P(tstart).rad
    # Convert shifts in heliocentric to geocentric (i.e. rotate for solar P-angle)
    xgeo = (pshift[0] * np.cos(p0rad) - pshift[1] * np.sin(p0rad)) * asec2rad
    ygeo = (pshift[0] * np.sin(p0rad) + pshift[1] * np.cos(p0rad)) * asec2rad
    ra = ra0 - xgeo / np.cos(dec0)
    dec = dec0 + ygeo
    c = SkyCoord(ra=ra * u.rad, dec=dec * u.rad)
    rax = c.ra.hms
    decx = c.dec.dms
    phasecenter = 'J2000 {:02d}:{:02d}:{:07.4f} {:3d}.{:02d}.{:06.3f}'.format(
        int(rax.h), int(rax.m), rax.s, int(decx.d), int(abs(decx.m)),
        abs(decx.s))
    return phasecenter
Exemple #2
0
def _get_sun_pos(met):
    """
    Convenience wrapper module for getting the solar position
    and north pole angle at a given time.

    Synax:
    ----------
    sun_pos, sun_np = _get_sun_pos(met)

    Parameters
    ----------
    met: Time for the observation, given in MJD.

    Returns
    -------

    sun_pos:
        Index of evtdata that passes the filtering.
    """
    # Deprecated from sunpy v1 onwards
    #     from sunpy.coordinates import get_sun_P
    #  Use new version instead
    from sunpy.coordinates import sun

    sun_time = astropy.time.Time(met, format='mjd')
    astro_sun_pos = get_sun(sun_time)

    # Get the center of the Sun, and assign it degrees.
    sun_pos = np.array([astro_sun_pos.ra.deg, astro_sun_pos.dec.deg]) * u.deg

    # Solar NP roll angle:
    #     sun_np = get_sun_P(last_met)
    sun_np = sun.P(met)

    return sun_pos, sun_np
Exemple #3
0
def test_P_array_time():
    # Validate against published values from the Astronomical Almanac (2013)
    sun_P = sun.P(Time(['2013-01-01',
                        '2013-02-01',
                        '2013-03-01',
                        '2013-04-01',
                        '2013-05-01',
                        '2013-06-01',
                        '2013-07-01',
                        '2013-08-01',
                        '2013-09-01',
                        '2013-10-01',
                        '2013-11-01',
                        '2013-12-01'], scale='tt'))
    assert_quantity_allclose(sun_P, [1.98,
                                     -12.23,
                                     -21.55,
                                     -26.15,
                                     -24.11,
                                     -15.39,
                                     -2.64,
                                     10.85,
                                     21.08,
                                     25.97,
                                     24.47,
                                     16.05]*u.deg, atol=5e-3*u.deg)
def get_beam(fname):
    solar_PA = sun_coord.P(
        sunpy.time.parse_time(fits.open(fname)[0].header['DATE-OBS'])).degree
    b_maj = fits.open(fname)[0].header['BMAJ']
    b_min = fits.open(fname)[0].header['BMIN']
    b_ang = fits.open(fname)[0].header[
        'BPA'] - solar_PA  # should consider the beam for the data
    return [b_maj, b_min, b_ang]
Exemple #5
0
 def get_beam(self):
     if self.havedata:
         solar_PA = sun_coord.P(self.t_obs).degree
         b_maj =  self.header['BMAJ']
         b_min  = self.header['BMIN']
         b_ang = self.header['BPA']+solar_PA # should consider the beam for the data
         return [b_maj,b_min,b_ang]
     else:
         print("No data loaded")
def j2000xy(RA, DEC, t_sun):
    [RA_sun, DEC_sun] = sun_position(t_sun, False)
    rotate_angel = sun_coord.P(t_sun)

    # shift the center and transfer into arcsec
    x_shift = -(RA - RA_sun.degree) * 3600
    y_shift = (DEC - DEC_sun.degree) * 3600

    # rotate xy according to the position angle
    xx = x_shift * np.cos(-rotate_angel.rad) - y_shift * np.sin(
        -rotate_angel.rad)
    yy = x_shift * np.sin(-rotate_angel.rad) + y_shift * np.cos(
        -rotate_angel.rad)
    return [xx, yy]
def plot_image(fname, vmax_set=np.nan, log_scale=False):
    [xx, yy, data_new] = reduct_fits_image(fname)
    t_cur_datetime = sunpy.time.parse_time(
        fits.open(fname)[0].header['DATE-OBS'])
    solar_PA = sun_coord.P(
        sunpy.time.parse_time(fits.open(fname)[0].header['DATE-OBS'])).degree
    freq_cur = fits.open(fname)[0].header[34] / 1000000.
    b_major = fits.open(fname)[0].header['BMAJ'] * 3600
    b_min = fits.open(fname)[0].header['BMIN'] * 3600
    b_angel = fits.open(fname)[0].header[
        'BPA'] + solar_PA  # should consider the beam for the data
    #print(b_major,b_min,b_angel+solar_PA)
    fig = plt.figure(num=None, figsize=(8, 6), dpi=80)
    fig.text(1400, 1800, str(int(freq_cur)) + 'MHz')
    ax = plt.gca()
    cmap_now = 'CMRmap_r'
    cmap_now = 'gist_ncar_r'
    vmin_now = 0
    if log_scale:
        data_new = 10 * np.log10(data_new)
    if vmax_set > 0:
        vmax_now = vmax_set
    else:
        vmax_now = 1.2 * np.nanmax(data_new)
    ax.text(1400, 1800, str(int(freq_cur)) + 'MHz')
    circle1 = plt.Circle((0, 0), 960, color='r', fill=False)
    beam0 = Ellipse((-500, -1800), b_major, b_min, -b_angel)
    ax.text(-550,
            -1800,
            'Beam shape:',
            horizontalalignment='right',
            verticalalignment='center')
    ax.add_artist(circle1)
    ax.add_artist(beam0)
    plt.xlabel('X (ArcSec)')
    plt.ylabel('Y (ArcSec)')

    plt.imshow(data_new,
               vmin=vmin_now,
               vmax=vmax_now,
               interpolation='nearest',
               cmap=cmap_now,
               origin='lower',
               extent=(min(xx), max(xx), min(yy), max(yy)))
    plt.colorbar()
    plt.xlim([-2500, 2500])
    plt.ylim([-2500, 2500])
    plt.title(str(t_cur_datetime))

    return fig
Exemple #8
0
    def plot_image(self,vmax_set=np.nan,log_scale=False):
        if self.havedata:
            t_cur_datetime = self.t_obs
            solar_PA = sun_coord.P(self.t_obs).degree
            freq_cur = self.freq
            [b_maj,b_min,b_angel] = self.get_beam()
            b_maj = b_maj*3600
            b_min = b_min*3600
            data_new = self.data_xy
            xx = self.xx
            yy = self.yy

            #print(b_major,b_min,b_angel+solar_PA)
            fig=plt.figure()#num=None, figsize=(8, 6),dpi=120)
            ax = plt.gca()
            cmap_now = 'CMRmap_r'
            cmap_now = 'gist_ncar_r'
            cmap_now = 'gist_heat'
            vmin_now = 0
            if log_scale:
                data_new = 10*np.log10(data_new)
            if vmax_set>0:
                vmax_now = vmax_set
            else:
                vmax_now = 1.2*np.nanmax(data_new)
            ax.text(1400,1800, str(int(freq_cur)) + 'MHz',color='w')
            circle1 = plt.Circle((0,0), 960, color='r',fill=False)
            beam0 = Ellipse((-500, -1800), b_maj, b_min, b_angel ,color='w')
            print(b_angel)
            ax.text(-600,-1800,'Beam shape:',horizontalalignment='right',verticalalignment='center' ,color='w')
            ax.add_artist(circle1)
            ax.add_artist(beam0)
            plt.xlabel('X (ArcSec)')
            plt.ylabel('Y (ArcSec)')
            
            plt.imshow(data_new,vmin=vmin_now, vmax=vmax_now , 
                            interpolation='nearest',cmap=cmap_now, origin='lower',
                            extent=(min(xx),max(xx),min(yy),max(yy)))
            plt.colorbar()
            plt.xlim([-2500,2500])
            plt.ylim([-2500,2500])
            plt.title(str(t_cur_datetime))

            plt.show()

        else:
            print("No data loaded")
def sun_coord_trasform(data, header, act_r=True, act_s=False):
    # act_r : rotation operation
    # act_s : shift operation
    [RA_sun, DEC_sun] = get_cur_solar_centroid(header)
    [RA_obs, DEC_obs] = get_obs_image_centroid(header)
    x_shift_pix = (RA_sun - RA_obs) / header['CDELT1']
    y_shift_pix = (DEC_sun - DEC_obs) / header['CDELT2']
    if act_s == False:
        x_shift_pix = 0
        y_shift_pix = 0
    rotate_angel = sun_coord.P(sunpy.time.parse_time(
        header['DATE-OBS'])).degree
    if act_r == False:
        rotate_angel = 0
    data_tmp = scipy.ndimage.shift(data, (x_shift_pix, y_shift_pix))
    data_new = scipy.ndimage.rotate(data_tmp, rotate_angel, reshape=False)
    return data_new
Exemple #10
0
 def make_map(self):
     # still in beta version, use with caution
     # ref : https://gist.github.com/hayesla/42596c72ab686171fe516f9ab43300e2
     hdu = fits.open(self.fname)
     header = hdu[0].header
     data = np.squeeze(hdu[0].data)
     data = np.squeeze(hdu[0].data)
     obstime = Time(header['date-obs'])
     frequency = header['crval3'] * u.Hz
     reference_coord = SkyCoord(header['crval1'] * u.deg,
                                header['crval2'] * u.deg,
                                frame='gcrs',
                                obstime=obstime,
                                distance=sun_coord.earth_distance(obstime),
                                equinox='J2000')
     lofar_loc = EarthLocation(lat=52.905329712 * u.deg,
                               lon=6.867996528 *
                               u.deg)  # location of the center of LOFAR
     lofar_coord = SkyCoord(lofar_loc.get_itrs(Time(obstime)))
     reference_coord_arcsec = reference_coord.transform_to(
         frames.Helioprojective(observer=lofar_coord))
     cdelt1 = (np.abs(header['cdelt1']) * u.deg).to(u.arcsec)
     cdelt2 = (np.abs(header['cdelt2']) * u.deg).to(u.arcsec)
     P1 = sun_coord.P(obstime)
     new_header = sunpy.map.make_fitswcs_header(
         data,
         reference_coord_arcsec,
         reference_pixel=u.Quantity(
             [header['crpix1'] - 1, header['crpix2'] - 1] * u.pixel),
         scale=u.Quantity([cdelt1, cdelt2] * u.arcsec / u.pix),
         rotation_angle=-P1,
         wavelength=frequency.to(u.MHz),
         observatory='LOFAR')
     lofar_map = sunpy.map.Map(data, new_header)
     lofar_map_rotate = lofar_map.rotate()
     bl = SkyCoord(-2500 * u.arcsec,
                   -2500 * u.arcsec,
                   frame=lofar_map_rotate.coordinate_frame)
     tr = SkyCoord(2500 * u.arcsec,
                   2500 * u.arcsec,
                   frame=lofar_map_rotate.coordinate_frame)
     lofar_submap = lofar_map_rotate.submap(bl, tr)
     return lofar_submap
Exemple #11
0
 def sun_coord_trasform(self,data,header,act_r=True,act_s=False):
     # act_r : rotation operation
     # act_s : shift operation
     if self.havedata:
         [RA_sun,DEC_sun] = self.get_cur_solar_centroid(self.t_obs);
         [RA_obs,DEC_obs] = self.get_obs_image_centroid(header);
         x_shift_pix = (RA_sun  - RA_obs) /header['CDELT1']
         y_shift_pix = (DEC_sun - DEC_obs)/header['CDELT2']
         if act_s==False:
             x_shift_pix = 0
             y_shift_pix = 0
         rotate_angel = sun_coord.P(self.t_obs).degree
         if act_r==False:
             rotate_angel = 0
         data_tmp = scipy.ndimage.shift(data,(x_shift_pix,y_shift_pix))
         data_new = scipy.ndimage.rotate(data_tmp,rotate_angel,reshape=False)
         return data_new
     else:
         print("No data loaded")
Exemple #12
0
def get_nustar_roll(time, angle):
    """Code to determine the NuSTAR roll angle for a given field-of-view on the
    Sun for a given time.
    
    Parameters
    ----------
    time: Date that is parsable by sunpy.time.parse_time()
    
    i.e. 
    
    time='2016-07-26T19:53:15.00'
    
    
    angle: Desired roll offset from solar north in degrees.
    
    For a "square" field of view, use angle=0 / 90 / 180 / 270 to have DET0
    at the NE / SE / SW / NW corners of a square field of view.
    
    For a "diamond" with DET0 to the south, use angle = 45.
        
    Returns
    ----------
    nustar_roll: NuSTAR PA angle with respect to celestial north.
    
    """

    #     Replaced with newer sunpy v1 function
    #     from sunpy import sun
    from sunpy.coordinates import sun

    # Get the solar north pole angle. cgs --> radians
    #     sun_np=sun.solar_north(t=time).deg * u.deg
    #     Update for sunpy v1.0+
    sun_np = sun.P(time).deg * u.deg

    nustar_roll = np.mod(sun_np + angle, 360 * u.deg)

    return nustar_roll
Exemple #13
0
##########################################################################
# Now we need to get the other parameters from the header that will be used
# to create the new header - here we can get the cdelt1 and cdelt2 which are
# the spatial scales of the data axes.
cdelt1 = (np.abs(header['cdelt1'])*u.deg).to(u.arcsec)
cdelt2 = (np.abs(header['cdelt2'])*u.deg).to(u.arcsec)

##################################################################################
# Finally, we need to specify the orientation of the HPC coordinate grid because
# GCRS north is not in the same direction as HPC north.  For convenience, we use
# :func:`~sunpy.coordinates.sun.P` to calculate this relative rotation angle,
# although due to subtleties in definitions, the returned value is inaccurate by
# 2 arcmin, equivalent to a worst-case shift of 0.6 arcsec for HPC coordinates
# on the disk. The image will need to be rotated by this angle so that solar north
# is pointing up.
P1 = sun.P(obstime)

##########################################################################
# Now we can use this information to create a new header using the helper
# function `~sunpy.map.make_fitswcs_header()`. This will create a MetaDict
# which we contain all the necessay WCS information to create a `~sunpy.map.Map`.
# We provide a reference coordinate (in HPC), the spatial
# scale of the observation (i.e. ``cdelt1`` and ``cdelt2``), and the rotation angle (P1).
# Note that here, 1 is subtracted from the crpix1 and crpix2 values, this is because
# the ``reference_pixel`` keyword in ~sunpy.map.make_fitswcs_header` is zero indexed rather
# than the fits convention of 1 indexed.

new_header = sunpy.map.make_fitswcs_header(data, reference_coord_arcsec,
                                           reference_pixel=u.Quantity([header['crpix1']-1,
                                                                       header['crpix2']-1]*u.pixel),
                                           scale=u.Quantity([cdelt1, cdelt2]*u.arcsec/u.pix),
Exemple #14
0
def test_P():
    # Validate against a published value from Astronomical Algorithms (Meeus 1998, p.191)
    assert_quantity_allclose(sun.P('1992-Oct-13'),
                             26.27 * u.deg,
                             atol=5e-3 * u.deg)
Exemple #15
0
def test_P_array_time():
    # Validate against published values from the Astronomical Almanac (2013)
    sun_P = sun.P(Time(['2013-04-01', '2013-12-01'], scale='tt'))
    assert_quantity_allclose(sun_P[0], -26.15 * u.deg, atol=1e-2 * u.deg)
    assert_quantity_allclose(sun_P[1], 16.05 * u.deg, atol=1e-2 * u.deg)
Exemple #16
0
def _delta_solar_skyfield(ra_x, dec_y, met, **kwargs):
    """ Function to compute the offsets from the center of
        the Sun as a function of time.

        Use the tStep argument to define how often you want
        to update the solar ephemeris. Default is every 5 seconds.

        Inputs: ra_x, dec_y, and met are all arrays that contain the
        RA, Dec, and time of arrival of each count, respectively. The arrival time
        must have astropy units attached to it.

        Outputs: sun_x, sun_y are the x and y values (in arcseconds)
        from the center of the Sun in the +North and +West directions.

    """
    import astropy.units as u
    from nustar_pysolar.utils import skyfield_ephem
    #      Don't think this is needed
    #     from sunpy import sun
    # Deprecated from sunpy v1 onwards
    #     from sunpy.coordinates import get_sun_P
    #  Use new version instead
    from sunpy.coordinates import sun

    # How often you want to update the solar ephemeris:
    tStep = kwargs.get('tStep', 5.0)
    tStep = tStep * u.s

    load_path = kwargs.get('load_path', None)
    observer, TheSun, ts = skyfield_ephem(load_path=load_path,
                                          parallax_correction=True,
                                          utc=met[0])

    # How many events do you want to do?
    maxEvt = kwargs.get('maxEvt', len(ra_x))

    # Keep last time we updated things
    last_met = met[0] - tStep * 2.
    last_i = 0

    sun_x = np.zeros_like(ra_x)
    sun_y = np.zeros_like(dec_y)
    for i in np.arange(len(ra_x)):
        if ((met[i] - last_met) > tStep):
            last_met = met[i]

            tcheck = ts.from_astropy(last_met)
            astrometric = observer.at(tcheck).observe(TheSun)
            this_ra, this_dec, dist = astrometric.radec()

            # Get the center of the Sun, and assign it degrees.
            # Doing it this was is necessary to do the vector math below.
            sun_pos = np.array(
                [this_ra.to(u.deg).value,
                 this_dec.to(u.deg).value]) * u.deg

            #             sun_np = get_sun_P(last_met)
            sun_np = sun.P(last_met)

            # Rotation matrix for a counter-clockwise rotation since we're going
            # back to celestial north from solar north
            rotMatrix = np.array([[np.cos(sun_np),
                                   np.sin(sun_np)],
                                  [-np.sin(sun_np),
                                   np.cos(sun_np)]])

        # Diagnostics
        #         di = (i -last_i)
        #        print("Updating Sun position...")
        #         if di > 0:
        #             print(i, di)
        #             dt = toc()
        #             tic()
        #             last_i = i
        #             print("Time per event: ",dt / float(di) )
        # From here on we do things for every photon:

        ph_pos = np.array([ra_x[i].value, dec_y[i].value]) * u.deg
        offset = ph_pos - sun_pos

        # Project the offset onto the Sun
        delta_offset = ((np.dot(offset, rotMatrix)).to(u.arcsec))

        # Account for East->West conversion for +X direction in heliophysics coords
        delta_offset = delta_offset * [-1., 1.]

        sun_x[i] = delta_offset[0]
        sun_y[i] = delta_offset[1]
        if (i > maxEvt):
            break

    return sun_x, sun_y
Exemple #17
0
def get_sky_position(time, offset):
    """Code for converting solar offsets to pointing position.

    Parameters
    ----------
    time: Date that is parsable by sunpy.time.parse_time()

    i.e.,

    time='2016-07-26T19:53:15.00'

    offset: Offset from the center of the Sun. Must have units from astropy:

    i.e.: offset = np.array([1000, 150]) * u.arcsec


    Returns
    ----------
    sky_position: Two-element array giving the [RA, Dec] coordinates of the


    Notes
    ----------
    Syntax:

    sky_position = get_sky_position(time, offset)



    """

    from astropy.coordinates import get_sun
    from astropy.time import Time
    #     Replaced with newer sunpy v1 function
    #     from sunpy import sun
    from sunpy.coordinates import sun

    # Convert the date into something that's usable by astropy.

    start_date = parse_time(time)
    astro_time = Time(start_date)

    # Use astropy get_sun for Sun sky position.
    # sunpy has a similar function, but it may be giving a different
    # epoch for the RA and dec. We need them in J2000 RA and dec.

    astro_sun_pos = get_sun(astro_time)

    # Get the solar north pole angle. cgs --> radians
    #     Update for sunpy v1.0+
    #     sun_np=sun.solar_north(t=time).cgs
    sun_np = sun.P(time).cgs

    # Get the center of the Sun, and assign it degrees.
    # Doing it this was is necessary to do the vector math below.
    sun_pos = np.array([astro_sun_pos.ra.deg, astro_sun_pos.dec.deg]) * u.deg

    # Rotation matrix for a counter-clockwise rotation since we're going
    # back to celestial north from solar north
    rotMatrix = np.array([[np.cos(sun_np), np.sin(sun_np)],
                          [-np.sin(sun_np), np.cos(sun_np)]])

    # Project the offset onto the Sun
    delta_offset = np.dot(offset, rotMatrix)

    # Scale to RA based on the declination.
    delta_offset = delta_offset * np.array([1. / np.cos(sun_pos[1]), 1.])

    # Account for the fact that +Ra == East and we have defined +X = West
    delta_offset = delta_offset * [-1.0, 1.0]

    # Apply the offset and return the sky position.
    sky_position = sun_pos + delta_offset

    return sky_position
Exemple #18
0
def get_skyfield_position(time,
                          offset,
                          load_path=None,
                          parallax_correction=False):
    """Code for converting solar coordinates to astrometric (J200) RA/Dec coordinates.

    Parameters
    ----------
    time: Date that is parsable by sunpy.time.parse_time()

    i.e.,

    time='2016-07-26T19:53:15.00'

    offset: Offset from the center of the Sun. Must have units from astropy:

    i.e.: offset = np.array([1000, 150]) * u.arcsec


    load_path (optional): Relative path from currently location to store bsp files

    parallax_correction: Use the NuSTAR TLE to correct for orbital parallax


    Returns
    ----------
    sky_position: Two-element array giving the [RA, Dec] coordinates of the
    target location. Note this is given in astrometric (J2000) RA/Dec, which is what
    we need for the NuSTAR planning system.

    Notes
    ----------
    Syntax:

    skyfield_position = get_skyfield_position(time, offset)

    """

    from astropy.time import Time
    #     Replaced with newer sunpy v1 function
    #     from sunpy import sun
    from sunpy.coordinates import sun
    from nustar_pysolar.utils import skyfield_ephem
    start_date = parse_time(time)
    utc = Time(start_date)

    observer, sunephem, ts = skyfield_ephem(
        load_path=load_path, parallax_correction=parallax_correction, utc=utc)

    tcheck = ts.from_astropy(utc)
    geocentric = observer.at(tcheck).observe(sunephem)
    this_ra_geo, this_dec_geo, dist = geocentric.radec()

    # Get the solar north pole angle. cgs --> radians
    #     sun_np = sunpy.sun.solar_north(t=time).cgs
    #     Update for sunpy v1.0+
    sun_np = sun.P(time).cgs

    # Get the center of the Sun, and assign it degrees.
    # Doing it this was is necessary to do the vector math below.
    sun_pos = np.array(
        [this_ra_geo.to(u.deg).value,
         this_dec_geo.to(u.deg).value]) * u.deg

    # Rotation matrix for a counter-clockwise rotation since we're going
    # back to celestial north from solar north
    rotMatrix = np.array([[np.cos(sun_np), np.sin(sun_np)],
                          [-np.sin(sun_np), np.cos(sun_np)]])

    # Project the offset onto the Sun
    delta_offset = np.dot(offset, rotMatrix)

    # Scale to RA based on the declination.
    delta_offset = delta_offset * np.array([1. / np.cos(sun_pos[1]), 1.])

    # Account for the fact that +Ra == East and we have defined +X = West
    delta_offset = delta_offset * [-1.0, 1.0]

    # Apply the offset and return the sky position.
    sky_position = sun_pos + delta_offset

    return sky_position
Exemple #19
0
def prepData(files, base_dir, prefix, custom_keywords={}, plot=False):
    diffs = {'center_x': [], 'center_y': [], 'radius': [], 'scale': []}
    os.makedirs(os.path.join(base_dir, 'level1'), exist_ok=True)
    os.makedirs(os.path.join(base_dir, 'level1_5'), exist_ok=True)

    with warnings.catch_warnings():
        warnings.simplefilter("ignore")
        for file in tqdm(files):
            try:
                # load existing file
                hdul = fits.open(file)
                hdu = hdul[0]
                hdu.verify('fix')
                d, h = hdu.data, hdu.header

                # set custom keywords
                h.update(custom_keywords)

                # evaluate center and radius
                imsave("demo.jpg", d)
                myCmd = os.popen(
                    '/home/rja/PythonProjects/SpringProject/spring/limbcenter/sunlimb demo.jpg'
                ).read()
                center_x, center_y, radius, d_radius = map(
                    float, myCmd.splitlines())

                if "EXPTIME" in h:
                    h['EXP_TIME'] = h['EXPTIME']
                    del h['EXPTIME']
                if 'TIME-OBS' in h:
                    obs_date = datetime.strptime(
                        h['DATE-OBS'] + 'T' + h['TIME-OBS'],
                        "%m/%d/1%yT%H:%M:%S")
                    h['DATE-OBS'] = obs_date.isoformat()
                    del h["TIME-OBS"]
                if 'TIME' in h:
                    obs_date = datetime.strptime(
                        h['DATE-OBS'] + 'T' + h['TIME'], "%d/%m/%YT%H:%M:%S")
                    h['DATE-OBS'] = obs_date.isoformat()
                    del h["TIME"]

                obs_time = parse(h["DATE-OBS"])
                rsun = angular_radius(obs_time)
                b0_angle = sun.B0(obs_time)
                l0 = sun.L0(obs_time)
                p_angle = sun.P(obs_time)
                filename = "%s_%s_fi_%s.fits" % (
                    prefix, h["OBS_TYPE"].lower(),
                    obs_time.strftime("%Y%m%d_%H%M%S"))

                # prepare existing header information
                if "ANGLE" not in h:
                    h["ANGLE"] = p_angle.value

                scale = rsun / (radius * u.pix)
                coord = SkyCoord(0 * u.arcsec,
                                 0 * u.arcsec,
                                 obstime=obs_time,
                                 observer='earth',
                                 frame=frames.Helioprojective)

                # create WCS header info
                header = header_helper.make_fitswcs_header(
                    d,
                    coord,
                    rotation_angle=h["ANGLE"] * u.deg,
                    reference_pixel=u.Quantity([center_x, center_y] * u.pixel),
                    scale=u.Quantity([scale, scale]),
                    instrument=h["INSTRUME"],
                    telescope=h["TELESCOP"],
                    observatory=h["OBSVTRY"],
                    exposure=h["EXP_TIME"] * u.ms,
                    wavelength=h["WAVELNTH"] * u.angstrom)

                header["KEYCOMMENTS"] = {
                    "EXPTIME": "[s] exposure time in seconds",
                    "DATE": "file creation date (YYYY-MM-DDThh:mm:ss UT)",
                    "DATE-OBS": "date of observation",
                    "WAVELNTH": "[Angstrom] wavelength",
                    "BANDPASS": "******",
                    "WAVEMIN": "[Angstrom] minimum wavelength",
                    "WAVEMAX": "[Angstrom] maximum wavelength",
                    "BZERO": "offset data range to that of unsigned short",
                    "CDELT1": "[arcsec/pix]",
                    "CDELT2": "[arcsec/pix]",
                    "SOLAR_R": "[pix]",
                    "DSUN_OBS": "[m]",
                    "RSUN_REF": "[m]",
                    "RSUN_ARC": "[%s]" % rsun.unit,
                    "ANGLE": "[deg]",
                    "SOLAR_P": "[%s]" % p_angle.unit,
                    "SOLAR_L0": "[%s]" % l0.unit,
                    "SOLAR_B0": "[%s]" % b0_angle.unit,
                    'SIMPLE': 'file does conform to FITS standard',
                    'BITPIX': 'number of bits per data pixel',
                    'CUNIT1': '[arcsec]',
                    'CUNIT2': '[arcsec]',
                    'CRVAL1': 'coordinate system value at reference pixel',
                    'CRVAL2': 'coordinate system value at reference pixel',
                    'CTYPE1': 'name of the coordinate axis',
                    'CTYPE2': 'name of the coordinate axis',
                    'INSTRUME': 'name of instrument',
                    'TELESCOP': 'name of telescope',
                    'OBSVTRY': 'name of observatory',
                }

                # set constants and default values
                header["FILENAME"] = filename
                header["DATE"] = datetime.now().strftime("%Y-%m-%dT%H:%M:%S")

                header["SOLAR_R"] = radius
                header["RSUN_ARC"] = rsun.value
                header["SOLAR_P"] = p_angle.value
                header["SOLAR_L0"] = l0.value
                header["SOLAR_B0"] = b0_angle.value

                header["DATAMIN"] = np.min(d)
                header["DATAMEAN"] = np.mean(d)
                header["DATAMAX"] = np.max(d)

                # copy existing keys
                for key, value in h.items():
                    if key not in header:
                        header[key] = value

                # copy comments
                for key, value in zip(list(h.keys()), list(h.comments)):
                    if key not in header["KEYCOMMENTS"]:
                        header["KEYCOMMENTS"][key] = value

                # LEVEL 1
                s_map = Map(d.astype(np.float32), header)
                level1_path = os.path.join(base_dir, 'level1', filename)

                h.add_history("unified FITS header")
                s_map.meta["HISTORY"] = h["HISTORY"]
                s_map.meta["LVL_NUM"] = "1.0"
                s_map = Map(s_map.data.astype(np.float32), s_map.meta)
                s_map.save(level1_path, overwrite=True)

                # LEVEL 1.5
                scale = s_map.scale[0].value
                s_map = padScale(s_map)

                s_map = s_map.rotate(
                    recenter=True,
                    scale=scale,
                    missing=s_map.min(),
                )
                center = np.floor(s_map.meta['crpix1'])
                range_side = (center + np.array([-1, 1]) * 2048 / 2) * u.pix
                s_map = s_map.submap(
                    u.Quantity([range_side[0], range_side[0]]),
                    u.Quantity([range_side[1], range_side[1]]))
                level1_5_path = os.path.join(base_dir, 'level1_5', filename)

                h.add_history("recentered and derotated")
                s_map.meta["HISTORY"] = h["HISTORY"]
                s_map.meta["LVL_NUM"] = "1.5"
                s_map = Map(s_map.data.astype(np.float32), s_map.meta)
                s_map.save(level1_5_path, overwrite=True)

                if plot:
                    s_map.plot()
                    s_map.draw_grid()
                    plt.savefig(level1_5_path.replace(".fits", ".jpg"))
                    plt.close()

                # check header
                hdul = fits.open(level1_5_path)
                hdu = hdul[0]
                hdu.verify('exception')

                # evaluate difference
                if 'center_x' in h and not isinstance(h["center_x"], str):
                    diffs['center_x'].append(
                        np.abs(h['center_x'] - header['crpix1']))
                if 'center_y' in h and not isinstance(h["center_y"], str):
                    diffs['center_y'].append(
                        np.abs(h['center_y'] - header['crpix2']))
                if 'SOLAR_R' in h and not isinstance(h["SOLAR_R"], str):
                    diffs['radius'].append(
                        np.abs(h['SOLAR_R'] - header['SOLAR_R']))
                if 'cdelt1' in h and not isinstance(h["cdelt1"], str):
                    diffs['scale'].append(
                        np.abs(h['cdelt1'] - header['cdelt1']))
            except Exception as ex:
                print("INVALID FILE", file)
                print("ERROR:", ex)
        return diffs