xyosc/vendor/github.com/goccmack/godsp/peaks/peaks.go

136 lines
2.7 KiB
Go
Raw Normal View History

2025-01-16 22:56:38 +01:00
// Copyright 2019 Marius Ackerman
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*
Package peaks finds the maxima in a vector. It works by lowering a horizontal line
across the signal, revealing peaks as it proceeds. Peaks that are closer to
each other than a minimum separation distance are merged to the left (lower index).
*/
package peaks
import (
"math"
"sort"
"github.com/goccmack/godsp"
)
const (
empty = -1
)
/*
Get returns a slice containing the indices of the peaks in x.
sep is the minimum distance between 2 peaks. Peaks closer to each other than
sep are merged to the lower index.
*/
func Get(x []float64, sep int) []int {
pks := []int{}
for i := range x {
if isMax(i, i-sep, i+sep, x) {
pks = append(pks, i)
}
}
return pks
}
func getMaxIndex(x []float64) int {
i, max := 0, math.Inf(-1)
for j, y := range x {
if y > max {
i, max = j, y
}
}
if max > 0 {
return i
}
return -1
}
func isMax(i, min, max int, x []float64) bool {
if min < 0 {
min = 0
}
if max > len(x) {
max = len(x)
}
for j := min; j < i; j++ {
if x[j] >= x[i] {
return false
}
}
for j := i + 1; j < max; j++ {
if x[j] > x[i] {
return false
}
}
return true
}
func getWindow(i, sep int, x []float64) (min, max int) {
min, max = i-sep, i+sep
if min < 0 {
min = 0
}
if max > len(x) {
max = len(x)
}
return
}
// func Get(x []float64, sep int) []int {
// si := getSortedIndices(x)
// pks := getEmptyPeaks(len(x))
// for _, xi := range si {
// if pks[xi] == empty {
// markNeighbours(xi, sep, pks)
// }
// }
// uniquePeaks := make([]int, 0, len(x)/(2*sep))
// for i, xi := range pks {
// if i == xi {
// uniquePeaks = append(uniquePeaks, xi)
// }
// }
// return uniquePeaks
// }
func getEmptyPeaks(n int) []int {
epks := make([]int, n)
for i := range epks {
epks[i] = empty
}
return epks
}
func getSortedIndices(x []float64) []int {
idx := godsp.Range(len(x))
sort.SliceStable(idx, func(i, j int) bool { return x[i] > x[j] })
return idx
}
func markNeighbours(xi, sep int, pks []int) {
min := xi - sep
if min < 0 {
min = 0
}
max := xi + sep
if max > len(pks) {
max = len(pks)
}
for i := min; i < max; i++ {
pks[i] = xi
}
}