FoFi – A free, automatic telescope focus finder software

Focus Finder

This article is about a new piece of software I developed over the past few years: A telescope focus finder software for Linux (and maybe later Windows).

At the time of publication this project is not even in ALPHA state. However, I feel now is the right time to publish it since it reached a state where at least the source code could be useful to others. You can find the source code on github here.

The software aims to support the amateur astronomer (and especially astrophotographer) with one of the most critical but also most annoying tasks: Finding the best focus position for the camera. The main goal is to provide a free and easy to use software that just does the job – automatically.

With a given configuration it should also be possible to execute “FoFi” from the command-line without requiring any user interaction. This way you can include a call to the Focus Finder into a script. This might be useful if the entire observation process should be automated and you want to re-focus from time to time to compensate the temperature drift.

Current project status

Without understatement this project currently is still in the proof-of-concept phase. That means it is not yet usable. However, if you think this project holds some potential and may be useful for you, or if you maybe work on a similar project, I would be happy to hear your feedback. We might join our efforts and share our experiences and of course any support in the development of this tool is also very welcome! Please get in contact with me.

In order to perform an automatic focusing a lot of groundwork was required: The control of the astronomy devices, the recognition of stars, the centroid calculate and measurement of the star diameter. Only if all that works reliably, one can even think about running more complex algorithms like measuring the star diameter automatically for different focus positions…

In short, those groundwork is now done and I am trying to figure out which movement patterns and algorithms make sense to finally calculate the best focus position reliably.

How the project was born

When starting this project there was the following situation: There was a free piece of software for automatically finding the “best” focus of the telescope: FocusMax. Steve Brady and Larry Weber developed it and offered it to the public in 2001. Meanwhile there is version 4 of this software available but as far as I was able to see it is no longer free. Instead CCDWare now offers it.

Without going into the details of this software there were two main problems for me:

  1. FocusMax worked (and maybe still works?) only with non-free 3rd party software (CCDWare, MaximDL)
  2. FocusMax does not run under Linux

I decided to look closer and tried to understand how the software works and read most of the documentation and papers around. Then I started to do some tests and also thought about improving the existing concept. I read further papers and proposals about telescope focusing. Beside the technical perspective also the usability was important. At that time FocusMax had a lot of buttons and was (at least from my point of view) hard to control. In comparison OpenPHD – a tool for guiding – has a very intuitive user interface even if the things going on “under the hood” are quite complex.

I tried to find a similar software like FocusMax but at that time I did not succeed. After some time a vision started to form: Write a telescope autofocus software which is

  1. Easy to use
  2. Works with Linux (+ maybe Windows, later)
  3. Is free for everybody and open-source

This is how the “FoFi” project started.

That said, I thought about a possible architecture and also about the different functions the program would have to perform. Each of the core functions turned out to be an interesting project by itself. So I decomposed the project into “sub projects” and first started to look at each of them separately.

Project decisions


I think one reason why the famous, free guiding software OpenPHD is so successful is it’s ease of use (and of course because it works). One major goal of this project is to provide a software which also is easy to understand and to use – but for focusing. In other terms that means that technical details and complexity should be kept away from the user where possible.

Target platform

The first target platform for this software is Linux. But since probably a lot of people out there use Windows, it would also be quite beneficial to supported it later. To keep that door open I made some decisions right at the beginning of the project. It has implications on the selection of the UI framework, the build environment and also the way(s) how the software communicates with the astronomy hardware.

In short, I made the following decisions:

  1. Use of the QT framework since it supports development of applications for Linux and Windows (wxWidgets was the other candidate).
  2. Creation of technology independent interfaces for the relevant astronomy devices (camera, filter, focus, …). For Linux, I use INDI to implement the hardware communication, for Windows ASCOM may be an option.
  3. I chose CMake as a build tool since it in principle supports Linux and Windows (and probably even more).

Different ways of usage

As already described above, focus finder should be able to run in different modes – UI mode and command-line mode. To accomplish this, I try to separate the “business logic” from the UI logic as good as possible. This way it is in principle possible to use a graphical QT interface but also a command-line interface to control the focus finder core.

It may also be possible to run a web-based UI against the actual focus finder core. With respect to the web UI, there are some interesting projects around which allow the integration of a REST API into a C++ application (see here and here). This way the application can run on a computer which is close to the telescope (for example a Raspberry PI) and the UI (or any service which wants to query or control the focus finder) can run theoretically everywhere.


Communication with astronomy devices on Linux

I took some time to look closer to the INDI project which is a kind of middleware for controlling astronomical devices under Linux. I would say it is more or less the equivalent to ASCOM in the Windows world.

When I started with INDI the amount of available drivers was quite limited. Luckily there were drivers available for the devices which I wanted to use right from the beginning.

There was just a minor problem with the Atik 383L+ driver. When transmitting a full resolution frame from the camera via USB to my PC the system always responded with a timeout. When reducing the frame size it worked. I sent a mail to “Atik Cameras” and asked for help. After explaining the problem I received an answer from Steve Chambers – the CEO – who offered me to have a look into the source code of the driver – I just had to sign an NDA (at that point in time I think the driver was not yet open-source). This way I was able to debug my problem. I finally found it, fixed it and sent back my code change to “Atik-Cameras”.

From this point in time I was able to use all my equipment with INDI. Meanwhile the INDI community and the list of supported devices grew rapidly.

Recognizing potential stars

SNR value for different star signals

Focus finder tries to determine the best focus based on the star diameter. Therefore you first have to select a star which focus finder should use. This selection may also happen automatically, later. Another idea is to use multiple “focus stars” at once, later. Anyway, at first, when the user clicks on a point in the image, the software needs to find out if there actually is something that could be a star. What sounds very simple turned out to be a small challenge. In the end I decided to use Signal-to-Noise Ratio (SNR) to solve this. This is not a 100% reliable detection mechanism of a star (e.g. the user could also click on a galaxy or parts of it) – but to decide if a normal star is present or not it is sufficient.
The SNR also turned out to be useful to check for each new frame if the star disappeared (e.g. because of clouds) and if the software can proceed or not.

In this article I tried different ways to calculate the SNR. The article also shows a (very) short test program written in C++. For doing all the image operations I decided to us the free and very nice C++ template library CImg.

How to make sure that the selected region only contains one star

To make the program more “intelligent”, the selected area should be validated before the focus finder algorithm runs. It should be detected that

  • There is exactly one star in the selected area
  • That the star is not fully saturated (also not in case of best focus)

However, this recognition does not work reliably, yet. Depending on the input star, the image binarization using the max entropy method in combination with the star clustering algorithm sometimes detects multiple stars even if there is just one in the given area. But for having a first working version of the software this feature is not critical.

Determining the star centroid

Calculating the Intensity Weighted Center (IWC) determines the star centroid.

The first step before even thinking about calculating a “star diameter” it is necessary to find the centroid of a star. I did not find so many algorithms for explicitly this purpose. However, conceptually there is a more generic approach from physics : “center of gravity” or “COG”. Image processing uses the same concept – just under a different name: Intensity Weighted Center (IWC). I found a paper which explains further details. In this article I published a C++ code snippet which calculates the centroid using the IWC method. In addition I did some experiments to determine the centroid with sub-pixel accuracy. However, for this piece of software this does not seem to bring any additional value. This approach can of course be problematic when the user selects a region with multiple stars or a region which shows a nebula or a galaxy.

Measuring the image focus

HFD values for different focuses

I read more and more about the different ways to measure the focus of an image. In principle one concentrates on one or more stars and measures their “width”. There are basically two indicators which measure the focus quality: The Half-Flux Diameter (HFD) and the Full Width Half Maximum (FWHM). Some time ago I published an article about the calculation of the HFD and also an article about the calculation of the FWHM. Both articles also show a small C++ code snippet which show the essential calculation steps.
In the end it turned out that both indicators may work in reality. However, determining the FWHM requires a curve fitting which is computationally more expensive and probably less robust. In the end, the user should be able to choose between the two measures. Maybe it also makes sense to combine both values.

Matching the focus curve

One more task was to find a geometry which best matches the shape of a focus curve. It turned out that the focus curve can look different depending on the different components telescope, camera and focuser. This was discussed in 2017 here. So the user should be able to choose between different curve types depending on the equipment.

For the FWHM calculation I came across the topic of curve fitting and the Levenberg Marquart algorithm. That turned out to be very useful now since again a curve fitting was required, now.

So far I implemented two different curve geometries – a parabolic and a hyperbolic curve. In this article I show how to match data points against a hyperbolic curve using the Levenberg Marquart algorithm with an implementation of the free GSL library.

Detecting outliers

Matching a Gaussian curve with outlier detection enabled. The yellow circles indicate two data points which have been identified as outliers.

There is a need for outlier detection. Why? Because in reality it turned out that collected data can contains a lot noise (what a surprise…). This can be the normal white noise but also – especially for HFD / FWHM values – the seeing which makes the values jump. This makes the curve matching more difficult in case there are values which are far off the actually expected range. In the end those outliers can lead to wrong results.

There are many methods and algorithms around – and especially with the hype around BigData the topic of outlier detection got another boost. On the Oracle website I found a short description of some simple methods to detect outliers.

The solution I am using here so far is the calculation of the 4th quantile of the residuals. This value is then multiplied by an “acceptance factor” (by default case 1.5). A data point counts as an outlier in case the residual is greater than this value. The focus finder then repeats the curve matching without the detected outliers. In case of new outliers, focus finder repeats the process described above. This continues until focus finder reaches a maximum number (given by the configuration). The default boundary is 15% of the data points. In this case the curve matching fails.

I am sure there are more advanced methods but for now this simple implementation does a pretty good job. Later the user may be able to choose between different outlier detection algorithms.

Automatic star recognition

With all those proof-of-concepts I still did not have any useful program. I now started to write some code to bring all the different pieces together. I tested with the INDI simulators but then came to a point where I had to do some important tests with real hardware. It turned out that the calculation of the HFD does not work correctly with the INDI CCD Simulator. As far as I understand it, the emulated star does not behave properly when the focus changes – i.e. the brightness of the pixels does not increase as expected when the focus improves. This results in strange HFD values.

However, some time ago I sent all my equipment to La Palma and did not have it available for testing. Therefore I decided to write a small program instead, which combines most of the different components without the requirement of having a hardware available. The result of this was an “automatic star recognizer”.

The “star recognizer” identifies all stars above a certain threshold and calculates centroid and HFD.

For just a few lines of code the software worked pretty well. Some time later I received an e-mail from Dan Gray from who somehow stumbled upon my website and found some of my code useful to develop his own autofocus software. This way some lines of my code made it into his observatory. I was a little proud when I heard about that 🙂 He published a screen capture video which shows the final result.

Next steps

The following steps are still necessary to have a first stable version which contains the base features – and thus make it “usable”:

  • Load & store settings and calibration data (some things are still hard-coded)
  • Improve the error handling when a real hardware is used
  • Finish the focus curve recording dialog
  • Using recorded focus curve parameters when user presses “Find focus”
  • Implement averaging of multiple focus curves
  • Implement the filter control

I will continue working on this project and post some updates from time to time. As mentioned before, if you think this project holds some potential and may be useful for you, or if you work on a similar project, I would be happy if you get in contact with me 🙂

Clear skies!

Last updated: June 9, 2022 at 23:51 pm


  1. Hi
    I’ve had issues with getting focus on my telescope rig, (I’m old with dodgy eyes, which probably doesn’t help!) I recently came across your FoFi tool and thought to give it a try.

    I’ve had a few issues with the cmake/build but mainly due to missing/incorrect libraries on my part: However, I’m now unable to get the final build to work. I get the following error but I’m not sure how to address it

    ***********************ERROR LOG STARTS **************************************
    /usr/bin/make -f source/focus_finder/gui/CMakeFiles/focus_finder_gui.dir/build.make source/focus_finder/gui/CMakeFiles/focus_finder_gui.dir/depend
    make[2]: Entering directory ‘/home/pi/focus_finder/build’
    make[2]: *** No rule to make target ‘../resources/mIconCollapse.svg’, needed by ‘source/focus_finder/gui/focus_finder_gui_autogen/7BV4YJK25R/qrc_res.cpp’. Stop.
    make[2]: Leaving directory ‘/home/pi/focus_finder/build’
    make[1]: *** [CMakeFiles/Makefile2:201: source/focus_finder/gui/CMakeFiles/focus_finder_gui.dir/all] Error 2
    make[1]: Leaving directory ‘/home/pi/focus_finder/build’

    ************************************END ERROR LOG************************

    This is on a rasperry pi 400 under Raspbian. Hope that you can find the time to advise! Thanks

    1. Hi Steve,

      sorry for my very late response. When I got your message I was on holiday and when I came back I simply forgot to answer.
      I think the problem you experienced is due to a missing file in the repository. I meanwhile added the missing file.
      I never compiled the code on a Raspberry PI but I think it would actually be a good idea (currently I unfortunately face a few problems with boost and the latest g++/gcc).

      However, I am sorry to say that “FoFi” is not yet ready to get operational. At the moment it is still in a experimental stage.
      For example there are currently still hard coded values in the code which just fit my own equipment. Furthermore parts of the UI
      are not yet functional – only the input masks exist but not more. I published the code since parts of the code might still be
      helpful to some developers who write code in the field of astronomy and image processing.
      I plan to continue developing FoFi but there is no schedule or date I can promise.

      All the best and clear skies

  2. Hn Carston;

    I found the series of articles on autofocus very interesting and follows many of the techniques I have used in my own autofocus software pipeline. In the FoFi article you mention outlier rmoval based on residuals, which I was not doing. However, you don’t appear to define how you calculate the residuals in the article. Are you using a sigma based on a Gaussian 2D fit (FWHM) or sigma based off the HFD (ideal Gaussian). Do you then take the background plus gaussian as the model to subtract from the actual image pixel values and get the residuals?

      1. Hi Pete,

        in the meantime I had a look at my code and I think I remember what I did. Currently I get the residual for each data point by simply calculating the difference between the y-value of that point and the corresponding y-value of the respective approximated curve (the curve is approximated with the Levenberg Marquart algorithm using all the dapa points). In my case the type of the appriximated curve is either a gaussian curve (in case of an FWHM calculation) or a hyperbolic curve (in case of a “focus” curve). In pseudo code it looks like this:

        auto residualFunction = [&](const PointFT &p) { return std::fabs(dataPoint.y() - curveFunction(dataPoint.x(), curveParameters)); };

        There may be residual functions which fit better for the purpose but for now I decided to keep it simple.

        In order to decide if a data point should be considered as an outlier, I currently use the 3rd quartile (Q3). Basically, the code looks a t all the residuals

        In addition I introduced an “outlierBoundaryFactor” to have a bit of control. This value defaults to 1.5. This is the respective pseudo code:

        float outlierBoundary = outlierBoundaryFactor * calculateThirdQuartile(residuals)

        Each data points for which the respective residual exceeds the calculated “outlierBoundary” are removed, and the curve fit is re-executed. This process is repeated until no more outliers exist. As you can imagine this approach holds a couple of pitfalls and I am not really happy with it. It is my first idea to deal with the problem. I actually have yet not done more research on the “outlier removal” topic. Any ideas, improvements or hints from your end are of course very welcome.

Leave a Reply

Your email address will not be published. Required fields are marked *