ents 2.3.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ents/__init__.py +23 -0
- ents/calibrate/PingSMU.py +51 -0
- ents/calibrate/PingSPS.py +66 -0
- ents/calibrate/README.md +3 -0
- ents/calibrate/__init__.py +0 -0
- ents/calibrate/linear_regression.py +78 -0
- ents/calibrate/plots.py +83 -0
- ents/calibrate/recorder.py +678 -0
- ents/calibrate/requirements.txt +9 -0
- ents/cli.py +546 -0
- ents/config/README.md +123 -0
- ents/config/__init__.py +1 -0
- ents/config/adv_trace.py +56 -0
- ents/config/user_config.py +935 -0
- ents/proto/__init__.py +33 -0
- ents/proto/decode.py +106 -0
- ents/proto/encode.py +298 -0
- ents/proto/esp32.py +179 -0
- ents/proto/soil_power_sensor_pb2.py +72 -0
- ents/simulator/__init__.py +0 -0
- ents/simulator/node.py +161 -0
- ents-2.3.2.dist-info/METADATA +206 -0
- ents-2.3.2.dist-info/RECORD +26 -0
- ents-2.3.2.dist-info/WHEEL +4 -0
- ents-2.3.2.dist-info/entry_points.txt +5 -0
- ents-2.3.2.dist-info/licenses/LICENSE +21 -0
ents/__init__.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from .proto.encode import (
|
|
2
|
+
encode_response,
|
|
3
|
+
encode_power_measurement,
|
|
4
|
+
encode_teros12_measurement,
|
|
5
|
+
encode_phytos31_measurement,
|
|
6
|
+
encode_bme280_measurement,
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
from .proto.decode import decode_response, decode_measurement
|
|
10
|
+
|
|
11
|
+
from .proto.esp32 import encode_esp32command, decode_esp32command
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"encode_response",
|
|
15
|
+
"encode_power_measurement",
|
|
16
|
+
"encode_teros12_measurement",
|
|
17
|
+
"encode_phytos31_measurement",
|
|
18
|
+
"encode_bme280_measurement",
|
|
19
|
+
"decode_response",
|
|
20
|
+
"decode_measurement",
|
|
21
|
+
"encode_esp32command",
|
|
22
|
+
"decode_esp32command",
|
|
23
|
+
]
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""To test connection via socket to Kiethley 2450
|
|
2
|
+
|
|
3
|
+
Stephen Taylor, 5/20/2024
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import socket
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def ping_smu(host, port):
|
|
10
|
+
try:
|
|
11
|
+
# Create a TCP/IP socket
|
|
12
|
+
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
13
|
+
|
|
14
|
+
# Set a timeout for the connection attempt
|
|
15
|
+
sock.settimeout(1)
|
|
16
|
+
|
|
17
|
+
# Connect to the SMU
|
|
18
|
+
sock.connect((host, port))
|
|
19
|
+
|
|
20
|
+
# Send the *IDN? command
|
|
21
|
+
sock.sendall(b"*RST\n")
|
|
22
|
+
sock.sendall(b"*IDN?\n")
|
|
23
|
+
|
|
24
|
+
# Receive the response
|
|
25
|
+
response = sock.recv(1024)
|
|
26
|
+
response = response.strip()
|
|
27
|
+
print("Response from SMU:", response)
|
|
28
|
+
|
|
29
|
+
# Close the socket
|
|
30
|
+
sock.close()
|
|
31
|
+
|
|
32
|
+
# Return True if connection successful
|
|
33
|
+
return True
|
|
34
|
+
except Exception as e:
|
|
35
|
+
# Connection failed
|
|
36
|
+
print(f"Error: {e}")
|
|
37
|
+
return False
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
if __name__ == "__main__":
|
|
41
|
+
# Specify the IP address or hostname of the SMU and its port
|
|
42
|
+
smu_host = (
|
|
43
|
+
"128.114.204.56" # Replace with the actual IP address or hostname of the SMU
|
|
44
|
+
)
|
|
45
|
+
smu_port = 23 # Replace with the actual port used by the SMU
|
|
46
|
+
|
|
47
|
+
# Ping the SMU
|
|
48
|
+
if ping_smu(smu_host, smu_port):
|
|
49
|
+
print("SMU is reachable.")
|
|
50
|
+
else:
|
|
51
|
+
print("Failed to ping SMU.")
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"""To test connection to Soil Power Sensor over serial
|
|
2
|
+
|
|
3
|
+
Stephen Taylor 5/20/2024
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import serial
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class SerialPing:
|
|
10
|
+
"""Simple serial ping utility"""
|
|
11
|
+
|
|
12
|
+
def __init__(self, port, baudrate=115200, xonxoff=True):
|
|
13
|
+
"""Constructor
|
|
14
|
+
|
|
15
|
+
Initializes serial connection.
|
|
16
|
+
|
|
17
|
+
Parameters
|
|
18
|
+
----------
|
|
19
|
+
port : str
|
|
20
|
+
Serial port name (e.g., COM1, /dev/ttyUSB0)
|
|
21
|
+
baudrate : int, optional
|
|
22
|
+
Baud rate for serial communication (default is 115200, STM32 functions at 115200)
|
|
23
|
+
xonxoff : bool, optional
|
|
24
|
+
Flow control (default is on)
|
|
25
|
+
"""
|
|
26
|
+
self.ser = serial.Serial(port, baudrate=baudrate, xonxoff=xonxoff, timeout=1)
|
|
27
|
+
# Print serial port settings
|
|
28
|
+
print("Serial Port Settings:")
|
|
29
|
+
print("Port:", self.ser.port)
|
|
30
|
+
print("Baudrate:", self.ser.baudrate)
|
|
31
|
+
print("Byte size:", self.ser.bytesize)
|
|
32
|
+
print("Parity:", self.ser.parity)
|
|
33
|
+
print("Stop bits:", self.ser.stopbits)
|
|
34
|
+
print("Timeout:", self.ser.timeout)
|
|
35
|
+
print("Xon/Xoff:", self.ser.xonxoff)
|
|
36
|
+
print("Rts/cts:", self.ser.rtscts)
|
|
37
|
+
print("Dsr/dtr:", self.ser.dsrdtr)
|
|
38
|
+
|
|
39
|
+
def ping(self):
|
|
40
|
+
"""Ping the device and return the response"""
|
|
41
|
+
# pdb.set_trace()
|
|
42
|
+
self.ser.write(b"check\n") # Send ping command
|
|
43
|
+
response = self.ser.readline() # Read response
|
|
44
|
+
return response
|
|
45
|
+
|
|
46
|
+
def close(self):
|
|
47
|
+
"""Close serial connection"""
|
|
48
|
+
self.ser.close()
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
# Example usage
|
|
52
|
+
if __name__ == "__main__":
|
|
53
|
+
# Replace 'COM1' with the appropriate serial port on your system
|
|
54
|
+
port = "COM14"
|
|
55
|
+
|
|
56
|
+
# Create SerialPing object
|
|
57
|
+
serial_ping = SerialPing(port)
|
|
58
|
+
|
|
59
|
+
try:
|
|
60
|
+
# Ping the device
|
|
61
|
+
response = serial_ping.ping()
|
|
62
|
+
print("Response:", response)
|
|
63
|
+
|
|
64
|
+
finally:
|
|
65
|
+
# Close serial connection
|
|
66
|
+
serial_ping.close()
|
ents/calibrate/README.md
ADDED
|
File without changes
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from sklearn import linear_model
|
|
3
|
+
from sklearn.metrics import (
|
|
4
|
+
mean_absolute_error,
|
|
5
|
+
mean_squared_error,
|
|
6
|
+
r2_score,
|
|
7
|
+
mean_absolute_percentage_error,
|
|
8
|
+
)
|
|
9
|
+
from scipy.stats import norm
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def linear_regression(meas: list, actual: list):
|
|
13
|
+
"""Performs linear regression between measured and actual values
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
meas: Measure values
|
|
17
|
+
actual: Ground truth values
|
|
18
|
+
|
|
19
|
+
Returns:
|
|
20
|
+
Linear regression model
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
model = linear_model.LinearRegression()
|
|
24
|
+
model.fit(meas, actual)
|
|
25
|
+
|
|
26
|
+
return model
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def print_eval(pred: list, actual: list):
|
|
30
|
+
"""Prints various evaluation parameters of the model
|
|
31
|
+
|
|
32
|
+
The following metrics are used:
|
|
33
|
+
- Mean absolute error
|
|
34
|
+
- Root mean square error
|
|
35
|
+
- R2 score
|
|
36
|
+
- Mean absolute percentage error
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
pred: Predicted values
|
|
40
|
+
actual: Actual values
|
|
41
|
+
"""
|
|
42
|
+
mae = mean_absolute_error(actual, pred)
|
|
43
|
+
print(f"Mean absolute error: {mae:.4f}")
|
|
44
|
+
|
|
45
|
+
rmse = np.sqrt(mean_squared_error(actual, pred))
|
|
46
|
+
print(f"Root mean square error: {rmse:.4f}")
|
|
47
|
+
|
|
48
|
+
r2 = r2_score(actual, pred)
|
|
49
|
+
print(f"R-squared: {r2:.4f}")
|
|
50
|
+
|
|
51
|
+
mape = mean_absolute_percentage_error(actual, pred)
|
|
52
|
+
print(f"Mean absolute percentage error: {mape:.4f}")
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def print_coef(model):
|
|
56
|
+
"""Print coefficients of a model
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
model: Linear model
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
slope = model.coef_
|
|
63
|
+
inter = model.intercept_
|
|
64
|
+
|
|
65
|
+
print(f"Slope: {slope}")
|
|
66
|
+
print(f"Intercept: {inter}")
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def print_norm(residuals: list):
|
|
70
|
+
"""Print mean offset and standard deviation
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
residuals: List of residuals
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
mu, std = norm.fit(residuals)
|
|
77
|
+
print(f"mu = {mu}")
|
|
78
|
+
print(f"std = {std}")
|
ents/calibrate/plots.py
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import matplotlib.pyplot as plt
|
|
2
|
+
from scipy.stats import norm
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def plot_measurements(actual: list, meas: list, title: str = "", block: bool = False):
|
|
7
|
+
"""Plot actual values vs measured values
|
|
8
|
+
|
|
9
|
+
Args:
|
|
10
|
+
actual: Actual values
|
|
11
|
+
meas: Measured values
|
|
12
|
+
title: Suffix of plot title
|
|
13
|
+
block: Wait for plot to close
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
fig, ax = plt.subplots()
|
|
17
|
+
ax.scatter(actual, meas, s=3)
|
|
18
|
+
ax.set_xlabel("Actual")
|
|
19
|
+
ax.set_ylabel("Measured")
|
|
20
|
+
ax.set_title(f"Measurements: {title}")
|
|
21
|
+
plt.show(block=block)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def plot_calib(raw: list, pred: list, title: str = "", block: bool = False):
|
|
25
|
+
"""Plot linear relationship between raw measured and calibrated measured
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
raw: Uncalibrated measurements
|
|
29
|
+
pred: Predicted voltages
|
|
30
|
+
title: Suffix of plot title
|
|
31
|
+
block: Wait for plot to close
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
fig, ax = plt.subplots()
|
|
35
|
+
ax.scatter(raw, pred, s=3)
|
|
36
|
+
ax.set_xlabel("Raw measurements")
|
|
37
|
+
ax.set_ylabel("Predicted measurements")
|
|
38
|
+
ax.set_title(f"Calibration: {title}")
|
|
39
|
+
plt.show(block=block)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def plot_residuals(pred: list, residuals: list, title: str = "", block: bool = False):
|
|
43
|
+
"""Plot residuals vs predicted measurements
|
|
44
|
+
|
|
45
|
+
Residuals is the difference between predicted values and actual values.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
pred: Predicted measurements
|
|
49
|
+
residuals: List of residuals
|
|
50
|
+
title: Suffix of plot title
|
|
51
|
+
block: Wait for plot to close
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
fig, ax = plt.subplots()
|
|
55
|
+
ax.scatter(pred, residuals)
|
|
56
|
+
ax.axhline(y=0, color="r", linestyle="--")
|
|
57
|
+
ax.set_xlabel("Predicted measurement")
|
|
58
|
+
ax.set_ylabel("Residuals (pred - actual)")
|
|
59
|
+
ax.set_title(f"Residuals: {title}")
|
|
60
|
+
ax.legend()
|
|
61
|
+
plt.show(block=block)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def plot_residuals_hist(residuals: list, title: str = "", block: bool = False):
|
|
65
|
+
"""Plot a histogram of residual error
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
residuals: Residuals
|
|
69
|
+
title: Suffix of plot title
|
|
70
|
+
block: Wait for plot to close
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
mu, std = norm.fit(residuals)
|
|
74
|
+
normdist_x = np.linspace(mu - 3 * std, mu + 3 * std, 100)
|
|
75
|
+
normdist_y = norm.pdf(normdist_x, mu, std)
|
|
76
|
+
|
|
77
|
+
fig, ax = plt.subplots()
|
|
78
|
+
ax.hist(residuals, bins=30, edgecolor="black")
|
|
79
|
+
ax.plot(normdist_x, normdist_y, color="r")
|
|
80
|
+
ax.set_xlabel("Residuals")
|
|
81
|
+
ax.set_ylabel("Frequency")
|
|
82
|
+
ax.set_title(f"Histogram of Residuals: {title}")
|
|
83
|
+
plt.show(block=block)
|