Browse Source

修改了部分随机代码

l2736 3 months ago
parent
commit
b9cbd694a9

+ 10 - 0
Avalonia/ShakerApp/ViewModels/ShakerConfig/RandomConfigViewModel.cs

@@ -212,6 +212,7 @@ namespace ShakerApp.ViewModels
                 OnPropertyChanged(nameof(FFTLength));
             }
         }
+
         public bool CanResetRMS { get => canResetRMS; set =>SetProperty(ref canResetRMS, value); }
         public double ResetRMS { get => resetRMS; set =>SetProperty(ref resetRMS , value); }
         [PropertyAssociation(nameof(RandomConfigModel.SynthesisType))]
@@ -318,6 +319,15 @@ namespace ShakerApp.ViewModels
             if (items.Count < 3) return;
             CalcRefSpectrum(items.Select(x => x.Frequency).ToArray(), items.Select(x => x.Value).ToArray(), items.Select(x => x.ValueType).ToArray(), FrequencyResolution,out var fy_array,out var f,out var turningfreq);
             if (fy_array.Length == 0) return;
+            this.Model.SpectralTables.Clear();
+            for(int i=0;i<turningfreq.Length;i++)
+            {
+                this.Model.SpectralTables.Add(new RandomSpectralTableModel()
+                {
+                    TurningFrequency = turningfreq[i],
+                    TurningValue = fy_array[(int)((turningfreq[i]-SpectrumItems[0].Value.Frequency)/FrequencyResolution)],
+                });
+            }
             RMSAcceleration = Math.Sqrt(ViewModels.MainViewModel.Default.Calc.Sum.Sum(ref fy_array[0], (uint)fy_array.Length) * FrequencyResolution);
             ResetRMS = RMSAcceleration;
             double[] f2 = new double[f.Length];

+ 7 - 4
NIFPGA/WriteFifo.cs

@@ -71,10 +71,13 @@ namespace NIFPGA
             if (length == 0) return;
             ulong[] temp= new ulong[length];
             _Convert.FloatConvertToDxp(ref value, _TypeInfo, ref temp[0], (uint)length);
-            //for(int i=0;i<value.Length;i++)
-            //{
-            //    temp[i] = Interop.NiFpgaDll_ConvertFromDoubleToFxp(_TypeInfo, value[i]);
-            //}
+            base.Write(temp, timeout, ref emptyElementsRemaining);
+        }
+        public void Write(ref double value, uint timeout, uint length, ref uint emptyElementsRemaining)
+        {
+            if (length == 0) return;
+            ulong[] temp = new ulong[length];
+            _Convert.DoubleConvertToDxp(ref value, _TypeInfo, ref temp[0], (uint)length);
             base.Write(temp, timeout, ref emptyElementsRemaining);
         }
     }

+ 3 - 0
Shaker.Model/AnalogType.cs

@@ -27,6 +27,9 @@ namespace Shaker.Models
         [Description("ValveDriveGiven")]
         [AnalogUnit("mm")]
         GivenDriver,
+        [Description("Acceleration")]
+        [AnalogUnit("g")]
+        DivideAcceleration,
     }
     public sealed class AnalogUnitAttribute : Attribute
     {

+ 2 - 0
Shaker.Model/Models/RandomConfigModel.cs

@@ -1,5 +1,6 @@
 namespace Shaker.Models
 {
+
     public class RandomConfigModel : BaseModel
     {
         public readonly uint SpectrumItemsCount = 20;
@@ -15,6 +16,7 @@
         public RandomIdentifyModel Identify = new RandomIdentifyModel();
         public List<RandomSpectrumItemModel> SpectrumItems = new List<RandomSpectrumItemModel>();
         public List<RandomPlanItemModel> PlanItems = new List<RandomPlanItemModel>();
+        public List<RandomSpectralTableModel> SpectralTables = new List<RandomSpectralTableModel>();
         public uint StopLins = 160;
         public uint WarnLines = 120;
         public double StopRMS = 6;

+ 14 - 1
Shaker.Model/Models/RandomDataModel.cs

@@ -15,7 +15,7 @@ namespace Shaker.Models
         public double[] CurrentAccelerationSynthesisPSD = new double[0];
         public double[] TransferFunction = new double[0];
         public double[] DriverPSD = new double[0];
-
+        public RandomStatus RandomStatus = RandomStatus.Start;
         public RandomTestStep RandomTestStep = RandomTestStep.Start;
         public double CurrentTestLevel = 0;
         public double CurrentTestTime = 0;
@@ -25,4 +25,17 @@ namespace Shaker.Models
         }
         public MainPageType MainPageType = MainPageType.RandomPage;
     }
+    public enum RandomStatus
+    {
+        Start,
+        Up,
+        Identify,
+        Stop,
+        Test,
+        IdentifyMaxLevel,
+        IdentifyOpenLoop,
+        TestUpStop,
+        TestDownStop,
+        TestRMSError,
+    }
 }

+ 12 - 0
Shaker.Model/Models/RandomSpectralTableModel.cs

@@ -0,0 +1,12 @@
+namespace Shaker.Models
+{
+    public class RandomSpectralTableModel:BaseModel
+    {
+        public double TurningFrequency = 0;
+        public double TurningValue = 0;
+        public override object Clone()
+        {
+            return this.CloneBase();
+        }
+    }
+}

+ 1 - 0
Shaker.Model/Models/RandomTestStep.cs

@@ -9,6 +9,7 @@ namespace Shaker.Models
     public enum RandomTestStep
     {
         Start,
+        Up,
         Identify,
         Stop,
         Test,

+ 9 - 0
Shaker/Service.cs

@@ -1,4 +1,5 @@
 
+using MathNet.Numerics;
 using Shaker.Models;
 using Shaker.Models.Tools;
 using ShakerService.Tools;
@@ -21,6 +22,8 @@ namespace ShakerService
     {
         public Service()
         {
+            var val= Enumerable.Range(0, 10).Select(x => (double)x).ToArray();
+           var res =  Interpolate.Linear(val, val).Interpolate(9.5);
             var path = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
             DateTime dateTime = File.GetLastWriteTime(Assembly.GetEntryAssembly().Location);
             Log.Default.Debug($"程序生成时间:{dateTime:F}" );
@@ -46,6 +49,8 @@ namespace ShakerService
         private Haukcode.HighResolutionTimer.HighResolutionTimer UpAmpttimer = new Haukcode.HighResolutionTimer.HighResolutionTimer();
         private Haukcode.HighResolutionTimer.HighResolutionTimer ReadConfigTimer = new Haukcode.HighResolutionTimer.HighResolutionTimer();
         private AutoResetEvent WaitTriggerLock = new AutoResetEvent(false);
+        private AutoResetEvent WaitRandomDataLocker = new AutoResetEvent(false);
+        private Task RandomTask;
         private Thread UpAmpttimerThread;
         private Thread ReadConfigTimerThread;
         /// <summary>
@@ -134,6 +139,9 @@ namespace ShakerService
         {
             shakertokenSource = new CancellationTokenSource();
             maintokenSource = new CancellationTokenSource();
+            WaitRandomDataLocker.Reset();
+            RandomTask = new Task(() => RandomControl());
+            RandomTask.Wait();
             Task.Run(() => DeviceDiscovery());
 
             OilSource.OilSource.Default.Connect();
@@ -169,6 +177,7 @@ namespace ShakerService
             ReadConfigTimerThread.Start();
             Task.Run(() => MainTask(),maintokenSource.Token);
             Task.Run(() => OilSourceTask(), maintokenSource.Token);
+            
         }
 
         private async void OilSourceTask()

+ 2 - 2
Shaker/ShakerService.ReadConfig.cs

@@ -40,11 +40,11 @@ namespace ShakerService
             double ampt = 0;
             double[] real = ShakerFpga.Instance.AccelerationData.Value;
             double[] img = new double[real.Length];
-            Calc.FFT.FFT(real, img);
+            ServiceDataCacheViewModel.Instance.Calc.FFT.FFT(real, img);
             double fftacc = Math.Sqrt(real[1] * real[1] + img[1] * img[1])/(real.Length/2);
             real = ShakerFpga.Instance.DisplacementData.Value;
             img = new double[real.Length];
-            Calc.FFT.FFT(real, img);
+            ServiceDataCacheViewModel.Instance.Calc.FFT.FFT(real, img);
             double fftdisp =DispToAcc(Math.Sqrt(real[1] * real[1] + img[1] * img[1])/(real.Length/2),currentfreq);
             double identifyacc = ShakerFpga.Instance.IdentifyAcceleration.Value;
             double identifydisp = DispToAcc(ShakerFpga.Instance.IdentifyDisplacement.Value,currentfreq);

+ 57 - 1
Shaker/ShakerService.ReadFifo.cs

@@ -1,5 +1,6 @@
 using NIFPGA;
 using NIFPGA.lvbitx;
+using Shaker.Models;
 using ShakerService.ViewModel;
 using System;
 using System.Collections.Generic;
@@ -22,13 +23,68 @@ namespace ShakerService
                     for (int i = 0; i <ServiceShakerConfigViewModel.Instance.ChannelCount; i++)
                     {
                         if (ServiceDataCacheViewModel.Instance.SensitivityValues[i] == 1) continue;
-                        Calc.Multiply.Multiply(ref ServiceDataCacheViewModel.Instance.DataCache[i, 0], ServiceDataCacheViewModel.Instance.SensitivityValues[i], ServiceShakerConfigViewModel.Instance.SampleRate);
+                        ServiceDataCacheViewModel.Instance.Calc.Multiply.Multiply(ref ServiceDataCacheViewModel.Instance.DataCache[i, 0], ServiceDataCacheViewModel.Instance.SensitivityValues[i], ServiceShakerConfigViewModel.Instance.SampleRate);
                     }
                     SendData(ServiceDataCacheViewModel.Instance.DataCache);
+                    if(ShakerFpga.Instance.TestType.Value == (ushort)TestType.Random) WaitRandomDataLocker.Set();
                     break;
                 }
                 Task.Delay(10).Wait() ;
             }
         }
+        private void RandomControl()
+        {
+            while(!shakertokenSource.IsCancellationRequested && !maintokenSource.IsCancellationRequested)
+            {
+                if(ShakerFpga.Instance.TestType.Value == (ushort)TestType.Random)
+                {
+                    WaitRandomDataLocker.WaitOne();
+                    switch(ServiceRandomConfigViewModel.Instance.RandomData.RandomTestStep)
+                    {
+                        case RandomTestStep.Start:
+                            {
+                                ServiceRandomConfigViewModel.Instance.RandomData.ClearCache();
+                                ServiceRandomConfigViewModel.Instance.RandomData.CurrentTestLevel = ServiceRandomConfigViewModel.Instance.Identify.StartDisplacement;
+                                double[] drive = ServiceRandomConfigViewModel.Instance.RandomData.CalcIdentifyDriver(RandomTestStep.Start, ServiceRandomConfigViewModel.Instance.RandomData.CurrentTestLevel);
+                                uint count = 0;
+                                ShakerFpga.Instance.Read.Write(ref drive[0],0,(uint)drive.Length,ref count);
+                                ServiceRandomConfigViewModel.Instance.RandomData.RandomTestStep = RandomTestStep.Up;
+                                ShakerFpga.Instance.SynthesisType.Value = (ushort)ServiceRandomConfigViewModel.Instance.SynthesisType;
+                                ShakerFpga.Instance.Weight.Value = ServiceShakerConfigViewModel.Instance.Accelerations.Select(x => (double)x.Weight).ToArray();
+                                ShakerFpga.Instance.MaxFixedFrequencyTime.Value = ulong.MaxValue;
+                                ShakerFpga.Instance.SignalStop.Value = false;
+                                ShakerFpga.Instance.SignalStart.Value = true;
+                                ServiceRandomConfigViewModel.Instance.RandomData.RandomStatus = RandomStatus.Start;
+                            }
+                            break;
+                        case RandomTestStep.Up:
+                            {
+                                int i = 0;
+                                var accrms = Enumerable.Range(0, ServiceConfigViewModel.Instance.AnalogCount)
+                                    .Where(x => ServiceShakerConfigViewModel.Instance.AnalogSignals[x].AnalogType == AnalogType.DivideAcceleration)
+                                    .Select(x=>
+                                    {
+                                        var data = ServiceDataCacheViewModel.Instance.GetAnalogData(x).Select(x => (double)x).ToArray();
+                                        data = ServiceRandomConfigViewModel.Instance.RandomData.CalcPSD(data);
+                                        data = ServiceRandomConfigViewModel.Instance.RandomData.AddAccelerationPSD(i, data, false);
+                                        i++;
+                                        return ServiceRandomConfigViewModel.Instance.SpectrumRMS(data);
+                                    });
+                                i = ServiceShakerConfigViewModel.Instance.AnalogSignals.FindIndex(x => x.AnalogType == AnalogType.GivenDriver);
+                                var data = ServiceDataCacheViewModel.Instance.GetAnalogData(i).Select(x => (double)x).ToArray();
+                                data = ServiceRandomConfigViewModel.Instance.RandomData.CalcPSD(data);
+                                ServiceRandomConfigViewModel.Instance.RandomData.AddDriverPSD(data, false);
+                            }
+                            break;
+                        case RandomTestStep.Stop:
+                            break;
+                        case RandomTestStep.Identify:
+                            break;
+                        case RandomTestStep.Test:
+                            break;
+                    }
+                }
+            }
+        }
     }
 }

+ 66 - 0
Shaker/ViewModel/PSDCache.cs

@@ -0,0 +1,66 @@
+using System.Net.Http.Headers;
+
+namespace ShakerService.ViewModel
+{
+    internal class PSDCache
+    {
+        private List<double[]> LinearAverageCache = new List<double[]>();
+        public List<double[]> ExponentialAverageCache= new List<double[]>();
+        public void Clear()
+        {
+            LinearAverageCache.Clear();
+            ExponentialAverageCache.Clear();
+        }
+        public double[] AveragePSD(double[] data, bool avg = true)
+        {
+            if(data ==null || data.Length ==0)return new double[0];
+            if (!avg) return data;
+            if (ServiceRandomConfigViewModel.Instance.LinearAverage > 1)
+            {
+                if (LinearAverageCache.Count == ServiceRandomConfigViewModel.Instance.LinearAverage)
+                {
+                    for(int i=0;i<LinearAverageCache.Count-1;i++)
+                    {
+                        if (i == 0)
+                        {
+                            ServiceDataCacheViewModel.Instance.Calc.Add.Add(ref LinearAverageCache[i][0], ref LinearAverageCache[i + 1][0], (uint)data.Length, ref data[0]);
+                        }
+                        else
+                        {
+                            ServiceDataCacheViewModel.Instance.Calc.Add.Add(ref data[0], ref LinearAverageCache[i + 1][0], (uint)data.Length);
+                        }
+                    }
+                    ServiceDataCacheViewModel.Instance.Calc.Division.Division(ref data[0], LinearAverageCache.Count, (uint)data.Length);
+                    LinearAverageCache.Clear();                    
+                }
+                else
+                {
+                    LinearAverageCache.Add(data);
+                }
+            }
+            if (ServiceRandomConfigViewModel.Instance.ExponentialAverage > 1)
+            {
+                ExponentialAverageCache.Add(data);
+                if (ExponentialAverageCache.Count > ServiceRandomConfigViewModel.Instance.ExponentialAverage)
+                {
+                    ExponentialAverageCache.RemoveRange(0, (int)ServiceRandomConfigViewModel.Instance.ExponentialAverage - ExponentialAverageCache.Count);
+                }
+                if (ExponentialAverageCache.Count == 0 || ExponentialAverageCache.Count == 1) return data;
+                for(int i=0;i< ExponentialAverageCache.Count-2;i++)
+                {
+                    if(i==0)
+                    {
+                        ServiceDataCacheViewModel.Instance.Calc.Add.Add(ref ExponentialAverageCache[i][0], ref ExponentialAverageCache[i + 1][0], (uint)data.Length, ref ExponentialAverageCache[^1][0]);
+                    }
+                    else
+                    {
+                        ServiceDataCacheViewModel.Instance.Calc.Add.Add(ref ExponentialAverageCache[^1][0], ref ExponentialAverageCache[i + 1][0], (uint)data.Length);
+                    }
+                }
+                ServiceDataCacheViewModel.Instance.Calc.Division.Division(ref ExponentialAverageCache[^1][0], ExponentialAverageCache.Count, (uint)data.Length);
+                return ExponentialAverageCache[^1];
+            }
+            else return data;
+        }
+    }
+}

+ 7 - 0
Shaker/ViewModel/ServiceDataCacheViewModel.cs

@@ -2,6 +2,7 @@
 using System;
 using System.Collections.Generic;
 using System.Linq;
+using System.Runtime.CompilerServices;
 using System.Text;
 using System.Threading.Tasks;
 
@@ -21,6 +22,12 @@ namespace ShakerService.ViewModel
         static ServiceDataCacheViewModel()
         {
 
+        }
+        public float[] GetAnalogData(int index)
+        {
+            float[] v = new float[DataCache.GetLength(1)];
+            Unsafe.CopyBlock(ref Unsafe.As<float, byte>(ref v[0]), ref Unsafe.As<float, byte>(ref DataCache[index, 0]), (uint)(v.Length * Unsafe.SizeOf<float>()));
+            return v;
         }
         public static ServiceDataCacheViewModel Instance { get; } = new ServiceDataCacheViewModel();
     }

+ 19 - 4
Shaker/ViewModel/ServiceRandomConfigViewModel.cs

@@ -1,8 +1,11 @@
-using Shaker.Models;
+using MathNet.Numerics;
+using Shaker.Models;
 using ShakerService.Tools;
 using System;
 using System.Collections.Generic;
 using System.Linq;
+using System.Numerics;
+using System.Runtime.ConstrainedExecution;
 using System.Text;
 using System.Threading.Tasks;
 
@@ -10,14 +13,15 @@ namespace ShakerService.ViewModel
 {
     internal class ServiceRandomConfigViewModel:BaseServiceViewModel<RandomConfigModel>
     {
-        public double[] PSDWindow { get;private set;}
-        public double[] FixedWindow { get; private set; }
+        public double[] PSDWindow { get;private set;} = new double[0];
+        public double[] FixedWindow { get; private set; } = new double[0];
         public double HanningWindowCompensationCoefficient => Model.HanningWindowCompensationCoefficient;
         public uint LinearAverage => Model.LinearAverage;
         public uint ExponentialAverage => Model.ExponentialAverage;
         public uint DOF { get; private set; }
         public double FrequencyResolution { get; private set; }
         public uint RandomSampleRate => Model.RandomSampleRate;
+        public double[] HanningWindow { get; private set; }
         public int MaxFrequency => (int)Model.MaxFrequency;
         public AccelerationSynthesisType SynthesisType => Model.SynthesisType;
         public double MinFrequency => Model.MinFrequency;
@@ -25,6 +29,7 @@ namespace ShakerService.ViewModel
         public double[] StartWindow { get;private set; }
         public double[] StopWindow { get; private set; }
         public double Sigma =>Model.Sigma;
+        public List<ServiceRandomSpectralTableViewModel> SpectralTables { get; } = new List<ServiceRandomSpectralTableViewModel>();
         public ServiceRandomIdentifyViewModel Identify { get; } = new ServiceRandomIdentifyViewModel();
         public List<ServiceRandomSpectrumItemViewModel> SpectrumItems { get; } = new List<ServiceRandomSpectrumItemViewModel>();
         public List<ServiceRandomPlanItemViewModel> PlanItems { get; } = new List<ServiceRandomPlanItemViewModel>();
@@ -52,6 +57,10 @@ namespace ShakerService.ViewModel
             });
             Log.Default.Info("初始化随机参数");
         }
+        public double SpectrumRMS(double[] psd)
+        {
+            return Math.Sqrt(ServiceDataCacheViewModel.Instance.Calc.Sum.Sum(ref psd[0], (uint)psd.Length) * FrequencyResolution);
+        }
         static ServiceRandomConfigViewModel()
         {
 
@@ -74,6 +83,11 @@ namespace ShakerService.ViewModel
             Identify.UpModel(model.Identify);
             SpectrumItems.Clear();
             PlanItems.Clear();
+            SpectralTables.Clear();
+            foreach(var item in model.SpectralTables)
+            {
+                SpectralTables.Add(new ServiceRandomSpectralTableViewModel(item));
+            }
             foreach (var item in model.SpectrumItems)
             {
                 SpectrumItems.Add(new ServiceRandomSpectrumItemViewModel(item));
@@ -96,8 +110,9 @@ namespace ShakerService.ViewModel
                 StartWindow[i] = Math.Pow(Math.Sin(d),3);
                 StopWindow[i] = Math.Pow(Math.Cos(d), 3);
             }
+            HanningWindow = Window.Hann(FFTFrameLength);
         }
-
+        
         public ServiceRandomDataViewModel RandomData { get; } = new ServiceRandomDataViewModel();
         public static ServiceRandomConfigViewModel Instance { get; } = new ServiceRandomConfigViewModel();
     }

+ 110 - 62
Shaker/ViewModel/ServiceRandomDataViewModel.cs

@@ -1,100 +1,148 @@
-using Shaker.Models;
+using MathNet.Numerics;
+using Shaker.Models;
 using System;
 using System.Collections.Generic;
 using System.ComponentModel;
 using System.Linq;
 using System.Net.Http.Headers;
+using System.Numerics;
 using System.Text;
 using System.Threading.Tasks;
 
 namespace ShakerService.ViewModel
 {
-    internal class PSDCache
-    {
-        private List<double[]> LinearAverageCache = new List<double[]>();
-        public List<double[]> ExponentialAverageCache= new List<double[]>();
-        public void Clear()
-        {
-            LinearAverageCache.Clear();
-            ExponentialAverageCache.Clear();
-        }
-        public double[] CalcPSD(double[] data)
-        {
-            if(data ==null || data.Length ==0)return new double[0];
-            if (ServiceRandomConfigViewModel.Instance.LinearAverage > 1)
-            {
-                if (LinearAverageCache.Count == ServiceRandomConfigViewModel.Instance.LinearAverage)
-                {
-                    for(int i=0;i<LinearAverageCache.Count-1;i++)
-                    {
-                        if (i == 0)
-                        {
-                            ServiceDataCacheViewModel.Instance.Calc.Add.Add(ref LinearAverageCache[i][0], ref LinearAverageCache[i + 1][0], (uint)data.Length, ref data[0]);
-                        }
-                        else
-                        {
-                            ServiceDataCacheViewModel.Instance.Calc.Add.Add(ref data[0], ref LinearAverageCache[i + 1][0], (uint)data.Length);
-                        }
-                    }
-                    ServiceDataCacheViewModel.Instance.Calc.Division.Division(ref data[0], LinearAverageCache.Count, (uint)data.Length);
-                    LinearAverageCache.Clear();
-                    
-                }
-                else
-                {
-                    LinearAverageCache.Add(data);
-                }
-            }
-            if (ServiceRandomConfigViewModel.Instance.ExponentialAverage > 1)
-            {
-                ExponentialAverageCache.Add(data);
-                if (ExponentialAverageCache.Count > ServiceRandomConfigViewModel.Instance.ExponentialAverage)
-                {
-                    ExponentialAverageCache.RemoveRange(0, (int)ServiceRandomConfigViewModel.Instance.ExponentialAverage - ExponentialAverageCache.Count);
-                }
-                if (ExponentialAverageCache.Count == 0 || ExponentialAverageCache.Count == 1) return data;
-                for(int i=0;i< ExponentialAverageCache.Count-2;i++)
-                {
-                    if(i==0)
-                    {
-                        ServiceDataCacheViewModel.Instance.Calc.Add.Add(ref ExponentialAverageCache[i][0], ref ExponentialAverageCache[i + 1][0], (uint)data.Length, ref ExponentialAverageCache[^1][0]);
-                    }
-                    else
-                    {
-                        ServiceDataCacheViewModel.Instance.Calc.Add.Add(ref ExponentialAverageCache[^1][0], ref ExponentialAverageCache[i + 1][0], (uint)data.Length);
-                    }
-                }
-                ServiceDataCacheViewModel.Instance.Calc.Division.Division(ref ExponentialAverageCache[^1][0], ExponentialAverageCache.Count, (uint)data.Length);
-                return ExponentialAverageCache[^1];
-            }
-            else return data;
-        }
-    }
     internal class ServiceRandomDataViewModel : BaseServiceViewModel<RandomDataModel>,IData
     {
+        public double[] IdentifyFirstDriver { get; set; } = new double[0];
+        public bool HastIdentifyFirstDriver { get; private set; }
         private List<PSDCache> AccelerationPSDCache { get; }= new List<PSDCache>();
         private PSDCache DriverCache { get; }= new PSDCache();
         public double CurrentIdentifyDisplacement { get => Model.CurrentIdentifyDisplacement; set => Model.CurrentIdentifyDisplacement = value; }
         public double CurrentIdentifyRms { get => Model.CurrentIdentifyRms; set => Model.CurrentIdentifyRms = value; }
         public int IdentifyIndex { get => Model.IdentifyIndex; set => Model.IdentifyIndex = value; }
         public List<double[]> CurrentAccelerationPSD { get => Model.CurrentAccelerationPSD; set => Model.CurrentAccelerationPSD = value; }
+        public List<double> CurrentAccelerationSpectrumRMS { get; } = new List<double>();
         public RandomTestStep RandomTestStep{ get => Model.RandomTestStep; set => Model.RandomTestStep = value; }
         public double[] CurrentAccelerationSynthesisPSD { get => Model.CurrentAccelerationSynthesisPSD; set => Model.CurrentAccelerationSynthesisPSD = value; }
+        public double CurrentAccelerationSynthesisSpectrumRMS { get; private set; }
+
         public double[] TransferFunction { get => Model.TransferFunction; set => Model.TransferFunction = value; }
         public double[] DriverPSD { get => Model.DriverPSD; set => Model.DriverPSD = value; }
         public double CurrentTestLevel { get => Model.CurrentTestLevel; set => Model.CurrentTestLevel = value; }
         public double CurrentTestTime { get => Model.CurrentTestTime; set => Model.CurrentTestTime = value; }
+        public RandomStatus RandomStatus { get => Model.RandomStatus; set => Model.RandomStatus = value; }
         public MainPageType MainPageType => Model.MainPageType;
+        public double[] AddAccelerationPSD(int index, double[] accpsd, bool avg = true)
+        {
+            CurrentAccelerationPSD[index] = AccelerationPSDCache[index].AveragePSD(accpsd, avg);
+            return CurrentAccelerationPSD[index];
+        }
+        public double[] AddDriverPSD(double[] driverpsd,bool avg = true)
+        {
+            DriverPSD = DriverCache.AveragePSD(driverpsd, avg);
+            return DriverPSD;
+        }
+
         public void InitCache()
         {
             AccelerationPSDCache.Clear();
             AccelerationPSDCache.AddRange(Enumerable.Range(0, ServiceShakerConfigViewModel.Instance.AccelerationSensorCount).Select(x => new PSDCache()));
+            CurrentAccelerationPSD = Enumerable.Range(0,ServiceShakerConfigViewModel.Instance.AccelerationSensorCount).Select(x => new double[0]).ToList();
+            CurrentAccelerationSpectrumRMS.Clear();
+            CurrentAccelerationSpectrumRMS.AddRange(Enumerable.Repeat(0d, ServiceShakerConfigViewModel.Instance.AccelerationSensorCount));
+            CurrentAccelerationSynthesisSpectrumRMS = 0;
             DriverCache.Clear();
         }
         public void ClearCache()
         {
             AccelerationPSDCache.ForEach(x => x.Clear());
+            CurrentAccelerationPSD = Enumerable.Range(0, ServiceShakerConfigViewModel.Instance.AccelerationSensorCount).Select(x => new double[0]).ToList();
             DriverCache.Clear();
+            HastIdentifyFirstDriver = false;
+            IdentifyIndex++;
+            CurrentAccelerationSpectrumRMS.Clear();
+            CurrentAccelerationSpectrumRMS.AddRange(Enumerable.Repeat(0d, ServiceShakerConfigViewModel.Instance.AccelerationSensorCount));
+            RandomStatus = RandomStatus.Start;
+        }
+        public double[] CalcIdentifyFirstDriver()
+        {
+            if (HastIdentifyFirstDriver) return IdentifyFirstDriver;
+            Random rdm = new Random();
+            int n_down = 0;
+            int n_up = 0;
+            var freqs = Enumerable.Range(0, ServiceRandomConfigViewModel.Instance.FFTFrameLength).Select(x => x * ServiceRandomConfigViewModel.Instance.FrequencyResolution).ToArray();
+            n_down = Array.FindLastIndex(freqs, x => x < ServiceRandomConfigViewModel.Instance.SpectralTables[0].TurningFrequency);
+            n_up = Array.FindLastIndex(freqs, x => x < ServiceRandomConfigViewModel.Instance.SpectralTables[^1].TurningFrequency) + 1;
+            double[] tempvalue = Enumerable.Repeat(8e-14, ServiceRandomConfigViewModel.Instance.FFTHalfFrameLength).ToArray();
+            for (int i = 0; i < n_up - n_down; i++)
+            {
+                int index = i + n_down;
+                double currentfreq = freqs[index];
+                int currenfreqindex = ServiceRandomConfigViewModel.Instance.SpectralTables.FindIndex(x => currentfreq < x.TurningFrequency);
+                if (currenfreqindex > 0)
+                {
+                    double v1 = Math.Log10(ServiceRandomConfigViewModel.Instance.SpectralTables[currenfreqindex].SqrtValue);
+                    double v2 = Math.Log10(ServiceRandomConfigViewModel.Instance.SpectralTables[currenfreqindex - 1].SqrtValue);
+
+                    double f1 = Math.Log10(ServiceRandomConfigViewModel.Instance.SpectralTables[currenfreqindex].TurningFrequency);
+                    double f2 = Math.Log10(ServiceRandomConfigViewModel.Instance.SpectralTables[currenfreqindex - 1].TurningFrequency);
+
+                    tempvalue[currenfreqindex] = Math.Pow(10, (v1 - v2) / (f1 - f2) * (v2 - Math.Log10(currentfreq)) + v2);
+                }
+            }
+            tempvalue[n_up] = ServiceRandomConfigViewModel.Instance.SpectralTables[^1].SqrtValue;
+            Complex[] random = Enumerable.Range(0, ServiceRandomConfigViewModel.Instance.FFTHalfFrameLength).Select(x => Complex.Exp(new Complex(0, rdm.NextDouble() * 4 * Math.PI)) * tempvalue[x]).ToArray();
+            Complex[] value = new Complex[ServiceRandomConfigViewModel.Instance.FFTFrameLength];
+            value[0] = new Complex(tempvalue[0], 0);
+            Array.Copy(random, 0, value, 1, value.Length);
+            for (int i = 1; i < random.Length; i++)
+            {
+                value[^i] = new Complex(random[i].Real, -random[i].Imaginary);
+            }
+            MathNet.Numerics.IntegralTransforms.Fourier.Inverse(value, options: MathNet.Numerics.IntegralTransforms.FourierOptions.Matlab);
+            IdentifyFirstDriver =  value.Select(x => x.Real).ToArray();
+            HastIdentifyFirstDriver = true;
+            return IdentifyFirstDriver;
+        }
+        public double[] CalcIdentifyDriver(RandomTestStep step,double level)
+        {
+            double[] driver = CalcIdentifyFirstDriver();
+            switch (step)
+            {
+                case RandomTestStep.Start:
+                    ServiceDataCacheViewModel.Instance.Calc.Multiply.Multiply(ref driver[0], ref ServiceRandomConfigViewModel.Instance.StartWindow[0], (uint)driver.Length);
+                    break;
+                case RandomTestStep.Stop:
+                    ServiceDataCacheViewModel.Instance.Calc.Multiply.Multiply(ref driver[0], ref ServiceRandomConfigViewModel.Instance.StartWindow[0], (uint)driver.Length);
+                    break;
+            }
+            double dispvalue = Shaker.Models.Tools.Tools.QuantitiesToVoltage(level, ServiceShakerConfigViewModel.Instance.DisplacementSensitivity);
+            double max = Math.Max(Math.Abs(driver.Max()), Math.Abs(driver.Min()));
+            ServiceDataCacheViewModel.Instance.Calc.Multiply.Multiply(ref driver[0], dispvalue / max, (uint)driver.Length);
+            double[] result = new double[driver.Length * ServiceShakerConfigViewModel.Instance.SampleRate / ServiceRandomConfigViewModel.Instance.RandomSampleRate];
+            var linear = MathNet.Numerics.Interpolate.Linear(Enumerable.Range(0, driver.Length).Select(x => (double)x), driver);
+            for(int i=0;i<result.Length;i++)
+            {
+                result[i] = linear.Integrate((double)i / result.Length * driver.Length);
+            };
+            return result;
+        }
+        public double[] CalcPSD(double[] data)
+        {
+            double[] result = new double[ServiceRandomConfigViewModel.Instance.FFTFrameLength];
+            for(int i=0;i<result.Length;i++)
+            {
+                result[i] = data[i * ServiceRandomConfigViewModel.Instance.RandomSampleRate / ServiceShakerConfigViewModel.Instance.SampleRate];
+            }
+            ServiceDataCacheViewModel.Instance.Calc.Multiply.Multiply(ref result[0], ref ServiceRandomConfigViewModel.Instance.HanningWindow[0], (uint)result.Length);
+            double[] img = new double[result.Length];
+            ServiceDataCacheViewModel.Instance.Calc.FFT.FFT(result, img);
+            result = result.Take(ServiceRandomConfigViewModel.Instance.FFTHalfFrameLength).ToArray();
+            ServiceDataCacheViewModel.Instance.Calc.Multiply.Multiply(ref result[0], 1.4142135624 / result.Length, (uint)result.Length);
+            ServiceDataCacheViewModel.Instance.Calc.Multiply.Multiply(ref result[0], ref result[0], (uint)result.Length);
+            ServiceDataCacheViewModel.Instance.Calc.Division.Division(ref result[0], ServiceRandomConfigViewModel.Instance.FrequencyResolution*ServiceRandomConfigViewModel.Instance.HanningWindowCompensationCoefficient, (uint)result.Length);
+            ServiceDataCacheViewModel.Instance.Calc.Multiply.Multiply(ref result[0], ref ServiceRandomConfigViewModel.Instance.PSDWindow[0], (uint)result.Length);
+            return result;
         }
         public void ReadFpgaData()
         {

+ 38 - 0
Shaker/ViewModel/ServiceRandomSpectralTableViewModel.cs

@@ -0,0 +1,38 @@
+using Shaker.Models;
+
+namespace ShakerService.ViewModel
+{
+    internal class ServiceRandomSpectralTableViewModel:BaseServiceViewModel<RandomSpectralTableModel>
+    {
+        public ServiceRandomSpectralTableViewModel():base()
+        {
+
+        }
+        public ServiceRandomSpectralTableViewModel(RandomSpectralTableModel model):base(model)
+        {
+            if(model.TurningValue<0)
+            {
+                SqrtValue = 0;
+            }
+            else
+            {
+                 SqrtValue = Math.Sqrt(model.TurningValue);
+            }
+        }
+        public double TurningFrequency=>Model.TurningFrequency;
+        public double TurningValue=>Model.TurningValue;
+        public double SqrtValue { get; private set; }
+        public override void UpModel(RandomSpectralTableModel model)
+        {
+            base.UpModel(model);
+            if(model.TurningValue<0)
+            {
+                SqrtValue = 0;
+            }
+            else
+            {
+                 SqrtValue = Math.Sqrt(Model.TurningValue);
+            }
+        }
+    }
+}