NCCOOS Trac Projects: Top | Web | Platforms | Processing | Viz | Sprints | Sandbox | (Wind)

root/DPWP/trunk/DPWP/ADCP_splitter/pd0.py

Revision 225 (checked in by gdusek, 15 years ago)

Re-organized directory structure and cleaned up some code.

Line 
1 #!/usr/bin/python
2 """Functions to handle Acoustic Doppler Current Profiler data.
3
4 As a script (to split a raw PD0 file into waves and currents):
5
6 python pd0.py [path] rawFile wavesFile currentsFile
7 python pd0.py [-p path] -r rawFile -w wavesFile -c currentsFile
8 python pd0.py [--path=path] \\
9                --raw=rawFile \\
10                --waves=wavesFile \\
11                --currents=currentsFile
12
13 where:
14     path         is a path to prepend to the following
15     rawFile      is path of raw PD0 format input file
16     wavesFile    is path of waves PD0 format output file
17     currentsFile is path of currents PD0 format output file
18
19 or (to run the test suite):
20
21 python pd0.py -t
22 python pd0.py --test
23
24 or (to see this help message):
25
26 python pd0.py -h
27 python pd0.py --help
28
29 As a module:
30
31 import adcp.pd0
32 adcp.pd0.split(rawFile,wavesFile,currentsFile)
33
34 where:
35     rawFile      is a file object representing the raw PD0 format input
36     wavesFile    is a file object representing the waves PD0 format output
37     currentsFile is a file object representing the currents PD0 format output
38 """
39
40 import struct
41
42 #
43 # The rawfile is assumed to be in PD0 format.
44 #
45 # PD0 format assumes the file is a succession of ensembles.
46 #
47 # Each ensemble starts with a two byte header identifying the type of data
48 # contained in the ensemble.
49 #
50 # Following the header is a two byte length field specifying the length of
51 # the header, length field, and data combined
52 #
53 # Following the length field is raw data for the number of bytes indicated by
54 # the length field
55 #
56 # Following the raw data is a checksum field which is the two least
57 # significant bytes of the sum of the byte values of the header, length field,
58 # and raw data.
59 #
60 def split(rawFile,wavesFile,currentsFile):
61     """Split PD0 format data into seperate waves and currents
62
63     split()rawFile,wavesFile,currentsFile -> None
64     """
65
66     # header IDs
67     wavesId=0x797f
68     currentsId=0x7f7f
69
70     # convenience function reused for header, length, and checksum
71     def __nextLittleEndianUnsignedShort(file):
72         """Get next little endian unsigned short from file"""
73         raw = file.read(2)
74         return (raw, struct.unpack('<H', raw)[0])
75
76     # factored for readability
77     def __computeChecksum(header, length, ensemble):
78         """Compute a checksum from header, length, and ensemble"""
79         cs = 0   
80         for byte in header:
81             value = struct.unpack('B', byte)[0]
82             cs += value
83         for byte in length:
84             value = struct.unpack('B', byte)[0]
85             cs += value
86         for byte in ensemble:
87             value = struct.unpack('B', byte)[0]
88             cs += value
89         return cs & 0xffff
90
91     # find the first instance of a waves or currents header
92     rawData=rawFile.read()
93     firstWaves = rawData.find(struct.pack('<H',wavesId))
94     firstCurrents= rawData.find(struct.pack('<H',currentsId))
95
96     # bail if neither waves nor currents found
97     if (firstWaves < 0) and (firstCurrents < 0):
98         raise IOError, "Neither waves nor currents header found"
99
100     # get the starting point by throwing out unfound headers
101     # and selecting the minumum
102     firstEnsemble = min(filter(lambda x: x >= 0,(firstWaves,firstCurrents)))
103
104     #seeks to the first occurence of a waves or currents data
105     rawFile.seek(firstEnsemble)
106
107     # loop through raw data
108     rawHeader, header = __nextLittleEndianUnsignedShort(rawFile)
109
110     while (header == wavesId) or (header == currentsId):
111         # get ensemble length
112         rawLength, length = __nextLittleEndianUnsignedShort(rawFile)
113         # read up to the checksum
114         rawEnsemble = rawFile.read(length-4)
115         # get checksum
116         rawChecksum, checksum = __nextLittleEndianUnsignedShort(rawFile)
117
118         computedChecksum = __computeChecksum(rawHeader, rawLength, rawEnsemble)
119
120         if checksum != computedChecksum:
121             raise IOError, "Checksum error"
122
123         # append to output stream
124         if header == wavesId:
125             wavesFile.write(rawHeader)
126             wavesFile.write(rawLength)
127             wavesFile.write(rawEnsemble)
128             wavesFile.write(rawChecksum)
129         elif header == currentsId:
130             currentsFile.write(rawHeader)
131             currentsFile.write(rawLength)
132             currentsFile.write(rawEnsemble)
133             currentsFile.write(rawChecksum)
134
135         try:
136             rawHeader, header = __nextLittleEndianUnsignedShort(rawFile)
137         except struct.error:
138             break
139
140
141 def test():
142     """Execute test suite"""
143     try:
144         import adcp.tests.runalltests as runalltests
145     except:
146         # possible if executed as script
147         import sys,os
148         sys.path.append(os.path.join(os.path.dirname(__file__),'tests'))
149         import runalltests
150     runalltests.runalltests(subset='pd0')
151
152 class __TestException(Exception):
153     """Flow control for running as script"""
154     pass
155
156 # wrapper function
157 def __test():
158    """Execute test suite from command line"""
159    test()
160    raise __TestException, 'Wrapper function for command line testing only'
161
162 def __main():
163     """Process as script from command line"""
164     import getopt,os,sys
165
166     # get the command line options and arguments
167     path = ''
168     rawName,wavesName,currentsName = 3*[None]
169
170     try:
171         opts, args = getopt.gnu_getopt(sys.argv[1:],
172                                        'htp:r:w:c:',
173                                        ['help',
174                                         'test',
175                                         'path=',
176                                         'raw=',
177                                         'waves=',
178                                         'currents='])
179         for opt,arg in opts:
180             if opt in ['-h','--help']:
181                 raise getopt.GetoptError,''
182             if opt in ['-t','--test']:
183                 __test()
184             elif opt in ['-p','--path']:
185                 path = arg
186             elif opt in ['-r','--raw']:
187                 rawName = arg
188             elif opt in ['-w','--waves']:
189                 wavesName = arg
190             elif opt in ['-c','--currents']:
191                 currentsName = arg
192             else:
193                 raise getopt.GetoptError,''
194         if (rawName is None) or \
195            (wavesName is None) or \
196            (currentsName is None):
197             if len(args) not in [3, 4]:
198                 raise getopt.GetoptError,''
199             else:
200                 if (rawName is not None) or \
201                    (wavesName is not None) or \
202                    (currentsName is not None):
203                     raise getopt.GetoptError,''
204                 else:
205                     if len(args) == 4:
206                         path = args[0]
207                         del args[0]
208                     rawName = args[0]
209                     wavesName = args[1]
210                     currentsName = args[2]
211         elif len(args) != 0:
212             raise getopt.GetoptError,''
213     except getopt.GetoptError:
214         print __doc__
215         return
216     except __TestException:
217         return
218
219     # split a raw PD0 file
220     rawName = os.path.join(path, rawName)
221     print 'Raw file path:', rawName
222     wavesName = os.path.join(path, wavesName)
223     print 'Waves file path:', wavesName
224     currentsName = os.path.join(path, currentsName)
225     print 'Currents file path:', currentsName
226     rawFile = open(rawName, 'rb')
227     try:
228         wavesFile = open(wavesName, 'wb')
229         try:
230             currentsFile = open(currentsName, 'wb')
231             try:
232                 split(rawFile, wavesFile, currentsFile)
233             finally:
234                 currentsFile.close()
235         finally:
236             wavesFile.close()
237     finally:
238         rawFile.close()
239
240 if __name__ == "__main__":
241     __main()
242
Note: See TracBrowser for help on using the browser.