28 Commits
Author SHA1 Message Date
waiwaylee c01185bf9c Merge branch 'feature' 2026-10-13 14:50:10 +08:00
waiwaylee b4aed8a37b 更新:1、回调TWS外推时间差计算方法,避免TAS时同步出现TWS航迹的问题;
Signed-off-by: waiwaylee <waiwaylee@foxmail.com>
2026-10-13 14:49:48 +08:00
waiwaylee e98280409e 更新:1、调整临时航迹记录时机,在转为可靠航迹前记录,避免转为正式航迹后丢失当前帧的临时航迹记录;
Signed-off-by: waiwaylee <waiwaylee@foxmail.com>
2026-10-10 17:44:19 +08:00
waiwaylee 56b5449b9f 更新:1、修改TWS外推时,时间差计算方法,不再使用固定数据率计算时间差,而是使用最新时间戳来计算;
Signed-off-by: waiwaylee <waiwaylee@foxmail.com>
2026-10-09 14:04:22 +08:00
waiwaylee 5cb67ccef8 更新:1、增加TAS航迹信息、TAS关联信息记录到数据库;
Signed-off-by: waiwaylee <waiwaylee@foxmail.com>
2026-10-09 10:58:53 +08:00
waiwaylee 9f37fba3e7 Merge branch 'update' 2026-10-08 16:37:19 +08:00
waiwaylee 3cab2339d2 Merge branch 'feature' into update 2026-10-08 16:36:47 +08:00
waiwaylee a6a7877a86 更新:1、更新文档;
Signed-off-by: waiwaylee <waiwaylee@foxmail.com>
2026-10-08 16:13:15 +08:00
waiwaylee b227ae1957 更新:1、数据处理增加版本号输出;
2、增加Debug模式下可靠航迹、关联信息等数据写入数据库;

Signed-off-by: waiwaylee <waiwaylee@foxmail.com>
2026-10-08 16:01:53 +08:00
waiwaylee c3c7e668d2 更新:1、增加临时航迹录入数据库;
2、增加点迹信息的圈ID记录;

Signed-off-by: waiwaylee <waiwaylee@foxmail.com>
2026-09-30 16:33:18 +08:00
waiwaylee a4f32e4036 更新:1、新增数据记录功能,将过程数据录入数据库;
2、增加输入点迹数据、凝聚后点迹数据记录;

Signed-off-by: waiwaylee <waiwaylee@foxmail.com>
2026-09-30 09:02:28 +08:00
waiwaylee ff9b111d34 更新:1、升级编译器版本至MSVC2022,升级C++至C++17;
Signed-off-by: waiwaylee <waiwaylee@foxmail.com>
2026-09-29 11:00:43 +08:00
waiwaylee 2934e3b59b 更新:1、更新文档;
Signed-off-by: waiwaylee <waiwaylee@foxmail.com>
2026-09-29 10:25:56 +08:00
waiwaylee ed7d6d7902 Merge branch 'feature' 2026-09-28 10:20:49 +08:00
waiwaylee e8c56f914b 更新:1、适配输出点迹归一化幅度和速度维FFT点数;
Signed-off-by: waiwaylee <waiwaylee@foxmail.com>
2026-09-28 09:28:01 +08:00
waiwaylee 42b598e2e5 更新:1、调整向站/背站判断条件;
Signed-off-by: waiwaylee <waiwaylee@foxmail.com>
2026-09-24 15:40:52 +08:00
waiwaylee fc49707b08 更新:1、调整向站/背站的判断逻辑,解决速度模糊时判断有误的问题;
Signed-off-by: waiwaylee <waiwaylee@foxmail.com>
2026-09-23 15:40:16 +08:00
waiwaylee 50e0b5cb74 更新:1、增加手动设置重点目标的功能;
2、调整航迹关联时外推圈数限制,不过多外推;
3、调整航迹消批条件,重点航迹消批圈数提高;
4、更新文档;

Signed-off-by: waiwaylee <waiwaylee@foxmail.com>
2026-09-23 09:35:13 +08:00
waiwaylee e0bf227be3 更新:1、航迹关联时增加高度门限判断,同时加大统计距离门限,提高机动目标关联概率;
2、修复TAS波束控制时,数据率判断有误的问题;
3、更新文档;

Signed-off-by: waiwaylee <waiwaylee@foxmail.com>
2026-09-22 09:20:12 +08:00
waiwaylee a76da34756 更新:1、更新项目文档;
Signed-off-by: waiwaylee <waiwaylee@foxmail.com>
2026-09-18 11:14:38 +08:00
waiwaylee f249fdc50d 更新:1、增加航机信息中靠近/远离标志位输出;
2、增加项目文档;

Signed-off-by: waiwaylee <waiwaylee@foxmail.com>
2026-09-18 11:09:57 +08:00
waiwaylee 7d5a56f3d2 更新:1、修改一周扫描完成的判断条件,原来采用判断波位号变小为一周扫描完成,但机扫模式下,可能存在转台反转的情况,导致波位号变小,但并未扫描一周,因此改为判断波位号为0时,表示扫描一周完成;
2、更新点迹和航迹头关联条件,限制部分切向高速短航迹;

Signed-off-by: waiwaylee <waiwaylee@foxmail.com>
2026-09-16 11:06:01 +08:00
waiwaylee 604f1585f6 更新:1、TAS航迹不关联时间戳更早的TWS点迹,但标记点迹已使用,避免生成TWS航迹;
2、调整目录结构,将过期的文档都放到archived_docs文件夹中留档;

Signed-off-by: waiwaylee <waiwaylee@foxmail.com>
2026-09-12 10:01:15 +08:00
waiwaylee d6cdbc0120 更新:1、更新项目文档;
2、删除一些无用的代码;

Signed-off-by: waiwaylee <waiwaylee@foxmail.com>
2026-09-09 09:35:55 +08:00
waiwaylee 15215926c5 更新:1、增加日志模块,将标准输出信息写入到日志文件;
2、将RadarPara中DATA_RATE_TAS的单位由Hz改为s;
3、修复TAS_Ctrl::tas_beam_output函数中给Tracking_beam->TAS_track_index赋值错误的问题;

Signed-off-by: waiwaylee <waiwaylee@foxmail.com>
2026-09-07 19:16:32 +08:00
waiwaylee 5f820987a9 更新:1、修复全部C4819 / C4018 / C4244警告;
2、更新项目文档;
2026-09-07 14:29:24 +08:00
waiwaylee f438ac53d9 更新:1、优化TAS逻辑
2、优化宏定义

Signed-off-by: waiwaylee <waiwaylee@foxmail.com>
2026-09-07 09:48:58 +08:00
waiwaylee e729c5baee 更新:修改FREQ0宏定义为9.2GHz
Signed-off-by: waiwaylee <waiwaylee@foxmail.com>
2026-09-02 11:30:10 +08:00
145 changed files with 324677 additions and 1228 deletions
+3
View File
@@ -3,4 +3,7 @@ build-data_process_class_dll-Desktop_Qt_5_7_0_MSVC2013_32bit-Release/
debug/
release/
build/
rdp_logs/
rdp_records/
design_docs/
.mnemon/
+10 -10
View File
@@ -2,10 +2,10 @@
"version": 4,
"configurations": [
{
"compilerPath": "D:\\APP\\VisualStudio2013\\VC\\bin\\cl.exe",
"cStandard": "c11",
"cppStandard": "c++11",
"intelliSenseMode": "windows-msvc-x86",
"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",
@@ -20,14 +20,14 @@
"${workspaceFolder}/data_process_class_dll/Eigen",
"${workspaceFolder}/**"
],
"name": "X256_PS (MSVC2013 x86)",
"name": "X256_PS (MSVC2022 x64)",
"compileCommands": "${workspaceFolder}/build/X256_PS/compile_commands.json"
},
{
"compilerPath": "D:\\APP\\VisualStudio2013\\VC\\bin\\cl.exe",
"cStandard": "c11",
"cppStandard": "c++11",
"intelliSenseMode": "windows-msvc-x86",
"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",
@@ -42,7 +42,7 @@
"${workspaceFolder}/data_process_class_dll/Eigen",
"${workspaceFolder}/**"
],
"name": "X256_MS (MSVC2013 x86)",
"name": "X256_MS (MSVC2022 x64)",
"compileCommands": "${workspaceFolder}/build/X256_MS/compile_commands.json"
}
]
+2 -2
View File
@@ -3,11 +3,11 @@ buildType:
choices:
release:
short: Release
long: Release (MSVC2013 x86)
long: Release (MSVC2022 x64, C++17)
buildType: Release
debug:
short: Debug
long: Debug (MSVC2013 x86)
long: Debug (MSVC2022 x64, C++17)
buildType: Debug
scanMode:
+12 -1
View File
@@ -1,11 +1,22 @@
@echo off
rem ===========================================================================
rem CMake build script (MSVC 2022 / v143, C++17, x64)
rem
rem Usage: build.bat <build-dir>
rem e.g. build.bat build\X256_PS
rem
rem Encoding: keep this file ASCII-only (see msvc_env.bat).
rem ===========================================================================
setlocal
set "BUILD_DIR=%~1"
if "%BUILD_DIR%"=="" (
echo Usage: build.bat ^<build-dir^>
exit /b 1
)
call D:\APP\VisualStudio2013\VC\vcvarsall.bat x86 >nul
rem Load the MSVC 2022 environment (vcvarsall.bat + cl.exe existence check).
call "%~dp0msvc_env.bat"
if errorlevel 1 exit /b %errorlevel%
cmake --build "%BUILD_DIR%"
exit /b %errorlevel%
+12 -1
View File
@@ -1,11 +1,22 @@
@echo off
rem ===========================================================================
rem CMake clean script (MSVC 2022 / v143, C++17, x64)
rem
rem Usage: clean.bat <build-dir>
rem e.g. clean.bat build\X256_PS
rem
rem Encoding: keep this file ASCII-only (see msvc_env.bat).
rem ===========================================================================
setlocal
set "BUILD_DIR=%~1"
if "%BUILD_DIR%"=="" (
echo Usage: clean.bat ^<build-dir^>
exit /b 1
)
call D:\APP\VisualStudio2013\VC\vcvarsall.bat x86 >nul
rem Load the MSVC 2022 environment (vcvarsall.bat + cl.exe existence check).
call "%~dp0msvc_env.bat"
if errorlevel 1 exit /b %errorlevel%
cmake --build "%BUILD_DIR%" --target clean
exit /b %errorlevel%
+57 -5
View File
@@ -1,21 +1,73 @@
@echo off
rem ===========================================================================
rem CMake configure script (MSVC 2022 / v143, C++17, x64)
rem
rem Usage: configure.bat <build-dir> <PHASE_SCANNING^|MECHANICAL_SCANNING> [Debug^|Release] [AUTO^|ON^|OFF]
rem e.g. configure.bat build\X256_PS PHASE_SCANNING Release
rem configure.bat build\X256_PS Debug ON (Debug + data recording enabled)
rem
rem The 4th 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. See
rem docs/data_record_debug.md.
rem
rem The generator stays "NMake Makefiles" (the choice made during the VS Code
rem migration); cl.exe is pinned to the MSVC 2022 x64 toolset.
rem
rem A build directory that was configured by an older compiler (for example the
rem MSVC 2013 cache) is removed first: neither its CMake cache nor its object
rem files can be reused across a compiler upgrade.
rem
rem Encoding: keep this file ASCII-only (see msvc_env.bat).
rem ===========================================================================
setlocal
set "BUILD_DIR=%~1"
set "SCAN_MODE=%~2"
set "BUILD_TYPE=%~3"
set "RECORD_MODE=%~4"
if "%BUILD_DIR%"=="" (
echo Usage: configure.bat ^<build-dir^> ^<PHASE_SCANNING^|MECHANICAL_SCANNING^> [Debug^|Release]
echo Usage: configure.bat ^<build-dir^> ^<PHASE_SCANNING^|MECHANICAL_SCANNING^> [Debug^|Release] [AUTO^|ON^|OFF]
exit /b 1
)
if "%SCAN_MODE%"=="" (
echo Usage: configure.bat ^<build-dir^> ^<PHASE_SCANNING^|MECHANICAL_SCANNING^> [Debug^|Release]
echo Usage: configure.bat ^<build-dir^> ^<PHASE_SCANNING^|MECHANICAL_SCANNING^> [Debug^|Release] [AUTO^|ON^|OFF]
exit /b 1
)
if "%BUILD_TYPE%"=="" set "BUILD_TYPE=Release"
call D:\APP\VisualStudio2013\VC\vcvarsall.bat x86 >nul
if "%RECORD_MODE%"=="" set "RECORD_MODE=AUTO"
rem Load the MSVC 2022 environment (vcvarsall.bat + cl.exe existence check).
call "%~dp0msvc_env.bat"
if errorlevel 1 exit /b %errorlevel%
rem Drop a build tree left behind by another toolset.
if not exist "%BUILD_DIR%\CMakeCache.txt" goto :configure
set "OLD_TOOLSET="
if exist "%BUILD_DIR%\.msvc_toolset" set /p OLD_TOOLSET=<"%BUILD_DIR%\.msvc_toolset"
if /i "%OLD_TOOLSET%"=="%MSVC_TOOLSET_VERSION%" goto :configure
echo [configure] "%BUILD_DIR%" was configured with MSVC toolset "%OLD_TOOLSET%" - rebuilding from scratch
rmdir /s /q "%BUILD_DIR%"
if exist "%BUILD_DIR%" (
echo [ERROR] cannot remove "%BUILD_DIR%", please delete it manually and retry.
exit /b 1
)
:configure
echo [configure] compiler : %MSVC_CXX_COMPILER%
echo [configure] build dir: %BUILD_DIR%
echo [configure] mode : %SCAN_MODE% / %BUILD_TYPE%
echo [configure] record : %RECORD_MODE% (RDP_DATA_RECORD: AUTO/ON/OFF)
pushd "%~dp0..\.."
cmake -S . -B "%BUILD_DIR%" -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=%BUILD_TYPE% -DRADAR_SCAN_MODE=%SCAN_MODE%
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%"
popd
exit /b %CMAKE_EXIT%
if not "%CMAKE_EXIT%"=="0" exit /b %CMAKE_EXIT%
rem Remember which toolset produced this build tree (checked on the next run).
> "%BUILD_DIR%\.msvc_toolset" echo %MSVC_TOOLSET_VERSION%
exit /b 0
+50
View File
@@ -0,0 +1,50 @@
@echo off
rem ===========================================================================
rem Shared MSVC 2022 (v143) environment setup.
rem
rem Called by configure.bat / build.bat / clean.bat. It:
rem 1) locates the MSVC 2022 toolset and cl.exe (default Hostx64\x64);
rem 2) calls vcvarsall.bat to set PATH / INCLUDE / LIB (nmake comes with it).
rem
rem NOTE: this script does NOT use setlocal, so the variables it defines stay
rem in the caller's environment (all callers run under setlocal).
rem
rem Override the defaults with these environment variables:
rem VS2022_DIR Visual Studio 2022 install root
rem MSVC_TOOLSET_VERSION MSVC toolset version
rem MSVC_HOST_ARCH compiler host architecture (x64 / x86)
rem MSVC_TARGET_ARCH target architecture (x64 / x86)
rem
rem Exit code: 0 = ok, non-zero = setup failed (caller must abort).
rem
rem Encoding: keep this file ASCII-only (cmd.exe parses .bat in the OEM code
rem page, UTF-8 comments would break parsing on non-UTF-8 consoles).
rem ===========================================================================
if not defined VS2022_DIR set "VS2022_DIR=D:\APP\VisualStudio2022"
if not defined MSVC_TOOLSET_VERSION set "MSVC_TOOLSET_VERSION=14.44.35207"
if not defined MSVC_HOST_ARCH set "MSVC_HOST_ARCH=x64"
if not defined MSVC_TARGET_ARCH set "MSVC_TARGET_ARCH=x64"
set "MSVC_TOOLSET_DIR=%VS2022_DIR%\VC\Tools\MSVC\%MSVC_TOOLSET_VERSION%"
set "MSVC_CXX_COMPILER=%MSVC_TOOLSET_DIR%\bin\Host%MSVC_HOST_ARCH%\%MSVC_TARGET_ARCH%\cl.exe"
set "VCVARS_BAT=%VS2022_DIR%\VC\Auxiliary\Build\vcvarsall.bat"
if not exist "%VCVARS_BAT%" (
echo [ERROR] vcvarsall.bat not found: "%VCVARS_BAT%"
echo Please set VS2022_DIR to the Visual Studio 2022 install root.
exit /b 1
)
if not exist "%MSVC_CXX_COMPILER%" (
echo [ERROR] cl.exe not found: "%MSVC_CXX_COMPILER%"
echo Please check MSVC_TOOLSET_VERSION / MSVC_HOST_ARCH / MSVC_TARGET_ARCH.
exit /b 1
)
call "%VCVARS_BAT%" %MSVC_TARGET_ARCH% >nul
if errorlevel 1 (
echo [ERROR] "vcvarsall.bat %MSVC_TARGET_ARCH%" failed.
exit /b 1
)
exit /b 0
+8 -4
View File
@@ -2,11 +2,15 @@
"cmake.configureOnOpen": false,
"cmake.generator": "NMake Makefiles",
"cmake.buildDirectory": "${workspaceFolder}/build/X256_PS",
"cmake.configureArgs": [
"-DCMAKE_CXX_COMPILER=D:/APP/VisualStudio2022/VC/Tools/MSVC/14.44.35207/bin/Hostx64/x64/cl.exe"
],
"cmake.environment": {
"PATH": "D:\\APP\\VisualStudio2013\\VC\\BIN;D:\\APP\\VisualStudio2013\\Common7\\IDE;C:\\Program Files (x86)\\Windows Kits\\8.1\\bin\\x86;${env:PATH}",
"INCLUDE": "D:\\APP\\VisualStudio2013\\VC\\INCLUDE;D:\\APP\\VisualStudio2013\\VC\\ATLMFC\\INCLUDE;C:\\Program Files (x86)\\Windows Kits\\8.1\\include\\shared;C:\\Program Files (x86)\\Windows Kits\\8.1\\include\\um;C:\\Program Files (x86)\\Windows Kits\\8.1\\include\\winrt;${env:INCLUDE}",
"LIB": "D:\\APP\\VisualStudio2013\\VC\\LIB;D:\\APP\\VisualStudio2013\\VC\\ATLMFC\\LIB;C:\\Program Files (x86)\\Windows Kits\\8.1\\lib\\winv6.3\\um\\x86;${env:LIB}"
"PATH": "D:\\APP\\VisualStudio2022\\VC\\Tools\\MSVC\\14.44.35207\\bin\\Hostx64\\x64;${env:PATH}",
"INCLUDE": "D:\\APP\\VisualStudio2022\\VC\\Tools\\MSVC\\14.44.35207\\include;D:\\APP\\VisualStudio2022\\VC\\Tools\\MSVC\\14.44.35207\\ATLMFC\\include;D:\\APP\\VisualStudio2022\\VC\\Auxiliary\\VS\\include;C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.26100.0\\ucrt;C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.26100.0\\um;C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.26100.0\\shared;C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.26100.0\\winrt;C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.26100.0\\cppwinrt;${env:INCLUDE}",
"LIB": "D:\\APP\\VisualStudio2022\\VC\\Tools\\MSVC\\14.44.35207\\ATLMFC\\lib\\x64;D:\\APP\\VisualStudio2022\\VC\\Tools\\MSVC\\14.44.35207\\lib\\x64;C:\\Program Files (x86)\\Windows Kits\\10\\lib\\10.0.26100.0\\ucrt\\x64;C:\\Program Files (x86)\\Windows Kits\\10\\lib\\10.0.26100.0\\um\\x64;${env:LIB}"
},
"C_Cpp.default.cppStandard": "c++11",
"C_Cpp.default.cppStandard": "c++17",
"C_Cpp.default.intelliSenseMode": "windows-msvc-x64",
"C_Cpp.default.compileCommands": "${workspaceFolder}/build/X256_PS/compile_commands.json"
}
+8 -8
View File
@@ -5,7 +5,7 @@
},
"tasks": [
{
"label": "CMake: 配置 X256_PS (Release, MSVC2013 x86)",
"label": "CMake: 配置 X256_PS (Release, MSVC2022 x64)",
"type": "shell",
"command": "${workspaceFolder}\\.vscode\\scripts\\configure.bat",
"args": [
@@ -16,7 +16,7 @@
"problemMatcher": []
},
{
"label": "CMake: 配置 X256_PS (Debug, MSVC2013 x86)",
"label": "CMake: 配置 X256_PS (Debug, MSVC2022 x64)",
"type": "shell",
"command": "${workspaceFolder}\\.vscode\\scripts\\configure.bat",
"args": [
@@ -60,7 +60,7 @@
"problemMatcher": []
},
{
"label": "CMake: 配置 X256_MS (Release, MSVC2013 x86)",
"label": "CMake: 配置 X256_MS (Release, MSVC2022 x64)",
"type": "shell",
"command": "${workspaceFolder}\\.vscode\\scripts\\configure.bat",
"args": [
@@ -71,7 +71,7 @@
"problemMatcher": []
},
{
"label": "CMake: 配置 X256_MS (Debug, MSVC2013 x86)",
"label": "CMake: 配置 X256_MS (Debug, MSVC2022 x64)",
"type": "shell",
"command": "${workspaceFolder}\\.vscode\\scripts\\configure.bat",
"args": [
@@ -118,7 +118,7 @@
"label": "CMake: 配置并构建 X256_PS (Release)",
"dependsOrder": "sequence",
"dependsOn": [
"CMake: 配置 X256_PS (Release, MSVC2013 x86)",
"CMake: 配置 X256_PS (Release, MSVC2022 x64)",
"CMake: 构建 X256_PS (Release)"
],
"group": {
@@ -131,7 +131,7 @@
"label": "CMake: 配置并构建 X256_PS (Debug)",
"dependsOrder": "sequence",
"dependsOn": [
"CMake: 配置 X256_PS (Debug, MSVC2013 x86)",
"CMake: 配置 X256_PS (Debug, MSVC2022 x64)",
"CMake: 构建 X256_PS (Debug)"
],
"group": "build",
@@ -141,7 +141,7 @@
"label": "CMake: 配置并构建 X256_MS (Release)",
"dependsOrder": "sequence",
"dependsOn": [
"CMake: 配置 X256_MS (Release, MSVC2013 x86)",
"CMake: 配置 X256_MS (Release, MSVC2022 x64)",
"CMake: 构建 X256_MS (Release)"
],
"group": "build",
@@ -151,7 +151,7 @@
"label": "CMake: 配置并构建 X256_MS (Debug)",
"dependsOrder": "sequence",
"dependsOn": [
"CMake: 配置 X256_MS (Debug, MSVC2013 x86)",
"CMake: 配置 X256_MS (Debug, MSVC2022 x64)",
"CMake: 构建 X256_MS (Debug)"
],
"group": "build",
+101 -5
View File
@@ -9,13 +9,109 @@
### Added
### Changed
### Fixed
### Removed
## [1.5.6] - 2026-10-08
> 本次发布把上一轮「工作区(未提交)」条目整体归档,并补充 a4f32e4 / c3c7e66 / b227ae1 三次提交的改动。
> 版本号宏位于 `parameters.h`(`MAJOR_VERSION` / `MINOR_VERSION` / `PATCH_VERSION`),当前为 **1.5.6**。
### Added
- **新增版本号输出(b227ae1)**:`parameters.h` 新增版本宏组——`MAJOR_VERSION` / `MINOR_VERSION` / `PATCH_VERSION`(当前 1 / 5 / 6)、扫描体制后缀 `VERSION_INFO`(相扫 `PS` / 机扫 `MS`)以及拼装宏 `VERSION_STRING`(`STRINGIFY` 两级宏展开,输出形如 `V1.5.6.PS`)。`Data_process_class_dll` 基类新增公开成员 `const char* version_info`,`Data_Process_Factory::GetB()` 创建实例后赋值为 `VERSION_STRING`;宿主可在拿到实例指针后直接读取该字段上报/核对 DLL 版本。
- **数据记录表扩充为 6 张在用表(7 个实体定义)(a4f32e4 / c3c7e66 / b227ae1)**:记录实体由「2 个示例」扩展为覆盖主要中间数据的 6 张在用表——`RdpRecPoint`(TWS/TAS 输入点迹,用 `point_type` 区分,逐点写入)、`RdpRecPointCohesionTWS` / `RdpRecPointCohesionTAS`(凝聚后点迹,分别写入)、`RdpRecTempTrack`(临时航迹,`track_init_process_logic()` 末尾在消亡临时航迹之后逐点写入)、`RdpRecTrustTrack`(可靠航迹,TWS 输出循环内按本圈更新航迹逐条写入)、`RdpRecAssoInfo`(点航关联信息,TWS 关联的候选/取中/删表三处写入)。原示例实体 `RdpRecTrackOut`(本圈输出航迹)与其在 `track_process()` 中的批量记录点已整体注释停用(实体定义保留注释,故 `rdp_record_entity.h` 中共 7 个实体定义),改由 `RdpRecTrustTrack` 记录内部真实航迹。
- **点迹/航迹记录新增 ID 与圈次维度(c3c7e66、b227ae1)**:`Data_Process` 新增自增成员 `point_id` / `frame_id` / `circle_id`(`data_process.h`)——每接收一帧 `frame_id` 自增;每个点迹 `point_id` 自增并随 `PointRecv` 落库;`circle_id` 在 TWS 满一圈(`Beam_index_aiz == 0`)、TAS 每帧处理结束时自增,写入该圈/帧的所有点迹。内部结构同步新增标识字段:`PointRecv` 增 `circle_id`;`Temp_track` 增 `temp_track_id` / `point_id` / `circle_id`(`Track_Init` 新增自增成员 `temp_track_id`);`Trust_Track` 增 `track_id` / `point_id` / `circle_id`(起批时取自临时航迹末点,关联成功时刷新为被关联点迹的 ID)。据此可把点迹、凝聚点迹、临时航迹、可靠航迹、关联信息按 `point_id` / `track_id` / `circle_id` 串成一条完整链路。
- **新增 `Asso_Info` 关联信息结构体(b227ae1)**:原 `Track_Asso::model_filter()` 内定义的局部结构体 `asso_info` 提升为 `struct.h` 中的全局 `Asso_Info`,在原有 `d1/d2/d3/d_min/point_index/track_index` 基础上补充 `point_id` / `track_id` / `circle_id`、门限相关量(`delta_T` / `delta_h` / `R` / `VT`、三个布尔量 `thd_d` / `thd_h` / `thd_r`)以及「本候选是否被最近邻取中」标志 `min_d_flag`,使关联信息可被完整记录与分析。
- **可靠航迹补充极坐标量快照(b227ae1)**:`Trust_Track` 新增 `Range` / `Azimuth` / `Elevation` / `Velocity` 四个字段,在 TWS 输出环节由本圈写入输出结构的距离/方位/俯仰/合速度回填(`Velocity` 取 `Range_V`,即 IMM 融合速度的模),供记录表落库;注意它们只在航迹当圈**有输出**时被刷新。
- 新增 CHANGELOG.md,采用 Keep a Changelog 格式记录项目变更历史。
- `RadarPara` 结构体新增 `DATA_RATE_TAS`(TAS 模式数据率)字段,由 DLL 调用者通过 `track_process_parameters_initial()` 输入。
- `RadarPara` 结构体新增 `DATA_RATE_TAS`(TAS 模式数据率)字段,由 DLL 调用者通过 `track_process_parameters_initial()` 输入(b3e2f52)。
- `Tracking_Target` 新增 `last_track_time` 字段,记录各 TAS 目标最近一次波束输出时间(f438ac5)。
- 新增 requirements.md,说明 TAS 波束控制逻辑的修改背景与约定(b3e2f52)。
- 更新 README.md:目录结构补充 CHANGELOG.md / requirements.md;版本历史表补充 93d3924、b3e2f52、e729c5b、f438ac5 提交记录。
- 新增日志文件输出功能:`parameters.h` 增加 `RDP_LOG_ENABLE` 开关(定义时启用;**当前源码中该宏为注释状态,即默认不重定向、日志输出到控制台**)与 `RDP_LOG_DIR` 目录宏(`.\\rdp_logs\\`);新增 `rdp_log.h/.cpp` 日志模块,首次 `Data_Process_Factory::GetB()` 时按 `RDP_LOG_ENABLE` 决定是否用 `rdbuf()` 将 `std::cout` 重定向到 `.\rdp_logs\` 下按日期+时间命名的日志文件(`rdp_YYYYMMDD_HHMMSS.log`,目录不存在自动创建),`Destroy()` 销毁实例时恢复 `std::cout` 并关闭文件;开关注释关闭时不重定向、日志仍输出到标准输出;日志文件在 `CMakeLists.txt` 的 SOURCES 中注册。
- 新增 `RDP_LOG` 日志输出宏(`rdp_log.h`):自动在每条日志前附加 `[时间戳][代码文件:行号]` 信息后再输出到标准输出(重定向开启时随之写入日志文件),供后续新增日志统一使用。
- 更新 README.md(604f158、d6cdbc0):目录结构补充 `archived_docs/` 归档目录;9.6 节改写 TWS 关联 ΔT≤0 的处理说明;功能特性、模块职责、已知问题与参考文档的文档引用路径同步指向 `archived_docs/`;版本历史表补充 604f158、d6cdbc0 提交记录。
- TWS 关联新增**高度门限**(e0bf227,`track_asso.cpp`):原实现中分距离段的高度门限代码块整体为注释状态、`d_h` 恒为 1(等于不生效),现改为独立判定 `thd_h = |h_track − h_point|` 与航迹距离分段比较后再参与关联(分段值见 Changed),与 TAS 通道的高度门限取值一致。
- 关联门限判定重构为三个显式布尔量(e0bf227):`track_asso.cpp` 与 `track_asso_tas.cpp` 的 `model_filter()` 原先把统计距离、高度、距离三类判据混写在一个长 `if` 条件里(高度门限在 TWS 侧整体被注释),现分别计算 `thd_d`(统计距离门限,按距离分段)、`thd_h`(高度门限)、`thd_r`(TWS 距离门限)后再以 `thd_d && thd_h && thd_r`(TAS 为 `thd_d && thd_h`)判定候选,逻辑等价性更易核对;TWS 侧同时移除了 `manual_tracking_flag==0` 的注释残留代码块与其缩进。
- 更新 README.md(e0bf227):目录结构(第 3 节)删除已移除的 `docs/data_process_flow_report.html` 并补充 `design_docs/` 留档目录(被 .gitignore 忽略);2、9.6、9.10、10.1、11.1、12、13 节按源码同步 `ASSO_THORD = 4`(关联波门等效 d < 4、TWS 分段比例门限 16/1000 与 16/100)、TWS/TAS 高度门限的分段取值、`thd_d/thd_h/thd_r` 三类门限判定结构、TAS 波束数据率门控与 e0bf227 提交记录;13 节参考文档表删除已不存在的 HTML 行。
- 同步 `docs/data_process_flow_report.md`(e0bf227):4.2 节波门由「统计距离 + 距离 + 恒通过的俯仰门限」改为「统计距离 thd_d + 高度 thd_h + 距离 thd_r」并给出 TWS 高度门限分段表,7 节 TAS 高度门限 2000~3000 m 段由 200 m 更正为 250 m;7.2 节判据与 9.1 节参数表按源码更新(`ASSO_THORD = 4`、`d² < 16`);8.2 节改为单条件数据率门控并在选中目标后按 CPI 时间计算外推步长。
- 输出航迹补充「靠近/远离」方向标志 `Flag_Direction`(f249fdc):TWS 关联输出(`track_asso.cpp`)与 TAS 关联输出(`track_asso_tas.cpp`)在写入 `vr_point` 后追加 `Flag_Direction = vr_point > 0 ? 1 : 0`,即径向速度为正判为靠近(1)、为零或负判为远离(0)。该字段此前已在 `data_process_class_dll.h` 的 `Track` 结构体中声明(注释「方向标志 1 靠近/0 远离」)但从未被赋值,宿主读到的是未初始化/旧值。
- 新增三份流程与专项文档(f249fdc):`docs/data_process_flow_report.md`(数据处理流程与关键逻辑说明:总体数据流、前处理与判圈、凝聚、TWS/TAS 关联与滤波、航迹起始、外推消亡与航迹号管理、TAS 波束控制、参数脉络与关键约定)、`docs/data_process_flow_report.html`(同内容的 HTML 渲染版)、`docs/track_init_readme.md`(航迹起始 Track Init 详细逻辑说明:分阶段流程、起始条件汇总、参数调参、输出字段映射、已知风险与代码索引)。
- 更新 README.md(7d5a56f、f249fdc):目录结构新增 `docs/` 目录;2、7.2、8.1 节满圈判据改为「方位波位号 == 0」并说明 `last_beam_num` 不再参与判断;8.3、9.3 节改写点-航迹头关联判据与起批输出历史点数;9.6 节补充方向标志输出与点迹消费说明;2、10.1 节 `RDP_LOG_ENABLE` 说明改为当前实际状态(注释停用、默认不重定向);11.4 节新增航迹起始未修复观察;版本历史表补充 7d5a56f、f249fdc,参考文档表补充 `docs/` 三份文档。
- 新增手动重点目标接口(50e0b5c):`data_process_class_dll.h` 增加 `primary_target_add(int track_index)`(将指定批号转为重点目标)与 `primary_target_cancel(int track_index)`(取消重点目标)两个虚接口(基类为空实现、返回 0),`Data_Process` 中按批号把 `Trust_Track::primary_target_flag` 置 1 / 清 0;重点航迹的消亡改用独立阈值 `TRACK_DIE_ROUND_PRIMARY`(= 10,两体制相同),普通航迹仍用 `TRACK_DIE_ROUND`。
- 新增未关联航迹的外推圈数上限宏(50e0b5c,`parameters.h`):`MAX_EXTRAPOLATE_NUM = 3`(TWS 通道,两体制相同)、`MAX_EXTRAPOLATE_NUM_TAS`(TAS 通道,相扫 5 / 机扫 48)。
- 输入/输出结构新增「归一化幅度」与「速度维 FFT 点数」相关字段(e8c56f9):`DataRev` 增加 `amp_asv`(目标归一化幅度 dB)与 `prn_fftn`(速度维 FFT 点数);`Track` 增加 `amp_asv_point` / `prn_fftn_point`;内部 `PointRecv`、`Trust_Track` 同步增加对应字段,`track_asso.cpp` 在关联实点时写入航迹、TWS 输出航迹时写回输出结构。⚠ 当时输入侧未打通(该描述后于本次发布更正为「已打通」,见 Changed 末条):`data_preprocess()` 未把 `DataRev.amp_asv` / `DataRev.prn_fftn` 拷入内部 `PointRecv`;**TAS 通道至今未涉及这两个字段**,TAS 输出中它们仍无有效数据来源(详见 README 11.2)。
- 更新 README.md(本次文档更新):2 节补充重点目标/外推上限/输出门控与结构体字段扩展;3 节把 `requirements.md` 移回仓库根目录;5.2、6.1、6.2、7.1、8.1、8.2、9.6、9.9、10.1、11.1、11.2、12、13 节按源码同步 `Flag_Direction` 新判据、外推圈数上限、消亡圈数(普通 6 / 重点 10 / TAS 相扫 10、机扫 96)、`Track_Update_Flag` 输出门控、新增字段与结构体布局变更提示;12 节版本历史补充 50e0b5c、fc49707、42b598e、e8c56f9 四条记录。
- 同步 `docs/data_process_flow_report.md` 与 `docs/track_init_readme.md`(本次文档更新):前者更新总体数据流表(⑥ 外推/消亡)、4.7 外推、6.1 消亡条件、7.2 TAS 外推门控、9.1 参数表与第 10 节观察;后者更新第 14 节 `Flag_Direction` 判据并补充 `amp_asv_point` / `prn_fftn_point` 两个未赋值字段。
- **新增数据记录调试功能(可选编译)**:新增 `rdp_record.h`(`rdp_record::Init()/Shutdown()/db_path()` 与 `RDP_RECORD`/`RDP_RECORD_BATCH` 记录点宏;功能关闭时宏展开为 `((void)0)`、实参不求值,头文件不含任何第三方依赖)、`rdp_record_entity.h`(记录实体=数据表定义区,含 2 个示例实体与 `RDP_RECORD_DECLARE` 实例化声明宏)、`rdp_record.cpp`(实体注册区:`YLT_REFL` + `RDP_RECORD_DEFINE`;pimpl 隔离 ormpp/SQLite;首次写入自动 `CREATE TABLE IF NOT EXISTS`;单条写入与 `std::vector` 批量事务写入;建表/插入失败每个实体只提示一次并降级为空操作;内部互斥锁串行化单连接)。`data_process_class_dll.cpp` 的 `Data_Process_Factory::GetB()/Destroy()` 分别调用 `rdp_record::Init()/Shutdown()`(关闭时为空实现),`parameters.h` 新增记录目录宏 `RDP_RECORD_DB_DIR`(`.\rdp_records\`),`.gitignore` 增加 `rdp_records/`。(实体定义区当时的「2 个示例实体」已扩展为 7 个,见本版 Added 首条。)
- 新增 2 个示例记录点(`data_process.cpp`,字段为示例可按需增删):① `data_preprocess()` 的 TWS 分支逐点写入表 **`RdpRecPointTws`**(输入点迹字段:CPI/GNSS 时间 ms、方位/俯仰波位、PRF/频点、点迹号/总数、距离/方位/俯仰/速度/幅度/SNR/RCS 等,当前实体为 22 个字段);② `track_process()` 的 TWS/TAS 处理帧结束后批量写入表 **`RdpRecTrackOut`**(输出航迹 20 个字段,两种模式共用一张表并用 `track_mode` 区分;每行取输出数组 `[i][0]`,行数超过 `MAX_TRACK_NUM` 时钳位)。⚠ 这两个实体名已随 a4f32e4 / b227ae1 演进:输入点迹表更名并扩展为 `RdpRecPoint`(TWS/TAS 共用、增 `point_id`/`frame_id`/`circle_id`),输出航迹示例整体停用、改由 `RdpRecTrustTrack` 记录内部航迹(详见本版 Added 首条的 6 张在用表清单)。
- 内置第三方源码 `third_party/ormpp/`(按上游原样保存,含 `UPSTREAM.md` 记录来源与升级方式):ormpp 0.2.0(commit `acf23989…`,Apache-2.0)+ 随其内置的 iguana 1.0.5、frozen 与 SQLite 3.49.1 amalgamation(公有领域),并保留两份 LICENSE 文件(与上游 git blob 逐字节一致)。
- 新增 `docs/data_record_debug.md`(数据记录调试功能专项说明):开关与构建(`RDP_DATA_RECORD` 取值、`configure.bat` 第 4 参数、记录模块以 C++20 独立编译与 C 编译器要求、关闭时零代码保证)、运行期行为(记录库目录与文件名、`synchronous=OFF` 取舍、`Init`/`Shutdown` 时机、失败降级、单连接 + 互斥锁与线程模型)、记录实体(表)约定(全局命名空间、结构体名即表名、首列 `int64_t id` 自增主键、类型映射表、**禁止数组字段**及原因)、**新增记录点四步配方**(含 `RDP_RECORD_DECLARE` → `YLT_REFL` + `RDP_RECORD_DEFINE` → 记录点写法 → 自动建表,以及「忘记注册报 LNK2019」自检点)、当时的 2 个示例记录点(现已扩展为 6 张在用表 + 1 个停用实体,见本版 Added 首条)、Python/DB Browser 查询示例与已知限制。
- 更新 README.md(本次文档更新):2 节新增「数据记录调试功能(可选编译)」特性条目;3 节目录结构补充 `third_party/ormpp/`、`rdp_record.h/.cpp`、`rdp_record_entity.h`、`docs/data_record_debug.md`、`rdp_records/` 并更新 `requirements.md` 说明;4.1 节环境要求补充内置依赖与 C 编译器;4.3 节补充 `RDP_DATA_RECORD` 与 `configure.bat` 第 4 参数用法;4.4 节产物补充 `rdp_record.lib`;4.5 节新增「第三方库告警例外(仅记录功能启用时)」;5.2 节模块职责新增 `rdp_record` 行;10.1 节编译期宏新增 `RDP_RECORD_ENABLE` / `RDP_RECORD_DB_DIR`;11.3 节补充「记录功能默认只在 Debug 启用」;12 节版本历史新增本次工作区条目;13 节参考文档新增 `docs/data_record_debug.md` 与 `third_party/ormpp/UPSTREAM.md`、更新 `requirements.md` 行。
### Changed
- TAS 波束控制增加数据率门控:`Beam_Ctrl()` 仅在队首目标航迹时间 `T_track` 与最新时间戳 `latest_timestamp` 的差值大于 `1 / DATA_RATE_TAS` 秒时输出 TAS 跟踪波束,使 TAS 目标数据率不再随波束时间(Beam_Ctrl 调用频率)变化;队列轮转逻辑保持不变。
- 波束门控使用 `Work_Parameter.DATA_RATE_TAS`(运行时参数)而非 `parameters.h` 中的 `DATA_RATE_TAS` 宏;参数 ≤ 0 时不做门控,保持原输出行为。
> 以下条目为上一轮「工作区(未提交)」状态的改动,随本次 1.5.6 发布一并归档(内容未改写)。
- TAS 波束控制改为按固定数据率调度:`Beam_Ctrl()` 仅当目标满足 `1 / DATA_RATE_TAS` 秒的数据率间隔(相对航迹时间 `T_track` 与上次波束输出 `last_track_time`)时才输出 TAS 跟踪波束,使 TAS 目标数据率不再随波束时间(Beam_Ctrl 调用频率)变化。
- `CMakeLists.txt` 新增缓存变量 `RDP_DATA_RECORD`(`AUTO`/`ON`/`OFF`,AUTO = Debug 启用 / Release 关闭;多配置生成器下 AUTO 直接 `FATAL_ERROR` 要求显式取值)与可选静态库 `rdp_record`:该目标以 `CXX_STANDARD 20` 独立编译(ormpp 要求 `CMAKE_CXX_STANDARD 20`,而内置 Eigen 3.3.7 在 `/std:c++20` 下会因 `std::result_of` 被 MSVC 移除而编译失败,故**雷达处理代码保持 C++17**)、经 `enable_language(C)` 编译内置 sqlite3 amalgamation(`sqlite3.c` 以源文件级 `/w` 单独降级第三方告警)、`RDP_RECORD_ENABLE` 与 `ORMPP_ENABLE_SQLITE3` 经 `PUBLIC` 传递给 DLL 并将静态库链接进 DLL;关闭时不添加任何目标、`SOURCES` 列表保持不变(Release 零痕迹的关键)。配置阶段打印启用/关闭状态。
- `parameters.h` 新增记录库目录宏 `RDP_RECORD_DB_DIR`(`.\rdp_records\`,相对宿主进程当前工作目录,目录不存在时自动创建)与开关说明注释:记录开关由构建系统定义 `RDP_RECORD_ENABLE`,**不提供手工开关宏**(避免 Release 误开)。
- `configure.bat` 新增可选第 4 参数 `AUTO|ON|OFF`(缺省 `AUTO`,即 CMake 侧默认值)并作为 `-DRDP_DATA_RECORD` 传入,回显新增 `record` 行;脚本仍保持纯 ASCII 编码。
- `.gitignore` 增加 `rdp_records/`(数据记录库输出目录)。
- `requirements.md` 内容更新为数据记录调试功能需求(结构体经 ormpp 映射写入 SQLite、Release 版可屏蔽、数据分类入不同表、宏记录点、便于快速新增结构体、不使用异步写入),本功能框架已按该说明实现。
- 本次改动的编译与运行验证:X256_PS/X256_MS × Release(记录关闭)/Debug(记录启用)四种配置均 **0 warning、0 error**;Release 产物的导出表与依赖表与改动前**逐行一致**、DLL 体积不变(PS 178,688 / MS 178,176 字节)、构建树中无记录模块目标文件、二进制内无 `sqlite` 字样;Debug(AUTO→启用)下 DLL 产物为 3,611,648 字节并额外生成 `rdp_record.lib`;运行时用临时校验程序(不提交)配合 Python `sqlite3` 验证:建库成功、一实体一表、单条与批量(事务)写入行数与取值正确、`id` 自增、`PRAGMA integrity_check` = ok、无 `-journal`/`-wal` 残留;记录目录无法创建时 DLL 接口照常返回、仅打印一次错误、后续写入静默忽略。
- TAS 波束输出目标改为队列内调度:不再固定取队首,而是遍历 TAS 队列,选择满足数据率门控且 CPI 时间最早的目标输出;原每次 Beam_Ctrl 末尾的整队循环移位(队列轮转)已停用(代码注释保留)。
- 数据率门控统一使用运行时参数 `Work_Parameter.DATA_RATE_TAS`(由 DLL 调用者经 `track_process_parameters_initial()` 输入,需为 >0 的有效值);`parameters.h` 中 `DATA_RATE_TAS` 编译期宏(相扫 0.3 / 机扫 0.0625)已注释停用,引导跟踪外推同步改用运行时参数。
- 编译时以 `#pragma message` 提示当前启用的扫描体制宏(MECHANICAL_SCANNING / PHASE_SCANNING)。
- 频点宏由 `FREQ0~FREQ20`(16.8 GHz)精简为单个 `FREQ0`,取值改为 9.2 GHz(e729c5b)。
- MSVC 编译选项按编译器版本条件追加 `/utf-8`(VS2015 及以上);MSVC 2013 不支持该选项,改用源码文件带 BOM 的 UTF-8 编码解决 C4819。
- 存量调试输出收敛为告警级 `RDP_LOG` 日志(1521592):`track_asso.cpp` / `track_asso_tas.cpp` 的 `model_filter()` 在 ΔT ≤ 0(乱序/重复时间戳)分支新增告警日志(含航迹/点迹/时间差信息,其中 `track_asso.cpp` 侧后于 604f158 注释停用);`tas_ctrl.cpp` 中 TAS 波束最早目标调度的 `printf` 调试输出移除,`data_process.cpp` 手动删除与 `track_die_tas.cpp` TAS 目标消亡的 `std::cout` 调试输出注释停用。重定向开启时上述日志随 `std::cout` 一并写入日志文件。
- `RadarPara.DATA_RATE_TAS` 单位由 Hz(1/s)改为 s(秒)(1521592):`tas_beam_output()` 的 TAS 波束输出门控由 `1 / DATA_RATE_TAS` 秒改为直接按 `DATA_RATE_TAS` 秒比较,`data_process_class_dll.h` 字段注释同步更新;调用者改按「波束输出间隔(秒)」输入该参数(原 0.3 Hz 的含义不再适用,需按新单位换算后传入)。
- requirements.md 内容更新为「雷达数据处理项目修改说明」(1521592):现描述日志文件输出功能需求(rdbuf() 重定向、日志开关宏、日期+时间命名日志、前缀日志宏,附参考示例)。
- TWS 点航关联中 ΔT ≤ 0(点迹 CPI 时间不晚于航迹时间,如 TAS 航迹遇上时间戳更早的 TWS 点迹、或重复时间戳)的候选不再被剔除(604f158):此类候选仍参与最近邻最小 d 的比较与选取,但不执行 IMM/EKF 更新、不刷新航迹时间与新鲜度(`T_track`、`Extrapolate_round`、`point_flag`、`associate_point_number` 均保持原值,该航迹仍按未关联航迹外推),同时把该点迹标记为已使用(`Use_Flag=1`)消费掉,使其无法再参与航迹起始,避免由此生成虚假 TWS 航迹;`track_asso.cpp` 中 ΔT≤0 的 `RDP_LOG` 告警日志随之注释停用(`track_asso_tas.cpp` 中同名告警日志仍启用)。
- 过期文档归档(604f158):`BUG_REPORT.md`、`BUG_FIX_REPORT.md`、`requirements.md` 由仓库根目录移入 `archived_docs/` 留档,仓库内对它们的引用路径同步改为 `archived_docs/...`。
- 扫描一圈完成判据改为「方位波位号 == 0」(7d5a56f):原判据「当前方位波位号 < 上一帧波位号」在机扫模式下遇到转台反转时波位号会变小但并未扫完一圈,造成误判;现 `data_preprocess()` 仅在 `Data_Input[0].Beam_index_aiz == 0` 时返回 1(TWS 满一圈,执行航迹处理),否则返回 2(继续蓄数据,只做滑窗凝聚)。原判据代码注释保留,`last_beam_num` 仍被赋值但不再参与判断。调用方需保证点迹的方位波位号按 0~Beam_num-1 编号且每圈必然出现 0。
- 点迹-航迹头关联判据改为统一速度区间并增加多普勒-几何一致性约束(7d5a56f,`track_init.cpp`):原先按扫描体制与距离分段收紧速度门限的分支(近程 `r≥1000` 全速 / `r<1000` 半速,中远程 `r≥2000` 全速 / `r<2000` 取 1/2~1/3 速)整体注释停用,改为 `dis ≤ V_MAX·ΔT` 且 `dis ≥ V_MIN·ΔT`,同时要求 `vr_point·v_track_head > 0`,并新增 `dis ≥ |vr_point|·ΔT` 与 `dis ≥ |v_track_head|·ΔT` 两条约束——切向高速目标在 ΔT 内的实际位移远小于 `|vr|·ΔT`,据此被排除,用于限制由切向高速点迹起批出的短航迹。其中 `vmax` 的近程/中远程分支取值相同,均取 `Work_Parameter.V_MAX`。
- 同步 `docs/` 内两份专项文档(本次文档更新):`docs/data_process_flow_report.md` 附录与 `docs/track_init_readme.md` 第 12.1 节的「与 README 的差异」说明改为「差异已按结论同步到 README」并保留更新前的旧表述作为改动缘由;其中「起始输入过滤」一行更正为「上游 `track_asso_process()` 已删除 `Use_Flag==1` 的点迹 + `track_init` 入口再次剔除」,「初始化用点」一行更正为「取下标 `track_start_point_num-3 ~ -1`,航迹更长时不是真正的末三点」;`docs/track_init_readme.md` 第 15 节第 2 条相应更正(`track_init.cpp` L505 的 `L` 覆盖为 `track_start_point_num`,第 L 个点之后的尾部点既不参与初始化也不输出),第 14 节补充说明 `Flag_Direction` 仅由关联输出路径赋值、起批输出不写该字段。
- 关联波门 `ASSO_THORD` 由 3 调大至 4(e0bf227,`parameters.h` 相扫/机扫两组宏同步):判据写作 `d*d < ASSO_THORD*ASSO_THORD`,而 `d` 本身已是马氏距离平方,等效门限由 `d < 3` 放宽为 `d < 4`(TWS 分距离段的比例门限随之由 9/1000、9/100 变为 16/1000、16/100),配合高度门限判定提高机动目标的关联概率;调参时仍需注意这一层「二次方」语义(BUG-20 有意保留)。
- TWS 与 TAS 关联的高度门限统一为同一组按航迹距离分段的取值(e0bf227):`r_track ≤ 1000 m` 时 `|h_track − h_point| ≤ 150 m`、`1000~2000 m` 时 ≤ 200 m、`2000~3000 m` 时 ≤ 250 m、`3000~5000 m` 时 ≤ 300 m、`> 5000 m` 不约束。其中 TWS 侧为该门限首次生效(原代码块被注释),TAS 侧 `2000~3000 m` 段由 200 m 放宽到 250 m。
- TAS 波束输出的数据率门控条件修正(e0bf227,`tas_ctrl.cpp::tas_beam_output()`):原判据同时要求「距上次波束输出超过 DATA_RATE_TAS」与「航迹时间 T_track 与最新时间戳之差超过 DATA_RATE_TAS」,后者与「选取 CPI 时间最早目标」的语义冲突,会额外抑制本可输出的目标;现仅保留 `delta_T_last_track = (latest_timestamp − last_track_time)/1000 > DATA_RATE_TAS` 一条门控,`delta_T` 改为在选中该目标后按 `(latest_timestamp − CPI_time)/1000` 计算并用于外推,使 TAS 目标输出间隔严格跟随数据率。
- 输出航迹改为「只输出本圈有更新的航迹」(50e0b5c):TWS `track_asso_process()` 与 TAS `track_asso_process_tas()` 的输出循环新增 `Track_Update_Flag == 1` 门控,输出后立即清零;该标志在关联到实点或外推成功时置 1。此前每个 TWS 周期都会把航迹表中的全部可靠航迹各输出一行(`Point_Sum = 1`),宿主不能再据输出行数推断航迹总数(当圈新起批的航迹仍由 `track_init` 的输出路径直接写入)。
- TWS 未关联航迹外推增加圈数上限(50e0b5c,`track_asso.cpp::model_filter()`):仅当 `Extrapolate_round <= MAX_EXTRAPOLATE_NUM`(=3)时按 `DATA_RATE_SHORT/MIDDLE/FAR` 预测一帧、推进航迹时间并置 `Track_Update_Flag = 1`;超过上限后不再推进位置与时间,只累加 `Extrapolate_round`、置 `point_flag = 0` 与 `Track_Update_Flag = 0`(该航迹此后不再输出,位置冻结在最后一次预测处),直到重新关联到实点才恢复。避免长时间未关联的航迹被反复外推到远处。
- TAS 未关联航迹外推同样增加上限(50e0b5c,`track_asso_tas.cpp`),但判定方向与 TWS 相反:`Extrapolate_round >= MAX_EXTRAPOLATE_NUM_TAS`(相扫 5 / 机扫 48)时才做预测、推进航迹时间并输出,未达该轮数时只累加 `Extrapolate_round` 且不输出(位置与航迹时间停在最后一次关联处),达到上限后才恢复逐帧外推输出(见 README 11.2 的观察)。
- 航迹消亡圈数与判据调整(50e0b5c,`parameters.h`、`track_die.cpp`、`track_die_tas.cpp`):`TRACK_DIE_ROUND` 由 4 调整为 6(两体制),新增重点航迹专用 `TRACK_DIE_ROUND_PRIMARY = 10`,`TRACK_DIE_ROUND_TAS` 相扫由 5 调整为 10(机扫保持 96);消亡判据由 `Extrapolate_round >= 阈值` 改为 `> 阈值`,并把 `manual_delete_flag == 1` 与轮数条件合并为统一的 `die_flag` 判定,TWS 侧按 `primary_target_flag` 在 6 / 10 之间二选一。因每圈未关联只自增 1,普通 TWS 航迹实际连续 7 圈无实点关联才消亡(重点航迹 11 圈,TAS 相扫 11 圈 / 机扫 97 圈)。
- 「靠近/远离」方向标志 `Flag_Direction` 改为按滤波速度矢量判定(fc49707 引入、42b598e 调整,`track_asso.cpp` 与 `track_asso_tas.cpp` 同步):不再使用受多普勒速度模糊影响、符号可能取反的实测径向速度(原 `Flag_Direction = vr_point > 0 ? 1 : 0`),改为用航迹 IMM 融合状态的速度方向角 `Direction_Angle = atan2(X[4], X[1])` 与位置矢量判定:`acos((-x·cos(Direction_Angle) − y·sin(Direction_Angle)) / r) < π/2` 时置 1(靠近),否则 0(远离),其中 `x`/`y` 为经 `Sys_delay` 补偿后的位置、`r` 为其模。该判据等价于「滤波速度矢量的径向分量指向雷达(径向接近)即判为靠近」;中间版本曾按合速度归一化并把门限取为 π/4(45° 锥角),42b598e 去掉归一化并把门限放宽到 π/2(90°)。
- 手动操作接口遍历命中目标后立即 `break`(fc49707,`data_process.cpp`):`primary_target_add` / `primary_target_cancel` / `tracking_start` / `tracking_stop` 在写入标志后退出循环,不再继续扫描整张航迹表(批号唯一,结果等价)。
- `requirements.md` 由 `archived_docs/` 移回仓库根目录(工作区改动,文件内容未变):README 目录结构(第 3 节)与参考文档表(第 13 节)中的引用路径同步改回 `requirements.md`。
- **构建工具链升级:编译器由 Microsoft Visual C++ 2013(Visual Studio 12,`_MSC_VER` 1800,x86)升级为 Microsoft Visual C++ 2022(v143 工具集,`_MSC_VER` 1944,cl 19.44.35228,x64),C++ 标准由 C++11 提升为 C++17**。`CMakeLists.txt` 的 `cmake_minimum_required` 由 3.10 提升到 3.21(VS2022 生成器自 3.21 起支持),新增 `CMAKE_CXX_STANDARD 17`(含 `CMAKE_CXX_STANDARD_REQUIRED ON`)与「必须使用 MSVC 2022(`_MSC_VER >= 1930`)」的配置期强校验(不满足直接 `FATAL_ERROR`),并在配置阶段打印实际编译器路径、`_MSC_VER` 与所选 C++ 标准。
- `CMakeLists.txt` 编译选项调整:`/utf-8` 由「MSVC >= 1900 条件追加」改为对 MSVC 无条件启用;新增 `/Zc:__cplusplus`(MSVC 默认把 `__cplusplus` 报为 `199711L`,加该选项后正确报 `201703L`,使依赖该宏判断的第三方库——内置 Eigen 3.3.7——走标准分支);显式追加 `/W3`(CMake 3.15 起策略 CMP0092 不再把 `/W3` 放入 `CMAKE_CXX_FLAGS`,显式恢复以保持原有警告级别);`/Zc:wchar_t`、`/FS` 与 Release 专用的 `/Zc:strictStrings` 保持不变。
- **全部构建脚本改为 MSVC 2022**:新增 `.vscode/scripts/msvc_env.bat`(定位 VS2022 工具集与 `cl.exe`、校验路径存在后调用 `vcvarsall.bat x64`,可用 `VS2022_DIR` / `MSVC_TOOLSET_VERSION` / `MSVC_HOST_ARCH` / `MSVC_TARGET_ARCH` 环境变量覆盖默认值),`configure.bat` / `build.bat` / `clean.bat` 统一改为调用该脚本并显式传入 `-DCMAKE_CXX_COMPILER`;四个脚本一律保持**纯 ASCII 编码**(cmd.exe 按 OEM 代码页解析 .bat,中文注释会破坏其解析)。
- `configure.bat` 新增「旧工具集构建树自动清理」:构建目录存在 `CMakeCache.txt` 但缺少 `.msvc_toolset` 标记(或标记与当前工具集版本不一致)时,先整体删除该目录再重新配置。原因是 MSVC 2013 的 CMake 缓存与目标文件不能跨编译器复用——nmake 仅按时间戳判断,会把旧目标文件当作最新而跳过重编译,导致新旧工具集目标文件混链。
- VS Code 工程配置同步为 MSVC 2022 x64 + C++17:`settings.json` 的 `cmake.environment` 改为 VS2022 v143 x64 与 Windows SDK 10.0.26100.0 的 `PATH`/`INCLUDE`/`LIB`,新增 `cmake.configureArgs` 固定 `cl.exe` 路径,`C_Cpp.default.cppStandard` 改为 `c++17` 并新增 `C_Cpp.default.intelliSenseMode`;`c_cpp_properties.json` 的 `compilerPath` 指向新 `cl.exe`、`intelliSenseMode` 改为 `windows-msvc-x64`、配置名改为「X256_PS/X256_MS (MSVC2022 x64)」;`cmake-variants.yaml` 与 `tasks.json` 的变体/任务描述同步为 MSVC2022 x64。
- 产物架构由 x86 变为 **x64(AMD64)**:宿主程序必须为 64 位、并使用同一版本头文件重新编译后才能加载新 DLL(`rdp_playback.exe` 回放工具同样需升级为 x64 版本)。
- TWS 关联中 ΔT 判定的分支结构理顺(b227ae1,`track_asso.cpp::model_filter()`):原写法为 `if (delta_T <= 0) { 空分支 } else { IMM/EKF 更新 }`,现改为 `if (delta_T > 0) { 置 min_d_flag、IMM/EKF 更新 }`,行为与原来等价(ΔT > 0 才做滤波更新,ΔT ≤ 0 仍只消费点迹),但空分支被移除、可读性提高;原 ΔT≤0 的告警日志注释块一并删除。
- 记录点由「输出结构」改为「内部结构」(b227ae1):`data_process.cpp` 中按输出数组批量记录航迹的辅助函数 `rdp_record_output_tracks()` 与 `RDP_RECORD_BATCH` 调用整体移除,改在 `track_asso.cpp` 的输出循环内对内部 `Trust_Track` 逐条 `RDP_RECORD(RdpRecTrustTrack(...))`——记录的是航迹的完整内部状态(含 IMM 的 `X`/`P`/`X1`~`X3`/`P2`/`P3`/`u`),不再受输出数组容量与列映射的限制;范围仍限于本圈有输出(`Track_Update_Flag==1`)的航迹,且仅 TWS 通道。
- `Temp_track` 字段命名规范化(c3c7e66,`struct.h`):`snr` → `SNR`、`Amp` → `amplitude`、`T` → `CPI_time`、`Temp_track_section_idx` → `temp_track_section_idx`,并新增 `temp_track_id` / `point_id` / `circle_id` / `pitch_num`;`track_init.cpp`、`track_init_direct_tracking.cpp` 中的引用同步更名(含注释停用代码)。
- 输入点迹的两个「新字段」已打通(实际始于 a4f32e4 的 `data_process.cpp` 改写,本次发布确认并更正文档):`data_preprocess()` 现已把 `DataRev.amp_asv` / `DataRev.prn_fftn` 拷入内部 `PointRecv`(TWS 与 TAS 两条分支均赋值),并随 `RdpRecPoint` 落库;此前 README/CHANGELOG 记录的「输入侧未打通、输出字段无有效数据来源」已不成立(TAS 关联/输出路径仍未写回 `Track` 的这两个字段,见 11.2)。
### Fixed
- 修正文档中「`data_preprocess()` 未搬入 `amp_asv` / `prn_fftn`」的过期描述(本次文档更新):README 11.2、`docs/data_process_flow_report.md`、`docs/track_init_readme.md` 三处同步更正为「已搬入」,并保留「TAS 输出路径未写回」这一仍然成立的部分。
- 修复 MSVC `/W3` 下全部 C4819 / C4018 / C4244 编译警告:源码文件改为带 BOM 的 UTF-8 编码;signed/unsigned 不匹配处改用 `size_t` 循环变量或显式 `static_cast<int>` 比较;double/float、__int64/double 等窄化转换改为 `static_cast` 显式转换,保持原有计算逻辑不变(涉及 `data_process.cpp`、`dot_coh.cpp`、`dot_coh_tas.cpp`、`tas_ctrl.cpp`、`track_asso.cpp`、`track_asso_tas.cpp`、`track_init.cpp`、`track_init_direct_tracking.cpp`、`track_index_mangement.cpp` 等)。
- 修复 `TAS_Ctrl::tas_beam_output()` 把航迹向量下标 `earliest_track_idx` 误赋给 `Tracking_beam->TAS_track_index` 的问题(1521592):现输出实际航迹号 `(*trust_track)[earliest_track_idx].Track_Index`,避免跟踪波束批号与目标不符。
- 修正 README.md(第 2、5.2、8.1、8.3、9.3、13 节)与 `docs/data_process_flow_report.md`(第 0、1、6.1~6.4 节、附录)中航迹起始的步骤顺序描述(纯文档勘误,无代码改动):两份文档原写作「航迹头(1 点)→ 点-航迹头关联(2 点)→ 点-临时航迹关联(≥3 点)→ 起批」,与 `track_init.cpp`(L49-126)每圈的实际执行顺序相反;现更正为「① 点迹先与 ≥2 点临时航迹关联(延长已有临时航迹)→ ② 再与 1 点航迹头关联(两点卡尔曼初始化出 2 点临时航迹)→ ③ 两个阶段都结束后统一删除被消费(`Use_Flag==1`)的点迹、剩余点迹逐条转为新航迹头 → ④ 长度达标的临时航迹经三点卡尔曼初始化转为可靠航迹」,并同步修正流程图与状态机;另补充两点此前的描述缺失:两个关联阶段互不排斥(航迹头阶段不检查临时航迹阶段留下的 `Use_Flag`,同一点迹可同时延长一条临时航迹并开启一条 2 点临时航迹)、两类关联都不做一对一分配(同一条临时航迹可被同圈多个点延长)。
- 修复速度模糊时「向站/背站」判断有误的问题(fc49707、42b598e):`Flag_Direction` 不再由受多普勒模糊影响、符号可能取反的实测径向速度 `vr_point` 决定,改为按航迹滤波速度矢量与雷达视线的几何关系判定(判据见 Changed),TWS、TAS 两通道行为一致。
- 修复升级到 MSVC 2022 后在 `/W3` 下新增报出的 **17 处 C4267**(`size_t` → `int` 窄化,可能丢失数据)告警:`track_asso.cpp`(7 处)、`track_init.cpp`(5 处)、`track_init_direct_tracking.cpp`(4 处)、`track_asso_tas.cpp`(1 处)、`tas_ctrl.cpp`(1 处)按项目既有约定改为 `static_cast<int>(...)` 显式转换,不改变任何计算逻辑、对外接口与结构体布局;被编辑的源文件均已恢复「带 BOM 的 UTF-8」编码,与仓库其余文件保持一致。修复后 X256_PS/X256_MS × Release/Debug 四种配置均为 **0 warning、0 error**。
### Removed
- 删除已注释停用的无用代码残留(d6cdbc0):`data_process.cpp` 中 TWS/TAS 分支的 `std::cout` 调试输出注释块,以及 `dot_coh.cpp` 中早期「1 km 以内 SNR < 20 dB 点迹过滤」的注释代码块。仅清理注释,不改变任何运行逻辑。
- 删除 `docs/data_process_flow_report.html`(e0bf227):该 HTML 渲染版与 `docs/data_process_flow_report.md` 内容重复且已明显滞后,保留价值不足;同内容的 HTML 已在 git 忽略的 `design_docs/` 内另行留档。README 目录结构、参考文档表与版本历史表中指向该文件的条目同步移除。
- 删除 `track_asso.cpp` TWS 输出循环中已注释停用的 `manual_tracking_flag == 0` 过滤代码块(fc49707):该过滤从未生效,删除后 TWS 输出仍会把手动转 TAS(`Track_Mode==1`)的航迹一并输出(BUG-25 未修复,见 README 11.2)。
## [1.5.5] - 2026-08-27
@@ -46,11 +142,11 @@
### Fixed
- 修复若干逻辑 BUG(数组越界/下溢、局部变量未初始化、时间差无效、方位角 ±π 环绕、IMM 概率奇异、输出数组容量越界等),明细见 `BUG_FIX_REPORT.md`。
- 修复若干逻辑 BUG(数组越界/下溢、局部变量未初始化、时间差无效、方位角 ±π 环绕、IMM 概率奇异、输出数组容量越界等),明细见 `archived_docs/BUG_FIX_REPORT.md`。
- 修复 TAS 外推时间差可能为无效值的问题。
- 修复航迹起始时未输出跟踪类型、可靠航迹手动转跟踪标志未清零的问题。
- 修复量测噪声协方差矩阵 R 赋值错误、数据预处理波位号判断规则、点迹数量上限、雅可比矩阵取值、起批负索引等问题。
### Removed
- 移除 `requirements.md`、`CLAUDE.md`、Makefile 系列、Qt 工程用户文件、内置 Eigen 3.3.7 冗余文件及「航迹点迹区分区说明」等不必要文件。
- 移除 `requirements.md`(93d3924 移除,后于 b3e2f52 重新加入并改为 TAS 波束控制说明)、`CLAUDE.md`、Makefile 系列、Qt 工程用户文件、内置 Eigen 3.3.7 冗余文件及「航迹点迹区分区说明」等不必要文件。
+132 -8
View File
@@ -1,8 +1,13 @@
cmake_minimum_required(VERSION 3.10)
cmake_minimum_required(VERSION 3.21)
# 雷达数据处理动态链接库
# 开发环境已由 Qt/qmake 迁移到 VS Code + CMake。
# 编译器保持为 Microsoft Visual C++ Compiler 12.0 (x86)。
# 编译器已由 Microsoft Visual C++ Compiler 12.0 (VS2013) 升级为
# Microsoft Visual C++ Compiler 2022(v143 工具集,_MSC_VER 19.4x,x64)。
# 语言标准同步提升为 C++17。
#
# 注意:VS2022 生成器("Visual Studio 17 2022")需要 CMake >= 3.21,
# 因此本工程的最低 CMake 版本随之提高;NMake/Ninja 生成器同样满足。
project(data_process_class_dll LANGUAGES CXX)
# 扫描模式:
@@ -16,6 +21,68 @@ if(NOT RADAR_SCAN_MODE STREQUAL "PHASE_SCANNING" AND NOT RADAR_SCAN_MODE STREQUA
message(FATAL_ERROR "RADAR_SCAN_MODE 必须为 PHASE_SCANNING 或 MECHANICAL_SCANNING,当前值:${RADAR_SCAN_MODE}")
endif()
# ---------------------------------------------------------------------------
# 数据记录调试功能开关(可选编译,详见 docs/data_record_debug.md)
# AUTO : Debug 构建启用、Release 构建关闭(默认;交付构建不做任何记录)
# ON : 强制启用(可用于 Release 排查,需自行承担性能开销)
# OFF : 强制关闭
# 启用时新增独立静态库 rdp_record:以 C++20 编译(ormpp 要求)、编译内置的 sqlite3
# amalgamation,并链接进 DLL;源码中的 RDP_RECORD 宏由 RDP_RECORD_ENABLE 控制,关闭时
# 展开为空操作(实参不求值),记录模块完全不参与编译。
# 依赖来源与升级方式见 third_party/ormpp/UPSTREAM.md。
# ---------------------------------------------------------------------------
set(RDP_DATA_RECORD "AUTO" CACHE STRING "数据记录调试功能:AUTO(Debug=ON/Release=OFF) / ON / OFF")
set_property(CACHE RDP_DATA_RECORD PROPERTY STRINGS AUTO ON OFF)
if(RDP_DATA_RECORD STREQUAL "AUTO")
if(CMAKE_CONFIGURATION_TYPES)
message(FATAL_ERROR "多配置生成器下 RDP_DATA_RECORD=AUTO 无法判定,请显式传入 ON 或 OFF")
elseif(CMAKE_BUILD_TYPE STREQUAL "Debug")
set(RDP_RECORD_ENABLED TRUE)
else()
set(RDP_RECORD_ENABLED FALSE)
endif()
elseif(RDP_DATA_RECORD STREQUAL "ON")
set(RDP_RECORD_ENABLED TRUE)
elseif(RDP_DATA_RECORD STREQUAL "OFF")
set(RDP_RECORD_ENABLED FALSE)
else()
message(FATAL_ERROR "RDP_DATA_RECORD 必须为 AUTO / ON / OFF,当前值:${RDP_DATA_RECORD}")
endif()
if(RDP_RECORD_ENABLED)
message(STATUS "数据记录调试功能:启用(C++20 静态库 rdp_record + 内置 SQLite)")
else()
message(STATUS "数据记录调试功能:关闭(不编译记录模块,RDP_RECORD 为空操作)")
endif()
# ---------------------------------------------------------------------------
# 语言标准:C++17
# ---------------------------------------------------------------------------
# CMAKE_CXX_STANDARD 由 CMake 转换为 MSVC 的 /std:c++17(CMake >= 3.10 起支持)。
# CMAKE_CXX_EXTENSIONS OFF 在 MSVC 下没有对应开关(只影响 GCC/Clang 的 -std=gnu++xx),
# 保留它是为了在换用其它编译器时同样不启用语言扩展。
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
# ---------------------------------------------------------------------------
# 编译器约束:必须使用 MSVC 2022(v143,_MSC_VER >= 1930)
# ---------------------------------------------------------------------------
if(NOT MSVC)
message(FATAL_ERROR "本工程仅支持 MSVC (cl.exe) 编译器,当前编译器:${CMAKE_CXX_COMPILER_ID}")
endif()
if(MSVC_VERSION LESS 1930)
message(FATAL_ERROR
"本工程要求 MSVC 2022 (v143) 及以上版本(_MSC_VER >= 1930),"
"当前 _MSC_VER=${MSVC_VERSION}(编译器:${CMAKE_CXX_COMPILER})。\n"
"请先运行 VS2022 的 vcvarsall.bat x64(见 .vscode/scripts/msvc_env.bat)后再配置。")
endif()
message(STATUS "编译器:${CMAKE_CXX_COMPILER}(MSVC _MSC_VER=${MSVC_VERSION})")
message(STATUS "C++ 标准:C++${CMAKE_CXX_STANDARD}")
# 导出编译命令,便于 VS Code C/C++ 插件 IntelliSense 使用。
# Visual Studio 生成器不支持该选项,因此仅对 Makefile/Ninja 生成器开启。
if(NOT CMAKE_GENERATOR MATCHES "Visual Studio")
@@ -23,6 +90,7 @@ if(NOT CMAKE_GENERATOR MATCHES "Visual Studio")
endif()
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/dot_coh.cpp
@@ -46,6 +114,49 @@ set(PUBLIC_HEADERS
add_library(data_process_class_dll SHARED ${SOURCES})
# ---------------------------------------------------------------------------
# 数据记录模块(RDP_DATA_RECORD 启用时构建):独立静态库、C++20(ormpp 要求),
# 与雷达处理代码(C++17)通过 rdp_record.h 的「仅声明 + 显式实例化」跨标准边界。
# ---------------------------------------------------------------------------
if(RDP_RECORD_ENABLED)
enable_language(C) # 用于编译 sqlite3 amalgamation
add_library(rdp_record STATIC
data_process_class_dll/rdp_record.cpp
third_party/ormpp/thirdparty/sqlite3/sqlite3.c
)
set_target_properties(rdp_record PROPERTIES
CXX_STANDARD 20
CXX_STANDARD_REQUIRED ON
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
)
# RDP_RECORD_ENABLE 经 PUBLIC 传递给 DLL:data_process.cpp 等源码中的 RDP_RECORD 宏随之生效
target_compile_definitions(rdp_record PUBLIC
RDP_RECORD_ENABLE
ORMPP_ENABLE_SQLITE3
)
target_include_directories(rdp_record
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/data_process_class_dll
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/third_party/ormpp
${CMAKE_CURRENT_SOURCE_DIR}/third_party/ormpp/iguana
${CMAKE_CURRENT_SOURCE_DIR}/third_party/ormpp/ormpp
${CMAKE_CURRENT_SOURCE_DIR}/third_party/ormpp/thirdparty/sqlite3
)
target_compile_options(rdp_record PRIVATE
/W3
/utf-8
/Zc:__cplusplus
/FS
)
# 第三方 amalgamation 单独降到 /w(源文件级选项在编译命令行中位于目标级之后,优先生效)
set_source_files_properties(third_party/ormpp/thirdparty/sqlite3/sqlite3.c
PROPERTIES COMPILE_OPTIONS "/w")
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
@@ -68,16 +179,29 @@ target_include_directories(data_process_class_dll PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/data_process_class_dll
)
# 原工程使用 MSVC 2013(Visual Studio 12)编译器;保持兼容,不强制更高 C++ 标准。
# 源码使用 C++11 特性(nullptr、类内成员初始化等),MSVC 2013 默认即可支持。
if(MSVC)
# 与原 qmake 生成的编译选项保持一致。
# 注意:MSVC 2013 的 Debug 运行库与 /Zc:strictStrings 同时使用会触发 STL 编译错误,
# 原 qmake 工程也仅在 Release 配置中使用该选项。
target_compile_options(data_process_class_dll PRIVATE
# 显式恢复 /W3 警告级别。
# CMake 3.15(策略 CMP0092)起不再把 /W3 放进 CMAKE_CXX_FLAGS,
# 而本工程此前(cmake_minimum_required 3.10)一直以 /W3 构建,
# README 4.5 记录的全部 C4819/C4018/C4244 告警清理也基于该级别,
# 因此这里显式指定,保持警告级别不变。
/W3
# MSVC 2022 完整支持 /utf-8(VS2015 Update 2 起),
# 源码本身为 UTF-8 with BOM,显式指定后中文字符串/注释不再受系统代码页影响(消除 C4819)。
/utf-8
# 让 __cplusplus 正确报告 201703L(MSVC 默认始终为 199711L)。
# C++17 下依赖 __cplusplus 判断的第三方库(Eigen 3.3.7 等)据此走标准分支。
/Zc:__cplusplus
/Zc:wchar_t
# NMake 生成器下并行/增量编译时统一 PDB 写入。
/FS
)
# 与原 qmake 生成的编译选项保持一致:Release 配置额外开启严格字符串字面量检查。
# (原 MSVC 2013 因 Debug 运行库与 /Zc:strictStrings 冲突而仅限 Release,
# 升级到 MSVC 2022 后该限制已不存在,此处仍沿用“仅 Release”以保持行为不变。)
target_compile_options(data_process_class_dll PRIVATE
$<$<CONFIG:Release>:/Zc:strictStrings>
)
endif()
+310 -113
View File
@@ -1,7 +1,9 @@
# 雷达数据处理 DLL(data_process_class_dll)
> X256 雷达数据处理动态链接库:将雷达输出的原始点迹(TWS/TAS)处理为稳定可靠的目标航迹,并给出 TAS 跟踪波束控制指令。
> 仓库 simianzhen256-v1.5.5-ms-vscode(上游 simianzhen256,v1.5.5;已由 Qt/qmake 工程迁移为 VS Code + CMake 工程)。
> 仓库 simianzhen256-v1.5.5-ms-vscode(目录名沿用旧版本,当前代码版本为 **V1.5.6**;已由 Qt/qmake 工程迁移为 VS Code + CMake 工程,编译器为 MSVC 2022 / v143,C++17,x64 目标)。
>
> 版本号由 `parameters.h` 的 `MAJOR_VERSION` / `MINOR_VERSION` / `PATCH_VERSION`(当前 1.5.6)与体制后缀拼装为 `VERSION_STRING`(相扫 `V1.5.6.PS` / 机扫 `V1.5.6.MS`),实例创建后可从 `Data_Process_Factory::GetB()` 返回对象的公开成员 `version_info`(`const char*`)读取。
---
@@ -35,7 +37,7 @@
- 航迹外推、消亡与航迹号管理
- TAS 手动转跟踪与跟踪波束调度输出
**技术栈**:C++(MSVC 2013 兼容,C++11 特性)、Eigen 3(随仓库内置,仅用 Eigen/Dense)、CMake ≥ 3.10。
**技术栈**:C++(MSVC 2022 / v143 工具集,C++17)、Eigen 3(随仓库内置,仅用 Eigen/Dense)、CMake ≥ 3.21。
**处理约定**:
@@ -51,15 +53,20 @@
## 2. 功能特性
- **双模式处理管线**:data_preprocess 按输入类型与扫描圈完成标志返回 0/1/2/3,track_process 据此调度 TWS 凝聚/关联/起始或 TAS 凝聚/关联。
- **双模式处理管线**:data_preprocess 按输入类型与扫描圈完成标志返回 0/1/2/3(TWS 以方位波位号 == 0 判为一圈完成,机扫转台反转不再误判),track_process 据此调度 TWS 凝聚/关联/起始或 TAS 凝聚/关联。
- **两帧滑窗点迹凝聚**:TWS 点迹先与上一帧缓存合并凝聚,再进入关联,兼顾跨波位(CPI)相邻点迹的分裂问题;近程模式增加速度维约束。
- **逻辑法航迹起始**:航迹头(单点)→ 点-头速度法关联(两点)→ 点-临时航迹 EKF 波门 + 折角约束关联(三点及以上)→ 三点卡尔曼初始化转可靠航迹;带起批屏蔽区与重复航迹去重。
- **逻辑法航迹起始**:每圈按固定顺序推进——① 未关联点迹先与 ≥2 点临时航迹做 EKF 波门 + 折角约束关联(延长临时航迹),② 再与 1 点航迹头做速度法关联(两点卡尔曼初始化出 2 点临时航迹),③ 两步都做完后删除被消费的点迹、剩余点迹转成新航迹头,④ 长度达标的临时航迹经三点卡尔曼初始化转可靠航迹;带起批屏蔽区与重复航迹去重。
- **3 模型 IMM 滤波**:三个 Singer 模型(低/中/高过程噪声),量测为「距离-方位-径向速度」三维的 EKF,多普勒残差按盲速折叠,似然用 3×3 新息协方差。
- **最近邻数据关联**:TWS 按航迹距离分段收紧模型 3 波门并附加距离门限;TAS 附加按距离分段的高度门限。
- **最近邻数据关联**:三模型统计距离波门 `ASSO_THORD = 4`(等效 d < 4,TWS 另按航迹距离分段收紧模型 3 波门),再叠加高度门限(≤1000 m 150 m / 2000 m 200 m / 3000 m 250 m / 5000 m 300 m,更远不约束)与 TWS 弦距-位移距离门限。ΔT ≤ 0(点迹时间戳不晚于航迹时间)的候选不更新航迹状态、仅消费该点迹(标记已使用),避免其继续起批而生成虚假 TWS 航迹。关联输出的航迹同时给出**靠近/远离方向标志**:按航迹 IMM 融合速度矢量与「雷达→目标」位置矢量的夹角判定——`acos((-x·cos(Direction_Angle) − y·sin(Direction_Angle)) / r) < π/2` 置 1(靠近),否则 0(远离),等价于「滤波速度的径向分量指向雷达」,不再使用受多普勒模糊影响的实测径向速度 `vr_point`;TWS、TAS 两通道一致(见 9.6)。
- **重点目标与外推上限**:`primary_target_add` / `primary_target_cancel` 可按批号手动标记/取消重点目标,重点航迹的消亡轮数放宽到 `TRACK_DIE_ROUND_PRIMARY`(10,普通航迹 6);未关联航迹的外推圈数受限(TWS ≤ `MAX_EXTRAPOLATE_NUM`=3,TAS 相扫 5 / 机扫 48),超限后停止外推。**输出航迹只包含本圈有更新的航迹**(`Track_Update_Flag` 门控),宿主不能再用输出行数推断航迹总数(见 9.9)。
- **点迹附加信息字段扩展(需同步重编译宿主)**:`DataRev` 新增 `amp_asv` / `prn_fftn`,`Track` 新增 `amp_asv_point` / `prn_fftn_point`,结构体布局随之变化,宿主必须使用同一版本头文件重新编译对接;输入侧已打通(TWS 输出有效,TAS 输出仍未写回),详见 11.2。
- **高度维独立平滑**:按距离分段选择滑动平均窗长(5/6/7/9 点),抑制俯仰测量跳变。
- **TAS 波束控制**:手动转 TAS 目标入队(队列长度与容量随扫描体制切换),按最新时间戳外推输出跟踪波束(距离/方位/俯仰/批号)。
- **航迹生命周期管理**:外推轮数计数、超时消亡、手动删除、全清空(含各模块 reset,防重连后幽灵航迹)。
- **防御性加固**:见 BUG_FIX_REPORT.md——越界防护、时间戳正序校验、方位角 ±π 环绕、asin 定义域钳位、IMM 概率奇异保护等已修复项。
- **防御性加固**:见 archived_docs/BUG_FIX_REPORT.md——越界防护、时间戳正序校验、方位角 ±π 环绕、asin 定义域钳位、IMM 概率奇异保护等已修复项。
- **日志文件输出**:`RDP_LOG_ENABLE` 定义时,首次 `Data_Process_Factory::GetB()` 自动用 `rdbuf()` 将 `std::cout` 重定向到 `RDP_LOG_DIR`(`.\rdp_logs\`)下按日期+时间命名的日志文件,`Destroy()` 时恢复控制台并关闭文件;`RDP_LOG` 日志宏在每条日志前附加 `[时间戳][文件:行号]`。该宏在 `parameters.h` 中**当前为注释状态(默认不启用)**,即默认不做重定向、日志输出到控制台(见 10.1)。
- **版本号输出**:`parameters.h` 的 `MAJOR_VERSION`/`MINOR_VERSION`/`PATCH_VERSION`(当前 1.5.6)经两级 `STRINGIFY` 宏拼成 `VERSION_STRING`(`V1.5.6.PS` / `V1.5.6.MS`);`Data_process_class_dll` 基类公开成员 `const char* version_info` 由 `Data_Process_Factory::GetB()` 在创建实例时赋值,宿主可直接读取以核对/上报 DLL 版本(b227ae1)。
- **数据记录调试功能(可选编译)**:`rdp_record` 模块把结构体按「一个记录实体一张表」写入 SQLite(一次运行一个 db 文件、多张表),结构体到表的映射由内置的 ormpp(`third_party/ormpp`,含 iguana、frozen 与 SQLite 3.49.1 amalgamation)完成;由 CMake 选项 `RDP_DATA_RECORD`(`AUTO`/`ON`/`OFF`)控制,**AUTO 时 Debug 构建启用、Release(交付)构建完全不编译**——记录模块源码不参与编译、`RDP_RECORD`/`RDP_RECORD_BATCH` 宏展开为 `((void)0)`(实参不求值),DLL 不新增依赖与导出符号、无性能损失。启用时 `GetB()` 建目录并开库(`.\rdp_records\rdp_record_YYYYMMDD_HHMMSS.db`),首次写入某实体时自动 `CREATE TABLE IF NOT EXISTS`,同步写入、失败降级(不抛出、不影响雷达处理)。当前内置 **6 张在用记录表**(输入点迹、TWS/TAS 凝聚后点迹、临时航迹、可靠航迹、点航关联信息,共 7 个实体定义、其中 1 个已停用),并新增 `point_id`/`frame_id`/`circle_id`/`temp_track_id`/`track_id` 等关联标识,可把一次处理的点迹→凝聚→临时航迹→可靠航迹→关联信息串成完整链路;新增记录数据只需「加结构体 + 注册 + 一行记录点」,详见 docs/data_record_debug.md(见 4.3、10.1、11.3)。
---
@@ -68,29 +75,47 @@
```text
.
├── CMakeLists.txt # CMake 构建脚本(扫描模式切换、导出宏、输出目录)
├── BUG_REPORT.md # 全量逻辑 BUG 分析报告(P0~P3,37 项)
├── BUG_FIX_REPORT.md # BUG 修复报告(修复明细 + 编译验证结果)
├── CHANGELOG.md # 变更日志(Keep a Changelog 格式,按 git 提交记录维护)
├── README.md # 本文件:项目说明(架构、接口、流程、算法、参数)
├── requirements.md # 需求说明:数据记录调试功能修改要求(结构体经 ormpp 写入 SQLite、Release 可屏蔽、数据分类入不同表、宏记录点;原日志文件输出需求已实现)
├── archived_docs/ # 过期文档留档(仅作历史参考;接口与流程以本 README 与 docs/ 专项说明为准)
│ ├── BUG_REPORT.md # 全量逻辑 BUG 分析报告(P0~P3,37 项)
│ └── BUG_FIX_REPORT.md # BUG 修复报告(修复明细 + 编译验证结果)
├── docs/ # 专项说明文档(按当前源码整理,与 README 互补)
│ ├── data_process_flow_report.md # 数据处理流程与关键逻辑说明(前处理/判圈、凝聚、关联与滤波、起始、外推消亡、TAS 波束、参数脉络)
│ ├── track_init_readme.md # 航迹起始(Track Init)详细逻辑说明(阶段流程、条件汇总、输出字段映射、风险点、代码索引)
│ └── data_record_debug.md # 数据记录调试功能说明(开关与构建、运行期行为、表结构约定、新增记录点四步配方、6 张在用记录表清单与字段、查询示例)
├── design_docs/ # 设计与讨论资料留档(被 .gitignore 忽略、不入库;含设计讨论、产品介绍与 README/流程报告的导出、渲染版本)
├── .vscode/ # VS Code 工程配置
│ ├── settings.json # cmake.generator=NMake、MSVC2013 x86 环境变量、compile_commands
│ ├── settings.json # cmake.generator=NMake、MSVC2022 x64 环境变量、compile_commands
│ ├── cmake-variants.yaml # X256_PS / X256_MS × Release / Debug 变体
│ ├── tasks.json # 配置/构建/清理任务
│ └── scripts/{configure,build,clean}.bat
│ ├── tasks.json # 配置/构建/清理任务(MSVC2022 x64)
│ └── scripts/ # 构建脚本(纯 ASCII,避免 cmd 代码页问题)
│ ├── msvc_env.bat # 共享环境设置:定位 VS2022/cl.exe 并调用 vcvarsall.bat(x64)
│ ├── configure.bat # CMake 配置(含旧工具集构建树自动清理)
│ ├── build.bat # CMake 构建
│ └── clean.bat # CMake 清理
├── backup/
│ └── data_process_class_dll/
│ ├── data_process_class_dll.pro # 迁移前的 Qt/qmake 工程文件(备份)
│ └── data_process_class_dll.pro.user
├── third_party/ # 内置第三方源码(按上游原样保存,来源/版本/升级方式见其 UPSTREAM.md)
│ └── ormpp/ # ormpp 0.2.0 + iguana 1.0.5 + frozen + SQLite 3.49.1 amalgamation(Apache-2.0 / 公有领域)
├── data_process_class_dll/ # 源码目录
│ ├── data_process_class_dll.h # 对外接口:DataRev/Track/TrackingBeam/RadarPara 结构体、抽象基类、工厂类
│ ├── data_process_class_dll.h # 对外接口:DataRev/Track/TrackingBeam/RadarPara 结构体、抽象基类(公开成员 version_info)、工厂类
│ ├── data_process_class_dll_global.h # DLL 导入导出宏
│ ├── data_process_class_dll.cpp # 工厂实现(GetB/Destroy 单例)
│ ├── data_process.h / .cpp # Data_Process 实现:接口分发、TWS/TAS 主流程、参数管理、清空/删除/转跟踪
│ ├── struct.h # 内部结构体:PointRecv / Trust_Track / Temp_track / Tracking_Target
│ ├── rdp_log.h / .cpp # 日志输出模块:rdbuf() 重定向 std::cout、RDP_LOG 宏(开关宏在 parameters.h)
│ ├── rdp_record.h / .cpp # 数据记录调试模块:结构体→SQLite 表(内置 ormpp)、RDP_RECORD/RDP_RECORD_BATCH 宏、按实体自动建表、失败降级(开关由 CMake 选项 RDP_DATA_RECORD 控制;实现以 C++20 独立编译)
│ ├── rdp_record_entity.h # 记录实体(=数据表)定义区:6 张在用表 + 1 个已停用实体(输入点迹、TWS/TAS 凝聚后点迹、临时航迹、可靠航迹、关联信息;输出航迹示例已停用)+ RDP_RECORD_DECLARE 声明宏;新增需要记录的数据在此加结构体
│ ├── data_process.h / .cpp # Data_Process 实现:接口分发、TWS/TAS 主流程、参数管理、清空/删除/转跟踪;含 point_id/frame_id/circle_id 自增计数(记录用)
│ ├── struct.h # 内部结构体:PointRecv / Trust_Track / Temp_track / Asso_Info / Tracking_Target
│ ├── parameters.h # 编译期宏参数(按 MECHANICAL_SCANNING / PHASE_SCANNING 分两组)
│ ├── dot_coh.h / .cpp # TWS 点迹凝聚(含两帧滑窗缓冲)
│ ├── dot_coh_tas.h / .cpp # TAS 点迹凝聚
│ ├── coor_trans.h / .cpp # 极坐标 ↔ 直角坐标转换
│ ├── kalman.h / .cpp # 卡尔曼滤波:两点/三点初始化、线性 KF、EKF(含多普勒)、统计距离、盲速计算
│ ├── track_asso.h / .cpp # TWS 航迹关联:IMM 交互 → 滤波 → 输出、距离/俯仰门限、高度平滑
│ ├── track_asso.h / .cpp # TWS 航迹关联:IMM 交互 → 滤波 → 输出、统计距离/高度/距离门限、高度平滑
│ ├── track_asso_tas.h / .cpp # TAS 航迹关联(独立实现,量测门限与数据率不同)
│ ├── track_init.h / .cpp # 逻辑法航迹起始:点-临时航迹关联、点-航迹头关联、转可靠航迹、屏蔽区
│ ├── track_init_direct_tracking.h/.cpp# 引导跟踪航迹起始(当前入口为空实现,未启用)
@@ -104,6 +129,8 @@
│ ├── X256_PS/ # 相扫构建目录(含 bin/data_process_class_dll.dll、rdp_playback.exe 等)
│ ├── X256_MS/ # 机扫构建目录
│ └── output/ # 早期打包导出(其中 data_process_class_dll.h 为旧版 32 位时间戳接口,勿作为当前 API 依据)
├── rdp_logs/ # 日志输出目录(运行时自动创建于宿主进程当前工作目录下,被 .gitignore 忽略)
├── rdp_records/ # 数据记录库输出目录(运行时自动创建于宿主进程当前工作目录下,被 .gitignore 忽略;仅记录功能启用时产生)
└── .gitignore
```
@@ -115,11 +142,13 @@
| 组件 | 要求 |
|---|---|
| 操作系统 | Windows(构建脚本按 D:\APP\VisualStudio2013 与 Windows Kits 8.1 路径配置) |
| 编译器 | Microsoft Visual C++ 2013(Visual Studio 12)**x86**(与原 Qt 工程保持一致) |
| CMake | ≥ 3.10 |
| 操作系统 | Windows(构建脚本默认按 D:\APP\VisualStudio2022 与 Windows SDK 10.0.26100.0 配置,可用环境变量覆盖) |
| 编译器 | Microsoft Visual C++ 2022(v143 工具集,`_MSC_VER` 19.44)**x64**(`D:\APP\VisualStudio2022\VC\Tools\MSVC\14.44.35207\bin\Hostx64\x64\cl.exe`) |
| C++ 标准 | C++17(`/std:c++17`;由 `CMAKE_CXX_STANDARD 17` + `CMAKE_CXX_STANDARD_REQUIRED ON` 强制) |
| CMake | ≥ 3.21(VS2022 生成器自 3.21 起支持;本工程实际在 CMake 4.x 上验证) |
| 生成器 | NMake Makefiles(VS Code 配置与脚本均使用该生成器) |
| 线性代数 | 无需安装——Eigen 头文件已内置在源码目录 |
| 数据记录(可选调试功能) | 无需安装——ormpp / iguana / frozen / SQLite amalgamation 已内置在 `third_party/ormpp`(版本与升级方式见该目录 UPSTREAM.md);启用时记录模块以 C++20 独立编译,所需的 C 编译器由同一 VS 2022 工具集提供 |
### 4.2 扫描体制
@@ -133,12 +162,13 @@
### 4.3 命令行构建
```bat
:: 需在 MSVC 2013 x86 环境中执行(先运行 vcvarsall.bat x86)
cmake -S . -B build/X256_PS -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Release -DRADAR_SCAN_MODE=PHASE_SCANNING
:: 需在 MSVC 2022 x64 环境中执行(先运行 vcvarsall.bat x64)
cmake -S . -B build/X256_PS -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Release -DRADAR_SCAN_MODE=PHASE_SCANNING ^
-DCMAKE_CXX_COMPILER="D:/APP/VisualStudio2022/VC/Tools/MSVC/14.44.35207/bin/Hostx64/x64/cl.exe"
cmake --build build/X256_PS
```
或直接使用仓库提供的脚本(内部已调用 vcvarsall.bat):
或直接使用仓库提供的脚本(内部已通过 msvc_env.bat 调用 vcvarsall.bat x64 并校验 cl.exe):
```bat
.vscode\scripts\configure.bat build/X256_PS PHASE_SCANNING Release
@@ -146,6 +176,20 @@ cmake --build build/X256_PS
.vscode\scripts\clean.bat build/X256_PS
```
configure.bat 会在构建目录中写入 `.msvc_toolset` 标记;若该目录是用其它工具集(例如旧的 MSVC 2013 缓存)配置的,脚本会先整体删除该目录再重新配置,避免新旧目标文件混链。用 `set VS2022_DIR=...`、`set MSVC_TOOLSET_VERSION=...`、`set MSVC_TARGET_ARCH=x86` 可覆盖默认的 VS 安装路径、工具集版本与目标架构。
数据记录调试功能由缓存变量 `RDP_DATA_RECORD` 控制,`configure.bat` 的第 4 个参数即该变量(取值 `AUTO`/`ON`/`OFF`,缺省 `AUTO`):
```bat
:: AUTO(默认):Debug 构建启用、Release 构建关闭——交付构建不做任何记录
.vscode\scripts\configure.bat build/X256_PS PHASE_SCANNING Debug AUTO
:: 显式关闭(Release 交付推荐)或显式开启(临时在 Release 中排查)
.vscode\scripts\configure.bat build/X256_PS PHASE_SCANNING Release OFF
.vscode\scripts\configure.bat build/X256_PS PHASE_SCANNING Debug ON
```
启用时配置阶段打印 `数据记录调试功能:启用(C++20 静态库 rdp_record + 内置 SQLite)`,并额外构建静态库 `rdp_record`(记录模块 + SQLite,链接进 DLL);关闭时打印 `...:关闭(不编译记录模块,RDP_RECORD 为空操作)`,记录模块源码完全不参与编译。开关语义、表结构约定与新增记录点配方见 docs/data_record_debug.md。
在 VS Code 中可直接运行任务(tasks.json):「CMake: 配置并构建 X256_PS (Release)」等,默认任务为 X256_PS Release。
### 4.4 产物
@@ -156,8 +200,37 @@ cmake --build build/X256_PS
| 导入库 data_process_class_dll.lib | build/<X256_XX>/lib/ |
| 安装(cmake --install) | DLL/导入库 → bin、lib,公开头文件 → include |
| compile_commands.json | 构建根目录(供 VS Code IntelliSense 使用) |
| rdp_record.lib(仅记录功能启用时) | build/<X256_XX>/lib/ —— 记录模块静态库(含 SQLite),已链接进 DLL,**无需随 DLL 分发** |
仓库 build/X256_PS/bin/ 内还带有 **rdp_playback.exe**(回放测试工具,依赖 Qt5Core.dll、sqlite3.dll,配置样例 RadarConfigParam.ini,可回放 .db 记录的点迹数据验证 DLL 输出),不在本仓库源码范围内。
产物为 **x64(AMD64)** 目标。宿主程序必须是 64 位、且使用与本 DLL 同一版本头文件重新编译——MSVC 2013 → MSVC 2022 同时伴随 x86 → x64 的 ABI 变化,旧的 32 位宿主无法加载该 DLL。
仓库 build/X256_PS/bin/ 内原附带 **rdp_playback.exe**(回放测试工具,依赖 Qt5Core.dll、sqlite3.dll,配置样例 RadarConfigParam.ini,可回放 .db 记录的点迹数据验证 DLL 输出),该工具不在本仓库源码范围内、当前构建目录中亦不存在,需自行放置;由于 DLL 已改为 x64,配套的 rdp_playback.exe 必须是 **x64 版本**(同时其自带 Qt5Core.dll / sqlite3.dll 也需为 x64),32 位版本无法加载新版 DLL。
### 4.5 编译警告处理
- 源码文件统一使用 **带 BOM 的 UTF-8** 编码保存;`CMakeLists.txt` 对 MSVC 统一追加 `/utf-8`,中文字符串与注释不再受系统代码页影响(消除 C4819),BOM 仅作为编辑器/旧工具的兼容保留。
- `CMakeLists.txt` 显式指定 `/W3`:CMake 3.15(策略 CMP0092)起不再把 `/W3` 放进 `CMAKE_CXX_FLAGS`,本工程此前一直以 `/W3` 构建,故显式恢复以保持警告级别不变。
- 修复了 `/W3` 下全部 C4819(源文件字符集)、C4018(signed/unsigned 不匹配)、C4244(类型转换可能丢失数据)警告:循环变量按需改为 `size_t`,窄化转换改为 `static_cast` 显式转换,不改变原有计算逻辑。
- MSVC 2022 在 `/W3` 下新增报出的 C4267(`size_t` → `int` 窄化)共 17 处已按同一约定修复(`track_asso.cpp`、`track_asso_tas.cpp`、`track_init.cpp`、`track_init_direct_tracking.cpp`、`tas_ctrl.cpp` 中改为 `static_cast<int>(...)`),同样不改变计算逻辑。
- 当前四种配置(X256_PS / X256_MS × Release / Debug)均以 **0 warning、0 error** 通过编译。
- cl.exe 的诊断信息为中文(跟随 VS 安装语言,`VSLANG=1033` 对 VS2022 的 cl.exe 无效),构建日志中偶见 `注意: 包含文件:` 行泄漏,属 CMake 依赖扫描的显示噪声,头文件依赖(`CMakeFiles/*.dir/**/*.obj.d`)记录完整,不影响增量编译。
- **第三方库告警例外(仅记录功能启用时)**:数据记录模块的第三方依赖按上游原样内置在 `third_party/ormpp`(保留上游字节,含换行符,不转 BOM/编码),其中 `third_party/ormpp/thirdparty/sqlite3/sqlite3.c` 以源文件级 `/w` 单独编译,`data_process_class_dll/rdp_record.cpp` 用 `#pragma warning(push, 0)` / `(pop)` 包裹第三方头;除此之外本工程源码仍按上一条的 `/W3` 零告警标准编译(X256_PS/X256_MS × Release/Debug 四种配置实测 0 warning、0 error)。
### 4.6 编译器升级说明(MSVC 2013 → MSVC 2022)
| 项目 | 升级前 | 升级后 |
|---|---|---|
| 编译器 | Microsoft Visual C++ 2013(VS 12.0,`_MSC_VER` 1800) | Microsoft Visual C++ 2022(v143,`_MSC_VER` 1944,cl 19.44.35228) |
| 目标架构 | x86(32 位) | x64(AMD64) |
| C++ 标准 | C++11(编译器默认) | C++17(`/std:c++17`) |
| CMake 最低版本 | 3.10 | 3.21 |
| 新增/调整编译选项 | — | `/utf-8`(改为无条件)、`/Zc:__cplusplus`、`/W3`(显式恢复) |
- **`/Zc:__cplusplus`**:MSVC 默认把 `__cplusplus` 报为 `199711L`,加该选项后正确报 `201703L`,使依赖 `__cplusplus` 判断的第三方库(内置 Eigen 3.3.7)走标准分支。已验证 Eigen 3.3.7 在此组合下编译通过。
- **`/Zc:strictStrings` 仍仅在 Release 启用**:原限制(MSVC 2013 Debug 运行库与该选项冲突)已不存在,此处保持与原 qmake 工程一致的行为。
- **旧构建目录需清理**:MSVC 2013 的 CMake 缓存与目标文件不能跨编译器复用(nmake 会因目标文件时间戳较新而跳过重编译,导致新旧目标文件混链)。`configure.bat` 已内置自动清理:构建目录中无 `.msvc_toolset` 标记或标记与当前工具集不一致时,先删除整个构建目录再配置。
- **源码改动**:仅补齐 17 处 `static_cast<int>(...)` 显式窄化转换以消除 MSVC 2022 新增的 C4267 告警,未改动任何计算逻辑、接口与结构体布局;DLL 导出符号与升级前一致(差异仅为 x86 → x64 的名称修饰,如 `QAE` → `QEAA`、`PAV` → `PEAV`)。
- **兼容性风险点已核查**:C++17 移除的 `std::auto_ptr`、`std::random_shuffle`、`std::unary_function`/`binary_function`、`std::binder1st/2nd` 等在本工程源码与内置 Eigen 3.3.7 中均未被使用(Eigen 的相应特化本身带有 `__cplusplus`/`EIGEN_COMP_MSVC` 版本保护)。
---
@@ -172,6 +245,7 @@ cmake --build build/X256_PS
classDiagram
class Data_process_class_dll {
<<abstract, exported API>>
+const char* version_info
+data_preprocess(DataRev[150]) int
+track_process(Track[][10], int*, int[], int*, int) int
+Beam_Ctrl(TrackingBeam*, Track[][10], int*)
@@ -231,17 +305,19 @@ classDiagram
| 类 / 文件 | 职责 |
|---|---|
| Data_Process(data_process.cpp) | 对外接口实现与流程编排:输入缓存、按 model 分发 TWS/TAS 流水线、参数管理、航迹清空/删除、手动转/停 TAS、波束控制转发 |
| Data_Process(data_process.cpp) | 对外接口实现与流程编排:输入缓存、按 model 分发 TWS/TAS 流水线、参数管理、航迹清空/删除、手动转/停 TAS、手动标记/取消重点目标(primary_target_add/cancel)、波束控制转发 |
| Dot_Coh | TWS 点迹凝聚:两帧滑窗合并凝聚(dot_coh_process_buff) |
| Dot_Coh_TAS | TAS 点迹凝聚(单帧,距离/方位/速度三维门限) |
| Track_Asso | TWS 点航关联 + IMM 滤波:模型交互 → 门限筛选(NN)→ 各模型 EKF → 概率更新 → 模型输出;未关联航迹外推;高度平滑 |
| Track_Asso_Tas | TAS 航迹关联(只处理指定批号 tas_track_idx,门限与 TWS 略有差异) |
| Track_Init | 逻辑法航迹起始:航迹头维护、点-头关联(两点)、点-临时航迹关联(三点及以上)、起批(三点卡尔曼初始化)、屏蔽区判断、临时航迹消亡 |
| Track_Die / Track_Die_Tas | 可靠航迹消亡:外推轮数超限或手动删除标志置位时输出消亡批号并删除 |
| TAS_Ctrl | TAS 波束控制:手动目标入队(tas_target_add)、失效目标出队(tas_target_del)、跟踪波束预测输出(tas_beam_output,含数据率门控)、队列轮转 |
| Track_Asso | TWS 点航关联 + IMM 滤波:模型交互 → 门限筛选(NN)→ 各模型 EKF → 概率更新 → 模型输出(只输出 `Track_Update_Flag==1` 的航迹);未关联航迹按上限外推;高度平滑;按滤波速度矢量写 `Flag_Direction`;ΔT≤0(乱序/重复时间戳)的候选取出后不更新滤波、不刷新航迹,仅消费该点迹 |
| Track_Asso_Tas | TAS 航迹关联(只处理指定批号 tas_track_idx,门限与 TWS 略有差异;输出同样受 `Track_Update_Flag` 门控,外推上限 `MAX_EXTRAPOLATE_NUM_TAS` 的判定方向与 TWS 相反) |
| Track_Init | 逻辑法航迹起始,每圈顺序固定:点-临时航迹关联(延长 ≥2 点临时航迹)→ 点-航迹头关联(两点起始)→ 剩余点转新航迹头 → 长度达标者起批(三点卡尔曼初始化);另含起批屏蔽区判断与临时航迹消亡 |
| Track_Die / Track_Die_Tas | 可靠航迹消亡:未关联轮数超限(普通 TWS 6 圈 / 重点航迹 10 圈 / TAS 相扫 10、机扫 96)或手动删除标志置位时输出消亡批号并删除 |
| TAS_Ctrl | TAS 波束控制:手动目标入队(tas_target_add)、失效目标出队(tas_target_del)、跟踪波束预测输出(tas_beam_output:数据率门控 + 队内选择最早 CPI 目标)、调度时刻记录(last_track_time) |
| Track_Ind_Mangement | 航迹号 1~500 的顺序分配与回绕复用 |
| kalman | 两点/三点滤波初始化、线性卡尔曼预测/滤波、EKF(3 维量测含多普勒)、马氏统计距离 d、盲速 Bind_speed |
| coor_trans | 极坐标 ↔ 直角坐标转换 |
| rdp_log(rdp_log.h/.cpp) | 日志输出:按 parameters.h 的 `RDP_LOG_ENABLE` 开关,在 `Init()` 中用 `rdbuf()` 将 `std::cout` 重定向到日期+时间命名的日志文件、`Shutdown()` 恢复控制台并关闭;提供 `RDP_LOG` 宏自动附加 `[时间戳][文件:行号]` 前缀 |
| rdp_record(rdp_record.h/.cpp、rdp_record_entity.h) | 数据记录调试(可选编译):由 CMake 选项 `RDP_DATA_RECORD` 决定是否参与编译;开启时把「记录实体」(结构体)经内置 ormpp 映射写入 SQLite 表(一实体一表,首次写入自动 `CREATE TABLE IF NOT EXISTS`,一次运行一个 db 文件),`RDP_RECORD`/`RDP_RECORD_BATCH` 记录点宏在关闭时展开为空操作;`Init()`/`Shutdown()` 由工厂的 `GetB()`/`Destroy()` 调用;建表/插入失败一律降级(每个实体只提示一次),不影响雷达处理。**当前 7 个实体定义、6 张在用表**:`RdpRecPoint`(输入点迹,TWS/TAS 共用)、`RdpRecPointCohesionTWS`、`RdpRecPointCohesionTAS`、`RdpRecTempTrack`、`RdpRecTrustTrack`、`RdpRecAssoInfo`(另有已停用的 `RdpRecTrackOut`),并有数组转字符串辅助 `arrayToString()`(用于把 `X`/`P` 等状态数组存为文本列) |
| Track_Init_Direct_Tracking / Track_Asso_Direct_Tracking | 引导跟踪(direct tracking)专用起批/关联。**当前入口为空实现、未启用**;track_asso_direct_tracking.cpp 未加入 CMake 源列表 |
---
@@ -252,8 +328,8 @@ classDiagram
| 结构体 | 用途 | 要点 |
|---|---|---|
| DataRev | 输入点迹 | 距离(m)、方位/俯仰(°)、速度(m/s)、幅度/门限/SNR/RCS、码盘值、CPI/GNSS 时间(ms, long long)、方位波位(0-24)/俯仰波位(0-13)、PRI(µs,**不允许为 0**)、频点号、点数、点迹号、使用标记、TAS 批号、点迹类型、十字星数据 speed_dim[129]/range_dim[8] |
| Track | 输出航迹 | 位置/速度、极坐标量、航迹号、更新点数、方向/实虚点标志、一步预测、跟踪模式、航向角、目标类型、关联点信息、航迹时间(s)/GNSS、SNR/RCS、十字星数据透传 |
| DataRev | 输入点迹 | 距离(m)、方位/俯仰(°)、速度(m/s)、幅度/门限/SNR/RCS、码盘值、CPI/GNSS 时间(ms, long long)、方位波位(0-24)/俯仰波位(0-13)、PRI(µs,**不允许为 0**)、频点号、点数、点迹号、使用标记、TAS 批号、点迹类型、归一化幅度 `amp_asv`(dB)、速度维 FFT 点数 `prn_fftn`、十字星数据 speed_dim[129]/range_dim[8] |
| Track | 输出航迹 | 位置/速度、极坐标量、航迹号、更新点数、方向标志 Flag_Direction(仅关联/外推输出路径赋值:按滤波速度矢量判定,1 靠近 / 0 远离,见 9.6;起批输出不写该字段)、实虚点标志 Flag_Point、一步预测、跟踪模式、航向角 Direction_Angle、目标类型、关联点信息、归一化幅度 `amp_asv_point` / 速度维 FFT 点数 `prn_fftn_point`、航迹时间(s)/GNSS、SNR/RCS、十字星数据透传 |
| TrackingBeam | TAS 跟踪波束指令 | open_flag、type、Range、Azi、Elev、TAS_track_index |
| Target_direct_tracking | 引导跟踪信息 | 批号、距离/方位/俯仰、开关(接口未启用) |
| RadarPara | 工作参数 | 见 [参数说明](#10-参数说明) |
@@ -262,13 +338,16 @@ classDiagram
| 结构体 | 用途 | 关键字段 |
|---|---|---|
| PointRecv | 内部点迹 | 距离/方位(弧度)/速度/幅度/高度/SNR/RCS/CPI/GNSS 时间、点迹号、使用标志、波位/PRF/频点、跟踪模式、点迹区号、十字星数据 |
| Trust_Track | 可靠航迹(IMM 全状态) | 航迹号、高度、航迹时间、更新标志、外推轮数 Extrapolate_round、实虚点标志、手动删除/手动 TAS 标志、关联点数、高度平滑缓冲 Hight_smooth、关联点信息、融合状态 X[6]/P[6][6]、三个子模型 X1/X2/X3、P1/P2/P3、模型概率 u[3] |
| Temp_track | 临时航迹(航迹起始阶段) | 4 维状态 X[4]/P[4][4]、极坐标点信息、时间戳、buff_round(已缓存圈数)、区号、关联标志 |
| Tracking_Target | TAS 队列元素 | 批号 Index、空标志 empty_flag |
| PointRecv | 内部点迹 | 距离/方位(弧度)/速度/幅度/高度/SNR/RCS/CPI/GNSS 时间、点迹号 `point_id` / 帧号 `frame_id` / 圈号 `circle_id`(记录链路用,c3c7e66 新增)、使用标志、波位/PRF/频点、跟踪模式、点迹区号、归一化幅度 `amp_asv_point` / 速度维 FFT 点数 `prn_fftn_point`、十字星数据 |
| Trust_Track | 可靠航迹(IMM 全状态) | 航迹号、标识字段 `track_id` / `point_id` / `circle_id`(b227ae1 新增,起批时取临时航迹末点的 `temp_track_id`/`point_id`/`circle_id`,关联成功时刷新为被关联点迹的 ID)、高度、极坐标量快照 `Range` / `Azimuth` / `Elevation` / `Velocity`(b227ae1 新增,仅在本圈有输出时回填)、航迹时间、更新标志 Track_Update_Flag(**输出门控**:仅置 1 的航迹当圈被输出,输出后清零)、外推轮数 Extrapolate_round、实虚点标志、手动删除/手动 TAS/重点目标(primary_target_flag,决定消亡用 6 还是 10 圈)标志、关联点数、高度平滑缓冲 Hight_smooth、关联点信息、归一化幅度 `amp_asv_point` / 速度维 FFT 点数 `prn_fftn_point`、融合状态 X[6]/P[6][6]、三个子模型 X1/X2/X3、P1/P2/P3、模型概率 u[3] |
| Temp_track | 临时航迹(航迹起始阶段) | 4 维状态 X[4]/P[4][4]、极坐标点信息(`r`/`azi`/`vr`/`height`/`SNR`/`RCS`/`amplitude`)、时间戳 `CPI_time` / `GNSS_time`、`buff_round`(已缓存圈数)、标识字段 `temp_track_id` / `point_id` / `circle_id`、区号 `temp_track_section_idx`、俯仰波位号 `pitch_num`、关联标志(c3c7e66 起字段统一更名,如原 `snr`/`Amp`/`T`) |
| Asso_Info | 点航关联候选(track_asso.cpp 使用) | `point_id` / `track_id` / `circle_id`、三模型统计距离 `d1`/`d2`/`d3` 与最小值 `d_min`、点迹/航迹下标、`delta_T`/`delta_h`/`R`/`VT`、门限判定结果 `thd_d`/`thd_h`/`thd_r`、是否被最近邻取中 `min_d_flag`(b227ae1 由局部结构体提升到 struct.h,用于记录 `RdpRecAssoInfo`) |
| Tracking_Target | TAS 队列元素 | 批号 Index、空标志 empty_flag、最近波束输出时间 last_track_time |
| Direct_Tracking_Target | 引导跟踪目标 | ID、方位/俯仰/距离、空标志 |
> 说明:Trust_Track 的状态布局为 [x, vx, ax, y, vy, ay],x/y 两轴解耦,每轴为「位置-速度-加速度」。
>
> ⚠ **结构体布局变更**:`DataRev` 新增 `amp_asv` / `prn_fftn`,`Track` 新增 `amp_asv_point` / `prn_fftn_point`,内部 `PointRecv` / `Trust_Track` / `Temp_track` 亦有新增与更名字段。宿主与 DLL 必须使用同一版本头文件并**同步重新编译**,否则字段错位。对外结构体(`data_process_class_dll.h`)在本版中仅新增了基类成员 `version_info`,`DataRev` / `Track` / `RadarPara` 布局自 1.5.5 起未再变化。
---
@@ -278,8 +357,9 @@ classDiagram
| 接口 | 返回值 | 说明 |
|---|---|---|
| Data_Process_Factory::GetB() | 实例指针 | 获取单例(首次调用时创建) |
| Data_Process_Factory::GetB() | 实例指针 | 获取单例(首次调用时创建,并写入 `version_info`) |
| Data_Process_Factory::Destroy() | — | 销毁实例并将指针置空 |
| version_info(基类公开成员) | `const char*` | DLL 版本字符串,由 `GetB()` 赋值为 `VERSION_STRING`(相扫 `V1.5.6.PS` / 机扫 `V1.5.6.MS`);只读使用,不要释放(b227ae1) |
| track_process_parameters_initial(RadarPara) | 0 成功 / -1 失败 | 初始化工作参数;校验 3 ≤ track_start_point_num ≤ 9 |
| track_process_parameters_modify(RadarPara) | 0 / -1 | 运行时修改参数(校验同上) |
| data_preprocess(DataRev[150]) | 0/1/2/3 | 输入一帧点迹并缓存,返回处理模式(见下表) |
@@ -289,19 +369,27 @@ classDiagram
| track_delete(n, idx[]) | 0 | 按批号标记手动删除(下一轮消亡输出) |
| tracking_start(track_index) | 0 | 手动转 TAS 跟踪(置 manual_tracking_flag,由 Beam_Ctrl 入队) |
| tracking_stop(track_index) | 0 | 手动取消 TAS(清标志并 Track_Mode=0;队列项延至下一次 Beam_Ctrl 移除) |
| primary_target_add(track_index) | 0 | 手动将指定批号标为**重点目标**(置 `primary_target_flag=1`,消亡轮数放宽到 10 圈;批号不存在时不报错、无副作用) |
| primary_target_cancel(track_index) | 0 | 取消指定批号的重点目标标记(置 `primary_target_flag=0`,恢复普通消亡轮数) |
| tracking_point(azimuth) | 0 | 手动打跟踪波束——**未实现(保留空实现)** |
| direct_tracking_process(...) | 0 | 引导跟踪——**未实现(保留空实现)** |
> **输出语义(50e0b5c 起)**:`track_process(model=1/3)` 只把本圈**有更新**的航迹写入 `Trust_Track_Output`(关联到实点、外推成功,或 TAS 指定批号被更新),行数由 `Trust_track_num_Output` 给出;未更新(含外推超限)的航迹当圈不出现在输出中,宿主不能据输出行数推断航迹总数。输出数组内容由 DLL 覆盖写入指定行,其余行保持宿主原值(DLL 不清零整块数组)。
> 日志输出为 DLL 内部功能(不对外导出,接口见 10.1 的宏开关):首次 `GetB()` 创建实例时自动初始化(`RDP_LOG_ENABLE` 定义则将 `std::cout` 重定向到 `.\rdp_logs\` 下按日期+时间命名的日志文件,该宏当前为注释状态,见 10.1),`Destroy()` 销毁实例时恢复 `std::cout` 并关闭日志文件。
> 数据记录同样为 DLL 内部功能(不对外导出):记录功能启用时,`data_preprocess` 逐点写输入点迹、`dot_coh`/`dot_coh_tas` 写凝聚后点迹、`track_init` 写临时航迹、`track_asso` 写可靠航迹与关联信息,全部落到 `.\rdp_records\rdp_record_YYYYMMDD_HHMMSS.db` 的 6 张在用表中(表清单与字段见 docs/data_record_debug.md)。
### 7.2 data_preprocess 返回值的含义
| 返回值 | 含义 | 触发条件 |
|---|---|---|
| 0 | 无效数据 | point_type 既非 0 也非 1 |
| 1 | **TWS 满一圈,执行航迹处理** | point_type==0 且当前方位波位号 < 上一帧波位号(波位号由大变小判为扫描一圈完成) |
| 2 | **TWS 未满一圈,继续蓄数据** | point_type==0 且波位号未变小;调用 track_process(model=2) 做滑窗凝聚 |
| 1 | **TWS 满一圈,执行航迹处理** | point_type==0 且当前帧方位波位号 == 0(7d5a56f 起改用波位号为 0 判满圈:机扫转台反转时波位号会变小但并未扫完一圈,原「波位号变小」判据会误判) |
| 2 | **TWS 未满一圈,继续蓄数据** | point_type==0 且方位波位号 != 0;调用 track_process(model=2) 做滑窗凝聚 |
| 3 | **TAS 处理** | point_type==1;同时记录 TAS_track_idx 与最新时间戳 |
> 首次调用时 last_beam_num 为 INT_MAX,因此必然返回 1;track_clear_all() 后同样复位为 INT_MAX。
> 7d5a56f 起满圈判据不再依赖上一帧波位号:`last_beam_num` 仍被赋值(`track_clear_all()` 复位为 INT_MAX),但不参与判断;因此首帧是否返回 1 取决于该帧方位波位号是否为 0。调用方需保证方位波位号按 0~Beam_num-1 编号且每圈均出现 0,否则 TWS 通道将持续返回 2(不满圈)。
### 7.3 典型调用序列
@@ -333,18 +421,20 @@ flowchart TD
```mermaid
flowchart TD
A["data_preprocess: 点迹入 Data_buffer(度→弧度、高度=Range·sin(Elev)、时间戳缓存)"] --> B["波位号变小(一圈完成)→ model=1"]
A["data_preprocess: 点迹入 Data_buffer(度→弧度、高度=Range·sin(Elev)、时间戳缓存)"] --> B["方位波位号 == 0(一圈完成)→ model=1"]
B --> C["track_process(1)"]
C --> D["dot_coh 两帧滑窗凝聚 → point_recv"]
D --> E["track_asso: IMM 交互 → 点航波门筛选(NN)→ EKF 滤波 → 模型输出"]
E --> F["track_die: 外推超限/手动删除 → 输出消亡批号"]
F --> G["track_init: 点-临时航迹关联 → 点-航迹头关联 → 起批转可靠航迹"]
E --> F["track_die: 连续未关联超限 / 手动删除 → 输出消亡批号"]
F --> G["track_init: 点-临时航迹关联 → 点-航迹头关联 → 剩余点转新航迹头 → 起批转可靠航迹"]
G --> H["输出 Trust_Track_Output(含起批历史点)"]
H --> I["Beam_Ctrl: 手动 TAS 目标入队 / 波束输出"]
```
其中 model=2(未满一圈)只执行滑窗凝聚 dot_coh_process_buff,把上一帧凝聚完成的点输出到 point_recv 供下一圈的关联/起批使用,当前帧未凝聚点作为下一次的缓存——即「凝聚在前、关联在后、缓存一帧」的两帧滑窗结构。
输出环节(`track_asso_process()` 末尾)只写 `Track_Update_Flag==1` 的航迹:关联到实点、或外推成功(`Extrapolate_round <= MAX_EXTRAPOLATE_NUM` = 3)时置 1;外推超限的航迹不再推进位置与航迹时间,当圈也不输出,直到重新关联上实点才恢复(详见 9.9)。
### 8.2 TAS 流程(model=3)
```mermaid
@@ -353,29 +443,34 @@ flowchart TD
B --> C["dot_coh_tas 单帧凝聚 → point_recv_tas"]
C --> D["track_asso_tas: 仅对 tas_track_idx 批号做 IMM+EKF 关联(含高度门限)"]
D --> E["未关联 → 按 latest_timestamp 外推(ΔT≤0 跳过)"]
E --> F["track_die_tas: TAS 超时/手动删除 → 消亡输出"]
E --> F["track_die_tas: TAS 连续未关联超上限 / 手动删除 → 消亡输出"]
F --> G["输出该批号航迹(point_type=1)"]
G --> H["Beam_Ctrl: 队列轮转 + 预测波束输出"]
G --> H["Beam_Ctrl: 数据率门控 + 队内最早 CPI 目标 + 预测波束输出"]
```
TAS 输出同样受 `Track_Update_Flag` 门控:只有 `Track_Index == tas_track_idx` 且本圈有更新(关联到实点、或外推成功)时才向 `Trust_Track_Output` 写一行(`point_type=1`)。TAS 外推上限为 `MAX_EXTRAPOLATE_NUM_TAS`(相扫 5 / 机扫 48),判定方向与 TWS 相反(`Extrapolate_round >= 上限` 时才外推并输出),详见 9.9 与 11.2。
### 8.3 航迹起始流程(逻辑法)
```mermaid
flowchart TD
A["未关联点迹"] --> B["转航迹头 Temp_track(1点) buff_round=1"]
B --> C{"下一帧点迹"}
C --> D["点-航迹头关联:速度区间门限 + 径向速度同号 → 两点卡尔曼初始化(航迹头→2 点临时航迹)"]
D --> E{"后续点迹"}
E --> F["点-临时航迹关联:EKF 统计距离波门 d²≤64、折角<60°、径向速度同号、|Δr|≥5m"]
F --> G{"长度 ≥ track_start_point_num(3~9) 且 ≤10"}
G -- 是 --> H["屏蔽区判断 → 重复航迹去重(保留末点 d 更小者)→ 三点卡尔曼初始化"]
H --> I["转可靠航迹:分配航迹号 1~500,IMM 三模型同状态初始化 u=1/3"]
I --> J["按历史点数输出 Trust_Track_Output[i][0..L-1]"]
G -- 否 --> K{"buff_round>2 或 长度≥10 ?"}
K -- 是 --> L["临时航迹消亡"]
K -- 否 --> E
A["本圈未关联点迹(point_recv → point_process)"] --> A1["临时航迹所有点的 buff_round +1"]
A1 --> B["① 点-临时航迹关联(≥2 点临时航迹、末点 buff_round≥2):EKF 统计距离波门 d²≤64、折角<60°、径向速度同号、|Δr|≥5m → 复制原航迹并追加新点"]
B --> C["② 点-航迹头关联(1 点航迹头、buff_round≥2):V_MIN·ΔT ≤ dis ≤ V_MAX·ΔT、径向速度同号、dis ≥ |vr_point|·ΔT 且 dis ≥ |v_track_head|·ΔT → 两点卡尔曼初始化(航迹头 + 新点 → 2 点临时航迹)"]
C --> D["③ 删除 Use_Flag==1 的点迹,剩余点全部转为新航迹头 Temp_track(1 点、buff_round=1)"]
D --> E{"④ 长度 ≥ track_start_point_num(3~9) 且 ≤10 且不在起批屏蔽区 ?"}
E -- 是 --> F["重复航迹去重(保留末点 d 更小者)→ 三点卡尔曼初始化"]
F --> G["转可靠航迹:分配航迹号 1~500,IMM 三模型同状态初始化 u=1/3"]
G --> H["输出前 track_start_point_num 个历史点 Trust_Track_Output[i][0..L-1]"]
E -- 否 --> I["留在临时航迹表,下一圈继续关联"]
H --> J["⑤ 临时航迹消亡:末点 buff_round>2 或 长度≥10"]
I --> J
```
> 顺序说明:`track_init_process_logic()` 中四步的先后顺序固定、不可颠倒——先延长已有临时航迹,再与航迹头配对,两步都走完才生成新航迹头,最后才把长度达标的临时航迹转为可靠航迹(因此当圈新建的 2 点临时航迹不可能当圈起批,长度刚达标的临时航迹则可以当圈起批);临时航迹消亡在第 ⑤ 步、即全部起始动作之后执行。
>
> 第 ① ② 步**互不排斥**:两者都只把命中的点迹标记 `Use_Flag=1`,航迹头关联并不检查临时航迹关联留下的标记,因此同一点迹可能既延长一条临时航迹、又开启一条新的两点临时航迹;两类关联也都不做一对一分配(同一条临时航迹可被本圈多个点延长,同一个航迹头也可与本圈多个点配对)。只有走完两步后才统一删除 `Use_Flag==1` 的点迹,其余点迹成为新航迹头。
---
## 9. 算法实现详解
@@ -427,29 +522,18 @@ $$
### 9.3 航迹起始(逻辑法)
#### (1) 点-航迹头关联(两点起始)
每圈(model=1)在 `track_init_process_logic()` 中按**固定顺序**执行下列四步,顺序不可颠倒:
对单点航迹头(buff_round ≥ 2),量测点需同时满足**速度区间门限**与**径向速度同号**:
1. **点-临时航迹关联**:本圈未关联点迹优先延长已有的 ≥2 点临时航迹(判据见 (1));
2. **点-航迹头关联**:随后点迹与 1 点航迹头配对,产生新的 2 点临时航迹(判据见 (2));
3. **剩余点迹转航迹头**:前两步只把命中的点迹标记为已使用(`Use_Flag=1`),两步都做完后才统一删除这些点迹,其余点迹逐条转为新航迹头(见 (3));
4. **转可靠航迹(起批)**:长度达标的临时航迹经三点卡尔曼初始化转为可靠航迹(见 (4));起批动作完成后才消亡不满足条件的临时航迹。
- 近程(work_mode==0):
> 两点补充:① 第 1、2 步**互不排斥**——航迹头关联不检查临时航迹关联留下的 `Use_Flag`,因此同一点迹可能既延长一条临时航迹、又开启一条新的两点临时航迹;两类关联也都不做一对一分配(同一条临时航迹可被本圈多个点延长,同一个航迹头也可与本圈多个点配对)。② 每圈入口先把**所有**临时航迹所有点的 `buff_round` 加 1 再做关联,新建点(航迹头或被关联上的新点)`buff_round = 1`,因此当圈新建的 2 点临时航迹要到下一圈末点 `buff_round` 变为 2 之后才参与第 1 步。
$$
r_p \ge 1000:\ V_{min}\Delta T \le d_{12} \le V_{max}\Delta T;\qquad
r_p < 1000:\ \tfrac12 V_{min}\Delta T \le d_{12} \le \tfrac12 V_{max}\Delta T
$$
#### (1) 点-临时航迹关联(延长 ≥2 点临时航迹)
- 中/远程(work_mode!=0):
$$
r_p \ge 2000:\ V_{min}\Delta T \le d_{12} \le V_{max}\Delta T;\qquad
r_p < 2000:\ \tfrac12 V_{min}\Delta T \le d_{12} \le \tfrac13 V_{max}\Delta T
$$
其中 d12 为两点间平面距离,ΔT 为时间差(秒),并需 vr_point · vr_head > 0。关联成功用 **两点卡尔曼初始化**(见 9.5.2)。
#### (2) 点-临时航迹关联(三点及以上)
对 ≥ 2 点的临时航迹,用航迹末点状态做 EKF 一步预测,计算三维(距离-方位-径向速度)马氏统计距离 d(见 9.5.3),并要求 |r_point − r_head| ≥ 5 m,同时满足:
对 ≥ 2 点的临时航迹(末点 buff_round ≥ 2),用航迹末点状态做 EKF 一步预测,计算三维(距离-方位-径向速度)马氏统计距离 d(见 9.5.3),并要求 |r_point − r_temp| ≥ 5 m,同时满足:
$$
d^2 \le TRACK\_START\_THRESHOLD^2 = 64,\qquad
@@ -463,13 +547,37 @@ $$
\alpha=\arccos\left(\frac{\vec{D_{12}}\cdot\vec{D_{23}}}{\|\vec{D_{12}}\|\,\|\vec{D_{23}}\|}\right)\cdot\frac{180}{\pi}
$$
#### (3) 转可靠航迹(起批)
关联成功后**复制原临时航迹并追加该点迹**,新航迹的 4 维状态用最近两点做**两点卡尔曼初始化**(见 9.5.2)重新得到。
临时航迹长度 L 满足 track_start_point_num ≤ L ≤ 10(起批点数由参数给定,典型 3~4,接口层校验 3~9)且不在起批屏蔽区内(屏蔽区为距离-方位矩形区域,最多 30 个)时进入起批队列;同源(共享前三点)的重复航迹只保留末点 d 最小者。起批时用 **最近三点卡尔曼初始化** 得到 6 维状态,构造 Trust_Track:
#### (2) 点-航迹头关联(两点起始)
对单点航迹头(buff_round ≥ 2),量测点需同时满足以下四个条件(7d5a56f 起的判据):
$$
V_{min}\Delta T \le d_{12} \le V_{max}\Delta T,\qquad
v_{r,point}\cdot v_{r,head}>0,\qquad
d_{12} \ge \left|v_{r,point}\right|\Delta T,\qquad
d_{12} \ge \left|v_{r,head}\right|\Delta T
$$
其中 d12 为两点间平面距离,ΔT 为时间差(秒),V_min/V_max 取 `RadarPara.V_MIN / V_MAX`(代码中 vmax 的近程/中远程分支取值相同,均为 V_MAX)。后两条为**多普勒-几何一致性约束**:切向飞行目标在 ΔT 内的实际位移远小于 |vr|·ΔT,据此被排除,用于限制切向高速点迹起批出的短航迹。
> 历史行为(已注释停用):原先按扫描体制与距离分段收紧速度门限——近程 r≥1000 用全速、r<1000 用半速;中远程 r≥2000 用全速、r<2000 取下限 1/2·V_min·ΔT / 上限 1/3·V_max·ΔT。
关联成功用 **两点卡尔曼初始化**(见 9.5.2)生成 2 点临时航迹:第 1 点为原航迹头,第 2 点为该点迹。
#### (3) 剩余点迹转航迹头
第 (1)(2) 步结束后统一删除 `Use_Flag==1` 的点迹;剩余点迹回写 `point_recv`(随后被整体清空),并**逐条新建 1 点临时航迹(航迹头)**:位置直接取点迹的直角坐标、速度置 0、`buff_round = 1`。新航迹头当圈不再参与任何关联,只能从下一圈开始按第 (2) 步配对。
#### (4) 转可靠航迹(起批)
临时航迹长度 N 满足 track_start_point_num ≤ N ≤ 10(起批点数由参数给定,典型 3~4,接口层校验 3~9)且不在起批屏蔽区内(屏蔽区为距离-方位矩形区域,最多 30 个)时进入起批队列;同源(前三点时间/距离相同)的重复航迹只保留 d 较小者(比较下标 `track_start_point_num-1` 处的 d)。起批时令 L = track_start_point_num,用航迹中下标 L-3、L-2、L-1 的三个点做 **三点卡尔曼初始化** 得到 6 维状态(航迹长度恰为 L 时即最近三点),构造 Trust_Track:
- 三个 IMM 子模型用同一状态/协方差初始化,模型概率 u = [1/3, 1/3, 1/3];
- 分配航迹号(1~500,见 9.8);
- 将临时航迹的全部 L 个点写入 Trust_Track_Output(每条航迹最多 10 个历史点)。
- 输出仅写入前 L 个历史点(`Trust_Track_Output[i][0..L-1]`,`Point_Sum = L`),航迹长度大于 L 时其余点不输出;
- 起批输出不写 `Flag_Direction`、`x_Predict/y_Predict/z_Predict`、`Range_Predict/Azimuth_Predict/Elevation_Predict`、`Target_Type`、`prf_point`、`v_z` 等字段,且 DLL 不清零输出数组,宿主读到的是自身缓存的旧值(详见 docs/track_init_readme.md 第 14 节)。
### 9.4 IMM 框架(3 个 Singer 模型)
@@ -646,7 +754,7 @@ $$
$$
v_{bind}=\frac{150000}{f_{GHz}\cdot PRI_{µs}}\quad(\mathrm{m/s}),\qquad
f_{GHz}=16.8+0.02\cdot f_{ind}
f_{GHz}=9.2+0.02\cdot f_{ind}
$$
(PRI ≤ 0 或 f ≤ 0 时返回安全值 1.0,接口约定 PRI 不允许为 0。)
@@ -657,18 +765,19 @@ $$
#### TWS(track_asso.cpp)
对每条航迹 × 每个点计算三模型 d1, d2, d3,波门按航迹距离分段:
对每条航迹 × 每个点计算三模型 d1, d2, d3,波门按航迹距离分段(`ASSO_THORD = 4`,判据写作 `d*d < ASSO_THORD*ASSO_THORD`,而 d 本身已是马氏距离平方,故下表的等价门限为 d < 4):
| 航迹距离 | 模型 1/2 门限 | 模型 3 门限 |
|---|---|---|
| r_track ≤ 500 m | d_i² < 9 | d_3² < 9/1000(≈关闭) |
| 500 < r_track < 1000 m | d_i² < 9 | d_3² < 9/100(收紧) |
| r_track ≥ 1000 m | d_i² < 9 | d_3² < 9 |
| r_track ≤ 500 m | d_i² < 16 | d_3² < 16/1000(≈关闭) |
| 500 < r_track < 1000 m | d_i² < 16 | d_3² < 16/100(收紧) |
| r_track ≥ 1000 m | d_i² < 16 | d_3² < 16 |
同时满足两个附加门限:
关联判据在 `model_filter()` 中拆为三个布尔量,三者同时通过才记为候选:`thd_d`(上表统计距离门限)、`thd_h`(高度门限)、`thd_r`(距离门限)。
- **俯仰门限 d_p**:当前恒为 true(分距离段 7°/5°/10° 的逻辑在源码中被注释停用);
- **距离门限 d_r**:以航迹-点迹弦距 R = √(r_t² + r_p² − 2·r_t·r_p·cos Δθ)(Δθ 为环绕方位差)与航迹速度位移 VT = v_track·ΔT 判断:
- **高度门限 thd_h**(e0bf227 起生效):以 |h_track − h_point| 与航迹距离分段比较,取值与 TAS 通道一致——≤ 1000 m 时 ≤ 150 m、1000~2000 m 时 ≤ 200 m、2000~3000 m 时 ≤ 250 m、3000~5000 m 时 ≤ 300 m、> 5000 m 时不约束。该项原为注释停用的代码块(等价于恒通过),现正式参与关联判定,用于压制俯仰测量跳变引起的错误关联;
- **俯仰门限 d_p**:分距离段 7°/5°/10° 的逻辑仍在源码中注释停用,不参与判定(其作用已由高度门限覆盖);
- **距离门限 thd_r**:以航迹-点迹弦距 R = √(r_t² + r_p² − 2·r_t·r_p·cos Δθ)(Δθ 为环绕方位差)与航迹速度位移 VT = v_track·ΔT 判断:
$$
VT<100\ \text{且}\ R>300\ \Rightarrow 拒绝;\qquad
@@ -676,19 +785,40 @@ VT<100\ \text{且}\ R>300\ \Rightarrow 拒绝;\qquad
VT>300\ \text{且}\ R>1200\ \Rightarrow 拒绝
$$
候选对按 d_min = min(d1,d2,d3) 全局排序,**最近邻**原则逐个取点:每条航迹每圈最多关联一个点、每个点只属于一条航迹。关联成功后:三模型分别 EKF 更新 → 模型概率更新 → 更新航迹时间/幅度/SNR/RCS/十字星数据 → 高度平滑。ΔT ≤ 0(乱序/重复时间戳)的候选被剔除且不占用点迹、不刷新航迹状态(BUG-15 修复)。
候选对按 d_min = min(d1,d2,d3) 全局排序,**最近邻**原则逐个取点:每条航迹每圈最多关联一个点、每个点只属于一条航迹。关联成功后:三模型分别 EKF 更新 → 模型概率更新 → 更新航迹时间/幅度/SNR/RCS/十字星数据 → 高度平滑。
**靠近/远离方向标志 Flag_Direction(fc49707 起、42b598e 调整,TWS/TAS 同步)**:不再使用实测径向速度(原 `Flag_Direction = vr_point > 0 ? 1 : 0`,实测多普勒在速度模糊时符号可能取反),改为按航迹 IMM 融合状态的速度矢量与位置矢量判定:
$$
D=\operatorname{atan2}(X[4],X[1])\ \text{(速度方向角)},\qquad
Flag\_Direction=\begin{cases}1,& \arccos\!\left(\dfrac{-x\cos D-y\sin D}{r}\right)<\dfrac{\pi}{2}\\[2mm]0,&\text{否则}\end{cases}
$$
其中 x、y 为经 `Sys_delay` 补偿后的航迹位置,r = √(x²+y²),`Direction_Angle` 输出字段即 D 的度数形式。该判据等价于「目标速度方向与『雷达→目标』位置方向的夹角大于 90°」,即**滤波速度矢量的径向分量指向雷达(径向接近)时判为靠近(1)**,否则远离(0);中间版本曾按合速度归一化并把门限取为 π/4(45° 锥角),42b598e 去掉归一化并放宽到 π/2。外推(虚点)输出同样按该判据写 `Flag_Direction`。
**ΔT ≤ 0 的候选(604f158 起的行为)**:ΔT = (点迹 CPI 时间 − 航迹 T_track)/1000,ΔT ≤ 0 表示该点迹时间戳不晚于航迹时间(典型场景:航迹已按 TAS 点迹更新过,本圈 TWS 点迹的时间戳更早,或存在重复时间戳)。这种候选**不再被剔除**,仍参与最近邻最小 d 的比较与选取,但选中后:
- 不执行 IMM/EKF 更新,不写回 X1/X2/X3、P1/P2/P3 与模型概率;
- 不刷新航迹时间 T_track / GNSS_time 与点迹信息;
- 不置实点标志:`Extrapolate_round`、`point_flag`、`associate_point_number` 均保持原值,因此该航迹仍按「未关联航迹」在后续外推环节推进一帧(见 9.9);
- 该候选对连同该航迹本圈的其余候选对被一并移出候选表,因此该航迹本圈不会再关联其他点迹;由于时间戳更早的候选仍参与最小 d 比较,它可能先于有效候选(ΔT>0)被取中;
- 该点迹的 `Use_Flag` 被置 1 消费掉:`track_asso_process()` 在关联结束后会把 `Use_Flag==1` 的点迹从 `point_recv` 中删除(track_init 入口也会再剔除一次 `Use_Flag==1` 的点),因此该点迹不会进入航迹起始、不会再参与起批,避免生成虚假 TWS 航迹。
> 历史行为:604f158 之前,ΔT ≤ 0 的候选在选取时被直接剔除并 `continue`,点迹保持未被使用,可能被后续用于航迹起始。该分支原有的 `RDP_LOG` 告警日志现已注释停用;TAS 关联(track_asso_tas.cpp,见下)中同类告警日志仍启用。**b227ae1 起该分支的写法由 `if (delta_T <= 0) {空} else {...}` 调整为 `if (delta_T > 0) {...}`**,判定结果与滤波行为不变;同一提交为每个候选补充了 `min_d_flag`(被最近邻取中时置 1)并配合 `RdpRecAssoInfo` 记录全部候选与取中结果(见 5.2 的 rdp_record 行)。
#### TAS(track_asso_tas.cpp)
只处理 tas_track_idx 指定的航迹,门限:
只处理 tas_track_idx 指定的航迹,只有统计距离与高度两个门限,两者同时通过(`thd_d && thd_h`)才记为候选:
$$
d_i^2 < ASSO\_THORD^2 = 9\ \ \text{且高度门限}\ d_h=1
thd\_d:\ d_i^2 < ASSO\_THORD^2 = 16 \qquad\text{且}\qquad thd\_h:\ \text{高度门限通过}
$$
高度门限按航迹距离分段:≤1000 m:|h_t−h_p| ≤ 150 m;1000~2000 m:≤ 200 m;2000~3000 m:≤ 200 m;3000~5000 m:≤ 300 m;>5000 m:不约束。
高度门限按航迹距离分段:≤1000 m:|h_t−h_p| ≤ 150 m;1000~2000 m:≤ 200 m;2000~3000 m:≤ 250 m;3000~5000 m:≤ 300 m;>5000 m:不约束(与 TWS 通道 9.6 的取值相同)。
> ⚠ **波门语义提示**:代码中 d 本身已是马氏距离平方,但门限写作 d*d < ASSO_THORD*ASSO_THORD,等效于 d < 3(而非 √d < 3)。该行为在 BUG-20 中被识别,**有意保留未改**;ASSO_THORD/1000、/100 已改为浮点除法(BUG-21 修复)。
输出航迹同样写入**方向标志** `Flag_Direction`,判据与 TWS 通道完全一致(fc49707/42b598e:按 IMM 融合速度矢量与位置矢量的夹角判定,见上文 TWS 小节)。
> ⚠ **波门语义提示**:代码中 d 本身已是马氏距离平方,但门限写作 d*d < ASSO_THORD*ASSO_THORD,等效于 d < 4(而非 √d < 4)。该行为在 BUG-20 中被识别,**有意保留未改**;ASSO_THORD/1000、/100 已改为浮点除法(BUG-21 修复)。TAS 不区分距离段,`ASSO_THORD` 原值直接作为门限。
### 9.7 高度平滑
@@ -708,29 +838,44 @@ $$
### 9.9 航迹外推与消亡
**外推**:未关联到实点的 TWS 航迹(manual_tracking_flag==0)按工作模式数据率 DATA_RATE_SHORT/MIDDLE/FAR 预测一帧;TAS 通道按 latest_timestamp − T_track 预测(ΔT ≤ 0 跳过);预测用各子模型 Singer F/Qj 推进,Extrapolate_round+1,point_flag=0(虚点/补点),航迹时间同步推进。
#### 外推
**消亡**:
- **TWS**:未关联到实点的航迹(`manual_tracking_flag==0`)按工作模式数据率 DATA_RATE_SHORT/MIDDLE/FAR 预测一帧(ΔT≤0 的候选虽被消费但未刷新航迹状态,其 `point_flag` 仍为 0,故同样在本环节外推,见 9.6)。预测用各子模型 Singer F/Qj 推进,`Extrapolate_round+1`,`point_flag=0`(虚点/补点),航迹时间同步推进,并置 `Track_Update_Flag=1`(本圈输出)。
- **TWS 外推上限(50e0b5c)**:仅当 `Extrapolate_round <= MAX_EXTRAPOLATE_NUM`(=3)时才做上述预测;超过上限后不再推进位置与航迹时间,只执行 `Extrapolate_round+1`、`point_flag=0`、`Track_Update_Flag=0`——该航迹当圈不输出、位置冻结在最后一次预测处,直到重新关联到实点(`Extrapolate_round` 清零)才恢复。目的是避免长时间未关联的航迹被反复外推到远处。
- **TAS**:按 `latest_timestamp − T_track` 预测(ΔT ≤ 0 跳过),航迹时间推进到最新时间戳。上限判定为 `Extrapolate_round >= MAX_EXTRAPOLATE_NUM_TAS`(相扫 5 / 机扫 48)时才预测并输出,未达该轮数时只累加 `Extrapolate_round` 且不输出(位置与航迹时间停在最后一次关联处)。⚠ 判定方向与 TWS 的 `<=` 相反,见 11.2。
- **输出门控**:`track_asso_process()` / `track_asso_process_tas()` 的输出循环只写 `Track_Update_Flag==1` 的航迹,写完即清零;因此「本圈未更新」的航迹(外推超限等)不会出现在 `Trust_Track_Output` 中。
#### 消亡(track_die.cpp / track_die_tas.cpp)
判据为 `Extrapolate_round > 阈值`(50e0b5c 起由 `>=` 改为 `>`),满足其一即消亡:
| 通道 | 条件 |
|---|---|
| TWS | Track_Mode==0 且 Extrapolate_round ≥ TRACK_DIE_ROUND(=4),或 manual_delete_flag==1 |
| TAS | Track_Mode==1 且 Extrapolate_round ≥ TRACK_DIE_ROUND_TAS(相扫 5 / 机扫 96),或 manual_delete_flag==1 |
| TWS 普通航迹 | Track_Mode==0 且 primary_target_flag==0 且 Extrapolate_round > TRACK_DIE_ROUND(=6) |
| 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(相扫 10 / 机扫 96) |
| 任意航迹 | manual_delete_flag==1(手动删除标记,不区分通道) |
消亡批号写入 Track_die_Index_Output 并删除本地航迹。
消亡批号写入 Track_die_Index_Output 并删除本地航迹。由于 `Extrapolate_round` 每圈未关联只自增 1,普通 TWS 航迹实际连续 7 圈无实点关联才消亡(重点航迹 11 圈;TAS 相扫 11 圈 / 机扫 97 圈)。`track_process()` 中的调用顺序为 `track_asso → track_die → track_init`(data_process.cpp),即消亡先于航迹起始执行。
> 重点目标标记(`primary_target_add` / `primary_target_cancel`)只影响消亡轮数,不改变关联、滤波、波束控制等其他环节,也不体现在输出航迹字段中。
### 9.10 TAS 波束控制(tas_ctrl.cpp)
- **入队条件**(仅手动):manual_tracking_flag==1 且 Track_Mode==0 且队列未满(MAX_TAS_NUM,相扫 4 / 机扫 1)且不在 TAS 禁止区内。入队即置 Track_Mode=1 并输出一次航迹更新。
- **入队条件**(仅手动):manual_tracking_flag==1 且 Track_Mode==0 且队列未满(MAX_TAS_NUM,相扫 4 / 机扫 1)且不在 TAS 禁止区内。入队即置 Track_Mode=1、以航迹时间初始化 last_track_time,并输出一次航迹更新。
- **出队**:队列中目标在航迹表中不存在、或不再处于手动 TAS 状态时清除。(自动转 TAS / 自动退出的 tas_auto_start/tas_auto_end 逻辑全部注释停用——只支持手动。)
- **波束输出**:取队首目标,先用**数据率门控**:航迹时间 T_track 与当前最新时间戳 latest_timestamp 的差值必须大于 `1 / DATA_RATE_TAS` 秒才输出(保证 TAS 目标数据率不随波束时间变化;DATA_RATE_TAS ≤ 0 时不做门控,按原逻辑输出)。门控通过后,用其状态外推到 latest_timestamp:
- **波束输出**:在 TAS 队列内遍历,逐个目标先按**数据率门控**筛选——仅要求目标距上次波束输出(`last_track_time`)的时间 `delta_T_last_track = (latest_timestamp − last_track_time)/1000` 大于运行时参数 `Work_Parameter.DATA_RATE_TAS` 秒(单位 s,1521592 起由 Hz 改为 s,需为 >0 的有效值),保证 TAS 目标输出间隔不随波束时间(Beam_Ctrl 调用频率)变化;对通过门控的目标再比较航迹时间 `T_track`,取 CPI 时间最早者,选中后按其 CPI 时间与最新时间戳之差外推到 latest_timestamp:
$$
\Delta T=(latest\_timestamp-CPI\_time)/1000,\qquad
x_p=x+v_x\Delta T,\quad y_p=y+v_y\Delta T,\quad
r_p=\sqrt{x_p^2+y_p^2},\quad \theta_p=\operatorname{atan2}(y_p,x_p)
$$
输出 TrackingBeam{open_flag=1, type=1, Range=rp, Azi=θp(°), Elev=asin(h/rp)(°), TAS_track_index};队空、目标不在航迹表或数据率门控未通过时 open_flag=0。每次 Beam_Ctrl 末尾做队列轮转(实现 TAS_QUEUE_LENGTH 深度的循环跟踪)。
输出 TrackingBeam{open_flag=1, type=1, Range=rp, Azi=θp(°), Elev=asin(h/rp)(°), TAS_track_index};队空、目标不在航迹表或数据率门控未通过时 open_flag=0。输出后更新该目标队列项的 last_track_time;原每次 Beam_Ctrl 末尾的整队循环移位(队列轮转)已停用,改为按数据率门控在队列内轮换目标。
> e0bf227 修正:数据率门控原先同时要求「距上次波束输出」与「航迹时间 T_track 距最新时间戳」都超过 DATA_RATE_TAS,第二个条件与「选 CPI 最早目标」的语义冲突(航迹时间越早反倒越容易被抑制),会漏掉本应输出的波束;现已只保留前者。
- tas_ctrl_process 入口强制 *Trust_track_num_Output=0 并在写入前检查容量,防止输出数组越界(BUG-09 修复)。
---
@@ -741,7 +886,11 @@ $$
| 宏 | 相扫 PHASE_SCANNING | 机扫 MECHANICAL_SCANNING | 含义 |
|---|---|---|---|
| DATA_RATE_TAS | 0.3 | 0.0625 | TAS 数据率(1/s)。**TAS 波束输出门控已改用运行时参数 RadarPara.DATA_RATE_TAS(见 10.2),不再使用该宏**;宏目前仅被未加入 CMake 的 track_asso_direct_tracking.cpp 引用 |
| RDP_LOG_ENABLE | 注释停用(默认不启用) | 同左 | 日志文件输出开关:定义时将 `std::cout` 重定向到日志文件;**当前源码中该宏为注释状态**(parameters.h 的注释文字仍写「定义(默认)」,以代码实际状态为准),不重定向、日志输出到标准输出/控制台 |
| RDP_LOG_DIR | `.\rdp_logs\` | 同左 | 日志文件输出目录(相对宿主进程当前工作目录,目录不存在时自动创建) |
| RDP_RECORD_ENABLE | 由 CMake 选项 `RDP_DATA_RECORD` 定义(AUTO 时 Debug 定义) | 同左 | 数据记录调试开关:**不要在 parameters.h 中手工定义**(由构建系统传入);定义时记录模块参与编译,未定义时 `RDP_RECORD`/`RDP_RECORD_BATCH` 展开为空操作,见 4.3 与 11.3 |
| RDP_RECORD_DB_DIR | `.\rdp_records\` | 同左 | 数据记录库输出目录(相对宿主进程当前工作目录,目录不存在时自动创建;仅记录功能启用时产生) |
| DATA_RATE_TAS | 已停用(原 0.3) | 已停用(原 0.0625) | 编译期宏已注释停用;TAS 数据率统一由运行时参数 RadarPara.DATA_RATE_TAS 提供(见 10.2) |
| SIGMA_R / SIGMA_A / SIGMA_E / SIGMA_V | 10.0 / 0.02 / 0.2 / 2.0(两体制相同) | 同左 | 量测误差:距离 m / 方位 rad / 俯仰 rad / 速度 m/s |
| DOT_COH_RANGE / DOT_COH_V / DOT_COH_AZI | 80 / 2 / 6 | 同左 | 凝聚门限:距离 m / 速度 m/s / 方位 ° |
| MAX_BEAM_NUM | 100 | 同左 | 最大波位数 |
@@ -749,12 +898,15 @@ $$
| R_MIN / R_MAX | 100 / 100000 | 同左 | 有效量程(m) |
| TRACK_START_THRESHOLD | 8 | 同左 | 起批波门(d² 门限 64;越大越容易起批) |
| ALPHA_START | 60 | 同左 | 起批折角(°) |
| TRACK_DIE_ROUND | 4 | 4 | TWS 航迹消亡外推轮数 |
| TRACK_DIE_ROUND_TAS | 5 | 96 | TAS 航迹消亡外推轮数 |
| ASSO_THORD | 3 | 同左 | 关联波门 |
| TRACK_DIE_ROUND | 6 | 6 | TWS **普通**航迹消亡圈数(判据 Extrapolate_round > 6;50e0b5c 起由 4 调整为 6,判据由 ≥ 改为 >) |
| TRACK_DIE_ROUND_PRIMARY | 10 | 10 | TWS **重点航迹**消亡圈数(primary_target_flag==1 时套用;50e0b5c 新增) |
| TRACK_DIE_ROUND_TAS | 10 | 96 | TAS 航迹消亡圈数(判据 Extrapolate_round > 阈值;相扫 50e0b5c 起由 5 调整为 10,机扫保持 96) |
| MAX_EXTRAPOLATE_NUM | 3 | 3 | TWS 未关联航迹最大外推圈数(Extrapolate_round ≤ 3 才继续外推;50e0b5c 新增) |
| MAX_EXTRAPOLATE_NUM_TAS | 5 | 48 | TAS 最大外推圈数(判据为 Extrapolate_round ≥ 该值时才外推,方向与 TWS 相反,见 9.9 与 11.2;50e0b5c 新增) |
| ASSO_THORD | 4 | 同左 | 关联波门(判据 d*d < ASSO_THORD²,d 已是马氏距离平方,等效 d < 4;e0bf227 起由 3 调大至 4) |
| H_F_WIN_LEN | 3 | 同左 | 高度平滑窗长基数(实际 +2/+3/+4/+6) |
| TAS_QUEUE_LENGTH / MAX_TAS_NUM | 4 / 4 | 1 / 1 | TAS 队列长度 / 最大 TAS 目标数 |
| FREQ0~FREQ20 | 16.8 GHz | 同左 | 各频点频率 |
| FREQ0 | 9.2 GHz | 同左 | 载波频点(原 FREQ0~FREQ20 一组宏已精简为单个 FREQ0,e729c5b 起由 16.8 GHz 改为 9.2 GHz) |
### 10.2 运行时参数(RadarPara)
@@ -771,7 +923,7 @@ $$
| north_angle | 北偏角 | 保留 |
| V_MAX / V_MIN | 目标速度上下限(m/s) | 航迹头关联速度区间门限 |
| DATA_RATE_SHORT / MIDDLE / FAR | 三模式数据率(s) | TWS 外推步长 |
| DATA_RATE_TAS | TAS 模式数据率(1/s) | 由调用者经 track_process_parameters_initial 输入;TAS 波束输出门控阈值 1/DATA_RATE_TAS 秒,≤0 时不做门控 |
| DATA_RATE_TAS | TAS 波束输出间隔(s) | 由调用者经 track_process_parameters_initial 输入(须为 >0 的有效值);`tas_beam_output()` 门控阈值为 DATA_RATE_TAS 秒(1521592 起单位由 Hz 改为 s),引导跟踪外推同样使用该运行时参数 |
| track_start_point_num | 起批点数 | 校验范围 3~9 |
| track_start_threshold / track_asso_threshold / track_asso_threshold_tas | 起批/关联波门 | **⚠ 声明但当前实现未使用**,实际门限为宏 TRACK_START_THRESHOLD / ASSO_THORD |
| Model1/2/3_Q_fast/slow | 三个 Singer 模型过程噪声强度 | Model1_Q_fast 未使用;分档规则见 9.5.1 |
@@ -780,15 +932,17 @@ $$
## 11. 已知问题与限制
详见 BUG_REPORT.md(37 项分析)与 BUG_FIX_REPORT.md(修复明细)。要点:
详见 archived_docs/BUG_REPORT.md(37 项分析)与 archived_docs/BUG_FIX_REPORT.md(修复明细)。要点:
### 11.1 已修复(BUG_FIX_REPORT.md)
### 11.1 已修复(archived_docs/BUG_FIX_REPORT.md)
- **越界/防御**:TAS Point_Sum 限幅 150(BUG-04);size()-1 下溢(BUG-05);起批点数校验(BUG-06);航迹号下标校验(BUG-10);Beam_Ctrl 输出计数清零+容量检查(BUG-09);消亡空向量保护(BUG-34);航迹号 500 可复用(BUG-35);局部结构体值初始化(BUG-37)。
- **时间戳/时序**:TWS/TAS 关联 ΔT≤0 拒绝(BUG-15/16);TAS 外推负时间差跳过(BUG-17);Bind_speed 除零保护(BUG-19)。
- **时间戳/时序**:TWS 关联 ΔT≤0 的候选不参与滤波更新、不刷新航迹状态并消费该点迹(604f158 起由「剔除候选、点迹保持未使用」改为当前行为,仍属 BUG-15 处置范围);TAS 关联 ΔT≤0 拒绝(BUG-16);TAS 外推负时间差跳过(BUG-17);Bind_speed 除零保护(BUG-19)。
- **数值稳定性**:方位残差 ±π 环绕(BUG-22);IMM 概率零分母/奇异保护(BUG-23);EKF 似然 3×3 新息协方差(BUG-24);航向角 atan2(BUG-26);asin 定义域钳位(BUG-27);近程模型 3 门限浮点除法(BUG-21);track_clear_all 全模块 reset(BUG-28);未初始化变量修复(BUG-11/12/14)。
- **后修订(e0bf227,BUG_FIX_REPORT.md 成文之后)**:TWS 关联高度门限由注释状态改为实际生效(`track_asso.cpp`),统计距离/高度/距离三类判据拆为独立布尔量,`ASSO_THORD` 由 3 调大至 4;TAS 关联高度门限 2000~3000 m 段放宽至 250 m;修复 `tas_beam_output()` 数据率门控误判(详见 CHANGELOG 的 Unreleased 与 9.6、9.10)。
- **后修订(50e0b5c、fc49707、42b598e、e8c56f9,BUG_FIX_REPORT.md 成文之后)**:输出航迹增加 `Track_Update_Flag` 门控(只输出本圈有更新的航迹);未关联航迹外推增加圈数上限;消亡判据改为 `>`,正常/重点航迹分别定为 6 / 10 圈(TAS 相扫 10、机扫 96);`Flag_Direction` 改用滤波速度矢量判定,修复速度模糊时的向站/背站误判;手动操作接口补 `break`;`DataRev`/`Track` 新增归一化幅度与速度维 FFT 点数字段(输入侧其后已打通,TAS 输出仍未写回,见 11.2)。
### 11.2 有意保留 / 未修复(标注于 BUG_REPORT)
### 11.2 有意保留 / 未修复(archived_docs/BUG_REPORT.md 标注项 + 后续源码观察)
- **BUG-01**:基类无虚析构,Destroy() 按基类指针 delete。
- **BUG-02**:Hight_smooth 只增不减,长时运行内存持续增长。
@@ -797,11 +951,14 @@ $$
- **BUG-08**:消亡航迹输出数组容量未传入,仅约定 ≤500。
- **BUG-13**:Work_Parameter 等成员构造后未初始化,必须先调 track_process_parameters_initial 再处理。
- **BUG-18**:卡尔曼两点/三点初始化对零/负时间差无保护(起批时间戳必须严格递增)。
- **BUG-20**:门限对马氏距离平方再次平方(等效 d<3),有意保留。
- **BUG-25**:TWS 关联/输出未排除 Track_Mode==1 的 TAS 航迹(原过滤逻辑被注释)。
- **BUG-20**:门限对马氏距离平方再次平方(e0bf227 把 `ASSO_THORD` 调为 4 后等效 d < 4),有意保留。
- **BUG-25**:TWS 关联/输出未排除 Track_Mode==1 的 TAS 航迹(原 `manual_tracking_flag==0` 过滤代码块已于 fc49707 删除,未恢复过滤)。
- **BUG-29**:tracking_stop 后 TAS 队列项延迟到下一次 Beam_Ctrl 才移除。
- **BUG-33**:起批关联未显式校验 ΔT>0。
- **BUG-36**:TAS 波束关闭时只清 open_flag,其余字段保留旧值。
- **归一化幅度 / 速度维 FFT 点数(e8c56f9 新增,输入侧已打通)**:`data_preprocess()` 现已在 TWS、TAS 两条分支都把 `DataRev.amp_asv` / `DataRev.prn_fftn` 拷入内部 `PointRecv`(`data_process.cpp`,并随 `RdpRecPoint` 落库),因此 TWS 关联路径(`track_asso.cpp` 写航迹、输出时写回 `Track`)取到的是有效数据。**TAS 通道仍未打通**:`track_asso_tas.cpp` 的关联与输出路径没有涉及这两个字段,TAS 输出航迹中 `amp_asv_point` / `prn_fftn_point` 仍保持宿主原值,宿主暂不应依赖 TAS 侧的这两个字段。
- **TAS 外推上限判据方向与 TWS 相反(50e0b5c)**:TWS 用 `Extrapolate_round <= MAX_EXTRAPOLATE_NUM` 外推,TAS 用 `Extrapolate_round >= MAX_EXTRAPOLATE_NUM_TAS`,实际效果是 TAS 航迹在未达上限的若干圈内既不外推也不输出(位置与航迹时间停在最后一次关联处),达到上限后才恢复逐帧外推输出;如属笔误,宜与 TWS 侧统一。
- **Flag_Direction 判据的 acos 无定义域钳位(fc49707/42b598e)**:对 `(-x·cos D − y·sin D)/r` 直接取 `acos`(与 BUG-27 同类问题),浮点舍入使参数略微越过 ±1 时返回 NaN,比较为假、按「远离 0」输出;`r` 为 0 时还存在除零。
### 11.3 未启用功能
@@ -810,17 +967,51 @@ $$
- TAS **自动**开启/退出逻辑(tas_auto_start/tas_auto_end)注释停用,仅支持手动 tracking_start/tracking_stop。
- RadarPara 中的起批/关联波门字段未被实现读取(见 10.2)。
- build/output/ 下为早期打包导出物,其 data_process_class_dll.h 时间戳仍为 32 位 int,与当前 64 位接口不一致,勿作为对接依据。
- **数据记录调试功能默认只在 Debug 启用**:CMake 选项 `RDP_DATA_RECORD` 的 AUTO 默认值为「Debug 启用、Release 关闭」,交付/Release 构建下记录模块(`rdp_record.cpp`、`sqlite3.c`)不参与编译、`RDP_RECORD` 为空操作(导出符号与依赖均无变化);如需在 Release 构建中临时排查可显式 `-DRDP_DATA_RECORD=ON`,但正式交付应保持 AUTO/OFF(见 docs/data_record_debug.md)。记录点中的字段错误只在启用记录功能的构建中才会被检查(关闭时宏丢弃实参)。
- **记录表的两个观察**:① 可靠航迹表 `RdpRecTrustTrack` 在输出循环内写库,而该循环入口已把 `Track_Update_Flag` 清零(`track_asso.cpp` 先置 0 再输出),因此表中的 `Track_Update_Flag` 恒为 0,判断「本圈是否更新」应改用该表是否出现对应 `track_id`/`circle_id` 的记录;② `Trust_Track` 的极坐标量快照 `Range`/`Azimuth`/`Elevation`/`Velocity` 只在航迹当圈有输出时回填,长期未输出(外推超限)的航迹在表中该项为 0 或旧值。
### 11.4 航迹起始相关的未修复观察(详见 docs/track_init_readme.md 第 15 节)
以下为阅读源码得到的观察,**未在本轮文档更新中改动代码**;修改前请结合实测数据评估:
- 点-临时航迹关联硬编码 `|Δr| ≥ 5 m` 门限,切向/远距离慢速目标可能漏关联导致起批失败。
- 起批判据对马氏距离平方再次平方(`d*d ≤ TRACK_START_THRESHOLD²`),实际门限为 `d ≤ 8`,按字面 `d² ≤ 64` 理解会严重放宽;调 `TRACK_START_THRESHOLD` 时需注意这一层。
- 折角计算 `acos` 无定义域钳位,浮点越界产生 NaN 时条件恒假(静默丢弃该关联)。
- 起批输出数组行号可能越过容量:容量判据用 `trust_track->size() < MAX_TRACK_INDEX`,而输出行计数在 track_asso 阶段(track_die 删除航迹之前)已推进,极端场景(当圈消亡 ≥ 2 条且起批占满配额)可写到 `MAX_TRACK_NUM` 之外。
- 起批输出有多个字段未赋值(见 9.3(4) 与 docs/track_init_readme.md 第 14 节),宿主不应依赖这些字段。
---
## 12. 版本历史
当前代码版本:**V1.5.6**(`parameters.h` 的 `PATCH_VERSION`;对应最近一次提交 b227ae1,2026-10-08)。版本号自本版起可运行时读取(`version_info`,见 7.1)。
近期提交(git log --oneline,新→旧):
| 提交 | 内容 |
|---|---|
| b227ae1(**V1.5.6** 版本号来源) | 数据处理增加版本号输出:`parameters.h` 新增版本宏(`MAJOR_VERSION`/`MINOR_VERSION`/`PATCH_VERSION` = 1.5.6、体制后缀 `VERSION_INFO`、拼装宏 `VERSION_STRING`),基类新增公开成员 `const char* version_info` 并由 `GetB()` 赋值;Debug 下记录可靠航迹与关联信息:`struct.h` 新增全局 `Asso_Info`(含 `min_d_flag` 与门限量)、`Trust_Track` 新增 `track_id`/`point_id`/`circle_id` 与极坐标量快照 `Range`/`Azimuth`/`Elevation`/`Velocity`,新增 `RdpRecTrustTrack`/`RdpRecAssoInfo` 两张表并在 `track_asso.cpp` 输出循环与关联三处埋点,移除 `data_process.cpp` 中按输出数组批量记录的旧示例(`RdpRecTrackOut` 实体注释停用);`track_asso.cpp` 的 ΔT 分支改写为 `if (delta_T > 0)`(行为等价) |
| c3c7e66 | 增加临时航迹录入数据库(新增 `RdpRecTempTrack` 表与 `track_init.cpp` 记录点)、输入点迹增加圈 ID(`PointRecv::circle_id`、`Data_Process::circle_id` 在满圈/TAS 帧末自增);`Temp_track` 字段更名(`snr`→`SNR`、`Amp`→`amplitude`、`T`→`CPI_time`、`Temp_track_section_idx`→`temp_track_section_idx`)并新增 `temp_track_id`/`point_id`/`circle_id`/`pitch_num`;临时航迹起批时把 ID 透传给 `Trust_Track` |
| a4f32e4 | 新增数据记录功能:把过程数据录入数据库(凝聚后点迹 `RdpRecPointCohesionTWS/TAS`,输入点迹改为 `RdpRecPoint`)、`data_process.h` 新增 `point_id`/`frame_id`/`circle_id` 计数、`dot_coh`/`dot_coh_tas` 埋记录点;<br>(该提交同时把上一轮的 CMake/工具链与记录框架改动一并提交,内容见下方「工作区(未提交…)」两条) |
| 工作区(已随本次发布归档,数据记录调试功能) | 新增可选编译的数据记录调试功能:CMake 缓存变量 `RDP_DATA_RECORD`(AUTO/ON/OFF,AUTO = Debug 启用 / Release 关闭)与独立静态库 `rdp_record`(**以 C++20 编译**,雷达代码保持 C++17);内置第三方源码 `third_party/ormpp`(ormpp 0.2.0 + iguana 1.0.5 + frozen + SQLite 3.49.1 amalgamation,附 UPSTREAM.md);新增 `rdp_record.h`(`Init`/`Shutdown`/`db_path` + `RDP_RECORD`/`RDP_RECORD_BATCH` 宏,关闭时展开为空操作)、`rdp_record_entity.h`(记录实体定义区)与 `rdp_record.cpp`(实体注册区、按实体自动建表、单条/批量写入、失败降级);`Data_Process_Factory::GetB()/Destroy()` 调用 `rdp_record::Init()/Shutdown()`;`parameters.h` 新增 `RDP_RECORD_DB_DIR`(`.\rdp_records\`);`configure.bat` 新增第 4 个参数(AUTO/ON/OFF);`.gitignore` 增加 `rdp_records/`;新增 `docs/data_record_debug.md`。验证:Release(PS/MS)导出表与依赖表与改动前逐行一致、DLL 体积不变(178,688 / 178,176 字节)、构建树无记录模块目标文件;Debug(PS/MS,AUTO→启用)0 warning 0 error;运行时校验(临时程序 + Python sqlite3)确认建库、一实体一表、批量事务写入、`id` 自增、取值一致、`integrity_check` = ok,且记录目录不可创建时接口照常返回、仅提示一次 |
| 工作区(已随本次发布归档) | 编译器由 MSVC 2013(Visual Studio 12,x86)升级为 **MSVC 2022(v143,x64)**、C++ 标准提升至 **C++17**:`CMakeLists.txt` 重写(`CMAKE_CXX_STANDARD 17`、`_MSC_VER >= 1930` 强校验、`/utf-8` `/Zc:__cplusplus` `/W3`)、`.vscode/scripts/` 全部构建脚本改为 MSVC 2022(新增共享的 `msvc_env.bat`,`configure.bat` 自动清理旧工具集构建树)、VS Code 配置同步(settings / c_cpp_properties / cmake-variants / tasks 改为 MSVC2022 x64 + C++17)、修复 MSVC 2022 新增的 17 处 C4267 窄化告警;X256_PS/X256_MS × Release/Debug 四种配置均 0 warning 0 error 通过,产物为 x64 DLL(导出符号与升级前一致,仅名称修饰随架构变化) |
| e8c56f9 | 输入/输出结构适配「归一化幅度」与「速度维 FFT 点数」:`DataRev` 新增 `amp_asv`/`prn_fftn`,`Track` 新增 `amp_asv_point`/`prn_fftn_point`,内部 `PointRecv`/`Trust_Track` 同步新增字段;`track_asso.cpp` 关联实点时写入航迹、TWS 输出时写回输出结构(TAS 侧未打通,见 11.2) |
| 42b598e | 调整「向站/背站」判断条件:判据由 `acos((-x·vx − y·vy)/r/Range_V) < π/4` 改为 `acos((-x·cos(Direction_Angle) − y·sin(Direction_Angle))/r) < π/2`(去掉按合速度归一化,锥角门限由 45° 放宽到 90°),TWS/TAS 同步 |
| fc49707 | 「向站/背站」判断改用滤波速度矢量:`Flag_Direction` 不再取 `vr_point > 0`(实测径向速度受多普勒模糊影响、符号可能取反),改按航迹 IMM 速度方向与位置矢量的夹角判定(TWS/TAS 同步);`primary_target_add/cancel`、`tracking_start/stop` 命中目标后 `break`;删除 TWS 输出循环中的注释残留代码块 |
| 50e0b5c | 新增手动重点目标接口(`primary_target_add`/`primary_target_cancel` + `Trust_Track::primary_target_flag`)与重点航迹消亡圈数 `TRACK_DIE_ROUND_PRIMARY`(10);输出航迹改为只输出 `Track_Update_Flag==1` 的本圈更新航迹;未关联航迹外推限制在 `MAX_EXTRAPOLATE_NUM`(3)圈内,TAS 侧新增 `MAX_EXTRAPOLATE_NUM_TAS`(相扫 5 / 机扫 48);`TRACK_DIE_ROUND` 由 4 调整为 6、`TRACK_DIE_ROUND_TAS` 相扫由 5 调整为 10,消亡判据改为 `>`;同步更新 README/CHANGELOG/docs |
| e0bf227 | 关联判定重构并加强:TWS `track_asso.cpp` 新增按航迹距离分段的高度门限(原高度门限代码块为注释状态)、统计距离/高度/距离三类判据拆为 `thd_d`/`thd_h`/`thd_r` 布尔量;`parameters.h` 的 `ASSO_THORD` 由 3 调大至 4(等效门限 d < 4,提高机动目标关联概率);TAS `track_asso_tas.cpp` 高度门限 2000~3000 m 段由 200 m 放宽至 250 m;修复 `tas_beam_output()` 的数据率门控条件(去掉与「取最早 CPI 目标」冲突的 T_track 判据);删除 `docs/data_process_flow_report.html`;`.gitignore` 增加 `design_docs/`;同步更新 README/CHANGELOG/docs |
| f249fdc | 输出航迹新增「靠近/远离」方向标志:`track_asso.cpp`(TWS)与 `track_asso_tas.cpp`(TAS)在写入 `vr_point` 后补 `Flag_Direction = vr_point > 0 ? 1 : 0`;新增 `docs/data_process_flow_report.md`、`docs/data_process_flow_report.html`、`docs/track_init_readme.md` 三份流程与专项文档(其中 HTML 版已于 e0bf227 删除) |
| 7d5a56f | 满圈判据由「波位号变小」改为「方位波位号 == 0」(避免机扫转台反转误判,`data_process.cpp`);点-航迹头关联改为统一速度区间 `V_MIN·ΔT ≤ dis ≤ V_MAX·ΔT` 并新增 `dis ≥ |vr_point|·ΔT`、`dis ≥ |v_track_head|·ΔT` 两条多普勒-几何一致性约束,限制切向高速短航迹(`track_init.cpp`);同步更新 README/CHANGELOG |
| 604f158 | TWS 关联中 ΔT≤0(点迹时间戳不晚于航迹时间)的候选不再剔除:仍参与最近邻选取但不更新 IMM/EKF、不刷新航迹时间与新鲜度,并标记该点迹已使用(`Use_Flag=1`)消费掉,避免其继续参与航迹起始生成虚假 TWS 航迹;`track_asso.cpp` 的 ΔT≤0 告警日志注释停用;过期文档(BUG_REPORT.md、BUG_FIX_REPORT.md、requirements.md)移入 archived_docs/ 留档 |
| d6cdbc0 | 删除 `data_process.cpp`、`dot_coh.cpp` 中已注释停用的调试输出与早期 1 km SNR 过滤代码残留(不改变运行逻辑);同步更新 README/CHANGELOG |
| 1521592 | 新增日志模块(`rdp_log.h/.cpp`,CMake SOURCES 注册、.gitignore 忽略 `rdp_logs/`):`RDP_LOG_ENABLE` / `RDP_LOG_DIR` 宏(parameters.h)、std::cout 经 `rdbuf()` 重定向到 `.\rdp_logs\` 下日期+时间命名日志、`RDP_LOG` 前缀宏、ΔT≤0 告警日志;`RadarPara.DATA_RATE_TAS` 单位由 Hz 改为 s;修复 `tas_beam_output()` 将向量下标误赋给 `Tracking_beam->TAS_track_index` 的问题;更新 README/CHANGELOG |
| 5f82098 | 修复 MSVC `/W3` 下全部 C4819 / C4018 / C4244 警告(UTF-8 with BOM、size_t / static_cast 等);更新项目文档 |
| f438ac5 | 优化 TAS 调度:波束输出改为在队列内选择满足数据率门控且 CPI 时间最早的目标,停用整队循环移位;DATA_RATE_TAS 编译期宏停用、统一使用运行时参数;#pragma message 提示扫描体制 |
| e729c5b | 频点宏精简为 FREQ0 = 9.2 GHz(原 FREQ0~FREQ20 均为 16.8 GHz) |
| b3e2f52 | TAS 波束控制改为按固定数据率调度,RadarPara 新增 DATA_RATE_TAS 运行时参数;新增 CHANGELOG.md、requirements.md,README 补充相应说明 |
| 93d3924 | 新增 README.md(本文档);移除 requirements.md;调整 .gitignore |
| 3500652 | 去掉 1 km 以内航迹起批的 20 dB 信噪比门限 |
| 01d28e0 | 改用 VS Code + CMake 重新编译(编译器不变);修复若干逻辑 BUG(见 BUG_FIX_REPORT.md) |
| 01d28e0 | 改用 VS Code + CMake 重新编译(编译器不变);修复若干逻辑 BUG(见 archived_docs/BUG_FIX_REPORT.md) |
| 0cb31b4 | 删除不必要文件;输入点迹/输出航迹的 CPI 与 GNSS 时间改为 64 位整型;修复 TAS 外推时间差可能无效的问题 |
| d101fe9 | 增加航迹清空功能(解决重连乱窜);TAS 外推时间间隔按波束时间计算;增加 1 km 内 SNR<20 dB 不起批限制(后于 3500652 移除) |
| 8636179 | 机扫/相扫程序合并,宏定义配置切换 |
@@ -841,8 +1032,14 @@ $$
| 文档 | 内容 |
|---|---|
| BUG_REPORT.md | 全量逻辑 BUG 分析(P0~P3 共 37 项,含位置、说明、建议、处置结论) |
| BUG_FIX_REPORT.md | 本次修复明细、修复状态汇总表与 MSVC 2013 x86 编译验证记录 |
| CHANGELOG.md | 变更日志(Keep a Changelog 格式,按 git 提交记录维护) |
| docs/data_process_flow_report.md | 数据处理流程与关键逻辑说明(按当前源码整理:前处理与判圈、凝聚、TWS/TAS 关联与滤波、航迹起始、外推消亡、TAS 波束控制、参数脉络;含与本文档的历史差异提示)——原同内容 HTML 渲染版(`data_process_flow_report.html`)已于 e0bf227 删除,HTML 版本在 git 忽略的 `design_docs/` 内留档 |
| docs/track_init_readme.md | 航迹起始(Track Init)专项说明:分阶段流程、起始条件汇总、调参直觉、输出字段映射(含未赋值字段)、已知风险与代码索引 |
| docs/data_record_debug.md | 数据记录调试功能说明:开关与构建(`RDP_DATA_RECORD`、C++20 隔离与 C 编译器要求)、运行期行为(目录/文件名、PRAGMA、失败降级、线程模型)、表结构约定、**新增记录点四步配方**、当前 6 张在用记录表与字段、查询示例与已知限制 |
| third_party/ormpp/UPSTREAM.md | 内置第三方源码的来源说明:ormpp / iguana / frozen / SQLite 版本、许可证、复现与升级步骤(含 blob 校验值) |
| archived_docs/BUG_REPORT.md | 全量逻辑 BUG 分析(P0~P3 共 37 项,含位置、说明、建议、处置结论)——过期留档,仅作历史参考 |
| archived_docs/BUG_FIX_REPORT.md | 修复明细、修复状态汇总表与 MSVC 2013 x86 编译验证记录——过期留档(对应 01d28e0 时期) |
| requirements.md | 修改需求说明:数据记录调试功能(结构体经 ormpp 映射写入 SQLite、Release 版可屏蔽、数据分类入不同表、宏记录点、便于快速新增结构体、不使用异步写入)——框架已按该说明实现,记录表已从 2 个示例扩展为 6 张在用表(7 个实体定义),详见 docs/data_record_debug.md(此前同名的日志文件输出需求亦已实现) |
| backup/data_process_class_dll/data_process_class_dll.pro | 迁移前的 Qt/qmake 工程文件(源文件清单参考) |
| .vscode/tasks.json、.vscode/cmake-variants.yaml | VS Code 构建任务与变体定义 |
| build/X256_PS/bin/RadarConfigParam.ini | 回放测试工具(rdp_playback.exe)配置样例 |
+1 -1
View File
@@ -1,4 +1,4 @@
#include "coor_trans.h"
#include "coor_trans.h"
#include <cmath>
#include <cstring>
#include <vector>
+1 -1
View File
@@ -1,4 +1,4 @@
#ifndef COOR_TRANS_H
#ifndef COOR_TRANS_H
#define COOR_TRANS_H
#include "parameters.h"
+96 -42
View File
@@ -1,4 +1,7 @@
#include "data_process.h"
#include "data_process.h"
#include "rdp_log.h"
#include "rdp_record.h"
#include "rdp_record_entity.h"
#include <cstring>
#include <vector>
#include <cmath>
@@ -16,6 +19,7 @@ int Data_Process::data_preprocess(struct DataRev Data_Input[150])
{
// 更新当前系统时间
latest_timestamp = Data_Input[0].CPI_time;
frame_id++;
//数据存入缓存区域 Data_buffer
if(Data_Input[0].point_type == 0) //tws数据
@@ -25,30 +29,46 @@ int Data_Process::data_preprocess(struct DataRev Data_Input[150])
for (int i=0; i<data_num;i++)
{
PointRecv Data_buffer_temp;
Data_buffer_temp.Amplitude=Data_Input[i].Amplitude;
Data_buffer_temp.Azimuth=Data_Input[i].Azimuth/180*PI;
Data_buffer_temp.snr = Data_Input[i].Snr;
Data_buffer_temp.point_id = point_id++;
Data_buffer_temp.frame_id = frame_id;
Data_buffer_temp.circle_id = circle_id;
Data_buffer_temp.amplitude=Data_Input[i].Amplitude;
Data_buffer_temp.azimuth=Data_Input[i].Azimuth/180*PI;
Data_buffer_temp.SNR = Data_Input[i].Snr;
Data_buffer_temp.RCS = Data_Input[i].RCS;
Data_buffer_temp.CPI_Time = Data_Input[i].CPI_time;
Data_buffer_temp.CPI_time = Data_Input[i].CPI_time;
Data_buffer_temp.GNSS_time = Data_Input[i].GNSS_time;
Data_buffer_temp.Freq_index = Data_Input[i].Freq_index;
Data_buffer_temp.freq_index = Data_Input[i].Freq_index;
Data_buffer_temp.PRF_index = Data_Input[i].PRI;
Data_buffer_temp.Range = Data_Input[i].Range;
Data_buffer_temp.Height= Data_Input[i].Range*sin(Data_Input[i].Elevation/180*PI);
Data_buffer_temp.Use_Flag = 0;
Data_buffer_temp.Velocity = Data_Input[i].Velocity;
Data_buffer_temp.pitch_num = Data_Input[i].pitch_num;
Data_buffer_temp.range = Data_Input[i].Range;
Data_buffer_temp.height= Data_Input[i].Range*sin(Data_Input[i].Elevation/180*PI);
Data_buffer_temp.use_flag = 0;
Data_buffer_temp.velocity = Data_Input[i].Velocity;
Data_buffer_temp.encoder_value = Data_Input[i].Encoder_value;
Data_buffer_temp.pitch_beam_index = Data_Input[i].pitch_num;
Data_buffer_temp.azimuth_beam_index = Data_Input[i].Beam_index_aiz;
Data_buffer_temp.point_type = Data_Input[i].point_type;
Data_buffer_temp.amp_asv_point = Data_Input[i].amp_asv;
Data_buffer_temp.prn_fftn_point = Data_Input[i].prn_fftn;
std::memcpy(Data_buffer_temp.speed_dim, Data_Input[i].speed_dim, sizeof(Data_buffer_temp.speed_dim));
std::memcpy(Data_buffer_temp.range_dim, Data_Input[i].range_dim, sizeof(Data_buffer_temp.range_dim));
//数据记录示例①:把本次接收的输入点迹写入表 RdpRecPointTws(字段为示例,可按需增删)。
//记录功能关闭时该语句展开为空操作(实参不求值),交付构建不产生任何代码,
//用法与新增记录点配方见 rdp_record.h 与 docs/data_record_debug.md。
RDP_RECORD(RdpRecPoint(Data_buffer_temp));
Data_buffer.push_back(Data_buffer_temp);
}
if(Data_Input[0].Beam_index_aiz < last_beam_num) //TWS
// 修改一周扫描完成的判断条件,原来采用判断波位号变小为一周扫描完成,但机扫模式下,可能存在转台反转的情况,导致波位号变小,但并未扫描一周,因此改为判断波位号为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
@@ -65,26 +85,36 @@ int Data_Process::data_preprocess(struct DataRev Data_Input[150])
data_num = min(max(Data_Input[0].Point_Sum, 0), 150);
TAS_track_idx = Data_Input[0].TAS_track_index;
latest_timestamp = Data_Input[0].CPI_time;
for (int i=0; i<data_num;i++)
{
PointRecv Data_buffer_temp;
Data_buffer_temp.Amplitude=Data_Input[i].Amplitude;
Data_buffer_temp.Azimuth=Data_Input[i].Azimuth/180*PI;
Data_buffer_temp.snr = Data_Input[i].Snr;
Data_buffer_temp.point_id = point_id++;
Data_buffer_temp.frame_id = frame_id;
Data_buffer_temp.circle_id = circle_id;
Data_buffer_temp.amplitude=Data_Input[i].Amplitude;
Data_buffer_temp.azimuth=Data_Input[i].Azimuth/180*PI;
Data_buffer_temp.SNR = Data_Input[i].Snr;
Data_buffer_temp.RCS = Data_Input[i].RCS;
Data_buffer_temp.CPI_Time = Data_Input[i].CPI_time;
Data_buffer_temp.CPI_time = Data_Input[i].CPI_time;
Data_buffer_temp.GNSS_time = Data_Input[i].GNSS_time;
Data_buffer_temp.Freq_index = Data_Input[i].Freq_index;
Data_buffer_temp.freq_index = Data_Input[i].Freq_index;
Data_buffer_temp.PRF_index = Data_Input[i].PRI;
Data_buffer_temp.Range = Data_Input[i].Range;
Data_buffer_temp.Height= Data_Input[i].Range*sin(Data_Input[i].Elevation/180*PI);
Data_buffer_temp.Use_Flag = 0;
Data_buffer_temp.Velocity = Data_Input[i].Velocity;
Data_buffer_temp.pitch_num = Data_Input[i].pitch_num;
Data_buffer_temp.range = Data_Input[i].Range;
Data_buffer_temp.height= Data_Input[i].Range*sin(Data_Input[i].Elevation/180*PI);
Data_buffer_temp.use_flag = 0;
Data_buffer_temp.velocity = Data_Input[i].Velocity;
Data_buffer_temp.pitch_beam_index = Data_Input[i].pitch_num;
Data_buffer_temp.encoder_value = Data_Input[i].Encoder_value;
Data_buffer_temp.azimuth_beam_index = Data_Input[i].Beam_index_aiz;
Data_buffer_temp.point_type = Data_Input[i].point_type;
Data_buffer_temp.amp_asv_point = Data_Input[i].amp_asv;
Data_buffer_temp.prn_fftn_point = Data_Input[i].prn_fftn;
std::memcpy(Data_buffer_temp.speed_dim, Data_Input[i].speed_dim, sizeof(Data_buffer_temp.speed_dim));
std::memcpy(Data_buffer_temp.range_dim, Data_Input[i].range_dim, sizeof(Data_buffer_temp.range_dim));
RDP_RECORD(RdpRecPoint(Data_buffer_temp));
Data_buffer_tas.push_back(Data_buffer_temp);
// std::cout << "Azimuth:" << Data_Input[i].Azimuth
@@ -94,7 +124,7 @@ int Data_Process::data_preprocess(struct DataRev Data_Input[150])
// << "data_num:" << data_num;
}
circle_id++;
return 3;
}
@@ -118,7 +148,6 @@ int model)
{
dot_coh.dot_coh_process_buff(&Data_buffer,&point_recv,Work_Parameter);
//std::cout << "point_recv :" <<point_recv.size();
std::vector<PointRecv>().swap(Data_buffer);
}
@@ -131,6 +160,7 @@ int model)
&trust_track,
Trust_Track_Output,
Trust_track_num_Output,
latest_timestamp,
Work_Parameter
);
@@ -144,11 +174,6 @@ int model)
Trust_Track_Output,
Trust_track_num_Output,
Work_Parameter);
// std::cout << "point_recv: " <<point_recv.size();
// std::cout << "trust_track: " <<trust_track.size();
// std::cout << "temp_track: " <<temp_track.size();
// std::cout << "Trust_track_num_Output: " <<*Trust_track_num_Output;
// std::cout << "Track_die_num_Output: " <<*Track_die_num_Output;
}
@@ -160,12 +185,6 @@ int model)
dot_coh_tas.dot_coh_tas_process(&Data_buffer_tas,&point_recv_tas); //点迹凝聚
// if(point_recv_tas.size()>0)
// {
// std::cout << "point_recv_tas :" <<point_recv_tas[0].Azimuth/PI*180<<" "<<point_recv_tas[0].Range;
// }
//std::cout << "TAS process :" << TAS_track_idx << "p a r:"<<point_recv_tas[0].Point_Sum<<" " <<point_recv_tas[0].Azimuth/PI*180<<" "<<point_recv_tas[0].Range;
std::vector<PointRecv>().swap(Data_buffer_tas);//凝聚完毕 清除Data_buffer
track_asso_tas.track_asso_process_tas(&point_recv_tas, //点迹文件
@@ -247,13 +266,13 @@ int Data_Process:: track_delete(int delete_track_num, //手动
{
for (int i=0;i<delete_track_num ;i++)
{
for (int j=0;j<trust_track.size();j++)
for (size_t j=0;j<trust_track.size();j++)
{
if(trust_track[j].Track_Index == delete_track_index[i])
{
trust_track[j].manual_delete_flag=1;
std::cout << "Delete :" << trust_track[j].Track_Index << std::endl;
// RDP_LOG << "Delete :" << trust_track[j].Track_Index << std::endl;
}
}
@@ -262,15 +281,49 @@ int Data_Process:: track_delete(int delete_track_num, //手动
return 0;
}
//手动添加重点目标
int Data_Process::primary_target_add(int track_index)
{
for ( size_t i=0; i<trust_track.size();i++){
if(trust_track[i].Track_Index==track_index)
{
trust_track[i].primary_target_flag = 1;
//std::cout<< "Add primary target :" << trust_track[i].Track_Index << std::endl;
break;
}
}
return 0;
}
//手动取消重点目标
int Data_Process::primary_target_cancel(int track_index)
{
for ( size_t i=0; i<trust_track.size();i++){
if(trust_track[i].Track_Index==track_index)
{
trust_track[i].primary_target_flag = 0;
//std::cout<< "Cancel primary target :" << trust_track[i].Track_Index << std::endl;
break;
}
}
return 0;
}
//手动转TAS跟踪函数
int Data_Process:: tracking_start(int track_index) //需要手动转入TAS跟踪的航迹号
{
for ( int i=0; i<trust_track.size();i++)
for ( size_t i=0; i<trust_track.size();i++){
if(trust_track[i].Track_Index==track_index)
{
trust_track[i].manual_tracking_flag = 1;
break;
}
}
return 0;
}
@@ -279,11 +332,12 @@ int Data_Process:: tracking_start(int track_index) //需要手动
int Data_Process:: tracking_stop(int track_index) //需要手动转入TAS跟踪的航迹号
{
for ( int i=0; i<trust_track.size();i++){
for ( size_t i=0; i<trust_track.size();i++){
if(trust_track[i].Track_Index==track_index)
{
trust_track[i].manual_tracking_flag = 0;
trust_track[i].Track_Mode = 0;
break;
}
}
+13 -1
View File
@@ -1,4 +1,4 @@
#ifndef DATA_PROCESS_H
#ifndef DATA_PROCESS_H
#define DATA_PROCESS_H
#pragma once
#include "data_process_class_dll.h"
@@ -59,6 +59,12 @@ public:
//手动取消TAS跟踪函数
int tracking_stop(int track_index); //需要取消TAS跟踪的航迹号
//手动添加重点目标
int primary_target_add(int track_index); //将指定目标转为重点目标
//手动取消重点目标
int primary_target_cancel(int track_index); //将指定重点目标取消
//手动打跟踪波束
int tracking_point(float Azimuth); //方位角
@@ -92,6 +98,12 @@ private:
long long latest_timestamp = 0; //最新时间戳
long long point_id = 0; //点迹id
long long frame_id = 0; //帧id
long long circle_id = 0; //圈id
struct RadarPara Work_Parameter; //工作参数 参数设置函数外部输入
Dot_Coh dot_coh; //点迹凝聚
@@ -1,8 +1,11 @@
#ifndef DATA_PROCESS_CLASS_DLL_LIBRARY
#ifndef DATA_PROCESS_CLASS_DLL_LIBRARY
#define DATA_PROCESS_CLASS_DLL_LIBRARY
#endif
#include "data_process_class_dll.h"
#include "data_process.h"
#include "rdp_log.h"
#include "rdp_record.h"
#include "parameters.h"
/*
Data_process_class_dll::Data_process_class_dll()
@@ -16,7 +19,15 @@ Data_process_class_dll *Data_Process_Factory::GetB()
{
if(!p)
{
p=new Data_Process();
//初始化日志输出:RDP_LOG_ENABLE 开启时重定向 std::cout 到日志文件
//rdp_log::Init();
//初始化数据记录:RDP_DATA_RECORD 开启时创建记录目录、打开本次运行的记录库;
//关闭时 rdp_record::Init() 为空实现,不产生任何开销
rdp_record::Init();
p=new Data_Process();
p->version_info = VERSION_STRING;
}
return p;
@@ -30,5 +41,11 @@ void Data_Process_Factory::Destroy()
if (p) {
delete p;
p = nullptr;
//恢复 std::cout 到控制台并关闭日志文件
//rdp_log::Shutdown();
//关闭记录库(RDP_DATA_RECORD 关闭时为空实现)
rdp_record::Shutdown();
}
}
@@ -1,8 +1,9 @@
#pragma once
#pragma once
#ifndef DATA_PROCESS_CLASS_DLL_H
#define DATA_PROCESS_CLASS_DLL_H
#include "data_process_class_dll_global.h"
#include <string>
/***************************************************** 结构体 ********************************************************/
// 输入点迹的数据结构 用户可见
@@ -28,8 +29,10 @@ struct DATA_PROCESS_CLASS_DLLSHARED_EXPORT DataRev
int Point_Num; //点迹号(1,2,3,4...)
int UseFlag; //点迹使用标记 使用1/未使用0 主程序存入数据时置为0
int TAS_track_index; //TAS的航迹号
int point_type; //点迹类型 TAS 1/TWS 0
int point_type; //点迹类型 TAS 1/TWS 0
int pitch_num; // 俯仰波位号
float amp_asv; //目标归一化幅度(dB)
float prn_fftn; //速度维FFT点数
float speed_dim[129]; // 目标的十字星数据,目标点速度维129个点
float range_dim[8]; // 目标的十字星数据,目标点距离维8个点
};
@@ -69,6 +72,8 @@ struct DATA_PROCESS_CLASS_DLLSHARED_EXPORT Track
float elev_point;
float vr_point;
float prf_point;
float amp_asv_point; //关联上的点迹信息(归一化幅度)
float prn_fftn_point; //点迹速度维FFT点数
float track_time; //航迹时间 (单位 s)
float track_snr; //信噪比
@@ -147,7 +152,7 @@ struct DATA_PROCESS_CLASS_DLLSHARED_EXPORT RadarPara
float DATA_RATE_MIDDLE;
float DATA_RATE_FAR;
float DATA_RATE_TAS; //TAS模式数据率,由dll调用者输入;TAS波束输出间隔阈值为 1/DATA_RATE_TAS 秒
float DATA_RATE_TAS; //TAS模式数据率,由dll调用者输入;TAS波束输出间隔阈值为 DATA_RATE_TAS 秒
//数据关联参数
int track_start_point_num; //起批点数(典型值 3或4)
@@ -172,6 +177,7 @@ class DATA_PROCESS_CLASS_DLLSHARED_EXPORT Data_process_class_dll
public:
// Data_process_class_dll();
const char* version_info;
//数据预处理函数
virtual int data_preprocess(struct DataRev Data_Input[150]) //雷达的输入点迹 主程序创建全局变量 输入数据
@@ -236,6 +242,18 @@ public:
return 0;
}
//手动添加重点目标
virtual int primary_target_add(int track_index) //将指定目标转为重点目标
{
return 0;
}
//手动取消重点目标
virtual int primary_target_cancel(int track_index) //将指定重点目标取消
{
return 0;
}
//手动打跟踪波束
virtual int tracking_point(float Azimuth) //方位角(当前接口未启用,保留空实现)
{
@@ -1,4 +1,4 @@
#ifndef DATA_PROCESS_CLASS_DLL_GLOBAL_H
#ifndef DATA_PROCESS_CLASS_DLL_GLOBAL_H
#define DATA_PROCESS_CLASS_DLL_GLOBAL_H
#if defined(DATA_PROCESS_CLASS_DLL_LIBRARY)
+58 -62
View File
@@ -1,7 +1,10 @@
#include "dot_coh.h"
#include "dot_coh.h"
#include <cmath>
#include <cstring>
#include <vector>
#include "rdp_record.h"
#include "rdp_record_entity.h"
using namespace std;
int Dot_Coh::dot_coh_process(std::vector <PointRecv> *data_input,
@@ -14,20 +17,20 @@ int Dot_Coh::dot_coh_process(std::vector <PointRecv> *data_input,
for (unsigned int loop_of_point=0; loop_of_point<data_input->size()-1;loop_of_point++ )
{
if( (*data_input)[loop_of_point].Use_Flag!=1)
if( (*data_input)[loop_of_point].use_flag!=1)
{
float point_0_R=(*data_input)[loop_of_point].Range;
float point_0_V=(*data_input)[loop_of_point].Velocity;
float point_0_F=(*data_input)[loop_of_point].Azimuth;
float point_0_A=(*data_input)[loop_of_point].Amplitude;
float point_0_R=static_cast<float>((*data_input)[loop_of_point].range);
float point_0_V=static_cast<float>((*data_input)[loop_of_point].velocity);
float point_0_F=static_cast<float>((*data_input)[loop_of_point].azimuth);
float point_0_A=static_cast<float>((*data_input)[loop_of_point].amplitude);
for (unsigned int i=loop_of_point+1;i<data_input->size();i++)
{
if((*data_input)[loop_of_point].Use_Flag!=1 && (*data_input)[i].Use_Flag!=1)
if((*data_input)[loop_of_point].use_flag!=1 && (*data_input)[i].use_flag!=1)
{
float point_1_R=(*data_input)[i].Range;
float point_1_V=(*data_input)[i].Velocity;
float point_1_F=(*data_input)[i].Azimuth;
float point_1_A=(*data_input)[i].Amplitude;
float point_1_R=static_cast<float>((*data_input)[i].range);
float point_1_V=static_cast<float>((*data_input)[i].velocity);
float point_1_F=static_cast<float>((*data_input)[i].azimuth);
float point_1_A=static_cast<float>((*data_input)[i].amplitude);
//凝聚条件: 距离、方位接近
if(Work_Parameter.work_mode == 0)
@@ -36,16 +39,16 @@ int Dot_Coh::dot_coh_process(std::vector <PointRecv> *data_input,
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)//
&& point_0_A <point_1_A )//
{
point_0_R=(*data_input)[i].Range;
point_0_V=(*data_input)[i].Velocity;
point_0_A=(*data_input)[i].Amplitude;
point_0_F=(*data_input)[i].Azimuth;
(*data_input)[loop_of_point].Use_Flag=1;
point_0_R=static_cast<float>((*data_input)[i].range);
point_0_V=static_cast<float>((*data_input)[i].velocity);
point_0_A=static_cast<float>((*data_input)[i].amplitude);
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)//
&& point_0_A >= point_1_A)//
{
(*data_input)[i].Use_Flag=1;
(*data_input)[i].use_flag=1;
}
}
else
@@ -54,16 +57,16 @@ int Dot_Coh::dot_coh_process(std::vector <PointRecv> *data_input,
if( (fabs(point_0_R-point_1_R)<=DOT_COH_RANGE && delta_F <=DOT_COH_AZI/180.0*PI )//
&& point_0_A <point_1_A )//
{
point_0_R=(*data_input)[i].Range;
point_0_V=(*data_input)[i].Velocity;
point_0_A=(*data_input)[i].Amplitude;
point_0_F=(*data_input)[i].Azimuth;
(*data_input)[loop_of_point].Use_Flag=1;
point_0_R=static_cast<float>((*data_input)[i].range);
point_0_V=static_cast<float>((*data_input)[i].velocity);
point_0_A=static_cast<float>((*data_input)[i].amplitude);
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 )//
&& point_0_A >= point_1_A)//
{
(*data_input)[i].Use_Flag=1;
(*data_input)[i].use_flag=1;
}
}
@@ -77,7 +80,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 <R_MIN || (*Iter).range > R_MAX )
{
data_input->erase(Iter);
Iter=data_input->begin();
@@ -88,7 +91,7 @@ int Dot_Coh::dot_coh_process(std::vector <PointRecv> *data_input,
}
}
for (int i=0;i<data_input->size();i++) //data_buffer_2 ---> point_recv
for (size_t i=0;i<data_input->size();i++) //data_buffer_2 ---> point_recv
{
(*point_recv).push_back((*data_input)[i]);
}
@@ -106,26 +109,18 @@ int Dot_Coh::dot_coh_process_buff( std::vector <PointRecv> *data_input,
//1.1 data_input、data_input_buff中的数据放在一起
std::vector <PointRecv> data_tmp;
for (int i=0;i<data_input_buff.size();i++)
for (size_t i=0;i<data_input_buff.size();i++)
{
data_tmp.push_back(data_input_buff[i]);
data_tmp[data_tmp.size()-1].point_section_asso = 1;
}
for (int i=0;i<data_input->size();i++)
for (size_t i=0;i<data_input->size();i++)
{
data_tmp.push_back((*data_input)[i]);
data_tmp[data_tmp.size()-1].point_section_asso = 2;
}
// //去除点迹中,1km以内,SNR小于20dB的点
// for (int i=0;i<data_input->size();i++)
// {
// if((*data_input)[i].Range > 1000 || (*data_input)[i].snr >= 20){
// data_tmp.push_back((*data_input)[i]);
// data_tmp[data_tmp.size()-1].point_section_asso = 2;
// }
// }
//1.2 把data_input、data_input_buff清空
@@ -138,20 +133,20 @@ int Dot_Coh::dot_coh_process_buff( std::vector <PointRecv> *data_input,
for (unsigned int loop_of_point=0; loop_of_point<data_tmp.size()-1;loop_of_point++ )
{
if( data_tmp[loop_of_point].Use_Flag!=1)
if( data_tmp[loop_of_point].use_flag!=1)
{
float point_0_R=data_tmp[loop_of_point].Range;
float point_0_V=data_tmp[loop_of_point].Velocity;
float point_0_F=data_tmp[loop_of_point].Azimuth;
float point_0_A=data_tmp[loop_of_point].Amplitude;
float point_0_R=static_cast<float>(data_tmp[loop_of_point].range);
float point_0_V=static_cast<float>(data_tmp[loop_of_point].velocity);
float point_0_F=static_cast<float>(data_tmp[loop_of_point].azimuth);
float point_0_A=static_cast<float>(data_tmp[loop_of_point].amplitude);
for (unsigned int i=loop_of_point+1;i<data_tmp.size();i++)
{
if(data_tmp[loop_of_point].Use_Flag!=1 && data_tmp[i].Use_Flag!=1)
if(data_tmp[loop_of_point].use_flag!=1 && data_tmp[i].use_flag!=1)
{
float point_1_R=data_tmp[i].Range;
float point_1_V=data_tmp[i].Velocity;
float point_1_F=data_tmp[i].Azimuth;
float point_1_A=data_tmp[i].Amplitude;
float point_1_R=static_cast<float>(data_tmp[i].range);
float point_1_V=static_cast<float>(data_tmp[i].velocity);
float point_1_F=static_cast<float>(data_tmp[i].azimuth);
float point_1_A=static_cast<float>(data_tmp[i].amplitude);
//凝聚条件: 距离、方位接近
if(Work_Parameter.work_mode == 0)
@@ -160,16 +155,16 @@ int Dot_Coh::dot_coh_process_buff( std::vector <PointRecv> *data_input,
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)//
&& point_0_A <point_1_A )//
{
point_0_R=data_tmp[i].Range;
point_0_V=data_tmp[i].Velocity;
point_0_A=data_tmp[i].Amplitude;
point_0_F=data_tmp[i].Azimuth;
data_tmp[loop_of_point].Use_Flag=1;
point_0_R=static_cast<float>(data_tmp[i].range);
point_0_V=static_cast<float>(data_tmp[i].velocity);
point_0_A=static_cast<float>(data_tmp[i].amplitude);
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)//
&& point_0_A >= point_1_A)//
{
data_tmp[i].Use_Flag=1;
data_tmp[i].use_flag=1;
}
}
else
@@ -178,16 +173,16 @@ int Dot_Coh::dot_coh_process_buff( std::vector <PointRecv> *data_input,
if( (fabs(point_0_R-point_1_R)<=DOT_COH_RANGE && delta_F <=DOT_COH_AZI/180.0*PI )//
&& point_0_A <point_1_A )//
{
point_0_R=data_tmp[i].Range;
point_0_V=data_tmp[i].Velocity;
point_0_A=data_tmp[i].Amplitude;
point_0_F=data_tmp[i].Azimuth;
data_tmp[loop_of_point].Use_Flag=1;
point_0_R=static_cast<float>(data_tmp[i].range);
point_0_V=static_cast<float>(data_tmp[i].velocity);
point_0_A=static_cast<float>(data_tmp[i].amplitude);
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 )//
&& point_0_A >= point_1_A)//
{
data_tmp[i].Use_Flag=1;
data_tmp[i].use_flag=1;
}
}
@@ -201,7 +196,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 <R_MIN || (*Iter).range > R_MAX )
{
data_tmp.erase(Iter);
Iter=data_tmp.begin();
@@ -213,7 +208,7 @@ int Dot_Coh::dot_coh_process_buff( std::vector <PointRecv> *data_input,
}
//1.5 将data_tmp中凝聚后的点再分到 data_input_buff和data_input中
for (int i=0;i<data_tmp.size();i++)
for (size_t i=0;i<data_tmp.size();i++)
{
if(data_tmp[i].point_section_asso==1)
{
@@ -229,15 +224,16 @@ int Dot_Coh::dot_coh_process_buff( std::vector <PointRecv> *data_input,
//2.data_input_buff数据输出给point_recv
for (int i=0;i<data_input_buff.size();i++)
for (size_t i=0;i<data_input_buff.size();i++)
{
(*point_recv).push_back(data_input_buff[i]);
(*point_recv).push_back(data_input_buff[i]);
RDP_RECORD(RdpRecPointCohesionTWS(data_input_buff[i]));
}
std::vector<PointRecv>().swap(data_input_buff);
//3.data_input数据输出给data_input_buff
for (int i=0;i<data_input->size();i++)
for (size_t i=0;i<data_input->size();i++)
{
data_input_buff.push_back((*data_input)[i]);
}
+1 -1
View File
@@ -1,4 +1,4 @@
#ifndef DOT_COH_H
#ifndef DOT_COH_H
#define DOT_COH_H
#include "data_process_class_dll.h"
#include "parameters.h"
+24 -21
View File
@@ -1,7 +1,9 @@
#include "dot_coh_tas.h"
#include "dot_coh_tas.h"
#include <cmath>
#include <cstring>
#include <vector>
#include "rdp_record.h"
#include "rdp_record_entity.h"
using namespace std;
int Dot_Coh_TAS::dot_coh_tas_process(std::vector <PointRecv> *data_input, //输入点迹
@@ -12,38 +14,38 @@ int Dot_Coh_TAS::dot_coh_tas_process(std::vector <PointRecv> *dat
return 1;
}
for (int loop_of_point=0; loop_of_point<data_input->size()-1;loop_of_point++ )
for (size_t loop_of_point=0; loop_of_point<data_input->size()-1;loop_of_point++ )
{
if( (*data_input)[loop_of_point].Use_Flag!=1)
if( (*data_input)[loop_of_point].use_flag!=1)
{
float point_0_R=(*data_input)[loop_of_point].Range;
float point_0_V=(*data_input)[loop_of_point].Velocity;
float point_0_F=(*data_input)[loop_of_point].Azimuth;
float point_0_A=(*data_input)[loop_of_point].Amplitude;
for ( int i=loop_of_point+1;i<(*data_input).size();i++)
float point_0_R=static_cast<float>((*data_input)[loop_of_point].range);
float point_0_V=static_cast<float>((*data_input)[loop_of_point].velocity);
float point_0_F=static_cast<float>((*data_input)[loop_of_point].azimuth);
float point_0_A=static_cast<float>((*data_input)[loop_of_point].amplitude);
for ( size_t i=loop_of_point+1;i<(*data_input).size();i++)
{
if((*data_input)[loop_of_point].Use_Flag!=1 && (*data_input)[i].Use_Flag!=1)
if((*data_input)[loop_of_point].use_flag!=1 && (*data_input)[i].use_flag!=1)
{
float point_1_R=(*data_input)[i].Range;
float point_1_V=(*data_input)[i].Velocity;
float point_1_F=(*data_input)[i].Azimuth;
float point_1_A=(*data_input)[i].Amplitude;
float point_1_R=static_cast<float>((*data_input)[i].range);
float point_1_V=static_cast<float>((*data_input)[i].velocity);
float point_1_F=static_cast<float>((*data_input)[i].azimuth);
float point_1_A=static_cast<float>((*data_input)[i].amplitude);
//凝聚条件: 距离、方位接近
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)
&& point_0_A <point_1_A )
{
point_0_R=(*data_input)[i].Range;
point_0_V=(*data_input)[i].Velocity;
point_0_A=(*data_input)[i].Amplitude;
point_0_F=(*data_input)[i].Azimuth;
(*data_input)[loop_of_point].Use_Flag=1;
point_0_R=static_cast<float>((*data_input)[i].range);
point_0_V=static_cast<float>((*data_input)[i].velocity);
point_0_A=static_cast<float>((*data_input)[i].amplitude);
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)
&& point_0_A >= point_1_A)
{
(*data_input)[i].Use_Flag=1;
(*data_input)[i].use_flag=1;
}
}
}
@@ -54,7 +56,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 <R_MIN || (*Iter).range > R_MAX )
{
data_input->erase(Iter);
Iter=data_input->begin();
@@ -65,9 +67,10 @@ int Dot_Coh_TAS::dot_coh_tas_process(std::vector <PointRecv> *dat
}
}
for (int i=0;i<data_input->size();i++)
for (size_t i=0;i<data_input->size();i++)
{
(*point_recv_tas).push_back((*data_input)[i]);
RDP_RECORD(RdpRecPointCohesionTAS((*data_input)[i]));
}
return 1;
+1 -1
View File
@@ -1,4 +1,4 @@
#ifndef DOT_COH_TAS_H
#ifndef DOT_COH_TAS_H
#define DOT_COH_TAS_H
#include "parameters.h"
#include "struct.h"
+1 -1
View File
@@ -1,4 +1,4 @@
#include "kalman.h"
#include "kalman.h"
#include "coor_trans.h"
#include "parameters.h"
#include <cmath>
+1 -1
View File
@@ -1,4 +1,4 @@
#ifndef KALMAN_H
#ifndef KALMAN_H
#define KALMAN_H
#include "parameters.h"
#include "struct.h"
+61 -30
View File
@@ -1,38 +1,57 @@
#ifndef PARAMETERS_H
#ifndef PARAMETERS_H
#define PARAMETERS_H
#pragma once
/**********************************雷达参数*************************************************/
#define PI 3.1415926f
//频点(GHz)
#define FREQ0 16.8
#define FREQ1 16.8
#define FREQ2 16.8
#define FREQ3 16.8
#define FREQ4 16.8
#define FREQ5 16.8
#define FREQ6 16.8
#define FREQ7 16.8
#define FREQ8 16.8
#define FREQ9 16.8
#define FREQ10 16.8
#define FREQ11 16.8
#define FREQ12 16.8
#define FREQ13 16.8
#define FREQ14 16.8
#define FREQ15 16.8
#define FREQ16 16.8
#define FREQ17 16.8
#define FREQ18 16.8
#define FREQ19 16.8
#define FREQ20 16.8
#define FREQ0 9.2
/**********************************版本信息*************************************************/
#define MAJOR_VERSION 1
#define MINOR_VERSION 5
#define PATCH_VERSION 7
#ifdef PHASE_SCANNING
#define VERSION_INFO "PS"
#else
#define VERSION_INFO "MS"
#endif
#define STRINGIFY_IMPL(x) #x
#define STRINGIFY(x) STRINGIFY_IMPL(x)
#define VERSION_STRING \
"V" STRINGIFY(MAJOR_VERSION) "." \
STRINGIFY(MINOR_VERSION) "." \
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
//日志文件输出目录(相对当前工作目录的路径,目录不存在时自动创建)
#define RDP_LOG_DIR ".\\rdp_logs\\"
/**********************************数据记录调试开关*******************************************/
// 数据记录调试功能(结构体 -> SQLite 数据表,ORM 由内置 ormpp 完成)由 CMake 选项
// RDP_DATA_RECORD(AUTO/ON/OFF)控制:AUTO 时 Debug 构建启用、Release 构建关闭(交付默认)。
// 启用时由构建系统定义 RDP_RECORD_ENABLE,记录模块(rdp_record.cpp + sqlite3)才参与编译;
// 关闭时 RDP_RECORD 宏展开为 ((void)0),实参不求值,不做任何记录、不留任何运行开销。
// 注意:此处不提供手工开关宏,请在构建配置中选择(详见 docs/data_record_debug.md)。
//记录数据库存放目录(相对宿主进程当前工作目录,不存在时自动创建)
#define RDP_RECORD_DB_DIR ".\\rdp_records\\"
//#define MECHANICAL_SCANNING
//#define PHASE_SCANNING
#ifdef MECHANICAL_SCANNING
/******************************数据处理参数**************************************/
#define DATA_RATE_TAS 0.0625
// #define DATA_RATE_TAS 0.0625
#define SIGMA_R 10.0 //测量误差
#define SIGMA_A 0.02
@@ -58,10 +77,14 @@
#define TRACK_START_THRESHOLD 8 //起航波门 3 越大越容易起批
#define ALPHA_START 60 //起航夹角 120 越大越容易起批
#define TRACK_DIE_ROUND 4 // 航迹消亡时间
#define TRACK_DIE_ROUND_TAS 96 //
#define MAX_EXTRAPOLATE_NUM 3 //最大外推点数
#define MAX_EXTRAPOLATE_NUM_TAS 48 //TAS最大外推点数
#define ASSO_THORD 3
#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
@@ -72,12 +95,14 @@
#define DIRECT_TRACKING_QUEUE_LENGTH 10
#pragma message("MECHANICAL_SCANNING")
#endif
#ifdef PHASE_SCANNING
/******************************数据处理参数**************************************/
#define DATA_RATE_TAS 0.3
// #define DATA_RATE_TAS 0.3
#define SIGMA_R 10.0 //测量误差
#define SIGMA_A 0.02
@@ -105,10 +130,14 @@
#define TRACK_START_THRESHOLD 8 //起航波门 3 越大越容易起批
#define ALPHA_START 60 //起航夹角 120 越大越容易起批
#define TRACK_DIE_ROUND 4 // 航迹消亡时间
#define TRACK_DIE_ROUND_TAS 5
#define MAX_EXTRAPOLATE_NUM 3 //最大外推点数
#define MAX_EXTRAPOLATE_NUM_TAS 5 //TAS最大外推点数
#define ASSO_THORD 3
#define TRACK_DIE_ROUND 6 // 航迹消亡圈数
#define TRACK_DIE_ROUND_PRIMARY 10 // 重点航迹消亡圈数
#define TRACK_DIE_ROUND_TAS 10 //TAS航迹消亡圈数
#define ASSO_THORD 4
#define H_F_WIN_LEN 3
@@ -119,6 +148,8 @@
#define DIRECT_TRACKING_QUEUE_LENGTH 10
#pragma message("PHASE_SCANNING")
#endif
#define Round(x) (((x) > 0) \
+154
View File
@@ -0,0 +1,154 @@
#include "rdp_log.h"
#include "parameters.h"
#include <fstream>
#include <sstream>
#include <time.h>
#include <direct.h>
namespace
{
//日志模块内部状态:日志文件流 + 重定向前 std::cout 的原始缓冲区
struct RdpLogState
{
std::ofstream file; //日志文件输出流
std::streambuf *original_buf; //重定向前 std::cout 的原始缓冲区
bool enabled; //是否已成功重定向到日志文件
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 &State()
{
static RdpLogState s;
return s;
}
//当前本地时间字符串,格式 "YYYY-MM-DD HH:MM:SS"
std::string CurrentTime()
{
char buf[32] = {0};
time_t now = time(NULL);
struct tm local;
if (localtime_s(&local, &now) != 0)
{
return std::string();
}
strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &local);
return std::string(buf);
}
//提取纯文件名(去掉路径前缀),日志前缀中只显示 文件名:行号
const char *FileName(const char *path)
{
if (path == 0)
{
return "";
}
const char *base = path;
for (const char *p = path; *p != '\0'; ++p)
{
if (*p == '\\' || *p == '/')
{
base = p + 1;
}
}
return base;
}
}
namespace rdp_log
{
bool Init()
{
RdpLogState &st = State();
if (st.enabled)
{
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
//日志开关关闭:不重定向,日志仍输出到标准输出
return false;
#endif
}
void Shutdown()
{
RdpLogState &st = State();
if (!st.enabled)
{
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();
}
std::string prefix(const char *file, int line)
{
std::ostringstream oss;
oss << "[" << CurrentTime() << "][" << FileName(file) << ":" << line << "] ";
return oss.str();
}
}
+27
View File
@@ -0,0 +1,27 @@
#ifndef RDP_LOG_H
#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>
#include <string>
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); //生成 "[时间戳][文件名:行号] " 前缀字符串
}
//日志输出宏:自动附加 时间戳、代码文件、代码行号 后输出到标准输出(std::cout)
#define RDP_LOG std::cout << rdp_log::prefix(__FILE__, __LINE__)
#endif // RDP_LOG_H
+273
View File
@@ -0,0 +1,273 @@
/***********************************数据记录调试模块实现****************************************/
// 本文件仅在构建系统定义 RDP_RECORD_ENABLE(CMake 选项 RDP_DATA_RECORD 开启)时参与编译,
// 且以 C++20 编译(ormpp 要求);雷达处理代码仍为 C++17,二者通过 rdp_record.h 的
// 「仅声明 + 显式实例化」跨标准边界,详见 docs/data_record_debug.md。
#ifdef RDP_RECORD_ENABLE
#include "rdp_record.h"
#include "rdp_record_entity.h"
#include "parameters.h"
#include "rdp_log.h"
#include <climits>
#include <direct.h>
#include <memory>
#include <mutex>
#include <string>
#include <time.h>
#include <vector>
//第三方库(ormpp / iguana)模板代码在 /W3 下可能产生告警,按 third_party/ormpp/UPSTREAM.md
//的约定在此隔离;本文件自身的逻辑仍按工程 /W3 标准编译
#pragma warning(push, 0)
#include "dbng.hpp"
#include "sqlite.hpp"
#pragma warning(pop)
//记录实体写入函数的显式实例化宏(与 rdp_record_entity.h 的 RDP_RECORD_DECLARE 配套使用)
#define RDP_RECORD_DEFINE(Entity) \
template void rdp_record::Write<Entity>(const Entity&); \
template void rdp_record::Write<Entity>(const std::vector<Entity>&)
namespace
{
//记录模块内部状态(Pimpl:ormpp / SQLite 类型不出现在 rdp_record.h 中)
struct Record_State
{
ormpp::dbng<ormpp::sqlite> db; //SQLite 连接(单连接,写入时串行使用)
std::string path; //当前 db 文件全路径
bool ready; //是否已成功打开并完成初始化
bool failed; //初始化失败:后续写入全部忽略,不再反复报错
std::mutex mtx; //写入串行化(宿主多线程调用时保护单连接)
Record_State() : ready(false), failed(false) {}
};
std::unique_ptr<Record_State>& State()
{
//函数内静态:宿主未调用 Data_Process_Factory::Destroy() 时由析构兜底关库
static std::unique_ptr<Record_State> s;
return s;
}
//当前本地时间字符串,格式 "YYYYMMDD_HHMMSS"(与 rdp_log 的日志文件命名风格一致)
bool TimeStamp(char* buf, size_t size)
{
time_t now = time(NULL);
struct tm local;
if (localtime_s(&local, &now) != 0)
{
return false;
}
return strftime(buf, size, "%Y%m%d_%H%M%S", &local) != 0;
}
//写入失败提示(「每个实体只提示一次」由调用方的静态标志控制)
void ReportError(Record_State& st, const char* what)
{
RDP_LOG << "[rdp_record] " << what << " failed: " << st.db.get_last_error() << std::endl;
}
//确保实体的数据表存在:每个实体类型只尝试建表一次(失败后该实体永久跳过)
//注意:ormpp 的 ormpp_auto_key 定义在全局命名空间(third_party/ormpp/ormpp/entity.hpp),
// 传该参数可在建表时登记自增主键,使后续插入自动跳过 id 列
template <typename T>
bool EnsureTable(Record_State& st)
{
static const bool created = st.db.create_datatable<T>(::ormpp_auto_key{"id"});
return created;
}
//写入前置处理:自动建表;返回 true 表示可以向该表写入
template <typename T>
bool PrepareWrite(Record_State& st)
{
static bool reported = false;
if (EnsureTable<T>(st))
{
return true;
}
if (!reported)
{
reported = true;
ReportError(st, "create table");
}
return false;
}
}
namespace rdp_record
{
bool Init()
{
std::unique_ptr<Record_State>& sp = State();
if (sp.get() != 0)
{
return sp->ready; //已经初始化过(含失败的幂等返回)
}
sp.reset(new Record_State());
Record_State& st = *sp;
//创建记录目录(目录已存在时 _mkdir 返回 EEXIST,忽略;逻辑与 rdp_log.cpp 一致)
std::string dir = RDP_RECORD_DB_DIR;
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());
}
//按当前日期+时间生成记录库文件名:rdp_record_YYYYMMDD_HHMMSS.db
char stamp[32] = {0};
if (!TimeStamp(stamp, sizeof(stamp)))
{
RDP_LOG << "[rdp_record] cannot get local time, recording disabled" << std::endl;
st.failed = true;
return false;
}
st.path = RDP_RECORD_DB_DIR;
st.path += "rdp_record_";
st.path += stamp;
st.path += ".db";
if (!st.db.connect(st.path))
{
RDP_LOG << "[rdp_record] cannot open database: " << st.path << std::endl;
st.failed = true;
return false;
}
//调试用途的性能取舍:关闭同步刷盘(提交时不再 fsync),崩溃时可能丢失尾部数据;
//如需崩溃安全可改为 PRAGMA synchronous=NORMAL(或在 sqlite3 层自行开启 WAL)。
//注意:ormpp 的 execute() 只接受「不返回结果行」的语句,故不在此设置 journal_mode。
st.db.execute("PRAGMA synchronous=OFF;");
st.ready = true;
RDP_LOG << "[rdp_record] enabled, database: " << st.path << std::endl;
return true;
}
void Shutdown()
{
std::unique_ptr<Record_State>& sp = State();
if (sp.get() == 0 || !sp->ready)
{
return;
}
std::lock_guard<std::mutex> lock(sp->mtx);
RDP_LOG << "[rdp_record] shutdown, database: " << sp->path << std::endl;
sp->db.disconnect();
sp->ready = false; //path 保留,便于宿主在关库后仍能取到本次记录库路径
}
std::string db_path()
{
std::unique_ptr<Record_State>& sp = State();
return (sp.get() == 0) ? std::string() : sp->path;
}
template <typename T>
void Write(const T& v)
{
std::unique_ptr<Record_State>& sp = State();
if (sp.get() == 0 || !sp->ready || sp->failed)
{
return; //未初始化 / 初始化失败 / 已关库:静默忽略
}
std::lock_guard<std::mutex> lock(sp->mtx);
if (!PrepareWrite<T>(*sp))
{
return;
}
//写入失败时每个实体只提示一次:不重试、不抛出、不影响雷达数据处理
static bool reported = false;
if (sp->db.insert(v) == INT_MIN && !reported)
{
reported = true;
ReportError(*sp, "insert");
}
}
template <typename T>
void Write(const std::vector<T>& v)
{
if (v.empty())
{
return;
}
std::unique_ptr<Record_State>& sp = State();
if (sp.get() == 0 || !sp->ready || sp->failed)
{
return;
}
std::lock_guard<std::mutex> lock(sp->mtx);
if (!PrepareWrite<T>(*sp))
{
return;
}
//写入失败时每个实体只提示一次:不重试、不抛出、不影响雷达数据处理
static bool reported = false;
if (sp->db.insert(v) == INT_MIN && !reported)
{
reported = true;
ReportError(*sp, "insert");
}
}
}
/***********************************记录实体注册区*********************************************/
// 新增记录实体时在此追加两行(字段顺序必须与 rdp_record_entity.h 中的结构体声明顺序一致):
// YLT_REFL(结构体名, 字段1, 字段2, ...);
// RDP_RECORD_DEFINE(结构体名);
//输入点迹
YLT_REFL(RdpRecPoint, id,point_id,frame_id,circle_id,range,azimuth, velocity,amplitude ,height,SNR,RCS,
encoder_value,amp_asv_point,prn_fftn_point,CPI_time,GNSS_time,use_flag,azimuth_beam_index,pitch_beam_index,PRF_index,freq_index,point_type,point_section_asso);
RDP_RECORD_DEFINE(RdpRecPoint);
//TWS凝聚后点迹
YLT_REFL(RdpRecPointCohesionTWS, id,point_id,frame_id,circle_id,range,azimuth, velocity,amplitude ,height,SNR,RCS,
encoder_value,amp_asv_point,prn_fftn_point,CPI_time,GNSS_time,use_flag,azimuth_beam_index,pitch_beam_index,PRF_index,freq_index,point_type,point_section_asso);
RDP_RECORD_DEFINE(RdpRecPointCohesionTWS);
//TAS凝聚后点迹
YLT_REFL(RdpRecPointCohesionTAS, id,point_id,frame_id,circle_id,range,azimuth, velocity,amplitude ,height,SNR,RCS,
encoder_value,amp_asv_point,prn_fftn_point,CPI_time,GNSS_time,use_flag,azimuth_beam_index,pitch_beam_index,PRF_index,freq_index,point_type,point_section_asso);
RDP_RECORD_DEFINE(RdpRecPointCohesionTAS);
//临时航迹
YLT_REFL(RdpRecTempTrack, id, temp_track_id,point_id,circle_id,X,P,r,azi,vr,height,SNR,RCS,amplitude, CPI_time,GNSS_time,d,buff_round,temp_track_section_idx,asso_flag,pitch_num);
RDP_RECORD_DEFINE(RdpRecTempTrack);
//可靠航迹
YLT_REFL(RdpRecTrustTrack, id,track_id,point_id,circle_id,Track_Index,Range,Azimuth,Elevation,Velocity,Amplitude,Height,T_track,GNSS_time,Track_Sum,Point_Index,Track_Mode,
Track_Update_Flag,Extrapolate_round,point_flag,approach_flag,manual_delete_flag,manual_tracking_flag,
associate_point_number,primary_target_flag,Target_Type,Track_section_idx,
range_point,azi_point,elev_point,vr_point,prf_point,point_type,snr_point,
amp_asv_point,prn_fftn_point,RCS,pitch_num,X,P,X1,X2,X3,P2,P3,u);
RDP_RECORD_DEFINE(RdpRecTrustTrack);
//关联信息
YLT_REFL(RdpRecAssoInfo, id,point_id,track_id,circle_id,d1,d2,d3,d_min,point_index,track_index,
delta_T,delta_h,R,VT,
tas_flag,thd_d,thd_h,thd_r,
min_d_flag);
RDP_RECORD_DEFINE(RdpRecAssoInfo);
// // 示例②:本圈输出的航迹
// YLT_REFL(RdpRecTrackOut, id, track_index, track_mode, point_sum, flag_point, flag_direction,
// gnss_time_ms, track_time_s, x, y, z, v_x, v_y, v_z, range_m, azi_deg, elev_deg,
// range_v_mps, snr, rcs);
// RDP_RECORD_DEFINE(RdpRecTrackOut);
#endif // RDP_RECORD_ENABLE
+56
View File
@@ -0,0 +1,56 @@
#ifndef RDP_RECORD_H
#define RDP_RECORD_H
#pragma once
/***********************************数据记录调试模块********************************************/
// 功能(完整说明见 docs/data_record_debug.md):
// 1) 把结构体数据按「一个记录实体一张表」的方式写入 SQLite 数据库,结构体到数据表的映射由
// 内置的 ormpp 完成(third_party/ormpp,见该目录 UPSTREAM.md);
// 2) 整个功能由构建开关控制:CMake 选项 RDP_DATA_RECORD(AUTO/ON/OFF)开启时由构建系统定义
// RDP_RECORD_ENABLE,rdp_record.cpp 与 sqlite3 amalgamation 才参与编译;关闭(交付/Release
// 构建默认)时本模块完全不编译,RDP_RECORD / RDP_RECORD_BATCH 展开为 ((void)0)、实参不求值,
// 不留任何代码与运行开销;
// 3) 生命周期:Data_Process_Factory::GetB() 调用 rdp_record::Init()(建目录、开库),
// Data_Process_Factory::Destroy() 调用 rdp_record::Shutdown()(关库);Init 之前、初始化
// 失败之后或 Shutdown 之后的写入一律静默忽略,不影响雷达数据处理;
// 4) 记录点用法(新增数据 = 加结构体 + 注册 + 一行记录点,配方见 docs/data_record_debug.md):
// 单条:RDP_RECORD(RdpRecPointTws{0, 值1, 值2, ...});
// 批量:RDP_RECORD_BATCH(recs); // recs 为 std::vector<某记录实体>
// 需要语句/局部变量的记录点请整体用 #ifdef RDP_RECORD_ENABLE ... #endif 包裹,
// 保证关闭记录功能时既无代码也无「变量未使用」告警;
// 5) 记录实体类型必须先在 rdp_record_entity.h 中声明并注册,否则链接期报 LNK2019
// (extern template 已阻止 C++17 调用方隐式实例化)。
#include <string>
#include <vector>
namespace rdp_record
{
bool Init(); //初始化记录库(由 Data_Process_Factory::GetB() 调用)
void Shutdown(); //关闭记录库(由 Data_Process_Factory::Destroy() 调用)
std::string db_path(); //当前记录库文件全路径(未启用或未成功打开时返回空串)
//写入单条记录实体(T 为记录实体类型,须在 rdp_record.cpp 中显式实例化)
template <typename T> void Write(const T& v);
//批量写入同一记录实体的多条记录(内部使用事务)
template <typename T> void Write(const std::vector<T>& v);
}
#ifdef RDP_RECORD_ENABLE
//记录宏:把指定记录实体写入数据库(首次写入该实体时自动建表)
#define RDP_RECORD(...) ::rdp_record::Write(__VA_ARGS__)
//批量记录宏:把 std::vector<记录实体> 一次性写入(每批一个事务)
#define RDP_RECORD_BATCH(...) ::rdp_record::Write(__VA_ARGS__)
#else
//记录功能关闭:记录模块不参与编译,宏展开为空(实参不求值)
#define RDP_RECORD(...) ((void)0)
#define RDP_RECORD_BATCH(...) ((void)0)
namespace rdp_record
{
inline bool Init() { return false; }
inline void Shutdown() {}
inline std::string db_path() { return std::string(); }
template <typename T> inline void Write(const T&) {}
template <typename T> inline void Write(const std::vector<T>&) {}
}
#endif
#endif // RDP_RECORD_H
+486
View File
@@ -0,0 +1,486 @@
#ifndef RDP_RECORD_ENTITY_H
#define RDP_RECORD_ENTITY_H
#pragma once
/*******************************数据记录实体(=数据库表)定义区*********************************/
// 使用说明(完整配方见 docs/data_record_debug.md):
// 1) 新增一张记录表:在本文件「记录实体定义区」添加结构体,并在其后紧跟一行
// RDP_RECORD_DECLARE(结构体名);
// 再到 rdp_record.cpp 的「记录实体注册区」添加两行
// YLT_REFL(结构体名, 字段1, 字段2, ...); // 顺序必须与结构体声明顺序一致
// RDP_RECORD_DEFINE(结构体名);
// 最后在需要记录的位置写 RDP_RECORD(结构体名{0, 值1, 值2, ...}); 或 RDP_RECORD_BATCH(向量);
// 建表无需任何代码:首次写入该实体时自动 CREATE TABLE IF NOT EXISTS;
// 2) 命名与建表约定:结构体定义在全局命名空间(勿放入 namespace),结构体名即数据库表名
// (ormpp 取「短结构体名」),建议以 RdpRec 开头;首个字段固定为 int64_t id(自增主键,
// 写入时固定填 0,由 SQLite 分配);
// 3) 字段类型仅限标量:int / int64_t / float / double / bool(SQLite 侧映射 INTEGER / INTEGER /
// FLOAT / DOUBLE / INTEGER)。禁止使用数组(含 float speed_dim[129]、char[N] 等)——ormpp 对
// C 数组按 C 字符串语义(strlen)绑定,会读越界或写入错误数据;大批量数组请另建「元素展开表」
// 或改用 blob 字段;
// 4) 表结构变更(增删字段)不会迁移已存在的旧表:本模块每次运行生成新的 db 文件,天然规避。
#include "rdp_record.h"
#include <cstdint>
#include <vector>
#include <iostream>
#include <sstream>
#include <string>
#include <type_traits>
#include <cstddef>
#include "struct.h"
//记录实体的写入函数实例化声明宏:声明「该实体的 Write 在 rdp_record.cpp 中显式实例化」,
//阻止 C++17 调用方隐式实例化(本模块的 ormpp 实现以 C++20 编译,见 docs/data_record_debug.md)
#define RDP_RECORD_DECLARE(Entity) \
extern template void rdp_record::Write<Entity>(const Entity&); \
extern template void rdp_record::Write<Entity>(const std::vector<Entity>&)
// 基本类型元素转字符串
template <typename T>
std::string elementToString(const T& value);
// 数组版本
template <typename T, std::size_t N>
std::string arrayToString(const T (&arr)[N]);
// 非数组元素:直接转字符串
template <typename T>
std::string arrayToString(const T& value);
// ============================ 记录实体定义区(在此新增结构体) ============================
// 输入点迹 —— 输入的原始点迹数据
struct RdpRecPoint
{
int64_t id; //自增主键(写入时固定填 0)
long long point_id; //点迹ID
long long frame_id; //帧ID
long long circle_id; //圈ID
double range; //径向距离
double azimuth; //方位 (弧度)
double velocity ; //速度
double amplitude ; //幅度
double height; //高度
double SNR; //信噪比
double RCS; //目标RCS
float encoder_value; //码盘值
float amp_asv_point; //归一化幅度dB
float prn_fftn_point; //点迹速度维FFT点数
long long CPI_time; //CPI时间
long long GNSS_time; //GNSS时间
int use_flag; //点迹使用标志 1使用 0未使用
int azimuth_beam_index; //方位波位号
int pitch_beam_index; //俯仰波位号
int PRF_index; //PRF号
int freq_index; //频点
int point_type; //TAS 1 TWS0
int point_section_asso; //点迹区
RdpRecPoint(){
id = 0;
}
RdpRecPoint(PointRecv& p){
id = 0;
point_id = p.point_id;
frame_id = p.frame_id;
circle_id = p.circle_id;
range = p.range;
azimuth = p.azimuth;
velocity = p.velocity;
amplitude = p.amplitude;
height = p.height;
SNR = p.SNR;
RCS = p.RCS;
encoder_value = p.encoder_value;
amp_asv_point = p.amp_asv_point;
prn_fftn_point = p.prn_fftn_point;
CPI_time = p.CPI_time;
GNSS_time = p.GNSS_time;
use_flag = p.use_flag;
azimuth_beam_index = p.azimuth_beam_index;
pitch_beam_index = p.pitch_beam_index;
PRF_index = p.PRF_index;
freq_index = p.freq_index;
point_type = p.point_type;
point_section_asso = p.point_section_asso;
}
};
RDP_RECORD_DECLARE(RdpRecPoint);
// TWS凝聚后点迹 —— 凝聚后的点迹数据
struct RdpRecPointCohesionTWS
{
int64_t id; //自增主键(写入时固定填 0)
long long point_id; //点迹ID
long long frame_id; //帧ID
long long circle_id; //圈ID
double range; //径向距离
double azimuth; //方位 (弧度)
double velocity ; //速度
double amplitude ; //幅度
double height; //高度
double SNR; //信噪比
double RCS; //目标RCS
float encoder_value; //码盘值
float amp_asv_point; //归一化幅度dB
float prn_fftn_point; //点迹速度维FFT点数
long long CPI_time; //CPI时间
long long GNSS_time; //GNSS时间
int use_flag; //点迹使用标志 1使用 0未使用
int azimuth_beam_index; //方位波位号
int pitch_beam_index; //俯仰波位号
int PRF_index; //PRF号
int freq_index; //频点
int point_type; //TAS 1 TWS0
int point_section_asso; //点迹区
RdpRecPointCohesionTWS(){
id = 0;
}
RdpRecPointCohesionTWS(PointRecv& p){
id = 0;
point_id = p.point_id;
frame_id = p.frame_id;
circle_id = p.circle_id;
range = p.range;
azimuth = p.azimuth;
velocity = p.velocity;
amplitude = p.amplitude;
height = p.height;
SNR = p.SNR;
RCS = p.RCS;
encoder_value = p.encoder_value;
amp_asv_point = p.amp_asv_point;
prn_fftn_point = p.prn_fftn_point;
CPI_time = p.CPI_time;
GNSS_time = p.GNSS_time;
use_flag = p.use_flag;
azimuth_beam_index = p.azimuth_beam_index;
pitch_beam_index = p.pitch_beam_index;
PRF_index = p.PRF_index;
freq_index = p.freq_index;
point_type = p.point_type;
point_section_asso = p.point_section_asso;
}
};
RDP_RECORD_DECLARE(RdpRecPointCohesionTWS);
// TAS凝聚后点迹 —— 凝聚后的点迹数据
struct RdpRecPointCohesionTAS
{
int64_t id; //自增主键(写入时固定填 0)
long long point_id; //点迹ID
long long frame_id; //帧ID
long long circle_id; //圈ID
double range; //径向距离
double azimuth; //方位 (弧度)
double velocity ; //速度
double amplitude ; //幅度
double height; //高度
double SNR; //信噪比
double RCS; //目标RCS
float encoder_value; //码盘值
float amp_asv_point; //归一化幅度dB
float prn_fftn_point; //点迹速度维FFT点数
long long CPI_time; //CPI时间
long long GNSS_time; //GNSS时间
int use_flag; //点迹使用标志 1使用 0未使用
int azimuth_beam_index; //方位波位号
int pitch_beam_index; //俯仰波位号
int PRF_index; //PRF号
int freq_index; //频点
int point_type; //TAS 1 TWS0
int point_section_asso; //点迹区
RdpRecPointCohesionTAS(){
id = 0;
}
RdpRecPointCohesionTAS(PointRecv& p){
id = 0;
point_id = p.point_id;
frame_id = p.frame_id;
circle_id = p.circle_id;
range = p.range;
azimuth = p.azimuth;
velocity = p.velocity;
amplitude = p.amplitude;
height = p.height;
SNR = p.SNR;
RCS = p.RCS;
encoder_value = p.encoder_value;
amp_asv_point = p.amp_asv_point;
prn_fftn_point = p.prn_fftn_point;
CPI_time = p.CPI_time;
GNSS_time = p.GNSS_time;
use_flag = p.use_flag;
azimuth_beam_index = p.azimuth_beam_index;
pitch_beam_index = p.pitch_beam_index;
PRF_index = p.PRF_index;
freq_index = p.freq_index;
point_type = p.point_type;
point_section_asso = p.point_section_asso;
}
};
RDP_RECORD_DECLARE(RdpRecPointCohesionTAS);
// 零时航迹
struct RdpRecTempTrack
{
int64_t id; //自增主键(写入时固定填 0)
long long temp_track_id; //临时航迹ID
long long point_id; //关联上的点迹ID
long long circle_id; //处理圈数ID
std::string X; //状态
std::string P; //协方差
double r; //距离
double azi; //方位
double vr; //径向速度
double height; //高度
double SNR; //信噪比
double RCS; //RCS
double amplitude; //幅度
long long CPI_time; //时间戳
long long GNSS_time; //GNSS时间戳
double d; //关联上的点的d(马氏距离)
int buff_round; //已缓存圈数
int temp_track_section_idx; //临时航迹区号 与点迹区的划分相同
int asso_flag; //关联标志 已关联:1 未关联:0
int pitch_num; // 俯仰波位号
RdpRecTempTrack(){
id = 0;
}
RdpRecTempTrack(Temp_track& p){
id = 0;
temp_track_id = p.temp_track_id;
point_id = p.point_id;
circle_id = p.circle_id;
X = arrayToString(p.X);
P = arrayToString(p.P);
r = p.r;
azi = p.azi;
vr = p.vr;
height = p.height;
RCS = p.RCS;
SNR = p.SNR;
amplitude = p.amplitude;
CPI_time = p.CPI_time;
GNSS_time = p.GNSS_time;
d = p.d;
buff_round = p.buff_round;
temp_track_section_idx = p.temp_track_section_idx;
asso_flag = p.asso_flag;
pitch_num = p.pitch_num;
}
};
RDP_RECORD_DECLARE(RdpRecTempTrack);
// 可靠航迹
struct RdpRecTrustTrack
{
int64_t id; //自增主键(写入时固定填 0)
long long track_id; //航迹ID
long long point_id; //关联上的点迹ID
long long circle_id; //处理圈数ID
int Track_Index; //航迹号
double Range; //距离
double Azimuth; //方位
double Elevation; //俯仰
double Velocity; //航向速度
double Amplitude; //幅度
double Height; //高度
long long T_track; //航迹时间
long long GNSS_time; //GNSS时间
int Track_Sum; //航迹总数
int Point_Index; //该航迹上的第几个点
int Track_Mode; //跟踪模式
int Track_Update_Flag; //航迹更新标志 1已更新/0未更新
int Extrapolate_round; //航迹持续外推时间
int point_flag; //是否实点 1实点 0外推
int approach_flag; //接近标志 1接近 0远离
int manual_delete_flag; //手动航迹删除
int manual_tracking_flag; //手动TAS
int associate_point_number; //关联的点数
int primary_target_flag; //重点目标标记
int Target_Type; //目标类型 搜索目标 低空目标 地面目标
int Track_section_idx; //航迹区号
double range_point; //关联上的点信息(距离、方位、俯仰)
double azi_point;
double elev_point;
double vr_point;
double prf_point;
int point_type; //TWS TAS
double snr_point; //信噪比
float amp_asv_point; //关联上的点迹信息(归一化幅度)
float prn_fftn_point; //点迹速度维FFT点数
double RCS;
int pitch_num; // 俯仰波位号
std::string X; //IMM
std::string P;
std::string X1;
std::string X2;
std::string X3;
std::string P2;
std::string P3;
std::string u;
RdpRecTrustTrack(){
id = 0;
}
RdpRecTrustTrack(Trust_Track& p){
id = 0;
track_id = p.track_id;
point_id = p.point_id;
circle_id = p.circle_id;
Track_Index = p.Track_Index;
Range = p.Range;
Azimuth = p.Azimuth;
Elevation = p.Elevation;
Velocity = p.Velocity;
Amplitude = p.Amplitude;
Height = p.Height;
T_track = p.T_track;
GNSS_time = p.GNSS_time;
Track_Sum = p.Track_Sum;
Point_Index = p.Point_Index;
Track_Mode = p.Track_Mode;
Track_Update_Flag = p.Track_Update_Flag;
Extrapolate_round = p.Extrapolate_round;
point_flag = p.point_flag;
approach_flag = p.approach_flag;
manual_delete_flag = p.manual_delete_flag;
manual_tracking_flag = p.manual_tracking_flag;
associate_point_number = p.associate_point_number;
primary_target_flag = p.primary_target_flag;
Target_Type = p.Target_Type; //目标类型 搜索目标 低空目标 地面目标
Track_section_idx = p.Track_section_idx; //航迹区号
range_point = p.range_point; //关联上的点信息(距离、方位、俯仰)
azi_point = p.azi_point;
elev_point = p.elev_point;
vr_point = p.vr_point;
prf_point = p.prf_point;
point_type = p.point_type; //TWS TAS
snr_point = p.snr_point; //信噪比
amp_asv_point = p.amp_asv_point; //关联上的点迹信息(归一化幅度)
prn_fftn_point = p.prn_fftn_point; //点迹速度维FFT点数
RCS = p.RCS;
pitch_num = p.pitch_num; // 俯仰波位号
X = arrayToString(p.X); //IMM
P = arrayToString(p.P);
X1 = arrayToString(p.X1);
X2 = arrayToString(p.X2);
X3 = arrayToString(p.X3);
P2 = arrayToString(p.P2);
P3 = arrayToString(p.P3);
u = arrayToString(p.u);
}
};
RDP_RECORD_DECLARE(RdpRecTrustTrack);
// 关联信息
struct RdpRecAssoInfo
{
int64_t id; //自增主键(写入时固定填 0)
long long point_id;
long long track_id;
long long circle_id;
double d1;
double d2;
double d3;
double d_min;
int point_index;
int track_index;
double delta_T;
double delta_h;
double R;
double VT;
bool tas_flag; //TAS关联标志 1是 0否
bool thd_d;
bool thd_h;
bool thd_r;
bool min_d_flag;
RdpRecAssoInfo(){
id = 0;
}
RdpRecAssoInfo(Asso_Info& p){
id = 0;
point_id = p.point_id;
track_id = p.track_id;
circle_id = p.circle_id;
d1 = p.d1;
d2 = p.d2;
d3 = p.d3;
d_min = p.d_min;
point_index = p.point_index;
track_index = p.track_index;
delta_T = p.delta_T;
delta_h = p.delta_h;
R = p.R;
VT = p.VT;
tas_flag = p.tas_flag;
thd_d = p.thd_d;
thd_h = p.thd_h;
thd_r = p.thd_r;
min_d_flag = p.min_d_flag;
}
};
RDP_RECORD_DECLARE(RdpRecAssoInfo);
// ============================ 数组转换函数 ============================
// 基本类型元素转字符串
template <typename T>
std::string elementToString(const T& value) {
std::ostringstream oss;
oss << value;
return oss.str();
}
// 前置声明数组版本
template <typename T, std::size_t N>
std::string arrayToString(const T (&arr)[N]);
// 非数组元素:直接转字符串
template <typename T>
std::string arrayToString(const T& value) {
return elementToString(value);
}
// 数组版本
template <typename T, std::size_t N>
std::string arrayToString(const T (&arr)[N]) {
std::string result;
// 如果 T 仍然是数组,说明当前不是最内层,用分号;
// 否则当前就是最内层,用逗号。
const char sep = std::is_array<T>::value ? ';' : ',';
for (std::size_t i = 0; i < N; ++i) {
if (i > 0) {
result += sep;
}
result += arrayToString(arr[i]);
}
return result;
}
#endif // RDP_RECORD_ENTITY_H
+78 -32
View File
@@ -1,4 +1,4 @@
#ifndef STRUCT_H
#ifndef STRUCT_H
#define STRUCT_H
#pragma once
@@ -9,25 +9,29 @@ using namespace std;
// 点迹数据结构
struct PointRecv
{
double Range; //径向距离
double Azimuth; //方位 (弧度)
double Velocity ; //速度
double Amplitude ; //幅度
double Height; //高度
double snr; //信噪比
long long point_id; //点迹ID
long long frame_id; //帧ID
long long circle_id; //圈ID
double range; //径向距离
double azimuth; //方位 (弧度)
double velocity ; //速度
double amplitude ; //幅度
double height; //高度
double SNR; //信噪比
double RCS; //目标RCS
long long CPI_Time; //CPI时间
long long GNSS_time; //GNSS时间
int Point_index; //点迹号 1~50
int Point_Sum; //点迹总数
int Use_Flag; //点迹使用标志 1使用 0未使用
int beam_index; //波位号
float encoder_value; //码盘值
float amp_asv_point; //归一化幅度dB
float prn_fftn_point; //点迹速度维FFT点数
long long CPI_time; //CPI时间
long long GNSS_time; //GNSS时间
int use_flag; //点迹使用标志 1使用 0未使用
int azimuth_beam_index; //方位波位号
int pitch_beam_index; //俯仰波位号
int PRF_index; //PRF号
int Freq_index; //频点
int track_mode; //TAS 1 TWS0
int freq_index; //频点
int point_type; //TAS 1 TWS0
int point_section_asso; //点迹区
int pitch_num; // 俯仰波位号
float speed_dim[129]; // 目标的十字星数据,目标点速度维129个点
float range_dim[8]; // 目标的十字星数据,目标点距离维8个点
};
@@ -35,13 +39,21 @@ struct PointRecv
// 可靠航迹数据结构
struct Trust_Track
{
long long track_id; //航迹ID
long long point_id; //关联上的点迹ID
long long circle_id; //处理圈数ID
int Track_Index; //航迹号
double Range; //距离
double Azimuth; //方位
double Elevation; //俯仰
double Velocity; //航向速度
double Amplitude; //幅度
double Height; //高度
long long T_track; //航迹时间
long long GNSS_time; //GNSS时间
long long T_track; //航迹时间
long long GNSS_time; //GNSS时间
int Track_Sum; //航迹总数
int Point_Index; //该航迹上的第几个点
@@ -54,8 +66,10 @@ struct Trust_Track
int approach_flag; //接近标志 1接近 0远离
int manual_delete_flag; //手动航迹删除
int manual_tracking_flag; //手动TAS
int associate_point_number; //关联的点数
int manual_tracking_flag; //手动TAS
int associate_point_number; //关联的点数
int primary_target_flag; //重点目标标记
int Target_Type; //目标类型 搜索目标 低空目标 地面目标
@@ -69,7 +83,9 @@ struct Trust_Track
double vr_point;
double prf_point;
int point_type; //TWS TAS
double snr_point; //信噪比
double snr_point; //信噪比
float amp_asv_point; //关联上的点迹信息(归一化幅度)
float prn_fftn_point; //点迹速度维FFT点数
double RCS;
@@ -86,32 +102,34 @@ struct Trust_Track
double P1[6][6];
double P2[6][6];
double P3[6][6];
double u[3];
double u[3];
};
//临时航迹结构体
struct Temp_track
{
long long temp_track_id; //临时航迹ID
long long point_id; //关联上的点迹ID
long long circle_id; //处理圈数ID
double X[4]; //状态
double X[4]; //状态
double P[4][4]; //协方差
double r; //距离
double azi; //方位
double vr; //径向速度
double height; //高度
double snr; //信噪比
double SNR; //信噪比
double RCS; //RCS
double Amp; //幅度
long long T; //时间戳
long long GNSS_time; //GNSS时间戳
double amplitude; //幅度
long long CPI_time; //时间戳
long long GNSS_time; //GNSS时间戳
double d; //关联上的点的d
double d; //关联上的点的d(马氏距离)
int buff_round; //已缓存圈数
int Temp_track_section_idx; //临时航迹区号 与点迹区的划分相同
int temp_track_section_idx; //临时航迹区号 与点迹区的划分相同
int asso_flag; //关联标志 已关联:1 未关联:0
@@ -120,12 +138,40 @@ struct Temp_track
float range_dim[8]; // 目标的十字星数据,目标点距离维8个点
};
//存储关联信息
struct Asso_Info
{
long long point_id;
long long track_id;
long long circle_id;
double d1;
double d2;
double d3;
double d_min;
int point_index;
int track_index;
double delta_T;
double delta_h;
double R;
double VT;
bool tas_flag; //TAS关联标志 1是 0否
bool thd_d;
bool thd_h;
bool thd_r;
bool min_d_flag;
};
// TAS跟踪目标结构体
struct Tracking_Target
{
int Index; //目标批号
int empty_flag; //是否为空标志位 1非空 0空
long long last_track_time; //最新跟踪时间
};
//引导跟踪目标结构体
+96 -85
View File
@@ -1,8 +1,11 @@
#include "tas_ctrl.h"
#include "tas_ctrl.h"
#include "coor_trans.h"
#include "rdp_log.h"
#include <cmath>
#include <cstring>
#include <vector>
#include <iomanip>
#include <sstream>
using namespace std;
@@ -39,14 +42,14 @@ void TAS_Ctrl::tas_ctrl_process(std::vector<Trust_Track> *trust_track
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);
//跟踪队列移位
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_queue[i],&tas_target_queue[i-1],sizeof(Tracking_Target));
}
memcpy(&tas_target_queue[0], &tas_target_tmp, sizeof(Tracking_Target));
// //跟踪队列移位
// 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_queue[i],&tas_target_queue[i-1],sizeof(Tracking_Target));
// }
// memcpy(&tas_target_queue[0], &tas_target_tmp, sizeof(Tracking_Target));
};
void TAS_Ctrl::tas_target_add(std::vector<Trust_Track> *trust_track,
@@ -55,7 +58,7 @@ void TAS_Ctrl::tas_target_add(std::vector<Trust_Track> *trust_track,
struct RadarPara Work_Parameter)
{
for (int i=0;i<trust_track->size();i++)
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));
@@ -81,6 +84,7 @@ void TAS_Ctrl::tas_target_add(std::vector<Trust_Track> *trust_track,
//插入跟踪队列
tas_target_queue[j].Index=(*trust_track)[i].Track_Index;
tas_target_queue[j].empty_flag=1;
tas_target_queue[j].last_track_time=(*trust_track)[i].T_track;
//跟踪目标数目加一
tas_target_num=tas_target_num+1;
@@ -94,18 +98,18 @@ void TAS_Ctrl::tas_target_add(std::vector<Trust_Track> *trust_track,
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=r_output;
Trust_Track_Output[*Trust_track_num_Output-1][0].Azimuth=azmi_output/PI*180;
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=
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=(*trust_track)[i].Amplitude;
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;
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=asin((*trust_track)[i].Height/r_output)/PI*180;
Trust_Track_Output[*Trust_track_num_Output-1][0].z=(*trust_track)[i].Height+Work_Parameter.Height;
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_snr = (*trust_track)[i].snr_point;
Trust_Track_Output[*Trust_track_num_Output-1][0].track_snr = static_cast<float>((*trust_track)[i].snr_point);
break;
}
@@ -163,7 +167,7 @@ void TAS_Ctrl::tas_target_del(std::vector<Trust_Track> *trust_track,
{
//查找TAS队列里的目标是否存在
int flag=0;
for (int j=0;j<trust_track->size();j++){
for (size_t j=0;j<trust_track->size();j++){
{
if(tas_target_queue[i].Index==(*trust_track)[j].Track_Index && (*trust_track)[j].Track_Mode == 1 && (*trust_track)[j].manual_tracking_flag == 1)
{
@@ -212,17 +216,17 @@ void TAS_Ctrl::tas_target_del(std::vector<Trust_Track> *trust_track,
// 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=r_output;
// Trust_Track_Output[*Trust_track_num_Output-1][0].Azimuth=azmi_output/PI*180;
// 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=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=(*trust_track)[i].Amplitude;
// 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;
// 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=asin((*trust_track)[i].Height/r_output)/PI*180;
// Trust_Track_Output[*Trust_track_num_Output-1][0].z=(*trust_track)[i].Height+Work_Parameter.Height;
// 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_snr = (*trust_track)[i].snr_point;
// Trust_Track_Output[*Trust_track_num_Output-1][0].track_snr = static_cast<float>((*trust_track)[i].snr_point);
// }
// }
// }
@@ -255,61 +259,75 @@ void TAS_Ctrl::tas_beam_output(std::vector<Trust_Track> *trust_track
struct RadarPara Work_Parameter,
long long latest_timestamp)
{
if(tas_target_queue[0].empty_flag==1)
long long CPI_time = 0;
long long earliest_CPI_time = LLONG_MAX;
float delta_T = 0;
float delta_T_last_track = 0;
double H_track = 0;
int earliest_track_idx=-1;
int earliest_tas_queue_idx=-1;
double X_now[6] = {0};
//查找TAS队列中CPI时间最早的目标且满足TAS数据率的目标
for(int tas_idx=0;tas_idx<TAS_QUEUE_LENGTH;tas_idx++)
{
double H_track = 0;
double X_now[6] = {0};
long long CPI_time = 0;
bool found_track = false;
for (int i=0;i<trust_track->size();i++ )
if(tas_target_queue[tas_idx].empty_flag==0)
{
if((*trust_track)[i].Track_Index == tas_target_queue[0].Index)
continue;
}
for (size_t track_idx=0;track_idx<trust_track->size();track_idx++ )
{
if((*trust_track)[track_idx].Track_Index == tas_target_queue[tas_idx].Index)
{
H_track = (*trust_track)[i].Height;
CPI_time = (*trust_track)[i].T_track;
for(int ii=0;ii<6;ii++)
X_now[ii]=(*trust_track)[i].X[ii];
found_track = true;
CPI_time = (*trust_track)[track_idx].T_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))
{
earliest_CPI_time = CPI_time;
H_track = (*trust_track)[track_idx].Height;
delta_T = (latest_timestamp - CPI_time) / 1000.0f;
earliest_track_idx = static_cast<int>(track_idx);
earliest_tas_queue_idx = tas_idx;
for(int ii=0;ii<6;ii++){
X_now[ii]=(*trust_track)[track_idx].X[ii];
}
}
break;
}
}
}
if (!found_track)
{
Tracking_beam->open_flag = 0;
return;
}
//没有满足条件的TAS目标,关闭跟踪波束
if(earliest_track_idx == -1)
{
Tracking_beam->open_flag = 0;
return;
}
//数据率门控:航迹时间与当前最新时间戳的差值大于 1/DATA_RATE_TAS 时才输出TAS跟踪波束,
//保证TAS目标数据率不随波束时间变化(使用Work_Parameter中的DATA_RATE_TAS参数值)。
//DATA_RATE_TAS<=0 时不做门控,按原逻辑每次输出(避免除零,兼容未配置该参数的调用者)。
if (Work_Parameter.DATA_RATE_TAS > 0)
{
double delta_T_track = (double)(latest_timestamp - CPI_time) / 1000.0;
if (delta_T_track <= 1.0 / Work_Parameter.DATA_RATE_TAS)
{
//更新间隔未到 不输出跟踪波束
Tracking_beam->open_flag = 0;
return;
}
}
//更新目标最后跟踪时间
tas_target_queue[earliest_tas_queue_idx].last_track_time = latest_timestamp;
float delta_T = (latest_timestamp - CPI_time) / 1000.0f;
//预测目标位置 计算跟踪波束波位号 俯仰角
double x_track=X_now[0]+X_now[1]*delta_T;
double y_track=X_now[3]+X_now[4]*delta_T;
double amzi,range;
coor_trans Coor_trans;
Coor_trans.cart2polar(x_track,y_track,&range,&amzi);
//预测目标位置 计算跟踪波束波位号 俯仰角
double x_track=X_now[0]+X_now[1]*delta_T;
double y_track=X_now[3]+X_now[4]*delta_T;
double amzi,range;
coor_trans Coor_trans;
Coor_trans.cart2polar(x_track,y_track,&range,&amzi);
//目标距离
Tracking_beam->Range=range;
//目标方位
Tracking_beam->Azi=amzi/PI*180;
//目标距离
Tracking_beam->Range=(float)range;
//目标方位
Tracking_beam->Azi=(float)amzi/PI*180;
// Tracking_beam->Azi+=6;
//目标俯仰角
double elev=asin(H_track/range)/PI*180;
//目标俯仰角
double elev=asin(H_track/range)/PI*180;
// if(elev<=0)
// elev=0;
@@ -318,23 +336,16 @@ void TAS_Ctrl::tas_beam_output(std::vector<Trust_Track> *trust_track
// else
// elev=elev;
Tracking_beam->Elev=elev;
Tracking_beam->Elev=(float)elev;
//跟踪波束类型
Tracking_beam->type=1;
//跟踪波束类型
Tracking_beam->type=1;
//跟踪目标批号
Tracking_beam->TAS_track_index = tas_target_queue[0].Index;
//跟踪目标批号
Tracking_beam->TAS_track_index = (*trust_track)[earliest_track_idx].Track_Index;
//跟踪波束开关开启
Tracking_beam->open_flag=1;
//std::cout << "TAS: range: " <<Tracking_beam->Range << "azi: " <<Tracking_beam->Azi <<"pit: " <<Tracking_beam->Elev;
}
else
{
//跟踪波束开关关闭
Tracking_beam->open_flag=0;
}
//跟踪波束开关开启
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;
}
+1 -1
View File
@@ -1,4 +1,4 @@
#ifndef TAS_CTRL_H
#ifndef TAS_CTRL_H
#define TAS_CTRL_H
#include "data_process_class_dll.h"
#include "parameters.h"
+281 -301
View File
@@ -1,10 +1,15 @@
#include "track_asso.h"
#include "track_asso.h"
#include "kalman.h"
#include "coor_trans.h"
#include "rdp_log.h"
#include <cmath>
#include <cstring>
#include <vector>
#include <Eigen/Dense>
#include "rdp_record.h"
#include "rdp_record_entity.h"
using namespace Eigen;
using namespace std;
@@ -15,22 +20,23 @@ 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 //雷达参数
)
{
//取出对应点迹区的点
for (int i=0;i< point_recv->size();i++)
for (size_t i=0;i< point_recv->size();i++)
{
point_process.push_back((*point_recv)[i]);
int n=point_process.size();
int n=static_cast<int>(point_process.size());
point_process[n-1].point_section_asso = 1;
}
//IMM算法
model_interaction(trust_track);
model_filter(trust_track, Work_Parameter);
model_filter(trust_track, Work_Parameter, latest_timestamp);
model_output(trust_track);
@@ -38,7 +44,7 @@ int Track_Asso:: track_asso_process(std::vector<PointRecv> *point_
std::vector <PointRecv>::iterator Iter;
for (Iter= point_process.begin(); Iter!=point_process.end();)
{
if((*Iter).Use_Flag==1 )
if((*Iter).use_flag==1 )
{
point_process.erase(Iter);
Iter=point_process.begin();
@@ -51,7 +57,7 @@ int Track_Asso:: track_asso_process(std::vector<PointRecv> *point_
//剩余点重新存入点迹
std::vector<PointRecv>().swap((*point_recv));
for (int i=0;i<point_process.size();i++)
for (size_t i=0;i<point_process.size();i++)
{
(*point_recv).push_back(point_process[i]);
@@ -62,11 +68,10 @@ int Track_Asso:: track_asso_process(std::vector<PointRecv> *point_
std::vector<PointRecv>().swap(point_process);
//输出航迹
for (int i=0;i<trust_track->size();i++)
for (size_t i=0;i<trust_track->size();i++)
{ // 输出更新航迹条件:
// if((*trust_track)[i].manual_tracking_flag==0)
// {
if((*trust_track)[i].Track_Update_Flag==1){
(*trust_track)[i].Track_Update_Flag=0;
*Trust_track_num_Output= *Trust_track_num_Output+1;
Trust_Track_Output[*Trust_track_num_Output-1][0].Point_Sum=1;
@@ -76,58 +81,86 @@ int Track_Asso:: track_asso_process(std::vector<PointRecv> *point_
coor_trans Coor_trans;
double r, azi;
Coor_trans.cart2polar(x, y, &r, &azi);
Trust_Track_Output[*Trust_track_num_Output-1][0].Range=r;
Trust_Track_Output[*Trust_track_num_Output-1][0].Azimuth=azi/PI*180;
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 = asin((*trust_track)[i].Height/r)/PI*180;
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 = (*trust_track)[i].elev_point;
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 =
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 = (*trust_track)[i].Amplitude;
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].track_time=(*trust_track)[i].T_track/1000.0;
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=(*trust_track)[i].Height+Work_Parameter.Height;
Trust_Track_Output[*Trust_track_num_Output-1][0].z=static_cast<float>((*trust_track)[i].Height+Work_Parameter.Height);
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=Direction_Angle/PI*180;
Trust_Track_Output[*Trust_track_num_Output-1][0].Direction_Angle=static_cast<float>(Direction_Angle/PI*180);
(*trust_track)[i].Range = Trust_Track_Output[*Trust_track_num_Output-1][0].Range;
(*trust_track)[i].Azimuth = Trust_Track_Output[*Trust_track_num_Output-1][0].Azimuth;
(*trust_track)[i].Elevation = Trust_Track_Output[*Trust_track_num_Output-1][0].Elevation;
(*trust_track)[i].Velocity = Trust_Track_Output[*Trust_track_num_Output-1][0].Range_V;
//关联点信息
Trust_Track_Output[*Trust_track_num_Output-1][0].range_point=(*trust_track)[i].range_point;
Trust_Track_Output[*Trust_track_num_Output-1][0].azi_point=(*trust_track)[i].azi_point;
Trust_Track_Output[*Trust_track_num_Output-1][0].elev_point=(*trust_track)[i].elev_point;
Trust_Track_Output[*Trust_track_num_Output-1][0].vr_point=(*trust_track)[i].vr_point;
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 = (*trust_track)[i].prf_point;
Trust_Track_Output[*Trust_track_num_Output-1][0].track_snr = (*trust_track)[i].snr_point;
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);
//直接输出点迹高度-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 = (*trust_track)[i].Height+Work_Parameter.Height;
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].Elevation = (*trust_track)[i].elev_point;
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].x=(*trust_track)[i].X[0];
Trust_Track_Output[*Trust_track_num_Output-1][0].y=(*trust_track)[i].X[3];
Trust_Track_Output[*Trust_track_num_Output-1][0].v_x=(*trust_track)[i].X[1];
Trust_Track_Output[*Trust_track_num_Output-1][0].v_y=(*trust_track)[i].X[4];
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]);
Trust_Track_Output[*Trust_track_num_Output-1][0].track_rcs = (*trust_track)[i].RCS;
// Trust_Track_Output[*Trust_track_num_Output-1][0].Flag_Direction=Trust_Track_Output[*Trust_track_num_Output-1][0].vr_point>0?1:0;
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));
// }
RDP_RECORD(RdpRecTrustTrack((*trust_track)[i]));
}
}
return 0;
@@ -137,7 +170,7 @@ int Track_Asso:: track_asso_process(std::vector<PointRecv> *point_
void Track_Asso:: model_interaction(std::vector <Trust_Track> *trust_track)
{
for (int loop_of_track=0;loop_of_track<trust_track->size();loop_of_track++)
for (size_t loop_of_track=0;loop_of_track<trust_track->size();loop_of_track++)
{
// if((*trust_track)[loop_of_track].manual_tracking_flag == 0)
@@ -397,181 +430,134 @@ void Track_Asso::IMM_d_cal(double v_track,
}
void Track_Asso:: model_filter(std::vector <Trust_Track> *trust_track,struct RadarPara Work_Parameter)
void Track_Asso:: model_filter(std::vector <Trust_Track> *trust_track,struct RadarPara Work_Parameter, long long latest_timestamp)
{
//存储关联信息
struct asso_info
{
double d1;
double d2;
double d3;
double d_min;
int point_index;
int track_index;
};
std::vector <asso_info> associated_info;
std::vector <Asso_Info> associated_info;
//遍历所有点迹航迹 计算点迹和航迹的统计距离
for (int loop_of_track = 0; loop_of_track<trust_track->size();loop_of_track++)
for (size_t loop_of_track = 0; loop_of_track<trust_track->size();loop_of_track++)
{
// if((*trust_track)[loop_of_track].manual_tracking_flag==0)
// {
(*trust_track)[loop_of_track].point_flag = 0; //航迹的point_flag置为0 关联上点后再置为1
(*trust_track)[loop_of_track].point_flag = 0; //航迹的point_flag置为0 关联上点后再置为1
for (int loop_of_point = 0; loop_of_point<point_process.size();loop_of_point++)
{
for (size_t loop_of_point = 0; loop_of_point<point_process.size();loop_of_point++)
{
//时间差
double delta_T = (point_process[loop_of_point].CPI_Time - (*trust_track)[loop_of_track].T_track)/1000.0;
//航迹X P
double X1[6],X2[6],X3[6],P1[6][6],P2[6][6],P3[6][6];
memcpy(X1,(*trust_track)[loop_of_track].X1,6*sizeof(double));
memcpy(X2,(*trust_track)[loop_of_track].X2,6*sizeof(double));
memcpy(X3,(*trust_track)[loop_of_track].X3,6*sizeof(double));
memcpy(P1,(*trust_track)[loop_of_track].P1,6*6*sizeof(double));
memcpy(P2,(*trust_track)[loop_of_track].P2,6*6*sizeof(double));
memcpy(P3,(*trust_track)[loop_of_track].P3,6*6*sizeof(double));
double v_track = sqrt(pow((*trust_track)[loop_of_track].X[1],2)+pow((*trust_track)[loop_of_track].X[4],2));
double h_track = (*trust_track)[loop_of_track].Height;
double r_track = sqrt(pow((*trust_track)[loop_of_track].X[0],2)+pow((*trust_track)[loop_of_track].X[3],2));
//时间差
double delta_T = (point_process[loop_of_point].CPI_time - (*trust_track)[loop_of_track].T_track)/1000.0;
//航迹X P
double X1[6],X2[6],X3[6],P1[6][6],P2[6][6],P3[6][6];
memcpy(X1,(*trust_track)[loop_of_track].X1,6*sizeof(double));
memcpy(X2,(*trust_track)[loop_of_track].X2,6*sizeof(double));
memcpy(X3,(*trust_track)[loop_of_track].X3,6*sizeof(double));
memcpy(P1,(*trust_track)[loop_of_track].P1,6*6*sizeof(double));
memcpy(P2,(*trust_track)[loop_of_track].P2,6*6*sizeof(double));
memcpy(P3,(*trust_track)[loop_of_track].P3,6*6*sizeof(double));
double v_track = sqrt(pow((*trust_track)[loop_of_track].X[1],2)+pow((*trust_track)[loop_of_track].X[4],2));
double h_track = (*trust_track)[loop_of_track].Height;
double r_track = sqrt(pow((*trust_track)[loop_of_track].X[0],2)+pow((*trust_track)[loop_of_track].X[3],2));
//量测信息
double Z[3] = {point_process[loop_of_point].Range,
point_process[loop_of_point].Azimuth,
point_process[loop_of_point].Velocity};
double prt = point_process[loop_of_point].PRF_index;
double freq_ind = point_process[loop_of_point].Freq_index;
double h_point = point_process[loop_of_point].Height;
//量测信息
double Z[3] = {point_process[loop_of_point].range,
point_process[loop_of_point].azimuth,
point_process[loop_of_point].velocity};
double prt = point_process[loop_of_point].PRF_index;
double freq_ind = point_process[loop_of_point].freq_index;
double h_point = point_process[loop_of_point].height;
//计算统计距离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);
//计算统计距离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);
//计算高度门限
// double d_h;
// if (r_track<1000)
// {
// if(abs(h_track-h_point)<=150)
// {
// d_h = 1;
// }
// else
// {
// d_h = 0;
// }
//判定统计距离门限,thd_d
bool thd_d = false;
// }
// else if (r_track<2000 && r_track>=1000)
// {
// if(abs(h_track-h_point)<=200)
// {
// d_h = 1;
// }
// else
// {
// d_h = 0;
// }
// }
// else if(r_track>=2000 && r_track<3000 )
// {
// if(abs(h_track-h_point)<=200)
// {
// d_h = 1;
// }
// else
// {
// d_h = 0;
// }
// }
// else if(r_track>=3000 && r_track<5000)
// {
// if(abs(h_track-h_point)<=300)
// {
// d_h = 1;
// }
// else
// {
// d_h = 0;
// }
// }
// else
// {
// d_h = 1;
// }
// std::cout << delta_T<<" "<<d1<<" "<<d2<<" "<<d3;
// std::cout << h_track<<" "<<h_point<<" "<<d_h;
// d_h = 1;
//计算俯仰门限
bool d_p = true;
// double p_track = atan2(h_track, r_track); //航迹俯仰
// double p_point = asin(h_point/point_process[loop_of_point].Range); //点迹俯仰
// if( (r_track > 3000 && abs(p_track - p_point)*180/PI <= 7) || \
// (r_track > 1000 && r_track <= 3000 && abs(p_track - p_point)*180/PI <= 5) || \
// (r_track <= 1000 && abs(p_track - p_point)*180/PI <= 10)){
// d_p = true;
// }
//计算距离门限,R=航迹点与量测点的距离,VT=航向速度乘以扫描时间差
double& t_x0 = (*trust_track)[loop_of_track].X[0];
double& t_y0 = (*trust_track)[loop_of_track].X[3];
double a_track = atan2(t_y0, t_x0); //航迹方位角
double& r_point = point_process[loop_of_point].Range; //点迹距离
double a_delta = abs(a_track - point_process[loop_of_point].Azimuth); //方位差
if(a_delta > PI){
a_delta = 2*PI - a_delta;
}
double R = sqrt(r_track * r_track + r_point*r_point - 2*r_track*r_point*cos(a_delta));
double VT = v_track*delta_T;
bool d_r = true;
if(VT < 100 && R > 300){
//VT<100m,使用固定门限300m
d_r = false;
}else if((VT>=100 && VT<=300 && R/VT>3)){
d_r = false;
}else if(VT>300 && R > 1200){
d_r = 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) && d_p && d_r)//
||( (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) && d_p && d_r)//
||( r_track>=1000 && (d1*d1<ASSO_THORD*ASSO_THORD || d2*d2<ASSO_THORD*ASSO_THORD || d3*d3<ASSO_THORD*ASSO_THORD) && d_p && d_r)//
)
{
//std::cout << "asooooooooooo";
struct asso_info associated_info_tmp;
associated_info_tmp.d1=d1;
associated_info_tmp.d2=d2;
associated_info_tmp.d3=d3;
associated_info_tmp.point_index=loop_of_point+1;
associated_info_tmp.track_index=loop_of_track+1;
if(d1<=d2&&d1<=d3)
associated_info_tmp.d_min=d1;
else if(d2<=d1&& d2<=d3)
associated_info_tmp.d_min=d2;
else
associated_info_tmp.d_min=d3;
associated_info.push_back(associated_info_tmp);
}
if( (r_track <= 500 && \
(d1*d1 < ASSO_THORD*ASSO_THORD || d2*d2 < ASSO_THORD*ASSO_THORD || d3*d3 < ASSO_THORD*ASSO_THORD / 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)) || \
(r_track >= 1000 && \
(d1*d1 < ASSO_THORD*ASSO_THORD || d2*d2 < ASSO_THORD*ASSO_THORD || d3*d3 < ASSO_THORD*ASSO_THORD))\
){
thd_d = true;
}
// }
//判定高度门限,thd_h
bool thd_h = false;
double delta_h = abs(h_track-h_point);
if( (r_track <= 1000 && delta_h <= 150) || \
((r_track > 1000 && r_track <= 2000) && delta_h <= 200) || \
((r_track > 2000 && r_track <= 3000) && delta_h <= 250) || \
((r_track > 3000 && r_track <= 5000) && delta_h <= 300) || \
r_track > 5000
){
thd_h = true;
}
//判定距离门限,R=航迹点与量测点的距离,VT=航向速度乘以扫描时间差
double& t_x0 = (*trust_track)[loop_of_track].X[0];
double& t_y0 = (*trust_track)[loop_of_track].X[3];
double a_track = atan2(t_y0, t_x0); //航迹方位角
double& r_point = point_process[loop_of_point].range; //点迹距离
double a_delta = abs(a_track - point_process[loop_of_point].azimuth); //方位差
if(a_delta > PI){
a_delta = 2*PI - a_delta;
}
double R = sqrt(r_track * r_track + r_point*r_point - 2*r_track*r_point*cos(a_delta));
double VT = v_track*delta_T;
bool thd_r = true;
if(VT < 100 && R > 300){
//VT<100m,使用固定门限300m
thd_r = false;
}else if((VT>=100 && VT<=300 && R/VT>3)){
thd_r = false;
}else if(VT>300 && R > 1200){
thd_r = false;
}
struct Asso_Info associated_info_tmp;
associated_info_tmp.d1=d1;
associated_info_tmp.d2=d2;
associated_info_tmp.d3=d3;
associated_info_tmp.point_index=static_cast<int>(loop_of_point)+1;
associated_info_tmp.track_index=static_cast<int>(loop_of_track)+1;
if(d1<=d2&&d1<=d3)
associated_info_tmp.d_min=d1;
else if(d2<=d1&& d2<=d3)
associated_info_tmp.d_min=d2;
else
associated_info_tmp.d_min=d3;
associated_info_tmp.point_id=point_process[loop_of_point].point_id;
associated_info_tmp.track_id=(*trust_track)[loop_of_track].track_id;
associated_info_tmp.circle_id=point_process[loop_of_point].circle_id;
associated_info_tmp.delta_T=delta_T;
associated_info_tmp.delta_h=delta_h;
associated_info_tmp.R=R;
associated_info_tmp.VT=VT;
associated_info_tmp.tas_flag=point_process[loop_of_point].point_type;
associated_info_tmp.thd_d=thd_d;
associated_info_tmp.thd_h=thd_h;
associated_info_tmp.thd_r=thd_r;
associated_info_tmp.min_d_flag=false;
//小于关联门限 保存关联信息
if(thd_d && thd_h && thd_r)
{
associated_info.push_back(associated_info_tmp);
}else{
RDP_RECORD(RdpRecAssoInfo(associated_info_tmp));
}
}
}
//最近邻法关联
int associated_num=associated_info.size();
for (int i=0;i<trust_track->size();i++)
int associated_num=static_cast<int>(associated_info.size());
for (size_t i=0;i<trust_track->size();i++)
{
//寻找最小d
if(associated_num>0)
@@ -594,9 +580,9 @@ void Track_Asso:: model_filter(std::vector <Trust_Track> *trust_track,struct Ra
double d3=associated_info[min_index-1].d3;
//卡尔曼滤波
double Z[3]={point_process[point_index-1].Range,point_process[point_index-1].Azimuth,point_process[point_index-1].Velocity};
double Z[3]={point_process[point_index-1].range,point_process[point_index-1].azimuth,point_process[point_index-1].velocity};
double prt = point_process[point_index-1].PRF_index;
double freq_ind = point_process[point_index-1].Freq_index;
double freq_ind = point_process[point_index-1].freq_index;
double X1[6],X2[6],X3[6],P1[6][6],P2[6][6],P3[6][6];
memcpy(X1,(*trust_track)[track_index-1].X1,6*sizeof(double));
memcpy(X2,(*trust_track)[track_index-1].X2,6*sizeof(double));
@@ -605,40 +591,14 @@ void Track_Asso:: model_filter(std::vector <Trust_Track> *trust_track,struct Ra
memcpy(P2,(*trust_track)[track_index-1].P2,6*6*sizeof(double));
memcpy(P3,(*trust_track)[track_index-1].P3,6*6*sizeof(double));
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 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);
if (delta_T <= 0)
if (delta_T > 0)
{
// 乱序/重复时间戳:不作为有效关联,不标记点迹已使用、不刷新航迹新鲜度
for (int ii=0;ii<associated_num;)
{
if(associated_info[ii].track_index==track_index)
{
associated_info.erase(associated_info.begin()+ii);
associated_num=associated_info.size();
}
else
ii++;
}
associated_num=associated_info.size();
for (int ii=0;ii<associated_num;)
{
if(associated_info[ii].point_index==point_index)
{
associated_info.erase(associated_info.begin()+ii);
associated_num=associated_info.size();
}
else
ii++;
}
associated_num=associated_info.size();
continue;
}
associated_info[min_index-1].min_d_flag=true;
if(delta_T>0)
{
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;
@@ -650,9 +610,9 @@ void Track_Asso:: model_filter(std::vector <Trust_Track> *trust_track,struct Ra
for (int ii=0;ii<3;ii++)
for (int jj=0;jj<3;jj++)
{
S1_out(ii,jj)=S1[ii][jj];
S2_out(ii,jj)=S2[ii][jj];
S3_out(ii,jj)=S3[ii][jj];
S1_out(ii,jj)=static_cast<float>(S1[ii][jj]);
S2_out(ii,jj)=static_cast<float>(S2[ii][jj]);
S3_out(ii,jj)=static_cast<float>(S3[ii][jj]);
}
double det_S1=S1_out.determinant();
double Possibility1=(det_S1>1e-12)?(1.0/sqrt(pow(2*PI,3)*det_S1)*exp(-0.5*d1)):0.0;
@@ -697,117 +657,137 @@ void Track_Asso:: model_filter(std::vector <Trust_Track> *trust_track,struct Ra
}
//更新航迹时间
(*trust_track)[track_index-1].T_track = point_process[point_index-1].CPI_Time;
(*trust_track)[track_index-1].T_track = point_process[point_index-1].CPI_time;
(*trust_track)[track_index-1].GNSS_time = point_process[point_index-1].GNSS_time;
//更新关联上的点迹信息
(*trust_track)[track_index-1].range_point=point_process[point_index-1].Range;
(*trust_track)[track_index-1].azi_point=point_process[point_index-1].Azimuth/PI*180;
(*trust_track)[track_index-1].elev_point= asin(point_process[point_index-1].Height/point_process[point_index-1].Range)/PI*180;
(*trust_track)[track_index-1].vr_point=point_process[point_index-1].Velocity;
(*trust_track)[track_index-1].point_id = point_process[point_index-1].point_id;
(*trust_track)[track_index-1].circle_id = point_process[point_index-1].circle_id;
(*trust_track)[track_index-1].range_point=point_process[point_index-1].range;
(*trust_track)[track_index-1].azi_point=point_process[point_index-1].azimuth/PI*180;
(*trust_track)[track_index-1].elev_point= asin(point_process[point_index-1].height/point_process[point_index-1].range)/PI*180;
(*trust_track)[track_index-1].vr_point=point_process[point_index-1].velocity;
(*trust_track)[track_index-1].point_type = 0;
(*trust_track)[track_index-1].prf_point = point_process[point_index-1].PRF_index;
(*trust_track)[track_index-1].snr_point = point_process[point_index-1].snr;
(*trust_track)[track_index-1].Amplitude= point_process[point_index-1].Amplitude; //幅度
(*trust_track)[track_index-1].snr_point = point_process[point_index-1].SNR;
(*trust_track)[track_index-1].Amplitude= point_process[point_index-1].amplitude; //幅度
(*trust_track)[track_index-1].RCS = point_process[point_index-1].RCS;
(*trust_track)[track_index-1].amp_asv_point = point_process[point_index-1].amp_asv_point;
(*trust_track)[track_index-1].prn_fftn_point = point_process[point_index-1].prn_fftn_point;
(*trust_track)[track_index-1].pitch_num = point_process[point_index-1].pitch_num;
(*trust_track)[track_index-1].pitch_num = point_process[point_index-1].pitch_beam_index;
std::memcpy((*trust_track)[track_index-1].speed_dim, point_process[point_index-1].speed_dim, sizeof((*trust_track)[track_index-1].speed_dim));
std::memcpy((*trust_track)[track_index-1].range_dim, point_process[point_index-1].range_dim, sizeof((*trust_track)[track_index-1].range_dim));
//更新高度
track_hight_update(track_index,point_index,trust_track);
(*trust_track)[track_index-1].Extrapolate_round = 0; //连续未用实点更新时间
(*trust_track)[track_index-1].point_flag=1; //实点
(*trust_track)[track_index-1].associate_point_number = (*trust_track)[track_index-1].associate_point_number+1; //关联点数+1
(*trust_track)[track_index-1].Track_Update_Flag = 1; //航迹更新标志
}
(*trust_track)[track_index-1].Extrapolate_round = 0; //连续未用实点更新时间
(*trust_track)[track_index-1].point_flag=1; //实点
(*trust_track)[track_index-1].associate_point_number = (*trust_track)[track_index-1].associate_point_number+1; //关联点数+1
//删除associated_info中关联上的航迹 点迹信息
for (int ii=0;ii<associated_num;)
{
if(associated_info[ii].track_index==track_index)
{
RDP_RECORD(RdpRecAssoInfo(associated_info[ii]));
associated_info.erase(associated_info.begin()+ii);
associated_num=associated_info.size();
associated_num=static_cast<int>(associated_info.size());
}
else
ii++;
}
associated_num=associated_info.size();
associated_num=static_cast<int>(associated_info.size());
for (int ii=0;ii<associated_num;)
{
if(associated_info[ii].point_index==point_index)
{
RDP_RECORD(RdpRecAssoInfo(associated_info[ii]));
associated_info.erase(associated_info.begin()+ii);
associated_num=associated_info.size();
associated_num=static_cast<int>(associated_info.size());
}
else
ii++;
}
associated_num=associated_info.size();
associated_num=static_cast<int>(associated_info.size());
//在接收点迹中 将关联上的点迹的使用标记位置为1 等待删除
point_process[point_index-1].Use_Flag = 1;
point_process[point_index-1].use_flag = 1;
}
}
//未关联上的航迹 进行外推 tws
for (int i =0; i<(*trust_track).size();i++ )
for (size_t i =0; i<(*trust_track).size();i++ )
{
if((*trust_track)[i].point_flag == 0 && (*trust_track)[i].manual_tracking_flag == 0)
if((*trust_track)[i].point_flag == 0 && (*trust_track)[i].manual_tracking_flag == 0) //未关联上点迹的航迹进行外推
{
double v_track = sqrt(pow((*trust_track)[i].X[1],2)+pow((*trust_track)[i].X[4],2));
double delta_T;
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
//外推点数未到上限,则继续外推,否则不外推
if((*trust_track)[i].Extrapolate_round <= MAX_EXTRAPOLATE_NUM)
{
double v_track = sqrt(pow((*trust_track)[i].X[1],2)+pow((*trust_track)[i].X[4],2));
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;
}
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);
//航迹X P
double X1[6],X2[6],X3[6],P1[6][6],P2[6][6],P3[6][6];
memcpy(X1,(*trust_track)[i].X1,6*sizeof(double));
memcpy(X2,(*trust_track)[i].X2,6*sizeof(double));
memcpy(X3,(*trust_track)[i].X3,6*sizeof(double));
memcpy(P1,(*trust_track)[i].P1,6*6*sizeof(double));
memcpy(P2,(*trust_track)[i].P2,6*6*sizeof(double));
memcpy(P3,(*trust_track)[i].P3,6*6*sizeof(double));
//外推
double X1_pred[6],X2_pred[6],X3_pred[6],P1_pred[6][6],P2_pred[6][6],P3_pred[6][6];
kalman Kalman;
Kalman.kalman_pred(F, Q1, X1, P1, X1_pred, P1_pred);
Kalman.kalman_pred(F, Q2, X2, P2, X2_pred, P2_pred);
Kalman.kalman_pred(F, Q3, X3, P3, X3_pred, P3_pred);
memcpy((*trust_track)[i].X1,X1_pred,6*sizeof(double));
memcpy((*trust_track)[i].X2,X2_pred,6*sizeof(double));
memcpy((*trust_track)[i].X3,X3_pred,6*sizeof(double));
memcpy((*trust_track)[i].P1,P1_pred,6*6*sizeof(double));
memcpy((*trust_track)[i].P2,P2_pred,6*6*sizeof(double));
memcpy((*trust_track)[i].P3,P3_pred,6*6*sizeof(double));
(*trust_track)[i].T_track = static_cast<long long>((*trust_track)[i].T_track+delta_T*1000.0); //更新航迹时间
(*trust_track)[i].GNSS_time = static_cast<long long>((*trust_track)[i].GNSS_time+delta_T*1000.0); //更新航迹时间
(*trust_track)[i].Extrapolate_round =(*trust_track)[i].Extrapolate_round+1; //连续未用实点更新时间
(*trust_track)[i].point_flag=0; //虚点
(*trust_track)[i].Track_Update_Flag = 1; //航迹更新标志
}else{
(*trust_track)[i].Extrapolate_round =(*trust_track)[i].Extrapolate_round+1; //连续未用实点更新时间
(*trust_track)[i].point_flag=0; //虚点
(*trust_track)[i].Track_Update_Flag = 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);
//航迹X P
double X1[6],X2[6],X3[6],P1[6][6],P2[6][6],P3[6][6];
memcpy(X1,(*trust_track)[i].X1,6*sizeof(double));
memcpy(X2,(*trust_track)[i].X2,6*sizeof(double));
memcpy(X3,(*trust_track)[i].X3,6*sizeof(double));
memcpy(P1,(*trust_track)[i].P1,6*6*sizeof(double));
memcpy(P2,(*trust_track)[i].P2,6*6*sizeof(double));
memcpy(P3,(*trust_track)[i].P3,6*6*sizeof(double));
//外推
double X1_pred[6],X2_pred[6],X3_pred[6],P1_pred[6][6],P2_pred[6][6],P3_pred[6][6];
kalman Kalman;
Kalman.kalman_pred(F, Q1, X1, P1, X1_pred, P1_pred);
Kalman.kalman_pred(F, Q2, X2, P2, X2_pred, P2_pred);
Kalman.kalman_pred(F, Q3, X3, P3, X3_pred, P3_pred);
memcpy((*trust_track)[i].X1,X1_pred,6*sizeof(double));
memcpy((*trust_track)[i].X2,X2_pred,6*sizeof(double));
memcpy((*trust_track)[i].X3,X3_pred,6*sizeof(double));
memcpy((*trust_track)[i].P1,P1_pred,6*6*sizeof(double));
memcpy((*trust_track)[i].P2,P2_pred,6*6*sizeof(double));
memcpy((*trust_track)[i].P3,P3_pred,6*6*sizeof(double));
(*trust_track)[i].T_track = (*trust_track)[i].T_track+delta_T*1000.0; //更新航迹时间
(*trust_track)[i].GNSS_time = (*trust_track)[i].GNSS_time+delta_T*1000.0; //更新航迹时间
(*trust_track)[i].Extrapolate_round =(*trust_track)[i].Extrapolate_round+1; //连续未用实点更新时间
(*trust_track)[i].point_flag=0; //虚点
}
}
@@ -815,7 +795,7 @@ void Track_Asso:: model_filter(std::vector <Trust_Track> *trust_track,struct Ra
void Track_Asso:: model_output(std::vector <Trust_Track> *trust_track)
{
for(int loop_of_track=0; loop_of_track<trust_track->size(); loop_of_track++)
for(size_t loop_of_track=0; loop_of_track<trust_track->size(); loop_of_track++)
{
// if((*trust_track)[loop_of_track].manual_tracking_flag==0)
@@ -919,10 +899,10 @@ void Track_Asso:: track_hight_update(int updata_
)
{
if(updata_track_index<=trust_track->size() && asso_point_index<=point_process.size())
if(updata_track_index<=static_cast<int>(trust_track->size()) && asso_point_index<=static_cast<int>(point_process.size()))
{
(*trust_track)[updata_track_index-1].Hight_smooth.push_back(point_process[asso_point_index-1].Height);
(*trust_track)[updata_track_index-1].Hight_smooth.push_back(point_process[asso_point_index-1].height);
int height_win_length;
@@ -944,10 +924,10 @@ void Track_Asso:: track_hight_update(int updata_
}
double sum=0;
if((*trust_track)[updata_track_index-1].Hight_smooth.size()<height_win_length)
if((*trust_track)[updata_track_index-1].Hight_smooth.size()<static_cast<size_t>(height_win_length))
{
for (int i=0;i<(*trust_track)[updata_track_index-1].Hight_smooth.size();i++)
for (size_t i=0;i<(*trust_track)[updata_track_index-1].Hight_smooth.size();i++)
{
sum=sum+(*trust_track)[updata_track_index-1].Hight_smooth[i];
@@ -956,10 +936,10 @@ void Track_Asso:: track_hight_update(int updata_
(*trust_track)[updata_track_index-1].Height=sum/(*trust_track)[updata_track_index-1].Hight_smooth.size();
}
else if((*trust_track)[updata_track_index-1].Hight_smooth.size()>=height_win_length)
else if((*trust_track)[updata_track_index-1].Hight_smooth.size()>=static_cast<size_t>(height_win_length))
{
int N=(*trust_track)[updata_track_index-1].Hight_smooth.size();
int N=static_cast<int>((*trust_track)[updata_track_index-1].Hight_smooth.size());
for (int i=0;i<height_win_length;i++)
{
sum=sum+(*trust_track)[updata_track_index-1].Hight_smooth[N-1-i];
+4 -2
View File
@@ -1,4 +1,4 @@
#ifndef TRACK_ASSO_H
#ifndef TRACK_ASSO_H
#define TRACK_ASSO_H
#include "data_process_class_dll.h"
@@ -16,6 +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 //工作参数
);
@@ -30,7 +31,8 @@ private:
void model_interaction(std::vector <Trust_Track> *trust_track); //模型交互
void model_filter(std::vector <Trust_Track> *trust_track, //滤波
struct RadarPara Work_Parameter
struct RadarPara Work_Parameter,
long long latest_timestamp
);
void model_output(std::vector <Trust_Track> *trust_track ); //模型输出
@@ -1,4 +1,4 @@
#include "track_asso_direct_tracking.h"
#include "track_asso_direct_tracking.h"
#include "kalman.h"
#include "coor_trans.h"
#include <cmath>
@@ -223,14 +223,14 @@ void Track_Asso_Direct_Tracking::model_filter(Trust_Track *trust_track,
//量测信息
// double Z[2]={point_process[loop_of_point].Range*cos(point_process[loop_of_point].Azimuth),
// point_process[loop_of_point].Range*sin(point_process[loop_of_point].Azimuth)};
double Z[3] = {point_process[loop_of_point].Range,
point_process[loop_of_point].Azimuth,
point_process[loop_of_point].Velocity};
double Z[3] = {point_process[loop_of_point].range,
point_process[loop_of_point].azimuth,
point_process[loop_of_point].velocity};
// double v_point = point_process[loop_of_point].Velocity;
double prt = point_process[loop_of_point].PRF_index;
double freq_ind = point_process[loop_of_point].Freq_index;
double delta_T = (point_process[loop_of_point].CPI_Time - T_track)/1000.0;
double h_point = point_process[loop_of_point].Height;
double freq_ind = point_process[loop_of_point].freq_index;
double delta_T = (point_process[loop_of_point].CPI_time - T_track)/1000.0;
double h_point = point_process[loop_of_point].height;
//计算统计距离d
double d1,d2,d3;
@@ -329,13 +329,13 @@ void Track_Asso_Direct_Tracking::model_filter(Trust_Track *trust_track,
double d3=associated_info[min_index-1].d3;
//卡尔曼滤波
double Z[3]={point_process[point_index-1].Range,point_process[point_index-1].Azimuth,point_process[point_index-1].Velocity};
double Z[3]={point_process[point_index-1].range,point_process[point_index-1].azimuth,point_process[point_index-1].velocity};
double prt = point_process[point_index-1].PRF_index;
double freq_ind = point_process[point_index-1].Freq_index;
double freq_ind = point_process[point_index-1].freq_index;
// double Z[2];
// Z[0]=point_process[point_index-1].Range*cos(point_process[point_index-1].Azimuth);
// 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 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);
@@ -401,19 +401,19 @@ void Track_Asso_Direct_Tracking::model_filter(Trust_Track *trust_track,
}
//更新航迹时间
(*trust_track).T_track = point_process[point_index-1].CPI_Time;
(*trust_track).T_track = point_process[point_index-1].CPI_time;
//更新关联上的点迹信息
(*trust_track).range_point=point_process[point_index-1].Range;
(*trust_track).azi_point=point_process[point_index-1].Azimuth/PI*180;
(*trust_track).elev_point= asin(point_process[point_index-1].Height/point_process[point_index-1].Range)/PI*180;
(*trust_track).vr_point=point_process[point_index-1].Velocity;
(*trust_track).range_point=point_process[point_index-1].range;
(*trust_track).azi_point=point_process[point_index-1].azimuth/PI*180;
(*trust_track).elev_point= asin(point_process[point_index-1].height/point_process[point_index-1].range)/PI*180;
(*trust_track).vr_point=point_process[point_index-1].velocity;
(*trust_track).point_type = 1;
(*trust_track).prf_point = point_process[point_index-1].PRF_index;
(*trust_track).snr_point = point_process[point_index-1].snr;
(*trust_track).snr_point = point_process[point_index-1].SNR;
(*trust_track).Extrapolate_round = 0; //连续未用实点更新时间
(*trust_track).point_flag=1; //实点
(*trust_track).Amplitude= point_process[point_index-1].Amplitude; //幅度
(*trust_track).Amplitude= point_process[point_index-1].amplitude; //幅度
(*trust_track).associate_point_number = (*trust_track).associate_point_number+1; //关联点数+1
(*trust_track).RCS = point_process[point_index-1].RCS;
@@ -424,7 +424,7 @@ void Track_Asso_Direct_Tracking::model_filter(Trust_Track *trust_track,
//未关联上,航迹外推
else
{
double delta_T = DATA_RATE_TAS;
double delta_T = Work_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);
@@ -648,7 +648,7 @@ void Track_Asso_Direct_Tracking::track_hight_update(int asso_point
)
{
(*trust_track).Hight_smooth.push_back(point_process[asso_point_index-1].Height);
(*trust_track).Hight_smooth.push_back(point_process[asso_point_index-1].height);
int height_win_length;
if((*trust_track).range_point <= 1000)
@@ -1,4 +1,4 @@
#ifndef TRACK_ASSO_DIRECT_TRACKING_H
#ifndef TRACK_ASSO_DIRECT_TRACKING_H
#define TRACK_ASSO_DIRECT_TRACKING_H
#include "data_process_class_dll.h"
#include "parameters.h"
+177 -162
View File
@@ -1,10 +1,15 @@
#include "track_asso_tas.h"
#include "track_asso_tas.h"
#include "kalman.h"
#include "coor_trans.h"
#include "rdp_log.h"
#include <cmath>
#include <cstring>
#include <vector>
#include <Eigen/Dense>
#include "rdp_record.h"
#include "rdp_record_entity.h"
using namespace Eigen;
using namespace std;
@@ -20,7 +25,7 @@ long long latest_timestamp //最新时间戳
{
//取出点迹
for (int i=0;i< point_recv_tas->size();i++)
for (size_t i=0;i< point_recv_tas->size();i++)
{
point_process.push_back((*point_recv_tas)[i]);
}
@@ -36,10 +41,11 @@ long long latest_timestamp //最新时间戳
std::vector<PointRecv>().swap(point_process);
//输出航迹
for (int i=0;i<trust_track->size();i++)
for (size_t i=0;i<trust_track->size();i++)
{ // 输出更新航迹条件:
if((*trust_track)[i].Track_Index == tas_track_idx)
if((*trust_track)[i].Track_Index == tas_track_idx && (*trust_track)[i].Track_Update_Flag == 1)
{
(*trust_track)[i].Track_Update_Flag = 0; //航迹更新标志
*Trust_track_num_Output= *Trust_track_num_Output+1;
Trust_Track_Output[*Trust_track_num_Output-1][0].Point_Sum=1;
@@ -49,46 +55,71 @@ long long latest_timestamp //最新时间戳
coor_trans Coor_trans;
double r, azi;
Coor_trans.cart2polar(x, y, &r, &azi);
Trust_Track_Output[*Trust_track_num_Output-1][0].Range=r;
Trust_Track_Output[*Trust_track_num_Output-1][0].Azimuth=azi/PI*180;
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);
{
double elev_ratio = (r > 0.0) ? (*trust_track)[i].Height / r : 0.0;
if (elev_ratio > 1.0) elev_ratio = 1.0;
if (elev_ratio < -1.0) elev_ratio = -1.0;
Trust_Track_Output[*Trust_track_num_Output-1][0].Elevation = asin(elev_ratio)/PI*180;
Trust_Track_Output[*Trust_track_num_Output-1][0].Elevation = static_cast<float>(asin(elev_ratio)/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 =
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 = (*trust_track)[i].Amplitude;
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].track_time=(*trust_track)[i].T_track/1000.0;
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=(*trust_track)[i].Height+Work_Parameter.Height;
Trust_Track_Output[*Trust_track_num_Output-1][0].z=static_cast<float>((*trust_track)[i].Height+Work_Parameter.Height);
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=Direction_Angle/PI*180;
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=(*trust_track)[i].range_point;
Trust_Track_Output[*Trust_track_num_Output-1][0].azi_point=(*trust_track)[i].azi_point;
Trust_Track_Output[*Trust_track_num_Output-1][0].elev_point=(*trust_track)[i].elev_point;
Trust_Track_Output[*Trust_track_num_Output-1][0].vr_point=(*trust_track)[i].vr_point;
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=1;
Trust_Track_Output[*Trust_track_num_Output-1][0].prf_point = (*trust_track)[i].prf_point;
Trust_Track_Output[*Trust_track_num_Output-1][0].track_snr = (*trust_track)[i].snr_point;
Trust_Track_Output[*Trust_track_num_Output-1][0].x=(*trust_track)[i].X[0];
Trust_Track_Output[*Trust_track_num_Output-1][0].y=(*trust_track)[i].X[3];
Trust_Track_Output[*Trust_track_num_Output-1][0].v_x=(*trust_track)[i].X[1];
Trust_Track_Output[*Trust_track_num_Output-1][0].v_y=(*trust_track)[i].X[4];
Trust_Track_Output[*Trust_track_num_Output-1][0].track_rcs = (*trust_track)[i].RCS;
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].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]);
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;
// Trust_Track_Output[*Trust_track_num_Output-1][0].Flag_Direction=Trust_Track_Output[*Trust_track_num_Output-1][0].vr_point>0?1:0;
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;
}
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));
(*trust_track)[i].Range = Trust_Track_Output[*Trust_track_num_Output-1][0].Range;
(*trust_track)[i].Azimuth = Trust_Track_Output[*Trust_track_num_Output-1][0].Azimuth;
(*trust_track)[i].Elevation = Trust_Track_Output[*Trust_track_num_Output-1][0].Elevation;
(*trust_track)[i].Velocity = Trust_Track_Output[*Trust_track_num_Output-1][0].Range_V;
RDP_RECORD(RdpRecTrustTrack((*trust_track)[i]));
}
}
@@ -101,7 +132,7 @@ long long latest_timestamp //最新时间戳
void Track_Asso_Tas:: model_interaction( std::vector<Trust_Track> *trust_track, int tas_track_idx)
{
for (int loop_of_track=0;loop_of_track<trust_track->size();loop_of_track++)
for (size_t loop_of_track=0;loop_of_track<trust_track->size();loop_of_track++)
{
if((*trust_track)[loop_of_track].Track_Index == tas_track_idx)
@@ -220,16 +251,7 @@ void Track_Asso_Tas::model_filter( std::vector<Trust_Track> *t
long long latest_timestamp //最新时间戳
)
{
//存储关联信息
struct asso_info
{
double d1;
double d2;
double d3;
double d_min;
int point_index;
};
std::vector <asso_info> associated_info;
std::vector <Asso_Info> associated_info;
//航迹信息
double X1[6] = {0},X2[6] = {0},X3[6] = {0},P1[6][6] = {{0}},P2[6][6] = {{0}},P3[6][6] = {{0}};
@@ -238,17 +260,12 @@ void Track_Asso_Tas::model_filter( std::vector<Trust_Track> *t
double r_track = 0;
double h_track = 0;
bool found_tas_track = false;
for (int loop_of_track=0;loop_of_track<trust_track->size();loop_of_track++)
for (size_t loop_of_track=0;loop_of_track<trust_track->size();loop_of_track++)
{
if((*trust_track)[loop_of_track].Track_Index == tas_track_idx)
{
found_tas_track = true;
// if(point_process.size()>0)
// {
// std::cout << "model_filter point_process :" <<point_process[0].Azimuth/PI*180<<" "<<point_process[0].Range;
// }
(*trust_track)[loop_of_track].point_flag = 0; //航迹的point_flag置为0 关联上点后再置为1
memcpy(X1,(*trust_track)[loop_of_track].X1,6*sizeof(double));
memcpy(X2,(*trust_track)[loop_of_track].X2,6*sizeof(double));
@@ -257,7 +274,7 @@ void Track_Asso_Tas::model_filter( std::vector<Trust_Track> *t
memcpy(P2,(*trust_track)[loop_of_track].P2,6*6*sizeof(double));
memcpy(P3,(*trust_track)[loop_of_track].P3,6*6*sizeof(double));
T_track = (*trust_track)[loop_of_track].T_track;
T_track = static_cast<double>((*trust_track)[loop_of_track].T_track);
v_track = sqrt(pow((*trust_track)[loop_of_track].X[1],2)+pow((*trust_track)[loop_of_track].X[4],2));
r_track = sqrt(pow((*trust_track)[loop_of_track].X[0],2)+pow((*trust_track)[loop_of_track].X[3],2));
h_track = (*trust_track)[loop_of_track].Height;
@@ -269,119 +286,99 @@ void Track_Asso_Tas::model_filter( std::vector<Trust_Track> *t
return;
// 计算量测和航迹统计距离
for (int loop_of_point = 0; loop_of_point<point_process.size();loop_of_point++)
for (size_t loop_of_point = 0; loop_of_point<point_process.size();loop_of_point++)
{
//量测信息
// double Z[2]={point_process[loop_of_point].Range*cos(point_process[loop_of_point].Azimuth),
// point_process[loop_of_point].Range*sin(point_process[loop_of_point].Azimuth)};
double Z[3] = {point_process[loop_of_point].Range,
point_process[loop_of_point].Azimuth,
point_process[loop_of_point].Velocity};
double Z[3] = {point_process[loop_of_point].range,
point_process[loop_of_point].azimuth,
point_process[loop_of_point].velocity};
// double v_point = point_process[loop_of_point].Velocity;
double prt = point_process[loop_of_point].PRF_index;
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)
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;
continue;
double h_point = point_process[loop_of_point].Height;
}
double h_point = point_process[loop_of_point].height;
//计算统计距离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);
//计算高度门限
double d_h;
if (r_track<1000)
{
if(abs(h_track-h_point)<=150)
{
d_h = 1;
}
else
{
d_h = 0;
}
//判定统计距离门限,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){
thd_d = true;
}
else if (r_track<2000 && r_track>=1000)
{
if(abs(h_track-h_point)<=200)
{
d_h = 1;
}
else
{
d_h = 0;
}
//判定高度门限,thd_h
bool thd_h = false;
double delta_h = abs(h_track-h_point);
if( (r_track <= 1000 && delta_h <= 150) || \
((r_track > 1000 && r_track <= 2000) && delta_h <= 200) || \
((r_track > 2000 && r_track <= 3000) && delta_h <= 250) || \
((r_track > 3000 && r_track <= 5000) && delta_h <= 300) || \
r_track > 5000
){
thd_h = true;
}
else if(r_track>=2000 && r_track<3000 )
{
if(abs(h_track-h_point)<=200)
{
d_h = 1;
}
else
{
d_h = 0;
}
}
else if(r_track>=3000 && r_track<5000)
{
if(abs(h_track-h_point)<=300)
{
d_h = 1;
}
else
{
d_h = 0;
}
}
struct Asso_Info associated_info_tmp;
associated_info_tmp.d1=d1;
associated_info_tmp.d2=d2;
associated_info_tmp.d3=d3;
associated_info_tmp.point_index=static_cast<int>(loop_of_point)+1;
associated_info_tmp.track_index=tas_track_idx;
if(d1<=d2&&d1<=d3)
associated_info_tmp.d_min=d1;
else if(d2<=d1&& d2<=d3)
associated_info_tmp.d_min=d2;
else
{
d_h = 1;
}
associated_info_tmp.d_min=d3;
associated_info_tmp.point_id=point_process[loop_of_point].point_id;
associated_info_tmp.track_id=(*trust_track)[tas_track_idx].track_id;
associated_info_tmp.circle_id=point_process[loop_of_point].circle_id;
associated_info_tmp.delta_T=delta_T;
associated_info_tmp.delta_h=delta_h;
associated_info_tmp.R=0;
associated_info_tmp.VT=0;
associated_info_tmp.tas_flag=point_process[loop_of_point].point_type;
associated_info_tmp.thd_d=thd_d;
associated_info_tmp.thd_h=thd_h;
associated_info_tmp.thd_r=true;
associated_info_tmp.min_d_flag=false;
// std::cout << "T d" <<point_process[loop_of_point].CPI_Time/1000<<" "<<T_track/1000<<" "<<delta_T<<" "<<d1<<" "<<d2<<" "<<d3;
// std::cout << "h_track" <<h_track<<" h_point"<<h_point<<" d_h"<<d_h;
//小于关联门限 保存关联信息
if((d1*d1<ASSO_THORD*ASSO_THORD || d2*d2<ASSO_THORD*ASSO_THORD || d3*d3<ASSO_THORD*ASSO_THORD)
&& d_h == 1)//
if(thd_d && thd_h)
{
// std::cout << "TAS asoooooo111" ;
struct asso_info associated_info_tmp;
associated_info_tmp.d1=d1;
associated_info_tmp.d2=d2;
associated_info_tmp.d3=d3;
associated_info_tmp.point_index=loop_of_point+1;
if(d1<=d2&&d1<=d3)
associated_info_tmp.d_min=d1;
else if(d2<=d1&& d2<=d3)
associated_info_tmp.d_min=d2;
else
associated_info_tmp.d_min=d3;
associated_info.push_back(associated_info_tmp);
}else{
RDP_RECORD(RdpRecAssoInfo(associated_info_tmp));
}
}
//有关联 滤波
if (associated_info.size()>0)
{
//RDP_LOG << "associated point for track index " << tas_track_idx << std::endl;
// std::cout << "TAS asoooooo222" ;
//找最近点
int min_index=1;
double min_d=associated_info[0].d_min;
for (int ii=0;ii<associated_info.size();ii++)
for (size_t ii=0;ii<associated_info.size();ii++)
{
if(associated_info[ii].d_min<min_d)
{
min_d=associated_info[ii].d_min;
min_index=ii+1;
min_index=static_cast<int>(ii)+1;
}
}
int point_index=associated_info[min_index-1].point_index;
@@ -389,14 +386,16 @@ void Track_Asso_Tas::model_filter( std::vector<Trust_Track> *t
double d2=associated_info[min_index-1].d2;
double d3=associated_info[min_index-1].d3;
associated_info[min_index-1].min_d_flag=true;
//卡尔曼滤波
double Z[3]={point_process[point_index-1].Range,point_process[point_index-1].Azimuth,point_process[point_index-1].Velocity};
double Z[3]={point_process[point_index-1].range,point_process[point_index-1].azimuth,point_process[point_index-1].velocity};
double prt = point_process[point_index-1].PRF_index;
double freq_ind = point_process[point_index-1].Freq_index;
double freq_ind = point_process[point_index-1].freq_index;
// double Z[2];
// Z[0]=point_process[point_index-1].Range*cos(point_process[point_index-1].Azimuth);
// 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 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);
@@ -413,9 +412,9 @@ void Track_Asso_Tas::model_filter( std::vector<Trust_Track> *t
for (int ii=0;ii<3;ii++)
for (int jj=0;jj<3;jj++)
{
S1_out(ii,jj)=S1[ii][jj];
S2_out(ii,jj)=S2[ii][jj];
S3_out(ii,jj)=S3[ii][jj];
S1_out(ii,jj)=static_cast<float>(S1[ii][jj]);
S2_out(ii,jj)=static_cast<float>(S2[ii][jj]);
S3_out(ii,jj)=static_cast<float>(S3[ii][jj]);
}
double det_S1=S1_out.determinant();
double Possibility1=(det_S1>1e-12)?(1.0/sqrt(pow(2*PI,3)*det_S1)*exp(-0.5*d1)):0.0;
@@ -425,7 +424,7 @@ void Track_Asso_Tas::model_filter( std::vector<Trust_Track> *t
double Possibility3=(det_S3>1e-12)?(1.0/sqrt(pow(2*PI,3)*det_S3)*exp(-0.5*d3)):0.0;
//更新航迹
for(int i=0;i<trust_track->size();i++)
for(size_t i=0;i<trust_track->size();i++){
if((*trust_track)[i].Track_Index == tas_track_idx)
{
@@ -465,24 +464,27 @@ void Track_Asso_Tas::model_filter( std::vector<Trust_Track> *t
}
//更新航迹时间
(*trust_track)[i].T_track = point_process[point_index-1].CPI_Time;
(*trust_track)[i].T_track = point_process[point_index-1].CPI_time;
(*trust_track)[i].GNSS_time = point_process[point_index-1].GNSS_time;
//更新关联上的点迹信息
(*trust_track)[i].range_point=point_process[point_index-1].Range;
(*trust_track)[i].azi_point=point_process[point_index-1].Azimuth/PI*180;
(*trust_track)[i].elev_point= asin(point_process[point_index-1].Height/point_process[point_index-1].Range)/PI*180;
(*trust_track)[i].vr_point=point_process[point_index-1].Velocity;
(*trust_track)[i].point_id=point_process[point_index-1].point_id;
(*trust_track)[i].circle_id=point_process[point_index-1].circle_id;
(*trust_track)[i].range_point=point_process[point_index-1].range;
(*trust_track)[i].azi_point=point_process[point_index-1].azimuth/PI*180;
(*trust_track)[i].elev_point= asin(point_process[point_index-1].height/point_process[point_index-1].range)/PI*180;
(*trust_track)[i].vr_point=point_process[point_index-1].velocity;
(*trust_track)[i].point_type = 1;
(*trust_track)[i].prf_point = point_process[point_index-1].PRF_index;
(*trust_track)[i].snr_point = point_process[point_index-1].snr;
(*trust_track)[i].snr_point = point_process[point_index-1].SNR;
(*trust_track)[i].Extrapolate_round = 0; //连续未用实点更新时间
(*trust_track)[i].point_flag=1; //实点
(*trust_track)[i].Amplitude= point_process[point_index-1].Amplitude; //幅度
(*trust_track)[i].Amplitude= point_process[point_index-1].amplitude; //幅度
(*trust_track)[i].associate_point_number = (*trust_track)[i].associate_point_number+1; //关联点数+1
(*trust_track)[i].RCS = point_process[point_index-1].RCS;
(*trust_track)[i].Track_Update_Flag = 1; //航迹更新标志
(*trust_track)[i].pitch_num = point_process[point_index-1].pitch_num;
(*trust_track)[i].pitch_num = point_process[point_index-1].pitch_beam_index;
std::memcpy((*trust_track)[i].speed_dim, point_process[point_index-1].speed_dim, sizeof((*trust_track)[i].speed_dim));
std::memcpy((*trust_track)[i].range_dim, point_process[point_index-1].range_dim, sizeof((*trust_track)[i].range_dim));
@@ -490,46 +492,59 @@ void Track_Asso_Tas::model_filter( std::vector<Trust_Track> *t
//更新高度
track_hight_update(tas_track_idx,point_index,trust_track);
}
}
for(auto& info : associated_info)
{
RDP_RECORD(RdpRecAssoInfo(info));
}
}
//未关联上,航迹外推
else
{
for(int i=0;i<trust_track->size();i++)
//RDP_LOG << "unassociated point for track index " << tas_track_idx << std::endl;
for(size_t i=0;i<trust_track->size();i++){
if((*trust_track)[i].Track_Index == tas_track_idx)
{
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);
if((*trust_track)[i].Extrapolate_round >= MAX_EXTRAPOLATE_NUM_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);
double X1_pred[6],X2_pred[6],X3_pred[6],P1_pred[6][6],P2_pred[6][6],P3_pred[6][6];
kalman Kalman;
Kalman.kalman_pred(F, Q1, X1, P1, X1_pred, P1_pred);
Kalman.kalman_pred(F, Q2, X2, P2, X2_pred, P2_pred);
Kalman.kalman_pred(F, Q3, X3, P3, X3_pred, P3_pred);
memcpy((*trust_track)[i].X1,X1_pred,6*sizeof(double));
memcpy((*trust_track)[i].X2,X2_pred,6*sizeof(double));
memcpy((*trust_track)[i].X3,X3_pred,6*sizeof(double));
memcpy((*trust_track)[i].P1,P1_pred,6*6*sizeof(double));
memcpy((*trust_track)[i].P2,P2_pred,6*6*sizeof(double));
memcpy((*trust_track)[i].P3,P3_pred,6*6*sizeof(double));
double X1_pred[6],X2_pred[6],X3_pred[6],P1_pred[6][6],P2_pred[6][6],P3_pred[6][6];
kalman Kalman;
Kalman.kalman_pred(F, Q1, X1, P1, X1_pred, P1_pred);
Kalman.kalman_pred(F, Q2, X2, P2, X2_pred, P2_pred);
Kalman.kalman_pred(F, Q3, X3, P3, X3_pred, P3_pred);
memcpy((*trust_track)[i].X1,X1_pred,6*sizeof(double));
memcpy((*trust_track)[i].X2,X2_pred,6*sizeof(double));
memcpy((*trust_track)[i].X3,X3_pred,6*sizeof(double));
memcpy((*trust_track)[i].P1,P1_pred,6*6*sizeof(double));
memcpy((*trust_track)[i].P2,P2_pred,6*6*sizeof(double));
memcpy((*trust_track)[i].P3,P3_pred,6*6*sizeof(double));
(*trust_track)[i].T_track = (*trust_track)[i].T_track+delta_T*1000.0; //更新航迹时间
(*trust_track)[i].GNSS_time = (*trust_track)[i].GNSS_time+delta_T*1000.0; //更新航迹时间
(*trust_track)[i].point_flag=0; //虚点
(*trust_track)[i].Extrapolate_round =(*trust_track)[i].Extrapolate_round+1;
(*trust_track)[i].T_track = static_cast<long long>((*trust_track)[i].T_track+delta_T*1000.0); //更新航迹时间
(*trust_track)[i].GNSS_time = static_cast<long long>((*trust_track)[i].GNSS_time+delta_T*1000.0); //更新航迹时间
(*trust_track)[i].point_flag=0; //虚点
(*trust_track)[i].Extrapolate_round =(*trust_track)[i].Extrapolate_round+1;
(*trust_track)[i].Track_Update_Flag = 1; //航迹更新标志
}else{
(*trust_track)[i].point_flag=0; //虚点
(*trust_track)[i].Extrapolate_round =(*trust_track)[i].Extrapolate_round+1;
(*trust_track)[i].Track_Update_Flag = 0; //航迹更新标志
}
}
}
}
}
void Track_Asso_Tas::model_output(std::vector<Trust_Track> *trust_track, int tas_track_idx )
{
for(int i=0; i<trust_track->size(); i++)
for(size_t i=0; i<trust_track->size(); i++)
if((*trust_track)[i].Track_Index==tas_track_idx)
{
@@ -737,10 +752,10 @@ void Track_Asso_Tas::track_hight_update(int tas_
std::vector<Trust_Track> *trust_track //航迹
)
{
for(int i=0; i<trust_track->size(); i++)
for(size_t i=0; i<trust_track->size(); i++)
if((*trust_track)[i].Track_Index==tas_track_idx)
{
(*trust_track)[i].Hight_smooth.push_back(point_process[asso_point_index-1].Height);
(*trust_track)[i].Hight_smooth.push_back(point_process[asso_point_index-1].height);
int height_win_length;
if((*trust_track)[i].range_point <= 1000)
@@ -761,10 +776,10 @@ void Track_Asso_Tas::track_hight_update(int tas_
}
double sum=0;
if((*trust_track)[i].Hight_smooth.size()<height_win_length)
if((*trust_track)[i].Hight_smooth.size()<static_cast<size_t>(height_win_length))
{
for (int ii=0;ii<(*trust_track)[i].Hight_smooth.size();ii++)
for (size_t ii=0;ii<(*trust_track)[i].Hight_smooth.size();ii++)
{
sum=sum+(*trust_track)[i].Hight_smooth[ii];
@@ -773,10 +788,10 @@ void Track_Asso_Tas::track_hight_update(int tas_
(*trust_track)[i].Height=sum/(*trust_track)[i].Hight_smooth.size();
}
else if((*trust_track)[i].Hight_smooth.size()>=height_win_length)
else if((*trust_track)[i].Hight_smooth.size()>=static_cast<size_t>(height_win_length))
{
int N=(*trust_track)[i].Hight_smooth.size();
int N=static_cast<int>((*trust_track)[i].Hight_smooth.size());
for (int ii=0;ii<height_win_length;ii++)
{
sum=sum+(*trust_track)[i].Hight_smooth[N-1-ii];
+1 -1
View File
@@ -1,4 +1,4 @@
#ifndef TRACK_ASSO_TAS_H
#ifndef TRACK_ASSO_TAS_H
#define TRACK_ASSO_TAS_H
#include "data_process_class_dll.h"
#include "parameters.h"
+18 -4
View File
@@ -1,4 +1,4 @@
#include "track_die.h"
#include "track_die.h"
#include "kalman.h"
#include "coor_trans.h"
#include <cmath>
@@ -14,10 +14,24 @@ void Track_Die::track_die_process( std::vector <Trust_Track> *trust_track,
std::vector <Trust_Track>::iterator Iter;
for (Iter=trust_track->begin(); Iter!=trust_track->end();)
{
if( ((*Iter).Track_Mode == 0 && (*Iter).Extrapolate_round >= TRACK_DIE_ROUND)
// ||((*Iter).Track_Mode == 1 && (*Iter).Extrapolate_round >= TRACK_DIE_ROUND_TAS)
||(*Iter).manual_delete_flag == 1)
bool die_flag = false;
if((*Iter).manual_delete_flag == 1)
{
die_flag = true;
}
if((*Iter).Track_Mode == 0 && (*Iter).primary_target_flag == 0 && (*Iter).Extrapolate_round > TRACK_DIE_ROUND)
{
die_flag = true;
}
if((*Iter).Track_Mode == 0 && (*Iter).primary_target_flag == 1 && (*Iter).Extrapolate_round > TRACK_DIE_ROUND_PRIMARY)
{
die_flag = true;
}
if(die_flag)
{
//输出消亡信息
*Track_die_num_Output=*Track_die_num_Output+1;
+1 -1
View File
@@ -1,4 +1,4 @@
#ifndef TRACK_DIE_H
#ifndef TRACK_DIE_H
#define TRACK_DIE_H
#include "data_process_class_dll.h"
#include "parameters.h"
+15 -4
View File
@@ -1,6 +1,7 @@
#include "track_die_tas.h"
#include "track_die_tas.h"
#include "kalman.h"
#include "coor_trans.h"
#include "rdp_log.h"
#include <iostream>
#include <cmath>
#include <cstring>
@@ -16,11 +17,21 @@ void Track_Die_Tas::track_die_process_tas( std::vector <Trust_Track> *trust_trac
std::vector <Trust_Track>::iterator Iter;
for (Iter=trust_track->begin(); Iter!=trust_track->end();)
{
if( ((*Iter).Track_Mode == 1 && (*Iter).Extrapolate_round >= TRACK_DIE_ROUND_TAS)
||(*Iter).manual_delete_flag == 1)
bool die_flag = false;
if((*Iter).manual_delete_flag == 1)
{
std::cout << "remove tas target :" << (*Iter).Track_Index << std::endl;
die_flag = true;
}
if((*Iter).Track_Mode == 1 && (*Iter).Extrapolate_round > TRACK_DIE_ROUND_TAS)
{
die_flag = true;
}
if(die_flag)
{
// RDP_LOG << "remove tas target :" << (*Iter).Track_Index << std::endl;
//输出消亡信息
*Track_die_num_Output=*Track_die_num_Output+1;
Track_die_Index_Output[*Track_die_num_Output-1]=(*Iter).Track_Index;
+1 -1
View File
@@ -1,4 +1,4 @@
#ifndef TRACK_DIE_TAS_H
#ifndef TRACK_DIE_TAS_H
#define TRACK_DIE_TAS_H
#include "data_process_class_dll.h"
#include "parameters.h"
@@ -1,4 +1,4 @@
#include "track_index_mangement.h"
#include "track_index_mangement.h"
#include <cmath>
#include <cstring>
#include <vector>
@@ -30,7 +30,7 @@ int Track_Ind_Mangement ::track_ind_get( std::vector<Trust_Track> *trust_track
{
//建立航迹号列表
int List[MAX_TRACK_INDEX]={0};
for (int i=0;i<trust_track->size();i++)
for (size_t i=0;i<trust_track->size();i++)
{
int ti = (*trust_track)[i].Track_Index;
if (ti >= 1 && ti <= MAX_TRACK_INDEX)
@@ -1,4 +1,4 @@
#ifndef TRACK_INDEX_MANGEMENT_H
#ifndef TRACK_INDEX_MANGEMENT_H
#define TRACK_INDEX_MANGEMENT_H
#include "data_process_class_dll.h"
#include "parameters.h"
+148 -195
View File
@@ -1,4 +1,4 @@
#include "track_init.h"
#include "track_init.h"
#include "kalman.h"
#include "coor_trans.h"
#include <cmath>
@@ -6,6 +6,8 @@
#include <vector>
#include <iostream>
#include<iomanip>
#include "rdp_record.h"
#include "rdp_record_entity.h"
using namespace std;
int Track_Init::track_init_process_logic( std::vector <PointRecv> *point_recv, //输入点迹
@@ -15,32 +17,17 @@ int Track_Init::track_init_process_logic( std::vector <PointRecv>
int *Trust_track_num_Output,
struct RadarPara Work_Parameter
)
{
// for(int i=0;i<(*point_recv).size();i++)
// {
// if((*point_recv)[i].CPI_Time == 30675948 || (*point_recv)[i].CPI_Time == 30682320
// || (*point_recv)[i].CPI_Time == 30682132 || (*point_recv)[i].CPI_Time == 30688692
// || (*point_recv)[i].CPI_Time == 30691692 || (*point_recv)[i].CPI_Time == 30691880)
// {
// std::cout << "azi=" << (*point_recv)[i].Azimuth
// << "dis=" << (*point_recv)[i].Range
// << "h=" << (*point_recv)[i].Height
// << "v=" << (*point_recv)[i].Velocity
// << "wave=" << (*point_recv)[i].beam_index
// << "time=" << (*point_recv)[i].CPI_Time;
// }
// }
//取出待起航的点迹数据
for (int i=0;i<(*point_recv).size();i++)
for (size_t i=0;i<(*point_recv).size();i++){
point_process.push_back((*point_recv)[i]);
}
//临时航迹的buff_round+1
for (int i=0;i<temp_track->size();i++)
for (size_t i=0;i<temp_track->size();i++)
{
for (int j=0;j<(*temp_track)[i].size();j++)
for (size_t j=0;j<(*temp_track)[i].size();j++)
{
(*temp_track)[i][j].buff_round = (*temp_track)[i][j].buff_round+1;
}
@@ -58,7 +45,7 @@ int Track_Init::track_init_process_logic( std::vector <PointRecv>
std::vector <PointRecv>::iterator Iter;
for (Iter=point_process.begin(); Iter!=point_process.end();)
{
if((*Iter).Use_Flag==1)
if((*Iter).use_flag==1)
{
point_process.erase(Iter);
Iter=point_process.begin();
@@ -71,34 +58,37 @@ int Track_Init::track_init_process_logic( std::vector <PointRecv>
//剩余点重新存入点迹
std::vector<PointRecv>().swap((*point_recv));
for (int i=0;i<point_process.size();i++)
for (size_t i=0;i<point_process.size();i++)
(*point_recv).push_back(point_process[i]);
//剩余点转为航迹头
for (unsigned int i=0;i<point_process.size();i++)
{
Temp_track temp_track_tmp = {};
temp_track_tmp.r = point_process[i].Range;
temp_track_tmp.azi = point_process[i].Azimuth;
temp_track_tmp.height = point_process[i].Height;
temp_track_tmp.Amp = point_process[i].Amplitude;
temp_track_tmp.vr = point_process[i].Velocity;
temp_track_tmp.snr = point_process[i].snr;
temp_track_tmp.temp_track_id = temp_track_id++;
temp_track_tmp.point_id = point_process[i].point_id;
temp_track_tmp.circle_id = point_process[i].circle_id;
temp_track_tmp.r = point_process[i].range;
temp_track_tmp.azi = point_process[i].azimuth;
temp_track_tmp.height = point_process[i].height;
temp_track_tmp.amplitude = point_process[i].amplitude;
temp_track_tmp.vr = point_process[i].velocity;
temp_track_tmp.SNR = point_process[i].SNR;
temp_track_tmp.RCS = point_process[i].RCS;
temp_track_tmp.pitch_num = point_process[i].pitch_num;
temp_track_tmp.pitch_num = point_process[i].pitch_beam_index;
std::memcpy(temp_track_tmp.speed_dim, point_process[i].speed_dim, sizeof(temp_track_tmp.speed_dim));
std::memcpy(temp_track_tmp.range_dim, point_process[i].range_dim, sizeof(temp_track_tmp.range_dim));
temp_track_tmp.asso_flag = 0;
temp_track_tmp.X[0]=point_process[i].Range*cos(point_process[i].Azimuth);
temp_track_tmp.X[0]=point_process[i].range*cos(point_process[i].azimuth);
temp_track_tmp.X[1]=0;
temp_track_tmp.X[2]=point_process[i].Range*sin(point_process[i].Azimuth);
temp_track_tmp.X[2]=point_process[i].range*sin(point_process[i].azimuth);
temp_track_tmp.X[3]=0;
temp_track_tmp.T = point_process[i].CPI_Time;
temp_track_tmp.CPI_time = point_process[i].CPI_time;
temp_track_tmp.GNSS_time = point_process[i].GNSS_time;
temp_track_tmp.buff_round = 1;
// temp_track_tmp.Temp_track_section_idx = floor(point_process[i].beam_index/BEAM_NUM_DOT_SECTION)+1;
temp_track->push_back( std::vector <Temp_track> ());
int n=temp_track->size();
int n=static_cast<int>(temp_track->size());
(*temp_track)[n-1].push_back(temp_track_tmp);
// if(point_process[i].CPI_Time == 30675948 || point_process[i].CPI_Time == 30682320)
@@ -117,6 +107,15 @@ int Track_Init::track_init_process_logic( std::vector <PointRecv>
std::vector<PointRecv>().swap(point_process);
std::vector<PointRecv>().swap((*point_recv));
#ifdef RDP_RECORD_ENABLE
//记录临时航迹
for(auto& vec_t : *temp_track){
for(auto& t : vec_t){
RDP_RECORD(RdpRecTempTrack(t));
}
}
#endif
//临时航迹满足起始长度 转为可靠航迹
tmp_track_to_trust_track(trust_track,temp_track,Trust_Track_Output, Trust_track_num_Output,Work_Parameter);
@@ -125,19 +124,6 @@ int Track_Init::track_init_process_logic( std::vector <PointRecv>
//消亡临时航迹
tmp_track_die(temp_track);
// std::cout << "-----------------------------------------------";
// for(int i=0;i<temp_track->size();i++)
// {
// for(int j=0;j<(*temp_track)[i].size();j++)
// {
// std::cout << "temp_track_azi=" << (*temp_track)[i][j].azi
// << "temp_track_dis=" << (*temp_track)[i][j].r
// << "temp_track_h=" << (*temp_track)[i][j].height
// << "temp_track_v=" << (*temp_track)[i][j].vr
// << "temp_track_time=" << (*temp_track)[i][j].T;
// }
// }
return 0;
}
@@ -155,26 +141,26 @@ void Track_Init::point_temp_track_asso(std::vector <std::vector<Temp_track>>
};
std::vector <Asso_info> asso_info;
for ( int i=0;i<point_process.size();i++)
for ( size_t i=0;i<point_process.size();i++)
{
for ( int j=0;j<(*temp_track).size();j++)
for ( size_t j=0;j<(*temp_track).size();j++)
{
int L = (*temp_track)[j].size();
int L = static_cast<int>((*temp_track)[j].size());
if( (*temp_track)[j].size() > 1 && (*temp_track)[j][L-1].buff_round >= 2)
{
//点迹信息
// double Z[2]={ point_process[i].Range*cos(point_process[i].Azimuth),point_process[i].Range*sin(point_process[i].Azimuth)} ;
double Z[3]={point_process[i].Range,
point_process[i].Azimuth,
point_process[i].Velocity};
double vr_point=point_process[i].Velocity;
double r_point=point_process[i].Range;
double Z[3]={point_process[i].range,
point_process[i].azimuth,
point_process[i].velocity};
double vr_point=point_process[i].velocity;
double r_point=point_process[i].range;
double prt = point_process[i].PRF_index;
double freq_ind = point_process[i].Freq_index;
double h_point = point_process[i].Height;
double T_point = point_process[i].CPI_Time;
double freq_ind = point_process[i].freq_index;
double h_point = point_process[i].height;
double T_point = static_cast<double>(point_process[i].CPI_time);
//航迹信息
double X[4];
@@ -184,7 +170,7 @@ void Track_Init::point_temp_track_asso(std::vector <std::vector<Temp_track>>
double v_temp_track=(*temp_track)[j][L-1].vr;
double r_temp_track=(*temp_track)[j][L-1].r;
double h_temp_track=(*temp_track)[j][L-1].height;
double T_track_head = (*temp_track)[j][L-1].T;
double T_track_head = static_cast<double>((*temp_track)[j][L-1].CPI_time);
double delta_T = (T_point - T_track_head)/1000.0;
@@ -204,29 +190,15 @@ 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(T_track_head == 33855816 && T_point == 33859004 )
// {
// std::cout << "r_point=" << r_point
// << "h_point=" << h_point
// << "T_point=" << T_point
// << "r_temp_track=" << r_temp_track
// << "v_temp_track=" << v_temp_track
// << "h_temp_track=" << h_temp_track
// << "T_track_head=" << T_track_head
// << "d=" << d
// << "alpha=" << alpha;
// }
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
{
struct Asso_info asso_info_tmp;
asso_info_tmp.point_idx = i+1;
asso_info_tmp.track_idx = j+1;
asso_info_tmp.point_idx = static_cast<int>(i)+1;
asso_info_tmp.track_idx = static_cast<int>(j)+1;
asso_info_tmp.d = d;
asso_info.push_back(asso_info_tmp);
(*temp_track)[j][L-1].asso_flag = 1;
point_process[i].Use_Flag = 1;
point_process[i].use_flag = 1;
//std::cout << "v_temp_track : " << v_temp_track << "vr_point : "<< vr_point;
}
}
@@ -235,44 +207,48 @@ void Track_Init::point_temp_track_asso(std::vector <std::vector<Temp_track>>
}
// temp_track中加入新关联上的临时航迹
for (int i = 0 ; i<asso_info.size();i++ )
for (size_t i = 0 ; i<asso_info.size();i++ )
{
(*temp_track).push_back(std::vector <Temp_track> ());
//前L个点
int L = (*temp_track)[asso_info[i].track_idx-1].size();
int L = static_cast<int>((*temp_track)[asso_info[i].track_idx-1].size());
for (int j=0;j < L;j++ )
{
(*temp_track)[(*temp_track).size()-1].push_back((*temp_track)[asso_info[i].track_idx-1][j]);
(*temp_track)[(*temp_track).size()-1][j].asso_flag = 0;
(*temp_track)[(*temp_track).size()-1][j].temp_track_id = temp_track_id;
}
//关联上的点
Temp_track asso_track_info_tmp = {};
asso_track_info_tmp.r = point_process[asso_info[i].point_idx-1].Range;
asso_track_info_tmp.azi = point_process[asso_info[i].point_idx-1].Azimuth;
asso_track_info_tmp.height = point_process[asso_info[i].point_idx-1].Height;
asso_track_info_tmp.Amp = point_process[asso_info[i].point_idx-1].Amplitude;
asso_track_info_tmp.T = point_process[asso_info[i].point_idx-1].CPI_Time;
asso_track_info_tmp.temp_track_id = temp_track_id++;
asso_track_info_tmp.point_id = point_process[asso_info[i].point_idx-1].point_id;
asso_track_info_tmp.circle_id = point_process[asso_info[i].point_idx-1].circle_id;
asso_track_info_tmp.r = point_process[asso_info[i].point_idx-1].range;
asso_track_info_tmp.azi = point_process[asso_info[i].point_idx-1].azimuth;
asso_track_info_tmp.height = point_process[asso_info[i].point_idx-1].height;
asso_track_info_tmp.amplitude = point_process[asso_info[i].point_idx-1].amplitude;
asso_track_info_tmp.CPI_time = point_process[asso_info[i].point_idx-1].CPI_time;
asso_track_info_tmp.GNSS_time = point_process[asso_info[i].point_idx-1].GNSS_time;
asso_track_info_tmp.vr = point_process[asso_info[i].point_idx-1].Velocity;
asso_track_info_tmp.snr = point_process[asso_info[i].point_idx-1].snr;
asso_track_info_tmp.vr = point_process[asso_info[i].point_idx-1].velocity;
asso_track_info_tmp.SNR = point_process[asso_info[i].point_idx-1].SNR;
asso_track_info_tmp.RCS = point_process[asso_info[i].point_idx-1].RCS;
asso_track_info_tmp.asso_flag=0;
asso_track_info_tmp.buff_round = 1;
asso_track_info_tmp.pitch_num = point_process[asso_info[i].point_idx-1].pitch_num;
asso_track_info_tmp.pitch_num = point_process[asso_info[i].point_idx-1].pitch_beam_index;
std::memcpy(asso_track_info_tmp.speed_dim, point_process[asso_info[i].point_idx-1].speed_dim, sizeof(asso_track_info_tmp.speed_dim));
std::memcpy(asso_track_info_tmp.range_dim, point_process[asso_info[i].point_idx-1].range_dim, sizeof(asso_track_info_tmp.range_dim));
asso_track_info_tmp.d = asso_info[i].d;
// asso_track_info_tmp.Temp_track_section_idx = floor(point_process[asso_info[i].point_idx-1].beam_index/BEAM_NUM_DOT_SECTION)+1;
double Z0[2]={(*temp_track)[asso_info[i].track_idx-1][L-1].r*cos((*temp_track)[asso_info[i].track_idx-1][L-1].azi),
(*temp_track)[asso_info[i].track_idx-1][L-1].r*sin((*temp_track)[asso_info[i].track_idx-1][L-1].azi)};
double Z1[2]={point_process[asso_info[i].point_idx-1].Range*cos(point_process[asso_info[i].point_idx-1].Azimuth),
point_process[asso_info[i].point_idx-1].Range*sin(point_process[asso_info[i].point_idx-1].Azimuth)};
double Z1[2]={point_process[asso_info[i].point_idx-1].range*cos(point_process[asso_info[i].point_idx-1].azimuth),
point_process[asso_info[i].point_idx-1].range*sin(point_process[asso_info[i].point_idx-1].azimuth)};
double X[4];
double P[4][4];
kalman Kalman;
double T = ( point_process[asso_info[i].point_idx-1].CPI_Time - (*temp_track)[asso_info[i].track_idx-1][L-1].T)/1000.0;
double T = ( point_process[asso_info[i].point_idx-1].CPI_time - (*temp_track)[asso_info[i].track_idx-1][L-1].CPI_time)/1000.0;
Kalman.kalman_filter_init_2dots(Z0,Z1,T,X,P);
memcpy(asso_track_info_tmp.X, X, 4*sizeof(double));
@@ -316,10 +292,10 @@ void Track_Init::point_track_head_asso( std::vector <std::vector<Temp_track>>
std::vector <Asso_info> asso_info;
//关联
for ( int i=0;i<point_process.size();i++)
for ( size_t i=0;i<point_process.size();i++)
{
for ( int j=0;j<temp_track->size();j++)
for ( size_t j=0;j<temp_track->size();j++)
{
if((*temp_track)[j].size()==1 && (*temp_track)[j][0].buff_round >= 2)
@@ -328,11 +304,11 @@ void Track_Init::point_track_head_asso( std::vector <std::vector<Temp_track>>
//点迹信息
double x_point, y_point, vr_point;
coor_trans Coor_trans;
Coor_trans.polar2cart(&x_point,&y_point,point_process[i].Range,point_process[i].Azimuth);
vr_point=point_process[i].Velocity;
double r_point=point_process[i].Range;
double h_point = point_process[i].Height;
double T_point = point_process[i].CPI_Time;
Coor_trans.polar2cart(&x_point,&y_point,point_process[i].range,point_process[i].azimuth);
vr_point=point_process[i].velocity;
double r_point=point_process[i].range;
double h_point = point_process[i].height;
double T_point = static_cast<double>(point_process[i].CPI_time);
//航迹信息
double x_track_head, y_track_head;
@@ -340,101 +316,75 @@ void Track_Init::point_track_head_asso( std::vector <std::vector<Temp_track>>
y_track_head=(*temp_track)[j][0].X[2];
double v_track_head=(*temp_track)[j][0].vr;
double h_track_head = (*temp_track)[j][0].height;
double T_track_head = (*temp_track)[j][0].T;
double T_track_head = static_cast<double>((*temp_track)[j][0].CPI_time);
// if(T_track_head == 30675948)
// {
// std::cout << "r_point=" << r_point
// << "h_point=" << h_point
// << "T_point=" << T_point
// << "x_track_head=" << x_track_head
// << "y_track_head=" << y_track_head
// << "v_track_head=" << v_track_head
// << "T_track_head=" << T_track_head;
// }
//距离差
double dis = sqrt(pow(x_point-x_track_head,2)+pow(y_point-y_track_head,2));
// if(T_point == 30682320)
// {
// std::cout <<"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!";
// std::cout << "r_point=" << r_point
// << "h_point=" << h_point
// << "T_point=" << T_point
// << "x_track_head=" << x_track_head
// << "y_track_head=" << y_track_head
// << "v_track_head=" << v_track_head
// << "T_track_head=" << T_track_head;
// }
double vmax;
if(Work_Parameter.work_mode == 0) //近程模式 最大速度减小一点
{
vmax = Work_Parameter.V_MAX;
}
else //中远程模式 最大速度正常用
{
vmax = Work_Parameter.V_MAX;
}
//距离差
double dis = sqrt(pow(x_point-x_track_head,2)+pow(y_point-y_track_head,2));
double delta_T = (T_point - T_track_head)/1000.0;
double vmax;
if(Work_Parameter.work_mode == 0) //近程模式 最大速度减小一点
{
vmax = Work_Parameter.V_MAX;
}
else //中远程模式 最大速度正常用
{
vmax = Work_Parameter.V_MAX;
}
double delta_T = (T_point - T_track_head)/1000.0;
//满足关联条件的点航
// if( ( (r_point>=1000 && dis<=vmax*delta_T && dis>=V_MIN*delta_T) || (r_point<1000 && dis<=vmax*delta_T/2.0 && dis>=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(
( (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) ))
)
&& vr_point*v_track_head>0 )
{
// if(T_track_head == 30675948)
// {
// std::cout << "r_point=" << r_point
// << "h_point=" << h_point
// << "T_point=" << T_point
// << "x_track_head=" << x_track_head
// << "y_track_head=" << y_track_head
// << "v_track_head=" << v_track_head
// << "T_track_head=" << T_track_head;
// }
// 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) ))
// )
// && vr_point*v_track_head>0 )
//点航关联条件
if( dis <= vmax* delta_T && dis >= Work_Parameter.V_MIN * delta_T && \
vr_point*v_track_head>0 && \
dis >= abs(vr_point) * delta_T && \
dis >= abs(v_track_head) * delta_T
)
{
struct Asso_info asso_info_tmp;
asso_info_tmp.point_idx = i+1;
asso_info_tmp.track_idx = j+1;
asso_info_tmp.point_idx = static_cast<int>(i)+1;
asso_info_tmp.track_idx = static_cast<int>(j)+1;
asso_info.push_back(asso_info_tmp);
(*temp_track)[j][0].asso_flag = 1;
point_process[i].Use_Flag = 1;
point_process[i].use_flag = 1;
//std::cout << "v_track_head: " << v_track_head << "vr_point: " << vr_point;
}
}
}
}
}
// temp_track中加入新关联上的临时航迹
for (int i = 0 ; i<asso_info.size();i++ )
for (size_t i = 0 ; i<asso_info.size();i++ )
{
(*temp_track).push_back(std::vector <Temp_track> ());
//第一个点
(*temp_track)[(*temp_track).size()-1].push_back((*temp_track)[asso_info[i].track_idx-1][0]);
(*temp_track)[(*temp_track).size()-1][0].asso_flag = 0;
(*temp_track)[(*temp_track).size()-1][0].temp_track_id = temp_track_id;
//第二个点
struct Temp_track asso_track_info_tmp = {};
asso_track_info_tmp.r = point_process[asso_info[i].point_idx-1].Range;
asso_track_info_tmp.azi = point_process[asso_info[i].point_idx-1].Azimuth;
asso_track_info_tmp.height = point_process[asso_info[i].point_idx-1].Height;
asso_track_info_tmp.Amp = point_process[asso_info[i].point_idx-1].Amplitude;
asso_track_info_tmp.vr = point_process[asso_info[i].point_idx-1].Velocity;
asso_track_info_tmp.snr = point_process[asso_info[i].point_idx-1].snr;
asso_track_info_tmp.temp_track_id = temp_track_id++;
asso_track_info_tmp.point_id = point_process[asso_info[i].point_idx-1].point_id;
asso_track_info_tmp.circle_id = point_process[asso_info[i].point_idx-1].circle_id;
asso_track_info_tmp.r = point_process[asso_info[i].point_idx-1].range;
asso_track_info_tmp.azi = point_process[asso_info[i].point_idx-1].azimuth;
asso_track_info_tmp.height = point_process[asso_info[i].point_idx-1].height;
asso_track_info_tmp.amplitude = point_process[asso_info[i].point_idx-1].amplitude;
asso_track_info_tmp.vr = point_process[asso_info[i].point_idx-1].velocity;
asso_track_info_tmp.SNR = point_process[asso_info[i].point_idx-1].SNR;
asso_track_info_tmp.RCS = point_process[asso_info[i].point_idx-1].RCS;
asso_track_info_tmp.T = point_process[asso_info[i].point_idx-1].CPI_Time;
asso_track_info_tmp.CPI_time = point_process[asso_info[i].point_idx-1].CPI_time;
asso_track_info_tmp.GNSS_time = point_process[asso_info[i].point_idx-1].GNSS_time;
asso_track_info_tmp.pitch_num = point_process[asso_info[i].point_idx-1].pitch_num;
asso_track_info_tmp.pitch_num = point_process[asso_info[i].point_idx-1].pitch_beam_index;
std::memcpy(asso_track_info_tmp.speed_dim, point_process[asso_info[i].point_idx-1].speed_dim, sizeof(asso_track_info_tmp.speed_dim));
std::memcpy(asso_track_info_tmp.range_dim, point_process[asso_info[i].point_idx-1].range_dim, sizeof(asso_track_info_tmp.range_dim));
asso_track_info_tmp.asso_flag = 0;
@@ -442,12 +392,12 @@ void Track_Init::point_track_head_asso( std::vector <std::vector<Temp_track>>
// asso_track_info_tmp.Temp_track_section_idx = floor(point_process[asso_info[i].point_idx-1].beam_index/BEAM_NUM_DOT_SECTION)+1;;
double Z0[2]={(*temp_track)[asso_info[i].track_idx-1][0].r*cos((*temp_track)[asso_info[i].track_idx-1][0].azi),
(*temp_track)[asso_info[i].track_idx-1][0].r*sin((*temp_track)[asso_info[i].track_idx-1][0].azi)};
double Z1[2]={point_process[asso_info[i].point_idx-1].Range*cos(point_process[asso_info[i].point_idx-1].Azimuth),
point_process[asso_info[i].point_idx-1].Range*sin(point_process[asso_info[i].point_idx-1].Azimuth)};
double Z1[2]={point_process[asso_info[i].point_idx-1].range*cos(point_process[asso_info[i].point_idx-1].azimuth),
point_process[asso_info[i].point_idx-1].range*sin(point_process[asso_info[i].point_idx-1].azimuth)};
double X[4];
double P[4][4];
kalman Kalman;
double T = (point_process[asso_info[i].point_idx-1].CPI_Time - (*temp_track)[asso_info[i].track_idx-1][0].T)/1000.0;
double T = (point_process[asso_info[i].point_idx-1].CPI_time - (*temp_track)[asso_info[i].track_idx-1][0].CPI_time)/1000.0;
Kalman.kalman_filter_init_2dots(Z0,Z1,T,X,P);
memcpy(asso_track_info_tmp.X, X, 4*sizeof(double));
@@ -498,7 +448,7 @@ void Track_Init::tmp_track_to_trust_track(std::vector<Trust_Track> *trust_tra
std::vector <std::vector<Temp_track>>::iterator Iter;
for (Iter=temp_track->begin(); Iter!=temp_track->end();)
{
int L=(*Iter).size();
int L=static_cast<int>((*Iter).size());
double range=(*Iter)[L-1].r;
double azi=(*Iter)[L-1].azi/PI*180;
@@ -544,9 +494,9 @@ void Track_Init::tmp_track_to_trust_track(std::vector<Trust_Track> *trust_tra
if (Track_to_start[i].size() < 3 || Track_to_start[j].size() < 3)
continue;
if( (Track_to_start[i][0].T == Track_to_start[j][0].T && Track_to_start[i][0].r == Track_to_start[j][0].r)
|| (Track_to_start[i][1].T == Track_to_start[j][1].T && Track_to_start[i][1].r == Track_to_start[j][1].r)
|| (Track_to_start[i][2].T == Track_to_start[j][2].T && Track_to_start[i][2].r == Track_to_start[j][2].r)
if( (Track_to_start[i][0].CPI_time == Track_to_start[j][0].CPI_time && Track_to_start[i][0].r == Track_to_start[j][0].r)
|| (Track_to_start[i][1].CPI_time == Track_to_start[j][1].CPI_time && Track_to_start[i][1].r == Track_to_start[j][1].r)
|| (Track_to_start[i][2].CPI_time == Track_to_start[j][2].CPI_time && Track_to_start[i][2].r == Track_to_start[j][2].r)
)
{
if(Track_to_start[i][L-1].d<Track_to_start[j][L-1].d)
@@ -595,22 +545,25 @@ void Track_Init::tmp_track_to_trust_track(std::vector<Trust_Track> *trust_tra
double Z2[2]={(Track_to_start[i][L-1].r)*cos(Track_to_start[i][L-1].azi),(Track_to_start[i][L-1].r)*sin(Track_to_start[i][L-1].azi) };
double X[6];
double P[6][6];
double T1=(Track_to_start[i][L-2].T-Track_to_start[i][L-3].T)/1000.0;
double T2=(Track_to_start[i][L-1].T-Track_to_start[i][L-2].T)/1000.0;
double T1=(Track_to_start[i][L-2].CPI_time-Track_to_start[i][L-3].CPI_time)/1000.0;
double T2=(Track_to_start[i][L-1].CPI_time-Track_to_start[i][L-2].CPI_time)/1000.0;
kalman Kalman;
Kalman.kalman_filter_init_3dots(Z0, Z1, Z2, T1,T2, X ,P);
Trust_Track trust_track_tmp = {};
memcpy(trust_track_tmp.X, X, 6*sizeof(double));
memcpy(trust_track_tmp.P, P, 6*6*sizeof(double));
trust_track_tmp.Amplitude=Track_to_start[i][L-1].Amp;
trust_track_tmp.snr_point = Track_to_start[i][L-1].snr;
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;
trust_track_tmp.Amplitude=Track_to_start[i][L-1].amplitude;
trust_track_tmp.snr_point = Track_to_start[i][L-1].SNR;
trust_track_tmp.RCS = Track_to_start[i][L-1].RCS;
trust_track_tmp.pitch_num = Track_to_start[i][L-1].pitch_num;
std::memcpy(trust_track_tmp.speed_dim, Track_to_start[i][L-1].speed_dim, sizeof(trust_track_tmp.speed_dim));
std::memcpy(trust_track_tmp.range_dim, Track_to_start[i][L-1].range_dim, sizeof(trust_track_tmp.range_dim));
trust_track_tmp.T_track = Track_to_start[i][L-1].T;
trust_track_tmp.T_track = Track_to_start[i][L-1].CPI_time;
trust_track_tmp.GNSS_time = Track_to_start[i][L-1].GNSS_time;
trust_track_tmp.point_flag=1; //实点
trust_track_tmp.Track_Mode=0; //跟踪模式 TWS 0
@@ -646,19 +599,19 @@ void Track_Init::tmp_track_to_trust_track(std::vector<Trust_Track> *trust_tra
Trust_Track_Output[*Trust_track_num_Output-1][j].Track_Index=index;
Trust_Track_Output[*Trust_track_num_Output-1][j].Point_Sum=L;
Trust_Track_Output[*Trust_track_num_Output-1][j].Range=Track_to_start[i][j].r;
Trust_Track_Output[*Trust_track_num_Output-1][j].Azimuth=Track_to_start[i][j].azi/PI*180;
Trust_Track_Output[*Trust_track_num_Output-1][j].Range=static_cast<float>(Track_to_start[i][j].r);
Trust_Track_Output[*Trust_track_num_Output-1][j].Azimuth=static_cast<float>(Track_to_start[i][j].azi/PI*180);
{
double elev_ratio = (Track_to_start[i][j].r > 0.0) ? Track_to_start[i][j].height / Track_to_start[i][j].r : 0.0;
if (elev_ratio > 1.0) elev_ratio = 1.0;
if (elev_ratio < -1.0) elev_ratio = -1.0;
Trust_Track_Output[*Trust_track_num_Output-1][j].Elevation=asin(elev_ratio)/PI*180;
Trust_Track_Output[*Trust_track_num_Output-1][j].Elevation=static_cast<float>(asin(elev_ratio)/PI*180);
}
Trust_Track_Output[*Trust_track_num_Output-1][j].Range_V=sqrt(pow(Track_to_start[i][j].X[1],2)+pow(Track_to_start[i][j].X[3],2));
Trust_Track_Output[*Trust_track_num_Output-1][j].z=Track_to_start[i][j].height;
Trust_Track_Output[*Trust_track_num_Output-1][j].Amplitude=Track_to_start[i][j].Amp;
Trust_Track_Output[*Trust_track_num_Output-1][j].track_snr = Track_to_start[i][j].snr;
Trust_Track_Output[*Trust_track_num_Output-1][j].track_rcs = Track_to_start[i][j].RCS;
Trust_Track_Output[*Trust_track_num_Output-1][j].Range_V=static_cast<float>(sqrt(pow(Track_to_start[i][j].X[1],2)+pow(Track_to_start[i][j].X[3],2)));
Trust_Track_Output[*Trust_track_num_Output-1][j].z=static_cast<float>(Track_to_start[i][j].height);
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].pitch_num = Track_to_start[i][j].pitch_num;
@@ -668,25 +621,25 @@ void Track_Init::tmp_track_to_trust_track(std::vector<Trust_Track> *trust_tra
Direction_Angle=atan2(Track_to_start[i][j].X[3],Track_to_start[i][j].X[1]);
if(Direction_Angle<0)
Direction_Angle=Direction_Angle+2*PI;
Trust_Track_Output[*Trust_track_num_Output-1][j].Direction_Angle=Direction_Angle/PI*180;
Trust_Track_Output[*Trust_track_num_Output-1][j].Direction_Angle=static_cast<float>(Direction_Angle/PI*180);
Trust_Track_Output[*Trust_track_num_Output-1][j].track_time=(Track_to_start[i][j].T)/1000.0;
Trust_Track_Output[*Trust_track_num_Output-1][j].track_time=static_cast<float>((Track_to_start[i][j].CPI_time)/1000.0);
Trust_Track_Output[*Trust_track_num_Output-1][j].GNSS_time=Track_to_start[i][j].GNSS_time;
Trust_Track_Output[*Trust_track_num_Output-1][j].x=Track_to_start[i][j].X[0];
Trust_Track_Output[*Trust_track_num_Output-1][j].v_x=Track_to_start[i][j].X[1];
Trust_Track_Output[*Trust_track_num_Output-1][j].y=Track_to_start[i][j].X[2];
Trust_Track_Output[*Trust_track_num_Output-1][j].v_y=Track_to_start[i][j].X[3];
Trust_Track_Output[*Trust_track_num_Output-1][j].x=static_cast<float>(Track_to_start[i][j].X[0]);
Trust_Track_Output[*Trust_track_num_Output-1][j].v_x=static_cast<float>(Track_to_start[i][j].X[1]);
Trust_Track_Output[*Trust_track_num_Output-1][j].y=static_cast<float>(Track_to_start[i][j].X[2]);
Trust_Track_Output[*Trust_track_num_Output-1][j].v_y=static_cast<float>(Track_to_start[i][j].X[3]);
Trust_Track_Output[*Trust_track_num_Output-1][j].range_point=Track_to_start[i][j].r;
Trust_Track_Output[*Trust_track_num_Output-1][j].azi_point=Track_to_start[i][j].azi/PI*180;
Trust_Track_Output[*Trust_track_num_Output-1][j].range_point=static_cast<float>(Track_to_start[i][j].r);
Trust_Track_Output[*Trust_track_num_Output-1][j].azi_point=static_cast<float>(Track_to_start[i][j].azi/PI*180);
{
double elev_ratio = (Track_to_start[i][j].r > 0.0) ? Track_to_start[i][j].height / Track_to_start[i][j].r : 0.0;
if (elev_ratio > 1.0) elev_ratio = 1.0;
if (elev_ratio < -1.0) elev_ratio = -1.0;
Trust_Track_Output[*Trust_track_num_Output-1][j].elev_point=asin(elev_ratio)/PI*180;
Trust_Track_Output[*Trust_track_num_Output-1][j].elev_point=static_cast<float>(asin(elev_ratio)/PI*180);
}
Trust_Track_Output[*Trust_track_num_Output-1][j].vr_point=Track_to_start[i][j].vr;
Trust_Track_Output[*Trust_track_num_Output-1][j].vr_point=static_cast<float>(Track_to_start[i][j].vr);
Trust_Track_Output[*Trust_track_num_Output-1][j].point_type=0;
Trust_Track_Output[*Trust_track_num_Output-1][j].Flag_Point=1;
Trust_Track_Output[*Trust_track_num_Output-1][j].Track_Mode=0;//跟踪模式 TWS 0
@@ -706,7 +659,7 @@ void Track_Init::tmp_track_die(std::vector <std::vector<Temp_track>> *temp_t
std::vector <std::vector<Temp_track>>::iterator Iter;
for (Iter=temp_track->begin(); Iter!=temp_track->end();)
{
int n=(*Iter).size();
int n=static_cast<int>((*Iter).size());
if (n <= 0)
{
temp_track->erase(Iter);
+3 -1
View File
@@ -1,4 +1,4 @@
#ifndef TRACK_INIT_H
#ifndef TRACK_INIT_H
#define TRACK_INIT_H
#include "data_process_class_dll.h"
#include "track_index_mangement.h"
@@ -27,6 +27,8 @@ public:
private:
long long temp_track_id = 0; //临时航迹id
std::vector<PointRecv> point_process; //要处理的点迹
void point_temp_track_asso(std::vector <std::vector<Temp_track>> *temp_track,
@@ -1,4 +1,4 @@
#include "track_init_direct_tracking.h"
#include "track_init_direct_tracking.h"
#include "kalman.h"
#include "coor_trans.h"
#include <cmath>
@@ -33,26 +33,26 @@ void Track_Init_Direct_Tracking::point_temp_track_asso(std::vector <std::vector<
};
std::vector <Asso_info> asso_info;
for ( int i=0;i<point_process.size();i++)
for ( size_t i=0;i<point_process.size();i++)
{
for ( int j=0;j<(*temp_track).size();j++)
for ( size_t j=0;j<(*temp_track).size();j++)
{
int L = (*temp_track)[j].size();
int L = static_cast<int>((*temp_track)[j].size());
if( (*temp_track)[j].size() > 1 && (*temp_track)[j][L-1].buff_round >= 2)
{
//点迹信息
// double Z[2]={ point_process[i].Range*cos(point_process[i].Azimuth),point_process[i].Range*sin(point_process[i].Azimuth)} ;
double Z[3]={point_process[i].Range,
point_process[i].Azimuth,
point_process[i].Velocity};
double vr_point=point_process[i].Velocity;
double r_point=point_process[i].Range;
double Z[3]={point_process[i].range,
point_process[i].azimuth,
point_process[i].velocity};
double vr_point=point_process[i].velocity;
double r_point=point_process[i].range;
double prt = point_process[i].PRF_index;
double freq_ind = point_process[i].Freq_index;
double h_point = point_process[i].Height;
double T_point = point_process[i].CPI_Time;
double freq_ind = point_process[i].freq_index;
double h_point = point_process[i].height;
double T_point = static_cast<double>(point_process[i].CPI_time);
//航迹信息
double X[4];
@@ -62,7 +62,7 @@ void Track_Init_Direct_Tracking::point_temp_track_asso(std::vector <std::vector<
double v_temp_track=(*temp_track)[j][L-1].vr;
double r_temp_track=(*temp_track)[j][L-1].r;
double h_temp_track=(*temp_track)[j][L-1].height;
double T_track_head = (*temp_track)[j][L-1].T;
double T_track_head = static_cast<double>((*temp_track)[j][L-1].CPI_time);
double delta_T = (T_point - T_track_head)/1000.0;
@@ -85,11 +85,11 @@ void Track_Init_Direct_Tracking::point_temp_track_asso(std::vector <std::vector<
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
{
struct Asso_info asso_info_tmp;
asso_info_tmp.point_idx = i+1;
asso_info_tmp.track_idx = j+1;
asso_info_tmp.point_idx = static_cast<int>(i)+1;
asso_info_tmp.track_idx = static_cast<int>(j)+1;
asso_info.push_back(asso_info_tmp);
(*temp_track)[j][L-1].asso_flag = 1;
point_process[i].Use_Flag = 1;
point_process[i].use_flag = 1;
}
}
}
@@ -97,12 +97,12 @@ void Track_Init_Direct_Tracking::point_temp_track_asso(std::vector <std::vector<
}
// temp_track中加入新关联上的临时航迹
for (int i = 0 ; i<asso_info.size();i++ )
for (size_t i = 0 ; i<asso_info.size();i++ )
{
(*temp_track).push_back(std::vector <Temp_track> ());
//前L个点
int L = (*temp_track)[asso_info[i].track_idx-1].size();
int L = static_cast<int>((*temp_track)[asso_info[i].track_idx-1].size());
for (int j=0;j < L;j++ )
{
(*temp_track)[(*temp_track).size()-1].push_back((*temp_track)[asso_info[i].track_idx-1][j]);
@@ -111,15 +111,15 @@ void Track_Init_Direct_Tracking::point_temp_track_asso(std::vector <std::vector<
//关联上的点
Temp_track asso_track_info_tmp = {};
asso_track_info_tmp.r = point_process[asso_info[i].point_idx-1].Range;
asso_track_info_tmp.azi = point_process[asso_info[i].point_idx-1].Azimuth;
asso_track_info_tmp.height = point_process[asso_info[i].point_idx-1].Height;
asso_track_info_tmp.Amp = point_process[asso_info[i].point_idx-1].Amplitude;
asso_track_info_tmp.T = point_process[asso_info[i].point_idx-1].CPI_Time;
asso_track_info_tmp.vr = point_process[asso_info[i].point_idx-1].Velocity;
asso_track_info_tmp.snr = point_process[asso_info[i].point_idx-1].snr;
asso_track_info_tmp.r = point_process[asso_info[i].point_idx-1].range;
asso_track_info_tmp.azi = point_process[asso_info[i].point_idx-1].azimuth;
asso_track_info_tmp.height = point_process[asso_info[i].point_idx-1].height;
asso_track_info_tmp.amplitude = point_process[asso_info[i].point_idx-1].amplitude;
asso_track_info_tmp.CPI_time = point_process[asso_info[i].point_idx-1].CPI_time;
asso_track_info_tmp.vr = point_process[asso_info[i].point_idx-1].velocity;
asso_track_info_tmp.SNR = point_process[asso_info[i].point_idx-1].SNR;
asso_track_info_tmp.RCS = point_process[asso_info[i].point_idx-1].RCS;
asso_track_info_tmp.pitch_num = point_process[asso_info[i].point_idx-1].pitch_num;
asso_track_info_tmp.pitch_num = point_process[asso_info[i].point_idx-1].pitch_beam_index;
std::memcpy(asso_track_info_tmp.speed_dim, point_process[asso_info[i].point_idx-1].speed_dim, sizeof(asso_track_info_tmp.speed_dim));
std::memcpy(asso_track_info_tmp.range_dim, point_process[asso_info[i].point_idx-1].range_dim, sizeof(asso_track_info_tmp.range_dim));
asso_track_info_tmp.asso_flag=0;
@@ -127,12 +127,12 @@ void Track_Init_Direct_Tracking::point_temp_track_asso(std::vector <std::vector<
// asso_track_info_tmp.Temp_track_section_idx = floor(point_process[asso_info[i].point_idx-1].beam_index/BEAM_NUM_DOT_SECTION)+1;
double Z0[2]={(*temp_track)[asso_info[i].track_idx-1][L-1].r*cos((*temp_track)[asso_info[i].track_idx-1][L-1].azi),
(*temp_track)[asso_info[i].track_idx-1][L-1].r*sin((*temp_track)[asso_info[i].track_idx-1][L-1].azi)};
double Z1[2]={point_process[asso_info[i].point_idx-1].Range*cos(point_process[asso_info[i].point_idx-1].Azimuth),
point_process[asso_info[i].point_idx-1].Range*sin(point_process[asso_info[i].point_idx-1].Azimuth)};
double Z1[2]={point_process[asso_info[i].point_idx-1].range*cos(point_process[asso_info[i].point_idx-1].azimuth),
point_process[asso_info[i].point_idx-1].range*sin(point_process[asso_info[i].point_idx-1].azimuth)};
double X[4];
double P[4][4];
kalman Kalman;
double T = ( point_process[asso_info[i].point_idx-1].CPI_Time - (*temp_track)[asso_info[i].track_idx-1][L-1].T)/1000.0;
double T = ( point_process[asso_info[i].point_idx-1].CPI_time - (*temp_track)[asso_info[i].track_idx-1][L-1].CPI_time)/1000.0;
Kalman.kalman_filter_init_2dots(Z0,Z1,T,X,P);
memcpy(asso_track_info_tmp.X, X, 4*sizeof(double));
@@ -175,10 +175,10 @@ void Track_Init_Direct_Tracking::point_track_head_asso( std::vector <std::vector
std::vector <Asso_info> asso_info;
//关联
for ( int i=0;i<point_process.size();i++)
for ( size_t i=0;i<point_process.size();i++)
{
for ( int j=0;j<temp_track->size();j++)
for ( size_t j=0;j<temp_track->size();j++)
{
if((*temp_track)[j].size()==1 && (*temp_track)[j][0].buff_round >= 2)
@@ -187,11 +187,11 @@ void Track_Init_Direct_Tracking::point_track_head_asso( std::vector <std::vector
//点迹信息
double x_point, y_point, vr_point;
coor_trans Coor_trans;
Coor_trans.polar2cart(&x_point,&y_point,point_process[i].Range,point_process[i].Azimuth);
vr_point=point_process[i].Velocity;
double r_point=point_process[i].Range;
double h_point = point_process[i].Height;
double T_point = point_process[i].CPI_Time;
Coor_trans.polar2cart(&x_point,&y_point,point_process[i].range,point_process[i].azimuth);
vr_point=point_process[i].velocity;
double r_point=point_process[i].range;
double h_point = point_process[i].height;
double T_point = static_cast<double>(point_process[i].CPI_time);
//航迹信息
double x_track_head, y_track_head;
@@ -199,7 +199,7 @@ void Track_Init_Direct_Tracking::point_track_head_asso( std::vector <std::vector
y_track_head=(*temp_track)[j][0].X[2];
double v_track_head=(*temp_track)[j][0].vr;
double h_track_head = (*temp_track)[j][0].height;
double T_track_head = (*temp_track)[j][0].T;
double T_track_head = static_cast<double>((*temp_track)[j][0].CPI_time);
//距离差
double dis = sqrt(pow(x_point-x_track_head,2)+pow(y_point-y_track_head,2));
@@ -225,11 +225,11 @@ void Track_Init_Direct_Tracking::point_track_head_asso( std::vector <std::vector
&& 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
{
struct Asso_info asso_info_tmp;
asso_info_tmp.point_idx = i+1;
asso_info_tmp.track_idx = j+1;
asso_info_tmp.point_idx = static_cast<int>(i)+1;
asso_info_tmp.track_idx = static_cast<int>(j)+1;
asso_info.push_back(asso_info_tmp);
(*temp_track)[j][0].asso_flag = 1;
point_process[i].Use_Flag = 1;
point_process[i].use_flag = 1;
}
}
@@ -237,7 +237,7 @@ void Track_Init_Direct_Tracking::point_track_head_asso( std::vector <std::vector
}
// temp_track中加入新关联上的临时航迹
for (int i = 0 ; i<asso_info.size();i++ )
for (size_t i = 0 ; i<asso_info.size();i++ )
{
(*temp_track).push_back(std::vector <Temp_track> ());
@@ -247,15 +247,15 @@ void Track_Init_Direct_Tracking::point_track_head_asso( std::vector <std::vector
//第二个点
struct Temp_track asso_track_info_tmp = {};
asso_track_info_tmp.r = point_process[asso_info[i].point_idx-1].Range;
asso_track_info_tmp.azi = point_process[asso_info[i].point_idx-1].Azimuth;
asso_track_info_tmp.height = point_process[asso_info[i].point_idx-1].Height;
asso_track_info_tmp.Amp = point_process[asso_info[i].point_idx-1].Amplitude;
asso_track_info_tmp.vr = point_process[asso_info[i].point_idx-1].Velocity;
asso_track_info_tmp.snr = point_process[asso_info[i].point_idx-1].snr;
asso_track_info_tmp.r = point_process[asso_info[i].point_idx-1].range;
asso_track_info_tmp.azi = point_process[asso_info[i].point_idx-1].azimuth;
asso_track_info_tmp.height = point_process[asso_info[i].point_idx-1].height;
asso_track_info_tmp.amplitude = point_process[asso_info[i].point_idx-1].amplitude;
asso_track_info_tmp.vr = point_process[asso_info[i].point_idx-1].velocity;
asso_track_info_tmp.SNR = point_process[asso_info[i].point_idx-1].SNR;
asso_track_info_tmp.RCS = point_process[asso_info[i].point_idx-1].RCS;
asso_track_info_tmp.T = point_process[asso_info[i].point_idx-1].CPI_Time;
asso_track_info_tmp.pitch_num = point_process[asso_info[i].point_idx-1].pitch_num;
asso_track_info_tmp.CPI_time = point_process[asso_info[i].point_idx-1].CPI_time;
asso_track_info_tmp.pitch_num = point_process[asso_info[i].point_idx-1].pitch_beam_index;
std::memcpy(asso_track_info_tmp.speed_dim, point_process[asso_info[i].point_idx-1].speed_dim, sizeof(asso_track_info_tmp.speed_dim));
std::memcpy(asso_track_info_tmp.range_dim, point_process[asso_info[i].point_idx-1].range_dim, sizeof(asso_track_info_tmp.range_dim));
asso_track_info_tmp.asso_flag = 0;
@@ -263,12 +263,12 @@ void Track_Init_Direct_Tracking::point_track_head_asso( std::vector <std::vector
// asso_track_info_tmp.Temp_track_section_idx = floor(point_process[asso_info[i].point_idx-1].beam_index/BEAM_NUM_DOT_SECTION)+1;;
double Z0[2]={(*temp_track)[asso_info[i].track_idx-1][0].r*cos((*temp_track)[asso_info[i].track_idx-1][0].azi),
(*temp_track)[asso_info[i].track_idx-1][0].r*sin((*temp_track)[asso_info[i].track_idx-1][0].azi)};
double Z1[2]={point_process[asso_info[i].point_idx-1].Range*cos(point_process[asso_info[i].point_idx-1].Azimuth),
point_process[asso_info[i].point_idx-1].Range*sin(point_process[asso_info[i].point_idx-1].Azimuth)};
double Z1[2]={point_process[asso_info[i].point_idx-1].range*cos(point_process[asso_info[i].point_idx-1].azimuth),
point_process[asso_info[i].point_idx-1].range*sin(point_process[asso_info[i].point_idx-1].azimuth)};
double X[4];
double P[4][4];
kalman Kalman;
double T = (point_process[asso_info[i].point_idx-1].CPI_Time - (*temp_track)[asso_info[i].track_idx-1][0].T)/1000.0;
double T = (point_process[asso_info[i].point_idx-1].CPI_time - (*temp_track)[asso_info[i].track_idx-1][0].CPI_time)/1000.0;
Kalman.kalman_filter_init_2dots(Z0,Z1,T,X,P);
memcpy(asso_track_info_tmp.X, X, 4*sizeof(double));
@@ -306,7 +306,7 @@ void Track_Init_Direct_Tracking::tmp_track_to_trust_track( std::vector<Trust_Tr
std::vector <std::vector<Temp_track>>::iterator Iter;
for (Iter=temp_track->begin(); Iter!=temp_track->end();)
{
int L=(*Iter).size();
int L=static_cast<int>((*Iter).size());
if (L < 3)
{
++Iter;
@@ -323,8 +323,8 @@ void Track_Init_Direct_Tracking::tmp_track_to_trust_track( std::vector<Trust_Tr
double Z2[2]={((*Iter)[L-1].r)*cos((*Iter)[L-1].azi),((*Iter)[L-1].r)*sin((*Iter)[L-1].azi) };
double X[6];
double P[6][6];
double T1=((*Iter)[L-2].T-(*Iter)[L-3].T)/1000.0;
double T2=((*Iter)[L-1].T-(*Iter)[L-2].T)/1000.0;
double T1=((*Iter)[L-2].CPI_time-(*Iter)[L-3].CPI_time)/1000.0;
double T2=((*Iter)[L-1].CPI_time-(*Iter)[L-2].CPI_time)/1000.0;
kalman Kalman;
Kalman.kalman_filter_init_3dots(Z0, Z1, Z2, T1,T2, X ,P);
Trust_Track trust_track_tmp = {};
@@ -332,10 +332,10 @@ void Track_Init_Direct_Tracking::tmp_track_to_trust_track( std::vector<Trust_Tr
memcpy(trust_track_tmp.P, P, 6*6*sizeof(double));
trust_track_tmp.Track_Index = track_ID;
trust_track_tmp.Amplitude=(*Iter)[L-1].Amp;
trust_track_tmp.snr_point = (*Iter)[L-1].snr;
trust_track_tmp.Amplitude=(*Iter)[L-1].amplitude;
trust_track_tmp.snr_point = (*Iter)[L-1].SNR;
trust_track_tmp.RCS = (*Iter)[L-1].RCS;
trust_track_tmp.T_track = (*Iter)[L-1].T;
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;
@@ -370,29 +370,29 @@ void Track_Init_Direct_Tracking::tmp_track_to_trust_track( std::vector<Trust_Tr
Trust_Track_Output[*Trust_track_num_Output-1][j].Track_Index=track_ID;
Trust_Track_Output[*Trust_track_num_Output-1][j].Point_Sum=L;
Trust_Track_Output[*Trust_track_num_Output-1][j].Range=(*Iter)[j].r;
Trust_Track_Output[*Trust_track_num_Output-1][j].Azimuth=(*Iter)[j].azi/PI*180;
Trust_Track_Output[*Trust_track_num_Output-1][j].Elevation=asin((*Iter)[j].height/(*Iter)[j].r)/PI*180;
Trust_Track_Output[*Trust_track_num_Output-1][j].Range_V=sqrt(pow((*Iter)[j].X[1],2)+pow((*Iter)[j].X[3],2));
Trust_Track_Output[*Trust_track_num_Output-1][j].z=(*Iter)[j].height;
Trust_Track_Output[*Trust_track_num_Output-1][j].Amplitude=(*Iter)[j].Amp;
Trust_Track_Output[*Trust_track_num_Output-1][j].track_snr = (*Iter)[j].snr;
Trust_Track_Output[*Trust_track_num_Output-1][j].track_rcs = (*Iter)[j].RCS;
Trust_Track_Output[*Trust_track_num_Output-1][j].Range=static_cast<float>((*Iter)[j].r);
Trust_Track_Output[*Trust_track_num_Output-1][j].Azimuth=static_cast<float>((*Iter)[j].azi/PI*180);
Trust_Track_Output[*Trust_track_num_Output-1][j].Elevation=static_cast<float>(asin((*Iter)[j].height/(*Iter)[j].r)/PI*180);
Trust_Track_Output[*Trust_track_num_Output-1][j].Range_V=static_cast<float>(sqrt(pow((*Iter)[j].X[1],2)+pow((*Iter)[j].X[3],2)));
Trust_Track_Output[*Trust_track_num_Output-1][j].z=static_cast<float>((*Iter)[j].height);
Trust_Track_Output[*Trust_track_num_Output-1][j].Amplitude=static_cast<float>((*Iter)[j].amplitude);
Trust_Track_Output[*Trust_track_num_Output-1][j].track_snr = static_cast<float>((*Iter)[j].SNR);
Trust_Track_Output[*Trust_track_num_Output-1][j].track_rcs = static_cast<float>((*Iter)[j].RCS);
double Direction_Angle;
Direction_Angle=atan2((*Iter)[j].X[3],(*Iter)[j].X[1]);
if(Direction_Angle<0)
Direction_Angle=Direction_Angle+2*PI;
Trust_Track_Output[*Trust_track_num_Output-1][j].Direction_Angle=Direction_Angle/PI*180;
Trust_Track_Output[*Trust_track_num_Output-1][j].Direction_Angle=static_cast<float>(Direction_Angle/PI*180);
Trust_Track_Output[*Trust_track_num_Output-1][j].track_time=((*Iter)[j].T)/1000.0;
Trust_Track_Output[*Trust_track_num_Output-1][j].x=(*Iter)[j].X[0];
Trust_Track_Output[*Trust_track_num_Output-1][j].v_x=(*Iter)[j].X[1];
Trust_Track_Output[*Trust_track_num_Output-1][j].y=(*Iter)[j].X[2];
Trust_Track_Output[*Trust_track_num_Output-1][j].v_y=(*Iter)[j].X[3];
Trust_Track_Output[*Trust_track_num_Output-1][j].range_point=(*Iter)[j].r;
Trust_Track_Output[*Trust_track_num_Output-1][j].azi_point=(*Iter)[j].azi/PI*180;
Trust_Track_Output[*Trust_track_num_Output-1][j].elev_point=asin((*Iter)[j].height/(*Iter)[j].r)/PI*180;
Trust_Track_Output[*Trust_track_num_Output-1][j].vr_point=(*Iter)[j].vr;
Trust_Track_Output[*Trust_track_num_Output-1][j].track_time=static_cast<float>(((*Iter)[j].CPI_time)/1000.0);
Trust_Track_Output[*Trust_track_num_Output-1][j].x=static_cast<float>((*Iter)[j].X[0]);
Trust_Track_Output[*Trust_track_num_Output-1][j].v_x=static_cast<float>((*Iter)[j].X[1]);
Trust_Track_Output[*Trust_track_num_Output-1][j].y=static_cast<float>((*Iter)[j].X[2]);
Trust_Track_Output[*Trust_track_num_Output-1][j].v_y=static_cast<float>((*Iter)[j].X[3]);
Trust_Track_Output[*Trust_track_num_Output-1][j].range_point=static_cast<float>((*Iter)[j].r);
Trust_Track_Output[*Trust_track_num_Output-1][j].azi_point=static_cast<float>((*Iter)[j].azi/PI*180);
Trust_Track_Output[*Trust_track_num_Output-1][j].elev_point=static_cast<float>(asin((*Iter)[j].height/(*Iter)[j].r)/PI*180);
Trust_Track_Output[*Trust_track_num_Output-1][j].vr_point=static_cast<float>((*Iter)[j].vr);
Trust_Track_Output[*Trust_track_num_Output-1][j].point_type=0;
Trust_Track_Output[*Trust_track_num_Output-1][j].Flag_Point=1;
Trust_Track_Output[*Trust_track_num_Output-1][j].Track_Mode=1;//跟踪模式 TWS 0
@@ -419,7 +419,7 @@ void Track_Init_Direct_Tracking::tmp_track_die(std::vector <std::vector<Temp_tra
std::vector <std::vector<Temp_track>>::iterator Iter;
for (Iter=temp_track->begin(); Iter!=temp_track->end();)
{
int n=(*Iter).size();
int n=static_cast<int>((*Iter).size());
if (n <= 0)
{
temp_track->erase(Iter);
@@ -1,4 +1,4 @@
#ifndef TRACK_INIT_DIRECT_TRACKING_H
#ifndef TRACK_INIT_DIRECT_TRACKING_H
#define TRACK_INIT_DIRECT_TRACKING_H
#include "data_process_class_dll.h"
#include "parameters.h"
+628
View File
@@ -0,0 +1,628 @@
# 雷达数据处理流程与关键逻辑说明
> 适用对象:本仓库 `data_process_class_dll`(X256 雷达数据处理动态链接库)
---
## 0. 一页速览
| 环节 | 输入 | 处理 | 输出 |
|---|---|---|---|
| ① 前处理 | 一帧原始点迹(TWS 或 TAS) | 单位换算、缓存、判圈 | 处理模式码 0/1/2/3 |
| ② 调度 | 模式码 | 分流到 TWS / TAS 通道 | — |
| ③ 凝聚 | 单 CPI 或相邻两 CPI 点迹 | 同类点合并(保幅度大者) | 凝聚点迹表 |
| ④ 关联 | 凝聚点迹 + 已有航迹 | IMM 交互 → 波门筛选 → 最近邻一对一 → EKF 更新 | 更新航迹 + 剩余点迹 |
| ⑤ 起始 | 剩余(未关联)点迹 | 逻辑法(每圈顺序固定):点 × ≥2 点临时航迹关联 → 点 × 1 点航迹头关联 → 剩余点转新航迹头 → 达标临时航迹起批 | 新可靠航迹 + 更新后的临时航迹表 |
| ⑥ 外推/消亡 | 未关联航迹 | 未到外推上限则按数据率预测一帧并输出,超限只累加轮数、不再预测也不再输出(TWS 上限 3 轮;TAS 判据方向相反,相扫 5 / 机扫 48 轮起才预测) | 本圈有更新的航迹(含补点行)/ 消亡批号 |
| ⑦ 波束控制 | TAS 队列 + 航迹 | 数据率门控 + 队列调度 → 位置外推 | 跟踪波束指令 |
**贯穿全流程的三条主线**
1. **时间主线**:所有时间戳为 64 位毫秒(CPI/GNSS),参与运算时除 1000 转秒;ΔT = 点迹时间 − 航迹时间,ΔT 的正负决定"有效更新 / 只消费不更新 / 跳过"。
2. **状态主线**:每条可靠航迹维护一套 IMM(3 个 Singer 模型)状态 [x, vx, ax, y, vy, ay],x/y 两轴解耦,量测为「距离-方位-径向速度」三维 EKF。
3. **生命周期主线**:可靠航迹靠"外推轮数"消亡(判据为 `>`:TWS 普通航迹 6 圈、重点目标 10 圈,TAS 相扫 10 / 机扫 96 圈);临时航迹靠"缓存圈数 buff_round"消亡。
---
## 1. 总体数据流
```mermaid
flowchart TD
IN["宿主逐波位/逐CPI送入 DataRev[150]"] --> DP["data_preprocess 前处理与判圈"]
DP -->|"model=2 TWS未满圈"| M2["滑窗凝聚(只凝聚,不关联)"]
DP -->|"model=1 TWS满一圈"| M1["TWS 通道"]
DP -->|"model=3 TAS数据"| M3["TAS 通道"]
M2 --> BUF["本帧凝聚结果留作下一圈缓存"]
M2 --> PR["上一帧凝聚点 → point_recv(参与下一圈关联)"]
M1 --> A1["IMM 交互 + 波门筛选 + 最近邻关联 + EKF 更新"]
A1 --> A2["未关联航迹外推 / 超限消亡"]
A2 --> A3["剩余点迹 → 逻辑法起始:点×临时航迹 → 点×航迹头 → 剩余点转航迹头 → 起批"]
A3 --> OUT1["输出更新航迹 + 起批航迹历史点 + 消亡批号"]
M3 --> B1["TAS 凝聚(单帧)"]
B1 --> B2["仅对指定批号航迹做关联/EKF 或外推"]
B2 --> B3["TAS 超时消亡"]
B3 --> OUT2["输出该批号航迹"]
OUT1 --> BC["Beam_Ctrl 波束控制"]
OUT2 --> BC
BC --> OUT3["TrackingBeam:距离/方位/俯仰/批号"]
```
---
## 2. 入口前处理与"判圈"
### 2.1 前处理动作
对每一个输入点迹统一做下面几件事(TWS 与 TAS 相同):
| 动作 | 说明 |
|---|---|
| 单位换算 | 方位 度 → 弧度(内部统一弧度,输出再转度) |
| 高度折算 | `Height = Range · sin(Elevation)`(由量测俯仰直接折算,不经坐标滤波) |
| 字段搬运 | 幅度、SNR、RCS、时间戳、频点号、PRI、俯仰波位、十字星数据(`speed_dim[129]`/`range_dim[8]`)全部透传 |
| 使用标记 | 全部置 0(未使用) |
| 数量保护 | TWS:`min(Point_Sum, 150)`;TAS:`clamp(Point_Sum, 0, 150)` |
| 全局时间 | 每次调用都用本帧首点的 CPI 时间刷新"最新时间戳",供 TAS 外推/波束使用 |
### 2.2 判圈逻辑(决定走哪条通道)
```mermaid
flowchart TD
A["收到一帧点迹"] --> B{"point_type ?"}
B -->|"0 = TWS"| C["写入 TWS 缓存"]
C --> D{"方位波位号 Beam_index_aiz == 0 ?"}
D -->|"是"| E["return 1:满一圈,执行 TWS 航迹处理"]
D -->|"否"| F["return 2:未满圈,仅做滑窗凝聚"]
B -->|"1 = TAS"| G["写入 TAS 缓存<br/>记录 TAS 批号与本帧时间戳"]
G --> H["return 3:TAS 处理"]
B -->|"其它"| I["return 0:无效数据,丢弃"]
```
**要点**
- 判圈条件由"波位号由大变小"改为 **波位号为 0**(7d5a56f),避免机扫转台反转时误判一圈完成(代码中保留了旧判断的注释)。
- 波位号 0 是每圈的起始波位,因此**每圈恰好触发一次 model=1**。
- 清空(`track_clear_all`)后圈状态复位,下一次调用必然判为"满一圈"。
- TAS 数据不参与判圈,**每来一帧就处理一次**(TAS 是调度的跟踪波位,本身就是单目标 CPI)。
### 2.3 调度:model 与动作的对应
| model | 触发 | 实际执行的动作(顺序固定) |
|---|---|---|
| 2 | TWS 未满圈 | 两帧滑窗凝聚;缓存本帧点迹 |
| 1 | TWS 满一圈 | ① 关联/滤波/外推 ② 航迹消亡 ③ 航迹起始 |
| 3 | TAS 帧 | ① TAS 凝聚 ② TAS 关联/外推 ③ TAS 消亡 |
| 0 | 非法类型 | 无 |
> 顺序很关键:**消散在起始之前**,消亡腾出的资源不会与本圈更新冲突;**起始在最后**,只有"没被任何可靠航迹用掉"的点迹才有资格起批。
---
## 3. 点迹凝聚
### 3.1 目的与判据
同一目标在相邻波位/相邻 CPI 上会分裂成多个点,需先合并(保留幅度最大者,其余标记删除)。
| 模式 | 距离门限 | 方位门限 | 速度门限 |
|---|---|---|---|
| 近程 `work_mode==0` | ≤ 80 m | ≤ 6°(按环绕最小角差) | ≤ 2 m/s |
| 中/远程 `work_mode!=0` | ≤ 80 m | ≤ 6° | 不约束 |
方位差按环绕处理:
```
Δθ = min( |θ0 − θ1| , 2π − |θ0 − θ1| )
```
### 3.2 合并规则(链式比较)
```mermaid
flowchart TD
A["两两比较点迹 (0, i)"] --> B{"距离/方位/速度<br/>均在门限内?"}
B -- 否 --> Z["跳过"]
B -- 是 --> C{"幅度:点0 < 点i ?"}
C -- 是 --> D["点0 标记删除<br/>并把点i 的 R/方位/V/幅度<br/>接管为当前基准值"]
C -- 否 --> E["点i 标记删除"]
D --> F["后续比较以幅度更大者为基准(链式凝聚)"]
E --> F
F --> G["统一删除:被标记点 + 量程外点<br/>r < 100 m 或 r > 100000 m"]
```
### 3.3 TWS 两帧滑窗(关键机制)
```mermaid
flowchart TD
A["上一帧缓存点(区号=1)"] --> C["合并成同一张表"]
B["本帧新点(区号=2)"] --> C
C --> D["整表凝聚 + 量程过滤"]
D --> E{"点的区号"}
E -->|"1(较早的点)"| F["输出到 point_recv<br/>→ 本圈参与关联/起始"]
E -->|"2(本帧的点)"| G["留在缓存中<br/>→ 与下一帧合并后再输出"]
F --> H["缓存清空,本帧剩余点成为新缓存"]
```
**为什么这样设计**
- 相邻两个 CPI 的同一目标点会落在不同帧里,单帧凝聚无法合并 → 延迟一帧输出,换取跨 CPI 的分裂点合并;
- 代价是关联/起始用的是"上一帧"的点,时间上滞后一圈,但由于后续所有 ΔT 都按真实 CPI 时间戳计算,滤波本身不受影响;
- 因此 TWS 通道的正常节奏是:**未满圈 model=2 只做凝聚 + 缓存;满圈 model=1 用上一帧凝聚结果做关联与起始**。
> TAS 凝聚为**单帧**不缓存,门限恒为「距离 ≤ 80 m、方位 ≤ 6°、速度 ≤ 2 m/s」,之后同样做量程过滤。
---
## 4. TWS 点航关联与滤波(model=1 的核心)
### 4.1 处理骨架
```mermaid
flowchart TD
A["全部可靠航迹"] --> A1["IMM 交互(mix)"]
A1 --> B["滤波前先统一置 point_flag=0(本圈默认未关联实点)"]
B --> C["逐航迹 × 逐点:算 ΔT、三个模型马氏距离 d1/d2/d3"]
C --> D{"波门判定<br/>统计距离波门 thd_d + 高度门限 thd_h + 距离门限 thd_r"}
D -- 通过 --> E["记入候选表(含 d_min = min(d1,d2,d3))"]
D -- 不通过 --> F["丢弃该候选对"]
E --> G["最近邻:反复取全表 d_min 最小者"]
G --> H["该航迹本圈候选全部移除<br/>该点迹候选全部移除<br/>(一对一)"]
H --> I{"ΔT > 0 ?"}
I -- 是 --> J["三模型 EKF 更新 → 模型概率更新 → 融合输出<br/>更新航迹时间/点信息/高度平滑<br/>Extrapolate_round=0, point_flag=1"]
I -- 否 --> K["不更新状态、不刷新航迹时间<br/>但把该点迹标记为已使用(消费掉)"]
J --> L{"还有候选?"}
K --> L
L -- 有 --> G
L -- 无 --> M{"未关联航迹(point_flag==0 且非手动TAS):<br/>Extrapolate_round ≤ MAX_EXTRAPOLATE_NUM ?"}
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 追加起批历史点)"]
M2 --> N
```
### 4.2 波门(统计距离、高度、距离三类都通过才算候选)
**(1)统计距离波门 thd_d**——按航迹距离分段收紧高机动模型(`ASSO_THORD = 4`):
| 航迹距离 r_track | 模型 1 / 2 | 模型 3 |
|---|---|---|
| ≤ 500 m | d² < 16 | d² < 16/1000(≈ 关闭,抑制近程高机动模型的乱关联) |
| 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 中被识别,有意保留)。
**(2)高度门限 thd_h**(e0bf227 起在 TWS 侧生效)——|h_track − h_point| 按航迹距离分段:
| 航迹距离 r_track | 高度差门限 |
|---|---|
| ≤ 1000 m | 150 m |
| 1000 ~ 2000 m | 200 m |
| 2000 ~ 3000 m | 250 m |
| 3000 ~ 5000 m | 300 m |
| > 5000 m | 不约束 |
> 该门限此前在 `track_asso.cpp` 中整块被注释、`d_h` 恒为 1(等于不生效),现改为独立布尔量 `thd_h` 参与判定,取值与 TAS 通道一致,用于压制俯仰测量跳变引起的错误关联。
**(3)距离门限 thd_r**——抑制远距离突跳。用航迹-点迹弦距 R 与航迹位移 VT = v_track·ΔT 判断:
```
VT < 100 且 R > 300 → 拒绝(慢速目标不允许 300 m 以上的跳变)
100 ≤ VT ≤ 300 且 R/VT > 3 → 拒绝(允许的跳变与速度位移成比例)
VT > 300 且 R > 1200 → 拒绝(高速目标给固定 1200 m 上限)
```
**(4)俯仰门限 d_p**:分距离段 7°/5°/10° 的逻辑仍被注释停用,不参与判定(其作用已由高度门限覆盖)。
> 实现要点:三类判据分别算成 `thd_d` / `thd_h` / `thd_r` 三个布尔量,最后以 `thd_d && thd_h && thd_r` 决定是否记入候选表(e0bf227 起;此前三者混写在一个长 `if` 条件里)。
### 4.3 最近邻(NN)选取规则
- 候选对按 `d_min = min(d1, d2, d3)` 排序,**每轮取全表最小值**;
- 选中后,候选表中"该航迹的全部候选"和"该点迹的全部候选"一并删除 → **每条航迹每圈最多关联一个点,每个点最多被一条航迹使用**;
- 直到候选表为空或遍历完航迹数。
### 4.4 ΔT ≤ 0 的特殊处理(当前行为)
ΔT = (点迹 CPI 时间 − 航迹 T_track)/1000 ≤ 0,典型场景是"航迹刚被 TAS 点迹更新过,本圈 TWS 点迹时间更早"或"重复时间戳"。
| 环节 | 行为 |
|---|---|
| 是否参与最近邻比较 | **参与**(仍可能与最小 d 一起被选中) |
| IMM/EKF 更新 | **不执行**,X1/X2/X3、P1/P2/P3、模型概率均保持原值 |
| 航迹时间/点信息 | **不刷新**(T_track、GNSS_time、关联点信息、幅度/SNR/RCS 都不变) |
| 新鲜度标志 | `point_flag` 保持 0、`Extrapolate_round` 不归零、`associate_point_number` 不递增 |
| 本圈后续 | 该航迹本圈不再关联其它点迹;由于 point_flag 仍为 0,**在随后的外推环节被推进一帧**(未超外推上限时;超限后只累加轮数、不再预测也不输出该行) |
| 点迹归属 | 该点迹被标记"已使用"消费掉 → **不会再进入航迹起始**,避免生成虚假 TWS 航迹 |
### 4.5 关联成功后的更新内容
1. 三个模型的 EKF 状态/协方差更新;
2. 模型概率按新息协方差似然更新(见第 5 节);
3. 航迹时间 T_track / GNSS 时间跟进到该点迹;
4. 关联点信息(距离/方位/俯仰/径向速度/PRI)、幅度、SNR、RCS、归一化幅度 `amp_asv_point`、速度维 FFT 点数 `prn_fftn_point`、俯仰波位、十字星数据刷新;
5. **高度维平滑**(见 4.6);
6. `Extrapolate_round = 0`、`point_flag = 1`、`associate_point_number += 1`、`Track_Update_Flag = 1`(输出门控,见 4.7 末)。
### 4.6 高度平滑
关联实点后把点迹高度压入滑窗,输出高度取最近 w 个点的均值(不足 w 个则全平均):
| 点迹距离 | 窗口长度 w |
|---|---|
| ≤ 1000 m | 5 |
| 1000 ~ 2000 m | 6 |
| 2000 ~ 4000 m | 7 |
| > 4000 m | 9 |
> 距离越远俯仰测量跳变越大,窗口随之放长;仅 TWS/TAS 关联到实点时才刷新,外推补点不改变高度。
### 4.7 未关联航迹的外推(含外推圈数上限)
对 `point_flag == 0` 且 `manual_tracking_flag == 0` 的航迹(含 ΔT≤0 被消费的情形),按 `Extrapolate_round` 分两支(50e0b5c):
**(1)未到上限**:`Extrapolate_round <= MAX_EXTRAPOLATE_NUM`(机扫/相扫均为 3)时照常外推:
- 推算步长取运行时数据率:`work_mode` 0/1/2 → `DATA_RATE_SHORT / MIDDLE / FAR`(秒);
- 用各子模型的 Singer F、Q 推进一帧(**不做量测更新**);
- `T_track`、`GNSS_time` 各推进 ΔT×1000 毫秒;
- `Extrapolate_round += 1`,`point_flag = 0`(补点/虚点),`Track_Update_Flag = 1` → 本圈作为补点行输出。
**(2)已超上限**:判据不再成立时**不再做 Singer 预测、不推进航迹时间**,只执行:
- `Extrapolate_round += 1`、`point_flag = 0`、`Track_Update_Flag = 0` → 本圈**不输出**该航迹(航迹仍留在航迹表中,等消亡判据命中)。
**输出门控(50e0b5c)**:TWS 输出航迹的循环改为「`Track_Update_Flag == 1` 才输出,输出后清零」,即每圈只输出**本圈关联到实点或成功外推**的航迹;此前是每圈把航迹表中全部可靠航迹各输出一行。门控只判更新标志、不判 `manual_tracking_flag`,因此手动转 TAS 的航迹若本圈在 TWS 侧关联到实点,仍会经 TWS 通道输出。
> 由于外推上限(3)小于 TWS 消亡轮数(6),普通航迹在停止预测后还会**连续 3 圈不向宿主输出**任何航迹行(`Extrapolate_round` 依次走到 5、6、7,第 7 圈在 `track_die` 中消亡),只在最后输出一次消亡批号。
>
> **TAS 通道的判据方向相反**(`track_asso_tas.cpp` L484):`Extrapolate_round >= MAX_EXTRAPOLATE_NUM_TAS` 时才做预测、推进航迹时间并置 `Track_Update_Flag = 1`;未达该轮数时只累加圈数(不预测、不输出)。即 TWS 是"先外推、到上限后停",TAS 是"先不预测、到轮数后才开始外推",详见 7.2 与第 10 节。
---
## 5. 航迹起始(逻辑法)
### 5.1 状态机(每圈按 ①→⑤ 的固定顺序执行)
```mermaid
flowchart TD
A["本圈未被可靠航迹关联的点迹"] --> A0["临时航迹所有点的 buff_round +1"]
A0 --> B["① 点 × ≥2 点临时航迹关联(三点及以上)<br/>EKF 统计距离 + 折角 + 多普勒同号<br/>命中 → 复制原航迹 + 追加新点"]
B --> C["② 点 × 1 点航迹头关联(两点起始)<br/>速度区间 + 多普勒一致性<br/>命中 → 两点卡尔曼初始化 → 2 点临时航迹"]
C --> D["③ 删除 Use_Flag==1 的点迹<br/>剩余点全部转成新航迹头(1 点,buff_round=1)<br/>当圈不再参与关联"]
D --> E{"④ 长度 ≥ 起批点数<br/>且 ≤10 且不在起批屏蔽区?"}
E -- 是 --> F["同源去重(保留末点 d 更小者)"]
F --> G["三点卡尔曼初始化 → 分配航迹号 1~500<br/>IMM 三模型同状态,u=1/3"]
G --> H["输出该航迹的历史点(前 N 个点)"]
E -- 否 --> I["留在临时航迹表,下一圈继续关联"]
H --> J["⑤ 临时航迹消亡(不对外输出)<br/>末点 buff_round > 2 或 长度 ≥ 10"]
I --> J
```
**每圈顺序不可颠倒(`track_init.cpp` L49-126)**:点迹**先**做点-临时航迹关联(延长已有 ≥2 点临时航迹),**再**做点-航迹头关联(产生新的 2 点临时航迹),两阶段都结束后才把未被消费的点迹转成新航迹头,最后才把长度达标的临时航迹转可靠航迹,消亡放在最末。由此可得:
- 当圈新建的 2 点临时航迹**不可能当圈起批**——长度不足,且新点 `buff_round=1` 要到下一圈才具备关联资格;
- 当圈刚被延长到达标长度的临时航迹**可以当圈起批**(第 ④ 步在第 ① 步之后执行);
- 两个关联阶段**互不排斥**:都只把命中的点迹标记 `Use_Flag=1`,航迹头阶段不检查临时航迹阶段留下的标记,因此同一点迹可同时延长一条临时航迹、又开启一条 2 点临时航迹,只有走完两阶段后才统一删除被标记的点迹。
> 第 ⑤ 步的消亡判据对**未起批**的临时航迹生效;已起批的航迹在第 ④ 步中已从临时航迹表移除。
### 5.2 关联判据汇总
每圈按 **① 点-临时航迹关联 → ② 点-航迹头关联** 的顺序依次执行,两阶段的关联判据如下。
**阶段①:点 ↔ ≥2 点临时航迹(三点及以上)**
```
|r_point − r_末点| ≥ 5 m 距离差硬门限(近距点不参与)
d ≤ 8(代码写作 d² ≤ TRACK_START_THRESHOLD²=64) 3 维 EKF 统计距离波门
α < ALPHA_START = 60° 折角:最近三点连线夹角
v_r,point · v_r,末点 > 0 多普勒同号
```
- d 由 4 维 CV 状态一步预测 + 「距离-方位-径向速度」EKF 得到(过程噪声 0.03²、同样做方位环绕与盲速折叠);
- 折角 α 为向量 D12(前一点→末点)与 D23(末点→新点)的夹角;α 越小越接近直线;
- 门限同样写作 `d² ≤ TRACK_START_THRESHOLD²`,而 d 本身已是马氏距离平方,实际生效为 d ≤ 8(与可靠航迹关联门限的语义一致)。
**阶段②:点 ↔ 航迹头(两点起始)**
用航迹头位置与点迹的平面距离 `dis` 与 ΔT(秒):
```
dis ≤ V_MAX · ΔT 平均速度不超上限
dis ≥ V_MIN · ΔT 平均速度不低于下限(滤除静止/慢速杂波)
v_r,point · v_r,head > 0 两点多普勒同号(同接近/同远离)
dis ≥ |v_r,point| · ΔT 几何位移不小于"点迹自身多普勒"位移
dis ≥ |v_r,head| · ΔT 同上,用航迹头的多普勒约束
```
- 后两条是**多普勒-几何一致性**约束,用于剔除"切向高速短航迹"(7d5a56f 新增):切向飞行时真实几何位移大而多普勒径向速度小,纯速度区间门限会放进虚假航迹;
- 判据隐含要求 ΔT > 0(ΔT ≤ 0 时 `dis ≤ V_MAX·ΔT` 必然不成立);
- 近程/中远程分支当前取值相同(都是 V_MAX),**实际不做距离分段**。
### 5.3 临时航迹的"圈数"生命周期
`buff_round` 只在起始流程里自增,并且**每次调用(= 每圈)先把所有临时航迹所有点的 buff_round +1,再做关联**;因此关联判据看到的是自增后的圈数,当圈刚建立/刚追加的点(buff_round=1)当圈不可能再被关联:
| 动作 | 效果 |
|---|---|
| 新点加入 | buff_round = 1 |
| 关联资格 | 只看**末点**,要求 buff_round ≥ 2 → **上一圈加入的点本圈才能被延续**;同一圈内一条临时航迹可被多个点同时延续(每个点各复制出一条新航迹,见 6.4),并非一对一 |
| 消亡条件 | 末点 buff_round > 2(连续 2 圈无新点)或长度 ≥ 10 |
**时间轴示例(以第 k 圈加入新点为起点)**
```
第 k 圈:新点加入 → 末点 buff_round=1(本圈不再关联)
第 k+1 圈:+1 → 2 → 允许关联;成功则新点 1,失败则 2(不 >2,存活)
第 k+2 圈:+1 → 3 → 仍允许关联;成功则新点 1,失败则 3>2 → 消亡
```
即:**允许中间空 1 圈**,最大关联间隔 2 圈;生命周期完全由"圈数"驱动,与绝对时间无关。
### 5.4 航迹分裂与去重
- 关联阶段**不做一对一分配**:一个点满足多条临时航迹条件时,每条都各自复制出一条新航迹;多个点命中同一条临时航迹时也各自复制;点-航迹头阶段同理(一个航迹头可与多个点各生成一条 2 点临时航迹)→ 同一圈会产生大量同源临时航迹;
- 两个关联阶段也**互不排斥**:点-航迹头阶段不检查点-临时航迹阶段留下的 `Use_Flag`,同一点迹可同时参与两边的分裂试探;
- 起批前做**同源去重**:比较两条候选航迹的**前三个点**,只要有任一点的"时间 T + 距离 r"完全相同即判为同源,保留末点统计距离 d 更小者;
- 这样既保证不漏关联(允许分裂试探),又不会让同一批点重复起批。
### 5.5 起批与输出
| 条件 | 说明 |
|---|---|
| 长度 | `track_start_point_num ≤ L ≤ 10`(接口层校验 3~9,典型 3 或 4) |
| 位置 | 末点不落在**起批屏蔽区**(距离-方位矩形,最多 30 个,用严格不等号判断) |
| 容量 | 可靠航迹数 < 500 且能分配到一个未占用航迹号 |
| 初始化 | 用**最后三个点**做三点卡尔曼初始化 → 6 维状态;三模型同状态、u=1/3 |
| 输出 | 该航迹的**前 N 个点**(N = 起批点数)作为历史点一次性输出,每条航迹最多 10 个历史点 |
> 达到长度即当圈起批,没有"连续 M 圈确认"的环节;若被屏蔽区拦住,航迹会继续在临时航迹表里增长,直到离开屏蔽区(此时长度已大于起批点数)或长度达到 10 被判消亡。
---
## 6. 外推、消亡与航迹号管理
### 6.1 消亡条件
| 通道 | 条件(满足其一即消亡) |
|---|---|
| TWS 普通航迹 | `Track_Mode==0` 且 `primary_target_flag==0` 且 `Extrapolate_round > TRACK_DIE_ROUND`(相扫/机扫均为 6) |
| 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`(相扫 10 / 机扫 96) |
| 手动删除 | `manual_delete_flag == 1`(各通道单独判定,置位即消亡,由外部删除接口 `track_delete` 置位) |
三个判据都改为 `die_flag` 组合("或"关系),且比较符由 `>=` 改为 `>`(50e0b5c):即允许的外推圈数比旧版各多 1 圈,且**重点航迹与普通航迹走不同门限**。由于每圈未关联只自增 1,判据为 `>` 时实际是"连续 N+1 圈没有实点关联"才消亡:普通 TWS 航迹 7 圈、重点航迹 11 圈、TAS 相扫 11 圈 / 机扫 97 圈。
> `primary_target_flag`(重点目标标记)由外部接口 `Data_Process::primary_target_add / primary_target_cancel` 按批号置 1 / 清 0(`data_process.cpp` L268-297);新起批航迹在 `track_init.cpp` 中以 `Trust_Track trust_track_tmp = {}` 零初始化,故恒为 0。该标记目前只影响上表的 TWS 消亡轮数。
消亡时把批号写入消亡输出数组并从航迹表删除;**临时航迹消亡不对外输出**(起批失败的中间态对宿主不可见)。
### 6.2 航迹号分配
```mermaid
flowchart TD
A["需要起批"] --> B{"航迹表为空?"}
B -- 是 --> C["分配 1"]
B -- 否 --> D["用航迹表建立 1~500 占用位图"]
D --> E["从 lastest_index+1 向上找第一个空闲号(含 500)"]
E --> F{"找到?"}
F -- 是 --> G["分配并更新 lastest_index"]
F -- 否 --> H["从 1 回绕扫一遍找空闲号"]
H --> I{"找到?"}
I -- 是 --> G
I -- 否 --> J["返回 0:全满,本候选不起批"]
```
特点:**优先顺序递增分配**(便于人工按批号识别新老),只有走到表尾才回绕复用早期空号。
---
## 7. TAS 通道(model=3)
### 7.1 与 TWS 通道的差异
| 项 | TWS | TAS |
|---|---|---|
| 凝聚 | 相邻两帧滑窗,近程多速度维约束 | 单帧,恒有速度维约束 |
| 处理对象 | 全部可靠航迹 × 全部点 | **仅指定批号**的这一条航迹 × 全部点 |
| 俯仰/高度门限 | 俯仰门限已注释停用;**高度门限生效**(按距离分段,e0bf227 起) | **高度门限生效**(按距离分段,取值与 TWS 相同) |
| ΔT ≤ 0 | 参与选取但只消费点迹、不更新 | **直接跳过该点**(并记告警日志) |
| 外推时间 | 按 work_mode 数据率固定步长 | 按 `最新时间戳 − 航迹时间` |
| 外推触发轮数 | `Extrapolate_round <= MAX_EXTRAPOLATE_NUM`(3)才预测 | `Extrapolate_round >= MAX_EXTRAPOLATE_NUM_TAS`(相扫 5 / 机扫 48)才预测(方向与 TWS 相反) |
| 输出 | 本圈有更新的航迹(关联到实点或成功外推,输出后清 `Track_Update_Flag`) | 该批号航迹且本圈有更新 |
### 7.2 TAS 关联与更新
```mermaid
flowchart TD
A["TAS 本帧点迹 + 指定批号航迹"] --> B{"该批号航迹存在?"}
B -- 否 --> Z["直接返回(不做任何处理)"]
B -- 是 --> C["IMM 交互;航迹 point_flag 置 0"]
C --> D["逐点:ΔT ≤ 0 → 记告警并跳过"]
D --> E["逐点:三模型统计距离 d1/d2/d3 + 高度门限"]
E --> F{"d² < 16(任一模型)且高度门限通过?"}
F -- 是 --> G["记入候选表"]
F -- 否 --> H["丢弃"]
G --> I["取 d_min 最小的一点"]
I --> J["三模型 EKF 更新 → 概率更新 → 融合<br/>刷新时间/点信息/高度平滑"]
J --> K["输出该批号航迹(Track_Update_Flag==1 才输出,输出后清零)"]
E --> L{"候选表为空?"}
L -- 是 --> L1{"Extrapolate_round ≥ MAX_EXTRAPOLATE_NUM_TAS ?"}
L1 -- 是 --> M["按 ΔT = 最新时间戳 − 航迹时间 外推一帧<br/>(ΔT ≤ 0 则跳过),Track_Update_Flag=1"]
L1 -- 否 --> M2["不预测、不推进时间<br/>仅 Extrapolate_round+1,Track_Update_Flag=0"]
M --> K
M2 --> K
```
**高度门限**(|h_航迹 − h_点迹| 上限,按航迹距离分段):
| 航迹距离 | 门限 |
|---|---|
| ≤ 1000 m | 150 m |
| 1000 ~ 2000 m | 200 m |
| 2000 ~ 3000 m | 250 m |
| 3000 ~ 5000 m | 300 m |
| > 5000 m | 不约束 |
**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 节。
---
## 8. TAS 波束控制(外部接口 Beam_Ctrl)
### 8.1 队列管理
```mermaid
flowchart TD
A["Beam_Ctrl 调用"] --> B["输出航迹计数清零"]
B --> C["入队 tas_target_add"]
C --> D["出队 tas_target_del"]
D --> E["波束输出 tas_beam_output"]
E --> F["输出 TrackingBeam"]
```
**入队条件**(只支持手动转 TAS):
- 该航迹 `manual_tracking_flag == 1`(由外部 `tracking_start` 置位);
- 航迹当前 `Track_Mode == 0`(还不是 TAS);
- 队列未满(相扫最多 4 个 / 机扫最多 1 个);
- 不在 TAS 禁止区(距离-方位矩形)。
入队动作:写批号、标记非空、`last_track_time = T_track`、**航迹 Track_Mode 置 1**,并**立即输出一次该航迹更新**(通知宿主跟踪状态变化)。
**出队条件**:队列中的批号在航迹表中已不存在,或不再处于"手动 + TAS"状态 → 清空该队列项。
> 自动开启/退出 TAS 的逻辑(按距离/速度/高度阈值)已全部注释停用,**只支持手动**。
### 8.2 波束输出调度(关键)
```mermaid
flowchart TD
A["遍历 TAS 队列"] --> B["按批号在航迹表中找到该航迹"]
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 = 最新时间戳"]
C -.->|"无任何合格目标"| H["open_flag = 0(其余字段保持旧值)"]
```
**要点**
- 门控只用一个条件(e0bf227 修正):目标「距上次波束输出的时间」(`latest_timestamp − last_track_time`)要大于 `DATA_RATE_TAS` 秒(单位是秒,由宿主经参数初始化传入,须为 > 0 的有效值)→ 保证 TAS 目标的数据率**不随 Beam_Ctrl 的调用频率变化**;修正前还要求「航迹时间与最新时间戳之差」也大于该门限,与"取最早 CPI 目标"的语义冲突,会漏掉本应输出的波束;
- 多目标时选**队列中 CPI 时间最早**的目标(同时要求其 CPI 时间早于当前已选者),等价于"按最久未更新者优先"的轮转;
- 选中后才按该目标的 CPI 时间算外推步长 `ΔT = (最新时间戳 − CPI 时间)/1000`;
- 原来的"每次调用整队循环移位"已停用;
- 俯仰用航迹高度直接算(叠加雷达平台高度的处理只影响对外输出的 z,不参与波束俯仰)。
---
## 9. 参数脉络(决定行为的旋钮)
### 9.1 编译期宏(随扫描体制切换)
| 宏 | 相扫 | 机扫 | 作用点 |
|---|---|---|---|
| `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 | TWS 普通航迹消亡轮数(判据 `Extrapolate_round > 该值`) |
| `TRACK_DIE_ROUND_PRIMARY` | 10 | 10 | TWS 重点航迹(`primary_target_flag==1`)消亡轮数 |
| `TRACK_DIE_ROUND_TAS` | 10 | 96 | TAS 航迹消亡轮数(判据 `Extrapolate_round > 该值`) |
| `MAX_TRACK_NUM / MAX_TRACK_INDEX` | 500 / 500 | 同左 | 航迹数上限 / 航迹号上限 |
| `TAS_QUEUE_LENGTH / MAX_TAS_NUM` | 4 / 4 | 1 / 1 | TAS 队列长度 / 最大 TAS 目标数 |
| `H_F_WIN_LEN` | 3 | 同左 | 高度平滑窗长基数(实际 +2/+3/+4/+6) |
| `FREQ0` | 9.2 GHz | 同左 | 盲速计算的基准频率 |
| `RDP_LOG_ENABLE / RDP_LOG_DIR` | — | — | 日志落盘开关与目录 |
### 9.2 运行时参数(宿主传入,随任务变化)
| 参数 | 影响环节 |
|---|---|
| `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 禁止区 |
> `track_start_threshold / track_asso_threshold / track_asso_threshold_tas` 三个字段**当前未被实现读取**,实际门限来自编译期宏。
---
## 10. 关键约定与注意事项
**时序与时间戳**
- 时间戳为 64 位毫秒;`T_track` 输出为秒(`/1000`);所有 ΔT 用秒参与运算;
- TWS 关联中 ΔT ≤ 0 的候选"只消费点迹、不更新航迹"(点迹不会再去起批);
- TAS 关联中 ΔT ≤ 0 直接跳过该点;TAS 外推 ΔT ≤ 0 跳过本轮;
- 两点/三点卡尔曼初始化对 T 无除零保护 → **起批路径的时间戳必须严格递增**。
**量纲与坐标**
- 外部接口角度为度,内部统一弧度,输出再度;
- 平面坐标为水平投影,z = 目标高度 + 雷达平台高度;
- 点高度由 `R·sin(俯仰)` 直接折算,不参与滤波状态(由高度平滑单独维护)。
**门限语义**
- 关联与起始的波门写作 `d² < 门限²`,而 d 已是马氏距离平方 → 实际等效 **d < 门限**;
- 起始的"距离差 ≥ 5 m"为硬编码,对切向远距离慢速目标可能造成漏关联;
- 起批去重只比较**前三个点**,若起批点数 > 3,共享前三点后分叉的两条航迹会被判为同源。
**容量与边界**
- 点迹数上限 150(前处理已限幅);航迹数/航迹号上限 500,全满时新目标不再起批;
- 屏蔽区/禁止区的组数按 30 组数组定义,配置数量未做上限校验;
- 高度平滑缓存只增不减,长时运行下每条航迹的内存占用会持续增长。
**外推、消亡与输出门控(50e0b5c / fc49707 / 42b598e / e8c56f9)**
- **外推圈数上限**:TWS 只在 `Extrapolate_round <= MAX_EXTRAPOLATE_NUM`(3)时预测并输出;TAS 相反,`Extrapolate_round >= MAX_EXTRAPOLATE_NUM_TAS`(相扫 5 / 机扫 48)时才预测并输出。两处比较方向不一致,此处仅如实记录,**不判定为缺陷**;
- **消亡判据**由 `>=` 改为 `>`,并区分重点目标:普通 TWS 航迹 `> TRACK_DIE_ROUND`(6)、重点航迹(`primary_target_flag==1`)`> TRACK_DIE_ROUND_PRIMARY`(10)、TAS 航迹 `> TRACK_DIE_ROUND_TAS`(相扫 10 / 机扫 96);`manual_delete_flag == 1` 单独判为消亡;因外推上限(3)小于消亡轮数(6),普通 TWS 航迹停止预测后还会连续 3 圈不向宿主输出;
- **输出门控**: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 改为上式;
- **归一化幅度 / 速度维 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)**:`parameters.h` 新增版本宏并拼装为 `VERSION_STRING`(当前 `V1.5.6.PS` / `V1.5.6.MS`),实例创建后可由基类公开成员 `version_info` 读取;`Data_Process` 新增 `point_id` / `frame_id` / `circle_id` 自增计数(`circle_id` 在 TWS 满一圈、TAS 每帧末自增),`PointRecv`/`Temp_track`/`Trust_Track` 新增对应的标识字段,记录功能据此把「输入点迹 → 凝聚点迹 → 临时航迹 → 可靠航迹 → 关联信息」串成同一条链路。
**未启用**
- 引导跟踪(direct tracking)、手动打跟踪波束接口为空实现;
- TAS 自动开启/退出逻辑停用,只支持手动转/停 TAS;
- `tracking_stop` 后,队列项要等到下一次 `Beam_Ctrl` 才真正移除。
---
## 附录:与现有 `README.md` 的差异提示
> 说明:下表是本文档成文时(README 尚未同步 7d5a56f 改动)发现的差异,保留以说明改动缘由;随后已按下表结论更新 README 第 2、7.2、8.1、8.3、9.3、9.6、10.1 等节,「README 描述」列对应更新前的旧表述,现以本文档与 README 的描述为准。
| 项 | README 描述(更新前) | 当前源码 |
|---|---|---|
| 满圈判据 | 当前方位波位号 < 上一帧波位号 | **方位波位号 == 0**(7d5a56f,适应机扫转台反转) |
| 点-航迹头判据 | 按距离分段收紧速度门限(半速、1/2~1/3 速) | 该分支注释停用;改为统一速度区间 **+ 两条多普勒-几何一致性约束**(7d5a56f) |
| 起始输出历史点 | "临时航迹的全部 L 个点" | 实际输出**前 `track_start_point_num` 个点** |
| 起始输入过滤 | "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` 中该宏当前为**注释状态**(不落盘,日志进控制台) |
| 起始步骤顺序 | "航迹头(1 点)→ 点-航迹头关联(2 点)→ 点-临时航迹关联(≥3 点)→ 起批" | 每圈实际顺序为**点 × ≥2 点临时航迹关联 → 点 × 1 点航迹头关联 → 剩余点转新航迹头 → 长度达标的临时航迹起批**,消亡最后执行(`track_init.cpp` L49-126,见第 6 节) |
> 后续:README 与 `track_init_readme.md`(航迹起始专项说明)已按上表结论同步(见 CHANGELOG 的 Unreleased 记录);流程落地实现仍以本文为准。
>
> 补充修订(本次):起始流程的**步骤顺序**原先在本文档(第 0、6 节)与 README(第 2、5.2、8.3、9.3 节)中都写作「航迹头 → 2 点 → ≥3 点 → 起批」,与源码每圈「点 × 临时航迹关联 → 点 × 航迹头关联 → 剩余点转航迹头 → 达标者起批」的顺序相反,现已按源码修正,并补充说明两个关联阶段互不排斥、均不做一对一分配。
+195
View File
@@ -0,0 +1,195 @@
# 数据记录调试功能(rdp_record)
> 面向调试的数据记录框架:把内部结构体按「一个记录实体一张表」写入 SQLite,供事后查询分析。
> 本功能**默认只在 Debug 构建中启用**,Release/交付构建完全不编译(无依赖、无代码、无性能损失)。
> 相关代码:`data_process_class_dll/rdp_record.h`、`rdp_record_entity.h`、`rdp_record.cpp`;
> 第三方依赖来源见 `third_party/ormpp/UPSTREAM.md`。
---
## 1. 目的与范围
- **目的**:调试期把关键中间数据落库(点迹、航迹、关联结果……),用于事后 SQL 查询与对比分析;
- **框架性设计**:本功能**不预设记录哪些数据**。新增一类记录数据的成本固定为三步:
加记录实体结构体 → 注册 → 在需要的位置写一行记录点宏(配方见第 5 节);
- **非目标**:不做异步写入、不做查询/回放工具、不做数据裁剪与轮转、不追求多线程写入性能。
## 2. 开关与构建
| 项目 | 说明 |
|---|---|
| 开关 | CMake 缓存变量 `RDP_DATA_RECORD`,取值 `AUTO`(默认)/ `ON` / `OFF` |
| AUTO 语义 | `Debug` 构建启用、`Release`(及其它)构建关闭 |
| 生效方式 | 启用时构建系统定义 `RDP_RECORD_ENABLE` 并额外编译静态库 `rdp_record`(`rdp_record.cpp` + 内置 `sqlite3.c`)并链接进 DLL |
| 关闭时的保证 | 记录模块源码**不参与编译**;`RDP_RECORD(...)` / `RDP_RECORD_BATCH(...)` 展开为 `((void)0)`(实参不求值,也不会产生「变量已初始化但未使用」告警);DLL 不新增依赖与导出符号 |
```bat
:: 交付/Release:关闭(AUTO 的默认结果,也可显式 OFF)
.vscode\scripts\configure.bat build/X256_PS PHASE_SCANNING Release OFF
.vscode\scripts\build.bat build/X256_PS
:: Debug:启用(AUTO 的默认结果,也可显式 ON)
.vscode\scripts\configure.bat build/X256_PS PHASE_SCANNING Debug ON
.vscode\scripts\build.bat build/X256_PS
:: 直接用 cmake(需先进入 MSVC 2022 x64 环境)
cmake -S . -B build/debug_rec -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Debug ^
-DRADAR_SCAN_MODE=PHASE_SCANNING -DRDP_DATA_RECORD=ON
cmake --build build/debug_rec
```
配置阶段会打印开关状态:`数据记录调试功能:启用(C++20 静态库 rdp_record + 内置 SQLite)` 或 `...:关闭(不编译记录模块,RDP_RECORD 为空操作)`。
**语言标准与依赖**:记录模块以 **C++20** 独立编译(ormpp 要求 `CMAKE_CXX_STANDARD 20`),并编译内置的 SQLite amalgamation(因此启用时需要 C 编译器,随 VS 2022 工具集提供);**雷达处理代码与内置 Eigen 仍为 C++17**,二者通过 `rdp_record.h` 的「仅声明 + 显式实例化」跨标准边界:调用方(C++17)只把记录实体按 `const` 引用递给 `rdp_record::Write<T>()`,实现在 `rdp_record.cpp`(C++20)中显式实例化。
## 3. 运行期行为
| 项目 | 说明 |
|---|---|
| 存放目录 | `RDP_RECORD_DB_DIR`(`parameters.h`,默认 `.\rdp_records\`,相对宿主进程当前工作目录,不存在时自动创建) |
| 文件名 | `rdp_record_YYYYMMDD_HHMMSS.db`(**每次运行一个新文件**,与 `rdp_logs` 的日志命名风格一致) |
| 初始化 | `Data_Process_Factory::GetB()` 内调用 `rdp_record::Init()`(幂等):建目录 → 开库 → 打印记录库路径 |
| 关闭 | `Data_Process_Factory::Destroy()` 内调用 `rdp_record::Shutdown()`;宿主未调用时由静态析构兜底关库 |
| 建表 | 首次写入某记录实体时自动执行 `CREATE TABLE IF NOT EXISTS`(每实体只尝试一次) |
| PRAGMA | `synchronous=OFF`(调试取舍:提交时不再 fsync,崩溃可能丢失尾部数据;如需崩溃安全可改为 `NORMAL` 或自行在 sqlite3 层开启 WAL)。未设置 `journal_mode`——ormpp 的 `execute()` 只接受不返回结果行的语句 |
| 线程模型 | 单连接 + 内部互斥锁串行化写入;宿主多线程调用不会破坏数据,但**不保证高并发写入性能**(调试功能,按要求不做异步) |
| 失败降级 | 目录不可建/库打不开:`Init()` 返回 `false`,只打印一次日志,之后所有写入静默忽略;建表或插入失败:**每个实体只提示一次**,不重试、不抛出、不影响雷达数据处理 |
| 自增主键 | 每个记录实体的首字段固定 `int64_t id`:建表时登记自增主键,插入时自动跳过该列(写入时固定填 `0`,由 SQLite 分配) |
## 4. 记录实体(表)约定
1. 结构体定义在**全局命名空间**(勿放进 `namespace`),推荐以 `RdpRec` 开头;
2. **结构体名即表名**(ormpp 取「短结构体名」),无需任何建表代码;
3. 首字段固定为 `int64_t id`;
4. 字段类型仅限标量,映射关系:
| C++ 类型 | SQLite 类型 | 备注 |
|---|---|---|
| `int` | `INTEGER` | 枚举同理(按整数存) |
| `int64_t` / `long long` | `INTEGER` | 毫秒时间戳用它 |
| `float` | `FLOAT` | |
| `double` | `DOUBLE` | |
| `bool` | `INTEGER` | |
| `std::string` | `TEXT` | 用于把数组类状态(如 `X[6]`、`P[6][6]`、`u[3]`)经 `rdp_record_entity.h` 的 `arrayToString()` 先转成文本再入库——这是「禁止数组字段」的替代写法,见第 5 条 |
5. **禁止数组字段**(如 `float speed_dim[129]`、`char[N]`):ormpp 对 C 数组按 C 字符串语义(`strlen`)绑定,会读越界或写入错误数据。需要记录大批量数组时,按下列三种方式之一处理:① 用一个 `std::string` 字段存 `arrayToString(数组)` 的结果(`RdpRecTempTrack`/`RdpRecTrustTrack` 的 `X`/`P`/`X1`~`X3`/`P2`/`P3`/`u` 即此法,多维数组以 `;` 分隔内层、`,` 分隔元素);② 另建「元素展开表」(一行一个元素);③ 改用 blob 字段;
6. 命名约定:字段名带单位后缀(`_ms` 毫秒、`_m` 米、`_deg` 度、`_mps` 米/秒、`_s` 秒),避免单位歧义;
7. **表结构变更不迁移旧表**:增删字段后不会修改已存在的表结构;由于每次运行生成新的 db 文件,正常调试流程天然规避该问题(若手工复用旧库,需自行建新表或删库)。
## 5. 新增记录点:四步配方
以「记录航迹号 + 高度」为例:
**第 1 步 — 声明实体**(`rdp_record_entity.h`「记录实体定义区」,紧随其后写一行 `RDP_RECORD_DECLARE`):
```cpp
struct RdpRecTrackHeight
{
int64_t id; //自增主键(写入时固定填 0)
int track_index; //航迹号
double height_m; //高度(m)
};
RDP_RECORD_DECLARE(RdpRecTrackHeight);
```
**第 2 步 — 注册实体**(`rdp_record.cpp`「记录实体注册区」,字段顺序必须与结构体声明顺序一致):
```cpp
YLT_REFL(RdpRecTrackHeight, id, track_index, height_m);
RDP_RECORD_DEFINE(RdpRecTrackHeight);
```
**第 3 步 — 埋记录点**(在需要记录的位置):
```cpp
//单条(就地构造;宏为可变参数,允许花括号内有逗号)
RDP_RECORD(RdpRecTrackHeight{0, trust_track[i].Track_Index, trust_track[i].Height});
//批量(需要语句/局部变量时,整体用 #ifdef 包裹,保证交付构建零代码)
#ifdef RDP_RECORD_ENABLE
std::vector<RdpRecTrackHeight> recs;
for (size_t i = 0; i < trust_track.size(); ++i)
{
recs.push_back(RdpRecTrackHeight{0, trust_track[i].Track_Index, trust_track[i].Height});
}
RDP_RECORD_BATCH(recs);
#endif
```
**第 4 步 — 无需建表**:首次写入该实体时自动 `CREATE TABLE IF NOT EXISTS`;不同实体自动落到不同表。
> 检查点:若忘记第 2 步(注册/实例化),链接期会报 **LNK2019**(`extern template` 已阻止隐式实例化),这正是配方的自检点。
## 6. 现有记录表与记录点
当前共 **7 个记录实体定义**,其中 **6 张表在用**(第 ⑦ 项已停用)。下表逐项给出记录点、埋点位置、表名与用途;完整字段清单见本节末的「字段速查」。
| # | 记录点 | 位置 | 表 | 说明 |
|---|---|---|---|---|
| ① | 输入点迹(TWS + TAS) | `data_process.cpp::data_preprocess()`,TWS 分支(`point_type == 0`)与 TAS 分支(`point_type == 1`)的逐点循环内 | `RdpRecPoint` | TWS/TAS **共用一张表**,用 `point_type` 区分;每个输入点迹一行,含 `point_id` / `frame_id` / `circle_id` 三个链路标识 |
| ② | TWS 凝聚后点迹 | `dot_coh.cpp::dot_coh_process_buff()`,输出到 `point_recv` 的循环内 | `RdpRecPointCohesionTWS` | 两帧滑窗凝聚后进入关联的点迹 |
| ③ | TAS 凝聚后点迹 | `dot_coh_tas.cpp::dot_coh_tas_process()`,输出到 `point_recv_tas` 的循环内 | `RdpRecPointCohesionTAS` | 单帧凝聚后的 TAS 点迹 |
| ④ | 临时航迹 | `track_init.cpp::track_init_process_logic()` 末尾(**消亡临时航迹之后**),双层遍历 `temp_track` | `RdpRecTempTrack` | 每条临时航迹的每个点一行,含 `temp_track_id` / `point_id` / `circle_id`;因在消亡之后记录,表中是「本圈存活下来 + 本圈新建」的临时航迹 |
| ⑤ | 可靠航迹 | `track_asso.cpp::track_asso_process()` 的**输出循环内**(`Track_Update_Flag==1` 的分支末尾) | `RdpRecTrustTrack` | 内部 `Trust_Track` 全量落库(含 IMM 的 `X`/`P`/`X1`~`X3`/`P2`/`P3`/`u`,数组经 `arrayToString()` 存为文本列)。**只记录本圈有输出的航迹,且仅 TWS 通道**——`track_asso_tas.cpp`(TAS)与 `track_init.cpp`(起批)均未埋此点 |
| ⑥ | 点航关联信息 | `track_asso.cpp::model_filter()` 三处:未通过门限的候选、被最近邻取中的候选、因航迹/点迹已被消费而删表的候选 | `RdpRecAssoInfo` | 每个候选一行(`d1`/`d2`/`d3`/`d_min`、`delta_T`/`delta_h`/`R`/`VT`、`thd_d`/`thd_h`/`thd_r`),并以 `min_d_flag` 标记是否被最近邻取中,可用于复现关联决策 |
| ⑦ | (已停用)本圈输出航迹 | 原 `data_process.cpp::track_process()` 的 `model == 1/3` 分支 | ~~`RdpRecTrackOut`~~ | 该实体与记录点已在 b227ae1 中整体注释停用,改由 ⑤ 记录内部航迹;`rdp_record.cpp` 与 `rdp_record_entity.h` 中保留注释代码以备参考 |
> 表清单即上表 ①②③④⑤⑥ 六张在用表(`rdp_record_entity.h` 中共 7 个实体定义,其中第 ⑦ 项已停用)。
**链路串联**:①→②/③(点迹 → 凝聚后点迹)按 `point_id` 关联;②/③→④/⑤ 按 `point_id` 与 `circle_id` 关联;④→⑤ 通过起批时透传的 `track_id`(取自临时航迹末点的 `temp_track_id`)关联;⑤→⑥ 按 `track_id` 与 `point_id` 关联。`circle_id` 在 TWS 满一圈(`Beam_index_aiz == 0`)、TAS 每帧处理结束时自增,可用来划分「第几圈/第几帧」。
**字段速查**(字段顺序即建表列顺序;首列均为 `int64_t id` 自增主键):
- `RdpRecPoint` / `RdpRecPointCohesionTWS` / `RdpRecPointCohesionTAS`(三者字段一致):`point_id`、`frame_id`、`circle_id`、`range`、`azimuth`(弧度)、`velocity`、`amplitude`、`height`、`SNR`、`RCS`、`encoder_value`、`amp_asv_point`、`prn_fftn_point`、`CPI_time`、`GNSS_time`、`use_flag`、`azimuth_beam_index`、`pitch_beam_index`、`PRF_index`、`freq_index`、`point_type`、`point_section_asso`;
- `RdpRecTempTrack`:`temp_track_id`、`point_id`、`circle_id`、`X`、`P`(文本)、`r`、`azi`、`vr`、`height`、`SNR`、`RCS`、`amplitude`、`CPI_time`、`GNSS_time`、`d`、`buff_round`、`temp_track_section_idx`、`asso_flag`、`pitch_num`;
- `RdpRecTrustTrack`:`track_id`、`point_id`、`circle_id`、`Track_Index`、`Range`、`Azimuth`、`Elevation`、`Velocity`、`Amplitude`、`Height`、`T_track`、`GNSS_time`、`Track_Sum`、`Point_Index`、`Track_Mode`、`Track_Update_Flag`、`Extrapolate_round`、`point_flag`、`approach_flag`、`manual_delete_flag`、`manual_tracking_flag`、`associate_point_number`、`primary_target_flag`、`Target_Type`、`Track_section_idx`、`range_point`、`azi_point`、`elev_point`、`vr_point`、`prf_point`、`point_type`、`snr_point`、`amp_asv_point`、`prn_fftn_point`、`RCS`、`pitch_num`、`X`、`P`、`X1`、`X2`、`X3`、`P2`、`P3`、`u`(后 8 项为文本);
- `RdpRecAssoInfo`:`point_id`、`track_id`、`circle_id`、`d1`、`d2`、`d3`、`d_min`、`point_index`、`track_index`、`delta_T`、`delta_h`、`R`、`VT`、`thd_d`、`thd_h`、`thd_r`、`min_d_flag`。
各表的字段为**调试用途**,按第 5 节增删即可,不影响框架。注意记录点中的字段错误只在启用记录功能的构建中才会被检查(关闭时宏丢弃实参)。
**两点使用提醒**:
- `RdpRecTrustTrack::Track_Update_Flag` **恒为 0**:写库发生在输出循环内,而该循环入口已先把标志清零(`track_asso.cpp` 先置 0、再输出、最后记录)。判断「某航迹本圈是否更新」应改用该表是否出现对应 `track_id` / `circle_id` 的记录,或改用 `RdpRecAssoInfo::min_d_flag`。
- `Range` / `Azimuth` / `Elevation` / `Velocity` 四个内部快照**只在航迹当圈有输出时被回填**,长期未输出(外推超限)的航迹在表中为 0 或旧值。
## 7. 查询与分析
```python
# 取最近一次运行的记录库并查看表结构、行数与抽样数据(Python 自带 sqlite3 即可)
import glob, os, sqlite3
path = max(glob.glob(r'rdp_records\rdp_record_*.db'), key=os.path.getmtime)
con = sqlite3.connect(path)
print(path)
print(con.execute("select name from sqlite_master where type='table'").fetchall())
print(con.execute("pragma table_info(RdpRecPoint)").fetchall())
print(con.execute("select count(*) from RdpRecPoint").fetchone())
print(con.execute("select * from RdpRecPoint order by id desc limit 3").fetchall())
print(con.execute("pragma integrity_check").fetchone())
# 按点迹链路串联:输入点迹 -> 凝聚后点迹 -> 临时航迹
print(con.execute("""
select p.circle_id, p.point_id, p.range, c.range, t.temp_track_id, t.buff_round
from RdpRecPoint p
left join RdpRecPointCohesionTWS c on c.point_id = p.point_id
left join RdpRecTempTrack t on t.point_id = p.point_id
order by p.id desc limit 10
""").fetchall())
# 查看某圈的关联决策(min_d_flag=1 为最终取中的候选)
print(con.execute("""
select track_id, point_id, d_min, delta_T, thd_d, thd_h, thd_r, min_d_flag
from RdpRecAssoInfo where circle_id = (select max(circle_id) from RdpRecAssoInfo)
order by min_d_flag desc, d_min asc limit 20
""").fetchall())
```
也可用 DB Browser for SQLite 直接打开 db 文件;同一 db 内所有表一一对应各记录实体。
## 8. 注意事项与已知限制
- **交付构建不要开启**:`Release` + `RDP_DATA_RECORD=ON` 仅用于临时排查,会带来 sqlite3 编译与运行时写入开销;正式交付保持 `AUTO`/`OFF`;
- **Release 下记录点不做类型检查**:关闭时宏丢弃实参,因此记录点里的字段拼写/类型错误只在启用记录功能的构建中才会暴露——新增记录点后请至少构建一次开启记录功能的配置;
- `NaN`/`Inf` 会由 SQLite 存为 `NULL`(`sqlite3_bind_double` 行为),分析时需注意;
- 记录库是调试产物:进程异常退出(未调用 `Destroy()`)时,`synchronous=OFF` 下可能丢失最后若干条记录;
- IntelliSense 可能对 `rdp_record.cpp` 按 `c_cpp_properties.json` 的 C++17 提示(该文件实际按 C++20 编译,`build/<...>/compile_commands.json` 中带有 `/std:c++20`),以实际构建结果为准;
- `third_party/ormpp/` 为上游原样内置(ormpp 0.2.0 / iguana 1.0.5 / SQLite 3.49.1,Apache-2.0 与公有领域),升级方式与校验值见该目录 `UPSTREAM.md`。
+757
View File
@@ -0,0 +1,757 @@
# 航迹起始(Track Init)详细逻辑说明
> 适用范围:`data_process_class_dll/track_init.cpp` / `track_init.h`(类 `Track_Init`),TWS 通道逻辑法航迹起始。
> 关联文件:`kalman.cpp`(滤波与统计距离)、`coor_trans.cpp`(坐标转换)、`track_index_mangement.cpp`(航迹号分配)、`data_process.cpp`(调用与参数)。
> 本文按**当前源码实际行为**梳理,行号均指当前版本文件行号;与 `README.md` 第 8.3 / 9.3 节存在差异之处,见第 12 节。
---
## 目录
1. [模块定位与调用时机](#1-模块定位与调用时机)
2. [数据结构与状态容器](#2-数据结构与状态容器)
3. [处理流程总览](#3-处理流程总览)
4. [阶段一:输入装填与圈数计数](#4-阶段一输入装填与圈数计数)
5. [阶段二:点迹与临时航迹关联](#5-阶段二点迹与临时航迹关联)
6. [阶段三:点迹与航迹头关联](#6-阶段三点迹与航迹头关联)
7. [阶段四:点迹消费与航迹头生成](#7-阶段四点迹消费与航迹头生成)
8. [阶段五:临时航迹转可靠航迹(起批)](#8-阶段五临时航迹转可靠航迹起批)
9. [阶段六:临时航迹消亡](#9-阶段六临时航迹消亡)
10. [起始条件汇总表](#10-起始条件汇总表)
11. [关键算法细节](#11-关键算法细节)
12. [与其他文档/实现的差异](#12-与其他文档实现的差异)
13. [参数与调参](#13-参数与调参)
14. [输出字段映射](#14-输出字段映射)
15. [已知问题与风险点](#15-已知问题与风险点)
16. [代码索引](#16-代码索引)
---
## 1. 模块定位与调用时机
### 1.1 在整体流程中的位置
航迹起始处于 TWS 处理链的**末端**:只有「当圈未被任何可靠航迹关联上」的剩余点迹才会进入起始流程。
```mermaid
flowchart LR
A["data_preprocess<br/>点迹入 Data_buffer"] --> B["dot_coh(model=2)<br/>两帧滑窗凝聚"]
B --> C["track_asso(model=1)<br/>IMM + EKF 点航关联<br/>已用点从 point_recv 删除"]
C --> D["track_die<br/>可靠航迹消亡"]
D --> E["<b>track_init</b><br/>逻辑法航迹起始"]
E --> F["Trust_Track_Output<br/>(起批航迹 + 历史点)"]
```
调用点(`data_process.cpp`):
```cpp
// L130-161,仅 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);
```
### 1.2 调用频率 = 扫描圈
`data_preprocess` 在 `Data_Input[0].Beam_index_aiz == 0` 时返回 1(`data_process.cpp` L51),宿主据此调用 `track_process(model=1)`。
因此 **`track_init_process_logic()` 每次调用对应 TWS 一圈**,`Temp_track::buff_round`(已缓存圈数)正是以「调用次数」为单位累加的。
### 1.3 类接口
| 成员 | 可见性 | 作用 |
|---|---|---|
| `track_init_process_logic(...)` | public | 起始主流程(唯一入口) |
| `reset()` | public | 清空内部点迹缓存 + 复位航迹号分配器 |
| `point_temp_track_asso(...)` | private | 点迹 ↔ ≥2 点临时航迹关联 |
| `point_track_head_asso(...)` | private | 点迹 ↔ 1 点航迹头关联 |
| `tmp_track_to_trust_track(...)` | private | 收集 → 去重 → 起批 → 输出 |
| `tmp_track_die(...)` | private | 临时航迹消亡 |
| `alpha_cal_track_init(...)` | private | 三点折角(度) |
| `track_init_prohibit(...)` | private | 起批屏蔽区判断 |
| `track_index_mangement` | private | 航迹号(1~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`。
---
## 2. 数据结构与状态容器
### 2.1 Temp_track(临时航迹点,`struct.h`)
| 字段 | 含义 | 起始流程中的作用 |
|---|---|---|
| `X[4]` | 4 维状态 **[x, vx, y, vy]** | EKF 一步预测的状态;由两点卡尔曼初始化填充 |
| `P[4][4]` | 状态协方差 | 同上的协方差 |
| `r / azi / vr / height / SNR / RCS / amplitude` | 该点的极坐标测量 | 初始化、输出、条件判据(`azi` 为**弧度**) |
| `CPI_time` / `GNSS_time` | CPI 时间 / GNSS 时间(ms) | ΔT 计算(除 1000 转秒) |
| `d` | 该点关联时的马氏统计距离 | 去重阶段用于「保留更优者」 |
| `buff_round` | **已缓存圈数** | 生命周期与关联时机控制(见 4.2) |
| `asso_flag` | 0 未关联 / 1 已关联(待删除)/ 2 去重淘汰 | 临时状态标记 |
| `pitch_num`、`speed_dim[129]`、`range_dim[8]` | 俯仰波位、十字星数据 | 纯透传 |
| `temp_track_id` / `point_id` / `circle_id` | 临时航迹 ID / 末关联点迹 ID / 处理圈 ID(c3c7e66 新增) | 数据记录链路标识;起批时透传给 `Trust_Track` |
| `temp_track_section_idx` | 临时航迹区号 | 相关赋值已被注释停用,当前不参与逻辑 |
> 字段更名提示(c3c7e66):`snr`→`SNR`、`Amp`→`amplitude`、`T`→`CPI_time`、`Temp_track_section_idx`→`temp_track_section_idx`。阅读早期提交或旧文档时注意对应关系。
>
> ⚠ **下标陷阱**:`Temp_track::X` 是 4 维 `[x, vx, y, vy]`(y 在 index 2),而 `Trust_Track::X` 是 6 维 `[x, vx, ax, y, vy, ay]`(y 在 index 3)。两套结构混用下标必错。
### 2.2 容器语义
| 容器 | 语义 |
|---|---|
| `temp_track`(外层 vector) | 「临时航迹表」:外层每一项 = 一条临时航迹,**下标是不稳定的位置号**(擦除后会重排);内层 vector = 该航迹按时间正序的点序列 |
| `point_process`(`Track_Init` 成员) | 本次调用待处理的点迹工作副本 |
| `point_recv`(`Data_Process` 成员) | TWS 点迹文件:凝聚输出 → 被 `track_asso` 消费 → 剩余点进入 `track_init` → 被清空 |
### 2.3 输入与输出
| 参数 | 方向 | 说明 |
|---|---|---|
| `point_recv` | 输入/输出 | 输入:当圈未被可靠航迹关联的点迹;**输出:被清空**(`track_init.cpp` L118) |
| `trust_track` | 输入/输出 | 可靠航迹表;起批成功的临时航迹 `push_back` 到此 |
| `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`) |
> **输出行数核算**:`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 时不再允许新起批。
---
## 3. 处理流程总览
```mermaid
flowchart TD
S["track_init_process_logic 入口"] --> A1["① 取出 point_recv → point_process"]
A1 --> A2["② temp_track 全部点的 buff_round += 1"]
A2 --> Q{"point_process 空?"}
Q -- 是 --> DIE["⑥ tmp_track_die"]
Q -- 否 --> B["③ point_temp_track_asso<br/>点 ×(≥2 点临时航迹)"]
B --> C["④ point_track_head_asso<br/>点 ×(1 点航迹头)"]
C --> E["⑤ 删除 Use_Flag==1 的点<br/>剩余点 → 新建航迹头(buff_round=1)"]
E --> F["⑦ tmp_track_to_trust_track<br/>收集 → 去重 → 起批 → 输出"]
F --> G["清空 point_process / point_recv"]
G --> DIE
DIE --> R["return 0"]
```
要点顺序(不可颠倒):
1. **buff_round 先自增,再做关联** —— 这正是「同一圈内不重复关联同一条临时航迹」的实现方式(见 4.2)。
2. **先关联 ≥2 点临时航迹,再关联 1 点航迹头** —— 前者优先,但两者**不互斥**:同一圈可同时被两边关联(点迹可以既延续长航迹,又延续航迹头)。
3. **关联结果统一用 `Use_Flag` 标记,最后一次性物理删除** —— 只有 `Use_Flag==0` 的剩余点才会变成新航迹头。
4. **转可靠航迹在航迹头生成之后** —— 因此当圈新建的航迹头(1 点)不参与当圈起批。
5. **消亡在最后执行** —— 且即使当圈无剩余点迹(跳过 ③~⑦)也照常执行。
---
## 4. 阶段一:输入装填与圈数计数
### 4.1 装填(`track_init.cpp` L36-38)
```cpp
for (size_t i=0;i<(*point_recv).size();i++)
point_process.push_back((*point_recv)[i]); // 全量拷贝,不检查 Use_Flag
```
- 上游 `track_asso` 已把关联上的点从 `point_recv` 中删除,所以这里进来的都是「未关联点」;函数内部**不再**用 `Use_Flag` 过滤输入(`Use_Flag` 只用于本次调用内部两阶段关联的标记)。
- 点迹的 `Range` 已在凝聚阶段做过 `[R_MIN=100, R_MAX=100000] m` 量程过滤。
### 4.2 buff_round 自增(L40-47)
```cpp
for (每一条临时航迹 j) (*temp_track)[i][j].buff_round += 1;
```
含义与后果(关键机制):
| 动作 | 效果 |
|---|---|
| 新建点(航迹头 / 新关联点) | `buff_round = 1` |
| 每经过一次 `track_init` 调用(一圈) | 该航迹内**所有**点的 `buff_round` +1;关联判据只看**末点** |
| 关联判据要求 `末点.buff_round >= 2` | 等价于:**上一圈加入的点才能被本圈延续** ⇒ 同一条临时航迹每圈最多吸收一个点(除分裂,见 5.5) |
| 消亡判据 `末点.buff_round > 2` | 等价于:**连续 2 圈没有新点**就消亡(允许中间空 1 圈,最大关联间隔 2 圈) |
> 时间戳只在关联时用 `ΔT = (T_point − T_末点)/1000` 秒表示,**不参与生命周期判断**;生命周期完全由「圈数」驱动。这也意味着临时航迹的存活时间与扫描周期成正比,与航迹的绝对时间无关。
**生命周期时间轴(以 scan k 为起点)**
```text
scan k : 新点加入 → 末点 buff_round = 1 (本圈不能再关联)
scan k+1 : 自增 → 2 → 允许关联;若关联成功 → 新点 1;若失败 → 2 > 2 不成立,存活
scan k+2 : 自增 → 3 → 仍然允许关联;若成功 → 新点 1;若失败 → 3 > 2 成立,消亡
```
---
## 5. 阶段二:点迹与临时航迹关联
函数:`point_temp_track_asso()`(L145-304)。双重循环:外层点迹、内层临时航迹,**无一对一竞争约束**。
### 5.1 参与资格(L164)
```cpp
if( (*temp_track)[j].size() > 1 && (*temp_track)[j][L-1].buff_round >= 2 )
```
- 只处理 **≥2 点** 的临时航迹(1 点的是航迹头,留给阶段三);
- 末点必须来自历史圈次。
### 5.2 关联判据(L167-221)
设点迹量测(`Z[3]`):
$$
Z=\begin{bmatrix} r_p \\ \theta_p \\ v_{r,p}\end{bmatrix},\qquad
\mathbf{X}_{k-1}=\text{末点状态}[x,v_x,y,v_y],\qquad
\Delta T=\frac{T_p-T_{\text{末点}}}{1000}\ \text{(s)}
$$
**前置硬门限(L191)**:`|r_p − r_末点| >= 5` —— 距离差小于 5 m 的点直接不参与(连 d 都不算)。
**统计距离(L194-195)**:调用 `kalman::d_cal_track_init_EKF(Z, X, P, ΔT, prt, freq_ind)` 得到 3 维量测的马氏距离平方 `d`(详见 11.2)。
**折角(L199-205)**:取末点前一点 → 末点 → 新点,计算折角 `α`(度,详见 11.3)。
**三条件同时成立(L221)**:
$$
\boxed{\;d^2 \le \text{TRACK\_START\_THRESHOLD}^2=64
\quad\wedge\quad
\alpha < \text{ALPHA\_START}=60^\circ
\quad\wedge\quad
v_{r,p}\cdot v_{r,\text{末点}} > 0\;}
$$
> ⚠ 由于 `d_cal_track_init_EKF` 返回的 `d` **本身就是马氏距离平方**,而判据又写作 `d*d <= 64`(即 `d <= 8`),实际生效的门限是「马氏距离平方 ≤ 8」。3 自由度卡方分布 95% 分位为 7.815,因此该写法恰好给出约 95.4% 的置信门限(详见 11.2 与 15.6)。
### 5.3 记录关联(L223-231)
命中后向 `asso_info` 记录 `(point_idx=i+1, track_idx=j+1, d)`,同时:
- `末点.asso_flag = 1` → 标记**原航迹**待删除;
- `point_process[i].Use_Flag = 1` → 标记该点已被消费。
索引在整个扫描循环中保持稳定(循环期间不修改两个容器),因此后面的 1-based 索引回查是安全的。
### 5.4 生成新临时航迹(L237-281)
对每一条关联记录:**复制原航迹的全部 L 个点**(并把各点 `asso_flag` 清 0)、再**追加关联上的新点**,作为一条**新的临时航迹** push 到 `temp_track` 末尾。
新点字段:极坐标测量、幅度、SNR、RCS、时间、俯仰波位、十字星数据全套透传;`asso_flag=0`、`buff_round=1`、`d = asso_info[i].d`;
状态与协方差由 **两点卡尔曼初始化** 得到(L268-279):
```cpp
Z0 = (末点.r·cos(末点.azi), 末点.r·sin(末点.azi)); // 原航迹末点
Z1 = (点.Range·cos(点.Azimuth), 点.Range·sin(点.Azimuth));
T = (点.CPI_Time − 末点.T)/1000.0;
Kalman.kalman_filter_init_2dots(Z0, Z1, T, X, P); // 4 维 CV 初始化
```
### 5.5 航迹分裂(重要特性)
关联循环**不做一对一分配**:
- 一个点若满足多条临时航迹的判据,会为**每一条**各自复制出一条新航迹 —— 一条母航迹可以派生多条子航迹;
- 一条临时航迹若被多个点同时命中,也会**各自复制**成多条子航迹(每个子航迹只带一个点);
- 因此同一圈内可能产生大量同源临时航迹,靠第 8.3 节的**去重**收敛。
### 5.6 删除被关联的原航迹(L283-303)
遍历 `temp_track`,凡 `size()>=2` 且 **末点 `asso_flag==1`** 者整体 `erase`(每次擦除后 `Iter = begin()` 重扫,O(n²) 但结果正确;1 点航迹不在此处理)。新生成的子航迹末点 `asso_flag==0`,不会被误删。
---
## 6. 阶段三:点迹与航迹头关联
函数:`point_track_head_asso()`(L306-445)。这是**两点起始**:航迹头 + 1 个点 → 2 点临时航迹。
### 6.1 参与资格(L325)
```cpp
if((*temp_track)[j].size()==1 && (*temp_track)[j][0].buff_round >= 2)
```
只处理 **1 点**且该点来自历史圈次的航迹头。
### 6.2 关联判据(L328-372)
点迹与航迹头都转直角坐标后取**平面距离**:
$$
x_p = r_p\cos\theta_p,\quad y_p=r_p\sin\theta_p;\qquad
x_h=\text{头.X}[0],\quad y_h=\text{头.X}[2]
$$
$$
dis=\sqrt{(x_p-x_h)^2+(y_p-y_h)^2},\qquad \Delta T=\frac{T_p-T_h}{1000}\ \text{(s)}
$$
**当前生效的判据(L368-372)**:
$$
\boxed{\;
dis \le V_{max}\Delta T
\;\wedge\;
dis \ge V_{min}\Delta T
\;\wedge\;
v_{r,p}\cdot v_{r,h} > 0
\;\wedge\;
dis \ge |v_{r,p}|\,\Delta T
\;\wedge\;
dis \ge |v_{r,h}|\,\Delta T\;}
$$
各条件的物理含义:
| 条件 | 含义 |
|---|---|
| `dis ≤ V_max·ΔT` | 平均速度不超过最大速度(`V_max = Work_Parameter.V_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` 注释里)。
**ΔT 语义**:判据隐含要求 `ΔT > 0`(若 `ΔT < 0`,则 `dis ≤ 负数` 必假)。`ΔT == 0` 时仅在 `dis == 0`(同时间同位置的点)才可能成立,属退化情形。
**与文档差异**:源码中被注释停用的旧判据按距离分段收紧门限(`r<1000` 时半速、中远程 `r<2000` 时 1/2~1/3 速),见第 12 节。
### 6.3 命中后的处理(L374-427)
- 记录 `(point_idx, track_idx)`,标记 `头.asso_flag=1`、`point_process[i].Use_Flag=1`;
- 逐条生成 2 点临时航迹:**第 1 点 = 原航迹头**(`asso_flag` 清 0),**第 2 点 = 新点**(全套测量透传,`buff_round=1`,`asso_flag=0`,**`d` 未赋值,保持 0**);
- 同样调用 `kalman_filter_init_2dots` 得到 4 维状态/协方差(`Z0` = 头点、`Z1` = 新点,`T = (点.CPI_Time − 头.T)/1000`)。
### 6.4 删除被关联的航迹头(L429-443)
凡 `[0].asso_flag==1` 的 1 点航迹头整条 `erase`(同样每次重扫)。
注意:本阶段遍历的是**全部** `temp_track`,其中由阶段二新建的 ≥2 点航迹因 `size()!=1` 不会被误处理。
---
## 7. 阶段四:点迹消费与航迹头生成
### 7.1 删除已消费点迹(L57-70)
```cpp
if((*Iter).Use_Flag==1) { point_process.erase(Iter); Iter = point_process.begin(); }
```
物理删除所有被阶段二或阶段三消费的点(两阶段共用同一 `Use_Flag`)。
> 结果:一个点迹**要么**参与延续已有临时航迹/航迹头(可同时延续两者),**要么**成为新航迹头,二者互斥。这保证不会「用同一个点既延续又起新头」。
### 7.2 剩余点回写(L72-75,等效空操作)
```cpp
std::vector<PointRecv>().swap((*point_recv));
for (...) (*point_recv).push_back(point_process[i]); // 回写未被使用的点
```
紧接着 L117-118 又把 `point_process` 与 `point_recv` 双双清空,因此这段回写**不产生任何对外效果**(`point_recv` 最终必然为空),属历史残留代码。
### 7.3 剩余点转航迹头(L77-114)
对每个剩余点构造 1 点 `Temp_track`:
| 字段 | 取值 |
|---|---|
| `r / azi / height / amplitude / vr / SNR / RCS` | 点迹对应量(`azi` 为弧度) |
| `pitch_num / speed_dim / range_dim` | 透传 |
| `X[0] = Range·cos(Azimuth)`、`X[1]=0`、`X[2] = Range·sin(Azimuth)`、`X[3]=0` | 位置赋值,速度置 0(**注意 4 维布局**) |
| `CPI_time`、`GNSS_time` | 点迹时间 |
| `temp_track_id = temp_track_id++`、`point_id`、`circle_id` | 取自该点迹(记录链路标识,c3c7e66 新增) |
| `asso_flag = 0`、`buff_round = 1` | 生命周期起点 |
| `d` | 未赋值 → 0 |
---
## 8. 阶段五:临时航迹转可靠航迹(起批)
函数:`tmp_track_to_trust_track()`(L458-671),四步。
### 8.1 步骤 1:收集候选(L467-499)
```cpp
int L = (*Iter).size();
double range = (*Iter)[L-1].r;
double azi = (*Iter)[L-1].azi/PI*180; // 转度,供屏蔽区比较
if( (L <= 10 && L >= Work_Parameter.track_start_point_num) && track_init_prohibit(range, azi, Work_Parameter)==0 )
{ 复制到 Track_to_start; 从 temp_track 中 erase; }
else
Iter++;
```
**入选条件**:
1. **长度**:`track_start_point_num ≤ L ≤ 10`(`track_start_point_num` 由宿主经 `track_process_parameters_initial` 传入,接口层校验范围 **3~9**,`data_process.cpp` L208);
2. **不在起批屏蔽区**内(用**末点**的距离/方位判断,见 8.5)。
命中即从 `temp_track` 摘出(先复制后删除,`Track_to_start` 成为候选表)。
> 结果:**一旦长度达到阈值且不落在屏蔽区,当圈即起批**(没有"连续 M 圈满足才起批"的确认期)。若因屏蔽区被拦住,航迹会继续在 `temp_track` 中增长,直到离开屏蔽区(此时 `L > track_start_point_num`,见 8.3 的副作用)或达到 `L=10` 被判消亡。
### 8.2 步骤 2:同源去重(L504-550)
```cpp
int L = Work_Parameter.track_start_point_num; // 注意:此 L 是"起批点数",不是航迹长度
for (i: 0 .. size-2) for (j: i+1 .. size-1) {
if (两者都未被标记 asso_flag==2) {
if (某条长度 < 3) continue; // 防御性判断(实际不会触发)
if (前三点中存在一点 "T 与 r 完全相同") { // 硬编码比较点 0/1/2
if (Track_to_start[i][L-1].d < Track_to_start[j][L-1].d) j 标记 asso_flag=2;
else i 标记 asso_flag=2;
}
}
}
// 然后统一删除 [0].asso_flag==2 的航迹
```
- **同源判定**:两条候选航迹的前三个点中,只要有一个点的 `T` 与 `r` 完全相等(同一点迹被复制而来),就视为同源重复;
- **择优准则**:比较 `[L-1].d`(第 `track_start_point_num` 个点的关联统计距离),**保留 d 更小者**;
- **标记位置**:只写 `[0].asso_flag = 2`,最后统一擦除;
- 去重仅看**前三点**,且无论航迹实际多长都用固定下标 0/1/2。
### 8.3 步骤 3:起批与构造可靠航迹(L552-667)
```cpp
if (Track_to_start[i].size() < 3) continue; // 防御
if (trust_track->size() < MAX_TRACK_INDEX) { // 500 条上限
int index = track_index_mangement.track_ind_get(trust_track);
if (index > 0) { ... }
}
```
**三点卡尔曼初始化**(L563-571,注意使用的是 `L-3, L-2, L-1`,即**最后三个点**):
```cpp
Z0 = 极坐标→直角(点[L-3]); Z1 = (点[L-2]); Z2 = (点[L-1]);
T1 = (点[L-2].T − 点[L-3].T)/1000.0; T2 = (点[L-1].T − 点[L-2].T)/1000.0;
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` 时两者一致;当因屏蔽区滞留使长度 > 阈值时,输出的历史点里**不包含**用于初始化的那三个点。
**Trust_Track 字段构造(L573-608)**:
| 字段 | 赋值 |
|---|---|
| `X[6] / P[6][6]` | 三点初始化结果 |
| `X1/X2/X3 = X`、`P1/P2/P3 = P` | 三个 IMM 子模型同状态、同协方差初始化 |
| `u[0..2] = 0.3333` | 模型概率均分 |
| `Track_Index` | `track_ind_get()` 分配的航迹号 |
| `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 新增) |
| `point_flag = 1` | 实点 |
| `Track_Mode = 0` | TWS |
| `Target_Type = 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(**不起批**,该候选被静默丢弃)。
### 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 节);
- 之后该航迹的后续更新由 `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])
return 1; // 位于屏蔽区,禁止起批
return 0;
```
- 判据是**距离-方位矩形区域**(`azi` 已转成度),最多 30 个(`RadarPara` 数组长度;数量未做上限校验);
- 采用**严格不等号**:恰好落在边界上的点不算屏蔽;
- 判断用**末点**极坐标,不使用高度。
---
## 9. 阶段六:临时航迹消亡
函数:`tmp_track_die()`(L673-697):
```cpp
if (n <= 0) erase; // 空航迹
if ((*Iter)[n-1].buff_round > 2 || n >= 10) erase;
```
| 消亡条件 | 含义 |
|---|---|
| 序列为空 | 防御性清理 |
| 末点 `buff_round > 2` | 连续 2 圈没有新点(允许空 1 圈) |
| 长度 `n >= 10` | 达到上限仍未完成起批(例如长期滞留屏蔽区) |
消亡**不输出任何信息**(与可靠航迹消亡不同,起批失败的临时航迹对宿主不可见)。
---
## 10. 起始条件汇总表
### 10.1 条件链(一条 3 点起批航迹的完整判据)
| 阶段 | 判据(源码位置) |
|---|---|
| **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`;末点不在起批屏蔽区;同源去重后保留;`trust_track.size() < 500` 且成功分配航迹号 |
| **5. 消亡**(L686) | `末点.buff_round > 2`(连续 2 圈无新点)或 `长度 ≥ 10` |
### 10.2 门限汇总
| 判据 | 数值 | 来源 | 可调 |
|---|---|---|---|
| 最大速度 `V_MAX` | 运行时 | `RadarPara.V_MAX` | ✅ |
| 最小速度 `V_MIN` | 运行时 | `RadarPara.V_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` | ✅ |
| 临时航迹长度上限 | 10(硬编码) | L484 / L686 | ❌ |
| 允许丢圈数 | 1 圈(`buff_round > 2` 消亡) | L686 | ❌ |
| 航迹头上限等待 | 2 圈 | L686(1 点航迹同判据) | ❌ |
| 起批屏蔽区 | ≤30 个矩形 | `RadarPara.track_prohibite_area_*` | ✅ |
| 航迹数上限 | 500 | `MAX_TRACK_INDEX` | 编译期 |
---
## 11. 关键算法细节
### 11.1 两点卡尔曼初始化(`kalman.cpp` L18-58)
输入两个直角坐标量测 `Z0, Z1` 与时间差 `T`(秒):
$$
X=\begin{bmatrix}x_1\\ (x_1-x_0)/T\\ y_1\\ (y_1-y_0)/T\end{bmatrix},
\qquad
P_{axis}=\begin{bmatrix}R & R/T\\ R/T & 2R/T^2\end{bmatrix}
$$
其中 `R` 取 `Z1` 处的**转换量测协方差**(极坐标→直角坐标,见 `README.md` 9.1 节),x/y 两轴独立填充 `P[4][4]`。
> 风险:`T → 0` 时速度与协方差同时发散,函数无除零保护(对应 BUG-18);临时航迹的两点初始化 `T` 来自相邻两圈,实际由时间戳决定,若宿主给出重复时间戳会异常。
### 11.2 EKF 统计距离(`kalman.cpp` L615-693)
对 4 维状态做 CV 一步预测(`F = [[1,T,0,0],[0,1,0,0],[0,0,1,T],[0,0,0,1]]`,过程噪声 `Q = 0.03²·I`,`G=[T²/2, T]ᵀ`):
$$
\hat X_{k|k-1}=F X,\qquad P_{k|k-1}=FPF^{T}+GQG^{T}
$$
非线性量测函数(距离-方位-径向速度):
$$
h(\hat X)=\begin{bmatrix}\sqrt{x^2+y^2}\\ \operatorname{atan2}(y,x)\bmod 2\pi\\ -\dfrac{x v_x+y v_y}{\sqrt{x^2+y^2}}\end{bmatrix},
\qquad H=\left.\frac{\partial h}{\partial X}\right|_{\hat X_{k|k-1}}
$$
量测噪声 `R = diag(σ_r², σ_a², σ_v²) = diag(10², 0.02², 2²)`,新息协方差 `S = H P Hᵀ + R`,残差做两项修正:
- **方位环绕**:`ν_θ = wrapToPi(θ_p − θ_pred)`,归一化到 `[−π, π)`;
- **多普勒盲速折叠**:`ν_v ← ν_v − round(ν_v / v_bind)·v_bind`,其中
$$
v_{bind}=\frac{150000}{f_{GHz}\cdot PRI_{\mu s}}\ \text{(m/s)},
\qquad f_{GHz}=9.2+0.02\cdot f_{ind}
$$
(`PRI ≤ 0` 或 `f ≤ 0` 时返回安全值 1.0;调用方约定 PRI 单位 µs,不允许为 0。)
返回马氏距离平方:
$$
d=\nu^{T}S^{-1}\nu,\qquad \nu=Z-h(\hat X_{k|k-1})
$$
**调用参数来源**:`prt = point.PRF_index`(实际存的是 `DataRev.PRI`,单位 µs);`freq_ind = point.Freq_index`。
### 11.3 折角计算(L448-456)
$$
\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°` 表示允许的航向偏折。
> 无 `acos` 定义域钳位:当两点重合(分母为 0)或浮点误差使比值略超 ±1 时结果为 `NaN`,`NaN < 60` 恒为 false → 表现为「该点无法关联」,不崩溃。
---
## 12. 与其他文档/实现的差异
### 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 描述(更新前) | 当前源码 |
|---|---|---|
| 点-航迹头判据 | 按距离分段:近程 `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` 是起批点数,不是航迹长度) |
| use_flag 过滤 | "track_init 跳过 Use_Flag==1 的点" | 上游 `track_asso_process()` 已删除 `Use_Flag==1` 的点迹,`track_init` 入口(L59-70)也会再剔除一次,两道处理叠加,描述成立 |
### 12.2 与 `track_init_direct_tracking.cpp`(引导跟踪版,未启用)的差异
| 项 | `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`(严格相等) |
| 去重 | 有同源去重 | 无 |
| 屏蔽区 | 有 | 无 |
| 航迹号 | `track_ind_get()` 分配 | 外部传入 `track_ID` |
| 消亡阈值 | `buff_round > 2` | `buff_round > 1`(更严格) |
| 输出模式 | `Track_Mode = 0`(TWS) | `Track_Mode = 1`(TAS) |
---
## 13. 参数与调参
### 13.1 编译期宏(`parameters.h`)
| 宏 | 相扫/机扫 | 含义 | 调大效果 |
|---|---|---|---|
| `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 精度) | — |
### 13.2 运行期参数(`RadarPara`)
| 字段 | 影响 |
|---|---|
| `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.3 调参直觉
- **虚警多、起批太容易**:先降 `TRACK_START_THRESHOLD`(8 → 5 左右),再降 `ALPHA_START`;必要时提高 `track_start_point_num`。
- **真实目标起批慢**:提高 `TRACK_START_THRESHOLD` / `ALPHA_START`,或降低 `track_start_point_num`(最小 3)。
- **静止/低速杂波起批**:提高 `V_MIN`。
- **某方位扇区/距离段禁止起批**(如地物、干扰区):配置起批屏蔽区,比调门限更精准。
---
## 14. 输出字段映射
对每条新起批航迹的每个历史点 `j = 0 .. track_start_point_num-1`(L614-664):
| 输出字段 | 来源 | 备注 |
|---|---|---|
| `Track_Index` | 新分配的航迹号 | 同一行内所有点相同 |
| `Point_Sum` | `= track_start_point_num` | 与 `track_asso` 输出的 `Point_Sum=1` 可作区分 |
| `Range` / `Azimuth` | 点的 `r` / `azi`(→度) | 极坐标 |
| `Elevation` | `asin(h/r)`(度,钳位 ±1) | BUG-27 修复后的定义域钳位 |
| `z` | 点的 `height` | |
| `Range_V` | `sqrt(X[1]² + X[3]²)` = `sqrt(vx²+vy²)` | 速度**模值**(非径向速度) |
| `Direction_Angle` | `atan2(vy, vx)`(→度, `[0,360)`) | 航向角 |
| `x / v_x / y / v_y` | 点的 `X[0..3]` | 注意 4 维布局 |
| `range_point / azi_point / elev_point / vr_point` | 点的原始测量 | `point_type=0` |
| `Amplitude / track_snr / track_rcs` | 点的 `amplitude / SNR / RCS` | |
| `pitch_num / speed_dim / range_dim` | 点透传 | |
| `track_time` | `CPI_time/1000`(秒) | |
| `GNSS_time` | 点透传 | |
| `Flag_Point` | 恒为 **1**(实点) | |
| `Track_Mode` | 恒为 **0**(TWS) | |
| **未赋值** | `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` L129 / `track_asso_tas.cpp` L93),起批输出不写该字段。该字段的判据已由 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 输出行中它们保持宿主原值 |
---
## 15. 已知问题与风险点
> 以下为阅读源码得到的观察,**不代表已确认的缺陷**,部分为有意保留的历史行为;修改前请结合实测数据评估。
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`,共享前三点但后续分叉的两条航迹会被误判为同源。
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` 时务必记得这一层。
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` 摘除 → 起批机会永久丢失。
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`。
---
## 16. 代码索引
### 16.1 `track_init.cpp`
| 行号 | 内容 |
|---|---|
| L11-143 | `track_init_process_logic` 主流程 |
| L36-38 | 装填点迹 |
| L40-47 | `buff_round` 自增 |
| L49 | `if(point_process.size()>0)` 主分支 |
| L52 / L55 | 两阶段关联调用 |
| L57-70 | 删除已消费点迹 |
| L72-75 | 点迹回写(等效空操作) |
| L77-114 | 剩余点 → 航迹头 |
| L117-118 | 清空 `point_process` / `point_recv` |
| L121 | 转可靠航迹调用 |
| L126 | 临时航迹消亡调用 |
| L145-304 | `point_temp_track_asso` |
| L164 | ≥2 点 + `buff_round≥2` 资格 |
| L191 | `abs(Δr) ≥ 5 m` 门限 |
| L194-195 | EKF 统计距离 `d` |
| L199-205 | 折角 `α` |
| L221 | 三条件判据 |
| L237-281 | 生成新临时航迹(两点初始化) |
| L283-303 | 删除被关联的原航迹 |
| L306-445 | `point_track_head_asso` |
| L325 | 1 点 + `buff_round≥2` 资格 |
| L361-372 | 门限判据(含停用的旧分支注释) |
| L388-427 | 生成 2 点临时航迹 |
| L429-443 | 删除被关联的航迹头 |
| L448-456 | `alpha_cal_track_init` |
| L458-671 | `tmp_track_to_trust_track` |
| L484 | 起批长度 + 屏蔽区条件 |
| L504-550 | 同源去重 |
| L552-667 | 起批与输出 |
| L563-571 | 三点卡尔曼初始化 |
| L573-604 | `Trust_Track` 构造 |
| L612-664 | 输出历史点 |
| L673-697 | `tmp_track_die` |
| L699-712 | `track_init_prohibit` |
| L714-723 | 构造 / `reset()` |
### 16.2 依赖
| 文件 | 函数 | 用途 |
|---|---|---|
| `kalman.cpp` L18-58 | `kalman_filter_init_2dots` | 两点(4 维)初始化 |
| `kalman.cpp` L125-194 | `kalman_filter_init_3dots` | 三点(6 维)初始化 |
| `kalman.cpp` L615-693 | `d_cal_track_init_EKF` | 起始关联统计距离 |
| `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]` |
+12 -12
View File
@@ -1,19 +1,19 @@
# 雷达数据处理项目修改说明
## 概述
新增一个数据记录调试功能,将各类结构体数据写入到数据库中,供后续查询分析。
本次主要修改TAS波束控制逻辑,
原有逻辑为:
- 维护一个tas_target_queue队列,队列中每个元素可以保存一个TAS跟踪目标
- 每次调用Beam_Ctrl()函数时,判断队列首个元素是否为空(empty_flag),若不为空,则输出TAS跟踪波束信息,否则不输出
- 将队列进行循环移位,队列第一个目标移动至队尾,下一次Beam_Ctrl()调用将会跟踪下一个目标
## 具体需求
原有逻辑的问题:
- 原有逻辑下,TAS跟踪目标的数据率(目标信息更新频率)与波束时间相关,波束时间越短,则调用Beam_Ctrl()的频率越高,TAS目标的数据率也越高
* 本次只设计功能框架,具体存储哪些数据不在此次考虑范围内;
* 数据库使用SQLite;
* 使用ormpp库实现结构体到数据表的隐射,项目地址:https://github.com/qicosmos/ormpp;
* 数据记录功能在release版可以屏蔽,不做任何记录,避免交付版本的性能损失;
* 数据分类记录,所有数据存储到一个db文件中,不同的数据存储到不同的表中;
* 使用宏或其他方式,在代码中添加记录点,将指定数据写入数据库;
* 能够快速添加新的结构体数据,便于后续增加需要记录的数据;
* 作为一个调试功能,不必使用异步写入;
新的逻辑为保证TAS目标的数据率不变:
- 维护一个tas_target_queue队列,队列中每个元素可以保存一个TAS跟踪目标
- 每次调用Beam_Ctrl()函数时,判断队列首个元素是否为空(empty_flag),若不为空,则再判断目标的航迹时间(T_track)和当前最新时间戳(latest_timestamp)的差值,若差值大于1 / DATA_RATE_TAS,则输出TAS跟踪波束信息,否则不输出
- 将队列进行循环移位,队列第一个目标移动至队尾,下一次Beam_Ctrl()调用将会跟踪下一个目标
## 其他
注意:在RadarPara结构体中新增一项:DATA_RATE_TAS(TAS模式数据率),由dll调用者输入该参数的值(track_process_parameters_initial()),上述逻辑中DATA_RATE_TAS使用Work_Parameter中的DATA_RATE_TAS参数值,而不是使用parameters.h中的DATA_RATE_TAS宏定义值。
* 如果有任何不明确或者有问题的地方,可以直接向我提问;
+201
View File
@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+35
View File
@@ -0,0 +1,35 @@
# 第三方库来源说明(ormpp / iguana / SQLite)
本目录为**按上游原样内置**的第三方源码(保留上游字节,含换行符,不做编码/格式转换),与源码目录下内置的 Eigen 3.3.7 同类处理:仓库自带依赖,离线可配置、可交付。
| 内容 | 本目录位置 | 上游 | 版本 / 标识 |
|---|---|---|---|
| ormpp(Modern C++ ORM 头文件库) | `ormpp/*.hpp` | <https://github.com/qicosmos/ormpp> | commit `acf23989f8e40f2c8671eaf209d014164f2d6319`(master,2026-08-29);`ormpp/version.hpp` = 0.2.0 |
| iguana(编译期反射,随 ormpp 内置) | `iguana/**` | 随 ormpp 内置;独立仓库 <https://github.com/qicosmos/iguana> | `iguana/version.hpp` = 1.0.5 |
| SQLite amalgamation | `thirdparty/sqlite3/{sqlite3.c,sqlite3.h,sqlite3ext.h}` | 随 ormpp 内置;<https://sqlite.org> | SQLite 3.49.1(`sqlite3.h` 的 `SQLITE_VERSION`),公共领域(Public Domain) |
| frozen(iguana 的 constexpr 容器依赖,随 ormpp 内置) | `frozen/**` | 随 ormpp 内置;独立仓库 <https://github.com/serge-sans-paille/frozen> | 18 个文件 / 86,136 字节(`iguana/field_reflection.hpp` 需要 `frozen/string.h`、`frozen/unordered_map.h`) |
| 许可证 | `LICENSE`、`iguana/LICENSE` | — | 均为 Apache License 2.0;与上游 git blob 逐字节一致(`261eeb9e…` / `8dada3ed…`) |
## 复现步骤(重新获取/升级时执行)
```bat
git -c core.autocrlf=false clone --depth 1 https://github.com/qicosmos/ormpp.git <tmp-ormpp>
git -c core.autocrlf=false clone --depth 1 https://github.com/qicosmos/iguana.git <tmp-iguana>
:: 仅拷贝以下内容到本目录(其余 tests/example/cfg/docs/lang/shell.c 等均不需要):
:: <tmp-ormpp>\ormpp\*.hpp + ormpp\pg_types.h -> third_party\ormpp\ormpp\
:: <tmp-ormpp>\iguana\ -> third_party\ormpp\iguana\
:: <tmp-ormpp>\frozen\ -> third_party\ormpp\frozen\ (iguana 依赖)
:: <tmp-ormpp>\thirdparty\sqlite3\sqlite3.c|sqlite3.h|sqlite3ext.h -> third_party\ormpp\thirdparty\sqlite3\
:: <tmp-ormpp>\LICENSE -> third_party\ormpp\LICENSE
:: <tmp-iguana>\LICENSE -> third_party\ormpp\iguana\LICENSE
```
注意:本机全局 `core.autocrlf=true` 会把源码转成 CRLF(`sqlite3.c` 由 9,461,193 字节变为 9,728,476 字节),因此克隆时必须显式 `-c core.autocrlf=false`。校验方式:与上游 blob 大小比对(`sqlite3.c` = 9,461,193、`dbng.hpp` = 11,681、`iguana/common.hpp` = 45,850)。
## 使用方式与约束
- **只被**可选目标 `rdp_record` 使用(见根目录 `CMakeLists.txt` 的 `RDP_DATA_RECORD` 选项);该目标以 **C++20** 编译(ormpp 的 `cmake/build.cmake` 强制 `CMAKE_CXX_STANDARD 20`),**雷达处理代码仍为 C++17**。
- 包含路径按上游约定给全(4 个):`third_party/ormpp`(`iguana/...`)、`third_party/ormpp/iguana`、`third_party/ormpp/ormpp`(`dbng.hpp`、`sqlite.hpp`)、`third_party/ormpp/thirdparty/sqlite3`(`sqlite3.h`)。
- 告警处理:`sqlite3.c` 以 `/w` 单独编译;首个包含第三方头的本项目文件(`data_process_class_dll/rdp_record.cpp`)用 `#pragma warning(push, 0)` / `(pop)` 隔离模板告警——这两处是 README 4.5「0 告警」的唯一例外,范围仅限本目录代码。
- **升级方式**:整目录替换为新的上游快照,同步更新本文件的 commit / 版本号 / blob 校验值,然后必须重新验证:`-DRDP_DATA_RECORD=ON` 构建通过(0 告警)+ 记录功能写入校验(见 `docs/data_record_debug.md`)。
+12
View File
@@ -0,0 +1,12 @@
target_sources(frozen-headers INTERFACE
"${prefix}/frozen/algorithm.h"
"${prefix}/frozen/map.h"
"${prefix}/frozen/random.h"
"${prefix}/frozen/set.h"
"${prefix}/frozen/string.h"
"${prefix}/frozen/unordered_map.h"
"${prefix}/frozen/unordered_set.h"
"${prefix}/frozen/bits/algorithms.h"
"${prefix}/frozen/bits/basic_types.h"
"${prefix}/frozen/bits/elsa.h"
"${prefix}/frozen/bits/pmh.h")
+203
View File
@@ -0,0 +1,203 @@
/*
* Frozen
* Copyright 2016 QuarksLab
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#ifndef FROZEN_LETITGO_ALGORITHM_H
#define FROZEN_LETITGO_ALGORITHM_H
#include "frozen/bits/basic_types.h"
#include "frozen/bits/version.h"
#include "frozen/string.h"
namespace frozen {
// 'search' implementation if C++17 is not available
// https://en.cppreference.com/w/cpp/algorithm/search
template <class ForwardIterator, class Searcher>
ForwardIterator search(ForwardIterator first, ForwardIterator last,
const Searcher &searcher) {
return searcher(first, last).first;
}
// text book implementation from
// https://en.wikipedia.org/wiki/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm
template <std::size_t size>
class knuth_morris_pratt_searcher {
bits::carray<std::ptrdiff_t, size> step_;
bits::carray<char, size> needle_;
static constexpr bits::carray<std::ptrdiff_t, size> build_kmp_cache(
char const (&needle)[size + 1]) {
std::ptrdiff_t cnd = 0;
bits::carray<std::ptrdiff_t, size> cache;
cache.fill(-1);
for (std::size_t pos = 1; pos < size; ++pos) {
if (needle[pos] == needle[cnd]) {
cache[pos] = cache[cnd];
cnd += 1;
}
else {
cache[pos] = cnd;
cnd = cache[cnd];
while (cnd >= 0 && needle[pos] != needle[cnd]) cnd = cache[cnd];
cnd += 1;
}
}
return cache;
}
public:
constexpr knuth_morris_pratt_searcher(char const (&needle)[size + 1])
: step_{build_kmp_cache(needle)}, needle_(needle) {}
template <class ForwardIterator>
constexpr std::pair<ForwardIterator, ForwardIterator> operator()(
ForwardIterator first, ForwardIterator last) const {
std::size_t i = 0;
ForwardIterator iter = first;
while (iter != last) {
if (needle_[i] == *iter) {
if (i == (size - 1))
return {iter - i, iter - i + size};
++i;
++iter;
}
else {
if (step_[i] > -1) {
i = step_[i];
}
else {
++iter;
i = 0;
}
}
}
return {last, last};
}
};
template <std::size_t N>
constexpr knuth_morris_pratt_searcher<N - 1> make_knuth_morris_pratt_searcher(
char const (&needle)[N]) {
return {needle};
}
// text book implementation from
// https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore%E2%80%93Horspool_algorithm
template <std::size_t size>
class boyer_moore_searcher {
using skip_table_type = bits::carray<std::ptrdiff_t, sizeof(char) << 8>;
using suffix_table_type = bits::carray<std::ptrdiff_t, size>;
skip_table_type skip_table_;
suffix_table_type suffix_table_;
bits::carray<char, size> needle_;
constexpr auto build_skip_table(char const (&needle)[size + 1]) {
skip_table_type skip_table;
skip_table.fill(size);
for (std::size_t i = 0; i < size - 1; ++i) skip_table[needle[i]] -= i + 1;
return skip_table;
}
constexpr bool is_prefix(char const (&needle)[size + 1], std::size_t pos) {
std::size_t suffixlen = size - pos;
for (std::size_t i = 0; i < suffixlen; i++) {
if (needle[i] != needle[pos + i])
return false;
}
return true;
}
constexpr std::size_t suffix_length(char const (&needle)[size + 1],
std::size_t pos) {
// increment suffix length slen to the first mismatch or beginning
// of the word
for (std::size_t slen = 0; slen < pos; slen++)
if (needle[pos - slen] != needle[size - 1 - slen])
return slen;
return pos;
}
constexpr auto build_suffix_table(char const (&needle)[size + 1]) {
suffix_table_type suffix;
std::ptrdiff_t last_prefix_index = size - 1;
// first loop
for (std::ptrdiff_t p = size - 1; p >= 0; p--) {
if (is_prefix(needle, p + 1))
last_prefix_index = p + 1;
suffix[p] = last_prefix_index + (size - 1 - p);
}
// second loop
for (std::size_t p = 0; p < size - 1; p++) {
auto slen = suffix_length(needle, p);
if (needle[p - slen] != needle[size - 1 - slen])
suffix[size - 1 - slen] = size - 1 - p + slen;
}
return suffix;
}
public:
constexpr boyer_moore_searcher(char const (&needle)[size + 1])
: skip_table_{build_skip_table(needle)},
suffix_table_{build_suffix_table(needle)},
needle_(needle) {}
template <class ForwardIterator>
constexpr std::pair<ForwardIterator, ForwardIterator> operator()(
ForwardIterator first, ForwardIterator last) const {
if (size == 0)
return {first, first + size};
ForwardIterator iter = first + size - 1;
while (iter < last) {
std::ptrdiff_t j = size - 1;
while (j > 0 && (*iter == needle_[j])) {
--iter;
--j;
}
if (*iter == needle_[0])
return {iter, iter + size};
iter += std::max(skip_table_[*iter], suffix_table_[j]);
}
return {last, last + size};
}
};
template <std::size_t N>
constexpr boyer_moore_searcher<N - 1> make_boyer_moore_searcher(
char const (&needle)[N]) {
return {needle};
}
} // namespace frozen
#endif
+240
View File
@@ -0,0 +1,240 @@
/*
* Frozen
* Copyright 2016 QuarksLab
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#ifndef FROZEN_LETITGO_BITS_ALGORITHMS_H
#define FROZEN_LETITGO_BITS_ALGORITHMS_H
#include <limits>
#include <tuple>
#include "frozen/bits/basic_types.h"
namespace frozen {
namespace bits {
auto constexpr next_highest_power_of_two(std::size_t v) {
// https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
constexpr auto trip_count = std::numeric_limits<decltype(v)>::digits;
v--;
for (std::size_t i = 1; i < trip_count; i <<= 1) v |= v >> i;
v++;
return v;
}
template <class T>
auto constexpr log(T v) {
std::size_t n = 0;
while (v > 1) {
n += 1;
v >>= 1;
}
return n;
}
constexpr std::size_t bit_weight(std::size_t n) {
return (n <= 8 * sizeof(unsigned int)) + (n <= 8 * sizeof(unsigned long)) +
(n <= 8 * sizeof(unsigned long long)) + (n <= 128);
}
unsigned int select_uint_least(std::integral_constant<std::size_t, 4>);
unsigned long select_uint_least(std::integral_constant<std::size_t, 3>);
unsigned long long select_uint_least(std::integral_constant<std::size_t, 2>);
template <std::size_t N>
unsigned long long select_uint_least(std::integral_constant<std::size_t, N>) {
static_assert(N < 2, "unsupported type size");
return {};
}
template <std::size_t N>
using select_uint_least_t = decltype(select_uint_least(
std::integral_constant<std::size_t, bit_weight(N)>()));
template <typename Iter, typename Compare>
constexpr auto min_element(Iter begin, const Iter end, Compare const &compare) {
auto result = begin;
while (begin != end) {
if (compare(*begin, *result)) {
result = begin;
}
++begin;
}
return result;
}
template <class T>
constexpr void cswap(T &a, T &b) {
auto tmp = a;
a = b;
b = tmp;
}
template <class T, class U>
constexpr void cswap(std::pair<T, U> &a, std::pair<T, U> &b) {
cswap(a.first, b.first);
cswap(a.second, b.second);
}
template <class... Tys, std::size_t... Is>
constexpr void cswap(std::tuple<Tys...> &a, std::tuple<Tys...> &b,
std::index_sequence<Is...>) {
using swallow = int[];
(void)swallow{(cswap(std::get<Is>(a), std::get<Is>(b)), 0)...};
}
template <class... Tys>
constexpr void cswap(std::tuple<Tys...> &a, std::tuple<Tys...> &b) {
cswap(a, b, std::make_index_sequence<sizeof...(Tys)>());
}
template <typename Iterator, class Compare>
constexpr Iterator partition(Iterator left, Iterator right,
Compare const &compare) {
auto pivot = left + (right - left) / 2;
auto value = *pivot;
cswap(*right, *pivot);
for (auto it = left; 0 < right - it; ++it) {
if (compare(*it, value)) {
cswap(*it, *left);
left++;
}
}
cswap(*right, *left);
return left;
}
template <typename Iterator, class Compare>
constexpr void quicksort(Iterator left, Iterator right,
Compare const &compare) {
while (0 < right - left) {
auto new_pivot = bits::partition(left, right, compare);
quicksort(left, new_pivot, compare);
left = new_pivot + 1;
}
}
template <typename T, std::size_t N, class Compare>
constexpr bits::carray<T, N> quicksort(bits::carray<T, N> const &array,
Compare const &compare) {
bits::carray<T, N> res = array;
quicksort(res.begin(), res.end() - 1, compare);
return res;
}
template <class T, class Compare>
struct LowerBound {
T const &value_;
Compare const &compare_;
constexpr LowerBound(T const &value, Compare const &compare)
: value_(value), compare_(compare) {}
template <class ForwardIt>
inline constexpr ForwardIt doit_fast(ForwardIt first,
std::integral_constant<std::size_t, 0>) {
return first;
}
template <class ForwardIt, std::size_t N>
inline constexpr ForwardIt doit_fast(ForwardIt first,
std::integral_constant<std::size_t, N>) {
auto constexpr step = N / 2;
static_assert(N / 2 == N - N / 2 - 1, "power of two minus 1");
auto it = first + step;
auto next_it = compare_(*it, value_) ? it + 1 : first;
return doit_fast(next_it, std::integral_constant<std::size_t, N / 2>{});
}
template <class ForwardIt, std::size_t N>
inline constexpr ForwardIt doitfirst(ForwardIt first,
std::integral_constant<std::size_t, N>,
std::integral_constant<bool, true>) {
return doit_fast(first, std::integral_constant<std::size_t, N>{});
}
template <class ForwardIt, std::size_t N>
inline constexpr ForwardIt doitfirst(ForwardIt first,
std::integral_constant<std::size_t, N>,
std::integral_constant<bool, false>) {
auto constexpr next_power = next_highest_power_of_two(N);
auto constexpr next_start = next_power / 2 - 1;
auto it = first + next_start;
if (compare_(*it, value_)) {
auto constexpr next = N - next_start - 1;
return doitfirst(
it + 1, std::integral_constant<std::size_t, next>{},
std::integral_constant<bool, next_highest_power_of_two(next) - 1 ==
next>{});
}
else
return doit_fast(first,
std::integral_constant<std::size_t, next_start>{});
}
template <class ForwardIt>
inline constexpr ForwardIt doitfirst(ForwardIt first,
std::integral_constant<std::size_t, 1>,
std::integral_constant<bool, false>) {
return doit_fast(first, std::integral_constant<std::size_t, 1>{});
}
};
template <std::size_t N, class ForwardIt, class T, class Compare>
constexpr ForwardIt lower_bound(ForwardIt first, const T &value,
Compare const &compare) {
return LowerBound<T, Compare>{value, compare}.doitfirst(
first, std::integral_constant<std::size_t, N>{},
std::integral_constant<bool, next_highest_power_of_two(N) - 1 == N>{});
}
template <std::size_t N, class Compare, class ForwardIt, class T>
constexpr bool binary_search(ForwardIt first, const T &value,
Compare const &compare) {
ForwardIt where = lower_bound<N>(first, value, compare);
return (!(where == first + N) && !(compare(value, *where)));
}
template <class InputIt1, class InputIt2>
constexpr bool equal(InputIt1 first1, InputIt1 last1, InputIt2 first2) {
for (; first1 != last1; ++first1, ++first2) {
if (!(*first1 == *first2)) {
return false;
}
}
return true;
}
template <class InputIt1, class InputIt2>
constexpr bool lexicographical_compare(InputIt1 first1, InputIt1 last1,
InputIt2 first2, InputIt2 last2) {
for (; (first1 != last1) && (first2 != last2); ++first1, ++first2) {
if (*first1 < *first2)
return true;
if (*first2 < *first1)
return false;
}
return (first1 == last1) && (first2 != last2);
}
} // namespace bits
} // namespace frozen
#endif
+224
View File
@@ -0,0 +1,224 @@
/*
* Frozen
* Copyright 2016 QuarksLab
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#ifndef FROZEN_LETITGO_BASIC_TYPES_H
#define FROZEN_LETITGO_BASIC_TYPES_H
#include <array>
#include <iterator>
#include <string>
#include <utility>
#include "frozen/bits/exceptions.h"
namespace frozen {
namespace bits {
// used as a fake argument for frozen::make_set and frozen::make_map in the case
// of N=0
struct ignored_arg {};
template <class T, std::size_t N>
class cvector {
T data[N] = {}; // zero-initialization for scalar type T, default-initialized
// otherwise
std::size_t dsize = 0;
public:
// Container typdefs
using value_type = T;
using reference = value_type &;
using const_reference = const value_type &;
using pointer = value_type *;
using const_pointer = const value_type *;
using iterator = pointer;
using const_iterator = const_pointer;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
// Constructors
constexpr cvector(void) = default;
constexpr cvector(size_type count, const T &value) : dsize(count) {
for (std::size_t i = 0; i < N; ++i) data[i] = value;
}
// Iterators
constexpr iterator begin() noexcept { return data; }
constexpr iterator end() noexcept { return data + dsize; }
// Capacity
constexpr size_type size() const { return dsize; }
// Element access
constexpr reference operator[](std::size_t index) { return data[index]; }
constexpr const_reference operator[](std::size_t index) const {
return data[index];
}
constexpr reference back() { return data[dsize - 1]; }
constexpr const_reference back() const { return data[dsize - 1]; }
// Modifiers
constexpr void push_back(const T &a) { data[dsize++] = a; }
constexpr void push_back(T &&a) { data[dsize++] = std::move(a); }
constexpr void pop_back() { --dsize; }
constexpr void clear() { dsize = 0; }
};
template <class T, std::size_t N>
class carray {
T data_[N] = {}; // zero-initialization for scalar type T,
// default-initialized otherwise
template <std::size_t M, std::size_t... I>
constexpr carray(T const (&init)[M], std::index_sequence<I...>)
: data_{init[I]...} {}
template <class Iter, std::size_t... I>
constexpr carray(Iter iter, std::index_sequence<I...>)
: data_{((void)I, *iter++)...} {}
public:
// Container typdefs
using value_type = T;
using reference = value_type &;
using const_reference = const value_type &;
using pointer = value_type *;
using const_pointer = const value_type *;
using iterator = pointer;
using const_iterator = const_pointer;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
// Constructors
constexpr carray(void) = default;
template <std::size_t M>
constexpr carray(T const (&init)[M])
: carray(init, std::make_index_sequence<N>()) {
static_assert(M >= N, "Cannot initialize a carray with an smaller array");
}
template <std::size_t M>
constexpr carray(std::array<T, M> const &init)
: carray(&init[0], std::make_index_sequence<N>()) {
static_assert(M >= N, "Cannot initialize a carray with an smaller array");
}
constexpr carray(std::initializer_list<T> init)
: carray(init.begin(), std::make_index_sequence<N>()) {
// clang & gcc doesn't recognize init.size() as a constexpr
// static_assert(init.size() >= N, "Cannot initialize a carray with an
// smaller initializer list");
}
// Iterators
constexpr iterator begin() noexcept { return data_; }
constexpr const_iterator begin() const noexcept { return data_; }
constexpr const_iterator cbegin() const noexcept { return data_; }
constexpr iterator end() noexcept { return data_ + N; }
constexpr const_iterator end() const noexcept { return data_ + N; }
constexpr const_iterator cend() const noexcept { return data_ + N; }
constexpr reverse_iterator rbegin() noexcept {
return reverse_iterator(end());
}
constexpr const_reverse_iterator rbegin() const noexcept {
return const_reverse_iterator(end());
}
constexpr const_reverse_iterator crbegin() const noexcept {
return const_reverse_iterator(end());
}
constexpr reverse_iterator rend() noexcept {
return reverse_iterator(begin());
}
constexpr const_reverse_iterator rend() const noexcept {
return const_reverse_iterator(begin());
}
constexpr const_reverse_iterator crend() const noexcept {
return const_reverse_iterator(begin());
}
// Capacity
constexpr size_type size() const { return N; }
constexpr size_type max_size() const { return N; }
// Element access
constexpr reference operator[](std::size_t index) { return data_[index]; }
constexpr const_reference operator[](std::size_t index) const {
return data_[index];
}
constexpr reference at(std::size_t index) {
if (index > N)
FROZEN_THROW_OR_ABORT(
std::out_of_range("Index (" + std::to_string(index) +
") out of bound (" + std::to_string(N) + ')'));
return data_[index];
}
constexpr const_reference at(std::size_t index) const {
if (index > N)
FROZEN_THROW_OR_ABORT(
std::out_of_range("Index (" + std::to_string(index) +
") out of bound (" + std::to_string(N) + ')'));
return data_[index];
}
constexpr reference front() { return data_[0]; }
constexpr const_reference front() const { return data_[0]; }
constexpr reference back() { return data_[N - 1]; }
constexpr const_reference back() const { return data_[N - 1]; }
constexpr value_type *data() noexcept { return data_; }
constexpr const value_type *data() const noexcept { return data_; }
// Modifiers
constexpr void fill(const value_type &val) {
for (std::size_t i = 0; i < N; ++i) data_[i] = val;
}
};
template <class T>
class carray<T, 0> {
public:
// Container typdefs
using value_type = T;
using reference = value_type &;
using const_reference = const value_type &;
using pointer = value_type *;
using const_pointer = const value_type *;
using iterator = pointer;
using const_iterator = const_pointer;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
// Constructors
constexpr carray(void) = default;
};
} // namespace bits
} // namespace frozen
#endif
+38
View File
@@ -0,0 +1,38 @@
/*
* Frozen
* Copyright 2016 QuarksLab
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#ifndef FROZEN_LETITGO_CONSTEXPR_ASSERT_H
#define FROZEN_LETITGO_CONSTEXPR_ASSERT_H
#include <cassert>
#ifdef _MSC_VER
// FIXME: find a way to implement that correctly for msvc
#define constexpr_assert(cond, msg)
#else
#define constexpr_assert(cond, msg) assert(cond &&msg);
#endif
#endif
+69
View File
@@ -0,0 +1,69 @@
/*
* Frozen
* Copyright 2016 QuarksLab
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#ifndef FROZEN_LETITGO_DEFINES_H
#define FROZEN_LETITGO_DEFINES_H
#if defined(_MSVC_LANG) && \
!(defined(__EDG__) && defined(__clang__)) // TRANSITION, VSO#273681
#define FROZEN_LETITGO_IS_MSVC
#endif
// Code taken from
// https://stackoverflow.com/questions/43639122/which-values-can-msvc-lang-have
#if defined(FROZEN_LETITGO_IS_MSVC)
#if _MSVC_LANG > 201402
#define FROZEN_LETITGO_HAS_CXX17 1
#else /* _MSVC_LANG > 201402 */
#define FROZEN_LETITGO_HAS_CXX17 0
#endif /* _MSVC_LANG > 201402 */
#else /* _MSVC_LANG etc. */
#if __cplusplus > 201402
#define FROZEN_LETITGO_HAS_CXX17 1
#else /* __cplusplus > 201402 */
#define FROZEN_LETITGO_HAS_CXX17 0
#endif /* __cplusplus > 201402 */
#endif /* _MSVC_LANG etc. */
// End if taken code
#if FROZEN_LETITGO_HAS_CXX17 == 1 && defined(FROZEN_LETITGO_IS_MSVC)
#define FROZEN_LETITGO_HAS_STRING_VIEW // We assume Visual Studio always has
// string_view in C++17
#else
#if FROZEN_LETITGO_HAS_CXX17 == 1 && __has_include(<string_view>)
#define FROZEN_LETITGO_HAS_STRING_VIEW
#endif
#endif
#ifdef __cpp_char8_t
#define FROZEN_LETITGO_HAS_CHAR8T
#endif
#if __cpp_deduction_guides >= 201703L
#define FROZEN_LETITGO_HAS_DEDUCTION_GUIDES
#endif
#if __cpp_lib_constexpr_string >= 201907L
#define FROZEN_LETITGO_HAS_CONSTEXPR_STRING
#endif
#endif // FROZEN_LETITGO_DEFINES_H
+60
View File
@@ -0,0 +1,60 @@
/*
* Frozen
* Copyright 2016 QuarksLab
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#ifndef FROZEN_LETITGO_ELSA_H
#define FROZEN_LETITGO_ELSA_H
#include <type_traits>
namespace frozen {
template <class T = void>
struct elsa {
static_assert(std::is_integral<T>::value || std::is_enum<T>::value,
"only supports integral types, specialize for other types");
constexpr std::size_t operator()(T const &value, std::size_t seed) const {
std::size_t key = seed ^ static_cast<std::size_t>(value);
key = (~key) + (key << 21); // key = (key << 21) - key - 1;
key = key ^ (key >> 24);
key = (key + (key << 3)) + (key << 8); // key * 265
key = key ^ (key >> 14);
key = (key + (key << 2)) + (key << 4); // key * 21
key = key ^ (key >> 28);
key = key + (key << 31);
return key;
}
};
template <>
struct elsa<void> {
template <class T>
constexpr std::size_t operator()(T const &value, std::size_t seed) const {
return elsa<T>{}(value, seed);
}
};
template <class T>
using anna = elsa<T>;
} // namespace frozen
#endif
+44
View File
@@ -0,0 +1,44 @@
#ifndef FROZEN_LETITGO_BITS_ELSA_STD_H
#define FROZEN_LETITGO_BITS_ELSA_STD_H
#include "elsa.h"
#include "hash_string.h"
#ifdef FROZEN_LETITGO_HAS_STRING_VIEW
#include <string_view>
#endif
#include <string>
namespace frozen {
#ifdef FROZEN_LETITGO_HAS_STRING_VIEW
template <typename CharT>
struct elsa<std::basic_string_view<CharT>> {
constexpr std::size_t operator()(
const std::basic_string_view<CharT> &value) const {
return hash_string(value);
}
constexpr std::size_t operator()(const std::basic_string_view<CharT> &value,
std::size_t seed) const {
return hash_string(value, seed);
}
};
#endif
template <typename CharT>
struct elsa<std::basic_string<CharT>> {
constexpr std::size_t operator()(
const std::basic_string<CharT> &value) const {
return hash_string(value);
}
constexpr std::size_t operator()(const std::basic_string<CharT> &value,
std::size_t seed) const {
return hash_string(value, seed);
}
};
} // namespace frozen
#endif // FROZEN_LETITGO_BITS_ELSA_STD_H
+40
View File
@@ -0,0 +1,40 @@
/*
* Frozen
* Copyright 2016 QuarksLab
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#ifndef FROZEN_LETITGO_EXCEPTIONS_H
#define FROZEN_LETITGO_EXCEPTIONS_H
#if defined(FROZEN_NO_EXCEPTIONS) || \
(defined(_MSC_VER) && !defined(_CPPUNWIND)) || \
(!defined(_MSC_VER) && !defined(__cpp_exceptions))
#include <cstdlib>
#define FROZEN_THROW_OR_ABORT(_) std::abort()
#else
#include <stdexcept>
#define FROZEN_THROW_OR_ABORT(err) throw err
#endif
#endif
+27
View File
@@ -0,0 +1,27 @@
#ifndef FROZEN_LETITGO_BITS_HASH_STRING_H
#define FROZEN_LETITGO_BITS_HASH_STRING_H
#include <cstddef>
namespace frozen {
template <typename String>
constexpr std::size_t hash_string(const String &value) {
std::size_t d = 5381;
for (const auto &c : value) d = d * 33 + static_cast<size_t>(c);
return d;
}
// https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function
// With the lowest bits removed, based on experimental setup.
template <typename String>
constexpr std::size_t hash_string(const String &value, std::size_t seed) {
std::size_t d = (0x811c9dc5 ^ seed) * static_cast<size_t>(0x01000193);
for (const auto &c : value)
d = (d ^ static_cast<size_t>(c)) * static_cast<size_t>(0x01000193);
return d >> 8;
}
} // namespace frozen
#endif // FROZEN_LETITGO_BITS_HASH_STRING_H
+259
View File
@@ -0,0 +1,259 @@
/*
* Frozen
* Copyright 2016 QuarksLab
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
// inspired from http://stevehanov.ca/blog/index.php?id=119
#ifndef FROZEN_LETITGO_PMH_H
#define FROZEN_LETITGO_PMH_H
#include <array>
#include <limits>
#include "frozen/bits/algorithms.h"
#include "frozen/bits/basic_types.h"
namespace frozen {
namespace bits {
// Function object for sorting buckets in decreasing order of size
struct bucket_size_compare {
template <typename B>
bool constexpr operator()(B const &b0, B const &b1) const {
return b0.size() > b1.size();
}
};
// Step One in pmh routine is to take all items and hash them into buckets,
// with some collisions. Then process those buckets further to build a perfect
// hash function.
// pmh_buckets represents the initial placement into buckets.
template <size_t M>
struct pmh_buckets {
// Step 0: Bucket max is 2 * sqrt M
// TODO: Come up with justification for this, should it not be O(log M)?
static constexpr auto bucket_max = 2 * (1u << (log(M) / 2));
using bucket_t = cvector<std::size_t, bucket_max>;
carray<bucket_t, M> buckets;
uint64_t seed;
// Represents a reference to a bucket. This is used because the buckets
// have to be sorted, but buckets are big, making it slower than sorting refs
struct bucket_ref {
unsigned hash;
const bucket_t *ptr;
// Forward some interface of bucket
using value_type = typename bucket_t::value_type;
using const_iterator = typename bucket_t::const_iterator;
constexpr auto size() const { return ptr->size(); }
constexpr const auto &operator[](std::size_t idx) const {
return (*ptr)[idx];
}
constexpr auto begin() const { return ptr->begin(); }
constexpr auto end() const { return ptr->end(); }
};
// Make a bucket_ref for each bucket
template <std::size_t... Is>
carray<bucket_ref, M> constexpr make_bucket_refs(
std::index_sequence<Is...>) const {
return {{bucket_ref{Is, &buckets[Is]}...}};
}
// Makes a bucket_ref for each bucket and sorts them by size
carray<bucket_ref, M> constexpr get_sorted_buckets() const {
carray<bucket_ref, M> result{
this->make_bucket_refs(std::make_index_sequence<M>())};
bits::quicksort(result.begin(), result.end() - 1, bucket_size_compare{});
return result;
}
};
template <size_t M, class Item, size_t N, class Hash, class Key, class PRG>
pmh_buckets<M> constexpr make_pmh_buckets(const carray<Item, N> &items,
Hash const &hash, Key const &key,
PRG &prg) {
using result_t = pmh_buckets<M>;
result_t result{};
bool rejected = false;
// Continue until all items are placed without exceeding bucket_max
while (1) {
for (auto &b : result.buckets) {
b.clear();
}
result.seed = prg();
rejected = false;
for (std::size_t i = 0; i < N; ++i) {
auto &bucket =
result.buckets[hash(key(items[i]), static_cast<size_t>(result.seed)) %
M];
if (bucket.size() >= result_t::bucket_max) {
rejected = true;
break;
}
bucket.push_back(i);
}
if (!rejected) {
return result;
}
}
}
// Check if an item appears in a cvector
template <class T, size_t N>
constexpr bool all_different_from(cvector<T, N> &data, T &a) {
for (std::size_t i = 0; i < data.size(); ++i)
if (data[i] == a)
return false;
return true;
}
// Represents either an index to a data item array, or a seed to be used with
// a hasher. Seed must have high bit of 1, value has high bit of zero.
struct seed_or_index {
using value_type = uint64_t;
private:
static constexpr value_type MINUS_ONE =
(std::numeric_limits<value_type>::max)();
static constexpr value_type HIGH_BIT = ~(MINUS_ONE >> 1);
value_type value_ = 0;
public:
constexpr value_type value() const { return value_; }
constexpr bool is_seed() const { return value_ & HIGH_BIT; }
constexpr seed_or_index(bool is_seed, value_type value)
: value_(is_seed ? (value | HIGH_BIT) : (value & ~HIGH_BIT)) {}
constexpr seed_or_index() = default;
constexpr seed_or_index(const seed_or_index &) = default;
constexpr seed_or_index &operator=(const seed_or_index &) = default;
};
// Represents the perfect hash function created by pmh algorithm
template <std::size_t M, class Hasher>
struct pmh_tables {
uint64_t first_seed_;
carray<seed_or_index, M> first_table_;
carray<std::size_t, M> second_table_;
Hasher hash_;
template <typename KeyType>
constexpr std::size_t lookup(const KeyType &key) const {
return lookup(key, hash_);
}
// Looks up a given key, to find its expected index in carray<Item, N>
// Always returns a valid index, must use KeyEqual test after to confirm.
template <typename KeyType, typename HasherType>
constexpr std::size_t lookup(const KeyType &key,
const HasherType &hasher) const {
auto const d =
first_table_[hasher(key, static_cast<size_t>(first_seed_)) % M];
if (!d.is_seed()) {
return static_cast<std::size_t>(d.value());
} // this is narrowing uint64 -> size_t but should be fine
else {
return second_table_[hasher(key, static_cast<std::size_t>(d.value())) %
M];
}
}
};
// Make pmh tables for given items, hash function, prg, etc.
template <std::size_t M, class Item, std::size_t N, class Hash, class Key,
class PRG>
pmh_tables<M, Hash> constexpr make_pmh_tables(const carray<Item, N> &items,
Hash const &hash, Key const &key,
PRG prg) {
// Step 1: Place all of the keys into buckets
auto step_one = make_pmh_buckets<M>(items, hash, key, prg);
// Step 2: Sort the buckets to process the ones with the most items first.
auto buckets = step_one.get_sorted_buckets();
// G becomes the first hash table in the resulting pmh function
carray<seed_or_index, M> G; // Default constructed to "index 0"
// H becomes the second hash table in the resulting pmh function
constexpr std::size_t UNUSED = (std::numeric_limits<std::size_t>::max)();
carray<std::size_t, M> H;
H.fill(UNUSED);
// Step 3: Map the items in buckets into hash tables.
for (const auto &bucket : buckets) {
auto const bsize = bucket.size();
if (bsize == 1) {
// Store index to the (single) item in G
// assert(bucket.hash == hash(key(items[bucket[0]]), step_one.seed) % M);
G[bucket.hash] = {false, static_cast<uint64_t>(bucket[0])};
}
else if (bsize > 1) {
// Repeatedly try different H of d until we find a hash function
// that places all items in the bucket into free slots
seed_or_index d{true, prg()};
cvector<std::size_t, decltype(step_one)::bucket_max> bucket_slots;
while (bucket_slots.size() < bsize) {
auto slot = hash(key(items[bucket[bucket_slots.size()]]),
static_cast<size_t>(d.value())) %
M;
if (H[slot] != UNUSED || !all_different_from(bucket_slots, slot)) {
bucket_slots.clear();
d = {true, prg()};
continue;
}
bucket_slots.push_back(slot);
}
// Put successful seed in G, and put indices to items in their slots
// assert(bucket.hash == hash(key(items[bucket[0]]), step_one.seed) % M);
G[bucket.hash] = d;
for (std::size_t i = 0; i < bsize; ++i) H[bucket_slots[i]] = bucket[i];
}
}
// Any unused entries in the H table have to get changed to zero.
// This is because hashing should not fail or return an out-of-bounds entry.
// A lookup fails after we apply user-supplied KeyEqual to the query and the
// key found by hashing. Sending such queries to zero cannot hurt.
for (std::size_t i = 0; i < M; ++i)
if (H[i] == UNUSED)
H[i] = 0;
return {step_one.seed, G, H, hash};
}
} // namespace bits
} // namespace frozen
#endif
+30
View File
@@ -0,0 +1,30 @@
/*
* Frozen
* Copyright 2016 QuarksLab
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#ifndef FROZEN_LETITGO_VERSION_H
#define FROZEN_LETITGO_VERSION_H
#define FROZEN_MAJOR_VERSION 1
#define FROZEN_MINOR_VERSION 1
#define FROZEN_PATCH_VERSION 1
#endif
+378
View File
@@ -0,0 +1,378 @@
/*
* Frozen
* Copyright 2016 QuarksLab
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#ifndef FROZEN_LETITGO_MAP_H
#define FROZEN_LETITGO_MAP_H
#include <utility>
#include "frozen/bits/algorithms.h"
#include "frozen/bits/basic_types.h"
#include "frozen/bits/constexpr_assert.h"
#include "frozen/bits/exceptions.h"
#include "frozen/bits/version.h"
namespace frozen {
namespace impl {
template <class Comparator>
class CompareKey {
Comparator const comparator_;
public:
constexpr CompareKey(Comparator const &comparator)
: comparator_(comparator) {}
template <class Key1, class Key2, class Value>
constexpr int operator()(std::pair<Key1, Value> const &self,
std::pair<Key2, Value> const &other) const {
return comparator_(std::get<0>(self), std::get<0>(other));
}
template <class Key1, class Key2, class Value>
constexpr int operator()(Key1 const &self_key,
std::pair<Key2, Value> const &other) const {
return comparator_(self_key, std::get<0>(other));
}
template <class Key1, class Key2, class Value>
constexpr int operator()(std::pair<Key1, Value> const &self,
Key2 const &other_key) const {
return comparator_(std::get<0>(self), other_key);
}
template <class Key1, class Key2>
constexpr int operator()(Key1 const &self_key, Key2 const &other_key) const {
return comparator_(self_key, other_key);
}
};
} // namespace impl
template <class Key, class Value, std::size_t N, class Compare = std::less<Key>>
class map {
using container_type = bits::carray<std::pair<Key, Value>, N>;
impl::CompareKey<Compare> less_than_;
container_type items_;
public:
using key_type = Key;
using mapped_type = Value;
using value_type = typename container_type::value_type;
using size_type = typename container_type::size_type;
using difference_type = typename container_type::difference_type;
using key_compare = decltype(less_than_);
using reference = typename container_type::reference;
using const_reference = typename container_type::const_reference;
using pointer = typename container_type::pointer;
using const_pointer = typename container_type::const_pointer;
using iterator = typename container_type::iterator;
using const_iterator = typename container_type::const_iterator;
using reverse_iterator = typename container_type::reverse_iterator;
using const_reverse_iterator =
typename container_type::const_reverse_iterator;
public:
/* constructors */
constexpr map(container_type items, Compare const &compare)
: less_than_{compare}, items_{bits::quicksort(items, less_than_)} {}
explicit constexpr map(container_type items) : map{items, Compare{}} {}
constexpr map(std::initializer_list<value_type> items, Compare const &compare)
: map{container_type{items}, compare} {
constexpr_assert(
items.size() == N,
"Inconsistent initializer_list size and type size argument");
}
constexpr map(std::initializer_list<value_type> items)
: map{items, Compare{}} {}
/* element access */
constexpr Value const &at(Key const &key) const {
return at_impl(*this, key);
}
constexpr Value &at(Key const &key) { return at_impl(*this, key); }
/* iterators */
constexpr iterator begin() { return items_.begin(); }
constexpr const_iterator begin() const { return items_.begin(); }
constexpr const_iterator cbegin() const { return items_.cbegin(); }
constexpr iterator end() { return items_.end(); }
constexpr const_iterator end() const { return items_.end(); }
constexpr const_iterator cend() const { return items_.cend(); }
constexpr reverse_iterator rbegin() { return items_.rbegin(); }
constexpr const_reverse_iterator rbegin() const { return items_.rbegin(); }
constexpr const_reverse_iterator crbegin() const { return items_.crbegin(); }
constexpr reverse_iterator rend() { return items_.rend(); }
constexpr const_reverse_iterator rend() const { return items_.rend(); }
constexpr const_reverse_iterator crend() const { return items_.crend(); }
/* capacity */
constexpr bool empty() const { return !N; }
constexpr size_type size() const { return N; }
constexpr size_type max_size() const { return N; }
/* lookup */
template <class KeyType>
constexpr std::size_t count(KeyType const &key) const {
return bits::binary_search<N>(items_.begin(), key, less_than_);
}
template <class KeyType>
constexpr const_iterator find(KeyType const &key) const {
return map::find_impl(*this, key);
}
template <class KeyType>
constexpr iterator find(KeyType const &key) {
return map::find_impl(*this, key);
}
template <class KeyType>
constexpr std::pair<const_iterator, const_iterator> equal_range(
KeyType const &key) const {
return equal_range_impl(*this, key);
}
template <class KeyType>
constexpr std::pair<iterator, iterator> equal_range(KeyType const &key) {
return equal_range_impl(*this, key);
}
template <class KeyType>
constexpr const_iterator lower_bound(KeyType const &key) const {
return lower_bound_impl(*this, key);
}
template <class KeyType>
constexpr iterator lower_bound(KeyType const &key) {
return lower_bound_impl(*this, key);
}
template <class KeyType>
constexpr const_iterator upper_bound(KeyType const &key) const {
return upper_bound_impl(*this, key);
}
template <class KeyType>
constexpr iterator upper_bound(KeyType const &key) {
return upper_bound_impl(*this, key);
}
/* observers */
constexpr const key_compare &key_comp() const { return less_than_; }
constexpr const key_compare &value_comp() const { return less_than_; }
private:
template <class This, class KeyType>
static inline constexpr auto &at_impl(This &&self, KeyType const &key) {
auto where = self.lower_bound(key);
if (where != self.end())
return where->second;
else
FROZEN_THROW_OR_ABORT(std::out_of_range("unknown key"));
}
template <class This, class KeyType>
static inline constexpr auto find_impl(This &&self, KeyType const &key) {
auto where = self.lower_bound(key);
if ((where != self.end()) && !self.less_than_(key, *where))
return where;
else
return self.end();
}
template <class This, class KeyType>
static inline constexpr auto equal_range_impl(This &&self,
KeyType const &key) {
auto lower = self.lower_bound(key);
using lower_t = decltype(lower);
if (lower == self.end())
return std::pair<lower_t, lower_t>{lower, lower};
else
return std::pair<lower_t, lower_t>{lower, lower + 1};
}
template <class This, class KeyType>
static inline constexpr auto lower_bound_impl(This &&self, KeyType const &key)
-> decltype(self.end()) {
auto where =
bits::lower_bound<N>(self.items_.begin(), key, self.less_than_);
if ((where != self.end()) && !self.less_than_(key, *where))
return where;
else
return self.end();
}
template <class This, class KeyType>
static inline constexpr auto upper_bound_impl(This &&self, KeyType const &key)
-> decltype(self.end()) {
auto where =
bits::lower_bound<N>(self.items_.begin(), key, self.less_than_);
if ((where != self.end()) && !self.less_than_(key, *where))
return where + 1;
else
return self.end();
}
};
template <class Key, class Value, class Compare>
class map<Key, Value, 0, Compare> {
using container_type = bits::carray<std::pair<Key, Value>, 0>;
impl::CompareKey<Compare> less_than_;
public:
using key_type = Key;
using mapped_type = Value;
using value_type = typename container_type::value_type;
using size_type = typename container_type::size_type;
using difference_type = typename container_type::difference_type;
using key_compare = decltype(less_than_);
using reference = typename container_type::reference;
using const_reference = typename container_type::const_reference;
using pointer = typename container_type::pointer;
using const_pointer = typename container_type::const_pointer;
using iterator = pointer;
using const_iterator = const_pointer;
using reverse_iterator = pointer;
using const_reverse_iterator = const_pointer;
public:
/* constructors */
constexpr map(const map &other) = default;
constexpr map(std::initializer_list<value_type>, Compare const &compare)
: less_than_{compare} {}
constexpr map(std::initializer_list<value_type> items)
: map{items, Compare{}} {}
/* element access */
template <class KeyType>
constexpr mapped_type at(KeyType const &) const {
FROZEN_THROW_OR_ABORT(std::out_of_range("invalid key"));
}
template <class KeyType>
constexpr mapped_type at(KeyType const &) {
FROZEN_THROW_OR_ABORT(std::out_of_range("invalid key"));
}
/* iterators */
constexpr iterator begin() { return nullptr; }
constexpr const_iterator begin() const { return nullptr; }
constexpr const_iterator cbegin() const { return nullptr; }
constexpr iterator end() { return nullptr; }
constexpr const_iterator end() const { return nullptr; }
constexpr const_iterator cend() const { return nullptr; }
constexpr reverse_iterator rbegin() { return nullptr; }
constexpr const_reverse_iterator rbegin() const { return nullptr; }
constexpr const_reverse_iterator crbegin() const { return nullptr; }
constexpr reverse_iterator rend() { return nullptr; }
constexpr const_reverse_iterator rend() const { return nullptr; }
constexpr const_reverse_iterator crend() const { return nullptr; }
/* capacity */
constexpr bool empty() const { return true; }
constexpr size_type size() const { return 0; }
constexpr size_type max_size() const { return 0; }
/* lookup */
template <class KeyType>
constexpr std::size_t count(KeyType const &) const {
return 0;
}
template <class KeyType>
constexpr const_iterator find(KeyType const &) const {
return end();
}
template <class KeyType>
constexpr iterator find(KeyType const &) {
return end();
}
template <class KeyType>
constexpr std::pair<const_iterator, const_iterator> equal_range(
KeyType const &) const {
return {end(), end()};
}
template <class KeyType>
constexpr std::pair<iterator, iterator> equal_range(KeyType const &) {
return {end(), end()};
}
template <class KeyType>
constexpr const_iterator lower_bound(KeyType const &) const {
return end();
}
template <class KeyType>
constexpr iterator lower_bound(KeyType const &) {
return end();
}
template <class KeyType>
constexpr const_iterator upper_bound(KeyType const &) const {
return end();
}
template <class KeyType>
constexpr iterator upper_bound(KeyType const &) {
return end();
}
/* observers */
constexpr key_compare const &key_comp() const { return less_than_; }
constexpr key_compare const &value_comp() const { return less_than_; }
};
template <typename T, typename U, typename Compare = std::less<T>>
constexpr auto make_map(
bits::ignored_arg =
{} /* for consistency with the initializer below for N = 0*/) {
return map<T, U, 0, Compare>{};
}
template <typename T, typename U, std::size_t N>
constexpr auto make_map(std::pair<T, U> const (&items)[N]) {
return map<T, U, N>{items};
}
template <typename T, typename U, std::size_t N>
constexpr auto make_map(std::array<std::pair<T, U>, N> const &items) {
return map<T, U, N>{items};
}
template <typename T, typename U, typename Compare, std::size_t N>
constexpr auto make_map(std::pair<T, U> const (&items)[N],
Compare const &compare = Compare{}) {
return map<T, U, N, Compare>{items, compare};
}
template <typename T, typename U, typename Compare, std::size_t N>
constexpr auto make_map(std::array<std::pair<T, U>, N> const &items,
Compare const &compare = Compare{}) {
return map<T, U, N, Compare>{items, compare};
}
} // namespace frozen
#endif
+97
View File
@@ -0,0 +1,97 @@
/*
* Frozen
* Copyright 2016 QuarksLab
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#ifndef FROZEN_LETITGO_RANDOM_H
#define FROZEN_LETITGO_RANDOM_H
#include <cstdint>
#include <type_traits>
#include "frozen/bits/algorithms.h"
#include "frozen/bits/version.h"
namespace frozen {
template <class UIntType, UIntType a, UIntType c, UIntType m>
class linear_congruential_engine {
static_assert(std::is_unsigned<UIntType>::value,
"UIntType must be an unsigned integral type");
template <class T>
static constexpr UIntType modulo(T val, std::integral_constant<UIntType, 0>) {
return static_cast<UIntType>(val);
}
template <class T, UIntType M>
static constexpr UIntType modulo(T val, std::integral_constant<UIntType, M>) {
// the static cast below may end up doing a truncation
return static_cast<UIntType>(val % M);
}
public:
using result_type = UIntType;
static constexpr result_type multiplier = a;
static constexpr result_type increment = c;
static constexpr result_type modulus = m;
static constexpr result_type default_seed = 1u;
linear_congruential_engine() = default;
constexpr linear_congruential_engine(result_type s) { seed(s); }
void seed(result_type s = default_seed) { state_ = s; }
constexpr result_type operator()() {
using uint_least_t =
bits::select_uint_least_t<bits::log(a) + bits::log(m) + 4>;
uint_least_t tmp =
static_cast<uint_least_t>(multiplier) * state_ + increment;
state_ = modulo(tmp, std::integral_constant<UIntType, modulus>());
return state_;
}
// constexpr void discard(unsigned long long n) {
// while (n--) operator()();
// }
// static constexpr result_type min() { return increment == 0u ? 1u : 0u; }
// static constexpr result_type max() { return modulus - 1u; }
friend constexpr bool operator==(linear_congruential_engine const &self,
linear_congruential_engine const &other) {
return self.state_ == other.state_;
}
friend constexpr bool operator!=(linear_congruential_engine const &self,
linear_congruential_engine const &other) {
return !(self == other);
}
private:
result_type state_ = default_seed;
};
using minstd_rand0 =
linear_congruential_engine<std::uint_fast32_t, 16807, 0, 2147483647>;
using minstd_rand =
linear_congruential_engine<std::uint_fast32_t, 48271, 0, 2147483647>;
// This generator is used by default in unordered frozen containers
using default_prg_t = minstd_rand;
} // namespace frozen
#endif
+283
View File
@@ -0,0 +1,283 @@
/*
* Frozen
* Copyright 2016 QuarksLab
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#ifndef FROZEN_SET_H
#define FROZEN_SET_H
#include <utility>
#include "frozen/bits/algorithms.h"
#include "frozen/bits/basic_types.h"
#include "frozen/bits/constexpr_assert.h"
#include "frozen/bits/defines.h"
#include "frozen/bits/version.h"
namespace frozen {
template <class Key, std::size_t N, class Compare = std::less<Key>>
class set {
using container_type = bits::carray<Key, N>;
Compare less_than_;
container_type keys_;
public:
/* container typedefs*/
using key_type = Key;
using value_type = Key;
using size_type = typename container_type::size_type;
using difference_type = typename container_type::size_type;
using key_compare = Compare;
using value_compare = Compare;
using reference = typename container_type::const_reference;
using const_reference = reference;
using pointer = typename container_type::const_pointer;
using const_pointer = pointer;
using iterator = typename container_type::const_iterator;
using reverse_iterator = typename container_type::const_reverse_iterator;
using const_iterator = iterator;
using const_reverse_iterator = reverse_iterator;
public:
/* constructors */
constexpr set(const set &other) = default;
constexpr set(container_type keys, Compare const &comp)
: less_than_{comp}, keys_(bits::quicksort(keys, less_than_)) {}
explicit constexpr set(container_type keys) : set{keys, Compare{}} {}
constexpr set(std::initializer_list<Key> keys, Compare const &comp)
: set{container_type{keys}, comp} {
constexpr_assert(
keys.size() == N,
"Inconsistent initializer_list size and type size argument");
}
constexpr set(std::initializer_list<Key> keys) : set{keys, Compare{}} {}
constexpr set &operator=(const set &other) = default;
/* capacity */
constexpr bool empty() const { return !N; }
constexpr size_type size() const { return N; }
constexpr size_type max_size() const { return N; }
/* lookup */
template <class KeyType>
constexpr std::size_t count(KeyType const &key) const {
return bits::binary_search<N>(keys_.begin(), key, less_than_);
}
template <class KeyType>
constexpr const_iterator find(KeyType const &key) const {
const_iterator where = lower_bound(key);
if ((where != end()) && !less_than_(key, *where))
return where;
else
return end();
}
template <class KeyType>
constexpr std::pair<const_iterator, const_iterator> equal_range(
KeyType const &key) const {
auto const lower = lower_bound(key);
if (lower == end())
return {lower, lower};
else
return {lower, lower + 1};
}
template <class KeyType>
constexpr const_iterator lower_bound(KeyType const &key) const {
auto const where = bits::lower_bound<N>(keys_.begin(), key, less_than_);
if ((where != end()) && !less_than_(key, *where))
return where;
else
return end();
}
template <class KeyType>
constexpr const_iterator upper_bound(KeyType const &key) const {
auto const where = bits::lower_bound<N>(keys_.begin(), key, less_than_);
if ((where != end()) && !less_than_(key, *where))
return where + 1;
else
return end();
}
/* observers */
constexpr const key_compare &key_comp() const { return less_than_; }
constexpr const key_compare &value_comp() const { return less_than_; }
/* iterators */
constexpr const_iterator begin() const { return keys_.begin(); }
constexpr const_iterator cbegin() const { return keys_.cbegin(); }
constexpr const_iterator end() const { return keys_.end(); }
constexpr const_iterator cend() const { return keys_.cend(); }
constexpr const_reverse_iterator rbegin() const { return keys_.rbegin(); }
constexpr const_reverse_iterator crbegin() const { return keys_.crbegin(); }
constexpr const_reverse_iterator rend() const { return keys_.rend(); }
constexpr const_reverse_iterator crend() const { return keys_.crend(); }
/* comparison */
constexpr bool operator==(set const &rhs) const {
return bits::equal(begin(), end(), rhs.begin());
}
constexpr bool operator!=(set const &rhs) const { return !(*this == rhs); }
constexpr bool operator<(set const &rhs) const {
return bits::lexicographical_compare(begin(), end(), rhs.begin(),
rhs.end());
}
constexpr bool operator<=(set const &rhs) const {
return (*this < rhs) || (*this == rhs);
}
constexpr bool operator>(set const &rhs) const {
return bits::lexicographical_compare(rhs.begin(), rhs.end(), begin(),
end());
}
constexpr bool operator>=(set const &rhs) const {
return (*this > rhs) || (*this == rhs);
}
};
template <class Key, class Compare>
class set<Key, 0, Compare> {
using container_type = bits::carray<Key, 0>; // just for the type definitions
Compare less_than_;
public:
/* container typedefs*/
using key_type = Key;
using value_type = Key;
using size_type = typename container_type::size_type;
using difference_type = typename container_type::size_type;
using key_compare = Compare;
using value_compare = Compare;
using reference = typename container_type::const_reference;
using const_reference = reference;
using pointer = typename container_type::const_pointer;
using const_pointer = pointer;
using iterator = pointer;
using reverse_iterator = pointer;
using const_iterator = const_pointer;
using const_reverse_iterator = const_pointer;
public:
/* constructors */
constexpr set(const set &other) = default;
constexpr set(bits::carray<Key, 0>, Compare const &) {}
explicit constexpr set(bits::carray<Key, 0>) {}
constexpr set(std::initializer_list<Key>, Compare const &comp)
: less_than_{comp} {}
constexpr set(std::initializer_list<Key> keys) : set{keys, Compare{}} {}
constexpr set &operator=(const set &other) = default;
/* capacity */
constexpr bool empty() const { return true; }
constexpr size_type size() const { return 0; }
constexpr size_type max_size() const { return 0; }
/* lookup */
template <class KeyType>
constexpr std::size_t count(KeyType const &) const {
return 0;
}
template <class KeyType>
constexpr const_iterator find(KeyType const &) const {
return end();
}
template <class KeyType>
constexpr std::pair<const_iterator, const_iterator> equal_range(
KeyType const &) const {
return {end(), end()};
}
template <class KeyType>
constexpr const_iterator lower_bound(KeyType const &) const {
return end();
}
template <class KeyType>
constexpr const_iterator upper_bound(KeyType const &) const {
return end();
}
/* observers */
constexpr key_compare key_comp() const { return less_than_; }
constexpr key_compare value_comp() const { return less_than_; }
/* iterators */
constexpr const_iterator begin() const { return nullptr; }
constexpr const_iterator cbegin() const { return nullptr; }
constexpr const_iterator end() const { return nullptr; }
constexpr const_iterator cend() const { return nullptr; }
constexpr const_reverse_iterator rbegin() const { return nullptr; }
constexpr const_reverse_iterator crbegin() const { return nullptr; }
constexpr const_reverse_iterator rend() const { return nullptr; }
constexpr const_reverse_iterator crend() const { return nullptr; }
};
template <typename T>
constexpr auto make_set(
bits::ignored_arg =
{} /* for consistency with the initializer below for N = 0*/) {
return set<T, 0>{};
}
template <typename T, std::size_t N>
constexpr auto make_set(const T (&args)[N]) {
return set<T, N>(args);
}
template <typename T, std::size_t N>
constexpr auto make_set(std::array<T, N> const &args) {
return set<T, N>(args);
}
template <typename T, typename Compare, std::size_t N>
constexpr auto make_set(const T (&args)[N],
Compare const &compare = Compare{}) {
return set<T, N, Compare>(args, compare);
}
template <typename T, typename Compare, std::size_t N>
constexpr auto make_set(std::array<T, N> const &args,
Compare const &compare = Compare{}) {
return set<T, N, Compare>(args, compare);
}
#ifdef FROZEN_LETITGO_HAS_DEDUCTION_GUIDES
template <class T, class... Args>
set(T, Args...) -> set<T, sizeof...(Args) + 1>;
#endif // FROZEN_LETITGO_HAS_DEDUCTION_GUIDES
} // namespace frozen
#endif
+150
View File
@@ -0,0 +1,150 @@
/*
* Frozen
* Copyright 2016 QuarksLab
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#ifndef FROZEN_LETITGO_STRING_H
#define FROZEN_LETITGO_STRING_H
#include <cstring>
#include <functional>
#include "frozen/bits/defines.h"
#include "frozen/bits/elsa.h"
#include "frozen/bits/hash_string.h"
#include "frozen/bits/version.h"
#ifdef FROZEN_LETITGO_HAS_STRING_VIEW
#include <string_view>
#endif
namespace frozen {
template <typename _CharT>
class basic_string {
using chr_t = _CharT;
chr_t const *data_;
std::size_t size_;
public:
template <std::size_t N>
constexpr basic_string(chr_t const (&data)[N]) : data_(data), size_(N - 1) {}
constexpr basic_string(chr_t const *data, std::size_t size)
: data_(data), size_(size) {}
#ifdef FROZEN_LETITGO_HAS_STRING_VIEW
constexpr basic_string(std::basic_string_view<chr_t> data)
: data_(data.data()), size_(data.size()) {}
#endif
constexpr basic_string(const basic_string &) noexcept = default;
constexpr basic_string &operator=(const basic_string &) noexcept = default;
constexpr std::size_t size() const { return size_; }
constexpr chr_t operator[](std::size_t i) const { return data_[i]; }
constexpr bool operator==(basic_string other) const {
if (size_ != other.size_)
return false;
for (std::size_t i = 0; i < size_; ++i)
if (data_[i] != other.data_[i])
return false;
return true;
}
constexpr bool operator<(const basic_string &other) const {
unsigned i = 0;
while (i < size() && i < other.size()) {
if ((*this)[i] < other[i]) {
return true;
}
if ((*this)[i] > other[i]) {
return false;
}
++i;
}
return size() < other.size();
}
constexpr const chr_t *data() const { return data_; }
constexpr const chr_t *begin() const { return data(); }
constexpr const chr_t *end() const { return data() + size(); }
};
template <typename _CharT>
struct elsa<basic_string<_CharT>> {
constexpr std::size_t operator()(basic_string<_CharT> value) const {
return hash_string(value);
}
constexpr std::size_t operator()(basic_string<_CharT> value,
std::size_t seed) const {
return hash_string(value, seed);
}
};
using string = basic_string<char>;
using wstring = basic_string<wchar_t>;
using u16string = basic_string<char16_t>;
using u32string = basic_string<char32_t>;
#ifdef FROZEN_LETITGO_HAS_CHAR8T
using u8string = basic_string<char8_t>;
#endif
namespace string_literals {
constexpr string operator"" _s(const char *data, std::size_t size) {
return {data, size};
}
constexpr wstring operator"" _s(const wchar_t *data, std::size_t size) {
return {data, size};
}
constexpr u16string operator"" _s(const char16_t *data, std::size_t size) {
return {data, size};
}
constexpr u32string operator"" _s(const char32_t *data, std::size_t size) {
return {data, size};
}
#ifdef FROZEN_LETITGO_HAS_CHAR8T
constexpr u8string operator"" _s(const char8_t *data, std::size_t size) {
return {data, size};
}
#endif
} // namespace string_literals
} // namespace frozen
namespace std {
template <typename _CharT>
struct hash<frozen::basic_string<_CharT>> {
size_t operator()(frozen::basic_string<_CharT> s) const {
return frozen::elsa<frozen::basic_string<_CharT>>{}(s);
}
};
} // namespace std
#endif
+275
View File
@@ -0,0 +1,275 @@
/*
* Frozen
* Copyright 2016 QuarksLab
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#ifndef FROZEN_LETITGO_UNORDERED_MAP_H
#define FROZEN_LETITGO_UNORDERED_MAP_H
#include <functional>
#include <tuple>
#include "frozen/bits/basic_types.h"
#include "frozen/bits/constexpr_assert.h"
#include "frozen/bits/elsa.h"
#include "frozen/bits/exceptions.h"
#include "frozen/bits/pmh.h"
#include "frozen/bits/version.h"
#include "frozen/random.h"
namespace frozen {
namespace bits {
struct GetKey {
template <class KV>
constexpr auto const &operator()(KV const &kv) const {
return kv.first;
}
};
} // namespace bits
template <class Key, class Value, std::size_t N, typename Hash = anna<Key>,
class KeyEqual = std::equal_to<Key>>
class unordered_map {
static constexpr std::size_t storage_size =
bits::next_highest_power_of_two(N) *
(N < 32 ? 2 : 1); // size adjustment to prevent high collision rate for
// small sets
using container_type = bits::carray<std::pair<Key, Value>, N>;
using tables_type = bits::pmh_tables<storage_size, Hash>;
KeyEqual const equal_;
container_type items_;
tables_type tables_;
public:
/* typedefs */
using Self = unordered_map<Key, Value, N, Hash, KeyEqual>;
using key_type = Key;
using mapped_type = Value;
using value_type = typename container_type::value_type;
using size_type = typename container_type::size_type;
using difference_type = typename container_type::difference_type;
using hasher = Hash;
using key_equal = KeyEqual;
using reference = typename container_type::reference;
using const_reference = typename container_type::const_reference;
using pointer = typename container_type::pointer;
using const_pointer = typename container_type::const_pointer;
using iterator = typename container_type::iterator;
using const_iterator = typename container_type::const_iterator;
public:
/* constructors */
unordered_map(unordered_map const &) = default;
constexpr unordered_map(container_type items, Hash const &hash,
KeyEqual const &equal)
: equal_{equal},
items_{items},
tables_{bits::make_pmh_tables<storage_size>(
items_, hash, bits::GetKey{}, default_prg_t{})} {}
explicit constexpr unordered_map(container_type items)
: unordered_map{items, Hash{}, KeyEqual{}} {}
constexpr unordered_map(std::initializer_list<value_type> items,
Hash const &hash, KeyEqual const &equal)
: unordered_map{container_type{items}, hash, equal} {
constexpr_assert(
items.size() == N,
"Inconsistent initializer_list size and type size argument");
}
constexpr unordered_map(std::initializer_list<value_type> items)
: unordered_map{items, Hash{}, KeyEqual{}} {}
/* iterators */
constexpr iterator begin() { return items_.begin(); }
constexpr iterator end() { return items_.end(); }
constexpr const_iterator begin() const { return items_.begin(); }
constexpr const_iterator end() const { return items_.end(); }
constexpr const_iterator cbegin() const { return items_.cbegin(); }
constexpr const_iterator cend() const { return items_.cend(); }
/* capacity */
constexpr bool empty() const { return !N; }
constexpr size_type size() const { return N; }
constexpr size_type max_size() const { return N; }
/* lookup */
template <class KeyType, class Hasher, class Equal>
constexpr std::size_t count(KeyType const &key, Hasher const &hash,
Equal const &equal) const {
auto const &kv = lookup(key, hash);
return equal(kv.first, key);
}
template <class KeyType>
constexpr std::size_t count(KeyType const &key) const {
return count(key, hash_function(), key_eq());
}
template <class KeyType, class Hasher, class Equal>
constexpr Value const &at(KeyType const &key, Hasher const &hash,
Equal const &equal) const {
return at_impl(*this, key, hash, equal);
}
template <class KeyType, class Hasher, class Equal>
constexpr Value &at(KeyType const &key, Hasher const &hash,
Equal const &equal) {
return at_impl(*this, key, hash, equal);
}
template <class KeyType>
constexpr Value const &at(KeyType const &key) const {
return at(key, hash_function(), key_eq());
}
template <class KeyType>
constexpr Value &at(KeyType const &key) {
return at(key, hash_function(), key_eq());
}
template <class KeyType, class Hasher, class Equal>
constexpr const_iterator find(KeyType const &key, Hasher const &hash,
Equal const &equal) const {
return find_impl(*this, key, hash, equal);
}
template <class KeyType, class Hasher, class Equal>
constexpr iterator find(KeyType const &key, Hasher const &hash,
Equal const &equal) {
return find_impl(*this, key, hash, equal);
}
template <class KeyType>
constexpr const_iterator find(KeyType const &key) const {
return find(key, hash_function(), key_eq());
}
template <class KeyType>
constexpr iterator find(KeyType const &key) {
return find(key, hash_function(), key_eq());
}
template <class KeyType, class Hasher, class Equal>
constexpr std::pair<const_iterator, const_iterator> equal_range(
KeyType const &key, Hasher const &hash, Equal const &equal) const {
return equal_range_impl(*this, key, hash, equal);
}
template <class KeyType, class Hasher, class Equal>
constexpr std::pair<iterator, iterator> equal_range(KeyType const &key,
Hasher const &hash,
Equal const &equal) {
return equal_range_impl(*this, key, hash, equal);
}
template <class KeyType>
constexpr std::pair<const_iterator, const_iterator> equal_range(
KeyType const &key) const {
return equal_range(key, hash_function(), key_eq());
}
template <class KeyType>
constexpr std::pair<iterator, iterator> equal_range(KeyType const &key) {
return equal_range(key, hash_function(), key_eq());
}
/* bucket interface */
constexpr std::size_t bucket_count() const { return storage_size; }
constexpr std::size_t max_bucket_count() const { return storage_size; }
/* observers*/
constexpr const hasher &hash_function() const { return tables_.hash_; }
constexpr const key_equal &key_eq() const { return equal_; }
private:
template <class This, class KeyType, class Hasher, class Equal>
static inline constexpr auto &at_impl(This &&self, KeyType const &key,
Hasher const &hash,
Equal const &equal) {
auto &kv = self.lookup(key, hash);
if (equal(kv.first, key))
return kv.second;
else
FROZEN_THROW_OR_ABORT(std::out_of_range("unknown key"));
}
template <class This, class KeyType, class Hasher, class Equal>
static inline constexpr auto find_impl(This &&self, KeyType const &key,
Hasher const &hash,
Equal const &equal) {
auto &kv = self.lookup(key, hash);
if (equal(kv.first, key))
return &kv;
else
return self.items_.end();
}
template <class This, class KeyType, class Hasher, class Equal>
static inline constexpr auto equal_range_impl(This &&self, KeyType const &key,
Hasher const &hash,
Equal const &equal) {
auto &kv = self.lookup(key, hash);
using kv_ptr = decltype(&kv);
if (equal(kv.first, key))
return std::pair<kv_ptr, kv_ptr>{&kv, &kv + 1};
else
return std::pair<kv_ptr, kv_ptr>{self.items_.end(), self.items_.end()};
}
template <class This, class KeyType, class Hasher>
static inline constexpr auto &lookup_impl(This &&self, KeyType const &key,
Hasher const &hash) {
return self.items_[self.tables_.lookup(key, hash)];
}
template <class KeyType, class Hasher>
constexpr auto const &lookup(KeyType const &key, Hasher const &hash) const {
return lookup_impl(*this, key, hash);
}
template <class KeyType, class Hasher>
constexpr auto &lookup(KeyType const &key, Hasher const &hash) {
return lookup_impl(*this, key, hash);
}
};
template <typename T, typename U, std::size_t N>
constexpr auto make_unordered_map(std::pair<T, U> const (&items)[N]) {
return unordered_map<T, U, N>{items};
}
template <typename T, typename U, std::size_t N, typename Hasher,
typename Equal>
constexpr auto make_unordered_map(std::pair<T, U> const (&items)[N],
Hasher const &hash = elsa<T>{},
Equal const &equal = std::equal_to<T>{}) {
return unordered_map<T, U, N, Hasher, Equal>{items, hash, equal};
}
template <typename T, typename U, std::size_t N>
constexpr auto make_unordered_map(std::array<std::pair<T, U>, N> const &items) {
return unordered_map<T, U, N>{items};
}
template <typename T, typename U, std::size_t N, typename Hasher,
typename Equal>
constexpr auto make_unordered_map(std::array<std::pair<T, U>, N> const &items,
Hasher const &hash = elsa<T>{},
Equal const &equal = std::equal_to<T>{}) {
return unordered_map<T, U, N, Hasher, Equal>{items, hash, equal};
}
} // namespace frozen
#endif
+198
View File
@@ -0,0 +1,198 @@
/*
* Frozen
* Copyright 2016 QuarksLab
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#ifndef FROZEN_LETITGO_UNORDERED_SET_H
#define FROZEN_LETITGO_UNORDERED_SET_H
#include <utility>
#include "frozen/bits/basic_types.h"
#include "frozen/bits/constexpr_assert.h"
#include "frozen/bits/elsa.h"
#include "frozen/bits/pmh.h"
#include "frozen/bits/version.h"
#include "frozen/random.h"
namespace frozen {
namespace bits {
struct Get {
template <class T>
constexpr T const &operator()(T const &key) const {
return key;
}
};
} // namespace bits
template <class Key, std::size_t N, typename Hash = elsa<Key>,
class KeyEqual = std::equal_to<Key>>
class unordered_set {
static constexpr std::size_t storage_size =
bits::next_highest_power_of_two(N) *
(N < 32 ? 2 : 1); // size adjustment to prevent high collision rate for
// small sets
using container_type = bits::carray<Key, N>;
using tables_type = bits::pmh_tables<storage_size, Hash>;
KeyEqual const equal_;
container_type keys_;
tables_type tables_;
public:
/* typedefs */
using key_type = Key;
using value_type = Key;
using size_type = typename container_type::size_type;
using difference_type = typename container_type::difference_type;
using hasher = Hash;
using key_equal = KeyEqual;
using const_reference = typename container_type::const_reference;
using reference = const_reference;
using const_pointer = typename container_type::const_pointer;
using pointer = const_pointer;
using const_iterator = const_pointer;
using iterator = const_iterator;
public:
/* constructors */
unordered_set(unordered_set const &) = default;
constexpr unordered_set(container_type keys, Hash const &hash,
KeyEqual const &equal)
: equal_{equal},
keys_{keys},
tables_{bits::make_pmh_tables<storage_size>(keys_, hash, bits::Get{},
default_prg_t{})} {}
explicit constexpr unordered_set(container_type keys)
: unordered_set{keys, Hash{}, KeyEqual{}} {}
constexpr unordered_set(std::initializer_list<Key> keys)
: unordered_set{keys, Hash{}, KeyEqual{}} {}
constexpr unordered_set(std::initializer_list<Key> keys, Hash const &hash,
KeyEqual const &equal)
: unordered_set{container_type{keys}, hash, equal} {
constexpr_assert(
keys.size() == N,
"Inconsistent initializer_list size and type size argument");
}
/* iterators */
constexpr const_iterator begin() const { return keys_.begin(); }
constexpr const_iterator end() const { return keys_.end(); }
constexpr const_iterator cbegin() const { return keys_.cbegin(); }
constexpr const_iterator cend() const { return keys_.cend(); }
/* capacity */
constexpr bool empty() const { return !N; }
constexpr size_type size() const { return N; }
constexpr size_type max_size() const { return N; }
/* lookup */
template <class KeyType, class Hasher, class Equal>
constexpr std::size_t count(KeyType const &key, Hasher const &hash,
Equal const &equal) const {
auto const k = lookup(key, hash);
return equal(k, key);
}
template <class KeyType>
constexpr std::size_t count(KeyType const &key) const {
return count(key, hash_function(), key_eq());
}
template <class KeyType, class Hasher, class Equal>
constexpr const_iterator find(KeyType const &key, Hasher const &hash,
Equal const &equal) const {
auto const &k = lookup(key, hash);
if (equal(k, key))
return &k;
else
return keys_.end();
}
template <class KeyType>
constexpr const_iterator find(KeyType const &key) const {
return find(key, hash_function(), key_eq());
}
template <class KeyType, class Hasher, class Equal>
constexpr std::pair<const_iterator, const_iterator> equal_range(
KeyType const &key, Hasher const &hash, Equal const &equal) const {
auto const &k = lookup(key, hash);
if (equal(k, key))
return {&k, &k + 1};
else
return {keys_.end(), keys_.end()};
}
template <class KeyType>
constexpr std::pair<const_iterator, const_iterator> equal_range(
KeyType const &key) const {
return equal_range(key, hash_function(), key_eq());
}
/* bucket interface */
constexpr std::size_t bucket_count() const { return storage_size; }
constexpr std::size_t max_bucket_count() const { return storage_size; }
/* observers*/
constexpr const hasher &hash_function() const { return tables_.hash_; }
constexpr const key_equal &key_eq() const { return equal_; }
private:
template <class KeyType, class Hasher>
constexpr auto const &lookup(KeyType const &key, Hasher const &hash) const {
return keys_[tables_.lookup(key, hash)];
}
};
template <typename T, std::size_t N>
constexpr auto make_unordered_set(T const (&keys)[N]) {
return unordered_set<T, N>{keys};
}
template <typename T, std::size_t N, typename Hasher, typename Equal>
constexpr auto make_unordered_set(T const (&keys)[N], Hasher const &hash,
Equal const &equal) {
return unordered_set<T, N, Hasher, Equal>{keys, hash, equal};
}
template <typename T, std::size_t N>
constexpr auto make_unordered_set(std::array<T, N> const &keys) {
return unordered_set<T, N>{keys};
}
template <typename T, std::size_t N, typename Hasher, typename Equal>
constexpr auto make_unordered_set(std::array<T, N> const &keys,
Hasher const &hash, Equal const &equal) {
return unordered_set<T, N, Hasher, Equal>{keys, hash, equal};
}
#ifdef FROZEN_LETITGO_HAS_DEDUCTION_GUIDES
template <class T, class... Args>
unordered_set(T, Args...) -> unordered_set<T, sizeof...(Args) + 1>;
#endif
} // namespace frozen
#endif
+201
View File
@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
File diff suppressed because it is too large Load Diff
+117
View File
@@ -0,0 +1,117 @@
#pragma once
#if (defined(_MSC_VER) && _MSC_VER >= 1920 && _MSVC_LANG >= 202002L) || \
(!defined(_MSC_VER) && defined(__cplusplus) && __cplusplus >= 202002L)
#include <bit>
#endif
#include <array>
#include <string>
#include <string_view>
#include <type_traits>
#include <vector>
#if defined __clang__
#define IGUANA_INLINE __attribute__((always_inline)) inline
#define IGUANA__INLINE_LAMBDA __attribute__((always_inline)) constexpr
#elif defined _MSC_VER
#define IGUANA_INLINE __forceinline
#define IGUANA__INLINE_LAMBDA constexpr
#else
#define IGUANA_INLINE __attribute__((always_inline)) inline
#define IGUANA__INLINE_LAMBDA constexpr __attribute__((always_inline))
#endif
#if __has_cpp_attribute(likely) && __has_cpp_attribute(unlikely) && \
__cplusplus >= 202002L
#define IGUANA_LIKELY [[likely]]
#define IGUANA_UNLIKELY [[unlikely]]
#else
#define IGUANA_LIKELY
#define IGUANA_UNLIKELY
#endif
#ifdef _MSC_VER
#if _MSC_VER >= 1920 && _MSVC_LANG >= 202002L
IGUANA_INLINE int countr_zero(unsigned long long x) {
return std::countr_zero(x);
}
template <typename T>
constexpr inline bool contiguous_iterator = std::contiguous_iterator<T>;
#else
IGUANA_INLINE int countr_zero(unsigned long long n) {
// x will never be zero in iguana
#if defined(_MSC_VER) && defined(_M_X64)
unsigned long c;
_BitScanForward64(&c, n);
return static_cast<int>(c);
#elif defined(_MSC_VER) && defined(_M_IX86)
unsigned long c;
if (static_cast<uint32_t>(n) != 0) {
_BitScanForward(&c, static_cast<uint32_t>(n));
return static_cast<int>(c);
}
else {
_BitScanForward(&c, static_cast<uint32_t>(n >> 32));
return static_cast<int>(c) + 32;
}
#endif
}
namespace std {
template <typename T>
using remove_cvref_t =
typename remove_cv<typename remove_reference<T>::type>::type;
}
template <typename T>
constexpr inline bool contiguous_iterator =
std::is_same_v<std::remove_cvref_t<T>,
typename std::vector<char>::iterator> ||
std::is_same_v<std::remove_cvref_t<T>,
typename std::vector<char>::const_iterator> ||
std::is_same_v<std::remove_cvref_t<T>, typename std::string::iterator> ||
std::is_same_v<std::remove_cvref_t<T>,
typename std::string::const_iterator> ||
std::is_same_v<std::remove_cvref_t<T>, char *> ||
std::is_same_v<std::remove_cvref_t<T>,
typename std::string_view::iterator> ||
std::is_same_v<std::remove_cvref_t<T>,
typename std::string_view::const_iterator>;
#endif
#else
#if __cplusplus >= 202002L
IGUANA_INLINE int countr_zero(unsigned long long x) {
return std::countr_zero(x);
}
template <typename T>
constexpr inline bool contiguous_iterator = std::contiguous_iterator<T>;
#else
IGUANA_INLINE int countr_zero(unsigned long long x) {
// x will never be zero in iguana
return __builtin_ctzll(x);
}
namespace std {
template <typename T>
using remove_cvref_t =
typename remove_cv<typename remove_reference<T>::type>::type;
}
template <typename T>
constexpr inline bool contiguous_iterator =
std::is_same_v<std::remove_cvref_t<T>,
typename std::vector<char>::iterator> ||
std::is_same_v<std::remove_cvref_t<T>,
typename std::vector<char>::const_iterator> ||
std::is_same_v<std::remove_cvref_t<T>, typename std::string::iterator> ||
std::is_same_v<std::remove_cvref_t<T>,
typename std::string::const_iterator> ||
std::is_same_v<std::remove_cvref_t<T>, char *> ||
std::is_same_v<std::remove_cvref_t<T>,
typename std::string_view::iterator> ||
std::is_same_v<std::remove_cvref_t<T>,
typename std::string_view::const_iterator>;
#endif
#endif
+83
View File
@@ -0,0 +1,83 @@
#pragma once
#include <charconv>
#include "dragonbox_to_chars.h"
#include "fast_float.h"
#include "iguana/define.h"
#include "itoa.hpp"
namespace iguana {
template <typename T>
struct is_char_type
: std::disjunction<std::is_same<T, char>, std::is_same<T, wchar_t>,
std::is_same<T, char16_t>, std::is_same<T, char32_t>> {};
inline void *to_chars_float(...) {
throw std::runtime_error("not allowed to invoke");
return {};
}
template <typename T, typename Ret = decltype(to_chars_float(
std::declval<T>(), std::declval<char *>()))>
using return_of_tochars = std::conditional_t<std::is_same_v<Ret, char *>,
std::true_type, std::false_type>;
// here std::true_type is used as a type , any other type is also ok.
using has_to_chars_float = iguana::return_of_tochars<std::true_type>;
namespace detail {
// check_number==true: check if the string [first, last) is a legal number
template <bool check_number = true, typename U>
std::pair<const char *, std::errc> from_chars(const char *first,
const char *last, U &value) {
using T = std::decay_t<U>;
if constexpr (std::is_floating_point_v<T>) {
auto [p, ec] = fast_float::from_chars(first, last, value);
if constexpr (check_number) {
if (p != last || ec != std::errc{})
IGUANA_UNLIKELY { throw std::runtime_error("Failed to parse number"); }
}
return {p, ec};
}
else {
auto [p, ec] = std::from_chars(first, last, value);
if constexpr (check_number) {
if (p != last || ec != std::errc{})
IGUANA_UNLIKELY { throw std::runtime_error("Failed to parse number"); }
}
return {p, ec};
}
}
// not support uint8 for now
template <typename T>
char *to_chars(char *buffer, T value) noexcept {
using U = std::decay_t<T>;
if constexpr (std::is_floating_point_v<U>) {
if constexpr (has_to_chars_float::value) {
return static_cast<char *>(to_chars_float(value, buffer));
}
else {
return jkj::dragonbox::to_chars(value, buffer);
}
}
else if constexpr (std::is_signed_v<U> && (sizeof(U) >= 8)) {
return xtoa(value, buffer, 10, 1); // int64_t
}
else if constexpr (std::is_unsigned_v<U> && (sizeof(U) >= 8)) {
return xtoa(value, buffer, 10, 0); // uint64_t
}
else if constexpr (std::is_integral_v<U> && (sizeof(U) > 1)) {
return itoa_fwd(value, buffer); // only support more than 2 bytes intergal
}
else if constexpr (!is_char_type<U>::value) {
return itoa_fwd(static_cast<int>(value),
buffer); // only support more than 2 bytes intergal
}
else {
static_assert(!sizeof(U), "only support arithmetic type except char type");
}
}
} // namespace detail
} // namespace iguana
File diff suppressed because it is too large Load Diff
+629
View File
@@ -0,0 +1,629 @@
// Copyright 2020-2022 Junekey Jeon
//
// The contents of this file may be used under the terms of
// the Apache License v2.0 with LLVM Exceptions.
//
// (See accompanying file LICENSE-Apache or copy at
// https://llvm.org/foundation/relicensing/LICENSE.txt)
//
// Alternatively, the contents of this file may be used under the terms of
// the Boost Software License, Version 1.0.
// (See accompanying file LICENSE-Boost or copy at
// https://www.boost.org/LICENSE_1_0.txt)
//
// Unless required by applicable law or agreed to in writing, this software
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.
#ifndef JKJ_HEADER_DRAGONBOX_TO_CHARS
#define JKJ_HEADER_DRAGONBOX_TO_CHARS
#include "dragonbox.h"
#if defined(__GNUC__) || defined(__clang__)
#define JKJ_FORCEINLINE inline __attribute__((always_inline))
#elif defined(_MSC_VER)
#define JKJ_FORCEINLINE __forceinline
#else
#define JKJ_FORCEINLINE inline
#endif
namespace jkj::dragonbox {
namespace to_chars_detail {
template <class Float, class FloatTraits>
extern char *to_chars(typename FloatTraits::carrier_uint significand,
int exponent, char *buffer) noexcept;
// These "//"'s are to prevent clang-format to ruin this nice alignment.
// Thanks to reddit user u/mcmcc:
// https://www.reddit.com/r/cpp/comments/so3wx9/dragonbox_110_is_released_a_fast_floattostring/hw8z26r/?context=3
static constexpr char radix_100_table[] = {
'0', '0', '0', '1', '0', '2', '0', '3', '0', '4', //
'0', '5', '0', '6', '0', '7', '0', '8', '0', '9', //
'1', '0', '1', '1', '1', '2', '1', '3', '1', '4', //
'1', '5', '1', '6', '1', '7', '1', '8', '1', '9', //
'2', '0', '2', '1', '2', '2', '2', '3', '2', '4', //
'2', '5', '2', '6', '2', '7', '2', '8', '2', '9', //
'3', '0', '3', '1', '3', '2', '3', '3', '3', '4', //
'3', '5', '3', '6', '3', '7', '3', '8', '3', '9', //
'4', '0', '4', '1', '4', '2', '4', '3', '4', '4', //
'4', '5', '4', '6', '4', '7', '4', '8', '4', '9', //
'5', '0', '5', '1', '5', '2', '5', '3', '5', '4', //
'5', '5', '5', '6', '5', '7', '5', '8', '5', '9', //
'6', '0', '6', '1', '6', '2', '6', '3', '6', '4', //
'6', '5', '6', '6', '6', '7', '6', '8', '6', '9', //
'7', '0', '7', '1', '7', '2', '7', '3', '7', '4', //
'7', '5', '7', '6', '7', '7', '7', '8', '7', '9', //
'8', '0', '8', '1', '8', '2', '8', '3', '8', '4', //
'8', '5', '8', '6', '8', '7', '8', '8', '8', '9', //
'9', '0', '9', '1', '9', '2', '9', '3', '9', '4', //
'9', '5', '9', '6', '9', '7', '9', '8', '9', '9' //
};
static constexpr char radix_100_head_table[] = {
'0', '.', '1', '.', '2', '.', '3', '.', '4', '.', //
'5', '.', '6', '.', '7', '.', '8', '.', '9', '.', //
'1', '.', '1', '.', '1', '.', '1', '.', '1', '.', //
'1', '.', '1', '.', '1', '.', '1', '.', '1', '.', //
'2', '.', '2', '.', '2', '.', '2', '.', '2', '.', //
'2', '.', '2', '.', '2', '.', '2', '.', '2', '.', //
'3', '.', '3', '.', '3', '.', '3', '.', '3', '.', //
'3', '.', '3', '.', '3', '.', '3', '.', '3', '.', //
'4', '.', '4', '.', '4', '.', '4', '.', '4', '.', //
'4', '.', '4', '.', '4', '.', '4', '.', '4', '.', //
'5', '.', '5', '.', '5', '.', '5', '.', '5', '.', //
'5', '.', '5', '.', '5', '.', '5', '.', '5', '.', //
'6', '.', '6', '.', '6', '.', '6', '.', '6', '.', //
'6', '.', '6', '.', '6', '.', '6', '.', '6', '.', //
'7', '.', '7', '.', '7', '.', '7', '.', '7', '.', //
'7', '.', '7', '.', '7', '.', '7', '.', '7', '.', //
'8', '.', '8', '.', '8', '.', '8', '.', '8', '.', //
'8', '.', '8', '.', '8', '.', '8', '.', '8', '.', //
'9', '.', '9', '.', '9', '.', '9', '.', '9', '.', //
'9', '.', '9', '.', '9', '.', '9', '.', '9', '.' //
};
// These digit generation routines are inspired by James Anhalt's itoa
// algorithm: https://github.com/jeaiii/itoa The main idea is for given n, find
// y such that floor(10^k * y / 2^32) = n holds, where k is an appropriate
// integer depending on the length of n. For example, if n = 1234567, we set k
// = 6. In this case, we have floor(y / 2^32) = 1, floor(10^2 * ((10^0 * y) mod
// 2^32) / 2^32) = 23, floor(10^2 * ((10^2 * y) mod 2^32) / 2^32) = 45, and
// floor(10^2 * ((10^4 * y) mod 2^32) / 2^32) = 67.
// See https://jk-jeon.github.io/posts/2022/02/jeaiii-algorithm/ for more
// explanation.
JKJ_FORCEINLINE static void print_9_digits(std::uint32_t s32, int &exponent,
char *&buffer) noexcept {
// -- IEEE-754 binary32
// Since we do not cut trailing zeros in advance, s32 must be of 6~9 digits
// unless the original input was subnormal.
// In particular, when it is of 9 digits it shouldn't have any trailing zeros.
// -- IEEE-754 binary64
// In this case, s32 must be of 7~9 digits unless the input is subnormal,
// and it shouldn't have any trailing zeros if it is of 9 digits.
if (s32 >= 1'0000'0000) {
// 9 digits.
// 1441151882 = ceil(2^57 / 1'0000'0000) + 1
auto prod = s32 * std::uint64_t(1441151882);
prod >>= 25;
std::memcpy(buffer, radix_100_head_table + std::uint32_t(prod >> 32) * 2,
2);
prod = std::uint32_t(prod) * std::uint64_t(100);
std::memcpy(buffer + 2, radix_100_table + std::uint32_t(prod >> 32) * 2, 2);
prod = std::uint32_t(prod) * std::uint64_t(100);
std::memcpy(buffer + 4, radix_100_table + std::uint32_t(prod >> 32) * 2, 2);
prod = std::uint32_t(prod) * std::uint64_t(100);
std::memcpy(buffer + 6, radix_100_table + std::uint32_t(prod >> 32) * 2, 2);
prod = std::uint32_t(prod) * std::uint64_t(100);
std::memcpy(buffer + 8, radix_100_table + std::uint32_t(prod >> 32) * 2, 2);
exponent += 8;
buffer += 10;
}
else if (s32 >= 100'0000) {
// 7 or 8 digits.
// 281474978 = ceil(2^48 / 100'0000) + 1
auto prod = s32 * std::uint64_t(281474978);
prod >>= 16;
auto two_digits = std::uint32_t(prod >> 32);
// If s32 is of 8 digits, increase the exponent by 7.
// Otherwise, increase it by 6.
exponent += (6 + unsigned(two_digits >= 10));
// Write the first digit and the decimal point.
std::memcpy(buffer, radix_100_head_table + two_digits * 2, 2);
// This third character may be overwritten later but we don't care.
buffer[2] = radix_100_table[two_digits * 2 + 1];
// Remaining 6 digits are all zero?
if (std::uint32_t(prod) <=
std::uint32_t((std::uint64_t(1) << 32) / 100'0000)) {
// The number of characters actually written is:
// 1, if only the first digit is nonzero, which means that either s32 is
// of 7 digits or it is of 8 digits but the second digit is zero, or 3,
// otherwise.
// Note that buffer[2] is never zero if s32 is of 7 digits, because the
// input is never zero.
buffer +=
(1 + (unsigned(two_digits >= 10) & unsigned(buffer[2] > '0')) * 2);
}
else {
// At least one of the remaining 6 digits are nonzero.
// After this adjustment, now the first destination becomes buffer + 2.
buffer += unsigned(two_digits >= 10);
// Obtain the next two digits.
prod = std::uint32_t(prod) * std::uint64_t(100);
two_digits = std::uint32_t(prod >> 32);
std::memcpy(buffer + 2, radix_100_table + two_digits * 2, 2);
// Remaining 4 digits are all zero?
if (std::uint32_t(prod) <=
std::uint32_t((std::uint64_t(1) << 32) / 1'0000)) {
buffer += (3 + unsigned(buffer[3] > '0'));
}
else {
// At least one of the remaining 4 digits are nonzero.
// Obtain the next two digits.
prod = std::uint32_t(prod) * std::uint64_t(100);
two_digits = std::uint32_t(prod >> 32);
std::memcpy(buffer + 4, radix_100_table + two_digits * 2, 2);
// Remaining 2 digits are all zero?
if (std::uint32_t(prod) <=
std::uint32_t((std::uint64_t(1) << 32) / 100)) {
buffer += (5 + unsigned(buffer[5] > '0'));
}
else {
// Obtain the last two digits.
prod = std::uint32_t(prod) * std::uint64_t(100);
two_digits = std::uint32_t(prod >> 32);
std::memcpy(buffer + 6, radix_100_table + two_digits * 2, 2);
buffer += (7 + unsigned(buffer[7] > '0'));
}
}
}
}
else if (s32 >= 1'0000) {
// 5 or 6 digits.
// 429497 = ceil(2^32 / 1'0000)
auto prod = s32 * std::uint64_t(429497);
auto two_digits = std::uint32_t(prod >> 32);
// If s32 is of 6 digits, increase the exponent by 5.
// Otherwise, increase it by 4.
exponent += (4 + unsigned(two_digits >= 10));
// Write the first digit and the decimal point.
std::memcpy(buffer, radix_100_head_table + two_digits * 2, 2);
// This third character may be overwritten later but we don't care.
buffer[2] = radix_100_table[two_digits * 2 + 1];
// Remaining 4 digits are all zero?
if (std::uint32_t(prod) <=
std::uint32_t((std::uint64_t(1) << 32) / 1'0000)) {
// The number of characters actually written is 1 or 3, similarly to the
// case of 7 or 8 digits.
buffer +=
(1 + (unsigned(two_digits >= 10) & unsigned(buffer[2] > '0')) * 2);
}
else {
// At least one of the remaining 4 digits are nonzero.
// After this adjustment, now the first destination becomes buffer + 2.
buffer += unsigned(two_digits >= 10);
// Obtain the next two digits.
prod = std::uint32_t(prod) * std::uint64_t(100);
two_digits = std::uint32_t(prod >> 32);
std::memcpy(buffer + 2, radix_100_table + two_digits * 2, 2);
// Remaining 2 digits are all zero?
if (std::uint32_t(prod) <=
std::uint32_t((std::uint64_t(1) << 32) / 100)) {
buffer += (3 + unsigned(buffer[3] > '0'));
}
else {
// Obtain the last two digits.
prod = std::uint32_t(prod) * std::uint64_t(100);
two_digits = std::uint32_t(prod >> 32);
std::memcpy(buffer + 4, radix_100_table + two_digits * 2, 2);
buffer += (5 + unsigned(buffer[5] > '0'));
}
}
}
else if (s32 >= 100) {
// 3 or 4 digits.
// 42949673 = ceil(2^32 / 100)
auto prod = s32 * std::uint64_t(42949673);
auto two_digits = std::uint32_t(prod >> 32);
// If s32 is of 4 digits, increase the exponent by 3.
// Otherwise, increase it by 2.
exponent += (2 + int(two_digits >= 10));
// Write the first digit and the decimal point.
std::memcpy(buffer, radix_100_head_table + two_digits * 2, 2);
// This third character may be overwritten later but we don't care.
buffer[2] = radix_100_table[two_digits * 2 + 1];
// Remaining 2 digits are all zero?
if (std::uint32_t(prod) <= std::uint32_t((std::uint64_t(1) << 32) / 100)) {
// The number of characters actually written is 1 or 3, similarly to the
// case of 7 or 8 digits.
buffer +=
(1 + (unsigned(two_digits >= 10) & unsigned(buffer[2] > '0')) * 2);
}
else {
// At least one of the remaining 2 digits are nonzero.
// After this adjustment, now the first destination becomes buffer + 2.
buffer += unsigned(two_digits >= 10);
// Obtain the last two digits.
prod = std::uint32_t(prod) * std::uint64_t(100);
two_digits = std::uint32_t(prod >> 32);
std::memcpy(buffer + 2, radix_100_table + two_digits * 2, 2);
buffer += (3 + unsigned(buffer[3] > '0'));
}
}
else {
// 1 or 2 digits.
// If s32 is of 2 digits, increase the exponent by 1.
exponent += int(s32 >= 10);
// Write the first digit and the decimal point.
std::memcpy(buffer, radix_100_head_table + s32 * 2, 2);
// This third character may be overwritten later but we don't care.
buffer[2] = radix_100_table[s32 * 2 + 1];
// The number of characters actually written is 1 or 3, similarly to the
// case of 7 or 8 digits.
buffer += (1 + (unsigned(s32 >= 10) & unsigned(buffer[2] > '0')) * 2);
}
}
template <>
inline char *to_chars<float, default_float_traits<float>>(
std::uint32_t s32, int exponent, char *buffer) noexcept {
// Print significand.
print_9_digits(s32, exponent, buffer);
// Print exponent and return
if (exponent < 0) {
std::memcpy(buffer, "E-", 2);
buffer += 2;
exponent = -exponent;
}
else {
buffer[0] = 'E';
buffer += 1;
}
if (exponent >= 10) {
std::memcpy(buffer, &radix_100_table[exponent * 2], 2);
buffer += 2;
}
else {
buffer[0] = char('0' + exponent);
buffer += 1;
}
return buffer;
}
template <>
inline char *to_chars<double, default_float_traits<double>>(
std::uint64_t const significand, int exponent, char *buffer) noexcept {
// Print significand by decomposing it into a 9-digit block and a 8-digit
// block.
std::uint32_t first_block, second_block;
bool no_second_block;
if (significand >= 1'0000'0000) {
first_block = std::uint32_t(significand / 1'0000'0000);
second_block = std::uint32_t(significand) - first_block * 1'0000'0000;
exponent += 8;
no_second_block = (second_block == 0);
}
else {
first_block = std::uint32_t(significand);
no_second_block = true;
}
if (no_second_block) {
print_9_digits(first_block, exponent, buffer);
}
else {
// We proceed similarly to print_9_digits(), but since we do not need to
// remove trailing zeros, the procedure is a bit simpler.
if (first_block >= 1'0000'0000) {
// The input is of 17 digits, thus there should be no trailing zero at
// all. The first block is of 9 digits. 1441151882 = ceil(2^57 /
// 1'0000'0000) + 1
auto prod = first_block * std::uint64_t(1441151882);
prod >>= 25;
std::memcpy(buffer, radix_100_head_table + std::uint32_t(prod >> 32) * 2,
2);
prod = std::uint32_t(prod) * std::uint64_t(100);
std::memcpy(buffer + 2, radix_100_table + std::uint32_t(prod >> 32) * 2,
2);
prod = std::uint32_t(prod) * std::uint64_t(100);
std::memcpy(buffer + 4, radix_100_table + std::uint32_t(prod >> 32) * 2,
2);
prod = std::uint32_t(prod) * std::uint64_t(100);
std::memcpy(buffer + 6, radix_100_table + std::uint32_t(prod >> 32) * 2,
2);
prod = std::uint32_t(prod) * std::uint64_t(100);
std::memcpy(buffer + 8, radix_100_table + std::uint32_t(prod >> 32) * 2,
2);
// The second block is of 8 digits.
// 281474978 = ceil(2^48 / 100'0000) + 1
prod = second_block * std::uint64_t(281474978);
prod >>= 16;
prod += 1;
std::memcpy(buffer + 10, radix_100_table + std::uint32_t(prod >> 32) * 2,
2);
prod = std::uint32_t(prod) * std::uint64_t(100);
std::memcpy(buffer + 12, radix_100_table + std::uint32_t(prod >> 32) * 2,
2);
prod = std::uint32_t(prod) * std::uint64_t(100);
std::memcpy(buffer + 14, radix_100_table + std::uint32_t(prod >> 32) * 2,
2);
prod = std::uint32_t(prod) * std::uint64_t(100);
std::memcpy(buffer + 16, radix_100_table + std::uint32_t(prod >> 32) * 2,
2);
exponent += 8;
buffer += 18;
}
else {
if (first_block >= 100'0000) {
// 7 or 8 digits.
// 281474978 = ceil(2^48 / 100'0000) + 1
auto prod = first_block * std::uint64_t(281474978);
prod >>= 16;
auto two_digits = std::uint32_t(prod >> 32);
std::memcpy(buffer, radix_100_head_table + two_digits * 2, 2);
buffer[2] = radix_100_table[two_digits * 2 + 1];
exponent += (6 + unsigned(two_digits >= 10));
buffer += unsigned(two_digits >= 10);
// Print remaining 6 digits.
prod = std::uint32_t(prod) * std::uint64_t(100);
std::memcpy(buffer + 2, radix_100_table + std::uint32_t(prod >> 32) * 2,
2);
prod = std::uint32_t(prod) * std::uint64_t(100);
std::memcpy(buffer + 4, radix_100_table + std::uint32_t(prod >> 32) * 2,
2);
prod = std::uint32_t(prod) * std::uint64_t(100);
std::memcpy(buffer + 6, radix_100_table + std::uint32_t(prod >> 32) * 2,
2);
buffer += 8;
}
else if (first_block >= 1'0000) {
// 5 or 6 digits.
// 429497 = ceil(2^32 / 1'0000)
auto prod = first_block * std::uint64_t(429497);
auto two_digits = std::uint32_t(prod >> 32);
std::memcpy(buffer, radix_100_head_table + two_digits * 2, 2);
buffer[2] = radix_100_table[two_digits * 2 + 1];
exponent += (4 + unsigned(two_digits >= 10));
buffer += unsigned(two_digits >= 10);
// Print remaining 4 digits.
prod = std::uint32_t(prod) * std::uint64_t(100);
std::memcpy(buffer + 2, radix_100_table + std::uint32_t(prod >> 32) * 2,
2);
prod = std::uint32_t(prod) * std::uint64_t(100);
std::memcpy(buffer + 4, radix_100_table + std::uint32_t(prod >> 32) * 2,
2);
buffer += 6;
}
else if (first_block >= 100) {
// 3 or 4 digits.
// 42949673 = ceil(2^32 / 100)
auto prod = first_block * std::uint64_t(42949673);
auto two_digits = std::uint32_t(prod >> 32);
std::memcpy(buffer, radix_100_head_table + two_digits * 2, 2);
buffer[2] = radix_100_table[two_digits * 2 + 1];
exponent += (2 + unsigned(two_digits >= 10));
buffer += unsigned(two_digits >= 10);
// Print remaining 2 digits.
prod = std::uint32_t(prod) * std::uint64_t(100);
std::memcpy(buffer + 2, radix_100_table + std::uint32_t(prod >> 32) * 2,
2);
buffer += 4;
}
else {
// 1 or 2 digits.
std::memcpy(buffer, radix_100_head_table + first_block * 2, 2);
buffer[2] = radix_100_table[first_block * 2 + 1];
exponent += unsigned(first_block >= 10);
buffer += (2 + unsigned(first_block >= 10));
}
// Next, print the second block.
// The second block is of 8 digits, but we may have trailing zeros.
// 281474978 = ceil(2^48 / 100'0000) + 1
auto prod = second_block * std::uint64_t(281474978);
prod >>= 16;
prod += 1;
auto two_digits = std::uint32_t(prod >> 32);
std::memcpy(buffer, radix_100_table + two_digits * 2, 2);
// Remaining 6 digits are all zero?
if (std::uint32_t(prod) <=
std::uint32_t((std::uint64_t(1) << 32) / 100'0000)) {
buffer += (1 + unsigned(buffer[1] > '0'));
}
else {
// Obtain the next two digits.
prod = std::uint32_t(prod) * std::uint64_t(100);
two_digits = std::uint32_t(prod >> 32);
std::memcpy(buffer + 2, radix_100_table + two_digits * 2, 2);
// Remaining 4 digits are all zero?
if (std::uint32_t(prod) <=
std::uint32_t((std::uint64_t(1) << 32) / 1'0000)) {
buffer += (3 + unsigned(buffer[3] > '0'));
}
else {
// Obtain the next two digits.
prod = std::uint32_t(prod) * std::uint64_t(100);
two_digits = std::uint32_t(prod >> 32);
std::memcpy(buffer + 4, radix_100_table + two_digits * 2, 2);
// Remaining 2 digits are all zero?
if (std::uint32_t(prod) <=
std::uint32_t((std::uint64_t(1) << 32) / 100)) {
buffer += (5 + unsigned(buffer[5] > '0'));
}
else {
// Obtain the last two digits.
prod = std::uint32_t(prod) * std::uint64_t(100);
two_digits = std::uint32_t(prod >> 32);
std::memcpy(buffer + 6, radix_100_table + two_digits * 2, 2);
buffer += (7 + unsigned(buffer[7] > '0'));
}
}
}
}
}
// Print exponent and return
if (exponent < 0) {
std::memcpy(buffer, "E-", 2);
buffer += 2;
exponent = -exponent;
}
else {
buffer[0] = 'E';
buffer += 1;
}
if (exponent >= 100) {
// d1 = exponent / 10; d2 = exponent % 10;
// 6554 = ceil(2^16 / 10)
auto prod = std::uint32_t(exponent) * std::uint32_t(6554);
auto d1 = prod >> 16;
prod = std::uint16_t(prod) * std::uint32_t(5); // * 10
auto d2 = prod >> 15; // >> 16
std::memcpy(buffer, &radix_100_table[d1 * 2], 2);
buffer[2] = char('0' + d2);
buffer += 3;
}
else if (exponent >= 10) {
std::memcpy(buffer, &radix_100_table[exponent * 2], 2);
buffer += 2;
}
else {
buffer[0] = char('0' + exponent);
buffer += 1;
}
return buffer;
}
// Avoid needless ABI overhead incurred by tag dispatch.
template <class PolicyHolder, class Float, class FloatTraits>
char *to_chars_n_impl(float_bits<Float, FloatTraits> br,
char *buffer) noexcept {
auto const exponent_bits = br.extract_exponent_bits();
auto const s = br.remove_exponent_bits(exponent_bits);
if (br.is_finite(exponent_bits)) {
if (s.is_negative()) {
*buffer = '-';
++buffer;
}
if (br.is_nonzero()) {
auto result = to_decimal<Float, FloatTraits>(
s, exponent_bits, policy::sign::ignore, policy::trailing_zero::ignore,
typename PolicyHolder::decimal_to_binary_rounding_policy{},
typename PolicyHolder::binary_to_decimal_rounding_policy{},
typename PolicyHolder::cache_policy{});
return to_chars_detail::to_chars<Float, FloatTraits>(
result.significand, result.exponent, buffer);
}
else {
std::memcpy(buffer, "0E0", 3);
return buffer + 3;
}
}
else {
if (s.has_all_zero_significand_bits()) {
if (s.is_negative()) {
*buffer = '-';
++buffer;
}
std::memcpy(buffer, "Infinity", 8);
return buffer + 8;
}
else {
std::memcpy(buffer, "NaN", 3);
return buffer + 3;
}
}
}
} // namespace to_chars_detail
// Returns the next-to-end position
template <class Float, class FloatTraits = default_float_traits<Float>,
class... Policies>
char *to_chars_n(Float x, char *buffer, Policies... policies) noexcept {
using namespace jkj::dragonbox::detail::policy_impl;
using policy_holder = decltype(make_policy_holder(
base_default_pair_list<
base_default_pair<decimal_to_binary_rounding::base,
decimal_to_binary_rounding::nearest_to_even>,
base_default_pair<binary_to_decimal_rounding::base,
binary_to_decimal_rounding::to_even>,
base_default_pair<cache::base, cache::full>>{},
policies...));
return to_chars_detail::to_chars_n_impl<policy_holder>(
float_bits<Float, FloatTraits>(x), buffer);
}
// Null-terminate and bypass the return value of fp_to_chars_n
template <class Float, class FloatTraits = default_float_traits<Float>,
class... Policies>
char *to_chars(Float x, char *buffer, Policies... policies) noexcept {
auto ptr = to_chars_n<Float, FloatTraits>(x, buffer, policies...);
*ptr = '\0';
return ptr;
}
// Maximum required buffer size (excluding null-terminator)
template <class FloatFormat>
inline constexpr std::size_t max_output_string_length =
std::is_same_v<FloatFormat, ieee754_binary32>
?
// sign(1) + significand(9) + decimal_point(1) + exp_marker(1) +
// exp_sign(1) + exp(2)
(1 + 9 + 1 + 1 + 1 + 2)
:
// format == ieee754_format::binary64
// sign(1) + significand(17) + decimal_point(1) + exp_marker(1) +
// exp_sign(1) + exp(3)
(1 + 17 + 1 + 1 + 1 + 3);
} // namespace jkj::dragonbox
#endif
File diff suppressed because it is too large Load Diff
+321
View File
@@ -0,0 +1,321 @@
//=== itoa.h - Fast integer to ascii conversion --*- C++ -*-//
//
// The MIT License (MIT)
// Copyright (c) 2016 Arturo Martin-de-Nicolas
//
// 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.
//===----------------------------------------------------------------------===//
#ifndef DEC_ITOA_IMPL_H
#define DEC_ITOA_IMPL_H
#include <array>
#include <cstddef>
#include <cstdint>
#include <cstring> // memcpy
#include <type_traits>
namespace dec_ {
// Using a lookup table to convert binary numbers from 0 to 99
// into ascii characters as described by Andrei Alexandrescu in
// https://www.facebook.com/notes/facebook-engineering/
// three-optimization-tips-for-c/10151361643253920/
template <typename T, size_t N, typename Gen, size_t... Is>
constexpr auto generate_array(Gen&& item, std::index_sequence<Is...>) {
return std::array<T, N>{{item(Is)...}};
}
const std::array<char, 200> digits = generate_array<char, 200>(
[](size_t i) {
return char('0' + ((i % 2) ? ((i / 2) % 10) : ((i / 2) / 10)));
},
std::make_index_sequence<200>{});
// extern const std::array<char,200> digits;
static inline uint16_t const& dd(uint8_t u) {
return reinterpret_cast<uint16_t const*>(digits.data())[u];
}
template <typename T>
static constexpr T pow10(size_t x) {
return x ? 10 * pow10<T>(x - 1) : 1;
}
// Division by a power of 10 is implemented using a multiplicative inverse.
// This strength reduction is also done by optimizing compilers, but
// presently the fastest results are produced by using the values
// for the multiplication and the shift as given by the algorithm
// described by Agner Fog in "Optimizing Subroutines in Assembly Language"
//
// http://www.agner.org/optimize/optimizing_assembly.pdf
//
// "Integer division by a constant (all processors)
// A floating point number can be divided by a constant by multiplying
// with the reciprocal. If we want to do the same with integers, we have
// to scale the reciprocal by 2n and then shift the product to the right
// by n. There are various algorithms for finding a suitable value of n
// and compensating for rounding errors. The algorithm described below
// was invented by Terje Mathisen, Norway, and not published elsewhere."
// using uint128_t = unsigned __int128;
template <typename UInt, bool A, UInt M, unsigned S>
struct MulInv {
using type = UInt;
static constexpr bool a{A};
static constexpr UInt m{M};
static constexpr unsigned s{S};
};
template <int, int, class...>
struct UT;
template <int N, class T, class... Ts>
struct UT<N, N, T, Ts...> {
using U = T;
};
template <int N, int M, class T, class... Ts>
struct UT<N, M, T, Ts...> {
using U = typename UT<N, 2 * M, Ts...>::U;
};
template <int N>
using MI = typename UT<N, 1, MulInv<uint8_t, 0, 205U, 11>,
MulInv<uint16_t, 1, 41943U, 22>,
MulInv<uint32_t, 0, 3518437209U, 45>,
MulInv<uint64_t, 0, 12379400392853802749U, 90>>::U;
template <int N>
using U = typename MI<N>::type;
// struct QR holds the result of dividing an unsigned N-byte variable
// by 10^N resulting in
template <size_t N>
struct QR {
U<N> q; // quotient with fewer than 2*N decimal digits
U<N / 2> r; // remainder with at most N decimal digits
};
template <size_t N>
QR<N> static inline split(U<N> u) {
constexpr MI<N> mi{};
U<N> q = (mi.m * (U<2 * N>(u) + mi.a)) >> mi.s;
return {q, U<N / 2>(u - q * pow10<U<N / 2>>(N))};
}
enum Direction { Fwd, Rev };
template <Direction D>
struct convert {
//===----------------------------------------------------------===//
// output the digits in either a forward or reverse direction
// use memcpy so compiler handles alignment on target architecture.
// Typically generates one store to memory with an optimizing
// compiler for target architecture that supports unaligned access.
//===----------------------------------------------------------===//
template <typename T>
static inline char* out(char* p, T&& obj) {
if (D == Rev)
p -= sizeof(T);
memcpy(p, reinterpret_cast<const void*>(&obj), sizeof(T));
if (D == Fwd)
p += sizeof(T);
return p;
}
//===----------------------------------------------------------===//
// head: find most significant digit, skip leading zeros
//===----------------------------------------------------------===//
// "x" contains quotient and remainder after division by 10^N
// quotient is less than 10^N
template <size_t N>
static inline char* head(char* p, QR<N> x) {
return (D == Fwd ? (tail(head(p, U<N / 2>(x.q)), x.r))
: (head(tail(p, x.r), U<N / 2>(x.q))));
}
// "u" is less than 10^2*N
template <typename UInt, size_t N = sizeof(UInt)>
static inline char* head(char* p, UInt u) {
return (u < pow10<U<N>>(N) ? (head(p, U<N / 2>(u)))
: (head<N>(p, split<N>(u))));
}
// recursion base case, selected when "u" is one byte
static inline char* head(char* p, U<1> u) {
return (u < 10 ? (out<char>(p, '0' + u)) : (out(p, dd(u))));
}
//===----------------------------------------------------------===//
// tail: produce all digits including leading zeros
//===----------------------------------------------------------===//
// recursive step, "u" is less than 10^2*N
template <typename UInt, size_t N = sizeof(UInt)>
static inline char* tail(char* p, UInt u) {
QR<N> x = split<N>(u);
return (D == Fwd ? (tail(tail(p, U<N / 2>(x.q)), x.r))
: (tail(tail(p, x.r), U<N / 2>(x.q))));
}
// recursion base case, selected when "u" is one byte
static inline char* tail(char* p, U<1> u) { return out(p, dd(u)); }
//===----------------------------------------------------------===//
// large values are >= 10^2*N
// where x contains quotient and remainder after division by 10^N
//===----------------------------------------------------------===//
template <size_t N>
static inline char* large(char* p, QR<N> x) {
QR<N> y = split<N>(x.q);
return (D == Fwd ? (tail(tail(head(p, U<N / 2>(y.q)), y.r), x.r))
: (head(tail(tail(p, x.r), y.r), U<N / 2>(y.q))));
}
//===----------------------------------------------------------===//
// handle values of "u" that might be >= 10^2*N
// where N is the size of "u" in bytes
//===----------------------------------------------------------===//
template <typename UInt, size_t N = sizeof(UInt)>
static inline char* itoa(char* p, UInt u) {
if (u < pow10<U<N>>(N))
return head(p, U<N / 2>(u));
QR<N> x = split<N>(u);
return (u < pow10<U<N>>(2 * N) ? (head<N>(p, x)) : (large<N>(p, x)));
}
// selected when "u" is one byte
static inline char* itoa(char* p, U<1> u) {
if (u < 10)
return out<char>(p, '0' + u);
if (u < 100)
return out(p, dd(u));
return (D == Fwd ? (out(out<char>(p, '0' + u / 100), dd(u % 100)))
: (out<char>(out(p, dd(u % 100)), '0' + u / 100)));
}
//===----------------------------------------------------------===//
// handle unsigned and signed integral operands
//===----------------------------------------------------------===//
// itoa: handle unsigned integral operands (selected by SFINAE)
template <typename U, std::enable_if_t<!std::is_signed<U>::value &&
std::is_integral<U>::value>* = nullptr>
static inline char* itoa(U u, char* p) {
return convert<D>::template itoa<>(p, u);
}
// itoa: handle signed integral operands (selected by SFINAE)
template <typename I, size_t N = sizeof(I),
std::enable_if_t<std::is_signed<I>::value &&
std::is_integral<I>::value>* = nullptr>
static inline char* itoa(I i, char* p) {
// Need "mask" to be filled with a copy of the sign bit.
// If "i" is a negative value, then the result of "operator >>"
// is implementation-defined, though usually it is an arithmetic
// right shift that replicates the sign bit.
// Use a conditional expression to be portable,
// a good optimizing compiler generates an arithmetic right shift
// and avoids the conditional branch.
U<N> mask = i < 0 ? ~U<N>(0) : 0;
// Now get the absolute value of "i" and cast to unsigned type U<N>.
// Cannot use std::abs() because the result is undefined
// in 2's complement systems for the most-negative value.
// Want to avoid conditional branch for performance reasons since
// CPU branch prediction will be ineffective when negative values
// occur randomly.
// Let "u" be "i" cast to unsigned type U<N>.
// Subtract "u" from 2*u if "i" is positive or 0 if "i" is negative.
// This yields the absolute value with the desired type without
// using a conditional branch and without invoking undefined or
// implementation defined behavior:
U<N> u = ((2 * U<N>(i)) & ~mask) - U<N>(i);
// Unconditionally store a minus sign when producing digits
// in a forward direction and increment the pointer only if
// the value is in fact negative.
// This avoids a conditional branch and is safe because we will
// always produce at least one digit and it will overwrite the
// minus sign when the value is not negative.
if (D == Fwd) {
*p = '-';
p += (mask & 1);
}
p = convert<D>::template itoa<>(p, u);
if (D == Rev && mask)
*--p = '-';
return p;
}
};
} // namespace dec_
// Programming interface: itoa_fwd, itoa_rev
template <typename I>
char* itoa_fwd(I i, char* p) {
return dec_::convert<dec_::Fwd>::itoa(i, p);
}
inline char* xtoa(long long sval, char* str, int radix, int signedp) {
unsigned long long uval;
unsigned int uradix = radix;
char* sp = str;
char* sp2;
char* sp3;
/* If signed, store sign at start of buffer for negative base-10 values */
if (signedp && (10 == uradix) && (0 > sval)) {
*sp++ = '-';
uval = -sval;
}
else {
uval = sval;
}
sp2 = sp;
do {
unsigned int rem = uval % uradix;
uval /= uradix;
if (10 > rem) {
*sp++ = '0' + (char)rem;
}
else {
*sp++ = 'A' + (char)rem - 10;
}
} while (0 < uval);
/* Mark end of string */
sp3 = sp;
*sp-- = 0;
/* Reverse string contents (excluding sign) in place */
while (sp2 < sp) {
char tmp = *sp2;
*sp2++ = *sp;
*sp-- = tmp;
}
return sp3;
}
template <typename I>
char* itoa_rev(I i, char* p) {
return dec_::convert<dec_::Rev>::itoa(i, p);
}
#endif // DEC_ITOA_IMPL_H
+143
View File
@@ -0,0 +1,143 @@
#pragma once
#include <cstdint>
#include <type_traits>
namespace iguana {
struct sint32_t {
using value_type = int32_t;
int32_t val;
bool operator==(const sint32_t& other) const { return val == other.val; }
};
inline bool operator==(sint32_t value1, int32_t value2) {
return value1.val == value2;
}
// for key in std::map
inline bool operator<(const sint32_t& lhs, const sint32_t& rhs) {
return lhs.val < rhs.val;
}
struct sint64_t {
using value_type = int64_t;
int64_t val;
bool operator==(const sint64_t& other) const { return val == other.val; }
};
inline bool operator==(sint64_t value1, int64_t value2) {
return value1.val == value2;
}
inline bool operator<(const sint64_t& lhs, const sint64_t& rhs) {
return lhs.val < rhs.val;
}
struct fixed32_t {
using value_type = uint32_t;
uint32_t val;
bool operator==(const fixed32_t& other) const { return val == other.val; }
};
inline bool operator==(fixed32_t value1, uint32_t value2) {
return value1.val == value2;
}
inline bool operator<(const fixed32_t& lhs, const fixed32_t& rhs) {
return lhs.val < rhs.val;
}
struct fixed64_t {
using value_type = uint64_t;
uint64_t val;
bool operator==(const fixed64_t& other) const { return val == other.val; }
};
inline bool operator==(fixed64_t value1, uint64_t value2) {
return value1.val == value2;
}
inline bool operator<(const fixed64_t& lhs, const fixed64_t& rhs) {
return lhs.val < rhs.val;
}
struct sfixed32_t {
using value_type = int32_t;
int32_t val;
bool operator==(const sfixed32_t& other) const { return val == other.val; }
};
inline bool operator==(sfixed32_t value1, int32_t value2) {
return value1.val == value2;
}
inline bool operator<(const sfixed32_t& lhs, const sfixed32_t& rhs) {
return lhs.val < rhs.val;
}
struct sfixed64_t {
using value_type = int64_t;
int64_t val;
bool operator==(const sfixed64_t& other) const { return val == other.val; }
};
inline bool operator==(sfixed64_t value1, int64_t value2) {
return value1.val == value2;
}
inline bool operator<(const sfixed64_t& lhs, const sfixed64_t& rhs) {
return lhs.val < rhs.val;
}
template <typename Type, typename T = std::decay_t<Type>>
constexpr bool is_pb_type_v =
std::is_same_v<sint32_t, T> || std::is_same_v<sint64_t, T> ||
std::is_same_v<fixed32_t, T> || std::is_same_v<fixed64_t, T> ||
std::is_same_v<sfixed32_t, T> || std::is_same_v<sfixed64_t, T>;
} // namespace iguana
// for key in std::unordered_map
namespace std {
template <>
struct hash<iguana::sint32_t> {
size_t operator()(const iguana::sint32_t& x) const noexcept {
return std::hash<int32_t>()(x.val);
}
};
template <>
struct hash<iguana::sint64_t> {
size_t operator()(const iguana::sint64_t& x) const noexcept {
return std::hash<int64_t>()(x.val);
}
};
template <>
struct hash<iguana::fixed32_t> {
size_t operator()(const iguana::fixed32_t& x) const noexcept {
return std::hash<uint32_t>()(x.val);
}
};
template <>
struct hash<iguana::fixed64_t> {
size_t operator()(const iguana::fixed64_t& x) const noexcept {
return std::hash<uint64_t>()(x.val);
}
};
template <>
struct hash<iguana::sfixed32_t> {
size_t operator()(const iguana::sfixed32_t& x) const noexcept {
return std::hash<int32_t>()(x.val);
}
};
template <>
struct hash<iguana::sfixed64_t> {
size_t operator()(const iguana::sfixed64_t& x) const noexcept {
return std::hash<int64_t>()(x.val);
}
};
} // namespace std
+131
View File
@@ -0,0 +1,131 @@
#pragma once
#include <cstddef>
#include <string>
#include <utility>
#include <vector>
namespace iguana::detail {
#if __cpp_lib_string_resize_and_overwrite >= 202110L
template <typename ch>
inline void resize(std::basic_string<ch> &str, std::size_t sz) {
str.resize_and_overwrite(sz, [sz](ch *, std::size_t) {
return sz;
});
}
#elif (defined(_MSC_VER) && _MSC_VER <= 1920)
// old msvc don't support visit private, discard it.
#else
template <typename Function, Function func_ptr>
class string_thief {
public:
friend void string_set_length_hacker(std::string &self, std::size_t sz) {
#if defined(_MSVC_STL_VERSION)
(self.*func_ptr)._Myval2._Mysize = sz;
#else
#if defined(_LIBCPP_VERSION)
(self.*func_ptr)(sz);
#else
#if (_GLIBCXX_USE_CXX11_ABI == 0) && defined(__GLIBCXX__)
(self.*func_ptr)()->_M_set_length_and_sharable(sz);
#else
#if defined(__GLIBCXX__)
(self.*func_ptr)(sz);
#endif
#endif
#endif
#endif
}
};
#if defined(__GLIBCXX__) // libstdc++
#if (_GLIBCXX_USE_CXX11_ABI == 0)
template class string_thief<decltype(&std::string::_M_rep),
&std::string::_M_rep>;
#else
template class string_thief<decltype(&std::string::_M_set_length),
&std::string::_M_set_length>;
#endif
#elif defined(_LIBCPP_VERSION)
template class string_thief<decltype(&std::string::__set_size),
&std::string::__set_size>;
#elif defined(_MSVC_STL_VERSION)
template class string_thief<decltype(&std::string::_Mypair),
&std::string::_Mypair>;
#endif
void string_set_length_hacker(std::string &, std::size_t);
template <typename ch>
inline void resize(std::basic_string<ch> &raw_str, std::size_t sz) {
std::string &str = *reinterpret_cast<std::string *>(&raw_str);
#if defined(__GLIBCXX__) || defined(_LIBCPP_VERSION) || \
defined(_MSVC_STL_VERSION)
if (sz > str.capacity()) {
str.reserve(sz);
}
string_set_length_hacker(str, sz);
str[sz] = '\0';
#else
raw_str.resize(sz);
#endif
}
#endif
#if (defined(_MSC_VER) && _MSC_VER <= 1920)
#else
void vector_set_length_hacker(std::vector<char> &self, std::size_t sz);
template <typename Function, Function func_ptr>
class vector_thief {
public:
friend void vector_set_length_hacker(std::vector<char> &self,
std::size_t sz) {
#if defined(_MSVC_STL_VERSION)
(self.*func_ptr)._Myval2._Mylast = self.data() + sz;
#else
#if defined(_LIBCPP_VERSION)
#if _LIBCPP_VERSION < 14000
((*(std::__vector_base<char, std::allocator<char> > *)(&self)).*func_ptr) =
self.data() + sz;
#else
(self.*func_ptr) = self.data() + sz;
#endif
#else
#if defined(__GLIBCXX__)
((*(std::_Vector_base<char, std::allocator<char> > *)(&self)).*func_ptr)
._M_finish = self.data() + sz;
#endif
#endif
#endif
}
};
#if defined(__GLIBCXX__) // libstdc++
template class vector_thief<decltype(&std::vector<char>::_M_impl),
&std::vector<char>::_M_impl>;
#elif defined(_LIBCPP_VERSION)
template class vector_thief<decltype(&std::vector<char>::__end_),
&std::vector<char>::__end_>;
#elif defined(_MSVC_STL_VERSION)
template class vector_thief<decltype(&std::vector<char>::_Mypair),
&std::vector<char>::_Mypair>;
#endif
template <typename ch>
inline void resize(std::vector<ch> &raw_vec, std::size_t sz) {
#if defined(__GLIBCXX__) || \
(defined(_LIBCPP_VERSION) && defined(_LIBCPP_HAS_NO_ASAN)) || \
defined(_MSVC_STL_VERSION)
std::vector<char> &vec = *reinterpret_cast<std::vector<char> *>(&raw_vec);
vec.reserve(sz);
vector_set_length_hacker(vec, sz);
#else
raw_vec.resize(sz);
#endif
}
#endif
}; // namespace iguana::detail
+17
View File
@@ -0,0 +1,17 @@
#pragma once
#include <string>
#if __has_include(<memory_resource>)
#include <memory_resource>
#endif
namespace iguana {
#ifdef IGUANA_ENABLE_PMR
#if __has_include(<memory_resource>)
inline char iguana_buf[2048];
inline std::pmr::monotonic_buffer_resource iguana_resource(iguana_buf, 2048);
using string_stream = std::pmr::string;
#endif
#else
using string_stream = std::string;
#endif
} // namespace iguana
+229
View File
@@ -0,0 +1,229 @@
//
// Created by QY on 2017-01-05.
//
#ifndef SERIALIZE_TRAITS_HPP
#define SERIALIZE_TRAITS_HPP
#include <deque>
#include <list>
#include <map>
#include <queue>
#include <type_traits>
#include <unordered_map>
#include <variant>
#include <vector>
#if __cplusplus > 201703L
#if __has_include(<span>)
#include <span>
#endif
#endif
#include "iguana/define.h"
namespace iguana {
template <class T>
struct is_signed_intergral_like
: std::integral_constant<bool, (std::is_integral<T>::value) &&
std::is_signed<T>::value> {};
template <class T>
struct is_unsigned_intergral_like
: std::integral_constant<bool, (std::is_integral<T>::value) &&
std::is_unsigned<T>::value> {};
template <template <typename...> class U, typename T>
struct is_template_instant_of : std::false_type {};
template <template <typename...> class U, typename... args>
struct is_template_instant_of<U, U<args...>> : std::true_type {};
template <typename T>
struct is_stdstring : is_template_instant_of<std::basic_string, T> {};
template <typename T>
struct is_tuple : is_template_instant_of<std::tuple, T> {};
#if __cplusplus > 201703L
#if __has_include(<span>)
template <typename>
struct is_span : std::false_type {};
template <typename T, size_t Extent>
struct is_span<std::span<T, Extent>> : std::true_type {};
#endif
#endif
template <class T>
struct is_sequence_container
: std::integral_constant<
bool, is_template_instant_of<std::deque, T>::value ||
is_template_instant_of<std::list, T>::value ||
is_template_instant_of<std::vector, T>::value ||
is_template_instant_of<std::queue, T>::value> {};
template <class T>
struct is_associat_container
: std::integral_constant<
bool, is_template_instant_of<std::map, T>::value ||
is_template_instant_of<std::unordered_map, T>::value> {};
template <class T>
struct is_emplace_back_able
: std::integral_constant<
bool, is_template_instant_of<std::deque, T>::value ||
is_template_instant_of<std::list, T>::value ||
is_template_instant_of<std::vector, T>::value> {};
template <typename T, typename Tuple>
struct has_type;
template <typename T, typename... Us>
struct has_type<T, std::tuple<Us...>>
: std::disjunction<std::is_same<T, Us>...> {};
template <class T>
struct member_tratis {};
template <class T, class Owner>
struct member_tratis<T Owner::*> {
using owner_type = Owner;
using value_type = T;
};
template <typename T>
inline constexpr bool is_int64_v =
std::is_same_v<T, int64_t> || std::is_same_v<T, uint64_t>;
template <typename T>
struct is_variant : std::false_type {};
template <typename... T>
struct is_variant<std::variant<T...>> : std::true_type {};
template <class T>
struct member_traits {
using value_type = T;
};
template <class T, class Owner>
struct member_traits<T Owner::*> {
using owner_type = Owner;
using value_type = T;
};
template <class T>
using member_value_type_t = typename member_traits<T>::value_type;
template <typename T, std::size_t I, typename = void>
struct variant_type_at {
using type = T;
};
template <typename T, std::size_t I>
struct variant_type_at<T, I, std::enable_if_t<is_variant<T>::value>> {
using type = std::variant_alternative_t<I, T>;
};
template <typename T, std::size_t I>
using variant_type_at_t =
typename variant_type_at<typename member_traits<T>::value_type, I>::type;
/// concepts
template <typename Type>
constexpr bool is_char_v =
std::is_same_v<Type, signed char> || std::is_same_v<Type, char> ||
std::is_same_v<Type, unsigned char> || std::is_same_v<Type, wchar_t> ||
std::is_same_v<Type, char16_t> || std::is_same_v<Type, char32_t>
#ifdef __cpp_lib_char8_t
|| std::is_same_v<Type, char8_t>
#endif
;
#if __cplusplus >= 202002L
template <typename Type>
concept is_container_v = requires(Type container) {
typename std::remove_cvref_t<Type>::value_type;
container.size();
container.begin();
container.end();
};
template <typename Type>
concept is_resizable_char_container_v = requires(Type container) {
typename std::remove_cvref_t<Type>::value_type;
container.size();
container.begin();
container.end();
requires is_char_v<typename std::remove_cvref_t<Type>::value_type>;
container.resize(std::size_t{});
};
template <typename T>
concept char_writer = requires(T t) {
t.write((const char *)nullptr, std::size_t{});
};
template <typename T>
concept char_reader = requires(T t) {
t.read((char *)nullptr, std::size_t{});
};
#else
template <typename T, typename = void>
struct container_impl : std::false_type {};
template <typename T>
struct container_impl<T,
std::void_t<typename std::remove_cvref_t<T>::value_type,
decltype(std::declval<T>().size()),
decltype(std::declval<T>().begin()),
decltype(std::declval<T>().end())>>
: std::true_type {};
template <typename T>
constexpr bool is_container_v = container_impl<T>::value;
template <typename T, typename = void>
struct resizable_char_container_impl : std::false_type {};
template <typename T>
struct resizable_char_container_impl<
T, std::void_t<typename std::remove_cvref_t<T>::value_type,
decltype(std::declval<T>().size()),
decltype(std::declval<T>().begin()),
decltype(std::declval<T>().end()),
std::enable_if_t<
is_char_v<typename std::remove_cvref_t<T>::value_type>>,
decltype(std::declval<T>().resize(std::size_t{}))>>
: std::true_type {};
template <typename T>
constexpr bool is_resizable_char_container_v =
resizable_char_container_impl<T>::value;
template <typename T, typename = void>
struct char_writer_impl : std::false_type {};
template <typename T>
struct char_writer_impl<T, std::void_t<decltype(std::declval<T>().write(
(const char *)nullptr, std::size_t{}))>>
: std::true_type {};
template <typename T>
constexpr bool char_writer = char_writer_impl<T>::value;
template <typename T, typename = void>
struct char_reader_impl : std::false_type {};
template <typename T>
struct char_reader_impl<T, std::void_t<decltype(std::declval<T>().read(
(char *)nullptr, std::size_t{}))>>
: std::true_type {};
template <typename T>
constexpr bool char_reader = char_reader_impl<T>::value;
#endif
} // namespace iguana
#endif // SERIALIZE_TRAITS_HPP
+154
View File
@@ -0,0 +1,154 @@
#pragma once
#include <cassert>
#include <stdexcept>
#include "iguana/define.h"
namespace iguana {
// https://github.com/Tencent/rapidjson/blob/master/include/rapidjson/reader.h
template <typename Ch = char, typename It>
inline unsigned parse_unicode_hex4(It &&it) {
unsigned codepoint = 0;
for (int i = 0; i < 4; i++) {
Ch c = *it;
codepoint <<= 4;
codepoint += static_cast<unsigned>(c);
if (c >= '0' && c <= '9')
codepoint -= '0';
else if (c >= 'A' && c <= 'F')
codepoint -= 'A' - 10;
else if (c >= 'a' && c <= 'f')
codepoint -= 'a' - 10;
else {
throw std::runtime_error("Invalid Unicode Escape Hex");
}
++it;
}
return codepoint;
}
// https://github.com/Tencent/rapidjson/blob/master/include/rapidjson/encodings.h
template <typename Ch = char, typename OutputStream>
inline void encode_utf8(OutputStream &os, unsigned codepoint) {
if (codepoint > 0x10FFFF)
IGUANA_UNLIKELY {
throw std::runtime_error("Invalid unicode codepoint:" +
std::to_string(codepoint));
}
if (codepoint <= 0x7F)
os.push_back(static_cast<Ch>(codepoint & 0xFF));
else if (codepoint <= 0x7FF) {
os.push_back(static_cast<Ch>(0xC0 | ((codepoint >> 6) & 0xFF)));
os.push_back(static_cast<Ch>(0x80 | ((codepoint & 0x3F))));
}
else if (codepoint <= 0xFFFF) {
os.push_back(static_cast<Ch>(0xE0 | ((codepoint >> 12) & 0xFF)));
os.push_back(static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F)));
os.push_back(static_cast<Ch>(0x80 | (codepoint & 0x3F)));
}
else {
os.push_back(static_cast<Ch>(0xF0 | ((codepoint >> 18) & 0xFF)));
os.push_back(static_cast<Ch>(0x80 | ((codepoint >> 12) & 0x3F)));
os.push_back(static_cast<Ch>(0x80 | ((codepoint >> 6) & 0x3F)));
os.push_back(static_cast<Ch>(0x80 | (codepoint & 0x3F)));
}
}
// https://github.com/Tencent/rapidjson/blob/master/include/rapidjson/encodings.h
static inline unsigned char GetRange(unsigned char c) {
static const unsigned char type[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0x10, 0x10, 0x10, 0x10,
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
0x40, 0x40, 0x40, 0x40, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 10, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3,
11, 6, 6, 6, 5, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8,
};
return type[c];
}
// https://github.com/Tencent/rapidjson/blob/master/include/rapidjson/encodings.h
template <typename Ch = char, typename It>
inline bool decode_utf8(It &&it, unsigned &codepoint) {
auto c = *(it++);
bool result = true;
auto copy = [&]() IGUANA__INLINE_LAMBDA {
c = *(it++);
codepoint = (codepoint << 6) | (static_cast<unsigned char>(c) & 0x3Fu);
};
auto trans = [&](unsigned mask) IGUANA__INLINE_LAMBDA {
result &= ((GetRange(static_cast<unsigned char>(c)) & mask) != 0);
};
auto tail = [&]() IGUANA__INLINE_LAMBDA {
copy();
trans(0x70);
};
if (!(c & 0x80)) {
codepoint = static_cast<unsigned char>(c);
return true;
}
unsigned char type = GetRange(static_cast<unsigned char>(c));
if (type >= 32) {
codepoint = 0;
}
else {
codepoint = (0xFFu >> type) & static_cast<unsigned char>(c);
}
switch (type) {
case 2:
tail();
return result;
case 3:
tail();
tail();
return result;
case 4:
copy();
trans(0x50);
tail();
return result;
case 5:
copy();
trans(0x10);
tail();
tail();
return result;
case 6:
tail();
tail();
tail();
return result;
case 10:
copy();
trans(0x20);
tail();
return result;
case 11:
copy();
trans(0x60);
tail();
tail();
return result;
default:
return false;
}
}
} // namespace iguana
+197
View File
@@ -0,0 +1,197 @@
#pragma once
#include "common.hpp"
namespace iguana {
using base = detail::base;
constexpr inline uint8_t ENABLE_JSON = 0x01;
constexpr inline uint8_t ENABLE_YAML = 0x02;
constexpr inline uint8_t ENABLE_XML = 0x04;
constexpr inline uint8_t ENABLE_PB = 0x08;
constexpr inline uint8_t ENABLE_ALL = 0x0F;
template <typename T, uint8_t ENABLE_FLAG = ENABLE_PB>
struct base_impl : public base {
base_impl() { [[maybe_unused]] static bool r = register_type<T>(); }
void to_pb(std::string& str) const override {
if constexpr ((ENABLE_FLAG & ENABLE_PB) != 0) {
to_pb_adl((iguana_adl_t*)nullptr, *(static_cast<T const*>(this)), str);
}
else {
throw std::runtime_error("Protobuf Disabled");
}
}
void from_pb(std::string_view str) override {
if constexpr ((ENABLE_FLAG & ENABLE_PB) != 0) {
from_pb_adl((iguana_adl_t*)nullptr, *(static_cast<T*>(this)), str);
}
else {
throw std::runtime_error("Protobuf Disabled");
}
}
void to_json(std::string& str) const override {
if constexpr ((ENABLE_FLAG & ENABLE_JSON) != 0) {
to_json_adl((iguana_adl_t*)nullptr, *(static_cast<T const*>(this)), str);
}
else {
throw std::runtime_error("Json Disabled");
}
}
void from_json(std::string_view str) override {
if constexpr ((ENABLE_FLAG & ENABLE_JSON) != 0) {
from_json_adl((iguana_adl_t*)nullptr, *(static_cast<T*>(this)), str);
}
else {
throw std::runtime_error("Json Disabled");
}
}
void to_xml(std::string& str) const override {
if constexpr ((ENABLE_FLAG & ENABLE_XML) != 0) {
to_xml_adl((iguana_adl_t*)nullptr, *(static_cast<T const*>(this)), str);
}
else {
throw std::runtime_error("Xml Disabled");
}
}
void from_xml(std::string_view str) override {
if constexpr ((ENABLE_FLAG & ENABLE_XML) != 0) {
from_xml_adl((iguana_adl_t*)nullptr, *(static_cast<T*>(this)), str);
}
else {
throw std::runtime_error("Xml Disabled");
}
}
void to_yaml(std::string& str) const override {
if constexpr ((ENABLE_FLAG & ENABLE_YAML) != 0) {
to_yaml_adl((iguana_adl_t*)nullptr, *(static_cast<T const*>(this)), str);
}
else {
throw std::runtime_error("Yaml Disabled");
}
}
void from_yaml(std::string_view str) override {
if constexpr ((ENABLE_FLAG & ENABLE_YAML) != 0) {
from_yaml_adl((iguana_adl_t*)nullptr, *(static_cast<T*>(this)), str);
}
else {
throw std::runtime_error("Yaml Disabled");
}
}
iguana::detail::field_info get_field_info(
std::string_view name) const override {
static auto map =
detail::get_members(ylt::reflection::internal::wrapper<T>::value);
iguana::detail::field_info info{};
for (auto const& [no, field] : map) {
if (info.offset > 0) {
break;
}
std::visit(
[&](auto const& val) {
if (val.field_name == name) {
info.offset = val.offset;
using value_type =
typename std::remove_reference_t<decltype(val)>::value_type;
#if defined(__clang__) || defined(_MSC_VER) || \
(defined(__GNUC__) && __GNUC__ > 8)
info.type_name = type_string<value_type>();
#endif
}
},
field);
}
return info;
}
std::vector<std::string_view> get_fields_name() const override {
static auto map =
detail::get_members(ylt::reflection::internal::wrapper<T>::value);
std::vector<std::string_view> vec;
for (auto const& [no, val] : map) {
std::visit(
[&](auto const& field) {
std::string_view const current_name{field.field_name.data(),
field.field_name.size()};
if (vec.empty() || (vec.back() != current_name)) {
vec.push_back(current_name);
}
},
val);
}
return vec;
}
std::any get_field_any(std::string_view name) const override {
static auto map =
detail::get_members(ylt::reflection::internal::wrapper<T>::value);
std::any result;
for (auto const& [no, field] : map) {
if (result.has_value()) {
break;
}
std::visit(
[&](auto const& val) {
if (val.field_name == name) {
using value_type =
typename std::remove_reference_t<decltype(val)>::value_type;
auto ptr =
reinterpret_cast<const char*>(static_cast<const T*>(this)) +
val.offset;
result = *((value_type*)ptr);
}
},
field);
}
return result;
}
virtual ~base_impl() {}
mutable size_t cache_size = 0;
protected:
void* object_ptr() override { return static_cast<T*>(this); }
const void* object_ptr() const override {
return static_cast<const T*>(this);
}
private:
virtual void dummy() {
// make sure init T before main, and can register_type before main.
[[maybe_unused]] static auto t =
ylt::reflection::internal::wrapper<T>::value;
}
};
IGUANA_INLINE std::shared_ptr<base> create_instance(std::string_view name) {
auto it = iguana::detail::g_pb_map.find(name);
if (it == iguana::detail::g_pb_map.end()) {
throw std::invalid_argument(std::string(name) +
"not inheried from iguana::base_impl");
}
return it->second();
}
} // namespace iguana
#ifdef YLT_USE_CXX26_REFLECTION
namespace ylt::reflection::reflect26 {
template <>
constexpr inline bool skip_base_v<iguana::detail::base> = true;
template <typename T, uint8_t ENABLE_FLAG>
constexpr inline bool skip_base_v<iguana::base_impl<T, ENABLE_FLAG>> = true;
} // namespace ylt::reflection::reflect26
#endif
+100
View File
@@ -0,0 +1,100 @@
#pragma once
#include <cassert>
#include <map>
#include <system_error>
namespace iguana {
class iguana_category : public std::error_category {
public:
virtual const char *name() const noexcept override {
return "iguana::category";
}
virtual std::string message(int err_val) const override {
for (auto &pair : err_map_) {
if (pair.second == err_val) {
return pair.first;
}
}
return "unrecognized error";
}
int add_message(const std::string &msg) {
if (auto it = err_map_.find(msg); it != err_map_.end()) {
return it->second;
}
else {
err_++;
err_map_.emplace(msg, err_);
return err_;
}
}
int err_ = 0;
std::map<std::string, int> err_map_;
};
inline iguana::iguana_category &category() {
static iguana::iguana_category instance;
return instance;
}
inline std::error_code make_error_code(const std::string data = "") {
assert(!data.empty());
auto err = iguana::category().add_message(data);
return std::error_code(err, iguana::category());
}
// dom parse error code
enum class dom_errc {
ok = 0,
wrong_type,
};
class iguana_dom_category : public std::error_category {
public:
virtual const char *name() const noexcept override {
return "iguana::dom_category";
}
virtual std::string message(int err_val) const override {
switch (static_cast<dom_errc>(err_val)) {
case dom_errc::ok:
return "ok";
case dom_errc::wrong_type: {
auto it = detail_msg_map_.find(dom_errc::wrong_type);
if (it != detail_msg_map_.end()) {
return std::string("wrong type, ")
.append("real type is ")
.append(it->second);
}
else {
return "wrong type";
}
}
default:
return "(unrecognized error)";
}
}
// private:
std::map<iguana::dom_errc, std::string> detail_msg_map_;
};
inline iguana::iguana_dom_category &dom_category() {
static iguana::iguana_dom_category instance;
return instance;
}
inline std::error_code make_error_code(iguana::dom_errc err,
const std::string &error_msg) {
auto &instance = iguana::dom_category();
if (!error_msg.empty()) {
instance.detail_msg_map_.emplace(err, error_msg);
}
return std::error_code((int)err, instance);
}
} // namespace iguana
+192
View File
@@ -0,0 +1,192 @@
#pragma once
#include <array>
#include <string_view>
#include "frozen/string.h"
#include "frozen/unordered_map.h"
namespace iguana {
#if defined(__clang__) || defined(_MSC_VER) || \
(defined(__GNUC__) && __GNUC__ > 8)
template <typename T>
constexpr std::string_view get_raw_name() {
#ifdef _MSC_VER
return __FUNCSIG__;
#else
return __PRETTY_FUNCTION__;
#endif
}
template <auto T>
constexpr std::string_view get_raw_name() {
#ifdef _MSC_VER
return __FUNCSIG__;
#else
return __PRETTY_FUNCTION__;
#endif
}
template <typename T>
inline constexpr std::string_view type_string() {
constexpr std::string_view sample = get_raw_name<int>();
constexpr size_t pos = sample.find("int");
constexpr std::string_view str = get_raw_name<T>();
constexpr auto next1 = str.rfind(sample[pos + 3]);
#if defined(_MSC_VER)
constexpr std::size_t npos = str.find_first_of(" ", pos);
if (npos != std::string_view::npos)
return str.substr(npos + 1, next1 - npos - 1);
else
return str.substr(pos, next1 - pos);
#else
return str.substr(pos, next1 - pos);
#endif
}
template <auto T>
inline constexpr std::string_view enum_string() {
constexpr std::string_view sample = get_raw_name<int>();
constexpr size_t pos = sample.find("int");
constexpr std::string_view str = get_raw_name<T>();
constexpr auto next1 = str.rfind(sample[pos + 3]);
#if defined(__clang__) || defined(_MSC_VER)
constexpr auto s1 = str.substr(pos, next1 - pos);
#else
constexpr auto s1 = str.substr(pos + 5, next1 - pos - 5);
#endif
return s1;
}
template <auto field>
inline constexpr std::string_view field_string() {
constexpr std::string_view raw_name = enum_string<field>();
return raw_name.substr(raw_name.rfind(":") + 1);
}
#if defined(__clang__) && (__clang_major__ >= 17)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wenum-constexpr-conversion"
#endif
template <typename E, E V>
constexpr std::string_view get_raw_name_with_v() {
#ifdef _MSC_VER
return __FUNCSIG__;
#else
return __PRETTY_FUNCTION__;
#endif
}
// True means the V is a valid enum value, and the second of the result is the
// name
template <typename E, E V>
constexpr std::pair<bool, std::string_view> try_get_enum_name() {
constexpr std::string_view sample_raw_name = get_raw_name_with_v<int, 5>();
constexpr size_t pos = sample_raw_name.find("5");
constexpr std::string_view enum_raw_name = get_raw_name_with_v<E, V>();
constexpr auto enum_end = enum_raw_name.rfind(&sample_raw_name[pos + 1]);
#ifdef _MSC_VER
constexpr auto enum_begin = enum_raw_name.rfind(',', enum_end) + 1;
#else
constexpr auto enum_begin = enum_raw_name.rfind(' ', enum_end) + 1;
#endif
constexpr std::string_view res =
enum_raw_name.substr(enum_begin, enum_end - enum_begin);
constexpr size_t pos_brackets = res.find(')');
size_t pos_colon = res.find("::");
return {pos_brackets == std::string_view::npos,
res.substr(pos_colon == std::string_view::npos ? 0 : pos_colon + 2)};
}
// Enumerate the numbers in a integer sequence to see if they are legal enum
// value
template <typename E, std::int64_t... Is>
constexpr inline auto get_enum_arr(
const std::integer_sequence<std::int64_t, Is...> &) {
constexpr std::size_t N = sizeof...(Is);
std::array<std::string_view, N> enum_names = {};
std::array<E, N> enum_values = {};
std::size_t num = 0;
(([&]() {
constexpr auto res = try_get_enum_name<E, static_cast<E>(Is)>();
if constexpr (res.first) {
// the Is is a valid enum value
enum_names[num] = res.second;
enum_values[num] = static_cast<E>(Is);
++num;
}
})(),
...);
return std::make_tuple(num, enum_values, enum_names);
}
template <std::size_t N, const std::array<int, N> &arr, size_t... Is>
constexpr auto array_to_seq(const std::index_sequence<Is...> &) {
return std::integer_sequence<std::int64_t, arr[Is]...>();
}
// convert array to map
template <typename E, size_t N, size_t... Is>
constexpr inline auto get_enum_to_str_map(
const std::array<std::string_view, N> &enum_names,
const std::array<E, N> &enum_values, const std::index_sequence<Is...> &) {
return frozen::unordered_map<E, frozen::string, sizeof...(Is)>{
{enum_values[Is], enum_names[Is]}...};
}
template <typename E, size_t N, size_t... Is>
constexpr inline auto get_str_to_enum_map(
const std::array<std::string_view, N> &enum_names,
const std::array<E, N> &enum_values, const std::index_sequence<Is...> &) {
return frozen::unordered_map<frozen::string, E, sizeof...(Is)>{
{enum_names[Is], enum_values[Is]}...};
}
#endif
// the default generic enum_value
// if the user has not defined a specialization template, this will be called
template <typename T>
struct enum_value {
constexpr static std::array<int, 0> value = {};
};
template <bool str_to_enum, typename E>
constexpr inline auto get_enum_map() {
#if defined(__clang__) || defined(_MSC_VER) || \
(defined(__GNUC__) && __GNUC__ > 8)
constexpr auto &arr = enum_value<E>::value;
constexpr auto arr_size = arr.size();
if constexpr (arr_size > 0) {
// the user has defined a specialization template
constexpr auto arr_seq =
array_to_seq<arr_size, arr>(std::make_index_sequence<arr_size>());
constexpr auto t = get_enum_arr<E>(arr_seq);
if constexpr (str_to_enum) {
return get_str_to_enum_map<E, arr_size>(
std::get<2>(t), std::get<1>(t),
std::make_index_sequence<std::get<0>(t)>{});
}
else {
return get_enum_to_str_map<E, arr_size>(
std::get<2>(t), std::get<1>(t),
std::make_index_sequence<std::get<0>(t)>{});
}
}
else {
return false;
}
#else
return false;
#endif
}
#if defined(__clang__) && (__clang_major__ >= 17)
#pragma clang diagnostic pop
#endif
} // namespace iguana
+10
View File
@@ -0,0 +1,10 @@
#pragma once
#include "iguana/dynamic.hpp"
#include "iguana/json_reader.hpp"
#include "iguana/json_writer.hpp"
#include "iguana/pb_reader.hpp"
#include "iguana/pb_writer.hpp"
#include "iguana/xml_reader.hpp"
#include "iguana/xml_writer.hpp"
#include "iguana/yaml_reader.hpp"
#include "iguana/yaml_writer.hpp"
+989
View File
@@ -0,0 +1,989 @@
#pragma once
#include "detail/utf.hpp"
#include "error_code.h"
#include "json_util.hpp"
#ifdef YLT_USE_CXX26_REFLECTION
#include "ylt/reflection/reflect26_dispatch.hpp"
#endif
namespace iguana {
template <typename T, typename It,
std::enable_if_t<ylt_refletable_v<T>, int> = 0>
IGUANA_INLINE void from_json(T &value, It &&it, It &&end);
template <typename T, typename View,
std::enable_if_t<ylt_refletable_v<T>, int> = 0>
IGUANA_INLINE void from_json(T &value, const View &view);
template <typename T, typename View,
std::enable_if_t<non_ylt_refletable_v<T>, int> = 0>
IGUANA_INLINE void from_json(T &value, const View &view);
namespace detail {
template <typename U, typename It, std::enable_if_t<optional_v<U>, int> = 0>
IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end);
template <typename U, typename It, std::enable_if_t<tuple_v<U>, int> = 0>
IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end);
template <typename U, typename It,
std::enable_if_t<sequence_container_v<U>, int> = 0>
IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end);
template <typename U, typename It, std::enable_if_t<smart_ptr_v<U>, int> = 0>
IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end);
template <typename U, typename It, std::enable_if_t<variant_v<U>, int> = 0>
IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end);
template <typename U, typename It,
std::enable_if_t<ylt_refletable_v<U>, int> = 0>
IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end) {
from_json(value, it, end);
}
template <typename U, typename It, std::enable_if_t<string_v<U>, int> = 0>
IGUANA_INLINE void parse_escape(U &value, It &&it, It &&end) {
if (it == end)
IGUANA_UNLIKELY { throw std::runtime_error(R"(Expected ")"); }
if (*it == 'u') {
++it;
if (std::distance(it, end) <= 4)
IGUANA_UNLIKELY {
throw std::runtime_error(R"(Expected 4 hexadecimal digits)");
}
auto code_point = parse_unicode_hex4(it);
encode_utf8(value, code_point);
}
else if (*it == 'n') {
++it;
value.push_back('\n');
}
else if (*it == 't') {
++it;
value.push_back('\t');
}
else if (*it == 'r') {
++it;
value.push_back('\r');
}
else if (*it == 'b') {
++it;
value.push_back('\b');
}
else if (*it == 'f') {
++it;
value.push_back('\f');
}
else {
value.push_back(*it); // add the escaped character
++it;
}
}
template <typename U, typename It, std::enable_if_t<num_v<U>, int> = 0>
IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end) {
skip_ws(it, end);
if (it != end && *it == 'n') {
++it;
match<'u', 'l', 'l'>(it, end);
return;
}
if constexpr (contiguous_iterator<std::decay_t<It>>) {
const auto size = std::distance(it, end);
if (size == 0)
IGUANA_UNLIKELY { throw std::runtime_error("Failed to parse number"); }
const auto start = &*it;
auto [p, ec] = detail::from_chars<false>(start, start + size, value);
if (ec != std::errc{} || !can_follow_number(*p))
IGUANA_UNLIKELY { throw std::runtime_error("Failed to parse number"); }
it += (p - &*it);
}
else {
char buffer[256];
size_t i{};
while (it != end && is_numeric(*it)) {
if (i > 254)
IGUANA_UNLIKELY { throw std::runtime_error("Number is too long"); }
buffer[i] = *it++;
++i;
}
detail::from_chars(buffer, buffer + i, value);
}
}
// template <typename U, typename It, std::enable_if_t<is_pb_type_v<U>, int> =
// 0> IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end) {
// from_json_impl(value.val, it, end);
// }
template <typename U, typename It, std::enable_if_t<numeric_str_v<U>, int> = 0>
IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end) {
skip_ws(it, end);
auto start = it;
while (it != end && is_numeric(*it)) {
++it;
}
value.value() =
std::string_view(&*start, static_cast<size_t>(std::distance(start, it)));
}
template <bool skip = false, typename U, typename It,
std::enable_if_t<char_v<U>, int> = 0>
IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end) {
if constexpr (!skip) {
skip_ws(it, end);
match<'"'>(it, end);
}
if (it == end)
IGUANA_UNLIKELY { throw std::runtime_error("Unxpected end of buffer"); }
if (*it == '\\')
IGUANA_UNLIKELY {
if (++it == end)
IGUANA_UNLIKELY { throw std::runtime_error("Unxpected end of buffer"); }
else if (*it == 'n') {
value = '\n';
}
else if (*it == 't') {
value = '\t';
}
else if (*it == 'r') {
value = '\r';
}
else if (*it == 'b') {
value = '\b';
}
else if (*it == 'f') {
value = '\f';
}
else
IGUANA_UNLIKELY { value = *it; }
}
else {
value = *it;
}
++it;
if constexpr (!skip) {
match<'"'>(it, end);
}
}
template <typename U, typename It, std::enable_if_t<bool_v<U>, int> = 0>
IGUANA_INLINE void from_json_impl(U &&value, It &&it, It &&end) {
skip_ws(it, end);
if (it < end)
IGUANA_LIKELY {
switch (*it) {
case 't':
++it;
match<'r', 'u', 'e'>(it, end);
value = true;
break;
case 'f':
++it;
match<'a', 'l', 's', 'e'>(it, end);
value = false;
break;
case 'n':
++it;
match<'u', 'l', 'l'>(it, end);
break;
IGUANA_UNLIKELY default
: throw std::runtime_error("Expected true or false");
}
}
else
IGUANA_UNLIKELY { throw std::runtime_error("Expected true or false"); }
}
template <bool skip = false, typename U, typename It,
std::enable_if_t<string_v<U>, int> = 0>
IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end) {
if constexpr (!skip) {
skip_ws(it, end);
// handle JSON null: leave value empty and skip "null"
if (it != end && *it == 'n') {
++it;
match<'u', 'l', 'l'>(it, end);
value.clear();
return;
}
match<'"'>(it, end);
}
value.clear();
if constexpr (contiguous_iterator<std::decay_t<It>>) {
auto start = it;
while (it < end) {
skip_till_escape_or_qoute(it, end);
if (*it == '"') {
value.append(&*start, static_cast<size_t>(std::distance(start, it)));
++it;
return;
}
else {
// Must be an escape
value.append(&*start, static_cast<size_t>(std::distance(start, it)));
++it; // skip first escape
parse_escape(value, it, end);
start = it;
}
}
}
else {
while (it != end) {
switch (*it) {
IGUANA_UNLIKELY case '\\':
++it;
parse_escape(value, it, end);
break;
// IGUANA_UNLIKELY case ']' : return;
IGUANA_UNLIKELY case '"':
++it;
return;
IGUANA_LIKELY default : value.push_back(*it);
++it;
}
}
}
}
template <bool skip = false, typename U, typename It,
std::enable_if_t<string_view_v<U>, int> = 0>
IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end) {
static_assert(contiguous_iterator<std::decay_t<It>>, "must be contiguous");
if constexpr (!skip) {
skip_ws(it, end);
match<'"'>(it, end);
}
using T = std::decay_t<U>;
auto start = it;
while (it != end) {
skip_till_qoute(it, end);
if (*(it - 1) != '\\') {
value = T(&*start, static_cast<size_t>(std::distance(start, it)));
++it;
return;
}
++it;
}
throw std::runtime_error("Expected \"");
}
template <typename U, typename It, std::enable_if_t<enum_v<U>, int> = 0>
IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end) {
static constexpr auto str_to_enum = get_enum_map<true, std::decay_t<U>>();
if constexpr (bool_v<decltype(str_to_enum)>) {
// not defined a specialization template
using T = std::underlying_type_t<std::decay_t<U>>;
from_json_impl(reinterpret_cast<T &>(value), it, end);
}
else {
std::string_view enum_names;
from_json_impl(enum_names, it, end);
auto it = str_to_enum.find(enum_names);
if (it != str_to_enum.end())
IGUANA_LIKELY { value = it->second; }
else {
throw std::runtime_error(std::string(enum_names) +
" missing corresponding value in enum_value");
}
}
}
template <typename T>
constexpr size_t get_array_size(const T &t) {
#if __cplusplus > 201703L
#if __has_include(<span>)
if constexpr (is_span<T>::value)
return t.size();
else
#endif
#endif
return sizeof(T) / sizeof(decltype(std::declval<T>()[0]));
}
template <typename U, typename It, std::enable_if_t<fixed_array_v<U>, int> = 0>
IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end) {
using T = std::remove_reference_t<U>;
size_t n = get_array_size(value);
skip_ws(it, end);
if constexpr (std::is_same_v<char, std::remove_reference_t<
decltype(std::declval<T>()[0])>>) {
if (*it == '"') {
match<'"'>(it, end);
auto value_it = std::begin(value);
for (size_t i = 0; i < n; ++i) {
if (*it != '"')
IGUANA_LIKELY { from_json_impl<true>(*value_it++, it, end); }
}
match<'"'>(it, end);
return;
}
}
match<'['>(it, end);
skip_ws(it, end);
if (it == end)
IGUANA_UNLIKELY { throw std::runtime_error("Unexpected end"); }
if (*it == ']')
IGUANA_UNLIKELY {
++it;
return;
}
auto value_it = std::begin(value);
for (size_t i = 0; i < n; ++i) {
from_json_impl(*value_it++, it, end);
skip_ws(it, end);
if (it == end) {
throw std::runtime_error("Unexpected end");
}
if (*it == ',')
IGUANA_LIKELY {
++it;
skip_ws(it, end);
}
else if (*it == ']') {
++it;
return;
}
else
IGUANA_UNLIKELY { throw std::runtime_error("Expected ]"); }
}
}
template <typename U, typename It,
std::enable_if_t<sequence_container_v<U>, int>>
IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end) {
value.clear();
skip_ws(it, end);
match<'['>(it, end);
skip_ws(it, end);
for (size_t i = 0; it != end; ++i) {
if (*it == ']')
IGUANA_UNLIKELY {
++it;
return;
}
if (i > 0)
IGUANA_LIKELY { match<','>(it, end); }
from_json_impl(value.emplace_back(), it, end);
skip_ws(it, end);
}
throw std::runtime_error("Expected ]");
}
template <typename It>
IGUANA_INLINE auto get_key(It &&it, It &&end) {
if constexpr (contiguous_iterator<std::decay_t<It>>) {
// skip white space and escape characters and find the string
skip_ws(it, end);
match<'"'>(it, end);
auto start = it;
skip_till_escape_or_qoute(it, end);
if (*it == '\\')
IGUANA_UNLIKELY {
// we dont' optimize this currently because it would increase binary
// size significantly with the complexity of generating escaped
// compile time versions of keys
it = start;
static thread_local std::string static_key{};
detail::from_json_impl<true>(static_key, it, end);
return std::string_view(static_key);
}
else
IGUANA_LIKELY {
auto key = std::string_view{
&*start, static_cast<size_t>(std::distance(start, it))};
++it;
if (key[0] == '@')
IGUANA_UNLIKELY { return key.substr(1); }
return key;
}
}
else {
static thread_local std::string static_key{};
detail::from_json_impl(static_key, it, end);
return std::string_view(static_key);
}
}
template <typename U, typename It,
std::enable_if_t<map_container_v<U>, int> = 0>
IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end) {
using T = std::remove_reference_t<U>;
using key_type = typename T::key_type;
skip_ws(it, end);
match<'{'>(it, end);
skip_ws(it, end);
bool first = true;
while (it != end) {
if (*it == '}')
IGUANA_UNLIKELY {
++it;
return;
}
else if (first)
IGUANA_UNLIKELY { first = false; }
else
IGUANA_LIKELY { match<','>(it, end); }
std::string_view key = get_key(it, end);
skip_ws(it, end);
match<':'>(it, end);
if constexpr (string_v<key_type> || string_view_v<key_type>) {
from_json_impl(value[key_type(key)], it, end);
}
else {
static thread_local key_type key_value{};
from_json_impl(key_value, key.begin(), key.end());
from_json_impl(value[key_value], it, end);
}
skip_ws(it, end);
}
}
template <typename U, typename It, std::enable_if_t<tuple_v<U>, int>>
IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end) {
skip_ws(it, end);
match<'['>(it, end);
skip_ws(it, end);
foreach_tuple(
[&](auto &v, auto i) IGUANA__INLINE_LAMBDA {
constexpr auto I = decltype(i)::value;
if (it == end || *it == ']') {
return;
}
if constexpr (I != 0) {
match<','>(it, end);
skip_ws(it, end);
}
from_json_impl(v, it, end);
skip_ws(it, end);
},
value);
match<']'>(it, end);
}
template <typename U, typename It, std::enable_if_t<optional_v<U>, int>>
IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end) {
skip_ws(it, end);
using T = std::remove_reference_t<U>;
if (it == end)
IGUANA_UNLIKELY { throw std::runtime_error("Unexexpected eof"); }
if (*it == 'n') {
++it;
match<'u', 'l', 'l'>(it, end);
if constexpr (!std::is_pointer_v<T>) {
value.reset();
if (it < end && *it == '"') {
++it;
}
}
}
else {
using value_type = typename T::value_type;
value_type t{};
if constexpr (string_v<value_type> || string_view_v<value_type>) {
if (it < end && *it == '"')
IGUANA_LIKELY { ++it; }
from_json_impl<true>(t, it, end);
}
else {
from_json_impl(t, it, end);
}
value = std::move(t);
}
}
template <typename U, typename It, std::enable_if_t<smart_ptr_v<U>, int>>
IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end) {
skip_ws(it, end);
if (it == end)
IGUANA_UNLIKELY { throw std::runtime_error("Unexexpected eof"); }
if (*it == 'n') {
++it;
match<'u', 'l', 'l'>(it, end);
}
else {
using value_type = typename std::remove_reference_t<U>::element_type;
if constexpr (unique_ptr_v<U>) {
value = std::make_unique<value_type>();
}
else {
value = std::make_shared<value_type>();
}
from_json_impl(*value, it, end);
}
}
template <typename It>
IGUANA_INLINE void skip_object_value(It &&it, It &&end) {
skip_ws(it, end);
while (it != end) {
switch (*it) {
case '{':
skip_until_closed<'{', '}'>(it, end);
break;
case '[':
skip_until_closed<'[', ']'>(it, end);
break;
case '"':
skip_string(it, end);
break;
case '/':
skip_comment(it, end);
continue;
case ',':
case '}':
case ']':
break;
default: {
++it;
continue;
}
}
break;
}
}
template <typename value_type, typename U, typename It>
IGUANA_INLINE bool from_json_variant_impl(U &value, It it, It end, It &temp_it,
It &temp_end) {
try {
value_type val{};
from_json_impl(val, it, end);
value = val;
temp_it = it;
temp_end = end;
return true;
} catch (std::exception &ex) {
return false;
}
}
template <typename U, typename It, size_t... Idx>
IGUANA_INLINE void from_json_variant(U &value, It &it, It &end,
std::index_sequence<Idx...>) {
static_assert(!has_duplicate_type_v<std::remove_reference_t<U>>,
"don't allow same type in std::variant");
bool r = false;
It temp_it = it;
It temp_end = end;
((void)(!r && (r = from_json_variant_impl<
variant_element_t<Idx, std::remove_reference_t<U>>>(
value, it, end, temp_it, temp_end),
true)),
...);
it = temp_it;
end = temp_end;
}
template <typename U, typename It, std::enable_if_t<variant_v<U>, int>>
IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end) {
from_json_variant(value, it, end,
std::make_index_sequence<
std::variant_size_v<std::remove_reference_t<U>>>{});
}
} // namespace detail
#ifdef YLT_USE_CXX26_REFLECTION
template <typename T, typename It, std::enable_if_t<ylt_refletable_v<T>, int>>
IGUANA_INLINE void from_json(T &value, It &&it, It &&end) {
skip_ws(it, end);
match<'{'>(it, end);
skip_ws(it, end);
if (*it == '}')
IGUANA_UNLIKELY {
++it;
return;
}
while (it != end) {
std::string_view key = detail::get_key(it, end);
skip_ws(it, end);
match<':'>(it, end);
bool found = ylt::reflection::reflect26::dispatch_by_name(
value, key, [&](auto &field) IGUANA__INLINE_LAMBDA {
using namespace detail;
from_json_impl(field, it, end);
});
if (!found)
IGUANA_UNLIKELY {
#ifdef THROW_UNKNOWN_KEY
throw std::runtime_error("Unknown key: " + std::string(key));
#else
detail::skip_object_value(it, end);
#endif
}
skip_ws(it, end);
if (*it == '}')
IGUANA_UNLIKELY {
++it;
return;
}
else
IGUANA_LIKELY { match<','>(it, end); }
}
}
#endif
#ifndef YLT_USE_CXX26_REFLECTION
template <typename T, typename It, std::enable_if_t<ylt_refletable_v<T>, int>>
IGUANA_INLINE void from_json(T &value, It &&it, It &&end) {
skip_ws(it, end);
match<'{'>(it, end);
skip_ws(it, end);
if (*it == '}')
IGUANA_UNLIKELY {
++it;
return;
}
using U = ylt::reflection::remove_cvref_t<T>;
constexpr auto Count = ylt::reflection::members_count_v<U>;
if constexpr (Count > 0) {
std::string_view key = detail::get_key(it, end);
#ifdef SEQUENTIAL_PARSE
bool parse_done = false;
ylt::reflection::for_each(
value, [&](auto &field, auto name, auto index) IGUANA__INLINE_LAMBDA {
if (parse_done || (key != name))
IGUANA_UNLIKELY { return; }
skip_ws(it, end);
match<':'>(it, end);
{
using namespace detail;
from_json_impl(field, it, end);
}
skip_ws(it, end);
if (*it == '}')
IGUANA_UNLIKELY {
++it;
parse_done = true;
return;
}
else
IGUANA_LIKELY { match<','>(it, end); }
key = detail::get_key(it, end);
});
if (parse_done) [[unlikely]] {
return;
}
#endif
while (it != end) {
static auto frozen_map = ylt::reflection::get_variant_map<U>();
if (frozen_map.size() > 0) {
const auto &member_it = frozen_map.find(key);
skip_ws(it, end);
match<':'>(it, end);
if (member_it != frozen_map.end())
IGUANA_LIKELY {
std::visit(
[&](auto offset) IGUANA__INLINE_LAMBDA {
using namespace detail;
using value_type = typename decltype(offset)::type;
auto member_ptr =
(value_type *)((char *)(&value) + offset.value);
from_json_impl(*member_ptr, it, end);
},
member_it->second);
}
else
IGUANA_UNLIKELY {
#ifdef THROW_UNKNOWN_KEY
throw std::runtime_error("Unknown key: " + std::string(key));
#else
detail::skip_object_value(it, end);
#endif
}
}
skip_ws(it, end);
if (*it == '}')
IGUANA_UNLIKELY {
++it;
return;
}
else
IGUANA_LIKELY { match<','>(it, end); }
key = detail::get_key(it, end);
}
}
}
#endif // !YLT_USE_CXX26_REFLECTION
template <typename T, typename It,
std::enable_if_t<non_ylt_refletable_v<T>, int> = 0>
IGUANA_INLINE void from_json(T &value, It &&it, It &&end) {
using namespace detail;
from_json_impl(value, it, end);
}
template <typename T, typename It>
IGUANA_INLINE void from_json(T &value, It &&it, It &&end,
std::error_code &ec) noexcept {
try {
from_json(value, it, end);
ec = {};
} catch (std::runtime_error &e) {
ec = iguana::make_error_code(e.what());
}
}
template <typename T, typename View, std::enable_if_t<ylt_refletable_v<T>, int>>
IGUANA_INLINE void from_json(T &value, const View &view) {
from_json(value, std::begin(view), std::end(view));
}
template <typename T, typename View,
std::enable_if_t<non_ylt_refletable_v<T>, int>>
IGUANA_INLINE void from_json(T &value, const View &view) {
from_json(value, std::begin(view), std::end(view));
}
template <typename T, typename View,
std::enable_if_t<json_view_v<View>, int> = 0>
IGUANA_INLINE void from_json(T &value, const View &view,
std::error_code &ec) noexcept {
try {
from_json(value, view);
ec = {};
} catch (std::runtime_error &e) {
ec = iguana::make_error_code(e.what());
}
}
template <typename T, typename Byte,
std::enable_if_t<json_byte_v<Byte>, int> = 0>
IGUANA_INLINE void from_json(T &value, const Byte *data, size_t size) {
std::string_view buffer(data, size);
from_json(value, buffer);
}
template <typename T, typename Byte,
std::enable_if_t<json_byte_v<Byte>, int> = 0>
IGUANA_INLINE void from_json(T &value, const Byte *data, size_t size,
std::error_code &ec) noexcept {
try {
from_json(value, data, size);
ec = {};
} catch (std::runtime_error &e) {
ec = iguana::make_error_code(e.what());
}
}
template <bool Is_view = false, typename It>
void parse(jvalue &result, It &&it, It &&end, bool parse_as_double = false);
template <bool Is_view = false, typename It>
inline void parse(jarray &result, It &&it, It &&end,
bool parse_as_double = false) {
skip_ws(it, end);
match<'['>(it, end);
if (*it == ']')
IGUANA_UNLIKELY {
++it;
return;
}
while (true) {
if (it == end) {
break;
}
result.emplace_back();
parse<Is_view>(result.back(), it, end, parse_as_double);
if (*it == ']')
IGUANA_UNLIKELY {
++it;
return;
}
match<','>(it, end);
}
throw std::runtime_error("Expected ]");
}
template <bool Is_view = false, typename It>
inline void parse(jobject &result, It &&it, It &&end,
bool parse_as_double = false) {
skip_ws(it, end);
match<'{'>(it, end);
if (*it == '}')
IGUANA_UNLIKELY {
++it;
return;
}
skip_ws(it, end);
while (true) {
if (it == end)
IGUANA_UNLIKELY { throw std::runtime_error("Expected }"); }
std::string key;
using namespace detail;
from_json_impl(key, it, end);
auto emplaced = result.try_emplace(key);
if (!emplaced.second)
throw std::runtime_error("duplicated key " + key);
match<':'>(it, end);
parse<Is_view>(emplaced.first->second, it, end, parse_as_double);
if (*it == '}')
IGUANA_UNLIKELY {
++it;
return;
}
match<','>(it, end);
}
}
template <bool Is_view, typename It>
inline void parse(jvalue &result, It &&it, It &&end, bool parse_as_double) {
using namespace detail;
skip_ws(it, end);
switch (*it) {
case 'n':
++it;
match<'u', 'l', 'l'>(it, end);
result.template emplace<std::nullptr_t>();
break;
case 'f':
case 't':
from_json_impl(result.template emplace<bool>(), it, end);
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '-': {
double d{};
from_json_impl(d, it, end);
if (!parse_as_double && (static_cast<int>(d) == d))
result.emplace<int>(d);
else
result.emplace<double>(d);
break;
}
case '"':
if constexpr (Is_view) {
result.template emplace<std::string_view>();
from_json_impl(std::get<std::string_view>(result), it, end);
}
else {
result.template emplace<std::string>();
from_json_impl(std::get<std::string>(result), it, end);
}
break;
case '[':
result.template emplace<jarray>();
parse<Is_view>(std::get<jarray>(result), it, end, parse_as_double);
break;
case '{': {
result.template emplace<jobject>();
parse<Is_view>(std::get<jobject>(result), it, end, parse_as_double);
break;
}
default:
throw std::runtime_error("parse failed");
}
skip_ws(it, end);
}
// set Is_view == true, parse str as std::string_view
// set parse_as_double == true, parse the number as double in any case
template <bool Is_view = false, typename It>
inline void parse(jvalue &result, It &&it, It &&end, std::error_code &ec,
bool parse_as_double = false) {
try {
parse<Is_view>(result, it, end, parse_as_double);
ec = {};
} catch (const std::runtime_error &e) {
result.template emplace<std::nullptr_t>();
ec = iguana::make_error_code(e.what());
}
}
template <bool Is_view = false, typename T, typename View,
std::enable_if_t<json_view_v<View>, int> = 0>
inline void parse(T &result, const View &view, bool parse_as_double = false) {
parse<Is_view>(result, std::begin(view), std::end(view), parse_as_double);
}
template <bool Is_view = false, typename T, typename View,
std::enable_if_t<json_view_v<View>, int> = 0>
inline void parse(T &result, const View &view, std::error_code &ec,
bool parse_as_double = false) noexcept {
try {
parse<Is_view>(result, view, parse_as_double);
ec = {};
} catch (std::runtime_error &e) {
ec = iguana::make_error_code(e.what());
}
}
IGUANA_INLINE std::string json_file_content(const std::string &filename) {
std::error_code ec;
uint64_t size = std::filesystem::file_size(filename, ec);
if (ec) {
throw std::runtime_error("file size error " + ec.message());
}
if (size == 0) {
throw std::runtime_error("empty file");
}
std::string content;
content.resize(size);
std::ifstream file(filename, std::ios::binary);
file.read(content.data(), content.size());
return content;
}
template <typename T>
IGUANA_INLINE void from_json_file(T &value, const std::string &filename) {
std::string content = json_file_content(filename);
from_json(value, content.begin(), content.end());
}
template <typename T>
IGUANA_INLINE void from_json_file(T &value, const std::string &filename,
std::error_code &ec) noexcept {
try {
from_json_file(value, filename);
ec = {};
} catch (std::runtime_error &e) {
ec = iguana::make_error_code(e.what());
}
}
template <typename T>
IGUANA_INLINE void from_json_adl(iguana_adl_t *p, T &t,
std::string_view pb_str) {
iguana::from_json(t, pb_str);
}
} // namespace iguana
+243
View File
@@ -0,0 +1,243 @@
// Glaze Library
// For the license information refer to glaze.hpp
#pragma once
#include "common.hpp"
#include "detail/string_stream.hpp"
#include "util.hpp"
#include "value.hpp"
namespace iguana {
class numeric_str {
public:
std::string_view &value() { return val_; }
std::string_view value() const { return val_; }
template <typename T>
T convert() {
static_assert(num_v<T>, "T must be numeric type");
if (val_.empty())
IGUANA_UNLIKELY { throw std::runtime_error("Failed to parse number"); }
T res;
detail::from_chars(val_.data(), val_.data() + val_.size(), res);
return res;
}
private:
std::string_view val_;
};
template <typename T>
constexpr inline bool numeric_str_v =
std::is_same_v<numeric_str, std::remove_cvref_t<T>>;
template <>
constexpr inline bool reflect26_excluded_v<numeric_str> = true;
template <typename It>
IGUANA_INLINE void skip_comment(It &&it, It &&end) {
++it;
if (it == end)
IGUANA_UNLIKELY {
throw std::runtime_error("Unexpected end, expected comment");
}
else if (*it == '/') {
while (++it != end && *it != '\n')
;
}
else if (*it == '*') {
while (++it != end) {
if (*it == '*')
IGUANA_UNLIKELY {
if (++it == end)
IGUANA_UNLIKELY { break; }
else if (*it == '/')
IGUANA_LIKELY {
++it;
break;
}
}
}
}
else
IGUANA_UNLIKELY throw std::runtime_error("Expected / or * after /");
}
template <typename It>
IGUANA_INLINE void skip_ws(It &&it, It &&end) {
while (it != end) {
// assuming ascii
if (static_cast<uint8_t>(*it) < 33) {
++it;
}
else if (*it == '/') {
skip_comment(it, end);
}
else {
break;
}
}
}
template <typename It>
IGUANA_INLINE void skip_ws_no_comments(It &&it, It &&end) {
while (it != end) {
// assuming ascii
if (static_cast<uint8_t>(*it) < 33)
IGUANA_LIKELY { ++it; }
else {
break;
}
}
}
inline constexpr auto has_escape = [](uint64_t chunk) IGUANA__INLINE_LAMBDA {
return has_zero(
chunk ^
0b0101110001011100010111000101110001011100010111000101110001011100);
};
template <typename It>
IGUANA_INLINE void skip_till_escape_or_qoute(It &&it, It &&end) {
static_assert(contiguous_iterator<std::decay_t<decltype(it)>>);
if (std::distance(it, end) >= 7)
IGUANA_LIKELY {
const auto end_m7 = end - 7;
for (; it < end_m7; it += 8) {
const auto chunk = *reinterpret_cast<const uint64_t *>(&*it);
uint64_t test = has_qoute(chunk) | has_escape(chunk);
if (test != 0) {
it += (countr_zero(test) >> 3);
return;
}
}
}
// Tail end of buffer. Should be rare we even get here
while (it < end) {
switch (*it) {
case '\\':
case '"':
return;
}
++it;
}
throw std::runtime_error("Expected \"");
}
template <typename It>
IGUANA_INLINE void skip_till_qoute(It &&it, It &&end) {
static_assert(contiguous_iterator<std::decay_t<decltype(it)>>);
if (std::distance(it, end) >= 7)
IGUANA_LIKELY {
const auto end_m7 = end - 7;
for (; it < end_m7; it += 8) {
const auto chunk = *reinterpret_cast<const uint64_t *>(&*it);
uint64_t test = has_qoute(chunk);
if (test != 0) {
it += (countr_zero(test) >> 3);
return;
}
}
}
// Tail end of buffer. Should be rare we even get here
while (it < end) {
if (*it == '"')
return;
++it;
}
throw std::runtime_error("Expected \"");
}
template <typename It>
IGUANA_INLINE void skip_string(It &&it, It &&end) noexcept {
++it;
while (it < end) {
if (*it == '"') {
++it;
break;
}
else if (*it == '\\' && ++it == end)
IGUANA_UNLIKELY
break;
++it;
}
}
template <char open, char close, typename It>
IGUANA_INLINE void skip_until_closed(It &&it, It &&end) {
++it;
size_t open_count = 1;
size_t close_count = 0;
while (it < end && open_count > close_count) {
switch (*it) {
case '/':
skip_comment(it, end);
break;
case '"':
skip_string(it, end);
break;
case open:
++open_count;
++it;
break;
case close:
++close_count;
++it;
break;
default:
++it;
}
}
}
IGUANA_INLINE bool is_numeric(char c) noexcept {
static constexpr int is_num[256] = {
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, // 2
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 3
0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 5
0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 7
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // D
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // E
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // F
};
return static_cast<bool>(is_num[static_cast<unsigned char>(c)]);
}
// '\t' '\r' '\n' '"' '}' ']' ',' ' ' '\0'
IGUANA_INLINE bool can_follow_number(char c) noexcept {
static constexpr int can_follow_num[256] = {
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, // 0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1
1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, // 2
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, // 5
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, // 7
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // D
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // E
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // F
};
return static_cast<bool>(can_follow_num[static_cast<unsigned char>(c)]);
}
} // namespace iguana
+320
View File
@@ -0,0 +1,320 @@
//
// Created by Qiyu on 17-6-5.
//
#ifndef SERIALIZE_JSON_HPP
#define SERIALIZE_JSON_HPP
#include "json_util.hpp"
namespace iguana {
template <bool Is_writing_escape = true, typename Stream, typename T,
std::enable_if_t<ylt_refletable_v<T>, int> = 0>
IGUANA_INLINE void to_json(T &&t, Stream &s);
namespace detail {
template <bool Is_writing_escape = true, typename Stream, typename T,
std::enable_if_t<optional_v<T>, int> = 0>
IGUANA_INLINE void to_json_impl(Stream &ss, const T &val);
template <bool Is_writing_escape = true, typename Stream, typename T,
std::enable_if_t<fixed_array_v<T>, int> = 0>
IGUANA_INLINE void to_json_impl(Stream &ss, const T &t);
template <bool Is_writing_escape = true, typename Stream, typename T,
std::enable_if_t<sequence_container_v<T>, int> = 0>
IGUANA_INLINE void to_json_impl(Stream &ss, const T &v);
template <bool Is_writing_escape = true, typename Stream, typename T,
std::enable_if_t<pair_v<T>, int> = 0>
IGUANA_INLINE void to_json_impl(Stream &ss, const T &v);
template <bool Is_writing_escape = true, typename Stream, typename T,
std::enable_if_t<smart_ptr_v<T>, int> = 0>
IGUANA_INLINE void to_json_impl(Stream &ss, const T &v);
template <bool Is_writing_escape = true, typename Stream, typename T,
std::enable_if_t<map_container_v<T>, int> = 0>
IGUANA_INLINE void to_json_impl(Stream &ss, const T &o);
template <bool Is_writing_escape = true, typename Stream, typename T,
std::enable_if_t<tuple_v<T>, int> = 0>
IGUANA_INLINE void to_json_impl(Stream &s, T &&t);
template <bool Is_writing_escape = true, typename Stream, typename T,
std::enable_if_t<variant_v<T>, int> = 0>
IGUANA_INLINE void to_json_impl(Stream &s, T &&t);
template <typename Stream, typename InputIt, typename T, typename F>
IGUANA_INLINE void join(Stream &ss, InputIt first, InputIt last, const T &delim,
const F &f) {
if (first == last)
return;
f(*first++);
while (first != last) {
ss.push_back(delim);
f(*first++);
}
}
template <bool Is_writing_escape, typename Stream>
IGUANA_INLINE void to_json_impl(Stream &ss, std::nullptr_t) {
ss.append("null");
}
template <bool Is_writing_escape, typename Stream>
IGUANA_INLINE void to_json_impl(Stream &ss, bool b) {
ss.append(b ? "true" : "false");
};
template <bool Is_writing_escape, typename Stream>
IGUANA_INLINE void to_json_impl(Stream &ss, char value) {
ss.append("\"");
ss.push_back(value);
ss.append("\"");
}
template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<num_v<T>, int> = 0>
IGUANA_INLINE void to_json_impl(Stream &ss, T value) {
char temp[65];
auto p = detail::to_chars(temp, value);
ss.append(temp, p - temp);
}
// template <bool Is_writing_escape, typename Stream, typename T,
// std::enable_if_t<is_pb_type_v<T>, int> = 0>
// IGUANA_INLINE void to_json_impl(Stream &ss, T value) {
// to_json_impl<Is_writing_escape>(ss, value.val);
// }
template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<numeric_str_v<T>, int> = 0>
IGUANA_INLINE void to_json_impl(Stream &ss, T v) {
ss.append(v.value().data(), v.value().size());
}
template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<string_container_v<T>, int> = 0>
IGUANA_INLINE void to_json_impl(Stream &ss, T &&t) {
ss.push_back('"');
if constexpr (Is_writing_escape) {
write_string_with_escape(t.data(), t.size(), ss);
}
else {
ss.append(t.data(), t.size());
}
ss.push_back('"');
}
template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<num_v<T>, int> = 0>
IGUANA_INLINE void render_key(Stream &ss, const T &t) {
ss.push_back('"');
to_json_impl<Is_writing_escape>(ss, t);
ss.push_back('"');
}
// template <bool Is_writing_escape, typename Stream, typename T,
// std::enable_if_t<is_pb_type_v<T>, int> = 0>
// IGUANA_INLINE void render_key(Stream &ss, const T &t) {
// render_key<Is_writing_escape>(ss, t.val);
// }
template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<string_container_v<T>, int> = 0>
IGUANA_INLINE void render_key(Stream &ss, T &&t) {
to_json_impl<Is_writing_escape>(ss, std::forward<T>(t));
}
template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<ylt_refletable_v<T>, int> = 0>
IGUANA_INLINE void to_json_impl(Stream &ss, T &&t) {
to_json(std::forward<T>(t), ss);
}
template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<enum_v<T>, int> = 0>
IGUANA_INLINE void to_json_impl(Stream &ss, T val) {
static constexpr auto enum_to_str = get_enum_map<false, std::decay_t<T>>();
if constexpr (bool_v<decltype(enum_to_str)>) {
to_json_impl<Is_writing_escape>(
ss, static_cast<std::underlying_type_t<T>>(val));
}
else {
auto it = enum_to_str.find(val);
if (it != enum_to_str.end())
IGUANA_LIKELY {
auto str = it->second;
to_json_impl<Is_writing_escape>(
ss, std::string_view(str.data(), str.size()));
}
else {
throw std::runtime_error(
std::to_string(static_cast<std::underlying_type_t<T>>(val)) +
" is a missing value in enum_value");
}
}
}
template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<optional_v<T>, int>>
IGUANA_INLINE void to_json_impl(Stream &ss, const T &val) {
if (!val) {
ss.append("null");
}
else {
to_json_impl<Is_writing_escape>(ss, *val);
}
}
template <bool Is_writing_escape, typename Stream, typename T>
IGUANA_INLINE void render_array(Stream &ss, const T &v) {
ss.push_back('[');
join(ss, std::begin(v), std::end(v), ',',
[&ss](const auto &jsv) IGUANA__INLINE_LAMBDA {
to_json_impl<Is_writing_escape>(ss, jsv);
});
ss.push_back(']');
}
template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<fixed_array_v<T>, int>>
IGUANA_INLINE void to_json_impl(Stream &ss, const T &t) {
if constexpr (std::is_same_v<char, std::remove_reference_t<
decltype(std::declval<T>()[0])>>) {
constexpr size_t n = sizeof(T) / sizeof(decltype(std::declval<T>()[0]));
ss.push_back('"');
auto get_length = [&t](int n) constexpr {
for (int i = 0; i < n; ++i) {
if (t[i] == '\0')
return i;
}
return n;
};
size_t len = get_length(n);
ss.append(std::data(t), len);
ss.push_back('"');
}
else {
render_array<Is_writing_escape>(ss, t);
}
}
template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<map_container_v<T>, int>>
IGUANA_INLINE void to_json_impl(Stream &ss, const T &o) {
ss.push_back('{');
join(ss, o.cbegin(), o.cend(), ',',
[&ss](const auto &jsv) IGUANA__INLINE_LAMBDA {
render_key<Is_writing_escape>(ss, jsv.first);
ss.push_back(':');
to_json_impl<Is_writing_escape>(ss, jsv.second);
});
ss.push_back('}');
}
template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<sequence_container_v<T>, int>>
IGUANA_INLINE void to_json_impl(Stream &ss, const T &v) {
constexpr char pre = pair_v<typename T::value_type> ? '{' : '[';
ss.push_back(pre);
join(ss, v.cbegin(), v.cend(), ',',
[&ss](const auto &jsv) IGUANA__INLINE_LAMBDA {
to_json_impl<Is_writing_escape>(ss, jsv);
});
constexpr char post = pair_v<typename T::value_type> ? '}' : ']';
ss.push_back(post);
}
template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<pair_v<T>, int>>
IGUANA_INLINE void to_json_impl(Stream &ss, const T &v) {
render_key<Is_writing_escape>(ss, v.first);
ss.push_back(':');
to_json_impl<Is_writing_escape>(ss, v.second);
}
constexpr auto write_json_key = [](auto &s, auto name) IGUANA__INLINE_LAMBDA {
s.push_back('"');
// will be replaced by string_view later
s.append(name.data(), name.size());
s.push_back('"');
};
template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<smart_ptr_v<T>, int>>
IGUANA_INLINE void to_json_impl(Stream &ss, const T &v) {
if (v) {
to_json_impl<Is_writing_escape>(ss, *v);
}
else {
ss.append("null");
}
}
template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<tuple_v<T>, int>>
IGUANA_INLINE void to_json_impl(Stream &s, T &&t) {
using U = typename std::decay_t<T>;
s.push_back('[');
constexpr size_t size = std::tuple_size_v<U>;
foreach_tuple(
[&s, size](auto &v, auto i) IGUANA__INLINE_LAMBDA {
to_json_impl<Is_writing_escape>(s, v);
if (i != size - 1)
IGUANA_LIKELY { s.push_back(','); }
},
std::forward<T>(t));
s.push_back(']');
}
template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<variant_v<T>, int>>
IGUANA_INLINE void to_json_impl(Stream &s, T &&t) {
static_assert(!has_duplicate_type_v<std::remove_reference_t<T>>,
"don't allow same type in std::variant");
std::visit(
[&s](auto value) {
to_json_impl<Is_writing_escape>(s, value);
},
t);
}
} // namespace detail
template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<ylt_refletable_v<T>, int>>
IGUANA_INLINE void to_json(T &&t, Stream &s) {
using namespace detail;
s.push_back('{');
using U = ylt::reflection::remove_cvref_t<T>;
constexpr auto Count = ylt::reflection::members_count_v<U>;
if constexpr (Count > 0) {
ylt::reflection::for_each(t, [&](auto &field, auto name, auto index) {
static_assert(
!std::is_pointer_v<std::remove_reference_t<decltype(field)>>,
"don't use raw pointer, please use smart pointer");
write_json_key(s, name);
s.push_back(':');
to_json_impl<Is_writing_escape>(s, field);
if (index < Count - 1)
IGUANA_LIKELY { s.push_back(','); }
});
}
s.push_back('}');
}
template <bool Is_writing_escape = true, typename Stream, typename T,
std::enable_if_t<non_ylt_refletable_v<T>, int> = 0>
IGUANA_INLINE void to_json(T &&t, Stream &s) {
using namespace detail;
to_json_impl<Is_writing_escape>(s, t);
}
template <typename T>
IGUANA_INLINE void to_json_adl(iguana_adl_t *p, T &t, std::string &pb_str) {
to_json(t, pb_str);
}
} // namespace iguana
#endif // SERIALIZE_JSON_HPP
+555
View File
@@ -0,0 +1,555 @@
#pragma once
#include "detail/string_resize.hpp"
#include "pb_util.hpp"
namespace iguana {
template <typename T>
IGUANA_INLINE void from_pb(T& t, std::string_view pb_str);
namespace detail {
template <typename T>
IGUANA_INLINE void from_pb_impl(T& val, std::string_view& pb_str,
uint32_t field_no = 0);
inline void skip_unknown_field(std::string_view& pb_str, WireType wire_type,
uint32_t field_number = 0);
template <typename T>
IGUANA_INLINE void decode_pair_value(T& val, std::string_view& pb_str) {
auto key = detail::decode_key(pb_str);
pb_str = pb_str.substr(key.tag_size);
WireType wire_type = key.wire_type;
if (wire_type != detail::get_wire_type<std::remove_reference_t<T>>()) {
return;
}
from_pb_impl(val, pb_str);
}
template <typename T>
IGUANA_INLINE void from_pb_impl(T& val, std::string_view& pb_str,
uint32_t field_no) {
size_t pos = 0;
if constexpr (ylt_refletable_v<T>) {
const size_t size = detail::decode_length_delimited(
pb_str, "Invalid message value: too few bytes.");
if (size == 0) {
return;
}
from_pb(val, pb_str.substr(0, size));
pb_str = pb_str.substr(size);
}
else if constexpr (is_sequence_container<T>::value) {
using item_type = typename T::value_type;
if constexpr (is_lenprefix_v<item_type>) {
// item_type non-packed
while (!pb_str.empty()) {
item_type item{};
from_pb_impl(item, pb_str);
val.push_back(std::move(item));
if (pb_str.empty()) {
break;
}
auto key = detail::decode_key(pb_str);
if (key.field_number != field_no ||
key.wire_type != detail::get_wire_type<item_type>()) {
break;
}
else {
pb_str = pb_str.substr(key.tag_size);
}
}
}
else {
// item_type packed
const size_t size = detail::decode_length_delimited(
pb_str, "Invalid packed repeated value: too few bytes.");
std::string_view packed = pb_str.substr(0, size);
pb_str = pb_str.substr(size);
if constexpr (is_fixed_v<item_type>) {
if (size % sizeof(item_type) != 0)
IGUANA_UNLIKELY {
throw std::invalid_argument(
"Invalid packed fixed repeated value: bad length.");
}
size_t num = size / sizeof(item_type);
size_t old_size = val.size();
detail::resize(val, old_size + num);
std::memcpy(val.data() + old_size, packed.data(), size);
}
else {
while (!packed.empty()) {
item_type item;
from_pb_impl(item, packed);
val.push_back(std::move(item));
}
}
}
}
else if constexpr (is_map_container<T>::value) {
using key_type = typename T::key_type;
using mapped_type = typename T::mapped_type;
while (!pb_str.empty()) {
const size_t size = detail::decode_length_delimited(
pb_str, "Invalid map entry value: too few bytes.");
std::string_view entry = pb_str.substr(0, size);
pb_str = pb_str.substr(size);
key_type map_key{};
mapped_type mapped{};
while (!entry.empty()) {
auto entry_key = detail::decode_key(entry);
WireType entry_wire_type = entry_key.wire_type;
uint32_t entry_field_number = entry_key.field_number;
entry = entry.substr(entry_key.tag_size);
if (entry_field_number == 1) {
if (entry_wire_type == detail::get_wire_type<key_type>()) {
from_pb_impl(map_key, entry, 1);
}
else {
skip_unknown_field(entry, entry_wire_type, entry_field_number);
}
}
else if (entry_field_number == 2) {
if (entry_wire_type == detail::get_wire_type<mapped_type>()) {
from_pb_impl(mapped, entry, 2);
}
else {
skip_unknown_field(entry, entry_wire_type, entry_field_number);
}
}
else {
skip_unknown_field(entry, entry_wire_type, entry_field_number);
}
}
val.insert_or_assign(std::move(map_key), std::move(mapped));
if (pb_str.empty()) {
break;
}
auto key = detail::decode_key(pb_str);
if (key.field_number != field_no ||
key.wire_type != WireType::LengthDelimeted) {
break;
}
pb_str = pb_str.substr(key.tag_size);
}
}
else if constexpr (std::is_integral_v<T>) {
val = static_cast<T>(detail::decode_varint(pb_str, pos));
pb_str = pb_str.substr(pos);
}
else if constexpr (detail::is_signed_varint_v<T>) {
constexpr size_t len = sizeof(typename T::value_type);
uint64_t temp = detail::decode_varint(pb_str, pos);
if constexpr (len == 8) {
val.val = detail::decode_zigzag(temp);
}
else {
val.val = detail::decode_zigzag(static_cast<uint32_t>(temp));
}
pb_str = pb_str.substr(pos);
}
else if constexpr (detail::is_fixed_v<T>) {
constexpr size_t size = sizeof(typename T::value_type);
if (pb_str.size() < size)
IGUANA_UNLIKELY {
throw std::invalid_argument("Invalid fixed int value: too few bytes.");
}
memcpy(&(val.val), pb_str.data(), size);
pb_str = pb_str.substr(size);
}
else if constexpr (std::is_same_v<T, double> || std::is_same_v<T, float>) {
constexpr size_t size = sizeof(T);
if (pb_str.size() < size)
IGUANA_UNLIKELY {
throw std::invalid_argument("Invalid fixed int value: too few bytes.");
}
memcpy(&(val), pb_str.data(), size);
pb_str = pb_str.substr(size);
}
else if constexpr (std::is_same_v<T, std::string> ||
std::is_same_v<T, std::string_view>) {
const size_t size = detail::decode_length_delimited(
pb_str, "Invalid string value: too few bytes.");
if constexpr (std::is_same_v<T, std::string_view>) {
val = std::string_view(pb_str.data(), size);
}
else {
detail::resize(val, size);
memcpy(val.data(), pb_str.data(), size);
}
pb_str = pb_str.substr(size);
}
else if constexpr (std::is_enum_v<T>) {
using U = std::underlying_type_t<T>;
U value{};
from_pb_impl(value, pb_str);
val = static_cast<T>(value);
}
else if constexpr (optional_v<T>) {
if (!val.has_value()) {
val.emplace();
}
from_pb_impl(*val, pb_str, field_no);
}
else {
static_assert(!sizeof(T), "err");
}
}
template <typename T, typename Field>
IGUANA_INLINE void parse_oneof(T& t, const Field& f, std::string_view& pb_str) {
using item_type = typename std::decay_t<Field>::sub_type;
from_pb_impl(t.template emplace<item_type>(), pb_str, f.field_no);
}
template <typename T>
constexpr bool is_unpacked_repeated_type() {
using value_type = std::remove_const_t<std::remove_reference_t<T>>;
if constexpr (is_sequence_container<value_type>::value) {
using item_type = typename value_type::value_type;
return !is_lenprefix_v<item_type>;
}
else {
return false;
}
}
template <typename T>
constexpr bool is_unpacked_repeated_wire_type(WireType wire_type) {
using value_type = std::remove_const_t<std::remove_reference_t<T>>;
if constexpr (is_unpacked_repeated_type<value_type>()) {
using item_type = typename value_type::value_type;
return wire_type == detail::get_wire_type<item_type>();
}
else {
return false;
}
}
template <typename T>
IGUANA_INLINE void from_pb_unpacked_repeated_item(T& val,
std::string_view& pb_str) {
using value_type = std::remove_const_t<std::remove_reference_t<T>>;
using item_type = typename value_type::value_type;
item_type item{};
from_pb_impl(item, pb_str);
val.push_back(std::move(item));
}
template <bool TimestampSchema, typename T>
IGUANA_INLINE auto from_pb_well_known_value(const T* current,
std::string_view& pb_str,
uint32_t field_no) {
using value_type = std::remove_const_t<std::remove_reference_t<T>>;
if constexpr (TimestampSchema) {
iguana::pb_timestamp wire_value{};
if (current != nullptr) {
wire_value = iguana::pb_timestamp{*current};
}
from_pb_impl(wire_value, pb_str, field_no);
return static_cast<value_type>(wire_value);
}
else {
iguana::pb_duration wire_value{};
if (current != nullptr) {
wire_value = iguana::pb_duration{*current};
}
from_pb_impl(wire_value, pb_str, field_no);
return static_cast<value_type>(wire_value);
}
}
template <bool TimestampSchema, typename T>
IGUANA_INLINE void from_pb_well_known_impl(T& val, std::string_view& pb_str,
uint32_t field_no) {
using value_type = std::remove_const_t<std::remove_reference_t<T>>;
if constexpr (optional_v<value_type>) {
using item_type = typename value_type::value_type;
const item_type* current = val.has_value() ? &*val : nullptr;
val = from_pb_well_known_value<TimestampSchema, item_type>(current, pb_str,
field_no);
}
else if constexpr (is_sequence_container<value_type>::value) {
using item_type = typename value_type::value_type;
val.push_back(from_pb_well_known_value<TimestampSchema, item_type>(
nullptr, pb_str, field_no));
}
else {
val = from_pb_well_known_value<TimestampSchema, value_type>(&val, pb_str,
field_no);
}
}
template <typename Wire, typename T>
IGUANA_INLINE void from_pb_schema_impl(T& val, std::string_view& pb_str,
uint32_t field_no) {
using value_type = ylt::reflection::remove_cvref_t<T>;
using wire_type = ylt::reflection::remove_cvref_t<Wire>;
if constexpr (std::is_same_v<value_type, wire_type>) {
from_pb_impl(val, pb_str, field_no);
}
else if constexpr (is_sequence_container<value_type>::value) {
using wire_item_type = typename wire_type::value_type;
if constexpr (is_lenprefix_v<wire_item_type>) {
wire_type wire_value{};
from_pb_impl(wire_value, pb_str, field_no);
assign_pb_wire_value(val, std::move(wire_value));
}
else {
const size_t size = decode_length_delimited(
pb_str, "Invalid packed repeated value: too few bytes.");
std::string_view packed = pb_str.substr(0, size);
pb_str = pb_str.substr(size);
while (!packed.empty()) {
wire_item_type wire_item{};
from_pb_impl(wire_item, packed);
typename value_type::value_type item{};
assign_pb_wire_value(item, std::move(wire_item));
val.push_back(std::move(item));
}
}
}
else {
wire_type wire_value{};
from_pb_impl(wire_value, pb_str, field_no);
assign_pb_wire_value(val, std::move(wire_value));
}
}
// pb_str must already point past the field tag varint.
inline void skip_unknown_field(std::string_view& pb_str, WireType wire_type,
uint32_t field_number) {
switch (wire_type) {
case WireType::Varint: {
if (pb_str.empty())
throw std::invalid_argument("unknown Varint field: too few bytes");
size_t skip_pos = 0;
decode_varint(pb_str, skip_pos);
pb_str = pb_str.substr(skip_pos);
break;
}
case WireType::Fixed64:
if (pb_str.size() < 8)
throw std::invalid_argument("unknown Fixed64 field: too few bytes");
pb_str = pb_str.substr(8);
break;
case WireType::LengthDelimeted: {
const size_t len = decode_length_delimited(
pb_str, "unknown LengthDelimited field: too few bytes");
pb_str = pb_str.substr(len);
break;
}
case WireType::Fixed32:
if (pb_str.size() < 4)
throw std::invalid_argument("unknown Fixed32 field: too few bytes");
pb_str = pb_str.substr(4);
break;
case WireType::StartGroup: {
pb_recursion_guard recursion_guard;
while (true) {
if (pb_str.empty())
throw std::invalid_argument("unknown group field: missing end tag");
auto key = detail::decode_key(pb_str, true);
pb_str = pb_str.substr(key.tag_size);
WireType child_wire_type = key.wire_type;
uint32_t child_field_number = key.field_number;
if (child_wire_type == WireType::EndGroup) {
if (child_field_number != field_number) {
throw std::invalid_argument(
"unknown group field: mismatched end tag");
}
return;
}
skip_unknown_field(pb_str, child_wire_type, child_field_number);
}
}
case WireType::EndGroup:
throw std::runtime_error("unexpected end group in unknown field");
default:
throw std::runtime_error("unknown wire type in unknown field");
}
}
} // namespace detail
template <typename T>
IGUANA_INLINE void from_pb(T& t, std::string_view pb_str) {
if (pb_str.empty())
IGUANA_UNLIKELY { return; }
detail::pb_recursion_guard recursion_guard;
auto key = detail::decode_key(pb_str);
size_t pos = key.tag_size;
WireType wire_type = key.wire_type;
uint32_t field_number = key.field_number;
#ifdef SEQUENTIAL_PARSE
static auto tp = detail::get_pb_members_tuple(t);
constexpr size_t SIZE = std::tuple_size_v<std::decay_t<decltype(tp)>>;
bool parse_done = false;
auto ptr = &t;
detail::for_each_n(
[&, ptr](auto i) IGUANA__INLINE_LAMBDA {
auto val = std::get<decltype(i)::value>(tp);
using sub_type = typename std::decay_t<decltype(val)>::sub_type;
using value_type = typename std::decay_t<decltype(val)>::value_type;
using wire_value_type =
typename std::decay_t<decltype(val)>::wire_value_type;
using wire_sub_type =
typename std::decay_t<decltype(val)>::wire_sub_type;
constexpr bool is_variant_v = variant_v<value_type>;
// sub_type is the element type when value_type is the variant type;
// otherwise, they are the same.
if (parse_done || field_number != val.field_no) {
return;
}
pb_str = pb_str.substr(pos);
if constexpr (std::decay_t<decltype(val)>::timestamp_schema ||
std::decay_t<decltype(val)>::duration_schema) {
if (wire_type != WireType::LengthDelimeted) {
throw std::runtime_error("unmatched wire_type");
}
}
else if constexpr (std::is_same_v<sub_type, std::monostate>) {
throw std::runtime_error("oneof monostate has no wire type");
}
else if constexpr (detail::is_unpacked_repeated_type<
wire_value_type>()) {
if (wire_type != WireType::LengthDelimeted &&
!detail::is_unpacked_repeated_wire_type<wire_value_type>(
wire_type)) {
throw std::runtime_error("unmatched wire_type");
}
}
else if (wire_type != detail::get_wire_type<wire_sub_type>())
IGUANA_UNLIKELY { throw std::runtime_error("unmatched wire_type"); }
auto member_ptr = (value_type*)((char*)(ptr) + val.offset);
if constexpr (std::decay_t<decltype(val)>::timestamp_schema ||
std::decay_t<decltype(val)>::duration_schema) {
detail::from_pb_well_known_impl<
std::decay_t<decltype(val)>::timestamp_schema>(
*member_ptr, pb_str, val.field_no);
}
else if constexpr (detail::is_unpacked_repeated_type<
wire_value_type>()) {
if (wire_type == WireType::LengthDelimeted) {
detail::from_pb_schema_impl<wire_value_type>(*member_ptr, pb_str,
val.field_no);
}
else {
typename wire_value_type::value_type wire_item{};
detail::from_pb_impl(wire_item, pb_str);
typename value_type::value_type item{};
detail::assign_pb_wire_value(item, std::move(wire_item));
member_ptr->push_back(std::move(item));
}
}
else if constexpr (is_variant_v) {
detail::parse_oneof(*member_ptr, val, pb_str);
}
else {
detail::from_pb_schema_impl<wire_value_type>(*member_ptr, pb_str,
val.field_no);
}
if (pb_str.empty()) {
parse_done = true;
return;
}
key = detail::decode_key(pb_str);
pos = key.tag_size;
wire_type = key.wire_type;
field_number = key.field_number;
},
std::make_index_sequence<SIZE>{});
if (parse_done)
IGUANA_LIKELY { return; }
#endif
static auto map = detail::get_members(t);
while (true) {
const char* field_start = pb_str.data();
pb_str = pb_str.substr(pos);
auto it = map.find(field_number);
if (it == map.end()) {
// Unknown field: skip according to wire type (proto3 forward compat)
detail::skip_unknown_field(pb_str, wire_type, field_number);
detail::append_pb_unknown_field(
t, field_start, static_cast<size_t>(pb_str.data() - field_start));
}
else {
auto& member = it->second;
std::visit(
[&t, &pb_str, wire_type](auto& val) {
using sub_type = typename std::decay_t<decltype(val)>::sub_type;
using value_type = typename std::decay_t<decltype(val)>::value_type;
using wire_value_type =
typename std::decay_t<decltype(val)>::wire_value_type;
using wire_sub_type =
typename std::decay_t<decltype(val)>::wire_sub_type;
if constexpr (std::decay_t<decltype(val)>::timestamp_schema ||
std::decay_t<decltype(val)>::duration_schema) {
if (wire_type != WireType::LengthDelimeted) {
throw std::runtime_error("unmatched wire_type");
}
}
else if constexpr (std::is_same_v<sub_type, std::monostate>) {
throw std::runtime_error("oneof monostate has no wire type");
}
else if constexpr (detail::is_unpacked_repeated_type<
wire_value_type>()) {
if (wire_type != WireType::LengthDelimeted &&
!detail::is_unpacked_repeated_wire_type<wire_value_type>(
wire_type)) {
throw std::runtime_error("unmatched wire_type");
}
}
else if (wire_type != detail::get_wire_type<wire_sub_type>()) {
throw std::runtime_error("unmatched wire_type");
}
if constexpr (std::decay_t<decltype(val)>::timestamp_schema ||
std::decay_t<decltype(val)>::duration_schema) {
detail::from_pb_well_known_impl<
std::decay_t<decltype(val)>::timestamp_schema>(
val.value(t), pb_str, val.field_no);
}
else if constexpr (detail::is_unpacked_repeated_type<
wire_value_type>()) {
if (wire_type == WireType::LengthDelimeted) {
detail::from_pb_schema_impl<wire_value_type>(
val.value(t), pb_str, val.field_no);
}
else {
typename wire_value_type::value_type wire_item{};
detail::from_pb_impl(wire_item, pb_str);
typename value_type::value_type item{};
detail::assign_pb_wire_value(item, std::move(wire_item));
val.value(t).push_back(std::move(item));
}
}
else if constexpr (variant_v<value_type>) {
detail::parse_oneof(val.value(t), val, pb_str);
}
else {
detail::from_pb_schema_impl<wire_value_type>(val.value(t), pb_str,
val.field_no);
}
},
member);
}
if (!pb_str.empty())
IGUANA_LIKELY {
key = detail::decode_key(pb_str);
pos = key.tag_size;
wire_type = key.wire_type;
field_number = key.field_number;
}
else {
return;
}
}
}
template <typename T>
IGUANA_INLINE void from_pb_adl(iguana_adl_t* p, T& t, std::string_view pb_str) {
iguana::from_pb(t, pb_str);
}
} // namespace iguana
+826
View File
@@ -0,0 +1,826 @@
#pragma once
#include <cassert>
#include <cstddef>
#include <cstring>
#include <limits>
#include <map>
#include <stdexcept>
#include <string>
#include <string_view>
#include <tuple>
#include <type_traits>
#include <unordered_map>
#include <utility>
#include <variant>
#include <vector>
#include "common.hpp"
#include "detail/pb_type.hpp"
#include "util.hpp"
namespace iguana {
enum class WireType : uint32_t {
Varint = 0,
Fixed64 = 1,
LengthDelimeted = 2,
StartGroup = 3,
EndGroup = 4,
Fixed32 = 5,
Unknown
};
template <typename T>
constexpr bool inherits_from_base_v = std::is_base_of_v<detail::base, T>;
namespace detail {
template <typename T>
constexpr bool is_fixed_v =
std::is_same_v<T, fixed32_t> || std::is_same_v<T, fixed64_t> ||
std::is_same_v<T, sfixed32_t> || std::is_same_v<T, sfixed64_t>;
template <typename T>
constexpr bool is_signed_varint_v =
std::is_same_v<T, sint32_t> || std::is_same_v<T, sint64_t>;
template <typename T>
constexpr inline WireType get_wire_type() {
if constexpr (std::is_integral_v<T> || is_signed_varint_v<T> ||
std::is_enum_v<T> || std::is_same_v<T, bool>) {
return WireType::Varint;
}
else if constexpr (std::is_same_v<T, fixed32_t> ||
std::is_same_v<T, sfixed32_t> ||
std::is_same_v<T, float>) {
return WireType::Fixed32;
}
else if constexpr (std::is_same_v<T, fixed64_t> ||
std::is_same_v<T, sfixed64_t> ||
std::is_same_v<T, double>) {
return WireType::Fixed64;
}
else if constexpr (std::is_same_v<T, std::string> ||
std::is_same_v<T, std::string_view> ||
ylt_refletable_v<T> || is_sequence_container<T>::value ||
is_map_container<T>::value) {
return WireType::LengthDelimeted;
}
else if constexpr (optional_v<T>) {
return get_wire_type<typename T::value_type>();
}
else {
throw std::runtime_error("unknown type");
}
}
template <typename T>
constexpr bool is_lenprefix_v = (get_wire_type<T>() ==
WireType::LengthDelimeted);
template <typename T>
inline constexpr bool pb_well_known_chrono_v =
std::is_same_v<std::remove_const_t<std::remove_reference_t<T>>,
std::chrono::system_clock::time_point> ||
std::is_same_v<std::remove_const_t<std::remove_reference_t<T>>,
std::chrono::nanoseconds>;
template <bool TimestampSchema, typename T>
IGUANA_INLINE auto make_pb_well_known_value(const T& value) {
if constexpr (TimestampSchema) {
return iguana::pb_timestamp{value};
}
else {
return iguana::pb_duration{value};
}
}
[[nodiscard]] IGUANA_INLINE uint32_t encode_zigzag(int32_t v) {
return (static_cast<uint32_t>(v) << 1U) ^
static_cast<uint32_t>(
-static_cast<int32_t>(static_cast<uint32_t>(v) >> 31U));
}
[[nodiscard]] IGUANA_INLINE uint64_t encode_zigzag(int64_t v) {
return (static_cast<uint64_t>(v) << 1U) ^
static_cast<uint64_t>(
-static_cast<int64_t>(static_cast<uint64_t>(v) >> 63U));
}
[[nodiscard]] IGUANA_INLINE int64_t decode_zigzag(uint64_t u) {
return static_cast<int64_t>((u >> 1U)) ^
static_cast<uint64_t>(-static_cast<int64_t>(u & 1U));
}
[[nodiscard]] IGUANA_INLINE int64_t decode_zigzag(uint32_t u) {
return static_cast<int64_t>((u >> 1U)) ^
static_cast<uint64_t>(-static_cast<int64_t>(u & 1U));
}
template <class T>
IGUANA_INLINE uint64_t decode_varint(T& data, size_t& pos) {
if (data.empty())
IGUANA_UNLIKELY {
throw std::invalid_argument("Invalid varint value: too few bytes.");
}
const auto* begin = reinterpret_cast<const unsigned char*>(data.data());
uint64_t val = 0;
const size_t size = data.size();
const size_t limit = size < 10 ? size : 10;
for (size_t i = 0; i < limit; ++i) {
const uint64_t byte = begin[i];
if (i == 9 && (byte & 0xfeU) != 0)
IGUANA_UNLIKELY {
throw std::invalid_argument("Invalid varint value: too many bytes.");
}
val |= (byte & 0x7fU) << (7 * i);
if ((byte & 0x80U) == 0) {
pos = i + 1;
return val;
}
}
if (size >= 10)
IGUANA_UNLIKELY {
throw std::invalid_argument("Invalid varint value: too many bytes.");
}
throw std::invalid_argument("Invalid varint value: too few bytes.");
}
struct pb_key {
uint32_t field_number;
WireType wire_type;
size_t tag_size;
};
IGUANA_INLINE pb_key decode_key(std::string_view data,
bool allow_end_group = false) {
size_t pos = 0;
const uint64_t raw_key = decode_varint(data, pos);
if (raw_key > std::numeric_limits<uint32_t>::max())
IGUANA_UNLIKELY {
throw std::invalid_argument("Invalid protobuf tag: too large.");
}
const uint32_t key = static_cast<uint32_t>(raw_key);
const uint32_t wire = key & 0b0111U;
const uint32_t field_number = key >> 3U;
if (field_number == 0)
IGUANA_UNLIKELY {
throw std::invalid_argument(
"Invalid protobuf tag: field number must be positive.");
}
if (wire > static_cast<uint32_t>(WireType::Fixed32))
IGUANA_UNLIKELY {
throw std::invalid_argument("Invalid protobuf tag: unknown wire type.");
}
auto wire_type = static_cast<WireType>(wire);
if (wire_type == WireType::EndGroup && !allow_end_group)
IGUANA_UNLIKELY {
throw std::invalid_argument("unexpected end group in protobuf stream");
}
return {field_number, wire_type, pos};
}
IGUANA_INLINE size_t decode_length_delimited(std::string_view& pb_str,
const char* error) {
size_t pos = 0;
const uint64_t size = decode_varint(pb_str, pos);
pb_str = pb_str.substr(pos);
if (size > pb_str.size())
IGUANA_UNLIKELY { throw std::invalid_argument(error); }
if (size > std::numeric_limits<size_t>::max())
IGUANA_UNLIKELY {
throw std::invalid_argument("Invalid length-delimited value: too large.");
}
return static_cast<size_t>(size);
}
inline constexpr size_t max_pb_recursion_depth = 100;
inline thread_local size_t pb_recursion_depth = 0;
struct pb_recursion_guard {
pb_recursion_guard() {
if (pb_recursion_depth >= max_pb_recursion_depth)
IGUANA_UNLIKELY {
throw std::runtime_error("protobuf message recursion limit exceeded");
}
++pb_recursion_depth;
}
~pb_recursion_guard() { --pb_recursion_depth; }
};
// value == 0 ? 1 : floor(log2(value)) / 7 + 1
constexpr size_t variant_uint32_size_constexpr(uint32_t value) {
if (value == 0) {
return 1;
}
int log = 0;
while (value >>= 1) ++log;
return log / 7 + 1;
}
template <uint64_t v, typename Writer, size_t... I>
IGUANA_INLINE void append_varint_u32(Writer& writer,
std::index_sequence<I...>) {
uint8_t temp = 0;
((temp = static_cast<uint8_t>((v >> (7 * I)) | 0x80),
writer.write((const char*)&temp, 1)),
...);
}
template <uint32_t v, typename Writer>
IGUANA_INLINE void serialize_varint_u32(Writer& writer) {
constexpr auto size = variant_uint32_size_constexpr(v);
append_varint_u32<v>(writer, std::make_index_sequence<size - 1>{});
uint8_t temp = static_cast<uint8_t>(v >> (7 * (size - 1)));
writer.write((const char*)&temp, 1);
}
template <typename Writer>
IGUANA_INLINE void serialize_varint(uint64_t v, Writer& writer) {
uint8_t temp = static_cast<uint8_t>(v);
if (v < 0x80) {
writer.write((const char*)&temp, 1);
return;
}
temp = static_cast<uint8_t>(v | 0x80);
writer.write((const char*)&temp, 1);
v >>= 7;
if (v < 0x80) {
temp = static_cast<uint8_t>(v);
writer.write((const char*)&temp, 1);
return;
}
do {
temp = static_cast<uint8_t>(v | 0x80);
writer.write((const char*)&temp, 1);
v >>= 7;
} while (v >= 0x80);
temp = static_cast<uint8_t>(v);
writer.write((const char*)&temp, 1);
}
IGUANA_INLINE uint32_t log2_floor_uint32(uint32_t n) {
#if defined(__GNUC__)
return 31 ^ static_cast<uint32_t>(__builtin_clz(n));
#else
unsigned long where;
_BitScanReverse(&where, n);
return where;
#endif
}
IGUANA_INLINE size_t variant_uint32_size(uint32_t value) {
// This computes value == 0 ? 1 : floor(log2(value)) / 7 + 1
// Use an explicit multiplication to implement the divide of
// a number in the 1..31 range.
// Explicit OR 0x1 to avoid calling Bits::Log2FloorNonZero(0), which is
// undefined.
uint32_t log2value = log2_floor_uint32(value | 0x1);
return static_cast<size_t>((log2value * 9 + 73) / 64);
}
IGUANA_INLINE int Log2FloorNonZero_Portable(uint32_t n) {
if (n == 0)
return -1;
int log = 0;
uint32_t value = n;
for (int i = 4; i >= 0; --i) {
int shift = (1 << i);
uint32_t x = value >> shift;
if (x != 0) {
value = x;
log += shift;
}
}
assert(value == 1);
return log;
}
IGUANA_INLINE uint32_t Log2FloorNonZero(uint32_t n) {
#if defined(__GNUC__)
return 31 ^ static_cast<uint32_t>(__builtin_clz(n));
#elif defined(_MSC_VER)
unsigned long where;
_BitScanReverse(&where, n);
return where;
#else
return Log2FloorNonZero_Portable(n);
#endif
}
IGUANA_INLINE int Log2FloorNonZero64_Portable(uint64_t n) {
const uint32_t topbits = static_cast<uint32_t>(n >> 32);
if (topbits == 0) {
// Top bits are zero, so scan in bottom bits
return static_cast<int>(Log2FloorNonZero(static_cast<uint32_t>(n)));
}
else {
return 32 + static_cast<int>(Log2FloorNonZero(topbits));
}
}
IGUANA_INLINE uint32_t log2_floor_uint64(uint64_t n) {
#if defined(__GNUC__)
return 63 ^ static_cast<uint32_t>(__builtin_clzll(n));
#elif defined(_MSC_VER) && defined(_M_X64)
unsigned long where;
_BitScanReverse64(&where, n);
return where;
#else
return Log2FloorNonZero64_Portable(n);
#endif
}
IGUANA_INLINE size_t variant_uint64_size(uint64_t value) {
// This computes value == 0 ? 1 : floor(log2(value)) / 7 + 1
// Use an explicit multiplication to implement the divide of
// a number in the 1..63 range.
// Explicit OR 0x1 to avoid calling Bits::Log2FloorNonZero(0), which is
// undefined.
uint32_t log2value = log2_floor_uint64(value | 0x1);
return static_cast<size_t>((log2value * 9 + 73) / 64);
}
template <typename U>
constexpr IGUANA_INLINE size_t variant_intergal_size(U value) {
using T = std::remove_reference_t<U>;
if constexpr (sizeof(T) == 8) {
return variant_uint64_size(static_cast<uint64_t>(value));
}
else if constexpr (sizeof(T) <= 4) {
if constexpr (std::is_signed_v<T>) {
if (value < 0) {
return 10;
}
}
return variant_uint32_size(static_cast<uint32_t>(value));
}
else {
static_assert(!sizeof(T), "intergal in not supported");
}
}
template <typename F, size_t... I>
IGUANA_INLINE constexpr void for_each_n(F&& f, std::index_sequence<I...>) {
(std::forward<F>(f)(std::integral_constant<size_t, I>{}), ...);
}
template <size_t key_size, bool omit_default_val, typename Type>
IGUANA_INLINE size_t str_numeric_size(Type&& t) {
using T = std::remove_const_t<std::remove_reference_t<Type>>;
if constexpr (std::is_same_v<T, std::string> ||
std::is_same_v<T, std::string_view>) {
// string
if constexpr (omit_default_val) {
if (t.size() == 0)
IGUANA_UNLIKELY { return 0; }
}
if constexpr (key_size == 0) {
return t.size();
}
else {
return key_size + variant_uint32_size(static_cast<uint32_t>(t.size())) +
t.size();
}
}
else {
// numeric
if constexpr (omit_default_val) {
if constexpr (is_fixed_v<T> || is_signed_varint_v<T>) {
if (t.val == 0)
IGUANA_UNLIKELY { return 0; }
}
else {
if (t == static_cast<T>(0))
IGUANA_UNLIKELY { return 0; }
}
}
if constexpr (std::is_integral_v<T>) {
if constexpr (std::is_same_v<bool, T>) {
return 1 + key_size;
}
else {
return key_size + variant_intergal_size(t);
}
}
else if constexpr (detail::is_signed_varint_v<T>) {
return key_size + variant_intergal_size(encode_zigzag(t.val));
}
else if constexpr (detail::is_fixed_v<T>) {
return key_size + sizeof(typename T::value_type);
}
else if constexpr (std::is_same_v<T, double> || std::is_same_v<T, float>) {
return key_size + sizeof(T);
}
else if constexpr (std::is_enum_v<T>) {
using U = std::underlying_type_t<T>;
return key_size + variant_intergal_size(static_cast<U>(t));
}
else {
static_assert(!sizeof(T), "err");
}
}
}
template <size_t key_size, bool omit_default_val = true, typename Type,
typename Arr>
IGUANA_INLINE size_t pb_key_value_size(Type&& t, Arr& size_arr);
template <bool TimestampSchema, size_t key_size, bool omit_default_val = true,
typename Type, typename Arr>
IGUANA_INLINE size_t pb_well_known_key_value_size(Type&& t, Arr& size_arr) {
using T = std::remove_const_t<std::remove_reference_t<Type>>;
if constexpr (optional_v<T>) {
if (!t.has_value()) {
return 0;
}
return pb_well_known_key_value_size<TimestampSchema, key_size,
omit_default_val>(*t, size_arr);
}
else if constexpr (is_sequence_container<T>::value) {
size_t len = 0;
for (auto& item : t) {
len += pb_well_known_key_value_size<TimestampSchema, key_size, false>(
item, size_arr);
}
return len;
}
else {
auto wire_value = make_pb_well_known_value<TimestampSchema>(t);
return pb_key_value_size<key_size, omit_default_val>(wire_value, size_arr);
}
}
template <typename Wire, typename T>
IGUANA_INLINE auto make_pb_wire_scalar(const T& value) {
using W = ylt::reflection::remove_cvref_t<Wire>;
using U = ylt::reflection::remove_cvref_t<T>;
if constexpr (std::is_same_v<W, U>) {
return value;
}
else if constexpr (is_pb_type_v<W>) {
return W{static_cast<typename W::value_type>(value)};
}
else {
return W{value};
}
}
template <typename T, typename Wire>
IGUANA_INLINE void assign_pb_wire_value(T& value, Wire&& wire) {
using U = ylt::reflection::remove_cvref_t<T>;
using W = ylt::reflection::remove_cvref_t<Wire>;
if constexpr (std::is_same_v<U, W>) {
value = std::forward<Wire>(wire);
}
else if constexpr (optional_v<U>) {
if (wire.has_value()) {
typename U::value_type item{};
assign_pb_wire_value(item, *wire);
value = std::move(item);
}
else {
value.reset();
}
}
else if constexpr (is_sequence_container<U>::value) {
value.clear();
for (auto& item : wire) {
typename U::value_type converted{};
assign_pb_wire_value(converted, item);
value.push_back(std::move(converted));
}
}
else if constexpr (is_pb_type_v<W>) {
value = wire.val;
}
else {
value = static_cast<U>(wire);
}
}
template <typename Wire, size_t key_size, bool omit_default_val = true,
typename Type, typename Arr>
IGUANA_INLINE size_t pb_schema_key_value_size(Type&& t, Arr& size_arr);
template <typename Variant, typename T, size_t I>
constexpr inline size_t get_variant_index() {
if constexpr (I == 0) {
static_assert(std::is_same_v<std::variant_alternative_t<0, Variant>, T>,
"Type T is not found in Variant");
return 0;
}
else if constexpr (std::is_same_v<std::variant_alternative_t<I, Variant>,
T>) {
return I;
}
else {
return get_variant_index<Variant, T, I - 1>();
}
}
template <size_t field_no, typename Type, typename Arr>
IGUANA_INLINE size_t pb_oneof_size(Type&& t, Arr& size_arr) {
using T = std::decay_t<Type>;
int len = 0;
std::visit(
[&len, &size_arr](auto&& value) IGUANA__INLINE_LAMBDA {
using raw_value_type = decltype(value);
using value_type =
std::remove_const_t<std::remove_reference_t<decltype(value)>>;
constexpr auto offset =
get_variant_index<T, value_type, std::variant_size_v<T> - 1>();
constexpr uint32_t key =
((field_no + offset) << 3) |
static_cast<uint32_t>(get_wire_type<value_type>());
len = pb_key_value_size<variant_uint32_size_constexpr(key), false>(
std::forward<raw_value_type>(value), size_arr);
},
std::forward<Type>(t));
return len;
}
// returns size = key_size + optional(len_size) + len
// when key_size == 0, return len
template <size_t key_size, bool omit_default_val, typename Type, typename Arr>
IGUANA_INLINE size_t pb_key_value_size(Type&& t, Arr& size_arr) {
using T = std::remove_const_t<std::remove_reference_t<Type>>;
if constexpr (ylt_refletable_v<T> || is_custom_reflection_v<T>) {
size_t len = 0;
static auto tuple = get_pb_members_tuple(std::forward<Type>(t));
constexpr size_t SIZE = std::tuple_size_v<std::decay_t<decltype(tuple)>>;
size_t pre_index = -1;
if constexpr (!inherits_from_base_v<T> && key_size != 0) {
pre_index = size_arr.size();
size_arr.push_back(0); // placeholder
}
for_each_n(
[&len, &t, &size_arr](auto i) IGUANA__INLINE_LAMBDA {
using field_type =
std::tuple_element_t<decltype(i)::value,
std::decay_t<decltype(tuple)>>;
auto value = std::get<decltype(i)::value>(tuple);
using U = typename field_type::value_type;
using wire_type = typename field_type::wire_value_type;
using sub_type = typename field_type::sub_type;
auto& val = value.value(t);
if constexpr (field_type::timestamp_schema ||
field_type::duration_schema) {
constexpr uint32_t sub_key =
(value.field_no << 3) |
static_cast<uint32_t>(WireType::LengthDelimeted);
constexpr auto sub_keysize = variant_uint32_size_constexpr(sub_key);
len += pb_well_known_key_value_size<field_type::timestamp_schema,
sub_keysize>(val, size_arr);
}
else if constexpr (field_type::optional_schema) {
constexpr uint32_t sub_key =
(value.field_no << 3) |
static_cast<uint32_t>(get_wire_type<wire_type>());
constexpr auto sub_keysize = variant_uint32_size_constexpr(sub_key);
len += pb_schema_key_value_size<wire_type, sub_keysize, false>(
val, size_arr);
}
else if constexpr (variant_v<U>) {
constexpr auto offset =
get_variant_index<U, sub_type, std::variant_size_v<U> - 1>();
if (val.index() == offset) {
if constexpr (!std::is_same_v<sub_type, std::monostate>) {
constexpr uint32_t sub_key =
(value.field_no << 3) |
static_cast<uint32_t>(get_wire_type<sub_type>());
constexpr auto sub_keysize =
variant_uint32_size_constexpr(sub_key);
len += pb_key_value_size<sub_keysize, false>(
std::get<offset>(val), size_arr);
}
}
}
else {
constexpr uint32_t sub_key =
(value.field_no << 3) |
static_cast<uint32_t>(get_wire_type<wire_type>());
constexpr auto sub_keysize = variant_uint32_size_constexpr(sub_key);
len +=
pb_schema_key_value_size<wire_type, sub_keysize>(val, size_arr);
}
},
std::make_index_sequence<SIZE>{});
len += pb_unknown_fields_size(t);
if constexpr (inherits_from_base_v<T>) {
t.cache_size = len;
}
else if constexpr (key_size != 0) {
size_arr[pre_index] = len;
}
if constexpr (key_size == 0) {
// for top level
return len;
}
else {
if (len == 0) {
// equals key_size + variant_uint32_size(len)
return key_size + 1;
}
else {
return key_size + variant_uint32_size(static_cast<uint32_t>(len)) + len;
}
}
}
else if constexpr (is_sequence_container<T>::value) {
using item_type = typename T::value_type;
size_t len = 0;
if constexpr (is_lenprefix_v<item_type>) {
for (auto& item : t) {
len += pb_key_value_size<key_size, false>(item, size_arr);
}
return len;
}
else {
for (auto& item : t) {
// here 0 to get pakced size, and item must be numeric
len += str_numeric_size<0, false>(item);
}
if (len == 0) {
return 0;
}
else {
return key_size + variant_uint32_size(static_cast<uint32_t>(len)) + len;
}
}
}
else if constexpr (is_map_container<T>::value) {
size_t len = 0;
for (auto& [k, v] : t) {
// the key_size of k and v is constant 1
auto kv_len = pb_key_value_size<1, false>(k, size_arr) +
pb_key_value_size<1, false>(v, size_arr);
len += key_size + variant_uint32_size(static_cast<uint32_t>(kv_len)) +
kv_len;
}
return len;
}
else if constexpr (optional_v<T>) {
if (!t.has_value()) {
return 0;
}
return pb_key_value_size<key_size, omit_default_val>(*t, size_arr);
}
else {
return str_numeric_size<key_size, omit_default_val>(t);
}
}
template <typename Wire, size_t key_size, bool omit_default_val, typename Type,
typename Arr>
IGUANA_INLINE size_t pb_schema_key_value_size(Type&& t, Arr& size_arr) {
using T = ylt::reflection::remove_cvref_t<Type>;
using W = ylt::reflection::remove_cvref_t<Wire>;
if constexpr (std::is_same_v<T, W>) {
return pb_key_value_size<key_size, omit_default_val>(std::forward<Type>(t),
size_arr);
}
else if constexpr (optional_v<T>) {
if (!t.has_value()) {
return 0;
}
return pb_schema_key_value_size<typename W::value_type, key_size,
omit_default_val>(*t, size_arr);
}
else if constexpr (is_sequence_container<T>::value) {
using wire_item_type = typename W::value_type;
size_t len = 0;
if constexpr (is_lenprefix_v<wire_item_type>) {
for (auto& item : t) {
len += pb_schema_key_value_size<wire_item_type, key_size, false>(
item, size_arr);
}
return len;
}
else {
for (auto& item : t) {
auto wire_item = make_pb_wire_scalar<wire_item_type>(item);
len += str_numeric_size<0, false>(wire_item);
}
if (len == 0) {
return 0;
}
return key_size + variant_uint32_size(static_cast<uint32_t>(len)) + len;
}
}
else {
auto wire_value = make_pb_wire_scalar<W>(t);
return pb_key_value_size<key_size, omit_default_val>(wire_value, size_arr);
}
}
// return the payload size
template <bool skip_next = true, typename Type>
IGUANA_INLINE size_t pb_value_size(Type&& t, uint32_t*& sz_ptr) {
using T = std::remove_const_t<std::remove_reference_t<Type>>;
if constexpr (ylt_refletable_v<T> || is_custom_reflection_v<T>) {
if constexpr (inherits_from_base_v<T>) {
return t.cache_size;
}
else {
// *sz_ptr is secure and logically guaranteed
if constexpr (skip_next) {
return *(sz_ptr++);
}
else {
return *sz_ptr;
}
}
}
else if constexpr (is_sequence_container<T>::value) {
using item_type = typename T::value_type;
size_t len = 0;
if constexpr (!is_lenprefix_v<item_type>) {
for (auto& item : t) {
len += str_numeric_size<0, false>(item);
}
return len;
}
else {
static_assert(!sizeof(item_type), "the size of this type is meaningless");
}
}
else if constexpr (is_map_container<T>::value) {
static_assert(!sizeof(T), "the size of this type is meaningless");
}
else if constexpr (optional_v<T>) {
if (!t.has_value()) {
return 0;
}
return pb_value_size(*t, sz_ptr);
}
else {
return str_numeric_size<0, false>(t);
}
}
template <typename T, size_t... I>
inline auto build_pb_members_impl(
const std::array<size_t, sizeof...(I)>& offset_arr,
std::index_sequence<I...>) {
constexpr auto names = ylt::reflection::get_member_names<T>();
constexpr auto numbers = get_pb_field_numbers((T*)nullptr);
#ifdef YLT_USE_CXX26_REFLECTION
static constexpr auto members =
ylt::reflection::reflect26::data_members_array<T>();
return std::tuple_cat(
build_pb_fields_impl<T, numbers[I] - 1,
ylt::reflection::reflect26::meta_type_t<members[I]>>(
offset_arr[I], names[I])...);
#else
using Tuple = decltype(ylt::reflection::object_to_tuple(std::declval<T>()));
return std::tuple_cat(
build_pb_fields_impl<T, numbers[I] - 1, std::tuple_element_t<I, Tuple>>(
offset_arr[I], names[I])...);
#endif
}
template <typename T>
inline auto build_pb_members() {
#ifdef YLT_USE_CXX26_REFLECTION
constexpr size_t N = ylt::reflection::members_count_v<T>;
static const auto& offset_arr =
ylt::reflection::internal::get_member_offset_arr<T>();
#else
using Tuple = decltype(ylt::reflection::object_to_tuple(std::declval<T>()));
constexpr size_t N = std::tuple_size_v<Tuple>;
static auto& offset_arr = ylt::reflection::internal::get_member_offset_arr(
ylt::reflection::internal::wrapper<T>::value);
#endif
auto res =
build_pb_members_impl<T>(offset_arr, std::make_index_sequence<N>{});
using ResultTuple = decltype(res);
validate_pb_members_tuple<ResultTuple>();
return res;
}
#define YLT_REFL_PB(STRUCT, ...) \
IGUANA_PB_YLT_REFL_FWD( \
STRUCT, WRAP_ARGS(IGUANA_PB_FIELD_NAME, unused, ##__VA_ARGS__)) \
inline constexpr auto get_pb_field_numbers(STRUCT*) { \
return std::array<size_t, YLT_ARG_COUNT(__VA_ARGS__)>{ \
WRAP_ARGS(IGUANA_PB_FIELD_NO, unused, ##__VA_ARGS__)}; \
} \
inline auto get_members_impl(STRUCT*) { \
return iguana::detail::build_pb_members<STRUCT>(); \
}
#define IGUANA_PB_GET_FIELD_(field, no) field
#define IGUANA_PB_GET_FIELD(pair) IGUANA_PB_GET_FIELD_ pair
#define IGUANA_PB_GET_NO_(field, no) no
#define IGUANA_PB_GET_NO(pair) IGUANA_PB_GET_NO_ pair
#define IGUANA_PB_FIELD_NAME(unused, pair) IGUANA_PB_GET_FIELD(pair)
#define IGUANA_PB_FIELD_NO(unused, pair) (size_t) IGUANA_PB_GET_NO(pair)
#define IGUANA_PB_YLT_REFL_FWD(STRUCT, ...) YLT_REFL(STRUCT, __VA_ARGS__)
} // namespace detail
} // namespace iguana
+814
View File
@@ -0,0 +1,814 @@
#pragma once
#include "detail/string_resize.hpp"
#include "pb_util.hpp"
namespace iguana {
namespace detail {
template <uint32_t key, typename V, typename Writer>
IGUANA_INLINE void encode_varint_field(V val, Writer& writer) {
static_assert(std::is_integral_v<V>, "must be integral");
if constexpr (key != 0) {
serialize_varint_u32<key>(writer);
}
serialize_varint(val, writer);
}
template <uint32_t key, typename V, typename Writer>
IGUANA_INLINE void encode_fixed_field(V val, Writer& writer) {
if constexpr (key != 0) {
serialize_varint_u32<key>(writer);
}
constexpr size_t size = sizeof(V);
// TODO: check Stream continuous
writer.write((const char*)&val, size);
}
template <uint32_t key, bool omit_default_val = true, typename Type,
typename Writer>
IGUANA_INLINE void to_pb_impl(Type&& t, uint32_t*& sz_ptr, Writer& writer);
template <uint32_t key, bool omit_default_val, typename T, typename Writer>
IGUANA_INLINE void encode_numeric_field(T t, Writer& writer);
template <bool TimestampSchema, uint32_t key, bool omit_default_val = true,
typename Type, typename Writer>
IGUANA_INLINE void to_pb_well_known_impl(Type&& t, uint32_t*& sz_ptr,
Writer& writer) {
using T = std::remove_const_t<std::remove_reference_t<Type>>;
if constexpr (optional_v<T>) {
if (!t.has_value()) {
return;
}
to_pb_well_known_impl<TimestampSchema, key, omit_default_val>(*t, sz_ptr,
writer);
}
else if constexpr (is_sequence_container<T>::value) {
for (auto& item : t) {
to_pb_well_known_impl<TimestampSchema, key, false>(item, sz_ptr, writer);
}
}
else {
auto wire_value = make_pb_well_known_value<TimestampSchema>(t);
to_pb_impl<key, omit_default_val>(wire_value, sz_ptr, writer);
}
}
template <typename Wire, uint32_t key, bool omit_default_val = true,
typename Type, typename Writer>
IGUANA_INLINE void to_pb_schema_impl(Type&& t, uint32_t*& sz_ptr,
Writer& writer) {
using T = ylt::reflection::remove_cvref_t<Type>;
using W = ylt::reflection::remove_cvref_t<Wire>;
if constexpr (std::is_same_v<T, W>) {
to_pb_impl<key, omit_default_val>(std::forward<Type>(t), sz_ptr, writer);
}
else if constexpr (optional_v<T>) {
if (!t.has_value()) {
return;
}
to_pb_schema_impl<typename W::value_type, key, omit_default_val>(*t, sz_ptr,
writer);
}
else if constexpr (is_sequence_container<T>::value) {
using wire_item_type = typename W::value_type;
if constexpr (is_lenprefix_v<wire_item_type>) {
for (auto& item : t) {
to_pb_schema_impl<wire_item_type, key, false>(item, sz_ptr, writer);
}
}
else {
if (t.empty())
IGUANA_UNLIKELY { return; }
serialize_varint_u32<key>(writer);
size_t len = 0;
for (auto& item : t) {
auto wire_item = make_pb_wire_scalar<wire_item_type>(item);
len += str_numeric_size<0, false>(wire_item);
}
serialize_varint(len, writer);
for (auto& item : t) {
auto wire_item = make_pb_wire_scalar<wire_item_type>(item);
encode_numeric_field<0, false>(wire_item, writer);
}
}
}
else {
auto wire_value = make_pb_wire_scalar<W>(t);
to_pb_impl<key, omit_default_val>(wire_value, sz_ptr, writer);
}
}
template <uint32_t key, typename V, typename Writer>
IGUANA_INLINE void encode_pair_value(V&& val, size_t size, uint32_t*& sz_ptr,
Writer& writer) {
if (size == 0)
IGUANA_UNLIKELY {
// map keys can't be omitted even if values are empty
// TODO: repeated ?
serialize_varint_u32<key>(writer);
serialize_varint(0, writer);
}
else {
to_pb_impl<key, false>(val, sz_ptr, writer);
}
}
template <uint32_t key, bool omit_default_val, typename T, typename Writer>
IGUANA_INLINE void encode_numeric_field(T t, Writer& writer) {
if constexpr (omit_default_val) {
if constexpr (is_fixed_v<T> || is_signed_varint_v<T>) {
if (t.val == 0) {
return;
}
}
else {
if (t == static_cast<T>(0))
IGUANA_UNLIKELY { return; }
}
}
if constexpr (std::is_integral_v<T>) {
detail::encode_varint_field<key>(t, writer);
}
else if constexpr (detail::is_signed_varint_v<T>) {
detail::encode_varint_field<key>(encode_zigzag(t.val), writer);
}
else if constexpr (detail::is_fixed_v<T>) {
detail::encode_fixed_field<key>(t.val, writer);
}
else if constexpr (std::is_same_v<T, double> || std::is_same_v<T, float>) {
detail::encode_fixed_field<key>(t, writer);
}
else if constexpr (std::is_enum_v<T>) {
using U = std::underlying_type_t<T>;
detail::encode_varint_field<key>(static_cast<U>(t), writer);
}
else {
static_assert(!sizeof(T), "unsupported type");
}
}
template <uint32_t field_no, typename Type, typename Writer>
IGUANA_INLINE void to_pb_oneof(Type&& t, uint32_t*& sz_ptr, Writer& writer) {
using T = std::decay_t<Type>;
std::visit(
[&sz_ptr, &writer](auto&& value) IGUANA__INLINE_LAMBDA {
using raw_value_type = decltype(value);
using value_type =
std::remove_const_t<std::remove_reference_t<decltype(value)>>;
constexpr auto offset =
get_variant_index<T, value_type, std::variant_size_v<T> - 1>();
constexpr uint32_t key =
((field_no + offset) << 3) |
static_cast<uint32_t>(get_wire_type<value_type>());
to_pb_impl<key, false>(std::forward<raw_value_type>(value), sz_ptr,
writer);
},
std::forward<Type>(t));
}
// omit_default_val = true indicates to omit the default value in searlization
template <uint32_t key, bool omit_default_val, typename Type, typename Writer>
IGUANA_INLINE void to_pb_impl(Type&& t, uint32_t*& sz_ptr, Writer& writer) {
using T = std::remove_const_t<std::remove_reference_t<Type>>;
if constexpr (ylt_refletable_v<T> || is_custom_reflection_v<T>) {
// can't be omitted even if values are empty
if constexpr (key != 0) {
auto len = pb_value_size(t, sz_ptr);
serialize_varint_u32<key>(writer);
serialize_varint(len, writer);
if (len == 0)
IGUANA_UNLIKELY { return; }
}
static auto tuple = get_pb_members_tuple(std::forward<Type>(t));
constexpr size_t SIZE = std::tuple_size_v<std::decay_t<decltype(tuple)>>;
for_each_n(
[&t, &sz_ptr, &writer](auto i) IGUANA__INLINE_LAMBDA {
using field_type =
std::tuple_element_t<decltype(i)::value,
std::decay_t<decltype(tuple)>>;
auto value = std::get<decltype(i)::value>(tuple);
auto& val = value.value(t);
using U = typename field_type::value_type;
using wire_type = typename field_type::wire_value_type;
using sub_type = typename field_type::sub_type;
if constexpr (field_type::timestamp_schema ||
field_type::duration_schema) {
constexpr uint32_t sub_key =
(value.field_no << 3) |
static_cast<uint32_t>(WireType::LengthDelimeted);
to_pb_well_known_impl<field_type::timestamp_schema, sub_key>(
val, sz_ptr, writer);
}
else if constexpr (field_type::optional_schema) {
constexpr uint32_t sub_key =
(value.field_no << 3) |
static_cast<uint32_t>(get_wire_type<wire_type>());
to_pb_schema_impl<wire_type, sub_key, false>(val, sz_ptr, writer);
}
else if constexpr (variant_v<U>) {
constexpr auto offset =
get_variant_index<U, sub_type, std::variant_size_v<U> - 1>();
if (val.index() == offset) {
if constexpr (!std::is_same_v<sub_type, std::monostate>) {
constexpr uint32_t sub_key =
(value.field_no << 3) |
static_cast<uint32_t>(get_wire_type<sub_type>());
to_pb_impl<sub_key, false>(std::get<offset>(val), sz_ptr,
writer);
}
}
}
else {
constexpr uint32_t sub_key =
(value.field_no << 3) |
static_cast<uint32_t>(get_wire_type<wire_type>());
to_pb_schema_impl<wire_type, sub_key>(val, sz_ptr, writer);
}
},
std::make_index_sequence<SIZE>{});
write_pb_unknown_fields(t, writer);
}
else if constexpr (is_sequence_container<T>::value) {
// TODO support std::array
// repeated values can't be omitted even if values are empty
using item_type = typename T::value_type;
if constexpr (is_lenprefix_v<item_type>) {
// non-packed
for (auto& item : t) {
to_pb_impl<key, false>(item, sz_ptr, writer);
}
}
else {
if (t.empty())
IGUANA_UNLIKELY { return; }
serialize_varint_u32<key>(writer);
serialize_varint(pb_value_size(t, sz_ptr), writer);
for (auto& item : t) {
encode_numeric_field<false, 0>(item, writer);
}
}
}
else if constexpr (is_map_container<T>::value) {
using first_type = typename T::key_type;
using second_type = typename T::mapped_type;
constexpr uint32_t key1 =
(1 << 3) | static_cast<uint32_t>(get_wire_type<first_type>());
constexpr auto key1_size = variant_uint32_size_constexpr(key1);
constexpr uint32_t key2 =
(2 << 3) | static_cast<uint32_t>(get_wire_type<second_type>());
constexpr auto key2_size = variant_uint32_size_constexpr(key2);
for (auto& [k, v] : t) {
serialize_varint_u32<key>(writer);
// k must be string or numeric
auto k_val_len = str_numeric_size<0, false>(k);
auto v_val_len = pb_value_size<false>(v, sz_ptr);
auto pair_len = key1_size + key2_size + k_val_len + v_val_len;
if constexpr (is_lenprefix_v<first_type>) {
pair_len += variant_uint32_size(k_val_len);
}
if constexpr (is_lenprefix_v<second_type>) {
pair_len += variant_uint32_size(v_val_len);
}
serialize_varint(pair_len, writer);
// map k and v can't be omitted even if values are empty
encode_pair_value<key1>(k, k_val_len, sz_ptr, writer);
encode_pair_value<key2>(v, v_val_len, sz_ptr, writer);
}
}
else if constexpr (optional_v<T>) {
if (!t.has_value()) {
return;
}
to_pb_impl<key, omit_default_val>(*t, sz_ptr, writer);
}
else if constexpr (std::is_same_v<T, std::string> ||
std::is_same_v<T, std::string_view>) {
if constexpr (omit_default_val) {
if (t.size() == 0)
IGUANA_UNLIKELY { return; }
}
serialize_varint_u32<key>(writer);
serialize_varint(t.size(), writer);
writer.write(t.data(), t.size());
}
else {
encode_numeric_field<key, omit_default_val>(t, writer);
}
}
#if defined(__clang__) || defined(_MSC_VER) || \
(defined(__GNUC__) && __GNUC__ > 8)
template <typename T>
IGUANA_INLINE constexpr std::string_view get_type_string() {
if constexpr (std::is_integral_v<T>) {
if constexpr (std::is_same_v<T, bool>) {
return "bool";
}
else if constexpr (sizeof(T) <= 4) {
if constexpr (std::is_unsigned_v<T>) {
return "uint32";
}
else {
return "int32";
}
}
else {
if constexpr (std::is_unsigned_v<T>) {
return "uint64";
}
else {
return "int64";
}
}
}
else if constexpr (std::is_same_v<T, iguana::sint32_t>) {
return "sint32";
}
else if constexpr (std::is_same_v<T, iguana::sint64_t>) {
return "sint64";
}
else if constexpr (std::is_same_v<T, iguana::fixed32_t>) {
return "fixed32";
}
else if constexpr (std::is_same_v<T, iguana::fixed64_t>) {
return "fixed64";
}
else if constexpr (std::is_same_v<T, iguana::sfixed32_t>) {
return "sfixed32";
}
else if constexpr (std::is_same_v<T, iguana::sfixed64_t>) {
return "sfixed64";
}
else if constexpr (std::is_same_v<T, std::string> ||
std::is_same_v<T, std::string_view>) {
return "string";
}
else if constexpr (std::is_floating_point_v<T>) {
return type_string<T>();
}
else {
constexpr auto str_type_name = type_string<T>();
constexpr size_t pos = str_type_name.rfind("::");
if constexpr (pos != std::string_view::npos) {
constexpr size_t pos = str_type_name.rfind("::") + 2;
if constexpr (detail::is_signed_varint_v<T> || detail::is_fixed_v<T>) {
return str_type_name.substr(pos, str_type_name.size() - pos - 2);
}
else {
return str_type_name.substr(pos);
}
}
else {
return str_type_name;
}
}
}
template <typename T, typename Stream>
IGUANA_INLINE void numeric_to_proto(Stream& out, std::string_view field_name,
uint32_t field_no) {
constexpr auto name = get_type_string<T>();
out.append(name).append(" ");
out.append(field_name)
.append(" = ")
.append(std::to_string(field_no))
.append(";\n");
}
template <size_t space_count = 2, typename Stream>
IGUANA_INLINE void build_proto_field(Stream& out, std::string_view str_type,
std::string_view field_name,
uint32_t field_no) {
for (size_t i = 0; i < space_count; i++) {
out.append(" ");
}
if (!str_type.empty()) {
out.append(str_type);
}
out.append(" ")
.append(field_name)
.append(" = ")
.append(std::to_string(field_no))
.append(";\n");
}
template <typename T, typename Map>
IGUANA_INLINE void build_sub_proto(Map& map, std::string_view str_type,
std::string& sub_str);
template <typename Type, typename Stream>
IGUANA_INLINE void to_proto_impl(
Stream& out, std::unordered_map<std::string_view, std::string>& map,
std::string_view field_name = "", uint32_t field_no = 0) {
std::string sub_str;
using T = std::remove_const_t<std::remove_reference_t<Type>>;
if constexpr (ylt_refletable_v<T> || is_custom_reflection_v<T>) {
constexpr auto name = type_string<T>();
out.append("message ").append(name).append(" {\n");
static T t;
static auto tuple = get_pb_members_tuple(t);
constexpr size_t SIZE = std::tuple_size_v<std::decay_t<decltype(tuple)>>;
for_each_n(
[&out, &sub_str, &map](auto i) mutable {
using field_type =
std::tuple_element_t<decltype(i)::value,
std::decay_t<decltype(tuple)>>;
auto value = std::get<decltype(i)::value>(tuple);
using U = typename field_type::value_type;
using WireU = typename field_type::wire_value_type;
using sub_type = typename field_type::sub_type;
if constexpr (field_type::bytes_schema) {
if constexpr (is_sequence_container<U>::value) {
build_proto_field(
out, "repeated bytes",
{value.field_name.data(), value.field_name.size()},
value.field_no);
}
else if constexpr (optional_v<U>) {
out.append(" optional");
build_proto_field(
out, "bytes",
{value.field_name.data(), value.field_name.size()},
value.field_no);
}
else {
build_proto_field(
out, "bytes ",
{value.field_name.data(), value.field_name.size()},
value.field_no);
}
}
else if constexpr (field_type::timestamp_schema) {
if constexpr (is_sequence_container<U>::value) {
build_proto_field(
out, "repeated google.protobuf.Timestamp",
{value.field_name.data(), value.field_name.size()},
value.field_no);
}
else {
build_proto_field(
out, "google.protobuf.Timestamp",
{value.field_name.data(), value.field_name.size()},
value.field_no);
}
}
else if constexpr (field_type::duration_schema) {
if constexpr (is_sequence_container<U>::value) {
build_proto_field(
out, "repeated google.protobuf.Duration",
{value.field_name.data(), value.field_name.size()},
value.field_no);
}
else {
build_proto_field(
out, "google.protobuf.Duration",
{value.field_name.data(), value.field_name.size()},
value.field_no);
}
}
else if constexpr (field_type::optional_schema) {
static_assert(optional_v<U>,
"pb_optional member must be std::optional<T>");
out.append(" optional");
to_proto_impl<typename WireU::value_type>(
out, map, {value.field_name.data(), value.field_name.size()},
value.field_no);
}
else if constexpr (ylt_refletable_v<U>) {
constexpr auto str_type = get_type_string<U>();
build_proto_field(
out, str_type,
{value.field_name.data(), value.field_name.size()},
value.field_no);
build_sub_proto<U>(map, str_type, sub_str);
}
else if constexpr (variant_v<U>) {
constexpr size_t var_size = std::variant_size_v<U>;
constexpr size_t first_case = pb_variant_first_case_index<U>();
constexpr auto offset =
get_variant_index<U, sub_type, var_size - 1>();
if (offset == first_case) {
out.append(" oneof ");
out.append(value.field_name.data(), value.field_name.size())
.append(" {\n");
}
if constexpr (!std::is_same_v<sub_type, std::monostate>) {
constexpr auto str_type = get_type_string<sub_type>();
std::string field_name = " one_of_";
field_name.append(str_type);
out.append(" ");
build_proto_field(out, str_type, field_name, value.field_no);
if constexpr (ylt_refletable_v<sub_type>) {
build_sub_proto<sub_type>(map, str_type, sub_str);
}
}
if (offset == var_size - 1) {
out.append(" }\n");
}
}
else {
to_proto_impl<WireU>(
out, map, {value.field_name.data(), value.field_name.size()},
value.field_no);
}
},
std::make_index_sequence<SIZE>{});
out.append("}\r\n\r\n");
}
else if constexpr (is_sequence_container<T>::value) {
out.append(" repeated");
using item_type = typename T::value_type;
if constexpr (is_lenprefix_v<item_type>) {
// non-packed
if constexpr (ylt_refletable_v<item_type>) {
constexpr auto str_type = get_type_string<item_type>();
build_proto_field(out, str_type, field_name, field_no);
build_sub_proto<item_type>(map, str_type, sub_str);
}
else {
to_proto_impl<item_type>(out, map, field_name, field_no);
}
}
else {
out.append(" ");
numeric_to_proto<item_type>(out, field_name, field_no);
}
}
else if constexpr (is_map_container<T>::value) {
out.append(" map<");
using first_type = typename T::key_type;
using second_type = typename T::mapped_type;
constexpr auto str_first = get_type_string<first_type>();
constexpr auto str_second = get_type_string<second_type>();
out.append(str_first).append(", ").append(str_second).append(">");
build_proto_field<1>(out, "", field_name, field_no);
if constexpr (ylt_refletable_v<second_type>) {
constexpr auto str_type = get_type_string<second_type>();
build_sub_proto<second_type>(map, str_type, sub_str);
}
}
else if constexpr (optional_v<T>) {
to_proto_impl<typename T::value_type>(
out, map, {field_name.data(), field_name.size()}, field_no);
}
else if constexpr (std::is_same_v<T, std::string> ||
std::is_same_v<T, std::string_view>) {
build_proto_field(out, "string ", field_name, field_no);
}
else if constexpr (enum_v<T>) {
constexpr auto str_type = get_type_string<T>();
static constexpr auto enum_to_str = get_enum_map<false, std::decay_t<T>>();
if constexpr (bool_v<decltype(enum_to_str)>) {
build_proto_field(out, "int32", field_name, field_no);
}
else {
static_assert(enum_to_str.size() > 0, "empty enum not allowed");
static_assert((int)(enum_to_str.begin()->first) == 0,
"the first enum value must be zero in proto3");
build_proto_field(out, str_type, field_name, field_no);
if (map.find(str_type) == map.end()) {
sub_str.append("enum ").append(str_type).append(" {\n");
for (auto& [k, field_name] : enum_to_str) {
std::string_view name{field_name.data(), field_name.size()};
size_t pos = name.rfind("::");
if (pos != std::string_view::npos) {
name = name.substr(pos + 2);
}
sub_str.append(" ")
.append(name)
.append(" = ")
.append(std::to_string(static_cast<std::underlying_type_t<T>>(k)))
.append(";\n");
}
sub_str.append("}\r\n\r\n");
map.emplace(str_type, std::move(sub_str));
}
}
}
else {
out.append(" ");
numeric_to_proto<Type>(out, field_name, field_no);
}
}
template <typename T, typename Map>
IGUANA_INLINE void build_sub_proto(Map& map, std::string_view str_type,
std::string& sub_str) {
if (map.find(str_type) == map.end()) {
to_proto_impl<T>(sub_str, map);
map.emplace(str_type, std::move(sub_str));
}
}
template <typename T>
constexpr bool proto_needs_timestamp_import();
template <typename T>
constexpr bool proto_needs_duration_import();
template <typename T>
constexpr bool proto_value_needs_timestamp_import();
template <typename T>
constexpr bool proto_value_needs_duration_import();
template <typename Variant, size_t... I>
constexpr bool proto_variant_needs_timestamp_import(std::index_sequence<I...>) {
return (proto_value_needs_timestamp_import<
std::variant_alternative_t<I, Variant>>() ||
...);
}
template <typename Variant, size_t... I>
constexpr bool proto_variant_needs_duration_import(std::index_sequence<I...>) {
return (proto_value_needs_duration_import<
std::variant_alternative_t<I, Variant>>() ||
...);
}
template <typename T>
constexpr bool proto_value_needs_timestamp_import() {
using U = std::remove_cvref_t<T>;
if constexpr (std::is_same_v<U, std::monostate> ||
pb_well_known_chrono_v<U>) {
return false;
}
else if constexpr (optional_v<U>) {
return proto_value_needs_timestamp_import<typename U::value_type>();
}
else if constexpr (is_sequence_container<U>::value) {
return proto_value_needs_timestamp_import<typename U::value_type>();
}
else if constexpr (is_map_container<U>::value) {
return proto_value_needs_timestamp_import<typename U::mapped_type>();
}
else if constexpr (variant_v<U>) {
return proto_variant_needs_timestamp_import<U>(
std::make_index_sequence<std::variant_size_v<U>>{});
}
else if constexpr (ylt_refletable_v<U> || is_custom_reflection_v<U>) {
return proto_needs_timestamp_import<U>();
}
else {
return false;
}
}
template <typename T>
constexpr bool proto_value_needs_duration_import() {
using U = std::remove_cvref_t<T>;
if constexpr (std::is_same_v<U, std::monostate> ||
pb_well_known_chrono_v<U>) {
return false;
}
else if constexpr (optional_v<U>) {
return proto_value_needs_duration_import<typename U::value_type>();
}
else if constexpr (is_sequence_container<U>::value) {
return proto_value_needs_duration_import<typename U::value_type>();
}
else if constexpr (is_map_container<U>::value) {
return proto_value_needs_duration_import<typename U::mapped_type>();
}
else if constexpr (variant_v<U>) {
return proto_variant_needs_duration_import<U>(
std::make_index_sequence<std::variant_size_v<U>>{});
}
else if constexpr (ylt_refletable_v<U> || is_custom_reflection_v<U>) {
return proto_needs_duration_import<U>();
}
else {
return false;
}
}
template <typename Tuple, size_t... I>
constexpr bool proto_tuple_needs_timestamp_import(std::index_sequence<I...>) {
return ((std::tuple_element_t<I, Tuple>::timestamp_schema ||
proto_value_needs_timestamp_import<
typename std::tuple_element_t<I, Tuple>::value_type>()) ||
...);
}
template <typename Tuple, size_t... I>
constexpr bool proto_tuple_needs_duration_import(std::index_sequence<I...>) {
return ((std::tuple_element_t<I, Tuple>::duration_schema ||
proto_value_needs_duration_import<
typename std::tuple_element_t<I, Tuple>::value_type>()) ||
...);
}
template <typename T>
constexpr bool proto_needs_timestamp_import() {
if constexpr (ylt_refletable_v<T> || is_custom_reflection_v<T>) {
using Tuple =
std::decay_t<decltype(get_pb_members_tuple(std::declval<T&>()))>;
return proto_tuple_needs_timestamp_import<Tuple>(
std::make_index_sequence<std::tuple_size_v<Tuple>>{});
}
else {
return false;
}
}
template <typename T>
constexpr bool proto_needs_duration_import() {
if constexpr (ylt_refletable_v<T> || is_custom_reflection_v<T>) {
using Tuple =
std::decay_t<decltype(get_pb_members_tuple(std::declval<T&>()))>;
return proto_tuple_needs_duration_import<Tuple>(
std::make_index_sequence<std::tuple_size_v<Tuple>>{});
}
else {
return false;
}
}
#endif
} // namespace detail
template <
typename T, typename Stream,
std::enable_if_t<ylt_refletable_v<T> || detail::is_custom_reflection_v<T>,
int> = 0>
IGUANA_INLINE void to_pb(T const& t, Stream& out) {
std::vector<uint32_t> size_arr;
auto byte_len = detail::pb_key_value_size<0>(t, size_arr);
auto sz_ptr = size_arr.empty() ? nullptr : &size_arr[0];
if constexpr (is_resizable_char_container_v<Stream>) {
detail::resize(out, byte_len);
memory_writer writer{out.data()};
detail::to_pb_impl<0>(t, sz_ptr, writer);
}
else if constexpr (char_writer<Stream>) {
detail::to_pb_impl<0>(t, sz_ptr, out);
}
else {
static_assert(!sizeof(Stream), "Invalid stream type");
}
}
#if defined(__clang__) || defined(_MSC_VER) || \
(defined(__GNUC__) && __GNUC__ > 8)
template <typename T, bool gen_header = true, typename Stream>
IGUANA_INLINE void to_proto(Stream& out, std::string_view ns = "") {
if (gen_header) {
constexpr std::string_view crlf = "\r\n\r\n";
out.append(R"(syntax = "proto3";)").append(crlf);
if constexpr (detail::proto_needs_timestamp_import<T>()) {
out.append(R"(import "google/protobuf/timestamp.proto";)").append(crlf);
}
if constexpr (detail::proto_needs_duration_import<T>()) {
out.append(R"(import "google/protobuf/duration.proto";)").append(crlf);
}
if (!ns.empty()) {
out.append("package ").append(ns).append(";").append(crlf);
}
out.append(R"(option optimize_for = SPEED;)").append(crlf);
out.append(R"(option cc_enable_arenas = true;)").append(crlf);
}
std::unordered_map<std::string_view, std::string> map;
detail::to_proto_impl<T>(out, map);
for (auto& [k, s] : map) {
out.append(s);
}
}
template <typename T, bool gen_header = true, typename Stream>
IGUANA_INLINE void to_proto_file(Stream& stream, std::string_view ns = "") {
if (!stream.is_open()) {
return;
}
std::string out;
to_proto<T, gen_header>(out, ns);
stream.write(out.data(), out.size());
}
#endif
template <typename T, typename Stream>
IGUANA_INLINE void to_pb_adl(iguana_adl_t* p, T const& t, Stream& out) {
to_pb(t, out);
}
} // namespace iguana
+144
View File
@@ -0,0 +1,144 @@
#pragma once
// https://github.com/stephenberry/glaze/blob/main/include/glaze/json/prettify.hpp
namespace iguana {
namespace detail {
enum class general_state : uint32_t {
NORMAL,
ESCAPED,
STRING,
BEFORE_ASTERISK,
COMMENT,
BEFORE_FSLASH
};
template <typename Out, typename NL>
inline void prettify_normal_state(const char c, Out &out, uint32_t &indent,
NL nl, general_state &state) noexcept {
switch (c) {
case ',':
out += c;
nl();
break;
case '[':
out += c;
++indent;
nl();
break;
case ']':
--indent;
nl();
out += c;
break;
case '{':
out += c;
++indent;
nl();
break;
case '}':
--indent;
nl();
out += c;
break;
case '\\':
out += c;
state = general_state::ESCAPED;
break;
case '\"':
out += c;
state = general_state::STRING;
break;
case '/':
out += " /";
state = general_state::BEFORE_ASTERISK;
break;
case ':':
out += ": ";
break;
case ' ':
case '\n':
case '\r':
case '\t':
break;
default:
out += c;
break;
}
}
inline void prettify_other_states(const char c, general_state &state) noexcept {
switch (state) {
case general_state::ESCAPED:
state = general_state::NORMAL;
break;
case general_state::STRING:
if (c == '"') {
state = general_state::NORMAL;
}
break;
case general_state::BEFORE_ASTERISK:
state = general_state::COMMENT;
break;
case general_state::COMMENT:
if (c == '*') {
state = general_state::BEFORE_FSLASH;
}
break;
case general_state::BEFORE_FSLASH:
if (c == '/') {
state = general_state::NORMAL;
}
else {
state = general_state::COMMENT;
}
break;
default:
break;
}
}
} // namespace detail
/// <summary>
/// pretty print a JSON string
/// </summary>
template <typename In, typename Out>
inline void prettify(const In &in, Out &out, const bool tabs = false,
const uint32_t indent_size = 3) noexcept {
out.reserve(in.size());
uint32_t indent{};
auto nl = [&]() {
out += "\n";
for (uint32_t i = 0; i < indent * (tabs ? 1 : indent_size); i++) {
out += tabs ? "\t" : " ";
}
};
using namespace detail;
general_state state{general_state::NORMAL};
for (auto c : in) {
if (state == general_state::NORMAL) {
prettify_normal_state(c, out, indent, nl, state);
continue;
}
else {
out += c;
prettify_other_states(c, state);
}
}
}
/// <summary>
/// allocating version of prettify
/// </summary>
template <typename In>
inline std::string prettify(const In &in, const bool tabs = false,
const uint32_t indent_size = 3) noexcept {
std::string out{};
prettify(in, out, tabs, indent_size);
return out;
}
} // namespace iguana

Some files were not shown because too many files have changed in this diff Show More