作者:桂。

时间:2017-08-14  19:22:26

链接:http://www.cnblogs.com/xingshansi/p/7359940.html


前言

CORDIC算法常用来求解信号的幅度与相位,它的优势在于借助:移位寄存器+加法器/减法器便可以实现求解,而无需乘法器。大大简化了运算。本文围绕CORDIC整理用到的知识,先做个引子,不定期更新。

一、CORDIC算法

  CORDIC(Coordinate Rotation Digital Computer) 算法由Volder于1959年提出,该算法利用移位和加减运算,实现常用三角函数计算。

  A-二分查找

  现在给定一坐标点(X = 100,Y = 200)可知theta = 63.435°。

这里只讨论第一象限,当四个象限同时讨论时,只需要根据x,y的符号位给定一个标志位,对theta进行补偿即可。CORDIC的核心思想:将(X,Y)旋转一定的角度,当纵坐标为0时,旋转的角度就是theta。顺时针旋转theta后的坐标为(x',y'):

如何旋转呢,可以借鉴二分查找法的思想。我们知道θ的范围是0到90度。那么就先旋转45度试试。这里为什么选择45°呢?个人的理解:

tan(x/y) = 90° - tan(y/x)

可见45°是一个分界线。

旋转之后纵坐标为70.71,还是大于0,说明旋转的度数不够,接着再旋转22.5度(45度二分)

这时总共旋转了45+22.5=67.5度。结果纵坐标变为了负数,说明θ<67.5度,这时就要往回转,还是二分查找法的思想,这次转-11.25度(回转为负角度)。

依次类推,直到迭代的结果满足要求为止。什么时候满足要求呢?这个需要根据项目要求的精度确定,例如误差<0.5°,即45°/2(n-1)<0.5°,二分次数n最小取8。可见只需要存取8个特定角度的正弦+8个特定角度的余弦,便可以实现角度的估计,这少量的数值可以提前写入存储器,运行时直接调用。

  B-角度运算的初步改进

从旋转矩阵可以看出,每二分查找一次,就需要4次乘法运算。为了减小运算量,对旋转矩阵进一步处理:

求解相位时,cos(theta)只影响x,y的比例关系,而比例并不影响theta的取值,因此可以忽略:

这样便将每次二分的4次乘法运算缩减为2次。

  C-角度运算的进一步改进

  注意到第一次循环时,tan(45)=1,所以第一次循环实际上是不需要乘法运算的。第二次运算tan(22.5)=0.4142135623731,是个很不整的小数,如果参与乘法的小数比较有规律/比较整,则乘法运算会简化一些,因此这里转化思路:不再严格的二分区间查找,而是非均匀分割角度区间,使得tan(theta)尽可能整→这样一来,乘法不再需要乘法器,而是移位便可以实现。

tan(45)= 1
tan(26.565051177078)= 1/2
tan(14.0362434679265)= 1/4
tan(7.1250163489018)= 1/8
tan(3.57633437499735)= 1/16
tan(1.78991060824607)= 1/32
tan(0.8951737102111)= 1/64
tan(0.4476141708606)= 1/128
tan(0.2238105003685)= 1/256
...

  MATLAB仿真(迭代次数由项目指标给定的精度确定):

function theta = cordic(x,y)
%仅以第一象限为例
angle = atand(2.^([0:-1:-15]));
len = length(angle);
theta = 0;
for i = 0:len-1
if (y>0)
x_new = x+y*2^-i;%此处移位寄存器实现
y_new = y-x*2^-i;%此处移位寄存器实现
x = x_new;
y = y_new;
theta = theta+angle(i+1);
else
x_new = x-y*2^-i;%此处移位寄存器实现
y_new = y+x*2^-i;%此处移位寄存器实现
x = x_new;
y = y_new;
theta = theta-angle(i+1);
end
end

  至此便完成了CORDIC的整体思路。其他函数可仿照该思路类推。

CORDIC求解幅值的思路:

可近似处理:

具体参考:《Design of Jacobi EVD processor based on CORDIC for DOA estimation with MUSIC algorithm》

二、求解三角思路延伸

  以arctan为例,迭代次数随着theta精度的增加而增加,迭代次数过多会导致时间延迟过大,因此可以考虑函数逼近的思路。常用的有泰勒逼近和切比雪夫逼近。

因为tan(45°) = 1,且tan(x/y) = 90°-tan(y/x),因此arctan(x)只需要讨论x属于[0,1]的情况,这一点刚好满足切比雪夫近似

  A-泰勒逼近

即高等数学里的泰勒展开:

对应的指令:

syms x
y = atan(x);
p50=taylor(y,'Order',3,'ExpansionPoint',0.5);

  Order:指定阶数,expansionpoint:指定展开的位置。

求解:

double(subs(p50,x,i));%i为指定位置的数值

  B-切比雪夫逼近

切比雪夫多项式可表示为:

,可以求得以下递推关系:

函数f(x)可以近似表达为傅里叶级数:

其中:

这就完成了切比雪夫近似的思想。

给出代码实现:

function f = Chebyshev(y,k,x0)
%用切比雪夫多项式逼近已知函数
%已知函数:y(类型:表达式)
%逼近已知函数所需项数:k
%逼近点的x坐标:x0
%求得的切比雪夫逼近多项式或在x0处的逼近值:f syms t;
T(1:k+1) = t;
T(1) = 1;
T(2) = t;
c(1:k+1) = 0.0; c(1)=quad(matlabFunction(y(t)*T(1)/sqrt(1-t^2)),-1,1)/pi;
c(2)=2*quad(matlabFunction(y(t)*T(2)/sqrt(1-t^2)),-1,1)/pi;
f = c(1)+c(2)*t; for i=3:k+1
T(i) = 2*t*T(i-1)-T(i-2);
c(i) = 2*quad(matlabFunction(y(t)*T(i)/sqrt(1-t^2)),-1,1)/pi;
f = f + c(i)*T(i);
f = vpa(f,6); if(i==k+1)
if(nargin == 3)
f = subs(f,'t',x0);
else
f = vpa(expand(f),6);
end
end
end

  函数中vpa用来控制精度,例如:

digits(3);%控制精度
vpa(1/3);

  结果便是0.333,而不是0.33333333...

  调用形式:

 Chebyshev(y,3,i);%i为对应横坐标

  这里对于taylor、chebv展开,都按3阶处理(其他阶数类似),得出逼近误差:

可以看出chebv的逼近更加稳健。

三、CORDIC仿真

分两个思路整理,首先是调用IP核,其次是不使用IP核。

  A-调用IP核

直接看使用手册即可。

  B-不使用IP核

//CORDIC implementation for sine and cosine for Final Project 

//Claire Barnes

module CORDIC(clock, cosine, sine, x_start, y_start, angle);

  parameter width = 16;

  // Inputs
input clock;
input signed [width-1:0] x_start,y_start;
input signed [31:0] angle; // Outputs
output signed [width-1:0] sine, cosine; // Generate table of atan values
wire signed [31:0] atan_table [0:30]; assign atan_table[00] = 'b00100000000000000000000000000000; // 45.000 degrees -> atan(2^0)
assign atan_table[01] = 'b00010010111001000000010100011101; // 26.565 degrees -> atan(2^-1)
assign atan_table[02] = 'b00001001111110110011100001011011; // 14.036 degrees -> atan(2^-2)
assign atan_table[03] = 'b00000101000100010001000111010100; // atan(2^-3)
assign atan_table[04] = 'b00000010100010110000110101000011;
assign atan_table[05] = 'b00000001010001011101011111100001;
assign atan_table[06] = 'b00000000101000101111011000011110;
assign atan_table[07] = 'b00000000010100010111110001010101;
assign atan_table[08] = 'b00000000001010001011111001010011;
assign atan_table[09] = 'b00000000000101000101111100101110;
assign atan_table[10] = 'b00000000000010100010111110011000;
assign atan_table[11] = 'b00000000000001010001011111001100;
assign atan_table[12] = 'b00000000000000101000101111100110;
assign atan_table[13] = 'b00000000000000010100010111110011;
assign atan_table[14] = 'b00000000000000001010001011111001;
assign atan_table[15] = 'b00000000000000000101000101111100;
assign atan_table[16] = 'b00000000000000000010100010111110;
assign atan_table[17] = 'b00000000000000000001010001011111;
assign atan_table[18] = 'b00000000000000000000101000101111;
assign atan_table[19] = 'b00000000000000000000010100010111;
assign atan_table[20] = 'b00000000000000000000001010001011;
assign atan_table[21] = 'b00000000000000000000000101000101;
assign atan_table[22] = 'b00000000000000000000000010100010;
assign atan_table[23] = 'b00000000000000000000000001010001;
assign atan_table[24] = 'b00000000000000000000000000101000;
assign atan_table[25] = 'b00000000000000000000000000010100;
assign atan_table[26] = 'b00000000000000000000000000001010;
assign atan_table[27] = 'b00000000000000000000000000000101;
assign atan_table[28] = 'b00000000000000000000000000000010;
assign atan_table[29] = 'b00000000000000000000000000000001;
assign atan_table[30] = 'b00000000000000000000000000000000; reg signed [width:0] x [0:width-1];
reg signed [width:0] y [0:width-1];
reg signed [31:0] z [0:width-1]; // make sure rotation angle is in -pi/2 to pi/2 range
wire [1:0] quadrant;
assign quadrant = angle[31:30]; always @(posedge clock)
begin // make sure the rotation angle is in the -pi/2 to pi/2 range
case(quadrant)
2'b00,
2'b11: // no changes needed for these quadrants
begin
x[0] <= x_start;
y[0] <= y_start;
z[0] <= angle;
end 2'b01:
begin
x[0] <= -y_start;
y[0] <= x_start;
z[0] <= {2'b00,angle[29:0]}; // subtract pi/2 for angle in this quadrant
end 2'b10:
begin
x[0] <= y_start;
y[0] <= -x_start;
z[0] <= {2'b11,angle[29:0]}; // add pi/2 to angles in this quadrant
end
endcase
end // run through iterations
genvar i; generate
for (i=0; i < (width-1); i=i+1)
begin: xyz
wire z_sign;
wire signed [width:0] x_shr, y_shr; assign x_shr = x[i] >>> i; // signed shift right
assign y_shr = y[i] >>> i; //the sign of the current rotation angle
assign z_sign = z[i][31]; always @(posedge clock)
begin
// add/subtract shifted data
x[i+1] <= z_sign ? x[i] + y_shr : x[i] - y_shr;
y[i+1] <= z_sign ? y[i] - x_shr : y[i] + x_shr;
z[i+1] <= z_sign ? z[i] + atan_table[i] : z[i] - atan_table[i];
end
end
endgenerate // assign output
assign cosine = x[width-1];
assign sine = y[width-1]; endmodule

  

参考

  • http://blog.csdn.net/liyuanbhu/article/details/8458769

Cordic算法简介的更多相关文章

  1. [黑金原创教程] FPGA那些事儿《数学篇》- CORDIC 算法

    简介 一本为完善<设计篇>的书,教你CORDIC算法以及定点数等,内容请看目录. 贴士 这本教程难度略高,请先用<时序篇>垫底. 目录 Experiment 01:认识CORD ...

  2. 使用CORDIC算法求解角度正余弦及Verilog实现

    本文是用于记录在了解和学习CORDIC算法期间的收获,以供日后自己及他人参考:并且附上了使用Verilog实现CORDIC算法求解角度的正弦和余弦的代码.简单的testbench测试代码.以及在Mod ...

  3. webrtc 的回声抵消(aec、aecm)算法简介(转)

    webrtc 的回声抵消(aec.aecm)算法简介        webrtc 的回声抵消(aec.aecm)算法主要包括以下几个重要模块:1.回声时延估计 2.NLMS(归一化最小均方自适应算法) ...

  4. 三角函数计算,Cordic 算法入门

    [-] 三角函数计算Cordic 算法入门 从二分查找法说起 减少乘法运算 消除乘法运算 三角函数计算,Cordic 算法入门 三角函数的计算是个复杂的主题,有计算机之前,人们通常通过查找三角函数表来 ...

  5. AES算法简介

    AES算法简介 一. AES的结构 1.总体结构 明文分组的长度为128位即16字节,密钥长度可以为16,24或者32字节(128,192,256位).根据密钥的长度,算法被称为AES-128,AES ...

  6. (转)三角函数计算,Cordic 算法入门

    由于最近要使用atan2函数,但是时间上消耗比较多,因而网上搜了一下简化的算法. 原帖地址:http://blog.csdn.net/liyuanbhu/article/details/8458769 ...

  7. 基于FPGA的cordic算法的verilog初步实现

    最近在看cordic算法,由于还不会使用matlab,真是痛苦,一系列的笔算才大概明白了这个算法是怎么回事.于是尝试用verilog来实现.用verilog实现之前先参考软件的程序,于是先看了此博文h ...

  8. Cordic 算法之 反正切

    在通信的算法中,常采用Cordic算法之一,知道角度产生正交的的正弦余弦, 或者知道正弦和余弦求角度,求反正切. 1. 求正弦和余弦值. 方法:旋转角度,得到正弦余弦值: 再旋转角度,到达下一个正弦余 ...

  9. Cordic 算法的原理介绍

    cordic 算法知道正弦和余弦值,求反正切,即角度. 采用用不断的旋转求出对应的正弦余弦值,是一种近似求解发. 旋转的角度很讲求,每次旋转的角度必须使得 正切值近似等于 1/(2^N).旋转的目的是 ...

随机推荐

  1. PCL源码剖析之MarchingCubes算法

    原文:http://blog.csdn.net/lming_08/article/details/19432877 MarchingCubes算法简介 MarchingCubes(移动立方体)算法是目 ...

  2. PCL学习笔记二:Registration (ICP算法)

    原文:http://blog.csdn.net/u010696366/article/details/8941938 PCL Registration API Registration:不断调整,把不 ...

  3. matlab中,怎样把矩阵中所有的0改为2

    一句话搞定:>> a(find(a==0))=[2]:把矩阵中所有的0改为2

  4. [Unity-6] GameObject有时候渲染不出来

    问题描写叙述:在做游戏的过程中遇到了这样一个问题.一个怪物,假设让他出如今屏幕的中央是没问题的,可是让他出如今屏幕的边缘的位置发现他没有出现. 问题原因:经过检查发现,我给这个GameObject加入 ...

  5. 【深入JAVA】java注解

    在阅读的过程中有不论什么问题,欢迎一起交流 邮箱:1494713801@qq.com    QQ:1494713801 1.什么是java注解     注解,顾名思义,注解,就是对某一事物进行加入凝视 ...

  6. Drupal 通过API动态的加入样式文件

    前面几篇文章中讲到关于样式的载入方式.已经了解到能够通过 theme.info 载入样式文件,但都须要更新缓存才干够使用.因些这样子没有办法动态的载入一些样式文件,在DP中提供了两个API操作样式文件 ...

  7. js的正则匹配 和 blur

    <script type="text/javascript" src="http://code.jquery.com/jquery-1.10.2.min.js&qu ...

  8. ZH奶酪:Ubuntu14.04 安装Android SDK(SDK tools only)

    1.安装JDK(我安装的是Oracle的,而不是openjdk) jdk目录:usr/lib/jvm/java-7-oracle/bin/java 2.下载Android-SDK,在下边的网页选择对应 ...

  9. or1200中载入存储类指令说明

    下面内容摘自<步步惊芯--软核处理器内部设计分析>一书 OR1200中实现的载入存储类指令有8条,每条指令的作用与说明如表9.1所看到的. watermark/2/text/aHR0cDo ...

  10. 【BIEE】清除缓存

    清除缓存步骤: 1.管理→管理会话→关闭所有游标 2.管理→发出SQL语句:CALL SAPURGEALLCACHE(); 点击发送SQL语句后