Вычисление синуса и косинуса с использованием ядра вращения CORDIC

Этот пример показывает, как вычислить синус и косинус с помощью ядра вращения CORDIC в MATLAB ®. Основанные на CORDIC алгоритмы имеют решающее значение для многих встраиваемых приложений, включая управление двигателем, навигацию, обработку сигналов и беспроводную связь.

Введение

CORDIC - это аббревиатура для COordinate Rotation DIgital Computer. Алгоритм CORDIC на основе вращения Givens (см. [1,2]) является одним из наиболее аппаратно эффективных алгоритмов, потому что он требует только итерационных операций shift-add. Алгоритм CORDIC устраняет необходимость в явных умножителях и подходит для вычисления множества функций, таких как синус, косинус, арксин, арксозин, арктангенс, векторные величины, деление, квадратный корень, гиперболические и логарифмические функции.

Алгоритм CORDIC с фиксированной точкой требует следующих операций:

  • Поиск по 1 таблице на итерацию

  • 2 сдвиги на итерацию

  • 3 сложения за итерацию

Алгоритм ядра CORDIC, использующий режим расчета вращения

Можно использовать алгоритм CORDIC rotation computing mode, чтобы вычислять синус и косинус одновременно, вычислять полярно-декартовые преобразования и для других операций. В режиме вращения известны векторы величины и угол поворота, и компоненты координат (X-Y) вычисляются после поворота.

Алгоритм CORDIC режима вращения начинается с инициализации углового аккумулятора с желаемым углом поворота. Затем решение о повороте в каждой итерации CORDIC выполняется способом, который уменьшает величину аккумулятора остаточного угла. Решение о повороте основывается на знаке остаточного угла в аккумуляторе угла после каждой итерации.

В режиме вращения уравнения CORDIC:

$$ z_{i+1} = z_{i} - d_{i}*\mbox{atan}(2^{-i}) $$

$$ x_{i+1} = x_{i} - y_{i}*d_{i}*2^{-i} $$

$$ y_{i+1} = y_{i} + x_{i}*d_{i}*2^{-i} $$

где если$$ d_{i} = -1 $$ и $$ z_{i} < 0 $$$$ +1 $$в противном случае;

$$ i = 0, 1, ..., N-1 $$, и$$ N $$ общее количество итераций.

Это обеспечивает следующий результат при$$ N $$ подходах:$$ +\infty $$

$$ z_{N} = 0 $$

$$ x_{N} = A_{N}(x_{0}\cos{z_{0}} - y_{0}\sin{z_{0}}) $$

$$ y_{N} = A_{N}(y_{0}\cos{z_{0}} + x_{0}\sin{z_{0}}) $$

Где:

$$ A_{N} = \prod_{i=0}^{N-1}{\sqrt{1+2^{-2i}}} $$.

Обычно$$ N $$ выбирается как достаточно большое постоянное значение. Таким образом,$$ A_{N} $$ могут быть предварительно вычислены.

В режиме вращения алгоритм CORDIC ограничивается углами поворота между$$ -\pi/2 $$ и. $$ \pi/2 $$Чтобы поддерживать углы за пределами этой области значений, часто используется квадрантная коррекция.

Эффективная реализация MATLAB алгоритма ядра вращений CORDIC

Пример реализации кода MATLAB алгоритма CORDIC Rotation Kernel следует (для случая скаляра x, y, и z). Этот же код может использоваться как для операции с фиксированной точкой, так и для операции с плавающей точкой.

Ядро вращения CORDIC

function [x, y, z] = cordic_rotation_kernel(x, y, z, inpLUT, n)
% Perform CORDIC rotation kernel algorithm for N iterations.
xtmp = x;
ytmp = y;
for idx = 1:n
    if z < 0
        z(:) = accumpos(z, inpLUT(idx));
        x(:) = accumpos(x, ytmp);
        y(:) = accumneg(y, xtmp);
    else
        z(:) = accumneg(z, inpLUT(idx));
        x(:) = accumneg(x, ytmp);
        y(:) = accumpos(y, xtmp);
    end
    xtmp = bitsra(x, idx); % bit-shift-right for multiply by 2^(-idx)
    ytmp = bitsra(y, idx); % bit-shift-right for multiply by 2^(-idx)
end

Основанные на CORDIC расчеты синуса и косинуса с использованием нормализованных входных параметров

Расчеты синуса и косинуса с использованием ядра вращения CORDIC

Разумный выбор начальных значений позволяет алгоритму CORDIC rotation mode непосредственно вычислять синус и косинус одновременно.

Во-первых, выполняются следующие шаги инициализации:

  • Интерполяционная таблица входного угла inpLUT установлено в atan(2 .^ -(0:N-1)).

  • $$ z_{0} $$ задается значение$$ \theta $$ входного параметра.

  • $$ x_{0} $$ установлено в.$$ 1 / A_{N} $$

  • $$ y_{0} $$ установлено в нуль.

После$$ N $$ итераций эти начальные значения приводят к следующим выходам как$$ N $$ подходы:$$ +\infty $$

  • $$ x_{N} \approx cos(\theta) $$

  • $$ y_{N} \approx sin(\theta) $$

Другие приближения функций, основанные на ядре вращения, возможны через предварительную и последующую обработку и с использованием других начальных условий (см. [1,2]).

Алгоритм CORDIC обычно запускается через заданное (постоянное) количество итераций, поскольку прекращение итераций CORDIC раньше приведет к нарушению конвейерного кода, и коэффициент усиления CORDIC$$ A_{n} $$ не будет постоянным, потому что$$ n $$ он будет варьироваться.

Для очень больших значений, $$ n $$алгоритм CORDIC гарантированно сходится, но не всегда монотонно. Обычно можно достичь большей точности, увеличив общее количество итераций.

Пример

Предположим, что у вас есть датчик угла поворота (например, в сервоприводе), который использует форматированные целочисленные значения, чтобы представлять измеренные углы поворота. Также предположим, что у вас есть 16-битный целочисленный арифметический модуль, которая может выполнять операции сложения, вычитания, сдвига и памяти. С помощью такого устройства можно было реализовать ядро вращения CORDIC, чтобы эффективно вычислить косинус и синус (эквивалентно, декартовые координаты X и Y) из значений угла датчика, без использования умножений или больших интерполяционных таблиц.

sumWL  = 16; % CORDIC sum word length
thNorm = -1.0:(2^-8):1.0; % Normalized [-1.0, 1.0] angle values
theta  = fi(thNorm, 1, sumWL); % Fixed-point angle values (best precision)

z_NT   = numerictype(theta);             % Data type for Z
xyNT   = numerictype(1, sumWL, sumWL-2); % Data type for X-Y
x_out  = fi(zeros(size(theta)), xyNT);   % X array pre-allocation
y_out  = fi(zeros(size(theta)), xyNT);   % Y array pre-allocation
z_out  = fi(zeros(size(theta)), z_NT);   % Z array pre-allocation

niters = 13; % Number of CORDIC iterations
inpLUT = fi(atan(2 .^ (-((0:(niters-1))'))) .* (2/pi), z_NT); % Normalized
AnGain = prod(sqrt(1+2.^(-2*(0:(niters-1))))); % CORDIC gain
inv_An = 1 / AnGain; % 1/A_n inverse of CORDIC gain

for idx = 1:length(theta)
    % CORDIC rotation kernel iterations
    [x_out(idx), y_out(idx), z_out(idx)] = ...
        fidemo.cordic_rotation_kernel(...
            fi(inv_An, xyNT), fi(0, xyNT), theta(idx), inpLUT, niters);
end

% Plot the CORDIC-approximated sine and cosine values
figure;
subplot(411);
plot(thNorm, x_out);
axis([-1 1 -1 1]);
title('Normalized X Values from CORDIC Rotation Kernel Iterations');
subplot(412);
thetaRadians = pi/2 .* thNorm; % real-world range [-pi/2 pi/2] angle values
plot(thNorm, cos(thetaRadians) - double(x_out));
title('Error between MATLAB COS Reference Values and X Values');
subplot(413);
plot(thNorm, y_out);
axis([-1 1 -1 1]);
title('Normalized Y Values from CORDIC Rotation Kernel Iterations');
subplot(414);
plot(thNorm, sin(thetaRadians) - double(y_out));
title('Error between MATLAB SIN Reference Values and Y Values');

Ссылки

  1. Jack E. Volder, The CORDIC Trigonometric Computing Technique, IRE Transactions on Electronic Computers, Volume EC-8, September 1959, pp330-334.

  2. Рэй Андрака, Обзор алгоритма CORDIC для компьютеров на основе FPGA, Труды 1998 ACM/SIGDA шестой международный симпозиум по программируемым массивам ворот для поля, 22-24, 1998 февраля, pp191-200