forked from M-Labs/artiq
examples: add TDR toy example
This commit is contained in:
parent
90368415a6
commit
278570faf6
|
@ -0,0 +1,75 @@
|
|||
from artiq import *
|
||||
|
||||
|
||||
class PulseNotReceivedError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class TDR(EnvExperiment):
|
||||
"""Time domain reflectometer.
|
||||
|
||||
From ttl2 an impedance matched pulse is send onto a coax
|
||||
cable with an open end. pmt0 (very short stub, high impedance) also
|
||||
listens on the transmission line near ttl2.
|
||||
|
||||
When the forward propagating pulse passes pmt0, the voltage is half of the
|
||||
logic voltage and does not register as a rising edge. Once the
|
||||
rising edge is reflected at an open end (same sign) and passes by pmt0 on
|
||||
its way back to ttl2, it is detected. Analogously, hysteresis leads to
|
||||
detection of the falling edge once the reflection reaches pmt0 after
|
||||
one round trip time.
|
||||
|
||||
This works marginally and is just a proof of principle: it relies on
|
||||
hysteresis at FPGA inputs around half voltage and good impedance steps,
|
||||
as well as reasonably low loss cable. It does not work well for longer
|
||||
cables (>100 ns RTT). The default drive strength of 12 mA and 3.3 V would
|
||||
be ~300 Ω but it seems 40 Ω series impedance at the output matches
|
||||
the hysteresis of the input.
|
||||
|
||||
This is also equivalent to a loopback tester or a delay measurement.
|
||||
"""
|
||||
def build(self):
|
||||
self.attr_device("core")
|
||||
self.attr_device("pmt0")
|
||||
self.attr_device("ttl2")
|
||||
|
||||
def run(self):
|
||||
n = 1000 # repetitions
|
||||
latency = 50e-9 # calibrated latency without a transmission line
|
||||
pulse = 1e-6 # pulse length, larger than rtt
|
||||
try:
|
||||
self.many(n, seconds_to_mu(pulse, self.core))
|
||||
except PulseNotReceivedError:
|
||||
print("to few edges: cable too long or wiring bad")
|
||||
else:
|
||||
print(self.t)
|
||||
t_rise = mu_to_seconds(self.t[0], self.core)/n - latency
|
||||
t_fall = mu_to_seconds(self.t[1], self.core)/n - latency - pulse
|
||||
print("round trip times:")
|
||||
print("rising: {:5g} ns, falling {:5g} ns".format(
|
||||
t_rise/1e-9, t_fall/1e-9))
|
||||
|
||||
def rep(self, t):
|
||||
self.t = t
|
||||
|
||||
@kernel
|
||||
def many(self, n, p):
|
||||
t = [0 for i in range(2)]
|
||||
self.core.break_realtime()
|
||||
for i in range(n):
|
||||
self.one(t, p)
|
||||
self.rep(t)
|
||||
|
||||
@kernel
|
||||
def one(self, t, p):
|
||||
with parallel:
|
||||
self.pmt0.gate_both_mu(2*p)
|
||||
with sequential:
|
||||
t0 = now_mu()
|
||||
self.ttl2.pulse_mu(p)
|
||||
for i in range(len(t)):
|
||||
ti = self.pmt0.timestamp_mu()
|
||||
if ti <= 0:
|
||||
raise PulseNotReceivedError
|
||||
t[i] += ti - t0
|
||||
self.pmt0.count()
|
Loading…
Reference in New Issue