Phase drift of NCO
No matter what phase resolution is used for an NCO, phase drift will occur if phase increment value in not an integer. This is because the error of fraction builds up until the phase wraps up full cycle.This drift matters if your application is phase sensitive.
The code below shows two cases of NCO, one(generic) with phase resolution of 2^16 but with rounded increment value. the second(dedicated) with lower phase resolution but integer increment value.
Notice also that in case the accumulator resolution is higher than LUT and hence accumulator bits are to be truncated (and rounded) then it will result in further phase error build up. In this case unbiased rounding is useful.
clear all; close all;
n = 50000; %number of test samples
Fs = 100; %sampling rate in Msps
Fo = 1.17; %target NCO frequency in MHz
M = [2^16 10000]; %NCO phase resolution, two cases
for i = 1:2
inc = round(M(i)*Fo/Fs); %phase increment
lut = round(2047*cos(2*pi*(0:M(i)-1)/M(i))); %lut, one cycle cosine data
addr = 0:inc:inc*(n-1);
addr = round(mod(addr,M(i)));
addr(addr >= M(i)) = addr(addr >= M(i)) - M(i); %check address overflow
y(i,1:n) = lut(addr+1); %add 1 for matlab LUT
end
fprintf('increment value = %2.6f\r',M*Fo/Fs);
subplot(2,1,1);hold;
plot([y(1,1:1000) zeros(1,100) y(1,end-999:end)],'.-');
plot([y(2,1:1000) zeros(1,100) y(2,end-999:end)],'r.--');
legend('drift','no drift');
title('initial and last 1000 samples');
[P1, F1] = pwelch(y(1,:), hann(n), 0, n, Fs);
[P2, F2] = pwelch(y(2,:), hann(n), 0, n, Fs);
subplot(2,1,2); hold
plot(F1,10*log10(P1));
plot(F2,10*log10(P2),'r--');
legend('drift','no drift')
axis ([0 50 -20 100]);
grid
xlabel('MHz')