diff --git a/on-host/arpist-traject/src/main.rs b/on-host/arpist-traject/src/main.rs
index 5f289aaf790bb3b888bf189af8ad16275a1f94da..0837d14e49c93fea67abafd2dadf79ed4c0bb787 100644
--- a/on-host/arpist-traject/src/main.rs
+++ b/on-host/arpist-traject/src/main.rs
@@ -1,8 +1,11 @@
-use syntax::CachedLexer;
-
+mod semantics;
 mod syntax;
 mod tokens;
 
+use syntax::CachedLexer;
+
+use crate::semantics::Executor;
+
 // use logos::Logos;
 
 // use self::tokens::Token;
@@ -15,6 +18,8 @@ fn main() {
     //     println!("{:?}", token.0.as_ref().unwrap());
     // }
 
-    let p = CachedLexer::new(&input).parse_program();
-    dbg!(p);
+    let p = CachedLexer::new(&input).parse_program().unwrap();
+    dbg!(&p);
+    let traj = Executor::new(p).run().unwrap();
+    dbg!(traj);
 }
diff --git a/on-host/arpist-traject/src/semantics.rs b/on-host/arpist-traject/src/semantics.rs
new file mode 100644
index 0000000000000000000000000000000000000000..045b5450da811106345c8d99dfe906f2605ff361
--- /dev/null
+++ b/on-host/arpist-traject/src/semantics.rs
@@ -0,0 +1,232 @@
+use crate::syntax::{
+    Blocks, GeneratorKind, GroupExpr, Operation, Operator, Program, SettingKind, TimeAnchorBlock,
+    TimeExpr,
+};
+
+pub struct Executor {
+    initial_coordinates: (f64, f64, f64),
+    last_timeref: Option<f64>,
+    generator: Option<PositionParametricGenerator>,
+    trajectory: Trajectory,
+    execution: ProgramExecution,
+}
+
+impl Executor {
+    pub fn new(program: Program) -> Self {
+        Self {
+            initial_coordinates: (0.0, 0.0, 0.0),
+            last_timeref: None,
+            generator: None,
+            trajectory: Trajectory { points: vec![] },
+            execution: ProgramExecution::from(program),
+        }
+    }
+
+    /// Validate the program execution order
+    fn validate(&self) -> bool {
+        let execution = self.execution.clone();
+        let mut in_settings = true;
+        let mut started_first_time_ref = false;
+        let mut waiting_generator = false;
+        for block in execution {
+            match block {
+                Blocks::Setting(_) => {
+                    // check that settings are at the beginning
+                    if !in_settings {
+                        return false;
+                    }
+                }
+                Blocks::TimeDefinition(_) => {
+                    // not allowed if waiting for generator
+                    if waiting_generator {
+                        return false;
+                    }
+                    // allow generator after the first time reference
+                    if !started_first_time_ref {
+                        started_first_time_ref = true;
+                    }
+                    // disable settings after the first time reference
+                    in_settings = false;
+                    // wait for generator
+                    waiting_generator = true;
+                }
+                Blocks::Generator(_) => {
+                    // check that generator is after the first time reference
+                    if !waiting_generator {
+                        return false;
+                    }
+                    // disable generator after it's been used
+                    waiting_generator = false;
+                }
+            }
+        }
+        true
+    }
+
+    pub fn run(mut self) -> Result<Trajectory, String> {
+        if !self.validate() {
+            return Err("Invalid program".to_string());
+        }
+        for block in self.execution {
+            match block {
+                Blocks::Setting(setting) => match setting.kind {
+                    SettingKind::GPS => {
+                        self.initial_coordinates = (
+                            **setting.get("latitude").unwrap(),
+                            **setting.get("longitude").unwrap(),
+                            **setting.get("altitude").unwrap(),
+                        );
+                    }
+                },
+                Blocks::Generator(generator) => match generator.kind {
+                    GeneratorKind::PositionParametric => {
+                        let x = generator.get("x").unwrap();
+                        let y = generator.get("y").unwrap();
+                        let z = generator.get("z").unwrap();
+                        self.generator = Some(PositionParametricGenerator::new(
+                            x.clone(),
+                            y.clone(),
+                            z.clone(),
+                        ));
+                    }
+                },
+                Blocks::TimeDefinition(TimeAnchorBlock { timeref, freq, .. }) => {
+                    match self.last_timeref {
+                        Some(init_t) => {
+                            let generator = self.generator.as_ref().unwrap();
+                            // get the frequency
+                            let freq = freq.ok_or("Missing frequency.")?;
+                            // set last position as the initial position
+                            let starting_pos = if self.trajectory.points.is_empty() {
+                                let (ix, iy, iz) = self.initial_coordinates;
+                                Point::new(ix, iy, iz, init_t)
+                            } else {
+                                self.trajectory.points.last().unwrap().clone()
+                            };
+                            let end_t = *timeref;
+                            if end_t > init_t {
+                                let mut prev_t = init_t;
+                                let mut t = prev_t;
+                                while t < end_t {
+                                    let (x, y, z) = generator.generate(t - init_t);
+                                    let mut p = Point::new(x, y, z, t);
+                                    p.absolute_from(&starting_pos);
+                                    self.trajectory.push(p);
+
+                                    // update time reference
+                                    prev_t = t;
+                                    t = prev_t + freq
+                                }
+                                self.last_timeref = Some(t);
+                            } else {
+                                return Err("Invalid time reference".to_string());
+                            }
+                        }
+                        None => {
+                            self.last_timeref = Some(*timeref);
+                        }
+                    }
+                }
+            }
+        }
+        Ok(self.trajectory)
+    }
+}
+
+#[derive(Debug, Clone)]
+pub struct Trajectory {
+    points: Vec<Point>,
+}
+
+impl Trajectory {
+    fn push(&mut self, point: Point) {
+        self.points.push(point);
+    }
+}
+
+#[derive(Debug, Clone)]
+struct Point {
+    x: f64,
+    y: f64,
+    z: f64,
+    time: f64,
+}
+
+impl Point {
+    fn new(x: f64, y: f64, z: f64, time: f64) -> Self {
+        Self { x, y, z, time }
+    }
+
+    fn absolute_from(&mut self, other: &Point) {
+        self.x += other.x;
+        self.y += other.y;
+        self.z += other.z;
+    }
+}
+
+#[derive(Debug, Clone)]
+struct ProgramExecution {
+    blocks: Vec<Blocks>,
+}
+
+impl From<Program> for ProgramExecution {
+    fn from(program: Program) -> Self {
+        let Program(mut blocks) = program;
+        blocks.reverse();
+        Self { blocks }
+    }
+}
+
+impl Iterator for ProgramExecution {
+    type Item = Blocks;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        self.blocks.pop()
+    }
+}
+
+impl TimeExpr {
+    fn eval(&self, t: f64) -> f64 {
+        match self {
+            TimeExpr::Group(group) => group.eval(t),
+            TimeExpr::Num(num) => num.value,
+            TimeExpr::Op(op) => op.eval(t),
+            TimeExpr::TimeVar(_) => t,
+        }
+    }
+}
+
+impl Operation {
+    fn eval(&self, t: f64) -> f64 {
+        let (l, r) = (self.lhs.eval(t), self.rhs.eval(t));
+        match self.op {
+            Operator::Add => l + r,
+            Operator::Sub => l - r,
+            Operator::Mul => l * r,
+            Operator::Div => l / r,
+            Operator::Pow => l.powf(r),
+        }
+    }
+}
+
+impl GroupExpr {
+    fn eval(&self, t: f64) -> f64 {
+        self.value.eval(t)
+    }
+}
+
+struct PositionParametricGenerator {
+    x: TimeExpr,
+    y: TimeExpr,
+    z: TimeExpr,
+}
+
+impl PositionParametricGenerator {
+    fn new(x: TimeExpr, y: TimeExpr, z: TimeExpr) -> Self {
+        Self { x, y, z }
+    }
+
+    fn generate(&self, t: f64) -> (f64, f64, f64) {
+        (self.x.eval(t), self.y.eval(t), self.z.eval(t))
+    }
+}
diff --git a/on-host/arpist-traject/src/syntax.rs b/on-host/arpist-traject/src/syntax.rs
index a48bf781ef365841f67feb3735afb782d1e8b633..f6b27dee025a47c27541b25e43bca26e4ca90ff9 100644
--- a/on-host/arpist-traject/src/syntax.rs
+++ b/on-host/arpist-traject/src/syntax.rs
@@ -44,6 +44,15 @@ pub struct GeneratorBlock {
     pub parameters: Vec<ExprParameter>,
 }
 
+impl GeneratorBlock {
+    pub fn get(&self, name: &str) -> Option<&TimeExpr> {
+        self.parameters
+            .iter()
+            .find(|p| p.name == name)
+            .map(|p| &p.value)
+    }
+}
+
 #[derive(Debug, Clone)]
 pub struct NumberParameter {
     span: Range<usize>,
@@ -55,7 +64,7 @@ pub struct NumberParameter {
 pub struct ExprParameter {
     span: Range<usize>,
     name: String,
-    value: TimeExpr,
+    pub value: TimeExpr,
 }
 
 #[derive(Debug, Clone)]
@@ -80,15 +89,15 @@ impl TimeExpr {
 #[derive(Debug, Clone)]
 pub struct GroupExpr {
     span: Range<usize>,
-    value: Box<TimeExpr>,
+    pub value: Box<TimeExpr>,
 }
 
 #[derive(Debug, Clone)]
 pub struct Operation {
     span: Range<usize>,
-    op: Operator,
-    lhs: Box<TimeExpr>,
-    rhs: Box<TimeExpr>,
+    pub op: Operator,
+    pub lhs: Box<TimeExpr>,
+    pub rhs: Box<TimeExpr>,
 }
 
 #[derive(Debug, Clone)]
@@ -103,7 +112,7 @@ pub enum Operator {
 #[derive(Debug, Clone)]
 pub struct Number {
     span: Range<usize>,
-    value: f64,
+    pub value: f64,
 }
 
 impl Deref for Number {
diff --git a/on-host/test.txt b/on-host/test.txt
index d2e9fa60490bf2bf72a1975ad9ca7848a4a453a0..1115aea1b082ffe3e6ff12754613fd942c094e3a 100644
--- a/on-host/test.txt
+++ b/on-host/test.txt
@@ -1,7 +1,7 @@
 = [GPS]
-\ x: 0
-\ y: 0
-\ z: 0
+\ latitude: 0
+\ longitude: 0
+\ altitude: 0
 |
 @ T+0
 |