How can I measure the delay of a noisy fading delay line by transmitting and receiving sample buffers?

I want to write a sonar / radar application. For simplicity's sake (and not use adruino and a dedicated RTOS) - I just used using the audio I / O of the analog ports on the integrated motherboard card as a sample hardware setup + Windows API Steinberg ASIO SDK with MSVS 2015. So for my latency tests I used only 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 is quite simple, it produces a short "burst" (1/4 of the entire buffer length) of a square wave, the rest of the buffer fills with silence.

And then the second callback - which is only called when I've written the buffer from the string. And that's my question. I have to somehow detect this burst (is it actually recorded?) And measure the latency offset from the start of the recorded buffer in the samples; and return this sampling 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 help, some simple algorithm)

UPD. Consider a mono signal (channels = 1), bit / sample = 16, sampling rate - 192 kHz, signal length - 256 samples (1.33333 ms), the pause between signals is at least 768 samples (4 ms). I am asking about the correct algorithm for calculating the sample bias!

UPD2 I'm not a very good box, but for easier understanding: sonar dsp task

To avoid additional questions: the transmitted signal always starts from the beginning of the buffer. Consider transmit and receive buffers at the same time point (e.g. software is perfect, does not add latency). The red lines represent HW / Software transitions. Detect the received signal and calculate its start offset.

+3


source to share


1 answer


Prologue

Well, this is a big problem on PCs and especially on Windows . Back in the time I was writing a sound module for my ZX Spectrum emulator , I did my fair share of trial and error and I also created an ultrasonic sonar system for a mobile robot before that. Here are some ideas:

  • Sound API

    In Windows there is a more reliable the API , but not all are suitable for this task. I prefer WAVEIN / WAVEOUT as it has the best latency I've tried ( DirectSound is worse). But for continuous operation, latency is not that important.

  • Jitter and delay rejection

    As mentioned in the comments, you need to get both the direct and reflected signal in order to sync something with:

    sonar

    Therefore, use a 2

    MIC or one that hears both direct and reflected signal (for example, mechanically connected to the reproduced signal). The best number of pulses sent is usually 7

    (don't ask me why this empirical knowledge molds old guys in the field and has the best results, especially for ultrasound ~40KHz

    ).

    The gap between subsequent measurements should be large enough to completely cancel the reflected signals.

    If your setup uses continuous buffered playback, then the offset between Out

    and L

    should be the same all the time (after the audio pipeline is fully started), but grossly different from your application, so yo cannot use some constant instead.

    The stereo input channels are sampled simultaneously, so you ignore the delay in the audio pipeline. If you only need a mono signal for the MIC, then you can connect them or use them with different weights.

  • HW

    As you use 48KHz

    , I hope your speaker and microphones are capable of transmitting / detecting such a signal. If you only have standard audio files than use lower frequencies, not like 8KHz

    . To test this, you can use this:

    load my Win32 sound card Oscilloscope,generator and Spectrum analyzer

    run generator and oscilloscope or spectrum analyzer. on the generator set desired square wave and look at the oscilloscope if the signal is present and what it looks like ...

PS

Now, if you need help locating the signal in L,R

, you must first see the actual received signals (you can screen oscilloscopes).

[Edit1] sample echo

I modified your image a bit to extract examples:

echo sample

So, I added red squares (in paint) to detect the sampling 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 the index step in the array represents 30

pixels, which is equal 1T = 1/192000 sec

to which should match your audio sample but scaled for the image, so the amplitudes can have different offsets and scales in the axis y

.

So now we have some test data to determine how to start the echo?

  • calculate mean zero

    therefore, on average, the first few samples, where there is no echo yet, and we call it y0

  • peak detection

    therefore, define some threshold thr

    that will define the pulse peaks. A peak is when it sample[i]>thr

    means that some signal is present.

  • detect zero crossings

    just remember the last sign of the peak, and if you crossed zero ( y0

    ) opposite the current peak .

  • determine if a signal is echo

    if the number of zero crossings is about twice that of the transmission of pulses, and the duration of the group of pulses sent and received are similar, then you can classify the found signal as an echo.

Here's the C ++ code for that:

    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 things starting with pic0

or pic1

since you already have samples data[size]

. Result:

result

Black line - found average zero. y0

Blue lines are found as peaks above zero, and aqua circles are found as pulses (around zero crossings).

+2


source







All Articles