디지털 발진기를 구현하는 방법?


20

x86-64 프로세서를 사용하여 구현 된 초당 샘플 의 고정 샘플 속도로 작동하는 부동 소수점 디지털 신호 처리 시스템이 있습니다. DSP 시스템이 중요하게 동 기적으로 잠겨 있다고 가정하면, 어떤 주파수 에서 디지털 발진기를 구현하는 가장 좋은 방법은 무엇 입니까?fs=32768f

특히, 신호를 생성하고 싶습니다 :

y(t)=sin(2πft)
여기서 샘플 번호 대한 .t=n/fsn

하나의 아이디어는 각 클록 사이클 에서 각도 만큼 회전 하는 벡터 를 추적 하는 것 입니다.(x,y)Δϕ=2πf/fs

Matlab 의사 코드 구현으로 (실제 구현은 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)]

그런 다음 각 클럭 사이클에서 벡터를 약간 회전시킵니다.

%% 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

이를 통해 오실레이터가 사이클 당 4 개의 곱셈으로 계산 될 수 있습니다. 그러나 위상 오류와 진폭 안정성이 걱정됩니다. (간단한 테스트에서 나는 진폭이 죽는 즉시 폭발하지 않았다 놀랐습니다 - 어쩌면 sincos명령 보장 ?).sin2+cos2=1

이것을하는 올바른 방법은 무엇입니까?

답변:


12

엄격하게 재귀 적 접근 방식은 반복 횟수가 증가함에 따라 오류 누적에 취약하다는 것이 맞습니다. 이것이 일반적으로 수행되는 또 하나의 강력한 방법은 수치 제어 발진기 (NCO) 를 사용하는 것입니다 . 기본적으로 발진기의 순간 위상을 추적하는 누산기가 있으며 다음과 같이 업데이트됩니다.

δ=2πffs

ϕ[]=(ϕ[1]+δ)모드2π

매 순간마다 NCO의 누적 위상을 원하는 정현파 출력으로 변환합니다. 이를 수행하는 방법은 계산 복잡성, 정확성 등에 대한 요구 사항에 따라 다릅니다. 한 가지 확실한 방법은 출력을 다음과 같이 계산하는 것입니다.

엑스[]=코사인(ϕ[])

엑스에스[]=(ϕ[])

사용 가능한 사인 / 코사인의 구현을 사용하십시오. 처리량이 많은 시스템 및 / 또는 임베디드 시스템에서 위상에서 사인 / 코사인 값으로의 매핑은 종종 조회 테이블을 통해 수행됩니다. 룩업 테이블의 크기 (즉, 사인과 코사인에 대한 위상 인수에서 수행하는 양자화의 양)는 메모리 소비와 근사 오차 간의 상충 관계로 사용될 수 있습니다. 좋은 계산은 필요한 계산의 양이 일반적으로 테이블 크기와 무관하다는 것입니다. 또한 필요한 경우 코사인 및 사인 함수에 내재 된 대칭을 활용하여 LUT 크기를 제한 할 수 있습니다. 샘플링 된 정현파 기간의 4 분의 1 만 저장하면됩니다.

합리적인 크기의 LUT가 제공 할 수있는 것보다 높은 정확도가 필요한 경우 항상 테이블 샘플 (예 : 선형 또는 입방 형 보간) 간의 보간을 볼 수 있습니다.

δϕ[]


2
답변 해주셔서 감사합니다. 실행 시간은 sincos소수의 곱셈과 어떻게 비교됩니까? mod작업에주의 할만한 함정이 있습니까?
nibot

시스템의 모든 오실레이터에 동일한 위상 대 진폭 LUT를 사용할 수 있다는 점이 매력적입니다.
nibot

mod 2pi의 목적은 무엇입니까? 또한 mod 1.0을 수행하는 구현을 보았습니다. 모듈로 연산이 무엇인지 확장 할 수 있습니까?
BigBrownBear00

1
ϕ[][0,2π)

1
2π[0,1.0)ϕ[]

8

당신이 가진 것은 매우 좋고 효율적인 발진기입니다. 잠재적 수치 드리프트 문제는 실제로 해결 될 수 있습니다. 상태 변수 v에는 두 부분이 있습니다. 하나는 본질적으로 실수 부분이고 다른 하나는 허수 부분입니다. 그런 다음 r과 i를 불러 봅시다. 우리는 r ^ 2 + i ^ 2 = 1임을 알고 있습니다. 시간이 지남에 따라 위아래로 표류 할 수 있지만 과 같은 게인 보정 계수를 곱하여 쉽게 수정할 수 있습니다.

=1아르 자형2+나는2

분명히 이것은 매우 비싸지 만, 우리는 이득 보정이 단일성과 매우 가깝다는 것을 알고 있으며 로 간단한 Taylor 확장으로 이것을 근사 할 수 있습니다

=1아르 자형2+나는212((아르 자형2+나는2))

또한 모든 단일 샘플에 대해이 작업을 수행 할 필요는 없지만 100 또는 1000 개의 샘플마다 한 번씩이 안정성을 유지하기에 충분합니다. 이것은 프레임 기반 처리를 수행 할 때 특히 유용합니다. 프레임 당 한 번만 업데이트해도됩니다. 다음은 Matlab이 10,000,000 개의 샘플을 계산하는 빠른 방법입니다.

%% 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);

이 답변은 Hilmar가 또 다른 질문으로 더 설명합니다 : dsp.stackexchange.com/a/1087/34576
sircolinton

7

벡터 v를 재귀 적으로 업데이트하지 않으면 불안정한 크기 드리프트를 피할 수 있습니다. 대신 프로토 타입 벡터 v를 현재 출력 단계로 회전하십시오. 여전히 일부 삼각 함수가 필요하지만 버퍼 당 한 번만 필요합니다.

크기 드리프트 및 임의 주파수 없음

의사 코드 :

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

양자화 된 주파수 변환을 허용 할 수 있으면 곱셈, cexp에 필요한 삼각 함수 및 모듈러스가 2pi를 초과하지 않아도됩니다. 1024 샘플 페이저 버퍼의 경우 fs / 1024

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.