mirror of
https://github.com/make-42/xyosc
synced 2025-01-19 02:57:34 +01:00
136 lines
2.7 KiB
Go
136 lines
2.7 KiB
Go
|
// 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
|
||
|
}
|
||
|
}
|