mirror of
https://github.com/slsdetectorgroup/slsDetectorPackage.git
synced 2026-08-06 20:22:26 +02:00
reworked pattern generator
This commit is contained in:
@@ -1,226 +0,0 @@
|
||||
from . import Detector, Pattern
|
||||
from .bits import setbit, clearbit
|
||||
import textwrap
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
class PatternGenerator:
|
||||
"""
|
||||
Class to generate a pattern for the SLS detector. Intents to as closely as possible
|
||||
mimic the old pattern generation in the C code.
|
||||
"""
|
||||
def __init__(self):
|
||||
self.pattern = Pattern()
|
||||
self.iaddr = 0
|
||||
|
||||
def SB(self, *bits):
|
||||
"""
|
||||
Set one or several bits. Change will take affect with the next PW.
|
||||
"""
|
||||
for bit in bits:
|
||||
self.pattern.word[self.iaddr] = setbit(bit, self.pattern.word[self.iaddr])
|
||||
return self.pattern.word[self.iaddr]
|
||||
|
||||
def CB(self, *bits):
|
||||
"""
|
||||
Clear one or several bits. Change will take affect with the next PW.
|
||||
"""
|
||||
for bit in bits:
|
||||
self.pattern.word[self.iaddr] = clearbit(bit, self.pattern.word[self.iaddr])
|
||||
return self.pattern.word[self.iaddr]
|
||||
|
||||
|
||||
def _pw(self, verbose = False):
|
||||
if verbose:
|
||||
print(f'{self.iaddr:#06x} {self.pattern.word[self.iaddr]:#018x}')
|
||||
|
||||
#Limits are inclusive so we need to increment the address before writing the next word
|
||||
self.pattern.limits[1] = self.iaddr
|
||||
self.iaddr += 1
|
||||
self.pattern.word[self.iaddr] = self.pattern.word[self.iaddr-1]
|
||||
|
||||
def PW(self, x = 1, verbose = False):
|
||||
for i in range(x):
|
||||
self._pw(verbose)
|
||||
|
||||
# def REPEAT(self, x, verbose = False):
|
||||
# for i in range(x):
|
||||
# self._pw(verbose)
|
||||
|
||||
# def PW2(self, verbose = 0):
|
||||
# self.REPEAT(2, verbose)
|
||||
|
||||
|
||||
def CLOCKS(self, bit, times = 1, length = 1, verbose = False):
|
||||
"""
|
||||
clocks "bit" n "times", every half clock is long "length"
|
||||
length is optional, default value is 1
|
||||
"""
|
||||
for i in range(0, times):
|
||||
self.SB(bit); self.PW(length, verbose)
|
||||
self.CB(bit); self.PW(length, verbose)
|
||||
|
||||
def CLOCK(self, bit, length = 1, verbose = 0):
|
||||
self.CLOCKS(bit, 1, length ,verbose)
|
||||
|
||||
def serializer(self, value, serInBit, clkBit, nbits, msbfirst = True, length = 1):
|
||||
"""serializer(value,serInBit,clkBit,nbits,msbfirst=1,length=1)
|
||||
Produces the .pat file needed to serialize a word into a shift register.
|
||||
value: value to be serialized
|
||||
serInBit: control bit corresponding to serial in
|
||||
clkBit: control bit corresponding to the clock
|
||||
nbits: number of bits of the target register to load
|
||||
msbfirst: if 1 pushes in the MSB first (default),
|
||||
if 0 pushes in the LSB first
|
||||
length: length of all the PWs in the pattern
|
||||
It produces no output because it modifies directly the members of the class pat via SB and CB"""
|
||||
|
||||
c = value
|
||||
self.CB(serInBit, clkBit)
|
||||
self.PW(length) #generate initial line with clk and serIn to 0
|
||||
|
||||
start = 0
|
||||
stop = nbits
|
||||
step = 1
|
||||
|
||||
if msbfirst:
|
||||
start = nbits - 1
|
||||
stop = -1
|
||||
step =- 1 #reverts loop if msb has to be pushed in first
|
||||
|
||||
for i in range(start, stop, step):
|
||||
if c & (1<<i):
|
||||
self.SB(serInBit)
|
||||
self.PW(length)
|
||||
else:
|
||||
self.CB(serInBit)
|
||||
self.PW(length)
|
||||
self.SB(clkBit)
|
||||
self.PW(length)
|
||||
self.CB(clkBit)
|
||||
self.PW(length)
|
||||
|
||||
self.CB(serInBit, clkBit)
|
||||
self.PW(length) #generate final line with clk and serIn to 0
|
||||
|
||||
|
||||
#NOT IMPLEMENTED YET
|
||||
#TODO! What should setstop do? Or can we remove it?
|
||||
#def setstop():
|
||||
#
|
||||
|
||||
def setoutput(self, bit):
|
||||
self.pattern.ioctrl = setbit(bit, self.pattern.ioctrl)
|
||||
|
||||
def setinput(self, bit):
|
||||
self.pattern.ioctrl= clearbit(bit, self.pattern.ioctrl)
|
||||
|
||||
#TODO! What should setclk do? Or can we remove it?
|
||||
# def setclk(bit):
|
||||
# self.clkctrl=self.setbit(bit,self.clkctrl)
|
||||
|
||||
def setinputs(self, *args):
|
||||
for i in args:
|
||||
self.setinput(i)
|
||||
|
||||
def setoutputs(self, *args):
|
||||
for i in args:
|
||||
self.setoutput(i)
|
||||
|
||||
#def setclks(self, *args):
|
||||
# for i in args:
|
||||
# self.setclk(i)
|
||||
|
||||
def setnloop(self, i, reps):
|
||||
self.pattern.nloop[i] = reps
|
||||
|
||||
def setstartloop(self, i):
|
||||
"""
|
||||
Set startloop[i] to the current address.
|
||||
"""
|
||||
self.pattern.startloop[i] = self.iaddr
|
||||
|
||||
def setstoploop(self, i):
|
||||
"""
|
||||
Set stoploop[i] to the current address.
|
||||
"""
|
||||
self.pattern.stoploop[i] = self.iaddr
|
||||
|
||||
|
||||
def setstart(self):
|
||||
"""
|
||||
Set start of pattern to the current address.
|
||||
"""
|
||||
self.pattern.limits[0]=self.iaddr
|
||||
|
||||
|
||||
def setstop(self,l):
|
||||
"""
|
||||
Set stop of pattern to the current address.
|
||||
"""
|
||||
self.pattern.limits[1] = self.iaddr
|
||||
|
||||
|
||||
def setwaitpoint(self, i):
|
||||
"""
|
||||
Set wait[i] to the current address.
|
||||
"""
|
||||
self.pattern.wait[i] = self.iaddr
|
||||
|
||||
|
||||
def setwaittime(self, i, t):
|
||||
"""
|
||||
Set waittime[i] to t.
|
||||
"""
|
||||
self.pattern.waittime[i] = t
|
||||
|
||||
|
||||
def setwait(self, i, t):
|
||||
"""
|
||||
Set wait[i] to the current address and waittime[i] to t.
|
||||
"""
|
||||
self.setwait(i)
|
||||
self.setwaittime(i, t)
|
||||
|
||||
|
||||
def __repr__(self):
|
||||
return textwrap.dedent(f"""\
|
||||
PatternBuilder:
|
||||
patlimits: {self.pattern.limits}
|
||||
startloop: {self.pattern.startloop}
|
||||
stoploop: {self.pattern.stoploop}
|
||||
nloop: {self.pattern.nloop}
|
||||
wait: {self.pattern.wait}
|
||||
waittime: {self.pattern.waittime}""")
|
||||
|
||||
def __str__(self):
|
||||
return self.pattern.str()
|
||||
|
||||
def print(self):
|
||||
print(self)
|
||||
|
||||
def save(self, fname):
|
||||
"""Save pattern to text file"""
|
||||
fname = str(fname) #Accept also Path objects, but C++ code needs a string
|
||||
self.pattern.save(fname)
|
||||
|
||||
def load(self, fname):
|
||||
"""Load pattern from text file"""
|
||||
fname = str(fname) #Accept also Path objects, but C++ code needs a string
|
||||
n = self.pattern.load(fname)
|
||||
|
||||
#If the firs and only word is 0 we assume an empty pattern
|
||||
if n == 1 and self.pattern.word[0] == 0:
|
||||
n = 0
|
||||
self.iaddr = n
|
||||
|
||||
#To make PW work as expected we need to set 1+last word to the last word
|
||||
if n > 0:
|
||||
self.pattern.word[n] = self.pattern.word[n-1]
|
||||
|
||||
|
||||
def send_to_detector(self, det):
|
||||
"""
|
||||
Load the pattern into the detector.
|
||||
"""
|
||||
det.setPattern(self.pattern)
|
||||
@@ -1,6 +1,8 @@
|
||||
# SPDX-License-Identifier: LGPL-3.0-or-other
|
||||
# Copyright (C) 2021 Contributors to the SLS Detector Package
|
||||
# from .detector import Detector, DetectorError, free_shared_memory
|
||||
from importlib.metadata import PackageNotFoundError, version
|
||||
from pathlib import Path
|
||||
|
||||
from .eiger import Eiger
|
||||
from .ctb import Ctb
|
||||
from .dacs import NamedDacs, DetectorDacs, Dac
|
||||
@@ -12,7 +14,7 @@ from .gotthard2 import Gotthard2
|
||||
from .moench import Moench
|
||||
from .pattern import Pattern, patternParameters
|
||||
from .gaincaps import Mythen3GainCapsWrapper
|
||||
from .PatternGenerator import PatternGenerator
|
||||
from .pattern_generator import PatternGenerator
|
||||
|
||||
from . import _slsdet
|
||||
from ._slsdet import freeSharedMemory, getUserDetails
|
||||
@@ -20,7 +22,7 @@ from ._slsdet import freeSharedMemory, getUserDetails
|
||||
xy = _slsdet.xy
|
||||
defs = _slsdet.slsDetectorDefs
|
||||
|
||||
#Make enums and #defines available at top level
|
||||
# Make enums and #defines available at top level
|
||||
from .enums import *
|
||||
from .defines import *
|
||||
|
||||
@@ -38,17 +40,7 @@ Hz = _slsdet.Hz
|
||||
kHz = _slsdet.kHz
|
||||
MHz = _slsdet.MHz
|
||||
|
||||
import os
|
||||
def read_version():
|
||||
try:
|
||||
version_file = os.path.join(os.path.dirname(__file__), 'VERSION')
|
||||
with open(version_file, "r") as f:
|
||||
return f.read().strip()
|
||||
except:
|
||||
raise RuntimeError("VERSION file not found in slsdet package from init.py")
|
||||
|
||||
__version__ = read_version()
|
||||
|
||||
|
||||
|
||||
|
||||
try:
|
||||
__version__ = version("slsdet")
|
||||
except PackageNotFoundError:
|
||||
__version__ = Path(__file__).parent.joinpath("VERSION").read_text().strip()
|
||||
|
||||
+17
-22
@@ -1,31 +1,26 @@
|
||||
import numpy as np
|
||||
|
||||
|
||||
def _bitmask(bit, word):
|
||||
dtype = word.dtype if hasattr(word, 'dtype') else np.uint64
|
||||
if bit >= np.iinfo(dtype).bits:
|
||||
raise ValueError(f"bit {bit} out of range for {np.dtype(dtype).name}")
|
||||
return np.dtype(dtype).type(1 << bit)
|
||||
|
||||
def setbit(bit, word):
|
||||
if isinstance(word, np.generic):
|
||||
mask = word.dtype.type(1)
|
||||
mask = mask << bit
|
||||
else:
|
||||
mask = 1 << bit
|
||||
return word | mask
|
||||
|
||||
|
||||
def setbit_arr(bit, arr):
|
||||
arr |= arr.dtype.type(1 << bit)
|
||||
|
||||
"""
|
||||
Set the bit at position bit in word(s).
|
||||
"""
|
||||
return word | _bitmask(bit, word)
|
||||
|
||||
def clearbit(bit, word):
|
||||
"""
|
||||
Clear the bit at position bit in word.
|
||||
Two paths to avoid converting the types.
|
||||
Clear the bit at position bit in word(s).
|
||||
"""
|
||||
if isinstance(word, np.generic):
|
||||
mask = word.dtype.type(1)
|
||||
mask = ~(mask << bit)
|
||||
else:
|
||||
mask = ~(1 << bit)
|
||||
return word & mask
|
||||
return word & ~_bitmask(bit, word)
|
||||
|
||||
|
||||
def clearbit_arr(bit, arr):
|
||||
arr &= arr.dtype.type(~(1 << bit))
|
||||
def flipbit(bit, word):
|
||||
"""
|
||||
Flip the bit at position bit in word(s).
|
||||
"""
|
||||
return word ^ _bitmask(bit, word)
|
||||
+25
-27
@@ -5,11 +5,14 @@ from . import _slsdet
|
||||
from ._slsdet import Pattern
|
||||
|
||||
|
||||
class patternParameters(_slsdet.patternParameters):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.view = self.numpy_view()
|
||||
self.names = self.view.dtype.names
|
||||
class _StructViewMixin:
|
||||
"""
|
||||
Exposes the fields of a numpy structured dtype view as attributes.
|
||||
"""
|
||||
|
||||
def _init_view(self, view):
|
||||
self.__dict__["view"] = view
|
||||
self.__dict__["names"] = view.dtype.names
|
||||
|
||||
def __getattr__(self, name):
|
||||
if name in self.names:
|
||||
@@ -18,9 +21,7 @@ class patternParameters(_slsdet.patternParameters):
|
||||
raise KeyError(f"Key: {name} not found")
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
if name in ["view", "names"]:
|
||||
self.__dict__[name] = value
|
||||
elif name in self.names:
|
||||
if name in self.names:
|
||||
self.view[name] = value
|
||||
else:
|
||||
raise KeyError(f"Key: {name} not found")
|
||||
@@ -29,27 +30,24 @@ class patternParameters(_slsdet.patternParameters):
|
||||
def __dir__(self):
|
||||
return self.names
|
||||
|
||||
def copy(self):
|
||||
"""
|
||||
Return a new instance with the same field values.
|
||||
"""
|
||||
new = type(self)()
|
||||
new.view[:] = self.view
|
||||
return new
|
||||
|
||||
class Pattern(_slsdet.Pattern):
|
||||
__copy__ = copy
|
||||
|
||||
|
||||
class patternParameters(_StructViewMixin, _slsdet.patternParameters):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.view = self.data().numpy_view()
|
||||
self.names = self.view.dtype.names
|
||||
self._init_view(self.numpy_view())
|
||||
|
||||
def __getattr__(self, name):
|
||||
if name in self.names:
|
||||
return self.view[name][0]
|
||||
else:
|
||||
raise KeyError(f"Key: {name} not found")
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
if name in ["view", "names"]:
|
||||
self.__dict__[name] = value
|
||||
elif name in self.names:
|
||||
self.view[name] = value
|
||||
else:
|
||||
raise KeyError(f"Key: {name} not found")
|
||||
|
||||
# Provide custom dir for tab completion
|
||||
def __dir__(self):
|
||||
return self.names
|
||||
class Pattern(_StructViewMixin, _slsdet.Pattern):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._init_view(self.data().numpy_view())
|
||||
@@ -0,0 +1,215 @@
|
||||
from . import Pattern
|
||||
from .bits import setbit, clearbit, flipbit
|
||||
import textwrap
|
||||
|
||||
class PatternGenerator:
|
||||
"""
|
||||
Class to generate a pattern for the SLS detector. Intents to as closely as possible
|
||||
mimic the old pattern generation in the C code.
|
||||
"""
|
||||
def __init__(self, verbose = False):
|
||||
self.pattern = Pattern()
|
||||
self.iaddr = 0
|
||||
self.verbose = verbose
|
||||
|
||||
def clear_pattern(self):
|
||||
"""
|
||||
Clear the pattern and reset the address to 0.
|
||||
"""
|
||||
self.pattern[:] = 0
|
||||
self.iaddr = 0
|
||||
|
||||
def SB(self, *bits):
|
||||
"""
|
||||
Set one or multiple bits. Change will take affect with the next PW.
|
||||
"""
|
||||
for bit in bits:
|
||||
self.pattern.word[self.iaddr] = setbit(bit, self.pattern.word[self.iaddr])
|
||||
return self.pattern.word[self.iaddr]
|
||||
|
||||
def CB(self, *bits):
|
||||
"""
|
||||
Clear one or multiple bits. Change will take affect with the next PW.
|
||||
"""
|
||||
for bit in bits:
|
||||
self.pattern.word[self.iaddr] = clearbit(bit, self.pattern.word[self.iaddr])
|
||||
return self.pattern.word[self.iaddr]
|
||||
|
||||
def FB(self, *bits):
|
||||
"""
|
||||
Flip one or multiple bits. Change will take affect with the next PW.
|
||||
"""
|
||||
for bit in bits:
|
||||
self.pattern.word[self.iaddr] = flipbit(bit, self.pattern.word[self.iaddr])
|
||||
return self.pattern.word[self.iaddr]
|
||||
|
||||
def _pw(self):
|
||||
if self.verbose:
|
||||
print(f'{self.iaddr:#06x} {self.pattern.word[self.iaddr]:#018x}')
|
||||
|
||||
# Increment the address before the next word since limits are inclusive
|
||||
self.pattern.limits[1] = self.iaddr
|
||||
self.iaddr += 1
|
||||
self.pattern.word[self.iaddr] = self.pattern.word[self.iaddr - 1]
|
||||
|
||||
def PW(self, x = 1):
|
||||
for _ in range(x):
|
||||
self._pw()
|
||||
|
||||
def CLOCKS(self, bits, repeats = 1, clock_duration = 1):
|
||||
"""
|
||||
Generate clock pulses on the specified bits.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
bit_mask : int
|
||||
Bitmask selecting which clock line(s) to pulse.
|
||||
repeats : int, optional
|
||||
Number of full clock cycles to generate (default 1).
|
||||
half_period : int, optional
|
||||
Hold time for each half of the cycle (default 1).
|
||||
verbose : bool, optional
|
||||
If True, print timing/debug info during the wait (default False).
|
||||
"""
|
||||
for _ in range(repeats):
|
||||
self.FB(*bits)
|
||||
self.PW(clock_duration)
|
||||
self.FB(*bits)
|
||||
self.PW(clock_duration)
|
||||
|
||||
def serializer(self, value, ser_in_bit, clk_bit, nbits, msb_first = True, length = 1):
|
||||
"""
|
||||
Serialize `value` into a shift register via ser_in_bit/clk_bit.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
value : int
|
||||
Value to serialize.
|
||||
ser_in_bit : int
|
||||
Control bit corresponding to serial in.
|
||||
clk_bit : int
|
||||
Control bit corresponding to the clock.
|
||||
nbits : int
|
||||
Number of bits of the target register to load.
|
||||
msb_first : bool, optional
|
||||
Push in the MSB first if True (default), else LSB first.
|
||||
length : int, optional
|
||||
Duration of each PW in the pattern (default 1).
|
||||
"""
|
||||
bit_order = range(nbits - 1, -1, -1) if msb_first else range(nbits)
|
||||
|
||||
self.CB(ser_in_bit, clk_bit)
|
||||
self.PW(length) # initial line with clk and serIn low
|
||||
|
||||
for i in bit_order:
|
||||
if value & (1 << i):
|
||||
self.SB(ser_in_bit)
|
||||
else:
|
||||
self.CB(ser_in_bit)
|
||||
self.PW(length)
|
||||
self.SB(clk_bit)
|
||||
self.PW(length)
|
||||
self.CB(clk_bit)
|
||||
self.PW(length)
|
||||
|
||||
self.CB(ser_in_bit, clk_bit)
|
||||
self.PW(length) # final line with clk and serIn low
|
||||
|
||||
|
||||
#NOT IMPLEMENTED YET
|
||||
#TODO! What should setstop do? Or can we remove it?
|
||||
#def setstop():
|
||||
|
||||
def setoutput(self, *bits):
|
||||
for bit in bits:
|
||||
self.pattern.ioctrl = setbit(bit, self.pattern.ioctrl)
|
||||
|
||||
def setinput(self, *bits):
|
||||
for bit in bits:
|
||||
self.pattern.ioctrl = clearbit(bit, self.pattern.ioctrl)
|
||||
|
||||
#TODO! What should setclk do? Or can we remove it?
|
||||
# def setclk(bit):
|
||||
# self.clkctrl=self.setbit(bit,self.clkctrl)
|
||||
|
||||
#def setclks(self, *args):
|
||||
# for i in args:
|
||||
# self.setclk(i)
|
||||
|
||||
def setnloop(self, i, reps):
|
||||
self.pattern.nloop[i] = reps
|
||||
|
||||
def setstartloop(self, i):
|
||||
"""
|
||||
Set startloop[i] to the current address.
|
||||
"""
|
||||
self.pattern.startloop[i] = self.iaddr
|
||||
|
||||
def setstoploop(self, i):
|
||||
"""
|
||||
Set stoploop[i] to the current address.
|
||||
"""
|
||||
self.pattern.stoploop[i] = self.iaddr
|
||||
|
||||
def setstart(self):
|
||||
"""
|
||||
Set start of pattern to the current address.
|
||||
"""
|
||||
self.pattern.limits[0] = self.iaddr
|
||||
|
||||
def setstop(self):
|
||||
"""
|
||||
Set stop of pattern to the current address.
|
||||
"""
|
||||
self.pattern.limits[1] = self.iaddr
|
||||
|
||||
def setwaitpoint(self, i):
|
||||
"""
|
||||
Set wait[i] to the current address.
|
||||
"""
|
||||
self.pattern.wait[i] = self.iaddr
|
||||
|
||||
def setwaittime(self, i, t):
|
||||
"""
|
||||
Set waittime[i] to t.
|
||||
"""
|
||||
self.pattern.waittime[i] = t
|
||||
|
||||
def setwait(self, i, t):
|
||||
"""
|
||||
Set wait[i] to the current address and waittime[i] to t.
|
||||
"""
|
||||
self.setwait(i)
|
||||
self.setwaittime(i, t)
|
||||
|
||||
def __repr__(self):
|
||||
return textwrap.dedent(f"""\
|
||||
PatternGenerator:
|
||||
patlimits: {self.pattern.limits}
|
||||
startloop: {self.pattern.startloop}
|
||||
stoploop: {self.pattern.stoploop}
|
||||
nloop: {self.pattern.nloop}
|
||||
wait: {self.pattern.wait}
|
||||
waittime: {self.pattern.waittime}""")
|
||||
|
||||
def __str__(self):
|
||||
return self.pattern.str()
|
||||
|
||||
def export_pattern(self):
|
||||
"""
|
||||
Generate the pattern and return it as a Pattern object.
|
||||
"""
|
||||
return self.pattern.copy()
|
||||
|
||||
def load_pattern(self, fname):
|
||||
"""Load pattern from text file"""
|
||||
iaddr = self.pattern.load(fname)
|
||||
|
||||
# Assume an empty pattern if the first and only word is zero
|
||||
if iaddr == 1 and self.pattern.word[0] == 0:
|
||||
iaddr = 0
|
||||
self.iaddr = iaddr
|
||||
|
||||
# Set (last + 1) word to the last word
|
||||
if iaddr > 0:
|
||||
self.pattern.word[iaddr] = self.pattern.word[iaddr - 1]
|
||||
@@ -25,5 +25,7 @@ void init_pattern(py::module &m) {
|
||||
.def("save", &sls::Pattern::save)
|
||||
.def("str", &sls::Pattern::str)
|
||||
.def("data", (pat * (sls::Pattern::*)()) & sls::Pattern::data,
|
||||
py::return_value_policy::reference);
|
||||
py::return_value_policy::reference)
|
||||
.def(py::self == py::self)
|
||||
.def(py::self != py::self);
|
||||
}
|
||||
|
||||
@@ -10,5 +10,6 @@ ODR warnings
|
||||
#include <pybind11/pybind11.h>
|
||||
#include <pybind11/operators.h>
|
||||
#include <pybind11/stl.h>
|
||||
#include <pybind11/stl/filesystem.h>
|
||||
#include <pybind11/numpy.h>
|
||||
#include "typecaster.h"
|
||||
|
||||
Reference in New Issue
Block a user