►
From YouTube: Opulent Voice: The Case of the Misbehaving Interleaver
Description
Paul KB5MU describes the adventure of understanding and fixing an unexpected failure in the Interleaver test of the C++ implementation of Opulent Voice, the baseline high-quality digital voice mode for the Phase 4 Ground and Space stations, including the Haifuraiya satellite project.
A
It's
designed
around
using
the
Opus
vocoder
to
send
very
high
quality
digital
voice
at
sixteen
thousand
bits
per
second,
the
voice
data
is
divided
up
into
40,
millisecond
frames,
2152
bits,
including
some
overhead,
and
each
frame
is
encoded.
With
a
convolutional
error
correction
code,
certain
aspects
of
the
opulent
voice
design
were
adopted
from
m17,
which
is
a
lower
rate,
digital
voice
mode
intended
for
VHF
and
UHF
amateur
radio
use.
A
This
is
possible
because,
like
everything
we
do
at
Ori,
m17s
design
and
implementation
are
open
source.
The
m17
project
published
a
prototype,
C
plus
plus
implementation
of
their
modulator
or
transmitter,
and
their
demodulator
or
receiver
written
by
Rob
Riggs
of
mobilelinked
to
get
going
quickly
on
opulent
voice
implementation.
We
started
with
that
code
base.
A
Since
then,
we've
made
many
changes
to
the
design
to
support
the
higher
rate
voice,
codec
and
our
own
ideas
about
the
feature
set.
We've
demonstrated
early
prototypes
already,
but
we're
still
working
on
turning
the
code
base
into
a
complete
implementation
of
opulent
voice,
along
with
the
code
that
actually
implements
m17
Rob
provided
a
number
of
unit
tests
that
can
be
run
automatically.
A
A
It
passed
the
tests
then,
but
now
it
was
failing
as
far
as
I
knew
I
had
not
changed
anything
that
could
break
that
part
of
the
code.
So
I
began
to
investigate
before
I.
Get
into
that.
Let
me
explain
what
an
interleaver
does
remember:
I
mentioned
40
millisecond
frames
and
the
convolutional
error
correction
code.
A
The
bits
are
transmitted
in
the
New
Order
and
arrive
at
the
receiver
on
the
third
row,
possibly
with
a
burst
error,
as
shown
here
within
the
receiver.
The
de-inter
lever
unscrambles
the
bits,
as
shown
by
the
lower
set
of
crisscrossing
arrows,
spreading
out
the
errors
so
that
the
FEC
decoder
has
a
better
chance
of
correcting
them.
A
The
interleaver
shown
here
happens
to
be
of
the
symmetric
type.
The
two
Tangles
of
crisscrossing
arrows,
look,
identical,
interleaving
and
de-interlining
are
the
same
operation.
This
is
because
all
of
the
non-vertical
arrows
come
in
pairs.
Each
pair,
swapping
a
pair
of
bits
in
the
transmission
swapping
them
back
is
exactly
the
same.
A
A
A
A
You
have
a
rectangular
array
of
storage
locations,
you
put
bits
in
by
rows
and
read
them
out
by
columns
any
burst
error
shorter
than
the
row
length
is
guaranteed
to
be
spread
out
with
spacing
equal
to
the
column
height
and
that's
pretty
good.
This
works
well.
If
the
block
size
is
a
perfect
square
or
can
be
factored
into
two
numbers
of
similar
size
for
Block
sizes
that
are
prime
or
have
one
large
prime
factor,
things
get
a
little
messy.
A
A
Every
F
of
I
has
to
be
in
the
same
range
as
I,
namely
from
0
to
the
block
size,
minus
1.,
and
every
possible
F
of
I
must
be
used
exactly
once
in
the
code.
This
could
be
just
a
lookup
table.
A
list
of
all
the
possible
values
of
f
of
I
this
table
would
have
as
many
entries
as
our
interleaver
block
has
bits.
A
We
would
still
need
to
figure
out
what
to
put
in
the
table.
It
can't
just
be
random
if
we
want
to
get
the
optimum
spacing
for
burst
errors.
A
good
way
to
generate
a
suitable
permutation
is
by
using
a
permutation
polynomial.
A
quadratic
polynomial
is
enough.
This
is
just
an
expression
of
the
form
ax
squared,
plus
BX,
plus
C
modulo
of
the
block
size
and
C
might
as
well
be
zero
because
it
just
rotates
the
whole
interleaved
block.
A
A
F
of
x
equals
1076
times
x,
squared
plus
59
times
x,
modulo
of
the
block
size.
This
makes
a
permutation
of
length
2152
our
frame
size
and
create
spacing.
That
is
very
nearly
the
theoretical
maximum
in
the
code.
We
don't
actually
bother
with
a
lookup
table.
We
just
use
the
Expression
directly
to
compute
the
indices
as
we
need
them
at
some
point.
We
should
probably
measure
how
much
CPU
time
this
is
costing
us,
maybe
switching
to
a
lookup
table
would
be
a
worthwhile
optimization.
A
A
A
A
A
A
A
The
first
screen
full
of
bytes
were
all
correct.
Scrolling
down
it
turns
out.
Most
of
the
bytes
were
correct.
There
was
just
one
run
of
19
consecutive
bytes
that
were
wrong.
A
A
Well,
you
know
the
interleaver
is
not
really
working
with
bytes
it's
working
with
the
individual
bits.
They
just
happen
to
be
packed
up
into
bytes.
Maybe
the
pattern
will
make
more
sense.
If
we
look
at
bit
errors
instead
of
bytes
I
was
hoping.
Kaleidoscope
would
color
the
erroneous
bits
for
me,
but
apparently
it's
too
smart
for
that.
So
I
used
some
more
text,
editor
magic
and
converted
that
display
into
this
one.
Each
X
represents
a
bit
error
and
each
dot
represents
a
correct
bit.
A
A
I
showed
the
results
to
Michelle
and
she
agreed
that
they
looked
random
and
made
no
sense.
Unless
maybe
the
interleaver
or
D
interleaver
was
using
a
lookup
table
that
had
somehow
become
corrupted
it
it
isn't.
It
uses
the
permutation
polynomial
directly,
here's
the
code,
it's
dead,
simple,
so
it's
time
for
some
more
detailed
code
inspection.
A
Here's
the
entire
interleave
function
again.
It
starts
by
declaring
a
buffer
the
size
of
a
frame
of
packed
bites.
It
pre-fills
that
buffer
with
zeros-
although
this
is
not
really
necessary,
then
for
every
bit
index
I.
It
moves
the
bit
at
that
index
in
the
input
to
the
corresponding
place
in
the
output.
A
To
do
that,
it
calls
three
helper
functions
after
the
loop
ends.
It
copies
the
local
temporary
buffer
over
the
input
data,
the
local
buffer
goes
out
of
Scope
when
the
function
ends
up
to
three
helper
functions,
two
of
them
a
signed
bit
index
and
get
bit
index
just
translate
between
bit
indexes
and
bits
packed
into
bytes.
A
A
A
Aren't
they
let's
review
the
test
results
we
see
51,
bytes,
correct,
then
19
bytes
wrong,
then
199
bytes
correct,
without
knowing
exactly
where
the
wrong
data
is
coming
from.
It's
impossible
to
be
completely
precise
about
these
counts,
because
some
of
the
bits
might
be
right
just
by
accident,
but
it's
highly
unlikely
that
accidents
change
the
picture
very
much.
A
A
A
A
A
A
A
A
A
A
On
other
occasions,
I've
developed
on
a
virtual
machine
on
the
Ori
remote
Labs
unraid
server
running
Ubuntu
Linux
in
64-bit
mode.
On
that
VM
size
T
is
a
64-bit
unsigned
integer
and
the
calculation
works.
Just
fine
and
I
was
using
the
VM.
When
we
redesigned
the
interleaver,
it
was
easy
to
forget
the
difference
between
the
Raspberry
Pi
and
the
VM,
because
I
operate
them
both
remotely
using
visual
studio
code
on
my
desktop
Macintosh
Visual
Studio
code
does
an
amazingly
good
job
with
remote
development
and
even
remote
debugging.
A
A
more
modern
programming
language
would
have
cut
this
problem
for
me
either
it
would
have
integers
of
unlimited
size
like
python
does,
or
it
would
have
caught
the
overflowing
multiplication
as
an
error
at
runtime,
if
it
couldn't
figure
it
out
at
compile
time,
C
plus
plus,
does
none
of
those
things
in
the
name
of
runtime
efficiency.
Just
another
reason
why
I'm,
not
a
big
fan
of
C
plus
plus
this
experience,
has
reinforced
my
enthusiasm
for
unit
testing,
though
I'm
not
always
careful
about
providing
formal
unit
tests
for
my
own
code.