diff --git a/e2e/calculate-all-params.e2e-spec.ts b/e2e/calculate-all-params.e2e-spec.ts
index dcbd5b7c34bc1aa36b71d913555c0a3660ff7dc2..ace899e9eafbbf0eb508fdfb9969f4b577dc94a5 100644
--- a/e2e/calculate-all-params.e2e-spec.ts
+++ b/e2e/calculate-all-params.e2e-spec.ts
@@ -23,7 +23,7 @@ describe("ngHyd − calculate all parameters of all calculators", () => {
     11, 12, 13, 15, 17, 18, 19, 20,
     21,
     // 22 - Solveur is not calculated here because it is not independent
-    23
+    23, 24
   ];
 
   // for each calculator
diff --git a/e2e/check-translations.e2e-spec.ts b/e2e/check-translations.e2e-spec.ts
index d8491b8d970b93b1ca0cf6b46822aed0ff4cc0ee..40799429fd5507884c8fdc8b9cb6b368e2c9f089 100644
--- a/e2e/check-translations.e2e-spec.ts
+++ b/e2e/check-translations.e2e-spec.ts
@@ -25,7 +25,7 @@ describe("ngHyd − check translation of all calculators", () => {
   });
 
   // get calculators list (IDs) @TODO read it from config, but can't import jalhyd here :/
-  const calcTypes = [ 0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 15, 17, 18, 19, 20, 21, 22, 23 ];
+  const calcTypes = [ 0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 15, 17, 18, 19, 20, 21, 22, 23, 24 ];
 
   // options of "Language" selector on preferences page
   const langs = [ "English", "Français" ];
diff --git a/e2e/clone-all-calc.e2e-spec.ts b/e2e/clone-all-calc.e2e-spec.ts
index 1f0ba7fcb4e709057e83ac12fa424f117a700285..4f0b7c3d4c831e6e4deaeb316730ba53763e7158 100644
--- a/e2e/clone-all-calc.e2e-spec.ts
+++ b/e2e/clone-all-calc.e2e-spec.ts
@@ -23,7 +23,7 @@ describe("ngHyd − clone all calculators with all possible <select> values", ()
     11, 12, 13, 15, 17, 18, 19, 20,
     21,
     // 22 - Solveur is not cloned here because it is not independent
-    23
+    23, 24
   ];
 
   // for each calculator
diff --git a/src/app/calculators/trigo/trigo.config.json b/src/app/calculators/trigo/trigo.config.json
new file mode 100644
index 0000000000000000000000000000000000000000..6dbb51a8c4590022f6b10d9fc72652b9c7682b63
--- /dev/null
+++ b/src/app/calculators/trigo/trigo.config.json
@@ -0,0 +1,32 @@
+[
+    {
+        "id": "fs_trigo",
+        "type": "fieldset",
+        "defaultOperation": "COS",
+        "defaultUnit": "DEG",
+        "fields": [
+            {
+                "id": "select_operation",
+                "type": "select",
+                "source": "trigo_operation"
+            },
+            {
+                "id": "select_unit",
+                "type": "select",
+                "source": "trigo_unit"
+            }
+        ]
+    },
+    {
+        "id": "fs_params",
+        "type": "fieldset",
+        "fields": [ "X", "Y" ]
+    },
+    {
+        "type": "options",
+        "idCal": "Y",
+        "operationSelectId": "select_operation",
+        "unitSelectId": "select_unit",
+        "_help": "util/trigo.html"
+    }
+]
\ No newline at end of file
diff --git a/src/app/calculators/trigo/trigo.en.json b/src/app/calculators/trigo/trigo.en.json
new file mode 100644
index 0000000000000000000000000000000000000000..9208d6dcaed2d4670e24fe0729678c00fac881dc
--- /dev/null
+++ b/src/app/calculators/trigo/trigo.en.json
@@ -0,0 +1,20 @@
+{
+    "fs_trigo": "Trigonometric parameters",
+
+    "select_operation": "Operation",
+    "select_operation_0": "cos",
+    "select_operation_1": "sin",
+    "select_operation_2": "tan",
+    "select_operation_3": "cosh",
+    "select_operation_4": "sinh",
+    "select_operation_5": "tanh",
+
+    "select_unit": "Unit",
+    "select_unit_0": "Degrees",
+    "select_unit_1": "Radians",
+
+    "fs_params": "Equation parameters",
+
+    "X": "X",
+    "Y": "Y"
+}
\ No newline at end of file
diff --git a/src/app/calculators/trigo/trigo.fr.json b/src/app/calculators/trigo/trigo.fr.json
new file mode 100644
index 0000000000000000000000000000000000000000..15c5a8a4eaaeb08f0ba37d9cc332e6e2c8a4b52d
--- /dev/null
+++ b/src/app/calculators/trigo/trigo.fr.json
@@ -0,0 +1,20 @@
+{
+    "fs_trigo": "Paramètres trigonométriques",
+
+    "select_operation": "Opération",
+    "select_operation_0": "cos",
+    "select_operation_1": "sin",
+    "select_operation_2": "tan",
+    "select_operation_3": "cosh",
+    "select_operation_4": "sinh",
+    "select_operation_5": "tanh",
+
+    "select_unit": "Unité",
+    "select_unit_0": "Degrés",
+    "select_unit_1": "Radians",
+
+    "fs_params": "Paramètres de l'équation",
+
+    "X": "X",
+    "Y": "Y"
+}
\ No newline at end of file
diff --git a/src/app/formulaire/definition/concrete/form-trigo.ts b/src/app/formulaire/definition/concrete/form-trigo.ts
new file mode 100644
index 0000000000000000000000000000000000000000..510f8b3c9bef678ebe202b2d10f45d967a6b1de1
--- /dev/null
+++ b/src/app/formulaire/definition/concrete/form-trigo.ts
@@ -0,0 +1,53 @@
+import { IObservable } from "jalhyd";
+
+import { FormulaireBase } from "./form-base";
+import { FieldSet } from "../../fieldset";
+import { FormResultFixedVar } from "../form-result-fixedvar";
+
+/**
+ * Formulaire pour les fonctions trigonométriques
+ */
+export class FormulaireTrigo extends FormulaireBase {
+
+    /** id of select configuring operation */
+    private _operationSelectId: string;
+
+    /** id of select configuring unit */
+    private _unitSelectId: string;
+
+    protected parseOptions(json: {}) {
+        super.parseOptions(json);
+        this._operationSelectId = this.getOption(json, "operationSelectId");
+        this._unitSelectId = this.getOption(json, "unitSelectId");
+    }
+
+    public afterParseFieldset(fs: FieldSet) {
+        if (this._operationSelectId) {
+            const sel = fs.getFormulaireNodeById(this._operationSelectId);
+            if (sel) {
+                fs.properties.addObserver(this);
+            }
+        }
+        if (this._unitSelectId) {
+            const sel = fs.getFormulaireNodeById(this._unitSelectId);
+            if (sel) {
+                fs.properties.addObserver(this);
+            }
+        }
+    }
+
+    // interface Observer
+
+    public update(sender: IObservable, data: any) {
+        super.update(sender, data);
+        if (data.action === "propertyChange") {
+            /* if (data.name === "gridType") {
+                this.reset();
+                // Inclined grids have more input fields (OEntH and cIncl)
+                this.getFieldsetById("fs_grille").updateFields();
+                // Alpha and Beta are not always shown
+                this.getFieldsetById("fs_plan").updateFields();
+            } */
+        }
+    }
+}
diff --git a/src/app/formulaire/fieldset.ts b/src/app/formulaire/fieldset.ts
index 0681164586f2f3c9ac4b8e798ea6dd2963ee1772..cca4508d69b7809b040ef1c0f0a5605a0681f8f2 100644
--- a/src/app/formulaire/fieldset.ts
+++ b/src/app/formulaire/fieldset.ts
@@ -11,6 +11,8 @@ import {
     GrilleProfile,
     BiefRegime,
     Solveur,
+    TrigoOperation,
+    TrigoUnit,
 } from "jalhyd";
 
 import { FormulaireElement } from "./formulaire-element";
@@ -288,6 +290,11 @@ export class FieldSet extends FormulaireElement implements Observer {
             case "fs_water_line": // Bief
                 this.setSelectValueFromProperty("select_regime", "regime");
                 break;
+
+            case "fs_trigo": // Trigo
+                this.setSelectValueFromProperty("select_operation", "trigoOperation");
+                this.setSelectValueFromProperty("select_unit", "trigoUnit");
+                break;
         }
     }
 
@@ -351,6 +358,8 @@ export class FieldSet extends FormulaireElement implements Observer {
         this.setPropertyValueFromConfig(json, "defaultGridType", "gridType", GrilleType);
         this.setPropertyValueFromConfig(json, "defaultRegime", "regime", BiefRegime);
         this.setPropertyValueFromConfig(json, "varCalc", "varCalc");
+        this.setPropertyValueFromConfig(json, "defaultOperation", "trigoOperation", TrigoOperation);
+        this.setPropertyValueFromConfig(json, "defaultUnit", "trigoUnit", TrigoUnit);
 
         this.updateFields();
     }
@@ -402,6 +411,7 @@ export class FieldSet extends FormulaireElement implements Observer {
             switch (data.action) {
                 case "select":
                     const senderId: string = sender.id.replace(/\d+$/, "");
+                    console.log("fieldset.update()", sender.constructor.name, data);
                     switch (senderId) {
                         case "select_section": // sections paramétrées, courbes de remous, régimes uniformes
                             // "nodeType" is a property of the section child, not of the parent
@@ -442,6 +452,12 @@ export class FieldSet extends FormulaireElement implements Observer {
                         case "select_regime": // Bief
                             this.setPropValue("regime", data.value.value);
                             break;
+                        case "select_operation": // Trigo
+                            this.setPropValue("trigoOperation", data.value.value);
+                            break;
+                        case "select_unit": // Trigo
+                            this.setPropValue("trigoUnit", data.value.value);
+                            break;
                     }
                     break;
             }
diff --git a/src/app/formulaire/select-field.ts b/src/app/formulaire/select-field.ts
index b2ea54a8c44b80617e149fd9e6f05dc907a57239..174fc258c664be103a99cd378fa711daa3ee8237 100644
--- a/src/app/formulaire/select-field.ts
+++ b/src/app/formulaire/select-field.ts
@@ -9,9 +9,8 @@ import {
     LoiDebit,
     GrilleType,
     GrilleProfile,
-    Solveur,
-    ParamValueMode,
-    Session
+    TrigoUnit,
+    TrigoOperation
  } from "jalhyd";
 
 import { Field } from "./field";
@@ -198,6 +197,17 @@ export class SelectField extends Field {
                 this.addEntry(new SelectEntry(this._entriesBaseId + BiefRegime.Fluvial, BiefRegime.Fluvial));
                 this.addEntry(new SelectEntry(this._entriesBaseId + BiefRegime.Torrentiel, BiefRegime.Torrentiel));
                 break;
+
+            case "trigo_operation": // Trigo: opération (cos, sin…)
+                for (let j = 0; j < Object.keys(TrigoOperation).length / 2; j++) {
+                    this.addEntry(new SelectEntry(this._entriesBaseId + j, j));
+                }
+                break;
+
+            case "trigo_unit": // Trigo: unité (degrés, radians)
+                this.addEntry(new SelectEntry(this._entriesBaseId + TrigoUnit.DEG, TrigoUnit.DEG));
+                this.addEntry(new SelectEntry(this._entriesBaseId + TrigoUnit.RAD, TrigoUnit.RAD));
+                break;
         }
 
         this.afterParseConfig();
diff --git a/src/app/services/formulaire.service.ts b/src/app/services/formulaire.service.ts
index d8b6d13f8b70b15c23ca71bd479c3a89dbe92f8b..416056c4d436c6a3a177c20861bdf6e61423c6a3 100644
--- a/src/app/services/formulaire.service.ts
+++ b/src/app/services/formulaire.service.ts
@@ -87,6 +87,7 @@ export class FormulaireService extends Observable {
         this.calculatorPaths[CalculatorType.Bief] = "bief";
         this.calculatorPaths[CalculatorType.Solveur] = "solveur";
         this.calculatorPaths[CalculatorType.YAXB] = "yaxb";
+        this.calculatorPaths[CalculatorType.Trigo] = "trigo";
     }
 
     private get _intlService(): I18nService {
diff --git a/src/locale/messages.en.json b/src/locale/messages.en.json
index 4d329f2cbc297e6e36a3c9a4b057ad840e823f2b..8b45f7600fae877ccba848f43dba7e517f8ecccc 100644
--- a/src/locale/messages.en.json
+++ b/src/locale/messages.en.json
@@ -467,8 +467,10 @@
     "INFO_EXAMPLE_LABEL_PAB_COMPLETE": "Standard fish ladder",
     "INFO_EXAMPLES_TITLE": "Examples",
     "INFO_EXAMPLES_SUBTITLE": "Load standard examples",
-    "INFO_YAXB_TITRE": "Y = A.X + B",
-    "INFO_YAXB_TITRE_COURT": "Y=A.X+B",
+    "INFO_YAXB_TITRE": "Linear function",
+    "INFO_YAXB_TITRE_COURT": "Linear f.",
+    "INFO_TRIGO_TITRE": "Trigonometric function",
+    "INFO_TRIGO_TITRE_COURT": "Trigo. f.",
     "WARNING_WARNINGS_ABSTRACT": "%nb% warnings occurred during calculation",
     "WARNING_REMOUS_ARRET_CRITIQUE": "Calculation stopped: critical elevation reached at abscissa %x%",
     "WARNING_STRUCTUREKIVI_HP_TROP_ELEVE": "h/p must not be greater than 2.5. h/p is forced to 2.5",
diff --git a/src/locale/messages.fr.json b/src/locale/messages.fr.json
index c4ab404cfb5dbe946dbaa8f60719489020b48aa8..4ae53cad428f802274cc75a4c567684c7df556e6 100644
--- a/src/locale/messages.fr.json
+++ b/src/locale/messages.fr.json
@@ -466,8 +466,10 @@
     "INFO_EXAMPLE_LABEL_PAB_COMPLETE": "Passe à bassins type",
     "INFO_EXAMPLES_TITLE": "Exemples",
     "INFO_EXAMPLES_SUBTITLE": "Charger des exemples types",
-    "INFO_YAXB_TITRE": "Y = A.X + B",
-    "INFO_YAXB_TITRE_COURT": "Y=A.X+B",
+    "INFO_YAXB_TITRE": "Fonction affine",
+    "INFO_YAXB_TITRE_COURT": "F. affine",
+    "INFO_TRIGO_TITRE": "Fonction trigonométrique",
+    "INFO_TRIGO_TITRE_COURT": "F. trigo.",
     "WARNING_WARNINGS_ABSTRACT": "%nb% avertissements rencontrés lors du calcul",
     "WARNING_REMOUS_ARRET_CRITIQUE": "Arrêt du calcul&nbsp;: hauteur critique atteinte à l'abscisse %x%",
     "WARNING_STRUCTUREKIVI_HP_TROP_ELEVE": "h/p ne doit pas être supérieur à 2,5. h/p est forcé à 2,5",