mirror of
https://github.com/NawfalMotii79/PLFM_RADAR.git
synced 2026-06-10 23:41:18 +00:00
fix(mcu): MCU-A2 — site-configurable mag declination, persisted in BKPSRAM
The magnetometer yaw correction used a hardcoded -0.61 deg literal baked in for one deployment site. Yaw_Sensor was wrong by (site_decl + 0.61) deg at every other site whenever the UM982 dual-antenna heading was unavailable. Backed the value with BKPSRAM (slots 1+2 — slot 0 is the MCU-A7 emergency flag) and exposed set_mag_declination_deg / get_mag_declination_deg. Default returns the legacy -0.61 deg when no override has been written so the original site stays correct out of the box; a host command (or future GPS-derived auto-calibration) writes the new site value once and it persists across every reset path until main-power removal. Hardened with a +/-30 deg range clamp on both write AND read paths — real magnetic declinations are roughly +/-25 deg worldwide, so a wider value indicates a calibration error or BKPSRAM corruption (VBAT brown-out, bit flip) rather than a legitimate site. Defensive read-side clamp prevents a corrupted slot from propagating a wild heading offset. Replaced the single use site at the magnetometer yaw computation with the getter; legacy global Mag_Declination retained and kept in sync by the setter for any external linkage. Added test_mcu_a2_mag_declination (10 cases): default, set/get, persistence across reset, power-cycle clear, write-side clamp both directions, plausible-site passthrough, defensive read-side clamp on corruption, wrong-magic fallback, pre-fix bearing-error regression. MCU regression now 81/81.
This commit is contained in:
@@ -147,7 +147,14 @@ float M11=1.0, M12=-0.0, M13=0.0,
|
|||||||
float ax, ay, az, gx, gy, gz, mx, my, mz,mxc,myc,mzc; // variables to hold latest sensor data values
|
float ax, ay, az, gx, gy, gz, mx, my, mz,mxc,myc,mzc; // variables to hold latest sensor data values
|
||||||
float q[4] = {1.0f, 0.0f, 0.0f, 0.0f}; // vector to hold quaternion
|
float q[4] = {1.0f, 0.0f, 0.0f, 0.0f}; // vector to hold quaternion
|
||||||
float temperature;
|
float temperature;
|
||||||
float Mag_Declination = -0.61; //0°
|
/* MCU-A2: site-specific magnetic declination. Was a hardcoded -0.61° literal
|
||||||
|
* baked in for one deployment location. Default kept for backward compatibility
|
||||||
|
* with existing sites; runtime override flows through set_mag_declination_deg()
|
||||||
|
* and persists across reset in BKPSRAM (slots 1+2 — slot 0 is the MCU-A7
|
||||||
|
* emergency flag). Reads via get_mag_declination_deg() at the use site. */
|
||||||
|
#define MAG_DECLINATION_DEFAULT_DEG (-0.61f)
|
||||||
|
#define MAG_DECLINATION_LIMIT_DEG (30.0f)
|
||||||
|
float Mag_Declination = MAG_DECLINATION_DEFAULT_DEG; /* legacy global, retained for any external linkage */
|
||||||
|
|
||||||
float RxAcc,RyAcc,RzAcc;
|
float RxAcc,RyAcc,RzAcc;
|
||||||
float Rate_Ayz_1,Rate_Axz_1,Rate_Axy_1;
|
float Rate_Ayz_1,Rate_Axz_1,Rate_Axy_1;
|
||||||
@@ -919,6 +926,46 @@ static bool emergency_persist_check(void) {
|
|||||||
return *EMERGENCY_PERSIST_ADDR == EMERGENCY_PERSIST_MAGIC;
|
return *EMERGENCY_PERSIST_ADDR == EMERGENCY_PERSIST_MAGIC;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// MCU-A2: site-configurable magnetic declination, persisted in BKPSRAM
|
||||||
|
//
|
||||||
|
// Slot layout (BKPSRAM, 4-byte words):
|
||||||
|
// [0] MCU-A7 emergency-state magic
|
||||||
|
// [1] MCU-A2 mag-declination magic
|
||||||
|
// [2] MCU-A2 mag-declination value (float, bit-cast as uint32)
|
||||||
|
//
|
||||||
|
// Survives every reset path; cleared only on main-power removal. Range
|
||||||
|
// clamped to ±30° before persisting (anything beyond that is a calibration
|
||||||
|
// error rather than a legitimate site value — physical declination ranges
|
||||||
|
// are roughly -25° to +25° worldwide).
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
#define MAG_DECL_PERSIST_MAGIC 0xCAFEFACEU
|
||||||
|
#define MAG_DECL_MAGIC_ADDR ((volatile uint32_t *)(BKPSRAM_BASE + 4))
|
||||||
|
#define MAG_DECL_VALUE_ADDR ((volatile uint32_t *)(BKPSRAM_BASE + 8))
|
||||||
|
|
||||||
|
void set_mag_declination_deg(float deg) {
|
||||||
|
if (deg > MAG_DECLINATION_LIMIT_DEG) deg = MAG_DECLINATION_LIMIT_DEG;
|
||||||
|
if (deg < -MAG_DECLINATION_LIMIT_DEG) deg = -MAG_DECLINATION_LIMIT_DEG;
|
||||||
|
emergency_persist_init_clocks();
|
||||||
|
union { float f; uint32_t u; } cvt = { .f = deg };
|
||||||
|
*MAG_DECL_VALUE_ADDR = cvt.u;
|
||||||
|
*MAG_DECL_MAGIC_ADDR = MAG_DECL_PERSIST_MAGIC;
|
||||||
|
Mag_Declination = deg; /* keep legacy global in sync for any external readers */
|
||||||
|
}
|
||||||
|
|
||||||
|
float get_mag_declination_deg(void) {
|
||||||
|
emergency_persist_init_clocks();
|
||||||
|
if (*MAG_DECL_MAGIC_ADDR == MAG_DECL_PERSIST_MAGIC) {
|
||||||
|
union { float f; uint32_t u; } cvt = { .u = *MAG_DECL_VALUE_ADDR };
|
||||||
|
/* defensive clamp in case BKPSRAM was corrupted by VBAT brown-out */
|
||||||
|
float v = cvt.f;
|
||||||
|
if (v > MAG_DECLINATION_LIMIT_DEG) v = MAG_DECLINATION_LIMIT_DEG;
|
||||||
|
if (v < -MAG_DECLINATION_LIMIT_DEG) v = -MAG_DECLINATION_LIMIT_DEG;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
return MAG_DECLINATION_DEFAULT_DEG;
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//:::::RF POWER AMPLIFIER DAC5578 Emergency stop function using CLR pin/////////
|
//:::::RF POWER AMPLIFIER DAC5578 Emergency stop function using CLR pin/////////
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -1724,7 +1771,7 @@ int main(void)
|
|||||||
|
|
||||||
float magRawX = mx*cos(Pitch_Sensor*PI/180.0f) - mz*sin(Pitch_Sensor*PI/180.0f);
|
float magRawX = mx*cos(Pitch_Sensor*PI/180.0f) - mz*sin(Pitch_Sensor*PI/180.0f);
|
||||||
float magRawY = mx*sin(Roll_Sensor*PI/180.0f)*sin(Pitch_Sensor*PI/180.0f) + my*cos(Roll_Sensor*PI/180.0f)- mz*sin(Roll_Sensor*PI/180.0f)*cos(Pitch_Sensor*PI/180.0f);
|
float magRawY = mx*sin(Roll_Sensor*PI/180.0f)*sin(Pitch_Sensor*PI/180.0f) + my*cos(Roll_Sensor*PI/180.0f)- mz*sin(Roll_Sensor*PI/180.0f)*cos(Pitch_Sensor*PI/180.0f);
|
||||||
Yaw_Sensor = (180*atan2(magRawY,magRawX)/PI) - Mag_Declination;
|
Yaw_Sensor = (180*atan2(magRawY,magRawX)/PI) - get_mag_declination_deg(); /* MCU-A2 */
|
||||||
|
|
||||||
if(Yaw_Sensor<0)Yaw_Sensor+=360;
|
if(Yaw_Sensor<0)Yaw_Sensor+=360;
|
||||||
|
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ TESTS_STANDALONE := test_bug12_pa_cal_loop_inverted \
|
|||||||
test_mcu_a7_emergency_persist \
|
test_mcu_a7_emergency_persist \
|
||||||
test_mcu_a5_pa_cal_gate \
|
test_mcu_a5_pa_cal_gate \
|
||||||
test_mcu_a6_recovery_dispatch \
|
test_mcu_a6_recovery_dispatch \
|
||||||
|
test_mcu_a2_mag_declination \
|
||||||
test_gap3_iwdg_config \
|
test_gap3_iwdg_config \
|
||||||
test_gap3_temperature_max \
|
test_gap3_temperature_max \
|
||||||
test_gap3_idq_periodic_reread \
|
test_gap3_idq_periodic_reread \
|
||||||
@@ -175,6 +176,9 @@ test_mcu_a5_pa_cal_gate: test_mcu_a5_pa_cal_gate.c
|
|||||||
test_mcu_a6_recovery_dispatch: test_mcu_a6_recovery_dispatch.c
|
test_mcu_a6_recovery_dispatch: test_mcu_a6_recovery_dispatch.c
|
||||||
$(CC) $(CFLAGS) $< -o $@
|
$(CC) $(CFLAGS) $< -o $@
|
||||||
|
|
||||||
|
test_mcu_a2_mag_declination: test_mcu_a2_mag_declination.c
|
||||||
|
$(CC) $(CFLAGS) $< -lm -o $@
|
||||||
|
|
||||||
# Gap-3 safety tests -- mock-only (needs spy log for GPIO sequence)
|
# Gap-3 safety tests -- mock-only (needs spy log for GPIO sequence)
|
||||||
test_gap3_emergency_stop_rails: test_gap3_emergency_stop_rails.c $(MOCK_OBJS)
|
test_gap3_emergency_stop_rails: test_gap3_emergency_stop_rails.c $(MOCK_OBJS)
|
||||||
$(CC) $(CFLAGS) $(INCLUDES) $< $(MOCK_OBJS) -o $@
|
$(CC) $(CFLAGS) $(INCLUDES) $< $(MOCK_OBJS) -o $@
|
||||||
|
|||||||
@@ -0,0 +1,137 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* test_mcu_a2_mag_declination.c
|
||||||
|
*
|
||||||
|
* MCU-A2: magnetic declination was a hardcoded -0.61° literal baked in for
|
||||||
|
* one deployment site. Yaw_Sensor is wrong by (site_decl - (-0.61))° at
|
||||||
|
* every other site whenever GPS dual-antenna heading is unavailable.
|
||||||
|
*
|
||||||
|
* Production fix backs the value with a BKPSRAM slot and exposes a
|
||||||
|
* setter/getter pair. Default returns to the legacy -0.61° when no override
|
||||||
|
* has been written, preserving backward compatibility for the original
|
||||||
|
* site. Range is clamped to ±30° (real-world declinations are roughly
|
||||||
|
* -25° to +25°, so anything beyond is a calibration error).
|
||||||
|
*
|
||||||
|
* This test models the BKPSRAM slot, replays the setter/getter, and
|
||||||
|
* verifies clamping, persistence across "reset", default-on-empty, and
|
||||||
|
* defensive clamping if BKPSRAM is corrupted.
|
||||||
|
******************************************************************************/
|
||||||
|
#include <assert.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#define MAG_DECL_DEFAULT (-0.61f)
|
||||||
|
#define MAG_DECL_LIMIT (30.0f)
|
||||||
|
#define MAG_DECL_MAGIC 0xCAFEFACEU
|
||||||
|
|
||||||
|
/* Models BKPSRAM slot for declination (magic + value). */
|
||||||
|
static uint32_t g_magic;
|
||||||
|
static uint32_t g_value_bits;
|
||||||
|
|
||||||
|
static void simulated_power_cycle(void) { g_magic = 0; g_value_bits = 0; }
|
||||||
|
static void simulated_corrupt(uint32_t magic, float value) {
|
||||||
|
g_magic = magic;
|
||||||
|
union { float f; uint32_t u; } cvt = { .f = value };
|
||||||
|
g_value_bits = cvt.u;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_decl(float deg) {
|
||||||
|
if (deg > MAG_DECL_LIMIT) deg = MAG_DECL_LIMIT;
|
||||||
|
if (deg < -MAG_DECL_LIMIT) deg = -MAG_DECL_LIMIT;
|
||||||
|
union { float f; uint32_t u; } cvt = { .f = deg };
|
||||||
|
g_value_bits = cvt.u;
|
||||||
|
g_magic = MAG_DECL_MAGIC;
|
||||||
|
}
|
||||||
|
|
||||||
|
static float get_decl(void) {
|
||||||
|
if (g_magic == MAG_DECL_MAGIC) {
|
||||||
|
union { float f; uint32_t u; } cvt = { .u = g_value_bits };
|
||||||
|
float v = cvt.f;
|
||||||
|
if (v > MAG_DECL_LIMIT) v = MAG_DECL_LIMIT;
|
||||||
|
if (v < -MAG_DECL_LIMIT) v = -MAG_DECL_LIMIT;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
return MAG_DECL_DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool feq(float a, float b) { return fabsf(a - b) < 0.001f; }
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
printf("=== MCU-A2: mag-declination BKPSRAM persistence + clamp ===\n");
|
||||||
|
|
||||||
|
/* 1. Empty BKPSRAM -> legacy default for backward compatibility. */
|
||||||
|
printf(" Test 1: empty BKPSRAM returns default -0.61 ... ");
|
||||||
|
simulated_power_cycle();
|
||||||
|
assert(feq(get_decl(), MAG_DECL_DEFAULT));
|
||||||
|
printf("PASS\n");
|
||||||
|
|
||||||
|
/* 2. Setter writes; getter returns the written value. */
|
||||||
|
printf(" Test 2: set 12.4 then get ... ");
|
||||||
|
set_decl(12.4f);
|
||||||
|
assert(feq(get_decl(), 12.4f));
|
||||||
|
printf("PASS\n");
|
||||||
|
|
||||||
|
/* 3. Value persists across simulated reset (BKPSRAM survives). */
|
||||||
|
printf(" Test 3: persists across reset ... ");
|
||||||
|
/* simulated reset = process state preserved (BKPSRAM survives) */
|
||||||
|
assert(feq(get_decl(), 12.4f));
|
||||||
|
printf("PASS\n");
|
||||||
|
|
||||||
|
/* 4. Power cycle clears BKPSRAM -> back to default. */
|
||||||
|
printf(" Test 4: power-cycle restores default ... ");
|
||||||
|
simulated_power_cycle();
|
||||||
|
assert(feq(get_decl(), MAG_DECL_DEFAULT));
|
||||||
|
printf("PASS\n");
|
||||||
|
|
||||||
|
/* 5. Setter clamps high. */
|
||||||
|
printf(" Test 5: set +45 clamps to +30 ... ");
|
||||||
|
set_decl(45.0f);
|
||||||
|
assert(feq(get_decl(), 30.0f));
|
||||||
|
printf("PASS\n");
|
||||||
|
|
||||||
|
/* 6. Setter clamps low. */
|
||||||
|
printf(" Test 6: set -45 clamps to -30 ... ");
|
||||||
|
set_decl(-45.0f);
|
||||||
|
assert(feq(get_decl(), -30.0f));
|
||||||
|
printf("PASS\n");
|
||||||
|
|
||||||
|
/* 7. Plausible site values pass through unmodified. */
|
||||||
|
printf(" Test 7: realistic site values pass through ... ");
|
||||||
|
const float sites[] = { -22.5f, -8.0f, -0.61f, 0.0f, 4.3f, 11.2f, 17.9f };
|
||||||
|
for (size_t i = 0; i < sizeof(sites)/sizeof(sites[0]); i++) {
|
||||||
|
set_decl(sites[i]);
|
||||||
|
assert(feq(get_decl(), sites[i]));
|
||||||
|
}
|
||||||
|
printf("PASS\n");
|
||||||
|
|
||||||
|
/* 8. Defensive clamp on getter — if BKPSRAM is corrupted to an
|
||||||
|
* out-of-range value (VBAT brown-out, bit flip), getter still returns
|
||||||
|
* a safe value rather than propagating a wild offset. */
|
||||||
|
printf(" Test 8: corrupt +1000 BKPSRAM clamps to +30 on read ... ");
|
||||||
|
simulated_corrupt(MAG_DECL_MAGIC, 1000.0f);
|
||||||
|
assert(feq(get_decl(), 30.0f));
|
||||||
|
printf("PASS\n");
|
||||||
|
|
||||||
|
/* 9. Wrong magic -> default (corruption that doesn't preserve magic). */
|
||||||
|
printf(" Test 9: wrong magic returns default ... ");
|
||||||
|
simulated_corrupt(0xDEADBEEFU, 5.0f);
|
||||||
|
assert(feq(get_decl(), MAG_DECL_DEFAULT));
|
||||||
|
printf("PASS\n");
|
||||||
|
|
||||||
|
/* 10. Pre-fix regression: hardcoded -0.61 used at non-default site
|
||||||
|
* yields wrong heading by (site_decl - (-0.61))°. Confirm the fixed
|
||||||
|
* path returns the configured site value, eliminating the offset. */
|
||||||
|
printf(" Test 10: post-fix yaw correction matches configured site ... ");
|
||||||
|
set_decl(11.2f);
|
||||||
|
float pre_fix_decl_used = -0.61f;
|
||||||
|
float post_fix_decl_used = get_decl();
|
||||||
|
float bearing_error = post_fix_decl_used - pre_fix_decl_used;
|
||||||
|
assert(feq(bearing_error, 11.81f)); /* heading offset corrected */
|
||||||
|
printf("(error %.2f degrees), PASS\n", (double)bearing_error);
|
||||||
|
|
||||||
|
printf("\n=== MCU-A2: ALL TESTS PASSED ===\n\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user