1+ #!/usr/bin/env python
2+
3+ from datetime import datetime , timedelta , UTC
4+ from obspy import read
5+ import argparse
6+ import glob
7+ import math
8+ import matplotlib
9+ import matplotlib .pyplot as plt
10+ import numpy as np
11+ import os
12+ import sys
13+
14+ # Argument parsing
15+ parser = argparse .ArgumentParser (
16+ description = "Plot helicorder for given date."
17+ )
18+ parser .add_argument (
19+ "--date" ,
20+ type = str ,
21+ help = "Target date in YYYYMMDD format (default: yesterday in UTC)" ,
22+ )
23+ args = parser .parse_args ()
24+
25+ # Target date setup
26+ if args .date :
27+ try :
28+ target_dt = datetime .strptime (args .date , "%Y%m%d" ).replace (tzinfo = UTC )
29+ except ValueError :
30+ sys .exit ("Invalid date format. Use YYYYMMDD (e.g., 20250101)." )
31+ else :
32+ target_dt = datetime .now (UTC ) - timedelta (days = 1 )
33+
34+ year = target_dt .strftime ("%Y" )
35+ datestr = target_dt .strftime ("%Y%m%d" )
36+ julday = target_dt .strftime ("%j" )
37+ print (f"Processing date: { datestr } (Julian day { julday } )" )
38+
39+ # Station-specific scale configuration
40+ station_scale = {
41+ # --- Network ---
42+ # Format: StationName : ScaleValue
43+ # Example: JN01: 2000, JN02: 30000
44+ # ScaleValue must be an integer
45+ }
46+ threshold_raw = 1.5
47+
48+ # Load files
49+ pattern = f"/path_to_mseed_data/station.network.location.channel.{ year } .{ julday } "
50+ files = sorted (glob .glob (pattern ))
51+ print (f"Found { len (files )} files for { year } .{ julday } " )
52+
53+ if not files :
54+ sys .exit (f"No file matching pattern: { pattern } " )
55+
56+ try :
57+ st = read (files [0 ])
58+ for f in files [1 :]:
59+ st += read (f )
60+ st .merge (fill_value = "interpolate" )
61+ st .detrend ("demean" )
62+ st .taper (0.0005 , "cosine" )
63+ st .filter ("bandpass" , freqmin = 1 , freqmax = 20 )
64+ except Exception as e :
65+ sys .exit (f"Error reading waveform files: { e } " )
66+
67+ # Output directory
68+ outdir = "/path_to_output_directory"
69+ os .makedirs (outdir , exist_ok = True )
70+
71+ # Main plotting loop
72+ for tr in st :
73+ try :
74+ station = tr .stats .station
75+ channel = tr .stats .channel [- 1 ]
76+ data = tr .data
77+ delta = tr .stats .delta
78+ npts = tr .stats .npts
79+ times = np .arange (0 , npts * delta , delta )
80+ starttime = tr .stats .starttime .datetime
81+
82+ interval = 60 * 15
83+ segments = [int (i * interval / delta ) for i in range (math .ceil (times [- 1 ] / interval )
84+ + 1 )]
85+
86+ fig , ax = plt .subplots (figsize = (10 , 8 ))
87+ scale = station_scale .get (station , 5000 )
88+
89+ for i in range (len (segments ) - 1 ):
90+ seg_data = data [segments [i ]:segments [i + 1 ]]
91+ seg_time = np .linspace (0 , interval , len (seg_data ))
92+ color = "red" if np .any (np .abs (seg_data / scale ) > threshold_raw ) else "black"
93+ ax .plot (seg_time , seg_data / scale + i , color = color , linewidth = 0.5 )
94+
95+ # Axis configuration
96+ ax .set_ylim (len (segments ) - 1 , - 0.5 )
97+ ax .set_xlim (0 , interval )
98+ ax .set_xticks (np .linspace (0 , interval , 16 ))
99+ ax .set_xticklabels ([str (int (x / 60 )) for x in np .linspace (0 , 15 * 60 , 16 )])
100+ ax .set_yticks (np .arange (0 , len (segments ) - 1 , 4 ))
101+ yt_labels = [(starttime + timedelta (minutes = 15 * i )).strftime ("%H" )
102+ for i in range (0 , len (segments ) - 1 , 4 )]
103+ ax .set_yticklabels (yt_labels )
104+ ax .set_xlabel ("Time (minutes)" )
105+ ax .set_title (f"{ tr .id } { str (starttime )[:10 ]} " )
106+ ax .set_facecolor ("white" )
107+ plt .tight_layout ()
108+
109+ outfile = f"{ outdir } /helicorder_{ station } _{ datestr } 0000_{ channel } .png"
110+ os .makedirs (os .path .dirname (outfile ), exist_ok = True )
111+ print (f"Saving { outfile } " )
112+ plt .savefig (outfile , dpi = 150 )
113+ plt .close (fig )
114+
115+ except Exception as e :
116+ print (f"Error plotting { tr .id } : { e } " )
117+ continue
118+
119+ print ("All helicorders saved successfully!" )
0 commit comments