void TimeBasedCalculation() { // New window time is the original window time minus overlapping window. Set elapsed time in proportional position. elapsedWindowTime = windowTime * overlappingFraction; // Check that data is available if (timestamps.Count == 0 && dataBuffer.Count == 0) { ExciteOMeterManager.LogInFile("No incoming data " + incomingDataType.ToString() + " was found to calculate feature " + outputDataType.ToString()); return; } // Calculate feature featureValue = CalculateFeature(timestamps.ToArray(), dataBuffer.ToArray()); // Send events and log in file ExciteOMeterManager.DebugLog("A new feature was calculated in " + outputDataType.ToString() + ": " + timestamps[timestamps.Count - 1] + ", " + featureValue.ToString()); EoM_Events.Send_OnDataReceived(outputDataType, timestamps[timestamps.Count - 1], featureValue); LoggerController.instance.WriteLine(logIdentifier, timestamps[timestamps.Count - 1] + "," + featureValue.ToString()); // Rearrange overlap in signal // Overlap should not be greater than 95%, because it would generate very often feature calculations that might affect performance. int elementsToDelete = (int)(Mathf.Clamp(1.0f - (overlappingFraction), 0f, 0.95f) * dataBuffer.Count); timestamps.RemoveRange(0, elementsToDelete); dataBuffer.RemoveRange(0, elementsToDelete); }
/// <summary> /// Calculate feature value from a unidimensional sample-based feature /// </summary> void SampleBasedCalculation() { // Check that data is available if (timestamps.Count == 0 && dataBuffer.Count == 0) { // ExciteOMeterManager.DebugLog("No timestamps or data were found to calculate features"); ExciteOMeterManager.LogInFile("No incoming data " + incomingDataType.ToString() + " was found to calculate feature " + outputDataType.ToString()); return; } // Calculate feature featureValue = CalculateFeature(timestamps.ToArray(), dataBuffer.ToArray()); // Calculate offset of timestamp that corresponds to the calculated feature (# displacements to the left in timestamps) // Examples: Assume `sampleBuffer=5` // If `offsetSamplesTimestamp=0`, t for calculated feature is [t-4,t-3,...,t] // If `offsetSamplesTimestamp=3`, t for calculated feature is [t-1,t,t+1,t+2,t+3] indexOffsetForTimestamp = sampleBuffer - offsetSamplesTimestamp - 1; // Send events and log in file ExciteOMeterManager.DebugLog("A new feature was calculated in " + outputDataType.ToString() + ": " + timestamps[indexOffsetForTimestamp] + ", " + featureValue.ToString()); // Flag to know if it is the first calculation of the feature. // If so, the new feature has to match all the timestamps existing before the first timestamp of the feature. if (matchLengthOfInputSignal) { if (isFirstCalculatedFeature) { isFirstCalculatedFeature = false; idxStartRepeating = 0; // No previous data in array, repeat from beginning of input timestamps. } else { // CASE: Match length and buffer already contains data from previous window // Based on overlap and offset, the position where to start repeating timestamps is the following formula. idxStartRepeating = overlappingSamples - offsetSamplesTimestamp; } // Fill the previous timestamps of the input signal with the same value of this feature. for (int i = idxStartRepeating; i <= indexOffsetForTimestamp; i++) { // Write in files to collect data corresponding to EoM_Events.Send_OnDataReceived(outputDataType, timestamps[i], featureValue); LoggerController.instance.WriteLine(logIdentifier, ExciteOMeterManager.ConvertFloatToString(timestamps[i]) + "," + ExciteOMeterManager.ConvertFloatToString(featureValue)); } } else { Debug.LogWarning("Error calculating feature. Matching sampling error: logIdentifier" + logIdentifier.ToString()); //// CASE: DO NOT MATCH LENGTH OF INPUT SIGNAL, BUT USE TIMESTAMP DIFFERENT THAN LAST SAMPLE //EoM_Events.Send_OnDataReceived(outputDataType, timestamps[indexOffsetForTimestamp], featureValue); //LoggerController.instance.WriteLine(logIdentifier, ExciteOMeterManager.ConvertFloatToString(timestamps[indexOffsetForTimestamp]) + "," + ExciteOMeterManager.ConvertFloatToString(featureValue)); } // Rearrange overlap in signal elementsToDelete = sampleBuffer - overlappingSamples; timestamps.RemoveRange(0, elementsToDelete); dataBuffer.RemoveRange(0, elementsToDelete); }
// Start is called before the first frame update void Start() { // PostProcessing flag numInstances++; // Default values, if this needs to be changed per feature reimplementing the following function in the child class isTimeBasedFeature = !SettingsManager.Values.featureSettings.DEFAULT_IS_SAMPLE_BASED; windowTime = SettingsManager.Values.featureSettings.DEFAULT_WINDOW_TIME_SECS; overlappingFraction = SettingsManager.Values.featureSettings.DEFAULT_OVERLAP_FRACTION; isSampleBasedFeature = SettingsManager.Values.featureSettings.DEFAULT_IS_SAMPLE_BASED; sampleBuffer = SettingsManager.Values.featureSettings.DEFAULT_SAMPLE_BUFFER; overlappingSamples = SettingsManager.Values.featureSettings.DEFAULT_OVERLAP_SAMPLES; offsetSamplesTimestamp = SettingsManager.Values.featureSettings.DEFAULT_OFFSET_SAMPLES_TIMESTAMP; // Only for sample-based, whether input and output are forced to have same length by resampling data. matchLengthOfInputSignal = SettingsManager.Values.featureSettings.matchInputOutputLength; // CONDITIONS if (isSampleBasedFeature) { if (overlappingSamples >= sampleBuffer) { // Error, the number of samples to delete ExciteOMeterManager.LogInFile("The samples to delete in buffer feature " + outputDataType.ToString() + " are larger than sampleBufferLength. Check config.json"); overlappingSamples = 0; } else if (offsetSamplesTimestamp > overlappingSamples) { ExciteOMeterManager.LogInFile("The offset timestamp of timestamp in feature " + outputDataType.ToString() + " needs to be lower than overlapSamplesLength. Check config.json"); offsetSamplesTimestamp = 0; } } // If there are some configurations needed for the specific feature SetupStart(); }
////////////// MULTIDIMENSIONAL FEATURES /// <summary> /// Calculate features from a MULTIDIMENSIONAL sample-based feature /// </summary> void SampleBasedCalculationArray() { // Check that data is available if (timestamps.Count == 0 && dataBufferArray.Count == 0) { // ExciteOMeterManager.DebugLog("No timestamps or data were found to calculate features"); ExciteOMeterManager.LogInFile("No incoming data " + incomingDataType.ToString() + " was found to calculate feature " + outputDataType.ToString()); return; } // Calculate feature featureArray = CalculateFeatureArray(timestamps.ToArray(), dataBufferArray.ToArray()); // Calculate offset of timestamp that corresponds to the calculated feature (# displacements to the left in timestamps) // Examples: Assume `sampleBuffer=5` // If `offsetSamplesTimestamp=0`, t for calculated feature is [t-4,t-3,...,t] // If `offsetSamplesTimestamp=3`, t for calculated feature is [t-1,t,t+1,t+2,t+3] indexOffsetForTimestamp = sampleBuffer - offsetSamplesTimestamp - 1; // Send events and log in file ExciteOMeterManager.DebugLog("A new feature was calculated in " + outputDataType.ToString() + ": " + timestamps[indexOffsetForTimestamp] + ", Length: " + featureArray.Length.ToString()); // Flag to know if it is the first calculation of the feature. // If so, the new feature has to match all the timestamps existing before the first timestamp of the feature. if (matchLengthOfInputSignal) { if (isFirstCalculatedFeature) { isFirstCalculatedFeature = false; idxStartRepeating = 0; // No previous data in array, repeat from beginning of input timestamps. } else { // CASE: Match length and buffer already contains data from previous window // Based on overlap and offset, the position where to start repeating timestamps is the following formula. idxStartRepeating = overlappingSamples - offsetSamplesTimestamp; } // Get the DataType for each of the features that are calculated DataType[] featureDataTypes = Constants.SubsetOfFeaturesTransformDataTypes(logIdentifier); // Fill the previous timestamps of the input signal with the same value of this feature. for (int i = idxStartRepeating; i <= indexOffsetForTimestamp; i++) { // Write in files to collect data corresponding to // Create string to save in CSV string featureArrayText = ""; foreach (float v in featureArray) { featureArrayText += "," + ExciteOMeterManager.ConvertFloatToString(v, 4); } bool logIsWriting = LoggerController.instance.WriteLine(logIdentifier, ExciteOMeterManager.ConvertFloatToString(timestamps[i]) + featureArrayText); if (!logIsWriting) { Debug.LogWarning("Error writing movement data. Please setup LoggerController with a file with LogID is" + logIdentifier.ToString()); } //// --------- TODO //// Send an event with the multidimensional data for the receivers taht can handle multidimensionality //EoM_Events.Send_OnDataArrayReceived(outputDataType, timestamps[i], featureArray); //// Visualizer is designed to analyze unidimensional data, therefore multidimensional needs to be sent one by one to the system //StartCoroutine(SendDataEventsMovement(ExciteOMeterManager.GetTimestamp())); // BUG: Sending events from the coroutine does not seem to be received... // --------- // If the above works, delete the remaining code!! ---------------------- if (featureDataTypes.Length != featureArray.Length) { Debug.LogError("Mismatch between the calculated array of features and the expected, in feature with logIdentifier" + logIdentifier); return; } for (int j = 0; j < featureArray.Length; j++) { EoM_Events.Send_OnDataReceived(featureDataTypes[j], timestamps[i], featureArray[j]); } /// -------------------------------------------------------- } } else { // CASE: DO NOT MATCH LENGTH OF INPUT SIGNAL, BUT USE TIMESTAMP DIFFERENT THAN LAST SAMPLE EoM_Events.Send_OnDataReceived(outputDataType, timestamps[indexOffsetForTimestamp], featureValue); LoggerController.instance.WriteLine(logIdentifier, ExciteOMeterManager.ConvertFloatToString(timestamps[indexOffsetForTimestamp]) + "," + ExciteOMeterManager.ConvertFloatToString(featureValue)); } // Rearrange overlap in signal elementsToDelete = sampleBuffer - overlappingSamples; timestamps.RemoveRange(0, elementsToDelete); dataBufferArray.RemoveRange(0, elementsToDelete); }