• 一个简单的波形包络提取算法


    最近做了一个项目来提取语音信号包络波形,因此,花一些时间来研究各种包络提取算法。

    所谓包络检测被称为振幅解调,在许多领域都有重要的应用。它假设载波信号被确定,所以,通常的方式同步解调,优选以这样的方式的SNR,混合信号噪声抑制最强。的叫法。在信号检測领域,这样的方式通常称为“相敏检波”。锁相放大器(Lock-in Amplifier)就是这样的方式最典型的样例。

    假设载波比較乱。就像我如今的应用场景。要提取噪声的幅度随时间变化的规律,那么包络检波法会更适宜。我这里的代码就是採用的包络检波法。

    包络检波法的基本原理能够看以下这个电路图。这个是最主要的半波包络检波。

    当 Ui(t) > Uo(t-) 时 Uo(t) = Ui(t)

    当 Ui(t) < Uo(t-) 

    RC dUo/dt = Uo

    化成差分方程为:

    把这个过程用程序来实现就有了以下的代码。

    /**
     * 包络检波。模拟了硬件半波检波的过程
     * rc = 0 时初始化
     **/
    double env_1(double x, double rct)
    {
        static double old_y = 0.0;
        if(rct == 0.0)
        {
            old_y = 0.0;
        }
        else
        {
            if(x > old_y)
            {
                old_y = x;
            }
            else
            {
                old_y *= rct / ( rct + 1 );
            }
        }
        return old_y;
    }
    
    void env_2(double x[], double y[], int N, double rct)
    {
        double xx = 0.0;
        int i;
        y[0] = fabs(x[0]);
        for(i = 1; i < N; i++)
        {
            if( x[i] > y[i-1])
            {
                y[i] = x[i];
            }
            else
            {
                y[i] = y[i-1] * rct / ( rct + 1 );
            }
        }
    }
    

    上面是半波检測的代码,仅仅要略微添加几行。就能实现全波检測。

    /**
     * 包络检波,模拟了硬件全波检波的过程
     * rc = 0 时初始化
     **/
    double env_3(double x, double rct)
    {
        static double old_y = 0.0;
        if(rct == 0.0)
        {
            old_y = 0.0;
        }
        else
        {
            x = fabs(x);
            if(x > old_y)
            {
                old_y = x;
            }
            else
            {
                old_y *= rct / ( rct + 1 );
            }
        }
        return old_y;
    }
    void env_4(double x[], double y[], int N, double rct)
    {
        double xx = 0.0;
        int i;
        y[0] = fabs(x[0]);
        for(i = 1; i < N; i++)
        {
            xx = fabs(x[i]);
            if( xx > y[i-1])
            {
                y[i] = xx;
            }
            else
            {
                y[i] = y[i-1] * rct / ( rct + 1 );
            }
        }
    }

    这个代码中有个參数 rct,相应的是硬件电路中的RC时间常数,要依据待检測的包络信号的频带来确定。

    以下是用这个代码实际提取包络的算例。能够看出这个代码的效果还是蛮不错的。

    (比採用Hilbert 变换得到的结果还要好)

    以下是C++的代码,功能同样。


    /**
     * 包络检波功能,模拟了硬件半波检波和全波检波功能
     */
    class env_detect
    {
    private:
        double m_rct;
        double m_old;
    public:
        env_detect(){m_rct = 100.0, m_old = 0.0;};
        void init(double rct);
        double env_half(double in);
        void env_half(double in[], double out[], int N);
        double env_full(double in);
        void env_full(double in[], double out[], int N);
    };
    
    
    /** rief 初始化
     *
     * param rct 为RC低通滤波的时间常数
     * 
    eturn
     */
    void env_detect::init(double rct)
    {
        m_rct = rct;
        m_old = 0.0;
    }
    
     /** rief 半波包络检測
      *
      * param in 输入波形。每次传入一个数据点
      * 
    eturn 输出波形
      */
    double env_detect::env_half(double in)
    {
        if(in > m_old)
        {
            m_old = in;
        }
        else
        {
            m_old *= m_rct / ( m_rct + 1 );
        }
        return m_old;
    }
     /** rief 半波包络检測
      *
      * param in[] 输入波形
      * param N 数组的点数
      * param out[] 输出波形
      * 
    eturn
      */
    void env_detect::env_half(double in[], double out[], int N)
    {
        for(int i = 0; i < N; i++)
        {
            if( in[i] > m_old)
            {
                m_old = in[i];
                out[i] = m_old;
            }
            else
            {
                m_old *= m_rct / ( m_rct + 1 );
                out[i] = m_old;
            }
        }
    }
    
    /** rief 全波包络检測
     *
     * param in 输入波形,每次传入一个数据点
     * 
    eturn 输出波形
     */
    double env_detect::env_full(double in)
    {
        double abs_in = fabs(in);
        if(abs_in > m_old)
        {
            m_old = abs_in;
        }
        else
        {
            m_old *= m_rct / ( m_rct + 1 );
        }
        return m_old;
    }
     /** rief 全波包络检測
      *
      * param in[] 输入波形
      * param N 数组的点数
      * param out[] 输出波形
      * 
    eturn
      */
    void env_detect::env_full(double in[], double out[], int N)
    {
        double abs_in;
        for(int i = 0; i < N; i++)
        {
            abs_in = fabs(in[i]);
            if( abs_in > m_old)
            {
                m_old = abs_in;
                out[i] = m_old;
            }
            else
            {
                m_old *= m_rct / ( m_rct + 1 );
                out[i] = m_old;
            }
        }
    }


  • 相关阅读:
    推荐一套 Spring Boot 快速开发框架,接私活、练手必备!
    C# 三种字节数组(byte[])拼接的性能对比测试
    C#//字节数组转16进制字符串
    C# 16进制与字符串、字节数组之间的转换
    linux脚本学习
    ubuntu12.04纪事
    linux常用命令
    linshi
    2022壬寅年天干四化
    码农们来一起讨论下数据库设计....
  • 原文地址:https://www.cnblogs.com/zfyouxi/p/4593116.html
Copyright © 2020-2023  润新知