Repository navigation
Software Requirement Specification
This page documents the system's non-functional requirements (NFRs): quantified, testable thresholds that define how well the system performs its functions (the functions themselves are on the User Stories page). Each requirement is a Card-Conversation-Confirmation card: the ask, the assumptions and derivation behind it, and the test that verifies it. Requirements are grouped by quality attribute (Performance, Security, Scalability, Capacity, Usability) and traced to their use case via the Specification ID.
How to read a card: the Let row defines every variable. Variables are written in bold italics. Assumptions are listed first, then each derivation step is computed in order, so every value in a formula is already defined above it. The last step is the result.
| Requirement | Specification ID | Subsystem(s) | Related Card(s) |
|---|---|---|---|
| One account per email | SP-4.0.1 | API Gateway | — |
| Password signups must verify their email | SP-4.0.2 | API Gateway | — |
| Unverified accounts purged after 7 days | SP-4.0.3 | API Gateway, PostgreSQL | — |
| First mesh view in ≈ 4.4 s | SP-4.0.4 | RoamView App, Object Storage | — |
| Cached mesh reload in ≈ 600 ms | SP-4.0.5 | RoamView App | — |
| Mesh history list in ≤ 200 ms | SP-4.0.6 | RoamView App, API Gateway, PostgreSQL | — |
| Stop command registered in ≤ 250 ms | SP-4.1.1 | RoamView App, API Gateway, Streaming Service, IOT Gateway | Story-4.1 |
| Reject all invalid control commands | SP-4.1.2 | API Gateway | Story-4.1 |
| Fair control-lease scheduling | SP-4.1.3 | RoamView App, API Gateway | Story-4.1 |
| Rate-limit sessions to 8 req/s | SP-4.1.4 | API Gateway | Story-4.1 |
| Stream at least 640×360 @ 50 fps | SP-4.1.5 | RoamView App | Story-4.1 |
| IOT Gateway relays 18 Mbps, ≤ 50 ms | SP-4.1.6 | IOT Gateway | Story-4.1 |
| Streaming Service relays 18 Mbps, ≤ 50 ms | SP-4.1.7 | Streaming Service | Story-4.1 |
| API Gateway relays 18 Mbps, ≤ 50 ms | SP-4.1.8 | API Gateway | Story-4.1 |
| App plays 1080p50 on hardware-decode devices | SP-4.1.9 | RoamView App | Story-4.1 |
| Backend serves 20 users / 60 req/s | SP-4.1.10 | API Gateway | Story-4.1 |
| Guests view public meshes only | SP-4.3.1 | API Gateway, RoamView App | — |
| Rate-limit guests to 10 req / 10 s | SP-4.3.2 | API Gateway | — |
| Save 30-min inspection video (≈ 563 MB) | SP-4.5.1 | Ingestion Service | — |
| Video playable ≈ 4.3 min after session ends | SP-4.5.2 | Ingestion Service | — |
| Keep ≤ 900 motion-triggered frames per inspection | SP-4.5.3 | Processing Service | — |
| Reconstruction data ≤ 2.5 GB per inspection | SP-4.5.4 | Object Storage | — |
| TSDF fusion compute (to be measured) | SP-4.5.5 | Processing Service | — |
Date last modified: Oct 7, 2026
| Card | Let | nacc = accounts per email; nem = emails per account. |
| Requirement | Each account is tied to exactly one email, whether created by password or Google OAuth. | |
| Specification | • nacc = 1 • nem = 1 • Google OAuth login with an existing password email links to that account (no new account) |
|
| Conversation | Assumptions | Email is the account identifier; Google emails are pre-verified; password emails per SP-4.0.2; Case-insensitive comparison (design decision); Gmail dot/plus variants are not merged. Open item: decide whether to normalize; Google is the only OAuth provider. |
| Derivation | 1. Every account has exactly one email, so Accounts → Emails is a function (nem = 1). 2. No two accounts share an email, so it is injective (nacc = 1). 3. Password and OAuth sign-in share one identity space, so the check runs on the email, not per method. |
|
| Confirmation | Quantity / units / precision | nacc, nem: exact counts. |
| Verification | 1. Two password signups with the same email: second rejected. 2. Password account for X, then Google login as X: logs into the existing account. 3. Google signup for Y, then password signup with Y: rejected or redirected to linking. 4. Change an email to one already in use: rejected. 5. User@Example.com vs user@example.com: same account. |
Date last modified: Oct 7, 2026
| Card | Let | Texp = token validity; b = token entropy in bits. |
| Requirement | Password signups prove they own their email before the account can be used. | |
| Specification | • New password account starts unverified; no login until verified • Single-use emailed token, Texp = 24 h, b ≥ 128 bits • Google OAuth accounts exempt |
|
| Conversation | Assumptions |
Texp = 24 h (decided); b = 128 (design decision); If the token expired, a login attempt issues a new one; otherwise only the resend button does (decided); A new token invalidates the old one (one live token per account); Resend limit per time window: open, prevents email-bombing; Never-verified accounts purged per SP-4.0.3. |
| Derivation | 1. Unforgeable: a random token (b ≥ 128) cannot be guessed; single use prevents reuse. 2. Exclusive: only the inbox owner receives the link. 3. Bounded: it expires after Texp = 24 h. |
|
| Confirmation | Quantity / units / precision |
Texp: 24 h, exact; b: ≥ 128 bits; Resend limit: attempts per window, exact once decided. |
| Verification | 1. Click the link within Texp: verified, can log in. 2. Login before verifying: rejected. 3. Login after expiry: new token sent, old link dead. 4. Resend before expiry: new token sent, old link dead. 5. Reuse a verified link: rejected. 6. Google signup is never unverified. 7. OAuth linking (SP-4.0.1) against an unverified password account: blocked. |
Date last modified: Oct 7, 2026
| Card | Let | U = new users per day; funv = fraction never verifying; Tttl = unverified-account lifetime; D = project duration; m = bytes per account record; Npend = pending unverified accounts; Nver = verified accounts; S = auth storage. |
| Requirement | Auth storage stays bounded as users sign up. | |
| Specification | • Hard-delete unverified accounts after Tttl = 7 days • Npend ≈ 28 accounts • Nver ≈ 5,840 accounts after 1 year • S ≈ 3.5 MB in the existing PostgreSQL instance (no extra provisioning) |
|
| Conversation | Assumptions |
U = 20 users/day (SP-4.1.3); funv = 0.2 (design assumption, not measured); Tttl = 7 days (decided); D = 365 days (capacity doc, Scenario A); m = 600 B (capacity doc: ~300 B row × 2 for indexes). |
| Derivation | 1. Npend = U × funv × Tttl = 20 × 0.2 × 7 = 28 2. Nver = U × (1 − funv) × D = 20 × 0.8 × 365 = 5,840 3. S = (Nver + Npend) × m = 5,868 × 600 B ≈ 3.5 MB (negligible vs the 20 GB smallest managed-Postgres tier) |
|
| Confirmation | Quantity / units / precision |
Tttl: 7 days, exact; Npend, Nver, S: ±20%; m: ≈ 600 B (reused estimate). |
| Verification | 1. Unverified account is deleted after Tttl. 2. Account verified within Tttl is never purged. 3. After several live weeks, compare real Npend and m with the estimates. 4. Total DB size stays within the provisioned tier. |
Date last modified: Oct 7, 2026
| Card | Let | Tfirst = time from opening a mesh to the root tile rendered; M = finest-level mesh size; Droot = root tile size; B = client bandwidth; Ths = connection setup; Tmeta = signed-URL and tileset metadata; Tdl = root tile download; Trend = render. |
| Requirement | A first-time viewer sees something quickly instead of waiting on a ~200 MB download. | |
| Specification | • Tfirst ≈ 4.4 s (root tile rendered) at B = 20 Mbps • Droot ≈ 9.4 MB • Finer tiles stream in the background without blocking interaction |
|
| Conversation | Assumptions |
M = 150 MB (capacity wiki); Tile pyramid 1 + ¼ + 1/16 + …: the root is the 1/16 term (capacity wiki); B = 20 Mbps (stated minimum, so 4.4 s is a worst case); Ths = 100 ms (~1 RTT to CDN edge); Tmeta = 200 ms (signed-URL call plus small tileset.json);Trend = 300 ms (GPU parse/upload); CDN edge cache warm; Ths, Tmeta, Trend are estimates, not measured. |
| Derivation | 1. Droot = M × 1/16 = 150 × 1/16 ≈ 9.4 MB 2. Tdl = Droot × 8 / B = 75.2 Mb / 20 Mbps ≈ 3.76 s 3. Tfirst = Ths + Tmeta + Tdl + Trend = 100 + 200 + 3,760 + 300 ≈ 4,360 ms ≈ 4.4 s (At B = 50 Mbps: ≈ 2.1 s.) |
|
| Confirmation | Quantity / units / precision |
Droot: MB, ±20%; B: Mbps, ±2; Ths, Tmeta: ms, ±50; Trend: ms, pending measurement; Tfirst: s, ±15%. |
| Verification | Throttle to B = 20 Mbps, open a mesh and time click to root tile rendered; expect Tfirst ≈ 4.4 s. Repeat at ~50 and ~100 Mbps to confirm Tdl scales as predicted. |
Date last modified: Oct 7, 2026
| Card | Let | Treload = time to render the root tile on a local cache hit; Tfirst = time on a first load (SP-4.0.4); Ths, Tmeta, Trend as in SP-4.0.4. |
| Requirement | Re-opening a recently viewed mesh is near-instant, with no re-download of unchanged data. | |
| Specification | • Local cache hit (root tile held in the browser/app cache): Treload ≈ 600 ms (95th percentile) • Treload ≪ Tfirst = 4.4 s (SP-4.0.4), about 7× faster |
|
| Conversation | Assumptions |
Ths = 100 ms, Tmeta = 200 ms, Trend = 300 ms (SP-4.0.4 estimates); the download term drops out because the tile is local; Scope is a local cache hit only. A CDN-only hit is SP-4.0.4's case; Tiles use long max-age + immutable, valid ≥ 7 days (design decision, confirm configured);Cache key ignores signed-URL query parameters, or tiles are stored by mesh id + version + tile id (confirm in viewer design); "Reload" = same mesh and version; a new version behaves like SP-4.0.4; Browsers may evict cache, so a hit is not guaranteed. |
| Derivation | 1. Treload = Ths + Tmeta + Trend = 100 + 200 + 300 = 600 ms 2. Treload / Tfirst = 0.6 / 4.36 ≈ 14%, about 7× faster |
|
| Confirmation | Quantity / units / precision |
Treload: ms, ±100; Ths, Tmeta: ms, ±50; Trend: ms, pending measurement; Cache validity: ≥ 7 days. |
| Verification | 1. Open a mesh cold, then re-open it 20 times within the validity window; 95th-percentile Treload ≤ 600 ms. 2. Network inspector shows 0 bytes for the tile, but the signed-URL call still occurs. 3. Clear local cache with the CDN warm: time is close to Tfirst. 4. Re-open after a new scan version exists: behaves like a cold load. |
Date last modified: Oct 7, 2026
| Card | Let | Thist = time to return a room's history list; Lapi = backend response-time target (SP-4.1.10); n = rows in the list; m = bytes per row; S = response payload. |
| Requirement | A room's scan-history list appears quickly. It is a metadata query, not an asset download. | |
| Specification | • Thist ≤ 200 ms (95th percentile) • Metadata and preview URLs only (no full mesh data) |
|
| Conversation | Assumptions |
n = 50 rows (above typical use of 20 or fewer); m = 600 B (capacity wiki, SP-4.0.3); Lapi = 200 ms (SP-4.1.10; same API Gateway and Postgres); Query indexed by room/session id (confirm in schema); Full mesh loads later per SP-4.0.4/4.0.5. |
| Derivation | 1. S = n × m = 50 × 600 B ≈ 30 KB, so bandwidth is negligible. 2. Time is dominated by API round trip + DB query, the same class as SP-4.1.10 traffic. 3. Thist = Lapi = 200 ms |
|
| Confirmation | Quantity / units / precision |
Thist: ms, ≤ 200 (95th percentile); n: rows, typically single digits to low tens. |
| Verification | Request history for a room with 20–50 past scans; 95th-percentile Thist ≤ 200 ms and payload has no full mesh data. |
Date last modified: Oct 7, 2026
| Card | Let | s = robot top speed; a = braking deceleration; d = stopping distance to an obstacle; db = braking distance; tvideo = video delay; treact = user reaction time; ttot = total time budget before braking starts; tcmd = command latency. |
| Requirement | A user who sees a wall while the robot moves at top speed can stop it before hitting it, despite the cloud path. | |
| Specification | • Stop command registered by the robot (App → API Gateway → Streaming Service → IOT Gateway) within tcmd ≤ 250 ms (95th percentile) • Stopping distance d = 0.6 m at s = 0.5 m/s |
|
| Conversation | Assumptions | Indoor tour, flat ground; s = 0.5 m/s (hardware spec); a = 1 m/s² (hardware spec); d = 0.6 m (design decision, raised from 0.45 m, which the cloud path cannot meet); tvideo = 450 ms (300 ms local glass-to-glass + 150 ms worst-case cloud relay, hardware spec); treact = 250 ms (design assumption); Commands are tiny, so latency matters, not throughput. |
| Derivation | 1. db = s² / (2a) = 0.5² / (2 × 1) = 0.125 m 2. ttot = (d − db) / s = (0.6 − 0.125) / 0.5 = 0.95 s 3. tcmd = ttot − tvideo − treact = 0.95 − 0.45 − 0.25 = 0.25 s = 250 ms |
|
| Confirmation | Quantity / units / precision |
s: m/s, ±0.05; a: m/s², ±0.1; d: m, ±0.05; tvideo: ms, ±20; treact: ms, ±50; tcmd: ms, ±10. |
| Verification | Start a mock tour through the cloud, send 50 stop commands, and record send-to-registration time at the IOT Gateway. Pass if 95th-percentile tcmd ≤ 250 ms. |
Date last modified: Oct 7, 2026
| Card | Let | p = share of invalid commands accepted; r = share of invalid commands rejected. |
| Requirement | Only one legitimate, currently authorized session can send control commands to the robot. | |
| Specification | • Invalid = token absent, malformed, expired, revoked or tampered • r = 100% of invalid commands rejected • Rejected commands never forwarded to the robot |
|
| Conversation | Assumptions | Attacker is on the same network, no physical access to the robot; TLS encrypts traffic; Adaptive CPA: attacker knows the algorithm, not the secret key. |
| Derivation | 1. No invalid session is accepted, so p = 0%. 2. r = 100% − p = 100% |
|
| Confirmation | Quantity / units / precision | r: %, over 50 forged or expired requests, ±1%. |
| Verification | After starting a mock tour, send 10 commands each with: no token, malformed token, expired token, revoked token, and a valid token with one byte altered. All rejected (r = 100%) and neither gateway log shows any reaching the robot. Send 10 valid commands; all accepted. |
Date last modified: Oct 7, 2026
| Card | Let | H = operable hours per day; Troom = room inspection time; P = longest legitimate pause; Tho = handover time; k = idle safety factor; m = block margin factor; U = control users per day; Tidle = idle timeout; Tblock = block length; Nslots = blocks per day; Bmax = blocks per user per day. |
| Requirement | Users reserve control in advance and no one person takes all the time. On-Site Admins set availability and per-user limits. | |
| Specification | • Non-overlapping blocks of Tblock = 420 s within operable hours H = 8 h • Bmax = 3 blocks per user per day (a cancelled block still counts) • Control only during the holder's block; released at block end or after Tidle = 180 s without a valid command • On-Site Admin: sets H, Tblock and Bmax; edits or removes blocks; exempt from Bmax • Blocks cancelled by an admin change do not count toward Bmax |
|
| Conversation | Assumptions | 1 robot; H = 8 h = 28,800 s (default set by On-Site Admin; excludes charging and maintenance); U = 20 users/day (traffic assumption, not measured); Troom = 300 s; P = 120 s (design assumption); Tho = 60 s (design decision); k = 1.5 (design decision); m = 1.2 (design decision). |
| Derivation | 1. Tidle = k × P = 1.5 × 120 = 180 s 2. Tblock = m × Troom + Tho = 1.2 × 300 + 60 = 420 s 3. Nslots = floor(H / Tblock) = floor(28,800 / 420) = 68 4. Bmax = max(1, floor(Nslots / U)) = floor(68 / 20) = 3 (Tblock and Bmax are defaults the On-Site Admin can change.) |
|
| Confirmation | Quantity / units / precision |
Troom, Tidle, Tblock: s, ±30; H: h, ±0.5; U: users, ±5; Nslots, Bmax: integers, exact. |
| Verification | 1. Booking limit: reserve Bmax blocks (accepted), one more (rejected). Cancel one: same user still cannot book another, a different user can take the slot. 2. Overlap: same block as another user rejected. 3. Admin controls: On-Site Admin changes H, Tblock, Bmax and edits a block; schedule reflects each. A regular user cannot. 4. Admin change: user books up to Bmax, admin changes hours so one block no longer fits; it is cancelled and the user can still book up to Bmax. 5. Admin books more than Bmax: accepted. 6. Commands outside the block rejected; control released at block end despite continuous activity. 7. Acquire control, send nothing for more than Tidle (180 s); control released. |
Date last modified: Oct 7, 2026
| Card | Let | treact = user reaction time (SP-4.1.1); f = max human command rate; W = rate-limit window; c = burst factor; Nmax = max requests per window. |
| Requirement | The robot stays responsive during a block, with no denial of service or slowdown from excessive or malicious request loads. | |
| Specification | • Per session (block holder or not): Nmax = 8 requests per W = 1 s • Excess requests get a 429 and are not forwarded to the IOT Gateway or robot |
|
| Conversation | Assumptions | One block holder at a time (SP-4.1.3); Legitimate commands come from human input, not scripts; treact = 250 ms (SP-4.1.1); W = 1 s (design decision); c = 2: allows double taps and retries (design decision); Attacker may hold a valid session or flood without one; invalid tokens are handled by SP-4.1.2. |
| Derivation | 1. f = 1 / treact = 1 / 0.25 s = 4 req/s 2. Nmax = c × f × W = 2 × 4 × 1 = 8 requests |
|
| Confirmation | Quantity / units / precision |
W: s, ±0.1; treact: ms, ±50; c: unitless, ±0.1; Nmax: requests, ±1. |
| Verification | 1. As block holder, send f = 4 req/s for 60 s; 0% rejected. 2. From one session, send 5 × Nmax (40 req/s) for 10 s; every request beyond Nmax per window returns 429 and none reach the IOT Gateway (check logs). 3. During test 2, the block holder's commands still register within 250 ms (SP-4.1.1). |
Date last modified: Oct 7, 2026
| Card | Let | θ = camera horizontal field of view; R = viewing distance; d = box edge length; nmin = min pixels across the box; wR = scene width visible at R; ρ = required pixel density; Wpx × Hpx = stream resolution; f = frame rate. |
| Requirement | A user can tell an average box from another box 12 m away, including while the robot moves. | |
| Specification | • Minimum resolution 640×360 px (derived floor Wpx = 593, Hpx = 334, rounded up to the next 16:9 tier) • Minimum frame rate f = 50 fps • Holds while the robot moves at 0.5 m/s |
|
| Conversation | Assumptions |
θ = 102° (Pi Camera Module 3 Wide, assumed rectilinear; native 4608×2592, 16:9); R = 12 m (assumed longest room dimension); d = 0.3 m (assumed average box); nmin = 6 px (design decision, recognition level); f = 50 fps (design decision). Open item: the Module 3's HDR mode limits frame rate, so 50 fps likely needs HDR off (confirm on the Pi 5); Object near image centre (worst case for pixel density); This is a floor only; the operating resolution is bounded above by SP-4.1.6 to 4.1.9. |
| Derivation | 1. wR = 2 × R × tan(θ/2) = 2 × 12 × tan 51° ≈ 29.6 m 2. ρ = nmin / d = 6 / 0.3 = 20 px/m 3. Wpx ≥ ρ × wR = 20 × 29.6 ≈ 593 px 4. Hpx = Wpx × 9/16 ≈ 334 px 5. Next 16:9 tier: 640×360 (426×240 falls short) 6. Check: 640 / 29.6 = 21.6 px/m, so the box = 21.6 × 0.3 = 6.5 px ≥ 6 ✓ (at 426 px: 4.3 px ✗) |
|
| Confirmation | Quantity / units / precision |
d: m, ±0.05 (pending); R: m, ±0.5; θ: °, ±1; nmin: px, integer; Wpx × Hpx: 640×360 (floor 593×334); f: fps, ±1. |
| Verification | Place two 0.3 m boxes at R = 12 m with a known gap, near view centre. Stream at 640×360; 5 subjects independently distinguish them (pass at ≥ 90%). Repeat at 426×240 to confirm failure, and at 640×360 with the robot moving at 0.5 m/s. |
Date last modified: Oct 7, 2026
| Card | Let | Wpx × Hpx = max resolution; f = frame rate; β = encoded bits per pixel; o = overhead margin; Benc = encoded video bitrate; Bgw = gateway bandwidth per direction; Savg, Speak = average and keyframe size; kI = keyframe size factor; nbuf = buffered frames; Mgw = buffer memory; tbuf = buffer delay; tgw = added delay budget. |
| Requirement | The IOT Gateway relays video to the Streaming Service without dropped frames or becoming a bottleneck, up to the system maximum. | |
| Specification | • At 1920×1080 @ f = 50 fps: Bgw ≈ 18 Mbps in and 18 Mbps out (≈ 36 Mbps aggregate) • Mgw ≈ 1 MB (nbuf = 2 frames) • tgw ≤ 50 ms (95th percentile) • Pass-through only (no decode or transcode) |
|
| Conversation | Assumptions | Sized at the maximum (1920×1080 @ 50 fps), not the SP-4.1.5 floor; H.264 encoded on the robot; the gateway sees only the encoded stream; β = 0.145 bits/pixel (extrapolated from the hardware doc's 720p30 ≈ 4 Mbit/s; conservative upper estimate); o = 0.2 (TLS, retransmits; design decision); kI = 8 (design assumption, not measured); nbuf = 2 (design decision, minimal to protect SP-4.1.1); tgw = 50 ms: one third of the 150 ms cloud allowance in SP-4.1.1, shared with SP-4.1.7 and 4.1.8; Single robot, single stream; Robot uplink = 20 Mbps (capacity wiki). Risk: 18 Mbps leaves ~10% headroom for control and telemetry. |
| Derivation | 1. Benc = Wpx × Hpx × f × β = 1920 × 1080 × 50 × 0.145 ≈ 15.03 Mbps 2. Bgw = Benc × (1 + o) = 15.03 × 1.2 ≈ 18.04 Mbps 3. Savg = Benc / f = 15.03 / 50 ≈ 0.30 Mb ≈ 37.6 KB 4. Speak = kI × Savg = 8 × 37.6 ≈ 300 KB 5. Mgw = Speak × nbuf = 300 × 2 = 0.6 MB, rounded up to 1 MB 6. tbuf = nbuf / f = 2 / 50 = 40 ms, leaving ≈ 10 ms of tgw for forwarding |
|
| Confirmation | Quantity / units / precision |
Bgw: Mbps, ±1; Mgw: MB, ±0.5; nbuf: integer; tgw: ms, ±10; kI: ±2 (pending measurement). |
| Verification | 1. Run a ≥ 10 min mock tour at 1080p50; inbound and outbound each reach Bgw ≈ 18 Mbps with 0 dropped frames. 2. Peak buffer ≤ 1 MB and ≤ 2 frames. 3. Timestamp frames at ingress and egress; 95th-percentile hop latency ≤ 50 ms. 4. No decode/encode activity; CPU stays low. 5. Repeat at 640×360; bandwidth and buffer scale down. |
Date last modified: Oct 7, 2026
| Card | Let | Wpx, Hpx, f, β, o, kI, nbuf, Benc, Speak as in SP-4.1.6; Bss = service bandwidth per direction; ℓpkt = packet size; q = packet rate per direction; Mbuf = frame buffer; Mwork = runtime working set; Mss = service RAM; Pss = service CPU; tbuf = buffer delay; tss = added delay budget. |
| Requirement | The Streaming Service forwards video to the API Gateway without losing resolution or frame rate, and without delaying control commands, up to the system maximum. | |
| Specification | • At 1920×1080 @ f = 50 fps: Bss ≈ 18 Mbps in and 18 Mbps out • Pss ≈ 0.5 vCPU, Mss ≈ 50 MB • tss ≤ 50 ms (95th percentile) • Repackage only (no decode or transcode) |
|
| Conversation | Assumptions | Stream basis as SP-4.1.6: 1080p50, β = 0.145, o = 0.2, kI = 8, nbuf = 2; ℓpkt = 1,200 B; Mwork = 50 MB (runtime and per-connection state, estimate); Forwarding handles 10⁴–10⁵ packets/s per core; (S)RTP/TLS of ~4.5 MB/s is negligible with AES hardware support; tss = 50 ms: shared share of the 150 ms cloud allowance (SP-4.1.1); Low-latency delivery (e.g. WebRTC); HLS/DASH excluded, since segment length alone would exceed tss; H.264 Level ≥ 4.2 (Level 4.1 caps 1080p near 30 fps); Control commands use a separate priority channel (SP-4.1.8); Single viewer, single robot; Pss and Mss are estimates. Open item (hardware doc): the Pi 5 has no hardware H.264 encoder; software 1080p50 would break the hardware CPU ≤ 75% spec. Decide the 1080p50 source (e.g. OAK-D encoder) there. Open item (SP-4.1.1): three 50 ms hops use the whole 150 ms cloud allowance, leaving none for network transit. Raise tvideo or shrink the hop budgets. |
| Derivation | 1. Benc = 1920 × 1080 × 50 × 0.145 ≈ 15.03 Mbps 2. Bss = Benc × (1 + o) = 15.03 × 1.2 ≈ 18.04 Mbps 3. Speak = kI × (Benc / f) = 8 × 37.6 ≈ 300 KB 4. Mbuf = Speak × nbuf = 0.6 MB (encoded frames) 5. Mss = Mbuf + Mwork = 0.6 + 50 ≈ 50 MB 6. q = Bss / (8 × ℓpkt) = 18.04 Mbps / 9,600 bit ≈ 1,880 packets/s (≈ 3,800 aggregate) 7. That rate needs ≈ 0.1–0.25 vCPU, so Pss = 0.5 vCPU with margin (order-of-magnitude estimate) 8. tbuf = nbuf / f = 2 / 50 = 40 ms, leaving ≈ 10 ms of tss |
|
| Confirmation | Quantity / units / precision |
Bss: Mbps, ±1; Pss: vCPU, ±0.1 (estimate); Mss: MB, ±10 (estimate); nbuf: integer; tss: ms, ±10; kI: ±2 (pending measurement). |
| Verification | 1. Run a ≥ 10 min mock tour at 1080p50; in and out each ≈ 18 Mbps with 0 dropped frames at the API Gateway boundary. 2. Monitor CPU and RAM; Pss ≤ 0.5 vCPU, Mss ≤ 50 MB (record real values). 3. 95th-percentile hop latency ≤ 50 ms and buffer never above 2 frames. 4. No decode/encode; outbound payload equals inbound. 5. During test 1, send 50 stop commands; their hop latency stays ≤ 50 ms. 6. Repeat at 640×360 @ 50 fps (≈ 2.0 Mbps with overhead); resources scale down. |
Date last modified: Oct 7, 2026
| Card | Let | Bss = video bandwidth from the Streaming Service (SP-4.1.7); Bapi = gateway video bandwidth per direction; fctl = control request rate; ℓcmd = command size; Bctl = control bandwidth; Q = scheduling request rate; ℓresp = scheduling response size; Bsch = scheduling bandwidth; U = concurrent scheduling users; Clive, Ctot = live and total connections; mconn = state per connection; Mbuf, Mrt = relay buffer and runtime memory; Mapi = gateway RAM; Papi = gateway CPU; tapi = added delay budget. |
| Requirement | The API Gateway forwards the stream to the App and relays authenticated commands without perceptible delay or drops, while also serving scheduling and account traffic. | |
| Specification | • At 1920×1080 @ 50 fps: Bapi ≈ 18 Mbps in and 18 Mbps out (control ≈ 0.013 Mbps, negligible) • Clive = 2 (1 stream + 1 control); Ctot ≈ 22 • Papi ≈ 0.25 vCPU, Mapi ≈ 64 MB • tapi ≤ 50 ms (95th percentile) for video and commands |
|
| Conversation | Assumptions | Sized at 1080p50, not the SP-4.1.5 floor; no re-encode at this hop; Bss = 18.04 Mbps (SP-4.1.7); fctl = 8 req/s (SP-4.1.4 Nmax); ℓcmd = 200 B (design assumption); Q = 60 req/s, U = 20 (SP-4.1.10); ℓresp = 2 KB (design assumption); Clive = 2: one control lease (SP-4.1.3), single viewer; mconn = 50 KB; Mrt = 60 MB; Mbuf = 0.6 MB (SP-4.1.6); tapi = 50 ms: one third of the 150 ms cloud allowance (SP-4.1.1); Mesh downloads (SP-4.0.4/4.0.5) go via the CDN, not this relay; guest traffic (SP-4.3.2) is excluded; Papi and Mapi are estimates. |
| Derivation | 1. Bapi = Bss ≈ 18.04 Mbps 2. Bctl = fctl × ℓcmd = 8 × 200 B = 1.6 KB/s ≈ 0.013 Mbps 3. Bsch = Q × ℓresp = 60 × 2 KB = 120 KB/s ≈ 1 Mbps 4. Ctot = Clive + U = 2 + 20 = 22 5. Mapi = Mbuf + Ctot × mconn + Mrt = 0.6 + 22 × 0.05 + 60 ≈ 62 MB, budgeted as 64 MB 6. TLS plus cheap auth (SP-4.1.2) and rate-limit (SP-4.1.4) checks on ~36 Mbps give Papi ≈ 0.25 vCPU (order-of-magnitude estimate) |
|
| Confirmation | Quantity / units / precision |
Bapi: Mbps, ±1; Clive: integer; Ctot: ±2; Papi: vCPU, ±0.1 (estimate); Mapi: MB, ±20 (estimate); tapi: ms, ±10. |
| Verification | 1. Run a ≥ 10 min mock tour at 1080p50 with commands at the SP-4.1.4 rate; outbound ≈ 18 Mbps, 0 dropped frames. 2. 95th-percentile hop latency ≤ 50 ms. 3. During test 1, add 20 virtual users at ≈ 60 req/s; bandwidth, drops and tapi do not degrade. 4. Record CPU and RAM in tests 1 and 3 and replace the estimates. 5. Repeat at 640×360; bandwidth scales down. |
Date last modified: Oct 7, 2026
| Card | Let | Bapi = gateway video bandwidth (SP-4.1.8); Bapp = App inbound bandwidth; Blink = client downlink; h = link headroom; γ = bytes per pixel (YUV420); Sframe = decoded frame size; ncl = buffered frames; Mbuf = frame buffer memory; Mwork = decoder and render working set; Mapp = App decode/render RAM; f = frame rate; tbuf = buffer delay. |
| Requirement | The user's device decodes and renders the stream smoothly, with no stutter or dropped frames, up to the system maximum. | |
| Specification | • Device_class: hardware H.264 decode, High Profile Level 4.2 (1080p @ 50 fps) • Bapp ≈ 18 Mbps on a downlink Blink ≥ 20 Mbps • Mapp ≤ 50 MB • ncl = 2 frames buffered (tbuf ≤ 40 ms) • 0 dropped frames at 1920×1080 @ 50 fps |
|
| Conversation | Assumptions | 1920×1080 @ f = 50 fps, H.264 Level ≥ 4.2 (SP-4.1.7); Bapi = 18.04 Mbps (SP-4.1.8); γ = 1.5 B/px; ncl = 2 (traded against the SP-4.1.1 latency budget); Mwork = 20–40 MB (design estimate, not measured); Blink = 20 Mbps (SP-4.0.4 minimum); Device_class (design decision): hardware decode is required, not a preference. Reference: iPhone 6s / iOS 12+, Android 7.0+ on a mainstream chipset (2016+), or laptop/desktop with Intel Quick Sync (2011+), AMD UVD (2010+) or Apple Silicon. These mark when 1080p hardware decode became standard, not a guarantee per device; name one reference test device before verification; Software 1080p50 decode is not modeled. Open item: 10% headroom is thin. Raise the minimum downlink (e.g. ≥ 30 Mbps) or add an adaptive fallback toward the 640×360 floor (SP-4.1.5). |
| Derivation | 1. Bapp = Bapi ≈ 18.04 Mbps 2. Sframe = 1920 × 1080 × γ = 3.11 MB 3. Mbuf = Sframe × ncl = 3.11 × 2 = 6.2 MB 4. Mapp = Mbuf + Mwork = 6.2 + (20 to 40) ≈ 26–46 MB, budgeted at ≤ 50 MB 5. tbuf = ncl / f = 2 / 50 = 40 ms (inside the tvideo budget of SP-4.1.1) 6. h = (Blink − Bapp) / Blink = (20 − 18.04) / 20 ≈ 10% |
|
| Confirmation | Quantity / units / precision |
Mapp: MB, ≤ 50 (derived 26–46), ±10; Bapp: Mbps, ±1; ncl: integer; tbuf: 40 ms, exact from ncl and f; Device_class: as above. |
| Verification | 1. On the reference low-end device, run a ≥ 10 min mock tour at 1080p50; 0 dropped or stuttered frames and hardware decode in use. 2. Mapp ≤ 50 MB and buffer ≤ 2 frames. 3. End-to-end latency stays within the SP-4.1.1 budget. 4. Throttle downlink to Blink = 20 Mbps; no stalls. 5. Repeat at 640×360 @ 50 fps; comfortably within budget. |
Date last modified: Oct 7, 2026
| Card | Let | Uday = users per day; p = share of daily users active at peak; Upeak = concurrent users at peak; q = requests per second per active user; F = functions counted; Qpeak = peak request rate; Pbe = CPU budget; Mbe = RAM budget; Mos = OS/runtime overhead; creq = CPU time per request; msess = RAM per session; Lapi = response-time target. |
| Requirement | Scheduling and account features stay responsive when many people browse or manage the schedule at once, not only the single lease holder. | |
| Specification | • Upeak = 20 concurrent users; Qpeak = 60 req/s • Budget Pbe = 2 vCPU, Mbe = 2 GB • Lapi ≤ 200 ms (95th percentile) • Implies creq ≤ 33 ms and msess ≤ 75 MB |
|
| Conversation | Assumptions |
Uday = 20 users/day (SP-4.1.3); p = 1.0: everyone active at once (conservative design decision); q = 1 req/s (design assumption, not measured); F = 3 (schedule view, book/cancel, own bookings); admin scheduling and auth are separate lower-traffic paths; Pbe = 2 vCPU, Mbe = 2 GB (design decision); Mos = 0.5 GB; Lapi = 200 ms (design decision, looser than SP-4.1.1 since this traffic is not time-critical); creq and msess pending measurement; Excludes live video/control (SP-4.1.6 to 4.1.9) and command rate limiting (SP-4.1.4); schedule database capacity not covered. |
| Derivation | 1. Upeak = p × Uday = 1.0 × 20 = 20 2. Qpeak = Upeak × q × F = 20 × 1 × 3 = 60 req/s 3. creq ≤ Pbe / Qpeak = 2 vCPU / 60 ≈ 33 ms 4. msess ≤ (Mbe − Mos) / Upeak = (2 − 0.5) GB / 20 ≈ 75 MB (non-binding at this scale) |
|
| Confirmation | Quantity / units / precision |
Upeak: users, ±2; Qpeak: req/s, ±10; Lapi: ms, ≤ 200; Pbe, Mbe: pending measurement. |
| Verification | Simulate Upeak = 20 virtual users at q against the schedule, booking, cancellation and own-bookings endpoints for 5 min. 95th-percentile response ≤ Lapi, 0 failed requests; record real CPU/RAM to get measured creq and msess. |
Date last modified: Oct 7, 2026
| Card | Let | A = set of all system actions; G = actions permitted to a guest session. |
| Requirement | A visitor without an account can browse meshes marked public, including their scan history, and do nothing else. | |
| Specification | • Guest may: view a public mesh (per SP-4.0.4/4.0.5) and its scan history (SP-4.0.6), so |G| = 2 • Guest denied, enforced server-side: private meshes and history, live control, requesting or booking a session, annotation, account actions • Public flag is set per mesh by an admin |
|
| Conversation | Assumptions | Visibility is a per-mesh public/private flag set by an On-Site Admin or Admin (confirm in data model);A mesh and its history share the same visibility; A guest has no identity, so SP-4.0.1 to 4.0.3 do not apply; "Requesting a session" covers booking (SP-4.1.3) and immediate control acquisition; No account data (emails, "last scanned by") appears in public responses; Guest rate limit: SP-4.3.2. |
| Derivation | 1. A = {view_public_mesh, view_public_history, view_private_mesh, view_private_history, control_robot, request_session, annotate, manage_account} 2. G = {view_public_mesh, view_public_history} 3. Enforce at the server (API Gateway/backend); a client-side check can be bypassed by a direct request. |
|
| Confirmation | Quantity / units / precision | |G|: exactly 2; Actions in A minus G: all denied. |
| Verification | 1. Guest opens a public mesh: loads and is navigable. 2. Guest fetches its history: returned. 3. Guest requests a private mesh and its history directly (not just checking the list): server rejects. 4. Guest tries to book a block and to acquire control: both rejected. 5. Guest tries to annotate: rejected. 6. Public responses contain no account identifiers or emails. |
Date last modified: Oct 7, 2026
| Card | Let | fb = human browsing request rate; W = rate-limit window; nb = requests per window at human pace; c = burst factor; Nguest = max requests per window. |
| Requirement | Public browsing stays available to real visitors and cannot be used for denial of service, even without an account to tie requests to. | |
| Specification | • Per guest source (client IP + anonymous session token): Nguest = 10 requests per W = 10 s • Excess requests get a 429 and do not reach the mesh-serving path |
|
| Conversation | Assumptions |
fb = 0.33 req/s: one navigation per 3 s (design assumption, not measured; count user-initiated navigation only, not tile prefetches); W = 10 s (design decision); c = 3 (design decision); IP is the enforcement anchor (a client can discard its token); the token only separates users behind one IP; Shared IPs (NAT, campus, CGNAT) may throttle legitimate users. Known limitation, not solved here; Applies to the guest path only; authenticated traffic is governed by SP-4.1.4 and SP-4.1.10. |
| Derivation | 1. nb = fb × W = 0.33 × 10 ≈ 3.3 2. Nguest = c × nb = 3 × 3.3 ≈ 10 requests |
|
| Confirmation | Quantity / units / precision |
W: s, ±1; fb: req/s, ±0.1 (estimate); c: unitless; Nguest: requests, ±2. |
| Verification | 1. Guest browses at a normal pace for 60 s; 0% rejected. 2. From one IP/token, send 5 × Nguest for 10 s; excess returns 429 and none reach the mesh path. 3. Two tokens behind one IP browsing normally: confirm whether they are throttled and document the result. 4. During test 2, guests on other IPs are unaffected. |
Date last modified: Oct 7, 2026
| Card | Let | Bvid = video bitrate; Tinsp = inspection duration; V = recording size. |
| Requirement | Every autonomous inspection saves a continuous video for human playback, within a predictable storage size. Separate from reconstruction frames (SP-4.5.3) and the live stream (SP-4.1.x). | |
| Specification | • H.264 recording of every autonomous inspection, encoded on the OAK-D Pro's onboard RVC2 encoder • V ≈ 563 MB per Tinsp = 30 min (720p30 at Bvid = 2.5 Mbps) • Retained 90 days |
|
| Conversation | Assumptions | OAK-D Pro W hardware encoder, so no Pi CPU cost (unlike the Pi Camera's software encode in SP-4.1.x); Bvid = 2.5 Mbps (design decision, matches the capacity wiki; the encoder can go higher later); Tinsp = 30 min = 1,800 s; Video is continuous, not frame-sampled. |
| Derivation | V = Bvid × Tinsp / 8 = 2.5 × 1,800 / 8 ≈ 563 MB | |
| Confirmation | Quantity / units / precision |
V: MB, ±10%; Retention: 90 days, exact. |
| Verification | Run a 30-min mock inspection. File size within 10% of V, plays back with no gaps, and Pi CPU stays low (the OAK-D is encoding). |
Date last modified: Oct 7, 2026
| Card | Let | V = recording size (SP-4.5.1); Bup = robot uplink; tfin = finalize time; tup = upload time; tsave = total save time. |
| Requirement | After an inspection ends, the user does not wait long before the video is watchable. | |
| Specification | • Finalize + upload to object storage completes in tsave ≈ 4.3 min (255 s) after session end • At Bup = 20 Mbps |
|
| Conversation | Assumptions |
V = 563 MB (SP-4.5.1); Bup = 20 Mbps (capacity wiki); tfin = 30 s (muxing after recording stops; assumed 10–30 s, worst case used); Video file only; the full-session upload is ≈ 21 min (SP-4.5.4); Local buffering (64 GB, ~15 inspections) means a slow upload does not block the robot. |
| Derivation | 1. tup = V × 8 / Bup = 563 × 8 / 20 = 225 s 2. tsave = tfin + tup = 30 + 225 = 255 s ≈ 4.3 min |
|
| Confirmation | Quantity / units / precision | tsave: min, ±20%. |
| Verification | At Bup = 20 Mbps, complete a mock inspection and time session end to the video playable in the app; within tsave. |
Date last modified: Oct 7, 2026
| Card | Let | θ = camera horizontal field of view; R = scene distance; ωmin = min overlap between kept frames; w = footprint width at R; dmax = distance trigger; ψmax = yaw trigger; s = robot speed; ftr = frame rate while moving; Tinsp = inspection duration; Ntr = frames from translation; ρ = extra-frame allowance for rotation; Nnom = nominal max frames; fcap = frame rate cap; Ncap = frame cap. |
| Requirement | Keep enough overlapping depth frames to fuse a room without gaps, avoid near-duplicates, and bound the total per inspection. | |
| Specification | • Keep one depth+RGB+pose frame when the robot moves dmax = 1.31 m or rotates ψmax = 28° since the last kept frame • Overlap between kept frames ≥ ωmin = 70% • Cap fcap = 0.5 fps, so Ncap ≤ 900 frames per 30 min |
|
| Conversation | Assumptions |
θ = 95° (OAK-D Pro W RGB; the 127° depth view is wider, so RGB is the conservative limit); R = 2 m (design assumption). Near walls (R = 1 m), dmax ≈ 0.65 m and top-speed demand ≈ 0.77 fps exceeds fcap, so overlap can fall below ωmin. A fix is to set R per frame from median depth. Flagged, not solved; ωmin = 0.7 (design decision); s = 0.5 m/s top speed, 0.3 m/s cruise; Tinsp = 1,800 s (SP-4.5.1); ρ = 0.25 (design assumption, not measured); fcap = 0.5 fps (design decision); if it binds, the sampler skips frames and logs a warning; Motion-triggered, not timed: a paused robot adds no new coverage; Affects only reconstruction input; the human video (SP-4.5.1) stays continuous. |
| Derivation | 1. w = 2 × R × tan(θ/2) = 2 × 2 × tan 47.5° ≈ 4.36 m 2. dmax = w × (1 − ωmin) = 4.36 × 0.3 ≈ 1.31 m 3. ψmax = θ × (1 − ωmin) = 95° × 0.3 ≈ 28° (about 13 frames per 360° turn) 4. ftr = s / dmax = 0.5 / 1.31 ≈ 0.38 fps at top speed (0.3 / 1.31 ≈ 0.23 fps at cruise) 5. Ntr = ftr × Tinsp ≈ 0.38 × 1,800 ≈ 687 frames 6. Nnom = Ntr × (1 + ρ) = 687 × 1.25 ≈ 859 frames 7. Ncap = fcap × Tinsp = 0.5 × 1,800 = 900 frames, above 859, so the cap does not normally bind |
|
| Confirmation | Quantity / units / precision |
R: m, ±0.5; ωmin: ±0.05; dmax: m, ±20%; ψmax: °, ±5; ftr: ≈ 0.38 fps (ceiling); ρ: ±0.1; fcap, Ncap: exact. |
| Verification | 1. Drive straight at 0.5 m/s at R ≈ 2 m; kept frames ≤ dmax apart (±20%) at ≈ 0.38 fps. 2. Rotate in place; a frame about every ψmax (±5°), none while stationary. 3. Stay still 60 s; 0 frames kept. 4. 30 min at top speed with continuous turning; never above Ncap. 5. Mock inspection of a known room shows no gaps around R ≈ 2 m. Doubling dmax makes gaps appear (validates the derivation). 6. Repeat near a wall (R ≈ 1 m) and record whether the cap causes gaps. |
Date last modified: Oct 7, 2026
| Card | Let | Ncap = frame cap (SP-4.5.3); Nc, Nt, Nr = typical frame counts (cruise, top speed, top speed + rotation); Srgb, Sd = RGB and depth size per frame; Sf = frame set size; Smax = worst-case storage; V = video size (SP-4.5.1); Bup = uplink; tup = full-session upload time. |
| Requirement | Frames kept for 3D reconstruction (SP-4.5.3) need predictable, bounded storage, separate from the continuous video (SP-4.5.1). | |
| Specification | • ≤ Ncap = 900 frame sets per 30-min inspection • Smax ≤ 2.5 GB (typical 1.2–2.4 GB) • Retained 7 days, until the finished mesh/tileset supersedes them |
|
| Conversation | Assumptions |
Ncap = 900 (hard cap, SP-4.5.3); Nc = 412, Nt = 687, Nr = 859 (SP-4.5.3); counts are upper bounds, since slower motion keeps fewer frames; Srgb = 2.5 MB, Sd = 0.3 MB (capacity wiki); V = 563 MB (SP-4.5.1); Bup = 20 Mbps; Pose data (a few dozen bytes per frame) not budgeted; If the cap binds, frames are skipped, so storage stays bounded; Intermediate data only; the final mesh/tileset is sized in the capacity wiki. |
| Derivation | 1. Sf = Srgb + Sd = 2.5 + 0.3 = 2.8 MB 2. Smax = Ncap × Sf = 900 × 2.8 = 2,520 MB ≈ 2.5 GB 3. Typical: Nc × Sf ≈ 1.15 GB; Nt × Sf ≈ 1.92 GB; Nr × Sf ≈ 2.40 GB 4. Cross-check for SP-4.5.2: tup = (Smax + V) × 8 / Bup = 3,083 × 8 / 20 ≈ 1,233 s ≈ 20.6 min (matches the wiki's ~21 min) |
|
| Confirmation | Quantity / units / precision |
Ncap: exact; typical Nc–Nr = 412–859; Smax: ≤ 2,520 MB, typical 1.15–2.4 GB; Srgb, Sd: ±15% per frame (cap implies ≤ ~2.9 GB worst case); Retention: 7 days. |
| Verification | 1. 30 min at top speed with continuous turning; frames ≤ Ncap and storage ≤ 2.5 GB (≤ 2.9 GB allowing for size variation). 2. Run at cruise and top speed with pauses; compare actual counts and storage with 412–859 and replace estimates. 3. A slow or paused run keeps fewer frames than a top-speed run. 4. Intermediate frames purged 7 days after the mesh/tileset supersedes them. |
Date last modified: Oct 7, 2026
| Card | Let | N = frame sets per inspection; tproc = processing time per inspection; Pgpu, Pcpu = GPU and CPU capacity; Mrec = RAM. |
| Requirement | The processing node turns an inspection's frames into a mesh in reasonable time, at far lower cost than the original COLMAP+OpenMVS pipeline. | |
| Specification | • TSDF fusion + mesh extraction for one inspection (N ≤ 900 frames, SP-4.5.4) completes in tproc • tproc, Pgpu, Pcpu, Mrec: pending measurement • Expected well below the old 8 vCPU / 32 GB RAM / 8 GB VRAM |
|
| Conversation | Assumptions | Path B: SLAM-derived poses + OAK-D depth fusion; no SfM feature matching or dense stereo; N ≤ 900 (SP-4.5.4); The old 8 vCPU / 32 GB / 8 GB VRAM minimum was sized for COLMAP + OpenMVS on ~900 4K frames; upper reference only; TSDF fusion is comparatively light and may not need a dedicated GPU at this scale (needs a real implementation to profile); Mesh extraction (e.g. marching cubes) still has real cost, likely closer to the old meshing/texturing stage than to zero; Whether the node can be downsized is the most valuable early test, since it could change hosting cost. |
| Derivation | No closed formula exists until a fusion library is chosen (e.g. Open3D TSDF integration). 1. Cost scales with N ≤ 900 and the voxel grid resolution. 2. The old pipeline's matching scaled roughly with N² image pairs. Fusion avoids that term, so a large reduction is expected, but it is not yet a number. |
|
| Confirmation | Quantity / units / precision | tproc, Pgpu, Pcpu, Mrec: pending measurement. |
| Verification | Implement TSDF fusion + mesh extraction on SP-4.5.4's frame set, starting on a CPU-only or modest-GPU instance. Measure tproc and peak CPU/RAM/VRAM, compare with the old requirements, and right-size the node. |
RoamView Wiki
- Home
- 1. Introduction
- 2. Project Proposal
- 3. Backlog
- 4. Sprint Summaries
- 5. Architecture
- 6. UI Design
- 7. Testing
- 8. Defect Tracking
- 9. Quality Measurements
- 10. Ethics & Accountability