src.acoustools.Levitator
1from ctypes import CDLL, POINTER 2import ctypes 3 4import torch, os 5 6from acoustools.Utilities import get_convert_indexes 7from torch import Tensor 8 9 10class LevitatorController(): 11 ''' 12 Class to enable the manipulation of an OpenMPD style acoustic levitator from python. 13 ''' 14 15 def __init__(self, bin_path:str|None = None, ids:tuple[int] = (1000,999), matBoardToWorld:list[int]|None=None, 16 print_lines:bool=False): 17 ''' 18 Creates the controller - reccomended to use in a `with` block\n 19 ```python 20 from acoustools.Levitator import LevitatorController 21 from acoustools.Utilities import create_points, add_lev_sig, propagate_abs 22 from acoustools.Solvers import wgs 23 24 p = create_points(1,1,x=0,y=0,z=0) 25 x = wgs(p) 26 print(propagate_abs(x,p)) 27 print(x.shape) 28 x = add_lev_sig(x) 29 30 with LevitatorController(ids=-1) as lev: 31 32 lev.levitate(x) 33 print('Levitating...') 34 input() 35 print('Stopping...') 36 37 ``` 38 THIS CHANGES THE CURRENT WORKING DIRECTORY AND THEN CHANGES IT BACK \n 39 :param bin_path: The path to the binary files needed. If `None` will use files contained in AcousTools. Default: None. 40 :param ids: IDs of boards. Default `(1000,999)`. For two board setup will be (Top, Bottom) if `-1` then all messages will be ignored. Use when testing code but no device is conncted 41 :param matBoardToWorld: Matric defining the mapping between simulated and real boards. When `None` uses a default setting. Default `None`. 42 :param print_lines: If False supresses some print messages 43 ''' 44 45 self.mode = 1 46 ''' 47 @private 48 ''' 49 if type(ids) == int: 50 ids = (ids,) 51 52 if ids[0] == -1: 53 self.mode = 0 54 print('Virtual Levitator mode - no messages will be sent') 55 else: 56 if bin_path is None: 57 self.bin_path = os.path.dirname(__file__)+"/../../bin/x64/" 58 59 cwd = os.getcwd() 60 os.chdir(self.bin_path) 61 files = os.listdir() 62 63 for id in ids: 64 if 'board_'+str(id)+'.pat' not in files: 65 data_file = open('board_master.pat','r') 66 data = data_file.read() 67 68 file = open('board_'+str(id)+'.pat','w') 69 data_id = data.replace('<XXXXXXXX>',str(id)) 70 file.write(data_id) 71 file.close() 72 data_file.close() 73 74 75 print(os.getcwd()) 76 self.levitatorLib = CDLL(self.bin_path+'Levitator.dll') 77 78 self.board_number = len(ids) 79 self.ids = (ctypes.c_int * self.board_number)(*ids) 80 81 if matBoardToWorld is None: 82 if self.board_number == 2: 83 self.matBoardToWorld = (ctypes.c_float * (16*self.board_number)) ( 84 1, 0, 0, 0, 85 0, 1, 0, 0, 86 0, 0, 1, 0, 87 0, 0, 0, 1, 88 89 1, 0, 0, 0, 90 0, 1, 0, 0, 91 0, 0, 1, 0, 92 0, 0, 0, 1 93 94 95 ) 96 elif self.board_number == 1: 97 self.matBoardToWorld = (ctypes.c_float * (16*self.board_number)) ( 98 1, 0, 0, 0, 99 0, 1, 0, 0, 100 0, 0, 1, 0, 101 0, 0, 0, 1 102 ) 103 else: 104 raise ValueError('For number of boards > 2, matBoardToWorld shouldnt be None') 105 106 else: 107 self.matBoardToWorld = (ctypes.c_float * (16*self.board_number))(*matBoardToWorld) 108 109 110 self.levitatorLib.connect_to_levitator.argtypes = [POINTER(ctypes.c_int), POINTER(ctypes.c_float), ctypes.c_int, ctypes.c_bool] 111 self.levitatorLib.connect_to_levitator.restype = ctypes.c_void_p 112 self.controller = self.levitatorLib.connect_to_levitator(self.ids,self.matBoardToWorld,self.board_number,print_lines) 113 114 os.chdir(cwd) 115 116 self.IDX = get_convert_indexes(256*self.board_number).cpu().detach() 117 118 def __enter__(self): 119 return self 120 121 def __exit__(self, exc_type, exc_value, traceback) -> None: 122 self.disconnect() 123 124 125 def send_message(self, phases, amplitudes=None, relative_amplitude=1, num_geometries = 1, sleep_ms = 0, loop=False, num_loops = 0): 126 ''' 127 RECCOMENDED NOT TO USE - USE `levitate` INSTEAD\\ 128 @private 129 sends messages to levitator 130 ''' 131 if self.mode: 132 self.levitatorLib.send_message.argtypes = [ctypes.c_void_p,POINTER(ctypes.c_float), POINTER(ctypes.c_float), ctypes.c_float, ctypes.c_int, ctypes.c_int, ctypes.c_int] 133 self.levitatorLib.send_message(self.controller,phases,amplitudes,relative_amplitude,num_geometries, sleep_ms, loop, num_loops) 134 135 def disconnect(self): 136 ''' 137 Disconnects the levitator 138 ''' 139 if self.mode: 140 self.levitatorLib.disconnect.argtypes = [ctypes.c_void_p] 141 self.levitatorLib.disconnect(self.controller) 142 143 def turn_off(self): 144 ''' 145 Turns of all transducers 146 ''' 147 if self.mode: 148 self.levitatorLib.turn_off.argtypes = [ctypes.c_void_p] 149 self.levitatorLib.turn_off(self.controller) 150 151 def set_frame_rate(self, frame_rate:int): 152 ''' 153 Set a new framerate 154 :param frame_rate: The new frame rate to use. Note OpenMPD cannot use framerates below 157Hz 155 ''' 156 if self.mode: 157 self.levitatorLib.set_new_frame_rate.argtypes = [ctypes.c_void_p, ctypes.c_int] 158 new_frame_rate = self.levitatorLib.set_new_frame_rate(self.controller, frame_rate) 159 160 def levitate(self, hologram:list[Tensor]|Tensor, relative_amplitude:int=-1, permute:bool=True, sleep_ms:float = 0, loop:bool=False, num_loops:int=0): 161 ''' 162 Send a single phase map to the levitator - This is the recomended function to use as will deal with dtype conversions etc 163 :param hologram: `Torch.Tensor` of phases or list of `Torch.Tensor` of phases, expects a batched dimension in dim 0. If phases is complex then ` phases = torch.angle(hologram)` will be run for phase and ` amp = torch.abs(hologram)` for amplitude, else phases left as is 164 :param relative_amplitude: Single value [0,1] or -1 to set amplitude to. If -1 will ignore Default -1 165 :param permute: Convert between acoustools transducer order and OpenMPD. Default True. 166 :param sleep_ms: Time to wait between frames in ms. 167 :param loop: If True will restart from the start of phases, default False 168 :param num_loops: A set number of times to repeat the phases 169 ''' 170 171 172 if self.mode: 173 to_output = [] 174 to_output_amplitudes = [] 175 176 if type(hologram) is Tensor and hologram.shape[0] > 1: 177 holos = [] 178 for h in hologram: 179 holos.append(h.unsqueeze(0).cpu().detach()) 180 hologram = holos 181 182 183 if type(hologram) is list: 184 #chunk this up - blocks of 32.... 185 num_geometries = len(hologram) 186 for phases_elem in hologram: 187 phases_elem = phases_elem.cpu().detach() 188 189 if permute: 190 phases_elem = phases_elem[:,self.IDX] 191 192 if torch.is_complex(phases_elem): 193 amp_elem = torch.abs(phases_elem) 194 phases_elem = torch.angle(phases_elem) 195 196 else: 197 amp_elem = torch.ones_like(phases_elem) 198 199 to_output = to_output + phases_elem.squeeze().tolist() 200 to_output_amplitudes = to_output_amplitudes + amp_elem.squeeze().tolist() 201 else: 202 num_geometries = 1 203 if permute: 204 hologram = hologram.cpu().detach() 205 hologram = hologram[:,self.IDX] 206 207 if torch.is_complex(hologram): 208 amp = torch.abs(hologram) 209 hologram = torch.angle(hologram) 210 else: 211 amp = torch.ones_like(hologram) 212 to_output = hologram[0].squeeze().tolist() 213 to_output_amplitudes = amp[0].squeeze().tolist() 214 215 216 phases = (ctypes.c_float * (256*self.board_number *num_geometries))(*to_output) 217 218 219 if relative_amplitude == -1: 220 amplitudes = (ctypes.c_float * (256*self.board_number*num_geometries))(*to_output_amplitudes) 221 else: 222 amplitudes = None 223 relative_amplitude = ctypes.c_float(relative_amplitude) 224 225 226 self.send_message(phases, amplitudes, relative_amplitude, num_geometries,sleep_ms=sleep_ms,loop=loop,num_loops=num_loops)
class
LevitatorController:
11class LevitatorController(): 12 ''' 13 Class to enable the manipulation of an OpenMPD style acoustic levitator from python. 14 ''' 15 16 def __init__(self, bin_path:str|None = None, ids:tuple[int] = (1000,999), matBoardToWorld:list[int]|None=None, 17 print_lines:bool=False): 18 ''' 19 Creates the controller - reccomended to use in a `with` block\n 20 ```python 21 from acoustools.Levitator import LevitatorController 22 from acoustools.Utilities import create_points, add_lev_sig, propagate_abs 23 from acoustools.Solvers import wgs 24 25 p = create_points(1,1,x=0,y=0,z=0) 26 x = wgs(p) 27 print(propagate_abs(x,p)) 28 print(x.shape) 29 x = add_lev_sig(x) 30 31 with LevitatorController(ids=-1) as lev: 32 33 lev.levitate(x) 34 print('Levitating...') 35 input() 36 print('Stopping...') 37 38 ``` 39 THIS CHANGES THE CURRENT WORKING DIRECTORY AND THEN CHANGES IT BACK \n 40 :param bin_path: The path to the binary files needed. If `None` will use files contained in AcousTools. Default: None. 41 :param ids: IDs of boards. Default `(1000,999)`. For two board setup will be (Top, Bottom) if `-1` then all messages will be ignored. Use when testing code but no device is conncted 42 :param matBoardToWorld: Matric defining the mapping between simulated and real boards. When `None` uses a default setting. Default `None`. 43 :param print_lines: If False supresses some print messages 44 ''' 45 46 self.mode = 1 47 ''' 48 @private 49 ''' 50 if type(ids) == int: 51 ids = (ids,) 52 53 if ids[0] == -1: 54 self.mode = 0 55 print('Virtual Levitator mode - no messages will be sent') 56 else: 57 if bin_path is None: 58 self.bin_path = os.path.dirname(__file__)+"/../../bin/x64/" 59 60 cwd = os.getcwd() 61 os.chdir(self.bin_path) 62 files = os.listdir() 63 64 for id in ids: 65 if 'board_'+str(id)+'.pat' not in files: 66 data_file = open('board_master.pat','r') 67 data = data_file.read() 68 69 file = open('board_'+str(id)+'.pat','w') 70 data_id = data.replace('<XXXXXXXX>',str(id)) 71 file.write(data_id) 72 file.close() 73 data_file.close() 74 75 76 print(os.getcwd()) 77 self.levitatorLib = CDLL(self.bin_path+'Levitator.dll') 78 79 self.board_number = len(ids) 80 self.ids = (ctypes.c_int * self.board_number)(*ids) 81 82 if matBoardToWorld is None: 83 if self.board_number == 2: 84 self.matBoardToWorld = (ctypes.c_float * (16*self.board_number)) ( 85 1, 0, 0, 0, 86 0, 1, 0, 0, 87 0, 0, 1, 0, 88 0, 0, 0, 1, 89 90 1, 0, 0, 0, 91 0, 1, 0, 0, 92 0, 0, 1, 0, 93 0, 0, 0, 1 94 95 96 ) 97 elif self.board_number == 1: 98 self.matBoardToWorld = (ctypes.c_float * (16*self.board_number)) ( 99 1, 0, 0, 0, 100 0, 1, 0, 0, 101 0, 0, 1, 0, 102 0, 0, 0, 1 103 ) 104 else: 105 raise ValueError('For number of boards > 2, matBoardToWorld shouldnt be None') 106 107 else: 108 self.matBoardToWorld = (ctypes.c_float * (16*self.board_number))(*matBoardToWorld) 109 110 111 self.levitatorLib.connect_to_levitator.argtypes = [POINTER(ctypes.c_int), POINTER(ctypes.c_float), ctypes.c_int, ctypes.c_bool] 112 self.levitatorLib.connect_to_levitator.restype = ctypes.c_void_p 113 self.controller = self.levitatorLib.connect_to_levitator(self.ids,self.matBoardToWorld,self.board_number,print_lines) 114 115 os.chdir(cwd) 116 117 self.IDX = get_convert_indexes(256*self.board_number).cpu().detach() 118 119 def __enter__(self): 120 return self 121 122 def __exit__(self, exc_type, exc_value, traceback) -> None: 123 self.disconnect() 124 125 126 def send_message(self, phases, amplitudes=None, relative_amplitude=1, num_geometries = 1, sleep_ms = 0, loop=False, num_loops = 0): 127 ''' 128 RECCOMENDED NOT TO USE - USE `levitate` INSTEAD\\ 129 @private 130 sends messages to levitator 131 ''' 132 if self.mode: 133 self.levitatorLib.send_message.argtypes = [ctypes.c_void_p,POINTER(ctypes.c_float), POINTER(ctypes.c_float), ctypes.c_float, ctypes.c_int, ctypes.c_int, ctypes.c_int] 134 self.levitatorLib.send_message(self.controller,phases,amplitudes,relative_amplitude,num_geometries, sleep_ms, loop, num_loops) 135 136 def disconnect(self): 137 ''' 138 Disconnects the levitator 139 ''' 140 if self.mode: 141 self.levitatorLib.disconnect.argtypes = [ctypes.c_void_p] 142 self.levitatorLib.disconnect(self.controller) 143 144 def turn_off(self): 145 ''' 146 Turns of all transducers 147 ''' 148 if self.mode: 149 self.levitatorLib.turn_off.argtypes = [ctypes.c_void_p] 150 self.levitatorLib.turn_off(self.controller) 151 152 def set_frame_rate(self, frame_rate:int): 153 ''' 154 Set a new framerate 155 :param frame_rate: The new frame rate to use. Note OpenMPD cannot use framerates below 157Hz 156 ''' 157 if self.mode: 158 self.levitatorLib.set_new_frame_rate.argtypes = [ctypes.c_void_p, ctypes.c_int] 159 new_frame_rate = self.levitatorLib.set_new_frame_rate(self.controller, frame_rate) 160 161 def levitate(self, hologram:list[Tensor]|Tensor, relative_amplitude:int=-1, permute:bool=True, sleep_ms:float = 0, loop:bool=False, num_loops:int=0): 162 ''' 163 Send a single phase map to the levitator - This is the recomended function to use as will deal with dtype conversions etc 164 :param hologram: `Torch.Tensor` of phases or list of `Torch.Tensor` of phases, expects a batched dimension in dim 0. If phases is complex then ` phases = torch.angle(hologram)` will be run for phase and ` amp = torch.abs(hologram)` for amplitude, else phases left as is 165 :param relative_amplitude: Single value [0,1] or -1 to set amplitude to. If -1 will ignore Default -1 166 :param permute: Convert between acoustools transducer order and OpenMPD. Default True. 167 :param sleep_ms: Time to wait between frames in ms. 168 :param loop: If True will restart from the start of phases, default False 169 :param num_loops: A set number of times to repeat the phases 170 ''' 171 172 173 if self.mode: 174 to_output = [] 175 to_output_amplitudes = [] 176 177 if type(hologram) is Tensor and hologram.shape[0] > 1: 178 holos = [] 179 for h in hologram: 180 holos.append(h.unsqueeze(0).cpu().detach()) 181 hologram = holos 182 183 184 if type(hologram) is list: 185 #chunk this up - blocks of 32.... 186 num_geometries = len(hologram) 187 for phases_elem in hologram: 188 phases_elem = phases_elem.cpu().detach() 189 190 if permute: 191 phases_elem = phases_elem[:,self.IDX] 192 193 if torch.is_complex(phases_elem): 194 amp_elem = torch.abs(phases_elem) 195 phases_elem = torch.angle(phases_elem) 196 197 else: 198 amp_elem = torch.ones_like(phases_elem) 199 200 to_output = to_output + phases_elem.squeeze().tolist() 201 to_output_amplitudes = to_output_amplitudes + amp_elem.squeeze().tolist() 202 else: 203 num_geometries = 1 204 if permute: 205 hologram = hologram.cpu().detach() 206 hologram = hologram[:,self.IDX] 207 208 if torch.is_complex(hologram): 209 amp = torch.abs(hologram) 210 hologram = torch.angle(hologram) 211 else: 212 amp = torch.ones_like(hologram) 213 to_output = hologram[0].squeeze().tolist() 214 to_output_amplitudes = amp[0].squeeze().tolist() 215 216 217 phases = (ctypes.c_float * (256*self.board_number *num_geometries))(*to_output) 218 219 220 if relative_amplitude == -1: 221 amplitudes = (ctypes.c_float * (256*self.board_number*num_geometries))(*to_output_amplitudes) 222 else: 223 amplitudes = None 224 relative_amplitude = ctypes.c_float(relative_amplitude) 225 226 227 self.send_message(phases, amplitudes, relative_amplitude, num_geometries,sleep_ms=sleep_ms,loop=loop,num_loops=num_loops)
Class to enable the manipulation of an OpenMPD style acoustic levitator from python.
LevitatorController( bin_path: str | None = None, ids: tuple[int] = (1000, 999), matBoardToWorld: list[int] | None = None, print_lines: bool = False)
16 def __init__(self, bin_path:str|None = None, ids:tuple[int] = (1000,999), matBoardToWorld:list[int]|None=None, 17 print_lines:bool=False): 18 ''' 19 Creates the controller - reccomended to use in a `with` block\n 20 ```python 21 from acoustools.Levitator import LevitatorController 22 from acoustools.Utilities import create_points, add_lev_sig, propagate_abs 23 from acoustools.Solvers import wgs 24 25 p = create_points(1,1,x=0,y=0,z=0) 26 x = wgs(p) 27 print(propagate_abs(x,p)) 28 print(x.shape) 29 x = add_lev_sig(x) 30 31 with LevitatorController(ids=-1) as lev: 32 33 lev.levitate(x) 34 print('Levitating...') 35 input() 36 print('Stopping...') 37 38 ``` 39 THIS CHANGES THE CURRENT WORKING DIRECTORY AND THEN CHANGES IT BACK \n 40 :param bin_path: The path to the binary files needed. If `None` will use files contained in AcousTools. Default: None. 41 :param ids: IDs of boards. Default `(1000,999)`. For two board setup will be (Top, Bottom) if `-1` then all messages will be ignored. Use when testing code but no device is conncted 42 :param matBoardToWorld: Matric defining the mapping between simulated and real boards. When `None` uses a default setting. Default `None`. 43 :param print_lines: If False supresses some print messages 44 ''' 45 46 self.mode = 1 47 ''' 48 @private 49 ''' 50 if type(ids) == int: 51 ids = (ids,) 52 53 if ids[0] == -1: 54 self.mode = 0 55 print('Virtual Levitator mode - no messages will be sent') 56 else: 57 if bin_path is None: 58 self.bin_path = os.path.dirname(__file__)+"/../../bin/x64/" 59 60 cwd = os.getcwd() 61 os.chdir(self.bin_path) 62 files = os.listdir() 63 64 for id in ids: 65 if 'board_'+str(id)+'.pat' not in files: 66 data_file = open('board_master.pat','r') 67 data = data_file.read() 68 69 file = open('board_'+str(id)+'.pat','w') 70 data_id = data.replace('<XXXXXXXX>',str(id)) 71 file.write(data_id) 72 file.close() 73 data_file.close() 74 75 76 print(os.getcwd()) 77 self.levitatorLib = CDLL(self.bin_path+'Levitator.dll') 78 79 self.board_number = len(ids) 80 self.ids = (ctypes.c_int * self.board_number)(*ids) 81 82 if matBoardToWorld is None: 83 if self.board_number == 2: 84 self.matBoardToWorld = (ctypes.c_float * (16*self.board_number)) ( 85 1, 0, 0, 0, 86 0, 1, 0, 0, 87 0, 0, 1, 0, 88 0, 0, 0, 1, 89 90 1, 0, 0, 0, 91 0, 1, 0, 0, 92 0, 0, 1, 0, 93 0, 0, 0, 1 94 95 96 ) 97 elif self.board_number == 1: 98 self.matBoardToWorld = (ctypes.c_float * (16*self.board_number)) ( 99 1, 0, 0, 0, 100 0, 1, 0, 0, 101 0, 0, 1, 0, 102 0, 0, 0, 1 103 ) 104 else: 105 raise ValueError('For number of boards > 2, matBoardToWorld shouldnt be None') 106 107 else: 108 self.matBoardToWorld = (ctypes.c_float * (16*self.board_number))(*matBoardToWorld) 109 110 111 self.levitatorLib.connect_to_levitator.argtypes = [POINTER(ctypes.c_int), POINTER(ctypes.c_float), ctypes.c_int, ctypes.c_bool] 112 self.levitatorLib.connect_to_levitator.restype = ctypes.c_void_p 113 self.controller = self.levitatorLib.connect_to_levitator(self.ids,self.matBoardToWorld,self.board_number,print_lines) 114 115 os.chdir(cwd) 116 117 self.IDX = get_convert_indexes(256*self.board_number).cpu().detach()
Creates the controller - reccomended to use in a with block
from acoustools.Levitator import LevitatorController
from acoustools.Utilities import create_points, add_lev_sig, propagate_abs
from acoustools.Solvers import wgs
p = create_points(1,1,x=0,y=0,z=0)
x = wgs(p)
print(propagate_abs(x,p))
print(x.shape)
x = add_lev_sig(x)
with LevitatorController(ids=-1) as lev:
lev.levitate(x)
print('Levitating...')
input()
print('Stopping...')
THIS CHANGES THE CURRENT WORKING DIRECTORY AND THEN CHANGES IT BACK
Parameters
- bin_path: The path to the binary files needed. If
Nonewill use files contained in AcousTools. Default: None. - ids: IDs of boards. Default
(1000,999). For two board setup will be (Top, Bottom) if-1then all messages will be ignored. Use when testing code but no device is conncted - matBoardToWorld: Matric defining the mapping between simulated and real boards. When
Noneuses a default setting. DefaultNone. - print_lines: If False supresses some print messages
def
disconnect(self):
136 def disconnect(self): 137 ''' 138 Disconnects the levitator 139 ''' 140 if self.mode: 141 self.levitatorLib.disconnect.argtypes = [ctypes.c_void_p] 142 self.levitatorLib.disconnect(self.controller)
Disconnects the levitator
def
turn_off(self):
144 def turn_off(self): 145 ''' 146 Turns of all transducers 147 ''' 148 if self.mode: 149 self.levitatorLib.turn_off.argtypes = [ctypes.c_void_p] 150 self.levitatorLib.turn_off(self.controller)
Turns of all transducers
def
set_frame_rate(self, frame_rate: int):
152 def set_frame_rate(self, frame_rate:int): 153 ''' 154 Set a new framerate 155 :param frame_rate: The new frame rate to use. Note OpenMPD cannot use framerates below 157Hz 156 ''' 157 if self.mode: 158 self.levitatorLib.set_new_frame_rate.argtypes = [ctypes.c_void_p, ctypes.c_int] 159 new_frame_rate = self.levitatorLib.set_new_frame_rate(self.controller, frame_rate)
Set a new framerate
Parameters
- frame_rate: The new frame rate to use. Note OpenMPD cannot use framerates below 157Hz
def
levitate( self, hologram: list[torch.Tensor] | torch.Tensor, relative_amplitude: int = -1, permute: bool = True, sleep_ms: float = 0, loop: bool = False, num_loops: int = 0):
161 def levitate(self, hologram:list[Tensor]|Tensor, relative_amplitude:int=-1, permute:bool=True, sleep_ms:float = 0, loop:bool=False, num_loops:int=0): 162 ''' 163 Send a single phase map to the levitator - This is the recomended function to use as will deal with dtype conversions etc 164 :param hologram: `Torch.Tensor` of phases or list of `Torch.Tensor` of phases, expects a batched dimension in dim 0. If phases is complex then ` phases = torch.angle(hologram)` will be run for phase and ` amp = torch.abs(hologram)` for amplitude, else phases left as is 165 :param relative_amplitude: Single value [0,1] or -1 to set amplitude to. If -1 will ignore Default -1 166 :param permute: Convert between acoustools transducer order and OpenMPD. Default True. 167 :param sleep_ms: Time to wait between frames in ms. 168 :param loop: If True will restart from the start of phases, default False 169 :param num_loops: A set number of times to repeat the phases 170 ''' 171 172 173 if self.mode: 174 to_output = [] 175 to_output_amplitudes = [] 176 177 if type(hologram) is Tensor and hologram.shape[0] > 1: 178 holos = [] 179 for h in hologram: 180 holos.append(h.unsqueeze(0).cpu().detach()) 181 hologram = holos 182 183 184 if type(hologram) is list: 185 #chunk this up - blocks of 32.... 186 num_geometries = len(hologram) 187 for phases_elem in hologram: 188 phases_elem = phases_elem.cpu().detach() 189 190 if permute: 191 phases_elem = phases_elem[:,self.IDX] 192 193 if torch.is_complex(phases_elem): 194 amp_elem = torch.abs(phases_elem) 195 phases_elem = torch.angle(phases_elem) 196 197 else: 198 amp_elem = torch.ones_like(phases_elem) 199 200 to_output = to_output + phases_elem.squeeze().tolist() 201 to_output_amplitudes = to_output_amplitudes + amp_elem.squeeze().tolist() 202 else: 203 num_geometries = 1 204 if permute: 205 hologram = hologram.cpu().detach() 206 hologram = hologram[:,self.IDX] 207 208 if torch.is_complex(hologram): 209 amp = torch.abs(hologram) 210 hologram = torch.angle(hologram) 211 else: 212 amp = torch.ones_like(hologram) 213 to_output = hologram[0].squeeze().tolist() 214 to_output_amplitudes = amp[0].squeeze().tolist() 215 216 217 phases = (ctypes.c_float * (256*self.board_number *num_geometries))(*to_output) 218 219 220 if relative_amplitude == -1: 221 amplitudes = (ctypes.c_float * (256*self.board_number*num_geometries))(*to_output_amplitudes) 222 else: 223 amplitudes = None 224 relative_amplitude = ctypes.c_float(relative_amplitude) 225 226 227 self.send_message(phases, amplitudes, relative_amplitude, num_geometries,sleep_ms=sleep_ms,loop=loop,num_loops=num_loops)
Send a single phase map to the levitator - This is the recomended function to use as will deal with dtype conversions etc
Parameters
- hologram:
Torch.Tensorof phases or list ofTorch.Tensorof phases, expects a batched dimension in dim 0. If phases is complex thenphases = torch.angle(hologram)will be run for phase andamp = torch.abs(hologram)for amplitude, else phases left as is - relative_amplitude: Single value [0,1] or -1 to set amplitude to. If -1 will ignore Default -1
- permute: Convert between acoustools transducer order and OpenMPD. Default True.
- sleep_ms: Time to wait between frames in ms.
- loop: If True will restart from the start of phases, default False
- num_loops: A set number of times to repeat the phases