Flexible Gas Analyzer using Laser Photoacoustic Spectroscopy
A comprehensive software and hardware platform for high-precision gas concentration measurement using differential Helmholtz resonator photoacoustic spectroscopy.
- Overview
- Key Features
- Architecture
- Quick Start
- Project Structure
- Configuration
- Physical Principles
- Signal Processing Pipeline
- Technology Stack
- API Reference
- Hardware Interface
- Development
- Documentation
- License
Rust-Photoacoustic is a complete platform for laser photoacoustic spectroscopy (LPAS), designed for real-time gas concentration measurement with laboratory-grade precision. The system provides:
- ppb-level sensitivity (parts per billion) for trace gas detection
- Real-time processing with <10ms latency
- Multi-platform support (Linux, macOS, Windows)
- Industrial integration via Modbus TCP and REST API
- Modern web interface for visualization and control
This project follows a transparent continuous integration approach where development happens openly in the main branch. Tests are designed and implemented in real-time, ensuring code functionality aligns with project objectives.
β οΈ Status: Active development - APIs may change between commits.
mindmap
root((Rust-Photoacoustic))
Signal Processing
Real-time FFT
Butterworth filters
Differential subtraction
Lock-in detection
Python integration
Hardware Control
USB-HID interface
I2C/SPI protocols
Thermal regulation
DDS modulation
Web Interface
Real-time streaming
OAuth2/JWT security
6 languages + RTL
Interactive graphs
Industrial
Modbus TCP server
Redis Pub/Sub
Kafka streaming
REST API
| Metric | Value | Notes |
|---|---|---|
| Sampling Rate | 48 kHz | Stereo, 16-bit |
| FFT Resolution | 4096 points | Configurable 256-8192 |
| Processing Latency | < 10 ms | Real-time streaming |
| Web Streaming | 60 fps | SSE with compression |
| ADC Resolution | 16-bit | Β±7.8 Β΅V @ PGA=16 |
| DDS Resolution | 0.004 Hz | 28-bit frequency register |
| Temperature Control | Β±0.1Β°C | PID regulation |
flowchart TB
subgraph Hardware["π§ Hardware Layer"]
MIC["Microphones A/B"]
LASER["QCL/DFB Laser"]
TEC["TEC Controllers"]
NTC["NTC Thermistors"]
end
subgraph LaserSmart["β‘ Laser+Smart Interface"]
USB["USB-HID"]
ADC["4Γ ADS1115<br/>16-bit ADC"]
DAC["LTC2641<br/>12-bit DAC"]
DDS["AD9833<br/>DDS Modulator"]
end
subgraph Backend["π¦ Rust Backend"]
ACQ["Acquisition<br/>Daemon"]
PROC["Processing<br/>Graph Engine"]
THERM["Thermal<br/>Regulation"]
API["REST API<br/>+ SSE"]
MODBUS["Modbus<br/>Server"]
end
subgraph Frontend["βοΈ React Frontend"]
DASH["Dashboard"]
AUDIO["Audio<br/>Analyzer"]
GRAPH["Processing<br/>Graph Editor"]
THERMAL["Thermal<br/>Monitor"]
end
subgraph External["π External Systems"]
SCADA["SCADA/PLC"]
REDIS["Redis"]
KAFKA["Kafka"]
end
MIC --> ADC
LASER --> DAC
TEC --> DAC
NTC --> ADC
ADC --> USB
DAC --> USB
DDS --> USB
USB --> ACQ
ACQ --> PROC
PROC --> API
PROC --> MODBUS
THERM --> API
API --> DASH
API --> AUDIO
API --> GRAPH
API --> THERMAL
MODBUS --> SCADA
PROC --> REDIS
PROC --> KAFKA
- Rust 1.75 or later
- Node.js 20+ (for frontend)
- Python 3.10+ (optional, for Python drivers)
# Clone the repository
git clone https://github.com/sctg-development/rust-photoacoustic.git
cd rust-photoacoustic
# Build the backend
cd rust
cargo build --release
# Install frontend dependencies
cd ../web
npm install
# Start the application
cd ../rust
cargo run --release -- --server# Run with a test audio file
cargo run -- --input-file examples/test_signal.wav --frequency 2000
# Start server on custom port
cargo run -- --server --web-port 9000
# Enable verbose logging
cargo run -- --server -vrust-photoacoustic/
βββ rust/ # π¦ Rust Backend
β βββ src/
β β βββ main.rs # Application entry point
β β βββ lib.rs # Library exports
β β βββ acquisition/ # Audio signal acquisition (CPAL)
β β βββ preprocessing/ # Digital filters, differential
β β βββ spectral/ # FFT, spectral analysis
β β βββ processing/ # Processing graph engine (~3000 lines)
β β βββ visualization/ # Web server, API, OAuth2
β β βββ thermal_regulation/ # PID controllers
β β βββ modbus/ # Modbus TCP server
β β βββ daemon/ # Acquisition daemon
β β βββ config/ # Configuration management
β β βββ photoacoustic/ # Core domain logic
β β βββ utility/ # Helpers, certificate generation
β βββ auth-macros/ # Procedural macros for auth
β βββ examples/ # Usage examples
β βββ tests/ # Integration tests
β βββ Cargo.toml
β
βββ web/ # βοΈ React Frontend
β βββ src/
β β βββ pages/ # Route pages
β β β βββ index.tsx # Dashboard
β β β βββ audio.tsx # Audio analyzer
β β β βββ thermal.tsx # Thermal monitor
β β β βββ graph.tsx # Processing graph editor
β β βββ components/ # Reusable UI components
β β βββ hooks/ # Custom React hooks
β β β βββ useAudioStream.ts # Audio streaming (~2000 lines)
β β β βββ useProcessingGraph.ts
β β βββ authentication/ # Auth0/OIDC integration
β β βββ locales/ # i18n translations (6 languages)
β β βββ contexts/ # React contexts
β βββ package.json
β
βββ hardware/ # β‘ Hardware Designs
β βββ 6C47543F-DEE8-4421-881E-CF5E1C8FF55D/ # Laser+Smart
β βββ *.SchDoc # Altium schematics
β βββ components/ # JLCPCB component library
β βββ doc/ # Hardware documentation
β
βββ docs/ # π Documentation
β βββ COMPLETE_TECHNICAL_DOCUMENTATION.md
β βββ acquisition_daemon_guide_en.md
β βββ jwt_tokens.md
β βββ ...
β
βββ .github/ # π CI/CD
βββ workflows/
βββ ci.yml
βββ code-quality.yml
βββ release-multiarch.yml
The application uses YAML configuration. Create config.yaml:
# Audio acquisition settings
acquisition:
sample_rate: 48000 # Hz
buffer_size: 4096 # samples
channels: 2 # stereo (A/B microphones)
# Signal processing settings
processing:
excitation_frequency: 2000.0 # Hz (laser modulation)
filter_bandwidth: 100.0 # Hz (bandpass filter)
fft_size: 4096 # FFT window size
window_function: hann # hann, blackman_harris, hamming
averaging_count: 10 # spectra to average
# Web server settings
visualization:
port: 8080
address: "127.0.0.1"
name: "LaserSmartApiServer/0.1.0"
hmac_secret: "your-secure-jwt-secret-key"
# Optional: Base64-encoded SSL certificates
# cert: "LS0tLS1CRUdJTi..."
# key: "LS0tLS1CRUdJTi..."
# Modbus TCP server (industrial integration)
modbus:
enabled: true
address: "0.0.0.0"
port: 5020
# Thermal regulation (PID control)
thermal_regulation:
enabled: true
setpoint: 25.0 # Β°C
kp: 1.0 # Proportional gain
ki: 0.1 # Integral gain
kd: 0.05 # Derivative gain
# Processing graph (hot-reloadable)
processing_graph:
nodes:
- id: "audio_input"
type: "AudioSource"
config:
device: "default"
- id: "bandpass"
type: "ButterworthFilter"
config:
filter_type: "bandpass"
low_cutoff: 1900.0
high_cutoff: 2100.0
order: 4
- id: "fft"
type: "FFTAnalyzer"
config:
size: 4096# Core options
--config <path> # Configuration file path (default: config.yaml)
--server # Start in server mode
--input-device <name> # Audio input device (e.g., hw:0,0)
--input-file <path> # Analyze WAV file instead of live audio
# Processing parameters
--frequency <Hz> # Excitation frequency (default: 2000)
--bandwidth <Hz> # Filter bandwidth (default: 100)
--window-size <samples> # FFT size (default: 4096)
--averages <count> # Spectra to average (default: 10)
# Server options
--web-port, -p <port> # Web server port (default: 8080)
--web-address <addr> # Bind address (default: 127.0.0.1)
--hmac-secret <secret> # JWT signing secret
# Modbus options
--modbus-enabled # Enable Modbus server
--modbus-address <addr> # Modbus bind address
--modbus-port <port> # Modbus port (default: 5020)
# Logging
--verbose, -v # Debug logging
--quiet, -q # Suppress all output
--show-config-schema # Print JSON schema and exitCertificates are auto-generated during build for development. For production:
- Place certificates in
resources/directory - Or specify base64-encoded in config:
visualization:
cert: |
LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0t...
key: |
LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0t...sequenceDiagram
participant L as Modulated Laser
participant G as Gas Sample
participant M as Microphone
participant S as Signal Processing
L->>G: Pulsed IR radiation (f = 2 kHz)
Note over G: Molecular absorption<br/>HβO, COβ, CHβ...
G->>G: Thermal expansion<br/>(photoacoustic effect)
G->>M: Acoustic wave
M->>S: Electrical signal
S->>S: FFT at excitation frequency
Note over S: Amplitude β concentration
Key equations:
-
Photoacoustic signal:
$S_{PA} = \alpha \cdot P_L \cdot C \cdot Q$ -
$\alpha$ : absorption coefficient (cmβ»ΒΉ) -
$P_L$ : laser power (W) -
$C$ : gas concentration -
$Q$ : cell quality factor
-
-
Helmholtz resonance:
$f_0 = \frac{c}{2\pi}\sqrt{\frac{A}{V \cdot L}}$ -
$c$ : speed of sound -
$A$ : neck cross-section -
$V$ : cavity volume -
$L$ : effective neck length
-
Two microphones (A and B) enable common-mode noise rejection:
This cancels:
- Ambient acoustic noise
- Electronic interference
- Temperature fluctuations
graph LR
subgraph Acquisition
A1[Mic A] --> |48kHz/16-bit| B1[Buffer A]
A2[Mic B] --> |48kHz/16-bit| B2[Buffer B]
end
subgraph Preprocessing
B1 --> C1[Bandpass<br/>Filter]
B2 --> C2[Bandpass<br/>Filter]
C1 --> D[Differential<br/>A - B]
C2 --> D
end
subgraph Analysis
D --> E[Windowing<br/>Hann/Blackman]
E --> F[FFT<br/>4096 pts]
F --> G[Peak<br/>Detection]
end
subgraph Output
G --> H1[REST API]
G --> H2[Modbus]
G --> H3[SSE Stream]
end
| Node Type | Description | Parameters |
|---|---|---|
AudioSource |
Audio input acquisition | device, sample_rate |
WavFileSource |
WAV file reader | path, loop |
ButterworthFilter |
IIR bandpass/lowpass/highpass | order, cutoff frequencies |
Differential |
Channel subtraction | - |
FFTAnalyzer |
Spectral analysis | size, window, overlap |
PeakDetector |
Frequency/amplitude extraction | threshold, range |
Averager |
Temporal averaging | count |
PythonNode |
Custom Python processing | script_path |
| Category | Technology | Version |
|---|---|---|
| Web Framework | Rocket | 0.5.1 |
| Async Runtime | Tokio | 1.45 |
| Audio | CPAL | 0.17 |
| FFT | RustFFT | 6.4 |
| Auth | jsonwebtoken + oxide-auth | 10.2 |
| Modbus | tokio-modbus | 0.17 |
| Python | PyO3 | 0.27 |
| Serialization | Serde | 1.0 |
| Category | Technology | Version |
|---|---|---|
| Framework | React | 19.2 |
| Build Tool | Vite | 7.3 |
| UI Library | HeroUI | 2.x |
| Charts | Chart.js | 4.5 |
| Flow Editor | ReactFlow | 11.x |
| Audio Viz | AudioMotion | 4.5 |
| i18n | i18next | 25.x |
| Auth | Auth0 / OIDC | 2.x |
| Component | Part Number | Function |
|---|---|---|
| MCU | ATmega32U4 | USB-HID controller |
| ADC | ADS1115 Γ4 | 16-bit, IΒ²C, 860 SPS |
| DAC | LTC2641 | 12-bit, SPI |
| DDS | AD9833 | Frequency synthesis |
| GPIO | MCP23017 | IΒ²C expander |
| Reference | REF5040 | 4.096V precision |
The API supports OAuth2 with PKCE and JWT bearer tokens:
# Get OAuth2 authorization
GET /oauth/authorize?client_id=<id>&redirect_uri=<uri>&response_type=code&code_challenge=<challenge>
# Exchange code for token
POST /oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&code=<code>&code_verifier=<verifier>
# Access protected endpoints
GET /api/v1/spectrum
Authorization: Bearer <jwt_token>| Method | Endpoint | Description |
|---|---|---|
| GET | /api/v1/status |
System status |
| GET | /api/v1/spectrum |
Current spectrum data |
| GET | /api/v1/spectrum/stream |
SSE spectrum stream |
| GET | /api/v1/config |
Current configuration |
| PUT | /api/v1/config |
Update configuration |
| GET | /api/v1/graph |
Processing graph state |
| PUT | /api/v1/graph |
Update processing graph |
| GET | /api/v1/thermal |
Thermal sensor readings |
| PUT | /api/v1/thermal/setpoint |
Set temperature target |
| Address | Type | Description |
|---|---|---|
| 0-1 | Float32 | Excitation frequency (Hz) |
| 2-3 | Float32 | Signal amplitude |
| 4-5 | Float32 | Phase (degrees) |
| 6-7 | Float32 | Temperature (Β°C) |
| 100+ | Float32[] | Spectrum data |
Interactive API documentation available at:
- RapiDoc:
https://localhost:8080/rapidoc - Swagger:
https://localhost:8080/swagger
The Laser+Smart interface board provides:
- 4Γ 16-bit ADC channels (ADS1115) for microphone and sensor inputs
- 12-bit DAC (LTC2641) for laser power control
- DDS synthesizer (AD9833) for laser modulation (0.004 Hz resolution)
- 16 GPIO (MCP23017) for auxiliary control
- USB-HID interface (no drivers required)
βββββββββββββββββββββββββββββββββββββββββββββββ
β Laser+Smart Board β
ββββββββββββββββ¬βββββββββββββββ¬ββββββββββββββββ€
β ADC0-1 β DAC β DDS β
β Mic A/B β Laser PWR β Modulation β
β Β±2.048V β 0-4.096V β 100Hz-14kHz β
ββββββββββββββββΌβββββββββββββββΌββββββββββββββββ€
β ADC2-3 β GPIO β USB β
β NTC Sensors β TEC Control β HID to Host β
β Β±2.048V β 8 outputs β Full Speed β
ββββββββββββββββ΄βββββββββββββββ΄ββββββββββββββββ
# Development build
cargo build
# Release build with optimizations
cargo build --release
# With Python driver support
cargo build --features python-driver
# Static build (musl)
cargo build --release --features static --target x86_64-unknown-linux-musl# Unit tests
cargo test
# Integration tests
cargo test --test '*'
# With coverage
cargo tarpaulin --out Html
# Frontend tests
cd web && npm test# Backend with hot-reload (requires cargo-watch)
cargo watch -x 'run -- --server -v'
# Frontend dev server
cd web && npm run dev
# Full stack with proxy
cd web && npm run dev:env- Complete Technical Documentation - Comprehensive guide for all audiences
- Acquisition Daemon Guide - Audio acquisition system
- JWT Tokens - Authentication details
- Hardware Analysis - Laser+Smart interface
This project is licensed under the SCTG Non-Commercial License 1.0.
- β Free for research and education
- β Open source contributions welcome
- β Commercial use requires separate license
See LICENSE for details.
Contributions are welcome! Please read our contributing guidelines before submitting pull requests.
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
- Author: Ronan Le Meillat
- Organization: SCTG Development
- Repository: github.com/sctg-development/rust-photoacoustic