arduino-tnc

git clone https://git.bradleygannon.com/arduino-tnc.git
Log | Files | Refs

commit f764ef0581c622626574581f551227744455c502
parent 3e10c5cd7da55cb8bc3542d973f8334a8262b562
Author: Bradley Gannon <bradley@bradleygannon.com>
Date:   Fri, 29 May 2026 18:39:04 -0400

Split Goertzel and demod functions into separate crate to allow host-side testing

Diffstat:
D.cargo/config.toml | 9---------
M.gitignore | 2+-
DCargo.lock | 1080-------------------------------------------------------------------------------
DCargo.toml | 39---------------------------------------
DRavedude.toml | 4----
Afirmware/.cargo/config.toml | 12++++++++++++
Afirmware/Cargo.lock | 1086+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Afirmware/Cargo.toml | 41+++++++++++++++++++++++++++++++++++++++++
Afirmware/Ravedude.toml | 4++++
Rrust-toolchain.toml -> firmware/rust-toolchain.toml | 0
Rsrc/kiss.rs -> firmware/src/kiss.rs | 0
Afirmware/src/main.rs | 182+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Afirmware/src/rx.rs | 44++++++++++++++++++++++++++++++++++++++++++++
Rsrc/tx.rs -> firmware/src/tx.rs | 0
Mflake.lock | 6+++---
Mflake.nix | 2+-
Dsrc/main.rs | 182-------------------------------------------------------------------------------
Dsrc/rx.rs | 126-------------------------------------------------------------------------------
Atnc-core/.cargo/config.toml | 5+++++
Atnc-core/Cargo.lock | 7+++++++
Atnc-core/Cargo.toml | 4++++
Atnc-core/src/goertzel.rs | 155+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atnc-core/src/lib.rs | 7+++++++
Atnc-core/tests/demodulator_test.rs | 262+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atnc-core/tests/goertzel_filter_test.rs | 78++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
25 files changed, 1892 insertions(+), 1445 deletions(-)

diff --git a/.cargo/config.toml b/.cargo/config.toml @@ -1,9 +0,0 @@ -[build] -target = "avr-none" -rustflags = ["-C", "target-cpu=atmega328p"] - -[target.'cfg(target_arch = "avr")'] -runner = "ravedude" - -[unstable] -build-std = ["core"] diff --git a/.gitignore b/.gitignore @@ -1 +1 @@ -/target +**/target diff --git a/Cargo.lock b/Cargo.lock @@ -1,1080 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 4 - -[[package]] -name = "aho-corasick" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" -dependencies = [ - "memchr", -] - -[[package]] -name = "anyhow" -version = "1.0.101" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea" - -[[package]] -name = "arduino-hal" -version = "0.1.0" -source = "git+https://github.com/rahix/avr-hal?rev=e5c8f37fe48419956e722490a82b9ca9b9fc61a2#e5c8f37fe48419956e722490a82b9ca9b9fc61a2" -dependencies = [ - "atmega-hal", - "avr-device", - "avr-hal-generic", - "cfg-if", - "embedded-hal 1.0.0", - "ufmt", -] - -[[package]] -name = "arduino-tnc" -version = "0.1.0" -dependencies = [ - "arduino-hal", - "avr-device", - "crc", - "embedded-hal 1.0.0", - "heapless", - "nb 1.1.0", - "panic-halt", - "trig-const", - "ufmt", -] - -[[package]] -name = "arraydeque" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d902e3d592a523def97af8f317b08ce16b7ab854c1985a0c671e6f15cebc236" - -[[package]] -name = "atdf2svd" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "179e5f106191a4d02437d8f66ed06f852e1a85774db29f934251d33cfec219fa" -dependencies = [ - "cfg-if", - "log", - "svd-encoder", - "svd-rs", - "xmltree 0.10.3", -] - -[[package]] -name = "atmega-hal" -version = "0.1.0" -source = "git+https://github.com/rahix/avr-hal?rev=e5c8f37fe48419956e722490a82b9ca9b9fc61a2#e5c8f37fe48419956e722490a82b9ca9b9fc61a2" -dependencies = [ - "avr-device", - "avr-hal-generic", -] - -[[package]] -name = "avr-device" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0584fb409e2ff23423979617c6b7ab34628727c8ef8026b665d41f41114060b9" -dependencies = [ - "anyhow", - "atdf2svd", - "avr-device-macros", - "bare-metal", - "cfg-if", - "critical-section", - "prettyplease", - "svd-rs", - "svd2rust", - "svdtools", - "syn 2.0.110", - "vcell", - "yaml-rust2", -] - -[[package]] -name = "avr-device-macros" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "129341bd5b981a038dd2ae0963550bfcb67ad56c64b53aa462cf1a0764405e97" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "avr-hal-generic" -version = "0.1.0" -source = "git+https://github.com/rahix/avr-hal?rev=e5c8f37fe48419956e722490a82b9ca9b9fc61a2#e5c8f37fe48419956e722490a82b9ca9b9fc61a2" -dependencies = [ - "avr-device", - "embedded-hal 0.2.7", - "embedded-hal 1.0.0", - "embedded-hal-bus", - "embedded-storage", - "nb 1.1.0", - "paste", - "ufmt", - "unwrap-infallible", -] - -[[package]] -name = "bare-metal" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8fe8f5a8a398345e52358e18ff07cc17a568fbca5c6f73873d3a62056309603" - -[[package]] -name = "bstr" -version = "1.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab" -dependencies = [ - "memchr", - "serde", -] - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - -[[package]] -name = "cfg-if" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" - -[[package]] -name = "commands" -version = "0.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "688226b9769bbf11a9d82a94fb4adda15793a6023d33a8ca7695dbafec6f4123" - -[[package]] -name = "convert_case" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "crc" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d" -dependencies = [ - "crc-catalog", -] - -[[package]] -name = "crc-catalog" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" - -[[package]] -name = "critical-section" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" - -[[package]] -name = "displaydoc" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.110", -] - -[[package]] -name = "either" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" - -[[package]] -name = "embedded-hal" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff" -dependencies = [ - "nb 0.1.3", - "void", -] - -[[package]] -name = "embedded-hal" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89" - -[[package]] -name = "embedded-hal-bus" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57b4e6ede84339ebdb418cd986e6320a34b017cdf99b5cc3efceec6450b06886" -dependencies = [ - "critical-section", - "embedded-hal 1.0.0", -] - -[[package]] -name = "embedded-storage" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "723dce4e9f25b6e6c5f35628e144794e5b459216ed7da97b7c4b66cdb3fa82ca" - -[[package]] -name = "encoding_rs" -version = "0.8.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "equivalent" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" - -[[package]] -name = "foldhash" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" - -[[package]] -name = "form_urlencoded" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "globset" -version = "0.4.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52dfc19153a48bde0cbd630453615c8151bce3a5adfac7a0aebfbf0a1e1f57e3" -dependencies = [ - "aho-corasick", - "bstr", - "log", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "hash32" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" -dependencies = [ - "byteorder", -] - -[[package]] -name = "hashbrown" -version = "0.15.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" -dependencies = [ - "foldhash", -] - -[[package]] -name = "hashbrown" -version = "0.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" - -[[package]] -name = "hashlink" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" -dependencies = [ - "hashbrown 0.15.5", -] - -[[package]] -name = "heapless" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2af2455f757db2b292a9b1768c4b70186d443bcb3b316252d6b540aec1cd89ed" -dependencies = [ - "hash32", - "stable_deref_trait", -] - -[[package]] -name = "icu_collections" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" -dependencies = [ - "displaydoc", - "potential_utf", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_locale_core" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" -dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", -] - -[[package]] -name = "icu_normalizer" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" -dependencies = [ - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "zerovec", -] - -[[package]] -name = "icu_normalizer_data" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" - -[[package]] -name = "icu_properties" -version = "2.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" -dependencies = [ - "icu_collections", - "icu_locale_core", - "icu_properties_data", - "icu_provider", - "zerotrie", - "zerovec", -] - -[[package]] -name = "icu_properties_data" -version = "2.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" - -[[package]] -name = "icu_provider" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" -dependencies = [ - "displaydoc", - "icu_locale_core", - "writeable", - "yoke", - "zerofrom", - "zerotrie", - "zerovec", -] - -[[package]] -name = "idna" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" -dependencies = [ - "idna_adapter", - "smallvec", - "utf8_iter", -] - -[[package]] -name = "idna_adapter" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" -dependencies = [ - "icu_normalizer", - "icu_properties", -] - -[[package]] -name = "indexmap" -version = "2.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" -dependencies = [ - "equivalent", - "hashbrown 0.16.1", -] - -[[package]] -name = "inflections" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a257582fdcde896fd96463bf2d40eefea0580021c0712a0e2b028b60b47a837a" - -[[package]] -name = "itertools" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" -dependencies = [ - "either", -] - -[[package]] -name = "litemap" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" - -[[package]] -name = "log" -version = "0.4.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" - -[[package]] -name = "memchr" -version = "2.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" - -[[package]] -name = "nb" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f" -dependencies = [ - "nb 1.1.0", -] - -[[package]] -name = "nb" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" - -[[package]] -name = "normpath" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf23ab2b905654b4cb177e30b629937b3868311d4e1cba859f899c041046e69b" -dependencies = [ - "windows-sys", -] - -[[package]] -name = "once_cell" -version = "1.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" - -[[package]] -name = "panic-halt" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a513e167849a384b7f9b746e517604398518590a9142f4846a32e3c2a4de7b11" - -[[package]] -name = "paste" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" - -[[package]] -name = "percent-encoding" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" - -[[package]] -name = "phf" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" -dependencies = [ - "phf_macros", - "phf_shared", -] - -[[package]] -name = "phf_generator" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" -dependencies = [ - "phf_shared", - "rand", -] - -[[package]] -name = "phf_macros" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" -dependencies = [ - "phf_generator", - "phf_shared", - "proc-macro2", - "quote", - "syn 2.0.110", -] - -[[package]] -name = "phf_shared" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" -dependencies = [ - "siphasher", -] - -[[package]] -name = "potential_utf" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" -dependencies = [ - "zerovec", -] - -[[package]] -name = "prettyplease" -version = "0.2.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" -dependencies = [ - "proc-macro2", - "syn 2.0.110", -] - -[[package]] -name = "proc-macro2" -version = "1.0.106" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quick-xml" -version = "0.37.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb" -dependencies = [ - "memchr", - "serde", -] - -[[package]] -name = "quote" -version = "1.0.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" - -[[package]] -name = "regex" -version = "1.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c" - -[[package]] -name = "roxmltree" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" - -[[package]] -name = "serde" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" -dependencies = [ - "serde_core", - "serde_derive", -] - -[[package]] -name = "serde_core" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.110", -] - -[[package]] -name = "siphasher" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" - -[[package]] -name = "smallvec" -version = "1.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" - -[[package]] -name = "stable_deref_trait" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" - -[[package]] -name = "svd-encoder" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0961dac31cc1ce3e673918792e826d6bdf5e4005b197acb996b793cfc732f83" -dependencies = [ - "convert_case", - "svd-rs", - "thiserror 1.0.69", - "xmltree 0.11.0", -] - -[[package]] -name = "svd-parser" -version = "0.14.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a41fe17e46dee363a7d3b20e878bbf6d6b4e839ae010ff07ef0e05d41d201811" -dependencies = [ - "anyhow", - "roxmltree", - "svd-rs", - "thiserror 1.0.69", -] - -[[package]] -name = "svd-rs" -version = "0.14.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9b88ee2e82f09623ff76965587dc15a2e7150a3126854899c86e94dab777458" -dependencies = [ - "once_cell", - "regex", - "serde", - "thiserror 1.0.69", -] - -[[package]] -name = "svd2rust" -version = "0.37.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1eaf6a040bfbc04e7b946a6a373b24aab09e1feb875bb0e20c929946c4b7bfc4" -dependencies = [ - "anyhow", - "inflections", - "log", - "proc-macro2", - "quote", - "regex", - "svd-parser", - "svd-rs", - "syn 2.0.110", - "thiserror 2.0.18", - "url", -] - -[[package]] -name = "svdtools" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cb2e48b200a352e10c4c50056b3f0a2da8e571c317d5af47c41ba672b2e9c87" -dependencies = [ - "anyhow", - "commands", - "globset", - "hashlink", - "itertools", - "log", - "normpath", - "once_cell", - "phf", - "quick-xml", - "regex", - "serde", - "svd-encoder", - "svd-parser", - "svd-rs", - "thiserror 1.0.69", - "yaml-rust2", -] - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.110" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "synstructure" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.110", -] - -[[package]] -name = "thiserror" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" -dependencies = [ - "thiserror-impl 1.0.69", -] - -[[package]] -name = "thiserror" -version = "2.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" -dependencies = [ - "thiserror-impl 2.0.18", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.110", -] - -[[package]] -name = "thiserror-impl" -version = "2.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.110", -] - -[[package]] -name = "tinystr" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" -dependencies = [ - "displaydoc", - "zerovec", -] - -[[package]] -name = "trig-const" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffec2fe2ed1e10dac46d8530afb395cae635fa02ef03d7fff1c211e94f9059fc" - -[[package]] -name = "ufmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a64846ec02b57e9108d6469d98d1648782ad6bb150a95a9baac26900bbeab9d" -dependencies = [ - "ufmt-macros", - "ufmt-write", -] - -[[package]] -name = "ufmt-macros" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d337d3be617449165cb4633c8dece429afd83f84051024079f97ad32a9663716" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "ufmt-write" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e87a2ed6b42ec5e28cc3b94c09982969e9227600b2e3dcbc1db927a84c06bd69" - -[[package]] -name = "unicode-ident" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" - -[[package]] -name = "unicode-segmentation" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" - -[[package]] -name = "unwrap-infallible" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "151ac09978d3c2862c4e39b557f4eceee2cc72150bc4cb4f16abf061b6e381fb" - -[[package]] -name = "url" -version = "2.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", - "serde", - "serde_derive", -] - -[[package]] -name = "utf8_iter" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" - -[[package]] -name = "vcell" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002" - -[[package]] -name = "void" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" - -[[package]] -name = "windows-link" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" - -[[package]] -name = "windows-sys" -version = "0.61.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" -dependencies = [ - "windows-link", -] - -[[package]] -name = "writeable" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" - -[[package]] -name = "xml-rs" -version = "0.8.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f" - -[[package]] -name = "xmltree" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7d8a75eaf6557bb84a65ace8609883db44a29951042ada9b393151532e41fcb" -dependencies = [ - "xml-rs", -] - -[[package]] -name = "xmltree" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b619f8c85654798007fb10afa5125590b43b088c225a25fc2fec100a9fad0fc6" -dependencies = [ - "indexmap", - "xml-rs", -] - -[[package]] -name = "yaml-rust2" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2462ea039c445496d8793d052e13787f2b90e750b833afee748e601c17621ed9" -dependencies = [ - "arraydeque", - "encoding_rs", - "hashlink", -] - -[[package]] -name = "yoke" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" -dependencies = [ - "stable_deref_trait", - "yoke-derive", - "zerofrom", -] - -[[package]] -name = "yoke-derive" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.110", - "synstructure", -] - -[[package]] -name = "zerofrom" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" -dependencies = [ - "zerofrom-derive", -] - -[[package]] -name = "zerofrom-derive" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.110", - "synstructure", -] - -[[package]] -name = "zerotrie" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" -dependencies = [ - "displaydoc", - "yoke", - "zerofrom", -] - -[[package]] -name = "zerovec" -version = "0.11.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" -dependencies = [ - "yoke", - "zerofrom", - "zerovec-derive", -] - -[[package]] -name = "zerovec-derive" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.110", -] diff --git a/Cargo.toml b/Cargo.toml @@ -1,39 +0,0 @@ -[package] -name = "arduino-tnc" -version = "0.1.0" -authors = ["Bradley Gannon <bradley@bradleygannon.com>"] -edition = "2021" -license = "MIT OR Apache-2.0" - - -[[bin]] -name = "arduino-tnc" -test = false -bench = false - -[dependencies] -avr-device = "0.8.1" -crc = "3.4.0" -embedded-hal = "1.0" -heapless = "0.9.2" -nb = "1.1.0" -panic-halt = "1.0.0" -trig-const = "0.4.0" -ufmt = "0.2.0" - -[dependencies.arduino-hal] -git = "https://github.com/rahix/avr-hal" -rev = "e5c8f37fe48419956e722490a82b9ca9b9fc61a2" -features = ["arduino-nano"] - -[profile.dev] -panic = "abort" -lto = true -opt-level = "s" - -[profile.release] -panic = "abort" -codegen-units = 1 -debug = true -lto = true -opt-level = "s" diff --git a/Ravedude.toml b/Ravedude.toml @@ -1,4 +0,0 @@ -[general] -board = "nano" -open-console = true -serial-baudrate = 57600 diff --git a/firmware/.cargo/config.toml b/firmware/.cargo/config.toml @@ -0,0 +1,12 @@ +[build] +target = "avr-none" + +[unstable] +build-std = ["core"] + +[target.'cfg(target_arch = "avr")'] +runner = "ravedude" +rustflags = ["-C", "target-cpu=atmega328p"] + +[alias] +flash = "run --release --" diff --git a/firmware/Cargo.lock b/firmware/Cargo.lock @@ -0,0 +1,1086 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "arduino-hal" +version = "0.1.0" +source = "git+https://github.com/rahix/avr-hal?rev=e5c8f37fe48419956e722490a82b9ca9b9fc61a2#e5c8f37fe48419956e722490a82b9ca9b9fc61a2" +dependencies = [ + "atmega-hal", + "avr-device", + "avr-hal-generic", + "cfg-if", + "embedded-hal 1.0.0", + "ufmt", +] + +[[package]] +name = "arduino-tnc" +version = "0.1.0" +dependencies = [ + "arduino-hal", + "avr-device", + "crc", + "embedded-hal 1.0.0", + "heapless", + "nb 1.1.0", + "panic-halt", + "tnc-core", + "trig-const", + "ufmt", +] + +[[package]] +name = "arraydeque" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d902e3d592a523def97af8f317b08ce16b7ab854c1985a0c671e6f15cebc236" + +[[package]] +name = "atdf2svd" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "179e5f106191a4d02437d8f66ed06f852e1a85774db29f934251d33cfec219fa" +dependencies = [ + "cfg-if", + "log", + "svd-encoder", + "svd-rs", + "xmltree 0.10.3", +] + +[[package]] +name = "atmega-hal" +version = "0.1.0" +source = "git+https://github.com/rahix/avr-hal?rev=e5c8f37fe48419956e722490a82b9ca9b9fc61a2#e5c8f37fe48419956e722490a82b9ca9b9fc61a2" +dependencies = [ + "avr-device", + "avr-hal-generic", +] + +[[package]] +name = "avr-device" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0584fb409e2ff23423979617c6b7ab34628727c8ef8026b665d41f41114060b9" +dependencies = [ + "anyhow", + "atdf2svd", + "avr-device-macros", + "bare-metal", + "cfg-if", + "critical-section", + "prettyplease", + "svd-rs", + "svd2rust", + "svdtools", + "syn 2.0.110", + "vcell", + "yaml-rust2", +] + +[[package]] +name = "avr-device-macros" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "129341bd5b981a038dd2ae0963550bfcb67ad56c64b53aa462cf1a0764405e97" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "avr-hal-generic" +version = "0.1.0" +source = "git+https://github.com/rahix/avr-hal?rev=e5c8f37fe48419956e722490a82b9ca9b9fc61a2#e5c8f37fe48419956e722490a82b9ca9b9fc61a2" +dependencies = [ + "avr-device", + "embedded-hal 0.2.7", + "embedded-hal 1.0.0", + "embedded-hal-bus", + "embedded-storage", + "nb 1.1.0", + "paste", + "ufmt", + "unwrap-infallible", +] + +[[package]] +name = "bare-metal" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fe8f5a8a398345e52358e18ff07cc17a568fbca5c6f73873d3a62056309603" + +[[package]] +name = "bstr" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "commands" +version = "0.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "688226b9769bbf11a9d82a94fb4adda15793a6023d33a8ca7695dbafec6f4123" + +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "crc" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "217698eaf96b4a3f0bc4f3662aaa55bdf913cd54d7204591faa790070c6d0853" + +[[package]] +name = "critical-section" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" + +[[package]] +name = "displaydoc" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.110", +] + +[[package]] +name = "either" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" + +[[package]] +name = "embedded-hal" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff" +dependencies = [ + "nb 0.1.3", + "void", +] + +[[package]] +name = "embedded-hal" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89" + +[[package]] +name = "embedded-hal-bus" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57b4e6ede84339ebdb418cd986e6320a34b017cdf99b5cc3efceec6450b06886" +dependencies = [ + "critical-section", + "embedded-hal 1.0.0", +] + +[[package]] +name = "embedded-storage" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "723dce4e9f25b6e6c5f35628e144794e5b459216ed7da97b7c4b66cdb3fa82ca" + +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "globset" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52dfc19153a48bde0cbd630453615c8151bce3a5adfac7a0aebfbf0a1e1f57e3" +dependencies = [ + "aho-corasick", + "bstr", + "log", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "hashlink" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" +dependencies = [ + "hashbrown 0.15.5", +] + +[[package]] +name = "heapless" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25ba4bd83f9415b58b4ed8dc5714c76e626a105be4646c02630ad730ad3b5aa4" +dependencies = [ + "hash32", + "stable_deref_trait", +] + +[[package]] +name = "icu_collections" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" +dependencies = [ + "displaydoc", + "potential_utf", + "utf8_iter", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" + +[[package]] +name = "icu_properties" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" + +[[package]] +name = "icu_provider" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", +] + +[[package]] +name = "inflections" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a257582fdcde896fd96463bf2d40eefea0580021c0712a0e2b028b60b47a837a" + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "litemap" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" + +[[package]] +name = "log" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "616ec5685824bcc94416c6d4a7a446eea774a31efd7062c8480ba6fd06d7a6e5" + +[[package]] +name = "memchr" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" + +[[package]] +name = "nb" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f" +dependencies = [ + "nb 1.1.0", +] + +[[package]] +name = "nb" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" + +[[package]] +name = "normpath" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9985ef7269fa99f3b12437bb698381da2428743ab90f20393f399fa14cab21a" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "panic-halt" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a513e167849a384b7f9b746e517604398518590a9142f4846a32e3c2a4de7b11" + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "phf" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" +dependencies = [ + "phf_macros", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" +dependencies = [ + "phf_shared", + "rand", +] + +[[package]] +name = "phf_macros" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", + "syn 2.0.110", +] + +[[package]] +name = "phf_shared" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +dependencies = [ + "siphasher", +] + +[[package]] +name = "potential_utf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" +dependencies = [ + "zerovec", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn 2.0.110", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quick-xml" +version = "0.37.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "roxmltree" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.110", +] + +[[package]] +name = "siphasher" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "svd-encoder" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0961dac31cc1ce3e673918792e826d6bdf5e4005b197acb996b793cfc732f83" +dependencies = [ + "convert_case", + "svd-rs", + "thiserror 1.0.69", + "xmltree 0.11.0", +] + +[[package]] +name = "svd-parser" +version = "0.14.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a41fe17e46dee363a7d3b20e878bbf6d6b4e839ae010ff07ef0e05d41d201811" +dependencies = [ + "anyhow", + "roxmltree", + "svd-rs", + "thiserror 1.0.69", +] + +[[package]] +name = "svd-rs" +version = "0.14.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9b88ee2e82f09623ff76965587dc15a2e7150a3126854899c86e94dab777458" +dependencies = [ + "once_cell", + "regex", + "serde", + "thiserror 1.0.69", +] + +[[package]] +name = "svd2rust" +version = "0.37.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1eaf6a040bfbc04e7b946a6a373b24aab09e1feb875bb0e20c929946c4b7bfc4" +dependencies = [ + "anyhow", + "inflections", + "log", + "proc-macro2", + "quote", + "regex", + "svd-parser", + "svd-rs", + "syn 2.0.110", + "thiserror 2.0.18", + "url", +] + +[[package]] +name = "svdtools" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cb2e48b200a352e10c4c50056b3f0a2da8e571c317d5af47c41ba672b2e9c87" +dependencies = [ + "anyhow", + "commands", + "globset", + "hashlink", + "itertools", + "log", + "normpath", + "once_cell", + "phf", + "quick-xml", + "regex", + "serde", + "svd-encoder", + "svd-parser", + "svd-rs", + "thiserror 1.0.69", + "yaml-rust2", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.110" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.110", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl 2.0.18", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.110", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.110", +] + +[[package]] +name = "tinystr" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tnc-core" +version = "0.1.0" + +[[package]] +name = "trig-const" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffec2fe2ed1e10dac46d8530afb395cae635fa02ef03d7fff1c211e94f9059fc" + +[[package]] +name = "ufmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a64846ec02b57e9108d6469d98d1648782ad6bb150a95a9baac26900bbeab9d" +dependencies = [ + "ufmt-macros", + "ufmt-write", +] + +[[package]] +name = "ufmt-macros" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d337d3be617449165cb4633c8dece429afd83f84051024079f97ad32a9663716" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ufmt-write" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e87a2ed6b42ec5e28cc3b94c09982969e9227600b2e3dcbc1db927a84c06bd69" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-segmentation" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" + +[[package]] +name = "unwrap-infallible" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "151ac09978d3c2862c4e39b557f4eceee2cc72150bc4cb4f16abf061b6e381fb" + +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", + "serde_derive", +] + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "vcell" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002" + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "writeable" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" + +[[package]] +name = "xml-rs" +version = "0.8.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f" + +[[package]] +name = "xmltree" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7d8a75eaf6557bb84a65ace8609883db44a29951042ada9b393151532e41fcb" +dependencies = [ + "xml-rs", +] + +[[package]] +name = "xmltree" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b619f8c85654798007fb10afa5125590b43b088c225a25fc2fec100a9fad0fc6" +dependencies = [ + "indexmap", + "xml-rs", +] + +[[package]] +name = "yaml-rust2" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2462ea039c445496d8793d052e13787f2b90e750b833afee748e601c17621ed9" +dependencies = [ + "arraydeque", + "encoding_rs", + "hashlink", +] + +[[package]] +name = "yoke" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.110", + "synstructure", +] + +[[package]] +name = "zerofrom" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.110", + "synstructure", +] + +[[package]] +name = "zerotrie" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.110", +] diff --git a/firmware/Cargo.toml b/firmware/Cargo.toml @@ -0,0 +1,41 @@ +[package] +name = "arduino-tnc" +version = "0.1.0" +authors = ["Bradley Gannon <bradley@bradleygannon.com>"] +edition = "2021" +license = "MIT OR Apache-2.0" + +[[bin]] +bench = false +name = "arduino-tnc" +test = false + +[dependencies] +avr-device = "0.8.1" +crc = "3.4.0" +embedded-hal = "1.0" +heapless = "0.9.2" +nb = "1.1.0" +panic-halt = "1.0.0" +trig-const = "0.4.0" +ufmt = "0.2.0" + +[dependencies.arduino-hal] +git = "https://github.com/rahix/avr-hal" +rev = "e5c8f37fe48419956e722490a82b9ca9b9fc61a2" +features = ["arduino-nano"] + +[dependencies.tnc-core] +path = "../tnc-core" + +[profile.dev] +opt-level = "s" +lto = true +panic = "abort" + +[profile.release] +opt-level = "s" +debug = true +lto = true +panic = "abort" +codegen-units = 1 diff --git a/firmware/Ravedude.toml b/firmware/Ravedude.toml @@ -0,0 +1,4 @@ +[general] +board = "nano" +open-console = true +serial-baudrate = 115200 diff --git a/rust-toolchain.toml b/firmware/rust-toolchain.toml diff --git a/src/kiss.rs b/firmware/src/kiss.rs diff --git a/firmware/src/main.rs b/firmware/src/main.rs @@ -0,0 +1,182 @@ +#![no_std] +#![no_main] +#![feature(abi_avr_interrupt)] + +mod kiss; +mod rx; +mod tx; + +use core::cell::RefCell; +use core::hint::spin_loop; + +use arduino_hal::clock::MHz16; +use arduino_hal::hal::usart::{Event, Usart0}; +use arduino_hal::{pac, Adc}; +use avr_device::generic::Periph; +use avr_device::interrupt::{self, Mutex}; +use kiss::{KissAction, KissTncState}; +use panic_halt as _; +use rx::Receiver; +use tx::Transmitter; + +type TC0 = Periph<pac::tc0::RegisterBlock, 53>; +type TC1 = Periph<pac::tc1::RegisterBlock, 54>; + +struct Tnc { + state: TncState, + serial: Option<Usart0<MHz16>>, + receiver: Receiver, + transmitter: Transmitter, + kiss_state: KissTncState, + sample_timer: Option<TC0>, + symbol_timer: Option<TC1>, +} + +impl Tnc { + // 52 * 8 = 416 cycles; at 16 MHz this gives a period of 26 µs (about 38460 Hz) + const SAMPLE_TIMER_COMPARE: u8 = 52; + // this should theoretically be 13_333 cycles, but in practice we have to go a little faster to + // account for overhead; this value seems to work well + const SYMBOL_TIMER_COMPARE: u16 = 13_300; + + const fn default() -> Tnc { + Tnc { + state: TncState::Receiving, + serial: None, + receiver: Receiver::default(), + transmitter: Transmitter::default(), + kiss_state: KissTncState::Idle, + sample_timer: None, + symbol_timer: None, + } + } + pub(crate) fn handle_usart_byte(&mut self, byte: u8) { + if let Some(action) = self.kiss_state.update(byte) { + match action { + KissAction::PushTx(b) => { + let _ = self.transmitter.push_message_byte(b); + } + KissAction::ClearTx => self.transmitter.clear_message(), + KissAction::SendTx => { + if self.transmitter.has_pending_bytes() { + self.state = TncState::Transmitting; + self.transmitter.start(); + } + } + KissAction::_SetTxDelay { .. } => todo!(), + KissAction::_SetPersistence { .. } => todo!(), + KissAction::_SetSlotTime { .. } => todo!(), + KissAction::_SetTxTail { .. } => todo!(), + KissAction::_SetFullDuplex { .. } => todo!(), + KissAction::_SetHardware { .. } => todo!(), + KissAction::_Return => todo!(), + } + }; + } +} + +enum TncState { + Receiving, + Transmitting, +} + +static TNC: Mutex<RefCell<Tnc>> = Mutex::new(RefCell::new(Tnc::default())); + +#[avr_device::interrupt(atmega328p)] +fn TIMER0_COMPA() { + interrupt::free(|cs| { + let mut tnc = TNC.borrow(cs).borrow_mut(); + match tnc.state { + TncState::Receiving => { + tnc.receiver.sample_update(); + if tnc.receiver.is_at_symbol_transition() { + if let Some(ref mut symbol_timer) = tnc.symbol_timer { + symbol_timer + .tcnt1() + .write(|w| w.set(Tnc::SYMBOL_TIMER_COMPARE / 2)); + } + } + if let Some(byte) = tnc.receiver.get_output() { + if let Some(ref mut serial) = tnc.serial { + let _ = ufmt::uwriteln!(serial, "0x{:02x}", byte); + } + } + } + TncState::Transmitting => { + tnc.transmitter.sample_update(); + if tnc.transmitter.is_done() { + tnc.state = TncState::Receiving; + } + } + } + }); +} + +#[avr_device::interrupt(atmega328p)] +fn TIMER1_COMPA() { + interrupt::free(|cs| { + let mut tnc = TNC.borrow(cs).borrow_mut(); + match tnc.state { + TncState::Receiving => tnc.receiver.symbol_update(), + TncState::Transmitting => tnc.transmitter.symbol_update(), + } + }); +} + +#[avr_device::interrupt(atmega328p)] +fn USART_RX() { + interrupt::free(|cs| { + let mut tnc = TNC.borrow(cs).borrow_mut(); + if let Some(ref mut serial) = tnc.serial { + let byte = serial.read_byte(); + tnc.handle_usart_byte(byte); + } + }); +} + +#[arduino_hal::entry] +fn main() -> ! { + let dp = arduino_hal::Peripherals::take().unwrap(); + let pins = arduino_hal::pins!(dp); + + let mut serial = arduino_hal::default_serial!(dp, pins, 115200); + serial.listen(Event::RxComplete); + + let sample_timer = dp.TC0; + sample_timer.tccr0a().write(|w| w.wgm0().ctc()); + sample_timer.tccr0b().write(|w| w.cs0().prescale_8()); + sample_timer + .ocr0a() + .write(|w| w.set(Tnc::SAMPLE_TIMER_COMPARE)); + sample_timer.timsk0().write(|w| w.ocie0a().set_bit()); + + let symbol_timer = dp.TC1; + symbol_timer + .tccr1b() + .write(|w| w.wgm1().set(0b01).cs1().direct()); + symbol_timer + .ocr1a() + .write(|w| w.set(Tnc::SYMBOL_TIMER_COMPARE)); + symbol_timer.timsk1().write(|w| w.ocie1a().set_bit()); + + interrupt::free(|cs| { + let mut tnc = TNC.borrow(cs).borrow_mut(); + tnc.sample_timer = Some(sample_timer); + tnc.symbol_timer = Some(symbol_timer); + tnc.receiver + .initialize(Adc::new(dp.ADC, Default::default()), pins.a0); + tnc.transmitter.initialize( + pins.d2.into_output(), + pins.d3.into_output(), + pins.d4.into_output(), + pins.d5.into_output(), + pins.d6.into_output(), + ); + }); + + unsafe { avr_device::interrupt::enable() }; + + loop { + spin_loop(); + } +} diff --git a/firmware/src/rx.rs b/firmware/src/rx.rs @@ -0,0 +1,44 @@ +use arduino_hal::{ + hal::port::PC0, + port::{ + mode::{Analog, Floating, Input}, + Pin, + }, + Adc, +}; +use tnc_core::goertzel::Bell202Demodulator; + +pub(crate) struct Receiver { + adc: Option<Adc>, + input: Option<Pin<Analog, PC0>>, + demod: Bell202Demodulator, +} + +impl Receiver { + pub(crate) const fn default() -> Receiver { + Receiver { + adc: None, + input: None, + demod: Bell202Demodulator::new(), + } + } + pub(crate) fn initialize(&mut self, mut adc: Adc, input: Pin<Input<Floating>, PC0>) { + self.input = Some(input.into_analog_input(&mut adc)); + self.adc = Some(adc); + } + pub(crate) fn is_at_symbol_transition(&self) -> bool { + self.demod.get_output().is_some() + } + pub(crate) fn get_output(&self) -> Option<u8> { + self.demod.get_output() + } + pub(crate) fn sample_update(&mut self) { + if let (Some(ref input), Some(ref mut adc)) = (&self.input, &mut self.adc) { + let new_sample = input.analog_read(adc) as f32 / 512. - 0.5; + self.demod.sample_update(new_sample); + } + } + pub(crate) fn symbol_update(&mut self) { + self.demod.symbol_update(); + } +} diff --git a/src/tx.rs b/firmware/src/tx.rs diff --git a/flake.lock b/flake.lock @@ -46,11 +46,11 @@ }, "stablePkgs": { "locked": { - "lastModified": 1779467186, - "narHash": "sha256-nOesoDCiXcUftqbRBMz9tt4blI5PvljMWbm3kuCA+0s=", + "lastModified": 1779796641, + "narHash": "sha256-ZsIrKmhp4vbBXoXXmR/tBXA/UCsAQiJL9vsgZEduhVY=", "owner": "nixos", "repo": "nixpkgs", - "rev": "b77b3de8775677f84492abe84635f87b0e153f0f", + "rev": "25f538306313eae3927264466c70d7001dcea1df", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix @@ -26,7 +26,7 @@ inherit system; }; rust = fenix.fromToolchainFile { - file = ./rust-toolchain.toml; + file = ./firmware/rust-toolchain.toml; sha256 = "sha256-DnyK5MS+xYySA+csnnMogu2gtEfyiy10W0ATmAvmjGg="; }; libInputs = [ stable.stdenv.cc.cc.lib ]; diff --git a/src/main.rs b/src/main.rs @@ -1,182 +0,0 @@ -#![no_std] -#![no_main] -#![feature(abi_avr_interrupt)] - -mod kiss; -mod rx; -mod tx; - -use core::cell::RefCell; -use core::hint::spin_loop; - -use arduino_hal::clock::MHz16; -use arduino_hal::hal::usart::{Event, Usart0}; -use arduino_hal::{pac, Adc}; -use avr_device::generic::Periph; -use avr_device::interrupt::{self, Mutex}; -use kiss::{KissAction, KissTncState}; -use panic_halt as _; -use rx::Receiver; -use tx::Transmitter; - -type TC0 = Periph<pac::tc0::RegisterBlock, 53>; -type TC1 = Periph<pac::tc1::RegisterBlock, 54>; - -struct Tnc { - state: TncState, - serial: Option<Usart0<MHz16>>, - receiver: Receiver, - transmitter: Transmitter, - kiss_state: KissTncState, - sample_timer: Option<TC0>, - symbol_timer: Option<TC1>, -} - -impl Tnc { - // 52 * 8 = 416 cycles; at 16 MHz this gives a period of 26 µs (about 38460 Hz) - const SAMPLE_TIMER_COMPARE: u8 = 52; - // this should theoretically be 13_333 cycles, but in practice we have to go a little faster to - // account for overhead; this value seems to work well - const SYMBOL_TIMER_COMPARE: u16 = 13_300; - - const fn default() -> Tnc { - Tnc { - state: TncState::Receiving, - serial: None, - receiver: Receiver::default(), - transmitter: Transmitter::default(), - kiss_state: KissTncState::Idle, - sample_timer: None, - symbol_timer: None, - } - } - pub(crate) fn handle_usart_byte(&mut self, byte: u8) { - if let Some(action) = self.kiss_state.update(byte) { - match action { - KissAction::PushTx(b) => { - let _ = self.transmitter.push_message_byte(b); - } - KissAction::ClearTx => self.transmitter.clear_message(), - KissAction::SendTx => { - if self.transmitter.has_pending_bytes() { - self.state = TncState::Transmitting; - self.transmitter.start(); - } - } - KissAction::_SetTxDelay { .. } => todo!(), - KissAction::_SetPersistence { .. } => todo!(), - KissAction::_SetSlotTime { .. } => todo!(), - KissAction::_SetTxTail { .. } => todo!(), - KissAction::_SetFullDuplex { .. } => todo!(), - KissAction::_SetHardware { .. } => todo!(), - KissAction::_Return => todo!(), - } - }; - } -} - -enum TncState { - Receiving, - Transmitting, -} - -static TNC: Mutex<RefCell<Tnc>> = Mutex::new(RefCell::new(Tnc::default())); - -#[avr_device::interrupt(atmega328p)] -fn TIMER0_COMPA() { - interrupt::free(|cs| { - let mut tnc = TNC.borrow(cs).borrow_mut(); - match tnc.state { - TncState::Receiving => { - tnc.receiver.sample_update(); - if tnc.receiver.is_at_symbol_transition() { - if let Some(ref mut symbol_timer) = tnc.symbol_timer { - symbol_timer - .tcnt1() - .write(|w| w.set(Tnc::SYMBOL_TIMER_COMPARE / 2)); - } - } - if let Some(byte) = tnc.receiver.get_output() { - if let Some(ref mut serial) = tnc.serial { - let _ = ufmt::uwriteln!(serial, "0x{:02x}", byte); - } - } - } - TncState::Transmitting => { - tnc.transmitter.sample_update(); - if tnc.transmitter.is_done() { - tnc.state = TncState::Receiving; - } - } - } - }); -} - -#[avr_device::interrupt(atmega328p)] -fn TIMER1_COMPA() { - interrupt::free(|cs| { - let mut tnc = TNC.borrow(cs).borrow_mut(); - match tnc.state { - TncState::Receiving => tnc.receiver.symbol_update(), - TncState::Transmitting => tnc.transmitter.symbol_update(), - } - }); -} - -#[avr_device::interrupt(atmega328p)] -fn USART_RX() { - interrupt::free(|cs| { - let mut tnc = TNC.borrow(cs).borrow_mut(); - if let Some(ref mut serial) = tnc.serial { - let byte = serial.read_byte(); - tnc.handle_usart_byte(byte); - } - }); -} - -#[arduino_hal::entry] -fn main() -> ! { - let dp = arduino_hal::Peripherals::take().unwrap(); - let pins = arduino_hal::pins!(dp); - - let mut serial = arduino_hal::default_serial!(dp, pins, 57600); - serial.listen(Event::RxComplete); - - let sample_timer = dp.TC0; - sample_timer.tccr0a().write(|w| w.wgm0().ctc()); - sample_timer.tccr0b().write(|w| w.cs0().prescale_8()); - sample_timer - .ocr0a() - .write(|w| w.set(Tnc::SAMPLE_TIMER_COMPARE)); - sample_timer.timsk0().write(|w| w.ocie0a().set_bit()); - - let symbol_timer = dp.TC1; - symbol_timer - .tccr1b() - .write(|w| w.wgm1().set(0b01).cs1().direct()); - symbol_timer - .ocr1a() - .write(|w| w.set(Tnc::SYMBOL_TIMER_COMPARE)); - symbol_timer.timsk1().write(|w| w.ocie1a().set_bit()); - - interrupt::free(|cs| { - let mut tnc = TNC.borrow(cs).borrow_mut(); - tnc.sample_timer = Some(sample_timer); - tnc.symbol_timer = Some(symbol_timer); - tnc.receiver - .initialize(Adc::new(dp.ADC, Default::default()), pins.a0); - tnc.transmitter.initialize( - pins.d2.into_output(), - pins.d3.into_output(), - pins.d4.into_output(), - pins.d5.into_output(), - pins.d6.into_output(), - ); - }); - - unsafe { avr_device::interrupt::enable() }; - - loop { - spin_loop(); - } -} diff --git a/src/rx.rs b/src/rx.rs @@ -1,126 +0,0 @@ -use core::f32::consts::TAU; - -use arduino_hal::{ - hal::port::PC0, - port::{ - mode::{Analog, Floating, Input}, - Pin, - }, - Adc, -}; -use trig_const::cos; - -#[derive(PartialEq, Clone, Copy)] -enum Symbol { - Mark, - Space, -} - -struct GoertzelFilter { - coeff: f32, - s0: f32, - s1: f32, - s2: f32, - energy: f32, -} - -impl GoertzelFilter { - const fn new(frequency: f32, sample_rate: f32) -> Self { - GoertzelFilter { - coeff: 2. * cos((TAU * frequency / sample_rate) as f64) as f32, - s0: 0., - s1: 0., - s2: 0., - energy: 0., - } - } - fn update(&mut self, x: f32) { - self.s0 = x + self.coeff * self.s1 - self.s2; - self.s2 = self.s1; - self.s1 = self.s0; - self.energy = self.s1 * self.s1 + self.s2 * self.s2 - self.coeff * self.s1 * self.s2; - } - fn energy(&self) -> f32 { - self.energy - } -} - -pub(crate) struct Receiver { - adc: Option<Adc>, - input: Option<Pin<Analog, PC0>>, - mark: GoertzelFilter, - space: GoertzelFilter, - symbol_estimate: Symbol, - previous_symbol_estimate: Symbol, - symbol_phase: u8, - byte: u8, - byte_phase: u8, - output: Option<u8>, -} - -impl Receiver { - const MARK_HZ: f32 = 1200.; - const SPACE_HZ: f32 = 2200.; - const SAMPLE_RATE_HZ: f32 = 38400.; - const SYMBOL_RATE_HZ: f32 = 1200.; - const SAMPLES_PER_SYMBOL: u8 = (Self::SAMPLE_RATE_HZ / Self::SYMBOL_RATE_HZ) as u8; - - pub(crate) const fn default() -> Receiver { - Receiver { - adc: None, - input: None, - mark: GoertzelFilter::new(Receiver::MARK_HZ, Receiver::SAMPLE_RATE_HZ), - space: GoertzelFilter::new(Receiver::SPACE_HZ, Receiver::SAMPLE_RATE_HZ), - symbol_estimate: Symbol::Space, - previous_symbol_estimate: Symbol::Space, - symbol_phase: 0, - byte: 0, - byte_phase: 0, - output: None, - } - } - pub(crate) fn initialize(&mut self, mut adc: Adc, input: Pin<Input<Floating>, PC0>) { - self.input = Some(input.into_analog_input(&mut adc)); - self.adc = Some(adc); - } - pub(crate) fn is_at_symbol_transition(&self) -> bool { - self.symbol_phase == 0 - } - pub(crate) fn get_output(&self) -> Option<u8> { - self.output - } - pub(crate) fn sample_update(&mut self) { - if let (Some(ref input), Some(ref mut adc)) = (&self.input, &mut self.adc) { - let new_sample = input.analog_read(adc) as f32 / 512. - 0.5; - self.mark.update(new_sample); - self.space.update(new_sample); - let new_symbol_estimate = if self.mark.energy() > self.space.energy() { - Symbol::Mark - } else { - Symbol::Space - }; - if new_symbol_estimate != self.symbol_estimate { - self.symbol_phase = 0; - } else { - self.symbol_phase = self.symbol_phase.saturating_add(1) % Self::SAMPLES_PER_SYMBOL; - } - self.symbol_estimate = new_symbol_estimate; - } - } - pub(crate) fn symbol_update(&mut self) { - self.byte <<= 1; - if self.symbol_estimate == self.previous_symbol_estimate { - self.byte += 1; - } - self.byte_phase = (self.byte_phase + 1) % 8; - if self.byte == 0x7e { - self.byte_phase = 0; - } - if self.byte_phase == 0 { - self.output = Some(self.byte); - } else { - self.output = None; - } - self.previous_symbol_estimate = self.symbol_estimate; - } -} diff --git a/tnc-core/.cargo/config.toml b/tnc-core/.cargo/config.toml @@ -0,0 +1,5 @@ +# Override the workspace root's AVR target + build-std for this package. +# x86_64 has a pre-built core, so build-std from the parent is effectively +# a no-op for this crate. +[build] +target = "x86_64-unknown-linux-gnu" diff --git a/tnc-core/Cargo.lock b/tnc-core/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "tnc-core" +version = "0.1.0" diff --git a/tnc-core/Cargo.toml b/tnc-core/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "tnc-core" +version = "0.1.0" +edition = "2021" diff --git a/tnc-core/src/goertzel.rs b/tnc-core/src/goertzel.rs @@ -0,0 +1,155 @@ +//! Block-based Goertzel demodulation for Bell 202 AFSK (mark @ 1200 Hz, space @ 2200 Hz). + +/// A single Goertzel resonator. +#[derive(Clone, Copy, Debug)] +pub struct GoertzelFilter { + /// 2cos(2πf / sample_rate) + pub coeff: f32, + pub s0: f32, + pub s1: f32, + pub s2: f32, + pub energy: f32, +} + +impl GoertzelFilter { + /// Create a new Goertzel filter with a pre-computed coefficient. + /// + /// The coefficient should be `2cos(2πf / sample_rate)`. We can't easily compute the + /// coefficient inside this method because it's `const`. + pub const fn new_with_coeff(coeff: f32) -> Self { + Self { + coeff, + s0: 0.0, + s1: 0.0, + s2: 0.0, + energy: 0.0, + } + } + + pub const fn mark() -> Self { + Self::new_with_coeff(1.961_69) + } + + pub const fn space() -> Self { + Self::new_with_coeff(1.872_207_2) + } + + /// Feed one sample through the resonator. + /// + /// After calling `update`, the `energy` field contains the squared energy of the current + /// block. For block-based usage, reset `s0`, `s1`, `s2` to 0.0 at each symbol boundary. + pub fn update(&mut self, x: f32) { + self.s0 = x + self.coeff * self.s1 - self.s2; + self.s2 = self.s1; + self.s1 = self.s0; + self.energy = self.s1 * self.s1 + self.s2 * self.s2 - self.coeff * self.s1 * self.s2; + } + + pub fn energy(&self) -> f32 { + self.energy + } + + pub fn reset(&mut self) { + self.s0 = 0.0; + self.s1 = 0.0; + self.s2 = 0.0; + self.energy = 0.0; + } +} + +/// A Bell 202 demodulator that produces byte-aligned output using NRZI decoding and HDLC flag +/// detection. +pub struct Bell202Demodulator { + mark: GoertzelFilter, + space: GoertzelFilter, + symbol_estimate: Symbol, + previous_symbol_estimate: Symbol, + symbol_phase: u8, + byte: u8, + byte_phase: u8, + output: Option<u8>, +} + +/// A decoded symbol: mark (1200 Hz) or space (2200 Hz). +#[derive(PartialEq, Clone, Copy, Debug)] +pub enum Symbol { + Mark, + Space, +} + +impl Bell202Demodulator { + pub const SAMPLE_RATE_HZ: f32 = 38460.; + pub const MARK_HZ: f32 = 1200.; + pub const SPACE_HZ: f32 = 2200.; + pub const SYMBOL_RATE_HZ: f32 = 1200.; + pub const SAMPLES_PER_SYMBOL: u8 = (Self::SAMPLE_RATE_HZ / Self::SYMBOL_RATE_HZ) as u8; + + pub const fn new() -> Self { + Self { + mark: GoertzelFilter::mark(), + space: GoertzelFilter::space(), + symbol_estimate: Symbol::Space, + previous_symbol_estimate: Symbol::Space, + symbol_phase: 0, + byte: 0, + byte_phase: 0, + output: None, + } + } + + /// Feed one sample into the Goertzel resonators. + /// + /// This should be called at the configured sample rate. + pub fn sample_update(&mut self, sample: f32) { + self.mark.update(sample); + self.space.update(sample); + let new_symbol_estimate = if self.mark.energy() > self.space.energy() { + Symbol::Mark + } else { + Symbol::Space + }; + if new_symbol_estimate != self.symbol_estimate { + self.symbol_phase = 0; + } else { + self.symbol_phase = self.symbol_phase.saturating_add(1) % Self::SAMPLES_PER_SYMBOL; + } + self.symbol_estimate = new_symbol_estimate; + } + + /// Called once per symbol period. Produces NRZI-decoded, byte-aligned output. + /// + /// The demodulator outputs a byte whenever `0x7e` (HDLC flag) is detected in the NRZI stream. + /// Returns the output byte, or `None` if no flag was found. + pub fn symbol_update(&mut self) -> Option<u8> { + self.byte <<= 1; + if self.symbol_estimate == self.previous_symbol_estimate { + self.byte += 1; + } + self.byte_phase = (self.byte_phase + 1) % 8; + if self.byte == 0x7e { + self.byte_phase = 0; + } + if self.byte_phase == 0 { + self.output = Some(self.byte); + } else { + self.output = None; + } + self.previous_symbol_estimate = self.symbol_estimate; + self.output + } + + pub fn reset(&mut self) { + self.mark.reset(); + self.space.reset(); + self.symbol_estimate = Symbol::Space; + self.previous_symbol_estimate = Symbol::Space; + self.symbol_phase = 0; + self.byte = 0; + self.byte_phase = 0; + self.output = None; + } + + pub fn get_output(&self) -> Option<u8> { + self.output + } +} diff --git a/tnc-core/src/lib.rs b/tnc-core/src/lib.rs @@ -0,0 +1,7 @@ +//! Shared, hardware-agnostic algorithms for the Arduino TNC. +//! +//! To run tests: `cargo test` (from workspace root or from tnc-core/) + +#![no_std] + +pub mod goertzel; diff --git a/tnc-core/tests/demodulator_test.rs b/tnc-core/tests/demodulator_test.rs @@ -0,0 +1,262 @@ +use std::f64::consts::TAU; + +use tnc_core::goertzel::{Bell202Demodulator, GoertzelFilter, Symbol}; + +const SAMPLE_RATE: f32 = 38460.; + +fn generate_audio_phase_continuous( + symbols: &[Symbol], + sample_rate: f32, + mark_hz: f32, + space_hz: f32, +) -> Vec<f32> { + let samples_per_symbol = (sample_rate / 1200.) as usize; + let mut audio = Vec::with_capacity(symbols.len() * samples_per_symbol); + + let mut phase = 0_f64; + let dt = 1. / sample_rate as f64; + + for sym in symbols { + let freq = match sym { + Symbol::Mark => mark_hz as f64, + Symbol::Space => space_hz as f64, + }; + let phase_inc = freq * dt * TAU; + + for _ in 0..samples_per_symbol { + audio.push(phase.sin() as f32); + phase += phase_inc; + } + } + + audio +} + +struct Rng(u64); + +impl Rng { + fn new(seed: u64) -> Self { + Self(seed) + } + fn next(&mut self) -> u64 { + self.0 = self + .0 + .wrapping_mul(6364136223846793005) + .wrapping_add(1442695040888963407); + self.0 + } + fn uniform(&mut self) -> f32 { + self.next() as f32 / u64::MAX as f32 + } +} + +fn generate_symbols(n: usize, seed: u64) -> Vec<Symbol> { + let mut rng = Rng::new(seed); + (0..n) + .map(|_| { + if rng.next() % 2 == 0 { + Symbol::Mark + } else { + Symbol::Space + } + }) + .collect() +} + +fn add_noise(samples: &[f32], noise_std: f32, seed: u64) -> Vec<f32> { + let mut rng = Rng::new(seed); + samples + .iter() + .map(|&s| { + if noise_std <= 0.0 { + return s; + } + let normal: f32 = (0..12).map(|_| rng.uniform() - 0.5).sum::<f32>() / (3_f32).sqrt(); + s + normal * noise_std + }) + .collect() +} + +#[test] +fn mark_only_tone_yields_mark_decisions() { + let mut demod = Bell202Demodulator::new(); + let sps = Bell202Demodulator::SAMPLES_PER_SYMBOL as usize; + let total_samples = 64 * sps; + let dt = 1. / SAMPLE_RATE as f64; + let mut phase = 0_f64; + let phase_inc = 1200. * dt * TAU; + + for i in 0..total_samples { + let sample = phase.sin() as f32; + demod.sample_update(sample); + if i > 0 && i % sps == 0 { + demod.symbol_update(); + } + phase += phase_inc; + } +} + +#[test] +fn space_only_tone_yields_space_decisions() { + let mut demod = Bell202Demodulator::new(); + let sps = Bell202Demodulator::SAMPLES_PER_SYMBOL as usize; + let total_samples = 64 * sps; + let dt = 1. / SAMPLE_RATE as f64; + let mut phase = 0_f64; + let phase_inc = 2200. * dt * TAU; + + for i in 0..total_samples { + let sample = phase.sin() as f32; + demod.sample_update(sample); + if i > 0 && i % sps == 0 { + demod.symbol_update(); + } + phase += phase_inc; + } +} + +#[test] +fn alternating_symbols_no_noise() { + let mut symbols = Vec::with_capacity(64); + for i in 0..64 { + symbols.push(if i % 2 == 0 { + Symbol::Mark + } else { + Symbol::Space + }); + } + let audio = generate_audio_phase_continuous(&symbols, SAMPLE_RATE, 1200., 2200.); + let sps = Bell202Demodulator::SAMPLES_PER_SYMBOL as usize; + + let mut mark = GoertzelFilter::mark(); + let mut space = GoertzelFilter::space(); + let mut correct = 0usize; + let mut total = 0usize; + + for (i, &sample) in audio.iter().enumerate() { + mark.update(sample); + space.update(sample); + if i > 0 && i % sps == 0 { + total += 1; + let expected_mark = symbols[i / sps - 1] == Symbol::Mark; + if (mark.energy() > space.energy()) == expected_mark { + correct += 1; + } + mark.reset(); + space.reset(); + } + } + + let accuracy = correct as f64 / total as f64; + assert!( + accuracy >= 0.95, + "Symbol accuracy should be >= 95% for clean alternating symbols, got {:.1}%", + accuracy * 100.0 + ); +} + +#[test] +fn random_symbols_no_noise() { + let symbols = generate_symbols(200, 42); + let audio = generate_audio_phase_continuous(&symbols, SAMPLE_RATE, 1200.0, 2200.0); + let sps = Bell202Demodulator::SAMPLES_PER_SYMBOL as usize; + + let mut mark = GoertzelFilter::mark(); + let mut space = GoertzelFilter::space(); + let mut correct = 0usize; + let mut total = 0usize; + + for (i, &sample) in audio.iter().enumerate() { + mark.update(sample); + space.update(sample); + if i > 0 && i % sps == 0 { + total += 1; + let expected_mark = symbols[i / sps - 1] == Symbol::Mark; + if (mark.energy() > space.energy()) == expected_mark { + correct += 1; + } + mark.reset(); + space.reset(); + } + } + + let accuracy = correct as f64 / total as f64; + assert!( + accuracy >= 0.99, + "Symbol accuracy should be >= 99% for clean random symbols, got {:.1}% ({} / {})", + accuracy * 100.0, + correct, + total + ); +} + +#[test] +fn moderate_noise_accuracy() { + let noise_std = 0.5 * 10.0f32.powf(-15.0 / 20.0); + let symbols = generate_symbols(200, 42); + let audio = generate_audio_phase_continuous(&symbols, SAMPLE_RATE, 1200., 2200.); + let noisy = add_noise(&audio, noise_std, 12345); + let sps = Bell202Demodulator::SAMPLES_PER_SYMBOL as usize; + + let mut mark = GoertzelFilter::mark(); + let mut space = GoertzelFilter::space(); + let mut correct = 0usize; + let mut total = 0usize; + + for (i, &sample) in noisy.iter().enumerate() { + mark.update(sample); + space.update(sample); + if i > 0 && i % sps == 0 { + total += 1; + let expected_mark = symbols[i / sps - 1] == Symbol::Mark; + if (mark.energy() > space.energy()) == expected_mark { + correct += 1; + } + mark.reset(); + space.reset(); + } + } + + let accuracy = correct as f64 / total as f64; + assert!( + accuracy >= 0.90, + "Symbol accuracy at ~15 dB SNR should be >= 90%, got {:.1}% ({} / {})", + accuracy * 100.0, + correct, + total + ); +} + +#[test] +fn frequency_offset_tolerance() { + let space_hz = 3200_f32; + let symbols = generate_symbols(100, 42); + let audio = generate_audio_phase_continuous(&symbols, SAMPLE_RATE, 1200.0, space_hz); + let sps = Bell202Demodulator::SAMPLES_PER_SYMBOL as usize; + + let mut mark = GoertzelFilter::mark(); + let mut space = GoertzelFilter::space(); + let mut correct = 0usize; + let mut total = 0usize; + + for (i, &sample) in audio.iter().enumerate() { + mark.update(sample); + space.update(sample); + if i > 0 && i % sps == 0 { + total += 1; + let expected_mark = symbols[i / sps - 1] == Symbol::Mark; + if (mark.energy() > space.energy()) == expected_mark { + correct += 1; + } + mark.reset(); + space.reset(); + } + } + + let accuracy = correct as f64 / total as f64; + assert!( + accuracy >= 0.95, + "Symbol accuracy with +1000 Hz space offset should be >= 95%, got {:.1}%", + accuracy * 100.0 + ); +} diff --git a/tnc-core/tests/goertzel_filter_test.rs b/tnc-core/tests/goertzel_filter_test.rs @@ -0,0 +1,78 @@ +use tnc_core::goertzel::GoertzelFilter; + +const SAMPLE_RATE: f32 = 38460.; + +fn generate_tone(frequency: f32, duration_samples: usize, sample_rate: f32) -> Vec<f32> { + let mut samples = Vec::new(); + let dt = 1. / sample_rate; + for i in 0..duration_samples { + let phase = 2.0 * std::f32::consts::PI * frequency * dt * i as f32; + samples.push(phase.sin()); + } + samples +} + +#[test] +fn mark_tone_gives_higher_mark_energy() { + let mut mark = GoertzelFilter::mark(); + let mut space = GoertzelFilter::space(); + + let samples = generate_tone(1200.0, 32, SAMPLE_RATE); + + for &sample in &samples { + mark.update(sample); + space.update(sample); + } + + assert!( + mark.energy() > space.energy(), + "Mark resonator should have higher energy than space resonator for 1200 Hz tone. \ + mark_energy={}, space_energy={}", + mark.energy(), + space.energy() + ); +} + +#[test] +fn space_tone_gives_higher_space_energy() { + let mut mark = GoertzelFilter::mark(); + let mut space = GoertzelFilter::space(); + + let samples = generate_tone(2200.0, 32, SAMPLE_RATE); + + for &sample in &samples { + mark.update(sample); + space.update(sample); + } + + assert!( + space.energy() > mark.energy(), + "Space resonator should have higher energy than mark resonator for 2200 Hz tone. \ + mark_energy={}, space_energy={}", + mark.energy(), + space.energy() + ); +} + +#[test] +fn filter_reset_clears_states() { + let mut filter = GoertzelFilter::mark(); + + let samples = generate_tone(1200.0, 32, SAMPLE_RATE); + for &sample in &samples { + filter.update(sample); + } + + let energy_before = filter.energy(); + assert!( + energy_before > 0.0, + "Filter should have non-zero energy after feeding samples" + ); + + filter.reset(); + + assert_eq!(filter.s0, 0.0, "s0 should be reset to 0.0"); + assert_eq!(filter.s1, 0.0, "s1 should be reset to 0.0"); + assert_eq!(filter.s2, 0.0, "s2 should be reset to 0.0"); + assert_eq!(filter.energy, 0.0, "energy should be reset to 0.0"); +}