///|
/// Entry point for the CLI or runnable program in this template.
fn main {
println("==================================================")
println(" MoonBit Seismic Wave Signal Processing Demo ")
println("==================================================")
// 1. Create a synthetic seismic trace with a transient wave
let n = 200
let sampling_rate = 50.0
let data = Array::new(capacity=n)
// Background noise + event starting at sample 100
for i = 0; i < n; i = i + 1 {
let t = i.to_double() / sampling_rate
let noise = 0.05 * @math.sin(2.0 * 3.141592653589793 * 15.0 * t) // high frequency noise
let event = if i >= 100 {
1.0 * @math.sin((i - 100).to_double() * 0.1)
} else {
0.0
}
data.push(noise + event)
}
let trace = @seismic.Trace::new("ST01", "BHZ", sampling_rate, 0.0, data)
println("1. Created synthetic Trace:")
println(" Station: " + trace.station + ", Channel: " + trace.channel)
println(
" Samples: " +
trace.npts().to_string() +
", Sampling Rate: " +
trace.sampling_rate.to_string() +
" Hz",
)
println(
" Mean: " +
trace.mean().to_string() +
", Std Dev: " +
trace.std().to_string(),
)
// 2. Filter the trace to remove noise
let filtered = trace.clone()
@seismic.lowpass(filtered, 5.0, true)
println("\n2. Applied 2nd-order Lowpass Filter (Cutoff: 5.0 Hz, Zero-phase):")
println(" Original Std Dev: " + trace.std().to_string())
println(" Filtered Std Dev: " + filtered.std().to_string())
// 3. STA/LTA pick arrival
let ratio = @seismic.classic_sta_lta(filtered.data, 5, 20)
let triggers = @seismic.trigger_onset(
ratio,
2.0,
1.2,
filtered.delta(),
filtered.start_time,
)
println("\n3. Ran STA/LTA Arrival Picker:")
if triggers.length() > 0 {
// Access fields from Trigger struct via getters or directly
// since fields are public by default in structural matches or package visibility
// Let's print trigger details using a loop
for t in triggers {
// Print first trigger info
println(" Detected Event Arrival!")
println(" Onset Time: " + t.on_time.to_string() + " s")
println(" Offset Time: " + t.off_time.to_string() + " s")
break
}
} else {
println(" No trigger found.")
}
// 4. Magnitude Estimation
// Suppose peak amplitude measured is 5.5 mm on Wood-Anderson seismograph at 120 km epicentral distance
let ml = @seismic.local_magnitude(5.5, 120.0)
println("\n4. Earthquake Magnitude Estimation:")
match ml {
Ok(val) => println(" Richter Local Magnitude (M_L): " + val.to_string())
Err(err) => println(" Error: " + err)
}
// 5. Travel-time curves for IASPEI91 velocity model
let model = @seismic.iaspei91()
let tp = @seismic.travel_time(model, 15.0, 80.0, false)
let ts = @seismic.travel_time(model, 15.0, 80.0, true)
println(
"\n5. 1D Travel-Time Calculations (IASPEI91 model, source depth 15km, distance 80km):",
)
println(" P-wave travel time: " + tp.to_string() + " s")
println(" S-wave travel time: " + ts.to_string() + " s")
// 6. Velocity Model Interpolation
let depths = [0.0, 10.0, 20.0, 35.0]
let velocities = [5.5, 6.0, 6.5, 8.0]
let spline_res = @seismic.CubicSpline::new(depths, velocities)
println("\n6. Velocity Model Interpolation:")
match spline_res {
Ok(spline) => {
let v_at_15 = spline.evaluate(15.0)
println(
" Cubic Spline interpolated velocity at 15.0 km depth: " +
v_at_15.to_string() +
" km/s",
)
}
Err(e) => println(" Spline Error: " + e)
}
// 7. SAC Serialization
let sac_bytes = @seismic.to_sac_bytes(trace, false)
println("\n7. SAC File Serialization:")
println(
" Successfully serialized trace to SAC binary format (" +
sac_bytes.length().to_string() +
" bytes).",
)
println("==================================================")
}