From 1390b13132b84c1f1eb857649d7e7ad7fb8b310e Mon Sep 17 00:00:00 2001 From: Stefano Belletti <stefano.belletti@skywarder.eu> Date: Fri, 17 Jan 2025 18:14:31 +0100 Subject: [PATCH] Final implementation of colored noise --- .../Functions/NoiseAnalysis_colored.m | 6 +-- .../Functions/NoiseAnalysis_pink.m | 6 +-- .../NoiseAnalysis/main_noiseAnalysis.m | 2 +- commonFunctions/sensors/Sensor1D.m | 19 ++++++--- commonFunctions/sensors/Sensor3D.m | 37 ++++++++++++++---- .../Lyra_Port_sensor_vect_res.mat | Bin 4452 -> 4423 bytes 6 files changed, 50 insertions(+), 20 deletions(-) diff --git a/commonFunctions/sensors/NoiseAnalysis/Functions/NoiseAnalysis_colored.m b/commonFunctions/sensors/NoiseAnalysis/Functions/NoiseAnalysis_colored.m index d525ae5..f285150 100644 --- a/commonFunctions/sensors/NoiseAnalysis/Functions/NoiseAnalysis_colored.m +++ b/commonFunctions/sensors/NoiseAnalysis/Functions/NoiseAnalysis_colored.m @@ -87,15 +87,15 @@ if ~plot_val end switch track case 2 - sensor_vect(sensor_number).track1.white_variance = white_variance*sensor_vect(sensor_number).factor; + sensor_vect(sensor_number).track1.white_variance = white_variance; sensor_vect(sensor_number).track1.fcut = fcut; sensor_vect(sensor_number).track1.butterOrder = butterOrder; case 3 - sensor_vect(sensor_number).track2.white_variance = white_variance*sensor_vect(sensor_number).factor; + sensor_vect(sensor_number).track2.white_variance = white_variance; sensor_vect(sensor_number).track2.fcut = fcut; sensor_vect(sensor_number).track2.butterOrder = butterOrder; case 4 - sensor_vect(sensor_number).track3.white_variance = white_variance*sensor_vect(sensor_number).factor; + sensor_vect(sensor_number).track3.white_variance = white_variance; sensor_vect(sensor_number).track3.fcut = fcut; sensor_vect(sensor_number).track3.butterOrder = butterOrder; end diff --git a/commonFunctions/sensors/NoiseAnalysis/Functions/NoiseAnalysis_pink.m b/commonFunctions/sensors/NoiseAnalysis/Functions/NoiseAnalysis_pink.m index 9daf7a7..16acacc 100644 --- a/commonFunctions/sensors/NoiseAnalysis/Functions/NoiseAnalysis_pink.m +++ b/commonFunctions/sensors/NoiseAnalysis/Functions/NoiseAnalysis_pink.m @@ -139,15 +139,15 @@ if ~plot_val case 2 sensor_vect(sensor_number).track1.peaks_vect_f = peaks_vect_f; sensor_vect(sensor_number).track1.peaks_vect_val = factor*peaks_vect_val; - sensor_vect(sensor_number).track1.variance = white_variance*sensor_vect(sensor_number).factor; + sensor_vect(sensor_number).track1.variance = white_variance; case 3 sensor_vect(sensor_number).track2.peaks_vect_f = peaks_vect_f; sensor_vect(sensor_number).track2.peaks_vect_val = factor*peaks_vect_val; - sensor_vect(sensor_number).track2.variance = white_variance*sensor_vect(sensor_number).factor; + sensor_vect(sensor_number).track2.variance = white_variance; case 4 sensor_vect(sensor_number).track3.peaks_vect_f = peaks_vect_f; sensor_vect(sensor_number).track3.peaks_vect_val = factor*peaks_vect_val; - sensor_vect(sensor_number).track3.variance = white_variance*sensor_vect(sensor_number).factor; + sensor_vect(sensor_number).track3.variance = white_variance; end end diff --git a/commonFunctions/sensors/NoiseAnalysis/main_noiseAnalysis.m b/commonFunctions/sensors/NoiseAnalysis/main_noiseAnalysis.m index 87c4405..b0cecd7 100644 --- a/commonFunctions/sensors/NoiseAnalysis/main_noiseAnalysis.m +++ b/commonFunctions/sensors/NoiseAnalysis/main_noiseAnalysis.m @@ -5,7 +5,7 @@ restoredefaultpath %% Path -matFolder = ""; +matFolder = "C:\Users\stebe\Desktop\New data"; addpath(genpath("./Functions")) addpath(genpath(matFolder)) diff --git a/commonFunctions/sensors/Sensor1D.m b/commonFunctions/sensors/Sensor1D.m index 8c7f226..dd2fcc5 100644 --- a/commonFunctions/sensors/Sensor1D.m +++ b/commonFunctions/sensors/Sensor1D.m @@ -21,6 +21,7 @@ classdef Sensor1D < handle noiseDataTrack1; noiseFactor; colored_opts; + noiseVariance; % Defining gaussian white noise % offset @@ -100,11 +101,13 @@ classdef Sensor1D < handle error("Sensor not defined") end - if strcmp(obj.noiseType, "colored") + if strcmp(obj.noiseType, "colored") || strcmp(obj.noiseType, "pink") obj.colored_opts.white_variance = vect(ii).colored_data.white_variance; obj.colored_opts.fcut = vect(ii).colored_data.fcut; obj.colored_opts.butterOrder = vect(ii).colored_data.butterOrder; - obj.colored_opts.filterStatus = 0; + obj.colored_opts.filterStatus1 = 0; + obj.colored_opts.filterStatus2 = 0; + obj.colored_opts.filterStatus3 = 0; end else if strcmp("Sensor1D", class(obj)) || strcmp("SensorFault", class(obj)) @@ -156,11 +159,15 @@ classdef Sensor1D < handle for ii = 1:length(obj.noiseDataTrack1.peaks_vect_f) inputArg = inputArg + obj.noiseDataTrack1.peaks_vect_val(ii)*obj.noiseFactor*sin(2*pi*obj.noiseDataTrack1.peaks_vect_f(ii)*t + randn(1)); end - inputArg = inputArg + sqrt(obj.noiseDataTrack1.variance*obj.noiseFactor).*randn(length(inputArg),1); - elseif strcmp(obj.noiseType, "colored") - inputArg = inputArg + sqrt(obj.colored_opts.white_variance*obj.noiseFactor).*randn(length(inputArg),1); + % Colored noise + white_noise = sqrt(obj.noiseDataTrack1.variance*obj.noiseFactor).*randn(length(inputArg),1); [b, a] = butter(obj.colored_opts.butterOrder, obj.colored_opts.fcut, 'low'); - [inputArg, obj.colored_opts.filterStatus] = filter(b, a, inputArg, obj.colored_opts.filterStatus); + [colored_noise, obj.colored_opts.filterStatus1] = filter(b, a, white_noise, obj.colored_opts.filterStatus1); + inputArg = inputArg + colored_noise; + elseif strcmp(obj.noiseType, "colored") + inputArg = inputArg + sqrt(obj.noiseDataTrack1.white_variance*obj.noiseFactor).*randn(length(inputArg),1); + [b, a] = butter(obj.noiseDataTrack1.butterOrder, obj.noiseDataTrack1.fcut, 'low'); + [inputArg, obj.colored_opts.filterStatus1] = filter(b, a, inputArg, obj.colored_opts.filterStatus1); else error("This noise is not defined") end diff --git a/commonFunctions/sensors/Sensor3D.m b/commonFunctions/sensors/Sensor3D.m index bada245..5126456 100644 --- a/commonFunctions/sensors/Sensor3D.m +++ b/commonFunctions/sensors/Sensor3D.m @@ -135,15 +135,38 @@ classdef Sensor3D < Sensor1D inputArg3 = inputArg3 + sqrt(obj.noiseDataTrack3*obj.noiseFactor).*randn(length(inputArg3),1); elseif strcmp(obj.noiseType, "pink") for ii = 1:length(obj.noiseDataTrack1.peaks_vect_f) - inputArg1 = inputArg1 + obj.noiseDataTrack1.peaks_vect_val(ii)*obj.noiseFactor*sin(2*pi*obj.noiseDataTrack1.peaks_vect_f(ii)*t + randn(1)); - inputArg2 = inputArg2 + obj.noiseDataTrack2.peaks_vect_val(ii)*obj.noiseFactor*sin(2*pi*obj.noiseDataTrack2.peaks_vect_f(ii)*t + randn(1)); - inputArg3 = inputArg3 + obj.noiseDataTrack3.peaks_vect_val(ii)*obj.noiseFactor*sin(2*pi*obj.noiseDataTrack3.peaks_vect_f(ii)*t + randn(1)); + inputArg1 = inputArg1 + obj.noiseDataTrack1.peaks_vect_val(ii)*sin(2*pi*obj.noiseDataTrack1.peaks_vect_f(ii)*t + randn(1)); + inputArg2 = inputArg2 + obj.noiseDataTrack2.peaks_vect_val(ii)*sin(2*pi*obj.noiseDataTrack2.peaks_vect_f(ii)*t + randn(1)); + inputArg3 = inputArg3 + obj.noiseDataTrack3.peaks_vect_val(ii)*sin(2*pi*obj.noiseDataTrack3.peaks_vect_f(ii)*t + randn(1)); end - inputArg1 = inputArg1 + sqrt(obj.noiseDataTrack1.variance*obj.noiseFactor).*randn(length(inputArg1),1); - inputArg2 = inputArg2 + sqrt(obj.noiseDataTrack2.variance*obj.noiseFactor).*randn(length(inputArg2),1); - inputArg3 = inputArg3 + sqrt(obj.noiseDataTrack3.variance*obj.noiseFactor).*randn(length(inputArg3),1); + % Colored noise + white_noise1 = sqrt(obj.noiseDataTrack1.variance*obj.noiseFactor).*randn(1,1); + [b1, a1] = butter(obj.noiseDataTrack1.butterOrder, obj.noiseDataTrack1.fcut, 'low'); + [colored_noise1, obj.colored_opts.filterStatus1] = filter(b1, a1, white_noise1, obj.colored_opts.filterStatus1); + white_noise2 = sqrt(obj.noiseDataTrack2.variance*obj.noiseFactor).*randn(1,1); + [b2, a2] = butter(obj.noiseDataTrack2.butterOrder, obj.noiseDataTrack2.fcut, 'low'); + [colored_noise2, obj.colored_opts.filterStatus2] = filter(b2, a2, white_noise2, obj.colored_opts.filterStatus2); + white_noise3 = sqrt(obj.noiseDataTrack3.variance*obj.noiseFactor).*randn(1,1); + [b3, a3] = butter(obj.noiseDataTrack3.butterOrder, obj.noiseDataTrack3.fcut, 'low'); + [colored_noise3, obj.colored_opts.filterStatus3] = filter(b3, a3, white_noise3, obj.colored_opts.filterStatus3); + + inputArg1 = inputArg1 + colored_noise1; + inputArg2 = inputArg2 + colored_noise2; + inputArg3 = inputArg3 + colored_noise3; elseif strcmp(obj.noiseType, "colored") - error("Yet to be implemented") + white_noise1 = sqrt(obj.noiseDataTrack1.variance*obj.noiseFactor).*randn(1,1); + [b1, a1] = butter(obj.noiseDataTrack1.butterOrder, obj.noiseDataTrack1.fcut, 'low'); + [colored_noise1, obj.colored_opts.filterStatus1] = filter(b1, a1, white_noise1, obj.colored_opts.filterStatus1); + white_noise2 = sqrt(obj.noiseDataTrack2.variance*obj.noiseFactor).*randn(1,1); + [b2, a2] = butter(obj.noiseDataTrack2.butterOrder, obj.noiseDataTrack2.fcut, 'low'); + [colored_noise2, obj.colored_opts.filterStatus2] = filter(b2, a2, white_noise2, obj.colored_opts.filterStatus2); + white_noise3 = sqrt(obj.noiseDataTrack3.variance*obj.noiseFactor).*randn(1,1); + [b3, a3] = butter(obj.noiseDataTrack3.butterOrder, obj.noiseDataTrack3.fcut, 'low'); + [colored_noise3, obj.colored_opts.filterStatus3] = filter(b3, a3, white_noise3, obj.colored_opts.filterStatus3); + + inputArg1 = inputArg1 + colored_noise1; + inputArg2 = inputArg2 + colored_noise2; + inputArg3 = inputArg3 + colored_noise3; else error("This noise is not defined") end diff --git a/data/2024_Lyra_Portugal_October/Lyra_Port_sensor_vect_res.mat b/data/2024_Lyra_Portugal_October/Lyra_Port_sensor_vect_res.mat index 2c3b98ad9def8becb8ced991dfe7f38b560308d0..0b806e6b498b7f8c1b7a91c4cc43ae2c1a5c0a80 100644 GIT binary patch delta 3455 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 J=HI-T%mDm&*AxH% delta 3484 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 -- GitLab