Sinusoidal Generation

by Prof. Brian L. Evans

We seek to generate a one-sided discrete-time cosine (or sine) signal with fixed-frequency w0 of the form

cos(w0 n) u[n]
The sinusoidal frequency, w0, is in units of radians per sample.

Consider a one-sided continuous-time analog-amplitude cosine of frequency f0 in Hz

cos(2 pi f0 t) u(t)
sampled at a sampling rate of fs in Hz by substituting t = n / fs,
cos(2 pi (f0 / fs) n) u[n]
Hence, w0 = 2 pi f0 / fs. For example, for f0 = 1200 Hz and fs = 8000 Hz, w0 = (3/10) pi.

There are three common methods in software to generate a one-sided discrete-time sinusoidal waveform on chip:

After discussing each method, we compare their design tradeoffs.

1.0 Math Library Function Call

The function call in C would be to the cos or sin function (which requires math.h). The calculation is performed in double-precision floating-point arithmetic. There is no standard on how the cos and sin C math library functions are actually implemented.

On a desktop computer, precision of the calculation is often the primary concern. Hence, additional computation is used to obtain higher accuracy in numerical calculations. In an embedded scenario, implementation complexity is generally at a premium, so alternate methods are typically employed to compute cosine and sine values other than a call to a C math library function.

In a desktop scenario, the C function for cos(theta) could be implemented by the following steps:

  1. Compute x = theta modulo 2 pi
  2. Compute a ratio of two 10th-order polynomials in x that approximate the cosine function
Each 10th-order polynomial can be factored into Horner's form to minimize the number of multiplications:
a10 x10 + a9 x9 + a8 x8 + ... + a0 = ( ... (((a10 x + a9) x + a8) x ... ) + a0
For each cosine (or sine) value computed, the function call would need to compute 20 multiplications, 20 additions, and 1 division. In addition, 22 coefficients would need to be stored in memory. One intermediate variable, x, would need to be computed. Calculations are in floating-point arithmetic only.

Version 1.8 of the freely distributable GNU Scientific Library (GSL) implements the cosine as the function gsl_sf_cos_e, where sf means special function, in the file specfunc/trig.c. The implementation uses a Chebyshev approximation over one-eighth of the period for cosine and sine:

  1. Compute x = theta modulo (pi/4) and the octant (0-7) of theta
  2. Compute an 11th-order polynomial in x that approximates the cosine function over one octant
For each cosine (or sine) value computed, the gsl_sf_cos_e function call would need to compute about 20 multiplications, 30 additions, 2 divisions, and 2 power calculations to compute the cosine value. Additional computations would be needed to calculate the error in the approximation.

2.0 Difference Equation

The generation of a one-sided cosine signal cos(w0 n) u[n] by using a second-order difference equation is derived on page K-29 in the course reader. This is problem 1.1 from Midterm #1 from Spring 2004. The difference equation, with x[n] as input and y[n] as output, follows:
y[n] = (2 cos w0) y[n-1] - y[n-2] + x[n] - (cos w0) x[n-1]
The impulse response of the above system, i.e. the response to x[n] = d[n] where d[n] is the discrete-time impulse function, will be a one-sided cosine function.

For each cosine (or sine) value computed, the difference equation would need to compute 2 multiplications and 3 additions. In addition, 2 coefficients would need to be stored in memory. Two previous values of y[n] and one previous value of x[n] would be stored in memory. Calculations could be performed in floating-point or fixed-point arithmetic.

2.1 Improving Signal Quality

If the difference equation could be implemented with exact precision coefficients and arithmetic, then the output cosine signal would have perfect quality. However, the representation of values of cos(w0), with the exception of values -1, -1/2, 0, 1/2, and 1, in floating-point or fixed-point data types results in a loss of precision of the coefficients cos(w0) and 2 cos(w0). In addition, there is potential loss of precision in the multiplication-accumulation operations. Loss of precision in coefficient representation and arithmetic operations will accumulate due to the feedback in the difference equation.

One way to reduce the accumulation of precision loss in cosine sample calculations is to reboot the filter after each period of samples has been calculated. Reboot means to reset the filter to its initial state and run the filter again.

2.2 First-Order Difference Equation

A first-order difference equation could be used to generate a sinusoidal signal. In class today, a student asked whether or not a first-order difference equation could generate a sinusoidal signal. A first-order difference equation
y[n] - a y[n-1] = x[n]
with x[n] = d[n] generates one-sided output signals of the form
y[n] = C an u[n]
There are two interesting cases with respect to sinusoidal generation:
  1. Complex-valued sinusoid of fixed frequency w0 occurs when a = exp(j w0). y[n] = C exp(j w0 n) u[n].
  2. Real-valued sinusoid of fixed frequency w0 = pi occurs when a = exp(j pi) = -1. y[n] = C (-1)n = C cos(pi n) u[n]. This is a special case of #1.

3.0 Lookup Table

For the lookup table, we will precompute and store one period of samples for a cosine. The cosine frequency w0 can be written as 2 pi N / L. Here, N and L are integers. We remove all common factors between N and L. The value of L is the period of the cosine.

A lookup table of one period would contain L entries. The entries could be in either floating-point or fixed-point format. Using symmetry of the cosine over one period, one could store one-half of the period, or even one-fourth of the period, provided that L is divisible by 2 or 4, respectively.

Some programmable digital signal processors have read-only memory (ROM) lookup tables of cos(theta) and sin(theta) available on chip. One can interpolate values in these lookup tables to generate cosine and sine signals of different frequencies.

4.0 Design Tradeoffs

The tradeoff in signal quality vs. implementation complexity is described in the table below.

Method MACs per cosine sample Read-only memory (words) Writable memory (words) Cosine quality in floating-point Cosine quality in fixed-point
C function call 22 22 1 Second best Not applicable
Difference equation 2 2 3 Worst Second best
Lookup table 0 L 0 Best Best

MACs means multiplication-accumulation operations. L is the period of the cosine signal. Additional memory can be saved in the lookup table method by storing one-half or even one-fourth of the period by using symmetry properties of the cosine signal, but at a small increase in computation.


Last updated 06/07/22. Send comments to (Mailbox)bevans@ece.utexas.edu