diff --git a/pq-tls-benchmark-framework/emulation-exp/code/kex/scripts/generate_graphs.py b/pq-tls-benchmark-framework/emulation-exp/code/kex/scripts/generate_graphs.py
index 51a91941484104e7d1061f5c9a8a407bbd136465..0ad3804eb905d5c23a284a01cb35689dff124078 100755
--- a/pq-tls-benchmark-framework/emulation-exp/code/kex/scripts/generate_graphs.py
+++ b/pq-tls-benchmark-framework/emulation-exp/code/kex/scripts/generate_graphs.py
@@ -1254,7 +1254,107 @@ def plot_distributions(data):
                 plt.close()
                 # return
 
+    def plot_cdf_comparison_between_cquiche_cubic_and_tlstcp(data, cutoff=None):
+        print("plot_cdf_comparison_between_cquiche_cubic_and_tlstcp")
+
+        subdir_string = ""
+        graph_name_extension = ""
+        if cutoff is not None:
+            subdir_string = f"cutoff-at-{cutoff}/"
+            graph_name_extension = f"-cutoff-at-{cutoff}"
+
+        os.makedirs(
+            f"{PLOTS_DIR}/distributions/cdf/comparison-between-cquiche-cubic-and-tlstcp/{subdir_string}",
+            mode=0o777,
+            exist_ok=True,
+        )
+
+        unique_combinations = data[["scenario", "sec_level"]].drop_duplicates()
+
+        # filter out every scenario that is not packetloss or corrupt
+        unique_combinations = unique_combinations[
+            unique_combinations["scenario"].isin(["packetloss", "corrupt"])
+        ]
+        # print(unique_combinations)
+
+        for _, row in unique_combinations.iterrows():
+            filtered_data = filter_data(
+                data,
+                scenario=row["scenario"],
+                sec_level=row["sec_level"],
+            )
+            filtered_data = filtered_data.query(
+                "protocol == 'tlstcp' or protocol == 'cquiche-cubic'"
+            )
+            # print(filtered_data)
+
+            def get_row_name_for_scenario(scenario):
+                match scenario:
+                    case "packetloss":
+                        return "srv_pkt_loss"
+                    case "corrupt":
+                        return "srv_corrupt"
+                    case _:
+                        print("No case for this scenario:", row["scenario"])
+                        exit(1)
+
+            row_name = get_row_name_for_scenario(row["scenario"])
+
+            scenario_param_values = filtered_data[row_name].unique()
+            # print(scenario_param_values)
+
+            for param_value in scenario_param_values:
+                filtered_data_single_param_value = filtered_data.query(
+                    f"{row_name} == {param_value}"
+                )
+                # print(filtered_data_single_param_value)
+
+                plt.figure()
+                for _, row in filtered_data_single_param_value.iterrows():
+                    color, _ = get_color_and_mode(
+                        row["kem_alg"], combined_with_hybrids=False
+                    )
+                    mode = "-"
+                    if row["protocol"] == "tlstcp":
+                        mode = "--"
+
+                    prefix_label = "cquiche-cubic-"
+                    if row["protocol"] == "tlstcp":
+                        prefix_label = "tlstcp-"
+                    plt.ecdf(
+                        row["measurements"],
+                        label=f"{prefix_label}{row['kem_alg']}",
+                        color=color,
+                        linestyle=mode,
+                    )
+
+                # plt.ylim(bottom=0)
+                if cutoff is not None:
+                    plt.xlim(0, cutoff)
+                # plt.xlim(left=0, right=x.max() + (x.max() / 50))
+                plt.ylabel("Wahrscheinlichkeit")
+                plt.xlabel(f"Time-to-first-byte (ms)")
+                plt.yticks(
+                    np.arange(0, 1.1, 0.1), [f"{y:.1f}" for y in np.arange(0, 1.1, 0.1)]
+                )
+                plt.grid()
+
+                plt.legend(
+                    bbox_to_anchor=(0.5, 1),
+                    loc="lower center",
+                    ncol=3,
+                    fontsize="small",
+                )
+
+                plt.tight_layout()
+                plt.savefig(
+                    f"{PLOTS_DIR}/distributions/cdf/comparison-between-cquiche-cubic-and-tlstcp/{subdir_string}cdf-for-{row['scenario']}-{row['sec_level']}-{param_value}{graph_name_extension}.pdf"
+                )
+                plt.close()
+
     def plot_cdf_of_sec_level(data, cutoff=None):
+        print("plot_cdf_of_sec_level")
+
         subdir_string = ""
         graph_name_extension = ""
         if cutoff is not None:
@@ -1325,6 +1425,9 @@ def plot_distributions(data):
                 # plt.xlim(left=0, right=x.max() + (x.max() / 50))
                 plt.ylabel("Wahrscheinlichkeit")
                 plt.xlabel(f"Time-to-first-byte (ms)")
+                plt.yticks(
+                    np.arange(0, 1.1, 0.1), [f"{y:.1f}" for y in np.arange(0, 1.1, 0.1)]
+                )
                 plt.grid()
 
                 plt.legend(
@@ -1348,6 +1451,10 @@ def plot_distributions(data):
     plot_cdf_of_sec_level(data, cutoff=2000)
     plot_cdf_of_sec_level(data, cutoff=5000)
 
+    plot_cdf_comparison_between_cquiche_cubic_and_tlstcp(data, cutoff=None)
+    plot_cdf_comparison_between_cquiche_cubic_and_tlstcp(data, cutoff=2000)
+    plot_cdf_comparison_between_cquiche_cubic_and_tlstcp(data, cutoff=5000)
+
 
 # TODO make a violinplot/eventplot for many algos in static scenario
 def plot_static_data(data):