UserContributedScripts: dumpDRX.py

File dumpDRX.py, 5.8 KB (added by jayce, 3 years ago)

Script to dump the raw voltage data from a DRX file

Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4import os
5import sys
6import struct
7import getopt
8from datetime import datetime
9
10from lsl.reader import drx, errors
11from lsl.common.progress import ProgressBar
12
13
14def usage(exitCode=None):
15        print """dumpDRX.py - dump the raw voltage data from a DRX file containing
16multiple seconds into several files
17
18Usage: dumpDRX.py [OPTIONS] file
19
20Options:
21-h, --help             Display this help information
22-c, --count            Number of seconds to keep
23-o, --offset           Number of seconds to skip before splitting
24-d, --date             Label the split files with a date rather than a
25                       sequence number
26"""
27       
28        if exitCode is not None:
29                sys.exit(exitCode)
30        else:
31                return True
32
33
34def parseConfig(args):
35        config = {}
36        # Command line flags - default values
37        config['offset'] = 0
38        config['count'] = 0
39        config['date'] = False
40       
41        # Read in and process the command line flags
42        try:
43                opts, arg = getopt.getopt(args, "hc:o:d", ["help", "count=", "offset=", "date"])
44        except getopt.GetoptError, err:
45                # Print help information and exit:
46                print str(err) # will print something like "option -a not recognized"
47                usage(exitCode=2)
48               
49        # Work through opts
50        for opt, value in opts:
51                if opt in ('-h', '--help'):
52                        usage(exitCode=0)
53                elif opt in ('-c', '--count'):
54                        config['count'] = float(value)
55                elif opt in ('-o', '--offset'):
56                        config['offset'] = float(value)
57                elif opt in ('-d', '--date'):
58                        config['date'] = True
59                else:
60                        assert False
61                       
62        # Add in arguments
63        config['args'] = arg
64       
65        # Return configuration
66        return config
67
68
69def main(args):
70        config = parseConfig(args)
71        filename = config['args'][0]
72       
73        sizeB = os.path.getsize(filename)
74       
75        # Open the file and get some basic info about the data contained
76        fh = open(filename, 'rb')
77        nFramesFile = sizeB / drx.FrameSize
78       
79        while True:
80                try:
81                        junkFrame = drx.readFrame(fh)
82                        try:
83                                srate = junkFrame.getSampleRate()
84                                t0 = junkFrame.getTime()
85                                break
86                        except ZeroDivisionError:
87                                pass
88                except errors.syncError:
89                        fh.seek(-drx.FrameSize+1, 1)
90                       
91        fh.seek(-drx.FrameSize, 1)
92       
93        beams = drx.getBeamCount(fh)
94        tunepols = drx.getFramesPerObs(fh)
95        tunepol = tunepols[0] + tunepols[1] + tunepols[2] + tunepols[3]
96        beampols = tunepol
97       
98        # Offset in frames for beampols beam/tuning/pol. sets
99        offset = int(round(config['offset'] * srate / 4096 * beampols))
100        offset = int(1.0 * offset / beampols) * beampols
101        fh.seek(offset*drx.FrameSize, 1)
102       
103        # Iterate on the offsets until we reach the right point in the file.  This
104        # is needed to deal with files that start with only one tuning and/or a
105        # different sample rate. 
106        while True:
107                ## Figure out where in the file we are and what the current tuning/sample
108                ## rate is
109                junkFrame = drx.readFrame(fh)
110                srate = junkFrame.getSampleRate()
111                t1 = junkFrame.getTime()
112                tunepols = drx.getFramesPerObs(fh)
113                tunepol = tunepols[0] + tunepols[1] + tunepols[2] + tunepols[3]
114                beampols = tunepol
115                fh.seek(-drx.FrameSize, 1)
116               
117                ## See how far off the current frame is from the target
118                tDiff = t1 - (t0 + config['offset'])
119               
120                ## Half that to come up with a new seek parameter
121                tCorr = -tDiff / 2.0
122                cOffset = int(tCorr * srate / 4096 * beampols)
123                cOffset = int(1.0 * cOffset / beampols) * beampols
124                offset += cOffset
125               
126                ## If the offset is zero, we are done.  Otherwise, apply the offset
127                ## and check the location in the file again/
128                if cOffset is 0:
129                        break
130                fh.seek(cOffset*drx.FrameSize, 1)
131               
132        # Update the offset actually used
133        config['offset'] = t1 - t0
134       
135        print "Filename:     %s" % filename
136        print "Size:         %.1f MB" % (float(sizeB)/1024/1024)
137        print "Captures:     %i (%.2f seconds)" % (nFramesFile/beampols, nFramesFile/beampols*4096/srate)
138        print "Tuning/Pols.: %i " % tunepol
139        print "Sample Rate:  %.2f MHz" % (srate/1e6)
140        print "==="
141       
142        if config['count'] > 0:
143                nCaptures = config['count'] * srate / 4096
144        else:
145                config['count'] = nCaptures * 4096 / srate
146        nSkip = int(config['offset'] * srate / 4096 )
147       
148        print "Seconds to Skip:  %.2f (%i captures)" % (config['offset'], nSkip)
149        print "Seconds to Split: %.2f (%i captures)" % (config['count'], nCaptures)
150       
151        # Make sure that the first frame in the file is the first frame of a capture
152        # (tuning 1, pol 0).  If not, read in as many frames as necessary to get to
153        # the beginning of a complete capture.
154        beam, tune, pol = junkFrame.parseID()
155       
156        skip = 0
157        while (2*(tune-1)+pol) != 0:
158                frame = drx.readFrame(fh)
159                beam, tune, pol = frame.parseID()
160                skip += 1
161               
162        # Offset
163        fh.seek(fh.tell() + nSkip*drx.FrameSize*tunepol)
164       
165        if config['date']:
166                filePos = fh.tell()
167                junkFrame = drx.readFrame(fh)
168                fh.seek(filePos)
169               
170                dt = datetime.utcfromtimestamp(junkFrame.getTime())
171                captFilename = "%s_%s" % (os.path.splitext(os.path.basename(filename))[0], dt.isoformat())
172        else:
173                captFilename = "%s_s%04i" % (os.path.splitext(os.path.basename(filename))[0], config['count'])
174               
175        print "Writing %.2f s to file '%s_[12][XY].dat'" % (nCaptures*4096/srate, captFilename)
176       
177        # Ready the output files - one for each tune/pol
178        fhOut = []
179        fhOut.append( open("%s_1X.dat" % captFilename, 'wb') )
180        fhOut.append( open("%s_1Y.dat" % captFilename, 'wb') )
181        fhOut.append( open("%s_2X.dat" % captFilename, 'wb') )
182        fhOut.append( open("%s_2Y.dat" % captFilename, 'wb') )
183       
184        pb = ProgressBar(max=nCaptures)
185        for c in xrange(int(nCaptures)):
186                for i in xrange(tunepol):
187                        cFrame = fh.read(drx.FrameSize)
188                        id = struct.unpack('>B', cFrame[4])[0]
189                        tuning = (id>>3)&7
190                        pol    = (id>>7)&1
191                       
192                        aStand = 2*(tuning-1) + pol
193                       
194                        # Save only the I/Q data (the last 4096 bytes)
195                        fhOut[aStand].write(cFrame[32:])
196                       
197                pb.inc(amount=1)
198                if c != 0 and c % 100 == 0:
199                        sys.stdout.write(pb.show()+'\r')
200                        sys.stdout.flush()
201                       
202        sys.stdout.write(pb.show()+'\r')
203        sys.stdout.write('\n')
204        sys.stdout.flush()
205        for f in fhOut:
206                f.close()
207               
208        fh.close()
209
210
211if __name__ == "__main__":
212        main(sys.argv[1:])