Page 1 of 1

Help Synchronizing and logging multiple channels

Posted: Mon Jun 13, 2022 4:36 pm
by dunderMethods
Hello, I am using a 1046_0 PhidgetBridge to read 3x Load Cells. My goal is to simply log data from each input at 120hz and write the data to CSV format. For my application it is important that there is a minimal time difference between samples in each row of sensor data - in other words I am trying to avoid a race condition or dropped samples among channels resulting in misalignment of samples in my log data.

Example: Suppose I build a weigh scale by using 3x load cells to support a platform upon which I place a test weight of 1kg. When I place the weight I expect to see the signal from all 3 inputs increase at roughly the same time. However, if there is a sample misalignment/dropped sample/race condition, there may be a significant delay in response from one or more channels.

I have been reading the Phidget22 API documentation and do not see this multi-channel synchronized logging topic covered. There is discussion of polling vs callbacks but they only talk about logging at the individual channel level and do not appear to provide tips on how to optimize/synchronize samples of multiple channels over time.

Here is my code in a Jypyter Notebook:
Phidget LC.zip
(15.83 KiB) Downloaded 679 times
The code I have written so far Creates, Addresses, Opens, and Attaches to three input channels. They all share the same callback function which appends the latest sample from each channel to a dict of corresponding {channel_number: [s1, s2, s3, sn...], }.

Code: Select all

def getCallback(data_dict):
    def onVoltageRatioChange(self, voltageRatio):
        data_dict[self.getChannel()].append(voltageRatio)
    return onVoltageRatioChange
When I am done logging I run a function that closes each channel. Then I count the number of samples recorded from each channel and they are usually 1-2 samples apart which makes sense due to the time difference between opening and closing each channel. However, sometimes they are off by quite a bit more, like 15 samples - this is concerning.

Next I find the channel with the lowest number of samples and drop the most recent samples from the other channels until all channels have the same number of samples, then write the data to CSV (rows = samples/time, cols = channels)

Code: Select all

# Store the number of samples from the channel with the lowest sample count
data_len = min([len(data[ch]) for ch in lc_channels])

# Drop excess samples from the other channels
res = [data[ch][:data_len] for ch in lc_channels]

# Make a Data Frame from the sensor data
df_data = pd.DataFrame(res).T

csv = df_data.to_csv()
But I have no confidence that my samples will always be properly synchronized.

What is the technical term for this kind of problem/solution and what is the best approach to ensure that each row of sensor samples are synchronized with each other?

Thank you!

Re: Help Synchronizing and logging multiple channels

Posted: Tue Jun 14, 2022 1:30 pm
by dunderMethods
I've come up with a solution but would still like to hear any thoughts.

My solution involves creating a dictionary with one key per channel, all values set to False.

Code: Select all

sample_state = {ch: False for ch in lc_channels}
Whenever a channel yields a new sample, my onVoltageRatioChange callback function checks whether a sample has already been recorded for that channel on this iteration; If it has, the sample is ignored, if not, it stores the new value and sets the corresponding sample_state value to True. When all sample_state values are True, then all sample_state values are reset to False so that we can record a new line of samples. Here is the new callback:

Code: Select all

def onVoltageRatioChange(self, voltageRatio):
    global sample_state
    channel = self.getChannel()
    if not sample_state[channel]:
        data[channel].append(voltageRatio)
        sample_state[channel] = True
    if all(sample_state.values()):
            sample_state = {ch: False for ch in lc_channels}
            print_values()
This seems to work well, although I may occasionally be dropping a sample here or there.

I did find that on some occasions you might happen to end logging before all samples have been written for a particular iteration. To handle this I added this block of code that waits until all channels contain the same number of samples. If it takes more than 5 attempts to rectify, it gives up.

Code: Select all

# This block waits until all channels contain the same number of samples
attempts = 0
while len(set([len(values) for values in data.values()])) != 1:
    time.sleep(.01)
    attempts += 1
    print(f'Attempt #{attempts}')
    if attempts < 5:
        break
Here is the updated Jupyter Notebook. It now supports logging, writing to CSV, and if you provide calibration coefficients it will continuously print the weight value for each input along with the sum.
Phidget LC.zip
(24.83 KiB) Downloaded 729 times