@@ -1104,6 +1104,7 @@ def set_time_constraints(self, channels,
11041104 def set_csd (self , channels , csd ,
11051105 coupling = 'AC' ,
11061106 ignored_frequency_peaks = None ,
1107+ ignore_harmonics = False ,
11071108 calc_icov = True ):
11081109 """
11091110 Add csd, calculate inverse (optional)
@@ -1131,6 +1132,8 @@ def set_csd(self, channels, csd,
11311132 calculation. The nearest bin (both positive/negative frequencies) from
11321133 given frequency is selected and CSD frequency set to infinity.
11331134
1135+ ignore_harmonics : boolean
1136+ if True, harmonics of the ignored_frequency_peaks are also ignored up to max frequency
11341137
11351138 Returns
11361139 -------
@@ -1173,11 +1176,13 @@ def set_csd(self, channels, csd,
11731176 if calc_icov :
11741177 self .calc_icovf (channel_name ,
11751178 coupling = coupling ,
1176- ignored_frequency_peaks = ignored_frequency_peaks )
1179+ ignored_frequency_peaks = ignored_frequency_peaks ,
1180+ ignore_harmonics = ignore_harmonics )
11771181
11781182
11791183 def set_psd (self , channel , psd , coupling = 'AC' ,
1180- ignored_frequency_peaks = None ):
1184+ ignored_frequency_peaks = None ,
1185+ ignore_harmonics = False ):
11811186 """
11821187 Add psd for specified channel
11831188 If psd already exist, it is overwritten
@@ -1202,7 +1207,9 @@ def set_psd(self, channel, psd, coupling='AC',
12021207 frequencies that are ignored in optimal filter
12031208 calculation. The nearest bin (both positive/negative frequencies) from
12041209 given frequency is selected and CSD frequency set to infinity.
1205-
1210+
1211+ ignore_harmonics : boolean
1212+ if True, harmonics of the ignored_frequency_peaks are also ignored up to max frequency
12061213
12071214 Returns
12081215 -------
@@ -1236,13 +1243,15 @@ def set_psd(self, channel, psd, coupling='AC',
12361243 csd = psd [np .newaxis , np .newaxis , :] # now shape is (1, 1, nfreq)
12371244
12381245 self .set_csd (channel , csd , coupling = coupling ,
1239- ignored_frequency_peaks = ignored_frequency_peaks )
1246+ ignored_frequency_peaks = ignored_frequency_peaks ,
1247+ ignore_harmonics = ignore_harmonics )
12401248
12411249
12421250
12431251 def calc_icovf (self , channels ,
12441252 coupling = 'AC' ,
1245- ignored_frequency_peaks = None ):
1253+ ignored_frequency_peaks = None ,
1254+ ignore_harmonics = False ):
12461255 """
12471256 A function that inverts the csd or covariance noise matrix between channels.
12481257
@@ -1265,7 +1274,8 @@ def calc_icovf(self, channels,
12651274 calculation. The nearest bin (both positive/negative frequencies) from
12661275 given frequency is selected and CSD frequency set to infinity.
12671276
1268-
1277+ ignore_harmonics : boolean
1278+ if True, harmonics of the ignored_frequency_peaks are also ignored up to max frequency
12691279
12701280 Returns
12711281 -------
@@ -1287,29 +1297,70 @@ def calc_icovf(self, channels,
12871297 if coupling == 'AC' :
12881298 temp_icovf [:,:,0 ] = 0.0
12891299
1300+
12901301 if ignored_frequency_peaks is not None :
12911302
1303+ # normalize input to a list
12921304 if not isinstance (ignored_frequency_peaks , list ):
12931305 ignored_frequency_peaks = [ignored_frequency_peaks ]
1306+ print (ignored_frequency_peaks )
1307+ freqs = np .asarray (self ._fft_freqs )
1308+ fmax = float (np .max (np .abs (freqs )))
1309+
1310+ # tolerance ?
1311+ """
1312+ # estimate frequency-bin spacing (robust to sign order)
1313+ # sort by absolute frequency to avoid the 0 jump
1314+ sort_idx = np.argsort(np.abs(freqs))
1315+ df_est = np.median(np.diff(np.sort(np.abs(freqs))))
1316+ if not np.isfinite(df_est) or df_est <= 0:
1317+ # fallback if something weird with freqs
1318+ df_est = np.min(np.abs(np.diff(np.sort(freqs)))) if freqs.size > 1 else 0.0
1319+
1320+ # tolerance: half a bin by default
1321+ tol = 0.5 * df_est if df_est > 0 else 0.0
1322+ """
1323+
1324+ # accumulate all bins to zero in one mask
1325+ mask_all = np .zeros_like (freqs , dtype = bool )
1326+
1327+ for f in ignored_frequency_peaks :
1328+ if f is None or not np .isfinite (f ):
1329+ continue
12941330
1295- for freq in ignored_frequency_peaks :
1331+ f = float (np .abs (f ))
1332+ if f == 0.0 :
1333+ # Special case: DC (closest-to-zero bin)
1334+ idx0 = int (np .argmin (np .abs (freqs )))
1335+ mask_all [idx0 ] = True
1336+ continue
12961337
1297- # make sure frequency positive
1298- freq = np .abs (freq )
1338+ # Build the target frequencies to zero
1339+ if ignore_harmonics :
1340+ kmax = int (np .floor (fmax / f ))
1341+ if kmax <= 0 :
1342+ continue
1343+ targets = f * np .arange (1 , kmax + 1 , dtype = float )
1344+ else :
1345+ targets = np .array ([f ], dtype = float )
12991346
1300- # positive frequency
1301- idx_pos = int (np .argmin (np .abs (self ._fft_freqs - freq )))
1302- mask = np .zeros_like (self ._fft_freqs , dtype = bool )
1303- mask [idx_pos ] = True
1347+ # For each target harmonic, hit both + and - sides
1348+ for t in targets :
1349+
1350+ # positive side
1351+ idx_pos = int (np .argmin (np .abs (freqs - t )))
1352+ #if np.abs(freqs[idx_pos] - t) <= tol:
1353+ mask_all [idx_pos ] = True
1354+
1355+ # negative side
1356+ idx_neg = int (np .argmin (np .abs (freqs + t )))
1357+ #if np.abs(freqs[idx_neg] + t) <= tol:
1358+ mask_all [idx_neg ] = True
1359+
1360+ # apply once
1361+ temp_icovf [..., mask_all ] = 0.0
13041362
1305- # negative frequency
1306- idx_neg = int (np .argmin (np .abs (self ._fft_freqs + freq )))
1307- mask [idx_neg ] = True
13081363
1309- # modify
1310- temp_icovf [..., mask ] = 0.0
1311-
1312-
13131364 self ._icovf [channel_name ] = temp_icovf
13141365
13151366
0 commit comments