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