mirror of
https://gitlab.science.ru.nl/mthesis-edeboone/m.internship-documentation.git
synced 2024-12-24 22:13:33 +01:00
Fig: WR clocks and PPS
This commit is contained in:
parent
47e5f36cd2
commit
f463b291bf
1 changed files with 150 additions and 0 deletions
150
figures/white-rabbit/src/wr-clocks.py
Executable file
150
figures/white-rabbit/src/wr-clocks.py
Executable file
|
@ -0,0 +1,150 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
__doc__ = \
|
||||
"""Generate some figures showing the alignment of clocks
|
||||
in a White Rabbit system with GrandMaster setup.
|
||||
"""
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
import scipy.signal as sig
|
||||
rng = np.random.default_rng(12345)
|
||||
|
||||
### Functions
|
||||
def pps(t, t_start, width=0.5):
|
||||
"""
|
||||
Generate a PPS with width $width$ and starting at $t_start.
|
||||
"""
|
||||
|
||||
return (t > t_start) & (t < t_start + width)
|
||||
|
||||
|
||||
def detect_rising_edges(threshold, data):
|
||||
"""
|
||||
Detect rising edges in data.
|
||||
|
||||
https://stackoverflow.com/a/50365462
|
||||
"""
|
||||
return np.flatnonzero((data[:-1] < threshold) & (data[1:] > threshold))+1
|
||||
|
||||
|
||||
|
||||
def first_shared_edge(x1, x2, threshold=0.3):
|
||||
try:
|
||||
length = len(x2)
|
||||
except TypeError:
|
||||
length = 1
|
||||
|
||||
x1_edges = detect_rising_edges(threshold, x1)
|
||||
if length > 1:
|
||||
x2_edges = detect_rising_edges(threshold, x2)
|
||||
else:
|
||||
x2_edges = x2
|
||||
|
||||
start_edge = x1_edges[x1_edges > x2_edges][0]
|
||||
|
||||
return start_edge
|
||||
|
||||
|
||||
def aligned_pps(t, clock_in, pps_in, width=None):
|
||||
t_start = t[first_shared_edge(clock_in, pps_in)]
|
||||
|
||||
if width is not None:
|
||||
return pps(t, t_start, width)
|
||||
else:
|
||||
return pps(t, t_start)
|
||||
|
||||
## Main
|
||||
def main(time_base = 10e-9):
|
||||
"""
|
||||
Generate a figure showing the required GrandMaster inputs
|
||||
with an aligned PPS out and DIO clock,
|
||||
and a random input event and its timestamp.
|
||||
"""
|
||||
|
||||
clock_freq = 10e6 # Hz
|
||||
dio_freq = 12.5*clock_freq # Hz
|
||||
|
||||
pps_in_early = -1.7/clock_freq #s
|
||||
pps_in_width = 10e1/clock_freq #s
|
||||
pps_out_width = 10e1/clock_freq #s
|
||||
|
||||
t = np.linspace(-2.25*1/clock_freq, 0.8*1/clock_freq, 5000) #
|
||||
|
||||
random_event_idx = rng.integers(len(t)*2/3, len(t)) # Somewhere within the time space
|
||||
random_event = t[random_event_idx]
|
||||
|
||||
|
||||
## Create Grandmaster input signals
|
||||
clock_in = (sig.square(2*np.pi*clock_freq*t)+1)/2
|
||||
pps_in = pps(t, pps_in_early, pps_in_width)
|
||||
|
||||
## Determine output signal
|
||||
clock_alignment = t[first_shared_edge(clock_in, pps_in)]
|
||||
pps_out = aligned_pps(t, clock_in, pps_in, width=pps_out_width)
|
||||
dio = (sig.square(2*np.pi*dio_freq*(t - clock_alignment))+1)/2
|
||||
|
||||
## Random event timestamp
|
||||
timestamped_event = t[first_shared_edge(dio, random_event_idx)]
|
||||
|
||||
# Create the figure
|
||||
fig, axs = plt.subplots(4,1, sharex=True, gridspec_kw={'hspace': 0}, figsize=(16,4))
|
||||
|
||||
## Plot signals
|
||||
i=0
|
||||
axs[i].set_ylabel("$\mathrm{PPS}_\mathrm{in}$", rotation='horizontal', ha='right', va='center')
|
||||
axs[i].plot(t, pps_in, 'purple', label="PPS in")
|
||||
|
||||
i+=1
|
||||
axs[i].set_ylabel("GM Clock\n($10\,\mathrm{MHz}$)", rotation='horizontal', ha='right', va='center')
|
||||
axs[i].plot(t, clock_in, label='10MHz in')
|
||||
|
||||
i+=1
|
||||
axs[i].set_ylabel("FMC DIO\n($125\,MHz$)", rotation='horizontal', ha='right', va='center')
|
||||
axs[i].plot(t, dio, 'y', label='DIO')
|
||||
axs[i].plot(random_event, 0.5, 'r*')
|
||||
axs[i].axvline(timestamped_event, color='b')
|
||||
|
||||
i+=1
|
||||
axs[i].set_ylabel("$\mathrm{PPS}_\mathrm{out}$", rotation='horizontal', ha='right', va='center')
|
||||
axs[i].plot(t, pps_out, 'g', label="PPS out")
|
||||
|
||||
|
||||
## Styling
|
||||
for ax in axs:
|
||||
ax.axvline(clock_alignment, color='r', linestyle='--')
|
||||
ax.set_ylim(-0.2, 1.2)
|
||||
ax.set_yticks([])
|
||||
ax.set_yticklabels([])
|
||||
ax.grid()
|
||||
|
||||
if time_base == 10e-9:
|
||||
axs[-1].set_xlabel("Time (ns)")
|
||||
|
||||
ticks = axs[-1].get_xticks()/time_base
|
||||
axs[-1].set_xticklabels(np.floor(ticks))
|
||||
else:
|
||||
axs[-1].set_xlabel("Time (s)")
|
||||
|
||||
return fig, (clock_alignment, random_event, timestamped_event)
|
||||
|
||||
if __name__ == "__main__":
|
||||
from argparse import ArgumentParser
|
||||
import os.path as path
|
||||
|
||||
parser = ArgumentParser(description=__doc__)
|
||||
parser.add_argument("fname", metavar="path/to/figure[/]", nargs="?", help="Location for generated figure, will append __file__ if a directory. If not supplied, figure is shown.")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.fname is not None and path.isdir(args.fname):
|
||||
args.fname = path.join(args.fname, path.splitext(path.basename(__file__))[0] + ".pdf")
|
||||
|
||||
###
|
||||
fig, _ = main()
|
||||
|
||||
if args.fname is not None:
|
||||
plt.savefig(args.fname)
|
||||
else:
|
||||
plt.show()
|
||||
|
Loading…
Reference in a new issue