WeatherModel.py 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. #!/usr/bin/env python
  2. from aman.com.Weather import Weather
  3. import math
  4. import scipy.interpolate
  5. class WeatherModel:
  6. def __init__(self, gaforId, weather : Weather):
  7. self.Gafor = gaforId
  8. self.Weather = weather
  9. self.Altitudes = None
  10. self.Directions = None
  11. self.Windspeeds = None
  12. self.WindDirectionModel = None
  13. self.WindSpeedModel = None
  14. self.LastWeatherUpdate = None
  15. self.MinimumAltitude = 1000000
  16. self.MaximumAltitude = -1
  17. # create the density interpolation model
  18. # the density model is based on https://aerotoolbox.com/atmcalc/
  19. altitudes = [
  20. 50000,
  21. 45000,
  22. 40000,
  23. 38000,
  24. 36000,
  25. 34000,
  26. 32000,
  27. 30000,
  28. 28000,
  29. 26000,
  30. 24000,
  31. 22000,
  32. 20000,
  33. 18000,
  34. 16000,
  35. 15000,
  36. 14000,
  37. 13000,
  38. 12000,
  39. 11000,
  40. 10000,
  41. 9000,
  42. 8000,
  43. 7000,
  44. 6000,
  45. 5000,
  46. 4000,
  47. 3000,
  48. 2000,
  49. 1000,
  50. 0
  51. ]
  52. densities = [
  53. 0.18648,
  54. 0.23714,
  55. 0.24617,
  56. 0.33199,
  57. 0.36518,
  58. 0.39444,
  59. 0.42546,
  60. 0.45831,
  61. 0.402506,
  62. 0.432497,
  63. 0.464169,
  64. 0.60954,
  65. 0.65269,
  66. 0.69815,
  67. 0.74598,
  68. 0.77082,
  69. 0.79628,
  70. 0.82238,
  71. 0.84914,
  72. 0.87655,
  73. 0.90464,
  74. 0.93341,
  75. 0.96287,
  76. 0.99304,
  77. 1.02393,
  78. 1.05555,
  79. 1.08791,
  80. 1.12102,
  81. 1.1549,
  82. 1.18955,
  83. 1.225
  84. ]
  85. self.densityModel = scipy.interpolate.interp1d(altitudes, densities)
  86. def calculateTAS(self, altitude : int, ias : int):
  87. if altitude >= 50000:
  88. altitude = 49999
  89. if altitude <= 0:
  90. altitude = 1
  91. # calculation based on https://aerotoolbox.com/airspeed-conversions/
  92. return ias * math.sqrt(1.225 / self.densityModel(altitude).item())
  93. def updateWindModel(self):
  94. if None == self.Weather or None == self.Weather.Provider:
  95. return
  96. if None == self.LastWeatherUpdate or self.LastWeatherUpdate != self.Weather.Provider.UpdateTime:
  97. self.MinimumAltitude = 1000000
  98. self.MaximumAltitude = -1
  99. self.WindDirectionModel = None
  100. self.WindSpeedModel = None
  101. self.Altitudes = None
  102. self.Directions = None
  103. self.Windspeeds = None
  104. if None != self.Weather.Provider.WindData and self.Gafor in self.Weather.Provider.WindData:
  105. self.Altitudes = []
  106. self.Directions = []
  107. self.Windspeeds = []
  108. # collect the data for the wind model
  109. for level in self.Weather.Provider.WindData[self.Gafor]:
  110. self.Altitudes.append(level[0])
  111. self.Directions.append(level[1])
  112. self.Windspeeds.append(level[2])
  113. # define the thresholds for later boundary checks
  114. if self.MinimumAltitude > level[0]:
  115. self.MinimumAltitude = level[0]
  116. if self.MaximumAltitude < level[0]:
  117. self.MaximumAltitude = level[0]
  118. # calculate the models
  119. if 1 < len(self.Altitudes):
  120. self.WindDirectionModel = scipy.interpolate.interp1d(self.Altitudes, self.Directions)
  121. self.WindSpeedModel = scipy.interpolate.interp1d(self.Altitudes, self.Windspeeds)
  122. self.LastWeatherUpdate = self.Weather.Provider.UpdateTime
  123. else:
  124. self.LastWeatherUpdate = None
  125. def interpolateWindData(self, altitude : int):
  126. self.updateWindModel()
  127. # initialized the wind data
  128. if None != self.WindDirectionModel and None != self.WindSpeedModel:
  129. direction = 0.0
  130. speed = 0.0
  131. if None != self.WindSpeedModel and None != self.WindDirectionModel:
  132. if self.MaximumAltitude <= altitude:
  133. altitude = self.MaximumAltitude - 1
  134. if self.MinimumAltitude >= altitude:
  135. altitude = self.MinimumAltitude + 1
  136. direction = self.WindDirectionModel(altitude).item()
  137. speed = self.WindSpeedModel(altitude).item()
  138. else:
  139. speed = 0
  140. direction = 0
  141. return speed, direction
  142. def calculateGS(self, altitude : int, ias : int, heading : int):
  143. speed, direction = self.interpolateWindData(altitude)
  144. tas = self.calculateTAS(altitude, ias)
  145. return tas + speed * math.cos(math.radians(direction) - math.radians(heading))
  146. def convertGSToTAS(self, altitude : int, gs : int, heading : int):
  147. speed, direction = self.interpolateWindData(altitude)
  148. return gs - speed * math.cos(math.radians(direction) - math.radians(heading))
  149. def estimateCourse(self, altitude : int, gs : int, heading : int):
  150. tas = self.convertGSToTAS(altitude, gs, heading)
  151. speed, direction = self.interpolateWindData(altitude)
  152. aca = heading - direction
  153. wca = speed * aca / tas
  154. if 0 <= aca:
  155. course = heading + wca
  156. else:
  157. course = heading - wca
  158. while 0 > course:
  159. course += 360
  160. while 360 < course:
  161. course -= 360
  162. return course