I purchased an Aeropak Tailcone suitable for 76mm Motor Mount and 3.9″ Air-frame. The weight of all its parts together is 150 grams.
Here are some pictures of it.
I had to carefully sand down the motor mount external surface, approximately 1″ from the bottom to allow me to fit the threaded Aeropak mount component on. See pictures below.
I had to sand, then test, then sand multiple times, to ensure I didn’t over-do the sandpapering.
I have to clean the motor mount, remove the fibreglass dust to test joint and to ensure it is ready for gluing, later on.
One of the very important steps I have to do soon is to glue the motor mount (via top Centering Ring) into the main Air-frame tube. I have to be particularly careful here because we want to have the motor mount sticking out just enough to ensure that there is adequate room to install the Aeropack Tailcone. So here I am conducting a trial Dry-run installation.
As you can see here, I’ve used wires to allow me to remove the AFT Centering ring, which I’ll need to do at some point during the install.
I’m reasonably confident with it, but going to double check it.
I decided to purchase a Syringe pack of 24 hr Alaldite glue. I choose 24hr glue because it offers strengths up to 150kg. I choose to get it in syringe form to help with the delivery of equal amounts of Part A and Part B.
I was very careful to follow the instructions. The instructions said to mix it for a minute and then let it rest for 5 to 10 minutes. So I did this.
Before doing any gluing I marked 7cm down the motor mount, so I would glue it in the right place. I also marked the strap.
I was very particular about the GRIT of sand paper. I wanted to ensure I had low enough grit to ensure I get a good join.
I sand-papered it over a larger area than the strap because the glue will spread out beyond the Motor Mount. Increased surface area, increased strength.
Then I cleaned the motor mount of dust.
I wanted to make sure glue did get everywhere I don’t want it, including INSIDE the motor mount. So I’ve taped it well.
Following the instructions on the Araldite, I applied glue to both materials.
Then I brought the two together and used making tape to keep the two together as it dried. The glue job was complete at 16:00. So by tomorrow morning it will be dry. It won’t be at it’s max strength, but it will be enough to start on the other end of the strap.
I will probably apply more araldite over the cord, below where the centering ring is to be, to get increased bond strength.
The Centering rings are what we use to attach the Motor Mount to the Air-frame. The Centering rings provided did not fit the Motor mount, too tight (this is not bad). So I very carefully sanded them down by hand, as evenly as possible. Every few minutes I would check to see if they would fit. The top centering ring is notched, to slide over the Bridle cord. So I marked the top of the Motor mount as ‘Top’. And I marked the bottom of the Motor Mount as bottom.
Eventually after about 30mins of patient sanding I was able to slide both centering rings over the Motor Mount.
I’ve already started building the Avionics bay, admittedly a little early in the steps, but decided that it would be good to see how it fitted in with everything else. There was a bit of gluing here, but decided that constructing parts of the Avionics Bay is not going to stifle the remaining construction of the rocket.
I wasn’t terribly with the result of the tray. I should have laid down some tape to ‘restrict where some glue could go. I’m sure it would work (be strong enough), but from a cosmetic point of view – not good.
So, I’ve decided to make my own tray. See pictures below.
Wood pieces, all identical
I had to take extra care in creating these wood pieces. I performed just TWO cuts of three pieces sticky-taped together in a special decide to ensure that all the cuts would be square. Because I cut them altogether, this meant they were precisely the same length.
In terms of drilling holes, I just took my time and very carefully ensured the drill was level and the markings were as close as possible.
I’ve weighed all the components of the rocket. I’ve tabulated them below.
To identify which parts I’m referring to, I’ve included a picture of the components on the installation instructions.
Booster Airframe: 1494 grams
Motor Mount: 443 grams
Payload Airframe: 721 grams
Nose Cone: 349 grams
Nose Cone Coupler Tube: 222 grams
One Top Fin: 151 grams
One Bottom Fin: 113 grams
Bridle Strap: ~40 grams
Avionics Bay Fibre-Glass (52mm length): 60 grams
Avionics Bay (Wood + Bulkheads + threaded rod + nuts + washers + eye-ring bolts : 395 grams
Nose Cone Bulk plate + Eye Bolt + Nut: 90 grams
Avionics Bay Coupler (275 mm): 322 grams
Centering Rings (2 of these): 15 grams
We need to know these weights so that we can create a simulation file in Openrocket to simulate the flight of the rocket.
NOTE: I’ve opted to use a different sled arrangement in the Avionics Payload. Wasn’t happy with the one provided. The weights are almost the same, just a few grams lighter.
I have finally purchased and received my L2 rocket parts; a kit called Katana 4 from AusRocketry. It is a fibreglass kit, 4″ suitable for J, K and possibly L motors. I’m going to take my time making this one, making sure I get all the joins really well done, the holes in the right place, the paint just perfect!
The first step has been to identify all the pieces.
Well, not quite all the pieces, but some of the main ones. I will get myself a weigher (previous one stopped working) so I can get the individual weights. Then i’ll be able to create myself a simulation file in OpenRocket and work out how it will perform.
The second step has been to clean them up, vaccuming off dust and wiping any residual dirt. . See some pictures below.
Here are some other pictures to put the rocket into perspective.
I’ve created a jig (from 3-D Printout and some wood) into which we can mount the Stabilisation system and have it connected to the computer and to either an internal or external PSU for the Servo Motors. This set-up is good, because it allows me to easily update the Arduino program as I find bugs or worthwhile enhancements.
Below is a video a recent test.
In the first ~4 seconds, it shows me lifting (accelerating) the system up. You will notice that the red turns off. This marks the time that the rocket has sensed sufficient acceleration for it to assume the rocket has been launched.
Then we see after a very short period (0.1 seconds in real time), the red LED turns on. This marks the time the rocket circuitry has decided that it should move the masses. You see them moving very quickly to the front.
Then as the movie continues (in normal speed), you see me rotating the rocket, but the masses continue to maintain their ‘direction’. This is to ensure that the moment generated by the rocket motor stays in the same direction.
It has been estimated that in a state of minimal air resistance this would lead to an angle of ~ 14 degrees. Of course, there will be air resistance and this experimental launch will give us an indication of the ‘real’ amount it affects the motion of the rocket. This is very important for us in the tuning of the stabilisation system.
I’ve created a Java 3D simulation of this rocket during the motor firing.
This is for a Callisto rocket with a 125G131-14A (SS) motor. It’s final motion properties are:-
Rotational Velocity: 32 degrees/second
Rotational Position: 14 degrees from the vertical
Down-Stream Position: 1.25 metres
Down-Stream Velocity: 6.5 metres/second
We can determine that that the masses are moved to their position 2 frames later, so the total number of frames passed by time masses are moved is 5. This equates to approximately 0.21 seconds.
We are interested to know is what the velocity and position is when this starts. We want it to be when stabilisation has occurred, but we also want as much time as possible in this position, so we can maximum amount of data.
NOTE: I am aware that the moment generated is a result of ALL the forces on the rocket cross product with the distance vector and I’m obviously ignoring a non-trivial force, the aerodynamic forces. It is expected however that we should see some tenancy for the rocket to veer to one side. The results will be interesting and useful.
It is expected that shortly after the rocket motor has finished, the rocket still stabilise and not rotate much more. This is because aerodynamic forces will provide counter-acting Moment.
I put together a small air-pressure sensor that could fit inside a Junior Model rocket nose cone. The components used were:-
3.3v Mini Trinket
BMP180 GY-68 sensor
50mAH Lithium Ion Battery
A lot of hardcore programming to get it to fit in 5k of space!
Some pictures of this are shown below.
NOTE: We had a very small hole drilled into the side of the Nose Cone to allow air pressure to equalise.
The code had to be able to detect a reduction in Air Pressure, to sense that the rocket flight had started. Then it had to record the last 10 measurements plus another 40 measurements inside the EPROM of the Arduino. We had a measurement taking place every 0.2 seconds, so we got 10 seconds of measurements.
And it worked quite well! Below is a plot of points for maiden flight.
Below is the code I used. Please note, that I have not included a cut-down version of SoftwareSerial. Essentially the Trinket never needs to receive data on its software Serial link, so I commented out code in the library. This allows the program to JUST fit inside the 5310 Bytes.
#include <TinyWireM.h>
#include <SoftwareSerial.h>
#include <EEPROM.h>
// Uncomment following line, recompile and upload to
// read results. This is because we don't have enough
// space to have READ and WRITE functionality co-existing.
// #define READ
SoftwareSerial mySerial(4, 3); // RX, TX
int addr = 0; // EEPROM memory
#define BMP180_ADDRESS 0x77 // I2C address of BMP180
// define calibration data for temperature:
int ac1;
int ac2;
int ac3;
unsigned int ac4;
unsigned int ac5;
unsigned int ac6;
int b1;
int b2;
int mb;
int mc;
int md;
long b5;
//define variables for pressure and temperature calculation
long x1,x2;
//define variables for pressure calculation
long x3,b3,b6,p;
unsigned long b4,b7;
//define variables for temperature and pressure reading
long p_avg = 0;
byte iter = 0;
int p_threshold = 200;
int p_threshold_count_required = 3;
int p_threshold_count = 0;
boolean launch_detected = false;
boolean information_saved = false;
long rawpressure;
long hist_measurements[10];
long measurements[40];
short hist_measurement_index = 0;
short measurement_index = 0;
const unsigned char OSS = 3; // Oversampling Setting
/* blz 12 Datasheet
OSS=0 ultra Low Power Setting, 1 sample, 4.5 ms 3uA
OSS=1 Standard Power Setting, 2 samples, 7.5 ms 5uA
OSS=2 High Resolution, 4 samples, 13.5 ms 7uA
OSS=3 Ultra High Resolution, 2 samples, 25.5 ms 12uA
*/
void setup() {
pinMode(1, OUTPUT);
digitalWrite(1, HIGH);
TinyWireM.begin();
delay(5000);
// First read calibration data from EEPROM
ac1 = bmp180ReadInt(0xAA);
ac2 = bmp180ReadInt(0xAC);
ac3 = bmp180ReadInt(0xAE);
ac4 = bmp180ReadInt(0xB0);
ac5 = bmp180ReadInt(0xB2);
ac6 = bmp180ReadInt(0xB4);
b1 = bmp180ReadInt(0xB6);
b2 = bmp180ReadInt(0xB8);
mb = bmp180ReadInt(0xBA);
mc = bmp180ReadInt(0xBC);
md = bmp180ReadInt(0xBE);
delay(5000);
mySerial.begin(9600);
// mySerial.println("S");
digitalWrite(1, LOW);
#ifdef READ
// uncomment this and comment out the writeResults below when wanting to read values.
readResults();
#endif
}
void loop() {
// first, read uncompensated temperature
//temperature = bmp180ReadUT();
//and then calculate calibrated temperature
unsigned int rawtemp = bmp180ReadUT();
b5 = computeB5 (rawtemp);
// temperature = bmp180CorrectTemperature(bmp180ReadUT());
// then , read uncompensated pressure
rawpressure = bmp180ReadUP();
b6 = b5 - 4000;
// Calculate B3
x1 = (b2 * (b6 * b6)>>12)>>11;
x2 = (ac2 * b6)>>11;
x3 = x1 + x2;
b3 = (((((long)ac1)*4 + x3)<<OSS) + 2)>>2;
// Calculate B4
x1 = (ac3 * b6)>>13;
x2 = (b1 * ((b6 * b6)>>12))>>16;
x3 = ((x1 + x2) + 2)>>2;
b4 = (ac4 * (unsigned long)(x3 + 32768))>>15;
b7 = ((unsigned long)(rawpressure - b3) * (50000>>OSS));
if (b7 < 0x80000000) {
p = (b7<<1)/b4;
} else {
p = (b7/b4)<<1;
}
x1 = (p>>8) * (p>>8);
x1 = (x1 * 3038)>>16;
x2 = (-7357 * p)>>16;
p += (x1 + x2 + 3791)>>4;
// Debugging
// mySerial.println(p);
if (iter < 10) {
p_avg = (iter * p_avg + p)/(iter+1);
iter++;
// Debugging
/*
if (iter == 10) {
mySerial.print("a");
mySerial.println(p_avg);
mySerial.println(((p_avg >> 32) & 0xff), HEX);
mySerial.println(((p_avg >> 16) & 0xff), HEX);
mySerial.println(((p_avg >> 8) & 0xff), HEX);
mySerial.println((p_avg & 0xff), HEX);
delay(120);
}
*/
}
if ( p_avg - p > p_threshold) {
p_threshold_count++;
} else {
p_threshold_count = 0;
}
if (p_threshold_count >= p_threshold_count_required) {
launch_detected = true;
}
#ifndef READ
if (launch_detected) {
digitalWrite(1, HIGH);
measurements[measurement_index] = p;
measurement_index++;
if (measurement_index > 40) {
measurement_index = 0;
if (! information_saved) {
information_saved = true;
writeResults (p_avg, hist_measurement_index);
}
}
} else {
hist_measurements[hist_measurement_index] = p;
hist_measurement_index++;
if (hist_measurement_index > 9) hist_measurement_index = 0;
}
#endif
// mySerial.println(rawtemp);
// mySerial.println(temperature);
// mySerial.println(b5);
delay(200);
}
int bmp180ReadInt(unsigned char address)
{
unsigned char msb, lsb;
TinyWireM.beginTransmission(BMP180_ADDRESS);
TinyWireM.send(address);
TinyWireM.endTransmission();
TinyWireM.requestFrom(BMP180_ADDRESS, 2);
while(TinyWireM.available()<2);
msb = TinyWireM.receive();
lsb = TinyWireM.receive();
return (int) msb<<8 | lsb;
}
unsigned int bmp180ReadUT()
{
unsigned int ut;
// Write 0x2E into Register 0xF4 and wait at least 4.5mS
// This requests a temperature reading
// with results in 0xF6 and 0xF7
TinyWireM.beginTransmission(BMP180_ADDRESS);
TinyWireM.send(0xF4);
TinyWireM.send(0x2E);
TinyWireM.endTransmission();
// Wait at least 4.5ms
delay(10);
// Then read two bytes from registers 0xF6 (MSB) and 0xF7 (LSB)
// and combine as unsigned integer
ut = bmp180ReadInt(0xF6);
return ut;
}
/*
double bmp180CorrectTemperature(unsigned int ut)
{
x1 = (((long)ut - (long)ac6)*(long)ac5) >> 15;
x2 = ((long)mc << 11)/(x1 + md);
b5 = x1 + x2;
return (((b5 + 8)>>4));
}
*/
// Read the uncompensated pressure value
unsigned long bmp180ReadUP()
{
unsigned char msb, lsb, xlsb;
unsigned long up = 0;
// Write 0x34+(OSS<<6) into register 0xF4
// Request a pressure reading w/ oversampling setting
TinyWireM.beginTransmission(BMP180_ADDRESS);
TinyWireM.send(0xF4);
TinyWireM.send(0x34 + (OSS<<6));
TinyWireM.endTransmission();
// Wait for conversion, delay time dependent on OSS
// delay(5 + (5*OSS));
delay(26);
// Read register 0xF6 (MSB), 0xF7 (LSB), and 0xF8 (XLSB)
TinyWireM.beginTransmission(BMP180_ADDRESS);
TinyWireM.send(0xF6);
TinyWireM.endTransmission();
TinyWireM.requestFrom(BMP180_ADDRESS, 3);
// Wait for data to become available
while(TinyWireM.available() < 3)
;
msb = TinyWireM.receive();
lsb = TinyWireM.receive();
xlsb = TinyWireM.receive();
up = (((unsigned long) msb << 16) | ((unsigned long) lsb << 8) | (unsigned long) xlsb) >> (8-OSS);
return up;
}
double bmp180CorrectPressure(unsigned long up)
{
b6 = b5 - 4000;
// Calculate B3
x1 = (b2 * (b6 * b6)>>12)>>11;
x2 = (ac2 * b6)>>11;
x3 = x1 + x2;
b3 = (((((long)ac1)*4 + x3)<<OSS) + 2)>>2;
// Calculate B4
x1 = (ac3 * b6)>>13;
x2 = (b1 * ((b6 * b6)>>12))>>16;
x3 = ((x1 + x2) + 2)>>2;
b4 = (ac4 * (unsigned long)(x3 + 32768))>>15;
b7 = ((unsigned long)(up - b3) * (50000>>OSS));
if (b7 < 0x80000000) {
p = (b7<<1)/b4;
} else {
p = (b7/b4)<<1;
}
x1 = (p>>8) * (p>>8);
x1 = (x1 * 3038)>>16;
x2 = (-7357 * p)>>16;
p += (x1 + x2 + 3791)>>4;
return p;
}
int32_t computeB5(int32_t UT) {
int32_t X1 = (UT - (int32_t)ac6) * ((int32_t)ac5) >> 15;
int32_t X2 = ((int32_t)mc << 11) / (X1+(int32_t)md);
return X1 + X2;
}
// avg_pressure = 32-bit measurement of air pressure - AVERAGE
// hist_index = where we are up to in the histortical measurements...the place of the last value
// This helps us know where the start/end numbers are in the historical data...when we go to
// put all the data back together again.
// historical_measurements = pointer to where the historical measurements are
// measurements = pointer to post launch test results are stored.
void writeResults(long avg_pressure, short hist_index)
{
// Save the Average value
EEPROM.write(addr, ((avg_pressure >> 16) & 0xff));
addr++;
EEPROM.write(addr, ((avg_pressure >> 8) & 0xff));
addr++;
EEPROM.write(addr, (avg_pressure & 0xff));
addr++;
EEPROM.write(addr, hist_index);
addr++;
for (int i = 0; i < 10; i++) {
EEPROM.write(addr, ((hist_measurements[i] >> 16) & 0xff));
addr++;
EEPROM.write(addr, ((hist_measurements[i] >> 8) & 0xff));
addr++;
EEPROM.write(addr, (hist_measurements[i] & 0xff));
addr++;
}
for (int i = 0; i < 40; i++) {
EEPROM.write(addr, ((measurements[i] >> 16) & 0xff));
addr++;
EEPROM.write(addr, ((measurements[i] >> 8) & 0xff));
addr++;
EEPROM.write(addr, (measurements[i] & 0xff));
addr++;
}
int i = 0;
while (i < 100) {
digitalWrite(1, HIGH);
delay(50);
digitalWrite(1, LOW);
delay(150);
i++;
}
}
void readResults()
{
byte val;
for (int i = 0; i < 154; i++) {
val = EEPROM.read(i);
mySerial.println(val, HEX);
}
while(1);
}
Then I needed to write a PERL script to interrogate these data from the Serial Port and put in a CSV File.