Skip to content

Commit 29f6645

Browse files
BradG13531patrickelectric
authored andcommitted
Added .svlog support to Surveyor and fixed some bugs in Omniscan files
1 parent 215e65e commit 29f6645

File tree

4 files changed

+305
-157
lines changed

4 files changed

+305
-157
lines changed

examples/omniscan450Example.py

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111

1212
import signal
1313
import sys
14-
import os
1514
from pathlib import Path
1615
from datetime import datetime
1716

@@ -84,17 +83,19 @@ def signal_handler(sig, frame):
8483

8584
else:
8685
raise ValueError(f"Invalid log argument: {args.log}")
87-
88-
if args.device is not None:
89-
myOmniscan450.connect_serial(args.device, args.baudrate)
90-
elif args.udp is not None:
91-
(host, port) = args.udp.split(':')
92-
myOmniscan450.connect_udp(host, int(port))
93-
elif args.tcp is not None:
94-
(host, port) = args.tcp.split(':')
95-
myOmniscan450.connect_tcp(host, int(port))
86+
else:
87+
myOmniscan450 = Omniscan450()
9688

9789
if args.log is None or new_log:
90+
if args.device is not None:
91+
myOmniscan450.connect_serial(args.device, args.baudrate)
92+
elif args.udp is not None:
93+
(host, port) = args.udp.split(':')
94+
myOmniscan450.connect_udp(host, int(port))
95+
elif args.tcp is not None:
96+
(host, port) = args.tcp.split(':')
97+
myOmniscan450.connect_tcp(host, int(port))
98+
9899
if myOmniscan450.initialize() is False:
99100
print("Failed to initialize Omniscan450!")
100101
exit(1)
@@ -109,7 +110,7 @@ def signal_handler(sig, frame):
109110

110111
input("Press Enter to continue...")
111112

112-
# Running Omniscan from existing log file
113+
# Running omniscan450Example.py from existing log file
113114
if args.log is not None and not new_log:
114115
with open(log_path, 'rb') as f:
115116
while True:
@@ -170,6 +171,8 @@ def signal_handler(sig, frame):
170171
# View power results
171172
if new_log:
172173
print("Logging...\nCTRL+C to stop logging")
174+
else:
175+
print("CTRL-C to end program...")
173176
try:
174177
while True:
175178
data = myOmniscan450.wait_message([definitions.OMNISCAN450_OS_MONO_PROFILE])

examples/surveyor240Example.py

Lines changed: 116 additions & 142 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
parser.add_argument('--udp', action="store", required=False, type=str, help="Surveyor IP:Port. E.g: 192.168.2.86:62312")
2626
parser.add_argument('--tcp', action="store", required=False, type=str, help="Surveyor IP:Port. E.g: 192.168.2.86:62312")
2727
parser.add_argument('--range', action="store", required=False, type=str, help="Set range. E.g: 5000 or 0:5000")
28-
parser.add_argument('--log', action="store", nargs='?', const=True, type=str, help="Log filename or folder name. Will log if it doesn't exist, or replay all packets inside directory if it does. Left blank creates new log.")
28+
parser.add_argument('--log', action="store", nargs='?', const=True, type=str, help="Log filename and/or directory path. Will create new log if blank or directory is specified. Will replay if file is specified and exists.")
2929
args = parser.parse_args()
3030
if args.device is None and args.udp is None and args.tcp is None and args.log is None:
3131
parser.print_help()
@@ -45,37 +45,60 @@ def signal_handler(sig, frame):
4545

4646
signal.signal(signal.SIGINT, signal_handler)
4747

48-
# Make a new Surveyor240
49-
mySurveyor240 = Surveyor240()
50-
if args.device is not None:
51-
mySurveyor240.connect_serial(args.device, args.baudrate)
52-
elif args.udp is not None:
53-
(host, port) = args.udp.split(':')
54-
mySurveyor240.connect_udp(host, int(port))
55-
elif args.tcp is not None:
56-
(host, port) = args.tcp.split(':')
57-
mySurveyor240.connect_tcp(host, int(port))
58-
59-
# Check for log argument
60-
# If no log is specified, create one using date and time
61-
# If a log is specified, existing log will be opened
48+
# Check for log argument and make new Surveyor240
49+
# If no .svlog is specified, create one using default directory
50+
# If directory specified, .svlog be created in specified directory
51+
# If a .svlog is specified, existing log will be opened
6252
new_log = False
63-
new_folder_name = None
53+
log_path = ""
54+
replay_path = None
55+
default_dir = Path("logs/surveyor").resolve()
6456
if args.log is not None:
6557
if args.log is True:
58+
# Logging to default directory
59+
default_dir.mkdir(parents=True, exist_ok=True)
60+
mySurveyor240 = Surveyor240(logging=True, log_directory=default_dir)
61+
print(f"Logging to new file in: {default_dir}")
6662
new_log = True
6763
elif isinstance(args.log, str):
68-
log_path = os.path.join("logs/surveyor", args.log)
69-
if args.log.endswith(".txt"):
70-
new_log = False
71-
elif os.path.exists(log_path):
72-
print(f"Replaying from existing log folder: {log_path}")
73-
new_log = False
74-
else:
75-
new_folder_name = args.log
64+
log_path = Path(args.log).expanduser()
65+
66+
if log_path.suffix == ".svlog" and log_path.parent == Path("."):
67+
log_path = default_dir / log_path.name
68+
69+
log_path = log_path.resolve()
70+
71+
if log_path.suffix == ".svlog":
72+
if log_path.exists() and log_path.is_file():
73+
# File exists, replaying
74+
new_log = False
75+
mySurveyor240 = Surveyor240(logging=False)
76+
replay_path = log_path
77+
print(f"Replaying from: {replay_path}")
78+
else:
79+
raise FileNotFoundError(f"Log file not found: {log_path}")
80+
81+
elif log_path.is_dir() or log_path.suffix == "":
82+
# Path is directory, logging to that directory
83+
mySurveyor240 = Surveyor240(logging=True, log_directory=log_path)
84+
print(f"Logging to new file: {Surveyor240.current_log}")
7685
new_log = True
86+
87+
else:
88+
raise ValueError(f"Invalid log argument: {args.log}")
89+
else:
90+
mySurveyor240 = Surveyor240()
7791

7892
if args.log is None or new_log:
93+
if args.device is not None:
94+
mySurveyor240.connect_serial(args.device, args.baudrate)
95+
elif args.udp is not None:
96+
(host, port) = args.udp.split(':')
97+
mySurveyor240.connect_udp(host, int(port))
98+
elif args.tcp is not None:
99+
(host, port) = args.tcp.split(':')
100+
mySurveyor240.connect_tcp(host, int(port))
101+
79102
if mySurveyor240.initialize() is False:
80103
print("Failed to initialize Surveyor240!")
81104
exit(1)
@@ -87,37 +110,55 @@ def signal_handler(sig, frame):
87110

88111
input("Press Enter to continue...")
89112

90-
# Running Surveyor240 from existing log file
113+
# Running surveyor240Example.py from existing log file
91114
if args.log is not None and not new_log:
92-
log_path = Path("logs/surveyor") / args.log
93-
if not log_path.exists():
94-
print(f"Log path does not exist: {log_path}")
95-
sys.exit(1)
96-
97-
if log_path.is_dir():
98-
for file in sorted(log_path.iterdir()):
99-
if file.suffix == ".txt":
100-
print(f"\n---------Replaying File: {file.name}---------")
101-
with open(file, 'rb') as f:
102-
raw_bytes = f.read()
103-
data = PingMessage(msg_data=raw_bytes)
104-
105-
if data:
106-
print(data)
107-
else:
108-
print("Failed to get report")
109-
elif log_path.is_file():
110-
print(f"\n---------Replaying File: {log_path.name}---------")
111-
with open(log_path, 'rb') as f:
112-
raw_bytes = f.read()
113-
data = PingMessage(msg_data=raw_bytes)
114-
115-
if data:
116-
print(data)
117-
else:
118-
print("Failed to get report")
119-
else:
120-
print(f"Invalid log path: {log_path}")
115+
with open(log_path, 'rb') as f:
116+
while True:
117+
data = Surveyor240.read_packet(f)
118+
119+
if data == None:
120+
break # EOF or bad packet
121+
122+
# print(f"ID: {data.message_id}\tName: {data.name}")
123+
124+
## Surveyor will report the Water Stats packet if temperature and/or pressure sensor is connected
125+
# if data.message_id == definitions.SURVEYOR240_WATER_STATS:
126+
# print(f"Temperature: {(data.temperature * 9/5) + 32} F")
127+
# print(f"Temperature: {data.temperature} C")
128+
# print(f"Pressure: {data.pressure} bar")
129+
130+
if data.message_id == definitions.SURVEYOR240_ATTITUDE_REPORT:
131+
# Print pitch and roll data
132+
vector = (data.up_vec_x, data.up_vec_y, data.up_vec_z)
133+
pitch = math.asin(vector[0])
134+
roll = math.atan2(vector[1], vector[2])
135+
print(f"Pitch: {pitch}\tRoll: {roll}")
136+
137+
# if data.message_id == definitions.SURVEYOR240_YZ_POINT_DATA:
138+
# # Display YZ point data in a table
139+
# yz_data = Surveyor240.create_yz_point_data(data)
140+
# print(f"Length of yz_data: {len(yz_data)}\tNum_points: {data.num_points}")
141+
# print("Index\tY\tZ")
142+
# for i in range(0, len(yz_data), 2):
143+
# print(f"{i//2}\t{yz_data[i]:.2f}\t{yz_data[i+1]:.2f}")
144+
# print(f"Temperature: {(data.water_degC * 9/5) + 32} F")
145+
# print(f"Temperature: {data.water_degC} C")
146+
# print(f"Pressure: {data.water_bar} Bar")
147+
148+
# if data.message_id == definitions.SURVEYOR240_ATOF_POINT_DATA:
149+
# # Just an example packet, could check for other packet types and
150+
# # show results from those too
151+
152+
# # Use create_atof_list to get formatted atof_t[num_points] list
153+
# atof_data = Surveyor240.create_atof_list(data)
154+
# if len(atof_data) == 0:
155+
# continue
156+
# else:
157+
# # Just the first data point in atof[]
158+
# distance = 0.5 * data.sos_mps * atof_data[0].tof
159+
# y = distance * math.sin(atof_data[0].angle)
160+
# z = -distance * math.cos(atof_data[0].angle)
161+
# print(f"Distance: {distance:.3f} meters\tY: {y:.3f}\tZ: {z:.3f}\t{atof_data[0]}")
121162

122163
# Connected to physical Surveyor
123164
else:
@@ -155,94 +196,27 @@ def signal_handler(sig, frame):
155196
)
156197

157198
if new_log:
158-
if new_folder_name is None:
159-
log_folder_name = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
160-
else:
161-
log_folder_name = new_folder_name
162-
log_path = os.path.join("logs/surveyor", log_folder_name)
163-
os.makedirs(log_path, exist_ok=True)
164-
print(f"Logging new files in: {log_path}")
165-
166-
print("\n---------Attitude Report---------")
167-
while True:
168-
data = mySurveyor240.wait_message([definitions.SURVEYOR240_ATTITUDE_REPORT])
169-
if data:
170-
# Create new log if specified
171-
if new_log:
172-
attitude_data_path = os.path.join(log_path, "Attitude_Report.txt")
173-
with open(attitude_data_path, 'ab') as f:
174-
f.write(data.msg_data)
175-
176-
# Print pitch and roll data
177-
vector = (data.up_vec_x, data.up_vec_y, data.up_vec_z)
178-
pitch = math.asin(vector[0])
179-
roll = math.atan2(vector[1], vector[2])
180-
print(f"Pitch: {pitch}\tRoll: {roll}")
181-
break
182-
else:
183-
print("Failed to get attitude report")
184-
time.sleep(0.1)
185-
186-
print("\n---------ATOF Point Data---------")
187-
while True:
188-
data = mySurveyor240.wait_message([definitions.SURVEYOR240_ATOF_POINT_DATA])
189-
if data:
190-
if new_log:
191-
atof_data_path = os.path.join(log_path, "ATOF_Point_Data.txt")
192-
with open(atof_data_path, 'ab') as f:
193-
f.write(data.msg_data)
194-
195-
# Use create_atof_list to get formatted atof_t[num_points] list
196-
atof_data = Surveyor240.create_atof_list(data)
197-
if len(atof_data) == 0:
198-
continue
199-
else:
200-
for i in range(len(atof_data)):
201-
distance = 0.5 * data.sos_mps * atof_data[i].tof
202-
y = distance * math.sin(atof_data[i].angle)
203-
z = -distance * math.cos(atof_data[i].angle)
204-
print(f"{i}.\tDistance: {distance:.3f} meters\tY: {y:.3f}\tZ: {z:.3f}\t{atof_data[i]}")
205-
break
206-
else:
207-
print("Failed to get atof point data")
208-
209-
print("\n---------YZ Point Data---------")
210-
while True:
211-
data = mySurveyor240.wait_message([definitions.SURVEYOR240_YZ_POINT_DATA])
212-
if data:
213-
if new_log:
214-
yz_data_path = os.path.join(log_path, "YZ_Point_Data.txt")
215-
with open(yz_data_path, 'ab') as f:
216-
f.write(data.msg_data)
217-
218-
# Display YZ point data in a table
219-
yz_data = Surveyor240.create_yz_point_data(data)
220-
print(f"Length of yz_data: {len(yz_data)}\tNum_points: {data.num_points}")
221-
print("Index\tY\tZ")
222-
for i in range(0, len(yz_data), 2):
223-
print(f"{i//2}\t{yz_data[i]:.2f}\t{yz_data[i+1]:.2f}")
224-
print(f"Temperature: {(data.water_degC * 9/5) + 32} F")
225-
print(f"Temperature: {data.water_degC} C")
226-
print(f"Pressure: {data.water_bar} Bar")
227-
break
228-
else:
229-
print("Failed to get yz point data")
230-
231-
print("\n---------Water Stats---------")
232-
while True:
233-
data = mySurveyor240.wait_message([definitions.SURVEYOR240_WATER_STATS])
234-
if data:
235-
if new_log:
236-
water_stats_path = os.path.join(log_path, "Water_Stats.txt")
237-
with open(water_stats_path, 'ab') as f:
238-
f.write(data.msg_data)
239-
240-
print(f"Temperature: {(data.temperature * 9/5) + 32} F")
241-
print(f"Temperature: {data.temperature} C")
242-
print(f"Pressure: {data.pressure} bar")
243-
break
244-
else:
245-
print("Failed to get water stats data")
199+
print("Logging...\nCTRL+C to stop logging")
200+
else:
201+
print("CTRL-C to end program...")
202+
try:
203+
while True:
204+
# Set multiple packets to listen for
205+
data = mySurveyor240.wait_message([definitions.SURVEYOR240_ATOF_POINT_DATA,
206+
definitions.SURVEYOR240_ATTITUDE_REPORT,
207+
definitions.SURVEYOR240_YZ_POINT_DATA,
208+
definitions.SURVEYOR240_WATER_STATS])
209+
210+
## To watch pitch and roll data in real time while recording, uncomment this block
211+
# if data.message_id == definitions.SURVEYOR240_ATTITUDE_REPORT:
212+
# # Print pitch and roll data
213+
# vector = (data.up_vec_x, data.up_vec_y, data.up_vec_z)
214+
# pitch = math.asin(vector[0])
215+
# roll = math.atan2(vector[1], vector[2])
216+
# print(f"Pitch: {pitch}\tRoll: {roll}")
217+
218+
except KeyboardInterrupt:
219+
print("Stopping logging...")
246220

247221
# Stop pinging from Surveyor
248222
mySurveyor240.control_set_ping_parameters(ping_enable = False)

generate/templates/omniscan450.py.in

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import time
1515
import math
1616
import struct
1717
import socket
18-
from datetime import datetime
18+
from datetime import datetime, timezone
1919
from pathlib import Path
2020

2121
# Imports for svlog header
@@ -187,6 +187,7 @@ class Omniscan450(PingDevice):
187187
"session_clients": [],
188188
"session_plan_name": None,
189189

190+
"is_recording": True,
190191
"sonarlink_version": "",
191192
"os_hostname": platform.node(),
192193
"os_uptime": None,
@@ -199,8 +200,8 @@ class Omniscan450(PingDevice):
199200
"process_uptime": time.process_time(),
200201
"process_arch": platform.machine(),
201202

202-
"timestamp": datetime.utcnow().isoformat() + "Z",
203-
"timestamp_timezone_offset": -time.timezone // 60
203+
"timestamp": datetime.now(timezone.utc).isoformat(),
204+
"timestamp_timezone_offset": datetime.now().astimezone().utcoffset().total_seconds() // 60
204205
}
205206

206207
json_bytes = json.dumps(content, indent=2).encode("utf-8")

0 commit comments

Comments
 (0)