Skip to content
design
Instruments

Heave

The vertical motion of the vessel in meters, with a trend band and advice zones.

On this page
0.3
Heave
m
Motion sensor
-0.8
Lowest, 10 s
m
0.5
Highest, 10 s
m
Hove to in a building swell on a live feed: the yacht riding the heave, the ten-second band reaching the advice and caution zones, and a bar column on a wider range.
Code
import {
  AdviceType,
  Heave,
  ObcHeaveType,
  Priority,
  Readout,
  ReadoutSize,
  VesselImage,
  type LinearAdvice,
} from "@bosunkit/instruments";
import { useEffect, useState, type CSSProperties, type Dispatch, type ReactElement, type SetStateAction } from "react";

export const description: string =
  "Hove to in a building swell on a live feed: the yacht rides the heave beside its scale, the band of the last ten " +
  "seconds reaches the advice and caution zones as the sets come through, and a bar column repeats it on a wider " +
  "range.";

/** The swell where boarding the tender gets awkward, and where it stops. */
const ADVICE: LinearAdvice[] = [
  { min: 0.8, max: 1.2, type: AdviceType.advice, hinted: false },
  { min: -1.2, max: -0.8, type: AdviceType.advice, hinted: false },
  { min: 1.2, max: 3, type: AdviceType.caution, hinted: true },
  { min: -3, max: -1.2, type: AdviceType.caution, hinted: true },
];

const WIDE_ADVICE: LinearAdvice[] = [
  { min: 2, max: 5, type: AdviceType.caution, hinted: true },
  { min: -5, max: -2, type: AdviceType.caution, hinted: true },
];

const SAMPLE_SECONDS: number = 0.5;
const TREND_SECONDS: number = 10;

const PANEL: CSSProperties = {
  display: "flex",
  flexWrap: "wrap",
  alignItems: "center",
  justifyContent: "center",
  gap: 24,
  width: "100%",
  maxWidth: 660,
};

const COLUMN: CSSProperties = { display: "flex", flexDirection: "column", gap: 16 };

/** Heave in meters at a time in seconds: two wave trains under an envelope that builds and eases as sets pass. */
function heaveAt(time: number): number {
  const envelope: number = 0.7 + 0.45 * Math.sin(time / 12);
  return envelope * (0.9 * Math.sin((2 * Math.PI * time) / 7) + 0.45 * Math.sin((2 * Math.PI * time) / 4.3 + 1));
}

/** The lowest and highest heave over the trend window ending at `time`. */
function trendAt(time: number): { low: number; high: number } {
  let low: number = Number.POSITIVE_INFINITY;
  let high: number = Number.NEGATIVE_INFINITY;
  for (let past: number = 0; past <= TREND_SECONDS; past += SAMPLE_SECONDS) {
    const value: number = heaveAt(time - past);
    low = Math.min(low, value);
    high = Math.max(high, value);
  }
  return { low, high };
}

export default function Showcase(): ReactElement {
  const [sample, setSample]: [number, Dispatch<SetStateAction<number>>] = useState<number>(0);

  useEffect((): (() => void) => {
    const interval: number = window.setInterval((): void => {
      setSample((previous: number): number => previous + 1);
    }, SAMPLE_SECONDS * 1000);
    return (): void => {
      window.clearInterval(interval);
    };
  }, []);

  const time: number = sample * SAMPLE_SECONDS;
  const heave: number = heaveAt(time);
  const { low, high }: { low: number; high: number } = trendAt(time);

  return (
    <div style={PANEL}>
      <div style={{ width: 280, height: 280 }}>
        <Heave
          heave={heave}
          minTrendHeave={low}
          maxTrendHeave={high}
          instrumentRange={3}
          gainScale={2}
          draftOffset={0.2}
          vesselImage={VesselImage.sailingYachtFore}
          advice={ADVICE}
          priority={Priority.enhanced}
          type={ObcHeaveType.vessel}
        />
      </div>
      <div style={{ width: 120, height: 280 }}>
        <Heave
          heave={heave}
          minTrendHeave={low}
          maxTrendHeave={high}
          instrumentRange={5}
          advice={WIDE_ADVICE}
          type={ObcHeaveType.bar}
        />
      </div>
      <div style={COLUMN}>
        <Readout label="Heave" value={heave} fractionDigits={1} unit="m" src="Motion sensor" size={ReadoutSize.large} />
        <Readout label="Lowest, 10 s" value={low} fractionDigits={1} unit="m" />
        <Readout label="Highest, 10 s" value={high} fractionDigits={1} unit="m" />
      </div>
    </div>
  );
}

The heave instrument shows how far the vessel rises and falls, in meters, positive up. A scale column carries the current value and a band between the recent lowest and highest heave, and the default layout sets it beside a vessel silhouette that rides the motion. The scale spans plus and minus instrumentRange, 10 meters by default. It ports obc-heave; the overview lists the other instruments.

Examples

Heave

The vessel 0.6 meters above its mean height.
Code
import { Heave } from "@bosunkit/instruments";
import type { ReactElement } from "react";

export const description: string = "The vessel 0.6 meters above its mean height.";

export default function HeaveExample(): ReactElement {
  return (
    <div style={{ width: 200, height: 200 }}>
      <Heave heave={0.6} />
    </div>
  );
}

With vessel

The vessel with the band between its recent lowest and highest heave.
Code
import { Heave } from "@bosunkit/instruments";
import type { ReactElement } from "react";

export const description: string = "The vessel with the band between its recent lowest and highest heave.";

export default function WithVessel(): ReactElement {
  return (
    <div style={{ width: 200, height: 200 }}>
      <Heave heave={0.6} minTrendHeave={-0.9} maxTrendHeave={1.4} />
    </div>
  );
}

Bar

The scale column on its own, for a narrow strip beside another instrument.
Code
import { Heave, ObcHeaveType } from "@bosunkit/instruments";
import type { ReactElement } from "react";

export const description: string = "The scale column on its own, for a narrow strip beside another instrument.";

export default function Bar(): ReactElement {
  return (
    <div style={{ width: 200, height: 200 }}>
      <Heave heave={0.6} minTrendHeave={-0.9} maxTrendHeave={1.4} type={ObcHeaveType.bar} />
    </div>
  );
}

Reference and recent range

Heave is vertical motion in meters, positive upward. Identify the motion reference and supply recent minimum and maximum values from a stated window. That envelope is not a forecast or a safety limit.

Keep the range steady while comparing motion, and explain a clipped extreme with the numeric reading. Sensor alignment, filtering and availability belong to the data adapter. A flat line or centered vessel must not imply that an unavailable motion sensor reports a calm sea.

Props

PropTypeDefaultDescription
heavenumber | undefined0Current heave in meters (positive up).
gainScalenumber | undefined10Visual gain divisor applied to the vessel's vertical motion.
minTrendHeavenumber | undefined0Lower edge of the recent heave trend band, meters.
maxTrendHeavenumber | undefined0Upper edge of the recent heave trend band, meters.
draftOffsetnumber | undefined0Static vertical offset of the vessel silhouette, meters.
adviceLinearAdvice[] | undefined[]Heave advice zones; triggered when the trend band overlaps the range.
instrumentRangenumber | undefined10Full-scale heave of the instrument in meters (symmetric ±).
vesselImageVesselImage | undefinedVesselImage.psvForeFore-view vessel silhouette riding the heave.
priorityPriority | undefinedPriority.regularColor priority: regular (gray palette, default) or enhanced (blue palette) for the scale fill and the trend band.
typeObcHeaveType | undefinedObcHeaveType.vesselvessel (default) frames the scale next to a vessel silhouette; bar renders the scale column on its own.
classNamestring | undefined–Class names added to the instrument's root element.