public virtual IBFSample[] ApplyMontage(IBFSample[] samples, SignalFilter filter, int boardId, int numberOfChannels, int sampleRate) { var montagedSignal = new List <IBFSample>(); foreach (var nextSample in samples) { var newSample = new BFSampleImplementation(nextSample, NumberOfDerivations, 0, 0, 0); for (int i = 0; i < Derivations.Count; i++) { var result = 0.0; foreach (var nextChannel in Derivations[i].Channels) { result += nextSample.GetExgDataForChannel(nextChannel.RawChannelNumber) * nextChannel.Factor; } newSample.SetExgDataForChannel(i, result); } montagedSignal.Add(newSample); } samples = montagedSignal.ToArray(); if (filter != null) { samples = FilterBrainflowSample.FilterChunk(filter, samples, boardId, numberOfChannels, sampleRate); } return(samples); }
/// <summary> /// Add a function to the filter /// will determine if this function has dynamic parameters of data[] only, or data[] and sampling_rate /// </summary> static void AddSignalFilterFunction(SignalFilter filter, MethodInfo mi, Dictionary <string, string> paramDict, object[] parameters) { if (paramDict.ContainsKey("sampling_rate")) { filter.AddFunction(new SignalFilterFunctionDataSampleRate(typeof(DataFilter).Assembly, mi, parameters)); } else { filter.AddFunction(new SignalFilterFunctionData(typeof(DataFilter).Assembly, mi, parameters)); } }
/// <summary> /// Create a default filter for 60Hz mains noise /// </summary> static void CreateDefaultFilterNotch50() { var newFilter = new SignalFilter("default50"); MethodInfo mi = typeof(DataFilter).GetMethod("perform_bandstop", BindingFlags.Public | BindingFlags.Static); var paramDict = new Dictionary <string, string> { ["data"] = "", ["sampling_rate"] = "0", ["center_freq"] = "50.0", ["band_width"] = "2.0", ["order"] = "6", ["filter_type"] = "1", ["ripple"] = "1.0" }; // create object array from parameters, casting to proper type object[] parameters = mi.GetParameters().Select(p => paramDict[p.Name].Length > 0 ? Convert.ChangeType(paramDict[p.Name], p.ParameterType) : null).ToArray(); newFilter.AddFunction(new SignalFilterFunctionDataSampleRate(typeof(DataFilter).Assembly, mi, parameters)); mi = typeof(DataFilter).GetMethod("perform_bandpass", BindingFlags.Public | BindingFlags.Static); paramDict = new Dictionary <string, string> { ["data"] = "", ["sampling_rate"] = "0", ["center_freq"] = "25.5", ["band_width"] = "47", ["order"] = "2", ["filter_type"] = "0", ["ripple"] = "0.0" }; // create object array from parameters, casting to proper type parameters = mi.GetParameters().Select(p => paramDict[p.Name].Length > 0 ? Convert.ChangeType(paramDict[p.Name], p.ParameterType) : null).ToArray(); newFilter.AddFunction(new SignalFilterFunctionDataSampleRate(typeof(DataFilter).Assembly, mi, parameters)); mi = typeof(DataFilter).GetMethod("perform_wavelet_denoising", BindingFlags.Public | BindingFlags.Static); paramDict = new Dictionary <string, string> { ["data"] = "", ["wavelet"] = "db12", ["decomposition_level"] = "3", }; // create object array from parameters, casting to proper type parameters = mi.GetParameters().Select(p => paramDict[p.Name].Length > 0 ? Convert.ChangeType(paramDict[p.Name], p.ParameterType) : null).ToArray(); newFilter.AddFunction(new SignalFilterFunctionData(typeof(DataFilter).Assembly, mi, parameters)); Filters.Add(newFilter.Name, newFilter); }
// Implementation #region Implementation /// <summary> /// Constructor /// </summary> public RealTimeSignalProcessing(int boardId, int sampleRate, SignalFilter filter, ISignalMontage montage) { BoardId = boardId; NumberOfChannels = BrainhatBoardShim.GetNumberOfExgChannels(BoardId); SampleRate = sampleRate; Filter = filter; Montage = montage; Name = KeyName((Filter == null ? "" : Filter.Name), Montage.Name); PeriodMilliseconds = 33; FilterBufferLength = 10; ProcessingTimes = new ConcurrentQueue <double>(); FilteredData = new ConcurrentQueue <IBFSample>(); }
public static IBFSample[] FilterChunk(SignalFilter filter, IEnumerable <IBFSample> chunk, int boardId, int numberOfChannels, int sampleRate) { try { if (chunk == null || chunk.Count() == 0) { throw new ArgumentException("Invalid chunk"); } // copy the data for filtering var filteredSamples = new List <IBFSample>(chunk.Select(x => new BFSampleImplementation(x))); for (int i = 0; i < numberOfChannels; i++) { var filtered = filter.ApplyFilter(chunk.GetExgDataForChannel(i), sampleRate); for (int j = 0; j < chunk.Count(); j++) { filteredSamples[j].SetExgDataForChannel(i, filtered[j]); } } var lastTimeStamp = chunk.First().TimeStamp; int lastSampleIndex = (int)chunk.First().SampleIndex; for (int i = 0; i < filteredSamples.Count; i++) { filteredSamples[i].TimeStamp = lastTimeStamp + filteredSamples[i].SampleIndex.TimeBetweenSamples(lastSampleIndex, boardId, sampleRate); lastTimeStamp = filteredSamples[i].TimeStamp; lastSampleIndex = (int)filteredSamples[i].SampleIndex; } return(filteredSamples.ToArray()); } catch (Exception e) { throw e; } }
//public async Task<SignalFiltering> StartSignalFilteringAsync() //{ // var filterName = ActiveFilters.FirstOrDefault().Key; // if (filterName != null) // return await StartSignalFilteringAsync(filterName); // return null; //} /// <summary> /// Start the signal filtering task /// </summary> public async Task <RealTimeSignalProcessing> StartRealTimeSignalProcessingAsync(SignalFilter filter, ISignalMontage montage) { var useMontage = montage; if (useMontage == null) { useMontage = SignalMontages.MakeDefaultMontage(NumberOfChannels); } var montageName = useMontage.Name; var filterName = filter == null ? "XXXDEFAULTXXX" : filter.Name; if (CancelTokenSource == null) { Log?.Invoke(this, new LogEventArgs(Name, this, "StartSignalFiltering", $"You must start the processor first.", LogLevel.ERROR)); return(null); } if (ActiveFilters.ContainsKey(RealTimeSignalProcessing.KeyName(filterName, montageName))) { return(ActiveFilters[RealTimeSignalProcessing.KeyName(filterName, montageName)]); } var newFilter = new RealTimeSignalProcessing(BoardId, SampleRate, filter, useMontage) { FilterBufferLength = 30, }; newFilter.GetRawChunk = GetRawChunk; newFilter.Log += OnComponentLog; ActiveFilters.Add(RealTimeSignalProcessing.KeyName(filterName, montageName), newFilter); await newFilter.StartRealTimeProcessingAsync(); return(newFilter); }
/// <summary> /// Load filter definitions from XML file /// </summary> public void LoadSignalFilters(string xmlFilePath) { try { Filters.Clear(); using (var reader = new StreamReader(xmlFilePath)) { var doc = XDocument.Load(reader); var filters = doc.Element("brainHatSignalFilters")?.Element("Filters")?.Elements("Filter"); if (filters == null) { throw new Exception("Document does not hae a <Filters> element."); } foreach (var nextFilter in filters) { var filterName = nextFilter.Element("Name")?.Value; if (filterName == null || Filters.ContainsKey(filterName)) { throw new Exception("Filter does not have a name or name is duplicated."); } var newFilter = new SignalFilter(filterName); var functions = nextFilter.Element("Functions")?.Elements("Function"); if (functions == null) { throw new Exception($"Filter {filterName} does not have any functions."); } foreach (var nextFunction in nextFilter.Element("Functions")?.Elements("Function")) { // get function name from XML var functionName = nextFunction.Attribute("Name")?.Value; // get method from DataFilter class MethodInfo mi = typeof(DataFilter).GetMethod(functionName, BindingFlags.Public | BindingFlags.Static); if (mi == null) { throw new Exception($"Filter {filterName} specifies an invalid function"); } // get dictionary of parameters from XML var paramDict = nextFunction.Elements("Parameter").ToDictionary(d => d.Attribute("Name").Value, d => d.Attribute("Value").Value); // create object array from parameters, casting to proper type object[] parameters = mi.GetParameters().Select(p => paramDict[p.Name].Length > 0 ? Convert.ChangeType(paramDict[p.Name], p.ParameterType) : null).ToArray(); AddSignalFilterFunction(newFilter, mi, paramDict, parameters); } Filters.Add(filterName, newFilter); } } } catch (Exception e) { throw e; } }