"""Generate two independent noiseless FMCW single-target frames (500 + 12)."""
import argparse
import json
from pathlib import Path
import numpy as np


def main():
    root = Path(__file__).resolve().parent
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument('--out', type=Path, required=True)
    args = parser.parse_args()
    config = json.loads((root / 'radar_config.json').read_text(encoding='utf-8'))
    nfft, count = config['fft_length'], config['valid_samples']
    # These dimensions/scaling are also fixed in the RTL and its testbench.
    if (nfft, count, config['fft_shift_bits'], len(config['cases'])) != (512, 500, 10, 2):
        raise ValueError('Update RTL and testbench before changing frame layout or scaling')
    slope = config['bandwidth_hz'] / config['chirp_duration_s']
    t = np.arange(count) / config['sample_rate_hz']
    i = np.zeros((2, nfft), dtype=np.int64)
    q = np.zeros_like(i)
    for case_id, case in enumerate(config['cases']):
        fb = 2 * slope * case['range_m'] / config['c_m_s']
        if not 0 < fb < config['sample_rate_hz'] / 2:
            raise ValueError('Beat frequency must be in the positive search band')
        z = case['amplitude'] * np.exp(2j * np.pi * fb * t)
        for dest, component in ((i, z.real), (q, z.imag)):
            rounded = np.rint(component * 2**15)
            if np.any(rounded < -32768) or np.any(rounded > 32767):
                raise ValueError('Q15 saturation in generated input')
            dest[case_id, :count] = np.clip(rounded, -32768, 32767).astype(np.int64)
        print(f'case={case_id} R={case["range_m"]} m fb={fb:.6f} Hz bin={fb*nfft/config["sample_rate_hz"]:.6f}')
    words = ((q & 0xffff) << 16) | (i & 0xffff)
    args.out.mkdir(parents=True, exist_ok=True)
    (args.out / 'tone_q15.mem').write_text(''.join(f'{w:08x}\n' for w in words.flat), encoding='ascii')
    ref = np.fft.fft(i + 1j*q, axis=1) / 2**config['fft_shift_bits']
    np.savez(args.out / 'reference.npz', i=i, q=q, fft=ref, config=json.dumps(config))


if __name__ == '__main__':
    main()
