;+ ; NAME: ; SNIRAC ; ; PURPOSE: ; Calculates S/N for IRAC, including the Poisson noise ; contirbution of the target. ; ; CATEGORY: ; Spitzer Space Telescope ; ; CALLING SEQUENCE: ; SNIRAC ; ; OUTPUTS: ; None. ; ; COMMON BLOCKS: ; block1. ; ; RESTRICTIONS: ; None. ; ; MODIFICATION HISTORY: ; V1.0 - written by DWH, Spitzer SUST, March 2007 ; V1.1 - fixes for bugs found by Seppo (f_ch should not be changed ; in snirac_calc) and Sean (resolve_all calls obsolete program ; setlog under IDL 6.2-Mac). ; V1.2 - as requested by Sean, removed 1.5x scaling of total noise ; (sigma), replaced with info in Help file telling users that ; S/N is an optimistic estimate and should be scaled by ; 0.9-0.5x; also note this in the results window when a S/N ; value is returned. ; V1.3 - Seppo found inconsistency in calculated S/N values compared ; to old command line version of the routine (and by-hand ; calculations). These resulted from the ch or frametime ; values being reset to the defaults of 1 and 30 every time ; either one of these values was changed (i.e., if the user ; changed ch, then frametime would reset to the default, and ; vice-versa. Fixed this by implementing an explicit check for ; which LIST parameter had been changed, by splitting the ; relevant case block into twwo case blocks. Also, converted ; snirac_calc function to a subroutine and expanded common ; block1 so that intermediate information can be reported ; in the results window. Also fixed bug in snirac_calc that ; would cause background_sb to be scaled up by 1.5x in channels ; 3 and 4 every time the user recalculates without changing the ; entered parameter values. ; V1.4 - Added output of total noise (sigma) in results window. ; ;- ;*********************************************** ;*** DON'T FORGET TO UPDATE THE VERSION NUMBER ;*********************************************** pro snirac_event, ev resolve_routine, ['snirac', 'snirac_event', 'snirac_help', 'snirac_help_event', 'snirac_calc'], /no_recompile common block1, VERSION, ch, frametime, nexp, fd, background_sb, rin, rout, snout, nback, sigma_rn, e_s, e_bg, sigma_s, sigma_bg, sigma widget_control, ev.id, get_uvalue=uval if (n_elements(uval) eq 0) then uval = '' name = strmid(tag_names(ev, /structure_name), 7, 4) case (name) of 'BUTT' : begin if (uval eq 'DONE') then begin widget_control, /destroy, ev.top return endif if (uval eq 'HELP') then begin snirac_help return endif if (uval eq 'CALC') then begin if (fd lt 0.0 or background_sb lt 0.0) then begin out_text=' ' widget_control, widget_info(ev.top, find_by_uname='info_window'), set_value=out_text, /append out_text='Enter values for at least Target Flux Density and Background Surface Brightness' widget_control, widget_info(ev.top, find_by_uname='info_window'), set_value=out_text, /append out_text=' ' widget_control, widget_info(ev.top, find_by_uname='info_window'), set_value=out_text, /append return endif else begin ;***DEBUG print, ch, frametime, nexp, fd, background_sb, rin, rout snirac_calc out_text=' ' widget_control, widget_info(ev.top, find_by_uname='info_window'), set_value=out_text, /append out_text='Number of pixels in background annulus, nback = '+strtrim(string(nback, format='(f20.2)'),2) widget_control, widget_info(ev.top, find_by_uname='info_window'), set_value=out_text, /append out_text='Detector read noise in electrons, sigma_rn = '+strtrim(string(sigma_rn, format='(f20.2)'),2) widget_control, widget_info(ev.top, find_by_uname='info_window'), set_value=out_text, /append out_text='Source signal in electrons, e_s = '+strtrim(string(e_s, format='(f20.2)'),2) widget_control, widget_info(ev.top, find_by_uname='info_window'), set_value=out_text, /append out_text='Background signal in electrons, e_bg = '+strtrim(string(e_bg, format='(f20.2)'),2) widget_control, widget_info(ev.top, find_by_uname='info_window'), set_value=out_text, /append out_text='Poisson noise of the source + background in electrons, sigma_s = '+strtrim(string(sigma_s, format='(f20.2)'),2) widget_control, widget_info(ev.top, find_by_uname='info_window'), set_value=out_text, /append out_text='Poisson noise of the background alone in electrons, sigma_bg = '+strtrim(string(sigma_bg, format='(f20.2)'),2) widget_control, widget_info(ev.top, find_by_uname='info_window'), set_value=out_text, /append out_text='Total noise in electrons, sigma = '+strtrim(string(sigma, format='(f20.2)'),2) widget_control, widget_info(ev.top, find_by_uname='info_window'), set_value=out_text, /append out_text=' ' widget_control, widget_info(ev.top, find_by_uname='info_window'), set_value=out_text, /append out_text='**********************************************************************' widget_control, widget_info(ev.top, find_by_uname='info_window'), set_value=out_text, /append out_text='Optimistic S/N = '+snout+' (scale by 0.9-0.5x -- see Help for more info)' widget_control, widget_info(ev.top, find_by_uname='info_window'), set_value=out_text, /append out_text='**********************************************************************' widget_control, widget_info(ev.top, find_by_uname='info_window'), set_value=out_text, /append out_text=' ' widget_control, widget_info(ev.top, find_by_uname='info_window'), set_value=out_text, /append return endelse endif endcase 'TEXT' : begin widget_control, ev.id, get_value=val case uval of 'w_nexp' : begin nexp = float(val) out_text = 'Selected Total Number of Exposures: '+val endcase 'w_fd' : begin fd = float(val) out_text = 'Selected Target Flux Density: '+val+' micro-Jy' endcase 'w_background_sb' : begin background_sb = float(val) out_text = 'Selected Background Surface Brightness: '+val+' (MJy/sr)' endcase 'w_rin' : begin rin = float(val) out_text = 'Selected Inner Radius of Background Annulus: '+val+' pixels' endcase 'w_rout' : begin rout = float(val) out_text = 'Selected Outer Radius of Background Annulus: '+val+' pixels' endcase endcase endcase 'LIST' : begin uval=uval[ev.index] if (ch lt 0.0) then ch=1 if (frametime lt 0.0) then frametime=30.0 idx = where(uval eq [ '0.1 s', '2 s', '12 s', '30 s', '100 s' ], count) ;*** DEBUG print, ch, frametime, ' ', uval, count if (count le 0) then begin case uval of '1 (3.6 microns)' : begin ch=1 out_text = 'Selected Channel: '+uval end '2 (4.5 microns)' : begin ch=2 out_text = 'Selected Channel: '+uval end '3 (5.8 microns)' : begin ch=3 out_text = 'Selected Channel: '+uval end '4 (8.0 microns)' : begin ch=4 out_text = 'Selected Channel: '+uval end endcase endif else begin case uval of '0.1 s' : begin frametime=0.1 out_text = 'Selected Frame Time: '+uval end '2 s' : begin frametime=2.0 out_text = 'Selected Frame Time: '+uval end '12 s' : begin frametime=12.0 out_text = 'Selected Frame Time: '+uval end '30 s' : begin frametime=30.0 out_text = 'Selected Frame Time: '+uval end '100 s' : begin frametime=100.0 out_text = 'Selected Frame Time: '+uval end endcase endelse ;*** DEBUG print, ch, frametime, ' ', uval, count endcase else: endcase widget_control, widget_info(ev.top, find_by_uname='info_window'), set_value=out_text, /append end pro snirac_calc common block1 ; calculate number of pixels in background annulus nback = !pi * (rout^2.0 - rin^2.0) ; adjust channel number to array subscript ch_int = fix(ch) - 1 ; determine frametime index case 1 of frametime eq 0.1 : fr = 0 frametime eq 2. : fr = 1 frametime eq 12. : fr = 2 frametime eq 30. : fr = 3 frametime eq 100. : fr = 4 endcase ; noise pixels (table 6.1 in the SOM, v7.1) ; selected via npix[ch] npix = [7.0, 7.2, 10.8, 13.4] ; read noise in electrons for the desired frametime (table 6.3 in the SOM, v7.1) ; selected via sigma_readnoise[fr,ch] sigma_readnoise = [[16.9, 11.8, 9.4, 7.8, 8.4], $ [16.8, 12.1, 9.4, 7.5, 7.9], $ [ 9.0, 9.1, 8.8, 10.7, 13.1], $ [ 8.4, 7.1, 6.7, 6.9, 6.8]] ; detector read noise, electrons sigma_rn = sqrt( npix[ch_int] * sigma_readnoise[fr,ch_int]^2. ) ;***DEBUG print, 'Detector read noise in electrons, sigma_rn = ', strtrim(string(sigma_rn),2) ; convert micro-Jy to equivalent MJy/sr scale = 34.98 ; detector gain, electons/DN (Table 6.5 in the SOM, v7.1) ; selected via GAIN[ch_int] GAIN = [3.3, 3.7, 3.8, 3.8] ; convert from MJy/sr to DN/s (Table 5.1 in the IRAC Data Handbook, v3.0) ; selected via FLUXCONV[ch_int] FLUXCONV = [0.1088, 0.1388, 0.5952, 0.2021] ; signal of the source in electrons e_s = fd / scale * GAIN[ch_int] * frametime / FLUXCONV[ch_int] ;***DEBUG print, 'source signal in electrons, e_s = ', strtrim(string(e_s),2) ; ...but ths can be simplified to... ; convert flux density to electrons, electrons/micro-Jy*sec ; selected via fd_to_e[ch_int] fd_to_e = [0.867, 0.764, 0.182, 0.537] ; signal of the source in electrons e_s = fd * frametime * fd_to_e[ch_int] if (ch_int eq 2 or ch_int eq 3) then new_background_sb = background_sb * 1.5 else new_background_sb = background_sb ; scaling between surface brightness and electrons, electrons/(MJy/sr * sec) ; selected via sb_to_e[ch_int] sb_to_e = [30.331, 26.729, 6.384, 18.802] ; number of electrons contributed by the background e_bg = new_background_sb * frametime * sb_to_e[ch_int] ;***DEBUG print, 'background signal in electrons, e_bg = ', strtrim(string(e_bg),2) ; Poisson noise of the source + background sigma_s = sqrt( e_s + npix[ch_int] * e_bg ) ;***DEBUG print, 'Poisson noise of the source + background in electrons, sigma_s = ', strtrim(string(sigma_s),2) ; Poisson noise of the subtracted background alone sigma_bg = sqrt( e_bg / nback ) ;***DEBUG print, 'Poisson noise of the background alone in electrons, sigma_bg = ', strtrim(string(sigma_bg),2) ; scaling factor accounting for the noise in the annulus used to estimate the background to subtract ap_fac = npix[ch_int] ; any systematic/confusion error sigma_sys = 0.0 sigma = sqrt(sigma_rn^2. + sigma_s^2. + ap_fac*sigma_bg^2. + sigma_sys^2.) ;***DEBUG print, 'sigma = ', strtrim(string(sigma),2) sigma = sqrt(sigma_rn^2. + frametime*(fd*fd_to_e[ch_int] + npix[ch_int]*new_background_sb*sb_to_e[ch_int] + npix[ch_int]*new_background_sb*sb_to_e[ch_int] / nback)) ;***DEBUG print, 'sigma = ', strtrim(string(sigma),2) sn = sqrt(nexp) * fd * fd_to_e[ch_int] * frametime / sigma ;***DEBUG print, ' ' ;***DEBUG print, 'S/N = ', strtrim(string(sn,format='(f12.2)'),2) ;***DEBUG print, ' ' snout = strtrim(string(sn,format='(f12.2)'),2) end pro snirac_help_event, ev widget_control, ev.id, get_uvalue=uval if (n_elements(uval) eq 0) then uval = '' name = strmid(tag_names(ev, /structure_name), 7, 4) case (name) of 'BUTT' : begin if (uval eq 'DONE') then begin widget_control, /destroy, ev.top return endif endcase endcase end pro snirac_help common block1 helpbase = widget_base(title='Help Window - IRAC S/N Calculator v'+VERSION, row=2) widget_control, /managed, helpbase hb1 = widget_base(helpbase, /frame, /row) ht1 = widget_text(hb1, xsize=80, ysize=40, /scroll, /wrap, $ uname='help_window', $ value=[ 'IRAC S/N Calculator v'+VERSION, $ '', $ '', $ 'This tool calculates IRAC S/N based on the memo "Estimating Signal-To-Noise Ratio of a Point Source Measurement for IRAC" (issued by the SSC on 12 Feb 2007; see http://ssc.spitzer.caltech.edu/documents/irac_memo.txt - the memo is also reproduced below).', $ '', $ 'The following assumptions (based on those in the memo) were made during the calculation:', $ '', $ '1) Systematic and/or confusion error noise (sigma_sys) = 0.0.', $ '', $ '2) Photometry aperture has area larger than the number of noise pixels (see npix below).', $ '', $ '3) Background (background_sb) in channels 3 and 4 is scaled up by 1.5x to account for internal scattering in the arrays.', $ '', $ 'Note that the returned S/N value is an upper (i.e., optimistic) estimate. In practice, the calculated total noise (sigma) value should be scaled up by 10-50% to conservatively account for systematic effects (e.g., resulting from bright source photometry or confusion) and/or the importance of obtaining a high precision measurement. These factors are best evaluated by the observer; the S/N value reported by the Calculator should be correspondingly scaled down by a factor of 0.9-0.5x to account for these effects.', $ '', $ '', $ '-----------------------------------------------------------------------', $ 'Estimating Signal-To-Noise Ratio of a Point Source Measurement for IRAC', $ '-----------------------------------------------------------------------', $ '',$ 'When planning observations that need to measure the flux density or variations in the flux density of a point source to a certain precision, it is important to include the Poisson noise of the source in the estimation of the signal-to-noise ratio. The online sensitivity estimator, SENS-PET, that determines the detection signal-to-noise ratio (how significant a source is compared to the background) only includes the read noise of the instrument and the Poisson noise of the background in the calculation of the uncertainty. This memo discusses in detail how to estimate the uncertainty in a measurement of the source flux density for the benefit of observers who are interested in variations in source brightness (as in observations of extrasolar planet transits, etc).', $ '',$ 'The uncertainty in the measured flux density of a point source in a single exposure is', $ '',$ 'sigma^2 = sigma_rn^2 + sigma_s^2 + ap_fac * sigma_bg^2 + sigma_sys^2',$ '',$ 'where sigma_rn is the read noise of the detector, sigma_s is the Poisson noise of the source + background, sigma_bg is the Poisson noise of the subtracted background alone, ap_fac is a scaling factor accounting for the noise in the annulus used to estimate the background to subtract, and sigma_sys is any systematic/confusion error. In this memo, sigma is calculated for a single exposure and in the limit where sigma_sys = 0; therefore, sigma decreases as the square-root of the number of exposures.', $ '', $ 'For an optimal source extraction, the number of pixels contributing to the source is the noise pixels (npix) for that particular array (see table 6.1 in the IRAC chapter of the Spitzer Observers Manual, SOM). Then the read noise for the source is', $ '', $ 'sigma_rn^2 = npix * sigma_readnoise^2,', $ '', $ 'where sigma_readnoise is the read noise in electrons for the desired frametime (see Table 6.3 of the SOM). If a smaller aperture is used, then npix is the number of pixels in the aperture, but the flux density of the source should be scaled by the fraction of the source flux density enclosed by the aperture.', $ '', $ 'The term sigma_s^2 is the Poisson noise of the source plus the background in the aperture used. Then, sigma_s^2 = e_s + npix * e_bg, where e_s is the signal of the source in electrons and e_bg is the signal of the background per pixel in electrons.', $ '', $ 'Typically, the source flux density (fd) is given in micro-Jy. To convert to electrons detected, use', $ '', $ 'e_s = fd(micro-Jy) / scale * GAIN * FRAMETIME / FLUXCONV', $ '', $ 'where scale (34.98) converts from micro-Jy to equivalent MJy/sr, so that the the correct number of electrons are determined when using the FLUXCONV and GAIN values supplied; FLUXCONV converts from MJy/sr to DN/s; GAIN converts from DN to electrons; and, FRAMETIME is the frame time used. Note that GAIN, FRAMETIME and FLUXCONV can all be found in the headers of a BCD image. GAIN and FLUXCONV are constants for a given IRAC channel. FLUXCONV can also be found in Table 5.1 of the IRAC Data Handbook and GAIN is tabulated in Table 6.5 of the SOM.', $ '', $ 'Simplifying the expression,', $ '', $ 'e_s = fd * FRAMETIME * fd_to_e', $ '', $ 'where fd_to_e = 0.867, 0.764, 0.182, 0.537 electrons / (uJy * s) for channels 1-4, respectively.', $ '', $ 'Likewise, the number of electrons contributed by the background is given by', $ '', $ 'e_bg = background_sb * FRAMETIME * sb_to_e', $ '', $ 'where the background_sb is an estimate of the surface brightness of the background in MJy/sr (such as the estimate given by Spot) and sb_to_e is the scaling between surface brightness and electrons. Note that for channels 3 and 4, the Spot estimates of the background surface brightness should be scaled up by ~1.5, to account for extra background signal due to internal scattering in the arrays.', $ '', $ 'The term sb_to_e = 30.331, 26.729, 6.384, 18.802 electrons / (MJy/sr * s) for channels 1-4, respectively.', $ '', $ 'To be conservative, assume that the background subtraction used in either aperture photometry or PSF fitting contributes noise from the background summed over the number of noise pixels for the source. Then, ap_fac = npix and', $ '', $ 'sigma_bg^2 = e_bg / nback = background_sb * FRAMETIME * sb_to_e / nback', $ '', $ 'where nback is the number of pixels used in a reasonably-sized background annulus.', $ '', $ 'A reasonable estimate of the noise (in electron units) in a single exposure is then', $ '', $ 'sigma^2 = sigma_rn^2 + FRAMETIME * (fd * fd_to_e + npix * background_sb * sb_to_e + npix * background_sb * sb_to_e / nback).', $ '', $ 'To be conservative, scale the noise upward by 30%-50% to account for systematics, etc.', $ '', $ 'The signal-to-noise ratio for one exposure is simply', $ '', $ 'fd * fd_to_e * FRAMETIME / sigma.', $ '' ]) hb4 = widget_base(helpbase, /frame, /row, space=30) ht1 = widget_button(hb4, value='Dismiss', uvalue='DONE') widget_control, helpbase, /realize xmanager, 'SNIRAC_HELP', helpbase, /no_block end pro snirac resolve_routine, ['snirac', 'snirac_event', 'snirac_help', 'snirac_help_event', 'snirac_calc'], /no_recompile common block1 ;*********************************************** ;*** DON'T FORGET TO UPDATE THE VERSION NUMBER VERSION = '1.0' ; original code VERSION = '1.1' ; fixes for bugs found by Seppo and Sean VERSION = '1.2' ; removed 1.5x scaling of total noise, requested by Sean VERSION = '1.3' ; fix for bug found by Seppo VERSION = '1.4' ; added output of sigma in results window ;*********************************************** ;*** Set default values fd=-999.999 background_sb=-999.999 ch=1 frametime=30. nexp=1. rin = 10. rout = 20. swin = !d.window ;Previous window base = widget_base(title='IRAC S/N Calculator v'+VERSION, row=3) widget_control, /managed, base b0 = widget_base(base, /frame, column=1) widget_control, base, set_uvalue=t1 b1 = widget_base(base, /frame, /row) t1 = widget_text(b1, xsize=80, ysize=20, /scroll, uname='info_window', $ value=[ 'IRAC S/N Calculator v'+VERSION, $ '', $ '', $ 'Default IRAC Channel: 1 (3.6 microns)', $ 'Default IRAC Frame Time: 30 s', $ 'Default Total Number of Exposures: 1', $ 'Default Inner Radius of Background Aperture: 10 pixels', $ 'Default Inner Radius of Background Aperture: 20 pixels', $ '' ]) b2 = widget_base(base, /frame, /column, space=30) t2 = widget_base(b2, /column) t1 = widget_label(t2, value='IRAC Channel') ch_list = [ '1 (3.6 microns)', '2 (4.5 microns)', '3 (5.8 microns)', '4 (8.0 microns)' ] t1 = widget_list(t2, ysize=4, uvalue=ch_list, value=ch_list, tab_mode=1) t1 = widget_label(t2, value='IRAC Frame Time') frametime_list = [ '0.1 s', '2 s', '12 s', '30 s', '100 s' ] t1 = widget_list(t2, ysize=5, uvalue=frametime_list, value=frametime_list, tab_mode=1) b3 = widget_base(base, /frame, /column, space=30) t2 = widget_base(b3, /row) t1 = widget_label(t2, value = 'Total Number of Exposures:') t1 = widget_text(t2, /editable, /all_events, xsize=10, ysize=1, value='1', uvalue='w_nexp') t2 = widget_base(b3, /row) t1 = widget_label(t2, value = 'Target Flux Density (micro-Jy):') t1 = widget_text(t2, /editable, /all_events, xsize=10, ysize=1, uvalue='w_fd') t2 = widget_base(b3, /row) t1 = widget_label(t2, value = 'Background Surface Brightness (MJy/sr):') t1 = widget_text(t2, /editable, /all_events, xsize=10, ysize=1, uvalue='w_background_sb') t2 = widget_base(b3, /row) t1 = widget_label(t2, value = 'Inner Radius of Background Annulus (pixels):') t1 = widget_text(t2, /editable, /all_events, xsize=10, ysize=1, value='10', uvalue='w_rin') t2 = widget_base(b3, /row) t1 = widget_label(t2, value = 'Outer Radius of Background Annulus (pixels):') t1 = widget_text(t2, /editable, /all_events, xsize=10, ysize=1, value='20', uvalue='w_rout') b4 = widget_base(base, /frame, /row, space=30) t1 = widget_button(b4, value='Calculate', uvalue='CALC') t1 = widget_button(b4, value='Help', uvalue='HELP') t1 = widget_button(b4, value='Exit', uvalue='DONE') widget_control, base, /realize xmanager, 'SNIRAC', base, /no_block end