選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

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