赵工的个人空间


网络课堂部分转网页计算部分转编程演练

 网页编程轻松学

首页 > 网络课堂 > 网页编程轻松学 > JS实现Cordic三角函数
JS实现Cordic三角函数
去年开始接触WebAssembly,是一种可以在浏览器中运行的汇编语言,据称运行速度会很快,就想试试。但遇到一个问题,就是WebAssembly指令只提供了基础的加减乘除算法,连三角函数等都没有,稍微复杂的计算都实现不了,想进行一些复杂一些的计算都要自己写代码。
三角函数,虽然可以使用泰勒级数来计算,但实际计算量比较大而且收敛比较慢,其实直接这样计算效率比较低,但查不到改进的算法,也不知一些编程语言中的对应函数的底层计算代码,很是头疼。不过查到了一种使用迭代计算三角函数的方法,也就是CORDIC(Coordinate Rotation Digital Computer,坐标旋转数字计算)算法,可以只通过加减和移位就能计算任意角度的三角函数值,精度可以满足于工程需要,因此就非常感兴趣。
按网上资料,CORDIC算法是J.D.Volder1于1959年首次提出,首先用于导航系统,可以用来实现三角函数、双曲线、指数、对数的计算,J. Walther在1974年用它研究了一种能计算出多种超越函数的统一算法。其基本原理为:
Cordic算法
初始向量(X0,Y0)旋转θ角度之后得到向量(X1,Y1),此向量有如下关系:
X1=X0*cos(θ)-Y0*sin(θ)=cos(θ)(X0-Y0*tan(θ))
Y1=Y0*cos(θ)+X0*sin(θ)=cos(θ)(Y0+X0*tan(θ))
可以从一个起始角度开始,经过一系列旋转,达到需要的角度,为了计算方便,一般会选择一些特殊角度,比如设置第i次旋转角度为δ=arctan(2-i)。
以sin/cos计算为例,可利用正/余弦的和角公式迭代进行:
cos(a+b) = cos(a)cos(b) – sin(a)sin(b) = cos(a) [cos(b) – tan(a)sin(b)]
sin(a+b) = sin(a)cos(b) + cos(a)sin(b) = cos(a) [tan(a)cos(b) +sin(b)]
取a=arctan(2-i), 即tan(a)=2-i, 则cos(b) – tan(a)sin(b)。迭代公式为:
Cordic算法
其中的di为旋转角度。
看懂没有?估计多数人都没看懂,不过不要紧,有一篇文章介绍得比较详细,可以参考网文,网上也能搜索到多篇对其原理的介绍,但写得比较明白的不是很多,而代码有效的就更少,而且基本都是matlab代码,比较麻烦。我使用js写了一个计算代码:
function cordicsincos(angle,n=20){
  ceof=[1,1/2,1/4,1/8,1/16,1/32,1/64,1/128,1/256,1/512,1/1024,1/2048,1/4096,1/8192,1/16384,1/32768,1/65536,1/131072,1/262144
,1/524288,1/1048576];
  dangle=[45,26.565051177078,14.0362434679265,7.1250163489018,3.57633437499735,1.78991060824607,0.8951737102111,0.4476141708606
,0.2238105003685,0.1119056770662,0.0559528918938,0.027976452617,0.01398822714227,0.006994113675353,0.003497056950704
,0.001748528426980,0.000874264213694,0.000437132106872,0.000218566053439,0.000109283026720,0.000054641513360];
  let d=1,k=0.607253,x=1,y=0,z=angle;
  for(let i=0;i<=n;i++){
    if(z>=0) d=1;
    else d=-1;
    let xn=x;
    x=xn-(y*d*ceof[i]);
    y=y+(xn*d*ceof[i]);
    z=z-(d*dangle[i]);
  }
  return {cos:x*k,sin:y*k,da:z,n:n};
}
上述代码是给出一个角度计算出其对应的正弦余弦值的函数,其中先预存了两个数组值,一个是2n的倒数,预存之后可以直接使用,一个是对应的特殊旋转角度,即前面一个数组对应值的反正切角度,每次旋转为这个角度,对应的正切值就为1/2n,容易用移位方法简单计算。因为是使用JS来计算的,使用的是浮点数,因此计算中并没有使用移位方法,如果使用整数来计算,比如在单片机或fpga中实现,就需要转换为整数用移位方法来计算了。
因为两个数组长度预存为0~20,共21个元素,因此下面的迭代运算最高可以使用n=20,这时计算精度最高,当然也可以使用较低的精度,比如选n=16,一般工程应用中已经足够。迭代运算,是从0度开始旋转,每次旋转的角度都按dangle中给出的固定值,并逐次减小,可以根据需要使用正向旋转或反向旋转,这样可以可以得到在-99~99度之间的任意角度(一定精度内),如果超出这个角度范围就需要预处理为这个角度范围。
其中k为一个固定缩减因子,工程中一般可以使用0.607253;而每次迭代的z为每次迭代后的角度与需要计算的角度的偏差,最终计算后为剩余的残差。d则为方向因子,表示正向旋转或反向旋转。
使用上述JS代码的完整HTML代码为:
<!DOCTYPE html>
<html lang="zh-Hans">
<head>
  <meta charset="gb18030" />
  <title>CORDIC方法计算三角函数</title>
</head>
<body>
<div class="header"><span id="outText" class="err"></span></div>
<div class="code1">
<input type="text" name="ina1" id="ina1" maxlength="20" class="incode" size="10" value="1"/>
</div>
<div class="code1">
<label for="ocode1" id="label4" class="result"><strong>结果:</strong></label>
<div id="result" class="ocode"></div>
</div>
<div class="code1">
<label for="ocode2" id="label5" class="result"><strong>内部函数结果:</strong></label>
<div id="result2" class="ocode"></div>
</div>
<div class="button"><br />
<input type="button" name="sendMsg" id="sendMsg" class="dbutton" onclick="operate()" value="计算" /><br />
</div>
</div>
<script type="text/javascript">
function operate() {
  let sremind='';
  let out1='';
  let out2='';
  let result={};
  let sa1=document.getElementById('ina1').value;
  if(isNaN(na1)){
    sremind='输入值必须为数值!';
    out1='';
    out2='';
  }else{
    if(na1>=95||na1<=-95){
      sremind='输入值必须为-95~-95之间的数值!';
      out1='';
      out2='';
    }else{
      result=cordicsincos(na1);
      out1='余弦值为:'+result.cos+'<br>正弦值为:'+result.sin+'<br>正切值为:'+result.sin/result.cos+'<br>角度残差为:'+result.da+'<br>迭代次数:'+result.n;
      sremind='';
      out2='余弦值为:'+Math.cos(Math.PI*na1/180)+'<br>正弦值为:'+Math.sin(Math.PI*na1/180)+'<br>正切值为:'+Math.tan(Math.PI*na1/180);
    }
  }
  document.getElementById('outText').innerText=sremind;
  document.getElementById('result').innerHTML=out1;
  document.getElementById('result2').innerHTML=out2;
}
function cordicsincos(angle,n=20){
  ceof=[1,1/2,1/4,1/8,1/16,1/32,1/64,1/128,1/256,1/512,1/1024,1/2048,1/4096,1/8192,1/16384,1/32768,1/65536,1/131072,1/262144
,1/524288,1/1048576];
  dangle=[45,26.565051177078,14.0362434679265,7.1250163489018,3.57633437499735,1.78991060824607,0.8951737102111,0.4476141708606
,0.2238105003685,0.1119056770662,0.0559528918938,0.027976452617,0.01398822714227,0.006994113675353,0.003497056950704
,0.001748528426980,0.000874264213694,0.000437132106872,0.000218566053439,0.000109283026720,0.000054641513360];
  let d=1,k=0.607253,x=1,y=0,z=angle;
  for(let i=0;i<=n;i++){
    if(z>=0) d=1;
    else d=-1;
    let xn=x;
    x=xn-(y*d*ceof[i]);
    y=y+(xn*d*ceof[i]);
    z=z-(d*dangle[i]);
  }
  return {cos:x*k,sin:y*k,da:z,n:n};
}
</script>
</body>
</html>
为了在浏览器中使用js代码计算三角函数值,需要加入一个<input>标记来输入数据,并使用<div>来输出数据,还有一个计算按钮。而js代码中则需要先获取<input>中的输入,进行判断并转化为数值,还需要判断数值范围,然后调用上述的函数计算出对应的正弦余弦及正切值并在<div>中显示出来,为了知道计算出来的数值是否准确,还使用了js内部三角函数计算对应结果进行显示,可以看出计算结果的差别。
Cordic算法
将上述代码保存为html文件,打开此文件,输入角度值,点击按钮就可以显示出如上的计算结果。
需要说明的是,上述计算的输入值使用的是角度值,是按角度来计算的。前面已经说明了,其中的计算还是使用的浮点数,因为是用js写的代码,可以使用浮点数,而CORDIC的最大好处是可以通过加减及移位计算就能得到三角函数值,方便在简单的硬件中实现,具体方法可以参考网上资料,转为整数而使用移位和加减使用WebAssembly的算法可参见页面,具体做法有时间再写。
Copyright@dwenzhao.cn All Rights Reserved   备案号:粤ICP备15026949号
联系邮箱:dwenzhao@163.com  QQ:1608288659