Help us shape the future of Go! Take the 2026 Go Developer Survey

sim

package
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 14, 2026 License: MIT Imports: 3 Imported by: 0

Documentation

Overview

Package sim models the acoustic channel for tests and for `murmur bench`.

The point of this package is that a reliability claim is only worth making if it has been tested against a channel that is actually hostile. requires BER curves over reverb, drift, clipping and dropout, and every one of those has to be reproducible: a seed in, the same samples out, forever.

A Chain is a []float32 -> []float32 function, which means it also satisfies audio.Transformer, so a simulated channel can be spliced into a live signal path without any layer above audio knowing.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Chain

type Chain struct {
	Steps []Step
}

Chain applies steps in order.

func (Chain) Add

func (c Chain) Add(steps ...Step) Chain

Add appends steps to the chain, returning it for fluent construction.

func (Chain) Apply

func (c Chain) Apply(pcm []float32) []float32

Apply runs every step in order.

type Preset

type Preset struct {
	Chain

	Name    string
	Desc    string
	Variant int64
}

Preset names a channel condition that murmur claims to survive.

func PresetByName

func PresetByName(name string) (Preset, bool)

PresetByName returns a preset at variant 0.

func PresetVariant

func PresetVariant(name string, variant int64) (Preset, bool)

PresetVariant returns a preset whose generators are offset by variant, giving an independent realisation of the same channel condition. Frames in a bench run use successive variants so that each frame sees the same statistics but a different noise and dropout pattern, which is what makes a measured frame-loss ratio meaningful.

func Presets

func Presets() []Preset

Presets returns every preset at variant 0, for listing and for `murmur bench`.

For a bench run, materialise a fresh preset per frame with PresetVariant instead. The channel steps hold generators, so reusing one Preset instance across frames advances those generators and makes the frames neither independent nor reproducible - which would defeat the only reason the bench exists.

type Step

type Step interface {
	Apply(pcm []float32) []float32
}

Step rewrites a block of samples. Implementations must not modify the input.

type StepFunc

type StepFunc func([]float32) []float32

StepFunc adapts a function to Step.

func AWGN

func AWGN(snrDB float64, seed int64) StepFunc

AWGN adds white Gaussian noise at the requested SNR.

The signal level is estimated from the RMS of samples that are at least 10% of the peak, not from the whole block. A plain block RMS would count a burst's leading silence as signal and therefore add less noise than the caller asked for, which would make a simulated channel quietly gentler than its label claims - the most dangerous kind of test-fixture bug, because it makes reliability look better than it is.

func BandLimit

func BandLimit(lowHz, highHz float64, sampleRate, taps int) StepFunc

BandLimit applies a bandpass, modelling the combined rolloff of a laptop speaker and a laptop microphone. Cutoffs outside the channel are what make "the band is the limit" a literal statement rather than a rule of thumb.

func Clip

func Clip(level float64) StepFunc

Clip models a speaker or amplifier hitting its limit. Loudness compliance murmur keeps well below this, but the test exists to show what happens if the amplitude policy is ignored.

func Delay

func Delay(ms float64, sampleRate int) StepFunc

Delay shifts audio later by d milliseconds, zero-filling the start.

func Drift

func Drift(ppm float64, _ int) StepFunc

Drift resamples by a fixed parts-per-million offset, modelling two machines whose crystals disagree. Positive ppm means the receiver's clock is fast, so it captures the sender's burst in slightly fewer samples than were sent.

func Dropout

func Dropout(probability, durationMS float64, sampleRate int, seed int64) StepFunc

Dropout zeroes random slices of audio, modelling a cough, a door, or a hand passing in front of a speaker. This is the failure that ARQ exists for: no amount of inner FEC survives a 200 ms gap, but a retransmission does.

func Gain

func Gain(db float64) StepFunc

Gain scales by a decibel amount.

func Quantize

func Quantize(bits int) StepFunc

Quantize models a fixed-point converter, including the int16 ADC/DAC at the edge of a real capture path. Quantisation noise is correlated with the signal, which is why it can matter for tone detection more than its level suggests.

func Reverb

func Reverb(t60Seconds, drrDB float64, sampleRate int, seed int64) StepFunc

Reverb convolves the signal with a synthetic room impulse response.

drrDB is the direct-to-reverberant ratio: how much louder the direct path is than everything that arrives after it, in decibels. It is the parameter that actually determines whether an acoustic modem works, and modelling it explicitly is the difference between a channel that is hard and a channel that is fictitious.

An earlier version of this function normalised the impulse response to unit energy while building a direct path of amplitude 1 and a diffuse tail of RMS 0.35. That puts 99.6% of the energy in the tail - a direct-to-reverberant ratio near -24 dB, which no room on earth produces. It made every reverberant test fail, and would have sent the PHY design chasing a phantom.

Realistic values: +10 dB is a small room with the laptops a metre apart, +4 dB is a livelier room, 0 dB is a hall.

func Silence

func Silence(samples int) StepFunc

Silence prepends the given number of zero samples.

Prepending rather than shifting matters. The Delay step below shifts a signal later and therefore drops the tail, which for a burst means silently truncating its final symbols - a channel-induced failure that looks exactly like a decoder bug. Silence lengthens the buffer instead, so a test that offsets a burst still delivers all of it.

func (StepFunc) Apply

func (f StepFunc) Apply(pcm []float32) []float32

Apply implements Step.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL