直接数字频率合成技术及其C++的实现

DDFS-Direct Digital Frequency Synthesizer 直接数字频率合成技术可以用来产生任意波形的周期信号。所谓的DDFS简单的说是查表法,内部维护一个Lookup Table储存一个周期的波形。那么这个查找表其实就是给出了相位到函数值的一个映射关系:

这里相位ω又是时间t的线性函数。

而调整输出频率实际上就是调整相位函数的系数kΔ。

举个具体的例子,比如我们的查找表存储的是正弦波的一个周期。查找表的长度为1024。正弦波的相位是以2π为周期的,那么查找表相邻的两项间的相位差为。也就是说查找表的相位分别为:

需要注意的是最后一个元素对应的相位并不是2π,因为相位与相位0其实是相同的,查找表中只能出现一次。

我们还需要一个相位累加器和一个频率寄存器,相位累加器用来记录当前的相位值是多少。比如说我们用一个16位无符号整数寄存器来记录这个相位值,那么0到65537(注意这里是65537)就对应0到2π这个相位周期。相位随时间变化是线性的,所以我们每次只需将相位累加器累加一个固定的相位就可以了。累加的这个值就存在频率寄存器中,比如频率寄存器中的值为5,那么相位累加器的值依次为:0,5,10,15,20,...,65530,65535,3,8,13,...。这里超过65536的值比如65537自动溢出成为0,刚好利用到了整型变量的这种周期性。

频率寄存器的值设为多少合适呢,如果我们的采样频率为 Fs,我们希望的输出频率为f。查找表的长度为M,相位累加器的长度为N,频率寄存器的值为k。那么相位累加器的最小相位变化量为。输出波形一个周期有Fs/f 个数据点。数据点间的相位差为。那么有如下等式:

实际计算出的k值不一定是个整数,四舍五入就可以了。举个具体的例子:采样频率为1kHz,我们希望输出10Hz的正弦波,相位累加器的长度为65536。那么频率寄存器的值应为

可以看出,相位累加器的长度越长,频率分辨率就越高。因此实际使用时相位累加器通常会设计为32位或更高位数。查找表的长度通常就短很多,一般都是10位或12位。这时只需将相位累加器的最高几位作为查找表的指标就可以了。

直接数字频率合成技术并不仅仅是用于硬件生成特定函数波形,在数值计算领域,也可以采用这项技术。还是以生成正弦波为例。虽然大多数编程语言中都提供了sin()和cos()函数,但是这个函数的计算速度相对还是较慢的。如果你的程序中需要调用几百万次,那么花费的时间还是很可观的。另外,随着t的增加,sin(t)的计算精度也会下降。这时,采用DDS技术的优势就体现出来了。首先查表法保证它的速度比任何的现有的三角函数计算代码都要快的多,其次,所有的运算都是整数运算保证了不存在任何累计计算误差。

下面是实现代码,可以生成多种周期函数波形:

#ifndef DDS_H_INCLUDED
#define DDS_H_INCLUDED

class LookupTable
{
    friend class DDS;
private:
    unsigned int m_length;
    unsigned int m_shift;
    double *m_table;
public:
    LookupTable();
    LookupTable(unsigned int N);
    ~LookupTable();
    void sine(double amplitude = 1.0, double baseline = 0.0);
    void rect(double dutycircle = 0.5, double amplitude = 1.0, double baseline = 0.0);
    void sawtooth(double dutycircle = 0.5, double amplitude = 1.0, double baseline = 0.0);
    void arbitrary(double table[]);
};

class DDS
{
private:
    LookupTable *m_pLut;
    unsigned int m_iter;
    unsigned int m_stride;
    unsigned int m_phase;
public:
    DDS(LookupTable *pLut, unsigned int stride = 1, unsigned int phase = 0);
    void setPhase(double phase = 0.0);
    void setFreq(double freq);
    void setFreq(double freq, double fs);
    inline double value();
};
inline double DDS::value(void)
{
    unsigned int index = (m_iter + m_phase);
    index = index >> m_pLut->m_shift;
    double value = m_pLut->m_table[index];
    m_iter += m_stride;
    return value;
}
#endif // DDS_H_INCLUDED
#include "dds.h"
#include <cstdlib>
#include <cmath>
LookupTable::LookupTable()
{
    m_length = 0;
    m_table = NULL;
}
LookupTable::~LookupTable()
{
    delete[] m_table;
}
LookupTable::LookupTable(unsigned int N)
{
    if(N < 1)
    {
        N = 1;
    }
    if(N > 16)
    {
        N = 16;
    }
    m_length = 0x01 << N;
    m_shift = 32 - N;
    m_table = new double[m_length];
}

void LookupTable::sine(double amplitude, double baseline)
{
    if(m_length != 0)
    {
        for(unsigned int i = 0; i < m_length; i++)
        {
            m_table[i] = amplitude * sin(2 * M_PI / m_length * i) + baseline;
        }
    }
}

void LookupTable::rect(double dutycircle, double amplitude, double baseline)
{
    if(m_length != 0)
    {
        for(unsigned int i = 0; i < m_length; i++)
        {
             double value = (1.0 * i / m_length < dutycircle) ? 0 : 1;
             m_table[i] = amplitude * value + baseline;
        }
    }
}

void LookupTable::sawtooth(double dutycircle, double amplitude, double baseline)
{
    double pos, value;
    if(m_length != 0)
    {
        for(unsigned int i = 0; i < m_length; i++)
        {
            pos = 1.0 * i / m_length;
            if(pos < dutycircle)
            {
                value = pos / dutycircle;
            }
            else
            {
                value = (1.0 - pos) / (1.0 - dutycircle);
            }
            m_table[i] = amplitude * value + baseline;
        }
    }
}
void LookupTable::arbitrary(double table[])
{
    if(m_length != 0)
    {
        for(unsigned int i = 0; i < m_length; i++)
        {
            m_table[i] = table[i];
        }
    }
}
DDS::DDS(LookupTable *pLut, unsigned int stride, unsigned int phase)
{
    m_iter = 0;
    m_pLut = pLut;
    m_stride = stride;
    m_phase = phase;
}
void DDS::setPhase(double phase)
{
    m_phase = round(phase /(2 * M_PI) * 4294967296.00);
}
void DDS::setFreq(double omega)
{
    double stride = omega / (2 *M_PI) * 4294967296.00;
    m_stride = round(stride);
}
void DDS::setFreq(double freq, double fs)
{
    double omega = 2 *M_PI *freq / fs;
    setFreq(omega);
}
#include <iostream>
#include "dds.h"
using namespace std;

int main()
{
    LookupTable sincos(10);
    LookupTable sawtooth(10);
    sincos.sine(2, 0);
    sawtooth.sawtooth(0.3, 1, 0);
    DDS dds1(&sincos);
    DDS dds2(&sincos);
    DDS dds3(&sawtooth);
    dds1.setFreq(10, 1000);
    dds1.setPhase(0.0);
    dds2.setPhase(3.1415926/2);
    dds2.setFreq(10, 1000);
    dds3.setFreq(10, 1000);
    for(int i =0; i <200; i++)
    {
        cout << i << ", " << dds1.value() << ", " ;
        cout << dds2.value() << ", " << dds3.value() << endl;
    }
    return 0;
}

将输出结果绘出图形如下:

希望这个代码对大家有用。

时间: 2024-08-05 11:38:34

直接数字频率合成技术及其C++的实现的相关文章

采用DDS(数字频率合成法)设计信号发生器

§2.1设计指导思想 用大规模CPLD设计多功能信号发生器,要求能够输出方波.锯齿波.三角波.正弦波. 具体是用VHDL硬件描述语言编写多功能信号发生器程序,经过编译.仿真,再下载到CPLD器件上,再经数模转换器输出各类波形. 1.CPLD(COMPLEX Programmable Logic Device,复杂可编程逻辑器件)属于最具有代表性的IC芯片之一.CPLD基本上是由多个SPLD(SIMPLE PLD)在单片上的集成,集成度高,可以实现比较复杂的电路或系统.CPLD的优点是其结构的规则

模拟频率与数字频率

在数字信号处理的学习中,很多刚入门朋友常常为模拟频率.数字频率及其相互之间的关系所迷惑,甚至是一些已经对数字信号处理有所了解的朋友也为这个问题所困惑. 我们通常所说的频率,在没有特别指明的情况下,指的是模拟频率,其单位为赫兹(Hz),或者为1/秒(1/s),数学符号用f来表示.这是因为现实世界中的信号大多为模拟信号,频率是其重要的物理特性.以赫兹表示的模拟频率表示的是每秒时间内信号变化的周期数.如果用单位圆表示的话,如图1所示,旋转一圈表示信号变化一个周期,则模拟频率则指的是每秒时间内信号旋转的

信号处理中数字频率和模拟频率简明讲解

数字频率和模拟频率 在数字信号处理的学习中,很多刚入门朋友常常为模拟频率.数字频率及其相互之间的关系所迷惑,甚至是一些已经对数字信号处理有所了解的朋友也为这个问题所困惑. 我们通常所说的频率,在没有特别指明的情况下,指的是模拟频率,其单位为赫兹(Hz),或者为1/秒(1/s),数学符号用f来表示.这是因为现实世界中的信号大多为模拟信号,频率是其重要的物理特性.以赫兹表示的模拟频率表示的是每秒时间内信号变化的周期数.如果用单位圆表示的话,如图1所示,旋转一圈表示信号变化一个周期,则模拟频率则指的是

[Matlab]频率f,角频率Ω和数字频率w的物理含义

需要结合 http://anony3721.blog.163.com/blog/static/51197420111129503233/ 或者 https://blog.csdn.net/xiaoyanwin/article/details/15420707 食用. %信号处理中的各种频率 %freqs.m %MatlabR2015b %2018年6月4日 09:46:34 clear; close all; clc; %模拟角频率 Omega: rad/s %物理意义:在2*pi的时间段里面包

2016华为上机题二(数字频率)

2.统计数字字符串中数字的出现频率,输入字符串最多有255个,字符串中只有数字字符,没有其它字符.统计其中出现次数最多的字符,当出现次数相当时,则输出较小的字符.输出格式为:字符+,+次数. 例如 412444 4,3 1 #include <iostream> 2 #include <stdio.h> 3 #include <cmath> 4 #include <string> 5 #include <algorithm> 6 #include

关于2020年北京市数字编辑专业技术职称评定 (初级、中级)备考系统的使用通知

各在京出版社.期刊社.新媒体单位.网站: 北京市人力资源和社会保障局.原北京市新闻出版广电局于2015年11月12日联合发布了数字编辑职称评定工作,并连续举办4年.近年全国出版业都在加大数字化转型,此考试也正是顺应该趋势,配合各单位建设标准的数字化队伍.2016年网络编辑师国家职业资格鉴定证书停办,也促成数字编辑成为“网络编辑”必要的证书.数字编辑考试适合在北京工作的传统编辑人员.网络编辑人员.在公司任职技术人员.运营人员参加,通过获得数字编辑证书及时做好自身转型工作. 据中国新闻出版广电报报道

第三次电子测量作业BGD150206220

信号发生器是一种用于产生多种波形信号的仪器,在电子信息行业的设计.生产过程中有着广泛的应用.直接数字频率合成(DDS)技术是一种采用数字化的方法合成所需频率信号的先进的电路设计方案,用这种技术设计的信号发生器具有控制灵活.频率分辨率高.相位连续.切换速度快.输出相位噪声低和可以产生任意波形等优点.频率合成信号发生器是科研.教学实验及各种电子测量技术中很重要的一种信号源,随着科学技术的迅速发展,对信号源的要求也越来越高,要求信号源的频率稳定度.准确度及分辨率要高,以适应各种高精度的测量,为了满足这

基于FPGA的DDS任意波形发生器设计

一.简介 DDS技术最初是作为频率合成技术提出的,由于其易于控制,相位连续,输出频率稳定度高,分辨率高, 频率转换速度快等优点,现在被广泛应用于任意波形发生器(AWG).基于DDS技术的任意波形发生器用高速存储器作为查找表,通过高速D/A转换器来合成出存储在存储器内的波形.所以它不仅能产生正弦.余弦.方波.三角波和锯齿波等常见波形,而且还可以利用各种编辑手段,产生传统函数发生器所不能产生的真正意义上的任意波形. 二.原理 根据傅立叶变换定理可知,任何周期信号都可以分解为一系列正弦或余弦信号之和,

【小梅哥FPGA进阶教程】第九章 基于串口猎人软件的串口示波器

九.基于串口猎人软件的串口示波器 1.实验介绍 本实验,为芯航线开发板的综合实验,该实验利用芯航线开发板上的ADC.独立按键.UART等外设,搭建了一个具备丰富功能的数据采集卡,芯航线开发板负责进行数据的采集并将数据通过串口发送到PC机上,PC端,利用强大的串口调试工具--串口猎人,来实现数据的接收分析,并将数据分别以波形.码表.柱状图的形式动态显示出来,以让使用者能够直观的看到ADC采集到的信号细节.同时,用户也可以使用串口猎人通过串口给下位机(FPGA)发送指令,下位机将对接收到的指令进行解