Here’s some cool software that’s free and open source, Surveyors Applications for Least Squares Adjustment (SALSA). It was developed by the Applied Research Facility at the University of Texas, Austin.

It was funded and supported by the National Geospatial-Intelligence Agency. It can adjust GNSS observation vectors and standard terrestrial observations .

I’ve downloaded the software and manual but really haven’t had an opportunity to start using; Metashape is taking up most of my free time

Pretty cool software, cheaper than MicroSurvey’s Starnet adjustment package. It would be cool if Emlid would incorporate this into Emlid Studio !


Adding SALSA to Emlid Studio would be great! You have my vote of support.

1 Like

Will definitely test it in my next adjustment task. I knew about GNU gama also, but was never able to use it without a system error…

1 Like

Hi @azevo_ca,

Thanks for your suggestion! I’ll pass it on to the team for further discussion.

1 Like

I had the opportunity to use SALSA this week on a big network. 200+ GNSS baselines for 35 points.
It worked smooth :slight_smile: sub-centimeter residuals everywhere.

I love the ability to export the visual elements like points, baselines and ellipses in kmz so I can play with it in QGIS when doing my job report.


Really nice finding !

Oh, btw, every RINEX data has been collected with RS2s devices. Another proof that you can do geodetic work with those little pieces of magic :wink:

I did the PPK with WaSoft instead of Emlid Studio for convenience. Considering the big number of baselines, I prefer to use a command line interface such as WaSoft.


I tried open salsa a week ago, but gave up. Does it just accept Rinex, or?

It does not read RINEX as it is really just for network adjustment. So you have to do the PPK with the RINEX files beforehand, with Emlid Studio for example. Salsa reads a text file with baseline components and the related variance-covariance matrix. Basically for GNSS baselines it is a list of blocks like this

DXYZ C1 C10 264.10290 -64.24400 -241.39550 m ...
          3.000e-03          0.000e+00          0.000e+00 ...
                             4.000e-03          0.000e+00 ...

I build this file with a small script reading my baselines results and converting them into this format.


Would you be willing to share the script (and some instructions, if needed) ?


Me too please… I’ve installed and reviewed the manual. It’s kind of difficult to understand. Kinda like Javad’s gear when we first bought it.


The example I use below is a minimal network with 3 baselines, 1 reference point and 2 points to determine. The workflow would be as follows.

  1. Do all your PPK work in Emlid Studio with the following settings.

Name the output file correctly with the format BASE_ROVER, like here CHTL is the base and C8 is the rover.

Select the output format to be ECEF-X/Y/Z.

You then have a bunch of POS files. Put them all in a single folder named as you wish, for example “my_pos_files_folder”. DO NOT put the *_events.pos in there, for scripting convenience. In my example, we have 3 baselines so 3 POS files.

  1. After installation, open SALSA and create a new project in Project > New. Input the name and path to the project file. It created a .proj file that you can open with a Notepad.

  2. Put the focus in the project window and add the reference points in the project in Record > Insert > and POSC for cartesian inputs, or POSG for geographic inputs.
    Example with the reference point CHTL here.

The point is added in the project window.

Click on Project > Save. You also see the info of this reference point in the proj file, in a Notepad.

  1. Add the baselines in the project. As it needs DXYZ format, that is where the script become handy. You can run the script below to generate a txt file with the concatenated information readable by SALSA. Just edit line 85 to point on your POS files folder.
# -*- coding: utf-8 -*-
Created on Sun Jul 16 11:19:11 2023

@author: Florian Birot - FB Geomatics

import os
import re # to do some cool regex :-)
import pandas as pd
from datetime import datetime

def posfile2DXYZ(file_in):
    "Reads the baseline info of a pos file made by Emlid Studio and return a DXYZ text block readable by SALSA"
    filename = os.path.split(file_in)
    file, ext = os.path.splitext(filename[-1])
    base_name, rover_name = file.split('_') # works only if you respect the POS file naming convention BASE_ROVER.pos
    with open(file_in) as f:
        baseline_data ='\n')[:-1]
    # deal with the base information
    ref_pos = next(l for l in baseline_data if l.startswith('% ref pos'))
    # ref_lat, ref_long, ref_h = [float(c) for c in re.findall(r"[-+]?(?:\d*\.*\d+)", ref_pos)]
    # ref_x, ref_y, ref_z = geo2ecef(ref_long, ref_lat, ref_h)
    ref_x, ref_y, ref_z = [float(c) for c in re.findall(r"[-+]?(?:\d*\.*\d+)", ref_pos)]
    # deal with the rover information
    rov_data = baseline_data[-1]
    computed_at = datetime.strptime(rov_data[:23], "%Y/%m/%d %H:%M:%S.%f")
    rov_x, rov_y, rov_z, q, ns, sdx, sdy, sdz, sdxy, sdyz, sdzx, age, ratio = [float(c) for c in re.findall(r"[-+]?(?:\d*\.*\d+)", rov_data[23:])]
    # construct a baseline Pandas Series for convenience
    s = pd.Series({
            "computed_at": computed_at,
            "base": base_name,
            "rover": rover_name,
            "dx": rov_x - ref_x,
            "dy": rov_y - ref_y,
            "dz": rov_z - ref_z,
            "cx": '%.3e' % sdx**2,
            "cy": '%.3e' % sdy**2,
            "cz": '%.3e' % sdz**2,
            "cxy": '%.3e' % sdxy**2,
            "cyz": '%.3e' % sdyz**2,
            "cxz": '%.3e' % sdzx**2,
            "l_cx": "         ",
            "l_cy": "                            ",
            "l_cz": "                                               ",
            "l_cxy": "          ",
            "l_cyz": "          ",
            "l_cxz": "          "

    # construct the text block
    line1 = f"DXYZ {s.base} {s.rover} {s.dx:.5f} {s.dy:.5f} {} m ...\n"
    line2 = f"{s.l_cx}{}{s.l_cxy}{s.cxy}{s.l_cxz}{s.cxz} ...\n"
    line3 = f"{s.l_cy}{}{s.l_cyz}{s.cyz} ...\n"
    line4 = f"{s.l_cz}{}\n"
    return line1 + line2 + line3 + line4

def buildDXYZ_from_folder(folder_in, file_out):
    "builds a TXT file with DXYZ blocks readable by SALSA from the folder where POS files are stored"
    pos_files = [os.path.join(folder_in,f) for f in os.listdir(folder_in) if f.endswith('.pos')]
    # clear
    if os.path.isfile(file_out):
    # writes content
    with open(file_out, "a") as f:
        for file in pos_files:

# set the pos_folder parameter with your local path to the folder where pos files are
pos_folder = "my_pos_files_folder" # do not put *_events.pos in this folder

# and run the function with this folder and the output file
buildDXYZ_from_folder(pos_folder, "dxyz_blocks.txt")

That created the file dxyz_blocs.txt, that you can open in a Notepad.

  1. Open the SALSA .proj file in a notepad. Copy the content of the script output file and paste it to append it to the .proj file. Then Save the file.

  2. In SALSA, click on Project > Reload. Your baselines now appear in the project.

  3. Generate the initial positions by clicking on Project > Generate initial position. You then see every point in the map on bottom right view.


  1. Do the network adjustement by clicking on Project > Calculate adjustment.

  2. That created a CSV file with the projet name. The final coordinates are in there.

I won’t go into the details of analyzing the residuals and chi-squared test here.


Thanks so much Florian ! I’ll use your directions and use Emlid first. I want to try my unadjusted post processed data from Justin 3 and compare the results.

Thanks again for getting us started using SALSA ! As I’ve said before, this would be excellent if the Emlid developers would incorporate SALSA into Emlid Studio !

You the man !


A simple explanation of least square.
Good simple explanation of least square

Btw, this tool reminds me of gnss solutions…


Thanks for the kind words :grinning:

I went into the hustle of doing GPS baselines adjustments with my own script last year, so it was probably easier to grasp salsa because of that experience. This is a really nice tool. Also compatible with Geolab files.

Definitely agree with you on the Emlid Studio implementation :wink:


Agree, this is top notch @Florian . Taking time to show us this great tool.
I would love to learn more about it.
I read that it supports OPUS native format for PPP, but could not find if it supports NrCan, do you know anything about that?
Are there any guidelines for max baseline in a network adjustment?


All there is in the user manual is a mention to “OPUS Precise Point Positioning format, typically .opus”, page 213. I haven’t read anything else about PPP in the user manual. I guess the OPUS results can be used to set coordinates with variance to points of the network that have been post-processed with it. Typically in a POSC or POSG entry, you can apply uncertainties on the coordinates. This way, you take into account the accuracy of the PPP results. In a similar fashion, one could convert the NRCan results into a SALSA readable text block.

You mean max number or max length ? I don’t think there is in both case. The longer the baseline the riskier it is to have big residuals because PPK has bigger chance to be poor. But that’s a PPK limitation, not a network adjustment one itself.

1 Like

Ah, sorry. Meant max baseline length.
When I think about it, the recommende max baseline for any receiver still applies I guess. But your final numbers should look better with a network adjustment.
I was thinking adjusting with multiple baselines vs a single baseline would yield some extra leeway, thus pushing the baseline length a bit further.

1 Like

This topic was automatically closed 100 days after the last reply. New replies are no longer allowed.