Duck
Duck

Reputation: 35953

Trying to decode a FM like signal encoded on audio

I have an audio signal that has a kind of FM encoded signal on it. The encoded signal is using this Biphase mark coding technique <-- see at the end of this page.

This signal is a digital representation of a timecode, in hours, minutes, seconds and frames. It basically works like this:

  1. lets consider that we are working in 25 frames per second;
  2. we know that the code is transmitting 80 bits of information every frame (that is 80 bits per frame x 25 frames per second = 2000 bits per second);
  3. The wave is being sampled at 44100 samples per second. So, if we divide 44100/2000 we see that every bit uses 22,05 samples;
  4. A bit happens when the signal changes sign.
  5. If the wave changes sign and keeps its sign during the whole bit period it is a ZERO. If the wave changes sign two times over one bit period it is a ONE;

What my code does is this:

  1. detects the first zero crossing, that is the clock start (to)
  2. measures the level for to = to + 0.75*bitPeriod... 0.75 to give a tolerance.
  3. if that second level is different, we have a 1, if not we have a 0;

This is the code:

// data is a C array of floats representing the audio levels

float bitPeriod = ceil(44100 / 2000);
int firstZeroCrossIndex = findNextZeroCross(data);
// firstZeroCrossIndex is the value where the signal changed
// for example: data[0] = -0.23 and data[1] = 0.5
// firstZeroCrossIndex will be equal to 1

// if firstZeroCrossIndex is invalid, go away
if (firstZeroCrossIndex < 0) return 

float firstValue = data[firstZeroCrossIndex];
int lastSignal = sign(firstValue);

if (lastSignal == 0) return; // invalid, go away

while (YES) {

    float newValue = data[firstZeroCrossIndex + 0.75* bitPeriod];
    int newSignal = sign(newValue);

    if (lastSignal == newSignal)
      printf("0");
    else 
      printf("1");

    firstZeroCrossIndex += bitPeriod;

    // I think I must invert the signal here for the next loop interaction
    lastSignal = -newSignal;

    if (firstZeroCrossIndex > maximuPossibleIndex)
      break;

}

This code appears logical to me but the result coming from it is a total nonsense. What am I missing?

NOTE: this code is executing over a live signal and reads values from a circular ring buffer. sign returns -1 if the value is negative, 1 if the value is positive or 0 if the value is zero.

Upvotes: 2

Views: 308

Answers (1)

Johannes Overmann
Johannes Overmann

Reputation: 5151

Cool problem! :-)

The code fails in two independent ways:

  1. You are searching for the first (any) zero crossing. This is good. But then there is a 50% chance, that this transition is the one which occurs before every bit (0 or 1) or whether this transition is one which marks a 1 bit. If you get it wrong in the beginning you end up with nonsense.

  2. You keep on adding bitPeriod (float, 22.05) to firstZeroCrossIndex (int). This means that your sampling points will slowly run out of phase with your analog signal and you will see strange effects when you sample point gets near the signal transitions. You will get nonsense, periodically at least.

Solution to 1: You must search for at least one 0 first, so you know which transition indicates just the next bit and which indicates a 1 bit. In practice you will want to re-synchronize your sampler at every '0' bit.

Solution to 2: Do not add bitPeriod to your sampling point. Instead search for the next transition, like you did in the beginning. The next transition is either 'half a bit' away, or a 'complete bit' away, which gives you the information you want. After a 'half a bit' period you must see another 'half a bit' period. If not, you must re-synchronize since you took a middle transition for a start transition by accident. This is exactly the re-sync I was talking about in 1.

Upvotes: 2

Related Questions