Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

bah2026-ps9 — SH-WFS Wavefront Reconstruction, Turbulence Characterization & DM Actuator Maps

ISRO Bharatiya Antariksh Hackathon 2026 — Problem Statement 9.

Given a time-series of Shack–Hartmann Wavefront Sensor (.bmp) frames, this project (for every frame) reconstructs the wavefront phase map W(x,y) and its Zernike coefficients, derives the turbulence Fried parameter r₀ and coherence time τ₀, and computes a deformable-mirror actuator map in stroke-length units with inter-actuator coupling deconvolved — fast enough (< 10 ms/frame, target ≈ µs) to correct turbulence in real time.

Read ARCHITECTURE.md first — it is the primary deliverable and the implementation contract (problem analysis, algorithms, file formats, directory layout, validation plan, roadmap). Stage→research mapping is in docs/METHODS.md.

Design in one paragraph

Two tiers (research note 06): a self-contained C11 real-time core (src/bin/wfs_rt) that links only -lm (no BLAS/FFTW) — hand-rolled BMP I/O, a portable AVX2/OpenMP-optional GEMV, TWCoG centroiding, and 2–3 precomputed matrix–vector multiplies per frame; and a Python offline toolkit (aokit/) that builds the calibration matrices, generates synthetic datasets with known r₀/τ₀, runs the multi-method turbulence estimation, and validates everything. The two communicate through a small self-describing binary matrix format (AOMX) that both sides implement byte-for-byte.

.bmp ─▶ centroid (TWCoG) ─▶ slopes ─▶ { φ = R·s  (zonal),  a = M⁺·s (modal) } ─▶ a_dm = H⁺·(−W/2)
        precomputed offline:  R (Fried, waffle-nulled),  M⁺/Z (Zernike),  G = H⁺·(−½) (coupling deconvolved)

Quickstart

1. Build the C real-time core (only needs a C compiler + libm)

make            # -> bin/wfs_rt  (-O3 -march=native -fopenmp, links only -lm)
make test       # built-in self-test: AOMX + BMP roundtrips, GEMV sanity

2. Python offline toolkit

pip install -r requirements.txt        # numpy, scipy, matplotlib (if not present)
python3 -c "import aokit"               # import sanity
python3 -m json.tool config/example_config.json   # validate config JSON
MPLBACKEND=Agg python3 -m pytest -q    # 226 unit + integration tests (headless)

3. End-to-end (synthetic) workflow — the exact validated commands

This is the full inject-r₀/τ₀ → recover loop used to produce the Results below (10×10 lenslets, 256×256 frames, D = 1.5 mm pupil). The injected r0 is chosen so D/r0 is order unity (the pupil is only 1.5 mm, so a meaningful D/r0 ≈ 2.5 needs r0 ≈ 0.6 mm, not 0.15 m).

# 0. Build the C core
make

# 1. Build calibration matrices (AOMX) + the flat config.txt sidecar for the C core
python3 scripts/build_calibration.py --config config/example_config.json \
        --out results/calib

# 2. Generate a synthetic dataset with KNOWN injected r0/tau0 + ground_truth.json
python3 scripts/generate_dataset.py --config config/example_config.json \
        --r0 0.0006 --tau0 0.005 --frames 60 \
        --out results/synthetic/run01 --model geometric --seed 123

# 3. Run the fast C real-time core over all frames (glob is expanded internally)
bin/wfs_rt --config results/calib/config.txt --calib results/calib \
           --frames "results/synthetic/run01/frame_*.bmp" --out results/out_c

# 4. Run the pure-Python reference pipeline (same frames) + diagnostic plots
python3 scripts/run_pipeline_py.py --config config/example_config.json \
        --calib results/calib --frames "results/synthetic/run01/frame_*.bmp" \
        --out results/out_py --plots

# 5. Multi-method turbulence report (>=7 r0, >=6 tau0 estimators) + method plots
python3 scripts/analyze_turbulence.py --slopes results/out_py/slopes.csv \
        --coeffs results/out_py/zernike_coeffs.csv \
        --config config/example_config.json \
        --ground-truth results/synthetic/run01/ground_truth.json \
        --out results/turbulence_summary.json --plots

The C core (step 3) and the Python reference (step 4) write the same artifact filenames (slopes.csv, zernike_coeffs.csv, actuators.csv, phase_NNNN.aomx), so their outputs are directly comparable — that comparison is the C↔Python parity proof. bin/wfs_rt --selftest runs the built-in C roundtrips; MPLBACKEND=Agg python3 -m pytest -q runs all 226 unit + integration tests (including the end-to-end tests/test_integration.py).

Repository layout

ARCHITECTURE.md     # master architecture (primary deliverable)
docs/METHODS.md     # stage -> research-report map (+ integration conventions)
config/             # example_config.json (schema in ARCHITECTURE §4.1)
src/                # self-contained C11 real-time core (links only -lm)
  ├─ main.c         #   CLI, config sidecar parser, frame loop, timing report
  ├─ pipeline.c     #   per-frame: centroid -> slopes -> R/M+ -> G (zero in-loop alloc)
  ├─ centroid.c     #   TWCoG centroiding
  ├─ slopes.c       #   centroid -> slope (s = (c-ref)*p_pix/f)
  ├─ reconstruct.c  #   zonal phi=R*s + modal a=M+*s (one GEMV each)
  ├─ dmcmd.c        #   a_dm = G*phi, gain + stroke clip
  ├─ matio.c        #   AOMX binary matrix I/O (byte-matches aokit/matio.py)
  ├─ bmp.c          #   hand-rolled BMP reader
  └─ linalg.c       #   portable AVX2/OpenMP-optional GEMV
aokit/              # Python offline toolkit (numpy/scipy/matplotlib)
                    #   geometry zernike centroiding reconstructor dm turbulence
                    #   datagen validation viz config bmpio matio
scripts/            # build_calibration / generate_dataset / run_pipeline_py / analyze_turbulence
tests/              # pytest: 219 unit tests + test_integration.py (end-to-end) + matio parity
results/            # plots/ (key figures) + *summary.json + c_core_timing_report.txt
                    #   (bulky frame/AOMX/CSV artifacts are .gitignored)
research/           # seven deep-research reports backing every design choice

Status

Fully implemented and validated end-to-end. Both tiers are complete and the whole inject-r₀/τ₀ → recover loop runs through both the C real-time core and the Python reference, cross-checked for parity:

  • ARCHITECTURE.md (deep, synthesizing all 7 research reports).
  • C core builds warning-clean (-Wall -Wextra -Wshadow -Wpointer-arith -Wcast-qual, links only -lm); --selftest passes.
  • AOMX format byte-matched on both sides (src/matio.caokit/matio.py).
  • All four top-level scripts implemented; aokit fully functional.
  • 226 tests pass (pytest -q): 219 unit + 7 end-to-end integration (tests/test_integration.py, which also asserts C↔Python parity via subprocess).

Results

Measured on the validated run above (10×10 lenslets → 80 illuminated sub-apertures, 256×256 frames, 101 valid Fried phase nodes, 14 Zernike modes, 60 frames; injected r₀ = 0.6 mm, τ₀ = 5 ms). Reproduce with the Quickstart commands; figures are in results/plots/, the numbers in results/c_core_timing_report.txt and results/turbulence_summary.json.

Real-time performance (C core bin/wfs_rt, the 10 ms budget)

Stage (per frame) min mean max
read + decode (BMP) 119.6 µs 132.3 µs 263.0 µs
centroid (TWCoG, 80 sub-aps) 184.4 µs 200.3 µs 231.6 µs
reconstruct (φ=R·s, a=M⁺·s) 2.4 µs 3.3 µs 15.6 µs
DM command (a=G·φ, clip) 1.3 µs 1.5 µs 4.2 µs
TOTAL 310 µs 337 µs 499 µs

2964 FPS, ≈30× under the 10 ms budget at the mean and ≈20× at the worst frame. The pure matrix-vector reconstruct + DM step (the optimized fast path) is ≈5 µs/frame — the centroiding and disk decode dominate, both still far under budget.

C ↔ Python parity (validates the optimized C MVM against the reference)

Same 60 frames through both pipelines, max absolute difference:

Quantity max |C − Python| scale interpretation
slopes (rad) 8.1 × 10⁻⁹ 2.1 × 10⁻³ float32 round-off
Zernike coeffs (rad) 4.1 × 10⁻⁶ 1.4 × 10⁰ float32 round-off
phase map (rad) 1.3 × 10⁻⁸ 2.9 × 10⁻³ float32 round-off
DM actuators (m) 1.1 × 10⁻¹⁴ 1.9 × 10⁻⁹ exact

The C core stores/computes the matrices in float32; the only differences are at the float32 round-off floor, so the hand-rolled C GEMV path reproduces the NumPy/SciPy reference exactly.

r₀ / τ₀ recovery (multi-method cross-validation)

Independent estimators across three data domains, median-combined (full per-method tables in results/turbulence_summary.json):

Parameter injected per-method values combined median recovery error
r₀ 0.60 mm R1 0.75, R2 0.86, R3 0.49, R4 0.90, R6 0.16 mm 0.75 mm 25.6 %
τ₀ 5.00 ms T1 6.2, T2 2.7, T3 6.3, T4 9.3, T5 6.3, T6 12.9 ms 6.28 ms 25.6 %

5 r₀ estimators (Zernike-variance, slope-variance, DIMM, phase-variance, von Kármán) and 6 τ₀ estimators (autocorrelation, PSD knee, Greenwood, structure function, frozen-flow, Tyler) all run and agree to within their spread. The research notes (research/04 §6) flag the load-bearing constants (slope-variance 0.170, DIMM 0.358, Greenwood 0.314…) as convention-dependent; the residual ~25 % is consistent with that systematic and with the genuine realization scatter of turbulence statistics over a small 10×10 aperture and a short sequence — which the combiner's reported spread captures honestly. See results/plots/r0_method_comparison.png and tau0_method_comparison.png.

Reconstruction quality & figures

Metric value
Zernike reconstruction RMS 0.153 rad (λ/41)
Strehl (Maréchal, from coeff residual) 0.977
DM coupling-deconvolution fit residual ≈0 rad (G = −½·H⁺ inverts H on the valid nodes)
mean illuminated sub-apertures 80 / 80

Key figures (saved to results/plots/): spot_field.png (SH-WFS frame + sub-aperture boxes + measured centroids), phase_map.png (reconstructed wavefront on the Fried nodes), zernike_spectrum.png, actuator_map.png (DM stroke map), residual.png (reconstructed W vs DM correction), and the r₀/τ₀ method-comparison bar charts.

Environment

Ubuntu 24.04, x86_64, 4 cores, no GPU; gcc 13 / clang 18, OpenMP, AVX2/AVX-512. Python 3.11. The C core has no external library dependency (only libm); the Python toolkit installs numpy/scipy/matplotlib from wheels.

License / attribution

Prepared for ISRO BAH 2026 PS9. Each algorithmic choice is justified in the research/ reports (with primary-source URLs) and integrated in ARCHITECTURE.md.

About

Fast Shack-Hartmann wavefront sensor pipeline: real-time wavefront reconstruction, atmospheric turbulence characterization (Fried parameter r0, coherence time τ0), and deformable-mirror actuator maps with inter-actuator coupling. Self-contained C real-time core (~0.3 ms/frame) + Python toolkit. ISRO BAH 2026 - Problem Statement 9.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Contributors

Languages