Shifted discrete Fourier transform

Forward transform

We define the shifted discrete Fourier transform as

\[Z_k \equiv \sum_{n = 0}^{N - 1} z_n \twiddle{- 2 \pi}{\left( n + \frac{1}{2} \right) k}{N} \equiv \dft{k}{N}{z_0}{z_1}{z_{N - 1}},\]

where \(\seq{k}{0}{1}{N - 1}\).

Note

Since this relation leads to

\[Z_k = \twiddle{- \pi}{k}{N} \sum_{n = 0}^{N - 1} z_n \twiddle{- 2 \pi}{n k}{N},\]

the shifted transform can be computed using the normal discrete Fourier transform followed by multiplication by a pre-factor.

By utilizing the decimation in time, the forward transform leads to

\[\begin{split}\begin{pmatrix} Z_{k } \\ Z_{k + N / 2} \\ \end{pmatrix} = \begin{pmatrix} \twiddle{\pi}{k}{N} & \twiddle{- \pi}{k}{N} \\ - I \twiddle{\pi}{k}{N} & I \twiddle{- \pi}{k}{N} \\ \end{pmatrix} \begin{pmatrix} \dft{k}{N / 2}{z_0}{z_2}{z_{N - 2}} \\ \dft{k}{N / 2}{z_1}{z_3}{z_{N - 1}} \\ \end{pmatrix}\end{split}\]

for \(\seq{k}{0}{1}{N / 2 - 1}\).

src/sdft.c
50static int sdft (
51    const size_t nitems,
52    const size_t stride,
53    const trig_t * const trigs,
54    const double complex * const xs,
55    double complex * const ys
56) {
57  if (1 == nitems) {
58    ys[0] = xs[0];
59  } else if (0 == nitems % 2) {
60    const size_t nh = nitems / 2;
61    // divide-and-conquer
62    sdft(nh, stride * 2, trigs, xs         , ys     );
63    sdft(nh, stride * 2, trigs, xs + stride, ys + nh);
64    // unify two sub problem results
65    for (size_t i = 0; i < nh; i++) {
66      const size_t j = i + nh;
67      const trig_t * const trig = trigs + stride * i;
68      const double complex twiddle[2] = {
69        trig->cos + I * trig->sin,
70        trig->cos - I * trig->sin,
71      };
72      double complex * const yi = ys + i;
73      double complex * const yj = ys + j;
74      const double complex ye = *yi;
75      const double complex yo = *yj;
76      *yi =       twiddle[0] * ye +     twiddle[1] * yo;
77      *yj = - I * twiddle[0] * ye + I * twiddle[1] * yo;
78    }
79  } else {
80    // naive O(N^2) sdft
81    for (size_t k = 0; k < nitems; k++) {
82      double complex * const y = ys + k;
83      *y = 0. + I * 0.;
84      for (size_t n = 0; n < nitems; n++) {
85        *y += xs[stride * n] * cexp(- 2. * pi * (n + 0.5) * k * I / nitems);
86      }
87    }
88  }
89  return 0;
90}

Backward transform

The inverse transform is defined as

\[z_n = \frac{1}{N} \sum_{k = 0}^{N - 1} Z_k \twiddle{2 \pi}{\left( n + \frac{1}{2} \right) k}{N} \equiv \idft{n}{N}{Z_0}{Z_1}{Z_{N - 1}}.\]

Note

Due to

\[z_n = \frac{1}{N} \sum_{k = 0}^{N - 1} \left( Z_k \exp \left( \frac{\pi k}{N} I \right) \right) \twiddle{2 \pi}{n k}{N},\]

the inverse transform of a shifted signal can be computed using the normal inverse discrete Fourier transform where the input signal is multiplied by a pre-factor beforehand.

By utilizing the decimation in time, the inverse transform leads to

\[\begin{split}\begin{pmatrix} z_{n } \\ z_{n + N / 2} \\ \end{pmatrix} = \begin{pmatrix} \frac{1}{2} & \frac{1}{2} \twiddle{\pi}{2 n + 1}{N} \\ \frac{1}{2} & - \frac{1}{2} \twiddle{\pi}{2 n + 1}{N} \\ \end{pmatrix} \begin{pmatrix} \idft{n}{N / 2}{Z_0}{Z_2}{Z_{N - 2}} \\ \idft{n}{N / 2}{Z_1}{Z_3}{Z_{N - 1}} \\ \end{pmatrix}\end{split}\]

for \(\seq{n}{0}{1}{N / 2 - 1}\).

src/sdft.c
102static int isdft (
103    const size_t nitems,
104    const size_t stride,
105    const trig_t * const trigs,
106    const double complex * const xs,
107    double complex * const ys
108) {
109  if (1 == nitems) {
110    ys[0] = xs[0];
111  } else if (0 == nitems % 2) {
112    const size_t nh = nitems / 2;
113    // divide-and-conquer
114    isdft(nh, stride * 2, trigs, xs         , ys     );
115    isdft(nh, stride * 2, trigs, xs + stride, ys + nh);
116    // unify two sub problem results
117    for (size_t i = 0; i < nh; i++) {
118      const size_t j = i + nh;
119      const trig_t * const trig = trigs + (2 * i + 1) * stride;
120      const double complex twiddle = trig->cos + I * trig->sin;
121      double complex * const yi = ys + i;
122      double complex * const yj = ys + j;
123      const double complex ye = *yi;
124      const double complex yo = *yj;
125      *yi = 0.5 * ye + 0.5 * twiddle * yo;
126      *yj = 0.5 * ye - 0.5 * twiddle * yo;
127    }
128  } else {
129    // naive O(N^2) isdft
130    for (size_t n = 0; n < nitems; n++) {
131      double complex * const y = ys + n;
132      *y = 0. + I * 0.;
133      for (size_t k = 0; k < nitems; k++) {
134        *y += xs[stride * k] * cexp(2. * pi * (n + 0.5) * k * I / nitems);
135      }
136      *y /= nitems;
137    }
138  }
139  return 0;
140}