실습 3. 대역폭과 aliasing
필터 없이 표본을 솎으면 새 표본화율의 절반 밖 성분이 접혀 들어오고, 변조 신호가 차지하는 폭은 심볼률에 비례한다.
이 실습의 자리: Pluto 실습: 송수신기 이해 — 실습 2에서 I와 Q의 불균형을 쟀고, 이 실습은 필터와 표본화율이 남기는 주파수 범위를 보며, 다음 실습 4에서 받은 파형을 비트로 되돌린다.
준비
활동은 셋이다. 톤의 오프셋을 쓸어 보며 필터와 접힘을 관찰하고, 같은 캡처를 필터 없이 솎은 결과와 필터를 거친 결과를 비교한 뒤, 점유 대역폭 함수를 써서 변조 신호의 폭을 잰다.
그림과 표 옆의 기록에 넣기를 누르고 글 상자에 설명을 쓴 뒤, 도구 막대의 저장을 누른다. 손잡이는 값을 고르는 장치이며, 값을 고른 뒤 받기를 눌러 새로 측정한다. 셀을 처음 실행할 때는 기본값으로 한 번 받는다.
연결 방식, 송신 규칙, 장치에서 알아 둘 것은 실습 1의 준비와 같다. 첫 셀의 MODE만 고른다.
루프백 (MODE = 'loopback') |
수신 전용 (MODE = 'receive') |
|
|---|---|---|
| 활동 1·2 | 내가 보내는 복소 톤 +200 kHz (RF 2442.2 MHz) | 목록의 cw: 2442.1 MHz(LO + 100 kHz) |
| 활동 3 | 내가 만들어 보내는 BPSK. Rs를 25, 50, 100 kHz로 바꾼다 | 목록의 bpsk: RRC 펄스, β = 0.35, Rs = 50 kHz, 2442.1 MHz 중심 |
| 송신 쪽 준비 | 없음 | 강의실 송신 Pluto가 활동 1·2 동안 cw, 활동 3에서 bpsk를 보낸다 |
장치 없이 할 때는 ?device=fake(루프백), ?device=fake&signal=cw(수신 전용, 톤), ?device=fake&signal=bpsk(수신 전용, 점유 대역폭)를 주소에 붙인다. 합성 장치의 필터는 칩을 흉내 낸 모형이라 그 숫자를 Pluto의 측정값으로 옮겨 적지 않는다.
MODE = 'loopback' # 수신 전용은 'receive'
from sdrlab import pluto, show, dsp
from sdrlab.front_end import ReceiverLab, rc_spectrum
import numpy as np
sdr = await pluto.connect()
await sdr.setup('rx-basic', mode=MODE)
lab = ReceiverLab(sdr, MODE)
활동 1. 톤을 대역 밖으로 옮기기
표본에 담기는 폭 밖의 톤은 어디에 보일까? RF 주파수 의 톤은 기저대역 에 선다. 표본은 만 구별하므로, 그 밖의 는 의 정수배를 뺀 자리
에 나타난다. 다만 칩 안에서는 솎기 전에 필터가 밖을 줄인다. ADC 앞의 아날로그 필터는 RF 대역폭 로 정해지고, 드라이버 소스에 따르면 3차 Butterworth의 3 dB 모서리가 이다. 그 뒤의 디지털 필터가 표본화율을 로 낮춘다(6장 표본화율과 RF 대역폭). 6장의 그림은 두 필터를 이상적인 모양으로 그렸다. 실제로 밖의 톤이 얼마나 줄어드는지는 이 실습에서 잰다. 3차 Butterworth 하나의 감쇠는
이다. 잰 감쇠가 이보다 크면 그 나머지는 디지털 필터(또는 6장이 말한 단극 필터)의 몫이다.
도우미는 RX LO를 옮기며 같은 RF 톤의 기저대역 오프셋을 쓸고, 처음 받은 톤의 봉우리로 장치 사이의 클럭 차를 보정한다. 먼저 표의 접힌 자리를 보고, 그 자리의 세기와 잡음 바닥을 비교한다. 선이 바닥에 묻히면 측정한 것은 톤의 세기가 아니라 잡음이다. 가장 큰 선이 DC나 TX LO 누설일 수도 있으므로 가장 큰 봉우리의 자리만으로 접힘을 판정하지 않는다.
await show.control(lab.sweep, capture=True)
계산한 접힘 자리에 선이 보이는가, 잡음 바닥에 묻히는가? 측정한 감쇠가 아날로그 필터만의 곡선과 다른 까닭은 무엇인가?
활동 2. 필터 없이 솎기와 필터 뒤 솎기
같은 캡처를 솎는 두 방법은 무엇이 다를까? x[::M]은 표본을 M개마다 하나 남긴다. 새 표본화율은 이므로 그 절반 밖의 톤은 안쪽으로 접힌다. dsp.decimate(x, M)은 저역 통과 필터로 바깥 성분을 줄인 뒤 솎는다. 필터는 경계에서 갑자기 떨어지지 않으며, 이 도우미는 새 대역의 80 %에 모서리를 두므로 경계 안쪽의 톤도 줄어들 수 있다.
먼저 M을 골라 두 스펙트럼의 주파수 축과 봉우리 자리를 비교한다. 그다음 표의 같은 자리에서 세기가 얼마나 달라지는지 본다. 두 그림은 같은 캡처를 사용하며 가로축은 새 표본화율로 표시한다. 표의 마지막 열은 칩에서 표본화율을 낮춰 새로 받은 값이다. M = 4일 때 Python의 필터와 칩의 필터가 같은 접힘 자리에서 얼마나 줄이는지 비교한다.
await show.control(
lab.decimation,
capture=True,
M=show.choice([2, 3, 4, 6], value=4, label='솎는 비 M'),
)
M을 바꿀 때 필터 없이 솎은 톤의 자리는 어떻게 달라지는가? 필터를 거친 결과에서 같은 자리의 선은 어떻게 달라지는가?
활동 3. 변조 신호의 점유 대역폭
변조 신호가 차지하는 폭은 무엇으로 잴까? 변조 신호는 한 줄이 아니라 대역을 차지한다. 여기서는 전체 전력의 99 %가 담기는 폭, 곧 점유 대역폭(occupied bandwidth)을 쓴다. 스펙트럼의 전력을 낮은 주파수부터 차례로 더해 가서 합이 전체의 0.5 %에 닿는 주파수와 99.5 %에 닿는 주파수 사이의 폭이다.
4장 Raised cosine 펄스의 RC 스펙트럼 는 에서 끝나므로, 반송파에 실은 신호는 RF에서 를 넘지 않는다. 목록의 bpsk는 송신 필터로 RRC를 쓴다. 비트가 무작위이면 RRC로 성형한 신호의 전력 스펙트럼은 모양이고, 수신기가 정합 필터(RRC)를 한 번 더 거치면 모양이 된다. 셀은 두 스펙트럼의 99 % 폭과 스펙트럼 끝 사이의 폭을 함께 계산한다.
가장자리에는 전력이 조금밖에 없으므로 점유 대역폭은 스펙트럼 끝 사이의 폭보다 좁다. 정합 필터를 한 번 더 거치면 가장자리가 더 줄어 점유 대역폭도 좁아진다. 같은 펄스를 더 빠르게 내보내면 스펙트럼이 가로로 늘어나므로 폭은 심볼률에 비례한다.
쓸 함수 occupied_bw(f, power)
- 입력: 오름차순 주파수
f(길이 N 실수 배열, Hz), 각 칸의 전력power(같은 길이, 음이 아닌 값, 합은 양수) - 출력:
(폭, 아래 끝, 위 끝)(모두 Hz), 전체 전력의 99 %를 가운데에 담는 구간
계산 순서
np.cumsum(power) / np.sum(power)로 누적 전력 비율을 만든다.np.searchsorted로 누적 비율이 0.005와 0.995에 닿는 첫 칸의 번호를 찾는다.- 그 번호의 주파수를
f에서 꺼내고, 두 주파수의 차와 양 끝을float로 돌려준다.
def occupied_bw(f, power):
"""주파수 f(Hz), 칸별 power에서 전력 99 %의 (폭, 아래 끝, 위 끝)(Hz)를 돌려준다."""
# TODO: 위의 계산 순서대로 occupied_bw 함수를 구현하세요.
raise NotImplementedError('occupied_bw 함수를 구현하세요.')
확인 셀은 무작위 비트의 RRC 전력 스펙트럼과 정합 필터 뒤의 전력 스펙트럼을 쓴다. β = 0.35일 때 폭은 각각 약 1.167 Rs, 1.055 Rs이며, 끝 사이의 폭 1.35 Rs보다 좁다. 계산한 비의 차가 0.002보다 작으면 맞게 쓴 것이다.
symbol_rate = 50e3 # 심볼/s
rolloff = 0.35
frequencies = np.linspace(-100e3, 100e3, 200001) # Hz
raised_cosine_power = rc_spectrum(frequencies, rolloff, symbol_rate)
rrc_bandwidth = occupied_bw(frequencies, raised_cosine_power)[0]
matched_bandwidth = occupied_bw(frequencies, raised_cosine_power ** 2)[0]
print(
f'RRC {rrc_bandwidth / symbol_rate:.3f} Rs, '
f'정합 필터 뒤 {matched_bandwidth / symbol_rate:.3f} Rs, 끝 사이 {1 + rolloff:.2f} Rs'
)
correct = np.allclose(
[rrc_bandwidth / symbol_rate, matched_bandwidth / symbol_rate],
[1.167, 1.055],
atol=0.002,
rtol=0,
)
print('→ 맞음' if correct else '→ 다시 본다')
수신 전용은 강의실 송신 신호가 bpsk로 바뀐 뒤 받기를 누른다. 루프백은 도우미가 무작위 비트를 RRC 펄스로 만들어 심볼률을 바꾸며 보낸다. 도우미는 신호 대역의 주파수 배열 f(Hz)와 잡음 바닥을 뺀 전력 배열 power를 내 함수에 준다. 정합 필터 앞뒤에 같은 함수를 적용해 폭을 비교한다.
먼저 표의 심볼률별 폭을 보고, 그다음 같은 심볼률의 정합 필터 전후 스펙트럼을 본다. 이론은 무작위 비트의 결과이며, 강의실 송신 신호의 반복 헤더·문자열은 전력이 나뉘는 모양이 다를 수 있다. 잡음과 왜곡도 폭을 넓힐 수 있으므로 차이와 함께 스펙트럼 가장자리를 기록한다.
async def draw_bw():
await lab.bandwidth(occupied_bw)
await show.control(draw_bw, capture=True)
심볼률별 폭은 이론 열과 어떤 관계인가? 같은 심볼률에서 정합 필터를 거친 뒤 폭이 달라진 이유는 무엇인가?
송신 끄기
await sdr.tx_stop()
print('송신을 멈췄습니다.' if MODE == 'loopback' else '수신 전용: 송신하지 않았습니다.')
탭을 닫기 전에 실행한다. 도구 막대의 송신 멈춤도 같은 일을 한다.
강의 노트