It has been a while since a published a post, and it feels good to be back. Currently, I have been knee deep in coding software for the micro-resolution ultrasonic imaging system. One of the main objectives that makes the system a valuable asset is the timing between the components. Specifically, the timing between data acquisition and stage movement. The data acquisition card in the computer has to consistently record data from the ultrasonic pulse sent through the component being inspected and the stage has to move while the data acquisition is occurring.

Now, I have been working on synchronizing the data acquisition and stage movement for at least six months. It has been a long journey that has been rewarding and frustrating at times. The frustrating times occurred when I make some progress in the code but then create a new problem for me to figure out. The rewarding time came when I was able to simultaneously capture data and saw the stage moving without any hindrance. But in the end the reward outweighs the frustrations. I learned a great amount about Python while coding this section of the software. From things like Global Interrupter Lock (GIL) and understanding the difference between CPU-bound and I/O-bound processes. Without further ado, let’s get into some of the things I learned while on the path to synchronizing the data acquisition and stage movement.

First, Python gets a bad rap because it is an interpreted language. This means that it is not compiled but each line is interpreted during runtime. This causes Python to be somewhat slower than the interpreted languages such as C/C++, C#, Go and Rust to name a few. Most people who are new to Python will not know the difference but as you dig deeper and deeper into the language and begin to code intermediate/advanced code that does multiple things at once, you will begin to see the difference. The Python interpreter has to go through each line of code and execute it before moving to the next line. In my case, I was trying to run two functions that I created in my program. One that controlled the data acquisition and the movement of the stage. Now, for my application I only needed to run the data acquisition function only while the stage was moving down. But this was a tall task because I had no idea of the bottleneck of the GIL. The GIL in short only allows for one thread to be executed at a time and in my case this was something I needed to manipulate to run two functions at the same time.

From my understanding one simply cannot turn off the GIL in Python but one can create separate threads that can be used later. In my case I imported the Thread module (from threading) in python. The Thread module allowed me to create two threads within python, where they laid in wait until I called them to be used simultaneously. Now, one may ask why did I not use the multiprocessing module in Python, and based on my research multiprocessing is ideal for CPU bound task such as performing calculations. My use case was more geared towards an I/O bound task where I was communicating between auxiliary components such as the data acquisition card and the three-axis stage to do work.

My algorithm for controlling the stage movement and data acquisition card was as follows: first, I created two threads one for moving the stage down and the other for acquiring data. Next, I started both threads and joined them together to run at the same time. Again, it took me about six months to come up with this algorithm and execute it correctly. I may even had this exact code programmed a number of times but deleted it due to another bug that I found in my overall program. Finally, I needed all of thread calling, starting and joining to occur within a for loop that iterated over the step size that was apart of a large raster scan function with my code.

As you can see this program was pretty involved and I had to reduce the use of the threading module down to two specific task to decrease the complexity of the overall program. Nonetheless it was very rewarding when I was able to get the software program to run without any errors and perform my program the way I desired. I am now able to successfully acquire data at a specified rate and operate the down function on my three-axis stage with ease. Hopefully I will continue to do more with Python.

As always I hope you found this post valuable. Thank you for your time!

-DB PhD