iso_1996 1.1.0 → 2.0.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 42c3f17927b946ad5503a42169fbb4d6f708bda610d7c3f222065dbf8a812a85
4
- data.tar.gz: 6c5b485c2645308371a3d5310851a4250d4d804704b68a86e79afb77d81465ee
3
+ metadata.gz: 6722b04a3a9597e08f988a919443c5f09e2f84558cc6f901baf40a7af3af8be7
4
+ data.tar.gz: 6a7b230b84d0e0c03123c0e0bf163473ad3df78de44a93ccac3c4e634b133273
5
5
  SHA512:
6
- metadata.gz: 1965d9bc3d89059822aa55c9bcde48d28074a33d5ad277c40b186177771ace12bf4672f605ad2268c25e74c0d12f842bd87cc1b0411c115354432194d86969f7
7
- data.tar.gz: 2b0bcafd9ca8e0bdd9159a71ec8036da431b2f53fabf3e420cb535c9b37cd6587fd7b3025f18dc081181514cf9647ae91393e7175d53bb8ae15a483d16bde27d
6
+ metadata.gz: 0651a77612c0b8583070ffbdf85d101137c12e227c222709dc7d1f3a81350cab1f7201132e1e7c9411474a6350865e4ee3b90524719f2c1c8761181d3bc37292
7
+ data.tar.gz: ac81ec147e522a3d78126e92bae53f02b6785dd58c40e719a7895e5f0f19e77c02ef6dd3cba1fd7712206c4d32e7399ae65fecfaabf748139e26485f854f94ae
data/README.md CHANGED
@@ -1,14 +1,18 @@
1
1
 
2
- # ISO 1996 - Acoustics - Description, measurement and assessment of environmental noise Ruby gem.
2
+ # ISO 1996 - Acoustics - Description, measurement and assessment of environmental noise Ruby library.
3
3
 
4
4
  [![Gem Version](https://badge.fury.io/rb/iso_1996.svg)](https://badge.fury.io/rb/iso_1996)
5
5
  [![Documentation](https://img.shields.io/badge/docs-rdoc.info-blue)](https://rubydoc.info/gems/iso_1996)
6
6
 
7
- Ruby implementation of ISO 1996 - Acoustics - Description, measurement and assessment of environmental noise.
7
+ Ruby implementation of ISO 1996 - Acoustics - Description, measurement and assessment of environmental noise.
8
+ Provides complete calculations for environmental noise assessment according to ISO 1996-1:2016 and ISO 1996-2:2017 standards.
8
9
 
9
10
  ## Features
10
11
 
11
- - Complete implementation of all three parts of ISO 1996
12
+ - Complete implementation of ISO 1996-1:2016 and ISO 1996-2:2017
13
+ - Calculation of all key acoustic metrics
14
+ - Environmental corrections and uncertainty analysis
15
+ - Day-evening-night level (L<sub>den</sub>) assessment
12
16
  - Fully documented with RDoc comments
13
17
  - Comprehensive test suite
14
18
  - MIT licensed
@@ -31,6 +35,168 @@ gem install iso_1996
31
35
  Full documentation available at:
32
36
  https://rubydoc.info/gems/iso_1996
33
37
 
38
+ ## Example Usage
39
+
40
+ ```ruby
41
+ require 'iso_1996'
42
+
43
+ # ----------------------------------------------------------
44
+ # STEP 1: Data Collection - 6 measurements from sound level meter
45
+ # ----------------------------------------------------------
46
+ measurements = [
47
+ # Continuous measurements (A-weighted, 1-min each)
48
+ { time: Time.now - 300, pressure: 0.08, weighting: 'A', duration: 60 },
49
+ { time: Time.now - 240, pressure: 0.12, weighting: 'A', duration: 60 },
50
+ { time: Time.now - 180, pressure: 0.15, weighting: 'A', duration: 60 },
51
+ { time: Time.now - 120, pressure: 0.10, weighting: 'A', duration: 60 },
52
+ { time: Time.now - 60, pressure: 0.18, weighting: 'A', duration: 60 },
53
+
54
+ # Impulse measurement (C-weighted, 100ms)
55
+ { time: Time.now, pressure: 0.25, weighting: 'C', duration: 0.1 }
56
+ ]
57
+
58
+ # Background measurement (5-min average)
59
+ background = { pressure: 0.05, weighting: 'A', duration: 300 }
60
+
61
+ # ----------------------------------------------------------
62
+ # STEP 2: Calculate Basic Sound Levels (ISO 1996-1:2016)
63
+ # ----------------------------------------------------------
64
+
65
+ # Calculate SPL for each measurement
66
+ measurements.each do |m|
67
+ m[:spl] = ISO_1996::Part_1_2016.sound_pressure_level(m[:pressure])
68
+ end
69
+
70
+ # Calculate equivalent continuous sound level (L_Aeq)
71
+ a_weighted = measurements.select { |m| m[:weighting] == 'A' }
72
+ l_aeq = ISO_1996::Part_1_2016.equivalent_continuous_sound_level(
73
+ a_weighted.map { |m| m[:spl] },
74
+ a_weighted.sum { |m| m[:duration] }
75
+ )
76
+
77
+ # Calculate peak sound pressure level (L_Cpeak)
78
+ impulse = measurements.find { |m| m[:weighting] == 'C' }
79
+ l_cpeak = ISO_1996::Part_1_2016.peak_sound_pressure_level(impulse[:pressure])
80
+
81
+ # ----------------------------------------------------------
82
+ # STEP 3: Environmental Corrections (ISO 1996-2:2017)
83
+ # ----------------------------------------------------------
84
+
85
+ # Calculate background noise correction
86
+ background_spl = ISO_1996::Part_1_2016.sound_pressure_level(background[:pressure])
87
+ background_correction = ISO_1996::Part_2_2017.background_noise_correction(l_aeq, background_spl)
88
+
89
+ # Apply atmospheric absorption correction
90
+ attenuation_coefficient = 0.005 # dB/m (from ISO 9613-1 based on temp/humidity)
91
+ distance = 50.0 # meters
92
+ atmospheric_correction = ISO_1996::Part_2_2017.atmospheric_absorption_correction(
93
+ attenuation_coefficient, distance
94
+ )
95
+
96
+ # Calculate total correction
97
+ corrected_l_aeq = l_aeq - background_correction - atmospheric_correction
98
+
99
+ # ----------------------------------------------------------
100
+ # STEP 4: Day-Evening-Night Level Calculation (L_den) (ISO 1996-1:2016)
101
+ # ----------------------------------------------------------
102
+
103
+ # Normally collected separately for each period
104
+ day_measurements = [65.2, 67.1, 66.5] # 3-hour measurements
105
+ evening_measurements = [63.8, 62.4] # 2-hour measurements
106
+ night_measurements = [58.7, 57.9, 59.3] # 3-hour measurements
107
+
108
+ # Calculate L_Aeq for each period
109
+ l_day = ISO_1996::Part_1_2016.equivalent_continuous_sound_level(day_measurements, 3 * 3600)
110
+ l_evening = ISO_1996::Part_1_2016.equivalent_continuous_sound_level(evening_measurements, 2 * 3600)
111
+ l_night = ISO_1996::Part_1_2016.equivalent_continuous_sound_level(night_measurements, 3 * 3600)
112
+
113
+ # Calculate L_den with standard penalties
114
+ l_den = ISO_1996::Part_1_2016.day_evening_night_level(l_day, l_evening, l_night)
115
+
116
+ # ----------------------------------------------------------
117
+ # STEP 5: Assessment Procedures (ISO 1996-1:2016)
118
+ # ----------------------------------------------------------
119
+
120
+ # Determine adjustment factors (requires professional judgment)
121
+ is_tonal_audible = true # Based on spectral analysis
122
+ is_tonal_prominent = false
123
+ k_t = ISO_1996::Part_1_2016.tonal_adjustment_factor(
124
+ is_audible: is_tonal_audible,
125
+ is_prominent: is_tonal_prominent
126
+ )
127
+
128
+ is_impulse_audible = true
129
+ is_impulse_distinct = true
130
+ k_i = ISO_1996::Part_1_2016.impulsive_adjustment_factor(
131
+ is_audible: is_impulse_audible,
132
+ is_distinct: is_impulse_distinct
133
+ )
134
+
135
+ # Calculate assessment level
136
+ assessment_level = ISO_1996::Part_1_2016.assessment_level(corrected_l_aeq, k_t, k_i)
137
+
138
+ # ----------------------------------------------------------
139
+ # STEP 6: Compliance Evaluation
140
+ # ----------------------------------------------------------
141
+ noise_limit = 65.0 # dB(A) - local regulation
142
+ uncertainty_components = [0.5, 1.0, 0.7] # Instrument, position, environmental
143
+ measurement_uncertainty = ISO_1996::Part_2_2017.measurement_uncertainty(uncertainty_components)
144
+
145
+ is_compliant = !ISO_1996::Part_1_2016.compliance_evaluation(
146
+ assessment_level,
147
+ noise_limit,
148
+ measurement_uncertainty
149
+ )
150
+
151
+ # ----------------------------------------------------------
152
+ # STEP 7: Reporting
153
+ # ----------------------------------------------------------
154
+ puts "Environmental Noise Assessment Report"
155
+ puts "------------------------------------"
156
+ puts "Measurement Period: #{measurements.first[:time]} to #{measurements.last[:time]}"
157
+ puts "Equivalent Continuous Level (L_Aeq): #{l_aeq.round(1)} dB(A)"
158
+ puts "Background Correction: #{background_correction.round(1)} dB"
159
+ puts "Atmospheric Correction: #{atmospheric_correction.round(1)} dB"
160
+ puts "Corrected L_Aeq: #{corrected_l_aeq.round(1)} dB(A)"
161
+ puts "Tonal Adjustment (K_T): #{k_t} dB"
162
+ puts "Impulsive Adjustment (K_I): #{k_i} dB"
163
+ puts "Assessment Level (L_r): #{assessment_level.round(1)} dB(A)"
164
+ puts "Day Level (L_day): #{l_day.round(1)} dB(A)"
165
+ puts "Evening Level (L_evening): #{l_evening.round(1)} dB(A)"
166
+ puts "Night Level (L_night): #{l_night.round(1)} dB(A)"
167
+ puts "Day-Evening-Night Level (L_den): #{l_den.round(1)} dB(A)"
168
+ puts "Peak Sound Level (L_Cpeak): #{l_cpeak.round(1)} dB(C)"
169
+ puts "Noise Limit: #{noise_limit} dB(A)"
170
+ puts "Measurement Uncertainty: ±#{measurement_uncertainty.round(1)} dB"
171
+ puts "Compliance Status: #{is_compliant ? 'PASS' : 'FAIL'}"
172
+ puts "------------------------------------"
173
+ ```
174
+
175
+ ## Handling Non-formula Aspects of the Standard
176
+ The ISO 1996 standards contain important guidance that isn't expressed in formulas but requires professional judgment:
177
+
178
+ 1. Tonal and Impulsive Character Assessment:
179
+ - Use the tonal_adjustment_factor and impulsive_adjustment_factor methods with parameters based on:
180
+ * Spectral analysis (FFT) to identify prominent tones
181
+ * Auditory assessment by trained professionals
182
+ * Historical data and community complaints
183
+ 2. Meteorological Conditions:
184
+ - Measure and apply corrections for:
185
+ * Wind speed and direction (affects sound propagation)
186
+ * Temperature gradients (affects refraction)
187
+ * Humidity (affects high-frequency absorption)
188
+ * Use ISO 9613-1 for detailed atmospheric absorption coefficients
189
+ 3. Measurement Uncertainty:
190
+ - Calculate using the measurement_uncertainty method with components:
191
+ * Instrument calibration uncertainty
192
+ * Measurement position uncertainty
193
+ * Environmental conditions uncertainty
194
+ 4. Time Period Handling:
195
+ - For day-evening-night calculations:
196
+ * Collect separate measurements for each period
197
+ * Use local definitions for period boundaries (e.g., day: 7:00-19:00)
198
+ * Apply appropriate penalties (standard is 5 dB evening, 10 dB night)
199
+
34
200
  ## Contributing
35
201
  Bug reports and pull requests are welcome on GitHub at:
36
202
  https://github.com/ciembor/iso_1996
@@ -0,0 +1,206 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ISO_1996
4
+
5
+ ##
6
+ # == ISO 1996-1:2016 Acoustics - Description, measurement and assessment of environmental noise -
7
+ # Part 1: Basic quantities and assessment procedures
8
+ #
9
+ # Module implementing calculations defined in ISO 1996-1:2016
10
+ #
11
+ # Author:: Maciej Ciemborowicz
12
+ # Date:: July 13, 2025
13
+ #
14
+ module Part_1_2016
15
+ ##
16
+ # Constants defined in ISO 1996-1:2016 standard
17
+ module Constants
18
+ ##
19
+ # Reference sound pressure (p₀) as defined in Section 3.1.1
20
+ # Value: 20 μPa (20e-6 Pa)
21
+ REFERENCE_SOUND_PRESSURE = 20e-6 # Pa
22
+
23
+ ##
24
+ # Reference time (t₀) for sound exposure level as defined in Section 3.1.8
25
+ # Value: 1 second
26
+ REFERENCE_TIME = 1.0 # s
27
+
28
+ ##
29
+ # Standard duration for day period as defined in Annex C.2
30
+ DAY_DURATION = 12.0 # hours
31
+ ##
32
+ # Standard duration for evening period as defined in Annex C.2
33
+ EVENING_DURATION = 4.0 # hours
34
+ ##
35
+ # Standard duration for night period as defined in Annex C.2
36
+ NIGHT_DURATION = 8.0 # hours
37
+
38
+ ##
39
+ # Penalty for evening period as defined in Annex C.2
40
+ EVENING_PENALTY = 5.0 # dB
41
+ ##
42
+ # Penalty for night period as defined in Annex C.2
43
+ NIGHT_PENALTY = 10.0 # dB
44
+ end
45
+ include Constants
46
+
47
+ ##
48
+ # Calculate sound pressure level (L_p) as defined in Section 3.1.2
49
+ #
50
+ # L_p = 10 * log10(p² / p₀²) dB
51
+ #
52
+ # @param p [Float] Root-mean-square sound pressure (Pa)
53
+ # @return [Float] Sound pressure level in dB
54
+ #
55
+ # Example:
56
+ # Part_1_2016.sound_pressure_level(0.1) # => 74.0 dB
57
+ #
58
+ def self.sound_pressure_level(p)
59
+ 10 * Math.log10((p ** 2) / (Constants::REFERENCE_SOUND_PRESSURE ** 2))
60
+ end
61
+
62
+ ##
63
+ # Calculate sound exposure level (L_AE) as defined in Section 3.1.8
64
+ #
65
+ # L_AE = 10 * log10( (1/t₀) * ∫(p_A²(t)/p₀²) dt ) dB
66
+ #
67
+ # @param p_a [Float] A-weighted sound pressure (Pa)
68
+ # @return [Float] Sound exposure level in dB
69
+ #
70
+ # Note: This method assumes a single value for simplicity.
71
+ # For time-varying signals, integration over time is required.
72
+ #
73
+ def self.sound_exposure_level(p_a)
74
+ 10 * Math.log10((1.0 / Constants::REFERENCE_TIME) * (p_a ** 2) / (Constants::REFERENCE_SOUND_PRESSURE ** 2))
75
+ end
76
+
77
+ ##
78
+ # Calculate equivalent continuous sound level (L_Aeq,T) as defined in Section 3.1.7
79
+ #
80
+ # L_Aeq,T = 10 * log10( (1/T) * Σ(10^(0.1*L_i)) ) dB
81
+ #
82
+ # @param levels [Array<Float>] Array of sound pressure levels (dB)
83
+ # @param measurement_time [Float] Total measurement time (seconds)
84
+ # @return [Float] Equivalent continuous sound level in dB
85
+ #
86
+ # Example:
87
+ # levels = [65.0, 67.0, 63.0]
88
+ # Part_1_2016.equivalent_continuous_sound_level(levels, 3.0) # => ~65.1 dB
89
+ #
90
+ def self.equivalent_continuous_sound_level(levels, measurement_time)
91
+ raise ArgumentError, "Measurement time must be positive" if measurement_time <= 0
92
+
93
+ return -Float::INFINITY if levels.empty?
94
+
95
+ energy_sum = levels.sum { |l| 10 ** (l / 10.0) }
96
+ 10 * Math.log10(energy_sum / measurement_time)
97
+ end
98
+
99
+ ##
100
+ # Calculate peak sound pressure level (L_pC,peak) as defined in Section 3.1.10
101
+ #
102
+ # L_pC,peak = 20 * log10(p_Cmax / p₀) dB
103
+ #
104
+ # @param p_c_max [Float] Maximum C-weighted sound pressure (Pa)
105
+ # @return [Float] Peak sound pressure level in dB
106
+ #
107
+ def self.peak_sound_pressure_level(p_c_max)
108
+ 20 * Math.log10(p_c_max / Constants::REFERENCE_SOUND_PRESSURE)
109
+ end
110
+
111
+ ##
112
+ # Calculate day-evening-night level (L_den) as defined in Annex C.2
113
+ #
114
+ # L_den = 10 * log10( (1/24) * [t_d·10^(L_day/10) + t_e·10^((L_evening + P_e)/10) + t_n·10^((L_night + P_n)/10)] ) dB
115
+ #
116
+ # @param l_day [Float] Day-time equivalent sound level (dB)
117
+ # @param l_evening [Float] Evening-time equivalent sound level (dB)
118
+ # @param l_night [Float] Night-time equivalent sound level (dB)
119
+ # @param day_duration [Float] Duration of day period (hours)
120
+ # @param evening_duration [Float] Duration of evening period (hours)
121
+ # @param night_duration [Float] Duration of night period (hours)
122
+ # @param evening_penalty [Float] Penalty for evening period (dB)
123
+ # @param night_penalty [Float] Penalty for night period (dB)
124
+ # @return [Float] Day-evening-night level in dB
125
+ #
126
+ # Example:
127
+ # Part_1_2016.day_evening_night_level(65.0, 62.0, 58.0) # => ~67.1 dB
128
+ #
129
+ def self.day_evening_night_level(l_day, l_evening, l_night,
130
+ day_duration: Constants::DAY_DURATION,
131
+ evening_duration: Constants::EVENING_DURATION,
132
+ night_duration: Constants::NIGHT_DURATION,
133
+ evening_penalty: Constants::EVENING_PENALTY,
134
+ night_penalty: Constants::NIGHT_PENALTY)
135
+ total_hours = day_duration + evening_duration + night_duration
136
+
137
+ term_day = day_duration * 10 ** (l_day / 10.0)
138
+ term_evening = evening_duration * 10 ** ((l_evening + evening_penalty) / 10.0)
139
+ term_night = night_duration * 10 ** ((l_night + night_penalty) / 10.0)
140
+
141
+ 10 * Math.log10((term_day + term_evening + term_night) / total_hours)
142
+ end
143
+
144
+ ##
145
+ # Determine tonal adjustment factor (K_T) as defined in Annex D.3
146
+ #
147
+ # @param is_audible [Boolean] Whether the tone is clearly audible
148
+ # @param is_prominent [Boolean] Whether the tone is prominent
149
+ # @return [Float] Tonal adjustment factor in dB (0.0, 3.0, 6.0)
150
+ #
151
+ # According to Annex D.3:
152
+ # Prominent tone: 6 dB
153
+ # Clearly audible but not prominent: 3 dB
154
+ # Not clearly audible: 0 dB
155
+ #
156
+ def self.tonal_adjustment_factor(is_audible: false, is_prominent: false)
157
+ return 0.0 unless is_audible
158
+ return 6.0 if is_prominent
159
+ 3.0
160
+ end
161
+
162
+ ##
163
+ # Determine impulsive adjustment factor (K_I) as defined in Annex D.4
164
+ #
165
+ # @param is_audible [Boolean] Whether the impulsive sound is clearly audible
166
+ # @param is_distinct [Boolean] Whether the impulsive sound is distinct
167
+ # @return [Float] Impulsive adjustment factor in dB (0.0, 3.0, 6.0)
168
+ #
169
+ # According to Annex D.4:
170
+ # Distinct impulsive sound: 6 dB
171
+ # Clearly audible but not distinct: 3 dB
172
+ # Not clearly audible: 0 dB
173
+ #
174
+ def self.impulsive_adjustment_factor(is_audible: false, is_distinct: false)
175
+ return 0.0 unless is_audible
176
+ return 6.0 if is_distinct
177
+ 3.0
178
+ end
179
+
180
+ ##
181
+ # Calculate assessment level (L_r) as defined in Section 3.6
182
+ #
183
+ # L_r = L_AeqT + K_T + K_I dB
184
+ #
185
+ # @param l_aeq_t [Float] Equivalent continuous A-weighted sound pressure level (dB)
186
+ # @param k_t [Float] Tonal adjustment factor (dB)
187
+ # @param k_i [Float] Impulsive adjustment factor (dB)
188
+ # @return [Float] Assessment level in dB
189
+ #
190
+ def self.assessment_level(l_aeq_t, k_t, k_i)
191
+ l_aeq_t + k_t + k_i
192
+ end
193
+
194
+ ##
195
+ # Evaluate compliance with noise limits as defined in Section 9.2
196
+ #
197
+ # @param assessment_level [Float] Calculated assessment level (dB)
198
+ # @param noise_limit [Float] Applicable noise limit (dB)
199
+ # @param measurement_uncertainty [Float] Measurement uncertainty (dB)
200
+ # @return [Boolean] True if limit is exceeded (assessment_level > noise_limit + measurement_uncertainty)
201
+ #
202
+ def self.compliance_evaluation(assessment_level, noise_limit, measurement_uncertainty)
203
+ assessment_level > noise_limit + measurement_uncertainty
204
+ end
205
+ end
206
+ end
@@ -3,18 +3,17 @@
3
3
  module ISO_1996
4
4
 
5
5
  ##
6
- # == ISO 1996-2:2007 Determination of Sound Pressure Levels
6
+ # == ISO 1996-2:2017 Acoustics - Description, measurement and assessment of environmental noise -
7
+ # Part 2: Determination of sound pressure levels
7
8
  #
8
- # Module implementing calculations defined in ISO 1996-2:2007:
9
- # "Acoustics - Description, measurement and assessment of environmental noise -
10
- # Part 2: Determination of sound pressure levels"
9
+ # Module implementing calculations defined in ISO 1996-2:2017
11
10
  #
12
11
  # Author:: Maciej Ciemborowicz
13
- # Date:: July 11, 2025
12
+ # Date:: July 13, 2025
14
13
  #
15
- module EnvironmentalNoise
14
+ module Part_2_2017
16
15
  ##
17
- # Constants defined in ISO 1996-2:2007 standard
16
+ # Constants defined in ISO 1996-2:2017 standard
18
17
  module Constants
19
18
  ##
20
19
  # Minimum level difference for background correction (ΔL_min) as defined in Section 6.3
@@ -40,7 +39,7 @@ module ISO_1996
40
39
  # @raise [ArgumentError] if ΔL ≤ 3 dB (measurement uncertain)
41
40
  #
42
41
  # Example:
43
- # EnvironmentalNoise.background_noise_correction(65, 60) # => 1.7 dB
42
+ # Part_2_2017.background_noise_correction(65, 60) # => 1.7 dB
44
43
  #
45
44
  def self.background_noise_correction(l_total, l_background)
46
45
  delta_l = l_total - l_background
@@ -76,10 +75,10 @@ module ISO_1996
76
75
  # @return [Float] Combined measurement uncertainty in dB
77
76
  #
78
77
  # Example:
79
- # EnvironmentalNoise.measurement_uncertainty([0.5, 1.0, 0.7]) # => 1.28 dB
78
+ # Part_2_2017.measurement_uncertainty([0.5, 1.0, 0.7]) # => 1.28 dB
80
79
  #
81
80
  def self.measurement_uncertainty(uncertainty_components)
82
81
  Math.sqrt(uncertainty_components.sum { |c| c ** 2 })
83
82
  end
84
83
  end
85
- end
84
+ end
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ISO_1996
4
+ ##
5
+ # Withdrawn standards
6
+ #
7
+ module Withdrawn
8
+
9
+ ##
10
+ # == ISO 1996-1:2003 Basic Quantities and Assessment Procedures
11
+ #
12
+ # Module implementing calculations defined in ISO 1996-1:2003:
13
+ # "Acoustics - Description, measurement and assessment of environmental noise -
14
+ # Part 1: Basic quantities and assessment procedures"
15
+ #
16
+ # Author:: Maciej Ciemborowicz
17
+ # Date:: July 11, 2025
18
+ #
19
+ module Part_1_2003
20
+ ##
21
+ # Constants defined in ISO 1996-1:2003 standard
22
+ module Constants
23
+ ##
24
+ # Reference sound pressure (p₀) as defined in Section 3.1
25
+ # Value: 20 μPa (20e-6 Pa)
26
+ REFERENCE_SOUND_PRESSURE = 20e-6 # Pa
27
+
28
+ ##
29
+ # Reference time (t₀) for sound exposure level as defined in Section 3.9
30
+ # Value: 1 second
31
+ REFERENCE_TIME = 1.0 # s
32
+ end
33
+ include Constants
34
+
35
+ ##
36
+ # Calculate sound pressure level (L_p) as defined in Section 3.2
37
+ #
38
+ # L_p = 10 * log10(p² / p₀²) dB
39
+ #
40
+ # @param p [Float] Root-mean-square sound pressure (Pa)
41
+ # @return [Float] Sound pressure level in dB
42
+ #
43
+ # Example:
44
+ # Basic.sound_pressure_level(0.1) # => 74.0 dB
45
+ #
46
+ def self.sound_pressure_level(p)
47
+ 10 * Math.log10((p ** 2) / (Constants::REFERENCE_SOUND_PRESSURE ** 2))
48
+ end
49
+
50
+ ##
51
+ # Calculate A-weighted sound pressure level (L_A) as defined in Section 3.2
52
+ #
53
+ # L_A = 10 * log10( (1/T) * ∫(p_A²(t)/p₀²) dt ) dB
54
+ #
55
+ # @param p_a [Float] A-weighted sound pressure (Pa)
56
+ # @param measurement_time [Float] Measurement time interval (seconds)
57
+ # @return [Float] A-weighted sound pressure level in dB
58
+ #
59
+ def self.a_weighted_sound_pressure_level(p_a, measurement_time: 1.0)
60
+ 10 * Math.log10((1.0 / measurement_time) * (p_a ** 2) / (Constants::REFERENCE_SOUND_PRESSURE ** 2))
61
+ end
62
+
63
+ ##
64
+ # Calculate sound exposure level (L_AE) as defined in Section 3.9
65
+ #
66
+ # L_AE = 10 * log10( (1/t₀) * ∫(p_A²(t)/p₀²) dt ) dB
67
+ #
68
+ # @param p_a [Float] A-weighted sound pressure (Pa)
69
+ # @return [Float] Sound exposure level in dB
70
+ #
71
+ def self.sound_exposure_level(p_a)
72
+ 10 * Math.log10((1.0 / Constants::REFERENCE_TIME) * (p_a ** 2) / (Constants::REFERENCE_SOUND_PRESSURE ** 2))
73
+ end
74
+
75
+ ##
76
+ # Calculate equivalent continuous sound level (L_Aeq,T) as defined in Section 3.7
77
+ #
78
+ # L_Aeq,T = 10 * log10( (1/T) * Σ(10^(0.1*L_i)) ) dB
79
+ #
80
+ # @param levels [Array<Float>] Array of sound pressure levels (dB)
81
+ # @param measurement_time [Float] Total measurement time (seconds)
82
+ # @return [Float] Equivalent continuous sound level in dB
83
+ #
84
+ # Example:
85
+ # levels = [65.0, 67.0, 63.0]
86
+ # Basic.equivalent_continuous_sound_level(levels, 3.0) # => ~65.1 dB
87
+ #
88
+ def self.equivalent_continuous_sound_level(levels, measurement_time)
89
+ raise ArgumentError, "Measurement time must be positive" if measurement_time <= 0
90
+
91
+ return -Float::INFINITY if levels.empty?
92
+
93
+ energy_sum = levels.sum { |l| 10 ** (l / 10.0) }
94
+ 10 * Math.log10(energy_sum / measurement_time)
95
+ end
96
+
97
+ ##
98
+ # Calculate C-weighted peak sound pressure level (L_Cpeak) as defined in Section 3.10
99
+ #
100
+ # L_Cpeak = 20 * log10(p_Cmax / p₀) dB
101
+ #
102
+ # @param p_c_max [Float] Maximum C-weighted sound pressure (Pa)
103
+ # @return [Float] C-weighted peak sound pressure level in dB
104
+ #
105
+ def self.peak_sound_pressure_level(p_c_max)
106
+ 20 * Math.log10(p_c_max / Constants::REFERENCE_SOUND_PRESSURE)
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ISO_1996
4
+ module Withdrawn
5
+
6
+ ##
7
+ # == ISO 1996-2:2007 Determination of Sound Pressure Levels
8
+ #
9
+ # Module implementing calculations defined in ISO 1996-2:2007:
10
+ # "Acoustics - Description, measurement and assessment of environmental noise -
11
+ # Part 2: Determination of sound pressure levels"
12
+ #
13
+ # Author:: Maciej Ciemborowicz
14
+ # Date:: July 11, 2025
15
+ #
16
+ module Part_2_2007
17
+ ##
18
+ # Constants defined in ISO 1996-2:2007 standard
19
+ module Constants
20
+ ##
21
+ # Minimum level difference for background correction (ΔL_min) as defined in Section 6.3
22
+ # Value: 3 dB
23
+ MIN_BACKGROUND_LEVEL_DIFFERENCE = 3.0 # dB
24
+
25
+ ##
26
+ # Threshold for background correction (ΔL_threshold) as defined in Section 6.3
27
+ # Value: 10 dB
28
+ BACKGROUND_CORRECTION_THRESHOLD = 10.0 # dB
29
+ end
30
+ include Constants
31
+
32
+ ##
33
+ # Calculate background noise correction (K₁) as defined in Section 6.3 and Annex D
34
+ #
35
+ # K₁ = -10 * log10(1 - 10^(-0.1 * ΔL)) dB
36
+ # where ΔL = L_total - L_background
37
+ #
38
+ # @param l_total [Float] Total sound pressure level (dB)
39
+ # @param l_background [Float] Background sound pressure level (dB)
40
+ # @return [Float] Background noise correction in dB
41
+ # @raise [ArgumentError] if ΔL ≤ 3 dB (measurement uncertain)
42
+ #
43
+ # Example:
44
+ # EnvironmentalNoise.background_noise_correction(65, 60) # => 1.7 dB
45
+ #
46
+ def self.background_noise_correction(l_total, l_background)
47
+ delta_l = l_total - l_background
48
+
49
+ if delta_l <= Constants::MIN_BACKGROUND_LEVEL_DIFFERENCE
50
+ raise ArgumentError, "Measurement uncertain: ΔL ≤ #{Constants::MIN_BACKGROUND_LEVEL_DIFFERENCE} dB"
51
+ elsif delta_l >= Constants::BACKGROUND_CORRECTION_THRESHOLD
52
+ 0.0
53
+ else
54
+ -10 * Math.log10(1 - 10 ** (-0.1 * delta_l))
55
+ end
56
+ end
57
+
58
+ ##
59
+ # Calculate atmospheric absorption correction (A_atm) as defined in Section 7.3 and Annex A
60
+ #
61
+ # A_atm = α * d dB
62
+ #
63
+ # @param attenuation_coefficient [Float] Atmospheric attenuation coefficient (dB/m)
64
+ # @param propagation_distance [Float] Sound propagation distance (m)
65
+ # @return [Float] Atmospheric absorption correction in dB
66
+ #
67
+ def self.atmospheric_absorption_correction(attenuation_coefficient, propagation_distance)
68
+ attenuation_coefficient * propagation_distance
69
+ end
70
+
71
+ ##
72
+ # Calculate combined measurement uncertainty as defined in Section 9
73
+ #
74
+ # u_total = √(Σ(u_i²)) dB
75
+ #
76
+ # @param uncertainty_components [Array<Float>] Array of uncertainty components (dB)
77
+ # @return [Float] Combined measurement uncertainty in dB
78
+ #
79
+ # Example:
80
+ # EnvironmentalNoise.measurement_uncertainty([0.5, 1.0, 0.7]) # => 1.28 dB
81
+ #
82
+ def self.measurement_uncertainty(uncertainty_components)
83
+ Math.sqrt(uncertainty_components.sum { |c| c ** 2 })
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ISO_1996
4
+ module Withdrawn
5
+
6
+ ##
7
+ # == ISO 1996-3:1987 Application to Noise Limits
8
+ #
9
+ # Module implementing calculations defined in ISO 1996-3:1987:
10
+ # "Acoustics - Description, measurement and assessment of environmental noise -
11
+ # Part 3: Application to noise limits"
12
+ #
13
+ # Author:: Maciej Ciemborowicz
14
+ # Date:: July 11, 2025
15
+ #
16
+ module Part_3_1987
17
+ ##
18
+ # Constants defined in ISO 1996-3:1987 standard
19
+ module Constants
20
+ ##
21
+ # Impulse correction threshold (L_Cpeak,min) as defined in Section 7.2
22
+ # Value: 130 dB
23
+ IMPULSE_CORRECTION_THRESHOLD = 130.0 # dB
24
+
25
+ ##
26
+ # Standard 24-hour period as defined in Annex A
27
+ # Value: 24 hours
28
+ STANDARD_24H_PERIOD = 24.0 # hours
29
+ end
30
+ include Constants
31
+
32
+ ##
33
+ # Determine tonal adjustment factor (K_T) as defined in Section 6 and Table 1
34
+ #
35
+ # @param delta_l [Float] Difference between tone level and background level (dB)
36
+ # @return [Float] Tonal adjustment factor in dB
37
+ #
38
+ # According to Table 1:
39
+ # ΔL ≥ 15 dB → 6 dB
40
+ # 10 ≤ ΔL ≤ 14 dB → 4-6 dB (default 5 dB)
41
+ # 5 ≤ ΔL ≤ 9 dB → 0-3 dB (default 2 dB)
42
+ # ΔL < 5 dB → 0 dB
43
+ #
44
+ def self.tonal_adjustment_factor(delta_l)
45
+ case delta_l
46
+ when 15..Float::INFINITY then 6.0
47
+ when 10..14 then 5.0
48
+ when 5..9 then 2.0
49
+ else 0.0
50
+ end
51
+ end
52
+
53
+ ##
54
+ # Determine impulsive adjustment factor (K_I) as defined in Section 7.2
55
+ #
56
+ # @param l_cpeak [Float] C-weighted peak sound pressure level (dB)
57
+ # @param is_highly_annoying [Boolean] Whether noise is subjectively assessed as highly annoying
58
+ # @return [Float] Impulsive adjustment factor in dB
59
+ #
60
+ # According to version:
61
+ # K_I = 6 dB if L_Cpeak ≥ 130 dB OR noise is highly annoying
62
+ # K_I = 0 dB otherwise
63
+ #
64
+ def self.impulsive_adjustment_factor(l_cpeak, is_highly_annoying: false)
65
+ (l_cpeak >= Constants::IMPULSE_CORRECTION_THRESHOLD || is_highly_annoying) ? 6.0 : 0.0
66
+ end
67
+
68
+ ##
69
+ # Calculate assessment level (L_r) as defined in Section 8
70
+ #
71
+ # L_r = L_AeqT + K_T + K_I dB
72
+ #
73
+ # @param l_aeq_t [Float] Equivalent continuous A-weighted sound pressure level (dB)
74
+ # @param k_t [Float] Tonal adjustment factor (dB)
75
+ # @param k_i [Float] Impulsive adjustment factor (dB)
76
+ # @return [Float] Assessment level in dB
77
+ #
78
+ def self.assessment_level(l_aeq_t, k_t, k_i)
79
+ l_aeq_t + k_t + k_i
80
+ end
81
+
82
+ ##
83
+ # Evaluate compliance with noise limits as defined in Section 9
84
+ #
85
+ # @param l_r [Float] Assessment level (dB)
86
+ # @param noise_limit [Float] Noise limit value (dB)
87
+ # @param measurement_uncertainty [Float] Measurement uncertainty (dB)
88
+ # @return [Boolean] True if limit is exceeded (L_r > L_lim + uncertainty)
89
+ #
90
+ def self.compliance_evaluation(l_r, noise_limit, measurement_uncertainty)
91
+ l_r > noise_limit + measurement_uncertainty
92
+ end
93
+
94
+ ##
95
+ # Convert sound levels between time periods as defined in Annex A
96
+ #
97
+ # L_total = 10 * log10( [Σ(t_i * 10^(0.1*L_i)] / T_total ) dB
98
+ #
99
+ # @param period_levels [Array<Hash>] Array of hashes with :level (dB) and :duration (hours)
100
+ # @param total_period [Float] Total time period for normalization (hours)
101
+ # @return [Float] Equivalent sound level for total period in dB
102
+ #
103
+ # Example:
104
+ # periods = [
105
+ # {level: 65.0, duration: 16}, # Day
106
+ # {level: 55.0, duration: 8} # Night
107
+ # ]
108
+ # NoiseLimits.time_period_conversion(periods) # => ~62.1 dB
109
+ #
110
+ def self.time_period_conversion(period_levels, total_period: Constants::STANDARD_24H_PERIOD)
111
+ energy_sum = period_levels.sum { |period| period[:duration] * (10 ** (period[:level] / 10.0)) }
112
+ 10 * Math.log10(energy_sum / total_period)
113
+ end
114
+ end
115
+ end
116
+ end
data/lib/iso_1996.rb CHANGED
@@ -1,17 +1,26 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'iso_1996/withdrawn/part_1_2003'
4
+ require_relative 'iso_1996/withdrawn/part_2_2007'
5
+ require_relative 'iso_1996/withdrawn/part_3_1987'
6
+ require_relative 'iso_1996/part_1_2016'
7
+ require_relative 'iso_1996/part_2_2017'
8
+
3
9
  ##
4
10
  # = ISO 1996 - Acoustics - Description, measurement and assessment of environmental noise
5
11
  #
6
12
  # Ruby implementation of ISO 1996 - Acoustics - Description, measurement and assessment of environmental noise.
7
- # Includes all three parts:
13
+ # Includes current parts:
14
+ # - ISO 1996-1:2016: Basic quantities and assessment procedures
15
+ # - ISO 1996-2:2017: Determination of sound pressure levels
16
+ # As well as some legacy norms:
8
17
  # - ISO 1996-1:2003: Basic quantities and assessment procedures
9
18
  # - ISO 1996-2:2007: Determination of sound pressure levels
10
19
  # - ISO 1996-3:1987: Application to noise limits
11
20
  #
12
21
  # Author:: Maciej Ciemborowicz
13
22
  # Date:: July 11, 2025
14
- # Version:: 1.1.0
23
+ # Version:: 2.0.1
15
24
  # License:: MIT
16
25
  #
17
26
  # == Usage
@@ -26,16 +35,15 @@
26
35
  # # Noise limits assessment
27
36
  # assessment = ISO_1996::NoiseLimits.assessment_level(65, 2, 3)
28
37
  #
38
+ # @see https://www.iso.org/standard/59765.html ISO 1996-1:2016
39
+ # @see https://www.iso.org/standard/59766.html ISO 1996-2:2017
40
+ #
29
41
  # @see https://www.iso.org/standard/28633.html ISO 1996-1:2003
30
42
  # @see https://www.iso.org/standard/23776.html ISO 1996-2:2007
31
43
  # @see https://www.iso.org/standard/6750.html ISO 1996-3:1987
32
-
33
- require_relative 'iso_1996/basic'
34
- require_relative 'iso_1996/environmental_noise'
35
- require_relative 'iso_1996/noise_limits'
36
-
44
+ #
37
45
  module ISO_1996
38
46
  ##
39
47
  # Current version of the gem
40
- VERSION = "1.1.0"
48
+ VERSION = "2.0.1"
41
49
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: iso_1996
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 2.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Maciej Ciemborowicz
@@ -66,9 +66,11 @@ extra_rdoc_files: []
66
66
  files:
67
67
  - README.md
68
68
  - lib/iso_1996.rb
69
- - lib/iso_1996/basic.rb
70
- - lib/iso_1996/environmental_noise.rb
71
- - lib/iso_1996/noise_limits.rb
69
+ - lib/iso_1996/part_1_2016.rb
70
+ - lib/iso_1996/part_2_2017.rb
71
+ - lib/iso_1996/withdrawn/part_1_2003.rb
72
+ - lib/iso_1996/withdrawn/part_2_2007.rb
73
+ - lib/iso_1996/withdrawn/part_3_1987.rb
72
74
  homepage: https://github.com/ciembor/iso_1996
73
75
  licenses:
74
76
  - MIT
@@ -1,105 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ISO_1996
4
-
5
- ##
6
- # == ISO 1996-1:2003 Basic Quantities and Assessment Procedures
7
- #
8
- # Module implementing calculations defined in ISO 1996-1:2003:
9
- # "Acoustics - Description, measurement and assessment of environmental noise -
10
- # Part 1: Basic quantities and assessment procedures"
11
- #
12
- # Author:: Maciej Ciemborowicz
13
- # Date:: July 11, 2025
14
- #
15
- module Basic
16
- ##
17
- # Constants defined in ISO 1996-1:2003 standard
18
- module Constants
19
- ##
20
- # Reference sound pressure (p₀) as defined in Section 3.1
21
- # Value: 20 μPa (20e-6 Pa)
22
- REFERENCE_SOUND_PRESSURE = 20e-6 # Pa
23
-
24
- ##
25
- # Reference time (t₀) for sound exposure level as defined in Section 3.9
26
- # Value: 1 second
27
- REFERENCE_TIME = 1.0 # s
28
- end
29
- include Constants
30
-
31
- ##
32
- # Calculate sound pressure level (L_p) as defined in Section 3.2
33
- #
34
- # L_p = 10 * log10(p² / p₀²) dB
35
- #
36
- # @param p [Float] Root-mean-square sound pressure (Pa)
37
- # @return [Float] Sound pressure level in dB
38
- #
39
- # Example:
40
- # Basic.sound_pressure_level(0.1) # => 74.0 dB
41
- #
42
- def self.sound_pressure_level(p)
43
- 10 * Math.log10((p ** 2) / (Constants::REFERENCE_SOUND_PRESSURE ** 2))
44
- end
45
-
46
- ##
47
- # Calculate A-weighted sound pressure level (L_A) as defined in Section 3.2
48
- #
49
- # L_A = 10 * log10( (1/T) * ∫(p_A²(t)/p₀²) dt ) dB
50
- #
51
- # @param p_a [Float] A-weighted sound pressure (Pa)
52
- # @param measurement_time [Float] Measurement time interval (seconds)
53
- # @return [Float] A-weighted sound pressure level in dB
54
- #
55
- def self.a_weighted_sound_pressure_level(p_a, measurement_time: 1.0)
56
- 10 * Math.log10((1.0 / measurement_time) * (p_a ** 2) / (Constants::REFERENCE_SOUND_PRESSURE ** 2))
57
- end
58
-
59
- ##
60
- # Calculate sound exposure level (L_AE) as defined in Section 3.9
61
- #
62
- # L_AE = 10 * log10( (1/t₀) * ∫(p_A²(t)/p₀²) dt ) dB
63
- #
64
- # @param p_a [Float] A-weighted sound pressure (Pa)
65
- # @return [Float] Sound exposure level in dB
66
- #
67
- def self.sound_exposure_level(p_a)
68
- 10 * Math.log10((1.0 / Constants::REFERENCE_TIME) * (p_a ** 2) / (Constants::REFERENCE_SOUND_PRESSURE ** 2))
69
- end
70
-
71
- ##
72
- # Calculate equivalent continuous sound level (L_Aeq,T) as defined in Section 3.7
73
- #
74
- # L_Aeq,T = 10 * log10( (1/T) * Σ(10^(0.1*L_i)) ) dB
75
- #
76
- # @param levels [Array<Float>] Array of sound pressure levels (dB)
77
- # @param measurement_time [Float] Total measurement time (seconds)
78
- # @return [Float] Equivalent continuous sound level in dB
79
- #
80
- # Example:
81
- # levels = [65.0, 67.0, 63.0]
82
- # Basic.equivalent_continuous_sound_level(levels, 3.0) # => ~65.1 dB
83
- #
84
- def self.equivalent_continuous_sound_level(levels, measurement_time)
85
- raise ArgumentError, "Measurement time must be positive" if measurement_time <= 0
86
-
87
- return -Float::INFINITY if levels.empty?
88
-
89
- energy_sum = levels.sum { |l| 10 ** (l / 10.0) }
90
- 10 * Math.log10(energy_sum / measurement_time)
91
- end
92
-
93
- ##
94
- # Calculate C-weighted peak sound pressure level (L_Cpeak) as defined in Section 3.10
95
- #
96
- # L_Cpeak = 20 * log10(p_Cmax / p₀) dB
97
- #
98
- # @param p_c_max [Float] Maximum C-weighted sound pressure (Pa)
99
- # @return [Float] C-weighted peak sound pressure level in dB
100
- #
101
- def self.peak_sound_pressure_level(p_c_max)
102
- 20 * Math.log10(p_c_max / Constants::REFERENCE_SOUND_PRESSURE)
103
- end
104
- end
105
- end
@@ -1,114 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ISO_1996
4
-
5
- ##
6
- # == ISO 1996-3:1987 Application to Noise Limits
7
- #
8
- # Module implementing calculations defined in ISO 1996-3:1987:
9
- # "Acoustics - Description, measurement and assessment of environmental noise -
10
- # Part 3: Application to noise limits"
11
- #
12
- # Author:: Maciej Ciemborowicz
13
- # Date:: July 11, 2025
14
- #
15
- module NoiseLimits
16
- ##
17
- # Constants defined in ISO 1996-3:1987 standard
18
- module Constants
19
- ##
20
- # Impulse correction threshold (L_Cpeak,min) as defined in Section 7.2
21
- # Value: 130 dB
22
- IMPULSE_CORRECTION_THRESHOLD = 130.0 # dB
23
-
24
- ##
25
- # Standard 24-hour period as defined in Annex A
26
- # Value: 24 hours
27
- STANDARD_24H_PERIOD = 24.0 # hours
28
- end
29
- include Constants
30
-
31
- ##
32
- # Determine tonal adjustment factor (K_T) as defined in Section 6 and Table 1
33
- #
34
- # @param delta_l [Float] Difference between tone level and background level (dB)
35
- # @return [Float] Tonal adjustment factor in dB
36
- #
37
- # According to Table 1:
38
- # ΔL ≥ 15 dB → 6 dB
39
- # 10 ≤ ΔL ≤ 14 dB → 4-6 dB (default 5 dB)
40
- # 5 ≤ ΔL ≤ 9 dB → 0-3 dB (default 2 dB)
41
- # ΔL < 5 dB → 0 dB
42
- #
43
- def self.tonal_adjustment_factor(delta_l)
44
- case delta_l
45
- when 15..Float::INFINITY then 6.0
46
- when 10..14 then 5.0
47
- when 5..9 then 2.0
48
- else 0.0
49
- end
50
- end
51
-
52
- ##
53
- # Determine impulsive adjustment factor (K_I) as defined in Section 7.2
54
- #
55
- # @param l_cpeak [Float] C-weighted peak sound pressure level (dB)
56
- # @param is_highly_annoying [Boolean] Whether noise is subjectively assessed as highly annoying
57
- # @return [Float] Impulsive adjustment factor in dB
58
- #
59
- # According to version:
60
- # K_I = 6 dB if L_Cpeak ≥ 130 dB OR noise is highly annoying
61
- # K_I = 0 dB otherwise
62
- #
63
- def self.impulsive_adjustment_factor(l_cpeak, is_highly_annoying: false)
64
- (l_cpeak >= Constants::IMPULSE_CORRECTION_THRESHOLD || is_highly_annoying) ? 6.0 : 0.0
65
- end
66
-
67
- ##
68
- # Calculate assessment level (L_r) as defined in Section 8
69
- #
70
- # L_r = L_AeqT + K_T + K_I dB
71
- #
72
- # @param l_aeq_t [Float] Equivalent continuous A-weighted sound pressure level (dB)
73
- # @param k_t [Float] Tonal adjustment factor (dB)
74
- # @param k_i [Float] Impulsive adjustment factor (dB)
75
- # @return [Float] Assessment level in dB
76
- #
77
- def self.assessment_level(l_aeq_t, k_t, k_i)
78
- l_aeq_t + k_t + k_i
79
- end
80
-
81
- ##
82
- # Evaluate compliance with noise limits as defined in Section 9
83
- #
84
- # @param l_r [Float] Assessment level (dB)
85
- # @param noise_limit [Float] Noise limit value (dB)
86
- # @param measurement_uncertainty [Float] Measurement uncertainty (dB)
87
- # @return [Boolean] True if limit is exceeded (L_r > L_lim + uncertainty)
88
- #
89
- def self.compliance_evaluation(l_r, noise_limit, measurement_uncertainty)
90
- l_r > noise_limit + measurement_uncertainty
91
- end
92
-
93
- ##
94
- # Convert sound levels between time periods as defined in Annex A
95
- #
96
- # L_total = 10 * log10( [Σ(t_i * 10^(0.1*L_i)] / T_total ) dB
97
- #
98
- # @param period_levels [Array<Hash>] Array of hashes with :level (dB) and :duration (hours)
99
- # @param total_period [Float] Total time period for normalization (hours)
100
- # @return [Float] Equivalent sound level for total period in dB
101
- #
102
- # Example:
103
- # periods = [
104
- # {level: 65.0, duration: 16}, # Day
105
- # {level: 55.0, duration: 8} # Night
106
- # ]
107
- # NoiseLimits.time_period_conversion(periods) # => ~62.1 dB
108
- #
109
- def self.time_period_conversion(period_levels, total_period: Constants::STANDARD_24H_PERIOD)
110
- energy_sum = period_levels.sum { |period| period[:duration] * (10 ** (period[:level] / 10.0)) }
111
- 10 * Math.log10(energy_sum / total_period)
112
- end
113
- end
114
- end