badcode
badcode

Reputation: 610

Discrete Fourier Transform With C, Implementation problems?

I'm trying to understand some basics of DFT, some math equations, and try to implement it with C. Well, this is the function i used from a book (Algorithms for Image Processing And Computer Vision)

void slowft (float *x, COMPLEX *y, int n)
{
    COMPLEX tmp, z1, z2, z3, z4;
    int m, k;

/* Constant factor -2 pi */
    cmplx (0.0, (float)(atan (1.0)/n * -8.0), &tmp);
    
    printf (" constant factor -2 pi %f ", (float)(atan (1.0)/n * -8.0));
    for (m = 0; m<=n; m++)
    {
      NEXT();
      cmplx (x[0], 0.0, &(y[m]));
      for (k=1; k<=n-1; k++)
      {
/* Exp (tmp*k*m) */
        cmplx ((float)k, 0.0, &z2);
        cmult (tmp, z2, &z3);
        cmplx ((float)m, 0.0, &z2);
        cmult (z2, z3, &z4);
        cexp (z4, &z2);
/* *x[k] */
        cmplx (x[k], 0.0, &z3);
        cmult (z2, z3, &z4);
/* + y[m] */
        csum (y[m], z4, &z2);
        y[m].real = z2.real; y[m].imag = z2.imag;
      }
    }
}

So actually, I'm stuck on the Constant Factor part. I didn't understand:

1-) what it came from(especially arctan(1)) and

2-) what its purpose of it.

This is the equation of DFT:

enter image description here

And these are other functions that i used:

void cexp (COMPLEX z1, COMPLEX *res)
{
    COMPLEX x, y;

    x.real = exp((double)z1.real);
    x.imag = 0.0;
    y.real = (float)cos((double)z1.imag);
    y.imag = (float)sin((double)z1.imag);
    cmult (x, y, res);
}

void cmult (COMPLEX z1, COMPLEX z2, COMPLEX *res)
{
    res->real = z1.real*z2.real - z1.imag*z2.imag;
    res->imag = z1.real*z2.imag + z1.imag*z2.real;
}

void csum (COMPLEX z1, COMPLEX z2, COMPLEX *res)
{
    res->real = z1.real + z2.real;
    res->imag = z1.imag + z2.imag;
}

void cmplx (float rp, float ip, COMPLEX *z)
{
    z->real = rp;
    z->imag = ip;
}

float cnorm (COMPLEX z)
{
    return z.real*z.real + z.imag*z.imag;
}

Upvotes: 0

Views: 442

Answers (2)

Eric Postpischil
Eric Postpischil

Reputation: 222753

Here is the code with comments explaining it.

void slowft (float *x, COMPLEX *y, int n)
{
    COMPLEX tmp, z1, z2, z3, z4;
    int m, k;

/* Constant factor -2 pi */
    cmplx (0.0, (float)(atan (1.0)/n * -8.0), &tmp);
        /*  atan(1) is π/4, so this sets tmp to -2πi/n.  Note that the i
            factor, the imaginary unit, comes from putting the expression in
            the second argument, which gives the imaginary portion of the
            complex number being assigned.  (It is written as "j" in the
            equation displayed in the question.  That is because engineers use
            "j" for i, having historically already used "i" for other purposes.)
        */
    
    printf (" constant factor -2 pi %f ", (float)(atan (1.0)/n * -8.0));
    for (m = 0; m<=n; m++)
    {
      NEXT();
        // Well, that is a frightening thing to see in code.  It is cryptic.

      cmplx (x[0], 0.0, &(y[m]));
        /*  This starts to calculate a sum that will be accumulated in y[m].
            The sum will be over k from 0 to n-1.  For the first term, k is 0,
            so -2πiwk/n will be 0.  The coefficient is e to the power of that,
            and e**0 is 1, so the first term is x[0] * 1, so we just put x[0]
            diretly in y[m] with no multiplication.
        */

      for (k=1; k<=n-1; k++)
        //  This adds the rest of the terms.
      {

/* Exp (tmp*k*m) */
        cmplx ((float)k, 0.0, &z2);
            //  This sets z2 to k.

        cmult (tmp, z2, &z3);
            /*  This multiplies the -2πi/n from above with k, so it puts
                -2πi/n from above, and This computes -2πik/n it in z3.
             */

        cmplx ((float)m, 0.0, &z2);
            //  This sets z2 to m.  m corresponds to the ω in the equation.

        cmult (z2, z3, &z4);
            //  This multiplies m by -2πik/n, putting -2πiwk/n in z4.

        cexp (z4, &z2);
            /*  This raises e to the power of -2πiwk/n, finishing the
                coefficient of the term in the sum.
            */

/* *x[k] */
        cmplx (x[k], 0.0, &z3);
            //  This sets z3 to x[k].

        cmult (z2, z3, &z4);
            //  This multiplies x[k] by the coefficient, e**(-2πiwk/n).

/* + y[m] */
        csum (y[m], z4, &z2);
            /*  This adds the term (z4) to the sum being accumulated (y[m])
                and puts the updated sum in z2.
            */

        y[m].real = z2.real; y[m].imag = z2.imag;
            /*  This moves the updated sum to y[m].  This is not necessary
                because csum is passed its operands as values, so they are
                copied when calling the function, and it is safe to update its
                output.  csum(y[m], z4, &y[m]) above would have worked.  But
                this works too.
            */
    }
}

Standard C has support for complex arithmetic, so it would be easier and clearer to include <complex.h> and write code this way:

void slowft(float *x, complex float *y, int n)
{
    static const float TwoPi = 0x3.243f6a8885a308d313198a2e03707344ap1f;

    float t0 = -TwoPi/n;

    for (int m = 0; m <=n; m++)
    {
        float t1 = t0*m;

        y[m] = x[0];
        for (int k = 1; k < n; k++)
            y[m] += x[k] * cexpf(t1 * k * I);
    }
}

Upvotes: 1

John Bollinger
John Bollinger

Reputation: 180266

1-) what it came from(especially arctan(1)) and

The code comment immediately above clues you in:

/* Constant factor -2 pi */

... although actually what is being computed is -2 pi / n (in the broader context of producing a complex number with that as the coefficient of its imaginary component). Observe that the tangent has value 1 for angles whose sine and cosine are equal. The angle that has that property and is in the range [0, pi) is pi / 4, so atan(1.0) * -8.0 is (a good approximation to) -2 pi.

2-) what its purpose of it.

It (or actually its additive inverse) appears in the DFT equation you presented, so it is natural that it appears in a function intended to implement that formula.

Upvotes: 2

Related Questions