1. #!/usr/bin/python3
  2. import re
  3. import logging
  4. import matplotlib.pyplot as plt
  5. from mpl_toolkits.mplot3d import Axes3D
  6. class Heatmap(object):
  7. def __init__(self):
  8. self.source = open("console", "r")
  9. # [MSG:X Stallguard 0 SG_Val: 0708 Rate: 00180 mm/min SG_Setting:5 MPos:332.460,33.630,52.780]
  10. self.line_re = re.compile(r".*\[MSG:(?P<axis>[XYZ]) Stallguard "
  11. r"(?P<triggered>[0-1]) *"
  12. r"SG_Val: (?P<sgval>[0-9]+) *"
  13. r"Rate: (?P<feedrate>[0-9]+) mm/min "
  14. r"SG_Setting:(?P<sgsens>[0-9]+) "
  15. r"MPos:(?P<xpos>[0-9]+\.[0-9]+),(?P<ypos>[0-9]+\.[0-9]+),(?P<zpos>[0-9]+\.[0-9]+)\].*"
  16. )
  17. self.datapoints = {'X': [], 'Y':[], 'Z':[]}
  18. self.meaningful_feed_range = {"X" : (60,800), "Y": (60,800), "Z": (10,180)} # filter out rate < 60mm/min filter out jog with rate > 800mm/m
  19. self.normalization_window = 5 # running avg window
  20. self.logger = logging.getLogger(__name__)
  21. self.figs = {}
  22. def parse_source(self):
  23. for l in self.source.readlines():
  24. if "Stallguard" not in l:
  25. continue
  26. m = re.match(self.line_re, l)
  27. if not m:
  28. continue
  29. datapoint = m.groupdict()
  30. self.datapoints[datapoint["axis"]].append(datapoint)
  31. def is_significant(self, point):
  32. axis = point["axis"]
  33. rate = int(point["feedrate"])
  34. if self.meaningful_feed_range[axis][0] <= rate <= self.meaningful_feed_range[axis][1]:
  35. return True
  36. return False
  37. def normalize(self, window=5):
  38. for axis in self.datapoints.keys():
  39. n = 0
  40. while n < len(self.datapoints[axis]):
  41. if not self.is_significant(self.datapoints[axis][n]):
  42. self.logger.warn("removing %s", self.datapoints[axis][n])
  43. del self.datapoints[axis][n]
  44. continue
  45. sgval_sum = 0
  46. summed_for = 0
  47. start_window = max(n - window, 0)
  48. end_window = min(len(self.datapoints[axis])-1, n + window)
  49. nn = start_window
  50. while nn <= end_window and nn < len(self.datapoints[axis])-1:
  51. if not self.is_significant(self.datapoints[axis][nn]):
  52. self.logger.warn("removing %s", self.datapoints[axis][nn])
  53. del self.datapoints[axis][nn]
  54. continue
  55. sgval_sum += int(self.datapoints[axis][nn]["sgval"])
  56. summed_for += 1
  57. nn += 1
  58. self.datapoints[axis][n]["sgval"] = sgval_sum / summed_for
  59. n += 1
  60. def plot(self):
  61. for motor_axis in self.datapoints.keys():
  62. self.figs[motor_axis] = plt.figure(dpi=1200)
  63. ax = self.figs[motor_axis].add_subplot(111, projection='3d')
  64. xs = [float(point["xpos"]) for point in self.datapoints[motor_axis]]
  65. ys = [float(point["ypos"]) for point in self.datapoints[motor_axis]]
  66. zs = [float(point["zpos"]) for point in self.datapoints[motor_axis]]
  67. c = [point["sgval"] for point in self.datapoints[motor_axis]]
  68. # matplotlib.colors.Colormap
  69. scatter = ax.scatter(xs, ys, zs, zdir='z', s=1, c=c, cmap=plt.get_cmap('RdYlGn')) #), depthshade=True)
  70. legend1 = ax.legend(*scatter.legend_elements(num=5), loc="upper left", title="SG_val")
  71. ax.add_artist(legend1)
  72. self.figs[motor_axis].savefig("%s.png" % motor_axis)
  73. h = Heatmap()
  74. h.parse_source()
  75. h.normalize()
  76. h.plot()