diff --git a/Cargo.lock b/Cargo.lock index abb35fbc..dc0ec120 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -28,15 +28,15 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.7" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "autocfg" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "bit-set" @@ -65,11 +65,20 @@ version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "cc" -version = "1.0.104" +version = "1.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74b6a57f98764a267ff415d50a25e6e166f3831a5071af4995296ea97d210490" +checksum = "40545c26d092346d8a8dab71ee48e7685a7a9cba76e634790c215b41a4a7b4cf" +dependencies = [ + "shlex", +] [[package]] name = "cfg-if" @@ -87,23 +96,23 @@ dependencies = [ "iana-time-zone", "num-traits", "serde", - "windows-targets 0.52.6", + "windows-targets", ] [[package]] name = "clap" -version = "4.5.8" +version = "4.5.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84b3edb18336f4df585bc9aa31dd99c036dfa5dc5e9a2939a722a188f3a8970d" +checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.5.8" +version = "4.5.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1c09dd5ada6c6c78075d6fd0da3f90d8080651e2d6cc8eb2f1aaa4034ced708" +checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" dependencies = [ "anstyle", "clap_lex", @@ -112,9 +121,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" [[package]] name = "condtype" @@ -124,15 +133,15 @@ checksum = "baf0a07a401f374238ab8e2f11a104d2851bf9ce711ec69804834de8af45c7af" [[package]] name = "core-foundation-sys" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "csv" -version = "1.3.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe" +checksum = "acdc4883a9c96732e4733212c01447ebd805833b7275a73ca3ee080fd77afdaf" dependencies = [ "csv-core", "itoa", @@ -151,9 +160,9 @@ dependencies = [ [[package]] name = "divan" -version = "0.1.14" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0d567df2c9c2870a43f3f2bd65aaeb18dbce1c18f217c3e564b4fbaeb3ee56c" +checksum = "6e05d17bd4ff1c1e7998ed4623d2efd91f72f1e24141ac33aac9377974270e1f" dependencies = [ "cfg-if", "clap", @@ -165,9 +174,9 @@ dependencies = [ [[package]] name = "divan-macros" -version = "0.1.14" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27540baf49be0d484d8f0130d7d8da3011c32a44d4fc873368154f1510e574a2" +checksum = "1b4464d46ce68bfc7cb76389248c7c254def7baca8bece0693b02b83842c4c88" dependencies = [ "proc-macro2", "quote", @@ -202,12 +211,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "fast-float" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95765f67b4b18863968b4a1bd5bb576f732b29a4a28c7cd84c09fa3e2875f33c" - [[package]] name = "fast_polynomial" version = "0.1.0" @@ -219,9 +222,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" +checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4" [[package]] name = "float_eq" @@ -237,9 +240,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "futures" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ "futures-channel", "futures-core", @@ -252,9 +255,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", "futures-sink", @@ -262,15 +265,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-executor" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" dependencies = [ "futures-core", "futures-task", @@ -279,15 +282,15 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-macro" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", @@ -296,15 +299,15 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "futures-task" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] name = "futures-timer" @@ -314,9 +317,9 @@ checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" [[package]] name = "futures-util" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-channel", "futures-core", @@ -355,21 +358,21 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "hashbrown" -version = "0.14.5" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" [[package]] name = "heck" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "iana-time-zone" -version = "0.1.60" +version = "0.1.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -390,9 +393,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.6" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", "hashbrown", @@ -421,9 +424,9 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "js-sys" -version = "0.3.69" +version = "0.3.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" dependencies = [ "wasm-bindgen", ] @@ -436,15 +439,15 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.155" +version = "0.2.162" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398" [[package]] name = "libm" -version = "0.2.8" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" +checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" [[package]] name = "linux-raw-sys" @@ -452,16 +455,6 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" -[[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.22" @@ -511,6 +504,9 @@ version = "0.1.0-alpha.0" dependencies = [ "lox-math", "nom", + "numpy", + "pyo3", + "thiserror", ] [[package]] @@ -518,7 +514,6 @@ name = "lox-io" version = "0.1.0-alpha.0" dependencies = [ "csv", - "fast-float", "lox-derive", "lox-math", "nom", @@ -553,6 +548,7 @@ dependencies = [ "itertools", "libm", "lox-bodies", + "lox-ephem", "lox-math", "lox-time", "numpy", @@ -567,6 +563,7 @@ version = "0.1.0-alpha.2" dependencies = [ "divan", "lox-bodies", + "lox-ephem", "lox-math", "lox-orbits", "lox-time", @@ -591,9 +588,9 @@ dependencies = [ [[package]] name = "matrixmultiply" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7574c1cf36da4798ab73da5b215bbf444f50718207754cb522201d78d1cd0ff2" +checksum = "9380b911e3e96d10c1f415da0876389aaf1b56759054eeb0de7df940c456ba1a" dependencies = [ "autocfg", "rawpointer", @@ -622,14 +619,16 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "ndarray" -version = "0.15.6" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adb12d4e967ec485a5f71c6311fe28158e9d6f4bc4a447b474184d0f91a8fa32" +checksum = "882ed72dce9365842bf196bdeedf5055305f11fc8c03dee7bb0194a6cad34841" dependencies = [ "matrixmultiply", "num-complex", "num-integer", "num-traits", + "portable-atomic", + "portable-atomic-util", "rawpointer", ] @@ -719,9 +718,9 @@ dependencies = [ [[package]] name = "numpy" -version = "0.21.0" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec170733ca37175f5d75a5bea5911d6ff45d2cd52849ce98b685394e4f2f37f4" +checksum = "edb929bc0da91a4d85ed6c0a84deaa53d411abfb387fc271124f91bf6b89f14e" dependencies = [ "libc", "ndarray", @@ -734,38 +733,15 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" - -[[package]] -name = "parking_lot" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-targets 0.52.6", -] +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "pin-project-lite" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" [[package]] name = "pin-utils" @@ -775,30 +751,42 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "portable-atomic" -version = "1.6.0" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2" + +[[package]] +name = "portable-atomic-util" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" +checksum = "90a7d5beecc52a491b54d6dd05c7a45ba1801666a5baad9fdbfc6fef8d2d206c" +dependencies = [ + "portable-atomic", +] [[package]] name = "ppv-lite86" -version = "0.2.17" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] [[package]] name = "proc-macro-crate" -version = "3.1.0" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" +checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" dependencies = [ "toml_edit", ] [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" dependencies = [ "unicode-ident", ] @@ -825,15 +813,15 @@ dependencies = [ [[package]] name = "pyo3" -version = "0.21.2" +version = "0.22.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e00b96a521718e08e03b1a622f01c8a8deb50719335de3f60b3b3950f069d8" +checksum = "f402062616ab18202ae8319da13fa4279883a2b8a9d9f83f20dbade813ce1884" dependencies = [ "cfg-if", "indoc", "libc", "memoffset", - "parking_lot", + "once_cell", "portable-atomic", "pyo3-build-config", "pyo3-ffi", @@ -843,9 +831,9 @@ dependencies = [ [[package]] name = "pyo3-build-config" -version = "0.21.2" +version = "0.22.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7883df5835fafdad87c0d888b266c8ec0f4c9ca48a5bed6bbb592e8dedee1b50" +checksum = "b14b5775b5ff446dd1056212d778012cbe8a0fbffd368029fd9e25b514479c38" dependencies = [ "once_cell", "target-lexicon", @@ -853,9 +841,9 @@ dependencies = [ [[package]] name = "pyo3-ffi" -version = "0.21.2" +version = "0.22.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01be5843dc60b916ab4dad1dca6d20b9b4e6ddc8e15f50c47fe6d85f1fb97403" +checksum = "9ab5bcf04a2cdcbb50c7d6105de943f543f9ed92af55818fd17b660390fc8636" dependencies = [ "libc", "pyo3-build-config", @@ -863,9 +851,9 @@ dependencies = [ [[package]] name = "pyo3-macros" -version = "0.21.2" +version = "0.22.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77b34069fc0682e11b31dbd10321cbf94808394c56fd996796ce45217dfac53c" +checksum = "0fd24d897903a9e6d80b968368a34e1525aeb719d568dba8b3d4bfa5dc67d453" dependencies = [ "proc-macro2", "pyo3-macros-backend", @@ -875,9 +863,9 @@ dependencies = [ [[package]] name = "pyo3-macros-backend" -version = "0.21.2" +version = "0.22.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08260721f32db5e1a5beae69a55553f56b99bd0e1c3e6e0a5e8851a9d0f5a85c" +checksum = "36c011a03ba1e50152b4b394b479826cad97e7a21eb52df179cd91ac411cbfbe" dependencies = [ "heck", "proc-macro2", @@ -904,9 +892,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.36" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] @@ -956,20 +944,11 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" -[[package]] -name = "redox_syscall" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" -dependencies = [ - "bitflags", -] - [[package]] name = "regex" -version = "1.10.5" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", @@ -979,9 +958,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" dependencies = [ "aho-corasick", "memchr", @@ -996,9 +975,9 @@ checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a" [[package]] name = "regex-syntax" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "relative-path" @@ -1044,18 +1023,18 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustc_version" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ "semver", ] [[package]] name = "rustix" -version = "0.38.34" +version = "0.38.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +checksum = "99e4ea3e1cdc4b559b8e5650f9c8e5998e3e5c1343b4eaf034565f32318d63c0" dependencies = [ "bitflags", "errno", @@ -1082,12 +1061,6 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - [[package]] name = "semver" version = "1.0.23" @@ -1096,9 +1069,9 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.204" +version = "1.0.214" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" +checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" dependencies = [ "serde_derive", ] @@ -1116,9 +1089,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.204" +version = "1.0.214" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" +checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" dependencies = [ "proc-macro2", "quote", @@ -1127,11 +1100,12 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.120" +version = "1.0.132" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5" +checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] @@ -1147,6 +1121,12 @@ dependencies = [ "serde_json", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "slab" version = "0.4.9" @@ -1156,17 +1136,11 @@ dependencies = [ "autocfg", ] -[[package]] -name = "smallvec" -version = "1.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" - [[package]] name = "syn" -version = "2.0.68" +version = "2.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9" +checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" dependencies = [ "proc-macro2", "quote", @@ -1175,46 +1149,47 @@ dependencies = [ [[package]] name = "target-lexicon" -version = "0.12.14" +version = "0.12.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tempfile" -version = "3.10.1" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" dependencies = [ "cfg-if", "fastrand", + "once_cell", "rustix", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "terminal_size" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" +checksum = "4f599bd7ca042cfdf8f4512b277c02ba102247820f9d9d4a9f521f496751a6ef" dependencies = [ "rustix", - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] name = "thiserror" -version = "1.0.61" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.61" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", @@ -1223,15 +1198,15 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.6" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" [[package]] name = "toml_edit" -version = "0.21.1" +version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ "indexmap", "toml_datetime", @@ -1246,9 +1221,9 @@ checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "unindent" @@ -1273,19 +1248,20 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.92" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" dependencies = [ "cfg-if", + "once_cell", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.92" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" dependencies = [ "bumpalo", "log", @@ -1298,9 +1274,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.92" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1308,9 +1284,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.92" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" dependencies = [ "proc-macro2", "quote", @@ -1321,9 +1297,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.92" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" [[package]] name = "windows-core" @@ -1331,16 +1307,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", + "windows-targets", ] [[package]] @@ -1349,22 +1316,16 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.6", + "windows-targets", ] [[package]] -name = "windows-targets" -version = "0.48.5" +name = "windows-sys" +version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", + "windows-targets", ] [[package]] @@ -1373,46 +1334,28 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -1425,48 +1368,24 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" @@ -1475,9 +1394,30 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.5.40" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" dependencies = [ "memchr", ] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml index aadb90fb..711d460d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,6 @@ lox-time = { path = "crates/lox-time", version = "0.1.0-alpha.0" } csv = "1.3.0" divan = "0.1.14" dyn-clone = "1.0.17" -fast-float = "0.2.0" fast_polynomial = "0.1.0" float_eq = "1.0.1" glam = "0.28.0" @@ -32,9 +31,9 @@ itertools = "0.13.0" libm = "0.2.8" nom = "7.1.3" num = "0.4.1" -numpy = "0.21.0" +numpy = "0.22.1" proptest = "1.4.0" -pyo3 = "0.21.1" +pyo3 = "0.22.6" quick-xml = { version = "0.31.0", features = ["serde", "serialize"] } regex = "1.10.4" rstest = "0.21.0" diff --git a/crates/lox-ephem/Cargo.toml b/crates/lox-ephem/Cargo.toml index 2cef8f86..82079f2e 100644 --- a/crates/lox-ephem/Cargo.toml +++ b/crates/lox-ephem/Cargo.toml @@ -9,5 +9,12 @@ authors.workspace = true repository.workspace = true [dependencies] -nom.workspace = true lox-math.workspace = true + +nom.workspace = true +pyo3 = { workspace = true, optional = true } +numpy = { workspace = true, optional = true } +thiserror.workspace = true + +[features] +python = ["dep:pyo3", "dep:numpy"] diff --git a/crates/lox-ephem/src/lib.rs b/crates/lox-ephem/src/lib.rs index 3bc112be..d464fd1f 100644 --- a/crates/lox-ephem/src/lib.rs +++ b/crates/lox-ephem/src/lib.rs @@ -1 +1,77 @@ +use lox_math::types::julian_dates::Epoch; + +#[cfg(feature = "python")] +pub mod python; pub mod spk; + +pub(crate) type Position = (f64, f64, f64); +pub(crate) type Velocity = (f64, f64, f64); +pub(crate) type Body = i32; + +pub trait Ephemeris { + type Error: std::error::Error; + + fn position(&self, epoch: Epoch, origin: Body, target: Body) -> Result; + fn velocity(&self, epoch: Epoch, origin: Body, target: Body) -> Result; + fn state( + &self, + epoch: Epoch, + origin: Body, + target: Body, + ) -> Result<(Position, Velocity), Self::Error>; +} + +fn ancestors(id: i32) -> Vec { + let mut ancestors = vec![id]; + let mut current = id; + while current != 0 { + current /= 100; + ancestors.push(current); + } + ancestors +} + +pub fn path_from_ids(origin: i32, target: i32) -> Vec { + let ancestors_origin = ancestors(origin); + let ancestors_target = ancestors(target); + let n = ancestors_target.len(); + let mut path = ancestors_origin; + + ancestors_target + .into_iter() + .take(n - 1) + .rev() + .for_each(|id| path.push(id)); + + if *path.first().unwrap() != 0 && *path.last().unwrap() != 0 { + let idx = path.iter().position(|&id| id == 0).unwrap(); + if path[idx - 1] == path[idx + 1] { + let common_ancestor = vec![path[idx - 1]]; + path.splice((idx - 1)..=(idx + 1), common_ancestor); + } + } + + path +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_ancestors() { + assert_eq!(ancestors(0), vec![0]); + assert_eq!(ancestors(3), vec![3, 0]); + assert_eq!(ancestors(399), vec![399, 3, 0]); + } + + #[test] + fn test_path_from_ids() { + assert_eq!(path_from_ids(399, 499), [399, 3, 0, 4, 499]); + assert_eq!(path_from_ids(399, 0), [399, 3, 0]); + assert_eq!(path_from_ids(0, 399), [0, 3, 399]); + assert_eq!(path_from_ids(399, 3), [399, 3]); + assert_eq!(path_from_ids(3, 399), [3, 399]); + assert_eq!(path_from_ids(399, 301), [399, 3, 301]); + } +} diff --git a/crates/lox-ephem/src/python.rs b/crates/lox-ephem/src/python.rs new file mode 100644 index 00000000..f86298a4 --- /dev/null +++ b/crates/lox-ephem/src/python.rs @@ -0,0 +1,22 @@ +use pyo3::{exceptions::PyValueError, pyclass, pymethods, PyErr, PyResult}; + +use crate::spk::parser::{parse_daf_spk, DafSpkError, Spk}; + +impl From for PyErr { + fn from(err: DafSpkError) -> Self { + PyValueError::new_err(err.to_string()) + } +} + +#[pyclass(name = "SPK", module = "lox_space", frozen)] +pub struct PySpk(pub Spk); + +#[pymethods] +impl PySpk { + #[new] + fn new(path: &str) -> PyResult { + let data = std::fs::read(path)?; + let spk = parse_daf_spk(&data)?; + Ok(PySpk(spk)) + } +} diff --git a/crates/lox-ephem/src/spk/api.rs b/crates/lox-ephem/src/spk/api.rs index 8145d3f8..b06e0c33 100644 --- a/crates/lox-ephem/src/spk/api.rs +++ b/crates/lox-ephem/src/spk/api.rs @@ -10,11 +10,9 @@ use std::collections::HashMap; use lox_math::types::julian_dates::Epoch; -use super::parser::{DafSpkError, Spk, SpkSegment, SpkType2Array, SpkType2Coefficients}; +use crate::{Body, Ephemeris, Position, Velocity}; -type Position = (f64, f64, f64); -type Velocity = (f64, f64, f64); -type Body = i32; +use super::parser::{DafSpkError, Spk, SpkSegment, SpkType2Array, SpkType2Coefficients}; impl Spk { fn find_segment( @@ -105,13 +103,12 @@ impl Spk { Ok((coefficients, record)) } +} - pub fn position( - &self, - epoch: Epoch, - origin: Body, - target: Body, - ) -> Result { +impl Ephemeris for Spk { + type Error = DafSpkError; + + fn position(&self, epoch: Epoch, origin: Body, target: Body) -> Result { let (segment, sign) = self.find_segment(origin, target)?; if epoch < segment.initial_epoch || epoch > segment.final_epoch { @@ -141,12 +138,7 @@ impl Spk { Ok((x, y, z)) } - pub fn velocity( - &self, - epoch: Epoch, - origin: Body, - target: Body, - ) -> Result { + fn velocity(&self, epoch: Epoch, origin: Body, target: Body) -> Result { let (segment, sign) = self.find_segment(origin, target)?; if epoch < segment.initial_epoch || epoch > segment.final_epoch { @@ -197,7 +189,7 @@ impl Spk { Ok((x, y, z)) } - pub fn state( + fn state( &self, epoch: Epoch, origin: Body, diff --git a/crates/lox-ephem/src/spk/parser.rs b/crates/lox-ephem/src/spk/parser.rs index b692d3a7..4307f635 100644 --- a/crates/lox-ephem/src/spk/parser.rs +++ b/crates/lox-ephem/src/spk/parser.rs @@ -12,6 +12,7 @@ use std::iter::zip; use nom::bytes::complete as nb; use nom::error::ErrorKind; use nom::number::complete as nn; +use thiserror::Error; type BodyId = i32; @@ -47,17 +48,19 @@ pub struct DafSummary { pub final_address: usize, } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Error)] pub enum DafSpkError { - // The data type integer value does not match the ones in the spec + #[error("the data type integer value does not match the ones in the spec")] InvalidSpkSegmentDataType, - // The number of DAF components does not match the SPK specification + #[error("the number of DAF components does not match the SPK specification")] UnexpectedNumberOfComponents, + #[error("unable to parse")] UnableToParse, + #[error("unsupported SPK type {data_type}")] UnsupportedSpkArrayType { data_type: i32 }, - // Unable to find the segment for a given center body and target body + #[error("unable to find the segment for a given center body and target body")] UnableToFindMatchingSegment, - // Unable to find record for a given date + #[error("unable to find record for a given date")] UnableToFindMatchingRecord, } diff --git a/crates/lox-io/Cargo.toml b/crates/lox-io/Cargo.toml index 3d46c7c0..94dba76a 100644 --- a/crates/lox-io/Cargo.toml +++ b/crates/lox-io/Cargo.toml @@ -9,19 +9,17 @@ authors.workspace = true repository.workspace = true [dependencies] +lox-derive.workspace = true lox-math.workspace = true csv.workspace = true nom.workspace = true -serde.workspace = true -thiserror.workspace = true - quick-xml.workspace = true -serde_json.workspace = true -serde-aux.workspace = true regex.workspace = true -fast-float.workspace = true -lox-derive.workspace = true +serde-aux.workspace = true +serde.workspace = true +serde_json.workspace = true +thiserror.workspace = true [dev-dependencies] rstest.workspace = true diff --git a/crates/lox-io/src/ndm.rs b/crates/lox-io/src/ndm.rs index 0c67ed94..bae1033f 100644 --- a/crates/lox-io/src/ndm.rs +++ b/crates/lox-io/src/ndm.rs @@ -13,10 +13,10 @@ //! input that they accept. Some relaxations: //! //! - The KVN floating point numbers defined in the specification can have only -//! one character in the integer part of the number, but we accept any regular -//! float number. +//! one character in the integer part of the number, but we accept any regular +//! float number. //! - The KVN strings are defined in the specification as being either only -//! lower-case or only upper-case, but we accept any combination of cases. +//! lower-case or only upper-case, but we accept any combination of cases. //! //! The XML deserializer does not perform any validation on the schema types //! defined (e.g. non-positive double, lat-long, angle). The validation only diff --git a/crates/lox-io/src/ndm/kvn/parser.rs b/crates/lox-io/src/ndm/kvn/parser.rs index 939482ea..f53a3808 100644 --- a/crates/lox-io/src/ndm/kvn/parser.rs +++ b/crates/lox-io/src/ndm/kvn/parser.rs @@ -317,7 +317,9 @@ fn parse_kvn_covariance_matrix_line<'a, T: Iterator + ?Sized>( let result: Result, _> = next_line .split_whitespace() .map(|matrix_element| { - fast_float::parse(matrix_element.trim()) + matrix_element + .trim() + .parse::() .map_err(|_| KvnCovarianceMatrixParserErr::InvalidFormat { input: next_line }) }) .collect(); @@ -558,8 +560,9 @@ pub fn parse_kvn_numeric_line( let value = captures.name("value").unwrap().as_str(); let unit = captures.name("unit").map(|x| x.as_str().to_string()); - let value = - fast_float::parse(value).map_err(|_| KvnNumberParserErr::InvalidFormat { input })?; + let value = value + .parse::() + .map_err(|_| KvnNumberParserErr::InvalidFormat { input })?; Ok(KvnValue { value, unit }) } diff --git a/crates/lox-orbits/Cargo.toml b/crates/lox-orbits/Cargo.toml index f7cf5593..8fbd32cd 100644 --- a/crates/lox-orbits/Cargo.toml +++ b/crates/lox-orbits/Cargo.toml @@ -10,6 +10,7 @@ repository.workspace = true [dependencies] lox-bodies.workspace = true +lox-ephem.workspace = true lox-time.workspace = true lox-math.workspace = true diff --git a/crates/lox-orbits/src/analysis.rs b/crates/lox-orbits/src/analysis.rs index 7fceab71..e6307ddd 100644 --- a/crates/lox-orbits/src/analysis.rs +++ b/crates/lox-orbits/src/analysis.rs @@ -130,15 +130,7 @@ mod tests { let actual: Vec = gs .times() .iter() - .map(|t| { - elevation( - t.clone(), - &frame, - &gs, - &sc, - &NoOpFrameTransformationProvider, - ) - }) + .map(|t| elevation(*t, &frame, &gs, &sc, &NoOpFrameTransformationProvider)) .collect(); for (actual, expected) in actual.iter().zip(expected.iter()) { assert_close!(actual, expected, 1e-1); diff --git a/crates/lox-orbits/src/propagators/semi_analytical.rs b/crates/lox-orbits/src/propagators/semi_analytical.rs index 9e2dc3aa..f398b253 100644 --- a/crates/lox-orbits/src/propagators/semi_analytical.rs +++ b/crates/lox-orbits/src/propagators/semi_analytical.rs @@ -178,7 +178,7 @@ mod tests { let s0 = k0.to_cartesian(); let t1 = time + k0.orbital_period(); - let propagator = Vallado::new(s0.clone()); + let propagator = Vallado::new(s0); let s1 = propagator .propagate(t1) .expect("propagator should converge"); @@ -222,7 +222,7 @@ mod tests { let period = k0.orbital_period(); let t_end = period.to_decimal_seconds().ceil() as i64; let steps = TimeDelta::range(0..=t_end).map(|dt| time + dt); - let trajectory = Vallado::new(s0.clone()).propagate_all(steps).unwrap(); + let trajectory = Vallado::new(s0).propagate_all(steps).unwrap(); let s1 = trajectory.interpolate(period); let k1 = s1.to_keplerian(); diff --git a/crates/lox-orbits/src/python.rs b/crates/lox-orbits/src/python.rs index 17bec926..7e4c5a3c 100644 --- a/crates/lox-orbits/src/python.rs +++ b/crates/lox-orbits/src/python.rs @@ -9,6 +9,7 @@ use std::convert::TryFrom; use glam::DVec3; +use lox_ephem::python::PySpk; use numpy::{PyArray1, PyArray2, PyArrayMethods}; use pyo3::types::PyType; use pyo3::{ @@ -40,6 +41,7 @@ use crate::propagators::semi_analytical::{Vallado, ValladoError}; use crate::propagators::sgp4::{Sgp4, Sgp4Error}; use crate::propagators::Propagator; use crate::states::ToCartesian; +use crate::trajectories::TrajectoryTransformationError; use crate::{ frames::FrameTransformationProvider, states::State, @@ -48,6 +50,13 @@ use crate::{ mod generated; +impl From for PyErr { + fn from(err: TrajectoryTransformationError) -> Self { + // FIXME: wrong error type + PyValueError::new_err(err.to_string()) + } +} + impl From for PyErr { fn from(err: FindEventError) -> Self { // FIXME: wrong error type @@ -106,6 +115,7 @@ pub fn find_windows( } #[pyclass(name = "Frame", module = "lox_space", frozen)] +#[pyo3(eq)] #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)] pub enum PyFrame { Icrf, @@ -324,6 +334,7 @@ pub struct PyState(pub State); #[pymethods] impl PyState { #[new] + #[pyo3(signature = (time, position, velocity, origin=None, frame=None))] fn new( time: PyTime, position: (f64, f64, f64), @@ -369,6 +380,7 @@ impl PyState { PyArray1::from_slice_bound(py, &vel) } + #[pyo3(signature = (frame, provider=None))] fn to_frame( &self, frame: PyFrame, @@ -377,6 +389,22 @@ impl PyState { self.to_frame_generated(frame, provider) } + fn to_origin(&self, target: &Bound<'_, PyAny>, ephemeris: &Bound<'_, PySpk>) -> PyResult { + if self.0.reference_frame() != PyFrame::Icrf { + return Err(PyValueError::new_err( + "only inertial frames are supported for conversion to Keplerian elements", + )); + } + let target = PyBody::try_from(target)?; + let spk = &ephemeris.borrow().0; + let s1 = self + .0 + .with_frame(Icrf) + .to_origin(target, spk)? + .with_frame(PyFrame::Icrf); + Ok(Self(s1)) + } + fn to_keplerian(&self) -> PyResult { if self.0.reference_frame() != PyFrame::Icrf { return Err(PyValueError::new_err( @@ -426,6 +454,16 @@ pub struct PyKeplerian(pub Keplerian); #[pymethods] impl PyKeplerian { #[new] + #[pyo3(signature = ( + time, + semi_major_axis, + eccentricity, + inclination, + longitude_of_ascending_node, + argument_of_periapsis, + true_anomaly, + origin=None, + ))] #[allow(clippy::too_many_arguments)] fn new( time: PyTime, @@ -511,6 +549,7 @@ impl PyTrajectory { } #[classmethod] + #[pyo3(signature = (start_time, array, origin=None, frame=None))] fn from_numpy( _cls: &Bound<'_, PyType>, start_time: PyTime, @@ -599,6 +638,32 @@ impl PyTrajectory { } Err(PyValueError::new_err("invalid time argument")) } + + #[pyo3(signature = (frame, provider=None))] + fn to_frame( + &self, + frame: PyFrame, + provider: Option<&Bound<'_, PyUt1Provider>>, + ) -> PyResult { + let mut states: Vec> = + Vec::with_capacity(self.0.states().len()); + for s in self.0.states() { + states.push(PyState(s).to_frame(frame.clone(), provider)?.0); + } + Ok(PyTrajectory(Trajectory::new(&states)?)) + } + + fn to_origin(&self, target: &Bound<'_, PyAny>, ephemeris: &Bound<'_, PySpk>) -> PyResult { + let target = PyBody::try_from(target)?; + let spk = &ephemeris.borrow().0; + let s1 = self + .0 + .clone() + .with_frame(Icrf) + .to_origin(target, spk)? + .with_frame(PyFrame::Icrf); + Ok(Self(s1)) + } } #[pyclass(name = "Event", module = "lox_space", frozen)] @@ -665,6 +730,7 @@ impl From for PyErr { #[pymethods] impl PyVallado { #[new] + #[pyo3(signature =(initial_state, max_iter=None))] fn new(initial_state: PyState, max_iter: Option) -> PyResult { if initial_state.0.reference_frame() != PyFrame::Icrf { return Err(PyValueError::new_err( @@ -712,6 +778,7 @@ impl PyGroundLocation { PyGroundLocation(GroundLocation::new(longitude, latitude, altitude, planet)) } + #[pyo3(signature = (state, provider=None))] fn observables( &self, state: PyState, @@ -834,6 +901,7 @@ impl PySgp4 { PyTime(self.0.time().with_scale(PyTimeScale::Tai)) } + #[pyo3(signature = (steps, provider=None))] fn propagate<'py>( &self, py: Python<'py>, diff --git a/crates/lox-orbits/src/states.rs b/crates/lox-orbits/src/states.rs index 3a69fd5e..141ebe50 100644 --- a/crates/lox-orbits/src/states.rs +++ b/crates/lox-orbits/src/states.rs @@ -9,8 +9,10 @@ use std::f64::consts::{PI, TAU}; use std::ops::Sub; use glam::{DMat3, DVec3}; +use itertools::Itertools; -use lox_bodies::{PointMass, RotationalElements, Spheroid}; +use lox_bodies::{Body, PointMass, RotationalElements, Spheroid}; +use lox_ephem::{path_from_ids, Ephemeris}; use lox_math::glam::Azimuth; use lox_math::math::{mod_two_pi, normalize_two_pi}; use lox_math::roots::{BracketError, FindRoot, Secant}; @@ -224,6 +226,38 @@ where } } +impl State +where + T: TimeLike + Clone, + O: Origin + Body + Clone, +{ + pub fn to_origin( + &self, + target: O1, + ephemeris: &E, + ) -> Result, E::Error> { + // TODO: Fix time scale + let epoch = self.time().seconds_since_j2000(); + let mut pos = self.position(); + let mut vel = self.velocity(); + let mut pos_eph = DVec3::ZERO; + let mut vel_eph = DVec3::ZERO; + let origin_id = self.origin.id(); + let target_id = target.id(); + let path = path_from_ids(origin_id.0, target_id.0); + for (origin, target) in path.into_iter().tuple_windows() { + let (p, v) = ephemeris.state(epoch, origin, target)?; + let p: DVec3 = p.into(); + let v: DVec3 = v.into(); + pos_eph += p; + vel_eph += v; + } + pos -= pos_eph; + vel -= vel_eph; + Ok(State::new(self.time(), pos, vel, target, Icrf)) + } +} + impl TryToFrame, U> for State where T: TryToScale + TimeLike + Clone, @@ -341,10 +375,15 @@ where #[cfg(test)] mod tests { + use std::{path::PathBuf, sync::OnceLock}; + use float_eq::assert_float_eq; - use lox_bodies::{Earth, Jupiter}; - use lox_time::{time, time_scales::Tdb, Time}; + use lox_bodies::{Earth, Jupiter, Venus}; + use lox_ephem::spk::parser::{parse_daf_spk, Spk}; + use lox_math::assert_close; + use lox_math::is_close::IsClose; + use lox_time::{time, time_scales::Tdb, transformations::ToTai, utc::Utc, Time}; use crate::frames::NoOpFrameTransformationProvider; @@ -427,4 +466,42 @@ mod tests { assert_float_eq!(ground.longitude(), lon_exp, rel <= 1e-4); assert_float_eq!(ground.altitude(), alt_exp, rel <= 1e-4); } + + pub fn data_dir() -> PathBuf { + PathBuf::from(format!("{}/../../data", env!("CARGO_MANIFEST_DIR"))) + } + + #[test] + fn test_state_to_origin() { + let r_venus = DVec3::new( + 1.001977553295792e8, + 2.200234656010247e8, + 9.391473630346918e7, + ); + let v_venus = DVec3::new(-59.08617935009049, 22.682387107225292, 12.05029567478702); + let r = DVec3::new(6068279.27, -1692843.94, -2516619.18) / 1e3; + + let v = DVec3::new(-660.415582, 5495.938726, -5303.093233) / 1e3; + + let r_exp = r - r_venus; + let v_exp = v - v_venus; + + let utc = Utc::from_iso("2016-05-30T12:00:00.000").unwrap(); + let tai = utc.to_tai(); + + let s_earth = State::new(tai, r, v, Earth, Icrf); + let s_venus = s_earth.to_origin(Venus, ephemeris()).unwrap(); + + let r_act = s_venus.position(); + let v_act = s_venus.velocity(); + + assert_close!(r_act, r_exp); + assert_close!(v_act, v_exp); + } + + fn ephemeris() -> &'static Spk { + let contents = std::fs::read(data_dir().join("de440s.bsp")).unwrap(); + static EPHEMERIS: OnceLock = OnceLock::new(); + EPHEMERIS.get_or_init(|| parse_daf_spk(&contents).unwrap()) + } } diff --git a/crates/lox-orbits/src/trajectories.rs b/crates/lox-orbits/src/trajectories.rs index 058fa59b..2604be71 100644 --- a/crates/lox-orbits/src/trajectories.rs +++ b/crates/lox-orbits/src/trajectories.rs @@ -3,6 +3,7 @@ use std::sync::Arc; use csv::Error; use glam::DVec3; +use lox_ephem::Ephemeris; use thiserror::Error; use lox_bodies::{Body, RotationalElements}; @@ -232,6 +233,27 @@ where } } +impl Trajectory +where + T: TimeLike + Clone, + O: Origin + Body + Clone, +{ + pub fn to_origin( + &self, + target: O1, + ephemeris: &E, + ) -> Result, TrajectoryTransformationError> { + let mut states: Vec> = Vec::with_capacity(self.states.len()); + for state in &self.states { + let state = state.to_origin(target.clone(), ephemeris).map_err(|e| { + TrajectoryTransformationError::StateTransformationError(e.to_string()) + })?; + states.push(state); + } + Ok(Trajectory::new(&states)?) + } +} + impl Trajectory, O, R> where O: Origin + Clone, diff --git a/crates/lox-space/Cargo.toml b/crates/lox-space/Cargo.toml index 0251913d..c3fe7a2c 100644 --- a/crates/lox-space/Cargo.toml +++ b/crates/lox-space/Cargo.toml @@ -14,9 +14,10 @@ crate-type = ["lib", "cdylib"] [dependencies] lox-bodies.workspace = true +lox-ephem.workspace = true +lox-math.workspace = true lox-orbits.workspace = true lox-time.workspace = true -lox-math.workspace = true pyo3 = { workspace = true, optional = true } @@ -25,9 +26,10 @@ default = ["python"] python = [ "dep:pyo3", "lox-bodies/python", + "lox-ephem/python", + "lox-math/python", "lox-orbits/python", "lox-time/python", - "lox-math/python", ] [dev-dependencies] diff --git a/crates/lox-space/lox_space.pyi b/crates/lox-space/lox_space.pyi index 5d3257bd..bbc5242e 100644 --- a/crates/lox-space/lox_space.pyi +++ b/crates/lox-space/lox_space.pyi @@ -7,498 +7,324 @@ type Epoch = Literal["jd", "mjd", "j1950", "j2000"] type Unit = Literal["seconds", "days", "centuries"] type Vec3 = tuple[float, float, float] - def find_events(func: Callable[[float], float], start: Time, times: list[float]): ... - - def find_windows( - func: Callable[[float], float], start: Time, end: Time, times: list[float] + func: Callable[[float], float], start: Time, end: Time, times: list[float] ): ... - - def visibility( - times: list[Time], - gs: GroundLocation, - min_elevation: float, - sc: Trajectory, - provider: UT1Provider, + times: list[Time], + gs: GroundLocation, + min_elevation: float, + sc: Trajectory, + provider: UT1Provider, ): ... - class Frame: def __new__(cls, abbreviation: str): ... - def name(self) -> str: ... - def abbreviation(self) -> str: ... +class SPK: + def __new__(cls, path): ... class State: def __new__( - cls, - time: Time, - position: Vec3, - velocity: Vec3, - origin: Origin | None = None, - frame: Frame | None = None, + cls, + time: Time, + position: Vec3, + velocity: Vec3, + origin: Origin | None = None, + frame: Frame | None = None, ): ... - def time(self) -> Time: ... - def origin(self) -> Origin: ... - def reference_frame(self) -> Frame: ... - def position(self) -> np.ndarray: ... - def velocity(self) -> np.ndarray: ... - - def to_frame(self, frame: Frame) -> State: ... - + def to_frame(self, frame: Frame) -> Self: ... + def to_origin(self, target: Origin, ephemeris: SPK) -> Self: ... def to_keplerian(self) -> Keplerian: ... - def rotation_lvlh(self) -> np.ndarray: ... - def to_ground_location(self) -> GroundLocation: ... - class Keplerian: def __new__( - cls, - time: Time, - semi_major_axis: float, - eccentricity: float, - inclination: float, - longitude_of_ascending_node: float, - argument_of_periapsis: float, - true_anomaly: float, - origin: Origin | None = None, + cls, + time: Time, + semi_major_axis: float, + eccentricity: float, + inclination: float, + longitude_of_ascending_node: float, + argument_of_periapsis: float, + true_anomaly: float, + origin: Origin | None = None, ): ... - def time(self) -> Time: ... - def origin(self) -> Origin: ... - def semi_major_axis(self) -> float: ... - def eccentricity(self) -> float: ... - def inclination(self) -> float: ... - def longitude_of_ascending_node(self) -> float: ... - def argument_of_periapsis(self) -> float: ... - def true_anomaly(self) -> float: ... - def to_cartesian(self) -> State: ... - def orbital_period(self) -> TimeDelta: ... - class Trajectory: def __new__(cls, states: list[State]): ... - @classmethod def from_numpy( - cls, - start_time: Time, - states: np.ndarray, - origin: Origin | None = None, - frame: Frame | None = None, + cls, + start_time: Time, + states: np.ndarray, + origin: Origin | None = None, + frame: Frame | None = None, ) -> Self: ... - def origin(self) -> Origin: ... - def reference_frame(self) -> Frame: ... - def to_numpy(self) -> np.ndarray: ... - def states(self) -> list[State]: ... - def find_events(self, func: Callable[[State], float]) -> list[Event]: ... - def find_windows(self, func: Callable[[State], float]) -> list[Window]: ... - def interpolate(self, time: Time) -> State: ... - + def to_frame(self, frame: Frame) -> Self: ... + def to_origin(self, target: Origin, ephemeris: SPK) -> Self: ... class Event: def time(self) -> Time: ... - def cross(self) -> str: ... - class Window: def start(self) -> Time: ... - def end(self) -> Time: ... - def duration(self) -> TimeDelta: ... - class Vallado: def __new__(cls, initial_state: State, max_iter: int | None = None): ... - @overload def propagate(self, time: Time) -> State: ... - @overload def propagate(self, time: list[Time]) -> Trajectory: ... - def propagate(self, time: Time | list[Time]) -> State | Trajectory: ... - class GroundLocation: def __new__( - cls, - planet: Planet, - longitude: float, - latitude: float, - altitude: float, + cls, + planet: Planet, + longitude: float, + latitude: float, + altitude: float, ): ... - def longitude(self) -> float: ... - def latitude(self) -> float: ... - def altitude(self) -> float: ... - def observables(self) -> Observables: ... - def rotation_to_topocentric(self) -> np.ndarray: ... - class GroundPropagator: def __new__(cls, location: GroundLocation, provider: UT1Provider): ... - @overload def propagate(self, time: Time) -> State: ... - @overload def propagate(self, time: list[Time]) -> Trajectory: ... - def propagate(self, time: Time | list[Time]) -> State | Trajectory: ... - class SGP4: def __new__(cls, tle: str): ... - def time(self) -> Time: ... - @overload def propagate(self, time: Time) -> State: ... - @overload def propagate(self, time: list[Time]) -> Trajectory: ... - def propagate(self, time: Time | list[Time]) -> State | Trajectory: ... - class Observables: - def __new__(cls, azimuth: float, elevation: float, range: float, range_rate: float): ... - + def __new__( + cls, azimuth: float, elevation: float, range: float, range_rate: float + ): ... def azimuth(self) -> float: ... - def elevation(self) -> float: ... - def range(self) -> float: ... - def range_rate(self) -> float: ... - class Time: def __new__( - cls, - scale: Scale, - year: int, - month: int, - day: int, - hour: int = 0, - minute: int = 0, - seconds: float = 0.0, + cls, + scale: Scale, + year: int, + month: int, + day: int, + hour: int = 0, + minute: int = 0, + seconds: float = 0.0, ): ... - @classmethod def from_julian_date(cls, scale: Scale, jd: float, epoch: str = "jd") -> Self: ... - @classmethod def from_two_part_julian_date( - cls, scale: Scale, jd1: float, jd2: float + cls, scale: Scale, jd1: float, jd2: float ) -> Self: ... - @classmethod def from_day_of_year( - cls, - scale: Scale, - year: int, - doy: int, - hour: int = 0, - minute: int = 0, - seconds: float = 0.0, + cls, + scale: Scale, + year: int, + doy: int, + hour: int = 0, + minute: int = 0, + seconds: float = 0.0, ) -> Self: ... - @classmethod def from_iso(cls, iso: str, scale: Scale | None = None) -> Self: ... - @classmethod def from_seconds(cls, scale: Scale, seconds: int, subsecond: float) -> Self: ... - def seconds(self) -> int: ... - def subsecond(self) -> float: ... - def __str__(self) -> str: ... - def __repr__(self) -> str: ... - def __add__(self, other: TimeDelta) -> Self: ... - @overload def __sub__(self, other: TimeDelta) -> Self: ... - @overload def __sub__(self, other: Time) -> TimeDelta: ... - def __eq__(self, other: object) -> bool: ... - def __lt__(self, other: object) -> bool: ... - def __le__(self, other: object) -> bool: ... - def isclose( - self, other: Time, rel_tol: float = 1e-8, abs_tol: float = 1e-14 + self, other: Time, rel_tol: float = 1e-8, abs_tol: float = 1e-14 ) -> bool: ... - def julian_date( - self, - epoch: Epoch = "jd", - unit: Unit = "days", + self, + epoch: Epoch = "jd", + unit: Unit = "days", ) -> float: ... - def two_part_julian_date(self) -> tuple[float, float]: ... - def scale(self) -> Scale: ... - def year(self) -> int: ... - def month(self) -> int: ... - def day(self) -> int: ... - def day_of_year(self) -> int: ... - def hour(self) -> int: ... - def minute(self) -> int: ... - def second(self) -> int: ... - def millisecond(self) -> int: ... - def microsecond(self) -> int: ... - def nanosecond(self) -> int: ... - def picosecond(self) -> int: ... - def femtosecond(self) -> int: ... - def decimal_seconds(self) -> float: ... - def to_tai(self, provider: UT1Provider | None = None) -> Self: ... - def to_tcb(self, provider: UT1Provider | None = None) -> Self: ... - def to_tcg(self, provider: UT1Provider | None = None) -> Self: ... - def to_tdb(self, provider: UT1Provider | None = None) -> Self: ... - def to_tt(self, provider: UT1Provider | None = None) -> Self: ... - def to_ut1(self, provider: UT1Provider | None = None) -> Self: ... - def to_utc(self, provider: UT1Provider | None = None) -> UTC: ... - class TimeDelta: def __new__(cls, seconds: float): ... - def __repr__(self) -> str: ... - def __str__(self) -> str: ... - def __float__(self) -> float: ... - def __neg__(self) -> Self: ... - def __add__(self, other: Self) -> Self: ... - def __sub__(self, other: Self) -> Self: ... - def seconds(self) -> int: ... - def subsecond(self) -> float: ... - @classmethod def from_seconds(cls, seconds: int) -> Self: ... - @classmethod def from_minutes(cls, minutes: float) -> Self: ... - @classmethod def from_hours(cls, hours: float) -> Self: ... - @classmethod def from_days(cls, days: float) -> Self: ... - @classmethod def from_julian_years(cls, years: float) -> Self: ... - @classmethod def from_julian_centuries(cls, centuries: float) -> Self: ... - def to_decimal_seconds(self) -> float: ... - @classmethod def range(cls, start: int, end: int, step: int | None = None) -> list[Self]: ... - class UTC: def __new__( - cls, - year: int, - month: int, - day: int, - hour: int = 0, - minute: int = 0, - seconds: float = 0.0, + cls, + year: int, + month: int, + day: int, + hour: int = 0, + minute: int = 0, + seconds: float = 0.0, ): ... - @classmethod def from_iso(cls, iso: str) -> Self: ... - def __str__(self) -> str: ... - def __repr__(self) -> str: ... - def __eq__(self, other: object) -> bool: ... - def year(self) -> int: ... - def month(self) -> int: ... - def day(self) -> int: ... - def hour(self) -> int: ... - def minute(self) -> int: ... - def second(self) -> int: ... - def millisecond(self) -> int: ... - def microsecond(self) -> int: ... - def nanosecond(self) -> int: ... - def picosecond(self) -> int: ... - def decimal_seconds(self) -> float: ... - def to_tai(self) -> Time: ... - def to_tcb(self) -> Time: ... - def to_tcg(self) -> Time: ... - def to_tdb(self) -> Time: ... - def to_tt(self) -> Time: ... - def to_ut1(self, provider: UT1Provider) -> Time: ... - class UT1Provider: def __new__(cls, path: str): ... - type Origin = Sun | Barycenter | Planet | Satellite | MinorBody - class Sun: def __new__(cls): ... - def id(self) -> int: ... - def name(self) -> str: ... - def gravitational_parameter(self) -> float: ... - def mean_radius(self) -> float: ... - def polar_radius(self) -> float: ... - def equatorial_radius(self) -> float: ... - class Barycenter: def __new__(cls, name: str): ... - def id(self) -> int: ... - def name(self) -> str: ... - def gravitational_parameter(self) -> float: ... - class Planet: def __new__(cls, name: str): ... - def id(self) -> int: ... - def name(self) -> str: ... - def gravitational_parameter(self) -> float: ... - def mean_radius(self) -> float: ... - def polar_radius(self) -> float: ... - def equatorial_radius(self) -> float: ... - class Satellite: def __new__(cls, name: str): ... - def id(self) -> int: ... - def name(self) -> str: ... - def gravitational_parameter(self) -> float: ... - def mean_radius(self) -> float: ... - def polar_radius(self) -> float: ... - def subplanetary_radius(self) -> float: ... - def along_orbit_radius(self) -> float: ... - class MinorBody: def __new__(cls, name: str): ... - def id(self) -> int: ... - def name(self) -> str: ... - def gravitational_parameter(self) -> float: ... - def mean_radius(self) -> float: ... - def polar_radius(self) -> float: ... - def subplanetary_radius(self) -> float: ... - def along_orbit_radius(self) -> float: ... diff --git a/crates/lox-space/src/lib.rs b/crates/lox-space/src/lib.rs index a7d98172..afd2eb35 100644 --- a/crates/lox-space/src/lib.rs +++ b/crates/lox-space/src/lib.rs @@ -7,6 +7,7 @@ */ use lox_bodies::python::{PyBarycenter, PyMinorBody, PyPlanet, PySatellite, PySun}; +use lox_ephem::python::PySpk; use lox_orbits::python::{ elevation, find_events, find_windows, visibility, PyEvent, PyFrame, PyGroundLocation, PyGroundPropagator, PyKeplerian, PyObservables, PySgp4, PyState, PyTopocentric, PyTrajectory, @@ -48,5 +49,6 @@ fn lox_space(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_class::()?; m.add_class::()?; m.add_class::()?; + m.add_class::()?; Ok(()) } diff --git a/crates/lox-space/tests/test_states.py b/crates/lox-space/tests/test_states.py index 0a874aab..441f3b15 100644 --- a/crates/lox-space/tests/test_states.py +++ b/crates/lox-space/tests/test_states.py @@ -5,7 +5,18 @@ # file, you can obtain one at https://mozilla.org/MPL/2.0/. import lox_space as lox +import numpy as np +import numpy.testing as npt import pytest +from pathlib import Path + + +@pytest.fixture +def ephemeris(): + spk = ( + Path(__file__).parent.joinpath("..", "..", "..", "data", "de440s.bsp").resolve() + ) + return lox.SPK(str(spk)) def test_state_to_ground_location(): @@ -17,3 +28,31 @@ def test_state_to_ground_location(): assert ground.longitude() == pytest.approx(2.646276127963636) assert ground.latitude() == pytest.approx(-0.2794495715104036) assert ground.altitude() == pytest.approx(417.8524158044338) + + +def test_state_to_origin(ephemeris): + r_venus = np.array( + [ + 1.001977553295792e8, + 2.200234656010247e8, + 9.391473630346918e7, + ] + ) + v_venus = np.array([-59.08617935009049, 22.682387107225292, 12.05029567478702]) + r = np.array([6068279.27, -1692843.94, -2516619.18]) / 1e3 + + v = np.array([-660.415582, 5495.938726, -5303.093233]) / 1e3 + + r_exp = r - r_venus + v_exp = v - v_venus + utc = lox.UTC.from_iso("2016-05-30T12:00:00.000") + tai = utc.to_tai() + + s_earth = lox.State(tai, tuple(r), tuple(v)) + s_venus = s_earth.to_origin(lox.Planet("Venus"), ephemeris) + + r_act = s_venus.position() + v_act = s_venus.velocity() + + npt.assert_allclose(r_act, r_exp) + npt.assert_allclose(v_act, v_exp) diff --git a/crates/lox-time/src/lib.rs b/crates/lox-time/src/lib.rs index e5d68e87..b0eb82bd 100644 --- a/crates/lox-time/src/lib.rs +++ b/crates/lox-time/src/lib.rs @@ -144,7 +144,7 @@ impl Time { /// # Errors /// /// * Returns `TimeError::LeapSecondsOutsideUtc` if `time` is a leap second, since leap seconds - /// cannot be unambiguously represented by a continuous time format. + /// cannot be unambiguously represented by a continuous time format. pub fn from_date_and_time(scale: T, date: Date, time: TimeOfDay) -> Result { let mut seconds = (date.days_since_j2000() * time::SECONDS_PER_DAY) .to_i64() @@ -575,7 +575,7 @@ impl TimeBuilder { /// /// * [DateError] if `ymd` data passed into the builder did not correspond to a valid date; /// * [TimeOfDayError] if `hms` data passed into the builder did not correspond to a valid time - /// of day. + /// of day. pub fn build(self) -> Result, TimeError> { let date = self.date?; let time = self.time?; diff --git a/crates/lox-time/src/python/deltas.rs b/crates/lox-time/src/python/deltas.rs index f3f050bd..3ffe3cd8 100644 --- a/crates/lox-time/src/python/deltas.rs +++ b/crates/lox-time/src/python/deltas.rs @@ -96,6 +96,7 @@ impl PyTimeDelta { } #[classmethod] + #[pyo3(signature = (start, end, step=None))] pub fn range( _cls: &Bound<'_, PyType>, start: i64, diff --git a/crates/lox-time/src/python/time.rs b/crates/lox-time/src/python/time.rs index 5e308ad2..164ca5b8 100644 --- a/crates/lox-time/src/python/time.rs +++ b/crates/lox-time/src/python/time.rs @@ -121,7 +121,7 @@ impl PyTime { } #[classmethod] - #[pyo3(signature=(scale, year, day, hour = 0, minute = 0, seconds = 0.0))] + #[pyo3(signature=(scale, year, day, hour=0, minute=0, seconds=0.0))] pub fn from_day_of_year( _cls: &Bound<'_, PyType>, scale: &str, @@ -140,6 +140,7 @@ impl PyTime { } #[classmethod] + #[pyo3(signature = (iso, scale=None))] pub fn from_iso(_cls: &Bound<'_, PyType>, iso: &str, scale: Option<&str>) -> PyResult { let scale: PyTimeScale = match scale { Some(scale) => scale.parse()?, @@ -299,6 +300,7 @@ impl PyTime { self.0.decimal_seconds() } + #[pyo3(signature = (provider=None))] pub fn to_tai(&self, provider: Option<&Bound<'_, PyUt1Provider>>) -> PyResult { let time = match provider { Some(provider) => self.try_to_scale(Tai, provider.get())?, @@ -307,6 +309,7 @@ impl PyTime { Ok(PyTime(time.with_scale(PyTimeScale::Tai))) } + #[pyo3(signature = (provider=None))] pub fn to_tcb(&self, provider: Option<&Bound<'_, PyUt1Provider>>) -> PyResult { let time = match provider { Some(provider) => self.try_to_scale(Tcb, provider.get())?, @@ -315,6 +318,7 @@ impl PyTime { Ok(PyTime(time.with_scale(PyTimeScale::Tcb))) } + #[pyo3(signature = (provider=None))] pub fn to_tcg(&self, provider: Option<&Bound<'_, PyUt1Provider>>) -> PyResult { let time = match provider { Some(provider) => self.try_to_scale(Tcg, provider.get())?, @@ -323,6 +327,7 @@ impl PyTime { Ok(PyTime(time.with_scale(PyTimeScale::Tcg))) } + #[pyo3(signature = (provider=None))] pub fn to_tdb(&self, provider: Option<&Bound<'_, PyUt1Provider>>) -> PyResult { let time = match provider { Some(provider) => self.try_to_scale(Tdb, provider.get())?, @@ -331,6 +336,7 @@ impl PyTime { Ok(PyTime(time.with_scale(PyTimeScale::Tdb))) } + #[pyo3(signature = (provider=None))] pub fn to_tt(&self, provider: Option<&Bound<'_, PyUt1Provider>>) -> PyResult { let time = match provider { Some(provider) => self.try_to_scale(Tt, provider.get())?, @@ -339,6 +345,7 @@ impl PyTime { Ok(PyTime(time.with_scale(PyTimeScale::Tt))) } + #[pyo3(signature = (provider=None))] pub fn to_ut1(&self, provider: Option<&Bound<'_, PyUt1Provider>>) -> PyResult { let time = match provider { Some(provider) => self.try_to_scale(Ut1, provider.get())?, @@ -347,6 +354,7 @@ impl PyTime { Ok(PyTime(time.with_scale(PyTimeScale::Ut1))) } + #[pyo3(signature = (provider=None))] pub fn to_utc(&self, provider: Option<&Bound<'_, PyUt1Provider>>) -> PyResult { let tai = match provider { Some(provider) => self.try_to_scale(Tai, provider.get())?, diff --git a/data/de440s.bsp b/data/de440s.bsp new file mode 100644 index 00000000..b7d215c1 Binary files /dev/null and b/data/de440s.bsp differ