Làm thế nào để thực hiện một bộ dao động kỹ thuật số?


20

Tôi có một hệ thống xử lý tín hiệu số dấu phẩy động hoạt động với tốc độ mẫu cố định fs=32768 mẫu mỗi giây được thực hiện bằng bộ xử lý x86-64. Giả sử rằng hệ thống DSP được khóa đồng bộ với bất kỳ vấn đề gì, cách tốt nhất để thực hiện bộ tạo dao động kỹ thuật số ở tần số gì?f

Cụ thể, tôi muốn tạo tín hiệu: trong đó cho số mẫu .

y(t)=sin(2πft)
t=n/fsn

Một ý tưởng là theo dõi một vectơ mà chúng ta xoay theo một góc trên mỗi chu kỳ đồng hồ.(x,y)Δϕ=2πf/fs

Là một triển khai mã giả Matlab (triển khai thực tế là trong C):

%% Initialization code

f_s = 32768;             % sample rate [Hz]
f = 19.875;              % some constant frequency [Hz]

v = [1 0];               % initial condition     
d_phi = 2*pi * f / f_s;  % change in angle per clock cycle

% initialize the rotation matrix (only once):
R = [cos(d_phi), -sin(d_phi) ; ...
     sin(d_phi),  cos(d_phi)]

Sau đó, trên mỗi chu kỳ đồng hồ, chúng ta xoay vectơ xung quanh một chút:

%% in-loop code

while (forever),
  v = R*v;        % rotate the vector by d_phi
  y = v(1);       % this is the sine wave we're generating
  output(y);
end

Điều này cho phép bộ dao động được tính toán chỉ với 4 phép nhân trên mỗi chu kỳ. Tuy nhiên, tôi lo lắng về lỗi pha và ổn định biên độ. (Trong các thử nghiệm đơn giản, tôi đã ngạc nhiên rằng biên độ không chết hoặc phát nổ ngay lập tức - có thể sincoshướng dẫn đảm bảo ?).sin2+cos2=1

Cách đúng đắn để làm điều này là gì?

Câu trả lời:


12

Bạn đúng rằng cách tiếp cận đệ quy nghiêm ngặt dễ bị tích lũy lỗi khi số lần lặp tăng lên. Một cách mạnh mẽ hơn mà điều này thường được thực hiện là sử dụng bộ tạo dao động điều khiển số (NCO) . Về cơ bản, bạn có một bộ tích lũy theo dõi pha tức thời của bộ dao động, được cập nhật như sau:

δ=2πffs

ϕ[n]=(ϕ[n1]+δ)mod2π

Sau đó, tại mỗi thời điểm tức thì, bạn còn lại với việc chuyển đổi pha tích lũy trong NCO thành các đầu ra hình sin mong muốn. Cách bạn làm điều này phụ thuộc vào yêu cầu của bạn về độ phức tạp tính toán, độ chính xác, v.v ... Một cách rõ ràng là chỉ tính toán các đầu ra là

xc[n]=cos(ϕ[n])

xs[n]=sin(ϕ[n])

sử dụng bất cứ điều gì thực hiện sin / cosine bạn có sẵn. Trong các hệ thống thông lượng cao và / hoặc nhúng, việc ánh xạ từ các giá trị pha sang sin / cosine thường được thực hiện thông qua bảng tra cứu. Kích thước của bảng tra cứu (tức là lượng lượng tử hóa bạn thực hiện trên đối số pha với sin và cosine) có thể được sử dụng như một sự đánh đổi giữa mức tiêu thụ bộ nhớ và lỗi xấp xỉ. Điều tuyệt vời là số lượng tính toán cần thiết thường không phụ thuộc vào kích thước bảng. Ngoài ra, bạn có thể giới hạn kích thước LUT của mình nếu cần bằng cách tận dụng tính đối xứng vốn có trong các hàm cosin và sin; bạn chỉ thực sự cần lưu trữ một phần tư thời gian của hình sin được lấy mẫu.

Nếu bạn cần độ chính xác cao hơn LUT có kích thước hợp lý có thể cung cấp cho bạn, thì bạn luôn có thể xem xét nội suy giữa các mẫu bảng (ví dụ nội suy tuyến tính hoặc khối).

δϕ[n]


2
Cảm ơn câu trả lời. Làm thế nào để thời gian thực hiện sincosso sánh với một số phép nhân? Có bất kỳ cạm bẫy có thể để xem ra với các modhoạt động?
nibot

Điều hấp dẫn là LUT cùng pha với biên độ có thể được sử dụng cho tất cả các bộ dao động trong hệ thống.
nibot

Mục đích của mod 2pi là gì? Tôi cũng đã thấy các triển khai thực hiện mod 1.0. Bạn có thể mở rộng về những gì hoạt động modulo là có?
BigBrownBear00

1
ϕ[n][0,2π)

1
2π[0,1.0)ϕ[n]

8

g=1r2+i2

g=1r2+i212(3(r2+i2))

Hơn nữa, chúng tôi không cần phải làm điều này trên mỗi mẫu đơn lẻ, nhưng cứ 100 hoặc 1000 mẫu là quá đủ để giữ ổn định này. Điều này đặc biệt hữu ích nếu bạn thực hiện xử lý dựa trên khung. Cập nhật một lần trên mỗi khung là tốt. Dưới đây là một Matlab nhanh chóng tính toán 10.000.000 mẫu.

%% seed the oscillator
% set parameters
f0 = single(100); % say 100 Hz
fs = single(44100); % sample rate = 44100;
nf = 1024; % frame size

% initialize phasor and state
ph =  single(exp(-j*2*pi*f0/fs));
state = single(1 + 0i); % real part 1, imaginary part 0

% try it
x = zeros(nf,1,'single');
testRuns = 10000;
for k = 1:testRuns
  % overall frames
  % sample: loop
  for i= 1:nf
    % phasor multiply
    state = state *ph;
    % take real part for cosine, or imaginary for sine
    x(i) = real(state);
  end
  % amplitude corrections through a taylor exansion aroud
  % abs(state) very close to 1
  g = single(.5)*(single(3)-real(state)*real(state)-imag(state)*imag(state) );
  state = state*g;
end
fprintf('Deviation from unity amplitude = %f\n',g-1);

Câu trả lời này được Hilmar giải thích thêm trong một câu hỏi khác: dsp.stackexchange.com/a/1087/34576
sircolinton

7

Bạn có thể tránh trôi độ lớn không ổn định nếu bạn không làm cho nó cập nhật đệ quy vectơ v. Thay vào đó, hãy xoay vectơ nguyên mẫu v của bạn sang pha đầu ra hiện tại. Điều này vẫn yêu cầu một số chức năng trig, nhưng chỉ một lần cho mỗi bộ đệm.

Không có độ lớn trôi và tần số tùy ý

mã giả:

init(freq)
  precompute Nphasor samples in phasor
  phase=0

gen(Nsamps)
    done=0
    while done < Nsamps:
       ndo = min(Nsamps -done, Nphasor)
       append to output : multiply buf[done:done+ndo) by cexp( j*phase )
       phase = rem( phase + ndo * 2*pi*freq/fs,2*pi)
       done = done+ndo

Bạn có thể loại bỏ các bội số, các hàm lượng giác được yêu cầu bởi capi và mô đun còn lại trên 2pi nếu bạn có thể chịu được một bản dịch tần số lượng tử. ví dụ: fs / 1024 cho bộ đệm pha mẫu 1024.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.