Skip to content
Alberto Pico Lara edited this page Oct 28, 2026 · 1 revision

Welcome to the advanced_timegrapher wiki!


Table of Contents / Índice

  1. Overview / Visión General
  2. DSP Engine & Escapement Physics / Motor DSP y Física del Escape
  3. ADC Clock Calibration via PPS/GPS & Local SNTP / Calibración de Reloj ADC mediante PPS/GPS y SNTP Local

1. Overview / Visión General

🇲🇽 Español — Visión General del Proyecto

Advanced Timegrapher (adv-tg) es un cronocomparador de alta precisión por software para calibres mecánicos y de cuarzo. Diseñado bajo una arquitectura desacoplada en Python 3.12+ sobre NumPy (sin dependencias en C++ o SciPy en tiempo de ejecución), captura las señales acústicas de los escapes mecánicos a través de micrófonos piezoeléctricos de contacto (p. ej., CM18 USB piezo) y calcula en tiempo real:

  • Marcha (Rate): desvío diario en segundos por día ($\text{s/d}$).
  • Error de batimiento (Beat Error): asimetría temporal del tic y el tac en milisegundos ($\text{ms}$).
  • Amplitud: ángulo de oscilación del volante en grados ($^\circ$).
  • Deriva del reloj ADC: error de cristal del códec de audio en partes por millón ($\text{ppm}$).

Arquitectura por Capas

[ Micrófono / CM18 ]
         │
         ▼
[ Capa 1: Captura de Audio ] ──► Proceso hijo independiente (PortAudio/sounddevice)
         │                       Ring buffer circular SPSC sin contención de GIL
         ▼
[ Capa 2: Detección DSP ]   ──► Filtro pasabanda fase cero + Envolvente Hilbert FIR
         │                       Plantillas medianas para tic/tac (t_fine)
         ▼
[ Capa 3: Métricas ]        ──► Regresión ponderada por paridad + Jackknife robusto
         │
         ▼
[ Capa 4: Disciplina ADC ]  ──► Estimador Theil-Sen sobre envolvente inferior / PPS
         │
         ▼
[ Capa 5: GUI (PyQt6) ]     ──► Cinta de papel en pyqtgraph, osciloscopio P1/P2/P3

🇺🇸 English — Project Overview

Advanced Timegrapher (adv-tg) is a software-based acoustic timegrapher engineered for mechanical and quartz watch movements. Implemented in Python 3.12+ and NumPy without runtime SciPy or compiled C++ dependencies, it ingests contact acoustic signals from mechanical escapements using piezo microphones (such as the CM18 USB clamp) to measure in real time:

  • Rate: daily drift expressed in seconds per day ($\text{s/d}$).
  • Beat Error: time difference between tick and tock events in milliseconds ($\text{ms}$).
  • Amplitude: balance wheel oscillation rotation in degrees ($^\circ$).
  • ADC Clock Drift: sound card crystal frequency offset in parts per million ($\text{ppm}$).

2. DSP Engine & Escapement Physics / Motor DSP y Física del Escape

🇲🇽 Español — Motor DSP y Algoritmos de Medida

1. Cinemática del Escape de Áncora Suizo

Cada alternancia acústica se descompone en tres sub-eventos físicos de impacto:

  1. $P_1$ (Desbloqueo / Unlocking): La elipse entra en la horquilla del áncora y levanta la paleta de reposo.
  2. $P_2$ (Impulso / Impulse): El diente de la rueda de escape recorre el plano de impulso de la paleta.
  3. $P_3$ (Caída / Drop): El siguiente diente de la rueda de escape impacta contra la cara de reposo de la paleta complementaria.

2. Ecuaciones Fundamentales

  • Frecuencia y Periodo Nominal: $$f = \frac{\text{BPH}}{7200} \quad [\text{Hz}], \qquad P_{\text{nominal}} = \frac{3600}{\text{BPH}} \quad [\text{s}]$$

  • Amplitud del Volante ($A$): Calculada a partir del ángulo de alzamiento (Lift Angle, $\text{LA}$) y el tiempo transcurrido durante el ángulo de impulso ($t_{\text{lift}} = P_3 - P_1$): $$A = \frac{\text{LA}}{2 \cdot \sin(\pi \cdot f \cdot t_{\text{lift}})}$$

  • Marcha (Rate): $$\text{Rate} = \left(\frac{P_{\text{nominal}}}{P_{\text{medido}}} - 1\right) \cdot 86400 \quad [\text{s/d}]$$

  • Conversión ppm a s/d: $$1 \text{ ppm} = 0.0864 \text{ s/d}$$

3. Flujo de Señal Digital

  • Filtrado: Filtro Butterworth IIR pasabanda aplicado en modo bidireccional (filtfilt) para anular la distorsión de fase.
  • Envolvente: Filtro analítico de transformada de Hilbert FIR con ventanado Blackman y suavizado temporal.
  • Alineación Fina ($t_{\text{fine}}$): Generación de plantilla mediana móvil sobre los últimos 24 batimientos por paridad para independizar la medida del ruido de caída en $P_3$.

🇺🇸 English — DSP Engine and Movement Physics

1. Swiss Lever Escapement Events

Each recorded acoustic beat represents three distinct physical events:

  1. $P_1$ (Unlocking): Roller jewel contacts the pallet fork notch, lifting the locking face.
  2. $P_2$ (Impulse): Escape wheel tooth slides over the impulse plane of the pallet jewel.
  3. $P_3$ (Drop): Consecutive escape wheel tooth drops onto the locking face of the opposite pallet.

2. Key Formulas

  • Lift Time: $$t_{\text{lift}} = P_3 - P_1$$
  • Amplitude: $$A = \frac{\text{LA}}{2 \cdot \sin(\pi \cdot f \cdot t_{\text{lift}})}$$
  • Rate: $$\text{Rate} = \left(\frac{P_{\text{nominal}}}{P_{\text{measured}}} - 1\right) \cdot 86400 \quad [\text{s/d}]$$

3. ADC Clock Calibration via PPS/GPS & Local SNTP / Calibración de Reloj ADC mediante PPS/GPS y SNTP Local

🇲🇽 Español — Calibración de Reloj ADC

1. El Desafío del Oscilador Local del ADC

Los conversores analógico-digitales (ADC) de tarjetas de audio de consumo utilizan osciladores de cristal sin compensación térmica que suelen desviar entre $+5$ y $+15\text{ ppm}$. Dado que $1\text{ ppm} = 0.0864\text{ s/d}$, una desviación de $+9.2\text{ ppm}$ introduce un sesgo sistemático de: $$\text{Sesgo} = -9.2 \times 0.0864 \approx -0.795 \text{ s/d}$$ Esto provoca que un calibre perfectamente regulado a $+0.5\text{ s/d}$ se registre en pantalla en torno a $-0.3\text{ s/d}$.

Nivel de Confianza Fuente de Calibración Incertidumbre Típica
Nivel 0 Sin calibrar $\pm 30\text{ ppm}$
Nivel 1 Reloj de sistema sin disciplinar No se aplica en la marcha
Nivel 2 Reloj de sistema disciplinado por NTP público $0.1 - 1.0\text{ ppm}$
Nivel 3 Hardware externo (PPS/GPS o cuarzo patrón) $< 0.5\text{ ppm}$

2. Método A: Inyección Acústica de PPS/GPS mediante Buzzer (Nivel 3 Hardware)

Inyecta un pulso transitorio a exactamente 1.000000000 s utilizando la salida PPS (Pulse Per Second) de un módulo satelital.

  ┌───────────────────────┐
  │ Módulo GPS (NEO-6M)   │
  │                       │
  │                   PPS ├────┐  Pulso 1 Hz (100 ms)
  └───────────────────────┘    │
                               ▼
  ┌────────────────────────────────────────────────────────┐
  │ Circuito Disparador / Oscilador 4 kHz                  │
  │ (NE555 / Transistor NPN / Buzzer Piezoeléctrico)       │
  └────────────────────────────┬───────────────────────────┘
                               │
                               ▼ Clic acústico resonante (4 kHz, 10 ms)
  ┌────────────────────────────────────────────────────────┐
  │ Micrófono Piezoeléctrico CM18 USB                      │
  └────────────────────────────┬───────────────────────────┘
                               │ Audio USB
                               ▼
  ┌────────────────────────────────────────────────────────┐
  │ Host PC (`adv_tg.tools.adc_clock`)                     │
  └────────────────────────────────────────────────────────┘
Procedimiento y Algoritmo:
  1. Generación del Pulso: El pin PPS del módulo GPS (u-blox NEO-6M o similar) entrega un flanco positivo con jitter $< 50\text{ ns}$ al obtener fijación 3D de satélites.
  2. Generación Acústica: La entrada del micrófono corta la componente continua mediante capacitores de acoplo. El pulso activa un buzzer pasivo sintonizado a 4 kHz durante 10 ms, acoplado a la pinza del micrófono.
  3. Cálculo de Muestras Reales: Con una tasa nominal de muestreo $F_s = 48000\text{ Hz}$, el número de muestras entre flancos de subida del buzzer debe compararse contra el valor nominal: $$\Delta N_k = \text{sample_index}k - \text{sample_index}{k-1}$$
  4. Estimación Robusta Theil-Sen: $$\text{Deriva}_{\text{ppm}} = \left( 1 - \frac{\text{mediana}(\Delta N_k)}{F_s} \right) \times 10^6$$ Tras registrar entre 300 y 600 pulsos (5 a 10 minutos), la incertidumbre $\sigma$ se reduce a $< 0.1\text{ ppm}$. El valor se guarda en adc_calibration.json con nivel 3.

3. Método B: Disciplina mediante Servidor Local SNTP/NTP Stratum-1 (Nivel 2 Refinado)

Convierte un nodo de red local (por ejemplo, una Raspberry Pi) en un servidor de tiempo Stratum-1 conectado a un receptor GPS con soporte PPS por hardware.

  [ Constelación GPS ]
         │
         ▼
  [ Módulo GPS NEO-6M ] ──┬── UART (/dev/ttyAMA0) ──► Sentencias NMEA (Tiempo absoluto)
                          └── PPS  (GPIO 18)       ──► Flancos de subida (/dev/pps0)
                                  │
                                  ▼
                         [ Kernel Linux PPS + chrony ]  (Servidor Stratum-1 en LAN)
                                  │
                                  ▼ Red Local Ethernet / Wi-Fi (Jitter < 0.2 ms)
                         [ PC de Medida con adv_tg ]
                                  │
                         `CLOCK_MONOTONIC` disciplinado a < 0.1 ppm
Configuración de Referencia (chrony.conf en Raspberry Pi):
# Disciplina PPS a nivel de kernel
refclock PPS /dev/pps0 refid PPS precision 1e-9 poll 0 prefer
# Referencia de tiempo NMEA por puerto serie
refclock SHM 0 refid NMEA precision 1e-1 offset 0.128 delay 0.2 poll 0 noselect

# Permitir consultas directas de la subred local
allow 192.168.1.0/24
Integración en adv_tg:
  1. El equipo que corre adv_tg sincroniza su reloj de sistema con el servidor Stratum-1 local en intervalos cortos vía NTP.
  2. Cada bloque de audio de PortAudio se etiqueta con su índice de fin de muestra y la marca de tiempo de nanosegundos del sistema (CLOCK_MONOTONIC en Linux o time.time_ns en macOS/Windows).
  3. El módulo ClockDriftTracker (Capa 4) procesa la envolvente inferior de los tiempos de llegada usando Theil-Sen para eliminar el jitter del planificador del sistema operativo y del bus USB.
  4. Con el sistema anfitrión disciplinado con precisión de microsegundos, la calibración alcanza estabilidad comparable a un Nivel 3 sin requerir hardware acústico adicional.

🇺🇸 English — ADC Clock Calibration

1. The Clock Drift Dilemma

Low-cost USB sound cards incorporate crystal oscillators that routinely deviate by $+5$ to $+15\text{ ppm}$. Since $1\text{ ppm} = 0.0864\text{ s/d}$, an uncorrected $+9.2\text{ ppm}$ offset induces an error of $\approx -0.8\text{ s/d}$ across all mechanical escapement measurements.

Confidence Level Calibration Source Typical Uncertainty
Level 0 Uncalibrated defaults $\pm 30\text{ ppm}$
Level 1 Undisciplined system clock Discarded for rate correction
Level 2 System clock synced via public NTP $0.1 - 1.0\text{ ppm}$
Level 3 Hardware reference (PPS/GPS or reference quartz) $&lt; 0.5\text{ ppm}$

2. Method A: Acoustic PPS Injection via 4 kHz Buzzer (Hardware Level 3)

  • Hardware Setup: A u-blox NEO-6M GNSS receiver emits a 1 Hz pulse (100 ms duration, $&lt; 50\text{ ns}$ jitter).
  • Acoustic Transducer: Drives a 4 kHz piezo buzzer attached directly to the microphone clamp.
  • Drift Extraction: $$\text{Drift}{\text{ppm}} = \left( 1 - \frac{\Delta \text{Samples}}{F_s \cdot \Delta T{\text{GPS}}} \right) \times 10^6$$
  • A 10-minute acquisition yields an uncertainty $\sigma &lt; 0.1\text{ ppm}$, recorded directly into adc_calibration.json.

3. Method B: Local Stratum-1 NTP/SNTP Server Discipline (Refined Level 2)

  • Hardware Architecture: A dedicated local node (e.g., Raspberry Pi) paired with a GPS module using hardware PPS (/dev/pps0) and NMEA serial data.
  • Time Service: chrony configured as a Stratum-1 reference clock on the local subnet.
  • Client Synchronization: The workstation running adv_tg disciplines its operating system monotonic clock against the LAN Stratum-1 server, eliminating wide-area network jitter.
  • Internal Processing: The ClockDriftTracker engine applies Theil-Sen linear regression to the lower arrival envelope of incoming audio blocks, establishing sound card crystal offsets without physical acoustic coupling.