mso4000 0.1.0__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.
- mso4000-0.1.0.dist-info/METADATA +190 -0
- mso4000-0.1.0.dist-info/RECORD +6 -0
- mso4000-0.1.0.dist-info/WHEEL +5 -0
- mso4000-0.1.0.dist-info/licenses/LICENSE +22 -0
- mso4000-0.1.0.dist-info/top_level.txt +1 -0
- mso4000.py +313 -0
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mso4000
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Simple library for Tektronix MSO4000 series oscilloscopes
|
|
5
|
+
Author-email: Asaf Ayalon <ayalon.asaf.c0@s.mail.nagoya-u.ac.jp>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Keywords: oscilloscope,tektronix,mso4000,visa,measurement
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Intended Audience :: Science/Research
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.6
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Topic :: Scientific/Engineering
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
19
|
+
Requires-Python: >=3.6
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Requires-Dist: pyvisa>=1.11.0
|
|
23
|
+
Requires-Dist: numpy>=1.16.0
|
|
24
|
+
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: setuptools; extra == "dev"
|
|
26
|
+
Requires-Dist: wheel; extra == "dev"
|
|
27
|
+
Requires-Dist: twine; extra == "dev"
|
|
28
|
+
Dynamic: license-file
|
|
29
|
+
|
|
30
|
+
# MSO4000 Library
|
|
31
|
+
|
|
32
|
+
Simple, clean library for Tektronix MSO4000 series oscilloscopes.
|
|
33
|
+
|
|
34
|
+
## Features
|
|
35
|
+
|
|
36
|
+
- ā
Connection checking
|
|
37
|
+
- ā
Single waveform capture
|
|
38
|
+
- ā
Multiple waveform recording to CSV
|
|
39
|
+
- ā
Simple, clean API
|
|
40
|
+
|
|
41
|
+
## Installation
|
|
42
|
+
|
|
43
|
+
Install from PyPI:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
pip install mso4000
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Quick Start
|
|
50
|
+
|
|
51
|
+
### 1. Check Connection
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
from mso4000 import check_connection
|
|
55
|
+
|
|
56
|
+
success, info = check_connection()
|
|
57
|
+
if success:
|
|
58
|
+
print(f"Connected: {info}")
|
|
59
|
+
else:
|
|
60
|
+
print(f"Error: {info}")
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### 2. Record Waveforms to CSV (One-liner)
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
from mso4000 import quick_record
|
|
67
|
+
|
|
68
|
+
# Record 10 waveforms and save to CSV
|
|
69
|
+
filename = quick_record(channel=1, max_records=10)
|
|
70
|
+
print(f"Saved to: {filename}")
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### 3. Full Control
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
from mso4000 import MSO4000
|
|
77
|
+
|
|
78
|
+
# Create scope interface
|
|
79
|
+
scope = MSO4000(channel=1)
|
|
80
|
+
|
|
81
|
+
# Connect
|
|
82
|
+
if scope.connect():
|
|
83
|
+
# Capture single waveform
|
|
84
|
+
waveform = scope.capture_waveform()
|
|
85
|
+
print(f"Captured {len(waveform)} samples")
|
|
86
|
+
|
|
87
|
+
# Or record multiple to CSV
|
|
88
|
+
filename = scope.record_to_csv(max_records=20)
|
|
89
|
+
print(f"Saved to: {filename}")
|
|
90
|
+
|
|
91
|
+
# Close connection
|
|
92
|
+
scope.close()
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## API Reference
|
|
96
|
+
|
|
97
|
+
### `MSO4000` Class
|
|
98
|
+
|
|
99
|
+
#### `__init__(channel=1)`
|
|
100
|
+
Initialize the oscilloscope interface.
|
|
101
|
+
|
|
102
|
+
**Parameters:**
|
|
103
|
+
- `channel` (int): Channel number (1-4), default 1
|
|
104
|
+
|
|
105
|
+
#### `check_connection() -> Tuple[bool, Optional[str]]`
|
|
106
|
+
Check if oscilloscope is connected.
|
|
107
|
+
|
|
108
|
+
**Returns:**
|
|
109
|
+
- `(True, device_info)` if connected
|
|
110
|
+
- `(False, error_message)` if not connected
|
|
111
|
+
|
|
112
|
+
#### `connect() -> bool`
|
|
113
|
+
Connect to oscilloscope and configure for waveform transfer.
|
|
114
|
+
|
|
115
|
+
**Returns:**
|
|
116
|
+
- `True` if successful
|
|
117
|
+
- `False` if failed
|
|
118
|
+
|
|
119
|
+
#### `capture_waveform() -> Optional[np.ndarray]`
|
|
120
|
+
Capture a single waveform.
|
|
121
|
+
|
|
122
|
+
**Returns:**
|
|
123
|
+
- `numpy.ndarray` of voltage values, or `None` if failed
|
|
124
|
+
|
|
125
|
+
#### `record_to_csv(filename=None, max_records=None, delay=0.5) -> Optional[str]`
|
|
126
|
+
Record multiple waveforms and save to CSV.
|
|
127
|
+
|
|
128
|
+
**Parameters:**
|
|
129
|
+
- `filename` (str, optional): Output filename (auto-generated if None)
|
|
130
|
+
- `max_records` (int, optional): Maximum waveforms to record (None = until Ctrl+C)
|
|
131
|
+
- `delay` (float): Delay between captures in seconds (default 0.5)
|
|
132
|
+
|
|
133
|
+
**Returns:**
|
|
134
|
+
- Filename of saved CSV, or `None` if failed
|
|
135
|
+
|
|
136
|
+
#### `close()`
|
|
137
|
+
Close connection to oscilloscope.
|
|
138
|
+
|
|
139
|
+
### Convenience Functions
|
|
140
|
+
|
|
141
|
+
#### `check_connection() -> Tuple[bool, Optional[str]]`
|
|
142
|
+
Quick function to check connection.
|
|
143
|
+
|
|
144
|
+
#### `quick_record(channel=1, max_records=None, filename=None) -> Optional[str]`
|
|
145
|
+
Quick function to connect and record waveforms.
|
|
146
|
+
|
|
147
|
+
**Parameters:**
|
|
148
|
+
- `channel` (int): Channel number (1-4)
|
|
149
|
+
- `max_records` (int, optional): Maximum waveforms (None = until Ctrl+C)
|
|
150
|
+
- `filename` (str, optional): Output filename (auto-generated if None)
|
|
151
|
+
|
|
152
|
+
**Returns:**
|
|
153
|
+
- Filename of saved CSV, or `None` if failed
|
|
154
|
+
|
|
155
|
+
## CSV Output Format
|
|
156
|
+
|
|
157
|
+
The CSV file contains:
|
|
158
|
+
- **Column 1:** Sample index (0 to N-1)
|
|
159
|
+
- **Column 2:** Time in seconds (relative to waveform start)
|
|
160
|
+
- **Columns 3+:** Voltage values for each recorded waveform
|
|
161
|
+
|
|
162
|
+
Example:
|
|
163
|
+
```csv
|
|
164
|
+
Sample_Index,Time_sec,Waveform_1_V,Waveform_2_V,Waveform_3_V
|
|
165
|
+
0,0.000000,-0.123456,0.234567,-0.345678
|
|
166
|
+
1,0.000010,-0.125432,0.236789,-0.347890
|
|
167
|
+
...
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## Examples
|
|
171
|
+
|
|
172
|
+
See `example_mso4000.py` for complete examples.
|
|
173
|
+
|
|
174
|
+
## Requirements
|
|
175
|
+
|
|
176
|
+
- Python 3.6+
|
|
177
|
+
- pyvisa
|
|
178
|
+
- numpy
|
|
179
|
+
|
|
180
|
+
## Notes
|
|
181
|
+
|
|
182
|
+
- The library automatically selects USB resources (skips serial ports)
|
|
183
|
+
- Uses binary encoding (RIBINARY) for efficient data transfer
|
|
184
|
+
- Default timeout is 30 seconds for waveform transfers
|
|
185
|
+
- Press Ctrl+C to stop recording
|
|
186
|
+
|
|
187
|
+
## License
|
|
188
|
+
|
|
189
|
+
MIT License - See LICENSE file for details
|
|
190
|
+
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
mso4000.py,sha256=z6MNxykotPq9fFsd681l3uCDudVYjOqSyM_uB33YwJI,10448
|
|
2
|
+
mso4000-0.1.0.dist-info/licenses/LICENSE,sha256=8_up1FX6vk2DRcusQEZ4pWJGkgkjvEkD14xB1hdLe3c,1067
|
|
3
|
+
mso4000-0.1.0.dist-info/METADATA,sha256=w3CXDeUypQwCPFNejVe6qqzICsumThfDQmxcROcNcDU,4666
|
|
4
|
+
mso4000-0.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
5
|
+
mso4000-0.1.0.dist-info/top_level.txt,sha256=fl0KrUGzNDpxXFv9VzGSrI5WswtF3DqQeLMdHQ3slN0,8
|
|
6
|
+
mso4000-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Your Name
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
mso4000
|
mso4000.py
ADDED
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Simple library for Tektronix MSO4000 series oscilloscopes
|
|
3
|
+
Provides connection checking and waveform recording to CSV
|
|
4
|
+
"""
|
|
5
|
+
import pyvisa
|
|
6
|
+
import numpy as np
|
|
7
|
+
import time
|
|
8
|
+
import csv
|
|
9
|
+
from datetime import datetime
|
|
10
|
+
from typing import Optional, Tuple, List
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class MSO4000:
|
|
14
|
+
"""Simple interface for Tektronix MSO4000 series oscilloscopes"""
|
|
15
|
+
|
|
16
|
+
def __init__(self, channel: int = 1):
|
|
17
|
+
"""
|
|
18
|
+
Initialize MSO4000 interface
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
channel: Channel number (1-4)
|
|
22
|
+
"""
|
|
23
|
+
self.channel = channel
|
|
24
|
+
self.scope = None
|
|
25
|
+
self.rm = None
|
|
26
|
+
self.timebase = None
|
|
27
|
+
self.ch_scale = None
|
|
28
|
+
|
|
29
|
+
def check_connection(self) -> Tuple[bool, Optional[str]]:
|
|
30
|
+
"""
|
|
31
|
+
Check if oscilloscope is connected and accessible
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
Tuple of (success: bool, device_info: str or None)
|
|
35
|
+
"""
|
|
36
|
+
try:
|
|
37
|
+
self.rm = pyvisa.ResourceManager()
|
|
38
|
+
resources = self.rm.list_resources()
|
|
39
|
+
|
|
40
|
+
# Prefer USB resources
|
|
41
|
+
usb_resources = [r for r in resources if r.startswith('USB')]
|
|
42
|
+
if not usb_resources:
|
|
43
|
+
return False, "No USB oscilloscope found"
|
|
44
|
+
|
|
45
|
+
resource_name = usb_resources[0]
|
|
46
|
+
self.scope = self.rm.open_resource(resource_name)
|
|
47
|
+
self.scope.timeout = 5000
|
|
48
|
+
|
|
49
|
+
# Query identification
|
|
50
|
+
idn = self.scope.query("*IDN?")
|
|
51
|
+
device_info = idn.strip()
|
|
52
|
+
|
|
53
|
+
return True, device_info
|
|
54
|
+
|
|
55
|
+
except Exception as e:
|
|
56
|
+
return False, f"Connection error: {e}"
|
|
57
|
+
|
|
58
|
+
def connect(self) -> bool:
|
|
59
|
+
"""
|
|
60
|
+
Connect to oscilloscope and configure for waveform transfer
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
True if successful, False otherwise
|
|
64
|
+
"""
|
|
65
|
+
success, info = self.check_connection()
|
|
66
|
+
if not success:
|
|
67
|
+
print(f"ā {info}")
|
|
68
|
+
return False
|
|
69
|
+
|
|
70
|
+
try:
|
|
71
|
+
print(f"ā Connected to: {info}")
|
|
72
|
+
|
|
73
|
+
# Configure for waveform transfer
|
|
74
|
+
self.scope.write("*CLS")
|
|
75
|
+
self.scope.write(f":DATA:SOURCE CH{self.channel}")
|
|
76
|
+
self.scope.write(":DATA:ENCDG RIBINARY")
|
|
77
|
+
self.scope.write(":WFMOUTPRE:BYT_NR 2")
|
|
78
|
+
self.scope.write(":DATA INIT")
|
|
79
|
+
|
|
80
|
+
# Get scale info
|
|
81
|
+
self.timebase = float(self.scope.query(":HORIZONTAL:SCALE?"))
|
|
82
|
+
self.ch_scale = float(self.scope.query(f":CH{self.channel}:SCALE?"))
|
|
83
|
+
|
|
84
|
+
print(f"ā Configured: Timebase={self.timebase} s/div, CH{self.channel}={self.ch_scale} V/div")
|
|
85
|
+
return True
|
|
86
|
+
|
|
87
|
+
except Exception as e:
|
|
88
|
+
print(f"ā Configuration error: {e}")
|
|
89
|
+
return False
|
|
90
|
+
|
|
91
|
+
def capture_waveform(self) -> Optional[np.ndarray]:
|
|
92
|
+
"""
|
|
93
|
+
Capture a single waveform from the oscilloscope
|
|
94
|
+
|
|
95
|
+
Returns:
|
|
96
|
+
Voltage array (numpy array) or None if failed
|
|
97
|
+
"""
|
|
98
|
+
if not self.scope:
|
|
99
|
+
print("ā Not connected. Call connect() first.")
|
|
100
|
+
return None
|
|
101
|
+
|
|
102
|
+
try:
|
|
103
|
+
self.scope.timeout = 30000 # 30 seconds for data transfer
|
|
104
|
+
|
|
105
|
+
# Trigger and get waveform
|
|
106
|
+
self.scope.write(":TRIGGER:FORCE")
|
|
107
|
+
time.sleep(0.2)
|
|
108
|
+
|
|
109
|
+
# Get data using write + read_raw for binary data
|
|
110
|
+
self.scope.write("CURVE?")
|
|
111
|
+
curve_data_bytes = self.scope.read_raw()
|
|
112
|
+
|
|
113
|
+
# Parse binary data
|
|
114
|
+
curve_data_str = curve_data_bytes.decode('latin1', errors='ignore')
|
|
115
|
+
|
|
116
|
+
if curve_data_str.startswith('#'):
|
|
117
|
+
digit_count = int(curve_data_str[1])
|
|
118
|
+
byte_count = int(curve_data_str[2:2+digit_count])
|
|
119
|
+
header_len = 2 + digit_count
|
|
120
|
+
|
|
121
|
+
# Extract binary data
|
|
122
|
+
binary_data = curve_data_bytes[header_len:header_len+byte_count]
|
|
123
|
+
|
|
124
|
+
# Convert to array (16-bit signed integers, big-endian)
|
|
125
|
+
voltage_array = np.frombuffer(binary_data, dtype='>i2')
|
|
126
|
+
|
|
127
|
+
# Scale to voltage
|
|
128
|
+
voltage_scaled = (voltage_array / 32768.0) * self.ch_scale * 5.0
|
|
129
|
+
else:
|
|
130
|
+
# ASCII fallback
|
|
131
|
+
values = [float(x) for x in curve_data_str.split(',')]
|
|
132
|
+
voltage_scaled = np.array(values)
|
|
133
|
+
|
|
134
|
+
return voltage_scaled
|
|
135
|
+
|
|
136
|
+
except Exception as e:
|
|
137
|
+
print(f"ā Error capturing waveform: {e}")
|
|
138
|
+
return None
|
|
139
|
+
|
|
140
|
+
def record_to_csv(self, filename: Optional[str] = None,
|
|
141
|
+
max_records: Optional[int] = None,
|
|
142
|
+
delay: float = 0.5) -> Optional[str]:
|
|
143
|
+
"""
|
|
144
|
+
Record multiple waveforms and save to CSV
|
|
145
|
+
|
|
146
|
+
Args:
|
|
147
|
+
filename: Output CSV filename (auto-generated if None)
|
|
148
|
+
max_records: Maximum number of waveforms to record (None = until interrupted)
|
|
149
|
+
delay: Delay between captures in seconds
|
|
150
|
+
|
|
151
|
+
Returns:
|
|
152
|
+
Filename of saved CSV or None if failed
|
|
153
|
+
"""
|
|
154
|
+
if not self.scope:
|
|
155
|
+
print("ā Not connected. Call connect() first.")
|
|
156
|
+
return None
|
|
157
|
+
|
|
158
|
+
if filename is None:
|
|
159
|
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
160
|
+
filename = f"waveform_data_{timestamp}.csv"
|
|
161
|
+
|
|
162
|
+
all_data = []
|
|
163
|
+
timestamps = []
|
|
164
|
+
record_count = 0
|
|
165
|
+
|
|
166
|
+
print(f"\nš Recording waveforms... (Press Ctrl+C to stop)")
|
|
167
|
+
print(f" Output: {filename}\n")
|
|
168
|
+
|
|
169
|
+
try:
|
|
170
|
+
while True:
|
|
171
|
+
if max_records and record_count >= max_records:
|
|
172
|
+
print(f"\nā Record limit reached ({max_records} records)")
|
|
173
|
+
break
|
|
174
|
+
|
|
175
|
+
print(f"[{record_count+1}] Capturing... ", end="", flush=True)
|
|
176
|
+
|
|
177
|
+
voltage_data = self.capture_waveform()
|
|
178
|
+
|
|
179
|
+
if voltage_data is not None:
|
|
180
|
+
all_data.append(voltage_data)
|
|
181
|
+
timestamps.append(time.time())
|
|
182
|
+
record_count += 1
|
|
183
|
+
|
|
184
|
+
min_v = voltage_data.min()
|
|
185
|
+
max_v = voltage_data.max()
|
|
186
|
+
mean_v = voltage_data.mean()
|
|
187
|
+
print(f"ā {len(voltage_data):6d} samples | "
|
|
188
|
+
f"Min: {min_v:8.4f}V | Max: {max_v:8.4f}V | Mean: {mean_v:8.4f}V")
|
|
189
|
+
|
|
190
|
+
time.sleep(delay)
|
|
191
|
+
else:
|
|
192
|
+
print("ā FAILED")
|
|
193
|
+
time.sleep(1)
|
|
194
|
+
|
|
195
|
+
except KeyboardInterrupt:
|
|
196
|
+
print("\nā¹ Recording interrupted by user")
|
|
197
|
+
|
|
198
|
+
if not all_data:
|
|
199
|
+
print("ā No data to export!")
|
|
200
|
+
return None
|
|
201
|
+
|
|
202
|
+
# Export to CSV
|
|
203
|
+
print(f"\nš¾ Exporting to {filename}...")
|
|
204
|
+
|
|
205
|
+
try:
|
|
206
|
+
with open(filename, 'w', newline='') as csvfile:
|
|
207
|
+
max_samples = max(len(wf) for wf in all_data)
|
|
208
|
+
|
|
209
|
+
# Create header
|
|
210
|
+
headers = ['Sample_Index', 'Time_sec'] + \
|
|
211
|
+
[f'Waveform_{i+1}_V' for i in range(record_count)]
|
|
212
|
+
|
|
213
|
+
writer = csv.writer(csvfile)
|
|
214
|
+
writer.writerow(headers)
|
|
215
|
+
|
|
216
|
+
# Calculate time per point
|
|
217
|
+
num_points = len(all_data[0]) if all_data else 1
|
|
218
|
+
time_per_point = (self.timebase * 10.0) / num_points
|
|
219
|
+
|
|
220
|
+
# Write data
|
|
221
|
+
for sample_idx in range(max_samples):
|
|
222
|
+
row = [sample_idx, sample_idx * time_per_point]
|
|
223
|
+
|
|
224
|
+
for waveform in all_data:
|
|
225
|
+
if sample_idx < len(waveform):
|
|
226
|
+
row.append(waveform[sample_idx])
|
|
227
|
+
else:
|
|
228
|
+
row.append('')
|
|
229
|
+
|
|
230
|
+
writer.writerow(row)
|
|
231
|
+
|
|
232
|
+
print(f"ā Exported {record_count} waveforms, {max_samples} samples each")
|
|
233
|
+
|
|
234
|
+
import os
|
|
235
|
+
file_size = os.path.getsize(filename)
|
|
236
|
+
print(f"ā File size: {file_size / 1024 / 1024:.2f} MB")
|
|
237
|
+
|
|
238
|
+
return filename
|
|
239
|
+
|
|
240
|
+
except Exception as e:
|
|
241
|
+
print(f"ā Error exporting: {e}")
|
|
242
|
+
return None
|
|
243
|
+
|
|
244
|
+
def close(self):
|
|
245
|
+
"""Close connection to oscilloscope"""
|
|
246
|
+
if self.scope:
|
|
247
|
+
self.scope.close()
|
|
248
|
+
self.scope = None
|
|
249
|
+
print("ā Connection closed")
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
# Convenience functions
|
|
253
|
+
def check_connection() -> Tuple[bool, Optional[str]]:
|
|
254
|
+
"""
|
|
255
|
+
Quick function to check if oscilloscope is connected
|
|
256
|
+
|
|
257
|
+
Returns:
|
|
258
|
+
Tuple of (success: bool, device_info: str or None)
|
|
259
|
+
"""
|
|
260
|
+
scope = MSO4000()
|
|
261
|
+
return scope.check_connection()
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def quick_record(channel: int = 1, max_records: Optional[int] = None,
|
|
265
|
+
filename: Optional[str] = None) -> Optional[str]:
|
|
266
|
+
"""
|
|
267
|
+
Quick function to connect and record waveforms
|
|
268
|
+
|
|
269
|
+
Args:
|
|
270
|
+
channel: Channel number (1-4)
|
|
271
|
+
max_records: Maximum number of waveforms (None = until interrupted)
|
|
272
|
+
filename: Output CSV filename (auto-generated if None)
|
|
273
|
+
|
|
274
|
+
Returns:
|
|
275
|
+
Filename of saved CSV or None if failed
|
|
276
|
+
"""
|
|
277
|
+
scope = MSO4000(channel=channel)
|
|
278
|
+
|
|
279
|
+
if not scope.connect():
|
|
280
|
+
return None
|
|
281
|
+
|
|
282
|
+
try:
|
|
283
|
+
filename = scope.record_to_csv(filename=filename, max_records=max_records)
|
|
284
|
+
return filename
|
|
285
|
+
finally:
|
|
286
|
+
scope.close()
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
if __name__ == "__main__":
|
|
290
|
+
# Example usage
|
|
291
|
+
print("=" * 70)
|
|
292
|
+
print("MSO4000 Library - Example Usage")
|
|
293
|
+
print("=" * 70 + "\n")
|
|
294
|
+
|
|
295
|
+
# Check connection
|
|
296
|
+
print("[1] Checking connection...")
|
|
297
|
+
success, info = check_connection()
|
|
298
|
+
if success:
|
|
299
|
+
print(f"ā {info}\n")
|
|
300
|
+
else:
|
|
301
|
+
print(f"ā {info}\n")
|
|
302
|
+
exit(1)
|
|
303
|
+
|
|
304
|
+
# Record waveforms
|
|
305
|
+
print("[2] Recording waveforms...")
|
|
306
|
+
scope = MSO4000(channel=1)
|
|
307
|
+
|
|
308
|
+
if scope.connect():
|
|
309
|
+
filename = scope.record_to_csv(max_records=5) # Record 5 waveforms
|
|
310
|
+
if filename:
|
|
311
|
+
print(f"\nā Success! Data saved to: {filename}")
|
|
312
|
+
scope.close()
|
|
313
|
+
|