0

I have a stream of data at approx. 500 Hz coming from serial port. The data are sent by a microprocessor which is controlling a machine. The data are filtered out and shown in different text boxes by a class that strips out the header characters used by the transmission protocol. This is the code that assigns the four variables I get to the different text boxes.

private void SetText(string text)
    {
        if (this.txtOutput.InvokeRequired)
        {
            SetTextCallback d = new SetTextCallback(SetText);
            this.BeginInvoke(d, new object[] { text });
        }
        else
        {
            txtOutput.AppendText(text + "\r\n");
            string a = "", b = "", c = "", d="";
            string invia = text.ToString();
            Stripper strp = new Stripper();
            strp.Distri(invia, out a, out b, out c, out d);
            if (a != "")
            {
                textBox7.Text = a; //currentRes
            }
            if (b != "")
            {
                textBox2.Text = b; //temperature
            }

            if (d != "" )
            {
             textBox3.Text = d; //motor current
            }

            if (c == "1\r") //motor RPM
            {
                timer3.Start();
            }
        }
    }

The problem I am facing is that I get hundred of values "a", "b" and "d" per second and obviously the relevant text boxes are flickering. Moreover I would like to average the value for each variable taking 100 samples of each type before showing them in the relevant text box. This would avoid flickering and would give a more accurate reading.

How can I average each of the variable "a", "b" and "d" to x samples without freezing the application which meanwhile has to provide control for other features? Samples code will be really appreciated.

H H
  • 263,252
  • 30
  • 330
  • 514
FeliceM
  • 4,163
  • 9
  • 48
  • 75

1 Answers1

1

average the value for each variable taking 100 samples ... would give a more accurate reading

So where does this number 100 come from, just from the GUI design or is it somehow related to the data?

And do you want to average independent (adjacent) blocks or do you need a moving average?

A simple approach:

class Accum
{
    private int sum, count;

    public event AverageChanged; 

    public void Add(int value)
    {
       sum += value;
       count += 1;
       if (count >= 100)
       {
           OnAvarerageChanged(sum/count);
           sum = 0;
           count = 0;
       }
    }

    ....

}

You will need to create 3 instances of this on your form. Since all SerialPort data will happen on the same thread you'll only need to synchronize when handling the event.

Update:

The easiest but less general approach:

class Accum
{
    private int sum, count;

    //public event AverageChanged; 

    public void Add(int value)
    {
       sum += value;
       count += 1;
       if (count >= 100)
       {
           OnAvarerageChanged(sum/count);
           sum = 0;
           count = 0;
       }
    }

    public Label MyLabel { get; set; }

    private void OnAvarerageChanged(int av)
    {
        SetTextCallback d = new SetTextCallback(SetText);
        string text = av.ToString();
        MyLabel.BeginInvoke(d, new object[] { text });
    }

    private void SetText(string text)
    {
        this.MyLabel.Text = text;
    }

}
H H
  • 263,252
  • 30
  • 330
  • 514
  • Thanks for your suggestion. 100 is just a number, I tough that an average of 100 samples could give a more accurate measure and enough time to stop the text boxes flickering but obviously it can be even a 1000. Averaging independent blocks of x values is fine, I do not need more complex calculation. My concern is that if I make 3 classes working on averaging those values, the application my freeze . May be I am wrong? – FeliceM Jun 16 '13 at 08:14
  • No, if you just call this `Add()` method you will Update only 1/100 times, no freezing. – H H Jun 16 '13 at 08:17
  • Thanks for now. I am going to test now now. – FeliceM Jun 16 '13 at 08:19
  • You can find code for raising the event [here](http://stackoverflow.com/a/2448530/60761) but you can also fudge a little and pass a reference to your window (or label) to the Accum class. – H H Jun 16 '13 at 08:29
  • Henk, thanks. Looks complex to me, I am lost. I am passing the value to the class I named AverageD: AverageD avd = new AverageD(); avd.Add(Convert.ToInt32(d)); but I do not understand how to use the public event. One day I will get there.. Help – FeliceM Jun 16 '13 at 08:38
  • See my last comment above. Easiest is to add a public variable holding the Label to the Average class. Don't write 3 different classes. – H H Jun 16 '13 at 09:02
  • Henk, thanks. It is actually working according to your last suggestion. The problem I am facing now is that the application freeze. 500 data packets per seconds looks too much, I am pretty sure this is the problem because if I introduce a delay on the transmission of the data from the uC, 30 ms, then all is fine. Below 30 ms delay the app is unresponsive. May be I should consider a multithreading to run separately these methods. – FeliceM Jun 16 '13 at 15:23
  • 1
    This class should lighten the load (`/ 100`). Threads won't help. But I see this line: `txtOutput.AppendText(text + "\r\n");`, get rid of that. – H H Jun 16 '13 at 15:47
  • Wow!!! That solved the problem. I have only 1 ms delay for obvious reasons and the app is fully responsive. Amazing! Thanks a lot. May be I should run that line of code on a different thread. – FeliceM Jun 16 '13 at 16:10
  • One other thing to think about is the time to update a textbox. If you use textblocks instead, you decrease the time to update drastically, about a factor of 8. I ran a loop where I updated a textblock vs textbox, and got 1.3 seconds vs 10.x seconds. – user2019047 Jun 17 '13 at 16:54