the interface on top of an image acquired with the guiding solution

I have the following problem at home: the balcony is small, the tube is big and I am lazy. This sums up to a constant problem of drifting, the planet or lunar surface being imaged slowly walks away. Though there are methods to polar align a mount even when there is no way to see the north pole, the small balcony part means frequent small bumps into the whole thing. The oversized scope is also prone to accidents. I also tried the feature tracker in Sharpcap, failed, and the autoguiding of PHD2, failed. So I came up with the soapbox ensembleI call it noszogtató in Hungarian, for it means something like nagger-pusher-convincer. It got itself a soapbox, literally.

Contents

  • rationale
  • block diagram
  • communication
  • desktop app
  • arduino code
  • hardware
  • conclusions, photos

Rationale, specifications

I wanted the program to be as simple as a stone. Obviously, the homunculus is the best analogy: when the little man sees a drift, compensate.

Interval: compensate with fine tuned values at a preset time if the real human is the single source of feedback. This is what the big man did initially: push the buttons and push the object back to the center.

Grabbing the screen of the operating system, or that of a particular window,  is easier and more portable than writing driver interfaces, while allowing the recording software to connect to the camera.

For planetary imaging (planets and surfaces), the sampling rate is more than enough to not depend on a calibration and the prediction of commands.

No lengthy calibration, backlash counting, just a presumption: ra+ will move the object in the image left and dec+ will move the object downwards — so orient the camera accordingly and invert the axes as necessary. A future version may get a calibration and thus custom rotation-mirroring detection, but not now.

The device should stop pushing the mount on any technical failure.

 

 

 

Communication

In its default state, the device does nothing. The soapbox affects on a countdown basis: the desktop doesn’t set the state of the pins, but expresses a desire to add a value to the countdown associated with a certain movement, with a resolution of 10ms. The exclusive logic is done by the soapbox: if the latest command is dec+ add 500 and dec- is running, then make dec- zero and start dec+. This way, if the connection drops or the desktop program freezes etc, the device quickly consumes the values in the countdowns and becomes idle, leaving the mount alone.

The telescope has four (ra+, ra-, dec+, dec-) pulled up input pins and a ground exposed on the ST4, so the only thing that can be set on this port is the length of the pulling down. The autoguiding speed setting in the hand controller is certainly useful. Just make sure it doesn’t go into a ping-pong oscillation. At maximal resolution (Jupiter goes up to 240 pixels) I use it at 0.5x.

Desktop app

I wrote it in Lazarus, because I still like it. Though the pascal syntax has fallen out of my muscle memory, mostly tailored now for the C syntax, I spent some great months programming stuff in imperative pascal, and then in Delphi. I still like it, and it’s my go to language for the desktop. Eff Microsoft.

However, the code is as is, to me it’s more important that it works, regardless of the state of the source code — much of the original code I wrote in very awkward and uncomfortable positions, while some clouds passed in front of Jupiter.

There would have been no need for a desktop app if an interval was the only thing needed. But the desktop makes the screen grabbing possible. So here’s the block diagram of the desktop app.

The interface is designed for my laptop 1280×800 screen, on which to not cover too much of the sharpcap interface while staying always on top. The program is assuming it is SharpCap running underneath with a video live view of maximum 980x pixels (zoom out a bit if needed), with its layout and default windows/app colors in order to detect the ROI and whether the start recording dialog is open or not. Do rewrite it if needed.

The program tries to connect to the first CH340 chip (Arduino nano USB-serial adapter) it finds. This may change in the future.

Apparently it is not a good friend of eqmod, I use it with a hand controller.

The source code is here.

Sharpcap, Jupiter in clouds, the mountpusher with the streched out histogram

Sharpcap, Jupiter in clouds, the mountpusher with the streched out histogram

jupiter is there in the original too…

jupiter is there in the original too…

Arduino code

The arduino code is simple. It is based on a countdown logic. Each pin has its opposit, to drive ra+ is meaningless when driving ra-, for example. I use pins 9, 10, 11 and 12.

 

#include "timer.h"


// Astronomical mount guiding unit (noszogtato) for the ST4 port,
// Skywatcher HEQ5 mount
#define VERSION "1.01, 2019-06-24"




#define TIMER_INTERVAL_MS 10

auto timer = timer_create_default();

#define TIMER2_DIVIDER 21 // for some reason that extra 1 is needed, and below, comparing to 1 works, to zero does not
int timerDivider;
int statusCounter;
int reportStatusWithoutBeingAsked;
int statusToBeReported;

const int READ_BUFFER_LEN = 128;
char readBuffer[READ_BUFFER_LEN];

#define RA_PLUS 9
#define RA_MINUS 10
#define DEC_PLUS 11
#define DEC_MINUS 12



typedef struct HEQ5_Pin {
   int pinNumber;
   int inPairWithPinNumber;
   int timeLeft;
   int currentState;
} HEQ5_Pin;

HEQ5_Pin heq5Pins[5];

//---------------------------------------------------------------------

void pins_add(int pin, int ms){
  if (pin > 4){
    return ;
  }
  if (pin < 0){ return ; } if (ms > 0){
    // valid ms, increment the time left
    heq5Pins[pin].timeLeft += ms;
    if (heq5Pins[pin].inPairWithPinNumber > 0){
      for (ms =0; ms < 5; ms++){ if (heq5Pins[ms].pinNumber == heq5Pins[pin].inPairWithPinNumber){ heq5Pins[ms].timeLeft = 0; } } } }else{ //invalid ms given, stop the motion heq5Pins[pin].timeLeft = 0; if (heq5Pins[pin].inPairWithPinNumber > 0){
      for (ms =0; ms < 5; ms++){
        if (heq5Pins[ms].pinNumber == heq5Pins[pin].inPairWithPinNumber){
          heq5Pins[ms].timeLeft = 0;
        }
      }
    }    
  }
}

//---------------------------------------------------------------------

void pins_process(){
  for (int i=0; i<5; i++){ if (heq5Pins[i].timeLeft > 0){
      if (heq5Pins[i].currentState != HIGH){
          digitalWrite(heq5Pins[i].pinNumber, HIGH);  
          heq5Pins[i].currentState = HIGH;
      }
    }else{
      if (heq5Pins[i].currentState != LOW){
          digitalWrite(heq5Pins[i].pinNumber, LOW);  
          heq5Pins[i].currentState = LOW;
      }
    }
  }
}

void timerCallback(){
  for (int i = 0; i<5; i++){ if (heq5Pins[i].timeLeft > 0){
      heq5Pins[i].timeLeft -= TIMER_INTERVAL_MS;
      if (heq5Pins[i].timeLeft < 0){
        heq5Pins[i].timeLeft = 0;
      }
    }else{
      heq5Pins[i].timeLeft = 0;
    }
  }
  timerDivider--;
}

void setup() {
  // put your setup code here, to run once:
  readBuffer[0] = 0;
  Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB
  }

  heq5Pins[0].pinNumber = RA_MINUS;
  heq5Pins[0].inPairWithPinNumber = RA_PLUS;

  heq5Pins[1].pinNumber = RA_PLUS;
  heq5Pins[1].inPairWithPinNumber = RA_MINUS;

  heq5Pins[2].pinNumber = DEC_MINUS;
  heq5Pins[2].inPairWithPinNumber = DEC_PLUS;

  heq5Pins[3].pinNumber = DEC_PLUS;
  heq5Pins[3].inPairWithPinNumber = DEC_MINUS;

  // sanity check led
  heq5Pins[4].pinNumber = 13;
  heq5Pins[3].inPairWithPinNumber = 0;
  
  for (int i=0; i<5; i++){ heq5Pins[i].timeLeft = 0; heq5Pins[i].currentState = LOW; pinMode(heq5Pins[i].pinNumber, OUTPUT); digitalWrite(heq5Pins[i].pinNumber, heq5Pins[i].currentState); } timerDivider = TIMER2_DIVIDER; timer.every(TIMER_INTERVAL_MS, timerCallback); statusCounter = 0; reportStatusWithoutBeingAsked = 0; statusToBeReported = 1; Serial.println("==========================="); Serial.print("EQ Mount >>noszogtato<< for the Skywatcher ST4 port, version "); Serial.println(VERSION); Serial.println("VARADI NAGY Pal, csillagtura.ro"); Serial.println("==========================="); } void reportStatus(){ char statChr[2]={0,0}; if (reportStatusWithoutBeingAsked || (statusToBeReported > 0)){
    statusCounter++;
    if (statusCounter > 20){
      statusCounter = 0;
    }
    statusToBeReported--;
    if (statusCounter < 10){
      statChr[0] = 48+statusCounter;
    }else{
      statChr[0] = 65+statusCounter-10;
    };  
    Serial.print(statChr);
    Serial.print("S ");
    for (int i=0; i<5; i++){ if (heq5Pins[i].pinNumber == RA_MINUS){ Serial.print("rn,"); }; if (heq5Pins[i].pinNumber == RA_PLUS){ Serial.print("rp,"); }; if (heq5Pins[i].pinNumber == DEC_MINUS){ Serial.print("dn,"); }; if (heq5Pins[i].pinNumber == DEC_PLUS){ Serial.print("dp,"); }; Serial.print(heq5Pins[i].timeLeft); Serial.print(" "); } Serial.println("S/"); } } void loop() { if (Serial.available() > 0) {
    char c = Serial.read();
    if (c == '#'){
      readBuffer[0] = 0;
    }else{
      if (c == '/'){
        //set reporting
        if (strncmp(readBuffer, "rep=", 4) == 0){
          if (readBuffer[4]=='1'){
            reportStatusWithoutBeingAsked = 1;
          }else{
            reportStatusWithoutBeingAsked = 0;
          }
        }
        //getreport
        if (strncmp(readBuffer, "getrep", 6) == 0){
          statusToBeReported++;
        }
        
        //manual report

        //auto report
        
        //process the command pin, #P,TTTTTTT/
        //                         #rap,TTTTTT/
        
        int pinIndex = readBuffer[0] - 48;
        
        if (readBuffer[0] == 'r'){
          // rap, ran
          if (strncmp(readBuffer, "rap", 3) == 0){
            for (int pi = 0; pi<5; pi++){
              if (heq5Pins[pi].pinNumber == RA_PLUS){
                pinIndex = pi;
              }
            }
          }
          if (strncmp(readBuffer, "ras", 3) == 0){
            // stop ra movement
            pinIndex = -1;
            for (int pi = 0; pi<5; pi++){
              if (heq5Pins[pi].pinNumber == RA_PLUS){
                heq5Pins[pi].timeLeft = 0;
              }
              if (heq5Pins[pi].pinNumber == RA_MINUS){
                heq5Pins[pi].timeLeft = 0;
              }
            }
          }
          if ((strncmp(readBuffer, "ran", 3) == 0) || (strncmp(readBuffer, "ram", 3) == 0)){
            for (int pi = 0; pi<5; pi++){
              if (heq5Pins[pi].pinNumber == RA_MINUS){
                pinIndex = pi;
              }
            }
          }
        }
        if (readBuffer[0] == 'd'){
          // decp, decm/n
          if (strncmp(readBuffer, "decp", 4) == 0){
            for (int pi = 0; pi<5; pi++){
              if (heq5Pins[pi].pinNumber == DEC_PLUS){
                pinIndex = pi;
              }
            }
          }
          if (strncmp(readBuffer, "decs", 4) == 0){
            // stop dec movement
            pinIndex = -1;
            for (int pi = 0; pi<5; pi++){
              if (heq5Pins[pi].pinNumber == DEC_PLUS){
                heq5Pins[pi].timeLeft = 0;
              }
              if (heq5Pins[pi].pinNumber == DEC_MINUS){
                heq5Pins[pi].timeLeft = 0;
              }
            }
          }
          if ((strncmp(readBuffer, "decm", 4) == 0) || (strncmp(readBuffer, "decn", 4) == 0)){
            for (int pi = 0; pi<5; pi++){ if (heq5Pins[pi].pinNumber == DEC_MINUS){ pinIndex = pi; } } } } if (pinIndex > -1) {
          char * timeString = strstr(readBuffer, ",");
          int timeAdd = 0;
          if (timeString){
            timeAdd = atoi(timeString+1);
            pins_add(pinIndex, timeAdd);
          }
        }
        /*
         * debug, dont echo in production, the status report will get screwed
        Serial.print("ECHO: ");
        Serial.println(readBuffer);
        Serial.print("pin[");
        Serial.print(pinIndex);
        Serial.print("] += ");
        Serial.print(timeAdd);      
        Serial.println("");  
        */
      }else{
        //continue to concat
        int n = strlen(readBuffer);
        if (n < READ_BUFFER_LEN-2){
          readBuffer[n] = c;
          readBuffer[n+1] = 0;
        }
      }
    }
  };
  timer.tick();
  pins_process();  
 
  if (timerDivider <= 1){
    timerDivider = TIMER2_DIVIDER;
    reportStatus();
  }  
}

 

Hardware

For the hardware this github repo gave me the needed inspiration. However, the first instance of the hardware was a raspberry pi listening for http get commands and closing/opening relays. So the board is not exactly professional. Note that I use pins 9, 10, 11 and 12 in the code above. Make the wiring so it fits.

the hardware on kevinferrare’s github, different pins are used

The ST4 port is in fact an RJ11 or RJ12 outlet with 6 pins, out of which five are used. Coincidentally, the form factor of the plug is compatible with the RJ45, but a four pin telephone outlet can easily be extended by adding the two missing pins — and thanks to one of my colleagues, Adi, for giving me the idea and “making” me the first connector from two old, unused phone outlets, and giving me further ideas. I used TLP504A optocouplers and larger resistor, and added further 1K to the ST4 side (in fact, it remained from the relay version, I wasn’t comfortable with just shorting the pins).

Conclusions, photos

It works, if the sky stays clear.

The one drawback is the rotation of the field of view. Just as with the alt-az mount, an inaccurate equatorial alignment will yield a noticeable field rotation, of a few degrees perhaps, over the hours. Nothing big enough to render useless the stacking of a 30 Saturn video. The PIPP-AS!2/3-registax software stack does its job.

“home observatory” – sarcasm: a lot like the atacama desert, antennae and few quality people around

“home observatory” – sarcasm: a lot like the atacama desert, antennae and few quality people around

the soapbox in action

the soapbox in action

the soapbox in action

the soapbox in action

almost like a Pear product, white but does not cost a fortune

almost like a Pear product, white but does not cost a fortune

a soapbox

a soapbox

powered by an arduino nano

powered by an arduino nano

same with Saturn this time

same with Saturn this time

top right corner…

top right corner…

facebooktwittergoogle_plusredditpinterestlinkedinmail