위상 변조 신호의 원리
위상 변조(PM)는 정보를 반송파의 위상 변화에 저장하는 변조 방식입니다. 수학적으로 다음과 같이 표현됩니다:
\[s(t) = A \cdot \cos(\omega_c t + K_f \cdot m(t))\]여기서:
- \(A\): 반송파 진폭
- \(m(t)\): 기저대역 신호
- \(\omega_c\): 반송파 각주파수
- \(K_f\): 변조 감도
직교 변조 표현식
PM 신호는 동상(I)과 직교(Q) 성분으로 분해할 수 있습니다:
\[ \begin{align*} I(t) &= \cos(K_f \cdot m(t)) \\ Q(t) &= \sin(K_f \cdot m(t)) \\ s(t) &= A \cdot [I(t) \cdot \cos(\omega_c t) - Q(t) \cdot \sin(\omega_c t)] \end{align*} \]디지털 직교 복조 원리
수신된 신호에 대해 I/Q 성분을 분리합니다(\(\varphi\)는 위상 오차):
I 채널 처리
\[ I[n] = \text{LPF}\{s[n] \cdot \cos(\omega_c n + \varphi)\} = \frac{1}{2} \cos(K_f \cdot m[n] - \varphi) \]Q 채널 처리
\[ Q[n] = \text{LPF}\{s[n] \cdot \sin(\omega_c n + \varphi)\} = \frac{1}{2} \sin(K_f \cdot m[n] - \varphi) \]위상 복원
\[ M[n] = \arctan\left(\frac{Q[n]}{I[n]}\right) = K_f \cdot m[n] - \varphi \]실제 구현에서는 atan2 함수를 사용해 사분면 문제를 해결합니다:
\[ M[n] = \text{atan2}(Q[n], I[n]) \]MATLAB 시뮬레이션
sampleRate = 20000; % 샘플링 주파수
numPoints = 1000; % 데이터 포인트 수
baseFreq = 100; % 기저 주파수
carrierFreq = 500; % 반송파 주파수
time = (0:numPoints-1) / sampleRate;
baseSignal = cos(2*pi*baseFreq*time);
phaseConst = 2;
phaseOffset = pi/3;
% I/Q 신호 생성
I_comp = cos(phaseConst * baseSignal + phaseOffset);
Q_comp = sin(phaseConst * baseSignal + phaseOffset);
% 변조 신호 생성
modSignal = I_comp .* cos(2*pi*carrierFreq*time) - ...
Q_comp .* sin(2*pi*carrierFreq*time);
% 복조 (atan2 사용)
demodPhase = atan2(Q_comp, I_comp);
% 위상 언래핑
for idx = 2:length(demodPhase)
if demodPhase(idx) - demodPhase(idx-1) > pi
demodPhase(idx:end) = demodPhase(idx:end) - 2*pi;
elseif demodPhase(idx) - demodPhase(idx-1) < -pi
demodPhase(idx:end) = demodPhase(idx:end) + 2*pi;
end
end
% DC 오프셋 제거
dcComp = mean(demodPhase);
demodBase = demodPhase - dcComp;
FPGA 구현
하드웨어 설계
module phase_demod (
input clock,
input reset,
input data_valid,
input [15:0] i_component,
input [15:0] q_component,
output output_ready,
output [15:0] demod_output
);
wire cordic_valid;
wire [23:0] cordic_i_in;
wire [23:0] cordic_q_in;
wire cordic_ready;
wire [47:0] cordic_result;
// 데이터 형식 확장
assign cordic_valid = data_valid;
assign cordic_i_in = {{8{i_component[15]}}, i_component};
assign cordic_q_in = {{8{q_component[15]}}, q_component};
// CORDIC 프로세서
cordic_arctan arctan_unit (
.clk(clock),
.valid_in(cordic_valid),
.x_in(cordic_i_in),
.y_in(cordic_q_in),
.valid_out(cordic_ready),
.angle_out(cordic_result)
);
assign demod_output = cordic_result[39:24];
assign output_ready = cordic_ready;
endmodule
테스트벤치
module demod_testbench;
reg clk, rst;
reg valid;
reg [15:0] i_data, q_data;
wire ready;
wire [15:0] out_data;
phase_demod demod_uut (
.clock(clk),
.reset(rst),
.data_valid(valid),
.i_component(i_data),
.q_component(q_data),
.output_ready(ready),
.demod_output(out_data)
);
initial begin
clk = 0;
rst = 1;
#300 rst = 0;
end
always #50 clk = ~clk;
integer data_file;
integer count;
initial begin
data_file = $fopen("IQ_data.txt", "r");
count = 0;
end
always @(posedge clk) begin
if (!rst && count < 1000) begin
valid = 1;
$fscanf(data_file, "%d %d", i_data, q_data);
count = count + 1;
end else if (count >= 1000) begin
$fclose(data_file);
$finish;
end
end
endmodule