"""Tests for Apollo USB system constants — verify timing relationships. Every assertion here validates a relationship from IMPLEMENTATION_SPEC.md. If any of these fail, the entire demod chain is built on wrong assumptions. """ from apollo.constants import ( COHERENT_RATIO, DOWNLINK_FREQ_HZ, MASTER_CLOCK_HZ, PCM_HIGH_BIT_RATE, PCM_HIGH_CLOCK_DIVIDER, PCM_HIGH_FRAMES_PER_SEC, PCM_HIGH_WORD_RATE, PCM_HIGH_WORDS_PER_FRAME, PCM_LOW_BIT_RATE, PCM_LOW_CLOCK_DIVIDER, PCM_LOW_FRAMES_PER_SEC, PCM_LOW_WORD_RATE, PCM_LOW_WORDS_PER_FRAME, PCM_SUBCARRIER_HZ, PCM_WORD_LENGTH, SAMPLE_RATE_BASEBAND, SCO_DEVIATION_PERCENT, SCO_FREQUENCIES, SUBFRAME_FRAMES, UPLINK_FREQ_HZ, VCO_REFERENCE_HZ, ) class TestTimingHierarchy: """Verify the 512 kHz master clock divider chain (IMPL_SPEC section 5.5).""" def test_high_rate_bit_clock(self): assert MASTER_CLOCK_HZ / PCM_HIGH_CLOCK_DIVIDER == PCM_HIGH_BIT_RATE def test_low_rate_bit_clock(self): assert MASTER_CLOCK_HZ / PCM_LOW_CLOCK_DIVIDER == PCM_LOW_BIT_RATE def test_high_rate_word_rate(self): assert PCM_HIGH_BIT_RATE / PCM_WORD_LENGTH == PCM_HIGH_WORD_RATE def test_low_rate_word_rate(self): assert PCM_LOW_BIT_RATE / PCM_WORD_LENGTH == PCM_LOW_WORD_RATE def test_high_rate_frame_rate(self): assert PCM_HIGH_WORD_RATE / PCM_HIGH_WORDS_PER_FRAME == PCM_HIGH_FRAMES_PER_SEC def test_low_rate_frame_rate(self): assert PCM_LOW_WORD_RATE / PCM_LOW_WORDS_PER_FRAME == PCM_LOW_FRAMES_PER_SEC def test_pcm_subcarrier_is_doubled_clock(self): """PCM subcarrier = 512 kHz × 2 = 1.024 MHz.""" assert PCM_SUBCARRIER_HZ == MASTER_CLOCK_HZ * 2 def test_subframe_duration(self): """50 frames × 19.968 ms ≈ 1 second (high rate).""" frame_period = 1.0 / PCM_HIGH_FRAMES_PER_SEC subframe_period = SUBFRAME_FRAMES * frame_period assert abs(subframe_period - 1.0) < 0.01 # within 1% def test_high_rate_bits_per_frame(self): """128 words × 8 bits = 1024 bits per frame.""" assert PCM_HIGH_WORDS_PER_FRAME * PCM_WORD_LENGTH == 1024 def test_low_rate_bits_per_frame(self): """200 words × 8 bits = 1600 bits per frame.""" assert PCM_LOW_WORDS_PER_FRAME * PCM_WORD_LENGTH == 1600 class TestFrequencyRelationships: """Verify RF frequency relationships (IMPL_SPEC section 2.1).""" def test_coherent_ratio(self): """Downlink = Uplink × 240/221 (within rounding).""" expected = UPLINK_FREQ_HZ * COHERENT_RATIO[0] / COHERENT_RATIO[1] assert abs(DOWNLINK_FREQ_HZ - expected) < 1 # within 1 Hz def test_vco_multiplier_chain(self): """VCO × 2 × 2 × 5 × 3 × 2 × 2 = 2287.5 MHz (section 2.3).""" # 19.0625 × 2 × 2 × 5 × 3 × 2 × 2 = 19.0625 × 240 = not quite right # Actually: 76.25 MHz modulated, then ×2 ×5 ×3 = ×30, giving 2287.5 # VCO × 4 = 76.25, then ×30 = 2287.5 modulated_freq = VCO_REFERENCE_HZ * 4 # 76.25 MHz assert modulated_freq == 76_250_000 tx_freq = modulated_freq * 30 # ×2 ×5 ×3 assert tx_freq == DOWNLINK_FREQ_HZ class TestSampleRateRelationships: """Verify sample rate choices produce integer relationships.""" def test_baseband_is_10x_master_clock(self): assert SAMPLE_RATE_BASEBAND == MASTER_CLOCK_HZ * 10 def test_samples_per_pcm_subcarrier_cycle(self): """5.12 MHz / 1.024 MHz = exactly 5 samples/cycle.""" spc = SAMPLE_RATE_BASEBAND / PCM_SUBCARRIER_HZ assert spc == 5.0 def test_samples_per_high_rate_bit(self): """5.12 MHz / 51.2 kHz = exactly 100 samples/bit.""" spb = SAMPLE_RATE_BASEBAND / PCM_HIGH_BIT_RATE assert spb == 100.0 class TestSCOFrequencies: """Verify SCO channel specs (IMPL_SPEC section 4.3).""" def test_nine_channels(self): assert len(SCO_FREQUENCIES) == 9 def test_monotonically_increasing(self): freqs = [SCO_FREQUENCIES[i] for i in range(1, 10)] for i in range(len(freqs) - 1): assert freqs[i] < freqs[i + 1] def test_deviation_ranges(self): """Each SCO deviates ±7.5% from center.""" for ch, center in SCO_FREQUENCIES.items(): low = center * (1.0 - SCO_DEVIATION_PERCENT / 100.0) high = center * (1.0 + SCO_DEVIATION_PERCENT / 100.0) # Cross-check with IMPL_SPEC table values if ch == 1: assert abs(low - 13_412) < 1 assert abs(high - 15_588) < 1 elif ch == 9: assert abs(low - 152_625) < 1 assert abs(high - 177_375) < 1