<div dir="ltr"><p class="MsoNormal">Hi List<span></span></p>

<p class="MsoNormal">In listening to the teamspeak recording I thought I’d share
my experience of writing an audio resampler for my hermes lite software ( Spark
sdr <a href="http://www.ihopper.org/radio/">http://www.ihopper.org/radio/</a> ) in the hope that it might be useful.  I’m currently rewriting Spark in a form that
I can open source and happen the have just got to the resampler part.<span></span></p><p class="MsoNormal"><br></p>

<p class="MsoNormal">The hermes lite has no audio hardware so all audio uses the
pc sound system so there is no escape from the clock difference problem.<span></span></p><p class="MsoNormal"><br></p>

<p class="MsoNormal">The resampler is used for two functions, firstly to prevent
audio clicks from buffer over/underruns caused by clock speed differences and
secondly to minimise latency. The software monitors how close to buffer
underrun the system gets and if there is excess slack the resampler rate is
adjusted to reduce it.  This eliminates
any need for audio buffer size settings.<span></span></p><p class="MsoNormal"><br></p>

<p class="MsoNormal">The resampler I use is a very simple linear interpolation, I
had always meant to swap it out for something better but have never had any
issues or reports about the audio quality so have not had the motivation. It
has the advantage of very low cpu use and worst case latency of 1 sample. I
suspect it would cause problems for digimodes over vac but as Spark was
designed to avoid using vac I don’t care!<span></span></p><p class="MsoNormal"><br></p>

<p class="MsoNormal">Measuring the relative clock speed caused the most problems.
<span></span></p><p class="MsoNormal"><br></p>

<p class="MsoNormal">One pit I fell into was requesting a buffer size from
portaudio audio different to its preferred size, if the buffer requested is
smaller than the actual audio buffer size the portaudio callback is called in
bursts, this can cause a subtle error in reading the audio clock rate as the
thread producing the audio will statistically capture a clock reading more
often between bursts which will cause the filtered frequency to read high.  Letting Portaudio choose the buffer size
fixed this.<span></span></p><p class="MsoNormal"><br></p>

<p class="MsoNormal">Before I solved this issue I had tried a number of ways of
filtering the very jittery clock data you get packet by packet including iir
filters, delay locked loop, Kalman, moving linear regression and moving
average, I suspect they all would have worked but as I happened to be using the
moving average at the point I fixed the above problem and it all started
working, I stuck with it.  I’m currently
moving this code from c# to c++ and shall go back and re try the other options.<span></span></p><p class="MsoNormal"><br></p>

<p class="MsoNormal">Another trick was to not do any timing over the first few
samples, In Spark sdr the rest of the dsp chain keeps running if you do
something like change the audio device, this can cause a backlog of data that
appears at a high rate, for some audio device types portaudio sometimes appears
to request the first few buffers at a high rate.<span></span></p><p class="MsoNormal"><br></p>

<p class="MsoNormal">For the control loop I use the measured clock ratio as a
feed forward term and then have proportional and integral terms to correct for
drift of the mean buffer offset.  A much
slower loop then gently changes the target buffer offset to minimise
latency.  The final rate sent to the
resampler is passed through a simple lowpass filter.  There is further code to detect abnormal conditions
and reset everything if need be.  <span></span></p><p class="MsoNormal"><br></p><p class="MsoNormal">To measure the buffer offset from the writing  thread I estimate the current read position from the time elapsed since the last portaudio read event and the last known read position, this reduces the jitter of this measurement.</p><p class="MsoNormal"><br></p>

<p class="MsoNormal">Spark can connect to multiple radios at the same time so
there can be multiple clock sources, currently this is handled with a separate portaudio
connection for each receiver( sharing the audio device if need be), however the
portaudio documentation indicates this may not work for all audio devices.  A future task is to add a mixer in so
multiple streams can be handled by a single connection.<span></span></p><p class="MsoNormal"><br></p>

<p class="MsoNormal">Currently I measure the radio clock speed at the point of
filling the audio buffer and it works, it might be better to measure at the
point udp packets are received to reduce jitter.  From a software standpoint this is a little
more messy.  I do measure the radio clock
at the point of receiving udp packets for a different purpose, by comparing it
against the ntp corrected computer clock and filtering over a very much longer
time I am able to automatically frequency correct two hermes lites so wspr
spots are generally within 1Hz of each other, I may combine these two bits of
code.<span></span></p><p class="MsoNormal"><br></p>

<p class="MsoNormal">At John Laur’s suggestion my hermes emulator <a href="https://github.com/ahopper/Patroclus">https://github.com/ahopper/Patroclus</a>
has the option to adjust the virtual clock speed which is useful for testing these
resamplers ( this feature only works for protocol 1 currently).<span></span></p><p class="MsoNormal"><br></p>

<p class="MsoNormal">73 Alan M0NNB<span></span></p></div>