From 24736d05776266783e2182a7840192634bd96d17 Mon Sep 17 00:00:00 2001 From: Lucas Faber Date: Tue, 14 Apr 2026 23:55:15 +0200 Subject: [PATCH] vivohost --- vivohost/compareStartUpScripts.py | 280 ++++++++++++++++ vivohost/compareTimesheets.py | 172 ++++++++++ vivohost/generateAlinaChart.py | 247 ++++++++++++++ vivohost/startTau_generic_Generated.m | 461 ++++++++++++++++++++++++++ 4 files changed, 1160 insertions(+) create mode 100644 vivohost/compareStartUpScripts.py create mode 100644 vivohost/compareTimesheets.py create mode 100644 vivohost/generateAlinaChart.py create mode 100644 vivohost/startTau_generic_Generated.m diff --git a/vivohost/compareStartUpScripts.py b/vivohost/compareStartUpScripts.py new file mode 100644 index 0000000..4fbfc88 --- /dev/null +++ b/vivohost/compareStartUpScripts.py @@ -0,0 +1,280 @@ +import re +import pprint +import re +from collections import defaultdict + + +# ===================================================================== +# Unified DynamicObject (replaces DynamicMObject + DynamicCppObject) +# ===================================================================== +class DynamicObject(dict): + """ + A unified dynamic object that: + - Behaves like a dict + - Supports attribute access + - Auto-creates nested fields on demand + """ + def __getattr__(self, name): + if name not in self: + self[name] = DynamicObject() + return self[name] + + def __setattr__(self, name, value): + self[name] = value + + def ensure_path(self, path_parts): + obj = self + for part in path_parts: + if part not in obj: + obj[part] = DynamicObject() + obj = obj[part] + return obj + + def __repr__(self): + return dict.__repr__(self) + + +# ===================================================================== +# MATLAB FRD REG PARSER +# ===================================================================== +def parse_oFRDReg_from_mfile(filename): + oFRDReg = DynamicObject() + + # Regex to capture: oFRDReg. = ; + pattern = re.compile(r"oFRDReg\.([A-Za-z0-9_.]+)\s*=\s*([^;]+);") + + with open(filename, "r") as f: + for line in f: + match = pattern.search(line) + if match: + full_path = match.group(1) + value_raw = match.group(2).strip() + + # Convert numeric/string values when possible + try: + value = eval(value_raw) + except: + value = value_raw + + # Split path: e.g. MSI.vcm_sel → ["MSI", "vcm_sel"] + parts = full_path.split(".") + + parts = [ p.upper() for p in parts ] + attr_name = parts[-1] + parent_path = parts[:-1] + + # Create nested path + parent = oFRDReg.ensure_path(parent_path) + setattr(parent, attr_name, value) + + return oFRDReg + +# ===================================================================== +# Macro expansion helper +# ===================================================================== +def expand_macro_pattern(pattern, **kwargs): + """ + Expand a macro pattern containing tokens like: + CH1##_##address##_OFFSET + into the final register string. + kwargs maps token names → values, e.g. address="CFG". + """ + + s = pattern + + # Replace tokens inside patterns like address##_XYZ + for key, value in kwargs.items(): + s = s.replace(key, value) + + # Resolve concatenation '##' + s = s.replace("##", "") + + # Clean up double underscores + while "__" in s: + s = s.replace("__", "_") + + return s + + +# ===================================================================== +# C MACRO PARSER +# ===================================================================== + +def remove_cpp_comments(text): + """ + Remove /* block comments */ and // line comments. + """ + # Remove multiline comments + text = re.sub(r"/\*.*?\*/", "", text, flags=re.DOTALL) + + # Removedefines + text = re.sub(r"#define.*", "", text) + + # Remove single-line comments + text = re.sub(r"//.*", "", text) + + return text + +def parse_c_macros(filename): + registers = DynamicObject() + + # Macro → register expansion patterns + macro_definitions = { + # 3-argument write (slow/fast) + "SLOW_WRITE_FIELD": ( + "CH1_##address##", + "CH1_##address##_##field##", + ), + "FAST_WRITE_FIELD": ( + "CH0_##address##", + "CH0_##address##_##field##", + ), + + # Read field, 2 arguments + "SLOW_READ_FIELD": ( + "CH1_##address##", + "CH1_##address##_##field##", + ), + "FAST_READ_FIELD": ( + "CH0_##address##", + "CH0_##address##_##field##", + ), + + # Register read/write (1 or 2 arguments) + "SLOW_READ_REGISTER": ( + "CH1_##address##", + ), + "FAST_READ_REGISTER": ( + "CH0_##address##", + ), + "SLOW_WRITE_REGISTER": ( + "CH1_##address##", + ), + "FAST_WRITE_REGISTER": ( + "CH0_##address##", + ), + "FAST_WRITE_INHIBIT": ( + "CH0##_##address1##_CH0_##address2##", + ), + "SLOW_WRITE_INHIBIT": ( + "CH1##_##address1##_CH0_##address2##", + ) + } + + # Generic regex for ALL macro calls (1–4 args) + + call_regex = re.compile( + r"([A-Z_]+)\s*\(\s*([A-Za-z0-9_]+)" # FUN and arg1: address1 + r"(?:\s*,\s*([A-Za-z0-9_]+))?" # arg2: field, address2 + r"(?:\s*,\s*([A-Za-z0-9_+\-*/() ]+))?" # arg3: field, data + r"(?:\s*,\s*([A-Za-z0-9_+\-*/() ]+))?" # arg4: data (expression allowed) + r"\s*\)" + ) + + with open(filename, "r") as f: + text = f.read() + + text = remove_cpp_comments(text) + + for match in call_regex.finditer(text): + macro, a1, a2, a3, a4 = match.groups() + + text = text.replace(match.group(0), "") # remove after processing + + if macro not in macro_definitions or "READ" in macro: + continue + + patterns = macro_definitions[macro] + + # Build the macro variable map + kwargs = {} + if a4 == None: + if a1: kwargs["address"] = a1 + if a2: kwargs["field"] = a2 + if a3: kwargs["data"] = a3 + else: + if a1: kwargs["address1"] = a1 + if a2: kwargs["address2"] = a2 + if a3: kwargs["field"] = a3 + if a4: kwargs["data"] = a4 + + # Expand patterns into real register names + expanded = [expand_macro_pattern(p, **kwargs) for p in patterns] + + # Store results + if a2 and a3: # WRITE_FIELD + registers[expanded[0].upper()] = {a2.upper(): a3} + else: # REGISTER read/write + registers[expanded[0].upper()] = a3 + + return registers + +def upper_keys_recursive(d): + new_dict = {} + for k, v in d.items(): + key_up = str(k).upper() + + if isinstance(v, dict): + new_dict[key_up] = upper_keys_recursive(v) + else: + new_dict[key_up] = v + + return new_dict + + +def diff_dicts(d1, d2, path="",dictnames = ["M","CPP"]): + """ + Recursively compare two dictionaries and print differences. + Shows: + - Keys only in d1 + - Keys only in d2 + - Keys in both where values differ + """ + + + + # Keys present in both dictionaries + common_keys = d1.keys() & d2.keys() + + # Keys only in first dict + only_d1 = d1.keys() - d2.keys() + for key in sorted(only_d1): + print(f"{path}{key}: only in {dictnames[0]}") + + # Keys only in second dict + only_d2 = d2.keys() - d1.keys() + for key in sorted(only_d2): + print(f"{path}{key}: only in {dictnames[1]}") + + # Compare common keys + for key in sorted(common_keys): + v1 = d1[key] + v2 = d2[key] + new_path = f"{path}{key}." + + # If both values are dictionaries → recurse + if isinstance(v1, dict) and isinstance(v2, dict): + diff_dicts(v1, v2, new_path) + continue + + # If values differ → print + if v1 != v2: + print(f"{path}{key}:") + print(f" {dictnames[0]} = {v1}") + print(f" {dictnames[1]} = {v2}") +# ===================================================================== +# Example usage +# ===================================================================== + + +# Example usage: +if __name__ == "__main__": + obj = parse_oFRDReg_from_mfile("startTau_generic_Generated.m") + pprint.pprint(obj) + + regs = parse_c_macros("/home/lucas/git/mems_stable/trixel3_software/tic/source/trixel3_tic_mems_model_lbd001rrxab11.cpp") + + pprint.pprint(regs) + + diff_dicts(regs,obj) + print("Fin") \ No newline at end of file diff --git a/vivohost/compareTimesheets.py b/vivohost/compareTimesheets.py new file mode 100644 index 0000000..b5a020a --- /dev/null +++ b/vivohost/compareTimesheets.py @@ -0,0 +1,172 @@ +import pandas as pd +import pprint +from datetime import datetime +import json +# Create a PrettyPrinter with custom settings + +pp = pprint.PrettyPrinter( + indent=2, + width=100000, # large line width to avoid wrapping + compact=False, # don't compress structures + depth=None, # don't limit nesting depth + sort_dicts=False +) +# load data +absences = pd.read_excel("Activity Timeoff 2025_01_01 - 2026_02_28.xlsx") +# Load Excel (or CSV) file +# For Excel: timesheet = pd.read_excel("timesheet.xlsx", engine="openpyxl") +timesheets = pd.read_excel("[ALL] Template_Time_Sheets_FFG - Unified.xlsx", sheet_name=None) +df_timesheet = timesheets["2025"] + +## Sort RATES +df_rates = timesheets["Stundensätze"] +# Remove all non-numeric characters (keep digits and dot) +hourlyRateKey = "Hourly Rate for Application €/h" +df_rates = df_rates.rename(columns={hourlyRateKey: 'rate'}) +hourlyRateKey = 'rate' +df_rates[hourlyRateKey] = df_rates[hourlyRateKey].astype(str).str.replace(r'[^0-9.]', '', regex=True) +# Ensure the Hourly Rate column is numeric +df_rates[hourlyRateKey] = pd.to_numeric(df_rates[hourlyRateKey], errors='coerce') + + +## FORMAT timesheet data frame +# Fill missing Activity for earlier rows if necessary +df_timesheet['Activity'] = df_timesheet['Activity'].ffill() # forward-fill the activity for blank cells +# Convert 'Date' to datetime +df_timesheet['Date'] = pd.to_datetime(df_timesheet['Date'], errors='coerce') +# Forward-fill missing dates and activities +df_timesheet['Date'] = df_timesheet['Date'].ffill() +df_timesheet['Activity'] = df_timesheet['Activity'].ffill() +# Filter out rows where 'Person' is missing +df_timesheet_filtered = df_timesheet.dropna(subset=['Person']).copy() + + +# #FILETER ABSECES +absences['Start date'] = pd.to_datetime(absences['Start date'], errors='coerce') +absences['End date'] = pd.to_datetime(absences['End date'], errors='coerce') +df_absences_filtered = absences +df_absences_by_year = {} +for year in df_absences_filtered['Start date'].dt.year.unique(): + # Filter for the year + df_year = df_absences_filtered[df_absences_filtered['Start date'].dt.year == year].copy() + # Combine first and last name into a new column 'Person' + df_year['Person'] = df_year['Last name'].astype(str) + ", " + df_year['First name'].astype(str) + # Select and reorder columns + df_year = df_year[['Person', 'Start date', 'End date']] + # Reset index + df_year = df_year.reset_index(drop=True) + # Store in dictionary + df_absences_by_year[year] = df_year + +for year, df in df_absences_by_year.items(): + # Function to find matching full name(s) for a person + def get_short_name(person): + # Remove spaces from person + person_clean = str(person).replace(" ", "") + # Remove spaces from df_rates['Name'] and check for match + matches = df_rates[df_rates['Name'].str.replace(" ", "", regex=False) + .str.contains(person_clean, case=False, na=False)] + if not matches.empty: + # Return only the first match + return str(matches['Short'].iloc[0]) + + return "" # no match found + # Apply the function row-wise to create a new column + df['Short'] = df['Person'].apply(get_short_name) + +# Keep only the row with the highest Hourly Rate per Name +df_rates = df_rates.loc[df_rates.groupby('Short')[hourlyRateKey].idxmax()].reset_index(drop=True) + +# Sort By Year +# Create dictionary of DataFrames for each year +df_timesheet_by_year = {} +for year in df_timesheet_filtered['Date'].dt.year.unique(): + # Filter for the year + df_year = df_timesheet_filtered[df_timesheet_filtered['Date'].dt.year == year].copy() + # Convert 'Workpackage' to integer (nullable Int64 to handle NaN safely) + df_year['Workpackage'] = pd.to_numeric(df_year['Workpackage'], errors='coerce').astype('Int64') + # Select and reorder columns + df_year = df_year[['Person', 'Date', 'Workpackage', 'Time (in hours)', 'Activity']] + # Reset index + df_year = df_year.reset_index(drop=True) + # Store in dictionary + df_timesheet_by_year[year] = df_year + +for year, df in df_timesheet_by_year.items(): + # Function to find matching full name(s) for a person + def get_full_name(person): + matches = df_rates[df_rates['Short'].str.contains(str(person), case=False, na=False)] + if not matches.empty: + return str(matches['Name'].iloc[0]) + return "" # no match found + # Apply the function row-wise to create a new column + df['FullName'] = df['Person'].apply(get_full_name) + + + +# Build nested dictionary +data = {} +for year in df_timesheet_by_year.keys(): + for _, row in df_timesheet_by_year[year].iterrows(): + person = row['Person'] + date = row['Date'] + date_str = date.strftime("%Y-%m-%d") + # Skip rows without a person + if pd.isna(person): + continue + + data.setdefault(year, {}).setdefault(person, {}).setdefault("absences", []) + data.setdefault(year, {}).setdefault(person, {}).setdefault("workingdays", {}) + data[year][person]["workingdays"][date_str] = { + "workpackage": row['Workpackage'], + "time": row['Time (in hours)'], + "activity": row['Activity'] + } + + # Group by Person and Workpackage and sum hours + summary = df_timesheet_by_year[year].groupby(['FullName', 'Workpackage'])['Time (in hours)'].sum().reset_index() + # Merge hourly rate into summary using the full name + summary = summary.merge( + df_rates[['Name', hourlyRateKey]], + left_on='FullName', + right_on='Name', + how='left' + ) + # Compute costs + summary['costs'] = summary['Time (in hours)'] * summary[hourlyRateKey] + # Show all columns and rows + print(summary.to_string(index=False)) # index=False hides row numbers + +for year in df_absences_by_year.keys(): + for _, row in df_absences_by_year[year].iterrows(): + person = row['Short'] + start_date = row['Start date'] + end_date = row['End date'] + + # Ensure nested structure exists + data.setdefault(year, {}).setdefault(person, {}).setdefault("absences", []) + data.setdefault(year, {}).setdefault(person, {}).setdefault("workingdays", {}) + data[year][person]["absences"].append({"start": start_date, "end": end_date}) + +for year in data.keys(): + for person in data[year].keys(): + for absence in data[year][person]["absences"]: + # Generate all dates between start and end + all_dates = pd.date_range(start=absence["start"], end=absence["end"], freq='B') # 'B' = business days (Mon-Fri) + # Create a dict with dates as keys and empty dict as value + workdays_dict = set(data[year][person]['workingdays'].keys()) + absence_dict = {date.strftime("%Y-%m-%d"): {} for date in all_dates} + + # Get sets of dates + absence_dates = set(absence_dict) + working_dates = set(data[year][person]['workingdays'].keys()) + + + # Find intersection + overlap = absence_dates & working_dates + sorted_overlap = sorted(overlap, key=lambda x: datetime.strptime(x, "%Y-%m-%d")) + if overlap and absence["start"] != absence["end"]: + print(f"Dates overlapping for {person}: {overlap} - Vac: {absence["start"].strftime("%Y-%m-%d")} - {absence["end"].strftime("%Y-%m-%d")}") + elif overlap: + print(f"Dates overlapping for {person}: {overlap} - Vac: {absence["start"].strftime("%Y-%m-%d")}") +print("fin") \ No newline at end of file diff --git a/vivohost/generateAlinaChart.py b/vivohost/generateAlinaChart.py new file mode 100644 index 0000000..e58d4be --- /dev/null +++ b/vivohost/generateAlinaChart.py @@ -0,0 +1,247 @@ +import pandas as pd +import matplotlib.pyplot as plt +import os + +def interpolate_daily(df): + df = df.copy() + df["Datum"] = pd.to_datetime(df["Datum"], errors="coerce") + df = df.sort_values("Datum").set_index("Datum") + + daily_index = pd.date_range(df.index.min(), df.index.max(), freq="D") + + return ( + df.reindex(daily_index) + .interpolate(method="time") + .reset_index() + .rename(columns={"index": "Datum"}) + ) + +def plot_percentiles(ax, x, data_dict, p_keys, colors=None, linewidths=None, labels=None, fill=True, fill_color="lightcoral", fill_alpha=0.25): + """ + Plot multiple percentile curves from a dictionary like who_head_daily. + + Parameters + ---------- + ax : matplotlib axis + x : array-like (x values) + data_dict : dict-like, e.g. who_head_daily + p_keys : list of keys, e.g. ["P3", "P50", "P97"] + colors : list of colors (optional) + linewidths : list of linewidths (optional) + fill : whether to fill between min and max percentile + :param labels: + """ + + n = len(x) + + if colors is None: + colors = [None] * len(p_keys) + if linewidths is None: + linewidths = [1.5] * len(p_keys) + + # Plot lines + for i, key in enumerate(p_keys): + ax.plot( + x, + data_dict[key][:n], + color=colors[i], + linewidth=linewidths[i], + label=labels[i] + ) + + # Fill between min and max percentile + if fill and len(p_keys) >= 2: + lower = data_dict[p_keys[0]][:n] + upper = data_dict[p_keys[-1]][:n] + + ax.fill_between( + x, + lower, + upper, + color=fill_color, + alpha=fill_alpha + ) + + return ax + +# Build output path from ods_path +ods_path = "/data/Nextcloud/Documents/Dokumente/Shared/Alina/Wachstum.ods" +output_dir = os.path.dirname(ods_path) +output_path = os.path.join(output_dir, "Wachstum.png") + +# Load specific sheets into data structures (DataFrames) +alina_df = pd.read_excel( + ods_path, + sheet_name="Alina", + engine="odf" +) + +who_weight_df = pd.read_excel( + ods_path, + sheet_name="WHO Gewicht", + engine="odf" +) +who_height_df = pd.read_excel( + ods_path, + sheet_name="WHO Größe", + engine="odf" +) +who_head_df = pd.read_excel( + ods_path, + sheet_name="WHO Kopfumfang", + engine="odf" +) + +# Ensure Datum is datetime (safe to run even if already converted) +who_weight_df["Datum"] = pd.to_datetime(who_weight_df["Datum"],format="%d.%m.%Y",errors="coerce") +who_height_df["Datum"] = pd.to_datetime(who_height_df["Datum"],format="%d.%m.%Y",errors="coerce") +who_head_df["Datum"] = pd.to_datetime(who_weight_df["Datum"],format="%d.%m.%Y",errors="coerce") + +who_weight_daily = interpolate_daily(who_weight_df) +who_height_daily = interpolate_daily(who_height_df) +who_head_daily = interpolate_daily(who_head_df) + +alina_df["Datum"] = pd.to_datetime(alina_df["Datum"],format="%d.%m.%Y",errors="coerce") + +# Plot +last_alina_date = alina_df["Datum"].max() + pd.DateOffset(months=2) +x = who_weight_daily["Datum"][who_weight_daily["Datum"] <= last_alina_date] +fig, axes = plt.subplots( + nrows=3, + ncols=1, + figsize=(12, 10), + sharex=True +) + +percentile_list = ['P1', 'P3', 'P10', 'P25', + 'P50', 'P75', 'P90', 'P97', 'P99'] +colors_list = ["lightcoral" if p != "P50" else "darkred" for p in percentile_list] +linewidths_list = [1.5 if p != "P50" else 2.5 for p in percentile_list] +label_list = [p if p == "P50" or p == "P3" or p == "P97" else "" for p in percentile_list] +# ---------- 1. WEIGHT ---------- +ax = axes[0] +plot_percentiles( + ax, + x, + who_weight_daily, + percentile_list, + colors=colors_list, + linewidths=linewidths_list, + labels=label_list +) + + +ax.plot(alina_df["Datum"], alina_df["Gewicht"], + color="green", linewidth=1.5, marker="o", label="Alina") + +last_idx = alina_df["Gewicht"].last_valid_index() +who_row = who_weight_daily.loc[who_weight_daily["Datum"] == alina_df["Datum"][last_idx]].iloc[0] +perzentil = (alina_df["Gewicht"][last_idx]-who_row["P50"])/((who_row["P97"]-who_row["P50"])/47)+50 + +ax.text(alina_df["Datum"].iloc[-1]+pd.DateOffset(days=1), alina_df["Gewicht"].iloc[-1]+0.5,f"{perzentil:.2f} %", + fontsize=10, + ha="left", + va="bottom", + color="green") + +ax.set_ylabel("Gewicht [kg]") +ax.set_title("Wachstum") +# Enable minor ticks +ax.minorticks_on() + +# Configure major grid (e.g., solid, darker) +ax.grid(which='major', color='black', linestyle='-', linewidth=0.8) + +# Configure minor grid (e.g., dashed, lighter, thinner) +ax.grid(which='minor', color='gray', linestyle='--', linewidth=0.4, alpha=0.5) +ax.legend() + + +# ---------- 2. HEIGHT ---------- +ax = axes[1] +plot_percentiles( + ax, + x, + who_height_daily, + percentile_list, + colors=colors_list, + linewidths=linewidths_list, + labels=label_list +) +ax.plot(alina_df["Datum"], alina_df["Größe"], + color="green", linewidth=1.5, marker="o") + +last_idx = alina_df["Größe"].last_valid_index() +who_row = who_height_daily.loc[who_height_daily["Datum"] == alina_df["Datum"][last_idx]].iloc[0] +perzentil = (alina_df["Größe"][last_idx]-who_row["P50"])/((who_row["P97"]-who_row["P50"])/47)+50 + +ax.text(alina_df["Datum"].iloc[-1]+pd.DateOffset(days=1), alina_df["Größe"].iloc[-1]+0.5,f"{perzentil:.2f} %", + fontsize=10, + ha="left", + va="bottom", + color="green") + + +ax.set_ylabel("Größe [cm]") +# Enable minor ticks +ax.minorticks_on() + +# Configure major grid (e.g., solid, darker) +ax.grid(which='major', color='black', linestyle='-', linewidth=0.8) +# Configure minor grid (e.g., dashed, lighter, thinner) +ax.grid(which='minor', color='gray', linestyle='--', linewidth=0.4, alpha=0.5) + + +# ---------- 3. HEAD CIRCUMFERENCE ---------- +ax = axes[2] + +plot_percentiles( + ax, + x, + who_head_daily, + percentile_list, + colors=colors_list, + linewidths=linewidths_list, + labels=label_list +) + +ax.plot(alina_df["Datum"], alina_df["Kopfumfang"], + color="green", linewidth=1.5, marker="o") + +last_idx = alina_df["Kopfumfang"].last_valid_index() +who_row = who_head_daily.loc[who_head_daily["Datum"] == alina_df["Datum"][last_idx]].iloc[0] +perzentil = (alina_df["Kopfumfang"][last_idx]-who_row["P50"])/((who_row["P97"]-who_row["P50"])/47)+50 + +ax.text(alina_df["Datum"].iloc[-1]+pd.DateOffset(days=1), alina_df["Kopfumfang"].iloc[-1]+0.5,f"{perzentil:.2f} %", + fontsize=10, + ha="left", + va="bottom", + color="green") + +ax.set_ylabel("Kopfumfang [cm]") +# Enable minor ticks +ax.minorticks_on() + +# Configure major grid (e.g., solid, darker) +ax.grid(which='major', color='black', linestyle='-', linewidth=0.8) + +# Configure minor grid (e.g., dashed, lighter, thinner) +ax.grid(which='minor', color='gray', linestyle='--', linewidth=0.4, alpha=0.5) + +# ---------- X‑AXIS ---------- +axes[-1].set_xlabel("Datum") + +N = 7 # label every Nth tick + +axes[-1].set_xticks(x[::N]) +axes[-1].set_xticklabels( + x.iloc[::N].dt.strftime("%d.%m.%Y"), + rotation=45, + ha="right" +) + +plt.tight_layout() +plt.savefig(output_path, dpi=150) +plt.close() + +print("Fin") diff --git a/vivohost/startTau_generic_Generated.m b/vivohost/startTau_generic_Generated.m new file mode 100644 index 0000000..b6a498b --- /dev/null +++ b/vivohost/startTau_generic_Generated.m @@ -0,0 +1,461 @@ +function startTau_generic_Generated.m(oFRDReg, options) + arguments + oFRDReg + options.sTimeOut (1,1) double = 10.0 + end + + % Generated from sequence: LBDB11_Start_Tau_generic_Copy + % Sequence ID: 1722837829 + + + % ------ reset DUT and put into IDLE ------ + % Unsupported command type: DisplayController_Command_SetResetNPinState + % Unsupported command type: DisplayController_Command_SetClkMirror + % valid for 10MHz input-clock + oFRDReg.CLK_PLL.CLK_PLL = 3857; % 0x0F11 + + % Wait for driver entering IDLE... + sTimeOut = options.sTimeOut; + sStart = tic; + while(oFRDReg.OP_STATUS.opmode ~= 1) + if toc(sStart) > sTimeOut + error('ifx:runtime_error', 'Timeout waiting for OP_STATUS.opmode to equal 1'); + end + end + + % ------ MSI config ------ + oFRDReg.MSI.vcm_sel = 2; + oFRDReg.MSI.cmp_clk_provision = 1; + oFRDReg.MSI.ib_sel = 4; + oFRDReg.MSI.ib_source_sel = 1; + + % ------ Slow/Fast MSI config ------ + oFRDReg.CH1_MSI1.peak_det_source = 0; + oFRDReg.CH1_MSI1.peak_det_pol = 0; + oFRDReg.CH1_MSI1.tia_res_sel = 15; + oFRDReg.CH1_MSI1.tia_cap_sel = 15; + oFRDReg.CH1_MSI1.sah_tc = 2; + oFRDReg.CH1_MSI1.swap_polarity = 0; + oFRDReg.CH1_MSI2.sah_refdac_sel = 1; + oFRDReg.CH1_MSI2.sah_en = 0; + oFRDReg.CH1_MSI2.refdac_en = 1; + oFRDReg.CH1_MSI2.refdac_sel = 4; + oFRDReg.CH1_MSI2.ref_div_trim = 5; + oFRDReg.CH0_MSI1.peak_det_source = 0; + oFRDReg.CH0_MSI1.peak_det_pol = 0; + oFRDReg.CH0_MSI1.tia_res_sel = 15; + oFRDReg.CH0_MSI1.tia_cap_sel = 15; + oFRDReg.CH0_MSI1.sah_tc = 2; + oFRDReg.CH0_MSI1.swap_polarity = 0; + oFRDReg.CH0_MSI2.sah_refdac_sel = 1; + oFRDReg.CH0_MSI2.sah_en = 0; + oFRDReg.CH0_MSI2.refdac_en = 1; + oFRDReg.CH0_MSI2.refdac_sel = 0; + oFRDReg.CH0_MSI2.ref_div_trim = 0; + + % ------ Slow/Fast subtiming config ------ + oFRDReg.CH1_ST_MODULO.cnt_value = 7000; + oFRDReg.CH1_ST_POS.cnt_value = 0; + oFRDReg.CH1_ST_MOV.cnt_value = 3500; + oFRDReg.CH1_ST_DRIVE_ON.cnt_value = 3500; + oFRDReg.CH1_ST_DRIVE_OFF_MIN.cnt_value = 262; + oFRDReg.CH1_ST_DRIVE_OFF.cnt_value = 263; + oFRDReg.CH1_ST_DRIVE_OFF_MAX.cnt_value = 2188; + oFRDReg.CH0_ST_MODULO.cnt_value = 1700; + oFRDReg.CH0_ST_POS.cnt_value = 0; + oFRDReg.CH0_ST_MOV.cnt_value = 850; + oFRDReg.CH0_ST_DRIVE_ON.cnt_value = 850; + oFRDReg.CH0_ST_DRIVE_OFF_MIN.cnt_value = 54; + oFRDReg.CH0_ST_DRIVE_OFF.cnt_value = 55; + oFRDReg.CH0_ST_DRIVE_OFF_MAX.cnt_value = 805; + + % ------ Slow/Fast ADC config ------ + oFRDReg.CH1_ST_ADC_VTEMP.cnt_value = 1; + oFRDReg.CH1_ST_ADC_MIRRCURR.cnt_value = 88; + % currently disabled by setting to MODULO+1 + oFRDReg.CH1_ST_ADC_HVD.cnt_value = 7001; + oFRDReg.CH1_ADC_SCHED1.angle_amplitude = 2; + oFRDReg.CH1_ADC_SCHED2.hv = 200; + oFRDReg.CH1_ADC_SCHED3.temperature = 100; + oFRDReg.CH0_ST_ADC_VTEMP.cnt_value = 1; + oFRDReg.CH0_ST_ADC_MIRRCURR.cnt_value = 45; + % currently disabled by setting to MODULO+1 + oFRDReg.CH0_ST_ADC_HVD.cnt_value = 1701; + oFRDReg.CH0_ADC_SCHED1.angle_amplitude = 2; + oFRDReg.CH0_ADC_SCHED2.hv = 200; + oFRDReg.CH0_ADC_SCHED3.temperature = 100; + + % ------ Slow/Fast Amp.-Sense + control config ------ + + % slow - amplitude detection + oFRDReg.CH1_AMPL_SENSE1.method = 0; + oFRDReg.CH1_AMPL_SENSE1.diff_comp_pol = 0; + oFRDReg.CH1_AMPL_SENSE1.asym_comp_en = 1; + oFRDReg.CH1_AMPL_SENSE1.alpha_filter = 5; + oFRDReg.CH1_AMPL_SENSE1.filter_en = 1; + oFRDReg.CH1_MSI2.sah_refdac_sel = 1; + oFRDReg.CH1_MSI2.sah_en = 0; + oFRDReg.CH1_MSI2.refdac_en = 1; + oFRDReg.CH1_MSI2.refdac_sel = 0; + oFRDReg.CH1_MSI2.ref_div_trim = 0; + oFRDReg.CH1_ST_AMPL_DET_START.cnt_value = 6193; + oFRDReg.CH1_ST_AMPL_DET_STOP.cnt_value = 6543; + oFRDReg.CH1_ST_AMPL_CTRL_SETPOINT.cnt_value = 6368; + oFRDReg.CH1_ST_CMP_DIFF_EN_START.cnt_value = 3500; + oFRDReg.CH1_ST_CMP_DIFF_EN_STOP.cnt_value = 175; + + % slow - amplitude control + oFRDReg.CH1_AMPL_CTRL1.means_of_control = 0; + oFRDReg.CH1_AMPL_CTRL1.i_gain = 2; + oFRDReg.CH1_AMPL_CTRL2.p_gain = 20; + oFRDReg.CH1_AMPL_CTRL1.source_pid = 1; + oFRDReg.CH1_ST_CONTROL_ACTION.cnt_value = 262; + oFRDReg.CH1_ST_CONTROL_ACTION.en = 1; + oFRDReg.CH1_AMPL_CTRL1.phase_comp_en = 1; + oFRDReg.CH1_AMPL_CTRL1.phase_comp_op = 1; + + % slow - enable amplitude sensing + oFRDReg.CH1_CFG.ampl_sense_en = 1; + + % fast - peak sensing + + % for anti phase drive the tracking period needs to be very narrow otherwise inhibiting happens + oFRDReg.CH0_ST_MIRRCURR_PEAK_START.cnt_value = 1029; + oFRDReg.CH0_ST_MIRRCURR_PEAK_STOP.cnt_value = 54; + oFRDReg.CH0_ST_MIRRCURR_PEAK_RESET.cnt_value = 828; + oFRDReg.CH0_ST_MIRRCURR_PEAK_URESET.cnt_value = 939; + + % fast - amplitude detection + oFRDReg.CH0_AMPL_SENSE1.method = 0; + oFRDReg.CH0_AMPL_SENSE1.diff_comp_pol = 0; + oFRDReg.CH0_AMPL_SENSE1.asym_comp_en = 1; + oFRDReg.CH0_AMPL_SENSE1.alpha_filter = 7; + oFRDReg.CH0_AMPL_SENSE1.filter_en = 0; + oFRDReg.CH0_MSI2.sah_refdac_sel = 1; + oFRDReg.CH0_MSI2.sah_en = 0; + oFRDReg.CH0_MSI2.refdac_en = 1; + oFRDReg.CH0_MSI2.refdac_sel = 0; + oFRDReg.CH0_MSI2.ref_div_trim = 0; + oFRDReg.CH0_ST_AMPL_DET_START.cnt_value = 1188; + oFRDReg.CH0_ST_AMPL_DET_STOP.cnt_value = 1366; + oFRDReg.CH0_ST_AMPL_CTRL_SETPOINT.cnt_value = 1277; + oFRDReg.CH0_ST_CMP_DIFF_EN_START.cnt_value = 850; + oFRDReg.CH0_ST_CMP_DIFF_EN_STOP.cnt_value = 89; + oFRDReg.CH0_ST_AMPL_SH_START.cnt_value = 1342; + oFRDReg.CH0_ST_AMPL_SH_STOP.cnt_value = 1432; + + % fast - amplitude control + oFRDReg.CH0_AMPL_CTRL1.means_of_control = 0; + oFRDReg.CH0_AMPL_CTRL1.i_gain = 100; + oFRDReg.CH0_AMPL_CTRL2.p_gain = 100; + oFRDReg.CH0_AMPL_CTRL1.source_pid = 1; + oFRDReg.CH0_ST_CONTROL_ACTION.cnt_value = 54; + oFRDReg.CH0_ST_CONTROL_ACTION.en = 1; + oFRDReg.CH0_AMPL_CTRL1.phase_comp_en = 1; + oFRDReg.CH0_AMPL_CTRL1.phase_comp_op = 1; + + % fast - enable amplitude sensing + oFRDReg.CH0_CFG.ampl_sense_en = 1; + + % ------ Slow/Fast Phase-control config ------ + + % slow + oFRDReg.CH1_ST_PHASEDET_START.cnt_value = 6825; + oFRDReg.CH1_ST_PHASEDET_STOP.cnt_value = 175; + oFRDReg.CH1_ST_ZCREF_RESET.cnt_value = 3500; + oFRDReg.CH1_ST_CMP_SUM_EN_START.cnt_value = 6781; + oFRDReg.CH1_ST_CMP_SUM_EN_STOP.cnt_value = 175; + oFRDReg.CH1_ST_CONTROL_ACTION.cnt_value = 176; + oFRDReg.CH1_ST_CONTROL_ACTION.en = 1; + oFRDReg.CH1_PHASE_CTRL1.i_gain = 255; + oFRDReg.CH1_PHASE_CTRL2.p_gain = 600; + oFRDReg.CH1_PHASE_CTRL1.asym_comp_en = 1; + oFRDReg.CH1_PHASE_CTRL1.means_of_control = 0; + oFRDReg.CH1_PHASE_CTRL3.delay = 3; + + % fast + oFRDReg.CH0_ST_PHASEDET_START.cnt_value = 1655; + oFRDReg.CH0_ST_PHASEDET_STOP.cnt_value = 45; + oFRDReg.CH0_ST_ZCREF_RESET.cnt_value = 850; + oFRDReg.CH0_ST_CMP_SUM_EN_START.cnt_value = 1610; + oFRDReg.CH0_ST_CMP_SUM_EN_STOP.cnt_value = 45; + oFRDReg.CH0_ST_CONTROL_ACTION.cnt_value = 49; + oFRDReg.CH0_ST_CONTROL_ACTION.en = 1; + oFRDReg.CH0_PHASE_CTRL1.i_gain = 255; + oFRDReg.CH0_PHASE_CTRL2.p_gain = 600; + oFRDReg.CH0_PHASE_CTRL1.asym_comp_en = 1; + oFRDReg.CH0_PHASE_CTRL1.means_of_control = 0; + oFRDReg.CH0_PHASE_CTRL3.delay = 1; + + % ------ Slow/Fast HV config ------ + % approximately HV= 0,72 * lead -> 72V + oFRDReg.CH1_HVD1.lead = 100; + oFRDReg.CH1_HVD3.lag = 100; + oFRDReg.CH1_HVD2.disch_trim = 7; + oFRDReg.CH1_HVD1.en = 1; + oFRDReg.CH1_HVD1.peak_det_res_zcs = 10; + oFRDReg.CH1_HVD2.pol = 1; + oFRDReg.CH1_HVD2.en_vmax_hv_adc = 1; + oFRDReg.CH1_ST_HVD_PEAK_START.cnt_value = 0; + oFRDReg.CH1_ST_HVD_PEAK_STOP.cnt_value = 1750; + oFRDReg.CH1_ST_HVD_PEAK_RESET.cnt_value = 3500; + oFRDReg.CH1_ST_HVD_PEAK_URESET.cnt_value = 7000; + % approximately HV= 0,72 * lead -> 72V + oFRDReg.CH0_HVD1.lead = 100; + oFRDReg.CH0_HVD3.lag = 100; + oFRDReg.CH0_HVD2.disch_trim = 7; + oFRDReg.CH0_HVD1.en = 1; + oFRDReg.CH0_HVD1.peak_det_res_zcs = 10; + oFRDReg.CH0_HVD2.pol = 1; + oFRDReg.CH0_HVD2.en_vmax_hv_adc = 1; + oFRDReg.CH0_ST_HVD_PEAK_START.cnt_value = 0; + oFRDReg.CH0_ST_HVD_PEAK_STOP.cnt_value = 716; + oFRDReg.CH0_ST_HVD_PEAK_RESET.cnt_value = 850; + oFRDReg.CH0_ST_HVD_PEAK_URESET.cnt_value = 1611; + + % ------ Fref Master-Slave config ------ + + % Ratio: a=718, b=179, a/b=4.0112, k=2, LJRate=39.2, Tau_generic on B11 + oFRDReg.FREF_MOD1_INT.mod1_int_part = 13645; + oFRDReg.FREF_MOD1_FRACT1.mod1_fract_part_msb = 732; + oFRDReg.FREF_MOD1_FRACT2.mod1_fract_part_lsb = 125; + oFRDReg.CH1_CFG.pos_source = 1; + oFRDReg.FREF1.fref1 = 1; + oFRDReg.FREF1.mode_axis0 = 3; + oFRDReg.FREF1.mode_axis1 = 0; + oFRDReg.FREF1.period_axis = 1; + oFRDReg.FREF1.periodic_sd_output_en = 1; + oFRDReg.FREF1.synch_at_lj_fs_en = 1; + oFRDReg.FREF_SMART_START.threshold = 10; + oFRDReg.FREF_SMART_START.en = 0; + oFRDReg.CH1_FREQ_CTRL1.means_of_control = 1; + oFRDReg.CH1_FREQ_CTRL1.phase_comp_en = 0; + oFRDReg.FREF_WINDOW.window_width = 20; + oFRDReg.LJ_LOCK1.liss_f_s_duration = 15; + oFRDReg.FREF_DELAY_CH1.delay = 0; + oFRDReg.FREF1.delay_axis1_en = 0; + oFRDReg.FREF_LJ_PERIOD1.lj_period_lsb = 178; + oFRDReg.FREF1.en = 1; + + % ------ Slow Freq.-control config ------ + oFRDReg.CH1_FREQ_CTRL1.i_gain = 40; + oFRDReg.CH1_FREQ_CTRL2.p_gain = 30; + oFRDReg.CH1_FREQ_CTRL3.d_gain = 0; + oFRDReg.CH1_FREQ_CTRL1.source_pid = 1; + oFRDReg.CH1_FREQ_CTRL3.alpha_f = 5; + oFRDReg.CH1_FREQ_CTRL5.d_limit_f = 3000; + oFRDReg.CH1_FREQ_CTRL6.f_limit_top = 2; + oFRDReg.CH1_FREQ_CTRL7.f_limit_bottom = 0; + + % ------ Inhibiting config ------ + oFRDReg.HVD_INHIBIT1.inhibit_ctrl_en = 1; + oFRDReg.HVD_INHIBIT2.inhibit_time_switch_hv_off = 123; + oFRDReg.HVD_INHIBIT2.inhibit_time_switch_hv_on = 123; + oFRDReg.CH1_ST_INHIBIT1_HV_CH0_START.cnt_value = 6589; + oFRDReg.CH1_ST_INHIBIT1_HV_CH0_STOP.cnt_value = 175; + oFRDReg.CH1_ST_INHIBIT2_HV_CH0_START.cnt_value = 5957; + oFRDReg.CH1_ST_INHIBIT2_HV_CH0_STOP.cnt_value = 6543; + oFRDReg.CH0_ST_INHIBIT1_HV_CH1_START.cnt_value = 1546; + oFRDReg.CH0_ST_INHIBIT1_HV_CH1_STOP.cnt_value = 45; + oFRDReg.CH0_ST_INHIBIT2_HV_CH1_START.cnt_value = 1079; + oFRDReg.CH0_ST_INHIBIT2_HV_CH1_STOP.cnt_value = 1366; + % Go into operation mode + oFRDReg.OP_CFG.op_en = 1; + + % ------ configure anti phase drive ------ + + % slow + oFRDReg.CH1_SU_SWEEP_START_FREQ1.msb = 64234; + oFRDReg.CH1_SU_SWEEP_START_FREQ2.lsb = 233; + oFRDReg.CH1_SU_SWEEP_STOP_FREQ1.msb = 55058; + oFRDReg.CH1_SU_SWEEP_STOP_FREQ2.lsb = 53; + oFRDReg.CH1_SU_SWEEP_FREQ_STEP1.msb = 2; + oFRDReg.CH1_SU_SWEEP_FREQ_STEP2.lsb = 0; + oFRDReg.CH1_SU.stop_condition_peak_en = 1; + oFRDReg.CH1_SU.stop_condition_zc_en = 0; + oFRDReg.CH1_ST_MIRRCURR_PEAK_START.cnt_value = 3822; + oFRDReg.CH1_ST_MIRRCURR_PEAK_STOP.cnt_value = 3005; + oFRDReg.CH1_ST_MIRRCURR_PEAK_RESET.cnt_value = 3613; + oFRDReg.CH1_ST_MIRRCURR_PEAK_URESET.cnt_value = 3811; + oFRDReg.CH1_ST_ADC_MIRRCURR.cnt_value = 3006; + oFRDReg.CH1_ADC_SCHED1.angle_amplitude = 1; + oFRDReg.CH1_SU_STOP_COND_PEAK.peak_adc_th = 2120; + oFRDReg.CH1_SU_SWEEP.nr_of_zcs_per_f_step = 15; + oFRDReg.CH1_SU.nr_of_good_zc_for_stop = 4; + oFRDReg.CH1_HVD1.drive_method = 1; + + % fast + oFRDReg.CH0_SU_SWEEP_START_FREQ1.msb = 64656; + oFRDReg.CH0_SU_SWEEP_START_FREQ2.lsb = 260; + oFRDReg.CH0_SU_SWEEP_STOP_FREQ1.msb = 62426; + oFRDReg.CH0_SU_SWEEP_STOP_FREQ2.lsb = 498; + oFRDReg.CH0_SU_SWEEP_FREQ_STEP1.msb = 0; + oFRDReg.CH0_SU_SWEEP_FREQ_STEP2.lsb = 50; + oFRDReg.CH0_SU.stop_condition_peak_en = 1; + oFRDReg.CH0_SU.stop_condition_zc_en = 0; + oFRDReg.CH0_ST_MIRRCURR_PEAK_START.cnt_value = 1174; + oFRDReg.CH0_ST_MIRRCURR_PEAK_STOP.cnt_value = 352; + oFRDReg.CH0_ST_MIRRCURR_PEAK_RESET.cnt_value = 964; + oFRDReg.CH0_ST_MIRRCURR_PEAK_URESET.cnt_value = 1163; + oFRDReg.CH0_ST_ADC_MIRRCURR.cnt_value = 353; + oFRDReg.CH0_ADC_SCHED1.angle_amplitude = 1; + oFRDReg.CH0_SU_STOP_COND_PEAK.peak_adc_th = 2120; + oFRDReg.CH0_SU_SWEEP.nr_of_zcs_per_f_step = 15; + oFRDReg.CH0_SU.nr_of_good_zc_for_stop = 4; + oFRDReg.CH0_HVD1.drive_method = 1; + + % ------ Start slow-axis via peak-detector and wait ------ + oFRDReg.CH1_PHASE_CTRL1.bypass = 1; + oFRDReg.CH1_CFG.CH1_CFG = 1354; % 0x054A + oFRDReg.CH1_CFG.CH1_CFG = 1370; % 0x055A + + % wait until slow has jumped... + + % Wait for slow axis to finish sweep + sTimeOut = options.sTimeOut; + sStart = tic; + while(oFRDReg.CH1_SU.sweep_done ~= 1) + if toc(sStart) > sTimeOut + error('ifx:runtime_error', 'Timeout waiting for CH1_SU.sweep_done to equal 1'); + end + end + + % ------ Start fast-axis via peak-detector and wait ------ + oFRDReg.CH0_PHASE_CTRL1.bypass = 1; + oFRDReg.CH0_CFG.CH0_CFG = 330; % 0x014A + oFRDReg.CH0_CFG.CH0_CFG = 346; % 0x015A + + % wait until fast has jumped... + + % Wait for fast axis to finish sweep + sTimeOut = options.sTimeOut; + sStart = tic; + while(oFRDReg.CH0_SU.sweep_done ~= 1) + if toc(sStart) > sTimeOut + error('ifx:runtime_error', 'Timeout waiting for CH0_SU.sweep_done to equal 1'); + end + end + + % ------ Both axes finished sweep at this point. Change peak-detector-timings + + % Change peak-detector-timings of slow-axis. + % =slow_st_phasedet_start + oFRDReg.CH1_ST_MIRRCURR_PEAK_START.cnt_value = 6825; + % =slow_st_inhibit1_hv_fast_stop + oFRDReg.CH1_ST_MIRRCURR_PEAK_STOP.cnt_value = 175; + % =slow_st_inhibit1_hv_fast_stop + 1 + oFRDReg.CH1_ST_ADC_MIRRCURR.cnt_value = 176; + % =slow_st_phasedet_start - peak-detector-unreset-time [us] + oFRDReg.CH1_ST_MIRRCURR_PEAK_URESET.cnt_value = 6814; + % =slow_st_mirrcurr_peak_urset - peak-detector-reset-time [us] + oFRDReg.CH1_ST_MIRRCURR_PEAK_RESET.cnt_value = 6616; + oFRDReg.CH1_MSI1.peak_det_source = 1; + + % Change peak-detector-timings of fast-axis. + % =fast_st_drive_on + t_in_hv_off + oFRDReg.CH0_ST_MIRRCURR_PEAK_START.cnt_value = 974; + % =fast_st_drive_off_min + t_lead + oFRDReg.CH0_ST_MIRRCURR_PEAK_STOP.cnt_value = 152; + % =fast_st_mirrcurr_peak_stop_value + 1 + oFRDReg.CH0_ST_ADC_MIRRCURR.cnt_value = 153; + % =fast_st_mirrcurr_peak_start_value - peak-detector-unreset-time [us] + oFRDReg.CH0_ST_MIRRCURR_PEAK_URESET.cnt_value = 963; + % =fast_st_mirrcurr_peak_ureset_value - peak-detector-reset-time [us] + oFRDReg.CH0_ST_MIRRCURR_PEAK_RESET.cnt_value = 764; + oFRDReg.CH0_MSI1.peak_det_source = 1; + + % ------ Now switch to parametric-drive (if not done befeore), then set first fast-aixs to Amp-Ctrl-mode, then slow. + oFRDReg.CH0_ST_INHIBIT1_HV_CH1_START.en = 0; + oFRDReg.CH0_ST_INHIBIT2_HV_CH1_START.en = 0; + % switch to parametric drive + oFRDReg.CH0_HVD1.drive_method = 0; + oFRDReg.CH0_PHASE_CTRL1.bypass = 0; + oFRDReg.CH0_CFG.startup_en = 0; + % enable amplitude control of the fast axis + oFRDReg.CH0_CFG.ampl_ctrl_en = 1; + oFRDReg.CH0_CFG.sfr_drive_off_en = 0; + oFRDReg.CH1_ST_INHIBIT1_HV_CH0_START.en = 1; + oFRDReg.CH1_ST_INHIBIT2_HV_CH0_START.en = 1; + % switch to parametric drive + oFRDReg.CH1_HVD1.drive_method = 0; + oFRDReg.CH1_PHASE_CTRL1.bypass = 0; + oFRDReg.CH1_CFG.startup_en = 0; + % enable amplitude control of the slow axis + oFRDReg.CH1_CFG.ampl_ctrl_en = 1; + oFRDReg.CH1_CFG.sfr_drive_off_en = 0; + + % Wait until fast-axis is started (phase-error below 25)! + sTimeOut = options.sTimeOut; + sStart = tic; + while(abs(oFRDReg.CH0_STATUS_PHASE_COMP_ERR.value) > 25) + if toc(sStart) > sTimeOut + error('ifx:runtime_error', 'Timeout waiting for CH0_STATUS_PHASE_COMP_ERR compensated phase error to be below 25'); + end + end + oFRDReg.OPR_OK2.ch0_phase_ctrl_en = 0; + + % -------- Reduce TIA gains to avoid signal overdrive + oFRDReg.CH1_MSI1.tia_cap_sel = 5; + oFRDReg.CH1_MSI1.tia_res_sel = 5; + oFRDReg.CH0_MSI1.tia_cap_sel = 5; + oFRDReg.CH0_MSI1.tia_res_sel = 5; + % fast (master) axis is started, from now on it possible to lock to it + oFRDReg.LJ_LOCK1.en = 1; + + % ------ Enable frequ.-control ------ + % from here FREF has to be set correctly!! + oFRDReg.CH1_CFG.freq_ctrl_en = 1; + + % ------ Steer towards the final frequency via frequency control ------ + + % Wait to reach final frequency... + sTimeOut = options.sTimeOut; + sStart = tic; + while(oFRDReg.CH1_FREQ_CTRL1.f_limit_reached ~= 1) + if toc(sStart) > sTimeOut + error('ifx:runtime_error', 'Timeout waiting for CH1_FREQ_CTRL1.f_limit_reached to equal 1'); + end + end + + % ------ Restart fref with smart start ------ + oFRDReg.FREF1.en = 0; + oFRDReg.FREF_SMART_START.en = 1; + oFRDReg.FREF1.en = 1; + + % Wait for smart-start to start up... + sTimeOut = options.sTimeOut; + sStart = tic; + while(oFRDReg.FREF_SMART_START.started ~= 1) + if toc(sStart) > sTimeOut + error('ifx:runtime_error', 'Timeout waiting for FREF_SMART_START.started to equal 1'); + end + end + + % ------ Do phase&frequency control for lj synch, then wait for LJ-lock ------ + oFRDReg.CH1_PHASE_CTRL1.i_gain = 118; + oFRDReg.CH1_PHASE_CTRL2.p_gain = 395; + oFRDReg.CH1_FREQ_CTRL1.i_gain = 3; + oFRDReg.CH1_FREQ_CTRL2.p_gain = 2; + oFRDReg.CH1_FREQ_CTRL3.d_gain = 8; + oFRDReg.CH1_FREQ_CTRL3.alpha_pf = 13; + oFRDReg.CH1_FREQ_CTRL4.d_limit_pf = 3000; + oFRDReg.CH1_FREQ_CTRL1.source_pid = 0; + + % Wait for LJ-lock... + sTimeOut = options.sTimeOut; + sStart = tic; + while(oFRDReg.LJ_LOCK1.lock ~= 1) + if toc(sStart) > sTimeOut + error('ifx:runtime_error', 'Timeout waiting for LJ_LOCK1.lock to equal 1'); + end + end + + % ------ Set gains for low jitter ------ + oFRDReg.CH1_FREQ_CTRL3.d_gain = 1; + oFRDReg.CH1_FREQ_CTRL3.alpha_pf = 5; + oFRDReg.CH0_PHASE_CTRL1.i_gain = 6; + oFRDReg.CH0_PHASE_CTRL2.p_gain = 60; + oFRDReg.CH0_AMPL_CTRL1.i_gain = 1; + oFRDReg.CH0_AMPL_CTRL2.p_gain = 20; + +end \ No newline at end of file