As discussed in a previous post (here), read or readout noise is the noise or uncertainty added to every pixel value read out of a CCD array. This noise is largely associated with the imperfect conversion of an analog signal into a digital value.
The method to determine a CCD’s readout noise contribution to the final signal is as follows1:
The results of performing these operations on ten pairs of bias frames is summarized in the table below:
| Pair # | Std. Dev | Gain | Read Noise2 |
| Bias Pair 1 | 35.388 ADU | 0.37 e-/ADU | 9.26 e- RMS |
| Bias Pair 2 | 35.305 ADU | 0.37 e-/ADU | 9.24 e- RMS |
| Bias Pair 3 | 35.327 ADU | 0.37 e-/ADU | 9.24 e- RMS |
| Bias Pair 4 | 35.294 ADU | 0.37 e-/ADU | 9.23 e- RMS |
| Bias Pair 5 | 35.250 ADU | 0.37 e-/ADU | 9.22 e- RMS |
| Bias Pair 6 | 35.259 ADU | 0.37 e-/ADU | 9.22 e- RMS |
| Bias Pair 7 | 35.416 ADU | 0.37 e-/ADU | 9.27 e- RMS |
| Bias Pair 8 | 35.372 ADU | 0.37 e-/ADU | 9.25 e- RMS |
| Bias Pair 9 | 35.220 ADU | 0.37 e-/ADU | 9.21 e- RMS |
| Bias Pair 10 | 35.226 ADU | 0.37 e-/ADU | 9.22 e- RMS |
| Average | 35.306 ADU | 0.37 e-/ADU | 9.24 e- RMS |
1From “Handbook of CCD Astronomy Second Edition” by Steve B. Howell
2Published read noise for this device = 9.3 e- RMS
After several years of using general scripting software (CCD Commander) for all my astro-photography image capture needs, I’ve decided to learn and write my own scripts that are tailored to my specific equipment and to my unique needs. The programming language I’m using is Python (currently at version 2.6.5). Python is a general purpose, high-level programming language that is free and open-source software. It supports both object oriented programming and structured programming. I’ve found Python to be very easy to learn, write, and debug. It fully and reliably supports the COM interfaces that most (if not all) ASCOM-compliant astronomy software currently implements.
The first script that I have written and fully debugged is a routine that I use to capture dark and bias calibration frames. Typically, I do not spend time at my dark site capturing dark and bias frames. Instead, when I get home or when needed, I place the camera in the refrigerator (a dark and relatively cold place) and have the software automatically take the calibration frames while I sleep. On a typical night I might instruct the script to take, for example, 40 dark frames at 600 seconds each (10 minutes) at a CCD temperature of -10C and then take 100 bias frames at the same temperature.
When the program is started, the following menu is shown:
Exec_DarkBias.py
——————————-
1. Specify save path
2. Setup for Dark Frames
3. Setup for Bias Frames
4. Confirm setup
5. Reset settings
6. Begin script
7. Quit
Option ‘1′ displays the current save path and allows the operator either to accept this path or specify a new path for the calibration frames. The second option asks for operator input to specify the Dark frame exposure length, binning, number of frames and CCD temperature setpoint for these frames. Option ‘3′ asks for operator input to specify the Bias binning, number of frames, and CCD temperature. The fourth option displays the current settings for path, Dark frames, and Bias frames for operator verification. Option ‘5′ resets all path, Dark, and Bias settings to their default, startup values. Entering a ‘6′ starts the script operation and entering a ‘7′ quits the entire program.
When the script begins, the software first checks to make sure either Dark frames setup or Bias frames setup (or both) have been specified then it sets the Dark frame CCD temperature setpoint. Next a loop is entered where the CCD temperature is checked once a second. The CCD temperature is considered stable when the read-back temperature does not deviate more than +/-0.5C from the setpoint temperature for 120 consecutive readings (2-minutes). If the temperature does not stabilize within 8 minutes, the program fails, displays an error message, and terminates. Otherwise, the CCD temperature will be considered stable and Dark frame capture will begin. After all dark frames have been taken, the Bias frame CCD temperature will be set. If the Bias CCD temperature is the same as the Dark CCD temperature, no wait for stabilization will occur. Otherwise, the same CCD temperature stabilization loop will execute. After all Bias frames have been captured, the script will end and the program will return to the top-level menu. Here, another script can be set up or the program can be terminated.
This script makes use of the Maxim/DL CAMERA COM object only. Additionally, a log file is generated that saves the progress of the program for future reference. The program listing and a sample logfile for this program is available by clicking on the link below:
I was pretty sure my 11″ Zambuto mirror was due to be stripped and recoated but I had no idea that it was in such bad condition. Here are a couple of photos of the mirror that shows how badly the coating has deteriorated. The photos were made in a dark room with my Canon XSi DSLR. The shots were made at ISO 100, 30 second exposures during which I ‘painted’ the back of the mirror with a red light. The amount of red light ‘leaking’ through the coating is very obvious. Here are two photos of the 11″ mirror (click on images to view larger representations):
As a comparison, here are similar photos of a recently re-coated 8″ mirror:
Here’s the listing for a simple C program that prompts the user for his/her longitude (in decimal degrees, + for east of prime meridian and – for west of prime meridian). It utilizes built-in library functions and retrieves the local time and GMT time from the user’s computer then calculates the Julian Day and Sidereal Time. If the user enters ‘r’ or ‘R’, the program will re-calculate both values. Otherwise, the program exits normally.
The executable version of this program can be downloaded here.
Here is the complete program listing (or may be downloaded here):
#include
#include
#define BASE_YEAR 1900
/* constant string arrays of the days of the week and the months of the year */
const char *days[] = {“Sunday”,”Monday”,”Tuesday”,”Wednesday”,”Thursday”,”Friday”,”Saturday”};
const char *months[] = {“January”,”February”,”March”,”April”,”May”,”June”,”July”,
“August”,”September”,”October”,”November”,”December”};
/* prototypes of functions defined below */
void display_time(char *, struct tm *);
double julian_date(struct tm *, int);
double sidereal_time(double, struct tm *, double);
int main(void)
{
double julian, sidereal;
float longitude;
double sid_hr, sid_mn, sid_sc;
char c;
time_t timer;
/* Print header */
printf(“\n”);
printf(” ***********************************************************************************\n”);
printf(” * This program displays the computer’s local time, the Greenwich Mean Time (GMT), *\n”);
printf(” * the Julian date, and local sidereal time (LST). The only input required is the *\n”);
printf(” * the user’s longitude. This must be entered in degrees and decimal degrees *\n”);
printf(” * (dd.ddd) east or west the prime meridian. For locations east of the prime *\n”);
printf(” * meridian, the value entered should be positive (+). For locations west of the *\n”);
printf(” * prime meridian, the value should be negative (-). *\n”);
printf(” ***********************************************************************************\n”);
printf(“\n”);
/* Prompt user for longitude in decimal degrees */
printf(” Enter your longitude: “);
scanf(“%f”,&longitude);
do
{
/* Display the user’s longitude */
printf(“\n Longitude: \t%0.2f “,fabs(longitude));
if (longitude < 0.0)
printf("West\n");
else
printf("East\n");
/* Load time struct with current data. Show local time and GMT */
timer=time(NULL);
display_time(" Local: ",localtime(&timer));
display_time(" GMT: ",gmtime(&timer));
/* Call Julian date function and display result */
julian = julian_date(gmtime(&timer),0);
printf(" Julian Date:\t%0.6f\n",julian);
/* Call Julian date function for present day at 0h GMT then call sidereal time function */
julian = julian_date(gmtime(&timer),1);
sidereal = sidereal_time(julian,gmtime(&timer),longitude);
/* Split sidereal time into hours, minutes, and seconds then display */
sid_mn = modf(sidereal,&sid_hr) * 60;
sid_sc = modf(sid_mn,&sid_mn) * 60;
modf(sid_sc,&sid_sc);
printf(" LST: \t%02.0f:%02.0f:%02.0f\n",sid_hr,sid_mn,sid_sc);
/* Read to end of line or end of buf to flush the input stream */
do {
c = fgetc(stdin);
} while ((c != '\n') && (c != EOF));
/* Wait for user to press ENTER to end or 'r' to repeat */
printf("\n\n Press ENTER to exit or 'R' plus ENTER to repeat: ");
scanf("%c",&c);
} while ((c == 'r') || (c == 'R'));
return 0;
}
/* Displays the time and date contained in the data of the struct that 'time' points to */
void display_time(char *str, struct tm *time)
{
printf("%s\t%s, %02d %s",str,*(days+(time->tm_wday)),time->tm_mday,*(months+(time->tm_mon)));
printf(” %d”,time->tm_year + BASE_YEAR);
printf(” %02d:%02d:%02d\n”,time->tm_hour,time->tm_min,time->tm_sec);
}
/* Calculate the Julian date for the date and time contained in the struct that ‘time’ points to. */
/* For ‘t_zero’ = 0, show the exact Julian date. For ‘t_zero’ = 1, show the Julian date at prior 0h GMT. */
double julian_date(struct tm *time, int t_zero)
{
int A,B;
int year = time->tm_year + BASE_YEAR;
int month = time->tm_mon + 1;
double julian_day, DD;
if (!t_zero)
DD = time->tm_mday + ((time->tm_hour + time->tm_min/60.0 + time->tm_sec/3600.0)/24.0);
else
DD = time->tm_mday;
if (month < 2)
{
year -= 1;
month += 12;
}
A = year/100;
B = 2 - A + A/4;
julian_day = floor(365.25 * year) + floor(30.6001*(month + 1.0)) + DD + 1720994.5 + B;
/* Return 'julian_day' in decimal format */
return julian_day;
}
/* Calculate and return the sidereal time given inputs of the Julian date at prior 0h GMT, */
/* current GMT as given in struct that 'time' points to, and the user-inputed longitude. */
double sidereal_time(double julian, struct tm *time, double lngtd)
{
double T, UT;
double local_sidereal;
/* Calculate decimal equivalent of current GMT */
UT = time->tm_hour;
UT += time->tm_min/60.0 + time->tm_sec/3600.0;
T = (julian – 2415020.0) / 36525.0;
local_sidereal = 6.6460656 + 2400.051262 * T + 0.00002581 * T * T;
/* Normalize ‘local_sidereal between 0 and 23 */
while (local_sidereal > 24)
{
local_sidereal -= 24;
}
local_sidereal += 1.002737908 * UT;
local_sidereal += lngtd / 15.0;
/* Normalize ‘local_sidereal’ between 0 and 23 */
if (local_sidereal < 0)
{
local_sidereal += 24;
}
else if (local_sidereal > 24)
{
local_sidereal -= 24;
}
/* Return LST in decimal format */
return local_sidereal;
}
The following table shows the eyepieces that I currently use with my 11″ Starmaster (f/5.4). Also shown in the table are the eyepieces’ apparent field of view, the magnification when used in the 11″ Starmaster, the calculated true field of view, and the measured true field of view:
| Eyepiece | Apparent FOV | Magnification | Calc. True FOV | Meas. True FOV |
|---|---|---|---|---|
| TMB Optical 3.2mm | 60o | x472 | 0.13o | 0.125o |
| TMB Optical 6mm | 60o | x252 | 0.24o | 0.23o |
| Nagler Type 6 9mm | 82o | x168 | 0.49o | 0.50o |
| Nagler Type 5 16mm | 82o | x94 | 0.87o | 0.88o |
| Panoptic 27mm | 68o | x56 | 1.21o | 1.17o |
11″ Starmaster focal length = 11in. x 25.4mm/in. x 5.4 = 1509mm.
Magnification = (scope focal length) / (eyepiece focal length)
Eyepiece calculated field of view = (Apparent Field of View) / Magnfication
The eyepiece’s calculated field of view is found in a non-tracking scope by centering a star in a given eyepiece then timing how many seconds the star takes to leave the field of view. Divide this number of seconds by 120 to get the true size of the field of view in degrees. (Repeat several times for each eyepiece to be sure of the reading.)