예제 #1
0
def make_gaussmask_from_linelist(refwl, relint=None, step=0.01, gauss_sigma=1):

    #sigma in units of steps
    #steps should be adjusted depending on np.min(np.diff(refwl)), and spectrograph resolving power

    ### input could be sth like this:
    #     from readcol import readcol
    #     #read in master line list
    #     linelist = readcol('/Users/christoph/OneDrive - UNSW/linelists/thar_mm.arc', twod=False, skipline=8)
    #     refwl = linelist[0]
    #     relint = 10**linelist[2]

    if relint is None:
        relint = np.ones(len(refwl))

    refgrid = np.arange(np.floor(np.min(refwl)), np.ceil(np.max(refwl)), step)
    refspec = np.zeros(len(refgrid))
    for i, pos in enumerate(refwl):
        #print('Line '+str(i+1)+'/'+str(len(refwl)))
        ix = find_nearest(refgrid, pos, return_index=True)
        #print('ix = ',ix)
        if relint is None:
            amp = 1
        else:
            amp = relint[i]
        peak = CMB_pure_gaussian(refgrid[ix - 20:ix + 21], pos,
                                 gauss_sigma * step, amp)
        refspec[ix - 20:ix + 21] = refspec[ix - 20:ix + 21] + peak * relint[i]

    return refgrid, refspec
예제 #2
0
def make_pseudoslits(sigma=67., nfib=19):
    """
    This routine creates random-normal-sampled offsets for the stellar fibres that make up the Veloce pseudoslit. The user has the option to supply the desired level of scatter 
    in the fibre offsets, which is used to draw from a random-normal distribution the offsets (in steps of 10 m/s). Default value is 67 m/s, based on the considerations 
    in "WORD DOCUMENT HERE". Maximum offset is +/- 200 m/s, which corresponds to ~3 default sigmas, or a probability of ~0.27% that the offset more than that (in which case 
    the maximum offset is still used).
    NOTE: Alignment inaccuracies in the spatial directions are NOT treated here, as they should not influence the RV measurements.
    
    INPUT:
    'sigma'   : RMS of the fibre offsets (in m/s)
    'nfib'    : number of fibres (default for Veloce is 19 stellar fibres)
    
    OUTPUT:
    'offsets' : an nfib-element array of the individual fibre offsets (in m/s)
    
    MODHIST:
    15/05/2018 - CMB create
    """

    #For random samples from N(mu, sigma^2), use:   "sigma * np.random.randn(dimensions) + mu"
    pos = sigma * np.random.randn(nfib)

    #divide into discreet bins
    bins = np.arange(-200, 201, 10)

    #which bin is the right one?
    offsets = np.array([])
    for x in pos:
        offsets = np.append(offsets, find_nearest(bins, x))

    return offsets
예제 #3
0
def make_binmask_from_linelist(refwl, relint=None, step=0.01):

    ### input could be sth like this:
    #     from readcol import readcol
    #     #read in master line list
    #     linelist = readcol('/Users/christoph/OneDrive - UNSW/linelists/thar_mm.arc', twod=False, skipline=8)
    #     refwl = linelist[0]

    if relint is None:
        relint = np.ones(len(refwl))

    refgrid = np.arange(np.floor(np.min(refwl)), np.ceil(np.max(refwl)), step)
    refspec = np.zeros(len(refgrid))
    for i, pos in enumerate(refwl):
        #print('Line '+str(i+1)+'/'+str(len(refwl)))
        ix = find_nearest(refgrid, pos, return_index=True)
        #print('ix = ',ix)
        refspec[np.max([0, ix - 1]):np.min([ix + 2, len(refspec)])] = relint[i]

    return refgrid, refspec
예제 #4
0
def check_transformation_scatter_daophot(
        lfc_files,
        M_list=None,
        nx=4112,
        ny=4202,
        wrt='centre',
        n_sub=1,
        eps=0.5,
        return_residuals=True,
        ref_obsname='21sep30019',
        return_M_list=False,
        return_pixel_phase=False,
        lfc_path='/Users/christoph/OneDrive - UNSW/lfc_peaks/'):
    # INPUT:
    # 'lfc_files' - list of lfc files for which to check
    # 'M_list'    - corresponding list of calculated transformation matrices

    # WARNING: They obviously need to be in the same order. See how it's done at the start of "check_all_shifts_with_telemetry"

    if M_list is not None:
        assert len(lfc_files) == len(
            M_list
        ), 'ERROR: list of files and list of matrices have different lengths!!!'
    else:
        M_list_new = []

    # read reference LFC peak positions
    _, yref, xref, _, _, _, _, _, _, _, _ = readcol(lfc_path + ref_obsname +
                                                    'olc.nst',
                                                    twod=False,
                                                    skipline=2)
    xref = nx - xref
    yref = yref - 54.  # or 53??? but does not matter for getting the transformation matrix
    if wrt == 'centre':
        xref -= nx // 2
        yref -= ny // 2
    ref_peaks_xy_list = [(xpos, ypos) for xpos, ypos in zip(xref, yref)]

    all_delta_x_list = []
    all_delta_y_list = []
    if return_pixel_phase:
        xphi_list = []
        yphi_list = []

    # loop over all files
    for i, lfc_file in enumerate(lfc_files):

        print('Processing observation ' + str(i + 1) + '/' +
              str(len(lfc_files)) + '...')

        # read observation LFC peak positions
        try:
            _, y, x, _, _, _, _, _, _, _, _ = readcol(lfc_file,
                                                      twod=False,
                                                      skipline=2)
        except:
            _, y, x, _, _, _, _, _, _ = readcol(lfc_file,
                                                twod=False,
                                                skipline=2)
        del _
        x = nx - x
        y = y - 54.  # or 53??? but does not matter for getting the transformation matrix
        if wrt == 'centre':
            x -= nx // 2
            y -= ny // 2
        obs_peaks_xy_list = [(xpos, ypos) for xpos, ypos in zip(x, y)]
        obs_peaks_xy = np.array(obs_peaks_xy_list).T

        if M_list is None:
            # NOTE that we do not want to shift to the centre twice, so we hard-code 'corner' here!!! (xref, yref, x, y) are already transformed above!!!
            M = find_affine_transformation_matrix(
                xref, yref, x, y, timit=True, eps=2., wrt='corner'
            )  # note that within "wavelength_solution" this is called "Minv"
            M_list_new.append(M)
        else:
            M = M_list[i]

        # now we need to match the peaks so we can compare the reference peaks with the (back-)transformed obs peaks
        good_ref_peaks = []
        good_obs_peaks = []
        for n, refpeak in enumerate(ref_peaks_xy_list):
            # print(n)
            shifted_obs_peaks = obs_peaks_xy - np.expand_dims(
                np.array(refpeak), axis=1)
            distance = np.sqrt(shifted_obs_peaks[0, :]**2 +
                               shifted_obs_peaks[1, :]**2)

            if np.sum(distance < eps) > 0:
                if np.sum(distance < eps) > 1:
                    print('FUGANDA: ', refpeak)
                    print(
                        'There is probably a cosmic really close to an LFC peak - skipping this peak...'
                    )
                else:
                    good_ref_peaks.append(refpeak)
                    good_obs_peaks.append((obs_peaks_xy[0, distance < eps],
                                           obs_peaks_xy[1, distance < eps]))
            # print(n, refpeak, np.sum(distance < eps))

        # calculate pixel phase as defined by Anderson & King, 2000, PASP, 112:1360
        if return_pixel_phase:
            x_pixel_phase = np.squeeze(good_obs_peaks)[:, 0] - np.round(
                np.squeeze(good_obs_peaks)[:, 0], 0)
            y_pixel_phase = np.squeeze(good_obs_peaks)[:, 1] - np.round(
                np.squeeze(good_obs_peaks)[:, 1], 0)

        # divide good_ref_peaks into several subsections for a more detailed investigation
        x_step = nx / np.sqrt(n_sub).astype(int)
        y_step = ny / np.sqrt(n_sub).astype(int)
        x_centres = np.arange(0.5 * x_step,
                              (np.sqrt(n_sub).astype(int) + 0.5) * x_step,
                              x_step)
        y_centres = np.arange(0.5 * y_step,
                              (np.sqrt(n_sub).astype(int) + 0.5) * y_step,
                              y_step)
        if wrt == 'centre':
            x_centres -= nx // 2
            y_centres -= ny // 2
        peak_subsection_id = []
        for refpeak in good_ref_peaks:
            # first, figure out which subsection this particular peak falls into
            xpos = refpeak[0]
            ypos = refpeak[1]
            nearest_x_ix = find_nearest(x_centres, xpos, return_index=True)
            nearest_y_ix = find_nearest(y_centres, ypos, return_index=True)
            # then save that information
            peak_subsection_id.append((nearest_x_ix, nearest_y_ix))

        # give each subsection a label
        subsection_id = []
        for j in range(np.sqrt(n_sub).astype(int)):
            for i in range(np.sqrt(n_sub).astype(int)):
                subsection_id.append((i, j))  # (x,y)

        # # divide chip into several subsections for a more detailed investigation
        # section_masks = []
        # section_indices = []
        # x_step = nx / np.sqrt(n_sub).astype(int)
        # y_step = ny / np.sqrt(n_sub).astype(int)
        # for j in range(np.sqrt(n_sub).astype(int)):
        #     for i in range(np.sqrt(n_sub).astype(int)):
        #         q = np.zeros((ny, nx), dtype='bool')
        #         q[j * y_step : (j+1) * y_step, i * x_step : (i+1) * x_step] = True
        #         section_masks.append(q)
        #         section_indices.append((i,j))   # (x,y)

        # go to homogeneous coordinates (ie add a z-component equal to 1, so that we can include translation into the matrix)
        good_ref_peaks_xyz = np.hstack(
            (np.array(good_ref_peaks),
             np.expand_dims(np.repeat(1, len(good_ref_peaks)), axis=1)))
        good_obs_peaks_xyz = np.hstack(
            (np.squeeze(np.array(good_obs_peaks)),
             np.expand_dims(np.repeat(1, len(good_obs_peaks)), axis=1)))

        # calculate transformed co-ordinates (ie the observed peaks transformed back to match the reference peaks)
        xyz_prime = np.dot(good_obs_peaks_xyz, M)
        delta_x = good_ref_peaks_xyz[:, 0] - xyz_prime[:, 0]
        delta_y = good_ref_peaks_xyz[:, 1] - xyz_prime[:, 1]

        delta_x_list = []
        delta_y_list = []
        # loop over all subsections
        for tup in subsection_id:
            # find indices of peaks falling in each subsection
            ix = [i for i, x in enumerate(peak_subsection_id) if x == tup]
            if return_residuals:
                delta_x_list.append(delta_x[ix])
                delta_y_list.append(delta_y[ix])
            else:
                # return difference between ref and obs
                delta_x_list.append(good_ref_peaks_xyz[ix, 0] -
                                    good_obs_peaks_xyz[ix, 0])
                delta_y_list.append(good_ref_peaks_xyz[ix, 1] -
                                    good_obs_peaks_xyz[ix, 1])


#####   DO THIS IF YOU WANT TO GET A TRANSFORMATION MATRIX FOR EVERY SUBSECTION OF THE CHIP   #####
#                 M = find_affine_transformation_matrix(np.squeeze(good_ref_peaks)[ix,0], np.squeeze(good_ref_peaks)[ix,1], np.squeeze(good_obs_peaks)[ix,0], np.squeeze(good_obs_peaks)[ix,1], timit=True, eps=2., wrt='corner')
#                 good_ref_peaks_xyz = np.hstack((np.array(good_ref_peaks)[ix], np.expand_dims(np.repeat(1, len(ix)), axis=1)))
#                 good_obs_peaks_xyz = np.hstack((np.squeeze(np.array(good_obs_peaks)[ix]), np.expand_dims(np.repeat(1, len(ix)), axis=1)))
#                 xyz_prime = np.dot(good_obs_peaks_xyz, M)
#                 delta_x = good_ref_peaks_xyz[:, 0] - xyz_prime[:, 0]
#                 delta_y = good_ref_peaks_xyz[:, 1] - xyz_prime[:, 1]
#                 plt.plot(delta_x,'.')
#                 print(np.std(delta_x))
#                 sub_M_list.append(M)

# append to all-files list
        all_delta_x_list.append(delta_x_list)
        all_delta_y_list.append(delta_y_list)

    if M_list is None:
        M_list = M_list_new[:]

    if return_pixel_phase:
        if not return_M_list:
            return all_delta_x_list, all_delta_y_list, x_pixel_phase, y_pixel_phase
        else:
            return all_delta_x_list, all_delta_y_list, x_pixel_phase, y_pixel_phase, M_list
    else:
        if not return_M_list:
            return all_delta_x_list, all_delta_y_list
        else:
            return all_delta_x_list, all_delta_y_list, M_list
def plot_free_spectral_range(P_id):
    """ img = thorium image (already rotated so that orders are horizontal """

    thresholds = np.load(
        '/Users/christoph/UNSW/linelists/AAT_folder/thresholds.npy').item()
    fitshapes = np.load(
        '/Users/christoph/UNSW/dispsol/lab_tests/fitshapes.npy').item()
    thflux = np.load(
        '/Users/christoph/UNSW/veloce_spectra/reduced/tests/thorium-17-12_40orders.npy'
    ).item()
    thflux2 = np.load(
        '/Users/christoph/UNSW/veloce_spectra/reduced/tests/thorium-17-12_collapsed_40orders.npy'
    ).item()
    wl = np.load(
        '/Users/christoph/UNSW/dispsol/lab_tests/wavelengths.npy').item()
    #     #wavelength solution from Zemax as a reference
    #     zemax_dispsol = np.load('/Users/christoph/UNSW/dispsol/mean_dispsol_by_orders_from_zemax.npy').item()
    #     zemax_wl = {}

    xx = np.arange(4112)

    fig1 = plt.figure()
    plt.xlabel('pixel number')
    plt.ylabel('pixel number')
    plt.title('Veloce Rosso CCD')

    for ord in sorted(P_id.keys()):  #don't have anough lines in order 40
        ordnum = ord[-2:]
        m = 105 - int(ordnum)
        #zemax_wl[ord] = 10. * zemax_dispsol['order'+str(m)]['model'](xx[::-1])

        plt.plot(xx, P_id[ord](xx))
        plt.scatter(fitshapes[ord]['x'],
                    fitshapes[ord]['y'],
                    marker='.',
                    color='k')

    #now also plot order 40, for which we do not have a wavelength solution
    plt.plot(xx, P_id['order_40'](xx))

    # add labels to redmost and bluemost orders
    plt.text(-150, 4300, 'm')
    plt.text(-150, P_id['order_01'](0) - 35, '104')
    plt.text(-150, P_id['order_11'](0) - 35, '94')
    plt.text(-150, P_id['order_21'](0) - 35, '84')
    plt.text(-150, P_id['order_31'](0) - 35, '74')
    plt.text(-150, P_id['order_40'](0) - 35, '65')
    plt.text(4175, 4300, r'$\lambda_c$ ' + ur'[\u00c5]')
    plt.text(4175, P_id['order_01'](4111) - 35,
             np.round(614000. / 104., 0).astype(int))
    plt.text(4175, P_id['order_11'](4111) - 35,
             np.round(614000. / 94., 0).astype(int))
    plt.text(4175, P_id['order_21'](4111) - 35,
             np.round(614000. / 84., 0).astype(int))
    plt.text(4175, P_id['order_31'](4111) - 35,
             np.round(614000. / 74., 0).astype(int))
    plt.text(4175, P_id['order_40'](4111) - 35,
             np.round(614000. / 65., 0).astype(int))

    # calculate free spectral range
    #indices for the free spectral range
    lix = np.zeros(len(wl))
    lix_y = np.zeros(len(wl))
    rix = np.zeros(len(wl))
    rix_y = np.zeros(len(wl))
    zlix = np.zeros(len(wl))
    zlix_y = np.zeros(len(wl))
    zrix = np.zeros(len(wl))
    zrix_y = np.zeros(len(wl))
    for i, ord in enumerate(sorted(wl.keys())):
        ordnum = ord[-2:]
        if i != 0:
            lix[i] = find_nearest(wl[ord],
                                  np.max(wl['order_' +
                                            str(int(ordnum) - 1).zfill(2)]),
                                  return_index=True)
            lix_y[i] = P_id[ord](find_nearest(
                wl[ord],
                np.max(wl['order_' + str(int(ordnum) - 1).zfill(2)]),
                return_index=True))
            zlix[i] = find_nearest(
                zemax_wl[ord],
                np.max(zemax_wl['order_' + str(int(ordnum) - 1).zfill(2)]),
                return_index=True)
            zlix_y[i] = P_id[ord](find_nearest(
                zemax_wl[ord],
                np.max(zemax_wl['order_' + str(int(ordnum) - 1).zfill(2)]),
                return_index=True))
        if i != 38:
            rix[i] = find_nearest(wl[ord],
                                  np.min(wl['order_' +
                                            str(int(ordnum) + 1).zfill(2)]),
                                  return_index=True)
            rix_y[i] = P_id[ord](find_nearest(
                wl[ord],
                np.min(wl['order_' + str(int(ordnum) + 1).zfill(2)]),
                return_index=True))
            zrix[i] = find_nearest(
                zemax_wl[ord],
                np.min(zemax_wl['order_' + str(int(ordnum) + 1).zfill(2)]),
                return_index=True)
            zrix_y[i] = P_id[ord](find_nearest(
                zemax_wl[ord],
                np.min(zemax_wl['order_' + str(int(ordnum) + 1).zfill(2)]),
                return_index=True))
    lix_y = lix_y[lix != 0]  #runs from order02 to order39   (39 orders total)
    lix = lix[lix != 0]  #runs from order02 to order39   (39 orders total)
    rix_y = rix_y[rix != 0]  #runs from order01 to order38   (39 orders total)
    rix = rix[rix != 0]  #runs from order01 to order38   (39 orders total)
    zlix_y = zlix_y[zlix !=
                    0]  #runs from order02 to order39   (39 orders total)
    zlix = zlix[zlix != 0]  #runs from order02 to order39   (39 orders total)
    zrix_y = zrix_y[zrix !=
                    0]  #runs from order01 to order38   (39 orders total)
    zrix = zrix[zrix != 0]  #runs from order01 to order38   (39 orders total)

    #fit 2nd order polynomial to the FSR points
    l_fsr_fit = np.poly1d(np.polyfit(lix, lix_y, 2))
    r_fsr_fit = np.poly1d(np.polyfit(rix, rix_y, 2))
    zl_fsr_fit = np.poly1d(np.polyfit(zlix, zlix_y, 2))
    zr_fsr_fit = np.poly1d(np.polyfit(zrix, zrix_y, 2))

    #and overplot this fit
    yl = l_fsr_fit(xx)
    plt.plot(xx, yl, 'r--')
    yr = r_fsr_fit(xx)
    plt.plot(xx, yr, 'r--')
    plt.xlim(-200, 4312)
    plt.ylim(-200, 4402)
    ytop = np.repeat(4402, 4112)
    ylr = np.maximum(yl, yr)
    plt.fill_between(xx,
                     ytop,
                     ylr,
                     where=ytop > yl,
                     facecolor='gold',
                     alpha=0.25)

    return
def plot_free_spectral_range_wl(P_id, dispsol, degpol=5):
    """ img = thorium image (already rotated so that orders are horizontal """

    thresholds = np.load(
        '/Users/christoph/UNSW/linelists/AAT_folder/thresholds.npy').item()

    fig1 = plt.figure()
    plt.xlabel('pixel number')
    plt.ylabel(ur'wavelength [\u00c5]')
    plt.title('Veloce Rosso CCD')

    #prepare wavelength dictionary
    wavelengths = {}

    for ord in sorted(dispsol.keys()):  #don't have anough lines in order 40
        ordnum = ord[-2:]
        m = 105 - int(ordnum)
        print('OK, fitting ' + ord + '   (m = ' + str(m) + ')')
        coll = thresholds['collapsed'][ord]
        if coll:
            data = thflux2[ord]
        else:
            data = thflux[ord]

        xx = np.arange(len(data))

        fitted_line_pos = fit_emission_lines(
            data,
            return_all_pars=return_all_pars,
            varbeta=False,
            timit=False,
            verbose=False,
            thresh=thresholds['thresh'][ord],
            bgthresh=thresholds['bgthresh'][ord],
            maxthresh=thresholds['maxthresh'][ord])
        goodpeaks, mostpeaks, allpeaks = find_suitable_peaks(
            data,
            thresh=thresholds['thresh'][ord],
            bgthresh=thresholds['bgthresh'][ord],
            maxthresh=thresholds['maxthresh'][ord])

        line_number, refwlord = readcol(
            '/Users/christoph/UNSW/linelists/AAT_folder/ThAr_linelist_order_' +
            ordnum + '.dat',
            fsep=';',
            twod=False)
        lam = refwlord.copy()

        mask_order = np.load(
            '/Users/christoph/UNSW/linelists/posmasks/mask_order' + ordnum +
            '.npy')
        x = fitted_line_pos[mask_order]
        #stupid python!?!?!?
        if ordnum == '30':
            x = np.array([x[0][0], x[1][0], x[2], x[3]])

        #perform the fit
        fitdegpol = degpol
        while fitdegpol > len(x) / 2:
            fitdegpol -= 1
        if fitdegpol < 2:
            fitdegpol = 2
        thar_fit = np.poly1d(np.polyfit(x, lam, fitdegpol))

        wavelengths[ord] = thar_fit(xx)

        plt.plot(xx, thar_fit(xx))
        plt.scatter(x, lam, marker='.', color='k')

    # add labels to redmost and bluemost orders
    plt.text(4200, 9500, 'm')
    plt.text(4200, 5930, '104')
    plt.text(4200, 9370, '66')

    # calculate free spectral range
    #indices for the free spectral range
    lix = np.zeros(len(wavelengths))
    lix_wl = np.zeros(len(wavelengths))
    rix = np.zeros(len(wavelengths))
    rix_wl = np.zeros(len(wavelengths))
    for i, ord in enumerate(sorted(wavelengths.keys())):
        ordnum = ord[-2:]
        if i != 0:
            lix[i] = find_nearest(
                wavelengths[ord],
                np.max(wavelengths['order_' + str(int(ordnum) - 1).zfill(2)]),
                return_index=True)
            lix_wl[i] = wavelengths[ord][find_nearest(
                wavelengths[ord],
                np.max(wavelengths['order_' + str(int(ordnum) - 1).zfill(2)]),
                return_index=True)]
        if i != 38:
            rix[i] = find_nearest(
                wavelengths[ord],
                np.min(wavelengths['order_' + str(int(ordnum) + 1).zfill(2)]),
                return_index=True)
            rix_wl[i] = wavelengths[ord][find_nearest(
                wavelengths[ord],
                np.min(wavelengths['order_' + str(int(ordnum) + 1).zfill(2)]),
                return_index=True)]
    lix_wl = lix_wl[lix !=
                    0]  #runs from order02 to order39   (39 orders total)
    lix = lix[lix != 0]  #runs from order02 to order39   (39 orders total)
    rix_wl = rix_wl[rix !=
                    0]  #runs from order01 to order38   (39 orders total)
    rix = rix[rix != 0]  #runs from order01 to order38   (39 orders total)

    return
예제 #7
0
def make_LFC_linelist(delta_f=25.,
                      wlmin=550.,
                      wlmax=950.,
                      shift=0.,
                      savefile=True,
                      outpath='/Users/christoph/OneDrive - UNSW/linelists/'):
    """
    PURPOSE:
    make fake laser-comb line-list for JS's Echelle++
    
    INPUT:
    'delta_f'  : line spacing in GHz
    'wlmin'    : minimum wavelength in nm
    'wlmax'    : maximum wavelength in nm
    'shift'    : apply this RV shift (negative for blueshift)
    'savefile' : boolean - do you want to save the linelist to a file?
    'outpath'  : directory for outpuf file 
    
    OUTPUT:
    'wl'     : wl of lines in microns
    'relint' : relative intensities (should be all equal for the LFC)
    
    MODHIST:
    15/06/2018 - CMB create
    """

    veloce_grating_constant = 61.4  # m*lambda_c in microns for Veloce (only approximately)

    if shift % np.floor(shift) != 0:
        print(
            'WARNING: non-integer RV shift provided!!! It will be rounded to the nearest integer [in m/s]!'
        )
        shift = np.round(shift, 0)

    c = 2.99792458E8  #speed of light in m/s
    #convert delta_f to Hertz
    delta_f *= 1e9
    #min and max frequency in Hz
    fmin = c / (wlmax * 1e-9)
    fmax = c / (wlmin * 1e-9)

    #all the frequencies
    f0 = np.arange(fmin, fmax, delta_f)

    #calculate the "Doppler" shift (it really is just an offset in pixel space)
    fshift = fmin * (shift / c)

    #apply shift to frequencies (WARNING: don't Doppler-shift all frequencies, as this is not actually a Doppler shift, but is supposed to simulate a shift in pixels)
    f = f0 - fshift  #the minus sign is because the shift is applied to frequencies rather than to wavelengths

    #convert to the wavelengths
    wl = np.flip(
        (c / f) / 1e-6,
        axis=0)  #wavelength in microns (from shortest to longest wavelength)
    relint = [1] * len(wl)

    #make one line near the order centres a different intensity so that it is easier to identify the lines
    for o in np.arange(1, 44):
        m = 65 + o
        ordcen = veloce_grating_constant / m
        ordcen_ix = find_nearest(wl, ordcen, return_index=True)
        relint[ordcen_ix] = 2.

    if savefile:
        #string manipulations for the output file name
        if shift > 0:
            redblue = '_red'
        elif shift < 0:
            redblue = '_blue'
        else:
            redblue = ''
        np.savetxt(outpath + 'laser_linelist_25GHz' + redblue + '_' +
                   str(int(shift)) + 'ms.dat',
                   np.c_[wl, relint],
                   delimiter=';',
                   fmt='%12.10f; %i')
        return
    else:
        return wl, relint