├── .github └── workflows │ ├── generate_documentations.yml │ ├── release_macos_arm64.yml │ ├── release_macos_arm64_freetier.yml │ ├── release_macos_x64.yml │ ├── release_macos_x64_freetier.yml │ ├── release_ubuntu_22_x64.yml │ ├── release_ubuntu_22_x64_freetier.yml │ ├── release_windows_x64.yml │ ├── release_windows_x64_freetier.yml │ ├── unit_test_macos_arm64.yml │ ├── unit_test_macos_x64.yml │ ├── unit_test_ubuntu_22_x64.yml │ ├── unit_test_ubuntu_24_x64.yml │ └── unit_test_windows_x64.yml ├── .gitignore ├── .gitmodules ├── LICENSE ├── README.md ├── assets ├── azimuth_elevation.svg ├── performance.svg ├── phi_theta.svg ├── radarsimcpp.svg ├── radarsimdoc.svg ├── radarsimnb.svg ├── radarsimpy.svg ├── radarsimx.svg └── yaw_pitch_roll.svg ├── batch_build_linux.sh ├── batch_build_linux_cuda.sh ├── batch_build_win.bat ├── batch_build_win_cuda.bat ├── build_instructions.md ├── build_linux.sh ├── build_macos.sh ├── build_win.bat ├── gen_docs ├── Makefile ├── _static │ ├── radarsimdoc.png │ ├── radarsimdoc.svg │ └── radarsimx_main.svg ├── api │ ├── index.rst │ ├── process.rst │ ├── radar.rst │ ├── sim.rst │ └── tools.rst ├── conf.py ├── index.rst ├── make.bat └── user_guide │ ├── build.rst │ ├── coordinate.rst │ ├── dependence.rst │ ├── examples.rst │ ├── features.rst │ ├── index.rst │ └── installation.rst ├── models ├── ball_1m.stl ├── cr.stl ├── dihedral.stl ├── half_ring.stl ├── pannel.stl ├── plate.stl ├── plate5x5.stl ├── surface.stl ├── surface_400x400.stl ├── surface_60x60.stl ├── turbine.stl └── vehicles │ ├── ford_raptor.stl │ ├── gasoline_tanker.stl │ ├── honda_cb400.stl │ ├── lamborgini_aventador.stl │ ├── scania_truck.stl │ └── tesla_model_s.stl ├── references ├── An Efficient and Robust Ray–Box Intersection Algorithm.pdf ├── Gordon_1975_Far-field approximations to the Kirchoff-Helmholtz representations of scattered.pdf ├── Guerrero_2018_Frequency-modulated continuous-wave radar in automotive applications.pdf ├── Hung_2000_Matrix-construction calibration method for antenna arrays.pdf ├── Hwang_2015_Radar cross section analysis using physical optics and its applications to.pdf ├── Karras_2012_Maximizing parallelism in the construction of BVHs, octrees, and k-d trees.pdf ├── Mahafza_2015_Chapter 4 radar detection.pdf ├── Reflection and Refraction of Plane EM Waves.pdf ├── Zhao_2012_Fast physical optics calculation for SAR imaging of complex scatterers.pdf ├── add_phase_noise.m └── exact_and_approximate_pd_formulas_in_frsp.pdf ├── requirements.txt ├── setup.py ├── src └── radarsimpy │ ├── __init__.py │ ├── includes │ ├── __init__.py │ ├── radarsimc.pxd │ ├── rsvector.pxd │ └── type_def.pxd │ ├── lib │ ├── __init__.py │ ├── cp_radarsimc.pxd │ └── cp_radarsimc.pyx │ ├── mesh_kit.py │ ├── processing.py │ ├── radar.py │ ├── receiver.py │ ├── simulator.pyx │ ├── simulator_lidar.pyx │ ├── simulator_radar.pyx │ ├── simulator_rcs.pyx │ ├── tools.py │ └── transmitter.py └── tests ├── __init__.py ├── test_config_radar.py ├── test_config_receiver.py ├── test_config_transmitter.py ├── test_module_sim_lidar.py ├── test_module_sim_radar_ideal.py ├── test_module_sim_radar_mesh.py ├── test_module_sim_rcs.py ├── test_module_tools.py ├── test_processing_cfar.py ├── test_processing_doa.py ├── test_system_arbitrary_waveform.py ├── test_system_cw_radar.py ├── test_system_fmcw_radar.py ├── test_system_interference.py ├── test_system_mimo_radar.py ├── test_system_phase_noise.py └── test_system_pulsed_radar.py /.github/workflows/generate_documentations.yml: -------------------------------------------------------------------------------- 1 | name: Generate Documentations 2 | 3 | on: 4 | push: 5 | tags: 6 | - "*" 7 | workflow_dispatch: 8 | 9 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 10 | permissions: 11 | contents: read 12 | pages: write 13 | id-token: write 14 | 15 | jobs: 16 | build: 17 | runs-on: ubuntu-24.04 18 | strategy: 19 | matrix: 20 | python-version: ["3.12"] 21 | c_compiler: [gcc-14] 22 | cxx_compiler: [g++-14] 23 | 24 | steps: 25 | - uses: actions/checkout@v4 26 | with: 27 | ssh-key: ${{secrets.RADARSIMC}} 28 | submodules: recursive 29 | - name: Set up Python ${{matrix.python-version}} 30 | uses: actions/setup-python@v5 31 | with: 32 | python-version: ${{matrix.python-version}} 33 | - name: Install dependencies 34 | run: | 35 | python -m pip install --upgrade pip 36 | pip install -r requirements.txt 37 | pip install Sphinx pydata-sphinx-theme 38 | - name: Build 39 | env: 40 | CC: ${{matrix.c_compiler}} 41 | CXX: ${{matrix.cxx_compiler}} 42 | run: | 43 | sudo chown -R $USER:$USER /home/runner/work/radarsimpy 44 | chmod +x build_linux.sh 45 | ./build_linux.sh --tier=standard --arch=cpu --test=off 46 | - name: Generate documentation 47 | run: | 48 | cd gen_docs && make html && cd .. 49 | - name: Archive documentation 50 | if: success() 51 | uses: actions/upload-artifact@v4 52 | with: 53 | name: docs 54 | path: ./gen_docs/_build/html 55 | - name: Setup Pages 56 | uses: actions/configure-pages@v4 57 | - name: Upload artifact 58 | uses: actions/upload-pages-artifact@v3 59 | with: 60 | # Upload entire repository 61 | path: "./gen_docs/_build/html" 62 | - name: Deploy to GitHub Pages 63 | id: deployment 64 | uses: actions/deploy-pages@v4 65 | -------------------------------------------------------------------------------- /.github/workflows/release_macos_arm64.yml: -------------------------------------------------------------------------------- 1 | name: Release on MacOS Apple Silicon 2 | 3 | on: 4 | push: 5 | tags: 6 | - "*" 7 | workflow_dispatch: 8 | 9 | jobs: 10 | build: 11 | runs-on: macos-15 12 | strategy: 13 | matrix: 14 | python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] 15 | c_compiler: [gcc-14] 16 | cxx_compiler: [g++-14] 17 | 18 | steps: 19 | - uses: actions/checkout@v4 20 | with: 21 | ssh-key: ${{secrets.RADARSIMC}} 22 | submodules: recursive 23 | - uses: maxim-lobanov/setup-xcode@v1.6.0 24 | with: 25 | # Version of Xcode to use 26 | xcode-version: '16.3' 27 | - name: Set up Python ${{matrix.python-version}} 28 | uses: actions/setup-python@v5 29 | with: 30 | python-version: ${{matrix.python-version}} 31 | - name: Install dependencies 32 | run: | 33 | python -m pip install --upgrade pip 34 | pip install -r requirements.txt 35 | - name: Build 36 | env: 37 | CC: ${{matrix.c_compiler}} 38 | CXX: ${{matrix.cxx_compiler}} 39 | run: | 40 | chmod +x build_macos.sh 41 | ./build_macos.sh --tier=standard --arch=cpu --test=off 42 | - name: Archive built module 43 | uses: actions/upload-artifact@v4 44 | if: success() 45 | with: 46 | name: radarsimpy_macos_arm_py${{matrix.python-version}} 47 | path: ./radarsimpy 48 | -------------------------------------------------------------------------------- /.github/workflows/release_macos_arm64_freetier.yml: -------------------------------------------------------------------------------- 1 | name: Release on MacOS Apple Silicon Free Tier 2 | 3 | on: 4 | push: 5 | tags: 6 | - "*" 7 | workflow_dispatch: 8 | 9 | jobs: 10 | build: 11 | runs-on: macos-15 12 | strategy: 13 | matrix: 14 | python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] 15 | c_compiler: [gcc-14] 16 | cxx_compiler: [g++-14] 17 | 18 | steps: 19 | - uses: actions/checkout@v4 20 | with: 21 | ssh-key: ${{secrets.RADARSIMC}} 22 | submodules: recursive 23 | - uses: maxim-lobanov/setup-xcode@v1.6.0 24 | with: 25 | # Version of Xcode to use 26 | xcode-version: '16.3' 27 | - name: Set up Python ${{matrix.python-version}} 28 | uses: actions/setup-python@v5 29 | with: 30 | python-version: ${{matrix.python-version}} 31 | - name: Install dependencies 32 | run: | 33 | python -m pip install --upgrade pip 34 | pip install -r requirements.txt 35 | - name: Build 36 | env: 37 | CC: ${{matrix.c_compiler}} 38 | CXX: ${{matrix.cxx_compiler}} 39 | run: | 40 | chmod +x build_macos.sh 41 | ./build_macos.sh --tier=free --arch=cpu --test=off 42 | - name: Archive built module 43 | uses: actions/upload-artifact@v4 44 | if: success() 45 | with: 46 | name: radarsimpy_macos_arm_py${{matrix.python-version}}_freetier 47 | path: ./radarsimpy 48 | -------------------------------------------------------------------------------- /.github/workflows/release_macos_x64.yml: -------------------------------------------------------------------------------- 1 | name: Release on MacOS x64 2 | 3 | on: 4 | push: 5 | tags: 6 | - "*" 7 | 8 | jobs: 9 | build: 10 | runs-on: macos-13 11 | strategy: 12 | matrix: 13 | python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] 14 | c_compiler: [gcc-14] 15 | cxx_compiler: [g++-14] 16 | 17 | steps: 18 | - uses: actions/checkout@v4 19 | with: 20 | ssh-key: ${{secrets.RADARSIMC}} 21 | submodules: recursive 22 | - uses: maxim-lobanov/setup-xcode@v1.6.0 23 | with: 24 | # Version of Xcode to use 25 | xcode-version: '15.2' 26 | - name: Set up Python ${{matrix.python-version}} 27 | uses: actions/setup-python@v5 28 | with: 29 | python-version: ${{matrix.python-version}} 30 | - name: Install dependencies 31 | run: | 32 | python -m pip install --upgrade pip 33 | pip install -r requirements.txt 34 | - name: Build 35 | env: 36 | CC: ${{matrix.c_compiler}} 37 | CXX: ${{matrix.cxx_compiler}} 38 | run: | 39 | chmod +x build_macos.sh 40 | ./build_macos.sh --tier=standard --arch=cpu --test=off 41 | - name: Archive built module 42 | uses: actions/upload-artifact@v4 43 | if: success() 44 | with: 45 | name: radarsimpy_macos_py${{matrix.python-version}} 46 | path: ./radarsimpy 47 | -------------------------------------------------------------------------------- /.github/workflows/release_macos_x64_freetier.yml: -------------------------------------------------------------------------------- 1 | name: Release on MacOS x64 Free Tier 2 | 3 | on: 4 | push: 5 | tags: 6 | - "*" 7 | 8 | jobs: 9 | build: 10 | runs-on: macos-13 11 | strategy: 12 | matrix: 13 | python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] 14 | c_compiler: [gcc-14] 15 | cxx_compiler: [g++-14] 16 | 17 | steps: 18 | - uses: actions/checkout@v4 19 | with: 20 | ssh-key: ${{secrets.RADARSIMC}} 21 | submodules: recursive 22 | - uses: maxim-lobanov/setup-xcode@v1.6.0 23 | with: 24 | # Version of Xcode to use 25 | xcode-version: '15.2' 26 | - name: Set up Python ${{matrix.python-version}} 27 | uses: actions/setup-python@v5 28 | with: 29 | python-version: ${{matrix.python-version}} 30 | - name: Install dependencies 31 | run: | 32 | python -m pip install --upgrade pip 33 | pip install -r requirements.txt 34 | - name: Build 35 | env: 36 | CC: ${{matrix.c_compiler}} 37 | CXX: ${{matrix.cxx_compiler}} 38 | run: | 39 | chmod +x build_macos.sh 40 | ./build_macos.sh --tier=free --arch=cpu --test=off 41 | - name: Archive built module 42 | uses: actions/upload-artifact@v4 43 | if: success() 44 | with: 45 | name: radarsimpy_macos_py${{matrix.python-version}}_freetier 46 | path: ./radarsimpy 47 | -------------------------------------------------------------------------------- /.github/workflows/release_ubuntu_22_x64.yml: -------------------------------------------------------------------------------- 1 | name: Release on Ubuntu 22.04 x64 2 | 3 | on: 4 | push: 5 | tags: 6 | - "*" 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-22.04 11 | strategy: 12 | matrix: 13 | python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] 14 | c_compiler: [gcc-11] 15 | cxx_compiler: [g++-11] 16 | 17 | steps: 18 | - uses: actions/checkout@v4 19 | with: 20 | ssh-key: ${{secrets.RADARSIMC}} 21 | submodules: recursive 22 | - name: Set up Python ${{matrix.python-version}} 23 | uses: actions/setup-python@v5 24 | with: 25 | python-version: ${{matrix.python-version}} 26 | - name: Install dependencies 27 | run: | 28 | python -m pip install --upgrade pip 29 | pip install -r requirements.txt 30 | - name: Build 31 | env: 32 | CC: ${{matrix.c_compiler}} 33 | CXX: ${{matrix.cxx_compiler}} 34 | run: | 35 | sudo chown -R $USER:$USER /home/runner/work/radarsimpy 36 | chmod +x build_linux.sh 37 | ./build_linux.sh --tier=standard --arch=cpu --test=off 38 | - name: Archive built module 39 | uses: actions/upload-artifact@v4 40 | if: success() 41 | with: 42 | name: radarsimpy_ubuntu_22_py${{matrix.python-version}} 43 | path: ./radarsimpy 44 | -------------------------------------------------------------------------------- /.github/workflows/release_ubuntu_22_x64_freetier.yml: -------------------------------------------------------------------------------- 1 | name: Release on Ubuntu 22.04 x64 Free Tier 2 | 3 | on: 4 | push: 5 | tags: 6 | - "*" 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-22.04 11 | strategy: 12 | matrix: 13 | python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] 14 | c_compiler: [gcc-11] 15 | cxx_compiler: [g++-11] 16 | 17 | steps: 18 | - uses: actions/checkout@v4 19 | with: 20 | ssh-key: ${{secrets.RADARSIMC}} 21 | submodules: recursive 22 | - name: Set up Python ${{matrix.python-version}} 23 | uses: actions/setup-python@v5 24 | with: 25 | python-version: ${{matrix.python-version}} 26 | - name: Install dependencies 27 | run: | 28 | python -m pip install --upgrade pip 29 | pip install -r requirements.txt 30 | - name: Build 31 | env: 32 | CC: ${{matrix.c_compiler}} 33 | CXX: ${{matrix.cxx_compiler}} 34 | run: | 35 | sudo chown -R $USER:$USER /home/runner/work/radarsimpy 36 | chmod +x build_linux.sh 37 | ./build_linux.sh --tier=free --arch=cpu --test=off 38 | - name: Archive built module 39 | uses: actions/upload-artifact@v4 40 | if: success() 41 | with: 42 | name: radarsimpy_ubuntu_22_py${{matrix.python-version}}_freetier 43 | path: ./radarsimpy 44 | -------------------------------------------------------------------------------- /.github/workflows/release_windows_x64.yml: -------------------------------------------------------------------------------- 1 | name: Release on Windows x64 2 | 3 | on: 4 | push: 5 | tags: 6 | - "*" 7 | 8 | jobs: 9 | build: 10 | runs-on: windows-latest 11 | strategy: 12 | matrix: 13 | python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] 14 | 15 | steps: 16 | # - name: Remove Windows OpenSSH and use the default ssh in git 17 | # run: Remove-WindowsCapability -Online -Name OpenSSH.Client 18 | - uses: actions/checkout@v4 19 | with: 20 | ssh-key: ${{secrets.RADARSIMC}} 21 | submodules: recursive 22 | - name: Set up Python ${{matrix.python-version}} 23 | uses: actions/setup-python@v5 24 | with: 25 | python-version: ${{matrix.python-version}} 26 | - name: Install dependencies 27 | run: | 28 | python -m pip install --upgrade pip 29 | pip install -r requirements.txt 30 | - name: Build 31 | run: | 32 | .\build_win.bat --tier=standard --arch=cpu --test=off 33 | - name: Archive built module 34 | uses: actions/upload-artifact@v4 35 | if: success() 36 | with: 37 | name: radarsimpy_win_py${{matrix.python-version}} 38 | path: .\radarsimpy 39 | -------------------------------------------------------------------------------- /.github/workflows/release_windows_x64_freetier.yml: -------------------------------------------------------------------------------- 1 | name: Release on Windows x64 Free Tier 2 | 3 | on: 4 | push: 5 | tags: 6 | - "*" 7 | 8 | jobs: 9 | build: 10 | runs-on: windows-latest 11 | strategy: 12 | matrix: 13 | python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] 14 | 15 | steps: 16 | # - name: Remove Windows OpenSSH and use the default ssh in git 17 | # run: Remove-WindowsCapability -Online -Name OpenSSH.Client 18 | - uses: actions/checkout@v4 19 | with: 20 | ssh-key: ${{secrets.RADARSIMC}} 21 | submodules: recursive 22 | - name: Set up Python ${{matrix.python-version}} 23 | uses: actions/setup-python@v5 24 | with: 25 | python-version: ${{matrix.python-version}} 26 | - name: Install dependencies 27 | run: | 28 | python -m pip install --upgrade pip 29 | pip install -r requirements.txt 30 | - name: Build 31 | run: | 32 | .\build_win.bat --tier=free --arch=cpu --test=off 33 | - name: Archive built module 34 | uses: actions/upload-artifact@v4 35 | if: success() 36 | with: 37 | name: radarsimpy_win_py${{matrix.python-version}}_freetier 38 | path: .\radarsimpy 39 | -------------------------------------------------------------------------------- /.github/workflows/unit_test_macos_arm64.yml: -------------------------------------------------------------------------------- 1 | name: MacOS Apple Silicon 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | pull_request: 7 | branches: [master] 8 | 9 | jobs: 10 | build: 11 | runs-on: macos-15 12 | strategy: 13 | matrix: 14 | python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] 15 | c_compiler: [gcc-14] 16 | cxx_compiler: [g++-14] 17 | 18 | steps: 19 | - uses: actions/checkout@v4 20 | with: 21 | ssh-key: ${{secrets.RADARSIMC}} 22 | submodules: recursive 23 | - uses: maxim-lobanov/setup-xcode@v1.6.0 24 | with: 25 | # Version of Xcode to use 26 | xcode-version: '16.3' 27 | - name: Set up Python ${{matrix.python-version}} 28 | uses: actions/setup-python@v5 29 | with: 30 | python-version: ${{matrix.python-version}} 31 | - name: Install dependencies 32 | run: | 33 | python -m pip install --upgrade pip 34 | pip install -r requirements.txt 35 | - name: Build & Test 36 | env: 37 | CC: ${{matrix.c_compiler}} 38 | CXX: ${{matrix.cxx_compiler}} 39 | run: | 40 | chmod +x build_macos.sh 41 | ./build_macos.sh --tier=standard --arch=cpu --test=on 42 | -------------------------------------------------------------------------------- /.github/workflows/unit_test_macos_x64.yml: -------------------------------------------------------------------------------- 1 | name: MacOS x64 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | pull_request: 7 | branches: [master] 8 | 9 | jobs: 10 | build: 11 | runs-on: macos-13 12 | strategy: 13 | matrix: 14 | python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] 15 | c_compiler: [gcc-14] 16 | cxx_compiler: [g++-14] 17 | 18 | steps: 19 | - uses: actions/checkout@v4 20 | with: 21 | ssh-key: ${{secrets.RADARSIMC}} 22 | submodules: recursive 23 | - uses: maxim-lobanov/setup-xcode@v1.6.0 24 | with: 25 | # Version of Xcode to use 26 | xcode-version: '15.2' 27 | - name: Set up Python ${{matrix.python-version}} 28 | uses: actions/setup-python@v5 29 | with: 30 | python-version: ${{matrix.python-version}} 31 | - name: Install dependencies 32 | run: | 33 | python -m pip install --upgrade pip 34 | pip install -r requirements.txt 35 | - name: Build & Test 36 | env: 37 | CC: ${{matrix.c_compiler}} 38 | CXX: ${{matrix.cxx_compiler}} 39 | run: | 40 | chmod +x build_macos.sh 41 | ./build_macos.sh --tier=standard --arch=cpu --test=on 42 | -------------------------------------------------------------------------------- /.github/workflows/unit_test_ubuntu_22_x64.yml: -------------------------------------------------------------------------------- 1 | name: Ubuntu 22.04 x64 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | pull_request: 7 | branches: [master] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-22.04 12 | strategy: 13 | matrix: 14 | python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] 15 | c_compiler: [gcc-11] 16 | cxx_compiler: [g++-11] 17 | 18 | steps: 19 | - uses: actions/checkout@v4 20 | with: 21 | ssh-key: ${{secrets.RADARSIMC}} 22 | submodules: recursive 23 | - name: Set up Python ${{matrix.python-version}} 24 | uses: actions/setup-python@v5 25 | with: 26 | python-version: ${{matrix.python-version}} 27 | - name: Install dependencies 28 | run: | 29 | python -m pip install --upgrade pip 30 | pip install -r requirements.txt 31 | - name: Build & Test 32 | env: 33 | CC: ${{matrix.c_compiler}} 34 | CXX: ${{matrix.cxx_compiler}} 35 | run: | 36 | sudo chown -R $USER:$USER /home/runner/work/radarsimpy 37 | chmod +x build_linux.sh 38 | ./build_linux.sh --tier=standard --arch=cpu --test=on 39 | -------------------------------------------------------------------------------- /.github/workflows/unit_test_ubuntu_24_x64.yml: -------------------------------------------------------------------------------- 1 | name: Ubuntu 24.04 x64 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | pull_request: 7 | branches: [master] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-24.04 12 | strategy: 13 | matrix: 14 | python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] 15 | c_compiler: [gcc-13] 16 | cxx_compiler: [g++-13] 17 | 18 | steps: 19 | - uses: actions/checkout@v4 20 | with: 21 | ssh-key: ${{secrets.RADARSIMC}} 22 | submodules: recursive 23 | - name: Set up Python ${{matrix.python-version}} 24 | uses: actions/setup-python@v5 25 | with: 26 | python-version: ${{matrix.python-version}} 27 | - name: Install dependencies 28 | run: | 29 | python -m pip install --upgrade pip 30 | pip install -r requirements.txt 31 | - name: Build & Test 32 | env: 33 | CC: ${{matrix.c_compiler}} 34 | CXX: ${{matrix.cxx_compiler}} 35 | run: | 36 | sudo chown -R $USER:$USER /home/runner/work/radarsimpy 37 | chmod +x build_linux.sh 38 | ./build_linux.sh --tier=standard --arch=cpu --test=on 39 | -------------------------------------------------------------------------------- /.github/workflows/unit_test_windows_x64.yml: -------------------------------------------------------------------------------- 1 | name: Windows x64 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | pull_request: 7 | branches: [master] 8 | 9 | jobs: 10 | build: 11 | runs-on: windows-latest 12 | strategy: 13 | matrix: 14 | python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] 15 | 16 | steps: 17 | # - name: Remove Windows OpenSSH and use the default ssh in git 18 | # run: Remove-WindowsCapability -Online -Name OpenSSH.Client 19 | - uses: actions/checkout@v4 20 | with: 21 | ssh-key: ${{secrets.RADARSIMC}} 22 | submodules: recursive 23 | - name: Set up Python ${{matrix.python-version}} 24 | uses: actions/setup-python@v5 25 | with: 26 | python-version: ${{matrix.python-version}} 27 | - name: Install dependencies 28 | run: | 29 | python -m pip install --upgrade pip 30 | pip install -r requirements.txt 31 | - name: Build & Test 32 | run: | 33 | .\build_win.bat --tier=standard --arch=cpu --test=on 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib64/ 18 | parts/ 19 | sdist/ 20 | var/ 21 | wheels/ 22 | *.egg-info/ 23 | .installed.cfg 24 | *.egg 25 | MANIFEST 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *.cover 46 | .hypothesis/ 47 | .pytest_cache/ 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | local_settings.py 56 | db.sqlite3 57 | 58 | # Flask stuff: 59 | instance/ 60 | .webassets-cache 61 | 62 | # Scrapy stuff: 63 | .scrapy 64 | 65 | # Sphinx documentation 66 | docs/_build/ 67 | 68 | # PyBuilder 69 | target/ 70 | 71 | # Jupyter Notebook 72 | .ipynb_checkpoints 73 | 74 | # pyenv 75 | .python-version 76 | 77 | # celery beat schedule file 78 | celerybeat-schedule 79 | 80 | # SageMath parsed files 81 | *.sage.py 82 | 83 | # Environments 84 | .env 85 | .venv 86 | env/ 87 | venv/ 88 | ENV/ 89 | env.bak/ 90 | venv.bak/ 91 | 92 | # Spyder project settings 93 | .spyderproject 94 | .spyproject 95 | 96 | # Rope project settings 97 | .ropeproject 98 | 99 | # mkdocs documentation 100 | /site 101 | 102 | # mypy 103 | .mypy_cache/ 104 | 105 | \.idea/ 106 | 107 | \.vscode/ 108 | \.VSCodeCounter/ 109 | 110 | release/ 111 | radarsimpy/ 112 | !src/radarsimpy/ 113 | 114 | src/radarsimpy/*.html 115 | src/radarsimpy/sbr/*.html 116 | 117 | CMakeLists.txt.user 118 | 119 | src/build-*/ 120 | 121 | src/radarsim_main/.vs/ 122 | 123 | src/radarsimcpp/.vs/ 124 | 125 | src/radarsimcpp/CMakeSettings.json 126 | 127 | src/radarsimc_entry/.vs/ 128 | 129 | tests/radarsimpy 130 | 131 | src/radarsimcpp/build* 132 | 133 | src/radarsimcpp/entry/.vs/ 134 | 135 | gen_docs/_build/ 136 | 137 | *.dll 138 | 139 | debug/* 140 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "src/radarsimcpp"] 2 | path = src/radarsimcpp 3 | url = git@github.com:rookiepeng/radarsimcpp.git 4 | -------------------------------------------------------------------------------- /assets/azimuth_elevation.svg: -------------------------------------------------------------------------------- 1 | 2 | 92 | 93 | 94 | 95 | 99 | 102 | +azimuth +elevation0 azimuth0 elevation 228 | -------------------------------------------------------------------------------- /assets/radarsimx.svg: -------------------------------------------------------------------------------- 1 | 2 | 20 | 22 | 55 | 57 | 58 | 60 | image/svg+xml 61 | 63 | 64 | 65 | 66 | 71 | 79 | 82 | 95 | 108 | 113 | 126 | 139 | 152 | 165 | 178 | 191 | 204 | 217 | 223 | 224 | 225 | 226 | -------------------------------------------------------------------------------- /batch_build_linux.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Display banner and copyright information 4 | echo "Automatic build script of radarsimcpp/radarsimpy for Linux" 5 | echo "" 6 | echo "----------" 7 | echo "RadarSimPy - A Radar Simulator Built with Python" 8 | echo "Copyright (C) 2018 - PRESENT radarsimx.com" 9 | echo "E-mail: info@radarsimx.com" 10 | echo "Website: https://radarsimx.com" 11 | echo "" 12 | echo "██████╗ █████╗ ██████╗ █████╗ ██████╗ ███████╗██╗███╗ ███╗██╗ ██╗" 13 | echo "██╔══██╗██╔══██╗██╔══██╗██╔══██╗██╔══██╗██╔════╝██║████╗ ████║╚██╗██╔╝" 14 | echo "██████╔╝███████║██║ ██║███████║██████╔╝███████╗██║██╔████╔██║ ╚███╔╝ " 15 | echo "██╔══██╗██╔══██║██║ ██║██╔══██║██╔══██╗╚════██║██║██║╚██╔╝██║ ██╔██╗ " 16 | echo "██║ ██║██║ ██║██████╔╝██║ ██║██║ ██║███████║██║██║ ╚═╝ ██║██╔╝ ██╗" 17 | echo "╚═╝ ╚═╝╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝╚═╝ ╚═╝╚═╝ ╚═╝" 18 | 19 | # Store current working directory for later reference 20 | workpath=$(pwd) 21 | 22 | # Clean previous build artifacts and directories 23 | echo "## Clean old build files ##" 24 | rm -rf ./src/radarsimcpp/build 25 | rm -rf ./radarsimpy 26 | 27 | # Build CPU-only C++ library 28 | echo "## Building libradarsimcpp.so with CPU ##" 29 | mkdir ./src/radarsimcpp/build 30 | cd ./src/radarsimcpp/build 31 | 32 | # Configure CMake for CPU-only build 33 | cmake -DCMAKE_BUILD_TYPE=Release -DGTEST=ON .. 34 | cmake --build . 35 | 36 | # Build Python extensions for multiple Python versions (Free Tier) 37 | echo "## Building radarsimpy with Cython ##" 38 | cd $workpath 39 | # Build CPU-only extensions for Python 3.9-3.13 40 | conda run -n py313 python setup.py build_ext -b ./ --tier free --arch cpu 41 | conda run -n py312 python setup.py build_ext -b ./ --tier free --arch cpu 42 | conda run -n py311 python setup.py build_ext -b ./ --tier free --arch cpu 43 | conda run -n py310 python setup.py build_ext -b ./ --tier free --arch cpu 44 | conda run -n py39 python setup.py build_ext -b ./ --tier free --arch cpu 45 | 46 | # Copy built libraries and Python files 47 | echo "## Copying lib files to ./radarsimpy ##" 48 | cp ./src/radarsimpy/*.py ./radarsimpy 49 | cp ./src/radarsimpy/lib/__init__.py ./radarsimpy/lib 50 | cp ./src/radarsimcpp/build/*.so ./radarsimpy 51 | 52 | # Clean intermediate build files 53 | echo "## Cleaning radarsimpy builds ##" 54 | rm -rf build 55 | # Remove generated C/C++/HTML files 56 | rm -f ./src/radarsimpy/*.c 57 | rm -f ./src/radarsimpy/*.cpp 58 | rm -f ./src/radarsimpy/*.html 59 | rm -f ./src/radarsimpy/raytracing/*.c 60 | rm -f ./src/radarsimpy/raytracing/*.cpp 61 | rm -f ./src/radarsimpy/raytracing/*.html 62 | rm -f ./src/radarsimpy/lib/*.cpp 63 | rm -f ./src/radarsimpy/lib/*.html 64 | rm -f ./src/*.cpp 65 | rm -f ./src/*.html 66 | 67 | # Package Free Tier CPU release 68 | echo "## Copying lib files to freetier release folder ##" 69 | rm -rf ./trial/Linux_x86_64_CPU 70 | mkdir ./trial/Linux_x86_64_CPU 71 | mkdir ./trial/Linux_x86_64_CPU/radarsimpy 72 | cp -rf ./radarsimpy/* ./trial/Linux_x86_64_CPU/radarsimpy 73 | 74 | # Clean and rebuild for Standard Tier 75 | rm -rf ./radarsimpy 76 | # Build CPU-only extensions for Standard Tier 77 | conda run -n py313 python setup.py build_ext -b ./ --tier standard --arch cpu 78 | conda run -n py312 python setup.py build_ext -b ./ --tier standard --arch cpu 79 | conda run -n py311 python setup.py build_ext -b ./ --tier standard --arch cpu 80 | conda run -n py310 python setup.py build_ext -b ./ --tier standard --arch cpu 81 | conda run -n py39 python setup.py build_ext -b ./ --tier standard --arch cpu 82 | 83 | # Copy files for Standard Tier 84 | echo "## Copying lib files to ./radarsimpy ##" 85 | cp ./src/radarsimpy/*.py ./radarsimpy 86 | cp ./src/radarsimpy/lib/__init__.py ./radarsimpy/lib 87 | cp ./src/radarsimcpp/build/*.so ./radarsimpy 88 | 89 | # Final cleanup of build artifacts 90 | echo "## Cleaning radarsimpy builds ##" 91 | rm -rf build 92 | rm -f ./src/radarsimpy/*.c 93 | rm -f ./src/radarsimpy/*.cpp 94 | rm -f ./src/radarsimpy/*.html 95 | rm -f ./src/radarsimpy/raytracing/*.c 96 | rm -f ./src/radarsimpy/raytracing/*.cpp 97 | rm -f ./src/radarsimpy/raytracing/*.html 98 | rm -f ./src/radarsimpy/lib/*.cpp 99 | rm -f ./src/radarsimpy/lib/*.html 100 | rm -f ./src/*.cpp 101 | rm -f ./src/*.html 102 | 103 | # Package Standard Tier CPU release 104 | echo "## Copying lib files to standard release folder ##" 105 | rm -rf ./standard/Linux_x86_64_CPU 106 | mkdir ./standard/Linux_x86_64_CPU 107 | mkdir ./standard/Linux_x86_64_CPU/radarsimpy 108 | cp -rf ./radarsimpy/* ./standard/Linux_x86_64_CPU/radarsimpy 109 | 110 | # Run unit tests 111 | echo "## Build completed ##" 112 | echo "## Run Google test ##" 113 | ./src/radarsimcpp/build/radarsimcpp_test 114 | -------------------------------------------------------------------------------- /batch_build_linux_cuda.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Display banner and copyright information 4 | echo "Automatic build script of radarsimcpp/radarsimpy for Linux" 5 | echo "" 6 | echo "----------" 7 | echo "RadarSimPy - A Radar Simulator Built with Python" 8 | echo "Copyright (C) 2018 - PRESENT radarsimx.com" 9 | echo "E-mail: info@radarsimx.com" 10 | echo "Website: https://radarsimx.com" 11 | echo "" 12 | echo "██████╗ █████╗ ██████╗ █████╗ ██████╗ ███████╗██╗███╗ ███╗██╗ ██╗" 13 | echo "██╔══██╗██╔══██╗██╔══██╗██╔══██╗██╔══██╗██╔════╝██║████╗ ████║╚██╗██╔╝" 14 | echo "██████╔╝███████║██║ ██║███████║██████╔╝███████╗██║██╔████╔██║ ╚███╔╝ " 15 | echo "██╔══██╗██╔══██║██║ ██║██╔══██║██╔══██╗╚════██║██║██║╚██╔╝██║ ██╔██╗ " 16 | echo "██║ ██║██║ ██║██████╔╝██║ ██║██║ ██║███████║██║██║ ╚═╝ ██║██╔╝ ██╗" 17 | echo "╚═╝ ╚═╝╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝╚═╝ ╚═╝╚═╝ ╚═╝" 18 | 19 | # Store current working directory for later reference 20 | workpath=$(pwd) 21 | 22 | # Clean previous build artifacts and directories 23 | echo "## Clean old build files ##" 24 | rm -rf ./src/radarsimcpp/build 25 | rm -rf ./radarsimpy 26 | 27 | # Build CUDA-enabled C++ library 28 | echo "## Building libradarsimcpp.so with GPU ##" 29 | mkdir ./src/radarsimcpp/build 30 | cd ./src/radarsimcpp/build 31 | 32 | # Configure CMake for GPU build with CUDA support 33 | cmake -DCMAKE_BUILD_TYPE=Release -DGPU_BUILD=ON -DGTEST=ON .. 34 | cmake --build . 35 | 36 | # Build Python extensions for multiple Python versions (Free Tier) 37 | echo "## Building radarsimpy with Cython ##" 38 | cd $workpath 39 | # Build GPU-enabled extensions for Python 3.9-3.13 40 | conda run -n py313 python setup.py build_ext -b ./ --tier free --arch gpu 41 | conda run -n py312 python setup.py build_ext -b ./ --tier free --arch gpu 42 | conda run -n py311 python setup.py build_ext -b ./ --tier free --arch gpu 43 | conda run -n py310 python setup.py build_ext -b ./ --tier free --arch gpu 44 | conda run -n py39 python setup.py build_ext -b ./ --tier free --arch gpu 45 | 46 | # Copy built libraries and Python files 47 | echo "## Copying lib files to ./radarsimpy ##" 48 | cp ./src/radarsimpy/*.py ./radarsimpy 49 | cp ./src/radarsimpy/lib/__init__.py ./radarsimpy/lib 50 | cp ./src/radarsimcpp/build/*.so ./radarsimpy 51 | 52 | # Clean intermediate build files 53 | echo "## Cleaning radarsimpy builds ##" 54 | rm -rf build 55 | # Remove generated C/C++/HTML files 56 | rm -f ./src/radarsimpy/*.c 57 | rm -f ./src/radarsimpy/*.cpp 58 | rm -f ./src/radarsimpy/*.html 59 | rm -f ./src/radarsimpy/raytracing/*.c 60 | rm -f ./src/radarsimpy/raytracing/*.cpp 61 | rm -f ./src/radarsimpy/raytracing/*.html 62 | rm -f ./src/radarsimpy/lib/*.cpp 63 | rm -f ./src/radarsimpy/lib/*.html 64 | rm -f ./src/*.cpp 65 | rm -f ./src/*.html 66 | 67 | # Package Free Tier GPU release 68 | echo "## Copying lib files to freetier release folder ##" 69 | rm -rf ./trial/Linux_x86_64_GPU 70 | mkdir ./trial/Linux_x86_64_GPU 71 | mkdir ./trial/Linux_x86_64_GPU/radarsimpy 72 | cp -rf ./radarsimpy/* ./trial/Linux_x86_64_GPU/radarsimpy 73 | 74 | # Clean and rebuild for Standard Tier 75 | rm -rf ./radarsimpy 76 | # Build GPU-enabled extensions for Standard Tier 77 | conda run -n py313 python setup.py build_ext -b ./ --tier standard --arch gpu 78 | conda run -n py312 python setup.py build_ext -b ./ --tier standard --arch gpu 79 | conda run -n py311 python setup.py build_ext -b ./ --tier standard --arch gpu 80 | conda run -n py310 python setup.py build_ext -b ./ --tier standard --arch gpu 81 | conda run -n py39 python setup.py build_ext -b ./ --tier standard --arch gpu 82 | 83 | # Copy files for Standard Tier 84 | echo "## Copying lib files to ./radarsimpy ##" 85 | cp ./src/radarsimpy/*.py ./radarsimpy 86 | cp ./src/radarsimpy/lib/__init__.py ./radarsimpy/lib 87 | cp ./src/radarsimcpp/build/*.so ./radarsimpy 88 | 89 | # Final cleanup of build artifacts 90 | echo "## Cleaning radarsimpy builds ##" 91 | rm -rf build 92 | rm -f ./src/radarsimpy/*.c 93 | rm -f ./src/radarsimpy/*.cpp 94 | rm -f ./src/radarsimpy/*.html 95 | rm -f ./src/radarsimpy/raytracing/*.c 96 | rm -f ./src/radarsimpy/raytracing/*.cpp 97 | rm -f ./src/radarsimpy/raytracing/*.html 98 | rm -f ./src/radarsimpy/lib/*.cpp 99 | rm -f ./src/radarsimpy/lib/*.html 100 | rm -f ./src/*.cpp 101 | rm -f ./src/*.html 102 | 103 | # Package Standard Tier GPU release 104 | echo "## Copying lib files to standard release folder ##" 105 | rm -rf ./standard/Linux_x86_64_GPU 106 | mkdir ./standard/Linux_x86_64_GPU 107 | mkdir ./standard/Linux_x86_64_GPU/radarsimpy 108 | cp -rf ./radarsimpy/* ./standard/Linux_x86_64_GPU/radarsimpy 109 | 110 | # Run unit tests 111 | echo "## Build completed ##" 112 | echo "## Run Google test ##" 113 | ./src/radarsimcpp/build/radarsimcpp_test 114 | -------------------------------------------------------------------------------- /batch_build_win.bat: -------------------------------------------------------------------------------- 1 | REM Display header and copyright information 2 | @ECHO OFF 3 | 4 | ECHO Automatic build script of radarsimcpp/radarsimpy for Windows 5 | ECHO: 6 | ECHO ---------- 7 | ECHO RadarSimPy - A Radar Simulator Built with Python 8 | ECHO Copyright (C) 2018 - PRESENT radarsimx.com 9 | ECHO E-mail: info@radarsimx.com 10 | ECHO Website: https://radarsimx.com 11 | ECHO: 12 | ECHO ###### ##### # # 13 | ECHO # # ## ##### ## ##### # # # # # # # 14 | ECHO # # # # # # # # # # # # ## ## # # 15 | ECHO ###### # # # # # # # # ##### # # ## # # 16 | ECHO # # ###### # # ###### ##### # # # # # # 17 | ECHO # # # # # # # # # # # # # # # # # 18 | ECHO # # # # ##### # # # # ##### # # # # # 19 | ECHO: 20 | 21 | REM Store current directory path 22 | SET pwd=%cd% 23 | 24 | REM Clean up previous build artifacts 25 | ECHO clean old build files 26 | RMDIR /Q/S .\src\radarsimcpp\build 27 | 28 | ECHO clean old radarsimpy module 29 | RMDIR /Q/S .\radarsimpy 30 | 31 | REM Create and navigate to build directory 32 | MD ".\src\radarsimcpp\build" 33 | CD ".\src\radarsimcpp\build" 34 | 35 | REM Build C++ library with CMake 36 | ECHO ## Building radarsimcpp.dll with MSVC ## 37 | cmake -DGTEST=ON .. 38 | cmake --build . --config Release 39 | 40 | REM Build Python extensions for multiple Python versions (Free Tier) 41 | ECHO ## Building radarsimpy with Cython ## 42 | CD %pwd% 43 | REM Build for Python 3.9-3.13 with CPU support 44 | conda.exe run -n py313 python setup.py build_ext -b ./ --tier free --arch cpu 45 | conda.exe run -n py312 python setup.py build_ext -b ./ --tier free --arch cpu 46 | conda.exe run -n py311 python setup.py build_ext -b ./ --tier free --arch cpu 47 | conda.exe run -n py310 python setup.py build_ext -b ./ --tier free --arch cpu 48 | conda.exe run -n py39 python setup.py build_ext -b ./ --tier free --arch cpu 49 | 50 | REM Copy built files to radarsimpy directory 51 | ECHO ## Copying dll files to ./radarsimpy ## 52 | XCOPY ".\src\radarsimcpp\build\Release\radarsimcpp.dll" ".\radarsimpy\" 53 | XCOPY ".\src\radarsimpy\*.py" ".\radarsimpy\" 54 | XCOPY ".\src\radarsimpy\lib\__init__.py" ".\radarsimpy\lib\" 55 | 56 | REM Clean up intermediate build files 57 | ECHO ## Cleaning radarsimpy builds ## 58 | RMDIR build /s /q 59 | 60 | DEL ".\src\radarsimpy\*.c" 61 | DEL ".\src\radarsimpy\*.cpp" 62 | DEL ".\src\radarsimpy\*.html" 63 | DEL ".\src\radarsimpy\raytracing\*.c" 64 | DEL ".\src\radarsimpy\raytracing\*.cpp" 65 | DEL ".\src\radarsimpy\raytracing\*.html" 66 | DEL ".\src\radarsimpy\lib\*.cpp" 67 | DEL ".\src\radarsimpy\lib\*.html" 68 | DEL ".\src\*.cpp" 69 | DEL ".\src\*.html" 70 | 71 | REM Create FreeTier distribution 72 | ECHO ## Copying lib files to freetier release folder ## 73 | RMDIR /Q/S .\trial\Windows_x86_64_CPU 74 | XCOPY /E /I .\radarsimpy .\trial\Windows_x86_64_CPU\radarsimpy 75 | 76 | RMDIR /Q/S .\radarsimpy 77 | 78 | REM Build Standard Tier version 79 | REM Build Python extensions for multiple Python versions (Standard Tier) 80 | conda.exe run -n py313 python setup.py build_ext -b ./ --tier standard --arch cpu 81 | conda.exe run -n py312 python setup.py build_ext -b ./ --tier standard --arch cpu 82 | conda.exe run -n py311 python setup.py build_ext -b ./ --tier standard --arch cpu 83 | conda.exe run -n py310 python setup.py build_ext -b ./ --tier standard --arch cpu 84 | conda.exe run -n py39 python setup.py build_ext -b ./ --tier standard --arch cpu 85 | 86 | REM Copy built files to radarsimpy directory 87 | ECHO ## Copying dll files to ./radarsimpy ## 88 | XCOPY ".\src\radarsimcpp\build\Release\radarsimcpp.dll" ".\radarsimpy\" 89 | XCOPY ".\src\radarsimpy\*.py" ".\radarsimpy\" 90 | XCOPY ".\src\radarsimpy\lib\__init__.py" ".\radarsimpy\lib\" 91 | 92 | REM Clean up intermediate build files 93 | ECHO ## Cleaning radarsimpy builds ## 94 | RMDIR build /s /q 95 | 96 | DEL ".\src\radarsimpy\*.c" 97 | DEL ".\src\radarsimpy\*.cpp" 98 | DEL ".\src\radarsimpy\*.html" 99 | DEL ".\src\radarsimpy\raytracing\*.c" 100 | DEL ".\src\radarsimpy\raytracing\*.cpp" 101 | DEL ".\src\radarsimpy\raytracing\*.html" 102 | DEL ".\src\radarsimpy\lib\*.cpp" 103 | DEL ".\src\radarsimpy\lib\*.html" 104 | DEL ".\src\*.cpp" 105 | DEL ".\src\*.html" 106 | 107 | REM Create Standard Tier distribution 108 | ECHO ## Copying lib files to standard release folder ## 109 | RMDIR /Q/S .\standard\Windows_x86_64_CPU 110 | XCOPY /E /I .\radarsimpy .\standard\Windows_x86_64_CPU\radarsimpy 111 | 112 | REM Build completed 113 | ECHO ## Build completed ## 114 | 115 | REM Run unit tests 116 | ECHO ## Run Google test ## 117 | .\src\radarsimcpp\build\Release\radarsimcpp_test.exe 118 | -------------------------------------------------------------------------------- /batch_build_win_cuda.bat: -------------------------------------------------------------------------------- 1 | REM Display header and copyright information 2 | @ECHO OFF 3 | 4 | ECHO Automatic build script of radarsimcpp/radarsimpy for Windows 5 | ECHO: 6 | ECHO ---------- 7 | ECHO RadarSimPy - A Radar Simulator Built with Python 8 | ECHO Copyright (C) 2018 - PRESENT radarsimx.com 9 | ECHO E-mail: info@radarsimx.com 10 | ECHO Website: https://radarsimx.com 11 | ECHO: 12 | ECHO ###### ##### # # 13 | ECHO # # ## ##### ## ##### # # # # # # # 14 | ECHO # # # # # # # # # # # # ## ## # # 15 | ECHO ###### # # # # # # # # ##### # # ## # # 16 | ECHO # # ###### # # ###### ##### # # # # # # 17 | ECHO # # # # # # # # # # # # # # # # # 18 | ECHO # # # # ##### # # # # ##### # # # # # 19 | ECHO: 20 | 21 | REM Store current directory path 22 | SET pwd=%cd% 23 | 24 | REM Clean up previous build artifacts 25 | ECHO clean old build files 26 | RMDIR /Q/S .\src\radarsimcpp\build 27 | 28 | ECHO clean old radarsimpy module 29 | RMDIR /Q/S .\radarsimpy 30 | 31 | REM Create and navigate to build directory 32 | MD ".\src\radarsimcpp\build" 33 | CD ".\src\radarsimcpp\build" 34 | 35 | REM Build C++ library with CMake (with CUDA support) 36 | ECHO ## Building radarsimcpp.dll with MSVC ## 37 | cmake -DGPU_BUILD=ON -DGTEST=ON .. 38 | cmake --build . --config Release 39 | 40 | REM Build Python extensions for multiple Python versions (Free Tier) 41 | ECHO ## Building radarsimpy with Cython ## 42 | CD %pwd% 43 | REM Build for Python 3.9-3.13 with GPU support 44 | conda.exe run -n py313 python setup.py build_ext -b ./ --tier=free --arch=gpu 45 | conda.exe run -n py312 python setup.py build_ext -b ./ --tier=free --arch=gpu 46 | conda.exe run -n py311 python setup.py build_ext -b ./ --tier=free --arch=gpu 47 | conda.exe run -n py310 python setup.py build_ext -b ./ --tier=free --arch=gpu 48 | conda.exe run -n py39 python setup.py build_ext -b ./ --tier=free --arch=gpu 49 | 50 | REM Copy built files to radarsimpy directory 51 | ECHO ## Copying dll files to ./radarsimpy ## 52 | XCOPY ".\src\radarsimcpp\build\Release\radarsimcpp.dll" ".\radarsimpy\" 53 | XCOPY ".\src\radarsimpy\*.py" ".\radarsimpy\" 54 | XCOPY ".\src\radarsimpy\lib\__init__.py" ".\radarsimpy\lib\" 55 | 56 | REM Clean up intermediate build files 57 | ECHO ## Cleaning radarsimpy builds ## 58 | RMDIR build /s /q 59 | 60 | DEL ".\src\radarsimpy\*.c" 61 | DEL ".\src\radarsimpy\*.cpp" 62 | DEL ".\src\radarsimpy\*.html" 63 | DEL ".\src\radarsimpy\raytracing\*.c" 64 | DEL ".\src\radarsimpy\raytracing\*.cpp" 65 | DEL ".\src\radarsimpy\raytracing\*.html" 66 | DEL ".\src\radarsimpy\lib\*.cpp" 67 | DEL ".\src\radarsimpy\lib\*.html" 68 | DEL ".\src\*.cpp" 69 | DEL ".\src\*.html" 70 | 71 | REM Create FreeTier GPU distribution 72 | ECHO ## Copying lib files to freetier release folder ## 73 | RMDIR /Q/S .\trial\Windows_x86_64_GPU 74 | XCOPY /E /I .\radarsimpy .\trial\Windows_x86_64_GPU\radarsimpy 75 | 76 | RMDIR /Q/S .\radarsimpy 77 | 78 | REM Build Standard Tier GPU version 79 | REM Build Python extensions for multiple Python versions (Standard Tier) 80 | conda.exe run -n py313 python setup.py build_ext -b ./ --tier=standard --arch=gpu 81 | conda.exe run -n py312 python setup.py build_ext -b ./ --tier=standard --arch=gpu 82 | conda.exe run -n py311 python setup.py build_ext -b ./ --tier=standard --arch=gpu 83 | conda.exe run -n py310 python setup.py build_ext -b ./ --tier=standard --arch=gpu 84 | conda.exe run -n py39 python setup.py build_ext -b ./ --tier=standard --arch=gpu 85 | 86 | REM Copy built files to radarsimpy directory 87 | ECHO ## Copying dll files to ./radarsimpy ## 88 | XCOPY ".\src\radarsimcpp\build\Release\radarsimcpp.dll" ".\radarsimpy\" 89 | XCOPY ".\src\radarsimpy\*.py" ".\radarsimpy\" 90 | XCOPY ".\src\radarsimpy\lib\__init__.py" ".\radarsimpy\lib\" 91 | 92 | REM Clean up intermediate build files 93 | ECHO ## Cleaning radarsimpy builds ## 94 | RMDIR build /s /q 95 | 96 | DEL ".\src\radarsimpy\*.c" 97 | DEL ".\src\radarsimpy\*.cpp" 98 | DEL ".\src\radarsimpy\*.html" 99 | DEL ".\src\radarsimpy\raytracing\*.c" 100 | DEL ".\src\radarsimpy\raytracing\*.cpp" 101 | DEL ".\src\radarsimpy\raytracing\*.html" 102 | DEL ".\src\radarsimpy\lib\*.cpp" 103 | DEL ".\src\radarsimpy\lib\*.html" 104 | DEL ".\src\*.cpp" 105 | DEL ".\src\*.html" 106 | 107 | REM Create Standard Tier GPU distribution 108 | ECHO ## Copying lib files to standard release folder ## 109 | RMDIR /Q/S .\standard\Windows_x86_64_GPU 110 | XCOPY /E /I .\radarsimpy .\standard\Windows_x86_64_GPU\radarsimpy 111 | 112 | REM Run unit tests 113 | ECHO ## Run Google test ## 114 | .\src\radarsimcpp\build\Release\radarsimcpp_test.exe 115 | -------------------------------------------------------------------------------- /build_instructions.md: -------------------------------------------------------------------------------- 1 | # RadarSimPy Build Instructions 2 | 3 | ## Prerequisites for All Platforms 4 | 5 | - Python 3.9 or higher 6 | - CMake 3.20 or higher 7 | - C++ compiler with C++20 support 8 | - Python dependencies: 9 | 10 | ```bash 11 | pip install -r requirements.txt 12 | ``` 13 | 14 | ## Windows (MSVC) 15 | 16 | 1. Install required tools: 17 | - [Microsoft Visual Studio 2022](https://visualstudio.microsoft.com/) with "Desktop development with C++" workload 18 | - [CMake](https://cmake.org/download/) (Windows x64 Installer) 19 | - [CUDA Toolkit 12](https://developer.nvidia.com/cuda-downloads) (Required only for GPU version) 20 | 21 | 2. Build the project: 22 | 23 | ```batch 24 | # For CPU version 25 | build_win.bat --arch=cpu --test=on 26 | 27 | # For GPU version (requires CUDA) 28 | build_win.bat --arch=gpu --test=on 29 | ``` 30 | 31 | ## Ubuntu 22.04/24.04 32 | 33 | 1. Install system dependencies: 34 | 35 | ```bash 36 | # Basic development tools 37 | sudo apt-get update 38 | sudo apt-get install -y build-essential 39 | sudo snap install cmake --classic 40 | 41 | # For GPU version only 42 | # Install CUDA following NVIDIA's official guide: 43 | # https://developer.nvidia.com/cuda-downloads 44 | 45 | # Set up CUDA environment variables 46 | echo 'export PATH=/usr/local/cuda/bin:$PATH' >> ~/.bashrc 47 | echo 'export CUDA_PATH=/usr/local/cuda' >> ~/.bashrc 48 | source ~/.bashrc 49 | ``` 50 | 51 | 2. Build the project: 52 | 53 | ```bash 54 | # For CPU version 55 | ./build_linux.sh --arch=cpu --test=on 56 | 57 | # For GPU version 58 | ./build_linux.sh --arch=gpu --test=on 59 | ``` 60 | 61 | ## MacOS 62 | 63 | 1. Install build tools: 64 | 65 | ```bash 66 | # Install Homebrew if not already installed 67 | /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" 68 | 69 | # Install required tools 70 | brew install cmake gcc 71 | ``` 72 | 73 | 2. Build the project: 74 | 75 | ```bash 76 | ./build_macos.sh --arch=cpu --test=on 77 | ``` 78 | 79 | ## Build Output 80 | 81 | The compiled module will be available in the `radarsimpy` folder. 82 | 83 | ## Build Options 84 | 85 | - `--arch`: Build architecture (`cpu` or `gpu`) 86 | - `--test`: Enable testing (`on` or `off`) 87 | 88 | ## Troubleshooting 89 | 90 | - If CMake fails to find CUDA, ensure CUDA_PATH environment variable is set correctly 91 | -------------------------------------------------------------------------------- /build_linux.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Function to display help information and usage instructions 4 | Help() 5 | { 6 | # Display Help 7 | echo 8 | echo "Usages:" 9 | echo 10 | echo "Syntax: build_linux.sh --tier=[standard|free] --arch=[cpu|gpu] --test=[on|off]" 11 | echo "options:" 12 | echo " --help Show the usages of the parameters" 13 | echo " --tier Build tier, choose 'standard' or 'free'. Default is 'standard'" 14 | echo " --arch Build architecture, choose 'cpu' or 'gpu'. Default is 'cpu'" 15 | echo " --test Enable or disable unit test, choose 'on' or 'off'. Default is 'on'" 16 | echo 17 | } 18 | 19 | # Default configuration values 20 | TIER="standard" # Build tier (standard/free) 21 | ARCH="cpu" # Build architecture (cpu/gpu) 22 | TEST="on" # Unit test flag (on/off) 23 | 24 | # Parse command line arguments 25 | # Supports --help, --tier, --arch, and --test parameters 26 | for i in "$@"; do 27 | case $i in 28 | --help*) 29 | Help 30 | exit;; 31 | --tier=*) 32 | TIER="${i#*=}" 33 | shift # past argument 34 | ;; 35 | --arch=*) 36 | ARCH="${i#*=}" 37 | shift # past argument 38 | ;; 39 | --test=*) 40 | TEST="${i#*=}" 41 | shift # past argument 42 | ;; 43 | --*) 44 | echo "Unknown option $1" 45 | exit 1 46 | ;; 47 | *) 48 | ;; 49 | esac 50 | done 51 | 52 | # Validate the tier parameter 53 | if [ "${TIER,,}" != "standard" ] && [ "${TIER,,}" != "free" ]; then 54 | echo "ERROR: Invalid --tier parameters, please choose 'free' or 'standard'" 55 | exit 1 56 | fi 57 | 58 | # Validate the architecture parameter 59 | if [ "${ARCH,,}" != "cpu" ] && [ "${ARCH,,}" != "gpu" ]; then 60 | echo "ERROR: Invalid --arch parameters, please choose 'cpu' or 'gpu'" 61 | exit 1 62 | fi 63 | 64 | # Validate the test parameter 65 | if [ "${TEST,,}" != "on" ] && [ "${TEST,,}" != "off" ]; then 66 | echo "ERROR: Invalid --test parameters, please choose 'on' or 'off'" 67 | exit 1 68 | fi 69 | 70 | # Display project banner and copyright information 71 | echo "Automatic build script of radarsimcpp/radarsimpy for Linux" 72 | echo "" 73 | echo "----------" 74 | echo "RadarSimPy - A Radar Simulator Built with Python" 75 | echo "Copyright (C) 2018 - PRESENT radarsimx.com" 76 | echo "E-mail: info@radarsimx.com" 77 | echo "Website: https://radarsimx.com" 78 | echo "" 79 | echo "██████╗ █████╗ ██████╗ █████╗ ██████╗ ███████╗██╗███╗ ███╗██╗ ██╗" 80 | echo "██╔══██╗██╔══██╗██╔══██╗██╔══██╗██╔══██╗██╔════╝██║████╗ ████║╚██╗██╔╝" 81 | echo "██████╔╝███████║██║ ██║███████║██████╔╝███████╗██║██╔████╔██║ ╚███╔╝ " 82 | echo "██╔══██╗██╔══██║██║ ██║██╔══██║██╔══██╗╚════██║██║██║╚██╔╝██║ ██╔██╗ " 83 | echo "██║ ██║██║ ██║██████╔╝██║ ██║██║ ██║███████║██║██║ ╚═╝ ██║██╔╝ ██╗" 84 | echo "╚═╝ ╚═╝╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝╚═╝ ╚═╝╚═╝ ╚═╝" 85 | 86 | # Store current working directory 87 | workpath=$(pwd) 88 | 89 | # Clean up previous build artifacts 90 | echo "## Clean old build files ##" 91 | rm -rf ./src/radarsimcpp/build 92 | rm -rf ./radarsimpy 93 | 94 | # Build libradarsimcpp.so 95 | echo "## Building libradarsimcpp.so with ${ARCH^^} ##" 96 | mkdir ./src/radarsimcpp/build 97 | cd ./src/radarsimcpp/build 98 | 99 | # Configure CMake based on architecture and test settings 100 | if [ "${ARCH,,}" == "gpu" ]; then 101 | if [ "${TEST,,}" == "on" ]; then 102 | # GPU build with tests enabled 103 | cmake -DCMAKE_BUILD_TYPE=Release -DGPU_BUILD=ON -DGTEST=ON .. 104 | elif [ "${TEST,,}" == "off" ]; then 105 | # GPU build without tests 106 | cmake -DCMAKE_BUILD_TYPE=Release -DGPU_BUILD=ON -DGTEST=OFF .. 107 | fi 108 | elif [ "${ARCH,,}" == "cpu" ]; then 109 | if [ "${TEST,,}" == "on" ]; then 110 | # CPU build with tests enabled 111 | cmake -DCMAKE_BUILD_TYPE=Release -DGTEST=ON .. 112 | elif [ "${TEST,,}" == "off" ]; then 113 | # CPU build without tests 114 | cmake -DCMAKE_BUILD_TYPE=Release -DGTEST=OFF .. 115 | fi 116 | fi 117 | 118 | # Build the project using CMake 119 | cmake --build . 120 | 121 | # Build Python extensions using Cython 122 | echo "## Building radarsimpy with Cython ##" 123 | cd $workpath 124 | python setup.py build_ext -b ./ --tier "${TIER}" --arch "${ARCH}" 125 | 126 | # Copy library files to radarsimpy directory 127 | echo "## Copying lib files to ./radarsimpy ##" 128 | cp ./src/radarsimpy/*.py ./radarsimpy 129 | cp ./src/radarsimpy/lib/__init__.py ./radarsimpy/lib 130 | cp ./src/radarsimcpp/build/*.so ./radarsimpy 131 | 132 | # Clean up intermediate build files 133 | echo "## Cleaning radarsimpy builds ##" 134 | rm -rf build 135 | 136 | # Remove generated C/C++ source files and HTML documentation 137 | rm -f ./src/radarsimpy/*.c 138 | rm -f ./src/radarsimpy/*.cpp 139 | rm -f ./src/radarsimpy/*.html 140 | rm -f ./src/radarsimpy/raytracing/*.c 141 | rm -f ./src/radarsimpy/raytracing/*.cpp 142 | rm -f ./src/radarsimpy/raytracing/*.html 143 | rm -f ./src/radarsimpy/lib/*.cpp 144 | rm -f ./src/radarsimpy/lib/*.html 145 | rm -f ./src/*.cpp 146 | rm -f ./src/*.html 147 | 148 | echo "## Build completed ##" 149 | 150 | return_code=0 151 | # Run tests if enabled 152 | if [ "${TEST,,}" == "on" ]; then 153 | # Run C++ unit tests using Google Test 154 | echo "## Run Google test ##" 155 | ./src/radarsimcpp/build/radarsimcpp_test 156 | return_code=$(($return_code + $?)) 157 | 158 | # Run Python unit tests using pytest 159 | echo "## Pytest ##" 160 | pytest 161 | return_code=$(($return_code + $?)) 162 | fi 163 | 164 | exit $return_code 165 | -------------------------------------------------------------------------------- /build_macos.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Function to display help information and usage instructions 4 | Help() 5 | { 6 | # Display Help 7 | echo 8 | echo "Usages:" 9 | echo 10 | echo "Syntax: build_linux.sh --tier=[standard|free] --arch=[cpu|gpu] --test=[on|off]" 11 | echo "options:" 12 | echo " --help Show the usages of the parameters" 13 | echo " --tier Build tier, choose 'standard' or 'free'. Default is 'standard'" 14 | echo " --arch Build architecture, choose 'cpu' or 'gpu'. Default is 'cpu'" 15 | echo " --test Enable or disable unit test, choose 'on' or 'off'. Default is 'on'" 16 | echo 17 | } 18 | 19 | # Parse command line arguments 20 | # Supported arguments: 21 | # --tier: Build tier (standard/free) 22 | # --arch: Build architecture (cpu/gpu) 23 | # --test: Enable/disable unit testing (on/off) 24 | for i in "$@"; do 25 | case $i in 26 | --help*) 27 | Help 28 | exit;; 29 | --tier=*) 30 | TIER="${i#*=}" 31 | shift # past argument 32 | ;; 33 | --arch=*) 34 | ARCH="${i#*=}" 35 | shift # past argument 36 | ;; 37 | --test=*) 38 | TEST="${i#*=}" 39 | shift # past argument 40 | ;; 41 | --*) 42 | echo "Unknown option $1" 43 | exit 1 44 | ;; 45 | *) 46 | ;; 47 | esac 48 | done 49 | 50 | # Validate the tier parameter 51 | # Must be either 'standard' or 'free' 52 | if [ "${TIER}" != "standard" ] && [ "${TIER}" != "free" ]; then 53 | echo "ERROR: Invalid --tier parameters, please choose 'free' or 'standard'" 54 | exit 1 55 | fi 56 | 57 | # Validate the architecture parameter 58 | # Must be either 'cpu' or 'gpu' 59 | if [ "${ARCH}" != "cpu" ] && [ "${ARCH}" != "gpu" ]; then 60 | echo "ERROR: Invalid --arch parameters, please choose 'cpu' or 'gpu'" 61 | exit 1 62 | fi 63 | 64 | # Validate the test parameter 65 | # Must be either 'on' or 'off' 66 | if [ "${TEST}" != "on" ] && [ "${TEST}" != "off" ]; then 67 | echo "ERROR: Invalid --test parameters, please choose 'on' or 'off'" 68 | exit 1 69 | fi 70 | 71 | # Display welcome message and copyright information 72 | echo "Automatic build script of radarsimcpp/radarsimpy for macOS" 73 | echo "" 74 | echo "----------" 75 | echo "RadarSimPy - A Radar Simulator Built with Python" 76 | echo "Copyright (C) 2018 - PRESENT radarsimx.com" 77 | echo "E-mail: info@radarsimx.com" 78 | echo "Website: https://radarsimx.com" 79 | echo "" 80 | echo "██████╗ █████╗ ██████╗ █████╗ ██████╗ ███████╗██╗███╗ ███╗██╗ ██╗" 81 | echo "██╔══██╗██╔══██╗██╔══██╗██╔══██╗██╔══██╗██╔════╝██║████╗ ████║╚██╗██╔╝" 82 | echo "██████╔╝███████║██║ ██║███████║██████╔╝███████╗██║██╔████╔██║ ╚███╔╝ " 83 | echo "██╔══██╗██╔══██║██║ ██║██╔══██║██╔══██╗╚════██║██║██║╚██╔╝██║ ██╔██╗ " 84 | echo "██║ ██║██║ ██║██████╔╝██║ ██║██║ ██║███████║██║██║ ╚═╝ ██║██╔╝ ██╗" 85 | echo "╚═╝ ╚═╝╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝╚═╝ ╚═╝╚═╝ ╚═╝" 86 | 87 | # Store current working directory 88 | workpath=$(pwd) 89 | 90 | # Clean up previous build artifacts 91 | echo "## Clean old build files ##" 92 | rm -rf ./src/radarsimcpp/build 93 | rm -rf ./radarsimpy 94 | 95 | # Build the C++ library 96 | echo "## Building libradarsimcpp.so with ${ARCH} ##" 97 | mkdir ./src/radarsimcpp/build 98 | cd ./src/radarsimcpp/build 99 | 100 | # Convert architecture and test parameters to lowercase for consistency 101 | ARCH_LOWER=$(echo "$ARCH" | tr '[:upper:]' '[:lower:]') 102 | TEST_LOWER=$(echo "$TEST" | tr '[:upper:]' '[:lower:]') 103 | 104 | # Configure CMake based on build parameters 105 | # GPU build with testing enabled/disabled 106 | if [ "${ARCH_LOWER}" = "gpu" ]; then 107 | if [ "${TEST_LOWER}" = "on" ]; then 108 | cmake -DCMAKE_BUILD_TYPE=Release -DGPU_BUILD=ON -DGTEST=ON .. 109 | elif [ "${TEST_LOWER}" = "off" ]; then 110 | cmake -DCMAKE_BUILD_TYPE=Release -DGPU_BUILD=ON -DGTEST=OFF .. 111 | fi 112 | # CPU build with testing enabled/disabled 113 | elif [ "${ARCH_LOWER}" = "cpu" ]; then 114 | if [ "${TEST_LOWER}" = "on" ]; then 115 | cmake -DCMAKE_BUILD_TYPE=Release -DGTEST=ON .. 116 | elif [ "${TEST_LOWER}" = "off" ]; then 117 | cmake -DCMAKE_BUILD_TYPE=Release -DGTEST=OFF .. 118 | fi 119 | fi 120 | 121 | # Build the project using CMake 122 | cmake --build . 123 | 124 | # Build Python extensions using Cython 125 | echo "## Building radarsimpy with Cython ##" 126 | cd $workpath 127 | python setup.py build_ext -b ./ --tier "${TIER}" --arch "${ARCH}" 128 | 129 | # Copy built files to the final location 130 | echo "## Copying lib files to ./radarsimpy ##" 131 | cp ./src/radarsimpy/*.py ./radarsimpy 132 | cp ./src/radarsimpy/lib/__init__.py ./radarsimpy/lib 133 | cp ./src/radarsimcpp/build/*.dylib ./radarsimpy 134 | 135 | # Clean up intermediate build files 136 | echo "## Cleaning radarsimpy builds ##" 137 | rm -rf build 138 | 139 | # Remove generated C/C++ source files and HTML documentation 140 | rm -f ./src/radarsimpy/*.c 141 | rm -f ./src/radarsimpy/*.cpp 142 | rm -f ./src/radarsimpy/*.html 143 | rm -f ./src/radarsimpy/raytracing/*.c 144 | rm -f ./src/radarsimpy/raytracing/*.cpp 145 | rm -f ./src/radarsimpy/raytracing/*.html 146 | rm -f ./src/radarsimpy/lib/*.cpp 147 | rm -f ./src/radarsimpy/lib/*.html 148 | rm -f ./src/*.cpp 149 | rm -f ./src/*.html 150 | 151 | echo "## Build completed ##" 152 | 153 | return_code=0 154 | # Run tests if testing is enabled 155 | if [ "${TEST}" == "on" ]; then 156 | # Run C++ unit tests 157 | echo "## Run Google test ##" 158 | ./src/radarsimcpp/build/radarsimcpp_test 159 | return_code=$(($return_code + $?)) 160 | 161 | # Run Python unit tests 162 | echo "## Pytest ##" 163 | pytest 164 | return_code=$(($return_code + $?)) 165 | fi 166 | 167 | exit $return_code 168 | -------------------------------------------------------------------------------- /build_win.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | REM Default build configuration 4 | set TIER=standard 5 | set ARCH=cpu 6 | set TEST=on 7 | 8 | goto GETOPTS 9 | 10 | REM Help section - displays command line parameter usage 11 | :Help 12 | ECHO: 13 | ECHO Usages: 14 | ECHO --help Show the usages of the parameters 15 | ECHO --tier Build tier, choose 'standard' or 'free'. Default is 'standard' 16 | ECHO --arch Build architecture, choose 'cpu' or 'gpu'. Default is 'cpu' 17 | ECHO --test Enable or disable unit test, choose 'on' or 'off'. Default is 'on' 18 | ECHO: 19 | goto EOF 20 | 21 | REM Command line parameter parsing section 22 | :GETOPTS 23 | REM Parse command line arguments 24 | if /I "%1" == "--help" goto Help 25 | if /I "%1" == "--tier" set TIER=%2 & shift 26 | if /I "%1" == "--arch" set ARCH=%2 & shift 27 | if /I "%1" == "--test" set TEST=%2 & shift 28 | shift 29 | if not "%1" == "" goto GETOPTS 30 | 31 | REM Validate tier parameter 32 | if /I NOT %TIER% == free ( 33 | if /I NOT %TIER% == standard ( 34 | ECHO ERROR: Invalid --tier parameters, please choose 'free' or 'standard' 35 | goto EOF 36 | ) 37 | ) 38 | 39 | REM Validate architecture parameter 40 | if /I NOT %ARCH% == cpu ( 41 | if /I NOT %ARCH% == gpu ( 42 | ECHO ERROR: Invalid --arch parameters, please choose 'cpu' or 'gpu' 43 | goto EOF 44 | ) 45 | ) 46 | 47 | REM Validate test parameter 48 | if /I NOT %TEST% == on ( 49 | if /I NOT %TEST% == off ( 50 | ECHO ERROR: Invalid --test parameters, please choose 'on' or 'off' 51 | goto EOF 52 | ) 53 | ) 54 | 55 | REM Display banner and copyright information 56 | ECHO Automatic build script of radarsimcpp/radarsimpy for Windows 57 | ECHO: 58 | ECHO ---------- 59 | ECHO RadarSimPy - A Radar Simulator Built with Python 60 | ECHO Copyright (C) 2018 - PRESENT radarsimx.com 61 | ECHO E-mail: info@radarsimx.com 62 | ECHO Website: https://radarsimx.com 63 | ECHO: 64 | ECHO ###### ##### # # 65 | ECHO # # ## ##### ## ##### # # # # # # # 66 | ECHO # # # # # # # # # # # # ## ## # # 67 | ECHO ###### # # # # # # # # ##### # # ## # # 68 | ECHO # # ###### # # ###### ##### # # # # # # 69 | ECHO # # # # # # # # # # # # # # # # # 70 | ECHO # # # # ##### # # # # ##### # # # # # 71 | ECHO: 72 | 73 | REM Store current directory for later use 74 | SET pwd=%cd% 75 | 76 | REM Clean up previous build artifacts 77 | ECHO clean old build files 78 | RMDIR /Q/S .\src\radarsimcpp\build 79 | 80 | ECHO clean old radarsimpy module 81 | RMDIR /Q/S .\radarsimpy 82 | 83 | REM Create and enter build directory 84 | @REM Create fresh build directory and change to it 85 | MD ".\src\radarsimcpp\build" 86 | CD ".\src\radarsimcpp\build" 87 | 88 | REM Configure CMake build based on architecture and test settings 89 | ECHO ## Building radarsimcpp.dll with MSVC ## 90 | @REM MSVC requires explicit Release configuration 91 | if /I %ARCH% == gpu ( 92 | if /I %TEST% == on ( 93 | cmake -DGPU_BUILD=ON -DGTEST=ON .. 94 | ) else ( 95 | cmake -DGPU_BUILD=ON -DGTEST=OFF .. 96 | ) 97 | ) else if /I %ARCH% == cpu ( 98 | if /I %TEST% == on ( 99 | cmake -DGTEST=ON .. 100 | ) else ( 101 | cmake -DGTEST=OFF .. 102 | ) 103 | ) 104 | cmake --build . --config Release 105 | 106 | REM Build Python extensions using Cython 107 | ECHO ## Building radarsimpy with Cython ## 108 | CD %pwd% 109 | python setup.py build_ext -b ./ --tier %TIER% --arch %ARCH% 110 | 111 | REM Copy built artifacts to final locations 112 | ECHO ## Copying dll files to ./radarsimpy ## 113 | XCOPY ".\src\radarsimcpp\build\Release\radarsimcpp.dll" ".\radarsimpy\" 114 | XCOPY ".\src\radarsimpy\*.py" ".\radarsimpy\" 115 | XCOPY ".\src\radarsimpy\lib\__init__.py" ".\radarsimpy\lib\" 116 | 117 | REM Clean up intermediate build files 118 | ECHO ## Cleaning radarsimpy builds ## 119 | RMDIR build /s /q 120 | 121 | DEL ".\src\radarsimpy\*.c" 122 | DEL ".\src\radarsimpy\*.cpp" 123 | DEL ".\src\radarsimpy\*.html" 124 | DEL ".\src\radarsimpy\raytracing\*.c" 125 | DEL ".\src\radarsimpy\raytracing\*.cpp" 126 | DEL ".\src\radarsimpy\raytracing\*.html" 127 | DEL ".\src\radarsimpy\lib\*.cpp" 128 | DEL ".\src\radarsimpy\lib\*.html" 129 | DEL ".\src\*.cpp" 130 | DEL ".\src\*.html" 131 | 132 | SET TEST_FAILED=0 133 | REM Run tests if enabled 134 | if /I %TEST% == on ( 135 | ECHO ## Run Google test ## 136 | .\src\radarsimcpp\build\Release\radarsimcpp_test.exe 137 | if errorlevel 1 ( 138 | echo Google test failed! 139 | SET TEST_FAILED=1 140 | ) 141 | 142 | ECHO ## Pytest ## 143 | pytest 144 | if errorlevel 1 ( 145 | echo Pytest failed! 146 | SET TEST_FAILED=1 147 | ) 148 | ) 149 | 150 | exit /b %TEST_FAILED% 151 | 152 | :EOF -------------------------------------------------------------------------------- /gen_docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /gen_docs/_static/radarsimdoc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radarsimx/radarsimpy/0d312d496e0b99dedd8c1fbbfb5de978bc374c0f/gen_docs/_static/radarsimdoc.png -------------------------------------------------------------------------------- /gen_docs/api/index.rst: -------------------------------------------------------------------------------- 1 | API 2 | === 3 | 4 | .. toctree:: 5 | :maxdepth: 2 6 | 7 | radar 8 | sim 9 | process 10 | tools 11 | -------------------------------------------------------------------------------- /gen_docs/api/process.rst: -------------------------------------------------------------------------------- 1 | Processing 2 | =========== 3 | 4 | .. automodule:: radarsimpy.processing 5 | :members: 6 | :undoc-members: 7 | :inherited-members: 8 | :show-inheritance: 9 | :exclude-members: os_cfar_threshold -------------------------------------------------------------------------------- /gen_docs/api/radar.rst: -------------------------------------------------------------------------------- 1 | Radar Model 2 | =========== 3 | 4 | Radar Configuration and Phase Noise Modeling in Python 5 | 6 | This module contains classes and functions to define and simulate 7 | the parameters and behavior of radar systems. It includes tools for 8 | configuring radar system properties, modeling oscillator phase noise, 9 | and simulating radar motion and noise characteristics. A major focus 10 | of the module is on accurately modeling radar signal properties, 11 | including phase noise and noise amplitudes. 12 | 13 | --- 14 | 15 | - Copyright (C) 2018 - PRESENT radarsimx.com 16 | - E-mail: info@radarsimx.com 17 | - Website: https://radarsimx.com 18 | 19 | :: 20 | 21 | ██████╗ █████╗ ██████╗ █████╗ ██████╗ ███████╗██╗███╗ ███╗██╗ ██╗ 22 | ██╔══██╗██╔══██╗██╔══██╗██╔══██╗██╔══██╗██╔════╝██║████╗ ████║╚██╗██╔╝ 23 | ██████╔╝███████║██║ ██║███████║██████╔╝███████╗██║██╔████╔██║ ╚███╔╝ 24 | ██╔══██╗██╔══██║██║ ██║██╔══██║██╔══██╗╚════██║██║██║╚██╔╝██║ ██╔██╗ 25 | ██║ ██║██║ ██║██████╔╝██║ ██║██║ ██║███████║██║██║ ╚═╝ ██║██╔╝ ██╗ 26 | ╚═╝ ╚═╝╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ 27 | 28 | radarsimpy.Transmitter 29 | ----------------------- 30 | 31 | .. autoclass:: radarsimpy.Transmitter 32 | :members: 33 | :undoc-members: 34 | :inherited-members: 35 | :show-inheritance: 36 | :exclude-members: validate_rf_prop, validate_waveform_prop, process_waveform_modulation, process_pulse_modulation, process_txchannel_prop 37 | 38 | radarsimpy.Receiver 39 | -------------------- 40 | 41 | .. autoclass:: radarsimpy.Receiver 42 | :members: 43 | :undoc-members: 44 | :inherited-members: 45 | :show-inheritance: 46 | :exclude-members: validate_bb_prop, process_rxchannel_prop 47 | 48 | radarsimpy.Radar 49 | ----------------- 50 | 51 | .. autoclass:: radarsimpy.Radar 52 | :members: 53 | :undoc-members: 54 | :inherited-members: 55 | :show-inheritance: 56 | :exclude-members: cal_noise, gen_timestamp, validate_radar_motion, process_radar_motion 57 | -------------------------------------------------------------------------------- /gen_docs/api/sim.rst: -------------------------------------------------------------------------------- 1 | Simulator 2 | =========== 3 | 4 | .. automodule:: radarsimpy.simulator 5 | :members: 6 | :undoc-members: 7 | :inherited-members: 8 | :show-inheritance: 9 | :exclude-members: raise_err 10 | -------------------------------------------------------------------------------- /gen_docs/api/tools.rst: -------------------------------------------------------------------------------- 1 | Tools 2 | ================= 3 | 4 | .. automodule:: radarsimpy.tools 5 | :members: 6 | :undoc-members: 7 | :inherited-members: 8 | :show-inheritance: 9 | :exclude-members: log_factorial, threshold, marcumq, pd_swerling0, pd_swerling1, pd_swerling2, pd_swerling3, pd_swerling4 10 | -------------------------------------------------------------------------------- /gen_docs/conf.py: -------------------------------------------------------------------------------- 1 | """ 2 | Configuration file for the Sphinx documentation builder. 3 | 4 | This file only contains a selection of the most common options. For a full 5 | list see the documentation: 6 | https://www.sphinx-doc.org/en/master/usage/configuration.html 7 | 8 | -- Path setup -------------------------------------------------------------- 9 | 10 | If extensions (or modules to document with autodoc) are in another directory, 11 | add these directories to sys.path here. If the directory is relative to the 12 | documentation root, use os.path.abspath to make it absolute, like shown here. 13 | """ 14 | 15 | import os 16 | import sys 17 | import datetime 18 | 19 | sys.path.insert(0, os.path.abspath("..")) 20 | 21 | import radarsimpy # pylint: disable=wrong-import-position 22 | 23 | # -- Project information ----------------------------------------------------- 24 | 25 | project = "RadarSimPy" # pylint: disable=invalid-name 26 | copyright = ( # pylint: disable=redefined-builtin, invalid-name 27 | "2018 - " + str(datetime.datetime.now().year) + ", RadarSimX LLC" 28 | ) 29 | version = radarsimpy.__version__ # pylint: disable=invalid-name 30 | release = version # pylint: disable=invalid-name 31 | 32 | pygments_style = "sphinx" 33 | 34 | # -- General configuration --------------------------------------------------- 35 | 36 | # Add any Sphinx extension module names here, as strings. They can be 37 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 38 | # ones. 39 | extensions = [ 40 | "sphinx.ext.intersphinx", 41 | "sphinx.ext.autodoc", 42 | "sphinx.ext.mathjax", 43 | "sphinx.ext.viewcode", 44 | "sphinx.ext.doctest", # Add doctest extension 45 | ] 46 | 47 | autodoc_typehints = "description" 48 | 49 | # Add any paths that contain templates here, relative to this directory. 50 | templates_path = ["_templates"] 51 | 52 | html_favicon = "_static/radarsimdoc.png" # pylint: disable=invalid-name 53 | 54 | # List of patterns, relative to source directory, that match files and 55 | # directories to ignore when looking for source files. 56 | # This pattern also affects html_static_path and html_extra_path. 57 | exclude_patterns = [] 58 | 59 | 60 | # -- Options for HTML output ------------------------------------------------- 61 | 62 | # The theme to use for HTML and HTML Help pages. See the documentation for 63 | # a list of builtin themes. 64 | # 65 | html_theme = "pydata_sphinx_theme" # pylint: disable=invalid-name 66 | 67 | html_theme_options = { 68 | "logo": { 69 | "text": "RadarSimPy v" + version, 70 | "image_light": "_static/radarsimdoc.svg", 71 | "image_dark": "_static/radarsimdoc.svg", 72 | }, 73 | "navbar_align": "left", 74 | "secondary_sidebar_items": ["page-toc"], 75 | "show_nav_level": 2, 76 | "show_toc_level": 2, 77 | "show_version_warning_banner": True, 78 | "header_links_before_dropdown": 8, 79 | "icon_links": [ 80 | { 81 | "name": "RadarSimX", 82 | "url": "https://radarsimx.com/", 83 | "icon": "fas fa-globe", 84 | }, 85 | { 86 | "name": "GitHub", 87 | "url": "https://github.com/radarsimx/radarsimpy", 88 | "icon": "fab fa-github", 89 | }, 90 | ], 91 | "footer_start": ["copyright"], 92 | "footer_center ": ["sphinx-version"], 93 | "footer_end": ["theme-version"], 94 | } 95 | 96 | html_context = { 97 | "default_mode": "auto", 98 | "display_github": True, 99 | "github_user": "radarsimx", 100 | "github_repo": "radarsimpy", 101 | } 102 | 103 | # Add any paths that contain custom static files (such as style sheets) here, 104 | # relative to this directory. They are copied after the builtin static files, 105 | # so a file named "default.css" will overwrite the builtin "default.css". 106 | # html_static_path = ["_static"] 107 | -------------------------------------------------------------------------------- /gen_docs/index.rst: -------------------------------------------------------------------------------- 1 | .. RadarSimPy documentation master file, created by 2 | sphinx-quickstart on Wed Dec 16 08:59:19 2020. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to RadarSimPy's Documentation! 7 | ====================================== 8 | 9 | | 10 | 11 | .. image:: _static/radarsimx_main.svg 12 | :width: 60% 13 | :alt: RadarSimX 14 | :target: https://radarsimx.com 15 | 16 | | 17 | 18 | User Guide 19 | ---------- 20 | 21 | Step-by-step guides to help you get started with RadarSimPy and understand key concepts. 22 | 23 | .. toctree:: 24 | :maxdepth: 2 25 | 26 | user_guide/index 27 | 28 | 29 | API 30 | --- 31 | 32 | Complete reference documentation for all RadarSimPy classes and functions. 33 | 34 | .. toctree:: 35 | :maxdepth: 2 36 | 37 | api/index 38 | -------------------------------------------------------------------------------- /gen_docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.http://sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /gen_docs/user_guide/build.rst: -------------------------------------------------------------------------------- 1 | Build Instructions 2 | ================== 3 | 4 | .. warning:: 5 | Building radarsimpy requires access to the radarsimcpp source code. If you don't have access, 6 | please use the pre-built module. For organizations seeking full source code access for customization 7 | or advanced integration, please `submit Quote for Source Code `_. 8 | 9 | Windows 10 | ------- 11 | 12 | **CPU Version** 13 | 14 | .. code-block:: batch 15 | 16 | build_win.bat --arch cpu --test=on 17 | 18 | **GPU Version (CUDA)** 19 | 20 | .. code-block:: batch 21 | 22 | build_win.bat --arch gpu --test=on 23 | 24 | Linux 25 | ----- 26 | 27 | **CPU Version** 28 | 29 | .. code-block:: bash 30 | 31 | ./build_linux.sh --arch=cpu --test=on 32 | 33 | **GPU Version (CUDA)** 34 | 35 | .. code-block:: bash 36 | 37 | ./build_linux.sh --arch=gpu --test=on 38 | 39 | MacOS 40 | ----- 41 | 42 | .. code-block:: bash 43 | 44 | ./build_macos.sh --arch=cpu --test=on 45 | -------------------------------------------------------------------------------- /gen_docs/user_guide/coordinate.rst: -------------------------------------------------------------------------------- 1 | Coordinate Systems 2 | =================== 3 | 4 | **Global Coordinate** 5 | 6 | - **axis** (m): ``[x, y, z]`` 7 | - **phi** (deg): angle on the x-y plane. 0 deg is the positive x-axis, 90 deg is the positive y-axis 8 | - **theta** (deg): angle on the z-x plane. 0 deg is the positive z-axis, 90 deg is the x-y plane 9 | 10 | .. image:: https://raw.githubusercontent.com/radarsimx/radarsimpy/refs/heads/master/assets/phi_theta.svg 11 | :width: 400 12 | :alt: Phi Theta Plot 13 | 14 | **Local Coordinate** 15 | 16 | - **yaw** (deg): rotation along the z-axis. Positive yaw rotates the object from the positive x-axis to the positive y-axis 17 | - **pitch** (deg): rotation along the y-axis. Positive pitch rotates the object from the positive x-axis to the positive z-axis 18 | - **roll** (deg): rotation along the x-axis. Positive roll rotates the object from the positive y-axis to the positive z-axis 19 | - **origin** (m): ``[x, y, z]``, the motion (rotation and translation) centor of the object. Radar's origin is always at ``[0, 0, 0]`` 20 | 21 | .. image:: https://raw.githubusercontent.com/radarsimx/radarsimpy/master/assets/yaw_pitch_roll.svg 22 | :width: 400 23 | :alt: Yaw Pitch Roll Plot 24 | 25 | - **azimuth** (deg): azimuth -90 ~ 90 deg equal to phi -90 ~ 90 deg 26 | - **elevation** (deg): elevation -90 ~ 90 deg equal to theta 180 ~ 0 deg 27 | 28 | .. image:: https://raw.githubusercontent.com/radarsimx/radarsimpy/master/assets/azimuth_elevation.svg 29 | :width: 400 30 | :alt: Azimuth Elevation Plot 31 | -------------------------------------------------------------------------------- /gen_docs/user_guide/dependence.rst: -------------------------------------------------------------------------------- 1 | Dependencies 2 | ============ 3 | 4 | Python Requirements 5 | ------------------- 6 | 7 | * ``Python`` >= 3.9 8 | * ``NumPy`` >= 2.0 9 | * ``SciPy`` >= 1.11.0 10 | * One of the following mesh processing libraries: 11 | 12 | * ``PyMeshLab`` >= 2022.2 13 | * ``PyVista`` >= 0.43.0 14 | * ``trimesh`` >= 4.0.0 15 | * ``meshio`` >= 5.3.0 16 | 17 | System Requirements 18 | ------------------- 19 | 20 | Windows 21 | ^^^^^^^ 22 | * `Visual C++ Runtime `_ 23 | * For GPU version (CUDA 12): Latest NVIDIA drivers - See `compatibility guide `_ 24 | 25 | Linux 26 | ^^^^^ 27 | 28 | **Ubuntu 22.04/24.04** 29 | 30 | * GCC (included by default) 31 | * For GPU version (CUDA 12): Latest NVIDIA drivers 32 | 33 | **Other Linux distributions** 34 | 35 | * Try the Ubuntu builds first 36 | * `Request a custom build `_ if needed 37 | 38 | MacOS 39 | ^^^^^ 40 | 41 | **Intel/Apple Silicon** 42 | 43 | * GCC 14 installation:: 44 | 45 | brew install gcc@14 46 | -------------------------------------------------------------------------------- /gen_docs/user_guide/examples.rst: -------------------------------------------------------------------------------- 1 | Usage Examples 2 | ============== 3 | 4 | All usage examples are available on `radarsimx.com `_. 5 | Source files can be found in the `radarsimnb repository `_. 6 | 7 | Waveform 8 | -------- 9 | * `Pulsed Radar `_ 10 | * `Interferometric Radar `_ 11 | * `Arbitrary waveform `_ 12 | * `PMCW radar `_ 13 | * `Doppler radar `_ 14 | * `FMCW radar `_ 15 | 16 | MIMO 17 | ---- 18 | * `Imaging radar `_ 19 | * `DoA estimation `_ 20 | * `TDM MIMO FMCW radar `_ 21 | 22 | Angle Estimation 23 | ---------------- 24 | * `Imaging radar `_ 25 | * `DoA estimation `_ 26 | 27 | Ray Tracing 28 | ----------- 29 | * `Imaging radar `_ 30 | * `Multi-path effect `_ 31 | * `Micro-Doppler `_ 32 | * `Doppler of a turbine `_ 33 | * `FMCW radar with a car `_ 34 | * `FMCW radar with a plate `_ 35 | * `FMCW radar with a corner reflector `_ 36 | * `Cross-Polarization and Co-Polarization RCS `_ 37 | * `Car RCS `_ 38 | * `Plate RCS `_ 39 | * `Corner reflector RCS `_ 40 | * `LIDAR point cloud `_ 41 | 42 | CFAR 43 | ---- 44 | * `FMCW radar with a corner reflector `_ 45 | * `CFAR `_ 46 | 47 | Interference 48 | ------------ 49 | * `Interference `_ 50 | 51 | System Characterization 52 | ----------------------- 53 | * `FMCW Radar Link Budget (Point Target) `_ 54 | * `Phase noise `_ 55 | * `Receiver operating characteristic (ROC) `_ 56 | 57 | Radar Cross Section 58 | ------------------- 59 | * `Cross-Polarization and Co-Polarization RCS `_ 60 | * `Car RCS `_ 61 | * `Plate RCS `_ 62 | * `Corner reflector RCS `_ 63 | 64 | LiDAR 65 | ----- 66 | * `LIDAR point cloud `_ 67 | -------------------------------------------------------------------------------- /gen_docs/user_guide/features.rst: -------------------------------------------------------------------------------- 1 | Key Features 2 | ============ 3 | 4 | Radar Modeling 5 | -------------- 6 | 7 | **Radar Transceiver Modeling** 8 | 9 | * Arbitrary waveform (CW, FMCW, PMCW, Pulse, ...) 10 | * Phase noise 11 | * Phase/amplitude modulation (CDM, FDM, DDM, TDM, ...) 12 | * Fast-time/slow-time modulation 13 | 14 | Simulation 15 | ---------- 16 | 17 | * Radar baseband data from point targets 18 | * Radar baseband data from 3D modeled objects/environment 19 | * Interference simulation 20 | * Target's RCS simulation 21 | * LiDAR point cloud from 3D modeled objects/environment 22 | 23 | Signal Processing 24 | ----------------- 25 | 26 | **Range/Doppler Processing** 27 | 28 | * Direction of arrival (DoA) estimation 29 | * Multiple techniques for ULA DoA estimation: 30 | 31 | * MUltiple SIgnal Classification (MUSIC) 32 | * Root-MUSIC 33 | * Estimation of Signal Parameters via Rational Invariance Techniques (ESPRIT) 34 | 35 | * Iterative Adaptive Approach (IAA) for amplitude and phase estimation 36 | 37 | **Beamforming** 38 | 39 | * Capon beamformer 40 | * Bartlett beamformer 41 | 42 | **CFAR Processing** 43 | 44 | * 1D/2D cell-averaging CFAR (CA-CFAR) 45 | * 1D/2D ordered-statistic CFAR (OS-CFAR) 46 | 47 | Characterization 48 | ---------------- 49 | 50 | * Radar detection characteristics based on Swerling's models 51 | -------------------------------------------------------------------------------- /gen_docs/user_guide/index.rst: -------------------------------------------------------------------------------- 1 | User Guide 2 | ========== 3 | 4 | .. toctree:: 5 | :maxdepth: 2 6 | :caption: Get started 7 | 8 | features 9 | dependence 10 | installation 11 | coordinate 12 | examples 13 | build 14 | -------------------------------------------------------------------------------- /gen_docs/user_guide/installation.rst: -------------------------------------------------------------------------------- 1 | Installation 2 | ============ 3 | 4 | Quick Start 5 | ----------- 6 | 7 | 1. Download the `pre-built module `_ 8 | 2. Extract the package 9 | 3. Place the radarsimpy folder in your project directory 10 | 11 | Directory Structure 12 | ------------------- 13 | 14 | The following shows the typical project structure after installation: 15 | 16 | Common Files 17 | ^^^^^^^^^^^^ 18 | .. code-block:: none 19 | 20 | your_project/ 21 | ├── your_project.py 22 | ├── your_project.ipynb 23 | └── radarsimpy/ 24 | ├── __init__.py 25 | ├── radar.py 26 | ├── processing.py 27 | └── ... 28 | 29 | Platform-Specific Files 30 | ^^^^^^^^^^^^^^^^^^^^^^^ 31 | 32 | **Windows**:: 33 | 34 | radarsimpy/ 35 | ├── radarsimcpp.dll 36 | └── simulator.xxx.pyd 37 | 38 | **Linux**:: 39 | 40 | radarsimpy/ 41 | ├── libradarsimcpp.so 42 | └── simulator.xxx.so 43 | 44 | **MacOS**:: 45 | 46 | radarsimpy/ 47 | ├── libradarsimcpp.dylib 48 | └── simulator.xxx.so 49 | 50 | Verification 51 | ------------ 52 | 53 | To verify the installation, run: 54 | 55 | .. code-block:: python 56 | 57 | import radarsimpy 58 | print(radarsimpy.__version__) 59 | -------------------------------------------------------------------------------- /models/ball_1m.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radarsimx/radarsimpy/0d312d496e0b99dedd8c1fbbfb5de978bc374c0f/models/ball_1m.stl -------------------------------------------------------------------------------- /models/cr.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radarsimx/radarsimpy/0d312d496e0b99dedd8c1fbbfb5de978bc374c0f/models/cr.stl -------------------------------------------------------------------------------- /models/dihedral.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radarsimx/radarsimpy/0d312d496e0b99dedd8c1fbbfb5de978bc374c0f/models/dihedral.stl -------------------------------------------------------------------------------- /models/half_ring.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radarsimx/radarsimpy/0d312d496e0b99dedd8c1fbbfb5de978bc374c0f/models/half_ring.stl -------------------------------------------------------------------------------- /models/pannel.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radarsimx/radarsimpy/0d312d496e0b99dedd8c1fbbfb5de978bc374c0f/models/pannel.stl -------------------------------------------------------------------------------- /models/plate.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radarsimx/radarsimpy/0d312d496e0b99dedd8c1fbbfb5de978bc374c0f/models/plate.stl -------------------------------------------------------------------------------- /models/plate5x5.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radarsimx/radarsimpy/0d312d496e0b99dedd8c1fbbfb5de978bc374c0f/models/plate5x5.stl -------------------------------------------------------------------------------- /models/surface.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radarsimx/radarsimpy/0d312d496e0b99dedd8c1fbbfb5de978bc374c0f/models/surface.stl -------------------------------------------------------------------------------- /models/surface_400x400.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radarsimx/radarsimpy/0d312d496e0b99dedd8c1fbbfb5de978bc374c0f/models/surface_400x400.stl -------------------------------------------------------------------------------- /models/surface_60x60.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radarsimx/radarsimpy/0d312d496e0b99dedd8c1fbbfb5de978bc374c0f/models/surface_60x60.stl -------------------------------------------------------------------------------- /models/turbine.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radarsimx/radarsimpy/0d312d496e0b99dedd8c1fbbfb5de978bc374c0f/models/turbine.stl -------------------------------------------------------------------------------- /models/vehicles/ford_raptor.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radarsimx/radarsimpy/0d312d496e0b99dedd8c1fbbfb5de978bc374c0f/models/vehicles/ford_raptor.stl -------------------------------------------------------------------------------- /models/vehicles/gasoline_tanker.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radarsimx/radarsimpy/0d312d496e0b99dedd8c1fbbfb5de978bc374c0f/models/vehicles/gasoline_tanker.stl -------------------------------------------------------------------------------- /models/vehicles/honda_cb400.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radarsimx/radarsimpy/0d312d496e0b99dedd8c1fbbfb5de978bc374c0f/models/vehicles/honda_cb400.stl -------------------------------------------------------------------------------- /models/vehicles/lamborgini_aventador.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radarsimx/radarsimpy/0d312d496e0b99dedd8c1fbbfb5de978bc374c0f/models/vehicles/lamborgini_aventador.stl -------------------------------------------------------------------------------- /models/vehicles/scania_truck.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radarsimx/radarsimpy/0d312d496e0b99dedd8c1fbbfb5de978bc374c0f/models/vehicles/scania_truck.stl -------------------------------------------------------------------------------- /models/vehicles/tesla_model_s.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radarsimx/radarsimpy/0d312d496e0b99dedd8c1fbbfb5de978bc374c0f/models/vehicles/tesla_model_s.stl -------------------------------------------------------------------------------- /references/An Efficient and Robust Ray–Box Intersection Algorithm.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radarsimx/radarsimpy/0d312d496e0b99dedd8c1fbbfb5de978bc374c0f/references/An Efficient and Robust Ray–Box Intersection Algorithm.pdf -------------------------------------------------------------------------------- /references/Gordon_1975_Far-field approximations to the Kirchoff-Helmholtz representations of scattered.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radarsimx/radarsimpy/0d312d496e0b99dedd8c1fbbfb5de978bc374c0f/references/Gordon_1975_Far-field approximations to the Kirchoff-Helmholtz representations of scattered.pdf -------------------------------------------------------------------------------- /references/Guerrero_2018_Frequency-modulated continuous-wave radar in automotive applications.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radarsimx/radarsimpy/0d312d496e0b99dedd8c1fbbfb5de978bc374c0f/references/Guerrero_2018_Frequency-modulated continuous-wave radar in automotive applications.pdf -------------------------------------------------------------------------------- /references/Hung_2000_Matrix-construction calibration method for antenna arrays.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radarsimx/radarsimpy/0d312d496e0b99dedd8c1fbbfb5de978bc374c0f/references/Hung_2000_Matrix-construction calibration method for antenna arrays.pdf -------------------------------------------------------------------------------- /references/Hwang_2015_Radar cross section analysis using physical optics and its applications to.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radarsimx/radarsimpy/0d312d496e0b99dedd8c1fbbfb5de978bc374c0f/references/Hwang_2015_Radar cross section analysis using physical optics and its applications to.pdf -------------------------------------------------------------------------------- /references/Karras_2012_Maximizing parallelism in the construction of BVHs, octrees, and k-d trees.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radarsimx/radarsimpy/0d312d496e0b99dedd8c1fbbfb5de978bc374c0f/references/Karras_2012_Maximizing parallelism in the construction of BVHs, octrees, and k-d trees.pdf -------------------------------------------------------------------------------- /references/Mahafza_2015_Chapter 4 radar detection.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radarsimx/radarsimpy/0d312d496e0b99dedd8c1fbbfb5de978bc374c0f/references/Mahafza_2015_Chapter 4 radar detection.pdf -------------------------------------------------------------------------------- /references/Reflection and Refraction of Plane EM Waves.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radarsimx/radarsimpy/0d312d496e0b99dedd8c1fbbfb5de978bc374c0f/references/Reflection and Refraction of Plane EM Waves.pdf -------------------------------------------------------------------------------- /references/Zhao_2012_Fast physical optics calculation for SAR imaging of complex scatterers.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radarsimx/radarsimpy/0d312d496e0b99dedd8c1fbbfb5de978bc374c0f/references/Zhao_2012_Fast physical optics calculation for SAR imaging of complex scatterers.pdf -------------------------------------------------------------------------------- /references/add_phase_noise.m: -------------------------------------------------------------------------------- 1 | function Sout = add_phase_noise( Sin, Fs, phase_noise_freq, phase_noise_power, VALIDATION_ON ) 2 | % 3 | % function Sout = add_phase_noise( Sin, Fs, phase_noise_freq, phase_noise_power, VALIDATION_ON ) 4 | % 5 | % Oscillator Phase Noise Model 6 | % 7 | % INPUT: 8 | % Sin - input COMPLEX signal 9 | % Fs - sampling frequency ( in Hz ) of Sin 10 | % phase_noise_freq - frequencies at which SSB Phase Noise is defined (offset from carrier in Hz) 11 | % phase_noise_power - SSB Phase Noise power ( in dBc/Hz ) 12 | % VALIDATION_ON - 1 - perform validation, 0 - don't perfrom validation 13 | % 14 | % OUTPUT: 15 | % Sout - output COMPLEX phase noised signal 16 | % 17 | % NOTE: 18 | % Input signal should be complex 19 | % 20 | % EXAMPLE ( How to use add_phase_noise ): 21 | % Assume SSB Phase Noise is specified as follows: 22 | % ------------------------------------------------------- 23 | % | Offset From Carrier | Phase Noise | 24 | % ------------------------------------------------------- 25 | % | 1 kHz | -84 dBc/Hz | 26 | % | 10 kHz | -100 dBc/Hz | 27 | % | 100 kHz | -96 dBc/Hz | 28 | % | 1 MHz | -109 dBc/Hz | 29 | % | 10 MHz | -122 dBc/Hz | 30 | % ------------------------------------------------------- 31 | % 32 | % Assume that we have 10000 samples of complex sinusoid of frequency 3 KHz 33 | % sampled at frequency 40MHz: 34 | % 35 | % Fc = 3e3; % carrier frequency 36 | % Fs = 40e6; % sampling frequency 37 | % t = 0:9999; 38 | % S = exp(j*2*pi*Fc/Fs*t); % complex sinusoid 39 | % 40 | % Then, to produce phase noised signal S1 from the original signal S run follows: 41 | % 42 | % Fs = 40e6; 43 | % phase_noise_freq = [ 1e3, 10e3, 100e3, 1e6, 10e6 ]; % Offset From Carrier 44 | % phase_noise_power = [ -84, -100, -96, -109, -122 ]; % Phase Noise power 45 | % S1 = add_phase_noise( S, Fs, phase_noise_freq, phase_noise_power ); 46 | 47 | % Version 1.0 48 | % Alex Bur-Guy, October 2005 49 | % alex@wavion.co.il 50 | % 51 | % Revisions: 52 | % Version 1.5 - Comments. Validation. 53 | % Version 1.0 - initial version 54 | 55 | % NOTES: 56 | % 1) The presented model is a simple VCO phase noise model based on the following consideration: 57 | % If the output of an oscillator is given as V(t) = V0 * cos( w0*t + phi(t) ), 58 | % then phi(t) is defined as the phase noise. In cases of small noise 59 | % sources (a valid assumption in any usable system), a narrowband modulation approximation can 60 | % be used to express the oscillator output as: 61 | % 62 | % V(t) = V0 * cos( w0*t + phi(t) ) 63 | % 64 | % = V0 * [cos(w0*t)*cos(phi(t)) - sin(w0*t)*sin(phi(t)) ] 65 | % 66 | % ~ V0 * [cos(w0*t) - sin(w0*t)*phi(t)] 67 | % 68 | % This shows that phase noise will be mixed with the carrier to produce sidebands around the carrier. 69 | % 70 | % 71 | % 2) In other words, exp(j*x) ~ (1+j*x) for small x 72 | % 73 | % 3) Phase noise = 0 dBc/Hz at freq. offset of 0 Hz 74 | % 75 | % 4) The lowest phase noise level is defined by the input SSB phase noise power at the maximal 76 | % freq. offset from DC. (IT DOES NOT BECOME EQUAL TO ZERO ) 77 | % 78 | % The generation process is as follows: 79 | % First of all we interpolate (in log-scale) SSB phase noise power spectrum in M 80 | % equally spaced points (on the interval [0 Fs/2] including bounds ). 81 | % 82 | % After that we calculate required frequency shape of the phase noise by X(m) = sqrt(P(m)*dF(m)) 83 | % and after that complement it by the symmetrical negative part of the spectrum. 84 | % 85 | % After that we generate AWGN of power 1 in the freq domain and multiply it sample-by-sample to 86 | % the calculated shape 87 | % 88 | % Finally we perform 2*M-2 points IFFT to such generated noise 89 | % ( See comments inside the code ) 90 | % 91 | % 0 dBc/Hz 92 | % \ / 93 | % \ / 94 | % \ / 95 | % \P dBc/Hz / 96 | % .\ / 97 | % . \ / 98 | % . \ / 99 | % . \____________________________________________/ /_ This level is defined by the phase_noise_power at the maximal freq. offset from DC defined in phase_noise_freq 100 | % . \ 101 | % |__| _|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__ (N points) 102 | % 0 dF Fs/2 Fs 103 | % DC 104 | % 105 | % 106 | % For some basics about Oscillator phase noise see: 107 | % http://www.circuitsage.com/pll/plldynamics.pdf 108 | % 109 | % http://www.wj.com/pdf/technotes/LO_phase_noise.pdf 110 | 111 | if nargin < 5 112 | VALIDATION_ON = 0; 113 | end 114 | 115 | % Check Input 116 | error( nargchk(4,5,nargin) ); 117 | 118 | if ~any( imag(Sin(:)) ) 119 | error( 'Input signal should be complex signal' ); 120 | end 121 | if max(phase_noise_freq) >= Fs/2 122 | error( 'Maximal frequency offset should be less than Fs/2'); 123 | end 124 | 125 | % Make sure phase_noise_freq and phase_noise_power are the row vectors 126 | phase_noise_freq = phase_noise_freq(:).'; 127 | phase_noise_power = phase_noise_power(:).'; 128 | if length( phase_noise_freq ) ~= length( phase_noise_power ) 129 | error('phase_noise_freq and phase_noise_power should be of the same length'); 130 | end 131 | 132 | % Sort phase_noise_freq and phase_noise_power 133 | [phase_noise_freq, indx] = sort( phase_noise_freq ); 134 | phase_noise_power = phase_noise_power( indx ); 135 | 136 | % Add 0 dBc/Hz @ DC 137 | if ~any(phase_noise_freq == 0) 138 | phase_noise_power = [ 0, phase_noise_power ]; 139 | phase_noise_freq = [0, phase_noise_freq]; 140 | end 141 | 142 | % Calculate input length 143 | N = prod( size( Sin ) ); 144 | 145 | % Define M number of points (frequency resolution) in the positive spectrum 146 | % (M equally spaced points on the interval [0 Fs/2] including bounds), 147 | % then the number of points in the negative spectrum will be M-2 148 | % ( interval (Fs/2, Fs) not including bounds ) 149 | % 150 | % The total number of points in the frequency domain will be 2*M-2, and if we want 151 | % to get the same length as the input signal, then 152 | % 2*M-2 = N 153 | % M-1 = N/2 154 | % M = N/2 + 1 155 | % 156 | % So, if N is even then M = N/2 + 1, and if N is odd we will take M = (N+1)/2 + 1 157 | % 158 | if rem(N,2), % N odd 159 | M = (N+1)/2 + 1; 160 | else 161 | M = N/2 + 1; 162 | end 163 | 164 | 165 | % Equally spaced partitioning of the half spectrum 166 | F = linspace( 0, Fs/2, M ); % Freq. Grid 167 | dF = [diff(F) F(end)-F(end-1)]; % Delta F 168 | 169 | 170 | % Perform interpolation of phase_noise_power in log-scale 171 | intrvlNum = length( phase_noise_freq ); 172 | logP = zeros( 1, M ); 173 | for intrvlIndex = 1 : intrvlNum, 174 | leftBound = phase_noise_freq(intrvlIndex); 175 | t1 = phase_noise_power(intrvlIndex); 176 | if intrvlIndex == intrvlNum 177 | rightBound = Fs/2; 178 | t2 = phase_noise_power(end); 179 | inside = find( F>=leftBound & F<=rightBound ); 180 | else 181 | rightBound = phase_noise_freq(intrvlIndex+1); 182 | t2 = phase_noise_power(intrvlIndex+1); 183 | inside = find( F>=leftBound & F-' ); % approximation ( 1+j*x ) 250 | xlabel('Frequency [Hz]'); 251 | ylabel('dBc/Hz'); 252 | legend( ... 253 | 'Input SSB phase noise power', ... 254 | 'Interpolated SSB phase noise power', ... 255 | 'Positive spectrum of the generated phase noise exp(j*x)', ... 256 | 'Positive spectrum of the approximation ( 1+j*x )' ... 257 | ); 258 | end 259 | -------------------------------------------------------------------------------- /references/exact_and_approximate_pd_formulas_in_frsp.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/radarsimx/radarsimpy/0d312d496e0b99dedd8c1fbbfb5de978bc374c0f/references/exact_and_approximate_pd_formulas_in_frsp.pdf -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | numpy 2 | scipy 3 | trimesh 4 | cython 5 | setuptools 6 | pytest 7 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | """ 2 | Setup script for a Python package "radarsimpy" 3 | 4 | --- 5 | 6 | - Copyright (C) 2018 - PRESENT radarsimx.com 7 | - E-mail: info@radarsimx.com 8 | - Website: https://radarsimx.com 9 | 10 | :: 11 | 12 | ██████╗ █████╗ ██████╗ █████╗ ██████╗ ███████╗██╗███╗ ███╗██╗ ██╗ 13 | ██╔══██╗██╔══██╗██╔══██╗██╔══██╗██╔══██╗██╔════╝██║████╗ ████║╚██╗██╔╝ 14 | ██████╔╝███████║██║ ██║███████║██████╔╝███████╗██║██╔████╔██║ ╚███╔╝ 15 | ██╔══██╗██╔══██║██║ ██║██╔══██║██╔══██╗╚════██║██║██║╚██╔╝██║ ██╔██╗ 16 | ██║ ██║██║ ██║██████╔╝██║ ██║██║ ██║███████║██║██║ ╚═╝ ██║██╔╝ ██╗ 17 | ╚═╝ ╚═╝╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ 18 | 19 | """ 20 | 21 | # Import required system and platform modules 22 | import platform 23 | import argparse 24 | import sys 25 | import os 26 | from os.path import join as pjoin 27 | from setuptools import setup 28 | from setuptools import Extension 29 | from Cython.Distutils import build_ext 30 | from Cython.Build import cythonize 31 | import numpy 32 | 33 | # Set up command line argument parser for build configuration 34 | ap = argparse.ArgumentParser() 35 | ap.add_argument( 36 | "-t", "--tier", required=False, help="Build tier, choose `free` or `standard`" 37 | ) 38 | ap.add_argument( 39 | "-a", "--arch", required=False, help="Build architecture, choose `cpu` or `gpu`" 40 | ) 41 | 42 | # Parse command line arguments, separating setup.py specific args from pass-through args 43 | args, unknown = ap.parse_known_args() 44 | sys.argv = [sys.argv[0]] + unknown 45 | 46 | # Determine build tier (free vs standard) 47 | # Default to standard if not specified 48 | if args.tier is None: 49 | ARG_TIER = "standard" 50 | elif args.tier.lower() == "free": 51 | ARG_TIER = "free" 52 | elif args.tier.lower() == "standard": 53 | ARG_TIER = "standard" 54 | else: 55 | raise ValueError("Invalid --tier parameters, please choose 'free' or 'standard'") 56 | 57 | # Determine build architecture (CPU vs GPU) 58 | # Default to CPU if not specified 59 | if args.arch is None: 60 | ARG_ARCH = "cpu" 61 | elif args.arch.lower() == "cpu": 62 | ARG_ARCH = "cpu" 63 | elif args.arch.lower() == "gpu": 64 | ARG_ARCH = "gpu" 65 | else: 66 | raise ValueError("Invalid --arch parameters, please choose 'cpu' or 'gpu'") 67 | 68 | # Detect operating system for platform-specific configurations 69 | os_type = platform.system() # 'Linux', 'Windows', 'macOS' 70 | 71 | # Set platform-specific build configurations 72 | if os_type == "Linux": 73 | # Linux-specific: Set rpath to find shared libraries in the same directory 74 | LINK_ARGS = ["-Wl,-rpath,$ORIGIN"] 75 | LIB_DIRS = [ 76 | "src/radarsimcpp/build", 77 | "src/radarsimcpp/hdf5-lib-build/libs/lib_linux_gcc11_x86_64/lib", 78 | ] 79 | LIBS = ["hdf5", "hdf5_cpp", "hdf5_hl", "hdf5_hl_cpp"] 80 | if args.arch == "gpu": 81 | NVCC = "nvcc" 82 | CUDALIB = "lib64" 83 | LIBS = LIBS + ["cudart"] 84 | elif os_type == "Darwin": # macOS 85 | LIBS = [] 86 | if platform.processor() == "arm": # M1/M2 processors 87 | LINK_ARGS = ["-Wl,-rpath,$ORIGIN"] 88 | LIB_DIRS = ["src/radarsimcpp/build"] 89 | else: # Intel processors 90 | LINK_ARGS = ["-Wl,-rpath,$ORIGIN"] 91 | LIB_DIRS = ["src/radarsimcpp/build"] 92 | elif os_type == "Windows": 93 | LINK_ARGS = [] 94 | LIB_DIRS = ["src/radarsimcpp/build/Release"] 95 | LIBS = [] 96 | if args.arch == "gpu": 97 | NVCC = "nvcc.exe" 98 | CUDALIB = "lib\\x64" 99 | LIBS = ["cudart"] 100 | 101 | def find_in_path(name, path): 102 | """ 103 | Iterates over the directories in the search path by splitting the path string using 104 | os.pathsep as the delimiter. os.pathsep is a string that represents the separator 105 | used in the PATH environment variable on the current operating system 106 | (e.g., : on Unix-like systems and ; on Windows). 107 | 108 | :param name: The name of the file 109 | :type name: str 110 | :param path: The search path 111 | :type path: str 112 | :return: The absolute path of the file 113 | :rtype: str 114 | """ 115 | for path_name in path.split(os.pathsep): 116 | binpath = pjoin(path_name, name) 117 | if os.path.exists(binpath): 118 | return os.path.abspath(binpath) 119 | return None 120 | 121 | def locate_cuda(): 122 | """ 123 | Locate the CUDA installation on the system 124 | 125 | :raises EnvironmentError: The nvcc binary could not be located in your $PATH. 126 | Either add it to your path, or set $CUDA_PATH 127 | :raises EnvironmentError: The CUDA path could not be located in 128 | :return: dict with keys 'home', 'nvcc', 'include', and 'lib64' 129 | and values giving the absolute path to each directory. 130 | """ 131 | # The code first checks if the CUDA_PATH environment variable is set. 132 | # If it is, it uses the value of CUDA_PATH as the CUDA installation directory 133 | # and constructs the path to the nvcc binary (NVIDIA CUDA Compiler) inside that directory. 134 | if "CUDA_PATH" in os.environ: 135 | home = os.environ["CUDA_PATH"] 136 | nvcc = pjoin(home, "bin", NVCC) 137 | else: 138 | # If the CUDA_PATH environment variable is not set, it searches for the nvcc 139 | # binary in the system's PATH environment variable. If nvcc is not found in 140 | # the PATH, it raises an EnvironmentError. Otherwise, it sets the home variable 141 | # to the parent directory of nvcc. 142 | default_path = pjoin(os.sep, "usr", "local", "cuda", "bin") 143 | nvcc = find_in_path(NVCC, os.environ["PATH"] + os.pathsep + default_path) 144 | if nvcc is None: 145 | raise EnvironmentError( 146 | "The nvcc binary could not be located in your $PATH. " 147 | "Either add it to your path, or set $CUDA_PATH" 148 | ) 149 | home = os.path.dirname(os.path.dirname(nvcc)) 150 | 151 | cudaconfig = { 152 | "home": home, 153 | "nvcc": nvcc, 154 | "include": pjoin(home, "include"), 155 | "lib64": pjoin(home, CUDALIB), 156 | } 157 | for key, val in cudaconfig.items(): 158 | if not os.path.exists(val): 159 | raise EnvironmentError( 160 | "The CUDA " + key + " path could not be located in " + val 161 | ) 162 | 163 | return cudaconfig 164 | 165 | # Configure build macros based on tier and architecture 166 | if ARG_TIER == "free": 167 | if ARG_ARCH == "gpu": 168 | MACROS = [ 169 | ("NPY_NO_DEPRECATED_API", "NPY_1_7_API_VERSION"), # NumPy API version 170 | ("_FREETIER_", 1), # Enable free tier features 171 | ("_CUDA_", None), # Enable CUDA support 172 | ] 173 | elif ARG_ARCH == "cpu": 174 | MACROS = [ 175 | ("NPY_NO_DEPRECATED_API", "NPY_1_7_API_VERSION"), 176 | ("_FREETIER_", 1) 177 | ] 178 | else: # standard tier 179 | if ARG_ARCH == "gpu": 180 | MACROS = [ 181 | ("NPY_NO_DEPRECATED_API", "NPY_1_7_API_VERSION"), 182 | ("_CUDA_", None) 183 | ] 184 | elif ARG_ARCH == "cpu": 185 | MACROS = [("NPY_NO_DEPRECATED_API", "NPY_1_7_API_VERSION")] 186 | 187 | # Set include directories for header files 188 | INCLUDE_DIRS = ["src/radarsimcpp/includes", "src/radarsimcpp/includes/rsvector"] 189 | 190 | # Add CUDA-specific configurations if GPU build is selected 191 | if ARG_ARCH == "gpu": 192 | CUDA = locate_cuda() 193 | INCLUDE_DIRS = INCLUDE_DIRS + [CUDA["include"]] 194 | LIB_DIRS = LIB_DIRS + [CUDA["lib64"]] 195 | 196 | # Define Cython extension modules to be built 197 | ext_modules = [ 198 | # Core radar simulation C++ wrapper 199 | Extension( 200 | "radarsimpy.lib.cp_radarsimc", 201 | ["src/radarsimpy/lib/cp_radarsimc.pyx"], 202 | define_macros=MACROS, 203 | include_dirs=INCLUDE_DIRS, 204 | libraries=["radarsimcpp"] + LIBS, 205 | library_dirs=LIB_DIRS, 206 | extra_link_args=LINK_ARGS, 207 | ), 208 | # High-level simulator interface 209 | Extension( 210 | "radarsimpy.simulator", 211 | ["src/radarsimpy/simulator.pyx"], 212 | define_macros=MACROS, 213 | include_dirs=INCLUDE_DIRS, 214 | libraries=["radarsimcpp"] + LIBS, 215 | library_dirs=LIB_DIRS, 216 | extra_link_args=LINK_ARGS, 217 | ), 218 | ] 219 | 220 | # Configure and run setup 221 | setup( 222 | name="radarsimpy", 223 | cmdclass={"build_ext": build_ext}, # Use Cython's build_ext 224 | ext_modules=cythonize( 225 | ext_modules, 226 | annotate=False, # Don't generate HTML annotation files 227 | compiler_directives={"language_level": "3"}, # Use Python 3 syntax 228 | ), 229 | include_dirs=[numpy.get_include()], # Include NumPy headers 230 | ) 231 | -------------------------------------------------------------------------------- /src/radarsimpy/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | A Radar Simulator for Python 3 | 4 | RadarSimPy is a powerful and versatile Python-based Radar Simulator that models 5 | radar transceivers and simulates baseband data from point targets and 3D models. 6 | Its signal processing tools offer range/Doppler processing, direction of arrival 7 | estimation, and beamforming using various cutting-edge techniques, and you can 8 | even characterize radar detection using Swerling's models. Whether you're a beginner 9 | or an advanced user, RadarSimPy is the perfect tool for anyone looking to develop 10 | new radar technologies or expand their knowledge of radar systems. 11 | 12 | --- 13 | 14 | - Copyright (C) 2018 - PRESENT radarsimx.com 15 | - E-mail: info@radarsimx.com 16 | - Website: https://radarsimx.com 17 | 18 | :: 19 | 20 | ██████╗ █████╗ ██████╗ █████╗ ██████╗ ███████╗██╗███╗ ███╗██╗ ██╗ 21 | ██╔══██╗██╔══██╗██╔══██╗██╔══██╗██╔══██╗██╔════╝██║████╗ ████║╚██╗██╔╝ 22 | ██████╔╝███████║██║ ██║███████║██████╔╝███████╗██║██╔████╔██║ ╚███╔╝ 23 | ██╔══██╗██╔══██║██║ ██║██╔══██║██╔══██╗╚════██║██║██║╚██╔╝██║ ██╔██╗ 24 | ██║ ██║██║ ██║██████╔╝██║ ██║██║ ██║███████║██║██║ ╚═╝ ██║██╔╝ ██╗ 25 | ╚═╝ ╚═╝╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ 26 | 27 | """ 28 | 29 | from .radar import Radar 30 | from .transmitter import Transmitter 31 | from .receiver import Receiver 32 | 33 | __version__ = "13.1.0" 34 | -------------------------------------------------------------------------------- /src/radarsimpy/includes/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | A Python module for radar simulation 3 | 4 | --- 5 | 6 | - Copyright (C) 2018 - PRESENT radarsimx.com 7 | - E-mail: info@radarsimx.com 8 | - Website: https://radarsimx.com 9 | 10 | :: 11 | 12 | ██████╗ █████╗ ██████╗ █████╗ ██████╗ ███████╗██╗███╗ ███╗██╗ ██╗ 13 | ██╔══██╗██╔══██╗██╔══██╗██╔══██╗██╔══██╗██╔════╝██║████╗ ████║╚██╗██╔╝ 14 | ██████╔╝███████║██║ ██║███████║██████╔╝███████╗██║██╔████╔██║ ╚███╔╝ 15 | ██╔══██╗██╔══██║██║ ██║██╔══██║██╔══██╗╚════██║██║██║╚██╔╝██║ ██╔██╗ 16 | ██║ ██║██║ ██║██████╔╝██║ ██║██║ ██║███████║██║██║ ╚═╝ ██║██╔╝ ██╗ 17 | ╚═╝ ╚═╝╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ 18 | 19 | """ 20 | -------------------------------------------------------------------------------- /src/radarsimpy/includes/radarsimc.pxd: -------------------------------------------------------------------------------- 1 | # distutils: language = c++ 2 | """ 3 | A Python module for radar simulation 4 | 5 | --- 6 | 7 | - Copyright (C) 2018 - PRESENT radarsimx.com 8 | - E-mail: info@radarsimx.com 9 | - Website: https://radarsimx.com 10 | 11 | :: 12 | 13 | ██████╗ █████╗ ██████╗ █████╗ ██████╗ ███████╗██╗███╗ ███╗██╗ ██╗ 14 | ██╔══██╗██╔══██╗██╔══██╗██╔══██╗██╔══██╗██╔════╝██║████╗ ████║╚██╗██╔╝ 15 | ██████╔╝███████║██║ ██║███████║██████╔╝███████╗██║██╔████╔██║ ╚███╔╝ 16 | ██╔══██╗██╔══██║██║ ██║██╔══██║██╔══██╗╚════██║██║██║╚██╔╝██║ ██╔██╗ 17 | ██║ ██║██║ ██║██████╔╝██║ ██║██║ ██║███████║██║██║ ╚═╝ ██║██╔╝ ██╗ 18 | ╚═╝ ╚═╝╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ 19 | 20 | """ 21 | 22 | #------------------------------------------------------------------------------ 23 | # Imports 24 | #------------------------------------------------------------------------------ 25 | from libcpp cimport bool 26 | from libcpp.complex cimport complex as cpp_complex 27 | from libcpp.string cimport string 28 | from radarsimpy.includes.rsvector cimport Vec3, Vec2 29 | from radarsimpy.includes.type_def cimport int_t, vector 30 | 31 | #------------------------------------------------------------------------------ 32 | # Memory Management 33 | #------------------------------------------------------------------------------ 34 | cdef extern from "libs/mem_lib.hpp": 35 | cdef void Mem_Copy[T](T * ptr, int_t size, vector[T] &vect) except + 36 | cdef void Mem_Copy_Complex[T](T * ptr_real, T * ptr_imag, 37 | int_t size, vector[cpp_complex[T]] &vect) except + 38 | cdef void Mem_Copy_Vec3[T](T *ptr_x, T *ptr_y, T *ptr_z, 39 | int_t size, vector[Vec3[T]] &vect) except + 40 | 41 | cdef extern from "libs/free_tier.hpp": 42 | cdef int IsFreeTier() except + 43 | 44 | cdef extern from "type_def.hpp": 45 | cdef enum ErrorType: 46 | NO_ERROR 47 | ERROR_TOO_MANY_RAYS_PER_GRID 48 | 49 | #------------------------------------------------------------------------------ 50 | # Target 51 | #------------------------------------------------------------------------------ 52 | cdef extern from "target.hpp": 53 | cdef cppclass Target[T]: 54 | Target() except + 55 | Target(const T * points, 56 | const int_t * cells, 57 | const int_t & cell_size, 58 | const Vec3[T] & origin, 59 | const vector[Vec3[T]] & location_array, 60 | const vector[Vec3[T]] & speed_array, 61 | const vector[Vec3[T]] & rotation_array, 62 | const vector[Vec3[T]] & rotation_rate_array, 63 | const cpp_complex[T] & ep, 64 | const cpp_complex[T] & mu, 65 | const bool & skip_diffusion) except + 66 | Target(const T * points, 67 | const int_t * cells, 68 | const int_t & cell_size) except + 69 | Target(const T * points, 70 | const int_t * cells, 71 | const int_t & cell_size, 72 | const Vec3[T] & origin, 73 | const Vec3[T] & location, 74 | const Vec3[T] & speed, 75 | const Vec3[T] & rotation, 76 | const Vec3[T] & rotation_rate, 77 | const bool & skip_diffusion) except + 78 | 79 | #------------------------------------------------------------------------------ 80 | # Ray 81 | #------------------------------------------------------------------------------ 82 | cdef extern from "ray.hpp": 83 | cdef cppclass Ray[T]: 84 | Ray() except + 85 | Vec3[T] * direction_ 86 | Vec3[T] * location_ 87 | int reflections_ 88 | 89 | #------------------------------------------------------------------------------ 90 | # RCS 91 | #------------------------------------------------------------------------------ 92 | cdef extern from "simulator_rcs.hpp": 93 | cdef cppclass RcsSimulator[T]: 94 | RcsSimulator() except + 95 | vector[T] Run(vector[Target[float]] & targets, 96 | vector[Vec3[T]] inc_dir_array, 97 | vector[Vec3[T]] obs_dir_array, 98 | Vec3[cpp_complex[T]] inc_polarization, 99 | Vec3[cpp_complex[T]] obs_polarization, 100 | T frequency, 101 | T density) except + 102 | 103 | #------------------------------------------------------------------------------ 104 | # Point Cloud 105 | #------------------------------------------------------------------------------ 106 | cdef extern from "simulator_lidar.hpp": 107 | cdef cppclass LidarSimulator[T]: 108 | LidarSimulator() except + 109 | void AddTarget(const Target[T] & target) 110 | void Run(const vector[T] & phi, 111 | const vector[T] & theta, 112 | const Vec3[T] & position) 113 | 114 | vector[Ray[T]] cloud_ 115 | 116 | #------------------------------------------------------------------------------ 117 | # Point Target 118 | #------------------------------------------------------------------------------ 119 | cdef extern from "point.hpp": 120 | cdef cppclass Point[T]: 121 | Point() except + 122 | Point(const vector[Vec3[T]] & loc, 123 | const Vec3[T] & speed, 124 | const vector[T] & rcs, 125 | const vector[T] & phs) except + 126 | 127 | #------------------------------------------------------------------------------ 128 | # Transmitter Components 129 | #------------------------------------------------------------------------------ 130 | cdef extern from "transmitter.hpp": 131 | cdef cppclass TxChannel[T]: 132 | TxChannel() except + 133 | TxChannel(const Vec3[T] & location, 134 | const Vec3[cpp_complex[T]] & polar, 135 | const vector[T] & phi, 136 | const vector[T] & phi_ptn, 137 | const vector[T] & theta, 138 | const vector[T] & theta_ptn, 139 | const T & antenna_gain, 140 | const vector[T] & mod_t, 141 | const vector[cpp_complex[T]] & mod_var, 142 | const vector[cpp_complex[T]] & pulse_mod, 143 | const T & delay, 144 | const T & grid) except + 145 | 146 | cdef cppclass Transmitter[H, L]: 147 | Transmitter() except + 148 | Transmitter(const L & tx_power, 149 | const vector[H] & freq, 150 | const vector[H] & freq_time, 151 | const vector[H] & freq_offset, 152 | const vector[H] & pulse_start_time) except + 153 | Transmitter(const L & tx_power, 154 | const vector[H] & freq, 155 | const vector[H] & freq_time, 156 | const vector[H] & freq_offset, 157 | const vector[H] & pulse_start_time, 158 | const vector[cpp_complex[H]] & phase_noise) except + 159 | void AddChannel(const TxChannel[L] & channel) 160 | 161 | #------------------------------------------------------------------------------ 162 | # Receiver Components 163 | #------------------------------------------------------------------------------ 164 | cdef extern from "receiver.hpp": 165 | cdef cppclass RxChannel[T]: 166 | RxChannel() except + 167 | RxChannel(const Vec3[T] & location, 168 | const Vec3[cpp_complex[T]] & polar, 169 | const vector[T] & phi, 170 | const vector[T] & phi_ptn, 171 | const vector[T] & theta, 172 | const vector[T] & theta_ptn, 173 | const T & antenna_gain) except + 174 | 175 | cdef cppclass Receiver[T]: 176 | Receiver() except + 177 | Receiver(const T & fs, 178 | const T & rf_gain, 179 | const T & resistor, 180 | const T & baseband_gain, 181 | const T & baseband_bw) except + 182 | void AddChannel(const RxChannel[T] & channel) 183 | 184 | #------------------------------------------------------------------------------ 185 | # Radar System 186 | #------------------------------------------------------------------------------ 187 | cdef extern from "radar.hpp": 188 | cdef cppclass Radar[H, L]: 189 | Radar() except + 190 | Radar(Transmitter[H, L] & tx, 191 | Receiver[L] & rx, 192 | vector[H] & frame_start_time, 193 | vector[Vec3[L]] & location_array, 194 | Vec3[L] speed_array, 195 | vector[Vec3[L]] & rotation_array, 196 | Vec3[L] rotrate_array) except + 197 | void InitBaseband(H *bb_real, 198 | H *bb_imag) except + 199 | void SyncBaseband() except + 200 | void FreeDeviceMemory() except + 201 | 202 | #------------------------------------------------------------------------------ 203 | # Simulators 204 | #------------------------------------------------------------------------------ 205 | cdef extern from "simulator_point.hpp": 206 | cdef cppclass PointSimulator[H, L]: 207 | PointSimulator() except + 208 | void Run(Radar[H, L] & radar, 209 | vector[Point[L]] & points) 210 | 211 | cdef extern from "simulator_mesh.hpp": 212 | cdef cppclass MeshSimulator[H, L]: 213 | MeshSimulator() except + 214 | ErrorType Run(Radar[H, L] & radar, 215 | vector[Target[L]] & targets, 216 | int level, 217 | L density, 218 | Vec2[int_t] ray_filter, 219 | bool back_propagating, 220 | string log_path, 221 | bool debug) 222 | 223 | cdef extern from "simulator_interference.hpp": 224 | cdef cppclass InterferenceSimulator[H, L]: 225 | InterferenceSimulator() except + 226 | void Run(Radar[H, L] & radar, 227 | Radar[H, L] & interf_radar) 228 | -------------------------------------------------------------------------------- /src/radarsimpy/includes/rsvector.pxd: -------------------------------------------------------------------------------- 1 | # distutils: language = c++ 2 | """ 3 | A Python module for radar simulation 4 | 5 | --- 6 | 7 | - Copyright (C) 2018 - PRESENT radarsimx.com 8 | - E-mail: info@radarsimx.com 9 | - Website: https://radarsimx.com 10 | 11 | :: 12 | 13 | ██████╗ █████╗ ██████╗ █████╗ ██████╗ ███████╗██╗███╗ ███╗██╗ ██╗ 14 | ██╔══██╗██╔══██╗██╔══██╗██╔══██╗██╔══██╗██╔════╝██║████╗ ████║╚██╗██╔╝ 15 | ██████╔╝███████║██║ ██║███████║██████╔╝███████╗██║██╔████╔██║ ╚███╔╝ 16 | ██╔══██╗██╔══██║██║ ██║██╔══██║██╔══██╗╚════██║██║██║╚██╔╝██║ ██╔██╗ 17 | ██║ ██║██║ ██║██████╔╝██║ ██║██║ ██║███████║██║██║ ╚═╝ ██║██╔╝ ██╗ 18 | ╚═╝ ╚═╝╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ 19 | 20 | """ 21 | 22 | """ 23 | rsvector - Vector library module 24 | """ 25 | 26 | cdef extern from "rsvector.hpp" namespace "rsv" nogil: 27 | # 3D vector class 28 | cdef cppclass Vec3[T]: 29 | Vec3() except + 30 | Vec3(const T&, const T&, const T&) except + 31 | Vec3(T*) except + 32 | 33 | Vec3& operator=(const Vec3&) 34 | T& operator[](const unsigned int&) 35 | 36 | # 2D vector class 37 | cdef cppclass Vec2[T]: 38 | Vec2() except + 39 | Vec2(const T&, const T&) except + 40 | Vec2(T*) except + 41 | 42 | Vec2& operator=(const Vec2&) 43 | T& operator[](const unsigned int&) 44 | -------------------------------------------------------------------------------- /src/radarsimpy/includes/type_def.pxd: -------------------------------------------------------------------------------- 1 | # distutils: language = c++ 2 | """ 3 | A Python module for radar simulation 4 | 5 | --- 6 | 7 | - Copyright (C) 2018 - PRESENT radarsimx.com 8 | - E-mail: info@radarsimx.com 9 | - Website: https://radarsimx.com 10 | 11 | :: 12 | 13 | ██████╗ █████╗ ██████╗ █████╗ ██████╗ ███████╗██╗███╗ ███╗██╗ ██╗ 14 | ██╔══██╗██╔══██╗██╔══██╗██╔══██╗██╔══██╗██╔════╝██║████╗ ████║╚██╗██╔╝ 15 | ██████╔╝███████║██║ ██║███████║██████╔╝███████╗██║██╔████╔██║ ╚███╔╝ 16 | ██╔══██╗██╔══██║██║ ██║██╔══██║██╔══██╗╚════██║██║██║╚██╔╝██║ ██╔██╗ 17 | ██║ ██║██║ ██║██████╔╝██║ ██║██║ ██║███████║██║██║ ╚═╝ ██║██╔╝ ██╗ 18 | ╚═╝ ╚═╝╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ 19 | 20 | """ 21 | 22 | 23 | ctypedef int int_t 24 | ctypedef unsigned int uint_t 25 | 26 | ctypedef float float_t 27 | 28 | 29 | """ 30 | C++ vector 31 | 32 | Copy from Cython's libcpp/vector.pxd 33 | Remove ALLOCATOR=*, otherwise 'const &' doesn't work 34 | """ 35 | cdef extern from "" namespace "std" nogil: 36 | cdef cppclass vector[T]: 37 | ctypedef size_t size_type 38 | ctypedef ptrdiff_t difference_type 39 | 40 | cppclass const_iterator 41 | cppclass iterator: 42 | iterator() except + 43 | iterator(iterator&) except + 44 | T& operator*() 45 | iterator operator++() 46 | iterator operator--() 47 | iterator operator++(int) 48 | iterator operator--(int) 49 | iterator operator+(size_type) 50 | iterator operator-(size_type) 51 | difference_type operator-(iterator) 52 | difference_type operator-(const_iterator) 53 | bint operator==(iterator) 54 | bint operator==(const_iterator) 55 | bint operator!=(iterator) 56 | bint operator!=(const_iterator) 57 | bint operator<(iterator) 58 | bint operator<(const_iterator) 59 | bint operator>(iterator) 60 | bint operator>(const_iterator) 61 | bint operator<=(iterator) 62 | bint operator<=(const_iterator) 63 | bint operator>=(iterator) 64 | bint operator>=(const_iterator) 65 | cppclass const_iterator: 66 | const_iterator() except + 67 | const_iterator(iterator&) except + 68 | const_iterator(const_iterator&) except + 69 | operator=(iterator&) except + 70 | const T& operator*() 71 | const_iterator operator++() 72 | const_iterator operator--() 73 | const_iterator operator++(int) 74 | const_iterator operator--(int) 75 | const_iterator operator+(size_type) 76 | const_iterator operator-(size_type) 77 | difference_type operator-(iterator) 78 | difference_type operator-(const_iterator) 79 | bint operator==(iterator) 80 | bint operator==(const_iterator) 81 | bint operator!=(iterator) 82 | bint operator!=(const_iterator) 83 | bint operator<(iterator) 84 | bint operator<(const_iterator) 85 | bint operator>(iterator) 86 | bint operator>(const_iterator) 87 | bint operator<=(iterator) 88 | bint operator<=(const_iterator) 89 | bint operator>=(iterator) 90 | bint operator>=(const_iterator) 91 | 92 | cppclass const_reverse_iterator 93 | cppclass reverse_iterator: 94 | reverse_iterator() except + 95 | reverse_iterator(reverse_iterator&) except + 96 | T& operator*() 97 | reverse_iterator operator++() 98 | reverse_iterator operator--() 99 | reverse_iterator operator++(int) 100 | reverse_iterator operator--(int) 101 | reverse_iterator operator+(size_type) 102 | reverse_iterator operator-(size_type) 103 | difference_type operator-(iterator) 104 | difference_type operator-(const_iterator) 105 | bint operator==(reverse_iterator) 106 | bint operator==(const_reverse_iterator) 107 | bint operator!=(reverse_iterator) 108 | bint operator!=(const_reverse_iterator) 109 | bint operator<(reverse_iterator) 110 | bint operator<(const_reverse_iterator) 111 | bint operator>(reverse_iterator) 112 | bint operator>(const_reverse_iterator) 113 | bint operator<=(reverse_iterator) 114 | bint operator<=(const_reverse_iterator) 115 | bint operator>=(reverse_iterator) 116 | bint operator>=(const_reverse_iterator) 117 | cppclass const_reverse_iterator: 118 | const_reverse_iterator() except + 119 | const_reverse_iterator(reverse_iterator&) except + 120 | operator=(reverse_iterator&) except + 121 | const T& operator*() 122 | const_reverse_iterator operator++() 123 | const_reverse_iterator operator--() 124 | const_reverse_iterator operator++(int) 125 | const_reverse_iterator operator--(int) 126 | const_reverse_iterator operator+(size_type) 127 | const_reverse_iterator operator-(size_type) 128 | difference_type operator-(iterator) 129 | difference_type operator-(const_iterator) 130 | bint operator==(reverse_iterator) 131 | bint operator==(const_reverse_iterator) 132 | bint operator!=(reverse_iterator) 133 | bint operator!=(const_reverse_iterator) 134 | bint operator<(reverse_iterator) 135 | bint operator<(const_reverse_iterator) 136 | bint operator>(reverse_iterator) 137 | bint operator>(const_reverse_iterator) 138 | bint operator<=(reverse_iterator) 139 | bint operator<=(const_reverse_iterator) 140 | bint operator>=(reverse_iterator) 141 | bint operator>=(const_reverse_iterator) 142 | 143 | vector() except + 144 | vector(vector&) except + 145 | vector(size_type) except + 146 | vector(size_type, T&) except + 147 | #vector[InputIt](InputIt, InputIt) 148 | T& operator[](size_type) 149 | #vector& operator=(vector&) 150 | bint operator==(vector&, vector&) 151 | bint operator!=(vector&, vector&) 152 | bint operator<(vector&, vector&) 153 | bint operator>(vector&, vector&) 154 | bint operator<=(vector&, vector&) 155 | bint operator>=(vector&, vector&) 156 | void assign(size_type, const T&) 157 | void assign[InputIt](InputIt, InputIt) except + 158 | T& at(size_type) except + 159 | T& back() 160 | iterator begin() 161 | const_iterator const_begin "begin"() 162 | const_iterator cbegin() 163 | size_type capacity() 164 | void clear() 165 | bint empty() 166 | iterator end() 167 | const_iterator const_end "end"() 168 | const_iterator cend() 169 | iterator erase(iterator) 170 | iterator erase(iterator, iterator) 171 | T& front() 172 | iterator insert(iterator, const T&) except + 173 | iterator insert(iterator, size_type, const T&) except + 174 | iterator insert[InputIt](iterator, InputIt, InputIt) except + 175 | size_type max_size() 176 | void pop_back() 177 | void push_back(T&) except + 178 | reverse_iterator rbegin() 179 | const_reverse_iterator const_rbegin "rbegin"() 180 | const_reverse_iterator crbegin() 181 | reverse_iterator rend() 182 | const_reverse_iterator const_rend "rend"() 183 | const_reverse_iterator crend() 184 | void reserve(size_type) except + 185 | void resize(size_type) except + 186 | void resize(size_type, T&) except + 187 | size_type size() 188 | void swap(vector&) 189 | 190 | # C++11 methods 191 | T* data() 192 | const T* const_data "data"() 193 | void shrink_to_fit() except + 194 | iterator emplace(const_iterator, ...) except + 195 | T& emplace_back(...) except + 196 | -------------------------------------------------------------------------------- /src/radarsimpy/lib/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | A Python module for radar simulation 3 | 4 | --- 5 | 6 | - Copyright (C) 2018 - PRESENT radarsimx.com 7 | - E-mail: info@radarsimx.com 8 | - Website: https://radarsimx.com 9 | 10 | :: 11 | 12 | ██████╗ █████╗ ██████╗ █████╗ ██████╗ ███████╗██╗███╗ ███╗██╗ ██╗ 13 | ██╔══██╗██╔══██╗██╔══██╗██╔══██╗██╔══██╗██╔════╝██║████╗ ████║╚██╗██╔╝ 14 | ██████╔╝███████║██║ ██║███████║██████╔╝███████╗██║██╔████╔██║ ╚███╔╝ 15 | ██╔══██╗██╔══██║██║ ██║██╔══██║██╔══██╗╚════██║██║██║╚██╔╝██║ ██╔██╗ 16 | ██║ ██║██║ ██║██████╔╝██║ ██║██║ ██║███████║██║██║ ╚═╝ ██║██╔╝ ██╗ 17 | ╚═╝ ╚═╝╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ 18 | 19 | """ 20 | -------------------------------------------------------------------------------- /src/radarsimpy/lib/cp_radarsimc.pxd: -------------------------------------------------------------------------------- 1 | # distutils: language = c++ 2 | """ 3 | A Python module for radar simulation 4 | 5 | --- 6 | 7 | - Copyright (C) 2018 - PRESENT radarsimx.com 8 | - E-mail: info@radarsimx.com 9 | - Website: https://radarsimx.com 10 | 11 | :: 12 | 13 | ██████╗ █████╗ ██████╗ █████╗ ██████╗ ███████╗██╗███╗ ███╗██╗ ██╗ 14 | ██╔══██╗██╔══██╗██╔══██╗██╔══██╗██╔══██╗██╔════╝██║████╗ ████║╚██╗██╔╝ 15 | ██████╔╝███████║██║ ██║███████║██████╔╝███████╗██║██╔████╔██║ ╚███╔╝ 16 | ██╔══██╗██╔══██║██║ ██║██╔══██║██╔══██╗╚════██║██║██║╚██╔╝██║ ██╔██╗ 17 | ██║ ██║██║ ██║██████╔╝██║ ██║██║ ██║███████║██║██║ ╚═╝ ██║██╔╝ ██╗ 18 | ╚═╝ ╚═╝╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ 19 | 20 | """ 21 | 22 | from radarsimpy.includes.radarsimc cimport Radar 23 | from radarsimpy.includes.radarsimc cimport Target 24 | from radarsimpy.includes.radarsimc cimport Point 25 | from radarsimpy.includes.type_def cimport float_t 26 | 27 | 28 | cdef Point[float_t] cp_Point(location, speed, rcs, phase, shape) 29 | cdef Radar[double, float_t] cp_Radar(radar, frame_start_time) 30 | cdef Target[float_t] cp_Target(radar, target, timestamp, mesh_module) 31 | cdef Target[float_t] cp_RCS_Target(target, mesh_module) 32 | -------------------------------------------------------------------------------- /src/radarsimpy/mesh_kit.py: -------------------------------------------------------------------------------- 1 | """ 2 | Script for loading 3D mesh files 3 | 4 | This script provides utilities to load 3D mesh files using various available Python 5 | mesh processing libraries. 6 | 7 | --- 8 | 9 | - Copyright (C) 2025 - PRESENT radarsimx.com 10 | - E-mail: info@radarsimx.com 11 | - Website: https://radarsimx.com 12 | 13 | :: 14 | 15 | ██████╗ █████╗ ██████╗ █████╗ ██████╗ ███████╗██╗███╗ ███╗██╗ ██╗ 16 | ██╔══██╗██╔══██╗██╔══██╗██╔══██╗██╔══██╗██╔════╝██║████╗ ████║╚██╗██╔╝ 17 | ██████╔╝███████║██║ ██║███████║██████╔╝███████╗██║██╔████╔██║ ╚███╔╝ 18 | ██╔══██╗██╔══██║██║ ██║██╔══██║██╔══██╗╚════██║██║██║╚██╔╝██║ ██╔██╗ 19 | ██║ ██║██║ ██║██████╔╝██║ ██║██║ ██║███████║██║██║ ╚═╝ ██║██╔╝ ██╗ 20 | ╚═╝ ╚═╝╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ 21 | 22 | """ 23 | 24 | import importlib 25 | import numpy as np 26 | 27 | 28 | def check_module_installed(module_name: str) -> bool: 29 | """ 30 | Check if a Python module is installed 31 | 32 | :param str module_name: Name of the module to check 33 | 34 | :return: True if module is installed, False otherwise 35 | :rtype: bool 36 | """ 37 | return importlib.util.find_spec(module_name) is not None 38 | 39 | 40 | def safe_import(module_name: str) -> object: 41 | """ 42 | Safely import a module without raising an exception if not found 43 | 44 | :param str module_name: Name of the module to import 45 | 46 | :return: Imported module object or None if import fails 47 | :rtype: object 48 | """ 49 | try: 50 | return importlib.import_module(module_name) 51 | except ImportError: 52 | return None 53 | 54 | 55 | def import_mesh_module() -> object: 56 | """ 57 | Import the first available mesh processing module from a predefined list 58 | 59 | Tries to import modules in this order: pyvista, pymeshlab, trimesh, meshio 60 | 61 | :return: The mesh processing module object 62 | :rtype: object 63 | :raises ImportError: If no valid mesh processing module is found 64 | """ 65 | module_list = ["trimesh", "pyvista", "pymeshlab", "meshio"] 66 | 67 | for _, module_name in enumerate(module_list): 68 | if check_module_installed(module_name): 69 | module = safe_import(module_name) 70 | return module 71 | 72 | raise ImportError( 73 | "No valid module was found to process the 3D model file, " 74 | + "please install one of the following modules: " 75 | + "`pyvista`, `pymeshlab`, `trimesh`, `meshio`" 76 | ) 77 | 78 | 79 | def load_mesh(mesh_file_name: str, scale: float, mesh_module: object) -> dict: 80 | """ 81 | Load a 3D mesh file using the specified module 82 | 83 | :param str mesh_file_name: Path to the mesh file 84 | :param float scale: Scale factor to apply to the mesh vertices 85 | :param object mesh_module: The mesh processing module object 86 | 87 | :return: Dictionary containing mesh points and cells 88 | :rtype: dict with keys: 89 | - points (numpy.ndarray): Array of vertex coordinates 90 | - cells (numpy.ndarray): Array of face indices 91 | """ 92 | if mesh_module.__name__ == "pyvista": 93 | mesh_data = mesh_module.read(mesh_file_name) 94 | points = np.array(mesh_data.points) / scale 95 | cells = mesh_data.faces.reshape(-1, 4)[:, 1:] 96 | return {"points": points, "cells": cells} 97 | 98 | if mesh_module.__name__ == "pymeshlab": 99 | ms = mesh_module.MeshSet() 100 | ms.load_new_mesh(mesh_file_name) 101 | mesh_data = ms.current_mesh() 102 | points = np.array(mesh_data.vertex_matrix()) / scale 103 | cells = np.array(mesh_data.face_matrix()) 104 | if np.isfortran(points): 105 | points = np.ascontiguousarray(points) 106 | cells = np.ascontiguousarray(cells) 107 | ms.clear() 108 | return {"points": points, "cells": cells} 109 | 110 | if mesh_module.__name__ == "trimesh": 111 | mesh_data = mesh_module.load(mesh_file_name) 112 | points = np.array(mesh_data.vertices) / scale 113 | cells = np.array(mesh_data.faces) 114 | return {"points": points, "cells": cells} 115 | 116 | if mesh_module.__name__ == "meshio": 117 | mesh_data = mesh_module.read(mesh_file_name) 118 | points = mesh_data.points / scale 119 | cells = mesh_data.cells[0].data 120 | return {"points": points, "cells": cells} 121 | 122 | raise ImportError( 123 | "No valid module was found to process the 3D model file, " 124 | + "please install one of the following modules: " 125 | + "`pyvista`, `pymeshlab`, `trimesh`, `meshio`" 126 | ) 127 | -------------------------------------------------------------------------------- /src/radarsimpy/simulator.pyx: -------------------------------------------------------------------------------- 1 | # distutils: language = c++ 2 | """ 3 | The Python Module for Advanced Radar and Lidar Simulations 4 | 5 | This module provides tools for simulating and analyzing radar and lidar systems in complex 3D environments. 6 | The module supports a wide range of functionalities, including: 7 | 8 | 1. **Lidar Simulations**: 9 | 10 | - Simulate Lidar systems in dynamic or static 3D environments. 11 | - Generate point clouds and compute ray interactions with targets. 12 | - Model the dynamics of targets, such as motion and rotation. 13 | 14 | 2. **Radar Simulations**: 15 | 16 | - Simulate radar baseband responses for complex scenes. 17 | - Handle point targets, 3D mesh objects, interference modeling, and noise simulation. 18 | - Perform advanced ray-tracing for high-fidelity radar analysis. 19 | 20 | 3. **Radar Cross Section (RCS) Calculations**: 21 | 22 | - Calculate the RCS of targets using the Shooting and Bouncing Rays (SBR) method. 23 | - Model electromagnetic wave scattering from complex 3D geometries. 24 | - Support for defining target materials and permittivity properties. 25 | 26 | --- 27 | 28 | - Copyright (C) 2018 - PRESENT radarsimx.com 29 | - E-mail: info@radarsimx.com 30 | - Website: https://radarsimx.com 31 | 32 | :: 33 | 34 | ██████╗ █████╗ ██████╗ █████╗ ██████╗ ███████╗██╗███╗ ███╗██╗ ██╗ 35 | ██╔══██╗██╔══██╗██╔══██╗██╔══██╗██╔══██╗██╔════╝██║████╗ ████║╚██╗██╔╝ 36 | ██████╔╝███████║██║ ██║███████║██████╔╝███████╗██║██╔████╔██║ ╚███╔╝ 37 | ██╔══██╗██╔══██║██║ ██║██╔══██║██╔══██╗╚════██║██║██║╚██╔╝██║ ██╔██╗ 38 | ██║ ██║██║ ██║██████╔╝██║ ██║██║ ██║███████║██║██║ ╚═╝ ██║██╔╝ ██╗ 39 | ╚═╝ ╚═╝╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ 40 | 41 | """ 42 | 43 | include "simulator_radar.pyx" 44 | include "simulator_lidar.pyx" 45 | include "simulator_rcs.pyx" 46 | -------------------------------------------------------------------------------- /src/radarsimpy/simulator_lidar.pyx: -------------------------------------------------------------------------------- 1 | # distutils: language = c++ 2 | """ 3 | The Python Module for Lidar Simulations 4 | 5 | This module provides tools for simulating a lidar system in complex 3D environments. It leverages a high-performance C++ backend integrated with Python to support large-scale simulations with high accuracy and computational efficiency. The module includes features for simulating ray interactions with targets, generating point clouds, and modeling the dynamics of targets in the scene. 6 | 7 | --- 8 | 9 | - Copyright (C) 2018 - PRESENT radarsimx.com 10 | - E-mail: info@radarsimx.com 11 | - Website: https://radarsimx.com 12 | 13 | :: 14 | 15 | ██████╗ █████╗ ██████╗ █████╗ ██████╗ ███████╗██╗███╗ ███╗██╗ ██╗ 16 | ██╔══██╗██╔══██╗██╔══██╗██╔══██╗██╔══██╗██╔════╝██║████╗ ████║╚██╗██╔╝ 17 | ██████╔╝███████║██║ ██║███████║██████╔╝███████╗██║██╔████╔██║ ╚███╔╝ 18 | ██╔══██╗██╔══██║██║ ██║██╔══██║██╔══██╗╚════██║██║██║╚██╔╝██║ ██╔██╗ 19 | ██║ ██║██║ ██║██████╔╝██║ ██║██║ ██║███████║██║██║ ╚═╝ ██║██╔╝ ██╗ 20 | ╚═╝ ╚═╝╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ 21 | 22 | """ 23 | 24 | # Core imports 25 | from libcpp cimport bool 26 | import numpy as np 27 | cimport numpy as np 28 | cimport cython 29 | 30 | # RadarSimX imports 31 | from radarsimpy.includes.radarsimc cimport Target, LidarSimulator 32 | from radarsimpy.includes.radarsimc cimport Mem_Copy 33 | from radarsimpy.includes.rsvector cimport Vec3 34 | from radarsimpy.includes.type_def cimport float_t, int_t, vector 35 | 36 | from radarsimpy.mesh_kit import import_mesh_module, load_mesh 37 | 38 | np.import_array() 39 | np_float = np.float32 40 | 41 | 42 | @cython.cdivision(True) 43 | @cython.boundscheck(False) 44 | @cython.wraparound(False) 45 | cpdef sim_lidar(lidar, targets, frame_time=0): 46 | """ 47 | sim_lidar(lidar, targets, frame_time=0) 48 | 49 | Simulate a Lidar scene and compute ray interactions with targets. 50 | 51 | This function simulates a Lidar scanning scene in a 3D environment, calculating the interaction of Lidar rays with the provided targets. It handles both static and dynamic targets, allowing for customizable positions, velocities, and orientations of objects in the scene. The simulation produces a set of rays representing the Lidar's perception of the environment. 52 | 53 | :param dict lidar: 54 | Lidar configuration parameters. The following keys are required: 55 | 56 | - **position** (*numpy.ndarray*): 57 | The 3D position of the Lidar in meters (m), specified as [x, y, z]. 58 | - **phi** (*numpy.ndarray*): 59 | Array of phi scanning angles in degrees (°). Phi represents the horizontal scanning angles in the Lidar's field of view. The total number of scanning directions is determined by the combination of phi and theta angles. 60 | - **theta** (*numpy.ndarray*): 61 | Array of theta scanning angles in degrees (°). Theta represents the vertical scanning angles in the Lidar's field of view. The total number of scanning directions is computed as: 62 | ``len(phi) * len(theta)``. 63 | 64 | :param list[dict] targets: 65 | A list of target objects in the scene. Each target is represented as a dictionary containing the following keys: 66 | 67 | - **model** (*str*): 68 | File path to the target model (3D object) in the scene. 69 | - **origin** (*numpy.ndarray*): 70 | The origin position (rotation and translation center) of the target model in meters (m), specified as [x, y, z]. 71 | Default: ``[0, 0, 0]``. 72 | - **location** (*numpy.ndarray*): 73 | The 3D location of the target in meters (m), specified as [x, y, z]. 74 | Default: ``[0, 0, 0]``. 75 | - **speed** (*numpy.ndarray*): 76 | Speed vector of the target in meters per second (m/s), specified as [vx, vy, vz]. 77 | Default: ``[0, 0, 0]``. 78 | - **rotation** (*numpy.ndarray*): 79 | The angular orientation of the target in degrees (°), specified as [yaw, pitch, roll]. 80 | Default: ``[0, 0, 0]``. 81 | - **rotation_rate** (*numpy.ndarray*): 82 | The angular rotation rate of the target in degrees per second (°/s), specified as [yaw rate, pitch rate, roll rate]. 83 | Default: ``[0, 0, 0]``. 84 | - **unit** (*str*): 85 | The unit system for the target model's geometry. 86 | Supported values: ``mm``, ``cm``, ``m``. 87 | Default: ``m``. 88 | 89 | :param float frame_time: 90 | Simulation timestamp in seconds (s). This parameter determines the time reference for the Lidar's scanning operation and target positions. 91 | Default: ``0``. 92 | 93 | :return: 94 | Simulated Lidar rays based on the provided configuration and targets. 95 | 96 | :rtype: 97 | numpy.ndarray - A structured array representing the Lidar ray interactions with the scene, including details such as ray origins, directions, and intersections. 98 | """ 99 | cdef LidarSimulator[float_t] lidar_sim_c 100 | 101 | # Memory view declarations 102 | cdef float_t[:, :] points_mv 103 | cdef int_t[:, :] cells_mv 104 | cdef float_t[:] origin_mv 105 | cdef float_t[:] speed_mv 106 | cdef float_t[:] location_mv 107 | cdef float_t[:] rotation_mv 108 | cdef float_t[:] rotation_rate_mv 109 | cdef float_t scale 110 | cdef int_t idx_c 111 | 112 | # Process targets 113 | mesh_module = import_mesh_module() 114 | for idx_c in range(0, len(targets)): 115 | # Unit conversion 116 | unit = targets[idx_c].get("unit", "m") 117 | scale = 1000 if unit == "mm" else 100 if unit == "cm" else 1 118 | 119 | mesh_data = load_mesh(targets[idx_c]["model"], scale, mesh_module) 120 | points_mv = mesh_data["points"].astype(np_float) 121 | cells_mv = mesh_data["cells"].astype(np.int32) 122 | 123 | # Target parameters 124 | origin_mv = np.array(targets[idx_c].get("origin", (0, 0, 0)), dtype=np_float) 125 | temp_location = np.array(targets[idx_c].get("location", (0, 0, 0))) + frame_time*np.array(targets[idx_c].get("speed", (0, 0, 0))) 126 | location_mv = temp_location.astype(np_float) 127 | speed_mv = np.array(targets[idx_c].get("speed", (0, 0, 0)), dtype=np_float) 128 | 129 | temp_rotation = np.array(targets[idx_c].get("rotation", (0, 0, 0))) + frame_time*np.array(targets[idx_c].get("rotation_rate", (0, 0, 0))) 130 | rotation_mv = np.radians(temp_rotation.astype(np_float)) 131 | rotation_rate_mv = np.radians( 132 | np.array(targets[idx_c].get("rotation_rate", (0, 0, 0)), dtype=np_float) 133 | ) 134 | 135 | # Add target to pointcloud 136 | lidar_sim_c.AddTarget(Target[float_t](&points_mv[0, 0], 137 | &cells_mv[0, 0], 138 | cells_mv.shape[0], 139 | Vec3[float_t](&origin_mv[0]), 140 | Vec3[float_t](&location_mv[0]), 141 | Vec3[float_t](&speed_mv[0]), 142 | Vec3[float_t](&rotation_mv[0]), 143 | Vec3[float_t](&rotation_rate_mv[0]), 144 | targets[idx_c].get("skip_diffusion", False))) 145 | 146 | # Lidar parameters 147 | cdef float_t[:] phi_mv = np.radians(np.array(lidar["phi"], dtype=np_float)) 148 | cdef float_t[:] theta_mv = np.radians(np.array(lidar["theta"], dtype=np_float)) 149 | cdef float_t[:] position_mv = np.array(lidar["position"], dtype=np_float) 150 | 151 | cdef vector[float_t] phi_vt 152 | Mem_Copy(&phi_mv[0], (phi_mv.shape[0]), phi_vt) 153 | 154 | cdef vector[float_t] theta_vt 155 | Mem_Copy(&theta_mv[0], (theta_mv.shape[0]), theta_vt) 156 | 157 | # Perform ray tracing 158 | lidar_sim_c.Run(phi_vt, 159 | theta_vt, 160 | Vec3[float_t](&position_mv[0])) 161 | 162 | # Prepare output 163 | ray_type = np.dtype([("positions", np_float, (3,)), 164 | ("directions", np_float, (3,))]) 165 | rays = np.zeros(lidar_sim_c.cloud_.size(), dtype=ray_type) 166 | 167 | for idx_c in range(0, lidar_sim_c.cloud_.size()): 168 | rays[idx_c]["positions"][0] = lidar_sim_c.cloud_[idx_c].location_[1][0] 169 | rays[idx_c]["positions"][1] = lidar_sim_c.cloud_[idx_c].location_[1][1] 170 | rays[idx_c]["positions"][2] = lidar_sim_c.cloud_[idx_c].location_[1][2] 171 | rays[idx_c]["directions"][0] = lidar_sim_c.cloud_[idx_c].direction_[1][0] 172 | rays[idx_c]["directions"][1] = lidar_sim_c.cloud_[idx_c].direction_[1][1] 173 | rays[idx_c]["directions"][2] = lidar_sim_c.cloud_[idx_c].direction_[1][2] 174 | 175 | return rays 176 | -------------------------------------------------------------------------------- /src/radarsimpy/simulator_rcs.pyx: -------------------------------------------------------------------------------- 1 | # distutils: language = c++ 2 | 3 | """ 4 | The Python Module for Radar Cross Section (RCS) Simulation 5 | 6 | This module provides tools for simulating and calculating the Radar Cross Section (RCS) of targets using the Shooting and Bouncing Rays (SBR) method. By leveraging a high-performance C++ backend integrated with Python, it enables accurate and efficient modeling of electromagnetic wave scattering from complex 3D target geometries. The module is designed for radar researchers, engineers, and educators. 7 | 8 | --- 9 | 10 | - Copyright (C) 2018 - PRESENT radarsimx.com 11 | - E-mail: info@radarsimx.com 12 | - Website: https://radarsimx.com 13 | 14 | :: 15 | 16 | ██████╗ █████╗ ██████╗ █████╗ ██████╗ ███████╗██╗███╗ ███╗██╗ ██╗ 17 | ██╔══██╗██╔══██╗██╔══██╗██╔══██╗██╔══██╗██╔════╝██║████╗ ████║╚██╗██╔╝ 18 | ██████╔╝███████║██║ ██║███████║██████╔╝███████╗██║██╔████╔██║ ╚███╔╝ 19 | ██╔══██╗██╔══██║██║ ██║██╔══██║██╔══██╗╚════██║██║██║╚██╔╝██║ ██╔██╗ 20 | ██║ ██║██║ ██║██████╔╝██║ ██║██║ ██║███████║██║██║ ╚═╝ ██║██╔╝ ██╗ 21 | ╚═╝ ╚═╝╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ 22 | 23 | """ 24 | 25 | # Standard library imports 26 | import numpy as np 27 | 28 | # Cython imports 29 | cimport cython 30 | cimport numpy as np 31 | 32 | # Local imports 33 | from radarsimpy.includes.rsvector cimport Vec3 34 | from radarsimpy.includes.type_def cimport vector 35 | from radarsimpy.includes.radarsimc cimport Target, RcsSimulator, IsFreeTier 36 | from radarsimpy.lib.cp_radarsimc cimport cp_RCS_Target 37 | from libcpp.complex cimport complex as cpp_complex 38 | 39 | from radarsimpy.mesh_kit import import_mesh_module 40 | 41 | # Initialize NumPy 42 | np.import_array() 43 | 44 | 45 | @cython.cdivision(True) 46 | @cython.boundscheck(False) 47 | @cython.wraparound(False) 48 | cpdef sim_rcs( 49 | targets, 50 | f, 51 | inc_phi, 52 | inc_theta, 53 | inc_pol=[0, 0, 1], 54 | obs_phi=None, 55 | obs_theta=None, 56 | obs_pol=None, 57 | density=1 58 | ): 59 | """Calculate the Radar Cross Section (RCS) of targets using the Shooting and Bouncing Rays (SBR) method. 60 | 61 | This function computes the RCS of one or more targets by simulating how electromagnetic waves interact with the target models. The simulation uses the SBR technique, which accurately models wave scattering by tracing rays that shoot at the target and bounce off its surfaces. 62 | 63 | :param list[dict] targets: 64 | A list of target dictionaries specifying the properties of each target. Each dictionary contains the following keys: 65 | 66 | - **model** (*str*): 67 | File path to the 3D target model. 68 | - **origin** (*numpy.ndarray*): 69 | The origin position (rotation and translation center) of the target model in meters (m), specified as [x, y, z]. 70 | Default: ``[0, 0, 0]``. 71 | - **location** (*numpy.ndarray*): 72 | The 3D location of the target in meters (m), specified as [x, y, z]. 73 | Default: ``[0, 0, 0]``. 74 | - **rotation** (*numpy.ndarray*): 75 | The target's orientation in degrees (°), specified as [yaw, pitch, roll]. 76 | Default: ``[0, 0, 0]``. 77 | - **permittivity** (*complex*): 78 | The target's permittivity, which represents its electromagnetic material properties. 79 | Default: Perfect Electric Conductor (PEC). 80 | - **unit** (*str*): 81 | Unit of measurement for the target model's geometry. 82 | Supported values: ``mm``, ``cm``, ``m``. 83 | Default: ``m``. 84 | 85 | :param float f: 86 | The center frequency of the incident electromagnetic wave in Hertz (Hz). 87 | :param float inc_phi: 88 | The horizontal incidence angle (phi) of the incoming wave in degrees (°). 89 | This angle is measured relative to the target at the transmitter's point of view. 90 | :param float inc_theta: 91 | The vertical incidence angle (theta) of the incoming wave in degrees (°). 92 | This angle is measured relative to the target at the transmitter's point of view. 93 | :param list[float] inc_pol: 94 | The polarization of the incident wave, specified as a 3D vector [x, y, z]. 95 | Default: ``[0, 0, 1]`` (vertical polarization). 96 | :param float obs_phi: 97 | The horizontal observation angle (phi) in degrees (°). 98 | This is the angle at which the RCS is observed from the observer's point of view. 99 | Default: ``None`` (if not specified, it is set to the same value as `inc_phi`). 100 | :param float obs_theta: 101 | The vertical observation angle (theta) in degrees (°). 102 | This is the angle at which the RCS is observed from the observer's point of view. 103 | Default: ``None`` (if not specified, it is set to the same value as `inc_theta`). 104 | :param list[float] obs_pol: 105 | The polarization of the observer, specified as a 3D vector [x, y, z]. 106 | Default: ``None`` (if not specified, it is set to the same value as `inc_pol`). 107 | :param float density: 108 | The ray density, defined as the number of rays per wavelength. 109 | Higher ray density improves accuracy but increases computational cost. 110 | Default: ``1.0``. 111 | 112 | :return: 113 | The Radar Cross Section (RCS) of the target(s) in square meters (m²). 114 | To convert the result to decibels relative to one square meter (dBsm), use: 115 | ``10 * log10(RCS)``. 116 | :rtype: float 117 | """ 118 | if IsFreeTier(): 119 | if len(targets) > 3: 120 | raise RuntimeError( 121 | "\nTrial Version Limitation - Target Count\n" 122 | "----------------------------------------\n" 123 | "Current limitation: Maximum 3 targets\n" 124 | "Your scene: {} targets\n\n" 125 | "This limitation helps maintain reasonable simulation times in the trial version.\n" 126 | "To simulate more targets, please upgrade to the Standard Version:\n" 127 | "→ https://radarsimx.com/product/radarsimpy/\n" 128 | .format(len(targets)) 129 | ) 130 | 131 | cdef vector[Target[float]] targets_vt 132 | cdef Vec3[cpp_complex[double]] inc_pol_cpp 133 | cdef Vec3[cpp_complex[double]] obs_pol_cpp 134 | 135 | # Set default observation parameters if not provided 136 | if obs_pol is None: 137 | obs_pol = inc_pol 138 | if obs_phi is None: 139 | obs_phi = inc_phi 140 | if obs_theta is None: 141 | obs_theta = inc_theta 142 | 143 | if isinstance(inc_phi, (list, tuple, np.ndarray)): 144 | inc_phi = np.array(inc_phi) 145 | else: 146 | inc_phi = np.array([inc_phi]) 147 | 148 | if isinstance(inc_theta, (list, tuple, np.ndarray)): 149 | inc_theta = np.array(inc_theta) 150 | else: 151 | inc_theta = np.array([inc_theta]) 152 | 153 | if np.shape(inc_phi)!=np.shape(inc_theta): 154 | raise ValueError('The lengths of `inc_phi` and `inc_theta` must be the same') 155 | 156 | if isinstance(obs_phi, (list, tuple, np.ndarray)): 157 | obs_phi = np.array(obs_phi) 158 | else: 159 | obs_phi = np.array([obs_phi]) 160 | 161 | if isinstance(obs_theta, (list, tuple, np.ndarray)): 162 | obs_theta = np.array(obs_theta) 163 | else: 164 | obs_theta = np.array([obs_theta]) 165 | 166 | if np.shape(obs_phi)!=np.shape(obs_theta): 167 | raise ValueError('The lengths of `obs_phi` and `obs_theta` must be the same') 168 | 169 | if np.shape(inc_phi)!=np.shape(obs_phi): 170 | raise ValueError('The lengths of `inc_phi` and `obs_phi` must be the same') 171 | 172 | cdef int array_size = np.size(obs_phi) 173 | cdef int idx 174 | 175 | # Convert polarization vectors to C++ types 176 | inc_pol_cpp = Vec3[cpp_complex[double]]( 177 | cpp_complex[double](np.real(inc_pol[0]), np.imag(inc_pol[0])), 178 | cpp_complex[double](np.real(inc_pol[1]), np.imag(inc_pol[1])), 179 | cpp_complex[double](np.real(inc_pol[2]), np.imag(inc_pol[2])) 180 | ) 181 | 182 | obs_pol_cpp = Vec3[cpp_complex[double]]( 183 | cpp_complex[double](np.real(obs_pol[0]), np.imag(obs_pol[0])), 184 | cpp_complex[double](np.real(obs_pol[1]), np.imag(obs_pol[1])), 185 | cpp_complex[double](np.real(obs_pol[2]), np.imag(obs_pol[2])) 186 | ) 187 | 188 | # Process targets 189 | mesh_module = import_mesh_module() 190 | for idx_c in range(0, len(targets)): 191 | targets_vt.push_back(cp_RCS_Target(targets[idx_c], mesh_module)) 192 | 193 | # Convert angles to radians 194 | inc_phi_rad = np.radians(inc_phi) 195 | inc_theta_rad = np.radians(inc_theta) 196 | obs_phi_rad = np.radians(obs_phi) 197 | obs_theta_rad = np.radians(obs_theta) 198 | 199 | inc_dir_x=np.sin(inc_theta_rad) * np.cos(inc_phi_rad) 200 | inc_dir_y=np.sin(inc_theta_rad) * np.sin(inc_phi_rad) 201 | inc_dir_z=np.cos(inc_theta_rad) 202 | 203 | obs_dir_x=np.sin(obs_theta_rad) * np.cos(obs_phi_rad) 204 | obs_dir_y=np.sin(obs_theta_rad) * np.sin(obs_phi_rad) 205 | obs_dir_z=np.cos(obs_theta_rad) 206 | 207 | cdef vector[Vec3[double]] inc_dir 208 | cdef vector[Vec3[double]] obs_dir 209 | 210 | for idx in range(0, array_size): 211 | 212 | # Calculate direction vectors 213 | inc_dir.push_back(Vec3[double]( 214 | (inc_dir_x[idx]), 215 | (inc_dir_y[idx]), 216 | (inc_dir_z[idx]) 217 | )) 218 | 219 | obs_dir.push_back(Vec3[double]( 220 | (obs_dir_x[idx]), 221 | (obs_dir_y[idx]), 222 | (obs_dir_z[idx]) 223 | )) 224 | 225 | # Calculate RCS 226 | cdef RcsSimulator[double] rcs_sim_c 227 | 228 | cdef vector[double] rcs_vect = rcs_sim_c.Run( 229 | targets_vt, 230 | inc_dir, 231 | obs_dir, 232 | inc_pol_cpp, 233 | obs_pol_cpp, 234 | f, 235 | density) 236 | 237 | rcs=np.zeros(array_size) 238 | 239 | for idx in range(0, array_size): 240 | rcs[idx] = rcs_vect[idx] 241 | 242 | if array_size == 1: 243 | return rcs[0] 244 | else: 245 | return rcs 246 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | System level test of RadarSimPy 3 | 4 | --- 5 | 6 | - Copyright (C) 2018 - PRESENT radarsimx.com 7 | - E-mail: info@radarsimx.com 8 | - Website: https://radarsimx.com 9 | 10 | :: 11 | 12 | ██████╗ █████╗ ██████╗ █████╗ ██████╗ ███████╗██╗███╗ ███╗██╗ ██╗ 13 | ██╔══██╗██╔══██╗██╔══██╗██╔══██╗██╔══██╗██╔════╝██║████╗ ████║╚██╗██╔╝ 14 | ██████╔╝███████║██║ ██║███████║██████╔╝███████╗██║██╔████╔██║ ╚███╔╝ 15 | ██╔══██╗██╔══██║██║ ██║██╔══██║██╔══██╗╚════██║██║██║╚██╔╝██║ ██╔██╗ 16 | ██║ ██║██║ ██║██████╔╝██║ ██║██║ ██║███████║██║██║ ╚═╝ ██║██╔╝ ██╗ 17 | ╚═╝ ╚═╝╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ 18 | 19 | """ 20 | -------------------------------------------------------------------------------- /tests/test_config_radar.py: -------------------------------------------------------------------------------- 1 | """ 2 | A Python module for radar simulation 3 | 4 | --- 5 | 6 | - Copyright (C) 2018 - PRESENT radarsimx.com 7 | - E-mail: info@radarsimx.com 8 | - Website: https://radarsimx.com 9 | 10 | :: 11 | 12 | ██████╗ █████╗ ██████╗ █████╗ ██████╗ ███████╗██╗███╗ ███╗██╗ ██╗ 13 | ██╔══██╗██╔══██╗██╔══██╗██╔══██╗██╔══██╗██╔════╝██║████╗ ████║╚██╗██╔╝ 14 | ██████╔╝███████║██║ ██║███████║██████╔╝███████╗██║██╔████╔██║ ╚███╔╝ 15 | ██╔══██╗██╔══██║██║ ██║██╔══██║██╔══██╗╚════██║██║██║╚██╔╝██║ ██╔██╗ 16 | ██║ ██║██║ ██║██████╔╝██║ ██║██║ ██║███████║██║██║ ╚═╝ ██║██╔╝ ██╗ 17 | ╚═╝ ╚═╝╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ 18 | 19 | """ 20 | 21 | import pytest 22 | 23 | import numpy as np 24 | 25 | from radarsimpy import Radar, Transmitter, Receiver 26 | from radarsimpy.radar import cal_phase_noise 27 | 28 | 29 | class TestRadar: 30 | @pytest.fixture 31 | def radar_setup(self): 32 | """Fixture for setting up a basic radar system.""" 33 | tx = Transmitter(f=10e9, t=1e-6, tx_power=10, pulses=10, prp=2e-6) 34 | rx = Receiver(fs=10e6) 35 | radar = Radar(transmitter=tx, receiver=rx) 36 | return radar 37 | 38 | def test_init_basic(self, radar_setup): 39 | """Test initialization with basic parameters.""" 40 | radar = radar_setup 41 | # assert radar.time_prop["frame_size"] == 2 42 | # assert np.allclose(radar.time_prop["frame_start_time"], [0, 1e-3]) 43 | assert radar.sample_prop["samples_per_pulse"] == 10 44 | assert radar.array_prop["size"] == 1 45 | np.testing.assert_allclose(radar.array_prop["virtual_array"], [[0, 0, 0]]) 46 | assert radar.radar_prop["transmitter"].waveform_prop["f"][0] == 10e9 47 | assert radar.radar_prop["receiver"].bb_prop["fs"] == 10e6 48 | assert np.allclose(radar.radar_prop["location"], [0, 0, 0]) 49 | assert np.allclose(radar.radar_prop["speed"], [0, 0, 0]) 50 | assert np.allclose(radar.radar_prop["rotation"], [0, 0, 0]) 51 | assert np.allclose(radar.radar_prop["rotation_rate"], [0, 0, 0]) 52 | 53 | def test_gen_timestamp(self, radar_setup): 54 | """Test timestamp generation.""" 55 | radar = radar_setup 56 | timestamp = radar.gen_timestamp() 57 | assert timestamp.shape == (1, 10, 10) 58 | assert np.allclose(timestamp[0, 0, 0], 0) 59 | assert np.allclose(timestamp[0, 9, 9], 1.89e-05) 60 | 61 | def test_cal_noise(self, radar_setup): 62 | """Test noise calculation.""" 63 | radar = radar_setup 64 | noise = radar.cal_noise() 65 | assert isinstance(noise, float) 66 | 67 | def test_validate_radar_motion(self, radar_setup): 68 | """Test validation of radar motion inputs.""" 69 | radar = radar_setup 70 | with pytest.raises(ValueError): 71 | radar.validate_radar_motion( 72 | location=[0, 0, [1, 2]], 73 | speed=[0, 0, 0], 74 | rotation=[0, 0, 0], 75 | rotation_rate=[0, 0, 0], 76 | ) 77 | 78 | def test_process_radar_motion_scalar(self, radar_setup): 79 | """Test processing of radar motion with scalar inputs.""" 80 | radar = radar_setup 81 | radar.process_radar_motion( 82 | location=[1, 2, 3], 83 | speed=[4, 5, 6], 84 | rotation=[7, 8, 9], 85 | rotation_rate=[10, 11, 12], 86 | ) 87 | assert np.allclose(radar.radar_prop["location"], [1, 2, 3]) 88 | assert np.allclose(radar.radar_prop["speed"], [4, 5, 6]) 89 | assert np.allclose(radar.radar_prop["rotation"], np.radians([7, 8, 9])) 90 | assert np.allclose(radar.radar_prop["rotation_rate"], np.radians([10, 11, 12])) 91 | 92 | def test_cal_phase_noise(self): 93 | """Test phase noise calculation.""" 94 | fs = 10e6 95 | freq = np.array([1e3, 1e4, 1e5]) 96 | power = np.array([-100, -110, -120]) 97 | signal = np.ones((10, 100), dtype=complex) 98 | phase_noise = cal_phase_noise(signal, fs, freq, power) 99 | assert phase_noise.shape == (10, 100) 100 | assert np.allclose(np.abs(phase_noise), 1) 101 | 102 | def test_cal_phase_noise_validation(self): 103 | """Test phase noise calculation with validation.""" 104 | fs = 10e6 105 | freq = np.array([1e3, 1e4, 1e5]) 106 | power = np.array([-100, -110, -120]) 107 | signal = np.ones((10, 100), dtype=complex) 108 | phase_noise = cal_phase_noise(signal, fs, freq, power, validation=True) 109 | assert phase_noise.shape == (10, 100) 110 | assert np.allclose(np.abs(phase_noise), 1) 111 | 112 | def test_init_with_phase_noise(self): 113 | """Test initialization with phase noise.""" 114 | tx = Transmitter( 115 | f=10e9, 116 | t=1e-6, 117 | tx_power=10, 118 | pulses=10, 119 | prp=2e-6, 120 | pn_f=np.array([1e3, 1e4, 1e5]), 121 | pn_power=np.array([-100, -110, -120]), 122 | ) 123 | rx = Receiver(fs=10e6) 124 | radar = Radar(transmitter=tx, receiver=rx) 125 | assert radar.sample_prop["phase_noise"].shape == (191, ) 126 | 127 | def test_init_with_phase_noise_validation(self): 128 | """Test initialization with phase noise and validation.""" 129 | tx = Transmitter( 130 | f=10e9, 131 | t=1e-6, 132 | tx_power=10, 133 | pulses=10, 134 | prp=2e-6, 135 | pn_f=np.array([1e3, 1e4, 1e5]), 136 | pn_power=np.array([-100, -110, -120]), 137 | ) 138 | rx = Receiver(fs=10e6) 139 | radar = Radar(transmitter=tx, receiver=rx, validation=True) 140 | assert radar.sample_prop["phase_noise"].shape == (191, ) 141 | 142 | def test_init_with_multiple_channels(self): 143 | """Test initialization with multiple channels.""" 144 | tx = Transmitter( 145 | f=10e9, 146 | t=1e-6, 147 | tx_power=10, 148 | pulses=10, 149 | prp=2e-6, 150 | channels=[{"location": (0, 0, 0)}, {"location": (10, 0, 0)}], 151 | ) 152 | rx = Receiver( 153 | fs=10e6, 154 | channels=[{"location": (0, 0, 0)}, {"location": (0, 10, 0)}], 155 | ) 156 | radar = Radar(transmitter=tx, receiver=rx, time=[0, 1e-3]) 157 | assert radar.array_prop["size"] == 4 158 | np.testing.assert_allclose( 159 | radar.array_prop["virtual_array"], 160 | [[0, 0, 0], [0, 10, 0], [10, 0, 0], [10, 10, 0]], 161 | ) 162 | -------------------------------------------------------------------------------- /tests/test_config_receiver.py: -------------------------------------------------------------------------------- 1 | """ 2 | A Python module for radar simulation 3 | 4 | --- 5 | 6 | - Copyright (C) 2018 - PRESENT radarsimx.com 7 | - E-mail: info@radarsimx.com 8 | - Website: https://radarsimx.com 9 | 10 | :: 11 | 12 | ██████╗ █████╗ ██████╗ █████╗ ██████╗ ███████╗██╗███╗ ███╗██╗ ██╗ 13 | ██╔══██╗██╔══██╗██╔══██╗██╔══██╗██╔══██╗██╔════╝██║████╗ ████║╚██╗██╔╝ 14 | ██████╔╝███████║██║ ██║███████║██████╔╝███████╗██║██╔████╔██║ ╚███╔╝ 15 | ██╔══██╗██╔══██║██║ ██║██╔══██║██╔══██╗╚════██║██║██║╚██╔╝██║ ██╔██╗ 16 | ██║ ██║██║ ██║██████╔╝██║ ██║██║ ██║███████║██║██║ ╚═╝ ██║██╔╝ ██╗ 17 | ╚═╝ ╚═╝╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ 18 | 19 | """ 20 | 21 | import pytest 22 | 23 | import numpy as np 24 | 25 | from radarsimpy import Receiver 26 | 27 | 28 | class TestReceiver: 29 | def test_init_basic(self): 30 | """Test initialization with basic parameters.""" 31 | rx = Receiver(fs=10e6) 32 | assert rx.bb_prop["fs"] == 10e6 33 | assert rx.rf_prop["noise_figure"] == 10 34 | assert rx.rf_prop["rf_gain"] == 0 35 | assert rx.bb_prop["load_resistor"] == 500 36 | assert rx.bb_prop["baseband_gain"] == 0 37 | assert rx.bb_prop["bb_type"] == "complex" 38 | assert rx.bb_prop["noise_bandwidth"] == 10e6 39 | assert rx.rxchannel_prop["size"] == 1 40 | np.testing.assert_allclose(rx.rxchannel_prop["locations"], [[0, 0, 0]]) 41 | 42 | def test_init_custom_parameters(self): 43 | """Test initialization with custom parameters.""" 44 | rx = Receiver( 45 | fs=20e6, 46 | noise_figure=5, 47 | rf_gain=10, 48 | load_resistor=100, 49 | baseband_gain=20, 50 | bb_type="real", 51 | ) 52 | assert rx.bb_prop["fs"] == 20e6 53 | assert rx.rf_prop["noise_figure"] == 5 54 | assert rx.rf_prop["rf_gain"] == 10 55 | assert rx.bb_prop["load_resistor"] == 100 56 | assert rx.bb_prop["baseband_gain"] == 20 57 | assert rx.bb_prop["bb_type"] == "real" 58 | assert rx.bb_prop["noise_bandwidth"] == 10e6 59 | 60 | def test_validate_bb_prop(self): 61 | """Test validation of baseband properties.""" 62 | rx = Receiver(fs=10e6) 63 | with pytest.raises(ValueError): 64 | rx.bb_prop["bb_type"] = "invalid" 65 | rx.validate_bb_prop(rx.bb_prop) 66 | 67 | def test_process_rxchannel_prop_basic(self): 68 | """Test processing of receiver channel properties with basic parameters.""" 69 | rx = Receiver(fs=10e6) 70 | channels = [{"location": (1, 2, 3)}] 71 | rxch_prop = rx.process_rxchannel_prop(channels) 72 | assert rxch_prop["size"] == 1 73 | np.testing.assert_allclose(rxch_prop["locations"], [[1, 2, 3]]) 74 | np.testing.assert_allclose(rxch_prop["polarization"], [[0, 0, 1]]) 75 | assert rxch_prop["antenna_gains"][0] == 0 76 | np.testing.assert_allclose(rxch_prop["az_angles"][0], [-90, 90]) 77 | np.testing.assert_allclose(rxch_prop["az_patterns"][0], [0, 0]) 78 | np.testing.assert_allclose(rxch_prop["el_angles"][0], [-90, 90]) 79 | np.testing.assert_allclose(rxch_prop["el_patterns"][0], [0, 0]) 80 | 81 | def test_process_rxchannel_prop_custom_parameters(self): 82 | """Test processing of receiver channel properties with custom parameters.""" 83 | rx = Receiver(fs=10e6) 84 | channels = [ 85 | { 86 | "location": (4, 5, 6), 87 | "polarization": [1, 0, 0], 88 | "azimuth_angle": [-45, 45], 89 | "azimuth_pattern": [-3, -3], 90 | "elevation_angle": [-60, 60], 91 | "elevation_pattern": [-5, -5], 92 | } 93 | ] 94 | rxch_prop = rx.process_rxchannel_prop(channels) 95 | assert rxch_prop["size"] == 1 96 | np.testing.assert_allclose(rxch_prop["locations"], [[4, 5, 6]]) 97 | np.testing.assert_allclose(rxch_prop["polarization"], [[1, 0, 0]]) 98 | assert rxch_prop["antenna_gains"][0] == -3 99 | np.testing.assert_allclose(rxch_prop["az_angles"][0], [-45, 45]) 100 | np.testing.assert_allclose(rxch_prop["az_patterns"][0], [0, 0]) 101 | np.testing.assert_allclose(rxch_prop["el_angles"][0], [-60, 60]) 102 | np.testing.assert_allclose(rxch_prop["el_patterns"][0], [0, 0]) 103 | 104 | def test_process_rxchannel_prop_multiple_channels(self): 105 | """Test processing of receiver channel properties with multiple channels.""" 106 | rx = Receiver(fs=10e6) 107 | channels = [ 108 | {"location": (0, 0, 0)}, 109 | { 110 | "location": (10, 0, 0), 111 | "polarization": [0, 1, 0], 112 | "azimuth_angle": [-90, 90], 113 | "azimuth_pattern": [-10, -10], 114 | "elevation_angle": [-90, 90], 115 | "elevation_pattern": [-10, -10], 116 | }, 117 | ] 118 | rxch_prop = rx.process_rxchannel_prop(channels) 119 | assert rxch_prop["size"] == 2 120 | np.testing.assert_allclose(rxch_prop["locations"], [[0, 0, 0], [10, 0, 0]]) 121 | np.testing.assert_allclose(rxch_prop["polarization"], [[0, 0, 1], [0, 1, 0]]) 122 | np.testing.assert_allclose(rxch_prop["antenna_gains"], [0, -10]) 123 | 124 | def test_process_rxchannel_prop_invalid_pattern_length(self): 125 | """Test processing of receiver channel properties with invalid pattern length.""" 126 | rx = Receiver(fs=10e6) 127 | channels = [ 128 | { 129 | "location": (0, 0, 0), 130 | "azimuth_angle": [-90, 90], 131 | "azimuth_pattern": [-10], 132 | } 133 | ] 134 | with pytest.raises(ValueError): 135 | rx.process_rxchannel_prop(channels) 136 | 137 | def test_process_rxchannel_prop_invalid_elevation_pattern_length(self): 138 | """Test processing of receiver channel properties with invalid elevation pattern length.""" 139 | rx = Receiver(fs=10e6) 140 | channels = [ 141 | { 142 | "location": (0, 0, 0), 143 | "elevation_angle": [-90, 90], 144 | "elevation_pattern": [-10], 145 | } 146 | ] 147 | with pytest.raises(ValueError): 148 | rx.process_rxchannel_prop(channels) 149 | -------------------------------------------------------------------------------- /tests/test_module_sim_lidar.py: -------------------------------------------------------------------------------- 1 | """ 2 | A Python module for radar simulation 3 | 4 | --- 5 | 6 | - Copyright (C) 2018 - PRESENT radarsimx.com 7 | - E-mail: info@radarsimx.com 8 | - Website: https://radarsimx.com 9 | 10 | :: 11 | 12 | ██████╗ █████╗ ██████╗ █████╗ ██████╗ ███████╗██╗███╗ ███╗██╗ ██╗ 13 | ██╔══██╗██╔══██╗██╔══██╗██╔══██╗██╔══██╗██╔════╝██║████╗ ████║╚██╗██╔╝ 14 | ██████╔╝███████║██║ ██║███████║██████╔╝███████╗██║██╔████╔██║ ╚███╔╝ 15 | ██╔══██╗██╔══██║██║ ██║██╔══██║██╔══██╗╚════██║██║██║╚██╔╝██║ ██╔██╗ 16 | ██║ ██║██║ ██║██████╔╝██║ ██║██║ ██║███████║██║██║ ╚═╝ ██║██╔╝ ██╗ 17 | ╚═╝ ╚═╝╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ 18 | 19 | """ 20 | 21 | import numpy as np 22 | 23 | from radarsimpy.simulator import sim_lidar # pylint: disable=no-name-in-module 24 | 25 | 26 | def test_lidar_scene_basic(): 27 | """ 28 | Basic test case with a single target and a simple lidar setup. 29 | """ 30 | lidar = { 31 | "position": np.array([0, 0, 0]), 32 | "phi": np.array([0]), 33 | "theta": np.array([90]), 34 | } 35 | targets = [ 36 | { 37 | "model": "./models/plate5x5.stl", 38 | "location": np.array([10, 0, 0]), 39 | } 40 | ] 41 | rays = sim_lidar(lidar, targets) 42 | assert rays.shape[0] == 1 # Check if rays are generated 43 | assert np.allclose( 44 | rays[0]["positions"], [10, 0, 0], atol=1e-05 45 | ) # Check lidar position 46 | assert np.allclose( 47 | rays[0]["directions"], [-1, 0, 0], atol=1e-05 48 | ) # Check ray direction 49 | 50 | 51 | def test_lidar_scene_multiple_targets(): 52 | """ 53 | Test with multiple targets. 54 | """ 55 | lidar = { 56 | "position": np.array([0, 0, 0]), 57 | "phi": np.array([0, 90]), 58 | "theta": np.array([90]), 59 | } 60 | targets = [ 61 | { 62 | "model": "./models/ball_1m.stl", 63 | "location": np.array([10, 0, 0]), 64 | }, 65 | { 66 | "model": "./models/ball_1m.stl", 67 | "location": np.array([0, 10, 0]), 68 | }, 69 | ] 70 | rays = sim_lidar(lidar, targets) 71 | assert rays.shape[0] == 2 # Check if rays are generated for all targets 72 | assert np.allclose( 73 | rays[0]["positions"], [9.5000010e00, 0.0000000e00, -4.1525823e-07], atol=1e-05 74 | ) 75 | assert np.allclose( 76 | rays[0]["directions"], [-0.9956786, 0.06597178, -0.06535852], atol=1e-05 77 | ) 78 | assert np.allclose( 79 | rays[1]["positions"], [-4.1526718e-07, 9.5002060e00, -4.1526718e-07], atol=1e-05 80 | ) 81 | assert np.allclose( 82 | rays[1]["directions"], [-0.03301523, -0.99731606, -0.06534925], atol=1e-05 83 | ) 84 | 85 | 86 | def test_lidar_scene_target_movement(): 87 | """ 88 | Test with a moving target. 89 | """ 90 | lidar = { 91 | "position": np.array([0, 0, 0]), 92 | "phi": np.array([0]), 93 | "theta": np.array([90]), 94 | } 95 | targets = [ 96 | { 97 | "model": "./models/plate5x5.stl", 98 | "location": np.array([10, 0, 0]), 99 | "speed": np.array([1, 0, 0]), 100 | } 101 | ] 102 | rays_t0 = sim_lidar(lidar, targets, frame_time=0) 103 | rays_t1 = sim_lidar(lidar, targets, frame_time=1) 104 | assert np.allclose(rays_t0[0]["positions"], [10, 0, 0], atol=1e-05) 105 | assert np.allclose(rays_t0[0]["directions"], [-1, 0, 0], atol=1e-05) 106 | assert np.allclose(rays_t1[0]["positions"], [11, 0, 0], atol=1e-05) 107 | assert np.allclose(rays_t1[0]["directions"], [-1, 0, 0], atol=1e-05) 108 | 109 | 110 | def test_lidar_scene_target_rotation(): 111 | """ 112 | Test with a rotating target. 113 | """ 114 | lidar = { 115 | "position": np.array([0, 0, 0]), 116 | "phi": np.array([0]), 117 | "theta": np.array([90]), 118 | } 119 | targets = [ 120 | { 121 | "model": "./models/plate5x5.stl", 122 | "location": np.array([10, 0, 0]), 123 | "rotation_rate": np.array([45, 0, 0]), 124 | } 125 | ] 126 | rays_t0 = sim_lidar(lidar, targets, frame_time=0) 127 | rays_t1 = sim_lidar(lidar, targets, frame_time=1) 128 | assert np.allclose(rays_t0[0]["positions"], [10, 0, 0], atol=1e-05) 129 | assert np.allclose(rays_t0[0]["directions"], [-1, 0, 0], atol=1e-05) 130 | assert np.allclose(rays_t1[0]["positions"], [10, 0, 0], atol=1e-05) 131 | assert np.allclose(rays_t1[0]["directions"], [0, -1, 0], atol=1e-05) 132 | -------------------------------------------------------------------------------- /tests/test_module_sim_rcs.py: -------------------------------------------------------------------------------- 1 | """ 2 | A Python module for radar simulation 3 | 4 | --- 5 | 6 | - Copyright (C) 2018 - PRESENT radarsimx.com 7 | - E-mail: info@radarsimx.com 8 | - Website: https://radarsimx.com 9 | 10 | :: 11 | 12 | ██████╗ █████╗ ██████╗ █████╗ ██████╗ ███████╗██╗███╗ ███╗██╗ ██╗ 13 | ██╔══██╗██╔══██╗██╔══██╗██╔══██╗██╔══██╗██╔════╝██║████╗ ████║╚██╗██╔╝ 14 | ██████╔╝███████║██║ ██║███████║██████╔╝███████╗██║██╔████╔██║ ╚███╔╝ 15 | ██╔══██╗██╔══██║██║ ██║██╔══██║██╔══██╗╚════██║██║██║╚██╔╝██║ ██╔██╗ 16 | ██║ ██║██║ ██║██████╔╝██║ ██║██║ ██║███████║██║██║ ╚═╝ ██║██╔╝ ██╗ 17 | ╚═╝ ╚═╝╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ 18 | 19 | """ 20 | 21 | import pytest 22 | import numpy as np 23 | import numpy.testing as npt 24 | 25 | from radarsimpy.simulator import sim_rcs # pylint: disable=no-name-in-module 26 | 27 | 28 | def test_rcs_momostatic(): 29 | """ 30 | Tests the radar cross-section (RCS) calculation for a monostatic radar system. 31 | """ 32 | phi = 0 33 | theta = 90 34 | freq = np.array([1e9, 3e9]) 35 | pol = np.array([0, 0, 1]) 36 | density = 1 37 | rcs = np.zeros_like(freq) 38 | target = { 39 | "model": "./models/plate5x5.stl", 40 | "location": (0, 0, 0), 41 | } 42 | for f_idx, f in enumerate(freq): 43 | rcs[f_idx] = 10 * np.log10( 44 | sim_rcs([target], f, phi, theta, inc_pol=pol, density=density) 45 | ) 46 | 47 | npt.assert_almost_equal(rcs, np.array([48.3, 59.2]), decimal=1) 48 | 49 | 50 | def test_rcs_bistatic(): 51 | """ 52 | Tests the radar cross-section (RCS) calculation for a bistatic radar system. 53 | """ 54 | phi = np.array([-30, -24, 65]) 55 | theta = 90 56 | 57 | inc_phi = 30 58 | inc_theta = 90 59 | 60 | freq = 1e9 61 | pol = np.array([0, 0, 1]) 62 | density = 1 63 | rcs = np.zeros_like(phi) 64 | 65 | target = { 66 | "model": "./models/plate5x5.stl", 67 | "location": (0, 0, 0), 68 | } 69 | for phi_idx, phi_ang in enumerate(phi): 70 | rcs[phi_idx] = 10 * np.log10( 71 | sim_rcs( 72 | [target], 73 | freq, 74 | inc_phi=inc_phi, 75 | inc_theta=inc_theta, 76 | inc_pol=pol, 77 | obs_phi=phi_ang, 78 | obs_theta=theta, 79 | density=density, 80 | ) 81 | ) 82 | 83 | npt.assert_almost_equal(rcs, np.array([47, 34, 6]), decimal=0) 84 | -------------------------------------------------------------------------------- /tests/test_module_tools.py: -------------------------------------------------------------------------------- 1 | """ 2 | A Python module for radar simulation 3 | 4 | --- 5 | 6 | - Copyright (C) 2018 - PRESENT radarsimx.com 7 | - E-mail: info@radarsimx.com 8 | - Website: https://radarsimx.com 9 | 10 | :: 11 | 12 | ██████╗ █████╗ ██████╗ █████╗ ██████╗ ███████╗██╗███╗ ███╗██╗ ██╗ 13 | ██╔══██╗██╔══██╗██╔══██╗██╔══██╗██╔══██╗██╔════╝██║████╗ ████║╚██╗██╔╝ 14 | ██████╔╝███████║██║ ██║███████║██████╔╝███████╗██║██╔████╔██║ ╚███╔╝ 15 | ██╔══██╗██╔══██║██║ ██║██╔══██║██╔══██╗╚════██║██║██║╚██╔╝██║ ██╔██╗ 16 | ██║ ██║██║ ██║██████╔╝██║ ██║██║ ██║███████║██║██║ ╚═╝ ██║██╔╝ ██╗ 17 | ╚═╝ ╚═╝╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ 18 | 19 | """ 20 | 21 | import numpy.testing as npt 22 | 23 | from radarsimpy.tools import roc_pd, roc_snr, threshold 24 | 25 | 26 | def test_roc_pd(): 27 | """ 28 | Test the ROC Pd function 29 | """ 30 | npt.assert_almost_equal(roc_pd(1e-8, 13, 1, "Swerling 5"), 0.6287, decimal=4) 31 | npt.assert_almost_equal(roc_pd(1e-8, 11, 1, "Swerling 5"), 0.1683, decimal=4) 32 | npt.assert_almost_equal(roc_pd(1e-8, -3.2, 256, "Swerling 5"), 0.8411, decimal=4) 33 | npt.assert_almost_equal(roc_pd(1e-8, -4.8, 256, "Swerling 5"), 0.2249, decimal=4) 34 | 35 | npt.assert_almost_equal(roc_pd(1e-9, -10, 256, "Coherent"), 0.8765, decimal=4) 36 | npt.assert_almost_equal(roc_pd(1e-9, -12, 256, "Coherent"), 0.3767, decimal=4) 37 | npt.assert_almost_equal(roc_pd(1e-6, 12.4, 1, "Coherent"), 0.8733, decimal=4) 38 | npt.assert_almost_equal(roc_pd(1e-6, 8.8, 1, "Coherent"), 0.1953, decimal=4) 39 | 40 | npt.assert_almost_equal(roc_pd(1e-4, 16, 1, "Swerling 1"), 0.7980, decimal=4) 41 | npt.assert_almost_equal(roc_pd(1e-4, 6.8, 1, "Swerling 1"), 0.2036, decimal=4) 42 | npt.assert_almost_equal(roc_pd(1e-4, 3.6, 256, "Swerling 1"), 0.8959, decimal=4) 43 | npt.assert_almost_equal(roc_pd(1e-4, -7.6, 256, "Swerling 1"), 0.2560, decimal=4) 44 | 45 | npt.assert_almost_equal(roc_pd(1e-4, -4.4, 256, "Swerling 2"), 0.9120, decimal=4) 46 | npt.assert_almost_equal(roc_pd(1e-4, -7.2, 256, "Swerling 2"), 0.2125, decimal=4) 47 | npt.assert_almost_equal(roc_pd(1e-4, 16, 1, "Swerling 2"), 0.7980, decimal=4) 48 | npt.assert_almost_equal(roc_pd(1e-4, 6.8, 1, "Swerling 2"), 0.2036, decimal=4) 49 | 50 | npt.assert_almost_equal(roc_pd(1e-4, 15.2, 1, "Swerling 3"), 0.8846, decimal=4) 51 | npt.assert_almost_equal(roc_pd(1e-4, 6.8, 1, "Swerling 3"), 0.1931, decimal=4) 52 | npt.assert_almost_equal(roc_pd(1e-4, -0.4, 256, "Swerling 3"), 0.8889, decimal=4) 53 | npt.assert_almost_equal(roc_pd(1e-4, -8.4, 256, "Swerling 3"), 0.1775, decimal=4) 54 | 55 | npt.assert_almost_equal(roc_pd(1e-4, 15.2, 1, "Swerling 4"), 0.8846, decimal=4) 56 | npt.assert_almost_equal(roc_pd(1e-4, 6.8, 1, "Swerling 4"), 0.1931, decimal=4) 57 | npt.assert_almost_equal(roc_pd(1e-4, -4.8, 256, "Swerling 4"), 0.8413, decimal=4) 58 | npt.assert_almost_equal(roc_pd(1e-4, -7.2, 256, "Swerling 4"), 0.2155, decimal=4) 59 | 60 | 61 | def test_threshold(): 62 | """ 63 | Test the ROC threshold function 64 | """ 65 | npt.assert_almost_equal(threshold(1e-4, 1), 9.21, decimal=2) 66 | npt.assert_almost_equal(threshold(1e-4, 10), 26.19, decimal=2) 67 | npt.assert_almost_equal(threshold(1e-4, 20), 41.03, decimal=2) 68 | npt.assert_almost_equal(threshold(1e-4, 40), 67.89, decimal=2) 69 | 70 | 71 | def test_roc_snr(): 72 | """ 73 | Test the ROC SNR function 74 | """ 75 | npt.assert_almost_equal(roc_snr(1e-8, 0.6290, 1, "Swerling 5"), 13, decimal=0) 76 | npt.assert_almost_equal(roc_snr(1e-8, 0.1681, 1, "Swerling 5"), 11, decimal=0) 77 | npt.assert_almost_equal(roc_snr(1e-8, 0.8424, 256, "Swerling 5"), -3.2, decimal=1) 78 | npt.assert_almost_equal(roc_snr(1e-8, 0.2266, 256, "Swerling 5"), -4.8, decimal=1) 79 | 80 | npt.assert_almost_equal(roc_snr(1e-9, 0.8765, 256, "Coherent"), -10, decimal=0) 81 | npt.assert_almost_equal(roc_snr(1e-9, 0.3767, 256, "Coherent"), -12, decimal=0) 82 | npt.assert_almost_equal(roc_snr(1e-6, 0.8733, 1, "Coherent"), 12.4, decimal=1) 83 | npt.assert_almost_equal(roc_snr(1e-6, 0.1953, 1, "Coherent"), 8.8, decimal=1) 84 | 85 | npt.assert_almost_equal(roc_snr(1e-4, 0.7980, 1, "Swerling 1"), 16, decimal=0) 86 | npt.assert_almost_equal(roc_snr(1e-4, 0.2036, 1, "Swerling 1"), 6.8, decimal=1) 87 | npt.assert_almost_equal(roc_snr(1e-4, 0.8959, 256, "Swerling 1"), 3.6, decimal=1) 88 | npt.assert_almost_equal(roc_snr(1e-4, 0.2560, 256, "Swerling 1"), -7.6, decimal=1) 89 | 90 | npt.assert_almost_equal(roc_snr(1e-4, 0.9120, 256, "Swerling 2"), -4.4, decimal=1) 91 | npt.assert_almost_equal(roc_snr(1e-4, 0.2125, 256, "Swerling 2"), -7.2, decimal=1) 92 | npt.assert_almost_equal(roc_snr(1e-4, 0.7980, 1, "Swerling 2"), 16, decimal=0) 93 | npt.assert_almost_equal(roc_snr(1e-4, 0.2036, 1, "Swerling 2"), 6.8, decimal=1) 94 | 95 | npt.assert_almost_equal(roc_snr(1e-4, 0.8846, 1, "Swerling 3"), 15.2, decimal=1) 96 | npt.assert_almost_equal(roc_snr(1e-4, 0.1931, 1, "Swerling 3"), 6.8, decimal=1) 97 | npt.assert_almost_equal(roc_snr(1e-4, 0.8889, 256, "Swerling 3"), -0.4, decimal=1) 98 | npt.assert_almost_equal(roc_snr(1e-4, 0.1772, 256, "Swerling 3"), -8.4, decimal=1) 99 | 100 | npt.assert_almost_equal(roc_snr(1e-4, 0.8846, 1, "Swerling 4"), 15.2, decimal=1) 101 | npt.assert_almost_equal(roc_snr(1e-4, 0.1931, 1, "Swerling 4"), 6.8, decimal=1) 102 | npt.assert_almost_equal(roc_snr(1e-4, 0.8413, 256, "Swerling 4"), -4.8, decimal=1) 103 | npt.assert_almost_equal(roc_snr(1e-4, 0.2155, 256, "Swerling 4"), -7.2, decimal=1) 104 | -------------------------------------------------------------------------------- /tests/test_system_cw_radar.py: -------------------------------------------------------------------------------- 1 | """ 2 | System level test for raytracing-based sim_radar simulation 3 | 4 | --- 5 | 6 | - Copyright (C) 2018 - PRESENT radarsimx.com 7 | - E-mail: info@radarsimx.com 8 | - Website: https://radarsimx.com 9 | 10 | :: 11 | 12 | ██████╗ █████╗ ██████╗ █████╗ ██████╗ ███████╗██╗███╗ ███╗██╗ ██╗ 13 | ██╔══██╗██╔══██╗██╔══██╗██╔══██╗██╔══██╗██╔════╝██║████╗ ████║╚██╗██╔╝ 14 | ██████╔╝███████║██║ ██║███████║██████╔╝███████╗██║██╔████╔██║ ╚███╔╝ 15 | ██╔══██╗██╔══██║██║ ██║██╔══██║██╔══██╗╚════██║██║██║╚██╔╝██║ ██╔██╗ 16 | ██║ ██║██║ ██║██████╔╝██║ ██║██║ ██║███████║██║██║ ╚═╝ ██║██╔╝ ██╗ 17 | ╚═╝ ╚═╝╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ 18 | 19 | """ 20 | 21 | import numpy as np 22 | from radarsimpy import Radar, Transmitter, Receiver 23 | from radarsimpy.simulator import sim_radar # pylint: disable=no-name-in-module 24 | 25 | 26 | def test_sim_cw(): 27 | """ 28 | Test the CW radar simulator. 29 | """ 30 | tx = Transmitter( 31 | f=24.125e9, t=20, tx_power=10, pulses=1, channels=[{"location": (0, 0, 0)}] 32 | ) 33 | rx = Receiver( 34 | fs=0.5, 35 | noise_figure=12, 36 | rf_gain=20, 37 | baseband_gain=50, 38 | load_resistor=1000, 39 | channels=[{"location": (0, 0, 0)}], 40 | ) 41 | radar = Radar(transmitter=tx, receiver=rx) 42 | 43 | target = { 44 | "location": ( 45 | 1.4 + 1e-3 * np.sin(2 * np.pi * 0.1 * radar.time_prop["timestamp"]), 46 | 0, 47 | 0, 48 | ), 49 | "rcs": -10, 50 | "phase": 0, 51 | } 52 | targets = [target] 53 | 54 | result = sim_radar(radar, targets) 55 | 56 | assert np.allclose( 57 | result["baseband"], 58 | np.array( 59 | [ 60 | [ 61 | [ 62 | -0.28017324 + 0.57151868j, 63 | -0.62818491 + 0.09703288j, 64 | -0.55171423 + 0.31632635j, 65 | 0.08803159 + 0.63092172j, 66 | 0.30893151 + 0.55748964j, 67 | -0.28016539 + 0.57152253j, 68 | -0.62820815 + 0.09688231j, 69 | -0.55177488 + 0.31622055j, 70 | 0.08815256 + 0.63090483j, 71 | 0.3088246 + 0.55754887j, 72 | ] 73 | ] 74 | ] 75 | ), 76 | atol=1e-05, 77 | ) 78 | 79 | assert np.allclose( 80 | result["timestamp"], 81 | np.array([[[0.0, 2.0, 4.0, 6.0, 8.0, 10.0, 12.0, 14.0, 16.0, 18.0]]]), 82 | ) 83 | 84 | 85 | def test_sim_cw_raytracing(): 86 | """ 87 | Test the CW radar simulator. 88 | """ 89 | tx = Transmitter( 90 | f=24.125e9, t=20, tx_power=10, pulses=1, channels=[{"location": (0, 0, 0)}] 91 | ) 92 | rx = Receiver( 93 | fs=0.5, 94 | noise_figure=12, 95 | rf_gain=20, 96 | baseband_gain=50, 97 | load_resistor=1000, 98 | channels=[{"location": (0, 0, 0)}], 99 | ) 100 | radar = Radar(transmitter=tx, receiver=rx) 101 | 102 | target = { 103 | "model": "./models/cr.stl", 104 | "location": ( 105 | 1.4 + 1e-3 * np.sin(2 * np.pi * 0.1 * radar.time_prop["timestamp"]), 106 | 0, 107 | 0, 108 | ), 109 | } 110 | targets = [target] 111 | 112 | result = sim_radar(radar, targets, density=1, level="sample") 113 | 114 | assert np.allclose( 115 | result["baseband"], 116 | np.array( 117 | [ 118 | [ 119 | [ 120 | 2.00757447 + 2.22013679j, 121 | -0.67349305 + 2.91739497j, 122 | 0.41935286 + 2.9642617j, 123 | 2.90617447 + 0.7143177j, 124 | 2.96842678 - 0.37725781j, 125 | 2.00762345 + 2.22007286j, 126 | -0.67349343 + 2.91735525j, 127 | 0.41948373 + 2.96425345j, 128 | 2.90613151 + 0.71437308j, 129 | 2.96848674 - 0.37720091j, 130 | ] 131 | ] 132 | ] 133 | ), 134 | atol=1e-05, 135 | ) 136 | 137 | assert np.allclose( 138 | result["timestamp"], 139 | np.array([[[0.0, 2.0, 4.0, 6.0, 8.0, 10.0, 12.0, 14.0, 16.0, 18.0]]]), 140 | ) 141 | -------------------------------------------------------------------------------- /tests/test_system_phase_noise.py: -------------------------------------------------------------------------------- 1 | """ 2 | A Python module for radar simulation 3 | 4 | --- 5 | 6 | - Copyright (C) 2018 - PRESENT radarsimx.com 7 | - E-mail: info@radarsimx.com 8 | - Website: https://radarsimx.com 9 | 10 | :: 11 | 12 | ██████╗ █████╗ ██████╗ █████╗ ██████╗ ███████╗██╗███╗ ███╗██╗ ██╗ 13 | ██╔══██╗██╔══██╗██╔══██╗██╔══██╗██╔══██╗██╔════╝██║████╗ ████║╚██╗██╔╝ 14 | ██████╔╝███████║██║ ██║███████║██████╔╝███████╗██║██╔████╔██║ ╚███╔╝ 15 | ██╔══██╗██╔══██║██║ ██║██╔══██║██╔══██╗╚════██║██║██║╚██╔╝██║ ██╔██╗ 16 | ██║ ██║██║ ██║██████╔╝██║ ██║██║ ██║███████║██║██║ ╚═╝ ██║██╔╝ ██╗ 17 | ╚═╝ ╚═╝╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ 18 | 19 | """ 20 | 21 | from scipy import signal 22 | import numpy as np 23 | import numpy.testing as npt 24 | 25 | from radarsimpy import Radar, Transmitter, Receiver 26 | from radarsimpy.simulator import sim_radar # pylint: disable=no-name-in-module 27 | from radarsimpy.radar import cal_phase_noise 28 | import radarsimpy.processing as proc 29 | 30 | 31 | def test_phase_noise(): 32 | """ 33 | This function tests phase noise calculation 34 | """ 35 | sig = np.ones((1, 256)) 36 | pn_f = np.array([1000, 10000, 100000, 1000000]) 37 | pn_power_db_per_hz = np.array([-84, -100, -96, -109]) 38 | fs = 4e6 39 | 40 | pn = cal_phase_noise(sig, fs, pn_f, pn_power_db_per_hz, validation=True) 41 | 42 | # f = np.linspace(0, fs, 256) 43 | spec = 20 * np.log10(np.abs(np.fft.fft(pn[0, :] / 256))) 44 | 45 | # pn_power_db = pn_power_db_per_hz+10*np.log10(fs/256) 46 | 47 | npt.assert_almost_equal(spec[1], -63.4, decimal=2) 48 | npt.assert_almost_equal(spec[6], -60.21, decimal=2) 49 | npt.assert_almost_equal(spec[64], -73.09, decimal=2) 50 | 51 | 52 | def test_fmcw_phase_noise(): 53 | """ 54 | This function tests the phase noise simulation 55 | """ 56 | tx_channel = {"location": (0, 0, 0)} 57 | 58 | pn_f = np.array([1000, 10000, 100000, 1000000]) 59 | pn_power = np.array([-65, -70, -65, -90]) 60 | 61 | tx_pn = Transmitter( 62 | f=[24.125e9 - 50e6, 24.125e9 + 50e6], 63 | t=80e-6, 64 | tx_power=40, 65 | prp=100e-6, 66 | pulses=1, 67 | pn_f=pn_f, 68 | pn_power=pn_power, 69 | channels=[tx_channel], 70 | ) 71 | 72 | tx = Transmitter( 73 | f=[24.125e9 - 50e6, 24.125e9 + 50e6], 74 | t=80e-6, 75 | tx_power=40, 76 | prp=100e-6, 77 | pulses=1, 78 | channels=[tx_channel], 79 | ) 80 | 81 | rx_channel = {"location": (0, 0, 0)} 82 | 83 | rx = Receiver( 84 | fs=2e6, 85 | noise_figure=12, 86 | rf_gain=20, 87 | load_resistor=500, 88 | baseband_gain=30, 89 | channels=[rx_channel], 90 | ) 91 | 92 | radar_pn = Radar(transmitter=tx_pn, receiver=rx, seed=1234, validation=True) 93 | radar = Radar(transmitter=tx, receiver=rx, seed=1234, validation=True) 94 | 95 | target_1 = {"location": (150, 20, 0), "speed": (0, 0, 0), "rcs": 60, "phase": 0} 96 | 97 | targets = [target_1] 98 | 99 | data_cpp_pn = sim_radar(radar_pn, targets) 100 | data_matrix_cpp_pn = data_cpp_pn["baseband"] 101 | data_cpp = sim_radar(radar, targets) 102 | data_matrix_cpp = data_cpp["baseband"] 103 | 104 | range_window = signal.windows.chebwin(radar.sample_prop["samples_per_pulse"], at=60) 105 | range_profile_pn = proc.range_fft(data_matrix_cpp_pn, range_window) 106 | range_profile = proc.range_fft(data_matrix_cpp, range_window) 107 | 108 | range_profile_pn = 20 * np.log10(np.abs(range_profile_pn[0, 0, :])) 109 | range_profile = 20 * np.log10(np.abs(range_profile[0, 0, :])) 110 | 111 | profile_diff = range_profile_pn - range_profile 112 | 113 | npt.assert_allclose( 114 | profile_diff, 115 | np.array( 116 | [ 117 | 8.15236683e00, 118 | 8.62450253e00, 119 | 9.94566903e00, 120 | 8.41230320e00, 121 | 1.35971821e01, 122 | 1.62558002e01, 123 | 1.93212483e01, 124 | 1.31870147e01, 125 | 1.75406669e01, 126 | 1.38916956e01, 127 | 8.49327223e00, 128 | 1.86175487e01, 129 | 1.40706202e01, 130 | 1.76409000e01, 131 | 2.25048424e01, 132 | 1.45015542e01, 133 | 2.05210282e01, 134 | 1.86531317e01, 135 | 1.71046366e01, 136 | 4.10641684e00, 137 | 3.79405756e01, 138 | 2.75389544e01, 139 | 3.90926646e01, 140 | 6.27310149e00, 141 | 2.16279769e01, 142 | 2.52661677e01, 143 | 3.36658700e01, 144 | 2.88195604e01, 145 | 3.91194415e01, 146 | 4.59002235e01, 147 | 5.04732280e01, 148 | 3.94599231e01, 149 | 3.24503013e01, 150 | 2.96344893e01, 151 | 3.03904606e01, 152 | 2.83176478e01, 153 | 3.11181814e01, 154 | 2.53430024e01, 155 | 2.30302068e01, 156 | 1.72725494e01, 157 | 1.87363003e01, 158 | 1.71670574e01, 159 | 1.69392597e01, 160 | 1.04715126e01, 161 | 1.59758951e01, 162 | 7.97696140e00, 163 | 1.11580555e01, 164 | 1.44807343e01, 165 | 1.40912099e01, 166 | 1.41879078e01, 167 | 1.29359464e01, 168 | 1.20614666e01, 169 | 1.08958080e01, 170 | 8.03589480e00, 171 | 1.04874421e01, 172 | 6.87794869e00, 173 | 8.70538605e00, 174 | 1.02700420e01, 175 | -1.50547286e01, 176 | -1.89459088e00, 177 | 4.42629745e-01, 178 | 1.06804309e00, 179 | 7.46382310e00, 180 | -9.28290884e00, 181 | 7.02103212e00, 182 | 9.03193911e00, 183 | 4.89482735e00, 184 | 3.28958093e00, 185 | 5.25774634e00, 186 | 1.72219960e00, 187 | -7.17070791e-01, 188 | 4.87554162e00, 189 | 1.26008143e00, 190 | 4.53897983e00, 191 | -1.31826831e01, 192 | -3.97219210e-01, 193 | -1.88513576e01, 194 | -1.00752817e00, 195 | 5.18454718e-01, 196 | -6.54762623e-01, 197 | -9.00964293e-01, 198 | 9.64784119e-01, 199 | 4.68541185e00, 200 | 8.86610661e00, 201 | 1.16768714e01, 202 | 1.40424314e01, 203 | 1.23302152e01, 204 | 1.20765995e01, 205 | 1.62397170e01, 206 | 1.68698397e01, 207 | 1.63122911e01, 208 | 1.63895149e01, 209 | 2.22444422e01, 210 | 3.04330686e01, 211 | 3.34724037e01, 212 | 1.52160781e01, 213 | 1.19215742e01, 214 | 1.53405891e01, 215 | 2.20231153e01, 216 | -1.05176631e00, 217 | 8.30268334e-02, 218 | -3.95634294e-02, 219 | 5.80602668e-02, 220 | -6.59517939e-01, 221 | 1.34651845e01, 222 | 1.53163491e01, 223 | 2.45530362e01, 224 | 6.18439387e01, 225 | 2.27776171e01, 226 | 1.74963002e01, 227 | 1.77674211e01, 228 | 1.50284897e01, 229 | 1.25935400e01, 230 | 6.74313391e00, 231 | 6.07564392e00, 232 | 1.00810249e01, 233 | 4.78584396e00, 234 | -4.26958606e00, 235 | -5.58157923e00, 236 | -2.45911840e00, 237 | 4.72325979e00, 238 | 7.08286461e00, 239 | 3.99875291e00, 240 | 6.58567146e00, 241 | 7.95576358e00, 242 | 4.24917591e00, 243 | 4.43187106e00, 244 | 6.59002957e00, 245 | 5.28506440e00, 246 | -1.07894150e00, 247 | -2.71490104e00, 248 | 8.49118834e00, 249 | 7.33305209e00, 250 | 7.58354325e00, 251 | 3.00461831e00, 252 | 1.45074678e00, 253 | 9.11779594e00, 254 | 6.87048797e00, 255 | 3.04951896e-01, 256 | 8.08362341e00, 257 | 6.77752185e00, 258 | 6.09439334e00, 259 | 8.00496874e00, 260 | 9.93519129e00, 261 | 9.16553127e00, 262 | 9.02552562e00, 263 | 1.17643448e01, 264 | 4.90491193e00, 265 | -1.86823144e00, 266 | 6.35799399e00, 267 | 1.27685095e01, 268 | 1.34158011e01, 269 | 1.15192466e01, 270 | 1.08706573e01, 271 | 7.58792202e00, 272 | -8.31226080e00, 273 | 6.27604552e00, 274 | 7.86567283e00, 275 | 1.32263561e01, 276 | -6.63679916e00, 277 | ] 278 | ), 279 | atol=1, 280 | ) 281 | --------------------------------------------------------------------------------