Awk Generated White Noise (AGWN)

Here's some Awk code to implement the Box-Muller transform1 to generate Additive White Gaussian Noise (AWGN). I found it nice to prototype this in Awk instead of C or another scripting language.

#!/usr/bin/awk -f

function bm_normal_rect(z,    u1, u2, r) {
    do { u1 = rand() } while (u1 == 0.0)  # Avoid log(0).
    u2 = rand()
    r = sqrt(-2.0 * log(u1))
    z[0] = r * cos(tau * u2)
    z[1] = r * sin(tau * u2)
}

function bm_normal_polar(z,    u1, u2, s, r) {
    do {
        u1 = (rand() * 2.0) - 1.0
        u2 = (rand() * 2.0) - 1.0
        s  = u1*u1 + u2*u2
    } while (s > 1)

    if (s == 0.0) {
        z[0] = z[1] = 0.0  # Avoid division by zero.
    } else {
        r = sqrt(-2.0 * log(s) / s)
        z[0] = u1 * r
        z[1] = u2 * r
    }
}

BEGIN {
    pi = atan2(0, -1)
    tau = 2.0*pi

    delete z[0]  # Make an empty array for the return values.
    while (1) {
        bm_normal_rect(z)              # Faster.
        # bm_normal_polar(z)           # Slower.
        printf "%a\n%a\n", z[0], z[1]  # Hexadecimal float notation, faster
        # printf "%.17e\n%.17e\n", z[0], z[1]  # Round-trip decimal precision
    }
    exit
}

And some example output, using my GnuPlot wrapper:

./box_muller.awk | head -n 10000 | ~/tools/gp_wrap/gp_wrap.sh -t " AGWN" -p -w agwn.png

agwn.png

Interestingly, the polar method is an optimization that avoids \(\sin\) and \(\cos\), but at least in this Awk implementation the rectangular method runs faster on my machine (75.9s for 100e6 points) than the polar method (85.5s for 100e6 points). Always profile your code when choosing between optimization stategies. I haven't done any statistical tests on either output.

Footnotes:

1

See also Knuth's Algorithm P, TAOCP Vol. 2, 3rd ed., p. 122.

© Copyright 2023, Remington Furman

blog@remcycles.net

@remcycles@subdued.social