diff --git a/2025-06-01.gif b/2025-06-01.gif new file mode 100644 index 0000000..946ecea Binary files /dev/null and b/2025-06-01.gif differ diff --git a/make_gif.py b/make_gif.py index 78eb79f..2a2ee45 100644 --- a/make_gif.py +++ b/make_gif.py @@ -40,16 +40,18 @@ def create_gif(npz_path, output_path=None): # Максимумы по z dbz_max = np.nanmax(data['dbz'], axis=3) # (144, 100, 100) dbzd_max = np.nanmax(data['dbzd'], axis=3) # (144, 100, 100) - doppler_max = np.nanmax(data['doppler'], axis=3) # (144, 100, 100) meteo = data['meteo'] # (144, 100, 100) + + # Срезы по высоте + doppler_5km = data['doppler'][:, :, :, 5] # (144, 100, 100) # Диапазоны для colorbar vmin_dbz = np.nanpercentile(dbz_max, 1) vmax_dbz = np.nanpercentile(dbz_max, 99) vmin_dbzd = np.nanpercentile(dbzd_max, 1) vmax_dbzd = np.nanpercentile(dbzd_max, 99) - vmin_doppler = np.nanpercentile(doppler_max, 1) - vmax_doppler = np.nanpercentile(doppler_max, 99) + vmin_doppler = np.nanpercentile(doppler_5km, 1) + vmax_doppler = np.nanpercentile(doppler_5km, 99) vmin_meteo = np.nanmin(meteo) vmax_meteo = np.nanmax(meteo) @@ -67,9 +69,12 @@ def create_gif(npz_path, output_path=None): axes[0, 1].set_title('max(dBzD)') axes[0, 1].axis('off') plt.colorbar(im2, ax=axes[0, 1], fraction=0.046) + + mod_seismic = plt.cm.seismic.copy() + mod_seismic.set_bad(color='black') - im3 = axes[1, 0].imshow(doppler_max[0], cmap='seismic', vmin=vmin_doppler, vmax=vmax_doppler) - axes[1, 0].set_title('max(Doppler)') + im3 = axes[1, 0].imshow(doppler_5km[0], cmap=mod_seismic, vmin=vmin_doppler, vmax=vmax_doppler) + axes[1, 0].set_title('Doppler [5 km]') axes[1, 0].axis('off') plt.colorbar(im3, ax=axes[1, 0], fraction=0.046) @@ -86,7 +91,7 @@ def create_gif(npz_path, output_path=None): """Обновление кадра""" im1.set_array(dbz_max[frame]) im2.set_array(dbzd_max[frame]) - im3.set_array(doppler_max[frame]) + im3.set_array(doppler_5km[frame]) im4.set_array(meteo[frame]) if data['times'] is not None: @@ -107,7 +112,7 @@ def create_gif(npz_path, output_path=None): print("Создание анимации...") anim = FuncAnimation(fig, update, frames=n_frames, interval=100, blit=True) - print(f"Сохранение GIF: {output_path}") + print(f"Сохранение GIF: {output_path}\n") writer = PillowWriter(fps=10) anim.save(output_path, writer=writer) diff --git a/msg_converter.py b/msg_converter.py index 55e919a..a8607a9 100644 --- a/msg_converter.py +++ b/msg_converter.py @@ -55,8 +55,8 @@ DESCRIPTORS = { "0 31 002": {"bits": 16, "scale": 0, "offset": 0, "type": "UINT","name": "EXTENDED DELAYED DESCRIPT.REPLIC.FACTOR"}, "0 31 012": {"bits": 16, "scale": 0, "offset": 0, "type": "UINT","name": "EXT.DEL.DESCRIPT.AND DATA REPETIT.FACTOR"}, "0 21 001": {"bits": 7, "scale": 0, "offset": -64, "type": "UINT", "name": "HORIZONTAL REFLECTIVITY"}, - "0 21 003": {"bits": 7, "scale": 1, "offset": -5, "type": "UINT", "name": "DIFFERENTIAL REFLECTIVITY"}, - "0 21 014": {"bits": 13, "scale": 1, "offset": -4096, "type": "UINT", "name": "DOPPLER MEAN VELOCITY RADIAL"}, + "0 21 003": {"bits": 7, "scale": -1, "offset": -5, "type": "UINT", "name": "DIFFERENTIAL REFLECTIVITY"}, + "0 21 014": {"bits": 13, "scale": -1, "offset": -4096, "type": "UINT", "name": "DOPPLER MEAN VELOCITY RADIAL"}, "0 21 022": {"bits": 5, "scale": 0, "offset": 0, "type": "UINT", "name": "METEO [SFG NOTE: UNKNOWN]"}, } @@ -64,6 +64,8 @@ SEQUENCES = { "3 01 024": ["0 05 002", "0 06 002", "0 07 001"], # LAT/LON/HEIGHT } +DEBUG = False + # ============================================================================ # BUFR Reader @@ -137,14 +139,29 @@ class BitReader: bits, scale, offset = desc["bits"], desc["scale"], desc["offset"] dtype = desc["type"] + val = -9999999 + raw_val = -9999999 if dtype == "IA5": val = self.read_ia5(bits) elif dtype == "INT": - val = (self.read_int(bits) + offset) * (10 ** scale) + raw_val = self.read_int(bits) elif dtype == "UINT": - val = (self.read_uint(bits) + offset) * (10 ** scale) - else: - val = None + raw_val = self.read_uint(bits) + + + if dtype != "IA5": + val = (raw_val + offset) * (10 ** scale) + + if "DIFFERENTIAL" in desc["name"]: + val = val - int(val > 6) * 12 + + # hot fix from FK code + if "DOPPLER" in desc["name"]: + if val < -50: + val = np.nan + + if DEBUG: + print(f"desc_code: {desc_code}, val: {val}") return val @@ -281,13 +298,14 @@ def parse_bufr_block(bufr_data, data_type='dBz'): grid = np.full(10000, np.nan, dtype=np.float32) position = 0 n_pixels_with_data = reader.read_descriptor("0 31 002") - + + for _ in range(n_pixels_with_data): n_skip = reader.read_descriptor("0 31 012") marker = reader.read_descriptor(descriptor_code) # Заполняем пропущенные пиксели - if n_skip > 0 and position + n_skip <= 10000: + if n_skip > 0: # and position + n_skip <= 10000: grid[position:position + n_skip] = marker position += n_skip @@ -306,7 +324,8 @@ def parse_bufr_block(bufr_data, data_type='dBz'): # поэтому здесь, в отличие от старого кода для DAT-файлов, нет необходимости применять их еще раз. return grid - except Exception: + except Exception as e: + print(f"Error parsing BUFR block: {e}") return None @@ -542,7 +561,7 @@ def load_npz_data(npz_path): 'dbz': data['dbz'], 'dbzd': data['dbzd'], 'doppler': data['doppler'], - 'meteo': data['meteo'], + 'meteo': data['meteo'] if 'meteo' in data else np.zeros((144, 100, 100)), 'times': data['times'] if 'times' in data else None }