;+ ; Procedure getbs retrieves and calibrates a beam switched Nod scan pair. ;

; Beam switched modes are not recommended on the GBT. Total power Nod ; should be used instead. To process total power nod data use getnod ; instead of this routine. This procedure is provided only to ; accommodate old data. ;

; The getbs routine produces a spectrum calibrated in Ta (K). Other ; recognized unit are Ta* and Jy. ; ;

Summary ;

;

Parameters ; ;

The scan number is required. Either scan in the "Nod" ; scan pair can be given. Arguments to identify the IF number, ; polarization number, and feed number are optional. The default ; tracking feed number, trackfdnum, (0) is the lowest numbered FEED ; found in the data. This feed number is interpreted as the tracking ; (source) feed for the first scan in the signal phase. Specify ; trackfdnum=1 to identify the second FEED as the tracking ; feed. Tracking feed in this context means that the source signal was ; in that beam during the source phase of the first scan of the two ; "Nod" scans. ;

;

If ifnum, trackfdnum, or plnum are not supplied then the lowest ; values for each of those where data exists (all combinations may not ; have data) will be used, after using any user-supplied values. The ; value of ifnum is determined first, followed by trackfdnum and ; finally plnum. If a combination with data can not be found then ; showiftab is used to show the user what ; the set of valid combinations are. The summary line includes the ; ifnum, trackfdnum, and plnum used. ;

; Tsys and Available Units ;

; The procedure calculates Tsys based on the Tcal values and the data ; in the non-source feeds (both scans). The user can override this ; calculation by entering a zenith system temperature. The procedure ; will then correct the user-supplied Tsys for the observed elevation. ; If the data are calibrated to Ta* or Jy, additional parameters ; are used. A zenith opacity (tau) may be specified, and an aperture ; efficiency may be specified. The user is strongly encouraged to ; enter values for these calibration parameters, but they will be ; estimated if none are provided. The user can also supply a mean Tcal ; using the tcal keyword. That will override the tcal found in the data. ;

; Smoothing the Reference Spectra ;

; A parameter called smthoff can be used to smooth the reference ; spectra prior to calibration. In certain cases this can improve ; the signal to noise ratio, but it may degrade baseline shapes and ; artificially emphasize spectrometer glitches. Use with care. A ; value of smthoff=16 is often a good choice. ;

; The Special "bswitch" Keyword ;

; A parameter called bswitch allows the user to select between using ; all of the data (the detault, bswitch=0) or using only data when the ; beam switch is in the "cross" (reference) or "thru" (signal) ; position. Data taken in beam switched mode should be examined ; carefully in both cross and thru positions. ;

; Weighting of Integrations in Scan Average ;

; By default, internal averaging of integrations is weighted using ; tsys, exposure and frequency_resolution as described in the ; dcaccum documentation. ; To give all integrations equal weight instead of the default ; weighting based on Tsys, use the /eqweight keyword. ;

; Summary Information ;

; The scan number printed in the status line is that of the first ; scan in the "Nod" pair - no matter which scan number you actually ; provided as the scan parameter here. The values of ifnum, ; trackfdnum, and plnum are shown after the scan number. The first ; Tsys printed is the tsys value in the result. This is a weighted ; average of the Tsys values in the reference beams from both scans as ; described in donod The other 4 Tsys values ; printed on the status line are the 4 Tsys values calculated by donod ; and averaged (using the appropriate weighting scheme) over all ; integrations and beam switch positions. ;

; Using or Ignoring Flags ;

; Flags (set via flag) can be selectively ; applied or ignored using the useflag and skipflag keywords. Only ; one of those two keywords can be used at a time (it is an error to ; use both at the same time). Both can be either a boolean (/useflag ; or /skipflag) or an array of strings. The default is /useflag, ; meaning that all flag rules that have been previously set are ; applied when the data is fetched from disk, blanking any data as ; described by each rule. If /skipflag is set, then all of the flag ; rules associated with this data are ignored and no data will be ; blanked when fetched from disk (it may still contain blanked values ; if the actual values in the disk file have already been blanked by ; some other process). If useflag is a string or array of strings, ; then only those flag rules having the same idstring value are used ; to blank the data. If skipflag is a string or array of strings, ; then all flag rules except those with the same idstring value are ; used to blank the data. ;

; Dealing With Duplicate Scan Numbers ;

; There are 3 ways to attempt to resolve ambiguities when the ; same scan number appears in the data source. The instance keyword ; refers to the element of the returned array of scan_info structures ; that scan_info returns. So, if scan 23 ; appears 3 times then instance=1 refers to the second time that scan ; 23 appears as returned by scan_info. The file keyword is useful if ; a scan is unique to a specific file and multiple files have been ; accessed using dirin. If file is specified and instance is also ; specified, then instance refers to the instance of that scan just ; within that file (which may be different from its instance within ; all opened files when dirin is used). The timestamp keyword is ; another way to resolve ambiguous scan numbers. The timestamp here ; is a string used essentially as a label by the monitor and control ; system and is unique to each scan. The format of the timestamp ; string is "YYYY_MM_DD_HH:MM:SS". When timstamp is given, scan and ; instance are ignored. If more than one match is found, an error is ; printed and this procedure will not continue. ; ;

Once a unique match is found to the desired scan (using instance, ; file, or timestamp) then the scan paired with that scan necessary to ; finish this procedure is found. The match must be found within the ; same file as the desired scan. It must have the appropriate matching ; scan number (scan-1 if scan is the second scan in the procedure or ; scan+1 if scan is the first scan in the procedure). If those two ; rules are not sufficient to find a unique match, the matching ; scan with the closest timestamp in the appropriate direction (before ; or after depending on which procseqn is associate with scan) is used. ; Finally, the matched pair must have the appropriate procseqn given the ; procseqn that scan is. ; ;

; Note: if you see the message "No data found, can not continue" the ; most likely explanation is that the IF numbers are confused, ; probably due to a bad configuration (e.g. both feeds do not have ; data from the same IF and polarization). Consequently, this calibration ; routine can not reduce that data. It is likely that all of the ; IFNUM values for this data are -1. ; ; @param scan {in}{required}{type=integer} M&C scan number ; @keyword ifnum {in}{optional}{type=integer} IF number ; (starting with 0). Defaults to the lowest value associated with data ; taking into account any user-supplied values for trackfdnum, and plnum. ; @keyword intnum {in}{optional}{type=integer} Integration number, ; defaults to all integrations. ; @keyword plnum {in}{optional}{type=integer} Polarization number ; (starting with 0). Defaults to the lowest value with data after ; determining the values of ifnum and trackfdnum if not supplied by the ; user. ; @keyword sampler {in}{optional}{type=string} sampler name, this is ; an alternative way to specify ifnum and plnum. When sampler ; name is given, ifnum and plnum must not be given. Note that data ; from the associated switched sampler will also be used. ; @keyword trackfdnum {in}{optional}{type=integer} Tracking feed ; number. Defaults to the lowest value with data after determining ; the value of ifnum if not supplied by the user and using any value ; of plnum supplied by the user. ; @keyword bswitch {in}{optional}{type=integer} determines which ; beamswitch positions are used. 0=both, 1=ref, 2=sig (default 0) ; @keyword tau {in}{optional}{type=float} tau at zenith, if not ; supplied, it is estimated using get_tau ; tau is only used when the requested units are other than the default ; of Ta and when a user-supplied tsys value at zenith is to be used. ; @keyword tsys {in}{optional}{type=float} Tsys at zenith, this is ; converted to a Tsys at the observed elevation. If not suppled, the ; Tsys for each integration is calculated as described elsewhere. ; @keyword ap_eff {in}{optional}{type=float} Aperture efficiency, if ; not suppled, it is estimated using get_ap_eff ; ap_eff is only used when the requested units are Jy. ; @keyword smthoff {in}{optional}{type=integer} smooth factor for ; reference spectrum, defaults to 1 (no smoothing). ; @keyword units {in}{optional}{type=string} takes the value 'Jy', ; 'Ta', or 'Ta*', default 'Ta'. ; @keyword tcal {in}{optional}{type=float} Cal temperature (K) to use ; in the Tsys calculation. If not supplied, the mean_tcal value from ; the header of the cal_off switching phase data in each integration ; is used. This must be a scalar, vector tcal is not yet supported. ; The resulting data container will have it's mean_tcal header value ; set to this keyword when it is set by the user. ; @keyword eqweight {in}{optional}{type=boolean} When set, all integrations ; are averaged with equal weight (1.0). Default is unset. ; @keyword quiet {in}{optional}{type=boolean} When set, the normal ; status message on successful completion is not printed. This will ; not have any effect on error messages. Default is unset. ; @keyword keepints {in}{optional}{type=boolean} When set, the ; individual integrations are saved to the current output file ; (fileout). This is ignored if a specific integration is requested ; using the intnum keyword. Default is unset. ; @keyword useflag {in}{optional}{type=boolean or string} ; Apply all or just some of the flag rules? Default is set. ; @keyword skipflag {in}{optional}{type=boolean or string} Do not apply ; any or do not apply a few of the flag rules? Default is unset. ; @keyword instance {in}{optional}{type=integer} Which occurrence ; of this scan should be used. Default is 0. ; @keyword file {in}{optional}{type=string} When specified, limit the search ; for this scan (and instance) to this specific file. Default is all ; files currently opened. ; @keyword timestamp {in}{optional}{type=string} The M&C timestamp associated ; with the desired scan. When supplied, scan and instance are ignored. ; @keyword status {out}{optional}{type=integer} An output parameter to indicate ; whether the procedure finished as expected. A value of 1 means there were ; no problems, a value of -1 means there were problems with the ; arguments before any data was processed, and a value of 0 means that ; some of the individual integrations were processed (and possible ; saved to the output file if keepints was set) but there was a ; problem with the final average and buffer 0 likely ; contains just the result from the last integration processed. This ; keyword is primarily of use when using getbs within another ; procedure or function. ; ; @examples ;

;    ; average both polarizations from ifnum=1
;    sclear
;    getbs, 76, ifnum=1, plnum=0
;    accum
;    getbs, 76, ifnum=1, plnum=1
;    accum
;    ave
; 
; ; @uses accumave ; @uses accumclear ; @uses calsummary ; @uses check_calib_args ; @uses data_free ; @uses dcaccum ; @uses dcscale ; @uses dcsetunits ; @uses donod ; @uses find_paired_info ; @uses find_scan_info ; @uses get_calib_data ; @uses set_data_container ; @uses showiftab ; ; @version $Id$ ;- pro getbs,scan,ifnum=ifnum,intnum=intnum,plnum=plnum,trackfdnum=trackfdnum,sampler=sampler,bswitch=bswitch,tau=tau,$ tsys=tsys,ap_eff=ap_eff,smthoff=smthoff,units=units,eqweight=eqweight, $ tcal=tcal,quiet=quiet, keepints=keepints, useflag=useflag, skipflag=skipflag, $ instance=instance, file=file, timestamp=timestamp, status=status compile_opt idl2 status = -1 ; basic argument checks argsOK=check_calib_args(scan,ifnum=ifnum,intnum=intnum,plnum=plnum,fdnum=trackfdnum, $ sampler=sampler,eqweight=eqweight,units=units,bswitch=bswitch,$ quiet=quiet,keepints=keepints,useflag=useflag, $ skipflag=skipflag,instance=instance,file=file,$ timestamp=timestamp,tau=tau,ap_eff=ap_eff,/twofeeds,ret=ret, info=info) if not argsOK then return scan1Info = info if size(scan1Info,/type) ne 8 then return ; check for appropriate info in the scan found ; it must be 'Nod' if scan1Info.procedure ne 'Nod' then begin message,"Cannot handle this scan: Procedure = " + strcompress(scan1Info.procedure,/remove_all),/info return end ; it must be one of a pair of scans if scan1Info.procseqn gt 2 then begin sProcseqn = strcompress(string(scan1Info.procseqn),/remove_all) message,"More than two scans in this procedure - can not continue. At least : " + sProcseqn, /info return endif ; locate the paired scan scan2Info = find_paired_info(scan1Info) if size(scan2Info,/type) ne 8 then begin message,'The other scan for this beam-switched Nod procedure can not be found.',/info return endif ; make sure we have them in the right order if (scan2Info.scan lt scan1Info.scan) then begin tmp = scan2Info scan2Info = scan1Info scan1Info = tmp endif ; each scan must have 4 switching states, which must ; be 2 CAL states and 2 sig states if scan1Info.n_switching_states ne 4 then begin message,"This does not appeat to be beam-switched Nod data - must have 4 switching states in each scan.",/info return endif if scan1Info.n_cal_states ne 2 then begin message,"This does not appear to be beam-switched Nod data - must have 2 cal states in each scan.",/info return endif if scan1Info.n_sig_states ne 2 then begin message,"This does not appear to be beam-switched Nod data - must have 2 sig states in each scan.",/info return endif ; get the requested data scan1Data = get_calib_data(scan1Info, ret.ifnum, ret.plnum, ret.fdnum, ret.sampler, scan1count, $ intnum=intnum, useflag=useflag,skipflag=skipflag, /twofeeds) if scan1count le 0 then begin message,'No data found for the first scan in this pair, can not continue',/info return endif scan2Data = get_calib_data(scan2Info, ret.ifnum, ret.plnum, ret.fdnum, ret.sampler, scan2count, $ intnum=intnum, useflag=useflag, skipflag=skipflag, /twofeeds) if scan2count le 0 then begin message,'No data found for the second scan in this pair, can not continue',/info data_free,scan1Data return endif ; from this point on, sigdata and refdata contain data containers ; that must be freed whenever this routine returns to avoid memory leaks. ; find the 16 types of spectra feed1 = scan1Info.feeds[ret.fdnum] feed2 = scan1Info.feeds[(ret.fdnum eq 0) ? 1:0] s1_b1_ref_off = where(scan1Data.feed eq feed1 and scan1Data.cal_state eq 0 and scan1Data.sig_state eq 0 ,s1_b1_ref_off_count) s1_b1_ref_on = where(scan1Data.feed eq feed1 and scan1Data.cal_state eq 1 and scan1Data.sig_state eq 0 ,s1_b1_ref_on_count) s2_b1_ref_off = where(scan2Data.feed eq feed1 and scan2Data.cal_state eq 0 and scan2Data.sig_state eq 0 ,s2_b1_ref_off_count) s2_b1_ref_on = where(scan2Data.feed eq feed1 and scan2Data.cal_state eq 1 and scan2Data.sig_state eq 0 ,s2_b1_ref_on_count) s1_b2_ref_off = where(scan1Data.feed eq feed2 and scan1Data.cal_state eq 0 and scan1Data.sig_state eq 0 ,s1_b2_ref_off_count) s1_b2_ref_on = where(scan1Data.feed eq feed2 and scan1Data.cal_state eq 1 and scan1Data.sig_state eq 0 ,s1_b2_ref_on_count) s2_b2_ref_off = where(scan2Data.feed eq feed2 and scan2Data.cal_state eq 0 and scan2Data.sig_state eq 0 ,s2_b2_ref_off_count) s2_b2_ref_on = where(scan2Data.feed eq feed2 and scan2Data.cal_state eq 1 and scan2Data.sig_state eq 0 ,s2_b2_ref_on_count) s1_b1_sig_off = where(scan1Data.feed eq feed1 and scan1Data.cal_state eq 0 and scan1Data.sig_state eq 1 ,s1_b1_sig_off_count) s1_b1_sig_on = where(scan1Data.feed eq feed1 and scan1Data.cal_state eq 1 and scan1Data.sig_state eq 1 ,s1_b1_sig_on_count) s2_b1_sig_off = where(scan2Data.feed eq feed1 and scan2Data.cal_state eq 0 and scan2Data.sig_state eq 1 ,s2_b1_sig_off_count) s2_b1_sig_on = where(scan2Data.feed eq feed1 and scan2Data.cal_state eq 1 and scan2Data.sig_state eq 1 ,s2_b1_sig_on_count) s1_b2_sig_off = where(scan1Data.feed eq feed2 and scan1Data.cal_state eq 0 and scan1Data.sig_state eq 1 ,s1_b2_sig_off_count) s1_b2_sig_on = where(scan1Data.feed eq feed2 and scan1Data.cal_state eq 1 and scan1Data.sig_state eq 1 ,s1_b2_sig_on_count) s2_b2_sig_off = where(scan2Data.feed eq feed2 and scan2Data.cal_state eq 0 and scan2Data.sig_state eq 1 ,s2_b2_sig_off_count) s2_b2_sig_on = where(scan2Data.feed eq feed2 and scan2Data.cal_state eq 1 and scan2Data.sig_state eq 1 ,s2_b2_sig_on_count) ; final sanity checks ; Each type of data container must have the same count and that ; must be equal to the number of integrations to be processed if n_elements(intnum) eq 1 then begin expectedCount = 1 endif else begin ; use nIntegrations appropriate to the given sampler ; must be the same between scans sampIndx = (where(scan1Info.samplers eq scan1Data[0].sampler_name))[0] nIntScan1 = scan1Info.n_sampints[sampIndx] sampIndx = (where(scan2Info.samplers eq scan2Data[0].sampler_name))[0] nIntScan2 = scan2Info.n_sampints[sampIndx] ; number of integrations must be the same in both scans if nIntScan1 ne nIntScan2 then begin message,"The two scans have different number of integrations for this plnum and ifnum (sampler)",/info data_free, data return endif expectedCount = nIntScan1 endelse if (s1_b1_ref_off_count ne expectedCount or $ s1_b1_ref_off_count ne s1_b1_ref_on_count or s1_b1_ref_off_count ne s2_b1_ref_off_count or s1_b1_ref_off_count ne s2_b1_ref_on_count or $ s1_b1_ref_off_count ne s1_b2_ref_on_count or s1_b1_ref_off_count ne s2_b2_ref_off_count or s1_b1_ref_off_count ne s2_b2_ref_on_count or $ s1_b1_ref_off_count ne s1_b2_ref_off_count or $ s1_b1_sig_off_count ne expectedCount or $ s1_b1_sig_off_count ne s1_b1_sig_on_count or s1_b1_sig_off_count ne s2_b1_sig_off_count or s1_b1_sig_off_count ne s2_b1_sig_on_count or $ s1_b1_sig_off_count ne s1_b2_sig_on_count or s1_b1_sig_off_count ne s2_b2_sig_off_count or s1_b1_sig_off_count ne s2_b2_sig_on_count or $ s1_b1_sig_off_count ne s1_b2_sig_off_count) then begin message,"Unexpected number of spectra retrieved for some or all of the switching phases, can not continue.",/info data_free, data return endif status = 0 missing = 0 missingBeam = 0 if keyword_set(eqweight) then weight = 1.0 ; else undefined and use default weight thisaccum = {accum_struct} tauInts = fltarr(expectedCount) apEffInts = tauInts ntsysPerInt = (ret.bswitch eq 0) ? 8:4 tsysInts = fltarr(expectedCount,ntsysPerInt) b1count = 0 b2count = 0 lastcount = 0 for i = 0,(expectedCount-1) do begin if (ret.bswitch eq 0 or ret.bswitch eq 1) then begin donod,result,$ scan1Data[s1_b1_ref_off[i]],scan1Data[s1_b1_ref_on[i]],scan2Data[s2_b1_ref_off[i]],scan2Data[s2_b1_ref_on[i]],$ scan1Data[s1_b2_ref_off[i]],scan1Data[s1_b2_ref_on[i]],scan2Data[s2_b2_ref_off[i]],scan2Data[s2_b2_ref_on[i]],$ smthoff,ret_tsys,tsys=tsys,tau=tau,tcal=tcal,eqweight=eqweight,single_beam=single_beam ; invert so that any signal is positive dcscale, result, -1.0 ; convert to the desired units dcsetunits,result,units,tau=tau,ap_eff=ap_eff,ret_tau=ret_tau,ret_ap_eff=ret_ap_eff ; these are only used in the status line at the end tauInts = ret_tau apEffInts = ret_ap_eff tsysInts[i,0:3] = ret_tsys dcaccum,thisaccum,result,weight=weight if thisaccum.n gt lastcount then b1count += 1 lastcount = thisaccum.n missingBeam += keyword_set(single_beam) if keyword_set(keepints) then begin ; re-use raw data containers to conserve space ; defer the actual keep until later ; takes 3 steps because of the nature of IDL ; data passing (value vs reference) tmp = scan1Data[s1_b1_ref_off[i]] data_copy, result, tmp scan1Data[s1_b1_ref_off[i]] = tmp endif endif if (ret.bswitch eq 0 or ret.bswitch eq 2) then begin donod,result,$ scan1Data[s1_b1_sig_off[i]],scan1Data[s1_b1_sig_on[i]],scan2Data[s2_b1_sig_off[i]],scan2Data[s2_b1_sig_on[i]],$ scan1Data[s1_b2_sig_off[i]],scan1Data[s1_b2_sig_on[i]],scan2Data[s2_b2_sig_off[i]],scan2Data[s2_b2_sig_on[i]],$ smthoff,ret_tsys,tsys=tsys,tau=tau,tcal=tcal,eqweight=eqweight,single_beam=single_beam ; convert to the desired units dcsetunits,result,units,tau=tau,ap_eff=ap_eff,ret_tau=ret_tau,ret_ap_eff=ret_ap_eff ; these are only used in the status line at the end tauInts = ret_tau apEffInts = ret_ap_eff if ret.bswitch eq 0 then begin tsysInts[i,4:7] = ret_tsys endif else begin tsysInts[i,0:3] = ret_tsys endelse dcaccum,thisaccum,result,weight=weight if thisaccum.n gt lastcount then b2count += 1 lastcount = thisaccum.n missingBeam += keyword_set(single_beam) if keyword_set(keepints) then begin ; re-use raw data containers to conserve space ; defer the actual keep until later ; takes 3 steps because of the nature of IDL ; data passing (value vs reference) tmp = scan1Data[s1_b1_sig_off[i]] data_copy, result, tmp scan1Data[s1_b1_sig_off[i]] = tmp endif endif endfor if keyword_set(keepints) then begin if ret.bswitch eq 0 or ret.bswitch eq 1 then putchunk,scan1Data[s1_b1_ref_off] if ret.bswitch eq 0 or ret.bswitch eq 2 then putchunk,scan1Data[s1_b1_sig_off] endif naccum1 = thisaccum.n if naccum1 le 0 then begin message,'Result is all blanked - probably both beams in all integrations were flagged',/info ; this can only happen if result is all blanked set_data_container,result ; clean up accumclear, thisaccum data_free, scan1Data data_free, scan2Data data_free,result return endif accumave,thisaccum, result, /quiet if ret.bswitch eq 0 or ret.bswitch eq 1 then missing = b1count ne expectedCount if ret.bswitch eq 0 or ret.bswitch eq 2 then missing = missing or b2count ne expectedCount if ret.bswitch eq 2 then missingBeam = missingBeam / 2 ; symetric accumclear, thisaccum set_data_container,result status = 1 if not keyword_set(quiet) then begin if missing then begin nmissRef = expectedCount-b1count nmissSig = expectedCount-b2count if ret.bswitch eq 1 then nmissSig = 0 if ret.bswitch eq 2 then nmissRef = 0 endif calsummary, scan1Info.scan, result.tsys, result.units, $ tsysInts=tsysInts, tauInts=tauInts, apEffInts=apEffInts, $ missingInts=nmissSig, missingRefInts=nmissRef,$ missingBeam=missingBeam, eqweight=eqweight, bswitch=ret.bswitch, $ ifnum=ret.ifnum,plnum=ret.plnum,fdnum=ret.fdnum endif data_free, scan1Data data_free, scan2Data data_free, result end