pyart.aux_io.metranet_python — pyart-mch 0.4.1 documentation

# Source code for pyart.aux_io.metranet_python

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    """
    metranet python library
    ================

    functions to read METRANET files in pure python, no other library required!

    .. autosummary::
        :toctree: generated/

        read_polar
        read_product
        read_file
        _get_radar_site_info
        _nyquist_vel
        _selex2deg
        _float_mapping

    .. autosummary::
        :toctree: generated/
        :template: dev_template.rst

        RadarData
        PolarParser

    """

    import sys
    import struct
    import os
    import copy
    from warnings import warn
    import traceback

    import numpy as np

    from .pmfile_structure import MSWEEP_HEADER, MRAY_HEADER, MMOMENT_HEADER, MMOMENT_INFO_STRUCTURE
    from .pmfile_structure import BYTE_SIZES
    from .pmfile_structure import PRAY_HEADER, PMOMENTS
    from .dn_to_float import float_mapping, nyquist_vel
    from .lzw15 import decompress, readbytes_fh, unpackbyte

    # fix for python3
    if sys.version_info[0] == 3:
        def xrange(i):
            return range(i)

    ###############################################################################
    #        Constants
    ###############################################################################

    # some values valid for all sites
    NPM_MOM = 11
    NPH_MOM = 12
    NPL_MOM = 9

    # For some reasons, the radar name is not encoded in a consistent way in the
    # M binary files, this maps all names in files to a single character
    RENAME_RADARS_M = {'Weissfluhgipfel':'W',
                       'Albis':'A ',
                       'L':'L',
                       'Dole':'D',
                       'P':'P'}
    # In P files, radar name is three characters, map to single character
    RENAME_RADARS_P = {'WEI':'W',
                       'ALB':'A',
                       'LEM':'L',
                       'DOL':'D',
                       'PPM':'P'}

    # Dict to map moment names hardcoded in M files to terminology used
    # in metranet C library
    MOM_NAME_MAPPING = {'Z_V_CLUT':'ZVC',
                        'Z_CLUT':'ZHC',
                        'W':'WID',
                        'V':'VEL',
                        'PHIDP':'PHI',
                        'STAT1':'ST1',
                        'STAT2':'ST2',
                        'CLUT':'CLT',
                        'UZ':'ZH',
                        'UZ_V':'ZV',
                        'APH':'MPH'}

    # Other way round, f.ex CLT --> CLUT
    MOM_NAME_MAPPING_INV = dict(zip(MOM_NAME_MAPPING.values(),
                                    MOM_NAME_MAPPING.keys()))

    class RadarData():
        """
        A class for storing radar data.

        Attributes
        ----------
        header : dictionary
            file header (metadara valid for the whole sweep)
        pol_header : list
            list of ray metadata dictionaries
        data : dictionary
            dic of arrays containing data
        """

        def __init__(self, header=None, pol_header=None, data=None, scale=None):
            self.header = header
            self.pol_header = pol_header
            self.data = data
            self.scale = scale

    class PolarParser():
        """
        A class for parsing metranet polar data

        Parameters
        ----------
        filename : str
            complete file path of the file to read

        Attributes
        ----------
        file_format : char
            P or M depending on the file format that was used
        file_type : char
            file type (H, M or L)
        bname : str
            basename (without path) of the file to read
        bytarray: memoryview of a bytearray
            binary data contained in the file

        """

        def __init__(self, filename):
            # check if it is the right file. Open it and read it
            bfile = os.path.basename(filename)

            supported_file = (bfile.startswith('MH') or bfile.startswith('PH') or
                              bfile.startswith('MS') or bfile.startswith('PM') or
                              bfile.startswith('ML') or bfile.startswith('PL'))

            if not supported_file:
                raise ValueError(
                    """Only polar data files starting by MS, MH or ML or PM, PH, PL
                       are supported""")

            self.file_format = bfile[0] # M or P
            self.file_type = bfile[1] # H, M or L
            self.bname = bfile

            # Open binary file
            with open(filename, 'rb') as f:
                self.bytearray = memoryview(bytearray(f.read())) # bytearray

            self.read_pos = 0 # Current "cursor" position in file

            # Get endianness
            if self.file_format == 'P':
                self.endian_prefix = '>'
            elif self.file_format == 'M':
                if sys.byteorder == 'little':
                    self.endian_prefix = '<'
                else:
                    self.endian_prefix = '>'

        def parse(self, moments=None):
            """
                Parses a metranet binary file by calling the appropriate parser
                depending on the file type (M or P)

                Parameters
                ----------
                moments : list (optional)
                    list of moments to get from the file, possible moments
                    are ZH, ZV, ZDR, ST1, ST2, VEL, WID, PHI, RHO, CLT, ZVC, ZHC,
                    MPH, default = ALL moments available in the file are read

                Returns
                -------
                out : RadarClass instance
                    RadarClass instance containing the file data and metadata

            """
            self.read_pos = 0 # reset read position if needed

            out = None
            if self.file_format == 'P':
                out = self._parse_p(moments)
            elif self.file_format == 'M':
                out = self._parse_m(moments)

            return out

        def _parse_p(self, moments):
            """
                Parses a P format metranet binary file

                Parameters
                ----------
                moments : list
                    list of moments to get from the file, possible moments
                    are ZH, ZV, ZDR, ST1, ST2, VEL, WID, PHI, RHO, CLT, ZVC, ZHC,
                    MPH, if none ALL moments available in the file are read

                Returns
                -------
                head : dict
                    file metadata
                pol_header: list
                    list of metadata dictionaries for every ray
                moments_data: dictionary
                    dictionary containing the data in DN for every moment

            """

            # For P files we get available moment from pmfile_structure.py
            moments_avail = np.array(PMOMENTS['names'][self.file_type])
            nummoments = len(moments_avail)

            if moments is None:
                moments = moments_avail

            # Initialize outputs
            pol_header = []
            moments_data = {}
            for m in moments:
                moments_data[m] = []

            while self.read_pos != len(self.bytearray):
                # Get ray metadata
                ray = self._get_chunk(PRAY_HEADER)

                # Convert DN angles to float angles
                ray['startangle_az'] = _selex2deg(ray['startangle_az'])
                ray['startangle_el'] = _selex2deg(ray['startangle_el'])
                ray['endangle_az'] = _selex2deg(ray['endangle_az'])
                ray['endangle_el'] = _selex2deg(ray['endangle_el'])
                pol_header.append(ray)

                for i in range(nummoments): # After ray metadata get all moments
                    ngates = ray['numgates']

                    if moments_avail[i] in moments: # Check if this moment is required

                        # Check type of this particular moment (byte or short)
                        if moments_avail[i] in PMOMENTS['types'].keys():
                            data_type = PMOMENTS['types'][moments_avail[i]]
                        else:
                            data_type = 'B'

                        ffmt = self.endian_prefix + data_type

                        len_mom = ngates * BYTE_SIZES[data_type]
                        mom = np.ndarray((ngates,), ffmt,
                                         self.bytearray[self.read_pos:
                                                        self.read_pos + len_mom])
                        self.read_pos += len_mom
                        moments_data[moments_avail[i]].append(mom)
                    else:
                        # Even if moment is not required, we need to update cursor
                        self.read_pos += len_mom

            # Create global file header to be consistent with M files
            head = {}
            head['antmode'] = pol_header[0]['antmode']
            head['radarname'] = pol_header[0]['scanid']
            # Rename radar with appropriate dict if needed, otherwise get name from file
            if head['radarname'] in RENAME_RADARS_P.keys():
                head['radarname'] = RENAME_RADARS_P[head['radarname']]
            else:
                head['radarname'] = self.bname[2]
            head['startrange'] = pol_header[0]['startrange']
            head['currentsweep'] = pol_header[0]['currentsweep']
            head['gatewidth'] = pol_header[0]['gatewidth']

            radmetadata = _get_radar_site_info()
            radname = head['radarname']
            head['frequency'] = radmetadata[radname]['Frequency']
            head['radarheight'] = radmetadata[radname]['RadarHeight']
            head['radarlat'] = radmetadata[radname]['RadarLat']
            head['radarlon'] = radmetadata[radname]['RadarLon']

            return head, pol_header, moments_data

        def _parse_m(self, moments):
            """
                Parses a M format metranet binary file

                Parameters
                ----------
                moments : list
                    list of moments to get from the file, possible moments
                    are ZH, ZV, ZDR, ST1, ST2, VEL, WID, PHI, RHO, CLT, ZVC, ZHC,
                    MPH, if none ALL moments available in the file are read

                Returns
                -------
                head : dict
                    file metadata
                pol_header: list
                    list of metadata dictionaries for every ray
                moments_data: dictionary
                    dictionary containing the data in DN for every moment

            """

            head = self._get_chunk(MSWEEP_HEADER)
            if head['radarname'] in RENAME_RADARS_M.keys():
                head['radarname'] = RENAME_RADARS_M[head['radarname']] # make consistent names
            else: # get radar name from filename
                head['radarname'] = self.bname[2]

            head['frequency'] *= 10**9 # Be consistent with C-library where it is in hZ
            nummoments = head['nummoments']

            pol_header = []

            # Get all available moments
            moments_avail = [a['name'] for a in head['moments']]

            if moments is None:
                moments = moments_avail

            moments_data = {}
            for m in moments:
                moments_data[m] = []

            while self.read_pos != len(self.bytearray):
                pol = self._get_chunk(MRAY_HEADER)
                # Convert DN angles to float angles
                pol['startangle_az'] = _selex2deg(pol['startangle_az'])
                pol['startangle_el'] = _selex2deg(pol['startangle_el'])
                pol['endangle_az'] = _selex2deg(pol['endangle_az'])
                pol['endangle_el'] = _selex2deg(pol['endangle_el'])

                # Convert datetime to UTC + residue
                pol['datatime_residue'] = int(100 * ((pol['datatime']* 0.01) % 1))
                pol['datatime'] = int(0.01 * pol['datatime'])
                pol_header.append(pol)

                for i in range(nummoments):
                    mom_header = self._get_chunk(MMOMENT_HEADER)

                    len_mom = mom_header['datasize']

                    if moments_avail[i] in moments:
                        size_moment_bytes = head['moments'][i]['num_bytes']

                        ngates = int(len_mom/size_moment_bytes) # nb of gates
                        ffmt = self.endian_prefix
                        if size_moment_bytes == 1:
                            ffmt += 'B'
                        else:
                            ffmt += 'H'

                        mom = np.ndarray((ngates,), ffmt,
                                         self.bytearray[self.read_pos:
                                                        self.read_pos + len_mom])
                        moments_data[moments_avail[i]].append(mom)
                        self.read_pos += len_mom
                    else:
                        self.read_pos += len_mom

            return head, pol_header, moments_data

        def _get_chunk(self, file_info):
            """
            Parses part of the bytearray given the info about what data to
            expect

            Parameters
            ----------
            file_info : dict
                dictionary with three keys: 'names', 'len', 'type'
                containing the names, length and types of the variables that
                should be read from the binary file. These dictionaries are
                defined in the pmfile_structure.py file

            Returns
            -------
            dic_values : dict
                dictionary containing all the variables that were read from
                the file, with keys corresponding to the 'names' key in the
                file_info dict

            """

            # Read the bytearray byte by byte
            dic_values = {} # output dictionary

            for i in range(len(file_info['names'])):
                len_val = file_info['len'][i]
                name_val = file_info['names'][i]

                if isinstance(len_val, list):
                    # If length in pmfile_structure.py is a list, generate
                    # real length with previously read key
                    len_val = len_val[0] * dic_values[len_val[1]]

                type_var = file_info['type'][i]
                ffmt = self.endian_prefix + "{}".format(int(len_val))
                ffmt += type_var

                offset = len_val * BYTE_SIZES[type_var]

                if name_val == 'moments':

                    # M files only
                    # For the moments structure some additional processing is needed
                    val = []
                    for j in range(dic_values['nummoments']):
                        # recursion!
                        val.append(self._get_chunk(MMOMENT_INFO_STRUCTURE))

                else:
                    val = struct.unpack_from(
                        ffmt, self.bytearray[self.read_pos:self.read_pos + offset])

                    # Convert to array if list
                    if len(val) == 1:
                        val = val[0]
                    else:
                        val = np.array(val)

                    if type_var == 's': # For strings
                        # Strip null hexadecimal codes
                        val = val.decode('utf-8').replace('\x00', '')
                    self.read_pos += offset

                dic_values[name_val] = val

            return dic_values

    def read_polar(filename, moments=None, physic_value=True, masked_array=True,
                   reorder_angles=True):
        """
        Reads a METRANET polar data file

        Parameters
        ----------
        radar_file : str
            file name
        moments : list
            List of moments to read, by default all are used
        physic_value : boolean
            If true returns the physical value. Otherwise the digital value
        masked_array : boolean
            If true returns a numpy masked array with NaN values masked. Otherwise
            returns a regular masked array with NaN values
        reorder_anges: boolean
            If true all recorded rays are sorted by ascending order of their angles
            In addition the scan is truncated to a maximum of 360 rays

        Returns
        -------
        ret_data : RadarData object
            An object containing the information read from the file.

        """

        parser = PolarParser(filename)
        head, pol_header, moments_data = parser.parse(moments)

        # deep-copy needed since we alter the keys during loop
        moments = copy.deepcopy(list(moments_data.keys()))

        for m in moments:
            # Find index of moment in the moments structure of the header
            moments_data[m] = np.array(
                moments_data[m], dtype=moments_data[m][0].dtype)

            if masked_array:
                if parser.file_type == 'L' and m in ['UZ','UZ_V','Z_V_CLUT','Z_CLUT']:
                    mask = np.logical_or(
                        moments_data[m] == 0, moments_data[m] == 1)
                else:
                    mask = moments_data[m] == 0

            # Rename moment if needed
            if m in MOM_NAME_MAPPING.keys():
                moments_data[MOM_NAME_MAPPING[m]] = moments_data.pop(m)
                m = MOM_NAME_MAPPING[m]

            if physic_value:
                moments_data[m] = float_mapping(
                    m, pol_header[0]['datatime'], head['radarname'],
                    nyquist_vel(head['currentsweep'] - 1))[
                        moments_data[m]].astype(np.float32)

            if masked_array:
                moments_data[m] = np.ma.array(moments_data[m], mask=mask)
            else:
                moments_data[m][mask] = np.nan

        if reorder_angles:
            # Reorder dependent angle in ascending order
            if head['antmode'] in [0, 2]:
                angles = np.array([ray['startangle_az'] for ray in pol_header])
            else:
                angles = np.array([ray['startangle_el'] for ray in pol_header])

            nray = len(angles)

            maxrays = min([nray, 360])
            angles = angles[-maxrays:]
            order = np.argsort(angles)

            tmp = [pol_header[i + nray - maxrays] for i in order]
            pol_header = tmp

            for m in moments_data.keys():
                moments_data[m] = moments_data[m][-360:][order]

        ret_data = RadarData(
            header=head, pol_header=pol_header, data=moments_data)
        return ret_data

    def read_product(radar_file, physic_value=False, masked_array=False,
                     verbose=False):
        """
        Reads a METRANET cartesian data file

        Parameters
        ----------
        radar_file : str
            file name
        physic_value : boolean
            If true returns the physical value. Otherwise the digital value
        masked_array : boolean
            If true returns a numpy masked array with NaN values masked. Otherwise
            returns a regular masked array with NaN values
        verbose : boolean
            If true prints out extra information

        Returns
        -------
        ret_data : RadarData object
            An object containing the information read from the file. None if
            the file has not been properly read

        """
        ret_data = RadarData()
        prd_header = {'row': 0, 'column': 0}

        # read ASCII data
        if verbose:
            print("physic_value: ", physic_value)
            print("File %s: read ASCII" % radar_file)

        try:
            with open(radar_file, 'rb') as data_file:
                for t_line in data_file:
                    line = t_line.decode("utf-8").strip('\n')
                    if line.find('end_header') == -1:
                        data = line.split('=')
                        prd_header[data[0]] = data[1]
                    else:
                        break

                # read BINARY data
                prdt_size = int(prd_header['column']) * int(prd_header['row'])
                if prdt_size < 1:
                    print("Error, no size found row=%3d column=%3d" %
                          (prd_header['row'], prd_header['column']))
                    return None

                if verbose:
                    print("File %s: read BINARY data: expected %s bytes, " %
                          (radar_file, prdt_size), end='')
                    print(prd_header)

                if int(prd_header['table_size']) != 0:
                    prd_data_level = np.fromfile(
                        data_file, dtype=np.dtype('>f4'),
                        count=int(int(prd_header['table_size']) / 4))
                else:
                    prd_data_level = []

                # Read and decompress data using lzw15
                prd_data_compressed_iter = readbytes_fh(data_file)
                prd_data_iter = decompress(prd_data_compressed_iter)

                prd_data_list = list()
                for k in prd_data_iter:
                    prd_data_list.append(unpackbyte(k))

                prd_data = np.asarray(prd_data_list, dtype=np.ubyte)
                prd_data = np.reshape(
                    prd_data, (int(prd_header['row']), int(prd_header['column'])))

            data_file.close()

        except OSError as ee:
            warn(str(ee))
            print("Unable to read file '%s'" % radar_file)
            return None

        # convert 0 at end of array with NAN
        conv_zero2nan = True

        nlevels = np.size(prd_data_level)
        if nlevels == 0:
            prd_data_level = np.arange(256, dtype=np.uint32)
        else:
            while conv_zero2nan:
                if nlevels == 0:
                    conv_zero2nan = False
                elif prd_data_level[nlevels-1] == 0.0:
                    prd_data_level[nlevels-1] = np.nan
                else:
                    conv_zero2nan = False

        if verbose:
            print("Found %d bytes" % np.size(prd_data))
            print("prd_data_level[10] = %f" % prd_data_level[10])
            print("min/max prd_data: %d/%d" % (prd_data.min(), prd_data.max()))
            print("first 100 bytes", prd_data[0:100, 0])
            print("data level ", prd_data_level[0:10])

        if physic_value:
            ret_data.data = prd_data_level[prd_data]
            if masked_array:
                ret_data.data = np.ma.array(
                    ret_data.data, mask=np.isnan(ret_data.data))
                ret_data.data = np.ma.masked_where(prd_data == 0, ret_data.data)
        else:
            ret_data.data = prd_data
            if masked_array:
                ret_data.data = np.ma.array(
                    ret_data.data, mask=prd_data == 0)
        ret_data.header = prd_header
        ret_data.scale = prd_data_level

        return ret_data

    def read_file(file, moment="ZH", physic_value=False, masked_array=False,
                  reorder_angles=True, verbose=False):
        """
        Reads a METRANET data file

        Parameters
        ----------
        file : str
            file name
        moment : str
            moment name
        physic_value : boolean
            If true returns the physical value. Otherwise the digital value
        masked_array : boolean
            If true returns a numpy masked array with NaN values masked. Otherwise
            returns a regular masked array with NaN values
        reorder_angles : boolean
            If true angles are reordered
        verbose : boolean
            If true prints out extra information

        Returns
        -------
        ret_data : RadarData object
            An object containing the information read from the file

        """
        bfile = os.path.basename(file)

        if (bfile.startswith('PM') or bfile.startswith('PH') or
                bfile.startswith('PL') or bfile.startswith('MS') or
                bfile.startswith('MH') or bfile.startswith('ML')):
            # polar data from SITE (PH/PM/PL)
            if verbose:
                print("calling read_polar")
            ret = read_polar(
                file, moments=moment, physic_value=physic_value,
                masked_array=masked_array, reorder_angles=reorder_angles)
        else:
            # cartesian / CCS4 products
            if verbose:
                print("calling read_product")
            ret = read_product(
                file, physic_value=physic_value, masked_array=masked_array,
                verbose=verbose)

        return ret

    def _get_radar_site_info(verbose=False):
        """
            return dictionary with radar'info

            Returns
            -------
            radar_def : dict
                dictionary containing radar site information
        """
        radar_def_load = False
        try:
            import yaml
            yaml_module = True
        except ImportError:
            yaml_module = False

        if yaml_module:
            metranet_yaml_file = "metranet.yaml"
            path_yaml_file = [
                "/store/msrad/python/library/radar/io/", "/opt/ccs4/python",
                "/proj/lom/python/library/radar/io"]
            for p in path_yaml_file:
                full_file = p + "/" + metranet_yaml_file
                if os.path.isfile(full_file):
                    print("look for Radar_Site_info from %s" % full_file)
                    try:
                        with open(full_file) as f:
                            radar_def = yaml.load(f)
                            radar_def_load = True
                            if verbose:
                                print("Read Radar_Site_info from %s" % full_file)
                    except OSError as ee:
                        warn(str(ee))
                        traceback.print_exc()

        if not radar_def_load:
            # HardCoded definition
            print("**** HARD-CODED Radar site Value, may be not updated!!")
            # some values valid for all sites
            c_speed = 299792458  # m/s

            radar_default = {}

            radar_default['dwhname'] = 'undef'
            radar_default['RadarCHY'] = float('nan')
            radar_default['RadarCHX'] = float('nan')
            radar_default['FileId'] = 1179665477
            radar_default['Version'] = 0
            radar_default['RadarName'] = "undef"
            radar_default['ScanName'] = "undef"
            radar_default['RadarLat'] = float('nan')
            radar_default['RadarLon'] = float('nan')
            radar_default['RadarHeight'] = float('nan')
            radar_default['Frequency'] = float('nan')
            radar_default['WaveLength'] = float('nan')
            radar_default['PulseWidth'] = 0.5
            radar_default['SweepsOrder'] = (
                9, 7, 5, 3, 1, 19, 17, 15, 13, 11, 10, 8, 6, 4, 2, 20, 18, 16, 14,
                12)
            radar_default['NumMomentsPM'] = NPM_MOM
            radar_default['NumMomentsPH'] = NPH_MOM
            radar_default['NumMomentsPL'] = NPL_MOM

            radar_def = {}

            rname = 'A'
            radar_def[rname] = radar_default.copy()
            radar_def[rname]['dwhname'] = "ALB"
            radar_def[rname]['RadarName'] = "Albis"
            radar_def[rname]['RadarCHY'] = 681201
            radar_def[rname]['RadarCHX'] = 237604
            radar_def[rname]['RadarLat'] = 47.284333
            radar_def[rname]['RadarLon'] = 8.512000
            radar_def[rname]['RadarHeight'] = 938.0
            radar_def[rname]['ScanName'] = "1095516672"
            radar_def[rname]['Frequency'] = 5450e6
            radar_def[rname]['WaveLength'] = (
                c_speed/radar_def[rname]['Frequency']*1e2)

            rname = 'D'
            radar_def[rname] = radar_default.copy()
            radar_def[rname]['dwhname'] = "DOL"
            radar_def[rname]['RadarName'] = "Dole"
            radar_def[rname]['RadarCHY'] = 497057
            radar_def[rname]['RadarCHX'] = 142408
            radar_def[rname]['RadarLat'] = 46.425113
            radar_def[rname]['RadarLon'] = 6.099415
            radar_def[rname]['RadarHeight'] = 1682.0
            radar_def[rname]['ScanName'] = "1146047488"
            radar_def[rname]['Frequency'] = 5430e6
            radar_def[rname]['WaveLength'] = (
                c_speed/radar_def[rname]['Frequency']*1e2)

            rname = 'L'
            radar_def[rname] = radar_default.copy()
            radar_def[rname]['dwhname'] = "MLE"
            radar_def[rname]['RadarName'] = "Lema"
            radar_def[rname]['RadarCHY'] = 707957
            radar_def[rname]['RadarCHX'] = 99762
            radar_def[rname]['RadarLat'] = 46.040761
            radar_def[rname]['RadarLon'] = 8.833217
            radar_def[rname]['RadarHeight'] = 1626.0
            radar_def[rname]['ScanName'] = "1279610112"
            radar_def[rname]['Frequency'] = 5455e6
            radar_def[rname]['WaveLength'] = (
                c_speed/radar_def[rname]['Frequency']*1e2)

            rname = 'P'
            radar_def[rname] = radar_default.copy()
            radar_def[rname]['dwhname'] = "PPM"
            radar_def[rname]['RadarName'] = "PlaineMorte"
            radar_def[rname]['RadarCHY'] = 603687
            radar_def[rname]['RadarCHX'] = 135476
            radar_def[rname]['RadarLat'] = 46.370646
            radar_def[rname]['RadarLon'] = 7.486552
            radar_def[rname]['RadarHeight'] = 2937.0
            radar_def[rname]['ScanName'] = "0"
            radar_def[rname]['Frequency'] = 5468e6
            radar_def[rname]['WaveLength'] = (
                c_speed/radar_def[rname]['Frequency']*1e2)

            rname = 'W'
            radar_def[rname] = radar_default.copy()
            radar_def[rname]['dwhname'] = "WEI"
            radar_def[rname]['RadarName'] = "WeissFluhGipfel"
            radar_def[rname]['RadarCHY'] = 779700
            radar_def[rname]['RadarCHX'] = 189790
            radar_def[rname]['RadarLat'] = 47.284333
            radar_def[rname]['RadarLon'] = 9.794458
            radar_def[rname]['RadarHeight'] = 2850.0
            radar_def[rname]['ScanName'] = "0"
            radar_def[rname]['Frequency'] = 5433e6
            radar_def[rname]['WaveLength'] = (
                c_speed/radar_def[rname]['Frequency']*1e2)

        return radar_def

    def _selex2deg(angle):
        """
        Convert angles from SELEX format to degree

        Parameters
        ----------
        angle : float
            angle in DN

        Returns
        -------
        conv : float
            angle in degrees

        """
        conv = angle * 360. / 65535.
        return conv