WindTunnelControl.ps1 (code/wind_tunnel_control/)¶
A PowerShell + Windows Forms GUI that controls the VFD directly over Modbus/TCP. ~19 KB of script, one file, no dependencies beyond what ships with Windows 11.
This is the short-term replacement for AeroWare's broken VFD path. It came out of WT_MS_2 (2026-05-13) once AeroWare's Modbus path was confirmed unrepairable in place.
For the long-term replacement see ADCL WinSoft; for usage see Operations → Start via PowerShell.
Directory layout¶
code/wind_tunnel_control/
├── README.md
├── WindTunnelControl.ps1 ← the GUI itself
├── deploy.ps1 ← copy WindTunnelControl.ps1 + .cmd launcher to E:\Wind_tunnel\AeroWare\
├── ref1_sweep.ps1 ← calibration: write REF1 values, log resulting drive frequency
└── rpm_velocity_sweep.ps1 ← calibration: sweep RPM, log static-ring-derived wind speed
Modbus client (in-script)¶
Hand-rolled because Windows PowerShell does not ship with a Modbus library. ~50 lines.
- One TCP socket opened per call —
New-Object System.Net.Sockets.TcpClient, write the Modbus frame, read the reply, dispose. Simple, no connection pooling. - Supports FC
0x03(read holding registers) and FC0x06(write single register). - Parses the MBAP header; checks exception codes; throws on
0x83/0x86. - Helper
Read-Param -Group N -Index Mcomputes the address asN × 100 + M − 1.
The fresh-socket-per-call pattern is acceptable at the tool's 4 Hz polling rate — that is ~8 sockets/second to the RETA-01 and the adapter does not complain. It would not be acceptable at the higher rates ADCL WinSoft can run.
Startup probe¶
On launch the tool reads parameter 99.08 MOTOR NOM SPEED to know the motor nameplate RPM. If the read fails, the tool falls back to a hardcoded 1800 RPM and surfaces the failure on the status banner. The fallback exists because we never want the tool to refuse to launch on a single Modbus read failure — but it is also a footgun: a wrong nameplate value means the displayed speed is wrong. The status banner is the user's signal that the probe failed.
GUI¶
Windows Forms, not WPF. Single window with:
- Status banner at the top with
VFD: 192.168.50.10:502 (Unit ID 1). - Live readout polled at 4 Hz:
- Speed RPM (decoded from ACT1)
- Frequency Hz (decoded from drive parameters)
- Current A (decoded from ACT2)
- Raw CW / REF1 / SW hex values
- Decoded status word: RDY_ON · RDY_RUN · RDY_REF · TRIPPED · OFF2 · OFF3 · SWC_INH · ALARM · AT_SP · REMOTE · ABOVE
- RPM input with Apply Setpoint button.
- Raw REF1 escape hatch for calibration overrides.
- Two radio buttons: RPM control (enabled), Wind Speed control (disabled, phase 2).
- Three action buttons: Start Tunnel (with a confirmation dialog), Stop, Emergency Stop.
- Big status text:
Motor: stoppedorMotor: running at N RPM.
Start sequence¶
Identical to ADCL WinSoft (the calibration constant 22.42 is the same):
The Start button shows a confirmation dialog first, asking the operator to confirm the test section is clear, before issuing any writes.
Stop and E-Stop¶
- Stop writes
CW = 0x0476. The drive ramps per parameter22.02 DECEL TIME. - Emergency Stop writes
CW = 0x0000plusREF1 = 0. The drive coasts (no ramp). - Window close issues a best-effort
Stopfollowed byCW = 0x0000. Do not rely on this; the operator must stop the motor explicitly and verify zero RPM before closing.
Calibration scripts¶
ref1_sweep.ps1 and rpm_velocity_sweep.ps1 are the two scripts that produced the calibration constants used by both control tools:
ref1_sweep.ps1walks REF1 from 0 to a target maximum, polling the drive's frequency feedback, writing pairs to a CSV. This produced theREF1 = drive_RPM × 22.42fit.rpm_velocity_sweep.ps1walks RPM through a coarse set of setpoints, recording the static-ring-derived wind speed. This produced theMPH ≈ 0.0822 × drive_RPM − 13.14fit valid above ~300 RPM.
The CSV outputs from the 2026-05-13 sweeps live in WT_MS_2/captures/:
ref1_sweep_20260513_143847.csvrpm_velocity_20260513_144742.csv
When the calibration needs to be refreshed (after hardware changes, after a motor swap, after a long idle period), re-run these scripts and update the constants in both:
code/adcl_winsoft/src/adcl_winsoft/vfd/controller.py(REF1_PER_RPM)code/wind_tunnel_control/WindTunnelControl.ps1(the equivalent constant)
See Maintenance → Calibration refresh for the procedure.
Deployment¶
deploy.ps1 copies WindTunnelControl.ps1 and a .cmd launcher into E:\Wind_tunnel\AeroWare\. The destination directory is the legacy AeroWare install path; placing the new tool there gives the operator a single folder to look in.
What this tool intentionally lacks¶
- DAQ. The cDAQ is not touched.
- Calibration application. Raw drive register values are displayed; no engineering-unit conversions.
- Data recording. There is no CSV output.
- Authentication. Anyone who can run the script can spin the motor.
- Wind-speed setpoint. The radio button is in the UI but the write logic is not implemented — phase 2 needs closed-loop PID against cDAQ pressure feedback, which means cDAQ access, which is what ADCL WinSoft has and this tool does not.