<jupyter_start><jupyter_text>Introduction to Optical Spectroscopy  <NAME> (SDSU)  In this lecture I will discuss: * image vs. spectral information* (grating) spectrographs* the spectral reduction process* data needed for calibration  What information do we get from images? * relative **position** of a target  * does the relative position of the target change with time?* **brightness** of the target compared to other objects  * does the relative brightness of the target change with time?* angular size (or limit) * **color**  * the brightness in one bandpass compared to another   Imaging data<jupyter_code># http://skyserver.sdss.org/dr2/en/tools/explore/obj.asp?id=587722984438038552
image_url = 'http://das.sdss.org/imaging/752/40/corr/6/fpC-000752-g6-0451.fit.gz'
image = fits.getdata(image_url)

spec_url = 'http://das.sdss.org/spectro/1d_23/0302/1d/spSpec-51688-0302-325.fit'
head = fits.getheader(spec_url)
spec = fits.getdata(spec_url)

def show_image():
    sample = sigma_clip(image)
    vmin = sample.mean() - 1 * sample.std()
    vmax = sample.mean() + 15 * sample.std()
    plt.imshow(image[100:300, 1350:1700], vmin=vmin, vmax=vmax, origin='lower', cmap='gray');
show_image()<jupyter_output><empty_output><jupyter_text>Spectral data<jupyter_code>urlbase = 'http://classic.sdss.org/dr7/instruments/imager/filters/{}.dat'
def spec_v_phot(showbands=False):
    wav = 10**(head['crval1'] + head['cd1_1'] * np.arange(spec[0, :].size))
    scale = spec[0, :].max() * 2
    if showbands:
        for filt in ['g', 'r', 'i']:
            band = np.genfromtxt(urlbase.format(filt), names=('wav', 'T'), usecols=(0, 3))
            plt.plot(band['wav'], scale * band['T'], label=filt, color='k', ls='--')
            plt.fill_between(band['wav'], scale * band['T'], alpha=0.25, color='0.7')
    plt.plot(wav, spec[0, :], lw=2);
    plt.xlabel('Wavlength (Angstrom)')
    plt.ylabel('Flux Density'); plt.grid()
spec_v_phot()<jupyter_output><empty_output><jupyter_text>Spectra gives information about * temperature* composition* line-of-sight velocity  Higher spectral resolution gives more information about distant targets **Spectral resolution:** $$R = \lambda / \Delta \lambda$$ a picture is worth a 1000 words  a spectrum is worth 100,000 words  But spectra come at a cost * dividing up photons into smaller and smaller wavelength (energy) bins means each bin gets fewer and fewer photons    * spectroscopy is aperture hungry  Dispersing light ![](https://upload.wikimedia.org/wikipedia/commons/thumb/1/10/Comparison_refraction_diffraction_spectra.svg/200px-Comparison_refraction_diffraction_spectra.svg.png)  Recall interference fringes from a double slit  Positions of bright fringes are wavelength dependent<jupyter_code>n = 5000
detector = np.arange(n) - n / 2
def get_fringes(wav, spacing=10, distance=100000):
    # for each spot on the detector screen, determine the path length difference
    path1 = np.hypot(detector - spacing/2, distance)
    path2 = np.hypot(detector + spacing/2, distance)
    dpath = np.abs(path1 - path2)
    return np.cos(dpath / wav * 2 * np.pi)**2
plt.plot(detector, get_fringes(0.11) , c='r')
plt.grid()<jupyter_output><empty_output><jupyter_text>A look through a transmission grating ![](https://upload.wikimedia.org/wikipedia/commons/a/aa/Light-bulb-grating.png)  interference also works in reflection  Spectroscopy of astrophysical targets<jupyter_code>def plot_image(disperse=False, mask=False, detector=False):
    xa, xb = 0, 6
    maska, maskb = 1.9, 2.1

    stars = [(3, 4.9), (2, 3), (4, 5), (5, 1)]
    for star in stars:
        x, y = star
        if (not mask) or (maska < x < maskb):
            plt.plot(*star, marker='*', ms=30, color='k')
    
    if mask:
        plt.axvspan(xa, maska, color='0.4', alpha=0.95)
        plt.axvspan(maskb, xb, color='0.4', alpha=0.95)

    if disperse:
        n = 100
        xspec = np.linspace(0.5, 2.0, n)
        for star in stars:
            x, y = star
            if (not mask) or (maska < x < maskb):
                plt.scatter(x + xspec, y + np.zeros(n), marker='o', zorder=10
                            , c=xspec, vmin=xspec.min(), vmax=xspec.max(), cmap='rainbow')
                #plt.scatter(x - xspec, y + np.zeros(n), marker='o', zorder=10
                #            , c=xspec, vmin=xspec.min(), vmax=xspec.max(), cmap='rainbow')   
            
    if detector:
        x0, x1 = 3, 4
        y0, y1 = 2.75, 3.5
        plt.plot([x0, x1, x1, x0, x0], [y0, y0, y1, y1, y0], c='k')
        
    plt.ylim(0, 6)
    plt.xlim(xa, xb)
plot_image()<jupyter_output><empty_output><jupyter_text>Schematic Layout of Keck/LRIS ![](https://www2.keck.hawaii.edu/inst/lris/images/xlris_good.gif)  What the data look like (cartoon version)<jupyter_code># draw a spectrum

def add_line(image, xs, cx, sigma, weights=1):
    image += np.abs(np.exp( -(xs - cx)**2 / 2 / sigma**2) * weights)
    
def make_image():
    # start with a blank image
    nx, ny = 512, 128
    image = np.zeros((ny, nx))
    ys, xs = np.indices(image.shape)

    # add some sky lines
    for x in [15, 100, 250, 275, 310, 350, 400, 410, 430, 455]:        
        add_line(image, xs, x, 4)
    sky = image.copy()
    
    # add object trace
    weights = 5-(np.abs(xs-200)/150)**2
    weights /= weights.max()
    weights *= 3
    add_line(image, ys, 100, 6, weights=weights)
    
    return image, sky
image, sky = make_image()
plt.imshow(image, vmin=0, vmax=0.5 * image.max())
plt.xlabel('Wavlength Coordinate')
plt.ylabel('Spatial Coordinate');<jupyter_output><empty_output><jupyter_text>Note in real data:* the lines of constant wavelength are silghtly tilted and curved with respect to the pixel grid  Extracting a spectrum * spectroscopy is photometry but the band passes are smaller and you get many bands at once* same basic proceedure for reducing the data, but wavelengenth dependicies need to be handled* extraction is also analagous to photometry, but again wavelength dependicies matter<jupyter_code>spec = ????
plt.plot(spec, lw=5)
plt.xlabel('Wavelength Coordinate')
plt.ylabel('Relative Flux Density');<jupyter_output><empty_output><jupyter_text>Wavelength Calibration<jupyter_code>plt.plot(????)
plt.xlabel('Wavelength Coordinate')
plt.ylabel('Relative Flux Density');<jupyter_output><empty_output><jupyter_text>Flux calibration * observe a "standard star" -- an object with known flux densities* use the observed counts and known flux densities in each wavelength bin to get the response function (basically the wavelength dependent instrumental zeropoints)   Real data<jupyter_code>def show_spec_2d(imtype):
    # load the 2D science image data
    image = fits.getdata('media/spec_{}.fits'.format(imtype))

    # determine the image pixel distribution (used for displaying below)
    sample = sigma_clip(image)
    vmin = sample.mean() - 1 * sample.std()
    vmax = sample.mean() + 3 * sample.std()

    # show the image using matplotlib's `imshow` function
    plt.figure(figsize=(15, 3))
    plt.imshow(image, origin='lower', cmap='gray', aspect='auto', vmin=vmin, vmax=vmax)
    plt.xlabel('Column Number')
    plt.ylabel('Row Number');<jupyter_output><empty_output><jupyter_text>Calibration data: arc lamp spectra<jupyter_code>show_spec_2d('lamp')<jupyter_output><empty_output><jupyter_text>Calibration data: flat field spectrum<jupyter_code>show_spec_2d('flat')<jupyter_output><empty_output><jupyter_text>Calibration data: standard star spectrum<jupyter_code>show_spec_2d('std')<jupyter_output><empty_output><jupyter_text>Science Spectrum<jupyter_code>show_spec_2d('sci')<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.1354728396685519, 'fraction_numerical': 0.03432855451795344, 'mean_word_length': 4.019141914191419, 'pattern_counts': {'":': 0, '<': 54, '<?xml version=': 0, '>': 50, 'https://': 3, 'lorem ipsum': 0, 'www.': 1, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '12', 'dir': 'jupyter-structured-clean-dedup', 'id': '607671', 'license': 'BSD-3-Clause', 'n_tokens_mistral': 2848, 'n_tokens_neox': 2598, 'n_words': 677, 'path': 'spectra.lecture.ipynb', 'repo_name': 'rmquimby/Astronomical_Techniques', 'size': '7610'}