Some more realistic OpenFOAM runs

As a first test, I am trying to reproduce the graphs of coefficient of lift \(C_l\) and coefficient of drag \(C_d\) as functions of angle of attack \(\alpha\) from http://airfoiltools.com/airfoil/details?airfoil=naca2408-il. The airfoil is 1m long, the airspeed is 10m/s, and the kinematic viscosity \(\nu\) is 1E-5 for a Reynolds number of 1E6. \(C_l\) and \(C_d\) are measured for \(\alpha = \{-5,0,5,10,15,20\}\). I am unclear on the reference length and area in \(C_l\) and \(C_d\), but that will be obvious as a constant factor between the graphs.

On the recommendation of a friend (I do not want to get stuck down any rabbit holes) these use the SST k-omega turbulence model. The constants for incoming turbulence intensity are taken from: k-Omega-SST-DES-Report. At high angles of attack the airfoil sheds vortices:

And the plot of \(C_l\) and \(C_d\) vs \(\alpha\):

Error bars are given from the standard deviation of the forces. The vortex shedding causes periodic changes in each coefficient. I am bothered that \(C_l\) doesn’t turn over at \(\alpha\approx 15^{\circ}\) as for the airfoil tools database, and am now trying to figure out what causes the difference.

OpenFOAM Airfoils

I’ve been playing with OpenFOAM recently and ended up writing my own mesher. It’s been educational to work with a code which uses unstructured meshes for once. See https://github.com/garthwhelan/openfoam-mesher-stuff for details. So far all it does is generate hexahedral meshes with Cartesian coordinates, and merge boundary faces/points if two boundary patches share the same coordinate. It’s pretty straightforward to build more complicated meshes by deforming and combining Cartesian ones. For example, C meshes for airfoils can be made by deforming a rectangular Cartesian mesh so that the right edge surrounds an airfoil and then combining the interface behind the airfoil (automatically removing the duplicate faces and faces that are now internal). O meshes would involve doing the same thing, but using the entire right edge around the airfoil and merging what were the top and bottom edges.

Ultimately, I would like to build an RC plane and be able to say some of the design was inspired by CFD simulations, but this is a tall order. I think I will make sure that I can match the information on www.airfoiltools.com for \(C_l\) and \(C_d\) for a named airfoil and then use OpenFOAM to motivate the design of the control structures.

After going through some tutorials, I tried a (very nonphysical) run with an airfoil. The graph below was generated with icoFoam using a 1m airfoil at 1m/s, and with a realistic air viscosity. The boundary conditions are probably something nonphysical. Since then I’ve moved to using the freestream boundary conditions, pisoFoam, and RANS.

Paraview is actually really nice

Biofeedback Smartwatch Update

I’ve been busy recently, with finishing my doctorate and a month long trip. However, I now have a fully wireless biofeedback smartwatch prototype.

The picture is not fully assembled, but everything works other than the lack of a power switch, usb plug, and IC for charging the Li+ battery on the back. I’ve switched from using a naked nRF52810 IC to a TAIYO YUDEN module, it is more expensive, but I was having a lot of trouble getting antennas to work. The wires to the right are for programming with SWD, and the pair of magnet wires soldered to the board itself are for power so I don’t rundown the battery in testing. To the right of the module are two holes for debugging with UART; it’s been really helpful to use that and the logic analyzer to test things during development.

On the finger strap are two electrodes for the GSR voltage bridge, and the MAX30102 SPO2 sensor, which is soldered onto a PCB out of view.

I’m using the following program to activate the ADC and SPO2 sensor and broadcast data over bluetooth. I’m using the nordic UART profile, which contains two characteristics, a read and a write one. I’ve had to learn a lot more about bluetooth than I wanted to for this project.

https://github.com/garthwhelan/NRF-smartwatch/tree/master

You can see in the above picture I can stream to my laptop, which live plots the data with matplotlib. I’ve had issues with plot update speeds… it sounds like pyqtgraph is a lot faster but not what I want. I’m probably going to try gnuplot if this is still an issue.

On the plotting side, it’s the same as usual, except with the source of data being:


subprocess.Popen("sudo gatttool -i hci0 -t random -b (nrf mac address) --char-write-req -a 0x10 -n 0100 --listen",shell=True,stdout=subprocess.PIPE)

Which writes 1 (0100 reverse endian) to the handle of the characteristic to enable notifications (0x10). Again, there’s a better way to do this with the actual gatt libraries.

In the end I’m pretty happy with everyting. The GSR data is a lot better since switching to a TI adc (ads1246), and the setup is not that encumbering overall. I plan on working on more data analysis for biofeedback (e.g. getting actual SPO2 values, heart rate counters,…) before doing anything with a phone app.

Maxwell’s Equations

<long unrelated intro>

I want to write a general Maxwell solver.

Energy and momentum are carried by electromagnetic fields through the Poynting vector and the energy density:

\(\vec{p}_{em}=\vec{S}/c^{2}=(1/\mu_{0})\vec{E}\times\vec{B}\)

\(u_{em}= (\epsilon_{0}/2)E^{2}+(1/2\mu_{0})B^{2}\)

They follow the following conservation laws:

\(\frac{\partial u_{em}}{\partial t}+\nabla\cdot\vec{S}+\vec{J}\cdot\vec{E}=0\)

\(\frac{\partial \vec{p}_{em}}{\partial t}-\nabla\cdot\sigma+\rho\vec{E}+\vec{J}\times\vec{B}=0\)

I was thinking it would be neat to have a finite volume method solver for Maxwell’s equations in terms of these conservation laws, but this is a flawed conception as the energy equations aren’t closed and IMO any way to close them ruins the neatness. So instead I decided to take the exact opposite extreme and just play around with a finite difference scheme.

The electric and magnetic fields can be written in terms of the potentials \(\phi\) and \(\vec{A}\). In the Lorenz gauge, Maxwell’s equations are:

\(\nabla^{2}\phi-1/c^{2}\frac{\partial^{2} \phi}{\partial t^{2}}=-\rho/\epsilon_{0}\)

\(\nabla^{2}\vec{A}-1/c^{2}\frac{\partial^{2} \vec{A}}{\partial t^{2}}=-\mu_{0}\vec{J}\)

Which is the inhomogenous wave equation four times. I found a stencil for the 3D Laplacian in [R.C. O’Reilly and J.M. Beck Int. J. Numer. Meth. Engng 2006; 00-1-16] which also conducts stability analysis for solving the wave equation and heat equations using a forward Euler scheme.


\(\nabla^{2}\phi\approx\frac{3}{13h^{2}}\Big(\sum_{j\in\mathcal{N_{f}}} \phi_{j}+\frac{1}{2} \sum_{j\in\mathcal{N}_{e}} \phi_{j}+\frac{1}{3} \sum_{j\in\mathcal{N}_{c}} \phi_{j} – \frac{44}{3}\phi_{i}\Big)\)

Just as an example in C++:


#define n_points 512
#define tot_points n_points*n_points*n_points
#define dx 0.1
#define dt 0.01
#define cec -(44.0/3.0)*(3.0/(13.0*dx*dx)) //center,face,..
#define fac (3.0/(13.0*dx*dx))
#define edc 0.5*(3.0/(13.0*dx*dx))
#define coc (1.0/3.0)*(3.0/(13.0*dx*dx))
#define forxyz(X) for(int x = 1; x &lt; n_points-1; x++) {for(int y = 1; y &lt; n_points-1; y++) {for(int z = 1; z &lt; n_points-1; z++) { X }}}

int c(int x, int y, int z) {
  return n_points*n_points*x+n_points*y+z;
}

void laplacian(std::array&lt;float,tot_points> *arrout, std::array&lt;float,tot_points> *arrin) {
  (*arrout) = {};//1 sec out of 15                                                                                                                                                            
  //9 3 slices                                                                                                                                                                                

  float temp;

   for(int i = -1; i &lt; 2; i++) {
     for(int j = -1; j &lt; 2; j++) {
       int c1;
       int c2;

       if((i==0)and(j==0)) {//face center face                                                                                                                                                
         forxyz(
                c1=c(x+i,y+j,z);
                c2=c(x,y,z);
                (*arrout)[c2]+=fac*(*arrin)[c1-1]+cec*(*arrin)[c1]+fac*(*arrin)[c1+1];
                );

       } else if((abs(i)==1)and(abs(j)==1)) { //corner edge corner                                                                                                                            
         forxyz(
                c1=c(x+i,y+j,z);
                c2=c(x,y,z);
                (*arrout)[c2]+=coc*(*arrin)[c1-1]+edc*(*arrin)[c1]+coc*(*arrin)[c1+1];
                );

       } else {//edge face edge                                                                                                                                                                
   forxyz(
                c1=c(x+i,y+j,z);
    c2=c(x,y,z);
    (*arrout)[c2]+=edc*(*arrin)[c1-1]+fac*(*arrin)[c1]+edc*(*arrin)[c1+1];
    );
       }
     }
   }
  return;
}

To be updated with CUDA, method of relaxation, …

Biofeedback Smartwatch

<Informal because I have no energy, also partially for my own reference later. I’ve been busy when not blogging.>

The PCBs I designed for the Bluetooth biofeedback thing arrived a few days ago. From Oshpark, these were ~10usd total, with free shipping and took ~2 weeks. I’ve never done any QFN soldering (the leads are entirely under the IC) and I didn’t get a solderpaste stencil. Big mistake. Spent a lot of painstaking time with an Exacto knife, sewing needle and tweezers. Also tried using a hot air station for the first time, but a pan on the stove ended up working better.


Small one is meant to be worn on a strap on the finger, the large one is meant to be worn on a strap on the wrist.
SWD pins connected, and it can flash!!!!!!!

Anyway, I got 2 assembled and one of them works with my st-link and openocd. Unfortunately I lost all of my rf inductors during assembly (also the only parts I lost), so I can’t test the bluetooth. I’m using the nRF52810, have downloaded the SDK kit from https://www.nordicsemi.com/Software-and-Tools/Software/nRF5-SDK/Download#infotabs, and can get the examples to compile with the compiler from https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm. I’m mentioning where I got the compiler because I spent a few hours trying to get the gentoo package crossdev working before just downloading binaries.

openocd -f ~/openocd/tcl/interface/stlink.cfg -f ~/openocd/tcl/target/nrf52.cfg -c “program nrf52810_xxaa.bin verify reset exit”

Because it will take time anyway for new inductors to arrive, I did a redesign and ordered more boards. I hope to actually be able to wear the next generation around. I’ll put in some filler posts on computing before then.

For those who stuck with it

Optoisolators

After all the frustrations with the optoisolators last time I designed a board for the ACSL-6400, which is a quad optoisolator, and ordered some. Today they came so I assembled one and tested putting the CS, MOSI and SCLK signals through it. This one has 4 signals in the same direction, but there is also a 3 out 1 in version specifically for SPI. I didn’t use this because the ADS1299 has both DRDY and the MISO signals I want to connect.

Custom breakout board for the ACSL-6400
When looked at with the signal analyzer, everything is right (inversion is done in MCU software).

Everything appears to work. I have a ton of extra boards, so if anyone is interested in buying assembled quad optoisolator breakout boards, please contact me.

Sorting LaTeX references

I don’t use .bib files and just populate bibliographies like:


\begin{thebibliography}{99}

\bibitem{Romanelli1989}
  F.~Romanelli, Phys. Fluids B \textbf{1}, 1018 (1989).

%.... (other citations)

One issue is that this can be out of order compared to the order of /cite{} in the text. The following python script goes through the citations in the text and prints the appropriately ordered bibliography. Replacing the bibliography must be done by hand, but it’s safer that way.


f=open("article.tex","rb")

cites=[]
A=f.tell()

for line in f.readlines():
  while(True):
    a=line.find(r"\cite{")
    if(a==-1):
      break
    else:
      line=line[a+5:]
      b=line.find("}")
      blah=line[1:b]
      if(blah!=""):
        B=blah.split(",")
        for e in B:
          if e not in cites:
            cites.append(e)
      line=line[b:]

refs=[]

f.seek(A)
last_line=""
for line in f.readlines():
  if(last_line[:5]==r"\bibi"):
    refs.append((last_line,line))
  last_line=line

for i in cites:
  for j in refs:
    end=j[0].find("}")                                                                                                                                        
    if(i==j[0][9:end]):
      print j[0][:-1]
      print j[1]
      continue

BlueNRG2

Very much in the spirit of the 90-90 rule, this took much longer than I assumed.

So my goal is to put the galvinic skin response, hand temperature and SPO2 sensors onto a ring connected to a bluetooth ‘watch’, and have some phone app which can stream the data. To play around with a microcontroller which has bluetooth, I got the STEVAL-IDB008V2 dev board, which has a BlueNRG2 chip. Because I’ve only played with the Nucleos so far, I was overoptimistic about linux compatibility. On the plus side, my setup for development has gotten much more comfortable-I’m back to using emacs and makefiles, and I’ve learned how to use st-util to flash and debug and I’ve also gotten my first experience using logic analyzers with sigrok and pulseview.

The issue which held me up was actually getting the programs onto the chip. While the dev board has a JTAG header, the default application disables those pins so it cannot be programmed in that way. The alternative is to flash it with the UART bootloader, which requires the chip be booted into a specific mode and a different pair of pins.This board has a USB, but unlike the nucleos, it is not connected to a ST-Link. All of the software for flashing the chip was windows-only, and I had a lot of trouble getting that working.

Anyway, after much toil I managed to work out a way to reliably flash the chip using the windows software on a VM, but at this point I had already mostly written a bootloader using a nucleo as an intermediary. A warning on the very slim odds a reader will be stuck on the bootloader like I was. As far as I can tell, the bootloader will respond with a positive acknowledge even to invalid addresses, only to fail when receiving the data to flash. To work the last bugs out of the bootloader I was working on, I compared the output of the logic analyzer for each of them flashing the chip with the same data. This revealed that the address bytes I was using were off allowing me to fix the program.

~10usd logic analyzer that’s already worth it
Address bytes and acknowledge in Pulseview
Full writing in pulseview. First is the erase command, followed by blocks of 256 bytes.

EEG 4

I’ve been setting up the optoisolators (H11L1) for SPI. Because they invert the signal, you have to switch the clock polarity, polarity for CS, and take the complement of the data (A->0xFF-A). I used magnet wire for some practice, because it’s supposed to be good for fine SMD stuff.

Attempt 1
Oh gosh spaghetti
Test

I still haven’t programmed the ads1299 over this, there’s some issues I need to work out. All I can say is….

;iern winpoi4ny oi24nPON R;L N;LTJNA;LN IN OPQ3NROHN KL;NF842 H-082948ONX:lhnr0P28 H9-2408NI;NL;n:OJN59-8N8203VCH-4MWV88hirlfhlckh4ovrnmldrjksvjilhvnrotv92534vn hrlnnh-v2hnhihp98H7(Gto4hNP

Photoplethysmogram 3

I’ve gotten all of the sensors working at once now. One of the changes is that I’m now using a ’24’ bit differential external ADC. I’m using the NAU7802, which communicates with I2C and runs at a very slow 10-320 SPS. This is plenty fast and seems to be high enough resolution to measure GSR without any external amplifier. The ADC also has an internal PGA which hasn’t been nessesary so far.

More restricting than it could be

The spikes in graphs below for red and IR correspond to heart rate.

I’m currently using the MAX30102’s temperature sensor (which is meant for the LEDs) to measure finger temp. I will have to decide whether this is good enough.

I made several attempts dead-bug soldering the NAU7802. What worked best was to glue to face down on a peice of cardstock and then just solder to the prongs from there.