PCM 문서의 검증 코드다. if __name__ == "__main__": 아래가 자체 테스트다.

"""PCM(펄스 부호 변조) 계산 검증.

문서: 38.PCM (예시, 정의, 카드 C2·C3)
주장:
  1. 문서 예시의 표본 3.0, 1.4, 6.2, 1.3, 2.8, 5.9, 4.1을 가장 가까운 정수로 반올림하면
     3, 1, 6, 1, 3, 6, 4이고, 3비트로 적으면 011001110001011110100이다.
  2. 4 kHz 음성 -> 표본 8,000개/초, 표본마다 8비트 -> 64 kbps.
  3. 표본마다 n비트면 단계는 2^n개. 3비트 8단계, 8비트 256단계.
  4. 표본을 촘촘히 하거나 비트를 늘리면 데이터 속도가 그만큼 는다.
"""

SAMPLES = [3.0, 1.4, 6.2, 1.3, 2.8, 5.9, 4.1]


def quantize(x, levels):
    q = int(x + 0.5)              # 가장 가까운 정수 (이 예에는 .5가 없다)
    return min(max(q, 0), levels - 1)


def pcm(samples, bits):
    codes = [quantize(x, 2 ** bits) for x in samples]
    return codes, "".join(format(c, f"0{bits}b") for c in codes)


def bit_rate(highest_hz, bits_per_sample):
    sampling_rate = 2 * highest_hz
    return sampling_rate, sampling_rate * bits_per_sample


def main():
    codes, stream = pcm(SAMPLES, 3)
    assert codes == [3, 1, 6, 1, 3, 6, 4]
    assert stream == "011001110001011110100"
    errors = [round(x - c, 1) for x, c in zip(SAMPLES, codes)]
    assert max(abs(e) for e in errors) <= 0.5
    print("[OK] 문서 예시: 표본", SAMPLES, "-> 단계", codes, "->", stream)
    print("     반올림 오차", errors, "(모두 0.5 이하)")

    fs, r = bit_rate(4_000, 8)
    assert (fs, r) == (8_000, 64_000)
    print("[OK] 전화 음성: 4 kHz -> 8,000 표본/초 x 8비트 = 64,000 비트/초")

    assert 2 ** 3 == 8 and 2 ** 8 == 256
    print("[OK] 3비트 8단계, 8비트 256단계")

    # 카드 C2: 표본 2.2, 5.7, 0.4, 7.0을 3비트로
    codes2, stream2 = pcm([2.2, 5.7, 0.4, 7.0], 3)
    assert codes2 == [2, 6, 0, 7] and stream2 == "010110000111"
    print("[OK] 카드 C2: 2.2, 5.7, 0.4, 7.0 -> 2, 6, 0, 7 -> 010 110 000 111")

    # 카드 C3: 최고 주파수 20 kHz(음악), 16비트 -> 40,000 표본/초 x 16 = 640 kbps (한 채널)
    assert bit_rate(20_000, 16) == (40_000, 640_000)
    # 표본 비트를 8 -> 16으로 늘리면 데이터 속도 2배
    assert bit_rate(4_000, 16)[1] == 2 * bit_rate(4_000, 8)[1]
    print("[OK] 카드 C3: 20 kHz, 16비트 -> 640 kbps. 비트 2배면 속도 2배")
    print("ALL CHECKS PASSED")


if __name__ == "__main__":
    main()