Skip to content

Commit

Permalink
Merge pull request analogdevicesinc#484 from mc-so/dev/cn0565
Browse files Browse the repository at this point in the history
Add Support for CN0565.
  • Loading branch information
tfcollins authored Jan 15, 2024
2 parents 4c6472e + 39b52d8 commit 69b2d7a
Show file tree
Hide file tree
Showing 22 changed files with 2,204 additions and 0 deletions.
1 change: 1 addition & 0 deletions adi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
from adi.cn0511 import cn0511
from adi.cn0532 import cn0532
from adi.cn0554 import cn0554
from adi.cn0565 import cn0565
from adi.cn0566 import CN0566
from adi.cn0575 import cn0575
from adi.cn0579 import cn0579
Expand Down
120 changes: 120 additions & 0 deletions adi/cn0565.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# Copyright (C) 2023-2024 Analog Devices, Inc.
#
# SPDX short identifier: ADIBSD
# Author: Ivan Gil Mercano <[email protected]>

import numpy as np
from adi.ad5940 import ad5940
from adi.adg2128 import adg2128
from adi.context_manager import context_manager


class cn0565(ad5940, adg2128, context_manager):

"""The CN0565 class inherits features from both the AD5940 (providing high
precision in impedance and electrochemical frontend) and the ADG2128
(enabling arbitrary assignment of force and sense electrodes). '
These combined functionalities are utilized for Electrical
Impedance Tomography.
parameters:
uri: type=string
URI of the platform
"""

_device_name = "cn0565"

def __init__(self, uri=""):
context_manager.__init__(self, uri, self._device_name)
ad5940.__init__(self)
adg2128.__init__(self)
self._switch_sequence = None
self.add(0x71)
self.add(0x70)
self._electrode_count = 8
self._force_distance = 1
self._sense_distance = 1
self.excitation_frequency = 10000

@property
def electrode_count(self):
"""electrode_count: Number of electrodes"""
return self._electrode_count

@electrode_count.setter
def electrode_count(self, value):
self._electrode_count = value

@property
def force_distance(self):
"""force_distance: Number of electrodes between forcing electrodes. 1 means they are adjacent"""
return self._force_distance

@force_distance.setter
def force_distance(self, value):
self._force_distance = value

@property
def sense_distance(self):
"""sense_distance: Number of electrodes between sensing electrodes. 1 means they are adjacent"""
return self._sense_distance

@sense_distance.setter
def sense_distance(self, value):
self._sense_distance = value

@property
def switch_sequence(self):
"""switch_sequence: type=np.array
Sequence of combinations of forcing electrodes and sensing electrodes in the form of
f+, s+, s-, s+
"""
seq = 0
ret = []
for i in range(self.electrode_count):
f_plus = i
f_minus = (i + self.force_distance) % self.electrode_count
for j in range(self.electrode_count):
s_plus = j % self.electrode_count
if s_plus == f_plus or s_plus == f_minus:
continue
s_minus = (s_plus + self.sense_distance) % self.electrode_count
if s_minus == f_plus or s_minus == f_minus:
continue
ret.append((f_plus, s_plus, s_minus, f_minus))
seq += 1

return np.array(ret)

@property
def all_voltages(self):
"""all_voltages: type=np.array
Voltage readings from different electrode combinations
"""
if self._switch_sequence is None:
self._switch_sequence = self.switch_sequence

self.impedance_mode = False
ret = []
for seq in self._switch_sequence:
# reset cross point switch
self.gpio1_toggle = True

# set new cross point switch configuration from pregenerated sequence
self._xline[seq[0]][0] = True
self._xline[seq[1]][1] = True
self._xline[seq[2]][2] = True
self._xline[seq[3]][3] = True

# read impedance
s = self.channel["voltage0"].raw
ret.append([s.real, s.imag])

return np.array(ret).reshape(len(ret), 2)

@property
def electrode_count_available(self):
"""electrode_count_available: type=np.array
Supported Electrode Counts
"""
return np.array([8, 16, 32])
7 changes: 7 additions & 0 deletions doc/source/devices/adi.cn0565.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
cn0565
=================

.. automodule:: adi.cn0565
:members:
:undoc-members:
:show-inheritance:
1 change: 1 addition & 0 deletions doc/source/devices/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ Supported Devices
adi.cn0532
adi.cn0540
adi.cn0554
adi.cn0565
adi.cn0566
adi.cn0575
adi.cn0579
Expand Down
54 changes: 54 additions & 0 deletions examples/cn0565/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# CN0565
## 1. Install Prerequisite:

```cmd
pip install -r requirements.txt
pip install -e .
```
From the pyadi-iio main folder, install the prerequisites.
```cmd
pip install -r requirements.txt
```
After running this command, pip will read the requirements.txt file and install the specified packages along with their dependencies.

## 2. Run Example Script:
### Single Query
This will perform a single impedance measurement across a given electrode combinations.
```
python cn0565_example_single.py <f+> <f-> <s+> <s->
```
For example:
```cmd
python cn0565_example_single.py 0 3 1 2
```
This will use electrodes 0 and 3 for excitation and electrodes 1 and 2 for sensing.
### Running Multiple Measurements
This will perform the impedance across different possible electrode combinations.
```cmd
python cn0565_example.py
```
### Running EIT Examples
These examples uses [PyEIT](https://github.com/eitcom/pyEIT)
#### Back Projection
```cmd
python cn0565_back_projection.py
```
#### Jacobian
```cmd
python cn0565_jacobian.py
```
#### GREIT
```cmd
python cn0565_greit.py
```
#### All EIT Plots
```cmd
python cn0565_sample_plot.py
```

## 3. To run the GUI:
```cmd
python main.py
```
This will start the GUI for the Electrical Impedance Tomography Measurement System.
60 changes: 60 additions & 0 deletions examples/cn0565/cn0565_back_projection.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Copyright (C) 2022 Analog Devices, Inc.

# SPDX short identifier: ADIBSD

from __future__ import absolute_import, division, print_function

import matplotlib.pyplot as plt
import numpy as np
import pyeit.eit.bp as bp
import pyeit.eit.protocol as protocol
import pyeit.mesh as mesh
from adi import cn0565
from pyeit.eit.fem import EITForward
from pyeit.mesh.shape import thorax
from pyeit.mesh.wrapper import PyEITAnomaly_Circle

# variable/board declaration
value_type = "re" # re, im, others -> magnitude
n_el = 16 # no of electrodes
port = "COM6"
baudrate = 230400

# mesh and protocol creation
mesh = mesh.create(n_el, h0=0.08)
protocol = protocol.create(n_el, dist_exc=1, step_meas=1, parser_meas="std")

# board initialization
eit_board = cn0565(uri=f"serial:{port},{baudrate},8n1")
eit_board.excitation_frequency = 10000
eit_board.electrode_count = n_el
eit_board.force_distance = 1
eit_board.sense_distance = 1

# boundary voltage reading
voltages = eit_board.all_voltages
if value_type == "re":
current_data = voltages[:, 0]
elif value_type == "im":
current_data = voltages[:, 1]
else:
current_data = np.sqrt((voltages ** 2).sum(axis=1))

# Resistor array board is fixed. Use this to get absolute impedance
v0 = np.full_like(current_data, 1)
v1 = current_data


eit = bp.BP(mesh, protocol)
eit.setup(weight="none")
ds = 192.0 * eit.solve(v1, v0, normalize=True)
points = mesh.node
triangle = mesh.element

# Plot
fig, ax = plt.subplots()
im = ax.tripcolor(points[:, 0], points[:, 1], triangle, ds)
ax.set_title(r"Impedance Measurement Using Back Projection")
ax.axis("equal")
fig.colorbar(im, ax=ax)
plt.show()
Loading

0 comments on commit 69b2d7a

Please sign in to comment.