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 = 1      # 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-12-18 18:34:31.037242
COM18
---------------------------------------------------------------------------
SerialException                           Traceback (most recent call last)
Cell In[2], line 4
      2 microPAM_connected = 1      # change to 1 if microPAM is connected to USB
      3 if microPAM_connected:
----> 4     microPAM_showParameters()
      5     microPAM_syncTime()
      6 #    microPAM_setParameter("f",44100)
      7 #    microPAM_setParameter("s",12)
      8 #    microPAM_setParameter("c",1)

Cell In[1], line 12, in microPAM_showParameters()
     10 s=serial.tools.list_ports.comports(True)
     11 print(s[0].device)
---> 12 with serial.Serial(s[0].device) as ser:
     13     ser.reset_input_buffer
     14     ser.write(b'?p\r')

File ~\miniconda3\envs\cvas\Lib\site-packages\serial\serialwin32.py:33, in Serial.__init__(self, *args, **kwargs)
     31 self._overlapped_read = None
     32 self._overlapped_write = None
---> 33 super(Serial, self).__init__(*args, **kwargs)

File ~\miniconda3\envs\cvas\Lib\site-packages\serial\serialutil.py:244, in SerialBase.__init__(self, port, baudrate, bytesize, parity, stopbits, timeout, xonxoff, rtscts, write_timeout, dsrdtr, inter_byte_timeout, exclusive, **kwargs)
    241     raise ValueError('unexpected keyword arguments: {!r}'.format(kwargs))
    243 if port is not None:
--> 244     self.open()

File ~\miniconda3\envs\cvas\Lib\site-packages\serial\serialwin32.py:64, in Serial.open(self)
     62 if self._port_handle == win32.INVALID_HANDLE_VALUE:
     63     self._port_handle = None    # 'cause __del__ is called anyway
---> 64     raise SerialException("could not open port {!r}: {!r}".format(self.portstr, ctypes.WinError()))
     66 try:
     67     self._overlapped_read = win32.OVERLAPPED()

SerialException: could not open port 'COM18': PermissionError(13, 'Access is denied.', None, 5)

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 prev line to save cell to file (is only in microPAM-utilities.ipynb file)
# micoPAM GUI
# use this cell to test and develop GUI
# to compile "pyinstaller microPAM_GUI.py --noconfirm"
# will generate "dist/micoPAM_GUI/microPAM_GUI.exe"
# and  "dist/micoPAM_GUI/_internal" with all required pyd/dll files
#
import os
import tkinter as tk
import time
from datetime import datetime
import serial
import serial.tools.list_ports

class Window(tk.Frame):

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

    def mgetParam(self,ser,txt):
        ser.write(txt.encode())
        txt=ser.readline().decode('utf-8').rstrip()
        ip=txt.find("=")
        return txt[ip+2:]

    def mputEntry(self,edit,txt):
        edit.delete(0,tk.END)
        edit.insert(0,txt)

    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 mgetEntry(self,ser,str,edit):
        data=str+edit.get()+"\r"
        ser.write(data.encode())

    def ndays(self,d,m,y):
        def lpY(y): return (y%4==0) | ((y%100==0) & (y%400>0))
        dom=[31,28,31,30,31,30,31,31,30,31,30,31]
        #
        # number of days since 1-1-1970
        y1=y-1970
        days=y1*365
        for ii in range(y1): 
            if lpY(1970+ii): days +=1 
        #
        m -= 1
        for ii in range(m):
            days += dom[ii]
            if ii==1:
                if lpY(y): days += 1
        #
        d -= 1
        days += d
        return days, (days+4)%7 # 1-1-70 was thursday 1-1-24 was monday
    
    def nidays(self,days):
        def lpY(y): return (y%4==0) | ((y%100==0) & (y%400>0))
        dom=[31,28,31,30,31,30,31,31,30,31,30,31]
        #
        y1=0
        while days>0:
            if lpY(1970+y1): 
                days -=366
            else:
                days -= 365
            y1 +=1
        #
        if y1>0:
            y1 -= 1
        y1 += 1970
        if days<=0:
            if lpY(y1): 
                days += 366
            else:
                days += 365
        #
        days += 1
        m = 0
        while days >=0:
            if (m==1) & lpY(y1): days -=1
            days -= dom[m]
            m += 1
        days += dom[m-1]
        return (y1,m,days)

    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=210,y=10)
        self.pcClocklabel = tk.Label(text="", fg="Red", font=("Helvetica", 18))
        self.pcClocklabel.place(x=270,y=10)
        self.update_clock()

        label2 = tk.Label(text="MCU:",font=("Helvetica", 18))
        label2.place(x=190,y=50)
        self.mcuClocklabel = tk.Label(text="", fg="Black", font=("Helvetica", 18))
        self.mcuClocklabel.place(x=270,y=50)

        xo=100
        yo=50
        self.sernum_edit = self.mEntry("SerNum:",xo,yo,6,100);

        xo=100
        yo=90
        dxo=230
        ii=0
        self.b_edit = self.mEntry("Author:", xo+ii*dxo,yo,10,80); ii+=1
        self.k_edit = self.mEntry("Project:",xo+ii*dxo,yo,10,85); ii+=1
        self.n_edit = self.mEntry("Site:",   xo+ii*dxo-25,yo,10,55); ii+=1

        xo=120
        yo=160
        ii=0
        self.fsamp_edit = self.mEntry("fsamp:",xo,yo+ii*40,10,80); ii+=1
        self.proc_edit  = self.mEntry("proc:", xo,yo+ii*40,5,80); ii+=1
        self.shift_edit = self.mEntry("shift:",xo,yo+ii*40,5,80); ii+=1
        self.again_edit = self.mEntry("again:",xo,yo+ii*40,5,80); ii+=1

        xo=350
        yo=160
        ii=0
        self.t_acq_edit = self.mEntry("t_acq:",xo,yo+ii*40,5,80); ii+=1
        self.t_on_edit  = self.mEntry("t_on:", xo,yo+ii*40,5,80); ii+=1
        self.t_rep_edit = self.mEntry("t_rep:",xo,yo+ii*40,5,80); ii+=1
        ii=0
        xo += 160
        self.h_1_edit = self.mEntry("h_1:",xo,yo+ii*40,5,60); ii+=1
        self.h_2_edit = self.mEntry("h_2:",xo,yo+ii*40,5,60); ii+=1
        self.h_3_edit = self.mEntry("h_3:",xo,yo+ii*40,5,60); ii+=1
        self.h_4_edit = self.mEntry("h_4:",xo,yo+ii*40,5,60); ii+=1
        yo += 30
        self.d_start_edit = self.mEntry("d_start:",xo-320,yo+ii*40,3,90); 
        self.m_start_edit = self.mEntry("m_start:",xo-160,yo+ii*40,3,90); 
        self.y_start_edit = self.mEntry("y_start:",xo,yo+ii*40,5,90); ii+=1
        #
        self.d_on_edit    = self.mEntry("d_on:", xo,yo+ii*40,5,80); ii+=1
        self.d_rep_edit   = self.mEntry("d_rep:",xo,yo+ii*40,5,80); ii+=1

        # create buttons
        xm=600
        ym=160
        dym=60
        ii=0
        tk.Button(self, text="Exit", command=self.clickExitButton, font=("Helvetica", 18)).place(x=xm, y=10)
        tk.Button(self, text="Load", command=self.clickLoadButton, font=("Helvetica", 18)).place(x=xm, y=ym+ii*dym); ii+=1
        tk.Button(self, text="Sync", command=self.clickSyncButton, font=("Helvetica", 18)).place(x=xm, y=ym+ii*dym); ii+=1
        tk.Button(self, text="Save", command=self.clickSaveButton, font=("Helvetica", 18)).place(x=xm, y=ym+ii*dym); ii+=1
        self.storeButton = tk.Button(self, text="Store", command=self.clickStoreButton, font=("Helvetica", 18))
        self.storeButton.place(x=xm, y=ym+ii*dym)
    
        #
        date_time=datetime.now()
        self.mputEntry(self.d_start_edit,str(date_time.day))
        self.mputEntry(self.m_start_edit,str(date_time.month))
        self.mputEntry(self.y_start_edit,str(date_time.year))

        s=serial.tools.list_ports.comports(True)
        if len(s)>0:
            with serial.Serial(s[0].device) as ser:
                ser.reset_input_buffer()
                ser.reset_output_buffer()

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

    def clickLoadButton(self):
        self.storeButton["state"]=tk.DISABLED
        s=serial.tools.list_ports.comports(True)
        if len(s)>0:
            with serial.Serial(s[0].device) as ser:
                ser.reset_input_buffer()
                # stop acquisition
                ser.write(b'e\r')
                txt=ser.readline().decode('utf-8').rstrip()
                # stop monitor
                ser.write(b':m0\r')
                txt=ser.readline().decode('utf-8').rstrip()
                ser.reset_input_buffer()
                #
                # load now data from device
                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.b_edit,"?b")
                self.mUpdate(ser,self.k_edit,"?k")
                self.mUpdate(ser,self.n_edit,"?n")
                #
                self.mUpdate(ser,self.sernum_edit,"?h")
                #
                self.mUpdate(ser,self.t_acq_edit,"?a")
                self.mUpdate(ser,self.t_on_edit, "?o")
                self.mUpdate(ser,self.t_rep_edit,"?r")
                #
                self.mUpdate(ser,self.h_1_edit,"?1")
                self.mUpdate(ser,self.h_2_edit,"?2")
                self.mUpdate(ser,self.h_3_edit,"?3")
                self.mUpdate(ser,self.h_4_edit,"?4")
                #
                self.mUpdate(ser,self.d_on_edit,"?5")
                self.mUpdate(ser,self.d_rep_edit,"?6")
                #
                self.mUpdate(ser,self.fsamp_edit,"?f")
                self.mUpdate(ser,self.proc_edit, "?c")
                self.mUpdate(ser,self.shift_edit,"?s")
                self.mUpdate(ser,self.again_edit,"?g")
                #
                days=int(self.mgetParam(ser,"?0"))
                year,month,day=self.nidays(days+20000)
                self.mputEntry(self.d_start_edit,str(day))
                self.mputEntry(self.m_start_edit,str(month))
                self.mputEntry(self.y_start_edit,str(year))
        else:
            #for items in os.listdir():  print(items)
            print("current directory: ",os.getcwd())
            with open("config.txt","r") as f:
                for line in f:
                    ip0=line.find("=")
                    ip1=line.find(";")
                    match line[0]:
                        case 'b': self.mputEntry(self.b_edit,line[ip0+1:ip1])
                        case 'k': self.mputEntry(self.k_edit,line[ip0+1:ip1])
                        case 'n': self.mputEntry(self.n_edit,line[ip0+1:ip1])

                        case 'a': self.mputEntry(self.t_acq_edit,line[ip0+1:ip1])
                        case 'o': self.mputEntry(self.t_on_edit,line[ip0+1:ip1])
                        case 'r': self.mputEntry(self.t_rep_edit,line[ip0+1:ip1])
                        #
                        case '1': self.mputEntry(self.h_1_edit,line[ip0+1:ip1])
                        case '2': self.mputEntry(self.h_2_edit,line[ip0+1:ip1])
                        case '3': self.mputEntry(self.h_3_edit,line[ip0+1:ip1])
                        case '4': self.mputEntry(self.h_4_edit,line[ip0+1:ip1])
                        #
                        case '5': self.mputEntry(self.d_on_edit,line[ip0+1:ip1])
                        case '6': self.mputEntry(self.d_rep_edit,line[ip0+1:ip1])
                        #
                        case 'f': self.mputEntry(self.fsamp_edit,line[ip0+1:ip1])
                        case 'c': self.mputEntry(self.proc_edit,line[ip0+1:ip1])
                        case 's': self.mputEntry(self.shift_edit,line[ip0+1:ip1])
                        case 'g': self.mputEntry(self.again_edit,line[ip0+1:ip1])
                        #
                        case '0':
                            days=int(line[ip0+1:ip1])
                            year,month,day=self.nidays(days+20000)
                            self.mputEntry(self.d_start_edit,str(day))
                            self.mputEntry(self.m_start_edit,str(month))
                            self.mputEntry(self.y_start_edit,str(year))


    def clickSaveButton(self):
        dx=self.d_start_edit.get()
        mx=self.m_start_edit.get()
        yx=self.y_start_edit.get()
        days,dow=self.ndays(int(dx),int(mx),int(yx))
        #
        s=serial.tools.list_ports.comports(True)
        if len(s)>0:
            with serial.Serial(s[0].device) as ser:
                ser.read_all()
                self.mgetEntry(ser,'!b',self.b_edit)
                self.mgetEntry(ser,'!k',self.k_edit)
                self.mgetEntry(ser,'!n',self.n_edit)
                #
                self.mgetEntry(ser,'!a',self.t_acq_edit)
                self.mgetEntry(ser,'!o',self.t_on_edit)
                self.mgetEntry(ser,'!r',self.t_rep_edit)
                #
                self.mgetEntry(ser,'!1',self.h_1_edit)
                self.mgetEntry(ser,'!2',self.h_2_edit)
                self.mgetEntry(ser,'!3',self.h_3_edit)
                self.mgetEntry(ser,'!4',self.h_4_edit)
                #
                self.mgetEntry(ser,'!5',self.d_on_edit)
                self.mgetEntry(ser,'!6',self.d_rep_edit)
                #
                self.mgetEntry(ser,'!f',self.fsamp_edit)
                self.mgetEntry(ser,'!c',self.proc_edit)
                self.mgetEntry(ser,'!s',self.shift_edit)
                self.mgetEntry(ser,'!g',self.again_edit)
                #
                data="!0"+str(days-20000)+"\r"
                print('put', data)
                ser.write(data.encode())
                #
                ser.read_all()
        with open("config.txt","w") as f:
            f.write("b="+self.b_edit.get()+"; author\n")
            f.write("k="+self.k_edit.get()+"; project\n")
            f.write("n="+self.n_edit.get()+"; site\n")
            #
            f.write("a="+self.t_acq_edit.get()+"; t_acq\n")
            f.write("o="+self.t_on_edit.get()+"; t_on\n")
            f.write("r="+self.t_rep_edit.get()+"; t_rep\n")
            #
            f.write("1="+self.h_1_edit.get()+"; h_1\n")
            f.write("2="+self.h_2_edit.get()+"; h_2\n")
            f.write("3="+self.h_3_edit.get()+"; h_3\n")
            f.write("4="+self.h_4_edit.get()+"; h_4\n")
            #
            f.write("5="+self.d_on_edit.get()+"; d_on\n")
            f.write("6="+self.d_rep_edit.get()+"; d_rep\n")
            #
            f.write("f="+self.fsamp_edit.get()+"; fsamp\n")
            f.write("c="+self.proc_edit.get()+"; proc\n")
            f.write("s="+self.shift_edit.get()+"; shift\n")
            f.write("g="+self.again_edit.get()+"; again\n")
            #
            f.write("0="+str(days-20000)+"; d_0\n")

        print("current directory: ",os.getcwd())
        #for items in os.listdir():  print(items)
        self.storeButton["state"]=tk.NORMAL

    def clickStoreButton(self):
        s=serial.tools.list_ports.comports(True)
        if len(s)>0:
            with serial.Serial(s[0].device) as ser:
                ser.read_all()
                ser.write("!w1\r".encode())
                time.sleep(0.1)
                txt=ser.readline().decode('utf-8').rstrip()
                #
                ser.write(":w".encode())
                time.sleep(0.1)
                txt=ser.readline().decode('utf-8').rstrip()
                txt=ser.readline().decode('utf-8').rstrip()
                print(txt)

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

        s=serial.tools.list_ports.comports(True)
        if len(s)>0:
            with serial.Serial(s[0].device) as ser:
                ser.write(tt.encode())
            with serial.Serial(s[0].device) as ser:
                ser.write(dd.encode())
            with serial.Serial(s[0].device) as ser:
                ser.write(b':c')

    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("700x500")

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)   # 24 bit data (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/d7054c76241dbbeed0df9df77e143ca4b0de11bc68afe26f74cffc86695a9d4d.png

Get file information#

Run next cell and open microPAM data files (wav or bin) to print out meta data

#%%writefile microPAM_Info.py 
import numpy as np

from tkinter import filedialog

def headerDecode(xx):
        #print(xx[0].tobytes().decode())    # magic
        timestamp=xx[1:5].tobytes().decode() #timestamp
        sernum=xx[5:8].tobytes()[1:9].decode()   #sernum
        tt=xx[8]   #tt
        b_string=xx[9:19].tobytes().decode()    #owner
        k_string=xx[19:29].tobytes().decode()   #experiment
        n_string=xx[29:39].tobytes().decode()   #location
        pp=np.frombuffer(xx[39:47],dtype='int16')
        #print(xx[58].tobytes().decode())    #end

        print('owner     =',b_string)
        print('experiment=',k_string)
        print('location  =',n_string)
        print('sernum    =',sernum)
        print('timestamp =',timestamp)
        print('millis    =',tt)
        print('parameters=',pp)
        print('proc  =',pp[4])
        print('shift =',pp[5])
        print('again =',pp[13])
        print('dgain =',pp[14])
        print('t_acq =',pp[1])
        print('t_on  =',pp[2])
        print('t_rep =',pp[3])
        print('d_on  =',pp[10])
        print('d_rep =',pp[11])
        print('d_0   =',pp[15])
        print()

file_names = filedialog.askopenfilenames(initialdir="./")
#
wav_files = [name for name in file_names if name.endswith('.wav')]
bin_files = [name for name in file_names if name.endswith('.bin')]
if len(bin_files)>0:
    for name in bin_files:
        xx = np.fromfile(name, dtype='uint32',count=128)
        print(xx[0].tobytes().decode())    # WMXZ
        # bin header
        vers=xx[5]
        sernum=xx[6]
        fs = xx[7] # sampling frequency
        cp = xx[12] # data compress mode
        sh = xx[13] # shift in bits
        timestamp=(xx[1:5].tobytes()).decode()
        print('timestanp=',timestamp)
        print('vers     =',vers)
        print('sernum   =',hex(sernum))
        print('proc     =',cp)
        print('fs       =',fs)
        print('shift    =',sh)
        if(vers>=30):
            headerDecode(xx[20:68])

if len(wav_files)>0:
    for name in wav_files:
        xx = np.fromfile(name, dtype='uint32',count=128)
        # wav header
        print(xx[0].tobytes().decode())    # RIFF
        nch=xx[5]>>16
        fs=xx[6]
        nalign=xx[8]&0xffff
        nbits=xx[8]>>16
        # micoPAM info
        if xx[9].tobytes().decode()=='info':
            headerDecode(xx[11:59])
        #print(xx[126].tobytes().decode())    # data
        nbytes=xx[127]
        nsecs=nbytes/(nbits/8)/nch/fs

        print('nbits     =',nbits)
        print('nch       =',nch)
        print('fs        =',fs)
        print('nsecs     =',nsecs)
#
WMXZ
timestanp= 20241218_165040vers     = 30
sernum   = 0x155a71
proc     = 1
fs       = 48000
shift    = 0
owner     = WMXZexperiment= bla24location  = SP1sernum    =   155a71
timestamp = 20241218_165040millis    = 663492
parameters= [ 0 20 60  0  1  0  0 12 12 24  1  0 48  0  0  8]
proc  = 1
shift = 0
again = 0
dgain = 0
t_acq = 20
t_on  = 60
t_rep = 0
d_on  = 1
d_rep = 0
d_0   = 8