Merge branch 'feature'
This commit is contained in:
Vendored
+3
-26
@@ -12,38 +12,15 @@
|
||||
"UNICODE",
|
||||
"_UNICODE",
|
||||
"NOMINMAX",
|
||||
"DATA_PROCESS_CLASS_DLL_LIBRARY",
|
||||
"PHASE_SCANNING"
|
||||
"DATA_PROCESS_CLASS_DLL_LIBRARY"
|
||||
],
|
||||
"includePath": [
|
||||
"${workspaceFolder}/data_process_class_dll",
|
||||
"${workspaceFolder}/data_process_class_dll/Eigen",
|
||||
"${workspaceFolder}/**"
|
||||
],
|
||||
"name": "X256_PS (MSVC2022 x64)",
|
||||
"compileCommands": "${workspaceFolder}/build/X256_PS/Debug/REC_ON/compile_commands.json"
|
||||
},
|
||||
{
|
||||
"compilerPath": "D:\\APP\\VisualStudio2022\\VC\\Tools\\MSVC\\14.44.35207\\bin\\Hostx64\\x64\\cl.exe",
|
||||
"cStandard": "c17",
|
||||
"cppStandard": "c++17",
|
||||
"intelliSenseMode": "windows-msvc-x64",
|
||||
"defines": [
|
||||
"WIN32",
|
||||
"_WINDOWS",
|
||||
"UNICODE",
|
||||
"_UNICODE",
|
||||
"NOMINMAX",
|
||||
"DATA_PROCESS_CLASS_DLL_LIBRARY",
|
||||
"MECHANICAL_SCANNING"
|
||||
],
|
||||
"includePath": [
|
||||
"${workspaceFolder}/data_process_class_dll",
|
||||
"${workspaceFolder}/data_process_class_dll/Eigen",
|
||||
"${workspaceFolder}/**"
|
||||
],
|
||||
"name": "X256_MS (MSVC2022 x64)",
|
||||
"compileCommands": "${workspaceFolder}/build/X256_MS/Debug/REC_ON/compile_commands.json"
|
||||
"name": "X256 (MSVC2022 x64)",
|
||||
"compileCommands": "${workspaceFolder}/build/Debug/REC_ON/compile_commands.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Vendored
-14
@@ -28,17 +28,3 @@ recordMode:
|
||||
long: REC_ON - RDP_DATA_RECORD=ON (强制启用记录模块)
|
||||
settings:
|
||||
RDP_DATA_RECORD: ON
|
||||
|
||||
scanMode:
|
||||
default: X256_PS
|
||||
choices:
|
||||
X256_PS:
|
||||
short: X256_PS
|
||||
long: X256_PS - PHASE_SCANNING
|
||||
settings:
|
||||
RADAR_SCAN_MODE: PHASE_SCANNING
|
||||
X256_MS:
|
||||
short: X256_MS
|
||||
long: X256_MS - MECHANICAL_SCANNING
|
||||
settings:
|
||||
RADAR_SCAN_MODE: MECHANICAL_SCANNING
|
||||
|
||||
Vendored
+4
-4
@@ -5,14 +5,14 @@
|
||||
"name": "调试 DLL",
|
||||
"type": "cppvsdbg",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/build/X256_PS/Debug/REC_ON/bin/rdp_playback.exe",
|
||||
"args": ["-d", "${workspaceFolder}/build/X256_PS/Debug/REC_ON/bin/2026-07-18/15-34-32", "-i", "${workspaceFolder}/build/X256_PS/Debug/REC_ON/bin/RadarConfigParam.ini", "-c", "${workspaceFolder}/build/X256_PS/Debug/REC_ON/bin/2026-07-18/15-34-32/track.csv"],
|
||||
"program": "${workspaceFolder}/build/Debug/REC_ON/bin/rdp_playback.exe",
|
||||
"args": ["-d", "${workspaceFolder}/build/Debug/REC_ON/bin/2026-07-18/15-34-32", "-i", "${workspaceFolder}/build/Debug/REC_ON/bin/RadarConfigParam.ini", "-c", "${workspaceFolder}/build/Debug/REC_ON/bin/2026-07-18/15-34-32/track.csv"],
|
||||
"stopAtEntry": false,
|
||||
"cwd": "${workspaceFolder}",
|
||||
"environment": [],
|
||||
"externalConsole": false,
|
||||
"preLaunchTask": "配置并构建全部(Debug/REC_ON)",
|
||||
"symbolSearchPath": "${workspaceFolder}/build/X256_PS/Debug/REC_ON/bin"
|
||||
"preLaunchTask": "配置并构建(Debug/REC_ON)",
|
||||
"symbolSearchPath": "${workspaceFolder}/build/Debug/REC_ON/bin"
|
||||
},
|
||||
{
|
||||
"name": "附加到宿主进程(调试 DLL)",
|
||||
|
||||
Vendored
+6
-8
@@ -3,23 +3,21 @@ rem ===========================================================================
|
||||
rem CMake build script (MSVC 2022 / v143, C++17, x64)
|
||||
rem
|
||||
rem Usage:
|
||||
rem build.bat ^<PHASE_SCANNING^|MECHANICAL_SCANNING^> ^<Debug^|Release^> [AUTO^|ON^|OFF]
|
||||
rem build.bat ^<Debug^|Release^> [AUTO^|ON^|OFF]
|
||||
rem
|
||||
rem Examples:
|
||||
rem build.bat PHASE_SCANNING Release OFF
|
||||
rem build.bat MECHANICAL_SCANNING Debug ON
|
||||
rem build.bat Release OFF
|
||||
rem build.bat Debug ON
|
||||
rem
|
||||
rem The build tree must have been configured by configure.bat first (same
|
||||
rem configuration tuple):
|
||||
rem build\<X256_PS^|X256_MS^>\<Debug^|Release^>\<REC_AUTO^|REC_OFF^|REC_ON^>
|
||||
rem configuration): build\<Debug^|Release^>\<REC_AUTO^|REC_OFF^|REC_ON^>
|
||||
rem
|
||||
rem Encoding: keep this file ASCII-only (see msvc_env.bat).
|
||||
rem ===========================================================================
|
||||
setlocal
|
||||
if "%~1"=="" goto :usage
|
||||
if "%~2"=="" goto :usage
|
||||
|
||||
call "%~dp0config_paths.bat" "%~1" "%~2" "%~3"
|
||||
call "%~dp0config_paths.bat" "%~1" "%~2"
|
||||
if errorlevel 1 exit /b 1
|
||||
|
||||
rem Load the MSVC 2022 environment (vcvarsall.bat + cl.exe existence check).
|
||||
@@ -40,5 +38,5 @@ popd
|
||||
exit /b %CMAKE_EXIT%
|
||||
|
||||
:usage
|
||||
echo Usage: build.bat ^<PHASE_SCANNING^|MECHANICAL_SCANNING^> ^<Debug^|Release^> [AUTO^|ON^|OFF^]
|
||||
echo Usage: build.bat ^<Debug^|Release^> [AUTO^|ON^|OFF^]
|
||||
exit /b 1
|
||||
|
||||
Vendored
-49
@@ -1,49 +0,0 @@
|
||||
@echo off
|
||||
rem ===========================================================================
|
||||
rem Configure and build both scan modes for one build type / record mode.
|
||||
rem
|
||||
rem Usage:
|
||||
rem build_all.bat ^<Debug^|Release^> [AUTO^|ON^|OFF]
|
||||
rem
|
||||
rem Examples:
|
||||
rem build_all.bat Release OFF
|
||||
rem build_all.bat Debug ON
|
||||
rem build_all.bat Release AUTO
|
||||
rem
|
||||
rem Runs configure.bat + build.bat for both scan modes:
|
||||
rem PHASE_SCANNING -^> build\X256_PS\<Debug^|Release^>\<REC_*>
|
||||
rem MECHANICAL_SCANNING -^> build\X256_MS\<Debug^|Release^>\<REC_*>
|
||||
rem The run stops at the first failing configuration and returns non-zero.
|
||||
rem
|
||||
rem Encoding: keep this file ASCII-only (see msvc_env.bat).
|
||||
rem ===========================================================================
|
||||
setlocal EnableExtensions
|
||||
set "BUILD_TYPE=%~1"
|
||||
set "RECORD_MODE=%~2"
|
||||
if not defined BUILD_TYPE goto :usage
|
||||
if not defined RECORD_MODE set "RECORD_MODE=AUTO"
|
||||
|
||||
rem Validate + normalize the tuple once (also resolves the X256_* aliases).
|
||||
call "%~dp0config_paths.bat" PHASE_SCANNING "%BUILD_TYPE%" "%RECORD_MODE%"
|
||||
if errorlevel 1 exit /b 1
|
||||
|
||||
for %%S in (PHASE_SCANNING MECHANICAL_SCANNING) do (
|
||||
echo.
|
||||
echo [build_all] ==================== %%S / %BUILD_TYPE% / %RECORD_MODE% ====================
|
||||
call "%~dp0configure.bat" %%S "%BUILD_TYPE%" "%RECORD_MODE%"
|
||||
if errorlevel 1 goto :failed
|
||||
call "%~dp0build.bat" %%S "%BUILD_TYPE%" "%RECORD_MODE%"
|
||||
if errorlevel 1 goto :failed
|
||||
)
|
||||
echo.
|
||||
echo [build_all] all done: %BUILD_TYPE% / %RECORD_MODE%
|
||||
exit /b 0
|
||||
|
||||
:failed
|
||||
echo.
|
||||
echo [build_all] FAILED - stopped at the first failing configuration, see the log above
|
||||
exit /b 1
|
||||
|
||||
:usage
|
||||
echo Usage: build_all.bat ^<Debug^|Release^> [AUTO^|ON^|OFF^]
|
||||
exit /b 1
|
||||
Vendored
+49
@@ -0,0 +1,49 @@
|
||||
@echo off
|
||||
rem ===========================================================================
|
||||
rem Configure and build one build configuration (build type + record mode).
|
||||
rem
|
||||
rem Usage:
|
||||
rem build_config.bat ^<Debug^|Release^> [AUTO^|ON^|OFF]
|
||||
rem
|
||||
rem Examples:
|
||||
rem build_config.bat Release OFF
|
||||
rem build_config.bat Debug ON
|
||||
rem build_config.bat Release AUTO
|
||||
rem
|
||||
rem Runs configure.bat + build.bat for the same configuration:
|
||||
rem build\<Debug^|Release^>\<REC_AUTO^|REC_OFF^|REC_ON^>
|
||||
rem The mechanical-scanning and phase-scanning variants are merged into one
|
||||
rem build; the scan mode is a runtime config item (TOML [radar_params] scan_mode).
|
||||
rem The run stops at the first failure and returns non-zero.
|
||||
rem
|
||||
rem Encoding: keep this file ASCII-only (see msvc_env.bat).
|
||||
rem ===========================================================================
|
||||
setlocal EnableExtensions
|
||||
set "BUILD_TYPE=%~1"
|
||||
set "RECORD_MODE=%~2"
|
||||
if not defined BUILD_TYPE goto :usage
|
||||
if not defined RECORD_MODE set "RECORD_MODE=AUTO"
|
||||
|
||||
rem Validate + normalize the configuration once.
|
||||
call "%~dp0config_paths.bat" "%BUILD_TYPE%" "%RECORD_MODE%"
|
||||
if errorlevel 1 exit /b 1
|
||||
|
||||
echo.
|
||||
echo [build_config] ==================== %BUILD_TYPE% / %RECORD_MODE% ====================
|
||||
call "%~dp0configure.bat" "%BUILD_TYPE%" "%RECORD_MODE%"
|
||||
if errorlevel 1 goto :failed
|
||||
call "%~dp0build.bat" "%BUILD_TYPE%" "%RECORD_MODE%"
|
||||
if errorlevel 1 goto :failed
|
||||
|
||||
echo.
|
||||
echo [build_config] all done: %BUILD_TYPE% / %RECORD_MODE%
|
||||
exit /b 0
|
||||
|
||||
:failed
|
||||
echo.
|
||||
echo [build_config] FAILED - stopped at the first failure, see the log above
|
||||
exit /b 1
|
||||
|
||||
:usage
|
||||
echo Usage: build_config.bat ^<Debug^|Release^> [AUTO^|ON^|OFF^]
|
||||
exit /b 1
|
||||
Vendored
+6
-7
@@ -3,23 +3,22 @@ rem ===========================================================================
|
||||
rem CMake clean script - remove the build output of one configuration.
|
||||
rem
|
||||
rem Usage:
|
||||
rem clean.bat ^<PHASE_SCANNING^|MECHANICAL_SCANNING^> ^<Debug^|Release^> [AUTO^|ON^|OFF]
|
||||
rem clean.bat ^<Debug^|Release^> [AUTO^|ON^|OFF]
|
||||
rem
|
||||
rem Examples:
|
||||
rem clean.bat PHASE_SCANNING Release OFF
|
||||
rem clean.bat MECHANICAL_SCANNING Debug ON
|
||||
rem clean.bat Release OFF
|
||||
rem clean.bat Debug ON
|
||||
rem
|
||||
rem The directory removed is the same one configure.bat/build.bat use:
|
||||
rem build\<X256_PS^|X256_MS^>\<Debug^|Release^>\<REC_AUTO^|REC_OFF^|REC_ON^>
|
||||
rem build\<Debug^|Release^>\<REC_AUTO^|REC_OFF^|REC_ON^>
|
||||
rem Use clean_all.bat to clean every configuration at once.
|
||||
rem
|
||||
rem Encoding: keep this file ASCII-only (see msvc_env.bat).
|
||||
rem ===========================================================================
|
||||
setlocal
|
||||
if "%~1"=="" goto :usage
|
||||
if "%~2"=="" goto :usage
|
||||
|
||||
call "%~dp0config_paths.bat" "%~1" "%~2" "%~3"
|
||||
call "%~dp0config_paths.bat" "%~1" "%~2"
|
||||
if errorlevel 1 exit /b 1
|
||||
|
||||
pushd "%~dp0..\.."
|
||||
@@ -40,5 +39,5 @@ popd
|
||||
exit /b 0
|
||||
|
||||
:usage
|
||||
echo Usage: clean.bat ^<PHASE_SCANNING^|MECHANICAL_SCANNING^> ^<Debug^|Release^> [AUTO^|ON^|OFF^]
|
||||
echo Usage: clean.bat ^<Debug^|Release^> [AUTO^|ON^|OFF^]
|
||||
exit /b 1
|
||||
|
||||
Vendored
+16
-14
@@ -6,10 +6,10 @@ rem Usage:
|
||||
rem clean_all.bat
|
||||
rem
|
||||
rem Build layout (see config_paths.bat):
|
||||
rem build\<X256_PS^|X256_MS^>\<Debug^|Release^>\<REC_AUTO^|REC_OFF^|REC_ON^>
|
||||
rem build\<Debug^|Release^>\<REC_AUTO^|REC_OFF^|REC_ON^>
|
||||
rem
|
||||
rem A legacy single-configuration build tree (build\X256_PS or build\X256_MS
|
||||
rem containing CMakeCache.txt directly) is removed as a whole as well.
|
||||
rem The legacy per-scan-mode roots build\X256_PS and build\X256_MS (used before
|
||||
rem the mechanical/phase variants were merged) are removed as a whole as well.
|
||||
rem build\output (archived package export) is never touched.
|
||||
rem
|
||||
rem Encoding: keep this file ASCII-only (see msvc_env.bat).
|
||||
@@ -17,19 +17,21 @@ rem ===========================================================================
|
||||
setlocal EnableExtensions
|
||||
pushd "%~dp0..\.."
|
||||
set "FAILED="
|
||||
|
||||
rem legacy layout: one root per scan mode (contains its own build tree directly)
|
||||
for %%S in (X256_PS X256_MS) do (
|
||||
if exist "build\%%S\CMakeCache.txt" (
|
||||
call :remove "build\%%S"
|
||||
) else (
|
||||
for %%T in (Debug Release) do (
|
||||
for %%R in (REC_AUTO REC_OFF REC_ON) do (
|
||||
call :remove "build\%%S\%%T\%%R"
|
||||
)
|
||||
)
|
||||
rem drop the directory root when every configuration is gone
|
||||
rmdir "build\%%S" 2>nul
|
||||
)
|
||||
call :remove "build\%%S"
|
||||
)
|
||||
|
||||
rem current layout: build\<Debug|Release>\<REC_*>
|
||||
for %%T in (Debug Release) do (
|
||||
for %%R in (REC_AUTO REC_OFF REC_ON) do (
|
||||
call :remove "build\%%T\%%R"
|
||||
)
|
||||
rem drop the build type root when every configuration is gone
|
||||
rmdir "build\%%T" 2>nul
|
||||
)
|
||||
|
||||
echo.
|
||||
echo [clean_all] archived package export "build\output" left untouched
|
||||
if defined FAILED (
|
||||
|
||||
Vendored
+13
-28
@@ -3,54 +3,39 @@ rem ===========================================================================
|
||||
rem Derive the build directory of one build configuration.
|
||||
rem
|
||||
rem Internal helper, called by configure.bat / build.bat / clean.bat /
|
||||
rem build_all.bat as:
|
||||
rem call config_paths.bat ^<PHASE_SCANNING^|MECHANICAL_SCANNING^> ^<Debug^|Release^> [AUTO^|ON^|OFF]
|
||||
rem build_config.bat as:
|
||||
rem call config_paths.bat ^<Debug^|Release^> [AUTO^|ON^|OFF]
|
||||
rem
|
||||
rem Accepted aliases: X256_PS = PHASE_SCANNING, X256_MS = MECHANICAL_SCANNING.
|
||||
rem The mechanical-scanning and phase-scanning variants are merged into one
|
||||
rem build since V1.5.9, so a configuration is only build type + record mode.
|
||||
rem The scan mode is a runtime config item (TOML [radar_params] scan_mode).
|
||||
rem
|
||||
rem On success the following variables are exported into the caller scope:
|
||||
rem SCAN_MODE PHASE_SCANNING / MECHANICAL_SCANNING
|
||||
rem SCAN_TAG X256_PS / X256_MS
|
||||
rem BUILD_TYPE Debug / Release
|
||||
rem RECORD_MODE AUTO / ON / OFF
|
||||
rem REC_TAG REC_AUTO / REC_OFF / REC_ON
|
||||
rem BUILD_DIR build\<SCAN_TAG>\<BUILD_TYPE>\<REC_TAG>
|
||||
rem BUILD_DIR build\<BUILD_TYPE>\<REC_TAG>
|
||||
rem
|
||||
rem Exit code: 0 = ok, 1 = invalid configuration.
|
||||
rem
|
||||
rem Encoding: keep this file ASCII-only (see msvc_env.bat).
|
||||
rem ===========================================================================
|
||||
set "SCAN_MODE="
|
||||
set "SCAN_TAG="
|
||||
set "BUILD_TYPE="
|
||||
set "RECORD_MODE="
|
||||
set "REC_TAG="
|
||||
set "BUILD_DIR="
|
||||
|
||||
if "%~1"=="" goto :usage
|
||||
if "%~2"=="" goto :usage
|
||||
|
||||
if /i "%~1"=="PHASE_SCANNING" set "SCAN_MODE=PHASE_SCANNING"
|
||||
if /i "%~1"=="X256_PS" set "SCAN_MODE=PHASE_SCANNING"
|
||||
if /i "%~1"=="MECHANICAL_SCANNING" set "SCAN_MODE=MECHANICAL_SCANNING"
|
||||
if /i "%~1"=="X256_MS" set "SCAN_MODE=MECHANICAL_SCANNING"
|
||||
if not defined SCAN_MODE (
|
||||
echo [ERROR] invalid scan mode: "%~1"
|
||||
echo expected PHASE_SCANNING / X256_PS or MECHANICAL_SCANNING / X256_MS
|
||||
exit /b 1
|
||||
)
|
||||
if "%SCAN_MODE%"=="PHASE_SCANNING" set "SCAN_TAG=X256_PS"
|
||||
if "%SCAN_MODE%"=="MECHANICAL_SCANNING" set "SCAN_TAG=X256_MS"
|
||||
|
||||
if /i "%~2"=="Debug" set "BUILD_TYPE=Debug"
|
||||
if /i "%~2"=="Release" set "BUILD_TYPE=Release"
|
||||
if /i "%~1"=="Debug" set "BUILD_TYPE=Debug"
|
||||
if /i "%~1"=="Release" set "BUILD_TYPE=Release"
|
||||
if not defined BUILD_TYPE (
|
||||
echo [ERROR] invalid build type: "%~2"
|
||||
echo [ERROR] invalid build type: "%~1"
|
||||
echo expected Debug or Release
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
if not "%~3"=="" set "RECORD_MODE=%~3"
|
||||
if not "%~2"=="" set "RECORD_MODE=%~2"
|
||||
if not defined RECORD_MODE set "RECORD_MODE=AUTO"
|
||||
if /i "%RECORD_MODE%"=="AUTO" set "RECORD_MODE=AUTO"
|
||||
if /i "%RECORD_MODE%"=="ON" set "RECORD_MODE=ON"
|
||||
@@ -59,14 +44,14 @@ if "%RECORD_MODE%"=="AUTO" set "REC_TAG=REC_AUTO"
|
||||
if "%RECORD_MODE%"=="ON" set "REC_TAG=REC_ON"
|
||||
if "%RECORD_MODE%"=="OFF" set "REC_TAG=REC_OFF"
|
||||
if not defined REC_TAG (
|
||||
echo [ERROR] invalid record mode: "%~3"
|
||||
echo [ERROR] invalid record mode: "%~2"
|
||||
echo expected AUTO or ON or OFF
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
set "BUILD_DIR=build\%SCAN_TAG%\%BUILD_TYPE%\%REC_TAG%"
|
||||
set "BUILD_DIR=build\%BUILD_TYPE%\%REC_TAG%"
|
||||
exit /b 0
|
||||
|
||||
:usage
|
||||
echo Usage: config_paths.bat ^<PHASE_SCANNING^|MECHANICAL_SCANNING^> ^<Debug^|Release^> [AUTO^|ON^|OFF^]
|
||||
echo Usage: config_paths.bat ^<Debug^|Release^> [AUTO^|ON^|OFF^]
|
||||
exit /b 1
|
||||
|
||||
Vendored
+11
-12
@@ -3,17 +3,18 @@ rem ===========================================================================
|
||||
rem CMake configure script (MSVC 2022 / v143, C++17, x64, NMake Makefiles)
|
||||
rem
|
||||
rem Usage:
|
||||
rem configure.bat ^<PHASE_SCANNING^|MECHANICAL_SCANNING^> ^<Debug^|Release^> [AUTO^|ON^|OFF]
|
||||
rem configure.bat ^<Debug^|Release^> [AUTO^|ON^|OFF]
|
||||
rem
|
||||
rem Examples:
|
||||
rem configure.bat PHASE_SCANNING Release OFF
|
||||
rem configure.bat MECHANICAL_SCANNING Debug ON
|
||||
rem configure.bat Release OFF
|
||||
rem configure.bat Debug ON
|
||||
rem
|
||||
rem The build directory is derived from the configuration tuple (see
|
||||
rem config_paths.bat), so every configuration has its own build tree:
|
||||
rem build\<X256_PS^|X256_MS^>\<Debug^|Release^>\<REC_AUTO^|REC_OFF^|REC_ON^>
|
||||
rem The build directory is derived from the configuration (see
|
||||
rem config_paths.bat): build\<Debug^|Release^>\<REC_AUTO^|REC_OFF^|REC_ON^>
|
||||
rem The mechanical-scanning and phase-scanning variants are merged into one
|
||||
rem build; the scan mode is a runtime config item (TOML [radar_params] scan_mode).
|
||||
rem
|
||||
rem The 3rd argument selects the optional data-recording debug feature
|
||||
rem The 2nd argument selects the optional data-recording debug feature
|
||||
rem (CMake cache variable RDP_DATA_RECORD). When it is omitted, CMake's AUTO
|
||||
rem default applies: Debug = enabled, Release = disabled.
|
||||
rem See docs/data_record_debug.md.
|
||||
@@ -26,13 +27,12 @@ rem Encoding: keep this file ASCII-only (see msvc_env.bat).
|
||||
rem ===========================================================================
|
||||
setlocal
|
||||
if "%~1"=="" goto :usage
|
||||
if "%~2"=="" goto :usage
|
||||
|
||||
call "%~dp0config_paths.bat" "%~1" "%~2" "%~3"
|
||||
call "%~dp0config_paths.bat" "%~1" "%~2"
|
||||
if errorlevel 1 exit /b 1
|
||||
|
||||
echo [configure] build dir : %BUILD_DIR%
|
||||
echo [configure] settings : %SCAN_MODE% / %BUILD_TYPE% / RDP_DATA_RECORD=%RECORD_MODE%
|
||||
echo [configure] settings : %BUILD_TYPE% / RDP_DATA_RECORD=%RECORD_MODE%
|
||||
|
||||
rem Load the MSVC 2022 environment (vcvarsall.bat + cl.exe existence check).
|
||||
call "%~dp0msvc_env.bat"
|
||||
@@ -56,7 +56,6 @@ if exist "%BUILD_DIR%" (
|
||||
echo [configure] compiler : %MSVC_CXX_COMPILER%
|
||||
cmake -S . -B "%BUILD_DIR%" -G "NMake Makefiles" ^
|
||||
-DCMAKE_BUILD_TYPE=%BUILD_TYPE% ^
|
||||
-DRADAR_SCAN_MODE=%SCAN_MODE% ^
|
||||
-DRDP_DATA_RECORD=%RECORD_MODE% ^
|
||||
-DCMAKE_CXX_COMPILER="%MSVC_CXX_COMPILER%"
|
||||
set "CMAKE_EXIT=%errorlevel%"
|
||||
@@ -71,5 +70,5 @@ popd
|
||||
exit /b 0
|
||||
|
||||
:usage
|
||||
echo Usage: configure.bat ^<PHASE_SCANNING^|MECHANICAL_SCANNING^> ^<Debug^|Release^> [AUTO^|ON^|OFF^]
|
||||
echo Usage: configure.bat ^<Debug^|Release^> [AUTO^|ON^|OFF^]
|
||||
exit /b 1
|
||||
|
||||
Vendored
+2
-2
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"cmake.configureOnOpen": false,
|
||||
"cmake.generator": "NMake Makefiles",
|
||||
"cmake.buildDirectory": "${workspaceFolder}/build/${variant:scanMode}/${buildType}/${variant:recordMode}",
|
||||
"cmake.buildDirectory": "${workspaceFolder}/build/${buildType}/${variant:recordMode}",
|
||||
"cmake.configureArgs": [
|
||||
"-DCMAKE_CXX_COMPILER=D:/APP/VisualStudio2022/VC/Tools/MSVC/14.44.35207/bin/Hostx64/x64/cl.exe"
|
||||
],
|
||||
@@ -12,5 +12,5 @@
|
||||
},
|
||||
"C_Cpp.default.cppStandard": "c++17",
|
||||
"C_Cpp.default.intelliSenseMode": "windows-msvc-x64",
|
||||
"C_Cpp.default.compileCommands": "${workspaceFolder}/build/X256_PS/Debug/REC_ON/compile_commands.json"
|
||||
"C_Cpp.default.compileCommands": "${workspaceFolder}/build/Debug/REC_ON/compile_commands.json"
|
||||
}
|
||||
|
||||
Vendored
+13
-13
@@ -6,7 +6,7 @@
|
||||
"tasks": [
|
||||
{
|
||||
"label": "清理全部",
|
||||
"detail": "删除 build/<X256_PS|X256_MS>/<Debug|Release>/<REC_*> 下所有配置的构建输出(build/output 打包留档不动)",
|
||||
"detail": "删除 build/<Debug|Release>/<REC_*> 下所有配置的构建输出(含旧版 build/X256_PS、build/X256_MS;build/output 打包留档不动)",
|
||||
"type": "shell",
|
||||
"command": "${workspaceFolder}\\.vscode\\scripts\\clean_all.bat",
|
||||
"problemMatcher": [],
|
||||
@@ -17,10 +17,10 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "配置并构建全部(Release/REC_OFF)",
|
||||
"detail": "Release + RDP_DATA_RECORD=OFF:X256_PS 与 X256_MS 分别配置并构建",
|
||||
"label": "配置并构建(Release/REC_OFF)",
|
||||
"detail": "Release + RDP_DATA_RECORD=OFF(机扫/相扫已合并为同一版本,单次构建一个配置)",
|
||||
"type": "shell",
|
||||
"command": "${workspaceFolder}\\.vscode\\scripts\\build_all.bat",
|
||||
"command": "${workspaceFolder}\\.vscode\\scripts\\build_config.bat",
|
||||
"args": [
|
||||
"Release",
|
||||
"OFF"
|
||||
@@ -38,10 +38,10 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "配置并构建全部(Release/REC_ON)",
|
||||
"detail": "Release + RDP_DATA_RECORD=ON:X256_PS 与 X256_MS 分别配置并构建",
|
||||
"label": "配置并构建(Release/REC_ON)",
|
||||
"detail": "Release + RDP_DATA_RECORD=ON(机扫/相扫已合并为同一版本,单次构建一个配置)",
|
||||
"type": "shell",
|
||||
"command": "${workspaceFolder}\\.vscode\\scripts\\build_all.bat",
|
||||
"command": "${workspaceFolder}\\.vscode\\scripts\\build_config.bat",
|
||||
"args": [
|
||||
"Release",
|
||||
"ON"
|
||||
@@ -56,10 +56,10 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "配置并构建全部(Debug/REC_OFF)",
|
||||
"detail": "Debug + RDP_DATA_RECORD=OFF:X256_PS 与 X256_MS 分别配置并构建",
|
||||
"label": "配置并构建(Debug/REC_OFF)",
|
||||
"detail": "Debug + RDP_DATA_RECORD=OFF(机扫/相扫已合并为同一版本,单次构建一个配置)",
|
||||
"type": "shell",
|
||||
"command": "${workspaceFolder}\\.vscode\\scripts\\build_all.bat",
|
||||
"command": "${workspaceFolder}\\.vscode\\scripts\\build_config.bat",
|
||||
"args": [
|
||||
"Debug",
|
||||
"OFF"
|
||||
@@ -74,10 +74,10 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "配置并构建全部(Debug/REC_ON)",
|
||||
"detail": "Debug + RDP_DATA_RECORD=ON:X256_PS 与 X256_MS 分别配置并构建",
|
||||
"label": "配置并构建(Debug/REC_ON)",
|
||||
"detail": "Debug + RDP_DATA_RECORD=ON(机扫/相扫已合并为同一版本,单次构建一个配置)",
|
||||
"type": "shell",
|
||||
"command": "${workspaceFolder}\\.vscode\\scripts\\build_all.bat",
|
||||
"command": "${workspaceFolder}\\.vscode\\scripts\\build_config.bat",
|
||||
"args": [
|
||||
"Debug",
|
||||
"ON"
|
||||
|
||||
+60
-12
@@ -9,11 +9,34 @@
|
||||
|
||||
### Added
|
||||
|
||||
- 新增 `.vscode/scripts/config_paths.bat`:按「扫描体制 / 构建类型 / 记录模式」推导并校验构建目录 `build/<X256_PS|X256_MS>/<Debug|Release>/<REC_AUTO|REC_OFF|REC_ON>`(接受 `X256_PS`/`X256_MS` 别名),供所有构建脚本共用。
|
||||
- 新增 `.vscode/scripts/build_all.bat`:对 `PHASE_SCANNING` 与 `MECHANICAL_SCANNING` 依次「配置 + 构建」,任一配置失败即中止并返回非 0。
|
||||
- 新增 `.vscode/scripts/clean_all.bat`:删除全部配置的构建输出(含旧版单一构建目录;`build/output` 打包留档不动)。
|
||||
- `.vscode/tasks.json` 重建为 5 个任务:「清理全部」「配置并构建全部(Release/REC_OFF)」「配置并构建全部(Release/REC_ON)」「配置并构建全部(Debug/REC_OFF)」「配置并构建全部(Debug/REC_ON)」;后 4 个任务内部调用 `build_all.bat`,分别对两种扫描体制配置并构建,默认构建任务为「配置并构建全部(Release/REC_OFF)」。
|
||||
- **数据记录改为异步写线程 + 有界队列(数据记录性能优化,工作区未提交)**:`rdp_record.h` 新增 `rdp_record::Stats`(`enqueued_rows`/`dropped_rows`/`written_rows`/`transactions`/`write_errors`/`queue_peak_bytes`/`queue_peak_rows`)与 `stats()`,新增 6 个异步入队接口 `WritePoints`/`WriteCohesionTWS`/`WriteCohesionTAS`/`WriteTempTracks`/`WriteTrustTracks`/`WriteAssoInfos`(参数为源结构体 `std::vector` 按值、调用点 `std::move`)及对应宏 `RDP_RECORD_POINTS`/`RDP_RECORD_COHESION_TWS`/`RDP_RECORD_COHESION_TAS`/`RDP_RECORD_TEMP_TRACKS`/`RDP_RECORD_TRUST_TRACKS`/`RDP_RECORD_ASSO_INFOS`;`rdp_record.cpp` 的 `Init()` 建库后启动独立写线程(Windows 下 `THREAD_PRIORITY_BELOW_NORMAL`),处理线程只做「收集源结构体 → 一次入队」,实体构造(数组转文本)与写库全部迁移到写线程。
|
||||
### Changed
|
||||
|
||||
### Fixed
|
||||
|
||||
### Removed
|
||||
|
||||
## [1.5.9] - 2026-10-29
|
||||
|
||||
> 本次发布把 1.5.8 之后的全部改动(提交 423c200 … eb9f30c)归档:参数配置方式改造 + 机扫/相扫合并为单一份构建、工作参数改为全局对象、日志组件改为 spdlog、构建脚本与 VS Code 任务适配、**航迹指纹**与手动转 TAS 输出修复。
|
||||
> 版本号:`parameters.h` 的 `PATCH_VERSION` = 9、`VERSION_INFO` = `"GENERIC"`(合并后无体制后缀),运行时 `version_info` 为 **`V1.5.9.GENERIC`**(版本号于 019af7a 提升)。
|
||||
|
||||
### Added
|
||||
|
||||
- **航迹指纹(0f3da99)**:`Trust_Track` 新增 `int Track_fingerprint`(16 bit),起批转可靠航迹时用**末点**的 `r` / `azi` / `vr` / `RCS` / `CPI_time` 计算(`track_init.cpp`:`quantize` 量化到 0.01 m / 0.001° / 0.01 m·s⁻¹ / 0.1 dBsm,随后 splitmix64 混合并 `fold16` 折叠);TAS 波束输出的 `TrackingBeam::TAS_track_index` 改为下发**指纹**(不再下发可复用的航迹批号),TAS 数据帧的 `DataRev::TAS_track_index` 也被解释为指纹;`track_process(model=3)` 按指纹在航迹表中定位航迹(命中后置 `manual_tracking_flag=1`、`Track_Mode=1`)再做关联与外推。目的是让 TAS 控制命令在回放/重连后仍能唯一对应同一目标(批号会复用,指纹由目标特征生成)。
|
||||
- **内置 spdlog v1.17.0 并重写日志模块(日志组件 spdlog 改造,24ce1e6)**:对应 `requirements.md` 三条需求——① 日志同时输出到标准输出与日志文件(文件仍为 `RDP_LOG_DIR` = `.\rdp_logs\`,文件名按日期+时间命名、每次启动一个文件);② 原 `RDP_LOG` 流式宏按 spdlog 级别拆为 `RDP_LOG_TRACE`/`RDP_LOG_DEBUG`/`RDP_LOG_INFO`/`RDP_LOG_WARN`/`RDP_LOG_ERROR`/`RDP_LOG_CRITICAL`(另提供 `RDP_LOG_ERR` 作为 `RDP_LOG_ERROR` 别名,对应 spdlog 的 `err` 级别命名),调用改为 fmt 风格,如 `RDP_LOG_INFO("parameters_reload ok: {}", path)`;③ 原 `RDP_LOG_ENABLE` 宏删除,改用 spdlog 的 `SPDLOG_ACTIVE_LEVEL` 在编译期控制日志级别。实现要点:新增内置第三方源码 `third_party/spdlog`(v1.17.0,commit `79524ddd08a4ec981b7fea76afd08ee05f83755d`,MIT;含内置 {fmt} 12.1.0;`include/` 105 文件 + `src/` 7 文件 + LICENSE,来源/复现/升级见 `third_party/spdlog/UPSTREAM.md`);`CMakeLists.txt` 新增静态库目标 `spdlog`(`SPDLOG_COMPILED_LIB`,第三方源码以 `/w` 编译,头文件目录与 `SPDLOG_COMPILED_LIB`/`SPDLOG_ACTIVE_LEVEL` 经 PUBLIC 接口同时提供给 `data_process_class_dll` 与 `rdp_record` 两个目标,保证跨 C++17/C++20 配置一致);新增 CMake 缓存变量 `RDP_LOG_LEVEL`(AUTO/TRACE/DEBUG/INFO/WARN/ERROR/CRITICAL/OFF,默认 AUTO = Debug 构建 TRACE、其余构建 INFO;OFF = 编译期关闭全部日志,`rdp_log::Init()` 也不再创建日志文件;非法值在 configure 阶段报错);`rdp_log.h/.cpp` 重写为 spdlog 双 sink(`stdout_sink_mt` + `basic_file_sink_mt`)默认 logger,日志行格式为 `[YYYY-MM-DD HH:MM:SS.mmm] [级别] [文件名:行号] 内容`(级别按 spdlog 默认名输出 trace/debug/info/warning/error/critical),`flush_on(trace)` 逐条落盘,日志文件创建失败时降级为仅标准输出并返回 false(不中断雷达处理),spdlog 内部错误经 error handler 写标准错误;日志文件命名沿用旧风格 `rdp_YYYYMMDD_HHMMSS.log`,同一秒内文件已存在(Destroy→GetB 重连、同目录多进程启动)时追加 `_1`~`_999` 后缀、不复用同一文件;新增内部辅助 `rdp_log::log_file_path()`。
|
||||
- 对应本次日志改造的文档:新增 `third_party/spdlog/UPSTREAM.md`(内置 spdlog 的来源/版本/许可证/复现与升级约束);`README.md` 与 `docs/` 按源码同步(见 Fixed 中文档同步条目)。
|
||||
|
||||
- **新增配置参数 `scan_mode`(参数配置方式改造)**:`struct.h::RDP_parameter` 的雷达工作参数组新增 `int scan_mode`(紧跟 `radar_work_mode`),语义 **0=相扫模式 / 1=机扫模式**;`rdp_parameter_loader.cpp` 在 `[radar_params]` 登记该键为**必填 int**,并校验只允许 0/1;样例 `config/config_tmp.toml` 新增 `scan_mode = 0`(相扫)。该配置项决定 `data_preprocess()` 的 TWS 判圈逻辑(见 Changed)。
|
||||
- **参数加载器(参数配置方式改造,423c200)**:新增内部模块 `data_process_class_dll/rdp_parameter_loader.h/.cpp`,把 TOML 配置文件解析到 `struct.h::RDP_parameter`(接口 `int rdp_parameter_load(const char* path, RDP_parameter& parameter, std::string* error_msg)`)。采用**严格校验**:11 个配置段(`radar_params` / `cohesion` / `track_associate` / `track_init` / `track_die` / `track_quality` / `track_index` / `beam_control` / `singer_model` / `track_init_prohibite_area` / `tas_prohibite_area`)全部必填;标量键全部必填且校验类型(数字/整数)与取值范围;拒绝未知配置段与未知键(拼错即报错);屏蔽区 4 个数组必须存在且元素个数等于对应 `*_num`(`*_num` 限 0~30,为 0 时数组可省略或为空数组),并逐区校验距离/方位范围。任一错误返回 -1 并给出首条可读原因,**不修改**调用方传入的参数(全部通过后才一次性赋值)。
|
||||
- **新增公开虚接口 `parameters_reload`(参数配置方式改造)**:`data_process_class_dll.h` 的 `Data_process_class_dll` 新增 `virtual int parameters_reload(const char* parameter_file_path)`(基类空实现返回 -1);`Data_Process` 实现为「加载成功后整体赋值 `rdp_parameter`,失败保留当前参数并经 `RDP_LOG` 打印原因」。宿主改为在 `GetB()` 之后调用一次即可完成全部工作参数配置,重连/换配置时再次调用可热重载;不再需要构造并下发 `struct RadarPara`。
|
||||
- **示例配置文件 `config/config_tmp.toml`(参数配置方式改造)**:11 个配置段与 `RDP_parameter` 的注释分组一一对应,键名与结构体成员名完全一致(`[radar_params]`、`[cohesion]`、`[track_associate]`、`[track_init]`、`[track_die]`、`[track_quality]`、`[track_index]`、`[beam_control]`、`[singer_model]` 为标量段,两个屏蔽区段用与成员同名的 TOML 数组,样例为 `*_num = 0` + 空数组);数值与改造前的宏/`RadarPara` 默认值一致。
|
||||
- **内置 toml++ v3.4.0 单头文件(参数配置方式改造)**:新增 `third_party/toml/toml.hpp`(MIT,上游单头文件原样保存);`CMakeLists.txt` 的 `SOURCES` 增加 `data_process_class_dll/rdp_parameter_loader.cpp`、`target_include_directories` 增加 `third_party/toml`;`rdp_parameter_loader.cpp` 用 `#pragma warning(push, 0)` / `(pop)` 包裹第三方头,维持 `/W3` 零告警约定。
|
||||
- 文档同步(参数配置方式改造):更新 `README.md`(1/2/3/4/5/6/7/8/9/10/11/12/13 节)与 `docs/data_process_flow_report.md` 第 9 节、`docs/track_init_readme.md` 第 13 节等——参数来源改为「TOML 配置文件 → `RDP_parameter`」、宿主调用改为 `parameters_reload`、新增旧参数→新字段对照表、编译期宏清单仅保留非配置常量、已知问题与版本历史同步。
|
||||
- 新增 `.vscode/scripts/config_paths.bat`:按「构建类型 / 记录模式」推导并校验构建目录 `build/<Debug|Release>/<REC_AUTO|REC_OFF|REC_ON>`(机扫/相扫合并后不再有扫描体制维度),供所有构建脚本共用。
|
||||
- 新增 `.vscode/scripts/build_config.bat`:对单个配置「配置 + 构建」,失败即中止并返回非 0(取代原 `build_all.bat`)。
|
||||
- 新增 `.vscode/scripts/clean_all.bat`:删除全部配置的构建输出,并清理旧版 `build/X256_PS`、`build/X256_MS` 目录(`build/output` 打包留档不动)。
|
||||
- `.vscode/tasks.json` 重建为 5 个任务:「清理全部」「配置并构建(Release/REC_OFF)」「配置并构建(Release/REC_ON)」「配置并构建(Debug/REC_OFF)」「配置并构建(Debug/REC_ON)」;后 4 个任务内部调用 `build_config.bat` 单次配置并构建,默认构建任务为「配置并构建(Release/REC_OFF)」。
|
||||
- **数据记录改为异步写线程 + 有界队列(数据记录性能优化,17a3d99)**:`rdp_record.h` 新增 `rdp_record::Stats`(`enqueued_rows`/`dropped_rows`/`written_rows`/`transactions`/`write_errors`/`queue_peak_bytes`/`queue_peak_rows`)与 `stats()`,新增 6 个异步入队接口 `WritePoints`/`WriteCohesionTWS`/`WriteCohesionTAS`/`WriteTempTracks`/`WriteTrustTracks`/`WriteAssoInfos`(参数为源结构体 `std::vector` 按值、调用点 `std::move`)及对应宏 `RDP_RECORD_POINTS`/`RDP_RECORD_COHESION_TWS`/`RDP_RECORD_COHESION_TAS`/`RDP_RECORD_TEMP_TRACKS`/`RDP_RECORD_TRUST_TRACKS`/`RDP_RECORD_ASSO_INFOS`;`rdp_record.cpp` 的 `Init()` 建库后启动独立写线程(Windows 下 `THREAD_PRIORITY_BELOW_NORMAL`),处理线程只做「收集源结构体 → 一次入队」,实体构造(数组转文本)与写库全部迁移到写线程。
|
||||
- **批量事务与 SQLite 配置(数据记录性能优化)**:写线程按表累积待提交数据,达到 `RDP_RECORD_WRITE_BATCH_ROWS`(默认 4096 行)或等待 `RDP_RECORD_WRITE_MAX_WAIT_MS`(默认 50 ms)后以事务批量提交;SQLite 启用 `journal_mode=WAL`(失败回退默认 journal)、`synchronous=OFF`、`cache_size=-8192`(8 MB)、`busy_timeout=2000`;建表后读取 `sqlite_master` 去掉 `AUTOINCREMENT` 重建为普通 `INTEGER PRIMARY KEY`(重建失败时回退并告警)。
|
||||
- **有界队列与丢弃计数(数据记录性能优化)**:队列上限 `RDP_RECORD_QUEUE_CAP_BYTES`(默认 64 MB)与 `RDP_RECORD_QUEUE_MAX_BATCHES`(默认 4096 批),写线程跟不上时整批丢弃并累计 `dropped_rows`,日志按 `RDP_RECORD_DROP_LOG_INTERVAL`(默认每 1024 行)限频提示;处理线程不因记录阻塞。
|
||||
- **记录参数宏(数据记录性能优化)**:`parameters.h` 新增可调宏(均以 `#ifndef` 保护,可用编译选项覆盖):`RDP_RECORD_QUEUE_CAP_BYTES`、`RDP_RECORD_QUEUE_MAX_BATCHES`、`RDP_RECORD_WRITE_BATCH_ROWS`、`RDP_RECORD_WRITE_MAX_WAIT_MS`、`RDP_RECORD_ASSO_CHUNK_ROWS`(默认 131072)、`RDP_RECORD_SQLITE_CACHE_KB`、`RDP_RECORD_SQLITE_BUSY_TIMEOUT_MS`、`RDP_RECORD_SQLITE_WAL`、`RDP_RECORD_MAX_CONSECUTIVE_ERRORS`(默认 10)、`RDP_RECORD_DROP_LOG_INTERVAL`。
|
||||
@@ -23,26 +46,51 @@
|
||||
|
||||
### Changed
|
||||
|
||||
- 构建目录布局由「每个扫描体制一个目录(Release/Debug 共用、重新配置互相覆盖)」改为按完整配置隔离:`build/<X256_PS|X256_MS>/<Debug|Release>/<REC_AUTO|REC_OFF|REC_ON>`,关闭/开启记录模块的产物可并存、互不覆盖。
|
||||
- `configure.bat` / `build.bat` / `clean.bat` 参数由 `<build-dir> ...` 改为 `<PHASE_SCANNING|MECHANICAL_SCANNING> <Debug|Release> [AUTO|ON|OFF]`,构建目录自动推导;`clean.bat` 由 `cmake --build --target clean` 改为删除该配置的构建目录(因此放在配置目录 `bin/` 下、CMake 不会重建的 `rdp_playback.exe`/回放数据会一并删除,需放到 `build/output/` 或仓库外)。
|
||||
- VS Code 配置同步:`settings.json` 的 `cmake.buildDirectory` 改为 `${workspaceFolder}/build/${variant:scanMode}/${buildType}/${variant:recordMode}`、`compileCommands` 指向 `build/X256_PS/Debug/REC_ON/compile_commands.json`;`cmake-variants.yaml` 新增 `recordMode` 变体(REC_AUTO / REC_OFF / REC_ON,映射 `RDP_DATA_RECORD`);`c_cpp_properties.json` 的 `compileCommands` 更新到新目录;`launch.json` 的 `preLaunchTask` 改为「配置并构建全部(Debug/REC_ON)」,`program`/`symbolSearchPath` 指向 `build/X256_PS/Debug/REC_ON/bin`。
|
||||
- 更新 README(3 节目录结构、4.2~4.4 节构建说明、12 节版本历史、13 节参考文档)与 `docs/data_record_debug.md` 第 2 节构建示例,同步新的构建目录布局、脚本签名与 5 个任务;`requirements.md` 更新为本次构建脚本/VS Code 任务调整需求。
|
||||
- 本次改动的构建验证:5 个任务全部实际执行(清理全部 → Release/REC_OFF → Release/REC_ON → Debug/REC_OFF → Debug/REC_ON),退出码均为 0;8 种配置(PS/MS × Release/Debug × REC_OFF/REC_ON)构建日志均 0 warning、0 error;每个配置目录均生成 `bin/data_process_class_dll.dll`、`lib/`(REC_ON 另有 `lib/rdp_record.lib`)与 `compile_commands.json`;编译命令中的宏与配置对应(PS 为 `-DPHASE_SCANNING`、MS 为 `-DMECHANICAL_SCANNING`,REC_ON 含 `RDP_RECORD_ENABLE`);「清理全部」删除 8 个配置目录及旧版单一构建目录而 `build/output` 未被删除;错误扫描体制/构建类型/记录模式与未配置即构建等非法路径均返回非 0。补充验证:已构建配置再次执行 `build_all.bat` 为 0 个编译/链接步骤(增量幂等);模拟 MSVC 环境无效时 `build_all.bat` 在第一个配置即中止(未尝试第二个扫描体制,退出码 1);`clean.bat` 单配置清理后目录消失且可正常重建。
|
||||
- **日志组件行为改为 spdlog(日志组件 spdlog 改造,24ce1e6)**:`data_process_class_dll.cpp` 的 `Data_Process_Factory::GetB()` 每次创建实例时调用 `rdp_log::Init()`(在 `rdp_record::Init()` 之前,使记录模块初始化日志也写入同一文件),`Destroy()` 在 `rdp_record::Shutdown()` 之后调用 `rdp_log::Shutdown()`(flush 并关闭日志文件);不再使用 `rdbuf()` 重定向/恢复 `std::cout`,`std::cout` 保持宿主原始语义,日志的双写完全由 spdlog 双 sink 完成;`RDP_LOG_DIR` 路径与 `.\rdp_logs\` 命名风格保持与旧实现一致。全部 `RDP_LOG` 调用点按语义迁移为级别宏:`data_process.cpp`(`parameters_reload` 成功 INFO / 失败 ERROR)、`track_asso_tas.cpp`(ΔT≤0 告警 WARN)、`rdp_record.cpp`(19 处:初始化/统计/建表/关闭 INFO,PRAGMA 回退/队满丢弃/时间获取失败/数据库打开失败 WARN,写入失败/连续错误熔断/写线程异常 ERROR),其余注释代码中的旧宏引用同步改为对应级别宏注释;`parameters.h` 的日志说明改为 spdlog + `SPDLOG_ACTIVE_LEVEL`/`RDP_LOG_LEVEL` 描述。
|
||||
- **日志改造的第三方告警隔离**:spdlog 静态库第三方源码以目标级 `/w` 编译,包含其头文件的 `rdp_log.h` 用 `#pragma warning(push, 0)` / `(pop)` 包裹(README 4.5 已同步为第三个第三方告警例外),项目源码仍保持 `/W3` 零告警。
|
||||
- **本次日志改造的验证(Lead 实测,2026-10-28)**:① 构建:4 个配置(Release/Debug × REC_OFF/REC_ON)经 `.vscode/scripts/build_config.bat` 全部成功、退出码 0、**0 warning / 0 error**;重复执行无编译步骤(增量幂等);`RDP_LOG_LEVEL=BAD` 在 configure 阶段被拒绝(退出码 1)。② compile_commands:Debug 配置 `-DSPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_TRACE`、Release 配置 `-DSPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_INFO`(AUTO),各目标均含 `-DSPDLOG_COMPILED_LIB`,REC_ON 的 `rdp_record.cpp`(C++20)与 DLL 源文件定义一致。③ DLL 体积:Release REC_OFF 537,088 B(较改造前 331,264 B +205,824 B)、Release REC_ON 1,855,488 B(+202,752 B)、Debug REC_OFF 3,317,760 B(+1,602,560 B)、Debug REC_ON 5,867,520 B(+1,175,552 B),增量来自内置 spdlog/{fmt}(DLL 公开接口与业务数值行为不变);导出表仍 **31 项**(无 spdlog 符号导出),导入表仅 KERNEL32/MSVCP140/VCRUNTIME140*/api-ms-win-crt-*,无新增第三方 DLL 依赖(spdlog 为静态库)。④ 运行期(4 配置逐一编译并运行 `build/verify/build_api_test.bat`,各 38 项断言 0 失败):同一批日志消息在 stdout 与日志文件中逐行一致;日志文件名匹配 `rdp_\d{8}_\d{6}(_\d+)?\.log`,每次 GetB 一个文件、同秒 Destroy→GetB 重连产生 `_1` 后缀新文件;日志行匹配 `[YYYY-MM-DD HH:MM:SS.mmm] [级别] [文件:行号] 内容`(中文消息正常),REC_ON 配置可见 `[rdp_record]` 的 `journal_mode`/`enabled`/`table ... created without AUTOINCREMENT`/`stats`/`shutdown` 等日志并随双写落盘。⑤ 编译期级别隔离实测:在 `build/verify/` 下独立配置构建 `RDP_LOG_LEVEL=WARN` 与 `=OFF`(不污染标准构建目录)均 0 warning;WARN 构建运行期 stdout/日志无 `[info]` 行、仅有 `[error]`(失败路径)且日志文件仍创建;OFF 构建运行期不创建 `rdp_logs` 目录/文件、stdout 无任何日志行、接口退出码正常。⑥ 静态检查:源码已无 `RDP_LOG <<` 与 `RDP_LOG_ENABLE` 残留;`rdp_log.h` 提供 6 个级别宏 + `RDP_LOG_ERR` 别名;`GetB()/Destroy()` 已实际调用 `rdp_log::Init()/Shutdown()`(非注释)。验证程序/日志位于 git 忽略的 `build/verify/`,未入库;另由独立验证者以冻结源码快照复验全部验收项(4 配置干净构建 0 warning/0 error、双写/文件名/行格式/逐行一致、WARN/OFF 隔离、dumpbin 导出与依赖、幂等与非法值),结论一致,报告见 `build/verify/verification_report.md`(未入库)。
|
||||
- **工作参数改为全局对象(全局工作参数对象改造,7e4cb29)**:`struct.h` 在 `struct RDP_parameter` 之后新增 `extern RDP_parameter rdp_parameter;`(附注释:所有处理模块直接访问、定义在 `data_process.cpp`、由 `parameters_reload()` 在配置完整校验通过后整体赋值、处理期间只读、配置重载应与处理互斥);`data_process.cpp` 定义并零初始化 `RDP_parameter rdp_parameter{};`(当前 L14);`Data_Process` 删除 `RDP_parameter rdp_parameter` 成员(`data_process.h`,保留注释说明工作参数为全局对象);`parameters_reload()` 改为直接调用 `rdp_parameter_load(parameter_file_path, rdp_parameter, &error)`——loader 仅在全部校验通过后写入目标对象,失败不写入,故「失败保持当前参数」语义不变。删除各处理模块(`kalman` 11 个方法、`dot_coh`/`dot_coh_tas`、`track_asso`/`track_asso_tas`/`track_asso_direct_tracking`、`track_init`/`track_init_direct_tracking`、`track_die`/`track_die_tas`、`track_index_mangement`、`tas_ctrl`,共 12 个模块的 24 个 .h/.cpp 文件)全部 `const RDP_parameter& rdp_parameter` 形参及调用实参,函数体内 `rdp_parameter.<field>` 表达式一处未改、现直接解析到全局对象;公开接口签名、公式、阈值与数值行为均不变。**注意**:全局对象是 DLL 内单实例可变状态,`parameters_reload()` 与处理调用之间非线程安全(与 BUG-03 的线程模型约定一致,宿主应按单线程接口使用、重载配置时不得并发处理)。
|
||||
- **本次全局工作参数对象改造的验证(Lead 实测,2026-10-27)**:① 构建:4 个配置(Release/Debug × REC_OFF/REC_ON;机扫/相扫已合并为单一构建)全部成功、退出码 0、**0 warning / 0 error**。② DLL 体积:Release REC_OFF 331,264 B(较改造前 331,776 B 略减 512 B)、Release REC_ON 1,652,736 B、Debug REC_OFF 1,715,200 B、Debug REC_ON 4,691,968 B。③ 导出表(Release REC_OFF,共 31 项):`parameters_reload` 仍在列(`?parameters_reload@Data_process_class_dll@@UEAAHPEBD@Z`);全局对象 `rdp_parameter` 未导出(DLL 内部符号),宿主接口不变。④ 加载器单元测试 233 项断言全部通过(加载器接口未变)。⑤ 公开接口运行期测试:4 配置逐一编译链接运行,各 38 项断言全部通过(TWS/TAS 主流程冒烟、`parameters_reload` 成功/失败、`scan_mode` 判圈运行时切换、`Destroy()`→`GetB()` 重连等)。⑥ 静态检查:处理模块已无 `const RDP_parameter&` 形参(仅 `rdp_parameter_loader.cpp` 的 `validate_parameter(const RDP_parameter&, ...)` 校验辅助函数保留);全局对象声明在 `struct.h:285`、定义在 `data_process.cpp:14`;所有模块内 `rdp_parameter` 仅以 `.field` 表达式出现;`data_process.cpp` 仅有的传参是 `parameters_reload()` 中的 `rdp_parameter_load(..., rdp_parameter, ...)`。⑦ 记录点当前行号:TWS `RDP_RECORD_POINTS` 在 `data_process.cpp:75`、TAS 分支在 `data_process.cpp:153`。验证程序与日志位于 git 忽略的构建目录中,未入库。
|
||||
- **TWS 判圈改由配置项 `scan_mode` 运行时选择(参数配置方式改造)**:`data_process.cpp::data_preprocess()` 原用 `#ifdef PHASE_SCANNING / #else / #endif` 在编译期隔离两套判圈代码,现改为按 `rdp_parameter.scan_mode` 运行时选择——`scan_mode==0`(相扫)判 `Beam_index_aiz <= last_beam_num`,`scan_mode==1`(机扫)判 `Beam_index_aiz == 0`;两种情况都会更新 `last_beam_num`、仅判满圈时 `circle_id++` 并返回 1。行为与旧两套编译期分支**逐分支等价**(相扫首帧因 `last_beam_num` 初值 INT_MAX 仍判满圈;机扫仍以波位号 0 判满圈)。`PHASE_SCANNING` / `MECHANICAL_SCANNING` 宏与 `RADAR_SCAN_MODE` 缓存变量随后在「机扫/相扫版本合并」中一并移除(见 Removed),构建不再区分体制;判圈只由配置项 `scan_mode` 决定。
|
||||
- **本次 `scan_mode` 改动的验证(Lead 实测,2026-10-27,机扫/相扫合并前)**:① 构建:8 个配置(X256_PS/X256_MS × Release/Debug × REC_OFF/REC_ON)全部重建成功、退出码 0、0 warning 0 error;Release REC_OFF DLL 仍为 331,776 字节、Debug REC_ON 4,694,016 字节(与 `scan_mode` 改动前一致)。② 加载器单元测试 233 项断言全部通过:新增覆盖「样例 `scan_mode=0` 取值正确」「`scan_mode=2` 时加载返回 -1 且错误信息包含 `scan_mode`」,其余既有成功/失败路径(缺文件、语法错、缺键、类型错、未知键、缺段、越界、数组长度不符等)保持通过。③ 公开接口运行期测试(8 配置逐一编译链接运行)各 38 项断言全部通过(原 29 项 + 新增 9 项判圈运行时切换断言):同一份二进制加载样例(`scan_mode=0` 相扫)后波位号 24 → `data_preprocess` 返回 1(判满圈);加载临时配置(`scan_mode=1` 机扫)后波位号 24 → 返回 2、波位号 0 → 返回 1;再切回样例配置并清空后行为一致;PS 与 MS 构建的测试结果相同,证明判圈已完全由配置决定。④ 静态检查:`data_process.cpp` 中 `PHASE_SCANNING`/`MECHANICAL_SCANNING` 已无有效代码引用(仅注释性提及);宏仍存在于 `parameters.h`(`VERSION_INFO` / `#pragma message`)与 `CMakeLists.txt`(`RADAR_SCAN_MODE` 编译定义、构建目录/任务命名);`RDP_parameter` 成员数 = 加载器键数 = TOML 键数 = **63**(新增 `scan_mode` 后),11 个配置段不变。⑤ 该轮记录点行号:TWS `RDP_RECORD_POINTS` 在 `data_process.cpp:72`、TAS 分支在 `data_process.cpp:150`(全局工作参数对象改造后变为 `:75` / `:153`,见本区上方新增验证)。验证程序与日志位于 git 忽略的构建目录中,未入库。
|
||||
- **参数来源改为「TOML 配置文件 → `RDP_parameter`」(参数配置方式改造,423c200)**:原先「宿主下发 `struct RadarPara` + `parameters.h` 编译期宏」两条参数来源统一为运行期配置文件;`struct.h::RDP_parameter` 成为工作参数唯一来源,初始由 `Data_Process::rdp_parameter{}`(零初始化)持有,`parameters_reload()` 成功后才整体替换(**后于「全局工作参数对象改造」改为 `struct.h::rdp_parameter` 全局对象,见本区上方**)。改造只替换参数来源,不改变公式、阈值语义与处理流程,默认数值与改造前一致。
|
||||
- **各模块参数传递方式(参数配置方式改造)**:`kalman`(使用量测误差/频点的 11 个方法:`kalman_filter_init_2dots`、`d_cal_track_init`、`kalman_filter_init_3dots`、`d_cal`、`d_cal_EKF`、`d_cal_with_doppler`、`kalman_filter_EKF`、`kalman_filter`、`d_cal_track_init_EKF`、`d_cal_track_init_with_doppler`、`Bind_speed`)、`dot_coh`/`dot_coh_tas`、`track_asso`/`track_asso_tas`/`track_asso_direct_tracking`、`track_init`/`track_init_direct_tracking`、`track_die`/`track_die_tas`、`track_index_mangement`、`tas_ctrl` 的入口与内部函数新增 `const RDP_parameter& rdp_parameter` 形参(原按值传递的 `struct RadarPara Work_Parameter` 全部删除);无参数依赖的 `kalman_pred` 保持原签名(**后于「全局工作参数对象改造」删除这些形参、改为直接访问全局对象,见本区上方**)。
|
||||
- **旧宏与旧 `RadarPara` 字段的等价替换(参数配置方式改造)**:如 `SIGMA_R/A/E/V`→`sigma_range/azimuth/pitch/velocity`、`DOT_COH_RANGE/V/AZI`→`point_cohesion_range/velocity/azimuth`、`R_MIN/R_MAX`→`point_cohesion_range_min/max`、`FREQ0`→`freq_0_GHz`、`ASSO_THORD`→`associate_threshold`、`MAX_EXTRAPOLATE_NUM(_TAS)`→`extrapolate_max(_tas)`、`H_F_WIN_LEN`→`height_smooth_win`、`TRACK_START_THRESHOLD/ALPHA_START`→`track_init_threshold/track_init_angle`、`TRACK_START_QUALITY_L3~L7`→`track_init_quality_L3~L7`、`TRACK_DIE_QUALITY(_PRAMARY)/TRACK_DIE_ROUND(_PRIMARY/_TAS)`→`track_die_quality(_pramary)/track_die_round(_pramary/_tas)`、`TRACK_QUALITY_*`→`track_quality_*`、`work_mode/Height/Sys_delay`→`radar_work_mode/radar_height/radar_system_delay`、`track_start_point_num`→`track_init_point_num_min`、`V_MAX/V_MIN`→`track_init_velocity_max/min`、`track_prohibite_area_*`→`track_init_prohibite_area_*`、`TAS_prohibite_area_*`→`tas_prohibite_area_*`、`Model1/2/3_Q_fast/slow`→`model_1/2/3_Q_fast/slow`、`DATA_RATE_SHORT/MIDDLE/FAR`→`data_rate_tws`(契约确认的等价改法)、`DATA_RATE_TAS`→`data_rate_tas`;完整对照见 `README.md` 10.3 节。
|
||||
- **航迹号、TAS 队列与跟踪波束类型改为按配置分配(参数配置方式改造)**:`track_index_mangement.cpp` 的定长数组 `int List[MAX_TRACK_INDEX]` 改为按 `rdp_parameter.track_index_max` 分配的 `std::vector<int>`;TAS 队列固定容量改用编译期常量 `RDP_TAS_QUEUE_CAPACITY`(30),运行期同时跟踪目标数上限改用配置项 `tas_track_num_max`(加载器校验 0~30,原 `MAX_TAS_NUM` 相扫 4 / 机扫 1);跟踪波束 `Tracking_beam->type` 由硬编码 1 改为配置项 `default_track_beam_type`。
|
||||
- **`parameters.h` 精简为编译期常量(参数配置方式改造)**:删除全部雷达工作参数宏,仅保留 `PI`、版本宏(`MAJOR_VERSION`/`MINOR_VERSION`/`PATCH_VERSION`/`VERSION_INFO`/`VERSION_STRING`)、日志宏(`RDP_LOG_*`)、记录宏(`RDP_RECORD_*`)、接口容量常量 `MAX_TRACK_NUM`(500)/`RDP_TAS_QUEUE_CAPACITY`(30)与 `Round`;`requirements.md` 同时改写为本次参数配置方式改造的需求说明。
|
||||
- **本次改造的验证(Lead 实测,2026-10-27,机扫/相扫合并前)**:① 构建:8 个配置(X256_PS/X256_MS × Release/Debug × REC_OFF/REC_ON)全部成功、退出码 0、0 warning 0 error,每个配置均生成 `bin/data_process_class_dll.dll`;Release REC_OFF DLL 体积 331,776 字节(约 324 KB,较改造前约 178 KB 增大,原因是纯头文件的 toml++ 解析器编入 DLL),Debug REC_ON 为 4,694,016 字节。② 导出表(Release REC_OFF,共 31 个导出):新增 `parameters_reload`(`?parameters_reload@Data_process_class_dll@@UEAAHPEBD@Z`),`track_process_parameters_initial`/`track_process_parameters_modify` 与 `RadarPara` 相关符号已不存在,`Data_Process_Factory::GetB/Destroy` 等其余接口不变。③ 加载器单元测试(229 项断言全部通过):样例配置 62 个键逐字段取值正确(含整数/浮点与负的二次/一次衰减系数;该轮为 `scan_mode` 新增前的验证,新增后样例共 63 个键,见上一条 `scan_mode` 验证);文件不存在、空路径、nullptr、TOML 语法错误、缺标量键、类型错误、未知键、缺配置段、取值越界(起批点数 2、`data_rate_tas=0`)、区域数 31、区域方位 min≥max、数组长度与 `*_num` 不一致等失败路径均返回 -1 且不修改当前参数,失败后可重新加载成功;屏蔽区数组按 `*_num` 读取、未使用位置保持 0。④ 公开接口运行期测试(8 个配置逐一编译链接运行,各 29 项断言全部通过):`GetB()`/`version_info`(`V1.5.8.PS`/`V1.5.8.MS`)、`parameters_reload` 成功/失败返回值与「失败后再加载成功」、TWS/TAS 主流程冒烟、`Beam_Ctrl`、`track_clear_all`、`track_delete`/`tracking_start`/`tracking_stop`/`primary_target_add`/`primary_target_cancel`/`tracking_point`、`Destroy()` 后重新 `GetB()` 并再次加载配置均正常。⑤ 静态审计:源码无 `Work_Parameter`/`RadarPara`/旧参数宏残留(区分大小写整词审计 0 命中);首轮验证时 62 个 `RDP_parameter` 成员 ↔ 62 个加载器键 ↔ 62 个 TOML 键一一对应(新增 `scan_mode` 后为 63 个),11 个配置段与加载器一致。验证程序与日志位于 git 忽略的构建目录中,未入库。
|
||||
- **构建脚本适配机扫/相扫合并(本次构建配置调整)**:构建目录由「扫描体制 × 构建类型 × 记录模式」改为「构建类型 × 记录模式」——`build/<Debug|Release>/<REC_AUTO|REC_OFF|REC_ON>`,不再有 `X256_PS`/`X256_MS` 维度。
|
||||
- `config_paths.bat` / `configure.bat` / `build.bat` / `clean.bat` 参数改为 `<Debug|Release> [AUTO|ON|OFF]`,构建目录自动推导;`build_all.bat` 由 `build_config.bat`(单配置「配置 + 构建」)取代,失败即中止并返回非 0;`clean_all.bat` 在清理 4 种配置的同时清理旧版 `build/X256_PS`、`build/X256_MS` 目录(`build/output` 保留)。
|
||||
- VS Code 配置同步:`settings.json` 的 `cmake.buildDirectory` 改为 `${workspaceFolder}/build/${buildType}/${variant:recordMode}`、`compileCommands` 指向 `build/Debug/REC_ON/compile_commands.json`;`cmake-variants.yaml` 移除 `scanMode` 变体(仅保留 `buildType` + `recordMode`);`c_cpp_properties.json` 合并为单个「X256 (MSVC2022 x64)」配置(不再带 `PHASE_SCANNING`/`MECHANICAL_SCANNING` 宏);`launch.json` 的 `preLaunchTask` 改为「配置并构建(Debug/REC_ON)」,`program`/`symbolSearchPath` 指向 `build/Debug/REC_ON/bin`。
|
||||
- 更新 README(3 节目录结构、4.2~4.5 节构建说明、7.1/10.4/11.2/12/13 节)与 `docs/data_record_debug.md` 第 2 节、`docs/data_process_flow_report.md` 判圈/参数说明、`docs/data_record_perf_report.md` 中的构建路径;`requirements.md` 仍为参数配置方式改造需求(本次未改需求文档)。
|
||||
- **本次构建配置调整的验证(Lead 实测,2026-10-27)**:`CMakeLists.txt` 移除 `RADAR_SCAN_MODE` 后 4 个配置(Release/Debug × REC_OFF/REC_ON)重建成功、退出码 0、0 warning 0 error(已把 `rdp_record` 的 `/W3`、`/Zc:__cplusplus` 限定为 C++ 源文件,消除 sqlite3.c 上 `/w` 覆盖 `/W3` 的 D9025 命令行提示;C 文件仅保留 `/utf-8`/`/FS` 与源文件级 `/w`);产物 DLL 大小 Release REC_OFF 331,776 / Release REC_ON 1,653,248 / Debug REC_OFF 1,718,272 / Debug REC_ON 4,694,528 字节;`compile_commands.json` 中不再含 `PHASE_SCANNING`/`MECHANICAL_SCANNING`/`RADAR_SCAN_MODE`,REC_ON 配置含 `RDP_RECORD_ENABLE`;5 个任务实际执行返回 0;「清理全部」首次执行删除旧版 `build/X256_PS`、`build/X256_MS`,再次执行删除重建后的 4 个新配置目录(`build/output` 保留);已构建配置重复执行 `build_config.bat` 为 0 个编译/链接步骤(增量幂等);模拟 MSVC 环境无效时 `build_config.bat` 立即中止(退出码 1);非法构建类型/记录模式与未配置即构建均返回非 0。
|
||||
- **12 个业务记录点由「同步构造实体写库」改为「帧/圈内收集源结构体 → 一次入队」(数据记录性能优化)**:`data_process.cpp`(TWS/TAS 输入点迹)、`dot_coh.cpp`、`dot_coh_tas.cpp`、`track_init.cpp`(临时航迹)、`track_asso.cpp`(可靠航迹 + 关联信息按 `RDP_RECORD_ASSO_CHUNK_ROWS` 分块)、`track_asso_tas.cpp`(同)共 12 处;记录时机、条件与记录行数/表列均保持不变;6 个业务文件删除 `#include "rdp_record_entity.h"`,所有新增代码仍在 `#ifdef RDP_RECORD_ENABLE` 内,记录关闭构建零代码、零影响。
|
||||
- **记录线程模型与 Shutdown 语义(数据记录性能优化)**:由「处理线程单连接 + 互斥锁串行同步写」改为「处理线程只入队 + 独立写线程承担实体构造与全部写库」;`RDP_RECORD`/`RDP_RECORD_BATCH` 保留为遗留同步接口(在调用线程直接写库、会阻塞,仅供测试/特殊场景);`Shutdown()` 会**排空**队列与 pending 后再关库,并在关库前打印统计。
|
||||
- **未改变记录语义(数据记录性能优化)**:不采用 P0-a(关联信息裁剪)与 P2(记录策略/轮转),⑥ 仍按每个候选一行写入;跨表写入顺序仍不保证、各表内顺序保持;`REC_OFF` 构建零影响与优化前一致。
|
||||
- **本次优化的验证(可复查)**:8 种配置(PS/MS × Release/Debug × REC_OFF/REC_ON)0 warning 0 error;dup 场景 6 圈/200 目标/每帧 150 点 REC_ON vs REC_OFF:普通帧每帧新增 p99 ≈ 0.09~0.11 ms(≤1 ms 阈值内)、判圈帧 `track` 新增中位 ≈ 2.9~4.1 ms(压测 p99 ≈ 13~24 ms,因未采用 P0-a,属压测上限),受控 50 目标/50 点场景判圈帧新增中位约 3 ms、最大 5.96 ms;记录行数与改动前完全一致(`RdpRecPoint` 90,000 / `RdpRecPointCohesionTWS` 4,170 / `RdpRecAssoInfo` 211,584 / `RdpRecTempTrack` 8,330 / `RdpRecTrustTrack` 304,合计 314,388),`integrity_check=ok`、DDL 无 `AUTOINCREMENT`、`journal_mode=wal`、id 严格递增、`dropped_rows=0`;60 圈长跑 9,987,370 行、`dropped_rows=0`、`write_errors=0`、队列峰值约 19.8 MB、DB 约 1.06 GB;故障注入(记录目录不可建时 `Init` 静默失败、驱动正常;小队列测试构建丢弃 314,388 行且计数正确)与 `Destroy()`→`GetB()` 重连(两个独立 db 均正常写入)测试通过。原始日志与验收脚本位于 git 忽略的 `build/analysis/verify/`。
|
||||
- **本次优化的验证(可复查,机扫/相扫合并前)**:8 种配置(X256_PS/X256_MS × Release/Debug × REC_OFF/REC_ON)0 warning 0 error(合并后 4 种配置的构建验证见上文「构建脚本适配机扫/相扫合并」);dup 场景 6 圈/200 目标/每帧 150 点 REC_ON vs REC_OFF:普通帧每帧新增 p99 ≈ 0.09~0.11 ms(≤1 ms 阈值内)、判圈帧 `track` 新增中位 ≈ 2.9~4.1 ms(压测 p99 ≈ 13~24 ms,因未采用 P0-a,属压测上限),受控 50 目标/50 点场景判圈帧新增中位约 3 ms、最大 5.96 ms;记录行数与改动前完全一致(`RdpRecPoint` 90,000 / `RdpRecPointCohesionTWS` 4,170 / `RdpRecAssoInfo` 211,584 / `RdpRecTempTrack` 8,330 / `RdpRecTrustTrack` 304,合计 314,388),`integrity_check=ok`、DDL 无 `AUTOINCREMENT`、`journal_mode=wal`、id 严格递增、`dropped_rows=0`;60 圈长跑 9,987,370 行、`dropped_rows=0`、`write_errors=0`、队列峰值约 19.8 MB、DB 约 1.06 GB;故障注入(记录目录不可建时 `Init` 静默失败、驱动正常;小队列测试构建丢弃 314,388 行且计数正确)与 `Destroy()`→`GetB()` 重连(两个独立 db 均正常写入)测试通过。原始日志与验收脚本位于 git 忽略的 `build/analysis/verify/`。
|
||||
- **相扫 TAS 航迹消亡圈数放宽(5d105d2,`parameters.h`)**:`TRACK_DIE_ROUND_TAS` 由 10 调整为 **20**(仅相扫;机扫保持 96)。因判据为 `Extrapolate_round > 阈值`,相扫 TAS 航迹实际由「连续 11 圈无实点关联消亡」放宽为「连续 21 圈」才消亡,便于 TAS 目标在短时丢点后重新关联。
|
||||
- **TAS 高度平滑窗口加长(6284f3f)**:`track_asso_tas.cpp::track_hight_update()` 的分段偏移由 `H_F_WIN_LEN + {2,3,4,6}` 改为 `H_F_WIN_LEN + {6,8,10,12}`;同时相扫 `H_F_WIN_LEN` 由 3 调整为 **6**(机扫仍为 3)。合并效果:相扫 TAS 窗口 12/14/16/18 点、机扫 TAS 窗口 9/11/13/15 点(原为 5/6/7/9);TWS 仍用 `H_F_WIN_LEN + {2,3,4,6}`,相扫因此由 5/6/7/9 变为 **8/9/10/12**、机扫保持 5/6/7/9。
|
||||
|
||||
### Fixed
|
||||
|
||||
- **修复手动转 TAS 时输出航迹更新信息不完整(eb9f30c,`tas_ctrl.cpp::tas_target_add()`)**:目标由 TWS 手动转 TAS 的当圈,原先只填 `Point_Sum`/`Range`/`Azimuth`/`Track_Index`/`Range_V`/`Amplitude`/`Flag_Point`/`Track_Mode`/`Elevation`/`z`/`Target_Type`/`track_snr` 等少数字段,其余字段(`track_time`/`GNSS_time`/`Quality`/`Direction_Angle`/关联点信息/`Flag_Direction`/`track_rcs`/`pitch_num`/`x,y,v_x,v_y`/十字星数据等)保持宿主旧值;现改为与常规关联输出**同一套字段填充**(含按 `radar_system_delay` 补偿后的极坐标、`Quality × track_quality_max`、`Flag_Direction` 的航向-位置夹角判据、十字星 `memcpy`),并补写 `point_type=0`,避免宿主在转跟踪当圈读到过期字段。
|
||||
- 文档同步「航迹指纹」(本次文档更新):README(2/6.1/6.2/7.1/8.2/9.10/新增 9.12/11.2/12 节)、`docs/data_process_flow_report.md`(2.2/7.1/7.2/8.1/8.2 与第 10 节)、`docs/track_init_readme.md`(起批时计算指纹)、`docs/data_record_debug.md`(记录实体未含指纹的说明)同步指纹的生成、下发与匹配语义,并明确 `DataRev::TAS_track_index` / `TrackingBeam::TAS_track_index` 字段名未变、语义已改为指纹。
|
||||
- 文档同步「日志组件 spdlog 改造」(本次文档更新):`README.md`(1 技术栈/2 功能特性/3 目录结构/4.1 环境/4.3 构建选项/4.4 产物体积/4.5 第三方告警例外/5.2 模块职责/7.1 日志说明/9.6 历史注/10.1 失败日志/10.3 旧宏说明/10.4 宏表/12 版本历史/13 参考文档)、`docs/data_process_flow_report.md`、`docs/data_record_debug.md` 及相关 docs 中关于 `RDP_LOG`/`RDP_LOG_ENABLE`/`rdbuf()` 重定向的过期表述统一改为 spdlog 双写 + 级别宏 + `SPDLOG_ACTIVE_LEVEL`/`RDP_LOG_LEVEL` 编译期控制;`requirements.md` 说明同步为本次日志改造需求(其文件本身保持需求原文未改)。
|
||||
- 文档同步「全局工作参数对象改造」改动(本次文档更新):README 3/4.4/5.1/5.2/6.2/7.1/10.1/11.2/12 节、`docs/data_process_flow_report.md` 第 9 节、`docs/track_init_readme.md` 1.1/2.3/8.1~8.3/16.2 节、`docs/data_record_debug.md` 记录点行号——工作参数表述由「`Data_Process::rdp_parameter` 成员 / 函数形参逐层传入」改为「`struct.h::rdp_parameter` 全局对象(`data_process.cpp` 定义)」,同步删除文中传递实参的代码片段与表格行,记录点行号更新为 `data_process.cpp:75`(TWS)/ `:153`(TAS)。
|
||||
- 文档同步「`scan_mode` 运行时判圈」改动(本次文档更新):README 1/2/3/4.2/7.2/8.1/10.1/10.2/10.3/10.4/11.2/12/13 节、`docs/data_process_flow_report.md` 2.2 与第 9 节、`docs/track_init_readme.md` 1.2 与输出字段表、`docs/data_record_debug.md` 记录点行号(`data_process.cpp:72` TWS / `:150` TAS)——判圈表述由「编译期 `PHASE_SCANNING`/`MECHANICAL_SCANNING` 隔离」改为「由配置项 `scan_mode` 运行时选择」,并同步 `UNCONF_TARGET` 宏删除后的语义写法。
|
||||
- 修正文档中把参数来源写成「`parameters.h` 编译期宏 / 宿主下发 `RadarPara`」的过期描述(本次文档更新,对应参数配置方式改造):README 2、3、4.2、5.1、5.2、6.1、6.2、7.1、7.3、8.1~8.3、9.1~9.11、10、11、12、13 节与 `docs/data_process_flow_report.md` 第 9/10 节、`docs/track_init_readme.md` 第 1/2/4/6/8/10/11~16 节按源码同步——参数来源改为 TOML 配置文件 → `RDP_parameter`、旧宏改记为「原 …」并给出新字段名、新增 `parameters_reload` 调用方式与旧→新对照,数值未变。
|
||||
- 修正文档中 TAS 航迹消亡圈数与高度平滑窗口的过期数值(本次文档更新):README 2/5.2/9.7/9.9/10.1 节与 `docs/data_process_flow_report.md` 4.6、6.1、9.1 节按 5d105d2、6284f3f 同步为「相扫 TAS 20 圈 / 机扫 96」、「相扫 H_F_WIN_LEN=6、TAS 偏移 +6/+8/+10/+12」。
|
||||
- 修正 `docs/data_record_debug.md` 中「质量字段尚未加入记录实体」的过期描述(本次文档更新,对应 1ffb287):字段速查与记录点说明补齐 `quality` / `Quality` / `Unupdated_time` / `Point_sum` / `Valid_point_sum`。
|
||||
- 文档同步「机扫/相扫版本合并」后的构建配置(本次文档更新):README 3/4.2~4.5/7.1/10.4/11.2/12/13 节、`docs/data_record_debug.md` 第 2 节、`docs/data_process_flow_report.md` 2.2 与第 9 节、`docs/data_record_perf_report.md` 构建路径——构建目录去掉扫描体制维度、脚本签名与任务名同步、版本串改为 `V1.5.9.GENERIC`、删除「构建体制宏」相关说明。
|
||||
|
||||
### Removed
|
||||
|
||||
- 删除 `tasks.json` 中原有的 15 个任务(`CMake: 配置 X256_PS|X256_MS`、`CMake: 构建 ...`、`CMake: 清理 ...`、`CMake: 配置并构建 ...` 等),改为上述 5 个任务。
|
||||
- **删除旧日志开关与旧日志宏接口(日志组件 spdlog 改造)**:`parameters.h` 删除 `RDP_LOG_ENABLE` 宏(改由 `SPDLOG_ACTIVE_LEVEL` 编译期控制级别);`rdp_log` 不再提供 `RDP_LOG` 流式宏与 `rdp_log::prefix()`(改为 `RDP_LOG_*` 级别宏,时间戳/级别/文件:行号由 spdlog pattern 统一输出);删除 `rdp_log::Init()` 中 `std::cout.rdbuf()` 重定向及 `rdp_log::Shutdown()` 中的恢复逻辑(内部状态改为持有 spdlog logger 与日志路径)。
|
||||
- **删除 `struct RadarPara` 与两个旧公开虚接口(参数配置方式改造)**:`data_process_class_dll.h` 中的 `struct RadarPara` 及 `track_process_parameters_initial(RadarPara)` / `track_process_parameters_modify(RadarPara)` 整体删除,由 `parameters_reload(const char*)` 取代;`data_process.h` 中的 `struct RadarPara Work_Parameter` 删除。旧 `RadarPara` 中实现未使用的字段(`R_TAS_max/min`、`V_TAS_max/min`、`TAS_Height_1~5`、`cfar_th`、`north_angle`、`Beam_num`)未进入新结构体(相应自动 TAS 逻辑仍保持停用)。
|
||||
- **删除 `parameters.h` 中的雷达工作参数宏(参数配置方式改造)**:`SIGMA_R/A/E/V`、`DOT_COH_RANGE/V/AZI`、`R_MIN`/`R_MAX`、`FREQ0`、`ASSO_THORD`、`MAX_EXTRAPOLATE_NUM(_TAS)`、`H_F_WIN_LEN`、`TRACK_START_THRESHOLD`、`ALPHA_START`、`TRACK_START_QUALITY_L3~L7`、`TRACK_DIE_*`、`TRACK_QUALITY_*`、`MAX_TRACK_INDEX`、`MAX_TAS_NUM`、`TAS_QUEUE_LENGTH`、`DATA_RATE_*`(含 `H_F_WIN_LEN`、`MAX_BEAM_NUM`、`ASSOCIATE_THRESHOLD_MIN/MID/MAX`、`DIRECT_TRACKING_QUEUE_LENGTH` 等)以及已注释的 `V_MAX`/`V_MIN` 残留;这些参数改由配置文件提供(见 `README.md` 10.1~10.3)。
|
||||
- **删除 `UNCONF_TARGET` 宏(参数配置方式改造)**:`parameters.h` 的未确认目标类型语义常量 `UNCONF_TARGET`(0)删除,`track_init.cpp` / `track_init_direct_tracking.cpp` 起批处 `Target_Type` 改为直接写 0,语义不变。
|
||||
- **移除构建期的机扫/相扫版本区分(机扫/相扫版本合并)**:`CMakeLists.txt` 删除 `RADAR_SCAN_MODE` 缓存变量、取值校验与 `target_compile_definitions` 中的 `${RADAR_SCAN_MODE}`;构建不再定义 `PHASE_SCANNING` / `MECHANICAL_SCANNING`,`parameters.h` 的 `VERSION_INFO` 由 PS/MS 改为固定 `"GENERIC"`(版本号 1.5.9,版本串 `V1.5.9.GENERIC`);扫描模式只由运行期配置项 `[radar_params] scan_mode` 决定。构建目录/脚本/任务随之不再有扫描体制参数与 `X256_PS`/`X256_MS` 维度。
|
||||
- 删除 `tasks.json` 中旧的 `CMake: 配置/构建/清理/配置并构建...` 任务(15 个),改为上述 5 个任务(机扫/相扫合并后为「清理全部」+ 4 个「配置并构建(...)」)。
|
||||
|
||||
## [1.5.8] - 2026-10-20
|
||||
|
||||
|
||||
+98
-13
@@ -10,16 +10,10 @@ cmake_minimum_required(VERSION 3.21)
|
||||
# 因此本工程的最低 CMake 版本随之提高;NMake/Ninja 生成器同样满足。
|
||||
project(data_process_class_dll LANGUAGES CXX)
|
||||
|
||||
# 扫描模式:
|
||||
# PHASE_SCANNING -> 相扫雷达(X256_PS,原 Qt 工程默认激活配置)
|
||||
# MECHANICAL_SCANNING -> 机扫雷达(X256_MS)
|
||||
# 用法:cmake -S . -B build -DRADAR_SCAN_MODE=PHASE_SCANNING
|
||||
set(RADAR_SCAN_MODE "PHASE_SCANNING" CACHE STRING "Radar scan mode (PHASE_SCANNING or MECHANICAL_SCANNING)")
|
||||
set_property(CACHE RADAR_SCAN_MODE PROPERTY STRINGS PHASE_SCANNING MECHANICAL_SCANNING)
|
||||
|
||||
if(NOT RADAR_SCAN_MODE STREQUAL "PHASE_SCANNING" AND NOT RADAR_SCAN_MODE STREQUAL "MECHANICAL_SCANNING")
|
||||
message(FATAL_ERROR "RADAR_SCAN_MODE 必须为 PHASE_SCANNING 或 MECHANICAL_SCANNING,当前值:${RADAR_SCAN_MODE}")
|
||||
endif()
|
||||
# 机扫 / 相扫版本已合并为同一份 DLL:构建不再区分扫描体制,也不再定义
|
||||
# PHASE_SCANNING / MECHANICAL_SCANNING 编译期宏,也没有 RADAR_SCAN_MODE 缓存变量。
|
||||
# 扫描模式改由运行期配置项的 `[radar_params] scan_mode` 决定(0 相扫 / 1 机扫),
|
||||
# 随全部雷达工作参数一起由 TOML 配置文件经 parameters_reload() 载入,详见 README 4.2、10。
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 数据记录调试功能开关(可选编译,详见 docs/data_record_debug.md)
|
||||
@@ -56,6 +50,51 @@ else()
|
||||
message(STATUS "数据记录调试功能:关闭(不编译记录模块,RDP_RECORD 为空操作)")
|
||||
endif()
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 日志编译期级别(spdlog)
|
||||
# 原 parameters.h 的 RDP_LOG_ENABLE 布尔开关已移除:日志级别改由 spdlog 的
|
||||
# SPDLOG_ACTIVE_LEVEL 在编译期控制(低于该级别的 RDP_LOG_* 日志在编译期被
|
||||
# 剔除、实参不求值,OFF 即等价于原「关闭日志模块」)。本缓存变量把级别映射
|
||||
# 到 SPDLOG_ACTIVE_LEVEL,并同时用于 data_process_class_dll 与 rdp_record
|
||||
# 两个目标(两个目标使用同一份 spdlog 头文件配置,必须保持一致)。
|
||||
# AUTO : Debug 构建 = TRACE,其余构建 = INFO(默认)
|
||||
# TRACE / DEBUG / INFO / WARN / ERROR / CRITICAL : 显式指定最低编译级别
|
||||
# OFF : 编译期关闭全部 RDP_LOG_* 日志(rdp_log::Init() 也不再创建日志文件)
|
||||
# 修改方式:cmake -DRDP_LOG_LEVEL=WARN ...(或在 CMakeCache 中修改后重新配置)
|
||||
# ---------------------------------------------------------------------------
|
||||
set(RDP_LOG_LEVEL "AUTO" CACHE STRING "日志编译期级别:AUTO(Debug=TRACE/其它=INFO) / TRACE / DEBUG / INFO / WARN / ERROR / CRITICAL / OFF")
|
||||
set_property(CACHE RDP_LOG_LEVEL PROPERTY STRINGS AUTO TRACE DEBUG INFO WARN ERROR CRITICAL OFF)
|
||||
|
||||
if(RDP_LOG_LEVEL STREQUAL "AUTO")
|
||||
if(CMAKE_CONFIGURATION_TYPES)
|
||||
set(RDP_LOG_ACTIVE_LEVEL_DEFS
|
||||
"$<$<CONFIG:Debug>:SPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_TRACE>"
|
||||
"$<$<NOT:$<CONFIG:Debug>>:SPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_INFO>")
|
||||
set(RDP_LOG_LEVEL_DESC "AUTO(多配置生成器:Debug=TRACE,其它=INFO)")
|
||||
elseif(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
set(RDP_LOG_ACTIVE_LEVEL_DEFS "SPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_TRACE")
|
||||
set(RDP_LOG_LEVEL_DESC "AUTO(Debug 构建 → TRACE)")
|
||||
else()
|
||||
set(RDP_LOG_ACTIVE_LEVEL_DEFS "SPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_INFO")
|
||||
if(CMAKE_BUILD_TYPE)
|
||||
set(RDP_LOG_LEVEL_DESC "AUTO(${CMAKE_BUILD_TYPE} 构建 → INFO)")
|
||||
else()
|
||||
set(RDP_LOG_LEVEL_DESC "AUTO(未指定构建类型 → INFO)")
|
||||
endif()
|
||||
endif()
|
||||
elseif(RDP_LOG_LEVEL STREQUAL "OFF")
|
||||
set(RDP_LOG_ACTIVE_LEVEL_DEFS "SPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_OFF")
|
||||
set(RDP_LOG_LEVEL_DESC "OFF(编译期关闭全部 RDP_LOG_* 日志)")
|
||||
elseif(RDP_LOG_LEVEL MATCHES "^(TRACE|DEBUG|INFO|WARN|ERROR|CRITICAL)$")
|
||||
set(RDP_LOG_ACTIVE_LEVEL_DEFS "SPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_${RDP_LOG_LEVEL}")
|
||||
set(RDP_LOG_LEVEL_DESC "${RDP_LOG_LEVEL}")
|
||||
else()
|
||||
message(FATAL_ERROR
|
||||
"RDP_LOG_LEVEL 必须为 AUTO / TRACE / DEBUG / INFO / WARN / ERROR / CRITICAL / OFF,"
|
||||
"当前值:${RDP_LOG_LEVEL}")
|
||||
endif()
|
||||
message(STATUS "日志编译期级别:${RDP_LOG_LEVEL_DESC}(映射为 SPDLOG_ACTIVE_LEVEL)")
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 语言标准:C++17
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -89,10 +128,44 @@ if(NOT CMAKE_GENERATOR MATCHES "Visual Studio")
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
endif()
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 内置第三方库 spdlog(日志,v1.17.0,见 third_party/spdlog/UPSTREAM.md)
|
||||
# 以静态库方式编译(SPDLOG_COMPILED_LIB),供 data_process_class_dll 与
|
||||
# rdp_record(C++20 静态库,rdp_record.cpp 内也有日志)链接:
|
||||
# - PUBLIC 编译定义 SPDLOG_COMPILED_LIB / SPDLOG_ACTIVE_LEVEL 与头文件
|
||||
# 目录,保证所有使用方(含跨 C++17/C++20 的两目标)配置一致;
|
||||
# - SPDLOG_ACTIVE_LEVEL 由上方 RDP_LOG_LEVEL 决定;
|
||||
# - 第三方源码以 /w 编译,维持本项目 /W3 零告警约定(见 README 4.5)。
|
||||
# ---------------------------------------------------------------------------
|
||||
set(SPDLOG_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/third_party/spdlog")
|
||||
|
||||
add_library(spdlog STATIC
|
||||
third_party/spdlog/src/spdlog.cpp
|
||||
third_party/spdlog/src/async.cpp
|
||||
third_party/spdlog/src/cfg.cpp
|
||||
third_party/spdlog/src/color_sinks.cpp
|
||||
third_party/spdlog/src/file_sinks.cpp
|
||||
third_party/spdlog/src/stdout_sinks.cpp
|
||||
third_party/spdlog/src/bundled_fmtlib_format.cpp
|
||||
)
|
||||
target_include_directories(spdlog PUBLIC ${SPDLOG_ROOT}/include)
|
||||
target_compile_definitions(spdlog PUBLIC
|
||||
SPDLOG_COMPILED_LIB
|
||||
${RDP_LOG_ACTIVE_LEVEL_DEFS}
|
||||
)
|
||||
if(MSVC)
|
||||
target_compile_options(spdlog PRIVATE /w /utf-8 /Zc:__cplusplus /Zc:wchar_t /FS)
|
||||
endif()
|
||||
set_target_properties(spdlog PROPERTIES
|
||||
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
|
||||
)
|
||||
message(STATUS "第三方日志库:spdlog v1.17.0(静态库,third_party/spdlog)")
|
||||
|
||||
set(SOURCES
|
||||
data_process_class_dll/rdp_log.cpp
|
||||
data_process_class_dll/data_process_class_dll.cpp
|
||||
data_process_class_dll/data_process.cpp
|
||||
data_process_class_dll/rdp_parameter_loader.cpp
|
||||
data_process_class_dll/dot_coh.cpp
|
||||
data_process_class_dll/kalman.cpp
|
||||
data_process_class_dll/coor_trans.cpp
|
||||
@@ -114,6 +187,11 @@ set(PUBLIC_HEADERS
|
||||
|
||||
add_library(data_process_class_dll SHARED ${SOURCES})
|
||||
|
||||
# 日志库(静态库 spdlog):
|
||||
# 目标级 PUBLIC 接口提供 spdlog 头文件目录、SPDLOG_COMPILED_LIB 与 SPDLOG_ACTIVE_LEVEL,
|
||||
# 使 rdp_log.cpp 及各业务模块(RDP_LOG_* 宏)以一致的配置编译。
|
||||
target_link_libraries(data_process_class_dll PRIVATE spdlog)
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 数据记录模块(RDP_DATA_RECORD 启用时构建):独立静态库、C++20(ormpp 要求),
|
||||
# 与雷达处理代码(C++17)通过 rdp_record.h 的「仅声明 + 显式实例化」跨标准边界。
|
||||
@@ -144,9 +222,11 @@ if(RDP_RECORD_ENABLED)
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/third_party/ormpp/thirdparty/sqlite3
|
||||
)
|
||||
target_compile_options(rdp_record PRIVATE
|
||||
/W3
|
||||
# /W3 与 /Zc:__cplusplus 只用于 C++ 源文件:sqlite3.c 由下方源文件级 /w 关闭告警,
|
||||
# 若 C 文件也带 /W3,cl 会打印 D9025(overriding '/W3' with '/w')命令行提示。
|
||||
$<$<COMPILE_LANGUAGE:CXX>:/W3>
|
||||
/utf-8
|
||||
/Zc:__cplusplus
|
||||
$<$<COMPILE_LANGUAGE:CXX>:/Zc:__cplusplus>
|
||||
/FS
|
||||
)
|
||||
|
||||
@@ -154,13 +234,16 @@ if(RDP_RECORD_ENABLED)
|
||||
set_source_files_properties(third_party/ormpp/thirdparty/sqlite3/sqlite3.c
|
||||
PROPERTIES COMPILE_OPTIONS "/w")
|
||||
|
||||
# rdp_record.cpp 内的 RDP_LOG_* 日志经 rdp_log.h 使用 spdlog:
|
||||
# 链接 spdlog 目标以获得其 PUBLIC 头文件目录与 SPDLOG_COMPILED_LIB /
|
||||
# SPDLOG_ACTIVE_LEVEL 定义(与 DLL 目标保持一致)。
|
||||
target_link_libraries(rdp_record PRIVATE spdlog)
|
||||
target_link_libraries(data_process_class_dll PRIVATE rdp_record)
|
||||
endif()
|
||||
|
||||
# 编译 DLL 时定义导出宏;引用 DLL 的工程不应定义该宏。
|
||||
target_compile_definitions(data_process_class_dll PRIVATE
|
||||
DATA_PROCESS_CLASS_DLL_LIBRARY
|
||||
${RADAR_SCAN_MODE}
|
||||
)
|
||||
|
||||
# 与 qmake 工程保持一致的 Windows 编译定义
|
||||
@@ -175,8 +258,10 @@ if(WIN32)
|
||||
endif()
|
||||
|
||||
# Eigen 头文件位于源码目录下(<Eigen/Dense>)。
|
||||
# toml++ 为单头文件第三方库(<toml.hpp>,仅 rdp_parameter_loader.cpp 使用)。
|
||||
target_include_directories(data_process_class_dll PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/data_process_class_dll
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/third_party/toml
|
||||
)
|
||||
|
||||
if(MSVC)
|
||||
|
||||
@@ -0,0 +1,163 @@
|
||||
# 雷达数据处理配置文件(TOML 格式)
|
||||
#
|
||||
# 说明:
|
||||
# 1) 配置段与 struct.h::RDP_parameter 的注释分组一一对应,配置键名与结构体成员名完全一致;
|
||||
# 2) 所有标量键均为必填项,且会做类型与取值范围校验(详见 rdp_parameter_loader.cpp);
|
||||
# 3) 屏蔽区数组使用与结构体成员同名的 TOML 数组,元素个数必须等于对应 *_num(0~30);
|
||||
# *_num = 0 时数组可省略,存在时必须为空数组。
|
||||
|
||||
## 雷达工作参数
|
||||
[radar_params]
|
||||
# 工作模式:0 近程 / 1 中程 / 2 远程
|
||||
radar_work_mode = 0
|
||||
# 扫描模式:0 相扫模式 / 1 机扫模式
|
||||
scan_mode = 0
|
||||
# 频点0频率(GHz)
|
||||
freq_0_GHz = 9.2
|
||||
# 雷达平台高度(m)
|
||||
radar_height = 0
|
||||
# 系统延迟(s)
|
||||
radar_system_delay = 0
|
||||
# TWS模式数据率(s)
|
||||
data_rate_tws = 3
|
||||
# TAS模式数据率(s)
|
||||
data_rate_tas = 1
|
||||
# 纯跟踪模式数据率(s)
|
||||
data_rate_track_only = 0.2
|
||||
|
||||
## 点迹凝聚参数
|
||||
[cohesion]
|
||||
# 距离凝聚范围(m)
|
||||
point_cohesion_range = 80
|
||||
# 速度凝聚范围(m/s)
|
||||
point_cohesion_velocity = 2
|
||||
# 方位凝聚范围(度)
|
||||
point_cohesion_azimuth = 6
|
||||
# 最小凝聚距离(m)
|
||||
point_cohesion_range_min = 100
|
||||
# 最大凝聚距离(m)
|
||||
point_cohesion_range_max = 100000
|
||||
|
||||
## 航迹关联参数
|
||||
[track_associate]
|
||||
# 距离量测精度(m)
|
||||
sigma_range = 10.0
|
||||
# 方位量测精度(弧度)
|
||||
sigma_azimuth = 0.02
|
||||
# 俯仰量测精度(弧度)
|
||||
sigma_pitch = 0.2
|
||||
# 速度量测精度(m/s)
|
||||
sigma_velocity = 2.0
|
||||
# 关联门限
|
||||
associate_threshold = 4.0
|
||||
# 最大外推点数
|
||||
extrapolate_max = 3
|
||||
# TAS最大外推点数
|
||||
extrapolate_max_tas = 5
|
||||
# 高度平滑窗长
|
||||
height_smooth_win = 6
|
||||
|
||||
## 航迹起始参数
|
||||
[track_init]
|
||||
# 起航波门
|
||||
track_init_threshold = 8
|
||||
# 起航夹角(度)
|
||||
track_init_angle = 60
|
||||
# 最少起批点数(3~9)
|
||||
track_init_point_num_min = 3
|
||||
# 3点起批质量门限
|
||||
track_init_quality_L3 = 0.7
|
||||
# 4点起批质量门限
|
||||
track_init_quality_L4 = 0.6
|
||||
# 5点起批质量门限
|
||||
track_init_quality_L5 = 0.5
|
||||
# 6点起批质量门限
|
||||
track_init_quality_L6 = 0.4
|
||||
# 7点起批质量门限
|
||||
track_init_quality_L7 = 0.3
|
||||
# 航迹起始最小速度(m/s)
|
||||
track_init_velocity_min = 1
|
||||
# 航迹起始最大速度(m/s)
|
||||
track_init_velocity_max = 50
|
||||
|
||||
## 航迹消亡参数
|
||||
[track_die]
|
||||
# 航迹消亡圈数
|
||||
track_die_round = 6
|
||||
# 重点航迹消亡圈数
|
||||
track_die_round_pramary = 10
|
||||
# TAS航迹消亡圈数
|
||||
track_die_round_tas = 20
|
||||
# 航迹消批质量条件
|
||||
track_die_quality = 0.3
|
||||
# 重点航迹消批质量条件
|
||||
track_die_quality_pramary = 0.3
|
||||
|
||||
## 航迹质量管理参数
|
||||
[track_quality]
|
||||
# 航迹质量最大值
|
||||
track_quality_max = 100
|
||||
# 航迹质量最小值
|
||||
track_quality_min = 0
|
||||
# 马氏距离权重
|
||||
track_quality_d_weight = 0.5
|
||||
# 信噪比权重
|
||||
track_quality_snr_weight = 0.3
|
||||
# 航迹长度权重
|
||||
track_quality_len_weight = 0.2
|
||||
# 航迹质量迭代系数
|
||||
track_quality_iter_coeff = 0.8
|
||||
# 航迹质量下降系数2次项(必须 <= 0)
|
||||
track_quality_decrease_coeff_o2 = -0.0008
|
||||
# 航迹质量下降系数1次项(必须 <= 0)
|
||||
track_quality_decrease_coeff_o1 = -0.0025
|
||||
|
||||
## 航迹批号管理
|
||||
[track_index]
|
||||
# 最大航迹批号(1~500)
|
||||
track_index_max = 500
|
||||
|
||||
## 波束调度
|
||||
[beam_control]
|
||||
# 默认跟踪波束类型
|
||||
default_track_beam_type = 1
|
||||
# 最大同时跟踪目标数(TAS 队列容量上限 30)
|
||||
tas_track_num_max = 4
|
||||
|
||||
## singer模型机动参数(均必须 > 0)
|
||||
[singer_model]
|
||||
# 模型1
|
||||
model_1_Q_fast = 0.000001
|
||||
model_1_Q_slow = 0.000001
|
||||
# 模型2
|
||||
model_2_Q_fast = 0.01
|
||||
model_2_Q_slow = 0.01
|
||||
# 模型3
|
||||
model_3_Q_fast = 1.0
|
||||
model_3_Q_slow = 1.0
|
||||
|
||||
## 航迹起始屏蔽区(数组元素个数必须等于 track_init_prohibite_area_num)
|
||||
[track_init_prohibite_area]
|
||||
# 航迹起始屏蔽区数(0~30)
|
||||
track_init_prohibite_area_num = 0
|
||||
# 最小距离 (m)
|
||||
track_init_prohibite_area_range_min = []
|
||||
# 最大距离 (m)
|
||||
track_init_prohibite_area_range_max = []
|
||||
# 最小方位角 (度)
|
||||
track_init_prohibite_area_azimuth_min = []
|
||||
# 最大方位角 (度)
|
||||
track_init_prohibite_area_azimuth_max = []
|
||||
|
||||
## 禁止TAS跟踪区域(数组元素个数必须等于 tas_prohibite_area_num)
|
||||
[tas_prohibite_area]
|
||||
# 禁止TAS的区域数(0~30)
|
||||
tas_prohibite_area_num = 0
|
||||
# 最小距离 (m)
|
||||
tas_prohibite_area_range_min = []
|
||||
# 最大距离 (m)
|
||||
tas_prohibite_area_range_max = []
|
||||
# 最小方位角 (度)
|
||||
tas_prohibite_area_azimuth_min = []
|
||||
# 最大方位角 (度)
|
||||
tas_prohibite_area_azimuth_max = []
|
||||
@@ -1,12 +1,17 @@
|
||||
#include "data_process.h"
|
||||
#include "rdp_log.h"
|
||||
#include "rdp_record.h"
|
||||
#include "rdp_parameter_loader.h"
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
|
||||
// 全局工作参数对象(声明见 struct.h)。所有处理模块直接访问,由 parameters_reload() 加载。
|
||||
RDP_parameter rdp_parameter{};
|
||||
/*******************************************************************************/
|
||||
/*******************************************************************************/
|
||||
/************************************用户接口函数*******************************/
|
||||
@@ -70,45 +75,30 @@ int Data_Process::data_preprocess(struct DataRev Data_Input[150])
|
||||
RDP_RECORD_POINTS(rec_points);
|
||||
#endif
|
||||
|
||||
#ifdef PHASE_SCANNING
|
||||
// 相扫模式下,判断前后两帧波位号的大小,来判断是否扫描一周完成
|
||||
if(Data_Input[0].Beam_index_aiz <= last_beam_num) //TWS
|
||||
// 判圈:扫描模式由配置参数 scan_mode 决定(0=相扫,1=机扫),替代原编译期 PHASE_SCANNING/MECHANICAL_SCANNING 分支。
|
||||
// 相扫:判断前后两帧波位号的大小,来判断是否扫描一周完成(当前波位号 <= 上一帧波位号即判满圈);
|
||||
// 机扫:可能存在转台反转导致波位号变小但并未扫描一周,因此改为波位号为 0 时判满圈。
|
||||
const bool circle_done = (rdp_parameter.scan_mode == 0)
|
||||
? (Data_Input[0].Beam_index_aiz <= last_beam_num) //TWS 相扫
|
||||
: (Data_Input[0].Beam_index_aiz == 0); //TWS 机扫
|
||||
|
||||
last_beam_num = Data_Input[0].Beam_index_aiz;
|
||||
if(circle_done)
|
||||
{
|
||||
last_beam_num = Data_Input[0].Beam_index_aiz;
|
||||
circle_id++;
|
||||
// std::cout << Data_Input[0].CPI_time << std::endl;
|
||||
// RDP_LOG<<"CPI_time: "<<Data_Input[0].CPI_time<<std::endl;
|
||||
// RDP_LOG_DEBUG("CPI_time: {}", Data_Input[0].CPI_time);
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
last_beam_num = Data_Input[0].Beam_index_aiz;
|
||||
return 2;
|
||||
}
|
||||
#else
|
||||
// 机扫模式下,可能存在转台反转的情况,导致波位号变小,但并未扫描一周,因此改为判断波位号为0时,表示扫描一周完成
|
||||
if(Data_Input[0].Beam_index_aiz == 0) //TWS
|
||||
{
|
||||
last_beam_num = Data_Input[0].Beam_index_aiz;
|
||||
circle_id++;
|
||||
// std::cout << Data_Input[0].CPI_time << std::endl;
|
||||
// RDP_LOG<<"CPI_time: "<<Data_Input[0].CPI_time<<std::endl;
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
last_beam_num = Data_Input[0].Beam_index_aiz;
|
||||
return 2;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else if(Data_Input[0].point_type == 1) //tas数据
|
||||
{
|
||||
// std::cout << "TAS TARGET :" <<Data_Input[0].TAS_track_index;
|
||||
//std::cout << "TAS time :" <<Data_Input[0].CPI_time;
|
||||
|
||||
data_num = min(max(Data_Input[0].Point_Sum, 0), 150);
|
||||
TAS_track_idx = Data_Input[0].TAS_track_index;
|
||||
TAS_track_fingerprint = Data_Input[0].TAS_track_index;
|
||||
#ifdef RDP_RECORD_ENABLE
|
||||
//记录输入点迹:处理线程内先收集源结构体,循环结束后一次入队(构造实体与写库由记录写线程完成)
|
||||
std::vector<PointRecv> rec_points;
|
||||
@@ -182,7 +172,7 @@ int model)
|
||||
if(model==2)
|
||||
{
|
||||
|
||||
dot_coh.dot_coh_process_buff(&Data_buffer,&point_recv,Work_Parameter);
|
||||
dot_coh.dot_coh_process_buff(&Data_buffer,&point_recv);
|
||||
std::vector<PointRecv>().swap(Data_buffer);
|
||||
}
|
||||
|
||||
@@ -195,8 +185,7 @@ int model)
|
||||
&trust_track,
|
||||
Trust_Track_Output,
|
||||
Trust_track_num_Output,
|
||||
latest_timestamp,
|
||||
Work_Parameter
|
||||
latest_timestamp
|
||||
);
|
||||
|
||||
track_die.track_die_process( &trust_track,
|
||||
@@ -207,8 +196,7 @@ int model)
|
||||
&trust_track,
|
||||
&temp_track,
|
||||
Trust_Track_Output,
|
||||
Trust_track_num_Output,
|
||||
Work_Parameter);
|
||||
Trust_track_num_Output);
|
||||
|
||||
}
|
||||
|
||||
@@ -218,6 +206,17 @@ int model)
|
||||
*Trust_track_num_Output=0; //航迹更新数
|
||||
*Track_die_num_Output=0; //航迹消亡数目
|
||||
|
||||
//查找指定指纹的Track_idx
|
||||
int tas_track_idx = -1;
|
||||
for(auto& t : trust_track){
|
||||
if(t.Track_fingerprint == TAS_track_fingerprint){
|
||||
tas_track_idx = t.Track_Index;
|
||||
t.manual_tracking_flag = 1;
|
||||
t.Track_Mode = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
dot_coh_tas.dot_coh_tas_process(&Data_buffer_tas,&point_recv_tas); //点迹凝聚
|
||||
|
||||
std::vector<PointRecv>().swap(Data_buffer_tas);//凝聚完毕 清除Data_buffer
|
||||
@@ -226,8 +225,7 @@ int model)
|
||||
&trust_track, //航迹文件
|
||||
Trust_Track_Output, //更新航迹信息
|
||||
Trust_track_num_Output, //更新航迹数
|
||||
Work_Parameter, //工作参数
|
||||
TAS_track_idx,
|
||||
tas_track_idx,
|
||||
latest_timestamp //最新时间戳
|
||||
);
|
||||
|
||||
@@ -249,27 +247,21 @@ void Data_Process::Beam_Ctrl(struct TrackingBeam *Tracking_beam,
|
||||
int *Trust_track_num_Output)
|
||||
{
|
||||
|
||||
tas_ctrl.tas_ctrl_process(&trust_track,Tracking_beam, Trust_Track_Output, Trust_track_num_Output,Work_Parameter, latest_timestamp);
|
||||
tas_ctrl.tas_ctrl_process(&trust_track,Tracking_beam, Trust_Track_Output, Trust_track_num_Output, latest_timestamp);
|
||||
}
|
||||
|
||||
//数据处理参数初始化
|
||||
int Data_Process::track_process_parameters_initial(struct RadarPara Radar_Parameter)
|
||||
//数据处理参数加载(从 TOML 配置文件加载全部工作参数到全局 rdp_parameter)
|
||||
int Data_Process::parameters_reload(const char* parameter_file_path)
|
||||
{
|
||||
|
||||
//工作参数设置
|
||||
if (Radar_Parameter.track_start_point_num < 3 || Radar_Parameter.track_start_point_num > 9)
|
||||
std::string error;
|
||||
// rdp_parameter_load 仅在完整校验通过后写入目标对象,失败时保持原参数不变
|
||||
if (rdp_parameter_load(parameter_file_path, rdp_parameter, &error) != 0)
|
||||
{
|
||||
RDP_LOG_ERROR("parameters_reload failed: {}", error);
|
||||
return -1;
|
||||
memcpy(&Work_Parameter, &Radar_Parameter, sizeof(RadarPara));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//数据处理参数修改
|
||||
int Data_Process::track_process_parameters_modify(struct RadarPara Radar_Parameter)
|
||||
{
|
||||
if (Radar_Parameter.track_start_point_num < 3 || Radar_Parameter.track_start_point_num > 9)
|
||||
return -1;
|
||||
memcpy(&Work_Parameter, &Radar_Parameter, sizeof(RadarPara));
|
||||
RDP_LOG_INFO("parameters_reload ok: {}", parameter_file_path != nullptr ? parameter_file_path : "");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -307,7 +299,7 @@ int Data_Process:: track_delete(int delete_track_num, //手动
|
||||
{
|
||||
|
||||
trust_track[j].manual_delete_flag=1;
|
||||
// RDP_LOG << "Delete :" << trust_track[j].Track_Index << std::endl;
|
||||
// RDP_LOG_DEBUG("Delete : {}", trust_track[j].Track_Index);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -68,11 +68,10 @@ public:
|
||||
//手动打跟踪波束
|
||||
int tracking_point(float Azimuth); //方位角
|
||||
|
||||
//参数设置初始化
|
||||
int track_process_parameters_initial(struct RadarPara Radar_Parameter);
|
||||
|
||||
//参数设置修改函数
|
||||
int track_process_parameters_modify(struct RadarPara Radar_Parameter);
|
||||
//参数加载函数(从 TOML 配置文件加载全部工作参数到 rdp_parameter)
|
||||
//参数:parameter_file_path - 配置文件路径
|
||||
//返回:成功返回 0;失败返回 -1(失败时不改变当前参数)
|
||||
int parameters_reload(const char* parameter_file_path) override;
|
||||
|
||||
private:
|
||||
|
||||
@@ -84,13 +83,14 @@ private:
|
||||
std::vector <Trust_Track> trust_track; //可靠航迹 按航迹区存入
|
||||
std::vector <std::vector<Temp_track>> temp_track; //临时航迹 按临时航迹区存入
|
||||
|
||||
// 工作参数为全局对象 struct.h::rdp_parameter(由 parameters_reload() 加载),不再作为类成员。
|
||||
int Beam_num; //波位数
|
||||
int beam_count; //波位计数
|
||||
int data_num; //波位1点数
|
||||
|
||||
int beam_index[3]; //3个波位号(当前帧)
|
||||
|
||||
int TAS_track_idx; //TAS数据的目标批号
|
||||
int TAS_track_fingerprint; //TAS数据的目标批号
|
||||
|
||||
int Track_Mode; //跟踪模式 1TAS 0TWS
|
||||
|
||||
@@ -104,8 +104,6 @@ private:
|
||||
|
||||
long long circle_id = 0; //圈id
|
||||
|
||||
struct RadarPara Work_Parameter; //工作参数 参数设置函数外部输入
|
||||
|
||||
Dot_Coh dot_coh; //点迹凝聚
|
||||
|
||||
Dot_Coh_TAS dot_coh_tas;
|
||||
|
||||
@@ -19,8 +19,8 @@ Data_process_class_dll *Data_Process_Factory::GetB()
|
||||
{
|
||||
if(!p)
|
||||
{
|
||||
//初始化日志输出:RDP_LOG_ENABLE 开启时重定向 std::cout 到日志文件
|
||||
//rdp_log::Init();
|
||||
//初始化日志输出:标准输出 + 日志文件(.\rdp_logs\rdp_YYYYMMDD_HHMMSS.log)
|
||||
rdp_log::Init();
|
||||
|
||||
//初始化数据记录:RDP_DATA_RECORD 开启时创建记录目录、打开本次运行的记录库;
|
||||
//关闭时 rdp_record::Init() 为空实现,不产生任何开销
|
||||
@@ -42,10 +42,10 @@ void Data_Process_Factory::Destroy()
|
||||
delete p;
|
||||
p = nullptr;
|
||||
|
||||
//恢复 std::cout 到控制台并关闭日志文件
|
||||
//rdp_log::Shutdown();
|
||||
|
||||
//关闭记录库(RDP_DATA_RECORD 关闭时为空实现)
|
||||
rdp_record::Shutdown();
|
||||
|
||||
//flush 并关闭日志文件(须在记录模块关闭之后:其 Shutdown 仍会写日志)
|
||||
rdp_log::Shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,70 +109,6 @@ struct DATA_PROCESS_CLASS_DLLSHARED_EXPORT Target_direct_tracking
|
||||
int open_flag; // 1 引导跟踪 0 结束引导跟踪
|
||||
};
|
||||
|
||||
// 雷达参数结构体
|
||||
struct DATA_PROCESS_CLASS_DLLSHARED_EXPORT RadarPara
|
||||
{
|
||||
int Beam_num; //波位数
|
||||
|
||||
float Height; //雷达平台高度 (m)
|
||||
|
||||
float Sys_delay; //系统延迟 (s)
|
||||
float R_TAS_max; //TAS最大距离 (m)
|
||||
float R_TAS_min; //TAS最小距离 (m)
|
||||
|
||||
float V_TAS_max; //TAS最大速度 (m/s)
|
||||
float V_TAS_min; //TAS最小速度 (m/s)
|
||||
|
||||
float TAS_Height_1; //TAS跟踪高度1 (m)
|
||||
float TAS_Height_2; //TAS跟踪高度2 (m)
|
||||
float TAS_Height_3; //TAS跟踪高度3 (m)
|
||||
float TAS_Height_4; //TAS跟踪高度4 (m)
|
||||
float TAS_Height_5; //TAS跟踪高度5 (m)
|
||||
|
||||
int track_prohibite_area_num; //禁止航迹起始的区域数目
|
||||
float R_max_track_prohibited[30]; //最大距离 (m)
|
||||
float R_min_track_prohibited[30]; //最小距离 (m)
|
||||
float Azimuth_max_track_prohibited[30]; //最大方位角 (度)
|
||||
float Azimuth_min_track_prohibited[30]; //最小方位角 (度)
|
||||
|
||||
int TAS_prohibite_area_num; //禁止TAS的区域数目
|
||||
float R_max_TAS_prohibited[30]; //最大距离 (m)
|
||||
float R_min_TAS_prohibited[30]; //最小距离 (m)
|
||||
float Azimuth_max_TAS_prohibited[30]; //最大方位角 (度)
|
||||
float Azimuth_min_TAS_prohibited[30]; //最小方位角 (度)
|
||||
|
||||
int cfar_th; //cfar门限
|
||||
|
||||
int work_mode; //工作模式 0进程 1中程 2远程
|
||||
int north_angle; //北偏角
|
||||
|
||||
float V_MAX; //最大速度
|
||||
float V_MIN; //最小速度
|
||||
|
||||
float DATA_RATE_SHORT; //三种不同模式下的 数据率
|
||||
float DATA_RATE_MIDDLE;
|
||||
float DATA_RATE_FAR;
|
||||
|
||||
float DATA_RATE_TAS; //TAS模式数据率,由dll调用者输入;TAS波束输出间隔阈值为 DATA_RATE_TAS 秒
|
||||
|
||||
//数据关联参数
|
||||
int track_start_point_num; //起批点数(典型值 3或4)
|
||||
float track_start_threshold; //起航波门大小(典型值 3)
|
||||
float track_asso_threshold; //关联波门大小(典型值 3)
|
||||
float track_asso_threshold_tas; //关联波门大小tas (典型值 3)
|
||||
|
||||
//singer模型机动参数
|
||||
float Model1_Q_fast; //0.00001
|
||||
float Model1_Q_slow; //0.00001
|
||||
|
||||
float Model2_Q_fast; //0.1
|
||||
float Model2_Q_slow; //0.001
|
||||
|
||||
float Model3_Q_fast; //0.8
|
||||
float Model3_Q_slow; //0.4
|
||||
|
||||
};
|
||||
|
||||
class DATA_PROCESS_CLASS_DLLSHARED_EXPORT Data_process_class_dll
|
||||
{
|
||||
|
||||
@@ -261,16 +197,12 @@ public:
|
||||
return 0;
|
||||
}
|
||||
|
||||
//参数设置初始化函数
|
||||
virtual int track_process_parameters_initial(struct RadarPara Radar_Parameter)
|
||||
//数据处理参数加载函数(从 TOML 配置文件加载全部工作参数)
|
||||
//参数:parameter_file_path - 配置文件路径(宿主进程当前工作目录可达的路径)
|
||||
//返回:成功返回 0;失败返回 -1(失败时不改变当前参数)
|
||||
virtual int parameters_reload(const char*)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
//参数设置修改函数
|
||||
virtual int track_process_parameters_modify(struct RadarPara Radar_Parameter)
|
||||
{
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@@ -7,8 +7,7 @@
|
||||
using namespace std;
|
||||
|
||||
int Dot_Coh::dot_coh_process(std::vector <PointRecv> *data_input,
|
||||
std::vector <PointRecv> *point_recv,
|
||||
struct RadarPara Work_Parameter)
|
||||
std::vector <PointRecv> *point_recv)
|
||||
{
|
||||
|
||||
if(data_input->size()>1)
|
||||
@@ -32,10 +31,10 @@ int Dot_Coh::dot_coh_process(std::vector <PointRecv> *data_input,
|
||||
float point_1_A=static_cast<float>((*data_input)[i].amplitude);
|
||||
|
||||
//凝聚条件: 距离、方位接近
|
||||
if(Work_Parameter.work_mode == 0)
|
||||
if(rdp_parameter.radar_work_mode == 0)
|
||||
{
|
||||
float delta_F = fabs(point_0_F-point_1_F)<2*PI-fabs(point_0_F-point_1_F) ? fabs(point_0_F-point_1_F) :2*PI-fabs(point_0_F-point_1_F);
|
||||
if( (fabs(point_0_R-point_1_R)<=DOT_COH_RANGE && delta_F <=DOT_COH_AZI/180.0*PI && fabs(point_0_V-point_1_V)<=DOT_COH_V)//
|
||||
if( (fabs(point_0_R-point_1_R)<=rdp_parameter.point_cohesion_range && delta_F <=rdp_parameter.point_cohesion_azimuth/180.0*PI && fabs(point_0_V-point_1_V)<=rdp_parameter.point_cohesion_velocity)//
|
||||
&& point_0_A <point_1_A )//
|
||||
{
|
||||
point_0_R=static_cast<float>((*data_input)[i].range);
|
||||
@@ -44,7 +43,7 @@ int Dot_Coh::dot_coh_process(std::vector <PointRecv> *data_input,
|
||||
point_0_F=static_cast<float>((*data_input)[i].azimuth);
|
||||
(*data_input)[loop_of_point].use_flag=1;
|
||||
}
|
||||
else if( (fabs(point_0_R-point_1_R)<=DOT_COH_RANGE && delta_F <=DOT_COH_AZI/180.0*PI && fabs(point_0_V-point_1_V)<=DOT_COH_V)//
|
||||
else if( (fabs(point_0_R-point_1_R)<=rdp_parameter.point_cohesion_range && delta_F <=rdp_parameter.point_cohesion_azimuth/180.0*PI && fabs(point_0_V-point_1_V)<=rdp_parameter.point_cohesion_velocity)//
|
||||
&& point_0_A >= point_1_A)//
|
||||
{
|
||||
(*data_input)[i].use_flag=1;
|
||||
@@ -53,7 +52,7 @@ int Dot_Coh::dot_coh_process(std::vector <PointRecv> *data_input,
|
||||
else
|
||||
{
|
||||
float delta_F = fabs(point_0_F-point_1_F)<2*PI-fabs(point_0_F-point_1_F) ? fabs(point_0_F-point_1_F) :2*PI-fabs(point_0_F-point_1_F);
|
||||
if( (fabs(point_0_R-point_1_R)<=DOT_COH_RANGE && delta_F <=DOT_COH_AZI/180.0*PI )//
|
||||
if( (fabs(point_0_R-point_1_R)<=rdp_parameter.point_cohesion_range && delta_F <=rdp_parameter.point_cohesion_azimuth/180.0*PI )//
|
||||
&& point_0_A <point_1_A )//
|
||||
{
|
||||
point_0_R=static_cast<float>((*data_input)[i].range);
|
||||
@@ -62,7 +61,7 @@ int Dot_Coh::dot_coh_process(std::vector <PointRecv> *data_input,
|
||||
point_0_F=static_cast<float>((*data_input)[i].azimuth);
|
||||
(*data_input)[loop_of_point].use_flag=1;
|
||||
}
|
||||
else if( (fabs(point_0_R-point_1_R)<=DOT_COH_RANGE && delta_F <=DOT_COH_AZI/180.0*PI )//
|
||||
else if( (fabs(point_0_R-point_1_R)<=rdp_parameter.point_cohesion_range && delta_F <=rdp_parameter.point_cohesion_azimuth/180.0*PI )//
|
||||
&& point_0_A >= point_1_A)//
|
||||
{
|
||||
(*data_input)[i].use_flag=1;
|
||||
@@ -79,7 +78,7 @@ int Dot_Coh::dot_coh_process(std::vector <PointRecv> *data_input,
|
||||
std::vector <PointRecv>::iterator Iter;
|
||||
for (Iter=data_input->begin(); Iter!=data_input->end();)
|
||||
{
|
||||
if((*Iter).use_flag==1 || (*Iter).range <R_MIN || (*Iter).range > R_MAX )
|
||||
if((*Iter).use_flag==1 || (*Iter).range <rdp_parameter.point_cohesion_range_min || (*Iter).range > rdp_parameter.point_cohesion_range_max )
|
||||
{
|
||||
data_input->erase(Iter);
|
||||
Iter=data_input->begin();
|
||||
@@ -99,8 +98,7 @@ int Dot_Coh::dot_coh_process(std::vector <PointRecv> *data_input,
|
||||
}
|
||||
|
||||
int Dot_Coh::dot_coh_process_buff( std::vector <PointRecv> *data_input, //输入的点迹
|
||||
std::vector <PointRecv> *point_recv, //输出点迹
|
||||
struct RadarPara Work_Parameter //工作参数
|
||||
std::vector <PointRecv> *point_recv //输出点迹
|
||||
)
|
||||
{
|
||||
|
||||
@@ -148,10 +146,10 @@ int Dot_Coh::dot_coh_process_buff( std::vector <PointRecv> *data_input,
|
||||
float point_1_A=static_cast<float>(data_tmp[i].amplitude);
|
||||
|
||||
//凝聚条件: 距离、方位接近
|
||||
if(Work_Parameter.work_mode == 0)
|
||||
if(rdp_parameter.radar_work_mode == 0)
|
||||
{
|
||||
float delta_F = fabs(point_0_F-point_1_F)<2*PI-fabs(point_0_F-point_1_F) ? fabs(point_0_F-point_1_F) :2*PI-fabs(point_0_F-point_1_F);
|
||||
if( (fabs(point_0_R-point_1_R)<=DOT_COH_RANGE && delta_F <=DOT_COH_AZI/180.0*PI && fabs(point_0_V-point_1_V)<=DOT_COH_V)//
|
||||
if( (fabs(point_0_R-point_1_R)<=rdp_parameter.point_cohesion_range && delta_F <=rdp_parameter.point_cohesion_azimuth/180.0*PI && fabs(point_0_V-point_1_V)<=rdp_parameter.point_cohesion_velocity)//
|
||||
&& point_0_A <point_1_A )//
|
||||
{
|
||||
point_0_R=static_cast<float>(data_tmp[i].range);
|
||||
@@ -160,7 +158,7 @@ int Dot_Coh::dot_coh_process_buff( std::vector <PointRecv> *data_input,
|
||||
point_0_F=static_cast<float>(data_tmp[i].azimuth);
|
||||
data_tmp[loop_of_point].use_flag=1;
|
||||
}
|
||||
else if( (fabs(point_0_R-point_1_R)<=DOT_COH_RANGE && delta_F <=DOT_COH_AZI/180.0*PI && fabs(point_0_V-point_1_V)<=DOT_COH_V)//
|
||||
else if( (fabs(point_0_R-point_1_R)<=rdp_parameter.point_cohesion_range && delta_F <=rdp_parameter.point_cohesion_azimuth/180.0*PI && fabs(point_0_V-point_1_V)<=rdp_parameter.point_cohesion_velocity)//
|
||||
&& point_0_A >= point_1_A)//
|
||||
{
|
||||
data_tmp[i].use_flag=1;
|
||||
@@ -169,7 +167,7 @@ int Dot_Coh::dot_coh_process_buff( std::vector <PointRecv> *data_input,
|
||||
else
|
||||
{
|
||||
float delta_F = fabs(point_0_F-point_1_F)<2*PI-fabs(point_0_F-point_1_F) ? fabs(point_0_F-point_1_F) :2*PI-fabs(point_0_F-point_1_F);
|
||||
if( (fabs(point_0_R-point_1_R)<=DOT_COH_RANGE && delta_F <=DOT_COH_AZI/180.0*PI )//
|
||||
if( (fabs(point_0_R-point_1_R)<=rdp_parameter.point_cohesion_range && delta_F <=rdp_parameter.point_cohesion_azimuth/180.0*PI )//
|
||||
&& point_0_A <point_1_A )//
|
||||
{
|
||||
point_0_R=static_cast<float>(data_tmp[i].range);
|
||||
@@ -178,7 +176,7 @@ int Dot_Coh::dot_coh_process_buff( std::vector <PointRecv> *data_input,
|
||||
point_0_F=static_cast<float>(data_tmp[i].azimuth);
|
||||
data_tmp[loop_of_point].use_flag=1;
|
||||
}
|
||||
else if( (fabs(point_0_R-point_1_R)<=DOT_COH_RANGE && delta_F <=DOT_COH_AZI/180.0*PI )//
|
||||
else if( (fabs(point_0_R-point_1_R)<=rdp_parameter.point_cohesion_range && delta_F <=rdp_parameter.point_cohesion_azimuth/180.0*PI )//
|
||||
&& point_0_A >= point_1_A)//
|
||||
{
|
||||
data_tmp[i].use_flag=1;
|
||||
@@ -195,7 +193,7 @@ int Dot_Coh::dot_coh_process_buff( std::vector <PointRecv> *data_input,
|
||||
std::vector <PointRecv>::iterator Iter;
|
||||
for (Iter=data_tmp.begin(); Iter!=data_tmp.end();)
|
||||
{
|
||||
if((*Iter).use_flag==1 || (*Iter).range <R_MIN || (*Iter).range > R_MAX )
|
||||
if((*Iter).use_flag==1 || (*Iter).range <rdp_parameter.point_cohesion_range_min || (*Iter).range > rdp_parameter.point_cohesion_range_max )
|
||||
{
|
||||
data_tmp.erase(Iter);
|
||||
Iter=data_tmp.begin();
|
||||
|
||||
@@ -14,14 +14,12 @@ public:
|
||||
|
||||
// 点迹凝聚函数
|
||||
int dot_coh_process( std::vector <PointRecv> *data_input, //输入的点迹
|
||||
std::vector <PointRecv> *point_recv, //输出点迹
|
||||
struct RadarPara Work_Parameter //工作参数
|
||||
std::vector <PointRecv> *point_recv //输出点迹
|
||||
);
|
||||
|
||||
//点迹凝聚函数,缓存一帧,一边输出,一边进行滑窗凝聚
|
||||
int dot_coh_process_buff( std::vector <PointRecv> *data_input, //输入的点迹
|
||||
std::vector <PointRecv> *point_recv, //输出点迹
|
||||
struct RadarPara Work_Parameter //工作参数
|
||||
std::vector <PointRecv> *point_recv //输出点迹
|
||||
);
|
||||
|
||||
//构造函数
|
||||
|
||||
@@ -6,8 +6,7 @@
|
||||
using namespace std;
|
||||
|
||||
int Dot_Coh_TAS::dot_coh_tas_process(std::vector <PointRecv> *data_input, //输入点迹
|
||||
std::vector <PointRecv> *point_recv_tas //输出点迹
|
||||
)
|
||||
std::vector <PointRecv> *point_recv_tas) //输出点迹
|
||||
{
|
||||
if (data_input->size() == 0) {
|
||||
return 1;
|
||||
@@ -32,7 +31,7 @@ int Dot_Coh_TAS::dot_coh_tas_process(std::vector <PointRecv> *dat
|
||||
|
||||
//凝聚条件: 距离、方位接近
|
||||
float delta_F = fabs(point_0_F-point_1_F)<2*PI-fabs(point_0_F-point_1_F) ? fabs(point_0_F-point_1_F) :2*PI-fabs(point_0_F-point_1_F);
|
||||
if( (fabs(point_0_R-point_1_R)<=DOT_COH_RANGE && delta_F <=DOT_COH_AZI/180.0*PI && fabs(point_0_V-point_1_V)<=DOT_COH_V)
|
||||
if( (fabs(point_0_R-point_1_R)<=rdp_parameter.point_cohesion_range && delta_F <=rdp_parameter.point_cohesion_azimuth/180.0*PI && fabs(point_0_V-point_1_V)<=rdp_parameter.point_cohesion_velocity)
|
||||
&& point_0_A <point_1_A )
|
||||
{
|
||||
point_0_R=static_cast<float>((*data_input)[i].range);
|
||||
@@ -41,7 +40,7 @@ int Dot_Coh_TAS::dot_coh_tas_process(std::vector <PointRecv> *dat
|
||||
point_0_F=static_cast<float>((*data_input)[i].azimuth);
|
||||
(*data_input)[loop_of_point].use_flag=1;
|
||||
}
|
||||
else if( (fabs(point_0_R-point_1_R)<=DOT_COH_RANGE && delta_F <=DOT_COH_AZI/180.0*PI && fabs(point_0_V-point_1_V)<=DOT_COH_V)
|
||||
else if( (fabs(point_0_R-point_1_R)<=rdp_parameter.point_cohesion_range && delta_F <=rdp_parameter.point_cohesion_azimuth/180.0*PI && fabs(point_0_V-point_1_V)<=rdp_parameter.point_cohesion_velocity)
|
||||
&& point_0_A >= point_1_A)
|
||||
{
|
||||
(*data_input)[i].use_flag=1;
|
||||
@@ -55,7 +54,7 @@ int Dot_Coh_TAS::dot_coh_tas_process(std::vector <PointRecv> *dat
|
||||
std::vector <PointRecv>::iterator Iter;
|
||||
for (Iter=data_input->begin(); Iter!=data_input->end();)
|
||||
{
|
||||
if((*Iter).use_flag==1 || (*Iter).range <R_MIN || (*Iter).range > R_MAX )
|
||||
if((*Iter).use_flag==1 || (*Iter).range <rdp_parameter.point_cohesion_range_min || (*Iter).range > rdp_parameter.point_cohesion_range_max )
|
||||
{
|
||||
data_input->erase(Iter);
|
||||
Iter=data_input->begin();
|
||||
|
||||
@@ -26,13 +26,13 @@ void kalman:: kalman_filter_init_2dots(double Z0[2], double Z1[2], double T,doub
|
||||
double rho,theta;
|
||||
coor_trans Coor_trans;
|
||||
Coor_trans.cart2polar(Z1[0],Z1[1],&rho,&theta);
|
||||
double lambda_theta=exp(-SIGMA_A*SIGMA_A/2);
|
||||
double lambda_theta1=exp(-2*SIGMA_A*SIGMA_A);
|
||||
double lambda_theta=exp(-rdp_parameter.sigma_azimuth*rdp_parameter.sigma_azimuth/2);
|
||||
double lambda_theta1=exp(-2*rdp_parameter.sigma_azimuth*rdp_parameter.sigma_azimuth);
|
||||
|
||||
double R[2][2];
|
||||
R[0][0]=(pow(lambda_theta,-2)-2)*rho*rho*cos(theta)*cos(theta)+0.5*(rho*rho+SIGMA_R*SIGMA_R)*(1+lambda_theta1*cos(2*theta));
|
||||
R[1][1]=(pow(lambda_theta,-2)-2)*rho*rho*sin(theta)*sin(theta)+0.5*(rho*rho+SIGMA_R*SIGMA_R)*(1-lambda_theta1*cos(2*theta));
|
||||
R[0][1]=(pow(lambda_theta,-2)-2)*rho*rho*sin(theta)*cos(theta)+0.5*(rho*rho+SIGMA_R*SIGMA_R)*lambda_theta1*sin(2*theta);
|
||||
R[0][0]=(pow(lambda_theta,-2)-2)*rho*rho*cos(theta)*cos(theta)+0.5*(rho*rho+rdp_parameter.sigma_range*rdp_parameter.sigma_range)*(1+lambda_theta1*cos(2*theta));
|
||||
R[1][1]=(pow(lambda_theta,-2)-2)*rho*rho*sin(theta)*sin(theta)+0.5*(rho*rho+rdp_parameter.sigma_range*rdp_parameter.sigma_range)*(1-lambda_theta1*cos(2*theta));
|
||||
R[0][1]=(pow(lambda_theta,-2)-2)*rho*rho*sin(theta)*cos(theta)+0.5*(rho*rho+rdp_parameter.sigma_range*rdp_parameter.sigma_range)*lambda_theta1*sin(2*theta);
|
||||
R[1][0]=R[0][1];
|
||||
|
||||
P[0][0]=R[0][0];
|
||||
@@ -92,13 +92,13 @@ double kalman::d_cal_track_init(double Z[2],double X[4],double P[4][4],double T)
|
||||
double rho,theta;
|
||||
coor_trans Coor_trans;
|
||||
Coor_trans.cart2polar(Z[0],Z[1],&rho,&theta);
|
||||
double lambda_theta=exp(-SIGMA_A*SIGMA_A/2);
|
||||
double lambda_theta1=exp(-2*SIGMA_A*SIGMA_A);
|
||||
double lambda_theta=exp(-rdp_parameter.sigma_azimuth*rdp_parameter.sigma_azimuth/2);
|
||||
double lambda_theta1=exp(-2*rdp_parameter.sigma_azimuth*rdp_parameter.sigma_azimuth);
|
||||
|
||||
Matrix2d R;
|
||||
R(0,0)=(pow(lambda_theta,-2)-2)*rho*rho*cos(theta)*cos(theta)+0.5*(rho*rho+SIGMA_R*SIGMA_R)*(1+lambda_theta1*cos(2*theta));
|
||||
R(1,1)=(pow(lambda_theta,-2)-2)*rho*rho*sin(theta)*sin(theta)+0.5*(rho*rho+SIGMA_R*SIGMA_R)*(1-lambda_theta1*cos(2*theta));
|
||||
R(0,1)=(pow(lambda_theta,-2)-2)*rho*rho*sin(theta)*cos(theta)+0.5*(rho*rho+SIGMA_R*SIGMA_R)*lambda_theta1*sin(2*theta);
|
||||
R(0,0)=(pow(lambda_theta,-2)-2)*rho*rho*cos(theta)*cos(theta)+0.5*(rho*rho+rdp_parameter.sigma_range*rdp_parameter.sigma_range)*(1+lambda_theta1*cos(2*theta));
|
||||
R(1,1)=(pow(lambda_theta,-2)-2)*rho*rho*sin(theta)*sin(theta)+0.5*(rho*rho+rdp_parameter.sigma_range*rdp_parameter.sigma_range)*(1-lambda_theta1*cos(2*theta));
|
||||
R(0,1)=(pow(lambda_theta,-2)-2)*rho*rho*sin(theta)*cos(theta)+0.5*(rho*rho+rdp_parameter.sigma_range*rdp_parameter.sigma_range)*lambda_theta1*sin(2*theta);
|
||||
R(1,0)=R(0,1);
|
||||
|
||||
Matrix4d P_present;
|
||||
@@ -145,23 +145,23 @@ void kalman::kalman_filter_init_3dots(double Z0[2], double Z1[2],double Z2[2], d
|
||||
double rho,theta;
|
||||
coor_trans Coor_trans;
|
||||
Coor_trans.cart2polar(Z0[0],Z0[1],&rho,&theta);
|
||||
double lambda_theta=exp(-SIGMA_A*SIGMA_A/2);
|
||||
double lambda_theta1=exp(-2*SIGMA_A*SIGMA_A);
|
||||
R0[0][0]=(pow(lambda_theta,-2)-2)*rho*rho*cos(theta)*cos(theta)+0.5*(rho*rho+SIGMA_R*SIGMA_R)*(1+lambda_theta1*cos(2*theta));
|
||||
R0[1][1]=(pow(lambda_theta,-2)-2)*rho*rho*sin(theta)*sin(theta)+0.5*(rho*rho+SIGMA_R*SIGMA_R)*(1-lambda_theta1*cos(2*theta));
|
||||
R0[0][1]=(pow(lambda_theta,-2)-2)*rho*rho*sin(theta)*cos(theta)+0.5*(rho*rho+SIGMA_R*SIGMA_R)*lambda_theta1*sin(2*theta);
|
||||
double lambda_theta=exp(-rdp_parameter.sigma_azimuth*rdp_parameter.sigma_azimuth/2);
|
||||
double lambda_theta1=exp(-2*rdp_parameter.sigma_azimuth*rdp_parameter.sigma_azimuth);
|
||||
R0[0][0]=(pow(lambda_theta,-2)-2)*rho*rho*cos(theta)*cos(theta)+0.5*(rho*rho+rdp_parameter.sigma_range*rdp_parameter.sigma_range)*(1+lambda_theta1*cos(2*theta));
|
||||
R0[1][1]=(pow(lambda_theta,-2)-2)*rho*rho*sin(theta)*sin(theta)+0.5*(rho*rho+rdp_parameter.sigma_range*rdp_parameter.sigma_range)*(1-lambda_theta1*cos(2*theta));
|
||||
R0[0][1]=(pow(lambda_theta,-2)-2)*rho*rho*sin(theta)*cos(theta)+0.5*(rho*rho+rdp_parameter.sigma_range*rdp_parameter.sigma_range)*lambda_theta1*sin(2*theta);
|
||||
R0[1][0]=R0[0][1];
|
||||
|
||||
Coor_trans.cart2polar(Z1[0],Z1[1],&rho,&theta);
|
||||
R1[0][0]=(pow(lambda_theta,-2)-2)*rho*rho*cos(theta)*cos(theta)+0.5*(rho*rho+SIGMA_R*SIGMA_R)*(1+lambda_theta1*cos(2*theta));
|
||||
R1[1][1]=(pow(lambda_theta,-2)-2)*rho*rho*sin(theta)*sin(theta)+0.5*(rho*rho+SIGMA_R*SIGMA_R)*(1-lambda_theta1*cos(2*theta));
|
||||
R1[0][1]=(pow(lambda_theta,-2)-2)*rho*rho*sin(theta)*cos(theta)+0.5*(rho*rho+SIGMA_R*SIGMA_R)*lambda_theta1*sin(2*theta);
|
||||
R1[0][0]=(pow(lambda_theta,-2)-2)*rho*rho*cos(theta)*cos(theta)+0.5*(rho*rho+rdp_parameter.sigma_range*rdp_parameter.sigma_range)*(1+lambda_theta1*cos(2*theta));
|
||||
R1[1][1]=(pow(lambda_theta,-2)-2)*rho*rho*sin(theta)*sin(theta)+0.5*(rho*rho+rdp_parameter.sigma_range*rdp_parameter.sigma_range)*(1-lambda_theta1*cos(2*theta));
|
||||
R1[0][1]=(pow(lambda_theta,-2)-2)*rho*rho*sin(theta)*cos(theta)+0.5*(rho*rho+rdp_parameter.sigma_range*rdp_parameter.sigma_range)*lambda_theta1*sin(2*theta);
|
||||
R1[1][0]=R1[0][1];
|
||||
|
||||
Coor_trans.cart2polar(Z2[0],Z2[1],&rho,&theta);
|
||||
R2[0][0]=(pow(lambda_theta,-2)-2)*rho*rho*cos(theta)*cos(theta)+0.5*(rho*rho+SIGMA_R*SIGMA_R)*(1+lambda_theta1*cos(2*theta));
|
||||
R2[1][1]=(pow(lambda_theta,-2)-2)*rho*rho*sin(theta)*sin(theta)+0.5*(rho*rho+SIGMA_R*SIGMA_R)*(1-lambda_theta1*cos(2*theta));
|
||||
R2[0][1]=(pow(lambda_theta,-2)-2)*rho*rho*sin(theta)*cos(theta)+0.5*(rho*rho+SIGMA_R*SIGMA_R)*lambda_theta1*sin(2*theta);
|
||||
R2[0][0]=(pow(lambda_theta,-2)-2)*rho*rho*cos(theta)*cos(theta)+0.5*(rho*rho+rdp_parameter.sigma_range*rdp_parameter.sigma_range)*(1+lambda_theta1*cos(2*theta));
|
||||
R2[1][1]=(pow(lambda_theta,-2)-2)*rho*rho*sin(theta)*sin(theta)+0.5*(rho*rho+rdp_parameter.sigma_range*rdp_parameter.sigma_range)*(1-lambda_theta1*cos(2*theta));
|
||||
R2[0][1]=(pow(lambda_theta,-2)-2)*rho*rho*sin(theta)*cos(theta)+0.5*(rho*rho+rdp_parameter.sigma_range*rdp_parameter.sigma_range)*lambda_theta1*sin(2*theta);
|
||||
R2[1][0]=R2[0][1];
|
||||
|
||||
double P11[3][3]={{R2[0][0], R2[0][0]/T2, (R2[0][0]/T2)/((T2+T1)/2)} ,
|
||||
@@ -218,13 +218,13 @@ double kalman::d_cal(double Z[2],double X[6],double P[6][6])
|
||||
double rho,theta;
|
||||
coor_trans Coor_trans;
|
||||
Coor_trans.cart2polar(Z[0],Z[1],&rho,&theta);
|
||||
double lambda_theta=exp(-SIGMA_A*SIGMA_A/2);
|
||||
double lambda_theta1=exp(-2*SIGMA_A*SIGMA_A);
|
||||
double lambda_theta=exp(-rdp_parameter.sigma_azimuth*rdp_parameter.sigma_azimuth/2);
|
||||
double lambda_theta1=exp(-2*rdp_parameter.sigma_azimuth*rdp_parameter.sigma_azimuth);
|
||||
|
||||
Matrix2d R;
|
||||
R(0,0)=(pow(lambda_theta,-2)-2)*rho*rho*cos(theta)*cos(theta)+0.5*(rho*rho+SIGMA_R*SIGMA_R)*(1+lambda_theta1*cos(2*theta));
|
||||
R(1,1)=(pow(lambda_theta,-2)-2)*rho*rho*sin(theta)*sin(theta)+0.5*(rho*rho+SIGMA_R*SIGMA_R)*(1-lambda_theta1*cos(2*theta));
|
||||
R(0,1)=(pow(lambda_theta,-2)-2)*rho*rho*sin(theta)*cos(theta)+0.5*(rho*rho+SIGMA_R*SIGMA_R)*lambda_theta1*sin(2*theta);
|
||||
R(0,0)=(pow(lambda_theta,-2)-2)*rho*rho*cos(theta)*cos(theta)+0.5*(rho*rho+rdp_parameter.sigma_range*rdp_parameter.sigma_range)*(1+lambda_theta1*cos(2*theta));
|
||||
R(1,1)=(pow(lambda_theta,-2)-2)*rho*rho*sin(theta)*sin(theta)+0.5*(rho*rho+rdp_parameter.sigma_range*rdp_parameter.sigma_range)*(1-lambda_theta1*cos(2*theta));
|
||||
R(0,1)=(pow(lambda_theta,-2)-2)*rho*rho*sin(theta)*cos(theta)+0.5*(rho*rho+rdp_parameter.sigma_range*rdp_parameter.sigma_range)*lambda_theta1*sin(2*theta);
|
||||
R(1,0)=R(0,1);
|
||||
|
||||
//S
|
||||
@@ -284,9 +284,9 @@ double kalman::d_cal_EKF(double F[6][6], double Q[6][6] ,double Z[3],double X[6]
|
||||
H(2,3) = -x*(vy*x-vx*y)/((x*x+y*y)*sqrt(x*x+y*y)); H(2,4)=-y/sqrt(x*x+y*y); H(2,5)=0;
|
||||
|
||||
Matrix3d R;
|
||||
R(0,0)=SIGMA_R*SIGMA_R;
|
||||
R(1,1)=SIGMA_A*SIGMA_A;
|
||||
R(2,2)=SIGMA_V*SIGMA_V;
|
||||
R(0,0)=rdp_parameter.sigma_range*rdp_parameter.sigma_range;
|
||||
R(1,1)=rdp_parameter.sigma_azimuth*rdp_parameter.sigma_azimuth;
|
||||
R(2,2)=rdp_parameter.sigma_velocity*rdp_parameter.sigma_velocity;
|
||||
R(0,1)=0;
|
||||
R(1,0)=0;
|
||||
R(2,0)=0;
|
||||
@@ -362,15 +362,15 @@ double kalman:: d_cal_with_doppler(double F[6][6], double Q[6][6] , double Z[2],
|
||||
double rho,theta;
|
||||
coor_trans Coor_trans;
|
||||
Coor_trans.cart2polar(Z[0],Z[1],&rho,&theta);
|
||||
double lambda_theta=exp(-SIGMA_A*SIGMA_A/2);
|
||||
double lambda_theta1=exp(-2*SIGMA_A*SIGMA_A);
|
||||
double lambda_theta=exp(-rdp_parameter.sigma_azimuth*rdp_parameter.sigma_azimuth/2);
|
||||
double lambda_theta1=exp(-2*rdp_parameter.sigma_azimuth*rdp_parameter.sigma_azimuth);
|
||||
|
||||
Matrix3d R;
|
||||
R(0,0)=(pow(lambda_theta,-2)-2)*rho*rho*cos(theta)*cos(theta)+0.5*(rho*rho+SIGMA_R*SIGMA_R)*(1+lambda_theta1*cos(2*theta));
|
||||
R(1,1)=(pow(lambda_theta,-2)-2)*rho*rho*sin(theta)*sin(theta)+0.5*(rho*rho+SIGMA_R*SIGMA_R)*(1-lambda_theta1*cos(2*theta));
|
||||
R(0,1)=(pow(lambda_theta,-2)-2)*rho*rho*sin(theta)*cos(theta)+0.5*(rho*rho+SIGMA_R*SIGMA_R)*lambda_theta1*sin(2*theta);
|
||||
R(0,0)=(pow(lambda_theta,-2)-2)*rho*rho*cos(theta)*cos(theta)+0.5*(rho*rho+rdp_parameter.sigma_range*rdp_parameter.sigma_range)*(1+lambda_theta1*cos(2*theta));
|
||||
R(1,1)=(pow(lambda_theta,-2)-2)*rho*rho*sin(theta)*sin(theta)+0.5*(rho*rho+rdp_parameter.sigma_range*rdp_parameter.sigma_range)*(1-lambda_theta1*cos(2*theta));
|
||||
R(0,1)=(pow(lambda_theta,-2)-2)*rho*rho*sin(theta)*cos(theta)+0.5*(rho*rho+rdp_parameter.sigma_range*rdp_parameter.sigma_range)*lambda_theta1*sin(2*theta);
|
||||
R(1,0)=R(0,1);
|
||||
R(2,2)=SIGMA_V*SIGMA_V;
|
||||
R(2,2)=rdp_parameter.sigma_velocity*rdp_parameter.sigma_velocity;
|
||||
R(2,0)=0;
|
||||
R(2,1)=0;
|
||||
R(0,2)=0;
|
||||
@@ -477,9 +477,9 @@ void kalman::kalman_filter_EKF(double F[6][6], double Q[6][6], double X_cur[6],d
|
||||
H(2,3) = -x*(vy*x-vx*y)/((x*x+y*y)*sqrt(x*x+y*y)); H(2,4)=-y/sqrt(x*x+y*y); H(2,5)=0;
|
||||
|
||||
Matrix3d R;
|
||||
R(0,0)=SIGMA_R*SIGMA_R;
|
||||
R(1,1)=SIGMA_A*SIGMA_A;
|
||||
R(2,2)=SIGMA_V*SIGMA_V;
|
||||
R(0,0)=rdp_parameter.sigma_range*rdp_parameter.sigma_range;
|
||||
R(1,1)=rdp_parameter.sigma_azimuth*rdp_parameter.sigma_azimuth;
|
||||
R(2,2)=rdp_parameter.sigma_velocity*rdp_parameter.sigma_velocity;
|
||||
R(0,1)=0;
|
||||
R(1,0)=0;
|
||||
R(2,0)=0;
|
||||
@@ -570,13 +570,13 @@ void kalman::kalman_filter(double F[6][6], double Q[6][6],
|
||||
double rho,theta;
|
||||
coor_trans Coor_trans;
|
||||
Coor_trans.cart2polar(Z[0],Z[1],&rho,&theta);
|
||||
double lambda_theta=exp(-SIGMA_A*SIGMA_A/2);
|
||||
double lambda_theta1=exp(-2*SIGMA_A*SIGMA_A);
|
||||
double lambda_theta=exp(-rdp_parameter.sigma_azimuth*rdp_parameter.sigma_azimuth/2);
|
||||
double lambda_theta1=exp(-2*rdp_parameter.sigma_azimuth*rdp_parameter.sigma_azimuth);
|
||||
|
||||
Matrix2d R;
|
||||
R(0,0)=(pow(lambda_theta,-2)-2)*rho*rho*cos(theta)*cos(theta)+0.5*(rho*rho+SIGMA_R*SIGMA_R)*(1+lambda_theta1*cos(2*theta));
|
||||
R(1,1)=(pow(lambda_theta,-2)-2)*rho*rho*sin(theta)*sin(theta)+0.5*(rho*rho+SIGMA_R*SIGMA_R)*(1-lambda_theta1*cos(2*theta));
|
||||
R(0,1)=(pow(lambda_theta,-2)-2)*rho*rho*sin(theta)*cos(theta)+0.5*(rho*rho+SIGMA_R*SIGMA_R)*lambda_theta1*sin(2*theta);
|
||||
R(0,0)=(pow(lambda_theta,-2)-2)*rho*rho*cos(theta)*cos(theta)+0.5*(rho*rho+rdp_parameter.sigma_range*rdp_parameter.sigma_range)*(1+lambda_theta1*cos(2*theta));
|
||||
R(1,1)=(pow(lambda_theta,-2)-2)*rho*rho*sin(theta)*sin(theta)+0.5*(rho*rho+rdp_parameter.sigma_range*rdp_parameter.sigma_range)*(1-lambda_theta1*cos(2*theta));
|
||||
R(0,1)=(pow(lambda_theta,-2)-2)*rho*rho*sin(theta)*cos(theta)+0.5*(rho*rho+rdp_parameter.sigma_range*rdp_parameter.sigma_range)*lambda_theta1*sin(2*theta);
|
||||
R(1,0)=R(0,1);
|
||||
|
||||
//S
|
||||
@@ -663,9 +663,9 @@ double kalman::d_cal_track_init_EKF(double Z[3],double X[4],double P[4][4],doubl
|
||||
H(2,2) = -x*(vy*x-vx*y)/((x*x+y*y)*sqrt(x*x+y*y)); H(2,3)=-y/sqrt(x*x+y*y);
|
||||
|
||||
Matrix3d R;
|
||||
R(0,0)=SIGMA_R*SIGMA_R;
|
||||
R(1,1)=SIGMA_A*SIGMA_A;
|
||||
R(2,2)=SIGMA_V*SIGMA_V;
|
||||
R(0,0)=rdp_parameter.sigma_range*rdp_parameter.sigma_range;
|
||||
R(1,1)=rdp_parameter.sigma_azimuth*rdp_parameter.sigma_azimuth;
|
||||
R(2,2)=rdp_parameter.sigma_velocity*rdp_parameter.sigma_velocity;
|
||||
R(0,1)=0;
|
||||
R(1,0)=0;
|
||||
R(2,0)=0;
|
||||
@@ -744,15 +744,15 @@ double kalman::d_cal_track_init_with_doppler(double Z[2],double X[4],double P[4]
|
||||
double rho,theta;
|
||||
coor_trans Coor_trans;
|
||||
Coor_trans.cart2polar(Z[0],Z[1],&rho,&theta);
|
||||
double lambda_theta=exp(-SIGMA_A*SIGMA_A/2);
|
||||
double lambda_theta1=exp(-2*SIGMA_A*SIGMA_A);
|
||||
double lambda_theta=exp(-rdp_parameter.sigma_azimuth*rdp_parameter.sigma_azimuth/2);
|
||||
double lambda_theta1=exp(-2*rdp_parameter.sigma_azimuth*rdp_parameter.sigma_azimuth);
|
||||
|
||||
Matrix3d R;
|
||||
R(0,0)=(pow(lambda_theta,-2)-2)*rho*rho*cos(theta)*cos(theta)+0.5*(rho*rho+SIGMA_R*SIGMA_R)*(1+lambda_theta1*cos(2*theta));
|
||||
R(1,1)=(pow(lambda_theta,-2)-2)*rho*rho*sin(theta)*sin(theta)+0.5*(rho*rho+SIGMA_R*SIGMA_R)*(1-lambda_theta1*cos(2*theta));
|
||||
R(0,1)=(pow(lambda_theta,-2)-2)*rho*rho*sin(theta)*cos(theta)+0.5*(rho*rho+SIGMA_R*SIGMA_R)*lambda_theta1*sin(2*theta);
|
||||
R(0,0)=(pow(lambda_theta,-2)-2)*rho*rho*cos(theta)*cos(theta)+0.5*(rho*rho+rdp_parameter.sigma_range*rdp_parameter.sigma_range)*(1+lambda_theta1*cos(2*theta));
|
||||
R(1,1)=(pow(lambda_theta,-2)-2)*rho*rho*sin(theta)*sin(theta)+0.5*(rho*rho+rdp_parameter.sigma_range*rdp_parameter.sigma_range)*(1-lambda_theta1*cos(2*theta));
|
||||
R(0,1)=(pow(lambda_theta,-2)-2)*rho*rho*sin(theta)*cos(theta)+0.5*(rho*rho+rdp_parameter.sigma_range*rdp_parameter.sigma_range)*lambda_theta1*sin(2*theta);
|
||||
R(1,0)=R(0,1);
|
||||
R(2,2)=SIGMA_V*SIGMA_V;
|
||||
R(2,2)=rdp_parameter.sigma_velocity*rdp_parameter.sigma_velocity;
|
||||
R(2,0)=0;
|
||||
R(2,1)=0;
|
||||
R(0,2)=0;
|
||||
@@ -779,7 +779,7 @@ double kalman::d_cal_track_init_with_doppler(double Z[2],double X[4],double P[4]
|
||||
|
||||
double kalman::Bind_speed(double prt,double freq_ind)
|
||||
{
|
||||
double freq=FREQ0+freq_ind*0.02;
|
||||
double freq=rdp_parameter.freq_0_GHz+freq_ind*0.02;
|
||||
|
||||
if (prt <= 0.0 || freq <= 0.0)
|
||||
return 1.0; // 避免除零;正常流程 PRI 不允许为 0
|
||||
|
||||
@@ -4,18 +4,11 @@
|
||||
/**********************************雷达参数*************************************************/
|
||||
#define PI 3.1415926f
|
||||
|
||||
//频点(GHz)
|
||||
#define FREQ0 9.2
|
||||
|
||||
/**********************************版本信息*************************************************/
|
||||
#define MAJOR_VERSION 1
|
||||
#define MINOR_VERSION 5
|
||||
#define PATCH_VERSION 8
|
||||
#ifdef PHASE_SCANNING
|
||||
#define VERSION_INFO "PS"
|
||||
#else
|
||||
#define VERSION_INFO "MS"
|
||||
#endif
|
||||
#define PATCH_VERSION 9
|
||||
#define VERSION_INFO "GENERIC"
|
||||
|
||||
#define STRINGIFY_IMPL(x) #x
|
||||
#define STRINGIFY(x) STRINGIFY_IMPL(x)
|
||||
@@ -26,13 +19,15 @@
|
||||
STRINGIFY(PATCH_VERSION) "." \
|
||||
VERSION_INFO
|
||||
|
||||
/**********************************日志输出开关**********************************************/
|
||||
// 日志文件输出功能开关:
|
||||
// 1) 定义(默认)RDP_LOG_ENABLE:启用日志文件输出 —— 调用 Data_Process_Factory::GetB()
|
||||
// 时自动将 std::cout 重定向到日志文件,日志文件存放于 RDP_LOG_DIR 目录,
|
||||
// 文件名按当前日期+时间自动命名(如 rdp_20240826_153000.log);
|
||||
// 2) 注释掉 RDP_LOG_ENABLE:关闭 —— 不做重定向,日志仍输出到标准输出(控制台)。
|
||||
// #define RDP_LOG_ENABLE
|
||||
/**********************************日志输出*************************************************/
|
||||
// 日志基于内置 spdlog v1.17.0(third_party/spdlog,来源见其 UPSTREAM.md):
|
||||
// 1) 每次调用 Data_Process_Factory::GetB() 创建实例时,rdp_log::Init() 同时输出到
|
||||
// 标准输出与日志文件(RDP_LOG_DIR 目录),日志文件名按当前日期+时间自动命名
|
||||
// (如 rdp_20240826_153000.log,每次初始化一个文件);Destroy() 时关闭日志文件;
|
||||
// 2) 原 RDP_LOG_ENABLE 布尔开关已移除:日志级别改由 spdlog 的 SPDLOG_ACTIVE_LEVEL
|
||||
// 在编译期控制,由 CMake 选项 RDP_LOG_LEVEL(AUTO/TRACE/DEBUG/INFO/WARN/ERROR/
|
||||
// CRITICAL/OFF)映射;AUTO 时 Debug 构建 = TRACE、其余构建 = INFO,OFF 表示
|
||||
// 编译期关闭全部 RDP_LOG_* 日志。低于该级别的日志实参不求值。详见 README 10.4。
|
||||
|
||||
//日志文件输出目录(相对当前工作目录的路径,目录不存在时自动创建)
|
||||
#define RDP_LOG_DIR ".\\rdp_logs\\"
|
||||
@@ -90,133 +85,14 @@
|
||||
#define RDP_RECORD_DROP_LOG_INTERVAL 1024u
|
||||
#endif
|
||||
|
||||
//#define MECHANICAL_SCANNING
|
||||
//#define PHASE_SCANNING
|
||||
|
||||
/******************************通用数据处理参数**************************************/
|
||||
//航迹质量管理参数
|
||||
#define TRACK_QUALITY_MAX 100
|
||||
#define TRACK_QUALITY_MIN 0
|
||||
#define TRACK_QUALITY_D_WEIGHT 0.5
|
||||
#define TRACK_QUALITY_SNR_WEIGHT 0.3
|
||||
#define TRACK_QUALITY_LEN_WEIGHT 0.2
|
||||
#define TRACK_QUALITY_ITER_COEFF 0.8
|
||||
#define TRACK_QUALITY_DECREASE_COEFF_O2 -0.0008
|
||||
#define TRACK_QUALITY_DECREASE_COEFF_O1 -0.0025
|
||||
|
||||
//航迹起批质量条件
|
||||
#define TRACK_START_QUALITY_L3 0.7
|
||||
#define TRACK_START_QUALITY_L4 0.6
|
||||
#define TRACK_START_QUALITY_L5 0.5
|
||||
#define TRACK_START_QUALITY_L6 0.4
|
||||
#define TRACK_START_QUALITY_L7 0.3
|
||||
|
||||
//航迹消批质量条件
|
||||
#define TRACK_DIE_QUALITY 0.3
|
||||
#define TRACK_DIE_QUALITY_PRAMARY 0.3
|
||||
|
||||
#ifdef MECHANICAL_SCANNING
|
||||
/******************************机扫数据处理参数**************************************/
|
||||
// #define DATA_RATE_TAS 0.0625
|
||||
|
||||
#define SIGMA_R 10.0 //测量误差
|
||||
#define SIGMA_A 0.02
|
||||
#define SIGMA_E 0.2
|
||||
#define SIGMA_V 2.0
|
||||
|
||||
#define DOT_COH_RANGE 80 //点迹凝聚
|
||||
#define DOT_COH_V 2
|
||||
#define DOT_COH_AZI 6
|
||||
|
||||
#define MAX_BEAM_NUM 100 //最大TWS波位数
|
||||
/******************************编译期容量常量***********************************************/
|
||||
// 说明:下列常量是「接口/内存布局」需要的编译期容量,不是雷达工作参数,
|
||||
// 雷达工作参数统一由 TOML 配置文件经 parameters_reload() 载入 struct.h::RDP_parameter。
|
||||
|
||||
//输出航迹数组的容量(宿主接口固定维度,见 data_process_class_dll.h)
|
||||
#define MAX_TRACK_NUM 500
|
||||
#define MAX_TRACK_INDEX 500 //最大航迹批号
|
||||
|
||||
#define ASSOCIATE_THRESHOLD_MIN 1
|
||||
#define ASSOCIATE_THRESHOLD_MID 3 //关联波门
|
||||
#define ASSOCIATE_THRESHOLD_MAX 10
|
||||
|
||||
#define R_MIN 100
|
||||
#define R_MAX 100000
|
||||
|
||||
#define TRACK_START_THRESHOLD 8 //起航波门 3 越大越容易起批
|
||||
#define ALPHA_START 60 //起航夹角 120 越大越容易起批
|
||||
|
||||
#define MAX_EXTRAPOLATE_NUM 3 //最大外推点数
|
||||
#define MAX_EXTRAPOLATE_NUM_TAS 48 //TAS最大外推点数
|
||||
|
||||
#define TRACK_DIE_ROUND 6 // 航迹消亡圈数
|
||||
#define TRACK_DIE_ROUND_PRIMARY 10 // 重点航迹消亡圈数
|
||||
#define TRACK_DIE_ROUND_TAS 96 //TAS航迹消亡圈数
|
||||
|
||||
#define ASSO_THORD 4
|
||||
|
||||
#define H_F_WIN_LEN 3
|
||||
|
||||
#define TAS_QUEUE_LENGTH 1 //Tas调用的cpi数
|
||||
#define MAX_TAS_NUM 1
|
||||
|
||||
#define UNCONF_TARGET 0
|
||||
|
||||
#define DIRECT_TRACKING_QUEUE_LENGTH 10
|
||||
|
||||
#pragma message("MECHANICAL_SCANNING")
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef PHASE_SCANNING
|
||||
|
||||
/******************************相扫数据处理参数**************************************/
|
||||
// #define DATA_RATE_TAS 0.3
|
||||
|
||||
#define SIGMA_R 10.0 //测量误差
|
||||
#define SIGMA_A 0.02
|
||||
#define SIGMA_E 0.2
|
||||
#define SIGMA_V 2.0
|
||||
|
||||
#define DOT_COH_RANGE 80 //点迹凝聚
|
||||
#define DOT_COH_V 2
|
||||
#define DOT_COH_AZI 6
|
||||
|
||||
#define MAX_BEAM_NUM 100 //最大TWS波位数
|
||||
|
||||
#define MAX_TRACK_NUM 500
|
||||
#define MAX_TRACK_INDEX 500 //最大航迹批号
|
||||
|
||||
#define ASSOCIATE_THRESHOLD_MIN 1
|
||||
#define ASSOCIATE_THRESHOLD_MID 3 //关联波门
|
||||
#define ASSOCIATE_THRESHOLD_MAX 10
|
||||
|
||||
#define R_MIN 100
|
||||
#define R_MAX 100000
|
||||
|
||||
//#define V_MAX 500 //最大速度
|
||||
//#define V_MIN 1 //速度最小
|
||||
#define TRACK_START_THRESHOLD 8 //起航波门 3 越大越容易起批
|
||||
#define ALPHA_START 60 //起航夹角 120 越大越容易起批
|
||||
|
||||
#define MAX_EXTRAPOLATE_NUM 3 //最大外推点数
|
||||
#define MAX_EXTRAPOLATE_NUM_TAS 5 //TAS最大外推点数
|
||||
|
||||
#define TRACK_DIE_ROUND 6 // 航迹消亡圈数
|
||||
#define TRACK_DIE_ROUND_PRIMARY 10 // 重点航迹消亡圈数
|
||||
#define TRACK_DIE_ROUND_TAS 20 //TAS航迹消亡圈数
|
||||
|
||||
#define ASSO_THORD 4
|
||||
|
||||
#define H_F_WIN_LEN 6
|
||||
|
||||
#define TAS_QUEUE_LENGTH 4 //Tas调用的cpi数
|
||||
#define MAX_TAS_NUM 4
|
||||
|
||||
#define UNCONF_TARGET 0
|
||||
|
||||
#define DIRECT_TRACKING_QUEUE_LENGTH 10
|
||||
|
||||
#pragma message("PHASE_SCANNING")
|
||||
|
||||
#endif
|
||||
//TAS 跟踪队列容量上限(运行期配置项 tas_track_num_max 不得超过该值)
|
||||
#define RDP_TAS_QUEUE_CAPACITY 30
|
||||
|
||||
#define Round(x) (((x) > 0) \
|
||||
? (((int)((x) + 0.5) > (int)(x)) ? ((int)(x) + 1) : ((int)(x))) \
|
||||
|
||||
@@ -1,32 +1,30 @@
|
||||
#include "rdp_log.h"
|
||||
#include "parameters.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <cstdio>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <time.h>
|
||||
#include <direct.h>
|
||||
#include <io.h>
|
||||
|
||||
#include <spdlog/sinks/basic_file_sink.h>
|
||||
#include <spdlog/sinks/stdout_sinks.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
//日志模块内部状态:日志文件流 + 重定向前 std::cout 的原始缓冲区
|
||||
//日志行格式:与旧版 [时间戳][文件:行号] 风格一致,另加 spdlog 级别
|
||||
const char *const kPattern = "[%Y-%m-%d %H:%M:%S.%e] [%l] [%s:%#] %v";
|
||||
|
||||
//日志模块内部状态:默认 logger(标准输出 + 日志文件)与当前日志文件路径
|
||||
struct RdpLogState
|
||||
{
|
||||
std::ofstream file; //日志文件输出流
|
||||
std::streambuf *original_buf; //重定向前 std::cout 的原始缓冲区
|
||||
bool enabled; //是否已成功重定向到日志文件
|
||||
std::shared_ptr<spdlog::logger> logger; //默认 logger(含标准输出 sink,成功时另含文件 sink)
|
||||
std::string log_path; //当前日志文件全路径(降级为仅标准输出时为空)
|
||||
bool initialized; //是否已初始化(含文件创建失败时的降级初始化)
|
||||
|
||||
RdpLogState() : original_buf(0), enabled(false) {}
|
||||
|
||||
~RdpLogState()
|
||||
{
|
||||
//进程退出兜底:若宿主进程未调用 Data_Process_Factory::Destroy(),
|
||||
//此处恢复 std::cout,避免其仍指向即将析构的日志文件流缓冲区。
|
||||
if (enabled && original_buf)
|
||||
{
|
||||
std::cout.rdbuf(original_buf);
|
||||
enabled = false;
|
||||
}
|
||||
}
|
||||
RdpLogState() : initialized(false) {}
|
||||
};
|
||||
|
||||
RdpLogState &State()
|
||||
@@ -35,36 +33,78 @@ namespace
|
||||
return s;
|
||||
}
|
||||
|
||||
//当前本地时间字符串,格式 "YYYY-MM-DD HH:MM:SS"
|
||||
std::string CurrentTime()
|
||||
//创建日志目录(目录已存在时 _mkdir 返回 EEXIST,忽略;逻辑与原 rdp_log/rdp_record 一致)
|
||||
void EnsureDir(const std::string &dir_with_separator)
|
||||
{
|
||||
char buf[32] = {0};
|
||||
time_t now = time(NULL);
|
||||
std::string dir = dir_with_separator;
|
||||
while (dir.size() > 0 && (dir[dir.size() - 1] == '\\' || dir[dir.size() - 1] == '/'))
|
||||
{
|
||||
dir.erase(dir.size() - 1);
|
||||
}
|
||||
if (dir.size() > 0)
|
||||
{
|
||||
_mkdir(dir.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
//当前本地时间字符串,格式 "YYYYMMDD_HHMMSS"
|
||||
bool TimeStamp(char *buf, size_t size)
|
||||
{
|
||||
time_t now = time(NULL);
|
||||
struct tm local;
|
||||
if (localtime_s(&local, &now) != 0)
|
||||
{
|
||||
return std::string();
|
||||
return false;
|
||||
}
|
||||
strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &local);
|
||||
return std::string(buf);
|
||||
return strftime(buf, size, "%Y%m%d_%H%M%S", &local) != 0;
|
||||
}
|
||||
|
||||
//提取纯文件名(去掉路径前缀),日志前缀中只显示 文件名:行号
|
||||
const char *FileName(const char *path)
|
||||
//生成日志文件路径:RDP_LOG_DIR + rdp_YYYYMMDD_HHMMSS.log;
|
||||
//同一秒内文件已存在(Destroy→GetB 重连、同目录多进程启动)时追加 _1~_999 后缀,不复用同一文件
|
||||
bool MakeLogPath(std::string *path)
|
||||
{
|
||||
if (path == 0)
|
||||
char stamp[32] = {0};
|
||||
if (!TimeStamp(stamp, sizeof(stamp)))
|
||||
{
|
||||
return "";
|
||||
return false;
|
||||
}
|
||||
const char *base = path;
|
||||
for (const char *p = path; *p != '\0'; ++p)
|
||||
const std::string base = std::string(RDP_LOG_DIR) + "rdp_" + stamp;
|
||||
for (int suffix = 0; suffix < 1000; ++suffix)
|
||||
{
|
||||
if (*p == '\\' || *p == '/')
|
||||
std::string candidate = base;
|
||||
if (suffix > 0)
|
||||
{
|
||||
base = p + 1;
|
||||
candidate += "_" + std::to_string(suffix);
|
||||
}
|
||||
candidate += ".log";
|
||||
if (_access(candidate.c_str(), 0) != 0)
|
||||
{
|
||||
*path = candidate;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return base;
|
||||
return false;
|
||||
}
|
||||
|
||||
//初始化 logger:统一日志格式,运行期级别放开到 trace(实际输出级别由编译期
|
||||
//SPDLOG_ACTIVE_LEVEL 过滤),所有级别逐条 flush(日志量小,且调试场景要求异常退出也尽量不丢日志)
|
||||
void ConfigureLogger(const std::shared_ptr<spdlog::logger> &logger)
|
||||
{
|
||||
logger->set_pattern(kPattern);
|
||||
logger->set_level(spdlog::level::trace);
|
||||
logger->flush_on(spdlog::level::trace);
|
||||
spdlog::set_default_logger(logger);
|
||||
}
|
||||
|
||||
//仅标准输出 sink 的降级 logger(日志文件创建失败时使用)
|
||||
std::shared_ptr<spdlog::logger> MakeStdoutLogger()
|
||||
{
|
||||
std::vector<spdlog::sink_ptr> sinks;
|
||||
sinks.push_back(std::make_shared<spdlog::sinks::stdout_sink_mt>());
|
||||
std::shared_ptr<spdlog::logger> logger =
|
||||
std::make_shared<spdlog::logger>("rdp", sinks.begin(), sinks.end());
|
||||
ConfigureLogger(logger);
|
||||
return logger;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,82 +113,84 @@ namespace rdp_log
|
||||
bool Init()
|
||||
{
|
||||
RdpLogState &st = State();
|
||||
if (st.enabled)
|
||||
if (st.initialized)
|
||||
{
|
||||
return true; //已经重定向,避免重复初始化
|
||||
return true; //已经初始化,避免重复创建日志文件
|
||||
}
|
||||
|
||||
#ifdef RDP_LOG_ENABLE
|
||||
//创建日志目录(目录已存在时 _mkdir 返回 EEXIST,忽略)
|
||||
std::string log_dir = RDP_LOG_DIR;
|
||||
while (log_dir.size() > 0 &&
|
||||
(log_dir[log_dir.size() - 1] == '\\' || log_dir[log_dir.size() - 1] == '/'))
|
||||
{
|
||||
log_dir.erase(log_dir.size() - 1);
|
||||
}
|
||||
if (log_dir.size() > 0)
|
||||
{
|
||||
_mkdir(log_dir.c_str());
|
||||
}
|
||||
|
||||
//按当前日期+时间生成日志文件名:rdp_YYYYMMDD_HHMMSS.log
|
||||
char time_buf[32] = {0};
|
||||
time_t now = time(NULL);
|
||||
struct tm local;
|
||||
if (localtime_s(&local, &now) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
strftime(time_buf, sizeof(time_buf), "%Y%m%d_%H%M%S", &local);
|
||||
|
||||
std::string log_path = RDP_LOG_DIR;
|
||||
log_path += "rdp_";
|
||||
log_path += time_buf;
|
||||
log_path += ".log";
|
||||
|
||||
st.file.open(log_path.c_str(), std::ios::out | std::ios::trunc);
|
||||
if (!st.file.is_open())
|
||||
{
|
||||
//日志文件打开失败(目录不可写等):保持控制台输出,不做重定向
|
||||
std::cout << "[rdp_log] cannot open log file: " << log_path << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
//用 rdbuf() 将 std::cout 的流缓冲区替换为日志文件输出流
|
||||
st.original_buf = std::cout.rdbuf();
|
||||
std::cout.rdbuf(st.file.rdbuf());
|
||||
st.enabled = true;
|
||||
|
||||
//重定向成功后写入起始标记(该行已写入日志文件)
|
||||
std::cout << prefix(__FILE__, __LINE__)
|
||||
<< "rdp_log enabled, log file: " << log_path << std::endl;
|
||||
return true;
|
||||
#else
|
||||
//日志开关关闭:不重定向,日志仍输出到标准输出
|
||||
#if SPDLOG_ACTIVE_LEVEL > SPDLOG_LEVEL_CRITICAL
|
||||
//编译期关闭日志(RDP_LOG_LEVEL=OFF):不初始化 logger、不创建日志文件
|
||||
return false;
|
||||
#else
|
||||
try
|
||||
{
|
||||
EnsureDir(RDP_LOG_DIR);
|
||||
|
||||
std::string log_path;
|
||||
if (!MakeLogPath(&log_path))
|
||||
{
|
||||
throw spdlog::spdlog_ex("no usable log file name in RDP_LOG_DIR");
|
||||
}
|
||||
|
||||
std::vector<spdlog::sink_ptr> sinks;
|
||||
sinks.push_back(std::make_shared<spdlog::sinks::stdout_sink_mt>());
|
||||
//truncate=false:日志文件名已保证不存在(极端并发下万一被抢占则追加而非覆盖已有内容)
|
||||
sinks.push_back(std::make_shared<spdlog::sinks::basic_file_sink_mt>(log_path, false));
|
||||
|
||||
std::shared_ptr<spdlog::logger> logger =
|
||||
std::make_shared<spdlog::logger>("rdp", sinks.begin(), sinks.end());
|
||||
ConfigureLogger(logger);
|
||||
|
||||
//spdlog 内部错误(如日志文件写失败)打到标准错误,不影响雷达数据处理
|
||||
spdlog::set_error_handler([](const std::string &msg) {
|
||||
std::fprintf(stderr, "[rdp_log] spdlog error: %s\n", msg.c_str());
|
||||
});
|
||||
|
||||
st.logger = logger;
|
||||
st.log_path = log_path;
|
||||
st.initialized = true;
|
||||
|
||||
RDP_LOG_INFO("rdp_log enabled, log file: {}", log_path);
|
||||
return true;
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
//日志文件创建失败(目录不可写等):降级为仅标准输出,不中断雷达数据处理
|
||||
try
|
||||
{
|
||||
st.logger = MakeStdoutLogger();
|
||||
st.log_path.clear();
|
||||
st.initialized = true;
|
||||
std::fprintf(stderr, "[rdp_log] cannot create log file, fallback to stdout only: %s\n",
|
||||
e.what());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
std::fprintf(stderr, "[rdp_log] logger init failed\n");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Shutdown()
|
||||
{
|
||||
RdpLogState &st = State();
|
||||
if (!st.enabled)
|
||||
if (!st.initialized || !st.logger)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//写入结束标记后刷新、恢复 std::cout 并关闭日志文件
|
||||
std::cout << prefix(__FILE__, __LINE__) << "rdp_log shutdown" << std::endl;
|
||||
st.file.flush();
|
||||
std::cout.rdbuf(st.original_buf);
|
||||
st.enabled = false;
|
||||
st.file.close();
|
||||
RDP_LOG_INFO("rdp_log shutdown");
|
||||
//flush 并释放默认 logger(随之关闭日志文件)
|
||||
spdlog::shutdown();
|
||||
st.logger.reset();
|
||||
st.log_path.clear();
|
||||
st.initialized = false;
|
||||
}
|
||||
|
||||
std::string prefix(const char *file, int line)
|
||||
std::string log_file_path()
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "[" << CurrentTime() << "][" << FileName(file) << ":" << line << "] ";
|
||||
return oss.str();
|
||||
return State().log_path;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,26 +2,46 @@
|
||||
#define RDP_LOG_H
|
||||
#pragma once
|
||||
/*************************************日志输出模块*******************************************/
|
||||
// 功能:
|
||||
// 1) rdp_log::Init():按 parameters.h 中 RDP_LOG_ENABLE 宏开关决定是否将 std::cout
|
||||
// 重定向到日志文件(RDP_LOG_DIR 目录,文件名按当前日期+时间自动生成)。
|
||||
// 开关关闭(注释掉 RDP_LOG_ENABLE)时不做重定向,日志仍输出到标准输出。
|
||||
// 2) rdp_log::Shutdown():恢复 std::cout 原始缓冲区(回到控制台)并关闭日志文件。
|
||||
// 3) RDP_LOG 宏:日志输出宏,在每条日志前自动附加 [时间戳][代码文件:行号] 信息,
|
||||
// 随后输出到 std::cout(若开关开启则随 cout 一并写入日志文件)。
|
||||
// 用法示例:
|
||||
// RDP_LOG << "目标批号:" << track_index << std::endl;
|
||||
#include <iostream>
|
||||
// 基于 spdlog v1.17.0(third_party/spdlog,来源/版本见该目录 UPSTREAM.md,以静态库目标
|
||||
// spdlog 链接,见 CMakeLists.txt):
|
||||
// 1) rdp_log::Init():创建 RDP_LOG_DIR 下按「日期+时间」命名的日志文件(每次初始化一个
|
||||
// 文件;同一秒内文件已存在时追加 _1~_999 后缀),默认 logger 同时输出到 标准输出 与
|
||||
// 日志文件 两个 sink。由 Data_Process_Factory::GetB() 调用;
|
||||
// 2) rdp_log::Shutdown():打印结束标记、flush 并释放 logger(关闭日志文件),由
|
||||
// Data_Process_Factory::Destroy() 调用;
|
||||
// 3) 日志宏 RDP_LOG_TRACE / RDP_LOG_DEBUG / RDP_LOG_INFO / RDP_LOG_WARN /
|
||||
// RDP_LOG_ERROR / RDP_LOG_CRITICAL:级别与 spdlog 一致(RDP_LOG_ERR 为
|
||||
// RDP_LOG_ERROR 的别名),支持 fmt 风格的格式化,用法示例:
|
||||
// RDP_LOG_INFO("目标批号:{},时间:{}", track_index, cpi_time);
|
||||
// 低于 SPDLOG_ACTIVE_LEVEL 的日志调用在编译期被剔除(实参不求值),该级别由 CMake
|
||||
// 选项 RDP_LOG_LEVEL 控制(见 CMakeLists.txt 与 README 10.4),即原 RDP_LOG_ENABLE
|
||||
// 开关改由 SPDLOG_ACTIVE_LEVEL 在编译期控制日志级别(OFF = 编译期关闭全部日志)。
|
||||
//
|
||||
// 日志文件路径/名称与旧实现保持一致:RDP_LOG_DIR(.\rdp_logs\)+ rdp_YYYYMMDD_HHMMSS.log。
|
||||
// 日志行格式:"[YYYY-MM-DD HH:MM:SS.mmm] [级别] [文件名:行号] 内容",级别为 spdlog 默认
|
||||
// 名称 trace/debug/info/warning/error/critical。
|
||||
#include <string>
|
||||
|
||||
// 第三方头文件告警隔离:spdlog 按 third_party 依赖处理(见 README 4.5)
|
||||
#pragma warning(push, 0)
|
||||
#include <spdlog/spdlog.h>
|
||||
#pragma warning(pop)
|
||||
|
||||
namespace rdp_log
|
||||
{
|
||||
bool Init(); //初始化日志输出(由 Data_Process_Factory::GetB() 调用)
|
||||
void Shutdown(); //恢复 std::cout 并关闭日志文件(由 Data_Process_Factory::Destroy() 调用)
|
||||
std::string prefix(const char *file, int line); //生成 "[时间戳][文件名:行号] " 前缀字符串
|
||||
bool Init(); //初始化日志输出(标准输出 + 日志文件;由 GetB() 调用)
|
||||
void Shutdown(); //flush、释放 logger 并关闭日志文件(由 Destroy() 调用)
|
||||
std::string log_file_path(); //当前日志文件全路径(未初始化或文件创建失败时为空串)
|
||||
}
|
||||
|
||||
//日志输出宏:自动附加 时间戳、代码文件、代码行号 后输出到标准输出(std::cout)
|
||||
#define RDP_LOG std::cout << rdp_log::prefix(__FILE__, __LINE__)
|
||||
//日志级别宏(spdlog 编译期级别过滤:低于 SPDLOG_ACTIVE_LEVEL 的调用被剔除、实参不求值)
|
||||
#define RDP_LOG_TRACE(...) SPDLOG_TRACE(__VA_ARGS__)
|
||||
#define RDP_LOG_DEBUG(...) SPDLOG_DEBUG(__VA_ARGS__)
|
||||
#define RDP_LOG_INFO(...) SPDLOG_INFO(__VA_ARGS__)
|
||||
#define RDP_LOG_WARN(...) SPDLOG_WARN(__VA_ARGS__)
|
||||
#define RDP_LOG_ERROR(...) SPDLOG_ERROR(__VA_ARGS__)
|
||||
#define RDP_LOG_CRITICAL(...) SPDLOG_CRITICAL(__VA_ARGS__)
|
||||
//兼容别名:需求中的级别名 err 对应 spdlog::level::err,宏名同时提供 RDP_LOG_ERR
|
||||
#define RDP_LOG_ERR(...) RDP_LOG_ERROR(__VA_ARGS__)
|
||||
|
||||
#endif // RDP_LOG_H
|
||||
|
||||
@@ -0,0 +1,617 @@
|
||||
#include "rdp_parameter_loader.h"
|
||||
#include "parameters.h"
|
||||
#include <cfloat>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
// toml++ 为纯头文件第三方库(third_party/toml/toml.hpp,v3.4.0)。
|
||||
// 第三方头文件不参与本工程的告警治理:包含期间临时屏蔽告警(/W3 下仍要求 0 告警)。
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(push, 0)
|
||||
#endif
|
||||
#include <toml.hpp>
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
/************************************************************************************************
|
||||
* 实现说明
|
||||
*
|
||||
* 配置文件结构与 RDP_parameter 一一对应:
|
||||
* [radar_params] 雷达工作参数
|
||||
* [cohesion] 点迹凝聚参数
|
||||
* [track_associate] 航迹关联参数
|
||||
* [track_init] 航迹起始参数
|
||||
* [track_die] 航迹消亡参数
|
||||
* [track_quality] 航迹质量管理参数
|
||||
* [track_index] 航迹批号管理
|
||||
* [beam_control] 波束调度参数
|
||||
* [singer_model] singer 模型机动参数
|
||||
* [track_init_prohibite_area] 航迹起始屏蔽区(数组)
|
||||
* [tas_prohibite_area] 禁止 TAS 跟踪区(数组)
|
||||
*
|
||||
* 标量参数用「字段描述表」集中登记(段名 + 键名 + 成员指针),避免逐项重复代码;
|
||||
* 屏蔽区数组因为「长度由 *_num 决定」单独处理。加载流程:
|
||||
* 解析 TOML -> 校验段/键集合 -> 读取标量与数组到临时对象 -> 取值校验 -> 一次性赋值。
|
||||
* 任一步失败都不会修改调用方传入的 parameter。
|
||||
************************************************************************************************/
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
// 屏蔽区数组容量,必须与 struct.h::RDP_parameter 中的数组长度一致。
|
||||
constexpr int kProhibiteAreaCapacity = 30;
|
||||
|
||||
// 带上下文的错误收集:只记录第一条错误,统一返回 false 便于链式返回。
|
||||
class LoadContext
|
||||
{
|
||||
public:
|
||||
explicit LoadContext(std::string* error_msg) : error_msg_(error_msg) {}
|
||||
|
||||
bool fail(const std::string& message)
|
||||
{
|
||||
if (!failed_)
|
||||
{
|
||||
failed_ = true;
|
||||
if (error_msg_ != nullptr)
|
||||
*error_msg_ = message;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool failed() const { return failed_; }
|
||||
|
||||
private:
|
||||
std::string* error_msg_;
|
||||
bool failed_ = false;
|
||||
};
|
||||
|
||||
// 标量字段描述:TOML 段名、键名、RDP_parameter 成员指针。
|
||||
struct FloatField
|
||||
{
|
||||
const char* section;
|
||||
const char* key;
|
||||
float RDP_parameter::*member;
|
||||
};
|
||||
|
||||
struct IntField
|
||||
{
|
||||
const char* section;
|
||||
const char* key;
|
||||
int RDP_parameter::*member;
|
||||
};
|
||||
|
||||
// ------------------------------ 标量字段登记表 ------------------------------
|
||||
const FloatField kFloatFields[] = {
|
||||
// [radar_params] 雷达工作参数
|
||||
{"radar_params", "freq_0_GHz", &RDP_parameter::freq_0_GHz},
|
||||
{"radar_params", "radar_height", &RDP_parameter::radar_height},
|
||||
{"radar_params", "radar_system_delay", &RDP_parameter::radar_system_delay},
|
||||
{"radar_params", "data_rate_tws", &RDP_parameter::data_rate_tws},
|
||||
{"radar_params", "data_rate_tas", &RDP_parameter::data_rate_tas},
|
||||
{"radar_params", "data_rate_track_only", &RDP_parameter::data_rate_track_only},
|
||||
// [cohesion] 点迹凝聚参数
|
||||
{"cohesion", "point_cohesion_range", &RDP_parameter::point_cohesion_range},
|
||||
{"cohesion", "point_cohesion_velocity", &RDP_parameter::point_cohesion_velocity},
|
||||
{"cohesion", "point_cohesion_azimuth", &RDP_parameter::point_cohesion_azimuth},
|
||||
{"cohesion", "point_cohesion_range_min", &RDP_parameter::point_cohesion_range_min},
|
||||
{"cohesion", "point_cohesion_range_max", &RDP_parameter::point_cohesion_range_max},
|
||||
// [track_associate] 航迹关联参数
|
||||
{"track_associate", "sigma_range", &RDP_parameter::sigma_range},
|
||||
{"track_associate", "sigma_azimuth", &RDP_parameter::sigma_azimuth},
|
||||
{"track_associate", "sigma_pitch", &RDP_parameter::sigma_pitch},
|
||||
{"track_associate", "sigma_velocity", &RDP_parameter::sigma_velocity},
|
||||
{"track_associate", "associate_threshold", &RDP_parameter::associate_threshold},
|
||||
// [track_init] 航迹起始参数
|
||||
{"track_init", "track_init_threshold", &RDP_parameter::track_init_threshold},
|
||||
{"track_init", "track_init_angle", &RDP_parameter::track_init_angle},
|
||||
{"track_init", "track_init_quality_L3", &RDP_parameter::track_init_quality_L3},
|
||||
{"track_init", "track_init_quality_L4", &RDP_parameter::track_init_quality_L4},
|
||||
{"track_init", "track_init_quality_L5", &RDP_parameter::track_init_quality_L5},
|
||||
{"track_init", "track_init_quality_L6", &RDP_parameter::track_init_quality_L6},
|
||||
{"track_init", "track_init_quality_L7", &RDP_parameter::track_init_quality_L7},
|
||||
{"track_init", "track_init_velocity_min", &RDP_parameter::track_init_velocity_min},
|
||||
{"track_init", "track_init_velocity_max", &RDP_parameter::track_init_velocity_max},
|
||||
// [track_die] 航迹消亡参数
|
||||
{"track_die", "track_die_quality", &RDP_parameter::track_die_quality},
|
||||
{"track_die", "track_die_quality_pramary", &RDP_parameter::track_die_quality_pramary},
|
||||
// [track_quality] 航迹质量管理参数
|
||||
{"track_quality", "track_quality_max", &RDP_parameter::track_quality_max},
|
||||
{"track_quality", "track_quality_min", &RDP_parameter::track_quality_min},
|
||||
{"track_quality", "track_quality_d_weight", &RDP_parameter::track_quality_d_weight},
|
||||
{"track_quality", "track_quality_snr_weight", &RDP_parameter::track_quality_snr_weight},
|
||||
{"track_quality", "track_quality_len_weight", &RDP_parameter::track_quality_len_weight},
|
||||
{"track_quality", "track_quality_iter_coeff", &RDP_parameter::track_quality_iter_coeff},
|
||||
{"track_quality", "track_quality_decrease_coeff_o2", &RDP_parameter::track_quality_decrease_coeff_o2},
|
||||
{"track_quality", "track_quality_decrease_coeff_o1", &RDP_parameter::track_quality_decrease_coeff_o1},
|
||||
// [singer_model] singer 模型机动参数
|
||||
{"singer_model", "model_1_Q_fast", &RDP_parameter::model_1_Q_fast},
|
||||
{"singer_model", "model_1_Q_slow", &RDP_parameter::model_1_Q_slow},
|
||||
{"singer_model", "model_2_Q_fast", &RDP_parameter::model_2_Q_fast},
|
||||
{"singer_model", "model_2_Q_slow", &RDP_parameter::model_2_Q_slow},
|
||||
{"singer_model", "model_3_Q_fast", &RDP_parameter::model_3_Q_fast},
|
||||
{"singer_model", "model_3_Q_slow", &RDP_parameter::model_3_Q_slow},
|
||||
};
|
||||
|
||||
const IntField kIntFields[] = {
|
||||
// [radar_params] 雷达工作参数
|
||||
{"radar_params", "radar_work_mode", &RDP_parameter::radar_work_mode},
|
||||
{"radar_params", "scan_mode", &RDP_parameter::scan_mode},
|
||||
// [track_associate] 航迹关联参数
|
||||
{"track_associate", "extrapolate_max", &RDP_parameter::extrapolate_max},
|
||||
{"track_associate", "extrapolate_max_tas", &RDP_parameter::extrapolate_max_tas},
|
||||
{"track_associate", "height_smooth_win", &RDP_parameter::height_smooth_win},
|
||||
// [track_init] 航迹起始参数
|
||||
{"track_init", "track_init_point_num_min", &RDP_parameter::track_init_point_num_min},
|
||||
// [track_die] 航迹消亡参数
|
||||
{"track_die", "track_die_round", &RDP_parameter::track_die_round},
|
||||
{"track_die", "track_die_round_pramary", &RDP_parameter::track_die_round_pramary},
|
||||
{"track_die", "track_die_round_tas", &RDP_parameter::track_die_round_tas},
|
||||
// [track_index] 航迹批号管理
|
||||
{"track_index", "track_index_max", &RDP_parameter::track_index_max},
|
||||
// [beam_control] 波束调度参数
|
||||
{"beam_control", "default_track_beam_type", &RDP_parameter::default_track_beam_type},
|
||||
{"beam_control", "tas_track_num_max", &RDP_parameter::tas_track_num_max},
|
||||
// 屏蔽区数目(对应数组长度由它决定)
|
||||
{"track_init_prohibite_area", "track_init_prohibite_area_num", &RDP_parameter::track_init_prohibite_area_num},
|
||||
{"tas_prohibite_area", "tas_prohibite_area_num", &RDP_parameter::tas_prohibite_area_num},
|
||||
};
|
||||
|
||||
// 屏蔽区数组描述:段名 + 键名 + 成员指针(数组长度为 kProhibiteAreaCapacity)。
|
||||
struct AreaArrayField
|
||||
{
|
||||
const char* section;
|
||||
const char* key;
|
||||
float (RDP_parameter::*member)[kProhibiteAreaCapacity];
|
||||
};
|
||||
|
||||
const AreaArrayField kAreaArrayFields[] = {
|
||||
{"track_init_prohibite_area", "track_init_prohibite_area_range_min", &RDP_parameter::track_init_prohibite_area_range_min},
|
||||
{"track_init_prohibite_area", "track_init_prohibite_area_range_max", &RDP_parameter::track_init_prohibite_area_range_max},
|
||||
{"track_init_prohibite_area", "track_init_prohibite_area_azimuth_min", &RDP_parameter::track_init_prohibite_area_azimuth_min},
|
||||
{"track_init_prohibite_area", "track_init_prohibite_area_azimuth_max", &RDP_parameter::track_init_prohibite_area_azimuth_max},
|
||||
{"tas_prohibite_area", "tas_prohibite_area_range_min", &RDP_parameter::tas_prohibite_area_range_min},
|
||||
{"tas_prohibite_area", "tas_prohibite_area_range_max", &RDP_parameter::tas_prohibite_area_range_max},
|
||||
{"tas_prohibite_area", "tas_prohibite_area_azimuth_min", &RDP_parameter::tas_prohibite_area_azimuth_min},
|
||||
{"tas_prohibite_area", "tas_prohibite_area_azimuth_max", &RDP_parameter::tas_prohibite_area_azimuth_max},
|
||||
};
|
||||
|
||||
const char* const kAllSections[] = {
|
||||
"radar_params",
|
||||
"cohesion",
|
||||
"track_associate",
|
||||
"track_init",
|
||||
"track_die",
|
||||
"track_quality",
|
||||
"track_index",
|
||||
"beam_control",
|
||||
"singer_model",
|
||||
"track_init_prohibite_area",
|
||||
"tas_prohibite_area",
|
||||
};
|
||||
|
||||
// ------------------------------ 基础读取工具 ------------------------------
|
||||
bool node_to_double(const toml::node& node, double& value)
|
||||
{
|
||||
if (node.is_integer())
|
||||
{
|
||||
value = static_cast<double>(*node.value<std::int64_t>());
|
||||
return true;
|
||||
}
|
||||
if (node.is_floating_point())
|
||||
{
|
||||
value = *node.value<double>();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool read_float(const toml::table& section, const char* section_name, const char* key, float& out, LoadContext& ctx)
|
||||
{
|
||||
const toml::node* node = section.get(key);
|
||||
if (node == nullptr)
|
||||
return ctx.fail("缺少配置项 [" + std::string(section_name) + "] " + key);
|
||||
|
||||
double value = 0.0;
|
||||
if (!node_to_double(*node, value))
|
||||
return ctx.fail("配置项 [" + std::string(section_name) + "] " + key + " 必须是数字");
|
||||
|
||||
if (!std::isfinite(value) || std::fabs(value) > static_cast<double>(FLT_MAX))
|
||||
return ctx.fail("配置项 [" + std::string(section_name) + "] " + key + " 超出 float 可表示范围");
|
||||
|
||||
out = static_cast<float>(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool read_int(const toml::table& section, const char* section_name, const char* key, int& out, LoadContext& ctx)
|
||||
{
|
||||
const toml::node* node = section.get(key);
|
||||
if (node == nullptr)
|
||||
return ctx.fail("缺少配置项 [" + std::string(section_name) + "] " + key);
|
||||
|
||||
if (!node->is_integer())
|
||||
return ctx.fail("配置项 [" + std::string(section_name) + "] " + key + " 必须是整数");
|
||||
|
||||
const std::int64_t value = *node->value<std::int64_t>();
|
||||
if (value < std::numeric_limits<int>::min() || value > std::numeric_limits<int>::max())
|
||||
return ctx.fail("配置项 [" + std::string(section_name) + "] " + key + " 超出 int 可表示范围");
|
||||
|
||||
out = static_cast<int>(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
// 取配置段;缺失或不是表都算错误(严格模式:11 个配置段全部必填)。
|
||||
const toml::table* get_section(const toml::table& root, const char* section_name, LoadContext& ctx)
|
||||
{
|
||||
const toml::node* node = root.get(section_name);
|
||||
if (node == nullptr)
|
||||
{
|
||||
ctx.fail("缺少配置段 [" + std::string(section_name) + "]");
|
||||
return nullptr;
|
||||
}
|
||||
const toml::table* table = node->as_table();
|
||||
if (table == nullptr)
|
||||
{
|
||||
ctx.fail("配置项 [" + std::string(section_name) + "] 必须是 TOML 表");
|
||||
return nullptr;
|
||||
}
|
||||
return table;
|
||||
}
|
||||
|
||||
// 收集某个段允许出现的全部键(标量 + 数组)。
|
||||
std::vector<std::string_view> allowed_keys_of(const std::string_view section)
|
||||
{
|
||||
std::vector<std::string_view> keys;
|
||||
for (const FloatField& field : kFloatFields)
|
||||
{
|
||||
if (section == field.section)
|
||||
keys.emplace_back(field.key);
|
||||
}
|
||||
for (const IntField& field : kIntFields)
|
||||
{
|
||||
if (section == field.section)
|
||||
keys.emplace_back(field.key);
|
||||
}
|
||||
for (const AreaArrayField& field : kAreaArrayFields)
|
||||
{
|
||||
if (section == field.section)
|
||||
keys.emplace_back(field.key);
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
// 严格校验某个段中只出现允许的键(拼错的键按未知参数拒绝)。
|
||||
bool check_keys_of_section(const toml::table& section, const char* section_name, LoadContext& ctx)
|
||||
{
|
||||
const std::vector<std::string_view> allowed = allowed_keys_of(section_name);
|
||||
for (const auto& [key, value] : section)
|
||||
{
|
||||
(void)value;
|
||||
bool known = false;
|
||||
for (const std::string_view item : allowed)
|
||||
{
|
||||
if (key.str() == item)
|
||||
{
|
||||
known = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!known)
|
||||
return ctx.fail("配置段 [" + std::string(section_name) + "] 中存在未知配置项: " + std::string(key.str()));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// 读取一个屏蔽区段的 4 个数组。
|
||||
// 约定:*_num > 0 时数组必须存在且元素个数等于 *_num;*_num == 0 时数组可省略,
|
||||
// 存在时必须是空数组。数组元素写入 RDP_parameter 的定长数组(未使用位置保持 0)。
|
||||
bool read_area_arrays(const toml::table& section, const char* section_name, int area_num,
|
||||
RDP_parameter& parameter, LoadContext& ctx)
|
||||
{
|
||||
for (const AreaArrayField& field : kAreaArrayFields)
|
||||
{
|
||||
if (std::string_view(field.section) != section_name)
|
||||
continue;
|
||||
|
||||
const toml::node* node = section.get(field.key);
|
||||
if (node == nullptr)
|
||||
{
|
||||
if (area_num == 0)
|
||||
continue;
|
||||
return ctx.fail("缺少配置项 [" + std::string(section_name) + "] " + field.key);
|
||||
}
|
||||
|
||||
const toml::array* array = node->as_array();
|
||||
if (array == nullptr)
|
||||
return ctx.fail("配置项 [" + std::string(section_name) + "] " + field.key + " 必须是数组");
|
||||
|
||||
if (static_cast<int>(array->size()) != area_num)
|
||||
return ctx.fail("配置项 [" + std::string(section_name) + "] " + field.key + " 的元素个数(" +
|
||||
std::to_string(array->size()) + ")与 " + std::string(section_name) + "_num(" +
|
||||
std::to_string(area_num) + ")不一致");
|
||||
|
||||
float* destination = parameter.*(field.member);
|
||||
int index = 0;
|
||||
for (const toml::node& element : *array)
|
||||
{
|
||||
double value = 0.0;
|
||||
if (!node_to_double(element, value))
|
||||
return ctx.fail("配置项 [" + std::string(section_name) + "] " + field.key + " 的第 " +
|
||||
std::to_string(index + 1) + " 个元素必须是数字");
|
||||
if (!std::isfinite(value) || std::fabs(value) > static_cast<double>(FLT_MAX))
|
||||
return ctx.fail("配置项 [" + std::string(section_name) + "] " + field.key + " 的第 " +
|
||||
std::to_string(index + 1) + " 个元素超出 float 可表示范围");
|
||||
destination[index] = static_cast<float>(value);
|
||||
++index;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// ------------------------------ 取值校验 ------------------------------
|
||||
bool validate_parameter(const RDP_parameter& p, LoadContext& ctx)
|
||||
{
|
||||
// 雷达工作参数
|
||||
if (p.radar_work_mode < 0 || p.radar_work_mode > 2)
|
||||
return ctx.fail("参数 radar_work_mode 必须在 0~2 之间(实际 " + std::to_string(p.radar_work_mode) + ")");
|
||||
if (p.scan_mode != 0 && p.scan_mode != 1)
|
||||
return ctx.fail("参数 scan_mode 必须为 0(相扫模式)或 1(机扫模式)(实际 " + std::to_string(p.scan_mode) + ")");
|
||||
if (!(p.freq_0_GHz > 0.0f))
|
||||
return ctx.fail("参数 freq_0_GHz 必须大于 0");
|
||||
if (p.radar_system_delay < 0.0f)
|
||||
return ctx.fail("参数 radar_system_delay 不能为负");
|
||||
if (!(p.data_rate_tws > 0.0f))
|
||||
return ctx.fail("参数 data_rate_tws 必须大于 0");
|
||||
if (!(p.data_rate_tas > 0.0f))
|
||||
return ctx.fail("参数 data_rate_tas 必须大于 0");
|
||||
if (p.data_rate_track_only < 0.0f)
|
||||
return ctx.fail("参数 data_rate_track_only 不能为负");
|
||||
|
||||
// 点迹凝聚参数
|
||||
if (!(p.point_cohesion_range > 0.0f))
|
||||
return ctx.fail("参数 point_cohesion_range 必须大于 0");
|
||||
if (!(p.point_cohesion_velocity > 0.0f))
|
||||
return ctx.fail("参数 point_cohesion_velocity 必须大于 0");
|
||||
if (!(p.point_cohesion_azimuth > 0.0f))
|
||||
return ctx.fail("参数 point_cohesion_azimuth 必须大于 0");
|
||||
if (p.point_cohesion_range_min < 0.0f)
|
||||
return ctx.fail("参数 point_cohesion_range_min 不能为负");
|
||||
if (!(p.point_cohesion_range_max > p.point_cohesion_range_min))
|
||||
return ctx.fail("参数 point_cohesion_range_max 必须大于 point_cohesion_range_min");
|
||||
|
||||
// 航迹关联参数
|
||||
if (!(p.sigma_range > 0.0f))
|
||||
return ctx.fail("参数 sigma_range 必须大于 0");
|
||||
if (!(p.sigma_azimuth > 0.0f))
|
||||
return ctx.fail("参数 sigma_azimuth 必须大于 0");
|
||||
if (!(p.sigma_pitch > 0.0f))
|
||||
return ctx.fail("参数 sigma_pitch 必须大于 0");
|
||||
if (!(p.sigma_velocity > 0.0f))
|
||||
return ctx.fail("参数 sigma_velocity 必须大于 0");
|
||||
if (!(p.associate_threshold > 0.0f))
|
||||
return ctx.fail("参数 associate_threshold 必须大于 0");
|
||||
if (p.extrapolate_max < 0)
|
||||
return ctx.fail("参数 extrapolate_max 不能为负");
|
||||
if (p.extrapolate_max_tas < 0)
|
||||
return ctx.fail("参数 extrapolate_max_tas 不能为负");
|
||||
if (p.height_smooth_win < 1)
|
||||
return ctx.fail("参数 height_smooth_win 必须大于等于 1");
|
||||
|
||||
// 航迹起始参数
|
||||
if (!(p.track_init_threshold > 0.0f))
|
||||
return ctx.fail("参数 track_init_threshold 必须大于 0");
|
||||
if (!(p.track_init_angle > 0.0f) || p.track_init_angle > 180.0f)
|
||||
return ctx.fail("参数 track_init_angle 必须在 (0, 180] 度范围内");
|
||||
if (p.track_init_point_num_min < 3 || p.track_init_point_num_min > 9)
|
||||
return ctx.fail("参数 track_init_point_num_min 必须在 3~9 之间(实际 " +
|
||||
std::to_string(p.track_init_point_num_min) + ")");
|
||||
const float init_quality[] = {
|
||||
p.track_init_quality_L3, p.track_init_quality_L4, p.track_init_quality_L5,
|
||||
p.track_init_quality_L6, p.track_init_quality_L7};
|
||||
for (int i = 0; i < 5; ++i)
|
||||
{
|
||||
if (init_quality[i] < 0.0f || init_quality[i] > 1.0f)
|
||||
return ctx.fail("参数 track_init_quality_L" + std::to_string(i + 3) + " 必须在 0~1 之间");
|
||||
}
|
||||
if (p.track_init_velocity_min < 0.0f)
|
||||
return ctx.fail("参数 track_init_velocity_min 不能为负");
|
||||
if (!(p.track_init_velocity_max > p.track_init_velocity_min))
|
||||
return ctx.fail("参数 track_init_velocity_max 必须大于 track_init_velocity_min");
|
||||
|
||||
// 航迹消亡参数
|
||||
if (p.track_die_round < 0)
|
||||
return ctx.fail("参数 track_die_round 不能为负");
|
||||
if (p.track_die_round_pramary < 0)
|
||||
return ctx.fail("参数 track_die_round_pramary 不能为负");
|
||||
if (p.track_die_round_tas < 0)
|
||||
return ctx.fail("参数 track_die_round_tas 不能为负");
|
||||
if (p.track_die_quality < 0.0f || p.track_die_quality > 1.0f)
|
||||
return ctx.fail("参数 track_die_quality 必须在 0~1 之间");
|
||||
if (p.track_die_quality_pramary < 0.0f || p.track_die_quality_pramary > 1.0f)
|
||||
return ctx.fail("参数 track_die_quality_pramary 必须在 0~1 之间");
|
||||
|
||||
// 航迹质量管理参数
|
||||
if (p.track_quality_min < 0.0f)
|
||||
return ctx.fail("参数 track_quality_min 不能为负");
|
||||
if (!(p.track_quality_max > p.track_quality_min))
|
||||
return ctx.fail("参数 track_quality_max 必须大于 track_quality_min");
|
||||
if (p.track_quality_d_weight < 0.0f || p.track_quality_snr_weight < 0.0f || p.track_quality_len_weight < 0.0f)
|
||||
return ctx.fail("参数 track_quality_d_weight / track_quality_snr_weight / track_quality_len_weight 不能为负");
|
||||
if (p.track_quality_iter_coeff < 0.0f || p.track_quality_iter_coeff > 1.0f)
|
||||
return ctx.fail("参数 track_quality_iter_coeff 必须在 0~1 之间");
|
||||
if (p.track_quality_decrease_coeff_o2 > 0.0f || p.track_quality_decrease_coeff_o1 > 0.0f)
|
||||
return ctx.fail("参数 track_quality_decrease_coeff_o2 / track_quality_decrease_coeff_o1 必须小于等于 0");
|
||||
|
||||
// 航迹批号管理
|
||||
if (p.track_index_max < 1 || p.track_index_max > MAX_TRACK_NUM)
|
||||
return ctx.fail("参数 track_index_max 必须在 1~" + std::to_string(MAX_TRACK_NUM) + " 之间(实际 " +
|
||||
std::to_string(p.track_index_max) + ")");
|
||||
|
||||
// 波束调度参数
|
||||
if (p.default_track_beam_type < 0)
|
||||
return ctx.fail("参数 default_track_beam_type 不能为负");
|
||||
if (p.tas_track_num_max < 0 || p.tas_track_num_max > RDP_TAS_QUEUE_CAPACITY)
|
||||
return ctx.fail("参数 tas_track_num_max 必须在 0~" + std::to_string(RDP_TAS_QUEUE_CAPACITY) +
|
||||
" 之间(实际 " + std::to_string(p.tas_track_num_max) + ")");
|
||||
|
||||
// singer 模型机动参数
|
||||
const float model_q[] = {
|
||||
p.model_1_Q_fast, p.model_1_Q_slow, p.model_2_Q_fast,
|
||||
p.model_2_Q_slow, p.model_3_Q_fast, p.model_3_Q_slow};
|
||||
const char* const model_q_names[] = {
|
||||
"model_1_Q_fast", "model_1_Q_slow", "model_2_Q_fast",
|
||||
"model_2_Q_slow", "model_3_Q_fast", "model_3_Q_slow"};
|
||||
for (int i = 0; i < 6; ++i)
|
||||
{
|
||||
if (!(model_q[i] > 0.0f))
|
||||
return ctx.fail(std::string("参数 ") + model_q_names[i] + " 必须大于 0");
|
||||
}
|
||||
|
||||
// 屏蔽区
|
||||
if (p.track_init_prohibite_area_num < 0 || p.track_init_prohibite_area_num > kProhibiteAreaCapacity)
|
||||
return ctx.fail("参数 track_init_prohibite_area_num 必须在 0~" +
|
||||
std::to_string(kProhibiteAreaCapacity) + " 之间");
|
||||
if (p.tas_prohibite_area_num < 0 || p.tas_prohibite_area_num > kProhibiteAreaCapacity)
|
||||
return ctx.fail("参数 tas_prohibite_area_num 必须在 0~" + std::to_string(kProhibiteAreaCapacity) + " 之间");
|
||||
|
||||
for (int i = 0; i < p.track_init_prohibite_area_num; ++i)
|
||||
{
|
||||
const std::string prefix = "track_init_prohibite_area 第 " + std::to_string(i + 1) + " 区";
|
||||
if (p.track_init_prohibite_area_range_min[i] < 0.0f ||
|
||||
!(p.track_init_prohibite_area_range_max[i] > p.track_init_prohibite_area_range_min[i]))
|
||||
return ctx.fail(prefix + " 的距离范围非法(要求 max > min >= 0)");
|
||||
if (p.track_init_prohibite_area_azimuth_min[i] < 0.0f ||
|
||||
p.track_init_prohibite_area_azimuth_max[i] > 360.0f ||
|
||||
!(p.track_init_prohibite_area_azimuth_max[i] > p.track_init_prohibite_area_azimuth_min[i]))
|
||||
return ctx.fail(prefix + " 的方位范围非法(要求 0 =< min < max <= 360)");
|
||||
}
|
||||
for (int i = 0; i < p.tas_prohibite_area_num; ++i)
|
||||
{
|
||||
const std::string prefix = "tas_prohibite_area 第 " + std::to_string(i + 1) + " 区";
|
||||
if (p.tas_prohibite_area_range_min[i] < 0.0f ||
|
||||
!(p.tas_prohibite_area_range_max[i] > p.tas_prohibite_area_range_min[i]))
|
||||
return ctx.fail(prefix + " 的距离范围非法(要求 max > min >= 0)");
|
||||
if (p.tas_prohibite_area_azimuth_min[i] < 0.0f ||
|
||||
p.tas_prohibite_area_azimuth_max[i] > 360.0f ||
|
||||
!(p.tas_prohibite_area_azimuth_max[i] > p.tas_prohibite_area_azimuth_min[i]))
|
||||
return ctx.fail(prefix + " 的方位范围非法(要求 0 =< min < max <= 360)");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// ------------------------------ 加载主流程 ------------------------------
|
||||
bool load_all_sections(const toml::table& root, RDP_parameter& parameter, LoadContext& ctx)
|
||||
{
|
||||
// 1) 顶层只允许已知配置段
|
||||
for (const auto& [key, value] : root)
|
||||
{
|
||||
(void)value;
|
||||
bool known = false;
|
||||
for (const char* name : kAllSections)
|
||||
{
|
||||
if (key.str() == name)
|
||||
{
|
||||
known = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!known)
|
||||
return ctx.fail("配置文件中存在未知配置段: [" + std::string(key.str()) + "]");
|
||||
}
|
||||
|
||||
// 2) 先确认 11 个配置段都存在且为表(缺段优先报错,便于定位)
|
||||
for (const char* name : kAllSections)
|
||||
{
|
||||
if (get_section(root, name, ctx) == nullptr)
|
||||
return false;
|
||||
}
|
||||
|
||||
// 3) 再校验每段内只允许登记过的键
|
||||
for (const char* name : kAllSections)
|
||||
{
|
||||
const toml::table* section = get_section(root, name, ctx);
|
||||
if (section == nullptr)
|
||||
return false;
|
||||
if (!check_keys_of_section(*section, name, ctx))
|
||||
return false;
|
||||
}
|
||||
|
||||
// 4) 读取全部标量
|
||||
for (const FloatField& field : kFloatFields)
|
||||
{
|
||||
const toml::table* section = get_section(root, field.section, ctx);
|
||||
if (section == nullptr)
|
||||
return false;
|
||||
if (!read_float(*section, field.section, field.key, parameter.*(field.member), ctx))
|
||||
return false;
|
||||
}
|
||||
for (const IntField& field : kIntFields)
|
||||
{
|
||||
const toml::table* section = get_section(root, field.section, ctx);
|
||||
if (section == nullptr)
|
||||
return false;
|
||||
if (!read_int(*section, field.section, field.key, parameter.*(field.member), ctx))
|
||||
return false;
|
||||
}
|
||||
|
||||
// 4) 读取屏蔽区数组(长度依赖已读入的 *_num)
|
||||
const toml::table* init_area = get_section(root, "track_init_prohibite_area", ctx);
|
||||
if (init_area == nullptr)
|
||||
return false;
|
||||
if (!read_area_arrays(*init_area, "track_init_prohibite_area", parameter.track_init_prohibite_area_num, parameter, ctx))
|
||||
return false;
|
||||
|
||||
const toml::table* tas_area = get_section(root, "tas_prohibite_area", ctx);
|
||||
if (tas_area == nullptr)
|
||||
return false;
|
||||
if (!read_area_arrays(*tas_area, "tas_prohibite_area", parameter.tas_prohibite_area_num, parameter, ctx))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int rdp_parameter_load(const char* parameter_file_path, RDP_parameter& parameter, std::string* error_msg)
|
||||
{
|
||||
LoadContext ctx(error_msg);
|
||||
|
||||
if (parameter_file_path == nullptr || parameter_file_path[0] == '\0')
|
||||
{
|
||||
ctx.fail("配置文件路径为空");
|
||||
return -1;
|
||||
}
|
||||
if (error_msg != nullptr)
|
||||
error_msg->clear();
|
||||
|
||||
toml::table root;
|
||||
try
|
||||
{
|
||||
root = toml::parse_file(parameter_file_path);
|
||||
}
|
||||
catch (const toml::parse_error& err)
|
||||
{
|
||||
ctx.fail(std::string("TOML 解析失败: ") + std::string(err.what()));
|
||||
return -1;
|
||||
}
|
||||
catch (const std::exception& err)
|
||||
{
|
||||
ctx.fail(std::string("读取配置文件失败: ") + err.what());
|
||||
return -1;
|
||||
}
|
||||
|
||||
RDP_parameter loaded{};
|
||||
if (!load_all_sections(root, loaded, ctx))
|
||||
return -1;
|
||||
if (!validate_parameter(loaded, ctx))
|
||||
return -1;
|
||||
|
||||
// 全部校验通过后才一次性写入,保证失败时调用方参数不变。
|
||||
parameter = loaded;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
#ifndef RDP_PARAMETER_LOADER_H
|
||||
#define RDP_PARAMETER_LOADER_H
|
||||
#pragma once
|
||||
#include "struct.h"
|
||||
#include <string>
|
||||
|
||||
/************************************************************************************************
|
||||
* 雷达数据处理参数加载器(内部模块,不属于宿主接口)
|
||||
*
|
||||
* 功能:把 TOML 配置文件加载到 struct.h::RDP_parameter。
|
||||
* 约定:
|
||||
* 1) 配置文件的每个配置段与 RDP_parameter 的注释分组对应,配置键名与结构体成员名完全一致;
|
||||
* 2) 屏蔽区(track_init_prohibite_area / tas_prohibite_area)的数组使用与成员同名的 TOML 数组,
|
||||
* 数组长度必须等于对应的 *_num,且 *_num 不超过 30;
|
||||
* 3) 所有标量键均必须存在且类型/取值合法(严格校验),任一错误返回 -1 且不修改 parameter;
|
||||
* 4) 数组键在 *_num == 0 时可以省略,存在时必须为空数组;*_num > 0 时必须存在且长度等于 *_num。
|
||||
*
|
||||
* 参数:
|
||||
* parameter_file_path - 配置文件路径(UTF-8,宿主进程当前工作目录可达)
|
||||
* parameter - 输出参数;仅在成功时被写入
|
||||
* error_msg - 可选输出;失败时写入可读的错误原因(UTF-8)
|
||||
* 返回:成功返回 0;失败返回 -1
|
||||
************************************************************************************************/
|
||||
int rdp_parameter_load(const char* parameter_file_path, RDP_parameter& parameter, std::string* error_msg);
|
||||
|
||||
#endif // RDP_PARAMETER_LOADER_H
|
||||
@@ -150,21 +150,21 @@ namespace
|
||||
//写入失败提示
|
||||
void ReportError(Record_State& st, const char* what, const std::string& detail)
|
||||
{
|
||||
RDP_LOG << "[rdp_record] " << what << " failed (" << detail << "): "
|
||||
<< st.db.get_last_error() << std::endl;
|
||||
RDP_LOG_ERROR("[rdp_record] {} failed ({}): {}", what, detail, st.db.get_last_error());
|
||||
}
|
||||
|
||||
void LogStats(Record_State& st, const char* tag)
|
||||
{
|
||||
RDP_LOG << "[rdp_record] stats(" << tag << "): enqueued_rows="
|
||||
<< st.counters.enqueued_rows.load()
|
||||
<< ", written_rows=" << st.counters.written_rows.load()
|
||||
<< ", dropped_rows=" << st.counters.dropped_rows.load()
|
||||
<< ", transactions=" << st.counters.transactions.load()
|
||||
<< ", write_errors=" << st.counters.write_errors.load()
|
||||
<< ", queue_peak_bytes=" << st.counters.queue_peak_bytes.load()
|
||||
<< ", queue_peak_rows=" << st.counters.queue_peak_rows.load()
|
||||
<< std::endl;
|
||||
RDP_LOG_INFO("[rdp_record] stats({}): enqueued_rows={}, written_rows={}, dropped_rows={}, "
|
||||
"transactions={}, write_errors={}, queue_peak_bytes={}, queue_peak_rows={}",
|
||||
tag,
|
||||
st.counters.enqueued_rows.load(),
|
||||
st.counters.written_rows.load(),
|
||||
st.counters.dropped_rows.load(),
|
||||
st.counters.transactions.load(),
|
||||
st.counters.write_errors.load(),
|
||||
st.counters.queue_peak_bytes.load(),
|
||||
st.counters.queue_peak_rows.load());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@@ -253,21 +253,21 @@ namespace
|
||||
//因此用 query_s<std::tuple<...>> 消费返回行。
|
||||
auto rows = st.db.query_s<std::tuple<std::string>>("PRAGMA journal_mode=WAL;");
|
||||
std::string mode = rows.empty() ? std::string("<none>") : std::get<0>(rows[0]);
|
||||
RDP_LOG << "[rdp_record] journal_mode=" << mode << std::endl;
|
||||
RDP_LOG_INFO("[rdp_record] journal_mode={}", mode);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
RDP_LOG << "[rdp_record] set journal_mode=WAL failed, fallback to default journal" << std::endl;
|
||||
RDP_LOG_WARN("[rdp_record] set journal_mode=WAL failed, fallback to default journal");
|
||||
}
|
||||
#endif
|
||||
if (!st.db.execute("PRAGMA synchronous=OFF;"))
|
||||
{
|
||||
RDP_LOG << "[rdp_record] set synchronous=OFF failed" << std::endl;
|
||||
RDP_LOG_WARN("[rdp_record] set synchronous=OFF failed");
|
||||
}
|
||||
const std::string cache = "PRAGMA cache_size=-" + std::to_string(RDP_RECORD_SQLITE_CACHE_KB) + ";";
|
||||
if (!st.db.execute(cache))
|
||||
{
|
||||
RDP_LOG << "[rdp_record] set cache_size failed" << std::endl;
|
||||
RDP_LOG_WARN("[rdp_record] set cache_size failed");
|
||||
}
|
||||
try
|
||||
{
|
||||
@@ -277,7 +277,7 @@ namespace
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
RDP_LOG << "[rdp_record] set busy_timeout failed" << std::endl;
|
||||
RDP_LOG_WARN("[rdp_record] set busy_timeout failed");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -324,14 +324,14 @@ namespace
|
||||
if (st.db.execute("DROP TABLE \"" + table + "\";") &&
|
||||
st.db.execute(stripped))
|
||||
{
|
||||
RDP_LOG << "[rdp_record] table " << table << " created without AUTOINCREMENT" << std::endl;
|
||||
RDP_LOG_INFO("[rdp_record] table {} created without AUTOINCREMENT", table);
|
||||
return true;
|
||||
}
|
||||
|
||||
//回退:若重建失败(极端情况),尽量恢复一张可用表,保证记录功能继续
|
||||
if (st.db.create_datatable<T>(::ormpp_auto_key{"id"}))
|
||||
{
|
||||
RDP_LOG << "[rdp_record] warning: keep AUTOINCREMENT for " << table << std::endl;
|
||||
RDP_LOG_WARN("[rdp_record] warning: keep AUTOINCREMENT for {}", table);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -391,7 +391,7 @@ namespace
|
||||
if (errors >= RDP_RECORD_MAX_CONSECUTIVE_ERRORS)
|
||||
{
|
||||
st.failed.store(true);
|
||||
RDP_LOG << "[rdp_record] too many consecutive write errors, recording stopped" << std::endl;
|
||||
RDP_LOG_ERROR("[rdp_record] too many consecutive write errors, recording stopped");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -465,7 +465,7 @@ namespace
|
||||
}
|
||||
if ((total_dropped / interval) != ((total_dropped - newly_dropped) / interval))
|
||||
{
|
||||
RDP_LOG << "[rdp_record] queue full, dropped rows total=" << total_dropped << std::endl;
|
||||
RDP_LOG_WARN("[rdp_record] queue full, dropped rows total={}", total_dropped);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -540,8 +540,7 @@ namespace
|
||||
if (rows > 0)
|
||||
{
|
||||
const std::uint64_t total = st.counters.dropped_rows.fetch_add(rows) + rows;
|
||||
RDP_LOG << "[rdp_record] writer stopped, dropped queued rows=" << rows
|
||||
<< ", total dropped=" << total << std::endl;
|
||||
RDP_LOG_INFO("[rdp_record] writer stopped, dropped queued rows={}, total dropped={}", rows, total);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -628,7 +627,7 @@ namespace
|
||||
{
|
||||
//写线程内任何异常都不能抛出 DLL 边界;记录失败即降级
|
||||
st.failed.store(true);
|
||||
RDP_LOG << "[rdp_record] writer thread exception, recording stopped" << std::endl;
|
||||
RDP_LOG_ERROR("[rdp_record] writer thread exception, recording stopped");
|
||||
DropQueued(st);
|
||||
}
|
||||
}
|
||||
@@ -694,7 +693,7 @@ namespace rdp_record
|
||||
char stamp[32] = {0};
|
||||
if (!TimeStamp(stamp, sizeof(stamp)))
|
||||
{
|
||||
RDP_LOG << "[rdp_record] cannot get local time, recording disabled" << std::endl;
|
||||
RDP_LOG_WARN("[rdp_record] cannot get local time, recording disabled");
|
||||
st.failed = true;
|
||||
return false;
|
||||
}
|
||||
@@ -720,7 +719,7 @@ namespace rdp_record
|
||||
|
||||
if (!st.db.connect(st.path))
|
||||
{
|
||||
RDP_LOG << "[rdp_record] cannot open database: " << st.path << std::endl;
|
||||
RDP_LOG_WARN("[rdp_record] cannot open database: {}", st.path);
|
||||
st.failed = true;
|
||||
return false;
|
||||
}
|
||||
@@ -731,18 +730,17 @@ namespace rdp_record
|
||||
st.writer = std::thread(WriterLoop, std::ref(st));
|
||||
st.ready = true;
|
||||
|
||||
RDP_LOG << "[rdp_record] enabled, database: " << st.path
|
||||
<< " (async writer, write_batch_rows=" << RDP_RECORD_WRITE_BATCH_ROWS
|
||||
<< ", queue_cap_bytes=" << RDP_RECORD_QUEUE_CAP_BYTES << ")" << std::endl;
|
||||
RDP_LOG_INFO("[rdp_record] enabled, database: {} (async writer, write_batch_rows={}, queue_cap_bytes={})",
|
||||
st.path, RDP_RECORD_WRITE_BATCH_ROWS, RDP_RECORD_QUEUE_CAP_BYTES);
|
||||
return true;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
RDP_LOG << "[rdp_record] init exception: " << e.what() << std::endl;
|
||||
RDP_LOG_ERROR("[rdp_record] init exception: {}", e.what());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
RDP_LOG << "[rdp_record] init exception" << std::endl;
|
||||
RDP_LOG_ERROR("[rdp_record] init exception");
|
||||
}
|
||||
|
||||
st.failed = true;
|
||||
@@ -796,7 +794,7 @@ namespace rdp_record
|
||||
{
|
||||
}
|
||||
|
||||
RDP_LOG << "[rdp_record] shutdown, database: " << st.path << std::endl;
|
||||
RDP_LOG_INFO("[rdp_record] shutdown, database: {}", st.path);
|
||||
st.ready = false;
|
||||
//保留状态对象(连接已断开、写线程已退出):下次 Init() 检测到 !ready && !failed 时复位并重新建库。
|
||||
//不在 Shutdown 内直接释放,避免宿主在 Destroy 的同时仍有记录调用时出现悬空指针(防御性处理)。
|
||||
|
||||
@@ -44,6 +44,7 @@ struct Trust_Track
|
||||
long long circle_id; //处理圈数ID
|
||||
|
||||
int Track_Index; //航迹号
|
||||
int Track_fingerprint; //航迹指纹
|
||||
|
||||
double Range; //距离
|
||||
double Azimuth; //方位
|
||||
@@ -189,4 +190,99 @@ struct Direct_Tracking_Target
|
||||
|
||||
};
|
||||
|
||||
// 系统工作参数结构体
|
||||
struct RDP_parameter
|
||||
{
|
||||
// ******************* 雷达工作参数 ************************//
|
||||
int radar_work_mode; //工作模式
|
||||
int scan_mode; //扫描模式 0=相扫模式 1=机扫模式
|
||||
float freq_0_GHz; //频点0频率
|
||||
float radar_height; //雷达平台高度 (m)
|
||||
float radar_system_delay; //系统延迟 (s)
|
||||
float data_rate_tws; //TWS模式数据率
|
||||
float data_rate_tas; //TAS模式数据率
|
||||
float data_rate_track_only; //纯跟踪模式数据率
|
||||
|
||||
// ******************* 点迹凝聚参数 ************************//
|
||||
float point_cohesion_range; //距离凝聚范围
|
||||
float point_cohesion_velocity; //速度凝聚范围
|
||||
float point_cohesion_azimuth; //方位凝聚范围
|
||||
float point_cohesion_range_min; //最小凝聚距离
|
||||
float point_cohesion_range_max; //最大凝聚距离
|
||||
|
||||
// ******************* 航迹关联参数 ************************//
|
||||
float sigma_range; //距离量测精度
|
||||
float sigma_azimuth; //方位量测精度
|
||||
float sigma_pitch; //俯仰量测精度
|
||||
float sigma_velocity; //速度量测精度
|
||||
float associate_threshold; //关联门限
|
||||
int extrapolate_max; //最大外推点数
|
||||
int extrapolate_max_tas; //TAS最大外推点数
|
||||
int height_smooth_win; //高度平滑窗长
|
||||
|
||||
// ******************* 航迹起始参数 ************************//
|
||||
float track_init_threshold; //起航门限
|
||||
float track_init_angle; //起航夹角
|
||||
int track_init_point_num_min; //最少起批点数
|
||||
float track_init_quality_L3; //3点起批质量门限
|
||||
float track_init_quality_L4; //4点起批质量门限
|
||||
float track_init_quality_L5; //5点起批质量门限
|
||||
float track_init_quality_L6; //6点起批质量门限
|
||||
float track_init_quality_L7; //7点起批质量门限
|
||||
float track_init_velocity_min; //航迹起始最小速度
|
||||
float track_init_velocity_max; //航迹起始最大速度
|
||||
|
||||
// ******************* 航迹消亡参数 ************************//
|
||||
int track_die_round; //航迹消亡圈数
|
||||
int track_die_round_pramary; //重点航迹消亡圈数
|
||||
int track_die_round_tas; //TAS航迹消亡圈数
|
||||
float track_die_quality; //航迹消批质量条件
|
||||
float track_die_quality_pramary; //重点航迹消批质量条件
|
||||
|
||||
// ******************* 航迹质量管理参数 ************************//
|
||||
float track_quality_max; //航迹质量最大值
|
||||
float track_quality_min; //航迹质量最小值
|
||||
float track_quality_d_weight; //马氏距离权重
|
||||
float track_quality_snr_weight; //信噪比权重
|
||||
float track_quality_len_weight; //航迹长度权重
|
||||
float track_quality_iter_coeff; //航迹质量迭代系数
|
||||
float track_quality_decrease_coeff_o2; //航迹质量下降系数2次项
|
||||
float track_quality_decrease_coeff_o1; //航迹质量下降系数1次项
|
||||
|
||||
// ******************* 航迹批号管理 ************************//
|
||||
int track_index_max; //最大航迹批号
|
||||
|
||||
// ******************* 波束调度参数 ************************//
|
||||
int default_track_beam_type; //默认跟踪波束类型
|
||||
int tas_track_num_max; //最大同时跟踪目标数
|
||||
|
||||
// ******************* singer模型机动参数 ************************//
|
||||
float model_1_Q_fast; //模型1
|
||||
float model_1_Q_slow; //模型1
|
||||
float model_2_Q_fast; //模型2
|
||||
float model_2_Q_slow; //模型2
|
||||
float model_3_Q_fast; //模型3
|
||||
float model_3_Q_slow; //模型3
|
||||
|
||||
// ******************* 航迹起始屏蔽区 ************************//
|
||||
int track_init_prohibite_area_num; //航迹起始屏蔽区数
|
||||
float track_init_prohibite_area_range_min[30]; //最小距离 (m)
|
||||
float track_init_prohibite_area_range_max[30]; //最大距离 (m)
|
||||
float track_init_prohibite_area_azimuth_min[30]; //最小方位角 (度)
|
||||
float track_init_prohibite_area_azimuth_max[30]; //最大方位角 (度)
|
||||
|
||||
// ******************* 禁止TAS跟踪区 ************************//
|
||||
int tas_prohibite_area_num; //禁止TAS的区域数
|
||||
float tas_prohibite_area_range_min[30]; //最小距离 (m)
|
||||
float tas_prohibite_area_range_max[30]; //最大距离 (m)
|
||||
float tas_prohibite_area_azimuth_min[30]; //最小方位角 (度)
|
||||
float tas_prohibite_area_azimuth_max[30]; //最大方位角 (度)
|
||||
};
|
||||
|
||||
// ******************* 全局工作参数对象 ************************//
|
||||
// 所有处理模块直接访问该全局对象,不再通过函数参数逐层传递。
|
||||
// 定义在 data_process.cpp;由 Data_Process::parameters_reload() 在配置文件完整校验通过后整体赋值
|
||||
// (失败时保持原值)。处理期间只读;配置重载应与处理互斥,宿主按单线程接口使用(见 README BUG-03)。
|
||||
extern RDP_parameter rdp_parameter;
|
||||
|
||||
#endif // STRUCT_H
|
||||
|
||||
@@ -9,15 +9,9 @@
|
||||
|
||||
using namespace std;
|
||||
|
||||
// parameters.h 中定义了 DATA_RATE_TAS 宏(相扫0.3/机扫0.0625),
|
||||
// 而本模块的波束门控必须使用 RadarPara 结构体成员 Work_Parameter.DATA_RATE_TAS
|
||||
// (由dll调用者经 track_process_parameters_initial 输入),故取消该宏定义,
|
||||
// 否则 Work_Parameter.DATA_RATE_TAS 会被预处理器展开为非法常量表达式。
|
||||
#undef DATA_RATE_TAS
|
||||
|
||||
TAS_Ctrl::TAS_Ctrl()
|
||||
{
|
||||
memset(tas_target_queue,0,TAS_QUEUE_LENGTH*sizeof(Tracking_Target));
|
||||
memset(tas_target_queue,0,RDP_TAS_QUEUE_CAPACITY*sizeof(Tracking_Target));
|
||||
|
||||
tas_target_num=0;
|
||||
}
|
||||
@@ -33,19 +27,18 @@ void TAS_Ctrl::tas_ctrl_process(std::vector<Trust_Track> *trust_track
|
||||
struct TrackingBeam *Tracking_beam,
|
||||
struct Track Trust_Track_Output[][10],
|
||||
int *Trust_track_num_Output,
|
||||
struct RadarPara Work_Parameter,
|
||||
long long latest_timestamp)
|
||||
{
|
||||
if (Trust_track_num_Output == 0) return;
|
||||
*Trust_track_num_Output = 0;
|
||||
tas_target_add(trust_track, Trust_Track_Output, Trust_track_num_Output,Work_Parameter);
|
||||
tas_target_del(trust_track, Trust_Track_Output, Trust_track_num_Output,Work_Parameter);
|
||||
tas_beam_output(trust_track,Tracking_beam,Work_Parameter,latest_timestamp);
|
||||
tas_target_add(trust_track, Trust_Track_Output, Trust_track_num_Output);
|
||||
tas_target_del(trust_track, Trust_Track_Output, Trust_track_num_Output);
|
||||
tas_beam_output(trust_track,Tracking_beam,latest_timestamp);
|
||||
|
||||
// //跟踪队列移位
|
||||
// struct Tracking_Target tas_target_tmp;
|
||||
// memcpy(&tas_target_tmp, &tas_target_queue[TAS_QUEUE_LENGTH-1], sizeof(Tracking_Target));
|
||||
// for (int i=TAS_QUEUE_LENGTH-1;i>0;i--)
|
||||
// memcpy(&tas_target_tmp, &tas_target_queue[RDP_TAS_QUEUE_CAPACITY-1], sizeof(Tracking_Target));
|
||||
// for (int i=RDP_TAS_QUEUE_CAPACITY-1;i>0;i--)
|
||||
// {
|
||||
// memcpy(&tas_target_queue[i],&tas_target_queue[i-1],sizeof(Tracking_Target));
|
||||
// }
|
||||
@@ -54,27 +47,26 @@ void TAS_Ctrl::tas_ctrl_process(std::vector<Trust_Track> *trust_track
|
||||
|
||||
void TAS_Ctrl::tas_target_add(std::vector<Trust_Track> *trust_track,
|
||||
struct Track Trust_Track_Output[][10],
|
||||
int *Trust_track_num_Output,
|
||||
struct RadarPara Work_Parameter)
|
||||
int *Trust_track_num_Output)
|
||||
{
|
||||
|
||||
for (size_t i=0;i<trust_track->size();i++)
|
||||
{
|
||||
double r=sqrt(pow((*trust_track)[i].X[0],2)+pow((*trust_track)[i].X[3],2));
|
||||
double v=sqrt(pow((*trust_track)[i].X[1],2)+pow((*trust_track)[i].X[4],2));
|
||||
double h=(*trust_track)[i].Height+Work_Parameter.Height;
|
||||
double h=(*trust_track)[i].Height+rdp_parameter.radar_height;
|
||||
double azi = atan2((*trust_track)[i].X[3],(*trust_track)[i].X[0]);
|
||||
if(azi<0)
|
||||
azi=azi+2*PI;
|
||||
azi=azi/PI*180;
|
||||
|
||||
//进入跟踪的条件: 1.手动跟踪的目标 或 满足速度、距离、高度条件满足 关联点数大于2 2.不在禁止跟踪区域
|
||||
if( ( (*trust_track)[i].manual_tracking_flag == 1)//|| (tas_auto_start( v, r, h, Work_Parameter)==1 && (*trust_track)[i][k].associate_point_number>=2)
|
||||
&& tas_target_num < MAX_TAS_NUM
|
||||
if( ( (*trust_track)[i].manual_tracking_flag == 1)//|| (tas_auto_start( v, r, h)==1 && (*trust_track)[i][k].associate_point_number>=2)
|
||||
&& tas_target_num < rdp_parameter.tas_track_num_max
|
||||
&& (*trust_track)[i].Track_Mode == 0
|
||||
&& tas_prohibite_area( r, azi, v, h, Work_Parameter) == 0)
|
||||
&& tas_prohibite_area( r, azi, v, h) == 0)
|
||||
{
|
||||
for(int j=0;j<TAS_QUEUE_LENGTH;j++)
|
||||
for(int j=0;j<RDP_TAS_QUEUE_CAPACITY;j++)
|
||||
{
|
||||
if(tas_target_queue[j].empty_flag==0)// && j%2==0)
|
||||
{
|
||||
@@ -94,22 +86,80 @@ void TAS_Ctrl::tas_target_add(std::vector<Trust_Track> *trust_track,
|
||||
|
||||
//目标跟踪状态发生改变 对外输出航迹更新信息
|
||||
*Trust_track_num_Output= *Trust_track_num_Output+1;
|
||||
double r_output,azmi_output;
|
||||
coor_trans Coor_trans;
|
||||
Coor_trans.cart2polar((*trust_track)[i].X[0],(*trust_track)[i].X[3],&r_output,&azmi_output);
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].Point_Sum=1;
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].Range=static_cast<float>(r_output);
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].Azimuth=static_cast<float>(azmi_output/PI*180);
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].Track_Index=(*trust_track)[i].Track_Index; //航迹号
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].Range_V=
|
||||
static_cast<float>(sqrt((*trust_track)[i].X[1]*(*trust_track)[i].X[1]+(*trust_track)[i].X[4]*(*trust_track)[i].X[4]));
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].Amplitude=static_cast<float>((*trust_track)[i].Amplitude);
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].Flag_Point=(*trust_track)[i].point_flag;
|
||||
|
||||
double x,y;
|
||||
x=(*trust_track)[i].X[0]+(*trust_track)[i].X[1]*rdp_parameter.radar_system_delay;
|
||||
y=(*trust_track)[i].X[3]+(*trust_track)[i].X[4]*rdp_parameter.radar_system_delay;
|
||||
coor_trans Coor_trans;
|
||||
double r, azi;
|
||||
Coor_trans.cart2polar(x, y, &r, &azi);
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].Range=static_cast<float>(r);
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].Azimuth=static_cast<float>(azi/PI*180);
|
||||
|
||||
if((*trust_track)[i].Height<r)
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].Elevation = static_cast<float>(asin((*trust_track)[i].Height/r)/PI*180);
|
||||
else
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].Elevation = static_cast<float>((*trust_track)[i].elev_point);
|
||||
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].Track_Index=(*trust_track)[i].Track_Index;
|
||||
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].Range_V = static_cast<float>(sqrt((*trust_track)[i].X[1]*(*trust_track)[i].X[1]+(*trust_track)[i].X[4]*(*trust_track)[i].X[4]));
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].Amplitude = static_cast<float>((*trust_track)[i].Amplitude);
|
||||
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].Track_Mode=(*trust_track)[i].Track_Mode;
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].Elevation=static_cast<float>(asin((*trust_track)[i].Height/r_output)/PI*180);
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].z=static_cast<float>((*trust_track)[i].Height+Work_Parameter.Height);
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].Target_Type=(*trust_track)[i].Target_Type;
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].track_time=static_cast<float>((*trust_track)[i].T_track/1000.0);
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].GNSS_time=(*trust_track)[i].GNSS_time;
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].Flag_Point = (*trust_track)[i].point_flag;
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].z=static_cast<float>((*trust_track)[i].Height+rdp_parameter.radar_height);
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].Quality = static_cast<float>((*trust_track)[i].Quality * rdp_parameter.track_quality_max);
|
||||
|
||||
double Direction_Angle;
|
||||
Direction_Angle=atan2((*trust_track)[i].X[4], (*trust_track)[i].X[1]);
|
||||
if(Direction_Angle<0){Direction_Angle=Direction_Angle+2*PI;}
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].Direction_Angle=static_cast<float>(Direction_Angle/PI*180);
|
||||
|
||||
//关联点信息
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].range_point=static_cast<float>((*trust_track)[i].range_point);
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].azi_point=static_cast<float>((*trust_track)[i].azi_point);
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].elev_point=static_cast<float>((*trust_track)[i].elev_point);
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].vr_point=static_cast<float>((*trust_track)[i].vr_point);
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].point_type=0;
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].prf_point = static_cast<float>((*trust_track)[i].prf_point);
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].track_snr = static_cast<float>((*trust_track)[i].snr_point);
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].amp_asv_point = static_cast<float>((*trust_track)[i].amp_asv_point);
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].prn_fftn_point = static_cast<float>((*trust_track)[i].prn_fftn_point);
|
||||
|
||||
//输出平滑后的高度
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].z = static_cast<float>((*trust_track)[i].Height+rdp_parameter.radar_height);
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].x=static_cast<float>((*trust_track)[i].X[0]);
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].y=static_cast<float>((*trust_track)[i].X[3]);
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].v_x=static_cast<float>((*trust_track)[i].X[1]);
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].v_y=static_cast<float>((*trust_track)[i].X[4]);
|
||||
|
||||
if(acos((-x * cos(Direction_Angle) - y * sin(Direction_Angle)) / r ) < PI / 2)
|
||||
{
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].Flag_Direction=1;
|
||||
// if(r*abs(sin(azi - Direction_Angle))<500)
|
||||
// {
|
||||
// Trust_Track_Output[*Trust_track_num_Output-1][0].Flag_Direction=1;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// Trust_Track_Output[*Trust_track_num_Output-1][0].Flag_Direction=0;
|
||||
// }
|
||||
}
|
||||
else
|
||||
{
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].Flag_Direction=0;
|
||||
}
|
||||
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].track_rcs = static_cast<float>((*trust_track)[i].RCS);
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].pitch_num = (*trust_track)[i].pitch_num;
|
||||
|
||||
std::memcpy(Trust_Track_Output[*Trust_track_num_Output-1][0].speed_dim, (*trust_track)[i].speed_dim, sizeof(Trust_Track_Output[*Trust_track_num_Output-1][0].speed_dim));
|
||||
std::memcpy(Trust_Track_Output[*Trust_track_num_Output-1][0].range_dim, (*trust_track)[i].range_dim, sizeof(Trust_Track_Output[*Trust_track_num_Output-1][0].range_dim));
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
@@ -119,32 +169,16 @@ void TAS_Ctrl::tas_target_add(std::vector<Trust_Track> *trust_track,
|
||||
|
||||
};
|
||||
|
||||
//int TAS_Ctrl:: tas_auto_start(double v, double r, double h, struct RadarPara Work_Parameter)
|
||||
//{
|
||||
// 说明:TAS 自动开始(tas_auto_start)的原实现依赖改造前雷达参数结构体中的
|
||||
// TAS 距离/速度/高度门限,这些参数未纳入新的 RDP_parameter,故原实现保持停用;
|
||||
// 具体逻辑不再以注释代码形式保留(tas_ctrl.h 中保留声明,后续如需启用再按新参数补齐)。
|
||||
|
||||
//// if(r>Work_Parameter.R_TAS_min && r<Work_Parameter.R_TAS_max && v<Work_Parameter.V_TAS_max && v>Work_Parameter.V_TAS_min
|
||||
//// && ( (r<=1000 && h>=Work_Parameter.TAS_Height_1) || (r<=2000 && r>1000 && h>=Work_Parameter.TAS_Height_2) || (r<=3000 && r>2000 && h>=Work_Parameter.TAS_Height_3) || (r<=4000 && r>3000 && h>=Work_Parameter.TAS_Height_4) || (r>4000 && h>=Work_Parameter.TAS_Height_5) )
|
||||
//// )
|
||||
// if( r>Work_Parameter.R_TAS_min && r<Work_Parameter.R_TAS_max && v<Work_Parameter.V_TAS_max && v>Work_Parameter.V_TAS_min
|
||||
// && ( (r<=1000 && h>=Work_Parameter.TAS_Height_1) || (r<=2000 && r>1000 && h>=Work_Parameter.TAS_Height_2) || (r<=3000 && r>2000 && h>=Work_Parameter.TAS_Height_3) || (r<=4000 && r>3000 && h>=Work_Parameter.TAS_Height_4) || (r>4000 && h>=Work_Parameter.TAS_Height_5) ) )
|
||||
// {
|
||||
// return 1;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// return 0;
|
||||
// }
|
||||
|
||||
//// return 0;
|
||||
|
||||
//};
|
||||
|
||||
int TAS_Ctrl::tas_prohibite_area(double r,double azi, double v, double h,struct RadarPara Work_Parameter)
|
||||
int TAS_Ctrl::tas_prohibite_area(double r,double azi, double v, double h)
|
||||
{
|
||||
|
||||
for (int i=0;i<Work_Parameter.TAS_prohibite_area_num;i++)
|
||||
for (int i=0;i<rdp_parameter.tas_prohibite_area_num;i++)
|
||||
{
|
||||
if(r<Work_Parameter.R_max_TAS_prohibited[i] && r>Work_Parameter.R_min_TAS_prohibited[i] && azi<Work_Parameter.Azimuth_max_TAS_prohibited[i] && azi>Work_Parameter.Azimuth_min_TAS_prohibited[i])
|
||||
if(r<rdp_parameter.tas_prohibite_area_range_max[i] && r>rdp_parameter.tas_prohibite_area_range_min[i] && azi<rdp_parameter.tas_prohibite_area_azimuth_max[i] && azi>rdp_parameter.tas_prohibite_area_azimuth_min[i])
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
@@ -156,12 +190,11 @@ int TAS_Ctrl::tas_prohibite_area(double r,double azi, double v, double h,struct
|
||||
|
||||
void TAS_Ctrl::tas_target_del(std::vector<Trust_Track> *trust_track,
|
||||
struct Track Trust_Track_Output[][10],
|
||||
int *Trust_track_num_Output,
|
||||
struct RadarPara Work_Parameter)
|
||||
int *Trust_track_num_Output)
|
||||
{
|
||||
|
||||
//查找TAS目标是否已经消批 若消批则移出TAS队列
|
||||
for (int i=0;i<TAS_QUEUE_LENGTH;i++)
|
||||
for (int i=0;i<RDP_TAS_QUEUE_CAPACITY;i++)
|
||||
{
|
||||
if(tas_target_queue[i].empty_flag == 1)
|
||||
{
|
||||
@@ -189,14 +222,14 @@ void TAS_Ctrl::tas_target_del(std::vector<Trust_Track> *trust_track,
|
||||
// {
|
||||
// double r=sqrt(pow((*trust_track)[i].X[0],2)+pow((*trust_track)[i].X[3],2));
|
||||
// double v=sqrt(pow((*trust_track)[i].X[1],2)+pow((*trust_track)[i].X[4],2));
|
||||
// double h=(*trust_track)[i].Height+Work_Parameter.Height;
|
||||
// double h=(*trust_track)[i].Height+rdp_parameter.radar_height;
|
||||
|
||||
// if((*trust_track)[i].manual_tracking_flag == 0 && (*trust_track)[i].Track_Mode == 1 && tas_auto_end(v,r,h,Work_Parameter) == 1) //满足结束跟踪条件
|
||||
// if((*trust_track)[i].manual_tracking_flag == 0 && (*trust_track)[i].Track_Mode == 1 && tas_auto_end(v,r,h) == 1) //满足结束跟踪条件
|
||||
// {
|
||||
|
||||
// int track_index=(*trust_track)[i].Track_Index;
|
||||
|
||||
// for (int ii=0;ii<TAS_QUEUE_LENGTH;ii++)
|
||||
// for (int ii=0;ii<RDP_TAS_QUEUE_CAPACITY;ii++)
|
||||
// {
|
||||
// if(tas_target_queue[ii].Index == track_index)
|
||||
// {
|
||||
@@ -224,7 +257,7 @@ void TAS_Ctrl::tas_target_del(std::vector<Trust_Track> *trust_track,
|
||||
// Trust_Track_Output[*Trust_track_num_Output-1][0].Flag_Point=(*trust_track)[i].point_flag;
|
||||
// Trust_Track_Output[*Trust_track_num_Output-1][0].Track_Mode=(*trust_track)[i].Track_Mode;
|
||||
// Trust_Track_Output[*Trust_track_num_Output-1][0].Elevation=static_cast<float>(asin((*trust_track)[i].Height/r_output)/PI*180);
|
||||
// Trust_Track_Output[*Trust_track_num_Output-1][0].z=static_cast<float>((*trust_track)[i].Height+Work_Parameter.Height);
|
||||
// Trust_Track_Output[*Trust_track_num_Output-1][0].z=static_cast<float>((*trust_track)[i].Height+rdp_parameter.radar_height);
|
||||
// Trust_Track_Output[*Trust_track_num_Output-1][0].Target_Type=(*trust_track)[i].Target_Type;
|
||||
// Trust_Track_Output[*Trust_track_num_Output-1][0].track_snr = static_cast<float>((*trust_track)[i].snr_point);
|
||||
// }
|
||||
@@ -235,28 +268,11 @@ void TAS_Ctrl::tas_target_del(std::vector<Trust_Track> *trust_track,
|
||||
|
||||
};
|
||||
|
||||
//int TAS_Ctrl::tas_auto_end(double v, double r, double h, struct RadarPara Work_Parameter)
|
||||
//{
|
||||
// if((r<=1000 && h<Work_Parameter.TAS_Height_1) || (r<=2000 && r>1000 && h<Work_Parameter.TAS_Height_2) || (r<=3000 && r>2000 && h<Work_Parameter.TAS_Height_3) || (r<=4000 && r>3000 && h<Work_Parameter.TAS_Height_4) || (r>4000 && h<Work_Parameter.TAS_Height_5)
|
||||
// || v<Work_Parameter.V_TAS_min || v>Work_Parameter.V_TAS_max
|
||||
// )
|
||||
// {
|
||||
// return 1;
|
||||
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
|
||||
// return 0;
|
||||
// }
|
||||
|
||||
// return 0;
|
||||
|
||||
//};
|
||||
// 说明:TAS 自动结束(tas_auto_end)的原实现同样依赖未纳入 RDP_parameter 的
|
||||
// TAS 距离/速度/高度门限,故保持停用(tas_ctrl.h 中保留声明,实现待后续补齐)。
|
||||
|
||||
void TAS_Ctrl::tas_beam_output(std::vector<Trust_Track> *trust_track,
|
||||
struct TrackingBeam *Tracking_beam,
|
||||
struct RadarPara Work_Parameter,
|
||||
long long latest_timestamp)
|
||||
{
|
||||
|
||||
@@ -270,7 +286,7 @@ void TAS_Ctrl::tas_beam_output(std::vector<Trust_Track> *trust_track
|
||||
double X_now[6] = {0};
|
||||
|
||||
//查找TAS队列中CPI时间最早的目标且满足TAS数据率的目标
|
||||
for(int tas_idx=0;tas_idx<TAS_QUEUE_LENGTH;tas_idx++)
|
||||
for(int tas_idx=0;tas_idx<RDP_TAS_QUEUE_CAPACITY;tas_idx++)
|
||||
{
|
||||
if(tas_target_queue[tas_idx].empty_flag==0)
|
||||
{
|
||||
@@ -285,7 +301,7 @@ void TAS_Ctrl::tas_beam_output(std::vector<Trust_Track> *trust_track
|
||||
delta_T_last_track = (latest_timestamp - tas_target_queue[tas_idx].last_track_time) / 1000.0f;
|
||||
|
||||
//查找CPI时间最早,且满足TAS数据率的目标
|
||||
if(CPI_time < earliest_CPI_time && (delta_T_last_track > Work_Parameter.DATA_RATE_TAS))
|
||||
if(CPI_time < earliest_CPI_time && (delta_T_last_track > rdp_parameter.data_rate_tas))
|
||||
{
|
||||
earliest_CPI_time = CPI_time;
|
||||
H_track = (*trust_track)[track_idx].Height;
|
||||
@@ -339,13 +355,13 @@ void TAS_Ctrl::tas_beam_output(std::vector<Trust_Track> *trust_track
|
||||
Tracking_beam->Elev=(float)elev;
|
||||
|
||||
//跟踪波束类型
|
||||
Tracking_beam->type=1;
|
||||
Tracking_beam->type=rdp_parameter.default_track_beam_type;
|
||||
|
||||
//跟踪目标批号
|
||||
Tracking_beam->TAS_track_index = (*trust_track)[earliest_track_idx].Track_Index;
|
||||
Tracking_beam->TAS_track_index = (*trust_track)[earliest_track_idx].Track_fingerprint;
|
||||
|
||||
//跟踪波束开关开启
|
||||
Tracking_beam->open_flag=1;
|
||||
|
||||
//RDP_LOG << "TAS <" << (*trust_track)[earliest_track_idx].Track_Index << "> output: range: " <<Tracking_beam->Range << "azi: " <<Tracking_beam->Azi <<"pit: " <<Tracking_beam->Elev << std::endl;
|
||||
// RDP_LOG_INFO("TAS <{}> output: range: {} azi: {} pit: {}", Tracking_beam->TAS_track_index, Tracking_beam->Range, Tracking_beam->Azi, Tracking_beam->Elev);
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ public:
|
||||
struct TrackingBeam *Tracking_beam,
|
||||
struct Track Trust_Track_Output[][10],
|
||||
int *Trust_track_num_Output,
|
||||
struct RadarPara Work_Parameter,
|
||||
long long latest_timestamp);
|
||||
|
||||
private:
|
||||
@@ -26,31 +25,28 @@ private:
|
||||
//添加TAS目标
|
||||
void tas_target_add(std::vector<Trust_Track> *trust_track,
|
||||
struct Track Trust_Track_Output[][10],
|
||||
int *Trust_track_num_Output,
|
||||
struct RadarPara Work_Parameter);
|
||||
int *Trust_track_num_Output);
|
||||
|
||||
//删除TAS目标
|
||||
void tas_target_del(std::vector<Trust_Track> *trust_track,
|
||||
struct Track Trust_Track_Output[][10],
|
||||
int *Trust_track_num_Output,
|
||||
struct RadarPara Work_Parameter);
|
||||
int *Trust_track_num_Output);
|
||||
|
||||
//TAS队列信息输出
|
||||
void tas_beam_output(std::vector<Trust_Track> *trust_track,
|
||||
struct TrackingBeam *Tracking_beam,
|
||||
struct RadarPara Work_Parameter,
|
||||
long long latest_timestamp);
|
||||
|
||||
//TAS 自动开启条件
|
||||
int tas_auto_start(double v, double r, double h, struct RadarPara Work_Parameter);
|
||||
int tas_auto_start(double v, double r, double h);
|
||||
|
||||
//TAS 自动退出条件
|
||||
int tas_auto_end(double v, double r, double h, struct RadarPara Work_Parameter);
|
||||
int tas_auto_end(double v, double r, double h);
|
||||
|
||||
// 禁止跟踪区域判断
|
||||
int tas_prohibite_area(double r,double azi, double v, double h,struct RadarPara Work_Parameter);
|
||||
int tas_prohibite_area(double r,double azi, double v, double h);
|
||||
|
||||
struct Tracking_Target tas_target_queue[TAS_QUEUE_LENGTH]; //跟踪目标信息队列
|
||||
struct Tracking_Target tas_target_queue[RDP_TAS_QUEUE_CAPACITY]; //跟踪目标信息队列
|
||||
|
||||
int tas_target_num; //跟踪目标数量
|
||||
|
||||
|
||||
@@ -19,8 +19,7 @@ int Track_Asso:: track_asso_process(std::vector<PointRecv> *point_
|
||||
std::vector <Trust_Track> *trust_track, //航迹文件
|
||||
struct Track Trust_Track_Output[MAX_TRACK_NUM][10], //更新航迹信息
|
||||
int *Trust_track_num_Output, //更新航迹数
|
||||
long long latest_timestamp, //最新时间戳
|
||||
struct RadarPara Work_Parameter //雷达参数
|
||||
long long latest_timestamp //最新时间戳
|
||||
)
|
||||
{
|
||||
|
||||
@@ -35,7 +34,7 @@ int Track_Asso:: track_asso_process(std::vector<PointRecv> *point_
|
||||
//IMM算法
|
||||
model_interaction(trust_track);
|
||||
|
||||
model_filter(trust_track, Work_Parameter, latest_timestamp);
|
||||
model_filter(trust_track, latest_timestamp);
|
||||
|
||||
model_output(trust_track);
|
||||
|
||||
@@ -80,8 +79,8 @@ int Track_Asso:: track_asso_process(std::vector<PointRecv> *point_
|
||||
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].Point_Sum=1;
|
||||
double x,y;
|
||||
x=(*trust_track)[i].X[0]+(*trust_track)[i].X[1]*Work_Parameter.Sys_delay;
|
||||
y=(*trust_track)[i].X[3]+(*trust_track)[i].X[4]*Work_Parameter.Sys_delay;
|
||||
x=(*trust_track)[i].X[0]+(*trust_track)[i].X[1]*rdp_parameter.radar_system_delay;
|
||||
y=(*trust_track)[i].X[3]+(*trust_track)[i].X[4]*rdp_parameter.radar_system_delay;
|
||||
coor_trans Coor_trans;
|
||||
double r, azi;
|
||||
Coor_trans.cart2polar(x, y, &r, &azi);
|
||||
@@ -103,8 +102,8 @@ int Track_Asso:: track_asso_process(std::vector<PointRecv> *point_
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].track_time=static_cast<float>((*trust_track)[i].T_track/1000.0);
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].GNSS_time=(*trust_track)[i].GNSS_time;
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].Flag_Point = (*trust_track)[i].point_flag;
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].z=static_cast<float>((*trust_track)[i].Height+Work_Parameter.Height);
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].Quality = static_cast<float>((*trust_track)[i].Quality * TRACK_QUALITY_MAX);
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].z=static_cast<float>((*trust_track)[i].Height+rdp_parameter.radar_height);
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].Quality = static_cast<float>((*trust_track)[i].Quality * rdp_parameter.track_quality_max);
|
||||
|
||||
double Direction_Angle;
|
||||
Direction_Angle=atan2((*trust_track)[i].X[4], (*trust_track)[i].X[1]);
|
||||
@@ -130,7 +129,7 @@ int Track_Asso:: track_asso_process(std::vector<PointRecv> *point_
|
||||
//直接输出点迹高度-20260605
|
||||
//Trust_Track_Output[*Trust_track_num_Output-1][0].z = (*trust_track)[i].range_point * sin((*trust_track)[i].elev_point/180.0*PI);
|
||||
//输出平滑后的高度
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].z = static_cast<float>((*trust_track)[i].Height+Work_Parameter.Height);
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].z = static_cast<float>((*trust_track)[i].Height+rdp_parameter.radar_height);
|
||||
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].Elevation = static_cast<float>((*trust_track)[i].elev_point);
|
||||
|
||||
@@ -288,7 +287,7 @@ void Track_Asso:: model_interaction(std::vector <Trust_Track> *trust_track)
|
||||
}
|
||||
|
||||
// 产生 F Q 矩阵
|
||||
void Track_Asso::IMM_F_Q_gen(double v_track, double delta_T,double F[6][6], double Q1[6][6], double Q2[6][6], double Q3[6][6],struct RadarPara Work_Parameter)
|
||||
void Track_Asso::IMM_F_Q_gen(double v_track, double delta_T,double F[6][6], double Q1[6][6], double Q2[6][6], double Q3[6][6])
|
||||
{
|
||||
|
||||
//F
|
||||
@@ -316,7 +315,7 @@ void Track_Asso::IMM_F_Q_gen(double v_track, double delta_T,double F[6][6], doub
|
||||
|
||||
//模型1
|
||||
//Q
|
||||
double q1=Work_Parameter.Model1_Q_slow;
|
||||
double q1=rdp_parameter.model_1_Q_slow;
|
||||
double q111=1/(2*pow(alpha,5))*(1-exp(-2*alpha*delta_T)+2*alpha*delta_T+2*pow(alpha,3)*pow(delta_T,3)/3-2*pow(alpha,2)*pow(delta_T,2)-4*alpha*delta_T*exp(-alpha*delta_T));
|
||||
double q121=1/(2*pow(alpha,4))*(exp(-2*alpha*delta_T)+1-2*exp(-alpha*delta_T)+2*alpha*delta_T*exp(-alpha*delta_T)-2*alpha*delta_T+pow(alpha,2)*pow(delta_T,2));
|
||||
double q131=1/(2*pow(alpha,3))*(1-exp(-2*alpha*delta_T)-2*alpha*delta_T*exp(-alpha*delta_T));
|
||||
@@ -337,24 +336,24 @@ void Track_Asso::IMM_F_Q_gen(double v_track, double delta_T,double F[6][6], doub
|
||||
double q2;
|
||||
if(v_track<=5)
|
||||
{
|
||||
q2=Work_Parameter.Model2_Q_slow/20;
|
||||
q2=rdp_parameter.model_2_Q_slow/20;
|
||||
}
|
||||
else if(v_track<=15&&v_track>5)
|
||||
{
|
||||
q2=Work_Parameter.Model2_Q_slow;
|
||||
q2=rdp_parameter.model_2_Q_slow;
|
||||
}
|
||||
else if(v_track<=30&&v_track>15)
|
||||
{
|
||||
q2=Work_Parameter.Model2_Q_slow;
|
||||
q2=rdp_parameter.model_2_Q_slow;
|
||||
}
|
||||
else if(v_track<=100&&v_track>30)
|
||||
{
|
||||
q2=Work_Parameter.Model2_Q_fast;
|
||||
q2=rdp_parameter.model_2_Q_fast;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
q2=Work_Parameter.Model2_Q_fast;
|
||||
q2=rdp_parameter.model_2_Q_fast;
|
||||
}
|
||||
|
||||
double q112=1/(2*pow(alpha,5))*(1-exp(-2*alpha*delta_T)+2*alpha*delta_T+2*pow(alpha,3)*pow(delta_T,3)/3-2*pow(alpha,2)*pow(delta_T,2)-4*alpha*delta_T*exp(-alpha*delta_T));
|
||||
@@ -378,29 +377,29 @@ void Track_Asso::IMM_F_Q_gen(double v_track, double delta_T,double F[6][6], doub
|
||||
double q3;
|
||||
if(v_track<=5)
|
||||
{
|
||||
q3=Work_Parameter.Model3_Q_slow/20;
|
||||
q3=rdp_parameter.model_3_Q_slow/20;
|
||||
}
|
||||
else if(v_track<=15&&v_track>5)
|
||||
{
|
||||
q3=Work_Parameter.Model3_Q_slow;
|
||||
q3=rdp_parameter.model_3_Q_slow;
|
||||
}
|
||||
else if(v_track<=20&&v_track>15)
|
||||
{
|
||||
q3=Work_Parameter.Model3_Q_slow;
|
||||
q3=rdp_parameter.model_3_Q_slow;
|
||||
}
|
||||
else if(v_track<=50&&v_track>20)
|
||||
{
|
||||
q3=Work_Parameter.Model3_Q_fast;
|
||||
q3=rdp_parameter.model_3_Q_fast;
|
||||
|
||||
}
|
||||
else if(v_track<=100&&v_track>50)
|
||||
{
|
||||
q3=2*Work_Parameter.Model3_Q_fast;
|
||||
q3=2*rdp_parameter.model_3_Q_fast;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
q3=10*Work_Parameter.Model3_Q_fast;
|
||||
q3=10*rdp_parameter.model_3_Q_fast;
|
||||
}
|
||||
|
||||
double q113=1/(2*pow(alpha,5))*(1-exp(-2*alpha*delta_T)+2*alpha*delta_T+2*pow(alpha,3)*pow(delta_T,3)/3-2*pow(alpha,2)*pow(delta_T,2)-4*alpha*delta_T*exp(-alpha*delta_T));
|
||||
@@ -428,11 +427,10 @@ void Track_Asso::IMM_d_cal(double v_track,
|
||||
double X3[6], double P3[6][6],
|
||||
double Z[3], double prt,double freq_ind,
|
||||
double delta_T,
|
||||
double *d1,double *d2,double *d3,
|
||||
struct RadarPara Work_Parameter)
|
||||
double *d1,double *d2,double *d3)
|
||||
{
|
||||
double F[6][6], Q1[6][6], Q2[6][6], Q3[6][6];
|
||||
IMM_F_Q_gen( v_track, delta_T,F, Q1, Q2, Q3,Work_Parameter);
|
||||
IMM_F_Q_gen( v_track, delta_T,F, Q1, Q2, Q3);
|
||||
|
||||
kalman Kalman;
|
||||
*d1=Kalman.d_cal_EKF(F,Q1,Z,X1,P1,prt,freq_ind);
|
||||
@@ -441,7 +439,7 @@ void Track_Asso::IMM_d_cal(double v_track,
|
||||
|
||||
}
|
||||
|
||||
void Track_Asso:: model_filter(std::vector <Trust_Track> *trust_track,struct RadarPara Work_Parameter, long long latest_timestamp)
|
||||
void Track_Asso:: model_filter(std::vector <Trust_Track> *trust_track, long long latest_timestamp)
|
||||
{
|
||||
|
||||
|
||||
@@ -492,17 +490,17 @@ void Track_Asso:: model_filter(std::vector <Trust_Track> *trust_track,struct Ra
|
||||
|
||||
//计算统计距离d
|
||||
double d1,d2,d3;
|
||||
IMM_d_cal(v_track,X1,P1,X2,P2, X3,P3,Z,prt,freq_ind,delta_T,&d1,&d2,&d3,Work_Parameter);
|
||||
IMM_d_cal(v_track,X1,P1,X2,P2, X3,P3,Z,prt,freq_ind,delta_T,&d1,&d2,&d3);
|
||||
|
||||
//判定统计距离门限,thd_d
|
||||
bool thd_d = false;
|
||||
|
||||
if( (r_track <= 500 && \
|
||||
(d1*d1 < ASSO_THORD*ASSO_THORD || d2*d2 < ASSO_THORD*ASSO_THORD || d3*d3 < ASSO_THORD*ASSO_THORD / 1000.0)) || \
|
||||
(d1*d1 < rdp_parameter.associate_threshold*rdp_parameter.associate_threshold || d2*d2 < rdp_parameter.associate_threshold*rdp_parameter.associate_threshold || d3*d3 < rdp_parameter.associate_threshold*rdp_parameter.associate_threshold / 1000.0)) || \
|
||||
((r_track > 500 && r_track < 1000) && \
|
||||
(d1*d1 < ASSO_THORD*ASSO_THORD || d2*d2 < ASSO_THORD*ASSO_THORD || d3*d3 < ASSO_THORD*ASSO_THORD / 100.0)) || \
|
||||
(d1*d1 < rdp_parameter.associate_threshold*rdp_parameter.associate_threshold || d2*d2 < rdp_parameter.associate_threshold*rdp_parameter.associate_threshold || d3*d3 < rdp_parameter.associate_threshold*rdp_parameter.associate_threshold / 100.0)) || \
|
||||
(r_track >= 1000 && \
|
||||
(d1*d1 < ASSO_THORD*ASSO_THORD || d2*d2 < ASSO_THORD*ASSO_THORD || d3*d3 < ASSO_THORD*ASSO_THORD))\
|
||||
(d1*d1 < rdp_parameter.associate_threshold*rdp_parameter.associate_threshold || d2*d2 < rdp_parameter.associate_threshold*rdp_parameter.associate_threshold || d3*d3 < rdp_parameter.associate_threshold*rdp_parameter.associate_threshold))\
|
||||
){
|
||||
thd_d = true;
|
||||
}
|
||||
@@ -623,7 +621,7 @@ void Track_Asso:: model_filter(std::vector <Trust_Track> *trust_track,struct Ra
|
||||
double v_track = sqrt(pow((*trust_track)[track_index-1].X[1],2)+pow((*trust_track)[track_index-1].X[4],2));
|
||||
double delta_T = (point_process[point_index-1].CPI_time - (*trust_track)[track_index-1].T_track)/1000.0;
|
||||
double F[6][6], Q1[6][6], Q2[6][6], Q3[6][6];
|
||||
IMM_F_Q_gen( v_track, delta_T,F, Q1, Q2, Q3,Work_Parameter);
|
||||
IMM_F_Q_gen( v_track, delta_T,F, Q1, Q2, Q3);
|
||||
|
||||
if (delta_T > 0)
|
||||
{
|
||||
@@ -632,9 +630,9 @@ void Track_Asso:: model_filter(std::vector <Trust_Track> *trust_track,struct Ra
|
||||
double X1_filter[6],X2_filter[6],X3_filter[6],P1_filter[6][6],P2_filter[6][6],P3_filter[6][6];
|
||||
double S1[3][3] = {{0}},S2[3][3] = {{0}},S3[3][3] = {{0}};
|
||||
kalman Kalman;
|
||||
Kalman.kalman_filter_EKF(F,Q1,X1,P1,Z,X1_filter,P1_filter,S1, prt, freq_ind );
|
||||
Kalman.kalman_filter_EKF(F,Q2,X2,P2,Z,X2_filter,P2_filter,S2, prt, freq_ind );
|
||||
Kalman.kalman_filter_EKF(F,Q3,X3,P3,Z,X3_filter,P3_filter,S3, prt, freq_ind );
|
||||
Kalman.kalman_filter_EKF(F,Q1,X1,P1,Z,X1_filter,P1_filter,S1, prt, freq_ind);
|
||||
Kalman.kalman_filter_EKF(F,Q2,X2,P2,Z,X2_filter,P2_filter,S2, prt, freq_ind);
|
||||
Kalman.kalman_filter_EKF(F,Q3,X3,P3,Z,X3_filter,P3_filter,S3, prt, freq_ind);
|
||||
|
||||
Matrix3f S1_out,S2_out,S3_out;
|
||||
for (int ii=0;ii<3;ii++)
|
||||
@@ -690,9 +688,9 @@ void Track_Asso:: model_filter(std::vector <Trust_Track> *trust_track,struct Ra
|
||||
double d_tmp = exp(-associated_info[min_index-1].d_min);
|
||||
double snr_tmp = 1/(1+exp(-(point_process[point_index-1].SNR - 16) / 1.2));
|
||||
double len_tmp = 1/(1+exp(-((*trust_track)[track_index-1].Valid_point_sum - 4) / 1.2)) * 0.5 + (1 - (*trust_track)[track_index-1].Valid_point_sum / (*trust_track)[track_index-1].Point_sum) * 0.5;
|
||||
double tq_tmp = TRACK_QUALITY_D_WEIGHT * d_tmp + TRACK_QUALITY_SNR_WEIGHT * snr_tmp + TRACK_QUALITY_LEN_WEIGHT * len_tmp;
|
||||
double tq_tmp = rdp_parameter.track_quality_d_weight * d_tmp + rdp_parameter.track_quality_snr_weight * snr_tmp + rdp_parameter.track_quality_len_weight * len_tmp;
|
||||
|
||||
(*trust_track)[track_index-1].Quality = (*trust_track)[track_index-1].Quality * TRACK_QUALITY_ITER_COEFF + tq_tmp * (1 - TRACK_QUALITY_ITER_COEFF);
|
||||
(*trust_track)[track_index-1].Quality = (*trust_track)[track_index-1].Quality * rdp_parameter.track_quality_iter_coeff + tq_tmp * (1 - rdp_parameter.track_quality_iter_coeff);
|
||||
|
||||
//更新航迹时间
|
||||
(*trust_track)[track_index-1].T_track = point_process[point_index-1].CPI_time;
|
||||
@@ -780,34 +778,16 @@ void Track_Asso:: model_filter(std::vector <Trust_Track> *trust_track,struct Ra
|
||||
if((*trust_track)[i].point_flag == 0 && (*trust_track)[i].manual_tracking_flag == 0) //未关联上点迹的航迹进行外推
|
||||
{
|
||||
double delta_T = 0;
|
||||
// double delta_T = (latest_timestamp - (*trust_track)[i].T_track)/1000.0;
|
||||
// if (delta_T <= 0)
|
||||
// continue;
|
||||
|
||||
if(Work_Parameter.work_mode == 0)
|
||||
{
|
||||
delta_T = Work_Parameter.DATA_RATE_SHORT;
|
||||
}
|
||||
else if(Work_Parameter.work_mode == 1)
|
||||
{
|
||||
delta_T = Work_Parameter.DATA_RATE_MIDDLE;
|
||||
}
|
||||
else if(Work_Parameter.work_mode == 2)
|
||||
{
|
||||
delta_T = Work_Parameter.DATA_RATE_FAR;
|
||||
}
|
||||
else
|
||||
{
|
||||
delta_T = Work_Parameter.DATA_RATE_SHORT;
|
||||
}
|
||||
delta_T = rdp_parameter.data_rate_tws;
|
||||
|
||||
//外推点数未到上限,则继续外推,否则不外推
|
||||
if((*trust_track)[i].Extrapolate_round <= MAX_EXTRAPOLATE_NUM)
|
||||
if((*trust_track)[i].Extrapolate_round <= rdp_parameter.extrapolate_max)
|
||||
{
|
||||
double v_track = sqrt(pow((*trust_track)[i].X[1],2)+pow((*trust_track)[i].X[4],2));
|
||||
|
||||
double F[6][6], Q1[6][6], Q2[6][6], Q3[6][6];
|
||||
IMM_F_Q_gen( v_track, delta_T, F, Q1, Q2, Q3,Work_Parameter);
|
||||
IMM_F_Q_gen( v_track, delta_T, F, Q1, Q2, Q3);
|
||||
|
||||
//航迹X P
|
||||
double X1[6],X2[6],X3[6],P1[6][6],P2[6][6],P3[6][6];
|
||||
@@ -845,10 +825,10 @@ void Track_Asso:: model_filter(std::vector <Trust_Track> *trust_track,struct Ra
|
||||
|
||||
//航迹质量递减
|
||||
double t = (*trust_track)[i].Unupdated_time / 1000.0;
|
||||
(*trust_track)[i].Quality -= t * t * TRACK_QUALITY_DECREASE_COEFF_O2 + t * TRACK_QUALITY_DECREASE_COEFF_O1;
|
||||
(*trust_track)[i].Quality -= t * t * rdp_parameter.track_quality_decrease_coeff_o2 + t * rdp_parameter.track_quality_decrease_coeff_o1;
|
||||
(*trust_track)[i].Unupdated_time += static_cast<long long>(delta_T*1000.0);
|
||||
t = (*trust_track)[i].Unupdated_time / 1000.0;
|
||||
(*trust_track)[i].Quality += t * t * TRACK_QUALITY_DECREASE_COEFF_O2 + t * TRACK_QUALITY_DECREASE_COEFF_O1;
|
||||
(*trust_track)[i].Quality += t * t * rdp_parameter.track_quality_decrease_coeff_o2 + t * rdp_parameter.track_quality_decrease_coeff_o1;
|
||||
(*trust_track)[i].Quality = (*trust_track)[i].Quality < 0.0 ? 0.0 : (*trust_track)[i].Quality;
|
||||
(*trust_track)[i].Point_sum = (*trust_track)[i].Point_sum + 1;
|
||||
|
||||
@@ -965,7 +945,7 @@ void Track_Asso:: model_output(std::vector <Trust_Track> *trust_track)
|
||||
//高度维更新
|
||||
void Track_Asso:: track_hight_update(int updata_track_index, //更新的航迹号
|
||||
int asso_point_index, //点迹号
|
||||
std::vector <Trust_Track> *trust_track //航迹
|
||||
std::vector <Trust_Track> *trust_track //航迹
|
||||
)
|
||||
{
|
||||
|
||||
@@ -978,19 +958,19 @@ void Track_Asso:: track_hight_update(int updata_
|
||||
|
||||
if((*trust_track)[updata_track_index-1].range_point <= 1000)
|
||||
{
|
||||
height_win_length = H_F_WIN_LEN+2;
|
||||
height_win_length = rdp_parameter.height_smooth_win+2;
|
||||
}
|
||||
else if((*trust_track)[updata_track_index-1].range_point <= 2000 && (*trust_track)[updata_track_index-1].range_point > 1000)
|
||||
{
|
||||
height_win_length = H_F_WIN_LEN+3;
|
||||
height_win_length = rdp_parameter.height_smooth_win+3;
|
||||
}
|
||||
else if((*trust_track)[updata_track_index-1].range_point <= 4000 && (*trust_track)[updata_track_index-1].range_point > 2000)
|
||||
{
|
||||
height_win_length = H_F_WIN_LEN+4;
|
||||
height_win_length = rdp_parameter.height_smooth_win+4;
|
||||
}
|
||||
else
|
||||
{
|
||||
height_win_length = H_F_WIN_LEN+6;
|
||||
height_win_length = rdp_parameter.height_smooth_win+6;
|
||||
}
|
||||
|
||||
double sum=0;
|
||||
|
||||
@@ -16,8 +16,7 @@ public:
|
||||
std::vector <Trust_Track> *trust_track, //航迹文件
|
||||
struct Track Trust_Track_Output[MAX_TRACK_NUM][10], //更新航迹信息
|
||||
int *Trust_track_num_Output, //更新航迹数
|
||||
long long latest_timestamp, //最新时间戳
|
||||
struct RadarPara Work_Parameter //工作参数
|
||||
long long latest_timestamp //最新时间戳
|
||||
);
|
||||
|
||||
Track_Asso();
|
||||
@@ -31,7 +30,6 @@ private:
|
||||
void model_interaction(std::vector <Trust_Track> *trust_track); //模型交互
|
||||
|
||||
void model_filter(std::vector <Trust_Track> *trust_track, //滤波
|
||||
struct RadarPara Work_Parameter,
|
||||
long long latest_timestamp
|
||||
);
|
||||
|
||||
@@ -43,17 +41,16 @@ private:
|
||||
double X3[6], double P3[6][6],
|
||||
double Z[3], double prt,double freq_ind,
|
||||
double delta_T,
|
||||
double *d1,double *d2,double *d3,
|
||||
struct RadarPara Work_Parameter);
|
||||
double *d1,double *d2,double *d3);
|
||||
|
||||
void IMM_F_Q_gen(double v_track, double delta_T,double F[6][6], double Q1[6][6], double Q2[6][6], double Q3[6][6],struct RadarPara Work_Parameter);
|
||||
void IMM_F_Q_gen(double v_track, double delta_T,double F[6][6], double Q1[6][6], double Q2[6][6], double Q3[6][6]);
|
||||
|
||||
double Pt[3][3]; //模型转移概率
|
||||
|
||||
//高度维更新
|
||||
void track_hight_update(int updata_track_index, //更新的航迹号
|
||||
int asso_point_index, //点迹号
|
||||
std::vector <Trust_Track> *trust_track //航迹
|
||||
std::vector <Trust_Track> *trust_track //航迹
|
||||
);
|
||||
};
|
||||
#endif // TRACK_ASSO_H
|
||||
|
||||
@@ -11,8 +11,7 @@ using namespace std;
|
||||
int Track_Asso_Direct_Tracking::track_asso_process_direct_tracking(std::vector<PointRecv> *point_recv, //点迹
|
||||
Trust_Track *trust_track, //航迹
|
||||
struct Track Trust_Track_Output[MAX_TRACK_NUM][10], //更新航迹信息
|
||||
int *Trust_track_num_Output, //更新航迹数
|
||||
struct RadarPara Work_Parameter //工作参数
|
||||
int *Trust_track_num_Output //更新航迹数
|
||||
)
|
||||
{
|
||||
|
||||
@@ -25,7 +24,7 @@ int Track_Asso_Direct_Tracking::track_asso_process_direct_tracking(std::vector<P
|
||||
//IMM算法
|
||||
model_interaction(trust_track);
|
||||
|
||||
model_filter(trust_track, Work_Parameter);
|
||||
model_filter(trust_track);
|
||||
|
||||
model_output(trust_track);
|
||||
|
||||
@@ -38,8 +37,8 @@ int Track_Asso_Direct_Tracking::track_asso_process_direct_tracking(std::vector<P
|
||||
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].Point_Sum=1;
|
||||
double x,y;
|
||||
x=(*trust_track).X[0]+(*trust_track).X[1]*Work_Parameter.Sys_delay;
|
||||
y=(*trust_track).X[3]+(*trust_track).X[4]*Work_Parameter.Sys_delay;
|
||||
x=(*trust_track).X[0]+(*trust_track).X[1]*rdp_parameter.radar_system_delay;
|
||||
y=(*trust_track).X[3]+(*trust_track).X[4]*rdp_parameter.radar_system_delay;
|
||||
coor_trans Coor_trans;
|
||||
double r, azi;
|
||||
Coor_trans.cart2polar(x, y, &r, &azi);
|
||||
@@ -58,7 +57,7 @@ int Track_Asso_Direct_Tracking::track_asso_process_direct_tracking(std::vector<P
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].Track_Mode=(*trust_track).Track_Mode;
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].track_time=(*trust_track).T_track/1000.0;
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].Flag_Point = (*trust_track).point_flag;
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].z=(*trust_track).Height+Work_Parameter.Height;
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].z=(*trust_track).Height+rdp_parameter.radar_height;
|
||||
|
||||
double Direction_Angle;
|
||||
Direction_Angle=atan2((*trust_track).X[4], (*trust_track).X[1]);
|
||||
@@ -185,8 +184,7 @@ void Track_Asso_Direct_Tracking::model_interaction(Trust_Track *trust_track)
|
||||
|
||||
}
|
||||
|
||||
void Track_Asso_Direct_Tracking::model_filter(Trust_Track *trust_track, //滤波
|
||||
struct RadarPara Work_Parameter)
|
||||
void Track_Asso_Direct_Tracking::model_filter(Trust_Track *trust_track) //滤波
|
||||
{
|
||||
//存储关联信息
|
||||
struct asso_info
|
||||
@@ -234,7 +232,7 @@ void Track_Asso_Direct_Tracking::model_filter(Trust_Track *trust_track,
|
||||
|
||||
//计算统计距离d
|
||||
double d1,d2,d3;
|
||||
IMM_d_cal(v_track,X1,P1,X2,P2, X3,P3,Z,prt,freq_ind,delta_T,&d1,&d2,&d3,Work_Parameter);
|
||||
IMM_d_cal(v_track,X1,P1,X2,P2, X3,P3,Z,prt,freq_ind,delta_T,&d1,&d2,&d3);
|
||||
|
||||
//计算高度门限
|
||||
double d_h;
|
||||
@@ -290,7 +288,7 @@ void Track_Asso_Direct_Tracking::model_filter(Trust_Track *trust_track,
|
||||
}
|
||||
|
||||
//小于关联门限 保存关联信息
|
||||
if((d1*d1<ASSO_THORD*ASSO_THORD || d2*d2<ASSO_THORD*ASSO_THORD || d3*d3<ASSO_THORD*ASSO_THORD)
|
||||
if((d1*d1<rdp_parameter.associate_threshold*rdp_parameter.associate_threshold || d2*d2<rdp_parameter.associate_threshold*rdp_parameter.associate_threshold || d3*d3<rdp_parameter.associate_threshold*rdp_parameter.associate_threshold)
|
||||
&& d_h == 1 )
|
||||
{
|
||||
struct asso_info associated_info_tmp;
|
||||
@@ -337,7 +335,7 @@ void Track_Asso_Direct_Tracking::model_filter(Trust_Track *trust_track,
|
||||
// Z[1]=point_process[point_index-1].Range*sin(point_process[point_index-1].Azimuth);
|
||||
double delta_T = (point_process[point_index-1].CPI_time - T_track)/1000.0;
|
||||
double F[6][6], Q1[6][6], Q2[6][6], Q3[6][6];
|
||||
IMM_F_Q_gen( v_track, delta_T,F, Q1, Q2, Q3,Work_Parameter);
|
||||
IMM_F_Q_gen( v_track, delta_T,F, Q1, Q2, Q3);
|
||||
|
||||
double X1_filter[6],X2_filter[6],X3_filter[6],P1_filter[6][6],P2_filter[6][6],P3_filter[6][6];
|
||||
double S1[3][3] = {{0}},S2[3][3] = {{0}},S3[3][3] = {{0}};
|
||||
@@ -345,9 +343,9 @@ void Track_Asso_Direct_Tracking::model_filter(Trust_Track *trust_track,
|
||||
// Kalman.kalman_filter(F,Q1,X1,P1,Z,X1_filter,P1_filter,S1);
|
||||
// Kalman.kalman_filter(F,Q2,X2,P2,Z,X2_filter,P2_filter,S2);
|
||||
// Kalman.kalman_filter(F,Q3,X3,P3,Z,X3_filter,P3_filter,S3);
|
||||
Kalman.kalman_filter_EKF(F,Q1,X1,P1,Z,X1_filter,P1_filter,S1, prt, freq_ind );
|
||||
Kalman.kalman_filter_EKF(F,Q2,X2,P2,Z,X2_filter,P2_filter,S2, prt, freq_ind );
|
||||
Kalman.kalman_filter_EKF(F,Q3,X3,P3,Z,X3_filter,P3_filter,S3, prt, freq_ind );
|
||||
Kalman.kalman_filter_EKF(F,Q1,X1,P1,Z,X1_filter,P1_filter,S1, prt, freq_ind);
|
||||
Kalman.kalman_filter_EKF(F,Q2,X2,P2,Z,X2_filter,P2_filter,S2, prt, freq_ind);
|
||||
Kalman.kalman_filter_EKF(F,Q3,X3,P3,Z,X3_filter,P3_filter,S3, prt, freq_ind);
|
||||
Matrix3f S1_out,S2_out,S3_out;
|
||||
for (int ii=0;ii<3;ii++)
|
||||
for (int jj=0;jj<3;jj++)
|
||||
@@ -424,9 +422,9 @@ void Track_Asso_Direct_Tracking::model_filter(Trust_Track *trust_track,
|
||||
//未关联上,航迹外推
|
||||
else
|
||||
{
|
||||
double delta_T = Work_Parameter.DATA_RATE_TAS/1000.0;
|
||||
double delta_T = rdp_parameter.data_rate_tas/1000.0;
|
||||
double F[6][6], Q1[6][6], Q2[6][6], Q3[6][6];
|
||||
IMM_F_Q_gen( v_track, delta_T, F, Q1, Q2, Q3,Work_Parameter);
|
||||
IMM_F_Q_gen( v_track, delta_T, F, Q1, Q2, Q3);
|
||||
|
||||
double X1_pred[6],X2_pred[6],X3_pred[6],P1_pred[6][6],P2_pred[6][6],P3_pred[6][6];
|
||||
kalman Kalman;
|
||||
@@ -510,7 +508,7 @@ void Track_Asso_Direct_Tracking::model_output(Trust_Track *trust_track)
|
||||
}
|
||||
|
||||
// 产生 F Q 矩阵
|
||||
void Track_Asso_Direct_Tracking::IMM_F_Q_gen(double v_track, double delta_T,double F[6][6], double Q1[6][6], double Q2[6][6], double Q3[6][6],struct RadarPara Work_Parameter)
|
||||
void Track_Asso_Direct_Tracking::IMM_F_Q_gen(double v_track, double delta_T,double F[6][6], double Q1[6][6], double Q2[6][6], double Q3[6][6])
|
||||
{
|
||||
|
||||
//F
|
||||
@@ -538,7 +536,7 @@ void Track_Asso_Direct_Tracking::IMM_F_Q_gen(double v_track, double delta_T,doub
|
||||
|
||||
//模型1
|
||||
//Q
|
||||
double q1=Work_Parameter.Model1_Q_slow;
|
||||
double q1=rdp_parameter.model_1_Q_slow;
|
||||
double q111=1/(2*pow(alpha,5))*(1-exp(-2*alpha*delta_T)+2*alpha*delta_T+2*pow(alpha,3)*pow(delta_T,3)/3-2*pow(alpha,2)*pow(delta_T,2)-4*alpha*delta_T*exp(-alpha*delta_T));
|
||||
double q121=1/(2*pow(alpha,4))*(exp(-2*alpha*delta_T)+1-2*exp(-alpha*delta_T)+2*alpha*delta_T*exp(-alpha*delta_T)-2*alpha*delta_T+pow(alpha,2)*pow(delta_T,2));
|
||||
double q131=1/(2*pow(alpha,3))*(1-exp(-2*alpha*delta_T)-2*alpha*delta_T*exp(-alpha*delta_T));
|
||||
@@ -559,15 +557,15 @@ void Track_Asso_Direct_Tracking::IMM_F_Q_gen(double v_track, double delta_T,doub
|
||||
double q2;
|
||||
if(v_track<=5)
|
||||
{
|
||||
q2=Work_Parameter.Model2_Q_slow/20;
|
||||
q2=rdp_parameter.model_2_Q_slow/20;
|
||||
}
|
||||
else if(v_track<=100&&v_track>50)
|
||||
{
|
||||
q2=10.0*Work_Parameter.Model2_Q_fast;
|
||||
q2=10.0*rdp_parameter.model_2_Q_fast;
|
||||
}
|
||||
else
|
||||
{
|
||||
q2=Work_Parameter.Model2_Q_fast;
|
||||
q2=rdp_parameter.model_2_Q_fast;
|
||||
}
|
||||
|
||||
double q112=1/(2*pow(alpha,5))*(1-exp(-2*alpha*delta_T)+2*alpha*delta_T+2*pow(alpha,3)*pow(delta_T,3)/3-2*pow(alpha,2)*pow(delta_T,2)-4*alpha*delta_T*exp(-alpha*delta_T));
|
||||
@@ -591,15 +589,15 @@ void Track_Asso_Direct_Tracking::IMM_F_Q_gen(double v_track, double delta_T,doub
|
||||
double q3;
|
||||
if(v_track<=5)
|
||||
{
|
||||
q3=Work_Parameter.Model3_Q_slow/20;
|
||||
q3=rdp_parameter.model_3_Q_slow/20;
|
||||
}
|
||||
else if(v_track<=100&&v_track>50)
|
||||
{
|
||||
q3=10.0*Work_Parameter.Model3_Q_fast;
|
||||
q3=10.0*rdp_parameter.model_3_Q_fast;
|
||||
}
|
||||
else
|
||||
{
|
||||
q3=Work_Parameter.Model3_Q_fast;
|
||||
q3=rdp_parameter.model_3_Q_fast;
|
||||
}
|
||||
double q113=1/(2*pow(alpha,5))*(1-exp(-2*alpha*delta_T)+2*alpha*delta_T+2*pow(alpha,3)*pow(delta_T,3)/3-2*pow(alpha,2)*pow(delta_T,2)-4*alpha*delta_T*exp(-alpha*delta_T));
|
||||
double q123=1/(2*pow(alpha,4))*(exp(-2*alpha*delta_T)+1-2*exp(-alpha*delta_T)+2*alpha*delta_T*exp(-alpha*delta_T)-2*alpha*delta_T+pow(alpha,2)*pow(delta_T,2));
|
||||
@@ -626,11 +624,10 @@ void Track_Asso_Direct_Tracking::IMM_d_cal(double v_track,
|
||||
double X3[6], double P3[6][6],
|
||||
double Z[3], double prt,double freq_ind,
|
||||
double delta_T,
|
||||
double *d1,double *d2,double *d3,
|
||||
struct RadarPara Work_Parameter)
|
||||
double *d1,double *d2,double *d3)
|
||||
{
|
||||
double F[6][6], Q1[6][6], Q2[6][6], Q3[6][6];
|
||||
IMM_F_Q_gen( v_track, delta_T,F, Q1, Q2, Q3,Work_Parameter);
|
||||
IMM_F_Q_gen( v_track, delta_T,F, Q1, Q2, Q3);
|
||||
|
||||
kalman Kalman;
|
||||
// *d1=Kalman.d_cal_with_doppler(F,Q1,Z,X1,P1,v_point,prt,freq_ind);
|
||||
@@ -644,7 +641,7 @@ void Track_Asso_Direct_Tracking::IMM_d_cal(double v_track,
|
||||
|
||||
//高度维更新
|
||||
void Track_Asso_Direct_Tracking::track_hight_update(int asso_point_index, //点迹号
|
||||
Trust_Track *trust_track //航迹
|
||||
Trust_Track *trust_track //航迹
|
||||
)
|
||||
{
|
||||
|
||||
@@ -653,19 +650,19 @@ void Track_Asso_Direct_Tracking::track_hight_update(int asso_point
|
||||
int height_win_length;
|
||||
if((*trust_track).range_point <= 1000)
|
||||
{
|
||||
height_win_length = H_F_WIN_LEN+2;
|
||||
height_win_length = rdp_parameter.height_smooth_win+2;
|
||||
}
|
||||
else if((*trust_track).range_point <= 2000 && (*trust_track).range_point > 1000)
|
||||
{
|
||||
height_win_length = H_F_WIN_LEN+3;
|
||||
height_win_length = rdp_parameter.height_smooth_win+3;
|
||||
}
|
||||
else if((*trust_track).range_point <= 4000 && (*trust_track).range_point > 2000)
|
||||
{
|
||||
height_win_length = H_F_WIN_LEN+4;
|
||||
height_win_length = rdp_parameter.height_smooth_win+4;
|
||||
}
|
||||
else
|
||||
{
|
||||
height_win_length = H_F_WIN_LEN+6;
|
||||
height_win_length = rdp_parameter.height_smooth_win+6;
|
||||
}
|
||||
|
||||
double sum=0;
|
||||
|
||||
@@ -12,8 +12,7 @@ class Track_Asso_Direct_Tracking
|
||||
int track_asso_process_direct_tracking(std::vector<PointRecv> *point_recv, //点迹
|
||||
Trust_Track *trust_track, //航迹
|
||||
struct Track Trust_Track_Output[MAX_TRACK_NUM][10], //更新航迹信息
|
||||
int *Trust_track_num_Output, //更新航迹数
|
||||
struct RadarPara Work_Parameter //工作参数
|
||||
int *Trust_track_num_Output //更新航迹数
|
||||
);
|
||||
|
||||
Track_Asso_Direct_Tracking();
|
||||
@@ -27,8 +26,7 @@ class Track_Asso_Direct_Tracking
|
||||
//IMM
|
||||
void model_interaction(Trust_Track *trust_track); //模型交互
|
||||
|
||||
void model_filter(Trust_Track *trust_track, //滤波
|
||||
struct RadarPara Work_Parameter
|
||||
void model_filter(Trust_Track *trust_track //滤波
|
||||
);
|
||||
|
||||
void model_output(Trust_Track *trust_track); //模型输出
|
||||
@@ -39,16 +37,15 @@ class Track_Asso_Direct_Tracking
|
||||
double X3[6], double P3[6][6],
|
||||
double Z[3], double prt,double freq_ind,
|
||||
double delta_T,
|
||||
double *d1,double *d2,double *d3,
|
||||
struct RadarPara Work_Parameter);
|
||||
double *d1,double *d2,double *d3);
|
||||
|
||||
void IMM_F_Q_gen(double v_track, double delta_T,double F[6][6], double Q1[6][6], double Q2[6][6], double Q3[6][6],struct RadarPara Work_Parameter);
|
||||
void IMM_F_Q_gen(double v_track, double delta_T,double F[6][6], double Q1[6][6], double Q2[6][6], double Q3[6][6]);
|
||||
|
||||
double Pt[3][3]; //模型转移概率
|
||||
|
||||
//高度维更新
|
||||
void track_hight_update(int asso_point_index, //点迹号
|
||||
Trust_Track *trust_track //航迹
|
||||
Trust_Track *trust_track //航迹
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@ int Track_Asso_Tas::track_asso_process_tas(std::vector<PointRecv>
|
||||
std::vector<Trust_Track> *trust_track, //航迹文件
|
||||
struct Track Trust_Track_Output[MAX_TRACK_NUM][10], //更新航迹信息
|
||||
int *Trust_track_num_Output, //更新航迹数
|
||||
struct RadarPara Work_Parameter, //工作参数
|
||||
int tas_track_idx,
|
||||
long long latest_timestamp //最新时间戳
|
||||
)
|
||||
@@ -32,7 +31,7 @@ long long latest_timestamp //最新时间戳
|
||||
//IMM算法
|
||||
model_interaction(trust_track,tas_track_idx);
|
||||
|
||||
model_filter(trust_track, Work_Parameter,tas_track_idx, latest_timestamp);
|
||||
model_filter(trust_track,tas_track_idx, latest_timestamp);
|
||||
|
||||
model_output(trust_track,tas_track_idx);
|
||||
|
||||
@@ -54,8 +53,8 @@ long long latest_timestamp //最新时间戳
|
||||
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].Point_Sum=1;
|
||||
double x,y;
|
||||
x=(*trust_track)[i].X[0]+(*trust_track)[i].X[1]*Work_Parameter.Sys_delay;
|
||||
y=(*trust_track)[i].X[3]+(*trust_track)[i].X[4]*Work_Parameter.Sys_delay;
|
||||
x=(*trust_track)[i].X[0]+(*trust_track)[i].X[1]*rdp_parameter.radar_system_delay;
|
||||
y=(*trust_track)[i].X[3]+(*trust_track)[i].X[4]*rdp_parameter.radar_system_delay;
|
||||
coor_trans Coor_trans;
|
||||
double r, azi;
|
||||
Coor_trans.cart2polar(x, y, &r, &azi);
|
||||
@@ -75,7 +74,7 @@ long long latest_timestamp //最新时间戳
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].track_time=static_cast<float>((*trust_track)[i].T_track/1000.0);
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].GNSS_time=(*trust_track)[i].GNSS_time;
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].Flag_Point = (*trust_track)[i].point_flag;
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].z=static_cast<float>((*trust_track)[i].Height+Work_Parameter.Height);
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][0].z=static_cast<float>((*trust_track)[i].Height+rdp_parameter.radar_height);
|
||||
|
||||
double Direction_Angle;
|
||||
Direction_Angle=atan2((*trust_track)[i].X[4], (*trust_track)[i].X[1]);
|
||||
@@ -256,7 +255,6 @@ void Track_Asso_Tas:: model_interaction( std::vector<Trust_Track> *trust_track
|
||||
}
|
||||
|
||||
void Track_Asso_Tas::model_filter( std::vector<Trust_Track> *trust_track, //滤波
|
||||
struct RadarPara Work_Parameter,
|
||||
int tas_track_idx,
|
||||
long long latest_timestamp //最新时间戳
|
||||
)
|
||||
@@ -314,7 +312,7 @@ void Track_Asso_Tas::model_filter( std::vector<Trust_Track> *t
|
||||
double freq_ind = point_process[loop_of_point].freq_index;
|
||||
double delta_T = (point_process[loop_of_point].CPI_time - T_track)/1000.0;
|
||||
if (delta_T <= 0){
|
||||
RDP_LOG << "Warning: delta_T <= 0, skipping point association for point index " << point_process[loop_of_point].CPI_time << " " << T_track << std::endl;
|
||||
RDP_LOG_WARN("Warning: delta_T <= 0, skipping point association for point index {} {}", point_process[loop_of_point].CPI_time, T_track);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -322,12 +320,12 @@ void Track_Asso_Tas::model_filter( std::vector<Trust_Track> *t
|
||||
|
||||
//计算统计距离d
|
||||
double d1,d2,d3;
|
||||
IMM_d_cal(v_track,X1,P1,X2,P2, X3,P3,Z,prt,freq_ind,delta_T,&d1,&d2,&d3,Work_Parameter);
|
||||
IMM_d_cal(v_track,X1,P1,X2,P2, X3,P3,Z,prt,freq_ind,delta_T,&d1,&d2,&d3);
|
||||
|
||||
//判定统计距离门限,thd_d
|
||||
bool thd_d = false;
|
||||
|
||||
if(d1*d1<ASSO_THORD*ASSO_THORD || d2*d2<ASSO_THORD*ASSO_THORD || d3*d3<ASSO_THORD*ASSO_THORD){
|
||||
if(d1*d1<rdp_parameter.associate_threshold*rdp_parameter.associate_threshold || d2*d2<rdp_parameter.associate_threshold*rdp_parameter.associate_threshold || d3*d3<rdp_parameter.associate_threshold*rdp_parameter.associate_threshold){
|
||||
thd_d = true;
|
||||
}
|
||||
|
||||
@@ -388,7 +386,7 @@ void Track_Asso_Tas::model_filter( std::vector<Trust_Track> *t
|
||||
//有关联 滤波
|
||||
if (associated_info.size()>0)
|
||||
{
|
||||
//RDP_LOG << "associated point for track index " << tas_track_idx << std::endl;
|
||||
//RDP_LOG_DEBUG("associated point for track index {}", tas_track_idx);
|
||||
// std::cout << "TAS asoooooo222" ;
|
||||
|
||||
//找最近点
|
||||
@@ -418,7 +416,7 @@ void Track_Asso_Tas::model_filter( std::vector<Trust_Track> *t
|
||||
// Z[1]=point_process[point_index-1].Range*sin(point_process[point_index-1].Azimuth);
|
||||
double delta_T = (point_process[point_index-1].CPI_time - T_track)/1000.0;
|
||||
double F[6][6], Q1[6][6], Q2[6][6], Q3[6][6];
|
||||
IMM_F_Q_gen( v_track, delta_T,F, Q1, Q2, Q3,Work_Parameter);
|
||||
IMM_F_Q_gen( v_track, delta_T,F, Q1, Q2, Q3);
|
||||
|
||||
double X1_filter[6],X2_filter[6],X3_filter[6],P1_filter[6][6],P2_filter[6][6],P3_filter[6][6];
|
||||
double S1[3][3] = {{0}},S2[3][3] = {{0}},S3[3][3] = {{0}};
|
||||
@@ -426,9 +424,9 @@ void Track_Asso_Tas::model_filter( std::vector<Trust_Track> *t
|
||||
// Kalman.kalman_filter(F,Q1,X1,P1,Z,X1_filter,P1_filter,S1);
|
||||
// Kalman.kalman_filter(F,Q2,X2,P2,Z,X2_filter,P2_filter,S2);
|
||||
// Kalman.kalman_filter(F,Q3,X3,P3,Z,X3_filter,P3_filter,S3);
|
||||
Kalman.kalman_filter_EKF(F,Q1,X1,P1,Z,X1_filter,P1_filter,S1, prt, freq_ind );
|
||||
Kalman.kalman_filter_EKF(F,Q2,X2,P2,Z,X2_filter,P2_filter,S2, prt, freq_ind );
|
||||
Kalman.kalman_filter_EKF(F,Q3,X3,P3,Z,X3_filter,P3_filter,S3, prt, freq_ind );
|
||||
Kalman.kalman_filter_EKF(F,Q1,X1,P1,Z,X1_filter,P1_filter,S1, prt, freq_ind);
|
||||
Kalman.kalman_filter_EKF(F,Q2,X2,P2,Z,X2_filter,P2_filter,S2, prt, freq_ind);
|
||||
Kalman.kalman_filter_EKF(F,Q3,X3,P3,Z,X3_filter,P3_filter,S3, prt, freq_ind);
|
||||
Matrix3f S1_out,S2_out,S3_out;
|
||||
for (int ii=0;ii<3;ii++)
|
||||
for (int jj=0;jj<3;jj++)
|
||||
@@ -529,17 +527,17 @@ void Track_Asso_Tas::model_filter( std::vector<Trust_Track> *t
|
||||
//未关联上,航迹外推
|
||||
else
|
||||
{
|
||||
//RDP_LOG << "unassociated point for track index " << tas_track_idx << std::endl;
|
||||
//RDP_LOG_DEBUG("unassociated point for track index {}", tas_track_idx);
|
||||
for(size_t i=0;i<trust_track->size();i++){
|
||||
if((*trust_track)[i].Track_Index == tas_track_idx)
|
||||
{
|
||||
if((*trust_track)[i].Extrapolate_round >= MAX_EXTRAPOLATE_NUM_TAS)
|
||||
if((*trust_track)[i].Extrapolate_round >= rdp_parameter.extrapolate_max_tas)
|
||||
{
|
||||
double delta_T = (latest_timestamp - T_track)/1000.0;
|
||||
if (delta_T <= 0)
|
||||
continue;
|
||||
double F[6][6], Q1[6][6], Q2[6][6], Q3[6][6];
|
||||
IMM_F_Q_gen( v_track, delta_T, F, Q1, Q2, Q3,Work_Parameter);
|
||||
IMM_F_Q_gen( v_track, delta_T, F, Q1, Q2, Q3);
|
||||
|
||||
double X1_pred[6],X2_pred[6],X3_pred[6],P1_pred[6][6],P2_pred[6][6],P3_pred[6][6];
|
||||
kalman Kalman;
|
||||
@@ -648,7 +646,7 @@ void Track_Asso_Tas::model_output(std::vector<Trust_Track> *trust_track, int
|
||||
}
|
||||
|
||||
// 产生 F Q 矩阵
|
||||
void Track_Asso_Tas::IMM_F_Q_gen(double v_track, double delta_T,double F[6][6], double Q1[6][6], double Q2[6][6], double Q3[6][6],struct RadarPara Work_Parameter)
|
||||
void Track_Asso_Tas::IMM_F_Q_gen(double v_track, double delta_T,double F[6][6], double Q1[6][6], double Q2[6][6], double Q3[6][6])
|
||||
{
|
||||
|
||||
//F
|
||||
@@ -676,7 +674,7 @@ void Track_Asso_Tas::IMM_F_Q_gen(double v_track, double delta_T,double F[6][6],
|
||||
|
||||
//模型1
|
||||
//Q
|
||||
double q1=Work_Parameter.Model1_Q_slow;
|
||||
double q1=rdp_parameter.model_1_Q_slow;
|
||||
double q111=1/(2*pow(alpha,5))*(1-exp(-2*alpha*delta_T)+2*alpha*delta_T+2*pow(alpha,3)*pow(delta_T,3)/3-2*pow(alpha,2)*pow(delta_T,2)-4*alpha*delta_T*exp(-alpha*delta_T));
|
||||
double q121=1/(2*pow(alpha,4))*(exp(-2*alpha*delta_T)+1-2*exp(-alpha*delta_T)+2*alpha*delta_T*exp(-alpha*delta_T)-2*alpha*delta_T+pow(alpha,2)*pow(delta_T,2));
|
||||
double q131=1/(2*pow(alpha,3))*(1-exp(-2*alpha*delta_T)-2*alpha*delta_T*exp(-alpha*delta_T));
|
||||
@@ -697,15 +695,15 @@ void Track_Asso_Tas::IMM_F_Q_gen(double v_track, double delta_T,double F[6][6],
|
||||
double q2;
|
||||
if(v_track<=5)
|
||||
{
|
||||
q2=Work_Parameter.Model2_Q_slow/20;
|
||||
q2=rdp_parameter.model_2_Q_slow/20;
|
||||
}
|
||||
else if(v_track<=100&&v_track>50)
|
||||
{
|
||||
q2=10.0*Work_Parameter.Model2_Q_fast;
|
||||
q2=10.0*rdp_parameter.model_2_Q_fast;
|
||||
}
|
||||
else
|
||||
{
|
||||
q2=Work_Parameter.Model2_Q_fast;
|
||||
q2=rdp_parameter.model_2_Q_fast;
|
||||
}
|
||||
|
||||
double q112=1/(2*pow(alpha,5))*(1-exp(-2*alpha*delta_T)+2*alpha*delta_T+2*pow(alpha,3)*pow(delta_T,3)/3-2*pow(alpha,2)*pow(delta_T,2)-4*alpha*delta_T*exp(-alpha*delta_T));
|
||||
@@ -729,15 +727,15 @@ Q2[5][3]=q2*q132; Q2[5][4]=q2*q232; Q2[5][5]=q2*q332;
|
||||
double q3;
|
||||
if(v_track<=5)
|
||||
{
|
||||
q3=Work_Parameter.Model3_Q_slow/20;
|
||||
q3=rdp_parameter.model_3_Q_slow/20;
|
||||
}
|
||||
else if(v_track<=100&&v_track>50)
|
||||
{
|
||||
q3=10.0*Work_Parameter.Model3_Q_fast;
|
||||
q3=10.0*rdp_parameter.model_3_Q_fast;
|
||||
}
|
||||
else
|
||||
{
|
||||
q3=Work_Parameter.Model3_Q_fast;
|
||||
q3=rdp_parameter.model_3_Q_fast;
|
||||
}
|
||||
double q113=1/(2*pow(alpha,5))*(1-exp(-2*alpha*delta_T)+2*alpha*delta_T+2*pow(alpha,3)*pow(delta_T,3)/3-2*pow(alpha,2)*pow(delta_T,2)-4*alpha*delta_T*exp(-alpha*delta_T));
|
||||
double q123=1/(2*pow(alpha,4))*(exp(-2*alpha*delta_T)+1-2*exp(-alpha*delta_T)+2*alpha*delta_T*exp(-alpha*delta_T)-2*alpha*delta_T+pow(alpha,2)*pow(delta_T,2));
|
||||
@@ -764,11 +762,10 @@ double X2[6], double P2[6][6],
|
||||
double X3[6], double P3[6][6],
|
||||
double Z[3], double prt,double freq_ind,
|
||||
double delta_T,
|
||||
double *d1,double *d2,double *d3,
|
||||
struct RadarPara Work_Parameter)
|
||||
double *d1,double *d2,double *d3)
|
||||
{
|
||||
double F[6][6], Q1[6][6], Q2[6][6], Q3[6][6];
|
||||
IMM_F_Q_gen( v_track, delta_T,F, Q1, Q2, Q3,Work_Parameter);
|
||||
IMM_F_Q_gen( v_track, delta_T,F, Q1, Q2, Q3);
|
||||
|
||||
kalman Kalman;
|
||||
// *d1=Kalman.d_cal_with_doppler(F,Q1,Z,X1,P1,v_point,prt,freq_ind);
|
||||
@@ -783,7 +780,7 @@ struct RadarPara Work_Parameter)
|
||||
//高度维更新
|
||||
void Track_Asso_Tas::track_hight_update(int tas_track_idx, //更新的航迹号
|
||||
int asso_point_index, //点迹号
|
||||
std::vector<Trust_Track> *trust_track //航迹
|
||||
std::vector<Trust_Track> *trust_track //航迹
|
||||
)
|
||||
{
|
||||
for(size_t i=0; i<trust_track->size(); i++)
|
||||
@@ -794,19 +791,19 @@ void Track_Asso_Tas::track_hight_update(int tas_
|
||||
int height_win_length;
|
||||
if((*trust_track)[i].range_point <= 1000)
|
||||
{
|
||||
height_win_length = H_F_WIN_LEN+6;
|
||||
height_win_length = rdp_parameter.height_smooth_win+6;
|
||||
}
|
||||
else if((*trust_track)[i].range_point <= 2000 && (*trust_track)[i].range_point > 1000)
|
||||
{
|
||||
height_win_length = H_F_WIN_LEN+8;
|
||||
height_win_length = rdp_parameter.height_smooth_win+8;
|
||||
}
|
||||
else if((*trust_track)[i].range_point <= 4000 && (*trust_track)[i].range_point > 2000)
|
||||
{
|
||||
height_win_length = H_F_WIN_LEN+10;
|
||||
height_win_length = rdp_parameter.height_smooth_win+10;
|
||||
}
|
||||
else
|
||||
{
|
||||
height_win_length = H_F_WIN_LEN+12;
|
||||
height_win_length = rdp_parameter.height_smooth_win+12;
|
||||
}
|
||||
|
||||
double sum=0;
|
||||
|
||||
@@ -15,7 +15,6 @@ public:
|
||||
std::vector<Trust_Track> *trust_track, //航迹文件
|
||||
struct Track Trust_Track_Output[MAX_TRACK_NUM][10], //更新航迹信息
|
||||
int *Trust_track_num_Output, //更新航迹数
|
||||
struct RadarPara Work_Parameter, //工作参数
|
||||
int tas_track_idx,
|
||||
long long latest_timestamp //最新时间戳
|
||||
);
|
||||
@@ -31,7 +30,6 @@ private:
|
||||
void model_interaction(std::vector<Trust_Track> *trust_track, int tas_track_idx); //模型交互
|
||||
|
||||
void model_filter(std::vector<Trust_Track> *trust_track, //滤波
|
||||
struct RadarPara Work_Parameter,
|
||||
int tas_track_idx,
|
||||
long long latest_timestamp //最新时间戳
|
||||
);
|
||||
@@ -44,17 +42,16 @@ private:
|
||||
double X3[6], double P3[6][6],
|
||||
double Z[3], double prt,double freq_ind,
|
||||
double delta_T,
|
||||
double *d1,double *d2,double *d3,
|
||||
struct RadarPara Work_Parameter);
|
||||
double *d1,double *d2,double *d3);
|
||||
|
||||
void IMM_F_Q_gen(double v_track, double delta_T,double F[6][6], double Q1[6][6], double Q2[6][6], double Q3[6][6],struct RadarPara Work_Parameter);
|
||||
void IMM_F_Q_gen(double v_track, double delta_T,double F[6][6], double Q1[6][6], double Q2[6][6], double Q3[6][6]);
|
||||
|
||||
double Pt[3][3]; //模型转移概率
|
||||
|
||||
//高度维更新
|
||||
void track_hight_update(int tas_track_index, //更新的航迹号
|
||||
void track_hight_update(int tas_track_idx, //更新的航迹号
|
||||
int asso_point_index, //点迹号
|
||||
std::vector<Trust_Track> *trust_track //航迹
|
||||
std::vector<Trust_Track> *trust_track //航迹
|
||||
);
|
||||
|
||||
};
|
||||
|
||||
@@ -23,13 +23,13 @@ void Track_Die::track_die_process( std::vector <Trust_Track> *trust_track,
|
||||
}
|
||||
|
||||
//非重点航迹自动消亡
|
||||
if((*Iter).Track_Mode == 0 && (*Iter).primary_target_flag == 0 && (*Iter).Quality < TRACK_DIE_QUALITY)
|
||||
if((*Iter).Track_Mode == 0 && (*Iter).primary_target_flag == 0 && (*Iter).Quality < rdp_parameter.track_die_quality)
|
||||
{
|
||||
die_flag = true;
|
||||
}
|
||||
|
||||
//重点航迹自动消亡
|
||||
if((*Iter).Track_Mode == 0 && (*Iter).primary_target_flag == 1 && (*Iter).Extrapolate_round > TRACK_DIE_ROUND_PRIMARY)
|
||||
if((*Iter).Track_Mode == 0 && (*Iter).primary_target_flag == 1 && (*Iter).Extrapolate_round > rdp_parameter.track_die_round_pramary)
|
||||
{
|
||||
die_flag = true;
|
||||
}
|
||||
|
||||
@@ -24,14 +24,14 @@ void Track_Die_Tas::track_die_process_tas( std::vector <Trust_Track> *trust_trac
|
||||
die_flag = true;
|
||||
}
|
||||
|
||||
if((*Iter).Track_Mode == 1 && (*Iter).Extrapolate_round > TRACK_DIE_ROUND_TAS)
|
||||
if((*Iter).Track_Mode == 1 && (*Iter).Extrapolate_round > rdp_parameter.track_die_round_tas)
|
||||
{
|
||||
die_flag = true;
|
||||
}
|
||||
|
||||
if(die_flag)
|
||||
{
|
||||
// RDP_LOG << "remove tas target :" << (*Iter).Track_Index << std::endl;
|
||||
// RDP_LOG_DEBUG("remove tas target :{}", (*Iter).Track_Index);
|
||||
//输出消亡信息
|
||||
*Track_die_num_Output=*Track_die_num_Output+1;
|
||||
Track_die_Index_Output[*Track_die_num_Output-1]=(*Iter).Track_Index;
|
||||
|
||||
@@ -11,7 +11,7 @@ void Track_Ind_Mangement::reset()
|
||||
lastest_index = 0;
|
||||
}
|
||||
|
||||
int Track_Ind_Mangement ::track_ind_get( std::vector<Trust_Track> *trust_track)
|
||||
int Track_Ind_Mangement::track_ind_get( std::vector<Trust_Track> *trust_track)
|
||||
{
|
||||
|
||||
int isempty = 1;
|
||||
@@ -29,18 +29,19 @@ int Track_Ind_Mangement ::track_ind_get( std::vector<Trust_Track> *trust_track
|
||||
else
|
||||
{
|
||||
//建立航迹号列表
|
||||
int List[MAX_TRACK_INDEX]={0};
|
||||
const int track_index_max = rdp_parameter.track_index_max;
|
||||
std::vector<int> List(static_cast<std::size_t>(track_index_max), 0);
|
||||
for (size_t i=0;i<trust_track->size();i++)
|
||||
{
|
||||
int ti = (*trust_track)[i].Track_Index;
|
||||
if (ti >= 1 && ti <= MAX_TRACK_INDEX)
|
||||
if (ti >= 1 && ti <= track_index_max)
|
||||
List[ti-1]=1;
|
||||
}
|
||||
|
||||
//分配航迹号
|
||||
if(lastest_index<MAX_TRACK_INDEX)
|
||||
if(lastest_index<track_index_max)
|
||||
{
|
||||
for (int i=lastest_index+1;i<=MAX_TRACK_INDEX;i++)
|
||||
for (int i=lastest_index+1;i<=track_index_max;i++)
|
||||
{
|
||||
if(List[i-1]==0)
|
||||
{
|
||||
@@ -49,7 +50,7 @@ int Track_Ind_Mangement ::track_ind_get( std::vector<Trust_Track> *trust_track
|
||||
}
|
||||
}
|
||||
|
||||
for (int i=1;i<=MAX_TRACK_INDEX;i++)
|
||||
for (int i=1;i<=track_index_max;i++)
|
||||
{
|
||||
if(List[i-1]==0)
|
||||
{
|
||||
@@ -59,9 +60,9 @@ int Track_Ind_Mangement ::track_ind_get( std::vector<Trust_Track> *trust_track
|
||||
}
|
||||
|
||||
}
|
||||
else if(lastest_index == MAX_TRACK_INDEX)
|
||||
else if(lastest_index == track_index_max)
|
||||
{
|
||||
for (int i=1;i<=MAX_TRACK_INDEX;i++)
|
||||
for (int i=1;i<=track_index_max;i++)
|
||||
{
|
||||
if(List[i-1]==0)
|
||||
{
|
||||
|
||||
@@ -13,8 +13,7 @@ int Track_Init::track_init_process_logic( std::vector <PointRecv>
|
||||
std::vector<Trust_Track> *trust_track, //可靠航迹
|
||||
std::vector <std::vector<Temp_track>> *temp_track,
|
||||
struct Track Trust_Track_Output[MAX_TRACK_NUM][10],
|
||||
int *Trust_track_num_Output,
|
||||
struct RadarPara Work_Parameter
|
||||
int *Trust_track_num_Output
|
||||
)
|
||||
{
|
||||
|
||||
@@ -35,10 +34,10 @@ int Track_Init::track_init_process_logic( std::vector <PointRecv>
|
||||
if(point_process.size()>0)
|
||||
{
|
||||
//点迹与临时航迹关联
|
||||
point_temp_track_asso(temp_track,Work_Parameter);
|
||||
point_temp_track_asso(temp_track);
|
||||
|
||||
//点迹与航迹头关联
|
||||
point_track_head_asso(temp_track,Work_Parameter);
|
||||
point_track_head_asso(temp_track);
|
||||
|
||||
//删除关联上的点迹
|
||||
std::vector <PointRecv>::iterator Iter;
|
||||
@@ -118,7 +117,7 @@ int Track_Init::track_init_process_logic( std::vector <PointRecv>
|
||||
#endif
|
||||
|
||||
//临时航迹满足起始长度 转为可靠航迹
|
||||
tmp_track_to_trust_track(trust_track,temp_track,Trust_Track_Output, Trust_track_num_Output,Work_Parameter);
|
||||
tmp_track_to_trust_track(trust_track,temp_track,Trust_Track_Output, Trust_track_num_Output);
|
||||
|
||||
}
|
||||
|
||||
@@ -129,8 +128,7 @@ int Track_Init::track_init_process_logic( std::vector <PointRecv>
|
||||
|
||||
}
|
||||
|
||||
void Track_Init::point_temp_track_asso(std::vector <std::vector<Temp_track>> *temp_track,
|
||||
struct RadarPara Work_Parameter)
|
||||
void Track_Init::point_temp_track_asso(std::vector <std::vector<Temp_track>> *temp_track)
|
||||
{
|
||||
|
||||
//关联信息
|
||||
@@ -191,7 +189,7 @@ void Track_Init::point_temp_track_asso(std::vector <std::vector<Temp_track>>
|
||||
double y2=Z[0]*sin(Z[1]);
|
||||
double alpha = alpha_cal_track_init(x0,y0,x1,y1,x2,y2);
|
||||
|
||||
if(d*d<=TRACK_START_THRESHOLD*TRACK_START_THRESHOLD && alpha<ALPHA_START && vr_point*v_temp_track>0)// && fabs(h_point-h_temp_track )<= r_point*SIGMA_E&& abs(vr_point-v_temp_track)/abs(v_temp_track)<0.8
|
||||
if(d*d<=rdp_parameter.track_init_threshold*rdp_parameter.track_init_threshold && alpha<rdp_parameter.track_init_angle && vr_point*v_temp_track>0)// && fabs(h_point-h_temp_track )<= r_point*rdp_parameter.sigma_pitch&& abs(vr_point-v_temp_track)/abs(v_temp_track)<0.8
|
||||
{
|
||||
struct Asso_info asso_info_tmp;
|
||||
asso_info_tmp.point_idx = static_cast<int>(i)+1;
|
||||
@@ -278,8 +276,8 @@ void Track_Init::point_temp_track_asso(std::vector <std::vector<Temp_track>>
|
||||
snr_tmp = 1/(1+exp(-(temp_track_ref.back().SNR - 16) / 1.2));
|
||||
}
|
||||
|
||||
quality_tmp = d_tmp * TRACK_QUALITY_D_WEIGHT + snr_tmp * TRACK_QUALITY_SNR_WEIGHT;
|
||||
quality_tmp /= (TRACK_QUALITY_D_WEIGHT + TRACK_QUALITY_SNR_WEIGHT);
|
||||
quality_tmp = d_tmp * rdp_parameter.track_quality_d_weight + snr_tmp * rdp_parameter.track_quality_snr_weight;
|
||||
quality_tmp /= (rdp_parameter.track_quality_d_weight + rdp_parameter.track_quality_snr_weight);
|
||||
|
||||
if(L == 3){
|
||||
for(int i=0;i<L;i++)
|
||||
@@ -287,7 +285,7 @@ void Track_Init::point_temp_track_asso(std::vector <std::vector<Temp_track>>
|
||||
temp_track_ref[i].quality = quality_tmp;
|
||||
}
|
||||
}else{
|
||||
temp_track_ref[L-1].quality = temp_track_ref[L-2].quality * TRACK_QUALITY_ITER_COEFF + quality_tmp * (1 - TRACK_QUALITY_ITER_COEFF);
|
||||
temp_track_ref[L-1].quality = temp_track_ref[L-2].quality * rdp_parameter.track_quality_iter_coeff + quality_tmp * (1 - rdp_parameter.track_quality_iter_coeff);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -314,8 +312,7 @@ void Track_Init::point_temp_track_asso(std::vector <std::vector<Temp_track>>
|
||||
}
|
||||
}
|
||||
|
||||
void Track_Init::point_track_head_asso( std::vector <std::vector<Temp_track>> *temp_track,
|
||||
struct RadarPara Work_Parameter)
|
||||
void Track_Init::point_track_head_asso( std::vector <std::vector<Temp_track>> *temp_track)
|
||||
{
|
||||
|
||||
//关联上的信息
|
||||
@@ -357,26 +354,26 @@ void Track_Init::point_track_head_asso( std::vector <std::vector<Temp_track>>
|
||||
double dis = sqrt(pow(x_point-x_track_head,2)+pow(y_point-y_track_head,2));
|
||||
|
||||
double vmax;
|
||||
if(Work_Parameter.work_mode == 0) //近程模式 最大速度减小一点
|
||||
if(rdp_parameter.radar_work_mode == 0) //近程模式 最大速度减小一点
|
||||
{
|
||||
vmax = Work_Parameter.V_MAX;
|
||||
vmax = rdp_parameter.track_init_velocity_max;
|
||||
}
|
||||
else //中远程模式 最大速度正常用
|
||||
{
|
||||
vmax = Work_Parameter.V_MAX;
|
||||
vmax = rdp_parameter.track_init_velocity_max;
|
||||
}
|
||||
|
||||
double delta_T = (T_point - T_track_head)/1000.0;
|
||||
|
||||
|
||||
// if(
|
||||
// ( (Work_Parameter.work_mode == 0&&( (r_point>=1000 && dis<=vmax*delta_T && dis>=Work_Parameter.V_MIN*delta_T) || (r_point<1000 && dis<=vmax*delta_T/2.0 && dis>=Work_Parameter.V_MIN*delta_T/2.0) ))
|
||||
// || (Work_Parameter.work_mode != 0&&( (r_point>=2000 && dis<=vmax*delta_T && dis>=Work_Parameter.V_MIN*delta_T) || (r_point<2000 && dis<=vmax*delta_T/3.0 && dis>=Work_Parameter.V_MIN*delta_T/2.0) ))
|
||||
// ( (rdp_parameter.radar_work_mode == 0&&( (r_point>=1000 && dis<=vmax*delta_T && dis>=rdp_parameter.track_init_velocity_min*delta_T) || (r_point<1000 && dis<=vmax*delta_T/2.0 && dis>=rdp_parameter.track_init_velocity_min*delta_T/2.0) ))
|
||||
// || (rdp_parameter.radar_work_mode != 0&&( (r_point>=2000 && dis<=vmax*delta_T && dis>=rdp_parameter.track_init_velocity_min*delta_T) || (r_point<2000 && dis<=vmax*delta_T/3.0 && dis>=rdp_parameter.track_init_velocity_min*delta_T/2.0) ))
|
||||
// )
|
||||
// && vr_point*v_track_head>0 )
|
||||
|
||||
//点航关联条件
|
||||
if( dis <= vmax* delta_T && dis >= Work_Parameter.V_MIN * delta_T && \
|
||||
if( dis <= vmax* delta_T && dis >= rdp_parameter.track_init_velocity_min * delta_T && \
|
||||
vr_point*v_track_head>0 && \
|
||||
dis >= abs(vr_point) * delta_T && \
|
||||
dis >= abs(v_track_head) * delta_T
|
||||
@@ -473,8 +470,7 @@ double Track_Init::alpha_cal_track_init(double x0,double y0,double x1,double y1,
|
||||
void Track_Init::tmp_track_to_trust_track(std::vector<Trust_Track> *trust_track,
|
||||
std::vector <std::vector<Temp_track>> *temp_track,
|
||||
struct Track Trust_Track_Output[MAX_TRACK_NUM][10],
|
||||
int *Trust_track_num_Output,
|
||||
struct RadarPara Work_Parameter)
|
||||
int *Trust_track_num_Output)
|
||||
{
|
||||
|
||||
std::vector <std::vector<Temp_track>> Track_to_start;
|
||||
@@ -488,23 +484,6 @@ void Track_Init::tmp_track_to_trust_track(std::vector<Trust_Track> *trust_tra
|
||||
double azi=(*Iter)[L-1].azi/PI*180;
|
||||
double quality = (*Iter)[L-1].quality;
|
||||
|
||||
// //计算航迹初始质量
|
||||
// double average_snr = 0;
|
||||
// for(int i=0;i<L;i++)
|
||||
// {
|
||||
// average_snr += (*Iter)[i].SNR;
|
||||
// }
|
||||
// average_snr /= L;
|
||||
// double snr_tmp = 1/(1+exp(-(average_snr - 16) / 1.2));
|
||||
// double &d = (*Iter)[L-1].d;
|
||||
// double d_tmp = exp(-d);
|
||||
// double quality_tmp = d_tmp * TRACK_QUALITY_D_WEIGHT + snr_tmp * TRACK_QUALITY_SNR_WEIGHT;
|
||||
// quality_tmp /= (TRACK_QUALITY_D_WEIGHT + TRACK_QUALITY_SNR_WEIGHT);
|
||||
// for(int i=0;i<L;i++)
|
||||
// {
|
||||
// (*Iter)[i].quality = quality_tmp;
|
||||
// }
|
||||
|
||||
//航迹起批条件判断
|
||||
bool track_init_flag = true;
|
||||
|
||||
@@ -514,12 +493,12 @@ void Track_Init::tmp_track_to_trust_track(std::vector<Trust_Track> *trust_tra
|
||||
}
|
||||
|
||||
//航迹长度小于起始点数,不起批
|
||||
if(L < Work_Parameter.track_start_point_num){
|
||||
if(L < rdp_parameter.track_init_point_num_min){
|
||||
track_init_flag = false;
|
||||
}
|
||||
|
||||
//航迹起批禁止区域判断
|
||||
if(track_init_prohibit(range,azi,Work_Parameter)!=0)
|
||||
if(track_init_prohibit(range,azi)!=0)
|
||||
{
|
||||
track_init_flag = false;
|
||||
}
|
||||
@@ -527,32 +506,32 @@ void Track_Init::tmp_track_to_trust_track(std::vector<Trust_Track> *trust_tra
|
||||
//航迹质量起批条件判断
|
||||
switch(L){
|
||||
case 3:
|
||||
if(quality < TRACK_START_QUALITY_L3){
|
||||
if(quality < rdp_parameter.track_init_quality_L3){
|
||||
track_init_flag = false;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
if(quality < TRACK_START_QUALITY_L4){
|
||||
if(quality < rdp_parameter.track_init_quality_L4){
|
||||
track_init_flag = false;
|
||||
}
|
||||
break;
|
||||
case 5:
|
||||
if(quality < TRACK_START_QUALITY_L5){
|
||||
if(quality < rdp_parameter.track_init_quality_L5){
|
||||
track_init_flag = false;
|
||||
}
|
||||
break;
|
||||
case 6:
|
||||
if(quality < TRACK_START_QUALITY_L6){
|
||||
if(quality < rdp_parameter.track_init_quality_L6){
|
||||
track_init_flag = false;
|
||||
}
|
||||
break;
|
||||
case 7:
|
||||
if(quality < TRACK_START_QUALITY_L7){
|
||||
if(quality < rdp_parameter.track_init_quality_L7){
|
||||
track_init_flag = false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if(quality < TRACK_START_QUALITY_L7){
|
||||
if(quality < rdp_parameter.track_init_quality_L7){
|
||||
track_init_flag = false;
|
||||
}
|
||||
break;
|
||||
@@ -581,7 +560,7 @@ void Track_Init::tmp_track_to_trust_track(std::vector<Trust_Track> *trust_tra
|
||||
return;
|
||||
|
||||
//2.两两比较Track_to_start中的航迹信息,删除重复的航迹
|
||||
int L = Work_Parameter.track_start_point_num;
|
||||
int L = rdp_parameter.track_init_point_num_min;
|
||||
for (size_t i=0; i+1<Track_to_start.size();i++)
|
||||
{
|
||||
if(Track_to_start[i][0].asso_flag!=2)
|
||||
@@ -632,7 +611,7 @@ void Track_Init::tmp_track_to_trust_track(std::vector<Trust_Track> *trust_tra
|
||||
for (size_t i=0; i<Track_to_start.size();i++)
|
||||
{
|
||||
if (Track_to_start[i].size() < 3) continue;
|
||||
if(trust_track->size()<MAX_TRACK_INDEX)
|
||||
if(trust_track->size() < static_cast<std::size_t>(rdp_parameter.track_index_max))
|
||||
{
|
||||
int index = track_index_mangement.track_ind_get(trust_track);
|
||||
if(index>0)
|
||||
@@ -653,6 +632,9 @@ void Track_Init::tmp_track_to_trust_track(std::vector<Trust_Track> *trust_tra
|
||||
memcpy(trust_track_tmp.X, X, 6*sizeof(double));
|
||||
memcpy(trust_track_tmp.P, P, 6*6*sizeof(double));
|
||||
|
||||
//计算航迹指纹
|
||||
trust_track_tmp.Track_fingerprint = computeFingerprint(Track_to_start[i][L-1].r, Track_to_start[i][L-1].azi, Track_to_start[i][L-1].vr, Track_to_start[i][L-1].RCS, Track_to_start[i][L-1].CPI_time);
|
||||
|
||||
trust_track_tmp.track_id = Track_to_start[i][L-1].temp_track_id;
|
||||
trust_track_tmp.point_id = Track_to_start[i][L-1].point_id;
|
||||
trust_track_tmp.circle_id = Track_to_start[i][L-1].circle_id;
|
||||
@@ -667,7 +649,7 @@ void Track_Init::tmp_track_to_trust_track(std::vector<Trust_Track> *trust_tra
|
||||
trust_track_tmp.Unupdated_time = 0;
|
||||
trust_track_tmp.point_flag=1; //实点
|
||||
trust_track_tmp.Track_Mode=0; //跟踪模式 TWS 0
|
||||
trust_track_tmp.Target_Type=UNCONF_TARGET;
|
||||
trust_track_tmp.Target_Type=0;
|
||||
trust_track_tmp.Track_Index=index;
|
||||
trust_track_tmp.Height=Track_to_start[i][L-1].height;
|
||||
trust_track_tmp.Extrapolate_round=0;
|
||||
@@ -715,7 +697,7 @@ void Track_Init::tmp_track_to_trust_track(std::vector<Trust_Track> *trust_tra
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][j].Amplitude=static_cast<float>(Track_to_start[i][j].amplitude);
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][j].track_snr = static_cast<float>(Track_to_start[i][j].SNR);
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][j].track_rcs = static_cast<float>(Track_to_start[i][j].RCS);
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][j].Quality = static_cast<float>(Track_to_start[i][j].quality * TRACK_QUALITY_MAX);
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][j].Quality = static_cast<float>(Track_to_start[i][j].quality * rdp_parameter.track_quality_max);
|
||||
|
||||
Trust_Track_Output[*Trust_track_num_Output-1][j].pitch_num = Track_to_start[i][j].pitch_num;
|
||||
|
||||
@@ -783,12 +765,12 @@ void Track_Init::tmp_track_die(std::vector <std::vector<Temp_track>> *temp_t
|
||||
}
|
||||
};
|
||||
|
||||
int Track_Init::track_init_prohibit(double r, double azi, struct RadarPara Work_Parameter)
|
||||
int Track_Init::track_init_prohibit(double r, double azi)
|
||||
{
|
||||
|
||||
for (int i=0;i<Work_Parameter.track_prohibite_area_num;i++)
|
||||
for (int i=0;i<rdp_parameter.track_init_prohibite_area_num;i++)
|
||||
{
|
||||
if(r<Work_Parameter.R_max_track_prohibited[i] && r>Work_Parameter.R_min_track_prohibited[i] && azi<Work_Parameter.Azimuth_max_track_prohibited[i] && azi>Work_Parameter.Azimuth_min_track_prohibited[i])
|
||||
if(r<rdp_parameter.track_init_prohibite_area_range_max[i] && r>rdp_parameter.track_init_prohibite_area_range_min[i] && azi<rdp_parameter.track_init_prohibite_area_azimuth_max[i] && azi>rdp_parameter.track_init_prohibite_area_azimuth_min[i])
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
@@ -798,6 +780,51 @@ int Track_Init::track_init_prohibit(double r, double azi, struct RadarPara
|
||||
|
||||
}
|
||||
|
||||
uint64_t Track_Init::mix64(uint64_t x) {
|
||||
x ^= x >> 30; x *= 0xBF58476D1CE4E5B9ULL;
|
||||
x ^= x >> 27; x *= 0x94D049BB133111EBULL;
|
||||
x ^= x >> 31;
|
||||
return x;
|
||||
}
|
||||
|
||||
uint64_t Track_Init::quantize(double v, double scale) {
|
||||
if (std::isnan(v)) return 0x9E3779B97F4A7C15ULL;
|
||||
if (std::isinf(v))
|
||||
return v > 0 ? 0x2C08A6D5E3F1B907ULL : 0xD5E3F1B9072C08A6ULL;
|
||||
|
||||
double q = std::round(v * scale); // half away from zero
|
||||
constexpr double kLim = 9.0e18; // < 2^63,避免转 int64 溢出
|
||||
if (q > kLim) q = kLim;
|
||||
if (q < -kLim) q = -kLim;
|
||||
return static_cast<uint64_t>(static_cast<int64_t>(q));
|
||||
}
|
||||
|
||||
uint64_t Track_Init::bits_of(double v) {
|
||||
if (std::isnan(v)) return 0x7FF8000000000000ULL;
|
||||
if (v == 0.0) return 0ULL;
|
||||
uint64_t b;
|
||||
std::memcpy(&b, &v, sizeof(b));
|
||||
return b;
|
||||
}
|
||||
|
||||
uint16_t Track_Init::fold16(uint64_t h) {
|
||||
h ^= h >> 32;
|
||||
h ^= h >> 16;
|
||||
return static_cast<uint16_t>(h);
|
||||
}
|
||||
|
||||
uint16_t Track_Init::computeFingerprint(double r, double azi, double vr, double RCS,
|
||||
long long CPI_time)
|
||||
{
|
||||
uint64_t h = kSeed;
|
||||
h = mix64(h ^ quantize(r, 100.0)); // 距离:保留 0.01 m
|
||||
h = mix64(h ^ quantize(azi, 1000.0)); // 方位:保留 0.001°
|
||||
h = mix64(h ^ quantize(vr, 100.0)); // 径向速度:保留 0.01 m/s
|
||||
h = mix64(h ^ quantize(RCS, 10.0)); // RCS:保留 0.1 dBsm
|
||||
h = mix64(h ^ static_cast<uint64_t>(CPI_time));
|
||||
return fold16(h);
|
||||
}
|
||||
|
||||
Track_Init::Track_Init()
|
||||
{
|
||||
|
||||
|
||||
@@ -18,8 +18,7 @@ public:
|
||||
std::vector<Trust_Track> *trust_track, //可靠航迹
|
||||
std::vector <std::vector<Temp_track>> *temp_track,
|
||||
struct Track Trust_Track_Output[MAX_TRACK_NUM][10],
|
||||
int *Trust_track_num_Output,
|
||||
struct RadarPara Work_Parameter
|
||||
int *Trust_track_num_Output
|
||||
);
|
||||
|
||||
Track_Init();
|
||||
@@ -31,23 +30,27 @@ private:
|
||||
|
||||
std::vector<PointRecv> point_process; //要处理的点迹
|
||||
|
||||
void point_temp_track_asso(std::vector <std::vector<Temp_track>> *temp_track,
|
||||
struct RadarPara Work_Parameter); //临时航迹与点迹关联
|
||||
void point_temp_track_asso(std::vector <std::vector<Temp_track>> *temp_track); //临时航迹与点迹关联
|
||||
|
||||
void point_track_head_asso( std::vector <std::vector<Temp_track>> *temp_track,
|
||||
struct RadarPara Work_Parameter); //航迹头与点迹关联
|
||||
void point_track_head_asso( std::vector <std::vector<Temp_track>> *temp_track); //航迹头与点迹关联
|
||||
|
||||
void tmp_track_to_trust_track( std::vector <Trust_Track> *trust_track, //临时航迹转可靠航迹
|
||||
std::vector <std::vector<Temp_track>> *temp_track,
|
||||
struct Track Trust_Track_Output[MAX_TRACK_NUM][10],
|
||||
int *Trust_track_num_Output,
|
||||
struct RadarPara Work_Parameter);
|
||||
int *Trust_track_num_Output);
|
||||
|
||||
void tmp_track_die(std::vector <std::vector<Temp_track>> *temp_track);//临时航迹消亡
|
||||
|
||||
double alpha_cal_track_init(double x0,double y0,double x1,double y1,double x2,double y2); //计算夹角
|
||||
|
||||
int track_init_prohibit(double r, double azi, struct RadarPara Work_Parameter); //判断目标是否位于航迹起始屏蔽区 是返回1 否返回0
|
||||
int track_init_prohibit(double r, double azi); //判断目标是否位于航迹起始屏蔽区 是返回1 否返回0
|
||||
|
||||
const uint64_t kSeed = 0x243F6A8885A308D3ULL;
|
||||
uint64_t mix64(uint64_t x); // splitmix64 混合函数:对任意 64bit 输入产生雪崩均匀的 64bit 输出
|
||||
uint64_t quantize(double v, double scale); // 量化:把 double 映射到整数域,可容忍 ±0.5/scale 的浮点噪声
|
||||
uint64_t bits_of(double v); // bit-exact:直接使用 IEEE754 位模式(-0.0 归一为 +0.0,NaN 归一)
|
||||
uint16_t fold16(uint64_t h); // 把 64bit 折叠成 16bit,充分使用全部熵
|
||||
uint16_t computeFingerprint(double r, double azi, double vr, double RCS, long long CPI_time); //计算航迹指纹
|
||||
|
||||
Track_Ind_Mangement track_index_mangement;
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@ int Track_Init_Direct_Tracking::track_init_process_logic( std::vector<PointRecv>
|
||||
std::vector <std::vector<Temp_track>> *temp_track,
|
||||
struct Track Trust_Track_Output[MAX_TRACK_NUM][10],
|
||||
int *Trust_track_num_Output,
|
||||
struct RadarPara Work_Parameter,
|
||||
int track_ID
|
||||
)
|
||||
{
|
||||
@@ -22,8 +21,7 @@ int Track_Init_Direct_Tracking::track_init_process_logic( std::vector<PointRecv>
|
||||
|
||||
}
|
||||
|
||||
void Track_Init_Direct_Tracking::point_temp_track_asso(std::vector <std::vector<Temp_track>> *temp_track,
|
||||
struct RadarPara Work_Parameter) //临时航迹与点迹关联
|
||||
void Track_Init_Direct_Tracking::point_temp_track_asso(std::vector <std::vector<Temp_track>> *temp_track) //临时航迹与点迹关联
|
||||
{
|
||||
//关联信息
|
||||
struct Asso_info
|
||||
@@ -82,7 +80,7 @@ void Track_Init_Direct_Tracking::point_temp_track_asso(std::vector <std::vector<
|
||||
double y2=Z[0]*sin(Z[1]);
|
||||
double alpha = alpha_cal_track_init(x0,y0,x1,y1,x2,y2);
|
||||
|
||||
if(d*d<=TRACK_START_THRESHOLD*TRACK_START_THRESHOLD && alpha<ALPHA_START && vr_point*v_temp_track>0)// && fabs(h_point-h_temp_track )<= r_point*SIGMA_E&& abs(vr_point-v_temp_track)/abs(v_temp_track)<0.8
|
||||
if(d*d<=rdp_parameter.track_init_threshold*rdp_parameter.track_init_threshold && alpha<rdp_parameter.track_init_angle && vr_point*v_temp_track>0)// && fabs(h_point-h_temp_track )<= r_point*rdp_parameter.sigma_pitch&& abs(vr_point-v_temp_track)/abs(v_temp_track)<0.8
|
||||
{
|
||||
struct Asso_info asso_info_tmp;
|
||||
asso_info_tmp.point_idx = static_cast<int>(i)+1;
|
||||
@@ -163,8 +161,7 @@ void Track_Init_Direct_Tracking::point_temp_track_asso(std::vector <std::vector<
|
||||
}
|
||||
}
|
||||
|
||||
void Track_Init_Direct_Tracking::point_track_head_asso( std::vector <std::vector<Temp_track>> *temp_track,
|
||||
struct RadarPara Work_Parameter) //航迹头与点迹关联
|
||||
void Track_Init_Direct_Tracking::point_track_head_asso( std::vector <std::vector<Temp_track>> *temp_track) //航迹头与点迹关联
|
||||
{
|
||||
//关联上的信息
|
||||
struct Asso_info
|
||||
@@ -205,24 +202,24 @@ void Track_Init_Direct_Tracking::point_track_head_asso( std::vector <std::vector
|
||||
double dis = sqrt(pow(x_point-x_track_head,2)+pow(y_point-y_track_head,2));
|
||||
|
||||
double vmax;
|
||||
if(Work_Parameter.work_mode == 0) //近程模式 最大速度减小一点
|
||||
if(rdp_parameter.radar_work_mode == 0) //近程模式 最大速度减小一点
|
||||
{
|
||||
//vmax = V_MAX/5;
|
||||
vmax = Work_Parameter.V_MAX;
|
||||
//vmax = rdp_parameter.track_init_velocity_max/5;
|
||||
vmax = rdp_parameter.track_init_velocity_max;
|
||||
|
||||
}
|
||||
else //中远程模式 最大速度正常用
|
||||
{
|
||||
// vmax = V_MAX;
|
||||
vmax = Work_Parameter.V_MAX;
|
||||
// vmax = rdp_parameter.track_init_velocity_max;
|
||||
vmax = rdp_parameter.track_init_velocity_max;
|
||||
|
||||
}
|
||||
|
||||
double delta_T = (T_point - T_track_head)/1000.0;
|
||||
|
||||
//满足关联条件的点航
|
||||
if( ( (r_point>=1000 && dis<=vmax*delta_T && dis>=Work_Parameter.V_MIN*delta_T) || (r_point<1000 && dis<=vmax*delta_T/2.0 && dis>=Work_Parameter.V_MIN*delta_T/2.0) )
|
||||
&& vr_point*v_track_head>0 )//&& fabs(vr_point-v_track_head)/fabs(v_track_head)<0.2 && fabs(h_point-h_track_head)<=r_point*SIGMA_E
|
||||
if( ( (r_point>=1000 && dis<=vmax*delta_T && dis>=rdp_parameter.track_init_velocity_min*delta_T) || (r_point<1000 && dis<=vmax*delta_T/2.0 && dis>=rdp_parameter.track_init_velocity_min*delta_T/2.0) )
|
||||
&& vr_point*v_track_head>0 )//&& fabs(vr_point-v_track_head)/fabs(v_track_head)<0.2 && fabs(h_point-h_track_head)<=r_point*rdp_parameter.sigma_pitch
|
||||
{
|
||||
struct Asso_info asso_info_tmp;
|
||||
asso_info_tmp.point_idx = static_cast<int>(i)+1;
|
||||
@@ -299,7 +296,6 @@ void Track_Init_Direct_Tracking::tmp_track_to_trust_track( std::vector<Trust_Tr
|
||||
std::vector <std::vector<Temp_track>> *temp_track,
|
||||
struct Track Trust_Track_Output[MAX_TRACK_NUM][10],
|
||||
int *Trust_track_num_Output,
|
||||
struct RadarPara Work_Parameter,
|
||||
int track_ID)
|
||||
{
|
||||
//temp_track中的临时航迹转为可靠航迹
|
||||
@@ -314,7 +310,7 @@ void Track_Init_Direct_Tracking::tmp_track_to_trust_track( std::vector<Trust_Tr
|
||||
}
|
||||
double range=(*Iter)[L-1].r;
|
||||
double azi=(*Iter)[L-1].azi/PI*180;
|
||||
if( L==Work_Parameter.track_start_point_num) //按长度查找TRUST_TRACK_POINT
|
||||
if( L==rdp_parameter.track_init_point_num_min) //按长度查找TRUST_TRACK_POINT
|
||||
{
|
||||
|
||||
//加入本地航迹文件
|
||||
@@ -338,7 +334,7 @@ void Track_Init_Direct_Tracking::tmp_track_to_trust_track( std::vector<Trust_Tr
|
||||
trust_track_tmp.T_track = (*Iter)[L-1].CPI_time;
|
||||
trust_track_tmp.point_flag=1; //实点
|
||||
trust_track_tmp.Track_Mode=1; //跟踪模式 TWS 0
|
||||
trust_track_tmp.Target_Type=UNCONF_TARGET;
|
||||
trust_track_tmp.Target_Type=0;
|
||||
trust_track_tmp.Height=(*Iter)[L-1].height;
|
||||
trust_track_tmp.Extrapolate_round=0;
|
||||
trust_track_tmp.approach_flag = -1;
|
||||
|
||||
@@ -14,7 +14,6 @@ public:
|
||||
std::vector <std::vector<Temp_track>> *temp_track,
|
||||
struct Track Trust_Track_Output[MAX_TRACK_NUM][10],
|
||||
int *Trust_track_num_Output,
|
||||
struct RadarPara Work_Parameter,
|
||||
int track_ID
|
||||
);
|
||||
|
||||
@@ -25,17 +24,14 @@ private:
|
||||
|
||||
std::vector<PointRecv> point_process; //要处理的点迹
|
||||
|
||||
void point_temp_track_asso(std::vector <std::vector<Temp_track>> *temp_track,
|
||||
struct RadarPara Work_Parameter); //临时航迹与点迹关联
|
||||
void point_temp_track_asso(std::vector <std::vector<Temp_track>> *temp_track); //临时航迹与点迹关联
|
||||
|
||||
void point_track_head_asso( std::vector <std::vector<Temp_track>> *temp_track,
|
||||
struct RadarPara Work_Parameter); //航迹头与点迹关联
|
||||
void point_track_head_asso( std::vector <std::vector<Temp_track>> *temp_track); //航迹头与点迹关联
|
||||
|
||||
void tmp_track_to_trust_track( std::vector<Trust_Track> *trust_track, //临时航迹转可靠航迹
|
||||
std::vector <std::vector<Temp_track>> *temp_track,
|
||||
struct Track Trust_Track_Output[MAX_TRACK_NUM][10],
|
||||
int *Trust_track_num_Output,
|
||||
struct RadarPara Work_Parameter,
|
||||
int track_ID
|
||||
);
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
1. **时间主线**:所有时间戳为 64 位毫秒(CPI/GNSS),参与运算时除 1000 转秒;ΔT = 点迹时间 − 航迹时间,ΔT 的正负决定"有效更新 / 只消费不更新 / 跳过"。
|
||||
2. **状态主线**:每条可靠航迹维护一套 IMM(3 个 Singer 模型)状态 [x, vx, ax, y, vy, ay],x/y 两轴解耦,量测为「距离-方位-径向速度」三维 EKF。
|
||||
3. **生命周期主线**:可靠航迹的消亡分两种判据——**TWS 普通航迹自 V1.5.8 起按航迹质量**(`Quality < TRACK_DIE_QUALITY` = 0.3),**重点目标按外推轮数**(判据 `> 10`)、**TAS 按外推轮数**(判据 `> 阈值`:相扫 20 / 机扫 96 圈);临时航迹靠"缓存圈数 buff_round"消亡。
|
||||
3. **生命周期主线**:可靠航迹的消亡分两种判据——**TWS 普通航迹自 V1.5.8 起按航迹质量**(`Quality < track_die_quality`,配置项 0.3),**重点目标按外推轮数**(判据 `> 10`)、**TAS 按外推轮数**(判据 `> 阈值`:相扫 20 / 机扫 96 圈);临时航迹靠"缓存圈数 buff_round"消亡。
|
||||
|
||||
---
|
||||
|
||||
@@ -42,13 +42,13 @@ flowchart TD
|
||||
A3 --> OUT1["输出更新航迹 + 起批航迹历史点 + 消亡批号"]
|
||||
|
||||
M3 --> B1["TAS 凝聚(单帧)"]
|
||||
B1 --> B2["仅对指定批号航迹做关联/EKF 或外推"]
|
||||
B1 --> B2["按航迹指纹定位航迹,仅对它做关联/EKF 或外推"]
|
||||
B2 --> B3["TAS 超时消亡"]
|
||||
B3 --> OUT2["输出该批号航迹"]
|
||||
B3 --> OUT2["输出该航迹"]
|
||||
|
||||
OUT1 --> BC["Beam_Ctrl 波束控制"]
|
||||
OUT2 --> BC
|
||||
BC --> OUT3["TrackingBeam:距离/方位/俯仰/批号"]
|
||||
BC --> OUT3["TrackingBeam:距离/方位/俯仰/航迹指纹"]
|
||||
```
|
||||
|
||||
---
|
||||
@@ -74,21 +74,23 @@ flowchart TD
|
||||
flowchart TD
|
||||
A["收到一帧点迹"] --> B{"point_type ?"}
|
||||
B -->|"0 = TWS"| C["写入 TWS 缓存"]
|
||||
C --> D{"判圈条件成立 ?<br/>相扫: Beam_index_aiz <= last_beam_num<br/>机扫: Beam_index_aiz == 0"}
|
||||
C --> D{"判圈条件成立(由配置 scan_mode 选择)?<br/>scan_mode=0 相扫: Beam_index_aiz <= last_beam_num<br/>scan_mode=1 机扫: Beam_index_aiz == 0"}
|
||||
D -->|"是"| E["return 1:满一圈,执行 TWS 航迹处理"]
|
||||
D -->|"否"| F["return 2:未满圈,仅做滑窗凝聚"]
|
||||
B -->|"1 = TAS"| G["写入 TAS 缓存<br/>记录 TAS 批号与本帧时间戳"]
|
||||
B -->|"1 = TAS"| G["写入 TAS 缓存<br/>记录 TAS 航迹指纹与本帧时间戳"]
|
||||
G --> H["return 3:TAS 处理"]
|
||||
B -->|"其它"| I["return 0:无效数据,丢弃"]
|
||||
```
|
||||
|
||||
**要点**
|
||||
|
||||
- 判圈条件按扫描体制区分(3e6763c,源码用 `#ifdef PHASE_SCANNING` 编译期隔离):
|
||||
- **相扫**:比较前后两帧方位波位号,当前帧 `Beam_index_aiz <= last_beam_num` 判为满一圈;`last_beam_num` 参与判断,`track_clear_all()` 复位为 INT_MAX,故**清空后/首帧即判满一圈**;
|
||||
- **机扫**:判 **波位号为 0**(7d5a56f 起),避免转台反转时波位号变小导致误判一圈完成(代码中保留了旧判断的注释);该分支下 `last_beam_num` 仍被赋值但不参与判断。
|
||||
- 判圈条件由配置项 `[radar_params] scan_mode` **运行时**选择(0 相扫 / 1 机扫;加载器校验仅允许 0/1),替代原先 `#ifdef PHASE_SCANNING` 的编译期分支:
|
||||
- **scan_mode=0(相扫)**:比较前后两帧方位波位号,当前帧 `Beam_index_aiz <= last_beam_num` 判为满一圈;`last_beam_num` 参与判断,`track_clear_all()` 复位为 INT_MAX,故**清空后/首帧即判满一圈**;
|
||||
- **scan_mode=1(机扫)**:判 **波位号为 0**(7d5a56f 起),避免转台反转时波位号变小导致误判一圈完成(代码中保留了旧判断的注释);该模式下 `last_beam_num` 仍被更新但不参与判断。
|
||||
- 两种模式下 `last_beam_num` 每帧都会更新,仅判满圈时 `circle_id++` 并返回 1;判圈行为不再由构建期区分(机扫/相扫已合并为同一份构建),仅由配置项 `scan_mode` 决定(见 README 4.2)。
|
||||
- 机扫下波位号 0 是每圈的起始波位,因此**每圈恰好触发一次 model=1**。
|
||||
- TAS 数据不参与判圈,**每来一帧就处理一次**(TAS 是调度的跟踪波位,本身就是单目标 CPI)。
|
||||
- TAS 帧携带的是**航迹指纹**(`DataRev::TAS_track_index`,字段名沿用,V1.5.9 起语义为指纹),`track_process(model=3)` 先用它在航迹表中定位航迹,再进入 TAS 关联(见 7.1、7.2 与 README 9.12)。
|
||||
|
||||
### 2.3 调度:model 与动作的对应
|
||||
|
||||
@@ -177,7 +179,7 @@ flowchart TD
|
||||
J --> L{"还有候选?"}
|
||||
K --> L
|
||||
L -- 有 --> G
|
||||
L -- 无 --> M{"未关联航迹(point_flag==0 且非手动TAS):<br/>Extrapolate_round ≤ MAX_EXTRAPOLATE_NUM ?"}
|
||||
L -- 无 --> M{"未关联航迹(point_flag==0 且非手动TAS):<br/>Extrapolate_round ≤ extrapolate_max ?"}
|
||||
M -- 是 --> M1["按数据率 Singer 预测一帧<br/>推进航迹时间,Track_Update_Flag=1"]
|
||||
M -- 否 --> M2["不预测、不推进时间<br/>仅 Extrapolate_round+1,Track_Update_Flag=0"]
|
||||
M1 --> N["只输出 Track_Update_Flag==1 的航迹行并清零该标志<br/>(其后由 track_init 追加起批历史点)"]
|
||||
@@ -186,7 +188,7 @@ flowchart TD
|
||||
|
||||
### 4.2 波门(统计距离、高度、距离三类都通过才算候选)
|
||||
|
||||
**(1)统计距离波门 thd_d**——按航迹距离分段收紧高机动模型(`ASSO_THORD = 4`):
|
||||
**(1)统计距离波门 thd_d**——按航迹距离分段收紧高机动模型(`associate_threshold`,样例配置 4):
|
||||
|
||||
| 航迹距离 r_track | 模型 1 / 2 | 模型 3 |
|
||||
|---|---|---|
|
||||
@@ -194,7 +196,7 @@ flowchart TD
|
||||
| 500 ~ 1000 m | d² < 16 | d² < 16/100(收紧) |
|
||||
| ≥ 1000 m | d² < 16 | d² < 16 |
|
||||
|
||||
> ⚠ **门限语义**:代码里 d 已经是马氏距离平方,判据又写作 `d*d < ASSO_THORD*ASSO_THORD`,`ASSO_THORD = 4` 时等效于 **d < 4**(而非 √d < 4)。调整 ASSO_THORD 时务必记住这一层"二次方"(该行为在 BUG-20 中被识别,有意保留)。
|
||||
> ⚠ **门限语义**:代码里 d 已经是马氏距离平方,判据又写作 `d*d < associate_threshold*associate_threshold`,样例配置 4 时等效于 **d < 4**(而非 √d < 4)。调整 `associate_threshold` 时务必记住这一层"二次方"(该行为在 BUG-20 中被识别,有意保留)。
|
||||
|
||||
**(2)高度门限 thd_h**(e0bf227 起在 TWS 侧生效)——|h_track − h_point| 按航迹距离分段:
|
||||
|
||||
@@ -250,7 +252,7 @@ VT > 300 且 R > 1200 → 拒绝(高速目标给固定 1200 m 上限)
|
||||
|
||||
### 4.6 高度平滑
|
||||
|
||||
关联实点后把点迹高度压入滑窗,输出高度取最近 w 个点的均值(不足 w 个则全平均)。窗口按距离分段,且 TWS/TAS 与扫描体制取值不同(`H_F_WIN_LEN` 相扫 6 / 机扫 3;TAS 偏移于 6284f3f 由 +2/+3/+4/+6 改为 +6/+8/+10/+12):
|
||||
关联实点后把点迹高度压入滑窗,输出高度取最近 w 个点的均值(不足 w 个则全平均)。窗口按距离分段,TWS/TAS 偏移不同、窗长基数取配置项 `height_smooth_win`(样例配置为相扫 6;机扫需配 3。TAS 偏移于 6284f3f 由 +2/+3/+4/+6 改为 +6/+8/+10/+12):
|
||||
|
||||
| 点迹距离 | TWS 窗口(相扫/机扫) | TAS 窗口(相扫/机扫) |
|
||||
|---|---|---|
|
||||
@@ -265,9 +267,9 @@ VT > 300 且 R > 1200 → 拒绝(高速目标给固定 1200 m 上限)
|
||||
|
||||
对 `point_flag == 0` 且 `manual_tracking_flag == 0` 的航迹(含 ΔT≤0 被消费的情形),按 `Extrapolate_round` 分两支(50e0b5c):
|
||||
|
||||
**(1)未到上限**:`Extrapolate_round <= MAX_EXTRAPOLATE_NUM`(机扫/相扫均为 3)时照常外推:
|
||||
**(1)未到上限**:`Extrapolate_round <= extrapolate_max`(配置项,默认 3)时照常外推:
|
||||
|
||||
- 推算步长取运行时数据率:`work_mode` 0/1/2 → `DATA_RATE_SHORT / MIDDLE / FAR`(秒);
|
||||
- 推算步长取配置项 `data_rate_tws`(秒;原实现按 `work_mode` 在 `DATA_RATE_SHORT/MIDDLE/FAR` 三档中三选一,改造后统一为单字段);
|
||||
- 用各子模型的 Singer F、Q 推进一帧(**不做量测更新**);
|
||||
- `T_track`、`GNSS_time` 各推进 ΔT×1000 毫秒;
|
||||
- `Extrapolate_round += 1`,`point_flag = 0`(补点/虚点),`Track_Update_Flag = 1` → 本圈作为补点行输出。
|
||||
@@ -278,11 +280,11 @@ VT > 300 且 R > 1200 → 拒绝(高速目标给固定 1200 m 上限)
|
||||
|
||||
**输出门控(50e0b5c)**:TWS 输出航迹的循环改为「`Track_Update_Flag == 1` 才输出,输出后清零」,即每圈只输出**本圈关联到实点或成功外推**的航迹;此前是每圈把航迹表中全部可靠航迹各输出一行。门控只判更新标志、不判 `manual_tracking_flag`,因此手动转 TAS 的航迹若本圈在 TWS 侧关联到实点,仍会经 TWS 通道输出。
|
||||
|
||||
> **普通 TWS 航迹的消亡改由质量决定(V1.5.8)**:`track_die.cpp` 对 `Track_Mode==0 && primary_target_flag==0` 的航迹改用 `Quality < TRACK_DIE_QUALITY`(0.3)判消亡,因此外推超限后航迹仍会驻留,直到质量衰减到门限以下;`Extrapolate_round` 继续每圈 +1 但不再决定普通 TWS 航迹的存活。重点航迹(10 圈)与 TAS(相扫 20 / 机扫 96)不变。
|
||||
> **普通 TWS 航迹的消亡改由质量决定(V1.5.8)**:`track_die.cpp` 对 `Track_Mode==0 && primary_target_flag==0` 的航迹改用 `Quality < track_die_quality`(配置项 0.3)判消亡,因此外推超限后航迹仍会驻留,直到质量衰减到门限以下;`Extrapolate_round` 继续每圈 +1 但不再决定普通 TWS 航迹的存活。重点航迹(10 圈)与 TAS(相扫 20 / 机扫 96)不变。
|
||||
>
|
||||
> 质量在外推环节按未更新时长二次衰减(`Unupdated_time` 累计,ΔT 由 `work_mode` 数据率决定),且该计算位于外推上限判断之外,故上限内、外都会衰减。
|
||||
>
|
||||
> **TAS 通道的判据方向相反**(`track_asso_tas.cpp`):`Extrapolate_round >= MAX_EXTRAPOLATE_NUM_TAS` 时才做预测、推进航迹时间并置 `Track_Update_Flag = 1`;未达该轮数时只累加圈数(不预测、不输出)。即 TWS 是"先外推、到上限后停",TAS 是"先不预测、到轮数后才开始外推",详见 7.2 与第 10 节;TAS 不维护航迹质量。
|
||||
> **TAS 通道的判据方向相反**(`track_asso_tas.cpp`):`Extrapolate_round >= extrapolate_max_tas`(配置项,样例相扫 5 / 机扫需配 48)时才做预测、推进航迹时间并置 `Track_Update_Flag = 1`;未达该轮数时只累加圈数(不预测、不输出)。即 TWS 是"先外推、到上限后停",TAS 是"先不预测、到轮数后才开始外推",详见 7.2 与第 10 节;TAS 不维护航迹质量。
|
||||
|
||||
---
|
||||
|
||||
@@ -321,14 +323,14 @@ flowchart TD
|
||||
|
||||
```
|
||||
|r_point − r_末点| ≥ 5 m 距离差硬门限(近距点不参与)
|
||||
d ≤ 8(代码写作 d² ≤ TRACK_START_THRESHOLD²=64) 3 维 EKF 统计距离波门
|
||||
α < ALPHA_START = 60° 折角:最近三点连线夹角
|
||||
d ≤ 8(代码写作 d² ≤ track_init_threshold²=64) 3 维 EKF 统计距离波门
|
||||
α < track_init_angle = 60° 折角:最近三点连线夹角
|
||||
v_r,point · v_r,末点 > 0 多普勒同号
|
||||
```
|
||||
|
||||
- d 由 4 维 CV 状态一步预测 + 「距离-方位-径向速度」EKF 得到(过程噪声 0.03²、同样做方位环绕与盲速折叠);
|
||||
- 折角 α 为向量 D12(前一点→末点)与 D23(末点→新点)的夹角;α 越小越接近直线;
|
||||
- 门限同样写作 `d² ≤ TRACK_START_THRESHOLD²`,而 d 本身已是马氏距离平方,实际生效为 d ≤ 8(与可靠航迹关联门限的语义一致)。
|
||||
- 门限同样写作 `d² ≤ track_init_threshold²`,而 d 本身已是马氏距离平方,`track_init_threshold=8` 时实际生效为 d ≤ 8(与可靠航迹关联门限的语义一致)。
|
||||
|
||||
**阶段②:点 ↔ 航迹头(两点起始)**
|
||||
|
||||
@@ -377,7 +379,7 @@ dis ≥ |v_r,head| · ΔT 同上,用航迹头的多普勒约
|
||||
|
||||
| 条件 | 说明 |
|
||||
|---|---|
|
||||
| 长度 | `track_start_point_num ≤ L ≤ 10`(接口层校验 3~9,典型 3 或 4) |
|
||||
| 长度 | `track_init_point_num_min ≤ L ≤ 10`(配置加载器校验 3~9,典型 3 或 4) |
|
||||
| 位置 | 末点不落在**起批屏蔽区**(距离-方位矩形,最多 30 个,用严格不等号判断) |
|
||||
| 容量 | 可靠航迹数 < 500 且能分配到一个未占用航迹号 |
|
||||
| 初始化 | 用**最后三个点**做三点卡尔曼初始化 → 6 维状态;三模型同状态、u=1/3 |
|
||||
@@ -393,14 +395,14 @@ dis ≥ |v_r,head| · ΔT 同上,用航迹头的多普勒约
|
||||
|
||||
| 通道 | 条件(满足其一即消亡) |
|
||||
|---|---|
|
||||
| TWS 普通航迹 | `Track_Mode==0` 且 `primary_target_flag==0` 且 **`Quality < TRACK_DIE_QUALITY`(0.3)** —— V1.5.8(782bff5)起改按航迹质量,不再看外推圈数 |
|
||||
| TWS 重点航迹 | `Track_Mode==0` 且 `primary_target_flag==1` 且 `Extrapolate_round > TRACK_DIE_ROUND_PRIMARY`(10) |
|
||||
| TAS 航迹 | `Track_Mode==1` 且 `Extrapolate_round > TRACK_DIE_ROUND_TAS`(相扫 20 / 机扫 96) |
|
||||
| TWS 普通航迹 | `Track_Mode==0` 且 `primary_target_flag==0` 且 **`Quality < track_die_quality`(配置项 0.3)** —— V1.5.8(782bff5)起改按航迹质量,不再看外推圈数 |
|
||||
| TWS 重点航迹 | `Track_Mode==0` 且 `primary_target_flag==1` 且 `Extrapolate_round > track_die_round_pramary`(配置项 10) |
|
||||
| TAS 航迹 | `Track_Mode==1` 且 `Extrapolate_round > track_die_round_tas`(配置项;样例相扫 20 / 机扫需配 96) |
|
||||
| 手动删除 | `manual_delete_flag == 1`(各通道单独判定,置位即消亡,由外部删除接口 `track_delete` 置位) |
|
||||
|
||||
轮数类判据都改为 `die_flag` 组合("或"关系),且比较符由 `>=` 改为 `>`(50e0b5c):即允许的外推圈数比旧版各多 1 圈,且**重点航迹与普通航迹走不同门限**。由于每圈未关联只自增 1,判据为 `>` 时实际是"连续 N+1 圈没有实点关联"才消亡:重点航迹 11 圈、TAS 相扫 21 圈(阈值于 5d105d2 由 10 提高到 20)/ 机扫 97 圈。
|
||||
|
||||
> **普通 TWS 航迹的消亡时长(V1.5.8)**:改按质量后不再有固定圈数,存活时长由质量衰减速度决定——未关联期间按 `Unupdated_time` 的二次函数加速衰减(见 9.9 / README 9.11),`Quality` 跌破 0.3 即消亡;`TRACK_DIE_ROUND`(6)已不再被引用。
|
||||
> **普通 TWS 航迹的消亡时长(V1.5.8)**:改按质量后不再有固定圈数,存活时长由质量衰减速度决定——未关联期间按 `Unupdated_time` 的二次函数加速衰减(见 9.9 / README 9.11),`Quality` 跌破 `track_die_quality`(配置项 0.3)即消亡;`track_die_round`(配置项 6)已不再被引用。
|
||||
|
||||
> `primary_target_flag`(重点目标标记)由外部接口 `Data_Process::primary_target_add / primary_target_cancel` 按批号置 1 / 清 0(`data_process.cpp`);新起批航迹在 `track_init.cpp` 中以 `Trust_Track trust_track_tmp = {}` 零初始化,故恒为 0。该标记在 TWS 上决定走「质量消亡(普通)」还是「10 圈消亡(重点)」。
|
||||
|
||||
@@ -433,20 +435,21 @@ flowchart TD
|
||||
| 项 | TWS | TAS |
|
||||
|---|---|---|
|
||||
| 凝聚 | 相邻两帧滑窗,近程多速度维约束 | 单帧,恒有速度维约束 |
|
||||
| 处理对象 | 全部可靠航迹 × 全部点 | **仅指定批号**的这一条航迹 × 全部点 |
|
||||
| 处理对象 | 全部可靠航迹 × 全部点 | **仅由指纹定位到的那一条航迹** × 全部点(旧版按批号指定) |
|
||||
| 俯仰/高度门限 | 俯仰门限已注释停用;**高度门限生效**(按距离分段,e0bf227 起) | **高度门限生效**(按距离分段,取值与 TWS 相同) |
|
||||
| ΔT ≤ 0 | 参与选取但只消费点迹、不更新 | **直接跳过该点**(并记告警日志) |
|
||||
| ΔT ≤ 0 | 参与选取但只消费点迹、不更新 | **直接跳过该点**(并以 `RDP_LOG_WARN` 记告警日志) |
|
||||
| 外推时间 | 按 work_mode 数据率固定步长 | 按 `最新时间戳 − 航迹时间` |
|
||||
| 外推触发轮数 | `Extrapolate_round <= MAX_EXTRAPOLATE_NUM`(3)才预测 | `Extrapolate_round >= MAX_EXTRAPOLATE_NUM_TAS`(相扫 5 / 机扫 48)才预测(方向与 TWS 相反) |
|
||||
| 输出 | 本圈有更新的航迹(关联到实点或成功外推,输出后清 `Track_Update_Flag`) | 该批号航迹且本圈有更新 |
|
||||
| 外推触发轮数 | `Extrapolate_round <= extrapolate_max`(配置项 3)才预测 | `Extrapolate_round >= extrapolate_max_tas`(配置项,样例相扫 5 / 机扫需配 48)才预测(方向与 TWS 相反) |
|
||||
| 输出 | 本圈有更新的航迹(关联到实点或成功外推,输出后清 `Track_Update_Flag`) | 该航迹且本圈有更新;TAS 波束对外下发**航迹指纹**(`Track_fingerprint`),不再下发批号 |
|
||||
|
||||
### 7.2 TAS 关联与更新
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A["TAS 本帧点迹 + 指定批号航迹"] --> B{"该批号航迹存在?"}
|
||||
A["TAS 本帧点迹 + 航迹指纹"] --> B{"按指纹找到航迹?"}
|
||||
B -- 否 --> Z["直接返回(不做任何处理)"]
|
||||
B -- 是 --> C["IMM 交互;航迹 point_flag 置 0"]
|
||||
B -- 是 --> B1["置 manual_tracking_flag=1、Track_Mode=1"]
|
||||
B1 --> C["IMM 交互;航迹 point_flag 置 0"]
|
||||
C --> D["逐点:ΔT ≤ 0 → 记告警并跳过"]
|
||||
D --> E["逐点:三模型统计距离 d1/d2/d3 + 高度门限"]
|
||||
E --> F{"d² < 16(任一模型)且高度门限通过?"}
|
||||
@@ -454,9 +457,9 @@ flowchart TD
|
||||
F -- 否 --> H["丢弃"]
|
||||
G --> I["取 d_min 最小的一点"]
|
||||
I --> J["三模型 EKF 更新 → 概率更新 → 融合<br/>刷新时间/点信息/高度平滑"]
|
||||
J --> K["输出该批号航迹(Track_Update_Flag==1 才输出,输出后清零)"]
|
||||
J --> K["输出该航迹(Track_Update_Flag==1 才输出,输出后清零)"]
|
||||
E --> L{"候选表为空?"}
|
||||
L -- 是 --> L1{"Extrapolate_round ≥ MAX_EXTRAPOLATE_NUM_TAS ?"}
|
||||
L -- 是 --> L1{"Extrapolate_round ≥ extrapolate_max_tas ?"}
|
||||
L1 -- 是 --> M["按 ΔT = 最新时间戳 − 航迹时间 外推一帧<br/>(ΔT ≤ 0 则跳过),Track_Update_Flag=1"]
|
||||
L1 -- 否 --> M2["不预测、不推进时间<br/>仅 Extrapolate_round+1,Track_Update_Flag=0"]
|
||||
M --> K
|
||||
@@ -475,7 +478,7 @@ flowchart TD
|
||||
|
||||
**TAS 后续处理**:外推不受 work_mode 数据率约束,而是直接推进到"本帧最新时间戳",保证 TAS 航迹时间与波束调度时刻同步。
|
||||
|
||||
**TAS 外推轮数门控(50e0b5c)**:与 TWS **方向相反**——只有 `Extrapolate_round >= MAX_EXTRAPOLATE_NUM_TAS`(相扫 5 / 机扫 48)时才做预测、推进航迹时间并置 `Track_Update_Flag = 1`;未达该轮数时只 `Extrapolate_round += 1`、`point_flag = 0`、`Track_Update_Flag = 0`,既不预测也不输出。因此 TAS 航迹在刚丢失点迹时并不会立刻按"最新时间戳"外推:相扫体制下前 5 次未关联不产生任何输出行,第 6 次起才开始外推输出;机扫体制下前 48 次不输出。该不对称仅如实记录(成因不明,不作缺陷判定),连同 TWS/TAS 输出门控一并见第 10 节。
|
||||
**TAS 外推轮数门控(50e0b5c)**:与 TWS **方向相反**——只有 `Extrapolate_round >= extrapolate_max_tas`(配置项,样例相扫 5 / 机扫需配 48)时才做预测、推进航迹时间并置 `Track_Update_Flag = 1`;未达该轮数时只 `Extrapolate_round += 1`、`point_flag = 0`、`Track_Update_Flag = 0`,既不预测也不输出。因此 TAS 航迹在刚丢失点迹时并不会立刻按"最新时间戳"外推:相扫配置下前 5 次未关联不产生任何输出行,第 6 次起才开始外推输出;机扫配置(48)下前 48 次不输出。该不对称仅如实记录(成因不明,不作缺陷判定),连同 TWS/TAS 输出门控一并见第 10 节。
|
||||
|
||||
---
|
||||
|
||||
@@ -499,29 +502,31 @@ flowchart TD
|
||||
- 队列未满(相扫最多 4 个 / 机扫最多 1 个);
|
||||
- 不在 TAS 禁止区(距离-方位矩形)。
|
||||
|
||||
入队动作:写批号、标记非空、`last_track_time = T_track`、**航迹 Track_Mode 置 1**,并**立即输出一次该航迹更新**(通知宿主跟踪状态变化)。
|
||||
入队动作:写 `Track_Index`(队列内部键,仍是批号)、标记非空、`last_track_time = T_track`、**航迹 Track_Mode 置 1**,并**立即输出一次该航迹更新**(通知宿主跟踪状态变化;V1.5.9 起该行按常规关联输出的完整字段填充,见 README 9.10 与 CHANGELOG `[1.5.9]` 的 eb9f30c)。
|
||||
|
||||
**出队条件**:队列中的批号在航迹表中已不存在,或不再处于"手动 + TAS"状态 → 清空该队列项。
|
||||
|
||||
> 自动开启/退出 TAS 的逻辑(按距离/速度/高度阈值)已全部注释停用,**只支持手动**。
|
||||
>
|
||||
> **指纹与批号的分工(V1.5.9)**:队列、内部关联与出队仍以 `Track_Index` 为键;只有跨 DLL 边界的 `DataRev.TAS_track_index`(输入)与 `TrackingBeam.TAS_track_index`(输出)改用 **16 bit 航迹指纹** `Track_fingerprint`(起批时按末点特征生成,见 README 9.12)。
|
||||
|
||||
### 8.2 波束输出调度(关键)
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A["遍历 TAS 队列"] --> B["按批号在航迹表中找到该航迹"]
|
||||
B --> C{"距上次波束输出 > DATA_RATE_TAS 秒<br/>且 CPI 时间早于已选中的最早 CPI?"}
|
||||
A["遍历 TAS 队列"] --> B["按队列项 Index(Track_Index,内部键)在航迹表中找到该航迹"]
|
||||
B --> C{"距上次波束输出 > data_rate_tas 秒<br/>且 CPI 时间早于已选中的最早 CPI?"}
|
||||
C -- 否 --> A
|
||||
C -- 是 --> D["选中该目标(CPI 时间最早者)"]
|
||||
D --> E["位置外推:ΔT = (最新时间戳 − CPI 时间)/1000<br/>x + vx·ΔT, y + vy·ΔT"]
|
||||
E --> F["Range = √(xp²+yp²),Azi = atan2(yp,xp)<br/>Elev = asin(Height / Range)"]
|
||||
F --> G["open_flag=1, type=1, 批号=真实航迹号<br/>更新该目标 last_track_time = 最新时间戳"]
|
||||
F --> G["open_flag=1, type=default_track_beam_type, TAS_track_index=航迹指纹<br/>更新该目标 last_track_time = 最新时间戳"]
|
||||
C -.->|"无任何合格目标"| H["open_flag = 0(其余字段保持旧值)"]
|
||||
```
|
||||
|
||||
**要点**
|
||||
|
||||
- 门控只用一个条件(e0bf227 修正):目标「距上次波束输出的时间」(`latest_timestamp − last_track_time`)要大于 `DATA_RATE_TAS` 秒(单位是秒,由宿主经参数初始化传入,须为 > 0 的有效值)→ 保证 TAS 目标的数据率**不随 Beam_Ctrl 的调用频率变化**;修正前还要求「航迹时间与最新时间戳之差」也大于该门限,与"取最早 CPI 目标"的语义冲突,会漏掉本应输出的波束;
|
||||
- 门控只用一个条件(e0bf227 修正):目标「距上次波束输出的时间」(`latest_timestamp − last_track_time`)要大于配置项 `data_rate_tas` 秒(单位是秒,由 TOML 配置文件提供、加载器校验须 > 0)→ 保证 TAS 目标的数据率**不随 Beam_Ctrl 的调用频率变化**;修正前还要求「航迹时间与最新时间戳之差」也大于该门限,与"取最早 CPI 目标"的语义冲突,会漏掉本应输出的波束;
|
||||
- 多目标时选**队列中 CPI 时间最早**的目标(同时要求其 CPI 时间早于当前已选者),等价于"按最久未更新者优先"的轮转;
|
||||
- 选中后才按该目标的 CPI 时间算外推步长 `ΔT = (最新时间戳 − CPI 时间)/1000`;
|
||||
- 原来的"每次调用整队循环移位"已停用;
|
||||
@@ -531,46 +536,53 @@ flowchart TD
|
||||
|
||||
## 9. 参数脉络(决定行为的旋钮)
|
||||
|
||||
### 9.1 编译期宏(随扫描体制切换)
|
||||
> **参数配置方式改造后**:雷达工作参数不再来自 `parameters.h` 编译期宏或宿主下发的 `RadarPara`,而是由 TOML 配置文件(样例 `config/config_tmp.toml`)加载到**全局工作参数对象 `rdp_parameter`**(`struct.h` 中 `extern RDP_parameter rdp_parameter;` 声明、`data_process.cpp` 定义并零初始化),经公开接口 `parameters_reload(配置文件路径)` 在运行期一次性加载(失败返回 -1 且不改变当前参数;严格校验规则与完整参数表见 README 第 10 节)。各处理模块直接访问该全局对象,不再经函数形参逐层传递;全局对象为 DLL 内单实例、非线程安全,配置重载应与处理互斥。下表「样例值」为相扫配置;**机扫需在配置中改**的 5 项(`scan_mode` 及 4 个原按体制区分的参数)已单独标注。
|
||||
|
||||
| 宏 | 相扫 | 机扫 | 作用点 |
|
||||
|---|---|---|---|
|
||||
| `SIGMA_R / A / E / V` | 10 / 0.02 / 0.2 / 2.0 | 同左 | 量测噪声、EKF 的 R、转换量测协方差 |
|
||||
| `DOT_COH_RANGE / V / AZI` | 80 / 2 / 6 | 同左 | 凝聚门限(距离 m / 速度 m/s / 方位 °) |
|
||||
| `R_MIN / R_MAX` | 100 / 100000 | 同左 | 有效量程(凝聚后过滤) |
|
||||
| `TRACK_START_THRESHOLD` | 8 | 同左 | 起始统计距离波门(d ≤ 8) |
|
||||
| `ALPHA_START` | 60 | 同左 | 起始折角上限(°) |
|
||||
| `ASSO_THORD` | 4 | 同左 | 关联波门(判据 d*d < ASSO_THORD²,d 已是马氏距离平方,等效 d < 4) |
|
||||
| `MAX_EXTRAPOLATE_NUM` | 3 | 同左 | TWS 未关联航迹最大外推轮数(`Extrapolate_round <= 该值` 才预测并输出) |
|
||||
| `MAX_EXTRAPOLATE_NUM_TAS` | 5 | 48 | TAS 未关联航迹开始外推的轮数(判据 `>=`,方向与 TWS 相反) |
|
||||
| `TRACK_DIE_ROUND` | 6 | 6 | **已不被引用**:V1.5.8 起 TWS 普通航迹改按质量消亡(宏保留备用) |
|
||||
| `TRACK_DIE_ROUND_PRIMARY` | 10 | 10 | TWS 重点航迹(`primary_target_flag==1`)消亡轮数 |
|
||||
| `TRACK_DIE_ROUND_TAS` | 20 | 96 | TAS 航迹消亡轮数(判据 `Extrapolate_round > 该值`;相扫 50e0b5c 由 5→10、5d105d2 再调整为 20) |
|
||||
| `TRACK_DIE_QUALITY` | 0.3 | 0.3 | TWS 普通航迹质量消亡门限(`Quality < 该值`;V1.5.8 新增) |
|
||||
| `TRACK_START_QUALITY_L3~L7` | 0.7 / 0.6 / 0.5 / 0.4 / 0.3 | 同左 | 起批最低质量(按临时航迹长度 3/4/5/6/≥7;V1.5.8 新增) |
|
||||
| `TRACK_QUALITY_*` | 见 README 10.1 | 同左 | 质量权重(D/SNR/LEN:0.5/0.3/0.2)、平滑系数 0.8、衰减系数 O2/O1 = −0.0008/−0.0025、折算上限 100(V1.5.8 新增) |
|
||||
| `MAX_TRACK_NUM / MAX_TRACK_INDEX` | 500 / 500 | 同左 | 航迹数上限 / 航迹号上限 |
|
||||
| `TAS_QUEUE_LENGTH / MAX_TAS_NUM` | 4 / 4 | 1 / 1 | TAS 队列长度 / 最大 TAS 目标数 |
|
||||
| `H_F_WIN_LEN` | 6 | 3 | 高度平滑窗长基数:TWS 取 `+2/+3/+4/+6`、TAS 取 `+6/+8/+10/+12`(相扫基数 6284f3f 起由 3 调整为 6,TAS 偏移同提交加长) |
|
||||
| `FREQ0` | 9.2 GHz | 同左 | 盲速计算的基准频率 |
|
||||
| `RDP_LOG_ENABLE / RDP_LOG_DIR` | — | — | 日志落盘开关与目录 |
|
||||
### 9.1 配置文件参数(`[段] 键`,样例值 = 相扫)
|
||||
|
||||
### 9.2 运行时参数(宿主传入,随任务变化)
|
||||
| 配置键 | 样例值 | 作用点 |
|
||||
|---|---|---|
|
||||
| `[radar_params] radar_work_mode` | 0 | 凝聚是否带速度维约束(当前唯一生效点;航迹起始的远近程分支取值相同,属死分支;TWS 外推已改为固定 `data_rate_tws`,不再按模式三选一) |
|
||||
| `[radar_params] scan_mode` | 0(**机扫配 1**) | **扫描模式 0 相扫 / 1 机扫**(必填,加载器校验仅允许 0/1):决定 `data_preprocess()` 的 TWS 判圈逻辑(0 用波位号比较、1 用波位号==0),替代原编译期 `PHASE_SCANNING`/`MECHANICAL_SCANNING` 分支;机扫/相扫已合并为同一份构建,同一份 DLL 通过该配置切换扫描模式 |
|
||||
| `[radar_params] freq_0_GHz` | 9.2 | 盲速计算基准频率(原 `FREQ0`) |
|
||||
| `[radar_params] radar_height` / `radar_system_delay` | 0 / 0 | 输出 z 叠加平台高度 / 输出位置系统延迟补偿(原 `Height` / `Sys_delay`) |
|
||||
| `[radar_params] data_rate_tws` | 3 | TWS 未关联航迹外推步长(秒;原按 `work_mode` 在 `DATA_RATE_SHORT/MIDDLE/FAR` 中三选一,现统一单字段) |
|
||||
| `[radar_params] data_rate_tas` | 1 | TAS 波束输出间隔门控(秒,须 > 0) |
|
||||
| `[cohesion] point_cohesion_range/velocity/azimuth` | 80 / 2 / 6 | 凝聚门限:距离 m / 速度 m/s / 方位 °(原 `DOT_COH_*`) |
|
||||
| `[cohesion] point_cohesion_range_min/max` | 100 / 100000 | 有效量程(凝聚后过滤;原 `R_MIN/R_MAX`) |
|
||||
| `[track_associate] sigma_range/azimuth/pitch/velocity` | 10 / 0.02 / 0.2 / 2.0 | 量测噪声、EKF 的 R、转换量测协方差(原 `SIGMA_*`) |
|
||||
| `[track_associate] associate_threshold` | 4 | 关联波门(判据 `d*d < 该值²`,d 已是马氏距离平方 → 等效 d < 4;原 `ASSO_THORD`) |
|
||||
| `[track_associate] extrapolate_max` | 3 | TWS 未关联航迹最大外推轮数(`Extrapolate_round <= 该值` 才预测并输出;原 `MAX_EXTRAPOLATE_NUM`) |
|
||||
| `[track_associate] extrapolate_max_tas` | 5(**机扫配 48**) | TAS 开始外推的轮数(判据 `>=`,方向与 TWS 相反;原 `MAX_EXTRAPOLATE_NUM_TAS`) |
|
||||
| `[track_associate] height_smooth_win` | 6(**机扫配 3**) | 高度平滑窗长基数:TWS 取 `+2/+3/+4/+6`、TAS 取 `+6/+8/+10/+12`(原 `H_F_WIN_LEN`) |
|
||||
| `[track_init] track_init_threshold` / `track_init_angle` | 8 / 60 | 起始统计距离波门(d ≤ 8)/ 起始折角上限(°)(原 `TRACK_START_THRESHOLD` / `ALPHA_START`) |
|
||||
| `[track_init] track_init_point_num_min` | 3 | 起批所需点数(校验 3~9;原 `RadarPara.track_start_point_num`) |
|
||||
| `[track_init] track_init_quality_L3~L7` | 0.7 / 0.6 / 0.5 / 0.4 / 0.3 | 起批最低质量(按临时航迹长度 3/4/5/6/≥7;原 `TRACK_START_QUALITY_L3~L7`) |
|
||||
| `[track_init] track_init_velocity_min/max` | 1 / 50 | 航迹头-点关联的速度区间(原 `V_MIN/V_MAX`) |
|
||||
| `[track_die] track_die_round` | 6 | **已不被引用**:V1.5.8 起 TWS 普通航迹改按质量消亡(保留备用;原 `TRACK_DIE_ROUND`) |
|
||||
| `[track_die] track_die_round_pramary` | 10 | TWS 重点航迹(`primary_target_flag==1`)消亡轮数(原 `TRACK_DIE_ROUND_PRIMARY`) |
|
||||
| `[track_die] track_die_round_tas` | 20(**机扫配 96**) | TAS 航迹消亡轮数(判据 `Extrapolate_round > 该值`;原 `TRACK_DIE_ROUND_TAS`) |
|
||||
| `[track_die] track_die_quality` | 0.3 | TWS 普通航迹质量消亡门限(`Quality < 该值`;原 `TRACK_DIE_QUALITY`) |
|
||||
| `[track_quality] track_quality_*` | 100 / 0 / 0.5 / 0.3 / 0.2 / 0.8 / −0.0008 / −0.0025 | 质量折算上限/下限、三项权重(D/SNR/LEN)、平滑系数、衰减系数 O2/O1(原 `TRACK_QUALITY_*`) |
|
||||
| `[track_index] track_index_max` | 500 | 航迹号上限(原 `MAX_TRACK_INDEX`,内部航迹号表按该值分配) |
|
||||
| `[beam_control] default_track_beam_type` / `tas_track_num_max` | 1 / 4(**机扫配 1**) | 跟踪波束类型(原硬编码 1)/ 最大同时跟踪 TAS 目标数(原 `MAX_TAS_NUM`) |
|
||||
| `[singer_model] model_1/2/3_Q_fast/slow` | 0.000001 / 0.01 / 1.0 | 三个 Singer 模型的过程噪声强度(分档见 5.2;原 `RadarPara.Model*_Q_*`) |
|
||||
| `[track_init_prohibite_area]` / `[tas_prohibite_area]` | `*_num = 0` + 空数组 | 起批屏蔽区 / TAS 禁止区(0~30 组,4 个数组长度必须等于 `*_num`;原 `RadarPara.track_prohibite_area_*` / `TAS_prohibite_area_*`) |
|
||||
|
||||
| 参数 | 影响环节 |
|
||||
|---|---|
|
||||
| `work_mode`(0 近程/1 中程/2 远程) | 凝聚是否带速度维约束;TWS 外推步长;关联距离门限分支 |
|
||||
| `track_start_point_num`(校验 3~9) | 起批所需点数 |
|
||||
| `V_MAX / V_MIN` | 航迹头-点关联的速度区间 |
|
||||
| `DATA_RATE_SHORT / MIDDLE / FAR` | TWS 未关联航迹外推步长(秒) |
|
||||
| `DATA_RATE_TAS`(秒,须 > 0) | TAS 波束输出间隔门控 |
|
||||
| `Height` | 输出 z = 航迹高度 + 平台高度 |
|
||||
| `Sys_delay` | 输出位置做 x + vx·Sys_delay 的系统延迟补偿 |
|
||||
| `Model1/2/3_Q_slow/fast` | 三个 Singer 模型的过程噪声强度(分档见 5.2) |
|
||||
| `track_prohibite_area_*`(≤30 组) | 起批屏蔽区(距离 m + 方位 °) |
|
||||
| `TAS_prohibite_area_*`(≤30 组) | TAS 禁止区 |
|
||||
> 旧 `RadarPara` 中实现未使用的字段(`R_TAS_max/min`、`V_TAS_max/min`、`TAS_Height_1~5`、`cfar_th`、`north_angle`、`Beam_num`)未进入 `RDP_parameter`,TAS 自动开启/退出逻辑仍保持停用。
|
||||
|
||||
> `track_start_threshold / track_asso_threshold / track_asso_threshold_tas` 三个字段**当前未被实现读取**,实际门限来自编译期宏。
|
||||
### 9.2 编译期常量(`parameters.h` 保留)
|
||||
|
||||
| 常量 / 宏 | 值 | 说明 |
|
||||
|---|---|---|
|
||||
| `MAX_TRACK_NUM` | 500 | 输出航迹数组固定容量(宿主接口维度) |
|
||||
| `RDP_TAS_QUEUE_CAPACITY` | 30 | TAS 队列容量上限;运行期上限用 `tas_track_num_max` |
|
||||
| `PI` / `Round(x)` | 3.1415926f / 宏 | 圆周率 / 四舍五入取整 |
|
||||
| 版本宏 | 1.5.9 + `GENERIC` | `VERSION_STRING` → `V1.5.9.GENERIC`(机扫/相扫合并后无体制后缀) |
|
||||
| `RDP_LOG_TRACE` / `DEBUG` / `INFO` / `WARN` / `ERROR` / `CRITICAL` | 宏(映射 spdlog) | 日志级别宏(fmt 风格格式化),`RDP_LOG_ERR` 为 `RDP_LOG_ERROR` 别名;低于 `SPDLOG_ACTIVE_LEVEL` 的调用在编译期被剔除(实参不求值) |
|
||||
| `RDP_LOG_DIR` / `RDP_RECORD_*` | `.\rdp_logs\` / — | 日志文件目录(`rdp_YYYYMMDD_HHMMSS.log`,每次 `GetB()` 一个文件,同秒冲突追加 `_1`~`_999`);数据记录开关与性能参数。日志**无** `RDP_LOG_ENABLE` 开关,级别由 CMake 选项 `RDP_LOG_LEVEL` → `SPDLOG_ACTIVE_LEVEL` 在编译期控制(见 README 4.3、10.4) |
|
||||
|
||||
> 构建期扫描体制宏已随「机扫/相扫版本合并」移除:`CMakeLists.txt` 不再有 `RADAR_SCAN_MODE`,也不再定义 `PHASE_SCANNING` / `MECHANICAL_SCANNING`;`parameters.h` 的 `VERSION_INFO` 固定为 `GENERIC`(`V1.5.9.GENERIC`)。判圈由配置项 `scan_mode` 决定(见 2.2);`UNCONF_TARGET` 宏已删除(起批处 `Target_Type` 直接写 0)。
|
||||
|
||||
---
|
||||
|
||||
@@ -598,18 +610,24 @@ flowchart TD
|
||||
**容量与边界**
|
||||
|
||||
- 点迹数上限 150(前处理已限幅);航迹数/航迹号上限 500,全满时新目标不再起批;
|
||||
- 屏蔽区/禁止区的组数按 30 组数组定义,配置数量未做上限校验;
|
||||
- 屏蔽区/禁止区的组数按 30 组数组定义;配置入口已由加载器强制 `*_num ∈ [0,30]` 且 4 个数组长度等于 `*_num`(非法配置在 `parameters_reload` 阶段即失败),内部实现不再二次校验;
|
||||
- 高度平滑缓存只增不减,长时运行下每条航迹的内存占用会持续增长。
|
||||
|
||||
**外推、消亡与输出门控(50e0b5c / fc49707 / 42b598e / e8c56f9 / V1.5.8)**
|
||||
|
||||
- **外推圈数上限**:TWS 只在 `Extrapolate_round <= MAX_EXTRAPOLATE_NUM`(3)时预测并输出;TAS 相反,`Extrapolate_round >= MAX_EXTRAPOLATE_NUM_TAS`(相扫 5 / 机扫 48)时才预测并输出。两处比较方向不一致,此处仅如实记录,**不判定为缺陷**;
|
||||
- **普通 TWS 航迹消亡改按质量(V1.5.8)**:`Quality < TRACK_DIE_QUALITY`(0.3)即消亡,`TRACK_DIE_ROUND`(6)不再被引用;重点航迹仍按 `> TRACK_DIE_ROUND_PRIMARY`(10)、TAS 仍按 `> TRACK_DIE_ROUND_TAS`(相扫 20 / 机扫 96);`manual_delete_flag == 1` 单独判为消亡;
|
||||
- **外推圈数上限**:TWS 只在 `Extrapolate_round <= extrapolate_max`(配置项 3)时预测并输出;TAS 相反,`Extrapolate_round >= extrapolate_max_tas`(配置项,样例相扫 5 / 机扫需配 48)时才预测并输出。两处比较方向不一致,此处仅如实记录,**不判定为缺陷**;
|
||||
- **普通 TWS 航迹消亡改按质量(V1.5.8)**:`Quality < track_die_quality`(配置项 0.3)即消亡,`track_die_round`(配置项 6)不再被引用;重点航迹仍按 `> track_die_round_pramary`(配置项 10)、TAS 仍按 `> track_die_round_tas`(配置项;样例相扫 20 / 机扫需配 96);`manual_delete_flag == 1` 单独判为消亡;
|
||||
- **航迹质量(V1.5.8)**:内部 0~1,临时航迹每延长一点按「距离+SNR」更新、关联实点按「距离+SNR+有效点占比」平滑更新、未关联期间按 `Unupdated_time` 二次衰减;起批需达按长度分级的最低质量;输出 `Track.Quality` 为 0~100(×100)。TAS 通道不维护质量;
|
||||
- **输出门控**:TWS 与 TAS 的输出航迹循环都新增 `Track_Update_Flag == 1` 判定并在输出后清零,只输出**本圈关联到实点或成功外推**的航迹(此前 TWS 每圈把航迹表中全部可靠航迹各输出一行);
|
||||
- **接近/远离判据**(fc49707 + 42b598e):`Flag_Direction` 不再用实测径向速度 `vr_point > 0`(受多普勒速度模糊影响符号可能取反),改为用 IMM 融合速度方向角判定——`Direction_Angle = atan2(X[4], X[1])`(弧度转度后输出到 `Track.Direction_Angle`),`x`、`y` 为经 `Work_Parameter.Sys_delay` 补偿后的位置、`r` 为其模,`acos((-x·cosθ - y·sinθ)/r) < π/2` 判为靠近(1)、否则远离(0),等价于"速度方向与雷达→目标位置方向夹角 > 90°"。中间版本 fc49707 曾写为 `acos((-x·vx - y·vy)/r/Range_V) < π/4`(按合速度归一化、门限 45°),42b598e 改为上式;
|
||||
- **接近/远离判据**(fc49707 + 42b598e):`Flag_Direction` 不再用实测径向速度 `vr_point > 0`(受多普勒速度模糊影响符号可能取反),改为用 IMM 融合速度方向角判定——`Direction_Angle = atan2(X[4], X[1])`(弧度转度后输出到 `Track.Direction_Angle`),`x`、`y` 为经配置项 `radar_system_delay` 补偿后的位置、`r` 为其模,`acos((-x·cosθ - y·sinθ)/r) < π/2` 判为靠近(1)、否则远离(0),等价于"速度方向与雷达→目标位置方向夹角 > 90°"。中间版本 fc49707 曾写为 `acos((-x·vx - y·vy)/r/Range_V) < π/4`(按合速度归一化、门限 45°),42b598e 改为上式;
|
||||
- **归一化幅度 / 速度维 FFT 点数(e8c56f9 新增,输入侧已打通)**:`DataRev` 新增 `amp_asv`(归一化幅度 dB)、`prn_fftn`(速度维 FFT 点数),`Track`/内部 `PointRecv`/`Trust_Track` 也新增同名成对字段;`data_preprocess()` 现已在 TWS 与 TAS 两条分支都把这两个字段搬入 `PointRecv`(并随 `RdpRecPoint` 落库),因此 `track_asso.cpp` 写入航迹并在 TWS 输出行写回的 `amp_asv_point` / `prn_fftn_point` 有有效数据来源;**TAS 的关联与输出路径(`track_asso_tas.cpp`)仍未涉及这两个字段**,TAS 输出行中它们保持宿主原值。
|
||||
- **版本号与记录标识(b227ae1 / c3c7e66 / 5cb67cc)**:`parameters.h` 新增版本宏并拼装为 `VERSION_STRING`(当前 `V1.5.8.PS` / `V1.5.8.MS`),实例创建后可由基类公开成员 `version_info` 读取;`Data_Process` 新增 `point_id` / `frame_id` / `circle_id` 自增计数(`circle_id` 在 TWS 每判满一圈、TAS 每帧末自增),`PointRecv`/`Temp_track`/`Trust_Track` 新增对应的标识字段,记录功能据此把「输入点迹 → 凝聚点迹 → 临时航迹 → 可靠航迹 → 关联信息」串成同一条链路;可靠航迹与关联信息的记录点为 TWS/TAS 双通道(关联信息以 `Asso_Info::tas_flag` 区分来源)。
|
||||
- **版本号与记录标识(b227ae1 / c3c7e66 / 5cb67cc)**:`parameters.h` 新增版本宏并拼装为 `VERSION_STRING`(机扫/相扫合并后为 `V1.5.9.GENERIC`;历史上 b227ae1 时为 `V1.5.8.PS` / `V1.5.8.MS`),实例创建后可由基类公开成员 `version_info` 读取;`Data_Process` 新增 `point_id` / `frame_id` / `circle_id` 自增计数(`circle_id` 在 TWS 每判满一圈、TAS 每帧末自增),`PointRecv`/`Temp_track`/`Trust_Track` 新增对应的标识字段,记录功能据此把「输入点迹 → 凝聚点迹 → 临时航迹 → 可靠航迹 → 关联信息」串成同一条链路;可靠航迹与关联信息的记录点为 TWS/TAS 双通道(关联信息以 `Asso_Info::tas_flag` 区分来源)。
|
||||
|
||||
**航迹指纹(V1.5.9,0f3da99)**
|
||||
|
||||
- 起批时对**末点**计算 `Track_fingerprint`(16 bit):`r`/`azi`/`vr`/`RCS`/`CPI_time` 分别量化到 0.01 m / 0.001° / 0.01 m·s⁻¹ / 0.1 dBsm / 1 ms,经 splitmix64 逐个混合后折叠为 16 bit(`track_init.cpp::computeFingerprint()`);
|
||||
- TAS 输入帧的 `DataRev::TAS_track_index` 与 TAS 波束输出的 `TrackingBeam::TAS_track_index` 均改为**指纹**(字段名未变);`track_process(model=3)` 按指纹定位航迹,队列与内部关联仍用 `Track_Index`;
|
||||
- 16 bit 指纹存在碰撞可能,且同一目标消亡后重新起批通常得到不同指纹;记录实体 `RdpRecTrustTrack` 尚未包含该字段(详见 README 9.12、11.2)。
|
||||
|
||||
**未启用**
|
||||
|
||||
@@ -625,11 +643,11 @@ flowchart TD
|
||||
|
||||
| 项 | README 描述(更新前) | 当前源码 |
|
||||
|---|---|---|
|
||||
| 满圈判据 | 当前方位波位号 < 上一帧波位号 | 按体制区分(3e6763c):**相扫**当前波位号 ≤ 上一帧波位号(等价于原判据)、**机扫**方位波位号 == 0(7d5a56f,适应机扫转台反转) |
|
||||
| 满圈判据 | 当前方位波位号 < 上一帧波位号 | 判据由配置项 `scan_mode` 运行时选择(原按体制编译期分支):**scan_mode=0 相扫**当前波位号 ≤ 上一帧波位号(3e6763c,等价于原判据)、**scan_mode=1 机扫**方位波位号 == 0(7d5a56f,适应机扫转台反转) |
|
||||
| 点-航迹头判据 | 按距离分段收紧速度门限(半速、1/2~1/3 速) | 该分支注释停用;改为统一速度区间 **+ 两条多普勒-几何一致性约束**(7d5a56f) |
|
||||
| 起始输出历史点 | "临时航迹的全部 L 个点" | 实际输出**前 `track_start_point_num` 个点** |
|
||||
| 起始输出历史点 | "临时航迹的全部 L 个点" | 实际输出**前 `track_init_point_num_min` 个点** |
|
||||
| 起始输入过滤 | "track_init 跳过 Use_Flag==1 的点" | 上游 `track_asso_process()` 结束前已把 `Use_Flag==1` 的点迹从 `point_recv` 删除;`track_init` 入口(L59-70)也会再剔除一次 `Use_Flag==1` 的点,两道处理叠加,效果一致 |
|
||||
| 日志开关 | "RDP_LOG_ENABLE 默认开启" | `parameters.h` 中该宏当前为**注释状态**(不落盘,日志进控制台) |
|
||||
| 日志开关 | "RDP_LOG_ENABLE 默认开启" | `RDP_LOG_ENABLE` 开关已删除(日志组件改为 spdlog):日志**始终**同时写标准输出与 `.\rdp_logs\` 下按日期+时间命名的日志文件(每次 `GetB()` 一个文件),级别由 CMake 选项 `RDP_LOG_LEVEL` 映射 `SPDLOG_ACTIVE_LEVEL` 在编译期控制(`OFF` 时编译期关闭且不创建日志文件,见 README 4.3、10.4) |
|
||||
| 起始步骤顺序 | "航迹头(1 点)→ 点-航迹头关联(2 点)→ 点-临时航迹关联(≥3 点)→ 起批" | 每圈实际顺序为**点 × ≥2 点临时航迹关联 → 点 × 1 点航迹头关联 → 剩余点转新航迹头 → 长度达标的临时航迹起批**,消亡最后执行(`track_init.cpp` L49-126,见第 6 节) |
|
||||
|
||||
> 后续:README 与 `track_init_readme.md`(航迹起始专项说明)已按上表结论同步(见 CHANGELOG 对应版本的记录);流程落地实现仍以本文为准。
|
||||
|
||||
+20
-15
@@ -6,6 +6,7 @@
|
||||
> 雷达处理线程只承担拷贝与入队开销;写线程跟不上时丢弃数据而不是阻塞处理。
|
||||
> 相关代码:`data_process_class_dll/rdp_record.h`、`rdp_record_entity.h`、`rdp_record.cpp`;
|
||||
> 相关参数:`parameters.h` 的「数据记录性能参数」段;第三方依赖来源见 `third_party/ormpp/UPSTREAM.md`。
|
||||
> 记录模块的日志经 `rdp_log`(内置 spdlog,`third_party/spdlog`)输出,与 DLL 日志同样双写标准输出与 `.\rdp_logs\` 日志文件(分级 info/warn/error,见 README 2、4.3)。
|
||||
|
||||
---
|
||||
|
||||
@@ -36,25 +37,28 @@
|
||||
|---|---|
|
||||
| 开关 | CMake 缓存变量 `RDP_DATA_RECORD`,取值 `AUTO`(默认)/ `ON` / `OFF` |
|
||||
| AUTO 语义 | `Debug` 构建启用、`Release`(及其它)构建关闭 |
|
||||
| 生效方式 | 启用时构建系统定义 `RDP_RECORD_ENABLE` 并额外编译静态库 `rdp_record`(`rdp_record.cpp` + 内置 `sqlite3.c`)并链接进 DLL(`CMakeLists.txt` 118-158) |
|
||||
| 生效方式 | 启用时构建系统定义 `RDP_RECORD_ENABLE` 并额外编译静态库 `rdp_record`(`rdp_record.cpp` + 内置 `sqlite3.c`)并链接进 DLL(`CMakeLists.txt` 的 `if(RDP_RECORD_ENABLED)` 段) |
|
||||
| 关闭时的保证 | 记录模块源码**不参与编译**(`CMakeLists.txt` 的 `if(RDP_RECORD_ENABLED)` 内才 `add_library`/链接);所有记录宏展开为 `((void)0)`(实参不求值,不产生「变量已初始化但未使用」告警);DLL 不新增依赖与导出符号 |
|
||||
|
||||
```bat
|
||||
:: 交付/Release:关闭(AUTO 的默认结果,也可显式 OFF)
|
||||
.vscode\scripts\configure.bat PHASE_SCANNING Release OFF
|
||||
.vscode\scripts\build.bat PHASE_SCANNING Release OFF
|
||||
.vscode\scripts\configure.bat Release OFF
|
||||
.vscode\scripts\build.bat Release OFF
|
||||
|
||||
:: Debug:启用(AUTO 的默认结果,也可显式 ON)
|
||||
.vscode\scripts\configure.bat PHASE_SCANNING Debug ON
|
||||
.vscode\scripts\build.bat PHASE_SCANNING Debug ON
|
||||
.vscode\scripts\configure.bat Debug ON
|
||||
.vscode\scripts\build.bat Debug ON
|
||||
|
||||
:: 一次性「配置 + 构建」单个配置(VS Code 任务「配置并构建(...)」的内部实现)
|
||||
.vscode\scripts\build_config.bat Debug ON
|
||||
|
||||
:: 直接用 cmake(需先进入 MSVC 2022 x64 环境)
|
||||
cmake -S . -B build/X256_PS/Debug/REC_ON -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Debug ^
|
||||
-DRADAR_SCAN_MODE=PHASE_SCANNING -DRDP_DATA_RECORD=ON
|
||||
cmake --build build/X256_PS/Debug/REC_ON
|
||||
cmake -S . -B build/Debug/REC_ON -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Debug ^
|
||||
-DRDP_DATA_RECORD=ON
|
||||
cmake --build build/Debug/REC_ON
|
||||
```
|
||||
|
||||
构建目录按「扫描体制 + 构建类型 + 记录模式」隔离:`build/<X256_PS|X256_MS>/<Debug|Release>/<REC_AUTO|REC_OFF|REC_ON>`,因此同一扫描体制下 REC_OFF 与 REC_ON 的产物可以并存;VS Code 任务「配置并构建全部(...)」与脚本 `build_all.bat`/`clean_all.bat` 均基于该布局(见 README 4.3)。
|
||||
构建目录按「构建类型 + 记录模式」隔离:`build/<Debug|Release>/<REC_AUTO|REC_OFF|REC_ON>`(机扫/相扫已合并为同一版本,无扫描体制维度),因此 REC_OFF 与 REC_ON 的产物可以并存;VS Code 任务「配置并构建(...)」与脚本 `build_config.bat`/`clean_all.bat` 均基于该布局(见 README 4.3)。
|
||||
|
||||
配置阶段会打印开关状态:`数据记录调试功能:启用(C++20 静态库 rdp_record + 内置 SQLite)` 或 `...:关闭(不编译记录模块,RDP_RECORD 为空操作)`。
|
||||
|
||||
@@ -63,7 +67,7 @@ cmake --build build/X256_PS/Debug/REC_ON
|
||||
1. 关闭时构建系统不创建 `rdp_record` 静态库,也不把 `sqlite3.c` 加入任何目标;
|
||||
2. `rdp_record.h` 在未定义 `RDP_RECORD_ENABLE` 时把 `RDP_RECORD_POINTS`/`RDP_RECORD_COHESION_TWS`/`RDP_RECORD_COHESION_TAS`/`RDP_RECORD_TEMP_TRACKS`/`RDP_RECORD_TRUST_TRACKS`/`RDP_RECORD_ASSO_INFOS` 以及遗留的 `RDP_RECORD`/`RDP_RECORD_BATCH` 全部展开为 `((void)0)`,并提供 `Init/Shutdown/db_path/stats/Write` 的内联空实现;
|
||||
3. 6 个业务文件(`data_process.cpp`、`dot_coh.cpp`、`dot_coh_tas.cpp`、`track_init.cpp`、`track_asso.cpp`、`track_asso_tas.cpp`)中的收集变量、收集语句与宏调用**整体包在 `#ifdef RDP_RECORD_ENABLE` 内**;这些文件已不再包含 `rdp_record_entity.h`,因此实体结构体、`arrayToString()` 与 `struct.h` 不会经记录模块被处理代码拖入;
|
||||
4. 实测 8 种配置(PS/MS × Release/Debug × REC_ON/OFF)均 0 warning、0 error,且 OFF 构建的 DLL 不新增依赖/导出符号。
|
||||
4. 实测 4 种配置(Release/Debug × REC_ON/OFF)均 0 warning、0 error(机扫/相扫合并后不再有 PS/MS 维度),且 OFF 构建的 DLL 不新增依赖/导出符号。
|
||||
|
||||
**语言标准与依赖**:记录模块以 **C++20** 独立编译(ormpp 要求 `CMAKE_CXX_STANDARD 20`),并编译内置的 SQLite amalgamation(因此启用时需要 C 编译器,随 VS 2022 工具集提供);**雷达处理代码与内置 Eigen 仍为 C++17**,二者通过 `rdp_record.h` 的「`Write<T>` 仅声明 + 显式实例化」跨标准边界:调用方(C++17)不实例化记录实现,实现在 `rdp_record.cpp`(C++20)中显式实例化。V1.5.8 起常规记录点改走**非模板**的 `Write*` 异步入队接口,跨标准边界更简单;模板 `Write<T>` 仅作为遗留同步接口保留。
|
||||
|
||||
@@ -72,7 +76,7 @@ cmake --build build/X256_PS/Debug/REC_ON
|
||||
| 项目 | 说明 |
|
||||
|---|---|
|
||||
| 存放目录 | `RDP_RECORD_DB_DIR`(`parameters.h`,默认 `.\rdp_records\`,相对宿主进程当前工作目录,不存在时自动创建) |
|
||||
| 文件名 | `rdp_record_YYYYMMDD_HHMMSS.db`(**每次运行一个新文件**,与 `rdp_logs` 命名风格一致);同一秒内重复 `Init`(如 Destroy→GetB 重连)或同目录多进程启动时,检测到文件已存在则自动追加 `_1`/`_2`…(最多尝试 999 个后缀),不复用同一库文件 |
|
||||
| 文件名 | `rdp_record_YYYYMMDD_HHMMSS.db`(**每次运行一个新文件**,与 `rdp_logs` 下日志文件(`rdp_YYYYMMDD_HHMMSS.log`)的命名风格一致);同一秒内重复 `Init`(如 Destroy→GetB 重连)或同目录多进程启动时,检测到文件已存在则自动追加 `_1`/`_2`…(最多尝试 999 个后缀),不复用同一库文件 |
|
||||
| 初始化 | `Data_Process_Factory::GetB()` 内调用 `rdp_record::Init()`:建目录 → 生成文件名 → 打开 SQLite → 应用 PRAGMA → **启动记录写线程** → 置 ready;已初始化时幂等,初始化失败返回 `false` 且不抛异常 |
|
||||
| 写线程 | 独立 `std::thread`,Windows 下用 `SetThreadPriority(..., THREAD_PRIORITY_BELOW_NORMAL)` 把 CPU 优先级降到后台(I/O 保持正常优先级,避免排空过慢);写线程是 SQLite 连接的主要使用者,处理线程不直接访问数据库(遗留同步接口经 `db_mtx` 与写线程串行化) |
|
||||
| 队列与丢弃 | 处理线程把「一种表的一批源结构体」放进有界 `std::deque`;上限为 `RDP_RECORD_QUEUE_CAP_BYTES`(默认 64 MB,按容器容量近似统计)且 `RDP_RECORD_QUEUE_MAX_BATCHES`(默认 4096 批)。任一上限将超出时**整批丢弃**并累计 `dropped_rows`,不阻塞处理线程;丢弃日志按 `RDP_RECORD_DROP_LOG_INTERVAL`(默认每 1024 行)限频提示 |
|
||||
@@ -223,7 +227,7 @@ void WriteTrackHeights(std::vector<Trust_Track> v); //RdpRecTrackHeight
|
||||
|
||||
| # | 记录点 | 位置(文件:行) | 记录宏 | 表 | 说明 |
|
||||
|---|---|---|---|---|---|
|
||||
| ① | 输入点迹(TWS + TAS) | `data_process.cpp:70`(TWS 分支,判圈 return 之前)/ `data_process.cpp:160`(TAS 分支,`circle_id++` 之前) | `RDP_RECORD_POINTS` | `RdpRecPoint` | TWS/TAS **共用一张表**,用 `point_type` 区分;每个输入点迹一行,含 `point_id` / `frame_id` / `circle_id` 三个链路标识 |
|
||||
| ① | 输入点迹(TWS + TAS) | `data_process.cpp:75`(TWS 分支,判圈 return 之前)/ `data_process.cpp:153`(TAS 分支,`circle_id++` 之前) | `RDP_RECORD_POINTS` | `RdpRecPoint` | TWS/TAS **共用一张表**,用 `point_type` 区分;每个输入点迹一行,含 `point_id` / `frame_id` / `circle_id` 三个链路标识 |
|
||||
| ② | TWS 凝聚后点迹 | `dot_coh.cpp:239`(`data_input_buff` 输出到 `point_recv` 的循环之后) | `RDP_RECORD_COHESION_TWS` | `RdpRecPointCohesionTWS` | 两帧滑窗凝聚后进入关联的点迹 |
|
||||
| ③ | TAS 凝聚后点迹 | `dot_coh_tas.cpp:82`(输出到 `point_recv_tas` 的循环之后) | `RDP_RECORD_COHESION_TAS` | `RdpRecPointCohesionTAS` | 单帧凝聚后的 TAS 点迹 |
|
||||
| ④ | 临时航迹 | `track_init.cpp:117`(TWS 圈内**转为可靠航迹之前**,清空点迹之后、`tmp_track_to_trust_track()` 之前,双层遍历 `temp_track` 收集) | `RDP_RECORD_TEMP_TRACKS` | `RdpRecTempTrack` | 每条临时航迹的每个点一行,含 `temp_track_id` / `point_id` / `circle_id`;记录时机于 e982804 前移,因此表中包含**本圈参与起始判定但可能随即起批转走**的临时航迹(原先在消亡之后记录会丢失这部分数据) |
|
||||
@@ -232,7 +236,7 @@ void WriteTrackHeights(std::vector<Trust_Track> v); //RdpRecTrackHeight
|
||||
|
||||
> 表清单即上表 ①②③④⑤⑥ 六张在用表。① 在判圈判定之前入队,因此即使本帧触发判圈/返回,输入点迹也不会漏记;②③④⑤ 在收集循环结束后一次入队;⑥ 按分块上限多次入队并在函数返回前 flush。
|
||||
|
||||
**链路串联**:①→②/③(点迹 → 凝聚后点迹)按 `point_id` 关联;②/③→④/⑤ 按 `point_id` 与 `circle_id` 关联;④→⑤ 通过起批时透传的 `track_id`(取自临时航迹末点的 `temp_track_id`)关联;⑤→⑥ 按 `track_id` 与 `point_id` 关联。TWS 与 TAS 可先用 `RdpRecPoint.point_type`、`RdpRecTrustTrack.Track_Mode`、`RdpRecAssoInfo.tas_flag` 三处之一区分通道。`circle_id` 在 TWS 每判满一圈(相扫为波位号不再增大、机扫为波位号 0,见 README 7.2)、TAS 每帧处理结束时自增,可用来划分「第几圈/第几帧」。
|
||||
**链路串联**:①→②/③(点迹 → 凝聚后点迹)按 `point_id` 关联;②/③→④/⑤ 按 `point_id` 与 `circle_id` 关联;④→⑤ 通过起批时透传的 `track_id`(取自临时航迹末点的 `temp_track_id`)关联;⑤→⑥ 按 `track_id` 与 `point_id` 关联。TWS 与 TAS 可先用 `RdpRecPoint.point_type`、`RdpRecTrustTrack.Track_Mode`、`RdpRecAssoInfo.tas_flag` 三处之一区分通道。`circle_id` 在 TWS 每判满一圈(由配置项 `scan_mode` 选择判据:0 相扫为波位号不再增大、1 机扫为波位号 0,见 README 7.2)、TAS 每帧处理结束时自增,可用来划分「第几圈/第几帧」。
|
||||
|
||||
**字段速查**(字段顺序即建表列顺序;首列均为 `int64_t id` 自增主键):
|
||||
|
||||
@@ -243,11 +247,12 @@ void WriteTrackHeights(std::vector<Trust_Track> v); //RdpRecTrackHeight
|
||||
|
||||
各表的字段为**调试用途**,按第 6 节增删即可,不影响框架。注意记录点中的字段错误只在启用记录功能的构建中才会被检查(关闭时宏丢弃实参)。
|
||||
|
||||
**三点使用提醒**:
|
||||
**四点使用提醒**:
|
||||
|
||||
- `RdpRecTrustTrack::Track_Update_Flag` **恒为 0**(TWS 与 TAS 两处记录点相同):写库发生在输出分支内,而该分支入口已先把标志清零(`track_asso.cpp` / `track_asso_tas.cpp` 均先置 0、再输出、最后收集)。判断「某航迹本圈是否更新」应改用该表是否出现对应 `track_id` / `circle_id` 的记录,或改用 `RdpRecAssoInfo::min_d_flag`。
|
||||
- `Range` / `Azimuth` / `Elevation` / `Velocity` 四个内部快照**只在航迹当圈有输出时被回填**(两通道均在收集前回填),长期未输出(外推超限)的航迹在表中为 0 或旧值。
|
||||
- **TAS 关联信息的三个字段为占位值**:`track_asso_tas.cpp` 收集的候选把 `R` / `VT` 置 0(TAS 关联只用统计距离与高度两个门限),并把 `thd_r` 恒置 true;`track_index` 取 TAS 目标批号 `tas_track_idx`(不是航迹表下标,TWS 行才是下标)。分析 TAS 行时不要按 TWS 的语义解读这三项。
|
||||
- **记录表未含航迹指纹(V1.5.9)**:`Trust_Track::Track_fingerprint`(16 bit,TAS 输入/输出用它定位航迹)**尚未加入** `RdpRecTrustTrack` 实体;如需按指纹复盘 TAS 命令,按第 6 节配方给实体加字段并在 `rdp_record.cpp` 注册即可(同 `Quality` 的加法)。
|
||||
|
||||
## 8. 查询与分析
|
||||
|
||||
@@ -317,7 +322,7 @@ print(con.execute("""
|
||||
|
||||
| 项目 | 结果 |
|
||||
|---|---|
|
||||
| 构建 | 8 种配置(PS/MS × Release/Debug × REC_OFF/REC_ON)均 **0 warning、0 error** |
|
||||
| 构建 | 8 种配置(PS/MS × Release/Debug × REC_OFF/REC_ON,机扫/相扫合并前;合并后为 4 种配置)均 **0 warning、0 error** |
|
||||
| 普通帧开销 | dup 场景(6 圈/200 目标/每帧 150 点/100 波位)REC_ON vs REC_OFF:每帧 `total` 新增 **p99 ≈ 0.09~0.11 ms**(验收阈值 ≤ 1 ms,PASS) |
|
||||
| 判圈帧开销 | 同场景判圈帧 `track` 新增 **中位 ≈ 2.9~4.1 ms、p99 ≈ 13~24 ms**(多次验收运行)——已超 5 ms 验收阈值,因未采用 P0-a(单圈约 12.3 万关联候选),属压测上限;受控 50 目标/50 点场景判圈帧新增中位约 3 ms、**最大 5.96 ms** |
|
||||
| 端到端耗时 | 同场景 600 帧:ON wall ≈ 2.1 s vs OFF wall ≈ 1.8 s |
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 数据记录调试功能(RDP_DATA_RECORD)性能瓶颈分析与实时性优化报告
|
||||
|
||||
> **项目**:雷达数据处理动态库 `data_process_class_dll`(V1.5.8.PS / V1.5.8.MS,分支 `feature`,提交 `3dee734`)
|
||||
> **项目**:雷达数据处理动态库 `data_process_class_dll`(V1.5.8.PS / V1.5.8.MS,分支 `feature`,提交 `3dee734`)——本报告为**机扫/相扫合并前**的快照;合并后版本串为 `V1.5.9.GENERIC`,构建目录与产物路径见 README 4.3。
|
||||
> **问题**:Release 构建下,`RDP_DATA_RECORD=ON` 开启数据记录调试功能后,数据处理不能实时完成;`RDP_DATA_RECORD=OFF` 时满足实时性。
|
||||
> **本报告范围**:定位性能瓶颈、给出可落地的优化措施与验收标准。**本阶段不修改任何业务代码**——报告中的代码改动均为"建议方案",未实施(业务代码/CMake/第三方均无改动,仅新增本报告文件)。
|
||||
> **方法**:源码走读 + 数据量建模 + Release 产物微基准 + `REC_ON`/`REC_OFF` 双 DLL 端到端 A/B 实测。
|
||||
@@ -38,7 +38,7 @@
|
||||
|
||||
### 1.1 现象
|
||||
|
||||
| 配置(均为 Release / X256_PS) | 现象 |
|
||||
| 配置(均为 Release;使用当时的 X256_PS 构建,现机扫/相扫已合并,等价产物在 `build/Release/REC_*`) | 现象 |
|
||||
|---|---|
|
||||
| `RDP_DATA_RECORD=OFF`(交付/AUTO 默认) | 数据处理满足实时性 |
|
||||
| `RDP_DATA_RECORD=ON`(显式开启,用于 Release 现场排障) | 数据处理不能实时完成,且负载越密(点迹/航迹越多)越慢,最终表现为处理节拍被拖垮、数据积压 |
|
||||
@@ -57,7 +57,7 @@
|
||||
|
||||
由于没有宿主主程序,本次用两条独立证据链复现与量化:
|
||||
|
||||
1. **微基准**(`build/perf_bench_micro/`):直接链接已有 Release `REC_ON` 产物 `build/X256_PS/Release/REC_ON/lib/rdp_record.lib`,调用与业务完全相同的 `rdp_record::Write<T>()`,测单条/批量/不同 SQLite 配置的每行成本;并单独测实体构造(`arrayToString`)成本。
|
||||
1. **微基准**(`build/perf_bench_micro/`):直接链接已有 Release `REC_ON` 产物 `build/Release/REC_ON/lib/rdp_record.lib`(本报告测量时位于旧布局 `build/X256_PS/Release/REC_ON/lib/`;机扫/相扫合并后为 `build/Release/REC_ON/lib/`),调用与业务完全相同的 `rdp_record::Write<T>()`,测单条/批量/不同 SQLite 配置的每行成本;并单独测实体构造(`arrayToString`)成本。
|
||||
2. **端到端 A/B**(`build/perf_bench_pipeline/`):编写只使用公开 DLL 接口的合成数据驱动程序,分别加载 `REC_OFF` 与 `REC_ON` 两个 `data_process_class_dll.dll`,按宿主调用序列(`data_preprocess` → 按返回的 `model` 调 `track_process`)投喂同一份合成 TWS 数据,用 `QueryPerformanceCounter` 逐帧计时。
|
||||
|
||||
> 证据链互相印证:微基准单条写入 mean 约 1.1~1.3 ms/行(p50 约 0.73~0.78 ms);首轮端到端 A/B 全运行 103,374 行 / ≈97.6 s 增量 ≈ **0.94 ms/行**;含航迹最终 A/B 为 314,388 行 / 426.7 s ≈ **1.39 ms/行**(大库/多表/页缓存压力下略高),三条口径一致。
|
||||
|
||||
+85
-81
@@ -45,16 +45,18 @@ flowchart LR
|
||||
调用点(`data_process.cpp`):
|
||||
|
||||
```cpp
|
||||
// L130-161,仅 model==1(TWS 满一圈)时执行
|
||||
// track_process()(data_process.cpp L167 起),仅 model==1(TWS 满一圈)时执行
|
||||
track_asso.track_asso_process(&point_recv, &trust_track, ...); // 入口删除 Use_Flag==1 的点
|
||||
track_die.track_die_process(&trust_track, ...);
|
||||
track_init.track_init_process_logic(&point_recv, &trust_track, &temp_track,
|
||||
Trust_Track_Output, Trust_track_num_Output, Work_Parameter);
|
||||
Trust_Track_Output, Trust_track_num_Output);
|
||||
```
|
||||
|
||||
> 工作参数为**全局对象 `rdp_parameter`**(`struct.h` 中 `extern` 声明、`data_process.cpp` 定义并零初始化),由公开接口 `parameters_reload(配置文件路径)` 从 TOML 配置文件加载;各模块直接以 `rdp_parameter.<field>` 访问,不再作为函数形参逐层传递(参数来源见 README 第 10 节;改造前为宿主下发 `RadarPara` + `parameters.h` 宏,更早则由 `const RDP_parameter&` 形参传入)。
|
||||
|
||||
### 1.2 调用频率 = 扫描圈
|
||||
|
||||
`data_preprocess` 判为一圈完成时返回 1(`data_process.cpp`),宿主据此调用 `track_process(model=1)`。判据按扫描体制区分(3e6763c):**相扫**比较前后两帧方位波位号(当前 ≤ 上一帧即满圈,`last_beam_num` 参与判断,清空后/首帧即满圈),**机扫**判 `Data_Input[0].Beam_index_aiz == 0`(详见 README 7.2 与 `docs/data_process_flow_report.md` 2.2 节)。
|
||||
`data_preprocess` 判为一圈完成时返回 1(`data_process.cpp`),宿主据此调用 `track_process(model=1)`。判据由配置项 `[radar_params] scan_mode` **运行时**选择(0 相扫 / 1 机扫;替代原编译期 `PHASE_SCANNING`/`MECHANICAL_SCANNING` 分支):**scan_mode=0** 比较前后两帧方位波位号(当前 ≤ 上一帧即满圈,`last_beam_num` 参与判断,清空后/首帧即满圈);**scan_mode=1** 判 `Data_Input[0].Beam_index_aiz == 0`(详见 README 7.2 与 `docs/data_process_flow_report.md` 2.2 节)。
|
||||
因此 **`track_init_process_logic()` 每次调用对应 TWS 一圈**,`Temp_track::buff_round`(已缓存圈数)正是以「调用次数」为单位累加的。
|
||||
|
||||
### 1.3 类接口
|
||||
@@ -69,7 +71,7 @@ track_init.track_init_process_logic(&point_recv, &trust_track, &temp_track,
|
||||
| `tmp_track_die(...)` | private | 临时航迹消亡 |
|
||||
| `alpha_cal_track_init(...)` | private | 三点折角(度) |
|
||||
| `track_init_prohibit(...)` | private | 起批屏蔽区判断 |
|
||||
| `track_index_mangement` | private | 航迹号(1~500)分配器 |
|
||||
| `track_index_mangement` | private | 航迹号(1~`track_index_max`,默认 500)分配器 |
|
||||
|
||||
> `temp_track`(`std::vector<std::vector<Temp_track>>`)**不是** `Track_Init` 的成员,而是 `Data_Process::temp_track`,由调用方传入并跨圈保持;`track_clear_all()` 中清空(`data_process.cpp` L233),`Track_Init::reset()` 只清自己的 `point_process`。
|
||||
|
||||
@@ -114,9 +116,9 @@ track_init.track_init_process_logic(&point_recv, &trust_track, &temp_track,
|
||||
| `temp_track` | 输入/输出 | 临时航迹表(跨圈状态) |
|
||||
| `Trust_Track_Output[MAX_TRACK_NUM][10]` | 输出 | 与 `track_asso` **共用同一数组与同一计数**;起始流程在 `track_asso` 已写入的行之后追加新航迹的历史点 |
|
||||
| `Trust_track_num_Output` | 输出 | 累计行数(track_asso 输出 N 行后,track_init 在其后继续追加) |
|
||||
| `Work_Parameter` | 输入 | 运行时雷达参数(`RadarPara`) |
|
||||
| 全局 `rdp_parameter` | 读取 | 工作参数(TOML 配置加载的全局对象,`struct.h` 声明、`data_process.cpp` 定义);**不再经函数形参传入**,模块直接访问 |
|
||||
|
||||
> **输出行数核算**:`track_asso` 先为当圈**有更新**的可靠航迹写 N 行(输出循环判 `Track_Update_Flag==1` 并清零,即只含关联到实点或成功外推的航迹);`track_die` 随后删除 D 条(已写出的行与计数不变);起始流程每次起批前判 `trust_track->size() < MAX_TRACK_INDEX(500)`,故最多再追加 `499 − N + D` 行,**合计最多 499 + D 行**。正常情况下 D 很小,远小于 500;但理论上当 D ≥ 2 且当圈起批数量恰好占满配额(需数百条同时起批)时,行号会超过第一维容量 500,存在越界写入风险(极端场景,见 15.14)。第二维 `[j]`(j < L ≤ 10)始终安全。代价是可靠航迹数达 500 时不再允许新起批。
|
||||
> **输出行数核算**:`track_asso` 先为当圈**有更新**的可靠航迹写 N 行(输出循环判 `Track_Update_Flag==1` 并清零,即只含关联到实点或成功外推的航迹);`track_die` 随后删除 D 条(已写出的行与计数不变);起始流程每次起批前判 `trust_track->size() < track_index_max`(配置项,默认 500),故最多再追加 `499 − N + D` 行,**合计最多 499 + D 行**。正常情况下 D 很小,远小于 500;但理论上当 D ≥ 2 且当圈起批数量恰好占满配额(需数百条同时起批)时,行号会超过第一维容量 500,存在越界写入风险(极端场景,见 15.14)。第二维 `[j]`(j < L ≤ 10)始终安全。代价是可靠航迹数达 `track_index_max` 时不再允许新起批。
|
||||
|
||||
---
|
||||
|
||||
@@ -157,7 +159,7 @@ for (size_t i=0;i<(*point_recv).size();i++)
|
||||
```
|
||||
|
||||
- 上游 `track_asso` 已把关联上的点从 `point_recv` 中删除,所以这里进来的都是「未关联点」;函数内部**不再**用 `Use_Flag` 过滤输入(`Use_Flag` 只用于本次调用内部两阶段关联的标记)。
|
||||
- 点迹的 `Range` 已在凝聚阶段做过 `[R_MIN=100, R_MAX=100000] m` 量程过滤。
|
||||
- 点迹的 `Range` 已在凝聚阶段做过量程过滤(下限/上限为配置项 `point_cohesion_range_min` / `point_cohesion_range_max`,默认 100 m / 100000 m)。
|
||||
|
||||
### 4.2 buff_round 自增(L40-47)
|
||||
|
||||
@@ -308,13 +310,13 @@ $$
|
||||
|
||||
| 条件 | 含义 |
|
||||
|---|---|
|
||||
| `dis ≤ V_max·ΔT` | 平均速度不超过最大速度(`V_max = Work_Parameter.V_MAX`) |
|
||||
| `dis ≤ V_max·ΔT` | 平均速度不超过最大速度(`V_max = rdp_parameter.track_init_velocity_max`,配置项) |
|
||||
| `dis ≥ V_min·ΔT` | 平均速度不低于最小速度(滤除静止/慢速点) |
|
||||
| `v_r,p · v_r,h > 0` | 两点多普勒径向速度**同号**(同接近/同远离) |
|
||||
| `dis ≥ abs(v_r,p)·ΔT` | 位置变化不小于**点迹自身多普勒**在这段时间内的径向位移(多普勒-几何一致性) |
|
||||
| `dis ≥ abs(v_r,h)·ΔT` | 同上,用航迹头的多普勒约束 |
|
||||
|
||||
**近/远程分支(L348-356)当前是死分支**:`work_mode==0` 与 `else` 都取 `vmax = Work_Parameter.V_MAX`(注释"近程模式最大速度减小一点"未生效;原 `V_MAX/5` 写法仅保留在未启用的 `track_init_direct_tracking.cpp` 注释里)。
|
||||
**近/远程分支(L348-356)当前是死分支**:`work_mode==0` 与 `else` 都取 `vmax = rdp_parameter.track_init_velocity_max`(注释"近程模式最大速度减小一点"未生效;原按 `V_MAX/5` 收紧的写法仅保留在未启用的 `track_init_direct_tracking.cpp` 注释里)。
|
||||
|
||||
**ΔT 语义**:判据隐含要求 `ΔT > 0`(若 `ΔT < 0`,则 `dis ≤ 负数` 必假)。`ΔT == 0` 时仅在 `dis == 0`(同时间同位置的点)才可能成立,属退化情形。
|
||||
|
||||
@@ -382,8 +384,8 @@ double range = (*Iter)[L-1].r;
|
||||
double azi = (*Iter)[L-1].azi/PI*180; // 转度,供屏蔽区比较
|
||||
double quality = (*Iter)[L-1].quality; // V1.5.8:末点航迹质量
|
||||
|
||||
if( L <= 10 && L >= Work_Parameter.track_start_point_num
|
||||
&& track_init_prohibit(range, azi, Work_Parameter) == 0
|
||||
if( L <= 10 && L >= rdp_parameter.track_init_point_num_min
|
||||
&& track_init_prohibit(range, azi) == 0
|
||||
&& quality >= 按长度取的质量门限 ) // V1.5.8 新增
|
||||
{ 复制到 Track_to_start; 从 temp_track 中 erase; }
|
||||
else
|
||||
@@ -392,18 +394,18 @@ else
|
||||
|
||||
**入选条件**:
|
||||
|
||||
1. **长度**:`track_start_point_num ≤ L ≤ 10`(`track_start_point_num` 由宿主经 `track_process_parameters_initial` 传入,接口层校验范围 **3~9**,`data_process.cpp`);
|
||||
1. **长度**:`track_init_point_num_min ≤ L ≤ 10`(配置项 `[track_init] track_init_point_num_min`,由 TOML 配置文件提供,加载器校验范围 **3~9**;改造前由宿主经 `track_process_parameters_initial` 传入);
|
||||
2. **不在起批屏蔽区**内(用**末点**的距离/方位判断,见 8.5);
|
||||
3. **航迹质量达标(V1.5.8,d9a7bd0)**:按长度取门限 `TRACK_START_QUALITY_L3~L7`——L=3 →0.7、L=4 →0.6、L=5 →0.5、L=6 →0.4、L≥7 →0.3;比较对象是**末点** `quality`,不达标则本圈不起批、留在 `temp_track` 继续积累。
|
||||
3. **航迹质量达标(V1.5.8,d9a7bd0)**:按长度取门限 `track_init_quality_L3~L7`(配置项)——L=3 →0.7、L=4 →0.6、L=5 →0.5、L=6 →0.4、L≥7 →0.3;比较对象是**末点** `quality`,不达标则本圈不起批、留在 `temp_track` 继续积累。
|
||||
|
||||
命中即从 `temp_track` 摘出(先复制后删除,`Track_to_start` 成为候选表)。
|
||||
|
||||
> 结果:**一旦长度达到阈值、不落在屏蔽区且末点质量达标,当圈即起批**(没有"连续 M 圈满足才起批"的确认期)。若因屏蔽区被拦住,航迹会继续在 `temp_track` 中增长,直到离开屏蔽区(此时 `L > track_start_point_num`,见 8.3 的副作用)或达到 `L=10` 被判消亡;若因质量门限被拦住,则继续积累直到质量回升或临时航迹消亡。
|
||||
> 结果:**一旦长度达到阈值、不落在屏蔽区且末点质量达标,当圈即起批**(没有"连续 M 圈满足才起批"的确认期)。若因屏蔽区被拦住,航迹会继续在 `temp_track` 中增长,直到离开屏蔽区(此时 `L > track_init_point_num_min`,见 8.3 的副作用)或达到 `L=10` 被判消亡;若因质量门限被拦住,则继续积累直到质量回升或临时航迹消亡。
|
||||
|
||||
### 8.2 步骤 2:同源去重(L504-550)
|
||||
|
||||
```cpp
|
||||
int L = Work_Parameter.track_start_point_num; // 注意:此 L 是"起批点数",不是航迹长度
|
||||
int L = rdp_parameter.track_init_point_num_min; // 注意:此 L 是"起批点数",不是航迹长度
|
||||
for (i: 0 .. size-2) for (j: i+1 .. size-1) {
|
||||
if (两者都未被标记 asso_flag==2) {
|
||||
if (某条长度 < 3) continue; // 防御性判断(实际不会触发)
|
||||
@@ -417,7 +419,7 @@ for (i: 0 .. size-2) for (j: i+1 .. size-1) {
|
||||
```
|
||||
|
||||
- **同源判定**:两条候选航迹的前三个点中,只要有一个点的 `T` 与 `r` 完全相等(同一点迹被复制而来),就视为同源重复;
|
||||
- **择优准则**:比较 `[L-1].d`(第 `track_start_point_num` 个点的关联统计距离),**保留 d 更小者**;
|
||||
- **择优准则**:比较 `[L-1].d`(第 `track_init_point_num_min` 个点的关联统计距离),**保留 d 更小者**;
|
||||
- **标记位置**:只写 `[0].asso_flag = 2`,最后统一擦除;
|
||||
- 去重仅看**前三点**,且无论航迹实际多长都用固定下标 0/1/2。
|
||||
|
||||
@@ -425,7 +427,7 @@ for (i: 0 .. size-2) for (j: i+1 .. size-1) {
|
||||
|
||||
```cpp
|
||||
if (Track_to_start[i].size() < 3) continue; // 防御
|
||||
if (trust_track->size() < MAX_TRACK_INDEX) { // 500 条上限
|
||||
if (trust_track->size() < rdp_parameter.track_index_max) { // 默认 500 条上限
|
||||
int index = track_index_mangement.track_ind_get(trust_track);
|
||||
if (index > 0) { ... }
|
||||
}
|
||||
@@ -439,7 +441,7 @@ T1 = (点[L-2].T − 点[L-3].T)/1000.0; T2 = (点[L-1].T − 点[L-2].T)/1000
|
||||
Kalman.kalman_filter_init_3dots(Z0, Z1, Z2, T1, T2, X, P); // 6 维 [x,vx,ax,y,vy,ay]
|
||||
```
|
||||
|
||||
> ⚠ **不一致点**:初始化用「最后三点」,而输出历史点(8.4)只写「前 `L` 个点」。当航迹长度 = `track_start_point_num` 时两者一致;当因屏蔽区滞留使长度 > 阈值时,输出的历史点里**不包含**用于初始化的那三个点。
|
||||
> ⚠ **不一致点**:初始化用「最后三点」,而输出历史点(8.4)只写「前 `L` 个点」。当航迹长度 = `track_init_point_num_min` 时两者一致;当因屏蔽区滞留使长度 > 阈值时,输出的历史点里**不包含**用于初始化的那三个点。
|
||||
|
||||
**Trust_Track 字段构造(L573-608)**:
|
||||
|
||||
@@ -452,34 +454,35 @@ Kalman.kalman_filter_init_3dots(Z0, Z1, Z2, T1, T2, X, P); // 6 维 [x,vx,ax,y
|
||||
| `T_track` / `GNSS_time` / `Height` | 取**末点**的对应值(`CPI_time` 字段) |
|
||||
| `Amplitude / snr_point / RCS / pitch_num / speed_dim / range_dim` | 末点透传 |
|
||||
| `track_id / point_id / circle_id` | 取末点的 `temp_track_id / point_id / circle_id`(记录链路标识,b227ae1 新增) |
|
||||
| `Track_fingerprint` | **航迹指纹(V1.5.9,0f3da99)**:`computeFingerprint(末点.r, 末点.azi, 末点.vr, 末点.RCS, 末点.CPI_time)`,16 bit(量化 0.01 m / 0.001° / 0.01 m·s⁻¹ / 0.1 dBsm / 1 ms → splitmix64 混合 → 折叠 16 bit),TAS 波束与 TAS 输入均以它定位航迹(见 README 9.12) |
|
||||
| `point_flag = 1` | 实点 |
|
||||
| `Track_Mode = 0` | TWS |
|
||||
| `Target_Type = UNCONF_TARGET(0)` | 未确认目标 |
|
||||
| `Target_Type = 0` | 未确认目标(`UNCONF_TARGET` 宏已删除,直接写 0) |
|
||||
| `Extrapolate_round = 0` | 外推计数清零 |
|
||||
| `approach_flag = -1` | 接近标志复位 |
|
||||
| `manual_delete_flag / manual_tracking_flag / associate_point_number / point_type / primary_target_flag = 0` | 各类标志复位(`Trust_Track trust_track_tmp = {}` 零初始化,故新航迹的重点目标标记为 0) |
|
||||
|
||||
> L606-608 有一处**死代码**:`coor_trans::cart2polar(X[0], X[3], &r, &azi)`(把 `X[3]=vy` 当 y 用),计算结果 `r/azi` 从未被使用。
|
||||
|
||||
**航迹号分配**(`track_index_mangement.cpp`):空表返回 1;否则从 `lastest_index+1` 向上找第一个未被占用的号(含 500),找不到再从 1 回绕;全满返回 0(**不起批**,该候选被静默丢弃)。
|
||||
**航迹号分配**(`track_index_mangement.cpp`):空表返回 1;否则从 `lastest_index+1` 向上找第一个未被占用的号(含 `track_index_max`,默认 500),找不到再从 1 回绕;全满返回 0(**不起批**,该候选被静默丢弃)。
|
||||
|
||||
### 8.4 输出更新航迹(L612-664)
|
||||
|
||||
- `*Trust_track_num_Output += 1`,在 `track_asso` 已写的行之后追加一行;
|
||||
- 对 `j = 0 .. L-1`(`L = track_start_point_num`)写入该航迹的**历史点**:`Track_Index`、`Point_Sum = L`,以及每个点的距离/方位/俯仰、速度、幅度/SNR/RCS、俯仰波位、十字星数据、航向角、时间、直角坐标、原始点信息等(完整映射见第 14 节);
|
||||
- 对 `j = 0 .. L-1`(`L = rdp_parameter.track_init_point_num_min`)写入该航迹的**历史点**:`Track_Index`、`Point_Sum = L`,以及每个点的距离/方位/俯仰、速度、幅度/SNR/RCS、俯仰波位、十字星数据、航向角、时间、直角坐标、原始点信息等(完整映射见第 14 节);
|
||||
- 之后该航迹的后续更新由 `track_asso`(每次 `Point_Sum=1`)负责。
|
||||
|
||||
### 8.5 起批屏蔽区(L699-712)
|
||||
|
||||
```cpp
|
||||
for (i = 0; i < Work_Parameter.track_prohibite_area_num; i++)
|
||||
if (r < R_max_track_prohibited[i] && r > R_min_track_prohibited[i] &&
|
||||
azi < Azimuth_max_track_prohibited[i] && azi > Azimuth_min_track_prohibited[i])
|
||||
for (i = 0; i < rdp_parameter.track_init_prohibite_area_num; i++)
|
||||
if (r < rdp_parameter.track_init_prohibite_area_range_max[i] && r > rdp_parameter.track_init_prohibite_area_range_min[i] &&
|
||||
azi < rdp_parameter.track_init_prohibite_area_azimuth_max[i] && azi > rdp_parameter.track_init_prohibite_area_azimuth_min[i])
|
||||
return 1; // 位于屏蔽区,禁止起批
|
||||
return 0;
|
||||
```
|
||||
|
||||
- 判据是**距离-方位矩形区域**(`azi` 已转成度),最多 30 个(`RadarPara` 数组长度;数量未做上限校验);
|
||||
- 判据是**距离-方位矩形区域**(`azi` 已转成度),最多 30 个(`RDP_parameter` 定长数组长度;配置入口由加载器校验 `*_num ∈ [0,30]` 且 4 个数组长度等于 `*_num`,非法配置在 `parameters_reload` 阶段即失败);
|
||||
- 采用**严格不等号**:恰好落在边界上的点不算屏蔽;
|
||||
- 判断用**末点**极坐标,不使用高度。
|
||||
|
||||
@@ -492,10 +495,11 @@ L = temp_track_ref.size();
|
||||
d_tmp = exp(-temp_track_ref[L-1].d); // 末点统计距离项(d 已是马氏距离平方)
|
||||
// L == 3:取三个点 SNR 的平均;L > 3:只取末点 SNR
|
||||
snr_tmp = 1 / (1 + exp(-(SNR - 16) / 1.2)); // 以 16 dB 为界的 Sigmoid
|
||||
quality_tmp = (0.5 * d_tmp + 0.3 * snr_tmp) / (0.5 + 0.3); // 只用距离 + SNR 两项(不含长度项)
|
||||
// 权重为配置项 track_quality_d_weight(0.5) / track_quality_snr_weight(0.3)
|
||||
quality_tmp = (w_d * d_tmp + w_snr * snr_tmp) / (w_d + w_snr); // 只用距离 + SNR 两项(不含长度项)
|
||||
|
||||
if (L == 3) 三点全部写 quality_tmp; // 质量起点
|
||||
else 末点 quality = 前一点 quality * 0.8 + quality_tmp * 0.2; // 指数平滑
|
||||
else 末点 quality = 前一点 quality * c + quality_tmp * (1 - c); // 指数平滑,c = track_quality_iter_coeff(0.8)
|
||||
```
|
||||
|
||||
- `Temp_track::quality` 是 **0~1** 的归一化值;起批时 `Trust_Track.Quality = 末点 quality`,`Point_sum = Valid_point_sum = L`;
|
||||
@@ -533,28 +537,28 @@ if ((*Iter)[n-1].buff_round > 2 || n >= 10) erase;
|
||||
|---|---|
|
||||
| **0. 输入资格** | 该点未被 `track_asso` 关联(已从 `point_recv` 删除);凝聚后 `100 m ≤ r ≤ 100000 m` |
|
||||
| **1. 生成航迹头** | 未被阶段二/三消费(`Use_Flag==0`);创建时 `buff_round=1`(L77-114) |
|
||||
| **2. 航迹头 → 2 点**(L325, L368-372) | 航迹头 `size==1` 且 `buff_round ≥ 2`;`dis ≤ V_MAX·ΔT`;`dis ≥ V_MIN·ΔT`;`dis ≥ abs(vr_p)·ΔT`;`dis ≥ abs(vr_h)·ΔT`;`vr_p·vr_h > 0`(隐含 `ΔT > 0`) |
|
||||
| **3. 2 点 → 3 点及以上**(L164, L191, L221) | 临时航迹 `size > 1` 且末点 `buff_round ≥ 2`;`abs(r_p − r_末点) ≥ 5 m`;`d ≤ 8`(代码写 `d² ≤ 64`);`α < 60°`;`vr_p·vr_末点 > 0` |
|
||||
| **4. 转可靠航迹**(L484, L556) | `track_start_point_num ≤ L ≤ 10`;末点不在起批屏蔽区;**末点质量 ≥ 按 L 取的门限(V1.5.8)**;同源去重后保留;`trust_track.size() < 500` 且成功分配航迹号 |
|
||||
| **2. 航迹头 → 2 点**(L325, L368-372) | 航迹头 `size==1` 且 `buff_round ≥ 2`;`dis ≤ track_init_velocity_max·ΔT`;`dis ≥ track_init_velocity_min·ΔT`;`dis ≥ abs(vr_p)·ΔT`;`dis ≥ abs(vr_h)·ΔT`;`vr_p·vr_h > 0`(隐含 `ΔT > 0`) |
|
||||
| **3. 2 点 → 3 点及以上**(L164, L191, L221) | 临时航迹 `size > 1` 且末点 `buff_round ≥ 2`;`abs(r_p − r_末点) ≥ 5 m`;`d ≤ 8`(代码写 `d² ≤ 64`,`track_init_threshold=8`);`α < 60°`(`track_init_angle`);`vr_p·vr_末点 > 0` |
|
||||
| **4. 转可靠航迹**(L484, L556) | `track_init_point_num_min ≤ L ≤ 10`;末点不在起批屏蔽区;**末点质量 ≥ 按 L 取的门限(V1.5.8)**;同源去重后保留;`trust_track.size() < track_index_max` 且成功分配航迹号 |
|
||||
| **5. 消亡**(L686) | `末点.buff_round > 2`(连续 2 圈无新点)或 `长度 ≥ 10` |
|
||||
|
||||
### 10.2 门限汇总
|
||||
|
||||
| 判据 | 数值 | 来源 | 可调 |
|
||||
|---|---|---|---|
|
||||
| 最大速度 `V_MAX` | 运行时 | `RadarPara.V_MAX` | ✅ |
|
||||
| 最小速度 `V_MIN` | 运行时 | `RadarPara.V_MIN` | ✅ |
|
||||
| 最大速度 `vmax` | 50 m/s(样例) | 配置项 `track_init.track_init_velocity_max` | ✅(改配置文件) |
|
||||
| 最小速度 `vmin` | 1 m/s(样例) | 配置项 `track_init.track_init_velocity_min` | ✅(改配置文件) |
|
||||
| 距离差下限 `abs(Δr)` | **5 m(硬编码)** | `track_init.cpp` L191 | ❌ |
|
||||
| 起批波门 `d ≤ 8` | `TRACK_START_THRESHOLD=8` | `parameters.h` L49/L98 | 编译期 |
|
||||
| 起批折角 | `ALPHA_START=60°` | `parameters.h` L50/L99 | 编译期 |
|
||||
| 起批点数 | 3~9(典型 3~4) | `RadarPara.track_start_point_num` | ✅ |
|
||||
| 起批波门 `d ≤ 8` | `track_init_threshold=8` | 配置项 `track_init.track_init_threshold` | ✅(改配置文件) |
|
||||
| 起批折角 | `track_init_angle=60°` | 配置项 `track_init.track_init_angle` | ✅(改配置文件) |
|
||||
| 起批点数 | 3~9(典型 3~4) | 配置项 `track_init.track_init_point_num_min` | ✅(加载器校验 3~9) |
|
||||
| 临时航迹长度上限 | 10(硬编码) | L484 / L686 | ❌ |
|
||||
| 允许丢圈数 | 1 圈(`buff_round > 2` 消亡) | L686 | ❌ |
|
||||
| 航迹头上限等待 | 2 圈 | L686(1 点航迹同判据) | ❌ |
|
||||
| 起批屏蔽区 | ≤30 个矩形 | `RadarPara.track_prohibite_area_*` | ✅ |
|
||||
| 起批最低质量(V1.5.8) | L3→0.7 / L4→0.6 / L5→0.5 / L6→0.4 / L≥7→0.3 | `TRACK_START_QUALITY_L3~L7` | 编译期 |
|
||||
| 质量权重(V1.5.8) | 距离 0.5 / SNR 0.3 / 长度 0.2 | `TRACK_QUALITY_*_WEIGHT` | 编译期 |
|
||||
| 航迹数上限 | 500 | `MAX_TRACK_INDEX` | 编译期 |
|
||||
| 起批屏蔽区 | ≤30 个矩形 | 配置段 `[track_init_prohibite_area]`(数组长度 = `*_num`) | ✅(加载器校验 0~30) |
|
||||
| 起批最低质量(V1.5.8) | L3→0.7 / L4→0.6 / L5→0.5 / L6→0.4 / L≥7→0.3 | 配置项 `track_init.track_init_quality_L3~L7` | ✅(改配置文件) |
|
||||
| 质量权重(V1.5.8) | 距离 0.5 / SNR 0.3 / 长度 0.2 | 配置段 `[track_quality]` | ✅(改配置文件) |
|
||||
| 航迹数上限 | 500(默认) | 配置项 `track_index.track_index_max` | ✅(加载器校验 1~500) |
|
||||
|
||||
---
|
||||
|
||||
@@ -615,7 +619,7 @@ $$
|
||||
\alpha=\arccos\!\left(\frac{\vec D_{12}\cdot\vec D_{23}}{\|\vec D_{12}\|\,\|\vec D_{23}\|}\right)\cdot\frac{180}{\pi}
|
||||
$$
|
||||
|
||||
其中 `D12 = (末点 − 前一点)`、`D23 = (新点 − 末点)`,三点均取 **Temp_track 的直角坐标**(`X[0]` 为 x、`X[2]` 为 y)。`α` 越小表示越接近直线;`ALPHA_START=60°` 表示允许的航向偏折。
|
||||
其中 `D12 = (末点 − 前一点)`、`D23 = (新点 − 末点)`,三点均取 **Temp_track 的直角坐标**(`X[0]` 为 x、`X[2]` 为 y)。`α` 越小表示越接近直线;`track_init_angle`(配置项,样例 60°)表示允许的航向偏折。
|
||||
|
||||
> 无 `acos` 定义域钳位:当两点重合(分母为 0)或浮点误差使比值略超 ±1 时结果为 `NaN`,`NaN < 60` 恒为 false → 表现为「该点无法关联」,不崩溃。
|
||||
|
||||
@@ -625,14 +629,14 @@ $$
|
||||
|
||||
### 12.1 与 `README.md` 第 8.3 / 9.3 节的差异(README 已同步本节结论)
|
||||
|
||||
> 说明:下表为本文档成文时发现的差异,当时 README 尚未同步 7d5a56f 的改动;随后已按本节结论更新 README 第 2、7.2、8.1、8.3、9.3、9.6、10.1 等节,「README 描述」列对应的是更新前的旧表述,保留以说明改动缘由。
|
||||
> 说明:下表为本文档成文时发现的差异,当时 README 尚未同步 7d5a56f 的改动;随后已按本节结论更新 README 第 2、7.2、8.1、8.3、9.3、9.6、10.2 等节,「README 描述」列对应的是更新前的旧表述,保留以说明改动缘由。
|
||||
|
||||
| 项 | README 描述(更新前) | 当前源码 |
|
||||
|---|---|---|
|
||||
| 点-航迹头判据 | 按距离分段:近程 `r≥1000` 全速 / `r<1000` 半速;中远程 `r≥2000` 全速 / `r<2000` 1/2~1/3 速 | 该分支已**整体注释停用**,改为统一 `V_MIN·ΔT ≤ dis ≤ V_MAX·ΔT` **外加两条多普勒一致性条件** `dis ≥ abs(vr_p)·ΔT`、`dis ≥ abs(vr_h)·ΔT`(L361-372) |
|
||||
| 起批触发长度 | 长度满足 `track_start_point_num ≤ L ≤ 10` | 一致 |
|
||||
| 初始化用点 | 最近三点 | 不一致:取下标 `track_start_point_num-3 ~ -1` 的三个点,航迹长度大于起批点数时**不是**航迹真正的末三点(详见第 15 节第 2 条) |
|
||||
| 输出历史点 | "将临时航迹的全部 L 个点写入" | 实际只写**前 `track_start_point_num` 个点**(L614 的 `L` 是起批点数,不是航迹长度) |
|
||||
| 点-航迹头判据 | 按距离分段:近程 `r≥1000` 全速 / `r<1000` 半速;中远程 `r≥2000` 全速 / `r<2000` 1/2~1/3 速 | 该分支已**整体注释停用**,改为统一 `track_init_velocity_min·ΔT ≤ dis ≤ track_init_velocity_max·ΔT` **外加两条多普勒一致性条件** `dis ≥ abs(vr_p)·ΔT`、`dis ≥ abs(vr_h)·ΔT`(L361-372) |
|
||||
| 起批触发长度 | 长度满足 `track_init_point_num_min ≤ L ≤ 10` | 一致 |
|
||||
| 初始化用点 | 最近三点 | 不一致:取下标 `track_init_point_num_min-3 ~ -1` 的三个点,航迹长度大于起批点数时**不是**航迹真正的末三点(详见第 15 节第 2 条) |
|
||||
| 输出历史点 | "将临时航迹的全部 L 个点写入" | 实际只写**前 `track_init_point_num_min` 个点**(L614 的 `L` 是起批点数,不是航迹长度) |
|
||||
| use_flag 过滤 | "track_init 跳过 Use_Flag==1 的点" | 上游 `track_asso_process()` 已删除 `Use_Flag==1` 的点迹,`track_init` 入口(L59-70)也会再剔除一次,两道处理叠加,描述成立 |
|
||||
|
||||
### 12.2 与 `track_init_direct_tracking.cpp`(引导跟踪版,未启用)的差异
|
||||
@@ -640,8 +644,8 @@ $$
|
||||
| 项 | `track_init.cpp`(TWS,启用) | `track_init_direct_tracking.cpp`(未启用) |
|
||||
|---|---|---|
|
||||
| 入口 | 完整实现 | `track_init_process_logic` 空实现(L11-23) |
|
||||
| 点-航迹头判据 | 统一门限 + 两条多普勒一致性条件 | 仍为分距离段门限(`V_MAX/5` 亦在其注释中) |
|
||||
| 起批长度 | `L ≥ track_start_point_num`(长度可大于阈值) | `L == track_start_point_num`(严格相等) |
|
||||
| 点-航迹头判据 | 统一门限 + 两条多普勒一致性条件 | 仍为分距离段门限(原 `V_MAX/5` 亦在其注释中) |
|
||||
| 起批长度 | `L ≥ track_init_point_num_min`(长度可大于阈值) | `L == track_init_point_num_min`(严格相等) |
|
||||
| 去重 | 有同源去重 | 无 |
|
||||
| 屏蔽区 | 有 | 无 |
|
||||
| 航迹号 | `track_ind_get()` 分配 | 外部传入 `track_ID` |
|
||||
@@ -652,44 +656,44 @@ $$
|
||||
|
||||
## 13. 参数与调参
|
||||
|
||||
### 13.1 编译期宏(`parameters.h`)
|
||||
> **参数配置方式改造后**:与起始流程相关的全部可调参数都在 TOML 配置文件里(样例 `config/config_tmp.toml`),由公开接口 `parameters_reload(配置文件路径)` 加载到 `RDP_parameter`;`parameters.h` 不再有这些宏,宿主也不再下发 `RadarPara`。改参数 = 改配置文件后重新调用 `parameters_reload()`(加载失败时参数保持不变)。完整参数表与严格校验规则见 README 第 10 节。
|
||||
|
||||
| 宏 | 相扫/机扫 | 含义 | 调大效果 |
|
||||
### 13.1 配置项(样例值,均为「配置段.键」)
|
||||
|
||||
| 配置项 | 样例值 | 含义 | 调大效果 |
|
||||
|---|---|---|---|
|
||||
| `TRACK_START_THRESHOLD` | 8 / 8 | 起批波门,实际生效为 `d ≤ 8` | **更容易起批**(虚警增多) |
|
||||
| `ALPHA_START` | 60 / 60 | 起批折角(度) | 更容易起批(允许更急的转向) |
|
||||
| `SIGMA_R / SIGMA_A / SIGMA_V` | 10 / 0.02 / 2.0 | EKF 量测噪声 | 影响 `S` 与 `d`,进而影响关联难度 |
|
||||
| `MAX_TRACK_INDEX` | 500 | 航迹号/航迹数上限 | — |
|
||||
| `R_MIN / R_MAX` | 100 / 100000 | 有效量程(凝聚阶段过滤) | — |
|
||||
| `PI` | 3.1415926**f** | 角度换算(float 精度) | — |
|
||||
| `track_init.track_init_threshold` | 8 | 起批波门,实际生效为 `d ≤ 8` | **更容易起批**(虚警增多) |
|
||||
| `track_init.track_init_angle` | 60 | 起批折角(度) | 更容易起批(允许更急的转向) |
|
||||
| `track_associate.sigma_range/azimuth/pitch/velocity` | 10 / 0.02 / 0.2 / 2.0 | EKF 量测噪声 | 影响 `S` 与 `d`,进而影响关联难度 |
|
||||
| `track_index.track_index_max` | 500 | 航迹号/航迹数上限(1~500) | — |
|
||||
| `cohesion.point_cohesion_range_min/max` | 100 / 100000 | 有效量程(凝聚阶段过滤) | — |
|
||||
| `track_init.track_init_point_num_min` | 3 | 起批点数(加载器校验 3~9):调大 → 起批更慢、更抗虚警,但机动目标捕获更迟 | |
|
||||
| `track_init.track_init_velocity_max/min` | 50 / 1 | 头-点关联的速度区间;`min` 调大可滤除静止/慢速点 | |
|
||||
| `radar_params.radar_work_mode` | 0 | 当前对起始流程**无实际影响**(近/远程分支取值相同) | |
|
||||
| `track_init_prohibite_area.*_num` + 4 个数组 | 0 + 空数组 | 起批屏蔽区(距离 m / 方位 度;≤30 组,数组长度 = `*_num`) | |
|
||||
| `track_init.track_init_quality_L3~L7` | 0.7 / 0.6 / 0.5 / 0.4 / 0.3 | 按临时航迹长度的起批最低质量(V1.5.8) | 降低 → 更容易起批 |
|
||||
| `track_quality.*` | 0.5 / 0.3 / 0.2 / 0.8 | 质量权重、平滑系数与衰减系数(V1.5.8) | 影响质量评估与消亡速度 |
|
||||
|
||||
### 13.2 运行期参数(`RadarPara`)
|
||||
> 旧 `RadarPara` 中的 `track_start_threshold` 字段原本就未被实现读取;新结构体只保留实现真正使用/保留的参数(见 README 10.3 对照表)。
|
||||
|
||||
| 字段 | 影响 |
|
||||
|---|---|
|
||||
| `track_start_point_num` | 起批点数(接口校验 3~9):调大 → 起批更慢、更抗虚警,但机动目标捕获更迟 |
|
||||
| `V_MAX` / `V_MIN` | 头-点关联的速度区间;`V_MIN` 调大可滤除静止/慢速点 |
|
||||
| `work_mode` | 当前对起始流程**无实际影响**(近/远程分支取值相同) |
|
||||
| `track_prohibite_area_num` + 4×30 数组 | 起批屏蔽区(距离 m / 方位 度) |
|
||||
| `track_start_threshold` | **声明但未被实现读取**(实际用宏 `TRACK_START_THRESHOLD`) |
|
||||
### 13.2 调参直觉
|
||||
|
||||
### 13.3 调参直觉
|
||||
|
||||
- **虚警多、起批太容易**:先降 `TRACK_START_THRESHOLD`(8 → 5 左右),再降 `ALPHA_START`;必要时提高 `track_start_point_num`。
|
||||
- **真实目标起批慢**:提高 `TRACK_START_THRESHOLD` / `ALPHA_START`,或降低 `track_start_point_num`(最小 3)。
|
||||
- **静止/低速杂波起批**:提高 `V_MIN`。
|
||||
- **虚警多、起批太容易**:先降 `track_init_threshold`(8 → 5 左右),再降 `track_init_angle`;必要时提高 `track_init_point_num_min`。
|
||||
- **真实目标起批慢**:提高 `track_init_threshold` / `track_init_angle`,或降低 `track_init_point_num_min`(最小 3)。
|
||||
- **静止/低速杂波起批**:提高 `track_init_velocity_min`。
|
||||
- **某方位扇区/距离段禁止起批**(如地物、干扰区):配置起批屏蔽区,比调门限更精准。
|
||||
- **注意**:`config/config_tmp.toml` 是相扫取值(`scan_mode = 0`);机扫部署时需配 `scan_mode = 1`,且 `extrapolate_max_tas`、`height_smooth_win`、`track_die_round_tas`、`tas_track_num_max` 按机扫取值配置(见 README 4.2、10.1)。
|
||||
|
||||
---
|
||||
|
||||
## 14. 输出字段映射
|
||||
|
||||
对每条新起批航迹的每个历史点 `j = 0 .. track_start_point_num-1`(L614-664):
|
||||
对每条新起批航迹的每个历史点 `j = 0 .. track_init_point_num_min-1`(L614-664):
|
||||
|
||||
| 输出字段 | 来源 | 备注 |
|
||||
|---|---|---|
|
||||
| `Track_Index` | 新分配的航迹号 | 同一行内所有点相同 |
|
||||
| `Point_Sum` | `= track_start_point_num` | 与 `track_asso` 输出的 `Point_Sum=1` 可作区分 |
|
||||
| `Point_Sum` | `= track_init_point_num_min` | 与 `track_asso` 输出的 `Point_Sum=1` 可作区分 |
|
||||
| `Range` / `Azimuth` | 点的 `r` / `azi`(→度) | 极坐标 |
|
||||
| `Elevation` | `asin(h/r)`(度,钳位 ±1) | BUG-27 修复后的定义域钳位 |
|
||||
| `z` | 点的 `height` | |
|
||||
@@ -704,7 +708,7 @@ $$
|
||||
| `Flag_Point` | 恒为 **1**(实点) | |
|
||||
| `Track_Mode` | 恒为 **0**(TWS) | |
|
||||
| `Quality` | **该历史点的临时航迹质量 × 100**(V1.5.8) | 内部 `quality` 为 0~1,输出折算为 0~100;同一条航迹的各个历史点可能取值不同(L=3 时三点相同,之后只有末点更新) |
|
||||
| **未赋值** | `Flag_Direction`、`x_Predict/y_Predict/z_Predict`、`Range_Predict/Azimuth_Predict/Elevation_Predict`、`Target_Type`、`prf_point`、`v_z`、`amp_asv_point`、`prn_fftn_point` | DLL 不清零该数组,宿主读到的是其自身缓存的旧值/未初始化值。注意 `Flag_Direction` 只在关联输出路径赋值(`track_asso.cpp` / `track_asso_tas.cpp`),起批输出不写该字段。该字段的判据已由 f249fdc 的 `vr_point > 0 ? 1 : 0`(实测径向速度,受多普勒速度模糊影响符号可能取反)改为 fc49707 + 42b598e 的**航向-位置夹角法**:`Direction_Angle = atan2(X[4], X[1])`(弧度转度后同时输出到 `Direction_Angle`),`x`、`y` 为经 `Work_Parameter.Sys_delay` 补偿后的位置、`r` 为其模,`acos((-x·cos(Direction_Angle) - y·sin(Direction_Angle))/r) < π/2` 判为靠近(1)、否则远离(0),即滤波速度方向与"雷达→目标"位置方向夹角超 90° 判为接近。另 `amp_asv_point` / `prn_fftn_point` 属 e8c56f9 新增,`data_preprocess()` **现已**把 `DataRev.amp_asv / prn_fftn` 搬入 `PointRecv`(TWS 与 TAS 分支均赋值,并随 `RdpRecPoint` 落库),因此 TWS 关联/输出路径的该字段已有有效来源;TAS 关联与输出路径(`track_asso_tas.cpp`)仍未涉及这两个字段,TAS 输出行中它们保持宿主原值 |
|
||||
| **未赋值** | `Flag_Direction`、`x_Predict/y_Predict/z_Predict`、`Range_Predict/Azimuth_Predict/Elevation_Predict`、`Target_Type`、`prf_point`、`v_z`、`amp_asv_point`、`prn_fftn_point` | DLL 不清零该数组,宿主读到的是其自身缓存的旧值/未初始化值。注意 `Flag_Direction` 只在关联输出路径赋值(`track_asso.cpp` / `track_asso_tas.cpp`),起批输出不写该字段。该字段的判据已由 f249fdc 的 `vr_point > 0 ? 1 : 0`(实测径向速度,受多普勒速度模糊影响符号可能取反)改为 fc49707 + 42b598e 的**航向-位置夹角法**:`Direction_Angle = atan2(X[4], X[1])`(弧度转度后同时输出到 `Direction_Angle`),`x`、`y` 为经配置项 `radar_system_delay` 补偿后的位置、`r` 为其模,`acos((-x·cos(Direction_Angle) - y·sin(Direction_Angle))/r) < π/2` 判为靠近(1)、否则远离(0),即滤波速度方向与"雷达→目标"位置方向夹角超 90° 判为接近。另 `amp_asv_point` / `prn_fftn_point` 属 e8c56f9 新增,`data_preprocess()` **现已**把 `DataRev.amp_asv / prn_fftn` 搬入 `PointRecv`(TWS 与 TAS 分支均赋值,并随 `RdpRecPoint` 落库),因此 TWS 关联/输出路径的该字段已有有效来源;TAS 关联与输出路径(`track_asso_tas.cpp`)仍未涉及这两个字段,TAS 输出行中它们保持宿主原值 |
|
||||
|
||||
---
|
||||
|
||||
@@ -713,21 +717,21 @@ $$
|
||||
> 以下为阅读源码得到的观察,**不代表已确认的缺陷**,部分为有意保留的历史行为;修改前请结合实测数据评估。
|
||||
|
||||
1. **硬编码 5 m 距离差门限**(L191):`|r_p − r_末点| < 5` 的点永不参与关联。对切向飞行、远距离慢速目标可能造成漏关联(起批失败)。
|
||||
2. **初始化用点不是航迹真正的末三点**(L505、L563-565 vs L614):起批流程中 `L = track_start_point_num`(L505 覆盖了前面循环里的航迹长度变量),Z0/Z1/Z2 取下标 `L-3 / L-2 / L-1`,输出循环也只写到 `j = L-1`。因此当航迹长度 N 大于起批点数时,参与初始化的只是**前 L 个点中的最后三点**,而不是航迹真正的末三点;第 L 个点之后的尾部点既未参与初始化也不出现在输出中(这些点通常来自滞留在起批屏蔽区期间的积累)。
|
||||
3. **去重只比较前三个点**(L517-519 硬编码 0/1/2),且优度指标取 `[track_start_point_num-1].d`(长度大于阈值时非末点);若 `track_start_point_num > 3`,共享前三点但后续分叉的两条航迹会被误判为同源。
|
||||
2. **初始化用点不是航迹真正的末三点**(L505、L563-565 vs L614):起批流程中 `L = track_init_point_num_min`(L505 覆盖了前面循环里的航迹长度变量),Z0/Z1/Z2 取下标 `L-3 / L-2 / L-1`,输出循环也只写到 `j = L-1`。因此当航迹长度 N 大于起批点数时,参与初始化的只是**前 L 个点中的最后三点**,而不是航迹真正的末三点;第 L 个点之后的尾部点既未参与初始化也不出现在输出中(这些点通常来自滞留在起批屏蔽区期间的积累)。
|
||||
3. **去重只比较前三个点**(L517-519 硬编码 0/1/2),且优度指标取 `[track_init_point_num_min-1].d`(长度大于阈值时非末点);若 `track_init_point_num_min > 3`,共享前三点但后续分叉的两条航迹会被误判为同源。
|
||||
4. **无一对一关联约束**:一个点可同时为多条临时航迹续点(阶段二/三),产生航迹分裂,虚警场景下临时航迹表可能快速膨胀(靠去重与消亡收敛)。
|
||||
5. **ΔT ≤ 0 无显式校验**(BUG-33 遗留):头-点关联靠 `dis ≤ V_MAX·ΔT` 隐式排除负 ΔT;EKF 关联路径 `d_cal_track_init_EKF(ΔT<0)` 会得到无物理意义的结果,只是通常会被 `d ≤ 8` 挡住。
|
||||
6. **判据对马氏距离平方二次平方**(L221):实际门限为 `d ≤ 8`(3 自由度约 95.4%)。若按字面 `d² ≤ 64` 理解会误判为 `d ≤ 64`(极宽松)。修改 `TRACK_START_THRESHOLD` 时务必记得这一层。
|
||||
5. **ΔT ≤ 0 无显式校验**(BUG-33 遗留):头-点关联靠 `dis ≤ track_init_velocity_max·ΔT` 隐式排除负 ΔT;EKF 关联路径 `d_cal_track_init_EKF(ΔT<0)` 会得到无物理意义的结果,只是通常会被 `d ≤ 8` 挡住。
|
||||
6. **判据对马氏距离平方二次平方**(L221):`track_init_threshold=8` 时实际门限为 `d ≤ 8`(3 自由度约 95.4%)。若按字面 `d² ≤ 64` 理解会误判为 `d ≤ 64`(极宽松)。修改 `track_init_threshold` 时务必记得这一层。
|
||||
7. **两点/三点初始化无 `T > 0` 保护**(BUG-18):`T` 极小(重复/乱序时间戳)时速度、加速度与协方差会爆炸性放大。
|
||||
8. **死代码**:L72-75 的 `point_recv` 回写随即被 L117-118 清空;L606-608 的 `cart2polar` 结果未使用;L348-356 的近/远程分支取值相同。
|
||||
9. **`acos` 无定义域钳位**(L452):浮点越界产生 `NaN`,表现为条件恒假(静默丢弃该关联),不报错也不崩溃。
|
||||
10. **航迹数饱和静默丢弃**:`trust_track.size() ≥ 500` 或 `track_ind_get()` 返回 0 时,候选被直接跳过且该临时航迹已从 `temp_track` 摘除 → 起批机会永久丢失。
|
||||
10. **航迹数饱和静默丢弃**:`trust_track.size() ≥ track_index_max`(配置项,默认 500)或 `track_ind_get()` 返回 0 时,候选被直接跳过且该临时航迹已从 `temp_track` 摘除 → 起批机会永久丢失。
|
||||
11. **未赋值输出字段**(第 14 节):宿主若直接使用 `x_Predict`、`Flag_Direction`、`Target_Type` 等字段,会读到非本次写入的数据。
|
||||
12. **`PI` 为 float 常量**(3.1415926f)参与双精度运算,角度换算存在约 1e-7 量级偏差,属既有实现风格。
|
||||
13. **精度/量纲提醒**:`Temp_track::vr` 直接取点的 `Velocity`(多普勒)作为径向速度,EKF 关联时用于残差折叠(盲速 `v_bind`)与符号判据;若宿主的 `Velocity` 存在模糊,符号判据 `vr_p·vr_末点 > 0` 可能失效。
|
||||
14. **输出数组行号可能越界(极端场景)**:容量判据用 `trust_track->size() < 500`,而 `Trust_Track_Output` 的行计数已在 `track_asso` 阶段按「消亡前」的航迹数推进,中间的 `track_die` 删除 D 条后腾出的配额会让起始流程多写 D 行;当 D ≥ 2 且当圈起批数占满配额时,写入行号可超过 `MAX_TRACK_NUM=500`(详见 2.3)。建议把判据改为同时校验 `*Trust_track_num_Output < MAX_TRACK_NUM` 与 `trust_track->size() < MAX_TRACK_INDEX`。
|
||||
14. **输出数组行号可能越界(极端场景)**:容量判据用 `trust_track->size() < track_index_max`,而 `Trust_Track_Output` 的行计数已在 `track_asso` 阶段按「消亡前」的航迹数推进,中间的 `track_die` 删除 D 条后腾出的配额会让起始流程多写 D 行;当 D ≥ 2 且当圈起批数占满配额时,写入行号可超过 `MAX_TRACK_NUM=500`(详见 2.3)。建议把判据改为同时校验 `*Trust_track_num_Output < MAX_TRACK_NUM` 与 `trust_track->size() < track_index_max`。
|
||||
15. **起批质量门限比较的是「末点」质量而非整条航迹质量(V1.5.8)**:`tmp_track_to_trust_track()` 取 `(*Iter)[L-1].quality` 与按长度取的门限比较,而该质量在 L>3 时只是末点的指数平滑值(L=3 时三点同值),并不代表整条临时航迹的平均质量;长度越大门限越低,实际是「用更宽松的门限校验最新一点」。
|
||||
16. **起批质量来源依赖阶段二**:`quality` 只在「点-临时航迹关联」成功后更新,阶段三产生的 2 点临时航迹不带质量值;长度 3 的临时航迹在第三次延长时写入三点同值,因此其起批质量有定义。若将来把起批点数放宽到 3 以下(当前接口校验 3~9),则会出现质量恒为 0、永远无法通过质量门限的情况。
|
||||
16. **起批质量来源依赖阶段二**:`quality` 只在「点-临时航迹关联」成功后更新,阶段三产生的 2 点临时航迹不带质量值;长度 3 的临时航迹在第三次延长时写入三点同值,因此其起批质量有定义。若将来把起批点数放宽到 3 以下(当前加载器校验 3~9),则会出现质量恒为 0、永远无法通过质量门限的情况。
|
||||
|
||||
---
|
||||
|
||||
@@ -783,6 +787,6 @@ $$
|
||||
| `kalman.cpp` L780-789 | `Bind_speed` | 盲速计算 |
|
||||
| `coor_trans.cpp` L8-20 | `polar2cart` / `cart2polar` | 坐标转换 |
|
||||
| `track_index_mangement.cpp` L14-77 | `track_ind_get` | 航迹号分配 |
|
||||
| `data_process.cpp` L51 | `data_preprocess` | 一圈完成 → `model=1` |
|
||||
| `data_process.cpp` L154-159 | `track_process` | 起始流程调用点 |
|
||||
| `data_process.cpp` L204-222 | 参数初始化/修改 | 校验 `track_start_point_num ∈ [3,9]` |
|
||||
| `data_process.cpp` L22 | `data_preprocess` | 一圈完成 → `model=1` |
|
||||
| `data_process.cpp` L167 起 | `track_process` | 起始流程调用点(L187-201 调用 `track_asso`/`track_die`/`track_init`) |
|
||||
| `data_process.cpp` L246-258 | 参数加载 | `parameters_reload()`:TOML 配置 → 全局对象 `rdp_parameter`(原「参数初始化/修改」已删除) |
|
||||
|
||||
@@ -1,35 +1,3 @@
|
||||
# 雷达数据处理项目修改说明
|
||||
|
||||
## 概述
|
||||
|
||||
根据[data_record_perf_report.md](/docs/data_record_perf_report.md)中提出的优化措施,以及本文档的要求,对数据记录功能进行性能优化。
|
||||
|
||||
## 优化措施实施决策
|
||||
|
||||
### P0-a 关联信息裁剪/采样
|
||||
|
||||
不采用,实际运行时,航迹数和每圈点迹数都在可控范围,数据行数不会过大。
|
||||
|
||||
### P0-b 批量事务
|
||||
|
||||
采用
|
||||
|
||||
### P0-c 异步写线程+有界队列
|
||||
|
||||
采用
|
||||
|
||||
### P1-a 构造成本迁移
|
||||
|
||||
采用,按方案A执行
|
||||
|
||||
### P1-b:SQLite 配置与健壮性
|
||||
|
||||
采用
|
||||
|
||||
### P2:记录策略与运维
|
||||
|
||||
不采用
|
||||
|
||||
## 其他事项
|
||||
|
||||
其他不明确或有冲突的地方,直接向我提问
|
||||
Vendored
+25
@@ -0,0 +1,25 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 - present, Gabi Melman and spdlog contributors.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
-- NOTE: Third party dependency used by this software --
|
||||
This software depends on the fmt lib (MIT License),
|
||||
and users must comply to its license: https://raw.githubusercontent.com/fmtlib/fmt/master/LICENSE
|
||||
Vendored
+50
@@ -0,0 +1,50 @@
|
||||
# 第三方库来源说明(spdlog)
|
||||
|
||||
本目录按**上游原样内置** spdlog 源码(`include/` 头文件 + `src/` 编译单元,保留上游字节,含换行符,不做编码/格式转换),与 `third_party/ormpp`、`third_party/toml` 同类处理:仓库自带依赖,离线可配置、可交付。
|
||||
|
||||
| 内容 | 本目录位置 | 上游 | 版本 / 标识 |
|
||||
|---|---|---|---|
|
||||
| spdlog(C++ 日志库) | `include/spdlog/**`(105 个文件 / 940,660 字节)、`src/*.cpp`(7 个文件 / 9,168 字节) | <https://github.com/gabime/spdlog> | tag `v1.17.0`(commit `79524ddd08a4ec981b7fea76afd08ee05f83755d`,2026-01-04);`include/spdlog/version.h` = 1.17.0 |
|
||||
| 内置 {fmt}(随 spdlog 内置,供格式化使用) | `include/spdlog/fmt/bundled/**`(23 个文件) | 随 spdlog 内置;独立仓库 <https://github.com/fmtlib/fmt> | {fmt} 12.1.0(spdlog v1.17.0 的 bundled 版本;未启用 `SPDLOG_FMT_EXTERNAL`) |
|
||||
| 许可证 | `LICENSE` | — | MIT License(上游原文,1,323 字节) |
|
||||
|
||||
关键文件校验(与上游 v1.17.0 tarball 一致,可用下方复现步骤复核):
|
||||
|
||||
| 文件 | 字节数 | SHA-256 |
|
||||
|---|---|---|
|
||||
| `include/spdlog/version.h` | 417 | `a0c15d9295eba580732dada20940a443c53ddb47d8565c730d7af1ee5f3bd712` |
|
||||
| `include/spdlog/spdlog.h` | 11,866 | `54283b3e3bf96c3d…`(前 16 位) |
|
||||
| `include/spdlog/common.h` | 12,569 | `ddf4fa8ee4687f36…`(前 16 位) |
|
||||
| `include/spdlog/fmt/bundled/format-inl.h` | 81,008 | `e7baf108b84e89b29915bb35733540f8f4262cd8f4b41c15754eed4d2f185f61` |
|
||||
| `LICENSE` | 1,323 | `d678c20826e13925…`(前 16 位) |
|
||||
|
||||
> 说明:只保留使用所需的 `include/` 与 `src/`(上游 `tests/`、`bench/`、`example/`、`cmake/`、`scripts/`、`logos/`、`.github/` 等均不需要);`include/` + `src/` + `LICENSE` 共 **113 个文件 / 951,151 字节**(上游 v1.17.0 源码 tarball `spdlog-1.17.0.tar.gz` = 281,801 字节)。
|
||||
|
||||
## 复现步骤(重新获取/升级时执行)
|
||||
|
||||
```bat
|
||||
:: 在临时目录执行(本机全局 core.autocrlf=true 会把文本转成 CRLF,必须显式关闭)
|
||||
git -c core.autocrlf=false clone --depth 1 --branch v1.17.0 https://github.com/gabime/spdlog.git <tmp-spdlog>
|
||||
|
||||
:: 仅拷贝以下内容到本目录(其余目录不需要):
|
||||
:: <tmp-spdlog>\include\ -> third_party\spdlog\include\
|
||||
:: <tmp-spdlog>\src\ -> third_party\spdlog\src\
|
||||
:: <tmp-spdlog>\LICENSE -> third_party\spdlog\LICENSE
|
||||
```
|
||||
|
||||
或直接使用官方 tag 源码包(推荐,字节与上述 tarball 一致):
|
||||
|
||||
```bat
|
||||
:: https://codeload.github.com/gabime/spdlog/tar.gz/refs/tags/v1.17.0
|
||||
:: 解压后同样只拷贝 include\、src\、LICENSE
|
||||
```
|
||||
|
||||
校验方式:`include/` 105 个文件 / 940,660 字节、`src/` 7 个文件 / 9,168 字节,且 `include/spdlog/version.h` 的 SHA-256 为 `a0c15d92…f3bd712`;本目录所有文件保持 **LF** 换行(上游字节,不转 CRLF/BOM)。
|
||||
|
||||
## 使用方式与约束
|
||||
|
||||
- **仅由日志模块 `data_process_class_dll/rdp_log.h`(经 `rdp_log.cpp`、各业务模块的 `RDP_LOG_*` 宏)使用**。
|
||||
- 以**静态库**方式编译:根目录 `CMakeLists.txt` 定义目标 `spdlog`(`third_party/spdlog/src/*.cpp`,`SPDLOG_COMPILED_LIB`),链接进 `data_process_class_dll`;`rdp_record` 静态库(C++20,`rdp_record.cpp` 内也有日志)同样链接该目标。头文件包含路径与 `SPDLOG_COMPILED_LIB` / `SPDLOG_ACTIVE_LEVEL` 编译定义由该目标的 **PUBLIC** 接口传递给使用方。
|
||||
- **编译期日志级别**:spdlog 的 `SPDLOG_ACTIVE_LEVEL` 由 CMake 选项 `RDP_LOG_LEVEL`(AUTO/TRACE/DEBUG/INFO/WARN/ERROR/CRITICAL/OFF)映射,AUTO 时 Debug = TRACE、其余 = INFO;低于该级别的 `RDP_LOG_*` 调用在编译期被剔除(实参不求值)。配置方式见 `README.md` 4.3、10.4。
|
||||
- **告警处理**:第三方源码以目标级 `/w` 编译,本项目源码包含其头文件处用 `#pragma warning(push, 0)` / `(pop)` 包裹(`rdp_log.h`),维持 README 4.5 的 `/W3` 零告警约定;这是本目录作为第三方依赖的唯一例外。
|
||||
- **升级方式**:整目录替换为新的上游快照,同步更新本文件的版本 / commit / 校验值,然后必须重新验证:4 个配置(Release/Debug × REC_OFF/REC_ON)构建通过(0 warning)+ 日志双写(标准输出 + `rdp_logs` 文件)、按日期+时间命名/同秒 `_1` 后缀、`RDP_LOG_LEVEL` 编译期过滤等运行期行为(见 README 10.4)。
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
//
|
||||
// Async logging using global thread pool
|
||||
// All loggers created here share same global thread pool.
|
||||
// Each log message is pushed to a queue along with a shared pointer to the
|
||||
// logger.
|
||||
// If a logger deleted while having pending messages in the queue, it's actual
|
||||
// destruction will defer
|
||||
// until all its messages are processed by the thread pool.
|
||||
// This is because each message in the queue holds a shared_ptr to the
|
||||
// originating logger.
|
||||
|
||||
#include <spdlog/async_logger.h>
|
||||
#include <spdlog/details/registry.h>
|
||||
#include <spdlog/details/thread_pool.h>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
namespace spdlog {
|
||||
|
||||
namespace details {
|
||||
static const size_t default_async_q_size = 8192;
|
||||
}
|
||||
|
||||
// async logger factory - creates async loggers backed with thread pool.
|
||||
// if a global thread pool doesn't already exist, create it with default queue
|
||||
// size of 8192 items and single thread.
|
||||
template <async_overflow_policy OverflowPolicy = async_overflow_policy::block>
|
||||
struct async_factory_impl {
|
||||
template <typename Sink, typename... SinkArgs>
|
||||
static std::shared_ptr<async_logger> create(std::string logger_name, SinkArgs &&...args) {
|
||||
auto ®istry_inst = details::registry::instance();
|
||||
|
||||
// create global thread pool if not already exists..
|
||||
|
||||
auto &mutex = registry_inst.tp_mutex();
|
||||
std::lock_guard<std::recursive_mutex> tp_lock(mutex);
|
||||
auto tp = registry_inst.get_tp();
|
||||
if (tp == nullptr) {
|
||||
tp = std::make_shared<details::thread_pool>(details::default_async_q_size, 1U);
|
||||
registry_inst.set_tp(tp);
|
||||
}
|
||||
|
||||
auto sink = std::make_shared<Sink>(std::forward<SinkArgs>(args)...);
|
||||
auto new_logger = std::make_shared<async_logger>(std::move(logger_name), std::move(sink),
|
||||
std::move(tp), OverflowPolicy);
|
||||
registry_inst.initialize_logger(new_logger);
|
||||
return new_logger;
|
||||
}
|
||||
};
|
||||
|
||||
using async_factory = async_factory_impl<async_overflow_policy::block>;
|
||||
using async_factory_nonblock = async_factory_impl<async_overflow_policy::overrun_oldest>;
|
||||
|
||||
template <typename Sink, typename... SinkArgs>
|
||||
inline std::shared_ptr<spdlog::logger> create_async(std::string logger_name,
|
||||
SinkArgs &&...sink_args) {
|
||||
return async_factory::create<Sink>(std::move(logger_name),
|
||||
std::forward<SinkArgs>(sink_args)...);
|
||||
}
|
||||
|
||||
template <typename Sink, typename... SinkArgs>
|
||||
inline std::shared_ptr<spdlog::logger> create_async_nb(std::string logger_name,
|
||||
SinkArgs &&...sink_args) {
|
||||
return async_factory_nonblock::create<Sink>(std::move(logger_name),
|
||||
std::forward<SinkArgs>(sink_args)...);
|
||||
}
|
||||
|
||||
// set global thread pool.
|
||||
inline void init_thread_pool(size_t q_size,
|
||||
size_t thread_count,
|
||||
std::function<void()> on_thread_start,
|
||||
std::function<void()> on_thread_stop) {
|
||||
auto tp = std::make_shared<details::thread_pool>(q_size, thread_count, on_thread_start,
|
||||
on_thread_stop);
|
||||
details::registry::instance().set_tp(std::move(tp));
|
||||
}
|
||||
|
||||
inline void init_thread_pool(size_t q_size,
|
||||
size_t thread_count,
|
||||
std::function<void()> on_thread_start) {
|
||||
init_thread_pool(q_size, thread_count, on_thread_start, [] {});
|
||||
}
|
||||
|
||||
inline void init_thread_pool(size_t q_size, size_t thread_count) {
|
||||
init_thread_pool(q_size, thread_count, [] {}, [] {});
|
||||
}
|
||||
|
||||
// get the global thread pool.
|
||||
inline std::shared_ptr<spdlog::details::thread_pool> thread_pool() {
|
||||
return details::registry::instance().get_tp();
|
||||
}
|
||||
} // namespace spdlog
|
||||
@@ -0,0 +1,84 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef SPDLOG_HEADER_ONLY
|
||||
#include <spdlog/async_logger.h>
|
||||
#endif
|
||||
|
||||
#include <spdlog/details/thread_pool.h>
|
||||
#include <spdlog/sinks/sink.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
SPDLOG_INLINE spdlog::async_logger::async_logger(std::string logger_name,
|
||||
sinks_init_list sinks_list,
|
||||
std::weak_ptr<details::thread_pool> tp,
|
||||
async_overflow_policy overflow_policy)
|
||||
: async_logger(std::move(logger_name),
|
||||
sinks_list.begin(),
|
||||
sinks_list.end(),
|
||||
std::move(tp),
|
||||
overflow_policy) {}
|
||||
|
||||
SPDLOG_INLINE spdlog::async_logger::async_logger(std::string logger_name,
|
||||
sink_ptr single_sink,
|
||||
std::weak_ptr<details::thread_pool> tp,
|
||||
async_overflow_policy overflow_policy)
|
||||
: async_logger(
|
||||
std::move(logger_name), {std::move(single_sink)}, std::move(tp), overflow_policy) {}
|
||||
|
||||
// send the log message to the thread pool
|
||||
SPDLOG_INLINE void spdlog::async_logger::sink_it_(const details::log_msg &msg){
|
||||
SPDLOG_TRY{if (auto pool_ptr = thread_pool_.lock()){
|
||||
pool_ptr -> post_log(shared_from_this(), msg, overflow_policy_);
|
||||
}
|
||||
else {
|
||||
throw_spdlog_ex("async log: thread pool doesn't exist anymore");
|
||||
}
|
||||
}
|
||||
SPDLOG_LOGGER_CATCH(msg.source)
|
||||
}
|
||||
|
||||
// send flush request to the thread pool
|
||||
SPDLOG_INLINE void spdlog::async_logger::flush_(){
|
||||
SPDLOG_TRY{if (auto pool_ptr = thread_pool_.lock()){
|
||||
pool_ptr -> post_flush(shared_from_this(), overflow_policy_);
|
||||
}
|
||||
else {
|
||||
throw_spdlog_ex("async flush: thread pool doesn't exist anymore");
|
||||
}
|
||||
}
|
||||
SPDLOG_LOGGER_CATCH(source_loc())
|
||||
}
|
||||
|
||||
//
|
||||
// backend functions - called from the thread pool to do the actual job
|
||||
//
|
||||
SPDLOG_INLINE void spdlog::async_logger::backend_sink_it_(const details::log_msg &msg) {
|
||||
for (auto &sink : sinks_) {
|
||||
if (sink->should_log(msg.level)) {
|
||||
SPDLOG_TRY { sink->log(msg); }
|
||||
SPDLOG_LOGGER_CATCH(msg.source)
|
||||
}
|
||||
}
|
||||
|
||||
if (should_flush_(msg)) {
|
||||
backend_flush_();
|
||||
}
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void spdlog::async_logger::backend_flush_() {
|
||||
for (auto &sink : sinks_) {
|
||||
SPDLOG_TRY { sink->flush(); }
|
||||
SPDLOG_LOGGER_CATCH(source_loc())
|
||||
}
|
||||
}
|
||||
|
||||
SPDLOG_INLINE std::shared_ptr<spdlog::logger> spdlog::async_logger::clone(std::string new_name) {
|
||||
auto cloned = std::make_shared<spdlog::async_logger>(*this);
|
||||
cloned->name_ = std::move(new_name);
|
||||
return cloned;
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
// Fast asynchronous logger.
|
||||
// Uses pre allocated queue.
|
||||
// Creates a single back thread to pop messages from the queue and log them.
|
||||
//
|
||||
// Upon each log write the logger:
|
||||
// 1. Checks if its log level is enough to log the message
|
||||
// 2. Push a new copy of the message to a queue (or block the caller until
|
||||
// space is available in the queue)
|
||||
// Upon destruction, logs all remaining messages in the queue before
|
||||
// destructing..
|
||||
|
||||
#include <spdlog/logger.h>
|
||||
|
||||
namespace spdlog {
|
||||
|
||||
// Async overflow policy - block by default.
|
||||
enum class async_overflow_policy {
|
||||
block, // Block until message can be enqueued
|
||||
overrun_oldest, // Discard oldest message in the queue if full when trying to
|
||||
// add new item.
|
||||
discard_new // Discard new message if the queue is full when trying to add new item.
|
||||
};
|
||||
|
||||
namespace details {
|
||||
class thread_pool;
|
||||
}
|
||||
|
||||
class SPDLOG_API async_logger final : public std::enable_shared_from_this<async_logger>,
|
||||
public logger {
|
||||
friend class details::thread_pool;
|
||||
|
||||
public:
|
||||
template <typename It>
|
||||
async_logger(std::string logger_name,
|
||||
It begin,
|
||||
It end,
|
||||
std::weak_ptr<details::thread_pool> tp,
|
||||
async_overflow_policy overflow_policy = async_overflow_policy::block)
|
||||
: logger(std::move(logger_name), begin, end),
|
||||
thread_pool_(std::move(tp)),
|
||||
overflow_policy_(overflow_policy) {}
|
||||
|
||||
async_logger(std::string logger_name,
|
||||
sinks_init_list sinks_list,
|
||||
std::weak_ptr<details::thread_pool> tp,
|
||||
async_overflow_policy overflow_policy = async_overflow_policy::block);
|
||||
|
||||
async_logger(std::string logger_name,
|
||||
sink_ptr single_sink,
|
||||
std::weak_ptr<details::thread_pool> tp,
|
||||
async_overflow_policy overflow_policy = async_overflow_policy::block);
|
||||
|
||||
std::shared_ptr<logger> clone(std::string new_name) override;
|
||||
|
||||
protected:
|
||||
void sink_it_(const details::log_msg &msg) override;
|
||||
void flush_() override;
|
||||
void backend_sink_it_(const details::log_msg &incoming_log_msg);
|
||||
void backend_flush_();
|
||||
|
||||
private:
|
||||
std::weak_ptr<details::thread_pool> thread_pool_;
|
||||
async_overflow_policy overflow_policy_;
|
||||
};
|
||||
} // namespace spdlog
|
||||
|
||||
#ifdef SPDLOG_HEADER_ONLY
|
||||
#include "async_logger-inl.h"
|
||||
#endif
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
#include <spdlog/cfg/helpers.h>
|
||||
#include <spdlog/details/registry.h>
|
||||
|
||||
//
|
||||
// Init log levels using each argv entry that starts with "SPDLOG_LEVEL="
|
||||
//
|
||||
// set all loggers to debug level:
|
||||
// example.exe "SPDLOG_LEVEL=debug"
|
||||
|
||||
// set logger1 to trace level
|
||||
// example.exe "SPDLOG_LEVEL=logger1=trace"
|
||||
|
||||
// turn off all logging except for logger1 and logger2:
|
||||
// example.exe "SPDLOG_LEVEL=off,logger1=debug,logger2=info"
|
||||
|
||||
namespace spdlog {
|
||||
namespace cfg {
|
||||
|
||||
// search for SPDLOG_LEVEL= in the args and use it to init the levels
|
||||
inline void load_argv_levels(int argc, const char **argv) {
|
||||
const std::string spdlog_level_prefix = "SPDLOG_LEVEL=";
|
||||
for (int i = 1; i < argc; i++) {
|
||||
std::string arg = argv[i];
|
||||
if (arg.find(spdlog_level_prefix) == 0) {
|
||||
auto levels_string = arg.substr(spdlog_level_prefix.size());
|
||||
helpers::load_levels(levels_string);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void load_argv_levels(int argc, char **argv) {
|
||||
load_argv_levels(argc, const_cast<const char **>(argv));
|
||||
}
|
||||
|
||||
} // namespace cfg
|
||||
} // namespace spdlog
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
#include <spdlog/cfg/helpers.h>
|
||||
#include <spdlog/details/os.h>
|
||||
#include <spdlog/details/registry.h>
|
||||
|
||||
//
|
||||
// Init levels and patterns from env variables SPDLOG_LEVEL
|
||||
// Inspired from Rust's "env_logger" crate (https://crates.io/crates/env_logger).
|
||||
// Note - fallback to "info" level on unrecognized levels
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// set global level to debug:
|
||||
// export SPDLOG_LEVEL=debug
|
||||
//
|
||||
// turn off all logging except for logger1:
|
||||
// export SPDLOG_LEVEL="*=off,logger1=debug"
|
||||
//
|
||||
|
||||
// turn off all logging except for logger1 and logger2:
|
||||
// export SPDLOG_LEVEL="off,logger1=debug,logger2=info"
|
||||
|
||||
namespace spdlog {
|
||||
namespace cfg {
|
||||
inline void load_env_levels(const char* var = "SPDLOG_LEVEL") {
|
||||
auto env_val = details::os::getenv(var);
|
||||
if (!env_val.empty()) {
|
||||
helpers::load_levels(env_val);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace cfg
|
||||
} // namespace spdlog
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef SPDLOG_HEADER_ONLY
|
||||
#include <spdlog/cfg/helpers.h>
|
||||
#endif
|
||||
|
||||
#include <spdlog/details/os.h>
|
||||
#include <spdlog/details/registry.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace spdlog {
|
||||
namespace cfg {
|
||||
namespace helpers {
|
||||
|
||||
// inplace convert to lowercase
|
||||
inline std::string &to_lower_(std::string &str) {
|
||||
std::transform(str.begin(), str.end(), str.begin(), [](char ch) {
|
||||
return static_cast<char>((ch >= 'A' && ch <= 'Z') ? ch + ('a' - 'A') : ch);
|
||||
});
|
||||
return str;
|
||||
}
|
||||
|
||||
// inplace trim spaces
|
||||
inline std::string &trim_(std::string &str) {
|
||||
const char *spaces = " \n\r\t";
|
||||
str.erase(str.find_last_not_of(spaces) + 1);
|
||||
str.erase(0, str.find_first_not_of(spaces));
|
||||
return str;
|
||||
}
|
||||
|
||||
// return (name,value) trimmed pair from the given "name = value" string.
|
||||
// return empty string on missing parts
|
||||
// "key=val" => ("key", "val")
|
||||
// " key = val " => ("key", "val")
|
||||
// "key=" => ("key", "")
|
||||
// "val" => ("", "val")
|
||||
|
||||
inline std::pair<std::string, std::string> extract_kv_(char sep, const std::string &str) {
|
||||
auto n = str.find(sep);
|
||||
std::string k, v;
|
||||
if (n == std::string::npos) {
|
||||
v = str;
|
||||
} else {
|
||||
k = str.substr(0, n);
|
||||
v = str.substr(n + 1);
|
||||
}
|
||||
return std::make_pair(trim_(k), trim_(v));
|
||||
}
|
||||
|
||||
// return vector of key/value pairs from a sequence of "K1=V1,K2=V2,.."
|
||||
// "a=AAA,b=BBB,c=CCC,.." => {("a","AAA"),("b","BBB"),("c", "CCC"),...}
|
||||
inline std::unordered_map<std::string, std::string> extract_key_vals_(const std::string &str) {
|
||||
std::string token;
|
||||
std::istringstream token_stream(str);
|
||||
std::unordered_map<std::string, std::string> rv{};
|
||||
while (std::getline(token_stream, token, ',')) {
|
||||
if (token.empty()) {
|
||||
continue;
|
||||
}
|
||||
auto kv = extract_kv_('=', token);
|
||||
rv[kv.first] = kv.second;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void load_levels(const std::string &input) {
|
||||
if (input.empty() || input.size() >= 32768) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto key_vals = extract_key_vals_(input);
|
||||
std::unordered_map<std::string, level::level_enum> levels;
|
||||
level::level_enum global_level = level::info;
|
||||
bool global_level_found = false;
|
||||
|
||||
for (auto &name_level : key_vals) {
|
||||
const auto &logger_name = name_level.first;
|
||||
const auto &level_name = to_lower_(name_level.second);
|
||||
auto level = level::from_str(level_name);
|
||||
// ignore unrecognized level names
|
||||
if (level == level::off && level_name != "off") {
|
||||
continue;
|
||||
}
|
||||
if (logger_name.empty()) // no logger name indicates global level
|
||||
{
|
||||
global_level_found = true;
|
||||
global_level = level;
|
||||
} else {
|
||||
levels[logger_name] = level;
|
||||
}
|
||||
}
|
||||
|
||||
details::registry::instance().set_levels(std::move(levels),
|
||||
global_level_found ? &global_level : nullptr);
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
} // namespace cfg
|
||||
} // namespace spdlog
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <spdlog/common.h>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace spdlog {
|
||||
namespace cfg {
|
||||
namespace helpers {
|
||||
//
|
||||
// Init levels from given string
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// set global level to debug: "debug"
|
||||
// turn off all logging except for logger1: "off,logger1=debug"
|
||||
// turn off all logging except for logger1 and logger2: "off,logger1=debug,logger2=info"
|
||||
//
|
||||
SPDLOG_API void load_levels(const std::string &txt);
|
||||
} // namespace helpers
|
||||
|
||||
} // namespace cfg
|
||||
} // namespace spdlog
|
||||
|
||||
#ifdef SPDLOG_HEADER_ONLY
|
||||
#include "helpers-inl.h"
|
||||
#endif // SPDLOG_HEADER_ONLY
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef SPDLOG_HEADER_ONLY
|
||||
#include <spdlog/common.h>
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
|
||||
namespace spdlog {
|
||||
namespace level {
|
||||
|
||||
#if __cplusplus >= 201703L
|
||||
constexpr
|
||||
#endif
|
||||
static string_view_t level_string_views[] SPDLOG_LEVEL_NAMES;
|
||||
|
||||
static const char *short_level_names[] SPDLOG_SHORT_LEVEL_NAMES;
|
||||
|
||||
SPDLOG_INLINE const string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOEXCEPT {
|
||||
return level_string_views[l];
|
||||
}
|
||||
|
||||
SPDLOG_INLINE const char *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOEXCEPT {
|
||||
return short_level_names[l];
|
||||
}
|
||||
|
||||
SPDLOG_INLINE spdlog::level::level_enum from_str(const std::string &name) SPDLOG_NOEXCEPT {
|
||||
auto it = std::find(std::begin(level_string_views), std::end(level_string_views), name);
|
||||
if (it != std::end(level_string_views))
|
||||
return static_cast<level::level_enum>(std::distance(std::begin(level_string_views), it));
|
||||
|
||||
// check also for "warn" and "err" before giving up..
|
||||
if (name == "warn") {
|
||||
return level::warn;
|
||||
}
|
||||
if (name == "err") {
|
||||
return level::err;
|
||||
}
|
||||
return level::off;
|
||||
}
|
||||
} // namespace level
|
||||
|
||||
SPDLOG_INLINE spdlog_ex::spdlog_ex(std::string msg)
|
||||
: msg_(std::move(msg)) {}
|
||||
|
||||
SPDLOG_INLINE spdlog_ex::spdlog_ex(const std::string &msg, int last_errno) {
|
||||
#ifdef SPDLOG_USE_STD_FORMAT
|
||||
msg_ = std::system_error(std::error_code(last_errno, std::generic_category()), msg).what();
|
||||
#else
|
||||
memory_buf_t outbuf;
|
||||
fmt::format_system_error(outbuf, last_errno, msg.c_str());
|
||||
msg_ = fmt::to_string(outbuf);
|
||||
#endif
|
||||
}
|
||||
|
||||
SPDLOG_INLINE const char *spdlog_ex::what() const SPDLOG_NOEXCEPT { return msg_.c_str(); }
|
||||
|
||||
SPDLOG_INLINE void throw_spdlog_ex(const std::string &msg, int last_errno) {
|
||||
SPDLOG_THROW(spdlog_ex(msg, last_errno));
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void throw_spdlog_ex(std::string msg) { SPDLOG_THROW(spdlog_ex(std::move(msg))); }
|
||||
|
||||
} // namespace spdlog
|
||||
+406
@@ -0,0 +1,406 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <spdlog/details/null_mutex.h>
|
||||
#include <spdlog/tweakme.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
#include <exception>
|
||||
#include <functional>
|
||||
#include <initializer_list>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
#ifdef SPDLOG_USE_STD_FORMAT
|
||||
#include <version>
|
||||
#if __cpp_lib_format >= 202207L
|
||||
#include <format>
|
||||
#else
|
||||
#include <string_view>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef SPDLOG_COMPILED_LIB
|
||||
#undef SPDLOG_HEADER_ONLY
|
||||
#if defined(SPDLOG_SHARED_LIB)
|
||||
#if defined(_WIN32)
|
||||
#ifdef spdlog_EXPORTS
|
||||
#define SPDLOG_API __declspec(dllexport)
|
||||
#else // !spdlog_EXPORTS
|
||||
#define SPDLOG_API __declspec(dllimport)
|
||||
#endif
|
||||
#else // !defined(_WIN32)
|
||||
#define SPDLOG_API __attribute__((visibility("default")))
|
||||
#endif
|
||||
#else // !defined(SPDLOG_SHARED_LIB)
|
||||
#define SPDLOG_API
|
||||
#endif
|
||||
#define SPDLOG_INLINE
|
||||
#else // !defined(SPDLOG_COMPILED_LIB)
|
||||
#define SPDLOG_API
|
||||
#define SPDLOG_HEADER_ONLY
|
||||
#define SPDLOG_INLINE inline
|
||||
#endif // #ifdef SPDLOG_COMPILED_LIB
|
||||
|
||||
#include <spdlog/fmt/fmt.h>
|
||||
|
||||
#if !defined(SPDLOG_USE_STD_FORMAT) && \
|
||||
FMT_VERSION >= 80000 // backward compatibility with fmt versions older than 8
|
||||
#define SPDLOG_FMT_RUNTIME(format_string) fmt::runtime(format_string)
|
||||
#define SPDLOG_FMT_STRING(format_string) FMT_STRING(format_string)
|
||||
#if defined(SPDLOG_WCHAR_FILENAMES) || defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT)
|
||||
#include <spdlog/fmt/xchar.h>
|
||||
#endif
|
||||
#else
|
||||
#define SPDLOG_FMT_RUNTIME(format_string) format_string
|
||||
#define SPDLOG_FMT_STRING(format_string) format_string
|
||||
#endif
|
||||
|
||||
// visual studio up to 2013 does not support noexcept nor constexpr
|
||||
#if defined(_MSC_VER) && (_MSC_VER < 1900)
|
||||
#define SPDLOG_NOEXCEPT _NOEXCEPT
|
||||
#define SPDLOG_CONSTEXPR
|
||||
#else
|
||||
#define SPDLOG_NOEXCEPT noexcept
|
||||
#define SPDLOG_CONSTEXPR constexpr
|
||||
#endif
|
||||
|
||||
// If building with std::format, can just use constexpr, otherwise if building with fmt
|
||||
// SPDLOG_CONSTEXPR_FUNC needs to be set the same as FMT_CONSTEXPR to avoid situations where
|
||||
// a constexpr function in spdlog could end up calling a non-constexpr function in fmt
|
||||
// depending on the compiler
|
||||
// If fmt determines it can't use constexpr, we should inline the function instead
|
||||
#ifdef SPDLOG_USE_STD_FORMAT
|
||||
#define SPDLOG_CONSTEXPR_FUNC constexpr
|
||||
#else // Being built with fmt
|
||||
#if FMT_USE_CONSTEXPR
|
||||
#define SPDLOG_CONSTEXPR_FUNC FMT_CONSTEXPR
|
||||
#else
|
||||
#define SPDLOG_CONSTEXPR_FUNC inline
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#define SPDLOG_DEPRECATED __attribute__((deprecated))
|
||||
#elif defined(_MSC_VER)
|
||||
#define SPDLOG_DEPRECATED __declspec(deprecated)
|
||||
#else
|
||||
#define SPDLOG_DEPRECATED
|
||||
#endif
|
||||
|
||||
// disable thread local on msvc 2013
|
||||
#ifndef SPDLOG_NO_TLS
|
||||
#if (defined(_MSC_VER) && (_MSC_VER < 1900)) || defined(__cplusplus_winrt)
|
||||
#define SPDLOG_NO_TLS 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef SPDLOG_FUNCTION
|
||||
#define SPDLOG_FUNCTION static_cast<const char *>(__FUNCTION__)
|
||||
#endif
|
||||
|
||||
#ifdef SPDLOG_NO_EXCEPTIONS
|
||||
#define SPDLOG_TRY
|
||||
#define SPDLOG_THROW(ex) \
|
||||
do { \
|
||||
printf("spdlog fatal error: %s\n", ex.what()); \
|
||||
std::abort(); \
|
||||
} while (0)
|
||||
#define SPDLOG_CATCH_STD
|
||||
#else
|
||||
#define SPDLOG_TRY try
|
||||
#define SPDLOG_THROW(ex) throw(ex)
|
||||
#define SPDLOG_CATCH_STD \
|
||||
catch (const std::exception &) { \
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace spdlog {
|
||||
|
||||
class formatter;
|
||||
|
||||
namespace sinks {
|
||||
class sink;
|
||||
}
|
||||
|
||||
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
|
||||
using filename_t = std::wstring;
|
||||
// allow macro expansion to occur in SPDLOG_FILENAME_T
|
||||
#define SPDLOG_FILENAME_T_INNER(s) L##s
|
||||
#define SPDLOG_FILENAME_T(s) SPDLOG_FILENAME_T_INNER(s)
|
||||
#else
|
||||
using filename_t = std::string;
|
||||
#define SPDLOG_FILENAME_T(s) s
|
||||
#endif
|
||||
|
||||
using log_clock = std::chrono::system_clock;
|
||||
using sink_ptr = std::shared_ptr<sinks::sink>;
|
||||
using sinks_init_list = std::initializer_list<sink_ptr>;
|
||||
using err_handler = std::function<void(const std::string &err_msg)>;
|
||||
#ifdef SPDLOG_USE_STD_FORMAT
|
||||
namespace fmt_lib = std;
|
||||
|
||||
using string_view_t = std::string_view;
|
||||
using memory_buf_t = std::string;
|
||||
|
||||
template <typename... Args>
|
||||
#if __cpp_lib_format >= 202207L
|
||||
using format_string_t = std::format_string<Args...>;
|
||||
#else
|
||||
using format_string_t = std::string_view;
|
||||
#endif
|
||||
|
||||
template <class T, class Char = char>
|
||||
struct is_convertible_to_basic_format_string
|
||||
: std::integral_constant<bool, std::is_convertible<T, std::basic_string_view<Char>>::value> {};
|
||||
|
||||
#if defined(SPDLOG_WCHAR_FILENAMES) || defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT)
|
||||
using wstring_view_t = std::wstring_view;
|
||||
using wmemory_buf_t = std::wstring;
|
||||
|
||||
template <typename... Args>
|
||||
#if __cpp_lib_format >= 202207L
|
||||
using wformat_string_t = std::wformat_string<Args...>;
|
||||
#else
|
||||
using wformat_string_t = std::wstring_view;
|
||||
#endif
|
||||
#endif
|
||||
#define SPDLOG_BUF_TO_STRING(x) x
|
||||
#else // use fmt lib instead of std::format
|
||||
namespace fmt_lib = fmt;
|
||||
|
||||
using string_view_t = fmt::basic_string_view<char>;
|
||||
using memory_buf_t = fmt::basic_memory_buffer<char, 250>;
|
||||
|
||||
template <typename... Args>
|
||||
using format_string_t = fmt::format_string<Args...>;
|
||||
|
||||
template <class T>
|
||||
using remove_cvref_t = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
|
||||
|
||||
template <typename Char>
|
||||
#if FMT_VERSION >= 90101
|
||||
using fmt_runtime_string = fmt::runtime_format_string<Char>;
|
||||
#else
|
||||
using fmt_runtime_string = fmt::basic_runtime<Char>;
|
||||
#endif
|
||||
|
||||
// clang doesn't like SFINAE disabled constructor in std::is_convertible<> so have to repeat the
|
||||
// condition from basic_format_string here, in addition, fmt::basic_runtime<Char> is only
|
||||
// convertible to basic_format_string<Char> but not basic_string_view<Char>
|
||||
template <class T, class Char = char>
|
||||
struct is_convertible_to_basic_format_string
|
||||
: std::integral_constant<bool,
|
||||
std::is_convertible<T, fmt::basic_string_view<Char>>::value ||
|
||||
std::is_same<remove_cvref_t<T>, fmt_runtime_string<Char>>::value> {
|
||||
};
|
||||
|
||||
#if defined(SPDLOG_WCHAR_FILENAMES) || defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT)
|
||||
using wstring_view_t = fmt::basic_string_view<wchar_t>;
|
||||
using wmemory_buf_t = fmt::basic_memory_buffer<wchar_t, 250>;
|
||||
|
||||
template <typename... Args>
|
||||
using wformat_string_t = fmt::wformat_string<Args...>;
|
||||
#endif
|
||||
#define SPDLOG_BUF_TO_STRING(x) fmt::to_string(x)
|
||||
#endif
|
||||
|
||||
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
||||
#ifndef _WIN32
|
||||
#error SPDLOG_WCHAR_TO_UTF8_SUPPORT only supported on windows
|
||||
#endif // _WIN32
|
||||
#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
||||
|
||||
template <class T>
|
||||
struct is_convertible_to_any_format_string
|
||||
: std::integral_constant<bool,
|
||||
is_convertible_to_basic_format_string<T, char>::value ||
|
||||
is_convertible_to_basic_format_string<T, wchar_t>::value> {};
|
||||
|
||||
#if defined(SPDLOG_NO_ATOMIC_LEVELS)
|
||||
using level_t = details::null_atomic_int;
|
||||
#else
|
||||
using level_t = std::atomic<int>;
|
||||
#endif
|
||||
|
||||
#define SPDLOG_LEVEL_TRACE 0
|
||||
#define SPDLOG_LEVEL_DEBUG 1
|
||||
#define SPDLOG_LEVEL_INFO 2
|
||||
#define SPDLOG_LEVEL_WARN 3
|
||||
#define SPDLOG_LEVEL_ERROR 4
|
||||
#define SPDLOG_LEVEL_CRITICAL 5
|
||||
#define SPDLOG_LEVEL_OFF 6
|
||||
|
||||
#if !defined(SPDLOG_ACTIVE_LEVEL)
|
||||
#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO
|
||||
#endif
|
||||
|
||||
// Log level enum
|
||||
namespace level {
|
||||
enum level_enum : int {
|
||||
trace = SPDLOG_LEVEL_TRACE,
|
||||
debug = SPDLOG_LEVEL_DEBUG,
|
||||
info = SPDLOG_LEVEL_INFO,
|
||||
warn = SPDLOG_LEVEL_WARN,
|
||||
err = SPDLOG_LEVEL_ERROR,
|
||||
critical = SPDLOG_LEVEL_CRITICAL,
|
||||
off = SPDLOG_LEVEL_OFF,
|
||||
n_levels
|
||||
};
|
||||
|
||||
#define SPDLOG_LEVEL_NAME_TRACE spdlog::string_view_t("trace", 5)
|
||||
#define SPDLOG_LEVEL_NAME_DEBUG spdlog::string_view_t("debug", 5)
|
||||
#define SPDLOG_LEVEL_NAME_INFO spdlog::string_view_t("info", 4)
|
||||
#define SPDLOG_LEVEL_NAME_WARNING spdlog::string_view_t("warning", 7)
|
||||
#define SPDLOG_LEVEL_NAME_ERROR spdlog::string_view_t("error", 5)
|
||||
#define SPDLOG_LEVEL_NAME_CRITICAL spdlog::string_view_t("critical", 8)
|
||||
#define SPDLOG_LEVEL_NAME_OFF spdlog::string_view_t("off", 3)
|
||||
|
||||
#if !defined(SPDLOG_LEVEL_NAMES)
|
||||
#define SPDLOG_LEVEL_NAMES \
|
||||
{ \
|
||||
SPDLOG_LEVEL_NAME_TRACE, SPDLOG_LEVEL_NAME_DEBUG, SPDLOG_LEVEL_NAME_INFO, \
|
||||
SPDLOG_LEVEL_NAME_WARNING, SPDLOG_LEVEL_NAME_ERROR, SPDLOG_LEVEL_NAME_CRITICAL, \
|
||||
SPDLOG_LEVEL_NAME_OFF \
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !defined(SPDLOG_SHORT_LEVEL_NAMES)
|
||||
|
||||
#define SPDLOG_SHORT_LEVEL_NAMES \
|
||||
{ "T", "D", "I", "W", "E", "C", "O" }
|
||||
#endif
|
||||
|
||||
SPDLOG_API const string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOEXCEPT;
|
||||
SPDLOG_API const char *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOEXCEPT;
|
||||
SPDLOG_API spdlog::level::level_enum from_str(const std::string &name) SPDLOG_NOEXCEPT;
|
||||
|
||||
} // namespace level
|
||||
|
||||
//
|
||||
// Color mode used by sinks with color support.
|
||||
//
|
||||
enum class color_mode { always, automatic, never };
|
||||
|
||||
//
|
||||
// Pattern time - specific time getting to use for pattern_formatter.
|
||||
// local time by default
|
||||
//
|
||||
enum class pattern_time_type {
|
||||
local, // log localtime
|
||||
utc // log utc
|
||||
};
|
||||
|
||||
//
|
||||
// Log exception
|
||||
//
|
||||
class SPDLOG_API spdlog_ex : public std::exception {
|
||||
public:
|
||||
explicit spdlog_ex(std::string msg);
|
||||
spdlog_ex(const std::string &msg, int last_errno);
|
||||
const char *what() const SPDLOG_NOEXCEPT override;
|
||||
|
||||
private:
|
||||
std::string msg_;
|
||||
};
|
||||
|
||||
[[noreturn]] SPDLOG_API void throw_spdlog_ex(const std::string &msg, int last_errno);
|
||||
[[noreturn]] SPDLOG_API void throw_spdlog_ex(std::string msg);
|
||||
|
||||
struct source_loc {
|
||||
SPDLOG_CONSTEXPR source_loc() = default;
|
||||
SPDLOG_CONSTEXPR source_loc(const char *filename_in, int line_in, const char *funcname_in)
|
||||
: filename{filename_in},
|
||||
line{line_in},
|
||||
funcname{funcname_in} {}
|
||||
|
||||
SPDLOG_CONSTEXPR bool empty() const SPDLOG_NOEXCEPT { return line <= 0; }
|
||||
const char *filename{nullptr};
|
||||
int line{0};
|
||||
const char *funcname{nullptr};
|
||||
};
|
||||
|
||||
struct file_event_handlers {
|
||||
file_event_handlers()
|
||||
: before_open(nullptr),
|
||||
after_open(nullptr),
|
||||
before_close(nullptr),
|
||||
after_close(nullptr) {}
|
||||
|
||||
std::function<void(const filename_t &filename)> before_open;
|
||||
std::function<void(const filename_t &filename, std::FILE *file_stream)> after_open;
|
||||
std::function<void(const filename_t &filename, std::FILE *file_stream)> before_close;
|
||||
std::function<void(const filename_t &filename)> after_close;
|
||||
};
|
||||
|
||||
namespace details {
|
||||
|
||||
// to_string_view
|
||||
|
||||
SPDLOG_CONSTEXPR_FUNC spdlog::string_view_t to_string_view(const memory_buf_t &buf)
|
||||
SPDLOG_NOEXCEPT {
|
||||
return spdlog::string_view_t{buf.data(), buf.size()};
|
||||
}
|
||||
|
||||
SPDLOG_CONSTEXPR_FUNC spdlog::string_view_t to_string_view(spdlog::string_view_t str)
|
||||
SPDLOG_NOEXCEPT {
|
||||
return str;
|
||||
}
|
||||
|
||||
#if defined(SPDLOG_WCHAR_FILENAMES) || defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT)
|
||||
SPDLOG_CONSTEXPR_FUNC spdlog::wstring_view_t to_string_view(const wmemory_buf_t &buf)
|
||||
SPDLOG_NOEXCEPT {
|
||||
return spdlog::wstring_view_t{buf.data(), buf.size()};
|
||||
}
|
||||
|
||||
SPDLOG_CONSTEXPR_FUNC spdlog::wstring_view_t to_string_view(spdlog::wstring_view_t str)
|
||||
SPDLOG_NOEXCEPT {
|
||||
return str;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(SPDLOG_USE_STD_FORMAT) && __cpp_lib_format >= 202207L
|
||||
template <typename T, typename... Args>
|
||||
SPDLOG_CONSTEXPR_FUNC std::basic_string_view<T> to_string_view(
|
||||
std::basic_format_string<T, Args...> fmt) SPDLOG_NOEXCEPT {
|
||||
return fmt.get();
|
||||
}
|
||||
#endif
|
||||
|
||||
// make_unique support for pre c++14
|
||||
#if __cplusplus >= 201402L // C++14 and beyond
|
||||
using std::enable_if_t;
|
||||
using std::make_unique;
|
||||
#else
|
||||
template <bool B, class T = void>
|
||||
using enable_if_t = typename std::enable_if<B, T>::type;
|
||||
|
||||
template <typename T, typename... Args>
|
||||
std::unique_ptr<T> make_unique(Args &&...args) {
|
||||
static_assert(!std::is_array<T>::value, "arrays not supported");
|
||||
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
|
||||
}
|
||||
#endif
|
||||
|
||||
// to avoid useless casts (see https://github.com/nlohmann/json/issues/2893#issuecomment-889152324)
|
||||
template <typename T, typename U, enable_if_t<!std::is_same<T, U>::value, int> = 0>
|
||||
constexpr T conditional_static_cast(U value) {
|
||||
return static_cast<T>(value);
|
||||
}
|
||||
|
||||
template <typename T, typename U, enable_if_t<std::is_same<T, U>::value, int> = 0>
|
||||
constexpr T conditional_static_cast(U value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
|
||||
#ifdef SPDLOG_HEADER_ONLY
|
||||
#include "common-inl.h"
|
||||
#endif
|
||||
@@ -0,0 +1,63 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef SPDLOG_HEADER_ONLY
|
||||
#include <spdlog/details/backtracer.h>
|
||||
#endif
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
SPDLOG_INLINE backtracer::backtracer(const backtracer &other) {
|
||||
std::lock_guard<std::mutex> lock(other.mutex_);
|
||||
enabled_ = other.enabled();
|
||||
messages_ = other.messages_;
|
||||
}
|
||||
|
||||
SPDLOG_INLINE backtracer::backtracer(backtracer &&other) SPDLOG_NOEXCEPT {
|
||||
std::lock_guard<std::mutex> lock(other.mutex_);
|
||||
enabled_ = other.enabled();
|
||||
messages_ = std::move(other.messages_);
|
||||
}
|
||||
|
||||
SPDLOG_INLINE backtracer &backtracer::operator=(backtracer other) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
enabled_ = other.enabled();
|
||||
messages_ = std::move(other.messages_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void backtracer::enable(size_t size) {
|
||||
std::lock_guard<std::mutex> lock{mutex_};
|
||||
enabled_.store(true, std::memory_order_relaxed);
|
||||
messages_ = circular_q<log_msg_buffer>{size};
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void backtracer::disable() {
|
||||
std::lock_guard<std::mutex> lock{mutex_};
|
||||
enabled_.store(false, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
SPDLOG_INLINE bool backtracer::enabled() const { return enabled_.load(std::memory_order_relaxed); }
|
||||
|
||||
SPDLOG_INLINE void backtracer::push_back(const log_msg &msg) {
|
||||
std::lock_guard<std::mutex> lock{mutex_};
|
||||
messages_.push_back(log_msg_buffer{msg});
|
||||
}
|
||||
|
||||
SPDLOG_INLINE bool backtracer::empty() const {
|
||||
std::lock_guard<std::mutex> lock{mutex_};
|
||||
return messages_.empty();
|
||||
}
|
||||
|
||||
// pop all items in the q and apply the given fun on each of them.
|
||||
SPDLOG_INLINE void backtracer::foreach_pop(std::function<void(const details::log_msg &)> fun) {
|
||||
std::lock_guard<std::mutex> lock{mutex_};
|
||||
while (!messages_.empty()) {
|
||||
auto &front_msg = messages_.front();
|
||||
fun(front_msg);
|
||||
messages_.pop_front();
|
||||
}
|
||||
}
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
@@ -0,0 +1,45 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <spdlog/details/circular_q.h>
|
||||
#include <spdlog/details/log_msg_buffer.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
|
||||
// Store log messages in circular buffer.
|
||||
// Useful for storing debug data in case of error/warning happens.
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
class SPDLOG_API backtracer {
|
||||
mutable std::mutex mutex_;
|
||||
std::atomic<bool> enabled_{false};
|
||||
circular_q<log_msg_buffer> messages_;
|
||||
|
||||
public:
|
||||
backtracer() = default;
|
||||
backtracer(const backtracer &other);
|
||||
|
||||
backtracer(backtracer &&other) SPDLOG_NOEXCEPT;
|
||||
backtracer &operator=(backtracer other);
|
||||
|
||||
void enable(size_t size);
|
||||
void disable();
|
||||
bool enabled() const;
|
||||
void push_back(const log_msg &msg);
|
||||
bool empty() const;
|
||||
|
||||
// pop all items in the q and apply the given fun on each of them.
|
||||
void foreach_pop(std::function<void(const details::log_msg &)> fun);
|
||||
};
|
||||
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
|
||||
#ifdef SPDLOG_HEADER_ONLY
|
||||
#include "backtracer-inl.h"
|
||||
#endif
|
||||
@@ -0,0 +1,115 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
// circular q view of std::vector.
|
||||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
|
||||
#include "spdlog/common.h"
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
template <typename T>
|
||||
class circular_q {
|
||||
size_t max_items_ = 0;
|
||||
typename std::vector<T>::size_type head_ = 0;
|
||||
typename std::vector<T>::size_type tail_ = 0;
|
||||
size_t overrun_counter_ = 0;
|
||||
std::vector<T> v_;
|
||||
|
||||
public:
|
||||
using value_type = T;
|
||||
|
||||
// empty ctor - create a disabled queue with no elements allocated at all
|
||||
circular_q() = default;
|
||||
|
||||
explicit circular_q(size_t max_items)
|
||||
: max_items_(max_items + 1) // one item is reserved as marker for full q
|
||||
,
|
||||
v_(max_items_) {}
|
||||
|
||||
circular_q(const circular_q &) = default;
|
||||
circular_q &operator=(const circular_q &) = default;
|
||||
|
||||
// move cannot be default,
|
||||
// since we need to reset head_, tail_, etc to zero in the moved object
|
||||
circular_q(circular_q &&other) SPDLOG_NOEXCEPT { copy_moveable(std::move(other)); }
|
||||
|
||||
circular_q &operator=(circular_q &&other) SPDLOG_NOEXCEPT {
|
||||
copy_moveable(std::move(other));
|
||||
return *this;
|
||||
}
|
||||
|
||||
// push back, overrun (oldest) item if no room left
|
||||
void push_back(T &&item) {
|
||||
if (max_items_ > 0) {
|
||||
v_[tail_] = std::move(item);
|
||||
tail_ = (tail_ + 1) % max_items_;
|
||||
|
||||
if (tail_ == head_) // overrun last item if full
|
||||
{
|
||||
head_ = (head_ + 1) % max_items_;
|
||||
++overrun_counter_;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return reference to the front item.
|
||||
// If there are no elements in the container, the behavior is undefined.
|
||||
const T &front() const { return v_[head_]; }
|
||||
|
||||
T &front() { return v_[head_]; }
|
||||
|
||||
// Return number of elements actually stored
|
||||
size_t size() const {
|
||||
if (tail_ >= head_) {
|
||||
return tail_ - head_;
|
||||
} else {
|
||||
return max_items_ - (head_ - tail_);
|
||||
}
|
||||
}
|
||||
|
||||
// Return const reference to item by index.
|
||||
// If index is out of range 0…size()-1, the behavior is undefined.
|
||||
const T &at(size_t i) const {
|
||||
assert(i < size());
|
||||
return v_[(head_ + i) % max_items_];
|
||||
}
|
||||
|
||||
// Pop item from front.
|
||||
// If there are no elements in the container, the behavior is undefined.
|
||||
void pop_front() { head_ = (head_ + 1) % max_items_; }
|
||||
|
||||
bool empty() const { return tail_ == head_; }
|
||||
|
||||
bool full() const {
|
||||
// head is ahead of the tail by 1
|
||||
if (max_items_ > 0) {
|
||||
return ((tail_ + 1) % max_items_) == head_;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t overrun_counter() const { return overrun_counter_; }
|
||||
|
||||
void reset_overrun_counter() { overrun_counter_ = 0; }
|
||||
|
||||
private:
|
||||
// copy from other&& and reset it to disabled state
|
||||
void copy_moveable(circular_q &&other) SPDLOG_NOEXCEPT {
|
||||
max_items_ = other.max_items_;
|
||||
head_ = other.head_;
|
||||
tail_ = other.tail_;
|
||||
overrun_counter_ = other.overrun_counter_;
|
||||
v_ = std::move(other.v_);
|
||||
|
||||
// put &&other in disabled, but valid state
|
||||
other.max_items_ = 0;
|
||||
other.head_ = other.tail_ = 0;
|
||||
other.overrun_counter_ = 0;
|
||||
}
|
||||
};
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
@@ -0,0 +1,28 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include <spdlog/details/null_mutex.h>
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
|
||||
struct console_mutex {
|
||||
using mutex_t = std::mutex;
|
||||
static mutex_t &mutex() {
|
||||
static mutex_t s_mutex;
|
||||
return s_mutex;
|
||||
}
|
||||
};
|
||||
|
||||
struct console_nullmutex {
|
||||
using mutex_t = null_mutex;
|
||||
static mutex_t &mutex() {
|
||||
static mutex_t s_mutex;
|
||||
return s_mutex;
|
||||
}
|
||||
};
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
@@ -0,0 +1,151 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef SPDLOG_HEADER_ONLY
|
||||
#include <spdlog/details/file_helper.h>
|
||||
#endif
|
||||
|
||||
#include <spdlog/common.h>
|
||||
#include <spdlog/details/os.h>
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
|
||||
SPDLOG_INLINE file_helper::file_helper(const file_event_handlers &event_handlers)
|
||||
: event_handlers_(event_handlers) {}
|
||||
|
||||
SPDLOG_INLINE file_helper::~file_helper() { close(); }
|
||||
|
||||
SPDLOG_INLINE void file_helper::open(const filename_t &fname, bool truncate) {
|
||||
close();
|
||||
filename_ = fname;
|
||||
|
||||
auto *mode = SPDLOG_FILENAME_T("ab");
|
||||
auto *trunc_mode = SPDLOG_FILENAME_T("wb");
|
||||
|
||||
if (event_handlers_.before_open) {
|
||||
event_handlers_.before_open(filename_);
|
||||
}
|
||||
for (int tries = 0; tries < open_tries_; ++tries) {
|
||||
// create containing folder if not exists already.
|
||||
os::create_dir(os::dir_name(fname));
|
||||
if (truncate) {
|
||||
// Truncate by opening-and-closing a tmp file in "wb" mode, always
|
||||
// opening the actual log-we-write-to in "ab" mode, since that
|
||||
// interacts more politely with eternal processes that might
|
||||
// rotate/truncate the file underneath us.
|
||||
std::FILE *tmp;
|
||||
if (os::fopen_s(&tmp, fname, trunc_mode)) {
|
||||
continue;
|
||||
}
|
||||
std::fclose(tmp);
|
||||
}
|
||||
if (!os::fopen_s(&fd_, fname, mode)) {
|
||||
if (event_handlers_.after_open) {
|
||||
event_handlers_.after_open(filename_, fd_);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
details::os::sleep_for_millis(open_interval_);
|
||||
}
|
||||
|
||||
throw_spdlog_ex("Failed opening file " + os::filename_to_str(filename_) + " for writing",
|
||||
errno);
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void file_helper::reopen(bool truncate) {
|
||||
if (filename_.empty()) {
|
||||
throw_spdlog_ex("Failed re opening file - was not opened before");
|
||||
}
|
||||
this->open(filename_, truncate);
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void file_helper::flush() {
|
||||
if (std::fflush(fd_) != 0) {
|
||||
throw_spdlog_ex("Failed flush to file " + os::filename_to_str(filename_), errno);
|
||||
}
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void file_helper::sync() {
|
||||
if (!os::fsync(fd_)) {
|
||||
throw_spdlog_ex("Failed to fsync file " + os::filename_to_str(filename_), errno);
|
||||
}
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void file_helper::close() {
|
||||
if (fd_ != nullptr) {
|
||||
if (event_handlers_.before_close) {
|
||||
event_handlers_.before_close(filename_, fd_);
|
||||
}
|
||||
|
||||
std::fclose(fd_);
|
||||
fd_ = nullptr;
|
||||
|
||||
if (event_handlers_.after_close) {
|
||||
event_handlers_.after_close(filename_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void file_helper::write(const memory_buf_t &buf) {
|
||||
if (fd_ == nullptr) return;
|
||||
size_t msg_size = buf.size();
|
||||
auto data = buf.data();
|
||||
|
||||
if (!details::os::fwrite_bytes(data, msg_size, fd_)) {
|
||||
throw_spdlog_ex("Failed writing to file " + os::filename_to_str(filename_), errno);
|
||||
}
|
||||
}
|
||||
|
||||
SPDLOG_INLINE size_t file_helper::size() const {
|
||||
if (fd_ == nullptr) {
|
||||
throw_spdlog_ex("Cannot use size() on closed file " + os::filename_to_str(filename_));
|
||||
}
|
||||
return os::filesize(fd_);
|
||||
}
|
||||
|
||||
SPDLOG_INLINE const filename_t &file_helper::filename() const { return filename_; }
|
||||
|
||||
//
|
||||
// return file path and its extension:
|
||||
//
|
||||
// "mylog.txt" => ("mylog", ".txt")
|
||||
// "mylog" => ("mylog", "")
|
||||
// "mylog." => ("mylog.", "")
|
||||
// "/dir1/dir2/mylog.txt" => ("/dir1/dir2/mylog", ".txt")
|
||||
//
|
||||
// the starting dot in filenames is ignored (hidden files):
|
||||
//
|
||||
// ".mylog" => (".mylog". "")
|
||||
// "my_folder/.mylog" => ("my_folder/.mylog", "")
|
||||
// "my_folder/.mylog.txt" => ("my_folder/.mylog", ".txt")
|
||||
SPDLOG_INLINE std::tuple<filename_t, filename_t> file_helper::split_by_extension(
|
||||
const filename_t &fname) {
|
||||
auto ext_index = fname.rfind('.');
|
||||
|
||||
// no valid extension found - return whole path and empty string as
|
||||
// extension
|
||||
if (ext_index == filename_t::npos || ext_index == 0 || ext_index == fname.size() - 1) {
|
||||
return std::make_tuple(fname, filename_t());
|
||||
}
|
||||
|
||||
// treat cases like "/etc/rc.d/somelogfile or "/abc/.hiddenfile"
|
||||
auto folder_index = fname.find_last_of(details::os::folder_seps_filename);
|
||||
if (folder_index != filename_t::npos && folder_index >= ext_index - 1) {
|
||||
return std::make_tuple(fname, filename_t());
|
||||
}
|
||||
|
||||
// finally - return a valid base and extension tuple
|
||||
return std::make_tuple(fname.substr(0, ext_index), fname.substr(ext_index));
|
||||
}
|
||||
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
@@ -0,0 +1,61 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <spdlog/common.h>
|
||||
#include <tuple>
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
|
||||
// Helper class for file sinks.
|
||||
// When failing to open a file, retry several times(5) with a delay interval(10 ms).
|
||||
// Throw spdlog_ex exception on errors.
|
||||
|
||||
class SPDLOG_API file_helper {
|
||||
public:
|
||||
file_helper() = default;
|
||||
explicit file_helper(const file_event_handlers &event_handlers);
|
||||
|
||||
file_helper(const file_helper &) = delete;
|
||||
file_helper &operator=(const file_helper &) = delete;
|
||||
~file_helper();
|
||||
|
||||
void open(const filename_t &fname, bool truncate = false);
|
||||
void reopen(bool truncate);
|
||||
void flush();
|
||||
void sync();
|
||||
void close();
|
||||
void write(const memory_buf_t &buf);
|
||||
size_t size() const;
|
||||
const filename_t &filename() const;
|
||||
|
||||
//
|
||||
// return file path and its extension:
|
||||
//
|
||||
// "mylog.txt" => ("mylog", ".txt")
|
||||
// "mylog" => ("mylog", "")
|
||||
// "mylog." => ("mylog.", "")
|
||||
// "/dir1/dir2/mylog.txt" => ("/dir1/dir2/mylog", ".txt")
|
||||
//
|
||||
// the starting dot in filenames is ignored (hidden files):
|
||||
//
|
||||
// ".mylog" => (".mylog". "")
|
||||
// "my_folder/.mylog" => ("my_folder/.mylog", "")
|
||||
// "my_folder/.mylog.txt" => ("my_folder/.mylog", ".txt")
|
||||
static std::tuple<filename_t, filename_t> split_by_extension(const filename_t &fname);
|
||||
|
||||
private:
|
||||
const int open_tries_ = 5;
|
||||
const unsigned int open_interval_ = 10;
|
||||
std::FILE *fd_{nullptr};
|
||||
filename_t filename_;
|
||||
file_event_handlers event_handlers_;
|
||||
};
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
|
||||
#ifdef SPDLOG_HEADER_ONLY
|
||||
#include "file_helper-inl.h"
|
||||
#endif
|
||||
@@ -0,0 +1,141 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <iterator>
|
||||
#include <spdlog/common.h>
|
||||
#include <spdlog/fmt/fmt.h>
|
||||
#include <type_traits>
|
||||
|
||||
#ifdef SPDLOG_USE_STD_FORMAT
|
||||
#include <charconv>
|
||||
#include <limits>
|
||||
#endif
|
||||
|
||||
// Some fmt helpers to efficiently format and pad ints and strings
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
namespace fmt_helper {
|
||||
|
||||
inline void append_string_view(spdlog::string_view_t view, memory_buf_t &dest) {
|
||||
auto *buf_ptr = view.data();
|
||||
dest.append(buf_ptr, buf_ptr + view.size());
|
||||
}
|
||||
|
||||
#ifdef SPDLOG_USE_STD_FORMAT
|
||||
template <typename T>
|
||||
inline void append_int(T n, memory_buf_t &dest) {
|
||||
// Buffer should be large enough to hold all digits (digits10 + 1) and a sign
|
||||
SPDLOG_CONSTEXPR const auto BUF_SIZE = std::numeric_limits<T>::digits10 + 2;
|
||||
char buf[BUF_SIZE];
|
||||
|
||||
auto [ptr, ec] = std::to_chars(buf, buf + BUF_SIZE, n, 10);
|
||||
if (ec == std::errc()) {
|
||||
dest.append(buf, ptr);
|
||||
} else {
|
||||
throw_spdlog_ex("Failed to format int", static_cast<int>(ec));
|
||||
}
|
||||
}
|
||||
#else
|
||||
template <typename T>
|
||||
inline void append_int(T n, memory_buf_t &dest) {
|
||||
fmt::format_int i(n);
|
||||
dest.append(i.data(), i.data() + i.size());
|
||||
}
|
||||
#endif
|
||||
|
||||
template <typename T>
|
||||
SPDLOG_CONSTEXPR_FUNC unsigned int count_digits_fallback(T n) {
|
||||
// taken from fmt: https://github.com/fmtlib/fmt/blob/8.0.1/include/fmt/format.h#L899-L912
|
||||
unsigned int count = 1;
|
||||
for (;;) {
|
||||
// Integer division is slow so do it for a group of four digits instead
|
||||
// of for every digit. The idea comes from the talk by Alexandrescu
|
||||
// "Three Optimization Tips for C++". See speed-test for a comparison.
|
||||
if (n < 10) return count;
|
||||
if (n < 100) return count + 1;
|
||||
if (n < 1000) return count + 2;
|
||||
if (n < 10000) return count + 3;
|
||||
n /= 10000u;
|
||||
count += 4;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline unsigned int count_digits(T n) {
|
||||
using count_type =
|
||||
typename std::conditional<(sizeof(T) > sizeof(uint32_t)), uint64_t, uint32_t>::type;
|
||||
#ifdef SPDLOG_USE_STD_FORMAT
|
||||
return count_digits_fallback(static_cast<count_type>(n));
|
||||
#else
|
||||
return static_cast<unsigned int>(fmt::
|
||||
// fmt 7.0.0 renamed the internal namespace to detail.
|
||||
// See: https://github.com/fmtlib/fmt/issues/1538
|
||||
#if FMT_VERSION < 70000
|
||||
internal
|
||||
#else
|
||||
detail
|
||||
#endif
|
||||
::count_digits(static_cast<count_type>(n)));
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void pad2(int n, memory_buf_t &dest) {
|
||||
if (n >= 0 && n < 100) // 0-99
|
||||
{
|
||||
dest.push_back(static_cast<char>('0' + n / 10));
|
||||
dest.push_back(static_cast<char>('0' + n % 10));
|
||||
} else // unlikely, but just in case, let fmt deal with it
|
||||
{
|
||||
fmt_lib::format_to(std::back_inserter(dest), SPDLOG_FMT_STRING("{:02}"), n);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void pad_uint(T n, unsigned int width, memory_buf_t &dest) {
|
||||
static_assert(std::is_unsigned<T>::value, "pad_uint must get unsigned T");
|
||||
for (auto digits = count_digits(n); digits < width; digits++) {
|
||||
dest.push_back('0');
|
||||
}
|
||||
append_int(n, dest);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void pad3(T n, memory_buf_t &dest) {
|
||||
static_assert(std::is_unsigned<T>::value, "pad3 must get unsigned T");
|
||||
if (n < 1000) {
|
||||
dest.push_back(static_cast<char>(n / 100 + '0'));
|
||||
n = n % 100;
|
||||
dest.push_back(static_cast<char>((n / 10) + '0'));
|
||||
dest.push_back(static_cast<char>((n % 10) + '0'));
|
||||
} else {
|
||||
append_int(n, dest);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void pad6(T n, memory_buf_t &dest) {
|
||||
pad_uint(n, 6, dest);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void pad9(T n, memory_buf_t &dest) {
|
||||
pad_uint(n, 9, dest);
|
||||
}
|
||||
|
||||
// return fraction of a second of the given time_point.
|
||||
// e.g.
|
||||
// fraction<std::milliseconds>(tp) -> will return the millis part of the second
|
||||
template <typename ToDuration>
|
||||
inline ToDuration time_fraction(log_clock::time_point tp) {
|
||||
using std::chrono::duration_cast;
|
||||
using std::chrono::seconds;
|
||||
auto duration = tp.time_since_epoch();
|
||||
auto secs = duration_cast<seconds>(duration);
|
||||
return duration_cast<ToDuration>(duration) - duration_cast<ToDuration>(secs);
|
||||
}
|
||||
|
||||
} // namespace fmt_helper
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
@@ -0,0 +1,44 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef SPDLOG_HEADER_ONLY
|
||||
#include <spdlog/details/log_msg.h>
|
||||
#endif
|
||||
|
||||
#include <spdlog/details/os.h>
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
|
||||
SPDLOG_INLINE log_msg::log_msg(spdlog::log_clock::time_point log_time,
|
||||
spdlog::source_loc loc,
|
||||
string_view_t a_logger_name,
|
||||
spdlog::level::level_enum lvl,
|
||||
spdlog::string_view_t msg)
|
||||
: logger_name(a_logger_name),
|
||||
level(lvl),
|
||||
time(log_time)
|
||||
#ifndef SPDLOG_NO_THREAD_ID
|
||||
,
|
||||
thread_id(os::thread_id())
|
||||
#endif
|
||||
,
|
||||
source(loc),
|
||||
payload(msg) {
|
||||
}
|
||||
|
||||
SPDLOG_INLINE log_msg::log_msg(spdlog::source_loc loc,
|
||||
string_view_t a_logger_name,
|
||||
spdlog::level::level_enum lvl,
|
||||
spdlog::string_view_t msg)
|
||||
: log_msg(os::now(), loc, a_logger_name, lvl, msg) {}
|
||||
|
||||
SPDLOG_INLINE log_msg::log_msg(string_view_t a_logger_name,
|
||||
spdlog::level::level_enum lvl,
|
||||
spdlog::string_view_t msg)
|
||||
: log_msg(os::now(), source_loc{}, a_logger_name, lvl, msg) {}
|
||||
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
@@ -0,0 +1,40 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <spdlog/common.h>
|
||||
#include <string>
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
struct SPDLOG_API log_msg {
|
||||
log_msg() = default;
|
||||
log_msg(log_clock::time_point log_time,
|
||||
source_loc loc,
|
||||
string_view_t logger_name,
|
||||
level::level_enum lvl,
|
||||
string_view_t msg);
|
||||
log_msg(source_loc loc, string_view_t logger_name, level::level_enum lvl, string_view_t msg);
|
||||
log_msg(string_view_t logger_name, level::level_enum lvl, string_view_t msg);
|
||||
log_msg(const log_msg &other) = default;
|
||||
log_msg &operator=(const log_msg &other) = default;
|
||||
|
||||
string_view_t logger_name;
|
||||
level::level_enum level{level::off};
|
||||
log_clock::time_point time;
|
||||
size_t thread_id{0};
|
||||
|
||||
// wrapping the formatted text with color (updated by pattern_formatter).
|
||||
mutable size_t color_range_start{0};
|
||||
mutable size_t color_range_end{0};
|
||||
|
||||
source_loc source;
|
||||
string_view_t payload;
|
||||
};
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
|
||||
#ifdef SPDLOG_HEADER_ONLY
|
||||
#include "log_msg-inl.h"
|
||||
#endif
|
||||
@@ -0,0 +1,54 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef SPDLOG_HEADER_ONLY
|
||||
#include <spdlog/details/log_msg_buffer.h>
|
||||
#endif
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
|
||||
SPDLOG_INLINE log_msg_buffer::log_msg_buffer(const log_msg &orig_msg)
|
||||
: log_msg{orig_msg} {
|
||||
buffer.append(logger_name.begin(), logger_name.end());
|
||||
buffer.append(payload.begin(), payload.end());
|
||||
update_string_views();
|
||||
}
|
||||
|
||||
SPDLOG_INLINE log_msg_buffer::log_msg_buffer(const log_msg_buffer &other)
|
||||
: log_msg{other} {
|
||||
buffer.append(logger_name.begin(), logger_name.end());
|
||||
buffer.append(payload.begin(), payload.end());
|
||||
update_string_views();
|
||||
}
|
||||
|
||||
SPDLOG_INLINE log_msg_buffer::log_msg_buffer(log_msg_buffer &&other) SPDLOG_NOEXCEPT
|
||||
: log_msg{other},
|
||||
buffer{std::move(other.buffer)} {
|
||||
update_string_views();
|
||||
}
|
||||
|
||||
SPDLOG_INLINE log_msg_buffer &log_msg_buffer::operator=(const log_msg_buffer &other) {
|
||||
log_msg::operator=(other);
|
||||
buffer.clear();
|
||||
buffer.append(other.buffer.data(), other.buffer.data() + other.buffer.size());
|
||||
update_string_views();
|
||||
return *this;
|
||||
}
|
||||
|
||||
SPDLOG_INLINE log_msg_buffer &log_msg_buffer::operator=(log_msg_buffer &&other) SPDLOG_NOEXCEPT {
|
||||
log_msg::operator=(other);
|
||||
buffer = std::move(other.buffer);
|
||||
update_string_views();
|
||||
return *this;
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void log_msg_buffer::update_string_views() {
|
||||
logger_name = string_view_t{buffer.data(), logger_name.size()};
|
||||
payload = string_view_t{buffer.data() + logger_name.size(), payload.size()};
|
||||
}
|
||||
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
@@ -0,0 +1,32 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <spdlog/details/log_msg.h>
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
|
||||
// Extend log_msg with internal buffer to store its payload.
|
||||
// This is needed since log_msg holds string_views that points to stack data.
|
||||
|
||||
class SPDLOG_API log_msg_buffer : public log_msg {
|
||||
memory_buf_t buffer;
|
||||
void update_string_views();
|
||||
|
||||
public:
|
||||
log_msg_buffer() = default;
|
||||
explicit log_msg_buffer(const log_msg &orig_msg);
|
||||
log_msg_buffer(const log_msg_buffer &other);
|
||||
log_msg_buffer(log_msg_buffer &&other) SPDLOG_NOEXCEPT;
|
||||
log_msg_buffer &operator=(const log_msg_buffer &other);
|
||||
log_msg_buffer &operator=(log_msg_buffer &&other) SPDLOG_NOEXCEPT;
|
||||
};
|
||||
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
|
||||
#ifdef SPDLOG_HEADER_ONLY
|
||||
#include "log_msg_buffer-inl.h"
|
||||
#endif
|
||||
@@ -0,0 +1,177 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
// multi producer-multi consumer blocking queue.
|
||||
// enqueue(..) - will block until room found to put the new message.
|
||||
// enqueue_nowait(..) - enqueue immediately. overruns oldest message if no
|
||||
// room left.
|
||||
// dequeue_for(..) - will block until the queue is not empty or timeout have
|
||||
// passed.
|
||||
|
||||
#include <spdlog/details/circular_q.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
|
||||
template <typename T>
|
||||
class mpmc_blocking_queue {
|
||||
public:
|
||||
using item_type = T;
|
||||
explicit mpmc_blocking_queue(size_t max_items)
|
||||
: q_(max_items) {}
|
||||
|
||||
#ifndef __MINGW32__
|
||||
// try to enqueue and block if no room left
|
||||
void enqueue(T &&item) {
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(queue_mutex_);
|
||||
pop_cv_.wait(lock, [this] { return !this->q_.full(); });
|
||||
q_.push_back(std::move(item));
|
||||
}
|
||||
push_cv_.notify_one();
|
||||
}
|
||||
|
||||
// enqueue immediately. overrun oldest message in the queue if no room left.
|
||||
void enqueue_nowait(T &&item) {
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(queue_mutex_);
|
||||
q_.push_back(std::move(item));
|
||||
}
|
||||
push_cv_.notify_one();
|
||||
}
|
||||
|
||||
void enqueue_if_have_room(T &&item) {
|
||||
bool pushed = false;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(queue_mutex_);
|
||||
if (!q_.full()) {
|
||||
q_.push_back(std::move(item));
|
||||
pushed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (pushed) {
|
||||
push_cv_.notify_one();
|
||||
} else {
|
||||
++discard_counter_;
|
||||
}
|
||||
}
|
||||
|
||||
// dequeue with a timeout.
|
||||
// Return true, if succeeded dequeue item, false otherwise
|
||||
bool dequeue_for(T &popped_item, std::chrono::milliseconds wait_duration) {
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(queue_mutex_);
|
||||
if (!push_cv_.wait_for(lock, wait_duration, [this] { return !this->q_.empty(); })) {
|
||||
return false;
|
||||
}
|
||||
popped_item = std::move(q_.front());
|
||||
q_.pop_front();
|
||||
}
|
||||
pop_cv_.notify_one();
|
||||
return true;
|
||||
}
|
||||
|
||||
// blocking dequeue without a timeout.
|
||||
void dequeue(T &popped_item) {
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(queue_mutex_);
|
||||
push_cv_.wait(lock, [this] { return !this->q_.empty(); });
|
||||
popped_item = std::move(q_.front());
|
||||
q_.pop_front();
|
||||
}
|
||||
pop_cv_.notify_one();
|
||||
}
|
||||
|
||||
#else
|
||||
// apparently mingw deadlocks if the mutex is released before cv.notify_one(),
|
||||
// so release the mutex at the very end each function.
|
||||
|
||||
// try to enqueue and block if no room left
|
||||
void enqueue(T &&item) {
|
||||
std::unique_lock<std::mutex> lock(queue_mutex_);
|
||||
pop_cv_.wait(lock, [this] { return !this->q_.full(); });
|
||||
q_.push_back(std::move(item));
|
||||
push_cv_.notify_one();
|
||||
}
|
||||
|
||||
// enqueue immediately. overrun oldest message in the queue if no room left.
|
||||
void enqueue_nowait(T &&item) {
|
||||
std::unique_lock<std::mutex> lock(queue_mutex_);
|
||||
q_.push_back(std::move(item));
|
||||
push_cv_.notify_one();
|
||||
}
|
||||
|
||||
void enqueue_if_have_room(T &&item) {
|
||||
bool pushed = false;
|
||||
std::unique_lock<std::mutex> lock(queue_mutex_);
|
||||
if (!q_.full()) {
|
||||
q_.push_back(std::move(item));
|
||||
pushed = true;
|
||||
}
|
||||
|
||||
if (pushed) {
|
||||
push_cv_.notify_one();
|
||||
} else {
|
||||
++discard_counter_;
|
||||
}
|
||||
}
|
||||
|
||||
// dequeue with a timeout.
|
||||
// Return true, if succeeded dequeue item, false otherwise
|
||||
bool dequeue_for(T &popped_item, std::chrono::milliseconds wait_duration) {
|
||||
std::unique_lock<std::mutex> lock(queue_mutex_);
|
||||
if (!push_cv_.wait_for(lock, wait_duration, [this] { return !this->q_.empty(); })) {
|
||||
return false;
|
||||
}
|
||||
popped_item = std::move(q_.front());
|
||||
q_.pop_front();
|
||||
pop_cv_.notify_one();
|
||||
return true;
|
||||
}
|
||||
|
||||
// blocking dequeue without a timeout.
|
||||
void dequeue(T &popped_item) {
|
||||
std::unique_lock<std::mutex> lock(queue_mutex_);
|
||||
push_cv_.wait(lock, [this] { return !this->q_.empty(); });
|
||||
popped_item = std::move(q_.front());
|
||||
q_.pop_front();
|
||||
pop_cv_.notify_one();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
size_t overrun_counter() {
|
||||
std::lock_guard<std::mutex> lock(queue_mutex_);
|
||||
return q_.overrun_counter();
|
||||
}
|
||||
|
||||
size_t discard_counter() { return discard_counter_.load(std::memory_order_relaxed); }
|
||||
|
||||
size_t size() {
|
||||
std::lock_guard<std::mutex> lock(queue_mutex_);
|
||||
return q_.size();
|
||||
}
|
||||
|
||||
void reset_overrun_counter() {
|
||||
std::lock_guard<std::mutex> lock(queue_mutex_);
|
||||
q_.reset_overrun_counter();
|
||||
}
|
||||
|
||||
void reset_discard_counter() { discard_counter_.store(0, std::memory_order_relaxed); }
|
||||
|
||||
private:
|
||||
std::mutex queue_mutex_;
|
||||
std::condition_variable push_cv_;
|
||||
std::condition_variable pop_cv_;
|
||||
spdlog::details::circular_q<T> q_;
|
||||
std::atomic<size_t> discard_counter_{0};
|
||||
};
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
@@ -0,0 +1,35 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <utility>
|
||||
// null, no cost dummy "mutex" and dummy "atomic" int
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
struct null_mutex {
|
||||
void lock() const {}
|
||||
void unlock() const {}
|
||||
};
|
||||
|
||||
struct null_atomic_int {
|
||||
int value;
|
||||
null_atomic_int() = default;
|
||||
|
||||
explicit null_atomic_int(int new_value)
|
||||
: value(new_value) {}
|
||||
|
||||
int load(std::memory_order = std::memory_order_relaxed) const { return value; }
|
||||
|
||||
void store(int new_value, std::memory_order = std::memory_order_relaxed) { value = new_value; }
|
||||
|
||||
int exchange(int new_value, std::memory_order = std::memory_order_relaxed) {
|
||||
std::swap(new_value, value);
|
||||
return new_value; // return value before the call
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
+570
@@ -0,0 +1,570 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef SPDLOG_HEADER_ONLY
|
||||
#include <spdlog/details/os.h>
|
||||
#endif
|
||||
|
||||
#include <spdlog/common.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <string>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <thread>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <spdlog/details/windows_include.h>
|
||||
#include <io.h> // for _get_osfhandle, _isatty, _fileno
|
||||
#include <process.h> // for _get_pid
|
||||
|
||||
#ifdef __MINGW32__
|
||||
#include <share.h>
|
||||
#endif
|
||||
|
||||
#if defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)
|
||||
#include <cassert>
|
||||
#include <limits>
|
||||
#endif
|
||||
|
||||
#include <direct.h> // for _mkdir/_wmkdir
|
||||
|
||||
#else // unix
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef __linux__
|
||||
#include <sys/syscall.h> //Use gettid() syscall under linux to get thread id
|
||||
|
||||
#elif defined(_AIX)
|
||||
#include <pthread.h> // for pthread_getthrds_np
|
||||
|
||||
#elif defined(__DragonFly__) || defined(__FreeBSD__)
|
||||
#include <pthread_np.h> // for pthread_getthreadid_np
|
||||
|
||||
#elif defined(__NetBSD__)
|
||||
#include <lwp.h> // for _lwp_self
|
||||
|
||||
#elif defined(__sun)
|
||||
#include <thread.h> // for thr_self
|
||||
#endif
|
||||
|
||||
#endif // unix
|
||||
|
||||
#if defined __APPLE__
|
||||
#include <AvailabilityMacros.h>
|
||||
#endif
|
||||
|
||||
#ifndef __has_feature // Clang - feature checking macros.
|
||||
#define __has_feature(x) 0 // Compatibility with non-clang compilers.
|
||||
#endif
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
namespace os {
|
||||
|
||||
SPDLOG_INLINE spdlog::log_clock::time_point now() SPDLOG_NOEXCEPT {
|
||||
#if defined __linux__ && defined SPDLOG_CLOCK_COARSE
|
||||
timespec ts;
|
||||
::clock_gettime(CLOCK_REALTIME_COARSE, &ts);
|
||||
return std::chrono::time_point<log_clock, typename log_clock::duration>(
|
||||
std::chrono::duration_cast<typename log_clock::duration>(
|
||||
std::chrono::seconds(ts.tv_sec) + std::chrono::nanoseconds(ts.tv_nsec)));
|
||||
|
||||
#else
|
||||
return log_clock::now();
|
||||
#endif
|
||||
}
|
||||
SPDLOG_INLINE std::tm localtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT {
|
||||
#ifdef _WIN32
|
||||
std::tm tm;
|
||||
::localtime_s(&tm, &time_tt);
|
||||
#else
|
||||
std::tm tm;
|
||||
::localtime_r(&time_tt, &tm);
|
||||
#endif
|
||||
return tm;
|
||||
}
|
||||
|
||||
SPDLOG_INLINE std::tm localtime() SPDLOG_NOEXCEPT {
|
||||
std::time_t now_t = ::time(nullptr);
|
||||
return localtime(now_t);
|
||||
}
|
||||
|
||||
SPDLOG_INLINE std::tm gmtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT {
|
||||
#ifdef _WIN32
|
||||
std::tm tm;
|
||||
::gmtime_s(&tm, &time_tt);
|
||||
#else
|
||||
std::tm tm;
|
||||
::gmtime_r(&time_tt, &tm);
|
||||
#endif
|
||||
return tm;
|
||||
}
|
||||
|
||||
SPDLOG_INLINE std::tm gmtime() SPDLOG_NOEXCEPT {
|
||||
std::time_t now_t = ::time(nullptr);
|
||||
return gmtime(now_t);
|
||||
}
|
||||
|
||||
// fopen_s on non windows for writing
|
||||
SPDLOG_INLINE bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode) {
|
||||
#ifdef _WIN32
|
||||
#ifdef SPDLOG_WCHAR_FILENAMES
|
||||
*fp = ::_wfsopen((filename.c_str()), mode.c_str(), _SH_DENYNO);
|
||||
#else
|
||||
*fp = ::_fsopen((filename.c_str()), mode.c_str(), _SH_DENYNO);
|
||||
#endif
|
||||
#if defined(SPDLOG_PREVENT_CHILD_FD)
|
||||
if (*fp != nullptr) {
|
||||
auto file_handle = reinterpret_cast<HANDLE>(_get_osfhandle(::_fileno(*fp)));
|
||||
if (!::SetHandleInformation(file_handle, HANDLE_FLAG_INHERIT, 0)) {
|
||||
::fclose(*fp);
|
||||
*fp = nullptr;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#else // unix
|
||||
#if defined(SPDLOG_PREVENT_CHILD_FD)
|
||||
const int mode_flag = mode == SPDLOG_FILENAME_T("ab") ? O_APPEND : O_TRUNC;
|
||||
const int fd =
|
||||
::open((filename.c_str()), O_CREAT | O_WRONLY | O_CLOEXEC | mode_flag, mode_t(0644));
|
||||
if (fd == -1) {
|
||||
return true;
|
||||
}
|
||||
*fp = ::fdopen(fd, mode.c_str());
|
||||
if (*fp == nullptr) {
|
||||
::close(fd);
|
||||
}
|
||||
#else
|
||||
*fp = ::fopen((filename.c_str()), mode.c_str());
|
||||
#endif
|
||||
#endif
|
||||
|
||||
return *fp == nullptr;
|
||||
}
|
||||
|
||||
SPDLOG_INLINE int remove(const filename_t &filename) SPDLOG_NOEXCEPT {
|
||||
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
|
||||
return ::_wremove(filename.c_str());
|
||||
#else
|
||||
return std::remove(filename.c_str());
|
||||
#endif
|
||||
}
|
||||
|
||||
SPDLOG_INLINE int remove_if_exists(const filename_t &filename) SPDLOG_NOEXCEPT {
|
||||
return path_exists(filename) ? remove(filename) : 0;
|
||||
}
|
||||
|
||||
SPDLOG_INLINE int rename(const filename_t &filename1, const filename_t &filename2) SPDLOG_NOEXCEPT {
|
||||
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
|
||||
return ::_wrename(filename1.c_str(), filename2.c_str());
|
||||
#else
|
||||
return std::rename(filename1.c_str(), filename2.c_str());
|
||||
#endif
|
||||
}
|
||||
|
||||
// Return true if path exists (file or directory)
|
||||
SPDLOG_INLINE bool path_exists(const filename_t &filename) SPDLOG_NOEXCEPT {
|
||||
#ifdef _WIN32
|
||||
struct _stat buffer;
|
||||
#ifdef SPDLOG_WCHAR_FILENAMES
|
||||
return (::_wstat(filename.c_str(), &buffer) == 0);
|
||||
#else
|
||||
return (::_stat(filename.c_str(), &buffer) == 0);
|
||||
#endif
|
||||
#else // common linux/unix all have the stat system call
|
||||
struct stat buffer;
|
||||
return (::stat(filename.c_str(), &buffer) == 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// avoid warning about unreachable statement at the end of filesize()
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4702)
|
||||
#endif
|
||||
|
||||
// Return file size according to open FILE* object
|
||||
SPDLOG_INLINE size_t filesize(FILE *f) {
|
||||
if (f == nullptr) {
|
||||
throw_spdlog_ex("Failed getting file size. fd is null");
|
||||
}
|
||||
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||||
int fd = ::_fileno(f);
|
||||
#if defined(_WIN64) // 64 bits
|
||||
__int64 ret = ::_filelengthi64(fd);
|
||||
if (ret >= 0) {
|
||||
return static_cast<size_t>(ret);
|
||||
}
|
||||
|
||||
#else // windows 32 bits
|
||||
long ret = ::_filelength(fd);
|
||||
if (ret >= 0) {
|
||||
return static_cast<size_t>(ret);
|
||||
}
|
||||
#endif
|
||||
|
||||
#else // unix
|
||||
// OpenBSD and AIX doesn't compile with :: before the fileno(..)
|
||||
#if defined(__OpenBSD__) || defined(_AIX)
|
||||
int fd = fileno(f);
|
||||
#else
|
||||
int fd = ::fileno(f);
|
||||
#endif
|
||||
// 64 bits(but not in osx, linux/musl or cygwin, where fstat64 is deprecated)
|
||||
#if ((defined(__linux__) && defined(__GLIBC__)) || defined(__sun) || defined(_AIX)) && \
|
||||
(defined(__LP64__) || defined(_LP64))
|
||||
struct stat64 st;
|
||||
if (::fstat64(fd, &st) == 0) {
|
||||
return static_cast<size_t>(st.st_size);
|
||||
}
|
||||
#else // other unix or linux 32 bits or cygwin
|
||||
struct stat st;
|
||||
if (::fstat(fd, &st) == 0) {
|
||||
return static_cast<size_t>(st.st_size);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
throw_spdlog_ex("Failed getting file size from fd", errno);
|
||||
return 0; // will not be reached.
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#if !defined(SPDLOG_NO_TZ_OFFSET)
|
||||
#ifdef _WIN32
|
||||
// Compare the timestamp as Local (mktime) vs UTC (_mkgmtime) to get the offset.
|
||||
SPDLOG_INLINE int utc_minutes_offset(const std::tm &tm) {
|
||||
std::tm local_tm = tm; // copy since mktime might adjust it (normalize dates, set tm_isdst)
|
||||
std::time_t local_time_t = std::mktime(&local_tm);
|
||||
if (local_time_t == -1) {
|
||||
return 0; // fallback
|
||||
}
|
||||
|
||||
std::time_t utc_time_t = _mkgmtime(&local_tm);
|
||||
if (utc_time_t == -1) {
|
||||
return 0; // fallback
|
||||
}
|
||||
auto offset_seconds = utc_time_t - local_time_t;
|
||||
return static_cast<int>(offset_seconds / 60);
|
||||
}
|
||||
#else
|
||||
// On unix simply use tm_gmtoff
|
||||
SPDLOG_INLINE int utc_minutes_offset(const std::tm &tm) {
|
||||
return static_cast<int>(tm.tm_gmtoff / 60);
|
||||
}
|
||||
#endif // _WIN32
|
||||
#endif // SPDLOG_NO_TZ_OFFSET
|
||||
|
||||
// Return current thread id as size_t
|
||||
// It exists because the std::this_thread::get_id() is much slower(especially
|
||||
// under VS 2013)
|
||||
SPDLOG_INLINE size_t _thread_id() SPDLOG_NOEXCEPT {
|
||||
#ifdef _WIN32
|
||||
return static_cast<size_t>(::GetCurrentThreadId());
|
||||
#elif defined(__linux__)
|
||||
#if defined(__ANDROID__) && defined(__ANDROID_API__) && (__ANDROID_API__ < 21)
|
||||
#define SYS_gettid __NR_gettid
|
||||
#endif
|
||||
return static_cast<size_t>(::syscall(SYS_gettid));
|
||||
#elif defined(_AIX)
|
||||
struct __pthrdsinfo buf;
|
||||
int reg_size = 0;
|
||||
pthread_t pt = pthread_self();
|
||||
int retval = pthread_getthrds_np(&pt, PTHRDSINFO_QUERY_TID, &buf, sizeof(buf), NULL, ®_size);
|
||||
int tid = (!retval) ? buf.__pi_tid : 0;
|
||||
return static_cast<size_t>(tid);
|
||||
#elif defined(__DragonFly__) || defined(__FreeBSD__)
|
||||
return static_cast<size_t>(::pthread_getthreadid_np());
|
||||
#elif defined(__NetBSD__)
|
||||
return static_cast<size_t>(::_lwp_self());
|
||||
#elif defined(__OpenBSD__)
|
||||
return static_cast<size_t>(::getthrid());
|
||||
#elif defined(__sun)
|
||||
return static_cast<size_t>(::thr_self());
|
||||
#elif __APPLE__
|
||||
uint64_t tid;
|
||||
// There is no pthread_threadid_np prior to Mac OS X 10.6, and it is not supported on any PPC,
|
||||
// including 10.6.8 Rosetta. __POWERPC__ is Apple-specific define encompassing ppc and ppc64.
|
||||
#ifdef MAC_OS_X_VERSION_MAX_ALLOWED
|
||||
{
|
||||
#if (MAC_OS_X_VERSION_MAX_ALLOWED < 1060) || defined(__POWERPC__)
|
||||
tid = pthread_mach_thread_np(pthread_self());
|
||||
#elif MAC_OS_X_VERSION_MIN_REQUIRED < 1060
|
||||
if (&pthread_threadid_np) {
|
||||
pthread_threadid_np(nullptr, &tid);
|
||||
} else {
|
||||
tid = pthread_mach_thread_np(pthread_self());
|
||||
}
|
||||
#else
|
||||
pthread_threadid_np(nullptr, &tid);
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
pthread_threadid_np(nullptr, &tid);
|
||||
#endif
|
||||
return static_cast<size_t>(tid);
|
||||
#else // Default to standard C++11 (other Unix)
|
||||
return static_cast<size_t>(std::hash<std::thread::id>()(std::this_thread::get_id()));
|
||||
#endif
|
||||
}
|
||||
|
||||
// Return current thread id as size_t (from thread local storage)
|
||||
SPDLOG_INLINE size_t thread_id() SPDLOG_NOEXCEPT {
|
||||
#if defined(SPDLOG_NO_TLS)
|
||||
return _thread_id();
|
||||
#else // cache thread id in tls
|
||||
static thread_local const size_t tid = _thread_id();
|
||||
return tid;
|
||||
#endif
|
||||
}
|
||||
|
||||
// This is avoid msvc issue in sleep_for that happens if the clock changes.
|
||||
// See https://github.com/gabime/spdlog/issues/609
|
||||
SPDLOG_INLINE void sleep_for_millis(unsigned int milliseconds) SPDLOG_NOEXCEPT {
|
||||
#if defined(_WIN32)
|
||||
::Sleep(milliseconds);
|
||||
#else
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
|
||||
#endif
|
||||
}
|
||||
|
||||
// wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined)
|
||||
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
|
||||
SPDLOG_INLINE std::string filename_to_str(const filename_t &filename) {
|
||||
memory_buf_t buf;
|
||||
wstr_to_utf8buf(filename, buf);
|
||||
return SPDLOG_BUF_TO_STRING(buf);
|
||||
}
|
||||
#else
|
||||
SPDLOG_INLINE std::string filename_to_str(const filename_t &filename) { return filename; }
|
||||
#endif
|
||||
|
||||
SPDLOG_INLINE int pid() SPDLOG_NOEXCEPT {
|
||||
#ifdef _WIN32
|
||||
return conditional_static_cast<int>(::GetCurrentProcessId());
|
||||
#else
|
||||
return conditional_static_cast<int>(::getpid());
|
||||
#endif
|
||||
}
|
||||
|
||||
// Determine if the terminal supports colors
|
||||
// Based on: https://github.com/agauniyal/rang/
|
||||
SPDLOG_INLINE bool is_color_terminal() SPDLOG_NOEXCEPT {
|
||||
#ifdef _WIN32
|
||||
return true;
|
||||
#else
|
||||
|
||||
static const bool result = []() {
|
||||
const char *env_colorterm_p = std::getenv("COLORTERM");
|
||||
if (env_colorterm_p != nullptr) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static constexpr std::array<const char *, 16> terms = {
|
||||
{"ansi", "color", "console", "cygwin", "gnome", "konsole", "kterm", "linux", "msys",
|
||||
"putty", "rxvt", "screen", "vt100", "xterm", "alacritty", "vt102"}};
|
||||
|
||||
const char *env_term_p = std::getenv("TERM");
|
||||
if (env_term_p == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return std::any_of(terms.begin(), terms.end(), [&](const char *term) {
|
||||
return std::strstr(env_term_p, term) != nullptr;
|
||||
});
|
||||
}();
|
||||
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Determine if the terminal attached
|
||||
// Source: https://github.com/agauniyal/rang/
|
||||
SPDLOG_INLINE bool in_terminal(FILE *file) SPDLOG_NOEXCEPT {
|
||||
#ifdef _WIN32
|
||||
return ::_isatty(_fileno(file)) != 0;
|
||||
#else
|
||||
return ::isatty(fileno(file)) != 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if (defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) && defined(_WIN32)
|
||||
SPDLOG_INLINE void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target) {
|
||||
if (wstr.size() > static_cast<size_t>((std::numeric_limits<int>::max)()) / 4 - 1) {
|
||||
throw_spdlog_ex("UTF-16 string is too big to be converted to UTF-8");
|
||||
}
|
||||
|
||||
int wstr_size = static_cast<int>(wstr.size());
|
||||
if (wstr_size == 0) {
|
||||
target.resize(0);
|
||||
return;
|
||||
}
|
||||
|
||||
int result_size = static_cast<int>(target.capacity());
|
||||
if ((wstr_size + 1) * 4 > result_size) {
|
||||
result_size =
|
||||
::WideCharToMultiByte(CP_UTF8, 0, wstr.data(), wstr_size, NULL, 0, NULL, NULL);
|
||||
}
|
||||
|
||||
if (result_size > 0) {
|
||||
target.resize(result_size);
|
||||
result_size = ::WideCharToMultiByte(CP_UTF8, 0, wstr.data(), wstr_size, target.data(),
|
||||
result_size, NULL, NULL);
|
||||
|
||||
if (result_size > 0) {
|
||||
target.resize(result_size);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
throw_spdlog_ex(
|
||||
fmt_lib::format("WideCharToMultiByte failed. Last error: {}", ::GetLastError()));
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void utf8_to_wstrbuf(string_view_t str, wmemory_buf_t &target) {
|
||||
if (str.size() > static_cast<size_t>((std::numeric_limits<int>::max)()) - 1) {
|
||||
throw_spdlog_ex("UTF-8 string is too big to be converted to UTF-16");
|
||||
}
|
||||
|
||||
int str_size = static_cast<int>(str.size());
|
||||
if (str_size == 0) {
|
||||
target.resize(0);
|
||||
return;
|
||||
}
|
||||
|
||||
// find the size to allocate for the result buffer
|
||||
int result_size = ::MultiByteToWideChar(CP_UTF8, 0, str.data(), str_size, NULL, 0);
|
||||
|
||||
if (result_size > 0) {
|
||||
target.resize(result_size);
|
||||
result_size =
|
||||
::MultiByteToWideChar(CP_UTF8, 0, str.data(), str_size, target.data(), result_size);
|
||||
if (result_size > 0) {
|
||||
assert(result_size == static_cast<int>(target.size()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
throw_spdlog_ex(
|
||||
fmt_lib::format("MultiByteToWideChar failed. Last error: {}", ::GetLastError()));
|
||||
}
|
||||
#endif // (defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) &&
|
||||
// defined(_WIN32)
|
||||
|
||||
// return true on success
|
||||
static SPDLOG_INLINE bool mkdir_(const filename_t &path) {
|
||||
#ifdef _WIN32
|
||||
#ifdef SPDLOG_WCHAR_FILENAMES
|
||||
return ::_wmkdir(path.c_str()) == 0;
|
||||
#else
|
||||
return ::_mkdir(path.c_str()) == 0;
|
||||
#endif
|
||||
#else
|
||||
return ::mkdir(path.c_str(), mode_t(0755)) == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
// create the given directory - and all directories leading to it
|
||||
// return true on success or if the directory already exists
|
||||
SPDLOG_INLINE bool create_dir(const filename_t &path) {
|
||||
if (path_exists(path)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (path.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t search_offset = 0;
|
||||
do {
|
||||
auto token_pos = path.find_first_of(folder_seps_filename, search_offset);
|
||||
// treat the entire path as a folder if no folder separator not found
|
||||
if (token_pos == filename_t::npos) {
|
||||
token_pos = path.size();
|
||||
}
|
||||
|
||||
auto subdir = path.substr(0, token_pos);
|
||||
#ifdef _WIN32
|
||||
// if subdir is just a drive letter, add a slash e.g. "c:"=>"c:\",
|
||||
// otherwise path_exists(subdir) returns false (issue #3079)
|
||||
const bool is_drive = subdir.length() == 2 && subdir[1] == ':';
|
||||
if (is_drive) {
|
||||
subdir += '\\';
|
||||
token_pos++;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!subdir.empty() && !path_exists(subdir) && !mkdir_(subdir)) {
|
||||
return false; // return error if failed creating dir
|
||||
}
|
||||
search_offset = token_pos + 1;
|
||||
} while (search_offset < path.size());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Return directory name from given path or empty string
|
||||
// "abc/file" => "abc"
|
||||
// "abc/" => "abc"
|
||||
// "abc" => ""
|
||||
// "abc///" => "abc//"
|
||||
SPDLOG_INLINE filename_t dir_name(const filename_t &path) {
|
||||
auto pos = path.find_last_of(folder_seps_filename);
|
||||
return pos != filename_t::npos ? path.substr(0, pos) : filename_t{};
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4996)
|
||||
#endif // _MSC_VER
|
||||
std::string SPDLOG_INLINE getenv(const char *field) {
|
||||
#if defined(_MSC_VER) && defined(WINAPI_FAMILY) && defined(WINAPI_FAMILY_DESKTOP_APP) && \
|
||||
(WINAPI_FAMILY != WINAPI_FAMILY_DESKTOP_APP)
|
||||
return std::string{}; // not supported under uwp
|
||||
#else
|
||||
char *buf = std::getenv(field);
|
||||
return buf ? buf : std::string{};
|
||||
#endif
|
||||
}
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif // _MSC_VER
|
||||
|
||||
// Do fsync by FILE handlerpointer
|
||||
// Return true on success
|
||||
SPDLOG_INLINE bool fsync(FILE *fp) {
|
||||
#ifdef _WIN32
|
||||
return FlushFileBuffers(reinterpret_cast<HANDLE>(_get_osfhandle(_fileno(fp)))) != 0;
|
||||
#else
|
||||
return ::fsync(fileno(fp)) == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Do non-locking fwrite if possible by the os or use the regular locking fwrite
|
||||
// Return true on success.
|
||||
SPDLOG_INLINE bool fwrite_bytes(const void *ptr, const size_t n_bytes, FILE *fp) {
|
||||
#if defined(_WIN32) && defined(SPDLOG_FWRITE_UNLOCKED)
|
||||
return _fwrite_nolock(ptr, 1, n_bytes, fp) == n_bytes;
|
||||
#elif defined(SPDLOG_FWRITE_UNLOCKED)
|
||||
return ::fwrite_unlocked(ptr, 1, n_bytes, fp) == n_bytes;
|
||||
#else
|
||||
return std::fwrite(ptr, 1, n_bytes, fp) == n_bytes;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace os
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ctime> // std::time_t
|
||||
#include <spdlog/common.h>
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
namespace os {
|
||||
|
||||
SPDLOG_API spdlog::log_clock::time_point now() SPDLOG_NOEXCEPT;
|
||||
|
||||
SPDLOG_API std::tm localtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT;
|
||||
|
||||
SPDLOG_API std::tm localtime() SPDLOG_NOEXCEPT;
|
||||
|
||||
SPDLOG_API std::tm gmtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT;
|
||||
|
||||
SPDLOG_API std::tm gmtime() SPDLOG_NOEXCEPT;
|
||||
|
||||
// eol definition
|
||||
#if !defined(SPDLOG_EOL)
|
||||
#ifdef _WIN32
|
||||
#define SPDLOG_EOL "\r\n"
|
||||
#else
|
||||
#define SPDLOG_EOL "\n"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
SPDLOG_CONSTEXPR static const char *default_eol = SPDLOG_EOL;
|
||||
|
||||
// folder separator
|
||||
#if !defined(SPDLOG_FOLDER_SEPS)
|
||||
#ifdef _WIN32
|
||||
#define SPDLOG_FOLDER_SEPS "\\/"
|
||||
#else
|
||||
#define SPDLOG_FOLDER_SEPS "/"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
SPDLOG_CONSTEXPR static const char folder_seps[] = SPDLOG_FOLDER_SEPS;
|
||||
SPDLOG_CONSTEXPR static const filename_t::value_type folder_seps_filename[] =
|
||||
SPDLOG_FILENAME_T(SPDLOG_FOLDER_SEPS);
|
||||
|
||||
// fopen_s on non windows for writing
|
||||
SPDLOG_API bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode);
|
||||
|
||||
// Remove filename. return 0 on success
|
||||
SPDLOG_API int remove(const filename_t &filename) SPDLOG_NOEXCEPT;
|
||||
|
||||
// Remove file if exists. return 0 on success
|
||||
// Note: Non atomic (might return failure to delete if concurrently deleted by other process/thread)
|
||||
SPDLOG_API int remove_if_exists(const filename_t &filename) SPDLOG_NOEXCEPT;
|
||||
|
||||
SPDLOG_API int rename(const filename_t &filename1, const filename_t &filename2) SPDLOG_NOEXCEPT;
|
||||
|
||||
// Return if file exists.
|
||||
SPDLOG_API bool path_exists(const filename_t &filename) SPDLOG_NOEXCEPT;
|
||||
|
||||
// Return file size according to open FILE* object
|
||||
SPDLOG_API size_t filesize(FILE *f);
|
||||
|
||||
// Return utc offset in minutes or throw spdlog_ex on failure
|
||||
SPDLOG_API int utc_minutes_offset(const std::tm &tm = details::os::localtime());
|
||||
|
||||
// Return current thread id as size_t
|
||||
// It exists because the std::this_thread::get_id() is much slower(especially
|
||||
// under VS 2013)
|
||||
SPDLOG_API size_t _thread_id() SPDLOG_NOEXCEPT;
|
||||
|
||||
// Return current thread id as size_t (from thread local storage)
|
||||
SPDLOG_API size_t thread_id() SPDLOG_NOEXCEPT;
|
||||
|
||||
// This is avoid msvc issue in sleep_for that happens if the clock changes.
|
||||
// See https://github.com/gabime/spdlog/issues/609
|
||||
SPDLOG_API void sleep_for_millis(unsigned int milliseconds) SPDLOG_NOEXCEPT;
|
||||
|
||||
SPDLOG_API std::string filename_to_str(const filename_t &filename);
|
||||
|
||||
SPDLOG_API int pid() SPDLOG_NOEXCEPT;
|
||||
|
||||
// Determine if the terminal supports colors
|
||||
// Source: https://github.com/agauniyal/rang/
|
||||
SPDLOG_API bool is_color_terminal() SPDLOG_NOEXCEPT;
|
||||
|
||||
// Determine if the terminal attached
|
||||
// Source: https://github.com/agauniyal/rang/
|
||||
SPDLOG_API bool in_terminal(FILE *file) SPDLOG_NOEXCEPT;
|
||||
|
||||
#if (defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) && defined(_WIN32)
|
||||
SPDLOG_API void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target);
|
||||
|
||||
SPDLOG_API void utf8_to_wstrbuf(string_view_t str, wmemory_buf_t &target);
|
||||
#endif
|
||||
|
||||
// Return directory name from given path or empty string
|
||||
// "abc/file" => "abc"
|
||||
// "abc/" => "abc"
|
||||
// "abc" => ""
|
||||
// "abc///" => "abc//"
|
||||
SPDLOG_API filename_t dir_name(const filename_t &path);
|
||||
|
||||
// Create a dir from the given path.
|
||||
// Return true if succeeded or if this dir already exists.
|
||||
SPDLOG_API bool create_dir(const filename_t &path);
|
||||
|
||||
// non thread safe, cross platform getenv/getenv_s
|
||||
// return empty string if field not found
|
||||
SPDLOG_API std::string getenv(const char *field);
|
||||
|
||||
// Do fsync by FILE objectpointer.
|
||||
// Return true on success.
|
||||
SPDLOG_API bool fsync(FILE *fp);
|
||||
|
||||
// Do non-locking fwrite if possible by the os or use the regular locking fwrite
|
||||
// Return true on success.
|
||||
SPDLOG_API bool fwrite_bytes(const void *ptr, const size_t n_bytes, FILE *fp);
|
||||
|
||||
} // namespace os
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
|
||||
#ifdef SPDLOG_HEADER_ONLY
|
||||
#include "os-inl.h"
|
||||
#endif
|
||||
@@ -0,0 +1,26 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef SPDLOG_HEADER_ONLY
|
||||
#include <spdlog/details/periodic_worker.h>
|
||||
#endif
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
|
||||
// stop the worker thread and join it
|
||||
SPDLOG_INLINE periodic_worker::~periodic_worker() {
|
||||
if (worker_thread_.joinable()) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
active_ = false;
|
||||
}
|
||||
cv_.notify_one();
|
||||
worker_thread_.join();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
@@ -0,0 +1,58 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
// periodic worker thread - periodically executes the given callback function.
|
||||
//
|
||||
// RAII over the owned thread:
|
||||
// creates the thread on construction.
|
||||
// stops and joins the thread on destruction (if the thread is executing a callback, wait for it
|
||||
// to finish first).
|
||||
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
|
||||
class SPDLOG_API periodic_worker {
|
||||
public:
|
||||
template <typename Rep, typename Period>
|
||||
periodic_worker(const std::function<void()> &callback_fun,
|
||||
std::chrono::duration<Rep, Period> interval) {
|
||||
active_ = (interval > std::chrono::duration<Rep, Period>::zero());
|
||||
if (!active_) {
|
||||
return;
|
||||
}
|
||||
|
||||
worker_thread_ = std::thread([this, callback_fun, interval]() {
|
||||
for (;;) {
|
||||
std::unique_lock<std::mutex> lock(this->mutex_);
|
||||
if (this->cv_.wait_for(lock, interval, [this] { return !this->active_; })) {
|
||||
return; // active_ == false, so exit this thread
|
||||
}
|
||||
callback_fun();
|
||||
}
|
||||
});
|
||||
}
|
||||
std::thread &get_thread() { return worker_thread_; }
|
||||
periodic_worker(const periodic_worker &) = delete;
|
||||
periodic_worker &operator=(const periodic_worker &) = delete;
|
||||
// stop the worker thread and join it
|
||||
~periodic_worker();
|
||||
|
||||
private:
|
||||
bool active_;
|
||||
std::thread worker_thread_;
|
||||
std::mutex mutex_;
|
||||
std::condition_variable cv_;
|
||||
};
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
|
||||
#ifdef SPDLOG_HEADER_ONLY
|
||||
#include "periodic_worker-inl.h"
|
||||
#endif
|
||||
@@ -0,0 +1,270 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef SPDLOG_HEADER_ONLY
|
||||
#include <spdlog/details/registry.h>
|
||||
#endif
|
||||
|
||||
#include <spdlog/common.h>
|
||||
#include <spdlog/details/periodic_worker.h>
|
||||
#include <spdlog/logger.h>
|
||||
#include <spdlog/pattern_formatter.h>
|
||||
|
||||
#ifndef SPDLOG_DISABLE_DEFAULT_LOGGER
|
||||
// support for the default stdout color logger
|
||||
#ifdef _WIN32
|
||||
#include <spdlog/sinks/wincolor_sink.h>
|
||||
#else
|
||||
#include <spdlog/sinks/ansicolor_sink.h>
|
||||
#endif
|
||||
#endif // SPDLOG_DISABLE_DEFAULT_LOGGER
|
||||
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
|
||||
SPDLOG_INLINE registry::registry()
|
||||
: formatter_(new pattern_formatter()) {
|
||||
#ifndef SPDLOG_DISABLE_DEFAULT_LOGGER
|
||||
// create default logger (ansicolor_stdout_sink_mt or wincolor_stdout_sink_mt in windows).
|
||||
#ifdef _WIN32
|
||||
auto color_sink = std::make_shared<sinks::wincolor_stdout_sink_mt>();
|
||||
#else
|
||||
auto color_sink = std::make_shared<sinks::ansicolor_stdout_sink_mt>();
|
||||
#endif
|
||||
|
||||
const char *default_logger_name = "";
|
||||
default_logger_ = std::make_shared<spdlog::logger>(default_logger_name, std::move(color_sink));
|
||||
loggers_[default_logger_name] = default_logger_;
|
||||
|
||||
#endif // SPDLOG_DISABLE_DEFAULT_LOGGER
|
||||
}
|
||||
|
||||
SPDLOG_INLINE registry::~registry() = default;
|
||||
|
||||
SPDLOG_INLINE void registry::register_logger(std::shared_ptr<logger> new_logger) {
|
||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||
register_logger_(std::move(new_logger));
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void registry::register_or_replace(std::shared_ptr<logger> new_logger) {
|
||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||
register_or_replace_(std::move(new_logger));
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void registry::initialize_logger(std::shared_ptr<logger> new_logger) {
|
||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||
new_logger->set_formatter(formatter_->clone());
|
||||
|
||||
if (err_handler_) {
|
||||
new_logger->set_error_handler(err_handler_);
|
||||
}
|
||||
|
||||
// set new level according to previously configured level or default level
|
||||
auto it = log_levels_.find(new_logger->name());
|
||||
auto new_level = it != log_levels_.end() ? it->second : global_log_level_;
|
||||
new_logger->set_level(new_level);
|
||||
|
||||
new_logger->flush_on(flush_level_);
|
||||
|
||||
if (backtrace_n_messages_ > 0) {
|
||||
new_logger->enable_backtrace(backtrace_n_messages_);
|
||||
}
|
||||
|
||||
if (automatic_registration_) {
|
||||
register_logger_(std::move(new_logger));
|
||||
}
|
||||
}
|
||||
|
||||
SPDLOG_INLINE std::shared_ptr<logger> registry::get(const std::string &logger_name) {
|
||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||
auto found = loggers_.find(logger_name);
|
||||
return found == loggers_.end() ? nullptr : found->second;
|
||||
}
|
||||
|
||||
SPDLOG_INLINE std::shared_ptr<logger> registry::default_logger() {
|
||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||
return default_logger_;
|
||||
}
|
||||
|
||||
// Return raw ptr to the default logger.
|
||||
// To be used directly by the spdlog default api (e.g. spdlog::info)
|
||||
// This make the default API faster, but cannot be used concurrently with set_default_logger().
|
||||
// e.g do not call set_default_logger() from one thread while calling spdlog::info() from another.
|
||||
SPDLOG_INLINE logger *registry::get_default_raw() { return default_logger_.get(); }
|
||||
|
||||
// set default logger.
|
||||
// the default logger is stored in default_logger_ (for faster retrieval) and in the loggers_ map.
|
||||
SPDLOG_INLINE void registry::set_default_logger(std::shared_ptr<logger> new_default_logger) {
|
||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||
if (new_default_logger != nullptr) {
|
||||
loggers_[new_default_logger->name()] = new_default_logger;
|
||||
}
|
||||
default_logger_ = std::move(new_default_logger);
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void registry::set_tp(std::shared_ptr<thread_pool> tp) {
|
||||
std::lock_guard<std::recursive_mutex> lock(tp_mutex_);
|
||||
tp_ = std::move(tp);
|
||||
}
|
||||
|
||||
SPDLOG_INLINE std::shared_ptr<thread_pool> registry::get_tp() {
|
||||
std::lock_guard<std::recursive_mutex> lock(tp_mutex_);
|
||||
return tp_;
|
||||
}
|
||||
|
||||
// Set global formatter. Each sink in each logger will get a clone of this object
|
||||
SPDLOG_INLINE void registry::set_formatter(std::unique_ptr<formatter> formatter) {
|
||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||
formatter_ = std::move(formatter);
|
||||
for (auto &l : loggers_) {
|
||||
l.second->set_formatter(formatter_->clone());
|
||||
}
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void registry::enable_backtrace(size_t n_messages) {
|
||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||
backtrace_n_messages_ = n_messages;
|
||||
|
||||
for (auto &l : loggers_) {
|
||||
l.second->enable_backtrace(n_messages);
|
||||
}
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void registry::disable_backtrace() {
|
||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||
backtrace_n_messages_ = 0;
|
||||
for (auto &l : loggers_) {
|
||||
l.second->disable_backtrace();
|
||||
}
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void registry::set_level(level::level_enum log_level) {
|
||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||
for (auto &l : loggers_) {
|
||||
l.second->set_level(log_level);
|
||||
}
|
||||
global_log_level_ = log_level;
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void registry::flush_on(level::level_enum log_level) {
|
||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||
for (auto &l : loggers_) {
|
||||
l.second->flush_on(log_level);
|
||||
}
|
||||
flush_level_ = log_level;
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void registry::set_error_handler(err_handler handler) {
|
||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||
for (auto &l : loggers_) {
|
||||
l.second->set_error_handler(handler);
|
||||
}
|
||||
err_handler_ = std::move(handler);
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void registry::apply_all(
|
||||
const std::function<void(const std::shared_ptr<logger>)> &fun) {
|
||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||
for (auto &l : loggers_) {
|
||||
fun(l.second);
|
||||
}
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void registry::flush_all() {
|
||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||
for (auto &l : loggers_) {
|
||||
l.second->flush();
|
||||
}
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void registry::drop(const std::string &logger_name) {
|
||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||
auto is_default_logger = default_logger_ && default_logger_->name() == logger_name;
|
||||
loggers_.erase(logger_name);
|
||||
if (is_default_logger) {
|
||||
default_logger_.reset();
|
||||
}
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void registry::drop_all() {
|
||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||
loggers_.clear();
|
||||
default_logger_.reset();
|
||||
}
|
||||
|
||||
// clean all resources and threads started by the registry
|
||||
SPDLOG_INLINE void registry::shutdown() {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(flusher_mutex_);
|
||||
periodic_flusher_.reset();
|
||||
}
|
||||
|
||||
drop_all();
|
||||
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(tp_mutex_);
|
||||
tp_.reset();
|
||||
}
|
||||
}
|
||||
|
||||
SPDLOG_INLINE std::recursive_mutex ®istry::tp_mutex() { return tp_mutex_; }
|
||||
|
||||
SPDLOG_INLINE void registry::set_automatic_registration(bool automatic_registration) {
|
||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||
automatic_registration_ = automatic_registration;
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void registry::set_levels(log_levels levels, level::level_enum *global_level) {
|
||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||
log_levels_ = std::move(levels);
|
||||
auto global_level_requested = global_level != nullptr;
|
||||
global_log_level_ = global_level_requested ? *global_level : global_log_level_;
|
||||
|
||||
for (auto &logger : loggers_) {
|
||||
auto logger_entry = log_levels_.find(logger.first);
|
||||
if (logger_entry != log_levels_.end()) {
|
||||
logger.second->set_level(logger_entry->second);
|
||||
} else if (global_level_requested) {
|
||||
logger.second->set_level(*global_level);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SPDLOG_INLINE registry ®istry::instance() {
|
||||
static registry s_instance;
|
||||
return s_instance;
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void registry::apply_logger_env_levels(std::shared_ptr<logger> new_logger) {
|
||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||
auto it = log_levels_.find(new_logger->name());
|
||||
auto new_level = it != log_levels_.end() ? it->second : global_log_level_;
|
||||
new_logger->set_level(new_level);
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void registry::throw_if_exists_(const std::string &logger_name) {
|
||||
if (loggers_.find(logger_name) != loggers_.end()) {
|
||||
throw_spdlog_ex("logger with name '" + logger_name + "' already exists");
|
||||
}
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void registry::register_logger_(std::shared_ptr<logger> new_logger) {
|
||||
auto &logger_name = new_logger->name();
|
||||
throw_if_exists_(logger_name);
|
||||
loggers_[logger_name] = std::move(new_logger);
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void registry::register_or_replace_(std::shared_ptr<logger> new_logger) {
|
||||
loggers_[new_logger->name()] = std::move(new_logger);
|
||||
}
|
||||
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
@@ -0,0 +1,131 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
// Loggers registry of unique name->logger pointer
|
||||
// An attempt to create a logger with an already existing name will result with spdlog_ex exception.
|
||||
// If user requests a non existing logger, nullptr will be returned
|
||||
// This class is thread safe
|
||||
|
||||
#include <spdlog/common.h>
|
||||
#include <spdlog/details/periodic_worker.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace spdlog {
|
||||
class logger;
|
||||
|
||||
namespace details {
|
||||
class thread_pool;
|
||||
|
||||
class SPDLOG_API registry {
|
||||
public:
|
||||
using log_levels = std::unordered_map<std::string, level::level_enum>;
|
||||
registry(const registry &) = delete;
|
||||
registry &operator=(const registry &) = delete;
|
||||
|
||||
void register_logger(std::shared_ptr<logger> new_logger);
|
||||
void register_or_replace(std::shared_ptr<logger> new_logger);
|
||||
void initialize_logger(std::shared_ptr<logger> new_logger);
|
||||
std::shared_ptr<logger> get(const std::string &logger_name);
|
||||
std::shared_ptr<logger> default_logger();
|
||||
|
||||
// Return raw ptr to the default logger.
|
||||
// To be used directly by the spdlog default api (e.g. spdlog::info)
|
||||
// This make the default API faster, but cannot be used concurrently with set_default_logger().
|
||||
// e.g do not call set_default_logger() from one thread while calling spdlog::info() from
|
||||
// another.
|
||||
logger *get_default_raw();
|
||||
|
||||
// set default logger and add it to the registry if not registered already.
|
||||
// default logger is stored in default_logger_ (for faster retrieval) and in the loggers_ map.
|
||||
// Note: Make sure to unregister it when no longer needed or before calling again with a new
|
||||
// logger.
|
||||
void set_default_logger(std::shared_ptr<logger> new_default_logger);
|
||||
|
||||
void set_tp(std::shared_ptr<thread_pool> tp);
|
||||
|
||||
std::shared_ptr<thread_pool> get_tp();
|
||||
|
||||
// Set global formatter. Each sink in each logger will get a clone of this object
|
||||
void set_formatter(std::unique_ptr<formatter> formatter);
|
||||
|
||||
void enable_backtrace(size_t n_messages);
|
||||
|
||||
void disable_backtrace();
|
||||
|
||||
void set_level(level::level_enum log_level);
|
||||
|
||||
void flush_on(level::level_enum log_level);
|
||||
|
||||
template <typename Rep, typename Period>
|
||||
void flush_every(std::chrono::duration<Rep, Period> interval) {
|
||||
std::lock_guard<std::mutex> lock(flusher_mutex_);
|
||||
auto clbk = [this]() { this->flush_all(); };
|
||||
periodic_flusher_ = details::make_unique<periodic_worker>(clbk, interval);
|
||||
}
|
||||
|
||||
std::unique_ptr<periodic_worker> &get_flusher() {
|
||||
std::lock_guard<std::mutex> lock(flusher_mutex_);
|
||||
return periodic_flusher_;
|
||||
}
|
||||
|
||||
void set_error_handler(err_handler handler);
|
||||
|
||||
void apply_all(const std::function<void(const std::shared_ptr<logger>)> &fun);
|
||||
|
||||
void flush_all();
|
||||
|
||||
void drop(const std::string &logger_name);
|
||||
|
||||
void drop_all();
|
||||
|
||||
// clean all resources and threads started by the registry
|
||||
void shutdown();
|
||||
|
||||
std::recursive_mutex &tp_mutex();
|
||||
|
||||
void set_automatic_registration(bool automatic_registration);
|
||||
|
||||
// set levels for all existing/future loggers. global_level can be null if should not set.
|
||||
void set_levels(log_levels levels, level::level_enum *global_level);
|
||||
|
||||
static registry &instance();
|
||||
|
||||
void apply_logger_env_levels(std::shared_ptr<logger> new_logger);
|
||||
|
||||
private:
|
||||
registry();
|
||||
~registry();
|
||||
|
||||
void throw_if_exists_(const std::string &logger_name);
|
||||
void register_logger_(std::shared_ptr<logger> new_logger);
|
||||
void register_or_replace_(std::shared_ptr<logger> new_logger);
|
||||
bool set_level_from_cfg_(logger *logger);
|
||||
std::mutex logger_map_mutex_, flusher_mutex_;
|
||||
std::recursive_mutex tp_mutex_;
|
||||
std::unordered_map<std::string, std::shared_ptr<logger>> loggers_;
|
||||
log_levels log_levels_;
|
||||
std::unique_ptr<formatter> formatter_;
|
||||
spdlog::level::level_enum global_log_level_ = level::info;
|
||||
level::level_enum flush_level_ = level::off;
|
||||
err_handler err_handler_;
|
||||
std::shared_ptr<thread_pool> tp_;
|
||||
std::unique_ptr<periodic_worker> periodic_flusher_;
|
||||
std::shared_ptr<logger> default_logger_;
|
||||
bool automatic_registration_ = true;
|
||||
size_t backtrace_n_messages_ = 0;
|
||||
};
|
||||
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
|
||||
#ifdef SPDLOG_HEADER_ONLY
|
||||
#include "registry-inl.h"
|
||||
#endif
|
||||
@@ -0,0 +1,22 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "registry.h"
|
||||
|
||||
namespace spdlog {
|
||||
|
||||
// Default logger factory- creates synchronous loggers
|
||||
class logger;
|
||||
|
||||
struct synchronous_factory {
|
||||
template <typename Sink, typename... SinkArgs>
|
||||
static std::shared_ptr<spdlog::logger> create(std::string logger_name, SinkArgs &&...args) {
|
||||
auto sink = std::make_shared<Sink>(std::forward<SinkArgs>(args)...);
|
||||
auto new_logger = std::make_shared<spdlog::logger>(std::move(logger_name), std::move(sink));
|
||||
details::registry::instance().initialize_logger(new_logger);
|
||||
return new_logger;
|
||||
}
|
||||
};
|
||||
} // namespace spdlog
|
||||
@@ -0,0 +1,217 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
// tcp client helper
|
||||
#include <spdlog/common.h>
|
||||
#include <spdlog/details/os.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
#include <windows.h>
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
|
||||
#pragma comment(lib, "Ws2_32.lib")
|
||||
#pragma comment(lib, "Mswsock.lib")
|
||||
#pragma comment(lib, "AdvApi32.lib")
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
class tcp_client {
|
||||
SOCKET socket_ = INVALID_SOCKET;
|
||||
|
||||
static void init_winsock_() {
|
||||
WSADATA wsaData;
|
||||
auto rv = WSAStartup(MAKEWORD(2, 2), &wsaData);
|
||||
if (rv != 0) {
|
||||
throw_winsock_error_("WSAStartup failed", ::WSAGetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
static void throw_winsock_error_(const std::string &msg, int last_error) {
|
||||
char buf[512];
|
||||
::FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL,
|
||||
last_error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf,
|
||||
(sizeof(buf) / sizeof(char)), NULL);
|
||||
|
||||
throw_spdlog_ex(fmt_lib::format("tcp_sink - {}: {}", msg, buf));
|
||||
}
|
||||
|
||||
public:
|
||||
tcp_client() { init_winsock_(); }
|
||||
|
||||
~tcp_client() {
|
||||
close();
|
||||
::WSACleanup();
|
||||
}
|
||||
|
||||
bool is_connected() const { return socket_ != INVALID_SOCKET; }
|
||||
|
||||
void close() {
|
||||
::closesocket(socket_);
|
||||
socket_ = INVALID_SOCKET;
|
||||
}
|
||||
|
||||
SOCKET fd() const { return socket_; }
|
||||
|
||||
int connect_socket_with_timeout(SOCKET sockfd,
|
||||
const struct sockaddr *addr,
|
||||
int addrlen,
|
||||
const timeval &tv) {
|
||||
// If no timeout requested, do a normal blocking connect.
|
||||
if (tv.tv_sec == 0 && tv.tv_usec == 0) {
|
||||
int rv = ::connect(sockfd, addr, addrlen);
|
||||
if (rv == SOCKET_ERROR && WSAGetLastError() == WSAEISCONN) {
|
||||
return 0;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Switch to non‐blocking mode
|
||||
u_long mode = 1UL;
|
||||
if (::ioctlsocket(sockfd, FIONBIO, &mode) == SOCKET_ERROR) {
|
||||
return SOCKET_ERROR;
|
||||
}
|
||||
|
||||
int rv = ::connect(sockfd, addr, addrlen);
|
||||
int last_error = WSAGetLastError();
|
||||
if (rv == 0 || last_error == WSAEISCONN) {
|
||||
mode = 0UL;
|
||||
if (::ioctlsocket(sockfd, FIONBIO, &mode) == SOCKET_ERROR) {
|
||||
return SOCKET_ERROR;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (last_error != WSAEWOULDBLOCK) {
|
||||
// Real error
|
||||
mode = 0UL;
|
||||
if (::ioctlsocket(sockfd, FIONBIO, &mode)) {
|
||||
return SOCKET_ERROR;
|
||||
}
|
||||
return SOCKET_ERROR;
|
||||
}
|
||||
|
||||
// Wait until socket is writable or timeout expires
|
||||
fd_set wfds;
|
||||
FD_ZERO(&wfds);
|
||||
FD_SET(sockfd, &wfds);
|
||||
|
||||
rv = ::select(0, nullptr, &wfds, nullptr, const_cast<timeval *>(&tv));
|
||||
|
||||
// Restore blocking mode regardless of select result
|
||||
mode = 0UL;
|
||||
if (::ioctlsocket(sockfd, FIONBIO, &mode) == SOCKET_ERROR) {
|
||||
return SOCKET_ERROR;
|
||||
}
|
||||
|
||||
if (rv == 0) {
|
||||
WSASetLastError(WSAETIMEDOUT);
|
||||
return SOCKET_ERROR;
|
||||
}
|
||||
if (rv == SOCKET_ERROR) {
|
||||
return SOCKET_ERROR;
|
||||
}
|
||||
|
||||
int so_error = 0;
|
||||
int len = sizeof(so_error);
|
||||
if (::getsockopt(sockfd, SOL_SOCKET, SO_ERROR, reinterpret_cast<char *>(&so_error), &len) ==
|
||||
SOCKET_ERROR) {
|
||||
return SOCKET_ERROR;
|
||||
}
|
||||
if (so_error != 0 && so_error != WSAEISCONN) {
|
||||
// connection failed
|
||||
WSASetLastError(so_error);
|
||||
return SOCKET_ERROR;
|
||||
}
|
||||
|
||||
return 0; // success
|
||||
}
|
||||
|
||||
// try to connect or throw on failure
|
||||
void connect(const std::string &host, int port, int timeout_ms = 0) {
|
||||
if (is_connected()) {
|
||||
close();
|
||||
}
|
||||
struct addrinfo hints {};
|
||||
ZeroMemory(&hints, sizeof(hints));
|
||||
|
||||
hints.ai_family = AF_UNSPEC; // To work with IPv4, IPv6, and so on
|
||||
hints.ai_socktype = SOCK_STREAM; // TCP
|
||||
hints.ai_flags = AI_NUMERICSERV; // port passed as as numeric value
|
||||
hints.ai_protocol = 0;
|
||||
|
||||
timeval tv;
|
||||
tv.tv_sec = timeout_ms / 1000;
|
||||
tv.tv_usec = (timeout_ms % 1000) * 1000;
|
||||
|
||||
auto port_str = std::to_string(port);
|
||||
struct addrinfo *addrinfo_result;
|
||||
auto rv = ::getaddrinfo(host.c_str(), port_str.c_str(), &hints, &addrinfo_result);
|
||||
int last_error = 0;
|
||||
if (rv != 0) {
|
||||
last_error = ::WSAGetLastError();
|
||||
WSACleanup();
|
||||
throw_winsock_error_("getaddrinfo failed", last_error);
|
||||
}
|
||||
|
||||
// Try each address until we successfully connect(2).
|
||||
for (auto *rp = addrinfo_result; rp != nullptr; rp = rp->ai_next) {
|
||||
socket_ = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
|
||||
if (socket_ == INVALID_SOCKET) {
|
||||
last_error = ::WSAGetLastError();
|
||||
WSACleanup();
|
||||
continue;
|
||||
}
|
||||
if (connect_socket_with_timeout(socket_, rp->ai_addr, (int)rp->ai_addrlen, tv) == 0) {
|
||||
last_error = 0;
|
||||
break;
|
||||
}
|
||||
last_error = WSAGetLastError();
|
||||
::closesocket(socket_);
|
||||
socket_ = INVALID_SOCKET;
|
||||
}
|
||||
::freeaddrinfo(addrinfo_result);
|
||||
if (socket_ == INVALID_SOCKET) {
|
||||
WSACleanup();
|
||||
throw_winsock_error_("connect failed", last_error);
|
||||
}
|
||||
if (timeout_ms > 0) {
|
||||
DWORD tv = static_cast<DWORD>(timeout_ms);
|
||||
::setsockopt(socket_, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tv, sizeof(tv));
|
||||
::setsockopt(socket_, SOL_SOCKET, SO_SNDTIMEO, (const char *)&tv, sizeof(tv));
|
||||
}
|
||||
|
||||
// set TCP_NODELAY
|
||||
int enable_flag = 1;
|
||||
::setsockopt(socket_, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char *>(&enable_flag),
|
||||
sizeof(enable_flag));
|
||||
}
|
||||
|
||||
// Send exactly n_bytes of the given data.
|
||||
// On error close the connection and throw.
|
||||
void send(const char *data, size_t n_bytes) {
|
||||
size_t bytes_sent = 0;
|
||||
while (bytes_sent < n_bytes) {
|
||||
const int send_flags = 0;
|
||||
auto write_result =
|
||||
::send(socket_, data + bytes_sent, (int)(n_bytes - bytes_sent), send_flags);
|
||||
if (write_result == SOCKET_ERROR) {
|
||||
int last_error = ::WSAGetLastError();
|
||||
close();
|
||||
throw_winsock_error_("send failed", last_error);
|
||||
}
|
||||
|
||||
if (write_result == 0) // (probably should not happen but in any case..)
|
||||
{
|
||||
break;
|
||||
}
|
||||
bytes_sent += static_cast<size_t>(write_result);
|
||||
}
|
||||
}
|
||||
};
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
@@ -0,0 +1,203 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef _WIN32
|
||||
#error include tcp_client-windows.h instead
|
||||
#endif
|
||||
|
||||
// tcp client helper
|
||||
#include <spdlog/common.h>
|
||||
#include <spdlog/details/os.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
class tcp_client {
|
||||
int socket_ = -1;
|
||||
|
||||
public:
|
||||
bool is_connected() const { return socket_ != -1; }
|
||||
|
||||
void close() {
|
||||
if (is_connected()) {
|
||||
::close(socket_);
|
||||
socket_ = -1;
|
||||
}
|
||||
}
|
||||
|
||||
int fd() const { return socket_; }
|
||||
|
||||
~tcp_client() { close(); }
|
||||
|
||||
int connect_socket_with_timeout(int sockfd,
|
||||
const struct sockaddr *addr,
|
||||
socklen_t addrlen,
|
||||
const timeval &tv) {
|
||||
// Blocking connect if timeout is zero
|
||||
if (tv.tv_sec == 0 && tv.tv_usec == 0) {
|
||||
int rv = ::connect(sockfd, addr, addrlen);
|
||||
if (rv < 0 && errno == EISCONN) {
|
||||
// already connected, treat as success
|
||||
return 0;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Non-blocking path
|
||||
int orig_flags = ::fcntl(sockfd, F_GETFL, 0);
|
||||
if (orig_flags < 0) {
|
||||
return -1;
|
||||
}
|
||||
if (::fcntl(sockfd, F_SETFL, orig_flags | O_NONBLOCK) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int rv = ::connect(sockfd, addr, addrlen);
|
||||
if (rv == 0 || (rv < 0 && errno == EISCONN)) {
|
||||
// immediate connect or already connected
|
||||
::fcntl(sockfd, F_SETFL, orig_flags);
|
||||
return 0;
|
||||
}
|
||||
if (errno != EINPROGRESS) {
|
||||
::fcntl(sockfd, F_SETFL, orig_flags);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// wait for writability
|
||||
fd_set wfds;
|
||||
FD_ZERO(&wfds);
|
||||
FD_SET(sockfd, &wfds);
|
||||
|
||||
struct timeval tv_copy = tv;
|
||||
rv = ::select(sockfd + 1, nullptr, &wfds, nullptr, &tv_copy);
|
||||
if (rv <= 0) {
|
||||
// timeout or error
|
||||
::fcntl(sockfd, F_SETFL, orig_flags);
|
||||
if (rv == 0) errno = ETIMEDOUT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// check socket error
|
||||
int so_error = 0;
|
||||
socklen_t len = sizeof(so_error);
|
||||
if (::getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &so_error, &len) < 0) {
|
||||
::fcntl(sockfd, F_SETFL, orig_flags);
|
||||
return -1;
|
||||
}
|
||||
::fcntl(sockfd, F_SETFL, orig_flags);
|
||||
if (so_error != 0 && so_error != EISCONN) {
|
||||
errno = so_error;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// try to connect or throw on failure
|
||||
void connect(const std::string &host, int port, int timeout_ms = 0) {
|
||||
close();
|
||||
struct addrinfo hints {};
|
||||
memset(&hints, 0, sizeof(struct addrinfo));
|
||||
hints.ai_family = AF_UNSPEC; // To work with IPv4, IPv6, and so on
|
||||
hints.ai_socktype = SOCK_STREAM; // TCP
|
||||
hints.ai_flags = AI_NUMERICSERV; // port passed as as numeric value
|
||||
hints.ai_protocol = 0;
|
||||
|
||||
struct timeval tv;
|
||||
tv.tv_sec = timeout_ms / 1000;
|
||||
tv.tv_usec = (timeout_ms % 1000) * 1000;
|
||||
|
||||
auto port_str = std::to_string(port);
|
||||
struct addrinfo *addrinfo_result;
|
||||
auto rv = ::getaddrinfo(host.c_str(), port_str.c_str(), &hints, &addrinfo_result);
|
||||
if (rv != 0) {
|
||||
throw_spdlog_ex(fmt_lib::format("::getaddrinfo failed: {}", gai_strerror(rv)));
|
||||
}
|
||||
|
||||
// Try each address until we successfully connect(2).
|
||||
int last_errno = 0;
|
||||
for (auto *rp = addrinfo_result; rp != nullptr; rp = rp->ai_next) {
|
||||
#if defined(SOCK_CLOEXEC)
|
||||
const int flags = SOCK_CLOEXEC;
|
||||
#else
|
||||
const int flags = 0;
|
||||
#endif
|
||||
socket_ = ::socket(rp->ai_family, rp->ai_socktype | flags, rp->ai_protocol);
|
||||
if (socket_ == -1) {
|
||||
last_errno = errno;
|
||||
continue;
|
||||
}
|
||||
::fcntl(socket_, F_SETFD, FD_CLOEXEC);
|
||||
if (connect_socket_with_timeout(socket_, rp->ai_addr, rp->ai_addrlen, tv) == 0) {
|
||||
last_errno = 0;
|
||||
break;
|
||||
}
|
||||
last_errno = errno;
|
||||
::close(socket_);
|
||||
socket_ = -1;
|
||||
}
|
||||
::freeaddrinfo(addrinfo_result);
|
||||
if (socket_ == -1) {
|
||||
throw_spdlog_ex("::connect failed", last_errno);
|
||||
}
|
||||
|
||||
if (timeout_ms > 0) {
|
||||
// Set timeouts for send and recv
|
||||
::setsockopt(socket_, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tv, sizeof(tv));
|
||||
::setsockopt(socket_, SOL_SOCKET, SO_SNDTIMEO, (const char *)&tv, sizeof(tv));
|
||||
}
|
||||
|
||||
// set TCP_NODELAY
|
||||
int enable_flag = 1;
|
||||
::setsockopt(socket_, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char *>(&enable_flag),
|
||||
sizeof(enable_flag));
|
||||
|
||||
// prevent sigpipe on systems where MSG_NOSIGNAL is not available
|
||||
#if defined(SO_NOSIGPIPE) && !defined(MSG_NOSIGNAL)
|
||||
::setsockopt(socket_, SOL_SOCKET, SO_NOSIGPIPE, reinterpret_cast<char *>(&enable_flag),
|
||||
sizeof(enable_flag));
|
||||
#endif
|
||||
|
||||
#if !defined(SO_NOSIGPIPE) && !defined(MSG_NOSIGNAL)
|
||||
#error "tcp_sink would raise SIGPIPE since neither SO_NOSIGPIPE nor MSG_NOSIGNAL are available"
|
||||
#endif
|
||||
}
|
||||
|
||||
// Send exactly n_bytes of the given data.
|
||||
// On error close the connection and throw.
|
||||
void send(const char *data, size_t n_bytes) {
|
||||
size_t bytes_sent = 0;
|
||||
while (bytes_sent < n_bytes) {
|
||||
#if defined(MSG_NOSIGNAL)
|
||||
const int send_flags = MSG_NOSIGNAL;
|
||||
#else
|
||||
const int send_flags = 0;
|
||||
#endif
|
||||
auto write_result =
|
||||
::send(socket_, data + bytes_sent, n_bytes - bytes_sent, send_flags);
|
||||
if (write_result < 0) {
|
||||
close();
|
||||
throw_spdlog_ex("write(2) failed", errno);
|
||||
}
|
||||
|
||||
if (write_result == 0) // (probably should not happen but in any case..)
|
||||
{
|
||||
break;
|
||||
}
|
||||
bytes_sent += static_cast<size_t>(write_result);
|
||||
}
|
||||
}
|
||||
};
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
@@ -0,0 +1,125 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef SPDLOG_HEADER_ONLY
|
||||
#include <spdlog/details/thread_pool.h>
|
||||
#endif
|
||||
|
||||
#include <cassert>
|
||||
#include <spdlog/common.h>
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
|
||||
SPDLOG_INLINE thread_pool::thread_pool(size_t q_max_items,
|
||||
size_t threads_n,
|
||||
std::function<void()> on_thread_start,
|
||||
std::function<void()> on_thread_stop)
|
||||
: q_(q_max_items) {
|
||||
if (threads_n == 0 || threads_n > 1000) {
|
||||
throw_spdlog_ex(
|
||||
"spdlog::thread_pool(): invalid threads_n param (valid "
|
||||
"range is 1-1000)");
|
||||
}
|
||||
for (size_t i = 0; i < threads_n; i++) {
|
||||
threads_.emplace_back([this, on_thread_start, on_thread_stop] {
|
||||
on_thread_start();
|
||||
this->thread_pool::worker_loop_();
|
||||
on_thread_stop();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
SPDLOG_INLINE thread_pool::thread_pool(size_t q_max_items,
|
||||
size_t threads_n,
|
||||
std::function<void()> on_thread_start)
|
||||
: thread_pool(q_max_items, threads_n, std::move(on_thread_start), [] {}) {}
|
||||
|
||||
SPDLOG_INLINE thread_pool::thread_pool(size_t q_max_items, size_t threads_n)
|
||||
: thread_pool(q_max_items, threads_n, [] {}, [] {}) {}
|
||||
|
||||
// message all threads to terminate gracefully join them
|
||||
SPDLOG_INLINE thread_pool::~thread_pool() {
|
||||
SPDLOG_TRY {
|
||||
for (size_t i = 0; i < threads_.size(); i++) {
|
||||
post_async_msg_(async_msg(async_msg_type::terminate), async_overflow_policy::block);
|
||||
}
|
||||
|
||||
for (auto &t : threads_) {
|
||||
t.join();
|
||||
}
|
||||
}
|
||||
SPDLOG_CATCH_STD
|
||||
}
|
||||
|
||||
void SPDLOG_INLINE thread_pool::post_log(async_logger_ptr &&worker_ptr,
|
||||
const details::log_msg &msg,
|
||||
async_overflow_policy overflow_policy) {
|
||||
async_msg async_m(std::move(worker_ptr), async_msg_type::log, msg);
|
||||
post_async_msg_(std::move(async_m), overflow_policy);
|
||||
}
|
||||
|
||||
void SPDLOG_INLINE thread_pool::post_flush(async_logger_ptr &&worker_ptr,
|
||||
async_overflow_policy overflow_policy) {
|
||||
post_async_msg_(async_msg(std::move(worker_ptr), async_msg_type::flush), overflow_policy);
|
||||
}
|
||||
|
||||
size_t SPDLOG_INLINE thread_pool::overrun_counter() { return q_.overrun_counter(); }
|
||||
|
||||
void SPDLOG_INLINE thread_pool::reset_overrun_counter() { q_.reset_overrun_counter(); }
|
||||
|
||||
size_t SPDLOG_INLINE thread_pool::discard_counter() { return q_.discard_counter(); }
|
||||
|
||||
void SPDLOG_INLINE thread_pool::reset_discard_counter() { q_.reset_discard_counter(); }
|
||||
|
||||
size_t SPDLOG_INLINE thread_pool::queue_size() { return q_.size(); }
|
||||
|
||||
void SPDLOG_INLINE thread_pool::post_async_msg_(async_msg &&new_msg,
|
||||
async_overflow_policy overflow_policy) {
|
||||
if (overflow_policy == async_overflow_policy::block) {
|
||||
q_.enqueue(std::move(new_msg));
|
||||
} else if (overflow_policy == async_overflow_policy::overrun_oldest) {
|
||||
q_.enqueue_nowait(std::move(new_msg));
|
||||
} else {
|
||||
assert(overflow_policy == async_overflow_policy::discard_new);
|
||||
q_.enqueue_if_have_room(std::move(new_msg));
|
||||
}
|
||||
}
|
||||
|
||||
void SPDLOG_INLINE thread_pool::worker_loop_() {
|
||||
while (process_next_msg_()) {
|
||||
}
|
||||
}
|
||||
|
||||
// process next message in the queue
|
||||
// returns true if this thread should still be active (while no terminated msg was received)
|
||||
bool SPDLOG_INLINE thread_pool::process_next_msg_() {
|
||||
async_msg incoming_async_msg;
|
||||
q_.dequeue(incoming_async_msg);
|
||||
|
||||
switch (incoming_async_msg.msg_type) {
|
||||
case async_msg_type::log: {
|
||||
incoming_async_msg.worker_ptr->backend_sink_it_(incoming_async_msg);
|
||||
return true;
|
||||
}
|
||||
case async_msg_type::flush: {
|
||||
incoming_async_msg.worker_ptr->backend_flush_();
|
||||
return true;
|
||||
}
|
||||
|
||||
case async_msg_type::terminate: {
|
||||
return false;
|
||||
}
|
||||
|
||||
default: {
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
@@ -0,0 +1,117 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <spdlog/details/log_msg_buffer.h>
|
||||
#include <spdlog/details/mpmc_blocking_q.h>
|
||||
#include <spdlog/details/os.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
namespace spdlog {
|
||||
class async_logger;
|
||||
|
||||
namespace details {
|
||||
|
||||
using async_logger_ptr = std::shared_ptr<spdlog::async_logger>;
|
||||
|
||||
enum class async_msg_type { log, flush, terminate };
|
||||
|
||||
// Async msg to move to/from the queue
|
||||
// Movable only. should never be copied
|
||||
struct async_msg : log_msg_buffer {
|
||||
async_msg_type msg_type{async_msg_type::log};
|
||||
async_logger_ptr worker_ptr;
|
||||
|
||||
async_msg() = default;
|
||||
~async_msg() = default;
|
||||
|
||||
// should only be moved in or out of the queue..
|
||||
async_msg(const async_msg &) = delete;
|
||||
|
||||
// support for vs2013 move
|
||||
#if defined(_MSC_VER) && _MSC_VER <= 1800
|
||||
async_msg(async_msg &&other)
|
||||
: log_msg_buffer(std::move(other)),
|
||||
msg_type(other.msg_type),
|
||||
worker_ptr(std::move(other.worker_ptr)) {}
|
||||
|
||||
async_msg &operator=(async_msg &&other) {
|
||||
*static_cast<log_msg_buffer *>(this) = std::move(other);
|
||||
msg_type = other.msg_type;
|
||||
worker_ptr = std::move(other.worker_ptr);
|
||||
return *this;
|
||||
}
|
||||
#else // (_MSC_VER) && _MSC_VER <= 1800
|
||||
async_msg(async_msg &&) = default;
|
||||
async_msg &operator=(async_msg &&) = default;
|
||||
#endif
|
||||
|
||||
// construct from log_msg with given type
|
||||
async_msg(async_logger_ptr &&worker, async_msg_type the_type, const details::log_msg &m)
|
||||
: log_msg_buffer{m},
|
||||
msg_type{the_type},
|
||||
worker_ptr{std::move(worker)} {}
|
||||
|
||||
async_msg(async_logger_ptr &&worker, async_msg_type the_type)
|
||||
: log_msg_buffer{},
|
||||
msg_type{the_type},
|
||||
worker_ptr{std::move(worker)} {}
|
||||
|
||||
explicit async_msg(async_msg_type the_type)
|
||||
: async_msg{nullptr, the_type} {}
|
||||
};
|
||||
|
||||
class SPDLOG_API thread_pool {
|
||||
public:
|
||||
using item_type = async_msg;
|
||||
using q_type = details::mpmc_blocking_queue<item_type>;
|
||||
|
||||
thread_pool(size_t q_max_items,
|
||||
size_t threads_n,
|
||||
std::function<void()> on_thread_start,
|
||||
std::function<void()> on_thread_stop);
|
||||
thread_pool(size_t q_max_items, size_t threads_n, std::function<void()> on_thread_start);
|
||||
thread_pool(size_t q_max_items, size_t threads_n);
|
||||
|
||||
// message all threads to terminate gracefully and join them
|
||||
~thread_pool();
|
||||
|
||||
thread_pool(const thread_pool &) = delete;
|
||||
thread_pool &operator=(thread_pool &&) = delete;
|
||||
|
||||
void post_log(async_logger_ptr &&worker_ptr,
|
||||
const details::log_msg &msg,
|
||||
async_overflow_policy overflow_policy);
|
||||
void post_flush(async_logger_ptr &&worker_ptr, async_overflow_policy overflow_policy);
|
||||
size_t overrun_counter();
|
||||
void reset_overrun_counter();
|
||||
size_t discard_counter();
|
||||
void reset_discard_counter();
|
||||
size_t queue_size();
|
||||
|
||||
private:
|
||||
q_type q_;
|
||||
|
||||
std::vector<std::thread> threads_;
|
||||
|
||||
void post_async_msg_(async_msg &&new_msg, async_overflow_policy overflow_policy);
|
||||
void worker_loop_();
|
||||
|
||||
// process next message in the queue
|
||||
// return true if this thread should still be active (while no terminate msg
|
||||
// was received)
|
||||
bool process_next_msg_();
|
||||
};
|
||||
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
|
||||
#ifdef SPDLOG_HEADER_ONLY
|
||||
#include "thread_pool-inl.h"
|
||||
#endif
|
||||
@@ -0,0 +1,98 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
// Helper RAII over winsock udp client socket.
|
||||
// Will throw on construction if socket creation failed.
|
||||
|
||||
#include <spdlog/common.h>
|
||||
#include <spdlog/details/os.h>
|
||||
#include <spdlog/details/windows_include.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma comment(lib, "Ws2_32.lib")
|
||||
#pragma comment(lib, "Mswsock.lib")
|
||||
#pragma comment(lib, "AdvApi32.lib")
|
||||
#endif
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
class udp_client {
|
||||
static constexpr int TX_BUFFER_SIZE = 1024 * 10;
|
||||
SOCKET socket_ = INVALID_SOCKET;
|
||||
sockaddr_in addr_ = {};
|
||||
|
||||
static void init_winsock_() {
|
||||
WSADATA wsaData;
|
||||
auto rv = ::WSAStartup(MAKEWORD(2, 2), &wsaData);
|
||||
if (rv != 0) {
|
||||
throw_winsock_error_("WSAStartup failed", ::WSAGetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
static void throw_winsock_error_(const std::string &msg, int last_error) {
|
||||
char buf[512];
|
||||
::FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL,
|
||||
last_error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf,
|
||||
(sizeof(buf) / sizeof(char)), NULL);
|
||||
|
||||
throw_spdlog_ex(fmt_lib::format("udp_sink - {}: {}", msg, buf));
|
||||
}
|
||||
|
||||
void cleanup_() {
|
||||
if (socket_ != INVALID_SOCKET) {
|
||||
::closesocket(socket_);
|
||||
}
|
||||
socket_ = INVALID_SOCKET;
|
||||
::WSACleanup();
|
||||
}
|
||||
|
||||
public:
|
||||
udp_client(const std::string &host, uint16_t port) {
|
||||
init_winsock_();
|
||||
|
||||
addr_.sin_family = PF_INET;
|
||||
addr_.sin_port = htons(port);
|
||||
addr_.sin_addr.s_addr = INADDR_ANY;
|
||||
if (InetPtonA(PF_INET, host.c_str(), &addr_.sin_addr.s_addr) != 1) {
|
||||
int last_error = ::WSAGetLastError();
|
||||
::WSACleanup();
|
||||
throw_winsock_error_("error: Invalid address!", last_error);
|
||||
}
|
||||
|
||||
socket_ = ::socket(PF_INET, SOCK_DGRAM, 0);
|
||||
if (socket_ == INVALID_SOCKET) {
|
||||
int last_error = ::WSAGetLastError();
|
||||
::WSACleanup();
|
||||
throw_winsock_error_("error: Create Socket failed", last_error);
|
||||
}
|
||||
|
||||
int option_value = TX_BUFFER_SIZE;
|
||||
if (::setsockopt(socket_, SOL_SOCKET, SO_SNDBUF,
|
||||
reinterpret_cast<const char *>(&option_value), sizeof(option_value)) < 0) {
|
||||
int last_error = ::WSAGetLastError();
|
||||
cleanup_();
|
||||
throw_winsock_error_("error: setsockopt(SO_SNDBUF) Failed!", last_error);
|
||||
}
|
||||
}
|
||||
|
||||
~udp_client() { cleanup_(); }
|
||||
|
||||
SOCKET fd() const { return socket_; }
|
||||
|
||||
void send(const char *data, size_t n_bytes) {
|
||||
socklen_t tolen = sizeof(struct sockaddr);
|
||||
if (::sendto(socket_, data, static_cast<int>(n_bytes), 0, (struct sockaddr *)&addr_,
|
||||
tolen) == -1) {
|
||||
throw_spdlog_ex("sendto(2) failed", errno);
|
||||
}
|
||||
}
|
||||
};
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
@@ -0,0 +1,81 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
// Helper RAII over unix udp client socket.
|
||||
// Will throw on construction if the socket creation failed.
|
||||
|
||||
#ifdef _WIN32
|
||||
#error "include udp_client-windows.h instead"
|
||||
#endif
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <cstring>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/udp.h>
|
||||
#include <spdlog/common.h>
|
||||
#include <spdlog/details/os.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
|
||||
class udp_client {
|
||||
static constexpr int TX_BUFFER_SIZE = 1024 * 10;
|
||||
int socket_ = -1;
|
||||
struct sockaddr_in sockAddr_;
|
||||
|
||||
void cleanup_() {
|
||||
if (socket_ != -1) {
|
||||
::close(socket_);
|
||||
socket_ = -1;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
udp_client(const std::string &host, uint16_t port) {
|
||||
socket_ = ::socket(PF_INET, SOCK_DGRAM, 0);
|
||||
if (socket_ < 0) {
|
||||
throw_spdlog_ex("error: Create Socket Failed!");
|
||||
}
|
||||
|
||||
int option_value = TX_BUFFER_SIZE;
|
||||
if (::setsockopt(socket_, SOL_SOCKET, SO_SNDBUF,
|
||||
reinterpret_cast<const char *>(&option_value), sizeof(option_value)) < 0) {
|
||||
cleanup_();
|
||||
throw_spdlog_ex("error: setsockopt(SO_SNDBUF) Failed!");
|
||||
}
|
||||
|
||||
sockAddr_.sin_family = AF_INET;
|
||||
sockAddr_.sin_port = htons(port);
|
||||
|
||||
if (::inet_aton(host.c_str(), &sockAddr_.sin_addr) == 0) {
|
||||
cleanup_();
|
||||
throw_spdlog_ex("error: Invalid address!");
|
||||
}
|
||||
|
||||
::memset(sockAddr_.sin_zero, 0x00, sizeof(sockAddr_.sin_zero));
|
||||
}
|
||||
|
||||
~udp_client() { cleanup_(); }
|
||||
|
||||
int fd() const { return socket_; }
|
||||
|
||||
// Send exactly n_bytes of the given data.
|
||||
// On error close the connection and throw.
|
||||
void send(const char *data, size_t n_bytes) {
|
||||
ssize_t toslen = 0;
|
||||
socklen_t tolen = sizeof(struct sockaddr);
|
||||
if ((toslen = ::sendto(socket_, data, n_bytes, 0, (struct sockaddr *)&sockAddr_, tolen)) ==
|
||||
-1) {
|
||||
throw_spdlog_ex("sendto(2) failed", errno);
|
||||
}
|
||||
}
|
||||
};
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX // prevent windows redefining min/max
|
||||
#endif
|
||||
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
|
||||
#include <windows.h>
|
||||
+224
@@ -0,0 +1,224 @@
|
||||
//
|
||||
// Copyright(c) 2015 Gabi Melman.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cctype>
|
||||
#include <spdlog/common.h>
|
||||
|
||||
#if defined(__has_include)
|
||||
#if __has_include(<version>)
|
||||
#include <version>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if __cpp_lib_span >= 202002L
|
||||
#include <span>
|
||||
#endif
|
||||
|
||||
//
|
||||
// Support for logging binary data as hex
|
||||
// format flags, any combination of the following:
|
||||
// {:X} - print in uppercase.
|
||||
// {:s} - don't separate each byte with space.
|
||||
// {:p} - don't print the position on each line start.
|
||||
// {:n} - don't split the output to lines.
|
||||
// {:a} - show ASCII if :n is not set
|
||||
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// std::vector<char> v(200, 0x0b);
|
||||
// logger->info("Some buffer {}", spdlog::to_hex(v));
|
||||
// char buf[128];
|
||||
// logger->info("Some buffer {:X}", spdlog::to_hex(std::begin(buf), std::end(buf)));
|
||||
// logger->info("Some buffer {:X}", spdlog::to_hex(std::begin(buf), std::end(buf), 16));
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
|
||||
template <typename It>
|
||||
class dump_info {
|
||||
public:
|
||||
dump_info(It range_begin, It range_end, size_t size_per_line)
|
||||
: begin_(range_begin),
|
||||
end_(range_end),
|
||||
size_per_line_(size_per_line) {}
|
||||
|
||||
// do not use begin() and end() to avoid collision with fmt/ranges
|
||||
It get_begin() const { return begin_; }
|
||||
It get_end() const { return end_; }
|
||||
size_t size_per_line() const { return size_per_line_; }
|
||||
|
||||
private:
|
||||
It begin_, end_;
|
||||
size_t size_per_line_;
|
||||
};
|
||||
} // namespace details
|
||||
|
||||
// create a dump_info that wraps the given container
|
||||
template <typename Container>
|
||||
inline details::dump_info<typename Container::const_iterator> to_hex(const Container &container,
|
||||
size_t size_per_line = 32) {
|
||||
static_assert(sizeof(typename Container::value_type) == 1,
|
||||
"sizeof(Container::value_type) != 1");
|
||||
using Iter = typename Container::const_iterator;
|
||||
return details::dump_info<Iter>(std::begin(container), std::end(container), size_per_line);
|
||||
}
|
||||
|
||||
#if __cpp_lib_span >= 202002L
|
||||
|
||||
template <typename Value, size_t Extent>
|
||||
inline details::dump_info<typename std::span<Value, Extent>::iterator> to_hex(
|
||||
const std::span<Value, Extent> &container, size_t size_per_line = 32) {
|
||||
using Container = std::span<Value, Extent>;
|
||||
static_assert(sizeof(typename Container::value_type) == 1,
|
||||
"sizeof(Container::value_type) != 1");
|
||||
using Iter = typename Container::iterator;
|
||||
return details::dump_info<Iter>(std::begin(container), std::end(container), size_per_line);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// create dump_info from ranges
|
||||
template <typename It>
|
||||
inline details::dump_info<It> to_hex(const It range_begin,
|
||||
const It range_end,
|
||||
size_t size_per_line = 32) {
|
||||
return details::dump_info<It>(range_begin, range_end, size_per_line);
|
||||
}
|
||||
|
||||
} // namespace spdlog
|
||||
|
||||
namespace
|
||||
#ifdef SPDLOG_USE_STD_FORMAT
|
||||
std
|
||||
#else
|
||||
fmt
|
||||
#endif
|
||||
{
|
||||
|
||||
template <typename T>
|
||||
struct formatter<spdlog::details::dump_info<T>, char> {
|
||||
char delimiter = ' ';
|
||||
bool put_newlines = true;
|
||||
bool put_delimiters = true;
|
||||
bool use_uppercase = false;
|
||||
bool put_positions = true; // position on start of each line
|
||||
bool show_ascii = false;
|
||||
|
||||
// parse the format string flags
|
||||
template <typename ParseContext>
|
||||
SPDLOG_CONSTEXPR_FUNC auto parse(ParseContext &ctx) -> decltype(ctx.begin()) {
|
||||
auto it = ctx.begin();
|
||||
while (it != ctx.end() && *it != '}') {
|
||||
switch (*it) {
|
||||
case 'X':
|
||||
use_uppercase = true;
|
||||
break;
|
||||
case 's':
|
||||
put_delimiters = false;
|
||||
break;
|
||||
case 'p':
|
||||
put_positions = false;
|
||||
break;
|
||||
case 'n':
|
||||
put_newlines = false;
|
||||
show_ascii = false;
|
||||
break;
|
||||
case 'a':
|
||||
if (put_newlines) {
|
||||
show_ascii = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
++it;
|
||||
}
|
||||
return it;
|
||||
}
|
||||
|
||||
// format the given bytes range as hex
|
||||
template <typename FormatContext, typename Container>
|
||||
auto format(const spdlog::details::dump_info<Container> &the_range,
|
||||
FormatContext &ctx) const -> decltype(ctx.out()) {
|
||||
SPDLOG_CONSTEXPR const char *hex_upper = "0123456789ABCDEF";
|
||||
SPDLOG_CONSTEXPR const char *hex_lower = "0123456789abcdef";
|
||||
const char *hex_chars = use_uppercase ? hex_upper : hex_lower;
|
||||
|
||||
#if !defined(SPDLOG_USE_STD_FORMAT) && FMT_VERSION < 60000
|
||||
auto inserter = ctx.begin();
|
||||
#else
|
||||
auto inserter = ctx.out();
|
||||
#endif
|
||||
|
||||
int size_per_line = static_cast<int>(the_range.size_per_line());
|
||||
auto start_of_line = the_range.get_begin();
|
||||
for (auto i = the_range.get_begin(); i != the_range.get_end(); i++) {
|
||||
auto ch = static_cast<unsigned char>(*i);
|
||||
|
||||
if (put_newlines &&
|
||||
(i == the_range.get_begin() || i - start_of_line >= size_per_line)) {
|
||||
if (show_ascii && i != the_range.get_begin()) {
|
||||
*inserter++ = delimiter;
|
||||
*inserter++ = delimiter;
|
||||
for (auto j = start_of_line; j < i; j++) {
|
||||
auto pc = static_cast<unsigned char>(*j);
|
||||
*inserter++ = std::isprint(pc) ? static_cast<char>(*j) : '.';
|
||||
}
|
||||
}
|
||||
|
||||
put_newline(inserter, static_cast<size_t>(i - the_range.get_begin()));
|
||||
|
||||
// put first byte without delimiter in front of it
|
||||
*inserter++ = hex_chars[(ch >> 4) & 0x0f];
|
||||
*inserter++ = hex_chars[ch & 0x0f];
|
||||
start_of_line = i;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (put_delimiters && i != the_range.get_begin()) {
|
||||
*inserter++ = delimiter;
|
||||
}
|
||||
|
||||
*inserter++ = hex_chars[(ch >> 4) & 0x0f];
|
||||
*inserter++ = hex_chars[ch & 0x0f];
|
||||
}
|
||||
if (show_ascii) // add ascii to last line
|
||||
{
|
||||
if (the_range.get_end() - the_range.get_begin() > size_per_line) {
|
||||
auto blank_num = size_per_line - (the_range.get_end() - start_of_line);
|
||||
while (blank_num-- > 0) {
|
||||
*inserter++ = delimiter;
|
||||
*inserter++ = delimiter;
|
||||
if (put_delimiters) {
|
||||
*inserter++ = delimiter;
|
||||
}
|
||||
}
|
||||
}
|
||||
*inserter++ = delimiter;
|
||||
*inserter++ = delimiter;
|
||||
for (auto j = start_of_line; j != the_range.get_end(); j++) {
|
||||
auto pc = static_cast<unsigned char>(*j);
|
||||
*inserter++ = std::isprint(pc) ? static_cast<char>(*j) : '.';
|
||||
}
|
||||
}
|
||||
return inserter;
|
||||
}
|
||||
|
||||
// put newline(and position header)
|
||||
template <typename It>
|
||||
void put_newline(It inserter, std::size_t pos) const {
|
||||
#ifdef _WIN32
|
||||
*inserter++ = '\r';
|
||||
#endif
|
||||
*inserter++ = '\n';
|
||||
|
||||
if (put_positions) {
|
||||
spdlog::fmt_lib::format_to(inserter, SPDLOG_FMT_STRING("{:04X}: "), pos);
|
||||
}
|
||||
}
|
||||
};
|
||||
} // namespace std
|
||||
@@ -0,0 +1,220 @@
|
||||
// Formatting library for C++ - dynamic argument lists
|
||||
//
|
||||
// Copyright (c) 2012 - present, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#ifndef FMT_ARGS_H_
|
||||
#define FMT_ARGS_H_
|
||||
|
||||
#ifndef FMT_MODULE
|
||||
# include <functional> // std::reference_wrapper
|
||||
# include <memory> // std::unique_ptr
|
||||
# include <vector>
|
||||
#endif
|
||||
|
||||
#include "format.h" // std_string_view
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
namespace detail {
|
||||
|
||||
template <typename T> struct is_reference_wrapper : std::false_type {};
|
||||
template <typename T>
|
||||
struct is_reference_wrapper<std::reference_wrapper<T>> : std::true_type {};
|
||||
|
||||
template <typename T> auto unwrap(const T& v) -> const T& { return v; }
|
||||
template <typename T>
|
||||
auto unwrap(const std::reference_wrapper<T>& v) -> const T& {
|
||||
return static_cast<const T&>(v);
|
||||
}
|
||||
|
||||
// node is defined outside dynamic_arg_list to workaround a C2504 bug in MSVC
|
||||
// 2022 (v17.10.0).
|
||||
//
|
||||
// Workaround for clang's -Wweak-vtables. Unlike for regular classes, for
|
||||
// templates it doesn't complain about inability to deduce single translation
|
||||
// unit for placing vtable. So node is made a fake template.
|
||||
template <typename = void> struct node {
|
||||
virtual ~node() = default;
|
||||
std::unique_ptr<node<>> next;
|
||||
};
|
||||
|
||||
class dynamic_arg_list {
|
||||
template <typename T> struct typed_node : node<> {
|
||||
T value;
|
||||
|
||||
template <typename Arg>
|
||||
FMT_CONSTEXPR typed_node(const Arg& arg) : value(arg) {}
|
||||
|
||||
template <typename Char>
|
||||
FMT_CONSTEXPR typed_node(const basic_string_view<Char>& arg)
|
||||
: value(arg.data(), arg.size()) {}
|
||||
};
|
||||
|
||||
std::unique_ptr<node<>> head_;
|
||||
|
||||
public:
|
||||
template <typename T, typename Arg> auto push(const Arg& arg) -> const T& {
|
||||
auto new_node = std::unique_ptr<typed_node<T>>(new typed_node<T>(arg));
|
||||
auto& value = new_node->value;
|
||||
new_node->next = std::move(head_);
|
||||
head_ = std::move(new_node);
|
||||
return value;
|
||||
}
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* A dynamic list of formatting arguments with storage.
|
||||
*
|
||||
* It can be implicitly converted into `fmt::basic_format_args` for passing
|
||||
* into type-erased formatting functions such as `fmt::vformat`.
|
||||
*/
|
||||
FMT_EXPORT template <typename Context> class dynamic_format_arg_store {
|
||||
private:
|
||||
using char_type = typename Context::char_type;
|
||||
|
||||
template <typename T> struct need_copy {
|
||||
static constexpr detail::type mapped_type =
|
||||
detail::mapped_type_constant<T, char_type>::value;
|
||||
|
||||
enum {
|
||||
value = !(detail::is_reference_wrapper<T>::value ||
|
||||
std::is_same<T, basic_string_view<char_type>>::value ||
|
||||
std::is_same<T, detail::std_string_view<char_type>>::value ||
|
||||
(mapped_type != detail::type::cstring_type &&
|
||||
mapped_type != detail::type::string_type &&
|
||||
mapped_type != detail::type::custom_type))
|
||||
};
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using stored_t = conditional_t<
|
||||
std::is_convertible<T, std::basic_string<char_type>>::value &&
|
||||
!detail::is_reference_wrapper<T>::value,
|
||||
std::basic_string<char_type>, T>;
|
||||
|
||||
// Storage of basic_format_arg must be contiguous.
|
||||
std::vector<basic_format_arg<Context>> data_;
|
||||
std::vector<detail::named_arg_info<char_type>> named_info_;
|
||||
|
||||
// Storage of arguments not fitting into basic_format_arg must grow
|
||||
// without relocation because items in data_ refer to it.
|
||||
detail::dynamic_arg_list dynamic_args_;
|
||||
|
||||
friend class basic_format_args<Context>;
|
||||
|
||||
auto data() const -> const basic_format_arg<Context>* {
|
||||
return named_info_.empty() ? data_.data() : data_.data() + 1;
|
||||
}
|
||||
|
||||
template <typename T> void emplace_arg(const T& arg) {
|
||||
data_.emplace_back(arg);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void emplace_arg(const detail::named_arg<char_type, T>& arg) {
|
||||
if (named_info_.empty())
|
||||
data_.insert(data_.begin(), basic_format_arg<Context>(nullptr, 0));
|
||||
data_.emplace_back(detail::unwrap(arg.value));
|
||||
auto pop_one = [](std::vector<basic_format_arg<Context>>* data) {
|
||||
data->pop_back();
|
||||
};
|
||||
std::unique_ptr<std::vector<basic_format_arg<Context>>, decltype(pop_one)>
|
||||
guard{&data_, pop_one};
|
||||
named_info_.push_back({arg.name, static_cast<int>(data_.size() - 2u)});
|
||||
data_[0] = {named_info_.data(), named_info_.size()};
|
||||
guard.release();
|
||||
}
|
||||
|
||||
public:
|
||||
constexpr dynamic_format_arg_store() = default;
|
||||
|
||||
operator basic_format_args<Context>() const {
|
||||
return basic_format_args<Context>(data(), static_cast<int>(data_.size()),
|
||||
!named_info_.empty());
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an argument into the dynamic store for later passing to a formatting
|
||||
* function.
|
||||
*
|
||||
* Note that custom types and string types (but not string views) are copied
|
||||
* into the store dynamically allocating memory if necessary.
|
||||
*
|
||||
* **Example**:
|
||||
*
|
||||
* fmt::dynamic_format_arg_store<fmt::format_context> store;
|
||||
* store.push_back(42);
|
||||
* store.push_back("abc");
|
||||
* store.push_back(1.5f);
|
||||
* std::string result = fmt::vformat("{} and {} and {}", store);
|
||||
*/
|
||||
template <typename T> void push_back(const T& arg) {
|
||||
if (detail::const_check(need_copy<T>::value))
|
||||
emplace_arg(dynamic_args_.push<stored_t<T>>(arg));
|
||||
else
|
||||
emplace_arg(detail::unwrap(arg));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a reference to the argument into the dynamic store for later passing
|
||||
* to a formatting function.
|
||||
*
|
||||
* **Example**:
|
||||
*
|
||||
* fmt::dynamic_format_arg_store<fmt::format_context> store;
|
||||
* char band[] = "Rolling Stones";
|
||||
* store.push_back(std::cref(band));
|
||||
* band[9] = 'c'; // Changing str affects the output.
|
||||
* std::string result = fmt::vformat("{}", store);
|
||||
* // result == "Rolling Scones"
|
||||
*/
|
||||
template <typename T> void push_back(std::reference_wrapper<T> arg) {
|
||||
static_assert(
|
||||
need_copy<T>::value,
|
||||
"objects of built-in types and string views are always copied");
|
||||
emplace_arg(arg.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds named argument into the dynamic store for later passing to a
|
||||
* formatting function. `std::reference_wrapper` is supported to avoid
|
||||
* copying of the argument. The name is always copied into the store.
|
||||
*/
|
||||
template <typename T>
|
||||
void push_back(const detail::named_arg<char_type, T>& arg) {
|
||||
const char_type* arg_name =
|
||||
dynamic_args_.push<std::basic_string<char_type>>(arg.name).c_str();
|
||||
if (detail::const_check(need_copy<T>::value)) {
|
||||
emplace_arg(
|
||||
fmt::arg(arg_name, dynamic_args_.push<stored_t<T>>(arg.value)));
|
||||
} else {
|
||||
emplace_arg(fmt::arg(arg_name, arg.value));
|
||||
}
|
||||
}
|
||||
|
||||
/// Erase all elements from the store.
|
||||
void clear() {
|
||||
data_.clear();
|
||||
named_info_.clear();
|
||||
dynamic_args_ = {};
|
||||
}
|
||||
|
||||
/// Reserves space to store at least `new_cap` arguments including
|
||||
/// `new_cap_named` named arguments.
|
||||
void reserve(size_t new_cap, size_t new_cap_named) {
|
||||
FMT_ASSERT(new_cap >= new_cap_named,
|
||||
"set of arguments includes set of named arguments");
|
||||
data_.reserve(new_cap);
|
||||
named_info_.reserve(new_cap_named);
|
||||
}
|
||||
|
||||
/// Returns the number of elements in the store.
|
||||
auto size() const noexcept -> size_t { return data_.size(); }
|
||||
};
|
||||
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#endif // FMT_ARGS_H_
|
||||
+3010
File diff suppressed because it is too large
Load Diff
+2246
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,637 @@
|
||||
// Formatting library for C++ - color support
|
||||
//
|
||||
// Copyright (c) 2018 - present, Victor Zverovich and fmt contributors
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#ifndef FMT_COLOR_H_
|
||||
#define FMT_COLOR_H_
|
||||
|
||||
#include "format.h"
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
FMT_BEGIN_EXPORT
|
||||
|
||||
enum class color : uint32_t {
|
||||
alice_blue = 0xF0F8FF, // rgb(240,248,255)
|
||||
antique_white = 0xFAEBD7, // rgb(250,235,215)
|
||||
aqua = 0x00FFFF, // rgb(0,255,255)
|
||||
aquamarine = 0x7FFFD4, // rgb(127,255,212)
|
||||
azure = 0xF0FFFF, // rgb(240,255,255)
|
||||
beige = 0xF5F5DC, // rgb(245,245,220)
|
||||
bisque = 0xFFE4C4, // rgb(255,228,196)
|
||||
black = 0x000000, // rgb(0,0,0)
|
||||
blanched_almond = 0xFFEBCD, // rgb(255,235,205)
|
||||
blue = 0x0000FF, // rgb(0,0,255)
|
||||
blue_violet = 0x8A2BE2, // rgb(138,43,226)
|
||||
brown = 0xA52A2A, // rgb(165,42,42)
|
||||
burly_wood = 0xDEB887, // rgb(222,184,135)
|
||||
cadet_blue = 0x5F9EA0, // rgb(95,158,160)
|
||||
chartreuse = 0x7FFF00, // rgb(127,255,0)
|
||||
chocolate = 0xD2691E, // rgb(210,105,30)
|
||||
coral = 0xFF7F50, // rgb(255,127,80)
|
||||
cornflower_blue = 0x6495ED, // rgb(100,149,237)
|
||||
cornsilk = 0xFFF8DC, // rgb(255,248,220)
|
||||
crimson = 0xDC143C, // rgb(220,20,60)
|
||||
cyan = 0x00FFFF, // rgb(0,255,255)
|
||||
dark_blue = 0x00008B, // rgb(0,0,139)
|
||||
dark_cyan = 0x008B8B, // rgb(0,139,139)
|
||||
dark_golden_rod = 0xB8860B, // rgb(184,134,11)
|
||||
dark_gray = 0xA9A9A9, // rgb(169,169,169)
|
||||
dark_green = 0x006400, // rgb(0,100,0)
|
||||
dark_khaki = 0xBDB76B, // rgb(189,183,107)
|
||||
dark_magenta = 0x8B008B, // rgb(139,0,139)
|
||||
dark_olive_green = 0x556B2F, // rgb(85,107,47)
|
||||
dark_orange = 0xFF8C00, // rgb(255,140,0)
|
||||
dark_orchid = 0x9932CC, // rgb(153,50,204)
|
||||
dark_red = 0x8B0000, // rgb(139,0,0)
|
||||
dark_salmon = 0xE9967A, // rgb(233,150,122)
|
||||
dark_sea_green = 0x8FBC8F, // rgb(143,188,143)
|
||||
dark_slate_blue = 0x483D8B, // rgb(72,61,139)
|
||||
dark_slate_gray = 0x2F4F4F, // rgb(47,79,79)
|
||||
dark_turquoise = 0x00CED1, // rgb(0,206,209)
|
||||
dark_violet = 0x9400D3, // rgb(148,0,211)
|
||||
deep_pink = 0xFF1493, // rgb(255,20,147)
|
||||
deep_sky_blue = 0x00BFFF, // rgb(0,191,255)
|
||||
dim_gray = 0x696969, // rgb(105,105,105)
|
||||
dodger_blue = 0x1E90FF, // rgb(30,144,255)
|
||||
fire_brick = 0xB22222, // rgb(178,34,34)
|
||||
floral_white = 0xFFFAF0, // rgb(255,250,240)
|
||||
forest_green = 0x228B22, // rgb(34,139,34)
|
||||
fuchsia = 0xFF00FF, // rgb(255,0,255)
|
||||
gainsboro = 0xDCDCDC, // rgb(220,220,220)
|
||||
ghost_white = 0xF8F8FF, // rgb(248,248,255)
|
||||
gold = 0xFFD700, // rgb(255,215,0)
|
||||
golden_rod = 0xDAA520, // rgb(218,165,32)
|
||||
gray = 0x808080, // rgb(128,128,128)
|
||||
green = 0x008000, // rgb(0,128,0)
|
||||
green_yellow = 0xADFF2F, // rgb(173,255,47)
|
||||
honey_dew = 0xF0FFF0, // rgb(240,255,240)
|
||||
hot_pink = 0xFF69B4, // rgb(255,105,180)
|
||||
indian_red = 0xCD5C5C, // rgb(205,92,92)
|
||||
indigo = 0x4B0082, // rgb(75,0,130)
|
||||
ivory = 0xFFFFF0, // rgb(255,255,240)
|
||||
khaki = 0xF0E68C, // rgb(240,230,140)
|
||||
lavender = 0xE6E6FA, // rgb(230,230,250)
|
||||
lavender_blush = 0xFFF0F5, // rgb(255,240,245)
|
||||
lawn_green = 0x7CFC00, // rgb(124,252,0)
|
||||
lemon_chiffon = 0xFFFACD, // rgb(255,250,205)
|
||||
light_blue = 0xADD8E6, // rgb(173,216,230)
|
||||
light_coral = 0xF08080, // rgb(240,128,128)
|
||||
light_cyan = 0xE0FFFF, // rgb(224,255,255)
|
||||
light_golden_rod_yellow = 0xFAFAD2, // rgb(250,250,210)
|
||||
light_gray = 0xD3D3D3, // rgb(211,211,211)
|
||||
light_green = 0x90EE90, // rgb(144,238,144)
|
||||
light_pink = 0xFFB6C1, // rgb(255,182,193)
|
||||
light_salmon = 0xFFA07A, // rgb(255,160,122)
|
||||
light_sea_green = 0x20B2AA, // rgb(32,178,170)
|
||||
light_sky_blue = 0x87CEFA, // rgb(135,206,250)
|
||||
light_slate_gray = 0x778899, // rgb(119,136,153)
|
||||
light_steel_blue = 0xB0C4DE, // rgb(176,196,222)
|
||||
light_yellow = 0xFFFFE0, // rgb(255,255,224)
|
||||
lime = 0x00FF00, // rgb(0,255,0)
|
||||
lime_green = 0x32CD32, // rgb(50,205,50)
|
||||
linen = 0xFAF0E6, // rgb(250,240,230)
|
||||
magenta = 0xFF00FF, // rgb(255,0,255)
|
||||
maroon = 0x800000, // rgb(128,0,0)
|
||||
medium_aquamarine = 0x66CDAA, // rgb(102,205,170)
|
||||
medium_blue = 0x0000CD, // rgb(0,0,205)
|
||||
medium_orchid = 0xBA55D3, // rgb(186,85,211)
|
||||
medium_purple = 0x9370DB, // rgb(147,112,219)
|
||||
medium_sea_green = 0x3CB371, // rgb(60,179,113)
|
||||
medium_slate_blue = 0x7B68EE, // rgb(123,104,238)
|
||||
medium_spring_green = 0x00FA9A, // rgb(0,250,154)
|
||||
medium_turquoise = 0x48D1CC, // rgb(72,209,204)
|
||||
medium_violet_red = 0xC71585, // rgb(199,21,133)
|
||||
midnight_blue = 0x191970, // rgb(25,25,112)
|
||||
mint_cream = 0xF5FFFA, // rgb(245,255,250)
|
||||
misty_rose = 0xFFE4E1, // rgb(255,228,225)
|
||||
moccasin = 0xFFE4B5, // rgb(255,228,181)
|
||||
navajo_white = 0xFFDEAD, // rgb(255,222,173)
|
||||
navy = 0x000080, // rgb(0,0,128)
|
||||
old_lace = 0xFDF5E6, // rgb(253,245,230)
|
||||
olive = 0x808000, // rgb(128,128,0)
|
||||
olive_drab = 0x6B8E23, // rgb(107,142,35)
|
||||
orange = 0xFFA500, // rgb(255,165,0)
|
||||
orange_red = 0xFF4500, // rgb(255,69,0)
|
||||
orchid = 0xDA70D6, // rgb(218,112,214)
|
||||
pale_golden_rod = 0xEEE8AA, // rgb(238,232,170)
|
||||
pale_green = 0x98FB98, // rgb(152,251,152)
|
||||
pale_turquoise = 0xAFEEEE, // rgb(175,238,238)
|
||||
pale_violet_red = 0xDB7093, // rgb(219,112,147)
|
||||
papaya_whip = 0xFFEFD5, // rgb(255,239,213)
|
||||
peach_puff = 0xFFDAB9, // rgb(255,218,185)
|
||||
peru = 0xCD853F, // rgb(205,133,63)
|
||||
pink = 0xFFC0CB, // rgb(255,192,203)
|
||||
plum = 0xDDA0DD, // rgb(221,160,221)
|
||||
powder_blue = 0xB0E0E6, // rgb(176,224,230)
|
||||
purple = 0x800080, // rgb(128,0,128)
|
||||
rebecca_purple = 0x663399, // rgb(102,51,153)
|
||||
red = 0xFF0000, // rgb(255,0,0)
|
||||
rosy_brown = 0xBC8F8F, // rgb(188,143,143)
|
||||
royal_blue = 0x4169E1, // rgb(65,105,225)
|
||||
saddle_brown = 0x8B4513, // rgb(139,69,19)
|
||||
salmon = 0xFA8072, // rgb(250,128,114)
|
||||
sandy_brown = 0xF4A460, // rgb(244,164,96)
|
||||
sea_green = 0x2E8B57, // rgb(46,139,87)
|
||||
sea_shell = 0xFFF5EE, // rgb(255,245,238)
|
||||
sienna = 0xA0522D, // rgb(160,82,45)
|
||||
silver = 0xC0C0C0, // rgb(192,192,192)
|
||||
sky_blue = 0x87CEEB, // rgb(135,206,235)
|
||||
slate_blue = 0x6A5ACD, // rgb(106,90,205)
|
||||
slate_gray = 0x708090, // rgb(112,128,144)
|
||||
snow = 0xFFFAFA, // rgb(255,250,250)
|
||||
spring_green = 0x00FF7F, // rgb(0,255,127)
|
||||
steel_blue = 0x4682B4, // rgb(70,130,180)
|
||||
tan = 0xD2B48C, // rgb(210,180,140)
|
||||
teal = 0x008080, // rgb(0,128,128)
|
||||
thistle = 0xD8BFD8, // rgb(216,191,216)
|
||||
tomato = 0xFF6347, // rgb(255,99,71)
|
||||
turquoise = 0x40E0D0, // rgb(64,224,208)
|
||||
violet = 0xEE82EE, // rgb(238,130,238)
|
||||
wheat = 0xF5DEB3, // rgb(245,222,179)
|
||||
white = 0xFFFFFF, // rgb(255,255,255)
|
||||
white_smoke = 0xF5F5F5, // rgb(245,245,245)
|
||||
yellow = 0xFFFF00, // rgb(255,255,0)
|
||||
yellow_green = 0x9ACD32 // rgb(154,205,50)
|
||||
}; // enum class color
|
||||
|
||||
enum class terminal_color : uint8_t {
|
||||
black = 30,
|
||||
red,
|
||||
green,
|
||||
yellow,
|
||||
blue,
|
||||
magenta,
|
||||
cyan,
|
||||
white,
|
||||
bright_black = 90,
|
||||
bright_red,
|
||||
bright_green,
|
||||
bright_yellow,
|
||||
bright_blue,
|
||||
bright_magenta,
|
||||
bright_cyan,
|
||||
bright_white
|
||||
};
|
||||
|
||||
enum class emphasis : uint8_t {
|
||||
bold = 1,
|
||||
faint = 1 << 1,
|
||||
italic = 1 << 2,
|
||||
underline = 1 << 3,
|
||||
blink = 1 << 4,
|
||||
reverse = 1 << 5,
|
||||
conceal = 1 << 6,
|
||||
strikethrough = 1 << 7,
|
||||
};
|
||||
|
||||
// rgb is a struct for red, green and blue colors.
|
||||
// Using the name "rgb" makes some editors show the color in a tooltip.
|
||||
struct rgb {
|
||||
constexpr rgb() : r(0), g(0), b(0) {}
|
||||
constexpr rgb(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {}
|
||||
constexpr rgb(uint32_t hex)
|
||||
: r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b(hex & 0xFF) {}
|
||||
constexpr rgb(color hex)
|
||||
: r((uint32_t(hex) >> 16) & 0xFF),
|
||||
g((uint32_t(hex) >> 8) & 0xFF),
|
||||
b(uint32_t(hex) & 0xFF) {}
|
||||
uint8_t r;
|
||||
uint8_t g;
|
||||
uint8_t b;
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
|
||||
// A bit-packed variant of an RGB color, a terminal color, or unset color.
|
||||
// see text_style for the bit-packing scheme.
|
||||
struct color_type {
|
||||
constexpr color_type() noexcept = default;
|
||||
constexpr color_type(color rgb_color) noexcept
|
||||
: value_(static_cast<uint32_t>(rgb_color) | (1 << 24)) {}
|
||||
constexpr color_type(rgb rgb_color) noexcept
|
||||
: color_type(static_cast<color>(
|
||||
(static_cast<uint32_t>(rgb_color.r) << 16) |
|
||||
(static_cast<uint32_t>(rgb_color.g) << 8) | rgb_color.b)) {}
|
||||
constexpr color_type(terminal_color term_color) noexcept
|
||||
: value_(static_cast<uint32_t>(term_color) | (3 << 24)) {}
|
||||
|
||||
constexpr auto is_terminal_color() const noexcept -> bool {
|
||||
return (value_ & (1 << 25)) != 0;
|
||||
}
|
||||
|
||||
constexpr auto value() const noexcept -> uint32_t {
|
||||
return value_ & 0xFFFFFF;
|
||||
}
|
||||
|
||||
constexpr color_type(uint32_t value) noexcept : value_(value) {}
|
||||
|
||||
uint32_t value_ = 0;
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
/// A text style consisting of foreground and background colors and emphasis.
|
||||
class text_style {
|
||||
// The information is packed as follows:
|
||||
// ┌──┐
|
||||
// │ 0│─┐
|
||||
// │..│ ├── foreground color value
|
||||
// │23│─┘
|
||||
// ├──┤
|
||||
// │24│─┬── discriminator for the above value. 00 if unset, 01 if it's
|
||||
// │25│─┘ an RGB color, or 11 if it's a terminal color (10 is unused)
|
||||
// ├──┤
|
||||
// │26│──── overflow bit, always zero (see below)
|
||||
// ├──┤
|
||||
// │27│─┐
|
||||
// │..│ │
|
||||
// │50│ │
|
||||
// ├──┤ │
|
||||
// │51│ ├── background color (same format as the foreground color)
|
||||
// │52│ │
|
||||
// ├──┤ │
|
||||
// │53│─┘
|
||||
// ├──┤
|
||||
// │54│─┐
|
||||
// │..│ ├── emphases
|
||||
// │61│─┘
|
||||
// ├──┤
|
||||
// │62│─┬── unused
|
||||
// │63│─┘
|
||||
// └──┘
|
||||
// The overflow bits are there to make operator|= efficient.
|
||||
// When ORing, we must throw if, for either the foreground or background,
|
||||
// one style specifies a terminal color and the other specifies any color
|
||||
// (terminal or RGB); in other words, if one discriminator is 11 and the
|
||||
// other is 11 or 01.
|
||||
//
|
||||
// We do that check by adding the styles. Consider what adding does to each
|
||||
// possible pair of discriminators:
|
||||
// 00 + 00 = 000
|
||||
// 01 + 00 = 001
|
||||
// 11 + 00 = 011
|
||||
// 01 + 01 = 010
|
||||
// 11 + 01 = 100 (!!)
|
||||
// 11 + 11 = 110 (!!)
|
||||
// In the last two cases, the ones we want to catch, the third bit——the
|
||||
// overflow bit——is set. Bingo.
|
||||
//
|
||||
// We must take into account the possible carry bit from the bits
|
||||
// before the discriminator. The only potentially problematic case is
|
||||
// 11 + 00 = 011 (a carry bit would make it 100, not good!), but a carry
|
||||
// bit is impossible in that case, because 00 (unset color) means the
|
||||
// 24 bits that precede the discriminator are all zero.
|
||||
//
|
||||
// This test can be applied to both colors simultaneously.
|
||||
|
||||
public:
|
||||
FMT_CONSTEXPR text_style(emphasis em = emphasis()) noexcept
|
||||
: style_(static_cast<uint64_t>(em) << 54) {}
|
||||
|
||||
FMT_CONSTEXPR auto operator|=(text_style rhs) -> text_style& {
|
||||
if (((style_ + rhs.style_) & ((1ULL << 26) | (1ULL << 53))) != 0)
|
||||
report_error("can't OR a terminal color");
|
||||
style_ |= rhs.style_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
friend FMT_CONSTEXPR auto operator|(text_style lhs, text_style rhs)
|
||||
-> text_style {
|
||||
return lhs |= rhs;
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR auto operator==(text_style rhs) const noexcept -> bool {
|
||||
return style_ == rhs.style_;
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR auto operator!=(text_style rhs) const noexcept -> bool {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR auto has_foreground() const noexcept -> bool {
|
||||
return (style_ & (1 << 24)) != 0;
|
||||
}
|
||||
FMT_CONSTEXPR auto has_background() const noexcept -> bool {
|
||||
return (style_ & (1ULL << 51)) != 0;
|
||||
}
|
||||
FMT_CONSTEXPR auto has_emphasis() const noexcept -> bool {
|
||||
return (style_ >> 54) != 0;
|
||||
}
|
||||
FMT_CONSTEXPR auto get_foreground() const noexcept -> detail::color_type {
|
||||
FMT_ASSERT(has_foreground(), "no foreground specified for this style");
|
||||
return style_ & 0x3FFFFFF;
|
||||
}
|
||||
FMT_CONSTEXPR auto get_background() const noexcept -> detail::color_type {
|
||||
FMT_ASSERT(has_background(), "no background specified for this style");
|
||||
return (style_ >> 27) & 0x3FFFFFF;
|
||||
}
|
||||
FMT_CONSTEXPR auto get_emphasis() const noexcept -> emphasis {
|
||||
FMT_ASSERT(has_emphasis(), "no emphasis specified for this style");
|
||||
return static_cast<emphasis>(style_ >> 54);
|
||||
}
|
||||
|
||||
private:
|
||||
FMT_CONSTEXPR text_style(uint64_t style) noexcept : style_(style) {}
|
||||
|
||||
friend FMT_CONSTEXPR auto fg(detail::color_type foreground) noexcept
|
||||
-> text_style;
|
||||
|
||||
friend FMT_CONSTEXPR auto bg(detail::color_type background) noexcept
|
||||
-> text_style;
|
||||
|
||||
uint64_t style_ = 0;
|
||||
};
|
||||
|
||||
/// Creates a text style from the foreground (text) color.
|
||||
FMT_CONSTEXPR inline auto fg(detail::color_type foreground) noexcept
|
||||
-> text_style {
|
||||
return foreground.value_;
|
||||
}
|
||||
|
||||
/// Creates a text style from the background color.
|
||||
FMT_CONSTEXPR inline auto bg(detail::color_type background) noexcept
|
||||
-> text_style {
|
||||
return static_cast<uint64_t>(background.value_) << 27;
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR inline auto operator|(emphasis lhs, emphasis rhs) noexcept
|
||||
-> text_style {
|
||||
return text_style(lhs) | rhs;
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename Char> struct ansi_color_escape {
|
||||
FMT_CONSTEXPR ansi_color_escape(color_type text_color,
|
||||
const char* esc) noexcept {
|
||||
// If we have a terminal color, we need to output another escape code
|
||||
// sequence.
|
||||
if (text_color.is_terminal_color()) {
|
||||
bool is_background = esc == string_view("\x1b[48;2;");
|
||||
uint32_t value = text_color.value();
|
||||
// Background ASCII codes are the same as the foreground ones but with
|
||||
// 10 more.
|
||||
if (is_background) value += 10u;
|
||||
|
||||
buffer[size++] = static_cast<Char>('\x1b');
|
||||
buffer[size++] = static_cast<Char>('[');
|
||||
|
||||
if (value >= 100u) {
|
||||
buffer[size++] = static_cast<Char>('1');
|
||||
value %= 100u;
|
||||
}
|
||||
buffer[size++] = static_cast<Char>('0' + value / 10u);
|
||||
buffer[size++] = static_cast<Char>('0' + value % 10u);
|
||||
|
||||
buffer[size++] = static_cast<Char>('m');
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 7; i++) {
|
||||
buffer[i] = static_cast<Char>(esc[i]);
|
||||
}
|
||||
rgb color(text_color.value());
|
||||
to_esc(color.r, buffer + 7, ';');
|
||||
to_esc(color.g, buffer + 11, ';');
|
||||
to_esc(color.b, buffer + 15, 'm');
|
||||
size = 19;
|
||||
}
|
||||
FMT_CONSTEXPR ansi_color_escape(emphasis em) noexcept {
|
||||
uint8_t em_codes[num_emphases] = {};
|
||||
if (has_emphasis(em, emphasis::bold)) em_codes[0] = 1;
|
||||
if (has_emphasis(em, emphasis::faint)) em_codes[1] = 2;
|
||||
if (has_emphasis(em, emphasis::italic)) em_codes[2] = 3;
|
||||
if (has_emphasis(em, emphasis::underline)) em_codes[3] = 4;
|
||||
if (has_emphasis(em, emphasis::blink)) em_codes[4] = 5;
|
||||
if (has_emphasis(em, emphasis::reverse)) em_codes[5] = 7;
|
||||
if (has_emphasis(em, emphasis::conceal)) em_codes[6] = 8;
|
||||
if (has_emphasis(em, emphasis::strikethrough)) em_codes[7] = 9;
|
||||
|
||||
buffer[size++] = static_cast<Char>('\x1b');
|
||||
buffer[size++] = static_cast<Char>('[');
|
||||
|
||||
for (size_t i = 0; i < num_emphases; ++i) {
|
||||
if (!em_codes[i]) continue;
|
||||
buffer[size++] = static_cast<Char>('0' + em_codes[i]);
|
||||
buffer[size++] = static_cast<Char>(';');
|
||||
}
|
||||
|
||||
buffer[size - 1] = static_cast<Char>('m');
|
||||
}
|
||||
FMT_CONSTEXPR operator const Char*() const noexcept { return buffer; }
|
||||
|
||||
FMT_CONSTEXPR auto begin() const noexcept -> const Char* { return buffer; }
|
||||
FMT_CONSTEXPR auto end() const noexcept -> const Char* {
|
||||
return buffer + size;
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr size_t num_emphases = 8;
|
||||
Char buffer[7u + 4u * num_emphases] = {};
|
||||
size_t size = 0;
|
||||
|
||||
static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out,
|
||||
char delimiter) noexcept {
|
||||
out[0] = static_cast<Char>('0' + c / 100);
|
||||
out[1] = static_cast<Char>('0' + c / 10 % 10);
|
||||
out[2] = static_cast<Char>('0' + c % 10);
|
||||
out[3] = static_cast<Char>(delimiter);
|
||||
}
|
||||
static FMT_CONSTEXPR auto has_emphasis(emphasis em, emphasis mask) noexcept
|
||||
-> bool {
|
||||
return static_cast<uint8_t>(em) & static_cast<uint8_t>(mask);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char>
|
||||
FMT_CONSTEXPR auto make_foreground_color(color_type foreground) noexcept
|
||||
-> ansi_color_escape<Char> {
|
||||
return ansi_color_escape<Char>(foreground, "\x1b[38;2;");
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
FMT_CONSTEXPR auto make_background_color(color_type background) noexcept
|
||||
-> ansi_color_escape<Char> {
|
||||
return ansi_color_escape<Char>(background, "\x1b[48;2;");
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
FMT_CONSTEXPR auto make_emphasis(emphasis em) noexcept
|
||||
-> ansi_color_escape<Char> {
|
||||
return ansi_color_escape<Char>(em);
|
||||
}
|
||||
|
||||
template <typename Char> inline void reset_color(buffer<Char>& buffer) {
|
||||
auto reset_color = string_view("\x1b[0m");
|
||||
buffer.append(reset_color.begin(), reset_color.end());
|
||||
}
|
||||
|
||||
template <typename T> struct styled_arg : view {
|
||||
const T& value;
|
||||
text_style style;
|
||||
styled_arg(const T& v, text_style s) : value(v), style(s) {}
|
||||
};
|
||||
|
||||
template <typename Char>
|
||||
void vformat_to(buffer<Char>& buf, text_style ts, basic_string_view<Char> fmt,
|
||||
basic_format_args<buffered_context<Char>> args) {
|
||||
if (ts.has_emphasis()) {
|
||||
auto emphasis = make_emphasis<Char>(ts.get_emphasis());
|
||||
buf.append(emphasis.begin(), emphasis.end());
|
||||
}
|
||||
if (ts.has_foreground()) {
|
||||
auto foreground = make_foreground_color<Char>(ts.get_foreground());
|
||||
buf.append(foreground.begin(), foreground.end());
|
||||
}
|
||||
if (ts.has_background()) {
|
||||
auto background = make_background_color<Char>(ts.get_background());
|
||||
buf.append(background.begin(), background.end());
|
||||
}
|
||||
vformat_to(buf, fmt, args);
|
||||
if (ts != text_style()) reset_color<Char>(buf);
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
inline void vprint(FILE* f, text_style ts, string_view fmt, format_args args) {
|
||||
auto buf = memory_buffer();
|
||||
detail::vformat_to(buf, ts, fmt, args);
|
||||
print(f, FMT_STRING("{}"), string_view(buf.begin(), buf.size()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a string and prints it to the specified file stream using ANSI
|
||||
* escape sequences to specify text formatting.
|
||||
*
|
||||
* **Example**:
|
||||
*
|
||||
* fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
|
||||
* "Elapsed time: {0:.2f} seconds", 1.23);
|
||||
*/
|
||||
template <typename... T>
|
||||
void print(FILE* f, text_style ts, format_string<T...> fmt, T&&... args) {
|
||||
vprint(f, ts, fmt.str, vargs<T...>{{args...}});
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a string and prints it to stdout using ANSI escape sequences to
|
||||
* specify text formatting.
|
||||
*
|
||||
* **Example**:
|
||||
*
|
||||
* fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
|
||||
* "Elapsed time: {0:.2f} seconds", 1.23);
|
||||
*/
|
||||
template <typename... T>
|
||||
void print(text_style ts, format_string<T...> fmt, T&&... args) {
|
||||
return print(stdout, ts, fmt, std::forward<T>(args)...);
|
||||
}
|
||||
|
||||
inline auto vformat(text_style ts, string_view fmt, format_args args)
|
||||
-> std::string {
|
||||
auto buf = memory_buffer();
|
||||
detail::vformat_to(buf, ts, fmt, args);
|
||||
return fmt::to_string(buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats arguments and returns the result as a string using ANSI escape
|
||||
* sequences to specify text formatting.
|
||||
*
|
||||
* **Example**:
|
||||
*
|
||||
* ```
|
||||
* #include <fmt/color.h>
|
||||
* std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red),
|
||||
* "The answer is {}", 42);
|
||||
* ```
|
||||
*/
|
||||
template <typename... T>
|
||||
inline auto format(text_style ts, format_string<T...> fmt, T&&... args)
|
||||
-> std::string {
|
||||
return fmt::vformat(ts, fmt.str, vargs<T...>{{args...}});
|
||||
}
|
||||
|
||||
/// Formats a string with the given text_style and writes the output to `out`.
|
||||
template <typename OutputIt,
|
||||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
|
||||
auto vformat_to(OutputIt out, text_style ts, string_view fmt, format_args args)
|
||||
-> OutputIt {
|
||||
auto&& buf = detail::get_buffer<char>(out);
|
||||
detail::vformat_to(buf, ts, fmt, args);
|
||||
return detail::get_iterator(buf, out);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats arguments with the given text style, writes the result to the output
|
||||
* iterator `out` and returns the iterator past the end of the output range.
|
||||
*
|
||||
* **Example**:
|
||||
*
|
||||
* std::vector<char> out;
|
||||
* fmt::format_to(std::back_inserter(out),
|
||||
* fmt::emphasis::bold | fg(fmt::color::red), "{}", 42);
|
||||
*/
|
||||
template <typename OutputIt, typename... T,
|
||||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
|
||||
inline auto format_to(OutputIt out, text_style ts, format_string<T...> fmt,
|
||||
T&&... args) -> OutputIt {
|
||||
return vformat_to(out, ts, fmt.str, vargs<T...>{{args...}});
|
||||
}
|
||||
|
||||
template <typename T, typename Char>
|
||||
struct formatter<detail::styled_arg<T>, Char> : formatter<T, Char> {
|
||||
template <typename FormatContext>
|
||||
auto format(const detail::styled_arg<T>& arg, FormatContext& ctx) const
|
||||
-> decltype(ctx.out()) {
|
||||
const auto& ts = arg.style;
|
||||
auto out = ctx.out();
|
||||
|
||||
bool has_style = false;
|
||||
if (ts.has_emphasis()) {
|
||||
has_style = true;
|
||||
auto emphasis = detail::make_emphasis<Char>(ts.get_emphasis());
|
||||
out = detail::copy<Char>(emphasis.begin(), emphasis.end(), out);
|
||||
}
|
||||
if (ts.has_foreground()) {
|
||||
has_style = true;
|
||||
auto foreground =
|
||||
detail::make_foreground_color<Char>(ts.get_foreground());
|
||||
out = detail::copy<Char>(foreground.begin(), foreground.end(), out);
|
||||
}
|
||||
if (ts.has_background()) {
|
||||
has_style = true;
|
||||
auto background =
|
||||
detail::make_background_color<Char>(ts.get_background());
|
||||
out = detail::copy<Char>(background.begin(), background.end(), out);
|
||||
}
|
||||
out = formatter<T, Char>::format(arg.value, ctx);
|
||||
if (has_style) {
|
||||
auto reset_color = string_view("\x1b[0m");
|
||||
out = detail::copy<Char>(reset_color.begin(), reset_color.end(), out);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns an argument that will be formatted using ANSI escape sequences,
|
||||
* to be used in a formatting function.
|
||||
*
|
||||
* **Example**:
|
||||
*
|
||||
* fmt::print("Elapsed time: {0:.2f} seconds",
|
||||
* fmt::styled(1.23, fmt::fg(fmt::color::green) |
|
||||
* fmt::bg(fmt::color::blue)));
|
||||
*/
|
||||
template <typename T>
|
||||
FMT_CONSTEXPR auto styled(const T& value, text_style ts)
|
||||
-> detail::styled_arg<remove_cvref_t<T>> {
|
||||
return detail::styled_arg<remove_cvref_t<T>>{value, ts};
|
||||
}
|
||||
|
||||
FMT_END_EXPORT
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#endif // FMT_COLOR_H_
|
||||
@@ -0,0 +1,588 @@
|
||||
// Formatting library for C++ - experimental format string compilation
|
||||
//
|
||||
// Copyright (c) 2012 - present, Victor Zverovich and fmt contributors
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#ifndef FMT_COMPILE_H_
|
||||
#define FMT_COMPILE_H_
|
||||
|
||||
#ifndef FMT_MODULE
|
||||
# include <iterator> // std::back_inserter
|
||||
#endif
|
||||
|
||||
#include "format.h"
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
FMT_BEGIN_EXPORT
|
||||
|
||||
// A compile-time string which is compiled into fast formatting code.
|
||||
class compiled_string {};
|
||||
|
||||
template <typename S>
|
||||
struct is_compiled_string : std::is_base_of<compiled_string, S> {};
|
||||
|
||||
/**
|
||||
* Converts a string literal `s` into a format string that will be parsed at
|
||||
* compile time and converted into efficient formatting code. Requires C++17
|
||||
* `constexpr if` compiler support.
|
||||
*
|
||||
* **Example**:
|
||||
*
|
||||
* // Converts 42 into std::string using the most efficient method and no
|
||||
* // runtime format string processing.
|
||||
* std::string s = fmt::format(FMT_COMPILE("{}"), 42);
|
||||
*/
|
||||
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
|
||||
# define FMT_COMPILE(s) FMT_STRING_IMPL(s, fmt::compiled_string)
|
||||
#else
|
||||
# define FMT_COMPILE(s) FMT_STRING(s)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Converts a string literal into a format string that will be parsed at
|
||||
* compile time and converted into efficient formatting code. Requires support
|
||||
* for class types in constant template parameters (a C++20 feature).
|
||||
*
|
||||
* **Example**:
|
||||
*
|
||||
* // Converts 42 into std::string using the most efficient method and no
|
||||
* // runtime format string processing.
|
||||
* using namespace fmt::literals;
|
||||
* std::string s = fmt::format("{}"_cf, 42);
|
||||
*/
|
||||
#if FMT_USE_NONTYPE_TEMPLATE_ARGS
|
||||
inline namespace literals {
|
||||
template <detail::fixed_string Str> constexpr auto operator""_cf() {
|
||||
return FMT_COMPILE(Str.data);
|
||||
}
|
||||
} // namespace literals
|
||||
#endif
|
||||
|
||||
FMT_END_EXPORT
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename T, typename... Tail>
|
||||
constexpr auto first(const T& value, const Tail&...) -> const T& {
|
||||
return value;
|
||||
}
|
||||
|
||||
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
|
||||
template <typename... T> struct type_list {};
|
||||
|
||||
// Returns a reference to the argument at index N from [first, rest...].
|
||||
template <int N, typename T, typename... Args>
|
||||
constexpr auto get([[maybe_unused]] const T& first,
|
||||
[[maybe_unused]] const Args&... rest) -> const auto& {
|
||||
static_assert(N < 1 + sizeof...(Args), "index is out of bounds");
|
||||
if constexpr (N == 0)
|
||||
return first;
|
||||
else
|
||||
return detail::get<N - 1>(rest...);
|
||||
}
|
||||
|
||||
# if FMT_USE_NONTYPE_TEMPLATE_ARGS
|
||||
template <int N, typename T, typename... Args, typename Char>
|
||||
constexpr auto get_arg_index_by_name(basic_string_view<Char> name) -> int {
|
||||
if constexpr (is_static_named_arg<T>()) {
|
||||
if (name == T::name) return N;
|
||||
}
|
||||
if constexpr (sizeof...(Args) > 0)
|
||||
return get_arg_index_by_name<N + 1, Args...>(name);
|
||||
(void)name; // Workaround an MSVC bug about "unused" parameter.
|
||||
return -1;
|
||||
}
|
||||
# endif
|
||||
|
||||
template <typename... Args, typename Char>
|
||||
FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view<Char> name) -> int {
|
||||
# if FMT_USE_NONTYPE_TEMPLATE_ARGS
|
||||
if constexpr (sizeof...(Args) > 0)
|
||||
return get_arg_index_by_name<0, Args...>(name);
|
||||
# endif
|
||||
(void)name;
|
||||
return -1;
|
||||
}
|
||||
|
||||
template <typename Char, typename... Args>
|
||||
constexpr auto get_arg_index_by_name(basic_string_view<Char> name,
|
||||
type_list<Args...>) -> int {
|
||||
return get_arg_index_by_name<Args...>(name);
|
||||
}
|
||||
|
||||
template <int N, typename> struct get_type_impl;
|
||||
|
||||
template <int N, typename... Args> struct get_type_impl<N, type_list<Args...>> {
|
||||
using type =
|
||||
remove_cvref_t<decltype(detail::get<N>(std::declval<Args>()...))>;
|
||||
};
|
||||
|
||||
template <int N, typename T>
|
||||
using get_type = typename get_type_impl<N, T>::type;
|
||||
|
||||
template <typename T> struct is_compiled_format : std::false_type {};
|
||||
|
||||
template <typename Char> struct text {
|
||||
basic_string_view<Char> data;
|
||||
using char_type = Char;
|
||||
|
||||
template <typename OutputIt, typename... T>
|
||||
constexpr auto format(OutputIt out, const T&...) const -> OutputIt {
|
||||
return write<Char>(out, data);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char>
|
||||
struct is_compiled_format<text<Char>> : std::true_type {};
|
||||
|
||||
template <typename Char>
|
||||
constexpr auto make_text(basic_string_view<Char> s, size_t pos, size_t size)
|
||||
-> text<Char> {
|
||||
return {{&s[pos], size}};
|
||||
}
|
||||
|
||||
template <typename Char> struct code_unit {
|
||||
Char value;
|
||||
using char_type = Char;
|
||||
|
||||
template <typename OutputIt, typename... T>
|
||||
constexpr auto format(OutputIt out, const T&...) const -> OutputIt {
|
||||
*out++ = value;
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
// This ensures that the argument type is convertible to `const T&`.
|
||||
template <typename T, int N, typename... Args>
|
||||
constexpr auto get_arg_checked(const Args&... args) -> const T& {
|
||||
const auto& arg = detail::get<N>(args...);
|
||||
if constexpr (detail::is_named_arg<remove_cvref_t<decltype(arg)>>()) {
|
||||
return arg.value;
|
||||
} else {
|
||||
return arg;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
struct is_compiled_format<code_unit<Char>> : std::true_type {};
|
||||
|
||||
// A replacement field that refers to argument N.
|
||||
template <typename Char, typename V, int N> struct field {
|
||||
using char_type = Char;
|
||||
|
||||
template <typename OutputIt, typename... T>
|
||||
constexpr auto format(OutputIt out, const T&... args) const -> OutputIt {
|
||||
const V& arg = get_arg_checked<V, N>(args...);
|
||||
if constexpr (std::is_convertible<V, basic_string_view<Char>>::value) {
|
||||
auto s = basic_string_view<Char>(arg);
|
||||
return copy<Char>(s.begin(), s.end(), out);
|
||||
} else {
|
||||
return write<Char>(out, arg);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char, typename T, int N>
|
||||
struct is_compiled_format<field<Char, T, N>> : std::true_type {};
|
||||
|
||||
// A replacement field that refers to argument with name.
|
||||
template <typename Char> struct runtime_named_field {
|
||||
using char_type = Char;
|
||||
basic_string_view<Char> name;
|
||||
|
||||
template <typename OutputIt, typename T>
|
||||
constexpr static auto try_format_argument(
|
||||
OutputIt& out,
|
||||
// [[maybe_unused]] due to unused-but-set-parameter warning in GCC 7,8,9
|
||||
[[maybe_unused]] basic_string_view<Char> arg_name, const T& arg) -> bool {
|
||||
if constexpr (is_named_arg<typename std::remove_cv<T>::type>::value) {
|
||||
if (arg_name == arg.name) {
|
||||
out = write<Char>(out, arg.value);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename... T>
|
||||
constexpr auto format(OutputIt out, const T&... args) const -> OutputIt {
|
||||
bool found = (try_format_argument(out, name, args) || ...);
|
||||
if (!found) {
|
||||
FMT_THROW(format_error("argument with specified name is not found"));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char>
|
||||
struct is_compiled_format<runtime_named_field<Char>> : std::true_type {};
|
||||
|
||||
// A replacement field that refers to argument N and has format specifiers.
|
||||
template <typename Char, typename V, int N> struct spec_field {
|
||||
using char_type = Char;
|
||||
formatter<V, Char> fmt;
|
||||
|
||||
template <typename OutputIt, typename... T>
|
||||
constexpr FMT_INLINE auto format(OutputIt out, const T&... args) const
|
||||
-> OutputIt {
|
||||
const auto& vargs =
|
||||
fmt::make_format_args<basic_format_context<OutputIt, Char>>(args...);
|
||||
basic_format_context<OutputIt, Char> ctx(out, vargs);
|
||||
return fmt.format(get_arg_checked<V, N>(args...), ctx);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char, typename T, int N>
|
||||
struct is_compiled_format<spec_field<Char, T, N>> : std::true_type {};
|
||||
|
||||
template <typename L, typename R> struct concat {
|
||||
L lhs;
|
||||
R rhs;
|
||||
using char_type = typename L::char_type;
|
||||
|
||||
template <typename OutputIt, typename... T>
|
||||
constexpr auto format(OutputIt out, const T&... args) const -> OutputIt {
|
||||
out = lhs.format(out, args...);
|
||||
return rhs.format(out, args...);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename L, typename R>
|
||||
struct is_compiled_format<concat<L, R>> : std::true_type {};
|
||||
|
||||
template <typename L, typename R>
|
||||
constexpr auto make_concat(L lhs, R rhs) -> concat<L, R> {
|
||||
return {lhs, rhs};
|
||||
}
|
||||
|
||||
struct unknown_format {};
|
||||
|
||||
template <typename Char>
|
||||
constexpr auto parse_text(basic_string_view<Char> str, size_t pos) -> size_t {
|
||||
for (size_t size = str.size(); pos != size; ++pos) {
|
||||
if (str[pos] == '{' || str[pos] == '}') break;
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
template <typename Args, size_t POS, int ID, typename S>
|
||||
constexpr auto compile_format_string(S fmt);
|
||||
|
||||
template <typename Args, size_t POS, int ID, typename T, typename S>
|
||||
constexpr auto parse_tail(T head, S fmt) {
|
||||
if constexpr (POS != basic_string_view<typename S::char_type>(fmt).size()) {
|
||||
constexpr auto tail = compile_format_string<Args, POS, ID>(fmt);
|
||||
if constexpr (std::is_same<remove_cvref_t<decltype(tail)>,
|
||||
unknown_format>())
|
||||
return tail;
|
||||
else
|
||||
return make_concat(head, tail);
|
||||
} else {
|
||||
return head;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename Char> struct parse_specs_result {
|
||||
formatter<T, Char> fmt;
|
||||
size_t end;
|
||||
int next_arg_id;
|
||||
};
|
||||
|
||||
enum { manual_indexing_id = -1 };
|
||||
|
||||
template <typename T, typename Char>
|
||||
constexpr auto parse_specs(basic_string_view<Char> str, size_t pos,
|
||||
int next_arg_id) -> parse_specs_result<T, Char> {
|
||||
str.remove_prefix(pos);
|
||||
auto ctx =
|
||||
compile_parse_context<Char>(str, max_value<int>(), nullptr, next_arg_id);
|
||||
auto f = formatter<T, Char>();
|
||||
auto end = f.parse(ctx);
|
||||
return {f, pos + fmt::detail::to_unsigned(end - str.data()),
|
||||
next_arg_id == 0 ? manual_indexing_id : ctx.next_arg_id()};
|
||||
}
|
||||
|
||||
template <typename Char> struct arg_id_handler {
|
||||
arg_id_kind kind;
|
||||
arg_ref<Char> arg_id;
|
||||
|
||||
constexpr auto on_auto() -> int {
|
||||
FMT_ASSERT(false, "handler cannot be used with automatic indexing");
|
||||
return 0;
|
||||
}
|
||||
constexpr auto on_index(int id) -> int {
|
||||
kind = arg_id_kind::index;
|
||||
arg_id = arg_ref<Char>(id);
|
||||
return 0;
|
||||
}
|
||||
constexpr auto on_name(basic_string_view<Char> id) -> int {
|
||||
kind = arg_id_kind::name;
|
||||
arg_id = arg_ref<Char>(id);
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char> struct parse_arg_id_result {
|
||||
arg_id_kind kind;
|
||||
arg_ref<Char> arg_id;
|
||||
const Char* arg_id_end;
|
||||
};
|
||||
|
||||
template <int ID, typename Char>
|
||||
constexpr auto parse_arg_id(const Char* begin, const Char* end) {
|
||||
auto handler = arg_id_handler<Char>{arg_id_kind::none, arg_ref<Char>{}};
|
||||
auto arg_id_end = parse_arg_id(begin, end, handler);
|
||||
return parse_arg_id_result<Char>{handler.kind, handler.arg_id, arg_id_end};
|
||||
}
|
||||
|
||||
template <typename T, typename Enable = void> struct field_type {
|
||||
using type = remove_cvref_t<T>;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct field_type<T, enable_if_t<detail::is_named_arg<T>::value>> {
|
||||
using type = remove_cvref_t<decltype(T::value)>;
|
||||
};
|
||||
|
||||
template <typename T, typename Args, size_t END_POS, int ARG_INDEX, int NEXT_ID,
|
||||
typename S>
|
||||
constexpr auto parse_replacement_field_then_tail(S fmt) {
|
||||
using char_type = typename S::char_type;
|
||||
constexpr auto str = basic_string_view<char_type>(fmt);
|
||||
constexpr char_type c = END_POS != str.size() ? str[END_POS] : char_type();
|
||||
if constexpr (c == '}') {
|
||||
return parse_tail<Args, END_POS + 1, NEXT_ID>(
|
||||
field<char_type, typename field_type<T>::type, ARG_INDEX>(), fmt);
|
||||
} else if constexpr (c != ':') {
|
||||
FMT_THROW(format_error("expected ':'"));
|
||||
} else {
|
||||
constexpr auto result = parse_specs<typename field_type<T>::type>(
|
||||
str, END_POS + 1, NEXT_ID == manual_indexing_id ? 0 : NEXT_ID);
|
||||
if constexpr (result.end >= str.size() || str[result.end] != '}') {
|
||||
FMT_THROW(format_error("expected '}'"));
|
||||
return 0;
|
||||
} else {
|
||||
return parse_tail<Args, result.end + 1, result.next_arg_id>(
|
||||
spec_field<char_type, typename field_type<T>::type, ARG_INDEX>{
|
||||
result.fmt},
|
||||
fmt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compiles a non-empty format string and returns the compiled representation
|
||||
// or unknown_format() on unrecognized input.
|
||||
template <typename Args, size_t POS, int ID, typename S>
|
||||
constexpr auto compile_format_string(S fmt) {
|
||||
using char_type = typename S::char_type;
|
||||
constexpr auto str = basic_string_view<char_type>(fmt);
|
||||
if constexpr (str[POS] == '{') {
|
||||
if constexpr (POS + 1 == str.size())
|
||||
FMT_THROW(format_error("unmatched '{' in format string"));
|
||||
if constexpr (str[POS + 1] == '{') {
|
||||
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), fmt);
|
||||
} else if constexpr (str[POS + 1] == '}' || str[POS + 1] == ':') {
|
||||
static_assert(ID != manual_indexing_id,
|
||||
"cannot switch from manual to automatic argument indexing");
|
||||
constexpr auto next_id =
|
||||
ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
|
||||
return parse_replacement_field_then_tail<get_type<ID, Args>, Args,
|
||||
POS + 1, ID, next_id>(fmt);
|
||||
} else {
|
||||
constexpr auto arg_id_result =
|
||||
parse_arg_id<ID>(str.data() + POS + 1, str.data() + str.size());
|
||||
constexpr auto arg_id_end_pos = arg_id_result.arg_id_end - str.data();
|
||||
constexpr char_type c =
|
||||
arg_id_end_pos != str.size() ? str[arg_id_end_pos] : char_type();
|
||||
static_assert(c == '}' || c == ':', "missing '}' in format string");
|
||||
if constexpr (arg_id_result.kind == arg_id_kind::index) {
|
||||
static_assert(
|
||||
ID == manual_indexing_id || ID == 0,
|
||||
"cannot switch from automatic to manual argument indexing");
|
||||
constexpr auto arg_index = arg_id_result.arg_id.index;
|
||||
return parse_replacement_field_then_tail<get_type<arg_index, Args>,
|
||||
Args, arg_id_end_pos,
|
||||
arg_index, manual_indexing_id>(
|
||||
fmt);
|
||||
} else if constexpr (arg_id_result.kind == arg_id_kind::name) {
|
||||
constexpr auto arg_index =
|
||||
get_arg_index_by_name(arg_id_result.arg_id.name, Args{});
|
||||
if constexpr (arg_index >= 0) {
|
||||
constexpr auto next_id =
|
||||
ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
|
||||
return parse_replacement_field_then_tail<
|
||||
decltype(get_type<arg_index, Args>::value), Args, arg_id_end_pos,
|
||||
arg_index, next_id>(fmt);
|
||||
} else if constexpr (c == '}') {
|
||||
return parse_tail<Args, arg_id_end_pos + 1, ID>(
|
||||
runtime_named_field<char_type>{arg_id_result.arg_id.name}, fmt);
|
||||
} else if constexpr (c == ':') {
|
||||
return unknown_format(); // no type info for specs parsing
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if constexpr (str[POS] == '}') {
|
||||
if constexpr (POS + 1 == str.size())
|
||||
FMT_THROW(format_error("unmatched '}' in format string"));
|
||||
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), fmt);
|
||||
} else {
|
||||
constexpr auto end = parse_text(str, POS + 1);
|
||||
if constexpr (end - POS > 1) {
|
||||
return parse_tail<Args, end, ID>(make_text(str, POS, end - POS), fmt);
|
||||
} else {
|
||||
return parse_tail<Args, end, ID>(code_unit<char_type>{str[POS]}, fmt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename... Args, typename S,
|
||||
FMT_ENABLE_IF(is_compiled_string<S>::value)>
|
||||
constexpr auto compile(S fmt) {
|
||||
constexpr auto str = basic_string_view<typename S::char_type>(fmt);
|
||||
if constexpr (str.size() == 0) {
|
||||
return detail::make_text(str, 0, 0);
|
||||
} else {
|
||||
constexpr auto result =
|
||||
detail::compile_format_string<detail::type_list<Args...>, 0, 0>(fmt);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
#endif // defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
|
||||
} // namespace detail
|
||||
|
||||
FMT_BEGIN_EXPORT
|
||||
|
||||
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
|
||||
|
||||
template <typename CompiledFormat, typename... T,
|
||||
typename Char = typename CompiledFormat::char_type,
|
||||
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
|
||||
FMT_INLINE FMT_CONSTEXPR_STRING auto format(const CompiledFormat& cf,
|
||||
const T&... args)
|
||||
-> std::basic_string<Char> {
|
||||
auto s = std::basic_string<Char>();
|
||||
cf.format(std::back_inserter(s), args...);
|
||||
return s;
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename CompiledFormat, typename... T,
|
||||
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
|
||||
constexpr FMT_INLINE auto format_to(OutputIt out, const CompiledFormat& cf,
|
||||
const T&... args) -> OutputIt {
|
||||
return cf.format(out, args...);
|
||||
}
|
||||
|
||||
template <typename S, typename... T,
|
||||
FMT_ENABLE_IF(is_compiled_string<S>::value)>
|
||||
FMT_INLINE FMT_CONSTEXPR_STRING auto format(const S&, T&&... args)
|
||||
-> std::basic_string<typename S::char_type> {
|
||||
if constexpr (std::is_same<typename S::char_type, char>::value) {
|
||||
constexpr auto str = basic_string_view<typename S::char_type>(S());
|
||||
if constexpr (str.size() == 2 && str[0] == '{' && str[1] == '}') {
|
||||
const auto& first = detail::first(args...);
|
||||
if constexpr (detail::is_named_arg<
|
||||
remove_cvref_t<decltype(first)>>::value) {
|
||||
return fmt::to_string(first.value);
|
||||
} else {
|
||||
return fmt::to_string(first);
|
||||
}
|
||||
}
|
||||
}
|
||||
constexpr auto compiled = detail::compile<T...>(S());
|
||||
if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
|
||||
detail::unknown_format>()) {
|
||||
return fmt::format(
|
||||
static_cast<basic_string_view<typename S::char_type>>(S()),
|
||||
std::forward<T>(args)...);
|
||||
} else {
|
||||
return fmt::format(compiled, std::forward<T>(args)...);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename S, typename... T,
|
||||
FMT_ENABLE_IF(is_compiled_string<S>::value)>
|
||||
FMT_CONSTEXPR auto format_to(OutputIt out, const S&, T&&... args) -> OutputIt {
|
||||
constexpr auto compiled = detail::compile<T...>(S());
|
||||
if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
|
||||
detail::unknown_format>()) {
|
||||
return fmt::format_to(
|
||||
out, static_cast<basic_string_view<typename S::char_type>>(S()),
|
||||
std::forward<T>(args)...);
|
||||
} else {
|
||||
return fmt::format_to(out, compiled, std::forward<T>(args)...);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
template <typename OutputIt, typename S, typename... T,
|
||||
FMT_ENABLE_IF(is_compiled_string<S>::value)>
|
||||
auto format_to_n(OutputIt out, size_t n, const S& fmt, T&&... args)
|
||||
-> format_to_n_result<OutputIt> {
|
||||
using traits = detail::fixed_buffer_traits;
|
||||
auto buf = detail::iterator_buffer<OutputIt, char, traits>(out, n);
|
||||
fmt::format_to(std::back_inserter(buf), fmt, std::forward<T>(args)...);
|
||||
return {buf.out(), buf.count()};
|
||||
}
|
||||
|
||||
template <typename S, typename... T,
|
||||
FMT_ENABLE_IF(is_compiled_string<S>::value)>
|
||||
FMT_CONSTEXPR20 auto formatted_size(const S& fmt, T&&... args) -> size_t {
|
||||
auto buf = detail::counting_buffer<>();
|
||||
fmt::format_to(appender(buf), fmt, std::forward<T>(args)...);
|
||||
return buf.count();
|
||||
}
|
||||
|
||||
template <typename S, typename... T,
|
||||
FMT_ENABLE_IF(is_compiled_string<S>::value)>
|
||||
void print(std::FILE* f, const S& fmt, T&&... args) {
|
||||
auto buf = memory_buffer();
|
||||
fmt::format_to(appender(buf), fmt, std::forward<T>(args)...);
|
||||
detail::print(f, {buf.data(), buf.size()});
|
||||
}
|
||||
|
||||
template <typename S, typename... T,
|
||||
FMT_ENABLE_IF(is_compiled_string<S>::value)>
|
||||
void print(const S& fmt, T&&... args) {
|
||||
print(stdout, fmt, std::forward<T>(args)...);
|
||||
}
|
||||
|
||||
template <size_t N> class static_format_result {
|
||||
private:
|
||||
char data[N];
|
||||
|
||||
public:
|
||||
template <typename S, typename... T,
|
||||
FMT_ENABLE_IF(is_compiled_string<S>::value)>
|
||||
explicit FMT_CONSTEXPR static_format_result(const S& fmt, T&&... args) {
|
||||
*fmt::format_to(data, fmt, std::forward<T>(args)...) = '\0';
|
||||
}
|
||||
|
||||
auto str() const -> fmt::string_view { return {data, N - 1}; }
|
||||
auto c_str() const -> const char* { return data; }
|
||||
};
|
||||
|
||||
/**
|
||||
* Formats arguments according to the format string `fmt_str` and produces
|
||||
* a string of the exact required size at compile time. Both the format string
|
||||
* and the arguments must be compile-time expressions.
|
||||
*
|
||||
* The resulting string can be accessed as a C string via `c_str()` or as
|
||||
* a `fmt::string_view` via `str()`.
|
||||
*
|
||||
* **Example**:
|
||||
*
|
||||
* // Produces the static string "42" at compile time.
|
||||
* static constexpr auto result = FMT_STATIC_FORMAT("{}", 42);
|
||||
* const char* s = result.c_str();
|
||||
*/
|
||||
#define FMT_STATIC_FORMAT(fmt_str, ...) \
|
||||
fmt::static_format_result< \
|
||||
fmt::formatted_size(FMT_COMPILE(fmt_str), __VA_ARGS__) + 1>( \
|
||||
FMT_COMPILE(fmt_str), __VA_ARGS__)
|
||||
|
||||
FMT_END_EXPORT
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#endif // FMT_COMPILE_H_
|
||||
@@ -0,0 +1,5 @@
|
||||
// This file is only provided for compatibility and may be removed in future
|
||||
// versions. Use fmt/base.h if you don't need fmt::format and fmt/format.h
|
||||
// otherwise.
|
||||
|
||||
#include "format.h"
|
||||
@@ -0,0 +1,27 @@
|
||||
Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
--- Optional exception to the license ---
|
||||
|
||||
As an exception, if, as a result of your compiling your source code, portions
|
||||
of this Software are embedded into a machine-executable object form of such
|
||||
source code, you may redistribute such embedded portions in such object form
|
||||
without including the above copyright and permission notices.
|
||||
File diff suppressed because it is too large
Load Diff
+4395
File diff suppressed because it is too large
Load Diff
+427
@@ -0,0 +1,427 @@
|
||||
// Formatting library for C++ - optional OS-specific functionality
|
||||
//
|
||||
// Copyright (c) 2012 - present, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#ifndef FMT_OS_H_
|
||||
#define FMT_OS_H_
|
||||
|
||||
#include "format.h"
|
||||
|
||||
#ifndef FMT_MODULE
|
||||
# include <cerrno>
|
||||
# include <cstddef>
|
||||
# include <cstdio>
|
||||
# include <system_error> // std::system_error
|
||||
|
||||
# if FMT_HAS_INCLUDE(<xlocale.h>)
|
||||
# include <xlocale.h> // LC_NUMERIC_MASK on macOS
|
||||
# endif
|
||||
#endif // FMT_MODULE
|
||||
|
||||
#ifndef FMT_USE_FCNTL
|
||||
// UWP doesn't provide _pipe.
|
||||
# if FMT_HAS_INCLUDE("winapifamily.h")
|
||||
# include <winapifamily.h>
|
||||
# endif
|
||||
# if (FMT_HAS_INCLUDE(<fcntl.h>) || defined(__APPLE__) || \
|
||||
defined(__linux__)) && \
|
||||
(!defined(WINAPI_FAMILY) || \
|
||||
(WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)) && \
|
||||
!defined(__wasm__)
|
||||
# include <fcntl.h> // for O_RDONLY
|
||||
# define FMT_USE_FCNTL 1
|
||||
# else
|
||||
# define FMT_USE_FCNTL 0
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef FMT_POSIX
|
||||
# if defined(_WIN32) && !defined(__MINGW32__)
|
||||
// Fix warnings about deprecated symbols.
|
||||
# define FMT_POSIX(call) _##call
|
||||
# else
|
||||
# define FMT_POSIX(call) call
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// Calls to system functions are wrapped in FMT_SYSTEM for testability.
|
||||
#ifdef FMT_SYSTEM
|
||||
# define FMT_HAS_SYSTEM
|
||||
# define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
|
||||
#else
|
||||
# define FMT_SYSTEM(call) ::call
|
||||
# ifdef _WIN32
|
||||
// Fix warnings about deprecated symbols.
|
||||
# define FMT_POSIX_CALL(call) ::_##call
|
||||
# else
|
||||
# define FMT_POSIX_CALL(call) ::call
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// Retries the expression while it evaluates to error_result and errno
|
||||
// equals to EINTR.
|
||||
#ifndef _WIN32
|
||||
# define FMT_RETRY_VAL(result, expression, error_result) \
|
||||
do { \
|
||||
(result) = (expression); \
|
||||
} while ((result) == (error_result) && errno == EINTR)
|
||||
#else
|
||||
# define FMT_RETRY_VAL(result, expression, error_result) result = (expression)
|
||||
#endif
|
||||
|
||||
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
FMT_BEGIN_EXPORT
|
||||
|
||||
/**
|
||||
* A reference to a null-terminated string. It can be constructed from a C
|
||||
* string or `std::string`.
|
||||
*
|
||||
* You can use one of the following type aliases for common character types:
|
||||
*
|
||||
* +---------------+-----------------------------+
|
||||
* | Type | Definition |
|
||||
* +===============+=============================+
|
||||
* | cstring_view | basic_cstring_view<char> |
|
||||
* +---------------+-----------------------------+
|
||||
* | wcstring_view | basic_cstring_view<wchar_t> |
|
||||
* +---------------+-----------------------------+
|
||||
*
|
||||
* This class is most useful as a parameter type for functions that wrap C APIs.
|
||||
*/
|
||||
template <typename Char> class basic_cstring_view {
|
||||
private:
|
||||
const Char* data_;
|
||||
|
||||
public:
|
||||
/// Constructs a string reference object from a C string.
|
||||
basic_cstring_view(const Char* s) : data_(s) {}
|
||||
|
||||
/// Constructs a string reference from an `std::string` object.
|
||||
basic_cstring_view(const std::basic_string<Char>& s) : data_(s.c_str()) {}
|
||||
|
||||
/// Returns the pointer to a C string.
|
||||
auto c_str() const -> const Char* { return data_; }
|
||||
};
|
||||
|
||||
using cstring_view = basic_cstring_view<char>;
|
||||
using wcstring_view = basic_cstring_view<wchar_t>;
|
||||
|
||||
#ifdef _WIN32
|
||||
FMT_API const std::error_category& system_category() noexcept;
|
||||
|
||||
namespace detail {
|
||||
FMT_API void format_windows_error(buffer<char>& out, int error_code,
|
||||
const char* message) noexcept;
|
||||
}
|
||||
|
||||
FMT_API std::system_error vwindows_error(int error_code, string_view fmt,
|
||||
format_args args);
|
||||
|
||||
/**
|
||||
* Constructs a `std::system_error` object with the description of the form
|
||||
*
|
||||
* <message>: <system-message>
|
||||
*
|
||||
* where `<message>` is the formatted message and `<system-message>` is the
|
||||
* system message corresponding to the error code.
|
||||
* `error_code` is a Windows error code as given by `GetLastError`.
|
||||
* If `error_code` is not a valid error code such as -1, the system message
|
||||
* will look like "error -1".
|
||||
*
|
||||
* **Example**:
|
||||
*
|
||||
* // This throws a system_error with the description
|
||||
* // cannot open file 'foo': The system cannot find the file specified.
|
||||
* // or similar (system message may vary) if the file doesn't exist.
|
||||
* const char *filename = "foo";
|
||||
* LPOFSTRUCT of = LPOFSTRUCT();
|
||||
* HFILE file = OpenFile(filename, &of, OF_READ);
|
||||
* if (file == HFILE_ERROR) {
|
||||
* throw fmt::windows_error(GetLastError(),
|
||||
* "cannot open file '{}'", filename);
|
||||
* }
|
||||
*/
|
||||
template <typename... T>
|
||||
auto windows_error(int error_code, string_view message, const T&... args)
|
||||
-> std::system_error {
|
||||
return vwindows_error(error_code, message, vargs<T...>{{args...}});
|
||||
}
|
||||
|
||||
// Reports a Windows error without throwing an exception.
|
||||
// Can be used to report errors from destructors.
|
||||
FMT_API void report_windows_error(int error_code, const char* message) noexcept;
|
||||
#else
|
||||
inline auto system_category() noexcept -> const std::error_category& {
|
||||
return std::system_category();
|
||||
}
|
||||
#endif // _WIN32
|
||||
|
||||
// std::system is not available on some platforms such as iOS (#2248).
|
||||
#ifdef __OSX__
|
||||
template <typename S, typename... Args, typename Char = char_t<S>>
|
||||
void say(const S& fmt, Args&&... args) {
|
||||
std::system(format("say \"{}\"", format(fmt, args...)).c_str());
|
||||
}
|
||||
#endif
|
||||
|
||||
// A buffered file.
|
||||
class buffered_file {
|
||||
private:
|
||||
FILE* file_;
|
||||
|
||||
friend class file;
|
||||
|
||||
inline explicit buffered_file(FILE* f) : file_(f) {}
|
||||
|
||||
public:
|
||||
buffered_file(const buffered_file&) = delete;
|
||||
void operator=(const buffered_file&) = delete;
|
||||
|
||||
// Constructs a buffered_file object which doesn't represent any file.
|
||||
inline buffered_file() noexcept : file_(nullptr) {}
|
||||
|
||||
// Destroys the object closing the file it represents if any.
|
||||
FMT_API ~buffered_file() noexcept;
|
||||
|
||||
public:
|
||||
inline buffered_file(buffered_file&& other) noexcept : file_(other.file_) {
|
||||
other.file_ = nullptr;
|
||||
}
|
||||
|
||||
inline auto operator=(buffered_file&& other) -> buffered_file& {
|
||||
close();
|
||||
file_ = other.file_;
|
||||
other.file_ = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Opens a file.
|
||||
FMT_API buffered_file(cstring_view filename, cstring_view mode);
|
||||
|
||||
// Closes the file.
|
||||
FMT_API void close();
|
||||
|
||||
// Returns the pointer to a FILE object representing this file.
|
||||
inline auto get() const noexcept -> FILE* { return file_; }
|
||||
|
||||
FMT_API auto descriptor() const -> int;
|
||||
|
||||
template <typename... T>
|
||||
inline void print(string_view fmt, const T&... args) {
|
||||
fmt::vargs<T...> vargs = {{args...}};
|
||||
detail::is_locking<T...>() ? fmt::vprint_buffered(file_, fmt, vargs)
|
||||
: fmt::vprint(file_, fmt, vargs);
|
||||
}
|
||||
};
|
||||
|
||||
#if FMT_USE_FCNTL
|
||||
|
||||
// A file. Closed file is represented by a file object with descriptor -1.
|
||||
// Methods that are not declared with noexcept may throw
|
||||
// fmt::system_error in case of failure. Note that some errors such as
|
||||
// closing the file multiple times will cause a crash on Windows rather
|
||||
// than an exception. You can get standard behavior by overriding the
|
||||
// invalid parameter handler with _set_invalid_parameter_handler.
|
||||
class FMT_API file {
|
||||
private:
|
||||
int fd_; // File descriptor.
|
||||
|
||||
// Constructs a file object with a given descriptor.
|
||||
explicit file(int fd) : fd_(fd) {}
|
||||
|
||||
friend struct pipe;
|
||||
|
||||
public:
|
||||
// Possible values for the oflag argument to the constructor.
|
||||
enum {
|
||||
RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
|
||||
WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
|
||||
RDWR = FMT_POSIX(O_RDWR), // Open for reading and writing.
|
||||
CREATE = FMT_POSIX(O_CREAT), // Create if the file doesn't exist.
|
||||
APPEND = FMT_POSIX(O_APPEND), // Open in append mode.
|
||||
TRUNC = FMT_POSIX(O_TRUNC) // Truncate the content of the file.
|
||||
};
|
||||
|
||||
// Constructs a file object which doesn't represent any file.
|
||||
inline file() noexcept : fd_(-1) {}
|
||||
|
||||
// Opens a file and constructs a file object representing this file.
|
||||
file(cstring_view path, int oflag);
|
||||
|
||||
public:
|
||||
file(const file&) = delete;
|
||||
void operator=(const file&) = delete;
|
||||
|
||||
inline file(file&& other) noexcept : fd_(other.fd_) { other.fd_ = -1; }
|
||||
|
||||
// Move assignment is not noexcept because close may throw.
|
||||
inline auto operator=(file&& other) -> file& {
|
||||
close();
|
||||
fd_ = other.fd_;
|
||||
other.fd_ = -1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Destroys the object closing the file it represents if any.
|
||||
~file() noexcept;
|
||||
|
||||
// Returns the file descriptor.
|
||||
inline auto descriptor() const noexcept -> int { return fd_; }
|
||||
|
||||
// Closes the file.
|
||||
void close();
|
||||
|
||||
// Returns the file size. The size has signed type for consistency with
|
||||
// stat::st_size.
|
||||
auto size() const -> long long;
|
||||
|
||||
// Attempts to read count bytes from the file into the specified buffer.
|
||||
auto read(void* buffer, size_t count) -> size_t;
|
||||
|
||||
// Attempts to write count bytes from the specified buffer to the file.
|
||||
auto write(const void* buffer, size_t count) -> size_t;
|
||||
|
||||
// Duplicates a file descriptor with the dup function and returns
|
||||
// the duplicate as a file object.
|
||||
static auto dup(int fd) -> file;
|
||||
|
||||
// Makes fd be the copy of this file descriptor, closing fd first if
|
||||
// necessary.
|
||||
void dup2(int fd);
|
||||
|
||||
// Makes fd be the copy of this file descriptor, closing fd first if
|
||||
// necessary.
|
||||
void dup2(int fd, std::error_code& ec) noexcept;
|
||||
|
||||
// Creates a buffered_file object associated with this file and detaches
|
||||
// this file object from the file.
|
||||
auto fdopen(const char* mode) -> buffered_file;
|
||||
|
||||
# if defined(_WIN32) && !defined(__MINGW32__)
|
||||
// Opens a file and constructs a file object representing this file by
|
||||
// wcstring_view filename. Windows only.
|
||||
static file open_windows_file(wcstring_view path, int oflag);
|
||||
# endif
|
||||
};
|
||||
|
||||
struct FMT_API pipe {
|
||||
file read_end;
|
||||
file write_end;
|
||||
|
||||
// Creates a pipe setting up read_end and write_end file objects for reading
|
||||
// and writing respectively.
|
||||
pipe();
|
||||
};
|
||||
|
||||
// Returns the memory page size.
|
||||
auto getpagesize() -> long;
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct buffer_size {
|
||||
constexpr buffer_size() = default;
|
||||
size_t value = 0;
|
||||
FMT_CONSTEXPR auto operator=(size_t val) const -> buffer_size {
|
||||
auto bs = buffer_size();
|
||||
bs.value = val;
|
||||
return bs;
|
||||
}
|
||||
};
|
||||
|
||||
struct ostream_params {
|
||||
int oflag = file::WRONLY | file::CREATE | file::TRUNC;
|
||||
size_t buffer_size = BUFSIZ > 32768 ? BUFSIZ : 32768;
|
||||
|
||||
constexpr ostream_params() {}
|
||||
|
||||
template <typename... T>
|
||||
ostream_params(T... params, int new_oflag) : ostream_params(params...) {
|
||||
oflag = new_oflag;
|
||||
}
|
||||
|
||||
template <typename... T>
|
||||
ostream_params(T... params, detail::buffer_size bs)
|
||||
: ostream_params(params...) {
|
||||
this->buffer_size = bs.value;
|
||||
}
|
||||
|
||||
// Intel has a bug that results in failure to deduce a constructor
|
||||
// for empty parameter packs.
|
||||
# if defined(__INTEL_COMPILER) && __INTEL_COMPILER < 2000
|
||||
ostream_params(int new_oflag) : oflag(new_oflag) {}
|
||||
ostream_params(detail::buffer_size bs) : buffer_size(bs.value) {}
|
||||
# endif
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
FMT_INLINE_VARIABLE constexpr auto buffer_size = detail::buffer_size();
|
||||
|
||||
/// A fast buffered output stream for writing from a single thread. Writing from
|
||||
/// multiple threads without external synchronization may result in a data race.
|
||||
class ostream : private detail::buffer<char> {
|
||||
private:
|
||||
file file_;
|
||||
|
||||
FMT_API ostream(cstring_view path, const detail::ostream_params& params);
|
||||
|
||||
FMT_API static void grow(buffer<char>& buf, size_t);
|
||||
|
||||
public:
|
||||
FMT_API ostream(ostream&& other) noexcept;
|
||||
FMT_API ~ostream();
|
||||
|
||||
operator writer() {
|
||||
detail::buffer<char>& buf = *this;
|
||||
return buf;
|
||||
}
|
||||
|
||||
inline void flush() {
|
||||
if (size() == 0) return;
|
||||
file_.write(data(), size() * sizeof(data()[0]));
|
||||
clear();
|
||||
}
|
||||
|
||||
template <typename... T>
|
||||
friend auto output_file(cstring_view path, T... params) -> ostream;
|
||||
|
||||
inline void close() {
|
||||
flush();
|
||||
file_.close();
|
||||
}
|
||||
|
||||
/// Formats `args` according to specifications in `fmt` and writes the
|
||||
/// output to the file.
|
||||
template <typename... T> void print(format_string<T...> fmt, T&&... args) {
|
||||
vformat_to(appender(*this), fmt.str, vargs<T...>{{args...}});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Opens a file for writing. Supported parameters passed in `params`:
|
||||
*
|
||||
* - `<integer>`: Flags passed to [open](
|
||||
* https://pubs.opengroup.org/onlinepubs/007904875/functions/open.html)
|
||||
* (`file::WRONLY | file::CREATE | file::TRUNC` by default)
|
||||
* - `buffer_size=<integer>`: Output buffer size
|
||||
*
|
||||
* **Example**:
|
||||
*
|
||||
* auto out = fmt::output_file("guide.txt");
|
||||
* out.print("Don't {}", "Panic");
|
||||
*/
|
||||
template <typename... T>
|
||||
inline auto output_file(cstring_view path, T... params) -> ostream {
|
||||
return {path, detail::ostream_params(params...)};
|
||||
}
|
||||
#endif // FMT_USE_FCNTL
|
||||
|
||||
FMT_END_EXPORT
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#endif // FMT_OS_H_
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user