# Phase and Amplitude Calculation for a Pure Complex Tone in a DFT using Multiple Bins

## Introduction

This is an article to hopefully give a better understanding of the Discrete Fourier Transform (DFT) by deriving exact formulas to calculate the phase and amplitude of a pure complex tone from several DFT bin values and knowing the frequency. This article is functionally an extension of my prior article "Phase and Amplitude Calculation for a Pure Complex Tone in a DFT"[1] which used only one bin for a complex tone, but it is actually much more similar to my approach for real valued tones in my article "Phase and Amplitude Calculation for a Pure Real Tone in a DFT: Method 1"[2].

## The Base Definitions

The same variables and equation for describing the tone from my previous articles will be used. The variables are amplitude ($M$), frequency ($f$), phase ($\phi$), and the sample count in the frame ($N$). The signal definition is: $$S_n = M \cdot { e^{ i(\alpha n + \phi) } } \tag {1}$$ Where: $$\alpha = f \cdot \frac{ 2\pi }{ N } \tag {2}$$ The same $1/N$ normalized DFT definition is also used. $N$ is also the number of bins in the DFT so $k$ ranges from $0$ to $N-1$. $$Z_k = \frac{ 1 }{ N } \sum_{n=0}^{N-1} { S_n e^{ -i\beta_k n } } \tag {3}$$ Where: $$\beta_k = k \cdot \frac{ 2\pi }{ N } \tag {4}$$

## Finding the Frequency

If the frequency of the tone is not known, the two or three DFT bin values nearest the peak magnitude bin can be used to find it. If the magnitude of a bin adjacent to the peak is fairly large, the two bin frequency calculation formula should be used[3]. If the bins adjacent to the peak are small in comparison, the three bin formula [4] centered at the peak should be used. In either case, the frequency can be calculated. In the noiseless single tone case, the calculation will be exact. There are other estimation methods as well.

## Integer Frequency Case

If the frequency is an integer value in terms of cycles per frame, the next few sections do not apply. Simply set $q$ to the bin value of the frequency bin and skip ahead to the "Final Results" section.

## Bin Value Formula for a Pure Complex Tone

The alternative bin value formula found in my previous blog article titled "DFT Bin Value Formulas for Pure Complex Tones" [5] will be used instead of the main one. The reason is that it replaces a complex valued divide with a real valued one. This saves a lot of calculations.

First a change of variable is needed [Edit 2022-09-23, thanks echo21]: $$\delta_k = \beta_k - \alpha \tag {5}$$ The formula is then: $$Z_k = \frac{ M }{ N } e^{ i \left[ -\delta_k (N-1) / 2 + \phi \right] } \cdot \frac{ \sin( \delta_k N / 2 ) }{ \sin( \delta_k / 2 ) } \tag {6}$$

## Vector Formulation

The formula (6) can be rearranged slightly to make it more stackable. $$Z_k = q \cdot e^{ i \left[ -\delta_k \frac{(N-1)}{2} \right] } \cdot \frac{ \sin( N \delta_k / 2 ) }{ N \sin( \delta_k / 2 ) } \tag {7}$$ Where: $$q = M e^{ i \phi } \tag {8}$$ The complex value $q$ contains the amplitude and phase being sought. Therefore this is the value to be solved for. This can be done by stacking the bin value equations around the peak bin $p$ into a vector equation. The size of the vectors is flexible. $$\begin{bmatrix} . \\ . \\ Z_{p-1}\\ Z_{p}\\ Z_{p+1}\\ . \\ . \\ \end{bmatrix} = q \cdot \begin{bmatrix} . \\ . \\ e^{ i \left[ -\delta_{p-1} \frac{(N-1)}{2} \right] } \cdot \frac{ \sin( N \delta_{p-1} / 2 ) }{ N \sin( \delta_{p-1} / 2 ) } \\ e^{ i \left[ -\delta_{p} \frac{(N-1)}{2} \right] } \cdot \frac{ \sin( N \delta_{p} / 2 ) }{ N \sin( \delta_{p} / 2 ) } \\ e^{ i \left[ -\delta_{p+1} \frac{(N-1)}{2} \right] } \cdot \frac{ \sin( N \delta_{p+1} / 2 ) }{ N \sin( \delta_{p+1} / 2 ) } \\ . \\ . \\ \end{bmatrix} \tag {9}$$ This equation is much simpler in vector notation. $$\vec Z = q \vec Y \tag {10}$$ $\vec Z$ has the selected bin values from the DFT and $\vec Y$ is the calculated basis vector.

## Solving for q

Finding $q$ is a standard Linear Algebra best fit with a single basis vector. Dotting both sides of (10) with the complex conjugate of $\vec Y$ turns the vector equation into a simple scalar one. $$\vec Z \cdot \vec Y^* = q \vec Y \cdot \vec Y^* \tag {11}$$ The value of $q$ can now be calculated. $$q = \frac{ \vec Z \cdot \vec Y^* }{ \vec Y \cdot \vec Y^* } \tag {12}$$

## The Final Results

Thanks to the polar form of complex numbers, once the value of q is known finding the values of $\phi$ and $M$ is very straightforward. $$\phi = \arg( q ) = \operatorname{atan2}( Im[q], Re[q] ) \tag {13}$$ $$M = \| q \| = \sqrt{ (Im[q])^2 + (Re[q])^2 } \tag {14}$$ That's all there is to it.

## Numerical Results

Here are some numeric results for using different length vectors. A length one vector is identical to the one bin solution provided in my previous blog article[1]. The result values are the RMS values for the errors multiplied by 100. The magnitude of the signal is one.

***********************************************************

The sample count is 32
and the run size is 10000
The phase value is  0.123400

Errors are shown as RMS at 100x actual value

Target Noise Level = 0.100

Freq   One Vec     Two Vec    Three Vec   Four Vec    Five Vec
Phase Mag   Phase Mag   Phase Mag   Phase Mag   Phase Mag
---- ------------ ----------- ----------- ----------- -----------
3.1   1.30 1.27   1.28 1.27   1.28 1.26   1.28 1.26   1.28 1.26
3.2   1.35 1.36   1.31 1.33   1.29 1.31   1.29 1.30   1.28 1.30
3.3   1.49 1.49   1.36 1.37   1.33 1.34   1.31 1.33   1.30 1.32
3.4   1.68 1.68   1.39 1.41   1.35 1.37   1.32 1.34   1.31 1.33
3.5   1.96 1.98   1.40 1.41   1.36 1.37   1.33 1.33   1.31 1.32
3.6   1.69 1.68   1.40 1.40   1.37 1.37   1.34 1.33   1.33 1.32
3.7   1.48 1.47   1.36 1.35   1.33 1.32   1.31 1.31   1.30 1.30
3.8   1.38 1.36   1.33 1.32   1.32 1.30   1.31 1.29   1.31 1.29
3.9   1.30 1.30   1.30 1.29   1.29 1.29   1.29 1.28   1.29 1.28

***********************************************************


The results clearly show that when a frequency is near an integer bin, increasing the number of bins in the vector does not significantly improve the results. However, when the frequency is closer to being between two bins, having more leakage, using the extra bin values reduces the errors. The values in the last two columns indicates diminishing returns on extra bins.

## Conclusion

The phase and amplitude calculations of a pure complex tone from DFT bin values are significantly simpler than for a real valued tone. The derivation presented in this article is not very complicated and the results are easy to verify. It is important to keep in mind that these equations are exact as no approximation were used in their derivation.

## References

[1] Dawg, Cedron, Phase and Amplitude Calculation for a Pure Complex Tone in a DFT

[3] Dawg, Cedron, A Two Bin Exact Frequency Formula for a Pure Complex Tone in a DFT

[4] Dawg, Cedron, Three Bin Exact Frequency Formulas for a Pure Complex Tone in a DFT

[5] Dawg, Cedron, DFT Bin Value Formulas for Pure Complex Tones

[ - ]
Comment by September 23, 2022

Hi Cedron,

thanks for providing the community with your solutions!

A colleague of me and I independently implemented Matlab code from your formulae (1) (the signal Sn) and (13) (the estimated phase).

We get Z_k for formula (12) from

(20) Z_k = fft(Sn) in Matlab

If we use

(21) Y_k := exp(1i*(-delta_k * (N-1)/2)) * weight_k as in formula (9)

the estimated phase Phi (13) changes with fractional bin for both implementations. If we use

(22) Y_k := exp(1i*delta_k * (N-1)/2) * weight_k (note the inverted exponent)

the estimated phase Phi (13) is at least constant over fractional bin but it is inverted to the signal phase Phi (1).

Are you really sure that all signs in your formulae are correct? If so, what might go wrong on our side?

echo21

[ - ]
Comment by September 23, 2022
Hi echo21,

I am happy to hear my formulas are useful.  It does seem you have found a mistake, which occurs in my definition of (5) in this article vs (20) in the referenced article.  I will fix it shortly in this article.  The flipped sign washes out in the sin quotient, so that should make everything okay.

Your correction is very much appreciated.  Please let me know if that doesn't resolve the problem.

Ced
[ - ]
Comment by September 23, 2022

Hi Ced,

thanks for immediate reply and correction of formula (5)!

As expectable the estimated phase Phi (13) does not vary with fractional bin anymore. Anyway its sign is still inverted. This seems also be expectable as your today's negation of delta_k (5) cannot change the sign of Phi (13), can it?

To me it seems there must be another sign error in your formulae. (BTW: as a physicist I normally don't care about my own sign errors. I know it's just a sign  ;-) )

echo21

[ - ]
Comment by September 23, 2022
Sign errors are easy, but they generally aren't insignificant.  However, it is not readily apparent to me what is wrong.  If you flip the sign of 'q', the result of (13) and (14) would remain the same.  [Correction:  (13) should change by $\pi$] Nor, should using Y instead of Y* make a difference, except in very rare cases, but if you are getting the negative of the expected phi, it means you have the complex conjugate of q somehow.

If you send me an email to cedron at protonmail dot com, I'll search for my testing code, written in C, and I can send that to you as well.  My numerical results turned out fine, so that should help solve the problem.
[ - ]
Comment by September 24, 2022

Hi Ced, I agree and I am willing to find and fix my error with the flipped phase sign. But I don't have the time right now. As soon as I have I will follow up in this thread.

Best regards

echo21

[ - ]
Comment by September 23, 2022

[Edit 2022/09/26] As there had been ambiguities in my original post (see below) that resulted in a long irrelevant sub-thread here's a revised version:

Hi Ced,

even though your article titles "Phase and Amplitude Calculation for a Pure Complex Tone in a DFT using Multiple Bins" I'd like to mention that (13) and (14) also yield very accurate results if used with pure real tones. If I get the Z_k for formula (12) from

(23) Z = fft(real(Sn)) where Sn is defined in (1)

I neither find any major difference in the estimated phase (13) nor in the estimated magnitude (14) (the latter is of course the half of the pure complex tone case (1) as we now get a hermitian spectrum Z*(-f)=Z(f) (i.e. 2 peaks, one at -f and one at +f) ).

So even though you derived the calculus using pure complex tone (1) Sn it works fine for real tones as well, correct?

Can you reproduce this with your test code?

My explanation bases on the linearity of the DFT: as your calculus works for Sn and of course for conj(Sn) it has also to work with Sn+conj(Sn) = real(Sn). Are you happy with this explanation?

==== original post ====

Hi Ced,

let's follow-up on my issue with the flipped phase sign later in the other thread.

Here I'd like to mention that (13) and (14) yield good results if I get the Z_k for formula (12) from

(23) Z_k = fft(real(Sn))

So even though you derived the calculus using pure complex tone (1) Sn it works fine for real tones as well.

Can you reproduce this with your test code?

My explanation is that DFT is a linear function: as your calculus works for Sn and for conj(Sn) it will also work with Sn+conj(Sn) = real(Sn). Are you happy with this explanation?

[ - ]
Comment by September 23, 2022

Well, I'm not happy because it isn't working for you.  You shouldn't need to do that.  My test code is kind of complicated because I optimized a few things and had to include the noise generation stuff.  So, I decided to code an example in Gambas, a variant of VisualBasic, available only on Linux.  Float in Gambas is actually a double precision value.

' Gambas module file

'======================================================
Public Sub Main()

'---- Frame parameters

Dim N As Integer = 10

Dim RadiansPerBin As Float = 2.0 * Pi() / N

'---- Signal parameters

Dim M As Float = 1.2345
Dim f As Float = 4.4
Dim phi As Float = 0.12345

Dim alpha As Float = f * RadiansPerBin

'---- Build the Signal

Dim S As Complex[] = New Complex[N]
Dim Z As Complex[] = New Complex[N]

For nn As Integer = 0 To N - 1
Dim angle As Float = nn * alpha + phi
S[nn] = Complex.Polar(M, angle)
Print nn, S[nn]
Next

Print

'---- Calculate DFT

For k As Integer = 0 To N - 1
Dim Dot As Complex = 0.0
Dim Beta_k As Float = k * RadiansPerBin

For nn As Integer = 0 To N - 1
Dim angle_nk As Float = nn * Beta_k
Dot += S[nn] * Complex.Polar(1.0, -angle_nk)
Next

Z[k] = Dot / N

Print k, Z[k]

Next

Print

'---- Pretend to find closest bin

Dim center As Integer = 4

'---- Calculate Y

Dim Nminus1over2 As Float = (N - 1.0) / 2.0

Dim Y As Complex[] = New Complex[N]

For p As Integer = center - 1 To center + 1
Dim beta_p As Float = p * RadiansPerBin
Dim delta_p As Float = Beta_p - alpha
Dim angle_p As Float = -delta_p * Nminus1over2
Dim twist As Complex = Complex.Polar(1.0, angle_p)
Dim half As Float = delta_p / 2.0
Dim heft As Float = Sin(N * half) / (N * Sin(half))

Y[p] = twist * heft

Print Y[p]
Next

Print

'---- Do Dots

Dim ZYstar As Complex = 0.0
Dim YYstar As Complex = 0.0

For p = center - 1 To center + 1
Dim Ystar As Complex = Y[p].Conj()
ZYstar += Z[p] * Ystar
YYstar += Y[p] * Ystar
Next

Print ZYstar, YYstar

Print

'---- Calculate

Dim q As Complex = ZYstar / YYstar

Dim phi_calc As Float = q.Arg()
Dim M_calc As Float = Sqr(q.Abs2())

Print M_calc, phi_calc

End
'======================================================


The output looks like this:

0       1.22510511071213+0.152012228807469i
1       -1.19503335841401+0.309653874959967i
2       0.997122722298703-0.727830012211385i
3       -0.659169163062415+1.04378458719584i
4       0.228637253777442-1.21314271880315i
5       0.234006078341755+1.21211856074359i
6       -0.66378395217162-1.04085595297305i
7       1.00033534253653+0.723408219798702i
8       -1.19639260681525-0.304359951961177i
9       1.22442008487821-0.157434766639541i

0       0.119524751208147-0.000264593108273i
1       0.127514280593834+0.041120044968754i
2       0.138978813548416+0.100504712593541i
3       0.162574246700542+0.222725736402615i
4       0.291448169586557+0.890274524164614i
5       -0.001387045647865-0.626570688319986i
6       0.074797084126057-0.231947615332088i
7       0.094380207254558-0.130509792906318i
8       0.104806776057766-0.076501633542264i
9       0.112467827284121-0.036818466113126i

0.152906211904147+0.162828577391701i
0.323090818157766+0.68660293833912i
-0.063613060220655-0.503549221907977i

1.0821514755975+0.134274403293645i      0.883313167282815-1.25503595100492E-17i

1.2345  0.12345


The results look okay to me. I'm pretty sure the code is true to the formulas and should be comprehendible to a MATLAB user (which I am not, sorry).

[ - ]
Comment by September 24, 2022

Hi Ced,

you seem to got me wrong (to my fault; see below). I wrote

"let's follow-up on my issue with the flipped phase sign later in the other thread."

and

"Here (i.e. in this thread) I'd like to mention that (13) and (14) yield good results if I get the Z_k for formula (12) from ..."

"Well, I'm not happy because it isn't working for you.  You shouldn't need to do that.  My test code is kind of complicated ..."

I am sorry that I first made not clear enough that the 2 threads deal with different DSP topics and are completely independent from each other.

Thanks a lot!

Best regards

echo21

[ - ]
Comment by September 24, 2022

Well, I don't see them as independent, and making sure the formulas in the article are correct is kind of more important to my happiness. Your workaround works approximately, instead of exactly in my test code. Substituting this line in for the signal definition:

    S[nn] = M * Cos(angle)


Gives this as the bottom line results:

0.5622816061273 0.123017174933794


The phase is off slightly and magnitude is off roughly by a factor of two.

0.5622816061273 * 2 = 1.124563212


The reason for this comes from the definition of cosine and how the DFT works.

$\cos( \theta_n ) = \frac{1}{2} e^{ i \theta_n } + \frac{1}{2} e^{ -i \theta_n }$

Taking the DFT results in two peaks then. The formula is being applied at the first peak, and the second peak is interfering from some distance away. The interference is what causes the approximation. If your frequency is near DC or near the Nyquist, the interference will be greater. It will be least with frequencies near half Nyquist, which is when $\alpha$ is close to $\pi / 2$. The amount of interference will also be dependent on how close the frequency is to being a whole number of cycles per frame, or you can say closer to a bin.

This is also why frequency formulas that work on a complex signal also work pretty well on the corresponding real signal. The effect, and the magnitude can be seen on this comparison evaluation of formulas applied to a real pure tone with added noise from http://www.tsdconseil.fr/log/scriptscilab/festim/index-en.html

The Candan 2 formula and my complex formula are mathematically and thus numerically equivalent even though they have quite different forms. It is a bit of a exercise to show this with algebra, but it can be done. They both veer off due to being approximations while my real formula follows the CRLB line since it is exact. My improved real formula is not in that analysis.

My time domain frequency formulas, articles starting with titles "Exact Near Instantaneous Frequency Formulas" are the only ones I have which work the same for real or complex pure tones with corresponding parameters.

Why your workaround fixes the sign problem in your test code is a property of your test code, not the equations. If you want me to look at that, please start a new topic in the forum and we can discuss it further there.

[ - ]
Comment by September 24, 2022

Hi Ced,

you wrote "Your workaround works approximately, instead ..." and you wrote "Why your workaround fixes the sign problem in your test code ...

So obviously you are still getting me wrong here: As written before this thread "Thread about using calculus with pure real tones" has absolutely nothing to do with fixing my sign errors I reported in the first thread "Thread about sign errors". That I use a real cosine signal instead of complex exp(i...) signal was neither intended to fix the phase sign error nor does it fix it!

Please forgive me, but my purpose is not getting exact results with your formulae. Instead I want to apply it to real signals with hundreds of multi-tones in an unregular grid. The base tone is 50MHz +/-5MHz, the mean distance between all tones is 100MHz but every tone has an individual random frequency offset from -5MHz to +5MHz around the regular 100MHz grid and the final tone is 60GHz +/-5MHz. Every tone has a random phase. And as I have signals measured from real RF hardware there will be noise as well.Of course the measured tones suffer also from carrier phase noise: every tone is smeared over several bins.

So I started the second thread "Thread about using calculus with pure real tones" only to find whether it makes sense in doing so. Please tell if you don't like my application.

Thank you very much for confirming that you also get with your calculus more or less very similar results with complex tones (Sn=M*exp(i ...)) and with pure real tones (Sn=M*cos(...) ). That the magnitude of the cos is half the magnitude of the exp is fully understood.

Thanks also for emphasizing accuracy issues when my tones got close to DC or to Nyquist frequency! Fortunately this cannot happen: the bin frequency is 500kHz and the Nyquist frequency is 128GHz.

[ - ]
Comment by September 24, 2022
You're welcome.  The details help.  I think my confusion stems from interpreting "yield good results" as it fixed your sign error, rather than "precise enough".

I posted a new question in the forum where you can respond, we are truly getting broader than this article's content.

[ - ]
Comment by September 26, 2022

Hi Ced,

After clearing all the confusions I'd like to close this thread for tidy communication. So I've just revised my opening post. I appreciate if you write another direct reply to it.

If you like please also revise your initial direct reply from September 23, 2022 and mark it as somehow obsolete as replaced by the new direct reply.

Best regards

echo21

To post reply to a comment, click on the 'reply' button attached to each comment. To post a new comment (not a reply to a comment) check out the 'Write a Comment' tab at the top of the comments.