프로그램이 바꾸는 것: IIO 속성
코드 한 줄 sdr.rx_lo = 2440000000은 장치 안에서 무엇을 바꿀까?
드라이버의 속성
Linux의 IIO(Industrial I/O)는 ADC나 DAC처럼 아날로그와 디지털 사이를 변환하는 장치를 다루는 틀이다. Pluto의 Linux에서 AD9363 드라이버는 IIO 장치 ad9361-phy로 나타나고, 설정 하나하나가 이 장치의 속성(attribute)이다. 이름이 AD9361인 것은 같은 드라이버가 AD9361, AD9363, AD9364를 함께 다루기 때문이다. 표본은 수신용 장치 cf-ad9361-lpc와 송신용 장치 cf-ad9361-dds-core-lpc의 버퍼로 오간다.
Pluto 안의 서버 iiod는 USB로 온 요청을 받아 속성을 읽고 쓰고, 버퍼를 읽고 쓴다. 그래서 어떤 프로그램에서 부르든 장치에서 바뀌는 것은 같은 속성이다. 명령행 도구 iio_attr -c ad9361-phy altvoltage0 frequency 2450000000은 수신 LO를 2.45 GHz로 바꾼다(강의 자료 WE5 14쪽). PC의 Python 라이브러리 pyadi-iio와 이 과목의 브라우저 실습 쪽도 같은 속성을 쓴다.
altvoltage0과 altvoltage1은 수신·송신 LO 채널이다. voltage0은 수신 쪽 설정이면 입력 채널, 송신 쪽 설정이면 출력 채널을 가리킨다. 아래 표의 이름은 이 과목 실습 쪽에서 쓰는 이름이다.
| 하는 일 | 실습 쪽 이름 | ad9361-phy의 채널·속성 |
장치에서 바뀌는 곳 |
|---|---|---|---|
| 수신 중심 주파수 | rx_lo |
altvoltage0 · frequency |
수신 믹서의 LO |
| 송신 중심 주파수 | tx_lo |
altvoltage1 · frequency |
송신 믹서의 LO |
| 표본화율 | sample_rate |
입력 voltage0 · sampling_frequency |
ADC, 디지털 필터, 폭 |
| 수신 RF 대역폭 | rx_rf_bandwidth |
입력 voltage0 · rf_bandwidth |
ADC 앞 아날로그 필터 |
| 송신 RF 대역폭 | tx_rf_bandwidth |
출력 voltage0 · rf_bandwidth |
DAC 뒤 아날로그 필터 |
| 이득 방식 | rx_gain_mode |
입력 voltage0 · gain_control_mode |
수신 이득을 누가 정하는지 |
| 수신 이득 | rx_hardwaregain |
입력 voltage0 · hardwaregain |
수신 증폭(dB) |
| 송신 감쇠 | tx_hardwaregain |
출력 voltage0 · hardwaregain |
송신 출력(0 … −89.75 dB) |
| 캡처 길이 | rx(n)의 n |
속성 없음(cf-ad9361-lpc 버퍼 크기) |
한 번에 받는 |
이득 방식은 manual, slow_attack, fast_attack, hybrid 가운데 하나이고, 수신 이득은 manual일 때만 쓴 값이 적용된다. 실습 쪽은 다른 방식에서 rx_hardwaregain을 쓰면 오류를 내고, pyadi-iio는 오류 없이 무시한다. 송신 감쇠는 0.25 dB 간격으로 정한다.
pyadi-iio도 LO, 표본화율, RF 대역폭은 같은 이름을 쓴다. 이득은 끝에 _chan0을 붙인 gain_control_mode_chan0, rx_hardwaregain_chan0, tx_hardwaregain_chan0이고, 캡처 길이는 rx_buffer_size로 미리 정한다. 강의 자료의 pyadi-iio 코드에 이득과 캡처 길이를 더하면 다음과 같다(강의 자료 WE5 15쪽).
import adi
sdr = adi.Pluto() # USB로 이어진 Pluto를 찾는다
sdr.sample_rate = int(4e6) # fs = 4 MS/s
sdr.rx_lo = int(2.44e9) # 중심 주파수 2.44 GHz
sdr.rx_rf_bandwidth = int(4e6) # 아날로그 필터 ±2 MHz
sdr.gain_control_mode_chan0 = "manual" # 이득을 고정한다
sdr.rx_hardwaregain_chan0 = 30 # 수신 이득 30 dB
sdr.rx_buffer_size = 2**16 # N = 65,536
x = sdr.rx() # N개 한 캡처, 값은 ADC 코드
이 과목의 실습 쪽은 브라우저 안의 Python에서 도우미 sdrlab의 pluto로 장치를 부른다. 같은 설정과 캡처는 다음과 같다.
from sdrlab import pluto
sdr = await pluto.connect() # 실습 쪽의 Pluto 연결
applied = await sdr.configure(sample_rate=4e6, rx_lo=2.44e9,
rx_rf_bandwidth=4e6,
rx_gain_mode="manual",
rx_hardwaregain=30)
print(applied) # 장치가 실제로 적용한 값
x = await sdr.rx(2**16) # N = 65,536개 한 캡처
장치와 주고받는 호출에는 await를 붙인다. 설정은 configure()로 한 번에 쓰고, 이 함수는 장치에서 다시 읽은 값을 돌려준다. 캡처 길이는 미리 정하지 않고 rx(n)에 준다. rx(n)이 돌려주는 복소 표본(complex64)은 ADC 최대값이 1.0이 되도록 나눈 값이라, 크기를 곧바로 dBFS로 읽을 수 있다. pyadi-iio의 rx()는 ADC 코드값을 그대로 돌려주므로, 두 쪽의 값을 비교할 때는 ADC 최대값으로 나눠 맞춘다.
설정할 때 알아 둘 것
이득을 비교하는 실습에서는 manual 모드로 이득을 고정한다. slow_attack 같은 자동 이득 제어(AGC)에서는 칩이 평균 세기를 정해진 범위에 두려고 이득을 스스로 바꾸므로, 캡처마다 크기의 기준이 달라진다.
받은 세기는 dBFS, 곧 ADC가 나타낼 수 있는 최대값을 0 dB로 둔 값으로 읽는다. 수신 이득과 케이블 손실을 따로 교정하지 않으면 dBm으로 바꿀 수 없다.
장치는 요청한 값을 자기가 낼 수 있는 가장 가까운 값으로 맞춘다. 예를 들어 LO는 2.4 Hz 간격으로만 정해진다. 그래서 실습 쪽은 설정을 쓴 뒤 다시 읽은 값을 기록한다. 2.083 MS/s보다 낮은 표본화율은 칩 안 FIR 필터를 함께 설정해야 쓸 수 있다. pyadi-iio는 sample_rate를 쓸 때 FIR 필터를 함께 설정하고, 이 과목의 실습 쪽 첫 판은 MS/s만 받는다.
이 과목의 송신 규칙
송신은 다른 사람의 무선 기기와 같은 전파를 쓰므로 규칙을 둔다. 실습 쪽은 이 규칙을 벗어나는 설정을 받지 않는다.
- 송신은 2.4 GHz ISM 대역(2400–2483.5 MHz) 안에서만 한다. Pluto의 기준 발진기는 ±25 ppm까지 어긋날 수 있어(대역 끝에서 약 ±62 kHz) 실제 반송파가 설정값과 다를 수 있으므로, 양 끝 100 kHz를 비운 2400.1–2483.4 MHz를 쓴다. 톤 하나가 아니라 신호가 차지하는 대역 전체가 이 안에 있어야 한다. 기저대역에서 에 폭 인 신호를 두면 RF에서 를 차지한다.
- 송신 감쇠는 10 dB 이상, 곧
tx_hardwaregain을 −10 dB 이하로 둔다. ADI는 기본값 −10 dB에서 출력단이 선형 영역에 있고, 이 값에서는 TX를 SMA 케이블로 RX에 바로 이어도 안전하다고 적는다. 그보다 작은 감쇠로 TX를 RX에 직결하지 말라고도 적는다. - 한 Pluto의 TX를 RX로 돌려 받는 루프백은 SMA 케이블 사이에 30 dB 감쇠기를 넣는다.
아래 그림은 루프백 연결이다. TX 포트에서 나온 신호가 감쇠기를 지나 같은 장치의 RX 포트로 돌아오는 길을 따라가며, 그 길에 걸린 조건을 본다.
기저대역에서 를 보내면 4장의 규칙(실수 RF = I cos − Q sin)에 따라 RF에서 에 선다. 장치에서 이 부호가 맞는지는 실습 1에서 루프백으로 확인한다.
강의 노트