2

I have a sensor (a Quartzonix pressure transducer, actually) that spits out data via the serial port, around 3 times a second. I'd like to set up some code to give me an average reading based on x-amount of samples.

The output looks something like this:

01+   1.502347091823e01
01+   1.501987234092e01
01+   1.50234524524e01
01+   1.502123412341e01
01+   1.502236234523e01
01+   1.50198345e01
01+   1.502346234523e01

.. and will keep going on forever until the com port is closed or the transducer gets another command.

This is what code I have so far, and the code works to show me what the transducer is actually outputting:

Private Sub btnStart_Click(sender As Object, e As EventArgs) Handles btnStart.Click
    Dim a As String
    a = "$01MC" & Chr(13)
    MyComPort.WriteLine(a)

    Do
        Dim Incoming As String = MyComPort.ReadLine()
        Dim incomingtext As String = Incoming.Remove(0, 3)

        If Incoming Is Nothing Then
            Exit Do
        Else
            txtRawData.Text = Incoming
            boxPSIA.Text = Format(Val(incomingtext), "##0.000")

        End If
        Application.DoEvents()
    Loop

End Sub

The "$01MC" is the command the transducer needs to start spitting out the data. I've got some wierd timeout thing happening when i click the start button, but that's another show (maybe a .readtimeout adjustment is needed, not sure).

I have a text box txtReadingsToAvg for input of how many readings to average.. I'm just not wrapping my head around how to actually get it to caluclate the average (on, say, a button click and then spitting it out into a msgbox, or even into another text box).

diashto
  • 37
  • 7
  • 3
    For n readings, how about a queue or shift register n elements long. Each time you get a new reading, push it into the queue and the oldest reading drops off then end. Then average the queue. – peterG Oct 17 '16 at 12:19
  • Do you want an average since your program starts or an average since the sensor has started recording? – Alex B. Oct 17 '16 at 12:21
  • Or the average of the last n seconds / m samples? – djv Oct 17 '16 at 13:14
  • @peterG That would probably work.. but how to code it? – diashto Oct 17 '16 at 13:15
  • What I want is if the txtReadingsto Avg is, say, 10, when the user clicks a button it takes the next 10 readings and averages them.. the raw output is there to get the pressure close enough to get the readings through the program. – diashto Oct 17 '16 at 13:16
  • 2
    If you store that past values in a list, then `avg = Readings.Average()` will give you an average of that current set. For a moving average store the result in a different list or use `.Take()` to calculate it on the fly. Hard to tell what you are after – Ňɏssa Pøngjǣrdenlarp Oct 17 '16 at 13:21

1 Answers1

1

Not sure how your code even works. You said you get values @ about 3 Hz? Then a straight up Do...Loop would be too fast. There is an event which is raised when the serial port receives data. Make use of that.

You will probably need to change this around a little to suit your needs

' WithEvents allows events to be handled with "Handles" keyword
Private WithEvents myComPort As SerialPort
Private dataQueue As Queue(Of Double)
Private numReadingsToAvg As Integer = 0

Private Sub btnStart_Click(sender As Object, e As EventArgs) Handles btnStart.Click
    ' make a new queue here to initialize or clear an old queue
    dataQueue = New Queue(Of Double)()
    ' read the num avgs text box. you may want to change on the fly also
    numReadingsToAvg = Integer.Parse(Me.txtReadingsToAvg.Text)
    myComPort.WriteLine("$01MC" & Chr(13))
End Sub

Private Sub myComPort_DataReceived(sender As Object, e As SerialDataReceivedEventArgs) Handles myComPort.DataReceived
    Dim incomingLine As String = myComPort.ReadLine()
    ' DataReceived event happens on its own thread, not the UI
    ' must invoke call back to UI to change properties of controls
    txtRawData.Invoke(Sub() txtRawData.Text = incomingLine)
    Dim incomingValue As String = incomingLine.Remove(0, 3).Trim()
    If Not String.IsNullOrWhiteSpace(incomingValue) Then
        Exit Sub
    Else
        Dim measurement As Double = Double.Parse(incomingValue)
        boxPSIA.Invoke(Sub() boxPSIA.Text = Format(measurement, "##0.000"))
        dataQueue.Enqueue(measurement)
        ' if there are too many items, remove the last one
        If dataQueue.Count > numReadingsToAvg Then
            dataQueue.Dequeue()
        End If
        Dim average As Double = dataQueue.Average()
        ' you need to add this textbox
        anotherTextBox.Invoke(Sub() anotherTextBox.Text = Format(average, "##0.000"))
    End If
End Sub

By the way, Application.DoEvents() should rarely (never) be used, as there's always a better way to remedy whatever problem you are bandaging with DoEvents. Your original example suffered from clogging up the UI thread with a never-ending loop running on the UI. If you ever need to run a loop like that, it should almost always run on a different thread than the UI thread. In my example, there is no loop, and the timing is determined by the port itself. No need for any of this to happen on the UI thread.

Community
  • 1
  • 1
djv
  • 15,168
  • 7
  • 48
  • 72
  • This doesn't appear to be working.. the boxPSIA and anotherTextBox are not updating. – diashto Oct 17 '16 at 16:55
  • You need to debug it. Put a breakpoint on `Exit Sub` and see what `incomingLine` equals – djv Oct 17 '16 at 17:04
  • well, if i add boxPSIA.Text = Format(Val(incomingValue), "##0.000") above the if/then statement, the boxpsia updates along with the rawdata text (but in the correct format).. – diashto Oct 17 '16 at 17:21
  • Wierdly, if i put a dataqueue.enqueue(1) on the form_load event, things started working fairly well. Tweaked it a bit, and everything worked. Thanks for your help! – diashto Oct 19 '16 at 19:55
  • Great! Well looking at it again, you should set `Private numReadingsToAvg As Integer = 0` to something other than 0. This was probably the issue as `If dataQueue.Count > numReadingsToAvg Then dataQueue.Dequeue()` would try to dequeue when there were no points in the queue. Your workaround fixed this... but you need to change `numReadingsToAvg` to something realistic anyway - it was "x-amount of samples" in your original question. – djv Oct 19 '16 at 20:00