ringdownanalysis 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. ringdownanalysis-0.1.0/PKG-INFO +453 -0
  2. ringdownanalysis-0.1.0/README.md +411 -0
  3. ringdownanalysis-0.1.0/pyproject.toml +174 -0
  4. ringdownanalysis-0.1.0/ringdownanalysis/__init__.py +105 -0
  5. ringdownanalysis-0.1.0/ringdownanalysis/analyzer.py +581 -0
  6. ringdownanalysis-0.1.0/ringdownanalysis/batch_analyzer.py +737 -0
  7. ringdownanalysis-0.1.0/ringdownanalysis/compat.py +140 -0
  8. ringdownanalysis-0.1.0/ringdownanalysis/crlb.py +256 -0
  9. ringdownanalysis-0.1.0/ringdownanalysis/data_loader.py +327 -0
  10. ringdownanalysis-0.1.0/ringdownanalysis/estimators.py +782 -0
  11. ringdownanalysis-0.1.0/ringdownanalysis/legacy_ring_down_mc.py +905 -0
  12. ringdownanalysis-0.1.0/ringdownanalysis/monte_carlo.py +582 -0
  13. ringdownanalysis-0.1.0/ringdownanalysis/plots.py +511 -0
  14. ringdownanalysis-0.1.0/ringdownanalysis/signal.py +152 -0
  15. ringdownanalysis-0.1.0/ringdownanalysis.egg-info/PKG-INFO +453 -0
  16. ringdownanalysis-0.1.0/ringdownanalysis.egg-info/SOURCES.txt +26 -0
  17. ringdownanalysis-0.1.0/ringdownanalysis.egg-info/dependency_links.txt +1 -0
  18. ringdownanalysis-0.1.0/ringdownanalysis.egg-info/requires.txt +22 -0
  19. ringdownanalysis-0.1.0/ringdownanalysis.egg-info/top_level.txt +1 -0
  20. ringdownanalysis-0.1.0/setup.cfg +4 -0
  21. ringdownanalysis-0.1.0/tests/test_analyzer.py +259 -0
  22. ringdownanalysis-0.1.0/tests/test_batch_analysis_methods.py +332 -0
  23. ringdownanalysis-0.1.0/tests/test_batch_analyzer.py +488 -0
  24. ringdownanalysis-0.1.0/tests/test_compat.py +74 -0
  25. ringdownanalysis-0.1.0/tests/test_crlb.py +151 -0
  26. ringdownanalysis-0.1.0/tests/test_data_loader.py +257 -0
  27. ringdownanalysis-0.1.0/tests/test_estimators.py +256 -0
  28. ringdownanalysis-0.1.0/tests/test_signal.py +96 -0
@@ -0,0 +1,453 @@
1
+ Metadata-Version: 2.4
2
+ Name: ringdownanalysis
3
+ Version: 0.1.0
4
+ Summary: Frequency estimation of ring-down signals using NLS and DFT methods
5
+ Author-email: Miguel Dovale <mdovale@arizona.edu>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/mdovale/RingDownAnalysis
8
+ Project-URL: Repository, https://github.com/mdovale/RingDownAnalysis
9
+ Project-URL: Issues, https://github.com/mdovale/RingDownAnalysis/issues
10
+ Keywords: signal-processing,frequency-estimation,ring-down,cramér-rao,crlb
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.8
16
+ Classifier: Programming Language :: Python :: 3.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Topic :: Scientific/Engineering
21
+ Requires-Python: >=3.8
22
+ Description-Content-Type: text/markdown
23
+ Requires-Dist: numpy>=1.20.0
24
+ Requires-Dist: scipy>=1.7.0
25
+ Requires-Dist: matplotlib>=3.5.0
26
+ Requires-Dist: tqdm>=4.60.0
27
+ Requires-Dist: joblib>=1.0.0
28
+ Requires-Dist: pandas>=1.3.0
29
+ Provides-Extra: dev
30
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
31
+ Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
32
+ Requires-Dist: pytest-benchmark>=4.0.0; extra == "dev"
33
+ Requires-Dist: ruff>=0.1.0; extra == "dev"
34
+ Requires-Dist: mypy>=1.0.0; extra == "dev"
35
+ Requires-Dist: sphinx>=7.0.0; extra == "dev"
36
+ Requires-Dist: sphinx-rtd-theme>=2.0.0; extra == "dev"
37
+ Provides-Extra: examples
38
+ Requires-Dist: pandas>=1.3.0; extra == "examples"
39
+ Requires-Dist: jupyter>=1.0.0; extra == "examples"
40
+ Provides-Extra: all
41
+ Requires-Dist: ringdownanalysis[dev,examples]; extra == "all"
42
+
43
+ # Frequency and quality factor estimation of exponentially decaying sinusoids
44
+
45
+ This repository contains theoretical analysis, numerical simulations, and experimental data analysis for frequency estimation of ring-down signals. Ring-down signals are exponentially decaying sinusoids that arise from measurements of harmonic oscillators with quality factor Q, where the amplitude decays exponentially due to energy dissipation.
46
+
47
+ ## Quickstart
48
+
49
+ ### Installation
50
+
51
+ Since this package is not yet available on PyPI, install it from source:
52
+
53
+ ```bash
54
+ # Clone the repository
55
+ git clone https://github.com/mdovale/RingDownAnalysis.git
56
+ cd RingDownAnalysis
57
+
58
+ # Install in editable mode
59
+ pip install -e .
60
+ ```
61
+
62
+ For development with testing and linting tools:
63
+
64
+ ```bash
65
+ pip install -e ".[dev]"
66
+ ```
67
+
68
+ For examples and notebooks:
69
+
70
+ ```bash
71
+ pip install -e ".[examples]"
72
+ ```
73
+
74
+ Or install everything:
75
+
76
+ ```bash
77
+ pip install -e ".[all]"
78
+ ```
79
+
80
+ ### Basic Usage
81
+
82
+ #### Generate and Analyze a Ring-Down Signal
83
+
84
+ ```python
85
+ from ringdownanalysis import RingDownSignal, NLSFrequencyEstimator, DFTFrequencyEstimator
86
+ import numpy as np
87
+
88
+ # Generate a ring-down signal
89
+ signal = RingDownSignal(
90
+ f0=5.0, # Frequency (Hz)
91
+ fs=100.0, # Sampling frequency (Hz)
92
+ N=10000, # Number of samples
93
+ A0=1.0, # Initial amplitude
94
+ snr_db=60.0, # Initial SNR (dB)
95
+ Q=10000.0, # Quality factor
96
+ )
97
+
98
+ rng = np.random.default_rng(42)
99
+ t, x, phi0 = signal.generate(rng=rng)
100
+
101
+ # Estimate frequency using NLS method
102
+ nls_estimator = NLSFrequencyEstimator(tau_known=None)
103
+ f_nls = nls_estimator.estimate(x, signal.fs)
104
+
105
+ # Estimate frequency using DFT method
106
+ dft_estimator = DFTFrequencyEstimator(window="rect")
107
+ f_dft = dft_estimator.estimate(x, signal.fs)
108
+
109
+ print(f"True frequency: {signal.f0:.6f} Hz")
110
+ print(f"NLS estimate: {f_nls:.6f} Hz")
111
+ print(f"DFT estimate: {f_dft:.6f} Hz")
112
+
113
+ # Or estimate frequency, tau, and Q together
114
+ result_nls = nls_estimator.estimate_full(x, signal.fs)
115
+ result_dft = dft_estimator.estimate_full(x, signal.fs)
116
+
117
+ print(f"\nNLS full result: f={result_nls.f:.6f} Hz, tau={result_nls.tau:.2f} s, Q={result_nls.Q:.2e}")
118
+ print(f"DFT full result: f={result_dft.f:.6f} Hz, tau={result_dft.tau:.2f} s, Q={result_dft.Q:.2e}")
119
+ ```
120
+
121
+ #### Analyze Experimental Data
122
+
123
+ ```python
124
+ from ringdownanalysis import BatchRingDownAnalyzer
125
+ import pandas as pd
126
+
127
+ # Initialize batch analyzer
128
+ batch_analyzer = BatchRingDownAnalyzer()
129
+
130
+ # Process all files in data directory
131
+ results = batch_analyzer.process_directory("data", verbose=True)
132
+
133
+ # Get summary table
134
+ summary = batch_analyzer.get_summary_table()
135
+ df_summary = pd.DataFrame(summary['data'])
136
+ print(df_summary)
137
+ ```
138
+
139
+ See `examples/usage_example.py` and `examples/batch_analysis_example.py` for more complete examples.
140
+
141
+ #### Configure Logging
142
+
143
+ The package uses `NullHandler` by default (no log output). For easier debugging, enable console logging:
144
+
145
+ ```python
146
+ import logging
147
+ from ringdownanalysis import configure_logging, BatchRingDownAnalyzer
148
+
149
+ # Quick setup: INFO-level console output
150
+ configure_logging(level=logging.INFO)
151
+
152
+ analyzer = BatchRingDownAnalyzer()
153
+ results = analyzer.process_directory("data")
154
+ ```
155
+
156
+ For production (file + console, rotation, structured format):
157
+
158
+ ```python
159
+ from examples.logging_config_example import setup_production_logging
160
+ import logging
161
+
162
+ setup_production_logging(log_dir='logs', log_level=logging.INFO)
163
+ ```
164
+
165
+ See `examples/logging_config_example.py` for more logging configuration options.
166
+
167
+ ## Overview
168
+
169
+ The project compares two complementary approaches for frequency estimation:
170
+
171
+ 1. **Nonlinear Least Squares (NLS)** with explicit ring-down model
172
+ 2. **Frequency-Domain Methods (DFT)** with Lorentzian peak fitting
173
+
174
+ Both methods are evaluated against the Cramér-Rao Lower Bound (CRLB) derived from the explicit Fisher information matrix for ring-down signals.
175
+
176
+ ## Features
177
+
178
+ ### Object-Oriented API
179
+
180
+ The package provides a modern object-oriented API:
181
+
182
+ - **`RingDownSignal`**: Generate synthetic ring-down signals with specified parameters
183
+ - **`FrequencyEstimator`**: Base class for frequency estimation methods
184
+ - **`NLSFrequencyEstimator`**: Nonlinear least squares estimation
185
+ - `estimate()`: Returns frequency only
186
+ - `estimate_full()`: Returns `EstimationResult` with frequency, tau, and Q
187
+ - **`DFTFrequencyEstimator`**: DFT-based estimation with Lorentzian fitting
188
+ - `estimate()`: Returns frequency only
189
+ - `estimate_full()`: Returns `EstimationResult` with frequency, tau (via NLS with fixed frequency), and Q
190
+ - **`EstimationResult`**: Named tuple containing (f, tau, Q) estimates
191
+ - **`CRLBCalculator`**: Calculate Cramér-Rao Lower Bound for frequency estimation
192
+ - **`RingDownAnalyzer`**: Analyze individual ring-down data files
193
+ - **`BatchRingDownAnalyzer`**: Batch process multiple data files
194
+ - **`MonteCarloAnalyzer`**: Run Monte Carlo simulations to compare methods
195
+
196
+ ### Compatibility Layer
197
+
198
+ A function-based compatibility layer is also available for backward compatibility:
199
+
200
+ ```python
201
+ from ringdownanalysis import (
202
+ generate_ringdown,
203
+ estimate_freq_nls_ringdown,
204
+ estimate_freq_dft,
205
+ crlb_var_f_ringdown_explicit,
206
+ monte_carlo_analysis,
207
+ )
208
+ ```
209
+
210
+ ## Usage Examples
211
+
212
+ ### Monte Carlo Analysis
213
+
214
+ Compare estimation methods using Monte Carlo simulations:
215
+
216
+ ```python
217
+ from ringdownanalysis import MonteCarloAnalyzer
218
+
219
+ analyzer = MonteCarloAnalyzer()
220
+
221
+ results = analyzer.run(
222
+ f0=5.0,
223
+ fs=100.0,
224
+ N=1_000_000,
225
+ A0=1.0,
226
+ snr_db=60.0,
227
+ Q=10000.0,
228
+ n_mc=100,
229
+ seed=42,
230
+ )
231
+
232
+ print(f"NLS std: {results['stats']['nls']['std']:.6e} Hz")
233
+ print(f"DFT std: {results['stats']['dft']['std']:.6e} Hz")
234
+ print(f"CRLB std: {results['crlb_std']:.6e} Hz")
235
+ ```
236
+
237
+ ### Calculate CRLB
238
+
239
+ ```python
240
+ from ringdownanalysis import CRLBCalculator
241
+
242
+ crlb_calc = CRLBCalculator()
243
+ crlb_std = crlb_calc.standard_deviation(
244
+ A0=1.0,
245
+ sigma=0.001,
246
+ fs=100.0,
247
+ N=10000,
248
+ tau=636.6,
249
+ )
250
+
251
+ print(f"CRLB standard deviation: {crlb_std:.6e} Hz")
252
+ ```
253
+
254
+ ### Batch Analysis
255
+
256
+ Process multiple experimental data files:
257
+
258
+ ```python
259
+ from ringdownanalysis import BatchRingDownAnalyzer
260
+ import pandas as pd
261
+
262
+ batch_analyzer = BatchRingDownAnalyzer()
263
+
264
+ # Process all files in data directory
265
+ results = batch_analyzer.process_directory("data", verbose=True, n_jobs=-1)
266
+
267
+ # Q factors are automatically calculated during analysis (via estimate_full())
268
+ # Access them directly from results or use calculate_q_factors() for statistics
269
+ batch_analyzer.calculate_q_factors() # Ensures Q is in results dict
270
+ q_stats = batch_analyzer.get_q_factor_statistics()
271
+
272
+ # Get summary table
273
+ summary = batch_analyzer.get_summary_table()
274
+ df_summary = pd.DataFrame(summary['data'])
275
+
276
+ # Consistency analysis
277
+ consistency = batch_analyzer.consistency_analysis()
278
+
279
+ # CRLB comparison
280
+ crlb_analysis = batch_analyzer.crlb_comparison_analysis()
281
+ ```
282
+
283
+ See `examples/batch_analysis_example.py` for a complete batch analysis example.
284
+
285
+ ## Project Structure
286
+
287
+ ### Core Package (`ringdownanalysis/`)
288
+
289
+ - **`signal.py`**: `RingDownSignal` class for signal generation
290
+ - **`estimators.py`**: Frequency estimation classes (NLS, DFT)
291
+ - **`crlb.py`**: CRLB calculation
292
+ - **`data_loader.py`**: Data loading utilities for CSV and MAT files
293
+ - **`analyzer.py`**: Single-file analysis
294
+ - **`batch_analyzer.py`**: Batch processing and analysis
295
+ - **`monte_carlo.py`**: Monte Carlo simulation framework
296
+ - **`compat.py`**: Compatibility layer (function-based API)
297
+
298
+ ### Documentation (`docs/`)
299
+
300
+ - **`api/`**: Sphinx API documentation — build with ``make -C docs/api html``
301
+ - **`data_format.md`**: Data format specification for CSV and MAT files
302
+ - **`tn/main.tex`**: Comprehensive LaTeX document with theoretical foundation
303
+ - **`tn/main.pdf`**: Compiled technical note
304
+
305
+ ### Examples (`examples/`)
306
+
307
+ - **`usage_example.py`**: Comprehensive usage examples for all features
308
+ - **`batch_analysis_example.py`**: Batch analysis workflow example
309
+ - **`benchmark.py`**: Simple performance benchmark comparing NLS and DFT methods
310
+ - **`logging_config_example.py`**: Examples for configuring logging in production and debugging
311
+
312
+ ### Benchmarks (`benchmarks/`)
313
+
314
+ - **`benchmark_suite.py`**: Comprehensive pytest-benchmark test suite
315
+ - **`run_benchmarks.py`**: Script to run benchmarks and generate reports
316
+ - **`run_profiling.py`**: Script to profile workloads and identify bottlenecks
317
+ - **`profile_utils.py`**: cProfile utilities for profiling workloads
318
+ - **`README.md`**: Detailed guide for benchmarking and profiling
319
+
320
+ See `benchmarks/README.md` for detailed information on performance benchmarking and profiling.
321
+
322
+ ### Notebooks (`notebooks/`)
323
+
324
+ - **`analysis_example.ipynb`**: Interactive analysis examples
325
+ - **`batch_analysis_example.ipynb`**: Batch analysis in notebook format
326
+
327
+ ## Key Results
328
+
329
+ - **NLS Method**: Achieves statistical efficiency, approaching the CRLB for ring-down signals when using the explicit ring-down model
330
+ - **DFT Method**: Provides computationally efficient estimation with Lorentzian peak fitting, but suffers from statistical inefficiency due to discrete frequency sampling
331
+ - **Exponential Decay Impact**: The exponential amplitude decay reduces effective observation time and SNR, degrading estimation performance compared to constant-amplitude signals. The degradation depends on the ratio T/τ (observation time to decay time constant)
332
+ - **Scaling Relationships**: For slow decay (T ≪ τ), accuracy scales as T⁻³/². For rapid decay (T ≫ τ), accuracy is limited by τ and scales as τ⁻³/²
333
+
334
+ ## Security
335
+
336
+ **File input**: Load only CSV and MAT files from trusted sources. MAT files use `struct_as_record=False` to reduce deserialization risks; for untrusted input, consider sandboxing or alternative loaders. CSV files via Pandas are generally safe for typical scientific data.
337
+
338
+ **Path handling**: `process_directory()` validates that the directory exists and rejects path traversal in the glob pattern (e.g., `../`). For production use with user-supplied paths (e.g., from a web form), validate and resolve paths to a trusted base directory before passing them to the API.
339
+
340
+ ## Data Format
341
+
342
+ Experimental data files should be placed in the `data/` directory:
343
+
344
+ - **CSV files**: Moku:Lab Phasemeter format with time in column 1 and phase (cycles) in column 4
345
+ - **MAT files**: MATLAB format with `moku.data` structure containing time in column 1 and phase in column 4
346
+
347
+ See `docs/data_format.md` for the full specification (column indices, units, validation rules, edge cases).
348
+
349
+ ## Dependencies
350
+
351
+ Core dependencies (automatically installed via `pip install -e .`):
352
+ - NumPy >= 1.20.0
353
+ - SciPy >= 1.7.0
354
+ - Matplotlib >= 3.5.0
355
+ - tqdm >= 4.60.0
356
+ - joblib >= 1.0.0
357
+ - pandas >= 1.3.0
358
+
359
+ For reproducible environments (examples, notebooks), install from pinned versions:
360
+
361
+ ```bash
362
+ pip install -r requirements.txt
363
+ pip install -e .
364
+ ```
365
+
366
+ Optional dependencies:
367
+ - Jupyter >= 1.0.0 (for notebooks)
368
+ - pytest >= 7.0.0 (for testing)
369
+ - pytest-cov >= 4.0.0 (for coverage)
370
+ - pytest-benchmark >= 4.0.0 (for benchmarking)
371
+ - ruff >= 0.1.0 (for linting)
372
+
373
+ ## API Documentation
374
+
375
+ Build the Sphinx API docs:
376
+
377
+ ```bash
378
+ make -C docs/api html
379
+ ```
380
+
381
+ Open `docs/api/_build/html/index.html` in a browser. Or install dev dependencies and run:
382
+
383
+ ```bash
384
+ pip install -e ".[dev]"
385
+ cd docs/api && make html
386
+ ```
387
+
388
+ ## Testing
389
+
390
+ Run the test suite:
391
+
392
+ ```bash
393
+ pytest
394
+ ```
395
+
396
+ With coverage:
397
+
398
+ ```bash
399
+ pytest --cov=ringdownanalysis --cov-report=html
400
+ ```
401
+
402
+ ## Benchmarking
403
+
404
+ The package includes a comprehensive benchmarking and profiling suite to measure performance and identify bottlenecks:
405
+
406
+ ```bash
407
+ # Run benchmarks with medium workload
408
+ python benchmarks/run_benchmarks.py --size medium
409
+
410
+ # Profile critical workloads
411
+ python benchmarks/run_profiling.py all --size medium
412
+ ```
413
+
414
+ Or run benchmarks directly with pytest:
415
+
416
+ ```bash
417
+ pytest benchmarks/benchmark_suite.py --benchmark-only
418
+ ```
419
+
420
+ See `benchmarks/README.md` for detailed information on benchmarking and profiling workflows.
421
+
422
+ ## Development
423
+
424
+ ### CI/CD
425
+
426
+ GitHub Actions run on every push and pull request:
427
+
428
+ - **Lint**: Ruff check and format
429
+ - **Test**: pytest with coverage on Python 3.8, 3.11, 3.12
430
+ - **Typecheck**: mypy
431
+
432
+ Coverage is uploaded to [Codecov](https://codecov.io) (optional; add `CODECOV_TOKEN` secret for private repos).
433
+
434
+ ### Releasing
435
+
436
+ To publish a new version to PyPI:
437
+
438
+ 1. Update `version` in `pyproject.toml`
439
+ 2. Create and push a tag:
440
+
441
+ ```bash
442
+ git tag v0.1.0
443
+ git push origin v0.1.0
444
+ ```
445
+
446
+ 3. The release workflow builds and publishes to PyPI automatically.
447
+
448
+ **Setup**: Add `PYPI_API_TOKEN` as a repository secret (Settings → Secrets → Actions). Create a token at [pypi.org/manage/account/token/](https://pypi.org/manage/account/token/).
449
+
450
+ ## References
451
+
452
+ - S. M. Kay, *Fundamentals of Statistical Signal Processing: Estimation Theory*. Prentice Hall, 1993.
453
+ - D. C. Rife and R. R. Boorstyn, "Single tone parameter estimation from discrete-time observations," *IEEE Trans. Information Theory*, 1974.