New Upstream Snapshot - openvlbi

Ready changes

Summary

Merged new upstream version: 2.23.4+git20230124.1.76ae69c (was: 1.23.5).

Diff

diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
deleted file mode 100644
index 00c0617..0000000
--- a/.github/FUNDING.yml
+++ /dev/null
@@ -1,12 +0,0 @@
-# These are supported funding model platforms
-
-github: ahp-electronics # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
-patreon: # Replace with a single Patreon username
-open_collective: # Replace with a single Open Collective username
-ko_fi: # Replace with a single Ko-fi username
-tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
-community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
-liberapay: # Replace with a single Liberapay username
-issuehunt: iliaplatone # Replace with a single IssueHunt username
-otechie: # Replace with a single Otechie username
-custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
diff --git a/.github/docker/Dockerfile.ubuntu b/.github/docker/Dockerfile.ubuntu
deleted file mode 100644
index d547251..0000000
--- a/.github/docker/Dockerfile.ubuntu
+++ /dev/null
@@ -1,7 +0,0 @@
-FROM debian:latest
-
-WORKDIR /tmp
-
-RUN apt-get update && apt-get -y upgrade && apt-get install -y \
-    sudo git \
-    build-essential zlib1g-dev \
diff --git a/.github/workflows/default.yml b/.github/workflows/default.yml
deleted file mode 100644
index e9b1fdf..0000000
--- a/.github/workflows/default.yml
+++ /dev/null
@@ -1,41 +0,0 @@
-
-name: Linux
-on: [push, pull_request, workflow_dispatch]
-
-jobs:
-  build:
-    runs-on: ubuntu-latest
-
-    strategy:
-      fail-fast: false
-
-    steps:
-      - name: Get Sources
-        uses: actions/checkout@v2
-        with:
-          path: 'OpenVLBI'
-
-      - name: Build & Install
-        run: |
-            cd OpenVLBI
-            sudo ./scripts/build.sh
-
-      - name: Run Tests
-        run: |
-            cd OpenVLBI
-            mkdir -p tests
-            scripts/json_to_image.sh scripts/coverage.json tests/synthesis_delay_coverage
-            scripts/json_to_image.sh scripts/raw.json tests/synthesis_delay_raw
-            scripts/json_to_image.sh scripts/idft.json tests/synthesis_delay_idft
-
-      - name: Debs
-        uses: actions/upload-artifact@v2
-        with:
-          name: packages
-          path: OpenVLBI/packages
-
-      - name: Tests
-        uses: actions/upload-artifact@v2
-        with:
-          name: tests
-          path: OpenVLBI/tests
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
deleted file mode 100644
index 67b4b20..0000000
--- a/.github/workflows/docker.yml
+++ /dev/null
@@ -1,27 +0,0 @@
-name: Docker
-on:
-  workflow_dispatch:
-
-jobs:
-  build:
-    runs-on: ubuntu-latest
-
-    strategy:
-      fail-fast: false
-      matrix:
-        container: ["ubuntu"]
-
-    steps:
-      - name: Login to Registry
-        uses: docker/login-action@v1
-        with:
-          registry: ghcr.io
-          username: ${{ github.repository_owner }}
-          password: ${{ secrets.GITHUB_TOKEN }}
-
-      - name: Build and push
-        uses: docker/build-push-action@v2
-        with:
-          file: ./.github/docker/Dockerfile.${{ matrix.container }}
-          push: true
-          tags: ghcr.io/${{ github.repository_owner }}/${{ matrix.container }}
diff --git a/CITATION.cff b/CITATION.cff
index 4c7b5d5..f671ca6 100644
--- a/CITATION.cff
+++ b/CITATION.cff
@@ -5,7 +5,7 @@ authors:
   given-names: "Ilia"
   orcid: "https://orcid.org/0000-0002-8527-4611"
 title: "OpenVLBI"
-version: 1.23.5
+version: 1.23.6
 doi: 10.5281/zenodo.5775142
 date-released: 2021-12-24
 url: "https://github.com/iliaplatone/OpenVLBI"
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ad67866..9872328 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -18,7 +18,7 @@ option(WITH_JSON_SERVER "Add JSON server for OpenVLBI" ON)
 
 set (VLBI_VERSION_MAJOR 1)
 set (VLBI_VERSION_MINOR 23)
-set (VLBI_VERSION_RELEASE 5)
+set (VLBI_VERSION_RELEASE 6)
 set (VLBI_VERSION_STRING "${VLBI_VERSION_MAJOR}.${VLBI_VERSION_MINOR}.${VLBI_VERSION_RELEASE}")
 set(LIB_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}")
 set(DATA_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}")
@@ -68,6 +68,7 @@ SET(dsp_C_SRC
     ${CMAKE_CURRENT_SOURCE_DIR}/dsp/stats.c
     ${CMAKE_CURRENT_SOURCE_DIR}/dsp/buffer.c
     ${CMAKE_CURRENT_SOURCE_DIR}/dsp/convolution.c
+    ${CMAKE_CURRENT_SOURCE_DIR}/dsp/recon.c
     ${CMAKE_CURRENT_SOURCE_DIR}/dsp/signals.c
     ${CMAKE_CURRENT_SOURCE_DIR}/dsp/stream.c
     ${CMAKE_CURRENT_SOURCE_DIR}/dsp/fits.c
diff --git a/README.md b/README.md
index b085f2f..403f8dd 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
 [![CircleCi](https://circleci.com/gh/iliaplatone/OpenVLBI/tree/master.svg?style=shield)](https://circleci.com/gh/iliaplatone/OpenVLBI/?branch=master)
 [![Linux](https://github.com/iliaplatone/OpenVLBI/actions/workflows/default.yml/badge.svg)](https://github.com/iliaplatone/OpenVLBI/actions/workflows/default.yml)
 [![License: LGPL v3](https://img.shields.io/badge/License-LGPLv3-blue.svg)](https://www.gnu.org/licenses/lgpl-3.0)
-[![DOI](https://zenodo.org/badge/340341236.svg)](https://zenodo.org/badge/latestdoi/340341236)
+[![DOI](https://img.shields.io/badge/DOI-10.5281/zenodo.340341236-blue.svg)](https://zenodo.org/badge/latestdoi/340341236)
 
 
 OpenVLBI is an open source library and application suite for astronomical interferometry.
diff --git a/debian/changelog b/debian/changelog
index 21a9cc8..d734d3e 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,15 @@
+libopenvlbi (2.23.4+git20230124.1.76ae69c-1) UNRELEASED; urgency=low
+
+  * New upstream snapshot.
+
+ -- Debian Janitor <janitor@jelmer.uk>  Wed, 08 Feb 2023 16:51:19 -0000
+
+libopenvlbi (1.23.6) unstable; urgency=medium
+
+  *  New upstream release
+
+ -- Ilia Platone <info@iliaplatone.com>  Mon, 24 Jan 2023 00:00:00 +0100
+
 openvlbi (1.23.5-1) unstable; urgency=medium
 
   * New upstream release.
diff --git a/dsp/align.c b/dsp/align.c
index 15e9eb1..1656f83 100644
--- a/dsp/align.c
+++ b/dsp/align.c
@@ -1,4 +1,3 @@
-
 /*
 *   DSP API - a digital signal processing library for astronomy usage
 *   Copyright © 2017-2021  Ilia Platone
@@ -21,55 +20,136 @@
 #include "dsp.h"
 #include <time.h>
 
-static int dsp_qsort_double_desc (const void *arg1, const void *arg2)
+typedef struct {
+    double delta;
+    double *diff;
+} delta_diff;
+
+static int dsp_qsort_delta_diff_desc (const void *arg1, const void *arg2)
 {
-    double* a1 = (double*)arg1;
-    double* a2 = (double*)arg2;
-    return ((*a1) < (*a2) ? 1 : -1);
+    delta_diff* a1 = (delta_diff*)arg1;
+    delta_diff* a2 = (delta_diff*)arg2;
+    return ((*a1).delta < (*a2).delta ? 1 : -1);
 }
 
-static int dsp_qsort_star_diameter_desc(const void *arg1, const void *arg2)
+static int dsp_qsort_delta_diff_asc (const void *arg1, const void *arg2)
 {
-    dsp_star* a = (dsp_star*)arg1;
-    dsp_star* b = (dsp_star*)arg2;
-    if(a->diameter < b->diameter)
-        return 1;
-    return -1;
+    delta_diff* a1 = (delta_diff*)arg1;
+    delta_diff* a2 = (delta_diff*)arg2;
+    return ((*a1).delta > (*a2).delta ? 1 : -1);
 }
 
-static int dsp_qsort_double_asc (const void *arg1, const void *arg2)
+static int dsp_qsort_triangle_delta_desc(const void *arg1, const void *arg2)
+{
+    dsp_triangle* a = (dsp_triangle*)arg1;
+    dsp_triangle* b = (dsp_triangle*)arg2;
+    int ret = 0;
+    int s = 0;
+    for(s = 0; s < (*a).stars_count; s++)
+        ret |= ((*a).sizes[s] < (*b).sizes[s]);
+    return (ret ? 1 : -1);
+}
+
+static int dsp_qsort_triangle_delta_asc(const void *arg1, const void *arg2)
 {
-    double* a1 = (double*)arg1;
-    double* a2 = (double*)arg2;
-    return ((*a1) > (*a2) ? 1 : -1);
+    dsp_triangle* a = (dsp_triangle*)arg1;
+    dsp_triangle* b = (dsp_triangle*)arg2;
+    int ret = 0;
+    int s = 0;
+    for(s = 0; s < (*a).stars_count; s++)
+        ret |= ((*a).sizes[s] > (*b).sizes[s]);
+    return (ret ? 1 : -1);
+}
+
+static int dsp_qsort_triangle_diameter_desc(const void *arg1, const void *arg2)
+{
+    dsp_triangle* a = (dsp_triangle*)arg1;
+    dsp_triangle* b = (dsp_triangle*)arg2;
+    int ret = 0;
+    int s = 0;
+    for(s = 0; s < (*a).stars_count; s++) {
+        ret |= ((*a).stars[s].diameter < (*b).stars[s].diameter);
+        ret |= ((*a).stars[s].peak < (*b).stars[s].peak);
+        ret |= ((*a).stars[s].flux < (*b).stars[s].flux);
+    }
+    return (ret ? 1 : -1);
+}
+
+static int dsp_qsort_triangle_diameter_asc(const void *arg1, const void *arg2)
+{
+    dsp_triangle* a = (dsp_triangle*)arg1;
+    dsp_triangle* b = (dsp_triangle*)arg2;
+    int ret = 0;
+    int s = 0;
+    for(s = 0; s < (*a).stars_count; s++) {
+        ret |= ((*a).stars[s].diameter > (*b).stars[s].diameter);
+        ret |= ((*a).stars[s].peak > (*b).stars[s].peak);
+        ret |= ((*a).stars[s].flux > (*b).stars[s].flux);
+    }
+    return (ret ? 1 : -1);
+}
+
+static int dsp_qsort_star_diameter_desc(const void *arg1, const void *arg2)
+{
+    dsp_star* a = (dsp_star*)arg1;
+    dsp_star* b = (dsp_star*)arg2;
+    return ((*a).diameter < (*b).diameter ? 1 : -1);
 }
 
 static int dsp_qsort_star_diameter_asc(const void *arg1, const void *arg2)
 {
     dsp_star* a = (dsp_star*)arg1;
     dsp_star* b = (dsp_star*)arg2;
-    if(a->diameter > b->diameter)
-        return 1;
-    return -1;
+    return ((*a).diameter > (*b).diameter ? 1 : -1);
+}
+
+static int dsp_qsort_double_desc (const void *arg1, const void *arg2)
+{
+    double* a = (double*)arg1;
+    double* b = (double*)arg2;
+    return ((*a) < (*b) ? 1 : -1);
+}
+
+static int dsp_qsort_double_asc (const void *arg1, const void *arg2)
+{
+    double* a = (double*)arg1;
+    double* b = (double*)arg2;
+    return ((*a) > (*b) ? 1 : -1);
 }
 
 static double calc_match_score(dsp_triangle t1, dsp_triangle t2, dsp_align_info align_info)
 {
+    int x = 0;
     int d = 0;
-    double err = fabs((t1.index - t2.index)/t1.index);
-    for(d = 1; d < t1.dims; d++) {
-        err += fabs(t2.sizes[d]-t1.sizes[d]*align_info.factor[d-1])/t1.sizes[d];
-        err += fabs((t2.ratios[d]-t1.ratios[d])/t1.ratios[d]);
-        err += fabs(t2.stars[d].diameter-t1.stars[d].diameter*align_info.factor[d-1])/t1.stars[d].diameter;
+    int div = 0;
+    int stars_count = fmin(t1.stars_count, t2.stars_count);
+    int num_baselines = stars_count*(stars_count-1)/2;
+    double err = 0.0;
+    err += fabs((t2.index-t1.index)/t1.index);
+    div++;
+    for(d = 0; d < align_info.dims; d++) {
+        for(x = 0; x < stars_count; x++) {
+            err += fabs(t2.stars[x].diameter/align_info.factor[d]-t1.stars[x].diameter)/t1.stars[x].diameter;
+            div++;
+            err += fabs(t2.stars[x].flux/align_info.factor[d]-t1.stars[x].flux)/t1.stars[x].flux;
+            div++;
+            err += fabs(t2.stars[x].peak/align_info.factor[d]-t1.stars[x].peak)/t1.stars[x].peak;
+            div++;
+        }
+        for(x = 0; x < num_baselines; x++) {
+            err += fabs(t2.sizes[x]/align_info.factor[d]-t1.sizes[x])/t1.sizes[x];
+            div++;
+        }
     }
-    return err / t1.dims;
+    return err / div;
 }
 
 dsp_align_info *dsp_align_fill_info(dsp_triangle t1, dsp_triangle t2)
 {
     dsp_align_info *align_info = (dsp_align_info*)malloc(sizeof(dsp_align_info));
-    int dims = t1.dims - 1;
+    int dims = t1.dims;
     int d, x;
+    int num_baselines = t1.stars_count*(t1.stars_count-1)/2;
     align_info->dims = dims;
     align_info->center = (double*)malloc(sizeof(double)*(dims));
     align_info->offset = (double*)malloc(sizeof(double)*(dims));
@@ -92,10 +172,10 @@ dsp_align_info *dsp_align_fill_info(dsp_triangle t1, dsp_triangle t2)
             if(align_info->radians[d] < 0.0)
                 align_info->radians[d] += M_PI*2.0;
         }
-        for(x = 0; x < t2.dims; x++) {
+        for(x = 0; x < num_baselines; x++) {
             align_info->factor[d] += t2.sizes[x] / t1.sizes[x];
         }
-        align_info->factor[d] /= t2.dims;
+        align_info->factor[d] /= num_baselines;
     }
     align_info->score = calc_match_score(t1, t2, *align_info);
     return align_info;
@@ -114,100 +194,106 @@ void dsp_align_free_triangle(dsp_triangle *t)
     free(t);
 }
 
-dsp_triangle *dsp_align_calc_triangle(dsp_star* stars)
+dsp_triangle *dsp_align_calc_triangle(dsp_star* stars, int num_stars)
 {
     int x;
+    int y;
     int d;
     dsp_triangle *triangle = (dsp_triangle*)malloc(sizeof(dsp_triangle));
-    triangle->dims = stars[0].center.dims+1;
-    double *delta = (double*)malloc(sizeof(double)*triangle->dims);
-    double **diff = (double**)malloc(sizeof(double*)*triangle->dims);
-    triangle->sizes = (double*)malloc(sizeof(double)*triangle->dims);
-    triangle->ratios = (double*)malloc(sizeof(double)*triangle->dims);
-    triangle->stars = (dsp_star*)malloc(sizeof(dsp_star)*triangle->dims);
+    triangle->dims = stars[0].center.dims;
+    triangle->stars_count = num_stars;
+    int num_baselines = triangle->stars_count*(triangle->stars_count-1)/2;
+    delta_diff *deltadiff = (delta_diff*)malloc(sizeof(delta_diff)*num_baselines);
+    triangle->sizes = (double*)malloc(sizeof(double)*num_baselines);
+    triangle->ratios = (double*)malloc(sizeof(double)*num_baselines);
+    triangle->stars = (dsp_star*)malloc(sizeof(dsp_star)*triangle->stars_count);
     triangle->theta = (double*)malloc(sizeof(double)*(triangle->dims-1));
     triangle->index = 0.0;
-    qsort(stars, 3, sizeof(dsp_star), dsp_qsort_star_diameter_desc);
-    for(d = 0; d < triangle->dims; d++) {
-        diff[d] = (double*)malloc(sizeof(double)*stars[d].center.dims);
-        for(x = 0; x < triangle->dims-1; x++) {
-            diff[d][x] = stars[(d + 1) < triangle->dims ? d : 0].center.location[x]-stars[(d + 1) < triangle->dims ? (d + 1) : (triangle->dims - 1)].center.location[x];
-            delta[d] += pow(diff[d][x], 2);
+    qsort(stars, triangle->stars_count, sizeof(dsp_star), dsp_qsort_star_diameter_desc);
+    int idx = 0;
+    for(x = 0; x < triangle->stars_count; x++) {
+        for(y = x+1; y < triangle->stars_count; y++) {
+            deltadiff[idx].diff = (double*)malloc(sizeof(double)*triangle->dims);
+            for(d = 0; d < triangle->dims; d++) {
+                deltadiff[idx].diff[d] = stars[x].center.location[d]-stars[y].center.location[d];
+                deltadiff[idx].delta += pow(deltadiff[idx].diff[d], 2);
+            }
+            deltadiff[idx].delta = sqrt(deltadiff[idx].delta);
+            idx++;
         }
-        delta[d] = sqrt(delta[d]);
     }
-    for(d = 0; d < triangle->dims; d++) {
-        for(x = 0; x < stars[0].center.dims - 1; x++) {
-            if(d == 0) {
-                triangle->theta[x] = acos(diff[d][x] / delta[d]);
-                if(diff[d][x+1] < 0)
-                    triangle->theta[x] = M_PI*2.0-triangle->theta[x];
-            } else {
-                double index = acos(diff[d][x] / delta[d]);
-                if(diff[d][x+1] < 0)
-                    index = M_PI*2.0-index;
-                index -= triangle->theta[x];
-                if(index < 0)
-                    index += M_PI*2.0;
-                triangle->index += index;
-            }
+    qsort(deltadiff, num_baselines, sizeof(delta_diff), dsp_qsort_delta_diff_desc);
+    for(d = 0; d < triangle->dims-1; d++) {
+        triangle->theta[d] = acos(deltadiff[0].diff[d] / deltadiff[0].delta);
+        if(deltadiff[0].diff[d+1] < 0)
+            triangle->theta[d] = M_PI*2.0-triangle->theta[d];
+        for(x = 1; x < num_baselines; x++) {
+            double index = acos(deltadiff[x].diff[d] / deltadiff[x].delta);
+            if(deltadiff[x].diff[d+1] < 0)
+                index = M_PI*2.0-index;
+            index -= triangle->theta[d];
+            triangle->index += index;
         }
     }
-    for(d = 0; d < triangle->dims; d++) {
+    triangle->index /= (num_baselines+triangle->dims-2)*M_PI*2;
+    for(d = 0; d < triangle->stars_count; d++) {
         triangle->stars[d].diameter = stars[d].diameter;
+        triangle->stars[d].peak = stars[d].peak;
+        triangle->stars[d].flux = stars[d].flux;
+        triangle->stars[d].theta = stars[d].theta;
         triangle->stars[d].center.dims = stars[d].center.dims;
         triangle->stars[d].center.location = (double*)malloc(sizeof(double)*stars[d].center.dims);
-        triangle->sizes[d] = delta[d];
-        triangle->ratios[d] = delta[d] / delta[0];
         for(x = 0; x < triangle->stars[d].center.dims; x++) {
             triangle->stars[d].center.location[x] = stars[d].center.location[x];
         }
-        free(diff[d]);
     }
-    free(delta);
-    free(diff);
+    for(d = 0; d < num_baselines; d++) {
+        triangle->sizes[d] = deltadiff[d].delta;
+        triangle->ratios[d] = deltadiff[d].delta / deltadiff[0].delta;
+        free(deltadiff[d].diff);
+    }
+    free(deltadiff);
     return triangle;
 }
 
-int dsp_align_get_offset(dsp_stream_p stream1, dsp_stream_p stream2, double tolerance, double target_score)
+int dsp_align_get_offset(dsp_stream_p stream1, dsp_stream_p stream2, double tolerance, double target_score, int num_stars)
 {
     dsp_align_info *align_info;
     double decimals = pow(10, tolerance);
     double div = 0.0;
     int d, t1, t2, x, y;
     double phi = 0.0;
-    int dims = stream1->dims+1;
     double ratio = decimals*1600.0/div;
-    dsp_star *stars = (dsp_star*)malloc(sizeof(dsp_star)*dims);
+    dsp_star *stars = (dsp_star*)malloc(sizeof(dsp_star)*num_stars);
     for(d = 0; d < stream1->dims; d++) {
         div += pow(stream2->sizes[d], 2);
     }
     div = pow(div, 0.5);
     pwarn("decimals: %lf\n", decimals);
     target_score = (1.0-target_score / 100.0);
-    stream2->align_info.dims = 2;
+    stream2->align_info.dims = stream2->dims;
     stream2->align_info.triangles_count = 2;
     stream2->align_info.score = 1.0;
     stream2->align_info.decimals = decimals;
     pgarb("creating triangles for reference frame...\n");
     while(stream1->triangles_count > 0)
         dsp_stream_del_triangle(stream1, stream1->triangles_count-1);
-    for(x = 0; x < stream1->stars_count * (stream1->stars_count-stream1->dims) / (stream1->dims+1); x++) {
-        for(y = 0; y < stream1->dims+1; y++) {
+    for(x = 0; x < stream1->stars_count * (stream1->stars_count-num_stars+1) / 2; x++) {
+        for(y = 0; y < num_stars; y++) {
             stars[y] = stream1->stars[(x + y * (x / stream1->stars_count + 1)) % stream1->stars_count];
         }
-        dsp_triangle *t = dsp_align_calc_triangle(stars);
+        dsp_triangle *t = dsp_align_calc_triangle(stars, num_stars);
         dsp_stream_add_triangle(stream1, *t);
         dsp_align_free_triangle(t);
     }
     pgarb("creating triangles for current frame...\n");
     while(stream2->triangles_count > 0)
         dsp_stream_del_triangle(stream2, stream2->triangles_count-1);
-    for(x = 0; x < stream2->stars_count * (stream2->stars_count-stream2->dims) / (stream2->dims+1); x++) {
-        for(y = 0; y < stream2->dims+1; y++) {
+    for(x = 0; x < stream2->stars_count * (stream2->stars_count-num_stars+1) / 2; x++) {
+        for(y = 0; y < num_stars; y++) {
             stars[y] = stream2->stars[(x + y * (x / stream2->stars_count + 1)) % stream2->stars_count];
         }
-        dsp_triangle *t = dsp_align_calc_triangle(stars);
+        dsp_triangle *t = dsp_align_calc_triangle(stars, num_stars);
         dsp_stream_add_triangle(stream2, *t);
         dsp_align_free_triangle(t);
     }
@@ -217,18 +303,16 @@ int dsp_align_get_offset(dsp_stream_p stream1, dsp_stream_p stream2, double tole
     stream2->align_info.offset = (double*)malloc(sizeof(double)*stream2->align_info.dims);
     stream2->align_info.radians = (double*)malloc(sizeof(double)*(stream2->align_info.dims-1));
     for(t1 = 0; t1 < stream1->triangles_count; t1++) {
-        for(t2 = 0; t2 < stream2->triangles_count;) {
+        for(t2 = 0; t2 < stream2->triangles_count; t2++) {
             align_info = dsp_align_fill_info(stream1->triangles[t1], stream2->triangles[t2]);
             if(align_info->score < stream2->align_info.score) {
                 memcpy(stream2->align_info.center, align_info->center, sizeof(double)*stream2->align_info.dims);
                 memcpy(stream2->align_info.factor, align_info->factor, sizeof(double)*stream2->align_info.dims);
                 memcpy(stream2->align_info.offset, align_info->offset, sizeof(double)*stream2->align_info.dims);
-                memcpy(stream2->align_info.radians, align_info->radians, sizeof(double)*stream2->align_info.dims);
+                memcpy(stream2->align_info.radians, align_info->radians, sizeof(double)*(stream2->align_info.dims-1));
                 memcpy(stream2->align_info.triangles, align_info->triangles, sizeof(dsp_triangle)*2);
                 stream2->align_info.score = align_info->score;
                 stream2->align_info.decimals = decimals;
-            } else {
-                t2++;
             }
             free(align_info->center);
             free(align_info->factor);
@@ -243,13 +327,13 @@ int dsp_align_get_offset(dsp_stream_p stream1, dsp_stream_p stream2, double tole
     }
     phi = pow(phi, 0.5);
     stream2->align_info.err = 0xf;
-    if(stream2->align_info.score  < target_score)
+    if(floor(stream2->align_info.score * decimals)  < floor(target_score * decimals))
         stream2->align_info.err &= ~DSP_ALIGN_NO_MATCH;
-    if(fabs(phi * ratio * 10.0) < 1.0)
+    if(fabs(phi * ratio * decimals) < 1.0)
         stream2->align_info.err &= ~DSP_ALIGN_TRANSLATED;
-    if(fabs((stream2->align_info.factor[0] - 1.0) * 100.0) * decimals < 1)
+    if(floor(fabs(stream2->align_info.factor[0] - 1.0) * decimals) < 1)
         stream2->align_info.err &= ~DSP_ALIGN_SCALED;
-    if(fabs(radians) * decimals * 10.0 < 1)
+    if(floor(fabs(radians) * decimals) < 1)
         stream2->align_info.err &= ~DSP_ALIGN_ROTATED;
     return stream2->align_info.err;
 }
diff --git a/dsp/dsp.h.cmake b/dsp/dsp.h.cmake
index f8d5d36..ec25c05 100644
--- a/dsp/dsp.h.cmake
+++ b/dsp/dsp.h.cmake
@@ -24,8 +24,12 @@ extern "C" {
 #endif
 
 #ifndef DLL_EXPORT
+#ifdef _WIN32
+#define DLL_EXPORT __declspec(dllexport)
+#else
 #define DLL_EXPORT extern
 #endif
+#endif
 
 #ifdef __linux__
 #include <endian.h>
@@ -240,6 +244,12 @@ typedef struct dsp_star_t
     dsp_point center;
     /// The diameter of the star
     double diameter;
+    /// The peak of the star
+    double peak;
+    /// The flux of the star
+    double flux;
+    /// The deviation of the star
+    double theta;
     /// The name of the star
     char name[150];
 } dsp_star;
@@ -261,6 +271,8 @@ typedef struct dsp_triangle_t
     double *ratios;
     /// The stars of the triangle
     dsp_star *stars;
+    /// The number of stars of the triangle
+    int stars_count;
 } dsp_triangle;
 
 /**
@@ -1330,6 +1342,23 @@ DLL_EXPORT void dsp_modulation_frequency(dsp_stream_p stream, double samplefreq,
 */
 DLL_EXPORT void dsp_modulation_amplitude(dsp_stream_p stream, double samplefreq, double freq);
 
+/**\}*/
+/**
+ * \defgroup dsp_StreamReconstruction DSP API Stream reconstruction
+*/
+/**\{*/
+
+/**
+* \brief Try to reconstruct a stream from sub-stream matrices
+* \param stream the target DSP stream.
+* \param matrix the DSP stream with the matrices.
+*
+* The matrix stream must have all dimensions and sizes of the target plus one,
+* which is the number of matrices on to align.
+* The aligned stream should replace the target stream.
+*/
+void dsp_recons_align(dsp_stream_p stream, dsp_stream_p matrix);
+
 /**\}*/
 /**
  * \defgroup dsp_FileManagement DSP API File read/write functions
@@ -1497,10 +1526,11 @@ DLL_EXPORT dsp_align_info *dsp_align_fill_info(dsp_triangle t1, dsp_triangle t2)
 
 /**
 * \brief Create a dsp_triangle struct
-* \param stars the stars array meeded to build the triangle struct
+* \param stars the stars array needed to build the triangle struct
+* \param num_stars the count of the stars
 * \return A new dsp_triangle struct pointer
 */
-DLL_EXPORT dsp_triangle *dsp_align_calc_triangle(dsp_star* stars);
+DLL_EXPORT dsp_triangle *dsp_align_calc_triangle(dsp_star* stars, int num_stars);
 
 /**
 * \brief Free a dsp_triangle struct pointer
@@ -1514,9 +1544,10 @@ DLL_EXPORT void dsp_align_free_triangle(dsp_triangle *triangle);
 * \param to_align the stream to be aligned
 * \param tolerance number of decimals allowed
 * \param target_score the minimum matching score to reach
+* \param num_stars number of stars for each triangle
 * \return The alignment mask (bit1: translated, bit2: scaled, bit3: rotated)
 */
-DLL_EXPORT int dsp_align_get_offset(dsp_stream_p ref, dsp_stream_p to_align, double tolerance, double target_score);
+DLL_EXPORT int dsp_align_get_offset(dsp_stream_p ref, dsp_stream_p to_align, double tolerance, double target_score, int num_stars);
 
 /**\}*/
 /// \defgroup dsp_FitsExtensions
diff --git a/dsp/fits.h b/dsp/fits.h
index 5faf36d..a5108da 100644
--- a/dsp/fits.h
+++ b/dsp/fits.h
@@ -26,9 +26,6 @@ extern "C" {
 
 #ifdef _WIN32
 #define _TCHAR_DEFINED
-#define DLL_EXPORT __declspec(dllexport)
-#else
-#define DLL_EXPORT extern
 #endif
 
 #include <fitsio.h>
diff --git a/dsp/fits_extensions.h b/dsp/fits_extensions.h
index d0e6c06..042365b 100644
--- a/dsp/fits_extensions.h
+++ b/dsp/fits_extensions.h
@@ -24,12 +24,6 @@
 extern "C" {
 #endif
 
-#ifdef _WIN32
-#define DLL_EXPORT __declspec(dllexport)
-#else
-#define DLL_EXPORT extern
-#endif
-
 /**
  * \defgroup dsp_FitsExtensions DSP API FITS Extensions functions
 */
diff --git a/dsp/recon.c b/dsp/recon.c
new file mode 100644
index 0000000..d70d017
--- /dev/null
+++ b/dsp/recon.c
@@ -0,0 +1,45 @@
+/*
+*   DSP API - a digital signal processing library for astronomy usage
+*   Copyright © 2017-2021  Ilia Platone
+*
+*   This program is free software; you can redistribute it and/or
+*   modify it under the terms of the GNU Lesser General Public
+*   License as published by the Free Software Foundation; either
+*   version 3 of the License, or (at your option) any later version.
+*
+*   This program is distributed in the hope that it will be useful,
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+*   Lesser General Public License for more details.
+*
+*   You should have received a copy of the GNU Lesser General Public License
+*   along with this program; if not, write to the Free Software Foundation,
+*   Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+*/
+
+#include "dsp.h"
+
+void dsp_recons_align(dsp_stream_p stream, dsp_stream_p matrix)
+{
+    int z;
+    dsp_fourier_dft(matrix, 1);
+    for(z = 0; z < matrix->sizes[stream->dims]; z++) {
+        dsp_fourier_dft(stream, 1);
+        int x, y, d;
+        dsp_t mn = dsp_stats_min(stream->buf, stream->len);
+        dsp_t mx = dsp_stats_max(stream->buf, stream->len);
+        int* d_pos = (int*)malloc(sizeof(int)*stream->dims);
+        for(y = z*stream->len; y < z*stream->len+stream->len; y++) {
+            int* pos = dsp_stream_get_position(matrix, y);
+            for(d = 0; d < stream->dims; d++) {
+                d_pos[d] = stream->sizes[d]/2+pos[d]-matrix->sizes[d]/2;
+            }
+            x = dsp_stream_set_position(stream, d_pos);
+            free(pos);
+            stream->magnitude->buf[x] *= sqrt(matrix->magnitude->buf[y]);
+        }
+        free(d_pos);
+        dsp_fourier_idft(stream);
+        dsp_buffer_stretch(stream->buf, stream->len, mn, mx);
+    }
+}
diff --git a/dsp/stream.c b/dsp/stream.c
index 7857df0..329fbee 100755
--- a/dsp/stream.c
+++ b/dsp/stream.c
@@ -115,6 +115,10 @@ void dsp_stream_free_buffer(dsp_stream_p stream)
         free(stream->buf);
     if(stream->dft.buf != NULL)
         free(stream->dft.buf);
+    if(stream->magnitude != NULL)
+        dsp_stream_free_buffer(stream->magnitude);
+    if(stream->phase != NULL)
+        dsp_stream_free_buffer(stream->phase);
 }
 
 /**
@@ -180,6 +184,10 @@ void dsp_stream_free(dsp_stream_p stream)
         free(stream->stars);
     if(stream->triangles != NULL)
         free(stream->triangles);
+    if(stream->magnitude != NULL)
+        dsp_stream_free(stream->magnitude);
+    if(stream->phase != NULL)
+        dsp_stream_free(stream->phase);
     free(stream);
     stream = NULL;
 }
@@ -332,6 +340,9 @@ void dsp_stream_add_star(dsp_stream_p stream, dsp_star star)
     stream->stars = (dsp_star*)realloc(stream->stars, sizeof(dsp_star)*(stream->stars_count+1));
     strcpy(stream->stars[stream->stars_count].name, star.name);
     stream->stars[stream->stars_count].diameter = star.diameter;
+    stream->stars[stream->stars_count].peak = star.peak;
+    stream->stars[stream->stars_count].flux = star.flux;
+    stream->stars[stream->stars_count].theta = star.theta;
     stream->stars[stream->stars_count].center.dims = star.center.dims;
     stream->stars[stream->stars_count].center.location = (double*)malloc(sizeof(double)*star.center.dims);
     for(d = 0; d < star.center.dims; d++)
@@ -368,26 +379,35 @@ void dsp_stream_add_triangle(dsp_stream_p stream, dsp_triangle triangle)
 {
     int s;
     int d;
+    int num_baselines = triangle.stars_count*(triangle.stars_count-1)/2;
     stream->triangles = (dsp_triangle*)realloc(stream->triangles, sizeof(dsp_triangle)*(stream->triangles_count+1));
     stream->triangles[stream->triangles_count].dims = triangle.dims;
     stream->triangles[stream->triangles_count].index = triangle.index;
+    stream->triangles[stream->triangles_count].stars_count = triangle.stars_count;
     stream->triangles[stream->triangles_count].theta = (double*)malloc(sizeof(double)*(stream->dims-1));
-    stream->triangles[stream->triangles_count].ratios = (double*)malloc(sizeof(double)*triangle.dims);
-    stream->triangles[stream->triangles_count].sizes = (double*)malloc(sizeof(double)*triangle.dims);
-    stream->triangles[stream->triangles_count].stars = (dsp_star*)malloc(sizeof(dsp_star)*triangle.dims);
+    stream->triangles[stream->triangles_count].ratios = (double*)malloc(sizeof(double)*num_baselines);
+    stream->triangles[stream->triangles_count].sizes = (double*)malloc(sizeof(double)*num_baselines);
+    stream->triangles[stream->triangles_count].stars = (dsp_star*)malloc(sizeof(dsp_star)*triangle.stars_count);
     for (s = 0; s < triangle.dims; s++) {
         if(s < stream->dims - 1) {
             stream->triangles[stream->triangles_count].theta[s] = triangle.theta[s];
         }
-        stream->triangles[stream->triangles_count].sizes[s] = triangle.sizes[s];
-        stream->triangles[stream->triangles_count].ratios[s] = triangle.ratios[s];
+    }
+    for(s = 0; s < triangle.stars_count; s++) {
         stream->triangles[stream->triangles_count].stars[s].center.dims = triangle.stars[s].center.dims;
         stream->triangles[stream->triangles_count].stars[s].diameter = triangle.stars[s].diameter;
+        stream->triangles[stream->triangles_count].stars[s].peak = triangle.stars[s].peak;
+        stream->triangles[stream->triangles_count].stars[s].flux = triangle.stars[s].flux;
+        stream->triangles[stream->triangles_count].stars[s].theta = triangle.stars[s].theta;
         stream->triangles[stream->triangles_count].stars[s].center.location = (double*)malloc(sizeof(double)*stream->dims);
         for(d = 0; d < triangle.stars[s].center.dims; d++) {
             stream->triangles[stream->triangles_count].stars[s].center.location[d] = triangle.stars[s].center.location[d];
         }
     }
+    for(s = 0; s < num_baselines; s++) {
+        stream->triangles[stream->triangles_count].sizes[s] = triangle.sizes[s];
+        stream->triangles[stream->triangles_count].ratios[s] = triangle.ratios[s];
+    }
     stream->triangles_count++;
 }
 
@@ -398,16 +418,27 @@ void dsp_stream_add_triangle(dsp_stream_p stream, dsp_triangle triangle)
  */
 void dsp_stream_del_triangle(dsp_stream_p stream, int index)
 {
-    dsp_triangle* triangles = (dsp_triangle*)malloc(sizeof(dsp_triangle) * stream->triangles_count);
-    int triangles_count = stream->triangles_count;
-    memcpy(triangles, stream->triangles, sizeof(dsp_triangle*) * stream->triangles_count);
-    free(stream->triangles);
-    stream->triangles_count = 0;
+    int d;
+    free(stream->triangles[index].sizes);
+    free(stream->triangles[index].theta);
+    free(stream->triangles[index].ratios);
+    for(d = 0; d < stream->triangles[index].stars_count; d++) {
+        free(stream->triangles[index].stars[d].center.location);
+    }
+    free(stream->triangles[index].stars);
     int i;
-    for(i = 0; i < triangles_count; i++) {
-        if(i != index) {
-            dsp_stream_add_triangle(stream, triangles[i]);
+    for(i = index; i < stream->triangles_count - 1; i++) {
+        stream->triangles[i] = stream->triangles[i+1];
+    }
+    stream->triangles_count--;
+    if(index < stream->triangles_count) {
+        free(stream->triangles[i].sizes);
+        free(stream->triangles[i].theta);
+        free(stream->triangles[i].ratios);
+        for(d = 0; d < stream->triangles[i].dims; d++) {
+            free(stream->triangles[i].stars[d].center.location);
         }
+        free(stream->triangles[i].stars);
     }
 }
 
diff --git a/vlbi/astro.c b/vlbi/astro.c
index 36760ac..74f3a6a 100644
--- a/vlbi/astro.c
+++ b/vlbi/astro.c
@@ -289,7 +289,7 @@ dsp_align_info vlbi_astro_align_spectra(dsp_stream_p spectrum, dsp_stream_p cata
     int spectrum_count = spectrum->stars_count;
     catalog->stars_count = Min(catalog_count, max_lines);
     spectrum->stars_count = Min(spectrum_count, max_lines);
-    dsp_align_get_offset(catalog, spectrum, decimals, min_score);
+    dsp_align_get_offset(catalog, spectrum, decimals, min_score, 3);
     catalog->stars_count = catalog_count;
     spectrum->stars_count = spectrum_count;
     double factor = spectrum->align_info.factor[0];
@@ -399,7 +399,7 @@ double vlbi_astro_estimate_distance_ratio(double luminosity_ratio, double flux_r
 
 double vlbi_astro_estimate_distance_parallax(double rad, double baseline)
 {
-    return baseline * tan(M_PI+rad);
+    return baseline * tan(M_PI_2-rad);
 }
 
 double vlbi_astro_estimate_redshift(double wavelength0, int wavelength)
diff --git a/vlbi/baseline.cpp b/vlbi/baseline.cpp
index 9fd852c..a014c45 100644
--- a/vlbi/baseline.cpp
+++ b/vlbi/baseline.cpp
@@ -121,7 +121,7 @@ double *VLBIBaseline::getBaseline()
 void VLBIBaseline::getProjection()
 {
     double *b = getBaseline();
-    double *tmp = vlbi_matrix_calc_3d_projection(Target[1], Target[0], b);
+    double *tmp = vlbi_matrix_calc_parametric_projection(Target, b);
     free (b);
     double *proj = vlbi_matrix_calc_uv_coordinates(tmp, getWaveLength());
     free (tmp);
@@ -164,5 +164,5 @@ void VLBIBaseline::setTime(double time)
         vlbi_astro_alt_az_from_ra_dec(time, Ra, Dec, stationLocation()->geographic.lat, stationLocation()->geographic.lon, &Alt,
                                       &Az);
     }
-    setTarget(Az, Alt);
+    setTarget(Az, Alt, Dist);
 }
diff --git a/vlbi/baseline.h b/vlbi/baseline.h
index 2c86c09..414491d 100644
--- a/vlbi/baseline.h
+++ b/vlbi/baseline.h
@@ -52,14 +52,16 @@ public:
 
     inline double getRa() { return Ra; }
     inline double getDec() { return Dec; }
+    inline double getDistance() { return Dist; }
     inline double* getTarget() { return Target; }
     inline double getWaveLength() { return WaveLength; }
     inline double getSampleRate() { return SampleRate; }
 
     inline void setRa(double ra) { Ra = ra; }
     inline void setDec(double dec) { Dec = dec; }
-    inline void setTarget(double horiz, double vert) { Target[0] = horiz; Target[1] = vert; memcpy(getStream()->target, Target, sizeof(double)*2); getNode1()->setTarget(Target); getNode2()->setTarget(Target); }
-    inline void setTarget(double *target) { memcpy(Target, target, sizeof(double)*2); memcpy(getStream()->target, Target, sizeof(double)*2); getNode1()->setTarget(Target); getNode2()->setTarget(Target); }
+    inline void setDistance(double dist) { Dist = dist; }
+    inline void setTarget(double horiz, double vert, double dist = DBL_MAX) { Target[0] = horiz; Target[1] = vert; Target[2] = dist; memcpy(getStream()->target, Target, sizeof(double)*3); getNode1()->setTarget(Target); getNode2()->setTarget(Target); }
+    inline void setTarget(double *target) { memcpy(Target, target, sizeof(double)*3); memcpy(getStream()->target, Target, sizeof(double)*3); getNode1()->setTarget(Target); getNode2()->setTarget(Target); }
     inline void setWaveLength(double wavelength) { WaveLength = wavelength; getStream()->wavelength = wavelength; getNode1()->setWaveLength(WaveLength); getNode2()->setWaveLength(WaveLength); }
     inline void setSampleRate(double samplerate) { SampleRate = samplerate; getStream()->samplerate = samplerate; getNode1()->setSampleRate(SampleRate); getNode2()->setSampleRate(SampleRate); }
 
@@ -102,6 +104,7 @@ private:
     double Target[3];
     double Ra { 0 };
     double Dec { 0 };
+    double Dist { DBL_MAX };
     double baseline[3];
     double u { 0 };
     double v { 0 };
diff --git a/vlbi/baselinecollection.cpp b/vlbi/baselinecollection.cpp
index f37e9cb..f5bf62e 100644
--- a/vlbi/baselinecollection.cpp
+++ b/vlbi/baselinecollection.cpp
@@ -119,6 +119,15 @@ void BaselineCollection:: setDec(double dec)
     }
 }
 
+void BaselineCollection:: setDistance(double dist)
+{
+    Dist = dist;
+    for(int i = 0; i < Count(); i++)
+    {
+        At(i)->setDistance(dist);
+    }
+}
+
 void BaselineCollection::SetFrequency(double frequency)
 {
     Stream->wavelength = vlbi_astro_mean_speed(0) / frequency;
diff --git a/vlbi/baselinecollection.h b/vlbi/baselinecollection.h
index 76491fb..93030b9 100644
--- a/vlbi/baselinecollection.h
+++ b/vlbi/baselinecollection.h
@@ -40,6 +40,7 @@ class BaselineCollection : public VLBICollection
         void SetTarget(double *target);
         void setRa(double ra);
         void setDec(double dec);
+        void setDistance(double dist);
         inline double getRa()
         {
             return Ra;
@@ -97,7 +98,7 @@ class BaselineCollection : public VLBICollection
 
     protected:
         bool relative;
-        double Ra, Dec;
+        double Ra, Dec, Dist;
         NodeCollection *Nodes;
         int width { 128 };
         int height { 128 };
diff --git a/vlbi/matrix.c b/vlbi/matrix.c
index f49f8c4..aeda5bb 100644
--- a/vlbi/matrix.c
+++ b/vlbi/matrix.c
@@ -38,7 +38,22 @@ double* vlbi_matrix_calc_3d_projection(double alt, double az, double *baseline)
     double z = baseline[2];
     proj[0] = (x * sin(az) + y * cos(az));
     proj[1] = (y * sin(alt) * sin(az) - x * sin(alt) * cos(az) + z * cos(alt));
-    proj[2] = cos(az) * y * cos(alt) - x * sin(az) * cos(alt) + sin(alt) * z;
+    proj[2] = (cos(az) * y * cos(alt) - x * sin(az) * cos(alt) + sin(alt) * z);
+    return proj;
+}
+
+double* vlbi_matrix_calc_parametric_projection(double *target, double *baseline)
+{
+    double* proj = (double*)calloc(sizeof(double), 3);
+    target[0] *= M_PI / 180.0;
+    target[1] *= M_PI / 180.0;
+    double x = baseline[0];
+    double y = baseline[1];
+    double z = baseline[2];
+    proj[0] = (x * sin(target[1]) + y * cos(target[1])) / sin(atan(target[2]));
+    proj[1] = (y * sin(target[0]) * sin(target[1]) - x * sin(target[0]) * cos(target[1]) + z * cos(target[0])) / sin(atan(target[2]));
+    proj[2] = (cos(target[1]) * y * cos(target[0]) - x * sin(target[1]) * cos(target[0]) + sin(target[0]) * z);
+    proj[2] += proj[2] * cos(atan(target[2])) / 2;
     return proj;
 }
 
diff --git a/vlbi/node.h b/vlbi/node.h
index 64da8f4..ddeef89 100644
--- a/vlbi/node.h
+++ b/vlbi/node.h
@@ -101,14 +101,15 @@ class VLBINode
             getStream()->starttimeutc.tv_nsec = (starttime - getStream()->starttimeutc.tv_sec) * 1000000000.0;
         }
 
-        inline void setTarget(double horiz, double vert)
+        inline void setTarget(double horiz, double vert, double dist = DBL_MAX)
         {
             getStream()->target[0] = horiz;
             getStream()->target[1] = vert;
+            getStream()->target[2] = dist;
         }
         inline void setTarget(double *target)
         {
-            memcpy(getStream()->target, target, sizeof(double) * 2);
+            memcpy(getStream()->target, target, sizeof(double) * 3);
         }
         inline void setLocation(double *coords)
         {
diff --git a/vlbi/stream.cpp b/vlbi/stream.cpp
index 1446e5d..6f220fd 100644
--- a/vlbi/stream.cpp
+++ b/vlbi/stream.cpp
@@ -74,12 +74,13 @@ const char* vlbi_get_version()
     return VLBI_VERSION_STRING;
 }
 
-static double getDelay(double time, NodeCollection *nodes, VLBIBaseline *b, double Ra, double Dec,
+static double getDelay(double time, NodeCollection *nodes, VLBIBaseline *b, double Ra, double Dec, double Distance,
                        double wavelength)
 {
     double delay = 0.0;
     b->setRa(Ra);
     b->setDec(Dec);
+    b->setDistance(Distance);
     b->setRelative(nodes->isRelative());
     b->setWaveLength(wavelength);
     b->setTime(time);
@@ -88,7 +89,7 @@ static double getDelay(double time, NodeCollection *nodes, VLBIBaseline *b, doub
     return delay;
 }
 
-void vlbi_get_offsets(vlbi_context ctx, double J200Time, const char* node1, const char* node2, double Ra, double Dec,
+void vlbi_get_offsets(vlbi_context ctx, double J200Time, const char* node1, const char* node2, double Ra, double Dec, double Distance,
                       double *offset1,
                       double *offset2)
 {
@@ -99,6 +100,7 @@ void vlbi_get_offsets(vlbi_context ctx, double J200Time, const char* node1, cons
     if(b != nullptr) {
         b->setRa(Ra);
         b->setDec(Dec);
+        b->setDistance(Distance);
         double max_delay = 0;
         int farest = 0, x, y;
         for (x = 0; x < nodes->Count(); x++)
@@ -106,7 +108,7 @@ void vlbi_get_offsets(vlbi_context ctx, double J200Time, const char* node1, cons
             for (y = x + 1; y < nodes->Count(); y++)
             {
                 sprintf(baseline, "%s_%s", nodes->At(x)->getName(), nodes->At(y)->getName());
-                double delay = getDelay(J200Time, nodes, b, nodes->getBaselines()->Get(baseline)->getRa(), nodes->getBaselines()->Get(baseline)->getDec(), nodes->getBaselines()->Get(baseline)->getWaveLength());
+                double delay = getDelay(J200Time, nodes, b, nodes->getBaselines()->Get(baseline)->getRa(), nodes->getBaselines()->Get(baseline)->getDec(), nodes->getBaselines()->Get(baseline)->getDistance(), nodes->getBaselines()->Get(baseline)->getWaveLength());
                 if(fabs(delay) > max_delay)
                 {
                     max_delay = fabs(delay);
@@ -122,12 +124,12 @@ void vlbi_get_offsets(vlbi_context ctx, double J200Time, const char* node1, cons
         sprintf(baseline, "%s_%s", b->getNode1()->getName(), nodes->At(farest)->getName());
         if(collection->Contains(baseline)) {
             bl = collection->Get(baseline);
-            *offset1 = getDelay(J200Time, nodes, bl, bl->getRa(), bl->getDec(), bl->getWaveLength());
+            *offset1 = getDelay(J200Time, nodes, bl, bl->getRa(), bl->getDec(), bl->getDistance(), bl->getWaveLength());
         }
         sprintf(baseline, "%s_%s", b->getNode2()->getName(), nodes->At(farest)->getName());
         if(collection->Contains(baseline)) {
             bl = collection->Get(baseline);
-            *offset2 = getDelay(J200Time, nodes, bl, bl->getRa(), bl->getDec(), bl->getWaveLength());
+            *offset2 = getDelay(J200Time, nodes, bl, bl->getRa(), bl->getDec(), bl->getDistance(), bl->getWaveLength());
         }
     }
 }
@@ -195,7 +197,7 @@ static void* fillplane(void *arg)
         }
         else
         {
-            vlbi_get_offsets((void*)nodes, t, b->getNode1()->getName(), b->getNode2()->getName(), b->getRa(), b->getDec(), &offset1, &offset2);
+            vlbi_get_offsets((void*)nodes, t, b->getNode1()->getName(), b->getNode2()->getName(), b->getRa(), b->getDec(), b->getDistance(), &offset1, &offset2);
         }
         b->setTime(t);
         b->getProjection();
@@ -535,6 +537,7 @@ void vlbi_get_uv_plot(vlbi_context ctx, const char *name, int u, int v, double *
     baselines->SetSampleRate(sr);
     baselines->setRa(target[0]);
     baselines->setDec(target[1]);
+    baselines->setDistance(target[2]);
     parent->child_count = 0;
     pgarb("%ld nodes, %ld baselines\n", nodes->Count(), baselines->Count());
     baselines->SetDelegate(delegate);
diff --git a/vlbi/vlbi.h.cmake b/vlbi/vlbi.h.cmake
index 43e9139..934fe28 100644
--- a/vlbi/vlbi.h.cmake
+++ b/vlbi/vlbi.h.cmake
@@ -23,11 +23,13 @@
 extern "C" {
 #endif
 
+#ifndef DLL_EXPORT
 #ifdef _WIN32
 #define DLL_EXPORT __declspec(dllexport)
 #else
 #define DLL_EXPORT extern
 #endif
+#endif
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -702,10 +704,11 @@ DLL_EXPORT void vlbi_set_location(void *ctx, double lat, double lon, double el);
 * \param node2 The name of the second node
 * \param Ra The right ascension coordinate
 * \param Dec The declination coordinate
+* \param Distance The distance from the object
 * \param offset1 The offset calculated for the first node to the farest one
 * \param offset2 The offset calculated for the second node to the farest one
 */
-DLL_EXPORT void vlbi_get_offsets(vlbi_context ctx, double J2000Time, const char* node1, const char* node2, double Ra, double Dec, double *offset1, double *offset2);
+DLL_EXPORT void vlbi_get_offsets(vlbi_context ctx, double J2000Time, const char* node1, const char* node2, double Ra, double Dec, double Distance, double *offset1, double *offset2);
 
 /**\}*/
 /**
@@ -719,7 +722,7 @@ DLL_EXPORT void vlbi_get_offsets(vlbi_context ctx, double J2000Time, const char*
 * \param name The name of the new model
 * \param u The U size of the resulting UV plot
 * \param v The V size of the resulting UV plot
-* \param target The target position int Ra/Dec celestial coordinates
+* \param target The target position int Ra/Dec/Dist celestial coordinates
 * \param freq The frequency observed. This parameter will scale the plot inverserly.
 * \param sr The sampling rate per second. This parameter will be used as meter for the elements of the streams.
 * \param nodelay if 1 no delay calculation should be done. streams entered are already synced.
@@ -909,6 +912,14 @@ DLL_EXPORT double* vlbi_matrix_calc_baseline_center(double *loc1, double *loc2);
 */
 DLL_EXPORT double* vlbi_matrix_calc_3d_projection(double alt, double az, double *baseline);
 
+/**
+* \brief Return The parametric projection of the current observation.
+* \param target The alt/az/dist coordinates of the target into a 3 element vector.
+* \param baseline The current baseline in meters.
+* \return double* The 3d projection of the current observation.
+*/
+DLL_EXPORT double* vlbi_matrix_calc_parametric_projection(double *target, double *baseline);
+
 /**
 * \brief Return The UV coordinates of the current observation.
 * \param proj The 2d projection of the current baseline perspective distance.
diff --git a/vlbi_server.cpp b/vlbi_server.cpp
index 151a4f3..8de0cad 100644
--- a/vlbi_server.cpp
+++ b/vlbi_server.cpp
@@ -121,7 +121,7 @@ void VLBI::Server::DelNode(const char *name)
 
 void VLBI::Server::Plot(const char *name, int flags)
 {
-    double coords[3] = { Ra, Dec };
+    double coords[3] = { Ra, Dec, DBL_MAX };
     if((flags & plot_flags_custom_delegate) == 0) {
         setDelegate((flags & plot_flags_uv_coverage) != 0 ? coverage_delegate : vlbi_default_delegate);
     }

More details

Full run details

Historical runs