How can i measure soundcard latency?

I want to write sonar/radar like sample application. For sake of simplicity (and not to use Arduino and specialized RTOS) - I simply used using audio in/out analog ports of a motherboard integrated card as a sample hardware setup + Windows API with MSVS 2015. So, for my latency tests, I just used jack-jack cable from output(green) to line in (blue) minijacks. And, in my application, I have 2 important callbacks:

void on_playback_finished(short* buffer, int length) {
for(int i=0;i<length;i++)
buffer[i]=(i<(length/16))?(short)(sin(i)*32767):0
}


It's quite simple, it produces short "spike" (1/16th of the length of the buffer) of sine signal of some frequency, then makes a long pause of silence for the rest of the buffer(resting 15/16 of it).

And then second callback - which is called just when I recorded buffer from the line in. And this is my question. I must somehow detect this spike and measure latency:

void on_recorded_buffer(short* buffer, int length) {
// there is our spike, somewhere in the buffer[] samples array
// how to detect beginning of that spike,
// considering induced noise, attenuation and possible echo?
}


what to do? need assistance)

UPD. I am not a good drawer, but this could help to understand my question:

Red lines represent software<->hardware transitions. The 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!

• I think this is question is related more to programming than to SP. If you're using the same laptop to transmit and receive, why not just record the time before and after the program runs? – MBaz May 13 '17 at 2:00
• @MBaz because program will run forever in infinite loop, showing jittering latency :) and it is more related to SP algorithms, see: i know frequency of transmitted signal, and given recieved buffer; i have to determine time(or sample) offset of my signal "spike" then; its purely DSP task! – xakepp35 May 13 '17 at 2:06
• Cross correlate transmit and receive. See this Q&A: dsp.stackexchange.com/questions/30945/… – Dan Boschen May 13 '17 at 14:15
• @Dan Boschen Result will be zero. Because revieced signal will equal zero, where transmited signal occurs. And transmited signal will be zero where recieved signal occurs. They are shifted in time domain by much more that signal length itself!!!. So sum of samplewise multiplications will give zero in 99.99% of cases, its obvious. I need to determine sample OFFSET where recieved signal sits, relative to start of the buffer. (Transmitted signal always sits in the beginning of the buffer) – xakepp35 May 13 '17 at 19:54
• @Dan Boschen I've added an image. See my image and multiply signals in imaginations, you will always get zero. And no way to get sample offset – xakepp35 May 13 '17 at 20:13

Here are some approaches to consider:

FMCW:

Transmit a "chirp" signal where the frequency transmitted is slowly ramped from a low frequency to a high frequency (repeatably as a sawtooth or ramp pattern).

Multiply the received chirp with a replica of the transmitted chirp. The frequency of the resulting signal (after low pass filtering) will be proportional to the delay. An FFT is often used at this point to measure the frequency, but other frequency estimation techniques can also be used.

Review more information on "FMCW" radar for more details on this approach. Such as at this link: https://en.wikipedia.org/wiki/Continuous-wave_radar

Ranging with PRN sequences

Transmit a pseudo-random noise sequence that can be generated with linear feedback shift registers as a modulated signal in your audio band (FSK or BPSK are good choices).

Cross correlate the received sequence with the transmitted sequence efficiently with FFT's using xcorr = ifft(fft(rx)*fft(tx)), here assuming both tx and rx are real.

Note if resources are severely constrained but you have time on your side, you can perform the cross correlation over time: To demonstrate this I will give an example:

Given you want to measure 1 cm to 1 m, means you want to measure delays from 300 us to 3ms, this means we would want to use an LFSR that does not repeat from 3ms. Further for an accuracy of 1cm, a chip rate of 4KHz should be adequate. This means an 11 chip barker code such as the one I list below would work but you could also explore longer LFSR sequences.

11 bit barker code: 1 1 -1 1 -1 -1 -1 1 1 1

Note, the unique property of this code like other LFSR maximum length sequences, the cross correlation is maximum when the code is aligned (in this case = 11) and -1 every where else.

Using the above barker code with a 8Khz carrier, When the code is 1 transmit the 8KHz carrier in-phase and when it is -1 transmit it 180 degrees out of phase, thus you are mulitplying your carrier by plus or minus 1. (Note that an XOR gate is such a multiplier if you want to make this "ultra-low" resources). Because of the relationship I used between carrier and code, there will only be two cycles of the carrier for every code chip transmitted. The sequence is transmitted continuously.

At the receiver multiply in quadrature the received sequence with the same 8KHz carrier you already generated- meaning two multipliers where one of the received sequence is multiplied by the carrier directly and the other is multiplied by the carrier shifted 90 degrees at 8KHz, a quarter cycle. (again you could do this most simply with square waves and XOR gates by hard limiting the received signal and applying a DC offset to be midrange in the XOR gate, which would really be an "analog" solution as we would be using the XOR gate as an analog multiplier similar to a mixer- given you are using a sound card which will essentially be your A/D converter a digital multiplier will be more likely). The reason for the two multipliers is due to various phase delays, one path will be at a minimum when the other is maximum but with the two you will always have a signal to detect.

The multiplier output is followed with a simple moving average low pass filter, averaging over 1/16KHz = 62.5 us would be a good choice as it will have a null at 16KHz where we expect content of twice the carrier after the multiplication.

After the low pass filter, pass the signal through an FIR filter with the coefficients of the filter equal to your barker code sequence in reverse order. I recommend decimating the received sequence down to twice the chip rate (8 KHz) and then therefore using 2 samples/chip as the coefficients:

1 1 1 1 -1 -1 1 1 -1 -1 -1 -1 -1 -1 1 1 1 1 1 1

The output of the filter will be similar to my plot below that is specific to GPS (1023 length sequence), but to show that you will get spikes that repeat at the symbol rate:

If you compare the spike locations with a reference pulse created by your start of your sequence, you have a measure of delay. To do this, start a counter at the start of your sequence, and have a threshold detector on the filter output which stops the counter. Note as I described earlier that two multipliers and thus two filters are used and one output is "I" and the other filter output is "Q". A single estimate of magnitude can be determined using the following simple magnitude estimator:

$$|I+jQ| \approx Max(I,Q)+ 1/2 Min(I,Q)$$