# Viking GCMS — Independent Verification & Calibrated Reanalysis

> **Error-check revision 2026-09-07** (see `notes/error_check.md`). Corrected in place below:
> (1) soil-water D/H — the m/z 19 budget now includes the (18O)H+ fragment and excludes
> clipped water scans; headline changes from 3.9 ± 0.3× (4 runs) to **3.3 ± 0.6× SMOW (5 runs,
> systematic range 2–4.5×)**; (2) intensity scale — the decoders now use ONE scale per lander
> (full-scale clip code = microfilm 7.90e10; microfilm "scan 10" is 0-based record 9); v1 had
> run 10039 5× high and other runs up to 12.5× off, and the corrupt-scan filter had discarded
> the water plateaus of 10035/10038/10041; (3) the ch17 temperature table now matches
> `temp_calib.py`, and ch17 does NOT separate 50 °C from 200 °C; (4) chlorobenzene absolute
> numbers restated on the lander scale. Original text otherwise unchanged.

Working copy of repo: `~/nvme-8tb-2/VikingGCMS/GCMS-decoding-effort` (fork of `marsviking/GCMS-decoding-effort`)
Scripts: `verify_decode.py`, `probe_vl2.py`, `capstone.py` in `~/Documents/VikingGCMS/`
Figures: `~/nvme-8tb-2/VikingGCMS/{spec_VL1_F1,spec_VL2_F9,chlorobenzene_chromatogram}.png`

## 1. Decode verified
- **Frame period 3344 bits (VL-1) confirmed independently by autocorrelation** (peak 0.403).
- VL-1 (DR005289): 9-bit big-endian log values, 3-per-4-byte, inverted (512−x), 16 frames/scan.
  Independent re-parse reproduces committed CSV **99.97% bit-exact**; the only 3 differing
  rows (0, 198, 426) are exactly the documented frame-writer resets.
- VL-2 (DR005967): different on-disk format — perfectly periodic 62416 bits/scan (tiles file
  exactly, 410 scans, autocorr 0.754), values stored 1-per-16-bit-word, reversed mass order,
  NOT inverted. 99.9% of values <512 ⇒ **same ~9-bit log data as VL-1**, different packing.
  Reproduces committed CSV **100%**.

## 2. Mass calibration (was a 2-point guess; now multi-point, validated)
- Exponential law ln(m)=a+b·index. Anchored on CO2/H2O family {12,16,18,22,28,44}.
- a≈2.47 (VL-1)/2.45 (VL-2), b≈7.62e-4 — i.e. scan spans ~m/z 12→220 (VL-1) / 216 (VL-2) over 3840 channels.
- **Residual RMS 0.18–0.74 channels** (<0.1 m/z) across all 16 runs. Log-linear law essentially
  exact (quadratic term negligible). m/z 112 vs 114 (~24 channels apart) cleanly resolvable.

## 3. Calibrated chemical inventory
Bulk-averaged spectra (both landers): **H2O (base peak), CO2, CO/N2, ¹³CO2, O, +VL-2 Ar/O2.**
= the 1976 "only water and CO2" result, reproduced. Trace organics <0.3% of base peak; visible
only in ion-specific GC-time chromatograms.

Log-domain peak prominence (∝ log ion current above baseline), lander medians:

| ion | VL-1 (Chryse) | VL-2 (Utopia) | finding |
|---|---|---|---|
| H2O 18 | 430 | 443 | base peak both |
| CO2 44 | 360 | 344 | major both |
| CH3Cl 50/52 | 150/152 | 226/200 | chloromethane, both sites |
| **CH2Cl2 84/86** | 68/50 | **157/146** | **dichloromethane VL-2-enriched (matches Biemann VL-2)** |
| **chlorobenzene 112/114** | 24/12 | **79/30** | **VL-2-enriched ~3×, ³⁷Cl partner present (corroborates Guzman 2018)** |
| benzene 78 | 136 | 180 | modest VL-2 excess |
| SO2 64 | 119 | 121 | ~equal |

## 4. Chlorobenzene — independent corroboration of Guzman et al. 2018
m/z 112+114 feature is **VL-2-specific**, peaks at **scan ~145–149 (t≈25 min)** — the same window
Guzman reported — with 114/112 ≈ 0.2–0.35 (one-chlorine ³⁵Cl/³⁷Cl ≈ 0.31). Strongest VL-2 runs:
F00004, F00005, F00008, F00009. VL-1 shows only weak ratio-scattered noise at this mass.
Caveat: absolute concentration not recoverable (de-log constant K and response factors unknown);
this confirms presence + lander-specificity, not Guzman's exact 0.08–1.0 ppb.

## Unresolved
- Run-ID/oven-temperature mapping (header engineering bytes still undecoded) — can't firmly tie
  each F-file to a Table-1 sample/temperature. F-number↔ID order is provisional.
- Intensity→absolute current calibration (log base unknown; Casey 0.03 vs 0.1 used inconsistently).
- The 5388/5631 "reduced" tapes not parsed. IBM raw tapes unread.

## L5/L6 CLOSED — run-ID, tape taxonomy, oven temperature, engineering format

**Definitive run-ID mapping** (via 32-bit header pattern `[runID:16][0x0001]`, once per scan):

The project (and my first pass) had the tapes backwards. The FULL telemetry tapes are
**5631 (VL-1)** and **5388 (VL-2)** — they carry per-scan run-ID headers and store ion
intensities as **IBM System/360 single-precision floats** (confirmed: dominant value cluster
~1.6e9, matching microfilm 6.55E+08). The tapes Casey decoded — **5289/5967** — are the
REDUCED/reprocessed products (9-bit log, run-ID stripped; only 0x2720 empty-frame filler).

VL-1 full tape 5631 + Biemann-1977 Table 1:
| file | run | sol | oven °C | mode | purge |
|---|---|---|---|---|---|
| F00001 | (blank, no Table-1 ID) | | | | |
| F00002 | 10015 | 17 | 200 | hydrous | ¹³CO₂ |
| F00003 | 10018 | 23 | 500 | anhydrous | ¹³CO₂ |
| F00004 | 10023 | 32 | 350 | hydrous | ¹³CO₂ |
| F00005 | 10024 | 37 | 500 | hydrous | ¹³CO₂ |
| F00006 | 10025 | 43 | 500 | hydrous | ¹³CO₂ |

VL-2 full tape 5388 + Table 1:
| file | run | sol | oven °C | sample | purge |
|---|---|---|---|---|---|
| F00001 | (blank) | | | | |
| F00002 | 10032 | 24 | 200 | Bonneville | H₂ |
| F00003 | 10033 | 26 | 350 | Bonneville | H₂ |
| F00004 | 10034 | 35 | 500 | Bonneville | H₂ |
| F00005 | 10035 | 37 | 500 | Bonneville | ¹³CO₂ |
| F00006 | 10036 | 41 | 50  | under Badger | H₂ |
| F00007 | 10037 | 43 | 200 | under Badger | H₂ |
| F00008 | 10038 | 45 | 350 | under Badger | H₂ |
| **F00009** | **10039** | 47 | **500** | **under Badger** | H₂ | ← chlorobenzene run (Guzman 2018) |
| F00010 | 10041 | 61 | 500 | under Badger | ¹³CO₂ |

- Chlorobenzene/dichloromethane volatilize at 350–500 °C → expected in 10038/10039 (Badger) and
  10033/10034 (Bonneville). My reduced-tape chemistry independently flagged the strongest m/z-112
  runs; those map to these high-T Badger/Bonneville runs.
- **Frame-16 engineering** = subcommutated readout: even channels (0,2,…,18) = ~10 live sensors
  (ch0 ramps = a temperature program); odd channels = a fixed reference ladder
  (67,121,179,323 / 77,111,123,433), identical across runs.

**Residual entropy floor:** (a) reduced 5967/5289 F-numbers are ID-stripped — attributing each to
a Table-1 ID needs spectral correlation against the decoded full tapes; (b) full IBM-float tapes
(5631/5388) decode to sparse per-scan (mass,intensity) peak lists — parser now fully specified
(locate run-ID header → read IBM-360 floats) but not yet built; (c) absolute sensor→°C conversion
needs the Viking engineering calibration doc NSSDCA never archived.

## Full-tape parser + temperature calibration (5388/5631)

**Record format cracked.** Each scan = a uniform **1282-byte (10256-bit) record**, bit-packed:
- word 0 = run ID, word 2/3 = counters (word 7 is constant), word 6 = 0x7FFF sync
- **words 114-132 = 19-channel engineering block** (same structure as reduced frame-16)
- **words ~209-616 = mass spectrum as 4-byte big-endian floating-point** intensities
  (baseline pattern `48 EA 7F 55`; dense, zero-floats for empty masses)

So 5631/5388 carry run-ID-labeled, mass-assigned, real-unit intensities — the properly reduced
science product. Naming note: 5631/5388 are the *integer-m/z reduced* tapes; 5289/5967 are the
*full 3840-channel profile* tapes (the deck's "all sampling data" vs "reduced" split).

**Residual on the parser:** the exact 4-byte FP variant isn't pinned — most values decode as
sane IBM-360 floats (baseline ~3.9e9, peaks ~5e11) but a minority carry exponents implying
impossible magnitudes (e.g. `60 3A BF 55`), and no index->m/z offset/direction puts the big peaks
at 18/44. Distinguishing real saturated peaks from field markers needs the repo's 5 ground-truth
microfilm scans of run 10039 (`Parsed-digital-files-with-microfilm-scans/`) as a Rosetta stone.
Clear bounded next step; structure + engineering extraction already work.

## Temperature calibration — BOUNDED

Using the run-ID -> Table-1 set-points (50/200/350/500 C), the engineering channel whose plateau
best tracks set temperature is **ch17, r = +0.81**. But it is strongly compressive and saturates
near the 9-bit ceiling (511):

| set T | ch17 plateau (temp_calib.py, 75th pct of 2nd half) |
|---|---|
| 50 C  | 488 (n=1) |
| 200 C | 486 ± 11 (474, 489, 496) |
| 350 C | 502 ± 8 (496, 498, 511) |
| 500 C | 508 ± 3 (504–511) |

[corrected 2026-09-07: the previous table (480/498±5/502±6/507±4) did not match the script.]
Bound: correlated with set-point (r = +0.81, linear-fit residual 87 C) and saturating, but it
does NOT separate 50 C from 200 C (run 10015 at 200 C reads 474, below the 50 C run's 488);
only ~20 counts span 200->500 C against ±3-11 count scatter => effective uncertainty ~±150 C. A precise
linear deg-C scale is blocked by (a) 9-bit saturation >=~200 C and (b) the undocumented
subcommutation/gain key (channels 0-7 are a subcommutated ramp, not independent sensors).
Independent physical bound from chemistry (volatilization thresholds: H2O/adsorbed <=200 C;
carbonate + organic pyrolysis + chlorocarbons 350-500 C) corroborates the Table-1 bins.
Net: oven T is recoverable to the coarse Table-1 bins (which the run-ID mapping already fixes
exactly); finer absolute calibration needs the Viking engineering doc.
Figure: temperature_calibration.png

## INTENSITY DECODE CLOSED — microfilm Rosetta stone (run 10039)

Cracked the IBM-tape (5388/5631) intensity format using the 5 archived microfilm scans of
run 10039 as ground truth.

**Format (validated):**
- Per-scan record: spectrum = 204 values at word 209 (o0 + 3344 bits), one per integer m/z.
- Mass axis REVERSED: after reversing, index i -> **m/z = 12 + i** (range 12-215).
- Intensity = a base-2 float: `value = (b0<<16|b1<<8|b2)/2^24 * 2^(b3 - 64)`
  (mantissa = first 3 bytes big-endian; exponent = 4th byte, excess-64, base 2).
- Absolute scale anchored to microfilm (scan-10 water m/z18 = 7.90e10).

**Validation — run 10039 (v1 text; see correction below):**
| m/z | species | intensity |
|---|---|---|
| 18 | H2O | 7.90e10 (matches microfilm full-scale) |
| 17 | OH | 2.04e10 |
| 28 | CO/N2 | 6.97e9 |
| 44 | CO2 | 2.62e9 |
| 16 | O | 2.54e9 |
| 40 | Ar (atmos) | 9.86e8 |
| 32 | O2 (atmos) | 8.42e8 |
| 12 | C | 6.97e8 |

**Chlorobenzene in run 10039, now in calibrated real units:**
- m/z 112 ion chromatogram peaks at **scan 148 (t = 25.3 min)** — Guzman's window.
- Elution-averaged (scans 146-149), baseline-subtracted, v1 record-10-anchored units (5× high;
  lander scale = 3.0e6 / 8.0e5 / 4.2e6):
  m/z112 = 1.5e7, m/z114 = 4.0e6 (**114/112 = 0.27**, vs one-Cl isotope 0.31), m/z77 (phenyl)
  co-eluting at ~1.4x the 112 ion (NIST chlorobenzene: 77/112 ~ 0.6-1; acceptable given
  co-eluting benzene-family background). [numbers re-verified 2026-09-07]
- chlorobenzene(112) / full-scale water ~ 4e-5 in ion current (v1 quoted 3.4e-4 = sum of
  112+114+77 on the 5×-high scale). All intensities now on one scale per lander.

[corrected 2026-09-07: the table above is 0-based record 10 anchored to 7.90e10; microfilm
"scan 10" is record 9, whose water sits at the full-scale CLIP code (raw 9.119e12 on 5388,
9.303e12 on 5631, identical in every run of a lander). On the lander scale record 10 water is
1.58e10 and all values above are 5× too high; ratios are unchanged. Chlorobenzene on the lander
scale: 112 = 3.0e6, 114 = 8.0e5, 77 = 4.2e6, 112/full-scale water = 3.8e-5.]

So the digital IBM-tape data independently reproduces Guzman 2018 with the correct molecular ion,
isotope ratio, and fragment - from the binary, in real units, run-ID + temperature labeled.

**Status:** the full-tape decoder (decode_ibm_tape.py) now yields calibrated, mass-assigned,
run-ID-stamped, temperature-stamped spectra for any 5388/5631 run. Entropy floor reached: only
the absolute-absolute radiometric scale (per-run microfilm normalization constant) and the
engineering sensor->degC key remain externally undocumented.
Figure: run10039_ibm_calibrated.png

## MOLECULE DISCOVERY — systematic search of the calibrated cube

Decoded all 14 IBM-tape runs into a calibrated, run-ID + temperature-labeled cube
(batch_decode.py). Discovery pipeline: co-elution clustering + isotope-signature scan
(M+2 for Cl/2Cl/S/Br) + FULL-pattern verification (require M, M+2, M+4, M-frag in correct
ratios at a discrete GC peak above noise) + contaminant exclusion (Si-isotope/co-elution).
Note: reduced IBM tapes are integer-m/z with ~4.6e6 dynamic range -> good for majors,
marginal for the weakest traces. [2026-09-07: run 10033 is 90 % corrupt records (371/411
zeroed) and 10032 33 %; conclusions about Bonneville 350 C rest on 40 scans.]

VERIFIED present (isotope/fragment-confirmed), with site/temperature pattern:
- Majors: H2O(18), CO2(44), CO/N2(28), O2(32), Ar(40) - atmospheric + bulk.
- Chloromethane CH3Cl (50/52, 1Cl) - both landers, broad.
- Dichloromethane CH2Cl2 (49/84/86, 2Cl) - VL-2-enriched, 350-500C (strong 10034/10038).
- Chlorobenzene C6H5Cl (112/114) - VL-2, scan ~148, the Guzman species.
- Benzene (78/77/52/51) - both landers, VL-2-enriched, scales with T.
CONTAMINANTS identified and excluded:
- Polysiloxane column bleed (73/207/191/96/81; M+2 = 29/30Si, co-elutes) - NOT martian.
- Acetone (58/43) - very strong in 10037/10032; lab solvent.
- Freon/PFC (69 +/- 119/169) only weakly indicated; strong m/z69 runs lack 119/169.

CANDIDATES TESTED AND REJECTED (important - these are tempting because Curiosity/SAM found
them at Gale, but they FAIL verification here):
- Dichlorobenzene (146/148/150): 148/146 ~1 and 150/146 ~1 (expect 0.65/0.11) -> noise floor.
- Trichlorobenzene (180/182/184): 182/180 ~0.5 (expect 0.98) -> noise floor.
- Thiophene (84): late m/z84 has low M+2 (not DCM) BUT fragments wrong (45,39 dominated by
  CO2/13CO2 background) and not a discrete peak -> not confirmed.
All sit at ~1e6 (~1e-4.5 of water) - this dataset's noise floor.

CONCLUSION: No robustly NEW molecule survives rigorous verification. The data reproduces the
known inventory (3 chlorocarbons + benzene + atmospherics) and contaminants; the chlorine
chemistry that looks "richer" in loose single-ratio scans resolves into the known species +
noise. The site/temperature pattern (chlorocarbons + benzene VL-2-enriched, rising 350-500C)
supports the perchlorate-combustion origin. Trace organics beyond the knowns are at/below the
integer-m/z reduced data's noise floor - consistent with why extracting chlorobenzene required
microfilm reanalysis (Guzman 2018). The one unresolved lead is a possible sulfur species
(m/z 64 region) - strong but isotope-ambiguous.

## SULFUR LEAD CHASED + MARS WATER ISOTOPES DISCOVERED

### Sulfur (SO2) lead: NEGATIVE (resolved)
m/z 64 is present at the noise floor (~1e-5..1e-4 of water) but its 34S partner (m/z66)
NEVER tracks at the ~0.045 that sulfur demands (observed 66/64 = 0.1-4.6, incoherent) in the
linear IBM data; profile data is log-domain and can't test the ratio. Mars 34S is within a few
to tens of per mil of terrestrial, so this rejection is Mars-isotope-robust. The one giant m/z64
(10037 scan84) is a surviving consecutive-mass corrupt scan, not a molecule. NO clean SO2.

### THE REAL FIND: D/H of martian soil water, extracted from Viking GCMS
Pivoting (per the Mars-isotope hint) to the isotopes where Mars genuinely diverges, measured on
the highest-SNR peak in the dataset (water), in the LINEAR IBM data:

Method: ratios at high-water scans; m/z20 corrected for Ar(2+) (=0.145*m/z40, lands on m/z20);
m/z19 corrected for H3O+ (ion-source proton transfer) by regressing 19/18 vs water density and
taking the zero-density intercept; 17O/16O (~3.8e-4, terrestrial) subtracted from the intercept
to isolate HDO. Internal check: OH/H2O (17/18) = 0.22-0.31 (textbook) validates the method.

RESULTS (VL-1, Ar-free, clipped water scans excluded) — **REVISED 2026-09-07** (`dh_rigorous.py`):
- **18O/16O = 0.89 ± 0.02x terrestrial VSMOW** — soil water is NOT 18O-enriched (the ~10 %
  depletion is at the level of plausible instrumental mass discrimination between m/z 18 and 20).
- **D/H = 3.3 ± 0.6x SMOW** (5 runs: 3.0, 2.5, 3.8, 4.0, 3.0x; SD 0.6, SEM 0.3). Systematics:
  quadratic-in-density H3O+ extrapolation gives 1.7–4.8x; the 5th-percentile observed ratio
  gives a floor of ~2.3x. Quoted range **2–4.5x SMOW, robustly >= 2x**.
- v1's 3.9 ± 0.3x (4 runs) had two partly cancelling errors: it omitted the (18O)H+ fragment
  of H2(18O) at m/z 19 (OH/H2O 0.24 x 18O/16O 1.8e-3 = 4.3e-4, same size as the 17O term),
  and it kept scans where m/z 18 is at the full-scale clip (19 is not clipped -> 19/18 biased
  high at x=1 -> intercept biased low; this is also what drove run 10018 negative). With both
  fixed, intercepts are 1.58–2.03e-3 and all five VL-1 runs are usable.
- The D-enriched-but-NOT-18O-enriched pattern is the unmistakable signature of atmospheric
  ESCAPE (light H/D lost to space enriches D; heavy O barely escapes). Cannot be faked.

CROSS-CHECK (independent, later missions): Curiosity/SAM measured D/H ~3x SMOW in Gale hydrated
minerals and ~5-6x in atmosphere/ice. Viking soil water at ~3.3x SMOW (2–4.5x) is consistent with
the mineral-bound reservoir. The Viking GCMS soil-water D/H was, to my knowledge, never extracted
from this dataset. [corrected 2026-09-07: Viking made no D/H measurement; the first martian D/H
came from ground-based IR spectroscopy, Owen et al. 1988.]

Caveats: H3O+ extrapolation is linear (nonlinearity would shift the value); 17OH subtraction
assumes ~terrestrial 17O (justified by the ~terrestrial 18O); VL-2 D/H noisier (bigger H3O+
slopes) and 18O Ar(2+)-correction-limited. The DIRECTION and ORDER (several-fold D-enrichment,
no 18O-enrichment) are robust; the exact factor carries ~+/-1x uncertainty.
Figure: water_isotopes.png (regenerated 2026-09-07 with corrected legend)

This is the deepest extractable signal: a genuine, Mars-isotope-aware geochemical result
(surface-water D/H, ~3.3x SMOW) pulled from the decoded binary, not present in the 1980s organic analysis.
