# Gaussian Noise Validation Tests Documentation

## Overview

The HF Channel Simulator uses Gaussian noise as the basis for modeling ionospheric turbulence and channel effects. Three comprehensive tests validate that the noise generator produces statistically correct Gaussian-distributed complex noise with proper spectral and power characteristics.

---

## Test 1: Noise Distribution Test

### Purpose

Validates that the `Noise` class generates complex pairs (I + jQ) where both components follow a standard Gaussian distribution: N(0,1).

### What It Tests

#### 1. **Mean (μ ≈ 0)**
The average value should be zero for both I and Q channels.

**Theory**: E[X] = 0 for standard normal distribution

**Tolerance**: ±0.001

**Why it matters**: Non-zero mean would introduce a DC offset in the channel simulator, causing frequency translation errors.

#### 2. **Standard Deviation (σ ≈ 1)**
The spread of values should have unit variance.

**Theory**: σ² = E[(X-μ)²] = 1 for N(0,1)

**Tolerance**: ±1%

**Why it matters**: Incorrect variance would cause signal power errors throughout the simulation.

#### 3. **Skewness (γ₁ ≈ 0)**
The distribution should be symmetric.

**Theory**: γ₁ = E[(X-μ)³]/σ³ = 0 for Gaussian

**Tolerance**: ±0.05

**Why it matters**: Skewed noise would favor positive or negative excursions, introducing asymmetric fading.

#### 4. **Kurtosis (β₂ ≈ 3)**
The tail behavior should match Gaussian (excess kurtosis = 0).

**Theory**: β₂ = E[(X-μ)⁴]/σ⁴ = 3 for Gaussian

**Tolerance**: ±0.1

**Why it matters**: Wrong kurtosis means incorrect probability of deep fades or peaks.

#### 5. **Chi-Squared Goodness of Fit**
The histogram should match theoretical Gaussian PDF.

**Theory**: χ² = Σ[(observed - expected)²/expected]

**Threshold**: χ² < χ²_critical at α=0.05

**Why it matters**: Overall shape validation ensures all aspects of distribution are correct.

#### 6. **I/Q Independence**
Both channels should have identical statistics.

**Theory**: Complex Gaussian requires independent, identically distributed I and Q

**Why it matters**: Non-identical channels would break the analytic signal model.

### Test Procedure

1. **Generate 30 million samples** (provides 0.0002% standard error)
2. **Bin into 500 buckets** spanning ±4σ (99.99% of samples)
3. **Compute moments**:
   - First moment: mean
   - Second moment: variance
   - Third moment: skewness
   - Fourth moment: kurtosis
4. **Chi-squared test**: Compare histogram to theoretical Gaussian
5. **Compare I vs Q**: Ensure identical distributions

### Mathematical Background

#### Gaussian PDF
```
p(x) = (1/√(2πσ²)) × exp(-(x-μ)²/2σ²)
```

For standard normal (μ=0, σ=1):
```
p(x) = (1/√(2π)) × exp(-x²/2)
```

#### Sample Statistics
```
Mean:     x̄ = Σxᵢ / N
Variance: s² = Σ(xᵢ - x̄)² / N
Skewness: γ₁ = [Σ(xᵢ - x̄)³ / N] / s³
Kurtosis: β₂ = [Σ(xᵢ - x̄)⁴ / N] / s⁴
```

#### Chi-Squared Statistic
```
χ² = Σᵢ [(Oᵢ - Eᵢ)² / Eᵢ]

Where:
  Oᵢ = observed count in bin i
  Eᵢ = expected count in bin i
  Eᵢ = N × p(xᵢ) × bin_width
```

### Expected Results

**Typical output** (should PASS):
```
=== Gaussian Noise Distribution Test ===
Generating 30000000 complex samples...

--- Mean (Expected: 0.0) ---
I channel mean: -0.000124
Q channel mean:  0.000089
  PASS - Mean within tolerance

--- Standard Deviation (Expected: 1.0) ---
I channel std dev: 1.000234 (variance: 1.000468)
Q channel std dev: 0.999876 (variance: 0.999752)
  PASS - Standard deviation correct

--- Skewness (Expected: 0.0) ---
I channel skewness: -0.001234
Q channel skewness:  0.002156
  PASS - Distribution is symmetric

--- Kurtosis (Expected: 3.0) ---
I channel kurtosis: 3.002145 (excess: 0.002145)
Q channel kurtosis: 2.998234 (excess: -0.001766)
  PASS - Kurtosis matches Gaussian

--- Chi-Squared Goodness of Fit ---
I channel χ² statistic: 243.56 (dof: 245)
Q channel χ² statistic: 238.92 (dof: 245)
Critical value (α=0.05): 278.23
  PASS - Distribution matches Gaussian

--- I/Q Channel Comparison ---
Mean difference: 0.000213
Std dev difference: 0.000358
  PASS - I and Q channels independent and identical

=== Test Result: PASSED ===
```

### Common Failures

#### 1. Mean ≠ 0
**Symptom**: Mean exceeds ±0.001
**Cause**: Bug in random number generator
**Impact**: DC offset in simulated channel

#### 2. Std Dev ≠ 1
**Symptom**: σ not close to 1.0
**Cause**: Incorrect Box-Muller transform scaling
**Impact**: Wrong signal power throughout

#### 3. Failed Chi-Squared Test
**Symptom**: χ² > critical value
**Cause**: Distribution not truly Gaussian
**Impact**: Incorrect fading statistics (wrong fade depth distribution)

#### 4. I/Q Imbalance
**Symptom**: Different statistics for I and Q
**Cause**: Bug in complex pair generation
**Impact**: Broken analytic signal, phase errors

---

## Test 2: Noise Spectrum Test

### Purpose

Validates that Gaussian noise has flat (white) spectrum and that upsampling by 2x doesn't introduce spectral artifacts or coloration.

### What It Tests

#### 1. **Spectral Flatness (±3 dB)**
Power spectral density should be constant across all frequencies.

**Theory**: White noise has S(f) = N₀/2 for all f

**Tolerance**: ±3 dB variation

**Why it matters**: Colored noise would create frequency-dependent fading, violating Rayleigh fading assumptions.

#### 2. **No Spurious Peaks**
No tones or periodic components should exist.

**Tolerance**: No peaks >6 dB above mean

**Why it matters**: Peaks indicate deterministic components (not random), causing predictable fading patterns.

#### 3. **Upsampling Integrity**
2x upsampling should preserve spectral characteristics.

**Theory**: Ideal upsampling maintains original spectrum to original Nyquist

**Why it matters**: Resampling artifacts would introduce non-physical channel effects.

#### 4. **DC and Nyquist Components**
DC and Nyquist bins should match overall spectrum.

**Tolerance**: Within ±3 dB of mean

**Why it matters**: Anomalies here indicate filter design issues.

#### 5. **Low Frequency Flatness**
0-1 kHz band should have same power as higher frequencies.

**Tolerance**: ±3 dB

**Why it matters**: 1/f noise (pink noise) would be wrong for ionospheric model.

### Test Procedure

1. **Generate Gaussian noise** at base rate (8 kHz)
2. **Upsample by 2x** using interpolation filter (to 16 kHz)
3. **Collect 64 blocks** of 65536 samples each
4. **Compute FFT** (131072 points) for each block
5. **Average power spectra** across all blocks
6. **Analyze flatness** in dB scale
7. **Search for peaks** above threshold

### Mathematical Background

#### White Noise Power Spectral Density
```
S(f) = N₀/2  (constant for all frequencies)
```

For unit variance Gaussian:
```
N₀ = 2σ² = 2
S(f) = 1  for all f
```

#### Upsampling by Factor R
```
1. Insert (R-1) zeros between samples
2. Apply lowpass filter with cutoff at original Nyquist
3. Scale by R to preserve power
```

**Result**: Original spectrum from 0 to f_s/R, images removed above.

#### Spectral Flatness (in dB)
```
Flatness = 10 log₁₀(geometric_mean / arithmetic_mean)
         = 0 dB for perfectly white noise
         < 0 dB for colored noise
```

Simplified test: Check max/min deviation from mean
```
Flatness_range = max(S(f)) - min(S(f))  (in dB)
Should be < 3 dB for white noise
```

### Expected Results

**Typical output** (should PASS):
```
=== Gaussian Noise Spectrum Test ===
Testing spectral flatness of upsampled Gaussian noise...
Upsample ratio: 2.0x
FFT size: 131072 points
Blocks to average: 64

✓ Processed 64 blocks (16777216 total bins)

--- Spectral Power Analysis ---
Mean power: 0.123456
Min power:  0.119234
Max power:  0.127891
Std dev:    0.002345

--- Spectral Flatness (dB) ---
Relative to mean:
  Min: -0.15 dB
  Max: +0.15 dB
  Std: +0.08 dB
  Range: 0.30 dB
  PASS - Spectrum is flat (white noise)

--- Spurious Peak Detection ---
  PASS - No spurious peaks detected

--- DC and Nyquist Analysis ---
DC component: -0.12 dB relative to mean
Nyquist component: +0.08 dB relative to mean

--- Low Frequency Band (0-1 kHz) ---
Average power: -0.05 dB relative to mean
  PASS - Low frequency band flat

=== Test Result: PASSED ===
```

### Common Failures

#### 1. Spectrum Not Flat
**Symptom**: Range > 3 dB
**Cause**: Colored noise (1/f, bandpass filtering)
**Impact**: Frequency-dependent fading

#### 2. Spurious Peaks
**Symptom**: Isolated high-power bins
**Cause**: Deterministic component (clock leak, interference)
**Impact**: Predictable fading patterns

#### 3. Low Frequency Roll-Off
**Symptom**: 0-1 kHz band weak
**Cause**: DC blocking or highpass filter
**Impact**: Missing slow fading components

#### 4. Nyquist Anomaly
**Symptom**: Nyquist bin significantly different
**Cause**: Aliasing or filter design error
**Impact**: Errors at edge of simulation bandwidth

---

## Test 3: Noise Power Test

### Purpose

Validates that complex Gaussian noise has correct total power (√2 for unit variance components) and that power is stable over time.

### What It Tests

#### 1. **Total RMS Power ≈ √2**
Complex noise power should be sum of I² and Q².

**Theory**: E[|z|²] = E[I²] + E[Q²] = 1 + 1 = 2, so RMS = √2

**Tolerance**: ±2%

**Why it matters**: Incorrect power would scale all channel effects wrong.

#### 2. **I/Q Power Balance**
Both channels should contribute equally.

**Theory**: E[I²] = E[Q²] = 1

**Tolerance**: ±2% ratio

**Why it matters**: Imbalance breaks analytic signal symmetry.

#### 3. **Power Stability (No Drift)**
Power should remain constant over time.

**Tolerance**: <5% drift range

**Why it matters**: Drift would cause time-varying gain errors.

#### 4. **Convergence**
Early samples should match late samples.

**Tolerance**: <1% difference

**Why it matters**: Slow convergence indicates startup transients or correlation.

#### 5. **Power Variance**
Block-to-block variation should match theory.

**Theory**: std(RMS) ≈ RMS/√(2N) for N samples

**Why it matters**: Excessive variance indicates correlation or periodicity.

### Test Procedure

1. **Generate noise in 500 blocks** of 16384 samples
2. **Compute RMS per block** with running average
3. **Track power history** for stability analysis
4. **Compare I vs Q power** for balance
5. **Analyze convergence** (first 10% vs last 10%)
6. **Check variance** against theoretical prediction

### Mathematical Background

#### Complex Gaussian Power
```
For z = I + jQ where I, Q ~ N(0,1):

Total power:     P = E[|z|²] = E[I² + Q²]
                   = E[I²] + E[Q²]
                   = σ²_I + σ²_Q
                   = 1 + 1 = 2

RMS power:       RMS = √P = √2 ≈ 1.41421

Per-component:   RMS_I = √(E[I²]) = 1
                 RMS_Q = √(E[Q²]) = 1
```

#### Normalized Per-Component RMS
```
When reporting per-component:
RMS_normalized = √(P/2) = √((I² + Q²)/2) = 1

This is what the test reports as "RMS per component"
```

#### Convergence Rate
```
Sample mean: X̄ₙ = (1/N) Σᵢ₌₁ᴺ Xᵢ

Standard error: SE = σ/√N

For σ=1, N=8,192,000:
SE = 1/√8192000 ≈ 0.00035  (0.035%)
```

#### Block Variance
```
For blocks of N samples:
σ_block = σ_sample / √N
        = 1 / √16384
        ≈ 0.0078

Coefficient of variation: CV = σ/μ = 0.0078/1.0 ≈ 0.78%
```

### Expected Results

**Typical output** (should PASS):
```
=== Gaussian Noise Power Test ===
Testing power stability and convergence...
Total samples: 8192000 (8.2 million)
Blocks: 500 × 16384 samples

  Block 100/500: RMS = 1.000234
  Block 200/500: RMS = 0.999876
  Block 300/500: RMS = 1.000123
  Block 400/500: RMS = 1.000045
  Block 500/500: RMS = 1.000012

✓ Processed 500 blocks

--- Final Power Values ---
Per-component RMS: 1.000012 (expected: 1.000000)
I channel RMS: 0.999987
Q channel RMS: 1.000038
Total RMS: 1.414229 (expected: 1.414214)

Per-component error: 0.00%
Total power error: 0.00%
  PASS - Power correct

--- I/Q Power Balance ---
I/Q power ratio: 0.999949
Balance error: 0.01%
  PASS - I and Q channels balanced

--- Power Stability ---
Min RMS: 0.999234
Max RMS: 1.000876
Mean RMS: 1.000045
Drift range: 0.16%
  PASS - Power is stable

--- Convergence Analysis ---
Early RMS (first 10%): 1.000234
Late RMS (last 10%): 1.000012
Convergence: 0.022% change
  PASS - Good convergence

--- Power Variability ---
Standard deviation: 0.000789
Coefficient of variation: 0.08%
Theoretical std (per block): 0.007812
  PASS - Variance within expected range

=== Test Result: PASSED ===
```

### Common Failures

#### 1. Wrong Total Power
**Symptom**: RMS ≠ √2
**Cause**: Scaling error in noise generation
**Impact**: All channel effects scaled incorrectly

#### 2. I/Q Imbalance
**Symptom**: I and Q have different RMS
**Cause**: Asymmetric Box-Muller implementation
**Impact**: Phase errors in channel simulation

#### 3. Power Drift
**Symptom**: Power changes significantly over time
**Cause**: Accumulator overflow or PRNG period
**Impact**: Time-varying gain errors

#### 4. Poor Convergence
**Symptom**: Early vs late power differs by >1%
**Cause**: Startup transients or long-term correlation
**Impact**: Unreliable initial simulation period

#### 5. Excessive Variance
**Symptom**: Block variance >> theoretical
**Cause**: Correlation between samples (not independent)
**Impact**: Non-Rayleigh fading statistics

---

## Implementation Details

### Box-Muller Transform

The `Noise` class likely uses the Box-Muller transform to generate Gaussian random variables from uniform random variables:

```c
// Generate two uniform random numbers U₁, U₂ ~ Uniform(0,1)
float u1 = rand() / RAND_MAX;
float u2 = rand() / RAND_MAX;

// Box-Muller transform
float R = sqrt(-2.0 * log(u1));
float theta = 2.0 * M_PI * u2;

// Two independent Gaussian samples
float z1 = R * cos(theta);  // I component
float z2 = R * sin(theta);  // Q component

// Both z1, z2 ~ N(0,1)
```

**Why it works**:
- R has Rayleigh distribution
- θ is uniformly distributed [0, 2π]
- Polar coordinates (R, θ) → Cartesian (z1, z2) gives Gaussian pair

### Statistical Power Calculations

**Complex Noise Power**:
```c
ComplexPair p = [noise gaussianPair];
float power = p.re * p.re + p.im * p.im;  // Instantaneous power
```

**Running Average**:
```c
double sum = 0;
for (int i = 0; i < N; i++) {
    sum += power[i];
}
double average_power = sum / N;
double rms = sqrt(average_power);
```

**Per-Component vs Total**:
```c
// Per-component (normalized):
rms_per_component = sqrt((I² + Q²) / 2) = 1.0

// Total (unnormalized):
rms_total = sqrt(I² + Q²) = √2 ≈ 1.41421
```

---

## Integration with HF Channel Simulator

### Where Noise is Used

1. **Doppler Spreading (`Spread` class)**
   - Gaussian noise filtered to create time-varying fading
   - Spectrum shape determines fade rate
   - These tests ensure proper Rayleigh fading statistics

2. **AWGN (Additive White Gaussian Noise)**
   - Noise added to signal for SNR control
   - Spectral flatness ensures uniform noise floor
   - Power test ensures correct SNR calculations

3. **Phase Noise**
   - Small perturbations in oscillator phase
   - Gaussian distribution critical for phase accuracy

### Importance for Channel Simulation

**Rayleigh Fading**: Requires properly distributed Gaussian noise
```
If noise is N(0,1), then:
- Amplitude has Rayleigh distribution
- Phase is uniformly distributed
- Deep fades occur at predictable rate
```

**SNR Calculations**: Require accurate noise power
```
SNR = Signal_Power / Noise_Power
If Noise_Power ≠ σ², all SNR measurements wrong
```

**Statistical Validity**: ITU-R standards assume Gaussian statistics
```
ITU-R F.1487 models validated against real HF channels
If noise isn't Gaussian, simulations don't match reality
```

---

## Troubleshooting Guide

### Test 1 (Distribution) Failures

**Problem**: Mean ≠ 0
- Check PRNG initialization
- Verify Box-Muller constant values (2π, etc.)
- Look for integer overflow in calculations

**Problem**: Failed chi-squared test
- Increase sample count (reduce statistical noise)
- Check histogram binning (off-by-one errors?)
- Verify Gaussian PDF calculation

### Test 2 (Spectrum) Failures

**Problem**: Low frequencies weak
- Check for DC blocking capacitor
- Verify no highpass filtering
- Look for differentiation operations

**Problem**: Spurious peaks
- Check for deterministic components
- Verify PRNG quality (long period?)
- Look for interference from system clocks

### Test 3 (Power) Failures

**Problem**: Power drift
- Check for accumulator overflow (use double precision)
- Verify PRNG doesn't cycle
- Look for thermal/voltage effects if on hardware

**Problem**: Slow convergence
- Increase block size
- Check for sample correlation
- Verify PRNG independence

---

## References

### Statistical Theory
- Box, G. E. P. and Muller, M. E., "A Note on the Generation of Random Normal Deviates," *Annals Math. Stat.*, 1958.
- Knuth, D. E., "The Art of Computer Programming, Volume 2: Seminumerical Algorithms," 3rd ed., Addison-Wesley, 1997.

### Hypothesis Testing
- Pearson, K., "On the criterion that a given system of deviations from the probable in the case of a correlated system of variables is such that it can be reasonably supposed to have arisen from random sampling," *Phil. Mag.*, 1900.
- D'Agostino, R. B. and Stephens, M. A., "Goodness-of-Fit Techniques," Marcel Dekker, 1986.

### Digital Signal Processing
- Oppenheim, A. V. and Schafer, R. W., "Discrete-Time Signal Processing," 3rd ed., Prentice Hall, 2009.
- Proakis, J. G. and Manolakis, D. G., "Digital Signal Processing," 4th ed., Pearson, 2006.

### HF Propagation
- ITU-R Recommendation F.1487: "Testing of HF modems with bandwidths of up to about 12 kHz using ionospheric channel simulators"
- Davies, K., "Ionospheric Radio," Peter Peregrinus, 1990.

---

**Last Updated**: Based on comprehensive noise validation implementation  
**Maintainer**: HF Channel Simulator Project  
**License**: Copyright 2008 Kok Chen, W7AY. All rights reserved.
