"""
Utilities for reading ARM KAZR spectra files.
"""
import datetime
import re
import numpy as np
try:
import xarray as xr
_XARRAY_AVAILABLE = True
except ImportError:
_XARRAY_AVAILABLE = False
from ..config import FileMetadata
from ..core.radar_spectra import RadarSpectra
from ..exceptions import MissingOptionalDependency
from ..io.common import _test_arguments
KAZR_SPECTRA_FIELD_NAMES = {'spectra': 'spectra'}
[docs]def read_kazr_spectra(filename, field_names=None, additional_metadata=None,
file_field_names=False, exclude_fields=None,
include_fields=None, **kwargs):
"""
Read a ARM KAZR spectra netCDF file.
Parameters
----------
filename : str
Name of KAZR spectra netCDF file to read data from.
field_names : dict, optional
Dictionary mapping field names in the file names to radar_spectra field names.
Unlike other read functions, fields not in this dictionary or having a
value of None are still included in the radar_spectra.fields dictionary, to
exclude them use the `exclude_fields` parameter. Fields which are
mapped by this dictionary will be renamed from key to value.
additional_metadata : dict of dicts, optional
This parameter is not used, it is included for uniformity.
file_field_names : bool, optional
True to force the use of the field names from the file in which
case the `field_names` parameter is ignored. False will use to
`field_names` parameter to rename fields.
exclude_fields : list or None, optional
List of fields to exclude from the radar spectra object. This is applied
after the `file_field_names` and `field_names` parameters. Set
to None to include all fields specified by include_fields.
include_fields : list or None, optional
List of fields to include from the radar spectra object. This is applied
after the `file_field_names` and `field_names` parameters. Set
to None to include all fields not specified by exclude_fields.
delay_field_loading : bool
True to delay loading of field data from the file until the 'data'
key in a particular field dictionary is accessed. In this case
the field attribute of the returned RadarSpectra object will contain
LazyLoadDict objects not dict objects. Delayed field loading will not
provide any speedup in file where the number of gates vary between
rays (ngates_vary=True) and is not recommended.
Returns
-------
radar_spectra : RadarSpectra
RadarSpectra object.
"""
if not _XARRAY_AVAILABLE:
raise MissingOptionalDependency(
"Xarray is required to read KAZR spectra files "
"but is not installed!")
# test for non empty kwargs
_test_arguments(kwargs)
# create metadata retrieval object
if field_names is None:
field_names = KAZR_SPECTRA_FIELD_NAMES
filemetadata = FileMetadata('kazr_spectra', field_names,
additional_metadata,
file_field_names, exclude_fields)
# read the data
xrobj = xr.open_dataset(filename)
# retrieve the correct time format
# time format changed in later KAZR files
try:
times = [(datetime.timedelta(seconds=sec) +
datetime.datetime.utcfromtimestamp(
xrobj.base_time.values.astype("O")/1e9))
for sec in xrobj.time_offset.values]
except TypeError:
ts = [(i - np.datetime64('1970-01-01T00:00:00Z')) / np.timedelta64(1, 's')
for i in xrobj.time_offset.values]
bs = (
xrobj.base_time.values - np.datetime64(
'1970-01-01T00:00:00Z')) / np.timedelta64(1, 's')
times = [(datetime.timedelta(seconds=sec) +
datetime.datetime.utcfromtimestamp(bs.astype("O")/1e9))
for sec in ts]
times = np.array(times)
time_dict = filemetadata('time')
base_time = ('seconds since ' + datetime.datetime.strftime(
times[0], '%Y-%m-%DT%H:%M:%SZ'))
time_array = [(x-times[0]).seconds for x in times]
time_dict['units'] = base_time
time = xr.DataArray(np.array(time_array), attrs=time_dict, dims='time')
rng_array = np.squeeze(xrobj.range.values)
rng_dict = filemetadata('range')
_range = xr.DataArray(rng_array, attrs=rng_dict, dims='range')
#metadata = filemetadata('metadata')
# Retrieve the ARM site info
if 'nsa' in filename:
xrobj.attrs['site_id'] = 'nsa'
elif 'ena' in filename:
xrobj.attrs['site_id'] = 'ena'
else:
xrobj.attrs['site_id'] = 'sgp'
lat_dict = filemetadata('latitude')
lon_dict = filemetadata('longitude')
alt_dict = filemetadata('altitude')
lat, lon, alt = get_kazr_location(xrobj.attrs['site_id'])
latitude = xr.DataArray(np.array(lat, dtype='float32'), attrs=lat_dict)
longitude = xr.DataArray(np.array(lon, dtype='float32'), attrs=lon_dict)
altitude = xr.DataArray(np.array(alt, dtype='float32'), attrs=alt_dict)
elevation_dict = filemetadata('elevation')
elevation = xr.DataArray(np.array(90.0*np.ones(len(time_array)),
dtype='float32'),
attrs=elevation_dict, dims='time')
azimuth_dict = filemetadata('azimuth')
azimuth = xr.DataArray(np.array(360.0*np.ones(len(time_array)),
dtype='float32'),
attrs=azimuth_dict, dims='time')
fix_agl_dict = filemetadata('fixed_angle')
fixed_angle = xr.DataArray(np.array(90.0, dtype='float32'),
attrs=fix_agl_dict)
sweep_number_dict = filemetadata('sweep_number')
sweep_number = xr.DataArray(np.array([1], dtype='int32'),
attrs=sweep_number_dict)
sweep_mode_dict = filemetadata('sweep_mode')
sweep_mode = xr.DataArray(np.array('spectra'),
attrs=sweep_mode_dict)
sweep_start_ray_index_dict = filemetadata('sweep_start_ray_index')
sweep_start_ray_index = xr.DataArray(np.array([0], dtype='int32'),
attrs=sweep_start_ray_index_dict)
sweep_end_ray_index_dict = filemetadata('sweep_end_ray_index')
sweep_end_ray_index = xr.DataArray(np.array([len(times)-1], dtype='int32'),
attrs=sweep_end_ray_index_dict)
spectra_array = [_get_spectra(xrobj, x) for x in xrobj.time.values]
spectras = np.stack(spectra_array)
c = 299792458
npulses_max = xrobj.speclength.values
wavelength = c/float(re.findall(r"[-+]?\d*\.\d+|\d+",
xrobj.radar_operating_frequency)[0])*1e-9
scan_type = 'vpt'
metadata = xrobj.attrs
metadata['wavelength'] = wavelength
velocity_dict = xrobj.velocity_bins.attrs
bins = xrobj.velocity_bins.values
velocity_bins = xr.DataArray(bins, attrs=velocity_dict,
dims='npulses_max')
xrobj.close()
return RadarSpectra(time, _range, spectras, metadata, scan_type,
latitude, longitude, altitude,
sweep_number, sweep_mode, fixed_angle,
sweep_start_ray_index, sweep_end_ray_index,
azimuth, elevation, npulses_max, velocity_bins)
def _get_spectra(xrobj, time):
""" Retrieves the spectra values using time and locator mask. """
the_spectra_locs = xrobj.locator_mask.sel(time=time).values
the_spectra_loc = np.zeros((len(the_spectra_locs),
len(xrobj.speclength.values)))
the_spectra_locs[np.isnan(the_spectra_locs)] = -9999.0
for i, locs in enumerate(the_spectra_locs):
if locs != -9999.0:
the_spectra_loc[i, :] = xrobj.spectra.values[int(locs), :]
else:
the_spectra_loc[i, :] = np.nan*np.ones(len(
xrobj.speclength.values))
return the_spectra_loc
def get_kazr_location(arm_site):
"""
Return the latitude, longitude and altitude of a ARM KAZR Radar.
Parameters
----------
arm_site : str
Three letter ARM site acroynm.
Returns
-------
lat, lon, alt : float
Latitude (in degrees), longitude (in degrees), and altitude
(in meters above mean sea level) of the KAZR radar.
"""
loc = ARM_SITES[arm_site.upper()]
return loc['lat'], loc['lon'], loc['alt']
ARM_SITES = {
"SGP": {'lat': 36.605, 'lon': -97.485, 'alt': 318},
"ENA": {'lat': 39.0916, 'lon': -28.0257, 'alt': 30},
"NSA": {'lat': 71.323, 'lon': -156.609, 'alt': 8}, }