{"name":"TechTools Optics Lab API","version":"1.0","updated":"2026-09-15","engine_version":"1.1.0","summary":"Paraxial thin-lens / thin-mirror telescope ray tracing for four classic designs (Galilean, Keplerian, Newtonian, Cassegrain). Same physics as the browser tool at https://techtools.cz/tools/optics-lab/ (one engine, two implementations, kept in step by a parity test), returned as structured JSON so an agent can compute a design, read every ray polyline and explain the result without the UI.","base_url":"https://techtools.cz/optics-lab-api","auth":"None. Public, read-only, no data stored.","model":{"optics":"Thin lenses and thin (paraxial) mirrors. A surface bends a ray once, in its plane, by u' = u - y/f. The Newtonian diagonal is a pure fold; the Cassegrain secondary is a convex mirror with f \u003c 0. Real refraction at both glass surfaces is deliberately NOT modelled here (see /snell).","units":"Millimetres and degrees. Slopes are tan(angle), not radians.","object":"The object is at infinity: rays from one point arrive parallel.","sign_conventions":["field is positive ABOVE the axis; its rays arrive descending, so u0 = -tan(field)","M = -f_obj/f_eye. Negative M means an inverted image; the exit rays reproduce it (exit slope = M * u0)","the Galilean eyepiece focal length is reported negative (diverging); fe is always given as a magnitude","the Cassegrain secondary focal length is reported negative (convex)"],"coordinates":["x runs along the optical axis and light travels +x initially","refractors: the objective sits at x = 0","reflectors: the primary mirror vertex sits at x = 0 facing -x","Newtonian: the 45 degree diagonal folds the beam to +y, so the eyepiece sits on a vertical side axis (its exit slope is measured along that axis, and elements carry axis: \"vertical\")","Cassegrain: the secondary sends the beam back through a hole in the primary; focus and eyepiece at x \u003e 0"],"finite_apertures":"Every surface is finite. The secondary, the primary hole and the eyepiece are sized to illuminate design_field degrees. A ray that misses a mirror, hits the back of one or overshoots the eyepiece rim STOPS there and is reported as blocked or vignetted; it is never traced on through metal or glass. Exit-beam figures are computed only from the rays that reach the eye.","design_field":"design_field (deg) is what the instrument was BUILT for and defaults to the type default or |field|, whichever is larger, so a plain request never vignettes itself. Pass it explicitly to model an instrument designed for a narrower field than the star you point it at.","angle_families":{"note":"Two families, deliberately named apart. Do not compare one with the other.","exit_slope":"direction of the exit RAYS in the drawing's frame, as tan(angle). Newtonian: measured along the vertical side axis. Positive means rising in the drawing.","exit_angle_deg":"the same ray direction as an angle: atan(exit_slope).","apparent_angle_deg":"where the eye SEES the object point, with the same sign convention as `field` (positive = above the axis): orientation * atan(|M| * tan(field)), exact within the model. Null when the eyepiece is removed.","exit_angle_nominal_deg":"the textbook small-angle value M * field: the like-for-like quantity to compare apparent_angle_deg with. Null when the eyepiece is removed."},"honesty":"paraxial_ok is false once |apparent_angle_deg| exceeds paraxial_limit_deg (20): the paraxial model itself stops being valid there, so treat the numbers as qualitative and expect the note beyond_paraxial.","rounding":"Every number is rounded half away from zero to 4 decimals so the JS and Ruby engines agree."},"parameters":[{"name":"type","en":"telescope design to trace","cs":"typ dalekohledu, který se má trasovat","default":"galilean","values":["galilean","keplerian","newtonian","cassegrain"]},{"name":"fo","en":"objective / primary focal length (mm)","cs":"ohnisková vzdálenost objektivu / primáru (mm)","per_type":{"galilean":{"default":1000,"range":[200,3000]},"keplerian":{"default":1000,"range":[200,3000]},"newtonian":{"default":1200,"range":[300,3000]},"cassegrain":{"default":800,"range":[300,2000]}}},{"name":"fe","en":"eyepiece focal length magnitude (mm); the engine applies the sign of the type","cs":"velikost ohniskové vzdálenosti okuláru (mm); znaménko doplní typ","per_type":{"galilean":{"default":50,"range":[5,300]},"keplerian":{"default":50,"range":[5,300]},"newtonian":{"default":25,"range":[5,100]},"cassegrain":{"default":80,"range":[5,100]}}},{"name":"aperture","en":"objective / primary diameter (mm); sets ray spacing and the obstruction estimate","cs":"průměr objektivu / primáru (mm); určuje rozestup paprsků a odhad stínění","per_type":{"galilean":{"default":80,"range":[20,200]},"keplerian":{"default":80,"range":[20,200]},"newtonian":{"default":200,"range":[60,400]},"cassegrain":{"default":200,"range":[60,400]}}},{"name":"field","en":"angular position of the object point (deg), positive = above the axis","cs":"úhlová poloha pozorovaného bodu (°), kladná = nad osou","per_type":{"galilean":{"default":0,"range":[-5,5]},"keplerian":{"default":0,"range":[-5,5]},"newtonian":{"default":0,"range":[-1.5,1.5]},"cassegrain":{"default":0,"range":[-0.5,0.5]}}},{"name":"design_field","en":"field (deg) the instrument was built to illuminate fully: sizes the secondary, the primary hole and the eyepiece; defaults to the type default or |field|, whichever is larger","cs":"pole (°), na které byl přístroj postavený: určuje velikost sekundáru, otvoru primáru a okuláru; výchozí je typová hodnota nebo |field|, podle toho, co je větší","per_type":{"galilean":{"default":0.5,"range":[0.1,5]},"keplerian":{"default":0.5,"range":[0.1,5]},"newtonian":{"default":0.5,"range":[0.1,1.5]},"cassegrain":{"default":0.25,"range":[0.1,0.5]}}},{"name":"eyepiece","en":"true = eyepiece in place; false = removed (rays diverge after the focus)","cs":"true = okulár vložený; false = vyjmutý (paprsky se za ohniskem rozbíhají)","per_type":{}},{"name":"fs","en":"Cassegrain only: convex secondary focal length magnitude (mm)","cs":"jen Cassegrain: velikost ohniskové vzdálenosti vypuklého sekundáru (mm)","per_type":{"cassegrain":{"default":250,"range":[50,1000]}}},{"name":"p","en":"Cassegrain only: distance from the secondary to the primary focus (mm), must be \u003c fs","cs":"jen Cassegrain: vzdálenost sekundáru od ohniska primáru (mm), musí být \u003c fs","per_type":{"cassegrain":{"default":200,"range":[10,999]}}}],"endpoints":[{"method":"GET","path":"/telescopes","returns":{"engine":"optics-lab","version":"engine version","types":"text entry + kind, eyepiece_sign, defaults, ranges","sources":"bibliography","parameters":"bilingual parameter glossary"},"curl":"curl https://techtools.cz/optics-lab-api/telescopes"},{"method":"GET","path":"/telescopes/:id","params":{"id":"galilean | keplerian | newtonian | cassegrain"},"returns":"one merged type entry; 404 { error, types } for an unknown id","curl":"curl https://techtools.cz/optics-lab-api/telescopes/newtonian"},{"method":"GET","path":"/design","params":{"type":"galilean (default)","fo":"mm","fe":"mm","aperture":"mm","field":"deg","design_field":"deg, optional","eyepiece":"true|false|in|out|1|0","fs":"Cassegrain only, mm","p":"Cassegrain only, mm"},"returns":"the full design document (see design_response below); 422 on a refused design","notes":"Cached for 5 minutes. Unknown query keys are ignored.","curl":"curl 'https://techtools.cz/optics-lab-api/design?type=galilean\u0026fo=1000\u0026fe=50\u0026field=1'"},{"method":"POST","path":"/design","content_type":"application/json","body":{"type":"cassegrain","fo":800,"fs":250,"p":200,"fe":40,"aperture":200,"field":0},"returns":"same document as GET /design","curl":"curl -X POST -H 'Content-Type: application/json' -d '{\"type\":\"cassegrain\",\"fo\":800,\"fs\":250,\"p\":200,\"fe\":40}' https://techtools.cz/optics-lab-api/design"},{"method":"GET","path":"/snell","params":{"n1":"1..4","n2":"1..4","angle":"0..90, from the surface normal"},"returns":{"ok":true,"n1":1,"n2":1.5,"angle_in_deg":30,"law":"n1 * sin(theta1) = n2 * sin(theta2)","critical_angle_deg":"null when n1 \u003c= n2","total_internal_reflection":false,"angle_out_deg":19.4712,"bends":"towards_normal | away_from_normal | straight"},"notes":"All three parameters are required (422 with code \"missing\" otherwise). On total internal reflection angle_out_deg is null and reflected_angle_deg + note are returned instead.","curl":"curl 'https://techtools.cz/optics-lab-api/snell?n1=1\u0026n2=1.5\u0026angle=30'"},{"method":"GET","path":"/figures","returns":"the tool's figure/diagram content; 404 { error } if not published yet","curl":"curl https://techtools.cz/optics-lab-api/figures"},{"method":"GET","path":"/galileo","returns":"the Galileo history content; 404 { error } if not published yet","curl":"curl https://techtools.cz/optics-lab-api/galileo"},{"method":"GET","path":"/notes","returns":{"notes":"note code -\u003e { en, cs } for every code a design can emit"},"curl":"curl https://techtools.cz/optics-lab-api/notes"},{"method":"GET","path":"/docs","returns":"this document","curl":"curl https://techtools.cz/optics-lab-api/docs"}],"design_response":{"keys":["model","results","notes","rays","elements","bounds","ok","engine","version","type","units","input"],"note":"Key order is part of the contract: both engines emit exactly this order.","results":"focal lengths, separation/length, magnification (+ magnification_abs, orientation), input_angle_deg/input_slope, design_field_deg, eyepiece_radius_mm, intermediate_image { real, x, height_mm, note }, the ray census (rays_traced, rays_reaching_eye, rays_blocked, rays_vignetted) and the exit beam (exit_parallel, exit_slope, exit_angle_deg, apparent_angle_deg, exit_angle_nominal_deg, exit_slope_spread, paraxial_limit_deg, paraxial_ok; see model.angle_families). Reflectors add secondary_radius_mm, obstruction_ratio and reflections; Cassegrain adds secondary_magnification, effective_focal_mm, back_focus_mm and hole_radius_mm.","rays":{"id":"r0..rN","kind":"real | blocked | vignetted","entry_height":"mm at the entrance aperture","exit_slope":"tan(angle) for a real ray, null for a stopped one","points":"[[x, y], ...] polyline in mm; a stopped ray simply ends where it stopped","ghost":"Galilean only: the dashed continuation to the hypothetical crossing, else null","blocked_by":"present only on a stopped ray: secondary_back (hit the opaque back of the secondary) | secondary_rim (missed it and flew up the tube) | primary (hit the primary instead of its hole on the way back) | primary_hole (Cassegrain: arrived inside the hole on the way IN, so there is no mirror to reflect off) | eyepiece_rim"},"elements":"the drawn instrument: lenses, mirrors, the focus marker, the eye and the tube, each with x/y, half_height (physical radius) and f where meaningful; Newtonian eyepiece/eye carry axis: \"vertical\"","bounds":{"x_min":"mm","x_max":"mm","y_min":"mm","y_max":"mm"},"notes":"note codes; resolve the bilingual text with GET /notes"},"error_response":{"status":422,"shape":{"ok":false,"engine":"optics-lab","version":"1.1.0","errors":[{"param":"p","code":"secondary_diverging","message":"English explanation","message_cs":"české vysvětlení"}]},"codes":[{"code":"unknown_type","param":"type","when":"type is not one of galilean, keplerian, newtonian, cassegrain"},{"code":"not_a_number","param":"any numeric parameter","when":"the value is not a number (e.g. fo=abc)"},{"code":"out_of_range","param":"any numeric parameter","when":"the value is outside that type's range (see parameters below)"},{"code":"eyepiece_focal_too_long","param":"fe","when":"Galilean only: fe \u003e= fo, so the two lenses would coincide or cross"},{"code":"tube_too_short","param":"fo","when":"Newtonian: the diagonal needed for this focal length and design field would shadow more than half the primary"},{"code":"secondary_diverging","param":"p","when":"Cassegrain: p \u003e= fs, so the convex secondary never brings the beam to a focus"},{"code":"secondary_behind_primary","param":"p","when":"Cassegrain: p \u003e= fo, so the secondary would sit behind the primary focus"},{"code":"focus_inside_tube","param":"p","when":"Cassegrain: the back focus lands in front of the primary. Use a larger p or a SHORTER |fs|"},{"code":"secondary_too_large","param":"p","when":"Cassegrain: the secondary would shadow more than half the primary"},{"code":"hole_too_large","param":"design_field","when":"Cassegrain: the central hole needed for this design field would remove more than half the primary"}]},"examples":[{"request":"GET /design?type=galilean\u0026fo=1000\u0026fe=50\u0026aperture=80\u0026field=0","expect":{"separation_mm":950,"eyepiece_focal_mm":-50,"magnification":20,"orientation":"erect","exit_parallel":true,"intermediate_image.real":false},"why":"d = fo + f_eye = 1000 - 50; M = -1000 / -50 = +20, so the image stays upright"},{"request":"GET /design?type=keplerian\u0026fo=1000\u0026fe=50\u0026field=1","expect":{"separation_mm":1050,"magnification":-20,"orientation":"inverted","exit_angle_nominal_deg":-20,"intermediate_image.real":true},"why":"the converging eyepiece sits one focal length BEYOND the focus, so the tube is longer and the image inverts"},{"request":"GET /design?type=newtonian\u0026fo=1200\u0026fe=25\u0026aperture=200","expect":{"magnification":-48,"focus_offset_mm":135,"diagonal_position_mm":-1065,"reflections":2},"why":"M = -1200/25; the diagonal sits aperture/2 + 35 mm before the prime focus"},{"request":"GET /design?type=cassegrain\u0026fo=800\u0026fs=250\u0026p=200\u0026fe=40\u0026aperture=200","expect":{"secondary_magnification":5,"effective_focal_mm":4000,"back_focus_mm":400,"magnification":-100},"why":"m = fs/(fs-p) = 250/50 = 5, F = fo*m = 4000, q = p*fs/(fs-p) = 1000, B = q - (fo-p) = 400"},{"request":"GET /design?type=galilean\u0026fo=980\u0026fe=47.5","expect":{"magnification":20.6316,"separation_mm":932.5},"why":"Galileo's own instrument (Museo Galileo inv. 2428); the museum catalogue rounds it to 21x"},{"request":"GET /snell?n1=1\u0026n2=1.5\u0026angle=30","expect":{"angle_out_deg":19.4712,"bends":"towards_normal","critical_angle_deg":null},"why":"sin(theta2) = 1 * sin(30) / 1.5 = 0.3333"}],"links":{"tool":"https://techtools.cz/tools/optics-lab/","llms":"https://techtools.cz/llms.txt","sources":[{"title":"UC Santa Barbara Physics Lecture Demonstrations 80.51: Telescopes (Keplerian and Galilean)","url":"https://web.physics.ucsb.edu/~lecturedemonstrations/Composer/Pages/80.51.html"},{"title":"Museo Galileo: Galileo's telescope (inv. 2427)","url":"https://catalogue.museogalileo.it/object/GalileosTelescope.html"}]}}