Calibration frames#

Prerequisites

  • Basic statistics

  • Knowledge about CCD, bias, dark, and flat frames.

We will learn

  • What do bias, dark, and flat frames look like

  • How to deal with outliers; sigma-clipping.

  • How to combine individual calibration frames to make “master” calibration frames.

  • Further familiarize with operations on numpy arrays and data I/O.

Reference

  • Here is a great tutorial on CCD data reduction. Please check out!!

  • Howell, S., Handbook of CCD Astronomy, Second Ed, Cambridge University Press 2006

%load_ext autoreload
%autoreload 2

import os, sys
import numpy as np
import matplotlib.pyplot as plt

# We can beautify our plots by changing the matpltlib setting a little
plt.rcParams['font.size'] = 18
plt.rcParams['figure.figsize'] = (8, 6)
plt.rcParams['figure.dpi'] = 80
plt.rcParams['axes.linewidth'] = 2
Hide code cell source
# Don't worry about this cell, it's just to make the notebook look a little nicer.
try:
    import google.colab
    IN_COLAB = True
except ImportError:
    IN_COLAB = False

# Get the directory right
if IN_COLAB:
    # Download utils.py
    !wget -q -O /content/utils.py https://raw.githubusercontent.com/AstroJacobLi/ObsAstGreene/refs/heads/main/book/docs/utils.py
    # Function to check and install missing packages
    from google.colab import drive
    drive.mount('/content/drive/')
    os.chdir('/content/drive/Shareddrives/AST207/data')
else:
    # if not os.path.exists("../../../_static/ObsAstroData/"):
        # os.makedirs("../../../_static/ObsAstroData/")
    sys.path.append('../../docs/')
    os.chdir('../../_static/ObsAstroData/')

1. Bias frames#

As discussed in class, a small voltage is applied to every pixel to ensure the pixel value is always positive. This voltage introduces a “bias” to the pixel values. However, the bias can vary slightly between pixels and may change over time. To account for this, we take zero-time exposures (where dark current and real photons do not contribute to the pixel values) to characterize the bias.

Below we load bias frames taken using the camera of IFU-M on the 6.5 m Magellan telescope in Chile.

1.1 Read a bias frame#

from astropy.io import fits

FITS (Flexible Image Transport System) format is widely used in astronomy for images and tables. The following code will read the file b0854c1.fits, which is a list containing only one HDU (Header Data Unit).

fits.open('./calibration_frames/bias/b0860c1.fits')
[<astropy.io.fits.hdu.image.PrimaryHDU object at 0x10e7606d0>]
fits.open('./calibration_frames/bias/b0860c1.fits').info()
Filename: ./calibration_frames/bias/b0860c1.fits
No.    Name      Ver    Type      Cards   Dimensions   Format
  0  PRIMARY       1 PrimaryHDU     143   (2176, 1156)   int16 (rescales to uint16)   

Now we know that this file has one HDU, which is an image containing 2176 * 1156 pixels. The data type in int16. We can check the header of the HDU.

hdu = fits.open('./calibration_frames/bias/b0860c1.fits')[0]
hdu.header
SIMPLE  =                    T                                                  
BITPIX  =                   16                                                  
NAXIS   =                    2                                                  
NAXIS1  =                 2176                                                  
NAXIS2  =                 1156                                                  
BSCALE  =           1.00000000         / real=bzero+bscale*value                
BZERO   =       32768.00000000                                                  
BUNIT   = 'DU/PIXEL'                                                            
ORIGIN  = 'LCO/OCIW'                                                            
OBSERVER= 'Bailey'                     / observer name                          
TELESCOP= 'Clay'                       / telescope                              
SITENAME= 'LCO'                                                                 
SITEALT =                 2405         / meters                                 
SITELAT =            -29.01423                                                  
SITELONG=            -70.69242                                                  
TIMEZONE=                    4                                                  
DATE-OBS= '2023-03-21T10:50:38'        / (start)                                
UT-DATE = '2023-03-21'                 / UT date (start)                        
UT-TIME = '10:50:38'                   / UT time (start) 39038 seconds          
UT-END  = '10:50:39'                   / UT time (end) 39039 seconds            
LC-TIME = '07:50:38'                   / local time (start) 28238 seconds       
NIGHT   = '20Mar2023'                  / local night                            
MJD     =          60024.45183         / modified Julian day                    
INSTRUME= 'IFUM'                       / instrument name                        
SCALE   =                0.250         / arcsec/pixel                           
EGAIN   =                0.680         / electrons/DU                           
ENOISE  =                2.700         / electrons/read                         
NOPAMPS =                    4         / # of op-amps                           
OPAMP   =                    1         / this op-amp                            
CHOFFX  =              256.000         / x-offset [arcsec]                      
CHOFFY  =              257.000         / y-offset [arcsec]                      
DISPAXIS=                    1         / disperser axis                         
RA      = ' 17:57:11.9'                / right ascension                        
RA-D    =          269.2997917         / degrees                                
DEC     = '-28:59:59.0'                / declination                            
DEC-D   =          -28.9997222         / degrees                                
EQUINOX =           2000.00000         / of above coordinates                   
EPOCH   =           2023.21768         / epoch (start)                          
AIRMASS =                1.000         / airmass (start)                        
ST      =              64960.8         / sidereal time: 18:02:41 (start)        
ROTANGLE=               273.69         / rotator offset angle                   
TEL-AZIM=                 90.0         / telescope azimuth                      
TEL-ELEV=                 90.0         / telescope elevation                    
T-DOME  =                 13.9         / dome temperature [C]                   
T-CELL  =                 12.4         / primary cell temperature [C]           
T-TRUSS =                  8.7         / telescope truss temperature [C]        
WX-TEMP =                 13.6         / weather station temperature [C]        
FILENAME= 'b0860c1'                                                             
OBJECT  = '1x2 bias'                   / object name                            
COMMENT =                              / comment                                
EXPTYPE = 'Bias'                       / exposure type                          
EXPTIME =                0.000         / exposure time                          
NLOOPS  =                  250         / # of loops per sequence                
LOOP    =                    7         / # within this sequence                 
BINNING = '1x2'                        / binning                                
SPEED   = 'Slow'                       / readout speed                          
NOVERSCN=                  128         / overscan pixels                        
NBIASLNS=                  128         / bias lines                             
BIASSEC = '[2049:2176,1029:1156]'      / NOAO: bias section                     
DATASEC = '[1:2048,1:1028]'            / NOAO: data section                     
TRIMSEC = '[1:2048,1:1028]'            / NOAO: trim section                     
SUBRASTR= 'none'                       / full frame                             
TEMPCCD =               -109.9         / CCD temperature [C]                    
SHOE    = 'B'                                                                   
IFU     = 'LSB'                                                                 
IFUENC  =                -1801         / IFU encoder                            
OCCULTOR= 'Science Gap'                                                         
OCCENC  =                98000         / occultor encoder                       
SLIDE   = 'LoRes'                                                               
SLIDEENC=               4061.0         / slide encoder                          
SLIDESTP=               285126         / slide step                             
LO-ELEV = '9.1301'                     / step= -107184                          
HI-AZIM = '-0.55001?'                  / step= -14143                           
HI-ELEV = '0.40001?'                   / step= 10286                            
FOCUS   =                196.1         / step= 1961                             
FILTER  = 'BK7'                                                                 
SLITNAME= '80'                         / 607 526                                
BENEAR  =                    0         / lamp level (0..10)                     
LIHE    =                    0         / lamp level (0..10)                     
THXE    =                    0         / lamp level (0..10)                     
LAMPS   = 'Off'                        / lamp combo                             
LED-UV  =                    0         / LED level (0..4096)                    
LED-BL  =                    0         / LED level (0..4096)                    
LED-VI  =                    0         / LED level (0..4096)                    
LED-NR  =                    0         / LED level (0..4096)                    
LED-FR  =                    0         / LED level (0..4096)                    
LED-IR  =                    0         / LED level (0..4096)                    
LEDS    = 'Off'                        / LED combo                              
CONFIGFL= 'unknown'                    / configuration file                     
TEMP01  =               14.500         / IFU_Entrance                           
TEMP02  =               13.750         / IFU_Top                                
TEMP03  =               13.812         / Fiber_Exit                             
TEMP04  =               25.100         / IFU_Motor                              
TEMP05  =               24.600         / IFU_Drive                              
TEMP06  =               19.688         / IFU_Hoffman                            
TEMP07  =               20.562         / IFU_Shoebox                            
TEMP08  =               13.375         / Cradle_R                               
TEMP09  =               13.188         / Cradle_B                               
TEMP10  =               13.562         / Echelle_R                              
TEMP11  =               13.438         / Echelle_B                              
TEMP12  =               13.688         / Prism_R                                
TEMP13  =               13.375         / Prism_B                                
TEMP14  =               13.250         / LoResGrating_R                         
TEMP15  =               13.250         / LoResGrating_B                         
ADC-STGE= 'in'                         / ADC stage                              
ADC-CTRL= 'auto'                       / ADC control                            
MIRROR  = 'in'                         / Mirror stage                           
DCU-FFS = 'retracted'                  / DCU FF-Screen                          
DCU-LMPS= 'Off'                        / DCU lamps                              
SOFTWARE= 'Version beta (0071) (Oct  5 2022, 17:02:27)'                         
FITSVERS= '0.070'                      / FITS header version                    
COMMENT                                                                         
COMMENT                                                                         
COMMENT                                                                         
COMMENT                                                                         
COMMENT                                                                         
COMMENT                                                                         
COMMENT                                                                         
COMMENT                                                                         
COMMENT                                                                         
COMMENT                                                                         
COMMENT                                                                         
COMMENT                                                                         
COMMENT                                                                         
COMMENT                                                                         
COMMENT                                                                         
COMMENT                                                                         
COMMENT                                                                         
COMMENT                                                                         
COMMENT                                                                         
COMMENT                                                                         
COMMENT                                                                         
COMMENT                                                                         
COMMENT                                                                         
COMMENT                                                                         
COMMENT                                                                         
COMMENT                                                                         
COMMENT                                                                         
COMMENT                                                                         
COMMENT                                                                         
COMMENT                                                                         
COMMENT                                                                         
COMMENT                                                                         

Let’s read the data array.

img = hdu.data
img
array([[817, 817, 817, ..., 816, 813, 819],
       [816, 818, 820, ..., 817, 818, 828],
       [809, 827, 817, ..., 812, 814, 816],
       ...,
       [813, 810, 815, ..., 814, 816, 823],
       [821, 818, 816, ..., 823, 820, 816],
       [812, 819, 815, ..., 819, 816, 814]], dtype=uint16)

Exercise 1#

Use the statistical tools that you’ve learned to understand this bias data. This will help you to understand the characteristics of the bias frame.

  1. Where do the pixel values concentrate? How to quantify this?

  2. What’s the dispersion of the pixel values? How to quantify this?

  3. Are there any outliers? How to identify them?

  4. What’s the distribution of the pixel values? How to visualize this?

  5. Check that there is no negative pixel value in the bias frame. Why?

Tip: The dispersion in the pixel values of a bias frame is largely contributed by the read-out noise, a noise that is added to each pixel when reading the charge out. We already know the read-out noise from Basic Stats (part 2). Read-out noise may be viewed as a “toll” that must be paid for reading the stored charge. Thanks to the bias voltage, none of the pixels are negative.

## Your answer here

We provide a function show_image to help visualize astronomical images that typically have a large dynamical range. Please feel free to use it.

from utils import show_image
show_image(img, show_colorbar=True, figsize=(12, 12));
../../_images/988e7274581b17323570f6cffd626f7571180d197eddaeabde5e482e56594ab0.png

The bias frame shows some spatial patterns: some rows have a lower bias value than others. We also see some distinct “bright” pixels. They could be either bad pixels, or caused by Cosmic Rays (CR). As a consequence, when we plot the histogram of pixel values, we will see some outliers from the bulk of pixels.

During the Magellan observation, we took many bias frames. So let’s load another bias frame. What do you expect? Will it be very different from the one above?

show_image(fits.open('./calibration_frames/bias/b0861c1.fits')[0].data);
../../_images/b4cee1b0447821b3ddead1dd3bc878c4ff91bfdfbd55c4aa5337fe445dbeb474.png

Yeah, the bright spots changed their positions in the image. So it does seem like they are caused by cosmic rays.

Exercise 2#

  1. What remains the same between different bias frames? What is different?

  2. How might we use all the bias frames to make the best bias correction?

We have provided lots of bias frames in ./calibration_frames/bias. You are encouraged to load them and explore the data.

## Your answer here

1.2 Combine bias frames to make “master” bias#

We have already learned from basic statistics that combining images will reduce the noise level. Belowe we try to combine 40 bias frames. The bias frames are all named bXXXXc1.fits, where XXXX is a four-digit number, from 0860 to 0900.

biases = [fits.open(f'./calibration_frames/bias/b0{ind}c1.fits')[0].data for ind in range(860, 900)]
biases = np.array(biases)

Here I use “Nested List Comprehensions” to construct a list. This is equivalent to the following snippet, but is much shorter.

biases = []
for ind in range(860, 900):
    biases.append(fits.open(
        f'./calibration_frames/bias/b0{ind}c1.fits'
        )[0].data)
biases.shape
(40, 1156, 2176)
# We have introduced how to calculate the mean and median image using the `axis` argument.
# Check `basic_stat.ipynb`
med_bias = np.median(biases, axis=0)
mean_bias = np.mean(biases, axis=0)
fig, [ax1, ax2] = plt.subplots(2, 1, figsize=(12, 12))

ax1 = show_image(med_bias, ax=ax1, fig=fig,)
ax1.text(0.1, 0.8, 'Median-combined', fontsize=25, color='w', 
         ha='left', transform=ax1.transAxes)

ax2 = show_image(mean_bias, ax=ax2, fig=fig,)
ax2.text(0.1, 0.8, 'Mean-combined', fontsize=25, color='w', 
         ha='left', transform=ax2.transAxes)

plt.tight_layout()
../../_images/7dde74cc10b1bfc23cdd54862f480247bb6c9feda92d2f2776f57cab8f3337b0.png

Wow! The cosmic rays are everywhere in the mean-combined image! The vast difference between the median-combined and mean-combined images highlights that the mean is far less immune to outliers (i.e., cosmic rays) than the median. Because of this, we often use median to combine images.

The histogram below shows again that combining individual bias frames will help reduce the read-out noise, thus boost the SNR.

plt.hist(biases[0].flatten(), histtype='step', density=True, 
         range=(795, 840), bins=25, lw=1, label='Single bias frames')
for i in range(1, 21):
    plt.hist(biases[i].flatten(), histtype='step', density=True, 
         range=(795, 840), bins=25, alpha=0.1, color='C0')

plt.hist(med_bias.flatten(), histtype='step', density=True, 
         range=(795, 840), bins=25, lw=2, label='40 biases combined')

plt.xlabel('Pixel value [counts]')

plt.legend(loc='upper center', ncols=2, frameon=False, bbox_to_anchor=(0.5, 1.15));
../../_images/9285630ca2a8e486d1cbad54592def9e06f92a52c01d4bc6053c6cf3173f6378.png

1.3 Sigma-clipping#

In astronomy, there is yet another way to deal with outliers.

Recall that for a Gaussian distribution, the probability for a data point being \(>3\sigma\) away from the mean \(\mu\) is very very slim. So if we see such a data point that is quite far from the center of the data set, it is probably an outlier and should be removed. This is the idea of sigma clipping.

In the following, we flag out pixels that are \(>3\sigma\) away from the median value (we don’t use the mean value because it is heavily biased by these cosmic rays). You can see the cosmic rays being marked as black in the figure below (they are tiny dots).

hdu = fits.open('./calibration_frames/bias/b0860c1.fits')[0]
img = hdu.data.astype(float)
med, std = np.median(img), np.std(img)
flag = (np.abs(img - med) > 3 * std)
show_image(flag, cmap='Greys', vmin=0., vmax=0.1, show_colorbar=False);
../../_images/a713c40d8c8d7a09c0cc493a1bae7c300695a3a33da590996ab9b53f79d1c93a.png

We can replace these outlier pixels with np.nan (not a number, typically used to indicate a bad value). Then we repeat this for all 40 bias frames and combine them.

# This might be slow
biases_clipped = []
for ind in range(860, 900):
    img = fits.open(f'./calibration_frames/bias/b0{ind}c1.fits')[0].data.astype(float)
    med, std = np.median(img), np.std(img)
    flag = (np.abs(img - med) > 3 * std)
    img[flag] = np.nan
    biases_clipped.append(img)
biases_clipped = np.array(biases_clipped)
# might be slow
med_bias_clipped = np.nanmedian(biases_clipped, axis=0)
mean_bias_clipped = np.nanmean(biases_clipped, axis=0)
fig, [ax1, ax2] = plt.subplots(2, 1, figsize=(12, 12))

ax1 = show_image(med_bias_clipped, ax=ax1, fig=fig, vmin=816.6, vmax=818.4)
ax1.text(0.1, 0.8, 'Median-combined, sigma-clipped', fontsize=25, color='w', 
         ha='left', transform=ax1.transAxes)

ax2 = show_image(mean_bias_clipped, ax=ax2, fig=fig, vmin=816.6, vmax=818.4)
ax2.text(0.1, 0.8, 'Mean-combined, sigma-clipped', fontsize=25, color='w', 
         ha='left', transform=ax2.transAxes)

plt.tight_layout()
../../_images/3f139350869d7265669085b3d15e471a07d846dedbdaa1026554a7cbefab6bae.png

Yeah! After sigma-clipping, the mean-combined image looks much better and very similar to the median-combined one. This sigma-clipping trick is quite common in astronomy. We used a threshold of 3-sigma and only clipped once. You can repeat the procedure for several times to make sure all outliers are flagged. Actually, astropy already provides us a function sigma_clip to do these jobs. Let’s check it out.

from astropy.stats import sigma_clip
astropy_clipped = sigma_clip(biases,  # the bias frames before sigma-clipping
                             sigma=3,  # threshould is 3-sigma
                             maxiters=5,  # do clipping for 5 times at most
                             cenfunc='median', stdfunc='std',
                             # use median and standard deviation to calculate clipping criterion
                             axis=(1, 2), # calc median and std for each bias frame
                             masked=False)
# might be slow
med_bias_apy = np.nanmedian(astropy_clipped, axis=0)
mean_bias_apy = np.nanmean(astropy_clipped, axis=0)
fig, [ax1, ax2] = plt.subplots(2, 1, figsize=(12, 12))

ax1 = show_image(med_bias_apy, ax=ax1, fig=fig, vmin=816.6, vmax=818.4)
ax1.text(0.1, 0.8, 'Median-combined, astropy sigma-clipped', fontsize=25, color='w', 
         ha='left', transform=ax1.transAxes)

ax2 = show_image(mean_bias_apy, ax=ax2, fig=fig, vmin=816.6, vmax=818.4)
ax2.text(0.1, 0.8, 'Mean-combined, astropy sigma-clipped', fontsize=25, color='w', 
         ha='left', transform=ax2.transAxes)

plt.tight_layout()
../../_images/3430cd0ea95940195d8421d2c43f3541b7c9d1fb86f7f46967f1302c2f8b6671.png

We can write the median-combined image to a FITS file for future usage.

hdu = fits.PrimaryHDU(med_bias_clipped.astype(np.float32))
hdulist = fits.HDUList([hdu])
hdulist.writeto('./calibration_frames/bias/master_bias.fits', overwrite=True)

2. Dark frames#

Just to give you a flavor of dark frame, here we show the five dark frames that we took during the Magellan observation. They are full of cosmic rays. We construct a master dark frame by combining them using median.

darks = [fits.open(f'./calibration_frames/dark/b{ind}c1.fits')[0].data for ind in range(1212, 1217)]
darks = np.array(darks)
med_dark = np.median(darks, axis=0)
fig, [ax1, ax2] = plt.subplots(2, 1, figsize=(12, 12))

ax1 = show_image(darks[0], ax=ax1, fig=fig)
ax1.text(0.1, 0.8, 'Single dark frame (3600s)', fontsize=25, color='w', 
         ha='left', transform=ax1.transAxes)

ax2 = show_image(med_dark, ax=ax2, fig=fig)
ax2.text(0.1, 0.8, 'Master dark frame (median-combined)', fontsize=25, color='w', 
         ha='left', transform=ax2.transAxes)

plt.tight_layout()
../../_images/58209f74bdaa47bd8f67517ac097ddbeef3f9ace5ff8dbc510b15b600c999607.png

The light area in the lower left corner is due to sensor glow, which is light emitted by the electronics of the CCD. The counts in a dark frame scales linearly with time. The exposure time for a dark frame is typically similar to (or longer than) the exposure time of your science frames.

3. Flat frames#

Flat frames are used to correct for pixel-to-pixel sensitivity variations due to dust on the optics, vignetting, CCD quantum efficiency, etc. They are taken by pointing the telescope at a uniformly illuminated screen. The screen can be a white screen, a twilight sky, or a dome screen. Below, we load the flat frames taken during the Magellan observation.

Let’s load a few flat frames of the Magellan IMACS camera and see how they look like. These data are taken with for the i filter using the “screen” in the dome (so-called “dome flat”).

camera = 'ift'
detector = 'c2'
flats = [fits.open(f'./calibration_frames/flat/{camera}/{camera}{ind:04d}{detector}.fits')[0].data.T for ind in range(14, 19)]
flats = np.array(flats)[:, :1024, :2048] # we trim off the overscan region
med_flat = np.median(flats, axis=0)
fig, [ax1, ax2] = plt.subplots(2, 1, figsize=(12, 12))

ax1 = show_image(flats[0], ax=ax1, fig=fig)
ax1.text(0.1, 0.8, f'Single dome flat frame for {camera}-{detector}', fontsize=25, color='w', 
         ha='left', transform=ax1.transAxes)

ax2 = show_image(med_flat, ax=ax2, fig=fig)
ax2.text(0.1, 0.8, f'Master flat frame for {camera}-{detector} \n (median-combined)', fontsize=25, color='w', 
         ha='left', transform=ax2.transAxes)
plt.tight_layout()
../../_images/524dfe3a88826c16a6e76ced87d19d8c968f71dac67727f2424000433b72abff.png

For the above figure, we see that the flat frames are not flat at all! The vignetting effect (i.e., the illumination is not uniform across the field) is quite obvious. The absolute value of the pixels in the flat frame is not important, but the relative value is. So we normalize the flat frames by dividing them by their median value.

med_flat_norm = med_flat / np.nanmedian(med_flat)
show_image(med_flat_norm, vmin=0.7, vmax=1.1, cmap='inferno', 
           show_colorbar=True, figsize=(12, 6), colorbar_label='Normalized pixel value');
../../_images/7dc2a08ce0b89b0c7837624b4e3b3757f373695ecfb221080482fb586bbde4a9.png

Note that the above flat frame is only 1/8 of the full field-of-view of the IMACS camera, but the illumination pattern is still quite obvious. Stars at the edge/corner of the field-of-view will look dimmer than those in the center, despite having the same magnitude/intrinsic brightness. In order to correct for this, we need to divide any science frame by the flat frame.

Not all flat frames look this good! Let’s see the flat frames of another camera (also on Magellan)! You’ll see lots of dust spots and artifacts.

camera = 'iff'
detector = 'c3'
flats = [fits.open(f'./calibration_frames/flat/{camera}/{camera}{ind:04d}{detector}.fits')[0].data.T for ind in range(8, 13)]
flats = np.array(flats)[:, :1024, :2048] # we trim off the overscan region
med_flat = np.median(flats, axis=0)
fig, [ax1, ax2] = plt.subplots(2, 1, figsize=(12, 12))

ax1 = show_image(flats[0], ax=ax1, fig=fig)
ax1.text(0.1, 0.8, f'Single dome flat frame for {camera}-{detector}', fontsize=25, color='w', 
         ha='left', transform=ax1.transAxes)

ax2 = show_image(med_flat, ax=ax2, fig=fig)
ax2.text(0.1, 0.8, f'Master flat frame for {camera}-{detector} \n (median-combined)', fontsize=25, color='w', 
         ha='left', transform=ax2.transAxes)
plt.tight_layout()
../../_images/bfe1d0820455816e805fb6cae2137f7b59fe5ef9a7b37565f98fd76ef69659ce.png
med_flat_norm = med_flat / np.nanmedian(med_flat)
show_image(med_flat_norm, vmin=0.6, vmax=1.1, cmap='inferno', 
           show_colorbar=True, figsize=(12, 6), colorbar_label='Normalized pixel value');
../../_images/f54f82f57608334ed7ea3e96cf2ae071382df756c06bb6d77285d34d9c478599.png

Oh my gosh… The flat frames are full of dust spots and dead pixels! The dust spots are caused by dust on the optics, while the dead pixels are pixels that are not sensitive to light (you can see their sensitivity is <0.6 from the colorbar). These dust spots and dead pixels are “fixed” in the camera and will show up in every science frame. This highlights the need of flat frames to correct the science exposures!

Exercise 3#

Actually, the median flat we just made is not ready to use for flat-fielding. Do you know why? Does the flat frame also have a bias level and a dark current? How can we correct for this?

Summary#

We have learned how to read and understand bias, dark, and flat frames. We have also learned how to combine multiple calibration frames to make “master” calibration frames. We have also learned how to deal with outliers using sigma-clipping.

Exercise 4#

  1. Does a dark frame also have a bias level? If so, how can we correct this?

  2. Load all dark frames in ./calibration_frames/dark, subtract the master bias frame from each dark frame, and then combine them to make a master dark frame. Try different combining methods (mean, median, sigma-clipping) and compare the results.