Guest User

Untitled

a guest
Nov 24th, 2017
78
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 4.41 KB | None | 0 0
  1. # WORK IN PROGRESS (c) A.R.R. JSTN 2017
  2.  
  3. class RadarImageParser
  4. attr_reader :data
  5.  
  6. CODES_WITH_ELEVATION_ANGLES = [ 19, 20, 32, 37, 38, 65, 66, 67, 90, 94, 159 ]
  7. CODES_WITH_NONSTANDARD_THRESHOLDS = [ 32, 81, 93, 94, 99, 134, 135, 138, 153, 154, 155, 159, 161, 163, 177, 195 ]
  8.  
  9. def initialize(io_or_string)
  10. @io = io_or_string.is_a?(String) ? StringIO.new(io_or_string, "rb") : io
  11. parse
  12. end
  13.  
  14. private
  15.  
  16. def parse
  17. @data = Hash.new
  18.  
  19. # BLOCK: Message Header (ICD 3-7)
  20. @data[:message_header] = {
  21. wmo_header: string, # like "SDUS55 KABQ 031229"
  22. awips_header: string, # like "N0RABX"
  23. message_code: halfword, # -131 to -16, 0 to 211 (ICD Table II)
  24. message_time: unixtime,
  25. length_of_message: fullword, # number of bytes including header
  26. source_id: halfword, # 0-999
  27. destination_id: halfword, # 0-999
  28. number_of_blocks: halfword, #1-51, including this header
  29. }
  30.  
  31. # BLOCK: Product Description (ICD 3-27, 3-32)
  32. raise "no product description block" unless halfword == -1
  33. @data[:product_description] = {
  34. radar_latitude: fullword.to_f / 1000,
  35. radar_longitude: fullword.to_f / 1000,
  36. radar_height: halfword, # -100 to 11000, feet above mean sea level
  37. product_code: halfword, # 16 to 299, -16 to -299 (ICD Table III)
  38. operational_mode: halfword, # 0-2
  39. volume_coverage_pattern: halfword, # 1-767
  40. sequence_number: halfword, # -13, 0 to 32767
  41. volume_scan_number: halfword, # 1 to 80
  42. volume_scan_time: unixtime,
  43. generation_time: unixtime,
  44. product_dependent_param_1: halfword, # ICD Table V
  45. product_dependent_param_2: halfword, # ICD Table V
  46. elevation_number: halfword, # 0-20 (for elevation based products)
  47. product_dependent_param_3: halfword # ICD Table V, NOTE 3
  48. }
  49.  
  50. if CODES_WITH_ELEVATION_ANGLES.include? @data[:product_description][:product_code]
  51. param = @data[:product_description].delete(:product_dependent_param_3)
  52. @data[:product_description][:elevation_angle] = param.to_f / 10
  53. end
  54.  
  55. if CODES_WITH_NONSTANDARD_THRESHOLDS.include? @data[:product_description][:product_code]
  56. raise "nonstandard thresholds TKTKTK"
  57. else
  58. 16.times do |i|
  59. msb = @io.getbyte
  60. lsb = @io.getbyte
  61. threshold = ""
  62.  
  63. if msb & 0b10000000 > 0
  64. threshold += case lsb
  65. when 1 then "TH"
  66. when 2 then "ND"
  67. when 3 then "RF"
  68. when 4 then "BI" # Biological
  69. when 5 then "GC" # AP/Ground Clutter
  70. when 6 then "IC" # Ice Crystals
  71. when 7 then "GR" # Graupel
  72. when 8 then "WS" # Wet Snow
  73. when 9 then "DS" # Dry Snow
  74. when 10 then "RA" # Light and Moderate Rain
  75. when 11 then "HR" # Heavy Rain
  76. when 12 then "BD" # Big Drops
  77. when 13 then "HA" # Hail and Rain Mixed
  78. when 14 then "UK" # Unknown
  79. when 15 then "LH" # Large Hail
  80. when 16 then "GH" # Giant Hail
  81. else ""
  82. end
  83. else
  84. if msb & 0b00001000 > 0 # bit 4
  85. threshold += ">"
  86. elsif msb & 0b00000100 > 0 # bit 5
  87. threshold += "<"
  88. elsif msb & 0b00000010 > 0 # bit 6
  89. threshold += "+"
  90. elsif msb & 0b00000001 > 0 # bit 7
  91. threshold += "-"
  92. end
  93.  
  94. if msb & 0b01000000 > 0 # bit 1
  95. threshold += lsb.to_f / 100
  96. elsif msb & 0b00100000 > 0 # bit 2
  97. threshold += lsb.to_f / 20
  98. elsif msb & 0b00010000 > 0 # bit 3
  99. threshold += lsb.to_f / 10
  100. else
  101. threshold += "#{lsb}"
  102. end
  103. end
  104.  
  105. @data[:product_description]["threshold_#{i + 1}".to_sym] = threshold
  106. end
  107. end
  108.  
  109. @data
  110. end
  111.  
  112. def string
  113. @io.gets.strip
  114. end
  115.  
  116. def halfword
  117. # 16 bit signed integer, big-endian (INT*2)
  118. @io.read(2).unpack("s>").first
  119. end
  120.  
  121. def fullword
  122. # 32 bit signed integer, big-endian (INT*4)
  123. @io.read(4).unpack("l>").first
  124. end
  125.  
  126. def unixtime
  127. # modified julian, offset by 1 day
  128. days = halfword.days - 1.day
  129. seconds = fullword.seconds
  130. Time.zone.at(days + seconds).iso8601
  131. end
  132. end
  133.  
  134. # HC SVNT DRACONES
Add Comment
Please, Sign In to add comment