Forum Discussion
Altera_Forum
Honored Contributor
11 years agoBPSK Demodulation
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?66 Replies
- Altera_Forum
Honored Contributor
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"? - Altera_Forum
Honored Contributor
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
- Altera_Forum
Honored Contributor
Hello Mr Kazem, here is my latest attempt:
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?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'); - Altera_Forum
Honored Contributor
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. - Altera_Forum
Honored Contributor
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? - Altera_Forum
Honored Contributor
--- 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. - Altera_Forum
Honored Contributor
Can you give a rule of thumb for controlling the NCO? Do I control phase of frequency? As they give different behaviors.
- Altera_Forum
Honored Contributor
--- 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 - Altera_Forum
Honored Contributor
Hello Kazem :)
I think I did it this time. Please take your time and test it at your leisure before judging :D
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=10423clear 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'); - Altera_Forum
Honored Contributor
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.