Measurements¶
The JII-MultispeQ
package allows the direct connection to a MultispeQ using a python script, instead of using a
separate application. In the following examples, it’s shown how to connect to a MultispeQ, use protocols or commands
with the device and save the measurements to your local computer, as well as analyze them with custom functions and
load them for a later analysis.
Important
The JII-MultispeQ
package is for local measurements. If the measurements are intended to be saved with a project
online, use the PhotosynQ, Inc. Desktop or Mobile app.
Connect a MultispeQ¶
Make sure your MultispeQ is powered on and connect it to your computer either using USB or Bluetooth®. After that, you have to open the connection by selecting the Serial port the device is connected to. Depending on the operating system used, the port names have different names.
Operating system |
USB |
Bluetooth® |
---|---|---|
Windows |
COM… |
COM… |
macOS |
usbmodem… |
Instrument-Name |
Linux |
ACM… |
Not tested |
Note
Connecting a Device
When connecting the device via USB, make sure the mirco-USB cable is designed for data transfer, not just charging.
When using Bluetooth®, the required pairing code is
1234
.
Available Ports¶
Use the get_ports()
to see the list of available ports on your
computer. Make sure to use the port, not just the name when selecting your device’s port.
1import jii_multispeq.device as _device
2
3## View Ports
4_device.get_ports( )
jii_multispeq.device.get_ports()
function (on macOS).¶Port Name Description Manufacturer Product
------------------------------- -------------------------- ------------- -------------- ----------
/dev/cu.debug-console cu.debug-console n/a
/dev/cu.Bluetooth-Incoming-Port cu.Bluetooth-Incoming-Port n/a
/dev/cu.usbmodem42949672951 cu.usbmodem42949672951 USB Serial Teensyduino USB Serial
/dev/cu.MultiSpeQ cu.MultiSpeQ n/a
Establish a Connection¶
Once the serial port is identified, use the following code to open the connection to your MultispeQ device.
The returned connection (serial.serialposix.Serial
) will be used with all other functions.
1import jii_multispeq.device as _device
2
3## Establish connection
4_connection = _device.connect( "/com1" )
Test the Connection¶
When the connection is established, it needs to be checked it the MultispeQ device is properly responding.
The is_connected()
checks if the connection is open and identifies the MultispeQ.
1## Test if connection is open and device is communicating
2if _device.is_connected( _connection ):
Take a Measurement¶
If the MultispeQ device is connected and the connection is open, measurements can be run now. The example below shows how a single relative Chlorophyll (SPAD) measurement can be taken.
1import jii_multispeq.measurement as _measurement
2import jii_multispeq.device as _device
3
4## MultispeQ Protocol
5spad_protocol = [{
6 "spad": [1]
7}]
8
9## MultispeQ Response Analysis
10def spad_fn( _data ):
11 output = {}
12 output["SPAD"] = _data["spad"][0]
13 return output
14
15## Establish connection
16_connection = _device.connect( "/com1" )
17
18## Test if connection is open and device is communicating
19if _device.is_connected( _connection ):
20
21 data, crc32 = _measurement.measure( _connection, spad_protocol,
22 None,
23 'Single SPAD meaurement' )
24
25 ## Run the analysis function
26 data = _measurement.analyze( data, spad_fn )
27
28 ## View response as a table
29 _measurement.view( data )
30
31## Close the connection
32_device.disconnect( _connection )
Parameter Value Type
--------------- -------------------------------- ---------------
created_at 2024-12-21T10:38:11.498360+01:00 <class 'str'>
device_battery 95 <class 'int'>
device_firmware 2.3465 <class 'float'>
device_id 01:12:53:## <class 'str'>
device_name MultispeQ <class 'str'>
device_version 2 <class 'str'>
md5_measurement 3565bc573416002424c6072bdfb033f2 <class 'str'>
md5_protocol b133a7f804cd1d1608da0577b3d89f72 <class 'str'>
notes Single SPAD meaurement <class 'str'>
protocol n/a <class 'list'>
SPAD 24.548 <class 'float'>
Save Measurement to Disk¶
The jii_multispeq.measurement.measure()
allows to save the data from the MultispeQ
to the disk as well. The example below shows how to only return the data, setting the
filename to None
or returning the data and saving the output to a file by specifying
the file name and a directory. If the filename is not set, or defined as auto
, a
filename is generated based on date and time (YYYY-MM-DD_HHmm
). In case the directory is not set, files will
automatically saved to the ./local
directory.
If a defined directory doesn’t exists it will be created. Files will not be overwritten, to prevent data loss. In case a filename already exists, a number will be appended to the filename.
1## Only return data
2data, crc32 = _measurement.measure( _connection, spad_protocol,
3 None,
4 'Single SPAD meaurement' )
5
6## Save data to defined file and define directory
7data, crc32 = _measurement.measure( _connection, spad_protocol,
8 "SPAD",
9 'Single SPAD meaurement',
10 './chlorophyll-measurements' )
11
12## Save data automatically
13data, crc32 = _measurement.measure( _connection, spad_protocol,
14 'Single SPAD meaurement' )
Note
Available Protocols
The JII-MultispeQ-Protocols package provides preset protocols for the MultispeQ. In addition, it provides a validation function for custom MultispeQ protocols, helping to identify potential issues with a protocol before running it.
Load Saved Measurement(s)¶
Saved measurement files can be loaded into a list of dictionaries (list[dict]
) or they can be loaded
into a pandas dataframe.
File Summary¶
It might be helpful to list files in the selected directory, to check what the content is without having to
open individual files using the list_files()
function of the
measurement
module. The filenames, the date and time the measurement was saved and
also the notes are listed by individual directories.
1import jii_multispeq.measurement as _measurement
2
3## View Files in Directories
4_measurement.list_files('./local')
Load Files¶
1import jii_multispeq.measurement as _measurement
2
3## Load Files Into a List
4data = _measurement.load_files('./local')
5
6## Load Files Into a Dataframe
7df = _measurement.load_files_df('./local')
Recursive Loading¶
1## Load Files Into a List Using Recursive Search
2data = _measurement.load_files('./local', True)
MultispeQ/
├── local/
│ ├── Measurement.json
│ ├── Measurement - 1.json
│ ├── Measurement - 2.json
│ ├── Measurement - 3.json
│ ├── Measurement - 4.json
│ └── sub-directory/
│ ├── Measurement.json
│ ├── Measurement - 1.json
│ └── Measurement - 2.json
└── script.py
Process Data¶
1def fn( _data ):
2 ## Data Processing here
3 return _data
4
5## Load Files and Apply Function on File Content
6data = _measurement.load_files('./local', fn=fn)
TL;DR¶
The following functions are available in the applications and probably more convinient to use.
Send a Command¶
1import jii_multispeq.device as _device
2
3## Establish connection
4_connection = _device.connect( "/com1" )
5
6## Test if connection is open and device is communicating
7if _device.is_connected( _connection ):
8
9 ## Send the command for device settings (memory)
10 response = _device.send_command( _connection, "print_memory" )
11
12 ## View response
13 print( response )
14
15## Close the connection
16_device.disconnect( _connection )
print_memory
.¶ 1{
2 "device_name": "MultispeQ",
3 "device_version": 2,
4 "device_id": "01:12:53:##",
5 "firmware": 2.3465,
6 "compiled": [ "Aug 2 2021", "16:00:28" ],
7 "calTimes": [ 1627920106, 1065353216, 1627920743, 1627920666, 1627920788, 0, -1050673152, 0 ],
8 "device_mod": 0,
9 "mag_address": 14,
10 "mag_x_scale": 800.0,
11 "mag_y_scale": 837.0,
12 "mag_z_scale": 694.0,
13 "max_PAR": [ 351, 4095, 3054, 4095, 4095, 4095, 1859, 4095, 4095, 4095 ],
14 "light_slope_all": [ 0.362 ],
15 "light_slope_r": [ -0.235 ],
16 "light_slope_g": [ -0.136 ],
17 "light_slope_b": [ -0.01 ],
18 "light_yint": [ 0.0 ],
19 "bleed3": [
20 [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 ],
21 [ 31.0, 173.0, 360.0, 401.0, 476.0, 726.0, 860.0, 986.0 ],
22 [ 7.0, 19.0, 33.0, 39.0, 63.0, 134.0, 178.0, 230.0 ],
23 [ 9.0, -48.0, -48.0, -46.0, -48.0, -141.0, -118.0, -123.0 ],
24 [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 ],
25 [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 ],
26 [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 ],
27 [ -10.0, 44.0, 41.0, 84.0, 125.0, 206.0, 312.0, 420.0 ],
28 [ 6.0, 10.0, -2.0, -5.0, 1.0, 6.0, -11.0, 7.0 ],
29 [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 ]
30 ],
31 "thickness_a": 68202.516,
32 "thickness_b": -6.395,
33 "thickness_c": 149665.172,
34 "mag_orientation": 0.0,
35 "open_position": 39965.0,
36 "closed_position": 41634.0,
37 "spad_scale": 45.829,
38 "spad_offset": -100.0,
39 "spad_yint": 6.805,
40 "blink_mode": 0,
41 "pilot_blink": 0,
42 "status_blink": 1,
43 "shutdown_time (s)": 1800,
44 "usb_on": 0,
45 "pix_pin": 14,
46 "par_map": [
47 [ 58, 95, 131, 203, 351, 351, 351 ],
48 [ 64, 101, 136, 206, 348, 771, 1477 ],
49 [ 39, 248, 455, 870, 1699, 3054, 3054 ],
50 [ 127, 371, 615, 1101, 2075, 4995, 9862 ],
51 [ 150, 177, 203, 257, 363, 683, 1217 ],
52 [ 150, 417, 683, 1217, 2283, 4095, 4095 ],
53 [ 73, 127, 180, 286, 497, 1132, 1859 ],
54 [ 150, 283, 417, 683, 1217, 2817, 4095 ],
55 [ 150, 417, 683, 1217, 2283, 4095, 4095 ],
56 [ 150, 283, 417, 683, 1217, 2817, 4095 ]
57 ],
58 "detector_offsets": [ 8.0, 0.0, -84.0, 0.0 ],
59 "par_tweak": 0.749
60}
Get Device Settings¶
The device’s settings can be viewed using the print_memory
command like in the example above, but there
is also a dedicated function for it. The code example below shows how to use this function.
In addition, the view()
of the measurement
module is
used to print it out in a more convinient way.
1import jii_multispeq.device as _device
2import jii_multispeq.measurement as _measurement
3
4## Establish connection
5_connection = _device.connect( "/com1" )
6
7## Test if connection is open and device is communicating
8if _device.is_connected( _connection ):
9
10 ## Send the command for device settings (memory)
11 response, crc32 = _device.get_memory( _connection )
12
13 ## View response as a table
14 _measurement.view( response )
15
16## Close the connection
17_device.disconnect( _connection )
1Parameter Value Type
2----------------- ----------- ---------------
3bleed3 n/a <class 'list'>
4blink_mode 0 <class 'int'>
5calTimes n/a <class 'list'>
6closed_position 41634.0 <class 'float'>
7compiled n/a <class 'list'>
8detector_offsets n/a <class 'list'>
9device_id 01:12:53:## <class 'str'>
10device_mod 0 <class 'int'>
11device_name MultispeQ <class 'str'>
12device_version 2 <class 'int'>
13firmware 2.3465 <class 'float'>
14light_slope_all n/a <class 'list'>
15light_slope_b n/a <class 'list'>
16light_slope_g n/a <class 'list'>
17light_slope_r n/a <class 'list'>
18light_yint n/a <class 'list'>
19mag_address 14 <class 'int'>
20mag_orientation 0.0 <class 'float'>
21mag_x_scale 800.0 <class 'float'>
22mag_y_scale 837.0 <class 'float'>
23mag_z_scale 694.0 <class 'float'>
24max_PAR n/a <class 'list'>
25open_position 39965.0 <class 'float'>
26par_map n/a <class 'list'>
27par_tweak 0.749 <class 'float'>
28pilot_blink 0 <class 'int'>
29pix_pin 14 <class 'int'>
30shutdown_time (s) 1800 <class 'int'>
31spad_offset -100.0 <class 'float'>
32spad_scale 45.829 <class 'float'>
33spad_yint 6.805 <class 'float'>
34status_blink 1 <class 'int'>
35thickness_a 68202.516 <class 'float'>
36thickness_b -6.395 <class 'float'>
37thickness_c 149665.172 <class 'float'>
38usb_on 0 <class 'int'>