245 lines
9.5 KiB
C#
245 lines
9.5 KiB
C#
using KLHZ.Trader.Core.Math.Declisions.Utils;
|
||
using MathNet.Numerics;
|
||
using MathNet.Numerics.IntegralTransforms;
|
||
|
||
namespace KLHZ.Trader.Core.Tests
|
||
{
|
||
public class FFTTests
|
||
{
|
||
[Test]
|
||
public static void Test1()
|
||
{
|
||
// Размер сигнала
|
||
int N = 1024;
|
||
|
||
// Генерируем случайный сигнал
|
||
var signal = new float[N];
|
||
Random random = new Random();
|
||
for (int i = 0; i < N; i++)
|
||
{
|
||
signal[i] = (float)System.Math.Cos(0.01 * i);// (float)random.NextDouble() * 2 - 1;
|
||
}
|
||
// нормированный случайный сигнал [-1, 1]
|
||
|
||
// Выполняем прямое преобразование Фурье
|
||
Complex32[] fftResult = signal.Select(s => new Complex32(s, 0)).ToArray();
|
||
Fourier.Forward(fftResult);
|
||
|
||
// Выделяем первые 10 гармоник (включая нулевую)
|
||
var firstTenHarmonics = fftResult.AsSpan(0, 10).ToArray();
|
||
|
||
//Копируем первые 11 элементов (нулевая и первые 10)
|
||
|
||
// Вычисляем амплитуды и фазы гармоник
|
||
double[] amplitudes = new double[firstTenHarmonics.Length];
|
||
double[] phases = new double[firstTenHarmonics.Length];
|
||
for (int k = 0; k < firstTenHarmonics.Length; k++)
|
||
{
|
||
amplitudes[k] = firstTenHarmonics[k].Magnitude / N;
|
||
phases[k] = firstTenHarmonics[k].Phase;
|
||
}
|
||
|
||
// Последний индекс сигнала
|
||
int lastPointIndex = N - 1;
|
||
|
||
// Реконструкция последней точки сигнала
|
||
double reconstructedLastPoint = 0;
|
||
double reconstructedFirstPoint = 0;
|
||
for (int k = 1; k < firstTenHarmonics.Length; k++) // начинаем с первой гармоники
|
||
{
|
||
reconstructedLastPoint += amplitudes[k] *
|
||
System.Math.Cos((2 * System.Math.PI * k * lastPointIndex) / N + phases[k]);
|
||
}
|
||
for (int k = 1; k < firstTenHarmonics.Length; k++) // начинаем с первой гармоники
|
||
{
|
||
reconstructedFirstPoint += amplitudes[k] *
|
||
System.Math.Cos((2 * System.Math.PI * k * 1) / N + phases[k]);
|
||
}
|
||
Console.WriteLine($"Реконструированное значение последней точки: {reconstructedLastPoint}");
|
||
}
|
||
public static Complex32[] InverseFourierManual(Complex32[] spectrum)
|
||
{
|
||
int N = spectrum.Length;
|
||
Complex32[] result = new Complex32[N];
|
||
|
||
for (int t = 0; t < N; t++)
|
||
{
|
||
Complex32 sum = Complex32.Zero;
|
||
|
||
for (int k = 0; k < N; k++)
|
||
{
|
||
double angle = 2 * System.Math.PI * k * t / N;
|
||
sum += spectrum[k] * Complex32.Exp(new Complex32(0, (float)angle));
|
||
}
|
||
|
||
result[t] = sum / N;
|
||
}
|
||
|
||
return result;
|
||
}
|
||
[Test]
|
||
public static void Test()
|
||
{
|
||
var da = new List<float>();
|
||
var da2 = new List<float>();
|
||
var dates = new List<DateTime>();
|
||
var dt = DateTime.UtcNow;
|
||
var dt1 = dt;
|
||
var dt2 = dt.AddSeconds(3600);
|
||
var T1 = TimeSpan.FromMinutes(10);
|
||
var T2 = TimeSpan.FromMinutes(7);
|
||
var T3 = TimeSpan.FromMinutes(30);
|
||
while (dt < dt2)
|
||
{
|
||
dt = dt.AddSeconds(1);
|
||
var phase = (dt - dt1).TotalSeconds / T1.TotalSeconds * 2 * System.Math.PI;
|
||
var phase2 = (dt - dt1).TotalSeconds / T2.TotalSeconds * 2 * System.Math.PI;
|
||
var phase3 = (dt - dt1).TotalSeconds / T3.TotalSeconds * 2 * System.Math.PI;
|
||
da.Add((float)System.Math.Cos(phase) + (float)System.Math.Sin(phase2) + (float)System.Math.Sin(phase3));
|
||
dates.Add(dt);
|
||
}
|
||
|
||
var start = da.ToArray();
|
||
|
||
var harms = FFT.GetHarmonics(da.Select(d => (decimal)d).ToArray(), dates.Last() - dates.First(), TimeSpan.FromSeconds(7), TimeSpan.FromSeconds(70));
|
||
|
||
var damax = da.Max();
|
||
var damin = da.Min();
|
||
|
||
for (int i = 0; i < da.Count; i++)
|
||
{
|
||
da[i] = da[i] / (damax - damin);
|
||
}
|
||
|
||
foreach (var d in dates)
|
||
{
|
||
da2.Add((float)FFT.CalcAmplitude(harms, dates.First(), d));
|
||
}
|
||
|
||
var da2max = da2.Max();
|
||
var da2min = da2.Min();
|
||
|
||
for (int i = 0; i < da2.Count; i++)
|
||
{
|
||
da2[i] = da2[i] / (da2max - da2min);
|
||
}
|
||
|
||
damax = da.Max();
|
||
damin = da.Min();
|
||
da2max = da2.Max();
|
||
da2min = da2.Min();
|
||
//var tem = arrv.Select(a => Complex32.Abs(a)).ToArray();
|
||
//var s = tem.Sum();
|
||
//Fourier.Inverse(arrv);
|
||
//var res = arrv.Select(a => a.Real).ToArray();
|
||
var diffs = new List<float>();
|
||
for (int i = 0; i < da2.Count; i++)
|
||
{
|
||
var diff = (da2[i] - da[i]) / (damax - damin);
|
||
diffs.Add(diff);
|
||
}
|
||
}
|
||
|
||
[Test]
|
||
public static void CalcHarmonyPeriodTest1()
|
||
{
|
||
var period1 = FFT.CaclHarmonycPeriod(TimeSpan.FromHours(1), 1000, 1);
|
||
var period2 = FFT.CaclHarmonycPeriod(TimeSpan.FromHours(1), 1000, 2);
|
||
var period3 = FFT.CaclHarmonycPeriod(TimeSpan.FromHours(1), 1000, 3);
|
||
var period4 = FFT.CaclHarmonycPeriod(TimeSpan.FromHours(1), 1000, 4);
|
||
}
|
||
|
||
|
||
[Test]
|
||
public static void CalcPhaseRangeForMax_Sin()
|
||
{
|
||
var res = FFT.CalcPhaseRangeFoxMax(1, 0, 0, 0.001);
|
||
Assert.IsTrue(res.min < System.Math.PI / 2);
|
||
Assert.IsTrue(res.min > System.Math.PI / 2 * 0.9);
|
||
Assert.IsTrue(res.max > System.Math.PI / 2);
|
||
Assert.IsTrue(res.max < System.Math.PI / 2 * 1.1);
|
||
}
|
||
|
||
[Test]
|
||
public static void CalcPhaseRangeForMin_Sin()
|
||
{
|
||
var res = FFT.CalcPhaseRangeFoxMin(1, 0, 0, 0.001);
|
||
Assert.IsTrue(res.min < 3 * System.Math.PI / 2);
|
||
Assert.IsTrue(res.min > 3 * System.Math.PI / 2 * 0.9);
|
||
Assert.IsTrue(res.max > 3 * System.Math.PI / 2);
|
||
Assert.IsTrue(res.max < 3 * System.Math.PI / 2 * 1.1);
|
||
}
|
||
|
||
[Test]
|
||
public static void CalcPhaseRangeForMax_MinusSin()
|
||
{
|
||
var res = FFT.CalcPhaseRangeFoxMax(-1, 0, 0, 0.001);
|
||
Assert.IsTrue(res.min < 3 * System.Math.PI / 2);
|
||
Assert.IsTrue(res.min > 3 * System.Math.PI / 2 * 0.9);
|
||
Assert.IsTrue(res.max > 3 * System.Math.PI / 2);
|
||
Assert.IsTrue(res.max < 3 * System.Math.PI / 2 * 1.1);
|
||
}
|
||
|
||
[Test]
|
||
public static void CalcPhaseRangeForMax_Cos()
|
||
{
|
||
var res = FFT.CalcPhaseRangeFoxMax(0, 1, 0, 0.001);
|
||
Assert.IsTrue(res.min < System.Math.PI * 2);
|
||
Assert.IsTrue(res.min > System.Math.PI * 2 * 0.9);
|
||
Assert.IsTrue(res.max > 0);
|
||
Assert.IsTrue(res.max < System.Math.PI * 2 * 0.1);
|
||
}
|
||
|
||
[Test]
|
||
public static void CalcPhaseRangeForMin_Cos()
|
||
{
|
||
var res = FFT.CalcPhaseRangeFoxMin(0, 1, 0, 0.001);
|
||
Assert.IsTrue(res.min < System.Math.PI);
|
||
Assert.IsTrue(res.min > System.Math.PI * 0.9);
|
||
Assert.IsTrue(res.max > System.Math.PI);
|
||
Assert.IsTrue(res.max < System.Math.PI * 1.1);
|
||
}
|
||
|
||
[Test]
|
||
public static void CalcPhaseRangeForMax_CosWithShift()
|
||
{
|
||
var res = FFT.CalcPhaseRangeFoxMax(0, 1, System.Math.PI / 2, 0.001);
|
||
Assert.IsTrue(res.min < 3 * System.Math.PI / 2);
|
||
Assert.IsTrue(res.min > 3 * System.Math.PI / 2 * 0.9);
|
||
Assert.IsTrue(res.max > 3 * System.Math.PI / 2);
|
||
Assert.IsTrue(res.max < 3 * System.Math.PI / 2 * 1.1);
|
||
}
|
||
|
||
[Test]
|
||
public static void CalcPhaseRangeForMax_CosWithShift2()
|
||
{
|
||
var res = FFT.CalcPhaseRangeFoxMax(0, 1, -System.Math.PI / 2, 0.001);
|
||
Assert.IsTrue(res.min < System.Math.PI / 2);
|
||
Assert.IsTrue(res.min > System.Math.PI / 2 * 0.9);
|
||
Assert.IsTrue(res.max > System.Math.PI / 2);
|
||
Assert.IsTrue(res.max < System.Math.PI / 2 * 1.1);
|
||
}
|
||
|
||
[Test]
|
||
public static void TrimValues_Test1()
|
||
{
|
||
var da = new List<decimal>();
|
||
var dates = new List<DateTime>();
|
||
var dt = DateTime.UtcNow;
|
||
var dt1 = dt;
|
||
var dt2 = dt.AddSeconds(3600);
|
||
var i = 0;
|
||
while (dt < dt2)
|
||
{
|
||
da.Add(i);
|
||
dates.Add(dt);
|
||
dt = dt.AddSeconds(1);
|
||
i++;
|
||
}
|
||
|
||
var res = FFT.TrimValues(dates.ToArray(), da.ToArray(), TimeSpan.FromSeconds(30));
|
||
var res2 = FFT.TrimValues(dates.ToArray(), da.ToArray(), TimeSpan.FromSeconds(4011));
|
||
}
|
||
}
|
||
}
|