A1แผนความถี่และ chirp ที่ส่งออก
\(s(t)\) คือ complex envelope ของ chirp (ความถี่ขณะหนึ่งกวาดจาก −B/2 ถึง +B/2) ทุกสมการด้านล่างเขียนในรูป envelope นี้ ส่วนความถี่พาหะจะแยกเขียนเป็น \(e^{j2\pi f t}\)

อธิบายว่าสัญญาณที่เข้า RF-ADC หน้าตาเป็นอย่างไร ประกอบด้วยอะไรบ้าง แล้วแต่ละขั้นของ processing ทำอะไรกับมันด้วยสมการอะไร และให้ผลออกมาเป็นอะไร ตัวเลขและกราฟทั้งหมดมาจาก simulation จริงของโปรเจกต์นี้ (config/weather.yaml: B = 10 MHz, T = 200 µs, 64 chirps × 8 dwells, Pt 20 dBm, ฝนแบบ stratiform มองขึ้นฟ้าในแนวดิ่ง)
| ขั้น | สัญญาณ | ชนิด | อัตรา | ต่อ 1 chirp | หน่วย / ความหมาย |
|---|---|---|---|---|---|
| ADC | \(x[n]\) | real, int16 (14 บิต) | 2.5 GS/s | 500,000 | code ADC → mV ที่ขา ADC |
| P1 DDC | \(z[m]\) | complex I/Q | 62.5 MS/s | 12,500 | chirp ที่ย้ายลงมาอยู่ baseband |
| P2–P3 dechirp + LPF | \(b[n]\) | complex | 6.25 MS/s | 1,250 | ผลรวมของ tone: ความถี่ = ระยะ |
| P4 range FFT | \(X_m[k]\) | complex | 1 ค่า/bin/chirp | 401 bin (0–6 km) | ค่าเชิงซ้อนของแต่ละระยะ 15 m |
| P6 Doppler | \(S_k(v)\) | power | 1 ชุด/dwell | 401 × 64 | กำลังตามระยะ × ความเร็ว |
| P6–P10 moments | \(Z, v, \sigma_v, R\) | real | 1 profile/0.1 s | 401 | dBZ, m/s, mm/h ต่อความสูง |
จะเห็นว่าข้อมูลลดลงจาก 500,000 ตัวอย่างต่อ chirp เหลือ 401 ค่า โดยที่ข้อมูลเกี่ยวกับฝนยังอยู่ครบ เพราะแต่ละขั้นตัดเฉพาะแถบความถี่ที่ไม่มีสัญญาณที่เราสนใจทิ้งไป และสิ่งนี้เองที่เพิ่ม SNR ขึ้นทีละขั้น (A6)
ADC ไม่ได้เห็น beat signal แบบเรดาร์ FMCW ทั่วไป มันเห็น chirp ที่ IF เต็มแถบ ซึ่งเป็นผลรวมของสามส่วน คือ TX leakage, echo จากฝน และ noise
\(s(t)\) คือ complex envelope ของ chirp (ความถี่ขณะหนึ่งกวาดจาก −B/2 ถึง +B/2) ทุกสมการด้านล่างเขียนในรูป envelope นี้ ส่วนความถี่พาหะจะแยกเขียนเป็น \(e^{j2\pi f t}\)

สูตรที่สองคือ weather radar equation (Probert-Jones): กำลังจากฝนแปรตาม \(Z/R^2\) ไม่ใช่ \(1/R^4\) เพราะปริมาตรที่ลำคลื่นกวาดโตตาม \(R^2\) ส่วน \(a_c[m]\) เปลี่ยนจาก chirp ถึง chirp ตาม Doppler spectrum แบบ Gaussian (mean \(v\), width \(\sigma_v\)) โดยใช้วิธีของ Zrnić ส่วน PIA คือ attenuation สองทางจากฝนระหว่างทาง

\(G\) = gain จาก antenna ถึง ADC (สมมติ 20 dB), \(F\) = NF ของ mixer (สมมติ 10 dB), \(\mu,\nu\) มาจาก IQ imbalance 0.5 dB / 4° และ \(\mathcal{P}_+\) หมายถึงเก็บเฉพาะความถี่บวก noise ของ ADC (NSD −150 dBFS/Hz) บวกเข้ามาอีกทำให้ NF รวมเพิ่มขึ้น 1.2 dB


รวม A1–A3 แล้วผ่าน BPF ที่เลือก Nyquist zone 3 (2.5–3.75 GHz) จะได้สัญญาณแอนะล็อกที่ ADC รับจริง:
กล่าวคือ ADC เห็นคลื่น cosine ที่ 3.124 GHz ความถี่เลื่อน ±5 MHz ตาม chirp แอมพลิจูดเกือบคงที่ 325 mV peak (มาจาก leakage) บนนั้นมี echo ฝนขนาดเล็กมากกับ noise ขี่อยู่ ใน time domain จึงแทบมองไม่เห็นฝนเลย


int16 code ช่วง ±8192, 500,000 ตัวอย่างต่อ chirp (35 Gb/s ต่อช่อง) นี่คือ raw IF data ที่จะเก็บจาก ZCU216zone ที่ \(z = \lfloor f_{IF}/(f_s/2)\rfloor + 1 = 3\) เป็น zone คี่ สเปกตรัมจึงไม่กลับด้าน ถ้า chirp คร่อมขอบ zone (เช่นใช้ PLL 8.54 GHz ในโหมด 1 GHz) ส่วนที่ล้นเข้า zone 4 จะพับกลับด้านมาทับกัน


ติดตาม echo จาก bin 15 m ที่ 1 km (−94.6 dBm ที่ ADC) ผ่านทุกขั้น ทุกขั้นที่แคบแถบลงช่วยตัด noise ออก สัญญาณจึงค่อย ๆ โผล่ขึ้นจาก noise

โค้ดส่วนนี้ใช้ร่วมกันระหว่าง simulation กับข้อมูลจริง (radar_sim/processing/) ขั้น P1 อาจทำใน DDC ของ RFSoC หรือในซอฟต์แวร์ก็ได้ ขั้นอื่นทำใน FPGA หรือ PC
radar_sim/adc.py: ddc(), io/capture.py: ddc_stream()ค่า \(\sqrt{2}\) ทำให้กำลังของ complex เท่ากับกำลังของ real (\(|z|^2 = P\)) หลัง DDC เราได้ complex envelope กลับคืนมาครบ ทั้ง phase และ amplitude ซึ่งเป็นสิ่งที่ Doppler ต้องใช้



processing/dechirp.py: dechirp()ความถี่ของ tone บอกระยะ และ phase ที่เปลี่ยนจาก chirp ถึง chirp บอกความเร็ว (ใช้ใน P6) พจน์ \(-2v/\lambda\) คือ range–Doppler coupling ที่ 6 m/s ทำให้ระยะเพี้ยนประมาณ 4.5 m (0.3 bin) \(\Delta f\) ใน reference ต้องตรงกับ offset ของ NCO ไม่เช่นนั้นทุก tone จะเลื่อน


processing/dechirp.py: beat_decimate()
processing/range_fft.pytone ที่อยู่นิ่งตลอด chirp มีพลังงานรวมกันแบบ coherent ใน bin เดียว ขณะที่ noise กระจายไปทุก bin ขั้นนี้จึงให้ gain มากที่สุด (+28 dB)


ข้อจำกัด: เป้าที่ความเร็วใกล้ 0 (เช่น เมฆที่แทบไม่ตก) ถูกตัดไปด้วยบางส่วน และส่วนที่ leakage แกว่งระหว่าง chirp (สมมติ −60 dBc) จะเหลือค้างเป็น residual

processing/doppler.pyphase ที่หมุนระหว่าง chirp บอกความเร็วเฉลี่ย และการที่ \(|R_1|\) น้อยกว่า \(S\) (สัญญาณ decorrelate) บอกความกว้างของ spectrum ถ้าความเร็วเกิน ±vmax phase จะหมุนเกิน ±180° และค่าที่ได้จะกลับฝั่ง (velocity aliasing)


threshold −6 dB ใช้ได้เพราะเฉลี่ย 8 × 64 chirp ทำให้ค่า noise ที่ประมาณได้แกว่งน้อยกว่า −13 dB

processing/calibration.py: reflectivity()\(\Delta R_{eff}\) ใหญ่กว่า 15 m เพราะ window ทำให้แต่ละ bin รับฝนจากช่วงที่กว้างกว่า bin ส่วน \(L_{ov}\) ชดเชยช่วง τ แรกที่ไม่มี beat (P2) ค่า cal ได้จากการ calibrate ด้วย corner reflector

\(k = 0.263 R^{0.979}\) ที่ ~37 GHz และ \(Z = 200R^{1.6}\) วิธีนี้ไม่เสถียรเมื่อ attenuation สูง จึงจำกัดการแก้ไว้ไม่เกิน 10 dB


นำ Doppler spectrum ของทุก bin มาวางเรียงตามความสูงจะได้ภาพที่อ่านโครงสร้างของเมฆฝนได้โดยตรง: ฝนตก 6–7 m/s และ spectrum กว้าง, bright band คือจุดที่ความเร็วกระโดดจาก ~1.5 เป็น ~6 m/s และ Z มียอด, ส่วนหิมะตกช้าราว 1 m/s

ในส่วน A–B ใช้ RX ช่องเดียว ส่วนนี้ใช้ครบทั้ง 4 TX × 4 RX เพื่อแยกทิศทาง (angle) ภายในลำคลื่น 20° ของแต่ละ sub-array ตัวเลขมาจาก config/weather_mimo.yaml (ฝนเดิม มีลมพัดตามแกน x 2 → 9 m/s) และ config/lab_mimo.yaml (corner reflector 2 ตัว)
ประโยชน์หลักของ MIMO สำหรับเรดาร์ตรวจฝนแบบชี้ขึ้นจึงไม่ใช่ภาพที่คมขึ้น แต่คือการได้ radial velocity ที่หลายมุมพร้อมกัน ซึ่งเอาไปคำนวณลมแนวนอนได้ (C7)
radar_sim/mimo.py, processing/mimo.py: mimo_profiles(), to_virtual()เพราะ TX ห่างกัน 2λ = 4 × ระยะห่าง RX ผลรวมตำแหน่ง TX+RX จึงเรียงเป็น array 16 ตัวที่ห่างกัน λ/2 พอดี phase ที่ต่างกันระหว่าง element เป็นเส้นตรงตาม \(\sin\theta\) (หลัง dechirp เครื่องหมายกลับเป็นลบเพราะมี conjugate)

*_cal.npy หรือ mimo.calibration ใน configscripts/calibrate_mimo.py, processing/mimo.py: estimate_calibration()ถ้าไม่ calibrate ความผิดพลาดของ phase ±25° ต่อช่องจะทำให้เกิด sidelobe สูงถึง −7 dB และเกิดเป้าปลอม หลัง calibrate ค่าที่ประมาณได้ต่างจากค่าจริงไม่เกิน 3° ต้อง calibrate ใหม่เมื่อเปลี่ยนสาย อุณหภูมิเปลี่ยน หรือ reset PLL

ถ้าไม่ชดเชย phase error \(2\pi f_D t T\) จะทำให้มุมเพี้ยนตามความเร็ว ในการทดสอบ เป้าที่ 10° ความเร็ว ±7 m/s: ชดเชยแล้วได้ 10.8° (ความละเอียดของ grid 1.8°) ถ้าไม่ชดเชยได้ 12.6° / 7.2° ถ้า Doppler alias การชดเชยจะผิดไปด้วย จึงต้องเลือก chirp ให้ vmax พอ
\(Z_{beam}\) คือสูตร P8 เดิม (ที่ถือว่ากำลังมาจากทั้ง beam) พจน์ที่เพิ่มเข้ามาเปลี่ยนเป็นกำลังจาก angle bin เดียว และชดเชยการที่ขอบ beam มี gain ต่ำกว่า

angle bin ที่ขอบ beam (เช่น 18°) ไม่ได้รับกำลังจาก 18° จริง เพราะ element pattern ที่ 18° ต่ำกว่า boresight ถึง −24 dB และ sidelobe ของ array ดึงกำลังจากกลาง beam เข้ามา ความเร็วที่วัดได้ใน bin นั้นจึงเป็นของมุมที่ใกล้ 0° กว่า ถ้าใช้มุมของ bin ตรง ๆ ลมจะออกมาต่ำกว่าจริงประมาณ 30%
ผลคือ bin ±18.2° มีมุมจริง ±12.3° และ bin ±9.0° มีมุมจริง ±6.1° (ถือว่า Z สม่ำเสมอข้าม beam ที่ระยะเดียวกัน) ทดสอบกับฝนที่ไม่มี noise แล้ว θeff ตรงกับค่าที่คำนวณจาก cell ของฝนจริงภายใน 0.7°
processing/mimo.py: retrieve_wind(), scripts/run_mimo.py\(V\) = ความเร็วตกของเม็ดฝน + ลมแนวดิ่ง (สองอย่างนี้แยกกันไม่ได้ด้วยเรดาร์ตัวเดียว) ความไวต่อ noise ของ \(u\) สูงเพราะ \(\sin\theta_{eff}\) มีค่าแค่ ≤ 0.2 จึงต้องเฉลี่ยข้ามหลาย gate และหลาย dwell เช่นเดียวกับ wind profiler ทั่วไป


| งาน | ได้อะไร | ต้องมีอะไร |
|---|---|---|
| Calibrate กับฮาร์ดแวร์จริง | \(c_v\) 16 ค่า และ element pattern \(f(\theta)\) จริงที่ใช้ใน C5–C6 | corner reflector บนแท่นหมุนหรือวางหลายมุม |
| เฉลี่ยนานขึ้น (30 s – 5 min) | u RMSE ต่ำกว่า 0.3 m/s และตรวจจับหิมะได้ | เก็บ capture ต่อเนื่อง หรือทำ P1–P4 ใน FPGA |
| Array แกนที่สอง หรือหมุนเรดาร์ 90° | ลมครบทั้ง u และ v (ทิศและความเร็วลม) | array 2 มิติ หรือวัดสองรอบ |
| ชี้เอียงแล้วหมุน (VAD / PPI / RHI) | ภาพฝนแบบ 3 มิติ และลมจาก VAD | แท่นหมุน (mechanical scan) |
| DDMA แทน TDM | ส่งทุก TX พร้อมกัน ได้ SNR เต็ม แต่ต้องแบ่งช่วง Doppler | phase code ต่อ chirp ที่ DAC |
| ใช้ \(\sigma_v\) ตามมุม | turbulence และ wind shear ในลำคลื่น | SNR สูงหรือเฉลี่ยนาน |
| ขั้น | ปริมาณ | ค่า | มาจาก |
|---|---|---|---|
| A4 | echo ของ bin นี้ที่ขา ADC | −94.6 dBm = 4.2 µV rms = 0.10 LSB | weather radar equation |
| A6 | SNR ที่ ADC (ทั้ง zone) | −42.8 dB | \(P_{bin}/(N_0 \cdot 1.25\ \text{GHz})\) |
| P2 | beat frequency | 334 kHz | \(2SR/c_0\) |
| P4 | \(R_0\) หลัง range FFT | −95.2 dBm | สัญญาณ + noise |
| P7 | noise \(\hat N\) | −104.2 dBm | Hildebrand–Sekhon |
| P7 | \(\hat S = R_0 - \hat N\), SNR | −95.7 dBm, +8.4 dB | |
| P6 | \(\arg R_1\) → \(\hat v\) | −119.3° → 6.62 m/s (จริง 6.70) | \(-\lambda\arg R_1/(4\pi T)\) |
| P6 | \(|R_1|/\hat S\) → \(\hat\sigma_v\) | 0.90 → 1.43 m/s (จริง 1.32) | |
| P8 | \(\hat S / G\) (ที่ antenna) | −115.7 dBm | \(G\) = 20 dB |
| P8 | \(10\log C + 10\log R^2\) | 116.8 + 60.0 dB | \(\Delta R_{eff}\) = 22.5 m |
| P8 | Z ที่วัดได้ | 31.1 dBZ (คาดหวัง 32.3) | −145.7 + 116.8 + 60.0 dB |
| P9 | PIA และ Z หลังแก้ | 2.5 dB → 33.6 dBZ (จริง 34.9) | Hitschfeld–Bordan |
| P10 | rain rate | 4.6 mm/h | \(Z = 200R^{1.6}\) |
ค่าตัวเลขและกราฟทั้งหมดสร้างใหม่ได้ด้วย python scripts/signal_walkthrough.py และ python scripts/run_pipeline.py --config config/weather.yaml ค่า gain, NF, isolation และ ADC full scale ยังเป็นสมมติฐาน (ดู config/default.yaml) เมื่อมีข้อมูลจริง ตัวเลขในส่วน A จะเปลี่ยน แต่สมการทั้งหมดยังคงเหมือนเดิม