#!/usr/bin/env python ## ============================================================================= ## ============================================================================= ## ## tec_maps.py ## ## Created by J. E. Kooi 2014/04/25 v2.0 ## Modified by gmoellen 2014/07/21 v2.1 (minor mods, mostly semantics) ## Modified by J. E. Kooi 2014/09/17 v2.2 (minor mods, formating and ## ~ Test for network connection ## ~ Heirarchy of IGS-related ## data products added ## ~ Added IGS RMS TEC maps ## ~ Made IGS default data product) ## Modified by gmoellen 2014/09/30 v2.3 (added simplified create method ## that hides all of the MAPGPS-related ## options, and thereby streamlines ## approved general usage for ## CASA 4.3) ## Modified by J. E. Kooi 2015/01/29 v2.4 (Changed get_IGS_TEC to accept ## IONEX format data with ANY grid ## spacing in deg. and time res in min) ## Modified by J. E. Kooi 2015/03/2 v2.5 (Changed ztec_value for doplot = ## True to plot a red bar over the VTEC ## to show the observing session) ## Modified by gmoellen 2017/05/30 v2.6 Generate plot disk file ## Modified by bkent 2017/10-31 v2.7 Fixed VisibleDeprecationWarning: ## using a non-integer ## number instead of an integer will ## result in an error in the future ## Modified by gmoellen 2020/08/28 v2.8 Updated TEC retrieval to use more-secure ## CDDIS server (ftp-ssl), as old ## service going offline 2020Oct31; ## other python cleanup ## 2020/10/13 python3-ification, for CASA 6. ## ## Tested in CASA 4.3.0 and 4.2.1 on RHEL release 6.4 (Santiago) ## Tested in CASA 4.3.0, 4.2.2, and 4.2.1 on Mac OS 10.8.5 ## ## ## ## The purpose of this python module is to retrieve vertical TEC/DTEC maps from ## either IGS (housed on the CDDIS servers) or MAPGPS (housed on the Madrigal ## servers). ## ## **** Currently, the MAPGPS is "turned off" and modification of line 306 **** ## **** is required to turn this functionality back "on." **** ## ## The IGS Final Product maps represent a combined TEC map: ## The different IGS Ionosphere Associate Analysis Centers IAAC TEC maps have ## been computed with different approaches but with a common formal resolution ## of 2 hours, 5 degrees, and 2.5 degrees in UT, longitude, and latitude ## (details can be found in e.g. Schaer, 1999; Feltens, 1998; ## Mannucci et al., 1998; Gao et al.; Hernandez-Pajares et al.,1999). The four ## IAACs TEC maps have been combined in an IGS combination product using weights ## obtained by two IGS Ionosphere Associate Validation Centers (IAVCs) from the ## corresponding performances in reproducing STEC and differences of STEC (IAVCs ## NRCAN and UPC respectively, see details in Feltens, 2002). ## ------ Krankowski & Sieradzki, 2013 (references can be found in this paper) ## ## The code has a hierarchy for IGS data products. It will initially search for ## the IGS Final product data (available ~ 10-14 days after an observation) and, ## if unavailable, then the IGS Rapid Product (available in ~ 1-2 days) and, ## if unavailable, then the JPL Rapid Product. ## ## The MAPGPS maps are computed with no pre-applied ionosphere model and, in ## this sense, represent 'raw TEC data.' The benefit is that they have a formal ## resolution of 5 minutes, 1.0 degree, and 1.0 degree in UT, longitude, and ## latitude (details can be found in Rideout & Coster, 2006). The drawback is ## that the data is sparsely gridded in many locations (e.g., there are no GPS ## ground stations in the middle of the ocean) and there are sporadic gaps in ## the data at some locations (e.g., even over the United States). ## ## For now, users are encouraged to use the IGS Final Product maps instead of ## MAPGPS products because there is data at all grid points and times, ## making it easier to deal with in the code. The code for MAPGPS is also set ## to make a 'patch' over North America and therefore is not a global map. ## However, the code is included and need only be uncommented. Feel free to ## experiment and compare the IGS and MAPGPS maps! ## ## This module calls several methods depending on the TEC/DTEC server and ## ## (1) Produces a CASA image of the TEC data for ## a) The whole world (IGS) (additional DTEC data image is output) ## b) A 15 deg. patch centered on the VLA (MAPGPS) ## (2) Optionally produces a vertical TEC/DTEC time series for the VLA ## ## !!!!! Important Note: In order to access the MAPGPS data, you must: ## (1) Have "madrigalWeb.py" and "globalDownload.py" and ## "madrigal.head" in the working directory ## (2) Provide your name, e-mail, and institution in the .create0 method to ## access the Madrigal server ## ## References: ## ## Krankowski, A., & Sieradzki, R. 2013, in International GNSS Service ## Technical Report, ed. R. Dach & Y. Jean, IGS Central Bureau ## (Astronomical Institute:University of Bern), 157 ## Rideout, W., & Coster, A. 2006, GPS Solutions, 10, 219 ## ## ============================================================================= ## ## Example Use: ## ## (0) The default use provides the IGS Product: ## > import tec_maps ## > msname = 'visibilities.ms' ## > CASA_image,CASA_RMS_image = tec_maps.create0(msname) ## ## (1) IGS Product (w/out the vertical TEC/DTEC time series at the VLA) ## > import tec_maps ## > msname = 'visibilities.ms' ## > CASA_image,CASA_RMS_image = tec_maps.create0(msname,'IGS') ## > viewer(CASA_image) ## > viewer(CASA_RMS_image) ## ## (2) IGS Product (WITH the vertical TEC/DTEC time series at the VLA) ## > import tec_maps ## > msname = 'visibilities.ms' ## > CASA_image,CASA_RMS_image = tec_maps.create0(msname,'IGS',True) ## > viewer(CASA_image) ## > viewer(CASA_RMS_image) ## ## (3) IGS Product (WITH the vertical TEC/DTEC time series at the VLA and for ## which the user specifies the output image name) ## > import tec_maps ## > msname = 'visibilities.ms' ## > imagename = 'my_image' ## > CASA_image,CASA_RMS_image = tec_maps.create0(msname,'IGS',True,imagename) ## > viewer(CASA_image) ## > viewer(CASA_RMS_image) ## ## (4) MAPGPS (WITH the vertical TEC/DTEC time series for the VLA) ## > import tec_maps ## > msname = 'visibilities.ms' ## > myname = 'John Doe' ## > myemail = 'john-doe@place.edu' ## > myplace = 'NRAO' ## > CASA_image,CASA_RMS_image = ## tec_maps.create0(msname,'MAPGPS',True,'',myname,myemail,myplace) ## > viewer(CASA_image) ## > viewer(CASA_RMS_image) ## ## ## ============================================================================= ## ============================================================================= import glob, pylab, os, datetime import numpy as np from matplotlib import rc import matplotlib.pyplot as plt from casatasks.private.casa_transition import is_CASA6 if is_CASA6: from casatools import table, quanta, coordsys, image tb = table() qa = quanta() cs = coordsys() ia = image() else: from casac import * tb = casac.table() qa = casac.quanta() cs = casac.coordsys() ia = casac.image() workDir = os.getcwd()+'/' def create(vis,doplot=False,imname=''): """ ## ============================================================================= ## ## This method opens the .ms to determine the days of observation and calls the ## necessary functions to acquire the desired set of TEC/DTEC data. The method ## finally calls ztec_value and make_image once it has the data. ## ## ============================================================================= ## ## Inputs: ## vis type = string Name of the measurement set for which to ## acquire TEC/DTEC data ## doplot type = boolean When True, this will open a plot of the ## interpolated TEC/DTEC at the VLA. ## imname type = string Name of the output TEC Map optionally ## specified by the user ## ## Returns: ## Opens a plot showing the zenith TEC/DTEC for the VLA ## (if doplot = True) and the name of the CASA image file containing ## the TEC map. ## ## ============================================================================= Usage: The default use provides the IGS Product: > vis = 'visibilities.ms' > CASA_image,CASA_RMS_image = tec_maps.create(vis) IGS Product (WITH the vertical TEC/RMS TEC time series at the VLA) > vis = 'visibilities.ms' > CASA_image,CASA_RMS_image = tec_maps.create(msname,plot_vla_tec = True) The TEC and RMS TEC images can then be examined using viewer: > viewer(CASA_image) > viewer(CASA_RMS_image) The TEC image should then be used in gencal (caltype='tecim') to generate a sampled caltable that will nominally correct for ionospheric effects """ # call the more general method return create0(ms_name=vis,plot_vla_tec=doplot,im_name=imname) def create0(ms_name,tec_server='IGS',plot_vla_tec=False,im_name='',username='',user_email='',affiliation=''): """ ## ============================================================================= ## ## This opens the .ms to determine the days of observation and calls the ## necessary functions to acquire the desired set of TEC/DTEC data. The method ## finally calls ztec_value and make_image once it has the data. ## ## ============================================================================= ## ## Inputs: ## ms_name type = string Name of the measurement set for which to ## acquire TEC/DTEC data ## tec_server type = string Server from which to retrieve TEC/DTEC ## plot_vla_tec type = boolean When True, this will open a plot of the ## interpolated TEC/DTEC at the VLA. ## im_name type = string Name of the output TEC Map optionally ## specified by the user ## username type = string MAPGPS only: full name of user accessing ## the site ## ex: '<First> <Last>' ## user_email type = string MAPGPS only: e-mail address at which the ## user can be reached ## ex: '<name>@<place>.com' ## affiliation type = string MAPGPS only: user's affiliated institute ## ex: 'NRAO' ## ## Returns: ## Opens a plot showing the zenith TEC/DTEC for the VLA ## (if plot_vla_tec = True) and the name of the CASA image file containing ## the TEC map. ## ## ============================================================================= Usage: The default use provides the IGS Product: > msname = 'visibilities.ms' > CASA_image,CASA_RMS_image = tec_maps.create(msname) IGS Product (WITH the vertical TEC/RMS TEC time series at the VLA) > msname = 'visibilities.ms' > CASA_image,CASA_RMS_image = tec_maps.create(msname,plot_vla_tec = True) The TEC and RMS TEC images can then be examined using viewer: > viewer(CASA_image) > viewer(CASA_RMS_image) """ ## Open the .ms to get the range in observation times tb.open(ms_name+'/OBSERVATION') obs_times = tb.getcol('TIME_RANGE') tb.close() t_min = min(obs_times) t_max = max(obs_times) ## Calculate the reference time for the TEC map to be generated. ref_time = 86400.*np.floor(t_min[0]/86400) ref_start = t_min[0]-ref_time ref_end = t_max[0]-ref_time ## Gets the day string and the integer number of days of observation (only tested for two continuous days) begin_day = qa.time(str(t_min[0])+'s',form='ymd')[0][:10] end_day = qa.time(str(t_max[0])+'s',form='ymd')[0][:10] num_of_days = int(np.ceil((t_max[0]-ref_time)/86400.)) # must be an int as used below! ## Set up the number of times we need to go get TEC files # (this is probably superfluous.... 2020Oct13 gmoellen) if begin_day == end_day: call_num = 1 elif num_of_days == 0: call_num = 2 else: call_num = num_of_days ## Set up the days for which we need to go get TEC files day_list = [] next_day = begin_day for iter in range(call_num): day_list.append(next_day) next_day = qa.time(str(t_min[0]+86400.*(iter+1))+'s',form='ymd')[0][:10] print('IGS files required for: '+str(day_list)) ## Runs the IGS methods if tec_server == 'IGS': ymd_date_num = 0 #array = [] lo=0 hi=0 for ymd_date in day_list: points_long,points_lat,ref_long,ref_lat,incr_long,incr_lat,incr_time,num_maps,tec_array,tec_type = get_IGS_TEC(ymd_date) ## Fill a new array with all the full set of TEC/DTEC values for all days in the observation set. if tec_type != '': if ymd_date_num == 0: # first pass make full output tec array # (this assumes all files have same number of timestamps (num_maps)) full_tec_array = np.zeros((2,int(points_long),int(points_lat),(int(num_maps)-1)*int(call_num)+1)) lo=0 hi=int(num_maps) else: # First time of each subsequent file duplicates last of previous one, so overwrite it lo+=(int(num_maps)-1) hi=lo+int(num_maps) full_tec_array[:,:,:,lo:hi] = tec_array[:,:,:,:] ymd_date_num +=1 if tec_type != '': if im_name == '': prefix = ms_name else: prefix = im_name plot_name='' if plot_vla_tec: plot_name=prefix+'.IGS_TEC_at_site.png' ztec_value(-107.6184,34.0790,points_long,points_lat,ref_long,ref_lat,incr_long,\ incr_lat,incr_time,ref_start,ref_end,int((num_maps-1)*call_num+1),\ full_tec_array,plot_vla_tec,plot_name) CASA_image = make_image(prefix,ref_long,ref_lat,ref_time,incr_long,incr_lat,\ incr_time*60,full_tec_array[0],tec_type,appendix = '.IGS_TEC') CASA_RMS_image = make_image(prefix,ref_long,ref_lat,ref_time,incr_long,incr_lat,\ incr_time*60,full_tec_array[1],tec_type,appendix = '.IGS_RMS_TEC') else: CASA_image = '' CASA_RMS_image = '' ## Runs the Madrigal methods; requires "madrigalWeb.py" and "globalDownload.py" and "madrigal.head" if tec_server == 'MAPGPS': print('\nCurrently, MAPGPS has been set to "False" \nand you must change tec_maps.py at line 306\n') CASA_image = '' CASA_RMS_image = '' if (tec_server == 'MAPGPS' and False): ## replace with: if tec_server == 'MAPGPS': mad_file_front = 'gps'+str(begin_day.split('/')[0])[2:]+str(begin_day.split('/')[1])+str(begin_day.split('/')[2]) mad_data_file = ms_name[:-3]+'_MAPGPS_Data' mad_file = '' mad_file = check_existence(mad_data_file) if username!='' and user_email!='' and affiliation!='': if mad_file == '': print('Retrieving the following MAPGPS files: '+begin_day+' to '+end_day) begin_mdy = str(begin_day.split('/')[1])+'/'+str(begin_day.split('/')[2])+'/'+str(begin_day.split('/')[0]) end_mdy = str(end_day.split('/')[1])+'/'+str(end_day.split('/')[2])+'/'+str(end_day.split('/')[0]) os.system('python globalDownload.py --url=http://madrigal.haystack.mit.edu/madrigal --outputDir='+workDir+\ ' --user_fullname="'+username+'" --user_email='+user_email+' --user_affiliation='+affiliation+\ ' --format=ascii --startDate='+str(begin_mdy)+' --endDate='+str(end_mdy)+' --inst=8000') non_day = qa.time(str(t_min[0]-86400.)+'s',form='ymd')[0][:10] non_file_prefix = 'gps'+str(non_day.split('/')[0])[2:]+str(non_day.split('/')[1])+str(non_day.split('/')[2]) unwanted_file = check_existence(non_file_prefix) os.system('rm -rf '+unwanted_file+'.txt') files = glob.glob(r''+workDir+'*.txt') outfile = open(workDir+mad_data_file+'.txt','a') for y in files: newfile = open(y,'r+') data = newfile.read() newfile.close() outfile.write(data) outfile.close() ## Making the CASA table is expensive, so we do it only once. print('Making a CASA table of: '+str(mad_data_file)) make_CASA_table(mad_data_file) try: CASA_image = convert_MAPGPS_TEC(ms_name,mad_data_file,ref_time,ref_start,\ ref_end,plot_vla_tec,im_name) except: print('An error was encountered retrieving/interpreting the MAPGPS files.') CASA_image = '' CASA_RMS_image = '' else: print('You need to supply your username, e-mail, and affiliation to access the Madrigal server.') CASA_image = '' CASA_RMS_image = '' ## Returns the name of the TEC image generated print('The following TEC map was generated: '+CASA_image+' & '+CASA_RMS_image) if len(plot_name)>0: print('The following TEC zenith plot was generated: '+plot_name) else: plot_name='none' return CASA_image,CASA_RMS_image,plot_name def get_IGS_TEC(ymd_date): """ ## ============================================================================= ## ## Retrieves the IGS data and, specifically, the IGS Final Product: this is ## the combined TEC map from the four IAACs TEC maps. ## ## ============================================================================= ## ## Inputs: ## ymd_date type = string The 'yyyy/mm/dd' date of observation for ## which this will retrieve data ## ## Returns: ## points_long type = integer Total number of points in longitude (deg) ## in the TEC map ## points_lat type = integer Total number of points in latitude (deg) ## in the TEC map ## start_long type = float Initial value for the longitude (deg) ## end_lat type = float Final value for the latitude (deg) ## incr_long type = float Increment by which longitude increases ## IGS data: 5 deg ## MAPGPS data: 1 deg ## incr_lat type = float Absolute value of the increment by which ## latitude increases ## IGS data: 2.5 deg ## MAPGPS data: 1 deg ## incr_time type = float Increment by which time increases ## IGS data: 120 min ## MAPGPS data: 5 min ## num_maps type = integer Number of maps (or time samples) of TEC ## tec_array type = array 4D array with axes consisting of ## [TEC_type,long.,lat.,time] that gives ## the TEC/DTEC in TECU ## tec_type type = string Specifies the origin of TEC data as a ## CASA table keyword ## ## ============================================================================= """ year = int(ymd_date.split('/')[0]) month = int(ymd_date.split('/')[1]) day = int(ymd_date.split('/')[2]) ## Gives the day of the year of any given year dayofyear = datetime.datetime.strptime(''+str(year)+' '+str(month)+' '+str(day)+'', '%Y %m %d').timetuple().tm_yday ## Convert dayofyear to 3-digit string, padded with zeros for IONEX filename format dayofyear = str(dayofyear).zfill(3) ## Outputing the name of the IONEX file you require. #igs_file = 'igsg'+str(dayofyear)+'0.'+str(list(str(year))[2])+''+str(list(str(year))[3])+'i' igs_file = 'igsg'+str(dayofyear)+'0.'+str(year)[2:4]+'i' print('\nFor '+ymd_date+', the required IGS file is called: '+igs_file) ## ========================================================================= ## ## This goes to the CDDIS website, and downloads and uncompresses the IGS ## file. The preference is for the IGS Final product (IGSG), available ## ~ 12-14 days after data is collected (i.e. data for September 1 should ## be available by September 14). If this file is not already in the ## current working directory, it will retrieve it. If the file is ## unavailable, it will try to retrieve the IGS Rapid Product (IGRG), ## released ~ 2-3 days after data is collected. If this file is ## unavailable, it will try to retrieve the JPL Rapid Product (JPRG), ## released ~ 1 day after data is collected. While the 'uncompress' command ## is not necessary, it is the most straightforward on Linux. ## ## ========================================================================= curlcmd='curl -u anonymous:casa-feedback@nrao.edu --ftp-ssl ' does_exist = '' does_exist = check_existence(igs_file) #CDDIS = 'ftp://cddis.gsfc.nasa.gov/gnss/products/ionex/' # pre-2020Nov01 version CDDIS = 'ftp://gdc.cddis.eosdis.nasa.gov/gps/products/ionex/' # new, more secure ftp-ssl server (2020Nov01) file_location = CDDIS+str(year)+'/'+str(dayofyear)+'/' if does_exist == '': print('Attempting retrieval of IGS Final product file: '+str(igs_file)) get_file = file_location+igs_file+'.Z' retrieve = test_IONEX_connection(get_file) if retrieve == True: os.system(curlcmd+get_file+' > '+workDir+igs_file+'.Z') os.system('uncompress '+igs_file+'.Z') else: igs_file = igs_file.replace('igs','igr') does_exist = check_existence(igs_file) if does_exist == '': print('Attempting retrieval of IGS Rapid product file: '+str(igs_file)) get_file = file_location+igs_file+'.Z' retrieve = test_IONEX_connection(get_file) if retrieve == True: os.system(curlcmd+get_file+' > '+workDir+igs_file+'.Z') os.system('uncompress '+igs_file+'.Z') else: igs_file = igs_file.replace('igr','jpr') does_exist = check_existence(igs_file) if does_exist == '': print('Attempting retrieval of JPL Rapid product file: '+str(igs_file)) get_file = file_location+igs_file+'.Z' retrieve = test_IONEX_connection(get_file) if retrieve == True: os.system(curlcmd+get_file+' > '+workDir+igs_file+'.Z') os.system('uncompress '+igs_file+'.Z') else: print('\nNo data products available. You may try to manually'+\ ' download the products at:\n'+\ 'ftp://gdc.cddis.eosdis.nasa.gov/gps/products/ionex\n') return 0,0,0,0,0,0,0,0,[0],'' else: print('JPL Rapid product file: '+igs_file+' already available in current working directory.') else: print('IGS Rapid product file: '+igs_file+' already available in current working directory.') else: print('IGS Final product file: '+igs_file+' already available in current working directory.') if igs_file.startswith('igs'): tec_type = 'IGS_Final_Product' elif igs_file.startswith('igr'): tec_type = 'IGS_Rapid_Product' elif igs_file.startswith('jpr'): tec_type = 'JPL_Rapid_Product' ## ========================================================================= ## ## The following section reads the lines of the ionex file for 1 day ## (13 maps total) into an array a[]. It also retrieves the thin-shell ## ionosphere height used by IGS, the lat./long. spacing, etc. for use ## later in this script. ## ## ========================================================================= print('Transforming IONEX data format to a TEC/DTEC array for '+str(ymd_date)) ## Opening and reading the IONEX file into memory as a list linestring = open(igs_file, 'r').read() LongList = linestring.split('\n') ## Create two lists without the header and only with the TEC and DTEC maps (based on code from ionFR.py) AddToList = 0 TECLongList = [] DTECLongList = [] for i in range(len(LongList)-1): ## Once LongList[i] gets to the DTEC maps, append DTECLongList if LongList[i].split()[-1] == 'MAP': if LongList[i].split()[-2] == 'RMS': AddToList = 2 if AddToList == 1: TECLongList.append(LongList[i]) if AddToList == 2: DTECLongList.append(LongList[i]) ## Determine the number of TEC/DTEC maps if LongList[i].split()[-1] == 'FILE': if LongList[i].split()[-3:-1] == ['MAPS','IN']: num_maps = float(LongList[i].split()[0]) ## Determine the shell ionosphere height (usually 450 km for IGS IONEX files) if LongList[i].split()[-1] == 'DHGT': ion_H = float(LongList[i].split()[0]) ## Determine the range in lat. and long. in the ionex file if LongList[i].split()[-1] == 'DLAT': start_lat = float(LongList[i].split()[0]) end_lat = float(LongList[i].split()[1]) incr_lat = float(LongList[i].split()[2]) if LongList[i].split()[-1] == 'DLON': start_long = float(LongList[i].split()[0]) end_long = float(LongList[i].split()[1]) incr_long = float(LongList[i].split()[2]) ## Find the end of the header so TECLongList can be appended if LongList[i].split()[0] == 'END': if LongList[i].split()[2] == 'HEADER': AddToList = 1 ## Variables that indicate the number of points in Lat. and Lon. points_long = ((end_long - start_long)/incr_long) + 1 points_lat = ((end_lat - start_lat)/incr_lat) + 1 ## Note that incr_lat is defined as '-' here number_of_rows = int(np.ceil(points_long/16)) ## Note there are 16 columns of data in IONEX format ## 4-D array that will contain TEC & DTEC (a[0] and a[1], respectively) values a = np.zeros((2,int(points_long),int(points_lat),int(num_maps))) ## Selecting only the TEC/DTEC values to store in the 4-D array. for Titer in range(2): counterMaps = 1 UseList = [] if Titer == 0: UseList = TECLongList elif Titer == 1: UseList = DTECLongList for i in range(len(UseList)): ## Pointing to first map (out of 13 maps) then by changing 'counterMaps' the other maps are selected if UseList[i].split()[0] == ''+str(counterMaps)+'': if UseList[i].split()[-4] == 'START': ## Pointing to the starting Latitude then by changing 'counterLat' we select TEC data ## at other latitudes within the selected map counterLat = 0 newstartLat = float(str(start_lat)) for iLat in range(int(points_lat)): if UseList[i+2+counterLat].split()[0].split('-')[0] == ''+str(newstartLat)+'': ## Adding to array a[] a line of Latitude TEC data counterLon = 0 for row_iter in range(number_of_rows): for item in range(len(UseList[i+3+row_iter+counterLat].split())): a[Titer,counterLon,iLat,counterMaps-1] = UseList[i+3+row_iter+counterLat].split()[item] counterLon = counterLon + 1 if '-'+UseList[i+2+counterLat].split()[0].split('-')[1] == ''+str(newstartLat)+'': ## Adding to array a[] a line of Latitude TEC data. Same chunk as above but ## in this case we account for the TEC values at negative latitudes counterLon = 0 for row_iter in range(number_of_rows): for item in range(len(UseList[i+3+row_iter+counterLat].split())): a[Titer,counterLon,iLat,counterMaps-1] = UseList[i+3+row_iter+counterLat].split()[item] counterLon = counterLon + 1 counterLat = counterLat + row_iter + 2 newstartLat = newstartLat + incr_lat counterMaps = counterMaps + 1 ## ========================================================================= ## ## The section creates a new array that is a copy of a[], but with the lower ## left-hand corner defined as the initial element (whereas a[] has the ## upper left-hand corner defined as the initial element). This also ## accounts for the fact that IONEX data is in 0.1*TECU. ## ## ========================================================================= ## The native sampling of the IGS maps minutes incr_time = 24*60/(int(num_maps)-1) tec_array = np.zeros((2,int(points_long),int(points_lat),int(num_maps))) for Titer in range(2): incr = 0 for ilat in range(int(points_lat)): tec_array[Titer,:,ilat,:] = 0.1*a[Titer,:,int(points_lat)-1-ilat,:] return points_long,points_lat,start_long,end_lat,incr_long,np.absolute(incr_lat),incr_time,num_maps,tec_array,tec_type def check_existence(file_prefix): """ ## ============================================================================= ## ## Checks to see if the IGS or MAPGPS file for a given day already exists and ## returns the file name prefix. Primarily used to ensure we only call the data ## and make the CASA table once. ## ## ============================================================================= ## ## Inputs: ## file_prefix type = string This is the prefix for the file name to ## search for in the current directorty ## IGS form: 'igsg'+'ddd'+'0.'+'yy'+'i' ## ex: August 6, 2011 is 'igsg2180.11i' ## MAPGPS form: 'gps'+'yy'+'mm'+'dd' ## ex: August 6, 2011 is 'gps110806' ## ## Returns: ## The name of the file it located. ## ## ============================================================================= """ file_name = '' return_file = '' for file_name in [doc for doc in os.listdir(workDir)]: if (file_prefix.startswith('igs') and file_name.startswith('igs')): if file_name.startswith(file_prefix): return_file = file_prefix if (file_prefix.startswith('igr') and file_name.startswith('igr')): if file_name.startswith(file_prefix): return_file = file_prefix if (file_prefix.startswith('jpr') and file_name.startswith('jpr')): if file_name.startswith(file_prefix): return_file = file_prefix if (file_prefix.startswith('gps') and file_name.startswith('gps')): if (file_name.startswith(file_prefix) and file_name.endswith('.txt')): return_file = file_name[:-4] if (file_prefix.endswith('_MAPGPS_Data') and file_name.endswith('_MAPGPS_Data.tab')): if (file_name.startswith(file_prefix) and file_name.endswith('.tab')): return_file = file_prefix return return_file def make_image(prefix,ref_long,ref_lat,ref_time,incr_long,incr_lat,incr_time,tec_array,tec_type,appendix=''): """ ## ============================================================================= ## ## Creates a new image file with the TEC data and then returns the image name. ## This also sets up the reference frame for use at the C++ level. ## ## ============================================================================= ## ## Inputs: ## prefix type = string Full file name for use in naming the image ## IGS form: 'igsg'+'ddd'+'0.'+'yy'+'i' ## ex: August 6, 2011 is 'igsg2180.11i' ## MAPGPS form: 'gps'+'yy'+'mm'+'dd'+'.###' ## ex: August 17, 2011 is 'gps110817g.001' ## ref_long type = float Reference long. (deg) for setting coordinates ## ref_lat type = float Reference lat. (deg) for setting coordinates ## ref_time type = float Reference time (s) for setting coordinates, ## UT 0 on the first day ## incr_long type = float Increment by which longitude increases ## IGS data: 5 deg ## MAPGPS data: 1 deg ## incr_lat type = float Increment by which latitude increases ## IGS data: 2.5 deg ## MAPGPS data: 1 deg ## incr_time type = float Increment by which time increases ## IGS data: 120 min ## MAPGPS data: 5 min ## tec_array type = array 3D array with axes consisting of ## [long.,lat.,time] giving the TEC in TECU ## tec_type type = string Specifies the origin of TEC data as a ## CASA table keyword ## appendix type = string Appendix to add to the end of the image name ## ## Returns: ## The name of the TEC map image ## ## ============================================================================= """ print('Making image: '+str(prefix+appendix)+'.im') ## Set the coordinate system for the TEC image cs0=cs.newcoordsys(linear=3) cs0.setnames(value='Long Lat Time') cs0.setunits(type='linear',value='deg deg s',overwrite=True) cs0.setreferencevalue([ref_long,ref_lat,ref_time]) cs0.setincrement(type='linear',value=[incr_long,incr_lat,incr_time]) ## Make and view the TEC image imname=prefix+appendix+'.im' ia.fromarray(outfile=imname,pixels=tec_array,csys=cs0.torecord(),overwrite=True) ia.summary() ## ia.statistics() ia.close() ## Specify in the image where the TEC data came from tb.open(imname,nomodify=False) tb.putkeyword('TYPE',tec_type) tb.close() return imname def ztec_value(my_long,my_lat,points_long,points_lat,ref_long,ref_lat,incr_long,incr_lat,incr_time,ref_start,ref_end,num_maps,tec_array,PLOT=False,PLOTNAME=''): """ ## ============================================================================= ## ## Determine the TEC value for the coordinates given at every time sampling of ## the TEC. This locates the 4 points in the IONEX grid map which surround the ## coordinate for which you want to calculate the TEC value. ## ## ============================================================================= ## ## Inputs: ## my_long type = float Long. (deg) at which this interpolates TEC ## my_lat type = float Lat. (deg) at which this interpolates TEC ## points_long type = integer Total number of points in long. (deg) ## in the TEC map ## points_lat type = integer Total number of points in lat. (deg) ## in the TEC map ## ref_long type = float Initial value for the longitude (deg) ## ref_lat type = float Initial value for the latitude (deg) ## incr_long type = float Increment by which longitude increases ## IGS data: 5 deg ## MAPGPS data: 1 deg ## incr_lat type = float Increment by which latitude increases ## IGS data: 2.5 deg ## MAPGPS data: 1 deg ## incr_time type = float Increment by which time increases ## IGS data: 120 min ## MAPGPS data: 5 min ## ref_start type = float Beginning of observations (in seconds) ## ref_end type = float End of observations (in seconds) ## num_maps type = integer Number of maps (or time samples) of TEC ## tec_array type = array 3D array with axes consisting of ## [long.,lat.,time] giving TEC in TECU ## PLOT type = boolean Determines whether to plot or return the ## TEC time series of the local long./lat. ## ## Returns: ## site_tec type = array 2D array containing the TEC/DTEC values ## for the local long./lat. ## ## ============================================================================= """ indexLat = 0 indexLon = 0 n = 0 m = 0 ## Find the corners of the grid that surrounds the local long./lat. for lon in range(int(points_long)): if (my_long > (ref_long + (n+1)*incr_long) and my_long <= (ref_long + (n+2)*incr_long)) : lowerIndexLon = n + 1 higherIndexLon = n + 2 n = n + 1 for lat in range(int(points_lat)): if (my_lat > (ref_lat + (m+1)*incr_lat) and my_lat <= (ref_lat + (m+2)*incr_lat)) : lowerIndexLat = m + 1 higherIndexLat = m + 2 m = m + 1 ## Using the 4-point formula indicated in the IONEX manual, find the TEC value at the local coordinates diffLon = my_long - (ref_long + lowerIndexLon*incr_long) WLON = diffLon/incr_long diffLat = my_lat - (ref_lat + lowerIndexLat*incr_lat) WLAT = diffLat/incr_lat site_tec = np.zeros((2,num_maps)) for Titer in range(2): for m in range(num_maps): site_tec[Titer,m] = (1.0-WLAT)*(1.0-WLON)*tec_array[Titer,lowerIndexLon,lowerIndexLat,m] +\ WLON*(1.0-WLAT)*tec_array[Titer,higherIndexLon,lowerIndexLat,m] +\ (1.0-WLON)*WLAT*tec_array[Titer,lowerIndexLon,higherIndexLat,m] +\ WLON*WLAT*tec_array[Titer,higherIndexLon,higherIndexLat,m] if PLOT == True: ## Set axis label size for the plots rc('xtick', labelsize=15) rc('ytick', labelsize=15) plottimes = [x*incr_time for x in range(num_maps)] plt.interactive(False) plt.clf() plt.errorbar(plottimes,site_tec[0],site_tec[1]) plt.axvspan(ref_start/60.0, ref_end/60.0, facecolor='r', alpha=0.5) plt.xlabel(r'$\mathrm{Time}$ $\mathrm{(minutes)}$', fontsize=20) plt.ylabel(r'$\mathrm{TEC}$ $\mathrm{(TECU)}$', fontsize=20) plt.title(r'$\mathrm{TEC}$ $\mathrm{values}$ $\mathrm{for}$ $\mathrm{Long.}$ $\mathrm{=}$ '+\ '$\mathrm{'+str(my_long)+'}$ / $\mathrm{Lat.}$ $\mathrm{=}$ $\mathrm{'+str(my_lat)+'}$',\ fontsize=20) plt.axis([min(plottimes),max(plottimes),0,1.1*max(site_tec[0])]) if len(PLOTNAME)>0: plt.savefig( PLOTNAME ) if PLOT == False: return site_tec def test_IONEX_connection(location): """ ## ============================================================================= ## ## Determines whether the machine is connected to the internet or not. Also ## used to determine whether a given IONEX file exists. ## ## ============================================================================= ## ## Inputs: ## location type = string website/online file to attempt to access ## ## Returns: ## True if the website/online file is accessible and False if not ## ## ============================================================================= """ try: ok = os.system('curl -u anonymous:casa-feedback@nrao.edu --ftp-ssl -Isf -o/dev/null '+location) if ok==0: return True else: print(location+' unavailable! (or no internet?)') return False except: print(location+' unavailable! (or no internet?)') return False def convert_MAPGPS_TEC(ms_name,mad_data_file,ref_time,ref_start,ref_end,plot_vla_tec,im_name): """ ## ============================================================================= ## ## This opens the MAPGPS Data table and selects a subset of TEC/DTEC values ## within a 15 deg square of the VLA. This then plots the zenith TEC/DTEC at the ## VLA site and makes the TEC map for use at the C++ level. We chose to deal ## with the MAPGPS data in this separate fashion because there are large ## 'gaps' in the data where no TEC/DTEC values exist. Consequently, we use the ## filled in CASA table to produce a TEC map and can not simply ## concatenate arrays. ## ## ============================================================================= ## ## Inputs: ## ms_name type = string Name of the measurement set for which to ## acquire TEC/DTEC data ## mad_data_file type = string Name of the MAPGPS TEC/DTEC data table ## ref_time type = float Reference time (s) for setting the ## coordinates, UT 0 on the first day ## plot_vla_tec type = boolean When True, this will open a plot of the ## interpolated TEC/DTEC at the VLA. ## im_name type = string Name of the output TEC Map optionally ## specified by the user ## ## Returns: ## Opens a plot showing the zenith TEC/DTEC at the VLA (if plot_vla_tec=True) ## and the name of the CASA image file containing the TEC map. ## ## ============================================================================= """ ## Only retrieve data in a 15x15 deg. patch centered (more or less) at the VLA tb.open(mad_data_file+'.tab') st0=tb.query('GDLAT>19 && GDLAT<49 && GLON>-122 && GLON<-92', ## If you want ALL the data to make a global map, use the line below: #st0=tb.query('GDLAT>-90. && GDLAT<90. && GLON>-180. && GLON<180', name='tecwindow') utimes=pylab.unique(st0.getcol('UT1_UNIX')) ulat=pylab.unique(st0.getcol('GDLAT')) ulong=pylab.unique(st0.getcol('GLON')) points_lat=len(ulat) points_long=len(ulong) num_maps=len(utimes) ## Initialize the array which will be used to make the image tec_array=pylab.zeros((2,points_long,points_lat,num_maps),dtype=float) minlat=min(ulat) minlong=min(ulong) print('rows'+str(len(utimes))) itime=0 for t in utimes: st1=st0.query('UT1_UNIX=='+str(t),name='bytime') n=st1.nrows() if itime%100==0: print(str(itime)+' '+str(n)) ilong=st1.getcol('GLON')-minlong ilat=st1.getcol('GDLAT')-minlat itec=st1.getcol('TEC') idtec=st1.getcol('DTEC') for i in range(n): tec_array[0,int(ilong[i]),int(ilat[i]),itime]=itec[i] tec_array[1,int(ilong[i]),int(ilat[i]),itime]=idtec[i] st1.close() ## Simply interpolate to cull as many zeros as possible ## (median of good neighbors, if at least four of them) thistec_array=tec_array[:,:,:,itime].copy() thisgood=thistec_array[0]>0.0 for i in range(1,points_long-1): for j in range(1,points_lat-1): if not thisgood[i,j]: mask=thisgood[(i-1):(i+2),(j-1):(j+2)] if pylab.sum(mask)>4: #print(str(itime)+' '+str(i)+' '+str(j)+str(pylab.sum(mask))) tec_array[0,i,j,itime]=pylab.median(thistec_array[0,(i-1):(i+2),(j-1):(j+2)][mask]) tec_array[1,i,j,itime]=pylab.median(thistec_array[1,(i-1):(i+2),(j-1):(j+2)][mask]) itime+=1 st0.close() tb.close() ztec_value(-107.6184,34.0790,points_long,points_lat,minlong,minlat,1,\ 1,5,ref_start,ref_end,int(num_maps),tec_array,plot_vla_tec) ## ref_time + 150 accounts for the fact that the MAPGPS map starts at 00:02:30 UT, not 00:00:00 UT if im_name == '': prefix = ms_name else: prefix = im_name CASA_image = make_image(prefix,minlong,minlat,ref_time+150.0,1,1,5*60,tec_array[0],'MAPGPS',appendix = '.MAPGPS_TEC') return CASA_image def make_CASA_table(file_name): """ ## ============================================================================= ## ## Makes a CASA table for the MAPGPS file for a given day. ## It requires the 'madrigal.head' file be in the working directory. ## ## ============================================================================= ## ## Inputs: ## file_name type = string This is the full MAPGPS file name ## MAPGPS form: 'gps'+'yy'+'mm'+'dd'+'.###' ## ex: August 17, 2011 is 'gps110817g.001' ## ## ============================================================================= """ os.system('rm -rf '+file_name+'.tab') tb.fromascii(tablename=file_name+'.tab',asciifile=file_name+'.txt',headerfile='madrigal.head',sep=" ", commentmarker='^YEAR MONT') # this will work when tb.fromascii is fixed... # (making the headerfile unnecessary) #tb.fromascii('madtest.tab','madtest.txt',sep=" ", # columnnames=['YEAR','MONTH','DAY','HOUR','MIN','SEC', # 'UT1_UNIX','UT2_UNIX','RECNO', # 'GDLAT','GLON','TEC','DTEC'], # datatypes=['I','I','I','I','I','I','I','I','I','R','R','R','R'], # autoheader=F,commentmarker='^YEAR MONT')