Ejemplo n.º 1
0
def get_localfcdata(inpars):
    ''' 
   Extract the required info for creating a finding chart from the ob and templates
   obtained from a local parameter file.
   
   Args:
      inpars: a dictionnary.
      
   Returns:
      a dictionnary containing the OB parameters
   '''

    # Deal with the ephemeris ...
    fc_params = {}

    #fc_params = {}
    fc_params['ob_name'] = inpars['ob_name']

    if type(inpars['ob_id']) == int:
        fc_params['ob_id'] = inpars['ob_id']
    else:
        fc_params['ob_id'] = -1

    fc_params['pi'] = inpars['pi']
    fc_params['prog_id'] = inpars['prog_id']
    fc_params['inst'] = inpars['inst']
    fc_params[
        'ins_mode'] = None  # Create a general entry, to be tweaked later on if needed

    # Also create a list where I keep track of anything special about the OB
    # possible flag include 'moving_target', 'parallactic_angle'
    fc_params['tags'] = []

    # Some other day ...
    #fc_params['ephem_points_past'] = []
    #fc_params['ephem_points_future'] = []

    if inpars['ephemeris'] is None:
        # Ok, just a normal target ... extract the required info
        tc = SkyCoord(
            ra=inpars['ra'],
            dec=inpars['dec'],
            obstime=Time(inpars['epoch'], format='decimalyear'),
            equinox=inpars['equinox'],
            frame='icrs',
            unit=(u.hourangle, u.deg),
            pm_ra_cosdec=inpars['pmra'] * u.arcsec / u.yr,
            pm_dec=inpars['pmdec'] * u.arcsec / u.yr,
            # I must specify a generic distance to the target,
            # if I want to later on propagate the proper motions
            distance=fcm_m.default_pm_d,
        )

        # Propagate the proper motion using astropy v3.0
        fc_params['target'] = tc.apply_space_motion(
            new_obstime=Time(fcm_m.obsdate))
        # Flag it as a moving target if needed
        if np.sqrt(inpars['pmra']**2 + inpars['pmdec']**2) > 0.0:
            fc_params['tags'] += ['moving_target']

        fc_params['ephem_points_past'] = []
        fc_params['ephem_points_future'] = []

    else:
        ephem_fn = os.path.join('.', inpars['ephemeris'])

        if not (os.path.isfile(ephem_fn)):
            raise Exception('Ouch! No ephemeris file found at: %s' %
                            (ephem_fn))

        # Ope the ephemeris file
        f = open(ephem_fn)
        ephemeris = f.readlines()
        f.close()

        # Now deal with this ephemeris file
        fc_params = get_target_from_ephem(fc_params, ephemeris)
        fc_params['tags'] += ['moving_target']

    # Extract the acquisition and observations parameters for the support instruments
    if inpars['inst'] == 'MUSE':
        return fcm_muse.get_localfcdata_muse(fc_params, inpars)
    elif inpars['inst'] == 'HAWKI':
        return fcm_hawki.get_localfcdata_hawki(fc_params, inpars)
    elif inpars['inst'] == 'XSHOOTER':
        return fcm_xshooter.get_localfcdata_xshooter(fc_params, inpars)
    elif inpars['inst'] == 'ESPRESSO':
        return fcm_espresso.get_localfcdata_espresso(fc_params, inpars)

    else:
        raise Exception('%s finding charts not (yet?) supported.' %
                        (inpars['inst']))
Ejemplo n.º 2
0
def get_p2fcdata(obID, api):
    ''' 
   Extract the required info for creating a finding chart from the ob and templates
   obtained via p2api.
   
   Args:
      obID: the OB Id, as an integer.
      api: a p2api.ApiConnection() object 
      
   Returns:
      a dictionnary containing the OB parameters
   '''

    # Fetch the OB
    ob, obVersion = api.getOB(obID)

    # Check for an ephemeris file ...
    # This will download a file, even if there is no ephemeris file upload by the user
    ephem_fn = os.path.join(fcm_m.data_loc, 'ephem_%i.txt' % (obID))
    api.getEphemerisFile(obID, ephem_fn)

    # I have no choice but to open it, check what's inside ...
    f = open(ephem_fn)
    ephemeris = f.readlines()
    f.close()

    if len(ephemeris) == 0:
        # Ok, there's no ephemeris file attached to the OB ... clean-up my mess
        os.remove(ephem_fn)
        ephem_fn = None
    else:
        print('   I found an ephemeris file, and stored it: %s' % ephem_fn)

    # Start filling my own dictionary of useful finding chart parameters
    fc_params = {}

    fc_params['inst'] = ob['instrument']
    fc_params[
        'ins_mode'] = None  # Create a general entry, to be tweaked later on if needed
    fc_params['ob_name'] = ob['name']

    fc_params['ob_id'] = obID

    # Get the PI name and progId
    runId = ob['runId']
    run, _ = api.getRun(runId)
    fc_params['pi'] = run['pi']['lastName']
    fc_params['prog_id'] = run['progId']

    # Also create a list where I keep track of anything special about the OB
    # possible flag include 'moving_target', 'parallactic_angle'
    fc_params['tags'] = []

    # Need to deal with the target coordinates. Ephemeris file, or not ?
    if ephem_fn is None:

        # Issue #6: https://github.com/fpavogt/fcmaker/issues/6
        # Make sure the equinox comes with a "J"
        if not (ob['target']['equinox'][0] == 'J'):
            warnings.warn(
                'Equinox %s invalid: it should start with a "J". Using %s instead.'
                % (ob['target']['equinox'], 'J' + ob['target']['equinox']))
            ob['target']['equinox'] = 'J' + ob['target']['equinox']

        # Ok, just a normal target ... extract the required info
        tc = SkyCoord(
            ra=ob['target']['ra'],
            dec=ob['target']['dec'],
            obstime=Time(ob['target']['epoch'], format='decimalyear'),
            equinox=ob['target']['equinox'],
            frame='icrs',
            unit=(u.hourangle, u.deg),
            pm_ra_cosdec=ob['target']['properMotionRa'] * u.arcsec / u.yr,
            pm_dec=ob['target']['properMotionDec'] * u.arcsec / u.yr,
            # I must specify a generic distance to the target,
            # if I want to later on propagate the proper motions
            distance=100 * u.pc,
        )

        # Propagate the proper motion using astropy v3.0
        fc_params['target'] = tc.apply_space_motion(
            new_obstime=Time(fcm_m.obsdate))

        fc_params['ephem_points_past'] = []
        fc_params['ephem_points_future'] = []
        # Flag it if this is a moving target
        if np.sqrt(ob['target']['properMotionRa']**2 +
                   ob['target']['properMotionDec']**2) > 0.0:
            fc_params['tags'] += ['moving_target']

    else:

        # Now deal with this ephemeris file
        fc_params = get_target_from_ephem(fc_params, ephemeris)
        fc_params['tags'] += ['moving_target']

    # Extract the acquisition and observations parameters for the support instruments
    if ob['instrument'] == 'MUSE':
        return fcm_muse.get_p2fcdata_muse(fc_params, ob, api)

    elif ob['instrument'] == 'HAWKI':
        return fcm_hawki.get_p2fcdata_hawki(fc_params, ob, api)

    elif ob['instrument'] == 'XSHOOTER':
        return fcm_xshooter.get_p2fcdata_xshooter(fc_params, ob, api)

    elif ob['instrument'] == 'ESPRESSO':
        return fcm_espresso.get_p2fcdata_espresso(fc_params, ob, api)

    else:
        raise Exception('%s finding charts not (yet?) supported.' % (inst))
Ejemplo n.º 3
0
def draw_fc(fc_params, bk_image=None, bk_lam=None, do_pdf=False, do_png=False):
    ''' 
   The finding chart master plotting function.
   
   Args:
      fc_params: a dictionnary containing the OB parameters
      bk_image: the background image as string, either a SkyView entry, or local filename
      bk_lam: the wavelength of the chart as a string
      do_pdf (bool): save a pdf file, in addition to jpg
      do_png (bool): save a png file, in addition to jpg
   Returns:
      The finding chart filename as a string
   '''

    # Load all the fields dictionaries
    fields = fcm_id.get_fields_dict(fc_params)

    # Get the radius and center of the charts
    (left_radius, right_radius) = fcm_id.get_chart_radius(fc_params)
    (left_center, right_center) = fcm_id.get_chart_center(fc_params)

    # Get the background image in place
    (fn_bk_image_L, survey_L, bk_lam_L) = get_bk_image(bk_image, bk_lam,
                                                       left_center,
                                                       left_radius, fc_params)

    # If this is not the DSS2 Red, then download this as well for the right-hand-side plot
    if not (survey_L == 'DSS2 Red'):
        (fn_bk_image_R, survey_R,
         bk_lam_R) = get_bk_image('DSS2 Red', None, right_center, right_radius,
                                  fc_params)
    else:
        (fn_bk_image_R, survey_R, bk_lam_R) = copy.deepcopy(
            (fn_bk_image_L, survey_L, bk_lam_L))

    # Start the plotting
    plt.close(1)
    fig1 = plt.figure(
        1,
        figsize=(14.17, 7))  #14.17 inches, at 50% = 1 full page plot in A&A.

    ax1 = aplpy.FITSFigure(fn_bk_image_L,
                           figure=fig1,
                           north=fcm_m.set_North,
                           subplot=[0.12, 0.12, 0.35, 0.76])
    ax1.show_grayscale(invert=True,
                       stretch='linear',
                       pmin=fcm_id.get_pmin(survey_L),
                       pmax=99.9)

    ax2 = aplpy.FITSFigure(fn_bk_image_R,
                           figure=fig1,
                           north=fcm_m.set_North,
                           subplot=[0.59, 0.12, 0.38, 0.8])
    ax2.show_grayscale(invert=True,
                       stretch='linear',
                       pmin=fcm_id.get_pmin(survey_R))

    ax1.recenter(left_center.ra, left_center.dec, radius=left_radius / 3600.)
    ax2.recenter(right_center.ra,
                 right_center.dec,
                 radius=right_radius / 3600.)

    # I will be adding stuff to the left-hand-side plot ... start collecting them
    ax1_legend_items = []

    # Do I have the epoch of observation for the background image ?
    '''
   try:
      bk_obsdate = fits.getval(fn_bk_image, 'DATE-OBS')
      
      # If not specified, assume UTC time zone for bk image   
      if bk_obsdate.tzinfo is None:
      #   #warnings.warn(' "--obsdate" timezone not specified. Assuming UTC.')
         bk_obsdate = bk_obsdate.replace(tzinfo=pytz.utc)
      
      nowm_time = Time(bk_obsdate)
      pm_track_time = (fcm_obsdate - bk_obsdate).total_seconds()*u.s
      
   except:
      # If not, just shows the default length set in fcmaker_metadata
      nowm_time = Time(fcm_m.obsdate) - fcm_m.pm_track_time
      pm_track_time = fcm_m.pm_track_time
   '''

    # Just keep things simple. Same lookback time for all charts.
    nowm_time = Time(fcm_m.obsdate) - fcm_m.pm_track_time
    #pm_track_time = fcm_m.pm_track_time

    # If yes, then let's show where are the fastest stars moved from/to.
    #if do_GAIA_pm and (fcm_m.min_abs_GAIA_pm >=0):
    if (fcm_m.min_abs_GAIA_pm >= 0):

        # Query GAIA
        print(
            '   Querying GAIA DR2 to look for high proper motion stars in the area ...'
        )

        # Make it a sync search, because I don't think I need more than 2000 targets ...
        # 2020-04: for some reason the sync search fails ... run an async one for now.
        j = Gaia.cone_search_async(left_center,
                                   right_radius * u.arcsec,
                                   verbose=False)
        r = j.get_results()

        selected = np.sqrt(r['pmra']**2 + r['pmdec']**2
                           ) * u.mas / u.yr > fcm_m.min_abs_GAIA_pm

        # Show the fastest stars
        past_tracks = []
        future_tracks = []

        # Need to propagate their coords to the fc epoch and the obstime
        for s in range(len(r['ra'][selected])):

            star = SkyCoord(
                ra=r['ra'][selected][s],
                dec=r['dec'][selected][s],
                obstime=Time(r['ref_epoch'][selected][s],
                             format='decimalyear'),
                frame='icrs',
                unit=(u.deg, u.deg),
                pm_ra_cosdec=r['pmra'][selected][s] * u.mas / u.yr,
                pm_dec=r['pmdec'][selected][s] * u.mas / u.yr,
                # I must specify a generic distance to the target,
                # if I want to later on propagate the proper motions
                distance=fcm_m.default_pm_d,
            )

            now = star.apply_space_motion(new_obstime=Time(fcm_m.obsdate))
            nowm = star.apply_space_motion(new_obstime=nowm_time)

            past_tracks += [
                np.array([[nowm.ra.deg, now.ra.deg],
                          [nowm.dec.deg, now.dec.deg]])
            ]

            for ax in [ax1, ax2]:
                ax.show_markers([now.ra.deg], [now.dec.deg],
                                marker='.',
                                color='crimson',
                                facecolor='crimson',
                                edgecolor='crimson')

        #if len(r['ra'][selected])>0:
        #   # Prepare a dedicated legend entry
        #   ax1_legend_items += [mlines.Line2D([], [],color='crimson',
        #                         markerfacecolor='crimson',
        #                         markeredgecolor='crimson',
        #                         linestyle='-',
        #                         linewidth=0.75,
        #                         marker='.',
        #                         #markersize=10,
        #                         label='PM* (track$=-$%.1f yr)' % (pm_track_time.to(u.yr).value)) ]
        for ax in [ax1, ax2]:
            ax.show_lines(past_tracks,
                          color='crimson',
                          linewidth=0.75,
                          linestyle='-')

    # Query UCAC2 via Vizier over the finding chart area, to look for suitable Guide Stars
    print('   Querying UCAC2 via Vizier to look for possible Guide Stars ...')
    Vizier.ROW_LIMIT = 10000
    gs_table = Vizier.query_region(
        right_center,
        radius=right_radius * u.arcsec,
        inner_radius=fcm_id.get_inner_GS_search(fc_params) * u.arcsec,
        catalog="UCAC2")
    gs_table = gs_table[gs_table.keys()[0]]

    # Turn the table into a list of SkyCoord, that I can clean as I go.
    # Only select guide stars in the nominal GS mag range
    gs_list = [
        SkyCoord(
            ra=line['RAJ2000'],
            dec=line['DEJ2000'],
            obstime=Time('J2000'),
            equinox='J2000',
            frame='icrs',
            unit=(u.deg, u.deg),
            pm_ra_cosdec=line['pmRA'] / 1000. * u.mas / u.yr,
            pm_dec=line['pmDE'] / 1000. * u.mas / u.yr,
            # Assume a fixed distance, so that I can then propagate proper motions
            distance=100. *
            u.pc).apply_space_motion(new_obstime=Time(fcm_m.obsdate))
        for line in gs_table
        if fcm_m.gs_mag[0] <= line['UCmag'] <= fcm_m.gs_mag[1]
    ]

    # Here, I will show all the ephemeris points I have prepared, ahead of the
    # observations (If I have any)

    if len(fc_params['ephem_points_past']) > 0:
        ax1.show_markers(
            [item.ra.deg for item in fc_params['ephem_points_past']],
            [item.dec.deg for item in fc_params['ephem_points_past']],
            marker='*',
            color='crimson',
            s=100,
            facecolor='none',
            edgecolor='crimson')

        # Prepare a dedicated legend entry
        ax1_legend_items += [
            mlines.Line2D(
                [],
                [],
                color='crimson',
                markerfacecolor='none',
                markeredgecolor='crimson',
                linestyle='',
                #linewidth=0.75,
                marker='*',
                #markersize=10,
                label='Target ($\Delta T=-%.0f$ min)' %
                (fc_params['ephem_past_delta'].total_seconds() / 60.))
        ]

    if len(fc_params['ephem_points_future']) > 0:
        ax1.show_markers(
            [item.ra.deg for item in fc_params['ephem_points_future']],
            [item.dec.deg for item in fc_params['ephem_points_future']],
            marker='*',
            color='crimson',
            s=100,
            facecolor='crimson',
            edgecolor='crimson')

        # Prepare a dedicated legend entry
        ax1_legend_items += [
            mlines.Line2D(
                [],
                [],
                color='crimson',
                markerfacecolor='crimson',
                markeredgecolor='crimson',
                linestyle='',
                #linewidth=0.75,
                marker='*',
                #markersize=10,
                label='Target ($\Delta T=+%.0f$ min)' %
                (fc_params['ephem_future_delta'].total_seconds() / 60.))
        ]

    # Show the observation footprint
    for f in fields:

        # Call the function that will plot all the important stuff for this field.
        fcm_id.plot_field(ax1, ax2, fc_params, fields[f])

        # Keep all the possible guide stars in the area.
        gs_list = [
            star for star in gs_list
            if (fields[f][2].separation(star) >
                (fcm_id.get_inner_GS_search(fc_params) / 3600. * u.deg)) and (
                    fields[f][2].separation(star) <
                    (fcm_id.get_GS_outer_radius(fc_params) / 3600. * u.deg))
        ]

    # Show all the suitable Guide Star in the area
    if len(gs_list) > 0:
        ax2.show_markers([np.array(star.ra) for star in gs_list],
                         [np.array(star.dec) for star in gs_list],
                         marker='o',
                         edgecolor='crimson',
                         s=50,
                         linewidth=1.0)
    else:
        warnings.warn('Watch out ... no suitable Guide Star found in UCAC2 !')

    # Add orientation arrows to the large view plot
    add_orient(ax2,
               right_center,
               radius=(right_radius * 0.82) * u.arcsec,
               arrow_width=(right_radius * 0.82) / 4.5 * u.arcsec,
               usetex=fcm_m.fcm_usetex)
    add_orient(ax1,
               left_center,
               radius=(left_radius * 0.82) * u.arcsec,
               arrow_width=(left_radius * 0.82) / 4.5 * u.arcsec,
               usetex=fcm_m.fcm_usetex)

    # Add a scale bar
    (scl, scll) = fcm_id.get_scalebar(fc_params['inst'],
                                      ins_mode=fc_params['ins_mode'])

    ax1.add_scalebar(scl)  # Length in degrees
    ax1.scalebar.show(scl,
                      label=scll,
                      corner='bottom left',
                      color='k',
                      frame=1)
    ax1.scalebar.set_font_size(12)

    scl2 = np.floor(right_radius / 60 / 6) / 60.
    scll2 = r'%.0f$^{\prime}$' % (scl2 * 60)
    ax2.add_scalebar(scl2)
    ax2.scalebar.show(scl2,
                      label=scll2,
                      corner='bottom left',
                      color='k',
                      frame=1)
    ax2.scalebar.set_font_size(12)

    for ax in [ax1, ax2]:
        ax.scalebar.set_linewidth(2)

    # Fine tune things a bit further, just because I can ...
    for ax in [ax1, ax2]:
        ax.tick_labels.set_xformat('hh:mm:ss')
        ax.axis_labels.set_xpad(10)
        ax.ticks.set_linewidth(1.5)
        ax.ticks.set_length(10)
        ax.ticks.set_color('k')
        ax.axis_labels.set_xtext('R.A. [J2000]')

    ax1.axis_labels.set_ytext('Dec. [J2000]')
    #ax1.axis_labels.set_ypad(-10)
    ax2.axis_labels.set_ytext(' ')

    # Add the required OB information to comply with ESO requirements ...
    # ... and make the life of the night astronomer a lot easier !
    ax1.add_label(0.0,
                  1.11,
                  'Run ID: ' + fc_params['prog_id'] + ' | ' + fc_params['pi'],
                  relative=True,
                  horizontalalignment='left',
                  size=14)

    # Fix some bugs for anyone not using LaTeX ... sigh ...
    if fcm_m.fcm_usetex:
        lab = fc_params['ob_name'].replace('_', '\_')
    else:
        lab = fc_params['ob_name']

    ax1.add_label(0.0,
                  1.06,
                  'OB: %i | %s' % (fc_params['ob_id'], lab),
                  relative=True,
                  horizontalalignment='left',
                  size=14)
    #ax1.add_label(0.0,1.08, r'$\lambda_{fc}$: %s (%s)' % (bk_lam_L, survey_L), relative=True,
    #              horizontalalignment='left')

    # Display the Wavelength of the plots
    ax1.add_label(0.02,
                  0.965,
                  r'%s (%s)' % (bk_lam_L, survey_L),
                  relative=True,
                  fontsize=12,
                  horizontalalignment='left',
                  verticalalignment='center',
                  bbox=dict(edgecolor='w', facecolor='w', alpha=0.85))
    ax2.add_label(0.02,
                  0.965,
                  r'%s (%s)' % (bk_lam_R, survey_R),
                  relative=True,
                  fontsize=12,
                  horizontalalignment='left',
                  verticalalignment='center',
                  bbox=dict(edgecolor='w', facecolor='w', alpha=0.85))

    # Add a legend (if warranted) for the left plot
    if len(ax1_legend_items) > 0:
        ax1.ax.legend(
            handles=ax1_legend_items,
            bbox_to_anchor=(-0.03, 0.82, 0.4, .1),  #loc='lower right',
            ncol=1,  #mode="expand", 
            borderaxespad=0.,
            fontsize=10,
            borderpad=0.3,
            handletextpad=0.,
            handlelength=2.0)

    # Start keeping track of any tags I need to show
    tag_string = r' '

    # Show the obsdate
    ax1.add_label(1.0,
                  1.02,
                  r'Date: ' +
                  datetime.strftime(fcm_m.obsdate, '%Y-%m-%d %H:%M %Z'),
                  relative=True,
                  color='k',
                  horizontalalignment='right',
                  fontsize=10)

    # Show the OB tags
    if 'moving_target' in fc_params['tags']:
        tag_string += '$\leadsto$ '

    if 'parallactic_angle' in fc_params['tags']:
        tag_string += '$\measuredangle$ '

    if len(tag_string) > 1:  # only show the tag if it has a non-zero length
        ax1.add_label(
            -0.18,
            1.08,
            tag_string,
            relative=True,
            color='k',
            horizontalalignment='center',
            verticalalignment='center',
            fontsize=30,
            bbox=dict(edgecolor='k',
                      facecolor='lightsalmon',
                      alpha=1,
                      linewidth=1,
                      boxstyle="sawtooth,pad=0.2,tooth_size=0.075"),
        )

    # Finally include the version of fcmaker in there
    ax1.add_label(1.01,
                  0.00,
                  r'Created with fcmaker v%s' % (fcm_v.__version__),
                  relative=True,
                  horizontalalignment='left',
                  verticalalignment='bottom',
                  fontsize=10,
                  rotation=90)

    # Save it all, both jpg for upload to p2, and pdf for nice quality.
    fn_out = os.path.join(
        fcm_m.plot_loc, fc_params['ob_name'].replace(' ', '_') + '_' +
        survey_L.replace(' ', '-'))

    fig1.savefig(fn_out + '.jpg')
    if do_pdf:
        fig1.savefig(fn_out + '.pdf')
    if do_png:
        fig1.savefig(fn_out + '.png')

    plt.close()

    return fn_out + '.jpg'
Ejemplo n.º 4
0
def get_target_from_ephem(fc_params, ephemeris):
    '''
   A function to extract the target coordinates and other useful stuff from a PAF
   ephemeris file.
    
   Args:
      fc_params: the parameters of the finding charts (a dictionary)
      ephemeris: the content of a PAF files as a list of strings from readlines()
   Returns:
      fc_params: the updated parameters of the finding charts (a dictionary)
    
    '''
    # First, extract all the ephemeris points, store them in a list
    # Time, Ra, Dec, pmra, pmdec
    # This VERY MUCH ASSUMES a PAF file !!!
    ephem = [
        list(line.split('"')[1].split(',')[i] for i in [0, 2, 3, 4, 5])
        for line in ephemeris if line[:16] == 'INS.EPHEM.RECORD'
    ]

    # Extract all the times, convert them to datetime values. UTC = PAF default!
    times = [dup.parse(point[0] + ' UTC') for point in ephem]

    # Here, let's do some sanity check:
    if (fcm_m.obsdate > times[-1]) or (fcm_m.obsdate < times[0]):
        raise Exception(
            'Ouch! Specified obsdate outside of Ephemeris range: %s - %s' %
            (times[0].strftime('%Y-%m-%d %H:%M:%S %Z'),
             times[-1].strftime('%Y-%m-%d %H:%M:%S %Z')))

    # Find the index of the closest ephemeris point
    closest = np.argmin(np.abs(np.array(times) - fcm_m.obsdate))

    # Check if I will be accurate (or not)
    if np.abs(times[closest] - fcm_m.obsdate) > timedelta(minutes=30):
        warnings.warn(
            'Observing time %.1f away from closest ephemeris point' %
            (np.abs(times[closest] - fcm_m.obsdate).total_seconds() / 60.))

    closest_target = SkyCoord(
        ra=ephem[closest][1],
        dec=ephem[closest][2],
        unit=(u.hourangle, u.degree),
        pm_ra_cosdec=float(ephem[closest][3]) * u.arcsec / u.second,
        pm_dec=float(ephem[closest][3]) * u.arcsec / u.second,
        obstime=Time(times[closest]),
        distance=fcm_m.ephem_d,
    )
    # Derive the target by propagating proper motions from
    fc_params['target'] = closest_target.apply_space_motion(
        new_obstime=Time(fcm_m.obsdate))

    # Now, I also want to show all the points in the ephemeris file with a fcm_m.ephem_range time interval
    # Find the index and associated times of all the near-by ephemeris entries
    # The advantage, for these, is that I make no errors by assuming a distance

    # For plotting purposes, deal with past and future ephemeris entry points separately
    close_past = (timedelta(seconds=0) <= -(np.array(times) - fcm_m.obsdate)
                  ) * (-(np.array(times) - fcm_m.obsdate) <=
                       timedelta(seconds=fcm_m.ephem_range.to(u.second).value))

    close_future = (timedelta(seconds=0) <=
                    (np.array(times) - fcm_m.obsdate)) * (
                        (np.array(times) - fcm_m.obsdate) <= timedelta(
                            seconds=fcm_m.ephem_range.to(u.second).value))

    # Now store the points as proper SkyCoord entries
    fc_params['ephem_points_past'] = [
        SkyCoord(
            ra=item[1],
            dec=item[2],
            unit=(u.hourangle, u.degree),
            pm_ra_cosdec=float(item[3]) * u.arcsec / u.second,
            pm_dec=float(item[3]) * u.arcsec / u.second,
            obstime=Time(dup.parse(item[0] + ' UTC')),
            distance=fcm_m.ephem_d,
        ) for item in list(np.array(ephem)[close_past])
    ]
    past_times = np.array(times)[close_past]
    # Also store the time delta, for legend purposes.
    fc_params['ephem_past_delta'] = np.median(past_times[1:] - past_times[:-1])

    # Idem for the future points.
    fc_params['ephem_points_future'] = [
        SkyCoord(
            ra=item[1],
            dec=item[2],
            unit=(u.hourangle, u.degree),
            pm_ra_cosdec=float(item[3]) * u.arcsec / u.second,
            pm_dec=float(item[3]) * u.arcsec / u.second,
            obstime=Time(dup.parse(item[0] + ' UTC')),
            distance=fcm_m.ephem_d,
        ) for item in list(np.array(ephem)[close_future])
    ]

    future_times = np.array(times)[close_future]
    # For legend purposes.
    fc_params['ephem_future_delta'] = np.median(future_times[1:] -
                                                future_times[:-1])

    return fc_params
Ejemplo n.º 5
0
def make_gaia_image(skycoord,
                    fov,
                    sampling,
                    fwhm,
                    obsdate=dup.parse("2018 07 01 00:00:00 UTC"),
                    fn=os.path.join('.', 'pseudo_gaia.fits')):
    ''' 
   Given a coordinate, use the Gaia catalogue to create a mock image of the sky.
   
   Args:
      skycoord: the astropy SkyCoord of the center of the image
      fov: size of the image to create, in astropy.units
      sampling: pixel size of the image to create, in astropy.units
      fwhm: fwhm of the fake stars in the image, in astropy.units
      obsdate: date of the observation (to propagate the motion of the stars) as datetime.datetime object 
      fn: relative path + FITS filename
   Returns:
      The FITS filename
   '''

    # Ok, assemble an array of the suitable size
    nx = int(np.floor((fov / sampling).value))
    im = np.zeros((nx, nx))

    # Query GAIA
    print('   Querying GAIA to create a pseudo-image of the sky ...')
    # Make it a sync search, because I don't think I need more than 2000 targets ...
    # TODO: figure out why cone_search fails ... and fix it ! Swap to async for now...
    j = Gaia.cone_search_async(skycoord, fov, verbose=False)
    r = j.get_results()
    nstars = len(r)

    # TODO: well, what if I do need more than 2000 targets ?
    # Issue a warning for now ...
    if nstars == 2000:
        warnings.warn(
            ' Reached query limit of 2k stars. Gaia image will not be complete.'
        )

    # Create a very basic header for the FITS file
    hdr = fits.Header()
    hdr.set('CRPIX1', nx / 2, 'X reference pixel')
    hdr.set('CRPIX2', nx / 2, 'Y reference pixel')
    hdr.set('CRVAL1', skycoord.ra.deg, 'Reference longitude')
    hdr.set('CRVAL2', skycoord.dec.deg, 'Reference latitude')
    hdr.set('CTYPE1', 'RA---TAN', 'Coordinates -- projection')
    hdr.set('CTYPE2', 'DEC--TAN', 'Coordinates -- projection')
    hdr.set('CDELT1', -sampling.to(u.deg).value, 'X scale')  # mind the sign !
    hdr.set('CDELT2', sampling.to(u.deg).value, 'Y scale')
    hdr.set('RADESYS', 'ICRS', 'Coordinate system')
    hdr.set('EQUINOX', 2000.0, 'Epoch of the equinox')
    hdr['COMMENT'] = 'Artificial sky image reconstructed from the Gaia catalogue.'
    hdr['COMMENT'] = 'Number of stars: %i' % (nstars)
    hdr['COMMENT'] = 'FWHM: %i mas' % (int(fwhm.to(u.mas).value))
    hdr['COMMENT'] = 'Bandpass:    285.5-908 THz '
    hdr['COMMENT'] = 'Epoch: %s' % (obsdate.strftime('%Y-%h-%d %H:%M:%S UTC'))
    hdr['COMMENT'] = 'Created %s with fcmaker v%s' % (
        datetime.utcnow().strftime('%Y-%h-%d %H:%M:%S UTC'), fcm_v.__version__)
    hdr['COMMENT'] = 'See http://fpavogt.github.io/fcmaker for details.'

    # Save the empty FITS file for now
    hdu = fits.PrimaryHDU(im, header=hdr)
    hdul = fits.HDUList([hdu])
    hdul.writeto(fn, overwrite=True)

    # Re-extract the WCS info
    w = WCS(fn)

    # Prepare some variables
    x, y = np.meshgrid(np.arange(0, nx, 1), np.arange(0, nx, 1))
    sigma = (fwhm / sampling).value / (2 * np.sqrt(2 * np.log(2)))

    # For each star, add a Gaussian of the suitable size
    for s in range(nstars):
        # First, create a SkyCoord entry
        star = SkyCoord(
            ra=r['ra'][s],
            dec=r['dec'][s],
            obstime=Time(r['ref_epoch'][s], format='decimalyear'),
            frame='icrs',
            unit=(u.deg, u.deg),
            pm_ra_cosdec=r['pmra'][s] * u.mas / u.yr,
            pm_dec=r['pmdec'][s] * u.mas / u.yr,
            # I must specify a generic distance to the target,
            # if I want to later on propagate the proper motions
            distance=fcm_m.default_pm_d,
        )

        # Then, propagate the star at the time of the observation
        star_now = star.apply_space_motion(new_obstime=Time(obsdate))

        # Get the image coordinate of the star
        star_coords = w.wcs_world2pix([star_now.ra.deg], [star_now.dec.deg], 0)

        # The distance of all pixels to the star
        d = np.sqrt((x - star_coords[0])**2 + (y - star_coords[1])**2)

        # Add the star to the array ... scale it as a function of its flux
        im += r['phot_g_mean_flux'][s] * np.exp(-(
            (d)**2 / (2.0 * (fwhm / sampling).value**2)))

    # Save the final FITS file
    hdu = fits.PrimaryHDU(im, header=hdr)
    hdul = fits.HDUList([hdu])
    hdul.writeto(fn, overwrite=True)

    return fn