# Circuit

```python
from discopy import grammar
from pytket.circuit.display import render_circuit_jupyter

from lambeq.ccg2discocat import DepCCGParser
from lambeq.circuit import IQPAnsatz
from lambeq.core.types import AtomicType

N = AtomicType.NOUN
S = AtomicType.SENTENCE
```

```python
depccg_parser = DepCCGParser()
diagram = depccg_parser.sentence2diagram('Alice runs')
diagram.draw()
```

```python
ansatz = IQPAnsatz({N: 1, S: 1}, n_layers=2)
discopy_circuit = ansatz(diagram)
discopy_circuit.draw(figsize=(10, 15))
```

```python
tket_circuit = ansatz(diagram).to_tk()

# This does not render properly on GitHub, please view it at:
# https://cqcl.github.io/lambeq/examples/circuit.html
render_circuit_jupyter(tket_circuit)
```

<!DOCTYPE html>

    <meta charset="UTF-8">

q[0]

q[1]

q[2]

c[0]

c[1]

                    Rx(2*Alice__n_0)

                    H

                    H

                    Rz(2*Alice__n_1)

                    Rz(2*runs__n.r@s_0)

                    Rx(2*Alice__n_2)

                    H

                    H

                    Rz(2*runs__n.r@s_1)

                    X

z

                    H

z

q[0]

q[1]

q[2]

c[0]

c[1]

```python
from sympy import default_sort_key

# Make sure you sort your symbols as they are returned as a set.
parameters = sorted(tket_circuit.free_symbols(), key=default_sort_key)

param_dict = {p: i * 0.001 for i, p in enumerate(parameters)}
param_dict
```

    {Alice__n_0: 0.0,
     Alice__n_1: 0.001,
     Alice__n_2: 0.002,
     runs__n.r@s_0: 0.003,
     runs__n.r@s_1: 0.004}

```python
tket_circuit.symbol_substitution(param_dict)

# This does not render properly on GitHub, please view it at:
# https://cqcl.github.io/lambeq/examples/circuit.html
render_circuit_jupyter(tket_circuit)
```

<!DOCTYPE html>

    <meta charset="UTF-8">

q[0]

q[1]

q[2]

c[0]

c[1]

                    Rx(0.0)

                    H

                    H

                    Rz(0.002)

                    Rz(0.006)

                    Rx(0.004)

                    H

                    H

                    Rz(0.008)

                    X

z

                    H

z

q[0]

q[1]

q[2]

c[0]

c[1]


# GaiaLab - Introductory infos

Toy model in which the snapshots are taken continuously, i.e. not at discrete time steps.

---

## Content:

- [#1.-Scanner-Object](#1.-Scanner-Object)
- [#2.-Source-detection](#2.-Source-detection)
    - Sirio
    - Vega
    - Proxima Centauri
- [#3.-For-more](#3.-For-more)

---

## Few definitions: (from wikipedia:)
* Attitude:   https://en.wikipedia.org/wiki/Attitude_control
* Parallax:   https://en.wikipedia.org/wiki/Parallax
* B-Spline:   https://en.wikipedia.org/wiki/B-spline
* Quaternion: https://en.wikipedia.org/wiki/Quaternion
* CCD (Charge-Coupled Device): https://en.wikipedia.org/wiki/Charge-coupled_device
* BCRS and GCRS (Barycentric/Geocentric Celestial Reference System): https://en.wikipedia.org/wiki/Barycentric_celestial_reference_system
* Some other things about references frames: https://www.gaia.ac.uk/science/astronomical-coordinate-systems
* TCB (Barycentric Coordinate Time): https://en.wikipedia.org/wiki/Barycentric_Coordinate_Time
* Topocentric function: https://en.wikipedia.org/wiki/Horizontal_coordinate_system

# Abstract

This notebook explains the wotking principle of the scanner object, together with auxiliary source and satellite objects

```python
# To use interact -- IPython widget
from __future__ import print_function
from ipywidgets import interact, interactive, fixed, interact_manual
import ipywidgets as widgets

# Generic module imports import
from IPython.display import Image
import sys
import numpy as np
from matplotlib import pyplot as plt

# append to path the folder that contains the analytic scanner and import local files
sys.path.append('../../GaiaLab/scan/analytic_scanner')

import constants as const

from scanner import Scanner
from satellite import Satellite
from source import Source

import analytic_plots as plots
from agis_functions import scanning_direction
from agis_functions import get_angular_FFoV_PFoV
from agis_functions import *

# Ipython magics
%load_ext autoreload
%autoreload 2
# for pseudo interactive plots
# %matplotlib notebook

# Set some user-specific variables
MAIN_FOLDER = '../../' # relative path to project folder
FIG_FOLDER = '../figures/' # relative path to figure
```

**About the frames we will use**

Here is an image representing the frames:

Figure 1. Definition of angles $\epsilon$, $\lambda$, $\nu$, $\xi$, and $\Omega$ in the nominal scanning law.

The nominal scanning law of Gaia is described by the following angles:
* $\epsilon$: obliquity of equator. This is a $\it{constant}$ chosen to be 23º 26' 21.448''
* $\xi$: revolving angle. At any time the z axis is at this $\it{constant}$ angle from $\vec{s}$. For Gaia, the current choice is 55º.
* $\lambda_{s}(t)$: nominal longitud of the sun in the ecliptic plane. This is chosen to have a constant speed: $\dot{\lambda}(t) = \frac{2\pi}{365}$
* $\nu(t)$: revolving phase. Gives a nearly constant precession rate.
* $\Omega(t)$: spin phase.

Frames of referenced used:
* Interial Frame: $\textbf{G} = \big[ \textbf{l},\textbf{m},\textbf{n}\big]$ gaia non-turning wrt to sun reference system
* Celestial frame of reference (ort): $\textbf{N} = \big[ \textbf{l},\textbf{j},\textbf{k}\big]$ Sun reference
* Instrument triad frame (ort): $\textbf{Z} = \big[ \textbf{x},\textbf{y},\textbf{z}\big]$  Gaia reference turning
* Ecliptic triad $\textbf{E}= \big[ \textbf{l},\textbf{j},\textbf{k}\big]$

Given by:
$$
\textbf{k} = \textbf{n}cos(\epsilon) - \textbf{m} sin(\epsilon) \\
\textbf{j} = \textbf{m}cos(\epsilon) + \textbf{n} sin(\epsilon) \\ \textbf{s}=\textbf{l}cos(\lambda_{s}(t)) + \textbf{j} sin(\lambda_{s}(t)
$$

The total inertial rotation of the telescope is therefore:
$$
\begin{equation}
\vec{\omega} = \textbf{k} \dot{\lambda_{s}} + \textbf{s} \dot{\nu} + \textbf{z} \dot{\Omega}
\end{equation}
$$

The precessional motion of the z-axis is:

$$
\begin{equation}
\dot{z} = \omega \times z= \big( k \times z\big)\dot{\lambda_{s}} + \big(s \times z\big)\dot{\nu}
\end{equation}
$$

And if we consider the rate of change of the z-axis wrt time to be a constant:
$$
\begin{equation}
\frac{dz}{dt}\frac{dt}{d\lambda_{s}} = \frac{dz}{dt} \dot{\lambda_{s}}^{-1} = S
\end{equation}$$

And keeping the condition $ \|\big( k \times z \big)\|^{2} = 1 - sin^{2}(\xi)sin^{2}(\nu)$ then the following ODE for $\nu$ is obtained:

$$
\begin{equation}
\dot{\nu} = \dot{\lambda_{s}}\frac{\sqrt{S^{2} - cos^{2}(\nu)} + cos(\xi)sin(\nu)}{sin(\xi)}
\end{equation}
$$

Taking the positive sign of the square root implying increasing $\nu$.
Then, for constant inertial spin rate about the z axis using the equation for the inertial rotation $\omega$, we obtain:
$$
\begin{equation}
\dot{\Omega} = \omega_{z} - \dot{\nu}cos\xi - \dot\lambda_{s}sin\xi sin\nu
\end{equation}
$$

To see how does the longitude and latitude of the z axis changes with time, according to Figure 1, the geometry gives the following equations in the ecliptic plane:
$$
\begin{equation}
\lambda_{z}(t) = \lambda_{s}(t) + arctan\big[ tan\xi cos\nu(t) \big]. \\
\beta_{z}(t) = arcsin \big[ sin\xi sin\nu(t) \big].
\end{equation}
$$

Frames of referenced used:
* Celestial Frame: $\textbf{G} = \big[ \textbf{l},\textbf{m},\textbf{n}\big]$
* Instrument Frame: $\textbf{Z} = \big[ \textbf{x},\textbf{y},\textbf{z}\big]$
* Barycentric Reference System $\textbf{BCRS}= \big[ \textbf{l'},\textbf{m'},\textbf{n'}\big]$

The BCRS is a frame of reference that has its axis all parallel to G-Frame, but that is displaced by 1AU. That is to say, it is the frame centered at the sun.

# 1. Construct Object

```python
t_init = 0  # [days] initial time of satellite position wrt epoch J2000. This is also the time followed by the scanner.
t_end = 365*5  # [days] end time at which we compute satellite position.
my_dt = 1/24 # [days] time step for the computation of the satellite attitude

# Create the Satellite object
gaia = Satellite(t_init, t_end, my_dt)

# Create the Scanner object
scanner = Scanner()
```

### 1.1 Check properties of objects

##### 1.1.1 properties of the Satellite

Plotting the time evolution of the attitude components, begining at $t=0$ up to $t=80$ days.

```python
# plot the attitude of the satellite
plots.plot_attitude(gaia, ti=0, tf=80, n_points=1000, style='-')
```

```python

```

Plotting the scanner positions:

```python
myTime = 50
plots.plot_3D_scanner_pos(gaia, 'X', 0, myTime, 1000, elevation=30, azimuth=30)
plots.plot_3D_scanner_pos(gaia, 'Z', 0, myTime, 1000)
def my_func(elev, azim):
    plots.plot_3D_scanner_pos(gaia, 'X', 0, myTime, 1000, elevation=elev, azimuth=azim)
interact(my_func, elev=widgets.IntSlider(min=0,max=90,step=1,value=20),
        azim=widgets.IntSlider(min=0,max=90,step=1,value=20))
# x,y,z wrt to lmn reference
```

 ##### 1.1.2 Scanner properties
 We just check some of the properties of the scanner:

```python
print('Size of the field of view of the scanner: ', np.degrees(scanner.zeta_limit),'°')
print('Is the scanner using two telescope in a gaia-like fascion? ', scanner.double_telescope)
```

    Size of the field of view of the scanner:  0.5 °
    Is the scanner using two telescope in a gaia-like fascion?  True

# 2. Source detection

In this section we create example stars from real data. The Source object takes as inputs:

> Source('name', $\alpha$, $\delta$, parallax, $\mu_{\alpha}$, $\mu_{\delta}$, $\mu_{r}$)

with units: [string, deg, deg, mas, mas/yr, mas/yr, km/s]

### 2.1 Create source

```python
# Comments / uncomment lines to play with different sources

source = Source("sirio", 101.28, -16.7161, 379.21, -546.05, -1223.14, -7.6)
# source = Source("vega", 279.2333, 38.78, 128.91, 201.03, 286.23, -13.9)
# source = Source("proxima",217.42, -62, 768.7, 3775.40, 769.33, 21.7)
```

##### 2.1.1 Visualize the evolution of the source in time

```python
# With respect to equatorial coordinates:
fig = plots.plot_star_trajectory(source, gaia);
# fig.show()
```

```python
fig, ax = plots.plot_scanner_position_over_source(source=source, sat=gaia, t_init=0, t_end=1,
                                                  num_points_of_discretization = 10000);
```

### 2.2 Scan source

```python
time_taken_for_scan = scanner.scan(gaia, source, t_init, t_end)
print('Scan took: ', time_taken_for_scan, 'seconds')
print('Source observed ', len(scanner.obs_times), 'times')
```

    Scan took:  1.22025728225708 seconds
    Source observed  75 times

##### 2.2.1 Visualize scan

```python
# Get fields angle from scanner:
scanner.compute_angles_eta_zeta(gaia, source)

```

```python
as_,ds_ = ([], [])
for i, t in enumerate(scanner.obs_times):

    attitude = gaia.func_attitude(t)
    phi = scanner.eta_scanned[i]
    zeta = scanner.zeta_scanned[i]
    a, d = field_angles_to_alpha_delta(phi, zeta, attitude)
    as_.append(a)
    ds_.append(d)
    print(a,d)
plt.figure()
as_ = ft.zero_to_two_pi_to_minus_pi_pi(np.array(as_))
ds_ = ft.transform_twoPi_into_halfPi(np.array(ds_))
plt.plot(as_,ds_,'+')
```

```python
fig = plots.plot_star_trajectory(source, gaia, obs_times=scanner.obs_times, equatorial=False,
                                 show_scanning_directions=False);
```

Where the red dots represent the position of the star at the time of observation.

##### 2.2.2 Quantitative results

Check the deviation from 0 in the along scan direction:

```python
print('Scan error in the along scan direction: ', scanner.scanner_error(), '[rads], \nor equivalently,',
       scanner.scanner_error()/const.rad_per_mas, '[mas]')
```

    Scan error in the along scan direction:  -8.979896929409987e-14 [rads],
    or equivalently, -1.852236700263647e-05 [mas]

Check that all the observations are inside the limitations of the Field of View (FoV) which is [-0.5°, 0.5°]:

```python
# Check that all the observations are in the field of view:
plt.figure()
plt.title('Across scan angle of the observations')
plt.plot(scanner.zeta_scanned, '+', label='Observation')
plt.hlines(np.radians([-0.5, 0.5]), xmin=0, xmax=len(scanner.obs_times), label='| 0.5°|')
plt.xlim(0, len(scanner.obs_times))
plt.xlabel('Index of the observation')
plt.ylabel('$\zeta$ [rads]')
plt.legend()
plt.show();
```

Where $\zeta$ is the Across scan angle of the observation.

# 3. Conclusion

Thank you for reading! We hope tha tthis notebook could help you understand the model and we hope you had fun discovering it!
For more about this project, visit the others notebooks as well as the gaia website.

_For any errors or remarks, don't hesitate to contact us!_

```python

```

```python

```

```python

```

```python

```

# Tests

```python
import constants as const
import frame_transformations as ft
equatorial = True
alphas_sol, deltas_sol = ([], [])  # coordinates of the founds sources
times_sol = []
sat=gaia
obs_times = scanner.obs_times
for t in obs_times:
    alpha_obs, delta_obs, delta_alpha_dx_mas, delta_delta_mas = source.topocentric_angles(sat, t)
    times_sol.append(t/const.days_per_year + 2000)  # +2000 do the fact that the reference epoch is J2000
    if equatorial is True:
        alphas_sol.append(alpha_obs)
        deltas_sol.append(delta_obs)
    else:
        alphas_sol.append(delta_alpha_dx_mas)
        deltas_sol.append(delta_delta_mas)

# Convert all in mas:
if equatorial is True:
    if obs_times:
        alphas_sol = np.array(alphas_sol) / const.rad_per_mas
        deltas_sol = np.array(deltas_sol) / const.rad_per_mas
```

```python
np.sort(obs_times)
```

    array([   9.54965349,    9.62360949,   41.51333726,   41.58729534,
             89.15177884,   89.22574167,   89.40178235,   89.47574443,
            100.3997511 ,  100.47371282,  100.64975141,  100.72371389,
            149.34471992,  149.52076293,  149.59472506,  215.01009736,
            267.11808211,  267.19204527,  267.36809264,  267.44205434,
            267.61809983,  267.6920601 ,  267.86810376,  267.94206263,
            293.43740771,  293.61344987,  293.68741195,  293.86345559,
            293.9374188 ,  294.11346395,  294.18742831,  294.363475  ,
            294.43744053,  352.54361573,  352.61757383,  391.31145956,
            419.46080196,  419.53475965,  467.15995955,  467.33601494,
            467.4099732 ,  467.58602379,  478.83412302,  478.90808131,
            479.08413362,  479.15809271,  526.53748509,  526.71353878,
            556.43222579,  556.50618798,  594.20535372,  594.38140926,
            654.30956608,  654.38353273,  654.55958575,  654.633551  ,
            654.80960218,  654.88356605,  655.05961542,  655.13357792,
            683.6260836 ,  683.80213668,  683.87609702,  684.05215275,
            684.12611518,  731.48404179,  731.55800951,  772.48564665,
            772.66170136,  844.00856099,  844.08252235,  844.25857877,
            858.5049547 ,  858.57891493,  858.75497464,  858.82893571,
            904.21915111,  904.3952057 ,  938.3517184 ,  938.42568381,
            974.06410084,  974.13806796,  974.31412333, 1036.98997297,
           1037.06394165, 1037.23999814, 1037.31396468, 1069.54637634,
           1069.72243539, 1069.79640185, 1110.41120398, 1110.48517018,
           1153.14654085, 1153.32259928, 1174.06152949, 1174.13549803,
           1220.1706271 , 1220.24459041, 1220.42065477, 1282.56537183,
           1282.63934171, 1320.5833642 , 1320.75942988, 1320.83339318,
           1354.80815324, 1354.98421257, 1355.0581839 , 1355.23424638,
           1417.6578794 , 1417.7318489 , 1489.07503113, 1489.14900105,
           1533.22053456, 1533.29450219, 1550.96422652, 1551.03819142,
           1596.64689116, 1596.82295523, 1619.63192632, 1619.80799253,
           1661.47384654, 1661.54781681, 1704.15626196, 1704.23023244,
           1737.46549893, 1737.64156578, 1737.7155364 , 1737.89160576,
           1737.96557834, 1797.56314728, 1797.63712055])

```python
def transform_twoPi_into_halfPi(deltas):
    deltas = np.array(deltas)
    to_modify_indices = np.where(deltas>np.pi)[0]
    deltas[to_modify_indices] -= 2*np.pi
    return deltas
```

```python
from agis_functions import *
ras0, ras1, decs0, decs1 = ([], [], [], [])
for t in np.linspace(0, t_end, num=1000):
    ra0, dec0, _, _ = np.array(get_angular_FFoV_PFoV(gaia, t))
    ra1, dec1, _, _ = np.array(get_angular_FFoV_PFoV(gaia, t+1/24/4))

    attitude = gaia.func_attitude(t)
    ra0, dec0 = generate_observation_wrt_attitude(attitude)

    ra0, ra1 = ft.zero_to_two_pi_to_minus_pi_pi(np.array([ra0,ra1]))
    dec0, dec1 = transform_twoPi_into_halfPi(np.array([dec0, dec1]))
    ras0.append(ra0)
    ras1.append(ra1)
    decs0.append(dec0)
    decs1.append(dec1)

plt.plot(ras0, decs0,'b,')
# plt.plot(ras1, decs1,'r+')
plt.grid()
```

```python

import helpers as helpers
for i, (t, a, d) in enumerate(zip(np.sort(obs_times), alphas_sol, deltas_sol)):
    if i >3:
        continue
    point = np.array([a, d])

    ra0, dec0, _, _ = np.array(get_angular_FFoV_PFoV(gaia, t))
    ra1, dec1, _, _ = np.array(get_angular_FFoV_PFoV(gaia, t+1/24/60/60))
    ra0, ra1 = ft.zero_to_two_pi_to_minus_pi_pi(np.array([ra0,ra1]))
    dec0, dec1 = transform_twoPi_into_halfPi(np.array([dec0, dec1]))

    vect_1 = np.array([ra1-ra0, dec1-dec0])/const.rad_per_mas

    """plt.plot(ra0, dec0,'b.')
    plt.plot(ra1, dec1,'r.')"""

    att = sat.func_attitude(t)
    point0 = ft.rotate_by_quaternion(att, [0, 0, 0])
    point1 = ft.rotate_by_quaternion(att, [0, 1, 0])

    point0 = ft.vector_to_alpha_delta(point0)
    point1 = ft.vector_to_alpha_delta(point1)

    vect = np.array([point1[0]-point0[0], point1[1]-point0[1]])
    # vect = helpers.normalize(vect)

    vector = scanning_direction(source, sat, t)
    adp = ft.vector_to_adp(vector)
    dir_alpha, dir_delta = ft.vector_to_alpha_delta(vector)
    directions = np.array([dir_alpha, dir_delta])
    # directions = helpers.rescaled_direction((dir_alpha, dir_delta), length)
    to_plot_x = np.array([point[0], point[0]+dir_alpha])
    to_plot_y = np.array([point[1], point[1]+dir_delta])
    # plt.plot(to_plot_x, to_plot_y, 'k-', alpha=0.5)
    # plt.quiver(point[0], point[1], vect[0], vect[1], color=['r'])
    plt.quiver(point[0], point[1], vect_1[0], vect_1[1], color=['b'])
    # plt.quiver(point[0], point[1], directions[0], directions[1], color=['k'])
    # plt.quiver(0, 0, directions[0], directions[1], color=['r'])
```

```python

```

```python

for t in [0, 10, 20, 30, 40, 100, 200, 300, 600, 800]:
    att = sat.func_attitude(t)
    point0 = ft.rotate_by_quaternion(att, [0, 0, 0])
    point1 = ft.rotate_by_quaternion(att, [0, 1, 0])

    point0 = ft.vector_to_alpha_delta(point0)
    point1 = ft.vector_to_alpha_delta(point1)

    vect = np.array([point1[0]-point0[0], point1[1]-point0[1]])

    # vect = helpers.normalize(vect)

    plt.quiver(0, 0, vect[0], vect[1], color=['r'])
plt.show()
```

```python

```