From abdce3cd3b7b285c188194376996610fa6a6fa1d Mon Sep 17 00:00:00 2001 From: "2569718930@qq.com" <2569718930@qq.com> Date: Mon, 18 May 2026 17:48:56 +0800 Subject: [PATCH] =?UTF-8?q?=E9=80=82=E9=85=8D=20NOAA=20MADIS=20HFMETAR=20n?= =?UTF-8?q?etCDF=20=E6=96=B0=E6=A0=BC=E5=BC=8F=EF=BC=9AstationId=20?= =?UTF-8?q?=E6=9B=BF=E4=BB=A3=20icaoId=EF=BC=8C=E5=BC=80=E5=B0=94=E6=96=87?= =?UTF-8?q?=E8=BD=AC=E6=91=84=E6=B0=8F=E5=BA=A6=EF=BC=8C=E6=B0=94=E5=8E=8B?= =?UTF-8?q?=20Pa=20=E8=BD=AC=20hPa?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/data_collection/madis_sources.py | 76 +++++++++++++++++----------- 1 file changed, 47 insertions(+), 29 deletions(-) diff --git a/src/data_collection/madis_sources.py b/src/data_collection/madis_sources.py index 8c4d3182..d19b5459 100644 --- a/src/data_collection/madis_sources.py +++ b/src/data_collection/madis_sources.py @@ -57,7 +57,11 @@ class MadisSourceMixin: def _madis_parse_hfmetar( self, nc_bytes: bytes, fname: str ) -> List[Dict[str, Any]]: - """Parse a MADIS HFMETAR NetCDF file and return per-station observations.""" + """Parse a MADIS HFMETAR NetCDF file and return per-station observations. + + NOAA restructured the netCDF layout (2026-05): stationId replaces icaoId, + temperatures are in Kelvin, altimeter in Pascal. + """ try: from netCDF4 import Dataset nc = Dataset(fname, memory=nc_bytes) @@ -70,71 +74,85 @@ class MadisSourceMixin: results: List[Dict[str, Any]] = [] try: - # MADIS HFMETAR uses these variable names - icaos = [str(s).strip() for s in nc.variables.get("icaoId", [])[:]] - temps = nc.variables.get("temperature", None) # in Celsius - dewpts = nc.variables.get("dewpoint", None) - winds = nc.variables.get("windSpeed", None) - pressures = nc.variables.get("seaLevelPress", None) - obs_times = nc.variables.get("observationTime", None) + station_ids = nc.variables.get("stationId") + temps = nc.variables.get("temperature") # Kelvin + dewpts = nc.variables.get("dewpoint") # Kelvin + winds = nc.variables.get("windSpeed") # m/s + pressures = nc.variables.get("altimeter") # Pa + obs_times = nc.variables.get("observationTime") # epoch seconds - n = len(icaos) + if station_ids is None: + logger.warning("MADIS: stationId variable not found in netCDF") + return [] + + n = station_ids.shape[0] for i in range(n): - icao = str(icaos[i]).strip().upper() - if not icao or icao == "0": + # Decode stationId (char array per row) + try: + import numpy as np + row = station_ids[i] + if isinstance(row, np.ndarray): + sid_bytes = b"".join(row.tobytes().split(b"\x00")[:1]) + icao = sid_bytes.decode("ascii", errors="replace").strip() + else: + icao = str(row).strip() + except Exception: + icao = "" + + icao = icao.upper() + if not icao or len(icao) != 4: continue + # Temperature (Kelvin → Celsius) temp_c = None if temps is not None: try: v = float(temps[i]) - if -90 < v < 60: - temp_c = round(v, 1) + if 180 < v < 340: # valid Kelvin range (-93C to +67C) + temp_c = round(v - 273.15, 1) except (ValueError, IndexError): pass if temp_c is None: - continue # skip stations without temperature + continue + # Dewpoint (Kelvin → Celsius) dewp_c = None if dewpts is not None: try: v = float(dewpts[i]) - if -90 < v < 60: - dewp_c = round(v, 1) + if 180 < v < 340: + dewp_c = round(v - 273.15, 1) except (ValueError, IndexError): pass + # Wind (m/s → kt) wind_kt = None if winds is not None: try: w = float(winds[i]) - if w >= 0: + if 0 <= w < 200: wind_kt = round(w * 1.94384, 1) except (ValueError, IndexError): pass + # Pressure (Pa → hPa) pressure_hpa = None if pressures is not None: try: p = float(pressures[i]) - if 800 < p < 1100: - pressure_hpa = round(p, 1) + if 50000 < p < 120000: # valid Pa range + pressure_hpa = round(p / 100.0, 1) except (ValueError, IndexError): pass + # Observation time (epoch seconds) obs_time = "" if obs_times is not None: try: - ts = str(obs_times[i]).strip() - # Try to parse as epoch or ISO - if ts and ts != "0": - try: - epoch = int(float(ts)) - dt = datetime.fromtimestamp(epoch, tz=timezone.utc) - obs_time = dt.isoformat() - except (ValueError, OverflowError): - obs_time = ts - except (ValueError, IndexError): + ts = obs_times[i] + dt = datetime.fromtimestamp(float(ts), tz=timezone.utc) + obs_time = dt.isoformat() + except Exception: pass results.append({