groff-lab-reports — Unix lab report writing template via the groff(1) ms macro package. IEEE format with eqn(1) and tbl(1) preprocessors. Demonstrated with an Ohm’s Law (V = IR) measurement exercise.
eqn report.ms | tbl | groff -ms -Tpdf > report.pdf eqn report.ms | tbl | groff -ms -Tps > report.ps eqn report.ms | tbl | groff -ms -Thtml > report.html eqn report.ms | tbl | groff -ms -Tascii | less # terminal preview
The -ms flag is shorthand for -m s: the -m option loads a named macro package, and s selects the ms Manuscript Macros. In older documentation and informal usage this flag appears as -ms, -m ms, or colloquially “the md package” — all refer to the same macro set. eqn and tbl are preprocessors that must run before groff in the pipeline; order matters.
The groff(1) document formatting system is the GNU implementation of AT&T troff. Source files are plain ASCII with embedded requests (lines beginning with a period) and escape sequences (backslash followed by a code). The ms macro package provides high-level commands for the standard elements of an academic paper: title block, abstract, numbered section headings, indented paragraphs, floating keeps, and reference lists.
Two preprocessors handle specialized content before groff ever sees the file:
V ~=~ I cdot R) into troff drawing commands.
Source files conventionally carry the .ms extension. They are plain text; any editor works. The pipeline above produces print-quality output with correct equation typesetting, aligned tables, and automatic page numbering.
brew install groff
/opt/homebrew/bin/.
Verify with groff --version. To open a PDF directly from the
pipeline on macOS:
eqn report.ms | tbl | groff -ms -Tpdf | open -f -a Preview
| Macro | Purpose | Notes |
|---|---|---|
.TL | Title | Follows with title text on next lines until next macro. |
.AU | Author name | May be repeated for multiple authors. |
.AI | Author institution | Printed below corresponding .AU line. |
.DA [date] | Date | Omit argument to use today’s date automatically. |
.AB | Abstract begin | .AB no suppresses the “ABSTRACT” heading. |
.AE | Abstract end | Closes the abstract block; body begins after. |
| Macro | Output | Notes |
|---|---|---|
.NH 1 | I. HEADING | Numbered heading, level 1 (Roman numeral). |
.NH 2 | A. Subheading | Level 2 (capital letter). |
.NH 3 | 1. Sub-sub | Level 3 (Arabic numeral). |
.SH title | TITLE | Unnumbered section heading; used for References. |
| Macro | Purpose | Notes |
|---|---|---|
.PP | Paragraph | Indented first line. Most common body macro. |
.LP | Left paragraph | No indent; used for reference list items. |
.IP label n | Hanging-indent paragraph | Label hangs left; body indents n characters. Equipment lists, reference entries. |
.RS [n] | Increase indent | Right-shift; stack with .RE to restore. |
.RE | Restore indent | Closes a .RS block. |
.QP | Block quote | Indented both sides; for long quotations. |
.DS [L|I|C|B] | Display start | L=left, I=indented, C=centered, B=centered-block. Prevents line-filling. |
.DE | Display end | Closes a .DS block. |
.ce n | Center next n lines | Low-level request; used for captions and one-liners. |
| Escape | Effect | Example source |
|---|---|---|
\fB...\fP | Bold | \fBDMM\fP |
\fI...\fP | Italic | \fIV = IR\fP |
\fR...\fP | Roman (reset) | \fP alone restores previous font. |
.B text | Bold word(s) | .B "Results" |
.I text | Italic word(s) | .I V |
| Escape | Glyph | Use |
|---|---|---|
\(*W | Ω | Ohms (resistance unit) |
\(mu | μ | Micro prefix (μA, μF, μs) |
\(pm | ± | Plus-or-minus (tolerance, uncertainty) |
\(de | ° | Degree (temperature) |
\(en | – | En dash (ranges: 0–100 mA) |
\(em | — | Em dash (parenthetical) |
\(cq | ’ | Closing quote / apostrophe (Ohm’s) |
\~ | Non-breaking space (5\~V, 100\~\(*W) | |
\& | (zero-width) | Prevent macro interpretation of a period at line start |
| Macro | Purpose |
|---|---|
.KS / .KE | Keep: the enclosed block will not be split across a page break. Use around tables and figures. |
.KF / .KE | Floating keep: moves to top of next page if it does not fit; body text flows past it. |
The complete groff -ms source for the V = IR
lab report. Copy into ohmslaw.ms and compile with the
pipeline in the SYNOPSIS above. Comments begin with .\"
and are stripped before output.
.\" ============================================================
.\" ohmslaw.ms -- Verification of Ohm's Law
.\" Compile: eqn ohmslaw.ms | tbl | groff -ms -Tpdf > ohmslaw.pdf
.\" ============================================================
.TL
Verification of Ohm\(cqs Law:
Voltage Measurement and Current Confirmation via Series Ammeter
.AU
J.\~ Smith
.AI
Department of Electrical Engineering
State University \(em January 2026
.AB
This experiment verified Ohm\(cqs Law (\fIV\fP\~=\~\fII\fP\|\(*W) by
applying a regulated 5\~V source across a nominally 100\~\(*W carbon
film resistor and measuring the terminal voltage and branch current
independently. A 4.5-digit DMM provided the voltage measurement; a
series panel ammeter confirmed the current. Measured terminal voltage
was 4.97\~V. Current calculated from the measured voltage and the
in-circuit resistance was 49.7\~mA; the ammeter indicated 49.5\~mA,
agreeing to within 0.4\~%. Both values fall within the combined
instrument uncertainty of \(pm1.8\~%. These results confirm the linear
voltage-current relationship predicted by Ohm\(cqs Law for this class
of resistive component at the stated conditions.
.AE
.
.\" ---- I. INTRODUCTION ------------------------------------
.NH 1
Introduction
.PP
Ohm\(cqs Law states that the voltage across a resistor is proportional
to the current through it, with the constant of proportionality equal to
the resistance. The governing equation is:
.EQ
V ~=~ I cdot R
.EN
where
.I V
is the terminal voltage (V),
.I I
is the current (A), and
.I R
is the resistance (\(*W).
The objective of this experiment is to verify Equation\~(1) by
independently measuring all three quantities and checking internal
consistency.
.
.\" ---- II. MATERIALS AND METHODS --------------------------
.NH 1
Materials and Methods
.
.NH 2
Equipment
.IP "\fBDigital Multimeter (DMM)\fP \(em Fluke 115" 4
DC voltage range 400\~mV\(en1000\~V; accuracy \(pm0.5\~%\~+\~2 counts.
Configured in voltmeter mode (high impedance, parallel with DUT).
.IP "\fBSeries Panel Ammeter\fP \(em 0\(en100\~mA" 4
Analog D\(cqArsonval movement, \(pm2\~% of full scale.
Connected in series between the supply and resistor.
.IP "\fBResistor (DUT)\fP" 4
Carbon film, 100\~\(*W nominal, \(pm1\~% manufacturer tolerance,
axial through-hole. Measured with a bench LCR meter before assembly:
99.8\~\(*W.
.IP "\fBRegulated DC Bench Supply\fP" 4
0\(en30\~V\~/\~0\(en5\~A; output resolution 10\~mV; load regulation
<\~0.1\~%. Set to nominally 5.00\~V before connecting the circuit.
.
.NH 2
Procedure
.PP
The resistor was placed in series with the ammeter between the supply
output terminals. The DMM was connected in parallel with the resistor
only (not including the ammeter) to measure terminal voltage across the
DUT. The supply was energized and adjusted to 5.00\~V on its own
display. After a 30-second warm-up period, terminal voltage and ammeter
current were recorded simultaneously. Three successive readings were
taken without disturbing the circuit; the mean of the three readings is
reported.
.
.\" ---- III. RESULTS ---------------------------------------
.NH 1
Results
.PP
Measured values are summarized in Table\~1. All quantities are means
of three readings; the stated uncertainty represents one standard
deviation of the three measurements combined in quadrature with the
instrument accuracy specification.
.KS
.TS
center box;
lb lb lb lb
l l l l .
QuantityNominalMeasuredError (%)
_
R (LCR)100 \(*W99.8 \(*W0.2
V\d\s-2s\s+2\u (DMM)5.00 V4.97 V0.6
I\d\s-2calc\s+2\u50.0 mA49.7 mA0.6
I\d\s-2meas\s+2\u (ammeter)50.0 mA49.5 mA1.0
.TE
.ce 1
\fBTable 1.\fP Measured values for the V = IR verification circuit.
.KE
.PP
The propagated relative uncertainty in the derived resistance is given by:
.EQ
{ delta R } over R ~=~ sqrt { left ( { delta V } over V right ) sup 2
~+~ left ( { delta I } over I right ) sup 2 }
.EN
Substituting \(*dV/V\~=\~0.7\~% and \(*dI/I\~=\~2.1\~% yields
\(*dR/R\~=\~2.2\~%, which bounds the observed deviations in Table\~1.
.
.\" ---- IV. DISCUSSION -------------------------------------
.NH 1
Discussion
.PP
The ammeter-voltmeter method introduces a systematic loading error
because the voltmeter draws a finite current (\(ap0.3\~mA at 5\~V
with input impedance 10\~M\(*W), and the ammeter has a non-zero internal
resistance (\(ap2\~\(*W). At 100\~\(*W the ammeter burden is 2\~%,
which is the dominant contributor to the uncertainty budget. The DMM
loading at this impedance level is negligible (<\~0.1\~%).
.PP
All measured values agree with nominal within the propagated uncertainty.
The 0.4\~% difference between calculated and directly measured current
is within both the \(pm2\~% ammeter accuracy and the \(pm2.2\~%
propagated resistance uncertainty. No correction for systematic loading
error was applied; the residuals are smaller than the systematic term.
.
.\" ---- V. CONCLUSION --------------------------------------
.NH 1
Conclusion
.PP
Ohm\(cqs Law was confirmed for a 99.8\~\(*W resistor energized at
4.97\~V. Voltage, current derived from V/R, and directly measured
current agree to within combined instrument uncertainty. The primary
error source is ammeter internal resistance; it is bounded by the
published accuracy specification and requires no correction at this
precision level.
.
.\" ---- REFERENCES -----------------------------------------
.SH
References
.LP
.IP "[1]" 4
G.\~S.\~Ohm,
.I "Die galvanische Kette, mathematisch bearbeitet."
Berlin: T.\~H.\~Riemann, 1827.
.IP "[2]" 4
IEEE Std 1459-2010,
.I "IEEE Standard Definitions for the Measurement of
Electric Power Quantities Under Sinusoidal, Nonsinusoidal,
Balanced, or Unbalanced Conditions."
IEEE, 2010. doi:10.1109/IEEESTD.2010.5439063.
.IP "[3]" 4
J.\~R.\~Taylor,
.I "An Introduction to Error Analysis,"
2nd\~ed. Sausalito, CA: University Science Books, 1997,
pp.\~45\(en73.
That is the complete source: 90 lines of plain text, no binary assets, no build system. The output is publication-quality PDF with typeset equations, an aligned box-ruled table, centered caption, and a hanging reference list.
The following is how a researcher approaches the document from scratch. Each block of roff source is shown exactly as typed, followed by an explanation of every macro and escape sequence used. Read this section alongside the simple listing above.
.\" ============================================================ .\" ohmslaw.ms -- Verification of Ohm's Law .\" Compile: eqn ohmslaw.ms | tbl | groff -ms -Tpdf > ohmslaw.pdf .\" ============================================================
The .\" request (period, double-quote) is a comment.
Everything after it on the same line is ignored by groff. The researcher
uses the first block of comments as a file header: the filename, a short
description, and the exact compile command. This is the roff equivalent
of a shebang line and is considered good practice — the document is
self-documenting about how to build it.
.TL Verification of Ohm\(cqs Law: Voltage Measurement and Current Confirmation via Series Ammeter .AU J.\~ Smith .AI Department of Electrical Engineering State University \(em January 2026
.TL (Title) begins the title block. The lines that follow, until the next macro, become the document title. A second line is permitted (used here for the subtitle after the colon).
.AU (Author) takes the author name on the following line.
For multiple authors, repeat .AU and .AI pairs.
The escape J.\~ Smith uses \~ (non-breaking
thin space) between the initial and surname to prevent a line break there.
.AI (Author Institution) prints the affiliation below the
author name. Here the researcher folds the date into the second line of
the institution block using \(em (em dash) rather than a
separate .DA macro — a common shortcut when only a month
and year are needed.
.DA date is the formal alternative. Written as
.DA January 2026 it prints the date in the standard ms
location below the institution. Without an argument it inserts the
system date at compile time.
.RP (Released Paper)
before the title block enables the formal cover-page layout with centered
title, author, and abstract. For a simple single-page lab report, leave
.RP out.
.AB This experiment verified Ohm\(cqs Law (\fIV\fP\~=\~\fII\fP\|\(*W) by applying a regulated 5\~V source across a nominally 100\~\(*W carbon film resistor ... .AE
.AB (Abstract begin) and .AE (Abstract end) bracket the abstract paragraph. The ms package centers and italicizes the heading “ABSTRACT” automatically and applies a narrower line measure to the paragraph.
Inside the abstract the researcher uses several escapes:
\(cq — typographer’s closing quote (for Ohm’s).
Do not use a straight apostrophe; it renders poorly in PostScript output.
\fI...\fP — italic font for variable names
(V, I). \fP restores the previous font.
\~ — non-breaking thin space. Keeps numeric values
and their units together: 5\~V, 100\~\(*W.
\(*W — the omega glyph (ohm sign, Ω). This is
the most common special character in electrical engineering lab reports.
The two-character code *W is the traditional troff name.
\| — hairline space. Used between a closing
parenthesis and the unit symbol to avoid optical crowding: )\|\(*W.
\(pm — plus-or-minus sign (±), used for tolerance
and uncertainty: \(pm1.8\~%.
.NH 1 Introduction .PP Ohm\(cqs Law states ... .NH 1 Materials and Methods .NH 2 Equipment
.NH 1 generates a numbered primary heading. The ms package
maintains internal counters; successive .NH 1 calls produce
I., II., III., etc. The heading text on the following line is typeset in
boldface and the counter is prefixed automatically.
.NH 2 generates a secondary heading (A., B., C.) and resets
when a new .NH 1 is encountered. There is no need to
type the Roman numeral or letter — the package tracks them.
The bare period on its own line (.) is a null request. The
researcher inserts it as a visual separator between sections in the source
file. It has no effect on output but makes the source easier to scan.
.IP "\fBDigital Multimeter (DMM)\fP \(em Fluke 115" 4 DC voltage range 400\~mV\(en1000\~V; accuracy \(pm0.5\~%\~+\~2 counts. Configured in voltmeter mode (high impedance, parallel with DUT). .IP "\fBSeries Panel Ammeter\fP \(em 0\(en100\~mA" 4 Analog D\(cqArsonval movement, \(pm2\~% of full scale.
.IP label n (Indented Paragraph) creates a hanging-indent entry. The label argument appears at the left margin; the body text indents n characters from the margin. This is the idiomatic ms construction for equipment lists, definition lists, and reference entries.
The label is double-quoted because it contains spaces. Inside it:
\fB...\fP bolds the instrument name.
\(em separates the instrument type from the model number
with an em dash — a common lab report convention.
\(en is the en dash for ranges (400 mV–1000 V,
0–100 mA). Do not use a hyphen for numeric ranges in
typeset output.
After the last .IP group, the researcher resumes with
.NH or .PP to exit the hanging-indent mode.
An explicit .LP can also reset the margin.
.EQ V ~=~ I cdot R .EN
.EQ and .EN delimit an equation block. The eqn preprocessor (the first command in the pipeline) reads between these delimiters and translates the human-readable notation into troff sizing and positioning commands.
The eqn language for common operators used in this lab report:
| eqn source | Renders as | Use |
|---|---|---|
V ~=~ I cdot R | V = I · R | Ohm’s Law; ~ is a full space, cdot is a multiplication dot. |
I sup 2 | I² | Superscript (squared). |
I sub 0 | I₀ | Subscript. |
{ delta V } over V | δV / V | Fraction; braces group a multi-token numerator. |
sqrt { x sup 2 + y sup 2 } | √(x²+y²) | Square root; argument in braces. |
left ( ... right ) | (…) | Auto-sizing parentheses that grow to enclose their argument. |
delta | δ | Greek letter; eqn knows all Greek names by word. |
cdot | · | Centered multiplication dot. |
times | × | Cross-product multiplication sign. |
The uncertainty propagation equation from the results section:
.EQ
{ delta R } over R ~=~ sqrt { left ( { delta V } over V right ) sup 2
~+~ left ( { delta I } over I right ) sup 2 }
.EN
Long equations may wrap across source lines freely; eqn treats whitespace as a token separator, not a line terminator. The researcher breaks the source line here for readability only.
To add an equation number right-aligned on the same baseline, append a
label argument to .EQ:
.EQ (1) V ~=~ I cdot R .EN
This places (1) flush right, matching the IEEE convention
of right-justified equation numbers. In-text references use
Equation\~(1) with a non-breaking space before the parenthesis.
.EQ/.EN. Define delimiters once at
the top of the file with .EQ delim $$ .EN,
then write $V = IR$ inline. Inline mode is useful for brief
symbol references; display mode is required for numbered equations.
.KS .TS center box; lb lb lb lb l l l l . QuantityNominalMeasuredError (%) _ R (LCR)100 \(*W99.8 \(*W0.2 V\d\s-2s\s+2\u (DMM)5.00 V4.97 V0.6 I\d\s-2calc\s+2\u50.0 mA49.7 mA0.6 I\d\s-2meas\s+2\u (ammeter)50.0 mA49.5 mA1.0 .TE .ce 1 \fBTable 1.\fP Measured values for the V = IR verification circuit. .KE
.TS and .TE delimit a table processed by the tbl preprocessor (the second command in the pipeline).
The tbl block has two parts before the data: a global options line and one or more format lines.
center box; — center centers the table on the
page; box draws a border around the entire table. Other options:
allbox (rules between every cell), expand (full
page width), tab(|) (change column delimiter from tab to pipe).
The line ends with a semicolon.
lb lb lb lb — the header
row: four columns, left-aligned (l), bold (b).l l l l .
— data rows: four columns, left-aligned, normal weight. The period
terminates the format section; data begins on the next line. Extra spaces
between column codes are cosmetic alignment in the source and ignored by tbl.
_ (underscore) draws a horizontal rule
across all columns — used here to separate the header from the data.
= draws a double rule.
V\d\s-2s\s+2\u produces a subscript letter s after
V:\d moves down half a line (subscript baseline)\s-2 reduces point size by 2s (the subscript character)\s+2 restores the point size\u moves back up to the normal baseline.KS/.KE) wraps the entire table plus caption to prevent
a page break between them. The caption itself uses .ce 1
(center the next one line) followed by a bold-font inline escape
\fBTable 1.\fP. The caption is written after
.TE but the researcher should verify placement in the compiled
output and move it above .TS if the layout style requires it.
The researcher types one of these commands at the shell prompt. The source file is read left-to-right through each preprocessor before reaching groff.
eqn ohmslaw.ms | tbl | groff -ms -Tpdf > ohmslaw.pdf
-Tpdf produces a self-contained PDF. Open with any PDF viewer.
On macOS, pipe to open -f -a Preview instead of
redirecting to a file for a quick preview.
eqn ohmslaw.ms | tbl | groff -ms -Tps > ohmslaw.psConvert to PDF with
ps2pdf ohmslaw.ps if needed
(requires Ghostscript: brew install ghostscript).
eqn ohmslaw.ms | tbl | groff -ms -Tascii | lessUseful for a quick structural check before investing in a full PDF render. Equations appear as plain-text approximations; tables are drawn with ASCII box characters.
eqn ohmslaw.ms | tbl | groff -ms -Thtml > ohmslaw.htmlThe
grohtml driver produces functional but unstyled HTML.
Equation rendering depends on the installed driver. For polished HTML
output, consider converting the PDF to HTML with a dedicated tool, or
authoring the HTML separately (see template.html in this
distribution).
eqn expands .EQ/.EN blocks into raw troff before
tbl sees the file. If the order is reversed, tbl
may misinterpret equation delimiters as table separators. The conventional
safe order is always eqn | tbl | groff. If the document
contains no equations, eqn can be omitted; if it contains no
tables, tbl can be omitted.
.ms file directly to
groff -ms without the preprocessors if it contains
.EQ/.EN or .TS/.TE blocks. The macros will
pass through unexpanded, producing garbage output or a segfault in older
groff versions. Always use the full pipeline.
For a report that will be revised and recompiled repeatedly, a one-rule
Makefile eliminates retyping the pipeline:
# Makefile for ohmslaw.ms GROFF = groff MS_FLAG = -ms PREPROC = eqn $(MS_SRC) | tbl MS_SRC = ohmslaw.ms all: ohmslaw.pdf ohmslaw.pdf: $(MS_SRC) $(PREPROC) | $(GROFF) $(MS_FLAG) -Tpdf > $@ clean: rm -f ohmslaw.pdf ohmslaw.ps
Run make to (re)build only when the source has changed.
Run make clean to remove generated files. This pattern
scales to multi-file documents by splitting chapters into separate
.ms files and concatenating them in the pipeline:
eqn intro.ms methods.ms results.ms discussion.ms refs.ms | tbl | groff -ms -Tpdf > report.pdf
.NH counters are maintained globally by groff, not per-file.
The section numbering will be continuous without any extra configuration.
| Symptom | Cause | Fix |
|---|---|---|
Equation appears as raw .EQ/.EN text in output |
eqn not in pipeline |
Use eqn file.ms | tbl | groff -ms |
Table columns misaligned or .TS appears literally |
tbl not in pipeline, or columns separated by spaces not tabs |
Use tab characters between columns; include tbl in pipeline |
| Omega glyph (Ω) shows as a box or question mark | Output device lacks the glyph, or wrong escape used | Use \(*W (troff escape); confirm -Tpdf or -Tps output device |
| Abstract heading missing; just indented text | .AB no was used (suppresses heading) |
Use .AB alone to include the “ABSTRACT” heading |
| Table split across a page break | No .KS/.KE keep block around the table |
Wrap .TS ... .TE and caption inside .KS/.KE |
| Section numbers reset unexpectedly | Files concatenated out of order in the pipeline | Order source files from introduction to references in the pipeline command |
groff: can't find macro file ms |
System groff installed without the ms macros, or wrong groff in PATH | Install via Homebrew (brew install groff); verify path with which groff |
Non-breaking space \~ appears literally |
Using Apple’s legacy /usr/bin/groff (version < 1.22) |
Use Homebrew groff 1.23+; substitute \&\ (backslash-space) for \~ in older versions |
groff -m s
(ms macros), groff -m e (me macros),
groff -m m (mm memorandum macros),
groff -m doc (mdoc, for BSD man pages). The
shorthand -ms fuses the flag and the name. There is no
-md macro set in standard groff; “md” in informal
usage is shorthand for the ms manuscript macros, which provide the
center, indent, and structured heading
commands described throughout this document.
-mom)
provides finer typographic control and explicit two-column mode, at the cost
of a steeper learning curve.
\~ non-breaking space escape, the
.AB/.AE macros, and the -Tpdf driver are all
available in groff ≥ 1.22. Features requiring 1.23+: the
pdfmark extension for PDF bookmarks and the updated
grohtml driver.
Lynx compatible.