casa
$Rev:20696$
|
00001 #!/usr/bin/python -u 00002 # 00003 # bryan butler 00004 # nrao 00005 # spring 2012 00006 # 00007 # python functions to return expected flux density from solar system 00008 # bodies. the flux density depends on the geometry (distance, size of 00009 # body, subearth latitude), and on the model brightness temperature. 00010 # uncertainties on the flux density can also be returned, but are all 00011 # set to 0.0 for now, because i don't have uncertainties on the model 00012 # brightness temperatures. 00013 # 00014 # the model brightness temperatures for the various bodies are taken 00015 # from a combination of modern models and historical observations. see 00016 # the written description for a more complete description of the model 00017 # for each body. 00018 # 00019 # for all of the bodies but Mars, the model is contained in a text file 00020 # that has tabulated brightness temperature as a function of frequency. 00021 # for Mars it is also a function of time. eventually, the full-up 00022 # model calculations should be in the code (for those bodies that have 00023 # proper models) but for now, just live with the tabulated versions. 00024 # 00025 # version 1.1 00026 # last edited: 2012Oct03 00027 # 00028 00029 from numpy import searchsorted 00030 from scipy import array 00031 from scipy.interpolate import interp1d 00032 from math import exp, pi, cos, sin, isnan, sqrt 00033 #from os import environ, listdir 00034 import os 00035 from taskinit import gentools 00036 (tb,me)=gentools(['tb','me']) 00037 00038 def solar_system_fd (source_name, MJDs, frequencies, observatory, casalog=None): 00039 ''' 00040 find flux density for solar system bodies: 00041 Venus - Butler et al. 2001 00042 Mars - Butler et al. 2012 00043 Jupiter - Orton et al. 2012 00044 Uranus - Orton & Hofstadter 2012 (modified ESA4) 00045 Neptune - Orton & Hofstadter 2012 (modified ESA3) 00046 Io - Butler et al. 2012 00047 Europa - Butler et al. 2012 00048 Ganymede - Butler et al. 2012 00049 Titan - Gurwell et al. 2012 00050 Callisto - Butler et al. 2012 00051 Ceres - Keihm et al. 2012 00052 Juno - ? 00053 Pallas - Keihm et al. 2012 00054 Vesta - Keihm et al. 2012 00055 Hygeia - Keihm et al. 2012 00056 00057 inputs: 00058 source_name = source name string. example: "Venus" 00059 MJDs = list of MJD times (day + fraction). example: 00060 [ 56018.232, 56018.273 ] 00061 must be sorted in ascending order. 00062 frequencies = list of [start, stop] frequencies for 00063 which to calculate the integrated model. 00064 example: 00065 [ [ 224.234567e9, 224.236567e9 ], 00066 [ 224.236567e9, 224.238567e9 ] ] 00067 observatory = observatory name string. example: "ALMA" 00068 00069 returned is a list, first element is the return status: 00070 0 -> success 00071 1 -> Error: unsupported body 00072 2 -> Error: unsupported frequency or time for body 00073 3 -> Error: Tb model file not found 00074 4 -> Error: ephemeris table not found, or time out of range 00075 (note - the case where the MJD times span two ephemeris 00076 files is not supported) 00077 5 -> Error: unknown observatory 00078 second element is a list of flux densities, one per time and 00079 frequency range, frequency changes fastest. 00080 third element is list of uncertainties (if known; 0 if unknown), 00081 one per time and frequency range, frequency changes fastest. 00082 fourth element is a list of major axis, minor axis, and PAs in 00083 asec and deg, one per MJD time. 00084 fifth element is a list of CASA directions, one per MJD time. 00085 00086 bjb 00087 nrao 00088 spring/summer/fall 2012 00089 ''' 00090 00091 RAD2ASEC = 2.0626480624710e5 00092 AU = 1.4959787066e11 00093 SUPPORTED_BODIES = [ 'Venus', 'Mars', 'Jupiter', 'Uranus', 'Neptune', 00094 'Io', 'Europa', 'Ganymede', 'Callisto', 'Titan', 00095 'Ceres', 'Juno', 'Pallas', 'Vesta', 'Hygeia' ] 00096 00097 capitalized_source_name = source_name.capitalize() 00098 statuses = [] 00099 fds = [] 00100 dfds = [] 00101 Rhats = [] 00102 directions = [] 00103 00104 # 00105 # check that body is supported 00106 # 00107 if (not capitalized_source_name in SUPPORTED_BODIES): 00108 for MJD in MJDs: 00109 estatuses = [] 00110 efds = [] 00111 edfds = [] 00112 for frequency in frequencies: 00113 estatuses.append(1) 00114 efds.append(0) 00115 edfds.append(0) 00116 statuses.append(estatuses) 00117 fds.append(efds) 00118 dfds.append(edfds) 00119 Rhats.append([0.0, 0.0, 0.0]) 00120 directions.append(me.direction('J2000',0.0,0.0)) 00121 return [ statuses, fds, dfds, Rhats, directions ] 00122 00123 # 00124 # check that observatory is known 00125 # 00126 if not observatory in me.obslist(): 00127 for MJD in MJDs: 00128 estatuses = [] 00129 efds = [] 00130 edfds = [] 00131 for frequency in frequencies: 00132 estatuses.append(5) 00133 efds.append(0) 00134 edfds.append(0) 00135 statuses.append(estatuses) 00136 fds.append(efds) 00137 dfds.append(edfds) 00138 Rhats.append([0.0, 0.0, 0.0]) 00139 directions.append(me.direction('J2000',0.0,0.0)) 00140 return [ statuses, fds, dfds, Rhats, directions ] 00141 00142 # 00143 # before calculating the models be sure that we have the ephemeris 00144 # information. otherwise don't waste our time calculating the model. 00145 # only really important for mars, but do it for them all. 00146 # 00147 ephemeris_path = os.environ['CASAPATH'].split()[0]+'/data/ephemerides/JPL-Horizons/' 00148 # 00149 # for testing only... 00150 # 00151 # ephemeris_path = '/home/rishi/ttsutsum/casatest/fluxCal/ephem/' 00152 file_list = os.listdir(ephemeris_path) 00153 files = [] 00154 for efile in file_list: 00155 if (efile.split('_')[0] == capitalized_source_name and 'J2000' in efile): 00156 files.append(efile) 00157 file_OK = 0 00158 # 00159 # ephemeris tables have the following keywords: 00160 # GeoDist, GeoLat, GeoLong, MJD0, NAME, VS_CREATE, VS_DATE, VS_TYPE, 00161 # VS_VERSION, dMJD, earliest, latest, meanrad, obsloc, radii, rot_per 00162 # and columns: 00163 # MJD, RA, DEC, Rho (geodist), RadVel, NP_ang, NP_dist, DiskLong (Ob-long), 00164 # DiskLat(Ob-lat), Sl_lon, Sl_lat, r (heliodist), rdot, phang 00165 # Note by TT: 00166 # The column names, Obs_lon and Obs_lat have been changed to DiskLong and 00167 # DiskLat respectively to be consistent with what column names assumed for 00168 # ephemeris tables by casacore's MeasComet class. 00169 00170 for efile in files: 00171 ephemeris_file = ephemeris_path + efile 00172 tb.open(ephemeris_file) 00173 table_source_name = tb.getkeyword('NAME').capitalize() 00174 if (table_source_name != capitalized_source_name): 00175 continue 00176 first_time = tb.getkeyword('earliest')['m0']['value'] 00177 last_time = tb.getkeyword('latest')['m0']['value'] 00178 if (first_time < MJDs[0] and last_time > MJDs[-1]): 00179 file_OK = 1 00180 break 00181 tb.close() 00182 # 00183 # if we didn't find an ephemeris file, set the statuses and return. 00184 # 00185 if (file_OK == 0): 00186 for MJD in MJDs: 00187 estatuses = [] 00188 efds = [] 00189 edfds = [] 00190 for frequency in frequencies: 00191 estatuses.append(4) 00192 efds.append(0) 00193 edfds.append(0) 00194 statuses.append(estatuses) 00195 fds.append(efds) 00196 dfds.append(edfds) 00197 Rhats.append([0.0, 0.0, 0.0]) 00198 directions.append(me.direction('J2000',0.0,0.0)) 00199 return [ statuses, fds, dfds, Rhats, directions ] 00200 00201 Req = 1000.0 * (tb.getkeyword('radii')['value'][0] + tb.getkeyword('radii')['value'][1]) / 2 00202 Rp = 1000.0 * tb.getkeyword('radii')['value'][2] 00203 times = tb.getcol('MJD').tolist() 00204 RAs = tb.getcol('RA').tolist() 00205 DECs = tb.getcol('DEC').tolist() 00206 distances = tb.getcol('Rho').tolist() 00207 RadVels = tb.getcol('RadVel').tolist() 00208 column_names = tb.colnames() 00209 if ('DiskLat' in column_names): 00210 selats = tb.getcol('DiskLat').tolist() 00211 has_selats = 1 00212 else: 00213 has_selats = 0 00214 selat = 0.0 00215 if ('NP_ang' in column_names): 00216 NPangs = tb.getcol('NP_ang').tolist() 00217 has_NPangs= 1 00218 else: 00219 has_NPangs = 0 00220 NPang = 0.0 00221 tb.close() 00222 MJD_shifted_frequencies = [] 00223 DDs = [] 00224 Rmeans = [] 00225 00226 for ii in range(len(MJDs)): 00227 MJD = MJDs[ii] 00228 DDs.append(1.4959787066e11 * interpolate_list (times, distances, MJD)[1]) 00229 if (has_selats): 00230 selat = interpolate_list (times, selats, MJD)[1] 00231 if (selat == -999.0): 00232 selat = 0.0 00233 # apparent polar radius 00234 Rpap = sqrt (Req*Req * sin(selat)**2.0 + 00235 Rp*Rp * cos(selat)**2.0) 00236 Rmean = sqrt (Rpap * Req) 00237 Rmeans.append(Rmean) 00238 # 00239 # need to check that the convention for NP angle is the 00240 # same as what is needed in the component list. 00241 # 00242 if (has_NPangs): 00243 NPang = interpolate_list (times, NPangs, MJD)[1] 00244 if (NPang == -999.0): 00245 NPang = 0.0 00246 Rhats.append([2*RAD2ASEC*Req/DDs[-1], 2*RAD2ASEC*Rpap/DDs[-1], NPang]) 00247 RA = interpolate_list (times, RAs, MJD)[1] 00248 RAstr=str(RA)+'deg' 00249 DEC = interpolate_list (times, DECs, MJD)[1] 00250 DECstr=str(DEC)+'deg' 00251 directions.append(me.direction('J2000',RAstr,DECstr)) 00252 # 00253 # now get the doppler shift 00254 # 00255 # NOTE: this is not exactly right, because it doesn't include the 00256 # distance to the body in any of these calls. the distance will matter 00257 # because it will change the line-of-sight vector from the observatory 00258 # to the body, which will change the doppler shift. jeff thinks using 00259 # the comet measures calls might fix this, but i haven't been able to 00260 # figure them out yet. i thought i had it figured out, with the 00261 # call to me.framecomet(), but that doesn't give the right answer, 00262 # unfortunately. i spot-checked the error introduced because of this, 00263 # and it looks to be of order 1 m/s for these bodies, so i'm not going 00264 # to worry about it. 00265 # 00266 me.doframe(me.observatory(observatory)) 00267 me.doframe(me.epoch('utc',str(MJD)+'d')) 00268 me.doframe(directions[-1]) 00269 # 00270 # instead of the call to me.doframe() in the line above, i thought the 00271 # following call to me.framecomet() would be right, but it doesn't give 00272 # the right answer :/. 00273 # me.framecomet(ephemeris_file) 00274 # 00275 # RadVel is currently in AU/day. we want it in km/s. 00276 # 00277 RadVel = interpolate_list (times, RadVels, MJD)[1] * AU / 86400000.0 00278 rv = me.radialvelocity('geo',str(RadVel)+'km/s') 00279 shifted_frequencies = [] 00280 for frequency in frequencies: 00281 # 00282 # the measure for radial velocity could be obtained via: 00283 # me.measure(rv,'topo')['m0']['value'] 00284 # but what we really want is a frequency shift. i could do it by 00285 # hand, but i'd rather do it with casa toolkit calls. unfortunately, 00286 # it's a bit convoluted in casa... 00287 # 00288 newfreq0 = me.tofrequency('topo',me.todoppler('optical',me.measure(rv,'topo')),me.frequency('topo',str(frequency[0])+'Hz'))['m0']['value'] 00289 newfreq1 = me.tofrequency('topo',me.todoppler('optical',me.measure(rv,'topo')),me.frequency('topo',str(frequency[1])+'Hz'))['m0']['value'] 00290 # 00291 # should check units to be sure frequencies are in Hz 00292 # 00293 # now, we want to calculate the model shifted in the opposite direction 00294 # as the doppler shift, so take that into account. 00295 # 00296 delta_frequency0 = frequency[0] - newfreq0 00297 newfreq0 = frequency[0] + delta_frequency0 00298 delta_frequency1 = frequency[1] - newfreq1 00299 newfreq1 = frequency[1] + delta_frequency1 00300 shifted_frequencies.append([newfreq0,newfreq1]) 00301 average_delta_frequency = (delta_frequency0 + delta_frequency1)/2 00302 # 00303 # should we print this to the log? 00304 # 00305 # print 'MJD, geo & topo velocities (km/s), and shift (MHz) = %7.1f %5.1f %5.1f %6.3f' % \ 00306 # (MJD, RadVel, me.measure(rv,'topo')['m0']['value']/1000, average_delta_frequency/1.0e6) 00307 msg='MJD, geo & topo velocities (km/s), and shift (MHz) = %7.1f %5.1f %5.1f %6.3f' % \ 00308 (MJD, RadVel, me.measure(rv,'topo')['m0']['value']/1000, average_delta_frequency/1.0e6) 00309 casalog.post(msg, 'INFO2') 00310 MJD_shifted_frequencies.append(shifted_frequencies) 00311 # me.done() 00312 00313 for ii in range(len(MJDs)): 00314 shifted_frequencies = MJD_shifted_frequencies[ii] 00315 if (capitalized_source_name == 'Mars'): 00316 [tstatuses,brightnesses,dbrightnesses] = brightness_Mars_int ([MJDs[ii]], shifted_frequencies) 00317 # modified by TT: take out an extra dimension (for times), to match the rest of the operation 00318 tstatuses=tstatuses[0] 00319 brightnesses=brightnesses[0] 00320 dbrightnesses=dbrightnesses[0] 00321 else: 00322 tstatuses = [] 00323 brightnesses = [] 00324 dbrightnesses = [] 00325 for shifted_frequency in shifted_frequencies: 00326 [status,brightness,dbrightness] = brightness_planet_int (capitalized_source_name, shifted_frequency) 00327 tstatuses.append(status) 00328 brightnesses.append(brightness) 00329 dbrightnesses.append(dbrightness) 00330 tfds = [] 00331 tdfds = [] 00332 for jj in range (len(tstatuses)): 00333 if not tstatuses[jj]: 00334 flux_density = brightnesses[jj] * 1.0e26 * pi * Rmeans[ii]*Rmeans[ii]/ (DDs[ii]*DDs[ii]) 00335 # 00336 # mean apparent planet radius, in arcseconds (used if we ever 00337 # calculate the primary beam reduction) 00338 # 00339 psize = (Rmeans[ii] / DDs[ii]) * RAD2ASEC 00340 # 00341 # primary beam reduction factor (should call a function, but 00342 # just set to 1.0 for now... 00343 # 00344 pbfactor = 1.0 00345 flux_density *= pbfactor 00346 tfds.append(flux_density) 00347 tdfds.append(0.0) 00348 else: 00349 tfds.append(0.0) 00350 tdfds.append(0.0) 00351 statuses.append(tstatuses) 00352 fds.append(tfds) 00353 dfds.append(tdfds) 00354 00355 return [ statuses, fds, dfds, Rhats, directions ] 00356 00357 00358 def brightness_Mars_int (MJDs, frequencies): 00359 ''' 00360 Planck brightness for Mars. this one is different because 00361 the model is tabulated vs. frequency *and* time. 00362 inputs: 00363 MJDs = list of MJD times 00364 frequencies = list of [start, stop] frequencies for 00365 which to calculate the integrated model. 00366 example: [ [ 224.234567e9, 224.236567e9 ] ] 00367 ''' 00368 00369 # 00370 # constants. these might be in CASA internally somewhere, but 00371 # i don't know where to pull them out, so oh well, define my own 00372 # 00373 HH = 6.6260755e-34 00374 KK = 1.380658e-23 00375 CC = 2.99792458e8 00376 00377 statuses = [] 00378 Tbs = [] 00379 dTbs = [] 00380 try: 00381 model_data_path = os.environ['CASAPATH'].split()[0]+'/data/alma/SolarSystemModels/' 00382 model_data_filename = model_data_path + 'Mars_Tb.dat' 00383 ff = open(model_data_filename) 00384 except: 00385 for MJD in MJDs: 00386 estatuses = [] 00387 eTbs = [] 00388 eTbds = [] 00389 for frequency in frequencies: 00390 estatuses.append(3) 00391 eTbs.append(0) 00392 edTbs.append(0) 00393 statuses.append(estatuses) 00394 Tbs.append(eTbs) 00395 dTbs.append(edTbs) 00396 return [ statuses, Tbs, dTbs ] 00397 00398 # first line holds frequencies, like: 00399 # 30.0 80.0 115.0 150.0 200.0 230.0 260.0 300.0 330.0 360.0 425.0 650.0 800.0 950.0 1000.0 00400 line = ff.readline()[:-1] 00401 fields = line.split() 00402 freqs = [] 00403 for field in fields: 00404 freqs.append(1.0e9*float(field)) 00405 # model output lines look like: 00406 #2010 01 01 00 55197.00000 189.2 195.8 198.9 201.2 203.7 204.9 205.9 207.1 207.8 208.5 209.8 213.0 214.6 214.8 214.5 00407 modelMJDs = [] 00408 modelTbs = [] 00409 for line in ff: 00410 fields = line[:-1].split() 00411 modelMJDs.append(float(fields[4])) 00412 lTbs = [] 00413 for ii in range(len(freqs)): 00414 lTbs.append(float(fields[5+ii])) 00415 modelTbs.append(lTbs) 00416 ff.close() 00417 for MJD in MJDs: 00418 nind = nearest_index (modelMJDs, MJD) 00419 mTbs = [] 00420 mfds = [] 00421 for ii in range(len(freqs)): 00422 lMJD = [] 00423 lTb = [] 00424 for jj in range(nind-10, nind+10): 00425 lMJD.append(modelMJDs[jj]) 00426 lTb.append(modelTbs[jj][ii]) 00427 mTbs.append(interpolate_list(lMJD, lTb, MJD)[1]) 00428 # 00429 # note: here, when we have the planck results, get a proper estimate of 00430 # the background temperature. 00431 # 00432 # note also that we want to do this here because the integral needs to 00433 # be done on the brightness, not on the brightness *temperature*. 00434 # 00435 Tbg = 2.725 00436 mfds.append((2.0 * HH * freqs[ii]**3.0 / CC**2.0) * \ 00437 ((1.0 / (exp(HH * freqs[ii] / (KK * mTbs[-1])) - 1.0)) - \ 00438 (1.0 / (exp(HH * freqs[ii] / (KK * Tbg)) - 1.0)))) 00439 estatuses = [] 00440 eTbs = [] 00441 edTbs = [] 00442 for frequency in frequencies: 00443 if (frequency[0] < freqs[0] or frequency[1] > freqs[-1]): 00444 estatuses.append(2) 00445 eTbs.append(0.0) 00446 edTbs.append(0.0) 00447 else: 00448 [estatus, eTb, edTb] = integrate_Tb (freqs, mfds, frequency) 00449 # 00450 # should we print out the Tb we found? not sure. i have a 00451 # vague recollection that crystal requested it, but i'm not 00452 # sure if it's really needed. we'd have to back out the 00453 # planck correction (along with the background), so it wouldn't 00454 # be trivial. 00455 # 00456 estatuses.append(estatus) 00457 eTbs.append(eTb) 00458 edTbs.append(edTb) 00459 statuses.append(estatuses) 00460 Tbs.append(eTbs) 00461 dTbs.append(edTbs) 00462 return [statuses, Tbs, dTbs ] 00463 00464 00465 def brightness_planet_int (source_name, frequency): 00466 ''' 00467 brightness temperature for supported planets. integrates over 00468 a tabulated model. inputs: 00469 source_name = source name string 00470 frequency = list of [start, stop] frequencies for 00471 which to calculate the integrated model. 00472 example: [ 224.234567e9, 224.236567e9 ] 00473 ''' 00474 00475 # 00476 # constants. these might be in CASA internally somewhere, but 00477 # i don't know where to pull them out, so oh well, define my own 00478 # 00479 HH = 6.6260755e-34 00480 KK = 1.380658e-23 00481 CC = 2.99792458e8 00482 00483 try: 00484 model_data_path = os.environ['CASAPATH'].split()[0]+'/data/alma/SolarSystemModels/' 00485 model_data_filename = model_data_path + source_name + '_Tb.dat' 00486 ff = open(model_data_filename) 00487 except: 00488 return [ 3, 0.0, 0.0 ] 00489 fds = [] 00490 Tbs = [] 00491 freqs = [] 00492 for line in ff: 00493 [freq,Tb] = line[:-1].split() 00494 Tbs.append(float(Tb)) 00495 freqs.append(1.0e9*float(freq)) 00496 # 00497 # note: here, when we have the planck results, get a proper 00498 # estimate of the background temperature. 00499 # 00500 # note also that we want to do this here because the integral 00501 # needs to be done on the brightness, not on the brightness 00502 # *temperature*. 00503 # 00504 Tbg = 2.725 00505 fds.append((2.0 * HH * freqs[-1]**3.0 / CC**2.0) * \ 00506 ((1.0 / (exp(HH * freqs[-1] / (KK * Tbs[-1])) - 1.0)) - \ 00507 (1.0 / (exp(HH * freqs[-1] / (KK * Tbg)) - 1.0)))) 00508 ff.close() 00509 if (frequency[0] < freqs[0] or frequency[1] > freqs[-1]): 00510 return [ 2, 0.0, 0.0 ] 00511 else: 00512 # 00513 # should we print out the Tb we found? not sure. i have a 00514 # vague recollection that crystal requested it, but i'm not 00515 # sure if it's really needed. we'd have to back out the 00516 # planck correction (along with the background), so it wouldn't 00517 # be trivial. and here, we'd have to return a variable and 00518 # work on that. 00519 # 00520 return integrate_Tb (freqs, fds, frequency) 00521 00522 00523 def nearest_index (input_list, value): 00524 """ 00525 find the index of the list input_list that is closest to value 00526 """ 00527 00528 ind = searchsorted(input_list, value) 00529 ind = min(len(input_list)-1, ind) 00530 ind = max(1, ind) 00531 if value < (input_list[ind-1] + input_list[ind]) / 2.0: 00532 ind = ind - 1 00533 return ind 00534 00535 00536 def interpolate_list (freqs, Tbs, frequency): 00537 ind = nearest_index (freqs, frequency) 00538 low = max(0,ind-5) 00539 if (low == 0): 00540 high = 11 00541 else: 00542 high = min(len(freqs),ind+6) 00543 if (high == len(freqs)): 00544 low = high - 11 00545 # 00546 # i wanted to put in a check for tabulated values that change 00547 # derivative, since that confuses the interpolator. benign cases are 00548 # fine, like radial velocity, but for the model Tbs, where there are 00549 # sharp spectral lines, then the fitting won't be sensible when you're 00550 # right at the center of the line, because the inflection is so severe. 00551 # i thought if i just only took values that had the same derivative as 00552 # the location where the desired value is that would work, but it 00553 # doesn't :/. i'm either not doing it right or there's something 00554 # deeper. 00555 # 00556 # if freqs[ind] < frequency: 00557 # deriv = Tbs[ind+1] - Tbs[ind] 00558 # else: 00559 # deriv = Tbs[ind] - Tbs[ind-1] 00560 # tTbs = [] 00561 # tfreqs = [] 00562 # for ii in range(low,high): 00563 # nderiv = Tbs[ii+1] - Tbs[ii] 00564 # if (nderiv >= 0.0 and deriv >= 0.0) or (nderiv < 0.0 and deriv < 0.0): 00565 # tTbs.append(Tbs[ii]) 00566 # tfreqs.append(freqs[ii]) 00567 # aTbs = array(tTbs) 00568 # afreqs = array(tfreqs) 00569 aTbs = array(Tbs[low:high]) 00570 afreqs = array(freqs[low:high]) 00571 # 00572 # cubic interpolation blows up near line centers (see above comment), 00573 # so check that it doesn't fail completely (put it in a try/catch), and 00574 # also that it's not a NaN and within the range of the tabulated values 00575 # 00576 range = max(aTbs) - min(aTbs) 00577 try: 00578 func = interp1d (afreqs, aTbs, kind='cubic') 00579 if isnan(func(frequency)) or func(frequency) < min(aTbs)-range/2 or func(frequency) > max(aTbs)+range/2: func = interp1d (afreqs, aTbs, kind='linear') 00580 except: 00581 func = interp1d (afreqs, aTbs, kind='linear') 00582 # 00583 # if it still failed, even with the linear interpolation, just take the 00584 # nearest tabulated point. 00585 # 00586 if isnan(func(frequency)) or func(frequency) < min(aTbs)-range/2 or func(frequency) > max(aTbs)+range/2: 00587 brightness = Tbs[ind] 00588 else: 00589 brightness = float(func(frequency)) 00590 return [ 0, brightness, 0.0 ] 00591 00592 00593 def integrate_Tb (freqs, Tbs, frequency): 00594 [status,low_Tb,low_dTb] = interpolate_list (freqs, Tbs, frequency[0]) 00595 low_index = nearest_index (freqs, frequency[0]) 00596 if (frequency[0] > freqs[low_index]): 00597 low_index = low_index + 1 00598 00599 [status,hi_Tb,hi_dTb] = interpolate_list (freqs, Tbs, frequency[1]) 00600 hi_index = nearest_index (freqs, frequency[1]) 00601 if (frequency[1] < freqs[hi_index]): 00602 hi_index = hi_index - 1 00603 00604 if (low_index > hi_index): 00605 Tb = (frequency[1] - frequency[0]) * (low_Tb + hi_Tb) / 2 00606 else: 00607 Tb = (freqs[low_index] - frequency[0]) * (low_Tb + Tbs[low_index]) / 2 + \ 00608 (frequency[1] - freqs[hi_index]) * (hi_Tb + Tbs[hi_index]) / 2 00609 ii = low_index 00610 while (ii < hi_index): 00611 Tb += (freqs[ii+1] - freqs[ii]) * (Tbs[ii+1] + Tbs[ii]) / 2 00612 ii += 1 00613 Tb /= (frequency[1] - frequency[0]) 00614 return [ 0, Tb, 0.0 ] 00615 00616