【问题标题】:Sine Table Interpolation正弦表插值
【发布时间】:2010-11-12 23:08:37
【问题描述】:

我想组装一个 SDR 系统,该系统最初可以调谐 AM,然后是 FM 等。 我计划用来执行此操作的系统将有一个用于直接数字合成 (DDS) 的正弦查找表。 为了正确调谐,我希望能够精确控制馈送到混频器(在这种情况下为乘数)的正弦波的频率。我预计线性插值会很接近,但认为非线性方法会提供更好的结果。

什么是用于正弦表的良好且快速的插值方法。目标系统上的乘法和加法很便宜;分割成本很高。

编辑: 我计划使用乘法/移位函数来实现常量,以将常量标准化为缩放整数。中间值将使用宽加,乘法将使用 18 或 17 位。可以使用浮点“预计算”,但不能在目标平台上使用。当我说“除法代价高昂”时,我的意思是它必须使用乘数和大量代码来实现。这不是不可想象的,但应该避免。但是,真正的浮点 IEEE 方法会占用该平台上的大量资源以及自定义实现。

任何 SDR 经验都会有所帮助。

【问题讨论】:

  • 您评估过线性插值吗?在 PC 上运行一个通过预期输入运行的快速程序应该可以为您提供任何建议解决方案的错误幅度。
  • 我没做过DDS,但是做过FM和相位失真合成。我刚刚读了一点关于 DDS 的内容。听起来好像经常使用线性插值,但我认为你可能会使用比线性更好的东西来降低本底噪声。当您开始实施时,您应该对输出进行 FFT,并将已知波通过输入,以查看您的本底噪声是多少以及是否有任何伪影。

标签: signal-processing trigonometry interpolation radio nonlinear-functions


【解决方案1】:

正弦表中的插值实际上是重新采样。显然,您只需调用sin 即可获得完美的结果,因此无论您的解决方案是什么,它都需要超越它。对于固定滤波器重采样,您仍然只有一组固定的可用点(3:1 上采样器意味着您在表中的每个点之间将有 2 个新点可用)。目标系统上的内存有多贵?我的主要建议是简单地提高表格分辨率并使用线性插值。您将获得与更小表和简单上采样相同的结果,但计算开销更少。

【讨论】:

  • 不幸的是,我不会调用“sin”函数。我可能会在我的 PC 上编写一个 C 程序来为我生成正弦表,但目标上没有 C lib。内存并不便宜,我可以用逻辑来换取快速内存,但它是有限的系统资源。只要它不会太疯狂,我应该能够将数学流水线化。
【解决方案2】:

如果你用线性插值没有得到很好的结果,你可以试试三角关系。

和差公式

sin(A+B)=sinA*cosB + cosA*sinB
sin(A-B)=sinA*cosB - cosA*sinB
cos(A+B)=cosA*cosB - sinA*sinB
cos(A-B)=cosA*cosB + sinA*sinB

您可以预先计算 A、B 范围的 sin 和 cos 值,即

A range: 0, 10, 20, ... 90
B range: 0.01 ... 0.99

【讨论】:

  • 这听起来不错。我需要想一想。我正计划使用 FPGA 平台并将其编码为 VHDL,因此我喜欢这样一个事实,即它只能使用乘法器和全加器。我必须编写一些测试代码,看看它是否能让我到达我想去的地方。
  • 要么 A 范围需要以 1 度为步长,要么 B 范围需要扩展到 9.99 - 或者您必须多次重复操作才能达到正弦 (27.5)。
  • @Jonathan Leffler 我认为你是对的,但我明白了。使用 18 位乘法器,这一点达到零交叉需要 5 个表条目从 1 到 -1,这是曲线的最陡峭部分,有 2 个 1k 表(有效地在查找时提供 20 位分辨率,这很快使用 CLB 作为 Mini ROM。)。数学方法需要更多的乘法器和加法器,并且不会让我在分辨率范围内更接近以增加复杂性。
【解决方案3】:

平滑函数的表插值 = ick hurl bleah。恕我直言,我只会在一些非常奇怪的函数上使用表插值,或者在绝对需要确保避免不连续的地方(请注意,插值表的导数是不连续的)。当您完成表查找和所需的插值代码时,您可能已经计算了一两个多项式,至少如果乘法不会导致您太烧心的话。

恕我直言,您最好对正弦波形的每个段(例如 -90 到 +90 度,或 -45 到 +45 度,然后是其他相同宽度的段)使用 Chebyshev approximation,然后选择将您的错误减少到所需值的最小次数多项式。如果段足够小,您可以使用二次甚至线性多项式;在准确性、分段数和多项式次数之间需要权衡取舍。

my post in this other question,它会省去你计算系数的麻烦(至少如果你相信我的数学的话)。

(编辑:如果不清楚,您可以在设计时在您最喜欢的高性能 PC 上进行切比雪夫近似,这样在运行时您就可以使用一个简单的微控制器或 FPGA 或其他任何东西1-4 次多项式。除非您知道自己在做什么,否则不要超过 4 次,3 或以下会更好。)

【讨论】:

  • 实际上,我仍然不太了解使用此算法的预计算 PC/嵌入式系统断点在哪里。看来这将花费 6 个乘数。我将不得不比较尼克。对于 18 位乘法器,D 的答案对此的表现。我将使用一个穷人的 FP,通过硬连线位移来缩放常量。
【解决方案4】:

您是否考虑过将泰勒级数用于三角函数(找到 here)?这涉及乘法和除法,但取决于您的数字的表示方式,您可以将除法转换为乘法(如果您非常幸运,也可以转换为位移)。您可以根据需要计算该系列的任意数量的项,并以这种方式获得您的精度。

或者,如果这个正弦波在某个时候将成为模拟信号,那么您可以使用查找表方法并使用模拟滤波器从生成的波形中去除采样频率。如果您的采样频率是正弦频率的 100 倍,则很容易去除。您需要一个可变过滤器来执行此操作。我从来没有做过这样的事情,但我知道有数字电位器可以采用二进制数并改变它们的电阻。这可能是可变 RC 滤波器的基础——可能带有一些用于增益的运算放大器等。

祝你好运!

【讨论】:

  • 泰勒到达那里的速度太慢了,IMO。过滤改变了阶段。
  • 除法不是不可想象的,我自己也想过,但是在这个平台上它非常昂贵,应该保留给无法以其他方式完成的应用程序
  • 泰勒级数不使用除法...并且在大多数情况下应避免使用切比雪夫近似,因为它们保证了收敛和有意均匀分布的误差。
【解决方案5】:

为什么是桌子?当信号为 -20db 时,This very fast function 的最差噪声峰值为 -90db。真是太好了。

对于音频的重采样,我总是使用 Elephant 论文中的一种插值器。这在previous SO question 中进行了讨论。

如果你使用的处理器没有 fp,你仍然可以做这些事情,但它们更难。我去过那儿。我感觉到你的痛苦。祝你好运!我曾经为了好玩而将 fp 转换为整数,但现在你必须付钱给我。 :-)


适用于您的问题的超酷在线参考资料:

http://www.audiomulch.com/~rossb/code/sinusoids/

http://www.dattalo.com/technical/theory/sinewave.html


编辑:基于您的 cmets 的其他想法

由于您正在开发一个棘手的处理器,也许您应该研究如何使您的正弦表有更多的角度可供查看,但仍然保持较小。

假设您将一个象限分成 90 块(实际上,您可能会使用 256 块,但为了熟悉和清晰,我们将其保留为 90 块)。将它们编码为 16 位。到目前为止,这是 180 字节的表。

现在,对于这些度数中的每一个,我们将有 9 个(实际上可能是 8 或 16)个中间点。

我们以3度到4度的范围为例。

sin(3)=0.052335956 //this will be in your table as a 16-bit number
sin(4)=0.069756474 //this will be in your table as a 16-bit number

所以我们要看看 sin(3.1)

sin(3.1)=0.054978813 //we're going to be tricky and store the result
                     // in 8 bits as a percentage of the distance between
                     // sin(3) and sin(4)

您要做的是弄清楚 sin(3.1) 如何介于 sin(3) 和 sin(4) 之间。如果介于两者之间,则将其编码为 128 的字节。如果介于两者之间的四分之一,则将其编码为 64。

那是额外的 90 个字节,您在 16 位分辨率中仅用 180+90*9 个字节编码到十分之一度。您可以根据需要进行扩展(可能达到 32 位角度和 16 位补间角度)并在两者之间快速线性插值。为了最大限度地减少存储空间,您可以利用连续值彼此接近这一事实。


编辑 2:在表格中编码中间角度的更好方法

我只记得当我这样做时,我最终非常紧凑地表达了根据线性插值的期望值与实际值之间的差异。这个错误总是在同一个方向。

我首先计算了范围内的最大误差,然后以此为基础。

工作得很好。我觉得我应该在博客条目中编写代码来说明。 :-)

【讨论】:

  • @Nosredna:我认为你遗漏了一些东西:这 90 个字节中的每一个字节都不同;您必须为您的方案添加 90bytes x 90pieces = 8100bytes。
  • (您可以按照 Nick D 的建议使用角度和/差公式)
  • 是的!我搞砸了。修复。
【解决方案6】:

人们编写了一些非常聪明的代码,用于在内存很小的系统上快速计算 sin(),这些系统甚至没有硬件乘法指令,更不用说除法指令了。

按照复杂度递增的顺序:

  • 使用方波。许多 AM 收音机在其ring demodulator 中使用方波,我不明白为什么您的 AM 解调器需要更复杂的东西。

  • 通过在每个四分之一周期 256 个值的原始表中查找“最接近的值”来近似 sin()。是的,您会看到看起来很可怕的楼梯,但是(通过一点模拟过滤)这通常效果很好。 (事实上​​,这通常是多余的,一张短得多的表格就足够了)。

  • 通过在原始表中查找 2 个最接近的值并在它们之间进行线性插值来近似 sin()。

  • 对于 sin(x),每四分之一周期具有 16 个短的、等间距的 x 三次样条的近似 sin()“提供优于 16 位的精度”。

Wikibooks: Fixed-Point Numbers 链接到最后 3 个的一些巧妙实现。

【讨论】:

    猜你喜欢
    • 2015-05-08
    • 2021-06-04
    • 1970-01-01
    • 1970-01-01
    • 2010-11-25
    • 1970-01-01
    • 1970-01-01
    • 2015-06-03
    • 1970-01-01
    相关资源
    最近更新 更多