Skip to content

From the Qt logger

The desktop Qt Logger (dvma.Logger(...)) has been removed. The browser-based web logger reached full parity and is now the single interactive interface. dvma.Logger(...) and dvma.Oscilloscope(...) no longer exist — accessing either raises an error that points here. From a notebook, the replacement is dvma.launch(settings). This page maps what you did in the Qt logger onto where it lives now.

The Python/notebook interface is unaffected

Everything you scriptMySettings, log_data, calculate_*, save_data/load_data, and plotting via DataSet.plot_*_data — is a stable compatibility contract and is unchanged. Only the interactive Qt windows (Logger, Oscilloscope) were removed. If you script your acquisition and analysis, keep doing so — see the Python interface guides.

Running the old Qt logger

The last version that still shipped the Qt GUI is preserved at the qt-final git tag. To run it, check out that tag (its [qt] extra still exists there):

git checkout qt-final
pip install -e ".[qt,soundcard]"

Why switch

  • No install for analysis. Students (and you) can open saved data in a browser with nothing to install — see the three modes.
  • Soundcard acquisition in the browser, and NI acquisition through a small local bridge, from the same UI.
  • A cleaner, more maintainable interface with the rough edges of the Qt plotting/view-switching logic left behind.

What maps where

Qt Logger Web logger
dvma.Logger(settings) dvma.launch(settings) — same settings; returns a Session handle instead of a window
MySettings(...) fields (device, fs, channels, stored_time, ...) Setup stage controls
Live Oscilloscope window Live scope + the persistent mini-monitor (docked on every stage)
Record button / log_data Acquire stage (with pretrigger arm and output stimulus)
Time view Time stage
FFT view Frequency stage (FFT / PSD / CSD)
TF view (+ coherence) TF stage (+ coherence overlay)
Sonogram view Sonogram stage (+ damping fit)
SDOF modal fitting Fit stage (Fit 1/2/3, Reject, Refine, per-mode edit)
Generate output panel Acquire output stimulus (sweep / white / Gaussian) — bridge only
Save / Load / Export Export stage (.dvma, .mat, CSV, PNG/PDF)
Per-channel calibration Calibrate dialog (sensitivity → cal factors, units)
Scaling tool — Best Match, x(iω) TF card's scaling group (Best match + x(iω)^p)

x(iω) is non-destructive in the web logger

The Qt Scaling tool's x(iω) button called multiply_by_power_of_iw, which mutates the stored FreqData/TfData in place. The web logger's x(iω)^p is instead a per-set display transform — it changes only what is plotted, never the stored arrays, so a set that recomputes or is re-fitted keeps its measured values. (It is persisted per set in the .dvma file and does not feed the modal fit.) Best Match matches Qt's maths and, like Qt's set_calibration_factors_all, writes through the calibration path — in the web logger that is the per-source-channel channel_cal_factors.

The data model is identical underneath: the web logger's analysis runs the very same pydvma core in every mode — a pyodide worker in the browser (Pages, JupyterLite), or natively in the pydvma serve process when the app is served locally (the default there; ?enginehost= overrides, and the browser engine is the automatic fallback) — so FFT/TF/windowing/modal maths are never reimplemented — results match the desktop tool exactly.

The notebook front door: dvma.launch

A labsheet notebook used to set MySettings and pop the Logger window:

import pydvma as dvma
settings = dvma.MySettings(device_driver='soundcard', fs=44100,
                           channels=2, stored_time=2.0)
logger = dvma.Logger(settings)          # Qt window (removed; see qt-final)

dvma.launch takes the same MySettings and opens the web logger instead — but where Logger gave you a window, launch gives you a handle to the running session, so the notebook and the browser can pass data back and forth:

import pydvma as dvma

settings = dvma.MySettings(device_driver='soundcard', fs=44100,
                           channels=2, stored_time=2.0)
session = dvma.launch(settings)     # starts the server, opens the browser
print(session.url)                  # http://127.0.0.1:<port>/ (also printed)

# ... record in the browser: Setup is prefilled, press Log on Acquire ...

data = session.data                 # a fresh DataSet, captures included
data.calculate_fft_set(window='hann')
data.plot_freq_data()

session.push(data)                  # hand it back; the app offers to reload
session.close()

Installed from the [serve] extra (pip install "pydvma[serve,soundcard]"), launch starts the whole pydvma-serve stack — acquisition bridge, native compute engine, session journal and the embedded UI — on a background thread inside your kernel, so it works the same from a plain script and from inside Jupyter (whose kernel already runs an event loop of its own). MySettings prefills Setup exactly as --settings does, and supplies the acquisition driver.

Useful arguments: open_browser=False starts the server without opening a tab (session.url still tells you where it is), and port=8760 pins the port instead of taking a free one — handy for a fixed bookmark, at the cost of failing if something already holds it. Session is a context manager, so with dvma.launch(settings) as session: stops the server on the way out.

Getting data in and out

session.data materialises a fresh DataSet on every access — it is a copy, not a live reference, so anything you do to it changes nothing until you push it back. session.push(data) is the only write path, and it merges rather than replaces:

  • Every item carries a unique_id — captures and derived results alike — so one you pulled, modified and pushed back replaces the stored copy in place. The pull → filter → push round trip updates data where it sits instead of duplicating it, and pushing an unmodified pull changes nothing at all.
  • A result you build in the notebook is a new item with a new id, so it appends. Compute the same spectrum twice, from two separate pulls, and you get two copies; push the same object twice and the second push simply replaces the first.
  • Derived items and modal fits inside a file written by an older pydvma have no id (they were not given one until the derived-data save round), so a repeated push of those does append copies. Recompute them — or re-Save in the app — to give them ids.
  • An item you pulled carries the app's display state for it (channel labels, units, the set's analysis settings) invisibly through the round trip, so pushing it back preserves what you see on screen. An item you built in the notebook has none, so replacing a stored item with a newly built one clears that state and the app re-seeds its defaults on reload.

A connected app is never silently overwritten: it raises a "pydvma session updated from a notebook — reload?" offer (with an empty tray it simply loads the pushed session).

What session.data holds is the session document: captures, loaded sets, and any analysis the app has materialised into it. Pressing Save Dataset in the app is what materialises the FFT and TF views (see what Save writes), and the save posts the updated session straight to the server — so a session.data read any time after that save arrives with freq_data_list / tf_data_list populated, each carrying the settings it was computed with. Analysis the app computed but has not saved is not in the document — compute what you need in the notebook.

Those materialised results carry their own unique_id, like captures do, so pulling a session and pushing it back replaces them in place. A pull → push of an unmodified session changes nothing. (One exception: derived items inside a file written by an older pydvma have no id, so a repeated push of those does append copies — recompute or re-Save them in the app to give them ids.)

The reverse direction has a trap worth knowing: if you modify a capture's samples and push it back, the app's stored spectra were computed from the old samples. They are not silently trusted — the app compares each stored result's source signature against the data it now has and flags the measurement with ⚠ source changed, one click from a recompute.

session.close() stops the server; session.data still reads afterwards (pulling your data out of a session you have finished with is the point), but push does not, because there is no longer an app to notify.

A worked notebook flow runs through the whole cycle.

Without a notebook

You do not need a kernel at all. The other two routes are unchanged (see the three modes):

  • Analysis / soundcard, no install — just open the Pages app at https://torebutlin.github.io/pydvma/app/ and configure the capture in Setup. Nothing to launch from the notebook.

  • Lab PC with NI (or a local soundcard bridge) — start the bridge from a terminal:

pydvma-serve --driver nidaq --open      # or --driver soundcard

This is the same server dvma.launch runs, minus the Session handle: it serves the app and the WebSocket bridge from one local port and opens your browser at it.

Pre-seeding settings with --settings

pydvma-serve --settings config.json pre-loads a JSON file and publishes it at the bridge's /config endpoint:

pydvma-serve --driver nidaq --settings my_lab_config.json --open

The app fetches /config on start. A JSON document there is both the "opened through pydvma serve" signature (so the app automatically switches on live acquisition rather than falling back to browser Web Audio) and a launch configuration the app consumes: the served MySettings fields pre-fill Setup and Acquire — sample rate, channels, duration, the selected device (device_driver + device_index, matched against the enumerated devices), the pretrigger (pretrig_samples/threshold/channel/timeout, armed when a sample count is given), the output stimulus, the NI voltage rails (VmaxNI/output_VmaxNI), and IEPE/terminal settings where the NI group is shown. A toast confirms "Settings loaded from pydvma-serve --settings".

This is the direct analogue of handing MySettings to Logger(...) — the notebook (or a launch script) starts the bridge with your lab's configuration and the student opens the tab already set up. The prefill runs once at boot and only fills defaults, so any change you make afterwards in Setup is never overwritten; unknown or malformed fields are skipped silently.

The wire accepts more than the app draws. The bridge's settings whitelist is derived from the MySettings signature, so fields with no Setup control of their own still reach the recorder — capture_fs, oversample and lpf_on (the capture rate and its oversampling rule), and input_gain_db / input_mode (the stated preamp gain that derives an audio interface's input full scale). Put them in the settings file and every capture taken through that bridge carries them.

Bridge auto-detection

You do not normally need to configure anything: opening the app through pydvma serve is detected automatically via /config. Advanced overrides also exist — a ?bridge=ws://host:port/ws URL parameter, or a window.__pydvma_bridge global — but they are rarely needed.

Files carry over

Data saved by the Qt logger opens directly in the web logger, and vice versa — both read and write the same .dvma format, and both still open legacy .npy pickle files from pydvma ≤ 1.4.0. You can record on the lab PC, save a .dvma, and re-open it later in the no-install browser app at home.

If you still need the old Qt logger

The Qt logger is gone from the current release, but nothing is lost: the last version that shipped it is preserved at the qt-final git tag.

git checkout qt-final
pip install -e ".[qt,soundcard]"   # the [qt] extra exists on that tag

The web logger's round-6 parity audit closed the last gaps, which is why the desktop GUI was retired. If you hit something the web logger does not yet do, please report it.