You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

214 satır
6.5KB

  1. #!/usr/bin/env python3
  2. import multiprocessing
  3. import os
  4. from argparse import ArgumentParser
  5. from math import ceil
  6. from time import sleep
  7. import pandas as pd
  8. import geopandas as gpd
  9. import contextily as cx
  10. import matplotlib.pyplot as plt
  11. from util import chunk_list
  12. def csv_to_dataframe(csv_list, dummy):
  13. global n
  14. global frame_list
  15. transmission_df = None
  16. for csv in csv_list:
  17. tmp_df = pd.read_csv(
  18. "{}{}".format(args.pcap_csv_folder, csv),
  19. dtype=dict(is_retranmission=bool, is_dup_ack=bool),
  20. )
  21. tmp_df["datetime"] = pd.to_datetime(tmp_df["datetime"]) - pd.Timedelta(hours=1)
  22. tmp_df = tmp_df.set_index("datetime")
  23. tmp_df.index = pd.to_datetime(tmp_df.index)
  24. if transmission_df is None:
  25. transmission_df = tmp_df
  26. else:
  27. transmission_df = pd.concat([transmission_df, tmp_df])
  28. n.value += 1
  29. frame_list.append(transmission_df)
  30. from itertools import islice
  31. def chunk(it, size):
  32. it = iter(it)
  33. return iter(lambda: tuple(islice(it, size)), ())
  34. if __name__ == "__main__":
  35. parser = ArgumentParser()
  36. parser.add_argument("-f", "--gps_file", required=True, help="GPS csv file.")
  37. parser.add_argument("-s", "--serial_file", required=True, help="Serial csv file.")
  38. parser.add_argument("-p", "--pcap_csv_folder", required=True, help="PCAP csv folder.")
  39. parser.add_argument("-a", "--column", required=True, help="Column to plot")
  40. parser.add_argument("-l", "--label", help="Label above the plot.")
  41. parser.add_argument("--no_legend", action="store_false", default=True, help="Do not show legend.")
  42. parser.add_argument("--save", default=None, help="Location to save pdf file.")
  43. parser.add_argument(
  44. "--show_providerinfo",
  45. default=False,
  46. help="Show providerinfo for map tiles an zoom levels.",
  47. )
  48. parser.add_argument(
  49. "-c",
  50. "--cores",
  51. default=1,
  52. type=int,
  53. help="Number of cores for multiprocessing.",
  54. )
  55. parser.add_argument(
  56. "-i",
  57. "--interval",
  58. default=10,
  59. type=int,
  60. help="Time interval for rolling window.",
  61. )
  62. args = parser.parse_args()
  63. manager = multiprocessing.Manager()
  64. n = manager.Value("i", 0)
  65. frame_list = manager.list()
  66. jobs = []
  67. # load all pcap csv into one dataframe
  68. pcap_csv_list = list()
  69. for filename in os.listdir(args.pcap_csv_folder):
  70. if filename.endswith(".csv") and "tcp" in filename:
  71. pcap_csv_list.append(filename)
  72. parts = chunk(pcap_csv_list, ceil(len(pcap_csv_list) / args.cores))
  73. print("Start processing with {} jobs.".format(args.cores))
  74. for p in parts:
  75. process = multiprocessing.Process(target=csv_to_dataframe, args=(p, "dummy"))
  76. jobs.append(process)
  77. for j in jobs:
  78. j.start()
  79. print("Started all jobs.")
  80. # Ensure all of the processes have finished
  81. finished_job_counter = 0
  82. working = ["|", "/", "-", "\\", "|", "/", "-", "\\"]
  83. w = 0
  84. while len(jobs) != finished_job_counter:
  85. sleep(1)
  86. print(
  87. "\r\t{}{}{}\t Running {} jobs ({} finished). Processed {} out of {} pcap csv files. ({}%) ".format(
  88. working[w],
  89. working[w],
  90. working[w],
  91. len(jobs),
  92. finished_job_counter,
  93. n.value,
  94. len(pcap_csv_list),
  95. round((n.value / len(pcap_csv_list)) * 100, 2),
  96. ),
  97. end="",
  98. )
  99. finished_job_counter = 0
  100. for j in jobs:
  101. if not j.is_alive():
  102. finished_job_counter += 1
  103. if (w + 1) % len(working) == 0:
  104. w = 0
  105. else:
  106. w += 1
  107. print("\r\nSorting table...")
  108. transmission_df = pd.concat(frame_list)
  109. frame_list = None
  110. transmission_df = transmission_df.sort_index()
  111. print("Calculate goodput...")
  112. transmission_df["goodput"] = transmission_df["payload_size"].rolling("{}s".format(args.interval)).sum()
  113. transmission_df["goodput"] = transmission_df["goodput"].apply(
  114. lambda x: ((x * 8) / args.interval) / 10**6
  115. )
  116. # load dataframe an put it into geopandas
  117. df = pd.read_csv(args.gps_file)
  118. df["kmh"] = df["speed (knots)"].apply(lambda x: x * 1.852)
  119. df = df.set_index("datetime")
  120. df.index = pd.to_datetime(df.index)
  121. gdf = gpd.GeoDataFrame(
  122. df,
  123. geometry=gpd.points_from_xy(df["longitude"], df["latitude"]),
  124. crs="EPSG:4326",
  125. )
  126. gdf = pd.merge_asof(
  127. gdf,
  128. transmission_df,
  129. tolerance=pd.Timedelta("10s"),
  130. right_index=True,
  131. left_index=True,
  132. )
  133. # read serial csv
  134. serial_df = pd.read_csv(args.serial_file)
  135. serial_df["datetime"] = pd.to_datetime(serial_df["datetime"]) - pd.Timedelta(hours=1)
  136. serial_df = serial_df.set_index("datetime")
  137. serial_df.index = pd.to_datetime(serial_df.index)
  138. gdf = pd.merge_asof(
  139. gdf,
  140. serial_df,
  141. tolerance=pd.Timedelta("1s"),
  142. right_index=True,
  143. left_index=True,
  144. )
  145. print(gdf)
  146. print("Start plotting...")
  147. # format to needed format and add basemap as background
  148. df_wm = gdf.to_crs(epsg=3857)
  149. #df_wm.to_csv("debug-data.csv")
  150. # ax2 = df_wm.plot(figsize=(10, 10), alpha=0.5, edgecolor='k')
  151. print(df_wm)
  152. ax2 = df_wm.plot()
  153. ax2 = df_wm.plot(args.column, cmap="hot", legend=args.no_legend, ax=ax2)
  154. # ax2 = df_wm.plot.scatter(x="longitude", y="latitude", c="kmh", cmap="hot")
  155. # zoom 17 is pretty
  156. cx.add_basemap(ax2, source=cx.providers.OpenStreetMap.Mapnik, zoom=15)
  157. # gdf.plot()
  158. ax2.set_axis_off()
  159. ax2.set_title(args.label if args.label else args.column)
  160. if args.show_providerinfo:
  161. #####################################
  162. # Identifying how many tiles
  163. latlon_outline = gdf.to_crs("epsg:4326").total_bounds
  164. def_zoom = cx.tile._calculate_zoom(*latlon_outline)
  165. print(f"Default Zoom level {def_zoom}")
  166. cx.howmany(*latlon_outline, def_zoom, ll=True)
  167. cx.howmany(*latlon_outline, def_zoom + 1, ll=True)
  168. cx.howmany(*latlon_outline, def_zoom + 2, ll=True)
  169. # Checking out some of the other providers and tiles
  170. print(cx.providers.CartoDB.Voyager)
  171. print(cx.providers.Stamen.TonerLite)
  172. print(cx.providers.Stamen.keys())
  173. #####################################
  174. # df.plot(x="longitude", y="latitude", kind="scatter", colormap="YlOrRd")
  175. if args.save:
  176. plt.savefig("{}gps_plot.pdf".format(args.save))
  177. else:
  178. plt.show()