Лабораторная 3.3 — Digital mixing and frequency shift¶
Lab 3.3 — Digital Mixing and Frequency Shift¶
Goal¶
Shift a complex IQ signal in frequency using a numerically controlled oscillator (NCO) and complex multiplication.
This lab is the direct bridge between SDR software processing and FPGA blocks such as DDS/NCO, complex multiplier, frequency translator and digital downconverter.
Theory¶
Digital mixing multiplies the input complex signal by a complex exponential:
y[n] = x[n] * exp(j * 2*pi*f_shift*n/Fs)
For a complex baseband signal, this shifts the spectrum by f_shift.
Important concepts:
- complex sinusoid;
- NCO / DDS;
- phase accumulator;
- frequency resolution;
- complex multiplication;
- positive and negative frequency shifts;
- spectral images and wraparound;
- fixed-point phase and sine/cosine representation.
Experiment¶
Generate or load an IQ signal with a tone or modulated waveform located away from DC.
Then:
- estimate or define the input frequency offset;
- generate an NCO;
- multiply the signal by the NCO;
- compare spectra before and after frequency shift;
- verify that the target signal moves to the expected frequency;
- discuss NCO frequency resolution and fixed-point phase accumulator width.
Python implementation¶
Minimum expected script structure:
import numpy as np
import matplotlib.pyplot as plt
fs = 2.4e6
n = 32768
t = np.arange(n) / fs
f0 = 420e3
x = np.exp(1j * 2*np.pi*f0*t)
f_shift = -420e3
nco = np.exp(1j * 2*np.pi*f_shift*t)
y = x * nco
freq = np.fft.fftshift(np.fft.fftfreq(n, d=1/fs))
X = np.fft.fftshift(np.fft.fft(x * np.hanning(n)))
Y = np.fft.fftshift(np.fft.fft(y * np.hanning(n)))
plt.figure()
plt.plot(freq/1e3, 20*np.log10(np.maximum(np.abs(X), 1e-12)), label="before")
plt.plot(freq/1e3, 20*np.log10(np.maximum(np.abs(Y), 1e-12)), label="after")
plt.grid(True)
plt.xlabel("Frequency, kHz")
plt.ylabel("Magnitude, dB")
plt.legend()
plt.show()
MATLAB implementation¶
Minimum expected script structure:
fs = 2.4e6;
N = 32768;
t = (0:N-1).' / fs;
f0 = 420e3;
x = exp(1j*2*pi*f0*t);
fShift = -420e3;
nco = exp(1j*2*pi*fShift*t);
y = x .* nco;
freq = fftshift((-floor(N/2):ceil(N/2)-1).' * fs / N);
X = fftshift(fft(x .* hann(N)));
Y = fftshift(fft(y .* hann(N)));
figure; hold on;
plot(freq/1e3, 20*log10(max(abs(X), 1e-12)), 'DisplayName', 'before');
plot(freq/1e3, 20*log10(max(abs(Y), 1e-12)), 'DisplayName', 'after');
grid on;
xlabel('Frequency, kHz');
ylabel('Magnitude, dB');
legend('Location', 'best');
C++ bridge¶
A future C++ primitive should separate NCO generation from complex multiplication:
struct NcoState {
uint64_t phase;
uint64_t phase_step;
};
std::complex<float> next_nco_sample(NcoState& nco);
std::complex<float> mix_sample(std::complex<float> x, std::complex<float> osc);
Validation tests:
- generated frequency matches target shift;
- phase continuity is preserved;
- positive and negative shifts are correct;
- mixed tone lands at expected FFT bin;
- fixed-point NCO error is bounded.
FPGA / Verilog bridge¶
Hardware mapping:
| Function | RTL block |
|---|---|
| phase accumulation | NCO phase accumulator |
| sine/cosine generation | LUT, CORDIC or vendor DDS |
| complex multiply | four multipliers or optimized three-multiplier form |
| output scaling | fixed-point rounding and saturation |
Streaming interface target:
clk, rst
in_valid, in_i, in_q
out_valid, out_i, out_q
NCO frequency resolution¶
For an accumulator of width A bits:
df = Fs / 2^A
| Accumulator width | Frequency resolution |
|---|---|
| 24 bit | Fs / 16,777,216 |
| 32 bit | Fs / 4,294,967,296 |
| 48 bit | Fs / 281,474,976,710,656 |
Expected plots¶
Produce at least:
- spectrum before mixing;
- spectrum after mixing;
- zoomed view around target frequency;
- optional phase accumulator trace;
- optional NCO quantization error plot.
Report checklist¶
- [ ] State
Fs, input tone frequency and target shift. - [ ] Show the complex NCO equation.
- [ ] Plot spectrum before and after mixing.
- [ ] Verify the final frequency location.
- [ ] Explain positive vs negative frequency shifts.
- [ ] Calculate NCO resolution for at least 32-bit phase accumulator.
- [ ] Explain how the mixer maps to FPGA.
- [ ] Discuss fixed-point scaling and saturation.
Engineering conclusion template¶
Digital mixing shifts the signal from ____ kHz to ____ kHz. In FPGA, the same
operation becomes an NCO plus complex multiplier. The main hardware trade-offs
are phase accumulator width, sine/cosine generation method, multiplier count and
output scaling.