arctan()函数的查表算法

文集库

2020-05-13 14:34:56

0

目标:实现反正切函数的查表算法,精度0.5°。

分析:

f(x)=arctan(x)函数单调但非线性,越接近原点处,斜率越大。

90°比较特殊,其值为正无穷大,只要得到的正切值tana大于114.59(89.5度对应的正切值),就可以认为是90度。

假如0-89.5°用一个查找表实现,索引为正切值,值为角度值。tan89.5为115,假如用N点uint16_t型查找表,需要保证

arctan(115 / N) < 0.5°

即 115 / N < 0.0087

可选的最小值为13219,意味着至少要建立一张13K×16的表,占用26KB的Flash,对于一般的MCU来说不可接受。

如果0-45°用一个查找表实现,由于tan45°=1,根据上面的计算公式,可以得出N的最小值为114,这个N值对一般的MCU均可以接受,甚至可以增加至256点来提高精度。对于45°以上的角度,可以根据两角和的三角公式来得到,推导过程如下:

如果测量的正切值K大于1,那么根据最后的公式,可以求出比45°大的那个角度a,再加上45°就是实际的角度。而a的计算可以由256点查找表来快速得到,这样就实现了用一个小容量查找表计算90度以内的反正切值。

查找表涉及两个数值:表索引idx和表的角度值angle[idx]。

索引值idx对应0-45°的正切值,由于这些值均小于1,采用整体扩大256倍的方法,将正切值就映射到了0-256的区间,从而索引idx的范围为0-255。

角度值angle[]是uint16_t型的,而实际的范围为0-45,根据65536/45=1456,可以用Q12的表示方式来提高分辨率。

查找表如下:

static const uint16_t tb_atan[256]={

0, 229, 458, 687, 916, 1145, 1374, 1603,

1832, 2061, 2290, 2519, 2748, 2976, 3205, 3433,

3662, 3890, 4118, 4346, 4574, 4802, 5029, 5257,

5484, 5711, 5938, 6165, 6391, 6618, 6844, 7070,

7296, 7521, 7746, 7971, 8196, 8421, 8645, 8869,

9093, 9317, 9540, 9763, 9986, 10208, 10431, 10652,

10874, 11095, 11316, 11537, 11757, 11977, 12197, 12416,

12635, 12853, 13071, 13289, 13507, 13724, 13940, 14157,

14373, 14588, 14803, 15018, 15232, 15446, 15660, 15873,

16085, 16297, 16509, 16720, 16931, 17142, 17352, 17561,

17770, 17979, 18187, 18394, 18601, 18808, 19014, 19220,

19425, 19630, 19834, 20038, 20241, 20444, 20646, 20848,

21049, 21250, 21450, 21649, 21848, 22047, 22245, 22443,

22640, 22836, 23032, 23227, 23422, 23616, 23810, 24003,

24196, 24388, 24580, 24771, 24961, 25151, 25340, 25529,

25717, 25905, 26092, 26278, 26464, 26649, 26834, 27018,

27202, 27385, 27568, 27750, 27931, 28112, 28292, 28471,

28650, 28829, 29007, 29184, 29361, 29537, 29712, 29887,

30062, 30236, 30409, 30582, 30754, 30925, 31096, 31266,

31436, 31605, 31774, 31942, 32109, 32276, 32442, 32608,

32773, 32938, 33101, 33265, 33428, 33590, 33751, 33913,

34073, 34233, 34392, 34551, 34709, 34867, 35024, 35180,

35336, 35492, 35646, 35801, 35954, 36107, 36260, 36412,

36563, 36714, 36864, 37014, 37163, 37312, 37460, 37607,

37754, 37901, 38047, 38192, 38337, 38481, 38624, 38768,

38910, 39052, 39194, 39335, 39475, 39615, 39754, 39893,

40032, 40169, 40307, 40443, 40580, 40715, 40850, 40985,

41119, 41253, 41386, 41519, 41651, 41782, 41913, 42044,

42174, 42303, 42432, 42561, 42689, 42817, 42944, 43070,

43196, 43322, 43447, 43572, 43696, 43819, 43943, 44065,

44188, 44309, 44431, 44551, 44672, 44792, 44911, 45030,

45148, 45266, 45384, 45501, 45618, 45734, 45849, 45965,

};

static int16_t calc_phase(int16_t real, int16_t imag)

{

int16_t

abs_real;

int16_t

abs_imag;

int16_t

phase;

uint16_t

zone;

uint32_t

tmp_tan;

uint32_t

delta_tan;

if (real

< 0)

{

abs_real=-real;

zone=1U;

}

else

{

abs_real=real;

zone=0U;

}

if (imag

< 0)

{

abs_imag=-imag;

zone +=2U;

}

else

{

abs_imag=imag;

zone=0U;

}

if (abs_imag

<=abs_real)

{

tmp_tan=(uint32_t)abs_imag<<8U;

tmp_tan /=abs_real;

phase=tb_atan[tmp_tan]>>3U;

}

else if

((abs_imag>>7U) <=abs_real)

{

delta_tan=((uint32_t)(abs_imag -

abs_real))<<15U;

tmp_tan=(uint32_t)abs_imag + (uint32_t)abs_real;

delta_tan /=tmp_tan;

delta_tan >>=7U;

phase=tb_atan[delta_tan]>>3U;

phase +=5760;

}

else

{

phase=11520;

}

switch

(zone)

{

case 0U:

{

break;

}

case 1U:

{

phase=23040 - phase;

break;

}

case 2U:

{

phase=-phase;

break;

}

case 3U:

{

phase=phase - 23040;

break;

}

default:

{

break;

}

}

return(phase);

}

辅助的python计算程序如下:

#!/usr/bin/python

# -*- coding: utf-8 -*-

from math import sin

from math import cos

from math import tan

from math import atan

from math import pi

'''

#验证FFT和相角计算的程序

wave=[]

Fai=17.5 / 180 * 3.1415926

for i in range(128):

base=cos(3.1415926*2*i/128 + Fai)

# harm=0.07*cos(3.1415926*11*2*i/128)

harm=0

wave.append((int)(32768*(base+harm)))

# 格式化输出

print "static const int16_t vol[128]={"

for i in range(128):

print "%d,"

%wave[i],

if i%8==7:

print

print "};"

# print ","

,round(cos(3.1415926*2*i/128),4)

'''

'''

for i in range(142,180):

print i/2.0,

tan(i * pi / 360)

'''

val_tan=[]

num=256

for i in range(num):

val_tan.append((atan(i * 1.0 / num) * 180 / pi) )

# 格式化输出

print "static const uint16_t arctan[%d]={" % num

for i in range(num):

print "%f,"

%val_tan[i],

if i%8==7:

print

print "};"

版权声明

本文仅代表作者观点,不代表百度立场。

本文系作者授权百度百家发表,未经许可,不得转载。

分享:

扫一扫在手机阅读、分享本文

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐