Skip to content

Commit

Permalink
examples: add TDR toy example
Browse files Browse the repository at this point in the history
jordens committed Jul 29, 2015
1 parent 9036841 commit 278570f
Showing 1 changed file with 75 additions and 0 deletions.
75 changes: 75 additions & 0 deletions examples/master/repository/tdr.py
Original file line number Diff line number Diff line change
@@ -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):

This comment has been minimized.

Copy link
@sbourdeauducq

sbourdeauducq Jul 29, 2015

Member

This isn't necessary, the compiler can handle self.t = xxx directly in kernel code.

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()

This comment has been minimized.

Copy link
@sbourdeauducq

sbourdeauducq Jul 29, 2015

Member

You can move this before with parallel and get rid of the with sequential.

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()

This comment has been minimized.

Copy link
@sbourdeauducq

sbourdeauducq Jul 29, 2015

Member

Should we add a flush function to make it clear what this does?

This comment has been minimized.

Copy link
@jordens

jordens Jul 29, 2015

Author Member

Hmm. Maybe documentation (this code or in the reference) is enough. Ack to the other things.

0 comments on commit 278570f

Please sign in to comment.