Numerical integration failure

I want to obtain new values ​​of a variable A. This is calculated from a variable X and another variable Z (see void loop function), which is obtained as the numerical integration of two variables, the independent variable X and the dependent variable f_T (T depends on turn of X). For this, I create a function called "next_X_Z". In it, I am obtaining new samples of X and T to obtain the integral at each point of X. The result is stored in the variable Z. To test that it works, I have defined an array with the values ​​of T (T_data) and another with those of X (X_data). So, to get T[0], T[1], X[0] and X[1] I do it by reading directly from the T_data and X_data arrays. However, the values ​​displayed by the serial port for T, X, and A remain zero. The only value that changes is Z little by little. Does anyone know why this happens? Is it correct to do the numerical integration in this way or is there a better one?

long LastSent;
const unsigned SendInterval = 1000;

int i;

float T_data[] = {7.01187E-06, 0.000529925, 0.000706801, 0.000710421, 0.000727124, 0.000756911, 0.000799782, 0.000855738, 0.000924777,
0.001006901, 0.001233484, 0.00106862, 0.000790697, 0.000600175, 0.000403409, 0.000283035, 0.000190234, 0.00012731, 8.29706E-05,
3.44802E-05, 0.000010053, 9.93814E-06, 7.01187E-06, 7.07994E-06, 1.01424E-05, 1.61991E-05, 2.52502E-05, 3.72957E-05, 5.23355E-05,
0.000111466, 0.000344161, 0.000647428, 0.000817139, 0.001023107, 0.001685989, 0.0022015, 0.002124776, 0.0018028, 0.001520281,
0.001125038, 0.000845196, 0.000609549, 0.000433978, 0.000299511, 0.000203097, 0.000134615, 8.78604E-05, 3.6183E-05, 1.07447E-05,
9.93814E-06, 7.01187E-06, 7.07994E-06, 1.01424E-05, 1.61991E-05, 2.52502E-05, 3.72957E-05, 5.23355E-05, 0.000111466, 0.000344161,
0.000647428, 0.000817139, 0.001023107, 0.001685989, 0.0022015, 0.002124776, 0.0018028, 0.001520281, 0.001125038, 0.000845196,
0.000609549, 0.000433978, 0.000299511, 0.000203097, 0.000134615, 8.78604E-05, 3.6183E-05, 1.07447E-05, 0.00001042};

float X_data[] = {0, 0.0000025, 0.000005, 91.54135, 183.082675, 274.624, 366.16525, 457.70675, 549.248, 640.78925, 936.65575,
1248.39025, 1467.511, 1717.63775, 2158.9925, 2641.0725, 3333.9025, 4226.61, 5475.095, 9539.6525, 29539.65, 29250, 25964.275,
22678.57, 19392.8575, 16107.1425, 12821.4275, 9535.715, 6250, 3750, 1250, 0, -136.215625, -317.8365, -681.07825, -900.45125,
-1144.28475, -1253.71925, -1369.29375, -1600.442, -1849.5615, -2186.94675, -2603.0925, -3153.4925, -3861.2025, -4801.1925,
-6051.615, -10090.8825, -27590.875, -29250, -25964.275, -22678.57, -19392.8575, -16107.1425, -12821.4275, -9535.715, -6250,
-3750, -1250, 0, 136.215625, 317.8365, 681.07825, 900.45125, 1144.28475, 1253.71925, 1369.29375, 1600.442, 1849.5615, 2186.94675,
2603.0925, 3153.4925, 3861.2025, 4801.1925, 6051.615, 10090.8825, 27590.875, 30000};

float T[2], X[2] = {0};
float Z = 0;


float next_X_Z () {

  int n_samples = 0;
  int i = 2;
  const float T1 = 7.01187E-06;
  float f_T[2];

  if (n_samples < 2) {
    for (int j = 0; j < 2; j++) {
      T[j] = T_data[j];
      X[j] = X_data[j];
    }
    n_samples = 2;
  } else {
    T[1] = T_data[i];
    X[1] = X_data[i];
    i++;
  }

  f_T[0] = (T[0] - T1) / T1;
  f_T[1] = (T[1] - T1) / T1;
  Z = Z + (X[1] - X[0]) / 2 * (f_T[1] + f_T[0]);
  T[0] = T[1];
  X[0] = X[1];

}


void setup() {

  Serial.begin(9600);

  LastSent = millis();
}


void loop() {
  float A;
  
  if((millis() - LastSent)>SendInterval){ 
    A = X[0] + Z;
    Serial.print("T = ");
    Serial.print(T[0]);
    Serial.print("X = ");
    Serial.println(X[0]);
    Serial.print("Z = ");
    Serial.print(Z);
    Serial.print("A = ");
    Serial.print(A);
    next_X_Z ();
  }
}

UPDATE
The output is displayed as follows:

1 Like

What is your theory about how this method of integration should work?

It is hard to see, given code completely lacking in comments, what is supposed to happen.

This condition in next_X_Z() is always true, so that only the two first values of the arrays are used.

1 Like

I can't see where LastSent is updated in the loop(). LastSent should, incidentally, be defined as unsigned long.

1 Like

AVR-duino floats are 32-bit good for 6 places in all cases and your data uses more.

2 Likes

Can you show your output?

It looks like you don't scan through your data, you just keep looking at the first positions.

You also need more digits of precision in your outputs. Maybe:

  if((millis() - LastSent)>SendInterval){ 
    LastSent = millis();
    A = X[0] + Z;
    Serial.print("T = ");
    Serial.print(T[0],6);
    Serial.print(" X = ");
    Serial.print(X[0],6);
    Serial.print(" Z = ");
    Serial.print(Z,6);
    Serial.print(" A = ");
    Serial.print(A,6);
    Serial.println();
    next_X_Z ();
  }
1 Like

The condition would be true only the first time we sampled from the arrays T and X. Right after that, we set it to 2 (the two samples we have already taken) and that part of the if loop should never run again, right?

I forgot to write it, sorry. I must have written it in the void loop like DaveX does

until the next call of the function where it is reset to zero again.

Please learn more about variable visibility, lifetime and persistence in C/C++.

1 Like

Although they use more, most of the data has decimals before reaching position 6 (so to speak). I imagine that the program should process them, even if it was truncating

Ok, now I understand what you say. Could it be a solution to declare the n_samples variable as a global variable instead of a local variable like I do? I thought that the initialization of a variable could only happen once...

You can make it a global or a static variable.

1 Like

Okay thank you very much. I will try to see how it goes

I just inserted a screenshot of the output, thanks

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.