Compare commits
12 Commits
53809c0631
...
f58ab13e9b
Author | SHA1 | Date |
---|---|---|
morgan | f58ab13e9b | |
morgan | 734e87eede | |
morgan | 6e562732e5 | |
morgan | 8be1abe80b | |
morgan | 2e2a346610 | |
morgan | fa8ad0b686 | |
morgan | 97db1ea08b | |
morgan | daabecc4b6 | |
morgan | 7c4a680787 | |
morgan | 0b724e84da | |
morgan | 0a66f4c343 | |
morgan | da5908e754 |
|
@ -2,3 +2,4 @@
|
|||
.direnv/**
|
||||
.vscode/**
|
||||
.venv/**
|
||||
__pycache__
|
38
README.md
38
README.md
|
@ -2,18 +2,24 @@
|
|||
|
||||
A time domain simulation for WRPLL
|
||||
|
||||
## Installing dependencies
|
||||
## Installation
|
||||
|
||||
### Poetry:
|
||||
```bash
|
||||
poetry install
|
||||
poetry run jupyter lab
|
||||
```
|
||||
|
||||
### Poetry shell:
|
||||
```bash
|
||||
poetry install
|
||||
poetry shell
|
||||
```
|
||||
|
||||
### Nix:
|
||||
```bash
|
||||
nix develop
|
||||
```
|
||||
### Others:
|
||||
```bash
|
||||
python -m venv .venv
|
||||
source .venv/bin/activate
|
||||
(venv) pip install -r requirements.txt
|
||||
```
|
||||
|
||||
## Quick start
|
||||
- Three notebook examples are included
|
||||
|
@ -48,21 +54,3 @@ source .venv/bin/activate
|
|||
</details><br>
|
||||
|
||||
## Limitation
|
||||
As the simulation is not cycle nor delay accurate, there will be more glitches than the hardware implementation
|
||||
|
||||
### Helper PLL glitches (remedies are added to follow hardware behavior)
|
||||
|
||||
- Cycle slipping issue will appear as $|\Delta{period}| \sim N$
|
||||
- During hardware testing, slipping issue is not common
|
||||
- It's recommended to turn cycle_slip_comp ON to reduce slipping and have a more accurate simulation
|
||||
|
||||
![cycle_slip](img/cycle_slipping.png)
|
||||
|
||||
|
||||
- Deglitcher fail issue will appear as $|\Delta{period}| \sim N/2$
|
||||
- There are no such issue for hardware
|
||||
- It's recommended to set blind_period higher than the hardware setting (around 300 is sufficient)
|
||||
|
||||
![deglitch_fail](img/deglitch_fail.png)
|
||||
|
||||
|
||||
|
|
96
flake.lock
96
flake.lock
|
@ -5,11 +5,11 @@
|
|||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1701680307,
|
||||
"narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=",
|
||||
"lastModified": 1694529238,
|
||||
"narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "4022d587cbbfd70fe950c1e2083a02621806a725",
|
||||
"rev": "ff7b65b44d01cf9ba6a71320833626af21126384",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
@ -18,13 +18,34 @@
|
|||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nix-github-actions": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"poetry2nix",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1698974481,
|
||||
"narHash": "sha256-yPncV9Ohdz1zPZxYHQf47S8S0VrnhV7nNhCawY46hDA=",
|
||||
"owner": "nix-community",
|
||||
"repo": "nix-github-actions",
|
||||
"rev": "4bb5e752616262457bc7ca5882192a564c0472d2",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"repo": "nix-github-actions",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1701539137,
|
||||
"narHash": "sha256-nVO/5QYpf1GwjvtpXhyxx5M3U/WN0MwBro4Lsk+9mL0=",
|
||||
"lastModified": 1702233072,
|
||||
"narHash": "sha256-H5G2wgbim2Ku6G6w+NSaQaauv6B6DlPhY9fMvArKqRo=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "933d7dc155096e7575d207be6fb7792bc9f34f6d",
|
||||
"rev": "781e2a9797ecf0f146e81425c822dca69fe4a348",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
@ -34,10 +55,34 @@
|
|||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"poetry2nix": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs"
|
||||
"nix-github-actions": "nix-github-actions",
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
],
|
||||
"systems": "systems_2",
|
||||
"treefmt-nix": "treefmt-nix"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1702365004,
|
||||
"narHash": "sha256-IRFvmyP1uk1hchRVxaXTqu6YoZCvMM/NVtUf2hD2Tag=",
|
||||
"owner": "nix-community",
|
||||
"repo": "poetry2nix",
|
||||
"rev": "c12ac880114d52a3cad5fa02b00f2e2090e89982",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"repo": "poetry2nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs",
|
||||
"poetry2nix": "poetry2nix"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
|
@ -54,6 +99,41 @@
|
|||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"systems_2": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"id": "systems",
|
||||
"type": "indirect"
|
||||
}
|
||||
},
|
||||
"treefmt-nix": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"poetry2nix",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1699786194,
|
||||
"narHash": "sha256-3h3EH1FXQkIeAuzaWB+nK0XK54uSD46pp+dMD3gAcB4=",
|
||||
"owner": "numtide",
|
||||
"repo": "treefmt-nix",
|
||||
"rev": "e82f32aa7f06bbbd56d7b12186d555223dc399d1",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "treefmt-nix",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
|
|
65
flake.nix
65
flake.nix
|
@ -1,38 +1,51 @@
|
|||
{
|
||||
inputs = {
|
||||
nixpkgs.url = "github:nixos/nixpkgs/nixos-23.11";
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
poetry2nix = {
|
||||
url = "github:nix-community/poetry2nix";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
};
|
||||
outputs = { nixpkgs, flake-utils, ... }: flake-utils.lib.eachDefaultSystem (system:
|
||||
outputs = { nixpkgs, poetry2nix, ... }:
|
||||
let
|
||||
pkgs = import nixpkgs {
|
||||
inherit system;
|
||||
|
||||
pkgs = import nixpkgs { system = "x86_64-linux"; };
|
||||
poetry2nixlib = (poetry2nix.lib.mkPoetry2Nix { inherit pkgs; });
|
||||
|
||||
poetryEnv = poetry2nixlib.mkPoetryEnv {
|
||||
python = pkgs.python3;
|
||||
projectDir = ./.;
|
||||
pyproject = ./pyproject.toml;
|
||||
poetrylock = ./poetry.lock;
|
||||
overrides = poetry2nixlib.overrides.withDefaults (self: super: {
|
||||
trace-updater = super.trace-updater.overridePythonAttrs (
|
||||
# ModuleNotFoundError: No module named 'setuptools'
|
||||
old: {
|
||||
buildInputs = (old.buildInputs or [ ]) ++ [ super.setuptools ];
|
||||
});
|
||||
tsdownsample = super.tsdownsample.override {
|
||||
# for bypassing building with maturin
|
||||
preferWheel = true;
|
||||
};
|
||||
plotly-resampler = super.plotly-resampler.overridePythonAttrs (
|
||||
# ModuleNotFoundError: No module named 'poetry'
|
||||
old: {
|
||||
buildInputs = (old.buildInputs or [ ]) ++ [ super.poetry-core ];
|
||||
});
|
||||
scipy = super.scipy.override {
|
||||
# fail to build
|
||||
preferWheel = true;
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
in rec {
|
||||
devShell = pkgs.mkShell {
|
||||
formatter.x86_64-linux = nixpkgs.legacyPackages.x86_64-linux.nixfmt;
|
||||
devShell.x86_64-linux = pkgs.mkShell {
|
||||
name = "WRPLL-sim";
|
||||
venvDir = "./.venv";
|
||||
buildInputs = with pkgs.python3Packages; [
|
||||
python
|
||||
numba
|
||||
numpy
|
||||
notebook # use jupyter notebook without missing libstdc++.so.6
|
||||
venvShellHook
|
||||
];
|
||||
|
||||
# Only run once when venv is created
|
||||
postVenvCreation = ''
|
||||
unset SOURCE_DATE_EPOCH
|
||||
pip install -r requirements.txt
|
||||
'';
|
||||
|
||||
postShellHook = ''
|
||||
# allow pip to install wheels
|
||||
unset SOURCE_DATE_EPOCH
|
||||
'';
|
||||
buildInputs = with pkgs; [ poetryEnv poetry nixpkgs-fmt ];
|
||||
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 22 KiB |
Binary file not shown.
Before Width: | Height: | Size: 23 KiB |
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,23 @@
|
|||
[tool.poetry]
|
||||
name = "wrpll-simulation"
|
||||
version = "0.1.0"
|
||||
description = ""
|
||||
authors = ["morgan <mc@m-labs.hk>"]
|
||||
readme = "README.md"
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.11"
|
||||
scipy = "^1.11.4"
|
||||
numpy = "^1.26.2"
|
||||
ipykernel = "^6.27.1"
|
||||
ipywidgets = "^8.1.1"
|
||||
jupyter = "^1.0.0"
|
||||
numba = "^0.58.1"
|
||||
pandas = "^2.1.4"
|
||||
plotly = "^5.18.0"
|
||||
plotly-resampler = "^0.9.1"
|
||||
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core"]
|
||||
build-backend = "poetry.core.masonry.api"
|
|
@ -1,11 +0,0 @@
|
|||
scipy
|
||||
numpy
|
||||
ipykernel
|
||||
ipywidgets
|
||||
jupyter
|
||||
jupyterlab
|
||||
notebook
|
||||
numba
|
||||
pandas
|
||||
plotly
|
||||
plotly-resampler
|
|
@ -39,74 +39,76 @@
|
|||
"from plotly.subplots import make_subplots\n",
|
||||
"import plotly.graph_objects as go\n",
|
||||
"import numpy as np\n",
|
||||
"from wave_gen import square_arr\n",
|
||||
"from wrpll import WRPLL_simulator\n",
|
||||
"\n",
|
||||
"from wrpll_simulation.wrpll import WRPLL_simulator"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# settings\n",
|
||||
"timestep = 4e-10 # even number is recommended to avoid strange glitches\n",
|
||||
"total_steps = 300_000_000\n",
|
||||
"timestep = 1e-10\n",
|
||||
"total_steps = 200_000_000\n",
|
||||
"sim_mode = \"both\"\n",
|
||||
"adpll_period = int(100e-6/timestep) # in simulation steps, 100μs is minimum, smaller = more frequency adjustment and filter calulation per unit time\n",
|
||||
"start_up_delay = int(100e-6/timestep) # in simulation steps, the frequency adjustment is DISABLE until steps > start_up_delay\n",
|
||||
"adpll_period = 200e-6 # in seconds, the period that pll will trigger, (minimum > total DCXO frequency change delay)\n",
|
||||
"start_up_delay = 100e-6 # in seconds, the frequency adjustment is DISABLE until time > start_up_delay\n",
|
||||
"\n",
|
||||
"gtx_freq = 125_001_519\n",
|
||||
"\n",
|
||||
"helper_init_freq = gtx_freq * (4096-1)/4096\n",
|
||||
"\n",
|
||||
"helper_filter = {\n",
|
||||
" \"KP\": 2,\n",
|
||||
" \"KI\": 4,\n",
|
||||
" \"KI\": 0.5,\n",
|
||||
" \"KD\": 0,\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"main_filter = { \n",
|
||||
"main_filter = {\n",
|
||||
" \"KP\": 12,\n",
|
||||
" \"KI\": 0,\n",
|
||||
" \"KD\": 0,\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"t = np.linspace(0, timestep*total_steps, total_steps)\n",
|
||||
"\n",
|
||||
"# simulation will start with\n",
|
||||
"# - random phase for main & helper\n",
|
||||
"# - gussian based base_adpll error\n",
|
||||
"# - gussian jitter for gtx, main and helper with the set standard deviation\n",
|
||||
"# simulation have RNG for\n",
|
||||
"# - gtx, main and helper jitter\n",
|
||||
"# - starting phase for main and helper\n",
|
||||
"# - base_adpll error\n",
|
||||
"\n",
|
||||
"wrpll_sim = WRPLL_simulator(\n",
|
||||
" time=t,\n",
|
||||
" timestep=timestep,\n",
|
||||
" total_steps=total_steps,\n",
|
||||
" sim_mode=sim_mode,\n",
|
||||
" helper_filter=helper_filter,\n",
|
||||
" main_filter=main_filter,\n",
|
||||
" gtx_freq=gtx_freq,\n",
|
||||
" adpll_write_period=adpll_period,\n",
|
||||
" start_up_delay=start_up_delay,\n",
|
||||
" helper_init_freq=helper_init_freq,\n",
|
||||
" # preset\n",
|
||||
" gtx_jitter_SD=19e-12, # 0 = no jitter\n",
|
||||
" dcxo_jitter_SD=9e-12,\n",
|
||||
" dcxo_freq=125_000_000,\n",
|
||||
" freq_acquisition_SD=100,\n",
|
||||
" N=4096, # hardware used 4096\n",
|
||||
" blind_period=300, # 300 is used to remove most glitches in simulation (for details see README 'Limitation')\n",
|
||||
" cycle_slip_comp=True,\n",
|
||||
")\n",
|
||||
"wrpll_sim.run()\n",
|
||||
"\n",
|
||||
"wrpll_sim.run()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# faster than pyplot with resampling feature\n",
|
||||
"# see https://github.com/predict-idlab/plotly-resampler\n",
|
||||
"\n",
|
||||
"fig = FigureWidgetResampler(make_subplots(rows=4, shared_xaxes=True))\n",
|
||||
"fig.add_trace(go.Scattergl(name='phase error'), hf_x=t, hf_y=wrpll_sim.phase_err, row=1, col=1)\n",
|
||||
"fig.add_trace(go.Scattergl(name='phase error'), hf_x=wrpll_sim.time, hf_y=wrpll_sim.phase_err, row=1, col=1)\n",
|
||||
"\n",
|
||||
"fig.add_trace(go.Scattergl(name='freq error (ppm)'), hf_x=t, hf_y=(\n",
|
||||
"fig.add_trace(go.Scattergl(name='freq error (ppm)'), hf_x=wrpll_sim.time, hf_y=(\n",
|
||||
" wrpll_sim.mainfreq-gtx_freq) * (1e6/gtx_freq), row=2, col=1)\n",
|
||||
"\n",
|
||||
"fig.add_trace(go.Scattergl(name='period error'), hf_x=t, hf_y=wrpll_sim.period_err, row=3, col=1)\n",
|
||||
"fig.add_trace(go.Scattergl(name='period error'), hf_x=wrpll_sim.time, hf_y=wrpll_sim.period_err, row=3, col=1)\n",
|
||||
"\n",
|
||||
"fig.add_trace(go.Scattergl(name='gtx'), hf_x=t, hf_y=wrpll_sim.gtx+1, row=4, col=1)\n",
|
||||
"fig.add_trace(go.Scattergl(name='main'), hf_x=t, hf_y=wrpll_sim.main, row=4, col=1)\n",
|
||||
"fig.add_trace(go.Scattergl(name='helper'), hf_x=t, hf_y=wrpll_sim.helper-1, row=4, col=1)\n",
|
||||
"fig.add_trace(go.Scattergl(name='gtx'), hf_x=wrpll_sim.time, hf_y=wrpll_sim.gtx+1, row=4, col=1)\n",
|
||||
"fig.add_trace(go.Scattergl(name='main'), hf_x=wrpll_sim.time, hf_y=wrpll_sim.main, row=4, col=1)\n",
|
||||
"fig.add_trace(go.Scattergl(name='helper'), hf_x=wrpll_sim.time, hf_y=wrpll_sim.helper-1, row=4, col=1)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"fig.update_layout(\n",
|
||||
|
|
|
@ -39,21 +39,27 @@
|
|||
"from plotly.subplots import make_subplots\n",
|
||||
"import plotly.graph_objects as go\n",
|
||||
"import numpy as np\n",
|
||||
"from wave_gen import square_arr\n",
|
||||
"from wrpll import WRPLL_simulator\n",
|
||||
"\n",
|
||||
"from wrpll_simulation.wrpll import WRPLL_simulator"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# settings\n",
|
||||
"timestep = 4e-10 # even number is recommended to avoid strange glitches\n",
|
||||
"total_steps = 200_000_000\n",
|
||||
"timestep = 1e-10\n",
|
||||
"total_steps = 100_000_000\n",
|
||||
"sim_mode = \"helper_pll\"\n",
|
||||
"adpll_period = int(100e-6/timestep) # in simulation steps, 100μs is minimum, smaller = more frequency adjustment and filter calulation per unit time\n",
|
||||
"start_up_delay = int(100e-6/timestep) # in simulation steps, the frequency adjustment is DISABLE until steps > start_up_delay\n",
|
||||
"adpll_period = 200e-6 # in seconds, the period that pll will trigger, (minimum > total DCXO frequency change delay)\n",
|
||||
"start_up_delay = 100e-6 # in seconds, the frequency adjustment is DISABLE until time > start_up_delay\n",
|
||||
"\n",
|
||||
"gtx_freq = 125_001_519\n",
|
||||
"\n",
|
||||
"helper_filter = {\n",
|
||||
" \"KP\": 2,\n",
|
||||
" \"KI\": 4,\n",
|
||||
" \"KI\": 0.5,\n",
|
||||
" \"KD\": 0,\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
|
@ -63,45 +69,39 @@
|
|||
" \"KD\": 0,\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"t = np.linspace(0, timestep*total_steps, total_steps)\n",
|
||||
"\n",
|
||||
"# simulation will start with\n",
|
||||
"# - random phase for main & helper\n",
|
||||
"# - gussian based base_adpll error\n",
|
||||
"# - gussian jitter for gtx, main and helper with the set standard deviation\n",
|
||||
"# simulation have RNG for\n",
|
||||
"# - gtx, main and helper jitter\n",
|
||||
"# - starting phase for main and helper\n",
|
||||
"# - base_adpll error\n",
|
||||
"\n",
|
||||
"wrpll_sim = WRPLL_simulator(\n",
|
||||
" time=t,\n",
|
||||
" timestep=timestep,\n",
|
||||
" total_steps=total_steps,\n",
|
||||
" sim_mode=sim_mode,\n",
|
||||
" helper_filter=helper_filter,\n",
|
||||
" main_filter=main_filter,\n",
|
||||
" gtx_freq=gtx_freq,\n",
|
||||
" adpll_write_period=adpll_period,\n",
|
||||
" start_up_delay=start_up_delay,\n",
|
||||
" # preset\n",
|
||||
" gtx_jitter_SD=19e-12, # 0 = no jitter\n",
|
||||
" dcxo_jitter_SD=9e-12,\n",
|
||||
" dcxo_freq=125_000_000,\n",
|
||||
" freq_acquisition_SD=500,\n",
|
||||
" N=4096, # hardware used 4096\n",
|
||||
" blind_period=300, # 300 is used to remove most glitches in simulation (for details see README 'Limitation')\n",
|
||||
" cycle_slip_comp=True,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"wrpll_sim.run()\n",
|
||||
"\n",
|
||||
"wrpll_sim.run()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# faster than pyplot with resampling feature\n",
|
||||
"# see https://github.com/predict-idlab/plotly-resampler\n",
|
||||
"\n",
|
||||
"fig = FigureWidgetResampler(make_subplots(rows=3, shared_xaxes=True))\n",
|
||||
"fig.add_trace(go.Scattergl(name='period error'), hf_x=t, hf_y=wrpll_sim.period_err, row=1, col=1)\n",
|
||||
"fig = FigureWidgetResampler(make_subplots(rows=2, shared_xaxes=True))\n",
|
||||
"fig.add_trace(go.Scattergl(name='period error'), hf_x=wrpll_sim.time, hf_y=wrpll_sim.period_err, row=1, col=1)\n",
|
||||
"\n",
|
||||
"fig.add_trace(go.Scattergl(name='gtx'), hf_x=t, hf_y=wrpll_sim.gtx+1, row=2, col=1)\n",
|
||||
"fig.add_trace(go.Scattergl(name='main'), hf_x=t, hf_y=wrpll_sim.main, row=2, col=1)\n",
|
||||
"fig.add_trace(go.Scattergl(name='helper'), hf_x=t, hf_y=wrpll_sim.helper-1, row=2, col=1)\n",
|
||||
"\n",
|
||||
"fig.add_trace(go.Scattergl(name='helper'), hf_x=t, hf_y=wrpll_sim.helper_adpll, row=3, col=1)\n",
|
||||
"fig.add_trace(go.Scattergl(name='gtx'), hf_x=wrpll_sim.time, hf_y=wrpll_sim.gtx+1, row=2, col=1)\n",
|
||||
"fig.add_trace(go.Scattergl(name='main'), hf_x=wrpll_sim.time, hf_y=wrpll_sim.main, row=2, col=1)\n",
|
||||
"fig.add_trace(go.Scattergl(name='helper'), hf_x=wrpll_sim.time, hf_y=wrpll_sim.helper-1, row=2, col=1)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"fig.update_layout(\n",
|
||||
|
@ -109,7 +109,7 @@
|
|||
"\n",
|
||||
" yaxis1=dict(title=\"beating period error\"),\n",
|
||||
" yaxis2=dict(title=\"Signal\"),\n",
|
||||
" height=500,\n",
|
||||
" height=1000,\n",
|
||||
" showlegend=True,\n",
|
||||
" title_text=\"PLL example\",\n",
|
||||
" legend=dict(\n",
|
||||
|
|
|
@ -42,74 +42,79 @@
|
|||
"from plotly.subplots import make_subplots\n",
|
||||
"import plotly.graph_objects as go\n",
|
||||
"import numpy as np\n",
|
||||
"from wave_gen import square_arr\n",
|
||||
"from wrpll import WRPLL_simulator\n",
|
||||
"\n",
|
||||
"from wrpll_simulation.wrpll import WRPLL_simulator"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# settings\n",
|
||||
"timestep = 4e-10 # even number is recommended to avoid strange glitches\n",
|
||||
"total_steps = 200_000_000\n",
|
||||
"timestep = 1e-10\n",
|
||||
"total_steps = 100_000_000\n",
|
||||
"sim_mode = \"main_pll\"\n",
|
||||
"adpll_period = int(100e-6/timestep) # in simulation steps, 100μs is minimum, smaller = more frequency adjustment and filter calulation per unit time\n",
|
||||
"start_up_delay = int(100e-6/timestep) # in simulation steps, the frequency adjustment is DISABLE until steps > start_up_delay\n",
|
||||
"adpll_period = 200e-6 # in seconds, the period that pll will trigger, (minimum > total DCXO frequency change delay)\n",
|
||||
"start_up_delay = 100e-6 # in seconds, the frequency adjustment is DISABLE until time > start_up_delay\n",
|
||||
"\n",
|
||||
"gtx_freq = 125_001_519\n",
|
||||
"\n",
|
||||
"helper_init_freq = gtx_freq * (4096-1)/4096\n",
|
||||
"\n",
|
||||
"helper_filter = { # unused\n",
|
||||
"helper_filter = { # unused\n",
|
||||
" \"KP\": 2,\n",
|
||||
" \"KI\": 4,\n",
|
||||
" \"KI\": 0.5,\n",
|
||||
" \"KD\": 0,\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"main_filter = { \n",
|
||||
"main_filter = {\n",
|
||||
" \"KP\": 12,\n",
|
||||
" \"KI\": 0,\n",
|
||||
" \"KD\": 0,\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"t = np.linspace(0, timestep*total_steps, total_steps)\n",
|
||||
"# simulation have RNG for\n",
|
||||
"# - gtx, main and helper jitter\n",
|
||||
"# - starting phase for main and helper\n",
|
||||
"# - base_adpll error\n",
|
||||
"\n",
|
||||
"# simulation will start with\n",
|
||||
"# - random phase for main & helper\n",
|
||||
"# - gussian based base_adpll error\n",
|
||||
"# - gussian jitter for gtx, main and helper with the set standard deviation\n",
|
||||
"\n",
|
||||
"wrpll_sim = WRPLL_simulator(\n",
|
||||
" time=t,\n",
|
||||
" timestep=timestep,\n",
|
||||
" total_steps=total_steps,\n",
|
||||
" sim_mode=sim_mode,\n",
|
||||
" helper_filter=helper_filter,\n",
|
||||
" main_filter=main_filter,\n",
|
||||
" gtx_freq=gtx_freq,\n",
|
||||
" adpll_write_period=adpll_period,\n",
|
||||
" start_up_delay=start_up_delay,\n",
|
||||
" helper_init_freq=helper_init_freq,\n",
|
||||
" # preset\n",
|
||||
" gtx_jitter_SD=19e-12, # 0 = no jitter\n",
|
||||
" dcxo_jitter_SD=9e-12,\n",
|
||||
" dcxo_freq=125_000_000,\n",
|
||||
" freq_acquisition_SD=100,\n",
|
||||
" N=4096, # hardware used 4096\n",
|
||||
" blind_period=300, # 300 is used to remove most glitches in simulation (for details see README 'Limitation')\n",
|
||||
" cycle_slip_comp=True,\n",
|
||||
" helper_init_freq=helper_init_freq\n",
|
||||
")\n",
|
||||
"wrpll_sim.run()\n",
|
||||
"\n",
|
||||
"wrpll_sim.run()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# faster than pyplot with resampling feature\n",
|
||||
"# see https://github.com/predict-idlab/plotly-resampler\n",
|
||||
"\n",
|
||||
"fig = FigureWidgetResampler(make_subplots(rows=4, shared_xaxes=True))\n",
|
||||
"fig.add_trace(go.Scattergl(name='phase error'), hf_x=t, hf_y=wrpll_sim.phase_err, row=1, col=1)\n",
|
||||
"fig.add_trace(go.Scattergl(name='phase error'), hf_x=wrpll_sim.time, hf_y=wrpll_sim.phase_err, row=1, col=1)\n",
|
||||
"\n",
|
||||
"fig.add_trace(go.Scattergl(name='freq error (ppm)'), hf_x=t, hf_y=(\n",
|
||||
"fig.add_trace(go.Scattergl(name='freq error (ppm)'), hf_x=wrpll_sim.time, hf_y=(\n",
|
||||
" wrpll_sim.mainfreq-gtx_freq) * (1e6/gtx_freq), row=2, col=1)\n",
|
||||
"\n",
|
||||
"fig.add_trace(go.Scattergl(name='period error'), hf_x=t, hf_y=wrpll_sim.period_err, row=3, col=1)\n",
|
||||
"fig.add_trace(go.Scattergl(name='period error'), hf_x=wrpll_sim.time, hf_y=wrpll_sim.period_err, row=3, col=1)\n",
|
||||
"\n",
|
||||
"fig.add_trace(go.Scattergl(name='gtx'), hf_x=t, hf_y=wrpll_sim.gtx+1, row=4, col=1)\n",
|
||||
"fig.add_trace(go.Scattergl(name='main'), hf_x=t, hf_y=wrpll_sim.main, row=4, col=1)\n",
|
||||
"fig.add_trace(go.Scattergl(name='helper'), hf_x=t, hf_y=wrpll_sim.helper-1, row=4, col=1)\n",
|
||||
"fig.add_trace(go.Scattergl(name='gtx'), hf_x=wrpll_sim.time, hf_y=wrpll_sim.gtx+1, row=4, col=1)\n",
|
||||
"fig.add_trace(go.Scattergl(name='main'), hf_x=wrpll_sim.time, hf_y=wrpll_sim.main, row=4, col=1)\n",
|
||||
"fig.add_trace(go.Scattergl(name='helper'), hf_x=wrpll_sim.time, hf_y=wrpll_sim.helper-1, row=4, col=1)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"fig.update_layout(\n",
|
||||
|
|
|
@ -1,69 +0,0 @@
|
|||
import numpy as np
|
||||
from numba import jit
|
||||
|
||||
|
||||
@jit(nopython=True)
|
||||
def square(time, freq, jitter_SD, jitter, cycle_num):
|
||||
"""
|
||||
|
||||
A scipy like square wave with jitter
|
||||
Parameters
|
||||
----------
|
||||
time : timestamp, in seconds
|
||||
|
||||
freq : frequency in Hz
|
||||
|
||||
jitter_SD : standard deviation of jitter, in seconds
|
||||
|
||||
jitter : gaussian noise with `jitter_SD` as its standard deviation
|
||||
|
||||
cycle_num : time // period = cycle_num
|
||||
----------
|
||||
|
||||
"""
|
||||
|
||||
period = 1/(freq)
|
||||
quarter = (period / 4)
|
||||
out = 0
|
||||
|
||||
# A T/4 shift is applied to the following to match scipy square fn
|
||||
# ┌──────┐
|
||||
# ───┘ └────
|
||||
# T/4 3T/4
|
||||
nth_cycle, t = np.divmod(time + period / 4, period)
|
||||
if t >= (quarter + jitter) and t <= (3*quarter + jitter):
|
||||
out = 1
|
||||
else:
|
||||
out = 0
|
||||
|
||||
# update jitter every cycle
|
||||
if nth_cycle != cycle_num:
|
||||
jitter = np.random.normal(0, jitter_SD)
|
||||
cycle_num = nth_cycle
|
||||
|
||||
return out, cycle_num, jitter
|
||||
|
||||
|
||||
@jit(nopython=True)
|
||||
def square_arr(time, freq, jitter_SD):
|
||||
"""
|
||||
|
||||
A scipy like square wave with jitter
|
||||
Parameters
|
||||
----------
|
||||
time : numpy array
|
||||
|
||||
freq : frequency in Hz
|
||||
|
||||
jitter_SD : standard deviation of jitter, in seconds
|
||||
----------
|
||||
|
||||
"""
|
||||
wave = np.zeros(len(time))
|
||||
jitter = np.random.normal(0, jitter_SD)
|
||||
cycle_num = 0
|
||||
|
||||
for i, t in enumerate(time):
|
||||
wave[i], cycle_num, jitter = square(t, freq, jitter_SD, jitter, cycle_num)
|
||||
|
||||
return wave
|
|
@ -1,12 +1,13 @@
|
|||
import numpy as np
|
||||
from numba import jit
|
||||
from wave_gen import square
|
||||
from numba import njit
|
||||
from wrpll_simulation.wave_gen import square
|
||||
|
||||
|
||||
@jit(nopython=True)
|
||||
@njit
|
||||
def simulation_jit(
|
||||
time,
|
||||
gtx,
|
||||
gtx_freq,
|
||||
gtx_jitter,
|
||||
helper_pll,
|
||||
main_pll,
|
||||
h_KP,
|
||||
|
@ -16,13 +17,15 @@ def simulation_jit(
|
|||
m_KI,
|
||||
m_KD,
|
||||
dcxo_freq,
|
||||
dcxo_jitter_SD,
|
||||
h_jitter,
|
||||
m_jitter,
|
||||
base_adpll,
|
||||
N,
|
||||
adpll_write_period,
|
||||
i2c_comm_delay,
|
||||
dcxo_settling_delay,
|
||||
blind_period,
|
||||
start_up_delay,
|
||||
cycle_slip_comp=True,
|
||||
helper_init_freq=0
|
||||
):
|
||||
|
||||
|
@ -30,6 +33,7 @@ def simulation_jit(
|
|||
|
||||
main = np.zeros(arr_len, dtype=np.int8)
|
||||
helper = np.zeros(arr_len, dtype=np.int8)
|
||||
gtx = np.zeros(arr_len, dtype=np.int8)
|
||||
|
||||
gtx_tag = 0
|
||||
gtx_ready = 0
|
||||
|
@ -40,7 +44,6 @@ def simulation_jit(
|
|||
main_ready = 0
|
||||
main_FF = 0
|
||||
main_beating = np.zeros(arr_len, dtype=np.int8)
|
||||
collector_r = 0
|
||||
|
||||
phase_err_arr = np.zeros(arr_len, dtype=np.int16)
|
||||
period_err_arr = np.zeros(arr_len, dtype=np.int16)
|
||||
|
@ -50,15 +53,20 @@ def simulation_jit(
|
|||
helperfreq = np.zeros(arr_len, dtype=np.int32)
|
||||
mainfreq = np.zeros(arr_len, dtype=np.int32)
|
||||
|
||||
# initial condition
|
||||
main_init_offset = (np.random.uniform(0, 360)) / (360 * dcxo_freq)
|
||||
helper_init_offset = (np.random.uniform(0, 360)) / (360 * dcxo_freq)
|
||||
phase_collector_r = 0
|
||||
colr_gtx_tag = colr_main_tag = 0
|
||||
phase_collector_state = 0
|
||||
|
||||
# intermediate values
|
||||
helper_jitter = np.random.normal(0, dcxo_jitter_SD)
|
||||
main_jitter = np.random.normal(0, dcxo_jitter_SD)
|
||||
helper_cycle_num = 0
|
||||
main_cycle_num = 0
|
||||
period_collector_r = 0
|
||||
colr_last_gtx_tag = 0
|
||||
beating_period = 0
|
||||
period_collector_state = 0
|
||||
|
||||
# initial condition
|
||||
timestep = time[1] - time[0]
|
||||
gtx_phase = 0
|
||||
h_phase = np.random.uniform(0, 360)
|
||||
m_phase = np.random.uniform(0, 360)
|
||||
|
||||
if main_pll and not helper_pll:
|
||||
helper_freq = helper_init_freq
|
||||
|
@ -66,12 +74,10 @@ def simulation_jit(
|
|||
helper_freq = dcxo_freq * (1 + base_adpll * 0.0001164 / 1_000_000) * ((N-1) / N)
|
||||
main_freq = dcxo_freq * (1 + base_adpll * 0.0001164 / 1_000_000)
|
||||
|
||||
FSM_state = 0
|
||||
coll_gtx_tag = coll_main_tag = 0
|
||||
last_gtx_tag = last_gtx_FF = last_gtx_beat = 0
|
||||
last_main_tag = last_main_FF = last_main_beat = 0
|
||||
|
||||
last_helper = last_main = 0
|
||||
last_helper = 0
|
||||
|
||||
counter = 0
|
||||
gtx_blind_counter = 0
|
||||
|
@ -84,40 +90,36 @@ def simulation_jit(
|
|||
wait_main = True
|
||||
|
||||
# firmware values
|
||||
FW_gtx_tag = FW_last_gtx_tag = 0
|
||||
FW_gtx_tag = 0
|
||||
FW_main_tag = 0
|
||||
|
||||
period_err = last_period_err = 0
|
||||
h_prop = h_integrator = h_derivative = 0
|
||||
h_adpll = base_adpll
|
||||
h_i2c_active = False
|
||||
h_i2c_active_index = 0
|
||||
period_colr_arm = h_i2c_active = False
|
||||
|
||||
phase_err = last_phase_err = 0
|
||||
m_prop = m_integrator = m_derivative = 0
|
||||
m_adpll = base_adpll
|
||||
m_i2c_active = False
|
||||
m_i2c_active_index = 0
|
||||
phase_colr_arm = m_i2c_active = False
|
||||
|
||||
adpll_active = False
|
||||
adpll_max = 8161512
|
||||
def clip(n, minn, maxn): return max(min(maxn, n), minn)
|
||||
|
||||
for i, t in enumerate(time):
|
||||
|
||||
helper[i], helper_cycle_num, helper_jitter = square(
|
||||
t + helper_init_offset, helper_freq, dcxo_jitter_SD, helper_jitter, helper_cycle_num)
|
||||
main[i], main_cycle_num, main_jitter = square(
|
||||
t + main_init_offset, main_freq, dcxo_jitter_SD, main_jitter, main_cycle_num)
|
||||
h_phase += 360 * helper_freq * (timestep + h_jitter[i])
|
||||
helper[i] = square(h_phase)
|
||||
|
||||
# continuous glitchless output, assume very small frequency change
|
||||
if h_i2c_active and helper[i] == 1:
|
||||
helper_freq = dcxo_freq * (1 + h_adpll * 0.0001164 / 1_000_000) * ((N-1) / N)
|
||||
h_i2c_active = False
|
||||
m_phase += 360 * main_freq * (timestep + m_jitter[i])
|
||||
main[i] = square(m_phase)
|
||||
|
||||
if m_i2c_active and main[i] == 1:
|
||||
main_freq = dcxo_freq * (1 + m_adpll * 0.0001164 / 1_000_000)
|
||||
m_i2c_active = False
|
||||
gtx_phase += 360 * gtx_freq * (timestep + gtx_jitter[i])
|
||||
gtx[i] = square(gtx_phase)
|
||||
|
||||
if last_helper == 0 and helper[i] == 1:
|
||||
if not last_helper and helper[i]:
|
||||
|
||||
gtx_FF, gtx_beating[i] = DDMTD(gtx[i], last_gtx_FF)
|
||||
main_FF, main_beating[i] = DDMTD(main[i], last_main_FF)
|
||||
|
@ -130,12 +132,15 @@ def simulation_jit(
|
|||
main_blind_counter, main_blinded, blind_period,
|
||||
last_main_beat, last_main_tag, counter)
|
||||
|
||||
collector_r, wait_gtx, wait_main, coll_gtx_tag, coll_main_tag, FSM_state = Collector_FSM(gtx_ready, main_ready, gtx_tag, main_tag,
|
||||
wait_gtx, wait_main, coll_gtx_tag, coll_main_tag, FSM_state)
|
||||
phase_collector_r, wait_gtx, wait_main, colr_gtx_tag, colr_main_tag, phase_collector_state = phase_collector_FSM(gtx_ready, main_ready, gtx_tag, main_tag,
|
||||
wait_gtx, wait_main, colr_gtx_tag, colr_main_tag, phase_collector_state)
|
||||
|
||||
if collector_r == 1:
|
||||
FW_gtx_tag = coll_gtx_tag
|
||||
FW_main_tag = coll_main_tag
|
||||
if phase_collector_r:
|
||||
FW_gtx_tag = colr_gtx_tag
|
||||
FW_main_tag = colr_main_tag
|
||||
|
||||
period_collector_r, colr_last_gtx_tag, beating_period, period_collector_state = period_collector_FSM(gtx_ready, gtx_tag,
|
||||
colr_last_gtx_tag, period_collector_state)
|
||||
|
||||
counter += 1
|
||||
|
||||
|
@ -157,31 +162,34 @@ def simulation_jit(
|
|||
|
||||
if i > start_up_delay:
|
||||
|
||||
# Firmware filters
|
||||
if adpll_active and collector_r == 1:
|
||||
if cycle_slip_comp:
|
||||
period = FW_gtx_tag - FW_last_gtx_tag
|
||||
if period > 3 * N/2:
|
||||
period = period - N
|
||||
period_err = N - period
|
||||
else:
|
||||
period_err = (N - (FW_gtx_tag - FW_last_gtx_tag))
|
||||
if i % adpll_write_period == 0:
|
||||
period_colr_arm = phase_colr_arm = True
|
||||
|
||||
# Firmware filters
|
||||
|
||||
if period_colr_arm and period_collector_r:
|
||||
|
||||
period_colr_arm = False
|
||||
period_err = N - beating_period
|
||||
|
||||
if helper_pll:
|
||||
h_prop = period_err * h_KP
|
||||
h_integrator += period_err * h_KI
|
||||
h_derivative = (period_err - last_period_err) * h_KD
|
||||
|
||||
h_adpll = clip(int(base_adpll + h_prop + h_integrator + h_derivative), -adpll_max, adpll_max)
|
||||
last_period_err = period_err
|
||||
h_i2c_active_index = i
|
||||
h_i2c_active = True
|
||||
|
||||
if phase_colr_arm and phase_collector_r:
|
||||
|
||||
phase_colr_arm = False
|
||||
tag_diff = ((FW_main_tag - FW_gtx_tag) % N)
|
||||
if tag_diff > N/2:
|
||||
phase_err = tag_diff - N
|
||||
else:
|
||||
phase_err = tag_diff
|
||||
|
||||
if helper_pll:
|
||||
h_prop = period_err * h_KP
|
||||
h_integrator += period_err * h_KI
|
||||
h_derivative = (period_err - last_period_err) * h_KD
|
||||
# h_derivative = 0
|
||||
|
||||
h_adpll = clip(int(base_adpll + h_prop + h_integrator + h_derivative), -adpll_max, adpll_max)
|
||||
last_period_err = period_err
|
||||
h_i2c_active = True
|
||||
|
||||
if main_pll:
|
||||
m_prop = phase_err * m_KP
|
||||
|
@ -190,16 +198,21 @@ def simulation_jit(
|
|||
|
||||
m_adpll = clip(int(base_adpll + m_prop + m_integrator + m_derivative), -adpll_max, adpll_max)
|
||||
last_phase_err = phase_err
|
||||
|
||||
m_i2c_active_index = i
|
||||
m_i2c_active = True
|
||||
|
||||
adpll_active = False
|
||||
# i2c communication delay
|
||||
|
||||
if i % adpll_write_period == 0:
|
||||
adpll_active = True
|
||||
FW_last_gtx_tag = FW_gtx_tag
|
||||
if h_i2c_active and i >= i2c_comm_delay + dcxo_settling_delay + h_i2c_active_index:
|
||||
helper_freq = dcxo_freq * (1 + h_adpll * 0.0001164 / 1_000_000) * ((N-1) / N)
|
||||
h_i2c_active = False
|
||||
|
||||
if m_i2c_active and i >= i2c_comm_delay + dcxo_settling_delay + m_i2c_active_index:
|
||||
main_freq = dcxo_freq * (1 + m_adpll * 0.0001164 / 1_000_000)
|
||||
m_i2c_active = False
|
||||
|
||||
last_helper = helper[i]
|
||||
last_main = main[i]
|
||||
|
||||
# Data
|
||||
period_err_arr[i] = period_err
|
||||
|
@ -209,18 +222,18 @@ def simulation_jit(
|
|||
helperfreq[i] = helper_freq
|
||||
mainfreq[i] = main_freq
|
||||
|
||||
return period_err_arr, phase_err_arr, helper_adpll_arr, main_adpll_arr, gtx_beating, main_beating, helper, main, helperfreq, mainfreq
|
||||
return period_err_arr, phase_err_arr, helper_adpll_arr, main_adpll_arr, gtx_beating, main_beating, gtx, helper, main, helperfreq, mainfreq
|
||||
|
||||
|
||||
@jit(nopython=True)
|
||||
@njit
|
||||
def DDMTD(sig_in, last_FF):
|
||||
return sig_in, last_FF
|
||||
|
||||
|
||||
@jit(nopython=True)
|
||||
@njit
|
||||
def Deglitcher(beating, t_out, t_ready, blind_counter, blinded, blind_period, last_beat, last_tag, counter):
|
||||
|
||||
if blind_counter == 0 and beating == 1 and last_beat == 0: # rising
|
||||
if blind_counter == 0 and beating and not last_beat: # rising
|
||||
t_out = counter
|
||||
t_ready = 1
|
||||
blinded = True
|
||||
|
@ -228,7 +241,7 @@ def Deglitcher(beating, t_out, t_ready, blind_counter, blinded, blind_period, la
|
|||
t_out = last_tag
|
||||
t_ready = 0
|
||||
|
||||
if beating == 1:
|
||||
if beating:
|
||||
blind_counter = blind_period - 1
|
||||
|
||||
if blind_counter != 0:
|
||||
|
@ -237,38 +250,56 @@ def Deglitcher(beating, t_out, t_ready, blind_counter, blinded, blind_period, la
|
|||
return t_out, t_ready, blind_counter, blinded
|
||||
|
||||
|
||||
@jit(nopython=True)
|
||||
def Collector_FSM(g_tag_r, m_tag_r, gtx_tag, main_tag, wait_gtx, wait_main, coll_gtx_tag, coll_main_tag, FSM_state):
|
||||
@njit
|
||||
def phase_collector_FSM(g_tag_r, m_tag_r, gtx_tag, main_tag, wait_gtx, wait_main, colr_gtx_tag, colr_main_tag, FSM_state):
|
||||
|
||||
collector_r = 0
|
||||
|
||||
match FSM_state:
|
||||
case 0: # IDEL
|
||||
if g_tag_r == 1 and m_tag_r == 1:
|
||||
coll_gtx_tag = gtx_tag
|
||||
coll_main_tag = main_tag
|
||||
if g_tag_r and m_tag_r:
|
||||
colr_gtx_tag = gtx_tag
|
||||
colr_main_tag = main_tag
|
||||
FSM_state = 3 # OUTPUT
|
||||
|
||||
elif g_tag_r == 1:
|
||||
coll_gtx_tag = gtx_tag
|
||||
elif g_tag_r:
|
||||
colr_gtx_tag = gtx_tag
|
||||
wait_main = True
|
||||
FSM_state = 2 # WAITMAIN
|
||||
|
||||
elif m_tag_r == 1:
|
||||
coll_main_tag = main_tag
|
||||
elif m_tag_r:
|
||||
colr_main_tag = main_tag
|
||||
wait_gtx = True
|
||||
FSM_state = 1 # WAITGTX
|
||||
case 1: # WAITGTX
|
||||
if g_tag_r == 1:
|
||||
coll_gtx_tag = gtx_tag
|
||||
if g_tag_r:
|
||||
colr_gtx_tag = gtx_tag
|
||||
FSM_state = 3 # OUTPUT
|
||||
case 2: # WAITMAIN
|
||||
if m_tag_r == 1:
|
||||
coll_main_tag = main_tag
|
||||
if m_tag_r:
|
||||
colr_main_tag = main_tag
|
||||
FSM_state = 3 # OUTPUT
|
||||
case 3: # OUTPUT
|
||||
wait_gtx = wait_main = False
|
||||
collector_r = 1
|
||||
FSM_state = 0
|
||||
|
||||
return collector_r, wait_gtx, wait_main, coll_gtx_tag, coll_main_tag, FSM_state
|
||||
return collector_r, wait_gtx, wait_main, colr_gtx_tag, colr_main_tag, FSM_state
|
||||
|
||||
|
||||
@njit
|
||||
def period_collector_FSM(g_tag_r, gtx_tag, colr_last_gtx_tag, FSM_state):
|
||||
collector_r = 0
|
||||
beating_period = 0
|
||||
match FSM_state:
|
||||
case 0: # IDEL
|
||||
if g_tag_r:
|
||||
colr_last_gtx_tag = gtx_tag
|
||||
FSM_state = 1
|
||||
case 1: # OUTPUT
|
||||
if g_tag_r:
|
||||
beating_period = gtx_tag - colr_last_gtx_tag
|
||||
collector_r = 1
|
||||
FSM_state = 0 # IDEL
|
||||
|
||||
return collector_r, colr_last_gtx_tag, beating_period, FSM_state
|
|
@ -0,0 +1,29 @@
|
|||
import numpy as np
|
||||
import numba as nb
|
||||
from numba import njit
|
||||
|
||||
|
||||
|
||||
def gussian_jitter(RMS_jitter, size, seed=None):
|
||||
return np.random.default_rng(seed).normal(0, RMS_jitter/2, size)
|
||||
|
||||
|
||||
@njit(fastmath=True)
|
||||
def square_with_jitter(time, freq, jitter):
|
||||
n = len(time)
|
||||
wave = np.empty(n)
|
||||
timestep = time[1] - time[0]
|
||||
|
||||
phase = 0.
|
||||
for i in range(n):
|
||||
phase += 360 * freq * (timestep + jitter[i])
|
||||
wave[i] = square(phase)
|
||||
return wave
|
||||
|
||||
|
||||
@njit
|
||||
def square(x):
|
||||
if np.mod(x, 360) < 180:
|
||||
return 0
|
||||
else:
|
||||
return 1
|
|
@ -1,31 +1,33 @@
|
|||
import numpy as np
|
||||
from numba import jit
|
||||
from wave_gen import square_arr
|
||||
from sim import simulation_jit
|
||||
from wrpll_simulation.sim import simulation_jit
|
||||
from wrpll_simulation.wave_gen import gussian_jitter
|
||||
|
||||
|
||||
class WRPLL_simulator():
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
time,
|
||||
timestep,
|
||||
total_steps,
|
||||
sim_mode,
|
||||
helper_filter,
|
||||
main_filter,
|
||||
gtx_freq,
|
||||
gtx_jitter_SD,
|
||||
dcxo_freq,
|
||||
dcxo_jitter_SD,
|
||||
freq_acquisition_SD,
|
||||
N,
|
||||
adpll_write_period,
|
||||
blind_period,
|
||||
start_up_delay,
|
||||
cycle_slip_comp,
|
||||
helper_init_freq=None
|
||||
i2c_comm_delay=85.6e-6,
|
||||
dcxo_settling_delay=100e-6,
|
||||
gtx_jitter=200e-15,
|
||||
dcxo_freq=125_000_000,
|
||||
dcxo_jitter=95e-15,
|
||||
freq_acquisition_error=100,
|
||||
N=4069,
|
||||
blind_period=128,
|
||||
helper_init_freq=None,
|
||||
seed=None
|
||||
):
|
||||
|
||||
self.time = time
|
||||
self.time = np.linspace(0, timestep*total_steps, total_steps)
|
||||
self.sim_mode = sim_mode
|
||||
self.h_KP = helper_filter["KP"]
|
||||
self.h_KI = helper_filter["KI"]
|
||||
|
@ -35,22 +37,26 @@ class WRPLL_simulator():
|
|||
self.m_KD = main_filter["KD"]
|
||||
|
||||
# init condition
|
||||
self.gtx_freq = gtx_freq
|
||||
self.gtx_jitter = gussian_jitter(gtx_jitter, len(self.time), seed)
|
||||
|
||||
self.dcxo_freq = dcxo_freq
|
||||
self.dcxo_jitter_SD = dcxo_jitter_SD
|
||||
self.h_jitter = gussian_jitter(dcxo_jitter, len(self.time), seed)
|
||||
self.m_jitter = gussian_jitter(dcxo_jitter, len(self.time), seed)
|
||||
self.N = N
|
||||
self.helper_init_freq = helper_init_freq
|
||||
|
||||
self.gtx = square_arr(time, gtx_freq, gtx_jitter_SD)
|
||||
|
||||
# freq_acquisition() error
|
||||
freq_diff = gtx_freq - dcxo_freq + np.random.normal(0, freq_acquisition_SD)
|
||||
freq_diff = gtx_freq - dcxo_freq + \
|
||||
np.random.default_rng(seed).uniform(-freq_acquisition_error, freq_acquisition_error)
|
||||
self.base_adpll = int(freq_diff * (1 / dcxo_freq) * (1e6 / 0.0001164))
|
||||
|
||||
# sim config
|
||||
self.adpll_write_period = adpll_write_period
|
||||
self.i2c_comm_delay = int(i2c_comm_delay/timestep)
|
||||
self.dcxo_settling_delay = int(dcxo_settling_delay/timestep)
|
||||
self.blind_period = blind_period
|
||||
self.start_up_delay = start_up_delay
|
||||
self.cycle_slip_comp = cycle_slip_comp
|
||||
self.adpll_write_period = int(adpll_write_period/timestep)
|
||||
self.start_up_delay = int(start_up_delay/timestep)
|
||||
|
||||
if type(self.sim_mode) is not str:
|
||||
raise ValueError(f"pll_type {type(self.sim_mode)} is not a string")
|
||||
|
@ -69,9 +75,10 @@ class WRPLL_simulator():
|
|||
|
||||
def run(self):
|
||||
print("running simulation...")
|
||||
self.period_err, self.phase_err, self.helper_adpll, self.main_adpll, self.gtx_beating, self.main_beating, self.helper, self.main, self.helperfreq, self.mainfreq = simulation_jit(
|
||||
self.period_err, self.phase_err, self.helper_adpll, self.main_adpll, self.gtx_beating, self.main_beating, self.gtx, self.helper, self.main, self.helperfreq, self.mainfreq = simulation_jit(
|
||||
self.time,
|
||||
self.gtx,
|
||||
self.gtx_freq,
|
||||
self.gtx_jitter,
|
||||
self.helper_pll,
|
||||
self.main_pll,
|
||||
self.h_KP,
|
||||
|
@ -81,13 +88,15 @@ class WRPLL_simulator():
|
|||
self.m_KI,
|
||||
self.m_KD,
|
||||
self.dcxo_freq,
|
||||
self.dcxo_jitter_SD,
|
||||
self.h_jitter,
|
||||
self.m_jitter,
|
||||
self.base_adpll,
|
||||
self.N,
|
||||
self.adpll_write_period,
|
||||
self.i2c_comm_delay,
|
||||
self.dcxo_settling_delay,
|
||||
self.blind_period,
|
||||
self.start_up_delay,
|
||||
self.cycle_slip_comp,
|
||||
self.helper_init_freq
|
||||
)
|
||||
print("Done!")
|
Loading…
Reference in New Issue