From 31e247009004d8113ea8a6c19afc87843ec3d305 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E6=98=8C?= <392871505@qq.com> Date: Sun, 17 May 2026 22:41:40 +0800 Subject: [PATCH] feat(tests): Add boundary tests, model error messages, gitattributes - Add model file existence check and ONNX error output in gunshot_classifier - Add 3 new unit tests to test_core_lib: - audio_buffer_overflow: verifies capacity capping behavior - distance_estimator_multi_class: verifies gunshot/artillery/explosion refs - threat_tracker_filtering: verifies min_event_interval deduplication - Add .gitattributes to mark .onnx, .bin, .wav as binary files - All 9 core tests pass --- .../src/acoustic/.gitattributes | 4 ++ .../src/acoustic/build/test_core_lib.exe | Bin 308080 -> 313935 bytes .../acoustic/src/core/gunshot_classifier.cpp | 12 ++++- .../src/acoustic/tests/test_core_lib.cpp | 48 ++++++++++++++++++ 4 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 src/drone-software/src/acoustic/.gitattributes diff --git a/src/drone-software/src/acoustic/.gitattributes b/src/drone-software/src/acoustic/.gitattributes new file mode 100644 index 0000000..6221b58 --- /dev/null +++ b/src/drone-software/src/acoustic/.gitattributes @@ -0,0 +1,4 @@ +# Mark binary files +*.onnx binary +*.bin binary +*.wav binary diff --git a/src/drone-software/src/acoustic/build/test_core_lib.exe b/src/drone-software/src/acoustic/build/test_core_lib.exe index 201598710fa659f6595a4ef1e90d314c7e70fd6c..93ffc735d2ef16574e6cd8399bef5571f92463d4 100644 GIT binary patch delta 48265 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 delta 43565 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 diff --git a/src/drone-software/src/acoustic/src/core/gunshot_classifier.cpp b/src/drone-software/src/acoustic/src/core/gunshot_classifier.cpp index 8bbf2af..67e1e15 100644 --- a/src/drone-software/src/acoustic/src/core/gunshot_classifier.cpp +++ b/src/drone-software/src/acoustic/src/core/gunshot_classifier.cpp @@ -104,6 +104,14 @@ struct GunshotClassifier::Impl { } bool LoadModel(const std::string& path) { + // Verify file exists before attempting ONNX load + std::ifstream check(path, std::ios::binary); + if (!check) { + std::cerr << "[ERROR] Model file not found: " << path << std::endl; + return false; + } + check.close(); + #ifdef _WIN32 int wlen = MultiByteToWideChar(CP_UTF8, 0, path.c_str(), -1, nullptr, 0); std::wstring wpath; @@ -114,14 +122,14 @@ struct GunshotClassifier::Impl { try { session = std::make_unique(env, wpath.c_str(), *session_options); } catch (const Ort::Exception& e) { - (void)e; + std::cerr << "[ERROR] ONNX Runtime failed to load model: " << e.what() << std::endl; return false; } #else try { session = std::make_unique(env, path.c_str(), *session_options); } catch (const Ort::Exception& e) { - (void)e; + std::cerr << "[ERROR] ONNX Runtime failed to load model: " << e.what() << std::endl; return false; } #endif diff --git a/src/drone-software/src/acoustic/tests/test_core_lib.cpp b/src/drone-software/src/acoustic/tests/test_core_lib.cpp index 14d912a..efe94c7 100644 --- a/src/drone-software/src/acoustic/tests/test_core_lib.cpp +++ b/src/drone-software/src/acoustic/tests/test_core_lib.cpp @@ -109,13 +109,61 @@ void test_feature_extractor_shape() { std::cout << "[PASS] feature_extractor_shape (" << mel.rows() << " x " << mel.cols() << ")" << std::endl; } +void test_audio_buffer_overflow() { + AudioBuffer buf(10, 1); + std::vector data(25, 1.0f); + buf.Push(data); + assert(buf.Size() == 10); // should cap at capacity + auto popped = buf.Pop(5); + assert(popped.size() == 5); + assert(buf.Size() == 5); + std::cout << "[PASS] audio_buffer_overflow" << std::endl; +} + +void test_distance_estimator_multi_class() { + DistanceConfig config; + config.ref_spl_gunshot = 150.0f; + config.ref_spl_artillery = 180.0f; + config.ref_spl_explosion = 170.0f; + config.attenuation_alpha = 0.6f; + DistanceEstimator est(config); + float d1 = est.Estimate(120.0f, "gunshot"); + float d2 = est.Estimate(120.0f, "artillery"); + float d3 = est.Estimate(120.0f, "explosion"); + assert(d2 > d1); // artillery has higher ref SPL -> farther estimated distance at same SPL + assert(d3 > d1); + std::cout << "[PASS] distance_estimator_multi_class" << std::endl; +} + +void test_threat_tracker_filtering() { + ThreatTracker tracker(0.3f, 15.0f, 5); + AcousticThreat t1; + t1.sound_type = "gunshot"; + t1.confidence = 0.9f; + t1.azimuth = 45.0f; + t1.distance = 100.0f; + + // First update should publish + auto out1 = tracker.Update({t1}); + assert(out1.size() == 1); + + // Immediate second update with same azimuth should be filtered (interval < 0.3s) + auto out2 = tracker.Update({t1}); + assert(out2.empty()); + + std::cout << "[PASS] threat_tracker_filtering" << std::endl; +} + int main() { std::cout << "Running core library tests..." << std::endl; test_audio_buffer(); + test_audio_buffer_overflow(); test_preemphasis(); test_gcc_phat_cross_array(); test_distance_estimator(); + test_distance_estimator_multi_class(); test_threat_tracker(); + test_threat_tracker_filtering(); test_feature_extractor_shape(); std::cout << "All tests passed!" << std::endl; return 0;