a script to chrono a paintball marker. there are a lot of (unwritten) TODOs and cleaning up that will be done *some day*... also, i haven't compared the results with a true chronograph, but it's close enough to get everyone shooting the same. #!/usr/bin/env python#http://www.python-forum.org/pythonforum/viewtopic.php?f=3&t=7431#p35020import pyaudioimport waveimport threadingimport structimport sysimport osimport subprocess# constantsFEET_PER_METER = 3.2808399SPEED_SOUND_MPS = 340.29SPEED_SOUND_FPS = SPEED_SOUND_MPS * FEET_PER_METER# TODO: compensate for speed of sound# detection rangeMIN_FPS = 150.0MAX_FPS = 350.0AMPLITUDE_THRESHOLD = 1.5# recording ratesBUFFER_SIZE = 1024RATE = 44100SECONDS_PER_SAMPLE = 1.0 / float(RATE)# other recording settingsRECORD_SECONDS = 20CHANNELS = 1FORMAT = pyaudio.paFloat32recording_done = False# get distances in feetDISTANCE_ST = raw_input( 'shooter-target distance in feet (10): ' )DISTANCE_ST = float(DISTANCE_ST) if DISTANCE_ST else 10.0#DISTANCE_MS = raw_input( 'mic-shooter distance in feet (5): ' )#DISTANCE_MS = float(DISTANCE_MS) if DISTANCE_MS else 5.0#DISTANCE_MT = raw_input( 'mic-target distance in feet (5): ' )#DISTANCE_MT = float(DISTANCE_MT) if DISTANCE_MT else 5.0MIN_SAMPLES_THRESHOLD = int( DISTANCE_ST / MAX_FPS * float(RATE) )MAX_SAMPLES_THRESHOLD = int( DISTANCE_ST / MIN_FPS * float(RATE) )print( 'MIN: %i (%ifps)' % (MIN_SAMPLES_THRESHOLD,MAX_FPS) )print( 'MAX: %i (%ifps)' % (MAX_SAMPLES_THRESHOLD,MIN_FPS) )pya = pyaudio.PyAudio()stream = pya.open( format = FORMAT, channels = CHANNELS, rate = RATE, input = True, frames_per_buffer = BUFFER_SIZE, )def stop_recording(): global recording_done recording_done = True#t = threading.Timer( RECORD_SECONDS, stop_recording )#t.start()# for displayingl_loud = ' .*@#'c_loud = len(l_loud)time = long(0)last_peak = []iters = 0max_amp = 0.0sum_amp,total = 0.0,0.0while not recording_done: iters += 1 data = stream.read( BUFFER_SIZE ) data = struct.unpack( '<{0}f'.format( BUFFER_SIZE ), data ) data = [ abs(x) for x in data ] #loud = [ l_loud[ min(int(abs(x)),c_loud-1) ] for x in data ] #sys.stdout.write( ''.join( loud ) ) #continue max_amp = max( max_amp, max( x for x in data ) ) sum_amp += sum( data ) total += float(BUFFER_SIZE) if int(iters % (RATE / BUFFER_SIZE / 2)) == 0: avg_amp = sum_amp / total print( 'amplitude: max = %0.3f, avg = %0.3f' % (max_amp,avg_amp) ) max_amp = 0.0 sum_amp = 0.0 total = 0.0 # find peaks peaks = [ long(t)+time for t,x in enumerate(data) if x > AMPLITUDE_THRESHOLD ] if len(peaks): print( 'peaks: %i' % (len(peaks),) ) peaks = last_peak + peaks if peaks: last_peak = [ peaks[-1] - BUFFER_SIZE ] else: last_peak = [] #if len(peaks): print( 'peaks: %s' % ' '.join([ str(peak) for peak in peaks ]) ) if len(peaks) <= 1: continue # find peak-distances that are within the range we're interested nsamps = [] lt = peaks[0] for t in peaks[1:]: dt = t - lt if dt > MIN_SAMPLES_THRESHOLD and dt < MAX_SAMPLES_THRESHOLD: nsamps += [ dt ] lt = t if not nsamps: continue for s in nsamps: t = s * SECONDS_PER_SAMPLE print( '%0.5fsecs (%isamps) = %0.2ffps' % (t, s, DISTANCE_ST / t) ) subprocess.Popen( [ 'espeak', '"%i"' % int( DISTANCE_ST / t ) ], stdout=subprocess.PIPE, stderr=subprocess.PIPE ) stream.close()pya.terminate() |