コード例 #1
0
        /// <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);
        }
コード例 #2
0
 /// <summary>
 /// Выделение одного момента времени из сонограммы
 /// </summary>
 /// <param name="sonogram"> Исходная сонограмма. </param>
 /// <param name="time"> Время среза (кадра FFT). </param>
 /// <param name="timeIdx"> Индекс временного среза (кадра FFT). </param>
 /// <param name="fftObj"> Объект FFT, для которого вызывается функция. </param>
 /// <param name="sampleRate"> Частота семплирования. </param>
 /// <returns> Выделенный поддиапазон сонограммы. </returns>
 public static double[] TimeSlice(double[][] sonogram, double time, out int timeIdx,
                                  ExactFFT.CFFT_Object fftObj,
                                  int sampleRate)
 {
     // Выделяем один временной срез (выделяем вектор, привязанный к ОДНОЙ строке)...
     return(SubTime(sonogram, time, time, out timeIdx, out timeIdx, fftObj, sampleRate)[0]);
 }
コード例 #3
0
        /// <summary>
        /// Вычисление количества строк в "водопаде"
        /// </summary>
        /// <param name="FFT_S"> Вектор входных данных
        /// ("левый" и "правый" каналы - чет./нечет.). </param>
        /// <param name="FFT_S_Offset"> Смещение данных для анализа во
        /// входном векторе FFT_S. </param>
        /// <param name="remainArrayItemsLRCount"> Остаток необработанных данных в исходном
        /// массиве (количество элементов, включая Re/Im). </param>
        /// <param name="fftObj"> Объект FFT, для которого вызывается функция. </param>
        public static int GetPlotRowsCount(double[] FFT_S, int FFT_S_Offset,
                                           out int remainArrayItemsLRCount,
                                           ExactFFT.CFFT_Object fftObj)
        {
            if (FFT_S == null)
            {
                throw new Exception("ExactPlotter::GetPlotRowsCount(): (FFT_S == null)");
            }

            return(GetPlotRowsCount(FFT_S.Length, FFT_S_Offset, out remainArrayItemsLRCount, fftObj));
        }
コード例 #4
0
        /// <summary>
        /// Выделение поддиапазона времени из сонограммы
        /// </summary>
        /// <param name="sonogram"> Исходная сонограмма. </param>
        /// <param name="startTime"> Стартовый кадр FFT (время строки сонограммы). </param>
        /// <param name="finishTime"> Конечный кадр FFT (время строки сонограммы). </param>
        /// <param name="startFrameIdx"> Стартовый кадр FFT (строка сонограммы). </param>
        /// <param name="finishFrameIdx"> Конечный кадр FFT (строка сонограммы). </param>
        /// <param name="fftObj"> Объект FFT, для которого вызывается функция. </param>
        /// <param name="sampleRate"> Частота семплирования. </param>
        /// <param name="harmReverse"> Требуется реверс гармоник? </param>
        /// <returns> Выделенный поддиапазон сонограммы. </returns>
        public static double[][] SubTime(double[][] sonogram, double startTime, double finishTime,
                                         out int startFrameIdx, out int finishFrameIdx,
                                         ExactFFT.CFFT_Object fftObj,
                                         int sampleRate,
                                         bool harmReverse = false)
        {
            double frameDuration, sleepCoeff, timeSliceDuration;

            GetFrameParameters(fftObj.N, fftObj.WindowStep, sampleRate,
                               out frameDuration, out sleepCoeff, out timeSliceDuration);

            startFrameIdx  = (int)(startTime / timeSliceDuration);
            finishFrameIdx = (int)(finishTime / timeSliceDuration);

            return(SubTime(sonogram, startFrameIdx, finishFrameIdx, harmReverse));
        }
コード例 #5
0
        /// <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));
        }
コード例 #6
0
        /// <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);
        }
コード例 #7
0
        /// <summary>
        /// Вычисление количества строк в "водопаде"
        /// </summary>
        /// <param name="FFT_S"> Вектор входных данных
        /// ("левый" и "правый" каналы - чет./нечет.). </param>
        /// <param name="FFT_S_Offset"> Смещение данных для анализа во
        /// входном векторе FFT_S. </param>
        /// <param name="remainArrayItemsLRCount"> Остаток необработанных данных в исходном
        /// массиве (количество элементов, включая Re/Im). </param>
        /// <param name="fftObj"> Объект FFT, для которого вызывается функция. </param>
        public static int GetPlotRowsCount(int FFT_S_Length, int FFT_S_Offset,
                                           out int remainArrayItemsLRCount,
                                           ExactFFT.CFFT_Object fftObj)
        {
            int stepAreaLength_Real, nSteps, areaWithoutFFT_Real, intersectFFTArea_Real;

            // Вычисляем размер поля данных для "поглощения" шагами FFT.
            // Деление на 2 требуется для перехода от комплексной "чет-нечет" формы к "вещественной"
            stepAreaLength_Real = ((FFT_S_Length - FFT_S_Offset) >> 1) - fftObj.N;

            // Невозможно обработать столь малый блок (некуда шагать)!
            if (stepAreaLength_Real < 0)
            {
                throw new Exception("ExactPlotter::GetPlotRowsCount: (stepAreaLength_Real < 0)");
            }

            // Количество шагов взвешивающим окном равно размеру области, первоначально не лежащей
            // под окном FFT, деленному на шаг окна FFT. Так-как шаги делаются целиком, нельзя
            // осуществить полшага, поэтому берется только целая часть числа.
            // Оставшееся количество попадает в необработанный остаток!
            // +1 учитывает "нулевой" шаг, при первоначальном положении на исходных данных.
            nSteps = ((stepAreaLength_Real / fftObj.WindowStep) + 1);

            // Когда вычисляется необработанный остаток, следует понимать, что следующий шаг FFT
            // покроет остаток, и выйдет даже за пределы поля исходных данных, поэтому процесс
            // и останавливается. Но если сделать шаг FFT, то оно захватит не только всё поле
            // необработанных данных, если его шаг меньше его самого, произойдет частичный
            // захват области под ним самим (в состоянии итерации при останове).
            // Размер этой области равен размеру окна FFT минус его шаг. И эта величина также
            // требует, чтобы её учитывали!
            // Если шаг FFT больше его самого, получится отрицательная величина.
            areaWithoutFFT_Real     = stepAreaLength_Real - ((nSteps - 1) * fftObj.WindowStep);
            intersectFFTArea_Real   = fftObj.N - fftObj.WindowStep;
            remainArrayItemsLRCount = (intersectFFTArea_Real + areaWithoutFFT_Real) << 1;

            // Отрицательный остаток обработки дает ошибку при построении сонограммы!
            if (remainArrayItemsLRCount < 0)
            {
                throw new Exception("ExactPlotter::GetPlotRowsCount: (remainArrayItemsLRCount < 0)");
            }

            return(nSteps);
        }
コード例 #8
0
        /// <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);
            });
        }
コード例 #9
0
        /// <summary>
        /// Выделение одной гармоники из сонограммы
        /// </summary>
        /// <param name="sonogram"> Исходная сонограмма. </param>
        /// <param name="harmFreq"> Частота выделяемой гармоники. </param>
        /// <param name="harmIdx"> Индекс выделяемой гармоники. </param>
        /// <param name="fftObj"> Объект FFT, для которого вызывается функция. </param>
        /// <param name="sampleRate"> Частота семплирования. </param>
        /// <returns> Выделенный поддиапазон сонограммы. </returns>
        public static double[] HarmSlice(double[][] sonogram, double harmFreq, out int harmIdx,
                                         ExactFFT.CFFT_Object fftObj,
                                         int sampleRate)
        {
            double[]   harmVector;
            double[][] harmColumn;

            // Выделяем ОДНУ гармонику (она размещается в одном столбце и множестве строк)...
            harmColumn = SubBand(sonogram, harmFreq, harmFreq, out harmIdx, out harmIdx, fftObj, sampleRate);

            // Данные вектора-столбца размещаем в векторе-строке...
            harmVector = new double[harmColumn.Length];

            // Работаем по всем строкам сонограммы...
            Parallel.For(0, harmColumn.Length, frame =>
            {
                harmVector[frame] = harmColumn[frame][0];
            });

            //...и возвращаем результат
            return(harmVector);
        }
コード例 #10
0
        /// <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);
        }