itaMaterial.m 12.7 KB
Newer Older
Philipp Marco Schäfer's avatar
Philipp Marco Schäfer committed
1
classdef itaMaterial < itaSimulationInputItem
Philipp Marco Schäfer's avatar
Philipp Marco Schäfer committed
2 3 4
    %itaMaterial represents a material and its acoustic properties which are
    %used for GA-based and wave-based simulations
    %   Properties:
Philipp Marco Schäfer's avatar
Philipp Marco Schäfer committed
5
    %   Impedance, absorption, scattering
6 7 8 9 10
    %   
    %   See also itaSimulationInputItem, ImpedanceType, ScatteringType
    %   
    %   Reference page in Help browser
    %       <a href="matlab:doc itaMaterial">doc itaMaterial</a>
11
    
Philipp Marco Schäfer's avatar
Philipp Marco Schäfer committed
12 13 14 15
    % <ITA-Toolbox>
    % This file is part of the ITA-Toolbox. Some rights reserved.
    % You can find the license for this m-file in the license.txt file in the ITA-Toolbox folder.
    % </ITA-Toolbox>
16 17
    
    properties(Access = private, Hidden = true)
Philipp Marco Schäfer's avatar
Philipp Marco Schäfer committed
18 19 20 21
        mImpedanceType = ImpedanceType.UserDefined;
        mAbsorptionType = ImpedanceType.UserDefined;
        mScatteringType = ScatteringType.UserDefined;
        
Philipp Marco Schäfer's avatar
Philipp Marco Schäfer committed
22 23 24
        mImpedance;
        mAbsorption;
        mScattering;
25 26
    end
    
Philipp Marco Schäfer's avatar
Philipp Marco Schäfer committed
27 28 29
    properties
        rho0Air = [];    %The density of air which occured while measuring the data
        cAir = [];       %The speed of sound in air which was present while measuring the data
30 31 32
    end
    
    properties(Dependent = true)
Philipp Marco Schäfer's avatar
Philipp Marco Schäfer committed
33 34 35 36
        impedanceType;          %ImpedanceType
        absorptionType;         %ImpedanceType
        scatteringType;         %ScatteringType
        
37 38 39
        impedance;              %Impedance Z - itaSuper
        absorption;             %Absorption coefficient alpha - itaSuper
        scattering;             %Scattering coefficient s - itaSuper
Philipp Marco Schäfer's avatar
Philipp Marco Schäfer committed
40 41
    end
    properties(Dependent = true, SetAccess = private)
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
        absorptionFromImpedance;%Same as absorption but computed from impedance (get only)
    end
    
    %% Constructor
    methods
        function obj = itaMaterial(varargin)
            if nargin == 0
                return;
            end
            
            if nargin == 1
                copyObj = varargin{1};
                if ~isa(copyObj, 'itaMaterial')
                    error('Input for copy constructor must be an object of same class.')
                end
                
                obj.mImpedance = copyObj.impedance;
                obj.mAbsorption = copyObj.absorption;
                obj.mScattering = copyObj.scattering;
                obj.rho0Air = copyObj.rho0Air;
                obj.cAir = copyObj.cAir;
            end
        end
    end
    
Philipp Marco Schäfer's avatar
Philipp Marco Schäfer committed
67 68 69 70 71 72 73 74 75 76 77 78 79 80
    methods(Static = true)
        function obj = SoundHard()
            obj = itaMaterial;
            obj.mImpedanceType = ImpedanceType.SoundHard;
            obj.mAbsorptionType = ImpedanceType.SoundHard;
        end
        function obj = SoundHardNoScattering()
            obj = itaMaterial;
            obj.mImpedanceType = ImpedanceType.SoundHard;
            obj.mAbsorptionType = ImpedanceType.SoundHard;
            obj.mScatteringType = ScatteringType.Zero;
        end
    end
    
81 82
    %% Set functions
    methods
Philipp Marco Schäfer's avatar
Philipp Marco Schäfer committed
83 84 85 86 87 88 89 90 91 92 93 94 95
        function this = set.impedanceType(this, impedanceType)
            assert(isa(impedanceType, 'ImpedanceType'), 'The impedanceType property must be an object of the class ImpedanceType.')
            this.mImpedanceType = impedanceType;
        end
        function this = set.absorptionType(this, absorptionType)
            assert(isa(absorptionType, 'ImpedanceType'), 'The absorptionType property must be an object of the class ImpedanceType.')
            this.mAbsorptionType = absorptionType;
        end
        function this = set.scatteringType(this, scatterType)
            assert(isa(scatterType, 'ScatteringType'), 'The scatteringType property must be an object of the class ImpedanceType.')
            this.mScatteringType = scatterType;
        end
        
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
        function this = set.impedance(this, impedance)
            if isnumeric(impedance) && isempty(impedance)
                this.mImpedance = [];
                return;
            end
            
            this.checkDataTypeForFreqData(impedance);
            this.mImpedance = impedance;
        end
        
        function this = set.absorption(this, alpha)
            if isnumeric(alpha) && isempty(alpha)
                this.mAbsorption = [];
                return;
            end
            
            this.checkDataTypeForFreqData(alpha);
            this.mAbsorption = alpha;
        end
        function this = set.scattering(this, scattering)
            if isnumeric(scattering) && isempty(scattering)
                this.mScattering = [];
                return;
            end
            
            this.checkDataTypeForFreqData(scattering)
            this.mScattering = scattering;
        end
        
        function this = set.rho0Air(this, rho0)
            if isnumeric(rho0) && isempty(rho0)
                this.rho0Air = [];
                return;
            end
            
            if ~isnumeric(rho0) || ~isscalar(rho0) || rho0 < 0
                error('Density must be a positive numeric scalar')
            end
            this.rho0Air = rho0;
        end
        function this = set.cAir(this, c)
            if isnumeric(c) && isempty(c)
                this.cAir = [];
                return;
            end
Philipp Marco Schäfer's avatar
Philipp Marco Schäfer committed
141
            assert(isnumeric(c) && isscalar(c) && c > 0, 'Speed of sound must be a positive numeric scalar')
142 143
            this.cAir = c;
        end
Philipp Marco Schäfer's avatar
Philipp Marco Schäfer committed
144
    end
145 146 147
    
    %% Get functions
    methods
Philipp Marco Schäfer's avatar
Philipp Marco Schäfer committed
148 149 150 151 152 153 154 155 156 157
        function impedanceType = get.impedanceType(this)
            impedanceType = this.mImpedanceType;
        end
        function absorptionType = get.absorptionType(this)
            absorptionType = this.mAbsorptionType;
        end
        function scatteringType = get.scatteringType(this)
            scatteringType = this.mScatteringType;
        end
        
Philipp Marco Schäfer's avatar
Philipp Marco Schäfer committed
158
        function alpha = get.absorption(this)
Philipp Marco Schäfer's avatar
Philipp Marco Schäfer committed
159 160 161 162 163 164 165
            switch this.mAbsorptionType
                case ImpedanceType.SoundHard
                    freqData = zeros( size(this.gaThirdOctavefreqs) );
                    alpha = itaResult(freqData, this.gaThirdOctavefreqs, 'freq');
                otherwise
                    alpha = this.mAbsorption;
            end
166 167 168 169 170 171 172 173 174 175 176 177 178
        end
        
        function alpha = get.absorptionFromImpedance(this)
            %Tries to convert the impedance to an absorption. Throws an
            %error if impedance is not defined or impedance of air is not
            %specified.
            if ~this.HasImpedance
                error('No impedance data defined yet')
            end
            if ~this.mediumImpedanceDefined
                error('Cannot convert without knowledge density and speed of sound of air. Set rho0Air and cAir first.');
            end
            
Philipp Marco Schäfer's avatar
Philipp Marco Schäfer committed
179
            alpha = this.convertImpedanceToAbsorption();
180 181 182
        end
        
        function scattering = get.scattering(this)
Philipp Marco Schäfer's avatar
Philipp Marco Schäfer committed
183 184 185 186 187 188 189
            switch this.mScatteringType
                case  ScatteringType.Zero
                    freqData = zeros( size(this.gaThirdOctavefreqs) );
                    scattering = itaResult(freqData, this.gaThirdOctavefreqs, 'freq');
                otherwise
                    scattering = this.mScattering;
            end
190 191
        end
        
Philipp Marco Schäfer's avatar
Philipp Marco Schäfer committed
192
        function Z = get.impedance(this)
Philipp Marco Schäfer's avatar
Philipp Marco Schäfer committed
193 194 195 196 197 198
            switch this.mImpedanceType
                case ImpedanceType.SoundHard
                    Z = inf;
                otherwise
                    Z = this.mImpedance;
            end
199
        end
Philipp Marco Schäfer's avatar
Philipp Marco Schäfer committed
200
    end
201 202 203 204
    
    %% Booleans
    
    methods
Philipp Marco Schäfer's avatar
Philipp Marco Schäfer committed
205 206 207 208 209 210 211 212 213 214 215 216 217
        function bool = HasImpedanceType(this, impedanceType)
            assert(isa(impedanceType, 'ImpedanceType'), 'Input must be an ImpedanceType')
            bool = arrayfun(@(x) x.mImpedanceType == impedanceType, this);
        end
        function bool = HasAbsorptionType(this, impedanceType)
            assert(isa(impedanceType, 'ImpedanceType'), 'Input must be an ImpedanceType')
            bool = arrayfun(@(x) x.mAbsorptionType == impedanceType, this);
        end
        function bool = HasScatteringType(this, scatterType)
            assert(isa(scatterType, 'ScatteringType'), 'Input must be a ScatteringType')
            bool = arrayfun(@(x) x.mScatteringType == scatterType, this);
        end
        
218
        function bool = HasAbsorption(this)
Philipp Marco Schäfer's avatar
Philipp Marco Schäfer committed
219
            bool = arrayfun(@(x) ~isempty(x.mAbsorption), this) | this.absorptionDefinedByType();
220 221
        end
        function bool = HasScattering(this)
Philipp Marco Schäfer's avatar
Philipp Marco Schäfer committed
222
            bool = arrayfun(@(x) ~isempty(x.mScattering), this) | this.scatteringDefinedByType();
223 224
        end
        function bool = HasImpedance(this)
Philipp Marco Schäfer's avatar
Philipp Marco Schäfer committed
225
            bool = arrayfun(@(x) ~isempty(x.mImpedance), this) | this.impedanceDefinedByType();
226 227 228 229 230
        end
        
        function bool = HasGaData(this)
            %Returns true if all data which is used for Geometrical
            %Acoustics (GA) is available
Philipp Marco Schäfer's avatar
Philipp Marco Schäfer committed
231
            bool = this.HasAbsorption() & this.HasScattering();
232 233 234 235
        end
        function bool = HasWaveData(this)
            %Returns true if all data which is used for Wave-based
            %Acoustics is available
Philipp Marco Schäfer's avatar
Philipp Marco Schäfer committed
236
            bool = this.HasImpedance();
237 238
        end
    end
Philipp Marco Schäfer's avatar
Philipp Marco Schäfer committed
239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
    methods(Access = private)
        function bool = impedanceDefinedByType(this)
            bool = ~this.HasImpedanceType(ImpedanceType.UserDefined);
        end
        function bool = absorptionDefinedByType(this)
            bool = ~this.HasAbsorptionType(ImpedanceType.UserDefined);
        end
        function bool = scatteringDefinedByType(this)
            bool = ~this.HasScatteringType(ScatteringType.UserDefined);
        end
    end
    
    methods(Hidden = true)
        function bool = HasNonInfImpedance(this)
            bool = this.HasImpedance & ~this.HasImpedanceType(ImpedanceType.SoundHard);
        end
    end
256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294
    
    methods(Access = private)
        function bool = mediumImpedanceDefined(this)
            %Returns true if all parameters for the calculation of the
            %medium's impedance (usually air) are defined
            bool = ~isempty(this.rho0Air) && ~isempty(this.cAir);
        end
    end
    
    %% Public functions
    methods
        function obj = CrossfadeWaveAndGaData(this, crossfadeFreq)
            %Cross-fades the wave-based material properties with the
            %geometrical ones at a given frequency. Data is returned in as
            %a new object of this class.
            %
            %Still needs implementation...
            
            if ~this.HasGaData || ~this.HasWaveData
                error('Wave-based data and/or data for Geometrical Acoustics is not set.')
            end
            
            if ~this.mediumImpedanceDefined
                error('')
            end
            
            this.checkInputForValidFrequency(crossfadeFreq);
            
            obj = itaMaterial(this);
            
            
            %Do crossfade here
            %this.mImpedance & this.mAbsorption
            %What is with scattering?
        end
    end
    
    %% Conversions
    methods(Access = private)
Philipp Marco Schäfer's avatar
Philipp Marco Schäfer committed
295
        function alpha = convertImpedanceToAbsorption(this)
296 297 298 299 300
            %Converts the impedance of this object to an absorption
            %coefficient
            
            alpha = [];
            
Philipp Marco Schäfer's avatar
Philipp Marco Schäfer committed
301
            if ~this.HasImpedance()
302 303
                warning('No impedance data available, returning empty data')
                return
Philipp Marco Schäfer's avatar
Philipp Marco Schäfer committed
304 305 306
            elseif ~this.mediumImpedanceDefined()
                warning('No medium impedance (Z0) defined, returning empty data')
                return
307 308 309
            end
            
            Z0 = this.rho0Air * this.cAir;
Philipp Marco Schäfer's avatar
Philipp Marco Schäfer committed
310
            Z = this.impedance.freqData;
311 312 313 314 315 316 317
            abs_R = abs( (Z-Z0)./(Z+Z0) );
            
            alphaFreqData = 1 - abs_R.^2;
            
            alpha = itaResult(alphaFreqData, this.impedance.freqVector, 'freq');
        end
        
Philipp Marco Schäfer's avatar
Philipp Marco Schäfer committed
318 319 320 321 322 323 324
        function Z = convertAbsorptionToImpedance(this, h, T, p0)
            R = sqrt(1 - this.absorption.freqData);
            Z0 = ita_constants('z_0', 'medium', 'air', 'T', T, 'p', p0, 'phi', h/100);
            Z = Z0.value * (1+R)./(1-R);
            
            Z = itaResult(Z, this.absorption.freqVector, 'freq');
        end
325 326
    end
    
Philipp Marco Schäfer's avatar
Philipp Marco Schäfer committed
327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354
    %% Plot interface
    methods
        function varargout = plotImpedance(this, varargin)
            matVis = itaMaterialVisualizer(this);
            [fgh, ax] = matVis.plotImpedance(varargin{:});
            if nargout
                varargout{1} = fgh;
                varargout{2} = ax;
            end
        end
        function varargout = plotAbsorption(this, varargin)
            matVis = itaMaterialVisualizer(this);
            [fgh, ax] = matVis.plotAbsorption(varargin{:});
            if nargout
                varargout{1} = fgh;
                varargout{2} = ax;
            end
        end
        function varargout = plotScattering(this, varargin)
            matVis = itaMaterialVisualizer(this);
            [fgh, ax] = matVis.plotScattering(varargin{:});
            if nargout
                varargout{1} = fgh;
                varargout{2} = ax;
            end
        end
    end
    
355 356
end