1- import json
21
3- import pandas as pd
4- import numpy as np
52import logging
63
7- from pybpodapi . state_machine import StateMachine
4+ import numpy as np
85
96from iblrig import sound
107from iblrig .base_tasks import BaseSession , BpodMixin
118from iblrig .misc import get_task_arguments
9+ from pybpodapi .state_machine import StateMachine
1210
1311logger = logging .getLogger ('iblrig' )
1412
1513
1614class Session (BpodMixin , BaseSession ):
1715 protocol_name = 'samuel_tonotopicMapping'
1816
17+ frequencies : list [int ] = []
18+ sequence : list [int ] = []
19+
1920 def __init__ (self , * args , ** kwargs ):
2021 super ().__init__ (* args , ** kwargs )
2122
2223 assert self .hardware_settings .device_sound .OUTPUT == 'harp' , 'This task requires a Harp sound-card'
2324 assert self .task_params ['n_freqs' ] <= 31 , 'Harp only supports up to 31 individual sounds'
2425
2526 # define frequencies (log spaced from freq_0 to freq_1, rounded to nearest integer)
26- self .frequencies = np .logspace (
27+ Session .frequencies = np .logspace (
2728 np .log10 (self .task_params ['freq_0' ]),
2829 np .log10 (self .task_params ['freq_1' ]),
2930 num = self .task_params ['n_freqs' ],
3031 )
31- self .frequencies = np .round (self .frequencies ).astype (int )
32+ Session .frequencies = np .round (self .frequencies ).astype (int )
33+
34+ # repetitions per state machine run (253 states max)
35+ self .repetitions = 253 // len (self .frequencies )
3236
3337 # generate stimuli
3438 self .stimuli = []
@@ -53,43 +57,58 @@ def start_mixin_sound(self):
5357
5458 module = self .bpod .sound_card
5559 module_port = f'Serial{ module .serial_port if module is not None else "3" } '
56- for idx , harp_index in enumerate (self .indices ):
57- bpod_message = [ord ("P" ), harp_index ]
60+ for frequency_idx , harp_idx in enumerate (self .indices ):
61+ bpod_message = [ord ('P' ), harp_idx ]
5862 bpod_action = (module_port , self .bpod ._define_message (self .bpod .sound_card , bpod_message ))
59- self .bpod .actions .update ({f'play_{ idx } ' : bpod_action })
63+ self .bpod .actions .update ({f'freq_{ frequency_idx } ' : bpod_action })
64+
65+ self .bpod .softcode_handler_function = Session .softcode_handler
6066
6167 def start_hardware (self ):
6268 self .start_mixin_bpod ()
6369 self .start_mixin_sound ()
6470
65- def get_state_machine (self , sequence ):
71+ @staticmethod
72+ def get_state_name (state_idx : int ):
73+ if state_idx < len (Session .sequence ):
74+ return f'{ state_idx + 1 :03d} _{ Session .frequencies [Session .sequence [state_idx ]]} '
75+ else :
76+ return 'exit'
77+
78+ @staticmethod
79+ def softcode_handler (softcode : int ) -> None :
80+ """log some information about the current state"""
81+ state_index = softcode - 1
82+ frequency_index = Session .sequence [state_index ]
83+ frequency = Session .frequencies [frequency_index ]
84+ n_states = len (Session .sequence )
85+ logger .info (f'{ state_index + 1 :03d} /{ n_states } : { frequency :5d} Hz' )
86+
87+ def get_state_machine (self , seed : int ) -> StateMachine :
88+ # generate shuffled sequence, seeded with state machine number
89+ Session .sequence = np .repeat (np .arange (len (self .frequencies )), self .repetitions )
90+ np .random .seed (seed )
91+ np .random .shuffle (Session .sequence )
92+
93+ # build state machine
6694 sma = StateMachine (self .bpod )
67- # table = self.sequence_table[self.sequence_table['sequence'] == sequence].reindex()
68-
69- for i , frequency in enumerate (self .frequencies ):
95+ for state_idx , frequency_idx in enumerate (Session .sequence ):
7096 sma .add_state (
71- state_name = f"trigger_sound_ { i } " ,
97+ state_name = self . get_state_name ( state_idx ) ,
7298 state_timer = self .task_params ['d_sound' ] + self .task_params ['d_pause' ],
73- output_actions = [self .bpod .actions [f'play_{ i } ' ]],
74- state_change_conditions = {
75- "Tup" : f"trigger_sound_{ i + 1 } " ,
76- },
99+ output_actions = [self .bpod .actions [f'freq_{ frequency_idx } ' ], ('SoftCode' , state_idx + 1 )],
100+ state_change_conditions = {'Tup' : self .get_state_name (state_idx + 1 )},
77101 )
78- sma .add_state (
79- state_name = f"trigger_sound_{ i + 1 } " ,
80- state_timer = 0.0 ,
81- state_change_conditions = {"Tup" : "exit" },
82- )
83102 return sma
84103
85104 def _run (self ):
86- logger .info (" Sending spacers to BNC ports" )
105+ logger .info (' Sending spacers to BNC ports' )
87106 self .send_spacers ()
88107
89108 sma = self .get_state_machine (1 )
90109 self .bpod .send_state_machine (sma )
91110 self .bpod .run_state_machine (sma )
92-
111+ self . bpod . session . current_trial . export ()
93112
94113
95114if __name__ == '__main__' : # pragma: no cover
0 commit comments