Chapter 17: Stochastic Processes and Random Vibrations

Lesson 2: Autocorrelation, Power Spectral Density, and White Noise Models

This lesson introduces the second-order language of stochastic system dynamics: autocorrelation, power spectral density (PSD), and white-noise models. The focus is on rigorous definitions, theorem-level connections, and computational estimators.

1. Conceptual Overview

In deterministic analysis, we transform a known signal into frequency domain. In stochastic analysis, the signal itself is random, so we characterize its ensemble second-order structure through \( R_{xx} \) and \( S_{xx} \).

\[ R_{xx}(\theta)=\mathbb{E}\!\left[(x(t)-\mu_x)(x(t+\theta)-\mu_x)\right], \qquad R_{xx}[m]=\mathbb{E}\!\left[(x[n]-\mu_x)(x[n+m]-\mu_x)\right] \]

For WSS processes these functions depend only on lag. At zero lag they equal the variance.

flowchart TD
  A["Random process x(t) or x[n]"] --> B["Check WSS assumptions and remove mean"]
  B --> C["Compute autocorrelation Rxx(lag)"]
  C --> D["Transform to PSD Sxx(freq)"]
  D --> E["Interpret bandwidth and dominant content"]
  E --> F["Build white / colored noise models"]
        

2. Autocorrelation Properties and Proofs

Even symmetry (real WSS):

\[ R_{xx}(-\theta)=R_{xx}(\theta), \qquad R_{xx}[-m]=R_{xx}[m] \]

Proof: For continuous time,

\[ R_{xx}(-\theta)=\mathbb{E}[x(t)x(t-\theta)] =\mathbb{E}[x(t+\theta)x(t)] =\mathbb{E}[x(t)x(t+\theta)] =R_{xx}(\theta) \]

Positive semidefinite property: for any real coefficients \( a_1,\dots,a_p \) and lags \( \theta_1,\dots,\theta_p \),

\[ \sum_{i=1}^p\sum_{j=1}^p a_i a_j R_{xx}(\theta_i-\theta_j) \ge 0 \]

Proof: Define \( z=\sum_i a_i x(t+\theta_i) \). Then \( \mathbb{E}[z^2] \ge 0 \), and expansion gives the claim.

\[ \mathbb{E}[z^2] =\sum_{i=1}^p\sum_{j=1}^p a_i a_j \mathbb{E}[x(t+\theta_i)x(t+\theta_j)] =\sum_{i=1}^p\sum_{j=1}^p a_i a_j R_{xx}(\theta_i-\theta_j) \]

3. Power Spectral Density and Wiener–Khinchin

The PSD is the Fourier transform of the autocorrelation. Continuous-time:

\[ S_{xx}(\omega)=\int_{-\infty}^{\infty} R_{xx}(\theta)e^{-j\omega\theta}\,d\theta, \qquad R_{xx}(\theta)=\frac{1}{2\pi}\int_{-\infty}^{\infty} S_{xx}(\omega)e^{j\omega\theta}\,d\omega \]

Discrete-time:

\[ S_{xx}(e^{j\omega})=\sum_{m=-\infty}^{\infty} R_{xx}[m]e^{-j\omega m}, \qquad R_{xx}[m]=\frac{1}{2\pi}\int_{-\pi}^{\pi} S_{xx}(e^{j\omega})e^{j\omega m}\,d\omega \]

A direct consequence is \( S_{xx}(e^{j\omega})\ge 0 \), and for zero-mean processes, the total spectral area gives variance:

\[ R_{xx}[0]=\operatorname{Var}(x[n])=\frac{1}{2\pi}\int_{-\pi}^{\pi} S_{xx}(e^{j\omega})\,d\omega \]

4. White Noise Models

Ideal continuous-time white noise is a generalized process:

\[ R_{ww}(\theta)=\frac{N_0}{2}\delta(\theta), \qquad S_{ww}(\omega)=\frac{N_0}{2} \]

It has flat PSD on all frequencies, which makes it useful analytically but idealized physically.

Discrete-time white noise with variance \( \sigma_w^2 \):

\[ \mathbb{E}[w[n]]=0, \qquad R_{ww}[m]=\sigma_w^2\delta[m], \qquad S_{ww}(e^{j\omega})=\sigma_w^2 \]

Band-limited white noise (practical simulation model):

\[ S_{ww}(\omega)= \begin{cases} S_0, & |\omega| \le \Omega_c \\ 0, & |\omega| > \Omega_c \end{cases} \]

\[ R_{ww}(\theta)=\frac{1}{2\pi}\int_{-\Omega_c}^{\Omega_c} S_0 e^{j\omega\theta}\,d\omega =\frac{S_0}{\pi}\frac{\sin(\Omega_c\theta)}{\theta} \]

5. Finite-Record Estimation of Autocorrelation and PSD

For a finite record \( x[0],\dots,x[N-1] \), the ensemble moments are replaced by estimators.

\[ \hat{R}_{xx}^{(b)}[m]=\frac{1}{N}\sum_{n=0}^{N-1-m} x[n]x[n+m], \qquad \hat{R}_{xx}^{(u)}[m]=\frac{1}{N-m}\sum_{n=0}^{N-1-m} x[n]x[n+m] \]

The periodogram estimator is

\[ I_N(e^{j\omega})=\frac{1}{N}\left|\sum_{n=0}^{N-1}x[n]e^{-j\omega n}\right|^2 \]

In practice, Welch/Bartlett averaging or lag-window smoothing is used to reduce variance.

\[ \hat{S}_{xx}(e^{j\omega})=\sum_{m=-M}^{M} w[m]\hat{R}_{xx}[m]e^{-j\omega m} \]

flowchart TD
  A["Finite record x[n]"] --> B["Subtract mean"]
  B --> C["Estimate Rxx[m]"]
  C --> D["Window / truncate lags"]
  D --> E["FFT to estimate PSD"]
  B --> F["Alternative: periodogram / Welch"]
  E --> G["Variance and bandwidth interpretation"]
  F --> G
        

6. Canonical Colored-Noise Example Used in Code

Let \( x[n]=w[n]+b\,w[n-1] \) with white input \( R_{ww}[m]=\sigma_w^2\delta[m] \). Then

\[ R_{xx}[m]=\sigma_w^2\Big((1+b^2)\delta[m]+b\delta[m-1]+b\delta[m+1]\Big) \]

\[ S_{xx}(e^{j\omega})=\sigma_w^2(1+b^2+2b\cos\omega) =\sigma_w^2\left|1+be^{-j\omega}\right|^2 \]

This example is simple enough for full derivation and rich enough to illustrate correlation-to-PSD mapping.

7. Python Implementation

Chapter17_Lesson2.py

# Chapter17_Lesson2.py
# Autocorrelation, Power Spectral Density, and White Noise Models

import numpy as np
import matplotlib.pyplot as plt

def autocorr_biased(x, M):
    x = np.asarray(x, float) - np.mean(x)
    N = len(x)
    return np.array([np.dot(x[:N-m], x[m:]) / N for m in range(M + 1)])

rng = np.random.default_rng(17)
N, sigma, b = 2048, 2.0, 0.6
w = sigma * rng.standard_normal(N)                 # white noise
x = np.r_[w[0], w[1:] + b * w[:-1]]               # colored: x[n]=w[n]+b w[n-1]

M = 80
Rw = autocorr_biased(w, M)
Rx = autocorr_biased(x, M)

# PSD from autocorrelation (Wiener-Khinchin route)
Re = np.r_[Rx, Rx[-2:0:-1]]                       # even extension
Sx = np.real(np.fft.fft(Re))
omega = 2 * np.pi * np.arange(len(Sx)) / len(Sx)

# Theory for x[n]=w[n]+b w[n-1]
Sx_th = sigma**2 * (1 + b**2 + 2 * b * np.cos(omega))

print("R_w[0] (variance estimate) =", Rw[0])
print("R_x[0] (variance estimate) =", Rx[0])
print("Theoretical var(x)        =", sigma**2 * (1 + b**2))

plt.figure()
plt.plot(Rw, label="White")
plt.plot(Rx, label="Colored")
plt.xlabel("Lag m"); plt.ylabel("Autocorrelation"); plt.grid(True); plt.legend()

plt.figure()
half = len(omega) // 2
plt.plot(omega[:half], Sx[:half], label="Estimated PSD")
plt.plot(omega[:half], Sx_th[:half], label="Theory")
plt.xlabel("rad/sample"); plt.ylabel("PSD"); plt.grid(True); plt.legend()
plt.show()

8. C++ Implementation

Chapter17_Lesson2.cpp

// Chapter17_Lesson2.cpp
// Autocorrelation and PSD (compact C++17 example)

#include <cmath>
#include <complex>
#include <iostream>
#include <numeric>
#include <random>
#include <vector>

std::vector<double> autocorrBiased(const std::vector<double>& x, int M) {
    int N = (int)x.size();
    double mu = std::accumulate(x.begin(), x.end(), 0.0) / N;
    std::vector<double> xc(N), R(M + 1, 0.0);
    for (int i = 0; i < N; ++i) xc[i] = x[i] - mu;
    for (int m = 0; m <= M; ++m) {
        for (int n = 0; n < N - m; ++n) R[m] += xc[n] * xc[n + m];
        R[m] /= N;
    }
    return R;
}

std::vector<double> evenExtend(const std::vector<double>& R) {
    int M = (int)R.size() - 1;
    std::vector<double> e;
    for (int i = 0; i <= M; ++i) e.push_back(R[i]);
    for (int i = M - 1; i >= 1; --i) e.push_back(R[i]);
    return e;
}

std::vector<double> dftReal(const std::vector<double>& x) {
    int N = (int)x.size();
    std::vector<double> S(N, 0.0);
    for (int k = 0; k < N; ++k) {
        std::complex<double> Xk(0.0, 0.0);
        for (int n = 0; n < N; ++n) {
            double a = -2.0 * M_PI * k * n / N;
            Xk += x[n] * std::complex<double>(std::cos(a), std::sin(a));
        }
        S[k] = Xk.real(); // real because autocorrelation extension is even
    }
    return S;
}

int main() {
    const int N = 512, M = 20;
    const double sigma = 1.5, b = 0.6;
    std::mt19937 gen(17);
    std::normal_distribution<double> gauss(0.0, sigma);

    std::vector<double> w(N), x(N);
    for (int n = 0; n < N; ++n) w[n] = gauss(gen);
    x[0] = w[0];
    for (int n = 1; n < N; ++n) x[n] = w[n] + b * w[n - 1];

    auto Rw = autocorrBiased(w, M);
    auto Rx = autocorrBiased(x, M);
    auto Sx = dftReal(evenExtend(Rx));

    std::cout << "R_w[0] = " << Rw[0] << "\\n";
    std::cout << "R_x[0] = " << Rx[0] << "\\n";
    std::cout << "Theo R_x[0] = " << sigma * sigma * (1.0 + b * b) << "\\n";
    std::cout << "First 8 PSD bins from autocorrelation-DFT:\\n";
    for (int k = 0; k < 8; ++k) std::cout << "k=" << k << "  " << Sx[k] << "\\n";
    return 0;
}

9. Java Implementation

Chapter17_Lesson2.java

// Chapter17_Lesson2.java
// Autocorrelation and PSD (compact Java example)

import java.util.Random;

public class Chapter17_Lesson2 {
    static double[] autocorrBiased(double[] x, int M) {
        int N = x.length;
        double mu = 0.0;
        for (double v : x) mu += v;
        mu /= N;

        double[] xc = new double[N];
        for (int i = 0; i < N; i++) xc[i] = x[i] - mu;

        double[] R = new double[M + 1];
        for (int m = 0; m <= M; m++) {
            for (int n = 0; n < N - m; n++) R[m] += xc[n] * xc[n + m];
            R[m] /= N;
        }
        return R;
    }

    static double[] evenExtend(double[] R) {
        int M = R.length - 1;
        double[] e = new double[2 * M];
        for (int i = 0; i <= M; i++) e[i] = R[i];
        for (int i = M - 1; i >= 1; i--) e[2 * M - i] = R[i];
        return e;
    }

    static double[] dftReal(double[] x) {
        int N = x.length;
        double[] S = new double[N];
        for (int k = 0; k < N; k++) {
            double re = 0.0, im = 0.0;
            for (int n = 0; n < N; n++) {
                double a = -2.0 * Math.PI * k * n / N;
                re += x[n] * Math.cos(a);
                im += x[n] * Math.sin(a);
            }
            S[k] = re; // autocorrelation extension is even, so imaginary part is near zero
        }
        return S;
    }

    public static void main(String[] args) {
        int N = 512, M = 20;
        double sigma = 1.2, b = 0.6;
        Random rng = new Random(17);

        double[] w = new double[N];
        for (int n = 0; n < N; n++) w[n] = sigma * rng.nextGaussian();

        double[] x = new double[N];
        x[0] = w[0];
        for (int n = 1; n < N; n++) x[n] = w[n] + b * w[n - 1];

        double[] Rw = autocorrBiased(w, M);
        double[] Rx = autocorrBiased(x, M);
        double[] Sx = dftReal(evenExtend(Rx));

        System.out.println("R_w[0] = " + Rw[0]);
        System.out.println("R_x[0] = " + Rx[0]);
        System.out.println("Theo R_x[0] = " + (sigma * sigma * (1 + b * b)));
        for (int k = 0; k < 8; k++) {
            System.out.println("PSD bin " + k + " = " + Sx[k]);
        }
    }
}

10. MATLAB / Simulink Implementation

Chapter17_Lesson2.m

% Chapter17_Lesson2.m
% Autocorrelation, PSD, and White Noise Models (MATLAB / Simulink)

clear; clc; close all; rng(17);

N = 2048; sigma = 2.0; b = 0.6;
w = sigma * randn(N,1);             % white noise
x = filter([1 b], 1, w);            % x[n] = w[n] + b w[n-1]

M = 80;
RwFull = xcorr(w-mean(w), M, 'biased');
RxFull = xcorr(x-mean(x), M, 'biased');
Rw = RwFull(M+1:end);
Rx = RxFull(M+1:end);

Re = [Rx; Rx(end-1:-1:2)];
Sx = real(fft(Re));
omega = 2*pi*(0:length(Sx)-1)'/length(Sx);
SxTheory = sigma^2 * (1 + b^2 + 2*b*cos(omega));

fprintf('R_w(0) = %.6f\n', Rw(1));
fprintf('R_x(0) = %.6f\n', Rx(1));
fprintf('Theo R_x(0) = %.6f\n', sigma^2*(1+b^2));

figure; plot(0:M, Rw, 0:M, Rx); grid on;
xlabel('Lag m'); ylabel('Autocorrelation'); legend('White','Colored');

figure; half = 1:floor(length(Sx)/2);
plot(omega(half), Sx(half), omega(half), SxTheory(half), '--'); grid on;
xlabel('rad/sample'); ylabel('PSD'); legend('Estimated','Theory');

% Simulink setup:
% 1) Band-Limited White Noise block
% 2) Discrete Transfer Fcn with numerator [1 b], denominator [1]
% 3) Spectrum Analyzer + Scope

11. Wolfram Mathematica Implementation

Chapter17_Lesson2.nb

(* Chapter17_Lesson2.nb *)
(* Wolfram Language script for autocorrelation and PSD *)

SeedRandom[17];
n = 2048; sigma = 2.0; b = 0.6;
w = RandomVariate[NormalDistribution[0, sigma], n];
x = Join[{w[[1]]}, Rest[w] + b Most[w]];

autocorrBiased[data_, mmax_] := Module[{z = N[data - Mean[data]], Nn = Length[data]},
  Table[Total[Take[z, Nn - m] Take[z, {1 + m, Nn}]]/Nn, {m, 0, mmax}]
];

rw = autocorrBiased[w, 80];
rx = autocorrBiased[x, 80];

re = Join[rx, Reverse[rx[[2 ;; -2]]]];
sx = Re[Fourier[re, FourierParameters -> {1, -1}]];
omega = Table[2 Pi (k - 1)/Length[sx], {k, Length[sx]}];
sxTheory = sigma^2 (1 + b^2 + 2 b Cos[omega]);

Print["R_w(0) = ", rw[[1]]];
Print["R_x(0) = ", rx[[1]]];
Print["Theo R_x(0) = ", sigma^2 (1 + b^2)];

ListLinePlot[{rw, rx}, PlotLegends -> {"White", "Colored"}, AxesLabel -> {"m", "R"}]
ListLinePlot[{Take[sx, Length[sx]/2], Take[sxTheory, Length[sxTheory]/2]},
  PlotLegends -> {"Estimated", "Theory"}, AxesLabel -> {"bin", "PSD"}]

12. Problems and Solutions

Problem 1: Prove that for a real WSS process, \( R_{xx}[-m]=R_{xx}[m] \).

Solution: Use stationarity and commutativity of multiplication:

\[ R_{xx}[-m]=\mathbb{E}[x[n]x[n-m]] =\mathbb{E}[x[n-m]x[n]] =R_{xx}[m] \]

Problem 2: Show that \( \mathbb{E}[\hat{R}_{xx}^{(b)}[m]] = \frac{N-m}{N}R_{xx}[m] \).

Solution: Take expectation term-by-term:

\[ \mathbb{E}[\hat{R}_{xx}^{(b)}[m]] =\frac{1}{N}\sum_{n=0}^{N-1-m}\mathbb{E}[x[n]x[n+m]] =\frac{N-m}{N}R_{xx}[m] \]

Problem 3: For \( x[n]=w[n]+b\,w[n-1] \), compute \( R_{xx}[0] \) and \( R_{xx}[1] \).

Solution:

\[ R_{xx}[0]=(1+b^2)\sigma_w^2, \qquad R_{xx}[1]=b\sigma_w^2 \]

and by evenness \( R_{xx}[-1]=b\sigma_w^2 \).

Problem 4: If a zero-mean process has band-limited PSD \( S_{xx}(\omega)=S_0 \) on \( |\omega|\le\Omega_c \), compute the variance.

Solution:

\[ \operatorname{Var}(x(t)) =\frac{1}{2\pi}\int_{-\Omega_c}^{\Omega_c} S_0\,d\omega =\frac{S_0\Omega_c}{\pi} \]

Problem 5: Derive the PSD for \( R_{xx}[m]=\sigma_x^2 a^{|m|} \) with \( |a| < 1 \).

Solution:

\[ S_{xx}(e^{j\omega}) =\sigma_x^2\left(1+2\sum_{m=1}^{\infty}a^m\cos(m\omega)\right) =\sigma_x^2\frac{1-a^2}{1-2a\cos\omega+a^2} \]

13. Summary

Autocorrelation and PSD are the central second-order tools for stochastic system dynamics. The Wiener–Khinchin theorem links them exactly, white-noise models provide canonical inputs, and finite-record estimators make the theory operational for simulation and laboratory data.

14. References

  1. Wiener, N. (1930). Generalized harmonic analysis. Acta Mathematica, 55, 117–258.
  2. Khintchine, A. (1934). Korrelationstheorie der stationären stochastischen Prozesse. Mathematische Annalen, 109, 604–615.
  3. Bochner, S. (1933). Monotone Funktionen, Stieltjessche Integrale und harmonische Analyse. Mathematische Annalen, 108, 378–410.
  4. Bartlett, M.S. (1950). Periodogram analysis and continuous spectra. Biometrika, 37(1/2), 1–16.
  5. Parzen, E. (1961). Mathematical considerations in the estimation of spectra. Technometrics, 3(2), 167–190.
  6. Welch, P.D. (1967). The use of fast Fourier transform for the estimation of power spectra. IEEE Transactions on Audio and Electroacoustics, 15(2), 70–73.
  7. Kolmogorov, A.N. (1941). Interpolation and extrapolation of stationary random sequences. Izvestiya Akademii Nauk SSSR, Seriya Matematicheskaya (foundational stationarity/spectral prediction theory).