wavesynth: new semantics, fix compensation

* "trigger" now means that the corresponding line will only start
  once the trigger line is high.
* "jump" is implicit as the last line in a frame must jump back.
* spline coefficients are now compensated for finite time step size
This commit is contained in:
Robert Jördens 2015-04-05 03:54:02 -06:00
parent 1f545346e3
commit e870b27830
3 changed files with 59 additions and 61 deletions

View File

@ -113,13 +113,9 @@ class _Frame:
"dac_divider": dac_divider, "dac_divider": dac_divider,
"duration": duration, "duration": duration,
"channel_data": channel_data, "channel_data": channel_data,
"wait_trigger": False,
"jump": False
} for dac_divider, duration, channel_data in segment.lines] } for dac_divider, duration, channel_data in segment.lines]
segment_program[-1]["wait_trigger"] = True segment_program[0]["trigger"] = True
r += segment_program r += segment_program
r[-1]["wait_trigger"] = False
r[-1]["jump"] = True
return r return r
@kernel @kernel
@ -203,8 +199,7 @@ class CompoundPDQ2(AutoDB):
"duration": full_line["duration"], "duration": full_line["duration"],
"channel_data": full_line["channel_data"] "channel_data": full_line["channel_data"]
[n*channels_per_pdq2:(n+1)*channels_per_pdq2], [n*channels_per_pdq2:(n+1)*channels_per_pdq2],
"wait_trigger": full_line["wait_trigger"], "trigger": full_line["trigger"],
"jump": full_line["jump"]
} }
frame_program.append(line) frame_program.append(line)
program.append(frame_program) program.append(frame_program)

View File

@ -19,8 +19,7 @@ class TestSynthesizer(unittest.TestCase):
"clear": False} "clear": False}
} }
], ],
"wait_trigger": False, "trigger": True
"jump": False
}, },
{ {
# frame 0, segment 0, line 1 # frame 0, segment 0, line 1
@ -34,8 +33,7 @@ class TestSynthesizer(unittest.TestCase):
"clear": False} "clear": False}
} }
], ],
"wait_trigger": False, "trigger": False
"jump": True
}, },
], ],
[ [
@ -52,8 +50,7 @@ class TestSynthesizer(unittest.TestCase):
"clear": False} "clear": False}
} }
], ],
"wait_trigger": False, "trigger": True
"jump": False
}, },
{ {
# frame 1, segment 0, line 1 # frame 1, segment 0, line 1
@ -67,8 +64,7 @@ class TestSynthesizer(unittest.TestCase):
"clear": False} "clear": False}
} }
], ],
"wait_trigger": False, "trigger": False
"jump": True
} }
], ],
[ [
@ -85,8 +81,7 @@ class TestSynthesizer(unittest.TestCase):
"clear": False} "clear": False}
} }
], ],
"wait_trigger": True, "trigger": True
"jump": False
}, },
{ {
# frame 2, segment 1, line 0 # frame 2, segment 1, line 0
@ -100,8 +95,7 @@ class TestSynthesizer(unittest.TestCase):
"clear": True} "clear": True}
} }
], ],
"wait_trigger": False, "trigger": True
"jump": True
} }
] ]
] ]
@ -112,14 +106,11 @@ class TestSynthesizer(unittest.TestCase):
def drive(self): def drive(self):
s = self.dev s = self.dev
r = s.trigger(0) y = []
y = r[0] for f in 0, 2, None, 1:
r = s.trigger(2) if f is not None:
y += r[0] s.select(f)
r = s.trigger() y += s.trigger()[0]
y += r[0]
r = s.trigger(1)
y += r[0]
x = list(range(600)) x = list(range(600))
return x, y return x, y

View File

@ -2,12 +2,21 @@ from copy import copy
from math import cos, pi from math import cos, pi
def discrete_compensate(c):
if len(c) > 2:
c[1] += c[2]/2
if len(c) > 3:
c[1] += c[3]/6
c[2] += c[3]
return c
class Spline: class Spline:
def __init__(self): def __init__(self):
self.c = [0.0] self.c = [0.0]
def set_coefficients(self, c): def set_coefficients(self, c):
self.c = copy(c) self.c = discrete_compensate(copy(c))
def next(self): def next(self):
r = self.c[0] r = self.c[0]
@ -22,17 +31,18 @@ class SplinePhase:
self.c0 = 0.0 self.c0 = 0.0
def set_coefficients(self, c): def set_coefficients(self, c):
self.c = self.c[0:1] + c[1:]
self.c0 = c[0] self.c0 = c[0]
self.c[1:] = discrete_compensate(c[1:])
def clear(self): def clear(self):
self.c[0] = 0.0 self.c[0] = 0.0
def next(self): def next(self):
r = self.c[0] + self.c0 r = self.c[0]
for i in range(len(self.c) - 1): for i in range(len(self.c) - 1):
self.c[i] = (self.c[i] + self.c[i + 1]) % 1.0 self.c[i] += self.c[i + 1]
return r self.c[i] %= 1.0
return r + self.c0
class DDS: class DDS:
@ -48,14 +58,17 @@ class Wave:
def __init__(self): def __init__(self):
self.bias = Spline() self.bias = Spline()
self.dds = DDS() self.dds = DDS()
self.last = 0. self.v = 0.
self.silence = False self.silence = False
def next(self): def next(self):
v = self.bias.next() + self.dds.next() v = self.bias.next() + self.dds.next()
if not self.silence: if not self.silence:
self.last = v self.v = v
return self.last return self.v
def set_silence(self, s):
self.silence = s
class TriggerError(Exception): class TriggerError(Exception):
@ -67,29 +80,30 @@ class Synthesizer:
self.channels = [Wave() for _ in range(nchannels)] self.channels = [Wave() for _ in range(nchannels)]
self.program = program self.program = program
# line_iter is None: "wait for segment selection" state # line_iter is None: "wait for segment selection" state
# otherwise: iterator on the current position in the segment # otherwise: iterator on the current position in the frame
self.line_iter = None self.line_iter = None
def trigger(self, selection=None): def select(self, selection):
if selection is None:
if self.line_iter is None:
raise TriggerError
else:
if self.line_iter is not None: if self.line_iter is not None:
raise TriggerError raise TriggerError
self.line_iter = iter(self.program[selection]) self.line_iter = iter(self.program[selection])
self.line = next(self.line_iter)
def trigger(self):
if self.line_iter is None:
raise TriggerError
line = self.line
if not line.get("trigger", False):
raise TriggerError
r = [[] for _ in self.channels] r = [[] for _ in self.channels]
while True: while True:
line = next(self.line_iter)
if line.get("dac_divider", 1) != 1:
raise NotImplementedError
for channel, channel_data in zip(self.channels, for channel, channel_data in zip(self.channels,
line["channel_data"]): line["channel_data"]):
if "bias" in channel_data: if "bias" in channel_data:
channel.bias.set_coefficients(channel_data["bias"]["amplitude"]) channel.bias.set_coefficients(
channel_data["bias"]["amplitude"])
if "dds" in channel_data: if "dds" in channel_data:
channel.dds.amplitude.set_coefficients( channel.dds.amplitude.set_coefficients(
channel_data["dds"]["amplitude"]) channel_data["dds"]["amplitude"])
@ -98,22 +112,20 @@ class Synthesizer:
channel_data["dds"]["phase"]) channel_data["dds"]["phase"])
if channel_data["dds"].get("clear", False): if channel_data["dds"].get("clear", False):
channel.dds.phase.clear() channel.dds.phase.clear()
channel.silence = channel_data.get("silence", False) channel.set_silence(channel_data.get("silence", False))
if line.get("dac_divider", 1) != 1:
raise NotImplementedError
for channel, rc in zip(self.channels, r): for channel, rc in zip(self.channels, r):
for i in range(line["duration"]): for i in range(line["duration"]):
rc.append(channel.next()) rc.append(channel.next())
if line.get("wait_trigger", False):
return r
if line.get("jump", False):
if not line.get("wait_trigger", False):
raise ValueError("Jumps should be with wait_trigger")
try: try:
next(self.line_iter) self.line = line = next(self.line_iter)
raise ValueError("Jump in the middle of a frame") if line.get("trigger", False):
return r
except StopIteration: except StopIteration:
pass
self.line_iter = None self.line_iter = None
return r return r