commit e1c1012c1c5011fce4333313b233f5461dbf1c81 Author: erik Date: Fri Oct 2 10:41:22 2026 +0000 Hama myScan Linux driver tools: scan mode, frame decoding, stitching UVC extension-unit control (mouse/scan mode, LEDs, exposure), JPEG header fix and embedded laser record decoding, session recorder, and a stitcher that combines laser dead reckoning with phase correlation and ECC refinement. FINDINGS.md holds the reverse-engineered protocol and laser calibration. Co-Authored-By: Claude Opus 5.5 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0733dd8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +.venv/ +__pycache__/ +*.pyc + +# Proprietary vendor binaries (Dacuda installer / Mac app) - not redistributable +*.exe +vendor/ + +# Raw capture frames; keep only the sensor log and stitched result of the sample session +capture/ +scan_text/* +!scan_text/records.csv +!scan_text/page.png diff --git a/FINDINGS.md b/FINDINGS.md new file mode 100644 index 0000000..6571027 --- /dev/null +++ b/FINDINGS.md @@ -0,0 +1,345 @@ +# Hama myScan scanner mouse: reverse-engineering findings + +Status as of 2026-10-02: **scanning works on Linux.** The stock `uvcvideo` and +`hid-generic` drivers handle the device. A small user-space tool switches the +mouse into scan mode (LED illumination plus per-frame sensor data) through the +UVC extension unit. Everything needed was reverse engineered from the +**Dacuda Scanner Mouse for Mac 2.4** package (see "The real driver" below). +What remains is software: auto-exposure and stitching frames into a page. + +## Quick reference: how to scan + +All access goes through UVC extension unit 6, selector 2 (5 bytes), using +`UVCIOC_CTRL_QUERY` on the video node. See `myscan/xu.py`. + +``` +bridge write : SET_CUR [0x00, reg, 0, value, 0] +bridge read : SET_CUR [0x80, reg, 0, 0, 0] then GET_CUR -> value in byte 3 +``` + +Start scanning, exactly as `Hal::OVMouseManager::startVideo` does: + +1. write `D8 = 0x00`, poll `D8` until it reads `0xFF` ("disable mouse", standby) +2. write `2D = 0x60` and `E7 = 0x28` (each one is read back to verify) +3. optionally write `E6` = exposure (`round(ms × 49.23)`, 0–255 ≈ 0–5.2 ms) +4. write `D8 = 0xE1`, poll until `0x99`; then stream MJPEG from `/dev/videoN` + +Stop: write `D8 = 0xE0`, poll until `0x11` (normal mouse mode again; `0x11` +is also the idle value after plugging in). + +Verified on the device: the acks come back exactly as expected (`FF`, `99`, +`11`), frames light up in proportion to `E6`, and each frame carries a live +sensor record (see "Embedded sensor record"). + +## Hardware probing results (device attached) + +### USB identity + +``` +idVendor=274f idProduct=c001 bcdDevice=c6.16 +Manufacturer: Omnivision Product: Scanner Mouse +``` + +High-speed USB 2.0, one configuration, max power 500 mA. It's a composite +device, and the kernel binds it without any extra driver: + +| Interface | Class | Kernel driver | Notes | +|---|---|---|---| +| 0 | Video Control (UVC 1.00) | `uvcvideo` | Interrupt status EP 0x82; one vendor extension unit | +| 1 | Video Streaming | `uvcvideo` | Isochronous EP 0x81, 3×1020 B; MJPEG only | +| 2 | HID mouse | `hid-generic` | Interrupt EP 0x83 | + +dmesg notes "UVC non compliance - GET_DEF(PROBE) not supported", which the +kernel works around automatically. + +### Video format and the bogus JPEG header + +- The only format is MJPEG, **640×296 at 30 fps** (`dwMaxVideoFrameBufferSize` + 378,880 = 640×296×2). +- Each frame is a complete JPEG (FFD8 … FFD9, about 4.6–18 KB, YCbCr 4:2:2), + **but its SOF0 header claims 480×640**. Decoded as-is, the image looks + smeared and the data runs out after 400 rows, because 1500 MCUs of 16×8 at + 480 px wide make 400 rows. +- **Fix:** rewrite the SOF0 height/width fields to 296/640 before decoding. + That gives clean 640×296 frames, with the 1500 MCUs filling 40 × 37.5 MCU + rows: + + ```python + i = raw.find(b'\xff\xc0') + raw = raw[:i+5] + (296).to_bytes(2,'big') + (640).to_bytes(2,'big') + raw[i+9:] + ``` +- The image is effectively grayscale (all channels equal). + +Verified: a frame taken with the mouse lifted toward a window clearly shows +window blinds. A frame taken while sliding over printed text shows text +strokes (motion-blurred, because the exposure is long). + +### Illumination (solved) + +In mouse mode the LEDs are off and frames are almost black (mean ≈ 8/255). +In scan mode the LEDs are strobed for the exposure window set in bridge +register `E6`. The power-on default is `E6 = 5` (≈ 0.1 ms), which is still +nearly black. Measured brightness (mean / max) on a dark fabric mousepad: + +| E6 | exposure | mean | max | +|---|---|---|---| +| 5 | 0.10 ms | 8.9 | 24 | +| 40 | 0.81 ms | 22.4 | 141 | +| 120 | 2.44 ms | 51.2 | 228 | +| 255 | 5.18 ms | 89.7 | 250 | + +The Mac app runs a PID auto-exposure loop over `E6` (strings `pidGainP/I/D`, +`exposureTimeUsMin/Max`) and stores per-device calibration in the mouse's +EEPROM ("HardwareData": `exposureTimeUs`, `imagerGain`, laser scaling, …). + +### UVC controls + +- The Input Terminal (camera) and Processing Unit advertise **no** standard + controls, so there are no exposure, gain or brightness controls through V4L2. +- **Extension Unit ID 6, GUID `dd880f8a-1cba-4954-8a25-f7875967f0f7`, 3 controls** + (queried with `UVCIOC_CTRL_QUERY` on `/dev/video4`): + +| Selector | Length | Info (GET/SET) | GET_CUR | Interpretation | +|---|---|---|---|---| +| 1 | 20 | 0x01 (GET only) | `020c0616 01070000 4f2701c0 0038f501 00774242` | Device info: contains VID/PID; `02 0c 06 16` matches bcdDevice c616 (firmware version/date?) | +| 2 | 5 | 0x03 (GET+SET) | `8f311c6900` (constant) | Unknown; possibly a register address/value or command register | +| 3 | 33 | 0x03 (GET+SET) | read stalls (EPROTO) | Probably a command/response buffer: write first, then read | + +MIN/DEF are all zeros, MAX all 0xFF, RES 1, so they say nothing useful. +Selector 2 turned out to be the register-access channel and selector 3 a +buffer channel; see "The real driver" for the full protocol. + +### HID mouse + +Report descriptor (73 bytes): 5 buttons + 3 padding bits, int16 X, int16 Y, +int8 wheel, int8 AC Pan. There is no report ID, and reports are 7 bytes: + +``` +byte 0: buttons bit0 left, bit1 right, bit2 middle, bit3/4 side, + bit7 = SCAN BUTTON (not declared in the descriptor) +byte 1-2: dX (int16 LE) byte 3-4: dY (int16 LE) byte 5: wheel byte 6: pan +``` + +The scan button sits in the "padding" bits, so the kernel's input layer +ignores it. It can only be read through `/dev/hidrawN`. Motion reports stop +when the mouse is lifted (laser lift-off detection). The 16-bit deltas at up +to ~125 Hz give a position track that can seed the frame stitching. + +### Tooling + +- `myscan/xu.py`: extension-unit register access and the scan-mode state machine. +- `myscan/frame.py`: JPEG header fix plus embedded sensor-record decoder. +- `tools/scantest.py`: enter scan mode, grab frames, restore mouse mode. +- `tools/probe.py` records raw frames and HID reports side by side (with + `frames.csv` / `hid.csv` timelines). Run it as + `.venv/bin/python tools/probe.py -d 60 -o capture`. +- `capture/` holds a 60 s recording: resting on paper, sliding over text, + button presses, and lifted toward a window. + +### Container passthrough (Incus, container `ai`) + +```sh +incus config device add ai myscan-usb usb vendorid=274f productid=c001 mode=0666 +incus config device add ai myscan-video4 unix-char source=/dev/video4 path=/dev/video4 mode=0666 required=false +incus config device add ai myscan-video5 unix-char source=/dev/video5 path=/dev/video5 mode=0666 required=false +incus config device add ai myscan-hidraw unix-char source=/dev/hidraw8 path=/dev/hidraw8 mode=0666 required=false +``` + +The node numbers can change when the mouse is replugged. + +--- + +## The real driver: Dacuda Scanner Mouse for Mac 2.4 + +- **Source:** the Wayback Machine has `Dacuda-ScannerMouse-Mac-V2.4.0.411.dmg` + (484 MB, archived 2016-05-23), linked from Dacuda's archived "scanner mouse + get started" page. The matching Windows `Dacuda-ScannerMouse-Win-V2.4.0.413.exe` + was never archived. The DMG is saved in `vendor/`, with the unpacked app in + `vendor/mac/payload/Scanner_Mouse.app`. +- **Key binaries:** + - `Contents/Frameworks/UVCControl.framework`: Omnivision's camera-control + library (author path `/Developer/usr/hslee4/UVCControl_110619/`), with + full symbols, fat x86_64/i386/ppc. Classes `UVCCameraControl` and + `CameraControlAppDelegate`, C API `OVIF_*`, plus `DaMouseCIF.m`. + - `Contents/MacOS/Scanner Mouse` (i386 only, 33 MB, Qt5): the app. Its + hardware layer is `Hal::OVMouseManager` + (`libs/Hal/Driver/LuckyCat/OVMouseManager.cpp`), used through `HalFree`. + +### UVCControl.framework: the transport layer + +All requests are class requests to interface 0, unit 6 +(`bmRequestType 0x21/0xA1`, `wIndex 0x0600`): + +| Method | Request | +|---|---| +| `UVCWriteUSBReg:withValue:` | SET_CUR sel 2 `[0x00, reg, reg>>8, val, 0]` | +| `UVCReadUSBReg:withValue:` | SET_CUR sel 2 `[0x80, reg, …]`, then GET_CUR sel 2; value = byte 3 | +| `UVCWriteSensorReg:withValue:` | SET_CUR sel 2 `[0x01, reg, reg>>8, val, 0]` | +| `UVCReadSensorReg:withValue:` | SET_CUR sel 2 `[0x81, reg, …]`, then GET_CUR; value = byte 3 | +| `UVCWriteBuf:` / `UVCReadBuf:` | SET_CUR / GET_CUR sel 3, variable length | +| `UVCGetExtensionInfo:` | GET_CUR sel 1 | + +`OVIF_SCCBRead/Write(id, reg, …)`: **id `0x88` is a pseudo-ID meaning "bridge +registers"**. Any other id is a real SCCB slave: the library first writes +bridge register `0xF1 = id`, then uses the sensor-register commands. +`OVIF_GetScanBtnStatus` reads bridge register `0xBC`, bit 0. + +### Mouse-controller registers (bridge registers, the app's "SCCB 0x88") + +| Reg | Use in the app | +|---|---| +| `D8` | Command/status: `00`→`FF` disable mouse (standby); `E1`→`99` start scan streaming; `E0`→`11` enable mouse; `ED`/`EC` security check (`performSecurityCheck`, challenge/response via `A1`–`A5`) | +| `2D` | Written `0x60` before starting a scan (default `0x20`) | +| `E7` | Written `0x28` before starting a scan (default already `0x28`) | +| `E6` | Exposure / LED on-time: `floor(ms × 49.23 + 0.5)` (`OVMouseManager::setExposureTime`) | +| `DC` | Laser readout position (`setLaserReadPosition`; refused while streaming) | +| `BC` | Bit 0 = scan button | +| `BD`/`BE`/`B8` | Proxy to the OV image sensor (SCCB 0x42): `BD` = register, `BE` = value, `B8` = 1 write / 2 read, with a status poll before | + +Image-sensor registers used (via the proxy): `0x00` gain, `0x01`–`0x03` colour +gains, `0x16`/`0x19` window position (`setYBaseOffset`). These follow the +standard OV76xx/77xx register map. + +### Embedded sensor record (per frame) + +`OVJpegSrcPreProc` and `DaReadMouseData` in UVCControl. In scan mode the +firmware hides a 20-byte record in each JPEG's headers: + +- SOF0 height/width bytes (4 bytes) carry: frame counter, plus 3 "mode" bytes. + The app restores them to height 296, width 640. +- Entries 49–63 (offset `0x36..0x44` from the `FF DB` marker) of the + **second** DQT carry 15 bytes. The app restores them to `0x19`. +- Each payload byte is decoded with `OVSPCal` using a 2-bit mode from the + SOF bytes and one bit from the first three DQT bytes. Implemented in + `myscan/frame.py`. + +Record layout after decoding (field names from app strings +`laser1Dx/laser1Dy/laser1Sq/laser2…`): + +| Bytes | Content | +|---|---| +| 0 | frame counter (increments by 1 per frame; verified) | +| 2–3, 4–5 | big-endian 16-bit: laser 1 dX, dY (presumably) | +| 8–9, 10–11 | big-endian 16-bit: laser 2 dX, dY (presumably) | +| 14, 15 | laser 1 / laser 2 surface quality (≈155–164 on fabric; verified to vary) | +| 16 | unknown | +| 17 | 5 flag bits (buttons / state) | +| 19 | set by the app when a frame was dropped (counter gap) | + +In mouse mode the headers are plain (480×640, standard tables), so the record +decodes to constants. + +--- + +# Original analysis of the supplied installer + +## Device background + +- **Product:** Hama myScan, Hama article no. 00052343. It is a 4-button, 1200 dpi + laser mouse with a built-in hand scanner (scan area about 2 × 4 cm, scan + resolutions 100/200/320/400 dpi). +- **Real manufacturer:** Dacuda AG (Zürich, Switzerland). Dacuda designed the + scanner-mouse hardware and its "SLAM Scan" stitching/OCR software and licensed + it to other brands. +- **Sister products from the same Dacuda platform:** + - LG LSM-100 "Smart Scan" mouse (sold with the LG Smart Scan software, Windows only) + - probably the IRIScan Mouse (I.R.I.S.) +- Products of this kind use a small CMOS camera to capture frames of the page. + The host software stitches the frames together using the mouse's position. + +## The supplied file: `00052343_wxpvista78_v2_3_2.exe` + +| Property | Value | +|---|---| +| Size | 2,046,568 bytes | +| Format | PE32, i386, GUI. A plain executable, not an installer archive. | +| Framework | wxWidgets, MSVC 9 (VC90 CRT) | +| Signed by | Dacuda (Comodo code-signing certificate, contact `lionel.roll@dacuda.com`) | +| PDB path | `D:\JENKINS\jobs\OnlineInstallerClient-Master\workspace\ScanMouse\bin\Release\HAM_Scanner_Mouse_Online_Installer.pdb` | +| Window title | "Scanner Mouse Driver Installer" | + +### What it does + +1. **Lists USB devices.** It opens the host controllers directly (`\\.\HCD%d`) + and walks the hub tree with `DeviceIoControl`, collecting each device's + descriptor (`wVendorID`, `wProductID`, `bDeviceClass`, …). With `-v` + (verbose) it writes the list to `%TEMP%/connectedDevices.txt`. Other + command-line flags are `-d` (developer) and `-h` (help). +2. **Asks the update server about every device.** The function at VA + `0x404c90` loops over **all** connected devices and appends each one to the + query (the `VID=` reference is at `0x404e9d`, `&PID=` at `0x404eba`): + + ``` + http://updater.dacuda.com/onlineinstaller_list?VID=&PID=&VID=... + ``` + + The path string `/onlineinstaller_list` is referenced at `0x403baa`. + **There is no local VID/PID filter.** The server decided which driver + matched the hardware. +3. **Downloads the real driver.** It saves the package as `\online.exe` in a + temp directory and runs it. The status text reads "Downloading Scanner + Mouse Driver (incl. 198 recognition languages)...". + +If no matching device is found, it shows "Please connect the Scanner Mouse +and run the installer again." + +### Conclusion + +The executable contains **no scanning protocol, no device IDs and no image +handling**. There is nothing in it to build a Linux driver from. + +## Status of download sources + +| Source | Result (2026-10-02) | +|---|---| +| `http://updater.dacuda.com/onlineinstaller_list?...` | HTTP 404. The hostname now points at Google hosting (`ghs.googlehosted.com`); the service is gone. | +| `http://www.hama.de/scannermaus` (the download URL given in Hama's docs) | 404 | +| Hama product pages for 00052343 | 404 | +| Wayback Machine (CDX query for `updater.dacuda.com*`) | Archive was "Temporarily Offline". **Retry this.** | +| Web search for driver mirrors or existing Linux work | Nothing found. The LG LSM-100 was only ever supported on Windows. | + +## Next steps (original plan; A.1–A.3 are now done, see the top of this file) + +### A. Probe the hardware directly (needs the mouse connected) + +1. Dump the USB descriptors (install `usbutils` first, then run `lsusb -v -d :`). + Look for interfaces besides the standard HID mouse: a vendor-specific class + (0xFF) with bulk/interrupt endpoints, a video-class interface (UVC), or a + second HID interface with large reports. +2. Check whether the scan button changes what the device reports or how it + enumerates. +3. Use pyusb/libusb to read from the extra endpoints and try to capture raw + sensor frames. Then work out the frame format (size, bit depth) by + rendering the frames as images. +4. If frames come through, write a Linux tool that grabs frames together with + the mouse position, stitches them, and optionally runs OCR with Tesseract. + +### B. Find the real Windows driver (fastest route if it turns up) + +- Retry the Wayback Machine for `updater.dacuda.com/*` and the Hama download pages. +- Look for the LG Smart Scan or IRIScan Mouse software. Being on the same + Dacuda platform, they probably share the USB protocol. +- Any old CD, any copy of `online.exe`, or an installed "Scanner Mouse" + program folder from a Windows machine would do. The protocol could then be + read out of the real driver. +- With a working Windows setup (a VM with USB passthrough is enough), capturing + traffic with Wireshark + USBPcap during a scan would show the protocol + directly. + +## References + +- Hama myScan manual: https://www.manualslib.de/manual/56025/Hama-116600.html +- Hama myScan review (PL): https://www.videotesty.pl/recenzja/1442/hama-myscan-myszi-skaner-w-jednym/ +- Dacuda background: https://www.venturekick.ch/Worldwide-renowned-scanner-mice +- LG LSM-100 review: https://www.computing.co.uk/review/2122872/review-lg-lsm-100-scanner-mouse +- LG Scanner Mouse review: https://www.itpro.com/637726/lg-scanner-mouse-review + +## Laser calibration & stitching (2026-10-02, recording scan_text/) + +- Record v0/v1 = laser 1 cumulative (a, b) counts, v3/v4 = laser 2; signed 16-bit, wrapping. ~3 counts per frame pixel. +- Frame content shift: dx = -0.022*a - 0.355*b, dy = 0.324*a + 0.002*b (a, b = mean of both lasers). +- Rotation: dtheta ≈ -4.4e-4 rad * (b1 - b2) (fit against ORB rotation over 10-30 frame windows; corr ~0.88). +- At fast motion (~25 px/frame) the lasers disagree (one over-counts by ~40%), so stitch.py checks each step against + frame-to-frame phase correlation, then ECC-refines every frame against the canvas. +- Frames are transposed + mirrored relative to the page; output = flip(transpose(canvas), -1). +- `tools/stitch.py ` -> /page.png. Full 751-frame session stitches cleanly. diff --git a/README.md b/README.md new file mode 100644 index 0000000..6a45303 --- /dev/null +++ b/README.md @@ -0,0 +1,16 @@ +# myscan + +Linux driver tools for the Hama myScan (00052343) scanner mouse, a Dacuda design +(USB 274f:c001: UVC MJPEG camera + HID mouse). + +- `myscan/xu.py` — UVC extension-unit access: switch between mouse and scan mode, LEDs, exposure +- `myscan/frame.py` — fix the lying JPEG header and decode the per-frame sensor record (laser dx/dy) +- `myscan/stitch.py` — assemble frames into a page image (laser dead reckoning + image registration) +- `tools/record.py` — record a scan session: `python tools/record.py -d 25 -o scan_text` +- `tools/stitch.py` — stitch a recording: `python tools/stitch.py scan_text` -> `scan_text/page.png` +- `tools/probe.py` — log raw frames and HID reports side by side +- `tools/scantest.py` — minimal scan-mode test: grab frames, decode records, restore mouse mode + +See [FINDINGS.md](FINDINGS.md) for the protocol and calibration notes. + +Setup: `python -m venv .venv && .venv/bin/pip install -r requirements.txt` diff --git a/myscan/frame.py b/myscan/frame.py new file mode 100644 index 0000000..c25b671 --- /dev/null +++ b/myscan/frame.py @@ -0,0 +1,80 @@ +"""Decode Hama myScan / Dacuda scanner-mouse MJPEG frames. + +The firmware hides a per-frame sensor record in the JPEG headers (reverse +engineered from OVJpegSrcPreProc / DaReadMouseData in Omnivision's +UVCControl.framework shipped with the Dacuda Scanner Mouse Mac app): + +* SOF0 height/width bytes carry 4 data bytes; the true size is 640x296. +* Entries 0x36..0x44 (relative to the FF marker) of the second DQT carry + 15 more data bytes; the true table value there is 0x19. +""" +import struct +from dataclasses import dataclass + +WIDTH, HEIGHT = 640, 296 + + +def _spcal(v, mode, bit): + # OVSPCal: undo the encoding that keeps quant-table bytes legal. + if mode == 2: + r = max(v - 1, 0) * 4 + 2 + elif mode == 3: + r = max(v - 1, 0) * 4 + 3 + elif mode == 1: + r = v * 4 + 1 + else: + r = v * 4 + return ((r & 0xFB) | ((bit & 1) << 2)) & 0xFF + + +@dataclass +class SensorRecord: + counter: int + v: tuple # six big-endian 16-bit values; [2] and [5] are not transmitted + b14: int + b15: int + b16: int + flags: int # five flag bits (byte 17) + raw: bytes + + +def fix_and_extract(jpeg: bytes): + """Return (clean_jpeg, SensorRecord | None).""" + buf = bytearray(jpeg) + s = bytearray(32) + have = 0 + dqt = 0 + i = 0 + n = min(len(buf), 40000) + while i < n - 1: + if buf[i] == 0xFF: + m = buf[i + 1] + if m == 0xD8: + have |= 1 + elif m == 0xC0: + have |= 2 + s[0], s[0x14], s[0x15], s[0x16] = buf[i + 5:i + 9] + buf[i + 5:i + 9] = struct.pack('>HH', HEIGHT, WIDTH) + elif m == 0xDB: + dqt += 1 + if dqt == 2: + have |= 4 + d = buf[i + 0x36:i + 0x45] + for dst, src in zip((0x18, 0x19, 0x1A, 2, 3, 4, 5, 8, 9, 10, 11, 14, 15, 16, 17), d): + s[dst] = src + buf[i + 0x36:i + 0x45] = b'\x19' * 15 + if have == 7: + break + i += 1 + if have != 7: + return bytes(buf), None + + # Each data byte gets a 2-bit mode from bytes 0x14..0x16 and one bit from 0x18..0x1a. + plan = [(2, 0x14, 6, 0x18, 5), (3, 0x14, 4, 0x18, 4), (4, 0x14, 2, 0x18, 3), (5, 0x14, 0, 0x18, 2), + (8, 0x15, 6, 0x18, 1), (9, 0x15, 4, 0x19, 5), (10, 0x15, 2, 0x19, 4), (11, 0x15, 0, 0x19, 3), + (14, 0x16, 6, 0x19, 2), (15, 0x16, 4, 0x19, 1), (16, 0x16, 2, 0x1A, 5), (17, 0x16, 0, 0x1A, 4)] + for dst, mreg, msh, breg, bsh in plan: + s[dst] = _spcal(s[dst], (s[mreg] >> msh) & 3, s[breg] >> bsh) + vals = tuple((s[2 * k + 2] << 8) | s[2 * k + 3] for k in range(6)) + rec = SensorRecord(s[0], vals, s[14], s[15], s[16], s[17] & 0x1F, bytes(s[:20])) + return bytes(buf), rec diff --git a/myscan/stitch.py b/myscan/stitch.py new file mode 100644 index 0000000..0723aed --- /dev/null +++ b/myscan/stitch.py @@ -0,0 +1,145 @@ +"""Assemble scan frames into a page image. + +Poses come from dead reckoning on the two laser sensors embedded in each +frame's record (see frame.py), then each frame is refined against the canvas +built so far with ECC (Euclidean model) before it is blended in. + +Laser calibration (from scan_text, 2026-10-02; fit against image registration): + v0/v1 = laser 1 cumulative (a, b) counts, v3/v4 = laser 2; 16-bit wrapping. + content shift between consecutive frames, in frame pixels: + dx = -0.022*a - 0.355*b, dy = 0.324*a + 0.002*b (a, b = mean of both lasers) + image rotation (rad) = ROT_GAIN * (db1 - db2) +""" +import numpy as np +import cv2 + +SHIFT = np.array([[-0.0222, -0.3551], # dx from (a, b) + [0.3239, 0.0020]]) # dy from (a, b) +ROT_GAIN = -4.4e-4 + + +def _s16(v): + v = np.asarray(v, np.int64) & 0xFFFF + return np.where(v >= 0x8000, v - 0x10000, v) + + +def laser_deltas(v): + """v: (N, 6) record values -> (N, 4) per-frame deltas (a1, b1, a2, b2); first row zero.""" + p = _s16(np.asarray(v)[:, [0, 1, 3, 4]]) + d = np.diff(p, axis=0, prepend=p[:1]) + return (d + 0x8000) % 0x10000 - 0x8000 + + +def _aff(theta, tx, ty): + c, s = np.cos(theta), np.sin(theta) + return np.array([[c, -s, tx], [s, c, ty], [0, 0, 1.0]]) + + +def laser_shift(d): + """Content shift (dx, dy) in frame pixels predicted from one frame's laser deltas.""" + return SHIFT @ ((d[0] + d[2]) / 2, (d[1] + d[3]) / 2) + + +def relative_motion(d, center, rot_gain=ROT_GAIN, shift=None): + """3x3 transform mapping current-frame pixels to previous-frame pixels. + shift overrides the laser-predicted content shift.""" + dx, dy = laser_shift(d) if shift is None else shift + th = rot_gain * (d[1] - d[3]) + cx, cy = center + # content moved by (dx, dy) and rotated by th about the center: p_prev = R(-th)(p - c - d) + c + return _aff(0, cx, cy) @ _aff(-th, 0, 0) @ _aff(0, -cx - dx, -cy - dy) + + +def _feather(h, w): + y = np.minimum(np.arange(h), np.arange(h)[::-1]) + 1 + x = np.minimum(np.arange(w), np.arange(w)[::-1]) + 1 + return np.minimum.outer(y, x).astype(np.float32).clip(max=40) / 40 + + +class Stitcher: + def __init__(self, frame_shape, margin=600, refine=True, rot_gain=ROT_GAIN): + self.h, self.w = frame_shape + self.refine, self.rot_gain = refine, rot_gain + self.size = (self.w + 2 * margin, self.h + 2 * margin) # grows as needed + self.acc = np.zeros(self.size[::-1], np.float32) + self.wsum = np.zeros_like(self.acc) + self.pose = _aff(0, margin, margin) # frame -> canvas + self.feather = _feather(self.h, self.w) + self.window = cv2.createHanningWindow((self.w, self.h), cv2.CV_32F) + self.prev = None + self.n = 0 + self.stats = {"refined": 0, "rejected": 0, "skipped": 0, "image_shift": 0} + + def _grow(self, M): + corners = np.array([[0, 0, 1], [self.w, 0, 1], [0, self.h, 1], [self.w, self.h, 1.0]]).T + q = (M @ corners)[:2] + pad = 400 + left = int(max(0, pad - q[0].min())) if q[0].min() < 50 else 0 + top = int(max(0, pad - q[1].min())) if q[1].min() < 50 else 0 + right = int(max(0, q[0].max() + pad - self.size[0])) if q[0].max() > self.size[0] - 50 else 0 + bottom = int(max(0, q[1].max() + pad - self.size[1])) if q[1].max() > self.size[1] - 50 else 0 + if left or top or right or bottom: + self.acc = cv2.copyMakeBorder(self.acc, top, bottom, left, right, cv2.BORDER_CONSTANT, 0) + self.wsum = cv2.copyMakeBorder(self.wsum, top, bottom, left, right, cv2.BORDER_CONSTANT, 0) + self.size = self.acc.shape[::-1] + shift = _aff(0, left, top) + self.pose = shift @ self.pose + return shift @ M + return M + + def _refine(self, frame, M): + # Canvas content seen through the predicted pose, in frame coordinates. + Mi = np.linalg.inv(M)[:2] + acc = cv2.warpAffine(self.acc, Mi, (self.w, self.h), flags=cv2.INTER_LINEAR) + ws = cv2.warpAffine(self.wsum, Mi, (self.w, self.h), flags=cv2.INTER_LINEAR) + mask = ws > 0.3 + if mask.mean() < 0.3: + self.stats["skipped"] += 1 + return M + patch = np.where(mask, acc / np.maximum(ws, 1e-6), 0).astype(np.float32) + W = np.eye(2, 3, dtype=np.float32) + try: + _, W = cv2.findTransformECC(patch, frame.astype(np.float32), W, cv2.MOTION_EUCLIDEAN, + (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 60, 1e-4), + mask.astype(np.uint8), 5) + except cv2.error: + self.stats["rejected"] += 1 + return M + # patch(x) ~ frame(W x), patch pixel x sits at canvas M x, so frame pixel y sits at M W^-1 y. + W3 = np.vstack([W, [0, 0, 1]]).astype(np.float64) + if abs(np.arctan2(W3[1, 0], W3[0, 0])) > np.radians(3) or np.hypot(W3[0, 2], W3[1, 2]) > 25: + self.stats["rejected"] += 1 + return M + self.stats["refined"] += 1 + return M @ np.linalg.inv(W3) + + def add(self, frame, deltas): + frame = frame.astype(np.float32) + if self.n: + # At speed the lasers can disagree with each other (one over-counts), so check + # the translation against frame-to-frame phase correlation. + shift = None + (dx, dy), resp = cv2.phaseCorrelate(self.prev, frame, self.window) + if resp > 0.2 and np.hypot(*(laser_shift(deltas) - (dx, dy))) > 2: + shift = (dx, dy) + self.stats["image_shift"] += 1 + M = self.pose @ relative_motion(deltas, (self.w / 2, self.h / 2), self.rot_gain, shift) + M = self._grow(M) + if self.refine: + M = self._refine(frame, M) + else: + M = self.pose + self.pose = M + self.prev = frame + self.acc += cv2.warpAffine(frame * self.feather, M[:2], self.size, flags=cv2.INTER_LINEAR) + self.wsum += cv2.warpAffine(self.feather, M[:2], self.size, flags=cv2.INTER_LINEAR) + self.n += 1 + + def image(self, background=255): + out = np.where(self.wsum > 0.05, self.acc / np.maximum(self.wsum, 1e-6), background) + out = out.clip(0, 255).astype(np.uint8) + ys, xs = np.nonzero(self.wsum > 0.05) + if len(ys): + out = out[ys.min():ys.max() + 1, xs.min():xs.max() + 1] + # Frames arrive transposed and mirrored relative to the page. + return cv2.flip(cv2.transpose(out), -1) diff --git a/myscan/xu.py b/myscan/xu.py new file mode 100644 index 0000000..6ba501d --- /dev/null +++ b/myscan/xu.py @@ -0,0 +1,104 @@ +"""Register access for the Hama myScan / Dacuda scanner mouse via its UVC extension unit. + +Reverse engineered from Omnivision's UVCControl.framework (Dacuda Scanner Mouse +for Mac 2.4). Extension unit 6 (GUID dd880f8a-1cba-4954-8a25-f7875967f0f7): + + selector 1 20 bytes, GET only device info block + selector 2 5 bytes, GET/SET register access: [cmd, addr_lo, addr_hi, value, 0] + cmd 0x00/0x80 = bridge ("USB") register write/read + cmd 0x01/0x81 = SCCB register write/read + reads: SET_CUR the request, then GET_CUR; value in byte 3 + selector 3 33 bytes, GET/SET buffer access (write first, then read) + +The SCCB slave used by cmd 0x01/0x81 is selected by bridge register 0xF1 +(0x42 = the OV image sensor). The app's SCCB "device 0x88" is a pseudo-ID that +maps to the bridge registers (cmd 0x00/0x80); that is where the mouse MCU's +command/status registers (0xD8, 0xBC, ...) live. The app reaches the image +sensor through the MCU's proxy registers (0xBD/0xBE/0xB8). +""" +import ctypes +import fcntl +import os +import time + +UNIT = 6 +SEL_INFO, SEL_REG, SEL_BUF = 1, 2, 3 +_SET_CUR, _GET_CUR = 0x01, 0x81 +_UVCIOC_CTRL_QUERY = 0xC0107521 # _IOWR('u', 0x21, struct uvc_xu_control_query) + +MCU = 0x88 +SENSOR = 0x42 + + +class _Query(ctypes.Structure): + _fields_ = [("unit", ctypes.c_uint8), ("selector", ctypes.c_uint8), + ("query", ctypes.c_uint8), ("size", ctypes.c_uint16), + ("data", ctypes.c_void_p)] + + +class XU: + def __init__(self, dev="/dev/video4"): + self.fd = os.open(dev, os.O_RDWR) + + def close(self): + os.close(self.fd) + + def _query(self, sel, query, data): + buf = (ctypes.c_uint8 * len(data))(*data) + q = _Query(UNIT, sel, query, len(data), ctypes.cast(buf, ctypes.c_void_p)) + fcntl.ioctl(self.fd, _UVCIOC_CTRL_QUERY, q) + return bytes(buf) + + def info(self): + return self._query(SEL_INFO, _GET_CUR, bytes(20)) + + def _reg(self, cmd, addr, value=0, read=False): + req = bytes([cmd, addr & 0xFF, (addr >> 8) & 0xFF, value & 0xFF, 0]) + self._query(SEL_REG, _SET_CUR, req) + if read: + return self._query(SEL_REG, _GET_CUR, bytes(5))[3] + + def bridge_read(self, addr): + return self._reg(0x80, addr, read=True) + + def bridge_write(self, addr, value): + self._reg(0x00, addr, value) + + def sccb_read(self, addr): + return self._reg(0x81, addr, read=True) + + def sccb_write(self, addr, value): + self._reg(0x01, addr, value) + + # --- mouse MCU (app's pseudo SCCB id 0x88 = bridge registers), as used by Hal::OVMouseManager --- + + def mcu_write_verified(self, reg, value, tries=10): + """Write + read back with retries, like the app's helper at 0x138b020.""" + for _ in range(tries): + self.bridge_write(reg, value) + if self.bridge_read(reg) == value: + return True + time.sleep(0.01) + return False + + def mcu_command(self, value, ack, tries=100): + """Write the MCU command register 0xD8 and poll until it reads `ack`.""" + self.bridge_write(0xD8, value) + got = None + for _ in range(tries): + got = self.bridge_read(0xD8) + if got == ack: + return True, got + return False, got + + def disable_mouse(self): # enter standby: needed before scanning + return self.mcu_command(0x00, 0xFF) + + def start_scan(self): # LEDs on + sensor data embedded in frames + return self.mcu_command(0xE1, 0x99) + + def enable_mouse(self): # back to normal mouse mode + return self.mcu_command(0xE0, 0x11) + + def scan_button(self): + return self.bridge_read(0xBC) & 1 diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..959d583 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +numpy +opencv-python-headless diff --git a/scan_text/page.png b/scan_text/page.png new file mode 100644 index 0000000..d68dd50 Binary files /dev/null and b/scan_text/page.png differ diff --git a/scan_text/records.csv b/scan_text/records.csv new file mode 100644 index 0000000..5d15ced --- /dev/null +++ b/scan_text/records.csv @@ -0,0 +1,752 @@ +t,frame,counter,v0,v1,v2,v3,v4,v5,b14,b15,b16,flags,e6,brightness,raw +0.0798,0,67,3,0,0,0,3,0,132,141,0,0,40,195.2,4300000300000000000000030000848d00000000 +0.1157,1,68,3,0,0,0,3,0,131,142,0,0,33,195.0,4400000300000000000000030000838e00000000 +0.1478,2,69,3,0,0,0,3,0,143,141,0,0,33,179.3,45000003000000000000000300008f8d00000000 +0.1797,3,70,3,0,0,0,3,0,134,146,0,0,33,179.9,4600000300000000000000030000869200000000 +0.2157,4,71,3,0,0,0,3,0,144,144,0,0,29,169.6,4700000300000000000000030000909000000000 +0.2477,5,72,3,0,0,0,3,0,149,142,0,0,29,169.6,4800000300000000000000030000958e00000000 +0.2798,6,73,3,0,0,0,3,0,140,135,0,0,29,170.0,49000003000000000000000300008c8700000000 +0.3157,7,74,3,0,0,0,3,0,129,140,0,0,27,164.8,4a00000300000000000000030000818c00000000 +0.3477,8,75,3,0,0,0,3,0,141,145,0,0,27,165.4,4b000003000000000000000300008d9100000000 +0.3798,9,76,3,0,0,0,3,0,135,144,0,0,27,165.6,4c00000300000000000000030000879000000000 +0.4157,10,77,3,0,0,0,3,0,137,138,0,0,25,159.4,4d00000300000000000000030000898a00000000 +0.4477,11,78,3,0,0,0,3,0,134,147,0,0,25,159.5,4e00000300000000000000030000869300000000 +0.4797,12,79,3,0,0,0,3,0,126,141,0,0,25,159.7,4f000003000000000000000300007e8d00000000 +0.5157,13,80,3,0,0,0,3,0,140,125,0,0,24,156.4,50000003000000000000000300008c7d00000000 +0.5478,14,81,3,0,0,0,3,0,135,149,0,0,24,156.4,5100000300000000000000030000879500000000 +0.5797,15,82,3,0,0,0,3,0,134,144,0,0,24,156.4,5200000300000000000000030000869000000000 +0.6118,16,83,3,0,0,0,3,0,133,148,0,0,23,153.0,5300000300000000000000030000859400000000 +0.6477,17,84,3,0,0,0,3,0,134,139,0,0,23,153.1,5400000300000000000000030000868b00000000 +0.6798,18,85,3,0,0,0,3,0,136,130,0,0,23,153.0,5500000300000000000000030000888200000000 +0.7117,19,86,3,0,0,0,3,0,129,140,0,0,23,153.0,5600000300000000000000030000818c00000000 +0.7477,20,87,3,0,0,0,3,0,124,146,0,0,23,153.0,57000003000000000000000300007c9200000000 +0.7797,21,88,3,0,0,0,3,0,133,135,0,0,23,152.9,5800000300000000000000030000858700000000 +0.8117,22,89,3,0,0,0,3,0,122,134,0,0,23,152.9,59000003000000000000000300007a8600000000 +0.8478,23,90,3,0,0,0,3,0,129,144,0,0,23,152.8,5a00000300000000000000030000819000000000 +0.8797,24,91,3,0,0,0,3,0,127,146,0,0,23,152.8,5b000003000000000000000300007f9200000000 +0.9117,25,92,3,0,0,0,3,0,121,142,0,0,23,152.7,5c00000300000000000000030000798e00000000 +0.9477,26,93,3,0,0,0,3,0,131,140,0,0,23,152.6,5d00000300000000000000030000838c00000000 +0.9797,27,94,3,0,0,0,3,0,129,137,0,0,23,152.5,5e00000300000000000000030000818900000000 +1.0117,28,95,3,0,0,0,3,0,125,135,0,0,23,152.5,5f000003000000000000000300007d8700000000 +1.0477,29,96,3,0,0,0,3,0,126,131,0,0,23,152.4,60000003000000000000000300007e8300000000 +1.0797,30,97,3,0,0,0,3,0,135,139,0,0,23,152.3,6100000300000000000000030000878b00000000 +1.1117,31,98,3,0,0,0,3,0,131,150,0,0,23,152.3,6200000300000000000000030000839600000000 +1.1477,32,99,12,0,0,21,3,0,136,141,0,0,23,152.4,6300000c00000000001500030000888d00000000 +1.1797,33,100,28,0,0,39,3,0,146,132,0,0,23,152.4,6400001c00000000002700030000928400000000 +1.2117,34,101,39,1,0,54,4,0,135,147,0,0,23,152.3,6500002700010000003600040000879300000000 +1.2437,35,102,41,1,0,56,4,0,144,131,0,0,23,152.3,6600002900010000003800040000908300000000 +1.2797,36,103,41,1,0,56,4,0,139,132,0,0,23,152.3,67000029000100000038000400008b8400000000 +1.3117,37,104,41,1,0,56,4,0,134,143,0,0,23,152.4,6800002900010000003800040000868f00000000 +1.3437,38,105,41,1,0,56,4,0,149,132,0,0,23,152.5,6900002900010000003800040000958400000000 +1.3797,39,106,41,1,0,56,4,0,139,141,0,0,23,152.7,6a000029000100000038000400008b8d00000000 +1.4117,40,107,41,1,0,56,4,0,134,139,0,0,23,152.8,6b00002900010000003800040000868b00000000 +1.4437,41,108,41,1,0,56,4,0,138,147,0,0,23,152.8,6c000029000100000038000400008a9300000000 +1.4797,42,109,41,1,0,56,4,0,140,138,0,0,23,152.9,6d000029000100000038000400008c8a00000000 +1.5117,43,110,41,1,0,56,4,0,137,146,0,0,23,152.8,6e00002900010000003800040000899200000000 +1.5437,44,111,41,1,0,56,4,0,134,143,0,0,23,152.7,6f00002900010000003800040000868f00000000 +1.5797,45,112,41,1,0,56,4,0,130,141,0,0,23,152.7,7000002900010000003800040000828d00000000 +1.6117,46,113,41,1,0,56,4,0,135,141,0,0,23,152.7,7100002900010000003800040000878d00000000 +1.6437,47,114,41,1,0,56,4,0,143,139,0,0,23,152.7,72000029000100000038000400008f8b00000000 +1.6797,48,115,41,1,0,56,4,0,139,141,0,0,23,152.7,73000029000100000038000400008b8d00000000 +1.7117,49,116,41,1,0,56,4,0,141,143,0,0,23,152.7,74000029000100000038000400008d8f00000000 +1.7437,50,117,41,1,0,56,4,0,131,134,0,0,23,152.7,7500002900010000003800040000838600000000 +1.7797,51,118,41,1,0,56,4,0,134,150,0,0,23,152.5,7600002900010000003800040000869600000000 +1.8117,52,119,41,1,0,56,4,0,136,136,0,0,23,152.3,7700002900010000003800040000888800000000 +1.8437,53,120,41,1,0,56,4,0,130,147,0,0,23,152.3,7800002900010000003800040000829300000000 +1.8758,54,121,45,1,0,61,4,0,136,135,0,0,23,152.2,7900002d00010000003d00040000888700000000 +1.9117,55,122,53,2,0,75,3,0,123,129,0,0,23,152.2,7a00003500020000004b000300007b8100000000 +1.9437,56,123,68,3,0,95,3,0,137,137,0,0,23,152.1,7b00004400030000005f00030000898900000000 +1.9757,57,124,86,3,0,112,3,0,133,145,0,0,23,152.1,7c00005600030000007000030000859100000000 +2.0117,58,125,98,3,0,126,4,0,136,135,0,0,23,152.1,7d00006200030000007e00040000888700000000 +2.0437,59,126,111,4,0,144,4,0,142,146,0,0,23,152.1,7e00006f000400000090000400008e9200000000 +2.0757,60,127,124,5,0,160,5,0,137,134,0,0,23,152.0,7f00007c0005000000a000050000898600000000 +2.1117,61,128,137,6,0,177,6,0,141,127,0,0,23,152.0,800000890006000000b1000600008d7f00000000 +2.1437,62,129,146,7,0,194,6,0,137,136,0,0,23,151.9,810000920007000000c200060000898800000000 +2.1759,63,130,159,8,0,204,7,0,121,145,0,0,23,151.7,8200009f0008000000cc00070000799100000000 +2.2118,64,131,160,8,0,207,6,0,128,125,0,0,23,151.9,830000a00008000000cf00060000807d00000000 +2.2437,65,132,160,8,0,207,6,0,131,135,0,0,23,151.8,840000a00008000000cf00060000838700000000 +2.2757,66,133,160,8,0,207,6,0,129,135,0,0,23,151.8,850000a00008000000cf00060000818700000000 +2.3119,67,134,160,8,0,207,6,0,130,136,0,0,23,151.9,860000a00008000000cf00060000828800000000 +2.3438,68,135,160,8,0,207,6,0,129,133,0,0,23,151.9,870000a00008000000cf00060000818500000000 +2.3758,69,136,160,8,0,207,6,0,127,137,0,0,23,152.0,880000a00008000000cf000600007f8900000000 +2.4117,70,137,160,8,0,207,6,0,126,134,0,0,23,151.9,890000a00008000000cf000600007e8600000000 +2.4437,71,138,160,8,0,207,6,0,132,141,0,0,23,152.1,8a0000a00008000000cf00060000848d00000000 +2.4757,72,139,160,8,0,207,6,0,131,130,0,0,23,152.0,8b0000a00008000000cf00060000838200000000 +2.5077,73,140,160,8,0,207,6,0,128,138,0,0,23,152.0,8c0000a00008000000cf00060000808a00000000 +2.5438,74,141,160,8,0,207,6,0,136,138,0,0,23,152.1,8d0000a00008000000cf00060000888a00000000 +2.5757,75,142,160,8,0,207,6,0,136,137,0,0,23,152.1,8e0000a00008000000cf00060000888900000000 +2.6078,76,143,160,8,0,207,6,0,128,143,0,0,23,152.1,8f0000a00008000000cf00060000808f00000000 +2.6438,77,144,160,8,0,207,6,0,133,133,0,0,23,152.1,900000a00008000000cf00060000858500000000 +2.6757,78,145,160,8,0,207,6,0,129,131,0,0,23,152.2,910000a00008000000cf00060000818300000000 +2.7078,79,146,160,8,0,207,6,0,130,129,0,0,23,152.1,920000a00008000000cf00060000828100000000 +2.7438,80,147,160,8,0,207,6,0,130,137,0,0,23,152.2,930000a00008000000cf00060000828900000000 +2.7757,81,148,160,8,0,207,6,0,129,133,0,0,23,152.2,940000a00008000000cf00060000818500000000 +2.8078,82,149,160,8,0,207,6,0,129,143,0,0,23,152.2,950000a00008000000cf00060000818f00000000 +2.8438,83,150,160,8,0,207,6,0,131,132,0,0,23,152.1,960000a00008000000cf00060000838400000000 +2.8759,84,151,160,8,0,207,6,0,126,131,0,0,23,152.0,970000a00008000000cf000600007e8300000000 +2.9078,85,152,160,8,0,207,6,0,131,136,0,0,23,152.0,980000a00008000000cf00060000838800000000 +2.9437,86,153,160,8,0,207,6,0,130,141,0,0,23,151.9,990000a00008000000cf00060000828d00000000 +2.9757,87,154,160,8,0,207,6,0,137,133,0,0,23,151.9,9a0000a00008000000cf00060000898500000000 +3.0078,88,155,160,8,0,207,6,0,136,127,0,0,23,151.9,9b0000a00008000000cf00060000887f00000000 +3.0437,89,156,160,8,0,207,6,0,141,135,0,0,23,152.0,9c0000a00008000000cf000600008d8700000000 +3.0758,90,157,160,8,0,207,6,0,133,133,0,0,23,152.0,9d0000a00008000000cf00060000858500000000 +3.1077,91,158,160,8,0,207,6,0,135,134,0,0,23,151.9,9e0000a00008000000cf00060000878600000000 +3.1398,92,159,160,8,0,207,6,0,134,144,0,0,23,151.9,9f0000a00008000000cf00060000869000000000 +3.1758,93,160,160,8,0,207,6,0,139,143,0,0,23,151.9,a00000a00008000000cf000600008b8f00000000 +3.2078,94,161,160,8,0,207,6,0,131,142,0,0,23,152.0,a10000a00008000000cf00060000838e00000000 +3.2398,95,162,160,8,0,207,6,0,130,143,0,0,23,152.0,a20000a00008000000cf00060000828f00000000 +3.2757,96,163,160,8,0,207,6,0,131,128,0,0,23,152.0,a30000a00008000000cf00060000838000000000 +3.3078,97,164,160,8,0,207,6,0,137,138,0,0,23,152.0,a40000a00008000000cf00060000898a00000000 +3.3398,98,165,160,8,0,207,6,0,130,123,0,0,23,151.9,a50000a00008000000cf00060000827b00000000 +3.3757,99,166,160,8,0,207,6,0,128,130,0,0,23,151.8,a60000a00008000000cf00060000808200000000 +3.4078,100,167,160,8,0,207,6,0,131,127,0,0,23,151.8,a70000a00008000000cf00060000837f00000000 +3.4397,101,168,160,8,0,207,6,0,126,144,0,0,23,151.8,a80000a00008000000cf000600007e9000000000 +3.4758,102,169,160,8,0,208,7,0,129,126,0,0,23,151.3,a90000a00008000000d000070000817e00000000 +3.5079,103,170,175,12,0,243,11,0,116,141,0,0,23,150.3,aa0000af000c000000f3000b0000748d00000000 +3.5398,104,171,206,14,0,274,17,0,132,143,0,0,23,149.6,ab0000ce000e0000011200110000848f00000000 +3.576,105,172,227,15,0,297,19,0,125,141,0,0,23,148.9,ac0000e3000f00000129001300007d8d00000000 +3.6078,106,173,243,18,0,313,21,0,140,128,0,0,23,148.7,ad0000f3001200000139001500008c8000000000 +3.6398,107,174,252,20,0,322,21,0,133,142,0,0,23,148.5,ae0000fc00140000014200150000858e00000000 +3.6759,108,175,261,21,0,334,22,0,132,123,0,0,23,148.3,af00010500150000014e00160000847b00000000 +3.7077,109,176,265,21,0,343,22,0,118,135,0,0,23,148.1,b000010900150000015700160000768700000000 +3.7398,110,177,270,22,0,354,22,0,121,145,0,0,23,147.8,b100010e00160000016200160000799100000000 +3.7718,111,178,280,22,0,371,22,0,125,138,0,0,23,147.4,b2000118001600000173001600007d8a00000000 +3.8077,112,179,293,23,0,395,22,0,139,128,0,0,23,147.1,b300012500170000018b001600008b8000000000 +3.8398,113,180,312,23,0,416,21,0,132,154,0,0,23,146.4,b40001380017000001a000150000849a00000000 +3.8717,114,181,339,22,0,440,19,0,126,142,0,0,23,145.9,b50001530016000001b8001300007e8e00000000 +3.9079,115,182,361,22,0,463,19,0,131,137,0,0,23,145.3,b60001690016000001cf00130000838900000000 +3.9398,116,183,383,21,0,484,17,0,137,127,0,0,23,144.7,b700017f0015000001e400110000897f00000000 +3.9717,117,184,406,20,0,510,16,0,125,137,0,0,23,144.2,b80001960014000001fe001000007d8900000000 +4.0078,118,185,438,20,0,537,16,0,124,142,0,0,24,147.0,b90001b6001400000219001000007c8e00000000 +4.0398,119,186,465,20,0,575,16,0,137,134,0,0,24,146.2,ba0001d100140000023f00100000898600000000 +4.0718,120,187,495,20,0,600,17,0,134,141,0,0,24,145.7,bb0001ef00140000025800110000868d00000000 +4.1079,121,188,523,21,0,621,15,0,133,130,0,0,24,145.1,bc00020b00150000026d000f0000858200000000 +4.1398,122,189,550,21,0,636,15,0,135,128,0,0,24,144.4,bd00022600150000027c000f0000878000000000 +4.1718,123,190,579,21,0,661,16,0,134,133,0,0,24,143.9,be00024300150000029500100000868500000000 +4.2077,124,191,603,21,0,680,17,0,138,144,0,0,25,146.4,bf00025b0015000002a8001100008a9000000000 +4.2397,125,192,621,21,0,698,17,0,125,136,0,0,25,146.0,c000026d0015000002ba001100007d8800000000 +4.2718,126,193,633,21,0,709,22,0,131,137,0,0,25,145.7,c10002790015000002c500160000838900000000 +4.3077,127,194,651,21,0,723,24,0,126,135,0,0,26,148.3,c200028b0015000002d3001800007e8700000000 +4.3397,128,195,665,21,0,733,24,0,132,133,0,0,26,148.1,c30002990015000002dd00180000848500000000 +4.3718,129,196,679,22,0,749,24,0,138,136,0,0,26,148.0,c40002a70016000002ed001800008a8800000000 +4.4038,130,197,696,22,0,768,23,0,140,133,0,0,26,147.6,c50002b8001600000300001700008c8500000000 +4.4398,131,198,717,22,0,785,24,0,130,129,0,0,26,147.5,c60002cd00160000031100180000828100000000 +4.4718,132,199,735,22,0,805,24,0,136,136,0,0,26,147.1,c70002df00160000032500180000888800000000 +4.5037,133,200,756,22,0,823,24,0,121,132,0,0,26,146.9,c80002f400160000033700180000798400000000 +4.5397,134,201,777,23,0,840,25,0,128,128,0,0,26,146.8,c900030900170000034800190000808000000000 +4.5718,135,202,795,22,0,861,24,0,132,139,0,0,26,146.4,ca00031b00160000035d00180000848b00000000 +4.6038,136,203,815,22,0,880,22,0,137,133,0,0,26,146.2,cb00032f00160000037000160000898500000000 +4.6398,137,204,833,22,0,898,21,0,143,131,0,0,26,146.1,cc000341001600000382001500008f8300000000 +4.6717,138,205,852,22,0,915,20,0,147,132,0,0,26,145.9,cd00035400160000039300140000938400000000 +4.7038,139,206,868,22,0,932,20,0,113,132,0,0,27,148.5,ce0003640016000003a400140000718400000000 +4.7397,140,207,884,22,0,949,20,0,133,139,0,0,27,148.5,cf0003740016000003b500140000858b00000000 +4.7717,141,208,899,22,0,965,20,0,143,140,0,0,27,148.1,d00003830016000003c5001400008f8c00000000 +4.8037,142,209,914,21,0,980,20,0,133,139,0,0,27,147.9,d10003920015000003d400140000858b00000000 +4.8398,143,210,925,21,0,991,20,0,115,152,0,0,27,147.7,d200039d0015000003df00140000739800000000 +4.8718,144,211,941,20,0,1004,19,0,139,133,0,0,27,147.5,d30003ad0014000003ec001300008b8500000000 +4.9039,145,212,954,20,0,1022,18,0,139,129,0,0,27,147.1,d40003ba0014000003fe001200008b8100000000 +4.9397,146,213,969,20,0,1034,18,0,128,139,0,0,27,146.9,d50003c900140000040a00120000808b00000000 +4.9718,147,214,984,21,0,1047,20,0,136,137,0,0,27,146.7,d60003d800150000041700140000888900000000 +5.0038,148,215,997,23,0,1056,21,0,125,136,0,0,27,146.3,d70003e5001700000420001500007d8800000000 +5.0359,149,216,1011,24,0,1068,19,0,134,137,0,0,27,146.1,d80003f300180000042c00130000868900000000 +5.0718,150,217,1028,26,0,1083,21,0,128,136,0,0,27,146.3,d9000404001a0000043b00150000808800000000 +5.1038,151,218,1045,26,0,1101,20,0,135,142,0,0,27,146.4,da000415001a0000044d00140000878e00000000 +5.1358,152,219,1059,25,0,1115,19,0,133,130,0,0,27,146.5,db00042300190000045b00130000858200000000 +5.1718,153,220,1073,26,0,1128,19,0,144,142,0,0,27,146.6,dc000431001a0000046800130000908e00000000 +5.2039,154,221,1087,26,0,1140,19,0,132,144,0,0,27,146.6,dd00043f001a0000047400130000849000000000 +5.2358,155,222,1098,26,0,1154,20,0,128,134,0,0,27,146.4,de00044a001a0000048200140000808600000000 +5.2719,156,223,1112,27,0,1166,20,0,129,131,0,0,27,146.5,df000458001b0000048e00140000818300000000 +5.3038,157,224,1125,28,0,1183,20,0,139,140,0,0,27,146.7,e0000465001c0000049f001400008b8c00000000 +5.3359,158,225,1142,30,0,1199,21,0,128,131,0,0,27,146.6,e1000476001e000004af00150000808300000000 +5.3717,159,226,1163,30,0,1217,22,0,133,140,0,0,27,146.7,e200048b001e000004c100160000858c00000000 +5.4038,160,227,1181,29,0,1231,23,0,134,133,0,0,27,146.9,e300049d001d000004cf00170000868500000000 +5.4358,161,228,1199,29,0,1249,21,0,141,137,0,0,27,146.9,e40004af001d000004e1001500008d8900000000 +5.4718,162,229,1218,30,0,1269,21,0,136,137,0,0,27,147.2,e50004c2001e000004f500150000888900000000 +5.5038,163,230,1238,30,0,1281,21,0,127,131,0,0,27,147.1,e60004d6001e00000501001500007f8300000000 +5.5358,164,231,1265,30,0,1309,23,0,125,152,0,0,27,147.0,e70004f1001e0000051d001700007d9800000000 +5.5718,165,232,1291,30,0,1331,24,0,125,156,0,0,27,147.3,e800050b001e00000533001800007d9c00000000 +5.6039,166,233,1318,30,0,1353,25,0,132,159,0,0,27,147.4,e9000526001e0000054900190000849f00000000 +5.6358,167,234,1337,30,0,1371,27,0,128,149,0,0,27,147.4,ea000539001e0000055b001b0000809500000000 +5.6679,168,235,1357,30,0,1386,28,0,136,150,0,0,27,147.5,eb00054d001e0000056a001c0000889600000000 +5.7038,169,236,1372,31,0,1401,29,0,134,131,0,0,27,147.9,ec00055c001f00000579001d0000868300000000 +5.7358,170,237,1384,31,0,1415,30,0,121,143,0,0,27,147.9,ed000568001f00000587001e0000798f00000000 +5.7678,171,238,1393,31,0,1426,30,0,142,147,0,0,27,147.9,ee000571001f00000592001e00008e9300000000 +5.8038,172,239,1407,32,0,1438,30,0,136,127,0,0,27,147.8,ef00057f00200000059e001e0000887f00000000 +5.8358,173,240,1422,33,0,1454,30,0,127,141,0,0,27,147.8,f000058e0021000005ae001e00007f8d00000000 +5.8678,174,241,1439,34,0,1471,31,0,140,134,0,0,27,148.0,f100059f0022000005bf001f00008c8600000000 +5.9038,175,242,1460,35,0,1488,32,0,125,146,0,0,27,148.2,f20005b40023000005d0002000007d9200000000 +5.9358,176,243,1477,35,0,1505,33,0,128,147,0,0,27,148.5,f30005c50023000005e100210000809300000000 +5.9678,177,244,1492,36,0,1516,34,0,133,153,0,0,27,148.5,f40005d40024000005ec00220000859900000000 +6.0038,178,245,1503,36,0,1525,35,0,119,156,0,0,27,148.5,f50005df0024000005f500230000779c00000000 +6.0358,179,246,1514,37,0,1537,37,0,134,155,0,0,27,148.6,f60005ea00250000060100250000869b00000000 +6.0678,180,247,1525,37,0,1547,38,0,140,145,0,0,27,148.6,f70005f500250000060b002600008c9100000000 +6.1038,181,248,1534,37,0,1555,39,0,138,142,0,0,27,148.6,f80005fe002500000613002700008a8e00000000 +6.1358,182,249,1542,38,0,1561,39,0,146,126,0,0,27,148.8,f900060600260000061900270000927e00000000 +6.1678,183,250,1554,38,0,1572,40,0,130,133,0,0,27,148.8,fa00061200260000062400280000828500000000 +6.2038,184,251,1566,38,0,1582,40,0,139,151,0,0,27,148.7,fb00061e00260000062e002800008b9700000000 +6.2358,185,252,1584,40,0,1599,41,0,133,143,0,0,27,148.7,fc00063000280000063f00290000858f00000000 +6.2678,186,253,1608,41,0,1617,44,0,140,149,0,0,27,148.7,fd000648002900000651002c00008c9500000000 +6.2998,187,254,1634,40,0,1641,45,0,148,130,0,0,27,148.8,fe000662002800000669002d0000948200000000 +6.3358,188,255,1658,41,0,1664,48,0,138,124,0,0,27,149.0,ff00067a002900000680003000008a7c00000000 +6.3678,189,0,1681,42,0,1681,48,0,147,150,0,0,27,149.3,00000691002a0000069100300000939600000000 +6.3997,190,1,1694,41,0,1692,49,0,128,143,0,0,27,149.5,0100069e00290000069c00310000808f00000000 +6.4357,191,2,1707,41,0,1704,50,0,130,148,0,0,27,149.6,020006ab0029000006a800320000829400000000 +6.4677,192,3,1723,41,0,1720,52,0,123,162,0,0,27,149.6,030006bb0029000006b8003400007ba200000000 +6.4997,193,4,1743,43,0,1740,55,0,135,153,0,0,27,149.7,040006cf002b000006cc00370000879900000000 +6.5358,194,5,1761,45,0,1755,59,0,136,154,0,0,27,149.7,050006e1002d000006db003b0000889a00000000 +6.5677,195,6,1784,48,0,1772,62,0,141,169,0,0,27,149.6,060006f80030000006ec003e00008da900000000 +6.5998,196,7,1801,49,0,1792,64,0,142,144,0,0,27,149.7,07000709003100000700004000008e9000000000 +6.6357,197,8,1819,50,0,1811,66,0,135,149,0,0,27,149.7,0800071b00320000071300420000879500000000 +6.6677,198,9,1837,51,0,1828,68,0,139,134,0,0,27,149.8,0900072d003300000724004400008b8600000000 +6.6997,199,10,1854,51,0,1844,68,0,135,148,0,0,27,149.8,0a00073e00330000073400440000879400000000 +6.7357,200,11,1867,50,0,1861,69,0,133,152,0,0,27,150.0,0b00074b00320000074500450000859800000000 +6.7677,201,12,1880,51,0,1875,70,0,126,150,0,0,27,150.1,0c000758003300000753004600007e9600000000 +6.7997,202,13,1893,51,0,1887,70,0,132,141,0,0,27,150.3,0d00076500330000075f00460000848d00000000 +6.8358,203,14,1906,51,0,1903,71,0,141,134,0,0,27,150.2,0e00077200330000076f004700008d8600000000 +6.8677,204,15,1926,51,0,1920,71,0,130,133,0,0,27,150.3,0f00078600330000078000470000828500000000 +6.8998,205,16,1945,53,0,1941,72,0,129,152,0,0,27,150.5,1000079900350000079500480000819800000000 +6.9318,206,17,1964,53,0,1956,74,0,132,171,0,0,27,150.6,110007ac0035000007a4004a000084ab00000000 +6.9677,207,18,1982,53,0,1972,74,0,130,163,0,0,27,150.8,120007be0035000007b4004a000082a300000000 +6.9998,208,19,2003,54,0,1989,77,0,131,153,0,0,27,151.1,130007d30036000007c5004d0000839900000000 +7.0318,209,20,2010,54,0,2009,77,0,141,146,0,0,27,151.1,140007da0036000007d9004d00008d9200000000 +7.0678,210,21,2030,54,0,2028,80,0,137,141,0,0,27,151.2,150007ee0036000007ec00500000898d00000000 +7.0999,211,22,2046,54,0,2049,83,0,126,152,0,0,27,151.2,160007fe003600000801005300007e9800000000 +7.1319,212,23,2070,53,0,2071,84,0,141,158,0,0,27,151.3,17000816003500000817005400008d9e00000000 +7.1678,213,24,2098,54,0,2096,87,0,128,145,0,0,27,151.1,1800083200360000083000570000809100000000 +7.1998,214,25,2123,54,0,2119,88,0,130,158,0,0,27,151.3,1900084b00360000084700580000829e00000000 +7.2318,215,26,2139,54,0,2136,88,0,122,157,0,0,27,151.0,1a00085b003600000858005800007a9d00000000 +7.2678,216,27,2158,54,0,2152,88,0,140,166,0,0,27,151.3,1b00086e003600000868005800008ca600000000 +7.2997,217,28,2180,54,0,2172,89,0,134,143,0,0,27,151.5,1c00088400360000087c00590000868f00000000 +7.3318,218,29,2198,55,0,2187,89,0,127,135,0,0,27,151.6,1d00089600370000088b005900007f8700000000 +7.3677,219,30,2214,56,0,2206,90,0,123,132,0,0,27,151.6,1e0008a600380000089e005a00007b8400000000 +7.3997,220,31,2235,57,0,2223,94,0,131,141,0,0,27,151.7,1f0008bb0039000008af005e0000838d00000000 +7.4318,221,32,2254,58,0,2238,96,0,134,157,0,0,27,151.6,200008ce003a000008be00600000869d00000000 +7.4678,222,33,2268,58,0,2251,98,0,144,164,0,0,27,151.6,210008dc003a000008cb0062000090a400000000 +7.4997,223,34,2277,58,0,2262,99,0,126,158,0,0,27,151.6,220008e5003a000008d6006300007e9e00000000 +7.5318,224,35,2287,59,0,2273,99,0,144,161,0,0,27,151.6,230008ef003b000008e10063000090a100000000 +7.5639,225,36,2300,60,0,2284,100,0,140,159,0,0,27,151.6,240008fc003c000008ec006400008c9f00000000 +7.5998,226,37,2314,59,0,2295,100,0,142,154,0,0,27,151.8,2500090a003b000008f7006400008e9a00000000 +7.6317,227,38,2324,59,0,2304,101,0,144,160,0,0,27,151.7,26000914003b000009000065000090a000000000 +7.6638,228,39,2334,60,0,2312,101,0,144,145,0,0,27,151.7,2700091e003c0000090800650000909100000000 +7.6998,229,40,2343,60,0,2323,100,0,143,141,0,0,27,151.7,28000927003c00000913006400008f8d00000000 +7.7319,230,41,2357,60,0,2334,100,0,134,156,0,0,27,151.7,29000935003c0000091e00640000869c00000000 +7.7638,231,42,2376,60,0,2349,102,0,138,167,0,0,27,151.7,2a000948003c0000092d006600008aa700000000 +7.7997,232,43,2394,60,0,2362,103,0,131,148,0,0,27,151.8,2b00095a003c0000093a00670000839400000000 +7.8317,233,44,2415,60,0,2378,104,0,131,157,0,0,27,152.1,2c00096f003c0000094a00680000839d00000000 +7.8638,234,45,2434,60,0,2394,105,0,128,138,0,0,27,152.2,2d000982003c0000095a00690000808a00000000 +7.8998,235,46,2448,60,0,2409,106,0,146,148,0,0,27,152.2,2e000990003c00000969006a0000929400000000 +7.9318,236,47,2460,61,0,2419,107,0,133,158,0,0,27,152.3,2f00099c003d00000973006b0000859e00000000 +7.9639,237,48,2468,63,0,2432,107,0,132,160,0,0,27,152.3,300009a4003f00000980006b000084a000000000 +7.9998,238,49,2483,63,0,2443,107,0,137,151,0,0,27,152.3,310009b3003f0000098b006b0000899700000000 +8.0318,239,50,2497,65,0,2458,109,0,132,156,0,0,27,152.5,320009c100410000099a006d0000849c00000000 +8.0638,240,51,2518,66,0,2476,111,0,121,169,0,0,27,152.4,330009d60042000009ac006f000079a900000000 +8.0958,241,52,2542,67,0,2500,112,0,144,144,0,0,27,152.4,340009ee0043000009c400700000909000000000 +8.1318,242,53,2565,68,0,2521,113,0,136,163,0,0,27,152.4,35000a050044000009d90071000088a300000000 +8.1638,243,54,2589,69,0,2544,113,0,139,155,0,0,27,152.2,36000a1d0045000009f0007100008b9b00000000 +8.1958,244,55,2598,68,0,2559,113,0,131,153,0,0,27,152.4,37000a260044000009ff00710000839900000000 +8.2318,245,56,2609,68,0,2569,114,0,128,155,0,0,27,152.4,38000a31004400000a0900720000809b00000000 +8.2638,246,57,2617,68,0,2577,114,0,138,145,0,0,27,152.3,39000a39004400000a11007200008a9100000000 +8.2958,247,58,2622,68,0,2580,114,0,131,149,0,0,27,152.3,3a000a3e004400000a1400720000839500000000 +8.3318,248,59,2628,69,0,2588,116,0,121,153,0,0,27,152.3,3b000a44004500000a1c00740000799900000000 +8.3638,249,60,2638,69,0,2597,117,0,145,159,0,0,27,152.5,3c000a4e004500000a2500750000919f00000000 +8.3958,250,61,2652,69,0,2607,118,0,130,158,0,0,27,152.6,3d000a5c004500000a2f00760000829e00000000 +8.4318,251,62,2669,69,0,2620,120,0,131,154,0,0,27,152.6,3e000a6d004500000a3c00780000839a00000000 +8.4637,252,63,2683,69,0,2635,122,0,132,131,0,0,27,152.6,3f000a7b004500000a4b007a0000848300000000 +8.4958,253,64,2699,69,0,2647,122,0,128,131,0,0,27,152.8,40000a8b004500000a57007a0000808300000000 +8.5318,254,65,2713,69,0,2659,123,0,143,137,0,0,27,152.8,41000a99004500000a63007b00008f8900000000 +8.5637,255,66,2724,69,0,2669,126,0,133,140,0,0,27,152.8,42000aa4004500000a6d007e0000858c00000000 +8.5958,256,67,2735,69,0,2680,126,0,126,156,0,0,27,152.9,43000aaf004500000a78007e00007e9c00000000 +8.6318,257,68,2744,70,0,2689,127,0,130,159,0,0,27,153.1,44000ab8004600000a81007f0000829f00000000 +8.6638,258,69,2756,71,0,2699,129,0,142,168,0,0,27,153.1,45000ac4004700000a8b008100008ea800000000 +8.6958,259,70,2768,73,0,2710,131,0,138,160,0,0,27,153.2,46000ad0004900000a96008300008aa000000000 +8.7278,260,71,2782,74,0,2721,133,0,138,173,0,0,27,153.1,47000ade004a00000aa1008500008aad00000000 +8.7637,261,72,2788,75,0,2727,134,0,131,170,0,0,27,152.9,48000ae4004b00000aa70086000083aa00000000 +8.7957,262,73,2796,75,0,2733,135,0,135,165,0,0,27,152.9,49000aec004b00000aad0087000087a500000000 +8.8278,263,74,2802,75,0,2739,135,0,128,164,0,0,27,152.9,4a000af2004b00000ab30087000080a400000000 +8.8638,264,75,2815,76,0,2752,138,0,135,146,0,0,27,152.8,4b000aff004c00000ac0008a0000879200000000 +8.8958,265,76,2826,76,0,2770,140,0,126,143,0,0,27,152.8,4c000b0a004c00000ad2008c00007e8f00000000 +8.9278,266,77,2840,76,0,2785,141,0,128,151,0,0,27,152.8,4d000b18004c00000ae1008d0000809700000000 +8.9638,267,78,2857,77,0,2799,143,0,135,158,0,0,27,152.8,4e000b29004d00000aef008f0000879e00000000 +8.9958,268,79,2871,78,0,2811,144,0,129,155,0,0,27,152.8,4f000b37004e00000afb00900000819b00000000 +9.0278,269,80,2885,79,0,2825,144,0,138,153,0,0,27,152.8,50000b45004f00000b09009000008a9900000000 +9.0637,270,81,2891,79,0,2838,144,0,132,151,0,0,27,152.8,51000b4b004f00000b1600900000849700000000 +9.0957,271,82,2907,79,0,2854,144,0,134,144,0,0,27,152.9,52000b5b004f00000b2600900000869000000000 +9.1277,272,83,2925,79,0,2869,146,0,134,165,0,0,27,153.0,53000b6d004f00000b350092000086a500000000 +9.1637,273,84,2941,81,0,2881,149,0,139,168,0,0,27,152.9,54000b7d005100000b41009500008ba800000000 +9.1958,274,85,2954,82,0,2892,150,0,138,153,0,0,27,153.0,55000b8a005200000b4c009600008a9900000000 +9.2278,275,86,2963,83,0,2901,151,0,136,143,0,0,27,153.0,56000b93005300000b5500970000888f00000000 +9.2637,276,87,2971,83,0,2908,151,0,135,136,0,0,27,153.1,57000b9b005300000b5c00970000878800000000 +9.2957,277,88,2977,84,0,2914,152,0,130,140,0,0,27,153.1,58000ba1005400000b6200980000828c00000000 +9.3278,278,89,2984,84,0,2921,152,0,121,137,0,0,27,153.2,59000ba8005400000b6900980000798900000000 +9.3598,279,90,2995,84,0,2931,152,0,124,148,0,0,27,153.3,5a000bb3005400000b73009800007c9400000000 +9.3958,280,91,3006,84,0,2939,153,0,127,147,0,0,27,153.5,5b000bbe005400000b7b009900007f9300000000 +9.4278,281,92,3021,85,0,2951,154,0,136,140,0,0,27,153.5,5c000bcd005500000b87009a0000888c00000000 +9.4598,282,93,3036,87,0,2966,156,0,146,133,0,0,27,153.6,5d000bdc005700000b96009c0000928500000000 +9.4957,283,94,3051,87,0,2979,157,0,132,134,0,0,27,153.7,5e000beb005700000ba3009d0000848600000000 +9.5278,284,95,3064,89,0,2990,158,0,140,138,0,0,27,153.7,5f000bf8005900000bae009e00008c8a00000000 +9.5598,285,96,3078,90,0,3000,159,0,145,142,0,0,27,153.6,60000c06005a00000bb8009f0000918e00000000 +9.5958,286,97,3090,91,0,3010,160,0,125,145,0,0,27,153.7,61000c12005b00000bc200a000007d9100000000 +9.6278,287,98,3101,91,0,3016,161,0,129,135,0,0,27,153.7,62000c1d005b00000bc800a10000818700000000 +9.6598,288,99,3114,91,0,3025,162,0,141,146,0,0,27,153.9,63000c2a005b00000bd100a200008d9200000000 +9.6958,289,100,3127,93,0,3037,164,0,143,142,0,0,27,153.8,64000c37005d00000bdd00a400008f8e00000000 +9.7277,290,101,3142,95,0,3052,166,0,135,132,0,0,27,153.9,65000c46005f00000bec00a60000878400000000 +9.7598,291,102,3158,95,0,3068,168,0,139,144,0,0,27,154.1,66000c56005f00000bfc00a800008b9000000000 +9.7957,292,103,3169,93,0,3084,169,0,119,140,0,0,27,154.1,67000c61005d00000c0c00a90000778c00000000 +9.8278,293,104,3180,95,0,3095,170,0,129,133,0,0,27,154.1,68000c6c005f00000c1700aa0000818500000000 +9.8598,294,105,3191,95,0,3105,171,0,139,148,0,0,27,154.2,69000c77005f00000c2100ab00008b9400000000 +9.8958,295,106,3201,95,0,3112,171,0,136,139,0,0,26,151.3,6a000c81005f00000c2800ab0000888b00000000 +9.9278,296,107,3207,96,0,3119,172,0,126,140,0,0,26,151.4,6b000c87006000000c2f00ac00007e8c00000000 +9.9598,297,108,3214,96,0,3126,172,0,135,132,0,0,26,151.4,6c000c8e006000000c3600ac0000878400000000 +9.9918,298,109,3223,96,0,3133,172,0,144,135,0,0,26,151.3,6d000c97006000000c3d00ac0000908700000000 +10.0278,299,110,3234,95,0,3145,173,0,136,135,0,0,26,151.4,6e000ca2005f00000c4900ad0000888700000000 +10.0598,300,111,3242,95,0,3150,173,0,129,140,0,0,26,151.4,6f000caa005f00000c4e00ad0000818c00000000 +10.0918,301,112,3252,97,0,3159,174,0,137,136,0,0,26,151.3,70000cb4006100000c5700ae0000898800000000 +10.1278,302,113,3266,99,0,3174,175,0,133,132,0,0,26,151.2,71000cc2006300000c6600af0000858400000000 +10.1597,303,114,3284,100,0,3190,175,0,122,140,0,0,26,151.1,72000cd4006400000c7600af00007a8c00000000 +10.1918,304,115,3300,101,0,3204,175,0,128,135,0,0,26,151.1,73000ce4006500000c8400af0000808700000000 +10.2277,305,116,3305,102,0,3213,177,0,131,136,0,0,26,151.1,74000ce9006600000c8d00b10000838800000000 +10.2598,306,117,3312,102,0,3222,177,0,130,143,0,0,26,151.1,75000cf0006600000c9600b10000828f00000000 +10.2918,307,118,3321,100,0,3237,180,0,127,146,0,0,26,151.1,76000cf9006400000ca500b400007f9200000000 +10.3278,308,119,3330,100,0,3250,182,0,123,143,0,0,26,151.2,77000d02006400000cb200b600007b8f00000000 +10.3598,309,120,3335,100,0,3262,183,0,135,136,0,0,26,151.1,78000d07006400000cbe00b70000878800000000 +10.3918,310,121,3344,101,0,3272,183,0,133,133,0,0,26,151.1,79000d10006500000cc800b70000858500000000 +10.4278,311,122,3358,101,0,3281,183,0,122,140,0,0,26,151.1,7a000d1e006500000cd100b700007a8c00000000 +10.4598,312,123,3367,101,0,3300,185,0,124,132,0,0,26,151.2,7b000d27006500000ce400b900007c8400000000 +10.4918,313,124,3381,103,0,3317,186,0,127,144,0,0,26,151.2,7c000d35006700000cf500ba00007f9000000000 +10.5278,314,125,3394,102,0,3328,187,0,148,132,0,0,26,151.2,7d000d42006600000d0000bb0000948400000000 +10.5598,315,126,3404,103,0,3342,188,0,123,140,0,0,26,151.3,7e000d4c006700000d0e00bc00007b8c00000000 +10.5918,316,127,3415,105,0,3361,189,0,134,125,0,0,26,151.3,7f000d57006900000d2100bd0000867d00000000 +10.6238,317,128,3427,106,0,3373,190,0,132,138,0,0,26,151.5,80000d63006a00000d2d00be0000848a00000000 +10.6598,318,129,3443,106,0,3387,190,0,120,130,0,0,26,151.5,81000d73006a00000d3b00be0000788200000000 +10.6918,319,130,3457,107,0,3390,191,0,139,129,0,0,26,151.5,82000d81006b00000d3e00bf00008b8100000000 +10.7237,320,131,3473,109,0,3406,192,0,136,148,0,0,26,151.6,83000d91006d00000d4e00c00000889400000000 +10.7598,321,132,3488,110,0,3421,191,0,133,138,0,0,26,151.6,84000da0006e00000d5d00bf0000858a00000000 +10.7918,322,133,3501,109,0,3433,191,0,130,147,0,0,26,151.5,85000dad006d00000d6900bf0000829300000000 +10.8238,323,134,3513,109,0,3443,194,0,134,132,0,0,26,151.5,86000db9006d00000d7300c20000868400000000 +10.8597,324,135,3535,110,0,3463,195,0,132,135,0,0,26,151.5,87000dcf006e00000d8700c30000848700000000 +10.8918,325,136,3558,110,0,3481,195,0,129,130,0,0,26,151.5,88000de6006e00000d9900c30000818200000000 +10.9238,326,137,3574,110,0,3498,196,0,133,136,0,0,26,151.3,89000df6006e00000daa00c40000858800000000 +10.9598,327,138,3585,110,0,3508,196,0,131,137,0,0,26,151.3,8a000e01006e00000db400c40000838900000000 +10.9917,328,139,3601,110,0,3517,197,0,123,136,0,0,26,151.2,8b000e11006e00000dbd00c500007b8800000000 +11.0238,329,140,3613,110,0,3530,199,0,142,136,0,0,26,151.1,8c000e1d006e00000dca00c700008e8800000000 +11.0598,330,141,3623,111,0,3536,201,0,137,132,0,0,26,151.2,8d000e27006f00000dd000c90000898400000000 +11.0917,331,142,3631,112,0,3541,200,0,123,139,0,0,26,151.3,8e000e2f007000000dd500c800007b8b00000000 +11.1237,332,143,3640,114,0,3551,201,0,146,141,0,0,26,151.4,8f000e38007200000ddf00c90000928d00000000 +11.1598,333,144,3655,114,0,3563,204,0,128,146,0,0,26,151.4,90000e47007200000deb00cc0000809200000000 +11.1918,334,145,3671,115,0,3571,207,0,142,130,0,0,26,151.4,91000e57007300000df300cf00008e8200000000 +11.2238,335,146,3688,117,0,3588,209,0,141,133,0,0,26,151.4,92000e68007500000e0400d100008d8500000000 +11.2558,336,147,3710,118,0,3603,210,0,140,133,0,0,26,151.3,93000e7e007600000e1300d200008c8500000000 +11.2918,337,148,3730,119,0,3622,212,0,124,134,0,0,26,151.2,94000e92007700000e2600d400007c8600000000 +11.3238,338,149,3743,120,0,3634,213,0,134,138,0,0,26,151.2,95000e9f007800000e3200d50000868a00000000 +11.3558,339,150,3751,121,0,3642,212,0,141,136,0,0,26,151.1,96000ea7007900000e3a00d400008d8800000000 +11.3918,340,151,3756,121,0,3648,213,0,134,131,0,0,26,151.1,97000eac007900000e4000d50000868300000000 +11.4238,341,152,3768,123,0,3659,214,0,148,138,0,0,26,151.0,98000eb8007b00000e4b00d60000948a00000000 +11.4558,342,153,3785,124,0,3673,215,0,145,138,0,0,26,151.0,99000ec9007c00000e5900d70000918a00000000 +11.4918,343,154,3807,126,0,3692,218,0,136,136,0,0,26,150.8,9a000edf007e00000e6c00da0000888800000000 +11.5238,344,155,3829,128,0,3710,221,0,139,132,0,0,26,150.9,9b000ef5008000000e7e00dd00008b8400000000 +11.5558,345,156,3853,129,0,3731,223,0,137,142,0,0,26,151.0,9c000f0d008100000e9300df0000898e00000000 +11.5918,346,157,3878,129,0,3753,222,0,139,138,0,0,26,150.9,9d000f26008100000ea900de00008b8a00000000 +11.6237,347,158,3893,129,0,3768,225,0,129,150,0,0,26,150.9,9e000f35008100000eb800e10000819600000000 +11.6558,348,159,3905,130,0,3780,225,0,133,139,0,0,26,150.7,9f000f41008200000ec400e10000858b00000000 +11.6918,349,160,3920,132,0,3793,225,0,138,138,0,0,26,150.6,a0000f50008400000ed100e100008a8a00000000 +11.7238,350,161,3949,132,0,3816,228,0,140,149,0,0,26,150.9,a1000f6d008400000ee800e400008c9500000000 +11.7558,351,162,3978,135,0,3842,232,0,129,132,0,0,26,150.6,a2000f8a008700000f0200e80000818400000000 +11.7918,352,163,4006,139,0,3869,235,0,144,145,0,0,26,150.9,a3000fa6008b00000f1d00eb0000909100000000 +11.8238,353,164,4033,140,0,3893,236,0,135,153,0,0,26,150.9,a4000fc1008c00000f3500ec0000879900000000 +11.8558,354,165,4056,140,0,3913,238,0,137,147,0,0,26,151.0,a5000fd8008c00000f4900ee0000899300000000 +11.8878,355,166,4072,141,0,3925,239,0,133,135,0,0,26,151.1,a6000fe8008d00000f5500ef0000858700000000 +11.9238,356,167,4086,143,0,3942,240,0,127,140,0,0,26,151.1,a7000ff6008f00000f6600f000007f8c00000000 +11.9558,357,168,4104,143,0,3961,240,0,139,145,0,0,26,151.3,a8001008008f00000f7900f000008b9100000000 +11.9878,358,169,4124,144,0,3978,243,0,132,131,0,0,26,151.4,a900101c009000000f8a00f30000848300000000 +12.0238,359,170,4134,145,0,3993,243,0,130,140,0,0,26,151.5,aa001026009100000f9900f30000828c00000000 +12.0558,360,171,4144,146,0,4005,245,0,126,147,0,0,26,151.5,ab001030009200000fa500f500007e9300000000 +12.0877,361,172,4153,147,0,4016,246,0,142,155,0,0,26,151.6,ac001039009300000fb000f600008e9b00000000 +12.1238,362,173,4164,148,0,4026,247,0,136,157,0,0,26,151.6,ad001044009400000fba00f70000889d00000000 +12.1558,363,174,4177,149,0,4038,249,0,135,162,0,0,26,151.5,ae001051009500000fc600f9000087a200000000 +12.1877,364,175,4190,149,0,4050,249,0,145,165,0,0,26,151.5,af00105e009500000fd200f9000091a500000000 +12.2238,365,176,4206,152,0,4062,251,0,132,149,0,0,26,151.6,b000106e009800000fde00fb0000849500000000 +12.2558,366,177,4214,155,0,4073,253,0,138,144,0,0,26,151.5,b1001076009b00000fe900fd00008a9000000000 +12.2878,367,178,4223,156,0,4086,254,0,147,146,0,0,26,151.6,b200107f009c00000ff600fe0000939200000000 +12.3238,368,179,4237,157,0,4101,258,0,139,155,0,0,26,151.7,b300108d009d00001005010200008b9b00000000 +12.3558,369,180,4250,159,0,4116,260,0,121,145,0,0,26,151.9,b400109a009f0000101401040000799100000000 +12.3878,370,181,4274,161,0,4129,262,0,145,133,0,0,26,151.7,b50010b200a10000102101060000918500000000 +12.4238,371,182,4291,162,0,4146,262,0,129,136,0,0,26,151.5,b60010c300a20000103201060000818800000000 +12.4558,372,183,4303,161,0,4162,263,0,144,130,0,0,26,151.5,b70010cf00a10000104201070000908200000000 +12.4877,373,184,4312,160,0,4169,263,0,133,136,0,0,26,151.4,b80010d800a00000104901070000858800000000 +12.5199,374,185,4315,160,0,4173,264,0,126,149,0,0,26,151.3,b90010db00a00000104d010800007e9500000000 +12.5558,375,186,4321,160,0,4179,264,0,138,154,0,0,26,151.3,ba0010e100a000001053010800008a9a00000000 +12.5878,376,187,4333,160,0,4189,266,0,132,148,0,0,26,151.2,bb0010ed00a00000105d010a0000849400000000 +12.6198,377,188,4344,161,0,4200,268,0,127,145,0,0,26,151.3,bc0010f800a100001068010c00007f9100000000 +12.6558,378,189,4361,161,0,4216,272,0,140,162,0,0,26,151.2,bd00110900a100001078011000008ca200000000 +12.6878,379,190,4377,163,0,4230,274,0,130,159,0,0,26,151.1,be00111900a30000108601120000829f00000000 +12.7198,380,191,4396,163,0,4246,277,0,134,163,0,0,26,151.2,bf00112c00a3000010960115000086a300000000 +12.7558,381,192,4413,165,0,4258,278,0,143,162,0,0,26,151.0,c000113d00a5000010a2011600008fa200000000 +12.7878,382,193,4429,165,0,4273,280,0,144,150,0,0,26,151.0,c100114d00a5000010b101180000909600000000 +12.8198,383,194,4444,165,0,4286,282,0,133,145,0,0,26,151.0,c200115c00a5000010be011a0000859100000000 +12.8557,384,195,4461,167,0,4302,285,0,135,157,0,0,26,150.9,c300116d00a7000010ce011d0000879d00000000 +12.8878,385,196,4481,169,0,4317,288,0,135,161,0,0,26,151.1,c400118100a9000010dd0120000087a100000000 +12.9198,386,197,4496,170,0,4333,291,0,129,156,0,0,26,150.9,c500119000aa000010ed01230000819c00000000 +12.9558,387,198,4513,171,0,4347,292,0,135,170,0,0,26,150.9,c60011a100ab000010fb0124000087aa00000000 +12.9878,388,199,4529,172,0,4360,294,0,137,148,0,0,26,150.8,c70011b100ac0000110801260000899400000000 +13.0198,389,200,4542,174,0,4370,294,0,127,160,0,0,26,150.9,c80011be00ae00001112012600007fa000000000 +13.0559,390,201,4548,174,0,4377,295,0,132,165,0,0,26,150.8,c90011c400ae000011190127000084a500000000 +13.0879,391,202,4560,174,0,4386,297,0,135,170,0,0,26,150.7,ca0011d000ae000011220129000087aa00000000 +13.1198,392,203,4574,175,0,4401,299,0,128,143,0,0,26,150.7,cb0011de00af00001131012b0000808f00000000 +13.1518,393,204,4592,176,0,4415,301,0,130,148,0,0,26,150.9,cc0011f000b00000113f012d0000829400000000 +13.1878,394,205,4607,178,0,4429,303,0,130,157,0,0,26,151.0,cd0011ff00b20000114d012f0000829d00000000 +13.2198,395,206,4619,178,0,4440,304,0,135,159,0,0,26,150.9,ce00120b00b20000115801300000879f00000000 +13.2518,396,207,4629,180,0,4449,307,0,124,161,0,0,26,150.9,cf00121500b400001161013300007ca100000000 +13.2878,397,208,4644,181,0,4461,308,0,140,160,0,0,26,151.0,d000122400b50000116d013400008ca000000000 +13.3199,398,209,4658,181,0,4471,310,0,134,170,0,0,26,150.9,d100123200b5000011770136000086aa00000000 +13.3519,399,210,4674,183,0,4483,312,0,138,141,0,0,26,150.9,d200124200b700001183013800008a8d00000000 +13.3878,400,211,4693,185,0,4499,315,0,131,144,0,0,26,151.0,d300125500b900001193013b0000839000000000 +13.4198,401,212,4710,188,0,4511,317,0,139,138,0,0,26,151.0,d400126600bc0000119f013d00008b8a00000000 +13.4518,402,213,4718,188,0,4524,319,0,117,143,0,0,26,150.9,d500126e00bc000011ac013f0000758f00000000 +13.4878,403,214,4726,187,0,4528,320,0,130,151,0,0,26,150.8,d600127600bb000011b001400000829700000000 +13.5198,404,215,4731,185,0,4533,320,0,129,164,0,0,26,150.7,d700127b00b9000011b50140000081a400000000 +13.5518,405,216,4737,186,0,4537,320,0,130,175,0,0,26,150.7,d800128100ba000011b90140000082af00000000 +13.5878,406,217,4751,187,0,4548,321,0,129,168,0,0,26,150.7,d900128f00bb000011c40141000081a800000000 +13.6198,407,218,4767,187,0,4565,323,0,147,159,0,0,26,150.7,da00129f00bb000011d501430000939f00000000 +13.6518,408,219,4790,188,0,4586,324,0,139,159,0,0,26,150.6,db0012b600bc000011ea014400008b9f00000000 +13.6878,409,220,4823,188,0,4614,326,0,132,146,0,0,26,150.6,dc0012d700bc0000120601460000849200000000 +13.7198,410,221,4851,188,0,4646,328,0,128,160,0,0,26,150.6,dd0012f300bc000012260148000080a000000000 +13.7518,411,222,4871,188,0,4663,330,0,136,156,0,0,26,150.5,de00130700bc00001237014a0000889c00000000 +13.7838,412,223,4884,187,0,4674,330,0,130,146,0,0,26,150.4,df00131400bb00001242014a0000829200000000 +13.8198,413,224,4888,187,0,4679,331,0,128,146,0,0,26,150.5,e000131800bb00001247014b0000809200000000 +13.8518,414,225,4889,187,0,4679,331,0,135,154,0,0,26,150.4,e100131900bb00001247014b0000879a00000000 +13.8839,415,226,4895,187,0,4685,332,0,137,151,0,0,26,150.4,e200131f00bb0000124d014c0000899700000000 +13.9199,416,227,4902,188,0,4691,333,0,132,161,0,0,26,150.3,e300132600bc00001253014d000084a100000000 +13.9518,417,228,4911,188,0,4701,334,0,141,157,0,0,26,150.3,e400132f00bc0000125d014e00008d9d00000000 +13.9839,418,229,4929,189,0,4713,336,0,131,156,0,0,26,150.3,e500134100bd0000126901500000839c00000000 +14.02,419,230,4948,189,0,4730,339,0,130,165,0,0,26,150.1,e600135400bd0000127a0153000082a500000000 +14.0519,420,231,4961,190,0,4749,341,0,134,132,0,0,26,149.9,e700136100be0000128d01550000868400000000 +14.0839,421,232,4975,191,0,4765,342,0,122,133,0,0,26,149.8,e800136f00bf0000129d015600007a8500000000 +14.1198,422,233,4991,192,0,4777,343,0,125,150,0,0,26,149.8,e900137f00c0000012a9015700007d9600000000 +14.1518,423,234,4994,192,0,4780,344,0,123,160,0,0,26,149.7,ea00138200c0000012ac015800007ba000000000 +14.1838,424,235,4994,192,0,4780,344,0,136,159,0,0,26,149.7,eb00138200c0000012ac01580000889f00000000 +14.2198,425,236,4994,192,0,4785,344,0,122,159,0,0,26,149.7,ec00138200c0000012b1015800007a9f00000000 +14.2518,426,237,4994,192,0,4788,343,0,129,163,0,0,26,149.7,ed00138200c0000012b40157000081a300000000 +14.2838,427,238,4994,192,0,4790,342,0,125,169,0,0,26,149.8,ee00138200c0000012b6015600007da900000000 +14.3198,428,239,4995,192,0,4794,340,0,135,159,0,0,26,149.8,ef00138300c0000012ba01540000879f00000000 +14.3518,429,240,4995,192,0,4797,339,0,127,158,0,0,26,149.8,f000138300c0000012bd015300007f9e00000000 +14.3838,430,241,4996,190,0,4800,337,0,132,159,0,0,26,149.8,f100138400be000012c001510000849f00000000 +14.4158,431,242,4998,188,0,4808,332,0,122,155,0,0,26,150.0,f200138600bc000012c8014c00007a9b00000000 +14.4518,432,243,5002,178,0,4819,321,0,137,153,0,0,26,150.5,f300138a00b2000012d301410000899900000000 +14.4838,433,244,5007,164,0,4831,303,0,130,141,0,0,26,151.0,f400138f00a4000012df012f0000828d00000000 +14.5158,434,245,5016,146,0,4845,277,0,134,136,0,0,26,151.4,f50013980092000012ed01150000868800000000 +14.5518,435,246,5029,120,0,4860,250,0,119,142,0,0,26,151.7,f60013a50078000012fc00fa0000778e00000000 +14.5838,436,247,5048,88,0,4878,218,0,132,153,0,0,26,151.7,f70013b800580000130e00da0000849900000000 +14.6158,437,248,5053,69,0,4903,178,0,127,174,0,0,26,151.6,f80013bd00450000132700b200007fae00000000 +14.6518,438,249,5072,39,0,4934,130,0,136,134,0,0,26,151.5,f90013d000270000134600820000888600000000 +14.6838,439,250,5086,6,0,4959,94,0,136,139,0,0,26,151.5,fa0013de00060000135f005e0000888b00000000 +14.7159,440,251,5098,65505,0,4978,48,0,129,133,0,0,26,151.8,fb0013eaffe10000137200300000818500000000 +14.7519,441,252,5106,65469,0,4992,6,0,131,133,0,0,26,151.9,fc0013f2ffbd0000138000060000838500000000 +14.7837,442,253,5113,65440,0,4997,65517,0,137,146,0,0,26,152.0,fd0013f9ffa000001385ffed0000899200000000 +14.8158,443,254,5117,65414,0,5001,65482,0,141,131,0,0,26,151.8,fe0013fdff8600001389ffca00008d8300000000 +14.8518,444,255,5119,65381,0,5004,65454,0,132,134,0,0,26,151.3,ff0013ffff650000138cffae0000848600000000 +14.8838,445,0,5121,65360,0,5006,65424,0,136,130,0,0,26,150.8,00001401ff500000138eff900000888200000000 +14.9158,446,1,5121,65334,0,5007,65396,0,137,132,0,0,26,150.2,01001401ff360000138fff740000898400000000 +14.9518,447,2,5123,65310,0,5010,65367,0,124,126,0,0,26,149.8,02001403ff1e00001392ff5700007c7e00000000 +14.9839,448,3,5122,65290,0,5007,65350,0,131,137,0,0,26,149.6,03001402ff0a0000138fff460000838900000000 +15.0158,449,4,5122,65283,0,5007,65340,0,128,128,0,0,26,149.6,04001402ff030000138fff3c0000808000000000 +15.0478,450,5,5122,65281,0,5007,65339,0,119,132,0,0,26,149.6,05001402ff010000138fff3b0000778400000000 +15.0838,451,6,5122,65281,0,5007,65339,0,130,140,0,0,26,149.6,06001402ff010000138fff3b0000828c00000000 +15.1158,452,7,5122,65281,0,5007,65339,0,132,137,0,0,26,149.6,07001402ff010000138fff3b0000848900000000 +15.1479,453,8,5121,65280,0,5007,65339,0,133,129,0,0,26,149.6,08001401ff000000138fff3b0000858100000000 +15.1838,454,9,5122,65275,0,4994,65328,0,130,132,0,0,26,149.6,09001402fefb00001382ff300000828400000000 +15.2158,455,10,5121,65274,0,4965,65318,0,124,135,0,0,26,149.2,0a001401fefa00001365ff2600007c8700000000 +15.2478,456,11,5095,65273,0,4923,65310,0,134,141,0,0,26,148.5,0b0013e7fef90000133bff1e0000868d00000000 +15.2839,457,12,5051,65264,0,4876,65299,0,150,139,0,0,26,148.4,0c0013bbfef00000130cff130000968b00000000 +15.3158,458,13,5000,65249,0,4816,65278,0,138,139,0,0,26,147.9,0d001388fee1000012d0fefe00008a8b00000000 +15.3478,459,14,4947,65227,0,4752,65251,0,157,130,0,0,26,147.7,0e001353fecb00001290fee300009d8200000000 +15.3838,460,15,4862,65197,0,4664,65225,0,172,139,0,0,26,147.6,0f0012fefead00001238fec90000ac8b00000000 +15.4159,461,16,4741,65161,0,4544,65185,0,157,158,0,0,26,146.7,10001285fe89000011c0fea100009d9e00000000 +15.4478,462,17,4623,65133,0,4435,65154,0,171,170,0,0,26,146.2,1100120ffe6d00001153fe820000abaa00000000 +15.4838,463,18,4512,65110,0,4323,65132,0,169,150,0,0,26,145.6,120011a0fe56000010e3fe6c0000a99600000000 +15.5158,464,19,4403,65094,0,4207,65116,0,159,149,0,0,26,145.5,13001133fe460000106ffe5c00009f9500000000 +15.5478,465,20,4270,65090,0,4103,65110,0,158,126,0,0,26,145.9,140010aefe4200001007fe5600009e7e00000000 +15.5838,466,21,4182,65088,0,3999,65107,0,166,162,0,0,27,148.4,15001056fe4000000f9ffe530000a6a200000000 +15.6158,467,22,4100,65087,0,3914,65104,0,179,139,0,0,27,147.9,16001004fe3f00000f4afe500000b38b00000000 +15.6478,468,23,4013,65086,0,3836,65100,0,159,147,0,0,27,147.6,17000fadfe3e00000efcfe4c00009f9300000000 +15.6798,469,24,3945,65085,0,3778,65095,0,155,157,0,0,27,147.2,18000f69fe3d00000ec2fe4700009b9d00000000 +15.7158,470,25,3865,65076,0,3702,65087,0,152,159,0,0,27,147.7,19000f19fe3400000e76fe3f0000989f00000000 +15.7479,471,26,3783,65070,0,3625,65075,0,159,170,0,0,27,147.5,1a000ec7fe2e00000e29fe3300009faa00000000 +15.7799,472,27,3686,65065,0,3548,65064,0,160,151,0,0,27,147.6,1b000e66fe2900000ddcfe280000a09700000000 +15.8158,473,28,3601,65062,0,3475,65057,0,137,161,0,0,27,147.4,1c000e11fe2600000d93fe21000089a100000000 +15.8478,474,29,3514,65060,0,3395,65054,0,165,156,0,0,27,147.8,1d000dbafe2400000d43fe1e0000a59c00000000 +15.8798,475,30,3429,65059,0,3322,65044,0,174,153,0,0,27,147.6,1e000d65fe2300000cfafe140000ae9900000000 +15.9158,476,31,3356,65056,0,3249,65041,0,167,167,0,0,27,147.3,1f000d1cfe2000000cb1fe110000a7a700000000 +15.9478,477,32,3269,65053,0,3171,65038,0,130,155,0,0,27,147.6,20000cc5fe1d00000c63fe0e0000829b00000000 +15.9798,478,33,3152,65048,0,3057,65035,0,149,167,0,0,27,148.0,21000c50fe1800000bf1fe0b000095a700000000 +16.0159,479,34,3019,65046,0,2954,65022,0,168,143,0,0,27,147.8,22000bcbfe1600000b8afdfe0000a88f00000000 +16.0479,480,35,2880,65045,0,2824,65006,0,140,137,0,0,27,146.9,23000b40fe1500000b08fdee00008c8900000000 +16.0798,481,36,2733,65039,0,2681,64991,0,158,154,0,0,27,147.3,24000aadfe0f00000a79fddf00009e9a00000000 +16.1158,482,37,2559,65032,0,2538,64985,0,156,164,0,0,27,147.3,250009fffe08000009eafdd900009ca400000000 +16.1478,483,38,2391,65024,0,2376,64975,0,136,164,0,0,27,148.1,26000957fe0000000948fdcf000088a400000000 +16.1799,484,39,2222,65014,0,2227,64962,0,160,126,0,0,27,148.2,270008aefdf6000008b3fdc20000a07e00000000 +16.2119,485,40,2045,65003,0,2057,64946,0,139,157,0,0,27,148.4,280007fdfdeb00000809fdb200008b9d00000000 +16.2478,486,41,1835,64991,0,1880,64913,0,149,136,0,0,27,148.2,2900072bfddf00000758fd910000958800000000 +16.2798,487,42,1620,64980,0,1685,64897,0,156,136,0,0,27,147.7,2a000654fdd400000695fd8100009c8800000000 +16.3118,488,43,1426,64972,0,1499,64889,0,139,122,0,0,27,146.9,2b000592fdcc000005dbfd7900008b7a00000000 +16.3479,489,44,1231,64966,0,1304,64878,0,127,141,0,0,27,146.6,2c0004cffdc600000518fd6e00007f8d00000000 +16.3799,490,45,1061,64960,0,1154,64865,0,122,136,0,0,27,145.8,2d000425fdc000000482fd6100007a8800000000 +16.4118,491,46,880,64959,0,994,64850,0,130,142,0,0,27,145.0,2e000370fdbf000003e2fd520000828e00000000 +16.4478,492,47,733,64957,0,854,64848,0,117,139,0,0,27,146.3,2f0002ddfdbd00000356fd500000758b00000000 +16.4798,493,48,597,64956,0,724,64844,0,144,133,0,0,27,147.9,30000255fdbc000002d4fd4c0000908500000000 +16.5118,494,49,473,64954,0,595,64843,0,123,141,0,0,27,149.2,310001d9fdba00000253fd4b00007b8d00000000 +16.5478,495,50,319,64949,0,450,64839,0,127,142,0,0,27,151.8,3200013ffdb5000001c2fd4700007f8e00000000 +16.5798,496,51,186,64944,0,317,64804,0,123,124,0,0,27,154.6,330000bafdb00000013dfd2400007b7c00000000 +16.6118,497,52,15,64939,0,176,64771,0,143,129,0,0,27,158.7,3400000ffdab000000b0fd0300008f8100000000 +16.6478,498,53,65392,64938,0,37,64777,0,142,136,0,0,27,161.9,3500ff70fdaa00000025fd0900008e8800000000 +16.6798,499,54,65234,64949,0,65442,64772,0,125,136,0,0,26,163.6,3600fed2fdb50000ffa2fd0400007d8800000000 +16.7118,500,55,65111,64950,0,65314,64772,0,126,142,0,0,26,162.5,3700fe57fdb60000ff22fd0400007e8e00000000 +16.7478,501,56,65013,64950,0,65244,64769,0,137,139,0,0,26,163.6,3800fdf5fdb60000fedcfd010000898b00000000 +16.7798,502,57,64996,64951,0,65247,64770,0,137,136,0,0,24,163.6,3900fde4fdb70000fedffd020000898800000000 +16.8118,503,58,64996,64951,0,65247,64768,0,130,131,0,0,24,156.8,3a00fde4fdb70000fedffd000000828300000000 +16.8438,504,59,64996,64951,0,65248,64769,0,132,133,0,0,24,157.0,3b00fde4fdb70000fee0fd010000848500000000 +16.8798,505,60,64996,64951,0,65248,64769,0,132,134,0,0,23,153.4,3c00fde4fdb70000fee0fd010000848600000000 +16.9118,506,61,64996,64951,0,65248,64769,0,151,146,0,0,23,153.4,3d00fde4fdb70000fee0fd010000979200000000 +16.9438,507,62,64996,64951,0,65248,64769,0,145,139,0,0,23,153.5,3e00fde4fdb70000fee0fd010000918b00000000 +16.9798,508,63,64996,64951,0,65248,64769,0,132,134,0,0,23,153.6,3f00fde4fdb70000fee0fd010000848600000000 +17.0118,509,64,64996,64951,0,65248,64769,0,141,141,0,0,23,153.6,4000fde4fdb70000fee0fd0100008d8d00000000 +17.0438,510,65,64996,64951,0,65248,64769,0,147,152,0,0,23,153.7,4100fde4fdb70000fee0fd010000939800000000 +17.0798,511,66,64996,64951,0,65248,64769,0,138,135,0,0,23,153.6,4200fde4fdb70000fee0fd0100008a8700000000 +17.1118,512,67,64996,64951,0,65249,64769,0,144,148,0,0,23,153.6,4300fde4fdb70000fee1fd010000909400000000 +17.1438,513,68,64996,64951,0,65249,64769,0,132,134,0,0,23,153.5,4400fde4fdb70000fee1fd010000848600000000 +17.1798,514,69,64996,64951,0,65249,64769,0,135,137,0,0,23,153.5,4500fde4fdb70000fee1fd010000878900000000 +17.2118,515,70,64996,64951,0,65249,64769,0,137,141,0,0,23,153.6,4600fde4fdb70000fee1fd010000898d00000000 +17.2438,516,71,64996,64951,0,65249,64769,0,138,130,0,0,23,153.4,4700fde4fdb70000fee1fd0100008a8200000000 +17.2798,517,72,64996,64951,0,65249,64768,0,133,135,0,0,23,153.5,4800fde4fdb70000fee1fd000000858700000000 +17.3118,518,73,64996,64951,0,65249,64768,0,132,144,0,0,23,153.4,4900fde4fdb70000fee1fd000000849000000000 +17.3438,519,74,64998,64952,0,65254,64768,0,137,133,0,0,23,153.4,4a00fde6fdb80000fee6fd000000898500000000 +17.3798,520,75,64999,64952,0,65256,64768,0,135,140,0,0,23,153.3,4b00fde7fdb80000fee8fd000000878c00000000 +17.4118,521,76,65001,64952,0,65259,64769,0,131,137,0,0,23,153.2,4c00fde9fdb80000feebfd010000838900000000 +17.4438,522,77,65004,64953,0,65263,64769,0,131,147,0,0,23,153.1,4d00fdecfdb90000feeffd010000839300000000 +17.4758,523,78,65006,64953,0,65267,64769,0,137,129,0,0,23,153.0,4e00fdeefdb90000fef3fd010000898100000000 +17.5118,524,79,65008,64953,0,65268,64769,0,128,128,0,0,23,153.0,4f00fdf0fdb90000fef4fd010000808000000000 +17.5438,525,80,65009,64953,0,65271,64769,0,121,130,0,0,23,153.0,5000fdf1fdb90000fef7fd010000798200000000 +17.5758,526,81,65014,64953,0,65277,64769,0,144,136,0,0,23,152.9,5100fdf6fdb90000fefdfd010000908800000000 +17.6118,527,82,65020,64955,0,65288,64770,0,152,140,0,0,23,152.8,5200fdfcfdbb0000ff08fd020000988c00000000 +17.6438,528,83,65026,64955,0,65290,64772,0,137,136,0,0,23,152.6,5300fe02fdbb0000ff0afd040000898800000000 +17.6758,529,84,65036,64956,0,65305,64772,0,149,138,0,0,23,152.4,5400fe0cfdbc0000ff19fd040000958a00000000 +17.7119,530,85,65048,64956,0,65318,64769,0,139,140,0,0,23,152.2,5500fe18fdbc0000ff26fd0100008b8c00000000 +17.7438,531,86,65064,64955,0,65330,64768,0,139,137,0,0,23,151.9,5600fe28fdbb0000ff32fd0000008b8900000000 +17.7758,532,87,65068,64959,0,65334,64772,0,143,141,4,4,23,151.6,5700fe2cfdbf0000ff36fd0400008f8d04040000 +17.8118,533,88,65068,64959,0,65334,64772,0,143,141,4,4,23,151.5,5800fe2cfdbf0000ff36fd0400008f8d04040000 +17.8438,534,89,65068,64959,0,65334,64772,0,143,141,4,4,23,151.3,5900fe2cfdbf0000ff36fd0400008f8d04040000 +17.8758,535,90,65068,64959,0,65334,64772,0,143,141,4,4,23,151.1,5a00fe2cfdbf0000ff36fd0400008f8d04040000 +17.9119,536,91,65140,64951,0,65399,64766,0,139,142,0,0,23,150.9,5b00fe74fdb70000ff77fcfe00008b8e00000000 +17.9438,537,92,65162,64950,0,65419,64766,0,133,138,0,0,23,150.7,5c00fe8afdb60000ff8bfcfe0000858a00000000 +17.9758,538,93,65166,64950,0,65423,64766,0,133,142,4,4,23,150.4,5d00fe8efdb60000ff8ffcfe0000858e04040000 +18.0118,539,94,65166,64950,0,65423,64766,0,133,142,4,4,23,150.2,5e00fe8efdb60000ff8ffcfe0000858e04040000 +18.0438,540,95,65166,64950,0,65423,64766,0,133,142,4,4,23,150.1,5f00fe8efdb60000ff8ffcfe0000858e04040000 +18.0758,541,96,65166,64950,0,65423,64766,0,133,142,4,4,23,149.9,6000fe8efdb60000ff8ffcfe0000858e04040000 +18.1078,542,97,65166,64950,0,65423,64766,0,133,142,4,4,23,149.9,6100fe8efdb60000ff8ffcfe0000858e04040000 +18.1438,543,98,65166,64950,0,65423,64766,0,133,142,4,4,23,149.8,6200fe8efdb60000ff8ffcfe0000858e04040000 +18.1758,544,99,65166,64950,0,65423,64766,0,133,142,4,4,23,149.8,6300fe8efdb60000ff8ffcfe0000858e04040000 +18.2078,545,100,65166,64950,0,65423,64766,0,133,142,4,4,23,149.7,6400fe8efdb60000ff8ffcfe0000858e04040000 +18.2439,546,101,65166,64950,0,65423,64766,0,133,142,4,4,23,149.8,6500fe8efdb60000ff8ffcfe0000858e04040000 +18.2758,547,102,65166,64950,0,65423,64766,0,133,142,4,4,23,149.8,6600fe8efdb60000ff8ffcfe0000858e04040000 +18.3079,548,103,65166,64950,0,65423,64766,0,133,142,4,4,23,149.7,6700fe8efdb60000ff8ffcfe0000858e04040000 +18.3438,549,104,65166,64950,0,65423,64766,0,133,142,4,4,23,149.7,6800fe8efdb60000ff8ffcfe0000858e04040000 +18.3758,550,105,65166,64950,0,65423,64766,0,133,142,4,4,23,149.6,6900fe8efdb60000ff8ffcfe0000858e04040000 +18.4078,551,106,65166,64950,0,65423,64766,0,133,142,4,4,23,149.7,6a00fe8efdb60000ff8ffcfe0000858e04040000 +18.4438,552,107,65166,64950,0,65423,64766,0,133,142,4,4,23,149.6,6b00fe8efdb60000ff8ffcfe0000858e04040000 +18.4758,553,108,65166,64950,0,65423,64766,0,133,142,4,4,23,149.4,6c00fe8efdb60000ff8ffcfe0000858e04040000 +18.5078,554,109,65166,64950,0,65423,64766,0,133,142,4,4,23,149.3,6d00fe8efdb60000ff8ffcfe0000858e04040000 +18.5438,555,110,65300,64952,0,65535,64777,0,134,139,0,0,23,149.1,6e00ff14fdb80000fffffd090000868b00000000 +18.5758,556,111,65300,64956,0,65535,64781,0,134,143,4,4,23,148.9,6f00ff14fdbc0000fffffd0d0000868f04040000 +18.6078,557,112,65320,64953,0,17,64778,0,137,131,0,0,23,148.9,7000ff28fdb900000011fd0a0000898300000000 +18.6438,558,113,65333,64953,0,27,64778,0,154,146,0,0,23,148.8,7100ff35fdb90000001bfd0a00009a9200000000 +18.6758,559,114,65344,64953,0,38,64779,0,156,134,0,0,23,148.6,7200ff40fdb900000026fd0b00009c8600000000 +18.7078,560,115,65356,64954,0,50,64780,0,124,138,0,0,23,148.5,7300ff4cfdba00000032fd0c00007c8a00000000 +18.7398,561,116,65366,64955,0,57,64780,0,135,138,0,0,23,148.0,7400ff56fdbb00000039fd0c0000878a00000000 +18.7759,562,117,65376,64956,0,60,64778,0,145,146,0,0,23,147.7,7500ff60fdbc0000003cfd0a0000919200000000 +18.8078,563,118,65390,64956,0,72,64779,0,149,139,0,0,23,147.5,7600ff6efdbc00000048fd0b0000958b00000000 +18.8398,564,119,65390,64956,0,1100,64783,0,149,143,4,4,23,147.2,7700ff6efdbc0000044cfd0f0000958f04040000 +18.8758,565,120,65390,64956,0,1100,64783,0,149,143,4,4,23,147.0,7800ff6efdbc0000044cfd0f0000958f04040000 +18.9078,566,121,65434,64958,0,104,64781,0,151,143,0,0,23,146.8,7900ff9afdbe00000068fd0d0000978f00000000 +18.9398,567,122,65452,64958,0,116,64782,0,157,137,0,0,23,146.5,7a00ffacfdbe00000074fd0e00009d8900000000 +18.9758,568,123,65462,64958,0,124,64784,0,158,128,0,0,23,146.3,7b00ffb6fdbe0000007cfd1000009e8000000000 +19.0078,569,124,65471,64960,0,126,64782,0,149,134,0,0,23,146.1,7c00ffbffdc00000007efd0e0000958600000000 +19.0398,570,125,65481,64961,0,134,64784,0,137,136,0,0,23,145.9,7d00ffc9fdc100000086fd100000898800000000 +19.0758,571,126,65495,64963,0,145,64785,0,165,137,0,0,23,145.5,7e00ffd7fdc300000091fd110000a58900000000 +19.1078,572,127,65510,64962,0,154,64788,0,152,132,0,0,23,145.1,7f00ffe6fdc20000009afd140000988400000000 +19.1398,573,128,65526,64962,0,169,64789,0,161,137,0,0,23,144.6,8000fff6fdc2000000a9fd150000a18900000000 +19.1758,574,129,3,64962,0,185,64790,0,165,133,0,0,24,147.6,81000003fdc2000000b9fd160000a58500000000 +19.2078,575,130,15,64963,0,194,64791,0,163,138,0,0,24,147.1,8200000ffdc3000000c2fd170000a38a00000000 +19.2398,576,131,27,64963,0,204,64790,0,159,137,0,0,24,146.8,8300001bfdc3000000ccfd1600009f8900000000 +19.2758,577,132,30,64963,0,209,64791,0,159,141,0,0,24,146.8,8400001efdc3000000d1fd1700009f8d00000000 +19.3078,578,133,34,64963,0,210,64785,0,154,142,0,0,24,146.7,85000022fdc3000000d2fd1100009a8e00000000 +19.3398,579,134,41,64963,0,214,64785,0,168,136,0,0,24,146.6,86000029fdc3000000d6fd110000a88800000000 +19.3718,580,135,49,64963,0,223,64784,0,161,131,0,0,24,146.5,87000031fdc3000000dffd100000a18300000000 +19.4078,581,136,56,64963,0,230,64784,0,159,123,0,0,24,146.2,88000038fdc3000000e6fd1000009f7b00000000 +19.4398,582,137,62,64964,0,234,64783,0,168,140,0,0,24,146.0,8900003efdc4000000eafd0f0000a88c00000000 +19.4718,583,138,69,64964,0,240,64784,0,151,143,0,0,24,145.8,8a000045fdc4000000f0fd100000978f00000000 +19.5078,584,139,79,64964,0,251,64784,0,148,135,0,0,24,145.6,8b00004ffdc4000000fbfd100000948700000000 +19.5398,585,140,90,64965,0,258,64786,0,164,143,0,0,24,145.4,8c00005afdc500000102fd120000a48f00000000 +19.5718,586,141,103,64965,0,271,64786,0,168,138,0,0,25,148.4,8d000067fdc50000010ffd120000a88a00000000 +19.6078,587,142,120,64965,0,284,64787,0,147,137,0,0,25,148.2,8e000078fdc50000011cfd130000938900000000 +19.6398,588,143,137,64966,0,292,64787,0,162,139,0,0,25,148.1,8f000089fdc600000124fd130000a28b00000000 +19.6718,589,144,1165,64966,0,1316,64791,0,166,143,4,4,25,147.8,9000048dfdc600000524fd170000a68f04040000 +19.7078,590,145,156,64966,0,314,64787,0,158,133,0,0,25,147.7,9100009cfdc60000013afd1300009e8500000000 +19.7398,591,146,164,64967,0,321,64787,0,153,147,0,0,25,147.6,920000a4fdc700000141fd130000999300000000 +19.7718,592,147,173,64967,0,332,64786,0,160,142,0,0,25,147.4,930000adfdc70000014cfd120000a08e00000000 +19.8078,593,148,187,64967,0,344,64786,0,167,145,0,0,25,147.3,940000bbfdc700000158fd120000a79100000000 +19.8398,594,149,198,64967,0,357,64787,0,148,131,0,0,25,147.1,950000c6fdc700000165fd130000948300000000 +19.8718,595,150,206,64969,0,364,64789,0,161,146,0,0,25,146.9,960000cefdc90000016cfd150000a19200000000 +19.9078,596,151,215,64969,0,372,64789,0,158,136,0,0,25,146.7,970000d7fdc900000174fd1500009e8800000000 +19.9398,597,152,1239,64973,0,1396,64789,0,158,140,4,4,25,146.5,980004d7fdcd00000574fd1500009e8c04040000 +19.9718,598,153,241,64968,0,393,64791,0,168,143,0,0,25,146.1,990000f1fdc800000189fd170000a88f00000000 +20.0038,599,154,257,64968,0,407,64791,0,164,130,0,0,25,145.7,9a000101fdc800000197fd170000a48200000000 +20.0399,600,155,275,64968,0,417,64794,0,164,132,0,0,25,145.5,9b000113fdc8000001a1fd1a0000a48400000000 +20.0718,601,156,296,64969,0,435,64792,0,162,130,0,0,26,145.1,9c000128fdc9000001b3fd180000a28200000000 +20.1038,602,157,309,64969,0,446,64794,0,164,133,0,0,26,147.8,9d000135fdc9000001befd1a0000a48500000000 +20.1398,603,158,317,64970,0,455,64794,0,168,142,0,0,26,147.6,9e00013dfdca000001c7fd1a0000a88e00000000 +20.1718,604,159,333,64972,0,466,64796,0,163,153,0,0,26,147.3,9f00014dfdcc000001d2fd1c0000a39900000000 +20.2038,605,160,349,64973,0,480,64798,0,140,169,0,0,26,147.1,a000015dfdcd000001e0fd1e00008ca900000000 +20.2398,606,161,363,64973,0,495,64799,0,139,168,0,0,26,146.9,a100016bfdcd000001effd1f00008ba800000000 +20.2718,607,162,381,64974,0,508,64801,0,137,176,0,0,26,146.7,a200017dfdce000001fcfd21000089b000000000 +20.3039,608,163,1405,64974,0,1532,64805,0,141,180,4,4,26,146.7,a300057dfdce000005fcfd2500008db404040000 +20.3398,609,164,421,64974,0,534,64804,0,159,147,0,0,26,146.4,a40001a5fdce00000216fd2400009f9300000000 +20.3718,610,165,439,64975,0,553,64807,0,170,131,0,0,26,146.1,a50001b7fdcf00000229fd270000aa8300000000 +20.4038,611,166,1463,64975,0,1581,64807,0,174,135,4,4,26,145.9,a60005b7fdcf0000062dfd270000ae8704040000 +20.4398,612,167,467,64975,0,576,64810,0,165,148,0,0,26,145.8,a70001d3fdcf00000240fd2a0000a59400000000 +20.4718,613,168,479,64975,0,584,64811,0,161,146,0,0,27,145.6,a80001dffdcf00000248fd2b0000a19200000000 +20.5038,614,169,491,64975,0,593,64811,0,156,132,0,0,27,148.3,a90001ebfdcf00000251fd2b00009c8400000000 +20.5398,615,170,503,64975,0,601,64810,0,146,135,0,0,27,148.2,aa0001f7fdcf00000259fd2a0000928700000000 +20.5718,616,171,512,64975,0,610,64810,0,150,132,0,0,27,148.0,ab000200fdcf00000262fd2a0000968400000000 +20.6038,617,172,525,64975,0,617,64810,0,163,145,0,0,27,147.7,ac00020dfdcf00000269fd2a0000a39100000000 +20.6358,618,173,536,64974,0,624,64810,0,154,148,0,0,27,147.5,ad000218fdce00000270fd2a00009a9400000000 +20.6718,619,174,547,64974,0,633,64811,0,150,127,0,0,27,147.6,ae000223fdce00000279fd2b0000967f00000000 +20.7038,620,175,560,64974,0,645,64811,0,146,141,0,0,27,147.4,af000230fdce00000285fd2b0000928d00000000 +20.7358,621,176,574,64974,0,657,64811,0,135,121,0,0,27,147.3,b000023efdce00000291fd2b0000877900000000 +20.7719,622,177,591,64974,0,672,64810,0,133,144,0,0,27,147.1,b100024ffdce000002a0fd2a0000859000000000 +20.8038,623,178,612,64974,0,690,64811,0,137,149,0,0,27,146.8,b2000264fdce000002b2fd2b0000899500000000 +20.8358,624,179,633,64974,0,711,64810,0,164,138,0,0,27,146.4,b3000279fdce000002c7fd2a0000a48a00000000 +20.8718,625,180,655,64974,0,731,64810,0,162,135,0,0,27,146.1,b400028ffdce000002dbfd2a0000a28700000000 +20.9038,626,181,676,64974,0,746,64811,0,166,135,0,0,27,145.7,b50002a4fdce000002eafd2b0000a68700000000 +20.9358,627,182,697,64974,0,769,64813,0,150,130,0,0,27,145.4,b60002b9fdce00000301fd2d0000968200000000 +20.9718,628,183,721,64975,0,789,64816,0,162,141,0,0,28,148.1,b70002d1fdcf00000315fd300000a28d00000000 +21.0038,629,184,744,64977,0,805,64814,0,154,158,0,0,28,148.3,b80002e8fdd100000325fd2e00009a9e00000000 +21.0358,630,185,767,64979,0,823,64816,0,167,150,0,0,28,148.5,b90002fffdd300000337fd300000a79600000000 +21.0718,631,186,785,64980,0,845,64817,0,159,130,0,0,28,148.5,ba000311fdd40000034dfd3100009f8200000000 +21.1038,632,187,807,64981,0,866,64818,0,150,142,0,0,28,148.5,bb000327fdd500000362fd320000968e00000000 +21.1358,633,188,826,64981,0,885,64819,0,163,147,0,0,28,148.5,bc00033afdd500000375fd330000a39300000000 +21.1718,634,189,839,64981,0,897,64819,0,158,153,0,0,28,148.6,bd000347fdd500000381fd3300009e9900000000 +21.2038,635,190,849,64981,0,903,64819,0,138,155,0,0,28,148.7,be000351fdd500000387fd3300008a9b00000000 +21.2358,636,191,851,64981,0,905,64819,0,137,153,0,0,28,148.7,bf000353fdd500000389fd330000899900000000 +21.2678,637,192,857,64981,0,910,64819,0,145,157,0,0,28,148.7,c0000359fdd50000038efd330000919d00000000 +21.3038,638,193,865,64981,0,917,64820,0,135,166,0,0,28,148.7,c1000361fdd500000395fd34000087a600000000 +21.3358,639,194,884,64981,0,930,64822,0,137,146,0,0,28,148.6,c2000374fdd5000003a2fd360000899200000000 +21.3678,640,195,901,64982,0,947,64823,0,145,160,0,0,28,148.9,c3000385fdd6000003b3fd37000091a000000000 +21.4038,641,196,918,64983,0,962,64826,0,155,150,0,0,28,149.0,c4000396fdd7000003c2fd3a00009b9600000000 +21.4358,642,197,935,64983,0,979,64826,0,158,145,0,0,28,149.2,c50003a7fdd7000003d3fd3a00009e9100000000 +21.4678,643,198,952,64982,0,993,64827,0,151,162,0,0,28,149.1,c60003b8fdd6000003e1fd3b000097a200000000 +21.5038,644,199,972,64982,0,1013,64827,0,172,146,0,0,28,149.0,c70003ccfdd6000003f5fd3b0000ac9200000000 +21.5359,645,200,993,64982,0,1034,64827,0,168,156,0,0,28,149.2,c80003e1fdd60000040afd3b0000a89c00000000 +21.5678,646,201,1016,64982,0,1055,64828,0,165,163,0,0,28,149.2,c90003f8fdd60000041ffd3c0000a5a300000000 +21.6038,647,202,2044,64982,0,1055,64828,0,165,167,4,4,28,149.4,ca0007fcfdd60000041ffd3c0000a5a704040000 +21.6358,648,203,1060,64982,0,1092,64832,0,166,155,0,0,28,149.7,cb000424fdd600000444fd400000a69b00000000 +21.6678,649,204,1060,64982,0,1092,64836,0,166,159,4,4,28,149.8,cc000424fdd600000444fd440000a69f04040000 +21.7038,650,205,1107,64984,0,1133,64837,0,168,146,0,0,28,149.9,cd000453fdd80000046dfd450000a89200000000 +21.7358,651,206,1127,64985,0,1151,64839,0,155,163,0,0,28,150.0,ce000467fdd90000047ffd4700009ba300000000 +21.7678,652,207,1145,64985,0,1169,64840,0,137,155,0,0,28,150.2,cf000479fdd900000491fd480000899b00000000 +21.8038,653,208,1154,64985,0,1176,64840,0,137,150,0,0,28,150.2,d0000482fdd900000498fd480000899600000000 +21.8358,654,209,1162,64985,0,1183,64841,0,132,151,0,0,28,150.2,d100048afdd90000049ffd490000849700000000 +21.8678,655,210,1168,64987,0,1191,64842,0,140,156,0,0,28,150.2,d2000490fddb000004a7fd4a00008c9c00000000 +21.8998,656,211,1172,64991,0,1191,64846,0,140,156,4,4,28,150.3,d3000494fddf000004a7fd4e00008c9c04040000 +21.9358,657,212,1172,64991,0,1191,64846,0,140,156,4,4,28,150.1,d4000494fddf000004a7fd4e00008c9c04040000 +21.9678,658,213,1222,64992,0,1239,64851,0,165,177,0,0,28,150.0,d50004c6fde0000004d7fd530000a5b100000000 +21.9998,659,214,1248,64994,0,1263,64854,0,152,151,0,0,28,149.7,d60004e0fde2000004effd560000989700000000 +22.0358,660,215,1277,64995,0,1286,64856,0,173,161,0,0,28,149.7,d70004fdfde300000506fd580000ada100000000 +22.0678,661,216,1300,64997,0,1310,64859,0,145,158,0,0,28,149.5,d8000514fde50000051efd5b0000919e00000000 +22.0998,662,217,1321,64999,0,1329,64860,0,151,148,0,0,28,149.6,d9000529fde700000531fd5c0000979400000000 +22.1358,663,218,1340,64999,0,1350,64861,0,141,162,0,0,28,149.8,da00053cfde700000546fd5d00008da200000000 +22.1678,664,219,1360,64999,0,1366,64863,0,158,161,0,0,28,149.9,db000550fde700000556fd5f00009ea100000000 +22.1998,665,220,1376,65001,0,1382,64865,0,170,154,0,0,28,150.1,dc000560fde900000566fd610000aa9a00000000 +22.2358,666,221,1380,65005,0,1382,64869,0,174,158,4,4,28,150.2,dd000564fded00000566fd650000ae9e04040000 +22.2678,667,222,1380,65005,0,1382,64869,0,174,158,4,4,28,150.2,de000564fded00000566fd650000ae9e04040000 +22.2998,668,223,1415,65003,0,1414,64869,0,159,156,0,0,28,150.3,df000587fdeb00000586fd6500009f9c00000000 +22.3358,669,224,1422,65003,0,1421,64869,0,148,152,0,0,28,150.4,e000058efdeb0000058dfd650000949800000000 +22.3679,670,225,1427,65004,0,1427,64869,0,161,152,0,0,28,150.4,e1000593fdec00000593fd650000a19800000000 +22.3998,671,226,1429,65005,0,1430,64870,0,155,142,0,0,28,150.4,e2000595fded00000596fd6600009b8e00000000 +22.4358,672,227,1439,65005,0,1440,64870,0,158,160,0,0,28,150.3,e300059ffded000005a0fd6600009ea000000000 +22.4678,673,228,1455,65007,0,1455,64871,0,162,159,0,0,28,150.3,e40005affdef000005affd670000a29f00000000 +22.4998,674,229,1462,65008,0,1461,64873,0,142,159,0,0,28,150.4,e50005b6fdf0000005b5fd6900008e9f00000000 +22.5318,675,230,1462,65008,0,1461,64873,0,142,152,0,0,28,150.4,e60005b6fdf0000005b5fd6900008e9800000000 +22.5678,676,231,1462,65008,0,1461,64873,0,145,152,0,0,28,150.4,e70005b6fdf0000005b5fd690000919800000000 +22.5998,677,232,1462,65008,0,1461,64873,0,142,153,0,0,28,150.4,e80005b6fdf0000005b5fd6900008e9900000000 +22.6318,678,233,1462,65008,0,1461,64873,0,141,154,0,0,28,150.4,e90005b6fdf0000005b5fd6900008d9a00000000 +22.6679,679,234,1462,65008,0,1461,64873,0,144,157,0,0,28,150.3,ea0005b6fdf0000005b5fd690000909d00000000 +22.6998,680,235,1462,65008,0,1461,64873,0,142,153,0,0,28,150.3,eb0005b6fdf0000005b5fd6900008e9900000000 +22.7318,681,236,1481,65014,0,1487,64876,0,141,152,0,0,28,150.1,ec0005c9fdf6000005cffd6c00008d9800000000 +22.7678,682,237,1519,65031,0,1518,64889,0,161,157,0,0,28,150.0,ed0005effe07000005eefd790000a19d00000000 +22.7998,683,238,1555,65050,0,1542,64901,0,153,143,0,0,28,150.0,ee000613fe1a00000606fd850000998f00000000 +22.8318,684,239,1579,65073,0,1553,64918,0,140,130,0,0,28,150.5,ef00062bfe3100000611fd9600008c8200000000 +22.8678,685,240,1584,65100,0,1557,64949,0,135,131,0,0,28,151.1,f0000630fe4c00000615fdb50000878300000000 +22.8998,686,241,1581,65134,0,1555,64980,0,129,129,0,0,28,151.6,f100062dfe6e00000613fdd40000818100000000 +22.9319,687,242,1577,65152,0,1546,65002,0,126,134,0,0,28,151.7,f2000629fe800000060afdea00007e8600000000 +22.9678,688,243,1559,65184,0,1529,65032,0,121,139,0,0,28,151.0,f3000617fea0000005f9fe080000798b00000000 +22.9999,689,244,1527,65214,0,1504,65059,0,132,136,0,0,28,150.3,f40005f7febe000005e0fe230000848800000000 +23.0318,690,245,1497,65234,0,1475,65074,0,135,133,0,0,28,149.8,f50005d9fed2000005c3fe320000878500000000 +23.0678,691,246,1467,65242,0,1446,65085,0,142,150,0,0,28,149.9,f60005bbfeda000005a6fe3d00008e9600000000 +23.0998,692,247,1426,65243,0,1416,65086,0,135,134,0,0,28,149.9,f7000592fedb00000588fe3e0000878600000000 +23.1318,693,248,1372,65243,0,1374,65088,0,141,142,0,0,28,149.3,f800055cfedb0000055efe4000008d8e00000000 +23.1638,694,249,1311,65242,0,1322,65088,0,126,127,0,0,28,148.9,f900051ffeda0000052afe4000007e7f00000000 +23.1998,695,250,1260,65237,0,1281,65085,0,123,147,0,0,28,149.1,fa0004ecfed500000501fe3d00007b9300000000 +23.2318,696,251,1211,65231,0,1245,65066,0,131,134,0,0,28,149.6,fb0004bbfecf000004ddfe2a0000838600000000 +23.2638,697,252,1169,65225,0,1216,65037,0,125,132,0,0,28,150.0,fc000491fec9000004c0fe0d00007d8400000000 +23.2998,698,253,1125,65207,0,1189,65004,0,127,134,0,0,28,149.8,fd000465feb7000004a5fdec00007f8600000000 +23.3318,699,254,1105,65185,0,1167,64970,0,133,149,0,0,28,149.8,fe000451fea10000048ffdca0000859500000000 +23.3638,700,255,1093,65155,0,1157,64936,0,136,150,0,0,28,149.1,ff000445fe8300000485fda80000889600000000 +23.3998,701,0,1090,65131,0,1154,64900,0,141,160,0,0,28,148.5,00000442fe6b00000482fd8400008da000000000 +23.4318,702,1,1088,65107,0,1154,64879,0,131,153,0,0,28,148.6,01000440fe5300000482fd6f0000839900000000 +23.4638,703,2,1086,65081,0,1158,64846,0,148,153,0,0,28,149.2,0200043efe3900000486fd4e0000949900000000 +23.4998,704,3,1087,65058,0,1164,64823,0,175,130,0,0,28,149.8,0300043ffe220000048cfd370000af8200000000 +23.5318,705,4,1090,65040,0,1172,64802,0,163,135,0,0,28,149.7,04000442fe1000000494fd220000a38700000000 +23.5638,706,5,1096,65023,0,1180,64782,0,153,141,0,0,28,149.9,05000448fdff0000049cfd0e0000998d00000000 +23.5998,707,6,1102,65006,0,1190,64770,0,153,141,0,0,28,149.7,0600044efdee000004a6fd020000998d00000000 +23.6318,708,7,1110,64993,0,1198,64752,0,154,134,0,0,28,149.3,07000456fde1000004aefcf000009a8600000000 +23.6638,709,8,1121,64976,0,1207,64737,0,138,140,0,0,28,148.6,08000461fdd0000004b7fce100008a8c00000000 +23.6958,710,9,1131,64964,0,1225,64721,0,132,133,0,0,28,148.5,0900046bfdc4000004c9fcd10000848500000000 +23.7318,711,10,1145,64945,0,1243,64698,0,126,138,0,0,28,148.6,0a000479fdb1000004dbfcba00007e8a00000000 +23.7638,712,11,1146,64933,0,1261,64676,0,130,137,0,0,28,148.6,0b00047afda5000004edfca40000828900000000 +23.7958,713,12,1162,64918,0,1277,64658,0,155,138,0,0,28,149.2,0c00048afd96000004fdfc9200009b8a00000000 +23.8318,714,13,1181,64900,0,1296,64644,0,159,139,0,0,28,149.3,0d00049dfd8400000510fc8400009f8b00000000 +23.8638,715,14,1204,64883,0,1315,64627,0,165,135,0,0,28,149.7,0e0004b4fd7300000523fc730000a58700000000 +23.8958,716,15,1222,64868,0,1336,64609,0,167,147,0,0,28,149.8,0f0004c6fd6400000538fc610000a79300000000 +23.9318,717,16,1239,64854,0,1355,64596,0,165,158,0,0,28,149.8,100004d7fd560000054bfc540000a59e00000000 +23.9638,718,17,1258,64840,0,1372,64583,0,155,153,0,0,28,149.6,110004eafd480000055cfc4700009b9900000000 +23.9959,719,18,1278,64830,0,1386,64572,0,154,139,0,0,28,149.3,120004fefd3e0000056afc3c00009a8b00000000 +24.0318,720,19,1300,64820,0,1403,64565,0,172,141,0,0,28,149.2,13000514fd340000057bfc350000ac8d00000000 +24.0638,721,20,1328,64811,0,1417,64560,0,145,142,0,0,28,149.2,14000530fd2b00000589fc300000918e00000000 +24.0958,722,21,1353,64806,0,1439,64554,0,134,134,0,0,28,149.2,15000549fd260000059ffc2a0000868600000000 +24.1318,723,22,1379,64805,0,1463,64549,0,149,132,0,0,28,149.5,16000563fd25000005b7fc250000958400000000 +24.1638,724,23,1405,64800,0,1483,64547,0,131,145,0,0,28,149.3,1700057dfd20000005cbfc230000839100000000 +24.1958,725,24,1431,64794,0,1502,64544,0,125,113,0,0,28,149.2,18000597fd1a000005defc2000007d7100000000 +24.2318,726,25,1454,64791,0,1511,64543,0,130,136,0,0,28,149.1,190005aefd17000005e7fc1f0000828800000000 +24.2638,727,26,1475,64788,0,1529,64542,0,133,145,0,0,28,149.2,1a0005c3fd14000005f9fc1e0000859100000000 +24.2958,728,27,1497,64787,0,1546,64541,0,135,137,0,0,28,149.3,1b0005d9fd130000060afc1d0000878900000000 +24.3278,729,28,1517,64787,0,1562,64543,0,130,117,0,0,28,149.6,1c0005edfd130000061afc1f0000827500000000 +24.3638,730,29,1536,64790,0,1579,64544,0,131,127,0,0,28,149.9,1d000600fd160000062bfc200000837f00000000 +24.3958,731,30,1562,64796,0,1602,64551,0,127,135,0,0,28,150.3,1e00061afd1c00000642fc2700007f8700000000 +24.4278,732,31,1596,64808,0,1631,64563,0,123,137,0,0,28,150.8,1f00063cfd280000065ffc3300007b8900000000 +24.4638,733,32,1624,64817,0,1660,64574,0,143,152,0,0,28,150.8,20000658fd310000067cfc3e00008f9800000000 +24.4958,734,33,1652,64827,0,1681,64580,0,141,162,0,0,28,151.1,21000674fd3b00000691fc4400008da200000000 +24.5278,735,34,1671,64835,0,1699,64588,0,148,155,0,0,28,151.1,22000687fd43000006a3fc4c0000949b00000000 +24.5638,736,35,1680,64841,0,1714,64599,0,145,164,0,0,28,151.4,23000690fd49000006b2fc57000091a400000000 +24.5958,737,36,1690,64849,0,1729,64612,0,156,149,0,0,28,151.5,2400069afd51000006c1fc6400009c9500000000 +24.6278,738,37,1701,64861,0,1739,64624,0,165,143,0,0,28,151.4,250006a5fd5d000006cbfc700000a58f00000000 +24.6638,739,38,1715,64875,0,1751,64639,0,162,146,0,0,28,151.2,260006b3fd6b000006d7fc7f0000a29200000000 +24.6958,740,39,1737,64899,0,1769,64659,0,171,136,0,0,28,150.6,270006c9fd83000006e9fc930000ab8800000000 +24.7278,741,40,1758,64921,0,1781,64687,0,163,138,0,0,28,150.1,280006defd99000006f5fcaf0000a38a00000000 +24.7638,742,41,1776,64946,0,1792,64709,0,133,147,0,0,28,149.9,290006f0fdb200000700fcc50000859300000000 +24.7958,743,42,1792,64976,0,1803,64744,0,148,144,0,0,28,149.9,2a000700fdd00000070bfce80000949000000000 +24.8279,744,43,1806,65006,0,1812,64767,0,123,136,0,0,28,150.6,2b00070efdee00000714fcff00007b8800000000 +24.8638,745,44,1815,65039,0,1818,64803,0,163,139,0,0,28,150.7,2c000717fe0f0000071afd230000a38b00000000 +24.8958,746,45,1824,65073,0,1821,64828,0,155,136,0,0,28,150.7,2d000720fe310000071dfd3c00009b8800000000 +24.9278,747,46,1830,65104,0,1821,64854,0,156,142,0,0,28,150.2,2e000726fe500000071dfd5600009c8e00000000 +24.9598,748,47,1830,65133,0,1821,64882,0,151,153,0,0,28,149.9,2f000726fe6d0000071dfd720000979900000000 +24.9958,749,48,1831,65161,0,1821,64912,0,125,156,0,0,28,150.1,30000727fe890000071dfd9000007d9c00000000 +25.0278,750,49,1829,65183,0,1821,64937,0,143,128,0,0,28,150.5,31000725fe9f0000071dfda900008f8000000000 diff --git a/tools/probe.py b/tools/probe.py new file mode 100644 index 0000000..8adfd86 --- /dev/null +++ b/tools/probe.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python3 +"""Record camera frames and HID mouse reports from the Hama myScan side by side. + +Saves every raw MJPEG frame plus a timeline (frames.csv, hid.csv) into an output +directory and prints a once-per-second summary, so we can see how the camera +reacts to buttons, movement and surfaces. +""" +import argparse +import csv +import os +import struct +import sys +import threading +import time + +# libjpeg prints "Corrupt JPEG data" warnings straight to fd 2 for every +# truncated frame; keep Python's stderr but silence the C-level noise. +sys.stderr = os.fdopen(os.dup(2), "w") +os.dup2(os.open(os.devnull, os.O_WRONLY), 2) + +import cv2 # noqa: E402 +import numpy as np # noqa: E402 + + +def hid_reader(path, out, stop, t0): + # Report layout (from the report descriptor): 1 byte buttons (5 bits), + # int16 X, int16 Y, int8 wheel, int8 AC pan. + fd = os.open(path, os.O_RDONLY | os.O_NONBLOCK) + while not stop.is_set(): + try: + r = os.read(fd, 64) + except BlockingIOError: + time.sleep(0.002) + continue + if len(r) >= 7: + b, x, y, w, p = struct.unpack("= 1.0: + new = hid[hid_seen:] + hid_seen = len(hid) + btn = sorted({h[1] for h in new}) + dx, dy = sum(h[2] for h in new), sum(h[3] for h in new) + recent = [fr for fr in frames if fr[0] > t - 1.0] + print(f"t={t:5.1f}s fps={len(recent):2d} size={np.mean([r[2] for r in recent]):7.0f}B " + f"rows={np.mean([r[3] for r in recent]):5.0f} bright={np.mean([r[4] for r in recent]):6.1f} " + f"hid={len(new):3d} buttons={btn} dx={dx:+d} dy={dy:+d}", flush=True) + last_print = t + + stop.set() + th.join() + cap.release() + with open(f"{a.out}/frames.csv", "w", newline="") as fh: + w = csv.writer(fh) + w.writerow(["t", "frame", "bytes", "decoded_rows", "brightness"]) + w.writerows(frames) + with open(f"{a.out}/hid.csv", "w", newline="") as fh: + w = csv.writer(fh) + w.writerow(["t", "buttons", "dx", "dy", "wheel", "pan", "raw"]) + w.writerows(hid) + print(f"done: {n} frames, {len(hid)} HID reports -> {a.out}/") + + +if __name__ == "__main__": + main() diff --git a/tools/record.py b/tools/record.py new file mode 100644 index 0000000..2eca294 --- /dev/null +++ b/tools/record.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python3 +"""Record a scan session: scan mode + auto-exposure, saving every frame (fixed +640x296 grayscale PNG) and its embedded sensor record to records.csv. +Mouse mode is restored on exit (also on Ctrl-C).""" +import argparse +import csv +import os +import sys +import time + +sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) +sys.stderr = os.fdopen(os.dup(2), "w") +os.dup2(os.open(os.devnull, os.O_WRONLY), 2) # silence libjpeg warnings + +import cv2 # noqa: E402 +import numpy as np # noqa: E402 + +from myscan.frame import fix_and_extract # noqa: E402 +from myscan.xu import XU # noqa: E402 + + +def main(): + ap = argparse.ArgumentParser() + ap.add_argument("-d", "--duration", type=float, default=20) + ap.add_argument("-o", "--out", default="scan") + ap.add_argument("--video", default="/dev/video4") + ap.add_argument("--target", type=float, default=150, help="auto-exposure target mean brightness") + ap.add_argument("--exposure", type=int, help="fixed E6 value instead of auto-exposure") + a = ap.parse_args() + os.makedirs(a.out, exist_ok=True) + + x = XU(a.video) + e6 = a.exposure or 40 + rows = [] + try: + if not (x.disable_mouse()[0] and x.mcu_write_verified(0x2D, 0x60) + and x.mcu_write_verified(0xE7, 0x28) and x.mcu_write_verified(0xE6, e6) + and x.start_scan()[0]): + sys.exit("failed to enter scan mode") + cap = cv2.VideoCapture(a.video, cv2.CAP_V4L2) + cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc(*"MJPG")) + cap.set(cv2.CAP_PROP_CONVERT_RGB, 0) + print(f"scanning for {a.duration:.0f}s -> {a.out}/ (Ctrl-C to stop early)", flush=True) + t0, n, last = time.time(), 0, 0.0 + while time.time() - t0 < a.duration: + ok, f = cap.read() + if not ok: + continue + t = time.time() - t0 + clean, rec = fix_and_extract(f.tobytes()) + im = cv2.imdecode(np.frombuffer(clean, np.uint8), cv2.IMREAD_GRAYSCALE) + if im is None or rec is None: + continue + cv2.imwrite(f"{a.out}/f{n:05d}.png", im) + mean = float(im.mean()) + rows.append([round(t, 4), n, rec.counter, *rec.v, rec.b14, rec.b15, rec.b16, rec.flags, e6, + round(mean, 1), rec.raw.hex()]) + # Proportional auto-exposure every 3 frames (brightness is ~linear in E6). + if a.exposure is None and n % 3 == 0 and mean > 1: + new = int(min(255, max(2, round(e6 * (a.target / mean) ** 0.7)))) + if new != e6 and x.mcu_write_verified(0xE6, new): + e6 = new + if t - last >= 1: + print(f"t={t:5.1f}s frames={n:4d} E6={e6:3d} bright={mean:5.1f} " + f"l1=({rec.v[0]:#06x},{rec.v[1]:#06x}) l2=({rec.v[3]:#06x},{rec.v[4]:#06x}) " + f"sq={rec.b14},{rec.b15} flags={rec.flags:05b}", flush=True) + last = t + n += 1 + cap.release() + except KeyboardInterrupt: + pass + finally: + print("restore mouse mode:", x.enable_mouse()[0]) + with open(f"{a.out}/records.csv", "w", newline="") as fh: + w = csv.writer(fh) + w.writerow(["t", "frame", "counter", "v0", "v1", "v2", "v3", "v4", "v5", + "b14", "b15", "b16", "flags", "e6", "brightness", "raw"]) + w.writerows(rows) + print(f"{len(rows)} frames saved to {a.out}/") + + +if __name__ == "__main__": + main() diff --git a/tools/scantest.py b/tools/scantest.py new file mode 100644 index 0000000..5e4ca85 --- /dev/null +++ b/tools/scantest.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 +"""Put the myScan into scan mode the way Hal::OVMouseManager::startVideo does, +grab a few seconds of frames, decode the embedded sensor records, then restore +mouse mode.""" +import argparse +import os +import sys +import time + +sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) +sys.stderr = os.fdopen(os.dup(2), "w") +os.dup2(os.open(os.devnull, os.O_WRONLY), 2) # silence libjpeg warnings + +import cv2 # noqa: E402 +import numpy as np # noqa: E402 + +from myscan.frame import fix_and_extract # noqa: E402 +from myscan.xu import XU # noqa: E402 + + +def main(): + ap = argparse.ArgumentParser() + ap.add_argument("-d", "--duration", type=float, default=4) + ap.add_argument("-o", "--out", default="scantest") + ap.add_argument("--video", default="/dev/video4") + a = ap.parse_args() + os.makedirs(a.out, exist_ok=True) + + x = XU(a.video) + print("D8 before:", hex(x.bridge_read(0xD8))) + try: + print("disable mouse:", x.disable_mouse()) + print("2D=60:", x.mcu_write_verified(0x2D, 0x60), " E7=28:", x.mcu_write_verified(0xE7, 0x28)) + print("start scan:", x.start_scan()) + + cap = cv2.VideoCapture(a.video, cv2.CAP_V4L2) + cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc(*"MJPG")) + cap.set(cv2.CAP_PROP_CONVERT_RGB, 0) + t0, n = time.time(), 0 + while time.time() - t0 < a.duration: + ok, f = cap.read() + if not ok: + continue + raw = f.tobytes() + clean, rec = fix_and_extract(raw) + im = cv2.imdecode(np.frombuffer(clean, np.uint8), cv2.IMREAD_GRAYSCALE) + cv2.imwrite(f"{a.out}/f{n:04d}.png", im) + if n % 10 == 0: + r = rec.raw.hex() if rec else None + print(f"#{n:3d} {len(raw):6d}B bright={im.mean():6.1f} rec={r}", flush=True) + n += 1 + cap.release() + print(f"{n} frames -> {a.out}/") + finally: + print("enable mouse:", x.enable_mouse()) + print("D8 after:", hex(x.bridge_read(0xD8))) + + +if __name__ == "__main__": + main() diff --git a/tools/stitch.py b/tools/stitch.py new file mode 100644 index 0000000..02a6778 --- /dev/null +++ b/tools/stitch.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +"""Stitch a directory recorded by tools/record.py into a page image.""" +import argparse +import csv +import os +import sys + +sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) + +import cv2 # noqa: E402 +import numpy as np # noqa: E402 + +from myscan.stitch import ROT_GAIN, Stitcher, laser_deltas # noqa: E402 + + +def main(): + ap = argparse.ArgumentParser() + ap.add_argument("dir") + ap.add_argument("-o", "--out", help="output image (default /page.png)") + ap.add_argument("--no-refine", action="store_true", help="lasers only, no image registration") + ap.add_argument("--rot-gain", type=float, default=ROT_GAIN) + ap.add_argument("--start", type=int, default=0) + ap.add_argument("--end", type=int) + a = ap.parse_args() + + rows = list(csv.DictReader(open(os.path.join(a.dir, "records.csv")))) + v = np.array([[int(r[f"v{k}"]) for k in range(6)] for r in rows]) + deltas = laser_deltas(v) + frames = [int(r["frame"]) for r in rows] + rng = range(a.start, a.end if a.end is not None else len(rows)) + + st = None + for i in rng: + im = cv2.imread(os.path.join(a.dir, f"f{frames[i]:05d}.png"), cv2.IMREAD_GRAYSCALE) + if st is None: + st = Stitcher(im.shape, refine=not a.no_refine, rot_gain=a.rot_gain) + st.add(im, deltas[i]) + out = a.out or os.path.join(a.dir, "page.png") + cv2.imwrite(out, st.image()) + print(f"{st.n} frames -> {out} {st.stats}") + + +if __name__ == "__main__": + main()