atmospheris 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +25 -0
  3. data/README.adoc +1254 -0
  4. data/lib/atmospheris/export/altitude_attrs.rb +296 -0
  5. data/lib/atmospheris/export/altitude_convertable_model.rb +80 -0
  6. data/lib/atmospheris/export/altitude_table.rb +49 -0
  7. data/lib/atmospheris/export/hypsometrical_table.rb +36 -0
  8. data/lib/atmospheris/export/iso_25331975/group_one.rb +21 -0
  9. data/lib/atmospheris/export/iso_25331975/group_one_attrs.rb +101 -0
  10. data/lib/atmospheris/export/iso_25331975/group_three.rb +21 -0
  11. data/lib/atmospheris/export/iso_25331975/group_three_attrs.rb +101 -0
  12. data/lib/atmospheris/export/iso_25331975/group_two.rb +21 -0
  13. data/lib/atmospheris/export/iso_25331975/group_two_attrs.rb +113 -0
  14. data/lib/atmospheris/export/iso_25331975.rb +28 -0
  15. data/lib/atmospheris/export/iso_25331985/pressure_attrs.rb +16 -0
  16. data/lib/atmospheris/export/iso_25331985/table_five_six_attrs.rb +16 -0
  17. data/lib/atmospheris/export/iso_25331985.rb +113 -0
  18. data/lib/atmospheris/export/iso_25331997.rb +86 -0
  19. data/lib/atmospheris/export/iso_25332025/altitude_attrs_group.rb +25 -0
  20. data/lib/atmospheris/export/iso_25332025/combined_altitude_attrs_group.rb +41 -0
  21. data/lib/atmospheris/export/iso_25332025.rb +81 -0
  22. data/lib/atmospheris/export/precision_value.rb +9 -0
  23. data/lib/atmospheris/export/pressure_attrs.rb +87 -0
  24. data/lib/atmospheris/export/utils.rb +30 -0
  25. data/lib/atmospheris/export.rb +17 -0
  26. data/lib/atmospheris/isa.rb +493 -0
  27. data/lib/atmospheris/namespace.rb +8 -0
  28. data/lib/atmospheris/unit_value_float.rb +23 -0
  29. data/lib/atmospheris/unit_value_integer.rb +23 -0
  30. data/lib/atmospheris/version.rb +5 -0
  31. data/lib/atmospheris.rb +14 -0
  32. metadata +117 -0
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Atmospheris
4
+ module Export
5
+ class PressureAttrs < Lutaml::Model::Serializable
6
+ include Utils
7
+ attribute :pressures, UnitValueFloat, collection: true
8
+ attribute :geometric_altitudes, UnitValueFloat, collection: true
9
+ attribute :geopotential_altitudes, UnitValueFloat, collection: true
10
+
11
+ key_value do
12
+ map "pressure", to: :pressures
13
+ map "geopotential-altitude", to: :geopotential_altitudes
14
+ map "geometric-altitude", to: :geometric_altitudes
15
+ end
16
+
17
+ xml do
18
+ element "hypsometrical-attributes"
19
+ namespace Atmospheris::Iso2533Namespace
20
+ map_element "pressure", to: :pressures
21
+ map_element "geometric-altitude", to: :geometric_altitudes
22
+ map_element "geopotential-altitude", to: :geopotential_altitudes
23
+ end
24
+
25
+ def set_pressure(value:, unit: :mbar, precision: :reduced)
26
+ method_name = "geopotential_altitude_from_pressure_#{unit}"
27
+ gp_h_m = Isa::NormalPrecision.instance.send(method_name, value)
28
+ gp_h_ft = m_to_ft(gp_h_m)
29
+ gm_h_m = Isa::NormalPrecision.instance.geometric_altitude_from_geopotential(gp_h_m)
30
+ gm_h_ft = m_to_ft(gm_h_m)
31
+
32
+ realize_altitudes(gm_h_m, gm_h_ft, gp_h_m, gp_h_ft, precision: precision)
33
+ realize_pressures(value, unit: unit)
34
+
35
+ self
36
+ end
37
+
38
+ def realize_altitudes(hgmm, hgmf, hgpm, hgpf, precision: :reduced)
39
+ self.geometric_altitudes = [
40
+ UnitValueFloat.new(
41
+ value: precision == :reduced ? hgmm.round(1) : hgmm,
42
+ unitsml: "m"
43
+ ),
44
+ UnitValueFloat.new(
45
+ value: precision == :reduced ? hgmf.round : hgmf,
46
+ unitsml: "ft"
47
+ )
48
+ ]
49
+
50
+ self.geopotential_altitudes = [
51
+ UnitValueFloat.new(
52
+ value: precision == :reduced ? hgpm.round(1) : hgpm,
53
+ unitsml: "m"
54
+ ),
55
+ UnitValueFloat.new(
56
+ value: precision == :reduced ? hgpf.round : hgpf,
57
+ unitsml: "ft"
58
+ )
59
+ ]
60
+ end
61
+
62
+ def realize_pressures(input, unit:)
63
+ self.pressures = case unit
64
+ when :mbar
65
+ [
66
+ UnitValueFloat.new(value: input, unitsml: "mbar"),
67
+ UnitValueFloat.new(
68
+ value: Isa::NormalPrecision.instance.mbar_to_mmhg(input),
69
+ unitsml: "mm_Hg"
70
+ )
71
+ ]
72
+ when :mmhg
73
+ [
74
+ UnitValueFloat.new(
75
+ value: Isa::NormalPrecision.instance.mmhg_to_mbar(input),
76
+ unitsml: "mbar"
77
+ ),
78
+ UnitValueFloat.new(value: input, unitsml: "mm_Hg")
79
+ ]
80
+ else
81
+ raise ArgumentError,
82
+ "Invalid unit: #{unit}. Use :mbar or :mmhg."
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Atmospheris
4
+ module Export
5
+ module Utils
6
+ def values_in_m_ft(value, unit: :meters)
7
+ case unit
8
+ when :meters
9
+ [value.to_f, m_to_ft(value)]
10
+ when :feet
11
+ [ft_to_m(value), value.to_f]
12
+ end
13
+ end
14
+
15
+ def round_to_sig_figs(num, num_sig_figs)
16
+ return 0.0 if num.nil? || num.zero?
17
+
18
+ num.round(num_sig_figs - Math.log10(num.abs).ceil).to_f
19
+ end
20
+
21
+ def m_to_ft(meters)
22
+ meters / 0.3048
23
+ end
24
+
25
+ def ft_to_m(feet)
26
+ feet * 0.3048
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Atmospheris
4
+ module Export
5
+ autoload :Utils, "atmospheris/export/utils"
6
+ autoload :AltitudeTable, "atmospheris/export/altitude_table"
7
+ autoload :AltitudeConvertableModel, "atmospheris/export/altitude_convertable_model"
8
+ autoload :AltitudeAttrs, "atmospheris/export/altitude_attrs"
9
+ autoload :PressureAttrs, "atmospheris/export/pressure_attrs"
10
+ autoload :HypsometricalTable, "atmospheris/export/hypsometrical_table"
11
+ autoload :PrecisionValue, "atmospheris/export/precision_value"
12
+ autoload :Iso25331975, "atmospheris/export/iso_25331975"
13
+ autoload :Iso25331985, "atmospheris/export/iso_25331985"
14
+ autoload :Iso25331997, "atmospheris/export/iso_25331997"
15
+ autoload :Iso25332025, "atmospheris/export/iso_25332025"
16
+ end
17
+ end
@@ -0,0 +1,493 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bigdecimal"
4
+ require "bigdecimal/math"
5
+ require "singleton"
6
+
7
+ module Atmospheris
8
+ module Isa
9
+ # International Standard Atmosphere (ISA) (ISO 2533:1975)
10
+ # ICAO Standard Atmosphere (ICAO Doc 7488/3, 1994)
11
+
12
+ class Algorithms
13
+ attr_accessor :precision
14
+ attr_reader :pressure_layers, :constants, :sqrt2, :pi
15
+
16
+ def set_precision(precision)
17
+ @precision = precision == :high ? :high : :normal
18
+ remove_instance_variable(:@pressure_layers) \
19
+ if defined?(@pressure_layers)
20
+ make_constants
21
+ end
22
+
23
+ # 2.3 Geopotential and geometric altitides; acceleration of free fall
24
+
25
+ # 2.3 Formula (8)
26
+ # H to h
27
+ # h(m)
28
+ def geometric_altitude_from_geopotential(geopotential_alt)
29
+ @constants[:radius] * geopotential_alt \
30
+ / (@constants[:radius] - geopotential_alt)
31
+ end
32
+
33
+ # 2.3 Formula (9)
34
+ # h to H
35
+ # H(m)
36
+ def geopotential_altitude_from_geometric(geometric_alt)
37
+ @constants[:radius] * geometric_alt \
38
+ / (@constants[:radius] + geometric_alt)
39
+ end
40
+
41
+ # 2.3 Formula (7)
42
+ # g(h)
43
+ def gravity_at_geometric(geometric_alt)
44
+ temp = @constants[:radius] / (@constants[:radius] + geometric_alt)
45
+ @constants[:g_n] * temp * temp
46
+ end
47
+
48
+ def gravity_at_geopotential(geopotential_alt)
49
+ geometric_h = geometric_altitude_from_geopotential(geopotential_alt)
50
+ gravity_at_geometric(geometric_h)
51
+ end
52
+
53
+ # 2.4 Atmospheric composition and air molar mass
54
+
55
+ # 2.5 Physical characteristics of the atmosphere at mean sea level
56
+
57
+ # 2.6 Temperature and vertical temperature gradient
58
+
59
+ # Formula (11)
60
+ # T
61
+ def temperature_at_layer_from_geopotential(geopotential_alt)
62
+ lower_layer_index = locate_lower_layer(geopotential_alt)
63
+ lower_layer = TEMPERATURE_LAYERS[lower_layer_index]
64
+ beta = num(lower_layer[:B])
65
+ capital_t_b = num(lower_layer[:T])
66
+ capital_h_b = num(lower_layer[:H])
67
+
68
+ capital_t_b + (beta * (geopotential_alt - capital_h_b))
69
+ end
70
+
71
+ def temperature_at_layer_celcius(geopotential_alt)
72
+ kelvin_to_celsius(
73
+ temperature_at_layer_from_geopotential(geopotential_alt)
74
+ )
75
+ end
76
+
77
+ def locate_lower_layer(geopotential_alt)
78
+ # Return first layer if lower than lowest
79
+ return 0 if geopotential_alt < num(TEMPERATURE_LAYERS[0][:H])
80
+
81
+ # Return second last layer if beyond last layer
82
+ i = TEMPERATURE_LAYERS.length - 1
83
+ return i - 1 if geopotential_alt >= num(TEMPERATURE_LAYERS[i][:H])
84
+
85
+ # find last layer with H smaller than our H
86
+ TEMPERATURE_LAYERS.each_with_index do |layer, ind|
87
+ return ind - 1 if num(layer[:H]) > geopotential_alt
88
+ end
89
+
90
+ nil
91
+ end
92
+
93
+ # Table 4 - Temperature and vertical temperature gradients
94
+ #
95
+ TEMPERATURE_LAYERS = [
96
+ # H is Geopotential altitude (base altitude) above mean sea level, m
97
+ # T is Temperature, K
98
+ # B is Temperature gradient, "beta", K m^-1
99
+
100
+ # This line is from ICAO 7488/3
101
+ # { H: "-5000", T: "320.65", B: "-0.0065" },
102
+
103
+ # This line is from ISO 2533:1975
104
+ { H: "-2000", T: "301.15", B: "-0.0065" },
105
+ { H: "0", T: "288.15", B: "-0.0065" },
106
+ { H: "11000", T: "216.65", B: "0" },
107
+ { H: "20000", T: "216.65", B: "0.001" },
108
+ { H: "32000", T: "228.65", B: "0.0028" },
109
+ { H: "47000", T: "270.65", B: "0" },
110
+ { H: "51000", T: "270.65", B: "-0.0028" },
111
+ { H: "71000", T: "214.65", B: "-0.002" },
112
+ { H: "80000", T: "196.65" }
113
+ ].freeze
114
+
115
+ # 2.7 Pressure
116
+
117
+ # Base pressure values given defined `TEMPERATURE_LAYERS` and constants
118
+ def pressure_layers
119
+ return @pressure_layers if @pressure_layers
120
+
121
+ # assume TEMPERATURE_LAYERS index 1 base altitude is 0 (mean sea level)
122
+ p = []
123
+
124
+ TEMPERATURE_LAYERS.each_with_index do |_x, i|
125
+ last_i = i.zero? ? 0 : i - 1
126
+ last_layer = TEMPERATURE_LAYERS[last_i]
127
+ beta = num(last_layer[:B])
128
+
129
+ if num(last_layer[:H]) <= 0
130
+ p_b = @constants[:p_n]
131
+ capital_h_b = 0
132
+ capital_t_b = @constants[:T_n]
133
+ else
134
+ p_b = p[last_i]
135
+ capital_h_b = num(last_layer[:H])
136
+ capital_t_b = num(last_layer[:T])
137
+ end
138
+
139
+ current_layer = TEMPERATURE_LAYERS[i]
140
+ geopotential_alt = num(current_layer[:H])
141
+ temp = num(current_layer[:T])
142
+ height_diff = geopotential_alt - capital_h_b
143
+
144
+ p_i = if beta.zero?
145
+ # Formula (13)
146
+ pressure_formula_beta_zero(p_b, temp, height_diff)
147
+ else
148
+ # Formula (12)
149
+ pressure_formula_beta_nonzero(p_b, beta, capital_t_b,
150
+ height_diff)
151
+ end
152
+ p[i] = p_i
153
+ end
154
+
155
+ @pressure_layers = p
156
+ end
157
+
158
+ # Formula (12)
159
+ def pressure_formula_beta_nonzero(p_b, beta, temp, height_diff)
160
+ p_b * (1 + ((beta / temp) * height_diff)) \
161
+ **(-@constants[:g_n] / (beta * @constants[:R]))
162
+ end
163
+
164
+ # Formula (13)
165
+ def pressure_formula_beta_zero(p_b, temp, height_diff)
166
+ p_b *
167
+ Math.exp(-(@constants[:g_n] / (@constants[:R] * temp)) * height_diff)
168
+ end
169
+
170
+ # puts "PRE-CALCULATED PRESSURE LAYERS:"
171
+ # pp @pressure_layers
172
+
173
+ def pa_to_mmhg(pascal)
174
+ pascal * num("0.007500616827")
175
+ end
176
+
177
+ def pa_to_mbar(pascal)
178
+ pascal * num("0.01")
179
+ end
180
+
181
+ # Pressure for a given geopotential altitude `H` (m) above mean sea level
182
+ def pressure_from_geopotential(geopotential_alt)
183
+ i = locate_lower_layer(geopotential_alt)
184
+ lower_temperature_layer = TEMPERATURE_LAYERS[i]
185
+ beta = num(lower_temperature_layer[:B])
186
+ capital_h_b = num(lower_temperature_layer[:H])
187
+ capital_t_b = num(lower_temperature_layer[:T])
188
+ temp = temperature_at_layer_from_geopotential(geopotential_alt)
189
+ p_b = pressure_layers[i]
190
+ height_diff = geopotential_alt - capital_h_b
191
+
192
+ if beta.zero?
193
+ # Formula (13)
194
+ pressure_formula_beta_zero(p_b, temp, height_diff)
195
+ else
196
+ # Formula (12)
197
+ pressure_formula_beta_nonzero(p_b, beta, capital_t_b, height_diff)
198
+ end
199
+ end
200
+
201
+ def pressure_from_geopotential_mbar(geopotential_alt)
202
+ pa_to_mbar(pressure_from_geopotential(geopotential_alt))
203
+ end
204
+
205
+ def pressure_from_geopotential_mmhg(geopotential_alt)
206
+ pa_to_mmhg(pressure_from_geopotential(geopotential_alt))
207
+ end
208
+
209
+ def p_p_n_from_geopotential(geopotential_alt)
210
+ pressure_from_geopotential(geopotential_alt) / @constants[:p_n]
211
+ end
212
+
213
+ # 2.8 Density and specific weight
214
+
215
+ # Density for a given geopotential altitude `H` (m) above mean sea level
216
+ # Formula (14)
217
+ # rho
218
+ def density_from_geopotential(geopotential_alt)
219
+ temp = temperature_at_layer_from_geopotential(geopotential_alt)
220
+ p = pressure_from_geopotential(geopotential_alt)
221
+
222
+ p / (@constants[:R] * temp)
223
+ end
224
+
225
+ def rho_rho_n_from_geopotential(geopotential_alt)
226
+ density_from_geopotential(geopotential_alt) / @constants[:rho_n]
227
+ end
228
+
229
+ def root_rho_rho_n_from_geopotential(geopotential_alt)
230
+ sqrt(rho_rho_n_from_geopotential(geopotential_alt))
231
+ end
232
+
233
+ # Specific weight
234
+ # Formula (15)
235
+ # gamma
236
+ def specific_weight_from_geopotential(geopotential_alt)
237
+ density_from_geopotential(geopotential_alt) *
238
+ gravity_at_geopotential(geopotential_alt)
239
+ end
240
+
241
+ # 2.9 Pressure scale height
242
+ # Formula (16)
243
+ # H_p
244
+ def pressure_scale_height_from_temp(temp)
245
+ (@constants[:R] * temp) / @constants[:g_n]
246
+ end
247
+
248
+ def pressure_scale_height_from_geopotential(geopotential_alt)
249
+ temp = temperature_at_layer_from_geopotential(geopotential_alt)
250
+ (@constants[:R] * temp) / gravity_at_geopotential(geopotential_alt)
251
+ end
252
+
253
+ # 2.10 Air number density
254
+ # Formula (17)
255
+ # n
256
+ def air_number_density_from_geopotential(geopotential_alt)
257
+ temp = temperature_at_layer_from_geopotential(geopotential_alt)
258
+ p = pressure_from_geopotential(geopotential_alt)
259
+
260
+ @constants[:N_A] * p / (@constants[:R_star] * temp)
261
+ end
262
+
263
+ # 2.11 Mean air-particle speed
264
+ # Formula (18)
265
+ # v_bar
266
+ # CORRECT
267
+ def mean_air_particle_speed_from_temp(temp)
268
+ num("1.595769") * sqrt(@constants[:R] * temp)
269
+ end
270
+
271
+ def mean_air_particle_speed_from_geopotential(geopotential_alt)
272
+ temp = temperature_at_layer_from_geopotential(geopotential_alt)
273
+ mean_air_particle_speed_from_temp(temp)
274
+ end
275
+
276
+ # 2.12 Mean free path of air particles
277
+ # Formula (19)
278
+ # l
279
+ def mean_free_path_of_air_particles_from_geopotential(geopotential_alt)
280
+ # 1 / (sqrt(2) * Pi * (3.65e-10**2) * air_number_density
281
+ 1 / (@sqrt2 * @pi * num("0.133225e-18") * \
282
+ air_number_density_from_geopotential(geopotential_alt))
283
+ end
284
+
285
+ # 2.13 Air-particle collision frequency
286
+ # Formula (20)
287
+ # omega
288
+ def air_particle_collision_frequency_from_temp(air_number_density, temp)
289
+ # 4 * (3.65e-10**2) * ...
290
+ 4 * num("0.133225e-18") *
291
+ ((@pi / (@constants[:R_star] * @constants[:M]))**num("0.5")) *
292
+ air_number_density * @constants[:R_star] * (temp**num("0.5"))
293
+ end
294
+
295
+ def air_particle_collision_frequency_from_geopotential(geopotential_alt)
296
+ temp = temperature_at_layer_from_geopotential(geopotential_alt)
297
+ n = air_number_density_from_geopotential(geopotential_alt)
298
+ air_particle_collision_frequency_from_temp(n, temp)
299
+ end
300
+
301
+ # 2.14 Speed of sound
302
+ # Formula (21)
303
+ # a (ms-1)
304
+ # CORRECT
305
+ def speed_of_sound_from_temp(temp)
306
+ # `kappa` (ratio of c_p / c_v) = 1.4 (see 2.14)
307
+ kappa = num("1.4")
308
+ sqrt(kappa * @constants[:R] * temp)
309
+ end
310
+
311
+ def speed_of_sound_from_geopotential(geopotential_alt)
312
+ temp = temperature_at_layer_from_geopotential(geopotential_alt)
313
+ speed_of_sound_from_temp(temp)
314
+ end
315
+
316
+ # 2.15 Dynamic viscosity
317
+ # Formula (22)
318
+ # mu (Pa s)
319
+ def dynamic_viscosity(temp)
320
+ # Sutherland's empirical constants in the equation for dynamic viscosity
321
+ capital_b_s = num("1.458e-6")
322
+ capital_s = num("110.4")
323
+
324
+ (capital_b_s * (temp**num("1.5"))) / (temp + capital_s)
325
+ end
326
+
327
+ def dynamic_viscosity_from_geopotential(geopotential_alt)
328
+ temp = temperature_at_layer_from_geopotential(geopotential_alt)
329
+ dynamic_viscosity(temp)
330
+ end
331
+
332
+ # 2.16 Kinematic viscosity
333
+ # Formula (23)
334
+ # v
335
+ def kinematic_viscosity(temp)
336
+ dynamic_viscosity(temp) / @constants[:rho_n]
337
+ end
338
+
339
+ def kinematic_viscosity_from_geopotential(geopotential_alt)
340
+ temp = temperature_at_layer_from_geopotential(geopotential_alt)
341
+ dynamic_viscosity(temp) / density_from_geopotential(geopotential_alt)
342
+ end
343
+
344
+ # 2.17 Thermal conductivity
345
+ # Formula (24)
346
+ # lambda
347
+ def thermal_conductivity_from_temp(temp)
348
+ (num("2.648151e-3") * (temp**num("1.5"))) \
349
+ / (temp + (num("245.4") * (num("10")**(num("-12") / temp))))
350
+ end
351
+
352
+ def thermal_conductivity_from_geopotential(geopotential_alt)
353
+ temp = temperature_at_layer_from_geopotential(geopotential_alt)
354
+ thermal_conductivity_from_temp(temp)
355
+ end
356
+
357
+ def kelvin_to_celsius(kelvin)
358
+ kelvin - num("273.15")
359
+ end
360
+
361
+ # ADD 1
362
+ # Formulae used in the calculation of the relationships
363
+ # between geopotential altitude and pressure
364
+ def geopotential_altitude_from_pressure_mbar(pressure)
365
+ if pressure >= pa_to_mbar(pressure_layers[2]) # H <= 11 000 m
366
+ (num("3.731444") - pressure**num("0.1902631")) / num("8.41728e-5")
367
+ elsif pressure >= pa_to_mbar(pressure_layers[3]) # H <= 20 000 m
368
+ (num("3.1080387") - log10(pressure)) / num("6.848325e-5")
369
+ elsif pressure >= pa_to_mbar(pressure_layers[4]) # H <= 32 000 m
370
+ (num("1.2386515") - pressure**num("0.02927125")) \
371
+ / (num("5.085177e-6") * pressure**num("0.02927125"))
372
+ elsif pressure >= pa_to_mbar(pressure_layers[5]) # H <= 47 000 m
373
+ (num("1.9630052") - pressure**num("0.08195949")) \
374
+ / (num("2.013664e-5") * pressure**num("0.08195949"))
375
+ end
376
+ end
377
+
378
+ def geopotential_altitude_from_pressure_mmhg(pressure)
379
+ if pressure >= pa_to_mmhg(pressure_layers[2]) # H <= 11 000 m
380
+ (num("3.532747") - pressure**num("0.1902631")) / num("7.96906e-5")
381
+ elsif pressure >= pa_to_mmhg(pressure_layers[3]) # H <= 20 000 m
382
+ (num("2.9831357") - log10(pressure)) / num("6.848325e-5")
383
+ elsif pressure >= pa_to_mmhg(pressure_layers[4]) # H <= 32 000 m
384
+ (num("1.2282678") - pressure**num("0.02927125")) \
385
+ / (num("5.085177e-6") * pressure**num("0.02927125"))
386
+ elsif pressure >= pa_to_mmhg(pressure_layers[5]) # H <= 47 000 m
387
+ (num("1.9172753") - pressure**num("0.08195949")) \
388
+ / (num("2.013664e-5") * pressure**num("0.08195949"))
389
+ end
390
+ end
391
+
392
+ def mbar_to_mmhg(mbar)
393
+ # Convert mbar to Pa, then Pa to mmHg
394
+ pa = mbar / num("0.01") # or mbar * 100
395
+ pa_to_mmhg(pa)
396
+ end
397
+
398
+ def mmhg_to_mbar(mmhg)
399
+ # Convert mmHg to Pa, then Pa to mbar
400
+ pa = mmhg / num("0.007500616827")
401
+ pa_to_mbar(pa)
402
+ end
403
+
404
+ private
405
+
406
+ def num(str)
407
+ if @precision == :high
408
+ BigDecimal(str)
409
+ else
410
+ str.to_f
411
+ end
412
+ end
413
+
414
+ def sqrt(num)
415
+ if @precision == :high
416
+ BigMath.sqrt(num, 100)
417
+ else
418
+ Math.sqrt(num)
419
+ end
420
+ end
421
+
422
+ def log10(num)
423
+ if @precision == :high
424
+ BigMath.log(num, 100) / BigMath.log(10, 100)
425
+ else
426
+ Math.log10(num)
427
+ end
428
+ end
429
+
430
+ def make_constants
431
+ # 2.1 Primary constants and characteristics
432
+ # Table 1 - Main constants and characteristics adopted for
433
+ # the calculation of the ISO Standard Atmosphere
434
+ @constants = {
435
+ # g_n gravitation at mean sea level (m.s-2)
436
+ g_n: num("9.80665"),
437
+
438
+ # Avogadro constant (mol-1)
439
+ N_A: num("6.02257e+23"),
440
+
441
+ # p_n pressure at mean sea level (Pa)
442
+ p_n: num("101325"),
443
+
444
+ # rho_n standard air density
445
+ rho_n: num("1.225"),
446
+
447
+ # T_n standard thermodynamic air temperature at mean sea level
448
+ T_n: num("288.15"),
449
+
450
+ # universal gas constant
451
+ R_star: num("8.31432"),
452
+
453
+ # radius of the Earth (m)
454
+ radius: num("6356766"),
455
+
456
+ # adiabatic index (dimensionless)
457
+ k: num("1.4")
458
+ }
459
+
460
+ # 2.2 The equation of the static atmosphere and the perfect gas law
461
+ # Formula (2)
462
+ # M: air molar mass at sea level, kg.kmol-1
463
+ # Value given in 2.1 as M: 28.964720
464
+ @constants[:M] =
465
+ (@constants[:rho_n] * @constants[:R_star] * @constants[:T_n]) / @constants[:p_n]
466
+
467
+ # Formula (3)
468
+ # R: specific gas constant, J.K-1.kg-1.
469
+ # Value given in 2.1 as R: 287.05287
470
+ @constants[:R] = @constants[:R_star] / @constants[:M]
471
+
472
+ @sqrt2 = sqrt(num("2"))
473
+ @pi = @precision == :high ? BigMath.PI(100) : Math::PI
474
+ end
475
+ end
476
+
477
+ class HighPrecision < Algorithms
478
+ include Singleton
479
+
480
+ def initialize
481
+ set_precision(:high)
482
+ end
483
+ end
484
+
485
+ class NormalPrecision < Algorithms
486
+ include Singleton
487
+
488
+ def initialize
489
+ set_precision(:normal)
490
+ end
491
+ end
492
+ end
493
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Atmospheris
4
+ class Iso2533Namespace < Lutaml::Xml::W3c::XmlNamespace
5
+ prefix_default "iso2533"
6
+ uri "urn:iso:std:iso:2533:tech:xsd"
7
+ end
8
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Atmospheris
4
+ class UnitValueFloat < Lutaml::Model::Serializable
5
+ attribute :value, :float
6
+ attribute :unitsml, :string
7
+ attribute :type, :string, default: -> { "float" }
8
+
9
+ key_value do
10
+ map "value", to: :value
11
+ map "unitsml", to: :unitsml
12
+ map "type", to: :type, render_default: true
13
+ end
14
+
15
+ xml do
16
+ element "unit-value-float"
17
+ namespace Atmospheris::Iso2533Namespace
18
+ map_content to: :value
19
+ map_attribute :unitsml, to: :unitsml
20
+ map_attribute :type, to: :type, render_default: true
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Atmospheris
4
+ class UnitValueInteger < Lutaml::Model::Serializable
5
+ attribute :value, :integer
6
+ attribute :unitsml, :string
7
+ attribute :type, :string, default: -> { "integer" }
8
+
9
+ key_value do
10
+ map "value", to: :value
11
+ map "unitsml", to: :unitsml
12
+ map "type", to: :type, render_default: true
13
+ end
14
+
15
+ xml do
16
+ element "unit-value-integer"
17
+ namespace Atmospheris::Iso2533Namespace
18
+ map_content to: :value
19
+ map_attribute :unitsml, to: :unitsml
20
+ map_attribute :type, to: :type, render_default: true
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Atmospheris
4
+ VERSION = "0.5.1"
5
+ end