├── .github └── workflows │ ├── build_wheels.yml │ └── install_dependencies.py ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── flatbuffers ├── events.fbs ├── frame.fbs ├── imus.fbs ├── ioheader.fbs └── triggers.fbs ├── pyproject.toml ├── src ├── aedat_core.rs ├── events_generated.rs ├── frame_generated.rs ├── imus_generated.rs ├── ioheader_generated.rs ├── lib.rs └── triggers_generated.rs ├── test.py └── test_data.aedat4 /.github/workflows/build_wheels.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | pull_request: 6 | release: 7 | types: 8 | - published 9 | 10 | env: 11 | CIBW_SKIP: cp36-* cp37-* cp38-* pp* *i686 *win32 12 | 13 | # cibuildhweel macOS configuration 14 | CIBW_ARCHS_MACOS: native 15 | 16 | # cibuildhweel linux configuration 17 | CIBW_ARCHS_LINUX: x86_64 18 | CIBW_MANYLINUX_X86_64_IMAGE: manylinux_2_28 19 | CIBW_MUSLLINUX_X86_64_IMAGE: musllinux_1_2 20 | CIBW_BEFORE_ALL_LINUX: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=stable --profile=minimal -y 21 | CIBW_ENVIRONMENT_LINUX: 'PATH="$HOME/.cargo/bin:$PATH"' 22 | 23 | # cibuildhweel Windows configuration 24 | CIBW_ARCHS_WINDOWS: AMD64 25 | 26 | jobs: 27 | build_wheels: 28 | name: Build wheels on ${{ matrix.os }} 29 | runs-on: ${{ matrix.os }} 30 | strategy: 31 | matrix: 32 | # macos-13 is an Intel runner, macos-latest is an ARM runner 33 | os: [macos-13, macos-latest, ubuntu-latest, windows-latest] 34 | steps: 35 | - uses: actions/checkout@v4 36 | - uses: dtolnay/rust-toolchain@stable 37 | - run: python -m pip install cibuildwheel==2.21.3 38 | - run: echo "MACOSX_DEPLOYMENT_TARGET=$(sw_vers -productVersion | cut -d '.' -f 1-2)" >> $GITHUB_ENV 39 | if: startsWith(matrix.os, 'macos') 40 | - run: python -m cibuildwheel 41 | - uses: actions/upload-artifact@v4 42 | with: 43 | name: ${{ matrix.os }} 44 | path: wheelhouse 45 | test_library: 46 | name: Test library on ${{ matrix.os }} with Python ${{ matrix.python }} and numpy ${{ matrix.numpy }} 47 | runs-on: ${{ matrix.os }} 48 | needs: [build_wheels] 49 | strategy: 50 | matrix: 51 | numpy: ["==1.26.4", ">=2"] 52 | python: ["3.9", "3.10", "3.11", "3.12", "3.13"] 53 | # macos-13 is an Intel runner, macos-latest is an ARM runner 54 | os: [macos-13, macos-latest, ubuntu-latest, windows-latest] 55 | exclude: 56 | # this configuration yields 'Windows fatal exception: access violation' 57 | # this may get fixed in a maintenance upgrade of 1.26.x (x > 4) 58 | - numpy: ==1.26.4 59 | os: windows-latest 60 | python: 3.13 61 | steps: 62 | - uses: actions/setup-python@v5 63 | with: 64 | python-version: ${{ matrix.python }} 65 | - uses: actions/checkout@v4 66 | - uses: actions/download-artifact@v4 67 | with: 68 | name: ${{ matrix.os }} 69 | path: wheelhouse 70 | - run: python -m pip install toml 71 | - run: python -m pip install 'numpy${{ matrix.numpy }}' 72 | - run: python .github/workflows/install_dependencies.py 73 | - run: ls wheelhouse 74 | - run: python -m pip install --no-index --find-links wheelhouse aedat 75 | - run: python test.py 76 | build_sdist: 77 | name: Build source distribution 78 | runs-on: ubuntu-latest 79 | steps: 80 | - uses: actions/checkout@v4 81 | - run: pipx run build --sdist 82 | - uses: actions/upload-artifact@v4 83 | with: 84 | name: dist 85 | path: dist/*.tar.gz 86 | upload_pypi: 87 | name: Upload wheels and sidst to PyPI 88 | runs-on: ubuntu-latest 89 | needs: [build_wheels, test_library, build_sdist] 90 | if: github.event_name == 'release' && github.event.action == 'published' 91 | steps: 92 | - uses: actions/download-artifact@v4 93 | with: 94 | path: wheelhouse 95 | pattern: "*" 96 | merge-multiple: true 97 | - uses: actions/download-artifact@v4 98 | with: 99 | name: dist 100 | path: dist 101 | - run: mv wheelhouse/* dist/ 102 | - uses: pypa/gh-action-pypi-publish@v1.8.14 103 | with: 104 | password: ${{ secrets.PYPI_API_TOKEN }} 105 | -------------------------------------------------------------------------------- /.github/workflows/install_dependencies.py: -------------------------------------------------------------------------------- 1 | import pathlib 2 | import subprocess 3 | import sys 4 | 5 | import toml 6 | 7 | dirname = pathlib.Path(__file__).resolve().parent 8 | 9 | print(f"load {dirname.parent.parent / 'pyproject.toml'}") 10 | with open(dirname.parent.parent / "pyproject.toml") as pyproject_file: 11 | pyproject = toml.load(pyproject_file) 12 | 13 | dependencies = [] 14 | if "project" in pyproject: 15 | project = pyproject["project"] 16 | if "dependencies" in project: 17 | dependencies += project["dependencies"] 18 | if "optional-dependencies" in project: 19 | optional_dependencies = project["optional-dependencies"] 20 | if "tests" in optional_dependencies: 21 | dependencies += optional_dependencies["tests"] 22 | 23 | if len(dependencies) > 0: 24 | subprocess.run([sys.executable, "-m", "pip", "install"] + dependencies, check=True) 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | **/*.rs.bk 3 | aedat.so 4 | aedat.pyd 5 | wheelhouse/ 6 | .venv 7 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "aedat" 7 | version = "2.2.0" 8 | dependencies = [ 9 | "flatbuffers", 10 | "lz4", 11 | "numpy", 12 | "pyo3", 13 | "roxmltree", 14 | "zstd", 15 | ] 16 | 17 | [[package]] 18 | name = "autocfg" 19 | version = "1.3.0" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" 22 | 23 | [[package]] 24 | name = "bitflags" 25 | version = "1.3.2" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 28 | 29 | [[package]] 30 | name = "cc" 31 | version = "1.2.10" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "13208fcbb66eaeffe09b99fffbe1af420f00a7b35aa99ad683dfc1aa76145229" 34 | dependencies = [ 35 | "jobserver", 36 | "libc", 37 | "shlex", 38 | ] 39 | 40 | [[package]] 41 | name = "cfg-if" 42 | version = "1.0.0" 43 | source = "registry+https://github.com/rust-lang/crates.io-index" 44 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 45 | 46 | [[package]] 47 | name = "flatbuffers" 48 | version = "24.12.23" 49 | source = "registry+https://github.com/rust-lang/crates.io-index" 50 | checksum = "4f1baf0dbf96932ec9a3038d57900329c015b0bfb7b63d904f3bc27e2b02a096" 51 | dependencies = [ 52 | "bitflags", 53 | "rustc_version", 54 | ] 55 | 56 | [[package]] 57 | name = "heck" 58 | version = "0.5.0" 59 | source = "registry+https://github.com/rust-lang/crates.io-index" 60 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 61 | 62 | [[package]] 63 | name = "indoc" 64 | version = "2.0.5" 65 | source = "registry+https://github.com/rust-lang/crates.io-index" 66 | checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" 67 | 68 | [[package]] 69 | name = "jobserver" 70 | version = "0.1.31" 71 | source = "registry+https://github.com/rust-lang/crates.io-index" 72 | checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" 73 | dependencies = [ 74 | "libc", 75 | ] 76 | 77 | [[package]] 78 | name = "libc" 79 | version = "0.2.155" 80 | source = "registry+https://github.com/rust-lang/crates.io-index" 81 | checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" 82 | 83 | [[package]] 84 | name = "lz4" 85 | version = "1.28.1" 86 | source = "registry+https://github.com/rust-lang/crates.io-index" 87 | checksum = "a20b523e860d03443e98350ceaac5e71c6ba89aea7d960769ec3ce37f4de5af4" 88 | dependencies = [ 89 | "lz4-sys", 90 | ] 91 | 92 | [[package]] 93 | name = "lz4-sys" 94 | version = "1.11.1+lz4-1.10.0" 95 | source = "registry+https://github.com/rust-lang/crates.io-index" 96 | checksum = "6bd8c0d6c6ed0cd30b3652886bb8711dc4bb01d637a68105a3d5158039b418e6" 97 | dependencies = [ 98 | "cc", 99 | "libc", 100 | ] 101 | 102 | [[package]] 103 | name = "matrixmultiply" 104 | version = "0.3.8" 105 | source = "registry+https://github.com/rust-lang/crates.io-index" 106 | checksum = "7574c1cf36da4798ab73da5b215bbf444f50718207754cb522201d78d1cd0ff2" 107 | dependencies = [ 108 | "autocfg", 109 | "rawpointer", 110 | ] 111 | 112 | [[package]] 113 | name = "memoffset" 114 | version = "0.9.1" 115 | source = "registry+https://github.com/rust-lang/crates.io-index" 116 | checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" 117 | dependencies = [ 118 | "autocfg", 119 | ] 120 | 121 | [[package]] 122 | name = "ndarray" 123 | version = "0.15.6" 124 | source = "registry+https://github.com/rust-lang/crates.io-index" 125 | checksum = "adb12d4e967ec485a5f71c6311fe28158e9d6f4bc4a447b474184d0f91a8fa32" 126 | dependencies = [ 127 | "matrixmultiply", 128 | "num-complex", 129 | "num-integer", 130 | "num-traits", 131 | "rawpointer", 132 | ] 133 | 134 | [[package]] 135 | name = "num-complex" 136 | version = "0.4.6" 137 | source = "registry+https://github.com/rust-lang/crates.io-index" 138 | checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" 139 | dependencies = [ 140 | "num-traits", 141 | ] 142 | 143 | [[package]] 144 | name = "num-integer" 145 | version = "0.1.46" 146 | source = "registry+https://github.com/rust-lang/crates.io-index" 147 | checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" 148 | dependencies = [ 149 | "num-traits", 150 | ] 151 | 152 | [[package]] 153 | name = "num-traits" 154 | version = "0.2.19" 155 | source = "registry+https://github.com/rust-lang/crates.io-index" 156 | checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 157 | dependencies = [ 158 | "autocfg", 159 | ] 160 | 161 | [[package]] 162 | name = "numpy" 163 | version = "0.23.0" 164 | source = "registry+https://github.com/rust-lang/crates.io-index" 165 | checksum = "b94caae805f998a07d33af06e6a3891e38556051b8045c615470a71590e13e78" 166 | dependencies = [ 167 | "libc", 168 | "ndarray", 169 | "num-complex", 170 | "num-integer", 171 | "num-traits", 172 | "pyo3", 173 | "rustc-hash", 174 | ] 175 | 176 | [[package]] 177 | name = "once_cell" 178 | version = "1.19.0" 179 | source = "registry+https://github.com/rust-lang/crates.io-index" 180 | checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" 181 | 182 | [[package]] 183 | name = "pkg-config" 184 | version = "0.3.30" 185 | source = "registry+https://github.com/rust-lang/crates.io-index" 186 | checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" 187 | 188 | [[package]] 189 | name = "portable-atomic" 190 | version = "1.6.0" 191 | source = "registry+https://github.com/rust-lang/crates.io-index" 192 | checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" 193 | 194 | [[package]] 195 | name = "proc-macro2" 196 | version = "1.0.84" 197 | source = "registry+https://github.com/rust-lang/crates.io-index" 198 | checksum = "ec96c6a92621310b51366f1e28d05ef11489516e93be030060e5fc12024a49d6" 199 | dependencies = [ 200 | "unicode-ident", 201 | ] 202 | 203 | [[package]] 204 | name = "pyo3" 205 | version = "0.23.4" 206 | source = "registry+https://github.com/rust-lang/crates.io-index" 207 | checksum = "57fe09249128b3173d092de9523eaa75136bf7ba85e0d69eca241c7939c933cc" 208 | dependencies = [ 209 | "cfg-if", 210 | "indoc", 211 | "libc", 212 | "memoffset", 213 | "once_cell", 214 | "portable-atomic", 215 | "pyo3-build-config", 216 | "pyo3-ffi", 217 | "pyo3-macros", 218 | "unindent", 219 | ] 220 | 221 | [[package]] 222 | name = "pyo3-build-config" 223 | version = "0.23.4" 224 | source = "registry+https://github.com/rust-lang/crates.io-index" 225 | checksum = "1cd3927b5a78757a0d71aa9dff669f903b1eb64b54142a9bd9f757f8fde65fd7" 226 | dependencies = [ 227 | "once_cell", 228 | "target-lexicon", 229 | ] 230 | 231 | [[package]] 232 | name = "pyo3-ffi" 233 | version = "0.23.4" 234 | source = "registry+https://github.com/rust-lang/crates.io-index" 235 | checksum = "dab6bb2102bd8f991e7749f130a70d05dd557613e39ed2deeee8e9ca0c4d548d" 236 | dependencies = [ 237 | "libc", 238 | "pyo3-build-config", 239 | ] 240 | 241 | [[package]] 242 | name = "pyo3-macros" 243 | version = "0.23.4" 244 | source = "registry+https://github.com/rust-lang/crates.io-index" 245 | checksum = "91871864b353fd5ffcb3f91f2f703a22a9797c91b9ab497b1acac7b07ae509c7" 246 | dependencies = [ 247 | "proc-macro2", 248 | "pyo3-macros-backend", 249 | "quote", 250 | "syn", 251 | ] 252 | 253 | [[package]] 254 | name = "pyo3-macros-backend" 255 | version = "0.23.4" 256 | source = "registry+https://github.com/rust-lang/crates.io-index" 257 | checksum = "43abc3b80bc20f3facd86cd3c60beed58c3e2aa26213f3cda368de39c60a27e4" 258 | dependencies = [ 259 | "heck", 260 | "proc-macro2", 261 | "pyo3-build-config", 262 | "quote", 263 | "syn", 264 | ] 265 | 266 | [[package]] 267 | name = "quote" 268 | version = "1.0.36" 269 | source = "registry+https://github.com/rust-lang/crates.io-index" 270 | checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" 271 | dependencies = [ 272 | "proc-macro2", 273 | ] 274 | 275 | [[package]] 276 | name = "rawpointer" 277 | version = "0.2.1" 278 | source = "registry+https://github.com/rust-lang/crates.io-index" 279 | checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" 280 | 281 | [[package]] 282 | name = "roxmltree" 283 | version = "0.20.0" 284 | source = "registry+https://github.com/rust-lang/crates.io-index" 285 | checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" 286 | 287 | [[package]] 288 | name = "rustc-hash" 289 | version = "2.1.0" 290 | source = "registry+https://github.com/rust-lang/crates.io-index" 291 | checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497" 292 | 293 | [[package]] 294 | name = "rustc_version" 295 | version = "0.4.0" 296 | source = "registry+https://github.com/rust-lang/crates.io-index" 297 | checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" 298 | dependencies = [ 299 | "semver", 300 | ] 301 | 302 | [[package]] 303 | name = "semver" 304 | version = "1.0.23" 305 | source = "registry+https://github.com/rust-lang/crates.io-index" 306 | checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" 307 | 308 | [[package]] 309 | name = "shlex" 310 | version = "1.3.0" 311 | source = "registry+https://github.com/rust-lang/crates.io-index" 312 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 313 | 314 | [[package]] 315 | name = "syn" 316 | version = "2.0.66" 317 | source = "registry+https://github.com/rust-lang/crates.io-index" 318 | checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" 319 | dependencies = [ 320 | "proc-macro2", 321 | "quote", 322 | "unicode-ident", 323 | ] 324 | 325 | [[package]] 326 | name = "target-lexicon" 327 | version = "0.12.14" 328 | source = "registry+https://github.com/rust-lang/crates.io-index" 329 | checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" 330 | 331 | [[package]] 332 | name = "unicode-ident" 333 | version = "1.0.12" 334 | source = "registry+https://github.com/rust-lang/crates.io-index" 335 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 336 | 337 | [[package]] 338 | name = "unindent" 339 | version = "0.2.3" 340 | source = "registry+https://github.com/rust-lang/crates.io-index" 341 | checksum = "c7de7d73e1754487cb58364ee906a499937a0dfabd86bcb980fa99ec8c8fa2ce" 342 | 343 | [[package]] 344 | name = "zstd" 345 | version = "0.13.2" 346 | source = "registry+https://github.com/rust-lang/crates.io-index" 347 | checksum = "fcf2b778a664581e31e389454a7072dab1647606d44f7feea22cd5abb9c9f3f9" 348 | dependencies = [ 349 | "zstd-safe", 350 | ] 351 | 352 | [[package]] 353 | name = "zstd-safe" 354 | version = "7.1.0" 355 | source = "registry+https://github.com/rust-lang/crates.io-index" 356 | checksum = "1cd99b45c6bc03a018c8b8a86025678c87e55526064e38f9df301989dce7ec0a" 357 | dependencies = [ 358 | "zstd-sys", 359 | ] 360 | 361 | [[package]] 362 | name = "zstd-sys" 363 | version = "2.0.10+zstd.1.5.6" 364 | source = "registry+https://github.com/rust-lang/crates.io-index" 365 | checksum = "c253a4914af5bafc8fa8c86ee400827e83cf6ec01195ec1f1ed8441bf00d65aa" 366 | dependencies = [ 367 | "cc", 368 | "pkg-config", 369 | ] 370 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "aedat" 3 | version = "2.2.0" 4 | edition = "2021" 5 | resolver = "2" 6 | 7 | [lib] 8 | name = "aedat" 9 | crate-type = ["cdylib"] 10 | 11 | [dependencies] 12 | flatbuffers = "24.12.23" 13 | lz4 = "1.28.0" 14 | numpy = "0.23.0" 15 | pyo3 = {version = "0.23.4", features = ["extension-module"]} 16 | roxmltree = "0.20.0" 17 | zstd = "0.13.2" 18 | 19 | [profile.release] 20 | lto = true 21 | codegen-units = 1 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 International Centre for Neuromorphic Systems 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | AEDAT is a fast AEDAT 4 python reader, with a Rust underlying implementation. 2 | 3 | Run `pip install aedat` to install it. 4 | 5 | - [Example](#example) 6 | - [Process frames](#process-frames) 7 | - [Pillow (PIL)](#pillow-pil) 8 | - [OpenCV](#opencv) 9 | - [Detailed example](#detailed-example) 10 | - [Install from source](#install-from-source) 11 | - [Linux](#linux) 12 | - [macOS](#macos) 13 | - [Windows](#windows) 14 | - [Contribute](#contribute) 15 | - [Publish](#publish) 16 | 17 | ## Example 18 | 19 | The `aedat` library provides a single class: `Decoder`. A decoder object is created by passing a file name to `Decoder`. The file name must be a [path-like object](https://docs.python.org/3/glossary.html#term-path-like-object). 20 | 21 | ```python 22 | import aedat 23 | 24 | decoder = aedat.Decoder("/path/to/file.aedat") 25 | print(decoder.id_to_stream()) 26 | 27 | for packet in decoder: 28 | print(packet["stream_id"], end=": ") 29 | if "events" in packet: 30 | print("{} polarity events".format(len(packet["events"]))) 31 | elif "frame" in packet: 32 | print("{} x {} frame".format(packet["frame"]["width"], packet["frame"]["height"])) 33 | elif "imus" in packet: 34 | print("{} IMU samples".format(len(packet["imus"]))) 35 | elif "triggers" in packet: 36 | print("{} trigger events".format(len(packet["triggers"]))) 37 | ``` 38 | 39 | ## Process frames 40 | 41 | ### Pillow (PIL) 42 | 43 | ```py 44 | import aedat 45 | import PIL.Image # https://pypi.org/project/Pillow/ 46 | 47 | index = 0 48 | for packet in decoder: 49 | if "frame" in packet: 50 | image = PIL.Image.fromarray( 51 | packet["frame"]["pixels"], 52 | mode=packet["frame"]["format"], 53 | ) 54 | image.save(f"{index}.png") 55 | index += 1 56 | ``` 57 | 58 | ### OpenCV 59 | 60 | ```py 61 | import aedat 62 | import cv2 # https://pypi.org/project/opencv-python/ 63 | 64 | index = 0 65 | for packet in decoder: 66 | if "frame" in packet: 67 | image = packet["frame"]["pixels"] 68 | if packet["frame"]["format"] == "RGB": 69 | image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR) 70 | elif packet["frame"]["format"] == "RGBA": 71 | image = cv2.cvtColor(image, cv2.COLOR_RGBA2BGRA) 72 | cv2.imwrite(f"{index}.png", image) 73 | index += 1 74 | ``` 75 | 76 | ## Detailed example 77 | 78 | This is the same as the first example, with detailed comments: 79 | 80 | ```python 81 | import aedat 82 | 83 | decoder = aedat.Decoder("/path/to/file.aedat") 84 | """ 85 | decoder is a packet iterator with an additional method id_to_stream 86 | id_to_stream returns a dictionary with the following structure: 87 | { 88 | : { 89 | "type": , 90 | } 91 | } 92 | type is one of "events", "frame", "imus", "triggers" 93 | if type is "events" or "frame", its parent dictionary has the following structure: 94 | { 95 | "type": , 96 | "width": , 97 | "height": , 98 | } 99 | """ 100 | print(decoder.id_to_stream()) 101 | 102 | for packet in decoder: 103 | """ 104 | packet is a dictionary with the following structure: 105 | { 106 | "stream_id": , 107 | } 108 | packet also has exactly one of the following fields: 109 | "events", "frame", "imus", "triggers" 110 | """ 111 | print(packet["stream_id"], end=": ") 112 | if "events" in packet: 113 | """ 114 | packet["events"] is a structured numpy array with the following dtype: 115 | [ 116 | ("t", ", 128 | "begin_t": , 129 | "end_t": , 130 | "exposure_begin_t": , 131 | "exposure_end_t": , 132 | "format": , 133 | "width": , 134 | "height": , 135 | "offset_x": , 136 | "offset_y": , 137 | "pixels": , 138 | } 139 | format is one of "L", "RGB", "RGBA" 140 | """ 141 | print("{} x {} frame".format(packet["frame"]["width"], packet["frame"]["height"])) 142 | elif "imus" in packet: 143 | """ 144 | packet["imus"] is a structured numpy array with the following dtype: 145 | [ 146 | ("t", "=1.26"] 12 | version = "2.2.0" 13 | 14 | [project.urls] 15 | homepage = "https://github.com/neuromorphicsystems/aedat/" 16 | repository = "https://github.com/neuromorphicsystems/aedat/" 17 | documentation = "https://github.com/neuromorphicsystems/aedat/" 18 | 19 | [build-system] 20 | requires = ["maturin==1.7.4", "numpy>=1.26"] 21 | build-backend = "maturin" 22 | -------------------------------------------------------------------------------- /src/aedat_core.rs: -------------------------------------------------------------------------------- 1 | use std::io::Read; 2 | 3 | #[allow( 4 | dead_code, 5 | unused_imports, 6 | clippy::derivable_impls, 7 | clippy::derive_partial_eq_without_eq, 8 | clippy::extra_unused_lifetimes, 9 | clippy::size_of_in_element_count 10 | )] 11 | #[path = "./ioheader_generated.rs"] 12 | pub(crate) mod ioheader_generated; 13 | 14 | #[allow( 15 | dead_code, 16 | unused_imports, 17 | clippy::derivable_impls, 18 | clippy::derive_partial_eq_without_eq, 19 | clippy::extra_unused_lifetimes, 20 | clippy::size_of_in_element_count 21 | )] 22 | #[path = "./events_generated.rs"] 23 | pub(crate) mod events_generated; 24 | 25 | #[allow( 26 | dead_code, 27 | unused_imports, 28 | clippy::derivable_impls, 29 | clippy::derive_partial_eq_without_eq, 30 | clippy::extra_unused_lifetimes, 31 | clippy::size_of_in_element_count 32 | )] 33 | #[path = "./frame_generated.rs"] 34 | pub(crate) mod frame_generated; 35 | 36 | #[allow( 37 | dead_code, 38 | unused_imports, 39 | clippy::derivable_impls, 40 | clippy::derive_partial_eq_without_eq, 41 | clippy::extra_unused_lifetimes, 42 | clippy::size_of_in_element_count 43 | )] 44 | #[path = "./imus_generated.rs"] 45 | pub(crate) mod imus_generated; 46 | 47 | #[allow( 48 | dead_code, 49 | unused_imports, 50 | clippy::derivable_impls, 51 | clippy::derive_partial_eq_without_eq, 52 | clippy::extra_unused_lifetimes, 53 | clippy::size_of_in_element_count 54 | )] 55 | #[path = "./triggers_generated.rs"] 56 | pub(crate) mod triggers_generated; 57 | 58 | const MAGIC_NUMBER: &str = "#!AER-DAT4.0\r\n"; 59 | 60 | #[derive(Debug)] 61 | pub struct ParseError { 62 | message: String, 63 | } 64 | 65 | impl ParseError { 66 | pub fn new(message: &str) -> ParseError { 67 | ParseError { 68 | message: message.to_string(), 69 | } 70 | } 71 | } 72 | 73 | impl std::fmt::Display for ParseError { 74 | fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { 75 | write!(formatter, "{}", self.message) 76 | } 77 | } 78 | 79 | impl std::convert::From for ParseError { 80 | fn from(error: std::io::Error) -> Self { 81 | ParseError { 82 | message: error.to_string(), 83 | } 84 | } 85 | } 86 | 87 | impl std::convert::From for ParseError { 88 | fn from(error: flatbuffers::InvalidFlatbuffer) -> Self { 89 | ParseError { 90 | message: error.to_string(), 91 | } 92 | } 93 | } 94 | 95 | impl std::convert::From for ParseError { 96 | fn from(error: std::str::Utf8Error) -> Self { 97 | ParseError { 98 | message: error.to_string(), 99 | } 100 | } 101 | } 102 | 103 | impl std::convert::From for ParseError { 104 | fn from(error: roxmltree::Error) -> Self { 105 | ParseError { 106 | message: error.to_string(), 107 | } 108 | } 109 | } 110 | 111 | impl std::convert::From for ParseError { 112 | fn from(error: std::num::ParseIntError) -> Self { 113 | ParseError { 114 | message: error.to_string(), 115 | } 116 | } 117 | } 118 | 119 | pub enum StreamContent { 120 | Events, 121 | Frame, 122 | Imus, 123 | Triggers, 124 | } 125 | 126 | impl StreamContent { 127 | fn from(identifier: &str) -> Result { 128 | match identifier { 129 | "EVTS" => Ok(StreamContent::Events), 130 | "FRME" => Ok(StreamContent::Frame), 131 | "IMUS" => Ok(StreamContent::Imus), 132 | "TRIG" => Ok(StreamContent::Triggers), 133 | _ => Err(ParseError::new("unsupported stream type")), 134 | } 135 | } 136 | } 137 | 138 | impl std::fmt::Display for StreamContent { 139 | fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { 140 | write!( 141 | formatter, 142 | "{}", 143 | match self { 144 | StreamContent::Events => "EVTS", 145 | StreamContent::Frame => "FRME", 146 | StreamContent::Imus => "IMUS", 147 | StreamContent::Triggers => "TRIG", 148 | } 149 | ) 150 | } 151 | } 152 | 153 | pub struct Stream { 154 | pub content: StreamContent, 155 | pub width: u16, 156 | pub height: u16, 157 | } 158 | 159 | pub struct Decoder { 160 | pub id_to_stream: std::collections::HashMap, 161 | file: std::fs::File, 162 | position: i64, 163 | compression: ioheader_generated::Compression, 164 | file_data_position: i64, 165 | } 166 | 167 | impl Decoder { 168 | pub fn new>(path: P) -> Result { 169 | let mut decoder = Decoder { 170 | id_to_stream: std::collections::HashMap::new(), 171 | file: std::fs::File::open(path)?, 172 | position: 0i64, 173 | file_data_position: 0, 174 | compression: ioheader_generated::Compression::None, 175 | }; 176 | { 177 | let mut magic_number_buffer = [0; MAGIC_NUMBER.len()]; 178 | decoder.file.read_exact(&mut magic_number_buffer)?; 179 | if std::str::from_utf8(&magic_number_buffer)? != MAGIC_NUMBER { 180 | return Err(ParseError::new( 181 | "the file does not contain AEDAT4 data (wrong magic number)", 182 | )); 183 | } 184 | decoder.position += MAGIC_NUMBER.len() as i64; 185 | } 186 | let length = { 187 | let mut bytes = [0; 4]; 188 | decoder.file.read_exact(&mut bytes)?; 189 | u32::from_le_bytes(bytes) 190 | }; 191 | decoder.position += 4i64 + length as i64; 192 | { 193 | let mut buffer = std::vec![0; length as usize]; 194 | decoder.file.read_exact(&mut buffer)?; 195 | let ioheader = unsafe { ioheader_generated::root_as_ioheader_unchecked(&buffer) }; 196 | decoder.compression = ioheader.compression(); 197 | decoder.file_data_position = ioheader.file_data_position(); 198 | let description = match ioheader.description() { 199 | Some(content) => content, 200 | None => return Err(ParseError::new("the description is empty")), 201 | }; 202 | let document = roxmltree::Document::parse(description)?; 203 | let dv_node = match document.root().first_child() { 204 | Some(content) => content, 205 | None => return Err(ParseError::new("the description has no dv node")), 206 | }; 207 | if !dv_node.has_tag_name("dv") { 208 | return Err(ParseError::new("unexpected dv node tag")); 209 | } 210 | let output_node = match dv_node.children().find(|node| { 211 | node.is_element() 212 | && node.has_tag_name("node") 213 | && node.attribute("name") == Some("outInfo") 214 | }) { 215 | Some(content) => content, 216 | None => return Err(ParseError::new("the description has no output node")), 217 | }; 218 | for stream_node in output_node.children() { 219 | if stream_node.is_element() && stream_node.has_tag_name("node") { 220 | if !stream_node.has_tag_name("node") { 221 | return Err(ParseError::new("unexpected stream node tag")); 222 | } 223 | let stream_id = match stream_node.attribute("name") { 224 | Some(content) => content, 225 | None => return Err(ParseError::new("missing stream node id")), 226 | } 227 | .parse::()?; 228 | let identifier = match stream_node.children().find(|node| { 229 | node.is_element() 230 | && node.has_tag_name("attr") 231 | && node.attribute("key") == Some("typeIdentifier") 232 | }) { 233 | Some(content) => match content.text() { 234 | Some(content) => content, 235 | None => { 236 | return Err(ParseError::new("empty stream node type identifier")) 237 | } 238 | }, 239 | None => return Err(ParseError::new("missing stream node type identifier")), 240 | } 241 | .to_string(); 242 | let mut width = 0u16; 243 | let mut height = 0u16; 244 | if identifier == "EVTS" || identifier == "FRME" { 245 | let info_node = match stream_node.children().find(|node| { 246 | node.is_element() 247 | && node.has_tag_name("node") 248 | && node.attribute("name") == Some("info") 249 | }) { 250 | Some(content) => content, 251 | None => return Err(ParseError::new("missing info node")), 252 | }; 253 | width = match info_node.children().find(|node| { 254 | node.is_element() 255 | && node.has_tag_name("attr") 256 | && node.attribute("key") == Some("sizeX") 257 | }) { 258 | Some(content) => match content.text() { 259 | Some(content) => content, 260 | None => return Err(ParseError::new("empty sizeX attribute")), 261 | }, 262 | None => return Err(ParseError::new("missing sizeX attribute")), 263 | } 264 | .parse::()?; 265 | height = match info_node.children().find(|node| { 266 | node.is_element() 267 | && node.has_tag_name("attr") 268 | && node.attribute("key") == Some("sizeY") 269 | }) { 270 | Some(content) => match content.text() { 271 | Some(content) => content, 272 | None => return Err(ParseError::new("empty sizeX attribute")), 273 | }, 274 | None => return Err(ParseError::new("missing sizeX attribute")), 275 | } 276 | .parse::()?; 277 | } 278 | if decoder 279 | .id_to_stream 280 | .insert( 281 | stream_id, 282 | Stream { 283 | content: StreamContent::from(&identifier)?, 284 | width, 285 | height, 286 | }, 287 | ) 288 | .is_some() 289 | { 290 | return Err(ParseError::new("duplicated stream id")); 291 | } 292 | } 293 | } 294 | } 295 | if decoder.id_to_stream.is_empty() { 296 | return Err(ParseError::new("no stream found in the description")); 297 | } 298 | Ok(decoder) 299 | } 300 | } 301 | 302 | pub struct Packet { 303 | pub buffer: std::vec::Vec, 304 | pub stream_id: u32, 305 | } 306 | 307 | impl Iterator for Decoder { 308 | type Item = Result; 309 | 310 | fn next(&mut self) -> Option { 311 | if self.file_data_position > -1 && self.position == self.file_data_position { 312 | return None; 313 | } 314 | let mut packet = Packet { 315 | buffer: Vec::new(), 316 | stream_id: { 317 | let mut bytes = [0; 4]; 318 | match self.file.read_exact(&mut bytes) { 319 | Ok(()) => (), 320 | Err(_) => return None, 321 | } 322 | u32::from_le_bytes(bytes) 323 | }, 324 | }; 325 | let length = { 326 | let mut bytes = [0; 4]; 327 | if let Err(error) = self.file.read_exact(&mut bytes) { 328 | return Some(Err(ParseError::from(error))); 329 | } 330 | u32::from_le_bytes(bytes) 331 | }; 332 | self.position += 8i64 + length as i64; 333 | let mut raw_buffer = std::vec![0; length as usize]; 334 | if let Err(error) = self.file.read_exact(&mut raw_buffer) { 335 | return Some(Err(ParseError::from(error))); 336 | } 337 | match self.compression { 338 | ioheader_generated::Compression::None => { 339 | std::mem::swap(&mut raw_buffer, &mut packet.buffer) 340 | } 341 | ioheader_generated::Compression::Lz4 | ioheader_generated::Compression::Lz4High => { 342 | match lz4::Decoder::new(&raw_buffer[..]) { 343 | Ok(mut result) => { 344 | if let Err(error) = result.read_to_end(&mut packet.buffer) { 345 | return Some(Err(ParseError::from(error))); 346 | } 347 | } 348 | Err(error) => return Some(Err(ParseError::from(error))), 349 | } 350 | } 351 | ioheader_generated::Compression::Zstd | ioheader_generated::Compression::ZstdHigh => { 352 | match zstd::stream::Decoder::new(&raw_buffer[..]) { 353 | Ok(mut result) => { 354 | if let Err(error) = result.read_to_end(&mut packet.buffer) { 355 | return Some(Err(ParseError::from(error))); 356 | } 357 | } 358 | Err(error) => return Some(Err(ParseError::from(error))), 359 | } 360 | } 361 | _ => return Some(Err(ParseError::new("unknown compression algorithm"))), 362 | } 363 | let expected_content = &(match self.id_to_stream.get(&packet.stream_id) { 364 | Some(content) => content, 365 | None => return Some(Err(ParseError::new("unknown stream id"))), 366 | } 367 | .content); 368 | if !flatbuffers::buffer_has_identifier(&packet.buffer, &expected_content.to_string(), true) 369 | { 370 | return Some(Err(ParseError::new( 371 | "the stream id and the identifier do not match", 372 | ))); 373 | } 374 | Some(Ok(packet)) 375 | } 376 | } 377 | -------------------------------------------------------------------------------- /src/events_generated.rs: -------------------------------------------------------------------------------- 1 | // automatically generated by the FlatBuffers compiler, do not modify 2 | 3 | 4 | // @generated 5 | 6 | use core::mem; 7 | use core::cmp::Ordering; 8 | 9 | extern crate flatbuffers; 10 | use self::flatbuffers::{EndianScalar, Follow}; 11 | 12 | // struct Event, aligned to 8 13 | #[repr(transparent)] 14 | #[derive(Clone, Copy, PartialEq)] 15 | pub struct Event(pub [u8; 16]); 16 | impl Default for Event { 17 | fn default() -> Self { 18 | Self([0; 16]) 19 | } 20 | } 21 | impl core::fmt::Debug for Event { 22 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { 23 | f.debug_struct("Event") 24 | .field("t", &self.t()) 25 | .field("x", &self.x()) 26 | .field("y", &self.y()) 27 | .field("on", &self.on()) 28 | .finish() 29 | } 30 | } 31 | 32 | impl flatbuffers::SimpleToVerifyInSlice for Event {} 33 | impl<'a> flatbuffers::Follow<'a> for Event { 34 | type Inner = &'a Event; 35 | #[inline] 36 | unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { 37 | <&'a Event>::follow(buf, loc) 38 | } 39 | } 40 | impl<'a> flatbuffers::Follow<'a> for &'a Event { 41 | type Inner = &'a Event; 42 | #[inline] 43 | unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { 44 | flatbuffers::follow_cast_ref::(buf, loc) 45 | } 46 | } 47 | impl<'b> flatbuffers::Push for Event { 48 | type Output = Event; 49 | #[inline] 50 | unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { 51 | let src = ::core::slice::from_raw_parts(self as *const Event as *const u8, ::size()); 52 | dst.copy_from_slice(src); 53 | } 54 | #[inline] 55 | fn alignment() -> flatbuffers::PushAlignment { 56 | flatbuffers::PushAlignment::new(8) 57 | } 58 | } 59 | 60 | impl<'a> flatbuffers::Verifiable for Event { 61 | #[inline] 62 | fn run_verifier( 63 | v: &mut flatbuffers::Verifier, pos: usize 64 | ) -> Result<(), flatbuffers::InvalidFlatbuffer> { 65 | use self::flatbuffers::Verifiable; 66 | v.in_buffer::(pos) 67 | } 68 | } 69 | 70 | impl<'a> Event { 71 | #[allow(clippy::too_many_arguments)] 72 | pub fn new( 73 | t: i64, 74 | x: i16, 75 | y: i16, 76 | on: bool, 77 | ) -> Self { 78 | let mut s = Self([0; 16]); 79 | s.set_t(t); 80 | s.set_x(x); 81 | s.set_y(y); 82 | s.set_on(on); 83 | s 84 | } 85 | 86 | pub fn t(&self) -> i64 { 87 | let mut mem = core::mem::MaybeUninit::<::Scalar>::uninit(); 88 | // Safety: 89 | // Created from a valid Table for this object 90 | // Which contains a valid value in this slot 91 | EndianScalar::from_little_endian(unsafe { 92 | core::ptr::copy_nonoverlapping( 93 | self.0[0..].as_ptr(), 94 | mem.as_mut_ptr() as *mut u8, 95 | core::mem::size_of::<::Scalar>(), 96 | ); 97 | mem.assume_init() 98 | }) 99 | } 100 | 101 | pub fn set_t(&mut self, x: i64) { 102 | let x_le = x.to_little_endian(); 103 | // Safety: 104 | // Created from a valid Table for this object 105 | // Which contains a valid value in this slot 106 | unsafe { 107 | core::ptr::copy_nonoverlapping( 108 | &x_le as *const _ as *const u8, 109 | self.0[0..].as_mut_ptr(), 110 | core::mem::size_of::<::Scalar>(), 111 | ); 112 | } 113 | } 114 | 115 | pub fn x(&self) -> i16 { 116 | let mut mem = core::mem::MaybeUninit::<::Scalar>::uninit(); 117 | // Safety: 118 | // Created from a valid Table for this object 119 | // Which contains a valid value in this slot 120 | EndianScalar::from_little_endian(unsafe { 121 | core::ptr::copy_nonoverlapping( 122 | self.0[8..].as_ptr(), 123 | mem.as_mut_ptr() as *mut u8, 124 | core::mem::size_of::<::Scalar>(), 125 | ); 126 | mem.assume_init() 127 | }) 128 | } 129 | 130 | pub fn set_x(&mut self, x: i16) { 131 | let x_le = x.to_little_endian(); 132 | // Safety: 133 | // Created from a valid Table for this object 134 | // Which contains a valid value in this slot 135 | unsafe { 136 | core::ptr::copy_nonoverlapping( 137 | &x_le as *const _ as *const u8, 138 | self.0[8..].as_mut_ptr(), 139 | core::mem::size_of::<::Scalar>(), 140 | ); 141 | } 142 | } 143 | 144 | pub fn y(&self) -> i16 { 145 | let mut mem = core::mem::MaybeUninit::<::Scalar>::uninit(); 146 | // Safety: 147 | // Created from a valid Table for this object 148 | // Which contains a valid value in this slot 149 | EndianScalar::from_little_endian(unsafe { 150 | core::ptr::copy_nonoverlapping( 151 | self.0[10..].as_ptr(), 152 | mem.as_mut_ptr() as *mut u8, 153 | core::mem::size_of::<::Scalar>(), 154 | ); 155 | mem.assume_init() 156 | }) 157 | } 158 | 159 | pub fn set_y(&mut self, x: i16) { 160 | let x_le = x.to_little_endian(); 161 | // Safety: 162 | // Created from a valid Table for this object 163 | // Which contains a valid value in this slot 164 | unsafe { 165 | core::ptr::copy_nonoverlapping( 166 | &x_le as *const _ as *const u8, 167 | self.0[10..].as_mut_ptr(), 168 | core::mem::size_of::<::Scalar>(), 169 | ); 170 | } 171 | } 172 | 173 | pub fn on(&self) -> bool { 174 | let mut mem = core::mem::MaybeUninit::<::Scalar>::uninit(); 175 | // Safety: 176 | // Created from a valid Table for this object 177 | // Which contains a valid value in this slot 178 | EndianScalar::from_little_endian(unsafe { 179 | core::ptr::copy_nonoverlapping( 180 | self.0[12..].as_ptr(), 181 | mem.as_mut_ptr() as *mut u8, 182 | core::mem::size_of::<::Scalar>(), 183 | ); 184 | mem.assume_init() 185 | }) 186 | } 187 | 188 | pub fn set_on(&mut self, x: bool) { 189 | let x_le = x.to_little_endian(); 190 | // Safety: 191 | // Created from a valid Table for this object 192 | // Which contains a valid value in this slot 193 | unsafe { 194 | core::ptr::copy_nonoverlapping( 195 | &x_le as *const _ as *const u8, 196 | self.0[12..].as_mut_ptr(), 197 | core::mem::size_of::<::Scalar>(), 198 | ); 199 | } 200 | } 201 | 202 | } 203 | 204 | pub enum EventPacketOffset {} 205 | #[derive(Copy, Clone, PartialEq)] 206 | 207 | pub struct EventPacket<'a> { 208 | pub _tab: flatbuffers::Table<'a>, 209 | } 210 | 211 | impl<'a> flatbuffers::Follow<'a> for EventPacket<'a> { 212 | type Inner = EventPacket<'a>; 213 | #[inline] 214 | unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { 215 | Self { _tab: flatbuffers::Table::new(buf, loc) } 216 | } 217 | } 218 | 219 | impl<'a> EventPacket<'a> { 220 | pub const VT_ELEMENTS: flatbuffers::VOffsetT = 4; 221 | 222 | #[inline] 223 | pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { 224 | EventPacket { _tab: table } 225 | } 226 | #[allow(unused_mut)] 227 | pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: flatbuffers::Allocator + 'bldr>( 228 | _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr, A>, 229 | args: &'args EventPacketArgs<'args> 230 | ) -> flatbuffers::WIPOffset> { 231 | let mut builder = EventPacketBuilder::new(_fbb); 232 | if let Some(x) = args.elements { builder.add_elements(x); } 233 | builder.finish() 234 | } 235 | 236 | 237 | #[inline] 238 | pub fn elements(&self) -> Option> { 239 | // Safety: 240 | // Created from valid Table for this object 241 | // which contains a valid value in this slot 242 | unsafe { self._tab.get::>>(EventPacket::VT_ELEMENTS, None)} 243 | } 244 | } 245 | 246 | impl flatbuffers::Verifiable for EventPacket<'_> { 247 | #[inline] 248 | fn run_verifier( 249 | v: &mut flatbuffers::Verifier, pos: usize 250 | ) -> Result<(), flatbuffers::InvalidFlatbuffer> { 251 | use self::flatbuffers::Verifiable; 252 | v.visit_table(pos)? 253 | .visit_field::>>("elements", Self::VT_ELEMENTS, false)? 254 | .finish(); 255 | Ok(()) 256 | } 257 | } 258 | pub struct EventPacketArgs<'a> { 259 | pub elements: Option>>, 260 | } 261 | impl<'a> Default for EventPacketArgs<'a> { 262 | #[inline] 263 | fn default() -> Self { 264 | EventPacketArgs { 265 | elements: None, 266 | } 267 | } 268 | } 269 | 270 | pub struct EventPacketBuilder<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> { 271 | fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, 272 | start_: flatbuffers::WIPOffset, 273 | } 274 | impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> EventPacketBuilder<'a, 'b, A> { 275 | #[inline] 276 | pub fn add_elements(&mut self, elements: flatbuffers::WIPOffset>) { 277 | self.fbb_.push_slot_always::>(EventPacket::VT_ELEMENTS, elements); 278 | } 279 | #[inline] 280 | pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>) -> EventPacketBuilder<'a, 'b, A> { 281 | let start = _fbb.start_table(); 282 | EventPacketBuilder { 283 | fbb_: _fbb, 284 | start_: start, 285 | } 286 | } 287 | #[inline] 288 | pub fn finish(self) -> flatbuffers::WIPOffset> { 289 | let o = self.fbb_.end_table(self.start_); 290 | flatbuffers::WIPOffset::new(o.value()) 291 | } 292 | } 293 | 294 | impl core::fmt::Debug for EventPacket<'_> { 295 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 296 | let mut ds = f.debug_struct("EventPacket"); 297 | ds.field("elements", &self.elements()); 298 | ds.finish() 299 | } 300 | } 301 | #[inline] 302 | /// Verifies that a buffer of bytes contains a `EventPacket` 303 | /// and returns it. 304 | /// Note that verification is still experimental and may not 305 | /// catch every error, or be maximally performant. For the 306 | /// previous, unchecked, behavior use 307 | /// `root_as_event_packet_unchecked`. 308 | pub fn root_as_event_packet(buf: &[u8]) -> Result { 309 | flatbuffers::root::(buf) 310 | } 311 | #[inline] 312 | /// Verifies that a buffer of bytes contains a size prefixed 313 | /// `EventPacket` and returns it. 314 | /// Note that verification is still experimental and may not 315 | /// catch every error, or be maximally performant. For the 316 | /// previous, unchecked, behavior use 317 | /// `size_prefixed_root_as_event_packet_unchecked`. 318 | pub fn size_prefixed_root_as_event_packet(buf: &[u8]) -> Result { 319 | flatbuffers::size_prefixed_root::(buf) 320 | } 321 | #[inline] 322 | /// Verifies, with the given options, that a buffer of bytes 323 | /// contains a `EventPacket` and returns it. 324 | /// Note that verification is still experimental and may not 325 | /// catch every error, or be maximally performant. For the 326 | /// previous, unchecked, behavior use 327 | /// `root_as_event_packet_unchecked`. 328 | pub fn root_as_event_packet_with_opts<'b, 'o>( 329 | opts: &'o flatbuffers::VerifierOptions, 330 | buf: &'b [u8], 331 | ) -> Result, flatbuffers::InvalidFlatbuffer> { 332 | flatbuffers::root_with_opts::>(opts, buf) 333 | } 334 | #[inline] 335 | /// Verifies, with the given verifier options, that a buffer of 336 | /// bytes contains a size prefixed `EventPacket` and returns 337 | /// it. Note that verification is still experimental and may not 338 | /// catch every error, or be maximally performant. For the 339 | /// previous, unchecked, behavior use 340 | /// `root_as_event_packet_unchecked`. 341 | pub fn size_prefixed_root_as_event_packet_with_opts<'b, 'o>( 342 | opts: &'o flatbuffers::VerifierOptions, 343 | buf: &'b [u8], 344 | ) -> Result, flatbuffers::InvalidFlatbuffer> { 345 | flatbuffers::size_prefixed_root_with_opts::>(opts, buf) 346 | } 347 | #[inline] 348 | /// Assumes, without verification, that a buffer of bytes contains a EventPacket and returns it. 349 | /// # Safety 350 | /// Callers must trust the given bytes do indeed contain a valid `EventPacket`. 351 | pub unsafe fn root_as_event_packet_unchecked(buf: &[u8]) -> EventPacket { 352 | flatbuffers::root_unchecked::(buf) 353 | } 354 | #[inline] 355 | /// Assumes, without verification, that a buffer of bytes contains a size prefixed EventPacket and returns it. 356 | /// # Safety 357 | /// Callers must trust the given bytes do indeed contain a valid size prefixed `EventPacket`. 358 | pub unsafe fn size_prefixed_root_as_event_packet_unchecked(buf: &[u8]) -> EventPacket { 359 | flatbuffers::size_prefixed_root_unchecked::(buf) 360 | } 361 | pub const EVENT_PACKET_IDENTIFIER: &str = "EVTS"; 362 | 363 | #[inline] 364 | pub fn event_packet_buffer_has_identifier(buf: &[u8]) -> bool { 365 | flatbuffers::buffer_has_identifier(buf, EVENT_PACKET_IDENTIFIER, false) 366 | } 367 | 368 | #[inline] 369 | pub fn event_packet_size_prefixed_buffer_has_identifier(buf: &[u8]) -> bool { 370 | flatbuffers::buffer_has_identifier(buf, EVENT_PACKET_IDENTIFIER, true) 371 | } 372 | 373 | #[inline] 374 | pub fn finish_event_packet_buffer<'a, 'b, A: flatbuffers::Allocator + 'a>( 375 | fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, 376 | root: flatbuffers::WIPOffset>) { 377 | fbb.finish(root, Some(EVENT_PACKET_IDENTIFIER)); 378 | } 379 | 380 | #[inline] 381 | pub fn finish_size_prefixed_event_packet_buffer<'a, 'b, A: flatbuffers::Allocator + 'a>(fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, root: flatbuffers::WIPOffset>) { 382 | fbb.finish_size_prefixed(root, Some(EVENT_PACKET_IDENTIFIER)); 383 | } 384 | -------------------------------------------------------------------------------- /src/frame_generated.rs: -------------------------------------------------------------------------------- 1 | // automatically generated by the FlatBuffers compiler, do not modify 2 | 3 | 4 | // @generated 5 | 6 | use core::mem; 7 | use core::cmp::Ordering; 8 | 9 | extern crate flatbuffers; 10 | use self::flatbuffers::{EndianScalar, Follow}; 11 | 12 | #[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] 13 | pub const ENUM_MIN_FRAME_FORMAT: i8 = 0; 14 | #[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] 15 | pub const ENUM_MAX_FRAME_FORMAT: i8 = 24; 16 | #[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] 17 | #[allow(non_camel_case_types)] 18 | pub const ENUM_VALUES_FRAME_FORMAT: [FrameFormat; 3] = [ 19 | FrameFormat::Gray, 20 | FrameFormat::Bgr, 21 | FrameFormat::Bgra, 22 | ]; 23 | 24 | #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] 25 | #[repr(transparent)] 26 | pub struct FrameFormat(pub i8); 27 | #[allow(non_upper_case_globals)] 28 | impl FrameFormat { 29 | pub const Gray: Self = Self(0); 30 | pub const Bgr: Self = Self(16); 31 | pub const Bgra: Self = Self(24); 32 | 33 | pub const ENUM_MIN: i8 = 0; 34 | pub const ENUM_MAX: i8 = 24; 35 | pub const ENUM_VALUES: &'static [Self] = &[ 36 | Self::Gray, 37 | Self::Bgr, 38 | Self::Bgra, 39 | ]; 40 | /// Returns the variant's name or "" if unknown. 41 | pub fn variant_name(self) -> Option<&'static str> { 42 | match self { 43 | Self::Gray => Some("Gray"), 44 | Self::Bgr => Some("Bgr"), 45 | Self::Bgra => Some("Bgra"), 46 | _ => None, 47 | } 48 | } 49 | } 50 | impl core::fmt::Debug for FrameFormat { 51 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { 52 | if let Some(name) = self.variant_name() { 53 | f.write_str(name) 54 | } else { 55 | f.write_fmt(format_args!("", self.0)) 56 | } 57 | } 58 | } 59 | impl<'a> flatbuffers::Follow<'a> for FrameFormat { 60 | type Inner = Self; 61 | #[inline] 62 | unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { 63 | let b = flatbuffers::read_scalar_at::(buf, loc); 64 | Self(b) 65 | } 66 | } 67 | 68 | impl flatbuffers::Push for FrameFormat { 69 | type Output = FrameFormat; 70 | #[inline] 71 | unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { 72 | flatbuffers::emplace_scalar::(dst, self.0); 73 | } 74 | } 75 | 76 | impl flatbuffers::EndianScalar for FrameFormat { 77 | type Scalar = i8; 78 | #[inline] 79 | fn to_little_endian(self) -> i8 { 80 | self.0.to_le() 81 | } 82 | #[inline] 83 | #[allow(clippy::wrong_self_convention)] 84 | fn from_little_endian(v: i8) -> Self { 85 | let b = i8::from_le(v); 86 | Self(b) 87 | } 88 | } 89 | 90 | impl<'a> flatbuffers::Verifiable for FrameFormat { 91 | #[inline] 92 | fn run_verifier( 93 | v: &mut flatbuffers::Verifier, pos: usize 94 | ) -> Result<(), flatbuffers::InvalidFlatbuffer> { 95 | use self::flatbuffers::Verifiable; 96 | i8::run_verifier(v, pos) 97 | } 98 | } 99 | 100 | impl flatbuffers::SimpleToVerifyInSlice for FrameFormat {} 101 | pub enum FrameOffset {} 102 | #[derive(Copy, Clone, PartialEq)] 103 | 104 | pub struct Frame<'a> { 105 | pub _tab: flatbuffers::Table<'a>, 106 | } 107 | 108 | impl<'a> flatbuffers::Follow<'a> for Frame<'a> { 109 | type Inner = Frame<'a>; 110 | #[inline] 111 | unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { 112 | Self { _tab: flatbuffers::Table::new(buf, loc) } 113 | } 114 | } 115 | 116 | impl<'a> Frame<'a> { 117 | pub const VT_T: flatbuffers::VOffsetT = 4; 118 | pub const VT_BEGIN_T: flatbuffers::VOffsetT = 6; 119 | pub const VT_END_T: flatbuffers::VOffsetT = 8; 120 | pub const VT_EXPOSURE_BEGIN_T: flatbuffers::VOffsetT = 10; 121 | pub const VT_EXPOSURE_END_T: flatbuffers::VOffsetT = 12; 122 | pub const VT_FORMAT: flatbuffers::VOffsetT = 14; 123 | pub const VT_WIDTH: flatbuffers::VOffsetT = 16; 124 | pub const VT_HEIGHT: flatbuffers::VOffsetT = 18; 125 | pub const VT_OFFSET_X: flatbuffers::VOffsetT = 20; 126 | pub const VT_OFFSET_Y: flatbuffers::VOffsetT = 22; 127 | pub const VT_PIXELS: flatbuffers::VOffsetT = 24; 128 | 129 | #[inline] 130 | pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { 131 | Frame { _tab: table } 132 | } 133 | #[allow(unused_mut)] 134 | pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: flatbuffers::Allocator + 'bldr>( 135 | _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr, A>, 136 | args: &'args FrameArgs<'args> 137 | ) -> flatbuffers::WIPOffset> { 138 | let mut builder = FrameBuilder::new(_fbb); 139 | builder.add_exposure_end_t(args.exposure_end_t); 140 | builder.add_exposure_begin_t(args.exposure_begin_t); 141 | builder.add_end_t(args.end_t); 142 | builder.add_begin_t(args.begin_t); 143 | builder.add_t(args.t); 144 | if let Some(x) = args.pixels { builder.add_pixels(x); } 145 | builder.add_offset_y(args.offset_y); 146 | builder.add_offset_x(args.offset_x); 147 | builder.add_height(args.height); 148 | builder.add_width(args.width); 149 | builder.add_format(args.format); 150 | builder.finish() 151 | } 152 | 153 | 154 | #[inline] 155 | pub fn t(&self) -> i64 { 156 | // Safety: 157 | // Created from valid Table for this object 158 | // which contains a valid value in this slot 159 | unsafe { self._tab.get::(Frame::VT_T, Some(0)).unwrap()} 160 | } 161 | #[inline] 162 | pub fn begin_t(&self) -> i64 { 163 | // Safety: 164 | // Created from valid Table for this object 165 | // which contains a valid value in this slot 166 | unsafe { self._tab.get::(Frame::VT_BEGIN_T, Some(0)).unwrap()} 167 | } 168 | #[inline] 169 | pub fn end_t(&self) -> i64 { 170 | // Safety: 171 | // Created from valid Table for this object 172 | // which contains a valid value in this slot 173 | unsafe { self._tab.get::(Frame::VT_END_T, Some(0)).unwrap()} 174 | } 175 | #[inline] 176 | pub fn exposure_begin_t(&self) -> i64 { 177 | // Safety: 178 | // Created from valid Table for this object 179 | // which contains a valid value in this slot 180 | unsafe { self._tab.get::(Frame::VT_EXPOSURE_BEGIN_T, Some(0)).unwrap()} 181 | } 182 | #[inline] 183 | pub fn exposure_end_t(&self) -> i64 { 184 | // Safety: 185 | // Created from valid Table for this object 186 | // which contains a valid value in this slot 187 | unsafe { self._tab.get::(Frame::VT_EXPOSURE_END_T, Some(0)).unwrap()} 188 | } 189 | #[inline] 190 | pub fn format(&self) -> FrameFormat { 191 | // Safety: 192 | // Created from valid Table for this object 193 | // which contains a valid value in this slot 194 | unsafe { self._tab.get::(Frame::VT_FORMAT, Some(FrameFormat::Gray)).unwrap()} 195 | } 196 | #[inline] 197 | pub fn width(&self) -> i16 { 198 | // Safety: 199 | // Created from valid Table for this object 200 | // which contains a valid value in this slot 201 | unsafe { self._tab.get::(Frame::VT_WIDTH, Some(0)).unwrap()} 202 | } 203 | #[inline] 204 | pub fn height(&self) -> i16 { 205 | // Safety: 206 | // Created from valid Table for this object 207 | // which contains a valid value in this slot 208 | unsafe { self._tab.get::(Frame::VT_HEIGHT, Some(0)).unwrap()} 209 | } 210 | #[inline] 211 | pub fn offset_x(&self) -> i16 { 212 | // Safety: 213 | // Created from valid Table for this object 214 | // which contains a valid value in this slot 215 | unsafe { self._tab.get::(Frame::VT_OFFSET_X, Some(0)).unwrap()} 216 | } 217 | #[inline] 218 | pub fn offset_y(&self) -> i16 { 219 | // Safety: 220 | // Created from valid Table for this object 221 | // which contains a valid value in this slot 222 | unsafe { self._tab.get::(Frame::VT_OFFSET_Y, Some(0)).unwrap()} 223 | } 224 | #[inline] 225 | pub fn pixels(&self) -> Option> { 226 | // Safety: 227 | // Created from valid Table for this object 228 | // which contains a valid value in this slot 229 | unsafe { self._tab.get::>>(Frame::VT_PIXELS, None)} 230 | } 231 | } 232 | 233 | impl flatbuffers::Verifiable for Frame<'_> { 234 | #[inline] 235 | fn run_verifier( 236 | v: &mut flatbuffers::Verifier, pos: usize 237 | ) -> Result<(), flatbuffers::InvalidFlatbuffer> { 238 | use self::flatbuffers::Verifiable; 239 | v.visit_table(pos)? 240 | .visit_field::("t", Self::VT_T, false)? 241 | .visit_field::("begin_t", Self::VT_BEGIN_T, false)? 242 | .visit_field::("end_t", Self::VT_END_T, false)? 243 | .visit_field::("exposure_begin_t", Self::VT_EXPOSURE_BEGIN_T, false)? 244 | .visit_field::("exposure_end_t", Self::VT_EXPOSURE_END_T, false)? 245 | .visit_field::("format", Self::VT_FORMAT, false)? 246 | .visit_field::("width", Self::VT_WIDTH, false)? 247 | .visit_field::("height", Self::VT_HEIGHT, false)? 248 | .visit_field::("offset_x", Self::VT_OFFSET_X, false)? 249 | .visit_field::("offset_y", Self::VT_OFFSET_Y, false)? 250 | .visit_field::>>("pixels", Self::VT_PIXELS, false)? 251 | .finish(); 252 | Ok(()) 253 | } 254 | } 255 | pub struct FrameArgs<'a> { 256 | pub t: i64, 257 | pub begin_t: i64, 258 | pub end_t: i64, 259 | pub exposure_begin_t: i64, 260 | pub exposure_end_t: i64, 261 | pub format: FrameFormat, 262 | pub width: i16, 263 | pub height: i16, 264 | pub offset_x: i16, 265 | pub offset_y: i16, 266 | pub pixels: Option>>, 267 | } 268 | impl<'a> Default for FrameArgs<'a> { 269 | #[inline] 270 | fn default() -> Self { 271 | FrameArgs { 272 | t: 0, 273 | begin_t: 0, 274 | end_t: 0, 275 | exposure_begin_t: 0, 276 | exposure_end_t: 0, 277 | format: FrameFormat::Gray, 278 | width: 0, 279 | height: 0, 280 | offset_x: 0, 281 | offset_y: 0, 282 | pixels: None, 283 | } 284 | } 285 | } 286 | 287 | pub struct FrameBuilder<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> { 288 | fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, 289 | start_: flatbuffers::WIPOffset, 290 | } 291 | impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> FrameBuilder<'a, 'b, A> { 292 | #[inline] 293 | pub fn add_t(&mut self, t: i64) { 294 | self.fbb_.push_slot::(Frame::VT_T, t, 0); 295 | } 296 | #[inline] 297 | pub fn add_begin_t(&mut self, begin_t: i64) { 298 | self.fbb_.push_slot::(Frame::VT_BEGIN_T, begin_t, 0); 299 | } 300 | #[inline] 301 | pub fn add_end_t(&mut self, end_t: i64) { 302 | self.fbb_.push_slot::(Frame::VT_END_T, end_t, 0); 303 | } 304 | #[inline] 305 | pub fn add_exposure_begin_t(&mut self, exposure_begin_t: i64) { 306 | self.fbb_.push_slot::(Frame::VT_EXPOSURE_BEGIN_T, exposure_begin_t, 0); 307 | } 308 | #[inline] 309 | pub fn add_exposure_end_t(&mut self, exposure_end_t: i64) { 310 | self.fbb_.push_slot::(Frame::VT_EXPOSURE_END_T, exposure_end_t, 0); 311 | } 312 | #[inline] 313 | pub fn add_format(&mut self, format: FrameFormat) { 314 | self.fbb_.push_slot::(Frame::VT_FORMAT, format, FrameFormat::Gray); 315 | } 316 | #[inline] 317 | pub fn add_width(&mut self, width: i16) { 318 | self.fbb_.push_slot::(Frame::VT_WIDTH, width, 0); 319 | } 320 | #[inline] 321 | pub fn add_height(&mut self, height: i16) { 322 | self.fbb_.push_slot::(Frame::VT_HEIGHT, height, 0); 323 | } 324 | #[inline] 325 | pub fn add_offset_x(&mut self, offset_x: i16) { 326 | self.fbb_.push_slot::(Frame::VT_OFFSET_X, offset_x, 0); 327 | } 328 | #[inline] 329 | pub fn add_offset_y(&mut self, offset_y: i16) { 330 | self.fbb_.push_slot::(Frame::VT_OFFSET_Y, offset_y, 0); 331 | } 332 | #[inline] 333 | pub fn add_pixels(&mut self, pixels: flatbuffers::WIPOffset>) { 334 | self.fbb_.push_slot_always::>(Frame::VT_PIXELS, pixels); 335 | } 336 | #[inline] 337 | pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>) -> FrameBuilder<'a, 'b, A> { 338 | let start = _fbb.start_table(); 339 | FrameBuilder { 340 | fbb_: _fbb, 341 | start_: start, 342 | } 343 | } 344 | #[inline] 345 | pub fn finish(self) -> flatbuffers::WIPOffset> { 346 | let o = self.fbb_.end_table(self.start_); 347 | flatbuffers::WIPOffset::new(o.value()) 348 | } 349 | } 350 | 351 | impl core::fmt::Debug for Frame<'_> { 352 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 353 | let mut ds = f.debug_struct("Frame"); 354 | ds.field("t", &self.t()); 355 | ds.field("begin_t", &self.begin_t()); 356 | ds.field("end_t", &self.end_t()); 357 | ds.field("exposure_begin_t", &self.exposure_begin_t()); 358 | ds.field("exposure_end_t", &self.exposure_end_t()); 359 | ds.field("format", &self.format()); 360 | ds.field("width", &self.width()); 361 | ds.field("height", &self.height()); 362 | ds.field("offset_x", &self.offset_x()); 363 | ds.field("offset_y", &self.offset_y()); 364 | ds.field("pixels", &self.pixels()); 365 | ds.finish() 366 | } 367 | } 368 | #[inline] 369 | /// Verifies that a buffer of bytes contains a `Frame` 370 | /// and returns it. 371 | /// Note that verification is still experimental and may not 372 | /// catch every error, or be maximally performant. For the 373 | /// previous, unchecked, behavior use 374 | /// `root_as_frame_unchecked`. 375 | pub fn root_as_frame(buf: &[u8]) -> Result { 376 | flatbuffers::root::(buf) 377 | } 378 | #[inline] 379 | /// Verifies that a buffer of bytes contains a size prefixed 380 | /// `Frame` and returns it. 381 | /// Note that verification is still experimental and may not 382 | /// catch every error, or be maximally performant. For the 383 | /// previous, unchecked, behavior use 384 | /// `size_prefixed_root_as_frame_unchecked`. 385 | pub fn size_prefixed_root_as_frame(buf: &[u8]) -> Result { 386 | flatbuffers::size_prefixed_root::(buf) 387 | } 388 | #[inline] 389 | /// Verifies, with the given options, that a buffer of bytes 390 | /// contains a `Frame` and returns it. 391 | /// Note that verification is still experimental and may not 392 | /// catch every error, or be maximally performant. For the 393 | /// previous, unchecked, behavior use 394 | /// `root_as_frame_unchecked`. 395 | pub fn root_as_frame_with_opts<'b, 'o>( 396 | opts: &'o flatbuffers::VerifierOptions, 397 | buf: &'b [u8], 398 | ) -> Result, flatbuffers::InvalidFlatbuffer> { 399 | flatbuffers::root_with_opts::>(opts, buf) 400 | } 401 | #[inline] 402 | /// Verifies, with the given verifier options, that a buffer of 403 | /// bytes contains a size prefixed `Frame` and returns 404 | /// it. Note that verification is still experimental and may not 405 | /// catch every error, or be maximally performant. For the 406 | /// previous, unchecked, behavior use 407 | /// `root_as_frame_unchecked`. 408 | pub fn size_prefixed_root_as_frame_with_opts<'b, 'o>( 409 | opts: &'o flatbuffers::VerifierOptions, 410 | buf: &'b [u8], 411 | ) -> Result, flatbuffers::InvalidFlatbuffer> { 412 | flatbuffers::size_prefixed_root_with_opts::>(opts, buf) 413 | } 414 | #[inline] 415 | /// Assumes, without verification, that a buffer of bytes contains a Frame and returns it. 416 | /// # Safety 417 | /// Callers must trust the given bytes do indeed contain a valid `Frame`. 418 | pub unsafe fn root_as_frame_unchecked(buf: &[u8]) -> Frame { 419 | flatbuffers::root_unchecked::(buf) 420 | } 421 | #[inline] 422 | /// Assumes, without verification, that a buffer of bytes contains a size prefixed Frame and returns it. 423 | /// # Safety 424 | /// Callers must trust the given bytes do indeed contain a valid size prefixed `Frame`. 425 | pub unsafe fn size_prefixed_root_as_frame_unchecked(buf: &[u8]) -> Frame { 426 | flatbuffers::size_prefixed_root_unchecked::(buf) 427 | } 428 | pub const FRAME_IDENTIFIER: &str = "FRME"; 429 | 430 | #[inline] 431 | pub fn frame_buffer_has_identifier(buf: &[u8]) -> bool { 432 | flatbuffers::buffer_has_identifier(buf, FRAME_IDENTIFIER, false) 433 | } 434 | 435 | #[inline] 436 | pub fn frame_size_prefixed_buffer_has_identifier(buf: &[u8]) -> bool { 437 | flatbuffers::buffer_has_identifier(buf, FRAME_IDENTIFIER, true) 438 | } 439 | 440 | #[inline] 441 | pub fn finish_frame_buffer<'a, 'b, A: flatbuffers::Allocator + 'a>( 442 | fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, 443 | root: flatbuffers::WIPOffset>) { 444 | fbb.finish(root, Some(FRAME_IDENTIFIER)); 445 | } 446 | 447 | #[inline] 448 | pub fn finish_size_prefixed_frame_buffer<'a, 'b, A: flatbuffers::Allocator + 'a>(fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, root: flatbuffers::WIPOffset>) { 449 | fbb.finish_size_prefixed(root, Some(FRAME_IDENTIFIER)); 450 | } 451 | -------------------------------------------------------------------------------- /src/imus_generated.rs: -------------------------------------------------------------------------------- 1 | // automatically generated by the FlatBuffers compiler, do not modify 2 | 3 | 4 | // @generated 5 | 6 | use core::mem; 7 | use core::cmp::Ordering; 8 | 9 | extern crate flatbuffers; 10 | use self::flatbuffers::{EndianScalar, Follow}; 11 | 12 | pub enum ImuOffset {} 13 | #[derive(Copy, Clone, PartialEq)] 14 | 15 | pub struct Imu<'a> { 16 | pub _tab: flatbuffers::Table<'a>, 17 | } 18 | 19 | impl<'a> flatbuffers::Follow<'a> for Imu<'a> { 20 | type Inner = Imu<'a>; 21 | #[inline] 22 | unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { 23 | Self { _tab: flatbuffers::Table::new(buf, loc) } 24 | } 25 | } 26 | 27 | impl<'a> Imu<'a> { 28 | pub const VT_T: flatbuffers::VOffsetT = 4; 29 | pub const VT_TEMPERATURE: flatbuffers::VOffsetT = 6; 30 | pub const VT_ACCELEROMETER_X: flatbuffers::VOffsetT = 8; 31 | pub const VT_ACCELEROMETER_Y: flatbuffers::VOffsetT = 10; 32 | pub const VT_ACCELEROMETER_Z: flatbuffers::VOffsetT = 12; 33 | pub const VT_GYROSCOPE_X: flatbuffers::VOffsetT = 14; 34 | pub const VT_GYROSCOPE_Y: flatbuffers::VOffsetT = 16; 35 | pub const VT_GYROSCOPE_Z: flatbuffers::VOffsetT = 18; 36 | pub const VT_MAGNETOMETER_X: flatbuffers::VOffsetT = 20; 37 | pub const VT_MAGNETOMETER_Y: flatbuffers::VOffsetT = 22; 38 | pub const VT_MAGNETOMETER_Z: flatbuffers::VOffsetT = 24; 39 | 40 | #[inline] 41 | pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { 42 | Imu { _tab: table } 43 | } 44 | #[allow(unused_mut)] 45 | pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: flatbuffers::Allocator + 'bldr>( 46 | _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr, A>, 47 | args: &'args ImuArgs 48 | ) -> flatbuffers::WIPOffset> { 49 | let mut builder = ImuBuilder::new(_fbb); 50 | builder.add_t(args.t); 51 | builder.add_magnetometer_z(args.magnetometer_z); 52 | builder.add_magnetometer_y(args.magnetometer_y); 53 | builder.add_magnetometer_x(args.magnetometer_x); 54 | builder.add_gyroscope_z(args.gyroscope_z); 55 | builder.add_gyroscope_y(args.gyroscope_y); 56 | builder.add_gyroscope_x(args.gyroscope_x); 57 | builder.add_accelerometer_z(args.accelerometer_z); 58 | builder.add_accelerometer_y(args.accelerometer_y); 59 | builder.add_accelerometer_x(args.accelerometer_x); 60 | builder.add_temperature(args.temperature); 61 | builder.finish() 62 | } 63 | 64 | 65 | #[inline] 66 | pub fn t(&self) -> i64 { 67 | // Safety: 68 | // Created from valid Table for this object 69 | // which contains a valid value in this slot 70 | unsafe { self._tab.get::(Imu::VT_T, Some(0)).unwrap()} 71 | } 72 | #[inline] 73 | pub fn temperature(&self) -> f32 { 74 | // Safety: 75 | // Created from valid Table for this object 76 | // which contains a valid value in this slot 77 | unsafe { self._tab.get::(Imu::VT_TEMPERATURE, Some(0.0)).unwrap()} 78 | } 79 | #[inline] 80 | pub fn accelerometer_x(&self) -> f32 { 81 | // Safety: 82 | // Created from valid Table for this object 83 | // which contains a valid value in this slot 84 | unsafe { self._tab.get::(Imu::VT_ACCELEROMETER_X, Some(0.0)).unwrap()} 85 | } 86 | #[inline] 87 | pub fn accelerometer_y(&self) -> f32 { 88 | // Safety: 89 | // Created from valid Table for this object 90 | // which contains a valid value in this slot 91 | unsafe { self._tab.get::(Imu::VT_ACCELEROMETER_Y, Some(0.0)).unwrap()} 92 | } 93 | #[inline] 94 | pub fn accelerometer_z(&self) -> f32 { 95 | // Safety: 96 | // Created from valid Table for this object 97 | // which contains a valid value in this slot 98 | unsafe { self._tab.get::(Imu::VT_ACCELEROMETER_Z, Some(0.0)).unwrap()} 99 | } 100 | #[inline] 101 | pub fn gyroscope_x(&self) -> f32 { 102 | // Safety: 103 | // Created from valid Table for this object 104 | // which contains a valid value in this slot 105 | unsafe { self._tab.get::(Imu::VT_GYROSCOPE_X, Some(0.0)).unwrap()} 106 | } 107 | #[inline] 108 | pub fn gyroscope_y(&self) -> f32 { 109 | // Safety: 110 | // Created from valid Table for this object 111 | // which contains a valid value in this slot 112 | unsafe { self._tab.get::(Imu::VT_GYROSCOPE_Y, Some(0.0)).unwrap()} 113 | } 114 | #[inline] 115 | pub fn gyroscope_z(&self) -> f32 { 116 | // Safety: 117 | // Created from valid Table for this object 118 | // which contains a valid value in this slot 119 | unsafe { self._tab.get::(Imu::VT_GYROSCOPE_Z, Some(0.0)).unwrap()} 120 | } 121 | #[inline] 122 | pub fn magnetometer_x(&self) -> f32 { 123 | // Safety: 124 | // Created from valid Table for this object 125 | // which contains a valid value in this slot 126 | unsafe { self._tab.get::(Imu::VT_MAGNETOMETER_X, Some(0.0)).unwrap()} 127 | } 128 | #[inline] 129 | pub fn magnetometer_y(&self) -> f32 { 130 | // Safety: 131 | // Created from valid Table for this object 132 | // which contains a valid value in this slot 133 | unsafe { self._tab.get::(Imu::VT_MAGNETOMETER_Y, Some(0.0)).unwrap()} 134 | } 135 | #[inline] 136 | pub fn magnetometer_z(&self) -> f32 { 137 | // Safety: 138 | // Created from valid Table for this object 139 | // which contains a valid value in this slot 140 | unsafe { self._tab.get::(Imu::VT_MAGNETOMETER_Z, Some(0.0)).unwrap()} 141 | } 142 | } 143 | 144 | impl flatbuffers::Verifiable for Imu<'_> { 145 | #[inline] 146 | fn run_verifier( 147 | v: &mut flatbuffers::Verifier, pos: usize 148 | ) -> Result<(), flatbuffers::InvalidFlatbuffer> { 149 | use self::flatbuffers::Verifiable; 150 | v.visit_table(pos)? 151 | .visit_field::("t", Self::VT_T, false)? 152 | .visit_field::("temperature", Self::VT_TEMPERATURE, false)? 153 | .visit_field::("accelerometer_x", Self::VT_ACCELEROMETER_X, false)? 154 | .visit_field::("accelerometer_y", Self::VT_ACCELEROMETER_Y, false)? 155 | .visit_field::("accelerometer_z", Self::VT_ACCELEROMETER_Z, false)? 156 | .visit_field::("gyroscope_x", Self::VT_GYROSCOPE_X, false)? 157 | .visit_field::("gyroscope_y", Self::VT_GYROSCOPE_Y, false)? 158 | .visit_field::("gyroscope_z", Self::VT_GYROSCOPE_Z, false)? 159 | .visit_field::("magnetometer_x", Self::VT_MAGNETOMETER_X, false)? 160 | .visit_field::("magnetometer_y", Self::VT_MAGNETOMETER_Y, false)? 161 | .visit_field::("magnetometer_z", Self::VT_MAGNETOMETER_Z, false)? 162 | .finish(); 163 | Ok(()) 164 | } 165 | } 166 | pub struct ImuArgs { 167 | pub t: i64, 168 | pub temperature: f32, 169 | pub accelerometer_x: f32, 170 | pub accelerometer_y: f32, 171 | pub accelerometer_z: f32, 172 | pub gyroscope_x: f32, 173 | pub gyroscope_y: f32, 174 | pub gyroscope_z: f32, 175 | pub magnetometer_x: f32, 176 | pub magnetometer_y: f32, 177 | pub magnetometer_z: f32, 178 | } 179 | impl<'a> Default for ImuArgs { 180 | #[inline] 181 | fn default() -> Self { 182 | ImuArgs { 183 | t: 0, 184 | temperature: 0.0, 185 | accelerometer_x: 0.0, 186 | accelerometer_y: 0.0, 187 | accelerometer_z: 0.0, 188 | gyroscope_x: 0.0, 189 | gyroscope_y: 0.0, 190 | gyroscope_z: 0.0, 191 | magnetometer_x: 0.0, 192 | magnetometer_y: 0.0, 193 | magnetometer_z: 0.0, 194 | } 195 | } 196 | } 197 | 198 | pub struct ImuBuilder<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> { 199 | fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, 200 | start_: flatbuffers::WIPOffset, 201 | } 202 | impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> ImuBuilder<'a, 'b, A> { 203 | #[inline] 204 | pub fn add_t(&mut self, t: i64) { 205 | self.fbb_.push_slot::(Imu::VT_T, t, 0); 206 | } 207 | #[inline] 208 | pub fn add_temperature(&mut self, temperature: f32) { 209 | self.fbb_.push_slot::(Imu::VT_TEMPERATURE, temperature, 0.0); 210 | } 211 | #[inline] 212 | pub fn add_accelerometer_x(&mut self, accelerometer_x: f32) { 213 | self.fbb_.push_slot::(Imu::VT_ACCELEROMETER_X, accelerometer_x, 0.0); 214 | } 215 | #[inline] 216 | pub fn add_accelerometer_y(&mut self, accelerometer_y: f32) { 217 | self.fbb_.push_slot::(Imu::VT_ACCELEROMETER_Y, accelerometer_y, 0.0); 218 | } 219 | #[inline] 220 | pub fn add_accelerometer_z(&mut self, accelerometer_z: f32) { 221 | self.fbb_.push_slot::(Imu::VT_ACCELEROMETER_Z, accelerometer_z, 0.0); 222 | } 223 | #[inline] 224 | pub fn add_gyroscope_x(&mut self, gyroscope_x: f32) { 225 | self.fbb_.push_slot::(Imu::VT_GYROSCOPE_X, gyroscope_x, 0.0); 226 | } 227 | #[inline] 228 | pub fn add_gyroscope_y(&mut self, gyroscope_y: f32) { 229 | self.fbb_.push_slot::(Imu::VT_GYROSCOPE_Y, gyroscope_y, 0.0); 230 | } 231 | #[inline] 232 | pub fn add_gyroscope_z(&mut self, gyroscope_z: f32) { 233 | self.fbb_.push_slot::(Imu::VT_GYROSCOPE_Z, gyroscope_z, 0.0); 234 | } 235 | #[inline] 236 | pub fn add_magnetometer_x(&mut self, magnetometer_x: f32) { 237 | self.fbb_.push_slot::(Imu::VT_MAGNETOMETER_X, magnetometer_x, 0.0); 238 | } 239 | #[inline] 240 | pub fn add_magnetometer_y(&mut self, magnetometer_y: f32) { 241 | self.fbb_.push_slot::(Imu::VT_MAGNETOMETER_Y, magnetometer_y, 0.0); 242 | } 243 | #[inline] 244 | pub fn add_magnetometer_z(&mut self, magnetometer_z: f32) { 245 | self.fbb_.push_slot::(Imu::VT_MAGNETOMETER_Z, magnetometer_z, 0.0); 246 | } 247 | #[inline] 248 | pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>) -> ImuBuilder<'a, 'b, A> { 249 | let start = _fbb.start_table(); 250 | ImuBuilder { 251 | fbb_: _fbb, 252 | start_: start, 253 | } 254 | } 255 | #[inline] 256 | pub fn finish(self) -> flatbuffers::WIPOffset> { 257 | let o = self.fbb_.end_table(self.start_); 258 | flatbuffers::WIPOffset::new(o.value()) 259 | } 260 | } 261 | 262 | impl core::fmt::Debug for Imu<'_> { 263 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 264 | let mut ds = f.debug_struct("Imu"); 265 | ds.field("t", &self.t()); 266 | ds.field("temperature", &self.temperature()); 267 | ds.field("accelerometer_x", &self.accelerometer_x()); 268 | ds.field("accelerometer_y", &self.accelerometer_y()); 269 | ds.field("accelerometer_z", &self.accelerometer_z()); 270 | ds.field("gyroscope_x", &self.gyroscope_x()); 271 | ds.field("gyroscope_y", &self.gyroscope_y()); 272 | ds.field("gyroscope_z", &self.gyroscope_z()); 273 | ds.field("magnetometer_x", &self.magnetometer_x()); 274 | ds.field("magnetometer_y", &self.magnetometer_y()); 275 | ds.field("magnetometer_z", &self.magnetometer_z()); 276 | ds.finish() 277 | } 278 | } 279 | pub enum ImuPacketOffset {} 280 | #[derive(Copy, Clone, PartialEq)] 281 | 282 | pub struct ImuPacket<'a> { 283 | pub _tab: flatbuffers::Table<'a>, 284 | } 285 | 286 | impl<'a> flatbuffers::Follow<'a> for ImuPacket<'a> { 287 | type Inner = ImuPacket<'a>; 288 | #[inline] 289 | unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { 290 | Self { _tab: flatbuffers::Table::new(buf, loc) } 291 | } 292 | } 293 | 294 | impl<'a> ImuPacket<'a> { 295 | pub const VT_ELEMENTS: flatbuffers::VOffsetT = 4; 296 | 297 | #[inline] 298 | pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { 299 | ImuPacket { _tab: table } 300 | } 301 | #[allow(unused_mut)] 302 | pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: flatbuffers::Allocator + 'bldr>( 303 | _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr, A>, 304 | args: &'args ImuPacketArgs<'args> 305 | ) -> flatbuffers::WIPOffset> { 306 | let mut builder = ImuPacketBuilder::new(_fbb); 307 | if let Some(x) = args.elements { builder.add_elements(x); } 308 | builder.finish() 309 | } 310 | 311 | 312 | #[inline] 313 | pub fn elements(&self) -> Option>>> { 314 | // Safety: 315 | // Created from valid Table for this object 316 | // which contains a valid value in this slot 317 | unsafe { self._tab.get::>>>(ImuPacket::VT_ELEMENTS, None)} 318 | } 319 | } 320 | 321 | impl flatbuffers::Verifiable for ImuPacket<'_> { 322 | #[inline] 323 | fn run_verifier( 324 | v: &mut flatbuffers::Verifier, pos: usize 325 | ) -> Result<(), flatbuffers::InvalidFlatbuffer> { 326 | use self::flatbuffers::Verifiable; 327 | v.visit_table(pos)? 328 | .visit_field::>>>("elements", Self::VT_ELEMENTS, false)? 329 | .finish(); 330 | Ok(()) 331 | } 332 | } 333 | pub struct ImuPacketArgs<'a> { 334 | pub elements: Option>>>>, 335 | } 336 | impl<'a> Default for ImuPacketArgs<'a> { 337 | #[inline] 338 | fn default() -> Self { 339 | ImuPacketArgs { 340 | elements: None, 341 | } 342 | } 343 | } 344 | 345 | pub struct ImuPacketBuilder<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> { 346 | fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, 347 | start_: flatbuffers::WIPOffset, 348 | } 349 | impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> ImuPacketBuilder<'a, 'b, A> { 350 | #[inline] 351 | pub fn add_elements(&mut self, elements: flatbuffers::WIPOffset>>>) { 352 | self.fbb_.push_slot_always::>(ImuPacket::VT_ELEMENTS, elements); 353 | } 354 | #[inline] 355 | pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>) -> ImuPacketBuilder<'a, 'b, A> { 356 | let start = _fbb.start_table(); 357 | ImuPacketBuilder { 358 | fbb_: _fbb, 359 | start_: start, 360 | } 361 | } 362 | #[inline] 363 | pub fn finish(self) -> flatbuffers::WIPOffset> { 364 | let o = self.fbb_.end_table(self.start_); 365 | flatbuffers::WIPOffset::new(o.value()) 366 | } 367 | } 368 | 369 | impl core::fmt::Debug for ImuPacket<'_> { 370 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 371 | let mut ds = f.debug_struct("ImuPacket"); 372 | ds.field("elements", &self.elements()); 373 | ds.finish() 374 | } 375 | } 376 | #[inline] 377 | /// Verifies that a buffer of bytes contains a `ImuPacket` 378 | /// and returns it. 379 | /// Note that verification is still experimental and may not 380 | /// catch every error, or be maximally performant. For the 381 | /// previous, unchecked, behavior use 382 | /// `root_as_imu_packet_unchecked`. 383 | pub fn root_as_imu_packet(buf: &[u8]) -> Result { 384 | flatbuffers::root::(buf) 385 | } 386 | #[inline] 387 | /// Verifies that a buffer of bytes contains a size prefixed 388 | /// `ImuPacket` and returns it. 389 | /// Note that verification is still experimental and may not 390 | /// catch every error, or be maximally performant. For the 391 | /// previous, unchecked, behavior use 392 | /// `size_prefixed_root_as_imu_packet_unchecked`. 393 | pub fn size_prefixed_root_as_imu_packet(buf: &[u8]) -> Result { 394 | flatbuffers::size_prefixed_root::(buf) 395 | } 396 | #[inline] 397 | /// Verifies, with the given options, that a buffer of bytes 398 | /// contains a `ImuPacket` and returns it. 399 | /// Note that verification is still experimental and may not 400 | /// catch every error, or be maximally performant. For the 401 | /// previous, unchecked, behavior use 402 | /// `root_as_imu_packet_unchecked`. 403 | pub fn root_as_imu_packet_with_opts<'b, 'o>( 404 | opts: &'o flatbuffers::VerifierOptions, 405 | buf: &'b [u8], 406 | ) -> Result, flatbuffers::InvalidFlatbuffer> { 407 | flatbuffers::root_with_opts::>(opts, buf) 408 | } 409 | #[inline] 410 | /// Verifies, with the given verifier options, that a buffer of 411 | /// bytes contains a size prefixed `ImuPacket` and returns 412 | /// it. Note that verification is still experimental and may not 413 | /// catch every error, or be maximally performant. For the 414 | /// previous, unchecked, behavior use 415 | /// `root_as_imu_packet_unchecked`. 416 | pub fn size_prefixed_root_as_imu_packet_with_opts<'b, 'o>( 417 | opts: &'o flatbuffers::VerifierOptions, 418 | buf: &'b [u8], 419 | ) -> Result, flatbuffers::InvalidFlatbuffer> { 420 | flatbuffers::size_prefixed_root_with_opts::>(opts, buf) 421 | } 422 | #[inline] 423 | /// Assumes, without verification, that a buffer of bytes contains a ImuPacket and returns it. 424 | /// # Safety 425 | /// Callers must trust the given bytes do indeed contain a valid `ImuPacket`. 426 | pub unsafe fn root_as_imu_packet_unchecked(buf: &[u8]) -> ImuPacket { 427 | flatbuffers::root_unchecked::(buf) 428 | } 429 | #[inline] 430 | /// Assumes, without verification, that a buffer of bytes contains a size prefixed ImuPacket and returns it. 431 | /// # Safety 432 | /// Callers must trust the given bytes do indeed contain a valid size prefixed `ImuPacket`. 433 | pub unsafe fn size_prefixed_root_as_imu_packet_unchecked(buf: &[u8]) -> ImuPacket { 434 | flatbuffers::size_prefixed_root_unchecked::(buf) 435 | } 436 | pub const IMU_PACKET_IDENTIFIER: &str = "IMUS"; 437 | 438 | #[inline] 439 | pub fn imu_packet_buffer_has_identifier(buf: &[u8]) -> bool { 440 | flatbuffers::buffer_has_identifier(buf, IMU_PACKET_IDENTIFIER, false) 441 | } 442 | 443 | #[inline] 444 | pub fn imu_packet_size_prefixed_buffer_has_identifier(buf: &[u8]) -> bool { 445 | flatbuffers::buffer_has_identifier(buf, IMU_PACKET_IDENTIFIER, true) 446 | } 447 | 448 | #[inline] 449 | pub fn finish_imu_packet_buffer<'a, 'b, A: flatbuffers::Allocator + 'a>( 450 | fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, 451 | root: flatbuffers::WIPOffset>) { 452 | fbb.finish(root, Some(IMU_PACKET_IDENTIFIER)); 453 | } 454 | 455 | #[inline] 456 | pub fn finish_size_prefixed_imu_packet_buffer<'a, 'b, A: flatbuffers::Allocator + 'a>(fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, root: flatbuffers::WIPOffset>) { 457 | fbb.finish_size_prefixed(root, Some(IMU_PACKET_IDENTIFIER)); 458 | } 459 | -------------------------------------------------------------------------------- /src/ioheader_generated.rs: -------------------------------------------------------------------------------- 1 | // automatically generated by the FlatBuffers compiler, do not modify 2 | 3 | 4 | // @generated 5 | 6 | use core::mem; 7 | use core::cmp::Ordering; 8 | 9 | extern crate flatbuffers; 10 | use self::flatbuffers::{EndianScalar, Follow}; 11 | 12 | #[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] 13 | pub const ENUM_MIN_COMPRESSION: i32 = 0; 14 | #[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] 15 | pub const ENUM_MAX_COMPRESSION: i32 = 4; 16 | #[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] 17 | #[allow(non_camel_case_types)] 18 | pub const ENUM_VALUES_COMPRESSION: [Compression; 5] = [ 19 | Compression::None, 20 | Compression::Lz4, 21 | Compression::Lz4High, 22 | Compression::Zstd, 23 | Compression::ZstdHigh, 24 | ]; 25 | 26 | #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] 27 | #[repr(transparent)] 28 | pub struct Compression(pub i32); 29 | #[allow(non_upper_case_globals)] 30 | impl Compression { 31 | pub const None: Self = Self(0); 32 | pub const Lz4: Self = Self(1); 33 | pub const Lz4High: Self = Self(2); 34 | pub const Zstd: Self = Self(3); 35 | pub const ZstdHigh: Self = Self(4); 36 | 37 | pub const ENUM_MIN: i32 = 0; 38 | pub const ENUM_MAX: i32 = 4; 39 | pub const ENUM_VALUES: &'static [Self] = &[ 40 | Self::None, 41 | Self::Lz4, 42 | Self::Lz4High, 43 | Self::Zstd, 44 | Self::ZstdHigh, 45 | ]; 46 | /// Returns the variant's name or "" if unknown. 47 | pub fn variant_name(self) -> Option<&'static str> { 48 | match self { 49 | Self::None => Some("None"), 50 | Self::Lz4 => Some("Lz4"), 51 | Self::Lz4High => Some("Lz4High"), 52 | Self::Zstd => Some("Zstd"), 53 | Self::ZstdHigh => Some("ZstdHigh"), 54 | _ => None, 55 | } 56 | } 57 | } 58 | impl core::fmt::Debug for Compression { 59 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { 60 | if let Some(name) = self.variant_name() { 61 | f.write_str(name) 62 | } else { 63 | f.write_fmt(format_args!("", self.0)) 64 | } 65 | } 66 | } 67 | impl<'a> flatbuffers::Follow<'a> for Compression { 68 | type Inner = Self; 69 | #[inline] 70 | unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { 71 | let b = flatbuffers::read_scalar_at::(buf, loc); 72 | Self(b) 73 | } 74 | } 75 | 76 | impl flatbuffers::Push for Compression { 77 | type Output = Compression; 78 | #[inline] 79 | unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { 80 | flatbuffers::emplace_scalar::(dst, self.0); 81 | } 82 | } 83 | 84 | impl flatbuffers::EndianScalar for Compression { 85 | type Scalar = i32; 86 | #[inline] 87 | fn to_little_endian(self) -> i32 { 88 | self.0.to_le() 89 | } 90 | #[inline] 91 | #[allow(clippy::wrong_self_convention)] 92 | fn from_little_endian(v: i32) -> Self { 93 | let b = i32::from_le(v); 94 | Self(b) 95 | } 96 | } 97 | 98 | impl<'a> flatbuffers::Verifiable for Compression { 99 | #[inline] 100 | fn run_verifier( 101 | v: &mut flatbuffers::Verifier, pos: usize 102 | ) -> Result<(), flatbuffers::InvalidFlatbuffer> { 103 | use self::flatbuffers::Verifiable; 104 | i32::run_verifier(v, pos) 105 | } 106 | } 107 | 108 | impl flatbuffers::SimpleToVerifyInSlice for Compression {} 109 | pub enum IoheaderOffset {} 110 | #[derive(Copy, Clone, PartialEq)] 111 | 112 | pub struct Ioheader<'a> { 113 | pub _tab: flatbuffers::Table<'a>, 114 | } 115 | 116 | impl<'a> flatbuffers::Follow<'a> for Ioheader<'a> { 117 | type Inner = Ioheader<'a>; 118 | #[inline] 119 | unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { 120 | Self { _tab: flatbuffers::Table::new(buf, loc) } 121 | } 122 | } 123 | 124 | impl<'a> Ioheader<'a> { 125 | pub const VT_COMPRESSION: flatbuffers::VOffsetT = 4; 126 | pub const VT_FILE_DATA_POSITION: flatbuffers::VOffsetT = 6; 127 | pub const VT_DESCRIPTION: flatbuffers::VOffsetT = 8; 128 | 129 | #[inline] 130 | pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { 131 | Ioheader { _tab: table } 132 | } 133 | #[allow(unused_mut)] 134 | pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: flatbuffers::Allocator + 'bldr>( 135 | _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr, A>, 136 | args: &'args IoheaderArgs<'args> 137 | ) -> flatbuffers::WIPOffset> { 138 | let mut builder = IoheaderBuilder::new(_fbb); 139 | builder.add_file_data_position(args.file_data_position); 140 | if let Some(x) = args.description { builder.add_description(x); } 141 | builder.add_compression(args.compression); 142 | builder.finish() 143 | } 144 | 145 | 146 | #[inline] 147 | pub fn compression(&self) -> Compression { 148 | // Safety: 149 | // Created from valid Table for this object 150 | // which contains a valid value in this slot 151 | unsafe { self._tab.get::(Ioheader::VT_COMPRESSION, Some(Compression::None)).unwrap()} 152 | } 153 | #[inline] 154 | pub fn file_data_position(&self) -> i64 { 155 | // Safety: 156 | // Created from valid Table for this object 157 | // which contains a valid value in this slot 158 | unsafe { self._tab.get::(Ioheader::VT_FILE_DATA_POSITION, Some(-1)).unwrap()} 159 | } 160 | #[inline] 161 | pub fn description(&self) -> Option<&'a str> { 162 | // Safety: 163 | // Created from valid Table for this object 164 | // which contains a valid value in this slot 165 | unsafe { self._tab.get::>(Ioheader::VT_DESCRIPTION, None)} 166 | } 167 | } 168 | 169 | impl flatbuffers::Verifiable for Ioheader<'_> { 170 | #[inline] 171 | fn run_verifier( 172 | v: &mut flatbuffers::Verifier, pos: usize 173 | ) -> Result<(), flatbuffers::InvalidFlatbuffer> { 174 | use self::flatbuffers::Verifiable; 175 | v.visit_table(pos)? 176 | .visit_field::("compression", Self::VT_COMPRESSION, false)? 177 | .visit_field::("file_data_position", Self::VT_FILE_DATA_POSITION, false)? 178 | .visit_field::>("description", Self::VT_DESCRIPTION, false)? 179 | .finish(); 180 | Ok(()) 181 | } 182 | } 183 | pub struct IoheaderArgs<'a> { 184 | pub compression: Compression, 185 | pub file_data_position: i64, 186 | pub description: Option>, 187 | } 188 | impl<'a> Default for IoheaderArgs<'a> { 189 | #[inline] 190 | fn default() -> Self { 191 | IoheaderArgs { 192 | compression: Compression::None, 193 | file_data_position: -1, 194 | description: None, 195 | } 196 | } 197 | } 198 | 199 | pub struct IoheaderBuilder<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> { 200 | fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, 201 | start_: flatbuffers::WIPOffset, 202 | } 203 | impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> IoheaderBuilder<'a, 'b, A> { 204 | #[inline] 205 | pub fn add_compression(&mut self, compression: Compression) { 206 | self.fbb_.push_slot::(Ioheader::VT_COMPRESSION, compression, Compression::None); 207 | } 208 | #[inline] 209 | pub fn add_file_data_position(&mut self, file_data_position: i64) { 210 | self.fbb_.push_slot::(Ioheader::VT_FILE_DATA_POSITION, file_data_position, -1); 211 | } 212 | #[inline] 213 | pub fn add_description(&mut self, description: flatbuffers::WIPOffset<&'b str>) { 214 | self.fbb_.push_slot_always::>(Ioheader::VT_DESCRIPTION, description); 215 | } 216 | #[inline] 217 | pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>) -> IoheaderBuilder<'a, 'b, A> { 218 | let start = _fbb.start_table(); 219 | IoheaderBuilder { 220 | fbb_: _fbb, 221 | start_: start, 222 | } 223 | } 224 | #[inline] 225 | pub fn finish(self) -> flatbuffers::WIPOffset> { 226 | let o = self.fbb_.end_table(self.start_); 227 | flatbuffers::WIPOffset::new(o.value()) 228 | } 229 | } 230 | 231 | impl core::fmt::Debug for Ioheader<'_> { 232 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 233 | let mut ds = f.debug_struct("Ioheader"); 234 | ds.field("compression", &self.compression()); 235 | ds.field("file_data_position", &self.file_data_position()); 236 | ds.field("description", &self.description()); 237 | ds.finish() 238 | } 239 | } 240 | #[inline] 241 | /// Verifies that a buffer of bytes contains a `Ioheader` 242 | /// and returns it. 243 | /// Note that verification is still experimental and may not 244 | /// catch every error, or be maximally performant. For the 245 | /// previous, unchecked, behavior use 246 | /// `root_as_ioheader_unchecked`. 247 | pub fn root_as_ioheader(buf: &[u8]) -> Result { 248 | flatbuffers::root::(buf) 249 | } 250 | #[inline] 251 | /// Verifies that a buffer of bytes contains a size prefixed 252 | /// `Ioheader` and returns it. 253 | /// Note that verification is still experimental and may not 254 | /// catch every error, or be maximally performant. For the 255 | /// previous, unchecked, behavior use 256 | /// `size_prefixed_root_as_ioheader_unchecked`. 257 | pub fn size_prefixed_root_as_ioheader(buf: &[u8]) -> Result { 258 | flatbuffers::size_prefixed_root::(buf) 259 | } 260 | #[inline] 261 | /// Verifies, with the given options, that a buffer of bytes 262 | /// contains a `Ioheader` and returns it. 263 | /// Note that verification is still experimental and may not 264 | /// catch every error, or be maximally performant. For the 265 | /// previous, unchecked, behavior use 266 | /// `root_as_ioheader_unchecked`. 267 | pub fn root_as_ioheader_with_opts<'b, 'o>( 268 | opts: &'o flatbuffers::VerifierOptions, 269 | buf: &'b [u8], 270 | ) -> Result, flatbuffers::InvalidFlatbuffer> { 271 | flatbuffers::root_with_opts::>(opts, buf) 272 | } 273 | #[inline] 274 | /// Verifies, with the given verifier options, that a buffer of 275 | /// bytes contains a size prefixed `Ioheader` and returns 276 | /// it. Note that verification is still experimental and may not 277 | /// catch every error, or be maximally performant. For the 278 | /// previous, unchecked, behavior use 279 | /// `root_as_ioheader_unchecked`. 280 | pub fn size_prefixed_root_as_ioheader_with_opts<'b, 'o>( 281 | opts: &'o flatbuffers::VerifierOptions, 282 | buf: &'b [u8], 283 | ) -> Result, flatbuffers::InvalidFlatbuffer> { 284 | flatbuffers::size_prefixed_root_with_opts::>(opts, buf) 285 | } 286 | #[inline] 287 | /// Assumes, without verification, that a buffer of bytes contains a Ioheader and returns it. 288 | /// # Safety 289 | /// Callers must trust the given bytes do indeed contain a valid `Ioheader`. 290 | pub unsafe fn root_as_ioheader_unchecked(buf: &[u8]) -> Ioheader { 291 | flatbuffers::root_unchecked::(buf) 292 | } 293 | #[inline] 294 | /// Assumes, without verification, that a buffer of bytes contains a size prefixed Ioheader and returns it. 295 | /// # Safety 296 | /// Callers must trust the given bytes do indeed contain a valid size prefixed `Ioheader`. 297 | pub unsafe fn size_prefixed_root_as_ioheader_unchecked(buf: &[u8]) -> Ioheader { 298 | flatbuffers::size_prefixed_root_unchecked::(buf) 299 | } 300 | pub const IOHEADER_IDENTIFIER: &str = "IOHE"; 301 | 302 | #[inline] 303 | pub fn ioheader_buffer_has_identifier(buf: &[u8]) -> bool { 304 | flatbuffers::buffer_has_identifier(buf, IOHEADER_IDENTIFIER, false) 305 | } 306 | 307 | #[inline] 308 | pub fn ioheader_size_prefixed_buffer_has_identifier(buf: &[u8]) -> bool { 309 | flatbuffers::buffer_has_identifier(buf, IOHEADER_IDENTIFIER, true) 310 | } 311 | 312 | #[inline] 313 | pub fn finish_ioheader_buffer<'a, 'b, A: flatbuffers::Allocator + 'a>( 314 | fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, 315 | root: flatbuffers::WIPOffset>) { 316 | fbb.finish(root, Some(IOHEADER_IDENTIFIER)); 317 | } 318 | 319 | #[inline] 320 | pub fn finish_size_prefixed_ioheader_buffer<'a, 'b, A: flatbuffers::Allocator + 'a>(fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, root: flatbuffers::WIPOffset>) { 321 | fbb.finish_size_prefixed(root, Some(IOHEADER_IDENTIFIER)); 322 | } 323 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | mod aedat_core; 2 | 3 | use numpy::convert::ToPyArray; 4 | use numpy::ndarray::IntoDimension; 5 | use numpy::prelude::*; 6 | use numpy::Element; 7 | use pyo3::prelude::*; 8 | 9 | impl std::convert::From for pyo3::PyErr { 10 | fn from(error: aedat_core::ParseError) -> Self { 11 | pyo3::PyErr::new::(error.to_string()) 12 | } 13 | } 14 | 15 | #[pyclass] 16 | struct Decoder { 17 | decoder: aedat_core::Decoder, 18 | } 19 | 20 | #[pymethods] 21 | impl Decoder { 22 | #[new] 23 | fn new(path: &pyo3::Bound<'_, pyo3::types::PyAny>) -> Result { 24 | match python_path_to_string(path) { 25 | Ok(result) => match aedat_core::Decoder::new(result) { 26 | Ok(result) => Ok(Decoder { decoder: result }), 27 | Err(error) => Err(pyo3::PyErr::from(error)), 28 | }, 29 | Err(error) => Err(error), 30 | } 31 | } 32 | 33 | fn id_to_stream(&self, python: pyo3::prelude::Python) -> PyResult { 34 | let python_id_to_stream = pyo3::types::PyDict::new(python); 35 | for (id, stream) in self.decoder.id_to_stream.iter() { 36 | let python_stream = pyo3::types::PyDict::new(python); 37 | match stream.content { 38 | aedat_core::StreamContent::Events => { 39 | python_stream.set_item("type", "events")?; 40 | python_stream.set_item("width", stream.width)?; 41 | python_stream.set_item("height", stream.height)?; 42 | } 43 | aedat_core::StreamContent::Frame => { 44 | python_stream.set_item("type", "frame")?; 45 | python_stream.set_item("width", stream.width)?; 46 | python_stream.set_item("height", stream.height)?; 47 | } 48 | aedat_core::StreamContent::Imus => python_stream.set_item("type", "imus")?, 49 | aedat_core::StreamContent::Triggers => { 50 | python_stream.set_item("type", "triggers")? 51 | } 52 | } 53 | python_id_to_stream.set_item(id, python_stream)?; 54 | } 55 | Ok(python_id_to_stream.into()) 56 | } 57 | 58 | fn __iter__(shell: pyo3::PyRefMut) -> PyResult> { 59 | Ok(shell.into()) 60 | } 61 | 62 | fn __next__(mut shell: pyo3::PyRefMut) -> PyResult> { 63 | let packet = match shell.decoder.next() { 64 | Some(result) => match result { 65 | Ok(result) => result, 66 | Err(result) => return Err(pyo3::PyErr::from(result)), 67 | }, 68 | None => return Ok(None), 69 | }; 70 | pyo3::Python::with_gil(|python| -> PyResult> { 71 | let python_packet = pyo3::types::PyDict::new(python); 72 | python_packet.set_item("stream_id", packet.stream_id)?; 73 | match shell 74 | .decoder 75 | .id_to_stream 76 | .get(&packet.stream_id) 77 | .unwrap() 78 | .content 79 | { 80 | aedat_core::StreamContent::Events => { 81 | let events = 82 | match aedat_core::events_generated::size_prefixed_root_as_event_packet( 83 | &packet.buffer, 84 | ) { 85 | Ok(result) => match result.elements() { 86 | Some(result) => result, 87 | None => { 88 | return Err(pyo3::PyErr::from(aedat_core::ParseError::new( 89 | "empty events packet", 90 | ))) 91 | } 92 | }, 93 | Err(_) => { 94 | return Err(pyo3::PyErr::from(aedat_core::ParseError::new( 95 | "the packet does not have a size prefix", 96 | ))) 97 | } 98 | }; 99 | let mut length = events.len() as numpy::npyffi::npy_intp; 100 | python_packet.set_item("events", unsafe { 101 | let dtype_as_list = pyo3::ffi::PyList_New(4_isize); 102 | set_dtype_as_list_field( 103 | python, 104 | dtype_as_list, 105 | 0, 106 | "t", 107 | None, 108 | u64::get_dtype(python).num(), 109 | ); 110 | set_dtype_as_list_field( 111 | python, 112 | dtype_as_list, 113 | 1, 114 | "x", 115 | None, 116 | u16::get_dtype(python).num(), 117 | ); 118 | set_dtype_as_list_field( 119 | python, 120 | dtype_as_list, 121 | 2, 122 | "y", 123 | None, 124 | u16::get_dtype(python).num(), 125 | ); 126 | set_dtype_as_list_field( 127 | python, 128 | dtype_as_list, 129 | 3, 130 | "on", 131 | Some("p"), 132 | bool::get_dtype(python).num(), 133 | ); 134 | let mut dtype: *mut numpy::npyffi::PyArray_Descr = std::ptr::null_mut(); 135 | if numpy::PY_ARRAY_API.PyArray_DescrConverter( 136 | python, 137 | dtype_as_list, 138 | &mut dtype, 139 | ) < 0 140 | { 141 | panic!("PyArray_DescrConverter failed"); 142 | } 143 | let array = numpy::PY_ARRAY_API.PyArray_NewFromDescr( 144 | python, 145 | numpy::PY_ARRAY_API.get_type_object( 146 | python, 147 | numpy::npyffi::array::NpyTypes::PyArray_Type, 148 | ), 149 | dtype, 150 | 1_i32, 151 | &mut length as *mut numpy::npyffi::npy_intp, 152 | std::ptr::null_mut(), 153 | std::ptr::null_mut(), 154 | 0_i32, 155 | std::ptr::null_mut(), 156 | ); 157 | for mut index in 0_isize..length { 158 | let event_cell = numpy::PY_ARRAY_API.PyArray_GetPtr( 159 | python, 160 | array as *mut numpy::npyffi::PyArrayObject, 161 | &mut index as *mut numpy::npyffi::npy_intp, 162 | ) as *mut u8; 163 | let event = events.get(index as usize); 164 | let mut event_array = [0u8; 13]; 165 | event_array[0..8].copy_from_slice(&(event.t() as u64).to_ne_bytes()); 166 | event_array[8..10].copy_from_slice(&(event.x() as u16).to_ne_bytes()); 167 | event_array[10..12].copy_from_slice(&(event.y() as u16).to_ne_bytes()); 168 | event_array[12] = if event.on() { 1 } else { 0 }; 169 | std::ptr::copy(event_array.as_ptr(), event_cell, event_array.len()); 170 | } 171 | PyObject::from_owned_ptr(python, array) 172 | })?; 173 | } 174 | aedat_core::StreamContent::Frame => { 175 | let frame = match aedat_core::frame_generated::size_prefixed_root_as_frame( 176 | &packet.buffer, 177 | ) { 178 | Ok(result) => result, 179 | Err(_) => { 180 | return Err(pyo3::PyErr::from(aedat_core::ParseError::new( 181 | "the packet does not have a size prefix", 182 | ))) 183 | } 184 | }; 185 | let python_frame = pyo3::types::PyDict::new(python); 186 | python_frame.set_item("t", frame.t())?; 187 | python_frame.set_item("begin_t", frame.begin_t())?; 188 | python_frame.set_item("end_t", frame.end_t())?; 189 | python_frame.set_item("exposure_begin_t", frame.exposure_begin_t())?; 190 | python_frame.set_item("exposure_end_t", frame.exposure_end_t())?; 191 | python_frame.set_item( 192 | "format", 193 | match frame.format() { 194 | aedat_core::frame_generated::FrameFormat::Gray => "L", 195 | aedat_core::frame_generated::FrameFormat::Bgr => "RGB", 196 | aedat_core::frame_generated::FrameFormat::Bgra => "RGBA", 197 | _ => { 198 | return Err(pyo3::PyErr::from(aedat_core::ParseError::new( 199 | "unknown frame format", 200 | ))) 201 | } 202 | }, 203 | )?; 204 | python_frame.set_item("width", frame.width())?; 205 | python_frame.set_item("height", frame.height())?; 206 | python_frame.set_item("offset_x", frame.offset_x())?; 207 | python_frame.set_item("offset_y", frame.offset_y())?; 208 | match frame.format() { 209 | aedat_core::frame_generated::FrameFormat::Gray => { 210 | let dimensions = 211 | [frame.height() as usize, frame.width() as usize].into_dimension(); 212 | python_frame.set_item( 213 | "pixels", 214 | match frame.pixels() { 215 | Some(result) => { 216 | result.bytes().to_pyarray(python).reshape(dimensions)? 217 | } 218 | None => numpy::array::PyArray2::::zeros( 219 | python, dimensions, false, 220 | ), 221 | }, 222 | )?; 223 | } 224 | aedat_core::frame_generated::FrameFormat::Bgr 225 | | aedat_core::frame_generated::FrameFormat::Bgra => { 226 | let channels = if frame.format() 227 | == aedat_core::frame_generated::FrameFormat::Bgr 228 | { 229 | 3_usize 230 | } else { 231 | 4_usize 232 | }; 233 | let dimensions = 234 | [frame.height() as usize, frame.width() as usize, channels] 235 | .into_dimension(); 236 | python_frame.set_item( 237 | "pixels", 238 | match frame.pixels() { 239 | Some(result) => { 240 | let mut pixels = result.bytes().to_owned(); 241 | for index in 0..(pixels.len() / channels) { 242 | pixels.swap(index * channels, index * channels + 2); 243 | } 244 | pixels.to_pyarray(python).reshape(dimensions)? 245 | } 246 | None => numpy::array::PyArray3::::zeros( 247 | python, dimensions, false, 248 | ), 249 | }, 250 | )?; 251 | } 252 | _ => { 253 | return Err(pyo3::PyErr::from(aedat_core::ParseError::new( 254 | "unknown frame format", 255 | ))) 256 | } 257 | } 258 | python_packet.set_item("frame", python_frame)?; 259 | } 260 | aedat_core::StreamContent::Imus => { 261 | let imus = match aedat_core::imus_generated::size_prefixed_root_as_imu_packet( 262 | &packet.buffer, 263 | ) { 264 | Ok(result) => match result.elements() { 265 | Some(result) => result, 266 | None => { 267 | return Err(pyo3::PyErr::from(aedat_core::ParseError::new( 268 | "empty events packet", 269 | ))) 270 | } 271 | }, 272 | Err(_) => { 273 | return Err(pyo3::PyErr::from(aedat_core::ParseError::new( 274 | "the packet does not have a size prefix", 275 | ))) 276 | } 277 | }; 278 | let mut length = imus.len() as numpy::npyffi::npy_intp; 279 | python_packet.set_item("imus", unsafe { 280 | let dtype_as_list = pyo3::ffi::PyList_New(11_isize); 281 | set_dtype_as_list_field( 282 | python, 283 | dtype_as_list, 284 | 0, 285 | "t", 286 | None, 287 | u64::get_dtype(python).num(), 288 | ); 289 | set_dtype_as_list_field( 290 | python, 291 | dtype_as_list, 292 | 1, 293 | "temperature", 294 | None, 295 | f32::get_dtype(python).num(), 296 | ); 297 | set_dtype_as_list_field( 298 | python, 299 | dtype_as_list, 300 | 2, 301 | "accelerometer_x", 302 | None, 303 | f32::get_dtype(python).num(), 304 | ); 305 | set_dtype_as_list_field( 306 | python, 307 | dtype_as_list, 308 | 3, 309 | "accelerometer_y", 310 | None, 311 | f32::get_dtype(python).num(), 312 | ); 313 | set_dtype_as_list_field( 314 | python, 315 | dtype_as_list, 316 | 4, 317 | "accelerometer_z", 318 | None, 319 | f32::get_dtype(python).num(), 320 | ); 321 | set_dtype_as_list_field( 322 | python, 323 | dtype_as_list, 324 | 5, 325 | "gyroscope_x", 326 | None, 327 | f32::get_dtype(python).num(), 328 | ); 329 | set_dtype_as_list_field( 330 | python, 331 | dtype_as_list, 332 | 6, 333 | "gyroscope_y", 334 | None, 335 | f32::get_dtype(python).num(), 336 | ); 337 | set_dtype_as_list_field( 338 | python, 339 | dtype_as_list, 340 | 7, 341 | "gyroscope_z", 342 | None, 343 | f32::get_dtype(python).num(), 344 | ); 345 | set_dtype_as_list_field( 346 | python, 347 | dtype_as_list, 348 | 8, 349 | "magnetometer_x", 350 | None, 351 | f32::get_dtype(python).num(), 352 | ); 353 | set_dtype_as_list_field( 354 | python, 355 | dtype_as_list, 356 | 9, 357 | "magnetometer_y", 358 | None, 359 | f32::get_dtype(python).num(), 360 | ); 361 | set_dtype_as_list_field( 362 | python, 363 | dtype_as_list, 364 | 10, 365 | "magnetometer_z", 366 | None, 367 | f32::get_dtype(python).num(), 368 | ); 369 | let mut dtype: *mut numpy::npyffi::PyArray_Descr = std::ptr::null_mut(); 370 | if numpy::PY_ARRAY_API.PyArray_DescrConverter( 371 | python, 372 | dtype_as_list, 373 | &mut dtype, 374 | ) < 0 375 | { 376 | panic!("PyArray_DescrConverter failed"); 377 | } 378 | let array = numpy::PY_ARRAY_API.PyArray_NewFromDescr( 379 | python, 380 | numpy::PY_ARRAY_API.get_type_object( 381 | python, 382 | numpy::npyffi::array::NpyTypes::PyArray_Type, 383 | ), 384 | dtype, 385 | 1_i32, 386 | &mut length as *mut numpy::npyffi::npy_intp, 387 | std::ptr::null_mut(), 388 | std::ptr::null_mut(), 389 | 0_i32, 390 | std::ptr::null_mut(), 391 | ); 392 | let mut index = 0_isize; 393 | for imu in imus { 394 | let imu_cell = numpy::PY_ARRAY_API.PyArray_GetPtr( 395 | python, 396 | array as *mut numpy::npyffi::PyArrayObject, 397 | &mut index as *mut numpy::npyffi::npy_intp, 398 | ) as *mut u8; 399 | let mut imu_array = [0u8; 48]; 400 | imu_array[0..8].copy_from_slice(&(imu.t() as u64).to_ne_bytes()); 401 | imu_array[8..12].copy_from_slice(&(imu.temperature()).to_ne_bytes()); 402 | imu_array[12..16] 403 | .copy_from_slice(&(imu.accelerometer_x()).to_ne_bytes()); 404 | imu_array[16..20] 405 | .copy_from_slice(&(imu.accelerometer_y()).to_ne_bytes()); 406 | imu_array[20..24] 407 | .copy_from_slice(&(imu.accelerometer_z()).to_ne_bytes()); 408 | imu_array[24..28].copy_from_slice(&(imu.gyroscope_x()).to_ne_bytes()); 409 | imu_array[28..32].copy_from_slice(&(imu.gyroscope_y()).to_ne_bytes()); 410 | imu_array[32..36].copy_from_slice(&(imu.gyroscope_z()).to_ne_bytes()); 411 | imu_array[36..40] 412 | .copy_from_slice(&(imu.magnetometer_x()).to_ne_bytes()); 413 | imu_array[40..44] 414 | .copy_from_slice(&(imu.magnetometer_y()).to_ne_bytes()); 415 | imu_array[44..48] 416 | .copy_from_slice(&(imu.magnetometer_z()).to_ne_bytes()); 417 | std::ptr::copy(imu_array.as_ptr(), imu_cell, imu_array.len()); 418 | index += 1_isize; 419 | } 420 | PyObject::from_owned_ptr(python, array) 421 | })?; 422 | } 423 | aedat_core::StreamContent::Triggers => { 424 | let triggers = 425 | match aedat_core::triggers_generated::size_prefixed_root_as_trigger_packet( 426 | &packet.buffer, 427 | ) { 428 | Ok(result) => match result.elements() { 429 | Some(result) => result, 430 | None => { 431 | return Err(pyo3::PyErr::from(aedat_core::ParseError::new( 432 | "empty events packet", 433 | ))) 434 | } 435 | }, 436 | Err(_) => { 437 | return Err(pyo3::PyErr::from(aedat_core::ParseError::new( 438 | "the packet does not have a size prefix", 439 | ))) 440 | } 441 | }; 442 | let mut length = triggers.len() as numpy::npyffi::npy_intp; 443 | python_packet.set_item("triggers", unsafe { 444 | let dtype_as_list = pyo3::ffi::PyList_New(2_isize); 445 | set_dtype_as_list_field( 446 | python, 447 | dtype_as_list, 448 | 0, 449 | "t", 450 | None, 451 | u64::get_dtype(python).num(), 452 | ); 453 | set_dtype_as_list_field( 454 | python, 455 | dtype_as_list, 456 | 1, 457 | "source", 458 | None, 459 | u8::get_dtype(python).num(), 460 | ); 461 | let mut dtype: *mut numpy::npyffi::PyArray_Descr = std::ptr::null_mut(); 462 | if numpy::PY_ARRAY_API.PyArray_DescrConverter( 463 | python, 464 | dtype_as_list, 465 | &mut dtype, 466 | ) < 0 467 | { 468 | panic!("PyArray_DescrConverter failed"); 469 | } 470 | let array = numpy::PY_ARRAY_API.PyArray_NewFromDescr( 471 | python, 472 | numpy::PY_ARRAY_API.get_type_object( 473 | python, 474 | numpy::npyffi::array::NpyTypes::PyArray_Type, 475 | ), 476 | dtype, 477 | 1_i32, 478 | &mut length as *mut numpy::npyffi::npy_intp, 479 | std::ptr::null_mut(), 480 | std::ptr::null_mut(), 481 | 0_i32, 482 | std::ptr::null_mut(), 483 | ); 484 | let mut index = 0_isize; 485 | for trigger in triggers { 486 | let trigger_cell = numpy::PY_ARRAY_API.PyArray_GetPtr( 487 | python, 488 | array as *mut numpy::npyffi::PyArrayObject, 489 | &mut index as *mut numpy::npyffi::npy_intp, 490 | ) as *mut u8; 491 | 492 | let mut trigger_array = [0u8; 9]; 493 | trigger_array[0..8] 494 | .copy_from_slice(&(trigger.t() as u64).to_ne_bytes()); 495 | use aedat_core::triggers_generated::TriggerSource; 496 | trigger_array[8] = match trigger.source() { 497 | TriggerSource::TimestampReset => 0_u8, 498 | TriggerSource::ExternalSignalRisingEdge => 1_u8, 499 | TriggerSource::ExternalSignalFallingEdge => 2_u8, 500 | TriggerSource::ExternalSignalPulse => 3_u8, 501 | TriggerSource::ExternalGeneratorRisingEdge => 4_u8, 502 | TriggerSource::ExternalGeneratorFallingEdge => 5_u8, 503 | TriggerSource::FrameBegin => 6_u8, 504 | TriggerSource::FrameEnd => 7_u8, 505 | TriggerSource::ExposureBegin => 8_u8, 506 | TriggerSource::ExposureEnd => 9_u8, 507 | _ => { 508 | return Err(pyo3::PyErr::from(aedat_core::ParseError::new( 509 | "unknown trigger source", 510 | ))) 511 | } 512 | }; 513 | std::ptr::copy( 514 | trigger_array.as_ptr(), 515 | trigger_cell, 516 | trigger_array.len(), 517 | ); 518 | index += 1_isize; 519 | } 520 | PyObject::from_owned_ptr(python, array) 521 | })?; 522 | } 523 | } 524 | Ok(Some(python_packet.into())) 525 | }) 526 | } 527 | } 528 | 529 | unsafe fn set_dtype_as_list_field( 530 | python: pyo3::Python, 531 | list: *mut pyo3::ffi::PyObject, 532 | index: i32, 533 | name: &str, 534 | title: Option<&str>, 535 | numpy_type: core::ffi::c_int, 536 | ) { 537 | let tuple = pyo3::ffi::PyTuple_New(2); 538 | if pyo3::ffi::PyTuple_SetItem( 539 | tuple, 540 | 0 as pyo3::ffi::Py_ssize_t, 541 | match title { 542 | Some(title) => { 543 | let tuple = pyo3::ffi::PyTuple_New(2); 544 | if pyo3::ffi::PyTuple_SetItem( 545 | tuple, 546 | 0 as pyo3::ffi::Py_ssize_t, 547 | pyo3::ffi::PyUnicode_FromStringAndSize( 548 | name.as_ptr() as *const core::ffi::c_char, 549 | name.len() as pyo3::ffi::Py_ssize_t, 550 | ), 551 | ) < 0 552 | { 553 | panic!("PyTuple_SetItem 0 failed"); 554 | } 555 | if pyo3::ffi::PyTuple_SetItem( 556 | tuple, 557 | 1 as pyo3::ffi::Py_ssize_t, 558 | pyo3::ffi::PyUnicode_FromStringAndSize( 559 | title.as_ptr() as *const core::ffi::c_char, 560 | title.len() as pyo3::ffi::Py_ssize_t, 561 | ), 562 | ) < 0 563 | { 564 | panic!("PyTuple_SetItem 1 failed"); 565 | } 566 | tuple 567 | } 568 | None => pyo3::ffi::PyUnicode_FromStringAndSize( 569 | name.as_ptr() as *const core::ffi::c_char, 570 | name.len() as pyo3::ffi::Py_ssize_t, 571 | ), 572 | }, 573 | ) < 0 574 | { 575 | panic!("PyTuple_SetItem 0 failed"); 576 | } 577 | if pyo3::ffi::PyTuple_SetItem( 578 | tuple, 579 | 1 as pyo3::ffi::Py_ssize_t, 580 | numpy::PY_ARRAY_API.PyArray_TypeObjectFromType(python, numpy_type), 581 | ) < 0 582 | { 583 | panic!("PyTuple_SetItem 1 failed"); 584 | } 585 | if pyo3::ffi::PyList_SetItem(list, index as pyo3::ffi::Py_ssize_t, tuple) < 0 { 586 | panic!("PyList_SetItem failed"); 587 | } 588 | } 589 | 590 | fn python_path_to_string(path: &pyo3::Bound<'_, pyo3::types::PyAny>) -> PyResult { 591 | if let Ok(result) = path.downcast::() { 592 | return Ok(result.to_string()); 593 | } 594 | if let Ok(result) = path.downcast::() { 595 | return Ok(result.to_string()); 596 | } 597 | let fspath_result = path.call_method0("__fspath__")?; 598 | { 599 | let fspath_as_string: Result< 600 | &pyo3::Bound<'_, pyo3::types::PyString>, 601 | pyo3::DowncastError<'_, '_>, 602 | > = fspath_result.downcast(); 603 | if let Ok(result) = fspath_as_string { 604 | return Ok(result.to_string()); 605 | } 606 | } 607 | let fspath_as_bytes: &pyo3::Bound<'_, pyo3::types::PyBytes> = fspath_result 608 | .downcast() 609 | .map_err(|__fspath__| pyo3::exceptions::PyTypeError::new_err("path must be a string, bytes, or an object with an __fspath__ method (such as pathlib.Path"))?; 610 | Ok(fspath_as_bytes.to_string()) 611 | } 612 | 613 | #[pymodule] 614 | fn aedat( 615 | _python: pyo3::prelude::Python, 616 | module: &pyo3::Bound<'_, pyo3::types::PyModule>, 617 | ) -> PyResult<()> { 618 | module.add_class::()?; 619 | Ok(()) 620 | } 621 | -------------------------------------------------------------------------------- /src/triggers_generated.rs: -------------------------------------------------------------------------------- 1 | // automatically generated by the FlatBuffers compiler, do not modify 2 | 3 | 4 | // @generated 5 | 6 | use core::mem; 7 | use core::cmp::Ordering; 8 | 9 | extern crate flatbuffers; 10 | use self::flatbuffers::{EndianScalar, Follow}; 11 | 12 | #[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] 13 | pub const ENUM_MIN_TRIGGER_SOURCE: i8 = 0; 14 | #[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] 15 | pub const ENUM_MAX_TRIGGER_SOURCE: i8 = 9; 16 | #[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] 17 | #[allow(non_camel_case_types)] 18 | pub const ENUM_VALUES_TRIGGER_SOURCE: [TriggerSource; 10] = [ 19 | TriggerSource::TimestampReset, 20 | TriggerSource::ExternalSignalRisingEdge, 21 | TriggerSource::ExternalSignalFallingEdge, 22 | TriggerSource::ExternalSignalPulse, 23 | TriggerSource::ExternalGeneratorRisingEdge, 24 | TriggerSource::ExternalGeneratorFallingEdge, 25 | TriggerSource::FrameBegin, 26 | TriggerSource::FrameEnd, 27 | TriggerSource::ExposureBegin, 28 | TriggerSource::ExposureEnd, 29 | ]; 30 | 31 | #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] 32 | #[repr(transparent)] 33 | pub struct TriggerSource(pub i8); 34 | #[allow(non_upper_case_globals)] 35 | impl TriggerSource { 36 | pub const TimestampReset: Self = Self(0); 37 | pub const ExternalSignalRisingEdge: Self = Self(1); 38 | pub const ExternalSignalFallingEdge: Self = Self(2); 39 | pub const ExternalSignalPulse: Self = Self(3); 40 | pub const ExternalGeneratorRisingEdge: Self = Self(4); 41 | pub const ExternalGeneratorFallingEdge: Self = Self(5); 42 | pub const FrameBegin: Self = Self(6); 43 | pub const FrameEnd: Self = Self(7); 44 | pub const ExposureBegin: Self = Self(8); 45 | pub const ExposureEnd: Self = Self(9); 46 | 47 | pub const ENUM_MIN: i8 = 0; 48 | pub const ENUM_MAX: i8 = 9; 49 | pub const ENUM_VALUES: &'static [Self] = &[ 50 | Self::TimestampReset, 51 | Self::ExternalSignalRisingEdge, 52 | Self::ExternalSignalFallingEdge, 53 | Self::ExternalSignalPulse, 54 | Self::ExternalGeneratorRisingEdge, 55 | Self::ExternalGeneratorFallingEdge, 56 | Self::FrameBegin, 57 | Self::FrameEnd, 58 | Self::ExposureBegin, 59 | Self::ExposureEnd, 60 | ]; 61 | /// Returns the variant's name or "" if unknown. 62 | pub fn variant_name(self) -> Option<&'static str> { 63 | match self { 64 | Self::TimestampReset => Some("TimestampReset"), 65 | Self::ExternalSignalRisingEdge => Some("ExternalSignalRisingEdge"), 66 | Self::ExternalSignalFallingEdge => Some("ExternalSignalFallingEdge"), 67 | Self::ExternalSignalPulse => Some("ExternalSignalPulse"), 68 | Self::ExternalGeneratorRisingEdge => Some("ExternalGeneratorRisingEdge"), 69 | Self::ExternalGeneratorFallingEdge => Some("ExternalGeneratorFallingEdge"), 70 | Self::FrameBegin => Some("FrameBegin"), 71 | Self::FrameEnd => Some("FrameEnd"), 72 | Self::ExposureBegin => Some("ExposureBegin"), 73 | Self::ExposureEnd => Some("ExposureEnd"), 74 | _ => None, 75 | } 76 | } 77 | } 78 | impl core::fmt::Debug for TriggerSource { 79 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { 80 | if let Some(name) = self.variant_name() { 81 | f.write_str(name) 82 | } else { 83 | f.write_fmt(format_args!("", self.0)) 84 | } 85 | } 86 | } 87 | impl<'a> flatbuffers::Follow<'a> for TriggerSource { 88 | type Inner = Self; 89 | #[inline] 90 | unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { 91 | let b = flatbuffers::read_scalar_at::(buf, loc); 92 | Self(b) 93 | } 94 | } 95 | 96 | impl flatbuffers::Push for TriggerSource { 97 | type Output = TriggerSource; 98 | #[inline] 99 | unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { 100 | flatbuffers::emplace_scalar::(dst, self.0); 101 | } 102 | } 103 | 104 | impl flatbuffers::EndianScalar for TriggerSource { 105 | type Scalar = i8; 106 | #[inline] 107 | fn to_little_endian(self) -> i8 { 108 | self.0.to_le() 109 | } 110 | #[inline] 111 | #[allow(clippy::wrong_self_convention)] 112 | fn from_little_endian(v: i8) -> Self { 113 | let b = i8::from_le(v); 114 | Self(b) 115 | } 116 | } 117 | 118 | impl<'a> flatbuffers::Verifiable for TriggerSource { 119 | #[inline] 120 | fn run_verifier( 121 | v: &mut flatbuffers::Verifier, pos: usize 122 | ) -> Result<(), flatbuffers::InvalidFlatbuffer> { 123 | use self::flatbuffers::Verifiable; 124 | i8::run_verifier(v, pos) 125 | } 126 | } 127 | 128 | impl flatbuffers::SimpleToVerifyInSlice for TriggerSource {} 129 | pub enum TriggerOffset {} 130 | #[derive(Copy, Clone, PartialEq)] 131 | 132 | pub struct Trigger<'a> { 133 | pub _tab: flatbuffers::Table<'a>, 134 | } 135 | 136 | impl<'a> flatbuffers::Follow<'a> for Trigger<'a> { 137 | type Inner = Trigger<'a>; 138 | #[inline] 139 | unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { 140 | Self { _tab: flatbuffers::Table::new(buf, loc) } 141 | } 142 | } 143 | 144 | impl<'a> Trigger<'a> { 145 | pub const VT_T: flatbuffers::VOffsetT = 4; 146 | pub const VT_SOURCE: flatbuffers::VOffsetT = 6; 147 | 148 | #[inline] 149 | pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { 150 | Trigger { _tab: table } 151 | } 152 | #[allow(unused_mut)] 153 | pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: flatbuffers::Allocator + 'bldr>( 154 | _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr, A>, 155 | args: &'args TriggerArgs 156 | ) -> flatbuffers::WIPOffset> { 157 | let mut builder = TriggerBuilder::new(_fbb); 158 | builder.add_t(args.t); 159 | builder.add_source(args.source); 160 | builder.finish() 161 | } 162 | 163 | 164 | #[inline] 165 | pub fn t(&self) -> i64 { 166 | // Safety: 167 | // Created from valid Table for this object 168 | // which contains a valid value in this slot 169 | unsafe { self._tab.get::(Trigger::VT_T, Some(0)).unwrap()} 170 | } 171 | #[inline] 172 | pub fn source(&self) -> TriggerSource { 173 | // Safety: 174 | // Created from valid Table for this object 175 | // which contains a valid value in this slot 176 | unsafe { self._tab.get::(Trigger::VT_SOURCE, Some(TriggerSource::TimestampReset)).unwrap()} 177 | } 178 | } 179 | 180 | impl flatbuffers::Verifiable for Trigger<'_> { 181 | #[inline] 182 | fn run_verifier( 183 | v: &mut flatbuffers::Verifier, pos: usize 184 | ) -> Result<(), flatbuffers::InvalidFlatbuffer> { 185 | use self::flatbuffers::Verifiable; 186 | v.visit_table(pos)? 187 | .visit_field::("t", Self::VT_T, false)? 188 | .visit_field::("source", Self::VT_SOURCE, false)? 189 | .finish(); 190 | Ok(()) 191 | } 192 | } 193 | pub struct TriggerArgs { 194 | pub t: i64, 195 | pub source: TriggerSource, 196 | } 197 | impl<'a> Default for TriggerArgs { 198 | #[inline] 199 | fn default() -> Self { 200 | TriggerArgs { 201 | t: 0, 202 | source: TriggerSource::TimestampReset, 203 | } 204 | } 205 | } 206 | 207 | pub struct TriggerBuilder<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> { 208 | fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, 209 | start_: flatbuffers::WIPOffset, 210 | } 211 | impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> TriggerBuilder<'a, 'b, A> { 212 | #[inline] 213 | pub fn add_t(&mut self, t: i64) { 214 | self.fbb_.push_slot::(Trigger::VT_T, t, 0); 215 | } 216 | #[inline] 217 | pub fn add_source(&mut self, source: TriggerSource) { 218 | self.fbb_.push_slot::(Trigger::VT_SOURCE, source, TriggerSource::TimestampReset); 219 | } 220 | #[inline] 221 | pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>) -> TriggerBuilder<'a, 'b, A> { 222 | let start = _fbb.start_table(); 223 | TriggerBuilder { 224 | fbb_: _fbb, 225 | start_: start, 226 | } 227 | } 228 | #[inline] 229 | pub fn finish(self) -> flatbuffers::WIPOffset> { 230 | let o = self.fbb_.end_table(self.start_); 231 | flatbuffers::WIPOffset::new(o.value()) 232 | } 233 | } 234 | 235 | impl core::fmt::Debug for Trigger<'_> { 236 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 237 | let mut ds = f.debug_struct("Trigger"); 238 | ds.field("t", &self.t()); 239 | ds.field("source", &self.source()); 240 | ds.finish() 241 | } 242 | } 243 | pub enum TriggerPacketOffset {} 244 | #[derive(Copy, Clone, PartialEq)] 245 | 246 | pub struct TriggerPacket<'a> { 247 | pub _tab: flatbuffers::Table<'a>, 248 | } 249 | 250 | impl<'a> flatbuffers::Follow<'a> for TriggerPacket<'a> { 251 | type Inner = TriggerPacket<'a>; 252 | #[inline] 253 | unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { 254 | Self { _tab: flatbuffers::Table::new(buf, loc) } 255 | } 256 | } 257 | 258 | impl<'a> TriggerPacket<'a> { 259 | pub const VT_ELEMENTS: flatbuffers::VOffsetT = 4; 260 | 261 | #[inline] 262 | pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { 263 | TriggerPacket { _tab: table } 264 | } 265 | #[allow(unused_mut)] 266 | pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: flatbuffers::Allocator + 'bldr>( 267 | _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr, A>, 268 | args: &'args TriggerPacketArgs<'args> 269 | ) -> flatbuffers::WIPOffset> { 270 | let mut builder = TriggerPacketBuilder::new(_fbb); 271 | if let Some(x) = args.elements { builder.add_elements(x); } 272 | builder.finish() 273 | } 274 | 275 | 276 | #[inline] 277 | pub fn elements(&self) -> Option>>> { 278 | // Safety: 279 | // Created from valid Table for this object 280 | // which contains a valid value in this slot 281 | unsafe { self._tab.get::>>>(TriggerPacket::VT_ELEMENTS, None)} 282 | } 283 | } 284 | 285 | impl flatbuffers::Verifiable for TriggerPacket<'_> { 286 | #[inline] 287 | fn run_verifier( 288 | v: &mut flatbuffers::Verifier, pos: usize 289 | ) -> Result<(), flatbuffers::InvalidFlatbuffer> { 290 | use self::flatbuffers::Verifiable; 291 | v.visit_table(pos)? 292 | .visit_field::>>>("elements", Self::VT_ELEMENTS, false)? 293 | .finish(); 294 | Ok(()) 295 | } 296 | } 297 | pub struct TriggerPacketArgs<'a> { 298 | pub elements: Option>>>>, 299 | } 300 | impl<'a> Default for TriggerPacketArgs<'a> { 301 | #[inline] 302 | fn default() -> Self { 303 | TriggerPacketArgs { 304 | elements: None, 305 | } 306 | } 307 | } 308 | 309 | pub struct TriggerPacketBuilder<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> { 310 | fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, 311 | start_: flatbuffers::WIPOffset, 312 | } 313 | impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> TriggerPacketBuilder<'a, 'b, A> { 314 | #[inline] 315 | pub fn add_elements(&mut self, elements: flatbuffers::WIPOffset>>>) { 316 | self.fbb_.push_slot_always::>(TriggerPacket::VT_ELEMENTS, elements); 317 | } 318 | #[inline] 319 | pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>) -> TriggerPacketBuilder<'a, 'b, A> { 320 | let start = _fbb.start_table(); 321 | TriggerPacketBuilder { 322 | fbb_: _fbb, 323 | start_: start, 324 | } 325 | } 326 | #[inline] 327 | pub fn finish(self) -> flatbuffers::WIPOffset> { 328 | let o = self.fbb_.end_table(self.start_); 329 | flatbuffers::WIPOffset::new(o.value()) 330 | } 331 | } 332 | 333 | impl core::fmt::Debug for TriggerPacket<'_> { 334 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 335 | let mut ds = f.debug_struct("TriggerPacket"); 336 | ds.field("elements", &self.elements()); 337 | ds.finish() 338 | } 339 | } 340 | #[inline] 341 | /// Verifies that a buffer of bytes contains a `TriggerPacket` 342 | /// and returns it. 343 | /// Note that verification is still experimental and may not 344 | /// catch every error, or be maximally performant. For the 345 | /// previous, unchecked, behavior use 346 | /// `root_as_trigger_packet_unchecked`. 347 | pub fn root_as_trigger_packet(buf: &[u8]) -> Result { 348 | flatbuffers::root::(buf) 349 | } 350 | #[inline] 351 | /// Verifies that a buffer of bytes contains a size prefixed 352 | /// `TriggerPacket` and returns it. 353 | /// Note that verification is still experimental and may not 354 | /// catch every error, or be maximally performant. For the 355 | /// previous, unchecked, behavior use 356 | /// `size_prefixed_root_as_trigger_packet_unchecked`. 357 | pub fn size_prefixed_root_as_trigger_packet(buf: &[u8]) -> Result { 358 | flatbuffers::size_prefixed_root::(buf) 359 | } 360 | #[inline] 361 | /// Verifies, with the given options, that a buffer of bytes 362 | /// contains a `TriggerPacket` and returns it. 363 | /// Note that verification is still experimental and may not 364 | /// catch every error, or be maximally performant. For the 365 | /// previous, unchecked, behavior use 366 | /// `root_as_trigger_packet_unchecked`. 367 | pub fn root_as_trigger_packet_with_opts<'b, 'o>( 368 | opts: &'o flatbuffers::VerifierOptions, 369 | buf: &'b [u8], 370 | ) -> Result, flatbuffers::InvalidFlatbuffer> { 371 | flatbuffers::root_with_opts::>(opts, buf) 372 | } 373 | #[inline] 374 | /// Verifies, with the given verifier options, that a buffer of 375 | /// bytes contains a size prefixed `TriggerPacket` and returns 376 | /// it. Note that verification is still experimental and may not 377 | /// catch every error, or be maximally performant. For the 378 | /// previous, unchecked, behavior use 379 | /// `root_as_trigger_packet_unchecked`. 380 | pub fn size_prefixed_root_as_trigger_packet_with_opts<'b, 'o>( 381 | opts: &'o flatbuffers::VerifierOptions, 382 | buf: &'b [u8], 383 | ) -> Result, flatbuffers::InvalidFlatbuffer> { 384 | flatbuffers::size_prefixed_root_with_opts::>(opts, buf) 385 | } 386 | #[inline] 387 | /// Assumes, without verification, that a buffer of bytes contains a TriggerPacket and returns it. 388 | /// # Safety 389 | /// Callers must trust the given bytes do indeed contain a valid `TriggerPacket`. 390 | pub unsafe fn root_as_trigger_packet_unchecked(buf: &[u8]) -> TriggerPacket { 391 | flatbuffers::root_unchecked::(buf) 392 | } 393 | #[inline] 394 | /// Assumes, without verification, that a buffer of bytes contains a size prefixed TriggerPacket and returns it. 395 | /// # Safety 396 | /// Callers must trust the given bytes do indeed contain a valid size prefixed `TriggerPacket`. 397 | pub unsafe fn size_prefixed_root_as_trigger_packet_unchecked(buf: &[u8]) -> TriggerPacket { 398 | flatbuffers::size_prefixed_root_unchecked::(buf) 399 | } 400 | pub const TRIGGER_PACKET_IDENTIFIER: &str = "TRIG"; 401 | 402 | #[inline] 403 | pub fn trigger_packet_buffer_has_identifier(buf: &[u8]) -> bool { 404 | flatbuffers::buffer_has_identifier(buf, TRIGGER_PACKET_IDENTIFIER, false) 405 | } 406 | 407 | #[inline] 408 | pub fn trigger_packet_size_prefixed_buffer_has_identifier(buf: &[u8]) -> bool { 409 | flatbuffers::buffer_has_identifier(buf, TRIGGER_PACKET_IDENTIFIER, true) 410 | } 411 | 412 | #[inline] 413 | pub fn finish_trigger_packet_buffer<'a, 'b, A: flatbuffers::Allocator + 'a>( 414 | fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, 415 | root: flatbuffers::WIPOffset>) { 416 | fbb.finish(root, Some(TRIGGER_PACKET_IDENTIFIER)); 417 | } 418 | 419 | #[inline] 420 | pub fn finish_size_prefixed_trigger_packet_buffer<'a, 'b, A: flatbuffers::Allocator + 'a>(fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, root: flatbuffers::WIPOffset>) { 421 | fbb.finish_size_prefixed(root, Some(TRIGGER_PACKET_IDENTIFIER)); 422 | } 423 | -------------------------------------------------------------------------------- /test.py: -------------------------------------------------------------------------------- 1 | import hashlib 2 | import pathlib 3 | 4 | import aedat 5 | 6 | dirname = pathlib.Path(__file__).resolve().parent 7 | 8 | decoder = aedat.Decoder(dirname / "test_data.aedat4") 9 | 10 | assert len(decoder.id_to_stream().keys()) == 4 11 | assert decoder.id_to_stream()[0]["type"] == "events" 12 | assert decoder.id_to_stream()[0]["width"] == 346 13 | assert decoder.id_to_stream()[0]["height"] == 260 14 | assert decoder.id_to_stream()[1]["type"] == "frame" 15 | assert decoder.id_to_stream()[1]["width"] == 346 16 | assert decoder.id_to_stream()[1]["height"] == 260 17 | assert decoder.id_to_stream()[2]["type"] == "imus" 18 | assert decoder.id_to_stream()[3]["type"] == "triggers" 19 | t_hasher = hashlib.sha3_224() 20 | x_hasher = hashlib.sha3_224() 21 | y_hasher = hashlib.sha3_224() 22 | on_hasher = hashlib.sha3_224() 23 | frame_hasher = hashlib.sha3_224() 24 | imus_hasher = hashlib.sha3_224() 25 | triggers_hasher = hashlib.sha3_224() 26 | for packet in decoder: 27 | if "events" in packet: 28 | events = packet["events"] 29 | t_hasher.update(events["t"].tobytes()) 30 | x_hasher.update(events["x"].tobytes()) 31 | y_hasher.update(events["y"].tobytes()) 32 | on_hasher.update(events["on"].tobytes()) 33 | if "frame" in packet: 34 | frame_hasher.update(packet["frame"]["pixels"].tobytes()) 35 | if "imus" in packet: 36 | imus_hasher.update(packet["imus"].tobytes()) 37 | if "triggers" in packet: 38 | triggers_hasher.update(packet["triggers"].tobytes()) 39 | print(f"{t_hasher.hexdigest()=}") 40 | print(f"{x_hasher.hexdigest()=}") 41 | print(f"{y_hasher.hexdigest()=}") 42 | print(f"{on_hasher.hexdigest()=}") 43 | print(f"{frame_hasher.hexdigest()=}") 44 | print(f"{imus_hasher.hexdigest()=}") 45 | print(f"{triggers_hasher.hexdigest()=}") 46 | -------------------------------------------------------------------------------- /test_data.aedat4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neuromorphicsystems/aedat/6570e5d2a5cbaa304f4a7c95f5e7f2c86133ce76/test_data.aedat4 --------------------------------------------------------------------------------