Sampling frequency

@BigBobby: The ADC is not involved. The sensor in question is I2C and has a FIFO.

Which brings up another point: the sensor has its own clock, and is asynchronous to the Arduino, that is, it collects data at its own rate.

I don't think it is possible to know when a data point has been collected, especially if the FIFO is used and, in addition, it takes some time to transfer the data over the I2C bus.

Oh sheesh...that's what I get for starting to read on the 2nd page. Sorry about the responses that don't pertain to the OP's problem.

I appreciate all the help you all have given me. Helps out so much. so there is no way to exactly collect one full set of data each millisecond?

ssiltare:
the thing about the blink without delay example, the sampling frequency is not constant. It makes it blink after it has passed 1 sec and not exactly one second. does that make sense?

No, it doesn't make sense. the > is there because the count starts at zero, not one. You can count on millis (or at least micros) to keep time. The accuracy of the processor clock is another matter.

exactly collect one full set of data each millisecond?

Define "exactly".
Can you explain why you are so concerned about the timing?
If timing really is critical, then Arduino code, 8 bit processors and consumer grade sensors are not for you!

so there is no way to exactly collect one full set of data each millisecond?

Using millis()? No. Prove it to yourself. Write a sketch that does nothing but print the value of millis() to the Serial Monitor application, at 115200.

Watch for 5 minutes. You'll see that millis() does not always change by 1. And, if you think about why, it's understandable.

1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, ...

A second has 1000 milliseconds, but the Arduino doesn't operate in decimal. It operates in binary, so, mapping the counts of clock ticks to milliseconds involves rounding. At some point, the rounding removes almost a whole millisecond. A few clock ticks later, and millis() suddenly jumps by 2.

If you are using millis() to determine when to perform an action, you will miss a point when millis() jumps by 2.

Using micros() might be possible, but it will not be easy.

The jumps of millis...

unsigned long lastMillis;
unsigned long currentMillis;

void setup() {
  Serial.begin(115200);
  Serial.println(F("steps of millis"));
  lastMillis = millis();
}

void loop() {
  currentMillis = millis();
  unsigned long difference = currentMillis - lastMillis;
  if (difference > 1) {
    Serial.print(lastMillis);
    Serial.print(F(" - "));
    Serial.print(currentMillis);
    Serial.print(F(" - "));
    Serial.println(difference);
  }
  lastMillis = currentMillis;
}
steps of millis
41 - 43 - 2
84 - 86 - 2
126 - 128 - 2
169 - 171 - 2
212 - 214 - 2
254 - 256 - 2
297 - 299 - 2
340 - 342 - 2
382 - 384 - 2
425 - 427 - 2
468 - 470 - 2
510 - 512 - 2
553 - 555 - 2
596 - 598 - 2
638 - 640 - 2
681 - 683 - 2
724 - 726 - 2
766 - 768 - 2
809 - 811 - 2
852 - 854 - 2
894 - 896 - 2
937 - 939 - 2
980 - 982 - 2
1022 - 1024 - 2
1065 - 1067 - 2
1108 - 1110 - 2
1150 - 1152 - 2
1193 - 1195 - 2
1236 - 1238 - 2
1278 - 1280 - 2
1321 - 1323 - 2
1364 - 1366 - 2
1406 - 1408 - 2
1449 - 1451 - 2
1492 - 1494 - 2
1534 - 1536 - 2

The jumps of millis...

Some of that can be attributed to the fact the Serial.print() blocks when the buffer is full. While it is convenient to see that m - n = x, if you know two values you can calculate the third, so you could print less data, to reduce the amount of blocking.

ssiltare:
the thing about the blink without delay example, the sampling frequency is not constant. It makes it blink after it has passed 1 sec and not exactly one second. does that make sense?

This is the code they have in that example:
if (currentMillis - previousMillis >= interval)
as you can see, they have the >= to indicate that they are just seeing if it has passed the one second mark and not exactly one second.

What I am trying to achieve is to collect data exactly 1000 times in one second. If I did the same thing I did with this example, I would not be able to achieve it because the additional milliseconds will eventually add up.
Hope this makes sense. I feel like I did a bad job explaining what was going through my head

To prevent an accumulating error in timing (time slippage due to the time it takes to go through the loop), use

previousMillis += interval;

EDIT:

Since SD cards have unpredictable write delays, if this delay is 2+ times greater than "interval", the if "(currentMillis - previousMillis >= interval)" condition will trigger more than once consecutively until previousMillis catches up to currentMillis.

PaulS:
Some of that can be attributed to the fact the Serial.print() blocks when the buffer is full. While it is convenient to see that m - n = x, if you know two values you can calculate the third, so you could print less data, to reduce the amount of blocking.

No, I disagree.

With a baudrate of 115200 I can transfer 11520 chars a second.

The steps happen all 40mS, 25 times a second.

That gives a maximum of 460 chars per event, I'm printing much less.

No, I disagree.

I should have said "Some of that might...", but you are right. The step by two interval of 40 (roughly 1000/24 (1024 - 1000)) is about what I would have expected had I had more caffeine before typing.