pyart.retrieve.iq — pyart-mch 0.4.1 documentation

# Source code for pyart.retrieve.iq

    """
    pyart.retrieve.radar
    ======================

    Retrievals from spectral data.

    .. autosummary::
        :toctree: generated/

        compute_spectra
        compute_pol_variables_iq
        compute_reflectivity_iq
        compute_st1_iq
        compute_st2_iq
        compute_wbn_iq
        compute_differential_reflectivity_iq
        compute_mean_phase_iq
        compute_differential_phase_iq
        compute_rhohv_iq
        compute_Doppler_velocity_iq
        compute_Doppler_width_iq
        _compute_power
        _compute_autocorrelation
        _compute_lag_diff
        _compute_crosscorrelation

    """
    from copy import deepcopy
    from warnings import warn

    import numpy as np
    from scipy.signal.windows import get_window
    from scipy.constants import speed_of_light

    from ..config import get_metadata, get_field_name
    from ..util import radar_from_spectra

    def compute_spectra(radar, fields_in_list, fields_out_list, window=None):
        """
        Computes the spectra from IQ data through a Fourier transform

        Parameters
        ----------
        radar : radar object
            Object containing the IQ data
        fields_in_list : list of str
            list of input IQ data fields names
        fields_out_list : list of str
            list with the output spectra fields names obtained from the input
            fields
        window : string, tupple or None
            Parameters of the window used to obtain the spectra. The
            parameters are the ones corresponding to function
            scipy.signal.windows.get_window. If None no window will be used

        Returns
        -------
        spectra : spectra radar object
            radar object containing the spectra fields

        """
        spectra = deepcopy(radar)
        spectra.fields = {}
        for field_name_in, field_name_out in zip(fields_in_list, fields_out_list):
            if field_name_in in ('IQ_hh_ADU', 'IQ_vv_ADU'):
                spectrum = np.ma.masked_all(
                    (radar.nrays, radar.ngates, radar.npulses_max),
                    dtype=np.complex64)
                for ray, npuls in enumerate(radar.npulses['data']):
                    wind_data = radar.fields[field_name_in]['data'][
                        ray, :, 0:npuls].filled(0.)
                    if window is not None:
                        wind = get_window(window, npuls)
                        wind = wind/np.sqrt(np.sum(np.power(wind, 2.))/npuls)
                        wind = np.broadcast_to(
                            np.atleast_2d(wind), (radar.ngates, npuls))
                        wind_data *= wind

                    spectrum[ray, :, 0:npuls] = np.fft.fftshift(
                        np.fft.fft(wind_data, axis=-1)/npuls, axes=-1)
            else:
                spectrum = np.ma.masked_all(
                    (radar.nrays, radar.ngates, radar.npulses_max),
                    dtype=np.float32)
                for ray, npuls in enumerate(radar.npulses['data']):
                    spectrum[ray, :, 0:npuls] = radar.fields[field_name_in]['data'][
                        ray, :, 0:npuls]/npuls

            field_dict = get_metadata(field_name_out)
            field_dict['data'] = spectrum
            spectra.fields.update({field_name_out: field_dict})

        return spectra

    def compute_pol_variables_iq(radar, fields_list, subtract_noise=False, lag=0,
                                 direction='negative_away', phase_offset=0.,
                                 signal_h_field=None, signal_v_field=None,
                                 noise_h_field=None, noise_v_field=None):
        """
        Computes the polarimetric variables from the IQ signals in ADU

        Parameters
        ----------
        radar : IQ radar object
            Object containing the required fields
        fields_list : list of str
            list of fields to compute
        subtract_noise : Bool
            If True noise will be subtracted from the signals
        lag : int
            The time lag to use in the estimators
        direction : str
            The convention used in the Doppler mean field. Can be negative_away or
            negative_towards
        phase_offset : float. Dataset keyword
            The system differential phase offset to remove
        signal_h_field, signal_v_field, noise_h_field, noise_v_field : str
            Name of the fields in radar which contains the signal and noise.
            None will use the default field name in the Py-ART configuration file.

        Returns
        -------
        radar : radar object
            Object containing the computed fields

        """
        if signal_h_field is None:
            signal_h_field = get_field_name('IQ_hh_ADU')
        if signal_v_field is None:
            signal_v_field = get_field_name('IQ_vv_ADU')
        if noise_h_field is None:
            noise_h_field = get_field_name('IQ_noiseADU_hh')
        if noise_v_field is None:
            noise_v_field = get_field_name('IQ_noiseADU_vv')

        if ('reflectivity' in fields_list or
                'differential_reflectivity' in fields_list or
                ('cross_correlation_ratio' in fields_list and lag == 0) or
                ('spectrum_width' in fields_list and lag == 0)):
            noise_h = None
            if noise_h_field in radar.fields:
                noise_h = radar.fields[noise_h_field]['data']
            pwr_h = _compute_power(
                radar.fields[signal_h_field]['data'], noise=noise_h,
                subtract_noise=subtract_noise)

        if ('reflectivity_vv' in fields_list or
                'differential_reflectivity' in fields_list or
                ('cross_correlation_ratio' in fields_list and lag == 0) or
                ('spectrum_width_vv' in fields_list and lag == 0)):
            noise_v = None
            if noise_v_field in radar.fields:
                noise_v = radar.fields[noise_v_field]['data']
            pwr_v = _compute_power(
                radar.fields[signal_v_field]['data'], noise=noise_v,
                subtract_noise=subtract_noise)

        if ('velocity' in fields_list or 'spectrum_width' in fields_list or
                ('differential_reflectivity' in fields_list and lag == 1) or
                ('cross_correlation_ratio' in fields_list and lag == 1)):
            rlag1_h = _compute_autocorrelation(radar, signal_h_field, lag=1)
            if ('differential_reflectivity' in fields_list or
                    'cross_correlation_ratio' in fields_list):
                rlagn_h_abs = np.ma.abs(rlag1_h)

        if ('velocity_vv' in fields_list or 'spectrum_width_vv' in fields_list or
                ('differential_reflectivity' in fields_list and lag == 1) or
                ('cross_correlation_ratio' in fields_list and lag == 1)):
            rlag1_v = _compute_autocorrelation(radar, signal_v_field, lag=1)
            if ('differential_reflectivity' in fields_list or
                    'cross_correlation_ratio' in fields_list):
                rlagn_v_abs = np.ma.abs(rlag1_v)

        if 'spectrum_width' in fields_list and lag >= 1:
            rlag2_h = _compute_autocorrelation(radar, signal_h_field, lag=2)

        if 'spectrum_width_vv' in fields_list and lag >= 1:
            rlag2_v = _compute_autocorrelation(radar, signal_v_field, lag=2)

        if lag > 0 and ('differential_reflectivity' in fields_list or
                        'cross_correlation_ratio' in fields_list):
            rlagn_h_abs = np.ma.abs(
                _compute_autocorrelation(radar, signal_h_field, lag=1))
            rlagn_v_abs = np.ma.abs(
                _compute_autocorrelation(radar, signal_v_field, lag=1))

        if ('reflectivity' in fields_list or
                'differential_reflectivity' in fields_list):
            dBADU2dBm_h = radar.radar_calibration['dBADU_to_dBm_hh']['data'][0]
            radconst_h = (
                radar.radar_calibration['calibration_constant_hh']['data'][0])
            mfloss_h = radar.radar_calibration['matched_filter_loss_h']['data'][0]

        if ('reflectivity_vv' in fields_list or
                'differential_reflectivity' in fields_list):
            dBADU2dBm_v = radar.radar_calibration['dBADU_to_dBm_vv']['data'][0]
            radconst_v = (
                radar.radar_calibration['calibration_constant_vv']['data'][0])
            mfloss_v = radar.radar_calibration['matched_filter_loss_v']['data'][0]

        if 'reflectivity' in fields_list or 'reflectivity_vv' in fields_list:
            pathatt = radar.radar_calibration['path_attenuation']['data'][0]
            rangeKm = np.broadcast_to(
                np.atleast_2d(radar.range['data']/1000.),
                (radar.nrays, radar.ngates))

        if ('velocity' in fields_list or 'velocity_vv' in fields_list or
                'spectrum_width' in fields_list or
                'spectrum_width_vv' in fields_list):
            prt = np.broadcast_to(
                np.expand_dims(radar.instrument_parameters['prt']['data'], axis=1),
                (radar.nrays, radar.ngates))
            wavelength = (
                speed_of_light/radar.instrument_parameters['frequency']['data'][0])

        fields = {}
        if ('reflectivity' in fields_list or
                ('differential_reflectivity' in fields_list and lag == 0)):
            dBZ = (
                10.*np.ma.log10(pwr_h)+dBADU2dBm_h+radconst_h+mfloss_h +
                pathatt*rangeKm+20.*np.log10(rangeKm))

            if 'reflectivity' in fields_list:
                dBZ_field = 'reflectivity'
                dBZ_dict = get_metadata(dBZ_field)
                dBZ_dict['data'] = dBZ
                fields.update({dBZ_field: dBZ_dict})

        if ('reflectivity_vv' in fields_list or
                ('differential_reflectivity' in fields_list and lag == 0)):
            dBZv = (
                10.*np.ma.log10(pwr_v)+dBADU2dBm_v+radconst_v+mfloss_v +
                pathatt*rangeKm+20.*np.log10(rangeKm))
            if 'reflectivity_vv' in fields_list:
                dBZv_field = 'reflectivity_vv'
                dBZv_dict = get_metadata(dBZv_field)
                dBZv_dict['data'] = dBZv
                fields.update({dBZv_field: dBZv_dict})

        if 'differential_reflectivity' in fields_list:
            if lag == 0:
                zdr = dBZ-dBZv
            else:
                zdr = (
                    (10.*np.ma.log10(rlagn_h_abs)+dBADU2dBm_h+radconst_h) -
                    (10.*np.ma.log10(rlagn_v_abs)+dBADU2dBm_v+radconst_v))
            zdr_field = 'differential_reflectivity'
            zdr_dict = get_metadata(zdr_field)
            zdr_dict['data'] = zdr
            fields.update({zdr_field: zdr_dict})

        if 'cross_correlation_ratio' in fields_list:
            if lag == 0:
                rhohv = np.ma.abs(np.ma.mean(
                    radar.fields[signal_h_field]['data'] *
                    np.ma.conjugate(radar.fields[signal_v_field]['data']),
                    axis=-1))
                rhohv /= np.ma.sqrt(pwr_h*pwr_v)
            else:
                rhohv = np.ma.abs(_compute_crosscorrelation(
                    radar, signal_h_field, signal_v_field, lag=lag))
                rhohv /= np.ma.sqrt(rlagn_h_abs*rlagn_v_abs)
            rhohv_field = 'cross_correlation_ratio'
            rhohv_dict = get_metadata(rhohv_field)
            rhohv_dict['data'] = rhohv
            fields.update({rhohv_field: rhohv_dict})

        if 'velocity' in fields_list:
            mean_vel = np.ma.angle(rlag1_h)/(2*np.pi*prt)*(wavelength/2.)
            if direction == 'negative_away':
                mean_vel = -mean_vel
            vel_field = 'velocity'
            vel_dict = get_metadata(vel_field)
            vel_dict['data'] = mean_vel
            fields.update({vel_field: vel_dict})

        if 'velocity_vv' in fields_list:
            mean_vel = np.ma.angle(rlag1_v)/(2*np.pi*prt)*(wavelength/2.)
            if direction == 'negative_away':
                mean_vel = -mean_vel
            vel_field = 'velocity_vv'
            vel_dict = get_metadata(vel_field)
            vel_dict['data'] = mean_vel
            fields.update({vel_field: vel_dict})

        if 'spectrum_width' in fields_list:
            if lag == 0:
                width = (
                    np.ma.sqrt(np.ma.log(np.ma.abs(pwr_h)/np.ma.abs(rlag1_h))) /
                    (np.sqrt(2.)*np.pi*prt)*(wavelength/2.))
            else:
                width = (
                    np.ma.sqrt(np.ma.log(np.ma.abs(rlag1_h)/np.ma.abs(rlag2_h))) /
                    (np.sqrt(6.)*np.pi*prt)*(wavelength/2.))
            width_field = 'spectrum_width'
            width_dict = get_metadata(width_field)
            width_dict['data'] = width
            fields.update({width_field: width_dict})

        if 'spectrum_width_vv' in fields_list:
            if lag == 0:
                width = (
                    np.ma.sqrt(np.ma.log(np.ma.abs(pwr_v)/np.ma.abs(rlag1_v))) /
                    (np.sqrt(2.)*np.pi*prt)*(wavelength/2.))
            else:
                width = (
                    np.ma.sqrt(np.ma.log(np.ma.abs(rlag1_v)/np.ma.abs(rlag2_v))) /
                    (np.sqrt(6.)*np.pi*prt)*(wavelength/2.))
            width_field = 'spectrum_width_vv'
            width_dict = get_metadata(width_field)
            width_dict['data'] = width
            fields.update({width_field: width_dict})

        if 'uncorrected_differential_phase' in fields_list:
            phidp_dict = compute_differential_phase_iq(
                radar, phase_offset=phase_offset, signal_h_field=signal_h_field,
                signal_v_field=signal_v_field)
            fields.update({'uncorrected_differential_phase': phidp_dict})

        radar = radar_from_spectra(radar)

        for field_name in fields_list:
            radar.add_field(field_name, fields[field_name])

        return radar

    def compute_reflectivity_iq(radar, subtract_noise=False, signal_field=None,
                                noise_field=None):
        """
        Computes the reflectivity from the IQ signal data

        Parameters
        ----------
        radar : IQ radar object
            Object containing the required fields
        subtract_noise : Bool
            If true the noise is subtracted from the power
        signal_field, noise_field : str
            Name of the signal and noise fields. None will use the default field
            name in the Py-ART configuration file.

        Returns
        -------
        dBZ_dict : field dictionary
            Field dictionary containing the reflectivity

        """
        if signal_field is None:
            signal_field = get_field_name('IQ_hh_ADU')
        if noise_field is None:
            noise_field = get_field_name('IQ_noiseADU_hh')

        if radar.radar_calibration is None:
            raise ValueError(
                'Unable to compute reflectivity. Calibration parameters unknown')

        pol = 'hh'
        if signal_field is not None and 'vv' in signal_field:
            pol = 'vv'

        if pol == 'hh':
            if ('dBADU_to_dBm_hh' not in radar.radar_calibration or
                    'calibration_constant_hh' not in radar.radar_calibration):
                raise ValueError(
                    'Unable to compute reflectivity. ' +
                    'Calibration parameters unknown')
            dBADU2dBm = radar.radar_calibration['dBADU_to_dBm_hh']['data'][0]
            radconst = (
                radar.radar_calibration['calibration_constant_hh']['data'][0])
        else:
            if ('dBADU_to_dBm_vv' not in radar.radar_calibration or
                    'calibration_constant_vv' not in radar.radar_calibration):
                raise ValueError(
                    'Unable to compute reflectivity. ' +
                    'Calibration parameters unknown')
            dBADU2dBm = radar.radar_calibration['dBADU_to_dBm_vv']['data'][0]
            radconst = (
                radar.radar_calibration['calibration_constant_vv']['data'][0])

        if (pol == 'hh' and 'matched_filter_loss_h' in radar.radar_calibration):
            mfloss = radar.radar_calibration['matched_filter_loss_h']['data'][0]
        elif (pol == 'vv' and
              'matched_filter_loss_v' in radar.radar_calibration):
            mfloss = radar.radar_calibration['matched_filter_loss_v']['data'][0]
        else:
            warn('Unknown matched filter losses. Assumed 0 dB')
            mfloss = 0.

        if 'path_attenuation' in radar.radar_calibration:
            pathatt = radar.radar_calibration['path_attenuation']['data'][0]
        else:
            warn('Unknown gas path attenuation. Assumed 0 dB/km')
            pathatt = 0.

        rangeKm = np.broadcast_to(
            np.atleast_2d(radar.range['data']/1000.), (radar.nrays, radar.ngates))

        noise = None
        if noise_field in radar.fields:
            noise = radar.fields[noise_field]['data']

        pwr = _compute_power(
            radar.fields[signal_field]['data'], noise=noise,
            subtract_noise=subtract_noise)

        dBZ = (
            10.*np.ma.log10(pwr)+dBADU2dBm+radconst+mfloss+pathatt*rangeKm +
            20.*np.log10(rangeKm))

        dBZ_field = 'reflectivity_'+pol

        dBZ_dict = get_metadata(dBZ_field)
        dBZ_dict['data'] = dBZ

        return dBZ_dict

    def compute_st1_iq(radar, signal_field=None):
        """
        Computes the statistical test one lag fluctuation from the horizontal or
        vertical channel IQ data

        Parameters
        ----------
        radar : IQ radar object
            Object containing the required fields
        signal_field : str
            Name of the field that contain the H or V IQ data. None will use the
            default field name in the Py-ART configuration file.

        Returns
        -------
        st1_dict : field dictionary
            Field dictionary containing the st1

        """
        if signal_field is None:
            signal_field = get_field_name('IQ_hh_ADU')

        st1 = _compute_lag_diff(radar, signal_field, is_log=True, lag=1)

        st1_field = 'stat_test_lag1'
        if 'vv' in signal_field:
            st1_field += '_vv'
        st1_dict = get_metadata(st1_field)
        st1_dict['data'] = st1

        return st1_dict

    def compute_st2_iq(radar, signal_field=None):
        """
        Computes the statistical test two lag fluctuation from the horizontal or
        vertical channel IQ data

        Parameters
        ----------
        radar : IQ radar object
            Object containing the required fields
        signal_field : str
            Name of the field that contain the H or V IQ data. None will use the
            default field name in the Py-ART configuration file.

        Returns
        -------
        st2_dict : field dictionary
            Field dictionary containing the st2

        """
        if signal_field is None:
            signal_field = get_field_name('IQ_hh_ADU')

        st2 = _compute_lag_diff(radar, signal_field, is_log=True, lag=2)

        st2_field = 'stat_test_lag2'
        if 'vv' in signal_field:
            st2_field += '_vv'
        st2_dict = get_metadata(st2_field)
        st2_dict['data'] = st2

        return st2_dict

    def compute_wbn_iq(radar, signal_field=None):
        """
        Computes the wide band noise from the horizontal or vertical channel IQ
        data

        Parameters
        ----------
        radar : IQ radar object
            Object containing the required fields
        signal_field : str
            Name of the field that contain the H or V IQ data. None will use the
            default field name in the Py-ART configuration file.

        Returns
        -------
        wbn_dict : field dictionary
            Field dictionary containing the wide band noise

        """
        if signal_field is None:
            signal_field = get_field_name('IQ_hh_ADU')

        rlag0 = _compute_autocorrelation(radar, signal_field, lag=0)
        rlag1 = _compute_autocorrelation(radar, signal_field, lag=1)
        wbn = 20*np.ma.log10(np.ma.abs(rlag0)/np.ma.abs(rlag1))

        wbn_field = 'wide_band_noise'
        if 'vv' in signal_field:
            wbn_field += '_vv'
        wbn_dict = get_metadata(wbn_field)
        wbn_dict['data'] = wbn

        return wbn_dict

    def compute_differential_reflectivity_iq(radar, subtract_noise=False, lag=0,
                                             signal_h_field=None,
                                             signal_v_field=None,
                                             noise_h_field=None,
                                             noise_v_field=None):
        """
        Computes the differential reflectivity from the horizontal and vertical
        IQ data

        Parameters
        ----------
        radar : IQ radar object
            Object containing the required fields
        subtract_noise : Bool
            If true the noise is subtracted from the power
        lag : int
            Time lag used to compute the differential reflectivity
        signal_h_field, signal_v_field, noise_h_field, noise_v_field : str
            Name of the signal and noise fields. None will use the default field
            name in the Py-ART configuration file.

        Returns
        -------
        zdr_dict : field dictionary
            Field dictionary containing the differential reflectivity

        """
        if signal_h_field is None:
            signal_h_field = get_field_name('IQ_hh_ADU')
        if signal_v_field is None:
            signal_v_field = get_field_name('IQ_vv_ADU')
        if noise_h_field is None:
            noise_h_field = get_field_name('IQ_noiseADU_hh')
        if noise_v_field is None:
            noise_v_field = get_field_name('IQ_noiseADU_vv')

        if radar.radar_calibration is None:
            raise ValueError(
                'Unable to compute spectral reflectivity. ' +
                'Calibration parameters unknown')

        if ('dBADU_to_dBm_hh' not in radar.radar_calibration or
                'dBADU_to_dBm_vv' not in radar.radar_calibration or
                'calibration_constant_hh' not in radar.radar_calibration or
                'calibration_constant_vv' not in radar.radar_calibration):
            raise ValueError(
                'Unable to compute spectral reflectivity. ' +
                'Calibration parameters unknown')

        dBADU2dBm_h = radar.radar_calibration['dBADU_to_dBm_hh']['data'][0]
        dBADU2dBm_v = radar.radar_calibration['dBADU_to_dBm_vv']['data'][0]
        radconst_h = (
            radar.radar_calibration['calibration_constant_hh']['data'][0])
        radconst_v = (
            radar.radar_calibration['calibration_constant_vv']['data'][0])

        if lag == 0:
            noise = None
            if noise_h_field in radar.fields:
                noise = radar.fields[noise_h_field]['data']

            pwr_h = _compute_power(
                radar.fields[signal_h_field]['data'], noise=noise,
                subtract_noise=subtract_noise)

            noise = None
            if noise_v_field in radar.fields:
                noise = radar.fields[noise_v_field]['data']

            pwr_v = _compute_power(
                radar.fields[signal_v_field]['data'], noise=noise,
                subtract_noise=subtract_noise)
        else:
            pwr_h = np.ma.abs(
                _compute_autocorrelation(radar, signal_h_field, lag=lag))
            pwr_v = np.ma.abs(
                _compute_autocorrelation(radar, signal_v_field, lag=lag))

        zdr = (
            (10.*np.ma.log10(pwr_h)+dBADU2dBm_h+radconst_h) -
            (10.*np.ma.log10(pwr_v)+dBADU2dBm_v+radconst_v))

        zdr_dict = get_metadata('differential_reflectivity')
        zdr_dict['data'] = zdr

        return zdr_dict

    def compute_mean_phase_iq(radar, signal_field=None):
        """
        Computes the differential phase from the horizontal or vertical channel
        IQ data

        Parameters
        ----------
        radar : IQ radar object
            Object containing the required fields
        signal_field : str
            Name of the field that contain the H or V IQ data. None will use the
            default field name in the Py-ART configuration file.

        Returns
        -------
        mph_dict : field dictionary
            Field dictionary containing the mean phase

        """
        if signal_field is None:
            signal_field = get_field_name('IQ_hh_ADU')

        mph = np.ma.mean(
            np.ma.angle(radar.fields[signal_field]['data'], deg=True), axis=-1)

        mph_field = 'mean_phase'
        if 'vv' in signal_field:
            mph_field += '_vv'
        mph_dict = get_metadata(mph_field)
        mph_dict['data'] = mph

        return mph_dict

    def compute_differential_phase_iq(radar, phase_offset=0., signal_h_field=None,
                                      signal_v_field=None):
        """
        Computes the differential phase from the horizontal and vertical channels
        IQ data

        Parameters
        ----------
        radar : IQ radar object
            Object containing the required fields
        phase_offset : float
            system phase offset to add
        signal_h_field, signal_v_field : str
            Name of the fields that contain the H and V IQ data. None will use the
            default field name in the Py-ART configuration file.

        Returns
        -------
        phidp_dict : field dictionary
            Field dictionary containing the differential phase

        """
        if signal_h_field is None:
            signal_h_field = get_field_name('IQ_hh_ADU')
        if signal_v_field is None:
            signal_v_field = get_field_name('IQ_vv_ADU')

        phidp = np.ma.angle(np.ma.mean(
            radar.fields[signal_h_field]['data'] *
            np.ma.conjugate(radar.fields[signal_v_field]['data']), axis=-1),
                            deg=True)-phase_offset

        if phase_offset != 0:
            phidp = (phidp+180.)%360.-180.

        phidp_field = 'uncorrected_differential_phase'
        phidp_dict = get_metadata(phidp_field)
        phidp_dict['data'] = phidp

        return phidp_dict

    def compute_rhohv_iq(radar, subtract_noise=False, lag=0, signal_h_field=None,
                         signal_v_field=None, noise_h_field=None,
                         noise_v_field=None):
        """
        Computes RhoHV from the horizontal and vertical channels IQ data

        Parameters
        ----------
        radar : IQ radar object
            Object containing the required fields
        subtract_noise : Bool
            If True noise will be subtracted from the signals
        lag : int
            Time lag used in the computation
        signal_h_field, signal_v_field, noise_h_field, noise_v_field : str
            Name of the fields in radar which contains the signal and noise.
            None will use the default field name in the Py-ART configuration file.

        Returns
        -------
        rhohv_dict : field dictionary
            Field dictionary containing the RhoHV

        """
        if signal_h_field is None:
            signal_h_field = get_field_name('IQ_hh_ADU')
        if signal_v_field is None:
            signal_v_field = get_field_name('IQ_vv_ADU')
        if noise_h_field is None:
            noise_h_field = get_field_name('IQ_noiseADU_hh')
        if noise_v_field is None:
            noise_v_field = get_field_name('IQ_noiseADU_vv')

        if lag == 0:
            rhohv = np.ma.abs(np.ma.mean(
                radar.fields[signal_h_field]['data'] *
                np.ma.conjugate(radar.fields[signal_v_field]['data']), axis=-1))

            noise = None
            if noise_h_field in radar.fields:
                noise = radar.fields[noise_h_field]['data']

            pwr_h = _compute_power(
                radar.fields[signal_h_field]['data'], noise=noise,
                subtract_noise=subtract_noise)

            noise = None
            if noise_v_field in radar.fields:
                noise = radar.fields[noise_v_field]['data']

            pwr_v = _compute_power(
                radar.fields[signal_v_field]['data'], noise=noise,
                subtract_noise=subtract_noise)
        else:
            rhohv = np.ma.abs(_compute_crosscorrelation(
                radar, signal_h_field, signal_v_field, lag=lag))
            pwr_h = np.ma.abs(
                _compute_autocorrelation(radar, signal_h_field, lag=lag))
            pwr_v = np.ma.abs(
                _compute_autocorrelation(radar, signal_v_field, lag=lag))

        rhohv /= np.ma.sqrt(pwr_h*pwr_v)

        rhohv_dict = get_metadata('cross_correlation_ratio')
        rhohv_dict['data'] = rhohv

        return rhohv_dict

    def compute_Doppler_velocity_iq(radar, signal_field=None,
                                    direction='negative_away'):
        """
        Computes the Doppler velocity from the IQ data

        Parameters
        ----------
        radar : IQ radar object
            Object containing the required fields
        signal_field : str
            Name of the field in the radar which contains the signal.
            None will use the default field name in the Py-ART configuration file.
        direction : str
            The convention used in the Doppler mean field. Can be negative_away or
            negative_towards

        Returns
        -------
        vel_dict : field dictionary
            Field dictionary containing the Doppler velocity

        """
        if signal_field is None:
            signal_field = get_field_name('IQ_hh_ADU')

        if radar.instrument_parameters is None:
            warn('Unable to compute Doppler velocity. No instrument information')
            return None

        if ('prt' not in radar.instrument_parameters or
                'frequency' not in radar.instrument_parameters):
            warn('Unable to compute Doppler velocity. '
                 'Unknown PRT or radar frequency')
            return None

        rlag_1 = _compute_autocorrelation(radar, signal_field, lag=1)
        prt = np.broadcast_to(
            np.expand_dims(radar.instrument_parameters['prt']['data'], axis=1),
            (radar.nrays, radar.ngates))
        wavelength = (
            speed_of_light/radar.instrument_parameters['frequency']['data'][0])
        mean_vel = np.ma.angle(rlag_1)/(2*np.pi*prt)*(wavelength/2.)

        if direction == 'negative_away':
            mean_vel = -mean_vel

        vel_field = 'velocity'
        if 'vv' in signal_field:
            vel_field += '_vv'
        vel_dict = get_metadata(vel_field)
        vel_dict['data'] = mean_vel

        return vel_dict

    def compute_Doppler_width_iq(radar, subtract_noise=True, signal_field=None,
                                 noise_field=None, lag=1):
        """
        Computes the Doppler width from the IQ data

        Parameters
        ----------
        radar : Radar radar object
            Object containing the required fields
        subtract_noise : Bool
            If True noise will be subtracted from the signals
        lag : int
            Time lag used in the denominator of the computation
        signal_field, noise_field : str
            Name of the field in the radar which contains the signal and noise.
            None will use the default field name in the Py-ART configuration file.

        Returns
        -------
        width_dict : field dictionary
            Field dictionary containing the Doppler spectrum width

        Reference
        ---------
        lag 1 algorithm based on:
            Time-Domain Computation of Mean and Variance of Doppler Spectra
            R. C. Srivastava, A. R. Jameson, and P. H. Hildebrand
            Journal of Applied Meteorology February 1979, Vol. 18, No. 2

        """
        if signal_field is None:
            signal_field = get_field_name('IQ_hh_ADU')
        if noise_field is None:
            noise_field = get_field_name('IQ_noiseADU_hh')

        prt = np.broadcast_to(
            np.expand_dims(radar.instrument_parameters['prt']['data'], axis=1),
            (radar.nrays, radar.ngates))
        wavelength = (
            speed_of_light/radar.instrument_parameters['frequency']['data'][0])

        if lag == 0:
            noise = None
            if noise_field in radar.fields:
                noise = radar.fields[noise_field]['data']

            rlag_1 = _compute_autocorrelation(radar, signal_field, lag=1)
            pwr = _compute_power(
                radar.fields[signal_field]['data'], noise=noise,
                subtract_noise=subtract_noise)

            width = (np.ma.sqrt(np.ma.log(np.ma.abs(pwr)/np.ma.abs(rlag_1))) /
                     (np.sqrt(2.)*np.pi*prt)*(wavelength/2.))
        else:
            rlag_2 = _compute_autocorrelation(radar, signal_field, lag=2)
            rlag_1 = _compute_autocorrelation(radar, signal_field, lag=1)

            width = (np.ma.sqrt(np.ma.log(np.ma.abs(rlag_1)/np.ma.abs(rlag_2))) /
                     (np.sqrt(6.)*np.pi*prt)*(wavelength/2.))

        width_field = 'spectrum_width'
        if 'vv' in signal_field:
            width_field += '_vv'
        width_dict = get_metadata(width_field)
        width_dict['data'] = width

        return width_dict

    def _compute_power(signal, noise=None, subtract_noise=False):
        """
        Compute the signal power in linear units

        Parameters
        ----------
        signal : float array
            The IQ signal
        noise : float array
            The noise power per pulse
        subtract_noise : Bool
            If True and noise not None the noise power will be subtracted from the
            signal power

        Returns
        -------
        pwr : float array
            The computed signal power

        """
        pwr = np.ma.mean(np.ma.power(np.ma.abs(signal), 2.), axis=-1)

        if subtract_noise and noise is not None:
            noise_gate = np.ma.mean(noise, axis=-1)
            pwr -= noise_gate
            pwr[pwr < 0.] = np.ma.masked

        return pwr

    def _compute_autocorrelation(radar, signal_field, lag=1):
        """
        Compute the signal autocorrelation in linear units

        Parameters
        ----------
        radar : IQ radar object
            The radar object containing the fields
        signal_field : str
            The IQ signal
        lag : int
            Time lag to compute

        Returns
        -------
        rlag : float array
            The computed autocorrelation lag

        """
        rlag = np.ma.masked_all((radar.nrays, radar.ngates), dtype=np.complex64)
        for ray, npulses in enumerate(radar.npulses['data']):
            if lag >= npulses:
                warn('lag larger than number of pulses in ray')
                continue
            rlag[ray, :] = np.ma.mean(
                np.ma.conjugate(
                    radar.fields[signal_field]['data'][ray, :, 0:npulses-lag]) *
                radar.fields[signal_field]['data'][ray, :, lag:npulses], axis=-1)

        return rlag

    def _compute_lag_diff(radar, signal_field, is_log=True, lag=1):
        """
        Compute the signal autocorrelation in linear units

        Parameters
        ----------
        radar : IQ radar object
            The radar object containing the fields
        signal_field : str
            The IQ signal
        lag : int
            Time lag to compute

        Returns
        -------
        rlag : float array
            The computed autocorrelation lag

        """
        rlag = np.ma.masked_all((radar.nrays, radar.ngates))
        pwr = np.ma.power(np.ma.abs(radar.fields[signal_field]['data']), 2.)
        if is_log:
            pwr = 10.*np.ma.log10(pwr)
        for ray, npulses in enumerate(radar.npulses['data']):
            if lag >= npulses:
                warn('lag larger than number of pulses in ray')
                continue

            rlag[ray, :] = np.ma.mean(np.ma.abs(
                pwr[ray, :, 0:npulses-lag]-pwr[ray, :, lag:npulses]), axis=-1)

        return rlag

    def _compute_crosscorrelation(radar, signal_h_field, signal_v_field, lag=1):
        """
        Compute the cross-correlation between H and V in linear units

        Parameters
        ----------
        radar : IQ radar object
            The radar object containing the fields
        signal_h_field, signal_v_field : str
            The IQ H and V signal names
        lag : int
            Time lag to compute

        Returns
        -------
        rlag : float array
            The computed cross-correlation lag

        """
        rlag = np.ma.masked_all((radar.nrays, radar.ngates), dtype=np.complex64)
        for ray, npulses in enumerate(radar.npulses['data']):
            if lag >= npulses:
                warn('lag larger than number of pulses in ray')
                continue
            rlag[ray, :] = np.ma.mean(
                radar.fields[signal_h_field]['data'][ray, :, 0:npulses-lag] *
                np.ma.conjugate(
                    radar.fields[signal_v_field]['data'][ray, :, lag:npulses]),
                axis=-1)

        return rlag