gpmmatch 1.5.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
gpmmatch/__init__.py ADDED
@@ -0,0 +1,34 @@
1
+ """
2
+ GPM volume matching package.
3
+
4
+ Volume matching of ground radar and GPM satellite data.
5
+ """
6
+
7
+ from .gpmmatch import (
8
+ NoRainError,
9
+ generate_filename,
10
+ get_radar_coordinates,
11
+ get_gr_reflectivity,
12
+ has_valid_data,
13
+ volume_matching,
14
+ vmatch_multi_pass,
15
+ MIN_SAMPLE_POINTS,
16
+ MIN_REFL_SAMPLES,
17
+ MAX_OFFSET_THRESHOLD,
18
+ )
19
+
20
+ __author__ = "Valentin Louf"
21
+ __email__ = "valentin.louf@bom.gov.au"
22
+
23
+ __all__ = [
24
+ "NoRainError",
25
+ "generate_filename",
26
+ "get_radar_coordinates",
27
+ "get_gr_reflectivity",
28
+ "has_valid_data",
29
+ "volume_matching",
30
+ "vmatch_multi_pass",
31
+ "MIN_SAMPLE_POINTS",
32
+ "MIN_REFL_SAMPLES",
33
+ "MAX_OFFSET_THRESHOLD",
34
+ ]
gpmmatch/correct.py ADDED
@@ -0,0 +1,416 @@
1
+ """
2
+ Various utilities for correction and conversion of satellite data.
3
+
4
+ @title: correct
5
+ @author: Valentin Louf <valentin.louf@bom.gov.au>
6
+ @institutions: Monash University and the Australian Bureau of Meteorology
7
+ @creation: 17/02/2020
8
+ @date: 12/05/2025
9
+
10
+ .. autosummary::
11
+ :toctree: generated/
12
+
13
+ compute_gaussian_curvature
14
+ convert_gpmrefl_grband_dfr
15
+ convert_gpmrefl_grband_phase_aware
16
+ correct_parallax
17
+ attenuation_correction_zphi
18
+ attenuation_correction_gunn_east
19
+ get_offset
20
+ grid_displacement
21
+ """
22
+
23
+ from typing import Tuple
24
+ import numpy as np
25
+ import xarray as xr
26
+
27
+
28
+ def compute_gaussian_curvature(lat0: float) -> float:
29
+ """
30
+ Determine the Earth's Gaussian radius of curvature at the radar
31
+ https://en.wikipedia.org/wiki/Earth_radius#Radii_of_curvature
32
+
33
+ Parameter:
34
+ ----------
35
+ lat0: float
36
+ Ground radar latitude.
37
+
38
+ Returns:
39
+ --------
40
+ ae: float
41
+ Scaled Gaussian radius.
42
+ """
43
+ # Major and minor radii of the Ellipsoid
44
+ a = 6378137.0 # Earth radius in meters
45
+ e2 = 0.0066943800
46
+ b = a * np.sqrt(1 - e2)
47
+
48
+ tmp = (a * np.cos(np.pi / 180 * lat0)) ** 2 + (b * np.sin(np.pi / 180 * lat0)) ** 2 # Denominator
49
+ an = (a**2) / np.sqrt(tmp) # Radius of curvature in the prime vertical (east–west direction)
50
+ am = (a * b) ** 2 / tmp**1.5 # Radius of curvature in the north–south meridian
51
+ ag = np.sqrt(an * am) # Earth's Gaussian radius of curvature
52
+ ae = (4 / 3.0) * ag
53
+
54
+ return ae
55
+
56
+
57
+ def convert_gpmrefl_grband_dfr(refl_gpm: np.ndarray, radar_band: str) -> np.ndarray:
58
+ """
59
+ Convert GPM reflectivity to ground radar band using the DFR relationship
60
+ found in Louf et al. (2019) paper.
61
+
62
+ Parameters:
63
+ ===========
64
+ refl_gpm:
65
+ Satellite reflectivity field.
66
+ radar_band: str
67
+ Possible values are 'S', 'C', or 'X'
68
+
69
+ Return:
70
+ =======
71
+ refl:
72
+ Reflectivity conversion from Ku-band to ground radar band
73
+ """
74
+ if radar_band == "S":
75
+ cof = np.array([2.01236803e-07, -6.50694273e-06, 1.10885533e-03, -6.47985914e-02, -7.46518423e-02])
76
+ dfr = np.poly1d(cof)
77
+ elif radar_band == "C":
78
+ cof = np.array([1.21547932e-06, -1.23266138e-04, 6.38562875e-03, -1.52248868e-01, 5.33556919e-01])
79
+ dfr = np.poly1d(cof)
80
+ elif radar_band == "X":
81
+ # Use of C band DFR relationship multiply by ratio
82
+ cof = np.array([1.21547932e-06, -1.23266138e-04, 6.38562875e-03, -1.52248868e-01, 5.33556919e-01])
83
+ dfr = 3.2 / 5.5 * np.poly1d(cof)
84
+ else:
85
+ raise ValueError(f"Radar reflectivity band ({radar_band}) not supported.")
86
+
87
+ return refl_gpm + dfr(refl_gpm)
88
+
89
+
90
+ def convert_gpmrefl_grband_phase_aware(
91
+ refl_gpm: np.ndarray,
92
+ height_gpm: np.ndarray,
93
+ height_bb: np.ndarray,
94
+ radar_band: str,
95
+ bb_margin: float = 500.0,
96
+ ) -> np.ndarray:
97
+ """
98
+ Convert GPM Ku-band reflectivity to ground radar band using phase-aware DFR.
99
+
100
+ This function accounts for hydrometeor phase (ice, melting layer, or liquid)
101
+ by using the GPM bright band (melting layer) height. Different DFR relationships
102
+ are applied based on whether the radar gate is above, within, or below the
103
+ melting layer.
104
+
105
+ Parameters:
106
+ ===========
107
+ refl_gpm: np.ndarray
108
+ GPM Ku-band reflectivity field (dBZ). Shape: (nscan, nray, nbin)
109
+ height_gpm: np.ndarray
110
+ Height of each GPM gate (meters). Shape: (nscan, nray, nbin)
111
+ height_bb: np.ndarray
112
+ Bright band (melting layer) height from GPM (meters). Shape: (nscan, nray)
113
+ Values < 0 indicate no bright band detected (all rain or all ice)
114
+ radar_band: str
115
+ Ground radar frequency band. Possible values: 'S', 'C', or 'X'
116
+ bb_margin: float, optional
117
+ Vertical margin around bright band height to define melting layer (meters).
118
+ Default is 500m (±500m around heightBB defines the melting layer).
119
+
120
+ Returns:
121
+ ========
122
+ refl_grband: np.ndarray
123
+ Reflectivity converted from Ku-band to ground radar band (dBZ).
124
+ Same shape as refl_gpm.
125
+
126
+ References:
127
+ ===========
128
+ - Rain DFR coefficients from Louf et al. (2019) J. Atmos. Oceanic Technol.
129
+ - Ice scattering theory: Tyynelä et al. (2011), Leinonen (2014)
130
+ - GPM bright band detection: Awaka et al. (2016)
131
+ """
132
+ if radar_band not in ["S", "C", "X"]:
133
+ raise ValueError(f"Radar reflectivity band ({radar_band}) not supported. Use 'S', 'C', or 'X'.")
134
+
135
+ # Define rain DFR polynomial coefficients (from Louf et al. 2019)
136
+ if radar_band == "S":
137
+ rain_cof = np.array([2.01236803e-07, -6.50694273e-06, 1.10885533e-03, -6.47985914e-02, -7.46518423e-02])
138
+ elif radar_band == "C":
139
+ rain_cof = np.array([1.21547932e-06, -1.23266138e-04, 6.38562875e-03, -1.52248868e-01, 5.33556919e-01])
140
+ elif radar_band == "X":
141
+ rain_cof = np.array([1.21547932e-06, -1.23266138e-04, 6.38562875e-03, -1.52248868e-01, 5.33556919e-01])
142
+ rain_cof = rain_cof * (3.2 / 5.5)
143
+
144
+ rain_dfr = np.poly1d(rain_cof)
145
+
146
+ # Ice DFR - smaller frequency dependence for ice particles
147
+ # Based on Mie scattering theory for ice spheres
148
+ # Ice has less frequency-dependent scattering than rain
149
+ ice_dfr_offset = {
150
+ 'S': 0.3, # dB - small positive offset for S-band
151
+ 'C': 0.5, # dB - moderate offset for C-band
152
+ 'X': 0.8 # dB - larger offset for X-band
153
+ }
154
+
155
+ refl_grband = np.full_like(refl_gpm, np.nan, dtype=np.float64)
156
+ # Expand heightBB to match 3D shape (nscan, nray, nbin)
157
+ height_bb_3d = np.broadcast_to(height_bb[..., np.newaxis], refl_gpm.shape)
158
+
159
+ no_bb_mask = height_bb_3d < 0
160
+ liquid_mask = (height_bb_3d >= 0) & (height_gpm < (height_bb_3d - bb_margin))
161
+ ice_mask = (height_bb_3d >= 0) & (height_gpm > (height_bb_3d + bb_margin))
162
+ melting_mask = (height_bb_3d >= 0) & (height_gpm >= (height_bb_3d - bb_margin)) & (height_gpm <= (height_bb_3d + bb_margin))
163
+
164
+ if np.any(no_bb_mask):
165
+ refl_grband[no_bb_mask] = refl_gpm[no_bb_mask] + rain_dfr(refl_gpm[no_bb_mask])
166
+
167
+ if np.any(liquid_mask):
168
+ refl_grband[liquid_mask] = refl_gpm[liquid_mask] + rain_dfr(refl_gpm[liquid_mask])
169
+
170
+ if np.any(ice_mask):
171
+ refl_grband[ice_mask] = refl_gpm[ice_mask] + ice_dfr_offset[radar_band]
172
+
173
+ if np.any(melting_mask):
174
+ # Calculate normalized height within melting layer (0 = bottom, 1 = top)
175
+ height_in_ml = height_gpm[melting_mask]
176
+ bb_height = height_bb_3d[melting_mask]
177
+
178
+ # Fraction of liquid (1 at bottom, 0 at top of melting layer)
179
+ liquid_fraction = 1.0 - (height_in_ml - (bb_height - bb_margin)) / (2 * bb_margin)
180
+ liquid_fraction = np.clip(liquid_fraction, 0, 1)
181
+
182
+ # Interpolate between ice and rain DFR
183
+ dfr_ice = ice_dfr_offset[radar_band]
184
+ dfr_rain = rain_dfr(refl_gpm[melting_mask])
185
+ dfr_mixed = liquid_fraction * dfr_rain + (1 - liquid_fraction) * dfr_ice
186
+
187
+ refl_grband[melting_mask] = refl_gpm[melting_mask] + dfr_mixed
188
+
189
+ return refl_grband
190
+
191
+
192
+ def attenuation_correction_zphi(zh: np.ndarray, kdp: np.ndarray, dr: float = 1, wavelength: str = "C") -> np.ndarray:
193
+ """
194
+ Attenuation correction using the ZPHI method.
195
+
196
+ Parameters:
197
+ -----------
198
+ zh : array-like
199
+ Horizontal reflectivity (dBZ)
200
+ kdp : array-like
201
+ Specific differential phase (deg/km)
202
+ dr : float
203
+ Range gate spacing in km
204
+ wavelength : str
205
+ 'C' for C-band (~5.3 cm) or 'X' for X-band (~3.2 cm)
206
+
207
+ Returns:
208
+ --------
209
+ zh_corrected : array
210
+ Attenuation-corrected horizontal reflectivity (dBZ)
211
+ """
212
+ if dr > 10:
213
+ raise ValueError("Range gate spacing 'dr' seems too large (>10 km). Check input.")
214
+
215
+ if wavelength.upper() == "C":
216
+ # C-band coefficients (Bringi et al. 2001)
217
+ alpha_h = 0.08 # coefficient for Ah = alpha * Kdp^beta
218
+ beta_h = 0.93
219
+
220
+ elif wavelength.upper() == "X":
221
+ # X-band coefficients (Park et al. 2005)
222
+ alpha_h = 0.28
223
+ beta_h = 0.95
224
+ else:
225
+ raise ValueError("Wavelength must be 'C' or 'X'")
226
+
227
+ # Calculate specific attenuation
228
+ ah = alpha_h * np.power(np.abs(kdp), beta_h) * np.sign(kdp)
229
+ PIA_h = np.nancumsum(ah * dr, axis=1) # Path Integrated Attenuation for Zh
230
+ zh_corrected = zh + 2 * PIA_h # Factor of 2 for two-way path
231
+
232
+ return zh_corrected
233
+
234
+
235
+ def attenuation_correction_gunn_east(
236
+ zh: np.ndarray, dr: float = 1, wavelength: str = "C", max_thld: float = 10
237
+ ) -> np.ndarray:
238
+ """
239
+ Attenuation correction using Gunn and East (1954) method.
240
+ This is based on the power-law relationship.
241
+ Eq. 2.66 p 106 of radar meteorology by Henri Sauvagot.
242
+
243
+ Parameters:
244
+ -----------
245
+ zh : array-like
246
+ Horizontal reflectivity in dBZ along a radial
247
+ dr : float
248
+ Range gate spacing in km
249
+ wavelength : str
250
+ 'C' for C-band or 'X' for X-band
251
+ max_thld: float
252
+ Capped-attenuation correction to avoid blowing up the refl in dB.
253
+
254
+ Returns:
255
+ --------
256
+ zh_corrected : array
257
+ Attenuation-corrected reflectivity (dBZ)
258
+ """
259
+ if dr > 10:
260
+ raise ValueError("Range gate spacing 'dr' seems too large (>10 km). Check input.")
261
+ if wavelength.upper() == "C":
262
+ l = 5.5
263
+ elif wavelength.upper() == "X":
264
+ l = 3.2
265
+ else:
266
+ raise ValueError("Wavelength must be 'C' or 'X'")
267
+
268
+ # Convert dBZ to linear Z (mm^6/m^3)
269
+ Z_linear = 10 ** (zh / 10.0)
270
+ R = (Z_linear / 200) ** (1 / 1.6)
271
+ ap = 0.35e-2 * R**1.6 / (l**4) + 0.22e-2 * R / l
272
+
273
+ PIA_h = np.nancumsum(ap * dr, axis=1)
274
+ PIA_h[PIA_h > max_thld / 2] = max_thld / 2
275
+ Z_corrected = zh + 2 * PIA_h
276
+
277
+ return Z_corrected
278
+
279
+
280
+ def correct_parallax(
281
+ sr_x: np.ndarray, sr_y: np.ndarray, gpmset: xr.Dataset
282
+ ) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:
283
+ """
284
+ Adjust the geo-locations of the SR pixels. The `sr_xy` coordinates of the
285
+ SR beam footprints need to be in the azimuthal equidistant projection of
286
+ the ground radar. This ensures that the ground radar is fixed at
287
+ xy-coordinate (0, 0), and every SR bin has its relative xy-coordinates
288
+ with respect to the ground radar site.
289
+
290
+ Parameters:
291
+ -----------
292
+ sr_x: ndarray
293
+ Array of x-coordinates of shape (nscans, nbeams)
294
+ sr_y: ndarray
295
+ Array of y-coordinates of shape (nscans, nbeams)
296
+ gpmset: xarray
297
+
298
+ Returns:
299
+ --------
300
+ sr_xyp : :class:`numpy:numpy.ndarray`
301
+ Array of parallax corrected coordinates
302
+ of shape (nscans, nbeams, nbins).
303
+ z_sr : :class:`numpy:numpy.ndarray`
304
+ Array of SR bin altitudes of shape (nscans, nbeams, nbins).
305
+ """
306
+ r_sr_inv, alpha = gpmset.nbin.values, gpmset.nray.values
307
+ # calculate height of bin
308
+ z = r_sr_inv * np.cos(np.deg2rad(alpha))[..., np.newaxis]
309
+ z_sr = np.repeat(z[np.newaxis, :, :], len(gpmset.nscan), axis=0)
310
+ # calculate bin ground xy-displacement length
311
+ ds = r_sr_inv * np.sin(np.deg2rad(alpha))[..., np.newaxis]
312
+
313
+ # calculate x,y-differences between ground coordinate
314
+ # and center ground coordinate [25th element]
315
+ center = int(np.floor(len(sr_x[-1]) / 2.0))
316
+ xdiff = sr_x - sr_x[:, center][:, np.newaxis]
317
+ ydiff = sr_y - sr_y[:, center][:, np.newaxis]
318
+
319
+ # assuming ydiff and xdiff being a triangles adjacent and
320
+ # opposite this calculates the xy-angle of the SR scan
321
+ ang = np.arctan2(ydiff, xdiff)
322
+
323
+ # calculate displacement dx, dy from displacement length
324
+ dx = ds * np.cos(ang)[..., np.newaxis]
325
+ dy = ds * np.sin(ang)[..., np.newaxis]
326
+
327
+ # subtract displacement from SR ground coordinates
328
+ sr_xp = sr_x[..., np.newaxis] - dx
329
+ sr_yp = sr_y[..., np.newaxis] - dy
330
+
331
+ return sr_xp, sr_yp, z_sr
332
+
333
+
334
+ def get_offset(matchset: xr.Dataset, dr: float, nbins: int = 200) -> float:
335
+ """
336
+ Compute the Offset between GR and GPM. It will try to compute the mode of
337
+ the distribution and if it fails, then it will use the mean.
338
+
339
+ Parameter:
340
+ ==========
341
+ matchset: xr.Dataset
342
+ Dataset of volume matching.
343
+ dr: float
344
+ Ground radar gate spacing (m).
345
+ nbins: int
346
+ Defines the number of equal-width bins in the distribution.
347
+
348
+ Returns:
349
+ ========
350
+ offset: float
351
+ Offset between GR and GPM
352
+ """
353
+ offset = np.arange(-15, 15, 0.2, dtype=np.float64)
354
+ area = np.zeros_like(offset)
355
+
356
+ refl_gpm = matchset.refl_gpm_grband.values.flatten().copy()
357
+ refl_gr = matchset.refl_gr_weigthed.values.flatten().copy()
358
+ fmin = matchset.fmin_gr.values.flatten().copy()
359
+
360
+ pos = (refl_gpm > 36) | (refl_gr > 36) | (fmin != 1)
361
+ if (~pos).sum() == 0: # Relaxing fmin parameter.
362
+ pos = (refl_gpm > 36) | (refl_gr > 36) | (fmin < 0.9)
363
+ if (~pos).sum() == 0:
364
+ pos = (refl_gpm > 36) | (refl_gr > 36) | (fmin < 0.7)
365
+
366
+ refl_gpm[pos] = np.nan
367
+ refl_gr[pos] = np.nan
368
+
369
+ pdf_gpm, _ = np.histogram(refl_gpm, range=(0, 50), bins=nbins, density=True)
370
+ for idx, a in enumerate(offset):
371
+ pdf_gr, _ = np.histogram(refl_gr - a, range=(0, 50), bins=nbins, density=True)
372
+ diff = np.min([pdf_gr, pdf_gpm], axis=0)
373
+ area[idx] = np.sum(diff)
374
+
375
+ smoothed_area = np.convolve([1] * 12, area, "same")
376
+ maxpos = np.argmax(smoothed_area)
377
+ gr_offset = offset[maxpos]
378
+ return gr_offset
379
+
380
+
381
+ def grid_displacement(field1: np.ndarray, field2: np.ndarray) -> Tuple[Tuple[int, int], Tuple[int, int]]:
382
+ """
383
+ Calculate the grid displacement using Phase correlation.
384
+ http://en.wikipedia.org/wiki/Phase_correlation
385
+
386
+ Parameters:
387
+ -----------
388
+ field1, field2 : ndarray
389
+ Fields separated in time.
390
+
391
+ Returns:
392
+ --------
393
+ displacement : two-tuple
394
+ integers if pixels, otherwise floats. Result of the calculation
395
+ """
396
+ # create copies of the data
397
+ ige1 = np.ma.masked_invalid(10 ** (field1 / 10)).filled(0)
398
+ ige2 = np.ma.masked_invalid(10 ** (field2 / 10)).filled(0)
399
+
400
+ # discrete fast fourier transformation and complex conjugation of image 2
401
+ image1FFT = np.fft.fft2(ige1)
402
+ image2FFT = np.conjugate(np.fft.fft2(ige2))
403
+
404
+ # inverse fourier transformation of product -> equal to cross correlation
405
+ imageCCor = np.real(np.fft.ifft2((image1FFT * image2FFT)))
406
+
407
+ # Shift the zero-frequency component to the center of the spectrum
408
+ imageCCorShift = np.fft.fftshift(imageCCor)
409
+ row, col = ige1.shape
410
+
411
+ # find the peak in the correlation
412
+ yShift, xShift = np.unravel_index(np.argmax(imageCCorShift), (row, col))
413
+ yShift -= row // 2
414
+ xShift -= col // 2
415
+
416
+ return (xShift, yShift)
gpmmatch/default.py ADDED
@@ -0,0 +1,197 @@
1
+ """
2
+ Volume matching of ground radar and GPM satellite. Default naming and attribute.
3
+
4
+ @title: gpmmatch
5
+ @author: Valentin Louf <valentin.louf@bom.gov.au>
6
+ @institutions: Monash University and the Australian Bureau of Meteorology
7
+ @creation: 24/02/2020
8
+ @date: 29/05/2025
9
+
10
+ .. autosummary::
11
+ :toctree: generated/
12
+
13
+ gpmset_metadata
14
+ get_metadata
15
+ """
16
+
17
+ from typing import Dict, Any
18
+
19
+
20
+ def gpmset_metadata() -> Dict[str, Any]:
21
+ """
22
+ Return a bunch of metadata (description, units, long_name, etc.) for the
23
+ GPM set metadata.
24
+ This metadata is used to describe the GPM data columns that are
25
+ volume-matched to the ground radar data.
26
+ The metadata includes information about the GPM overpass time, parallax
27
+ corrected coordinates, precipitation inside the ground radar scope,
28
+ range and elevation from the ground radar, and reflectivity in the
29
+ ground radar band.
30
+
31
+ Returns:
32
+ ========
33
+ metadata: dict
34
+ """
35
+ metadata = {
36
+ "overpass_time": {"description": "GPM overpass time at the closest from ground radar site"},
37
+ "x": {
38
+ "units": "m",
39
+ "description": "x-axis parallax corrected coordinates in relation to ground radar.",
40
+ },
41
+ "y": {
42
+ "units": "m",
43
+ "description": "y-axis parallax corrected coordinates in relation to ground radar.",
44
+ },
45
+ "z": {
46
+ "units": "m",
47
+ "description": "z-axis parallax corrected coordinates in relation to ground radar.",
48
+ },
49
+ "precip_in_gr_domain": {
50
+ "units": "1",
51
+ "description": "Satellite data-columns with precipitation inside the ground radar scope.",
52
+ },
53
+ "range_from_gr": {
54
+ "units": "m",
55
+ "description": "Range from satellite bins in relation to ground radar",
56
+ },
57
+ "elev_from_gr": {
58
+ "units": "degrees",
59
+ "description": "Elevation from satellite bins in relation to ground radar",
60
+ },
61
+ "reflectivity_grband": {"units": "dBZ"},
62
+ }
63
+ return metadata
64
+
65
+
66
+ def get_metadata() -> Dict[str, Dict[str, str]]:
67
+ """
68
+ Return a bunch of metadata (description, units, long_name, etc.) for the
69
+ output dataset.
70
+
71
+ Returns:
72
+ ========
73
+ metadata: dict
74
+ """
75
+ metadata = {
76
+ "fmin_gpm": {
77
+ "units": "",
78
+ "long_name": "fmin_gpm",
79
+ "description": "FMIN ratio for GPM.",
80
+ },
81
+ "fmin_gr": {
82
+ "units": "",
83
+ "long_name": "fmin_gr",
84
+ "description": "FMIN ratio for GR.",
85
+ },
86
+ "refl_gpm_raw": {
87
+ "units": "dBZ",
88
+ "long_name": "GPM_reflectivity",
89
+ "description": "GPM reflectivity volume-matched to ground radar.",
90
+ },
91
+ "refl_gpm_grband": {
92
+ "units": "dBZ",
93
+ "long_name": "GPM_reflectivity_grband_stratiform",
94
+ "description": "GPM reflectivity converted to ground radar frequency band.",
95
+ },
96
+ "refl_gr_raw": {
97
+ "units": "dBZ",
98
+ "long_name": "reflectivity",
99
+ "description": "Ground radar reflectivity volume matched using a `normal` average.",
100
+ },
101
+ "refl_gr_weigthed": {
102
+ "units": "dBZ",
103
+ "long_name": "reflectivity",
104
+ "description": "Ground radar reflectivity volume matched using a distance-weighted average.",
105
+ },
106
+ "std_refl_gpm": {
107
+ "units": "dB",
108
+ "long_name": "standard_deviation_reflectivity",
109
+ "description": "GPM reflectivity standard deviation of the volume-matched sample.",
110
+ },
111
+ "std_refl_gr": {
112
+ "units": "dB",
113
+ "long_name": "standard_deviation_reflectivity",
114
+ "description": "Ground radar reflectivity standard deviation of the volume-matched sample.",
115
+ },
116
+ "sample_gpm": {
117
+ "units": "1",
118
+ "long_name": "sample_size",
119
+ "description": "Number of GPM bins used to compute the volume-matched pixels at a given points",
120
+ },
121
+ "reject_gpm": {
122
+ "units": "1",
123
+ "long_name": "rejected_sample_size",
124
+ "description": "Number of GPM bins rejected to compute the volume-matched pixels at a given points",
125
+ },
126
+ "sample_gr": {
127
+ "units": "1",
128
+ "long_name": "sample_size",
129
+ "description": "Number of ground-radar bins used to compute the volume-matched pixels at a given points",
130
+ },
131
+ "reject_gr": {
132
+ "units": "1",
133
+ "long_name": "rejected_sample_size",
134
+ "description": "Number of ground-radar bins rejected to compute the volume-matched pixels at a given points",
135
+ },
136
+ "volume_match_gpm": {
137
+ "units": "m^3",
138
+ "long_name": "volume",
139
+ "description": "Volume of the GPM sample for each match points.",
140
+ },
141
+ "volume_match_gr": {
142
+ "units": "m^3",
143
+ "long_name": "volume",
144
+ "description": "Volume of the ground radar sample for each match points.",
145
+ },
146
+ "x": {
147
+ "units": "m",
148
+ "long_name": "projected_x_axis_coordinates",
149
+ "projection": "Azimuthal Equidistant from ground radar.",
150
+ },
151
+ "y": {
152
+ "units": "m",
153
+ "long_name": "projected_y_axis_coordinates",
154
+ "projection": "Azimuthal Equidistant from ground radar.",
155
+ },
156
+ "z": {
157
+ "units": "m",
158
+ "long_name": "projected_z_axis_coordinates",
159
+ "projection": "Azimuthal Equidistant from ground radar.",
160
+ },
161
+ "r": {
162
+ "units": "m",
163
+ "long_name": "range",
164
+ "description": "Range from ground radar.",
165
+ },
166
+ "timedelta": {
167
+ "units": "ns",
168
+ "long_name": "timedelta",
169
+ "description": "Maximum time delta between ground radar and GPM volumes.",
170
+ },
171
+ "elevation_gr": {
172
+ "units": "degrees",
173
+ "long_name": "elevation",
174
+ "description": "Ground radar reference elevation.",
175
+ },
176
+ "ntilt": {
177
+ "units": "1",
178
+ "long_name": "ground_radar_tilt_number",
179
+ "description": "Number of ground radar tilts used for volume matching.",
180
+ },
181
+ "nprof": {
182
+ "units": "1",
183
+ "long_name": "gpm_profile_number",
184
+ "description": "Number of GPM profiles (nrays x nscan) used for volume matching.",
185
+ },
186
+ "pir_gpm": {
187
+ "units": "dB m-1",
188
+ "long_name": "GPM_path_integrated_reflectivity",
189
+ "description": "Path integrated GPM reflectivity volume-matched.",
190
+ },
191
+ "pir_gr": {
192
+ "units": "dB m-1",
193
+ "long_name": "GR_path_integrated_reflectivity",
194
+ "description": "Path integrated GR reflectivity volume-matched.",
195
+ },
196
+ }
197
+ return metadata