DFT
표본 개에 어떤 주파수가 얼마나 들어 있는지 어떻게 계산할까?
DTFT와 DFT
표본 수열의 스펙트럼은 연속 신호의 푸리에 변환에 표본화를 이어 붙인 결과이고, 이것이 DTFT 이다. (rad/sample)는 표본 하나마다 도는 각이고, 는 마다 되풀이된다(신호 및 시스템 7장 이산시간 푸리에 변환).
컴퓨터가 가진 것은 표본 개이다. 그 개와 회전 개의 내적이 DFT이다.
은 시간 지표, 는 주파수 지표이고 둘 다 이며, 은 표본 수이다. 칸 는 물리 주파수 에 해당하고, 칸 간격은 이다. 관측 시간 의 역수와 같다(신호 및 시스템 7장 DFT: 회전과의 내적).
DFT 행렬
의 DFT를 한꺼번에 쓰면 행렬 곱 이다. 이고, 원소는 모두 1의 제곱근이다. 행 는 열을 하나 넘길 때마다 씩 도는 시계이다.
아래 그림은 일 때 를 시계로 그린 것이다. 입력은 이다. 먼저 '▶'로 행을 하나씩 내려가며 오른쪽 열이 채워지는 것을 보고, 행 2와 행 6의 값을 비교한다.
N = 8 시계 행렬로 쓴 X = Wx이다. 행 k의 시계를 위 줄의 x[n]과 자리마다 곱해 더한 값이 오른쪽 열의 X[k]이다. 신호 및 시스템 7장의 그림을 그대로 썼다.
해 보기
- 행 2까지 내려간다.
관찰
행 2의 바늘은 열마다 −90°씩 돈다. 이다.
- 행 6까지 내려간다.
관찰
으로 의 켤레이다. 행 6의 바늘은 행 2와 같은 속도로 반대 방향으로 돈다.
numpy의 np.fft.fft는 이 행렬 곱과 같은 값을 준다. 으로 나누지 않으므로, 진폭 1인 복소 톤이 칸에 정확히 놓이면 이다.
N = 8
n = np.arange(N)
W = np.exp(-2j * np.pi * np.outer(n, n) / N) # W[k, n]
x = np.array([1, 2, 0, -1, 0, 0, 0, 0])
np.allclose(W @ x, np.fft.fft(x)) # True
음의 주파수와 대칭
행 는 행 의 켤레이다. 같은 속도로 반대 방향으로 도는 회전이므로, 칸 는 음의 주파수 로 읽는다. 이 짝수이면 칸 는 와 가 같은 칸이고, numpy는 이 칸을 로 적는다. 그래서 np.fft.fftshift로 늘어놓은 축은 이고 칸은 따로 없다.
예를 들어 MHz, 이면 kHz이다. 칸 60은 kHz이고, 칸 340은 kHz이다.
입력 이 실수이면 두 칸의 값은 켤레이다.
과 (이 짝수일 때) 는 실수이다. 이 대칭은 을 써서 얻으므로, 입력이 복소수이면 성립한다는 보장이 없다. 복소 표본의 스펙트럼이 0 Hz에 대해 대칭이 아닌 이유이다(신호 및 시스템 7장 실수 신호와 복소 신호).
실습에서 쓰는 DFT의 성질
성질 표 전체는 신호 및 시스템 7장 DTFT의 성질에 있다. SDR 코드에서 자주 쓰는 것은 셋이다.
- 주파수 이동: 표본에 를 곱하면 스펙트럼 전체가 만큼 옮겨 간다. 이 의 정수배이면 칸으로 칸 옮겨 가고, 끝을 넘은 칸은 반대쪽 끝으로 돌아온다. 정수배가 아니면 선이 칸 사이에 놓여 이웃 칸으로 번진다(3장). 받은 신호를 소프트웨어로 0 Hz 둘레에 다시 맞추는 방법이 이것이다.
- 시간 이동: 입력을 표본 늦추면 에 이 곱해진다. 크기는 그대로이고 위상만 에 비례해 기운다. DFT에서는 끝에서 밀려난 표본이 앞으로 돌아오는 원형 이동이다.
- Parseval 관계: . 표본 제곱의 합(에너지)을 스펙트럼에서도 같은 값으로 계산한다.
강의 노트