AM Demodulation - Complex Magnitude

Status
Not open for further replies.

DD4WH

Well-known member
I have built a Software Defined Radio, the Teensy SDR (design and software by rheslip). SSB demodulation and spectrum display with 44.1kHz bandwidth work well.

I would like to add AM demodulation, but have problems in setting up a function/library for calculating complex magnitude. It should take I and Q (left and right channel) and calculate sqrt(I*I+Q*Q) and transmit it back to the audio stream. I am an absolute beginner in the depths if the more complex coding of libraries and register shifting etc. So what I did is taking snippets from other libraries/functions and put them together. I have heavily drawn from the "effect_multiply.cpp" and the "mixer.cpp", but I still have problems in understanding and setting up a function that uses the arm_cmplx_mag_q15 CMSIS function.

The desired audio object/function should take I and Q from the audio stream (left and right channel) and calculate the complex magnitude sqrt(I*I+Q*Q) and transmit/output that to a single audio output.

What I have up to now is the following AMDemod.cpp:

Code:
/* Audio Library for Teensy 3.X
 * Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
 *
 * Development of this audio library was funded by PJRC.COM, LLC by sales of
 * Teensy and Audio Adaptor boards.  Please support PJRC's efforts to develop
 * open source software by purchasing Teensy or other PJRC products.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice, development funding notice, and this permission
 * notice shall be included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include "AM_demod.h"

void AMDemod::update(void)
{

	audio_block_t *blocki, *blockq;
	uint32_t *pi, *pq, *end;
	uint32_t i12, i34; 
	uint32_t q12, q34; 

	blocki = receiveWritable(0); // receives I, end result is written in channel 0
	blockq = receiveReadOnly(1); // receives Q
	
	if (!blocki) {
		if (blockq) release(blockq);
		return;
	}
	
	if (!blockq) {
		release(blocki);
		return;
	}
	
	pi = (uint32_t *)(blocki->data);
	pq = (uint32_t *)(blockq->data);
	end = pi + AUDIO_BLOCK_SAMPLES/2;

	while (pi < end) {
		i12 = *pi;
		i34 = *(pi+1);

		q12 = *pq++;
		q34 = *pq++;

    i12 = arm_cmplx_mag_q15( ); // I have no idea what to put in here ?
    i34 = arm_cmplx_mag_q15( ); // I have no idea what to put in here ?
  
  
		*pi++ = i12;
		*pi++ = i34;
		
	}
	transmit(blocki);
	release(blocki);
	release(blockq);

}

and the following AMDemod.h

Code:
/* Audio Library for Teensy 3.X
 * Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
 *
 * Development of this audio library was funded by PJRC.COM, LLC by sales of
 * Teensy and Audio Adaptor boards.  Please support PJRC's efforts to develop
 * open source software by purchasing Teensy or other PJRC products.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice, development funding notice, and this permission
 * notice shall be included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#ifndef AM_demod_h_
#define AM_demod_h_
#include "AudioStream.h"
#include "utility/dspinst.h"

class AMDemod : public AudioStream
{
public:
	AMDemod() : AudioStream(2, inputQueueArray) { }
	virtual void update(void);
private:
	audio_block_t *inputQueueArray[2];
};

#endif

Am I on the right track? Maybe someone could fill in the gap in the .cpp and/or comment on my approach or give me some hints. Would be very much appreciated!

Best wishes, Frank DD4WH
 
Hi Frank,
There are a couple of problems with your code.
- the samples are signed 16-bit, not unsigned 32-bit.
- the arm_cmplx_mag_q15 would be nice to use except that it expects the real and
imaginary parts to be interleaved real,imag,real,imag,etc whereas you are
receiving one buffer with all the real and one with all the imag. I don't
know if it is worth copying the samples into a separate buffer, interleaving
them as you go, and then calling arm_cmplx_mag_q15 especially since it outputs
the results in 2.14 format (which loses one bit of precision).

I think you would be better to do the calculation yourself and use arm_sqrt_q15
to do the sqrt.

Try this code:
Code:
void AMDemod::update(void)
{

  audio_block_t *blocki, *blockq;
  int16_t *pi, *pq, *end;
  int32_t sum;

  blocki = receiveWritable(0); // receives I, end result is written in channel 0
  blockq = receiveReadOnly(1); // receives Q
  
  if (!blocki) {
    if (blockq) release(blockq);
    return;
  }
  
  if (!blockq) {
    release(blocki);
    return;
  }
  
  pi = (int16_t *)(blocki->data);
  pq = (int16_t *)(blockq->data);
  end = pi + AUDIO_BLOCK_SAMPLES;

  while (pi < end) {
    // square and sum I and Q
    sum = *pi * *pi;
    sum += *pq * *pq;
    // The result of squaring a 1.15 is 2.30.
    // Shift the sum up one bit to make it 1.31 (Q31)
    // and then that becomes the input to the arm sqrt function
    sum <<= 1;
    arm_sqrt_q31((q31_t)sum,(q31_t *) &sum);
    // The result is in the high order 16 bits of sum
    *pi++ = sum >> 16;
    pq++;    
  }
  transmit(blocki);
  release(blocki);
  release(blockq);
}

I can't test this with a live I/Q signal.
I've tested the calculation in the loop and it does produce the correct result with the sample values of I and Q that I tried.
It also should be fast enough to keep up with the incoming stream.

Give it a try.

Pete
 
AM demodulation works with the Teensy SDR

Hi Pete,

thanks a lot for your quick help! Your comments in the code also helped me to understand much better, what this bit shifting is about, although I still have to learn a lot.

Your code works perfectly with live IQ signals, I had to add a few includes to get it working with "arm_math.h" and similar things.

My SDR now does AM demodulation! I am very happy, however I have to tweak my software a bit to add an IF in order to get rid of hardware-produced noise and to be able to switch between SSB and AM.

Thanks again!

Best wishes, Frank
 
Status
Not open for further replies.
Back
Top