mirror of
https://github.com/m-labs/artiq.git
synced 2024-12-19 00:16:29 +08:00
74 lines
2.6 KiB
Python
74 lines
2.6 KiB
Python
# Copyright (C) 2014, 2015 Robert Jordens <jordens@gmail.com>
|
|
|
|
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.setattr_device("core")
|
|
self.setattr_device("pmt0")
|
|
self.setattr_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))
|
|
|
|
@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.t = t
|
|
|
|
@kernel
|
|
def one(self, t, p):
|
|
t0 = now_mu()
|
|
with parallel:
|
|
self.pmt0.gate_both_mu(2*p)
|
|
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() # flush
|