Shop OBEX P1 Docs P2 Docs Learn Events
Avaraging pressure I2C readings — Parallax Forums

Avaraging pressure I2C readings

msiriwardenamsiriwardena Posts: 301
edited 2013-02-11 08:48 in Propeller 1
I am working respiratory system project.What I am trying to accomplish is to read the inhaled air pressure[of arespiratory circuit] and the expiratory pressure of the same circuit.I have connected the I2C - MS4515DO (Specs attached) - I have no problem reading the I2C data with the attaced code via PST.

The readings are -ve during inspiration and +ve during expiration(as it should) - so far I am doing Ok.

The problem I have is I have no idea how to avarage out the +ve readings, separately avaraging the -ve values.for instance a period of 30.sec or 1.0 min.

I am trying to display these results on a LCD display.

Any help how to avage these valus and to capture them in 2 separate variables so I can update the vales every 0.5 min or 1.0 min - as onging vales witout avaraging
is not helpful to manage the respiratory syatus.

Attahed are 1. Code - I have now
2. I2C- MS4515DO Data sheet.
3.The 2 objects - pasm_I2C driver and FullDuplex serial

Thanks,

Siri

Comments

  • MagIO2MagIO2 Posts: 2,243
    edited 2013-02-03 12:20
    You have 2 possibilities.
    Either you store the data you need for calculating the average in a ring-buffer (one ring buffer for -ve and another for +ve). The ring-buffer is needed to subtract the oldest value before adding the newest value to a variable which always contains the sum. This way an average is calculated by 3 steps:
    1. subtract oldest
    2. add newest
    3. divide sum by number of samples
    This is the accurate way.

    Have a look at this: http://forums.parallax.com/showthread.php/107474-Smoothing-out-Data?highlight=running%20average
    This does not need any buffer but it is a bit less accurate.
  • Duane C. JohnsonDuane C. Johnson Posts: 955
    edited 2013-02-03 18:52
    There is another algorithm you might consider:
    Weighted_moving_average#Exponential_moving_average
    Instead of averaging a set of previously measured samples, which all would have the same weight, one recursively gives more
    recent samples more weight than older samples.

    If I can explain it well:
    Take the current value and multiply it by a factor, say 99%, then add the new value multiplied by 101% and divide the result by 2.
    The result is a running average where the oldest sample about 100 ago has little weight compared to the more resent samples.

    If someone has a better explanation of this please help me out.

    This algorithm is quite fast even though there are 2 multiplies per iteration, (the divide by 2 is a right shift).

    Duane J
  • shimniokshimniok Posts: 177
    edited 2013-02-03 22:18
    Averaging is basically a type of filtering. There are several ways to get at that. That part's easy.

    The key though is automatically categorizing the results into two sets of values, +ve and -ve. Right?

    A simplistic approach might find the average of all the values and group results > avg into the +ve category and < avg goes to -ve category. Then average these separately
  • shimniokshimniok Posts: 177
    edited 2013-02-04 19:59
    Ha, synchronicity! I was sure there must be something more out there on this problem. Then one blog I read posted this today:

    http://jeremykun.com/2013/02/04/k-means-clustering-and-birth-rates/
  • MagIO2MagIO2 Posts: 2,243
    edited 2013-02-04 23:03
    @shimniok:
    Maybe it is me who does not understand the problem?!
    So, please explain to me why you come up with clustering? Clustering in this special case is pretty easy to be done with a single if statement:
    ve := I2C.Read(SCL, 1)-31
    if ve<0
      ' add the value to the inspiration average using one of the explained averaging methods (ringbuffer/moving average/exponential moving average)
    else
      ' add the value to the expiration average using one of the explained averaging methods (ringbuffer/moving average/exponential moving average)
    

    No need to work with general purpose clustering, which means that the code has to find the clusters itself. This is what we in germany call "Shooting with canons on pigeons" ;o)
  • shimniokshimniok Posts: 177
    edited 2013-02-05 00:23
    Yup, agree. That's sort of what I posted in #4 above. Simple solution is best.

    Clustering is just a general case of the problem, was interesting to me so posted that url as a curiosity not meant as a solution. Just another case of what's going on in my head not making it to the keyboard. :)
  • msiriwardenamsiriwardena Posts: 301
    edited 2013-02-05 07:18
    Magio et al,

    Thanks for all the help.I will try the ring buffer as soon as I learn to create one ang populate it - the try the avaraging.

    Siri
  • Tracy AllenTracy Allen Posts: 6,664
    edited 2013-02-05 11:23
    Siri,

    You say you want to average over fixed time intervals like 30 or 60 seconds. You accumulate samples and then at the end of the interval simply divide by the number samples taken, and then rezero the accumulators for the next round. To make regular intervals, use the synchronized form of waitcnt().
    [SIZE=1]
    [FONT=courier new]CON
       LOG_INTERVAL_SECONDS = 60
       LOG_INT_MS = LOG_INTERVAL_SECONDS * 1000
       SAMPLE_MS = 50   ' milliseconds between samples
       N_SAMPLES = LOG_INT_MS / SAMPLE_MS 
       ' for example, sample at 50ms intervals for 60 seconds, 1200 samples
    
    PUB main | positives, negatives, time, inValue
      time := cnt   ' set up synchronous sampling at interval
      repeat   ' this will repeat at LOG_INTERVAL_SECONDS
        positives := negatives := 0
        repeat N_SAMPLES    ' N samples in LOG_INTERVAL_SECONDS
          waitcnt(time += SAMPLE_MS)   ' sync sampling interval
          inValue := GetData   ' this is i2c, inval := I2C.Read(SCL, 1)-31
          if inValue => 0
            positives += inValue
          else
            negatives += inValue
        positives /= N_SAMPLES   ' compute averages
        negatives /= N_SAMPLES
        ShowData  ' send your data to terminal[/FONT][/SIZE]
    
  • kwinnkwinn Posts: 8,697
    edited 2013-02-05 17:51
    Do you really want to calculate the average inhaled and exhaled pressures or are you trying to calculate the air volumes based on the pressures?
  • msiriwardenamsiriwardena Posts: 301
    edited 2013-02-07 08:06
    @Kwin - I am trying to calculate the the pressures.
    @ Tracy Allen - Thanks you very much for the code - I got it to work.

    Thank you all trying to help me with this.

    Regards,

    Siri
  • msiriwardenamsiriwardena Posts: 301
    edited 2013-02-08 09:55
    Tracy Allen,

    I thought I had it all worked out but The avarage reading ia always '0'

    I am attaching the code I am using - Please see where I amm going wrong.

    Thanks,

    Siri

    P.S - I changed the Con to -" LOG_INT_MS = LOG_INTERVAL_SECONDS / 1000" which was " LOG_INT_MS=LOG_INTERVAL_SECONDS * 1000"
  • MagIO2MagIO2 Posts: 2,243
    edited 2013-02-08 10:54
    Let's imagine you are a integer CPU! What do you think is the result of this:
      LOG_INTERVAL_SECONDS = 60
      LOG_INT_MS = LOG_INTERVAL_SECONDS / 1000
    

    And the previous definition was correct at least according to the name: 1 second = 1000 milliseconds

    Well .. in the end this
    N_SAMPLES = LOG_INT_MS / SAMPLE_MS
    is 0, which means that the repeat loop will not be executed.
  • msiriwardenamsiriwardena Posts: 301
    edited 2013-02-08 11:04
    @Magio

    I totally understand what you have pointed out but If I go back to what Tracy Allen had - I do not get any responses from the I2C at all.
    I guess there is much more to it but I agree 1 error does not fix the problem with another error crrectly.

    I figured out my mistake - I saw it as miliseconds = seconds /1000 - instead of how many miliseconds in the LOG_INTERVAL_SECONDS - I overlooked the INTERVAL part.
    Thanks @ Magio

    @Tracy - when I comment out the Repeat loop the readings are sent to the PST.
    I hope you will help to remedy this dilema.

    Thanks,
    Siri
  • Tracy AllenTracy Allen Posts: 6,664
    edited 2013-02-08 12:40
    Why did you change
    LOG_INT_MS = LOG_INTERVAL_SECONDS * 1000
    to (big difference!)
    LOG_INT_MS = LOG_INTERVAL_SECONDS / 1000

    1000 milliseconds in one second.






  • thebigmacdthebigmacd Posts: 9
    edited 2013-02-08 12:48
    I'm not 100% sure what you are trying to accomplish by averaging the pressures over 30-60 seconds, but I have an alternative suggestion to your strategy:

    You could simply track the local maximum pressure for each inhalation and exhalation period, and update the corresponding displayed value at the end of each period.

    For example, on startup, display 0 for both +ve and -ve. Then wait for a transition between +ve and -ve and start tracking the local maximum. At the next transition, write out the latest peak to the display, zero the opposite tracking variable, and start tracking that local maximum. Repeat. You might want to set a small threshold for the transition so you don't get false triggers when there are no breaths, or to detect the end of the test.

    The only variables you need in this case are two integers, one to track the -ve and one to track the +ve pressure peaks. By setting timestamps, you could also display the duration of the previous breath.

    If you really want to get fancy you could then take the average of the previous maximums and minimums as well as the breath period lengths, and display the session average.
  • Tracy AllenTracy Allen Posts: 6,664
    edited 2013-02-08 13:04
    Also, there are a couple of important statements you missed, and a bug on my part. There should be another variable:
    sampleCnts := clkfreq / 1000 * SAMPLE_MS
    Then the sychronized waitcnt can be
    waitcnt(time+= sampleCnts)
    I had put SAMPLE_MS there instead of the time in cnt units.

    To use the synchronized method, you have to have the initialization statement,
    time := cnt ' set up synchronous sampling at interval

    Without that, the program could be stuck for a minute aat a time while it waits to sync with cnt.

    You could alternatively change delay in the inner repeat loop into a non-synced waitcnt.
    waitcnt(sampleCnts + cnt)

    Another important action is setting the accumulations to zero before the inner repeat loop:
    positives := negatives := 0

    Thus a separate accumulation for each interval.

    PS--I like the suggestion from thebigmacd
  • msiriwardenamsiriwardena Posts: 301
    edited 2013-02-09 10:39
    @ Tracy Allen - I made all the changes as you suggested but the loop still not execute.I have been playing with loop for a while as soon as I comment out the loop ,the
    readings show up on the PST.The latest code Iam dealing with your amenmends is attached.
    I guess I need more help.

    @ bigmacd - I like your idea to read the peak -ve and +ve pressures but my knowledge in programing require lot more to do what you are suggesting.
    If you can show me how to do that - I will learn more for the future.(I am self a taught - electronic junkie)

    Thanks ,

    Siri
  • Tracy AllenTracy Allen Posts: 6,664
    edited 2013-02-09 15:20
    As you have it now, the i2c statements to read the sensor are split, two outside the repeat loop and two inside. I believe all of it has to be inside the inner repeat loop:
    I2C.Start(SCL)
           I2C.Write(SCL,$51)
            inval := I2C.Read(SCL, 1)-31
           I2C.Stop(SCL) 
    
    Alternatively, encapsulate that as a method and call it from the inner repeat
    loop:
         Repeat N_SAMPLES
             waitcnt(SampleCnts + cnt)   '<-- non-synchronized delay               
             inVal := readPressure   ' calls the method below
             ' ... and so on for average
    
    PRI readPressure : inVal
           I2C.Start(SCL)
           I2C.Write(SCL,$51)
            inval := I2C.Read(SCL, 1)-31
           I2C.Stop(SCL)
    

    I suggest changing the waitcnt to non-sychronized, because the last program still did not have the necessary clock synchronization command. The non-synchronized form is less demanding.
  • msiriwardenamsiriwardena Posts: 301
    edited 2013-02-10 11:24
    @Tracy Allen - Thank you very much for your help.
    I did all the changes as you wanted to but still it would not read the I2C.
    I took out all the CON parameters and replced them with with simple number(500 in the code) and also a if statement on
    the posive readings as it continue to read the negative values to that variable(positives) as I could not figure out why.

    I am attaching the code I have now - If you have time please help figure this issue.
    You can see the CON statements commentd out in the code.

    Thanks again,

    Siri
  • Tracy AllenTracy Allen Posts: 6,664
    edited 2013-02-10 23:03
    Siri, We could go around and around on this for a long time and open up a bug farm!

    I don't have the pressure sensor you are using, but I modified your little program so that it substitutes random number for each pressure reading, and prints them out on the debug screen as it takes them. Then at the end it shows you how many positives and how many negatives it found and also shows you the averages. I set it back to 10 seconds of averaging at 1/2 second between samples taken, so it ends up with a managable 20 samples so you can verify that the math is correct.

    Then you can substitute back in the method that acquires data from your pressure sensor, and change the overall interval back to 60 seconds and the sampling interval to ~100ms.

    Does that help?
  • msiriwardenamsiriwardena Posts: 301
    edited 2013-02-11 08:48
    @Tracy Allen - I got it to work for my satisfaction and readings are repeatable and looks accurate when compared to analog readings of pressure manometer.
    Thank you very much.
    Regards,

    :smile: Siri
Sign In or Register to comment.