#!/bin/bash set -e # ═══════════════════════════════════════════════════════════════════════════ # telfax — End-to-End Fax Demo # Shows the complete fax workflow over real POTS lines. # # Modem A (sender): /dev/cu.usbmodem00000021 (USR5637) → 2528-9852 # Modem B (receiver): /dev/cu.usbmodem123456781 (V90) → 2748-6656 # ═══════════════════════════════════════════════════════════════════════════ RECV_DEVICE="/dev/cu.usbmodem123456781" # V90 — answers calls SEND_DEVICE="/dev/cu.usbmodem00000021" # USR5637 — dials out RECV_NUMBER="27486656" # V90's phone number OUTPUT_DIR="/tmp" RESOLUTION="fine" CLASS="1" # Class 1 HDLC protocol # ── Timestamp for output files ── STAMP=$(date +%s) DEMO_DOC="/tmp/telfax_demo_doc_${STAMP}.tif" COVER_PAGE="/tmp/telfax_cover_${STAMP}.tif" PREVIEW_HTML="/tmp/telfax_demo_${STAMP}.html" RECV_LOG="/tmp/telfax_recv_${STAMP}.log" SEND_LOG="/tmp/telfax_send_${STAMP}.log" echo "" echo "╔══════════════════════════════════════════════════════════════════╗" echo "║ telfax — End-to-End Fax Demo ║" echo "║ Real POTS Lines | Class 1 | V.27ter 4800 ║" echo "╚══════════════════════════════════════════════════════════════════╝" echo "" # ══════════════════════════════════════════════════════════════════════ # STEP 1 — Create a demo document with visible content # ══════════════════════════════════════════════════════════════════════ echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo " STEP 1: Create a demo document (business letter)" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" python3 "$(dirname "$0")/create_demo_doc.py" "$DEMO_DOC" echo " ✓ Document saved: $DEMO_DOC" echo "" # ══════════════════════════════════════════════════════════════════════ # STEP 2 — Add a cover page (sender/recipient info) # ══════════════════════════════════════════════════════════════════════ echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo " STEP 2: Add a cover page" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" # Generate cover page only (preview with both cover + doc) # We'll use the preview command later which handles cover + doc together echo " Cover parameters:" echo " From: Jane Doe " echo " To: John Smith " echo " Subject: Business Proposal for Enterprise Fax Solution" echo " Notes: Please review at your earliest convenience." echo " ✓ Cover page will be auto-generated during send" echo "" # ══════════════════════════════════════════════════════════════════════ # STEP 3 — Preview cover page + document # ══════════════════════════════════════════════════════════════════════ echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo " STEP 3: Preview cover page + document (HTML in browser)" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" cargo run --release -- preview "$DEMO_DOC" \ --output "$PREVIEW_HTML" \ --resolution fine \ --to "John Smith " \ --from "Jane Doe " \ --subject "Business Proposal for Enterprise Fax Solution" \ --note "Please review at your earliest convenience." \ 2>&1 | grep -v "^warning:\|^ Compiling\|^ Finished\|^ Running\|^$" echo "" echo " ✓ Preview saved to: $PREVIEW_HTML" echo " ✓ First page = cover page, Second page = document" echo "" # Try to open in browser (macOS) if command -v open &>/dev/null; then echo " → Opening preview in browser..." open "$PREVIEW_HTML" 2>/dev/null || true fi # ══════════════════════════════════════════════════════════════════════ # STEP 4 — Start receiver in background, then send # ══════════════════════════════════════════════════════════════════════ echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo " STEP 4 & 5: Send fax + Receive fax" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" echo " ┌─ Sender ─────────────────────────────────────┐" echo " │ Device: $SEND_DEVICE │" echo " │ Number: 27486656 (V90) │" echo " │ Doc: $DEMO_DOC │" echo " │ Class: 1 (V.27ter 4800 bps) │" echo " └──────────────────────────────────────────────┘" echo " ┌─ Receiver ───────────────────────────────────┐" echo " │ Device: $RECV_DEVICE │" echo " │ Auto-answer: 2 rings │" echo " │ Output: $OUTPUT_DIR │" echo " │ Class: 1 │" echo " └──────────────────────────────────────────────┘" echo "" # Kill any leftover processes pkill -f "telfax.*receive" 2>/dev/null || true sleep 2 # Start receiver (background, wait for call) echo " → Starting receiver (V90 listening on 2748-6656)..." RUST_LOG=telfax=info cargo run --release -- receive \ -d "$RECV_DEVICE" \ -c 1 \ -r 2 \ -o "$OUTPUT_DIR" \ > "$RECV_LOG" 2>&1 & RECV_PID=$! echo " ✓ Receiver PID: $RECV_PID" # Wait for V90 to settle (auto-answer with ATS0=2 needs ~13s) echo " → Waiting 40s for V90 to settle before dialing..." sleep 40 # Send fax (foreground — shows live progress) echo " → Sending fax (this takes ~60s)..." echo "" RUST_LOG=telfax=info cargo run --release -- send \ -d "$SEND_DEVICE" \ -c 1 \ -r "$RESOLUTION" \ --to "John Smith " \ --from "Jane Doe " \ --subject "Business Proposal for Enterprise Fax Solution" \ --note "Please review at your earliest convenience." \ "$RECV_NUMBER" "$DEMO_DOC" \ 2>&1 | grep -v "^warning:\|^ Compiling\|^ Finished\|^ Running\|^$" \ | sed 's/.*telfax:/\t| telfax:/' \ | sed 's/.*telfax::/\t| telfax::/' \ || true echo "" # Wait for receive to finish echo " → Waiting for receiver to complete..." wait $RECV_PID 2>/dev/null || true echo "" # ══════════════════════════════════════════════════════════════════════ # STEP 6 — Show the status / progress # ══════════════════════════════════════════════════════════════════════ echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo " STEP 6: Transmission status" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" echo " ┌─ SENDER LOG ────────────────────────────────────┐" grep -E "Document:|training|Dummy frame|Sending page|Waiting for (CONNECT|OK)|error_rate|FRH CONNECT|MCF|DCN|sent successfully" "$SEND_LOG" \ | sed 's/.*telfax::/\t| telfax::/' \ | sed 's/.*telfax:/\t| telfax:/' || echo " (no sender log found)" echo " └────────────────────────────────────────────────┘" echo "" echo " ┌─ RECEIVER LOG ──────────────────────────────────┐" grep -E "Sending HDLC|FRH CONNECT|Received HDLC|error_rate|FRM CONNECT|FRM line.*CONNECT|Raw FRM|Found DLE|Destuffed|Received page|EOP|MCF|Hanging|TIFF saved|Decoding" "$RECV_LOG" \ | sed 's/.*telfax::/\t| telfax::/' \ | sed 's/.*telfax:/\t| telfax:/' || echo " (no receiver log found)" echo " └────────────────────────────────────────────────┘" echo "" # ══════════════════════════════════════════════════════════════════════ # STEP 7 — Show the received document # ══════════════════════════════════════════════════════════════════════ echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo " STEP 7: Received fax document" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" RECEIVED_TIF=$(grep "TIFF saved to" "$RECV_LOG" | sed 's/.*TIFF saved to //' | tr -d '[:space:]') if [ -f "$RECEIVED_TIF" ]; then SIZE=$(stat -f%z "$RECEIVED_TIF" 2>/dev/null || stat -c%s "$RECEIVED_TIF" 2>/dev/null) echo " ✓ Received file: $RECEIVED_TIF" echo " ✓ Size: $SIZE bytes" # Generate a preview of the received pages for quick viewing RECV_PREVIEW="/tmp/telfax_recv_preview_${STAMP}.html" cargo run --release -- preview "$RECEIVED_TIF" --output "$RECV_PREVIEW" 2>&1 \ | grep -v "^warning:\|^ Compiling\|^ Finished\|^ Running\|^$" || true echo "" echo " ✓ Preview of received fax: $RECV_PREVIEW" if command -v open &>/dev/null; then open "$RECV_PREVIEW" 2>/dev/null || true fi else echo " ⚠ No received TIFF found. Check receiver log: $RECV_LOG" grep -E "error|Error|Error:|Timeout|Protocol|NoCarrier" "$RECV_LOG" \ | sed 's/.*telfax::/\t| /' || true fi echo "" # ══════════════════════════════════════════════════════════════════════ # STEP 8 — Explain the whole flow # ══════════════════════════════════════════════════════════════════════ echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo " STEP 8: Protocol Flow Explanation" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" echo " T.30 Class 1 fax transmission over analog POTS:" echo "" echo " ┌─────────────────────────────────────────────────────────────┐" echo " │ RECEIVER (V90) SENDER (USR5637) │" echo " │ ────────────── ──────────────── │" echo " │ │" echo " │ 1. Auto-answer (2 rings) Dial 27486656 │" echo " │ ├────── CED (2100 Hz) ──────┤ │" echo " │ ├────── DIS (HDLC) ─────────┤ │" echo " │ ├────── DCS (HDLC) ─────────┤ │" echo " │ │ negotiate V.27ter 4800 │ │" echo " │ ├────── TCF (FRM) ──────────┤ FTM 1.5s zeros │" echo " │ │ ✓ CONNECT + receive │ │" echo " │ ├────── CFR (HDLC) ─────────┤ FTH (HDLC) │" echo " │ │ ├── dummy FTH (HDLC) │" echo " │ 2. FRM (enter before training) FTM page data │" echo " │ ├────── Page Data ──────────┤ V.27ter 4800 bps │" echo " │ │ ✓ CONNECT + receive │ 14,599 bytes MH │ echo " │ ├────── EOP (HDLC) ─────────┤ FRH │" echo " │ ├────── MCF (HDLC) ─────────┤ FTH │" echo " │ ├────── DCN (HDLC) ─────────┤ │" echo " │ │" echo " │ Total: 1 page, 14636 bytes MH, TIFF saved to disk │" echo " └─────────────────────────────────────────────────────────────┘" echo "" echo " Key engineering details:" echo " ─────────────────────────" echo " • V.27ter 4800 bps (fine resolution ~204×196 dpi)" echo " • Real POTS lines — no VoIP, no T.38" echo " • HDLC framing with CRC-16 (DLE-stuffed)" echo " • MH (Modified Huffman) run-length compression" echo " • Zero-delay FRM: V90 must catch training from start" echo " • DLE-ETX end-marker detection via rposition" echo "" echo "" echo "╔══════════════════════════════════════════════════════════════════╗" echo "║ Demo complete! ║" echo "║ ║" echo "║ All logs: ║" echo "║ Sender: $SEND_LOG ║" echo "║ Receiver: $RECV_LOG ║" echo "║ ║" echo "║ Outputs: ║" echo "║ Preview: $PREVIEW_HTML ║" echo "║ Received: $RECEIVED_TIF ║" echo "║ Demo doc: $DEMO_DOC ║" echo "╚══════════════════════════════════════════════════════════════════╝" echo ""