Shop OBEX P1 Docs P2 Docs Learn Events
Recording Geiger counter CPM — Parallax Forums

Recording Geiger counter CPM

Andrew (ARLISS)Andrew (ARLISS) Posts: 213
edited 2012-11-04 12:19 in Propeller 1
I am currently building a data logger using a Parallax Quickstart board. It collects various sensor data and records it to a microSD card periodically.

I have recently acquired a Geiger counter from Sparkfun and would like to record the values as counts per minute. As it stands, the counter outputs a random ASCII byte (0 or 1) each time an event happens. How would I go about monitoring the number of random bytes each minute? This is the first sensor of this kind I have used with Propeller, and my experience in Spin is minimal, so I would appreciate a push in the right direction.

Many thanks,
Andrew

Comments

  • Mark_TMark_T Posts: 1,981
    edited 2012-10-18 13:11
    That page says it has a TTL output pin from tube - presumably 5V logic. Although it appears to be a slowly varying signal requiring a schmitt-trigger to clean it up.
  • Andrew (ARLISS)Andrew (ARLISS) Posts: 213
    edited 2012-10-18 15:32
    Mark_T wrote: »
    That page says it has a TTL output pin from tube - presumably 5V logic. Although it appears to be a slowly varying signal requiring a schmitt-trigger to clean it up.

    Mark,

    From what I've read in application note AN015, creating a Schmitt-trigger on the Quickstart board shouldn't be too difficult. However, it's still the code that I'm having trouble getting a start on. Any pointers?

    Thanks again,
    Andrew
  • LevLev Posts: 182
    edited 2012-10-18 15:41
    Mark,

    From what I've read in application note AN015, creating a Schmitt-trigger on the Quickstart board shouldn't be too difficult. However, it's still the code that I'm having trouble getting a start on. Any pointers?

    Thanks again,
    Andrew

    If the pulse is reasonably clean you can use a counter to detect the high/low transition on the TTL pin. Be sure to include a 3.3K resistor on the pin if the input is 5V.
  • AribaAriba Posts: 2,690
    edited 2012-10-18 19:02
    I would measure the time between the pulses from the Geiger-module, build the average rate and calculate the CPM from that. So you get a much faster update rate than with counting the pulses for a fixed time intervall (1 minute?).

    I tried it and this code came out:
    CON
      _clkmode  = xtal1 + pll16x
      _xinfreq  = 5_000_000
    
      InPin = |< 31  'PropPin connected to GeigerModule TX pin over a 2.2k resistor
    
    VAR
      long cpm
      long stack[10]
    
    OBJ
      term : "FullDuplexSerial"
    
    PUB main
     term.start(31,30,0,115200)    'start Terminal
     cognew(GeigerCounter,@stack)  'start Geiger cog
     repeat
       term.dec(cpm)               'show CPM every half second
       term.tx(13)
       waitcnt(clkfreq/2+cnt)
    
    
    PUB GeigerCounter : filter | time
     time := cnt
     repeat
       waitpne(InPin,InPin,0)           'wait for a pulse
       filter := ((cnt-time)*1 + filter*9) / 10   'measure time and build average
       time := cnt
       cpm := clkfreq*60 / (filter+1)   'calc CPM from average time between pulses 
       waitcnt(clkfreq/1000 + cnt)      'wait a bytetime (9600 baud)
    

    I just use the Serial In pin (31) as pulse input, so I can simulate the Geiger pulses with hitting keys on the terminal. You need to connect the Geiger module TX pin over a resistor to a Propeller pin and set this pin in the CON section.

    Andy
  • kwinnkwinn Posts: 8,697
    edited 2012-10-18 19:38
    Ariba's method is good for low count rates (like background radiation) since it can produce a much faster update rate than counting for a fixed period, but it requires more cpu time for calculations. A fixed counting period reduces or eliminates calculations so it can handle high count rates, but has a low update rate or poor accuracy at low count rates. You can have the best of both methods by combining them.
  • smbakersmbaker Posts: 164
    edited 2012-10-19 16:36
    I have recently acquired a Geiger counter from Sparkfun and would like to record the values as counts per minute.

    How has this been working for you? I tried to build that circuit twice (using my own custom PCB) and could never quite get it to work. I was never convinced that their power supply design produced the correct voltage. There's also some concern in the sparkfun comments about the maximum frequency their detector can detect. I was clumsy and eventually broke my LND tube and gave up on that particular design.

    My latest project uses my own power supply and the detector circuit from this site: https://sites.google.com/site/diygeigercounter/ with a cheap russian SBM-20 geiger tube.

    My current project is here (uses a propeller, nixie tubes, and a russian SBM-20):

    http://www.youtube.com/watch?v=6qo9W1SOV3o&list=UU447n3ekXiEXOYTgOcENiEw
  • RaymanRayman Posts: 14,670
    edited 2012-10-19 16:41
    The Russian tubes are dirt cheap on Ebay... I don't think they have an alpha window like that particular LND tube though...
  • smbakersmbaker Posts: 164
    edited 2012-10-19 16:45
    Ebay is where I got mine from. I think I picked them up for under $20/tube from some former soviet country. The actual tube inside the metal can I believe is quite narrow as it seems very sensitive to how I position the radiation source relative to the tube. You're correct, they don't do alpha.

    The second advantage is the russian tubes are very durable. I was pretty bummed when I broke that ~ $95 LND tube, and said to myself "this is why I can't own nice things" and proceeded to order the cheaper tubes.
  • Andrew (ARLISS)Andrew (ARLISS) Posts: 213
    edited 2012-10-20 08:04
    I just used Ariba's code with the Sparkfun Geiger counter and had mixed results with the output. I connected the TX pin of the counter over a 2.2k resistor, as suggested, to the Propeller. The Geiger counter and the Quickstart board were powered independently. An Americium 241 source was placed close to the counter; here's what I saw in the Parallax Serial Terminal throughout a few minutes:

    http://pastebin.com/Y2cEsstc

    Oddly, during another trial, negative CPM numbers showed up in the terminal, notably after I removed the Americium sample and only background radiation was present:http://pastebin.com/3bivpdU8

    What are your thoughts on this? Is this behavior normal?


  • Duane DegnDuane Degn Posts: 10,588
    edited 2012-10-20 09:17
    I think Ariba's code is counting bits on the serial line instead of the actual output from the Geiger counter.

    The Geiger counter outputs ASCII "1" or ASCII "0". You'll want to read the Geiger counter's output with a serial driver. You should also be able to send the output directly to a PC terminal window with a Prop Plug or equivalent. The Prop Plug might need a current limiting resistor when used with a 5V signal.
  • LevLev Posts: 182
    edited 2012-10-20 11:30
    I believe (but have never actually tested one) that the Sparkfun device also includes a TTL output pin which is a simple filtered and buffered high/low from the Geiger tube. Pulses could easily be counted by a counter module. The rapid updates provided by the waitpeq procedure might not be necessary if you want average counts as your balloon rises through the atmosphere, and longer term averages provided by a Propeller counter (i.e. CTRA) might be acceptable. The rapid updates are more important if human exposure is a factor, which it is not, and this geiger device should not be used for safety monitoring in any case.
  • Duane DegnDuane Degn Posts: 10,588
    edited 2012-10-20 13:03
    If the TX from the Geiger counter is outputing ASCII ones and zeros, then I think this program should work.
    CON
      _clkmode  = xtal1 + pll16x
      _xinfreq  = 5_000_000
      GEIGER_RX = 0  ' Prop Pin connected to Geiger Module'S TX pin over a 2.2k resistor
      GEIGER_TX = 1  ' Prop Pin use to transmit serial, not used but I'm not sure if it's safe
                     ' to use "-1".
      PERIODS_TO_AVG = 30
      BUFFER_SIZE = PERIODS_TO_AVG + 1
      SAMPLE_TIME_MS = 1000
      
    VAR
      long cpm, sampleTime, avgCpm
      long stack[30]
      long counts[BUFFER_SIZE], countsTotal, averagingTime
      byte head, previousHead, tail, newSampleFlag
      
    OBJ
      term[2] : "FullDuplexSerial"
    PUB main 
      sampleTime := clkfreq * (SAMPLE_TIME_MS / 1000)
      averagingTime := (PERIODS_TO_AVG * SAMPLE_TIME_MS) / 1000
      
      term[0].start(31,30,0,115200)    'start Terminal
      cognew(GeigerCounter,@stack)  'start Geiger cog
      repeat
        if newSampleFlag == 1
          newSampleFlag := 0
          term[0].str(string(13, "Samples this period = "))
          term[0].dec(counts[previousHead])
          term[0].str(string(", rate = "))
          term[0].dec(counts[previousHead] * (60_000 / SAMPLE_TIME_MS))
          term[0].str(string(" cpm, total of last "))
          term[0].dec(averagingTime)               
          term[0].str(string(" seconds = "))
          term[0].dec(countsTotal)               
          term[0].str(string(" counts, average of last "))
          term[0].dec(averagingTime)               
          term[0].str(string(" seconds = "))
          term[0].dec((countsTotal * 60) / averagingTime)               
          term[0].str(string("cpm"))
            
    PUB GeigerCounter | previousTime, localCharacter
      term[1].start(GEIGER_RX, GEIGER_TX, 0, 9600)
      head := 0
      tail := 1
      previousTime := cnt
      repeat
        localCharacter := term[1].rxcheck
        if localCharacter == "1"
          counts[head]++
        if cnt - previousTime > sampleTime
          previousHead := head
          countsTotal -= counts[tail]
          countsTotal += counts[head]
          
          newSampleFlag := 1
          previousTime += sampleTime
          if ++head => BUFFER_SIZE
            head := 0
          if ++tail => BUFFER_SIZE
            tail := 0
          counts[head] := 0    
    


    I haven't tested it with a separate serial line. For testing I used the following modifications.
    PUB GeigerCounter | previousTime, localCharacter
      [B]' term[1].start(GEIGER_RX, GEIGER_TX, 0, 9600)[/B]
      head := 0
      tail := 1
      previousTime := cnt
      repeat
        localCharacter :=[B] term[0].rxcheck
    [/B]
    


    Here's some example output.
    Samples this period = 1, rate = 60 cpm, total of last 30 seconds = 30 counts, average of last 30 seconds = 60cpm
    Samples this period = 1, rate = 60 cpm, total of last 30 seconds = 30 counts, average of last 30 seconds = 60cpm
    Samples this period = 1, rate = 60 cpm, total of last 30 seconds = 30 counts, average of last 30 seconds = 60cpm
    Samples this period = 1, rate = 60 cpm, total of last 30 seconds = 30 counts, average of last 30 seconds = 60cpm
    Samples this period = 2, rate = 120 cpm, total of last 30 seconds = 31 counts, average of last 30 seconds = 62cpm
    Samples this period = 1, rate = 60 cpm, total of last 30 seconds = 31 counts, average of last 30 seconds = 62cpm
    Samples this period = 1, rate = 60 cpm, total of last 30 seconds = 31 counts, average of last 30 seconds = 62cpm
    Samples this period = 6, rate = 360 cpm, total of last 30 seconds = 36 counts, average of last 30 seconds = 72cpm
    Samples this period = 6, rate = 360 cpm, total of last 30 seconds = 41 counts, average of last 30 seconds = 82cpm
    Samples this period = 0, rate = 0 cpm, total of last 30 seconds = 40 counts, average of last 30 seconds = 80cpm
    Samples this period = 0, rate = 0 cpm, total of last 30 seconds = 39 counts, average of last 30 seconds = 78cpm
    
  • Duane DegnDuane Degn Posts: 10,588
    edited 2012-10-20 13:05
    I'm adding another post instead of editing my earlier one. I sometimes have problems with code indentation if I edit posts with code.

    The modified "test" version of the code has the second FDS start commented out.

    I kept typing "1" into the serial terminal to test it.

    The program in the previous post uses a circular buffer to compute the average count rate.

    I realized after I posted the above the output is missing a few words.

    "total of last 30 seconds = 30 counts, average of last 30 seconds = "

    should be:

    "total count of last 30 seconds = 30 counts, average rate of last 30 seconds = "

    The constants "SAMPLE_TIME_MS" and "PERIODS_TO_AVG" can be changed to change each sampling period (in milliseconds) and the averaging time.

    The average will be incorrect until a full set of samples has been taken (30 seconds in the above example).
  • AribaAriba Posts: 2,690
    edited 2012-10-20 16:03
    I just used Ariba's code with the Sparkfun Geiger counter and had mixed results with the output. I connected the TX pin of the counter over a 2.2k resistor, as suggested, to the Propeller. The Geiger counter and the Quickstart board were powered independently. An Americium 241 source was placed close to the counter; here's what I saw in the Parallax Serial Terminal throughout a few minutes:

    http://pastebin.com/Y2cEsstc

    Oddly, during another trial, negative CPM numbers showed up in the terminal, notably after I removed the Americium sample and only background radiation was present:http://pastebin.com/3bivpdU8

    What are your thoughts on this? Is this behavior normal?



    Sorry the clkfreq*60 produced an overflow in the calculation of the CPM. Here is an improved code:
    CON
      _clkmode  = xtal1 + pll16x
      _xinfreq  = 5_000_000
    
      InPin = |< 31  'PropPin connected to GeigerModule TX pin over a 2.2k resistor
    
    VAR
      long cpm
      long stack[10]
    
    OBJ
      term : "FullDuplexSerial"
    
    PUB main
     term.start(31,30,0,115200)    'start Terminal
     cognew(GeigerCounter,@stack)  'start Geiger cog
     repeat
       term.dec(cpm)               'show CPM every half second
       term.tx(13)
       waitcnt(clkfreq/2+cnt)
    
    
    PUB GeigerCounter : filter | time, tmp
     time := cnt
     repeat
       waitpne(InPin,InPin,0)           'wait for a pulse
       tmp := cnt-time
       if tmp > 0
         filter := (tmp + filter*9) / 10   'measure time and build average
       time := cnt
       cpm := clkfreq / (filter / 60)   'calc CPM from average time between pulses 
       waitcnt(clkfreq/1070 + cnt)      'wait 9 bittimes (@9600 baud)
       waitpeq(InPin,InPin,0)           'wait for idle
    
    The previous code produced negative numbers when the time between two events was more than 27 seconds. That's an effect of the 32bit resolution of the CNT register. This new code tests for that and produce no negative numbers, but the value will be too low for some time once the measured time was longer than 27 seconds.

    @Duane
    My code detects the startbit of a serial byte, and waits then until the whole byte was transfered, then it waits for the next startbit. So you can spare the serial object. The transfered value contains anyway no useful information, its only a trigger.

    Andy
  • Duane DegnDuane Degn Posts: 10,588
    edited 2012-10-20 18:40
    Ariba wrote: »
    The transfered value contains anyway no useful information, its only a trigger.

    Okay, I think I'm starting to understand. I thought an "1" indicated an event to count and a "0" was just to let the uC know the device was working but didn't detect an event. After reading the description again, I think the ones and zeros are to aid in generating random numbers?

    Thanks for clarifying.
  • Clock LoopClock Loop Posts: 2,069
    edited 2012-10-20 19:03
    Last I checked it output 1's and 0's at different time intervals. Each transmission, means trip threshold was reached. i.e. actual event in the tube in real-time
    I cannot recall but I think thats what I noticed when I viewed the serial output on a pc termnial.

    Its as simple as counting the time between transmissions to determine cpm.
    The sparkfun programming has an upper limit of 40 cpm.

    As per the sparkfun description.
    Each bit generated (actually an ASCII byte, 0 or 1) represents an actual event in the tube in real-time, so the output can be used to deduce CPM or what ever units you need. Here at SparkFun, on average, we get about 25 counts a minute.

    The sparkfun chip is 5v, so be sure to use a resistor on the connection to the prop. (i use a calculated resistor divider)

    I also would not bother with bypassing the logic on the sparkfun board, (attaching the prop to the tube ttl output), I would just read the serial output timing interval.
    The more you mess with the tube the higher chances are that you will zap something. (that tube is 500v)
  • Andrew (ARLISS)Andrew (ARLISS) Posts: 213
    edited 2012-10-24 06:02
    Duane Degn wrote: »
    Okay, I think I'm starting to understand. I thought an "1" indicated an event to count and a "0" was just to let the uC know the device was working but didn't detect an event. After reading the description again, I think the ones and zeros are to aid in generating random numbers?

    Thanks for clarifying.

    Duane,

    I successfully implemented your code in my program and it has been working okay, but is it only counting the ones? My understanding is that either a zero or a one is an event in the Geiger tube, and my original tests with only the Geiger counter connected to my laptop by USB seemed to confirm that. Otherwise, I haven't knowingly encountered any issues with the code you provided.

    Thanks again,

    Andrew
  • Duane DegnDuane Degn Posts: 10,588
    edited 2012-10-24 09:49
    Duane,

    I successfully implemented your code in my program and it has been working okay, but is it only counting the ones? My understanding is that either a zero or a one is an event in the Geiger tube, and my original tests with only the Geiger counter connected to my laptop by USB seemed to confirm that. Otherwise, I haven't knowingly encountered any issues with the code you provided.

    Thanks again,

    Andrew

    Andrew,

    Yes, the code I posted only counts ones. I missunderstood how the Geiger counter worked.

    I think my code could be fixed by changing the line:
    if localCharacter == "1"
    

    to:
    if localCharacter == "1" or localCharacter == "0"
    

    While several constants in the program may be changed, you need to make sure the constant "SAMPLE_TIME_MS" is large enough for each of the two cogs in the object to complete their respective loops. The lower limit of "SAMPLE_TIME_MS" is dependant on the baud of the debug line and the number of characters sent through the debug line. It's also dependant on the time it takes to read a character from the Geirger counter.

    If you changed the output from:
    Samples this period = 1, rate = 60 cpm, total of last 30 seconds = 30 counts, average of last 30 seconds = 60cpm
    

    to something like:
    s=1,r=60,t30s=30c,ave30s=60cpm
    

    should allow you to use a smaller value for "SAMPLE_TIME_MS".

    It might be interesting to set "SAMPLE_TIME_MS" equal to 500 and "PERIODS_TO_AVG" equal to 120. This combination should give you a running average over a full minute with a 2Hz update frequency.

    The running average isn't accurate until a full set of samples (120 in the above example) have been collected.
  • RaymanRayman Posts: 14,670
    edited 2012-10-24 15:38
    BTW: I think the Propeller could make a way cooler Geiger counter than the Sparkfun one. If it was with the Russian tubes and made it a Quickstart plugin board, I bet it could be really, really low cost too... Exposed high voltage is a little bit of a concern for me though...
  • Andrew (ARLISS)Andrew (ARLISS) Posts: 213
    edited 2012-11-03 13:15
    Duane,

    Your code has been working excellently with the Sparkfun Geiger counter. The program that is being used in my project records several other variables to a MicroSD card such as altitude, temperature, and humidity. I have added your code into the program but unfortunately have not had as much success as when it was running by itself.

    I have included your Geiger counter code program which works on its own, and the latest version of my program. The values being recorded to the SD card, no matter how high of a count rate shows up as zero. I tried adding Parallax Serial Terminal statements to figure out where the issue resides but have not had any success in doing so. I'm sure I am overlooking something simple, but have not been able to tackle it in my attempts the past few days.

    Your ideas and suggestions are appreciated!

    Andrew
  • Duane DegnDuane Degn Posts: 10,588
    edited 2012-11-03 17:07
    Andrew,

    I don't see what's causing the problem myself but I did find some code (mine) that bothers me.

    The code:
    sampleTime := clkfreq * (SAMPLE_TIME_MS / 1000)                    ' Geiger counter
    

    Will be a problem if the constant "SAMPLE_TIME" isn't a multiple of 1000.

    You probably better off using:
    sampleTime := (clkfreq / 1000) * SAMPLE_TIME_MS                    ' Geiger counter
    

    I don't see this line in the version that doesn't work.
    if localCharacter == "1" or localCharacter == "0" ' added or = 0 statement
    

    The code only checks for ones like my original flawed code instead of checking for both ones and zeros as it should.

    This shouldn't be the cause of your program only writing zero though. I can't find what the main problem is.
  • Andrew (ARLISS)Andrew (ARLISS) Posts: 213
    edited 2012-11-03 18:26
    Duane,

    Thank you for the response. I have made the changes you advised, but as you suspected, they were not the cause of only zeroes being written to the file. Could it be another routine in the program preventing the Geiger counter code from working properly? I've looked over the code many times now and I haven't been able to find the culprit.

    If others wish to chime in, I'd love to hear your thoughts. My limited experience has me at a dead end on this one.

    Andrew
  • Duane DegnDuane Degn Posts: 10,588
    edited 2012-11-03 20:39
    Andrew,

    I think you've run out of cogs. I'm betting you have more serial objects than you need. I think this is another case where a four port object would help.
  • Andrew (ARLISS)Andrew (ARLISS) Posts: 213
    edited 2012-11-04 06:02
    Duane Degn wrote: »
    Andrew,

    I think you've run out of cogs. I'm betting you have more serial objects than you need. I think this is another case where a four port object would help.

    That would seem to be the issue! I disabled the GPS_SmartMode object which uses its own instance of FullDuplexSerial. The Geiger counter readings now seem to be writing to the MicroSD card successfully. I will take a look at a four-port serial object as you suggested. I am currently looking at Tracy Allen's fullDuplexSerial4port; I will take a stab at implementing it later.

    Thanks again for your help!

    Andrew
  • Duane DegnDuane Degn Posts: 10,588
    edited 2012-11-04 12:19
    I am currently looking at Tracy Allen's fullDuplexSerial4port; I will take a stab at implementing it later.

    That's the one I'd suggest.

    Your welcome.
Sign In or Register to comment.