| 7 | |
---|
| 8 | The daily file is split into a list (modeled by the class Data) of samples |
---|
| 9 | (modeled by the class Sample) in chronological order. A Data object is |
---|
| 10 | initialized with a string representing the daily file data: |
---|
| 11 | |
---|
| 12 | dataHandle = open('20070601.dat') |
---|
| 13 | dataString = data.read() |
---|
| 14 | dataObject = Data(dataString) |
---|
| 15 | |
---|
| 16 | Each Sample object has attributes for a Header and Body object. The Samples |
---|
| 17 | within a Data object may also be accessed by time using a string of the format |
---|
| 18 | YYYY-MM-DD-HH-MM as in index on the Data object to return the first matching |
---|
| 19 | Sample in the Data object: |
---|
| 20 | |
---|
| 21 | dataObject[0] # the first Sample object of the day |
---|
| 22 | dataObject['2007-06-01-09-15'] # the Sample object for 9:15am |
---|
| 23 | dataObject[15].header # the Header object of the 16th Sample |
---|
| 24 | dataObject['2007-06-01-09-15'].body # the Body object for 9:15am |
---|
| 25 | |
---|
| 26 | Header objects act as dictionaries. Access each sample-wide parameter of |
---|
| 27 | interest using the header parameter name as a keyword on the Header object: |
---|
| 28 | |
---|
| 29 | dataObject[15].header['VAL2'] # the number of validations for beam 2 |
---|
| 30 | dataObject['2007-06-01-09-15'].header['SPU3'] # normalized false signal |
---|
| 31 | # probability on beam 3 |
---|
| 32 | dataObject[0].header['SNR1'] # signal to noise on beam 1 |
---|
| 33 | |
---|
| 34 | Consult your Sodar documentation for a complete list of header parameters. |
---|
| 35 | |
---|
| 36 | Body objects act as lists of dictionaries. The dictionaries access |
---|
| 37 | altitude-specific parameters by name as keywords. The dictionaries are in |
---|
| 38 | altitude-ascending order. Each dictionary may also by accessed by indexing with |
---|
| 39 | an altitude string: |
---|
| 40 | |
---|
| 41 | dataObject[15].body[0] # the data for the lowest altitude, 16th sample |
---|
| 42 | dataObject['2007-06-01-09-15'].body['70'] # the data for 70 meters |
---|
| 43 | dataObject[15].body[0]['SPEED'] # wind speed at lowest altitude |
---|
| 44 | dataObject['2007-06-01-09-15'].body['70']['DIR'] # wind direction |
---|
| 45 | # at 70 meters |
---|
| 46 | |
---|
| 47 | The body attribute of a Sample object may also be indexed directly on a Sample |
---|
| 48 | object for the most convenient semantics: |
---|
| 49 | |
---|
| 50 | dataObject[15][0]['SPEED'] # wind speed at lowest altitude, 16th sample |
---|
| 51 | dataObject['2007-06-01-09-15']['70']['DIR'] # wind direction, |
---|
| 52 | # 70 meters, 9:15am |
---|
11 | | """Daily sodar file data |
---|
12 | | |
---|
13 | | (a collection of samples) |
---|
14 | | """ |
---|
15 | | def __init__(self,data): |
---|
16 | | super(Data,self).__init__() |
---|
17 | | # samples in file are terminated by $ |
---|
| 64 | |
---|
| 65 | """Daily sodar file data. |
---|
| 66 | |
---|
| 67 | (A chronologically ordered list of samples.) |
---|
| 68 | """ |
---|
| 69 | |
---|
| 70 | def __init__(self, data): |
---|
| 71 | """Divide daily string into list of Samples separated by $.""" |
---|
| 72 | super(Data, self).__init__() |
---|
43 | | if sample.header['YEAR'].rjust(4,'0') != year: continue |
---|
44 | | if sample.header['MONTH'].rjust(2,'0') != month: continue |
---|
45 | | if sample.header['DAY'].rjust(2,'0') != day: continue |
---|
46 | | if sample.header['HOUR'].rjust(2,'0') != hour: continue |
---|
47 | | if sample.header['MIN'].rjust(2,'0') != min: continue |
---|
48 | | return sample |
---|
49 | | raise IndexError,'Data index out of range' |
---|
| 98 | try: |
---|
| 99 | if sample.header['YEAR'].rjust(4,'0') != year: continue |
---|
| 100 | if sample.header['MONTH'].rjust(2,'0') != month: continue |
---|
| 101 | if sample.header['DAY'].rjust(2,'0') != day: continue |
---|
| 102 | if sample.header['HOUR'].rjust(2,'0') != hour: continue |
---|
| 103 | if sample.header['MIN'].rjust(2,'0') != minute: continue |
---|
| 104 | return sample |
---|
| 105 | except TypeError: # sample.header may not exist |
---|
| 106 | continue |
---|
| 107 | raise IndexError('Data index out of range') |
---|
| 108 | |
---|
63 | | # fix for missing keys |
---|
64 | | self.header = Header(self.header) |
---|
65 | | self.body = Body(self.body) |
---|
66 | | |
---|
67 | | def __getitem__(self,index): |
---|
68 | | return self.body[index] |
---|
| 125 | # self.__dict__.get covers parsing invalid Samples |
---|
| 126 | self.header = self.__dict__.get('header', None) |
---|
| 127 | if self.header is not None: |
---|
| 128 | self.header = Header(self.header) |
---|
| 129 | self.body = self.__dict__.get('body', None) |
---|
| 130 | if self.body is not None: |
---|
| 131 | self.body = Body(self.body) |
---|
| 132 | |
---|
| 133 | def __getitem__(self, index): |
---|
| 134 | """Index Sample by body attribute.""" |
---|
| 135 | try: |
---|
| 136 | return self.body[index] |
---|
| 137 | except TypeError: # sample.body may not exist |
---|
| 138 | raise IndexError('Sample index out of range') |
---|
| 139 | |
---|
71 | | """A sodar data sample header |
---|
72 | | |
---|
73 | | (a collection of sample-wide parameters) |
---|
74 | | """ |
---|
75 | | def __init__(self,header): |
---|
76 | | super(Header,self).__init__() |
---|
77 | | headerLines = header.split('\n') |
---|
78 | | # every other line contains parameter keys; |
---|
79 | | # every other line contains parameter values |
---|
| 142 | |
---|
| 143 | """A sodar data sample header. |
---|
| 144 | |
---|
| 145 | (A dictionary of sample-wide parameters.) |
---|
| 146 | """ |
---|
| 147 | |
---|
| 148 | def __init__(self, header): |
---|
| 149 | |
---|
| 150 | """Identify discreet header parameter names and values. |
---|
| 151 | |
---|
| 152 | Every other line contains parameter keys; |
---|
| 153 | every other line contains parameter values. |
---|
| 154 | """ |
---|
| 155 | |
---|
| 156 | super(Header, self).__init__() |
---|
| 157 | headerLines = [headerLine.strip() |
---|
| 158 | for headerLine in header.split('\n') |
---|
| 159 | if headerLine.strip()] |
---|
| 160 | #fix for bad match between names and values |
---|
84 | | """A sodar data sample body |
---|
85 | | |
---|
86 | | (a collection of collections at each altitude) |
---|
87 | | """ |
---|
88 | | def __init__(self,body): |
---|
89 | | super(Body,self).__init__() |
---|
90 | | bodyLines = body.split('\n') |
---|
| 166 | |
---|
| 167 | """A sodar data sample body. |
---|
| 168 | |
---|
| 169 | (A list of dictionariess at each altitude.) |
---|
| 170 | """ |
---|
| 171 | |
---|
| 172 | def __init__(self, body): |
---|
| 173 | |
---|
| 174 | """Identify discreet body parameter names and values. |
---|
| 175 | |
---|
| 176 | The first line contains parameter keys; |
---|
| 177 | the remaining lines contains parameter values, |
---|
| 178 | one set of parameters for a single altitude per line. |
---|
| 179 | """ |
---|
| 180 | |
---|
| 181 | super(Body, self).__init__() |
---|
| 182 | bodyLines = [bodyLine.strip() |
---|
| 183 | for bodyLine in body.split('\n') |
---|
| 184 | if bodyLine.strip()] |
---|