diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..89491614e --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "libs/basilisk-rs/basilisk"] + path = libs/basilisk-rs/basilisk + url = https://github.com/AVSLab/basilisk.git diff --git a/Cargo.lock b/Cargo.lock index 5690b0c47..e7a466c13 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1016,6 +1016,18 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +[[package]] +name = "basilisk-sys" +version = "0.1.0" +dependencies = [ + "approx", + "bindgen 0.65.1", + "cc", + "paste", + "thingbuf", + "tracing", +] + [[package]] name = "bevy" version = "0.13.2" @@ -1998,6 +2010,29 @@ dependencies = [ "num-traits", ] +[[package]] +name = "bindgen" +version = "0.65.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfdf7b466f9a4903edc73f95d6d2bcd5baf8ae620638762244d3f60143643cc5" +dependencies = [ + "bitflags 1.3.2", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "log", + "peeking_take_while", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn 2.0.60", + "which", +] + [[package]] name = "bindgen" version = "0.66.1" @@ -2365,9 +2400,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.96" +version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "065a29261d53ba54260972629f9ca6bffa69bac13cd1fed61420f7fa68b9f8bd" +checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4" dependencies = [ "jobserver", "libc", @@ -9378,6 +9413,16 @@ version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" +[[package]] +name = "thingbuf" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "662b54ef6f7b4e71f683dadc787bbb2d8e8ef2f91b682ebed3164a5a7abca905" +dependencies = [ + "parking_lot", + "pin-project", +] + [[package]] name = "thiserror" version = "1.0.59" diff --git a/Cargo.toml b/Cargo.toml index 53e093d5c..778c27ab0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,8 @@ members = [ "services/sim-agent", "services/sim-builder", "apps/elodin", - "libs/unreal-plugin" + "libs/unreal-plugin", + "libs/basilisk-rs" ] [workspace.package] diff --git a/libs/basilisk-rs/.gitignore b/libs/basilisk-rs/.gitignore new file mode 100644 index 000000000..6680b260c --- /dev/null +++ b/libs/basilisk-rs/.gitignore @@ -0,0 +1,2 @@ +/target +basilisk/dist3 diff --git a/libs/basilisk-rs/Cargo.lock b/libs/basilisk-rs/Cargo.lock new file mode 100644 index 000000000..ae1658854 --- /dev/null +++ b/libs/basilisk-rs/Cargo.lock @@ -0,0 +1,574 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "basilisk-sys" +version = "0.1.0" +dependencies = [ + "bindgen", + "cc", + "flume", + "paste", + "tracing", +] + +[[package]] +name = "bindgen" +version = "0.65.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfdf7b466f9a4903edc73f95d6d2bcd5baf8ae620638762244d3f60143643cc5" +dependencies = [ + "bitflags 1.3.2", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "log", + "peeking_take_while", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", + "which", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "cc" +version = "1.0.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4" + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clang-sys" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "either" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" + +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "flume" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" +dependencies = [ + "futures-core", + "futures-sink", + "nanorand", + "spin", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "js-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "libc" +version = "0.2.154" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" + +[[package]] +name = "libloading" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" +dependencies = [ + "cfg-if", + "windows-targets", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + +[[package]] +name = "memchr" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "nanorand" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" +dependencies = [ + "getrandom", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "prettyplease" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustix" +version = "0.38.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags 2.5.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "syn" +version = "2.0.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c993ed8ccba56ae856363b1845da7266a7cb78e1d146c8a32d54b45a8b831fc9" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" diff --git a/libs/basilisk-rs/Cargo.toml b/libs/basilisk-rs/Cargo.toml new file mode 100644 index 000000000..107a65a3f --- /dev/null +++ b/libs/basilisk-rs/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "basilisk-sys" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +# queue +thingbuf = "0.1.6" + +# logs +tracing = "0.1.40" + +# macros +paste = "1.0.14" + +[build-dependencies] +bindgen = "0.65.1" +cc = "1.0.97" + + +[dev-dependencies] +approx = "0.5.1" diff --git a/libs/basilisk-rs/README.md b/libs/basilisk-rs/README.md new file mode 100644 index 000000000..8f0236147 --- /dev/null +++ b/libs/basilisk-rs/README.md @@ -0,0 +1,8 @@ +# Basilisk Rust + +basilisk-rs is a series of Rust bindings around [Basilisk](https://github.com/AVSLab/basilisk), a astrodynamics and flight software framework. + +Right we only compile and wrap a small number of flight software modules, at some point in the future we may expand this to other simulations + +## Updating Basilisk +`basilisk-rs` for the most part builds Basilisk itself, but there are some files it can not build on its own. In particular, Basilisk makes heavy use of SWIG. Instead of calling SWIGF from the `build.rs` we vendor those files into the `vendor` directory. When you want to update Basilisk, first update the submodule reference. Then run `just vendor` which will kick-off building Basilisk. You will need `swig4` installed, ideally from nixpkgs as homebrew's version can cause issues. diff --git a/libs/basilisk-rs/basilisk b/libs/basilisk-rs/basilisk new file mode 160000 index 000000000..5a446e037 --- /dev/null +++ b/libs/basilisk-rs/basilisk @@ -0,0 +1 @@ +Subproject commit 5a446e03781bcdcbe0c60f96f38e71f0f2f0ae46 diff --git a/libs/basilisk-rs/build.rs b/libs/basilisk-rs/build.rs new file mode 100644 index 000000000..884fad2ec --- /dev/null +++ b/libs/basilisk-rs/build.rs @@ -0,0 +1,38 @@ +use std::env; +use std::path::PathBuf; + +fn main() { + println!("cargo:rerun-if-changed=src/basilisk.h"); + + let bindings = bindgen::Builder::default() + // The input header we would like to generate + // bindings for. + .header("src/basilisk.h") + .clang_arg("-Ivendor") + .newtype_enum("logLevel_t") + .derive_partialeq(true) + .derive_default(true) + .derive_debug(true) + // Tell cargo to invalidate the built crate whenever any of the + // included header files changed. + .parse_callbacks(Box::new(bindgen::CargoCallbacks)) + // Finish the builder and generate the bindings. + .generate() + // Unwrap the Result and panic on failure. + .expect("Unable to generate bindings"); + + // Write the bindings to the $OUT_DIR/bindings.rs file. + let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); + bindings + .write_to_file(out_path.join("bindings.rs")) + .expect("Couldn't write bindings!"); + + cc::Build::new() + .file("vendor/fswAlgorithms/attControl/mrpSteering/mrpSteering.c") + .file("vendor/fswAlgorithms/attControl/mrpFeedback/mrpFeedback.c") + .file("vendor/architecture/utilities/rigidBodyKinematics.c") + .file("vendor/architecture/utilities/linearAlgebra.c") + .cargo_warnings(false) + .include("vendor") + .compile("basilisk"); +} diff --git a/libs/basilisk-rs/justfile b/libs/basilisk-rs/justfile new file mode 100644 index 000000000..99c54166a --- /dev/null +++ b/libs/basilisk-rs/justfile @@ -0,0 +1,11 @@ +pull: + git submodule update --init --recursive +venv: + [ -d .venv ] || python3 -m venv .venv && ./.venv/bin/pip3 install wheel 'conan<2.0' setuptools +vendor: pull venv + rm -rf vendor + mkdir -p vendor + source .venv/bin/activate && cd basilisk && CMAKE_PREFIX_PATH=$(readlink ../.venv/bin/python3)/../.. python3 conanfile.py + cp -r basilisk/src/fswAlgorithms vendor/ + cp -r basilisk/src/architecture vendor/ + cp -r basilisk/dist3/autoSource/cMsgCInterface vendor/ diff --git a/libs/basilisk-rs/src/att_control.rs b/libs/basilisk-rs/src/att_control.rs new file mode 100644 index 000000000..3ec41c785 --- /dev/null +++ b/libs/basilisk-rs/src/att_control.rs @@ -0,0 +1,306 @@ +use crate::{ + channel::BskChannel, + sys::{ + mrpFeedbackConfig, mrpSteeringConfig, AttGuidMsgPayload, CmdTorqueBodyMsgPayload, + RWArrayConfigMsgPayload, RWAvailabilityMsgPayload, RWSpeedMsgPayload, RateCmdMsgPayload, + VehicleConfigMsgPayload, + }, +}; + +pub const MODULE_ID: i64 = 0x224A; + +/// MRP steering control module - a wrapper around `mrpSteering` +/// +/// See https://hanspeterschaub.info/basilisk/Documentation/fswAlgorithms/attControl/mrpSteering/mrpSteering.html for more info +pub struct MRPSteering { + config: mrpSteeringConfig, +} + +impl MRPSteering { + /// Create a new MRPSteering + /// + /// # Args + /// - `k1`: proportional gain (rad/s) + /// - `k3`: cubic gained applied to mrp error in steering saturation function (rad/s) + /// - omega_max: maximum command rate (rad/s) + /// - feed_forward_enable: if outer feed forward is enabled + /// - rate_cmd: channel for rate command + /// - att_guid_cmd: channel for attitude guidance command + pub fn new( + k1: f64, + k3: f64, + omega_max: f64, + feed_forward_enable: bool, + rate_cmd: BskChannel, + att_guid_cmd: BskChannel, + ) -> Self { + let config = mrpSteeringConfig { + K1: k1, + K3: k3, + omega_max, + ignoreOuterLoopFeedforward: if feed_forward_enable { 0 } else { 1 }, + rateCmdOutMsg: rate_cmd.into(), + guidInMsg: att_guid_cmd.into(), + bskLogger: std::ptr::null_mut(), + }; + let mut this = Self { config }; + this.reset(); + this + } + + /// Resets the MRPSteering module + pub fn reset(&mut self) { + unsafe { + crate::sys::Reset_mrpSteering(&mut self.config, 0, MODULE_ID); + } + } + + /// Ticks the mrpSteering module forward, pulling any new messages, and sending output message + pub fn update(&mut self, time: u64) { + unsafe { + crate::sys::Update_mrpSteering(&mut self.config, time, MODULE_ID); + } + } +} + +/// Enum for MRPFeedback's two possible control laws +#[derive(Default)] +#[repr(i32)] +pub enum MRPFeedbackControlLaw { + #[default] + A = 0, + B = 1, +} + +pub struct MRPFeedback { + config: mrpFeedbackConfig, +} + +pub struct MRPFeedbackConfig { + k: f64, + p: f64, + k_i: f64, + integral_limit: f64, + control_law: MRPFeedbackControlLaw, + prior_time: u64, + z: [f64; 3], + int_sigma: [f64; 3], + known_torque_pnt_b_b: [f64; 3], + inertia: [f64; 9], +} + +impl MRPFeedback { + #[allow(clippy::too_many_arguments)] + pub fn new( + config: MRPFeedbackConfig, + rw_speed_cmd: BskChannel, + rw_availability_cmd: BskChannel, + rw_array_config: BskChannel, + cmd_torque_body: BskChannel, + int_feedback: BskChannel, + att_guid_cmd: BskChannel, + vehicle_config: BskChannel, + ) -> Self { + let MRPFeedbackConfig { + k, + p, + k_i, + integral_limit, + control_law, + prior_time, + z, + int_sigma, + known_torque_pnt_b_b, + inertia, + } = config; + let config = mrpFeedbackConfig { + K: k, + P: p, + Ki: k_i, + integralLimit: integral_limit, + controlLawType: control_law as i32, + priorTime: prior_time, + z, + int_sigma, + ISCPntB_B: inertia, + knownTorquePntB_B: known_torque_pnt_b_b, + rwSpeedsInMsg: rw_speed_cmd.into(), + rwAvailInMsg: rw_availability_cmd.into(), + rwParamsInMsg: rw_array_config.into(), + intFeedbackTorqueOutMsg: int_feedback.into(), + cmdTorqueOutMsg: cmd_torque_body.into(), + guidInMsg: att_guid_cmd.into(), + vehConfigInMsg: vehicle_config.into(), + bskLogger: std::ptr::null_mut(), + rwConfigParams: Default::default(), + }; + let mut this = Self { config }; + this.reset(0); + this + } + + pub fn reset(&mut self, time: u64) { + unsafe { + crate::sys::Reset_mrpFeedback(&mut self.config, time, MODULE_ID); + } + } + + pub fn update(&mut self, time: u64) { + unsafe { + crate::sys::Update_mrpFeedback(&mut self.config, time, MODULE_ID); + } + } +} + +pub struct RWArrayConfig { + pub wheels: Vec, +} + +pub struct RWConfig { + pub spin_axis: [f64; 3], + pub inertia: f64, + pub max_torque: f64, +} + +impl From for RWArrayConfigMsgPayload { + fn from(val: RWArrayConfig) -> Self { + let mut gs_matrix_b = [0.0; { 3 * 36 }]; + let mut u_max = [0.0; 36]; + let mut js_list = [0.0; 36]; + let len = val.wheels.len(); + for (i, wheel) in val.wheels.into_iter().enumerate() { + gs_matrix_b[i * 3..(i + 1) * 3].copy_from_slice(&wheel.spin_axis); + u_max[i] = wheel.max_torque; + js_list[i] = wheel.inertia; + } + RWArrayConfigMsgPayload { + GsMatrix_B: gs_matrix_b, + numRW: len as i32, + uMax: u_max, + JsList: js_list, + } + } +} + +pub struct RWAvailability { + pub availability: Vec, +} + +impl From for RWAvailabilityMsgPayload { + fn from(val: RWAvailability) -> Self { + let mut availability = [1; 36]; + for (i, avail) in val.availability.into_iter().enumerate() { + availability[i] = if avail { 0 } else { 1 }; + } + RWAvailabilityMsgPayload { + wheelAvailability: availability, + } + } +} + +#[cfg(test)] +mod tests { + use approx::assert_relative_eq; + + use super::*; + + #[test] + fn test_mrp_steer_basic() { + let att_guid = BskChannel::pair(); + let mut rate_cmd = BskChannel::pair(); + let mut mrp_steer = + MRPSteering::new(1.0, 1.0, 1.0, true, rate_cmd.clone(), att_guid.clone()); + att_guid.write(AttGuidMsgPayload { + sigma_BR: [1.0, 0.0, 0.0], + omega_BR_B: [0.0, 0.0, 0.0], + omega_RN_B: [1.0, 1.0, 1.0], + domega_RN_B: [1.0, 1.0, 1.0], + }); + mrp_steer.update(0); + let out = rate_cmd.read().unwrap(); + assert_relative_eq!( + out.omega_BastR_B.as_ref(), + [-0.8038134760954126, -0.0, -0.0].as_ref() + ); + assert_relative_eq!( + out.omegap_BastR_B.as_ref(), + [0.14790114643268049, -0.0, -0.0].as_ref() + ); + } + + #[test] + fn test_mrp_feedback_basic() { + let config = MRPFeedbackConfig { + k: 1.0, + p: 1.0, + k_i: 1.0, + integral_limit: 1.0, + control_law: MRPFeedbackControlLaw::A, + prior_time: 0, + z: [1.0, 1.0, 1.0], + int_sigma: [0.0, 0.0, 0.0], + known_torque_pnt_b_b: [0.0, 0.0, 0.0], + inertia: [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0], + }; + let rw_speed_cmd = BskChannel::pair(); + let rw_array_config = BskChannel::pair(); + rw_array_config.write( + RWArrayConfig { + wheels: vec![ + RWConfig { + spin_axis: [1.0, 0.0, 0.0], + inertia: 1.0, + max_torque: 1.0, + }, + RWConfig { + spin_axis: [0.0, 1.0, 0.0], + inertia: 1.0, + max_torque: 1.0, + }, + RWConfig { + spin_axis: [0.0, 0.0, 1.0], + inertia: 1.0, + max_torque: 1.0, + }, + ], + } + .into(), + ); + let rw_availability_cmd = BskChannel::pair(); + rw_availability_cmd.write( + RWAvailability { + availability: vec![true, true, true], + } + .into(), + ); + let mut cmd_torque_body = BskChannel::pair(); + let int_feedback = BskChannel::pair(); + let att_guid_cmd = BskChannel::pair(); + att_guid_cmd.write(AttGuidMsgPayload { + sigma_BR: [1.0, 0.0, 0.0], + omega_BR_B: [0.0, 0.0, 0.0], + omega_RN_B: [1.0, 1.0, 1.0], + domega_RN_B: [1.0, 1.0, 1.0], + }); + let vehicle_config = BskChannel::pair(); + let mut mrp_feedback = MRPFeedback::new( + config, + rw_speed_cmd, + rw_availability_cmd, + rw_array_config, + cmd_torque_body.clone(), + int_feedback, + att_guid_cmd, + vehicle_config, + ); + mrp_feedback.reset(0); + mrp_feedback.update(1); + let cmd_torque = cmd_torque_body.read().unwrap(); + assert_eq!( + cmd_torque, + CmdTorqueBodyMsgPayload { + torqueRequestBody: [-1.0, 0.0, 0.0] + } + ); + } +} diff --git a/libs/basilisk-rs/src/basilisk.h b/libs/basilisk-rs/src/basilisk.h new file mode 100644 index 000000000..9415300d5 --- /dev/null +++ b/libs/basilisk-rs/src/basilisk.h @@ -0,0 +1,8 @@ +#include "fswAlgorithms/attControl/mrpSteering/mrpSteering.h" +#include "fswAlgorithms/attControl/mrpFeedback/mrpFeedback.h" +#include "cMsgCInterface/RWSpeedMsg_C.h" +#include "cMsgCInterface/RWAvailabilityMsg_C.h" +#include "cMsgCInterface/RWArrayConfigMsg_C.h" +#include "cMsgCInterface/VehicleConfigMsg_C.h" +#include "cMsgCInterface/AttGuidMsg_C.h" +#include "cMsgCInterface/CmdTorqueBodyMsg_C.h" diff --git a/libs/basilisk-rs/src/channel.rs b/libs/basilisk-rs/src/channel.rs new file mode 100644 index 000000000..a139d8922 --- /dev/null +++ b/libs/basilisk-rs/src/channel.rs @@ -0,0 +1,242 @@ +use paste::paste; +use std::sync::Arc; +use std::{fmt::Debug, mem::MaybeUninit}; +use thingbuf::recycling::DefaultRecycle; +use thingbuf::Recycle; + +use crate::sys::*; + +// Create a pair of `Tx` and `Rx` channels +pub fn pair() -> (Tx, Rx) { + let (tx, rx) = thingbuf::mpsc::channel(2); + ( + Tx { + inner: Arc::new(tx), + }, + Rx { + inner: Arc::new(rx), + last_msg: None, + }, + ) +} + +/// The send half of a channel +pub struct Tx { + inner: std::sync::Arc>, +} + +impl Clone for Tx { + fn clone(&self) -> Self { + Self { + inner: self.inner.clone(), + } + } +} + +impl Tx +where + DefaultRecycle: Recycle, +{ + fn write(&self, msg: T) { + if self.inner.try_send(msg).is_err() { + tracing::warn!("bsk channel closed") + } + } +} + +/// The receive half of a channel +pub struct Rx { + inner: Arc>, + last_msg: Option, +} + +impl Clone for Rx { + fn clone(&self) -> Self { + Self { + inner: self.inner.clone(), + last_msg: None, + } + } +} + +impl Rx { + /// Internal function used by macro to generate basilisk compat interface, hence the strange naming + fn is_linked(&self) -> bool { + !self.inner.is_closed() + } + + fn read(&mut self) -> Option { + if let Ok(new_msg) = self.inner.try_recv() { + self.last_msg = Some(new_msg.clone()); + Some(new_msg) + } else { + self.last_msg.clone() + } + } +} + +const CHANNEL_MSG_HEADER: MsgHeader = MsgHeader { + isLinked: 0x2241, + isWritten: 0x2241, + timeWritten: 0x2241, + moduleID: 0x2241, +}; + +/// A struct that is designed to be byte compatible with the `FooMsg_C` structs in Basilisk +#[repr(C)] +pub struct BskChannel { + header: MsgHeader, + _payload: MaybeUninit, + rx_ptr: *mut Rx, + tx_ptr: *mut Tx, +} + +impl Clone for BskChannel { + fn clone(&self) -> Self { + let tx = Box::new(self.tx().unwrap().clone()); + let rx = Box::new(self.rx().unwrap().clone()); + Self { + header: self.header, + _payload: MaybeUninit::zeroed(), + rx_ptr: Box::into_raw(rx), + tx_ptr: Box::into_raw(tx), + } + } +} + +impl BskChannel { + pub fn pair() -> Self { + let (tx, rx) = crate::channel::pair(); + BskChannel::new(tx, rx) + } + + pub fn new(tx: Tx, rx: Rx) -> Self { + let tx = Box::new(tx); + let rx = Box::new(rx); + BskChannel { + header: CHANNEL_MSG_HEADER, + _payload: MaybeUninit::zeroed(), + rx_ptr: Box::into_raw(rx), + tx_ptr: Box::into_raw(tx), + } + } + + fn validate_header(&self) -> Option<()> { + if self.header != CHANNEL_MSG_HEADER { + tracing::warn!("invalid bsk channel"); + return None; + } + Some(()) + } + + fn rx_mut(&mut self) -> Option<&mut Rx> { + self.validate_header()?; + let rx = unsafe { &mut *self.rx_ptr }; + Some(rx) + } + + pub fn rx(&self) -> Option<&Rx> { + self.validate_header()?; + let rx = unsafe { &*self.rx_ptr }; + Some(rx) + } + + pub fn tx(&self) -> Option<&Tx> { + self.validate_header()?; + let tx = unsafe { &*self.tx_ptr }; + Some(tx) + } + + pub fn read(&mut self) -> Option { + let rx = self.rx_mut()?; + rx.read() + } + + pub fn write(&self, msg: T) -> Option<()> { + let tx = self.tx()?; + tx.write(msg); + Some(()) + } + + fn is_linked(&self) -> bool { + self.rx().map(|rx| rx.is_linked()).unwrap_or_default() + } +} + +macro_rules! impl_basilisk_channel { + ($msg_name:tt, $payload_name:tt) => { + impl From> for $msg_name { + fn from(val: BskChannel<$payload_name>) -> $msg_name { + unsafe { std::mem::transmute(val) } + } + } + + paste! { + /// Basilisk function to write msg to channel + /// + /// # Safety + /// Don't call this yourself, Basilisk will call it for you + #[no_mangle] + pub unsafe extern "C" fn [<$msg_name _write>]( + data: *const $payload_name, + channel: *mut $msg_name, + _module_id: i64, + _call_time: u64, + ) { + let channel: *mut BskChannel<$payload_name> = + unsafe { std::mem::transmute(channel) }; + let channel: &mut BskChannel<$payload_name> = unsafe { &mut *channel }; + if data.is_null() { + tracing::warn!("watcha doin passing null ptrs to write, you know better than that"); + return; + } + let data = unsafe { &*data }; + channel.write(data.clone()); + } + } + + paste! { + /// Basilisk function to read msg from channel + /// + /// # Safety + /// Don't call this yourself, Basilisk will call it for you + #[no_mangle] + pub unsafe extern "C" fn [<$msg_name _read>](channel: *mut $msg_name) -> $payload_name { + let channel: *mut BskChannel<$payload_name> = + unsafe { std::mem::transmute(channel) }; + let channel: &mut BskChannel<$payload_name> = unsafe { &mut *channel }; + channel.read().unwrap_or_default() + } + } + + paste! { + /// Basilisk function to check if channel is open + /// + /// # Safety + /// Don't call this yourself, Basilisk will call it for you + #[no_mangle] + pub unsafe extern "C" fn [<$msg_name _isLinked>](channel: *mut $msg_name) -> bool { + let channel: *mut BskChannel<$payload_name> = + unsafe { std::mem::transmute(channel) }; + let channel: &mut BskChannel<$payload_name> = unsafe { &mut *channel }; + channel.is_linked() + } + } + + + paste! { + #[no_mangle] + pub extern "C" fn [<$msg_name _zeroMsgPayload>]() -> $payload_name { + $payload_name::default() + } + } + }; +} + +impl_basilisk_channel!(AttGuidMsg_C, AttGuidMsgPayload); +impl_basilisk_channel!(RateCmdMsg_C, RateCmdMsgPayload); +impl_basilisk_channel!(RWSpeedMsg_C, RWSpeedMsgPayload); +impl_basilisk_channel!(RWAvailabilityMsg_C, RWAvailabilityMsgPayload); +impl_basilisk_channel!(RWArrayConfigMsg_C, RWArrayConfigMsgPayload); +impl_basilisk_channel!(CmdTorqueBodyMsg_C, CmdTorqueBodyMsgPayload); +impl_basilisk_channel!(VehicleConfigMsg_C, VehicleConfigMsgPayload); diff --git a/libs/basilisk-rs/src/lib.rs b/libs/basilisk-rs/src/lib.rs new file mode 100644 index 000000000..06fa2e38c --- /dev/null +++ b/libs/basilisk-rs/src/lib.rs @@ -0,0 +1,4 @@ +pub mod att_control; +pub mod channel; +mod log; +pub mod sys; diff --git a/libs/basilisk-rs/src/log.rs b/libs/basilisk-rs/src/log.rs new file mode 100644 index 000000000..5df64143a --- /dev/null +++ b/libs/basilisk-rs/src/log.rs @@ -0,0 +1,26 @@ +use std::ffi::c_char; + +use crate::sys::logLevel_t; +#[allow(non_snake_case)] +#[no_mangle] +unsafe extern "C" fn _bskLog(_logger: *const (), level: logLevel_t, msg: *const c_char) { + let msg = std::ffi::CStr::from_ptr(msg).to_string_lossy(); + println!("{}: {}", level.0, msg); + match level { + logLevel_t::BSK_DEBUG => { + tracing::debug!("{}", msg); + } + logLevel_t::BSK_INFORMATION => { + tracing::info!("{}", msg); + } + logLevel_t::BSK_WARNING => { + tracing::warn!("{}", msg); + } + logLevel_t::BSK_ERROR => { + tracing::error!("{}", msg); + } + level => { + tracing::trace!(?level, "{}", msg); + } + }; +} diff --git a/libs/basilisk-rs/src/queue.rs b/libs/basilisk-rs/src/queue.rs new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/libs/basilisk-rs/src/queue.rs @@ -0,0 +1 @@ + diff --git a/libs/basilisk-rs/src/sys.rs b/libs/basilisk-rs/src/sys.rs new file mode 100644 index 000000000..b681d1939 --- /dev/null +++ b/libs/basilisk-rs/src/sys.rs @@ -0,0 +1,6 @@ +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] +#![allow(rustdoc::broken_intra_doc_links)] + +include!(concat!(env!("OUT_DIR"), "/bindings.rs")); diff --git a/nix/shell.nix b/nix/shell.nix index ea9022eaf..f24241775 100644 --- a/nix/shell.nix +++ b/nix/shell.nix @@ -32,7 +32,9 @@ openblas lapack openssl + libclang ]; + LIBCLANG_PATH = "${pkgs.libclang.lib}/lib"; doCheck = false; }; elixir = pkgs.mkShell {