The current design (2023) Main Unit runs on Teensy 3.2 and Teensy 4.0 boards. These are based on ARM Cortex processor, with peripherals (UART, timer, SPI, etc.). The stepper controllers are TMC2130 or TMC2160 from Trinamic in STEP/DIR mode.

The software is built with the Arduino framework (setup / loop) and consists of 3 main parts: the Main Loop, the Timer Loop and the Motor Interrupts. Without an operating system, it relies on global variables. To prevent concurrent variable accesses between the 3 contexts, it disables interrupts (cli / sei).

Main Loop

The Main loop runs every 10mS (sidereal). It polls for commands from both UARTs (SHC and USB), which handle identical commands based on the LX200 standard (Goto, track, guide etc) with proprietary extensions. It computes the positions and speeds of both axes, then checks for safety limits.

Positioning Mode (Goto)

Much of the code is common between Eq and Altaz mounts.

The simplified call sequence for a Goto RA/Dec command is as follows:

GotoEqu(HA, Dec)                    // LX200 command
    EquToHor(HA, Dec)               // computes target Az/Alt
    goToHor(Az, Alt)                // can also be called directly by LX200 command 
        toInstrumentalDeg(Az, Alt)  // matrix operation that computes axes positions as floating-point degrees
        predictTarget()             // converts degrees to steps (long integers), taking into account the gear ratio
            Angle2InsrtAngle        // corrects positions according to pier side if needed
        Goto(Axis1, Axis2)          // check for errors, then sets targets for both axes

For retrieving current RA/Dec:

getEqu()                            // LX200 command :GR#, :GD#
    getHorApp()                     // retrieves axis positions in steps and converts to degrees
        toReferenceDeg()            // matrix operation - converts axis degrees to sky Az/Alt 
    HorTopoToEqu()                  // converts Az/Alt to HA/Dec    

Mapping sky coordinates to axis angles

The difference between Eq and AltAz mounts is handled inside the alignment matrix operations (toInstrumentalDeg / toReferenceDeg) based on Toshimi Taki's 2004 paper. At initialization, the alignment matrix is initialized as follows:
- for AltAz mounts, Axis1 is Azimuth reversed by 180ยบ, Axis2 is Altitude, so that in the Home position the optical tube is horizontal, pointing South.
- for Eq mounts, it converts Axis1/Axis2 to Altitude and Azimuth, so that in the Home position the optical tube points to the celestial pole.

The idea is to perform all Goto with the same (AltAz) code.
This simplifies somewhat the design, but it hides the amount of conversions going on behind the scenes (see tracking section below)

Mapping axis angles to steps

This mapping is not one-to-one: The same position (in steps) may represent 2 different axis angles, according to the hemisphere. This also applies to the direction of tracking.

The motor reverse bit is handled at the very lowest level so that the whole software uses the same coordinates for direct and reverse directions.

Slewing Mode

Slewing or centering is triggered by pressing direction keys. It moves the mount at predefined speeds in any direction, without a target. This is the call sequence:

MoveAxis1/2                         // LX200 command
MoveAxis1/2atRate (rate)            // directy sets the motor timers


Tracking does not use a fixed velocity for the RA axis, but repeated positioning mode on a moving pseudo-target. For an Eq mount, it goes like this:

computeTrackingRate                     // :Te# LX200 command. This enters the tracking mode called by the main loop:

    do_compensation_calc()              // Computes positions behind and ahead of the current position:
        for each position
                getHorApp()             // retrieves RA/Dec axis positions in steps and converts to degrees
                    toReferenceDeg()    // converts axis degrees to sky Az/Alt - matrix operation  
                horAppToEqu()           // convert Az/Alt to RA/Dec - trig operation 
            equToHor()                  // convert RA/Dec to Az/Alt - trig operation
            toInstrumentalDeg()         // convert Az/Alt to RA/Dec axis degrees - matrix operation
            instrtoStep()               // axis degrees to steps
        compute difference between the 2 axis positions, derive a speed then set the motor timers


Guiding for astrophoto is performed either by signals on the ST4 connector emulating button presses, or by software commands. It increases or decreases the tracking speed on both axes.

The code is somewhat confusing because the term is used both for centering (SHC button presses) and for automatic guiding via software (PHD2 etc.)

enableST4GuideRate()                    // :Mgdnnnn# Pulse guide command
    apply_GuidingA1()                   // modify the tracking pseudo-target

Timer Loop

The timer loop is triggered by a hardware timer that runs every 10mS (sidereal). From the current mode (Goto, track, guide etc.) it computes and programs the periods (rates) for both motor interrupts.

Motor Interrupts

There is one motor interrupt handler for each axis motor. Each one runs at a period determined by the axis speed and controls the STEP and DIR inputs of the TMC motor controller.