Skip to main content

Geting started with Megamicros Python SDK

Installation

You can install the Megamicros Python SDK using pip:

pip install megamicros

Checking your usb device

Beware that you need to have libusb installed, both the python interface and the C libusb.1.0.26 library The Python interface is installed automatically when you install the Megamicros package using pip. The C library must be installed manually on your system (MacOS and Linux), for example using brew install libusb on MacOS. On windows system you need to install the Zadig driver before using Megamicros (see the download page or download the latest version from the official website).

from megamicros.log import log
from megamicros.usb import Usb

# set log level to INFO to get more information (available levels are DEBUG, INFO, WARNING, ERROR, FATAL)
log.setLevel( "INFO" )

DEVICE_PRODUCT_ID = 0xAC03
# check
try:
if Usb.checkDeviceByVendorProduct( vendor_id=0xFE27, product_id=DEVICE_PRODUCT_ID ):
print( "Device found!" )
usb_device = Usb()
usb_device.open( vendor_id=0xFE27, product_id=DEVICE_PRODUCT_ID, bus_address=0x00, endpoint_in=0x81 )
usb_device.claim()
usb_device.release()
usb_device.close()
else:
print( "Device not found!" )
except Exception as e:
print( f"An error occurred: {e}" )

Using the Megamicros class

The Megamicros class is the base class for all Megamicros usb antennas. This Notebook shows how you can use the Megamicros interface provided you have a Megamicros device connected on your USB port.

import time
import numpy as np
import matplotlib.pyplot as plt

from megamicros.log import log
from megamicros.core.mu import Megamicros

# set log level to INFO to get more information (available levels are DEBUG, INFO, WARNING, ERROR, FATAL)
log.setLevel( "INFO" )

Declare your antenna object

The Megamicros constructor needs no argument. The device is detected and the class device parameters are populated.

antenna = Megamicros()

Lets know some properties of the connected antenna:

print( "Available MEMS: ", antenna.available_mems )
print( "Available Analogs: ", antenna.available_analogs )

Start acquisition

Try 1s of acquisition on all available MEMs:

antenna.setActiveMems( antenna.available_mems )
antenna.setActiveAnalogs( antenna.available_analogs )
antenna.setDuration( 1 )
antenna.run()
antenna.wait()

Getting data can be done by iterate through the antenna object provided the antenna had already received all the data:

# Print frames stored in the queue
print(f"queue content : {antenna.queue_content} frames")

# Retrieve data from the queue
for data in antenna:
print( f"data={data}" )

Once the frames have been retrieved, the queue is empty:

# Print frames stored in the queue
print(f"queue content : {antenna.queue_content} frames")

Advanced usage

You can force some MEMs to be activated while they are not available. Corresponding channels will stay at 0. However you cannot activate MEMS over the device you are connected to (MEMS 128 on a 32 system for example).

antenna.selftest()
available_mems = antenna.available_mems
print( "Available MEMS are: ", antenna.available_mems )

# Activate a MEMS that is not available (supposing 18 is not available)
antenna.setActiveMems( [18] )

antenna.run()
antenna.wait()

# Print frames stored in the queue
print(f"queue content : {antenna.queue_content} frames")

# Retrieve data from the queue
for data in antenna:
print( f"data={data}" )

Settings can be passed to the run() method as well:

antenna.run(
duration=1, # acquisition time in seconds
mems = antenna.available_mems, # activate all available MEMs
counter = True, # do not get counter channel
status = False, # do not get status channel
datatype = 'int32', # set datatype to ìnt32 (default)
sampling_frequency=50000 # set sampling frequency to 50kHz
)

antenna.wait()
# Only set MEMS 0 and 10. All previous args remain the same:
antenna.run(
mems = [0, 10],
)

antenna.wait()

Real time acquisition

The previous method suggests that real time is not possible. Actually, you can perform real time acqusition if you iterate before calling the wait method, just after the run call. The important point is that the run() call is a non-blocking. As such it implies that the wait() method is mandatory and must not be forgotten:

log.setLevel( "WARNING" ) 

antenna.run(
duration=1, # acquisition time in seconds
mems=[0, 1], # activate only MEMs 0 and 1
counter=True, # get counter channel
)

# Get only the counter (first channel)
for data in antenna:
print( f"Counter value: {data[0][0]}" )

antenna.wait()

# No more frames stored in the queue
print(f"queue content : {antenna.queue_content} frames")

Let us measure the acquisition time:

start_time = time.time()
antenna.run(
duration=1, # acquisition time in seconds
mems=[0, 1], # activate only MEMs 0 and 1
counter=True, # get counter channel
)
# Get only the counter (first channel)
counter = []
for data in antenna:
counter.append(int(data[0][0]))
antenna.wait()
end_time = time.time()

print(f"Acquisition time: {end_time - start_time} seconds")

Note that the elapsed time reaches 3.2 seconds, whereas the requested acquisition time was 1 second.

The method, as reported above, includes, in addition to the acquisition time (1s):

  • The MEMs initialization delay (MEMs powering), default is 200 millisecond
  • The timeout delay to leave the queue (2 seconds) As a consequence, the total elapsed is 3.2 secondes.

In the next example we decide to supress the MEMS powering delay by setting to 0 the time_activation setting:

log.setLevel( "WARNING" ) 
import time
start_time = time.time()

antenna.run(
duration=1, # acquisition time in seconds
mems=[0, 1], # activate only MEMs 0 and 1
counter=True, # get counter channel
time_activation=0 # no delay for MEMs powering
)

# Get only the counter (first channel) and the MEMS 0 data (second channel)
counter = np.array( [] )
mems_0 = np.array( [] )
for data in antenna:
counter = np.append(counter.astype(int), data[0][:]) # Counter data
mems_0 = np.append(mems_0.astype(float), data[1][:]) # MEMS 0 data
antenna.wait()
end_time = time.time()

print(f"Acquisition time: {end_time - start_time} seconds")

t = np.array( range( len(mems_0) ) )/antenna.sampling_frequency
signal = mems_0 * antenna.mems_sensibility

# Plot mems_0:
plt.figure()
plt.plot( t, signal )
plt.title('MEMS 0 activity')
plt.xlabel('time in seconds')
plt.ylabel('MEMS 0 values')
plt.show()

print( f"Counter values: {counter}" )

You can see during the first 200ms start of the signal a transitory state which correspond to the MEMS powering. The values of the counter variable are lower because we are very close to the start of acquisition.

Analog channels

If you have a Megamicros system that embed analog inputs, all you have to do is to declare them if you want to get those signals:

print( f"Available analog channels = {antenna.available_analogs}" )

antenna.run(
duration=1,
mems = [0, 1],
analogs = [0, 1],
counter=False,
status=False,
datatype = 'int32',
sampling_frequency=50000
)

# Wait until the thread terminates
antenna.wait()