滤除不需要的方波(无线电:DCS/DPL 信号)

信息处理 过滤器 声音的 无限脉冲响应 收音机
2022-01-27 21:36:55

我需要从录音中删除 DPL/DCS 信号。粗略地说,DCS 信号是一种低频 (67.15Hz) 方波,它与语音在同一载波上同时发送。因为它是方波,它有许多与语音重叠的强谐波。显然,必须保留语音。

从表面上看,这似乎是一个可以像消除 60Hz 电源线噪声一样解决的问题:大量的陷波滤波器。但是,电源线噪声往往较弱,并且来自更接近正弦波的源。

所以,我的问题是:有没有更好的方法来做到这一点?

此链接包含有关 DCS 的更多信息:http: //onfreq.com/syntorx/dcs.html 它提到“由于 DCS 产生远高于 300 Hz 的音频谐波(即进入从 300 到 3000 Hz 的频带的可听部分),收音机必须有良好的过滤器来去除不需要的 DCS 噪声。” 哈!

编辑:DCS 不是“方波”。我认为解释它的正确方法是说它是一个时钟频率为 67.15 Hz的NRZ二进制代码。

3个回答

如果您知道它是方波,并且频率大致恒定,我会考虑合成它的反转副本并将其添加到信号中。其余部分将是您的语音,加上方波频率的一些残余谐波,这可能由 1 通道自适应滤波器处理(请参阅此链接的第二节 IIC:http ://www.cs.cmu.edu/ 〜aarti/pubs/ANC.pdf)。自适应滤波器有可能自己完成所有工作——我在周期性干扰信号上使用它们时效果很好,并注意所涉及的延迟设置。为了获得反相信号的参数,您将执行以下两项操作之一:
1. 使用最小二乘法最小化将两参数方波拟合到信号(两个参数是初始时间延迟和幅度,或添加您认为合适的其他参数)。2. 使用 FFT 估计基本正弦波的相位和幅度,然后使用方波幅度与其基波的理论比率(不是 2/pi 吗?)从中重建方波。

您选择这两种方法中的哪一种取决于语音的带宽及其相对于 DCS 信号的幅度。祝你好运!

DCS 不应作为方波传输。
发送端应该在 DCS 信号与发送音频混合之前对其进行低通滤波器。因此,功能正常的 DCS 发射器绝不应传输高于 300Hz 的 DCS 信号谐波。

接收端,你只需要一个截止频率为 300Hz 的(陡峭的)高通滤波器。

如果您仍然在接收到的音频中从 DCS 信号中获得谐波,那么您应该查看发送端。它要么过度调制 DCS(电平太高),要么发送侧低通滤波器无效。您也可能两者兼有 - DCS 调制级别设置得太高,滤波器无法胜任工作。

鉴于您有一个包含 DCS 的录音,我会假设录音是由无线电解调器的线路电平输出制成的。如果发射机工作正常,您需要做的就是通过一个截止频率为 300 Hz 的高通滤波器运行您的录音。

我已经编写了软件,它将从音频流中删除一组频率,在 c++ 中,我用它来从其他信号中删除 60 Hz 电源线污染(基本上是 60 Hz 方波),这可能很有用。源代码包含在下面。

笔记:

  • 包含的代码是用文本编辑器从较大的东西中剪下来的。也许缺少一两行?

  • 我在 Ubuntu 15.04 上运行它。这个特殊的程序从线路输入或麦克风输入中吸收音频。右声道被复制到右声道输出未更改。左声道将前 50 个偶次谐波减去并复制并复制到左声道输出。即,去除 600、180、300、...、3060 Hz 的频率。每个频率的陷波滤波器的宽度约为 1 Hz。如果您通过左声道运行信号发生器,您会注意到每个陷波几赫兹内的一些失真。为了好玩,我将 FM 收音机的输出放入线路输入并插入耳机,考虑到正在进行的处理量,左声道的失真并没有那么糟糕。

  • 需要jackd和开发库:

    sudo apt-get install jackd

    sudo apt-get install libjack-dev

  • 出于某种奇怪的原因,你必须做两件疯狂的事情......

编辑(通过 sudo gedit 或其他)/etc/security/limits.conf 以包含行 @audio - rtprio 99

如果运行程序的用户名是 XXXX,你必须这样做

sudo usermod -a G audio XXXX

  • 主程序使用常量 4096 作为一次吸入的数据块的样本大小。如果机器上的声卡无法处理此问题,或者机器速度较慢,请使用 1024。这应该始终有效。

那里。我希望这是足够独立的。


文件freqs.h

#ifndef FREQS_H_INCLUDED
#define FREQS_H_INCLUDED

#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <math.h>

class freq_info
{public:
double      Hz;     // The frequency of interest
double      w;      // 2 * pi * Hz
double      dt;     // delta time; time between samples
int         npts;       // number of points
double      tot_time;   // total time of all points
int         npts_1_cycle;
int         npts_max_cycles;
double      *cos_vals;
double      *sin_vals;
double      projection_sin;
double      projection_cos;

double      data_X_sin;
double      data_X_cos;
int         npts_in_product;

inline void zero() { memset( (void*)this, 0, sizeof(*this)); }
inline freq_info() { zero(); }
inline ~freq_info()
{
    if(cos_vals!=0) delete [] cos_vals;
    if(sin_vals!=0) delete [] sin_vals;
}

int init( double frequency, double time_between_samples, int number_of_points);

void projection_init();

void data_X_trigs_range( float *data, int data_idx_beg, int data_idx_end);

void projection_fini();
int  produce_projection ( float *data, unsigned int npts_in);
};

class freq_group
{public:

// Following 3 should be same for all frequencies of interest
double      dt;     // delta time; time between samples
int     npts;       // number of points
double      tot_time;   // total time of all points

int     min_npts_max_cycles;  // minimum of npts_max_cycles for all
                              //    FI[i] entries

double      info_freq_min;
double      info_freq_max;

int     n_freqs;    // # frequencies
freq_info   *FI;        // Info for all frequencies of interest.

inline void zero() { memset( (void*)this, 0, sizeof(*this)); }
inline freq_group() { zero(); }

inline ~freq_group()
{
   if(FI!=0) delete [] FI;
}

void set_time_and_points(double time_between_samples, int number_of_points);

int setup_freqs( double * freqs_of_interest, int n_freqs_of_interest);

int remove_projections1(float *data_in, float *data_out, unsigned int npts_in);

};

文件jack_interface.h

#ifndef JACK_INTERFACE_H_INCLUDED
#define JACK_INTERFACE_H_INCLUDED

#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <jack/jack.h>

//When we are finally running, this holds a bunch of info...
class jack_interface
{public:
    enum {max_channels=4};
    char            jack_interface_name[32];
    unsigned int    sample_size;
    unsigned int    samps_per_sec;
    jack_client_t   *jclient;
    int     n_channels;             // or, number of ports
    jack_port_t*    in_ports [max_channels];
    jack_port_t*    out_ports[max_channels];

inline void zero() { memset( (void*)this, 0, sizeof(*this) ); }
inline jack_interface() { zero(); }

int create( char*       interface_name,
        unsigned int    requested_sample_size,
        int     n_channels,
        int         (*process) (jack_nframes_t sample_size, void *arg) );
};
#endif JACK_INTERFACE_H_INCLUDED

文件freqs.cpp

#include "freqs.h"
inline void ZAP(char *msg=0)
{
    if(msg!=0) printf("%s\n", msg);
    printf("Fatal error.\n");
    exit(1);
}

void freq_info::projection_init()
{
    projection_sin  = 0.0;
    projection_cos  = 0.0;
    data_X_sin  = 0.0;
    data_X_cos  = 0.0;
    npts_in_product = 0;
    return;
}
void freq_info::data_X_trigs_range(float *data, int data_idx_beg, int data_idx_end)
{
    static  char    name[]  =   "freq_info::data_X_trigs_range(...)";
    int     i;

    if( data_idx_beg<0  || data_idx_end<0   || data_idx_end<data_idx_beg    ||  data_idx_end>=npts)

    {
        printf("%s\nInput ranges out of range.\n", name);
        ZAP();
    }
    for(i=data_idx_beg; i<=data_idx_end; i++)
    {
        data_X_sin += double( data[i] ) * sin_vals[i];
        data_X_cos += double( data[i] ) * cos_vals[i];
    }
    npts_in_product += data_idx_end - data_idx_beg + 1;
    return;
}

void freq_info::projection_fini()
{
    projection_sin = (2.0 * data_X_sin * dt) / ( double(npts_in_product-1) * dt );

    projection_cos = (2.0 * data_X_cos * dt) / ( double(npts_in_product-1) * dt );

    return;
}

void freq_group::set_time_and_points(double time_between_samples, int number_of_points)
{
    static  char    name[]  = "freq_group::set_time_and_points(...)";

    if(FI!=0) delete [] FI;
    zero();

    if(time_between_samples<=0.0 || number_of_points<8)
    {
        printf("%s\nInput parameters too small.\n", name);
        ZAP();
    }

    dt      = time_between_samples;
    npts        = number_of_points;
    tot_time    = double(npts-1) * dt;
   return;
} 

int freq_group::setup_freqs(double *freq_list, int n_freqs_in)
{
    static  char    name[]  = "freq_group::setup_freqs(...)";
    int     rval=0;
    int     i;
    double  freq_min, freq_max;

    n_freqs = n_freqs_in;

    if(freq_list==0 || n_freqs<1)
    {
        printf("%s\nInput args null, zero, or invalid.\n", name);
        rval = 10;
        goto EXIT_SEQ;
    }
    if( dt<=0.0 || npts<8 || tot_time<=0.0 )
    {
        printf("%s\nfreq_group::set_time_and_points NOT called!\n", name);
        rval = 20;
        goto EXIT_SEQ;
    }

    freq_min = 1.0e300;
    freq_max = 1.0e-300;
    for(i=0; i<n_freqs; i++)
    {
        if( freq_min > freq_list[i] )
            freq_min = freq_list[i];

        if( freq_max < freq_list[i] )
            freq_max = freq_list[i];
    }

    if(freq_min<=0.0 || freq_max<=0.0)
    {
        printf("%s\nSpecified frequency zero or negative.\n", name);
        rval = 30;
        goto EXIT_SEQ;
    }
    if( 1.0/freq_min > tot_time)
    {
        printf("%s\nSpecified frequency, %20.10e Hz, too low for time length of sample.\n", name, freq_min);
        rval = 40;
        goto EXIT_SEQ;
    }
    info_freq_min = freq_min;
    info_freq_max = freq_max;

    if( dt > 2.0 * (1.0 / freq_max) )
    {
        printf("%s\nSpecified frequence, %20.10e Hz, too high for sample rate.\n", name, freq_max);
        rval = 50;
        goto EXIT_SEQ;
    }
    if(FI != 0)
    {
        delete [] FI;
    }

    FI = new freq_info[ n_freqs ];
    min_npts_max_cycles = npts+2;
    for(i=0; i<n_freqs; i++)
    {
        rval = FI[i].init( freq_list[i], dt, npts);
        if(rval!=0)
        {
            printf("%s\nError from freq_info::init(...)\n", name);
            goto EXIT_SEQ;
        }
        if(min_npts_max_cycles > FI[i].npts_max_cycles)
        {
          min_npts_max_cycles = FI[i].npts_max_cycles;
        }
   }

   EXIT_SEQ:;
  return rval;
}

int freq_info::init( double frequency, double time_between_samples, int number_of_points)
{
    static  char    name[]  = "freq_info::init(...)";
    int     rval=0;
    static double two_pi = 0.0;

    int     i, max_cycles;
    double  arg;

    if(frequency<=0 || time_between_samples<0.0 || number_of_points<8)
    {
        printf("%s\nInput parameters too small.\n", name);
        rval = 10;
        goto EXIT_SEQ;
    }
    if(two_pi == 0.0)
    {
        two_pi = 8.0 * atan( 1.0 );
    }

    if(cos_vals!=0)
         delete [] cos_vals;

    if(sin_vals!=0)
         delete [] sin_vals;

    zero();

    Hz      = frequency;
    w       = frequency * two_pi;
    dt      = time_between_samples;
    npts        = number_of_points;
    tot_time    = dt * double(npts - 1);

    npts_1_cycle    = int(  (1.0/Hz) / time_between_samples  ) + 1;
    max_cycles = int( tot_time * Hz );
 // Total # full cycles of Hz in the time period
    if(max_cycles<1)
    {
        printf("%s\nSpecified frequency too low for the time span.\n", name);
        rval = 30;
        goto EXIT_SEQ;
    }

    npts_max_cycles = int(  (double(max_cycles)/Hz) / time_between_samples  ) + 1;

    cos_vals = new double[npts+1];
    sin_vals = new double[npts+1];

    for(i=0; i<npts; i++)
    {
        arg = w * double(i) * dt;
        sin_vals[i] = sin( arg );
        cos_vals[i] = cos( arg );
    }


EXIT_SEQ:;
return rval;
}

int  freq_info::produce_projection ( float *data_in, unsigned int npts_in)
{
    static  char    name[]  = "freq_group::produce_projection (...)";
    int     rval=0;
    if( int(npts_in) != npts)
    {
        printf("%s\nMismatch in # of data points.\n", name);
        rval = 10;
        goto EXIT_SEQ;
    }

   projection_init();
   data_X_trigs_range(data_in, 0, this->npts_max_cycles);
   projection_fini();

EXIT_SEQ:;
    return rval;
}

int freq_group::remove_projections1(float *data_in, float *data_out, unsigned int npts_in)
{
    static  char    name[]  = "freq_group::remove_projections1(...)";
    int     rval=0;
    int     i,j,beg,end;
    double  accum;

    if( int(npts_in) != npts)
    {
        printf("%s\nMismatch in # of data points.\n", name);
        rval = 10;
        goto EXIT_SEQ;
    }
    memset( (void*)data_out, 0, sizeof(*data_out) * npts_in);

    for(i=0; i<this->n_freqs; i++)
    {
        FI[i].projection_init();
    }
    for(i=0; i<this->n_freqs; i++)
    {
        FI[i].data_X_trigs_range(data_in, 0, this->min_npts_max_cycles);
    }
    for(i=0; i<this->n_freqs; i++)
    {
        if(FI[i].npts_max_cycles==min_npts_max_cycles)
            continue;
    // have not quite finished a full cycle at this frequency yet...
        beg = min_npts_max_cycles + 1;
        end = FI[i].npts_max_cycles;
        FI[i].data_X_trigs_range(data_in, beg, end);
    }

    for(i=0; i<this->n_freqs; i++)
   {
        FI[i].projection_fini();
   }

    // OK, have the projection of the data onto the various frequencies.
    // Remove it!
    for(i=0; i<npts; i++)
    {
        accum = 0.0;
        for(j=0; j<this->n_freqs; j++)
        {
            accum +=    FI[j].projection_sin * FI[j].sin_vals[i] +
                    FI[j].projection_cos * FI[j].cos_vals[i];
        }
        data_out[i] = float( double(data_in[i]) - accum );
    }

EXIT_SEQ:;
    return rval;
}

文件jack_interface.cpp

#include "jack_interface.h"

void jack_interface_shutdown (void *arg)

{
// Do nothing
   return;
}

int jack_interface::create( char*       interface_name,
                unsigned int    requested_sample_size,
                int     n_channels,
                int         (*process) (jack_nframes_t sample_size, void *arg) )
{
    static  char    name[]  = "jack_interface::create(...)";
    int     rval=0;
    const  char     **ports;
    static char     default_interface_name[] = "simple";
    const  char     *server_name = NULL;
    jack_options_t  options = JackNullOption;
    jack_status_t   status;
    char            *use_interface_name;
    unsigned int     use_sample_size;
    static  char    IN1[]   = "in1";
    static  char    IN2[]   = "in2";
    static  char    IN3[]   = "in3";
    static  char    IN4[]   = "in4";
    static  char    OUT1[]  = "out1";
    static  char    OUT2[]  = "out2";
    static  char    OUT3[]  = "out3";
    static  char    OUT4[]  = "out4";
    char*           names_in[max_channels];
    char*           names_out[max_channels];
    int     i;

    // Validate (somewhat) args, etc.
    if(interface_name==0 || interface_name[0]==0)
        use_interface_name = default_interface_name;
    else
        use_interface_name = interface_name;

    if(requested_sample_size==0)
        use_sample_size=1024;
    else
        use_sample_size = requested_sample_size;

    if(n_channels<1 || n_channels>max_channels)
    {
        printf("%s\nn_channels must be between 1 and %d.\n", name, max_channels);
        rval = 10;
        goto EXIT_SEQ;
    }
    if(process==0)
    {
        printf("%s\nprocess function is null.\n", name);
        rval = 20;
        goto EXIT_SEQ;
    }
//--------------------------------------------------------
// open a client connection to the JACK server
    jclient = jack_client_open (use_interface_name, options, &status, server_name);
    if (jclient == NULL)
    {
        printf("%s\njack_client_open() failed, status = 0x%2.0x\n", name, status);
        if (status & JackServerFailed)
        {
            printf ("Unable to connect to JACK server\n");
        }
        rval = 50;
        goto EXIT_SEQ;
    }
    jack_set_buffer_size(jclient, use_sample_size);
    sample_size = jack_get_buffer_size(jclient);
    samps_per_sec = jack_get_sample_rate (jclient);
    jack_set_process_callback (jclient, process, 0);
    jack_on_shutdown (jclient, jack_interface_shutdown, 0);
    names_in [0] = IN1;
    names_in [1] = IN2;
    names_in [2] = IN3;
    names_in [3] = IN4;
    names_out[0] = OUT1;
    names_out[1] = OUT2;
    names_out[2] = OUT3;
    names_out[3] = OUT4;

for(i=0; i<n_channels; i++)
{

    in_ports[i] = jack_port_register (jclient, names_in[i],
      JACK_DEFAULT_AUDIO_TYPE,
      JackPortIsInput, 0);

    if(in_ports[i] == NULL)
    {
        printf("%s\nCannot create jack input port.\n", name);
        rval = 100;
        goto EXIT_SEQ;

    }
}
for(i=0; i<n_channels; i++)
{
    out_ports[i] = jack_port_register (jclient, names_out[i],
       JACK_DEFAULT_AUDIO_TYPE,
        JackPortIsOutput, 0);

    if(out_ports[i] == NULL)
    {
        printf("%s\nCannot create jack output port.\n", name);
        rval = 110;
        goto EXIT_SEQ;
    }
}
// Tell the JACK server to start.  Our
// process() callback will start running now.

if (jack_activate (jclient))
{
    printf ("%s\ncannot activate jack client\n", name);
    rval = 120;
    goto EXIT_SEQ;
}

//  Connect the ports.  You can't do this before the client is
//  activated, because we can't make connections to clients
//  that aren't running.  Note the confusing (but necessary)
//  orientation of the driver backend ports: playback ports are
//  "input" to the backend, and capture ports are "output" from
//  it.

ports = jack_get_ports (jclient, NULL, NULL,   JackPortIsPhysical|JackPortIsOutput);

if (ports == NULL)
{
    printf("%s\nno physical capture ports #1\n", name);
    rval = 130;
    goto EXIT_SEQ;
}
for(i=0; i<n_channels; i++)
{
    if( jack_connect(jclient, ports[i], jack_port_name(in_ports[i]) ) )
    {
        printf("%s\nCannot connect input port # %d\n", name, i);
        rval = 140;
        goto EXIT_SEQ;
    }
}
free (ports);

ports = jack_get_ports (jclient, NULL, NULL, JackPortIsPhysical|JackPortIsInput);

if (ports == NULL)
{
    printf( "%s\nno physical playback ports\n", name);
    rval = 150;
    goto EXIT_SEQ;
}

for(i=0; i<n_channels; i++)
{
    if( jack_connect(jclient, jack_port_name(out_ports[i]), ports[i]) )
    {
        printf("%s\nCannot connect output port # %d\n", name, i);
        rval = 160;
        goto EXIT_SEQ;
    }
}
free (ports);

EXIT_SEQ:;
return rval;

}

文件main.cpp

#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <jack/jack.h>
#include "jack_interface.h"
#include "freqs.h"

enum { nports = 2 };  
static  jack_port_t     *in_ports [ nports ];       
static  jack_port_t *out_ports[ nports ];
static  jack_interface  JI;
static  jack_port_t     *input_portl;
static  jack_port_t     *input_portr;
static  jack_port_t     *output_portl;
static  jack_port_t     *output_portr;
static  unsigned int    samples_per_sec;
static  unsigned int    last_size = 0;
static  unsigned int    sample_size_out;
static freq_group FG;  

int process (jack_nframes_t nframes, void *arg)
{
    jack_default_audio_sample_t *inl, *inr, *outl, *outr;
    input_portl = JI.in_ports[0];
    input_portr = JI.in_ports[1];
    output_portl= JI.out_ports[0];
    output_portr= JI.out_ports[1];
    inl     = (float*)  jack_port_get_buffer (input_portl,  nframes);
    inr     = (float*)  jack_port_get_buffer (input_portr,  nframes);
    outl    = (float*)  jack_port_get_buffer (output_portl, nframes);
    outr    = (float*)  jack_port_get_buffer (output_portr, nframes);

    FG.remove_projections1(last_l_data, outl, nframes);
    memcpy (outr, last_r_data, sizeof (jack_default_audio_sample_t)*nframes);

    return 0;
   }

int main()
{
    static  char client_name[] = "filter5";
    int     rval=0;
    int     i;
    enum { nfreqs = 50 };   // Will be filtering out 50 frequencies
    double Freqs[nfreqs];   // The list of frequencies to eleminate
    double  freq_base = 60.0;
    double  this_freq;

    FG.set_time_and_points(1.0/48000.0, 4096);

    for(i=0; i<nfreqs; i++)
    {
       this_freq = double(2*i + 1) * freq_base;
       Freqs[i] = this_freq;
    }
    FG.setup_freqs(Freqs, nfreqs);
    rval = JI.create(client_name, 4096, 2, process);
    sample_size_out = JI.sample_size;
    samples_per_sec = JI.samps_per_sec;
    sleep(-1);
   return rval;
}