mavfft_int.py 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. #!/usr/bin/env python
  2. '''
  3. interactively select accel and gyro data for FFT analysis
  4. '''
  5. from __future__ import print_function
  6. import numpy
  7. import pylab
  8. import matplotlib
  9. import matplotlib.pyplot as pyplot
  10. from argparse import ArgumentParser
  11. parser = ArgumentParser(description=__doc__)
  12. parser.add_argument("--condition", default=None, help="select packets by condition")
  13. parser.add_argument("logs", metavar="LOG", nargs="+")
  14. args = parser.parse_args()
  15. from pymavlink import mavutil
  16. try:
  17. raw_input # Python 2
  18. except NameError:
  19. raw_input = input # Python 3
  20. def plot_input(data, msg, prefix, start, end):
  21. preview = pylab.figure()
  22. preview.set_size_inches(12, 3, forward=True)
  23. for axis in ['X', 'Y', 'Z']:
  24. field = msg + '.' + prefix + axis
  25. d = numpy.array(data[field][start:end])
  26. pylab.plot( d, marker='.', label=field )
  27. pylab.legend(loc='upper right')
  28. # pylab.ylabel('m/sec/sec')
  29. pylab.subplots_adjust(left=0.06, right=0.95, top=0.95, bottom=0.16)
  30. preview.canvas.set_window_title('FFT input: ' + msg)
  31. pylab.show()
  32. def check_drops(data, msg, start, end):
  33. ts = 1e-6 * numpy.array(data[msg + '.TimeUS'])
  34. seqcnt = numpy.array(data[msg + '.SampleC'])
  35. deltas = numpy.diff(seqcnt[start:end])
  36. # print('ndeltas: ', len(deltas))
  37. duration = ts[end] - ts[start]
  38. print(msg + ' duration: {0:.3f} seconds'.format(duration))
  39. avg_rate = float(end - start - 1) / duration
  40. print('average logging rate: {0:.0f} Hz'.format(avg_rate))
  41. ts_mean = numpy.mean(deltas)
  42. dmin = numpy.min(deltas)
  43. dmax = numpy.max(deltas)
  44. print('sample count delta min: {0}, max: {1}'.format(dmin, dmax))
  45. if (dmin != dmax):
  46. print('sample count delta mean: ', '{0:.2f}, std: {0:.2f}'.format(ts_mean, numpy.std(deltas)))
  47. print('sensor sample rate: {0:.0f} Hz'.format(ts_mean * avg_rate))
  48. drop_lens = []
  49. drop_times = []
  50. intvl_count = [0]
  51. for i in range(0, len(deltas)):
  52. if (deltas[i] > 1.5 * ts_mean):
  53. drop_lens.append(deltas[i])
  54. drop_times.append(ts[start+i])
  55. print('dropout at sample {0}: length {1}'.format(start+i, deltas[i]))
  56. print('{0:d} sample intervals > {1:.3f}'.format(len(drop_lens), 1.5 * ts_mean))
  57. return avg_rate
  58. def fft(logfile):
  59. '''display fft for raw ACC data in logfile'''
  60. print("Processing log %s" % filename)
  61. mlog = mavutil.mavlink_connection(filename)
  62. data = {'ACC1.rate' : 1000,
  63. 'ACC2.rate' : 1600,
  64. 'ACC3.rate' : 1000,
  65. 'GYR1.rate' : 1000,
  66. 'GYR2.rate' : 800,
  67. 'GYR3.rate' : 1000}
  68. for acc in ['ACC1','ACC2','ACC3']:
  69. for ax in ['AccX', 'AccY', 'AccZ', 'SampleC', 'TimeUS']:
  70. data[acc+'.'+ax] = []
  71. for gyr in ['GYR1','GYR2','GYR3']:
  72. for ax in ['GyrX', 'GyrY', 'GyrZ', 'SampleC', 'TimeUS']:
  73. data[gyr+'.'+ax] = []
  74. # now gather all the data
  75. while True:
  76. m = mlog.recv_match(condition=args.condition)
  77. if m is None:
  78. break
  79. type = m.get_type()
  80. if type.startswith("ACC"):
  81. data[type+'.AccX'].append(m.AccX)
  82. data[type+'.AccY'].append(m.AccY)
  83. data[type+'.AccZ'].append(m.AccZ)
  84. data[type+'.SampleC'].append(m.SampleC)
  85. data[type+'.TimeUS'].append(m.TimeUS)
  86. if type.startswith("GYR"):
  87. data[type+'.GyrX'].append(m.GyrX)
  88. data[type+'.GyrY'].append(m.GyrY)
  89. data[type+'.GyrZ'].append(m.GyrZ)
  90. data[type+'.SampleC'].append(m.SampleC)
  91. data[type+'.TimeUS'].append(m.TimeUS)
  92. # SampleC is just a sample counter
  93. ts = 1e-6 * numpy.array(data['ACC1.TimeUS'])
  94. seqcnt = numpy.array(data['ACC1.SampleC'])
  95. print("Extracted %u data points" % len(data['ACC1.AccX']))
  96. # interactive selection of analysis window
  97. preview = pylab.figure()
  98. preview.set_size_inches(12, 3, forward=True)
  99. msg = 'ACC1'
  100. for axis in ['X', 'Y', 'Z']:
  101. field = msg + '.Acc' + axis
  102. d = numpy.array(data[field])
  103. pylab.plot( d, marker='.', label=field )
  104. pylab.legend(loc='upper right')
  105. pylab.ylabel('m/sec/sec')
  106. pylab.subplots_adjust(left=0.06, right=0.95, top=0.95, bottom=0.16)
  107. pylab.show()
  108. currentAxes = preview.gca()
  109. s_start = 0
  110. s_end = len(ts)-1
  111. n_samp = s_end - s_start
  112. currentAxes.set_xlim(s_start, s_end)
  113. # outer loop for repeating time window selection
  114. while True:
  115. while True:
  116. print('select sample range for fft analysis')
  117. preview.canvas.set_window_title('select sample range')
  118. try:
  119. s_start = input('start sample: ')
  120. s_end = input('end sample: ')
  121. currentAxes.set_xlim(s_start, s_end)
  122. except:
  123. break
  124. # process selected samples
  125. s_start = int(currentAxes.get_xlim()[0])
  126. s_end = int(currentAxes.get_xlim()[1])
  127. n_samp = s_end - s_start
  128. print('sample range: ', s_start, s_end)
  129. print('N samples: ', n_samp)
  130. # check for dropouts: (delta > 1)
  131. avg_rate = check_drops(data, 'ACC1', s_start, s_end)
  132. title = 'FFT input: {0:s} ACC1[{1:d}:{2:d}], {3:d} samples'.format(logfile, s_start, s_end, n_samp)
  133. currentAxes.set_xlabel('sample index : nsamples: {0:d}, avg rate: {1:.0f} Hz'.format(n_samp, avg_rate))
  134. preview.canvas.set_window_title(title)
  135. preview.savefig('acc1z.png')
  136. for msg in ['ACC1', 'GYR1', 'ACC2', 'GYR2']:
  137. if msg.startswith('ACC'):
  138. prefix = 'Acc'
  139. title = '{2} FFT [{0:d}:{1:d}]'.format(s_start, s_end, msg)
  140. else:
  141. prefix = 'Gyr'
  142. title = '{2} FFT [{0:d}:{1:d}]'.format(s_start, s_end, msg)
  143. # check for dropouts
  144. data[msg+'.rate'] = check_drops(data, msg, s_start, s_end)
  145. plot_input(data, msg, prefix, s_start, s_end)
  146. fftwin = pylab.figure()
  147. fftwin.set_size_inches(12, 3, forward=True)
  148. f_res = float(data[msg+'.rate']) / n_samp
  149. max_fft = 0
  150. abs_fft = {}
  151. index = 0
  152. avg = {'X':0, 'Y':0, 'Z':0}
  153. for axis in ['X', 'Y', 'Z']:
  154. field = msg + '.' + prefix + axis
  155. d = data[field][s_start:s_end]
  156. if len(d) == 0:
  157. continue
  158. d = numpy.array(d)
  159. freq = numpy.fft.rfftfreq(len(d), 1.0 / data[msg+'.rate'])
  160. # remove mean
  161. avg[axis] = numpy.mean(d)
  162. d -= avg[axis]
  163. print('{1} DC component: {0:.3f}'.format(avg[axis], field))
  164. # transform
  165. d_fft = numpy.fft.rfft(d)
  166. abs_fft[axis] = numpy.abs(d_fft)
  167. # remember the max over all axes
  168. thismax = numpy.max(abs_fft[axis])
  169. if (max_fft < thismax):
  170. max_fft = thismax
  171. index += 1
  172. for axis in ['X', 'Y', 'Z']:
  173. # scale to 0dB = max
  174. field = msg + '.' + prefix + axis
  175. db_fft = 20 * numpy.log10(abs_fft[axis] / max_fft)
  176. pylab.plot( freq, db_fft, label=field )
  177. fftwin.canvas.set_window_title(title)
  178. fftwin.gca().set_ylim(-90, 0)
  179. pylab.legend(loc='upper right')
  180. pylab.xlabel('Hz : res: ' + '{0:.3f}'.format(f_res))
  181. pylab.ylabel('dB X {0:.3f} Y {1:.3f} Z {2:.3f}\n'.format(avg['X'], avg['Y'], avg['Z']))
  182. pylab.subplots_adjust(left=0.1, right=0.95, top=0.95, bottom=0.16)
  183. fftwin.savefig(msg + '_fft.png')
  184. try:
  185. selection = raw_input('q to proceed to next file, anything else to select a new range: ')
  186. print(selection)
  187. except:
  188. continue
  189. if (selection == 'q'):
  190. break
  191. pylab.ion()
  192. for filename in args.logs:
  193. fft(filename)
  194. print('type ctrl-c to close windows and exit')
  195. pylab.ioff()
  196. pylab.show()