Add detailed comments to Goertzel and two-tone detector functions for improved code clarity
This commit is contained in:
61
main.go
61
main.go
@ -33,6 +33,11 @@ type goertzel struct {
|
||||
coeff float64
|
||||
}
|
||||
|
||||
// newGoertzel initializes and returns a new Goertzel filter configured to detect a specific target frequency.
|
||||
// targetHz specifies the frequency to detect in Hertz.
|
||||
// fs is the sampling rate in Hertz.
|
||||
// N is the number of samples to process.
|
||||
// The function calculates the filter coefficients based on the provided parameters.
|
||||
func newGoertzel(targetHz float64, fs float64, N int) *goertzel {
|
||||
g := &goertzel{N: N, fs: fs}
|
||||
g.k = int(0.5 + (float64(g.N)*targetHz)/fs)
|
||||
@ -41,6 +46,10 @@ func newGoertzel(targetHz float64, fs float64, N int) *goertzel {
|
||||
return g
|
||||
}
|
||||
|
||||
// Power computes the power of the target frequency in the input signal x using the Goertzel algorithm.
|
||||
// It processes the input slice x of length g.N, applying the Goertzel recurrence to accumulate state.
|
||||
// The function returns the squared magnitude (power) of the frequency bin specified by g.k.
|
||||
// x should be a slice of float64 samples, typically representing a windowed segment of a signal.
|
||||
func (g *goertzel) Power(x []float64) float64 {
|
||||
var s0, s1, s2 float64
|
||||
for i := 0; i < g.N; i++ {
|
||||
@ -54,6 +63,10 @@ func (g *goertzel) Power(x []float64) float64 {
|
||||
return real*real + imag*imag
|
||||
}
|
||||
|
||||
// windowHann applies a Hann window to the input slice x in place.
|
||||
// The Hann window is commonly used in signal processing to reduce spectral leakage
|
||||
// by tapering the beginning and end of the signal to zero.
|
||||
// The function modifies the input slice directly.
|
||||
func windowHann(x []float64) {
|
||||
n := float64(len(x))
|
||||
for i := range x {
|
||||
@ -61,6 +74,9 @@ func windowHann(x []float64) {
|
||||
}
|
||||
}
|
||||
|
||||
// pcmToFloat converts a slice of 16-bit PCM audio samples (buf) to a slice of float64 values.
|
||||
// The output slice has length N, and each element is the float64 representation of the corresponding PCM sample.
|
||||
// If N is greater than the length of buf, the output slice will contain zero values for the remaining elements.
|
||||
func pcmToFloat(buf []int16, N int) []float64 {
|
||||
out := make([]float64, N)
|
||||
for i := 0; i < N && i < len(buf); i++ {
|
||||
@ -69,6 +85,9 @@ func pcmToFloat(buf []int16, N int) []float64 {
|
||||
return out
|
||||
}
|
||||
|
||||
// rmsPCM calculates the root mean square (RMS) value of a slice of 16-bit PCM audio samples.
|
||||
// It returns the RMS as a float64, which represents the signal's effective amplitude.
|
||||
// If the input slice is empty, it returns 0.
|
||||
func rmsPCM(buf []int16) float64 {
|
||||
var s float64
|
||||
for _, v := range buf {
|
||||
@ -107,6 +126,23 @@ type twoToneDetector struct {
|
||||
logger *slog.Logger
|
||||
}
|
||||
|
||||
// newTwoToneDetector creates and initializes a new twoToneDetector instance.
|
||||
// It sets up a bank of Goertzel filters for frequencies ranging from 300 Hz to 3000 Hz in 10 Hz steps.
|
||||
// Parameters:
|
||||
//
|
||||
// fs - Sample rate in Hz.
|
||||
// winN - Window size for analysis.
|
||||
// hopN - Hop size between windows.
|
||||
// ratioThresh- Threshold for tone ratio detection.
|
||||
// rmsThresh - RMS threshold for signal detection.
|
||||
// minAms - Minimum duration of tone A in milliseconds.
|
||||
// minBms - Minimum duration of tone B in milliseconds.
|
||||
// gapMaxMs - Maximum allowed gap between tones in milliseconds.
|
||||
// logger - Logger for diagnostic output.
|
||||
//
|
||||
// Returns:
|
||||
//
|
||||
// Pointer to a twoToneDetector configured with the specified parameters.
|
||||
func newTwoToneDetector(fs, winN, hopN int, ratioThresh, rmsThresh float64, minAms, minBms, gapMaxMs int, logger *slog.Logger) *twoToneDetector {
|
||||
freqs := make([]float64, 0)
|
||||
for f := 300.0; f <= 3000.0; f += 10.0 {
|
||||
@ -131,6 +167,26 @@ func newTwoToneDetector(fs, winN, hopN int, ratioThresh, rmsThresh float64, minA
|
||||
}
|
||||
}
|
||||
|
||||
// stepWindow processes a window of PCM audio samples to detect a two-tone sequence.
|
||||
// It applies a Hann window to the samples, computes RMS and power ratios, and tracks
|
||||
// the presence and duration of two distinct tones (A and B) according to configured thresholds.
|
||||
// The function returns an event string (e.g., "TWO_TONE_DETECTED") when a valid two-tone
|
||||
// sequence is detected, along with the frequencies and durations (in milliseconds) of both tones,
|
||||
// and the timestamp of detection.
|
||||
//
|
||||
// Parameters:
|
||||
//
|
||||
// pcms - Slice of int16 PCM audio samples for the current window.
|
||||
// t0 - Start time of the current window.
|
||||
//
|
||||
// Returns:
|
||||
//
|
||||
// event - Event string indicating detection status (e.g., "TWO_TONE_DETECTED" or "").
|
||||
// aFreq - Frequency of detected Tone A (Hz).
|
||||
// aDur - Duration of Tone A (milliseconds).
|
||||
// bFreq - Frequency of detected Tone B (Hz).
|
||||
// bDur - Duration of Tone B (milliseconds).
|
||||
// timestamp - Timestamp of detection (time.Time). Zero value if no event detected.
|
||||
func (d *twoToneDetector) stepWindow(pcms []int16, t0 time.Time) (event string, aFreq, aDur, bFreq, bDur float64, timestamp time.Time) {
|
||||
xi := pcmToFloat(pcms, d.winN)
|
||||
windowHann(xi)
|
||||
@ -236,6 +292,10 @@ func (d *twoToneDetector) stepWindow(pcms []int16, t0 time.Time) (event string,
|
||||
return "", 0, 0, 0, 0, time.Time{}
|
||||
}
|
||||
|
||||
// reset reinitializes all internal state fields of the twoToneDetector,
|
||||
// clearing any ongoing detection data and preparing the detector for a new
|
||||
// detection sequence. This includes resetting flags, frequency values,
|
||||
// accumulated durations, start/end timestamps, and gap tracking.
|
||||
func (d *twoToneDetector) reset() {
|
||||
d.inA = false
|
||||
d.aFreq = 0
|
||||
@ -337,6 +397,7 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
// min returns the smaller of two integer values a and b.
|
||||
func min(a, b int) int {
|
||||
if a < b {
|
||||
return a
|
||||
|
Reference in New Issue
Block a user