Compare commits

...

7 Commits

4 changed files with 71 additions and 45 deletions

30
dmi.py
View File

@ -20,34 +20,42 @@ def main():
induction.start() induction.start()
try: try:
def stabilizer_cb(spectrum, peak_freq, locked, tuning): def stabilizer_cb(spectrum, peak_freq, locked, tuning):
gui.update_beat_spectrum(spectrum, peak_freq, locked) gui.update_ref(spectrum, peak_freq, locked)
induction.set(tuning) induction.set(tuning)
stabilizer = Stabilizer(freq_sample, block_size, 1088.1e6 - freq_base, stabilizer_cb) stabilizer = Stabilizer(freq_sample, block_size, 1088.1e6 - freq_base, stabilizer_cb)
position_tracker = PositionTracker(int(0.1*freq_sample/block_size)) position_tracker = PositionTracker()
sdr = SoapySDR.Device() sdr = SoapySDR.Device()
for channel in range(2): for channel in range(2):
sdr.setSampleRate(SoapySDR.SOAPY_SDR_RX, channel, freq_sample) sdr.setSampleRate(SoapySDR.SOAPY_SDR_RX, channel, freq_sample)
sdr.setFrequency(SoapySDR.SOAPY_SDR_RX, channel, freq_base) sdr.setFrequency(SoapySDR.SOAPY_SDR_RX, channel, freq_base)
buf_sdr = BufferedSDR(sdr, [0, 1], block_size, 256) buf_sdr = BufferedSDR(sdr, [0, 1], block_size, 32)
buf_sdr.start() buf_sdr.start()
try: try:
stabilizer_throttle = 0 throttle = 0
while True: while True:
buffers = buf_sdr.get() buffers = buf_sdr.get()
try: try:
samples_ref, samples_meas = buffers samples_ref, samples_meas = buffers
# We can't update faster than the MHS5200A serial interface # Throttle certain things to avoid overflows due to the limited speed of
stabilizer_throttle += 1 # the MHS5200A serial interface and GUI plotting.
if stabilizer_throttle == 8: throttle += 1
stabilizer.input(samples_ref) if throttle == 8:
stabilizer_throttle = 0 throttle = 0
#position, leakage = position_tracker.input(samples_ref, samples_meas) if throttle == 0:
#print(np.sum(position)/len(position), leakage) stabilizer.input(samples_ref)
gui.update_meas(samples_meas)
if stabilizer.locked():
position = position_tracker.input(samples_ref, samples_meas)
if throttle == 0:
gui.update_position(np.sum(position)/len(position))
else:
position_tracker.reset()
finally: finally:
buf_sdr.dispose(buffers) buf_sdr.dispose(buffers)
finally: finally:

12
gui.py
View File

@ -54,8 +54,16 @@ class GUI:
os.path.join(os.path.dirname(os.path.abspath(__file__)), "gui_impl.py"), os.path.join(os.path.dirname(os.path.abspath(__file__)), "gui_impl.py"),
str(freq_sample), str(freq_base), str(block_size)]) str(freq_sample), str(freq_base), str(block_size)])
def update_beat_spectrum(self, block, peak_freq, locked): def update_ref(self, block, peak_freq, locked):
obj = {"action": "update_beat_spectrum", "block": block, "peak_freq": peak_freq, "locked": locked} obj = {"action": "update_ref", "block": block, "peak_freq": peak_freq, "locked": locked}
self.impl.write_pyon(obj)
def update_meas(self, block):
obj = {"action": "update_meas", "block": block}
self.impl.write_pyon(obj)
def update_position(self, position):
obj = {"action": "update_position", "position": position}
self.impl.write_pyon(obj) self.impl.write_pyon(obj)
def close(self): def close(self):

View File

@ -4,6 +4,7 @@ import sys
import logging import logging
import numpy as np import numpy as np
from scipy.signal import blackmanharris
from quamash import QEventLoop, QtWidgets, QtCore from quamash import QEventLoop, QtWidgets, QtCore
import pyqtgraph as pg import pyqtgraph as pg
from sipyco.pipe_ipc import AsyncioChildComm from sipyco.pipe_ipc import AsyncioChildComm
@ -15,22 +16,15 @@ class SpectrogramItem(pg.ImageItem):
pg.ImageItem.__init__(self) pg.ImageItem.__init__(self)
depth = 100 depth = 100
self.setOpts(axisOrder="row-major")
self.img_array = np.zeros((depth, block_size)) self.img_array = np.zeros((depth, block_size))
self.setImage(self.img_array, autoLevels=True, autoDownsample=True)
self.setImage(self.img_array, autoLevels=True) self.setRect(QtCore.QRectF((freq_base-freq_sample/2)/1e6, -float(depth), freq_sample/1e6, float(depth)))
pos = np.array([0., 1., 0.5, 0.25, 0.75])
color = np.array([[0,255,255,255], [255,255,0,255], [0,0,0,255], (0, 0, 255, 255), (255, 0, 0, 255)], dtype=np.ubyte)
cmap = pg.ColorMap(pos, color)
lut = cmap.getLookupTable(0.0, 1.0, 256)
self.setLookupTable(lut)
self.setRect(QtCore.QRectF(0.0, (freq_base-freq_sample/2)/1e6, float(depth), freq_sample/1e6))
def add_block(self, block): def add_block(self, block):
self.img_array = np.roll(self.img_array, -1, 0) self.img_array = np.roll(self.img_array, -1, 0)
self.img_array[-1:] = np.fft.fftshift(block) self.img_array[-1:] = np.fft.fftshift(block)
self.setImage(self.img_array, autoLevels=True) self.setImage(self.img_array, autoLevels=True, autoDownsample=True)
class MainWindow(pg.GraphicsLayoutWidget): class MainWindow(pg.GraphicsLayoutWidget):
@ -40,18 +34,28 @@ class MainWindow(pg.GraphicsLayoutWidget):
self.freq_sample = freq_sample self.freq_sample = freq_sample
self.freq_base = freq_base self.freq_base = freq_base
self.block_size = block_size
self.text_ref = pg.LabelItem(size="24pt") self.text_ref = pg.LabelItem(size="24pt")
self.addItem(self.text_ref, row=0, col=0) self.addItem(self.text_ref, row=0, col=0)
self.text_locked = pg.LabelItem(size="24pt") self.text_locked = pg.LabelItem(size="24pt")
self.addItem(self.text_locked, row=0, col=1) self.addItem(self.text_locked, row=0, col=1)
self.update_params(None, False) self.update_ref(None, None, False)
p1 = self.addPlot(row=1, col=0, colspan=2) p1 = self.addPlot(row=1, col=0, colspan=2)
self.beat_spectrum = SpectrogramItem(freq_sample, freq_base, block_size) self.ref_spectrum = SpectrogramItem(freq_sample, freq_base, block_size)
p1.addItem(self.beat_spectrum) p1.addItem(self.ref_spectrum)
def update_params(self, peak_freq, locked): p2 = self.addPlot(row=2, col=0, colspan=2)
self.meas_spectrum = SpectrogramItem(freq_sample, freq_base, block_size)
p2.addItem(self.meas_spectrum)
self.position = self.addPlot(row=3, col=0, colspan=2)
self.position_history = np.zeros(300)
def update_ref(self, block, peak_freq, locked):
if block is not None:
self.ref_spectrum.add_block(block)
if peak_freq is None: if peak_freq is None:
self.text_ref.setText("REF: NO SIGNAL") self.text_ref.setText("REF: NO SIGNAL")
else: else:
@ -61,6 +65,16 @@ class MainWindow(pg.GraphicsLayoutWidget):
else: else:
self.text_locked.setText("REF LASER UNLOCKED", color="FF0000") self.text_locked.setText("REF LASER UNLOCKED", color="FF0000")
def update_meas(self, block):
assert len(block) == self.block_size
spectrum = np.abs(np.fft.fft(block*blackmanharris(self.block_size)))
self.meas_spectrum.add_block(spectrum)
def update_position(self, position):
self.position_history = np.roll(self.position_history, -1)
self.position_history[-1] = position
self.position.clear()
self.position.plot(self.position_history)
class IPCClient(AsyncioChildComm): class IPCClient(AsyncioChildComm):
@ -76,12 +90,12 @@ class IPCClient(AsyncioChildComm):
obj = await self.read_pyon() obj = await self.read_pyon()
try: try:
action = obj["action"] action = obj["action"]
if action == "update_beat_spectrum": del obj["action"]
main_window.beat_spectrum.add_block(obj["block"])
main_window.update_params(obj["peak_freq"], obj["locked"])
if action == "terminate": if action == "terminate":
self.close_cb() self.close_cb()
return return
else:
getattr(main_window, action)(**obj)
except: except:
logging.error("error processing parent message", logging.error("error processing parent message",
exc_info=True) exc_info=True)

View File

@ -116,11 +116,11 @@ class Stabilizer:
self.wiggle_direction = 1 self.wiggle_direction = 1
self.amp_threshold = 80.0 self.amp_threshold = 80.0
self.k = 50.0e-6 self.k = 30.0e-6
self.tolerance = 10e3 self.tolerance = 10e3
self.lock_counter_threshold = 60 self.lock_counter_threshold = 60
self.unlock_counter_threshold = 600 self.unlock_counter_threshold = 500
self.wiggle = 0.1 self.wiggle = 0.15
def input(self, samples): def input(self, samples):
spectrum = np.abs(np.fft.fft(samples*blackmanharris(len(samples)))) spectrum = np.abs(np.fft.fft(samples*blackmanharris(len(samples))))
@ -169,21 +169,17 @@ def continuous_unwrap(last_phase, last_phase_unwrapped, p):
class PositionTracker: class PositionTracker:
def __init__(self, leakage_avg): def __init__(self):
self.reset()
def reset(self):
self.last_phase = 0.0 self.last_phase = 0.0
self.last_position = 0.0 self.last_position = 0.0
self.leakage = np.zeros(leakage_avg)
self.leakage_ptr = 0
def input(self, ref, meas): def input(self, ref, meas):
demod = np.conjugate(ref)*meas demod = np.conjugate(ref)*meas
phase = np.angle(demod)
self.leakage[self.leakage_ptr] = np.real(np.sum(demod)/len(demod))
self.leakage_ptr = (self.leakage_ptr + 1) % len(self.leakage)
leakage = np.sum(self.leakage)/len(self.leakage)
phase = np.angle(demod - leakage)
position = continuous_unwrap(self.last_phase, self.last_position, phase)/(2.0*np.pi) position = continuous_unwrap(self.last_phase, self.last_position, phase)/(2.0*np.pi)
self.last_phase = phase[-1] self.last_phase = phase[-1]
self.last_position = position[-1] self.last_position = position[-1]
return position, leakage return position