microPAM utilities#

micoPAM console#

The microPAM software includes a basic menu that can interacted with from the PC, using either a terminal program or the following Python and R scripts.

Python script#

import serial
import serial.tools.list_ports

from datetime import datetime
def PC_time():
    date_time=datetime.now()
    print("Date and time is:", date_time)

def microPAM_showParameters():
    s=serial.tools.list_ports.comports(True)
    print(s[0].device)
    with serial.Serial(s[0].device) as ser:
        ser.reset_input_buffer
        ser.write(b'?p\r')
        for ii in range(5):
            print(ser.readline().decode('utf-8').rstrip())
    print()

def microPAM_syncTime():
    s=serial.tools.list_ports.comports(True)

    date_time=datetime.now()
    dd=date_time.strftime('!d%Y-%m-%d\r')
    tt=date_time.strftime('!t%H:%M:%S\r')

    with serial.Serial(s[0].device) as ser:
        ser.write(dd.encode())
        str=ser.readline().decode('utf-8').rstrip()
        print(str)

    with serial.Serial(s[0].device) as ser:
        ser.write(tt.encode())
        print(ser.readline().decode('utf-8').rstrip())

def microPAM_setParameter(token,value):
    s=serial.tools.list_ports.comports(True)
    with serial.Serial(s[0].device) as ser:
        data="!"+token+str(value)+"\r"
        ser.write(data.encode())
        print(ser.readline().decode('utf-8').rstrip())

def microPAM_readLines(value):
    s=serial.tools.list_ports.comports(True)
    with serial.Serial(s[0].device) as ser:
        for ii in range(value):
            print(ser.readline().decode('utf-8').rstrip())

def microPAM_command(value):
    s=serial.tools.list_ports.comports(True)
    with serial.Serial(s[0].device) as ser:
        ser.write(value.encode())

Example of python based setup of microPAM#

PC_time()
microPAM_connected = 0      # change to 1 if microPAM is connected to USB
if microPAM_connected:
    microPAM_showParameters()
    microPAM_syncTime()
#    microPAM_setParameter("f",44100)
#    microPAM_setParameter("s",12)
#    microPAM_setParameter("c",1)
    microPAM_showParameters()
Date and time is: 2024-03-18 12:39:32.923928

Example of python based monitoring of microPAM#

if microPAM_connected:
    microPAM_command("m")   # switch on monitor
    microPAM_readLines(10)
    microPAM_command("m")   # switch off monitor
if microPAM_connected:
    microPAM_command("s")   # start archiving
    microPAM_readLines(5)
    microPAM_command("e")   # stop archiving

microPAM GUI#

# %%writefile microPAM_GUI.py # uncomment to save cell to file
# micoPAM GUI
# use this cell to test and develop GUI
# to compile "pyinstaller microPAM_GUI.py"
# will generate "dist/micoPAM_GUI/microPAM_GUI.exe"
# and  "dist/micoPAM_GUI/_internal" with all required pyd/dll files
#
import tkinter as tk
import time
import serial
import serial.tools.list_ports

class Window(tk.Frame):

    def mEntry(self,txt,x,y,w):
        label = tk.Label(text=txt,font=("Helvetica", 18))
        label.place(x=x-80,y=y)
        edit = tk.Entry(text="", fg="Black", font=("Helvetica", 18),width=w)
        edit.place(x=x,y=y)
        return edit

    def mUpdate(self,ser,edit,txt):
        ser.write(txt.encode())
        txt=ser.readline().decode('utf-8').rstrip()
        ip=txt.find("=")
        edit.delete(0,tk.END)
        edit.insert(0,txt[ip+2:])

    def __init__(self, master=None):
        tk.Frame.__init__(self, master)        
        self.master = master

        # widget can take all window
        self.pack(fill=tk.BOTH, expand=1)

        label1 = tk.Label(text="PC:",font=("Helvetica", 18))
        label1.place(x=100,y=10)
        self.pcClocklabel = tk.Label(text="", fg="Red", font=("Helvetica", 18))
        self.pcClocklabel.place(x=160,y=10)
        self.update_clock()

        label2 = tk.Label(text="MCU:",font=("Helvetica", 18))
        label2.place(x=80,y=60)
        self.mcuClocklabel = tk.Label(text="", fg="Black", font=("Helvetica", 18))
        self.mcuClocklabel.place(x=160,y=60)

        xo=120
        yo=110
        ii=0
        self.fsampedit = self.mEntry("fsamp:",xo,yo+ii*40,10); ii+=1
        self.procedit  = self.mEntry("proc:",xo,yo+ii*40,5); ii+=1
        self.shiftedit = self.mEntry("shift:",xo,yo+ii*40,5); ii+=1
        self.againedit = self.mEntry("again:",xo,yo+ii*40,5); ii+=1

        xo=400
        yo=110
        ii=0
        self.t_acqedit = self.mEntry("t_acq:",xo,yo+ii*40,5); ii+=1
        self.t_onedit  = self.mEntry("t_on:",xo,yo+ii*40,5); ii+=1
        self.t_repedit = self.mEntry("t_rep:",xo,yo+ii*40,5); ii+=1
        self.t_1edit = self.mEntry("t_1:",xo,yo+ii*40,5); ii+=1
        self.t_2edit = self.mEntry("t_2:",xo,yo+ii*40,5); ii+=1
        self.t_3edit = self.mEntry("t_3:",xo,yo+ii*40,5); ii+=1
        self.t_4edit = self.mEntry("t_4:",xo,yo+ii*40,5); ii+=1

        # create button, link it to clickExitButton()
        exitButton = tk.Button(self, text="Exit", command=self.clickExitButton, font=("Helvetica", 18))
        exitButton.place(x=540, y=10)
        loadButton = tk.Button(self, text="Load", command=self.clickLoadButton, font=("Helvetica", 18))
        loadButton.place(x=540, y=80)
        saveButton = tk.Button(self, text="Save", command=self.clickSaveButton, font=("Helvetica", 18))
        saveButton.place(x=540, y=150)
        syncButton = tk.Button(self, text="Sync", command=self.clickSyncButton, font=("Helvetica", 18))
        syncButton.place(x=540, y=220)

    def clickExitButton(self):
        self.master.destroy() 

    def clickLoadButton(self):
        s=serial.tools.list_ports.comports(True)
        if len(s)==0:
            return
        with serial.Serial(s[0].device) as ser:
            ser.reset_input_buffer
            ser.write(b'e\r')
            txt=ser.readline().decode('utf-8').rstrip()
            ser.write(b':m0\r')
            txt=ser.readline().decode('utf-8').rstrip()
            ser.reset_input_buffer
            #
            ser.write(b'?d\r')
            txt1=ser.readline().decode('utf-8').rstrip()
            ser.write(b'?t\r')
            txt2=ser.readline().decode('utf-8').rstrip()
            ip1=txt1.find("=")
            ip2=txt2.find("=")
            self.mcuClocklabel.configure(text=txt1[ip1+2:]+txt2[ip2+1:])
            #
            self.mUpdate(ser,self.t_acqedit,"?a\r")
            self.mUpdate(ser,self.t_onedit, "?o\r")
            self.mUpdate(ser,self.t_repedit,"?r\r")
            #
            self.mUpdate(ser,self.t_1edit,"?1\r")
            self.mUpdate(ser,self.t_2edit,"?2\r")
            self.mUpdate(ser,self.t_3edit,"?3\r")
            self.mUpdate(ser,self.t_4edit,"?4\r")
            #
            self.mUpdate(ser,self.fsampedit,"?f\r")
            self.mUpdate(ser,self.procedit, "?c\r")
            self.mUpdate(ser,self.shiftedit,"?s\r")
            self.mUpdate(ser,self.againedit,"?g\r")

    def clickSaveButton(self):
        s=serial.tools.list_ports.comports(True)
        with serial.Serial(s[0].device) as ser:
            data="!a"+self.t_acqedit.get()+"\r"
            ser.write(data.encode())
            data="!o"+self.t_onedit.get()+"\r"
            ser.write(data.encode())
            data="!r"+self.t_repedit.get()+"\r"
            ser.write(data.encode())
            data="!1"+self.t_1edit.get()+"\r"
            ser.write(data.encode())
            data="!2"+self.t_2edit.get()+"\r"
            ser.write(data.encode())
            data="!3"+self.t_3edit.get()+"\r"
            ser.write(data.encode())
            data="!4"+self.t_4edit.get()+"\r"
            ser.write(data.encode())

            data="!f"+self.fsampedit.get()+"\r"
            ser.write(data.encode())
            data="!c"+self.procedit.get()+"\r"
            ser.write(data.encode())
            data="!s"+self.shiftedit.get()+"\r"
            ser.write(data.encode())
            data="!g"+self.againedit.get()+"\r"
            ser.write(data.encode())

    def clickSyncButton(self):
        s=serial.tools.list_ports.comports(True)
        date_time=datetime.now()
        dd=date_time.strftime('!d%Y-%m-%d\r')
        tt=date_time.strftime('!t%H:%M:%S\r')

        with serial.Serial(s[0].device) as ser:
            ser.write(dd.encode())
            ser.write(tt.encode())

    def update_clock(self):
        now = time.strftime("%Y-%m-%d %H:%M:%S")
        self.pcClocklabel.configure(text=now)
        self.after(1000, self.update_clock)

root = tk.Tk()
app = Window(root)
root.wm_title("MicroPAM V3 (WMXZ)")
root.geometry("640x400")


microPAM_connected = 0      # change to 1 if microPAM is connected to USB
if microPAM_connected:
    root.after(1000, app.update_clock)
    root.mainloop() 

R scripts#

The following functions allow the interaction with the microPAM using R

library('serial')

listParameters <- function()
{
	ser <- listPorts()
	com <- serialConnection(name="microPAM",port=ser,
			buffering='line',
			translation='cr')
	if(isOpen(com)==FALSE) open(com)

	write.serialConnection(com,"?p\r\n")
	flush(com)
	Sys.sleep(0.1)
	ret<-read.serialConnection(com)
	close(com)
	cat(c(ret,"\n"))
}

syncTime <- function()
{
	ser <- listPorts()
	com <- serialConnection(name="microPAM",port=ser,
			buffering='line',
			translation='cr')
	if(isOpen(com)==FALSE) open(com)

	now=Sys.time()

	dd=format(now,"!d%Y-%m-%d\r\n")
	write.serialConnection(com,dd)
	flush(com)
	Sys.sleep(0.1)

	ret<-read.serialConnection(com)
	cat(ret)

	tt=format(now,"!t%H-%M-%S\r\n")
	write.serialConnection(com,tt)
	flush(com)
	Sys.sleep(0.1)

	ret<-read.serialConnection(com)

	cat(c(ret,"\n"))
	close(com)
}

setParameter <- function(token,value)
{
	ser <- listPorts()
	com <- serialConnection(name="microPAM",port=ser,
			buffering='line',
			translation='cr')
	if(isOpen(com)==FALSE) open(com)

	uu=sprintf("!%s%d\r\n",token,value)
	write.serialConnection(com,uu)
	flush(com)
	Sys.sleep(0.1)

	ret<-read.serialConnection(com)
	cat(c(ret,"\n"))
	close(com)
}

doCommand <- function(token)
{
	ser <- listPorts()
	com <- serialConnection(name="microPAM",port=ser,
			buffering='line',
			translation='cr')
	if(isOpen(com)==FALSE) open(com)

	uu=sprintf("%s\r\n",token)
	write.serialConnection(com,uu)
	flush(com)
	close(com)
}

monitor <- function(num)
{
	ser <- listPorts()
	com <- serialConnection(name="microPAM",port=ser,
			buffering='line',
			translation='lf')
	if(isOpen(com)==FALSE) open(com)

	ret<-read.serialConnection(com)
	for (ii in 1:num)
	{ Sys.sleep(1)
  	  ret<-read.serialConnection(com)
	  cat(c(ret,'\n'))
	}
	close(com)
}

basic audio test (wav file generation) script#

import numpy as np
import wavio
rate = 22050             # samples per second
T = 3                    # sample duration (seconds)
f = 440.0                # sound frequency (Hz)
t = np.linspace(0, T, T*rate, endpoint=False)
sig = np.sin(2 * np.pi * f * t)
wavio.write("sine24.wav", sig, rate, sampwidth=3)

Elliptical filters#

Low-pass filter with elliptical (Cauer) filter for very low (< 1kHz) corner frequencies

  • first decimate data to about 2400 Hz

  • estimate biquad filter coefficients with python script

  • apply 3 biquad filter

import numpy as np
from scipy.signal import ellip, sosfreqz
import scipy.signal as sig
import matplotlib.pyplot as plt

Fs = 2400

x = sig.ellip(6, 1, 60, [300 / (Fs / 2)], output='sos')
print(x)
freqs, resps = sig.sosfreqz(x, fs = Fs)

fig = plt.figure()
ax = fig.add_subplot(111)
ax.grid()

ax.plot(freqs, 20 * np.log10(np.abs(resps)))

plt.show()
[[ 0.00454991  0.00480905  0.00454991  1.         -1.54273107  0.64191613]
 [ 1.         -0.63758644  1.          1.         -1.43156463  0.80165859]
 [ 1.         -1.02047284  1.          1.         -1.37624928  0.94360981]]
_images/edf2fdd71652999c80fdcbd2cbb6f65a2585673441d612bda2384a626228277f.png