Download tests/dl_wfr.py from logos42/headless: direct link, hf CLI and curl.
- Browser
- Download file 5.43 kB
-
https://huggingface.co/logos42/headless/resolve/main/tests/dl_wfr.py
- Command line
-
hf download hf://logos42/headless/tests/dl_wfr.py
-
curl -L -o dl_wfr.py https://huggingface.co/logos42/headless/resolve/main/tests/dl_wfr.py
5.43 kB
| #!/usr/bin/env python3 | |
| """LM4 特征升级 — 下载 RBSP-A WFR 波功率谱 (EMFISIS-L2, MERGED 版, 本机并行) | |
| 为什么是这个数据集: | |
| - `HFR-SPECTRA` / `WNA-SURVEY` 都是**稀疏/突发模式**, 大量时段 "no data"(实测 2015 全年多数日期无数据) | |
| - `WFR-SPECTRAL-MATRIX-DIAGONAL-MERGED` 是 **survey+burst 合并版, 全年连续**(6s 采样, 与 DENSITY L4 同频) | |
| 内容: 6 个对角功率谱, 各 65 个频段 (~2 Hz ~ 10 kHz) | |
| BuBu / BvBv / BwBw — 磁功率谱密度 [nT^2/Hz] | |
| EuEu / EvEv / EwEw — 电功率谱密度 [(V/m)^2/Hz] | |
| (这是真的"电磁波谱": 哨声/合声/嘶声/磁声波等活动随等离子体状态变化, | |
| 且与 density **无解析关系** —— 不像 fpe/fuh 那样是标签泄漏) | |
| CSV 列布局 (395 列): | |
| [0]=Time [1:66]=BuBu [66:131]=BvBv [131:196]=BwBw | |
| [196:261]=EuEu [261:326]=EvEv [326:391]=EwEw | |
| [391]=LWEzGainW [392]=LWExEyGainUV [393]=SCMGain [394]=MET | |
| 用法: python3 tests/dl_wfr.py --start 2015-01 --months 1 --workers 4 | |
| 输出: data/wave/wfr_YYYYMM.npz (time[], psd[N,6,65] float16 log10, gains) | |
| """ | |
| import argparse, io, json, sys, time, urllib.request, calendar | |
| from concurrent.futures import ThreadPoolExecutor, as_completed | |
| from pathlib import Path | |
| import numpy as np | |
| BASE = "https://cdaweb.gsfc.nasa.gov/hapi" | |
| DS = "RBSP-A_WFR-SPECTRAL-MATRIX-DIAGONAL-MERGED_EMFISIS-L2" | |
| NB = 65 # 频段数 | |
| NPARAM = 6 # BuBu BvBv BwBw EuEu EvEv EwEw | |
| FILL = -1.0e31 # HAPI fill | |
| OUT_DIR = Path(__file__).resolve().parents[1] / "data" / "wave" | |
| def fetch_day(day: str, retries: int = 3): | |
| """下载一整天 (UTC), 返回 (times, psd[N,6,65] float32 log10, gains[N,3]) 或 None""" | |
| t0 = f"{day}T00:00:00Z" | |
| t1 = f"{day}T23:59:59Z" | |
| url = f"{BASE}/data?id={DS}&time.min={t0}&time.max={t1}&format=csv" | |
| last = None | |
| for attempt in range(retries): | |
| try: | |
| raw = urllib.request.urlopen(url, timeout=600).read().decode("utf-8", "replace") | |
| break | |
| except Exception as e: | |
| last = e | |
| time.sleep(3 * (attempt + 1)) | |
| else: | |
| return day, None, f"下载失败: {last}" | |
| if raw.lstrip().startswith("{"): | |
| return day, None, "no data" | |
| times, rows = [], [] | |
| for line in raw.splitlines(): | |
| line = line.strip() | |
| if not line or line.startswith("#"): | |
| continue | |
| f = line.split(",") | |
| if len(f) != 1 + NPARAM * NB + 3 + 1: | |
| continue | |
| try: | |
| vals = np.asarray(f[1:1 + NPARAM * NB], dtype=np.float64).reshape(NPARAM, NB) | |
| gains = np.asarray(f[1 + NPARAM * NB:1 + NPARAM * NB + 3], dtype=np.float64) | |
| except ValueError: | |
| continue | |
| times.append(f[0]) | |
| rows.append(np.concatenate([vals.ravel(), gains])) | |
| if not rows: | |
| return day, None, "解析后无有效行" | |
| arr = np.asarray(rows, dtype=np.float64) # (N, 390+3) | |
| psd = arr[:, :NPARAM * NB].reshape(-1, NPARAM, NB) | |
| gains = arr[:, NPARAM * NB:] | |
| # 填值/非正 → NaN, 再取 log10 | |
| with np.errstate(invalid="ignore", divide="ignore"): | |
| psd = np.where((psd > 0) & (psd < 1e30), psd, np.nan) | |
| psd = np.log10(psd) | |
| return day, (times, psd.astype(np.float16), gains.astype(np.float32)), "ok" | |
| def main(): | |
| ap = argparse.ArgumentParser() | |
| ap.add_argument("--start", default="2015-01", help="起始月 YYYY-MM") | |
| ap.add_argument("--months", type=int, default=1) | |
| ap.add_argument("--workers", type=int, default=4) | |
| ap.add_argument("--out", default=str(OUT_DIR)) | |
| args = ap.parse_args() | |
| out_dir = Path(args.out) | |
| out_dir.mkdir(parents=True, exist_ok=True) | |
| y0, m0 = (int(x) for x in args.start.split("-")) | |
| for mi in range(args.months): | |
| y = y0 + (m0 - 1 + mi) // 12 | |
| m = (m0 - 1 + mi) % 12 + 1 | |
| ndays = calendar.monthrange(y, m)[1] | |
| days = [f"{y:04d}-{m:02d}-{d:02d}" for d in range(1, ndays + 1)] | |
| tag = f"{y:04d}{m:02d}" | |
| print(f"=== WFR {y}-{m:02d} ({ndays}d, {args.workers}w) ===", flush=True) | |
| t_start = time.time() | |
| results = {} | |
| n_ok = 0 | |
| with ThreadPoolExecutor(max_workers=args.workers) as ex: | |
| futs = {ex.submit(fetch_day, d): d for d in days} | |
| for i, fut in enumerate(as_completed(futs), 1): | |
| day, payload, status = fut.result() | |
| if payload is not None: | |
| results[day] = payload | |
| n_ok += 1 | |
| if i % 5 == 0 or i == len(days): | |
| print(f" {i}/{len(days)}d, ok={n_ok}, {time.time()-t_start:.0f}s", flush=True) | |
| if not results: | |
| print(f" !! {tag} 无数据", flush=True) | |
| continue | |
| all_t, all_p, all_g = [], [], [] | |
| for day in sorted(results): | |
| t, p, g = results[day] | |
| all_t.extend(t); all_p.append(p); all_g.append(g) | |
| psd = np.concatenate(all_p, axis=0) | |
| gains = np.concatenate(all_g, axis=0) | |
| fn = out_dir / f"wfr_{tag}.npz" | |
| np.savez_compressed(fn, time=np.array(all_t, dtype=object), | |
| psd=psd, gains=gains, n_bands=NB) | |
| mb = fn.stat().st_size / 1024 / 1024 | |
| print(f"[save] {fn.name} ({mb:.1f} MB, {len(all_t)} recs, {time.time()-t_start:.0f}s)", flush=True) | |
| print("done", flush=True) | |
| if __name__ == "__main__": | |
| main() | |