"""Synthetic zero-phase cosine sampling; extends engineering_labs.py::aliasing. Time window [0, 2) s; ideal instantaneous samples, no noise or filtering. Run locally: python3 experiment.py --frequency 12 --sample-rate 20 """ import math import time def run(name, p): if name != 'sampling': raise ValueError('Unknown experiment') if set(p) != {'frequency', 'sample_rate'}: raise ValueError('Unexpected parameters') f, fs = p['frequency'], p['sample_rate'] if any(type(v) not in (int, float) or not math.isfinite(v) for v in (f, fs)): raise ValueError('Finite numbers required') if not (0 <= f <= 40 and 2 <= fs <= 100 and fs == int(fs)): raise ValueError('Parameters out of range') start = time.perf_counter() folded = abs((f + fs / 2) % fs - fs / 2) samples = [n / fs for n in range(int(2 * fs))] original_samples = [math.cos(2 * math.pi * f * t) for t in samples] folded_samples = [math.cos(2 * math.pi * folded * t) for t in samples] max_difference = max(abs(a - b) for a, b in zip(original_samples, folded_samples)) # Dense curves are only a visual reference; metrics use the actual sample times. times = [n / 2000 for n in range(4000)] def series(ident, x, y): return dict(id=ident, x=x, y=y, x_unit='s', y_unit='normalized') return dict( metrics=[dict(id='folded_frequency_hz', value=folded, unit='Hz'), dict(id='nyquist_hz', value=fs / 2, unit='Hz'), dict(id='max_sample_difference', value=max_difference, unit='normalized'), dict(id='sample_count', value=len(samples), unit='count')], series=[series('original', times, [math.cos(2 * math.pi * f * t) for t in times]), series('folded', times, [math.cos(2 * math.pi * folded * t) for t in times]), series('samples', samples, original_samples)], stdout='', provenance=dict(data_type='synthetic', compute_ms=(time.perf_counter()-start)*1000)) if __name__ == '__main__': import argparse, json parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('--frequency', type=float, default=12) parser.add_argument('--sample-rate', type=int, default=20) args = parser.parse_args() print(json.dumps(run('sampling', dict(frequency=args.frequency, sample_rate=args.sample_rate)), allow_nan=False))