Fujimod
Try your camera firmware in the browser and build Fujimod SD boot files.
A self-measuring robotic observatory for affordable GoTo mounts, starting with Sky-Watcher SynScan: it measures and corrects its own faults, runs whole nights, hunts for new objects and tells you what
LifestylePython · JavaScriptGitHub @Ryan-ClintonOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
A self-measuring robotic observatory for affordable GoTo mounts, Sky-Watcher SynScan first, run by a person and an AI agent: whole nights, self-calibration, nova and comet searches, transit photometry, alerts by phone and house, a live science broadcast. AI proposes; code decides what is safe; a person authorises moves.
Taken from the repository README
Loading the full README.
Read the full README ↗A self-measuring robotic observatory for affordable GoTo mounts, run by a person and an AI agent together.
The demo, recorded by ./tour.py. These are not mocked-up screens: a
complete pretend rig is built in (mount, camera, sky, focuser, cloud, drift),
and the real centring, focusing and stacking code runs on it.
Home page · Download 0.2.1 · Try the demo, no telescope needed · What it has been tried on · Ask a question
telescopeyoke takes the kind of GoTo mount many amateur astronomers already own, starting with Sky-Watcher's SynScan family, and adds what much bigger systems do. It runs on the author's EQ3 Pro today; other mounts are welcome, and Mounts says which are planned and how to ask for yours. It measures its own pointing, polar alignment, focus, drift, camera timing and image depth, and corrects for them; plans and runs a whole night; searches its pictures and public data for new objects, and follows them up; tells you when something matters, on your phone or through the house's lights and speaker; and can explain the night live to an audience. An AI agent can operate it beside you, by one rule: AI proposes; deterministic code decides what is safe; a person authorises movement.
It is built around one idea: measure the imperfections of modest equipment instead of pretending they are not there. A home position set by eye, a manual focuser, a rough polar alignment, a garden with a house in the way: each is measured, and then corrected or planned around. The laptop stays with the telescope; the observatory comes back to you through the status page, your phone, the house and, if you want, a live broadcast.
It is an Ubuntu application. It also runs natively on Windows, where the tests and the demo pass and the camera has taken frames, but no mount has been driven yet.
New to motorised telescopes? Start with From box to first galaxy: a first night, step by step, with no knowledge of polar alignment, RA and Dec, plate solving or astrophotography assumed. Then What telescopeyoke just did for you explains it.
Looking for testers. So far it has run on one telescope: the author's EQ3 Pro. If you have an EQ3, EQ5, HEQ5 or EQ6 with a SynScan handset, on Linux or Windows, a report from you is worth more than any new feature. You do not have to let it move your mount:
./doctor.py --reportonly looks at what is connected, and what it writes is useful by itself. See Hardware.
OBSERVE plan → schedule → GoTo and centre → focus → image → hold → park
MEASURE pointing · polar error · true home · drift · focus · shutter · depth · precision
DISCOVER patrol → subtract → vet → second look → follow night to night → report
OPERATE cloud and rain · stopped runs · alerts by phone, lights and speaker
REASON JSON for every command · ty · MCP · skills and specialist agents · a person approves moves
SHARE status page · public live page · Twitch broadcast with a presenter and chat
The TelescopeYoke observatory puts the whole system on one page. What happened in its first 17 days, each recorded in the validation record:
| Live broadcast | Discovery | Science |
|---|---|---|
The Twitch broadcast's comet-hunt card, as ./stream.py preview draws it (the side panel's figures are its examples) | A known Kreutz sungrazer the hunt found by itself in GOES-19 coronagraph pictures, 19 October 2026 | The first exoplanet transit attempt, WASP-12 b, 20 October 2026: dimming seen, not a detection |
Why it is unusual. No one of these functions is new. What is unusual is that one inexpensive amateur rig combines measuring its own faults, robotic observing, searches for new objects with their vetting, AI-agent workflows, alerts in the house and a public science broadcast, and keeps physical safety outside the language model.
Most telescope software assumes a mount that has been set up carefully.
This assumes it has not, and measures what is actually there.
./ty characterise lists what a rig has had measured about itself so far
(the camera's real exposure, the pointing error on each side, the polar
error and how well it repeats, how the drift answers the motor, the skyline)
and the command for each thing it has not; in the application that is the
Rig knowledge screen, with a "Measure this" button on each line. Each of these
says how far it has been proven; Current status has the
table and docs/validation.md the full account.
Home position set by eye? It photographs the sky, plate-solves where the telescope really ended up, corrects, and repeats until the target is in the middle. On its first real night an EQ3 that started about ten degrees out centred M27 in three corrections: 107′ off, then 6′, then 1′ (the run is below). And after a polar alignment it can measure where home really is, from plate solves either side of the meridian, drive there, and have you mark the joints, so that home is two marks lined up from then on (Calibrating a mount). Both are proven under real stars: finding home took an EQ3 set 10.66° out by eye to 0.19° on the marks (17 October 2026).
Manual focuser? Keep your hand on it and listen. Every frame, once it has been measured, gives a click: your last turn has been seen. A tone follows, higher the sharper the stars. It starts on quick binned frames, moves by itself to many stars and then to the full sensor as focus nears, tells you when you have passed the minimum, and says "Focus good. Hold" only when the readings have come back and stayed on it (Focusing by ear). The star measurement has brought the real telescope to focus; the click, tone and levels have run only on made-up stars.
House and trees hiding half the garden? Teach it your garden. Give it a
phone panorama and it finds the line between sky and everything else, lets
you redraw the line where it is wrong, and ties the picture to the compass
from two known bearings. The planner then stops recommending targets you
cannot see from that spot. Set up in more than one place? Each spot in the
garden keeps its own skyline and blocked directions: ./ty spot NAME says
where the telescope stands tonight. The telescope can check and refine that skyline
itself, looking only where the line bends. Proven by day: on 17 October 2026 the
telescope's own survey measured the author's garden in 34 minutes, one look
checked by eye. The skyline has been found in real panoramas too; tying one
to the compass is not yet proven.
Setting up before dark? An experimental mode looks for Polaris by day,
when nothing else near the pole would show. polaris.py checks whether the sky, the Sun
and the focus make it worth trying, searches round the home position, proves
a candidate is a star by tipping the tube and seeing it move as the sky
should, then turns the RA axis with the star in view to find where the axis
really points and which way to move each bolt. landmark.py remembers a
distant chimney or aerial from a night when the mount was aligned, and by day
turns back to it to recover the azimuth. Experimental: the search has run
on the real mount three times and has not yet found Polaris, though it did
tell sensor blemishes from stars; the rest has run only on the simulated
mount.
Polar alignment still rough? By night, three plate solves measure how far the axis is from the pole and say which way to turn each adjuster: on the real mount, six rounds took it from 3.3° to 0.1° (13 October 2026). That is with the measurement corrected for the handset moving the Dec axis during it; the figures from 6 October were wrong for that reason (validation). It says what to do with your hands, with a drawing of each bolt: which to turn in, about how far, or to turn the whole tripod and by how many centimetres at the feet; and it learns what a turn of your mount's bolts does from what you tell it you turned. Or leave it rough: telescopeyoke works out the declination drift that error causes, predicts it for any other part of the sky, and creeps the Dec motor against it as a guider would, correcting part of the error at a time and never reversing the motor for a small overshoot (how). The measuring is proven on the real mount. An earlier, cruder drift correction cancelled most of the drift there; the model that predicts it across the sky has run only on the simulated mount.
Mount says it moved? That is not proof. On one real night the motors stopped with no sound while the handset went on reporting every move as made, and a survey stared at one tree for half an hour. Now a GoTo whose correction changes nothing, a polar measurement whose plate solves come back the same, and a survey whose pictures do not change all stop with "the mount is not moving" instead of carrying on. Written because it happened; tried on made-up frames, and by day it only records what it sees, after its first real frames nearly fooled it.
Tracking not good enough for long exposures? It works out the longest exposure the mount can hold, takes many short ones, checks each as it arrives, re-centres when the target wanders, and stacks the good ones. The live picture is never the last word: every raw frame is kept, and the quality pass goes back over them afterwards to choose, weight and clip, so a better method next month can remake last night's picture (How a picture is made). Real runs of 48 and 207 frames have been stacked; the newest fixes have not yet been through a real run.
Observe
schedule.py): the agreed steps in order, not before their times, gaps filled with more of the last field; a failed step stops the rest and fetches you; at the end it parks the mount and tells yougoto M27 --solve)shoot.py --hold), learning how the mount answers firstMeasure
precision.py)Discover (the discovery station)
Operate (alerts)
Reason (for programs and AI agents)
ty is one front door, the web API and MCP server are read-only, and skills and specialist agents hold the routines (see below)Share (streaming)
AI proposes; deterministic code decides what is safe; a person authorises movement. An AI agent (the author uses Claude) can read everything the observatory knows, plan a night, investigate a candidate and propose the next step: fourteen skills hold the routines (setup at the telescope, focusing, polar alignment, a discovery night, a claim, a comet report) and eight specialist agents do focused jobs (vetting a candidate, researching a place in catalogues, checking a comet track, reviewing a change that moves the mount, analysing a night). What may move is decided by code that does not depend on the agent: the Sun and altitude limits, the motion lock, the handset's state, dry runs, moves checked against the camera, and hooks that refuse any attempt to work round them. The MCP server and web API are read-only, every agent action is logged, and a person asks for each move. AGENTS.md and the observatory page have the detail.
It learns from real nights: a comet split in two by a gap in the pictures became joined tracks and a checked verdict by noon the next day; a transit that could not be measured became a precision diagnosis, a hold on the same pixels and a recipe the same day (the records).
Planned. This is what ./serve.py shows, to watch from indoors:
tonight's verdict and the targets worth pointing at, ranked for your sky.
Found. A real run from the first night. The mount's home position had been set by eye and was about ten degrees out; the telescope photographed the sky, worked out where it really was, and corrected itself:
$ ./mount.py goto M27 --solve
M27 Dumbbell Nebula: altitude 57°, hour angle +0.36 h (west: a swing over the pole if it is on the east side now)
off by -107.0' in hour angle, +94.4' in Dec
off by -6.2' in hour angle, -11.4' in Dec
off by +1.0' in hour angle, -1.8' in Dec
centred
Captured. The Dumbbell Nebula (M27) from that night: 48 two-second exposures through a 150 mm Newtonian on an EQ3 mount that was polar aligned by eye.
Try the demo: no telescope, nothing to set up, nothing that can move.
On Ubuntu or Debian:
wget https://github.com/Ryan-Clinton/telescopeyoke/releases/download/v0.2.1/TelescopeYoke-v0.2.1.zip
unzip TelescopeYoke-v0.2.1.zip
cd TelescopeYoke-v0.2.1
./install.sh --demo # the Python libraries, then TelescopeYoke (demo) opens
On Windows 10 or 11: install 64-bit Python 3.11 or newer from python.org
(tick "Add python.exe to PATH"),
download TelescopeYoke-v0.2.1.zip,
unpack it, and double-click try-demo.cmd.
That zip is release 0.2.1, which stays as it is; a hardware report says
which release made it. git clone https://github.com/Ryan-Clinton/telescopeyoke
gets the newest code instead, which changes from day to day.
Either way a window opens on a pretend mount, camera and sky. It is in the applications menu (or Start Menu) from then on as TelescopeYoke (demo).
The same from a terminal, a piece at a time:
./doctor.py # what is installed and connected
./tonight.py --demo # tonight's report with made-up weather
./serve.py --demo # the web page, on http://localhost:8080
./mount.py --demo goto M27 # drive a simulated mount
On Windows these are written python doctor.py, python tonight.py --demo
and so on, and .\ty.cmd (or python ty) for ./ty. See
Setup.
The application. ./install.sh puts TelescopeYoke and TelescopeYoke
(demo) in the applications menu (on Windows, install.ps1 puts them in the
Start Menu). It opens in a window of its own, with no terminal and no
browser: Tonight, Targets, Imaging, Focus and Mount for observing; Camera,
Telescope, Plate solver and Webcam for setting the equipment up; the tools;
and the doctor, settings and logs. The first time, it opens on what is ready
and what still needs doing. With a real mount, every move is checked with a
dry run and shown as a plan, and nothing moves until you confirm it. From a
terminal it is ./app.py, or ./app.py --demo. It has not yet been used
with a real mount or camera.
The demo is the whole thing on a pretend telescope. TelescopeYoke (demo)
has a pretend mount, camera and sky: a GoTo is centred by plate solving,
the focusing aid follows a focuser you turn with a button, and an imaging
run stacks frames and rejects the ones taken after you bring the cloud
over. It runs the same commands as a real night and keeps its files in a
folder of its own (demo/), apart from anything real.
Use the planner for real (still no telescope needed): put your location
in config.toml, then ./tonight.py.
Run a telescope: ./install.sh, then follow Setup.
The application is the front door. Underneath it is a set of scripts, each of which can be run by itself from a terminal, and the planner needs no telescope at all.
| For | Scripts | Needs |
|---|---|---|
| Planning the night | tonight.py, serve.py, clouds.py | Any computer with Python. No telescope. |
| Setting up | doctor.py, camera_setup.py, polaralign.py, polaris.py, landmark.py | The mount and camera. |
| Measuring the site | panorama.py, horizon.py | A phone panorama; for the telescope's own survey, the mount and camera. |
| Moving the mount | mount.py, watch.py | A SynScan mount and its serial lead. |
| Focusing | focus.py | A supported camera. |
| Imaging | snap.py, solve.py, shoot.py, planet.py, mosaic.py, liveview.py, skywatch.py | A supported camera and ASTAP. On Linux the camera is read through INDI; on Windows through Altair's own library, which is the only camera route there. |
| Processing | restack.py, calibrate.py, process.py, compare.py | The raw frames a run kept. |
Start with the planner; add hardware when you have it. Commands says what each script does.
Other mounts are wanted. It has driven one mount, a Sky-Watcher EQ3 Pro; nothing in it is meant to stay Sky-Watcher-only. Mounts says which mounts can be reached and how, in the order planned: the rest of Sky-Watcher's family through the motor board's own protocol, Celestron's NexStar hand controllers, any mount INDI drives on Linux, any ASCOM or Alpaca driver through Alpaca, and ZWO's AM mounts natively. Ask for yours, or offer to test one, with a Mount support request: the first step only reads, and nothing moves until you choose it.
Tried so far. One row for each set of equipment someone has run it on. There is one, and it is the author's:
| Mount | Handset | Camera | System | What has worked | Tried by |
|---|---|---|---|---|---|
| Sky-Watcher EQ3 Pro | SynScan, firmware 3.35, FTDI serial lead | Altair Hypercam 183C (on USB 2 on 3 and 4 October, on a 3 m USB 3 lead from 6 October) | Ubuntu 26.04, Python 3.14 | ✅ Planner, mount moves, camera, focusing, plate solving, GoTo with centring, under real stars | the author |
| the same EQ3 Pro | the same | the same Hypercam 183C | Windows 11 | ⚠️ The demo, and camera frames indoors. No mount driven, no star seen | the author |
| EQ5 | SynScan | any | Linux or Windows | ❔ wanted | could be you |
| HEQ5 | SynScan | any | Linux or Windows | ❔ wanted | could be you |
| EQ6, EQ6-R | SynScan | any | Linux or Windows | ❔ wanted | could be you |
| any of them | SynScan | another INDI camera | Linux | ❔ wanted | could be you |
| a mount converted with an EQStar or other EQMOD-style controller | none | any | Linux or Windows | ❔ wanted: whether it answers at all | could be you |
To add a row, working or not:
./doctor.py --report (on Windows python doctor.py --report), or
press "Write a hardware report" on the application's Doctor screen. It
writes out what the computer is, what the handset says the mount is and
its firmware version, and every check. It only looks: nothing is moved,
and your location is left out.You can stop there. If you want to go further without the mount turning,
./mount.py status reads its position and ./mount.py goto M27 --dry-run
checks a move against the limits and says what it would do, without making
it. For a question first ("will it work with my HEQ5?"), ask in
Discussions.
A controller that is not a SynScan handset (an EQDIR lead, an Astro-Gadget EQStar, anything normally driven through EQMOD): nobody knows yet whether telescopeyoke can talk to yours, and it does not guess. Name the port and it will ask:
./doctor.py --report --probe COM7 # Linux: --probe /dev/ttyUSB0
It asks that one port what it is, first as a SynScan handset and then as a Sky-Watcher motor board (the language EQMOD speaks), at 9600 and at 115200 baud, and writes what answered into the report along with the adapter's make and USB numbers. Every question only reads; nothing is told to move. Close EQMOD first, since only one program can have the port.
Keep them. telescopeyoke does not use ASCOM and changes nothing about an ASCOM or EQMOD installation: it talks to the mount's COM port itself. The one rule is that two programs cannot hold the same port at once, so close EQMOD (or NINA, or the SynScan app) before starting telescopeyoke on that mount, and the other way round. On Windows the doctor says so when it sees ASCOM or EQMOD installed. What it adds to a setup that already works is the part in between: targets ranked for your own horizon, a GoTo that plate-solves and centres itself, focusing by ear, and short exposures checked and stacked as they arrive.
A rig is one telescope with its own settings and its own files. With two out on a good night, each runs in a window of its own:
./app.py --new-rig heq5 # its settings (rigs/heq5.toml) and "TelescopeYoke (heq5)" in the menu
./app.py --rig heq5 # open it; set it up on its Settings screen
./ty --rig heq5 mount status # any command, for that rig
./ty rigs # every rig in one view: what each is and what it is imaging
Each rig keeps its own frames, pictures, calibration frames and remembered
measurements under rigs/heq5/, so two nights' work never mix. The
application's Rigs screen shows what every rig is doing. With no rig named
everything is as before: config.toml and the folders beside it. The motion
lock (MOTION_LOCKED) stops every rig at once.
What to expect of other equipment:
| Hardware | Status |
|---|---|
| Sky-Watcher Explorer 150P (150 mm f/5 Newtonian) | ✅ Tested by the author |
| Python 3.11, 3.12, 3.13, 3.14 | ✅ Tests pass in CI on Ubuntu and Windows (no hardware) |
| Windows 10 and 11 | ⚠️ Tests and the demo pass with no hardware. The camera has taken frames on Windows 11, indoors. No mount has been driven from Windows, and camera and mount have not been used together there. |
| SynScan Wi-Fi adapter, or an EQDIR lead (no handset) | ⚠️ The adapter has been found and read on a real EQ3 (firmware, gearing, position). No mount has been moved through it yet; the EQDIR lead is untried. |
| EQ5, HEQ5, EQ6 with a SynScan handset | ⚠️ Untested. Likely: same serial protocol. |
| Other INDI cameras | ⚠️ Untested. Likely for mono or RGGB colour sensors: set the driver and sensor size in config.toml. |
| Other telescopes | Set the focal length in config.toml. |
| ASCOM, Alpaca | ❌ Not used yet. An ASCOM or EQMOD installation is noticed and left alone; see above. Alpaca is planned as the way to every ASCOM driver (Mounts). |
| Celestron, ZWO, iOptron and others | 🗓️ Planned, in order, in Mounts; testers wanted. |
| Script | What it does |
|---|---|
tonight.py | Report for the night: darkness, Moon, weather verdict, ranked targets. --html writes the web page. |
serve.py | Serves the status page on port 8080: the night's report rebuilt every 10 minutes, and the imaging run, pictures and system panel refreshed every two seconds. --install runs it as a systemd user service (ty-serve) that starts at boot and restarts if it stops. |
clouds.py | Fetches the latest infrared satellite image with the site marked on it. |
mount.py | Moves the mount: status, settime (gives the handset this computer's date, time and the site's position, once you have pressed ENTER through its start-up screens), nudge (from home, tips the tube 5° and back, for someone beside it to see that it turns), home, zenith, goto NAME [--solve], point AZ ALT, sync, drift, compensate, stop. response sets the Dec motor creeping at five rates and measures the drift at each, to see whether the sky answers in proportion; pointing plate-solves at eight hour angles, four each side of the meridian, to see whether one correction for each side is enough. findhome works out from one run of plate solves where the home position really is, and truehome drives there for the joints to be marked. |
liveview.py | Takes a frame every few seconds so the status page shows what the telescope sees now. Steps aside while shoot.py runs. |
app.py | TelescopeYoke, the application: one window with everything in it, started from the applications menu. ./app.py --demo tries it with nothing plugged in. |
console.py | The same observing screens as a page in a browser, without the equipment set-up and tools: the companion to the application. This computer only. |
polaralign.py | Measures how far the polar axis is from the pole by plate solving at three positions, and says which way to turn each adjuster. --repeat 5 measures five times with the bolts left alone and gives the scatter. After each measurement it says which bolt to turn and about how far, or to turn the whole tripod; tells it what you turned since the last one, and it learns what a turn does on this mount. compares the answer with the last one from the other side of the meridian and gives no advice when they disagree. |
tonight.py, serve.py and mount.py accept --demo.
The status page shows two pictures while imaging: Now, the newest single exposure straight from the camera, and Live stack, everything added up so far.
It is built from plain, focused Python files rather than a framework, each
named for what it does (mount.py, focus.py, solve.py, shoot.py,
patrol.py). Most things have one obvious file, so a person can follow a
command from the screen to the hardware, and it means to stay that way.
plan the night → GoTo → photograph → plate-solve (ASTAP) → correct → photograph … → stack
sky.py does the astronomy locally with astropy: positions, darkness,
moonlight (Krisciunas & Schaefer's model), and a score for each target.feeds.py fetches weather, seeing, light pollution and comets, and
caches them so the report still works when the Wi-Fi drops.mount.py speaks the SynScan handset's serial protocol. It measures how
wrong the handset's clock is and corrects every GoTo for it, steers by the
raw axis angles where the handset's own GoTo is unreliable, and stores the
pointing error found by plate solving.indi.py is a small INDI client written for this project: about 150
lines that read and set properties and receive image BLOBs over the XML
protocol, with no dependencies. The stock INDI command-line tools cannot
address a device whose name contains a dot, which this camera's does.camera.py wraps that into "give me a frame", and recovers when the
driver stalls.stacking.py is the imaging pipeline's working parts, used live by
shoot.py and again afterwards by restack.py.tracking.py predicts the drift a misaligned polar axis causes and
decides how to trim the Dec motor against it.moved.py judges whether the telescope really turned, from two
pictures or two plate solves, for anything that makes many moves unattended.page.py lays the report out as the status page. It is plain HTML
with a little JavaScript reading status.json: no framework, no controls.simulator.py is a pretend handset and mount behind --demo and the
tests.| Page | What is in it |
|---|---|
| From box to first galaxy | A first night for someone who knows none of the words: assemble, point north, switch on, align, focus, GoTo, picture. Says which steps are proven on a real mount. |
| What telescopeyoke just did for you | The astronomy and the engineering behind each of those steps, for afterwards. |
| Calibrating a mount, in order | Level roughly, polar-align, find the true home and mark it, measure the pointing each side, and check that the mount really turns. |
| The TelescopeYoke observatory | The whole system on one page: observe, measure, discover, operate, reason, share; the safety rule; what it has learnt from real nights. |
| Looking for something new | The discovery station: novae in M31 and M33, comets in spacecraft pictures, transits, public alerts; its recent proofs. Operational; no find of its own yet. |
| The live stream | Running the Twitch broadcast: what viewers see, the presenter, the chat's commands. The design record says why it looks as it does. |
| Alerts | The house's lights and speaker, the phone, and calling the person. |
| How a picture is made |
For programs and AI agents, every command answers in one JSON shape with
--json, anything that moves the mount can be checked first with --dry-run,
and a read-only web API and MCP server offer the same information. Start with
AGENTS.md.
./mount.py nudge while you stand beside it: the tube tips 5° and comes
back. Then ./mount.py zenith to measure the handset's clock../focus.py --scene on something distant in daylight, then
./focus.py on stars. Turn the focuser and listen for the tone to rise: go on
until "Minimum passed", come back, and stop at "Focus good. Hold"../mount.py sync on any patch of stars, so later GoTos allow for the home
position having been set by eye../mount.py goto M27 --solve, then ./shoot.py M27 --frames 48 --recentre 8.Commands that move the mount need serial access; until you have logged out
and back in after joining the dialout group, prefix them with
sudo -u $USER -g dialout.
The laptop cannot see what the telescope is about to hit.
mount.py refuses to run if the handset's date is still
its default.MOUNT_NOT_MOVING. Then watch one small move yourself: if the tube does
not turn, switch the mount off and on.mount.py point refuses within 40° of it
while it is up; goto only knows night-sky objects.MOTION_LOCKED in this folder blocks all movement.mount.py will not go below 20° altitude or more than 5.75 hours from the
meridian.serve.py) is read-only on purpose. It is served to
the whole home network with no login, which is fine for pictures and
reports. Nothing that moves the mount will be added to it.console.py) answers this computer only. It
needs a key made each time it starts, shows every move as a plan first,
and moves the mount only when that plan is confirmed.What has been proven, and where. "Real stars" means on the author's EQ3 Pro, 150P and Hypercam 183C on Ubuntu, at night; "real equipment" means the hardware answered or moved but no star was involved. Everything is also run against the simulated rig and the tests.
| Feature | Under real stars | On real equipment | Simulated rig and tests |
|---|---|---|---|
| Night report and status page | ✅ | ✅ | ✅ |
Mount moves through the handset, sync | ✅ | ✅ | ✅ |
| GoTo centred by plate solving | ✅ to about an arcminute, from 10° out | ✅ | ✅ |
| Polar alignment from three plate solves | ✅ 3.3° to 0.1° over six rounds, with the corrected measurement; how well it repeats is not known | ✅ | ✅ |
| Imaging and stacking | ⚠️ runs of 48 and 207 frames; the newest fixes not yet rerun | ✅ | ✅ |
| Focusing: measuring the stars | ✅ the reading fell from 11 to about 2 as focus was reached | ✅ | ✅ |
| Focusing: click, tone and three levels | not yet | not yet | ✅ |
Motor focuser (focus.py --motor) | ✅ focused the 150P to HFR 2.47 (15 October 2026), always finishing inward because of the gears' slack | ✅ | ✅ |
Shutter time corrected (camera.py) | ✅ a 10 s frame's trails show 9.6 s open, from 6.4 s | ✅ | ✅ |
| Drift model and Dec correction | ⚠️ an earlier, cruder version only | ⚠️ the same | ✅ |
| Daytime Polaris | ⚠️ searched three times, Polaris not found | ⚠️ the search only | ✅ |
| Skyline from a phone panorama | does not need them | ⚠️ three real panoramas; not yet tied to the compass | ✅ |
| The telescope's own skyline survey | ⚠️ by night, a torch trial only | ✅ by day, 17 October 2026: 28 bearings in 34 minutes, a look at the hedge checked by eye | ✅ |
| Azimuth from a landmark | not yet | not yet | ✅ |
| Control without the handset (Wi-Fi, EQDIR) | not yet | ⚠️ the Wi-Fi adapter read; nothing moved | ✅ |
| The application window | not yet | ⚠️ opened on Ubuntu, in the demo only | ✅ |
| Windows |
docs/validation.md is the one
record of what has been proven: this table follows its "Current
conclusions", and the account under them says, night by night, what was
tried, what it showed, and what is still a guess. Two things from it that
anyone using the software should know: the camera exposed for less time
than it was asked (about two thirds at 10 s) until camera.py learnt to ask
for more, so stacks from before 13 October 2026 overstate their integration;
and on one night the motors stopped while the handset went on reporting every move
as made. A GoTo with centring, a polar measurement and the skyline survey now
check that the view changed, and stop if it did not; a plain GoTo with no
plate solve still takes the handset's word.
Covered by automated tests (pytest, run on every push on Python 3.11 to 3.14, on Ubuntu and on Windows; the badge at the top shows how the latest run went): the astronomy, the
mount logic against the simulated handset, frame alignment and hot-pixel
removal, star measurement and frame rejection, sub-pixel and rotation
registration, clipped stacking, calibration arithmetic, a whole simulated
imaging run, the focus measurement, the polar alignment geometry, the INDI
message handling, the drift formula against a mount modelled from first
principles, drift cancelling on both sides of the simulated mount, and the
demo report end to end. The tests cannot cover the
real mount, camera or sky.
Known limits:
home steers by
the axis readout instead.tonight.py uses weights chosen by judgement.Near term, the measurements the first nights showed are needed, each now
one command (./ty characterise shows which have been made):
./focus.py, then ./focus.py --report)../polaralign.py --repeat 5)../mount.py response), before
anything about the drift assist is changed../mount.py pointing).Then:
--record and replay.py are ready for it) and a focusing session. The
animation at the top is the demo.config.toml as other cameras are tried.Later, for image quality, in this order: colour calibration from catalogue stars (the plate solve already identifies them); gentle deconvolution, once calibration and alignment are proven; sky-gradient removal frame by frame; drizzle on the raw Bayer frames. None of these before a controlled comparison has shown what the current pipeline gains on real data.
Later, if people ask for them:
docs/agents/safety.md, not built).evals/ run with a real model in the loop.agent.py either way.docs/focuser.md).Not planned: ASCOM's Windows-only COM interface (Alpaca reaches the same drivers, and is planned: Mounts), mobile apps, a React front end, cloud services, AI target selection, Docker images, plugin systems, a sequencing language. Bigger projects (NINA, KStars/Ekos) do those well; this one stays small, readable, and aimed at ordinary SynScan gear.
Reports from other hardware are the most useful contribution: run
./doctor.py --report and paste it into a Hardware compatibility report
issue, as Hardware describes. Questions, ideas and pictures you
have taken go in
Discussions.
What each release added is in CHANGELOG.md; ./release.py
makes one. For code, see
CONTRIBUTING.md:
run pytest, and anything that changes how the mount moves comes with a test
against simulator.py.
Weather: Open-Meteo. Seeing: 7Timer.
Light pollution: D. Lorenz's atlas.
Comets: COBS and JPL Horizons.
Bright supernovae: the Rochester list of active ones, in ./ty night and ./tonight.py when one brighter than magnitude 15.5 is up.
The sky's events and the news: in-the-sky.org, Universe Today and the BAA, in ./ty night and ./tonight.py.
Cloud imagery: EUMETSAT.
MIT; see LICENSE. The one exception is data/targets.csv,
which is derived from OpenNGC and
is licensed CC-BY-SA-4.0; see data/LICENSE.
--turned AZ ALT--cross-checkpolaris.py | Polar alignment before dark, from Polaris alone, which shows in daylight when nothing else near the pole does. check says whether it is worth trying (sky, Sun, focus); find looks around the home position until the star is in the picture, proves it by tipping the tube, and can keep every look (--record); align turns the RA axis with it in view, says roughly which way to move each adjuster, and with --watch keeps saying how far is left while the bolts are turned. |
landmark.py | Sets the azimuth by day: remembers a distant fixed thing with the telescope's axis readings, and next time turns back to it and shows how far it has moved. |
panorama.py | The skyline from a phone panorama, with no motors: finds the line between sky and everything else in the picture, lets you put it right where it is wrong, ties the picture to the compass from two marks (something the telescope is pointing at, a remembered landmark, or typed bearings), and keeps the result for the planner. Panoramas from other heights add doubt where things close by sit differently. |
horizon.py | The telescope measuring its own skyline, or checking a panorama's. --trace starts 30° apart, adds bearings only where the skyline bends, starts each from the skyline already measured, and checks its own answer; --daylight works by day, judging each small square of the frame by brightness, smoothness and colour; a frame that shows the top itself gives the height at once. --show prints the skyline in use, --forget throws it away. --move-from SPOT --offset METRES BEARING --distances FROM-TO:METRES ... works a new spot's skyline out from another spot's, by geometry, with 3° of doubt added, until it is surveyed. Without --trace it looks on a fixed grid, as it first did. |
snap.py | Takes one camera frame, saves the FITS in frames/, publishes a preview. |
shoot.py | Takes a picture: many short exposures, each checked, lined up and stacked live, with the raw frames kept. --exposure auto picks the longest exposure the tracking allows. --frames 0 carries on until cloud stops it. While it runs, ./ty run stop ends it cleanly with its final picture; recentre, assist-on and assist-off are also understood. |
planet.py | Photographs a planet: hundreds of frames of a few hundredths of a second, a square round the planet kept from each, the sharpest quarter lined up and stacked, sharpened and given the planet's usual colour. --restack FOLDER stacks a saved capture again. Point the telescope first with mount.py goto Saturn --solve. |
mosaic.py | Splits an object bigger than the frame into overlapping panels along the camera's sides, and keeps their centres; each panel is then a target, M31-P1, for mount.py goto and shoot.py. With no grid it suggests one from the object's size. |
restack.py | The quality pass: goes back over a session's raw frames, keeps the best, weights and clips them, and writes the finished picture. shoot.py runs it at the end. --all, or several session folders, stacks sessions from one night or many into one picture. --on HOST does the work on a faster computer reached by SSH (a name in ~/.ssh/config) and brings the picture back (remote.py). |
calibrate.py | Makes master dark, bias and flat frames, which shoot.py and restack.py then apply automatically. |
camera_setup.py | Gets the camera ready: checks it is plugged in and has a driver, takes Altair's library files out of their SDK zip if they are missing, and takes a test frame. --check only looks. Also the "Set up the camera" button in the console. |
stream.py | Streams the telescope live to YouTube, Twitch or both at once while a run is imaging, and stops when it is not, in 1080p (720p if the upload falls behind): the stack with one exposure, what the object is, the programme (a transit's light curve once it is steady, a patrol's panel map) and the figures, a card between targets and at the night's end; never the place, the webcam or a candidate's position. Drawn by streamlook.py from the words in stream-targets.toml (docs/stream-look.md); ./stream.py preview draws each state. On Twitch it sets the channel's title, category and tags for each target (twitch.py, after a one-time ./stream.py twitch-login). A presenter (streamstory.py) turns chapters, explainers and a slow crawl through the night, all curated or measured, and streamchat.py answers !what, !next and the like in the chat. Off unless switched on in Settings; ./stream.py key puts the stream key in ~/.config/telescopeyoke/stream-key, and ./stream.py key youtube or key twitch each service's own key for streaming to both ([stream] service = ["youtube", "twitch"]: encoded once and sent to each through ffmpeg's tee, so one dropping leaves the other on air). Encoded on the laptop's video hardware where it works. watch --to FILE tries it with no account; watch --install runs it from boot. Live on Twitch since 20 October 2026; the public pages and what they read are in docs/live-page.md. |
schedule.py | Tonight's schedule: the agreed steps of a night (add LABEL MINUTES [--not-before HH:MM] -- COMMAND, or wait LABEL MINUTES PID for one already running), run in order by run, with each step's expected start and finish and its progress, shown on the console's home screen; a step that fails stops the rest and fetches the person (alert.py call). |
camera_test.py | --capabilities lists what the camera is and offers (model, firmware, FPGA, when it was made, frame buffer, USB link: the camera's part of Altair's Diagnose File); --throughput times every way of getting frames off it; --gain-sweep tries a range of gains on tonight's sky and suggests one. --timing shows whether frames arrive sooner than their exposures could have ended; --trail stops the mount following the sky for one frame and reads the real exposure from the stars' trails. --banding and --stripes measure the row stripes; --cooldown, cap on outside, measures them every few minutes as the camera cools, with the air temperature, dew point and USB port; --electrons, cap off at an even light, measures electrons per ADU at each gain (docs/banding.md). |
compare.py | Shows the same patch of sky from several stacks side by side at full size, with star measurements for each. |
process.py | Turns a finished stack into a cleaner picture: level sky, white stars, smoothed colour noise. |
focus.py | Hands-free focusing aid: a click and a tone for every frame measured, higher as focus improves, in three levels from quick binned frames to many stars on the full sensor. --quiet for no sound, --numbers for each reading spoken as a number, --field to go first to a bright star with many round it (this moves the telescope), --scene for a daytime view. --report says how quickly the last run answered a turn of the knob, level by level. |
solve.py | Plate-solves a frame: where is the telescope really pointing? |
skywatch.py | Photographs the sky every minute and stops when stars appear. |
doctor.py | Checks what is installed and connected, and says what is ready: planner, mount, imaging. --report writes the same out to post as a hardware report, with the mount's model and the handset's firmware as the handset gives them, and your location left out; --probe PORT adds what answers on a serial port you name. |
tour.py | Records the demo being used, pressing the same buttons a person would: the animation at the top of this page, and a still of each screen. Ubuntu only. |
replay.py | Turns a centring run recorded with mount.py goto --solve --record into a GIF. |
watch.py | Photographs the telescope itself with the webcam. |
release.py | Makes a release: the zip people download, its notes from CHANGELOG.md, and with --publish the tag and the release on GitHub. |
build_catalogue.py | Regenerates data/targets.csv from OpenNGC. |
ty | One front door for programs and AI agents: capabilities, status, context, night, targets, target NAME, session, observing, doctor, rigs, characterise (what this rig has had measured about itself). --rig NAME first runs any of them for one of several telescopes. Add --json for a fixed machine-readable shape. |
mcp_server.py | Read-only MCP server offering the same information to MCP-aware assistants. |
| Calibration, frame checks, lining up, stacking, the quality pass. |
| Focusing by ear | Turn the knob and the laptop talks you onto focus. |
| The cloud sensor | A plan for a £40 box on the wall that says whether the sky above the garden is clear, cloudy or raining, all night. Not yet built. |
| The mount is wonky; measure how wonky | Drift from a rough polar alignment, and how it is cancelled. |
| Setup | Installing, the camera driver, the plate solver, what the computer needs. |
| For programs and AI agents | --json, --dry-run, the ty command, the web API and the MCP server. |
| Checking it under real sky | Exactly what has and has not been proven, night by night, and the five experiments that will show whether the clever parts work. |
| not yet |
| ⚠️ camera frames indoors; no mount driven |
| ✅ |
| Checking that the telescope really turned | not yet | not yet | ✅ |
Finding and marking the true home (findhome, truehome) | ✅ 17 October 2026: from 10.66° out by eye to 0.19° on the marks | ✅ | ✅ |
| Pointing survey: one correction for each side | ✅ 17 October 2026 | ✅ | ✅ |
| Polar repeatability, creep response | not yet | not yet | ✅ |
| Discovery: subtraction, candidates, vetting | ⚠️ on real M31 stacks, with planted fakes; no find of our own yet | ✅ | ✅ a known nova found again in ZTF's archive images |
Comets in spacecraft pictures (sungrazer.py) | does not need them | ✅ 19 October 2026: a known Kreutz sungrazer found by the hunt itself in live CCOR-1 pictures | ✅ known comets found in archived LASCO pictures |
Lights, speaker and phone alerts (alert.py) | does not need them | ✅ cloud and rain alerts on a real light and speaker; the call that woke the author when a run stopped (20 October 2026) | ✅ |
Live stream on Twitch (stream.py) | ✅ on air 20 October 2026 on the WASP-12 b run | ✅ | ✅ |
| Exoplanet transit photometry | ⚠️ one attempt (WASP-12 b): dimming seen, not a detection | ✅ | ✅ |
Photometric hold (shoot.py --hold) | not yet | not yet | ✅ |
| The night's end: parked and told | not yet | ⚠️ mount.py home only | ✅ |
Several rigs, --probe | not yet | not yet | ✅ |
| Dark and flat frames, gain sweep | not yet | not yet | ✅ |