2004 NARAM R&D Project

This page documents my hardware and software development effort for a Kalman filter based altimeter. This culminated in another R&D report that was good enough for first place in C division at NARAM 46.

The latest version of the code is at the bottom of this page.

5 Oct. 2002

Robert Dehate sent me some of his Roctronics boards. Most of the parts needed to build an altimeter have arrived from various locations and I am ready to start building. (Late note: I used the version 3 PIC controller board along with the sensor and harness boards.)

The basic code for this hardware is complete for dual deployment. I still need to work on some extra features to make it easier to debug. Mainly storing and reading data from the 32KB EEPROM. But the main code is about as ready as I can get it without running it on the actual hardware. I have used the Microchip MPSIM simulator to check out most of the code including the Kalman filter and engineering unit conversion code. I found a few minor bugs.

Code size is now 1K and climbing. The code to store data in the EEPROM and communicate with a PC is likely to add several hundred more words of code. Total available on the 16F628 is 2K.

As soon as the code stabilizes I will post it here. If you would like to see it before then, drop me an E-mail.

Keep in mind that my purpose in writing this code is not to produce and sell a commercial product. It is to demonstrate a working altimeter using common hardware and a Kalman filter. Hopefully this will convince the various altimeter vendors that they can do it too.

1 Nov. 2002

The altimeter is assembled. Powered up the first time and immediately found a bug (several actually). The continuity beep code was working but it was indicating a good drogue and main even though nothing was connected. That was a simple coding error but there were other problems. It seems that PORTA on the 16F628 underwent several changes relative to the 16F84. The first is that it is now a Schmitt trigger input with different logic levels. This required a change in resistor values. The second was that PORTA powers up configured for the new analog comparators which, alas, can't be used for the continuity check without wasting two pins.

In any case it is now beeping nicely. Now I need to write some code to verify operation of the ADC and sensors.

Aug. 2003

I got sidetracked and didn't work on the code for a long time. Now that my level 3 effort is completed, I have started working on it again. Starting up again was difficult because of the long hiatus. But after re-learning a few things, I have made some progress. I checked out the sensor system and ADC which are now working well. I used the altitude beeping code to check this out. I then needed to see into the operation of the filter which meant that I needed to get the serial communication code working and serial EEPROM data storage as well. After a while, I got that all working. And amazingly, the Kalman filter code looks to be working fine. The first time a downloaded good data from the EEPROM, I was able to see the simulated flight progress from pre-launch, to in-flight, to apogee, waiting for main parachute, and main parachute deployment. I was shocked and amazed that it worked so well.

The code still needs some more tweaks here and there but I am planning a flight test (data only) this Labor Day weekend at the Dallas Area Rocket Society NTHP launch.

Here is an example of the data I collected while bench testing the altimeter. I do this by holding the altimeter inverted for a while and then flipping it upright. This simulates the acceleration of a launch. But it does not simulate the pressure changes during flight so the software doesn't quite behave like you would expect for a real flight.

Plot of data from a bench test

You might notice that the estimated velocity is still positive when the estimated altitude starts decreasing. This is because the estimates are updated by both the acceleration and pressure measurements. It just happens that the acceleration has higher gains than the altitude because of the relative noise levels. Once the forcing function of the acceleration measurement dies away, the large difference between the measured and estimated altitude makes itself felt.

The data plotted is only the integer portion of the state information in the altimeter. The 24LC256 EEPROM is pretty small so I had to draw the line somewhere.

21 Oct 2003

I finally managed to flight test the altimeter this past weekend. The flight test vehicle was my trusty Aerotech Initiator with an added payload bay. The motor of choice was a G64-7.

While the seven second delay resulted in ejection a bit before apogee, that was OK for this first flight. I noticed when I recovered the rocket that the altimeter was beeping out an altitude of 115 meters which was quite a bit lower than I expected and also very wrong. I hoped that this was caused by ejection prior to apogee but that proved to be a forlorn hope.

So the next step was to download the data and have a look. This first plot shows the raw pressure sensor data (roughly mangled to look more like altitude in the plot and shown in blue) and the Kalman filtered altitude.

Data from first flight

This is pretty disappointing as there are a few very obvious and very bad problems here. The most noticeable problem is the oscillation in altitude that appears at 4.6 seconds. The other problems are that the altitude increases much too rapidly and also peaks out much too soon.

The oscillation turns out to not be a problem at all. It results from the code dropping the acceleration measurement at apogee (The code uses velocity to determine apogee. See next plot.) and changing gains. There is a large difference in between the measured and estimated altitudes which results in a large correction factor. This causes the oscillation as the filter starts to converge on the correct output. Or it would converge if not for the other problem.

Velocity and acceleration data from first flight

I originally thought that the too rapid increase in altitude and early peak might be the result of a problem with the Kalman gains. Looking through the code and the gain values I could find no problem. But when I looked up the page I found a very serious problem.

When I started writing this code (something over a year ago) I apparently assumed that I would be sampling data 64 times per second. I changed my mind at some point to use 128 samples per second but forgot one tiny little detail.

The first part of the Kalman filter code predicts the altitude, velocity, and acceleration based on previous values. This requires dividing the velocity and acceleration by the sample rate (or sample rate squared). Changing the sample rate without changing these divisions causes big trouble. And this would cause exactly the problem that is shown in the recorded data.

Fortunately this is quickly and easily fixed by changing a few shift counts.

The dynamic model assumes that acceleration is constant so the bug did not effect the acceleration data. Or not very much anyway. The large difference in measured and filtered altitude causes a slight correction to be factored in but the gain is small so it doesn't have much effect. So here is a close look at the acceleration data.

Closeup plot of acceleration from motor thrust

I am pretty sure the slight blip in the data before first motion was caused by the red nozzle cap being blown off when the copperhead lit. I think that I forgot to cut a vent hole in it.

I have also decided to work on the data storage code.

The limited storage space in the 32KB EEPROM requires some compromises. For the first version I chose to store 8 bytes every 4 samples (or 32 times per second). This would allow enough samples to be stored to cover the entire flight. But I could only store filtered altitude, velocity, and acceleration along with the raw pressure data. That was it.

While trying to figure out this problem, I was wishing that I had some more information, primarily the raw acceleration reading. So I am going to recode the storage routines to store 16 bytes per sample. I am also going to increase the rate at which samples are stored to 64 times per second plus include all flight events. Once apogee is detected the storage rate will be lowered so the rest of the flight can be stored.

Once I complete the coding required for that I will post the updated code package here.

6 Nov. 2003

As part of the changes to the data recording code, I needed to include some extra information so I could determine the time between samples. I simply recorded one byte of the counter that I increment with each timer interrupt.

When I first looked at this data it was a mess. Sometimes there was one tick between samples and sometimes two. This was not a good sign as the most likely cause was that the code was not finishing before the next timer interrupt. I backed the timer down to 1/64 second and sure enough, my code was taking too long. A bit over 11 ms when 1/128 second is 7.8ms. I am not real sure how I messed this up.

So I have changed everything (Kalman gains, interrupt, etc.) to run at 64SPS. The new data storage code is in place as well. I now store the following:

Description Size (bits) Format
Altitude 24 16.8 Fixed Point
Velocity 24 16.8 Fixed Point
Acceleration 24 16.8 Fixed Point
Pressure 12 Raw ADC counts
Acceleration 16 Raw ADC counts
State 4 integer
time 8 LSB of interrupt counter
Spare 16 integer

Data is stored at 64 SPS for the first 16 seconds (including 2 seconds of prelaunch detect data) and then at 2 SPS for 512 seconds. In addition, detection of a flight event (apogee, main deployment, etc.) will also force data to be recorded. So even if these events happen while data is being recorded at 2 SPS, the exact time of the event will still get into the data. Because of the non-uniform sample rate of the recorded data, I needed the extra time information.

So I suppose it is a good thing that I changed the data storage code. I might not have ever figured out why the filter wasn't performing as expected.

The Kalman filter can actually deal with non-uniform sample rates. The problem is that it greatly increases the amount of computation because the gains are no longer constant and must be computed in real time. Take a look at the code that computes the Kalman gains (kgain32.c) and you will see that this simply is not a realistic task for a PIC controller.

20 Dec. 2003

Finally! A launch that isn't either blown away or washed out. Another flight test using the stretch Initiator on a G64-7. Visually the flight was about the same as the first. When I reached the rocket it was beeping out an altitude of 178 meters, which was much lower than expected. Since the rocket went much higher than that, I was disappointed and expected the worst when I looked at the data.

First up is a plot of altitude, velocity, and acceleration for the entire flight.

Data from flight 2

Something that jumps out immediately is that something odd happened about 58 seconds into the flight. Everything just starts oscillating wildly.

Here is a closer look. I thought that perhaps this was caused somehow by the code for the main event so I plotted the altimeter state information along with altitude.

But the main event happened several seconds prior. I would have sworn that I set the main altitude to 128 meters but it happened around 150 meters. Oops, I forgot that this is absolute altitude. Prelaunch was about 19 meters. Plus 128 is 147 and that is just right.

Here is a comparison of the raw sensor data and the filtered data. I have removed the offset from the raw data but it isn't scaled. First up is the pressure data.

Next is the acceleration data.

Nothing bizarre happened in the raw sensor data at 58 seconds so the problem must lie in the code somewhere. I get the feeling that this will be difficult to locate.

Zooming in on the acceleration data during the motor burn, it looks like the filter parameters might be set a bit too aggressive. I think I will back off a bit because the acceleration is getting smoothed just a bit too well.

Here is a closer look at the data up till apogee.

And then just the few seconds around apogee.

Here is the raw pressure data for comparison.

It is hard to tell because the motor ejection hit a little bit prior to apogee, but I think that the filter would have been early. It also looks like the acceleration data was not converging on 9.8 m/s/s like it should have. This is something that I will have to look at after I have data from a deployment flight. It could be that the sensitivity of the ADXL150 varies enough with temperature to cause problems. I sure hope not.

It looks like I have two problems to track down. One major and one minor: the craziness at 58 seconds and the altitude reporting code. I think I am OK to fly a deployment flight even without locating the first problem but I would like to take care of it first.

Something to keep me busy for a while. :-)

5 Jan. 2004

I have located and fixed a couple of bugs. The first fixed a confusion between movf and movwf instructions that hosed the upper byte of the AGL altitude resulting in the wrong value being beeped out. The other problem was actually in a supporting program (pdata.c) that generates the 16 entry look up table for pressure to altitude conversion. The problem involved the extraction of the fractional part of the slope and resulted in bogus slopes. I am pretty sure that this caused the odd behavior at 58 seconds in the second flight.

With these two bugs fixed, all is go for the next flight test with deployment. In addition to the bug fixes, I made one other change to the code. I am recording the battery voltage instead of the timer value now. This should provide some interesting data when the ejection charge is fired.

9 May 2004

After a long streak of getting rained out, I finally managed to flight test the software. The test vehicle was the stretched Aerotech Initiator again but flying this time on a F39. I used the F for a couple of reasons. One being that this would keep all of the action at apogee low enough so that it could be easily seen. The other being that I do not have a G64 with a long enough delay to allow the altimeter a chance at deploying the parachute at apogee.

The flight was visually perfect with deployment precisely at apogee and the altimeter was beeping out an altitude of 87 meters.

So then it was time to download the flight data and take a look.

Everything looks great right up until apogee when a huge spike occurs. This is the first time I have connected anything to the outputs of the altimeter so that is my primary suspect for this problem. I connected an Oxral E-match to the output in series with a 2 ohm resistor to limit current. I expected that the battery voltage would droop some. Here is a look at the data just till apogee.

A quick check of the raw sensor readings show that both were hit with large spikes exactly at the same time as the output was turned on.

The pressure reading took about a 100 count hit but the accelerometer spike was around 1000 counts (33 G equivalent) so It is a good thing that at apogee the Kalman gains are changed so that the acceleration measurement is ignored. Otherwise the spike in the data would have been much worse.

Since I recorded battery voltage on this flight, I looked at it next.

The battery (Ultralife lithium) voltage dropped about half a volt but that is nowhere near enough to cause the voltage regulator to drop out of regulation. (although this data could be in error because the ADC uses the 5 volt power rail as its voltage reference for conversions) I suspect that something else besides a drop in battery voltage is causing the spikes in sensor readings. The most likely cause being the circuitous route that the ejection output currents must travel in the Roctronics hardware.

You might also notice that there is a very slight delay between the battery voltage dropout and the spikes in the data. This is due to the time when the measurements are made. The sensors are checked at the very beginning of each computation cycle, a decision to fire the outputs is made, and then the battery voltage is measured. So the battery voltage will lead by one sample period.

You can look at the flight data yourself. It is in a plain text file suitable for use with GNUplot.

In order to track down the source of the problem, I performed some bench flights using three different configurations: Single battery with positive lead connected to battery two terminal. Single battery with both positive and negative leads connected to battery two terminals. Two batteries. All three configurations had the apogee pyro output shorted so the only load was the two ohm resistor.

Only the two battery configuration had no spikes in the sensor data. The other two configurations had the battery voltage dip to almost 5 volts while the pyro output was on. I was quite surprised that this did not lead to the micro-controller resetting or any other weirdness. The magnitude of the spike in the acceleration data in the bench tests was much lower than during flight. So most of the acceleration spike in the flight data resulted from the ejection charge going off.

I will rewire the harness board to use capacitive discharge in the future.

The primary conclusion for this flight test is that all known bugs in the Kalman filter code have been resolved and the filter is performing exactly as expected.

While the raw sensor data is very useful, I think I am going to modify the data processing program to convert these into engineering units as well. This will make it a bit easier to compare them to the Kalman filter data.

16 May 2004

Although some areas of Texas received a lot of rain (including reports of up to 17 inches near Hearne) Thursday, we lucked out and the Rockwall field had only a few tenths at most. So amazingly, DARS held another LMR launch this weekend. This will likely be the last launch on this field for quite a while as the weeds are taking over.

While I would have preferred to use a G64-10 for this flight I didn't have one. I did have an old G80FWL-10 sitting around so that is what I used. The downside for using this motor is that deployment happens at a much higher altitude so it is more difficult to visually judge how close to apogee it is. This was complicated by the light winds which resulted in a very vertical flight. If I had been standing back a hundred yards or so instead of at the launcher, I could have seen the apogee event a bit better. But in any case it looked perfect.

The altimeter was beeping out a max AGL altitude of 341 meters when I recovered the rocket about 30 feet from the launcher.

I made a few changes in the altimeter from the previous flight. 1) Capacitive discharge for the pyro outputs. 2) Change sensor board R6 to increase random noise in the pressure sensor reading. 3) Change Kalman gains so that acceleration more closely tracks the measured acceleration.

Here is a plot of the Kalman state information till just past apogee. Apogee was at 10.218 seconds.

Deployment occurred when commanded so the capacitive discharge is working.

I am not sure why but the noise in the pressure data was not as high as I expected. This might be due to my stacking two 5.1K surface mount resistors to get 2.5K. I will locate a 2.5K surface mount part and install it to see if that helps.

So how did the acceleration do?

As you can see the Kalman filtered acceleration tracks the measured acceleration very closely. This is good and bad. It is good because the velocity estimate is heavily driven by acceleration so the velocity should fairly closely track the actual velocity. This greatly helps when it comes time to ignore the pressure data if the vehicle get near Mach 1.

It is bad in that if the motor has a high level of vibration (like some hybrids) it might effect the quality of the state estimates. No way to know if this will be unacceptably bad until I can do a flight test with a Hypertek hybrid.

I changed my flight data processing software to convert the raw pressure and acceleration measurements into engineering units. I also added code to compute inertial velocity and altitude (using only the acceleration measurement). So now it is easier to compare how the Kalman code did in comparison to how an accelerometer based altimeter might work.

The velocities track pretty closely until motor burnout but then begin to diverge. The early close tracking is the result of my changing the Kalman gains. The divergence is a result of the pressure measurement corrections. Without them the various errors in the acceleration measurement accumulate. The inertial velocity was a bit over 5 m/s at apogee so it would have resulted in deployment a half second past apogee on this flight.

I thought it would be interesting to compare the three versions of altitude I now have in one plot.

And the same thing zoomed in near apogee to highlight the differences.

The difference between the Kalman and pressure altitude is due to the altimeter using a piecewise linear interpolation method to compute altitude and the post processing program using the more accurate exponential form.

The pressure altitude actually goes up slightly after deployment. This is not because the altimeter deployed early but because the ejection charge pushed the payload section with the altimeter a little bit higher.

One problem I had on the previous flight was a total collapse of battery voltage when the ejection charge was fired. I switched to using a capacitive discharge system on this flight in order to prevent that. So how did the battery voltage do?

Pretty well. There are some unusual features in this data but I think I understand what is causing them. The dip at 10.2 seconds is the expected drop from ejection. It is much smaller because the effective load resistance as seen by the battery is about 1000 ohms. After ejection the battery voltage recovers to a level above what it was prior to ejection. This is because the load presented by the continuity test resistors vanished when the electric match bridge opened up.

The dip at 36 seconds is from the second ejection event. This briefly connects the continuity circuit on this output. I think the dip at the end of flight must somehow be related to hitting the ground but I don't know the exact mechanism. In any case the battery voltage stayed within a vary narrow 40mV range throughout the entire flight.

The battery voltage is quite a bit lower (8.65 vs 8.9) than on the previous flight. Since I have been using the same lithium battery for all of these tests, it might be nearing the end of its life.

I think I have learned about all I can from the Initiator flights and it is time to move on to something else. Things I would like to test are performance with a Hypertek hybrid, supersonic flight, and high altitude flight.

I might be able to bum a ride on the next flight of project Aurora. This will be very fast and very high. It is scheduled for the June POTROCS launch in Wayside Texas.

5 July 2004

The flight of the Aurora didn't go as planned on several levels.

First is that the graphite nozzle failed and spit fuel grains all over the sky. Fortunately, Aurora already had enough velocity so that it went to 6,000' where it recovered normally.

I was still looking forward to the data but there were problems there as well. I didn't go to the launch to babysit the altimeter so I am not really sure what happened. The data has three distinct sections to it.

The first 4 seconds of data are totally bizarre. Pressure reading is at a full scale 4095 counts. Which simply cannot happen as the electronics in the Motorola pressure sensor will not go to the supply rail. Even stranger is that the acceleration reading is 8191 counts which is absolutely impossible for a 12 bit ADC.

Then there are 10 seconds or so of data that almost looks right. The pressure altitude is around 200 meters which is consistent with the Dallas area. Acceleration is at a nominal mid-scale value of ~2000 counts. And the altimeter cycles through the flight states.

Then there are 500 seconds of very boring rocket sitting on the pad data. Altitude is measured at 937 meters which is consistent with the altitude at the Wayside, TX launch site. Acceleration is at the typical 1993 counts for 1G sitting on the pad. But the altimeter has detected launch and has settled into a steady state with the Kalman filtered altitude at 982 meters, velocity of 28.3 m/s, and acceleration of 9.8 m/s/s. This is typical behavior because the acceleration measurement says it is moving and the pressure measurement says it isn't. The filter then settles into a state where these balance out. It makes for very boring data.

It appears that the altimeter was powered up with the rocket in a horizontal position and was then raised to vertical. The criteria for launch detect in the software is a velocity greater than 15 m/s. This is qualified with a sanity check that acceleration is greater than 10 m/s. This was apparently not high enough.

After this the altimeter was apparently powered up again and then moved around enough so that it detected launch again. This recorded a 14 second flight. Then something really bizarre happened to create the strange 4 seconds of data at the beginning.

The only interesting data to come out of this is that I got 500 seconds of battery voltage. It dropped from 8.93 to 8.87 volts during this time. Pretty good considering that I have used this same battery throughout the development cycle for this altimeter.

After looking at the data I began to wonder if the altimeter was damaged during the flight. But it operates normally.

18 July

I wasn't planning on producing a version of the filter code that uses only the pressure sensor data for a couple of reasons. The first was that my previous work has shown that the filter performs very well on flight data. The second reason is that it is a much simpler filter and if the more complex filter can be made to run in an altimeter, then the simpler filter surely would.

But I had a wild idea that I decided I just had to try out. I already optimize part of the code by replacing multiplies with simple shift operations. I can do this because I have chosen the sample rate to be a power of two. But I couldn't do this with the Kalman gains because they are arbitrary values. But I wondered what would happen if I rounded the gains to the nearest power of two.

A quick check on RDAS data showed that the performance was not significantly altered. This really surprised me as I thought that the filter would be more sensitive to gain changes.

After that test passed, I moved on to changing my existing altimeter code. This required modifications to the Kalman filter code to remove the acceleration innovation section. I also removed all of the code required to convert the pressure and acceleration measurements into engineering units.

Because I was going from 8 multiplies to 3 and replacing those with bit shifts, I thought that I could increase the sample rate from 64SPS to 256SPS. I was wrong for a couple of reasons.

The first reason was that the serial EEPROM chip lists a maximum write cycle time of 5ms and no typical time. The EEPROM was ignoring every other write request because it was still busy.

The second problem was that the code was just taking far too long to fit in the 3.8ms available at 256SPS. I wondered where all of the time was going to. I then discovered a handy feature of the MPLAB simulator: a stopwatch. This provides an easy way to measure the execution time of sections of code.

I located an error in my filter code using the stopwatch but the real surprise was the amount of time taken by the serial EEPROM routines. This code was taking about 7ms to execute.

I reviewed the code which I modified from the serial EEPROM routines that Robert DeHate has on the Roctronics web site. These included calls to delaying routines for timing purposes. I reviewed the data sheet for the EEPROM and decided that with a 4MHz clock, I wasn't going to need any delays. So they all went away. But the code still required about 3ms to execute. I couldn't see any easy way to make it work faster without replacing the bit banged serial routines with hardware. So that is as fast as it gets.

I also noticed that the routine for the ADC (also on a bit banged serial interface) was taking almost 0.7ms. I reviewed its data sheet and then removed the delays in it.

In the end I was able to get the execution time down to about 3.6ms. This is under the 3.8ms available at 256SPS but there isn't enough margin for me to feel comfortable at that sample rate. So the code runs at 128SPS.

I didn't alter the basic data storage routines so that means I get 8 seconds of 128SPS data and 256 seconds of 4SPS data recorded to the serial EEPROM.

I managed to get the new code ready to go before the 17 July DARS launch. I flew the altimeter in data only mode in my Initiator using yet another G64-7. I love that motor.

The downloaded data was interesting. The good news was that it got the apogee time right. The bad news is that it looked pretty bad in doing it. There were two problems. The first was that the raw pressure data looked awful.

The data is stair-stepped as though the ADC has less than 12 bits of resolution. And it exhibits an amazingly low amount of noise.

The filtered velocity doesn't look very good either:

While it got the apogee time right, it was very nearly a couple of seconds early. Which isn't good.

This is a result of two problems. The first is the actual pressure data. Because the pressure data is stepped, the filter sees a constant value for a while and converges to it. Constant data has zero velocity so the velocity heads to zero. The second problem is that the Kalman gains are too high. Lower gains would reduce the sensitivity of the filter to this problem.

I located the gain problem in my program to calculate the gains. It was a problem that I thought I had fixed before. The gain program was quiting sooner than it should have. It quit after just 34 iterations which is obviously too few. So I added the simple code to make sure it does at least 1,000 iterations before stopping. This resulted in much smaller gains which is what I expected.

The problem in the data is much tougher. I have two problems with the data. The first is apparent loss of resolution in the ADC and the second is that there just isn't enough noise in it.

First the noise. Motorola has an application note (AN1646) that discusses the random noise generated by their sensors. The claimed level of noise is about 16 counts in a 12 bit ADC. The example plot of the noise after filtering with the recommended RC filter (as used in the Roctronics hardware) is about 3mV peak-to-peak. Which means that since the ADC has a resolution of 1.2mV it should be seeing some noise. More noise than is in the flight data. I suppose that it is possible that this particular sensor has better noise performance than most but I doubt it.

The second problem is the loss of resolution. I double checked the data sheet to make sure I didn't violate any time parameters. The most important one is the sampling time. According to the data sheet, a sampling time of 1.5 microseconds is more than enough. And the sampling time in my code is much longer than that.

I played with the sampling time (longer, as short as possible) and just couldn't see any improvement. I do not know what the problem is here. In order to check this I needed a quick way to simulate the pressure drop from a flight. I put the altimeter into a coupler style payload bay and then bagged it using a FoodSaver. While I didn't improve the altimeter performance I did learn that my FoodSaver pulls 6 psi of vacuum. Not a very good vacuum but good enough.

I hope that the gain change will improve the performance so that the ADC issue will not cause too much trouble. I will flight test the altimeter again when I get a chance.

26 July

I have finished my R&D report for NARAM 46. Just in the nick of time as I have to turn it in on Sunday. Read it here first.

22 November 2004

My R&D report was good enough for 1st place in C division at NARAM-46. I love getting a first place but I will have to find another topic as this one is about played out. Lots of work still but I don't think that it will produce anything worthy of a first place.

I flew the altimeter with the pressure only code again on 24 July. This flight was on a F39 and the altimeter controlled ejection. Deployment was visibly just a little past apogee. While several friends said that it was good I knew that it just wasn't good enough and that I still had problems.

Downloading the data revealed that I still have major pressure data issues. The change in Kalman gains smoothed out the velocity estimate to the point where the altimeter wasn't in any danger of deploying early. But I am still very disappointed in the results.

Altitude has been post-processed into meters but the launcher offset has not been removed to show AGL altitude. The units for velocity are ADC counts/second.

I decided that my code tightening efforts on the ADC serial code were probably the culprit. I tried tweaking it but that didn't help any. Time to go back to the original slow code. I programmed the altimeter with the old ADC code and took that version with me to NARAM 46.

I would have sworn that I put in the old ADC code. But you can't tell by looking at the data from the flight at NARAM 46.

This is getting annoying.

A quick look at the acceleration data shows that it has been effected as well. This graph compares a brief period of two flights. The upper trace shows data from a May flight and the lower trace is from the flight at NARAM. The lower trace shows much greater variation and oddly enough, it seems to have a floor value that it will no go below.

I just looked at the timestamps for files. While the adc.asm file is clearly the old version from January, the .lst file shows that this version was not used by MPLAB in building the last version of the .hex file I have. I am not sure what happened but it was probably the old timestamp on the adc.asm file. I simply copied the old version of the adc.asm file into the project directory. The old time stamp was copied as well and this spoofed MPLAB into using the current adc.o file instead of assembling it. If I had taken a break and shut down MPASM, it would have gone through its usual annoying habit of assembling every file in the project. Instead it looked at the timestamps and decided that all was well. I guess I will just rebuild the code, program up the altimeter, and wait for the next rocket launch in the area to give it a try.

It looks like putting this down in a web document is useful after all. :-)

I will also adjust the liftoff criteria. I noticed that I left it at 15 which now means 15 counts/second rather than 15 meters/second. Since 1 count is more than 1 meter, this results in a delayed launch detect.

If you would like to some more details on the pressure only code, read about it here.

14 December

At the 11 December DARS launch I tried another flight test of this code. I had corrected the problem with the ADC code that resulted in very bad data on previous flights. I was planning on using a F39 motor to keep things low enough to judge if the ejection happened at apogee. But I couldn't find one in my motor box and went with a G64-10 instead. The flight looked OK with ejection around apogee.

But a look at the data showed that the timing was off.

This is a plot of unfiltered altitude and acceleration data. At first glance it looks pretty good. The pressure data is certainly a big improvement over the past couple of flights and ejection appears to have occurred pretty close to apogee. But a closer look reveals that ejection was a bit earlier than I like. Also note that because of the way pre-launch detect data buffering works, launch detect is at 1 second. The altitude had only increased by about 25 meters so this happened reasonably quickly.

The change in the data storage rate from 128 SPS to 4 SPS that happens at 8 seconds makes it more difficult to tell, but the rocket was definitely still going up when ejection occurred. This version of the filter does have a tendency to be a tenth of a second or two early but this appears to be earlier than that.

At this point I did something that I should have done first: cobbled up some code to post process the flight data using a Kalman filter. By doing this I could play with variations on the implementation without actually having to do a flight test.

In this test code, I run the Kalman filter using math similar to what is used in the altimeter. The numbers are represented the same way and I use shifts and adds for the math. I also processed the data in parallel using multiplies to apply the Kalman gains. This makes things a bit interesting as it requires more than a 32X32 multiply. Fortunately, GCC has a "long long int" type which is 64 bits long. This allows me to do the required math with a bit of care.

After processing the flight data I then could compare the filter results using the full precision Kalman gains and the gains rounded to a single bit of precision.

Probably the best metric to judge the performance of a Kalman filter is to look at the innovations. These are the differences between the predicted and measured states that are used to correct the filter after multiplication by the Kalman gains. If the filter is working correctly, these innovations should look like zero mean random noise. Because my system model assumes constant acceleration, the innovations will not be zero mean when the acceleration is changing. But they should converge to zero as the rocket approaches apogee.

A quick plot showed that this was not the case.

The simplified shift only version never converges to a constant value. The multiply version does converge to a non zero value which is to be expected because acceleration is constantly changing. The plot ends at 8 seconds because the stored data rate changes and this would require a change of Kalman gains and such which seemed to be more effort that was really needed.

After looking at things carefully, I realized that I had made a mistake in translating the Kalman gains into bit shifts. I had simply miscounted the bits. I had shifted by 5, 5, and 6 bits when I needed to go 6, 6, and 7 bits. After fixing this I then plotted the innovations again:

While there are some differences between the two, they are really quite close.

I think I will correct the altimeter code, see if I can locate some F39's, and try another flight test.

Because I have an extra 2 bytes of data in each 16 byte sample, I sometimes include another data item of interest. This time it was the execution time for the altimeter code. I read the interrupt timer towards the end of the data storage code. There are a few more instructions executed after this but not many. This plot shows how many milliseconds it takes to process each sample.

While there is some random variation, execution time is around 3.9 milliseconds which is about half of the 7.8 milliseconds available.

20 December

Yet another flight test. I didn't get a chance to purchase some more F39 reloads so I dug out a F72-10. The delay was a bit longer than I liked for a backup but I really didn't expect that I would need the backup. The flight was good and deployment appeared to be close to apogee.

With the lower impulse, ejection occurred before the data recording rate switched to 4 SPS which makes it a lot easier figuring out just exactly what was happening at apogee. But the data shows that there is still something going on that I don't quite understand.

This first plot has 4 different versions of the pressure measurement: unfiltered, filtered using full 16 bit gains, flight filtered, and post processed filtered.

While there is a slight difference between the flight and post processed versions, it is almost undetectable. Which is to be expected because they use the same method. The version using the full 16 bit gains diverges some which is expected. What is not expected is that the version using the full gains is not tracking the measured altitude well at all. For at least a few seconds prior to apogee it should track the pressure almost exactly. Since it doesn't, I have a problem somewhere.

Another curious item in the data is that at deployment there is a significant spike in the pressure data followed by an offset shift. I haven't seen this in any other flight and I don't what caused it.

It looks like the filter is not catching up in time for apogee. I use the model noise parameter to tune the filter and lower values provide faster response. So I tried changing the gains by altering the model noise parameter I feed to the gain program from 10,000 to 1,000. I then plotted the data:

This shows the raw altitude along with the altitude and velocity filtered using both 16 bit gains and shortened gains. This is a lot better. The filtered altitude is tracking the measurement nicely and the velocities show that apogee was about one second later than the ejection event.

Here is a plot of the innovations when using a noise parameter of 1,000.

Notice how these converge to zero well before apogee. This is the expected behavior and shows that the filter is working well.

There is just one small problem with this. If you look at figure 10 in my first R&D report, you will see that a noise parameter of 1000 is very close to the value that will result in premature ejection in high speed flights. But that data was derived using a full floating point Kalman filter. This simplified version might be more (or less) sensitive to this parameter. Caution would dictate using a noise parameter of at least 10,000.

Leaving the noise parameter at 10,000 will provide plenty of margin so that high speed will not be a problem. But then relatively short flights without a lot of coasting time will eject about a second early. Not exactly what I was hoping for. But this does show that a Kalman filter using just pressure data can be made to run very fast with reasonable performance.

I still vastly prefer using both acceleration and pressure data as this lets me run much lower noise parameters and still avoid high speed problems. Although I have to keep the multiplication routine around to handle the engineering unit conversions of pressure and acceleration, if really pressed for time, it looks like I could simplify the correction code by just using a simple shift with rounded gains.


The latest version of the code is now 1.3. I have flight tested this so far only on fairly low altitude flights. It seems to be bug free but that only means that no more bugs have appeared. If you decide to use this to control deployment, keep in mind that it is developmental and I make no guarantees about its operation. The code is designed as a vehicle for demonstrating a Kalman filter and not as something suitable for general use.

There is a lot of stuff in this zip file. All of the source code (100% assembly language) and project files suitable for Microchip's MPLAB program. Plus supporting programs.

File Description
main.asm Startup code, interrupt code, and some other things that have to be in the first page.
dual.asm This is the main altimeter code. It controls the progression from pre-flight to launch and landing.
pickal32.asm The Kalman filter code.
accel.asm Gets an acceleration sample and converts it engineering units.
altitude.asm Gets a pressure sample and converts it to altitude.
adc.asm Controls the ADC to sample an input.
b16tobcd.asm Binary to bcd conversion routine used by the altitude beeping code.
beepalt.asm Code to beep out the AGL altitude after the flight.
contbeep.asm Performs the status beep during pre-flight including continuity status.
data.asm Contains all of the data storage allocation in what passes for RAM in a PIC.
eedata.asm Defines some values (Kalman gains and such) stored in on chip flash memory. Also contains the routines used to read these values.
fxm3216s.asm A 32 bit by 16 bit multiply extracted from the Microchip math library.
serial.asm Simple code to upload data to a host computer using the hardware UART.
serialee.asm Code to write and read data to the external 256Kb serial EEPROM.
adc.inc Some constants defined for use with the ADC module.
data.inc References for all of the data items defined in data.asm
eedata.inc References for all of the data items defined in eedata.asm
.cod .cof .hex .mcp .mcw .lkr Various project files used/created by MPLAB. The .hex file is the code ready to be programmed into a 16F628.
kgain32.c/kgain32.exe This program calculates the Kalman filter gains for the third order, two measurement (pressure and acceleration)filter. Input is the sample rate, acceleration noise standard deviation, altitude noise standard deviation, and ratio of acceleration noise to model noise. Source and DOS executable.
kgain31.c/kgain31.exe Just like kgain32.c but for a single measurement (pressure) filter. These gains are used after apogee because the acceleration measurement becomes useless at that point. Source and executable.
pdata.c/pdata.exe One of the nastier details and an area of great compromise is the conversion of the pressure measurement to altitude. This is an exponential relationship which is beyond the capability of a PIC to compute. So I do a piecewise linear interpolation. The range is broken into 16 segments and slopes and offset are computed for each one. This is a lot of data for the table so I have the code emit assembly code that can be pasted into the source file. Source and executable.
process.c/process.exe This program takes the hex dump of data from the altimeter and processes it into decimal data and adds a time column. You can then plot the data using GNUPlot. The data is: time, altitude, velocity, acceleration, raw pressure reading, raw acceleration reading, altimeter state, spare channel, pressure altitude, acceleration, inertial velocity, and inertial altitude.

You will need to capture the data using a terminal program (hyperterminal) and then clean up the result before feeding it to process.c Start up Hyperterminal (19,200 bps 8N1, no flow control), power up the altimeter and you should a string of the character 'M'. Type an X (quickly) and the data will start.

I used a MAX232 chip I had handy to interface the altimeter to the serial port. Source and executable.

Debugging Log.pdf I have incuded a copy of the log I started while debugging the code. Just in case anyone is interested in the process, or lack thereof.

This code implements a dual deployment altimeter with data storage. The code uses slightly less than 1.5K of the 2K code space of a 16F628.

Because most of the execution time is spent performing eight (six for the Kalman filter and two for pressure and acceleration engineering unit conversion) 32X16 multiplies, a pressure only filter is much faster as it only has to do 3 multiplies.