#!/usr/bin/env python # Last modified: Time-stamp: <2008-07-31 17:18:05 haines> """ how to parse data, and assert what data and info goes into creating and updating monthly netcdf files Nortek/AWAC processed adcp 2-D power spectrum (wds) as function of frequency and direction parser : sample date and time and pressure from .wap, energy spectrum m^2/Hz from .was, normalized energy/deg from .wds based on George Voulgaris' matlab script (version 8, Feb 14, 2005, polar_waves_cur_rdi.m) and additional parameters. creator : lat, lon, z, time, freq, dir, Sxx(time, freq, dir), Sf(time, freq), Stheta(time, dir), Stheta_swell(time, dir), Stheta_wind(time, dir), Hs, Hs_swell, Hs_wind, Tp, Tp_swell, Tp_wind, Tm, Tm_swell, Tm_wind, Dp, Dp_swell, Dp_wind, Dm, Dm_swell, Dm_wind, updater : time, Sxx(time, freq, dir), Sf(time, freq), Stheta(time, dir), Stheta_swell(time, dir), Stheta_wind(time, dir), Hs, Hs_swell, Hs_wind, Tp, Tp_swell, Tp_wind, Tm, Tm_swell, Tm_wind, Dp, Dp_swell, Dp_wind, Dm, Dm_swell, Dm_wind, check that freq and dir have not changed from what is in current NetCDF file Examples -------- >> (parse, create, update) = load_processors(module_name_without_dot_py) For example, >> (parse, create, update) = load_processors('proc_rdi_logdata_adcp') or >> si = get_config(cn+'.sensor_info') >> (parse, create, update) = load_processors(si['adcp']['proc_module']) Then use the generic name of processor to parse data, create or update monthly output file >> lines = load_data(filename) >> data = parse(platform_info, sensor_info, lines) >> create(platform_info, sensor_info, data) or >> update(platform_info, sensor_info, data) """ from raw2proc import * from procutil import * from ncutil import * now_dt = datetime.utcnow() now_dt.replace(microsecond=0) def parser(platform_info, sensor_info, lines): """ parse and assign wave spectra data from RDI ADCP Dspec and compute wave statistics and parameters Notes ----- 1. adapted from polar_waves_cur_rdi.m (Version 8 - February 14, 2005) by George Voulgaris Coastal Processes & Sediment Dynamics Lab Department of Geological Sciences University of South Carolina, Columbia, SC 29205 Email: gvoulgaris@geol.sc.edu 2. This parser requires date/time be parsed from .wap for each spectum sample in .wds, strip .wds on input filename and load and parse .wap here. If .wap with same name not available, then use sample per hour starting at time parsed from filename. 3. The .wds contains several bursts of full directional wave spectrum. One for each hour in .wap. Each directional burst is formatted in .wds file as each row is a one frequency, default [0.02:0.01:0.99] or [0.02:0.01:0.49]. Each column is descretized by 4 degrees 0:4:356 as degrees (faxed doc also states that freq's could be reported to .was file, but I didn't find this so to be true) """ import numpy from datetime import datetime from time import strptime # get sample datetime from filename fn = sensor_info['fn'] sample_dt_start = filt_datetime(fn)[0] # try getting sample date/times from .wap wap_fn = os.path.splitext(fn)[0] + ".wap" if os.path.exists(wap_fn): wap_lines = load_data(wap_fn) data = { 'dt' : numpy.array(numpy.ones((len(wap_lines),), dtype=object)*numpy.nan), 'time' : numpy.array(numpy.ones((len(wap_lines),), dtype=long)*numpy.nan), 'press' : numpy.array(numpy.ones((len(wap_lines),), dtype=float)*numpy.nan), } i=0 for line in wap_lines: # split line and parse float and integers wap = [] sw = re.split(' ', line) for s in sw: m = re.search(REAL_RE_STR, s) if m: wap.append(float(m.groups()[0])) # get sample datetime from data sample_str = '%02d-%02d-%4d %02d:%02d:%02d' % tuple(wap[0:6]) if sensor_info['utc_offset']: sample_dt = scanf_datetime(sample_str, fmt='%m-%d-%Y %H:%M:%S') + \ timedelta(hours=sensor_info['utc_offset']) else: sample_dt = scanf_datetime(sample_str, fmt='%m-%d-%Y %H:%M:%S') # these items can also be teased out of raw adcp but for now get from config file # th = sensor_info['transducer_ht'] # Transducer height above bottom (meters) # pressure (dbar) converted to water depth pressure = wap[17] # pressure (dbar) at tranducer height (?) # water_depth = th + sw_dpth(pressure, lat) data['dt'][i] = sample_dt data['time'][i] = dt2es(sample_dt) data['press'][i] = pressure # dbar i=i+1 else: print "error: No corresponding .wap file" print " .... skipping %s" % (fn,) return data # assign specific fields nbursts = len(data['dt']) Df = 0.01 # (Hz) f = numpy.arange(0.02, 0.99, Df) nfreq = len(f) # Number of frequencies (no units) # Did we get the number of data rows that we expected? Should equal nfreq n = int(len(lines)/nfreq) if n != nbursts: print "Number of data rows %d does not match expected number %d" % (n, nbursts) print " .... skipping %s" % (fn,) return data Dtheta = 1.0 # degrees D = numpy.arange(0.0, 360.0, 4) D = numpy.mod(D,360) ndir = len(D) # Number of directions (no units) # now get power spectra from .was was_fn = os.path.splitext(fn)[0] + ".was" was_Sf = numpy.array(numpy.ones((nbursts,nfreq), dtype=float)*numpy.nan) if os.path.exists(was_fn): was_lines = load_data(was_fn) i=0 # first line is freq label for each column start at [1] for line in was_lines[1:]: # split line and parse float and integers was = [] sw = re.split(' ', line) for s in sw: m = re.search(REAL_RE_STR, s) if m: was.append(float(m.groups()[0])) # just the frequencies we have in directional spectra [0:nfreq] was_Sf[i] = was[0:nfreq] # (m^2/Hz) non-directional power spectrum for each sample time i=i+1 else: print "error: No corresponding .was file" print " .... skipping %s" % (fn,) return data # add these keys, value pairs to dictionary "data" already setup after reading .wap data['dirs'] = numpy.array(numpy.ones((ndir,), dtype=float)*numpy.nan) data['freqs'] = numpy.array(numpy.ones((nfreq,), dtype=float)*numpy.nan) data['Sxx'] = numpy.array(numpy.ones((nbursts,nfreq,ndir), dtype=float)*numpy.nan) data['Sf'] = numpy.array(numpy.ones((nbursts,nfreq), dtype=float)*numpy.nan) data['Stheta'] = numpy.array(numpy.ones((nbursts,ndir), dtype=float)*numpy.nan) data['Stheta_swell'] = numpy.array(numpy.ones((nbursts,ndir), dtype=float)*numpy.nan) data['Stheta_wind'] = numpy.array(numpy.ones((nbursts,ndir), dtype=float)*numpy.nan) data['Hs'] = numpy.array(numpy.ones((nbursts,), dtype=float)*numpy.nan) data['Hs_swell'] = numpy.array(numpy.ones((nbursts,), dtype=float)*numpy.nan) data['Hs_wind'] = numpy.array(numpy.ones((nbursts,), dtype=float)*numpy.nan) data['Tm'] = numpy.array(numpy.ones((nbursts,), dtype=float)*numpy.nan) data['Tm_swell'] = numpy.array(numpy.ones((nbursts,), dtype=float)*numpy.nan) data['Tm_wind'] = numpy.array(numpy.ones((nbursts,), dtype=float)*numpy.nan) data['Tp'] = numpy.array(numpy.ones((nbursts,), dtype=float)*numpy.nan) data['Tp_swell'] = numpy.array(numpy.ones((nbursts,), dtype=float)*numpy.nan) data['Tp_wind'] = numpy.array(numpy.ones((nbursts,), dtype=float)*numpy.nan) data['Dm'] = numpy.array(numpy.ones((nbursts,), dtype=float)*numpy.nan) data['Dm_swell'] = numpy.array(numpy.ones((nbursts,), dtype=float)*numpy.nan) data['Dm_wind'] = numpy.array(numpy.ones((nbursts,), dtype=float)*numpy.nan) data['Dp'] = numpy.array(numpy.ones((nbursts,), dtype=float)*numpy.nan) data['Dp_swell'] = numpy.array(numpy.ones((nbursts,), dtype=float)*numpy.nan) data['Dp_wind'] = numpy.array(numpy.ones((nbursts,), dtype=float)*numpy.nan) # for each burst read nfreq lines for j in range(nbursts): i = 0 Sxx = numpy.array(numpy.ones((nfreq,ndir), dtype=float)*numpy.nan) # each line is a freq, each column is a direction for line in lines[j*nfreq:nfreq*(j+1)]: wds = [] # split line and parse float and integers sw = re.split(' ', line) for s in sw: m = re.search(REAL_RE_STR, s) if m: wds.append(float(m.groups()[0])) # wds[] is in units of Normalized-Energy/degree from .wds # use power (m^2/Hz) at same time and freq from .was to get units of m^2/Hz/deg if len(wds) == ndir: Sxx[i,:] = numpy.array(wds[:])*was_Sf[j,i] # cross spectrum as m^2/Hz/deg i = i+1 # Did we get the number of data rows that we expected? Should equal nfreq if i != nfreq: print "Number of data rows %d does not match expected number %d" % (i, nfreq) # NOTE make fupper location dependent?? (add to config_files??) fupper = 0.65 # upper freq limit 0.65 Hz or wave periods less than T~1.538s iswell = f<=1/10. # swell band for T>10s iwind = (f>1/10.) * (f<=fupper) # wind band 1/fupper