►
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.
https://openresearch.institute
https://github.com/OpenResearchInstitute
A
It's
designed
around
using
the
Opus
vocoder
to
sound,
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
opulentvoice,
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
Unfortunately,
in
the
real
world
of
radio
errors
tend
to
come
in
bursts
in
order
to
make
the
most
of
forward
error
correction
system
designers
need
to
scramble
up
the
order
in
which
the
bits
are
transmitted
so
that
the
contiguous
bits
damaged
by
a
noise
burst
are
spaced
out
and
separated
before
they
go
into
the
convolutional
decoder.
Like
this,
the
original
stream
of
bits
is
shown
on
top
and
the
interleaver
is
the
function
that
scrambles
them
up
the
crisscrossing
arrows
into
a
new
order
shown
on
the
second
row.
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
this
symmetric
type.
The
two
Tangles
of
criss-crossing
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
interleavers,
using
a
lookup
table
that
had
somehow
become
corrupted
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
and
the
output
to
do
that.
It
calls
three
helper
functions
after
the
loop
ends.
A
A
As
far
as
I
know
the
other
helper
function
index
we've
already
seen
it
just
implements
the
polynomial
one
line
of
code.
So
let's
look
at
the
d-natively
function.
Does
it
look
familiar?
That's
because
it's
almost
identical
to
the
interleave
function.
The
only
difference
is
that
the
copy
is
in
the
opposite
direction.
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
that
VM
size
T
is
a
64-bit
unsigned
imagery
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.