1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37 """
38 Various functions related to static source error correction.
39 """
40
41 import airspeed as A
42 import math as M
43 import std_atm as SA
44 import unit_conversion as U
45
46
47
48
49
50
52 """
53 Returns true airspeed, given GPS groundspeed and track on at least
54 three legs (four legs preferred). Uses the method developed by Doug
55 Gray - http://www.kilohotel.com/rv8/rvlinks/doug_gray/TAS_FNL4.pdf
56
57 GS and TK are lists of ground speed and track data.
58
59 Three legs:
60 If verbose = 0, then only TAS is returned.
61 If verbose = 1, then TAS, wind speed and direction are returned.
62 If verbose = 2, then TAS, wind speed and direction and the heading
63 for each leg are returned. The wind speed and direction is
64 returned as a tuple, and the headings are returned as a tuple
65
66
67 Four legs:
68 Data from only three legs is sufficient to calculate TAS. If data
69 four legs is entered, four different calculations are conducted,
70 using a different mix of three data points for each calculation.
71 If the data quality is high, the TAS and wind for all four
72 calculations will be similar. The standard deviation on the TAS is
73 calculated - good quality data will have a standard deviation of
74 less than 1 kt.
75
76 If verbose = 0, then only TAS is returned.
77 If verbose = 1, then TAS and its standard deviation are returned.
78 If verbose = 2, then TAS, its standard deviation and the four wind
79 speeds and directions are returned (the winds are returned as a list
80 of four tuples)
81
82 Validated against sample data in four leg tab of NTPS GPS PEC spreadsheet:
83 http://www.ntps.edu/Files/GPS%20PEC.XLS
84
85 Examples:
86
87 Data for all examples:
88 >>> gs = [178, 185, 188, 184]
89 >>> tk = [178, 82, 355, 265]
90
91 Determine the TAS, given the above data from four runs:
92 >>> gps2tas(gs, tk)
93 183.72669557114619
94
95 Determine the TAS and standard deviation from the four calculations:
96 >>> gps2tas(gs, tk, verbose = 1)
97 (183.72669557114619, 0.82709634705928436)
98
99 Determine the TAS, standard deviation, and wind speed and direction
100 for each calculation:
101 >>> gps2tas(gs, tk, verbose = 2)
102 (183.72669557114619, 0.82709634705928436, ((5.2608369270843056, 194.51673740323213), (3.5823966532035927, 181.52174627838372), (5.1495218164839995, 162.69803415599802), (6.4436728241320145, 177.94783081049718)))
103
104 """
105
106 if 2 < len(GS) < 5:
107 pass
108 else:
109 raise ValueError, 'GS must be a list of three or four items'
110
111 if 2 < len(TK) < 5:
112 pass
113 else:
114 raise ValueError, 'TK must be a list of three or four items'
115
116 if len(GS) != len(TK):
117 raise ValueError, 'The ground speed and track arrays must have the same number of elements.'
118
119 if len(GS) == 3:
120 result = gps2tas3(GS, TK, verbose)
121 return result
122 else:
123 gs_data_sets, tk_data_sets, results = [], [], []
124
125 gs_data_sets.append([GS[0], GS[1], GS[2]])
126 gs_data_sets.append([GS[1], GS[2], GS[3]])
127 gs_data_sets.append([GS[2], GS[3], GS[0]])
128 gs_data_sets.append([GS[3], GS[0], GS[1]])
129
130 tk_data_sets.append([TK[0], TK[1], TK[2]])
131 tk_data_sets.append([TK[1], TK[2], TK[3]])
132 tk_data_sets.append([TK[2], TK[3], TK[0]])
133 tk_data_sets.append([TK[3], TK[0], TK[1]])
134
135 for (gs, tk) in zip (gs_data_sets, tk_data_sets):
136 results.append(gps2tas3(gs, tk, 2))
137
138 ave_TAS = 0
139 ave_wind_x = 0
140 ave_wind_y = 0
141 sum2_TAS = 0
142
143 for item in results:
144 ave_TAS +=item[0]
145 sum2_TAS += item[0] ** 2
146 ave_wind_x += item[1][0] * M.sin(M.pi * item[1][1] / 180.)
147 ave_wind_y += item[1][0] * M.cos(M.pi * item[1][1] / 180.)
148
149 ave_TAS /= 4.
150 std_dev_TAS = M.sqrt((sum2_TAS - 4 * ave_TAS ** 2) / 3)
151 ave_wind_x /= 4
152 ave_wind_y /= 4.
153 ave_wind_speed = M.sqrt(ave_wind_x ** 2 + ave_wind_y ** 2)
154 ave_wind_dir = (720. - (180. / M.pi * M.atan2(ave_wind_x, ave_wind_y))) % 360
155
156
157 if verbose == 0:
158 return ave_TAS
159 elif verbose == 1:
160 return ave_TAS, std_dev_TAS
161 elif verbose == 2:
162 return ave_TAS, std_dev_TAS, ((results[0][1][0], results[0][1][1]),(results[1][1][0], results[1][1][1]),(results[2][1][0], results[2][1][1]),(results[3][1][0], results[3][1][1]))
163 else:
164 raise ValueError, 'The value of verbose must be equal to 0, 1 or 2'
165
166
168 """
169 Returns true airspeed, given GPS groundspeed and track on three legs.
170 Uses the method developed by Doug Gray:
171 http://www.kilohotel.com/rv8/rvlinks/doug_gray/TAS_FNL4.pdf
172
173 GS and TK are arrays of ground speed and track data.
174
175 If verbose = 0, then only TAS is returned.
176 If verbose = 1, then TAS, wind speed and wind direction are returned.
177 If verbose = 2, then TAS, wind speed and direction and the heading for
178 each leg are returned. The wind speed and direction is returned as a
179 tuple, and the headings are returned as a tuple
180
181 Validated against sample in Doug Gray's paper.
182
183 Examples:
184
185 """
186 x, y, b, m, hdg = [], [], [], [], []
187
188 for (gs, tk) in zip(GS, TK):
189 x.append(gs * M.sin(M.pi * (360. - tk) / 180.))
190 y.append(gs * M.cos(M.pi * (360. - tk) / 180.))
191
192 m.append(-1 * (x[1] - x[0]) / (y[1] - y[0]))
193 m.append(-1 * (x[2] - x[0]) / (y[2] - y[0]))
194
195 b.append((y[0] + y[1]) / 2 - m[0] * (x[0] + x[1]) / 2)
196 b.append((y[0] + y[2]) / 2 - m[1] * (x[0] + x[2]) / 2)
197
198 wind_x = (b[0] - b[1]) / (m[1] - m[0])
199 wind_y = m[0] * wind_x + b[0]
200
201 wind_speed = M.sqrt(wind_x ** 2 + wind_y ** 2)
202 wind_dir = (540. - (180. / M.pi * M.atan2(wind_x, wind_y))) % 360.
203
204 TAS = M.sqrt((x[0] - wind_x) ** 2 + (y[0] - wind_y) ** 2)
205
206 if verbose >= 2:
207 hdg.append((540. - (180. / M.pi * M.atan2(wind_x - x[0], wind_y - y[0]))) % 360.)
208 hdg.append((540. - (180. / M.pi * M.atan2(wind_x - x[1], wind_y - y[1]))) % 360.)
209 hdg.append((540. - (180. / M.pi * M.atan2(wind_x - x[2], wind_y - y[2]))) % 360.)
210
211 return TAS, (wind_speed, wind_dir), (hdg[0], hdg[1], hdg[2])
212
213 elif verbose == 1:
214 return TAS, (wind_speed, wind_dir)
215 elif verbose == 0:
216 return TAS
217 else:
218 raise ValueError, 'The value of verbose must be equal to 0, 1 or 2'
219
220
223
224
225 if __name__ == '__main__':
226 main()
227