import os import sys import copy import glob import wave from Riff import * from pprint import pprint """ ======================================================================== Convert Stardust Dust Flux Monitor Instrument (DFMI) encounter data to a WAVE Audio file. ======================================================================== Based on DFMI SoundMaker 2, written by Pasquale DiDonna in Visual Basic. ======================================================================== 1) Parse the command line arguments: 1.1) WAVE directory [required] 1.2) Ouput WAVE files name [required] 1.3) Threshold ID for LEFT audio channel [default M1, optional, one of A1b,A1a,A2b,A2a, M1,M2,M3,M4, m1,m2,m3,m4, N1,N2,N3,N4] 1.4) Threshold ID for RIGHT audio channel [default m1, optional, one of A1b,A1a,A2b,A2a, M1,M2,M3,M4, m1,m2,m3,m4, N1,N2,N3,N4] 2) Read in sampled WAVE files from WAVE directory (1.1 above): - tick.wav: contains one click over 100ms - tick11.wave - tick120.wav: contain 11 to 120 clicks over 1s each - create a no-sound WAVE sample 2.1) In the Cornell Science Data Center, these WAVE files are in subdirectory wavs/ relative to where this file, tab2wav.py resides 3) Read STDIN as the comma-separated-values from a DFMI data file, e.g. dfmienc_v2_yyyymmdd_hhmmss.tab one line at a time 4) Check the DFMI SYNC, mode and clock columns: 4.1) DFMI SYNC must be "A5", column 4, or the line is skipped 4.2) DFMI mode must be "NORM", column 5, or the line is skipped 5) Calculate changes across twelve Threshold counters in columns 9-20 from previous line, adding 8- or 16-bit rollover so changes are always non-negative 6) Compare 1-second DFMI clock, column 3, against previous line's DFMI clock 6.1) When the DFMI clock does not change, accumulate counter changes within that DFMI clock second 6.2) When the DFMI clock does change, use the accumulated counts from the previous second, and from the Threshold counter columns selected (1.3 and 1.4 above), and assign to that second the two WAVE files with the maximum numbers of clicks less than each of those counters. 6.2.1) If an accumulated count is greater than 0 and less than 11, the script uses the WAVE data from tick.wav (one click in 100ms to generate a one-second sample with the correct number of clicks. 7) When all lines have been read, the concatenate and write out the samples to the output WAVE file (1.2 above) ======================================================================== Usage: python tab2wav.py wavs/ out.wav [aLeft[ aRight]] < dfmienc_v2_yyyymmdd_hhmmss.tab wavs/ => directory containing tick.wav, tick11.wav to tick120.wav out.wav => output WAVE filepath aLeft => threshold to use for left speaker: A[12][ab] M[1234] m[1234] N[1234] aLeft => threshold to use for right speaker: " e.g. to use all the data from the Tempel 1 encounter from 04:38:50 to 04:39:30, use this command: awk -F, \ '$1<"\\"2011-02-15T04:38:50{next}$1>"\\"2011-02-15T04:39:30{next}{print}' \ dfmienc_v2_110215_162959.tab \ | python csv2wav.py wavs out.wav M1 m1 < dfmienc_v1_110215_162959.csv """ ######################################################################## ### Utility routines ######################################################################## bytPerSec=20000 ######################################################################## ### find the key in tickDict that is closest to nInp without being ### greater than nInp. If 010: bestN=0 for N in tickDict: if nInp>N and N>bestN: bestN = N return bestN ### create matches 1 to 10 from 100ms in tickDict[-1] ... nPad = max( 0 ,(bytPerSec/nInp) - tickDict[-1].subChunks['data'][0].Length ) riff1Data = tickDict[-1].subChunks['data'][0].Data + chr(0) * nPad newRiff = emptyRiff.deepcopy() while newRiff.subChunks['data'][0].Length11: continue ### tickNNNN*.wav, too high else: id=int(bn[4:].split('.')[0]) ### tickNN.wav or tickNNN.wav, 1s, multiple clicks tickDict[id] = Riff(tickFn) ### Append zeroes if this Riff is shorter than 20,000 bytes L =tickDict[id].subChunks['data'][0].Length if id>10 and L