Metadata-Version: 2.4
Name: PyNAFF
Version: 1.2.0
Summary: Numerical Analysis of Fundamental Frequencies for one or more signals
Author: Foteini Asvesta, Panagiotis Zisopoulos
Author-email: Nikos Karastathis <nkarast@gmail.com>
License-Expression: GPL-3.0-only
Project-URL: Homepage, https://github.com/PZiso/PyNAFF
Project-URL: Repository, https://github.com/PZiso/PyNAFF
Project-URL: Issues, https://github.com/PZiso/PyNAFF/issues
Keywords: NAFF,frequency analysis,beam position monitor
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Science/Research
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: Physics
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE.txt
Requires-Dist: numpy>=1.22
Dynamic: license-file

# PyNAFF

Authors:

* Foteini Asvesta (fasvesta .at. cern .dot. ch)
* Nikos Karastathis (nkarast .at. cern .dot. ch)
* Panagiotis Zisopoulos (pzisopou .at. cern .dot. ch)

A Python implementation of J. Laskar's Numerical Analysis of Fundamental
Frequencies (NAFF) method.

## Installation

```bash
python -m pip install PyNAFF
```

## Single BPM

```python
import numpy as np
import PyNAFF as pnf

t = np.arange(3001)
signal = np.sin(2.0 * np.pi * 0.12345 * t)
result = pnf.naff(signal, turns=500, nterms=1, window=1)

# Each row is:
# [order, frequency, amplitude, real amplitude, imaginary amplitude]
frequency = result[0, 1]
```

`turns` is the number of integration intervals, so the input must contain at
least `turns + 1` observations. For real sinusoids, the reported amplitude is
the magnitude of one complex Fourier coefficient, equal to half the sinusoid's
peak amplitude.

## Multiple BPMs

Place observations on axis 0 and BPMs on axis 1:

```python
signals = np.column_stack([
    np.sin(2.0 * np.pi * 0.12345 * t),
    2.0 * np.sin(2.0 * np.pi * 0.27123 * t),
])
results = pnf.naff(signals, turns=500, nterms=1)

# results.shape == (2 BPMs, 1 term, 5 values)
frequencies = results[:, 0, 1]
amplitudes = results[:, 0, 2]
```

For multi-BPM input, unused term rows are filled with `NaN` when extraction
for a BPM stops before `nterms`.

The `tol` option controls duplicate residual handling as a fraction of one FFT
bin. If NAFF stops early because a residual peak is very close to a previously
extracted frequency, increasing `tol` can let it remove that residual and
continue to weaker frequencies. The default is `1e-4`; large values can also
turn spectral leakage into spurious frequencies, so compare results across
several values. `nterms` is an upper bound, not a guaranteed result count.

For real input, prefer `getFullSpectrum=False`. A full spectrum contains both
positive and negative conjugate frequencies, and each one occupies a result
row.
