Programmable Devices
CPLDs, FPGAs, SoC FPGAs, Configuration, and Transceivers
20687 Discussions

BPSK Demodulation

Altera_Forum
Honored Contributor II
4,300 Views

Hello again, I am still struggling with BPSK demodulation :) 

 

OK, now I am trying to implement Costas Loop for carrier synchronization, which involves Inphase and Quadrature Phase paths. 

Inphase is looking good, and works as expected, however, the Q component looks awkward. Take a look at these waveforms: 

 

http://www.alteraforum.com/forum/attachment.php?attachmentid=10363&stc=1  

 

I understand that multiplying sin * cos will produce a high frequency component, which is a sin modulated by half amplitude of the data signal. 

BPSK(t) * sin(2*pi*f*t) = 0.5 * [DATA(t) * sin(0) + DATA(t) * sin(2*pi*f*t)] 

=> 0.5 * DATA(t) * sin(2*pi*f*t) 

=> LPF{0.5 * DATA(t) * sin(2*pi*f*t)} = 0 

 

I feel that problem is in my LPF. I used the same Matched RRC filter that I used in the in-phase arm. 

Papers I read didn't mention that these two filters should be different, that's why I used the same filter. 

(I think those are unpractical DSP writers, KAZ :D ) 

 

So, should I use a separate Loop Filter for Q component? How do I determine it's parameters?
0 Kudos
66 Replies
Altera_Forum
Honored Contributor II
655 Views

freqz displays f normalised in radians so 1 means 0.5 of Fs and 0.3 means 0.15Fs. 

 

Your thinking is right about loop and if somebody wants to jam you on .15 then take him to court. If he is doing legally adjacent to your band then set your nominal nco to a closer start such as .20 

 

i is index of sample (time/period) and phase then equals that index as part of the gang i*2*pi *inc for the cos/sin values. Time here has no meaning until you apply specific Fs.
0 Kudos
Altera_Forum
Honored Contributor II
655 Views

 

--- Quote Start ---  

freqz displays f normalised in radians so 1 means 0.5 of Fs and 0.3 means 0.15Fs. 

 

Your thinking is right about loop and if somebody wants to jam you on .15 then take him to court. If he is doing legally adjacent to your band then set your nominal nco to a closer start such as .20 

 

i is index of sample (time/period) and phase then equals that index as part of the gang i*2*pi *inc for the cos/sin values. Time here has no meaning until you apply specific Fs. 

--- Quote End ---  

 

 

Thank you Mr KAZ. 

I hope you check that file and see if it can help to model our loop. (Actually it does have different behavior when you change - to + in error signal).
0 Kudos
Altera_Forum
Honored Contributor II
655 Views

 

--- Quote Start ---  

Thank you Mr KAZ. 

I hope you check that file and see if it can help to model our loop. (Actually it does have different behavior when you change - to + in error signal). 

--- Quote End ---  

 

 

 

I did some read on that file. This guy is probably funny trying to match some absolute phase of two signals. This is meaningless unless he got some specific application. 

 

absolute phase is meaningless to control as signal could arrive at any phase and this is immaterial in most radios (possibly all radios). what we want is to control frequency through phase until rf signal is centred on dc. Phase then doesn't matter and you should not compare phase if signals are of different frequencies. don't mix up with PLL that generate clock in phase with ref.
0 Kudos
Altera_Forum
Honored Contributor II
655 Views

Hhhhhhhh What do you want Mr Kazem!!! Matlab is meant to prove math through math... not like ModelSim prove math through hardware, even though it's not giving correct results. Problem is either in the idea, or in our predictions. Idea is true since it's not that hard to write mathematical equations in Matlab. 

I have a question, how different is a Costas Loop for QPSK than BPSK? I think it should be the same with one difference, the quadrature component in BPSK has no information while it does have in QPSK, and it should not affect Costas Loop in both cases. Is this correct?
0 Kudos
Altera_Forum
Honored Contributor II
655 Views

 

--- Quote Start ---  

Hhhhhhhh What do you want Mr Kazem!!! Matlab is meant to prove math through math... not like ModelSim prove math through hardware, even though it's not giving correct results. Problem is either in the idea, or in our predictions. Idea is true since it's not that hard to write mathematical equations in Matlab. 

--- Quote End ---  

 

 

I don't quite understand your thoughts. Unless proved otherwise I believe strongly now that the free simplified documented design for BPSK carrier tracking does not work efficiently because the error of sin*cos is vague except at very start cycle of sine wave. This start is marred with filter delay and vanishes quickly.  

 

I suggets you use the costas loop for qpsk/qam which I did. This loop's error is based on slicer output.  

 

You will need to see diagrams of that loop which is based on complex multiplication with sliced values of I/Q together with signal I/Q to produce error. 

 

In your case you have only two sliced states either -1 or +1. So the concept of I/Q sliced data doesn't apply but may be you can use it but ignore the Q channel at the end. 

 

For QPSK it was particulary efficient. So I guess for BPSK as well. Try your skill again with modelling it that way. 

 

To slice the symbols you need to have threshold after matched rrc then feedback the results into error detector.
0 Kudos
Altera_Forum
Honored Contributor II
655 Views

OMG! Our brains are synchronized without a Costas Loop! :D 

I edited my previous post to ask you about QPSK error circuitry! 

You are amazing Kazem!! 

I'm gonna model it right away!
0 Kudos
Altera_Forum
Honored Contributor II
655 Views

Alright, this code uses a slicer to calculate error: 

 

clear clc load RRC.mat rf_signal = cos(2*pi*(1:5000)*0.1); N = length(rf_signal); phi_inc = zeros(1, N); bb = zeros(1, N); bb_f = zeros(1, N); I_f = zeros(1, N); Q_f = zeros(1, N); I_r = zeros(1, N); Q_r = zeros(1, N); error = zeros(1, N); error_f = zeros(1, N); %loop filter coefficients alpha = 0.1; b = alpha; a = ; fs = 1.0E8; fc = 0.1; for i = 1:N % Downconverting to Baseband bb(i) = rf_signal(i).*exp(j*2*pi*fc*i); % Filtering bb_f = filter(RRC, bb(1:i)); I_f(i) = real(bb_f(i)); Q_f(i) = imag(bb_f(i)); % Slicing if I_f(i) > 0 I_r(i) = 1; else I_r(i) = -1; end if Q_f(i) > 0 Q_r(i) = 1; else Q_r(i) = -1; end % Error error(i) = I_f(i) .* Q_r(i) - Q_f(i) .* I_r(i); % Loop Filter error_f = filter(b, a, error(1:i)); phi_inc(i+1) = phi_inc(i) - error_f(i); end figure subplot 221 plot(real(bb)) title('I Channel') subplot 222 plot(imag(bb)) title('Q Channel') subplot 223 plot(I_f) title('I Channel Filter') subplot 224 plot(Q_f) title('Q Channel Filter') figure; plot(phi_inc); title('Phase'); figure; plot(error_f); title('Error');  

 

I didn't feed the phase back to NCO just to observe the error and phase signals. They look acceptable and have some life. Plus, if I add instead of subtract error I get an opposite signals. However, I tried to control NCO through phase and frequency and none got me a good result. But I feel that error signal without feedback is correct. 

I attached the RRC coeff so that you can try this code if you like.
0 Kudos
Altera_Forum
Honored Contributor II
655 Views

I can notice that I component has a DC offset... how can I get rid of it inside for loop? And do I have to scale to unity as you did?

0 Kudos
Altera_Forum
Honored Contributor II
655 Views

 

--- Quote Start ---  

I can notice that I component has a DC offset... how can I get rid of it inside for loop? And do I have to scale to unity as you did? 

--- Quote End ---  

 

 

You will get dc on both I and Q when it works. So if set rf freq and nco to target dc you get it. Depending on rf phase I or Q could be any pair of constants. 

 

you don't have dc in rf signal because it is man made clean tone. In real rf you should remove dc (or else it moves to nco frequency where you can filter it. 

 

if you get error changing sense you got it. 

 

next how to be gentle with nco: 

the error is basically mad. you need to filter it through IIR and add it proportional term (error x 8 or x 100...) and even a second accum if it helps. 

finally you scale the error (division by 2^n i.e. later shift in hardware). The scaling is also critical since in your model your nco expects +/- 0 ~ 0.5 range and very gently. 

 

run your model in feedback loop, set addition of error to zero to kill the loop effect. Then observe the error, the filtered error and final error and make sure it is gentle and with above nco range. 

 

In hardware the tuning word will have to be scaled and things will need proper scaling.
0 Kudos
Altera_Forum
Honored Contributor II
655 Views

Amazing! Thanks for presaging me! 

You are right about scaling... signal is in the range 0~400 which can't control the NCO which is in the range +/- 0.5 

But I didn't get this: "set addition of error to zero to kill the loop effect", what do you mean? 

And are you referring to the control signal by "final error"?
0 Kudos
Altera_Forum
Honored Contributor II
655 Views

That was for initial testing of error sense. Once done close the feedback loop. final error is tha t value you add or subtract to nco word

0 Kudos
Altera_Forum
Honored Contributor II
655 Views

Hello Mr Kazem, here is my latest attempt: 

 

clear clc load RRC.mat rf_signal = cos(2*pi*(1:5000)*0.1); fc = 0.1; N = length(rf_signal); phi_inc = zeros(1, N); bb = zeros(1, N); bb_f = zeros(1, N); I_f = zeros(1, N); Q_f = zeros(1, N); I_r = zeros(1, N); Q_r = zeros(1, N); error = zeros(1, N); error_f = zeros(1, N); %loop filter coefficients alpha = 0.1; b = alpha; a = ; for i = 1:N % Downconverting to Baseband bb(i) = rf_signal(i).*exp(j*2*pi*fc*i); % Filtering bb_f = filter(RRC, bb(1:i)); I_f(i) = real(bb_f(i)); Q_f(i) = imag(bb_f(i)); % Slicing if I_f(i) > 0 I_r(i) = 1; else I_r(i) = -1; end if Q_f(i) > 0 Q_r(i) = 1; else Q_r(i) = -1; end % Error error(i) = I_f(i) .* Q_r(i) - Q_f(i) .* I_r(i); % Loop Filter error_f = filter(b, a, error(1:i)); phi_inc(i+1) = phi_inc(i) - error_f(i)/2^10; end figure subplot 221 plot(real(bb)) title('I Channel') subplot 222 plot(imag(bb)) title('Q Channel') subplot 223 plot(I_f) title('I Channel Filter') subplot 224 plot(Q_f) title('Q Channel Filter') figure; plot(error); title('Error'); figure; plot(error_f); title('Filtered Error'); figure; plot(phi_inc); title('Final Error');  

 

I just scaled down the filtered error by 2^10. I think it is gentle as you mentioned and scaling bring it to range +/- 0.5. 

 

Error: 

https://www.alteraforum.com/forum/attachment.php?attachmentid=10413  

 

Filtered Error: 

https://www.alteraforum.com/forum/attachment.php?attachmentid=10414  

 

Final Error: 

https://www.alteraforum.com/forum/attachment.php?attachmentid=10415  

 

Of course loop is open as you can see from code. So, what do you think?
0 Kudos
Altera_Forum
Honored Contributor II
655 Views

Hi Siraj, 

 

Diagrams of Costas loops are different and reflects different rx structures. For demo purposes use the easiest one.  

work on control loops is no small job though you can readily see a diagram and follow it but you will need weeks of work to get the loop tuned. 

 

The second step is to use actual qpsk signal from your tx and pass through your rx until you get the constellations. 

 

To keep your test structure simple I suggest you: 

1)generate random qpsk 

2) pass through rrc upsampling by 2 or higher 

3) mix with a frquency 

4) keep it complex to rx (so you don't require lpf if you wish) 

5) downconvert inside the costas loop using your basic slicer 

6) after the loop(outside it) apply rrc again decimating by 2. 

plot(I,Q,'.') 

 

you should get clean constellations if your loop gets fc it to dc 

else it will be circle.  

When you decimate, you will have two output phases to choose. watch both 

you will spend a lot of time to tune the loop controlling it through various loop filters and error sense and scaling.
0 Kudos
Altera_Forum
Honored Contributor II
655 Views

I see... but that's all for MATLAB, and I'm afraid I won't have time to deploy it to FPGA! Since I am aware of additional work in hardware domain! 

I am reading an article from Matlab help about CORDIC-Based QPSK Carrier Synchronization. Do you recommend following it?
0 Kudos
Altera_Forum
Honored Contributor II
655 Views

 

--- Quote Start ---  

I see... but that's all for MATLAB, and I'm afraid I won't have time to deploy it to FPGA! Since I am aware of additional work in hardware domain! 

I am reading an article from Matlab help about CORDIC-Based QPSK Carrier Synchronization. Do you recommend following it? 

--- Quote End ---  

 

 

what is the cordic computing? is'it a feedback loop based as well? 

 

you may also try your first loop design and use proper qpsk system(as suggested above) instead of single tone and see if it behaves. directly observing constellations.
0 Kudos
Altera_Forum
Honored Contributor II
655 Views

Can you give a rule of thumb for controlling the NCO? Do I control phase of frequency? As they give different behaviors.

0 Kudos
Altera_Forum
Honored Contributor II
655 Views

 

--- Quote Start ---  

Can you give a rule of thumb for controlling the NCO? Do I control phase of frequency? As they give different behaviors. 

--- Quote End ---  

 

 

you change nco frequency by changing the increment value which points to phase. The actual sine values of 1 cycle (or half or quarter) are stored in lut. 

 

for negative frequency you can still add the negative increment (well subtract) so pointer goes backwards, or use positive increment but swap sin/cos or invert one of them
0 Kudos
Altera_Forum
Honored Contributor II
655 Views

Hello Kazem :) 

I think I did it this time. Please take your time and test it at your leisure before judging :D 

 

clear clc load RRC.mat fc = 0.1 % 0.11 %0.09 rf_signal = cos(2*pi*(1:5000)*0.1); N = length(rf_signal); bb = zeros(1, N); bb_f = zeros(1, N); I_f = zeros(1, N); Q_f = zeros(1, N); I_r = zeros(1, N); Q_r = zeros(1, N); error = zeros(1, N); PhErr = zeros(1, N); error_integral = zeros(1, N); Theta = zeros(1, N); % Loop Filter Coefficients BW = 200; % Hz loop_theta = 2*pi*BW; C1 = 4*(loop_theta)^2/(1+sqrt(2)*loop_theta+loop_theta^2); C2 = 2*sqrt(2)*loop_theta/(1+sqrt(2)*loop_theta+loop_theta^2); for i = 2:N % Downconverting to Baseband bb(i) = rf_signal(i).*exp(j*2*pi*fc*i).*exp(j*Theta(i-1)); % Filtering bb_f = filter(RRC, bb(1:i)); I_f(i) = real(bb_f(i)); Q_f(i) = imag(bb_f(i)); % Slicing if I_f(i) > 0 I_r(i) = 1; else I_r(i) = -1; end if Q_f(i) > 0 Q_r(i) = 1; else Q_r(i) = -1; end % Error error(i) = I_f(i) .* Q_r(i) - Q_f(i) .* I_r(i); % Loop Filter error_integral(i) = error(i).*C1 + error_integral(i-1); PhErr(i) = error(i).*C2 + error_integral(i); % Phase Accumulator Theta(i) = Theta(i-1) + PhErr(i); end figure subplot 221 plot(real(bb)) title('I Channel') subplot 222 plot(imag(bb)) title('Q Channel') subplot 223 plot(I_f) title('I Channel Filter') subplot 224 plot(Q_f) title('Q Channel Filter') figure; subplot 311; plot(error); title('Phase Error'); subplot 312; plot(PhErr); title('Loop Filter'); subplot 313; plot(Theta); title('Control Signal \theta');  

 

Just to motivate you, look at these signals: 

 

https://www.alteraforum.com/forum/attachment.php?attachmentid=10422  

https://www.alteraforum.com/forum/attachment.php?attachmentid=10423
0 Kudos
Altera_Forum
Honored Contributor II
666 Views

A true Rx down converter has several front end functions such as clock recovery and carrier tracking. 

 

carrier tracking has two components (frequency and phase). the nominal tx freq is used at rx to downconvert to dc and the the phase must also be locked at localrx nco. 

 

If you are not concerned about frequency but only phase (e.g. for demo) then your very first design of cos*sin should do.
0 Kudos
Altera_Forum
Honored Contributor II
666 Views

1- Isn't that the supposed situation? I mean, I am expected to provide a fixed frequency and the carrier synchronizer has the job of recovering the phase, isn't it? 

2- Do I understand that the first design (sin*cos) tracks phase only, while the above design (using sign circuitry) tracks frequency? 

3- Surprisingly, before you post I was trying to simulate this design using my original BPSK signal, and the above design failed to demodulate properly even though the loop locked. I eventually did as you mentioned, I used the sin*cos approach and eliminated the "Phase Accumulator" and used the PhErr signal to control NCO. It did demodulate and lock! I tried to shift the local NCO phase and tried to set fc at 0.050001 (TX's fc is 0.05) and it locked.
0 Kudos
Altera_Forum
Honored Contributor II
666 Views

There is here misunderstanding and it is difficult. 

A true receiver (for wireless QAM) needs first to have clock recovery circuit to lock to Tx clock and this is a nightmare. 

 

Once clock is recovered then other things are clocked by this pace maker.  

 

The carrier tracking at Rx needs to lock to both rf frequency and phase. You may know the nominal Tx frequency centre but due to oscillator jitter and shift at both ends (and any mobile Rx) it will have to follow it. The phase must also lock at least in one qaudrant terms(1 of 4 quadrants for a cycle). It is no good locking to same freq but a changing or tilted phase. 

 

The difficulty here is to understand these two concepts (freq and phase). If you can control phase of two different freqs you control both. freq and phase. If you control freq only you control freq ONLY. 

 

some designs control either or both. 

 

Your 1st design requires somebody else to control freq (apparently as we couldn't see any useful sense of error to be useful to push signal to baseband) but if given two inputs at same freq centre then it does give enough error to lock to one quadrant of cycle. 

 

To test it keep rf and nco freqs at .1 then change the phase rf and see error.
0 Kudos
Reply