Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions csharp_package/brainflow/brainflow/ml_module_library.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
Expand All @@ -19,7 +19,8 @@ public enum BrainFlowClassifiers
{
DEFAULT_CLASSIFIER = 0,
DYN_LIB_CLASSIFIER = 1,
ONNX_CLASSIFIER = 2
ONNX_CLASSIFIER = 2,
MOVING_AVERAGE_CLASSIFIER = 3
};

public static class MLModuleLibrary64
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ public enum BrainFlowClassifiers

DEFAULT_CLASSIFIER (0),
DYN_LIB_CLASSIFIER (1),
ONNX_CLASSIFIER (2);
ONNX_CLASSIFIER (2),
MOVING_AVERAGE_CLASSIFIER (3);

private final int protocol;
private static final Map<Integer, BrainFlowClassifiers> cl_map = new HashMap<Integer, BrainFlowClassifiers> ();
Expand Down
1 change: 1 addition & 0 deletions julia_package/brainflow/src/ml_model.jl
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ MetricType = Union{BrainFlowMetrics, Integer}
DEFAULT_CLASSIFIER = 0
DYN_LIB_CLASSIFIER = 1
ONNX_CLASSIFIER = 2
MOVING_AVERAGE_CLASSIFIER = 3

end

Expand Down
1 change: 1 addition & 0 deletions matlab_package/brainflow/BrainFlowClassifiers.m
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
DEFAULT_CLASSIFIER(0)
DYN_LIB_CLASSIFIER(1)
ONNX_CLASSIFIER(2)
MOVING_AVERAGE_CLASSIFIER(3)
end
end
1 change: 1 addition & 0 deletions nodejs_package/brainflow/brainflow.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,7 @@ export enum BrainFlowClassifiers {
DEFAULT_CLASSIFIER = 0,
USER_DEFINED = 1,
ONNX_CLASSIFIER = 2,
MOVING_AVERAGE_CLASSIFIER = 3,
}

export interface IBrainFlowInputParams {
Expand Down
1 change: 1 addition & 0 deletions python_package/brainflow/ml_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class BrainFlowClassifiers(enum.IntEnum):
DEFAULT_CLASSIFIER = 0 #:
DYN_LIB_CLASSIFIER = 1 #:
ONNX_CLASSIFIER = 2 #:
MOVING_AVERAGE_CLASSIFIER = 3 #:


class BrainFlowModelParams(object):
Expand Down
73 changes: 73 additions & 0 deletions python_package/examples/tests/moving_average_classifier.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import numpy as np
import sys
import os

# add python_package to sys.path
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..')))

from brainflow.ml_model import MLModel, BrainFlowMetrics, BrainFlowClassifiers, BrainFlowModelParams
from brainflow.exit_codes import BrainFlowError, BrainFlowExitCodes


def test_moving_average_classifier():
print("Testing MovingAverageClassifier...")

# 1. Test USER_DEFINED stream with window_len = 3
params = BrainFlowModelParams(
BrainFlowMetrics.USER_DEFINED.value,
BrainFlowClassifiers.MOVING_AVERAGE_CLASSIFIER.value
)
params.other_info = "3"

model = MLModel(params)
model.prepare()

# Step 1: Input 10.0 -> Avg: 10.0
out1 = model.predict(np.array([10.0], dtype=np.float64))
print(f"Step 1: In=10.0, Out={out1[0]}")
assert np.isclose(out1[0], 10.0)

# Step 2: Input 20.0 -> Avg: (10 + 20) / 2 = 15.0
out2 = model.predict(np.array([20.0], dtype=np.float64))
print(f"Step 2: In=20.0, Out={out2[0]}")
assert np.isclose(out2[0], 15.0)

# Step 3: Input 30.0 -> Avg: (10 + 20 + 30) / 3 = 20.0
out3 = model.predict(np.array([30.0], dtype=np.float64))
print(f"Step 3: In=30.0, Out={out3[0]}")
assert np.isclose(out3[0], 20.0)

# Step 4: Input 40.0 -> Avg: (20 + 30 + 40) / 3 = 30.0 (oldest 10.0 dropped)
out4 = model.predict(np.array([40.0], dtype=np.float64))
print(f"Step 4: In=40.0, Out={out4[0]}")
assert np.isclose(out4[0], 30.0)

model.release()

# 2. Test MINDFULNESS metric with MOVING_AVERAGE_CLASSIFIER
mf_params = BrainFlowModelParams(
BrainFlowMetrics.MINDFULNESS.value,
BrainFlowClassifiers.MOVING_AVERAGE_CLASSIFIER.value
)
mf_params.other_info = '{"window_len": 4}'

mf_model = MLModel(mf_params)
mf_model.prepare()

# 5 band powers input
feature_vector = np.array([0.1, 0.2, 0.3, 0.2, 0.2], dtype=np.float64)
mf_out1 = mf_model.predict(feature_vector)
print(f"Mindfulness moving avg 1: {mf_out1[0]}")
assert 0.0 <= mf_out1[0] <= 1.0

mf_out2 = mf_model.predict(feature_vector)
print(f"Mindfulness moving avg 2: {mf_out2[0]}")
assert np.isclose(mf_out1[0], mf_out2[0])

mf_model.release()

print("All MovingAverageClassifier tests passed successfully!")


if __name__ == '__main__':
test_moving_average_classifier()
1 change: 1 addition & 0 deletions rust_package/brainflow/src/ffi/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ pub enum BrainFlowClassifiers {
DefaultClassifier = 0,
DynLibClassifier = 1,
OnnxClassifier = 2,
MovingAverageClassifier = 3,
}
#[repr(i32)]
#[derive(FromPrimitive, ToPrimitive, Debug, Copy, Clone, Hash, PartialEq, Eq)]
Expand Down
1 change: 1 addition & 0 deletions src/ml/build.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ SET (ML_MODULE_SRC
${CMAKE_CURRENT_LIST_DIR}/base_classifier.cpp
${CMAKE_CURRENT_LIST_DIR}/mindfulness_classifier.cpp
${CMAKE_CURRENT_LIST_DIR}/generated/mindfulness_model.cpp
${CMAKE_CURRENT_LIST_DIR}/moving_average_classifier.cpp
)

add_library (
Expand Down
28 changes: 28 additions & 0 deletions src/ml/inc/moving_average_classifier.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#pragma once

#include <deque>
#include <memory>

#include "base_classifier.h"
#include "brainflow_constants.h"
#include "brainflow_model_params.h"


class MovingAverageClassifier : public BaseClassifier
{
protected:
int window_len;
std::deque<double> buffer;
double sum;
std::shared_ptr<BaseClassifier> base_classifier;

int parse_window_len ();

public:
MovingAverageClassifier (struct BrainFlowModelParams params);
~MovingAverageClassifier ();

int prepare () override;
int predict (double *data, int data_len, double *output, int *output_len) override;
int release () override;
};
5 changes: 5 additions & 0 deletions src/ml/ml_module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "dyn_lib_classifier.h"
#include "mindfulness_classifier.h"
#include "ml_module.h"
#include "moving_average_classifier.h"
#include "onnx_classifier.h"
#include "restfulness_classifier.h"

Expand Down Expand Up @@ -62,6 +63,10 @@ int prepare (const char *json_params)
{
model = std::shared_ptr<BaseClassifier> (new RestfulnessClassifier (key));
}
else if (key.classifier == (int)BrainFlowClassifiers::MOVING_AVERAGE_CLASSIFIER)
{
model = std::shared_ptr<BaseClassifier> (new MovingAverageClassifier (key));
}
else
{
return (int)BrainFlowExitCodes::UNSUPPORTED_CLASSIFIER_AND_METRIC_COMBINATION_ERROR;
Expand Down
138 changes: 138 additions & 0 deletions src/ml/moving_average_classifier.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
#include <cmath>
#include <cstdlib>
#include <string>

#include "brainflow_constants.h"
#include "json.hpp"
#include "mindfulness_classifier.h"
#include "moving_average_classifier.h"
#include "restfulness_classifier.h"

using json = nlohmann::json;


MovingAverageClassifier::MovingAverageClassifier (struct BrainFlowModelParams model_params)
: BaseClassifier (model_params)
{
window_len = 5;
sum = 0.0;
base_classifier = NULL;

if (params.metric == (int)BrainFlowMetrics::MINDFULNESS)
{
base_classifier = std::shared_ptr<BaseClassifier> (new MindfulnessClassifier (params));
}
else if (params.metric == (int)BrainFlowMetrics::RESTFULNESS)
{
base_classifier = std::shared_ptr<BaseClassifier> (new RestfulnessClassifier (params));
}
}

MovingAverageClassifier::~MovingAverageClassifier ()
{
buffer.clear ();
sum = 0.0;
base_classifier = NULL;
}

int MovingAverageClassifier::parse_window_len ()
{
int len = 5;
if (!params.other_info.empty ())
{
try
{
if (params.other_info.find ("{") != std::string::npos)
{
json j = json::parse (params.other_info);
if (j.contains ("window_len"))
{
len = j["window_len"].get<int> ();
}
else if (j.contains ("period"))
{
len = j["period"].get<int> ();
}
}
else
{
len = std::stoi (params.other_info);
}
}
catch (...)
{
safe_logger (spdlog::level::warn,
"Unable to parse window_len from other_info: {}. Using default value of 5.",
params.other_info);
len = 5;
}
}
if (len <= 0)
{
len = 5;
}
return len;
}

int MovingAverageClassifier::prepare ()
{
buffer.clear ();
sum = 0.0;
window_len = parse_window_len ();

if (base_classifier != NULL)
{
return base_classifier->prepare ();
}
return (int)BrainFlowExitCodes::STATUS_OK;
}

int MovingAverageClassifier::predict (
double *data, int data_len, double *output, int *output_len)
{
if ((data == NULL) || (output == NULL) || (data_len <= 0))
{
safe_logger (spdlog::level::err, "Incorrect arguments for predict.");
return (int)BrainFlowExitCodes::INVALID_ARGUMENTS_ERROR;
}

double raw_score = 0.0;
if (base_classifier != NULL)
{
double base_output = 0.0;
int base_output_len = 0;
int res = base_classifier->predict (data, data_len, &base_output, &base_output_len);
if (res != (int)BrainFlowExitCodes::STATUS_OK)
{
return res;
}
raw_score = base_output;
}
else
{
raw_score = data[0];
}

buffer.push_back (raw_score);
sum += raw_score;
if ((int)buffer.size () > window_len)
{
sum -= buffer.front ();
buffer.pop_front ();
}

*output = sum / buffer.size ();
*output_len = 1;
return (int)BrainFlowExitCodes::STATUS_OK;
}

int MovingAverageClassifier::release ()
{
buffer.clear ();
sum = 0.0;
if (base_classifier != NULL)
{
return base_classifier->release ();
}
return (int)BrainFlowExitCodes::STATUS_OK;
}
3 changes: 2 additions & 1 deletion src/utils/inc/brainflow_constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,8 @@ enum class BrainFlowClassifiers : int
{
DEFAULT_CLASSIFIER = 0,
DYN_LIB_CLASSIFIER = 1,
ONNX_CLASSIFIER = 2
ONNX_CLASSIFIER = 2,
MOVING_AVERAGE_CLASSIFIER = 3
};

enum class BrainFlowPresets : int
Expand Down
1 change: 1 addition & 0 deletions swift_package/Sources/BrainFlow/BrainFlowEnums.swift
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ public enum BrainFlowClassifiers: Int, CaseIterable, Sendable {
case DEFAULT_CLASSIFIER = 0
case DYN_LIB_CLASSIFIER = 1
case ONNX_CLASSIFIER = 2
case MOVING_AVERAGE_CLASSIFIER = 3

public var code: Int { rawValue }
}
Expand Down