diff --git a/src/phy/tetra_burst.c b/src/phy/tetra_burst.c
index 62efb72..cf1360c 100644
--- a/src/phy/tetra_burst.c
+++ b/src/phy/tetra_burst.c
@@ -23,6 +23,7 @@
 #include <stdint.h>
 #include <string.h>
 #include <stdio.h>
+#include <stdbool.h>
 
 #include <phy/tetra_burst.h>
 
@@ -266,12 +267,57 @@ int build_norm_c_d_burst(uint8_t *buf, const uint8_t *bkn1, const uint8_t *bb, c
 	return cur - buf;
 }
 
+/*
+ * Initialize a fast filter for searching for training sequences.
+ * After this function is called, each element of @tsq_all contains the first
+ * @FILTER_LOOKAHEAD_LEN bits of a given training sequence.
+ * Then, we can search for a given sequence using a single compare.
+ */
+uint32_t tsq_all[5];
+#define FILTER_LOOKAHEAD_LEN 16
+#define FILTER_LOOKAHEAD_MASK 0xFFFF
+void tetra_init_train_seq_filter() {
+	for(int i = 0; i<FILTER_LOOKAHEAD_LEN; i++) {
+		tsq_all[0] = (tsq_all[0] << 1) | y_bits[i];
+		tsq_all[1] = (tsq_all[1] << 1) | n_bits[i];
+		tsq_all[2] = (tsq_all[2] << 1) | p_bits[i];
+		tsq_all[3] = (tsq_all[3] << 1) | q_bits[i];
+		tsq_all[4] = (tsq_all[4] << 1) | x_bits[i];
+	}
+	for(int i = 0; i < 5; i++) {
+		tsq_all[i] &= FILTER_LOOKAHEAD_MASK;
+	}
+}
+
 int tetra_find_train_seq(const uint8_t *in, unsigned int end_of_in,
 			 uint32_t mask_of_train_seq, unsigned int *offset)
 {
 	const uint8_t *cur;
 
+	uint32_t filter = 0;
+
+	for (int i = 0; i<FILTER_LOOKAHEAD_LEN-2; i++) {
+		filter = (filter << 1) | in[i];
+	}
+
 	for (cur = in; cur < in + end_of_in; cur++) {
+		/*
+		 * We are shifting the incoming bitstream into @filter.
+		 * Then, we compare the filter for the beginning of all the training sequence.
+		 * When this simple comparison matches, we use a full-blown memcmp for the
+		 * whole sequence.
+		 */
+		filter = ((filter << 1) | cur[FILTER_LOOKAHEAD_LEN-1]) & FILTER_LOOKAHEAD_MASK;
+		bool match = false;
+		for(int i = 0; i<5; i++) {
+			if(filter == tsq_all[i]) {
+				match = true;
+			}
+		}
+		if(!match) {
+			continue;
+		}
+
 		int remain_len = (in + end_of_in) - cur;
 
 		if (mask_of_train_seq & (1 << TETRA_TRAIN_SYNC) &&
diff --git a/src/phy/tetra_burst.h b/src/phy/tetra_burst.h
index 8feb09c..10b9dbf 100644
--- a/src/phy/tetra_burst.h
+++ b/src/phy/tetra_burst.h
@@ -31,5 +31,6 @@ enum tetra_train_seq {
 /* find a TETRA training sequence in the burst buffer indicated */
 int tetra_find_train_seq(const uint8_t *in, unsigned int end_of_in,
 			 uint32_t mask_of_train_seq, unsigned int *offset);
+void tetra_init_train_seq_filter();
 
 #endif /* TETRA_BURST_H */
diff --git a/src/phy/tetra_burst_sync.c b/src/phy/tetra_burst_sync.c
index ae210dc..d2c6077 100644
--- a/src/phy/tetra_burst_sync.c
+++ b/src/phy/tetra_burst_sync.c
@@ -35,47 +35,45 @@ struct tetra_phy_state t_phy_state;
 
 void tetra_burst_rx_cb(const uint8_t *burst, unsigned int len, enum tetra_train_seq type, void *priv);
 
-static void make_bitbuf_space(struct tetra_rx_state *trs, unsigned int len)
-{
-	unsigned int bitbuf_space = sizeof(trs->bitbuf) - trs->bits_in_buf;
-
-	if (bitbuf_space < len) {
-		unsigned int delta = len - bitbuf_space;
-
-		DEBUGP("bitbuf left: %u, shrinking by %u\n", bitbuf_space, delta);
-		memmove(trs->bitbuf, trs->bitbuf + delta, trs->bits_in_buf - delta);
-		trs->bits_in_buf -= delta;
-		trs->bitbuf_start_bitnum += delta;
-		bitbuf_space = sizeof(trs->bitbuf) - trs->bits_in_buf;
-	}
-}
-
-/* input a raw bitstream into the tetra burst synchronizaer */
-int tetra_burst_sync_in(struct tetra_rx_state *trs, uint8_t *bits, unsigned int len)
+/* input a raw bitstream file into the tetra burst synchronizaer */
+int tetra_burst_sync_in(struct tetra_rx_state *trs, FILE *infile)
 {
 	int rc;
 	unsigned int train_seq_offs;
+	int len;
+	int bits_free = sizeof(trs->bitbuf) - trs->bits_in_buf;
 
-	DEBUGP("burst_sync_in: %u bits, state %u\n", len, trs->state);
+	len = fread(trs->bitbuf + trs->bits_in_buf, 1, bits_free, infile);
+
+	if (len < 0) {
+		perror("read");
+		exit(1);
+	} else if (feof(infile) && trs->bits_in_buf < TETRA_BITS_PER_TS) {
+		printf("EOF");
+		return -1;
+	}
 
-	/* First: append the data to the bitbuf */
-	make_bitbuf_space(trs, len);
-	memcpy(trs->bitbuf + trs->bits_in_buf, bits, len);
 	trs->bits_in_buf += len;
+	DEBUGP("burst_sync_in: %u bits, state %u\n", len, trs->state);
 
 	switch (trs->state) {
 	case RX_S_UNLOCKED:
 		if (trs->bits_in_buf < TETRA_BITS_PER_TS*2) {
 			/* wait for more bits to arrive */
 			DEBUGP("-> waiting for more bits to arrive\n");
-			return len;
+			return trs->bits_in_buf;
 		}
 		DEBUGP("-> trying to find training sequence between bit %u and %u\n",
 			trs->bitbuf_start_bitnum, trs->bits_in_buf);
 		rc = tetra_find_train_seq(trs->bitbuf, trs->bits_in_buf,
 					  (1 << TETRA_TRAIN_SYNC), &train_seq_offs);
-		if (rc < 0)
+		if (rc < 0) {
+			memmove(trs->bitbuf,
+				trs->bitbuf + trs->bits_in_buf - TETRA_BITS_PER_TS*2,
+				TETRA_BITS_PER_TS*2);
+			trs->bits_in_buf = TETRA_BITS_PER_TS*2;
 			return rc;
+		}
 		printf("found SYNC training sequence in bit #%u\n", train_seq_offs);
 		trs->state = RX_S_KNOW_FSTART;
 		trs->next_frame_start_bitnum = trs->bitbuf_start_bitnum + train_seq_offs + 296;
@@ -91,7 +89,7 @@ int tetra_burst_sync_in(struct tetra_rx_state *trs, uint8_t *bits, unsigned int
 	case RX_S_KNOW_FSTART:
 		/* we are locked, i.e. already know when the next frame should start */
 		if (trs->bitbuf_start_bitnum + trs->bits_in_buf < trs->next_frame_start_bitnum)
-			return 0;
+			return trs->bits_in_buf;
 		else {
 			/* shift start of frame to start of bitbuf */
 			int offset = trs->next_frame_start_bitnum - trs->bitbuf_start_bitnum;
@@ -107,7 +105,7 @@ int tetra_burst_sync_in(struct tetra_rx_state *trs, uint8_t *bits, unsigned int
 	case RX_S_LOCKED:
 		if (trs->bits_in_buf < TETRA_BITS_PER_TS) {
 			/* not sufficient data for the full frame yet */
-			return len;
+			return trs->bits_in_buf;
 		} else {
 			/* we have successfully received (at least) one frame */
 			tetra_tdma_time_add_tn(&t_phy_state.time, 1);
@@ -150,5 +148,5 @@ int tetra_burst_sync_in(struct tetra_rx_state *trs, uint8_t *bits, unsigned int
 		break;
 
 	}
-	return len;
+	return trs->bits_in_buf;
 }
diff --git a/src/phy/tetra_burst_sync.h b/src/phy/tetra_burst_sync.h
index 7862461..8b6fedb 100644
--- a/src/phy/tetra_burst_sync.h
+++ b/src/phy/tetra_burst_sync.h
@@ -21,6 +21,6 @@ struct tetra_rx_state {
 
 
 /* input a raw bitstream into the tetra burst synchronizaer */
-int tetra_burst_sync_in(struct tetra_rx_state *trs, uint8_t *bits, unsigned int len);
+int tetra_burst_sync_in(struct tetra_rx_state *trs, FILE *infile);
 
 #endif /* TETRA_BURST_SYNC_H */
diff --git a/src/tetra-rx.c b/src/tetra-rx.c
index c751772..f352412 100644
--- a/src/tetra-rx.c
+++ b/src/tetra-rx.c
@@ -38,7 +38,7 @@ void *tetra_tall_ctx;
 
 int main(int argc, char **argv)
 {
-	int fd;
+	FILE *infile;
 	struct tetra_rx_state *trs;
 	struct tetra_mac_state *tms;
 
@@ -47,9 +47,9 @@ int main(int argc, char **argv)
 		exit(1);
 	}
 
-	fd = open(argv[1], O_RDONLY);
-	if (fd < 0) {
-		perror("open");
+	infile = fopen(argv[1], "r");
+	if (infile < 0) {
+		perror("fopen");
 		exit(2);
 	}
 
@@ -61,20 +61,13 @@ int main(int argc, char **argv)
 	trs = talloc_zero(tetra_tall_ctx, struct tetra_rx_state);
 	trs->burst_cb_priv = tms;
 
-	while (1) {
-		uint8_t buf[64];
-		int len;
-
-		len = read(fd, buf, sizeof(buf));
-		if (len < 0) {
-			perror("read");
-			exit(1);
-		} else if (len == 0) {
-			printf("EOF");
-			break;
-		}
-		tetra_burst_sync_in(trs, buf, len);
+	tetra_init_train_seq_filter();
+
+	int rc;
+	while (!(feof(infile) && rc <= 0)) {
+		rc = tetra_burst_sync_in(trs, infile);
 	}
+	fclose(infile);
 
 	talloc_free(trs);
 	talloc_free(tms);
