129 lines
3.2 KiB
Python
129 lines
3.2 KiB
Python
#!/bin/env python3
|
|
|
|
from fractions import Fraction
|
|
from math import floor, nan
|
|
|
|
import matplotlib.pyplot as plt
|
|
import numpy as np
|
|
|
|
|
|
BANDS = [
|
|
(1_810_000, 2_000_000),
|
|
(3_500_000, 3_800_000),
|
|
(5_351_500, 5_366_500),
|
|
(7_000_000, 7_200_000),
|
|
(10_100_000, 10_150_000),
|
|
(14_000_000, 14_350_000),
|
|
(18_068_000, 18_168_000),
|
|
(21_000_000, 21_450_000),
|
|
(24_890_000, 24_990_000),
|
|
(28_000_000, 29_700_000)
|
|
]
|
|
|
|
|
|
XTAL_FREQ = 25_000_000
|
|
|
|
TARGET_PHASE = 90.0
|
|
|
|
# Official limit is 600MHz, however https://rfzero.net/tutorials/si5351a/ claims that 420Mhz is also okay
|
|
PLL_MIN = 420_000_000
|
|
PLL_MAX = 900_000_000
|
|
|
|
|
|
def calc_pll_params(in_freq, out_freq):
|
|
MAX_DENOM = 0x0FFFFF
|
|
|
|
divider = Fraction(floor(in_freq), floor(out_freq))
|
|
divider = divider.limit_denominator(MAX_DENOM)
|
|
|
|
a = floor(divider.numerator / divider.denominator)
|
|
b = divider.numerator % divider.denominator
|
|
c = divider.denominator
|
|
|
|
return a, b, c
|
|
|
|
|
|
def calc_error(freq, offset):
|
|
phase_offset_sec = 1.0 / freq * TARGET_PHASE / 360.0
|
|
target_pll_freq = offset / (4 * phase_offset_sec)
|
|
|
|
if target_pll_freq >= PLL_MIN and target_pll_freq <= PLL_MAX:
|
|
#print("\tVCO target for %d: %f" % (offset, target_pll_freq / 10**6))
|
|
|
|
pll_a, pll_b, pll_c = calc_pll_params(XTAL_FREQ, target_pll_freq)
|
|
pll_actual_freq = XTAL_FREQ / (pll_a + pll_b/pll_c)
|
|
|
|
#print("\tVCO actual for %d: %f" % (offset, target_pll_freq / 10**6))
|
|
|
|
ms_a, ms_b, ms_c = calc_pll_params(target_pll_freq, freq)
|
|
ms_actual_freq = pll_actual_freq / (ms_a + ms_b/ms_c)
|
|
|
|
err = ms_actual_freq - freq
|
|
#print("\tError: %f" % (ms_actual_freq - freq))
|
|
|
|
return err, target_pll_freq
|
|
|
|
return None, None
|
|
|
|
|
|
def main():
|
|
freqs = []
|
|
errs = []
|
|
pll_freqs = []
|
|
phases = []
|
|
|
|
for start, end in BANDS:
|
|
print("Band %d - %d" % (start, end))
|
|
|
|
min_err = None
|
|
min_err_phase = None
|
|
for phase in range(1,128):
|
|
err_sum = 0
|
|
for freq in range(start, end, 100):
|
|
err, pll_freq = calc_error(freq, phase)
|
|
if pll_freq is None:
|
|
break
|
|
err_sum += err
|
|
|
|
if min_err is None or err_sum <= min_err:
|
|
min_err = err_sum
|
|
min_err_phase = phase
|
|
|
|
if min_err_phase is None:
|
|
print("\tNo suitable PLL freq found for %d" % freq)
|
|
continue
|
|
|
|
print("Minimal error phase offset for band: %d" % (min_err_phase))
|
|
|
|
for freq in range(start, end, 1):
|
|
err, pll_freq = calc_error(freq, min_err_phase)
|
|
if pll_freq is None:
|
|
print("\tNo suitable PLL freq found for %d" % freq)
|
|
break
|
|
|
|
freqs += [freq]
|
|
pll_freqs += [pll_freq]
|
|
phases += [min_err_phase]
|
|
errs += [err]
|
|
|
|
freqs += [nan]
|
|
pll_freqs += [nan]
|
|
phases += [nan]
|
|
errs += [nan]
|
|
|
|
|
|
freqs = np.array(freqs) / 10**6
|
|
pll_freqs = np.array(pll_freqs) / 10**6
|
|
phases = np.array(phases)
|
|
errs = np.abs(np.array(errs))
|
|
|
|
fig, (ax1, ax2, ax3) = plt.subplots(3, 1)
|
|
ax1.plot(freqs, phases)
|
|
ax2.plot(freqs, pll_freqs)
|
|
ax3.plot(freqs, errs)
|
|
plt.show()
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|