diff --git a/Cargo.lock b/Cargo.lock
index da09b26f7d4311ba0d81d18cf0d0abe47f8a4afc..424abf97447ed81c86b7ffe845e4d30560e2bec5 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -216,7 +216,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
 
 [[package]]
-name = "cats-sdr-igate-decoder"
+name = "cats-sdr-igate"
 version = "0.1.0"
 dependencies = [
  "anyhow",
@@ -234,6 +234,7 @@ dependencies = [
  "rand",
  "serde",
  "serde_with",
+ "subprocess",
  "tokio",
  "tokio-stream",
  "toml",
@@ -1336,6 +1337,16 @@ version = "0.10.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
 
+[[package]]
+name = "subprocess"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c2e86926081dda636c546d8c5e641661049d7562a68f5488be4a1f7f66f6086"
+dependencies = [
+ "libc",
+ "winapi",
+]
+
 [[package]]
 name = "syn"
 version = "1.0.109"
@@ -1734,6 +1745,28 @@ dependencies = [
  "rustix",
 ]
 
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
 [[package]]
 name = "windows-core"
 version = "0.52.0"
diff --git a/Cargo.toml b/Cargo.toml
index 2d52f9e3e75c7c8ba26519678114041b359f08f1..886be17042ff5aeb5d291e3bc897a11783b3cd61 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,5 +1,5 @@
 [package]
-name = "cats-sdr-igate-decoder"
+name = "cats-sdr-igate"
 version = "0.1.0"
 edition = "2021"
 license = "MIT OR LGPL-2.1-only"
@@ -8,10 +8,9 @@ description = "An RX-only SDR CATS I-gate"
 
 [package.metadata.deb]
 assets = [
-       ["target/release/cats-sdr-igate-decoder", "/usr/bin/", "755"],
+       ["target/release/cats-sdr-igate", "/usr/bin/", "755"],
        ["debian/cats-sdr-igate.service", "/lib/systemd/system/cats-sdr-igate.service", "644"],
        ["config.example.toml", "/etc/cats-sdr-igate/config.toml", "644"],
-	   ["debian/start.sh", "/usr/bin/cats-sdr-igate", "755"]
 ]
 conf-files = ["/etc/cats-sdr-igate/config.toml"]
 maintainer-scripts = "debian/maintainer_scripts/"
@@ -39,6 +38,7 @@ rand = "0.8.5"
 bitvec = "1.0.1"
 libc = "0.2.152"
 itertools = "0.12.0"
+subprocess = "0.2.9"
 
 [build-dependencies]
 tonic-build = "0.9"
diff --git a/README.md b/README.md
index a5d4d739e28f0c72e7b9986e5a925f7eb9b2554e..5234d1f1dc65d2fc8fc0a785768281424918e8de 100644
--- a/README.md
+++ b/README.md
@@ -18,11 +18,11 @@ git pull
 cargo deb
 ```
 
-The resulting deb package will placed in `target/debian/cats-sdr-igate-decoder_VERSION.deb`
+The resulting deb package will placed in `target/debian/cats-sdr-igate_VERSION.deb`
 
 ### Installing
 
-Once built, the package can be installed with `sudo dpkg -i target/debian/cats-sdr-igate-decoder_VERSION.deb`, replacing `VERSION` with whatever version and architecture was built.
+Once built, the package can be installed with `sudo dpkg -i target/debian/cats-sdr-igate_VERSION.deb`, replacing `VERSION` with whatever version and architecture was built.
 
 ### Configuring
 
diff --git a/debian/start.sh b/debian/start.sh
deleted file mode 100755
index 3623a67a836844ba26c1026bd264a84668d71f1b..0000000000000000000000000000000000000000
--- a/debian/start.sh
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/sh
-
-rtl_fm -M raw -F9 -p 0 -d 0 -s 48000 -f 430500000 - | /usr/bin/cats-sdr-igate-decoder
diff --git a/src/decoder/mod.rs b/src/decoder/mod.rs
index 3b77cc0b868802802bf1131a601fdd808f96e74d..97554aef8158afb16a86c92da0c62a7af568fac3 100644
--- a/src/decoder/mod.rs
+++ b/src/decoder/mod.rs
@@ -7,6 +7,7 @@ use uuid::Uuid;
 use crate::{
     codec2::{Complex, Fsk},
     config::FelinetConfig,
+    decoder::rtl::RtlSdr,
     felinet::PacketIn as SemiPacketIn,
     util::{append_internet_to_packet_route, print_packet},
 };
@@ -16,16 +17,25 @@ use self::{dc_bias::RemoveDcBias, demod::Demod};
 mod dc_bias;
 mod demod;
 mod packet;
+mod rtl;
 
 pub fn decode_forever(
     felinet_send: mpsc::Sender<SemiPacketIn>,
     felinet_config: &Option<FelinetConfig>,
     uuid: &Uuid,
 ) {
-    let stdin = io::stdin();
-    let iq = stdin
-        .bytes()
-        .map(|x| x.expect("Could not read from stdin"))
+    const USE_STDIN: bool = false;
+
+    let bytes: Box<dyn Iterator<Item = u8>> = if USE_STDIN {
+        let stdin = io::stdin();
+
+        Box::new(stdin.bytes().map(|x| x.expect("Could not read from stdin")))
+    } else {
+        let rtl = RtlSdr::new().unwrap();
+        Box::new(rtl)
+    };
+
+    let iq = bytes
         .tuples()
         .map(|(a, b)| i16::from_le_bytes([a, b]))
         .tuples()
@@ -34,11 +44,8 @@ pub fn decode_forever(
             let imag = imag as f32;
             Complex { real, imag }
         });
-
     let iq_no_dc = RemoveDcBias::new(iq);
-
     let soft_bits = Fsk::new(iq_no_dc);
-
     let demod = Demod::new(soft_bits);
 
     demod.demod(|mut p| {
diff --git a/src/decoder/rtl.rs b/src/decoder/rtl.rs
new file mode 100644
index 0000000000000000000000000000000000000000..563d3b5bc673aa7f35a2949b2b62ef24345224fd
--- /dev/null
+++ b/src/decoder/rtl.rs
@@ -0,0 +1,76 @@
+use std::collections::VecDeque;
+
+use anyhow::Context;
+use subprocess::{Communicator, Popen, PopenConfig, Redirection};
+
+pub struct RtlSdr {
+    comms: Communicator,
+    out_buf: VecDeque<u8>,
+    err_buf: Vec<u8>,
+}
+
+impl RtlSdr {
+    pub fn new() -> anyhow::Result<Self> {
+        let mut p = Popen::create(
+            &[
+                "rtl_fm",
+                "-f",
+                "430500000",
+                "-M",
+                "raw",
+                "-F9",
+                "-p",
+                "0",
+                "-d",
+                "0",
+                "-s",
+                "48000",
+            ],
+            PopenConfig {
+                stdout: Redirection::Pipe,
+                stderr: Redirection::Pipe,
+                detached: true,
+                ..Default::default()
+            },
+        )
+        .context("Error starting rtl-fm")?;
+
+        let comms = p.communicate_start(None).limit_size(1024);
+
+        Ok(Self {
+            comms,
+            out_buf: VecDeque::new(),
+            err_buf: vec![],
+        })
+    }
+}
+
+impl Iterator for RtlSdr {
+    type Item = u8;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        if let Some(b) = self.out_buf.pop_front() {
+            return Some(b);
+        }
+
+        let (out, err) = self.comms.read().ok()?;
+        let out = out.expect("Missing stdout");
+        let err = err.expect("Missing stderr");
+
+        self.out_buf.extend(out.iter());
+        for e in err {
+            if e == b'\n' {
+                handle_err(&self.err_buf);
+                self.err_buf.clear();
+            } else {
+                self.err_buf.push(e);
+            }
+        }
+
+        self.out_buf.pop_front()
+    }
+}
+
+fn handle_err(err: &[u8]) {
+    eprintln!("[rtl_fm] {}", String::from_utf8_lossy(err));
+}
diff --git a/start.sh b/start.sh
deleted file mode 100755
index 66402a5d611af9eb4be15f4934c1e60e32fa81ea..0000000000000000000000000000000000000000
--- a/start.sh
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/sh
-
-rtl_fm -M raw -F9 -p 0 -d 0 -s 48000 -f 430500000 - | target/release/cats-sdr-igate-decoder