Skip to main content

M6 Software-Defined Radio

Here describes a design of a digital radio receiver for a multi-user transmitter with an error prone oscillator that functions in non-ideal transmission channels. The implementation is software-defined (i.e. no hardware other than an analog-to-digital converter) with Matlab simulations yielding deciphered messages that are fully comprehensible. It was designed by me and advised by Prof. Andrew Klein of Worcester Polytechnic Institute.


What to expect of the signal clarity by the end of this article.

Introduction

The M6 radio receiver was designed to work in tandem with a transmitter with the specifications listed in the table below:

 symbol source alphabet+1, +3 
 assigned intermediate frequency 210 MHz
 nominal symbol period121.6 nanoseconds
 SRRC pulse shape rolloff f Î² ∈ [0.1,0.3]
 FDM user slot allotment10.7 MHz 
 truncated width of SRRC pulse shape8 transmitter clock periods
 preamble sequenceA0Oh well whatever Nevermind 
 preamble length112 symbols 
 data length224 symbols/user 
 number of users3 users 
 total frame length784 symbols 
 time-varying IF carrier phaselowpass filtered white noise 
 transmitter IF offsetfixed, less than 0.01% of assigned value
 transmitter timing offsetfixed 
 transmitter symbol period offsetfixed, less than 0.01% of assigned value 
 intersymbol interferencemaximum delay spread = 7 symbols 
 sampler frequency50 MHz 

The receiver is called the M6 receiver.

Receiver System

The overall system is shown in the block diagram below:
Note: The following description of each module makes use of the medium test signal.

Low-Pass Filtering

The received signal was sampled at 50kHz which resulted in replicas of the message signal at intermediate frequency being formed every 50kHz. Therefore the receiver should low-pass filter the received signal to eliminate the replicas. The code snippet below shows this:


%% filtering replicas with LPF cutoff at about 30MHz
fnyq=1/(2*Ts); lcut=19.9E6; ucut=20.1E6;
b=firpm(500,[0 lcut/fnyq ucut/fnyq 1],[1 1 0 0]);
rlp=filter(b,1,r);
rlp=r;

What is left is just the frequency spectrum shown below:

Downconversion

Right now the signal is in the intermediate frequency. The next step is to demodulate it to baseband. The carrier signal must be recovered in order to do this. Since the carrier is suppressed, the signal must be preprocessed first (unless a Costas Loop is used instead). The preprocessing emphasizes the carrier signal relative to the message signal thereby making it easier to distinguish the carrier signal spectrum from the message signal spectrum. The preprocessing code and block diagram for the preprocessing stage is shown below:

%% pllpreprocess.m: send received signal through square and BPF  
rsq=rlp.^2;                                                                    % square nonlinearity
lcutA=18.9E6; lcutB=19.1E6; ucutA=20.9E6; ucutB=21.1E6;                        %cutoff frequencies for BPF
h=firpm(500,[0 lcutA/fnyq lcutB/fnyq ucutA/fnyq ucutB/fnyq 1],[0 0 1 1 0 0]);  % narrow BPF center frequency at 20MHz
rp=filter(h,1,rsq);                                                            % filter to give preprocessed r


The result of this preprocessing is shown below:
As you can see, the carrier signal has been greatly emphasized (although having twice the frequency and a phase shift).

The M6 will then use the information retrieved by the preprocessing stage in the dual phase-locked loop algorithm which can find the unknown carrier frequency and can track the time-varying carrier phase. The adaptive algorithm can be described by the equations in the block diagram and the code snippet below:


%% dualplls.m: estimation of carrier via dual loop structure
t=0:Ts:length(rp)*Ts-Ts;                                          % time vector
mu1=.02; mu2=.002;                                                % algorithm stepsizes
f0=(20E6)/2;                                                      % assumed freq. at receiver
lent=length(r); th1=zeros(1,lent);                                % initialize estimates
th2=zeros(1,lent); carest=zeros(1,lent);
for k=1:lent-1                       
 th1(k+1)=th1(k)-mu1*rp(k)*sin(4*pi*f0*t(k)+2*th1(k));            % top PLL
 th2(k+1)=th2(k)-mu2*rp(k)*sin(4*pi*f0*t(k)+2*th1(k)+2*th2(k));   % bottom PLL
 carest(k)=cos(2*pi*f0*t(k)+th1(k)+th2(k));                       % carrier estimate
en

So the recovered carrier signal is shown below:

Multiplying the carrier with the message signal will demodulate the message signal to baseband as shown below:

Match Filtering

Because of noise being present in the baseband, it is best to use a match filter to filter out the noise. In the process of demodulating the signal to baseband (as described in the previous step), replicas wereformed. The match filter will also filter out these replicas. The resulting signal is shown below:

Downsampling

It is time to downsample the signal so that the signal only has “message information” than entire transmission pulses. The M6 cannot just sample the pulses anywhere. It must sample when the eye is open.

The downsampling makes use of a timing recovery algorithm to find the best time to sample. The adaptive algorithm used in M6 is the output power maximization. It can be described by the equation below:
The code snippet below shows how it was implemented:


%% Downsampling - timing recovery
n=round(length(v)/M);              % number of data points
m=M;                               % oversampling factor
l=L;                               % 1/2 length of pulse shape (in symbols)

% clockrecOP.m:  clock recovery maximizing output power
% find tau to optimize J(tau)=|x(kT/m+tau)|^2 
% prepare transmitted signal
% run clock recovery algorithm
tnow=l*m+1; tau=0; xs=zeros(1,n);           % initialize variables
tausave=zeros(1,n); tausave(1)=tau; i=0;
mu=0.02;                                    % algorithm stepsize
delta=2;                                    % time for derivative
while tnow<length(v)-2*l*m                    % run iteration
    i=i+1;
    xs(i)=interpsinc(v,tnow+tau,l);           % interpolated value at tnow+tau
    x_deltap=interpsinc(v,tnow+tau+delta,l);  % get value to the right
    x_deltam=interpsinc(v,tnow+tau-delta,l);  % get value to the left
    dx=x_deltap-x_deltam;                     % calculate numerical derivative  
    tau=tau+mu*dx*xs(i);                      % alg update (energy)
    tnow=tnow+m; tausave(i)=tau;              % save for plotting
end

Equalization and Decision

Due to intersymbol interference from additive, noise, and multi-path interference, an equalizer is needed. The equalizer uses an adaptive least-mean-squares equalizer during preambles, which are the training signals, in order to sufficiently open the eye. This is so that the decision-directed equalizer, which needs a partially opened eye, can be used during the parts that have no known signal i.e. the data.

This means that the preambles must be located first. A correlator was used to locate a preamble which can then be used to locate the remaining preambles. The code snippet below shows the implementation of this:

%% Correlator 2
framlent=784;
preamble=letters2pam('A0Oh well whatever Nevermind');
prelent=length(preamble);   
xsc=xcorr(preamble,y);                % do cross correlation
[C,ind]=max(abs(xsc));                % location of largest correlation...
headstart=length(y)-ind+1;            % ...gives place where header starts
headstart=mod(headstart,framlent)+framlent;

numpre=0;
g=length(y)-headstart;
while g>=0
    g=g-framlent;
    numpre=numpre+1;
end

The algorithm can be described in the diagram below:
The equation for the LMS and DD equalizers are respectively given by:




The code snippet below shows how this combined equalizer (and the decision device) was implemented:

%% Equalization and Decision
cnt=1;
mu=.01; delayspread=7;                        % stepsize and delay delta
numtaps=4*delayspread; f=zeros(numtaps,1);    % initialize equalizer at 0
timer1=0;
timer2=0;
for j=0:1:(numpre-2)
    index1=headstart+framlent*j;
    % LMS Equalizer
    while timer1<=prelent-1 
        rr=xs(index1+delayspread+timer1-1:-1:index1+delayspread+timer1-numtaps);
        e=preamble(timer1+1)- rr*f;
        f=f+mu*e*rr';
        y(cnt)=rr*f;
        timer1=timer1+1;
        cnt=cnt+1;
    end
    timer1=0;
    index2=index1+prelent;
    % DD Equalizer
    while timer2<=datlent*3-1 
       rrb=(xs(index2+delayspread+timer2-1:-1:index2+delayspread+timer2-numtaps))';
       e=quantalph(rrb'*f,[-3 -1 1 3])- rrb'*f;
       f=f+mu*e*rrb;
       y(cnt)=rrb'*f;
       timer2=timer2+1;
       cnt=cnt+1;
    end
    timer2=0;
end

yflip=quantalph(y,[-3,-1,1,3])';

The two plots below illustrate the result of the equalizer. The first plot shows the constellations before the equalizer. The second shows the constellations after the equalizer.
As evident by the graph, the constellations are clearer after the equalizer.

Decoding

The decoding algorithm is shown below:


%% Decoding
savedheadstart=headstart;

newSlot = [];
oldSlot = [];
for count=1:numpre
    if ( headstart+prelent+1*datlent-1 <= length(yflip) )
        newSlot = yflip(headstart+prelent+0*datlent:headstart+prelent+1*datlent-1);
        message1 = [oldSlot newSlot];
        oldSlot = message1;
    else
        newSlot = yflip(headstart+prelent+0*datlent:length(yflip));
        message1 = [oldSlot newSlot];
        oldSlot = message1;
    end
    headstart=headstart+framlent;
end

headstart=savedheadstart;

newSlot = [];
oldSlot = [];
for count=1:numpre
    if ( headstart+prelent+2*datlent-1 <= length(yflip) )        
        newSlot = yflip(headstart+prelent+1*datlent:headstart+prelent+2*datlent-1);
        message2 = [oldSlot newSlot];
        oldSlot = message2;
    else
        newSlot = yflip(headstart+prelent+1*datlent:length(yflip));
        message2 = [oldSlot newSlot];
        oldSlot = message2;
    end
    headstart=headstart+framlent;
end

headstart=savedheadstart;

newSlot = [];
oldSlot = [];
for count=1:numpre
    if ( headstart+prelent+3*datlent-1 <= length(yflip) )
        newSlot = yflip(headstart+prelent+2*datlent:headstart+prelent+3*datlent-1);
        message3 = [oldSlot newSlot];
        oldSlot = message3;
    else
        newSlot = yflip(headstart+prelent+2*datlent:length(yflip));
        message3 = [oldSlot newSlot];
        oldSlot = message3;
    end
    headstart=headstart+framlent;
end

if desired_user==1
    decoded_text=pam2letters(message1)
end
if desired_user==2
    decoded_text=pam2letters(message2)
end
if desired_user==3
    decoded_text=pam2letters(message3)
end

Test Signals

These are the output of each test signals.

Easy Signal

BER: 0 Grade: 100

Medium Signal

BER: 0.00046992 Grade: 100

Difficult Signal

BER: 0.16996 Grade: 77

Conclusion

The digital radio receiver was able to decipher the first two test signals with minimal errors. However, the last one was difficult to decipher possibly due to a time-varying transmitter phase offset. This might be fixed by tuning the carrier recovery parameters.


This article was first published in 2009.

Comments

  1. Hi, can you explain why we have to use headstart=mod(headstart,framlent)+framlent;?

    ReplyDelete
  2. Can you send me the code for successful message decoding? Many thanks

    ReplyDelete

Post a Comment

Popular posts from this blog

The Ontology of Virtual Reality

In 2012 the Oculus Rift, a virtual reality (VR) headset display, made headlines in the technology world when it raised US$2.5 million through crowdfunding. In 2014 it was acquired by Facebook for a whopping US$2 billion. Ever since then leaders in technology have been pouring money in VR research & development and there is no shortage of creative studios producing VR content. Although VR have not reached universal adoption, the data looks to be auspicious. In 2010, just 2 years prior to VR's supposed harbinger, I wrote a chapter in my Sufficiency in the Philosophy of Technology under Prof. John Sanbonmatsu titled 'The Ontology of Virtual Reality'. Rereading it, I think it is still relevant today. Here it is: There are two aspects of virtual reality that pertains to human use: information and communication (Valentine and Holloway n.d.). Virtual reality is a space filled with information provided by its architects. Perhaps the easiest example to grasp is the internet...

Don Taylor's Creon in 'Antigone'

Who is the protagonist in Sophocles’ Antigone ? Is it Antigone or Creon? Scholars in literature often debate that question. But in Don Taylor’s 1984 rendition of Antigone , there is no doubt that Creon (played by John Shrapnel) is the protagonist. Don Taylor’s direction to have a Creon-focused Antigone was a precarious decision, but a highly successful one. You have to see it to believe it. The story of Antigone (played by Juliet Stevenson) is about the recalcitrant title character who buried her dead brother thereby violating a decree set forth by her uncle, the new king Creon. She did it to uphold a religious right she believed to be ubiquitous, but Creon viewed it as an act against his power and therefore refused to grant her impunity from death. Even the fact that she was his niece and future daughter-in-law could not have saved her nor did she want to be saved, at least not through family ties. After all, Antigone's iconoclastic action, her brother’s burial, was also ...