|
|
View previous topic :: View next topic |
Author |
Message |
kein
Joined: 23 Jul 2007 Posts: 103
|
Ring Buffer to implement simple lowpass filter |
Posted: Thu Jun 19, 2008 5:07 am |
|
|
Hi Guys,
I would like some help on the implementation of a ring buffer. I've a data coming from adc and wouldlike to apply a simple FIR filter to the data. That's a a signed ring int16 buffer of size 50 would be a good enough.
What I want to do is read 40 samples from the ringbuffer into a processing array while the ringbuffer continuous to fill up in an interrupt subroutine. the interrupts occurs every 10ms and adc should be read into the ringbuffer.
I should then apply a filter to the processing buffer while the ringbuffer continous to fillup. My sampling frequency is 100Hz and my signal of interest is between 1-2HZ. My PIC processor is 18F4450.
Any help please is welcome...
Kind regards |
|
|
Ttelmah Guest
|
|
Posted: Thu Jun 19, 2008 7:43 am |
|
|
The FIFO buffer used in the EX_SISR example, is a basic ring buffer design.
I'd suggest you make the size 64 words (rather than 50), or alter the way the 'remainder' calculation is done, since the example, is 'inefficient', if not using a binary buffer size. This design will work just as well with 16bit data as the 8bit data it currently handles, by simply changing the size of the array elements.
Instead of driving this with the external serial interrupt, simply use a timer interrupt to trigger the acquisition. I'd suggest that you have your reading 'lagging' by one, so when you arrive in the interrupt, you read the last ADC value acquired, then trigger a new acquisition. This way the code won't have to wait for the ADC.
The real 'work' will be in calculating the FIR filter. Are you using a DSP PIC, or a basic design?.
Best Wishes |
|
|
kein
Joined: 23 Jul 2007 Posts: 103
|
Digital filter and Circular buffer |
Posted: Tue Jun 24, 2008 12:39 am |
|
|
Thanks Ttelmah for your response. No I'm not using DSP chip however I'm generating filter coefficient in MATLAB (fixed point math) and then store them into a buffer. The algorithm is as shown below however something is not working properly. I don't know if this has to do with circular buffer or not.
What im doing is to wait until the difference between the head and the tail is equal to 8 and then begins to take a reading from the circular buffer.
But this doesn't seem to go well any help?
Code: |
#include <18F4550.h>
#device adc=10
#FUSES HSPLL, PLL5, CPUDIV1, NOWDT, PUT, BROWNOUT, NOLVP // 20 MHz xtal
#use delay(clock=48000000)
#use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8)
#define BUFF_SIZE 64
int1 filter_flag;
int1 new_data_flag;
int1 new_data_flag;
int16 new_data;
int16 ptimer;
int16 Ring[BUFF_SIZE];
const int order = 32;
const int16 filter[order] = {-17,-42,-80,-125,-159,-156,-80,104,421,883,1473,
2148,2838,3461,3934,4189,4189,3934,3461,2838,2148,
1473,883,421,104,-80,-156, 159,-125,-80,-42,-17
};
/*Timer2 interrupts every 3ms and keep on restarting adc every 10ms*/
#int_TIMER2
void TIMER2_isr(void)
{
static int8 ticker = 3;
if(--ticker==0
{
ticker = 3;
read_adc(ADC_START_ONLY);
}
}
/*Read adc results into a buffer*/
#int_AD
void AD_isr(void)
{
Ring [head] = (read_adc(ADC_READ_ONLY) - 0X266);
filter_flag = 1;
if(++head==BUFF_SIZE) head = 0;
}
void DigitalFiler ()
{
Int16 y = 0;
Int16 data[order];
int k;
if(filter_flag==1)
{
filter_glag = 0;
for (k = 0; k < order; k++) data[k] = 0;/*clear Buffer*/
data[0] = Ring[tail];
if (++tail == BUFF_SIZE) tail = 0;
for (k = 0; k < FILTER_ORDER; k++)
y += (Int16)(filter[k] * data[k]);
new_data = y ;
new_data_flag = 1 ;
for (k = order-1; k > 0 ; k--)
data[k]= data[k-1]; /* do convolution by reversing */
}
}
}
void FindPeak()
{
//throw new Exception("The method or operation is not implemented.");
if (new_data_flag == 1)
{
new_data_flag = 0;
if(++ptimer==1024) ptimer = 0 ;
printf("%3d,\t%3Ld\n\r",ptimer, new_data);
/*if (PAMP[pos] < new_data)
{
PAMP[pos] = new_data;
PTIME[pos] = ptimer;
}
/*..........Todos.................
pos++;
*/
}
}
void main()
{
set_tris_b(0x17); // TRISB = 00010111; RB4,RB2,RB1 and RB0
setup_timer_2(T2_DIV_BY_16,249,10);
setup_adc_ports(AN0_TO_AN2|VSS_VDD);
setup_adc(ADC_CLOCK_DIV_64);
set_adc_channel(0); // select the required channel //
delay_us(10);
enable_interrupts(INT_ADC);
enable_interrupts(INT_TIMER2);
enable_interrupts(GLOBAL);
for(;;)
{
DigitalFiler ();
FindPeak();
}
} |
|
|
|
Ttelmah Guest
|
|
Posted: Tue Jun 24, 2008 2:14 am |
|
|
Without looking very far, you are going to get a problem with subtracting 0x266, from the ADC value. If the value goes below 0x266, you are going to get a massive positive result. You either need to cast the ADC values to signed, and use a signed int16 to hold the readings, or check that the value is greater than 0x265, before performing the subtraction...
As a 'comment', read the ADC, in the timer loop, and gt rid of using the ADC interrupt. So:
Code: |
/*Timer2 interrupts every 3ms and keep on restarting adc every 10ms*/
#int_TIMER2
void TIMER2_isr(void)
{
static int8 ticker = 3;
if (ticker=3) {
Ring [head] = (read_adc(ADC_READ_ONLY) - 0X266);
) filter_flag = 1;
if(++head==BUFF_SIZE) head = 0;
}
if(--ticker==0
{
ticker = 3;
read_adc(ADC_START_ONLY);
}
}
|
The reading will be taken 3mSec after the ADC was triggered, and this saves the cost of having a second interrupt.
The ADC interrupt, will 'cost' perhaps 70+ instructions, which you might as well not have. :-)
Your digital filter code, transfers the first entry in Ring, but I don't see it using the latter entries for the higher orders. You only every load data[0]. Since the data buffer is not static, and you clear it every time new data arrives, I can't see how this will work...
The bracket counts don't match in the routine as posted, and you are accessing 'filter_glag', which doesn't exist, so I suspect this part is a major 'typo'...
Best Wishes |
|
|
kein
Joined: 23 Jul 2007 Posts: 103
|
What is wrong with my fir filter |
Posted: Sun Jun 29, 2008 7:14 am |
|
|
Thanks Tlemah
Last edited by kein on Mon Jun 30, 2008 7:04 am; edited 1 time in total |
|
|
kein
Joined: 23 Jul 2007 Posts: 103
|
What is wrong with my fir filter |
Posted: Mon Jun 30, 2008 7:03 am |
|
|
Thanks Tlemah for ur help. I've incorporated the changes u made into my code and even made changes to my filter. The fact is the filter distort my signal. In my interrupt subroutine I've am reading a sinusoidal signal into a variable which is saved into a ring buffer in filter function. The sin(12.5*t) has a frequency of 2Hz. When i output/print out the unfiltered signal I get the correct sin wave and if i pass it through the filter, I get something similar to a noisy signal. What is the problem here? The filter coefficient from a lowpass filter whose cut-off frequency is 3Hz and coefficients are 16-bit integers.
Please help..
Code:
Code: |
#include <18F4550.h>
#device adc=10
#FUSES HSPLL, PLL5, CPUDIV1, NOWDT, PUT, BROWNOUT, NOLVP // 20 MHz xtal
#use delay(clock=48000000)
#use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8)
#define BUFFER_SIZE 32
#define ORDER 32
#define BLEN 6
/*********************************************************************
* STRUCTURE: ADXL330ADC_SAMPLES { } *
* DESCRIPTION: This structure Contains three of all variables *
* flag, array of data[] and counter adxl330count *
* *
* PARAMTERS: adxl330flag -flag to signal event *
* adxl330Indx -data counter *
* adxl330Buf[] -Buffer for data storage *
* *
**********************************************************************/
typedef struct ADXL330ADC_SAMPLES
{
Int16 PAMP [BLEN];
Int16 PTIME[BLEN];
Int16 NAMP [BLEN];
Int16 NTIME[BLEN];
Int16 new_ADC;
int16 adc;
Int16 Ring[BUFFER_SIZE];
} SAMPLES;
/* GLOBAL Variables*/
unsigned char tail;
unsigned char dir_change_flag;
unsigned char pIndx;
unsigned char nIndx;
unsigned Int16 ptimer;
int16 tick;
int1 new_data_flag;
int1 filter_data_flag;
int1 filterflag;
int1 direction_flag;
int16 h [order + 1] = { 0x0000, 0x0002, 0x000B, 0x0024, 0x0059, 0x00B9,
0x0155, 0x023C, 0x0375, 0x0501, 0x06D1, 0x08CC,
0x0ACA, 0x0C9C, 0x0E14, 0x0F08, 0x0F5C, 0x0F08,
0x0E14, 0x0C9C, 0x0ACA, 0x08CC, 0x06D1, 0x0501,
0x0375, 0x023C, 0x0155, 0x00B9, 0x0059, 0x0024,
0x000B, 0x0002, 0x0000};
SAMPLES signal;
//void Adxl330BuffFree(void);
void TIMER2_isr(void);
void FindPeak(void);
void DigitalFilter(void);
//void ADXL330VarsInitialize(void);
void main()
{
//ADXL330VarsInitialize();
//Adxl330BuffFree();
setup_adc_ports(AN0_TO_AN2|VSS_VDD);
setup_adc(ADC_CLOCK_DIV_64);
set_adc_channel(0); /* select the required channel*/
delay_us(10);
//setup_timer_2 ( T2_DIV_BY_4, 0xc0, 2);
setup_timer_2 ( T2_DIV_BY_16,249, 10); /*interrupts occurs every 3ms*/
set_timer2(0); /*this sets timer2 register to 0*/
enable_interrupts(INT_TIMER2);
enable_interrupts(GLOBAL);
/*Setup_Oscillator parameter not selected from Intr Oscillator Config tab*/
for(;;)
{
/* TODO: USER CODE!!*/
DigitalFilter();
FindPeak();
}
}/*End Main()*/
/*Timer2 interrupts every 3ms and keep on restarting adc every 10ms*/
#int_TIMER2
void TIMER2_isr(void)
{
float t;
static int8 ticker = 3;
if(ticker == 3)
{
t = 0.01*tick;
//signal.new_ADC= (int16)((int16)read_adc(ADC_READ_ONLY) - 0X266);
signal.adc = (int16)((sin(50*t))*1000);
filterflag = 1;
if(++tick ==512) tick = 0;
}
if (--ticker == 0)
{
ticker = 3;
read_adc(ADC_START_ONLY);
}
}
//Int16 y = 0;
void DigitalFilter(void)
{
int32 y = 0;
if (filterflag == 1)
{
int i;
filterflag = 0;
signal.Ring[tail] = signal.adc;
for (i = 0; i < ORDER; i++)
{
y += (int32)(h[i] * signal.Ring[(i + tail) & (ORDER - 1)]);
}
signal.new_ADC = (int16)y;
filter_data_flag = 1;
tail = (tail + 1) & (ORDER - 1);
}
}
void FindPeak(void)
{
//throw new Exception("The method or operation is not implemented.");
if (filter_data_flag == 1)
{
filter_data_flag = 0;
printf("%5Ld\n",signal.new_ADC);
}
} |
|
|
|
Ttelmah Guest
|
|
Posted: Mon Jun 30, 2008 8:42 am |
|
|
You still have the _sign_ problem that I pointed out before.
This will completely distort the results.
You are now using a sin function in the interrupt to generate the psuedo value. This will return +1, to -1. So your value will be +1000 to -1000, which you are then putting into an int16, which is an _unsigned type_. The value will then be seen as +1000 to 0, and then jump to +65535, to 64536.
I pointed this out before....
Best Wishes |
|
|
kein
Joined: 23 Jul 2007 Posts: 103
|
Thank you Tlemah |
Posted: Tue Jul 01, 2008 4:40 am |
|
|
sorry I assumed int16 is signed by default . Yes indeed that solve some of my problem. The sin wave is perfect now however my filter output are really bad! Here are the changes made. Please help.
Kind regards,
Code: | #define order 32
signed Int16 Ring[BUFFER_SIZE];
signed Int16 new_ADC;
signed Int16 adc;
int16 h [order + 1] = { 0x0000, 0x0002, 0x000B, 0x0024, 0x0059, 0x00B9,
0x0155, 0x023C, 0x0375, 0x0501, 0x06D1, 0x08CC,
0x0ACA, 0x0C9C, 0x0E14, 0x0F08, 0x0F5C, 0x0F08,
0x0E14, 0x0C9C, 0x0ACA, 0x08CC, 0x06D1, 0x0501,
0x0375, 0x023C, 0x0155, 0x00B9, 0x0059, 0x0024,
0x000B, 0x0002, 0x0000};
/*Timer2 interrupts every 3ms and keep on restarting adc every 10ms*/
#int_TIMER2
void TIMER2_isr(void)
{
float t;
static int8 ticker = 3;
if(ticker == 3)
{
t = 0.01*tick;
//signal.new_ADC= (int16)((int16)read_adc(ADC_READ_ONLY) - 0X266);
adc = (signed int16)(1000*sin(50*t)); /*signal: -1000 to +1000*/
filterflag = 1;
if(++tick ==512) tick = 0;
}
if (--ticker == 0)
{
ticker = 3;
read_adc(ADC_START_ONLY);
}
}
void DigitalFilter(void)
{
signed int32 y = 0;
if (filterflag == 1)
{
int i;
filterflag = 0;
signal.Ring[tail] = signal.adc;
for (i = 0; i < ORDER; i++)
{
y += (int32)(h[i] * signal.Ring[(i + tail) & (ORDER - 1)]);
}
snew_ADC = (signed int16)y;
filter_data_flag = 1;
tail = (tail + 1) & (ORDER - 1);
}
}
void FindPeak(void)
{
//throw new Exception("The method or operation is not implemented.");
if (filter_data_flag == 1)
{
filter_data_flag = 0;
printf("%5Ld\n",new_ADC);
}
} |
|
|
|
Ttelmah Guest
|
|
Posted: Tue Jul 01, 2008 3:15 pm |
|
|
I would suspect you are overflowing the arithmetic in several places. If you 'sum' your filter terms, you get 45604. If the routine was called with all the entries equal at (say) +1000, and the arithmetic applied to this, the result would be 45604000. Even if you assume the 'average' value coming in is +500, the arithmetic would give 22802000. Have performed the addition, you put the result straight into a signed int16 variable (can hold 32767 max....).
However this won't happen, since the multiplication of the buffer value and the term, is being done using signed int16, and will overflow on the way. Your biggest term, is 0xF5C (3932), and times 1000, will give 3932000, over 100* too large for the arithmetic being used...
Also, declare your variables at the start of the function, not the block. Though C++, allows this, and CCS, supposedly supports this, the older 'C style' declarations are safer...
Code: |
void DigitalFilter(void) {
signed int32 y = 0;
int i;
if (filterflag == 1) {
filterflag = 0;
signal.Ring[tail] = signal.adc;
for (i = 0; i < ORDER; i++) {
y += (signed int32)h[i] * signal.Ring[(i + tail) & (ORDER - 1)];
}
snew_ADC = (signed int16)(y/65536);
filter_data_flag = 1;
tail = (tail + 1) & (ORDER - 1);
}
}
|
You will need to check the rest of the code for similar overflows, but these are the 'glaring' ones.
Best Wishes |
|
|
kein
Joined: 23 Jul 2007 Posts: 103
|
Tlemah, mate you are a genius |
Posted: Tue Jul 01, 2008 10:01 pm |
|
|
Dividing y by 65536 prevented the overflow indeed although the signal (sine wave) is still distorted making it look like a square wave. I've increased the number of division by factor of 8 and overflow is removed.
The filtered signal is sinusoidal just like the original sin wave but offsetted.
The offset doesn't seem normal to me! How can We prevents the offset
?
Code: | void DigitalFilter(void) {
signed int32 y = 0;
int i;
if (filterflag == 1) {
filterflag = 0;
signal.Ring[tail] = signal.adc;
for (i = 0; i < ORDER; i++) {
y += (signed int32)h[i] * signal.Ring[(i + tail) & (ORDER - 1)];
}
snew_ADC = (signed int16)(y>>19); //=y/524288-----y/y/65536);
filter_data_flag = 1;
tail = (tail + 1) & (ORDER - 1);
}
} |
|
|
|
Ttelmah Guest
|
|
Posted: Wed Jul 02, 2008 3:22 am |
|
|
Offset is normal.
The filter looks 'back in time' by 32 elements. The peak response is to the element 17 samples ago. Filters always introduce phase delays.
Now, If you are prepared to accept a slight loss of accuracy in your filter, and you want to do other things with the processor (currently the filter is using a _lot_ of the processor time), there are quite a few things you can change.
First, your 'simulation' (using the sin), is putting larger values into the buffer, than the real data. The ADC, only returns 0 to 1023. You subtract 0x266 (why this odd value? - if the input is symmetrical, you would normally expect the 'middle' value to be 0x200). With 0x266 subtracted, the 'real' values, can only be -614 to +409. If you can bring the signal to be symmetrical, and have -512 to +511, and then take your filter terms, and divide them by 64, you will 'lose' the some terms, and degrade the accuracy a little, but the multiplication, will then fit inside the signed int16 arithmetic. So the 'biggest' term, will be +511 * (0xF5C/64) = 511 * 62 = 31682. This will speed up the arithmetic a lot, and the loss of accuracy will be surprisingly small.
Now, your final result will need (presumably) to be scaled to some output range (DAC?.). If you can chose the terms, so that the final division, is a 'nice' value in binary terms (such as /256, /65536 etc.), this can be performed by taking the MSB's of the result, rather than an actual shift (or even worse a division...). However the saving from this is small (since this is only done once for each filter pass).
There is also a 'trick' you can do in the term array. If you make this _double the size_, with the whole array repeated a second time (so the elements go from 0 to 63), then you can avoid having to do any arithmetic on the index to the array. You do the maths like:
Code: |
void DigitalFilter(void) {
signed int32 y = 0;
int i;
signed int16 * filter;
if (filterflag == 1) {
filterflag = 0;
signal.Ring[tail] = signal.adc;
filter=&h[order-tail];
for (i = 0; i < ORDER; i++) {
y += *(filter++) * signal.Ring[i];
}
snew_ADC = y/4096;
filter_data_flag = 1;
tail = (tail + 1) & (ORDER - 1);
}
}
|
(This done with the filter elements assumed to have been divided by 64).
What happens, is that you always go through the ring buffer from 0 to order-1, but offset the start location in the 'term' array, so that the right term is selected, according to the value in 'tail'.
Also make the term array signed int16 - this removes the need for the code to convert the value in the arithmetic.
So, the delay is normal, but there may be quite a bit you can do to improve the performance in terms of processor usage.
Best Wishes |
|
|
kein
Joined: 23 Jul 2007 Posts: 103
|
Thanks again |
Posted: Wed Jul 02, 2008 5:22 am |
|
|
Just a quick question with regards to the filter coeffecient. When you say I double the size of the term array, did you mean double the size of coeffeicent array (h[2*size] or the ring buffer (ring[])?
Because below it seems you have increased or double the size of the coefficient array (h[]).
Code: | signal.Ring[tail] = signal.adc;
filter=&h[order-tail]; |
Below are the changes I made, the results are worst. I should be doing something wrong!
v Code: |
#define order 64
#define ORDER 32
signed int16 h1[32]
void main()
{
for( i=0 ; i < 32; i++)
h1[i] = h[i]/64;
.................................
}
void DigitalFilter(void) {
signed int32 y = 0;
int i;
signed int16 * filter;
if (filterflag == 1) {
filterflag = 0;
signal.Ring[tail] = signal.adc;
filter=&h1[order-tail];
for (i = 0; i < order; i++) {
y += *(filter++) * signal.Ring[i];
}
snew_ADC = y/4096;
filter_data_flag = 1;
tail = (tail + 1) & (ORDER - 1);
}
} |
|
|
|
Ttelmah Guest
|
|
Posted: Wed Jul 02, 2008 7:47 am |
|
|
Coefficient array.
The entries in this are the 'terms' calculated for the filter being used.
The 'point' is that when you have the ring array 'full', your 32 ring elements will correspond exactly to the elements in the coefficient array. Then when you add one more element to the ring, if you worked from the start of the ring buffer, you would want to use 'coefficient -1' as the coefficient for the first entry in the ring buffer, then use coefficient 0, for the next element, and so on. By having the buffer double the size, with the contents duplicated, you can operate through the data array, without having to use any offsets, just shifting where you start in the coefficient array.
In what you show, You also need to have the table contents duplicated. So:
Code: |
signed int16 h [(order*2) + 1] =
{ 0x0000, 0x0002, 0x000B, 0x0024, 0x0059, 0x00B9,
0x0155, 0x023C, 0x0375, 0x0501, 0x06D1, 0x08CC,
0x0ACA, 0x0C9C, 0x0E14, 0x0F08, 0x0F5C, 0x0F08,
0x0E14, 0x0C9C, 0x0ACA, 0x08CC, 0x06D1, 0x0501,
0x0375, 0x023C, 0x0155, 0x00B9, 0x0059, 0x0024,
0x000B, 0x0002,
0x0000, 0x0002, 0x000B, 0x0024, 0x0059, 0x00B9,
0x0155, 0x023C, 0x0375, 0x0501, 0x06D1, 0x08CC,
0x0ACA, 0x0C9C, 0x0E14, 0x0F08, 0x0F5C, 0x0F08,
0x0E14, 0x0C9C, 0x0ACA, 0x08CC, 0x06D1, 0x0501,
0x0375, 0x023C, 0x0155, 0x00B9, 0x0059, 0x0024,
0x000B, 0x0002, 0x0000};
|
I think this is right, but you should step through it term by term, and check what is going on. Also, I'd make the division maths:
[/code]
for( i=0 ; i <=64; i++)
h1[i] = (h[i]+31)/64;
[/code]
which will effectively give 4/5 rounding on the division.
Best Wishes |
|
|
kein
Joined: 23 Jul 2007 Posts: 103
|
Hi Tlemah |
Posted: Thu Jul 03, 2008 4:39 am |
|
|
Sorry for bothering you much but it does not work. The signal is worst and severly distorted.
Below is the code:
Code: | signed int16 h [(order*2) + 1] =
{ 0x0000, 0x0002, 0x000B, 0x0024, 0x0059, 0x00B9,
0x0155, 0x023C, 0x0375, 0x0501, 0x06D1, 0x08CC,
0x0ACA, 0x0C9C, 0x0E14, 0x0F08, 0x0F5C, 0x0F08,
0x0E14, 0x0C9C, 0x0ACA, 0x08CC, 0x06D1, 0x0501,
0x0375, 0x023C, 0x0155, 0x00B9, 0x0059, 0x0024,
0x000B, 0x0002,
0x0000, 0x0002, 0x000B, 0x0024, 0x0059, 0x00B9,
0x0155, 0x023C, 0x0375, 0x0501, 0x06D1, 0x08CC,
0x0ACA, 0x0C9C, 0x0E14, 0x0F08, 0x0F5C, 0x0F08,
0x0E14, 0x0C9C, 0x0ACA, 0x08CC, 0x06D1, 0x0501,
0x0375, 0x023C, 0x0155, 0x00B9, 0x0059, 0x0024,
0x000B, 0x0002, 0x0000};
signed int16 h1[(32*2)+1];
signed int16 Ring[2*32+1];
void DigitalFilter1(void)
{
signed int32 y = 0;
int i;
signed int16 * filter;
if (filterflag == 1)
{
filterflag = 0;
signal.Ring[tail] = signal.new_ADC;
filter=&h1[64-tail];
for (i = 0; i < 32-1; i++)
{
y += *(filter++) * Ring[i];
}
snew_ADC = (signed int16) y/4096;
filter_data_flag = 1;
tail = (tail + 1) & (32 - 1);
}
}
void main()
{
for( i=0 ; i <= 64; i++)
h1[i] = (h[i]+31)/64;
.................................
} |
|
|
|
Guest
|
|
Posted: Thu Jul 03, 2008 4:53 am |
|
|
Have you scaled the incoming test numbers or real values as I said?. This will _only_ work, with a maximum +511 to -512 range. Otherwise back to overflowing the maths.
You really should be doing the testing I described earlier. Stick the data into a simulator, or ICD, and look for each pass, at what the numbers actually 'do'.
Best Wishes |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|