diff --git a/goal.sh b/goal.sh
index 7f5c04923a49bf659e1a9f2680c0507e55de48f3..7d1aa6891f76a64e31f3bc333a57dc0c0c372287 100755
--- a/goal.sh
+++ b/goal.sh
@@ -1,3 +1,3 @@
 #!/usr/bin/env bash
-./gradlew :ros2rag.goal:installDist
-./ros2rag.goal/build/install/ros2rag.goal/bin/ros2rag.goal $@
+./gradlew :ros2rag.goal:installDist && \
+	./ros2rag.goal/build/install/ros2rag.goal/bin/ros2rag.goal $@
diff --git a/receiver.sh b/receiver.sh
index a242dc368e8aad738cf0be249b5339e50e014d7e..b4bbb00648d1965f64dbd255b662c398d7226754 100755
--- a/receiver.sh
+++ b/receiver.sh
@@ -1,3 +1,3 @@
 #!/usr/bin/env bash
-./gradlew :ros2rag.receiverstub:installDist
-./ros2rag.receiverstub/build/install/ros2rag.receiverstub/bin/ros2rag.receiverstub $@
+./gradlew :ros2rag.receiverstub:installDist && \
+	./ros2rag.receiverstub/build/install/ros2rag.receiverstub/bin/ros2rag.receiverstub $@
diff --git a/ros2rag.senderstub/src/main/java/de/tudresden/inf/st/ros2rag/senderstub/SenderMain.java b/ros2rag.senderstub/src/main/java/de/tudresden/inf/st/ros2rag/senderstub/SenderMain.java
index e518c86cfa2b7235744b349aae1dc22eedfe8efe..f540f1cc8825fcec1c357c7046ed94e47a24cbe7 100644
--- a/ros2rag.senderstub/src/main/java/de/tudresden/inf/st/ros2rag/senderstub/SenderMain.java
+++ b/ros2rag.senderstub/src/main/java/de/tudresden/inf/st/ros2rag/senderstub/SenderMain.java
@@ -3,30 +3,59 @@ package de.tudresden.inf.st.ros2rag.senderstub;
 import de.tudresden.inf.st.ros2rag.starter.ast.MqttHandler;
 import panda.Linkstate;
 
+import java.nio.file.Files;
+import java.nio.file.Paths;
 import java.util.concurrent.TimeUnit;
 
 public class SenderMain {
   public static void main(String[] args) throws Exception {
+    // either 3 or 4 arguments
+    // 3 arguments: topic filename wait
+    // 4 arguments: topic x y z
     // assume 4 arguments
-    if (args.length < 4) {
-      System.err.println("Sends a new position, expected arguments: topic x y z");
+    if (args.length < 3 || args.length > 4) {
+      System.err.println("Either sends a new position, arguments: topic x y z");
+      System.err.println("Or reads positions from a file, arguments: topic filename wait");
       return;
     }
+    MqttHandler sender = new MqttHandler("sender stub").dontSendWelcomeMessage();
+    sender.setHost("localhost", 1883);
+    sender.waitUntilReady(2, TimeUnit.SECONDS);
     final String topic = args[0];
+    if (args.length == 3) {
+      final String filename = args[1];
+      final int wait = Integer.parseInt(args[2]);
+      try {
+        for (String line : Files.readAllLines(Paths.get(filename))) {
+          if (line.isEmpty()) continue;
+          String[] lineSplit = line.split(";");
+          publish(sender, topic,
+              Float.parseFloat(lineSplit[0]),
+              Float.parseFloat(lineSplit[1]),
+              Float.parseFloat(lineSplit[2]));
+          TimeUnit.MILLISECONDS.sleep(wait);
+        }
+      } catch (InterruptedException ignore) {
+      }
+    } else {
+      publish(sender, topic,
+          Float.parseFloat(args[1]),
+          Float.parseFloat(args[2]),
+          Float.parseFloat(args[3]));
+    }
+    sender.close();
+  }
+
+  private static void publish(MqttHandler sender, String topic, float x, float y, float z) {
     Linkstate.PandaLinkState pls = Linkstate.PandaLinkState.newBuilder()
-        .setName(args[0])
+        .setName(topic)
         .setPos(Linkstate.PandaLinkState.Position.newBuilder()
-            .setPositionX(Float.parseFloat(args[1]))
-            .setPositionY(Float.parseFloat(args[2]))
-            .setPositionZ(Float.parseFloat(args[3]))
+            .setPositionX(x)
+            .setPositionY(y)
+            .setPositionZ(z)
             .build())
         .build();
     final byte[] message = pls.toByteArray();
-
-    MqttHandler sender = new MqttHandler("sender stub").dontSendWelcomeMessage();
-    sender.setHost("localhost", 1883);
-    sender.waitUntilReady(2, TimeUnit.SECONDS);
     sender.publish(topic, message);
-    sender.close();
   }
 }
diff --git a/send.sh b/send.sh
new file mode 100755
index 0000000000000000000000000000000000000000..8c55e2d92c86f4a7f6a71269fa8a0dcc5af2ff01
--- /dev/null
+++ b/send.sh
@@ -0,0 +1,3 @@
+#!/usr/bin/env bash
+./gradlew :ros2rag.senderstub:installDist && \
+	./ros2rag.senderstub/build/install/ros2rag.senderstub/bin/ros2rag.senderstub $@
diff --git a/send_one.sh b/send_one.sh
deleted file mode 100755
index 5af156b01aca6b785ef00e7cd45e573a92274ff1..0000000000000000000000000000000000000000
--- a/send_one.sh
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/usr/bin/env bash
-./ros2rag.senderstub/build/install/ros2rag.senderstub/bin/ros2rag.senderstub $@
diff --git a/sequence.csv b/sequence.csv
new file mode 100644
index 0000000000000000000000000000000000000000..4d66f7ad7c3ab11d5a7af84574269efbf4fada36
--- /dev/null
+++ b/sequence.csv
@@ -0,0 +1,30 @@
+0.0; 0.0; 0.0
+0.3; 0.3; 0.3
+0.6; 0.6; 0.6
+1.0; 1.0; 1.0
+
+1.0; 1.0; 1.0
+1.0; 1.0; 1.0
+1.0; 1.0; 1.0
+1.0; 1.0; 1.0
+1.0; 1.0; 1.0
+1.0; 1.0; 1.0
+
+1.0; 0.6; 1.0
+1.0; 0.3; 1.0
+1.0; 0.0; 1.0
+
+1.0; 0.0; 1.0
+1.0; 0.0; 1.0
+1.0; 0.0; 1.0
+1.0; 0.0; 1.0
+
+0.6; 0.0; 1.0
+0.3; 0.0; 1.0
+0.0; 0.0; 1.0
+
+0.0; 0.0; 1.0
+0.0; 0.0; 1.0
+0.0; 0.0; 1.0
+0.0; 0.0; 1.0
+0.0; 0.0; 1.0
diff --git a/starter.sh b/starter.sh
index 12d625da63994a690aee3ee89686bc0c8bfa81f5..e3bb9b9e8e3bb6684b32edb0b5eec17c00529048 100755
--- a/starter.sh
+++ b/starter.sh
@@ -1,3 +1,3 @@
 #!/usr/bin/env bash
-./gradlew :ros2rag.starter:installDist
-./ros2rag.starter/build/install/ros2rag.starter/bin/ros2rag.starter $@
+./gradlew :ros2rag.starter:installDist && \
+	./ros2rag.starter/build/install/ros2rag.starter/bin/ros2rag.starter $@