/// <summary> /// Построение комплексной сонограммы /// </summary> /// <param name="FFT_S"> Вектор входных данных /// ("левый" и "правый" каналы - чет./нечет.). </param> /// <param name="FFT_S_Offset"> Смещение данных для анализа во /// входном векторе FFT_S. </param> /// <param name="useTaperWindow"> Использовать взвешивающее окно? </param> /// <param name="recoverAfterTaperWindow"> Аннигилировать действие /// взвешивающего окна на обратном проходе? </param> /// <param name="useNorm"> Использовать нормализацию 1/N? </param> /// <param name="direction"> Направление преобразования (true - прямое). </param> /// <param name="usePolyphase"> Использовать полифазное FFT? </param> /// <param name="remainArrayItemsLRCount"> Остаток необработанных данных в исходном /// массиве (количество элементов, включая Re/Im). </param> /// <param name="fftObj"> Объект FFT, для которого вызывается функция. </param> /// <returns> Сонограмма. </returns> public static double[][] Process(double[] FFT_S, int FFT_S_Offset, bool useTaperWindow, bool recoverAfterTaperWindow, bool useNorm, bool direction, bool usePolyphase, out int remainArrayItemsLRCount, ExactFFT.CFFT_Object fftObj) { int plotRowsCount, frameOffset; double[][] FFT_T; if (FFT_S == null) { throw new Exception("ExactPlotter::Process(): (FFT_S == null)"); } // Вычисляем количество строк сонограммы... plotRowsCount = GetPlotRowsCount(FFT_S, FFT_S_Offset, out remainArrayItemsLRCount, fftObj); // Обрабатываем все фреймы... FFT_T = new double[plotRowsCount][]; Parallel.For(0, plotRowsCount, frame => { // Умножение на 2 треб. для real -> complex FFT_T[frame] = new double[fftObj.N << 1]; frameOffset = FFT_S_Offset + frame * fftObj.WindowStep << 1; ExactFFT.CFFT_Process(FFT_S, frameOffset, FFT_T[frame], useTaperWindow, recoverAfterTaperWindow, useNorm, direction, usePolyphase, fftObj); }); return(FFT_T); }
/// <summary> /// Выделение поддиапазона гармоник из сонограммы /// </summary> /// <param name="sonogram"> Исходная сонограмма. </param> /// <param name="lowFreq"> Частота нижней гармоники выделяемого диапазона. </param> /// <param name="highFreq"> Частота верхней гармоники выделяемого диапазона. </param> /// <param name="lowHarmIdx"> Нижний индекс гармоники. </param> /// <param name="highHarmIdx"> Верхний индекс гармоники. </param> /// <param name="fftObj"> Объект FFT, для которого вызывается функция. </param> /// <param name="sampleRate"> Частота семплирования. </param> /// <param name="harmReverse"> Требуется реверс гармоник? </param> /// <returns> Выделенный поддиапазон сонограммы. </returns> public static double[][] SubBand(double[][] sonogram, double lowFreq, double highFreq, out int lowHarmIdx, out int highHarmIdx, ExactFFT.CFFT_Object fftObj, int sampleRate, bool harmReverse = false) { if (sonogram == null) { throw new Exception("ExactPlotter::SubBand(): (sonogram == null)"); } lowHarmIdx = (int)ExactFFT.FFT_Node(lowFreq, sampleRate, fftObj.N, fftObj.IsComplex); highHarmIdx = (int)ExactFFT.FFT_Node(highFreq, sampleRate, fftObj.N, fftObj.IsComplex); return(SubBand(sonogram, lowHarmIdx, highHarmIdx, harmReverse)); }
/// <summary> /// Извлечение данных из комплексной сонограммы (L+R) /// </summary> /// <param name="FFT_T"> Выходной набор векторов коэффициентов. </param> /// <param name="usePolyphase"> Использовать полифазное FFT? </param> /// <param name="fftObj"> Объект FFT, для которого вызывается функция. </param> /// <returns> Результат разбора выходных данных CFFT. </returns> public static CFFT_ExploreResult Explore(double[][] FFT_T, bool usePolyphase, ExactFFT.CFFT_Object fftObj) { int plotRowsCount; CFFT_ExploreResult res; res = new CFFT_ExploreResult(); if (FFT_T == null) { throw new Exception("ExactPlotter::Explore(): (FFT_T == null)"); } // Считываем количество строк, которые имеет сонограмма... plotRowsCount = FFT_T.Length; // Подготавливаем выходные массивы... res.MagL = new double[plotRowsCount][]; res.MagR = new double[plotRowsCount][]; res.ACH = new double[plotRowsCount][]; res.ArgL = new double[plotRowsCount][]; res.ArgR = new double[plotRowsCount][]; res.PhaseLR = new double[plotRowsCount][]; // Работаем по всем строкам сонограммы... Parallel.For(0, plotRowsCount, frame => { // Количество гармоник в два раза меньше размера кадра FFT res.MagL[frame] = new double[fftObj.N >> 1]; res.MagR[frame] = new double[fftObj.N >> 1]; res.ACH[frame] = new double[fftObj.N >> 1]; res.ArgL[frame] = new double[fftObj.N >> 1]; res.ArgR[frame] = new double[fftObj.N >> 1]; res.PhaseLR[frame] = new double[fftObj.N >> 1]; // Извлечение данных FFT из комплексной сонограммы ExactFFT.CFFT_Explore(FFT_T[frame], res.MagL[frame], res.MagR[frame], res.ACH[frame], res.ArgL[frame], res.ArgR[frame], res.PhaseLR[frame], usePolyphase, fftObj); }); return(res); }
/// <summary> /// Перевод значений массива double в форму dB /// </summary> /// <param name="sonogram"> Данные для обработки. </param> /// <param name="zero_db_level"> Значение "нулевого" уровня. </param> /// <param name="fftObj"> Объект FFT, для которого вызывается функция. </param> public static void dB_Scale(double[][] sonogram, double zero_db_level, ExactFFT.CFFT_Object fftObj) { int plotRowsCount; if (sonogram == null) { throw new Exception("ExactPlotter::dB_Scale(): (sonogram == null)"); } // Считываем количество строк, которые имеет сонограмма... plotRowsCount = sonogram.Length; // Работаем по всем строкам сонограммы... Parallel.For(0, plotRowsCount, frame => { ExactFFT.dB_Scale(sonogram[frame], zero_db_level, fftObj); }); }
/// <summary> /// Исследование результатов комплексного FFT (идентично CFFT из MathCAD) /// </summary> /// <param name="FFT_T"> Выходной набор векторов коэффициентов. </param> /// <param name="usePolyphase"> Использовать полифазное FFT? </param> /// <param name="isMirror"> Зеркальное отображение спектра? </param> /// <param name="fftObj"> Объект FFT, для которого вызывается функция. </param> /// <returns> Результат разбора выходных данных CFFT. </returns> public static CFFT_ExploreResult ComplexExplore(double[][] FFT_T, bool usePolyphase, bool isMirror, ExactFFT.CFFT_Object fftObj) { int plotRowsCount; CFFT_ExploreResult res; res = new CFFT_ExploreResult(); if (FFT_T == null) { throw new Exception("ExactPlotter::ComplexExplore(): (FFT_T == null)"); } // Считываем количество строк, которые имеет сонограмма... plotRowsCount = FFT_T.Length; // Подготавливаем выходные массивы... res.Mag = new double[plotRowsCount][]; res.Arg = new double[plotRowsCount][]; // Работаем по всем строкам сонограммы... Parallel.For(0, plotRowsCount, frame => { // Количество гармоник равно размеру кадра FFT res.Mag[frame] = new double[fftObj.N]; res.Arg[frame] = new double[fftObj.N]; // Извлечение данных FFT из комплексной сонограммы (режим COMPLEX) ExactFFT.CFFT_ComplexExplore(FFT_T[frame], res.Mag[frame], res.Arg[frame], usePolyphase, isMirror, fftObj); }); return(res); }
private static void Main(string[] args) { int i, j, frameWidth, polyDiv2, windowStep, N, N2, depth; // double kaiserBeta; double ACH_Difference, sampFreq, trueFreq, exactFreq, exactFreqDiff; double[] FFT_S, FFT_T, MagC, MagL, MagR, ACH, ArgC, ArgL, ArgR, PhaseLR; int FFT_S_Offset; bool useTaperWindow, recoverAfterTaperWindow, useNorm, direction, usePolyphase, isMirror, isComplex; ExactFFT.CFFT_Object fftObj; ExactFFT.CFFT_SelfTestResult selfTestResult; ExactFFT.IsDumpMode = true; // *************************************************** Console.WriteLine("ExactFFT TEST \"C#\" 8.40, (c) DrAF, 2016"); // *************************************************** // * КОНСТРУКТОР // *************************************************** frameWidth = 4096; //kaiserBeta = 28; // 28 polyDiv2 = 1; windowStep = frameWidth / 3; isComplex = false; ExactFFT.TaperWindow taperWindow = ExactFFT.TaperWindow.BLACKMAN_HARRIS_92dbPS; fftObj = ExactFFT.CFFT_Constructor_Cosine(frameWidth, taperWindow, polyDiv2, windowStep, isComplex); //fftObj = ExactFFT.CFFT_Constructor_Kaiser(frameWidth, beta, polyDiv2, windowStep, isComplex); // *************************************************** // * САМОДИАГНОСТИКА // *************************************************** ACH_Difference = 1000; selfTestResult = ExactFFT.SelfTest_RND(ACH_Difference, fftObj); Console.WriteLine("Process & Explore: {0} ms / {1} ms", (selfTestResult.CFFT_Process_time * 1000).ToString("F4"), (selfTestResult.CFFT_Explore_time * 1000).ToString("F4")); Console.WriteLine("Self-test result: {0}", selfTestResult.AllOK); // Источник и приемник FFT_S = new double[frameWidth << 1]; FFT_T = new double[frameWidth << 1]; // (Количество точек FFT / 2) - количество гармоник вместе с нулевой N = fftObj.N; N2 = N >> 1; // Массивы результатов Фурье-анализа MagC = new double[N]; MagL = new double[N2]; MagR = new double[N2]; ACH = new double[N2]; ArgC = new double[N]; ArgL = new double[N2]; ArgR = new double[N2]; PhaseLR = new double[N2]; // Читаем все данные из файла с тестовым сигналом if (!File.Exists("3600_Hz_STEREO_36000_SampleRate_36_deg_65536.raw")) { Console.WriteLine("\nCan't open 3600_Hz_STEREO_36000_SampleRate_36_deg_65536.raw!"); return; } byte[] testSignalBytes = File.ReadAllBytes("3600_Hz_STEREO_36000_SampleRate_36_deg_65536.raw"); for (i = 0, j = 0; i < (frameWidth << 1); ++i, j += 2) { FFT_S[i] = (double)BitConverter.ToInt16(testSignalBytes, j); } DebugHelper.WriteDoubles(ExactFFT.DumpName, "FFT_S.double", FFT_S); // Прямой прогон FFT useTaperWindow = true; FFT_S_Offset = 0; recoverAfterTaperWindow = false; useNorm = true; direction = true; usePolyphase = false; isMirror = true; ExactFFT.CFFT_Process(FFT_S, FFT_S_Offset, FFT_T, useTaperWindow, recoverAfterTaperWindow, useNorm, direction, usePolyphase, fftObj); DebugHelper.WriteDoubles(ExactFFT.DumpName, "FFT_T.double", FFT_T); ExactFFT.CFFT_Explore(FFT_T, MagL, MagR, ACH, ArgL, ArgR, PhaseLR, usePolyphase, fftObj); DebugHelper.WriteDoubles(ExactFFT.DumpName, "MagL.double", MagL); DebugHelper.WriteDoubles(ExactFFT.DumpName, "MagR.double", MagR); DebugHelper.WriteDoubles(ExactFFT.DumpName, "PhaseLR.double", PhaseLR); ExactFFT.CFFT_ComplexExplore(FFT_T, MagC, ArgC, usePolyphase, isMirror, fftObj); DebugHelper.WriteDoubles(ExactFFT.DumpName, "MagC.double", MagC); DebugHelper.WriteDoubles(ExactFFT.DumpName, "ArgC.double", ArgC); // Вычисление точной частоты sampFreq = 36000; trueFreq = 3600; depth = 20; exactFreq = ExactFFT.ExactFreqAuto(MagL, depth, sampFreq, fftObj); exactFreqDiff = Math.Abs(exactFreq - trueFreq); DebugHelper.WriteDouble(ExactFFT.DumpName, "exactFreq.double", exactFreq); DebugHelper.WriteDouble(ExactFFT.DumpName, "trueFreq.double", trueFreq); DebugHelper.WriteDouble(ExactFFT.DumpName, "exactFreqDiff.double", exactFreqDiff); // *************************************************** // * ДЕСТРУКТОР // *************************************************** FFT_S = null; FFT_T = null; MagC = null; MagL = null; MagR = null; ACH = null; ArgC = null; ArgL = null; ArgR = null; PhaseLR = null; ExactFFT.CFFT_Destructor(fftObj); }