#!/usr/bin/env python3 """ Apollo Test Signal Generator Demo — pure Python, no GNU Radio needed. Generates synthetic USB baseband signals and analyzes them spectrally. Useful for verifying the signal generator and understanding the signal structure. Usage: uv run python examples/test_signal_gen_demo.py """ import numpy as np from apollo.constants import ( PCM_HIGH_BIT_RATE, PCM_HIGH_WORDS_PER_FRAME, PCM_SUBCARRIER_HZ, PCM_WORD_LENGTH, SAMPLE_RATE_BASEBAND, VOICE_SUBCARRIER_HZ, ) from apollo.usb_signal_gen import generate_usb_baseband def main(): print("Apollo USB Signal Generator Demo") print("=" * 50) # Generate a clean signal (no noise) print("\n1. Clean PCM-only signal (3 frames):") signal, bits = generate_usb_baseband(frames=3, snr_db=None) frame_bits = PCM_HIGH_WORDS_PER_FRAME * PCM_WORD_LENGTH expected_samples = 3 * int(frame_bits * SAMPLE_RATE_BASEBAND / PCM_HIGH_BIT_RATE) print(f" Samples: {len(signal)} (expected {expected_samples})") print(f" Duration: {len(signal)/SAMPLE_RATE_BASEBAND*1000:.1f} ms") print(f" Envelope std: {np.std(np.abs(signal)):.4f} (PM = near-constant)") # Analyze spectrum print("\n2. Spectral analysis:") fft = np.fft.fft(signal[:50000]) freqs = np.fft.fftfreq(50000, 1.0 / SAMPLE_RATE_BASEBAND) power = np.abs(fft) ** 2 # Check PCM subcarrier band pcm_mask = (np.abs(freqs) > 950_000) & (np.abs(freqs) < 1_100_000) pcm_power = np.mean(power[pcm_mask]) total_power = np.mean(power) print(f" PCM band (950-1100 kHz): {10*np.log10(pcm_power/total_power):.1f} dB re total") # Generate with voice print("\n3. Signal with voice subcarrier:") signal_v, _ = generate_usb_baseband(frames=3, voice_enabled=True, snr_db=None) fft_v = np.fft.fft(signal_v[:50000]) voice_mask = (np.abs(freqs) > 1_200_000) & (np.abs(freqs) < 1_300_000) voice_power = np.mean(np.abs(fft_v[voice_mask]) ** 2) print(f" Voice band (1.2-1.3 MHz): {10*np.log10(voice_power/total_power):.1f} dB re total") # Generate with noise print("\n4. Signal with 20 dB SNR noise:") signal_n, _ = generate_usb_baseband(frames=3, snr_db=20.0) print(f" Envelope std: {np.std(np.abs(signal_n)):.4f} (noisy = higher variance)") print("\nKey frequencies:") print(f" Sample rate: {SAMPLE_RATE_BASEBAND/1e6:.2f} MHz") print(f" PCM subcarrier: {PCM_SUBCARRIER_HZ/1e6:.3f} MHz") print(f" Voice subcarrier: {VOICE_SUBCARRIER_HZ/1e6:.3f} MHz") print(f" PCM bit rate: {PCM_HIGH_BIT_RATE/1000:.1f} kbps") if __name__ == "__main__": main()