-
First polarimetric images!
05/24/2024 at 11:49 • 0 commentsQuick update... This is the first image I got from the polarimetric camera! Note the difference in luminance of the cars in the different images at the top. This is what is used to calculate the angle and degree of polarization. For the image with the cars, the angle of polarization has been converted to YUV, where it is rotated 3 times around the color circle, in order to get more detail.
-
Finally, some progress
05/23/2024 at 21:51 • 0 commentsI've shelved this project so many times I thought it would never get anywhere. The main issue has always been that I wanted to keep it simple, but at the same time the math required synchronization between the polarization filter that is spinning and the camera that has to take pictures at very specific intervals.
So what do you do when you don't want to modify a camera to use an external trigger and keep the build simple but the math doesn't allow you? You change the math!
I posted a quick run through of the math in the project details, it probably needs some further explanation but I'll do that in the form of a Python/Jupyter notebook. In the end, I removed the requirement for synchronization which allows me to use a simple motor and a freerunning camera. I have the polarization filter trigger a button every 180 degrees, which is fed directly into the raspberry in order to store the times of these triggers. I then interpolate to get the predicted angles for each frame and calculate the polarization parameters from there.
It just got dark when I finished the build, so the first images will have to wait for tomorrow. Fingers crossed
-
Ok ok...
10/20/2022 at 02:22 • 0 commentsi scrapped this project a few days back, I’m sometimes too perfectionistic to even begin a project. So instead I’m just gonna build it out of lego, f*ck it 😅
-
TMC5160 motion controller mode
02/15/2021 at 18:02 • 0 commentsThe Trinamic TMC5160 has a motion controller mode which is very cool. It allows you to set things like acceleration parameters, destination, speed via SPI, instead of using the STEP / DIR pins.
I have been using the TMC5160 SilentStepStick as a board for many applications but by default it isn't configured as a motion controller and it doesn't expose a pin to change it.
You can cut a trace to put it in motion controller mode, and even though the documentation says it needs to be connected to ground, I've never actually had to do so (your results may vary).
made with the polarisation microscope
Check out the datasheet on this page and check page 34, register IOIN. This register can be read out to see if the cut was successful, Bit 6 should be 0 now and the motion controller should be active.
In order to test it out I wrote a simple program in C that runs on my raspberry pi. It initializes the TMC and accellerates until a maximum velocity is reached. After pressing enter it will decelerate and stop.
#include <stdint.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <sys/ioctl.h> #include <linux/types.h> #include <linux/spi/spidev.h> static const char* s_spidev = "/dev/spidev0.0"; static uint8_t s_mode = 3; static uint8_t s_bits = 8; static uint16_t s_delay = 0; static uint32_t s_speed = 1000000; static int transfer(int fd, uint8_t reg, uint32_t val) { printf("Transfer\n"); uint8_t tx[5] = { reg, (val>>24) & 0xFF, (val>>16) & 0xFF, (val>> 8) & 0xFF, (val>> 0) & 0xFF }; uint8_t rx[5] = {0}; struct spi_ioc_transfer tr = { .tx_buf = (unsigned long) tx, .rx_buf = (unsigned long) rx, .len = 5, .delay_usecs = s_delay, .speed_hz = 0, .bits_per_word = 0, }; int rc = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); if (rc == 1) return rc; printf("%02X %02X%02X%02X%02X -> %02X %02x%02x%02x%02x\n", tx[0], tx[1], tx[2], tx[3], tx[4], rx[0], rx[1], rx[2], rx[3], rx[4]); return 0; } int main() { int rc; int fd = open(s_spidev, O_RDWR); if (fd < 0) { perror("SPI OPEN"); return -1; } rc = ioctl(fd, SPI_IOC_WR_MODE, &s_mode); if (rc < 0) { perror("SPI WR MODE"); return -1; } rc = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &s_bits); if (rc < 0) { perror("SPI BIT PER WORD"); return -1; } rc = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &s_speed); if (rc < 0) { perror("SPI MAX SPEED HZ"); return -1; } transfer(fd, 0x01, 0x00000000);// READ GSTAT transfer(fd, 0x04, 0x00000000);// READ IOIN transfer(fd, 0x8b, 0x000000EE);// WRITE GLOBAL SCALER: 0xEE transfer(fd, 0x90, 0x000003E1);// WRITE IHOLD:0x01 IRUN:0x1F transfer(fd, 0x93, 0x00014000);// WRITE STEALTHCHOP UPPER VELOCITY transfer(fd, 0x94, 0x00018000);// WRITE COOLSTEP LOWER VELOCITY transfer(fd, 0x95, 0x0000002F);// WRITE COOLSTEP UPPER VELOCITY transfer(fd, 0xA0, 0x00000001);// WRITE RAMPMODE: 0 = POSITION MODE / 1 = VELOCITY MODE transfer(fd, 0xA3, 0x00000500);// WRITE START VELOCITY transfer(fd, 0xA4, 0x00000600);// WRITE A1 ACCELLERATION (FROM VSTART TO V1 IN POSITION MODE) transfer(fd, 0xA5, 0x00010000);// WRITE V1 VELOCITY (POSITION MODE) transfer(fd, 0xA6, 0x00000300);// WRITE AMAX ACCELLERATION (FROM V1 TO VMAX IN POSITION MODE, GENERAL IN VELOCITY MODE) transfer(fd, 0xA8, 0x00000F00);// WRITE DMAX DECELLERATION (FROM VMAX TO V1 IN POSITION MODE, GENERAL IN VELOCITY MODE) transfer(fd, 0xAA, 0x00000D00);// WRITE D1 DECELLERATION (FROM V1 TO VSTOP IN POSITION MODE) transfer(fd, 0xAB, 0x00000600);// WRITE VSTOP VELOCITY (POSITION MODE) transfer(fd, 0xAC, 0x00000800);// WRITE TZEROWAIT transfer(fd, 0xB3, 0x00000000);// WRITE DCSTEP = OFF transfer(fd, 0xB4, 0x00000000);// WRITE SWITCH MODE NO SWITCH STUFF transfer(fd, 0xEE, 0x0000001C);// WRITE DC CONTROL, GUESS transfer(fd, 0xEC, 0x10410155);// WRITE CHOPCONF transfer(fd, 0xA1, 0x00000000);// WRITE ACTUAL POSITION transfer(fd, 0xA7, 0x00060000);// WRITE VMAX / TARGET VELOCITY transfer(fd, 0xAD, 0x00400000);// WRITE TARGET POSITION (POSITION MODE) transfer(fd, 0x00, 0x00000000);// WRITE GCONF, diag0 = interrupt, diag1 = position compare printf("Waiting... "); fgetc(stdin); transfer(fd, 0xA7, 0x00000000);// WRITE VMAX / TARGET VELOCITY transfer(fd, 0xAD, 0x00000000);// WRITE TARGET POSITION (POSITION MODE) printf("Done.\n"); close(fd); return 0; }
Next up: setting X_COMPARE to get a sync signal, and updating it in an interrupt handler.
-
The idea
02/15/2021 at 14:14 • 0 commentsThis is what I'm thinking:
- Get a ball bearing with an inner diameter >= the diameter of the lens.
- 3d print a gear with an inner diameter == outer diameter of the ball bearing
- Attach a linear polarization filter to the gear so it can rotate in front of the lens.
- Attach a stepper motor that drives the gear
- Use a TMC5160 silentstepstick, cut a trace and put it into motion controller mode
- Have the TMC diag pin toggle on "position reached" and have it control the shutter
- Update the position in the TMC to trigger on the next angle
- Add the images to a rotating buffer of size 4
- Use the 4 most recent images as textures to a GLES shader that calculates polarization angle.