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.
The receiver is called the M6 receiver.
Note: The following description of each module makes use of the medium test signal.
What is left is just the frequency spectrum shown below:
So the recovered carrier signal is shown below:
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:
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:
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:
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 period | 121.6 nanoseconds |
SRRC pulse shape rolloff f | β ∈ [0.1,0.3] |
FDM user slot allotment | 10.7 MHz |
truncated width of SRRC pulse shape | 8 transmitter clock periods |
preamble sequence | A0Oh well whatever Nevermind |
preamble length | 112 symbols |
data length | 224 symbols/user |
number of users | 3 users |
total frame length | 784 symbols |
time-varying IF carrier phase | lowpass filtered white noise |
transmitter IF offset | fixed, less than 0.01% of assigned value |
transmitter timing offset | fixed |
transmitter symbol period offset | fixed, less than 0.01% of assigned value |
intersymbol interference | maximum delay spread = 7 symbols |
sampler frequency | 50 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
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:
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.
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: 100Medium Signal
BER: 0.00046992 Grade: 100Difficult Signal
BER: 0.16996 Grade: 77Conclusion
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.
Hey, I played SDR too
ReplyDeleteHi, can you explain why we have to use headstart=mod(headstart,framlent)+framlent;?
ReplyDeleteCan you send me the code for successful message decoding? Many thanks
ReplyDelete