aewallin/allantools

Allan deviation of a complex quantity

Opened this issue · 5 comments

Hi,

I'd like to suggest a small change in the calc_adev_phase function:

The line
s = np.sum(v_arr * v_arr)
should be replaced by
s = np.sum(abs(v_arr)**2)

This will make the adev and oadev functions work with complex data, without breaking anything for real data. Currently, adev and oadev do not work correctly with complex data.

This is probably a rare use case, but the definition of the allan deviation can be extended straightforwardly to complex data.
(The allan deviation with data_type="freq" at a certain tau value gives you the expected difference between the average of subsequent batches of data (up to a factor of sqrt(2)). Of course, such a thing also works for complex data - the only difference is that you then need an absolute value for calculating this difference.)

I guess that calc_hdev_phase, calc_gradev_phase, and calc_mtotdev_phase can probably be generalized in a similar manner, but I would not dare to suggest anything there because I don't understand what they are doing.

Thanks for your comment and PR.

Could you give some example where you use complex data as input to ADEV and OADEV? I haven't heard about this use case before.

The performance impact of your change is probably minimal (I haven't benchmarked it) - so in principle I can merge your PR without any issues.

Thank you for the fast answer! Our use case is as follows:
We are recording the complex spectra of optical pulses over an extended period of time. Then we want to know over which time scales a certain frequency component of this complex spectrum is stable, i.e., over which time scales averaging still improves the results. And also, up to which accuracy we can measure the complex amplitude at this frequency, because this will tell us the limit of detection when doing linear spectroscopy with these pulses. Both questions are answered by the Allan deviation.

Good point concerning the performance - I think it should be

s = np.sum(v_arr*v_arr) if np.isrealobj(v_arr) else np.sum(v_arr.real**2 + v_arr.imag**2)

then. This will not degrade the performance for the real case, and has also better performance than abs(v_arr)**2 for the complex case. I've updated the pull request.

@Leberwurscht Are you interested in the complex value of the spectral elements, or just the amount of power in a frequency bin? Are the relative phases of the frequency components stable over time?

An alternative method that might be helpful: instead of doing the ADev on the complex value, do two separate ADevs on the power and the unwrapped phase. This would then help you understand the stability of the power and of the phase independently. These may be more intuitive quantities to analyze than interpreting a real and complex part of an ADev.

Hi @jondoesntgit,

the spectral phases as well as the spectral magnitudes vary over time. Spectroscopic information is contained in the PSD and in the phase.
Doing separate adevs on the power and unwrapped phase is exactly what we have been doing so far. But from a physical point of view, at low concentration, the concentration of a solvent is directly proportional to the change in complex amplitude this solvent causes. So when we compare measurements against a reference, we actually subtract the complex amplitudes between reference and sample measurement. To be consistent with this, we also want to look at the Allan deviation of the complex amplitude itself.

Concerning interpreting real and complex part of an ADev: The (correctly generalized) Allan deviation does not have a real and complex part, even if the input is complex. This is because of the abs().