OpenDPD Studio 2.4.1¶
OpenDPD 2.4.1 adds the NR FR1 standard test models of 3GPP TS 38.141-1 to the Signal Generator. The 5G NR tab now opens on a test-model picker instead of the preset matrix. The models take the parameters that the standard leaves open and that a baseband stimulus can express: duplex mode, up to 16 carriers, subcarrier spacing and channel bandwidth per carrier, gaps between carriers, rank for the time-alignment model, the PN-sequence reading, and the NB-IoT settings of NR-N-TM; Studio also lets you override a carrier's cell identity, which the standard fixes. Some open parameters are not offered (channel-spacing variants, the NRTC configurations as such, NR-N-TM with TDD, PN23 polarity), and at default settings 35 of the 80 combinations of duplex mode, subcarrier spacing and bandwidth (10 of 40 FDD, 25 of 40 TDD) for a single carrier are shortened below the standard's 10 or 20 ms (see Known limitations). It is a patch release: apart from the test-model panel, the Signal Generator messages and accessible control names listed under What is new and the way the hosted API counts invalid Signal Generator requests against its rate limit (see Upgrade notes), Studio, the CLI, the Python SDK and the MATLAB toolbox keep their behaviour.
uv pip install --python .venv --upgrade "opendpd==2.4.1" --torch-backend=auto
uv run --no-project --python .venv opendpd gui
The wheel includes Studio. The output is a baseband stimulus that follows the standard's definition of each model. It is not a conformance waveform, was not compared with an instrument, and its EVM is an ideal-demodulation diagnostic (see Not verified). The guide NR standard test models documents the parameters, conventions and limits of the test models.
What is new¶
- Eleven models of TS 38.141-1 V18.14.0 clause 4.9.2.2: NR-FR1-TM1.1, 1.2, 2, 2a, 2b, 3.1, 3.1a, 3.1b, 3.2, 3.3 and NR-N-TM (NB-IoT operation in NR in-band), each with its clause, description, the tests it is defined for, and a read-only Defined by the standard table that resolves for your settings: N_RB, FFT size, resource-block-group size and indices, every user with modulation, PRB ranges and level, and the TDD pattern.
- Single and multi-carrier. Carriers are placed at the nominal channel spacing of TS 38.104 clause 5.4.1.2 (100 kHz-raster bands), centred at baseband DC, with equal power spectral density and cell identities 1, 2, … in ascending frequency unless overridden (an override is a Studio extension and adds a warning). An optional gap before a carrier (a multiple of 0.3 MHz) models a non-contiguous sub-block. Mixed subcarrier spacings are allowed with a warning.
- FDD and TDD. TDD follows table 4.9.2.2-1 (15 kHz D D D S U, 30 kHz D×7 S U U, 60 kHz D×14 S S U×4); flexible and uplink symbols are exactly zero samples. Standard duration: 10 ms FDD, 20 ms TDD.
- Two readings of the PN23 rules. Standard text (one stream per user, restarted every frame) or MATLAB 5G Toolbox (one stream per PDSCH allocation, never restarted), plus the standard's PN23 scope choice for TDD. In a single 10 ms FDD frame of TM1.1, 1.2 and 3.x the two give identical grids.
- Two layers for TM1.1 (ports 1000 and 1001, no precoding, as defined for the TAE test); the generator exports one of the two ports.
- NR-N-TM: the NR-FR1-TM1.1 signal with one NR resource block replaced by the NB-IoT test model of TS 36.141 (NPSS, NSSS, NRS, NPBCH, NPDCCH, NPDSCH), FDD at 15 kHz, 3–50 MHz, lower, upper or both edge positions. The boost X must reach the minimum of TS 38.104 table 6.3.4.2-1 for the host (6 dB for 3–20 MHz, 3 dB for 25 MHz and wider); the 12 dB maximum is a Studio limit.
- Capture control. Oversampling (automatic, 1, 2, 4 or 8) and capture length in subframes. Studio captures hold at most 1,000,000 samples, so wide or multi-carrier signals can be shortened to whole subframes; the panel, the analysis and the notes say so.
- Preview. The coverage label reads 3GPP test model; the allocation plot shows every carrier in MHz; per-carrier N_RB, cell ID, centre offset and EVM are in the analysis; the notes state the conventions, truncation and the definition of the shown EVM.
- The matrix is behind a button. Generic numerology matrix… reveals the 1,186-preset catalog's NR matrix, labelled as generic engineering stimuli that are not standard test models. Selecting matrix cells works as before and joins the same selection of up to 16 signals. Wi-Fi 6, Wi-Fi 7 and Custom signals are generated as before.
- Dataset names carry the model:
syn_pa_in_nrtm3p1a_bw98p31M_q256_c1_n1,nrntmfor NR-N-TM. - API.
GET /signal-generator/nr-test-modelsreturns the catalogue andPOST /signal-generator/nr-test-models/resolvereturns the complete validated generator configuration and a summary for a recipe.GeneratorConfighas an optionalnr_test_modelrecipe,GeneratorAnalysisan optional summary, andGeneratedSignal.coveragethe new valuestandard_test_model. - Other changes to the Signal Generator that apply to every signal.
- A selection whose samples add up to more than 4,000,000 now shows a warning with the totals instead of the general selection-limit notice, and Generate stays off.
- Refusals by the server are explained in plain words: the 4,000,000-sample batch limit, selecting the same signal twice (a new message: a dataset holds each signal once) and the temporary workspace limit.
- The Signal Generator page waits for the test-model catalogue request before it shows any family, Wi-Fi 6, Wi-Fi 7 and Custom included. If that request is refused (403 or 404) the 5G NR tab shows the matrix silently; after any other failure it shows the matrix with a one-line notice and a Retry button.
- The channel axis of the allocation plot of every signal uses whole-number ticks.
- On the hosted Studio,
POST /signal-generator/signalsand/signal-generator/batchescount a request against the per-network rate limit before they validate its body, so a client that repeatedly sends invalid requests, test-model recipes or not, receives 429 sooner than with 2.4.0 (see Upgrade notes).
- Accessible control names. The model cards separate the model name from its modulation in their text; workflow controls name both their symbol and step, and the language selector names its current language. The visible labels and accessible names agree under the updated axe audit.
- English-only server text. The labels of the panel are translated into the nine interface languages; the text written by the server for test models (descriptions, the table of fixed parameters, validation messages, warnings and notes) is English only.
Parameter ranges¶
| Parameter | Range |
|---|---|
| Models | NR-FR1-TM1.1, 1.2, 2, 2a, 2b, 3.1, 3.1a, 3.1b, 3.2, 3.3, NR-N-TM |
| Duplex | FDD, TDD (NR-N-TM: FDD) |
| Carriers | 1–16 (a Studio limit) |
| Subcarrier spacing | 15, 30, 60 kHz (NR-N-TM: 15 kHz) |
| Channel bandwidth | 15 kHz: 3–50 MHz · 30 kHz: 5–100 MHz · 60 kHz: 10–100 MHz, the 40 cells of TS 38.104 table 5.3.2-1 (NR-N-TM: 3–50 MHz) |
| Cell identity | automatic (1, 2, …) or 0–1007 per carrier (an override is a Studio extension) |
| Gap before carrier 2…n | 0 or a multiple of 0.3 MHz, up to 999.9 MHz (carriers and gaps together must fit the sample limit, about 835 MHz) |
| Layers / exported port | 1 or 2 (TM1.1 only) / 1000 or 1001 |
| PN23 convention / TDD scope | standard text or MATLAB / downlink symbols or all symbols |
| NB-IoT | lower, upper or both edge positions; boost X 6–12 dB for hosts of 3–20 MHz, 3–12 dB for 25 MHz and wider (default 6; 12 dB is a Studio limit) |
| Oversampling | automatic, 1, 2, 4, 8 |
| Capture length | standard (10 or 20 subframes, the default) or 1–20 subframes, at most 1,000,000 samples |
Also available as before for every signal: RMS, seed, RF carrier metadata, burst envelope, phase offset and jitter, frequency offset, I/Q gain and phase mismatch, DC offsets, SNR, clipping and the optional ideal filter (off for test models: the standard specifies no filtering or windowing for them).
Known limitations¶
- Shortened captures. A capture holds at most 1,000,000 samples. 35 of the 80 combinations are shortened at default settings: 10 of 40 FDD and 25 of 40 TDD. The 80 are the 40 (subcarrier spacing, bandwidth) cells of TS 38.104 table 5.3.2-1 in each duplex mode, for one carrier with automatic oversampling and the standard capture length; the count is the same for each of the ten NR-FR1 models, and NR-N-TM is never shortened. FDD: 30 and 60 kHz with 60–100 MHz keep 8 of 10 ms. TDD: 15, 30 and 60 kHz with 30–50 MHz keep 16 of 20 ms, and 30 and 60 kHz with 60–100 MHz keep 8 of 20 ms. Read the number as follows: a shortened capture has the standard's resource-block structure and is a valid PA and DPD stimulus, but it is not the standard-length signal, an 8 ms TDD capture never reaches the second radio frame, and Studio offers no route to the full duration for these combinations. The panel marks every shortened capture with a Shortened chip and a warning. The guide has the table.
- Open parameters that are not offered: the channel-spacing rules of TS 38.104 clause 5.4.1 other than the 100 kHz-raster rule of 5.4.1.2 and the operating-band dependence of its guard bands; the NRTC1–NRTC6 configurations as such and the NRTC6 ON-OFF patterns; NR-N-TM with TDD (the standard defines it in table 4.9.2.2-1a, Studio does not implement it); PN23 polarity and a PN stream shared by all users; more than one exported antenna port; and equal carrier power for NRTC3 (Studio always uses equal power spectral density). See What you cannot set.
Evidence¶
Unless a line says "in the repository", the MATLAB scripts and data behind these numbers are not stored in the repository.
- Resource grids against MATLAB R2026a (5G Toolbox 26.1), the author's run. MATLAB generated the ten FR1 models for FDD and TDD at 15, 30 and 60 kHz, several bandwidths (TM1.2 and TM3.3 at all 39 cells its wrapper accepts), cell IDs 1–1007 and 1–40 subframes: 438 configurations were compared with the package's grids with
data_convention="matlab"; 414 are bit-identical and the other 24 agree within 3.2×10⁻¹⁶ (one unit in the last place of the boosted amplitudes10^(P/20)of TM1.2 and TM3.3). For 58 of the 438 the MATLAB waveform was saved too: fed with MATLAB's grids, the package's OFDM modulator reproduces them to 4.4×10⁻¹⁷. This comparison used the MATLAB data convention. The default Standard text convention follows the standard's wording and gives different random bits for TDD, TM2/2a/2b and captures longer than one frame. - In the repository. SHA-256 digests of the 438 grids, rounded to 10⁻¹⁰ (so the test establishes agreement to about 10⁻¹⁰, not the bit-identical count), and a test (
tests/unit/test_nr_tm_grid_parity.py) that checks them. The repository holds no MathWorks code or data files; the oracle files hold numbers produced by public toolbox calls (hashes of the grids; the NB-IoT symbols and resource-element masks). - Fresh MATLAB runs of the pre-release review (a reviewer who had not written the generator). 106 random configurations (all ten models, 57 FDD and 49 TDD, oversampling 1–8, 12 of them two-layer TM1.1, 118 streams) compared sample by sample with the MATLAB data convention: largest error 2.7×10⁻¹⁵ of the RMS for the raw baseband and 3.0×10⁻⁷ for the exported single-precision samples; 117 resource-block-group references (TM1.2, TM3.2, TM3.3 at all 39 cells MATLAB accepts); 20 configurations at 3 MHz, which MATLAB's wrapper rejects (the 5 MHz model resized to 15 resource blocks, with PRB sets and powers from independently derived formulas); and 10 multi-carrier compositions built from MATLAB single-carrier waveforms, frequency-shifted and summed. All match the package to float rounding. A NumPy implementation written from the text of TS 38.141-1 and TS 38.211, without reading the package's code, matches the default Standard text convention in 58 cases (46 configurations over two frames and 12 two-layer cases) to 3.1×10⁻¹⁶ in the grid.
- Specification double entry. The resource-block-group selection, boost levels and power conservation of TM1.2, TM3.2 and TM3.3 for all 40 table cells were transcribed independently from page images of TS 38.141-1 and agree with the implementation and with closed forms written separately in the tests. N_RB equals TS 38.104 table 5.3.2-1 (V18.10.0 and V18.15.0, identical for the tables used here, and V18.9.0). PN23, Gold, the five QAM constellations and the DM-RS sequences are checked against literal implementations of the defining equations. At nominal spacing no ordered pair of table cells has overlapping occupied subcarriers (tightest gap 7.5 kHz, asserted by a test).
- Two layers. A two-layer TM1.1 grid (10 MHz, 15 kHz) obtained by setting two layers by hand on MATLAB's TM1.1 configuration is reproduced bit for bit on both ports; this case is not stored in the repository. The review repeated it with 12 configurations (above).
- NB-IoT building blocks against MATLAB LTE Toolbox. NPSS, NSSS, NRS, NPBCH, NPDCCH and NPDSCH symbols, resource-element sets and mapping order agree with MATLAB R2026a's NB-IoT functions to 10⁻¹² (NSSS 2×10⁻¹¹) for 13 combinations of cell identity, host cell ID and operation mode, and the NSSS sequence for 16 (N_ID, frame) pairs covering all four sequence tables (in the repository:
tests/data/nr_tm/nbiot_oracle.json). The puncturing, power split and placement into the NR grid have no reference to compare with. - Time domain and limits. Tests cover cyclic-prefix lengths, exact sample count, RMS, exact zeros in TDD uplink, demodulation of the output back to the grid, oversampling, carrier placement and equal power spectral density, and the refusal of invalid setups (for example 5 MHz at 60 kHz, rank 2 on TM3.1, overlapping carriers, more than 1,000,000 samples). Without impairments the shown EVM is about 10⁻⁶ %, the single-precision floor of the exported samples.
- Cost. Four 100 MHz carriers at 30 kHz (983,040 samples at 491.52 MS/s) synthesised in about 0.3 s with a peak below 0.4 GB on the development machine; up to eight 100 MHz carriers at 30 kHz fit (983.04 MS/s, 1 ms), nine are refused.
Upgrade notes¶
- Signal identities. Signals that are not test models keep their identifiers: their serialised configuration is byte-identical to 2.4.0 (the new
nr_test_modelkey is omitted when unset) and the hashed generator sources are unchanged. Saved signals, datasets, manifests and configuration files from 2.4.0 load unchanged, and a 2.4.0 configuration without the new key keeps working in the new panel. The identity of a test-model signal also covers the sources of the test-model package. - Rollback and downgrade. A workspace that contains 2.4.1 test-model signals cannot be read by 2.4.0: the strict 2.4.0 models reject the new
nr_test_modelkey and thestandard_test_modelcoverage value, and the signal list, the Signal Analyzer sources and datasets, the signal itself (GET and download) and a PA simulation on it answer 500. One such signal is enough to break the listing of the ordinary signals too. Before running 2.4.0 again on a local workspace, remove or archive thesignals/sg-*folders whosemanifest.jsonhas"coverage": "standard_test_model", or reset the workspace. On the hosted Studio the rollback restart clears the tenants (starting the public service removes every session directory); do not start 2.4.0 on a copy of a session directory that holds test-model signals. - Restart a running service. A Studio service that is still running 2.4.0 in a workspace must be stopped and started again after the upgrade: the Python SDK refuses to attach to a service of another version. Reload open browser tabs to get the new interface.
- Hosted Studio. The two new routes are on the public allowlist of this release; the hosted API must be redeployed to serve them. Until then the redeployed web build shows the NR matrix, as the test-model catalogue request is refused. The resolve route is a pure function of the recipe: it writes nothing to the workspace, takes no mutation or storage reservation and is rate-limited per network. For
resolve,/signal-generator/batchesand/signal-generator/signalsevery request is charged against the per-network rate before its body is judged, and the body is then validated in a worker thread (for a test-model recipe by arithmetic that builds no waveform; the plan of a recipe is derived once and memoised) and answered with 422 when invalid. This also changes the two ordinary generator routes for bodies that are not test-model recipes. 2.4.0 validated first and charged only requests that passed, so an invalid body cost nothing; now it counts, and a client that repeatedly posts invalid requests to/signal-generator/signalsor/signal-generator/batchesreceives 429rate_limitedas soon as the per-network budget of the minute (24 single-signal and 12 batch requests, counted together) is used up. Measured with the public test client, 30 invalid posts of{"preset_id": "wifi7-20", "n_samples": 5}to/signal-generator/signalsgave 24 × 422 and 6 × 429 with 2.4.1, and 30 × 422 with 2.4.0. Valid requests are charged as before. Details and capacity numbers are in Public Studio. - MATLAB toolbox. The toolbox stays at 2.4.0 and is compatible: it uses capability strings, not the Python version, and reads none of the new fields. MATLINK opens the same Signal Generator, so its NR default is now the standard test model.
Matlab/toolbox/docs/gui.mdwas reworded; no new.mltbxis released. - API contract.
docs/contracts/openapi.jsonand the generated frontend types contain the additive changes. No new dependency.
Not verified¶
- Any instrument or hardware. No signal was compared with the output of a commercial signal generator or with a base station, and no conformance measurement was made; the PN23 polarity of other instruments is unknown.
- Full Python 3.10, 3.11 and 3.12 suites were not run locally (development machine: Python 3.13); 53 targeted hostile-input cases also passed in a separate Python 3.11 environment. The changed Python files were checked to parse with the Python 3.10 grammar. The compute suite covers 3.10–3.13 in CI; its results are recorded on the pull request.
- Browsers. Local functional checks used scripted headless Chromium:
scripts/verify_signal_generator.mjsat 1366×768, 1920×1080 and 390×844, and Playwright against a real local backend. The CI accessibility journey also opens the NR test-model panel with captured backend catalogue/resolve responses, checks its collapsed and expanded details, and checks a 390×844 viewport in Chromium, Firefox and WebKit; see the pull request for results. This checks the controls and their labels against a mocked API, rather than waveform generation in those browsers. No screen reader or real touch device was used. - Translations. The new panel labels in the eight non-English interface languages have not been reviewed by native speakers, and the server-side text is English only.
- Independent references are missing for the NR-N-TM puncturing, power split and placement, and for the Standard text PN reading beyond what the from-the-text implementation shows (MATLAB departs from the text there). The 3 MHz cells and the multi-carrier compositions were checked against references that the reviewer built from MATLAB components, not against a MATLAB test model. No MATLAB release other than R2026a was used; TS 38.141-1 versions other than V18.14.0 were not compared, and the NB-IoT clauses were read in TS 36.141 V19.1.0 although the NR specification cites it without a version.
- EVM and level. The shown EVM is an ideal-demodulation diagnostic, not clause 6.5.3 receiver EVM; levels are RMS-normalised, not calibrated power. SSB, CSI-RS, PT-RS, PRACH, channel coding, the NRTC test-configuration rules, NRTC6 ON-OFF patterns, the other channel-spacing rules of TS 38.104 clause 5.4.1, FR2 models (TS 38.141-2) and TDD for NR-N-TM are not implemented.
- CI, the repository's independent-review gate, the PyPI artifacts and the hosted deployment are not covered by this text; see the pull request and the GitHub release.
Release checks¶
Checks run on the source tree of the branch before the pull request was opened, on the development machine (Linux, Python 3.13):
tests/unitandtests/integrationwith-m "not extended", 147 files in eight parallel shards: 3,788 passed, 1 skipped (the NR-N-TM TDD case, refused by design), 1 failed. The local failure wastests/integration/test_research_review_flow.py::test_multiplot_sources_declarations_and_full_metric_view_reproduction, also observed on the 2.4.0 source during development. Independent isolated reruns passed on both revisions; its cause was not established. The final CI result is recorded on the pull request. The legacy identifier formula of signals that are not test models is pinned by a test, and a reviewer generated 48 ordinary presets (every 25th of the 1,186) with the 2.4.0 source and with this tree: signal id, I/Q hash, manifest and provenance are identical.- Frontend:
types:check,typecheck,lint,vitest(77 files, 482 tests) andbuildexit 0.scripts/export_openapi.py --check,scripts/capture_nr_tm_fixtures.py --check,scripts/build_matlab_docs.py --checkandmkdocs build --strictpass. - Browser:
scripts/verify_signal_generator.mjsagainst a real local backend at 1366, 1920 and 390 px, and scripted checks of every model, a TDD recipe, 16 carriers, the matrix button and the language catalogues (see Not verified). - Review before the pull request: six independent lenses (spec conformance of TM1.x/TM2.x, of TM3.x/NR-N-TM/multi-carrier, backend and security, frontend, packaging and documentation truth, and a fresh MATLAB comparison) each followed by two verifiers for every serious finding, then a second and a third round of checks of the fixes. The one high finding (validation of late-invalid test-model recipes on the hosted API kept the shared event loop busy:
/healthzmedian 286, 602 and 1,427 ms with 1, 2 and 4 attacking sessions against 1 ms idle) is fixed (1 ms in all three cases) and its load test is repeatable. The findings that were accepted as limits are in Known limitations.
Captured frontend responses now record the NumPy/SciPy versions and generator source hashes that participate in signal identity. Reproduce the byte-for-byte fixture check with the recorded library versions; regeneration preserves the full API and scientific payload while updating source-dependent identifiers.
The status of the continuous-integration jobs, of the repository's independent-review gate, of the PyPI artifacts and of the hosted deployment is recorded on the pull request and on the GitHub release, not in this file.
Differences from the plan¶
- Two readings of the standard's PN23 rules are offered because the text ("can be generated per user", restart "in the first allocated slot of each frame") and MATLAB's behaviour (one stream per allocation object, no restart) differ; the default follows the text.
- The nominal channel spacing uses TS 38.104 clause 5.4.1.2 for 100 kHz-raster bands. TS 38.141-1 refers to clause 5.4.1 without saying which sub-clause applies; the other rules (clause 5.4.1.1, the 15 kHz- and 60 kHz-raster variants and the operating-band guard bands) also give non-overlapping carriers and differ by a fraction of a MHz (two 20 MHz carriers: 20.0 MHz by clause 5.4.1.1, 19.98 or 19.995 MHz for 15 kHz-raster bands, against 19.8 MHz here); they are not offered. Studio reads the guard bands of table 5.3.3-1 by a fixed rule instead of the operating band, and a gap in multiples of 0.3 MHz reaches only those guard-band variants that give a larger spacing (What you cannot set).
- NR-N-TM is generated for FDD at 15 kHz only. The standard also defines a TDD form (table 4.9.2.2-1a) that Studio does not implement, and no reference implementation exists to check it against. The test-model definition and the NB-IoT receiver tables cover 15 kHz hosts of 3–50 MHz; TS 38.104 table 6.3.4.2-1 also lists wider channels, so the restriction follows the better-supported reading and is a Studio decision.
Deployment¶
Deploy the same revision to the API, private GPU agent and pinned worker image, then update STUDIO_REF in lab-emi/opendpd-site and deploy its Pages workflow. Follow Public Studio.