Why Warehouses Need Check Characters

In high-volume retail fulfillment networks (such as Argos and Sainsbury’s logistics networks), human pickers navigate thousands of shelf bays per shift. If verification relied simply on reciting the shelf code itself (e.g., location "10AK"), confirmation bias would lead pickers to glance at the location code on their terminal, walk to an adjacent bay (e.g., "10AL"), and sub-consciously repeat "10AK" from memory.

To break this confirmation bias, warehouse systems decouple the shelf coordinate from the verification response. Each shelf label displays a prominently printed "Check Character" (or check digit) that cannot be deduced by simple visual inspection of the shelf code. The worker must read the physical check character printed on the shelf label into the microphone.

Behind the scenes, the warehouse system calculates this character deterministically using weighted mathematical algorithms.

The Store Location Algorithm: Weighted ASCII Modulo 26

In store stockrooms, locations typically follow a 4-character alphanumeric structure (e.g., "10AK", where "10" denotes the aisle, "A" denotes the bay, and "K" denotes the shelf level).

The algorithm processes each character by its 7-bit ASCII value and applies a position-dependent weight vector: W = [5, 7, 7, 3]. The weights ensure that adjacent digit swaps or single-character substitutions produce distinct mathematical sums.

The mathematical formulation is defined as:

typescript
/**
 * Store Check Character Mathematical Formulation
 *
 * Location: c0, c1, c2, c3 (e.g. '1', '0', 'A', 'K')
 * Position Weights: W = [5, 7, 7, 3]
 *
 * Sum = (ASCII(c0) * 5) + (ASCII(c1) * 7) + (ASCII(c2) * 7) + (ASCII(c3) * 3)
 * Remainder = Sum % 26
 * Check Character = NATO_PHONETIC_ALPHABET[Remainder]
 */

export function calculateStoreCheckChar(location: string): string {
  const chars = location.trim().toUpperCase().substring(0, 4);
  const weights = [5, 7, 7, 3];
  let sum = 0;

  for (let i = 0; i < 4; i++) {
    sum += chars.charCodeAt(i) * weights[i];
  }

  const remainder = sum % 26;
  const NATO = [
    'ALPHA', 'BRAVO', 'CHARLIE', 'DELTA', 'ECHO', 'FOXTROT',
    'GOLF', 'HOTEL', 'INDIA', 'JULIET', 'KILO', 'LIMA',
    'MIKE', 'NOVEMBER', 'OSCAR', 'PAPA', 'QUEBEC', 'ROMEO',
    'SIERRA', 'TANGO', 'UNIFORM', 'VICTOR', 'WHISKEY', 'X-RAY',
    'YANKEE', 'ZULU'
  ];

  return NATO[remainder];
}
TypeScript implementation of the Store weighted ASCII modulo 26 calculation.

Step-by-Step Calculation Walkthrough: Location "10AK"

Let us trace the exact arithmetic for location "10AK":

• Position 1: Character "1" → ASCII 49 × weight 5 = 245

• Position 2: Character "0" → ASCII 48 × weight 7 = 336

• Position 3: Character "A" → ASCII 65 × weight 7 = 455

• Position 4: Character "K" → ASCII 75 × weight 3 = 225

Total Sum: 245 + 336 + 455 + 225 = 1,261

Modulo Operation: 1,261 mod 26 = 13

NATO Alphabet Mapping: Index 13 maps to "NOVEMBER".

Therefore, when a picker arrives at shelf "10AK", the printed label displays "NOVEMBER" (or phonetic "N"), confirming exact location alignment.

Special Store Zone Prefixes and Operational Spoken Conventions
Prefix CodeOperational Zone NameSpoken Voice ConventionExample Location
JJewelleryJEWELLERY [Bay] [Shelf]J1PG → "JEWELLERY ONE PAPA GOLF"
SSecuritySECURITY [Bay] [Shelf]S1PG → "SECURITY ONE PAPA GOLF"
LLinbin (Small Parts Bin)[Aisle] LINBIN [Shelf]L10A → "ONE ZERO LINBIN ALPHA"
ZBulk Overstock[Aisle] BULK [Shelf]Z10A → "ONE ZERO BULK ALPHA"
CACash OfficeCASH OFFICE [Number]CA12 → "CASH OFFICE ONE TWO"
CPCollection PointCOLLECTION POINT [Number]CP12 → "COLLECTION POINT ONE TWO"
RETSReturns HoldingFixed Static AssignmentRETS → "OSCAR" (Store) / "FOUR EIGHT" (RFC)

The Regional Fulfilment Centre (RFC) Algorithm: Modulo 99

In massive Regional Fulfilment Centres (RFCs), warehouse layouts scale across multiple mezzanine floors and high-bay aisles. RFC locations use a 4-letter alphabetical coordinate system (e.g., "ABCD", "RETS", "ZZZZ").

Rather than mapping to a NATO word, the RFC engine outputs a two-digit spoken number (00 through 98) using an additive base offset of 45 and a modulo 99 operator.

The zero-indexed alphabetical value v_i is computed as ASCII(c_i) - 65, ensuring values range strictly between 0 and 25:

typescript
/**
 * RFC Check Character Formulation
 *
 * Location: 4 letters (e.g. 'R', 'E', 'T', 'S')
 * Letter Value: v_i = ASCII(char) - 65 (0 to 25)
 * Base Offset: 45
 * Position Weights: W = [5, 7, 7, 3]
 *
 * Sum = 45 + (v0 * 5) + (v1 * 7) + (v2 * 7) + (v3 * 3)
 * Check Digit = Sum % 99
 * Format: Spoken tens and units (e.g. 48 -> "FOUR EIGHT")
 */

export function calculateRfcCheckChar(location: string): { checkDigits: string; spokenWords: string } {
  const chars = location.trim().toUpperCase().substring(0, 4);
  const weights = [5, 7, 7, 3];
  let sum = 45; // Base offset constant

  for (let i = 0; i < 4; i++) {
    const letterValue = chars.charCodeAt(i) - 65;
    sum += letterValue * weights[i];
  }

  const checkVal = sum % 99;
  const tens = Math.floor(checkVal / 10);
  const units = checkVal % 10;
  const DIGIT_WORDS = ['ZERO', 'ONE', 'TWO', 'THREE', 'FOUR', 'FIVE', 'SIX', 'SEVEN', 'EIGHT', 'NINE'];

  return {
    checkDigits: checkVal.toString().padStart(2, '0'),
    spokenWords: `${DIGIT_WORDS[tens]} ${DIGIT_WORDS[units]}`
  };
}
RFC Modulo 99 calculation with base 45 offset and digit expansion.

Mathematical Comparison: Luhn vs Modulo 26 vs Modulo 99

How does this approach compare to standard commercial checksums like the Luhn algorithm (used in ISO/IEC 7812 credit card validation) or ISBN-10 (modulo 11)?

• Luhn Algorithm: Highly effective at detecting single-digit substitutions and adjacent digit transpositions for purely numeric strings, but relies on base-10 doubling and cannot natively handle mixed alphanumeric strings without character map tables.

• Store Modulo 26 with [5, 7, 7, 3] Weights: Tailored specifically for speech interaction. By mapping remainders to the 26 NATO words, it converts an abstract mathematical verification step into an unmistakable acoustic phrase.

• RFC Modulo 99: Expands the remainder space from 26 potential outcomes to 99 potential outcomes (00 to 98), reducing the probability of random accidental collision from 1 in 26 (~3.8%) down to approximately 1 in 99 (~1.01%).