NCCOOS Trac Projects: Top | Web | Platforms | Processing | Viz | Sprints | Sandbox | (Wind)

root/raw2proc/trunk/raw2proc/proc_nortek_wpa_adcp.py

Revision 213 (checked in by haines, 16 years ago)

ncutil--added functions; proc2latest--moved to procutil; configs--added the computed mean_water_depth where measured and mean_water_depth_time_period

Line 
1 #!/usr/bin/env python
2 # Last modified:  Time-stamp: <2008-10-16 17:02:54 haines>
3 """
4 how to parse data, and assert what data and info goes into
5 creating and updating monthly netcdf files
6
7 RDI/Wavesmon processed adcp current profile data
8
9 parser : sample date and time, currents, water temperature, pressure and water_depth
10
11 creator : lat, lon, z, time, ens, u, v, w, water_depth, water_temp (at tranducer depth), pressure
12 updator : time, ens, u, v, w, water_depth, water_temp (at tranducer depth), pressure
13
14
15 Examples
16 --------
17
18 >> (parse, create, update) = load_processors('proc_rdi_logdata_adcp')
19 or
20 >> si = get_config(cn+'.sensor_info')
21 >> (parse, create, update) = load_processors(si['adcp']['proc_module'])
22
23 >> lines = load_data(filename)
24 >> data = parse(platform_info, sensor_info, lines)
25 >> create(platform_info, sensor_info, data) or
26 >> update(platform_info, sensor_info, data)
27
28 """
29
30
31 from raw2proc import *
32 from procutil import *
33 from ncutil import *
34
35 import seawater
36
37 now_dt = datetime.utcnow()
38 now_dt.replace(microsecond=0)
39
40 def parser(platform_info, sensor_info, lines):
41     """
42     parse and assign ocean profile current data from Nortek AWAC ADCP Data
43
44     Notes
45     -----
46     1. This parser requires date/time be parsed from .wap for each to
47     get sig_wave_ht for determining depth of each bin and surface mask
48     and check time same as in .wpa file.
49
50     2. multiple profiles in one file separated by header w/time,
51        pitch, roll, heading, ducer pressure, bottom temp, top bin#
52        bottom bin# (??).  The profile data is several lines one for
53        each bin.
54    
55     MM DD YYYY HH MM SS  ERR STATUS BATT SNDSPD HDG  PITCH  ROLL  PRESS   WTEMP    ??  ?? TBIN BBIN
56     07 31 2008 23 54 00    0   48  18.2 1525.8 270.1  -2.4   0.2  10.503  21.64     0     0 3  34
57        1    0.9    0.071   24.04    0.029    0.065   -0.058 123 126 124
58        2    1.4    0.089  342.38   -0.027    0.085   -0.057 110 111 113
59        3    1.9    0.065  310.03   -0.050    0.042   -0.063 102 104 104
60        4    2.4    0.063   46.93    0.046    0.043   -0.045  93  95  99
61        5    2.9    0.049  355.33   -0.004    0.049   -0.047  87  89  92
62          ...
63      NBIN  DEPTH   SPEED    DIR       U         V       W    E1? E2? E3?
64        32   16.4    0.184  331.76   -0.087    0.162   -0.162  26  25  27
65        33   16.9    0.137  288.70   -0.130    0.044   -0.181  26  24  26
66        34   17.4    0.070   32.78    0.038    0.059   -0.248  25  25  26
67
68     3. not sure if depth column is hab or down from surface?
69
70
71     """
72
73     # get sample datetime from filename
74     fn = sensor_info['fn']
75     sample_dt_start = filt_datetime(fn)[0]
76
77     nbins = sensor_info['nbins']  # Number of bins in data
78     nbursts = len(lines)/(nbins+1)
79
80     data = {
81         'dt' : numpy.array(numpy.ones((nbursts,), dtype=object)*numpy.nan),
82         'time' : numpy.array(numpy.ones((nbursts,), dtype=long)*numpy.nan),
83         'z' : numpy.array(numpy.ones((nbins,), dtype=float)*numpy.nan),
84         'u' : numpy.array(numpy.ones((nbursts,nbins), dtype=float)*numpy.nan),
85         'v' : numpy.array(numpy.ones((nbursts,nbins), dtype=float)*numpy.nan),
86         'w' : numpy.array(numpy.ones((nbursts,nbins), dtype=float)*numpy.nan),
87         'e1' : numpy.array(numpy.ones((nbursts,nbins), dtype=int)*numpy.nan),
88         'e2' : numpy.array(numpy.ones((nbursts,nbins), dtype=int)*numpy.nan),
89         'e3' : numpy.array(numpy.ones((nbursts,nbins), dtype=int)*numpy.nan),
90         'wd' : numpy.array(numpy.ones((nbursts), dtype=float)*numpy.nan),
91         'wl' : numpy.array(numpy.ones((nbursts), dtype=float)*numpy.nan),
92         'water_temp' : numpy.array(numpy.ones((nbursts), dtype=float)*numpy.nan),
93         'pressure' : numpy.array(numpy.ones((nbursts), dtype=float)*numpy.nan),
94         }
95
96     # these items can also be teased out of raw adcp but for now get from config file
97     th = sensor_info['transducer_ht']  # Transducer height above bottom (meters)
98     bh = sensor_info['blanking_ht']    # Blanking height above Transducer (meters)
99     bin_size = sensor_info['bin_size'] # Bin Size (meters)
100    
101     # compute height for each bin above the bottom
102     bins = numpy.arange(1,nbins+1)
103     # bin_habs = (bins*bin_size+bin_size/2)+th+bh
104     bin_habs = (bins*bin_size+bin_size/2)+th+bh
105
106     # added by SH -- 15 Oct 2008
107     # raw2proc:ticket:27 adjust bin_habs along beam to nadir
108     # Nortek awac beam angle is fixed at 25 deg
109     # adjustment is cos(25 deg) (which is  approx .90*height)
110     bin_habs =  (bin_habs*numpy.cos(25.*numpy.pi/180))
111     iaboveblank = bin_habs > th+bh+(bin_size)
112
113     # current profile count
114     i = 0
115
116     wpa = []
117     for line in lines:
118         wpa = []
119         # split line and parse float and integers
120         sw = re.split(' ', line)
121         for s in sw:
122             m = re.search(REAL_RE_STR, s)
123             if m:
124                 wpa.append(float(m.groups()[0]))
125
126         if len(wpa)==19:                                                                             
127             # get sample datetime from data
128             sample_str = '%02d-%02d-%4d %02d:%02d:%02d' % tuple(wpa[0:6])
129             if  sensor_info['utc_offset']:
130                 sample_dt = scanf_datetime(sample_str, fmt='%m-%d-%Y %H:%M:%S') + \
131                             timedelta(hours=sensor_info['utc_offset'])
132             else:
133                 sample_dt = scanf_datetime(sample_str, fmt='%m-%d-%Y %H:%M:%S')
134            
135             # these items can also be teased out of raw adcp but for now get from config file
136             # th = sensor_info['transducer_ht']  # Transducer height above bottom (meters)
137            
138             error_code = int(wpa[6])
139             status_code = int(wpa[7])
140             battery_voltage = wpa[8] # volts
141             sound_speed = wpa[9]     # m/s
142             heading = wpa[10]        # deg
143             pitch = wpa[11]          # deg
144             roll = wpa[12]           # deg
145            
146             pressure = wpa[13]       # dbar
147             # pressure (dbar) converted to water depth
148             wd = th + seawater.depth(pressure, platform_info['lat']) # m
149             temperature = wpa[14]       # deg C
150
151             start_bin = int(wpa[17])     # first good bin from transducer (?)
152             wpa_nbins = int(wpa[18])     # Number of bins
153             # check this is same as in sensor_info
154
155             # initialize for new profile
156             hab = numpy.ones(nbins)*numpy.nan
157             spd = numpy.ones(nbins)*numpy.nan
158             dir = numpy.ones(nbins)*numpy.nan
159             u = numpy.ones(nbins)*numpy.nan
160             v = numpy.ones(nbins)*numpy.nan
161             w = numpy.ones(nbins)*numpy.nan
162             e1 = numpy.array(numpy.ones((nbins), dtype=int)*numpy.nan)
163             e2 = numpy.array(numpy.ones((nbins), dtype=int)*numpy.nan)
164             e3 = numpy.array(numpy.ones((nbins), dtype=int)*numpy.nan)
165
166         elif len(wpa)==10:
167             # current profile data at  each bin
168             bin_number = wpa[0]
169             j = wpa[0]-1
170             hab[j] = wpa[1]
171            
172             spd[j] = wpa[2] # m/s
173             dir[j] = wpa[3] # deg N
174
175             u[j] = wpa[4] # m/s
176             v[j] = wpa[5] # m/s
177             w[j] = wpa[6] # m/s
178
179             e1[j] = int(wpa[7]) # echo dB ??
180             e2[j] = int(wpa[8]) #
181             e3[j] = int(wpa[9]) #
182
183             # ibad = (current_spd==-32768) | (current_dir==-32768)
184             # current_spd[ibad] = numpy.nan
185             # current_dir[ibad] = numpy.nan
186
187             # if done reading profile, just read data for last bin
188             if bin_number==nbins:
189                 # compute water mask
190                 # if positive is up, in water is less than zero depth
191                 bin_depths =  (bin_habs)-(wd)
192                 iwater = bin_depths+bin_size/2 < 0
193                 iwater = iwater*iaboveblank
194                
195                 # use nominal water depth (MSL) averaged from full pressure record
196                 #  this should be checked/recalulated every so often
197                 z = bin_habs+platform_info['mean_water_depth']
198                
199                 data['dt'][i] = sample_dt # sample datetime
200                 data['time'][i] = dt2es(sample_dt) # sample time in epoch seconds
201                 data['z'] =  z
202                 data['wd'][i] = -1*wd
203                 data['wl'][i] = platform_info['mean_water_depth'] - (-1*wd)
204                 data['water_temp'][i] = temperature
205                 data['pressure'][i] = pressure
206                
207                 data['u'][i][iwater] =  u[iwater]
208                 data['v'][i][iwater] =  v[iwater]
209                 data['w'][i][iwater] =  w[iwater]
210
211                 data['e1'][i] =  e1
212                 data['e2'][i] =  e2
213                 data['e3'][i] =  e3
214
215                 # ready for next burst
216                 i = i+1
217             # if j+1==nbins
218         # if len(wpa)==19 elif ==10
219     # for line
220
221     return data
222  
223
224 def creator(platform_info, sensor_info, data):
225     #
226     #
227     title_str = sensor_info['description']+' at '+ platform_info['location']
228
229     if 'mean_water_depth' in platform_info.keys():
230         msl_str = platform_info['mean_water_depth']
231     else:
232         msl_str = 'None'
233     if 'mean_water_depth_time_period' in platform_info.keys():
234         msl_tp_str = platform_info['mean_water_depth_time_period']
235     else:
236         msl_tp_str = 'None'
237
238     global_atts = {
239         'title' : title_str,
240         'institution' : 'University of North Carolina at Chapel Hill (UNC-CH)',
241         'institution_url' : 'http://nccoos.unc.edu',
242         'institution_dods_url' : 'http://nccoos.unc.edu',
243         'metadata_url' : 'http://nccoos.unc.edu',
244         'references' : 'http://nccoos.unc.edu',
245         'contact' : 'Sara Haines (haines@email.unc.edu)',
246         #
247         'source' : 'fixed-profiler (acoustic doppler) observation',
248         'history' : 'raw2proc using ' + sensor_info['process_module'],
249         'comment' : 'File created using pycdf'+pycdfVersion()+' and numpy '+pycdfArrayPkg(),
250         # conventions
251         'Conventions' : 'CF-1.0; SEACOOS-CDL-v2.0',
252         # SEACOOS CDL codes
253         'format_category_code' : 'fixed-profiler',
254         'institution_code' : platform_info['institution'],
255         'platform_code' : platform_info['id'],
256         'package_code' : sensor_info['id'],
257         # institution specific
258         'project' : 'North Carolina Coastal Ocean Observing System (NCCOOS)',
259         'project_url' : 'http://nccoos.unc.edu',
260         # timeframe of data contained in file yyyy-mm-dd HH:MM:SS
261         # first date in monthly file
262         'start_date' : data['dt'][0].strftime("%Y-%m-%d %H:%M:%S"),
263         # last date in monthly file
264         'end_date' : data['dt'][-1].strftime("%Y-%m-%d %H:%M:%S"),
265         'release_date' : now_dt.strftime("%Y-%m-%d %H:%M:%S"),
266         #
267         'mean_water_depth' : msl_str,
268         'mean_water_depth_time_period' : msl_tp_str,
269         #
270         'creation_date' : now_dt.strftime("%Y-%m-%d %H:%M:%S"),
271         'modification_date' : now_dt.strftime("%Y-%m-%d %H:%M:%S"),
272         'process_level' : 'level1',
273         #
274         # must type match to data (e.g. fillvalue is real if data is real)
275         '_FillValue' : numpy.nan,
276         }
277
278     var_atts = {
279         # coordinate variables
280         'time' : {'short_name': 'time',
281                   'long_name': 'Time',
282                   'standard_name': 'time',
283                   'units': 'seconds since 1970-1-1 00:00:00 -0', # UTC
284                   'axis': 'T',
285                   },
286         'lat' : {'short_name': 'lat',
287              'long_name': 'Latitude',
288              'standard_name': 'latitude',
289              'reference':'geographic coordinates',
290              'units': 'degrees_north',
291              'valid_range':(-90.,90.),
292              'axis': 'Y',
293              },
294         'lon' : {'short_name': 'lon',
295                  'long_name': 'Longitude',
296                  'standard_name': 'longitude',
297                  'reference':'geographic coordinates',
298                  'units': 'degrees_east',
299                  'valid_range':(-180.,180.),
300                  'axis': 'Y',
301                  },
302         'z' : {'short_name': 'z',
303                'long_name': 'Height',
304                'standard_name': 'height',
305                'reference':'zero at mean-sea-level',
306                'positive' : 'up',
307                'units': 'm',
308                'axis': 'Z',
309                },
310         # data variables
311         'u': {'short_name' : 'u',
312               'long_name': 'East/West Component of Current',
313               'standard_name': 'eastward_current',
314               'units': 'm s-1',
315               'reference': 'clockwise from True East',
316               },
317         'v': {'short_name' : 'v',
318               'long_name': 'North/South Component of Current',
319               'standard_name': 'northward_current',                         
320               'units': 'm s-1',
321               'reference': 'clockwise from True North',
322               },
323         'w': {'short_name' : 'w',
324               'long_name': 'Vertical Component of Current',
325               'standard_name': 'upward_current',                         
326               'units': 'm s-1',
327               'reference': 'clockwise from True North',
328               },
329         'e1': {'short_name' : 'e1',
330               'long_name': 'Echo Beam 1 (??)',
331               'standard_name': 'beam_echo',
332               'units': 'dB',
333               },
334         'e2': {'short_name' : 'e2',
335               'long_name': 'Echo Beam 2 (??)',
336               'standard_name': 'beam_echo',
337               'units': 'dB',
338               },
339         'e3': {'short_name' : 'e3',
340               'long_name': 'Echo Beam 3 (??)',
341               'standard_name': 'beam_echo',
342               'units': 'dB',
343               },
344         'wd': {'short_name': 'wd',
345                'long_name': 'Water Depth',
346                'standard_name': 'water_depth',                         
347                'reference':'zero at surface',
348                'positive' : 'up',
349                'units': 'm',
350                },
351         'wl': {'short_name': 'wl',
352                'long_name': 'Water Level',
353                'standard_name': 'water_level',                         
354                'reference':'MSL',
355                'reference_to_MSL' : 0.,
356                'reference_MSL_datum' : platform_info['mean_water_depth'],
357                'reference_MSL_datum_time_period' : platform_info['mean_water_depth_time_period'],
358                'positive' : 'up',
359                'z' : 0.,
360                'units': 'm',
361                },
362         'pressure': {'short_name': 'p',
363                      'long_name': 'Pressure',
364                      'standard_name': 'pressure',                         
365                      'units': 'dbar',
366                      },
367         'water_temp': {'short_name': 'wtemp',
368                         'long_name': 'Water Temperature at Transducer',
369                         'standard_name': 'water_temperature',                         
370                         'units': 'deg C',
371                         },
372         }
373
374
375     # dimension names use tuple so order of initialization is maintained
376     dim_inits = (
377         ('ntime', NC.UNLIMITED),
378         ('nlat', 1),
379         ('nlon', 1),
380         ('nz', sensor_info['nbins'])
381         )
382    
383     # using tuple of tuples so order of initialization is maintained
384     # using dict for attributes order of init not important
385     # use dimension names not values
386     # (varName, varType, (dimName1, [dimName2], ...))
387     var_inits = (
388         # coordinate variables
389         ('time', NC.INT, ('ntime',)),
390         ('lat', NC.FLOAT, ('nlat',)),
391         ('lon', NC.FLOAT, ('nlon',)),
392         ('z',  NC.FLOAT, ('nz',)),
393         # data variables
394         ('u', NC.FLOAT, ('ntime', 'nz')),
395         ('v', NC.FLOAT, ('ntime', 'nz')),
396         ('w', NC.FLOAT, ('ntime', 'nz')),
397         ('e1', NC.INT, ('ntime', 'nz')),
398         ('e2', NC.INT, ('ntime', 'nz')),
399         ('e3', NC.INT, ('ntime', 'nz')),
400         ('wd', NC.FLOAT, ('ntime',)),
401         ('wl', NC.FLOAT, ('ntime',)),
402         ('pressure', NC.FLOAT, ('ntime',)),
403         ('water_temp', NC.FLOAT, ('ntime',)),
404         )
405
406     # subset data only to month being processed (see raw2proc.process())
407     i = data['in']
408    
409     # var data
410     var_data = (
411         ('lat',  platform_info['lat']),
412         ('lon', platform_info['lon']),
413         ('z', data['z']),
414         #
415         ('time', data['time'][i]),
416         ('u', data['u'][i]),
417         ('v', data['v'][i]),
418         ('w', data['w'][i]),
419         ('e1', data['e1'][i]),
420         ('e2', data['e2'][i]),
421         ('e3', data['e3'][i]),
422         ('wd', data['wd'][i]),
423         ('wl', data['wl'][i]),
424         ('pressure', data['pressure'][i]),
425         ('water_temp', data['water_temp'][i]),
426         )
427
428     return (global_atts, var_atts, dim_inits, var_inits, var_data)
429
430 def updater(platform_info, sensor_info, data):
431     #
432     global_atts = {
433         # update times of data contained in file (yyyy-mm-dd HH:MM:SS)
434         # last date in monthly file
435         'end_date' : data['dt'][-1].strftime("%Y-%m-%d %H:%M:%S"),
436         'release_date' : now_dt.strftime("%Y-%m-%d %H:%M:%S"),
437         #
438         'modification_date' : now_dt.strftime("%Y-%m-%d %H:%M:%S"),
439         }
440
441     # data variables
442     # update any variable attributes like range, min, max
443     var_atts = {}
444     # var_atts = {
445     #    'u': {'max': max(data.u),
446     #          'min': min(data.v),
447     #          },
448     #    'v': {'max': max(data.u),
449     #          'min': min(data.v),
450     #          },
451     #    }
452    
453     # subset data only to month being processed (see raw2proc.process())
454     i = data['in']
455
456     # data
457     var_data = (
458         ('time', data['time'][i]),
459         ('u', data['u'][i]),
460         ('v', data['v'][i]),
461         ('w', data['w'][i]),
462         ('e1', data['e1'][i]),
463         ('e2', data['e2'][i]),
464         ('e3', data['e3'][i]),
465         ('wd', data['wd'][i]),
466         ('wl', data['wl'][i]),
467         ('pressure', data['pressure'][i]),
468         ('water_temp', data['water_temp'][i]),
469         )
470
471     return (global_atts, var_atts, var_data)
472 #
Note: See TracBrowser for help on using the browser.