radar_data.py:
import matplotlib.pyplot as plt
import rasterio
from rasterio.transform import from_origin
from rasterio.warp import calculate_default_transform, reproject, Resampling
import geopandas as gpd
from shapely.geometry import Point, Polygon
from pyproj import Proj, Transformer
import struct
from tqdm import tqdm
import os
from lxml import etree
import io
import sys
import logging
import numpy as np  # Added missing import
import re
import zlib  # For data decompression
import base64

# Configure logging
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.StreamHandler(sys.stdout),
        logging.FileHandler('radar_data_processing.log', mode='w')
    ]
)

# Define constants and paths
RADAR_DATA_PATH = 'input/radar/dBZ.vol'  # Path to the radar data file
MASOVIAN_BOUNDARY_PATH = 'input/radar/masovian_boundary.geojson'  # Path to Masovian boundary GeoJSON or Shapefile
OUTPUT_PLOT_PATH = 'output/processed_radar_data.png'  # Path to save the output plot

# Radar specifications based on provided XML
AZIMUTH_RESOLUTION = 1.0  # Degrees between each azimuth
RANGE_RESOLUTION = 0.5  # As per 0.5 km

# Radar location from
RADAR_LATITUDE = 52.405250  # From 52.405250
RADAR_LONGITUDE = 20.961110  # From 20.961110
RADAR_ALTITUDE = 122.3  # From 122.300000 meters

# Coordinate Systems
SOURCE_CRS = 'EPSG:4326'  # Geographic coordinates
TARGET_CRS = 'EPSG:2180'  # Polish coordinate system

def decompress_qt(blob_data):
    """
    Decompresses data compressed with Qt's qCompress() function.

    Parameters:
        blob_data: Compressed data including the 4-byte header.

    Returns:
        Decompressed data as bytes.
    """
    if len(blob_data) < 4:
        raise ValueError("Data too short to contain Qt compressed header.")
    # Qt's qCompress uses little-endian for the header
    uncompressed_size = struct.unpack('<I', blob_data[:4])[0]
    compressed_data = blob_data[4:]
    try:
        decompressed_data = zlib.decompress(compressed_data)
    except zlib.error as e:
        logging.error(f"Zlib decompression error: {e}")
        raise
    if len(decompressed_data) != uncompressed_size:
        logging.warning(f"Decompressed data size {len(decompressed_data)} does not match expected size {uncompressed_size}.")
    return decompressed_data

def parse_vol_file(file_path):
    """
    Parses the .vol file to extract metadata about each radar slice.

    Parameters:
        file_path: Path to the .vol file.

    Returns:
        slices_info: List of dictionaries containing slice metadata.
        sensor_info: Dictionary containing radar sensor information.
        blobs_data: Dictionary mapping blobids to their binary data.
    """
    logging.info("Parsing the .vol file...")
    slices_info = []
    sensor_info = {}
    blobs_data = {}
    with open(file_path, 'rb') as f:
        content = f.read()

    # Identify the end of the XML content
    end_xml_marker_pattern = re.compile(rb'<!--\s*END XML\s*-->', re.IGNORECASE)
    match = end_xml_marker_pattern.search(content)
    if not match:
        logging.error("<!-- END XML --> marker not found in the file.")
        sys.exit(1)

    end_xml_index = match.start()
    xml_content_bytes = content[:end_xml_index]  # Exclude the END XML marker
    remaining_content = content[match.end():]

    # Parse XML
    try:
        root = etree.fromstring(xml_content_bytes)
    except etree.XMLSyntaxError as e:
        logging.error(f"Failed to parse XML content: {e}")
        sys.exit(1)

    # Extract sensor info
    sensor = root.find('.//sensorinfo')
    if sensor is not None:
        sensor_info['id'] = sensor.findtext('id')
        sensor_info['type'] = sensor.findtext('type')
        sensor_info['name'] = sensor.findtext('name')
        try:
            sensor_info['lon'] = float(sensor.findtext('lon'))
            sensor_info['lat'] = float(sensor.findtext('lat'))
            sensor_info['alt'] = float(sensor.findtext('alt'))
            sensor_info['wavelen'] = float(sensor.findtext('wavelen'))
            sensor_info['beamwidth'] = float(sensor.findtext('beamwidth'))
        except (TypeError, ValueError) as e:
            logging.error(f"Invalid sensor information: {e}")
            sys.exit(1)
    else:
        logging.error("sensorinfo not found in the XML.")
        sys.exit(1)

    # Extract slice information
    slices = root.findall('.//slice')
    for slice_elem in slices:
        slice_info = {}
        slice_info['refid'] = slice_elem.get('refid')
        # Find rawdata element for dBZ
        rawdata = slice_elem.find('.//rawdata[@type="dBZ"]')
        if rawdata is not None:
            try:
                slice_info['rays'] = int(rawdata.get('rays'))
                slice_info['bins'] = int(rawdata.get('bins'))
                slice_info['type'] = rawdata.get('type')
                slice_info['max'] = float(rawdata.get('max'))
                slice_info['min'] = float(rawdata.get('min'))
                slice_info['blobid'] = rawdata.get('blobid')
                slice_info['depth'] = int(rawdata.get('depth'))
                slices_info.append(slice_info)
                logging.info(f"Slice refid {slice_info['refid']}: rays {slice_info['rays']}, bins {slice_info['bins']}, blobid {slice_info['blobid']}.")
            except (TypeError, ValueError) as e:
                logging.warning(f"Invalid slice information in refid {slice_info.get('refid')}: {e}. Skipping this slice.")
        else:
            logging.warning(f"No dBZ rawdata found for slice refid {slice_elem.get('refid')}. Skipping this slice.")

    logging.info(f"Total slices with dBZ data: {len(slices_info)}.\n")

    # Collect required blobids for dBZ data
    required_blobids = set(slice_info['blobid'] for slice_info in slices_info)

    # Now parse the BLOBs and collect the data
    blob_header_pattern = re.compile(rb'<BLOB\s+blobid="(\d+)"\s+size="(\d+)"\s+compression="([^"]*)"\s*>', re.IGNORECASE)
    blob_footer_pattern = re.compile(rb'</BLOB>', re.IGNORECASE)
    offset = 0  # Offset within remaining_content

    while offset < len(remaining_content):
        match = blob_header_pattern.match(remaining_content, offset)
        if not match:
            # Skip any whitespace or non-matching content
            next_lt = remaining_content.find(b'<', offset + 1)
            if next_lt == -1:
                break
            offset = next_lt
            continue
        blobid = match.group(1).decode()
        size = int(match.group(2))
        compression = match.group(3).decode()
        offset = match.end()  # Move past the BLOB header

        # Skip any whitespace or newlines after the BLOB header
        while offset < len(remaining_content) and remaining_content[offset] in b'\x20\x09\x0a\x0d':
            offset += 1

        # Read the blob data
        blob_data = remaining_content[offset:offset + size]
        offset += size  # Move to the end of the blob data

        # Skip any whitespace or newlines after the data
        while offset < len(remaining_content) and remaining_content[offset] in b'\x20\x09\x0a\x0d':
            offset += 1

        # Move past the closing </BLOB> tag if present
        footer_match = blob_footer_pattern.match(remaining_content, offset)
        if footer_match:
            offset = footer_match.end()
        else:
            logging.warning(f"No closing </BLOB> tag found for blobid {blobid}. Continuing.")

        # Handle compression if necessary
        if blobid in required_blobids:
            # Retrieve the corresponding slice_info
            slice_info = next((s for s in slices_info if s['blobid'] == blobid), None)
            if not slice_info:
                logging.error(f"No slice info found for blobid {blobid}. Skipping.")
                continue
            depth = slice_info.get('depth', 8)
            if compression == 'qt':
                # Decompress using zlib
                try:
                    decompressed_data = decompress_qt(blob_data)
                    blobs_data[blobid] = decompressed_data
                    logging.info(f"Read and decompressed blobid {blobid} with size {len(decompressed_data)} bytes.")
                except Exception as e:
                    logging.error(f"Failed to decompress blobid {blobid}: {e}")
                    continue  # Skip this blob if decompression fails
            elif compression == 'none':
                blobs_data[blobid] = blob_data
                logging.info(f"Read blobid {blobid} with size {len(blob_data)} bytes (no compression).")
            else:
                logging.warning(f"Unsupported compression '{compression}' for blobid {blobid}. Skipping.")
        else:
            # Skip blobids not needed for dBZ data
            logging.info(f"Skipping blobid {blobid} as it is not required for dBZ data.")

    return slices_info, sensor_info, blobs_data

def extract_dBZ_data(slices_info, blobs_data):
    """
    Extracts dBZ data from the blobs_data based on slice metadata.

    Parameters:
        slices_info: List of dictionaries containing slice metadata.
        blobs_data: Dictionary mapping blobids to their binary data.

    Returns:
        dBZ_combined: 2D numpy array combining all slices (azimuths x range_bins).
    """
    logging.info("Extracting dBZ data from blobs_data...")
    dBZ_list = []

    for idx, slice_info in enumerate(slices_info):
        rays = slice_info['rays']
        bins = slice_info['bins']
        blobid = slice_info['blobid']
        depth = slice_info.get('depth', 16)
        try:
            blob_data = blobs_data[blobid]
        except KeyError:
            logging.error(f"Blob data for blobid {blobid} not found. Skipping this slice.")
            continue
        # Determine data type based on depth
        if depth == 8:
            dtype = np.uint8
            byte_size = 1
            # No scaling needed if data is raw
            dBZ = np.frombuffer(blob_data, dtype=dtype).reshape((rays, bins))
            dBZ = dBZ.astype(np.float32)  # Convert to float for consistency
            dBZ = dBZ * 1.0  # Apply scaling if necessary
        elif depth == 16:
            dtype = np.uint16
            byte_size = 2
            scale = (slice_info['max'] - slice_info['min']) / (2 ** depth - 2)
            offset = slice_info['min']
            dBZ = np.frombuffer(blob_data, dtype=dtype).reshape((rays, bins))
            dBZ = dBZ * scale + offset
        elif depth == 32:
            dtype = np.float32
            byte_size = 4
            dBZ = np.frombuffer(blob_data, dtype=dtype).reshape((rays, bins))
        else:
            logging.warning(f"Unsupported depth {depth} for slice refid {slice_info['refid']}. Skipping this slice.")
            continue
        dBZ_list.append(dBZ)
        logging.info(f"Slice {idx + 1} refid {slice_info['refid']}: Extracted dBZ data with shape {dBZ.shape}.")

    if not dBZ_list:
        logging.error("No dBZ data extracted. Exiting.")
        sys.exit(1)

    # Ensure all slices have the same number of bins by padding with np.nan
    max_bins = max(dBZ.shape[1] for dBZ in dBZ_list)
    logging.info(f"Maximum number of bins across slices: {max_bins}")

    padded_dBZ_list = []
    for idx, dBZ in enumerate(dBZ_list):
        current_bins = dBZ.shape[1]
        if current_bins < max_bins:
            padding = ((0, 0), (0, max_bins - current_bins))
            dBZ_padded = np.pad(dBZ, padding, mode='constant', constant_values=np.nan)
            logging.info(f"Padded slice {idx + 1} from {current_bins} to {max_bins} bins.")
        else:
            dBZ_padded = dBZ
        padded_dBZ_list.append(dBZ_padded)

    # Stack all slices vertically
    try:
        dBZ_combined = np.vstack(padded_dBZ_list)
        logging.info(f"Combined dBZ data shape after padding and stacking: {dBZ_combined.shape}\n")
    except Exception as e:
        logging.error(f"Failed to stack dBZ data: {e}")
        sys.exit(1)

    return dBZ_combined

def polar_to_cartesian(dBZ, az_res, rng_res, max_range=None):
    """
    Converts polar radar data to Cartesian coordinates.

    Parameters:
        dBZ: 2D numpy array of dBZ values (azimuths x ranges).
        az_res: Azimuth resolution in degrees.
        rng_res: Range resolution in km.
        max_range: Maximum radar range in km.

    Returns:
        xs: 1D numpy array of x coordinates in meters.
        ys: 1D numpy array of y coordinates in meters.
        dBZ_1D: 1D numpy array of dBZ values.
    """
    logging.info("Converting polar coordinates to Cartesian coordinates...")
    num_az, num_rng = dBZ.shape
    # Ensure that azimuths cover 0 to 360 degrees
    azimuths = np.deg2rad(np.linspace(0, 360, num=num_az, endpoint=False))
    # Calculate ranges
    ranges = np.linspace(rng_res / 2, num_rng * rng_res - rng_res / 2, num=num_rng) * 1000  # Convert km to meters

    if max_range is not None:
        expected_bins = int(max_range / rng_res)
        if num_rng < expected_bins:
            logging.warning(f"Number of range bins ({num_rng}) is less than expected ({expected_bins}). Adjusting max range.")
            max_range = num_rng * rng_res
        elif num_rng > expected_bins:
            logging.warning(f"Number of range bins ({num_rng}) is greater than expected ({expected_bins}). Truncating.")
            dBZ = dBZ[:, :expected_bins]
            num_rng = expected_bins
            ranges = ranges[:expected_bins]

    # Create meshgrid
    az, r = np.meshgrid(azimuths, ranges, indexing='ij')

    # Convert to Cartesian coordinates
    x = r * np.sin(az)
    y = r * np.cos(az)

    # Flatten the arrays
    xs = x.flatten()
    ys = y.flatten()
    dBZ_1D = dBZ.flatten()

    logging.info("Coordinate conversion completed.\n")
    return xs, ys, dBZ_1D

def create_geodataframe(xs, ys, dBZ):
    """
    Creates a GeoDataFrame from Cartesian coordinates and dBZ values.

    Parameters:
        xs: 1D numpy array of x coordinates in meters.
        ys: 1D numpy array of y coordinates in meters.
        dBZ: 1D numpy array of dBZ values.

    Returns:
        gdf: GeoDataFrame with geometry in geographic coordinates.
    """
    logging.info("Creating GeoDataFrame...")
    # Define local projection (using Azimuthal Equidistant projection centered at radar location)
    proj_local = Proj(proj='aeqd', lat_0=RADAR_LATITUDE, lon_0=RADAR_LONGITUDE, ellps='WGS84', units='m')
    transformer = Transformer.from_proj(proj_local, 'epsg:4326', always_xy=True)

    # Transform to geographic coordinates
    lon, lat = transformer.transform(xs, ys)

    # Create GeoDataFrame
    geometry = [Point(xy) for xy in zip(lon, lat)]
    gdf = gpd.GeoDataFrame({'dBZ': dBZ}, geometry=geometry, crs='EPSG:4326')

    logging.info("GeoDataFrame creation completed.\n")
    return gdf

def reproject_gdf(gdf, target_crs='EPSG:2180'):
    """
    Reprojects the GeoDataFrame to the target CRS.

    Parameters:
        gdf: Input GeoDataFrame.
        target_crs: Target Coordinate Reference System.

    Returns:
        gdf_reprojected: Reprojected GeoDataFrame.
    """
    logging.info(f"Reprojecting GeoDataFrame to {target_crs}...")
    try:
        gdf_reprojected = gdf.to_crs(target_crs)
        logging.info("Reprojection completed.\n")
    except Exception as e:
        logging.error(f"Failed to reproject GeoDataFrame: {e}")
        sys.exit(1)
    return gdf_reprojected

def clip_to_masovian(gdf, masovian_boundary):
    """
    Clips the GeoDataFrame to the Masovian boundary.

    Parameters:
        gdf: Input GeoDataFrame.
        masovian_boundary: GeoDataFrame or Polygon of Masovian boundary.

    Returns:
        gdf_clipped: Clipped GeoDataFrame.
    """
    logging.info("Clipping radar data to Masovian boundary...")
    try:
        # Ensure geometries are valid
        masovian_boundary = masovian_boundary.unary_union
        gdf_clipped = gdf[gdf.geometry.within(masovian_boundary)]
        logging.info(f"Clipped data contains {len(gdf_clipped)} points.\n")
    except Exception as e:
        logging.error(f"Failed to clip data to Masovian boundary: {e}")
        sys.exit(1)
    return gdf_clipped

def visualize_radar_data(gdf, masovian_boundary, output_path):
    """
    Visualizes the radar data.

    Parameters:
        gdf: GeoDataFrame containing clipped radar data.
        masovian_boundary: GeoDataFrame or Polygon of Masovian boundary.
        output_path: Path to save the plot.

    Returns:
        None
    """
    logging.info("Visualizing radar data...")
    try:
        fig, ax = plt.subplots(figsize=(12, 12))

        # Plot Masovian boundary
        masovian_boundary.boundary.plot(ax=ax, edgecolor='black', linewidth=1)

        # Plot radar dBZ values using scatter plot
        # Filter out invalid dBZ values (e.g., NaN or extreme values)
        valid_gdf = gdf[(gdf['dBZ'] >= -30) & (gdf['dBZ'] <= 95.5)]

        scatter = ax.scatter(
            valid_gdf.geometry.x,
            valid_gdf.geometry.y,
            c=valid_gdf['dBZ'],
            cmap='jet',
            s=5,
            alpha=0.6
        )

        ax.set_title("Processed Radar dBZ Data within Masovian Region", fontsize=16)
        ax.set_xlabel("Easting (meters)", fontsize=12)
        ax.set_ylabel("Northing (meters)", fontsize=12)
        ax.set_aspect('equal')

        # Remove axis ticks for cleaner look
        ax.set_xticks([])
        ax.set_yticks([])

        # Add colorbar manually
        cbar = fig.colorbar(scatter, ax=ax, fraction=0.036, pad=0.04)
        cbar.set_label('dBZ', fontsize=12)

        # Save the plot
        plt.savefig(output_path, dpi=300, bbox_inches='tight')
        plt.close()
        logging.info(f"Radar data visualization saved to {output_path}.\n")
    except Exception as e:
        logging.error(f"Failed to visualize radar data: {e}")
        sys.exit(1)

def main():
    # Step 1: Parse the .vol file
    if not os.path.exists(RADAR_DATA_PATH):
        logging.error(f"Radar data file not found at {RADAR_DATA_PATH}. Please check the path.")
        sys.exit(1)

    slices_info, sensor_info, blobs_data = parse_vol_file(RADAR_DATA_PATH)

    # Step 2: Extract dBZ data from blobs_data
    dBZ_combined = extract_dBZ_data(slices_info, blobs_data)

    # Step 3: Convert polar to Cartesian coordinates
    xs, ys, dBZ_1D = polar_to_cartesian(
        dBZ_combined,
        AZIMUTH_RESOLUTION,
        RANGE_RESOLUTION
    )

    # Step 4: Create GeoDataFrame
    gdf_geo = create_geodataframe(xs, ys, dBZ_1D)

    # Step 5: Reproject to EPSG:2180
    gdf_proj = reproject_gdf(gdf_geo, TARGET_CRS)

    # Step 6: Load Masovian Boundary
    if not os.path.exists(MASOVIAN_BOUNDARY_PATH):
        logging.error(f"Masovian boundary file not found at {MASOVIAN_BOUNDARY_PATH}. Please check the path.")
        sys.exit(1)
    try:
        masovian_boundary = gpd.read_file(MASOVIAN_BOUNDARY_PATH)
        logging.info("Loaded Masovian boundary successfully.\n")
    except Exception as e:
        logging.error(f"Failed to load Masovian boundary: {e}")
        sys.exit(1)

    # Ensure Masovian boundary is in the same CRS as target CRS
    if masovian_boundary.crs != TARGET_CRS:
        try:
            masovian_boundary = masovian_boundary.to_crs(TARGET_CRS)
            logging.info(f"Reprojected Masovian boundary to {TARGET_CRS}.\n")
        except Exception as e:
            logging.error(f"Failed to reproject Masovian boundary: {e}")
            sys.exit(1)

    # Step 7: Clip Radar Data to Masovian Boundary
    gdf_clipped = clip_to_masovian(gdf_proj, masovian_boundary)

    # Step 8: Visualize and Plot
    visualize_radar_data(gdf_clipped, masovian_boundary, OUTPUT_PLOT_PATH)

    logging.info("Radar data processing and visualization completed successfully.")

if __name__ == "__main__":
    main()
dBZ.vol (Germatronik Rainbow) file:
<volume type="vol" version="5.63.6" datetime="2024-10-05T18:49:14Z" owner="">
   <scan date="2024-10-05" datetimehighaccuracy="2024-10-05T18:48:54.193" time="18:48:54" datetimeendofslice="false" name="LEG_125_ZVW.vol">
      <unitid>SI</unitid>
      <advancedchanged>0</advancedchanged>
      <detailedchanged>1</detailedchanged>
      <scantime>249</scantime>
      <starttime>2024-10-05T18:46:32</starttime>
      <endtime>2024-10-05T18:49:14</endtime>
      <nominalstarttime>2024-10-05T18:46:30</nominalstarttime>
      <nominalendtime>2024-10-05T18:51:30</nominalendtime>
      <pargroup refid="sdfbase">
         <multitrip>Off</multitrip>
         <numtrip>2</numtrip>
         <maxsuppressedtrip>3</maxsuppressedtrip>
         <triprecovery>Off</triprecovery>
         <multitripprfmode>Off</multitripprfmode>
         <phase_coding>2</phase_coding>
         <phase_pattern>0</phase_pattern>
         <stoprange>250</stoprange>
         <numele>8</numele>
         <scanstrategy>Optimised</scanstrategy>
         <firstele>0.5</firstele>
         <lastele>40</lastele>
         <antdirection>0</antdirection>
         <sectorscan>Off</sectorscan>
         <gdrx_dp_proc_mode>GdrxDpModeHV_HV</gdrx_dp_proc_mode>
         <highprf>550.00</highprf>
         <stagger>None</stagger>
         <lowprf>0.00</lowprf>
         <midprf>0.00</midprf>
         <nyquistextensionfactor>2.05</nyquistextensionfactor>
         <nyquistextensionmode>FreePRF</nyquistextensionmode>
         <start_range>0</start_range>
         <rangestep>0.5</rangestep>
         <rangesamp>1</rangesamp>
         <cmd>Off</cmd>
         <antspeed>24</antspeed>
         <anglestep>1</anglestep>
         <timesamp>23</timesamp>
         <dynpw_matchedrangeresolution>None</dynpw_matchedrangeresolution>
         <pw_index>0</pw_index>
         <cf_doppler>Off</cf_doppler>
         <fftfilter>On</fftfilter>
         <filtermode>FFT</filtermode>
         <cf_spatial>On</cf_spatial>
         <speckle>Off</speckle>
         <gdrx_clutter_flag_filter>Off</gdrx_clutter_flag_filter>
         <cf_dectree>Off</cf_dectree>
         <cf_gip>Off</cf_gip>
         <matchedfiltertype>NoAlgType</matchedfiltertype>
         <filterwidth>6</filterwidth>
         <cf_gip_width>0.5000</cf_gip_width>
         <cf_gip_mode_const>On</cf_gip_mode_const>
         <gdrx_cluttermap>GdrxCmOff</gdrx_cluttermap>
         <int_filter_mode>1</int_filter_mode>
         <int_filter_corr_mode>Interpolate</int_filter_corr_mode>
         <int_filter_hconf>18</int_filter_hconf>
         <int_filter_vconf>18</int_filter_vconf>
         <int_filter_phaseint>On</int_filter_phaseint>
         <fixselect>AngleStep</fixselect>
         <superresolution>false</superresolution>
         <csr>30</csr>
         <sqi>0.3</sqi>
         <zsqi>0.5</zsqi>
         <log>2</log>
         <extrarclfile></extrarclfile>
         <comment></comment>
         <dualprfmode>SdfDPrfModeAda</dualprfmode>
         <prfcorrthr>2</prfcorrthr>
         <prfcorron>On</prfcorron>
         <prfcorrmode>SdfDPrfCorrMedian</prfcorrmode>
         <filterdepth>SdfCfDepthDefault</filterdepth>
         <fft_weight>GdrxWfHamming</fft_weight>
         <spatial_modus>SdfSpatialFilterAvg</spatial_modus>
         <spatial_min>3</spatial_min>
         <spatial_max>5</spatial_max>
         <gdrx_tripnotchwidth>0.5</gdrx_tripnotchwidth>
         <gdrx_tripccorthres>20</gdrx_tripccorthres>
         <gdrx_tripsnrthres>3</gdrx_tripsnrthres>
         <gdrx_tripsqithres>0.4</gdrx_tripsqithres>
         <gdrx_tripdftwin>GdrxWfRectangular</gdrx_tripdftwin>
         <datatypes>CCOR,CCORv,dBZ,dBuZ,dBZv,dBuZv,FLAGS,uKDP,KDP,uPhiDP,PhiDP,RhoHV,SNR,SNRu,SNRv,SQI,SQIv,V,W,ZDR</datatypes>
         <masterdatatypes>CCOR,dBZ,FLAGS,KDP,PhiDP,RhoHV,SNR,SQI,V,W,ZDR</masterdatatypes>
         <datatypes_format>dBZ=0,dBZv=0,dBuZ=0,dBuZv=0,V=0,Vv=0,Vu=0,Vvu=0,W=0,Wv=0,Wu=0,Wvu=0,ZDR=0,ZDRu=0,uPhiDP=1,uPhiDPu=1,PhiDP=1,KDP=0,uKDP=0,uKDPu=0,RhoHV=0,RhoHVu=0,CCOR=0,CCORv=0,SNR=1,SNRv=0,SNRu=0,SNRvu=0,SQI=0,SQIu=0,SQIv=0,SQIvu=0,FLAGS=-1</datatypes_format>
         <posanglelist>23.800000,15.000000,9.100000,5.400000,3.500000,2.500000,1.500000,0.500000,3.500000,2.500000,1.500000,0.500000</posanglelist>
         <dynpw>0.50</dynpw>
         <dynpw_constantdc_enabled>Off</dynpw_constantdc_enabled>
         <dynpw_constantdc>0.00</dynpw_constantdc>
         <useonlinenoisepower>Off</useonlinenoisepower>
      </pargroup>
      <slice refid="0">
         <dynz max="95.5" min="-31.5"/>
         <dynv max="47.79" min="-47.79"/>
         <dynw max="15.8678" min="0.0622266"/>
         <dynzdr max="12" min="-8"/>
         <dynldr max="10" min="-50"/>
         <dynkdp max="10" min="-10"/>
         <dynsnr max="127" min="0"/>
         <posangle>0.5</posangle>
         <antspeed>18</antspeed>
         <timesamp>29</timesamp>
         <calibslottypes>DATA</calibslottypes>
         <cf_gip_width>0.4</cf_gip_width>
         <int_filter_mode>3</int_filter_mode>
         <int_filter_hconf>10</int_filter_hconf>
         <int_filter_vconf>10</int_filter_vconf>
         <prtstaggering>0</prtstaggering>
         <prtprf>None</prtprf>
         <threshold>dBZ:622602,dBZv:622602,dBuZ:1,dBuZv:1,V:524294,Vv:524294,Vu:1,Vvu:1,W:524294,Wv:524294,Wu:1,Wvu:1,ZDR:622602,ZDRu:1,uPhiDP:2,uPhiDPu:1,PhiDP:524290,KDP:524290,uKDP:2,uKDPu:1,RhoHV:622594,RhoHVu:1</threshold>
         <numtrip>2</numtrip>
         <maxsuppressedtrip>3</maxsuppressedtrip>
         <stoprange>125</stoprange>
         <start_range>0</start_range>
         <rangestep>0.5</rangestep>
         <rangesamp>4</rangesamp>
         <sectorscan>Off</sectorscan>
         <cf_doppler>Off</cf_doppler>
         <fft_weight>GdrxWfHamming</fft_weight>
         <spatial_modus>SdfSpatialFilterAvg</spatial_modus>
         <spatial_min>3</spatial_min>
         <spatial_max>5</spatial_max>
         <startangle>0.0</startangle>
         <stopangle>0.0</stopangle>
         <highprf>1200</highprf>
         <stagger>4/3</stagger>
         <prfcorrmode>SdfDPrfCorrMedian</prfcorrmode>
         <prfcorron>On</prfcorron>
         <prfcorrthr>2</prfcorrthr>
         <dualprfmode>SdfDPrfModeAda</dualprfmode>
         <lowprf>900.00</lowprf>
         <fftfilter>Off</fftfilter>
         <filterwidth>4</filterwidth>
         <filterdepth>SdfCfDepthDefault</filterdepth>
         <statisticalfilter>Off</statisticalfilter>
         <cf_spatial>Off</cf_spatial>
         <superresolution>Off</superresolution>
         <antdirection>0</antdirection>
         <fixselect>AngleStep</fixselect>
         <anglestep>1</anglestep>
         <asyncoffset>0</asyncoffset>
         <anglesync>On</anglesync>
         <csr>30</csr>
         <sqi>0.1</sqi>
         <zsqi>0.1</zsqi>
         <log>2</log>
         <speckle>On</speckle>
         <extrarclfile></extrarclfile>
         <phase_coding>2</phase_coding>
         <gdrx_cluttermap>GdrxCmOff</gdrx_cluttermap>
         <gdrx_tripnotchwidth>0.5</gdrx_tripnotchwidth>
         <gdrx_tripccorthres>20</gdrx_tripccorthres>
         <gdrx_tripsnrthres>3</gdrx_tripsnrthres>
         <gdrx_tripsqithres>0.4</gdrx_tripsqithres>
         <gdrx_tripdftwin>GdrxWfRectangular</gdrx_tripdftwin>
         <gdrx_clutter_flag_filter>Off</gdrx_clutter_flag_filter>
         <cf_gip>On</cf_gip>
         <cf_gip_mode_const>On</cf_gip_mode_const>
         <cf_gip_max_width>0.5000</cf_gip_max_width>
         <cmd>On</cmd>
         <int_filter_phaseint>On</int_filter_phaseint>
         <int_filter_corr_mode>Interpolate</int_filter_corr_mode>
         <cf_dectree>Off</cf_dectree>
         <kdpautoscale>On</kdpautoscale>
         <triprecovery>Off</triprecovery>
         <midprf>0.00</midprf>
         <matchedfiltertype>SdfStaticLookupFilter</matchedfiltertype>
         <dynpw>0.95</dynpw>
         <dynpw_constantdc_enabled>On</dynpw_constantdc_enabled>
         <dynpw_constantdc>80</dynpw_constantdc>
         <dynpw_matchedrangeresolution>None</dynpw_matchedrangeresolution>
         <nyquistextensionmode>FreePRF</nyquistextensionmode>
         <nyquistextensionfactor>3.00</nyquistextensionfactor>
         <scanno>8</scanno>
         <scantotalno>12</scantotalno>
         <volno>28462</volno>
         <trackno>6139</trackno>
         <radarprefix_sp>262plc1_sq</radarprefix_sp>
         <radarprefix_rcp>262plc1_sq_lk</radarprefix_rcp>
         <acufaultstat>OK</acufaultstat>
         <gdrx5faultstat>OK</gdrx5faultstat>
         <gdrxtransmitfreq>5649.8447</gdrxtransmitfreq>
         <mpdbcufaultstat>OK</mpdbcufaultstat>
         <radargroundheight>93.3</radargroundheight>
         <radarLightningProtClass>0</radarLightningProtClass>
         <athattenuation>0.019</athattenuation>
         <rsplambda>5.31</rsplambda>
         <rsradarheight>122.3</rsradarheight>
         <rxfaultstat>OK</rxfaultstat>
         <spbantgain>45.38</spbantgain>
         <injectlossh>20.7</injectlossh>
         <spbdphtxcalpowkw>217.77</spbdphtxcalpowkw>
         <spbdpvantgain>45.37</spbdpvantgain>
         <injectlossv>21.08</injectlossv>
         <spbdpvrxloss>3.51</spbdpvrxloss>
         <spbdpvsysgainoffset>0.0</spbdpvsysgainoffset>
         <spbdpvtxcalpowkw>210.86</spbdpvtxcalpowkw>
         <spbdpvtxloss>0.66</spbdpvtxloss>
         <spbhorbeam>0.95</spbhorbeam>
         <spbphidpoffpw>0.00</spbphidpoffpw>
         <spbradomloss>0.2</spbradomloss>
         <spbrefindex>0.93</spbrefindex>
         <spbrxloss>3.32</spbrxloss>
         <spbsysgainoffset>0.0</spbsysgainoffset>
         <spbtxcalpowkw>217.77</spbtxcalpowkw>
         <spbtxloss>0.66</spbtxloss>
         <spbverbeam>0.99</spbverbeam>
         <txfaultstat>OK</txfaultstat>
         <txwarningstat>OK</txwarningstat>
         <rxbandwidth>1100000</rxbandwidth>
         <powerdiff>-0.15</powerdiff>
         <noise_power_dbz>-42.2651</noise_power_dbz>
         <noise_power_dbz_dpv>-42.3474</noise_power_dbz_dpv>
         <rspradconst>69.534</rspradconst>
         <rspdphradconst>69.534</rspdphradconst>
         <rspdpvradconst>69.884</rspdpvradconst>
         <spbtxcorr>4</spbtxcorr>
         <gdrxafcmode>20</gdrxafcmode>
         <gdrxafcfreq>0.0</gdrxafcfreq>
         <gdrxanctxfreq>59.8483</gdrxanctxfreq>
         <slicedata date="2024-10-05" datetimehighaccuracy="2024-10-05T18:48:54.193" time="18:48:54" stopdatetimehighaccuracy="2024-10-05T18:49:14.192">
            <rayinfo rays="360" blobid="0" depth="16" refid="startangle"/>
            <rayinfo rays="360" blobid="1" depth="16" refid="stopangle"/>
            <rawdata rays="360" bins="250" type="dBZ" blobid="2" depth="8" max="95.5" min="-31.5"/>
         </slicedata>
      </slice>
      <slice refid="1">
         <posangle>1.5</posangle>
         <scanno>7</scanno>
         <trackno>6138</trackno>
         <gdrxanctxfreq>59.9227</gdrxanctxfreq>
         <slicedata date="2024-10-05" datetimehighaccuracy="2024-10-05T18:48:33.191" time="18:48:33" stopdatetimehighaccuracy="2024-10-05T18:48:53.192">
            <rayinfo rays="360" blobid="3" depth="16" refid="startangle"/>
            <rayinfo rays="360" blobid="4" depth="16" refid="stopangle"/>
            <rawdata rays="360" bins="250" type="dBZ" blobid="5" depth="8" max="95.5" min="-31.5"/>
         </slicedata>
      </slice>
      <slice refid="2">
         <posangle>2.5</posangle>
         <scanno>6</scanno>
         <trackno>6137</trackno>
         <gdrxanctxfreq>60.0078</gdrxanctxfreq>
         <slicedata date="2024-10-05" datetimehighaccuracy="2024-10-05T18:48:12.193" time="18:48:12" stopdatetimehighaccuracy="2024-10-05T18:48:32.192">
            <rayinfo rays="360" blobid="6" depth="16" refid="startangle"/>
            <rayinfo rays="360" blobid="7" depth="16" refid="stopangle"/>
            <rawdata rays="360" bins="250" type="dBZ" blobid="8" depth="8" max="95.5" min="-31.5"/>
         </slicedata>
      </slice>
      <slice refid="3">
         <posangle>3.5</posangle>
         <scanno>5</scanno>
         <trackno>6136</trackno>
         <noise_power_dbz_dpv>-42.3475</noise_power_dbz_dpv>
         <gdrxanctxfreq>60.0972</gdrxanctxfreq>
         <slicedata date="2024-10-05" datetimehighaccuracy="2024-10-05T18:47:51.357" time="18:47:51" stopdatetimehighaccuracy="2024-10-05T18:48:11.355">
            <rayinfo rays="360" blobid="9" depth="16" refid="startangle"/>
            <rayinfo rays="360" blobid="10" depth="16" refid="stopangle"/>
            <rawdata rays="360" bins="250" type="dBZ" blobid="11" depth="8" max="95.5" min="-31.5"/>
         </slicedata>
      </slice>
      <slice refid="4">
         <dynv max="35.1257" min="-35.1257"/>
         <dynw max="11.6628" min="0.0457365"/>
         <posangle>5.4</posangle>
         <stoprange>170</stoprange>
         <highprf>882</highprf>
         <lowprf>661.50</lowprf>
         <dynpw>1.29</dynpw>
         <antspeed>16</antspeed>
         <int_filter_hconf>6</int_filter_hconf>
         <int_filter_vconf>6</int_filter_vconf>
         <timesamp>24</timesamp>
         <cf_gip_width>0.3</cf_gip_width>
         <scanno>4</scanno>
         <trackno>6135</trackno>
         <rxbandwidth>800000</rxbandwidth>
         <noise_power_dbz>-44.9218</noise_power_dbz>
         <noise_power_dbz_dpv>-45.0041</noise_power_dbz_dpv>
         <rspradconst>68.206</rspradconst>
         <rspdphradconst>68.206</rspdphradconst>
         <rspdpvradconst>68.556</rspdpvradconst>
         <gdrxanctxfreq>60.2026</gdrxanctxfreq>
         <slicedata date="2024-10-05" datetimehighaccuracy="2024-10-05T18:47:27.754" time="18:47:27" stopdatetimehighaccuracy="2024-10-05T18:47:50.252">
            <rayinfo rays="360" blobid="12" depth="16" refid="startangle"/>
            <rayinfo rays="360" blobid="13" depth="16" refid="stopangle"/>
            <rawdata rays="360" bins="340" type="dBZ" blobid="14" depth="8" max="95.5" min="-31.5"/>
         </slicedata>
      </slice>
      <slice refid="5">
         <posangle>9.1</posangle>
         <antspeed>20</antspeed>
         <timesamp>26</timesamp>
         <calibslottypes>DATA,NOISE</calibslottypes>
         <cf_gip_width>0.3</cf_gip_width>
         <scanno>3</scanno>
         <trackno>6134</trackno>
         <gdrxanctxfreq>60.2829</gdrxanctxfreq>
         <slicedata date="2024-10-05" datetimehighaccuracy="2024-10-05T18:47:08.491" time="18:47:08" stopdatetimehighaccuracy="2024-10-05T18:47:26.494">
            <rayinfo rays="360" blobid="15" depth="16" refid="startangle"/>
            <rayinfo rays="360" blobid="16" depth="16" refid="stopangle"/>
            <rawdata rays="360" bins="250" type="dBZ" blobid="17" depth="8" max="95.5" min="-31.5"/>
         </slicedata>
      </slice>
      <slice refid="6">
         <posangle>15.0</posangle>
         <timesamp>24</timesamp>
         <calibslottypes>DATA,NOISE</calibslottypes>
         <antspeed>22</antspeed>
         <cf_gip_width>0.3</cf_gip_width>
         <int_filter_mode>2</int_filter_mode>
         <int_filter_hconf>16</int_filter_hconf>
         <int_filter_vconf>16</int_filter_vconf>
         <scanno>2</scanno>
         <trackno>6133</trackno>
         <gdrxanctxfreq>60.111</gdrxanctxfreq>
         <slicedata date="2024-10-05" datetimehighaccuracy="2024-10-05T18:46:50.621" time="18:46:50" stopdatetimehighaccuracy="2024-10-05T18:47:06.984">
            <rayinfo rays="360" blobid="18" depth="16" refid="startangle"/>
            <rayinfo rays="360" blobid="19" depth="16" refid="stopangle"/>
            <rawdata rays="360" bins="250" type="dBZ" blobid="20" depth="8" max="95.5" min="-31.5"/>
         </slicedata>
      </slice>
      <slice refid="7">
         <posangle>23.8</posangle>
         <cf_gip_width>0.3</cf_gip_width>
         <int_filter_mode>2</int_filter_mode>
         <int_filter_hconf>16</int_filter_hconf>
         <int_filter_vconf>16</int_filter_vconf>
         <antspeed>22</antspeed>
         <timesamp>24</timesamp>
         <calibslottypes>DATA,NOISE</calibslottypes>
         <scanno>1</scanno>
         <trackno>6132</trackno>
         <noise_power_dbz_dpv>-42.3475</noise_power_dbz_dpv>
         <gdrxanctxfreq>59.9191</gdrxanctxfreq>
         <slicedata date="2024-10-05" datetimehighaccuracy="2024-10-05T18:46:32.575" time="18:46:32" stopdatetimehighaccuracy="2024-10-05T18:46:48.938">
            <rayinfo rays="360" blobid="21" depth="16" refid="startangle"/>
            <rayinfo rays="360" blobid="22" depth="16" refid="stopangle"/>
            <rawdata rays="360" bins="250" type="dBZ" blobid="23" depth="8" max="95.5" min="-31.5"/>
         </slicedata>
      </slice>
   </scan>
   <sensorinfo id="LEG" type="gdrx" name="Legionowo">
      <lon>20.961110</lon>
      <lat>52.405250</lat>
      <alt>122.300000</alt>
      <wavelen>0.0531</wavelen>
      <beamwidth>0.95</beamwidth>
   </sensorinfo>
</volume>
<!-- END XML -->
<BLOB blobid="0" size="735" compression="qt">

You should fix this error in the Python file:
2024-10-05 23:37:04,588 - INFO - Parsing the .vol file...
2024-10-05 23:37:04,588 - INFO - Slice refid 0: rays 360, bins 250, blobid 2.
2024-10-05 23:37:04,588 - INFO - Slice refid 1: rays 360, bins 250, blobid 5.
2024-10-05 23:37:04,588 - INFO - Slice refid 2: rays 360, bins 250, blobid 8.
2024-10-05 23:37:04,588 - INFO - Slice refid 3: rays 360, bins 250, blobid 11.
2024-10-05 23:37:04,588 - INFO - Slice refid 4: rays 360, bins 340, blobid 14.
2024-10-05 23:37:04,588 - INFO - Slice refid 5: rays 360, bins 250, blobid 17.
2024-10-05 23:37:04,588 - INFO - Slice refid 6: rays 360, bins 250, blobid 20.
2024-10-05 23:37:04,588 - INFO - Slice refid 7: rays 360, bins 250, blobid 23.
2024-10-05 23:37:04,588 - INFO - Total slices with dBZ data: 8.

2024-10-05 23:37:04,588 - INFO - Skipping blobid 0 as it is not required for dBZ data.
2024-10-05 23:37:04,588 - INFO - Skipping blobid 1 as it is not required for dBZ data.
2024-10-05 23:37:04,604 - ERROR - Zlib decompression error: Error -3 while decompressing data: incorrect header check
2024-10-05 23:37:04,604 - ERROR - Failed to decompress blobid 2: Error -3 while decompressing data: incorrect header check
2024-10-05 23:37:04,604 - INFO - Skipping blobid 3 as it is not required for dBZ data.
2024-10-05 23:37:04,604 - INFO - Skipping blobid 4 as it is not required for dBZ data.
2024-10-05 23:37:04,605 - ERROR - Zlib decompression error: Error -3 while decompressing data: incorrect header check
2024-10-05 23:37:04,605 - ERROR - Failed to decompress blobid 5: Error -3 while decompressing data: incorrect header check
2024-10-05 23:37:04,606 - INFO - Skipping blobid 6 as it is not required for dBZ data.
2024-10-05 23:37:04,606 - INFO - Skipping blobid 7 as it is not required for dBZ data.
2024-10-05 23:37:04,607 - ERROR - Zlib decompression error: Error -3 while decompressing data: incorrect header check
2024-10-05 23:37:04,607 - ERROR - Failed to decompress blobid 8: Error -3 while decompressing data: incorrect header check
2024-10-05 23:37:04,607 - INFO - Skipping blobid 9 as it is not required for dBZ data.
2024-10-05 23:37:04,607 - INFO - Skipping blobid 10 as it is not required for dBZ data.
2024-10-05 23:37:04,608 - ERROR - Zlib decompression error: Error -3 while decompressing data: incorrect header check
2024-10-05 23:37:04,608 - ERROR - Failed to decompress blobid 11: Error -3 while decompressing data: incorrect header check
2024-10-05 23:37:04,609 - INFO - Skipping blobid 12 as it is not required for dBZ data.
2024-10-05 23:37:04,609 - INFO - Skipping blobid 13 as it is not required for dBZ data.
2024-10-05 23:37:04,609 - ERROR - Zlib decompression error: Error -3 while decompressing data: incorrect header check
2024-10-05 23:37:04,609 - ERROR - Failed to decompress blobid 14: Error -3 while decompressing data: incorrect header check
2024-10-05 23:37:04,609 - INFO - Skipping blobid 15 as it is not required for dBZ data.
2024-10-05 23:37:04,609 - INFO - Skipping blobid 16 as it is not required for dBZ data.
2024-10-05 23:37:04,609 - ERROR - Zlib decompression error: Error -3 while decompressing data: incorrect header check
2024-10-05 23:37:04,609 - ERROR - Failed to decompress blobid 17: Error -3 while decompressing data: incorrect header check
2024-10-05 23:37:04,613 - INFO - Skipping blobid 18 as it is not required for dBZ data.
2024-10-05 23:37:04,613 - INFO - Skipping blobid 19 as it is not required for dBZ data.
2024-10-05 23:37:04,613 - ERROR - Zlib decompression error: Error -3 while decompressing data: incorrect header check
2024-10-05 23:37:04,614 - ERROR - Failed to decompress blobid 20: Error -3 while decompressing data: incorrect header check
2024-10-05 23:37:04,614 - INFO - Skipping blobid 21 as it is not required for dBZ data.
2024-10-05 23:37:04,615 - INFO - Skipping blobid 22 as it is not required for dBZ data.
2024-10-05 23:37:04,615 - ERROR - Zlib decompression error: Error -3 while decompressing data: incorrect header check
2024-10-05 23:37:04,615 - ERROR - Failed to decompress blobid 23: Error -3 while decompressing data: incorrect header check
2024-10-05 23:37:04,616 - INFO - Extracting dBZ data from blobs_data...
2024-10-05 23:37:04,616 - ERROR - Blob data for blobid 2 not found. Skipping this slice.
2024-10-05 23:37:04,616 - ERROR - Blob data for blobid 5 not found. Skipping this slice.
2024-10-05 23:37:04,616 - ERROR - Blob data for blobid 8 not found. Skipping this slice.
2024-10-05 23:37:04,616 - ERROR - Blob data for blobid 11 not found. Skipping this slice.
2024-10-05 23:37:04,616 - ERROR - Blob data for blobid 14 not found. Skipping this slice.
2024-10-05 23:37:04,616 - ERROR - Blob data for blobid 17 not found. Skipping this slice.
2024-10-05 23:37:04,616 - ERROR - Blob data for blobid 20 not found. Skipping this slice.
2024-10-05 23:37:04,616 - ERROR - Blob data for blobid 23 not found. Skipping this slice.
2024-10-05 23:37:04,616 - ERROR - No dBZ data extracted. Exiting.