I am building a balistic chronograph. I have most of it working but getting strange results from the Micros() function. At the beginning of the program and after a few runs, I get 4us resolution. For some reason after it runs for a while I get 64us resolution. I tried setting the prescaler for timer1 but it didn't seem to help. I get 4us resolution for a while and then it jumps to 64us. I've stripped the program down to the essentials, just waits for a high going pulse on two pins, reads the micros() function after each and calculates the FPS between the two pulses. I then send the results up the serial line.
I have now typed this message 5 times and keep hitting the backspace function.
The code below returns 64us resolution (35 x 64 = 2,240us; 34 x 64 = 2,176us, 36 x 64 = 2,304us) after about 5 minutes of running. Prior to that, I get the correct 4us resolution that I would expect. I tried setting the prescaler as in the commented-out code but it didn't help so I guess I didn't know what I am doing. It gives me 4us resolution prior to that for some odd reason. The test gun should be shooting around 450fps or 2,222us so I should be getting 2,224us or 449.64fps.
So I guess the first thing I need is the correct way to set the prescaler consistently and the next, if it will keep going to 64us resolution because of either the lcd routine or serial printing, then a way to reset the timer (timer1, I think) as well as the prescaler.
The strange thing is that it reports 4us resolution for the first few minutes.
Let me throw another thing in there... Is there a more efficient/faster / accurate way to grab the microseconds after a high going pulse? I initially used an interrupt on rising and set a flag but I removed it as overkill. Maybe if I do the interrupt on rising in two attached interrupt routines that are written in assembly grabbing the microseconds would be more accurate? I am going to move to three screens so they will be 1 foot apart and using the average as well as the time from screen 1 to 2 followed by 2 to 3 might give me the ballistic coefficient as well. The time from screen 2 to 3 should be slightly slower.
You seem to be using float variables to hold time values. That means that as the absolute value increases the precision reduces.
Since the return value from micros() is an unsigned long, any attempt to store those values in any different type is liable to cause overflow/precision errors.
The basic approach of calling micros() twice and subtracting the two values to get an elapsed time is correct and should work fine if you just change those floats to unsigned longs.
However, using micros the way you are using it (with @PeterH's suggestion) is very likely a good choice. What is the fastest bullet you expect to measure?
Since delta is a small number I will convert the stop and start time to unsigned long and leave deltas as float. I guess I am still looking for a way to ensure the is set to 4us and reset the timer.
As far as bullets go, I shoot air guns up to about 1400 fps but I need the resolution as low as possible since the speed variance is very critical. We also shoot high power and fast rifles like 7mm mag and I am looking at a 17 remington that goes to 4050fps plus. So in order to get the resolution at that speed we will use the first and third gate which are two feet apart.
Would an interrupt on rising be faster than waiting on high?
..for such high-speed events measurements I would take for example an XC9572XL CPLD breakout board from ebay ($14), add a 100MHz canned oscillator ($1), connect to arduino, and you can measure the events with 10ns resolution and 32bit range (or bigger).. 12.5micrometers bullet's position resolution @ 4500ft/sec.. $15 investment incl postage, and some effort.. just an idea..
p.
cncjerry:
Since delta is a small number I will convert the stop and start time to unsigned long and leave deltas as float.
That makes no sense at all. You don't seem to have taken on board that it is necessary to choose the appropriate data types for your data. This is fundamentally important, and if you leave this to trial and error or guesswork you will run into exactly the sort of precision/overflow problem that has bit you here. These are integer values and need to be stored in an integer big enough to hold the full range of values that this variable could have.
Yah instead of dividing by 1 million, just print the decimal point 6 places to the left.
Since long ago the method used to tell time of flight was to measure bullet drop. They fall at the same speed as any thrown or dropped object when fired horizontal.
When you have that working you might try for a ballistic pendulum. Then you can know bullet speed at range. Check out how old those are!
To make everyone happy, I will change delta_time to unsigned long int though I tested it as float and somewhere float needs to come into play without a lot of gymnastics. I want the numbers on the serial and LCD to be xxx.x though the .x means nothing, it looks good and at low speeds < 550 for instance, it will be accurate.
I don't see how you would be able to divide delta_time by 1,000,000 leaving it all as unsigned integer and since delta will always be under 5000us for valid data, overflow is not applicable.
As far as accuracy goes, for a gun at 4,500 fps, the returned delta time will be one of 2: 4545ps / 220us or 4464fps / 216us which are 1% and .8% respectively of target. This is acceptable at those speeds and represent the high end problem. Most of my concerns are down around 500fps.
As far as the cpld, I was thinking that if I used a 100mhz crystal and a simple counter circuit I could get higher accuracy. I have more than enough pins on the mega I am using and was kicking that around but I need this working already. Also, I don't know if the opto detectors can slew much faster than 4us anyway.
So I never got the prescaler code I wanted. Anyone?
I want the numbers on the serial and LCD to be xxx.x though the .x means nothing, it looks good and at low speeds < 550 for instance, it will be accurate.
What, exactly, does xxx.x represent? If the value is time, the .x will be meaningless at any speed. If it represents a speed, then the distance part of the calculation needs to be a float, but the time part (speed = distance/time) does not. Speed, of course needs to be a float.
You should consider changing the format as the speed increases. You might be able to measure the speed accurately to +/- a tenth of an inch (or foot or meter or mile) at low speeds, but not at higher speeds. At some point, the xxx.x format should change to just xxx.
how do I reset the timer?
This comes up about twice a month on the forum. The general answer is "you don't". How often do you reset your watch? Practically never, right? That's how often you should be trying to reset the Arduino's timers.
cncjerry:
I don't see how you would be able to divide delta_time by 1,000,000 leaving it all as unsigned integer and since delta will always be under 5000us for valid data, overflow is not applicable.
You don't need to divide. Keep your units as microseconds. Just print the decimal point 6 places to the left, as you would with pencil and paper. You might have to fill in a few zeros but so what? Keep your units as microseconds.
This comes up about twice a month on the forum. The general answer is "you don't". How often do you reset your watch? Practically never, right? That's how often you should be trying to reset the Arduino's timers.
Truth about Arduino:
Anyone who ask:
How can I reset mills or timers?
Does not need the true answer.
By the way wife highjacked the TV so I got this code working: