问题描述
我想编写类似声纳/雷达的应用程序.为了简单起见(而不是使用adruino和专用的RTOS)-我只是使用了主板集成卡的音频输入/输出模拟端口作为示例硬件设置+ Steinberg ASIO SDK with MSVS2015年.因此,对于我的延迟测试,我只使用了从输出(绿色)到linein(蓝色)微型插孔的插孔-插孔电缆.而且,在我的应用程序中,我有2个重要的回调:
const int halfPeriod = 2;//48khz rect wave(192khz采样率)void on_playback_finished(short * buffer,int length = 1024){for(int i = 0; i< length; i ++)buffer [i] =(i <256)?((((i/halfPeriod)%2)* 60000-30000):0;}
这很简单,它会产生短的方波尖峰"(整个缓冲区长度的1/4),其余的缓冲区都被静音填充.
然后是第二个回调-仅当我从行中记录缓冲区时才调用.这是我的问题.我必须以某种方式检测到该尖峰(是否实际上已记录?)并测量等待时间,即从样本中记录的缓冲区开始偏移的时间;并返回该样本偏移量:
int on_recorded_buffer(短*缓冲区,int长度= 1024){//在buffer [] samples数组中的某个地方有我们的峰值//如何检测峰值的开始,//考虑衰减和可能的感应噪声?返回0;//理想情况-零样本延迟//这表示在记录的缓冲区的最开始处检测到峰值"//返回42;//在记录的缓冲区的第42个样本处检测到信号开始}
该怎么办?需要帮助,一些简单的算法)
UPD.考虑到信号是单声道(通道= 1),比特/采样= 16,采样率是192kHz,信号长度是256个采样(1.33333ms),信号之间的间隔至少是768个采样(4ms).我在问进行样本偏移量计算的正确算法!
UPD2我不是很好的抽屉,但是为了更容易理解:
为避免其他问题:发送的信号始终在缓冲区的开头开始.考虑发送和接收缓冲区具有相同的时间点(例如,软件是完美的,没有增加延迟).红线代表硬件/软件转换.检测收到的信号并计算其偏移量.
序言
这在 PC 上尤其是在 Windows 上是一个大问题.当时我在为 ZX Spectrum 模拟器编写声音模块时,我做了相当多的试验和错误尝试,还为该移动机器人构建了超声波声纳系统:
所以这里有一些见解:
-
声音API
在 Windows 上有更多具有声音功能的 API ,并且并非全部都适合此任务.我更喜欢 WAVEIN/WAVEOUT ,因为它在我尝试过的所有操作中都具有最佳的延迟( DirectSound 是最糟糕的).但是对于连续执勤来说,延迟并不是那么重要.
-
抖动和延迟偏移
如评论中所述,您需要同时接收直接信号和反射信号,以使某些内容与之同步:
因此,请使用
2
个MIC或同时听到直接和反射信号的MIC(例如,机械连接到生殖器).最好发送的脉冲数通常是7
(不要问我,为什么它是该领域的老人们的经验知识,并且具有最佳的结果,尤其是对于超声〜40KHz
).后续测量之间的间隙必须足够大,以使反射信号完全衰减.
如果您的设置使用连续的缓冲复制,则
Out
和L
之间的偏移量应始终相同(在声音管道完全启动后),但粗略您的应用程序启动之间会有所不同,因此您不能使用某些常量.立体声输入通道是同时采样的,因此您可以忽略声音管道的等待时间.如果您只想为MIC提供单声道信号,则可以用不同的权重将它们连接在一起.
-
硬件
当您使用
48KHz
时,我希望您的生殖器和MIC能够发送/检测此类信号.如果您只得到标准的音频资料,而不是使用更低的频率,例如8KHz
.要进行检查,可以使用以下方法:-
因此,我在油漆中添加了红色方块以检测导致该现象的样本点:
int data [22] = {370,370,368,371,367,376,323,157,273,580,488,148,260,593,476,144,261,595,476,142,259,594};
其中数组中的索引步长表示
30
个像素,该像素为1T = 1/192000秒
并在y
轴上缩放.那么现在我们有了样本测试数据,那么如何检测回声信号的开始?
-
计算平均值零
因此平均没有声音回波的前几个样本称为
y0
-
检测峰
因此确定一些阈值
thr
,它将检测脉冲峰值.峰值是当sample [i]> thr
表示存在某些信号时. -
检测零交叉点
简单地记住上一个峰值符号,如果与当前峰值相反,则越过零(
y0
). -
确定信号是否为回声
如果过零次数约为脉冲发送次数的两倍,并且脉冲组发送和接收的持续时间相似,则可以将找到的信号归类为回波.
以下是一些C ++代码:
const int w = 10;int x,y,xx,yy,y0,thr,sig;int size = 22,data [32] = {370,370,368,371,367,376,323,157,273,580,488,148,260,593,476,144,261,595,476,142,259,594};//图片pic0,pic1;//pic0是输入图像,pic1是输出图像pic1 = pic0;//将输入图像复制到输出/*//从图像中检测样本,您可以忽略此pic1& = 0x00FFFFFF;大小= 0;xx = -w;yy = -w;对于(x = 1; x< pic1.xs-1; x ++)对于(y = 1; y< pic1.ys-1; y ++)如果(pic1.p [y] [x] .dd == 0x00ED1C24)如果((((xx-x)*(xx-x))+((yy-y)*(yy-y))>(w * w)){xx = x + 3;yy = y + 3;pic1.p [yy] [xx] .dd = 0;数据[大小] = yy;大小++;}*///y0 =数据开始时的平均值(无回声)表示零对于(y0 = 0,x = 0; x Canvas-> MoveTo(0,y0);pic1.bmp-> Canvas-> LineTo(pic1.xs,y0);//检测回声thr = y0/10;//阈值=地块一半大小的10%sig = 0;pic1.bmp->画布->笔-> Color = clBlue;pic1.bmp-> Canvas-> Brush-> Color = clAqua;为(x = 1; x< size; x ++)if(abs(data [x] -y0)> thr)//峰值{xx =(x * 30)+22;//数组索引到像素位置yy = data [x];//峰值线pic1.bmp-> Canvas-> MoveTo(xx,y0);pic1.bmp-> Canvas-> LineTo(xx,yy);//零交叉点y = sig;如果(yy> y0)sig = + 1;否则sig = -1;如果(y * sig< = 0){pic1.bmp->画布->椭圆(xx-w,yy-w,xx + w,yy + w);}}
您可以忽略所有以
pic0
或pic1
开头的内容,因为您已经获得了示例data [size]
.结果如下:黑线是发现的平均零
y0
蓝线是发现零以上的峰,水线是脉冲(在零交叉点附近).I want to write sonar/radar like application. For sake of simplicity (and not to use adruino and specialized RTOS) - I simply used using audio in/out analog ports of a motherboard integrated card as an sample hardware setup + Steinberg ASIO SDK with MSVS 2015. So, for my latency tests i just used jack-jack cable from output(green) to linein(blue) minijacks. And, in my application, i have 2 important callbacks:
const int halfPeriod = 2; // 48khz rect wave (192khz samplerate) void on_playback_finished(short* buffer, int length=1024) { for(int i=0;i<length;i++) buffer[i]= (i<256) ? (((i / halfPeriod) % 2) * 60000 - 30000 ) : 0; }
It's quite simple, it produces short "spike" (1/4th of length of the whole buffer) of square wave, rest of buffer is filled with silence.
And then second callback - which is called just when i recorded buffer from line in. And this is my question. I must somehow detect this spike (is it actually recorded, at all?) and measure latency, offset from start of recorded buffer in samples; and return that sample offset:
int on_recorded_buffer(short* buffer, int length=1024) { // there is our spike, somewhere in the buffer[] samples array // how to detect beginning of that spike, // considering attenuation and possible induced noise? return 0; // ideal case - zero sample latency // that means "spike is detected at very beginning of recorded buffer" // return 42; // signal start is detected at 42th sample of recorded buffer }
what to do? need assistance, some simple algorithm)
UPD. Consider signal is mono(channels=1), bits/sample=16, Sampling rate is 192kHz, Signal length is 256 samples (1.33333ms), pause between signals is at least 768 samples (4ms). I am asking about proper algorithm of doing sample offset calculation!
UPD2 Iam not very good drawer, but to easier understanding look:
To avoid extra questions: Transmitted signal always starts at beginning of buffer. Consider transmit and recieve buffers have same timepoint (e.g. software is perfect, adds no latency). Red lines represent HW/Software transitions. Detect recieved signal and calculate offset of it start.
解决方案Prologue
Well this is a big problem on PC and especially on Windows. Back in the time I was writing sound module for my ZX Spectrum emulator I did my fair share of trial&error and I also did build ultrasonic sonar system for this mobile robot:
So Here some insights:
Sound API
There are more sound capable API on Windows and not all are suitable for this task. I prefer WAVEIN / WAVEOUT as it has best latency from all I tried (DirectSound is the worst). But for continuous duty is latency not as important.
Jitter and latency offset
As mentioned in the comments you need to receive both direct and reflected signal to have something to synchronize with:
So use
2
MICs or one that is hearing both direct and reflected signal (for example mechanically connected to reproductor). The best number of impulses send is usually7
(do not ask me why it is empiric knowledge form the old guys in the field and has the best results especially for ultrasound~40KHz
).The gap between consequent measurements must be large enough for reflected signals to fully dampen.
If your setup uses continuous buffered reproduction than the offset between
Out
andL
should be the same all the time (after sound pipeline is fully started) but of coarse will be different between your app starts so yo can not use some constant instead.Stereo input channels are sampled at the same time so this way you ignore the sound pipeline latencies. If you want just mono signal for the MICs then you can Wire or them together with different weights.
HW
As you are using
48KHz
I hope your reproductor and MICs are capable of transmitting/detecting such signal. If you got just standard audio stuff than use lower frequencies instead like8KHz
. To check for that you can use this:download my Win32 sound-card
Oscilloscope,generator and Spectrum analyzer
run the generator and oscilloscope or spectrum analyzer. on generator set desired square wave and look on oscilloscope if the signal is present and how it looks...
P.S.
Now if you need help with detecting the signal in
L,R
wee need to see the actual received signals first (you can screen-shot the oscilloscpoe).[Edit1] sample echo
I modified your image a bit so I can extract the sample points:
So I added (in paint) red squares to detect the sample points leading to this:
int data[22]={ 370, 370, 368, 371, 367, 376, 323, 157, 273, 580, 488, 148, 260, 593, 476, 144, 261, 595, 476, 142, 259, 594 };
Where index step in array represent
30
pixels which is1T = 1/192000 sec
That should match your sample audio but scaled to image so the amplitudes can have different offset and scale iny
axis.So now we have sample test data so how to detect start of the echo signal?
compute average zero
so average few first samples where there is no echo yet and call it
y0
detect peaks
so determine some threshold
thr
which will detect the pulse peaks. Peak is whensample[i]>thr
that means some signal is present.detect zero crossings
simply remember last peak sign and if opposite to present peak you crossed the zero (
y0
).determine if signal is echo
if number of zero crossings is around twice as much as pulses send and also the durations of the pulses group send and received are similar then you can classify found signal as echo.
Here some C++ code for this:
const int w=10; int x,y,xx,yy,y0,thr,sig; int size=22,data[32]={ 370, 370, 368, 371, 367, 376, 323, 157, 273, 580, 488, 148, 260, 593, 476, 144, 261, 595, 476, 142, 259, 594 }; //picture pic0,pic1; // pic0 is input image and pic1 is output image pic1=pic0; // copy input image to output /* // detect samples from image you can ignore this pic1&=0x00FFFFFF; size=0; xx=-w; yy=-w; for (x=1;x<pic1.xs-1;x++) for (y=1;y<pic1.ys-1;y++) if (pic1.p[y][x].dd==0x00ED1C24) if (((xx-x)*(xx-x))+((yy-y)*(yy-y))>(w*w)) { xx=x+3; yy=y+3; pic1.p[yy][xx].dd=0; data[size]=yy; size++; } */ // y0 = average on start of data (no echo) means zero for (y0=0,x=0;x<5;x++) y0+=data[x]; y0/=5; pic1.bmp->Canvas->Pen->Color=clBlack; pic1.bmp->Canvas->MoveTo( 0,y0); pic1.bmp->Canvas->LineTo(pic1.xs,y0); // detect echo thr=y0/10; // threshold = 10% of plot half size sig=0; pic1.bmp->Canvas->Pen->Color=clBlue; pic1.bmp->Canvas->Brush->Color=clAqua; for (x=1;x<size;x++) if (abs(data[x]-y0)>thr) // peak { xx=(x*30)+22; // array index to pixel position yy=data[x]; // peak line pic1.bmp->Canvas->MoveTo(xx,y0); pic1.bmp->Canvas->LineTo(xx,yy); // zero crossing dot y=sig; if (yy>y0) sig=+1; else sig=-1; if (y*sig<=0) { pic1.bmp->Canvas->Ellipse(xx-w,yy-w,xx+w,yy+w); } }
You can ignore all the stuff starting with
pic0
orpic1
as you already got the samplesdata[size]
. Here result:Black line is the found average zero
y0
Blue lines are found peaks above zero and aqua circles are found pulses (around zero crossings).这篇关于发送和接收了样本缓冲区后,如何测量带衰减的嘈杂延迟线的延迟?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!
-
-