diff --git a/Cargo.lock b/Cargo.lock
index c369378739918a41d84521ac1550dd4d41cc96a7..c1304bff3b985361e6b3eea67f8a90367928f7e7 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -76,7 +76,7 @@ version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
 dependencies = [
- "windows-sys",
+ "windows-sys 0.48.0",
 ]
 
 [[package]]
@@ -86,7 +86,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628"
 dependencies = [
  "anstyle",
- "windows-sys",
+ "windows-sys 0.48.0",
 ]
 
 [[package]]
@@ -275,7 +275,8 @@ dependencies = [
  "futures-core",
  "half",
  "ham-cats",
- "prost 0.12.2",
+ "prost 0.12.3",
+ "rand",
  "rf4463",
  "rppal",
  "serde",
@@ -313,14 +314,14 @@ dependencies = [
  "iana-time-zone",
  "num-traits",
  "serde",
- "windows-targets",
+ "windows-targets 0.48.5",
 ]
 
 [[package]]
 name = "clap"
-version = "4.4.8"
+version = "4.4.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2275f18819641850fa26c89acc84d465c1bf91ce57bc2748b28c420473352f64"
+checksum = "41fffed7514f420abec6d183b1d3acfd9099c79c3a10a06ade4f8203f1411272"
 dependencies = [
  "clap_builder",
  "clap_derive",
@@ -328,9 +329,9 @@ dependencies = [
 
 [[package]]
 name = "clap_builder"
-version = "4.4.8"
+version = "4.4.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "07cdf1b148b25c1e1f7a42225e30a0d99a615cd4637eae7365548dd4529b95bc"
+checksum = "63361bae7eef3771745f02d8d892bec2fee5f6e34af316ba556e7f97a7069ff1"
 dependencies = [
  "anstream",
  "anstyle",
@@ -370,7 +371,7 @@ checksum = "2674ec482fbc38012cf31e6c42ba0177b431a0cb6f15fe40efa5aab1bda516f6"
 dependencies = [
  "is-terminal",
  "lazy_static",
- "windows-sys",
+ "windows-sys 0.48.0",
 ]
 
 [[package]]
@@ -510,12 +511,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
 
 [[package]]
 name = "errno"
-version = "0.3.7"
+version = "0.3.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f258a7194e7f7c2a7837a8913aeab7fd8c383457034fa20ce4dd3dcb813e8eb8"
+checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
 dependencies = [
  "libc",
- "windows-sys",
+ "windows-sys 0.52.0",
 ]
 
 [[package]]
@@ -594,9 +595,9 @@ dependencies = [
 
 [[package]]
 name = "gimli"
-version = "0.28.0"
+version = "0.28.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0"
+checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
 
 [[package]]
 name = "h2"
@@ -630,7 +631,7 @@ dependencies = [
 [[package]]
 name = "ham-cats"
 version = "0.1.0"
-source = "git+https://gitlab.scd31.com/cats/ham-cats#d32d0a72f3e5633a25cc6495b2fe7daed139fc84"
+source = "git+https://gitlab.scd31.com/cats/ham-cats#cccc04c1c96ebe64681a59d9c2a7c66e00418067"
 dependencies = [
  "arrayvec",
  "bitvec",
@@ -650,9 +651,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
 
 [[package]]
 name = "hashbrown"
-version = "0.14.2"
+version = "0.14.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156"
+checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
 
 [[package]]
 name = "heck"
@@ -678,7 +679,7 @@ version = "0.5.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb"
 dependencies = [
- "windows-sys",
+ "windows-sys 0.48.0",
 ]
 
 [[package]]
@@ -798,7 +799,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
 dependencies = [
  "equivalent",
- "hashbrown 0.14.2",
+ "hashbrown 0.14.3",
  "serde",
 ]
 
@@ -810,7 +811,7 @@ checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b"
 dependencies = [
  "hermit-abi",
  "rustix",
- "windows-sys",
+ "windows-sys 0.48.0",
 ]
 
 [[package]]
@@ -839,9 +840,9 @@ checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
 
 [[package]]
 name = "js-sys"
-version = "0.3.65"
+version = "0.3.66"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "54c0c35952f67de54bb584e9fd912b3023117cbafc0a77d8f3dee1fb5f572fe8"
+checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca"
 dependencies = [
  "wasm-bindgen",
 ]
@@ -911,7 +912,7 @@ checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0"
 dependencies = [
  "libc",
  "wasi",
- "windows-sys",
+ "windows-sys 0.48.0",
 ]
 
 [[package]]
@@ -983,9 +984,9 @@ checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
 
 [[package]]
 name = "percent-encoding"
-version = "2.3.0"
+version = "2.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94"
+checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
 
 [[package]]
 name = "petgraph"
@@ -1053,9 +1054,9 @@ dependencies = [
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.69"
+version = "1.0.70"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
+checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b"
 dependencies = [
  "unicode-ident",
 ]
@@ -1072,12 +1073,12 @@ dependencies = [
 
 [[package]]
 name = "prost"
-version = "0.12.2"
+version = "0.12.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a5a410fc7882af66deb8d01d01737353cf3ad6204c408177ba494291a626312"
+checksum = "146c289cda302b98a28d40c8b3b90498d6e526dd24ac2ecea73e4e491685b94a"
 dependencies = [
  "bytes",
- "prost-derive 0.12.2",
+ "prost-derive 0.12.3",
 ]
 
 [[package]]
@@ -1117,9 +1118,9 @@ dependencies = [
 
 [[package]]
 name = "prost-derive"
-version = "0.12.2"
+version = "0.12.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "065717a5dfaca4a83d2fe57db3487b311365200000551d7a364e715dbf4346bc"
+checksum = "efb6c9a1dd1def8e2124d17e83a20af56f1570d6c2d2bd9e266ccb768df3840e"
 dependencies = [
  "anyhow",
  "itertools 0.11.0",
@@ -1230,16 +1231,16 @@ dependencies = [
 
 [[package]]
 name = "ring"
-version = "0.17.5"
+version = "0.17.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fb0205304757e5d899b9c2e448b867ffd03ae7f988002e47cd24954391394d0b"
+checksum = "684d5e6e18f669ccebf64a92236bb7db9a34f07be010e3627368182027180866"
 dependencies = [
  "cc",
  "getrandom",
  "libc",
  "spin",
  "untrusted",
- "windows-sys",
+ "windows-sys 0.48.0",
 ]
 
 [[package]]
@@ -1265,15 +1266,15 @@ checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
 
 [[package]]
 name = "rustix"
-version = "0.38.24"
+version = "0.38.25"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9ad981d6c340a49cdc40a1028d9c6084ec7e9fa33fcb839cab656a267071e234"
+checksum = "dc99bc2d4f1fed22595588a013687477aedf3cdcfb26558c559edb67b4d9b22e"
 dependencies = [
  "bitflags 2.4.1",
  "errno",
  "libc",
  "linux-raw-sys",
- "windows-sys",
+ "windows-sys 0.48.0",
 ]
 
 [[package]]
@@ -1337,7 +1338,7 @@ version = "0.1.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88"
 dependencies = [
- "windows-sys",
+ "windows-sys 0.48.0",
 ]
 
 [[package]]
@@ -1375,18 +1376,18 @@ dependencies = [
 
 [[package]]
 name = "serde"
-version = "1.0.192"
+version = "1.0.193"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001"
+checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89"
 dependencies = [
  "serde_derive",
 ]
 
 [[package]]
 name = "serde_derive"
-version = "1.0.192"
+version = "1.0.193"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1"
+checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1490,7 +1491,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9"
 dependencies = [
  "libc",
- "windows-sys",
+ "windows-sys 0.48.0",
 ]
 
 [[package]]
@@ -1559,7 +1560,7 @@ dependencies = [
  "fastrand",
  "redox_syscall",
  "rustix",
- "windows-sys",
+ "windows-sys 0.48.0",
 ]
 
 [[package]]
@@ -1605,7 +1606,7 @@ dependencies = [
  "pin-project-lite",
  "socket2 0.5.5",
  "tokio-macros",
- "windows-sys",
+ "windows-sys 0.48.0",
 ]
 
 [[package]]
@@ -1716,7 +1717,7 @@ dependencies = [
  "hyper-timeout",
  "percent-encoding",
  "pin-project",
- "prost 0.12.2",
+ "prost 0.12.3",
  "rustls",
  "rustls-native-certs",
  "rustls-pemfile",
@@ -1831,9 +1832,9 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
 
 [[package]]
 name = "uuid"
-version = "1.5.0"
+version = "1.6.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "88ad59a7560b41a70d191093a945f0b87bc1deeda46fb237479708a1d6b6cdfc"
+checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560"
 dependencies = [
  "getrandom",
 ]
@@ -1861,9 +1862,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
 
 [[package]]
 name = "wasm-bindgen"
-version = "0.2.88"
+version = "0.2.89"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7daec296f25a1bae309c0cd5c29c4b260e510e6d813c286b19eaadf409d40fce"
+checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e"
 dependencies = [
  "cfg-if",
  "wasm-bindgen-macro",
@@ -1871,9 +1872,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-backend"
-version = "0.2.88"
+version = "0.2.89"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e397f4664c0e4e428e8313a469aaa58310d302159845980fd23b0f22a847f217"
+checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826"
 dependencies = [
  "bumpalo",
  "log",
@@ -1886,9 +1887,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-macro"
-version = "0.2.88"
+version = "0.2.89"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5961017b3b08ad5f3fe39f1e79877f8ee7c23c5e5fd5eb80de95abc41f1f16b2"
+checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2"
 dependencies = [
  "quote",
  "wasm-bindgen-macro-support",
@@ -1896,9 +1897,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-macro-support"
-version = "0.2.88"
+version = "0.2.89"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907"
+checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1909,9 +1910,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-shared"
-version = "0.2.88"
+version = "0.2.89"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b"
+checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f"
 
 [[package]]
 name = "which"
@@ -1953,7 +1954,7 @@ version = "0.51.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64"
 dependencies = [
- "windows-targets",
+ "windows-targets 0.48.5",
 ]
 
 [[package]]
@@ -1962,7 +1963,16 @@ version = "0.48.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
 dependencies = [
- "windows-targets",
+ "windows-targets 0.48.5",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets 0.52.0",
 ]
 
 [[package]]
@@ -1971,13 +1981,28 @@ version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
 dependencies = [
- "windows_aarch64_gnullvm",
- "windows_aarch64_msvc",
- "windows_i686_gnu",
- "windows_i686_msvc",
- "windows_x86_64_gnu",
- "windows_x86_64_gnullvm",
- "windows_x86_64_msvc",
+ "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",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
+dependencies = [
+ "windows_aarch64_gnullvm 0.52.0",
+ "windows_aarch64_msvc 0.52.0",
+ "windows_i686_gnu 0.52.0",
+ "windows_i686_msvc 0.52.0",
+ "windows_x86_64_gnu 0.52.0",
+ "windows_x86_64_gnullvm 0.52.0",
+ "windows_x86_64_msvc 0.52.0",
 ]
 
 [[package]]
@@ -1986,42 +2011,84 @@ version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
 
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
+
 [[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.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
+
 [[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.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
+
 [[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.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
+
 [[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.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
+
 [[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.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
+
 [[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.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
+
 [[package]]
 name = "winnow"
 version = "0.5.19"
diff --git a/Cargo.toml b/Cargo.toml
index a0f1cb29bf5bbf565f6035b4426a4d14272c8eca..959253ffe9ced2a875e8e60919aa59fecd041e10 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -37,6 +37,7 @@ tokio-stream = "0.1.14"
 colored = "2.0.4"
 clap = { version = "4.4.7", features = ["derive"] }
 uuid = { version = "1.5.0", features = ["v4"] }
+rand = "0.8.5"
 
 [build-dependencies]
 tonic-build = "0.9"
diff --git a/src/gate.rs b/src/gate.rs
index 546c9291d2a9896ef8f57b5eae0084150cbf65cc..c94d1736b8300e85cb308d27d71b058c0e293c20 100644
--- a/src/gate.rs
+++ b/src/gate.rs
@@ -4,14 +4,11 @@ use crate::felinet::{
     handler_client::HandlerClient, packet_filter::Filter, PacketFilter, PacketIn as SemiPacketIn,
 };
 use crate::radio::MAX_PACKET_LEN;
-use crate::util::{
-    append_internet_to_packet_route, append_to_packet_route, attempt_decode, print_packet,
-    should_digipeat,
-};
+use crate::util::{append_internet_to_packet_route, attempt_decode, print_packet};
 use anyhow::{anyhow, Context};
-use arrayvec::ArrayVec;
 use async_stream::stream;
 use half::f16;
+use ham_cats::buffer::Buffer;
 use ham_cats::{
     packet::Packet,
     whisker::{Gps, Identification, Route},
@@ -28,7 +25,8 @@ pub fn beacon_forever(
     felinet_send: broadcast::Sender<SemiPacketIn>,
     uuid: Uuid,
 ) -> anyhow::Result<()> {
-    let mut packet: Packet<MAX_PACKET_LEN> = Packet::default();
+    let mut buf = [0; MAX_PACKET_LEN];
+    let mut packet = Packet::new(&mut buf);
     packet
         .add_identification(
             Identification::new(c.icon, &c.callsign, c.ssid).context(
@@ -60,7 +58,8 @@ pub fn beacon_forever(
             .map_err(|e| anyhow!("Could not add gps to beacon packet: {e}"))?;
     }
 
-    let mut internet_packet = packet.clone();
+    let mut internet_buf = [0; MAX_PACKET_LEN];
+    let mut internet_packet = packet.clone_backing(&mut internet_buf);
     let mut rf_packet = packet;
 
     // internet route
@@ -77,7 +76,6 @@ pub fn beacon_forever(
         .map_err(|e| anyhow!("Could not add route to beacon packet: {e}"))?;
 
     let semi = internet_packet
-        .clone()
         .semi_encode()
         .map_err(|(e, _)| anyhow!("Could not encode beacon packet: {e}"))?
         .to_vec();
@@ -86,10 +84,13 @@ pub fn beacon_forever(
         uuid: uuid.into(),
     };
 
-    let full = rf_packet
-        .fully_encode()
-        .map_err(|e| anyhow!("Could not encode beacon packet: {e}"))?
-        .to_vec();
+    let mut full = [0; MAX_PACKET_LEN];
+    let mut full = Buffer::new_empty(&mut full);
+    rf_packet
+        .fully_encode(&mut full)
+        .map_err(|e| anyhow!("Could not encode beacon packet: {e}"))?;
+
+    let full = full.to_vec();
 
     tokio::task::spawn(async move {
         loop {
@@ -117,9 +118,11 @@ pub async fn packet_handler(
             .await
             .context("Packet receive channel died")?;
 
-        match attempt_decode(packet) {
+        let mut buf = [0; MAX_PACKET_LEN];
+        match attempt_decode(&packet, &mut buf) {
             Ok(mut packet) => {
-                let mut internet_packet = packet.clone();
+                let mut buf2 = [0; MAX_PACKET_LEN];
+                let mut internet_packet = packet.clone_backing(&mut buf2);
                 match append_internet_to_packet_route(&callsign, ssid, &mut internet_packet) {
                     Some(()) => match internet_packet.semi_encode() {
                         Ok(encoded) => {
@@ -140,25 +143,35 @@ pub async fn packet_handler(
                     }
                 }
 
-                if should_digipeat(&callsign, ssid, &packet, true) {
-                    if append_to_packet_route(&callsign, ssid, &mut packet).is_none() {
-                        eprintln!("Could not add routing to packet before digipeating. Skipping");
-                        continue;
-                    };
+                match packet.should_digipeat(&callsign, ssid) {
+                    Ok(()) => {
+                        if let Err(e) = packet.append_to_route(&callsign, ssid) {
+                            eprintln!(
+                                "Could not add routing to packet before digipeating: {e}. Skipping"
+                            );
+                            continue;
+                        };
 
-                    match packet.fully_encode() {
-                        Ok(encoded) => {
-                            packet_send
-                                .send(encoded.to_vec())
-                                .await
-                                .with_context(|| "Packet send channel died")?;
-                        }
+                        let mut encoded_buf = [0; MAX_PACKET_LEN];
+                        let mut encoded = Buffer::new_empty(&mut encoded_buf);
+                        match packet.fully_encode(&mut encoded) {
+                            Ok(()) => {
+                                packet_send
+                                    .send(encoded.to_vec())
+                                    .await
+                                    .with_context(|| "Packet send channel died")?;
+                            }
 
-                        Err(e) => {
-                            eprintln!("Could not fully-encode packet: {e:?}");
+                            Err(e) => {
+                                eprintln!("Could not fully-encode packet: {e:?}");
+                            }
                         }
                     }
-                }
+
+                    Err(e) => {
+                        eprintln!("Not digipeating: {e}");
+                    }
+                };
             }
 
             Err(e) => eprintln!("Recv error: {e}"),
@@ -230,14 +243,20 @@ pub async fn felinet_receive_once(
         .into_inner();
 
     while let Some(semi) = stream.next().await {
-        if let Some(pkt) = ArrayVec::try_from(&semi?.raw[..])
+        let mut buf = [0; MAX_PACKET_LEN];
+        let mut buf = Buffer::new_empty(&mut buf);
+
+        if let Some(pkt) = buf
+            .try_extend_from_slice(&semi?.raw[..])
             .ok()
-            .and_then(|av| Packet::<8191>::semi_decode(av).ok())
+            .and_then(|_| Packet::semi_decode(buf).ok())
         {
-            if should_digipeat(callsign, ssid, &pkt, false) {
+            if pkt.should_digipeat(callsign, ssid).is_ok() {
                 print_packet(&pkt, false);
 
-                if let Ok(data) = pkt.fully_encode() {
+                let mut buf2 = [0; MAX_PACKET_LEN];
+                let mut data = Buffer::new_empty(&mut buf2);
+                if let Ok(()) = pkt.fully_encode(&mut data) {
                     modem_send.send(data.to_vec()).await?;
                 }
             }
diff --git a/src/main.rs b/src/main.rs
index 7c976d8e5db4049efd0ecbc20b9d3b017a61b611..1bf053d1744eda5a3048d9cd48defca88576b221 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -6,6 +6,7 @@ use config::Config;
 use felinet::PingRequest;
 use gate::{beacon_forever, felinet_receive_forever, felinet_send_forever, packet_handler};
 use ham_cats::{
+    buffer::Buffer,
     packet::Packet,
     whisker::{Arbitrary, Identification},
 };
@@ -64,7 +65,8 @@ async fn xmit_test(
 
     radio.set_channel(channel);
 
-    let mut pkt: Packet<MAX_PACKET_LEN> = Packet::default();
+    let mut buf = [0; MAX_PACKET_LEN];
+    let mut pkt = Packet::new(&mut buf);
     pkt.add_identification(
         Identification::new(config.icon, &config.callsign, config.ssid)
             .context("Invalid identification")?,
@@ -78,8 +80,9 @@ async fn xmit_test(
             .map_err(|e| anyhow!("Could not add data to packet: {e}"))?;
     }
 
-    let data = pkt
-        .fully_encode()
+    let mut buf2 = [0; MAX_PACKET_LEN];
+    let mut data = Buffer::new_empty(&mut buf2);
+    pkt.fully_encode(&mut data)
         .map_err(|e| anyhow!("Could not encode packet: {e}"))?;
 
     tokio::task::spawn(async move {
diff --git a/src/radio.rs b/src/radio.rs
index 0b2b1f9944317888d754397c034a5c8afe749545..c60cdd9fd39b7ad34778b5eb51bceb2c141fe8e0 100644
--- a/src/radio.rs
+++ b/src/radio.rs
@@ -1,14 +1,12 @@
 use anyhow::{anyhow, bail, Context};
+use rand::{thread_rng, Rng};
 use rf4463::{config::RADIO_CONFIG_CATS, Rf4463};
 use rppal::{
     gpio::{Gpio, OutputPin},
     hal::Delay,
     spi::{Bus, Mode, SlaveSelect, Spi},
 };
-use std::{
-    thread,
-    time::{Duration, Instant},
-};
+use std::time::{Duration, Instant};
 use tokio::sync::mpsc::{error::TryRecvError, Receiver, Sender};
 
 pub const MAX_PACKET_LEN: usize = 8191;
@@ -18,6 +16,7 @@ pub struct RadioManager {
 
     tx: Sender<Vec<u8>>,
     rx: Receiver<Vec<u8>>,
+    rx_buf: [u8; MAX_PACKET_LEN],
 }
 
 impl RadioManager {
@@ -33,7 +32,14 @@ impl RadioManager {
             .map_err(|e| anyhow!("{e:?}"))?;
         radio.set_channel(20);
 
-        Ok(Self { radio, tx, rx })
+        let rx_buf = [0; MAX_PACKET_LEN];
+
+        Ok(Self {
+            radio,
+            tx,
+            rx,
+            rx_buf,
+        })
     }
 
     pub fn set_channel(&mut self, channel: u8) {
@@ -41,47 +47,16 @@ impl RadioManager {
     }
 
     pub async fn process_forever(&mut self) -> anyhow::Result<()> {
-        self.radio
-            .start_rx(None, true)
-            .map_err(|e| anyhow!("{e:?}"))?;
-
-        let mut rx_buf = [0; MAX_PACKET_LEN];
-
         loop {
-            self.radio
-                .interrupt(Some(&mut rx_buf), None)
-                .map_err(|e| anyhow!("{e:?}"))?;
-
-            let data = self
-                .radio
-                .finish_rx(&mut rx_buf)
-                .map_err(|e| anyhow!("{e:?}"))?;
-
-            if let Some(data) = data {
-                self.radio
-                    .start_rx(None, true)
-                    .map_err(|e| anyhow!("{e:?}"))?;
+            self.tick().await?;
 
-                self.tx
-                    .send(data.data().to_vec())
-                    .await
-                    .ok()
-                    .context("TX channel died")?;
-            }
-
-            if !self.radio.is_busy_rxing()? {
-                match self.rx.try_recv() {
-                    Ok(pkt) => {
-                        self.send_packet(&pkt)?;
-
-                        self.radio
-                            .start_rx(None, true)
-                            .map_err(|e| anyhow!("{e:?}"))?;
-                    }
-                    Err(TryRecvError::Empty) => {}
-                    Err(TryRecvError::Disconnected) => {
-                        bail!("RX channel disconnected")
-                    }
+            match self.rx.try_recv() {
+                Ok(pkt) => {
+                    self.tx(&pkt).await?;
+                }
+                Err(TryRecvError::Empty) => {}
+                Err(TryRecvError::Disconnected) => {
+                    bail!("RX channel disconnected")
                 }
             }
 
@@ -89,11 +64,47 @@ impl RadioManager {
         }
     }
 
-    fn send_packet(&mut self, data: &[u8]) -> anyhow::Result<()> {
+    async fn tick(&mut self) -> anyhow::Result<()> {
+        if self.radio.is_idle() {
+            self.radio
+                .start_rx(None, false)
+                .map_err(|e| anyhow!("{e}"))?;
+
+            tokio::time::sleep(Duration::from_millis(25)).await;
+        }
+
+        self.radio
+            .interrupt(Some(&mut self.rx_buf), None)
+            .map_err(|e| anyhow!("{e:?}"))?;
+
+        if let Some(data) = self
+            .radio
+            .finish_rx(&mut self.rx_buf)
+            .map_err(|e| anyhow!("{e}"))?
+        {
+            self.radio
+                .start_rx(None, false)
+                .map_err(|e| anyhow!("{e}"))?;
+
+            self.tx
+                .send(data.data().to_vec())
+                .await
+                .ok()
+                .context("TX channel died")?;
+        }
+
+        Ok(())
+    }
+
+    async fn tx(&mut self, data: &[u8]) -> anyhow::Result<()> {
+        // ensures we don't tx over a packet,
+        // and adds some random delay so that every node
+        // if offset slightly
+        self.tx_delay().await?;
+
         self.radio.start_tx(data).map_err(|e| anyhow!("{e:?}"))?;
 
         const TIMEOUT: Duration = Duration::from_secs(10);
-
         let start_time = Instant::now();
         while !self.radio.is_idle() {
             self.radio
@@ -104,9 +115,32 @@ impl RadioManager {
                 bail!("Timeout while transmitting");
             }
 
-            thread::sleep(Duration::from_millis(25));
+            tokio::time::sleep(Duration::from_millis(25)).await;
         }
 
         Ok(())
     }
+
+    async fn tx_delay(&mut self) -> anyhow::Result<()> {
+        loop {
+            let delay_ms = thread_rng().gen_range(0..50);
+
+            // since delay_ms < 100 we can safely sleep without calling tick
+            tokio::time::sleep(Duration::from_millis(delay_ms)).await;
+
+            let mut rx = false;
+
+            while self.radio.is_busy_rxing()? {
+                rx = true;
+                self.tick().await?;
+
+                tokio::time::sleep(Duration::from_millis(25)).await;
+            }
+
+            if !rx {
+                // didn't rx a packet, so we're safe to leave
+                break Ok(());
+            }
+        }
+    }
 }
diff --git a/src/util.rs b/src/util.rs
index e50cb0ad54656a31362c00afb4b70730218fa680..a8273c9e10ba0f4b0dbfd18f6afdf12da70f619b 100644
--- a/src/util.rs
+++ b/src/util.rs
@@ -6,80 +6,6 @@ use ham_cats::{
     whisker::{Route, RouteNode},
 };
 
-// RF to RF, also Internet to RF
-pub fn should_digipeat(
-    callsign: &str,
-    ssid: u8,
-    packet: &Packet<MAX_PACKET_LEN>,
-    echo: bool,
-) -> bool {
-    let route = match packet.route() {
-        Some(x) => x,
-        None => {
-            if echo {
-                println!("Not digipeating - Packet has no route")
-            };
-            return false;
-        }
-    };
-
-    if let Some(ident) = packet.identification() {
-        if &ident.callsign == callsign && ident.ssid == ssid {
-            // this node is the source of the packet
-            if echo {
-                println!("Not digipeating - Packet ident is us");
-            }
-            return false;
-        }
-    }
-
-    let max_hops: usize = route.max_hops.into();
-    let cur_hops = route
-        .iter()
-        .filter(|r| match r {
-            RouteNode::Internet => false,
-            RouteNode::Identity(_, _, is_future) => !is_future,
-        })
-        .count();
-
-    if max_hops <= cur_hops {
-        // already hit our max hops
-        if echo {
-            println!("Not digipeating - Max hops hit");
-        }
-        return false;
-    }
-
-    let already_digipeated = route.iter().any(|r| match r {
-        RouteNode::Internet => false,
-        RouteNode::Identity(rc, rs, is_future) => rc == callsign && rs == ssid && !is_future,
-    });
-
-    if already_digipeated {
-        // this node has already digipeated this packet
-        if echo {
-            println!("Not digipeating - Already digipeated by this node")
-        };
-        return false;
-    }
-
-    let next_node = route.iter().find_map(|r| match r {
-        RouteNode::Identity(c, s, is_future) if is_future => Some((c, s)),
-        _ => None,
-    });
-
-    match next_node {
-        Some((rc, rs)) => {
-            let out = rc == callsign && rs == ssid;
-            if !out && echo {
-                println!("Not digipeating - This packet's destiny is set and does not involve us");
-            }
-            out
-        }
-        None => true,
-    }
-}
-
 pub fn print_packet(pkt: &Packet<MAX_PACKET_LEN>, is_rf: bool) {
     let color = if is_rf {
         println!("-----  RF -----");
@@ -137,9 +63,12 @@ pub fn print_packet(pkt: &Packet<MAX_PACKET_LEN>, is_rf: bool) {
     println!();
 }
 
-pub fn attempt_decode(packet: Vec<u8>) -> anyhow::Result<Packet<MAX_PACKET_LEN>> {
+pub fn attempt_decode<'a>(
+    packet: &[u8],
+    buf: &'a mut [u8; MAX_PACKET_LEN],
+) -> anyhow::Result<Packet<'a, MAX_PACKET_LEN>> {
     let packet: Packet<MAX_PACKET_LEN> =
-        Packet::fully_decode(&packet).map_err(|e| anyhow!("Could not decode packet: {e:?}"))?;
+        Packet::fully_decode(packet, buf).map_err(|e| anyhow!("Could not decode packet: {e:?}"))?;
 
     print_packet(&packet, true);
 
@@ -169,7 +98,7 @@ pub fn append_internet_to_packet_route(
     if let Some((rc, rs)) = first_future {
         if rc == callsign && rs == ssid {
             // we can just do the normal thing
-            append_to_route(callsign, ssid, &mut route)?;
+            route.append_node(callsign, ssid).ok()?;
         } else {
             // rip off all the futures and add ourselves
             let mut new_route = Route::new(route.max_hops);
@@ -190,7 +119,7 @@ pub fn append_internet_to_packet_route(
             route.push_callsign(callsign, ssid, false);
         }
     } else {
-        append_to_route(callsign, ssid, &mut route)?;
+        route.append_node(callsign, ssid).ok()?;
     }
 
     route.push_internet()?;
@@ -199,53 +128,3 @@ pub fn append_internet_to_packet_route(
 
     Some(())
 }
-
-#[must_use]
-pub fn append_to_packet_route(
-    callsign: &str,
-    ssid: u8,
-    packet: &mut Packet<MAX_PACKET_LEN>,
-) -> Option<()> {
-    let mut route = packet.route().unwrap_or(Route::new(0));
-    append_to_route(callsign, ssid, &mut route)?;
-    packet.clear_route();
-    packet.add_route(route).ok()?;
-
-    Some(())
-}
-
-#[must_use]
-fn append_to_route(callsign: &str, ssid: u8, r: &mut Route) -> Option<()> {
-    let replace_future = r.iter().any(|rn| match rn {
-        RouteNode::Identity(rc, rs, is_future) => rc == callsign && rs == ssid && is_future,
-        _ => false,
-    });
-
-    if replace_future {
-        let mut new_route = Route::new(r.max_hops);
-        let mut already_replaced = false;
-
-        for rn in r.iter() {
-            match rn {
-                RouteNode::Identity(rc, rs, is_future)
-                    if rc == callsign && rs == ssid && is_future && !already_replaced =>
-                {
-                    already_replaced = true;
-                    new_route.push_callsign(callsign, ssid, false)?;
-                }
-                RouteNode::Identity(rc, rs, is_future) => {
-                    new_route.push_callsign(rc, rs, is_future)?;
-                }
-                RouteNode::Internet => {
-                    new_route.push_internet()?;
-                }
-            }
-        }
-
-        *r = new_route;
-    } else {
-        r.push_callsign(callsign, ssid, false)?;
-    }
-
-    Some(())
-}