-1

I'm fairly new to C# and can't seem to find anything working for me. I need to get data from the serial port which I can collect successfully. Every time I click the button the data gets displayed. This data needs to go in an array, over this array I want the calculate the moving average.

What is the best way to approach this problem?

private void btnRead_Click(object sender, EventArgs e)
{            
    // Filtering incoming data from string to double
    string inComingData = serialPort.ReadLine();
    int charLoc = 0;
    int serialCharLoc = inComingData.IndexOf("N");

    while(!(inComingData.Contains("N")) && !(charLoc == serialCharLoc)) 
    {
        inComingData = serialPort.ReadLine();              
    }
    rtbIncoming.Text = inComingData;
    string [] usableData = inComingData.Split(' ');
    string correctData = usableData[1];
    tbData.Text = correctData;
    serialPort.DiscardInBuffer();

This is the code I have right now to get the filtered data from the serial port.

atiyar
  • 7,762
  • 6
  • 34
  • 75
  • By "moving average" you mean the average "recomputed" to include each new data ? – Arcord Mar 08 '21 at 15:23
  • How do you compute the average temperature over the last 7 days? Every day you add today's temperature to an accumulator and you subtract the temperature from 7 days ago from that same accumulator. The "moving 7-day average" is the accumulator divided by 7. Do you understand this much? (Hint, this also involves some record-keeping of the temperatures over the last 7 days) – Wyck Mar 08 '21 at 15:27
  • Yes i do understand how moving average works. I want to have a fixed amount of data points, when this amount is reached the latest data has to be replaced with the newest. – Zjwamerjong Mar 09 '21 at 08:06

1 Answers1

0

So could keep track of every data you have :

public class MyClass // Don't know the name of your class 
{
   private readonly List<double> _values;
   
   public MyClass() 
   {
      _values = new List<double>();
   }

   // ...

   private void btnRead_Click(object sender, EventArgs e)
{            
    // Filtering incoming data from string to double
    string inComingData = serialPort.ReadLine();
    int charLoc = 0;
    int serialCharLoc = inComingData.IndexOf("N");

    while(!(inComingData.Contains("N")) && !(charLoc == serialCharLoc)) 
    {
        inComingData = serialPort.ReadLine();              
    }
    rtbIncoming.Text = inComingData;
    string [] usableData = inComingData.Split(' ');
    string correctData = usableData[1];
    tbData.Text = correctData;
    
    _values.Add(double.Parse(correctDate) // Only use "double.Parse" if you're sure it will be a double. If not use "double.TryParse".
    var average = _values.Average() // This will give you the average.
    var average2 = _values.TakeLast(x).Average() // This will give you the average for the last x items.
    
    serialPort.DiscardInBuffer();
}

If you have a really high number of element (I assumed it's not the case because it's only when a user click a new element is added) the performance will not be great so you will have to change a bit and do as proposed if the comment to keep an accumulator.

EDIT : TakeLast seems introduced more recently than I thought Here is a custom implementation you could use :

public static class LinqHelper
{
    public static IEnumerable<T> TakeLast<T>(this IEnumerable<T> source, int count)
    {
        if (source == null)
        {
            throw new ArgumentNullException(nameof(source));
        }


        int from = count < 0 ? source.Count() : count > source.Count() ? 0 : source.Count() - count;
        for (int i = from; i < source.Count(); i++)
        {
            yield return source.ElementAt(i);
        }

    }
}

I tried to respect the same behavior as the existing one but the implementation still naive I think so it might not work in some cases.

Arcord
  • 1,724
  • 1
  • 11
  • 16
  • The _values.TakeLast(x) is not working for me. It gives me the error: CS1061; 'List' does not contain a definition for 'TakeLast' and no accessible extension method 'TakeLast' accepting a first argument of type 'List – Zjwamerjong Mar 09 '21 at 08:24
  • What is the error? Is "LINQ" added to the using? – Arcord Mar 09 '21 at 08:25
  • I just tried and I have the .TakeLast method available on a List. Ensure you have this using : using System.Linq; – Arcord Mar 09 '21 at 08:27
  • I'm using system.Linq so this should not be the problem – Zjwamerjong Mar 09 '21 at 08:29
  • 1
    I just learned TakeLast was only available in .NET Core/Standard so if you're in .NET framework it will not work. I provide a naive implementation so you could use it anyway. – Arcord Mar 09 '21 at 13:23