diff --git a/examples/cn0569/adpd2140_gesture_sensor.py b/examples/cn0569/adpd2140_gesture_sensor.py new file mode 100644 index 000000000..ef5edd4ce --- /dev/null +++ b/examples/cn0569/adpd2140_gesture_sensor.py @@ -0,0 +1,297 @@ +# Copyright (C) 2021 Analog Devices, Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# - Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# - Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# - Neither the name of Analog Devices, Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# - The use of this software may or may not infringe the patent rights +# of one or more patent holders. This license does not release you +# from the requirement that you obtain separate licenses from these +# patent holders to use this software. +# - Use of the software either in source or binary form, must be run +# on or directly connected to an Analog Devices Inc. component. +# +# THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A +# PARTICULAR PURPOSE ARE DISCLAIMED. +# +# IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, INTELLECTUAL PROPERTY +# RIGHTS, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import math + +import adi + + +class adpd2140_gesture_sensor(adi.adpd1080): + """ADPD2140 gesture sensing class.""" + + _L0_thresh = 1000 + _L1_thresh = 1000 + _d0_thresh = 0.07 + _d1_thresh = 0.07 + + g0_incr = 0 + g1_incr = 0 + has_gest0 = False + has_gest1 = False + algo_time0 = False + algo_time1 = False + + avg = [0, 0, 0, 0, 0, 0, 0, 0] + + def __init__( + self, uri="", device_index=0, adpd2140_0=[0, 1, 2, 3], adpd2140_1=[4, 5, 6, 7] + ): + """ADPD1080 class constructor.""" + adi.adpd1080.__init__(self, uri) + + self._adpd2140_0 = adpd2140_0 + self._adpd2140_1 = adpd2140_1 + + def gain_calibration(self): + for _ in range(5): + data = self.rx() + for idx, val in enumerate(data): + self.avg[idx] += val.sum() + self.avg = [int(x / 5) for x in self.avg] + # todo: uncomment when iiod problem is solved + + # for i in range(8): + # self.channel[i].offset = self.channel[i].offset + avg[i] + + @property + def L0_thresh(self): + return self._L0_thresh + + @L0_thresh.setter + def L0_thresh(self, value): + self._L0_thresh = value + + @property + def L1_thresh(self): + return self._L1_thresh + + @L1_thresh.setter + def L1_thresh(self, value): + self._L1_thresh = value + + @property + def d0_thresh(self): + return self._d0_thresh + + @d0_thresh.setter + def d0_thresh(self, value): + self._d0_thresh = value + + @property + def d1_thresh(self): + return self._d1_thresh + + @d1_thresh.setter + def d1_thresh(self, value): + self._d1_thresh = value + + def get_gesture_0(self): + data = self.rx() + for i in range(4): + data[i] -= min(data[i], self.avg[i]) + + L0 = ( + data[self._adpd2140_0[0]].sum() + + data[self._adpd2140_0[1]].sum() + + data[self._adpd2140_0[2]].sum() + + data[self._adpd2140_0[3]].sum() + ) + + if L0 > self.L0_thresh and not self.has_gest0: + self.has_gest0 = True + self.start_x0 = ( + int(data[self._adpd2140_0[1]].sum()) + - int(data[self._adpd2140_0[0]].sum()) + ) / ( + int(data[self._adpd2140_0[1]].sum()) + + int(data[self._adpd2140_0[0]].sum()) + ) + self.start_y0 = ( + int(data[self._adpd2140_0[3]].sum()) + - int(data[self._adpd2140_0[2]].sum()) + ) / ( + int(data[self._adpd2140_0[3]].sum()) + + int(data[self._adpd2140_0[2]].sum()) + ) + return -1 + + if L0 < self.L0_thresh and self.has_gest0 and self.g0_incr >= 5: + self.has_gest0 = False + try: + self.end_x0 = ( + int(data[self._adpd2140_0[1]].sum()) + - int(data[self._adpd2140_0[0]].sum()) + ) / ( + int(data[self._adpd2140_0[1]].sum()) + + int(data[self._adpd2140_0[0]].sum()) + ) + self.end_y0 = ( + int(data[self._adpd2140_0[3]].sum()) + - int(data[self._adpd2140_0[2]].sum()) + ) / ( + int(data[self._adpd2140_0[3]].sum()) + + int(data[self._adpd2140_0[2]].sum()) + ) + except ZeroDivisionError: + self.end_x0 = 0.000001 + self.end_y0 = 0.000001 + self.algo_time0 = True + + if self.algo_time0: + self.algo_time0 = False + m = (self.start_y0 - self.end_y0) / (self.start_x0 - self.end_x0 + 0.000001) + d = math.sqrt( + (self.start_x0 - self.end_x0) ** 2 + (self.start_y0 - self.end_y0) ** 2 + ) + if d < self.d0_thresh: + return 0 + else: + if abs(m) > 1: + if self.start_y0 < self.end_y0: + return 1 + else: + return 2 + elif abs(m) < 1: + if self.start_x0 > self.end_x0: + return 3 + else: + return 4 + else: + return 0 + + if L0 >= self.L0_thresh: + self.g0_incr += 1 + return -1 + else: + self.g0_incr = 0 + return -1 + + def get_gesture_1(self): + data = self.rx() + for i in range(4): + data[i + 4] -= min(data[i + 4], self.avg[i + 4]) + + L1 = ( + data[self._adpd2140_1[0]].sum() + + data[self._adpd2140_1[1]].sum() + + data[self._adpd2140_1[2]].sum() + + data[self._adpd2140_1[3]].sum() + ) + + if L1 > self.L1_thresh and not self.has_gest1: + self.has_gest1 = True + self.start_x1 = ( + int(data[self._adpd2140_1[1]].sum()) + - int(data[self._adpd2140_1[0]].sum()) + ) / ( + int(data[self._adpd2140_1[1]].sum()) + + int(data[self._adpd2140_1[0]].sum()) + ) + self.start_y1 = ( + int(data[self._adpd2140_1[3]].sum()) + - int(data[self._adpd2140_1[2]].sum()) + ) / ( + int(data[self._adpd2140_1[3]].sum()) + + int(data[self._adpd2140_1[2]].sum()) + ) + return -1 + + if L1 < self.L1_thresh and self.has_gest1 and self.g1_incr >= 5: + self.has_gest1 = False + try: + self.end_x1 = ( + int(data[self._adpd2140_1[1]].sum()) + - int(data[self._adpd2140_1[0]].sum()) + ) / ( + int(data[self._adpd2140_1[1]].sum()) + + int(data[self._adpd2140_1[0]].sum()) + ) + self.end_y1 = ( + int(data[self._adpd2140_1[3]].sum()) + - int(data[self._adpd2140_1[2]].sum()) + ) / ( + int(data[self._adpd2140_1[3]].sum()) + + int(data[self._adpd2140_1[2]].sum()) + ) + except ZeroDivisionError: + self.end_x1 = 0.000001 + self.end_y1 = 0.000001 + self.algo_time1 = True + + if self.algo_time1: + self.algo_time1 = False + m = (self.start_y1 - self.end_y1) / (self.start_x1 - self.end_x1 + 0.000001) + d = math.sqrt( + (self.start_x1 - self.end_x1) ** 2 + (self.start_y1 - self.end_y1) ** 2 + ) + if d < self.d1_thresh: + return 0 + else: + if abs(m) > 1: + if self.start_y1 < self.end_y1: + return 1 + else: + return 2 + elif abs(m) < 1: + if self.start_x1 > self.end_x1: + return 3 + else: + return 4 + else: + return 0 + + if L1 >= self.L1_thresh: + self.g1_incr += 1 + return -1 + else: + self.g1_incr = 0 + return -1 + + +if __name__ == "__main__": + + adpd1080 = adpd2140_gesture_sensor(uri="serial:COM5") + adpd1080.rx_buffer_size = 8 + adpd1080.sample_rate = 512 + adpd1080.gain_calibration() + + gestures = [ + "CLICK_0", + "UP_0", + "DOWN_0", + "LEFT_0", + "RIGHT_0", + "CLICK_1", + "UP_1", + "DOWN_1", + "LEFT_1", + "RIGHT_1", + ] + + while True: + gesture0 = adpd1080.get_gesture_0() + gesture1 = adpd1080.get_gesture_1() + if gesture0 >= 0: + print(gestures[gesture0]) + if gesture1 >= 0: + print(gestures[gesture1 + 4]) diff --git a/examples/cn0569/cn0569_theremin_module.py b/examples/cn0569/cn0569_theremin_module.py new file mode 100644 index 000000000..3cbc1692f --- /dev/null +++ b/examples/cn0569/cn0569_theremin_module.py @@ -0,0 +1,124 @@ +# Copyright (C) 2021 Analog Devices, Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# - Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# - Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# - Neither the name of Analog Devices, Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# - The use of this software may or may not infringe the patent rights +# of one or more patent holders. This license does not release you +# from the requirement that you obtain separate licenses from these +# patent holders to use this software. +# - Use of the software either in source or binary form, must be run +# on or directly connected to an Analog Devices Inc. component. +# +# THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A +# PARTICULAR PURPOSE ARE DISCLAIMED. +# +# IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, INTELLECTUAL PROPERTY +# RIGHTS, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +import math +import time + +import adpd2140_gesture_sensor +import numpy as np +import simpleaudio as sa + +sample_rate = 44100 +T = 1 +t = np.linspace(0, T, T * sample_rate, False) + +freq_c4 = 65.41 +freq_d4 = 73.42 +freq_e4 = 82.41 +freq_f4 = 87.31 +freq_g4 = 98.00 + +freq_c5 = 130.81 +freq_d5 = 146.83 +freq_e5 = 164.81 +freq_f5 = 174.61 +freq_g5 = 196.00 + +note_c4 = np.sin(freq_c4 * t * 2 * np.pi) +note_d4 = np.sin(freq_d4 * t * 2 * np.pi) +note_e4 = np.sin(freq_e4 * t * 2 * np.pi) +note_f4 = np.sin(freq_f4 * t * 2 * np.pi) +note_g4 = np.sin(freq_g4 * t * 2 * np.pi) + +note_c5 = np.sin(freq_c5 * t * 2 * np.pi) +note_d5 = np.sin(freq_d5 * t * 2 * np.pi) +note_e5 = np.sin(freq_e5 * t * 2 * np.pi) +note_f5 = np.sin(freq_f5 * t * 2 * np.pi) +note_g5 = np.sin(freq_g5 * t * 2 * np.pi) + +norm_c4 = note_c4 * 32767 / np.max(np.abs(note_c4)) +norm_c4 = norm_c4.astype(np.int16) +norm_d4 = note_d4 * 32767 / np.max(np.abs(note_d4)) +norm_d4 = norm_d4.astype(np.int16) +norm_e4 = note_e4 * 32767 / np.max(np.abs(note_e4)) +norm_e4 = norm_e4.astype(np.int16) +norm_f4 = note_f4 * 32767 / np.max(np.abs(note_f4)) +norm_f4 = norm_f4.astype(np.int16) +norm_g4 = note_g4 * 32767 / np.max(np.abs(note_g4)) +norm_g4 = norm_g4.astype(np.int16) + +norm_c5 = note_c5 * 32767 / np.max(np.abs(note_c5)) +norm_c5 = norm_c5.astype(np.int16) +norm_d5 = note_d5 * 32767 / np.max(np.abs(note_d5)) +norm_d5 = norm_d5.astype(np.int16) +norm_e5 = note_e5 * 32767 / np.max(np.abs(note_e5)) +norm_e5 = norm_e5.astype(np.int16) +norm_f5 = note_f5 * 32767 / np.max(np.abs(note_f5)) +norm_f5 = norm_f5.astype(np.int16) +norm_g5 = note_g5 * 32767 / np.max(np.abs(note_g5)) +norm_g5 = norm_g5.astype(np.int16) + +theremin_args0 = [norm_c4, norm_d4, norm_e4, norm_f4, norm_g4] +theremin_args1 = [norm_c5, norm_d5, norm_e5, norm_f5, norm_g5] + +if __name__ == "__main__": + + adpd2140_pmod = adpd2140_gesture_sensor.adpd2140_gesture_sensor(uri="serial:COM4") + adpd2140_pmod.rx_buffer_size = 8 + # todo: uncomment when iiod problem is fixed + # adpd2140_pmod.sample_rate = 512 + adpd2140_pmod.gain_calibration() + + gestures = [ + "CLICK_0", + "UP_0", + "DOWN_0", + "LEFT_0", + "RIGHT_0", + "CLICK_1", + "UP_1", + "DOWN_1", + "LEFT_1", + "RIGHT_1", + ] + + while True: + gesture0 = adpd2140_pmod.get_gesture_0() + gesture1 = adpd2140_pmod.get_gesture_1() + if gesture0 >= 0: + print(gestures[gesture0]) + sa.play_buffer(theremin_args0[gesture0], 1, 2, sample_rate) + if gesture1 >= 0: + print(gestures[gesture1 + 4]) + sa.play_buffer(theremin_args1[gesture1], 1, 2, sample_rate)