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
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
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]
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
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
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
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")
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
########################################################################## # 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),
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)
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)
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
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
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
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