This document describes the main runtime flows in the OsmoWeb BTS Demo. It focuses on what happens in the browser UI and how the local frontend wrapper coordinates shared Osmo/WebSDR services.
| Owner | Responsibility |
|---|---|
OsmoMain.vue |
Owns UI state, user actions, polling, telemetry subscription, and cleanup. |
OsmoBtsTrx |
Wraps the shared TRX manager and exposes local methods for configure/start/stop/stats. |
@osmoweb/frontend-core/services |
Provides BTS allocation/update and release service calls. |
@osmoweb/frontend-core/osmo |
Provides the shared TRX manager instance. |
@websdr/frontend-core/telemetry |
Provides StreamMeter telemetry state. |
@websdr/vue3-components |
Provides SDR input and log UI components. |
@osmoweb/vue3-components |
Provides BTS input UI components and BTS state types. |
When OsmoMain.vue is created, it initializes runtime state before the component is mounted.
Initial setup:
- Create a
StreamMeterinstance. - Create a single
OsmoBtsTrxwrapper and pass the stream meter into it. - Create a tab-specific
btsInstanceId. - Load the stored radio selection from
localStorage. - Fall back to GSM EGSM 900 / ARFCN 975 when stored data is missing or invalid.
- Initialize BTS state as
configured.
On mount:
- Subscribe to stream meter updates.
- Connect
OsmoBtsTrx.onLogItemto the local log handler. - Register the
pagehidecleanup handler. - Call
osmoBtsTrx.reinitialize().
reinitialize() connects callbacks on the shared TRX manager and starts the worker with the current BTS and WebSocket URL values.
sequenceDiagram
participant UI as OsmoMain.vue
participant Storage as localStorage
participant Trx as OsmoBtsTrx
participant Manager as Shared TRX manager
participant Meter as StreamMeter
UI->>Storage: Load osmoweb:bts-config
UI->>Meter: subscribe(...)
UI->>Trx: onLogItem = addLineToLogArea
UI->>Trx: reinitialize()
Trx->>Manager: set callbacks
Trx->>Manager: startWorker({ bts, urls })
The radio configuration is edited through BtsInput.
When the user updates the BTS input:
BtsInputemits anupdateevent.handleBtsUpdate(...)receives the newBtsParams.- The new value is assigned to
btsConfig. - A reduced version is saved to
localStorage. btsStateis set toconfigured.
Only valid GSM selections are stored:
technologybandarfcn
The current start flow supports GSM only. getBtsUpdatePayload() rejects unsupported technology, missing band, or missing ARFCN before calling the shared service layer.
The SDR device is selected through SdrInput.
When the selected device changes:
- The
deviceref receivesdevName,vendorId, andproductId. - A watcher checks whether both IDs are non-zero.
OsmoBtsTrx.configure(...)stores the USB IDs.- If configuration fails,
osmoErroris updated.
sequenceDiagram
participant User
participant SdrInput
participant UI as OsmoMain.vue
participant Trx as OsmoBtsTrx
User->>SdrInput: Select SDR device
SdrInput-->>UI: device object
UI->>UI: Check vendorId and productId
UI->>Trx: configure({ vendorId, productId })
The Start button is enabled only when:
- BTS state is not
not-configured - a device is selected
- no start/stop operation is already in progress
When the user starts the BTS runtime:
handleBtsToggle()setsosmoBusytotrue.- The previous
osmoErroris cleared. getBtsUpdatePayload()validates GSM technology, band, and ARFCN.updateBts(...)is called withinstanceId,band, andarfcn.- The returned BTS runtime config is passed to
OsmoBtsTrx.configure(...). OsmoBtsTrx.configure(...)stores USB IDs and opens the BTS in the shared TRX manager.OsmoBtsTrx.start()opens WebSocket transport and the selected USB device.btsStatebecomesconnected.- The
btsRunningwatcher starts statistics polling. osmoBusyreturns tofalse.
sequenceDiagram
participant User
participant UI as OsmoMain.vue
participant Services as Osmoweb frontend services
participant Trx as OsmoBtsTrx
participant Manager as Shared TRX manager
User->>UI: Press Start
UI->>UI: osmoBusy = true
UI->>UI: validate GSM band and ARFCN
UI->>Services: updateBts({ instanceId, band, arfcn })
Services-->>UI: BTS runtime config
UI->>Trx: configure({ vendorId, productId, btsConfig })
Trx->>Manager: open_bts(id, band, arfcn, ipa, osmux_port)
UI->>Trx: start()
Trx->>Manager: open_ws(urls)
Trx->>Manager: open_usb(vendorId, productId)
UI->>UI: btsState = connected
UI->>UI: start stats polling
UI->>UI: osmoBusy = false
When the BTS is already running, the same control button stops it.
Stop sequence:
handleBtsToggle()detects thatbtsRunningistrue.stopAndReleaseBts()callsOsmoBtsTrx.stop().OsmoBtsTrx.stop()closes the shared TRX manager.releaseCurrentBts()callsreleaseBts(instanceId).btsStatereturns toconfigured.- The
btsRunningwatcher stops statistics polling and clears stored stats.
sequenceDiagram
participant User
participant UI as OsmoMain.vue
participant Trx as OsmoBtsTrx
participant Services as Osmoweb frontend services
User->>UI: Press Stop
UI->>Trx: stop()
Trx-->>UI: Shared TRX manager closed
UI->>Services: releaseBts(instanceId)
UI->>UI: btsState = configured
UI->>UI: stop stats polling
Start and stop actions share the same error handling path.
When an error is thrown:
osmoErroris set to the error message.btsStatebecomesdisconnected.- The error is logged to the browser console.
osmoBusyreturns tofalsein thefinallyblock.
The control panel includes the error value in its statistics object, so it can be inspected from the statistics modal.
Statistics polling is controlled by the computed btsRunning value.
When btsRunning changes to true:
- Existing polling is stopped.
refreshBtsStats()runs immediately.- A two-second interval is created.
When btsRunning changes to false:
- The interval is cleared.
osmoBtsStatsis reset to an empty object.
Polled groups:
['stats', 'rate-counters', 'bts', 'trx', 'transceiver', 'websdr']For each group:
OsmoBtsTrx.getBtsStats(group)calls the shared TRX manager.- String responses are parsed as JSON when possible.
normalizeBtsStats(group, value)converts the raw structure into a display-friendly tree.- Failed groups are represented by their error message.
StatisticsModalrenders the final nested object.
StreamMeter provides traffic and cloud connection state.
On every stream meter update:
| Stream meter field | Local UI field |
|---|---|
cloud_is_up |
cloudConnected |
downloaded |
rxBytesReceived |
uploaded |
txBytesSent |
wr_ahead_avg |
txLagSamples |
These values are displayed in BtsControlPanel and included in the statistics modal data.
Log callbacks are attached during OsmoBtsTrx.reinitialize().
Log sequence:
- The shared TRX manager emits a
JournalLogItem. OsmoBtsTrx.onLog(...)receives it.OsmoBtsTrx.onLog(...)callsonLogItem.OsmoMain.vueappends the item toLogArea.- The log item's subsystem is added to the subsystem filter list if it has not been seen before.
onWriteLog(...) and onChangeParameter(...) currently log only in debug mode.
The app performs cleanup in two cases.
On pagehide:
- If the BTS is not running, no action is taken.
- If the BTS is running,
OsmoBtsTrx.stop()is called. releaseCurrentBts()releases the active instance.
On component unmount:
- Unsubscribe from
StreamMeter. - Stop statistics polling.
- Remove the
pagehideevent listener. - Clear
onLogItem. - Release the current BTS if it is still running.
- Call
OsmoBtsTrx.destroy().
destroy() stops the shared TRX manager and stops the worker.
Typical state transitions:
configured -> connected -> configured
configured -> disconnected
disconnected -> connected
The not-configured state is supported by the UI contract, but the current default flow starts with a valid GSM configuration.
Control disabling:
| Condition | Effect |
|---|---|
osmoBusy |
Disables BTS control and inputs. |
| no selected device | Disables start/stop control. |
btsRunning |
Disables SDR and BTS input controls. |
btsState === 'not-configured' |
Disables start/stop control. |
The frontend persists only the radio selection needed to recreate BtsParams.
Storage key:
osmoweb:bts-config
Persisted fields:
technologybandarfcn
The runtime BTS allocation returned by updateBts(...) is not persisted by this local app. It is requested again when the user starts the BTS runtime.