← スキル一覧に戻る
signal-analysis
vamseeachanta / digitalmodel
⭐ 4🍴 0📅 2026年1月19日
Perform signal processing, rainflow cycle counting, and spectral analysis
SKILL.md
---
name: signal-analysis
description: Perform signal processing, rainflow cycle counting, and spectral analysis
for fatigue and time series data. Use for analyzing stress time histories, computing
FFT/PSD, extracting fatigue cycles (ASTM E1049-85), and batch processing OrcaFlex
signals.
updated: '2026-01-07'
---
# Signal Analysis Skill
Perform signal processing, rainflow cycle counting, and spectral analysis for fatigue assessment and time series characterization.
## Version Metadata
```yaml
version: 1.0.0
python_min_version: '3.10'
compatibility:
tested_python:
- '3.10'
- '3.11'
- '3.12'
- '3.13'
os:
- Windows
- Linux
- macOS
```
## Changelog
### [1.0.0] - 2026-01-07
**Added:**
- Initial version metadata and dependency management
- Semantic versioning support
- Compatibility information for Python 3.10-3.13
**Changed:**
- Enhanced skill documentation structure
## When to Use
- Analyzing fatigue from stress/load time series
- Computing rainflow cycles for damage calculation
- FFT and power spectral density analysis
- Frequency spectrum characterization
- Batch processing OrcaFlex simulation signals
- Time series conditioning and filtering
- Converting time-domain data to frequency-domain
## Prerequisites
- Python environment with `digitalmodel` package installed
- Time series data in CSV, Excel, or OrcaFlex format
- For OrcaFlex signals: completed .sim files
## Signal Processing Types
### 1. Rainflow Cycle Counting (ASTM E1049-85)
Extract stress/load cycles for fatigue analysis using industry-standard rainflow algorithm.
```yaml
signal_analysis:
rainflow:
flag: true
input_file: "data/stress_time_history.csv"
time_column: "time"
signal_column: "stress"
output:
cycles_file: "results/rainflow_cycles.csv"
histogram_file: "results/cycle_histogram.html"
summary_file: "results/rainflow_summary.json"
options:
hysteresis_filter: 0.0 # Filter small cycles (fraction of range)
residual_method: "none" # none, half_cycles, or pairs
```
### 2. FFT Spectral Analysis
Compute frequency content using Fast Fourier Transform.
```yaml
signal_analysis:
fft:
flag: true
input_file: "data/motion_time_history.csv"
time_column: "time"
signal_column: "heave"
output:
spectrum_file: "results/fft_spectrum.csv"
plot_file: "results/fft_spectrum.html"
options:
window: "hanning" # hanning, hamming, blackman, rectangular
normalize: true
one_sided: true # Show positive frequencies only
```
### 3. Power Spectral Density (Welch Method)
Estimate power spectral density with reduced variance using overlapping segments.
```yaml
signal_analysis:
psd:
flag: true
input_file: "data/vessel_motion.csv"
time_column: "time"
signal_columns:
- "surge"
- "heave"
- "pitch"
output:
psd_file: "results/psd_analysis.csv"
plot_file: "results/psd_plot.html"
options:
method: "welch"
segment_length: 1024
overlap: 0.5
window: "hanning"
detrend: "linear"
```
### 4. Time Series Conditioning
Prepare raw time series for analysis with filtering and preprocessing.
```yaml
signal_analysis:
conditioning:
flag: true
input_file: "data/raw_signal.csv"
output_file: "data/conditioned_signal.csv"
operations:
- type: "resample"
target_dt: 0.1 # seconds
method: "linear"
- type: "detrend"
method: "linear"
- type: "filter"
filter_type: "lowpass"
cutoff_frequency: 0.5 # Hz
order: 4
- type: "remove_offset"
method: "mean" # mean or first_value
```
### 5. OrcaFlex Signal Batch Processing
Process multiple OrcaFlex time histories in parallel.
```yaml
signal_analysis:
orcaflex_batch:
flag: true
sim_directory: "results/.sim/"
sim_pattern: "*.sim"
variables:
- object: "Line1"
variable_name: "Effective Tension"
arc_length: 0
- object: "Vessel1"
variable_name: "Heave"
processing:
rainflow: true
psd: true
statistics: true
output_directory: "results/signal_analysis/"
parallel: true
max_workers: 4
```
## Python API
### Rainflow Cycle Counting
```python
from digitalmodel.modules.signal_analysis.rainflow import RainflowCounter
# Initialize counter
counter = RainflowCounter()
# Load time history
import pandas as pd
data = pd.read_csv("stress_time_history.csv")
time = data["time"].values
stress = data["stress"].values
# Extract cycles
cycles = counter.count_cycles(stress)
# Get cycle matrix (range, mean, count)
cycle_matrix = counter.get_cycle_matrix(cycles)
# Calculate damage using S-N curve
from digitalmodel.modules.fatigue_analysis import SNCurve
sn_curve = SNCurve.from_code("DNV-RP-C203", "D")
damage = counter.calculate_damage(cycles, sn_curve)
print(f"Total cycles: {len(cycles)}")
print(f"Fatigue damage: {damage:.6f}")
```
### Spectral Analysis
```python
from digitalmodel.modules.signal_analysis.spectral import SpectralAnalyzer
import numpy as np
# Initialize analyzer
analyzer = SpectralAnalyzer()
# Load signal
data = pd.read_csv("motion_time_history.csv")
time = data["time"].values
signal = data["heave"].values
dt = time[1] - time[0]
# Compute FFT
frequencies, amplitudes = analyzer.compute_fft(
signal,
dt=dt,
window="hanning",
normalize=True
)
# Compute PSD (Welch method)
freq_psd, psd = analyzer.compute_psd(
signal,
dt=dt,
method="welch",
nperseg=1024,
overlap=0.5
)
# Find dominant frequency
dominant_freq = analyzer.find_peak_frequency(freq_psd, psd)
print(f"Dominant frequency: {dominant_freq:.3f} Hz")
# Compute spectral moments
m0, m2, m4 = analyzer.compute_spectral_moments(freq_psd, psd)
Tz = np.sqrt(m0/m2) # Zero-crossing period
print(f"Zero-crossing period: {Tz:.2f} s")
```
### Time Series Processing
```python
from digitalmodel.modules.signal_analysis.time_series import TimeSeriesProcessor
# Initialize processor
processor = TimeSeriesProcessor()
# Load raw data
data = pd.read_csv("raw_signal.csv")
time = data["time"].values
signal = data["stress"].values
# Resample to uniform time step
time_resampled, signal_resampled = processor.resample(
time, signal,
target_dt=0.1,
method="linear"
)
# Detrend
signal_detrended = processor.detrend(signal_resampled, method="linear")
# Apply low-pass filter
signal_filtered = processor.lowpass_filter(
signal_detrended,
dt=0.1,
cutoff_freq=0.5,
order=4
)
# Remove mean offset
signal_final = processor.remove_offset(signal_filtered, method="mean")
# Save processed signal
processed_df = pd.DataFrame({
"time": time_resampled,
"stress": signal_final
})
processed_df.to_csv("processed_signal.csv", index=False)
```
### OrcaFlex Signal Extraction
```python
from digitalmodel.modules.signal_analysis.orcaflex_signals import OrcaFlexSignalExtractor
from pathlib import Path
# Initialize extractor
extractor = OrcaFlexSignalExtractor()
# Extract time history from single .sim file
sim_file = Path("simulation.sim")
time, tension = extractor.extract_time_history(
sim_file,
object_name="Line1",
variable_name="Effective Tension",
arc_length=0
)
# Batch extraction from multiple .sim files
sim_files = list(Path("results/.sim/").glob("*.sim"))
results = extractor.batch_extract(
sim_files,
variables=[
{"object": "Line1", "variable_name": "Effective Tension"},
{"object": "Vessel1", "variable_name": "Heave"}
],
parallel=True,
max_workers=4
)
# Process extracted signals
for sim_name, sim_data in results.items():
for var_name, (time, signal) in sim_data.items():
print(f"{sim_name} - {var_name}: {len(signal)} samples")
```
### Generic Time Series Reader
```python
from digitalmodel.modules.signal_analysis.readers import GenericTimeSeriesReader
# Auto-detect file format and load
reader = GenericTimeSeriesReader()
# Read CSV
data = reader.read("data/measurements.csv")
# Read Excel
data = reader.read("data/measurements.xlsx", sheet_name="Sheet1")
# Read OrcaFlex .sim
data = reader.read("simulation.sim", object="Line1", variable="Tension")
# Read with column mapping
data = reader.read(
"data/custom_format.csv",
time_column="timestamp_sec",
signal_columns=["stress_mpa", "strain_mm"]
)
```
## Configuration Examples
### Complete Signal Analysis Workflow
```yaml
basename: signal_analysis_workflow
signal_analysis:
# Step 1: Condition raw signals
conditioning:
flag: true
input_file: "data/raw_stress.csv"
output_file: "data/conditioned_stress.csv"
operations:
- type: "resample"
target_dt: 0.1
- type: "detrend"
method: "linear"
- type: "filter"
filter_type: "lowpass"
cutoff_frequency: 1.0
# Step 2: Rainflow counting
rainflow:
flag: true
input_file: "data/conditioned_stress.csv"
time_column: "time"
signal_column: "stress"
output:
cycles_file: "results/cycles.csv"
histogram_file: "results/cycle_histogram.html"
# Step 3: Spectral analysis
psd:
flag: true
input_file: "data/conditioned_stress.csv"
time_column: "time"
signal_columns: ["stress"]
output:
psd_file: "results/stress_psd.csv"
plot_file: "results/stress_psd.html"
```
### Fatigue-Focused Analysis
```yaml
signal_analysis:
rainflow:
flag: true
input_file: "data/stress_history.csv"
output:
cycles_file: "results/fatigue_cycles.csv"
summary_file: "results/fatigue_summary.json"
options:
hysteresis_filter: 0.01 # Filter cycles < 1% of range
fatigue_damage:
flag: true
cycles_file: "results/fatigue_cycles.csv"
sn_curve:
code: "DNV-RP-C203"
curve_type: "D"
environment: "seawater_cathodic"
output:
damage_file: "results/fatigue_damage.json"
report_file: "results/fatigue_report.html"
```
## Output Formats
### Rainflow Cycles CSV
```csv
range,mean,count,from_stress,to_stress
245.3,125.6,1.0,3.0,248.3
198.7,156.2,1.0,56.8,255.6
167.4,89.3,0.5,5.6,173.0
```
### PSD Output CSV
```csv
frequency_hz,psd_stress,psd_heave,psd_pitch
0.001,1.23e+06,0.0045,0.00012
0.002,2.45e+06,0.0089,0.00024
0.005,5.67e+06,0.0234,0.00056
```
### Summary Statistics JSON
```json
{
"signal_name": "stress",
"sample_count": 36000,
"duration_sec": 3600,
"statistics": {
"min": -125.4,
"max": 456.7,
"mean": 165.3,
"std": 89.2,
"rms": 188.5
},
"spectral": {
"dominant_frequency_hz": 0.125,
"bandwidth_parameter": 0.342,
"spectral_moments": {
"m0": 7956.4,
"m2": 123.5,
"m4": 4.56
}
},
"rainflow": {
"total_cycles": 1234,
"max_range": 582.1,
"mean_range": 156.7
}
}
```
## Best Practices
### Signal Quality
1. **Check sampling rate** - Ensure Nyquist criterion for frequencies of interest
2. **Remove transients** - Skip build-up periods in OrcaFlex simulations
3. **Validate stationarity** - Use multiple segments for PSD estimation
4. **Handle gaps** - Interpolate or segment around missing data
### Rainflow Analysis
1. **Hysteresis filtering** - Remove small cycles (typically < 1-5% of range)
2. **Residual handling** - Choose appropriate method for incomplete cycles
3. **Cycle binning** - Use consistent bin sizes for histogram comparison
4. **S-N curve matching** - Ensure stress units match S-N curve units
### Performance Optimization
1. **Parallel processing** - Use batch extraction for multiple files
2. **Memory management** - Process long signals in chunks
3. **Downsampling** - Reduce sampling rate if high frequencies not needed
4. **Result caching** - Store intermediate results for reprocessing
## Error Handling
### Common Issues
1. **Non-uniform time step**
```python
# Check and resample if needed
dt_values = np.diff(time)
if np.std(dt_values) > 0.001 * np.mean(dt_values):
time, signal = processor.resample(time, signal, target_dt=np.mean(dt_values))
```
2. **NaN values in signal**
```python
# Interpolate or remove
if np.any(np.isnan(signal)):
signal = processor.interpolate_nans(time, signal)
```
3. **Insufficient samples for PSD**
```python
# Reduce segment length
if len(signal) < nperseg * 2:
nperseg = len(signal) // 4
```
## Related Skills
- [fatigue-analysis](../fatigue-analysis/SKILL.md) - Use rainflow cycles for fatigue damage calculation
- [orcaflex-post-processing](../orcaflex-post-processing/SKILL.md) - Extract time histories from OrcaFlex
- [structural-analysis](../structural-analysis/SKILL.md) - Stress analysis for signal generation
## References
- ASTM E1049-85: Standard Practices for Cycle Counting in Fatigue Analysis
- Welch, P.D. (1967): The Use of FFT for Estimation of Power Spectra
- DNV-RP-C203: Fatigue Design of Offshore Steel Structures