mirror of
https://github.com/NawfalMotii79/PLFM_RADAR.git
synced 2026-06-09 15:07:14 +00:00
fix(radar): RX chain corrections, GUI bin alignment, MCU boot ordering
FPGA — RX chain
matched_filter_multi_segment.v: drop the gratuitous /4 scaling on
DDC sign-extended input (was ddc_i[17:2] + ddc_i[1]); use
ddc_i[15:0] directly. fft_engine has INTERNAL_W=32 with
saturating 16-bit output, so full 16-bit input is safe. Restores
~12 dB of MF input dynamic range.
radar_receiver_final.v: remove latency_buffer (count-N-pulses-then-
prime FIFO that left frame 1 with all-zero ref). Replaced with
a single-FF alignment register on ref_i/ref_q that matches the
1-FF stage multi_segment ST_PROCESSING uses on adc_data.
Verified by tb/tb_rxb_fullchain_latency.v — autocorrelation peak
at bin 0 with peak/mean ~88x.
doppler_processor.v / mti_canceller.v / cfar_ca.v /
range_bin_decimator.v / radar_receiver_final.v / radar_system_top.v
/ usb_data_interface_ft2232h.v: switch port and parameter widths
from RP_NUM_RANGE_BINS / RP_RANGE_BIN_BITS (always 512 / 9-bit)
to RP_MAX_OUTPUT_BINS / RP_RANGE_BIN_WIDTH_MAX (auto-scales:
50T 512 / 9-bit, 200T 4096 / 12-bit). Unblocks 200T 20 km mode
at the RX module boundary; USB wire-protocol extension still
pending.
radar_receiver_final.v: doppler_frame_done_prev reset value 0 -> 1
to prevent false done pulse on cycle 1 when level signal is
HIGH at reset.
matched_filter_processing_chain.v: delete the broken `ifdef
SIMULATION inline behavioural FFT (482 lines removed). It
produced wrong-bin peaks and 100-1000x weak magnitudes. Chain
now uses production fft_engine.v + frequency_matched_filter.v
in both iverilog and Vivado. Iverilog tests are ~38x slower per
chain pass but produce correct results. Misleading "OK with
Xilinx IP" comments at three test sites updated since the FFT
is in-house, not an IP placeholder.
FPGA — testbenches
tb/tb_rxb_latency_measure.v (new): measures chain internal pipeline
depth (~2057 cycles, chirp-agnostic).
tb/tb_rxb_fullchain_latency.v (new): full-chain autocorrelation
verification — drives ddc with the same chirp samples the loader
serves as ref, finds peak position and peak/mean.
tb/tb_matched_filter_processing_chain.v: wait timeouts bumped
50000 -> 500000 cycles to accommodate production FFT pipeline.
MCU
main.cpp checkSystemHealthStatus: latch system_emergency_state on
the error_count > 10 path so the SAFE-MODE blink loop in main()
actually engages (was bypassed because predicate was false).
main.cpp: move FPGA reset BEFORE the if(PowerAmplifier) block so
adar_tr_x is driven LOW (RX commanded externally) before PA Vdd
reaches 22 V. Old reset block at the original location removed.
main.cpp MX_GPIO_Init: add GPIO_PIN_12 (FPGA reset) to the
explicit WritePin(LOW) list so the safe initial state is no
longer implicit.
main.cpp checkSystemHealth: rate-limit ADAR1000
verifyDeviceCommunication (HAL_Delay 1ms x 4 devices = 4 ms
blocking SPI burst per main-loop iteration) from every-loop to
every 2 s. readTemperature stays per-loop so over-temp
detection latency is unchanged.
USBHandler.cpp processSettingsData: dispatch threshold bumped
74 -> 82 (matches parser minimum); buffer drained after parse
attempt (slide remaining bytes left) so a false END find no
longer sticks the buffer until 256-byte overflow.
GUI
radar_protocol.py: NUM_RANGE_BINS 64 -> 512 (matches FPGA
RP_NUM_RANGE_BINS); NUM_CELLS 2048 -> 16384.
radar_protocol.py _ingest_sample: honor FPGA frame_start bit for
resync after a USB drop; capture range_profile[rbin] once per
range bin at dbin == 0 (FPGA emits the same range_i/range_q for
all 32 Doppler cells of a given range bin; previous accumulator
inflated the profile 32x).
v7/models.py RadarSettings: range_resolution 24 -> 6 m (matches
c/(2*100MHz)*4); max_distance and coverage_radius 1536 -> 3072 m;
map_size 2000 -> 4000.
v7/models.py WaveformConfig: n_range_bins 64 -> 512, fft_size
1024 -> 2048, decimation_factor 16 -> 4.
GUI_V65_Tk.py: _RANGE_PER_BIN math and stale "~24 m / ~1536 m"
comments updated.
test_v7.py: assertion values updated to match new defaults.
Tests
test_ddc_cosim_fuzz.py: remove unused os/tempfile imports, wrap
three long lines for ruff E501 compliance.
This commit is contained in:
@@ -77,20 +77,24 @@ void USBHandler::processSettingsData(const uint8_t* data, uint32_t length) {
|
||||
DIAG("USB", " settings buffer: +%lu bytes, total=%lu/%u", (unsigned long)bytes_to_copy, (unsigned long)buffer_index, MAX_BUFFER_SIZE);
|
||||
|
||||
// Check if we have a complete settings packet (contains "SET" and "END")
|
||||
if (buffer_index >= 74) { // Minimum size for valid settings packet
|
||||
// Minimum valid packet is "SET" + 9 doubles + 1 uint32 + "END" = 82 bytes
|
||||
// (matches RadarSettings::parseFromUSB length check).
|
||||
if (buffer_index >= 82) {
|
||||
// Look for "SET" at beginning and "END" somewhere in the packet
|
||||
bool has_set = (memcmp(usb_buffer, "SET", 3) == 0);
|
||||
bool has_end = false;
|
||||
|
||||
uint32_t packet_len = 0;
|
||||
|
||||
DIAG_BOOL("USB", " packet starts with SET", has_set);
|
||||
|
||||
|
||||
for (uint32_t i = 3; i <= buffer_index - 3; i++) {
|
||||
if (memcmp(usb_buffer + i, "END", 3) == 0) {
|
||||
has_end = true;
|
||||
DIAG("USB", " END marker found at offset %lu, packet_len=%lu", (unsigned long)i, (unsigned long)(i + 3));
|
||||
|
||||
packet_len = i + 3;
|
||||
DIAG("USB", " END marker found at offset %lu, packet_len=%lu", (unsigned long)i, (unsigned long)packet_len);
|
||||
|
||||
// Parse the complete packet up to "END"
|
||||
if (has_set && current_settings.parseFromUSB(usb_buffer, i + 3)) {
|
||||
if (has_set && current_settings.parseFromUSB(usb_buffer, packet_len)) {
|
||||
current_state = USBState::READY_FOR_DATA;
|
||||
DIAG("USB", " Settings parsed OK, state -> READY_FOR_DATA");
|
||||
} else {
|
||||
@@ -99,11 +103,19 @@ void USBHandler::processSettingsData(const uint8_t* data, uint32_t length) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If we didn't find a valid packet but buffer is full, reset
|
||||
if (buffer_index >= MAX_BUFFER_SIZE && !has_end) {
|
||||
|
||||
// [MCU-N9 FIX] Drain the consumed packet bytes (or false-positive END)
|
||||
// so a parse failure doesn't leave the buffer stuck on the same bytes
|
||||
// until MAX_BUFFER_SIZE overflow. Slide any remaining bytes left.
|
||||
if (has_end && packet_len > 0) {
|
||||
uint32_t remaining = buffer_index - packet_len;
|
||||
if (remaining > 0) {
|
||||
memmove(usb_buffer, usb_buffer + packet_len, remaining);
|
||||
}
|
||||
buffer_index = remaining;
|
||||
} else if (buffer_index >= MAX_BUFFER_SIZE) {
|
||||
DIAG_WARN("USB", " Buffer full (%u) without END marker -- resetting", MAX_BUFFER_SIZE);
|
||||
buffer_index = 0; // Reset buffer to avoid overflow
|
||||
buffer_index = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -685,11 +685,21 @@ SystemError_t checkSystemHealth(void) {
|
||||
}
|
||||
|
||||
// 3. Check ADAR1000 Communication and Temperature
|
||||
// [MCU-N7 FIX] verifyDeviceCommunication() writes the SCRATCHPAD register
|
||||
// and HAL_Delay(1) per device. Across 4 devices that is >=4 ms of
|
||||
// blocking SPI per main-loop iteration (chirp jitter source). Rate-limit
|
||||
// the comm check to every 2 s (matches the clock-check pattern at line
|
||||
// 658). readTemperature() is single-register SPI read with no HAL_Delay,
|
||||
// so it stays per-loop to keep PA over-temperature detection responsive.
|
||||
static uint32_t last_adar_comm_check = 0;
|
||||
bool run_comm_check = (HAL_GetTick() - last_adar_comm_check > 2000);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (!adarManager.verifyDeviceCommunication(i)) {
|
||||
current_error = ERROR_ADAR1000_COMM;
|
||||
DIAG_ERR("BF", "Health check: ADAR1000 #%d comm FAILED", i);
|
||||
return current_error;
|
||||
if (run_comm_check) {
|
||||
if (!adarManager.verifyDeviceCommunication(i)) {
|
||||
current_error = ERROR_ADAR1000_COMM;
|
||||
DIAG_ERR("BF", "Health check: ADAR1000 #%d comm FAILED", i);
|
||||
return current_error;
|
||||
}
|
||||
}
|
||||
|
||||
float temp = adarManager.readTemperature(i);
|
||||
@@ -699,6 +709,9 @@ SystemError_t checkSystemHealth(void) {
|
||||
return current_error;
|
||||
}
|
||||
}
|
||||
if (run_comm_check) {
|
||||
last_adar_comm_check = HAL_GetTick();
|
||||
}
|
||||
|
||||
// 4. Check IMU Communication
|
||||
static uint32_t last_imu_check = 0;
|
||||
@@ -949,10 +962,19 @@ bool checkSystemHealthStatus(void) {
|
||||
DIAG_ERR("SYS", "checkSystemHealthStatus: error detected (code %d), calling handleSystemError()", error);
|
||||
handleSystemError(error);
|
||||
|
||||
// If we're in emergency state or too many errors, shutdown
|
||||
// If we're in emergency state or too many errors, shutdown.
|
||||
// [MCU-N1 FIX] Latch system_emergency_state=true on the error_count>10
|
||||
// path too — otherwise the SAFE-MODE blink loop in main() exits in one
|
||||
// pass (its predicate is `while(system_emergency_state)`) and the main
|
||||
// loop continues running with PA rails already cut by
|
||||
// systemPowerDownSequence(), still toggling new_chirp via PD8.
|
||||
if (system_emergency_state || error_count > 10) {
|
||||
DIAG_ERR("SYS", "checkSystemHealthStatus returning FALSE (emergency=%s error_count=%lu)",
|
||||
system_emergency_state ? "true" : "false", error_count);
|
||||
if (!system_emergency_state) {
|
||||
system_emergency_state = true;
|
||||
DIAG_ERR("SYS", "Latching system_emergency_state due to error_count > 10");
|
||||
}
|
||||
DIAG_ERR("SYS", "checkSystemHealthStatus returning FALSE (emergency=true error_count=%lu)",
|
||||
error_count);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1834,6 +1856,24 @@ int main(void)
|
||||
* the MCU at boot indefinitely. The USB settings handshake (if ever
|
||||
* re-enabled) should be handled non-blocking in the main loop. */
|
||||
|
||||
/***************************************************************/
|
||||
/************ FPGA reset (BEFORE PA Vdd enable) ****************/
|
||||
/***************************************************************/
|
||||
/* [MCU-N2/N11 FIX] Reset FPGA early — before any PA-rail enables —
|
||||
* so `adar_tr_x` is driven LOW (RX commanded externally) when the PA Vdd
|
||||
* rail later comes up to 22 V. Without this, PA could be energised while
|
||||
* the FPGA is still in its implicit reset and `adar_tr_x` is undefined,
|
||||
* with the ADAR1000 already commanded to TX (TR_SOURCE=1) — a glitch
|
||||
* could key the PA into an undefined antenna load. Kept outside the
|
||||
* `if (PowerAmplifier)` block so the FPGA always boots cleanly even when
|
||||
* the PA path is disabled for bench testing. TX mixer enable (PD11) is
|
||||
* still LOW (set by MX_GPIO_Init), so no chirps fire. */
|
||||
DIAG("FPGA", "Resetting FPGA (GPIOD pin 12: LOW -> 10ms -> HIGH)");
|
||||
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12, GPIO_PIN_RESET);
|
||||
HAL_Delay(10);
|
||||
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12, GPIO_PIN_SET);
|
||||
DIAG("FPGA", "FPGA reset complete -- adar_tr_x driven LOW (RX commanded)");
|
||||
|
||||
/***************************************************************/
|
||||
/************RF Power Amplifier Powering up sequence************/
|
||||
/***************************************************************/
|
||||
@@ -1891,6 +1931,10 @@ int main(void)
|
||||
HAL_GPIO_WritePin(DAC_2_VG_LDAC_GPIO_Port, DAC_2_VG_LDAC_Pin, GPIO_PIN_SET);
|
||||
|
||||
//Enable RF Power Amplifier VDD = 22V
|
||||
/* [MCU-N2/N11] FPGA has already been reset earlier (before this PA block)
|
||||
* so `adar_tr_x` is now driven LOW (RX commanded). Safe to bring PA Vdd
|
||||
* up to 22 V here. TX mixer enable (PD11) is still LOW until later,
|
||||
* gating any FPGA-driven chirps. */
|
||||
DIAG("PA", "Enabling RFPA VDD=22V (EN_DIS_RFPA_VDD HIGH)");
|
||||
HAL_GPIO_WritePin(EN_DIS_RFPA_VDD_GPIO_Port, EN_DIS_RFPA_VDD_Pin, GPIO_PIN_SET);
|
||||
|
||||
@@ -1971,12 +2015,10 @@ int main(void)
|
||||
DIAG("PA", "PA IDQ calibration sequence COMPLETE");
|
||||
}
|
||||
|
||||
//RESET FPGA
|
||||
DIAG("FPGA", "Resetting FPGA (GPIOD pin 12: LOW -> 10ms -> HIGH)");
|
||||
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12, GPIO_PIN_RESET);
|
||||
HAL_Delay(10);
|
||||
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_12, GPIO_PIN_SET);
|
||||
DIAG("FPGA", "FPGA reset complete");
|
||||
/* [MCU-N2/N11] FPGA was already reset earlier in the boot sequence,
|
||||
* before PA Vdd was energised, to avoid an undefined `adar_tr_x` window.
|
||||
* No further reset needed here. Leaving the comment so future readers
|
||||
* understand why this block looks like it should be present. */
|
||||
|
||||
|
||||
|
||||
@@ -2730,7 +2772,7 @@ static void MX_GPIO_Init(void)
|
||||
|EN_P_3V3_VDD_SW_Pin, GPIO_PIN_RESET);
|
||||
|
||||
/*Configure GPIO pin Output Level */
|
||||
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11
|
||||
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12
|
||||
|STEPPER_CW_P_Pin|STEPPER_CLK_P_Pin|EN_DIS_RFPA_VDD_Pin|EN_DIS_COOLING_Pin, GPIO_PIN_RESET);
|
||||
|
||||
/*Configure GPIO pin Output Level */
|
||||
|
||||
Reference in New Issue
Block a user