Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7b34f5a589
|
||
|
|
6436f52889
|
||
|
|
49e6820eae
|
||
|
|
f8350ba235
|
||
|
|
09f0dc42e4
|
||
|
|
8e2303fcdf
|
||
|
|
4917abe9e6
|
||
|
|
b52629d1f6
|
||
|
|
b9159581f3
|
||
|
|
9e9163f01b
|
||
|
|
54a69b51ae
|
||
|
|
975ba4f312
|
||
|
|
6cd5ed61c4 | ||
|
|
1adca36802
|
||
|
|
3b1ef34bce
|
||
|
|
6b33be6956
|
||
|
|
f94a5cbe17
|
||
|
|
af31b6165c
|
||
|
|
946177a0b9
|
||
|
|
3361c9ee3d
|
||
|
|
ab802968ca
|
||
|
|
096026a964
|
||
|
|
6478741950
|
||
|
|
eb995c7b69
|
||
|
|
7207cfdbd0
|
||
|
|
36b2fc4188
|
||
|
|
62c4619c5e
|
||
|
|
f7beeb5e15
|
||
|
|
99ceb89e48
|
||
|
|
7217702068 | ||
|
|
11bf721f21 | ||
|
|
446c156338 | ||
|
|
909ea17f2f | ||
|
|
7a0df30ca1 | ||
|
|
a5f95bb6d7 | ||
|
|
f047389da9 | ||
|
|
b78f79e12e | ||
|
|
d607e5df4d | ||
|
|
4199eb0e42 | ||
|
|
0f4556a8a0 | ||
|
|
dc62de5b6b | ||
|
|
48296ab45c | ||
|
|
de2deb15ad | ||
|
|
c3d3479327 | ||
|
|
1bf4eeb242
|
||
|
|
6139748849 | ||
|
|
5637b4b14a
|
||
|
|
79d0f08273
|
||
|
|
c7aabc7560 | ||
|
|
c16086c8dc
|
||
|
|
525d5d466c | ||
|
|
0149f59c82 | ||
|
|
35ee5b72df | ||
|
|
47b6eb808e | ||
|
|
bdaa3e1ce4 | ||
|
|
ac64daca7f | ||
|
|
5a1314a97a | ||
|
|
7d45d9d8e4 | ||
|
|
991f49776d | ||
|
|
19acbe1a74 | ||
|
|
76ae3150a0 | ||
|
|
9c298cba2c | ||
|
|
f0784cfef6 | ||
|
|
60e881a27f | ||
|
|
06afb2e4d1 | ||
|
|
0c421a159c | ||
|
|
228da72eb1 | ||
|
|
0f8fcb4fe6 | ||
|
|
dbb7c8f33b | ||
|
|
c7d7b6ae8c | ||
|
|
343f7c7e3a | ||
|
|
ae4d5b8f33 | ||
|
|
fbef6eceee | ||
|
|
9a919151cc | ||
|
|
f52207eed3 | ||
|
|
7a6970f89e | ||
|
|
5228190c95 | ||
|
|
2035387730 | ||
|
|
04f2ca9aba | ||
|
|
b4ac04880a | ||
|
|
766013fd3c | ||
|
|
a9039fccc5 | ||
|
|
91e02ba46a | ||
|
|
1c40e87061 | ||
|
|
d3e4ab7876 | ||
|
|
a8a085b5aa | ||
|
|
9bcc548c9e | ||
|
|
c6cfe50a94 | ||
|
|
d0c08a35a2 | ||
|
|
f716270ab6 | ||
|
|
587fe17f36 | ||
|
|
f08600e8bc | ||
|
|
938af714d1 | ||
|
|
15639751af | ||
|
|
b2b6cf5c14 | ||
|
|
779a20386f | ||
|
|
959f3397f6 | ||
|
|
e5a00441e4 | ||
|
|
6f558c0dab | ||
|
|
bee1139aff | ||
|
|
c7a1f438d2 | ||
|
|
aa399dcc93 | ||
|
|
4d5693aa1d | ||
|
|
1e5a337f94 | ||
|
|
841c7082c9 | ||
|
|
9dec00c656 | ||
|
|
ecc766fb53 | ||
|
|
7cb0973700 | ||
|
|
ec57b65794 | ||
|
|
e10ab79b06 | ||
|
|
f55f01c352 | ||
|
|
f436b9de25 | ||
|
|
e03787ac7b | ||
|
|
85cf4d28da | ||
|
|
f0d9cff028 | ||
|
|
1092fb86cc | ||
|
|
9d6794a04e | ||
|
|
9ce3d6939c | ||
|
|
d9c1dfa147 | ||
|
|
60e2650707 | ||
|
|
415f7786ba | ||
|
|
80373fdd08 | ||
|
|
d7242688df | ||
|
|
285c888b8d | ||
|
|
33c1ffb7d2 | ||
|
|
95a5a19943 | ||
|
|
2bffbf4a60 | ||
|
|
1bb25ec1c1 | ||
|
|
fb6958da1e | ||
|
|
559e8bcc4d | ||
|
|
a4058a2882 | ||
|
|
813f64d922 | ||
|
|
3aea1a47b9 | ||
|
|
3d08888842 | ||
|
|
9a811a2d3d | ||
|
|
ec4a16209b | ||
|
|
54c40613e7 | ||
|
|
5e713e5151 | ||
|
|
12854429d1 | ||
|
|
8a7ce06fe6 | ||
|
|
295a0844be | ||
|
|
5da923e68b | ||
|
|
649c2c2f58 | ||
|
|
3446d70a59 | ||
|
|
a02898f301 | ||
|
|
c49ee22234 | ||
|
|
49c891377b | ||
|
|
d4dbc07c20 | ||
|
|
00b5dfea65 | ||
|
|
20b294a769 | ||
|
|
504472e7cc | ||
|
|
0d16b6fcaf | ||
|
|
9b0110b8d1 | ||
|
|
eaf4ed24fd | ||
|
|
b32065ca9c | ||
|
|
5feafd8877 | ||
|
|
a0a1aab660 | ||
|
|
c67bf20145 | ||
|
|
01d9d71ff5 | ||
|
|
8c4bdd9e41 | ||
|
|
f13f4faa02 |
No files matched your search
@@ -0,0 +1,3 @@
|
||||
# Shell scripts and the Justfile must stay LF so they run in Git Bash on Windows.
|
||||
*.sh text eol=lf
|
||||
Justfile text eol=lf
|
||||
@@ -0,0 +1,70 @@
|
||||
name: Build site
|
||||
|
||||
on:
|
||||
- push
|
||||
- workflow_dispatch
|
||||
|
||||
jobs:
|
||||
build:
|
||||
# NOTE: must match your act_runner labels (e.g. ubuntu-latest, ubuntu-24.04).
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Bootstrap pinned toolchain
|
||||
run: ./scripts/bootstrap.sh
|
||||
|
||||
- name: Build site
|
||||
run: ./scripts/calepin.sh compile site public
|
||||
|
||||
# The official upload-artifact@v4 refuses to run on Gitea (it detects a
|
||||
# non-GitHub server and aborts with GHESNotSupportedError). v3 uses the
|
||||
# artifact API that Gitea implements. On Gitea >= 1.22 with a current
|
||||
# act_runner, christopherhx/gitea-upload-artifact@v4 is a drop-in
|
||||
# replacement if you want the v4 API.
|
||||
- name: Upload site artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: public
|
||||
path: public/
|
||||
|
||||
deploy:
|
||||
needs: build
|
||||
# Publish only from main; move the gate to publish from another branch.
|
||||
if: github.ref == 'refs/heads/main'
|
||||
# Write access for the automatic Actions token to push the pages branch.
|
||||
permissions:
|
||||
contents: write
|
||||
# NOTE: must match your act_runner labels (e.g. ubuntu-latest, ubuntu-24.04).
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download site artifact
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: public
|
||||
path: public
|
||||
|
||||
- name: Check artifact layout
|
||||
run: test -f public/index.html
|
||||
|
||||
# Publishing interfaces with prod; it never runs under local act tests
|
||||
# (act sets env.ACT, the Gitea runner does not). The force-push to the
|
||||
# `pages` branch is what triggers the git-pages webhook.
|
||||
- name: Publish to pages branch
|
||||
env:
|
||||
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
||||
run: |
|
||||
if [ -z "$GITEA_TOKEN" ]; then
|
||||
echo "::error::Missing GITEA_TOKEN (the automatic Actions token)."
|
||||
exit 1
|
||||
fi
|
||||
cd public
|
||||
git init -q -b pages .
|
||||
git config user.name "Yggdrasil CI"
|
||||
git config user.email "ci@noreply.kb28.ch"
|
||||
git add -A
|
||||
git commit -q -m "Publish site from ${GITHUB_SHA}"
|
||||
repo_host="${GITHUB_SERVER_URL#*://}"
|
||||
repo_host="${repo_host%/}"
|
||||
git push -q --force "https://${GITHUB_REPOSITORY_OWNER}:${GITEA_TOKEN}@${repo_host}/${GITHUB_REPOSITORY}.git" pages
|
||||
@@ -0,0 +1,12 @@
|
||||
# Generated site and pinned toolchain
|
||||
public/
|
||||
/tools/
|
||||
|
||||
# Calepin build artifacts
|
||||
.calepin/
|
||||
.calepin-entry.*
|
||||
|
||||
# IDE
|
||||
.idea/
|
||||
.zed/
|
||||
.vscode/
|
||||
@@ -0,0 +1,17 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Docsify" type="ShConfigurationType">
|
||||
<option name="SCRIPT_TEXT" value="start http://localhost:3000 ; docsify serve " />
|
||||
<option name="INDEPENDENT_SCRIPT_PATH" value="false" />
|
||||
<option name="SCRIPT_PATH" value="$PROJECT_DIR$/.idea/runConfigurations/run.sh" />
|
||||
<option name="SCRIPT_OPTIONS" value="" />
|
||||
<option name="INDEPENDENT_SCRIPT_WORKING_DIRECTORY" value="true" />
|
||||
<option name="SCRIPT_WORKING_DIRECTORY" value="$PROJECT_DIR$" />
|
||||
<option name="INDEPENDENT_INTERPRETER_PATH" value="true" />
|
||||
<option name="INTERPRETER_PATH" value="powershell.exe" />
|
||||
<option name="INTERPRETER_OPTIONS" value="" />
|
||||
<option name="EXECUTE_IN_TERMINAL" value="true" />
|
||||
<option name="EXECUTE_SCRIPT_FILE" value="false" />
|
||||
<envs />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
@@ -1,29 +0,0 @@
|
||||
<!doctype html><html lang=en><head><meta charset=utf-8><meta content="width=device-width,initial-scale=1" name=viewport><title>Not found</title><meta content="The requested page does not exist." name=description><meta content="The requested page does not exist." property=og:description><meta content="The requested page does not exist." name=twitter:description><meta content=Yggdrasil property=og:site_name><meta content="Not found | Yggdrasil" property=og:title><meta content="Not found | Yggdrasil" name=twitter:title><meta content=article property=og:type><meta content=summary name=twitter:card><link href=https://yggdrasil.kb28.ch/404.html rel=canonical><meta content=https://yggdrasil.kb28.ch/404.html property=og:url><link href=/assets/yggdrasil.ico rel=icon><meta content="light dark" name=color-scheme><script data-calepin-theme-param=theme data-calepin-theme-storage-key=calepin-website-theme>(()=>{let currentScript=document.currentScript,storageKey=currentScript?.dataset?.calepinThemeStorageKey||`calepin-website-theme`,param=currentScript?.dataset?.calepinThemeParam||`theme`,normalize=value=>value===`light`||value===`dark`?value:``,root=document.documentElement,media=window.matchMedia?window.matchMedia(`(prefers-color-scheme: dark)`):null,systemTheme=()=>media&&media.matches?`dark`:`light`,mode=``;if(param)try{mode=normalize(new URL(window.location.href).searchParams.get(param)||``)}catch{mode=``}if(!mode)try{mode=normalize(localStorage.getItem(storageKey)||``)}catch{mode=``}let theme=mode||systemTheme();root.dataset.calepinThemeMode=mode,root.dataset.theme=theme,root.style.colorScheme=theme})();</script><style>:host,:root{--pico-font-family-emoji:"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--pico-font-family-sans-serif:system-ui,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,Helvetica,Arial,"Helvetica Neue",sans-serif,var(--pico-font-family-emoji);--pico-font-family-monospace:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,"Liberation Mono",monospace,var(--pico-font-family-emoji);--pico-font-family:var(--pico-font-family-sans-serif);--pico-line-height:1.5;--pico-font-weight:400;--pico-font-size:100%;--pico-text-underline-offset:.1rem;--pico-border-radius:.25rem;--pico-border-width:.0625rem;--pico-outline-width:.125rem;--pico-transition:.2s ease-in-out;--pico-spacing:1rem;--pico-typography-spacing-vertical:1rem;--pico-block-spacing-vertical:var(--pico-spacing);--pico-block-spacing-horizontal:var(--pico-spacing);--pico-grid-column-gap:var(--pico-spacing);--pico-grid-row-gap:var(--pico-spacing);--pico-form-element-spacing-vertical:.75rem;--pico-form-element-spacing-horizontal:1rem;--pico-group-box-shadow:0 0 0 #0000;--pico-group-box-shadow-focus-with-button:0 0 0 var(--pico-outline-width) var(--pico-primary-focus);--pico-group-box-shadow-focus-with-input:0 0 0 .0625rem var(--pico-form-element-border-color);--pico-modal-overlay-backdrop-filter:blur(.375rem);--pico-nav-element-spacing-vertical:1rem;--pico-nav-element-spacing-horizontal:.5rem;--pico-nav-link-spacing-vertical:.5rem;--pico-nav-link-spacing-horizontal:.5rem;--pico-nav-breadcrumb-divider:">";--pico-icon-checkbox:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-minus:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E");--pico-icon-chevron:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-date:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E");--pico-icon-time:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline points='12 6 12 12 16 14'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-search:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke=Line truncated
|
||||
`:Array.from(node.childNodes).map(codeText).join(``):``:``}document.querySelectorAll(selector).forEach(block=>{if(block.dataset.calepinCopyBound===`true`||block.querySelector(`:scope > .${buttonClass}`))return;block.dataset.calepinCopyBound=`true`;let button=document.createElement(`button`);button.type=`button`,button.className=buttonClass,button.setAttribute(`aria-label`,`Copy code`),button.setAttribute(`title`,`Copy code`),button.innerHTML=icon;let restoreTimeout=null;button.addEventListener(`click`,async()=>{restoreTimeout!==null&&(window.clearTimeout(restoreTimeout),restoreTimeout=null);let text=codeText(block.querySelector(`pre code, code, pre`));try{await copyText(text),button.classList.add(copiedClass),button.innerHTML=copiedIcon,restoreTimeout=window.setTimeout(()=>{button.classList.remove(copiedClass),button.innerHTML=icon,restoreTimeout=null},900)}catch{button.classList.remove(copiedClass),button.innerHTML=icon}}),block.prepend(button)})})();</script><script>(()=>{let pagefindModal=()=>document.querySelector(`pagefind-modal`);function modalSearchInput(modal){return modal?modal.querySelector(`pagefind-input input, input[type='search'], input`):null}function setModalSearchQuery(modal,query){if(!query)return!1;let input=modalSearchInput(modal);return input?(input.value=query,input.dispatchEvent(new Event(`input`,{bubbles:!0})),!0):!1}function focusModalSearch(modal){let input=modalSearchInput(modal);return input?(input.focus(),!0):!1}async function openPagefindSearch(query){let modal=pagefindModal();if(!modal)return!1;if(typeof modal.open!=`function`&&window.customElements?.whenDefined)try{await window.customElements.whenDefined(`pagefind-modal`)}catch{}if(typeof modal.open!=`function`)return!1;modal.open();let sync=()=>{let hasQuery=setModalSearchQuery(modal,query),hasFocus=focusModalSearch(modal);return hasQuery||hasFocus};return sync()||requestAnimationFrame(()=>{sync()||window.setTimeout(sync,50)}),!0}function isTextEntryElement(element){return element?element.isContentEditable||element.tagName===`INPUT`||element.tagName===`SELECT`||element.tagName===`TEXTAREA`:!1}let SEARCH_SUGGESTION_LIMIT=20,SEARCH_SUBRESULT_LIMIT=3,SEARCH_SUGGESTION_MIN_LENGTH=2,SEARCH_SUGGESTION_DELAY=160,pagefindModulePromise=null;function pagefindBundlePath(){let script=document.querySelector(`script[src$="pagefind-component-ui.js"], script[src$="pagefind-ui.js"]`);return script?.src?new URL(`./`,script.src).toString():new URL(`pagefind/`,window.location.href).toString()}function loadPagefindModule(){return pagefindModulePromise||=import(`${pagefindBundlePath()}pagefind.js`),pagefindModulePromise}function plainTextFromHtml(html){let element=document.createElement(`div`);return element.innerHTML=html||``,element.textContent?.trim()||``}function setSearchResultsOpen(input,results,isOpen){results.hidden=!isOpen,input.setAttribute(`aria-expanded`,String(isOpen))}function clearSearchResults(input,results){results.innerHTML=``,setSearchResultsOpen(input,results,!1)}function renderSearchMessage(input,results,message){results.innerHTML=``;let item=document.createElement(`p`);item.className=`calepin-website-search-empty`,item.textContent=message,results.appendChild(item),setSearchResultsOpen(input,results,!0)}function renderSearchResults(input,results,items){if(results.innerHTML=``,!items.length){renderSearchMessage(input,results,`No results`);return}let basePathPrefix=localCanonicalBasePathPrefix(),addSearchLink=(item,className,titleClassName,excerptClassName)=>{let href=hrefWithoutLocalBasePath(item.meta?.url||item.url||``,basePathPrefix);if(!href)return null;let link=document.createElement(`a`);link.className=className,link.href=href,link.setAttribute(`role`,`option`);let title=document.createElement(`span`);title.className=titleClassName,title.textContent=item.meta?.title||item.title||href,link.appendChild(title);let excerptText=plainTextFromHtml(item.excerpt||item.content||``);if(excerptText){let excerpt=document.createElement(`span`);excerpt.className=excerptClassName,excerpt.textContent=excerptText,link.appendChild(excerpt)}return link};for(let item of items){let link=addSearchLink(item,`calepin-website-search-result`,`calepin-website-search-result-title`,`calepin-website-search-result-excerpt`);if(!link)continue;results.appendChild(link);let subResults=Array.isArray(item.sub_results)?item.sub_results.slice(0,3):[];if(!subResults.length)continue;let nested=document.createElement(`div`);nested.className=`calepin-website-search-subresults`;for(let subResult of subResults){let subLink=addSearchLink(subResult,`calepin-website-search-subresult`,`calepin-website-search-subresult-title`,`calepin-website-search-subresult-excerpt`);subLink&&nested.appendChild(subLink)}nested.children.length&&results.appendChild(nested)}setSearchResultsOpen(input,results,!!results.children.length)}function initMobileSearchTriggers(){document.querySelectorAll(`[data-calepin-mobile-search-trigger]`).forEach(button=>{button.dataset.calepinSearchBound!==Line truncated
|
||||
*
|
||||
* One module-local script wires: view switcher (HTML/Source/PDF), the mobile
|
||||
* sidebar drawer, sidebar section folding, inline SVGs, internal link state
|
||||
* preservation, the native <dialog> video lightbox, and the Pagefind navbar
|
||||
* search bridge.
|
||||
*
|
||||
* The Rust theme loader links this file near the end of the page body, so
|
||||
* the DOM already exists when it runs and no DOMContentLoaded guard is
|
||||
* needed. */
|
||||
(()=>{if(!document.querySelector(`.calepin-website-topbar, .calepin-website-main, pagefind-modal, img[data-inline-svg], [data-video-dialog], [data-lightbox-dialog]`))return;let VIEW={param:`view`,rendered:`rendered`,source:`source`,pdf:`pdf`},MOBILE_QUERY=`(max-width: 56rem)`,SOURCE_DATA_ID=`calepin-website-source-data`,isMobile=()=>window.matchMedia(`(max-width: 56rem)`).matches,isPlainLeftClick=event=>event&&event.button===0&&!event.metaKey&&!event.ctrlKey&&!event.shiftKey&&!event.altKey,isInternalPageHref=href=>!!href&&!href.startsWith(`#`)&&!href.startsWith(`http://`)&&!href.startsWith(`https://`)&&!href.startsWith(`//`)&&!href.startsWith(`mailto:`)&&!href.startsWith(`tel:`);function setUrlParam(url,name,value){value?url.searchParams.set(name,value):url.searchParams.delete(name)}function readUrlParam(name,normalize,fallback=``){try{return normalize((new URL(window.location.href).searchParams.get(name)||``).toLowerCase())}catch{return fallback}}function updateCurrentUrlParam(name,value){try{let url=new URL(window.location.href);setUrlParam(url,name,value),window.history.replaceState({},``,url.toString())}catch{}}let getMain=()=>document.querySelector(`.calepin-website-main`),normalizeView=value=>value===VIEW.source||value===VIEW.pdf?value:VIEW.rendered;function currentView(selects){let select=Array.isArray(selects)?selects[0]:selects;return normalizeView(select&&select.value||VIEW.rendered)}function pageAssetCandidates(extension){let{pathname}=window.location;return pathname.endsWith(`/`)?[`${pathname}index.${extension}`]:pathname.endsWith(`.html`)?[`${pathname.slice(0,-5)}.${extension}`]:!pathname||pathname.endsWith(`.`)?[`index.${extension}`]:pathname.includes(`.`)?[`${pathname}.${extension}`]:[`${pathname}/index.${extension}`]}function replaceMainContent(node){let main=getMain();return main?(main.innerHTML=``,main.appendChild(node),!0):!1}function showMainFallback(message){let main=getMain();if(!main)return;let fallback=document.createElement(`p`);fallback.textContent=message,main.prepend(fallback)}function renderSourceText(text){let container=document.createElement(`div`);container.className=`calepin-website-source-view`;let pre=document.createElement(`pre`),code=document.createElement(`code`);code.className=`language-typ`,code.textContent=text,pre.appendChild(code),container.appendChild(pre),replaceMainContent(container)}async function renderSourceMode(){let element=document.getElementById(`calepin-website-source-data`);if(element)try{let inline=JSON.parse(element.textContent||``);if(typeof inline==`string`&&inline.length>0){renderSourceText(inline);return}}catch{}for(let sourcePath of pageAssetCandidates(`typ`))try{let response=await fetch(sourcePath);if(!response.ok)continue;renderSourceText(await response.text());return}catch{}showMainFallback(`Could not load source view.`)}async function renderPdfMode(){let isLocal=window.location.protocol===`file:`;for(let pdfPath of pageAssetCandidates(`pdf`)){if(!isLocal)try{let response=await fetch(pdfPath,{method:`HEAD`});if(!response.ok&&response.status!==405)continue}catch{}let viewer=document.createElement(`iframe`);viewer.className=`calepin-website-pdf-viewer`;try{viewer.src=new URL(pdfPath,window.location.href).toString()}catch{viewer.src=pdfPath}viewer.setAttribute(`title`,`Page PDF view`),viewer.setAttribute(`loading`,`lazy`),replaceMainContent(viewer);return}showMainFallback(`Could not load PDF view.`)}function applyView(mode,selects){let value=normalizeView(mode);(Array.isArray(selects)?selects:[selects]).filter(Boolean).forEach(select=>{select.value=value}),updateCurrentUrlParam(VIEW.param,value===VIEW.rendered?``:value),value===VIEW.source?renderSourceMode():value===VIEW.pdf&&renderPdfMode()}function initView(selects){let viewSelects=(Array.isArray(selects)?selects:[selects]).filter(Boolean);applyView(readUrlParam(VIEW.param,normalizeView,VIEW.rendered),viewSelects),viewSelects.forEach(select=>{select.dataset.calepinBound||(select.dataset.calepinBound=`true`,select.addEventListener(`change`,event=>{let value=normalizeView(event.target.value),url=new URL(window.location.href);setUrlParam(url,VIEW.param,value===VIEW.rendered?``:value),window.location.href=url.toString()}))})}
|
||||
// ----------------------------------------------------------- nav drawer
|
||||
function createNav(){let shell=document.querySelector(`.calepin-website-shell`),backdrop=document.querySelector(`.calepin-website-nav-backdrop`),toggle=document.querySelector(`.calepin-website-nav-toggle`);function setOpen(isOpen){!shell||!toggle||(shell.classList.toggle(`nav-open`,isOpen),document.body.classList.toggle(`calepin-website-nav-open`,isOpen),toggle.setAttribute(`aria-expanded`,String(isOpen)),backdrop&&backdrop.setAttribute(`aria-hidden`,String(!isOpen)))}let close=()=>setOpen(!1);return toggle&&toggle.addEventListener(`click`,event=>{event.preventDefault(),isMobile()&&shell&&setOpen(!shell.classList.contains(`nav-open`))}),backdrop?.addEventListener(`click`,close),document.addEventListener(`keydown`,event=>{event.key===`Escape`&&close()}),window.addEventListener(`resize`,()=>{isMobile()||close()}),{close}}
|
||||
// ------------------------------------------------------ sidebar sections
|
||||
// Only one section is open at a time, but "section" means siblings: opening a
|
||||
// nested subsection must not fold the parent it lives in, and must only fold
|
||||
// its own siblings.
|
||||
function initSidebarSections(){let sections=Array.from(document.querySelectorAll(`.calepin-website-sidebar-section`));sections.length<2||sections.forEach(section=>{section.addEventListener(`toggle`,()=>{section.open&§ions.forEach(other=>{other!==section&&other.parentElement===section.parentElement&&other.removeAttribute(`open`)})})})}
|
||||
// ------------------------------------------------------------- inline svg
|
||||
function svgTextFromSource(src){if(!src)return Promise.resolve(``);let prefix=`data:image/svg+xml`;if(src.startsWith(prefix)){let payload=src.slice(src.indexOf(`,`)+1);try{return Promise.resolve(src.startsWith(`${prefix};base64,`)?atob(payload):decodeURIComponent(payload))}catch{return Promise.resolve(payload)}}return fetch(src).then(response=>response.text()).catch(()=>``)}function inlineSvgImage(image){return svgTextFromSource(image.getAttribute(`src`)).then(text=>{if(!text)return;let svg=new DOMParser().parseFromString(text,`image/svg+xml`).querySelector(`svg`);if(!svg)return;svg.removeAttribute(`xmlns:xlink`),svg.removeAttribute(`width`),svg.removeAttribute(`height`),image.className&&svg.setAttribute(`class`,image.className),image.id&&svg.setAttribute(`id`,image.id);let style=image.getAttribute(`style`);style&&svg.setAttribute(`style`,style);let alt=image.getAttribute(`alt`);alt&&(svg.setAttribute(`aria-label`,alt),svg.setAttribute(`role`,`img`));let ariaHidden=image.getAttribute(`aria-hidden`);ariaHidden!==null&&svg.setAttribute(`aria-hidden`,ariaHidden),image.replaceWith(svg)})}function inlineSvgs(){document.querySelectorAll(`img[data-inline-svg]`).forEach(image=>inlineSvgImage(image))}
|
||||
// --------------------------------------------------------- link state
|
||||
function urlWithState(href,viewSelect){if(!isInternalPageHref(href))return href;try{let url=new URL(href,window.location.href),view=currentView(viewSelect);return setUrlParam(url,VIEW.param,view===VIEW.rendered?``:view),url.toString()}catch{return href}}function preserveStateInLinks(viewSelect){document.querySelectorAll(`a[href]`).forEach(link=>{let href=link.getAttribute(`href`);if(!isInternalPageHref(href))return;let next=urlWithState(href,viewSelect);next&&next!==href&&link.setAttribute(`href`,next)})}function initLinkInterception(nav,viewSelect){document.addEventListener(`click`,event=>{let tocLink=event.target.closest(`.calepin-website-toc a`);if(tocLink){let href=tocLink.getAttribute(`href`);if(href&&href.startsWith(`#`)){let target=null;try{target=document.querySelector(href)}catch{target=null}target&&target.scrollIntoView({behavior:`smooth`,block:`start`})}isMobile()&&nav.close();return}let link=event.target.closest(`a`);if(!link||!isPlainLeftClick(event))return;event.target.closest(`.calepin-website-sidebar a`)&&isMobile()&&nav.close();let href=link.getAttribute(`href`),next=urlWithState(href,viewSelect);next&&next!==href&&(event.preventDefault(),window.location.href=next)})}
|
||||
// ----------------------------------------------------- dialog lightbox
|
||||
function initDialogs(){document.querySelectorAll(`[data-video-dialog], [data-lightbox-dialog]`).forEach(trigger=>{let dialogId=trigger.dataset.videoDialog||trigger.dataset.lightboxDialog,dialog=document.getElementById(dialogId);!dialog||typeof dialog.showModal!=`function`||trigger.addEventListener(`click`,event=>{event.preventDefault(),dialog.showModal()})}),document.querySelectorAll(`dialog.calepin-video-dialog, dialog.calepin-screenshot-dialog`).forEach(dialog=>{let stop=()=>{let video=dialog.querySelector(`video`);video&&video.pause()};dialog.addEventListener(`click`,event=>{
|
||||
// Clicking the dialog backdrop (the dialog element itself) closes it.
|
||||
event.target===dialog&&dialog.close()}),dialog.querySelectorAll(`[data-close-dialog]`).forEach(button=>{button.addEventListener(`click`,()=>dialog.close())}),dialog.addEventListener(`close`,stop)})}
|
||||
// --------------------------------------------------------------- bootstrap
|
||||
let viewSelects=Array.from(document.querySelectorAll(`[data-calepin-view-mode], #calepin-website-view-mode`));initView(viewSelects);let nav=createNav();initSidebarSections(),inlineSvgs(),initDialogs(),preserveStateInLinks(viewSelects),initLinkInterception(nav,viewSelects),window.addEventListener(`pageshow`,event=>{event.persisted&&(initView(viewSelects),preserveStateInLinks(viewSelects))})})();</script>
|
||||
@@ -0,0 +1,20 @@
|
||||
# Agent Rules for Yggdrasil Migration
|
||||
|
||||
* **Commit Policy**: Small conventional commits. One logical change per
|
||||
commit, committed only once that step is verified and working. End every
|
||||
commit message with a blank line followed by an
|
||||
`Assisted-by: Delta:zai-glm-5.3` footer.
|
||||
* **Layout**: Sources live in `site/` (`calepin.toml` + `.typ` pages),
|
||||
generated output in `public/` (gitignored), pinned binaries in `tools/`
|
||||
(gitignored), helper scripts in `scripts/`, and the old Docsify
|
||||
documentation stays untouched in `legacy_docs/`.
|
||||
* **Toolchain**: `calepin` (0.0.61) and `typst` (0.15.1) are pinned in
|
||||
`scripts/bootstrap.sh` and downloaded into `tools/` with checksum
|
||||
verification. The build must never depend on global installs. CI installs
|
||||
the same pinned versions by calling the same scripts.
|
||||
* **Verification**: Before committing, run a fresh cycle: `rm -rf tools`,
|
||||
then `just clean`, then `just build` — the build must succeed with no
|
||||
calepin on `PATH`. Verify the served site (`just preview`): home 200,
|
||||
category page 200, sub-page 200, missing page shows the 404 page,
|
||||
`sitemap.xml` present. After every test/clean cycle `git status` must stay
|
||||
clean.
|
||||
@@ -0,0 +1,31 @@
|
||||
# Show the available recipes
|
||||
default:
|
||||
@just --list
|
||||
|
||||
# Bootstrap the pinned calepin + typst binaries into tools/
|
||||
tools:
|
||||
./scripts/bootstrap.sh
|
||||
|
||||
# Compile the site into public/
|
||||
build: tools
|
||||
./scripts/calepin.sh compile site public
|
||||
|
||||
# Build, then serve the site locally
|
||||
preview: build
|
||||
./scripts/calepin.sh serve public
|
||||
|
||||
# Rebuild on change and serve the result
|
||||
watch: tools
|
||||
./scripts/calepin.sh watch site public --serve
|
||||
|
||||
# Remove generated output and build caches
|
||||
clean:
|
||||
./scripts/clean.sh
|
||||
|
||||
# Remove all EXIF metadata from a file
|
||||
strip-exif file:
|
||||
exiftool -all= -overwrite_original {{ quote(file) }}
|
||||
|
||||
# Run the CI workflow locally with act (requires act + docker)
|
||||
ci:
|
||||
act push -W .gitea/workflows/build.yml -j build --artifact-server-path /tmp/act-artifacts
|
||||
@@ -0,0 +1,88 @@
|
||||
# Yggdrasil
|
||||
|
||||
Personal knowledge base built as a static website with
|
||||
[Calepin](https://vincentarelbundock.github.io/calepin/), a Typst-based static
|
||||
site generator.
|
||||
|
||||
## Layout
|
||||
|
||||
- `site/` — website sources: `calepin.toml` + `.typ` pages
|
||||
- `site/themes/yggdrasil/` — local theme extending the bundled `calepin` theme (scoped sidebars, academic-styled landing)
|
||||
- `public/` — generated site (gitignored)
|
||||
- `tools/` — pinned `calepin` and `typst` binaries (gitignored, downloaded on demand)
|
||||
- `scripts/` — helper scripts used by the Justfile and CI
|
||||
- `legacy_docs/` — the old Docsify documentation, kept unmodified for reference
|
||||
|
||||
## Quick start
|
||||
|
||||
`just` is the only prerequisite — the toolchain is downloaded and
|
||||
checksum-verified into `tools/` on first use (no global installs, no cargo):
|
||||
|
||||
just # list recipes
|
||||
just tools # download the pinned toolchain into tools/
|
||||
just build # compile site/ into public/
|
||||
just preview # build, then serve the site locally
|
||||
just watch # rebuild on change and serve
|
||||
just clean # remove public/ and build caches
|
||||
just ci # run the CI workflow locally with act (needs act + docker)
|
||||
|
||||
## Adding a page
|
||||
|
||||
Create `site/<category>/<page>.typ`:
|
||||
|
||||
```typst
|
||||
#set document(title: [Page title])
|
||||
#metadata((title: "Page title", translation_key: "category-page")) <website-metadata>
|
||||
|
||||
#title()
|
||||
|
||||
= Page title
|
||||
|
||||
Content…
|
||||
```
|
||||
|
||||
and reference it in `site/calepin.toml`:
|
||||
|
||||
```toml
|
||||
[[sidebar.section.item]]
|
||||
target = "<category>/<page>.typ"
|
||||
```
|
||||
|
||||
Sidebar labels come from the page's `#metadata(...)` line, not from the config.
|
||||
|
||||
Every page also renders a printable PDF twin (`pdf = true` in
|
||||
`calepin.toml`, selectable in the page's view picker). Add `pdf: false` to a
|
||||
page's `#metadata(...)` to skip its PDF, as `404.typ` does.
|
||||
|
||||
## Adding a category
|
||||
|
||||
1. Create `site/<category>/` with an `index.typ`, using
|
||||
`#metadata((title: "Overview", ...))` for its sidebar label.
|
||||
2. Add a `[[sidebar.section]]` named after the category in `calepin.toml`,
|
||||
listing the index page and its sub-pages.
|
||||
3. Add a `[[menus.main]]` entry with the category's `index.typ` as `target`
|
||||
and the category name as `label` (e.g. `Home`, `IT`), so it appears in the
|
||||
top navigation bar. The `label` is required here because the index page's
|
||||
metadata title is `Overview`.
|
||||
|
||||
Pages inside a category show only that category's sidebar section — the
|
||||
local theme (`site/themes/yggdrasil/partials/site-nav-sidebar.html`) filters
|
||||
the sidebar to the section containing the current page. Pages outside every
|
||||
category (landing page, `404`, standalone pages) fall back to the full
|
||||
sidebar.
|
||||
|
||||
## Deployment
|
||||
|
||||
`just build` produces a fully static site in `public/`: HTML pages with
|
||||
printable PDF twins, plus `sitemap.xml`, `robots.txt`, and `llms.txt`. The
|
||||
Gitea Actions workflow in
|
||||
`.gitea/workflows/build.yml` builds the site on every push with the same
|
||||
pinned toolchain and uploads `public/` as an artifact.
|
||||
|
||||
Pushes to `main` are also published: the `deploy` job force-pushes `public/`
|
||||
to the repo's `pages` branch using the automatic Actions token (no secrets to
|
||||
configure). A Gitea webhook on the repo then notifies the self-hosted
|
||||
[git-pages](https://codeberg.org/git-pages/git-pages) server, which clones
|
||||
the `pages` branch and serves the site. The push step is gated with
|
||||
`if: ${{ !env.ACT }}` so local `act` runs never publish, and
|
||||
`workflow_dispatch` enables manual publishes from the Gitea UI.
|
||||
@@ -1,29 +0,0 @@
|
||||
<!doctype html><html lang=en><head><meta charset=utf-8><meta content="width=device-width,initial-scale=1" name=viewport><title>Computer Science</title><meta content="Placeholder page for the Computer Science category. The real content comes later." name=description><meta content="Placeholder page for the Computer Science category. The real content comes later." property=og:description><meta content="Placeholder page for the Computer Science category. The real content comes later." name=twitter:description><meta content=Yggdrasil property=og:site_name><meta content="Computer Science | Yggdrasil" property=og:title><meta content="Computer Science | Yggdrasil" name=twitter:title><meta content=article property=og:type><meta content=summary name=twitter:card><link href=https://yggdrasil.kb28.ch/cs/ rel=canonical><meta content=https://yggdrasil.kb28.ch/cs/ property=og:url><link href=../assets/yggdrasil.ico rel=icon><meta content="light dark" name=color-scheme><script data-calepin-theme-param=theme data-calepin-theme-storage-key=calepin-website-theme>(()=>{let currentScript=document.currentScript,storageKey=currentScript?.dataset?.calepinThemeStorageKey||`calepin-website-theme`,param=currentScript?.dataset?.calepinThemeParam||`theme`,normalize=value=>value===`light`||value===`dark`?value:``,root=document.documentElement,media=window.matchMedia?window.matchMedia(`(prefers-color-scheme: dark)`):null,systemTheme=()=>media&&media.matches?`dark`:`light`,mode=``;if(param)try{mode=normalize(new URL(window.location.href).searchParams.get(param)||``)}catch{mode=``}if(!mode)try{mode=normalize(localStorage.getItem(storageKey)||``)}catch{mode=``}let theme=mode||systemTheme();root.dataset.calepinThemeMode=mode,root.dataset.theme=theme,root.style.colorScheme=theme})();</script><style>:host,:root{--pico-font-family-emoji:"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--pico-font-family-sans-serif:system-ui,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,Helvetica,Arial,"Helvetica Neue",sans-serif,var(--pico-font-family-emoji);--pico-font-family-monospace:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,"Liberation Mono",monospace,var(--pico-font-family-emoji);--pico-font-family:var(--pico-font-family-sans-serif);--pico-line-height:1.5;--pico-font-weight:400;--pico-font-size:100%;--pico-text-underline-offset:.1rem;--pico-border-radius:.25rem;--pico-border-width:.0625rem;--pico-outline-width:.125rem;--pico-transition:.2s ease-in-out;--pico-spacing:1rem;--pico-typography-spacing-vertical:1rem;--pico-block-spacing-vertical:var(--pico-spacing);--pico-block-spacing-horizontal:var(--pico-spacing);--pico-grid-column-gap:var(--pico-spacing);--pico-grid-row-gap:var(--pico-spacing);--pico-form-element-spacing-vertical:.75rem;--pico-form-element-spacing-horizontal:1rem;--pico-group-box-shadow:0 0 0 #0000;--pico-group-box-shadow-focus-with-button:0 0 0 var(--pico-outline-width) var(--pico-primary-focus);--pico-group-box-shadow-focus-with-input:0 0 0 .0625rem var(--pico-form-element-border-color);--pico-modal-overlay-backdrop-filter:blur(.375rem);--pico-nav-element-spacing-vertical:1rem;--pico-nav-element-spacing-horizontal:.5rem;--pico-nav-link-spacing-vertical:.5rem;--pico-nav-link-spacing-horizontal:.5rem;--pico-nav-breadcrumb-divider:">";--pico-icon-checkbox:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-minus:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E");--pico-icon-chevron:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-date:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E");--pico-icon-time:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline points='12 6 12 12 16 14'%3E%3C/polyline%3E%3C/svLine truncated
|
||||
`:Array.from(node.childNodes).map(codeText).join(``):``:``}document.querySelectorAll(selector).forEach(block=>{if(block.dataset.calepinCopyBound===`true`||block.querySelector(`:scope > .${buttonClass}`))return;block.dataset.calepinCopyBound=`true`;let button=document.createElement(`button`);button.type=`button`,button.className=buttonClass,button.setAttribute(`aria-label`,`Copy code`),button.setAttribute(`title`,`Copy code`),button.innerHTML=icon;let restoreTimeout=null;button.addEventListener(`click`,async()=>{restoreTimeout!==null&&(window.clearTimeout(restoreTimeout),restoreTimeout=null);let text=codeText(block.querySelector(`pre code, code, pre`));try{await copyText(text),button.classList.add(copiedClass),button.innerHTML=copiedIcon,restoreTimeout=window.setTimeout(()=>{button.classList.remove(copiedClass),button.innerHTML=icon,restoreTimeout=null},900)}catch{button.classList.remove(copiedClass),button.innerHTML=icon}}),block.prepend(button)})})();</script><script>(()=>{let pagefindModal=()=>document.querySelector(`pagefind-modal`);function modalSearchInput(modal){return modal?modal.querySelector(`pagefind-input input, input[type='search'], input`):null}function setModalSearchQuery(modal,query){if(!query)return!1;let input=modalSearchInput(modal);return input?(input.value=query,input.dispatchEvent(new Event(`input`,{bubbles:!0})),!0):!1}function focusModalSearch(modal){let input=modalSearchInput(modal);return input?(input.focus(),!0):!1}async function openPagefindSearch(query){let modal=pagefindModal();if(!modal)return!1;if(typeof modal.open!=`function`&&window.customElements?.whenDefined)try{await window.customElements.whenDefined(`pagefind-modal`)}catch{}if(typeof modal.open!=`function`)return!1;modal.open();let sync=()=>{let hasQuery=setModalSearchQuery(modal,query),hasFocus=focusModalSearch(modal);return hasQuery||hasFocus};return sync()||requestAnimationFrame(()=>{sync()||window.setTimeout(sync,50)}),!0}function isTextEntryElement(element){return element?element.isContentEditable||element.tagName===`INPUT`||element.tagName===`SELECT`||element.tagName===`TEXTAREA`:!1}let SEARCH_SUGGESTION_LIMIT=20,SEARCH_SUBRESULT_LIMIT=3,SEARCH_SUGGESTION_MIN_LENGTH=2,SEARCH_SUGGESTION_DELAY=160,pagefindModulePromise=null;function pagefindBundlePath(){let script=document.querySelector(`script[src$="pagefind-component-ui.js"], script[src$="pagefind-ui.js"]`);return script?.src?new URL(`./`,script.src).toString():new URL(`pagefind/`,window.location.href).toString()}function loadPagefindModule(){return pagefindModulePromise||=import(`${pagefindBundlePath()}pagefind.js`),pagefindModulePromise}function plainTextFromHtml(html){let element=document.createElement(`div`);return element.innerHTML=html||``,element.textContent?.trim()||``}function setSearchResultsOpen(input,results,isOpen){results.hidden=!isOpen,input.setAttribute(`aria-expanded`,String(isOpen))}function clearSearchResults(input,results){results.innerHTML=``,setSearchResultsOpen(input,results,!1)}function renderSearchMessage(input,results,message){results.innerHTML=``;let item=document.createElement(`p`);item.className=`calepin-website-search-empty`,item.textContent=message,results.appendChild(item),setSearchResultsOpen(input,results,!0)}function renderSearchResults(input,results,items){if(results.innerHTML=``,!items.length){renderSearchMessage(input,results,`No results`);return}let basePathPrefix=localCanonicalBasePathPrefix(),addSearchLink=(item,className,titleClassName,excerptClassName)=>{let href=hrefWithoutLocalBasePath(item.meta?.url||item.url||``,basePathPrefix);if(!href)return null;let link=document.createElement(`a`);link.className=className,link.href=href,link.setAttribute(`role`,`option`);let title=document.createElement(`span`);title.className=titleClassName,title.textContent=item.meta?.title||item.title||href,link.appendChild(title);let excerptText=plainTextFromHtml(item.excerpt||item.content||``);if(excerptText){let excerpt=document.createElement(`span`);excerpt.className=excerptClassName,excerpt.textContent=excerptText,link.appendChild(excerpt)}return link};for(let item of items){let link=addSearchLink(item,`calepin-website-search-result`,`calepin-website-search-result-title`,`calepin-website-search-result-excerpt`);if(!link)continue;results.appendChild(link);let subResults=Array.isArray(item.sub_results)?item.sub_results.slice(0,3):[];if(!subResults.length)continue;let nested=document.createElement(`div`);nested.className=`calepin-website-search-subresults`;for(let subResult of subResults){let subLink=addSearchLink(subResult,`calepin-website-search-subresult`,`calepin-website-search-subresult-title`,`calepin-website-search-subresult-excerpt`);subLink&&nested.appendChild(subLink)}nested.children.length&&results.appendChild(nested)}setSearchResultsOpen(input,results,!!results.children.length)}function initMobileSearchTriggers(){document.querySelectorAll(`[data-calepin-mobile-search-trigger]`).forEach(button=>{button.dataset.calepinSearchBound!==Line truncated
|
||||
*
|
||||
* One module-local script wires: view switcher (HTML/Source/PDF), the mobile
|
||||
* sidebar drawer, sidebar section folding, inline SVGs, internal link state
|
||||
* preservation, the native <dialog> video lightbox, and the Pagefind navbar
|
||||
* search bridge.
|
||||
*
|
||||
* The Rust theme loader links this file near the end of the page body, so
|
||||
* the DOM already exists when it runs and no DOMContentLoaded guard is
|
||||
* needed. */
|
||||
(()=>{if(!document.querySelector(`.calepin-website-topbar, .calepin-website-main, pagefind-modal, img[data-inline-svg], [data-video-dialog], [data-lightbox-dialog]`))return;let VIEW={param:`view`,rendered:`rendered`,source:`source`,pdf:`pdf`},MOBILE_QUERY=`(max-width: 56rem)`,SOURCE_DATA_ID=`calepin-website-source-data`,isMobile=()=>window.matchMedia(`(max-width: 56rem)`).matches,isPlainLeftClick=event=>event&&event.button===0&&!event.metaKey&&!event.ctrlKey&&!event.shiftKey&&!event.altKey,isInternalPageHref=href=>!!href&&!href.startsWith(`#`)&&!href.startsWith(`http://`)&&!href.startsWith(`https://`)&&!href.startsWith(`//`)&&!href.startsWith(`mailto:`)&&!href.startsWith(`tel:`);function setUrlParam(url,name,value){value?url.searchParams.set(name,value):url.searchParams.delete(name)}function readUrlParam(name,normalize,fallback=``){try{return normalize((new URL(window.location.href).searchParams.get(name)||``).toLowerCase())}catch{return fallback}}function updateCurrentUrlParam(name,value){try{let url=new URL(window.location.href);setUrlParam(url,name,value),window.history.replaceState({},``,url.toString())}catch{}}let getMain=()=>document.querySelector(`.calepin-website-main`),normalizeView=value=>value===VIEW.source||value===VIEW.pdf?value:VIEW.rendered;function currentView(selects){let select=Array.isArray(selects)?selects[0]:selects;return normalizeView(select&&select.value||VIEW.rendered)}function pageAssetCandidates(extension){let{pathname}=window.location;return pathname.endsWith(`/`)?[`${pathname}index.${extension}`]:pathname.endsWith(`.html`)?[`${pathname.slice(0,-5)}.${extension}`]:!pathname||pathname.endsWith(`.`)?[`index.${extension}`]:pathname.includes(`.`)?[`${pathname}.${extension}`]:[`${pathname}/index.${extension}`]}function replaceMainContent(node){let main=getMain();return main?(main.innerHTML=``,main.appendChild(node),!0):!1}function showMainFallback(message){let main=getMain();if(!main)return;let fallback=document.createElement(`p`);fallback.textContent=message,main.prepend(fallback)}function renderSourceText(text){let container=document.createElement(`div`);container.className=`calepin-website-source-view`;let pre=document.createElement(`pre`),code=document.createElement(`code`);code.className=`language-typ`,code.textContent=text,pre.appendChild(code),container.appendChild(pre),replaceMainContent(container)}async function renderSourceMode(){let element=document.getElementById(`calepin-website-source-data`);if(element)try{let inline=JSON.parse(element.textContent||``);if(typeof inline==`string`&&inline.length>0){renderSourceText(inline);return}}catch{}for(let sourcePath of pageAssetCandidates(`typ`))try{let response=await fetch(sourcePath);if(!response.ok)continue;renderSourceText(await response.text());return}catch{}showMainFallback(`Could not load source view.`)}async function renderPdfMode(){let isLocal=window.location.protocol===`file:`;for(let pdfPath of pageAssetCandidates(`pdf`)){if(!isLocal)try{let response=await fetch(pdfPath,{method:`HEAD`});if(!response.ok&&response.status!==405)continue}catch{}let viewer=document.createElement(`iframe`);viewer.className=`calepin-website-pdf-viewer`;try{viewer.src=new URL(pdfPath,window.location.href).toString()}catch{viewer.src=pdfPath}viewer.setAttribute(`title`,`Page PDF view`),viewer.setAttribute(`loading`,`lazy`),replaceMainContent(viewer);return}showMainFallback(`Could not load PDF view.`)}function applyView(mode,selects){let value=normalizeView(mode);(Array.isArray(selects)?selects:[selects]).filter(Boolean).forEach(select=>{select.value=value}),updateCurrentUrlParam(VIEW.param,value===VIEW.rendered?``:value),value===VIEW.source?renderSourceMode():value===VIEW.pdf&&renderPdfMode()}function initView(selects){let viewSelects=(Array.isArray(selects)?selects:[selects]).filter(Boolean);applyView(readUrlParam(VIEW.param,normalizeView,VIEW.rendered),viewSelects),viewSelects.forEach(select=>{select.dataset.calepinBound||(select.dataset.calepinBound=`true`,select.addEventListener(`change`,event=>{let value=normalizeView(event.target.value),url=new URL(window.location.href);setUrlParam(url,VIEW.param,value===VIEW.rendered?``:value),window.location.href=url.toString()}))})}
|
||||
// ----------------------------------------------------------- nav drawer
|
||||
function createNav(){let shell=document.querySelector(`.calepin-website-shell`),backdrop=document.querySelector(`.calepin-website-nav-backdrop`),toggle=document.querySelector(`.calepin-website-nav-toggle`);function setOpen(isOpen){!shell||!toggle||(shell.classList.toggle(`nav-open`,isOpen),document.body.classList.toggle(`calepin-website-nav-open`,isOpen),toggle.setAttribute(`aria-expanded`,String(isOpen)),backdrop&&backdrop.setAttribute(`aria-hidden`,String(!isOpen)))}let close=()=>setOpen(!1);return toggle&&toggle.addEventListener(`click`,event=>{event.preventDefault(),isMobile()&&shell&&setOpen(!shell.classList.contains(`nav-open`))}),backdrop?.addEventListener(`click`,close),document.addEventListener(`keydown`,event=>{event.key===`Escape`&&close()}),window.addEventListener(`resize`,()=>{isMobile()||close()}),{close}}
|
||||
// ------------------------------------------------------ sidebar sections
|
||||
// Only one section is open at a time, but "section" means siblings: opening a
|
||||
// nested subsection must not fold the parent it lives in, and must only fold
|
||||
// its own siblings.
|
||||
function initSidebarSections(){let sections=Array.from(document.querySelectorAll(`.calepin-website-sidebar-section`));sections.length<2||sections.forEach(section=>{section.addEventListener(`toggle`,()=>{section.open&§ions.forEach(other=>{other!==section&&other.parentElement===section.parentElement&&other.removeAttribute(`open`)})})})}
|
||||
// ------------------------------------------------------------- inline svg
|
||||
function svgTextFromSource(src){if(!src)return Promise.resolve(``);let prefix=`data:image/svg+xml`;if(src.startsWith(prefix)){let payload=src.slice(src.indexOf(`,`)+1);try{return Promise.resolve(src.startsWith(`${prefix};base64,`)?atob(payload):decodeURIComponent(payload))}catch{return Promise.resolve(payload)}}return fetch(src).then(response=>response.text()).catch(()=>``)}function inlineSvgImage(image){return svgTextFromSource(image.getAttribute(`src`)).then(text=>{if(!text)return;let svg=new DOMParser().parseFromString(text,`image/svg+xml`).querySelector(`svg`);if(!svg)return;svg.removeAttribute(`xmlns:xlink`),svg.removeAttribute(`width`),svg.removeAttribute(`height`),image.className&&svg.setAttribute(`class`,image.className),image.id&&svg.setAttribute(`id`,image.id);let style=image.getAttribute(`style`);style&&svg.setAttribute(`style`,style);let alt=image.getAttribute(`alt`);alt&&(svg.setAttribute(`aria-label`,alt),svg.setAttribute(`role`,`img`));let ariaHidden=image.getAttribute(`aria-hidden`);ariaHidden!==null&&svg.setAttribute(`aria-hidden`,ariaHidden),image.replaceWith(svg)})}function inlineSvgs(){document.querySelectorAll(`img[data-inline-svg]`).forEach(image=>inlineSvgImage(image))}
|
||||
// --------------------------------------------------------- link state
|
||||
function urlWithState(href,viewSelect){if(!isInternalPageHref(href))return href;try{let url=new URL(href,window.location.href),view=currentView(viewSelect);return setUrlParam(url,VIEW.param,view===VIEW.rendered?``:view),url.toString()}catch{return href}}function preserveStateInLinks(viewSelect){document.querySelectorAll(`a[href]`).forEach(link=>{let href=link.getAttribute(`href`);if(!isInternalPageHref(href))return;let next=urlWithState(href,viewSelect);next&&next!==href&&link.setAttribute(`href`,next)})}function initLinkInterception(nav,viewSelect){document.addEventListener(`click`,event=>{let tocLink=event.target.closest(`.calepin-website-toc a`);if(tocLink){let href=tocLink.getAttribute(`href`);if(href&&href.startsWith(`#`)){let target=null;try{target=document.querySelector(href)}catch{target=null}target&&target.scrollIntoView({behavior:`smooth`,block:`start`})}isMobile()&&nav.close();return}let link=event.target.closest(`a`);if(!link||!isPlainLeftClick(event))return;event.target.closest(`.calepin-website-sidebar a`)&&isMobile()&&nav.close();let href=link.getAttribute(`href`),next=urlWithState(href,viewSelect);next&&next!==href&&(event.preventDefault(),window.location.href=next)})}
|
||||
// ----------------------------------------------------- dialog lightbox
|
||||
function initDialogs(){document.querySelectorAll(`[data-video-dialog], [data-lightbox-dialog]`).forEach(trigger=>{let dialogId=trigger.dataset.videoDialog||trigger.dataset.lightboxDialog,dialog=document.getElementById(dialogId);!dialog||typeof dialog.showModal!=`function`||trigger.addEventListener(`click`,event=>{event.preventDefault(),dialog.showModal()})}),document.querySelectorAll(`dialog.calepin-video-dialog, dialog.calepin-screenshot-dialog`).forEach(dialog=>{let stop=()=>{let video=dialog.querySelector(`video`);video&&video.pause()};dialog.addEventListener(`click`,event=>{
|
||||
// Clicking the dialog backdrop (the dialog element itself) closes it.
|
||||
event.target===dialog&&dialog.close()}),dialog.querySelectorAll(`[data-close-dialog]`).forEach(button=>{button.addEventListener(`click`,()=>dialog.close())}),dialog.addEventListener(`close`,stop)})}
|
||||
// --------------------------------------------------------------- bootstrap
|
||||
let viewSelects=Array.from(document.querySelectorAll(`[data-calepin-view-mode], #calepin-website-view-mode`));initView(viewSelects);let nav=createNav();initSidebarSections(),inlineSvgs(),initDialogs(),preserveStateInLinks(viewSelects),initLinkInterception(nav,viewSelects),window.addEventListener(`pageshow`,event=>{event.persisted&&(initView(viewSelects),preserveStateInLinks(viewSelects))})})();</script>
|
||||
@@ -1,29 +0,0 @@
|
||||
<!doctype html><html lang=en><head><meta charset=utf-8><meta content="width=device-width,initial-scale=1" name=viewport><title>Cooking</title><meta content="Placeholder page — the real content comes later." name=description><meta content="Placeholder page — the real content comes later." property=og:description><meta content="Placeholder page — the real content comes later." name=twitter:description><meta content=Yggdrasil property=og:site_name><meta content="Cooking | Yggdrasil" property=og:title><meta content="Cooking | Yggdrasil" name=twitter:title><meta content=article property=og:type><meta content=summary name=twitter:card><link href=https://yggdrasil.kb28.ch/home/cooking.html rel=canonical><meta content=https://yggdrasil.kb28.ch/home/cooking.html property=og:url><link href=../assets/yggdrasil.ico rel=icon><meta content="light dark" name=color-scheme><script data-calepin-theme-param=theme data-calepin-theme-storage-key=calepin-website-theme>(()=>{let currentScript=document.currentScript,storageKey=currentScript?.dataset?.calepinThemeStorageKey||`calepin-website-theme`,param=currentScript?.dataset?.calepinThemeParam||`theme`,normalize=value=>value===`light`||value===`dark`?value:``,root=document.documentElement,media=window.matchMedia?window.matchMedia(`(prefers-color-scheme: dark)`):null,systemTheme=()=>media&&media.matches?`dark`:`light`,mode=``;if(param)try{mode=normalize(new URL(window.location.href).searchParams.get(param)||``)}catch{mode=``}if(!mode)try{mode=normalize(localStorage.getItem(storageKey)||``)}catch{mode=``}let theme=mode||systemTheme();root.dataset.calepinThemeMode=mode,root.dataset.theme=theme,root.style.colorScheme=theme})();</script><style>:host,:root{--pico-font-family-emoji:"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--pico-font-family-sans-serif:system-ui,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,Helvetica,Arial,"Helvetica Neue",sans-serif,var(--pico-font-family-emoji);--pico-font-family-monospace:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,"Liberation Mono",monospace,var(--pico-font-family-emoji);--pico-font-family:var(--pico-font-family-sans-serif);--pico-line-height:1.5;--pico-font-weight:400;--pico-font-size:100%;--pico-text-underline-offset:.1rem;--pico-border-radius:.25rem;--pico-border-width:.0625rem;--pico-outline-width:.125rem;--pico-transition:.2s ease-in-out;--pico-spacing:1rem;--pico-typography-spacing-vertical:1rem;--pico-block-spacing-vertical:var(--pico-spacing);--pico-block-spacing-horizontal:var(--pico-spacing);--pico-grid-column-gap:var(--pico-spacing);--pico-grid-row-gap:var(--pico-spacing);--pico-form-element-spacing-vertical:.75rem;--pico-form-element-spacing-horizontal:1rem;--pico-group-box-shadow:0 0 0 #0000;--pico-group-box-shadow-focus-with-button:0 0 0 var(--pico-outline-width) var(--pico-primary-focus);--pico-group-box-shadow-focus-with-input:0 0 0 .0625rem var(--pico-form-element-border-color);--pico-modal-overlay-backdrop-filter:blur(.375rem);--pico-nav-element-spacing-vertical:1rem;--pico-nav-element-spacing-horizontal:.5rem;--pico-nav-link-spacing-vertical:.5rem;--pico-nav-link-spacing-horizontal:.5rem;--pico-nav-breadcrumb-divider:">";--pico-icon-checkbox:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-minus:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E");--pico-icon-chevron:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-date:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E");--pico-icon-time:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline points='12 6 12 12 16 14'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-search:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' Line truncated
|
||||
`:Array.from(node.childNodes).map(codeText).join(``):``:``}document.querySelectorAll(selector).forEach(block=>{if(block.dataset.calepinCopyBound===`true`||block.querySelector(`:scope > .${buttonClass}`))return;block.dataset.calepinCopyBound=`true`;let button=document.createElement(`button`);button.type=`button`,button.className=buttonClass,button.setAttribute(`aria-label`,`Copy code`),button.setAttribute(`title`,`Copy code`),button.innerHTML=icon;let restoreTimeout=null;button.addEventListener(`click`,async()=>{restoreTimeout!==null&&(window.clearTimeout(restoreTimeout),restoreTimeout=null);let text=codeText(block.querySelector(`pre code, code, pre`));try{await copyText(text),button.classList.add(copiedClass),button.innerHTML=copiedIcon,restoreTimeout=window.setTimeout(()=>{button.classList.remove(copiedClass),button.innerHTML=icon,restoreTimeout=null},900)}catch{button.classList.remove(copiedClass),button.innerHTML=icon}}),block.prepend(button)})})();</script><script>(()=>{let pagefindModal=()=>document.querySelector(`pagefind-modal`);function modalSearchInput(modal){return modal?modal.querySelector(`pagefind-input input, input[type='search'], input`):null}function setModalSearchQuery(modal,query){if(!query)return!1;let input=modalSearchInput(modal);return input?(input.value=query,input.dispatchEvent(new Event(`input`,{bubbles:!0})),!0):!1}function focusModalSearch(modal){let input=modalSearchInput(modal);return input?(input.focus(),!0):!1}async function openPagefindSearch(query){let modal=pagefindModal();if(!modal)return!1;if(typeof modal.open!=`function`&&window.customElements?.whenDefined)try{await window.customElements.whenDefined(`pagefind-modal`)}catch{}if(typeof modal.open!=`function`)return!1;modal.open();let sync=()=>{let hasQuery=setModalSearchQuery(modal,query),hasFocus=focusModalSearch(modal);return hasQuery||hasFocus};return sync()||requestAnimationFrame(()=>{sync()||window.setTimeout(sync,50)}),!0}function isTextEntryElement(element){return element?element.isContentEditable||element.tagName===`INPUT`||element.tagName===`SELECT`||element.tagName===`TEXTAREA`:!1}let SEARCH_SUGGESTION_LIMIT=20,SEARCH_SUBRESULT_LIMIT=3,SEARCH_SUGGESTION_MIN_LENGTH=2,SEARCH_SUGGESTION_DELAY=160,pagefindModulePromise=null;function pagefindBundlePath(){let script=document.querySelector(`script[src$="pagefind-component-ui.js"], script[src$="pagefind-ui.js"]`);return script?.src?new URL(`./`,script.src).toString():new URL(`pagefind/`,window.location.href).toString()}function loadPagefindModule(){return pagefindModulePromise||=import(`${pagefindBundlePath()}pagefind.js`),pagefindModulePromise}function plainTextFromHtml(html){let element=document.createElement(`div`);return element.innerHTML=html||``,element.textContent?.trim()||``}function setSearchResultsOpen(input,results,isOpen){results.hidden=!isOpen,input.setAttribute(`aria-expanded`,String(isOpen))}function clearSearchResults(input,results){results.innerHTML=``,setSearchResultsOpen(input,results,!1)}function renderSearchMessage(input,results,message){results.innerHTML=``;let item=document.createElement(`p`);item.className=`calepin-website-search-empty`,item.textContent=message,results.appendChild(item),setSearchResultsOpen(input,results,!0)}function renderSearchResults(input,results,items){if(results.innerHTML=``,!items.length){renderSearchMessage(input,results,`No results`);return}let basePathPrefix=localCanonicalBasePathPrefix(),addSearchLink=(item,className,titleClassName,excerptClassName)=>{let href=hrefWithoutLocalBasePath(item.meta?.url||item.url||``,basePathPrefix);if(!href)return null;let link=document.createElement(`a`);link.className=className,link.href=href,link.setAttribute(`role`,`option`);let title=document.createElement(`span`);title.className=titleClassName,title.textContent=item.meta?.title||item.title||href,link.appendChild(title);let excerptText=plainTextFromHtml(item.excerpt||item.content||``);if(excerptText){let excerpt=document.createElement(`span`);excerpt.className=excerptClassName,excerpt.textContent=excerptText,link.appendChild(excerpt)}return link};for(let item of items){let link=addSearchLink(item,`calepin-website-search-result`,`calepin-website-search-result-title`,`calepin-website-search-result-excerpt`);if(!link)continue;results.appendChild(link);let subResults=Array.isArray(item.sub_results)?item.sub_results.slice(0,3):[];if(!subResults.length)continue;let nested=document.createElement(`div`);nested.className=`calepin-website-search-subresults`;for(let subResult of subResults){let subLink=addSearchLink(subResult,`calepin-website-search-subresult`,`calepin-website-search-subresult-title`,`calepin-website-search-subresult-excerpt`);subLink&&nested.appendChild(subLink)}nested.children.length&&results.appendChild(nested)}setSearchResultsOpen(input,results,!!results.children.length)}function initMobileSearchTriggers(){document.querySelectorAll(`[data-calepin-mobile-search-trigger]`).forEach(button=>{button.dataset.calepinSearchBound!==Line truncated
|
||||
*
|
||||
* One module-local script wires: view switcher (HTML/Source/PDF), the mobile
|
||||
* sidebar drawer, sidebar section folding, inline SVGs, internal link state
|
||||
* preservation, the native <dialog> video lightbox, and the Pagefind navbar
|
||||
* search bridge.
|
||||
*
|
||||
* The Rust theme loader links this file near the end of the page body, so
|
||||
* the DOM already exists when it runs and no DOMContentLoaded guard is
|
||||
* needed. */
|
||||
(()=>{if(!document.querySelector(`.calepin-website-topbar, .calepin-website-main, pagefind-modal, img[data-inline-svg], [data-video-dialog], [data-lightbox-dialog]`))return;let VIEW={param:`view`,rendered:`rendered`,source:`source`,pdf:`pdf`},MOBILE_QUERY=`(max-width: 56rem)`,SOURCE_DATA_ID=`calepin-website-source-data`,isMobile=()=>window.matchMedia(`(max-width: 56rem)`).matches,isPlainLeftClick=event=>event&&event.button===0&&!event.metaKey&&!event.ctrlKey&&!event.shiftKey&&!event.altKey,isInternalPageHref=href=>!!href&&!href.startsWith(`#`)&&!href.startsWith(`http://`)&&!href.startsWith(`https://`)&&!href.startsWith(`//`)&&!href.startsWith(`mailto:`)&&!href.startsWith(`tel:`);function setUrlParam(url,name,value){value?url.searchParams.set(name,value):url.searchParams.delete(name)}function readUrlParam(name,normalize,fallback=``){try{return normalize((new URL(window.location.href).searchParams.get(name)||``).toLowerCase())}catch{return fallback}}function updateCurrentUrlParam(name,value){try{let url=new URL(window.location.href);setUrlParam(url,name,value),window.history.replaceState({},``,url.toString())}catch{}}let getMain=()=>document.querySelector(`.calepin-website-main`),normalizeView=value=>value===VIEW.source||value===VIEW.pdf?value:VIEW.rendered;function currentView(selects){let select=Array.isArray(selects)?selects[0]:selects;return normalizeView(select&&select.value||VIEW.rendered)}function pageAssetCandidates(extension){let{pathname}=window.location;return pathname.endsWith(`/`)?[`${pathname}index.${extension}`]:pathname.endsWith(`.html`)?[`${pathname.slice(0,-5)}.${extension}`]:!pathname||pathname.endsWith(`.`)?[`index.${extension}`]:pathname.includes(`.`)?[`${pathname}.${extension}`]:[`${pathname}/index.${extension}`]}function replaceMainContent(node){let main=getMain();return main?(main.innerHTML=``,main.appendChild(node),!0):!1}function showMainFallback(message){let main=getMain();if(!main)return;let fallback=document.createElement(`p`);fallback.textContent=message,main.prepend(fallback)}function renderSourceText(text){let container=document.createElement(`div`);container.className=`calepin-website-source-view`;let pre=document.createElement(`pre`),code=document.createElement(`code`);code.className=`language-typ`,code.textContent=text,pre.appendChild(code),container.appendChild(pre),replaceMainContent(container)}async function renderSourceMode(){let element=document.getElementById(`calepin-website-source-data`);if(element)try{let inline=JSON.parse(element.textContent||``);if(typeof inline==`string`&&inline.length>0){renderSourceText(inline);return}}catch{}for(let sourcePath of pageAssetCandidates(`typ`))try{let response=await fetch(sourcePath);if(!response.ok)continue;renderSourceText(await response.text());return}catch{}showMainFallback(`Could not load source view.`)}async function renderPdfMode(){let isLocal=window.location.protocol===`file:`;for(let pdfPath of pageAssetCandidates(`pdf`)){if(!isLocal)try{let response=await fetch(pdfPath,{method:`HEAD`});if(!response.ok&&response.status!==405)continue}catch{}let viewer=document.createElement(`iframe`);viewer.className=`calepin-website-pdf-viewer`;try{viewer.src=new URL(pdfPath,window.location.href).toString()}catch{viewer.src=pdfPath}viewer.setAttribute(`title`,`Page PDF view`),viewer.setAttribute(`loading`,`lazy`),replaceMainContent(viewer);return}showMainFallback(`Could not load PDF view.`)}function applyView(mode,selects){let value=normalizeView(mode);(Array.isArray(selects)?selects:[selects]).filter(Boolean).forEach(select=>{select.value=value}),updateCurrentUrlParam(VIEW.param,value===VIEW.rendered?``:value),value===VIEW.source?renderSourceMode():value===VIEW.pdf&&renderPdfMode()}function initView(selects){let viewSelects=(Array.isArray(selects)?selects:[selects]).filter(Boolean);applyView(readUrlParam(VIEW.param,normalizeView,VIEW.rendered),viewSelects),viewSelects.forEach(select=>{select.dataset.calepinBound||(select.dataset.calepinBound=`true`,select.addEventListener(`change`,event=>{let value=normalizeView(event.target.value),url=new URL(window.location.href);setUrlParam(url,VIEW.param,value===VIEW.rendered?``:value),window.location.href=url.toString()}))})}
|
||||
// ----------------------------------------------------------- nav drawer
|
||||
function createNav(){let shell=document.querySelector(`.calepin-website-shell`),backdrop=document.querySelector(`.calepin-website-nav-backdrop`),toggle=document.querySelector(`.calepin-website-nav-toggle`);function setOpen(isOpen){!shell||!toggle||(shell.classList.toggle(`nav-open`,isOpen),document.body.classList.toggle(`calepin-website-nav-open`,isOpen),toggle.setAttribute(`aria-expanded`,String(isOpen)),backdrop&&backdrop.setAttribute(`aria-hidden`,String(!isOpen)))}let close=()=>setOpen(!1);return toggle&&toggle.addEventListener(`click`,event=>{event.preventDefault(),isMobile()&&shell&&setOpen(!shell.classList.contains(`nav-open`))}),backdrop?.addEventListener(`click`,close),document.addEventListener(`keydown`,event=>{event.key===`Escape`&&close()}),window.addEventListener(`resize`,()=>{isMobile()||close()}),{close}}
|
||||
// ------------------------------------------------------ sidebar sections
|
||||
// Only one section is open at a time, but "section" means siblings: opening a
|
||||
// nested subsection must not fold the parent it lives in, and must only fold
|
||||
// its own siblings.
|
||||
function initSidebarSections(){let sections=Array.from(document.querySelectorAll(`.calepin-website-sidebar-section`));sections.length<2||sections.forEach(section=>{section.addEventListener(`toggle`,()=>{section.open&§ions.forEach(other=>{other!==section&&other.parentElement===section.parentElement&&other.removeAttribute(`open`)})})})}
|
||||
// ------------------------------------------------------------- inline svg
|
||||
function svgTextFromSource(src){if(!src)return Promise.resolve(``);let prefix=`data:image/svg+xml`;if(src.startsWith(prefix)){let payload=src.slice(src.indexOf(`,`)+1);try{return Promise.resolve(src.startsWith(`${prefix};base64,`)?atob(payload):decodeURIComponent(payload))}catch{return Promise.resolve(payload)}}return fetch(src).then(response=>response.text()).catch(()=>``)}function inlineSvgImage(image){return svgTextFromSource(image.getAttribute(`src`)).then(text=>{if(!text)return;let svg=new DOMParser().parseFromString(text,`image/svg+xml`).querySelector(`svg`);if(!svg)return;svg.removeAttribute(`xmlns:xlink`),svg.removeAttribute(`width`),svg.removeAttribute(`height`),image.className&&svg.setAttribute(`class`,image.className),image.id&&svg.setAttribute(`id`,image.id);let style=image.getAttribute(`style`);style&&svg.setAttribute(`style`,style);let alt=image.getAttribute(`alt`);alt&&(svg.setAttribute(`aria-label`,alt),svg.setAttribute(`role`,`img`));let ariaHidden=image.getAttribute(`aria-hidden`);ariaHidden!==null&&svg.setAttribute(`aria-hidden`,ariaHidden),image.replaceWith(svg)})}function inlineSvgs(){document.querySelectorAll(`img[data-inline-svg]`).forEach(image=>inlineSvgImage(image))}
|
||||
// --------------------------------------------------------- link state
|
||||
function urlWithState(href,viewSelect){if(!isInternalPageHref(href))return href;try{let url=new URL(href,window.location.href),view=currentView(viewSelect);return setUrlParam(url,VIEW.param,view===VIEW.rendered?``:view),url.toString()}catch{return href}}function preserveStateInLinks(viewSelect){document.querySelectorAll(`a[href]`).forEach(link=>{let href=link.getAttribute(`href`);if(!isInternalPageHref(href))return;let next=urlWithState(href,viewSelect);next&&next!==href&&link.setAttribute(`href`,next)})}function initLinkInterception(nav,viewSelect){document.addEventListener(`click`,event=>{let tocLink=event.target.closest(`.calepin-website-toc a`);if(tocLink){let href=tocLink.getAttribute(`href`);if(href&&href.startsWith(`#`)){let target=null;try{target=document.querySelector(href)}catch{target=null}target&&target.scrollIntoView({behavior:`smooth`,block:`start`})}isMobile()&&nav.close();return}let link=event.target.closest(`a`);if(!link||!isPlainLeftClick(event))return;event.target.closest(`.calepin-website-sidebar a`)&&isMobile()&&nav.close();let href=link.getAttribute(`href`),next=urlWithState(href,viewSelect);next&&next!==href&&(event.preventDefault(),window.location.href=next)})}
|
||||
// ----------------------------------------------------- dialog lightbox
|
||||
function initDialogs(){document.querySelectorAll(`[data-video-dialog], [data-lightbox-dialog]`).forEach(trigger=>{let dialogId=trigger.dataset.videoDialog||trigger.dataset.lightboxDialog,dialog=document.getElementById(dialogId);!dialog||typeof dialog.showModal!=`function`||trigger.addEventListener(`click`,event=>{event.preventDefault(),dialog.showModal()})}),document.querySelectorAll(`dialog.calepin-video-dialog, dialog.calepin-screenshot-dialog`).forEach(dialog=>{let stop=()=>{let video=dialog.querySelector(`video`);video&&video.pause()};dialog.addEventListener(`click`,event=>{
|
||||
// Clicking the dialog backdrop (the dialog element itself) closes it.
|
||||
event.target===dialog&&dialog.close()}),dialog.querySelectorAll(`[data-close-dialog]`).forEach(button=>{button.addEventListener(`click`,()=>dialog.close())}),dialog.addEventListener(`close`,stop)})}
|
||||
// --------------------------------------------------------------- bootstrap
|
||||
let viewSelects=Array.from(document.querySelectorAll(`[data-calepin-view-mode], #calepin-website-view-mode`));initView(viewSelects);let nav=createNav();initSidebarSections(),inlineSvgs(),initDialogs(),preserveStateInLinks(viewSelects),initLinkInterception(nav,viewSelects),window.addEventListener(`pageshow`,event=>{event.persisted&&(initView(viewSelects),preserveStateInLinks(viewSelects))})})();</script>
|
||||
@@ -1,29 +0,0 @@
|
||||
<!doctype html><html lang=en><head><meta charset=utf-8><meta content="width=device-width,initial-scale=1" name=viewport><title>Gardening</title><meta content="Some plants description and advices for keep them alive" name=description><meta content="Some plants description and advices for keep them alive" property=og:description><meta content="Some plants description and advices for keep them alive" name=twitter:description><meta content=Yggdrasil property=og:site_name><meta content="Gardening | Yggdrasil" property=og:title><meta content="Gardening | Yggdrasil" name=twitter:title><meta content=article property=og:type><meta content=summary name=twitter:card><link href=https://yggdrasil.kb28.ch/home/gardening.html rel=canonical><meta content=https://yggdrasil.kb28.ch/home/gardening.html property=og:url><link href=../assets/yggdrasil.ico rel=icon><meta content="light dark" name=color-scheme><script data-calepin-theme-param=theme data-calepin-theme-storage-key=calepin-website-theme>(()=>{let currentScript=document.currentScript,storageKey=currentScript?.dataset?.calepinThemeStorageKey||`calepin-website-theme`,param=currentScript?.dataset?.calepinThemeParam||`theme`,normalize=value=>value===`light`||value===`dark`?value:``,root=document.documentElement,media=window.matchMedia?window.matchMedia(`(prefers-color-scheme: dark)`):null,systemTheme=()=>media&&media.matches?`dark`:`light`,mode=``;if(param)try{mode=normalize(new URL(window.location.href).searchParams.get(param)||``)}catch{mode=``}if(!mode)try{mode=normalize(localStorage.getItem(storageKey)||``)}catch{mode=``}let theme=mode||systemTheme();root.dataset.calepinThemeMode=mode,root.dataset.theme=theme,root.style.colorScheme=theme})();</script><style>:host,:root{--pico-font-family-emoji:"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--pico-font-family-sans-serif:system-ui,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,Helvetica,Arial,"Helvetica Neue",sans-serif,var(--pico-font-family-emoji);--pico-font-family-monospace:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,"Liberation Mono",monospace,var(--pico-font-family-emoji);--pico-font-family:var(--pico-font-family-sans-serif);--pico-line-height:1.5;--pico-font-weight:400;--pico-font-size:100%;--pico-text-underline-offset:.1rem;--pico-border-radius:.25rem;--pico-border-width:.0625rem;--pico-outline-width:.125rem;--pico-transition:.2s ease-in-out;--pico-spacing:1rem;--pico-typography-spacing-vertical:1rem;--pico-block-spacing-vertical:var(--pico-spacing);--pico-block-spacing-horizontal:var(--pico-spacing);--pico-grid-column-gap:var(--pico-spacing);--pico-grid-row-gap:var(--pico-spacing);--pico-form-element-spacing-vertical:.75rem;--pico-form-element-spacing-horizontal:1rem;--pico-group-box-shadow:0 0 0 #0000;--pico-group-box-shadow-focus-with-button:0 0 0 var(--pico-outline-width) var(--pico-primary-focus);--pico-group-box-shadow-focus-with-input:0 0 0 .0625rem var(--pico-form-element-border-color);--pico-modal-overlay-backdrop-filter:blur(.375rem);--pico-nav-element-spacing-vertical:1rem;--pico-nav-element-spacing-horizontal:.5rem;--pico-nav-link-spacing-vertical:.5rem;--pico-nav-link-spacing-horizontal:.5rem;--pico-nav-breadcrumb-divider:">";--pico-icon-checkbox:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-minus:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E");--pico-icon-chevron:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-date:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E");--pico-icon-time:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline points='12 6 12 12 16 14'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-search:url("data:image/svg+xml,%3Csvg xmlns='httLine truncated
|
||||
`:Array.from(node.childNodes).map(codeText).join(``):``:``}document.querySelectorAll(selector).forEach(block=>{if(block.dataset.calepinCopyBound===`true`||block.querySelector(`:scope > .${buttonClass}`))return;block.dataset.calepinCopyBound=`true`;let button=document.createElement(`button`);button.type=`button`,button.className=buttonClass,button.setAttribute(`aria-label`,`Copy code`),button.setAttribute(`title`,`Copy code`),button.innerHTML=icon;let restoreTimeout=null;button.addEventListener(`click`,async()=>{restoreTimeout!==null&&(window.clearTimeout(restoreTimeout),restoreTimeout=null);let text=codeText(block.querySelector(`pre code, code, pre`));try{await copyText(text),button.classList.add(copiedClass),button.innerHTML=copiedIcon,restoreTimeout=window.setTimeout(()=>{button.classList.remove(copiedClass),button.innerHTML=icon,restoreTimeout=null},900)}catch{button.classList.remove(copiedClass),button.innerHTML=icon}}),block.prepend(button)})})();</script><script>(()=>{let pagefindModal=()=>document.querySelector(`pagefind-modal`);function modalSearchInput(modal){return modal?modal.querySelector(`pagefind-input input, input[type='search'], input`):null}function setModalSearchQuery(modal,query){if(!query)return!1;let input=modalSearchInput(modal);return input?(input.value=query,input.dispatchEvent(new Event(`input`,{bubbles:!0})),!0):!1}function focusModalSearch(modal){let input=modalSearchInput(modal);return input?(input.focus(),!0):!1}async function openPagefindSearch(query){let modal=pagefindModal();if(!modal)return!1;if(typeof modal.open!=`function`&&window.customElements?.whenDefined)try{await window.customElements.whenDefined(`pagefind-modal`)}catch{}if(typeof modal.open!=`function`)return!1;modal.open();let sync=()=>{let hasQuery=setModalSearchQuery(modal,query),hasFocus=focusModalSearch(modal);return hasQuery||hasFocus};return sync()||requestAnimationFrame(()=>{sync()||window.setTimeout(sync,50)}),!0}function isTextEntryElement(element){return element?element.isContentEditable||element.tagName===`INPUT`||element.tagName===`SELECT`||element.tagName===`TEXTAREA`:!1}let SEARCH_SUGGESTION_LIMIT=20,SEARCH_SUBRESULT_LIMIT=3,SEARCH_SUGGESTION_MIN_LENGTH=2,SEARCH_SUGGESTION_DELAY=160,pagefindModulePromise=null;function pagefindBundlePath(){let script=document.querySelector(`script[src$="pagefind-component-ui.js"], script[src$="pagefind-ui.js"]`);return script?.src?new URL(`./`,script.src).toString():new URL(`pagefind/`,window.location.href).toString()}function loadPagefindModule(){return pagefindModulePromise||=import(`${pagefindBundlePath()}pagefind.js`),pagefindModulePromise}function plainTextFromHtml(html){let element=document.createElement(`div`);return element.innerHTML=html||``,element.textContent?.trim()||``}function setSearchResultsOpen(input,results,isOpen){results.hidden=!isOpen,input.setAttribute(`aria-expanded`,String(isOpen))}function clearSearchResults(input,results){results.innerHTML=``,setSearchResultsOpen(input,results,!1)}function renderSearchMessage(input,results,message){results.innerHTML=``;let item=document.createElement(`p`);item.className=`calepin-website-search-empty`,item.textContent=message,results.appendChild(item),setSearchResultsOpen(input,results,!0)}function renderSearchResults(input,results,items){if(results.innerHTML=``,!items.length){renderSearchMessage(input,results,`No results`);return}let basePathPrefix=localCanonicalBasePathPrefix(),addSearchLink=(item,className,titleClassName,excerptClassName)=>{let href=hrefWithoutLocalBasePath(item.meta?.url||item.url||``,basePathPrefix);if(!href)return null;let link=document.createElement(`a`);link.className=className,link.href=href,link.setAttribute(`role`,`option`);let title=document.createElement(`span`);title.className=titleClassName,title.textContent=item.meta?.title||item.title||href,link.appendChild(title);let excerptText=plainTextFromHtml(item.excerpt||item.content||``);if(excerptText){let excerpt=document.createElement(`span`);excerpt.className=excerptClassName,excerpt.textContent=excerptText,link.appendChild(excerpt)}return link};for(let item of items){let link=addSearchLink(item,`calepin-website-search-result`,`calepin-website-search-result-title`,`calepin-website-search-result-excerpt`);if(!link)continue;results.appendChild(link);let subResults=Array.isArray(item.sub_results)?item.sub_results.slice(0,3):[];if(!subResults.length)continue;let nested=document.createElement(`div`);nested.className=`calepin-website-search-subresults`;for(let subResult of subResults){let subLink=addSearchLink(subResult,`calepin-website-search-subresult`,`calepin-website-search-subresult-title`,`calepin-website-search-subresult-excerpt`);subLink&&nested.appendChild(subLink)}nested.children.length&&results.appendChild(nested)}setSearchResultsOpen(input,results,!!results.children.length)}function initMobileSearchTriggers(){document.querySelectorAll(`[data-calepin-mobile-search-trigger]`).forEach(button=>{button.dataset.calepinSearchBound!==Line truncated
|
||||
*
|
||||
* One module-local script wires: view switcher (HTML/Source/PDF), the mobile
|
||||
* sidebar drawer, sidebar section folding, inline SVGs, internal link state
|
||||
* preservation, the native <dialog> video lightbox, and the Pagefind navbar
|
||||
* search bridge.
|
||||
*
|
||||
* The Rust theme loader links this file near the end of the page body, so
|
||||
* the DOM already exists when it runs and no DOMContentLoaded guard is
|
||||
* needed. */
|
||||
(()=>{if(!document.querySelector(`.calepin-website-topbar, .calepin-website-main, pagefind-modal, img[data-inline-svg], [data-video-dialog], [data-lightbox-dialog]`))return;let VIEW={param:`view`,rendered:`rendered`,source:`source`,pdf:`pdf`},MOBILE_QUERY=`(max-width: 56rem)`,SOURCE_DATA_ID=`calepin-website-source-data`,isMobile=()=>window.matchMedia(`(max-width: 56rem)`).matches,isPlainLeftClick=event=>event&&event.button===0&&!event.metaKey&&!event.ctrlKey&&!event.shiftKey&&!event.altKey,isInternalPageHref=href=>!!href&&!href.startsWith(`#`)&&!href.startsWith(`http://`)&&!href.startsWith(`https://`)&&!href.startsWith(`//`)&&!href.startsWith(`mailto:`)&&!href.startsWith(`tel:`);function setUrlParam(url,name,value){value?url.searchParams.set(name,value):url.searchParams.delete(name)}function readUrlParam(name,normalize,fallback=``){try{return normalize((new URL(window.location.href).searchParams.get(name)||``).toLowerCase())}catch{return fallback}}function updateCurrentUrlParam(name,value){try{let url=new URL(window.location.href);setUrlParam(url,name,value),window.history.replaceState({},``,url.toString())}catch{}}let getMain=()=>document.querySelector(`.calepin-website-main`),normalizeView=value=>value===VIEW.source||value===VIEW.pdf?value:VIEW.rendered;function currentView(selects){let select=Array.isArray(selects)?selects[0]:selects;return normalizeView(select&&select.value||VIEW.rendered)}function pageAssetCandidates(extension){let{pathname}=window.location;return pathname.endsWith(`/`)?[`${pathname}index.${extension}`]:pathname.endsWith(`.html`)?[`${pathname.slice(0,-5)}.${extension}`]:!pathname||pathname.endsWith(`.`)?[`index.${extension}`]:pathname.includes(`.`)?[`${pathname}.${extension}`]:[`${pathname}/index.${extension}`]}function replaceMainContent(node){let main=getMain();return main?(main.innerHTML=``,main.appendChild(node),!0):!1}function showMainFallback(message){let main=getMain();if(!main)return;let fallback=document.createElement(`p`);fallback.textContent=message,main.prepend(fallback)}function renderSourceText(text){let container=document.createElement(`div`);container.className=`calepin-website-source-view`;let pre=document.createElement(`pre`),code=document.createElement(`code`);code.className=`language-typ`,code.textContent=text,pre.appendChild(code),container.appendChild(pre),replaceMainContent(container)}async function renderSourceMode(){let element=document.getElementById(`calepin-website-source-data`);if(element)try{let inline=JSON.parse(element.textContent||``);if(typeof inline==`string`&&inline.length>0){renderSourceText(inline);return}}catch{}for(let sourcePath of pageAssetCandidates(`typ`))try{let response=await fetch(sourcePath);if(!response.ok)continue;renderSourceText(await response.text());return}catch{}showMainFallback(`Could not load source view.`)}async function renderPdfMode(){let isLocal=window.location.protocol===`file:`;for(let pdfPath of pageAssetCandidates(`pdf`)){if(!isLocal)try{let response=await fetch(pdfPath,{method:`HEAD`});if(!response.ok&&response.status!==405)continue}catch{}let viewer=document.createElement(`iframe`);viewer.className=`calepin-website-pdf-viewer`;try{viewer.src=new URL(pdfPath,window.location.href).toString()}catch{viewer.src=pdfPath}viewer.setAttribute(`title`,`Page PDF view`),viewer.setAttribute(`loading`,`lazy`),replaceMainContent(viewer);return}showMainFallback(`Could not load PDF view.`)}function applyView(mode,selects){let value=normalizeView(mode);(Array.isArray(selects)?selects:[selects]).filter(Boolean).forEach(select=>{select.value=value}),updateCurrentUrlParam(VIEW.param,value===VIEW.rendered?``:value),value===VIEW.source?renderSourceMode():value===VIEW.pdf&&renderPdfMode()}function initView(selects){let viewSelects=(Array.isArray(selects)?selects:[selects]).filter(Boolean);applyView(readUrlParam(VIEW.param,normalizeView,VIEW.rendered),viewSelects),viewSelects.forEach(select=>{select.dataset.calepinBound||(select.dataset.calepinBound=`true`,select.addEventListener(`change`,event=>{let value=normalizeView(event.target.value),url=new URL(window.location.href);setUrlParam(url,VIEW.param,value===VIEW.rendered?``:value),window.location.href=url.toString()}))})}
|
||||
// ----------------------------------------------------------- nav drawer
|
||||
function createNav(){let shell=document.querySelector(`.calepin-website-shell`),backdrop=document.querySelector(`.calepin-website-nav-backdrop`),toggle=document.querySelector(`.calepin-website-nav-toggle`);function setOpen(isOpen){!shell||!toggle||(shell.classList.toggle(`nav-open`,isOpen),document.body.classList.toggle(`calepin-website-nav-open`,isOpen),toggle.setAttribute(`aria-expanded`,String(isOpen)),backdrop&&backdrop.setAttribute(`aria-hidden`,String(!isOpen)))}let close=()=>setOpen(!1);return toggle&&toggle.addEventListener(`click`,event=>{event.preventDefault(),isMobile()&&shell&&setOpen(!shell.classList.contains(`nav-open`))}),backdrop?.addEventListener(`click`,close),document.addEventListener(`keydown`,event=>{event.key===`Escape`&&close()}),window.addEventListener(`resize`,()=>{isMobile()||close()}),{close}}
|
||||
// ------------------------------------------------------ sidebar sections
|
||||
// Only one section is open at a time, but "section" means siblings: opening a
|
||||
// nested subsection must not fold the parent it lives in, and must only fold
|
||||
// its own siblings.
|
||||
function initSidebarSections(){let sections=Array.from(document.querySelectorAll(`.calepin-website-sidebar-section`));sections.length<2||sections.forEach(section=>{section.addEventListener(`toggle`,()=>{section.open&§ions.forEach(other=>{other!==section&&other.parentElement===section.parentElement&&other.removeAttribute(`open`)})})})}
|
||||
// ------------------------------------------------------------- inline svg
|
||||
function svgTextFromSource(src){if(!src)return Promise.resolve(``);let prefix=`data:image/svg+xml`;if(src.startsWith(prefix)){let payload=src.slice(src.indexOf(`,`)+1);try{return Promise.resolve(src.startsWith(`${prefix};base64,`)?atob(payload):decodeURIComponent(payload))}catch{return Promise.resolve(payload)}}return fetch(src).then(response=>response.text()).catch(()=>``)}function inlineSvgImage(image){return svgTextFromSource(image.getAttribute(`src`)).then(text=>{if(!text)return;let svg=new DOMParser().parseFromString(text,`image/svg+xml`).querySelector(`svg`);if(!svg)return;svg.removeAttribute(`xmlns:xlink`),svg.removeAttribute(`width`),svg.removeAttribute(`height`),image.className&&svg.setAttribute(`class`,image.className),image.id&&svg.setAttribute(`id`,image.id);let style=image.getAttribute(`style`);style&&svg.setAttribute(`style`,style);let alt=image.getAttribute(`alt`);alt&&(svg.setAttribute(`aria-label`,alt),svg.setAttribute(`role`,`img`));let ariaHidden=image.getAttribute(`aria-hidden`);ariaHidden!==null&&svg.setAttribute(`aria-hidden`,ariaHidden),image.replaceWith(svg)})}function inlineSvgs(){document.querySelectorAll(`img[data-inline-svg]`).forEach(image=>inlineSvgImage(image))}
|
||||
// --------------------------------------------------------- link state
|
||||
function urlWithState(href,viewSelect){if(!isInternalPageHref(href))return href;try{let url=new URL(href,window.location.href),view=currentView(viewSelect);return setUrlParam(url,VIEW.param,view===VIEW.rendered?``:view),url.toString()}catch{return href}}function preserveStateInLinks(viewSelect){document.querySelectorAll(`a[href]`).forEach(link=>{let href=link.getAttribute(`href`);if(!isInternalPageHref(href))return;let next=urlWithState(href,viewSelect);next&&next!==href&&link.setAttribute(`href`,next)})}function initLinkInterception(nav,viewSelect){document.addEventListener(`click`,event=>{let tocLink=event.target.closest(`.calepin-website-toc a`);if(tocLink){let href=tocLink.getAttribute(`href`);if(href&&href.startsWith(`#`)){let target=null;try{target=document.querySelector(href)}catch{target=null}target&&target.scrollIntoView({behavior:`smooth`,block:`start`})}isMobile()&&nav.close();return}let link=event.target.closest(`a`);if(!link||!isPlainLeftClick(event))return;event.target.closest(`.calepin-website-sidebar a`)&&isMobile()&&nav.close();let href=link.getAttribute(`href`),next=urlWithState(href,viewSelect);next&&next!==href&&(event.preventDefault(),window.location.href=next)})}
|
||||
// ----------------------------------------------------- dialog lightbox
|
||||
function initDialogs(){document.querySelectorAll(`[data-video-dialog], [data-lightbox-dialog]`).forEach(trigger=>{let dialogId=trigger.dataset.videoDialog||trigger.dataset.lightboxDialog,dialog=document.getElementById(dialogId);!dialog||typeof dialog.showModal!=`function`||trigger.addEventListener(`click`,event=>{event.preventDefault(),dialog.showModal()})}),document.querySelectorAll(`dialog.calepin-video-dialog, dialog.calepin-screenshot-dialog`).forEach(dialog=>{let stop=()=>{let video=dialog.querySelector(`video`);video&&video.pause()};dialog.addEventListener(`click`,event=>{
|
||||
// Clicking the dialog backdrop (the dialog element itself) closes it.
|
||||
event.target===dialog&&dialog.close()}),dialog.querySelectorAll(`[data-close-dialog]`).forEach(button=>{button.addEventListener(`click`,()=>dialog.close())}),dialog.addEventListener(`close`,stop)})}
|
||||
// --------------------------------------------------------------- bootstrap
|
||||
let viewSelects=Array.from(document.querySelectorAll(`[data-calepin-view-mode], #calepin-website-view-mode`));initView(viewSelects);let nav=createNav();initSidebarSections(),inlineSvgs(),initDialogs(),preserveStateInLinks(viewSelects),initLinkInterception(nav,viewSelects),window.addEventListener(`pageshow`,event=>{event.persisted&&(initView(viewSelects),preserveStateInLinks(viewSelects))})})();</script>
|
||||
@@ -1,29 +0,0 @@
|
||||
<!doctype html><html lang=en><head><meta charset=utf-8><meta content="width=device-width,initial-scale=1" name=viewport><title>Home</title><meta content="Placeholder page for the home category: cooking, washing clothes, gardening. The real content comes later." name=description><meta content="Placeholder page for the home category: cooking, washing clothes, gardening. The real content comes later." property=og:description><meta content="Placeholder page for the home category: cooking, washing clothes, gardening. The real content comes later." name=twitter:description><meta content=Yggdrasil property=og:site_name><meta content="Home | Yggdrasil" property=og:title><meta content="Home | Yggdrasil" name=twitter:title><meta content=article property=og:type><meta content=summary name=twitter:card><link href=https://yggdrasil.kb28.ch/home/ rel=canonical><meta content=https://yggdrasil.kb28.ch/home/ property=og:url><link href=../assets/yggdrasil.ico rel=icon><meta content="light dark" name=color-scheme><script data-calepin-theme-param=theme data-calepin-theme-storage-key=calepin-website-theme>(()=>{let currentScript=document.currentScript,storageKey=currentScript?.dataset?.calepinThemeStorageKey||`calepin-website-theme`,param=currentScript?.dataset?.calepinThemeParam||`theme`,normalize=value=>value===`light`||value===`dark`?value:``,root=document.documentElement,media=window.matchMedia?window.matchMedia(`(prefers-color-scheme: dark)`):null,systemTheme=()=>media&&media.matches?`dark`:`light`,mode=``;if(param)try{mode=normalize(new URL(window.location.href).searchParams.get(param)||``)}catch{mode=``}if(!mode)try{mode=normalize(localStorage.getItem(storageKey)||``)}catch{mode=``}let theme=mode||systemTheme();root.dataset.calepinThemeMode=mode,root.dataset.theme=theme,root.style.colorScheme=theme})();</script><style>:host,:root{--pico-font-family-emoji:"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--pico-font-family-sans-serif:system-ui,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,Helvetica,Arial,"Helvetica Neue",sans-serif,var(--pico-font-family-emoji);--pico-font-family-monospace:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,"Liberation Mono",monospace,var(--pico-font-family-emoji);--pico-font-family:var(--pico-font-family-sans-serif);--pico-line-height:1.5;--pico-font-weight:400;--pico-font-size:100%;--pico-text-underline-offset:.1rem;--pico-border-radius:.25rem;--pico-border-width:.0625rem;--pico-outline-width:.125rem;--pico-transition:.2s ease-in-out;--pico-spacing:1rem;--pico-typography-spacing-vertical:1rem;--pico-block-spacing-vertical:var(--pico-spacing);--pico-block-spacing-horizontal:var(--pico-spacing);--pico-grid-column-gap:var(--pico-spacing);--pico-grid-row-gap:var(--pico-spacing);--pico-form-element-spacing-vertical:.75rem;--pico-form-element-spacing-horizontal:1rem;--pico-group-box-shadow:0 0 0 #0000;--pico-group-box-shadow-focus-with-button:0 0 0 var(--pico-outline-width) var(--pico-primary-focus);--pico-group-box-shadow-focus-with-input:0 0 0 .0625rem var(--pico-form-element-border-color);--pico-modal-overlay-backdrop-filter:blur(.375rem);--pico-nav-element-spacing-vertical:1rem;--pico-nav-element-spacing-horizontal:.5rem;--pico-nav-link-spacing-vertical:.5rem;--pico-nav-link-spacing-horizontal:.5rem;--pico-nav-breadcrumb-divider:">";--pico-icon-checkbox:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-minus:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E");--pico-icon-chevron:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-date:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E");--pico-icon-time:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline pointsLine truncated
|
||||
`:Array.from(node.childNodes).map(codeText).join(``):``:``}document.querySelectorAll(selector).forEach(block=>{if(block.dataset.calepinCopyBound===`true`||block.querySelector(`:scope > .${buttonClass}`))return;block.dataset.calepinCopyBound=`true`;let button=document.createElement(`button`);button.type=`button`,button.className=buttonClass,button.setAttribute(`aria-label`,`Copy code`),button.setAttribute(`title`,`Copy code`),button.innerHTML=icon;let restoreTimeout=null;button.addEventListener(`click`,async()=>{restoreTimeout!==null&&(window.clearTimeout(restoreTimeout),restoreTimeout=null);let text=codeText(block.querySelector(`pre code, code, pre`));try{await copyText(text),button.classList.add(copiedClass),button.innerHTML=copiedIcon,restoreTimeout=window.setTimeout(()=>{button.classList.remove(copiedClass),button.innerHTML=icon,restoreTimeout=null},900)}catch{button.classList.remove(copiedClass),button.innerHTML=icon}}),block.prepend(button)})})();</script><script>(()=>{let pagefindModal=()=>document.querySelector(`pagefind-modal`);function modalSearchInput(modal){return modal?modal.querySelector(`pagefind-input input, input[type='search'], input`):null}function setModalSearchQuery(modal,query){if(!query)return!1;let input=modalSearchInput(modal);return input?(input.value=query,input.dispatchEvent(new Event(`input`,{bubbles:!0})),!0):!1}function focusModalSearch(modal){let input=modalSearchInput(modal);return input?(input.focus(),!0):!1}async function openPagefindSearch(query){let modal=pagefindModal();if(!modal)return!1;if(typeof modal.open!=`function`&&window.customElements?.whenDefined)try{await window.customElements.whenDefined(`pagefind-modal`)}catch{}if(typeof modal.open!=`function`)return!1;modal.open();let sync=()=>{let hasQuery=setModalSearchQuery(modal,query),hasFocus=focusModalSearch(modal);return hasQuery||hasFocus};return sync()||requestAnimationFrame(()=>{sync()||window.setTimeout(sync,50)}),!0}function isTextEntryElement(element){return element?element.isContentEditable||element.tagName===`INPUT`||element.tagName===`SELECT`||element.tagName===`TEXTAREA`:!1}let SEARCH_SUGGESTION_LIMIT=20,SEARCH_SUBRESULT_LIMIT=3,SEARCH_SUGGESTION_MIN_LENGTH=2,SEARCH_SUGGESTION_DELAY=160,pagefindModulePromise=null;function pagefindBundlePath(){let script=document.querySelector(`script[src$="pagefind-component-ui.js"], script[src$="pagefind-ui.js"]`);return script?.src?new URL(`./`,script.src).toString():new URL(`pagefind/`,window.location.href).toString()}function loadPagefindModule(){return pagefindModulePromise||=import(`${pagefindBundlePath()}pagefind.js`),pagefindModulePromise}function plainTextFromHtml(html){let element=document.createElement(`div`);return element.innerHTML=html||``,element.textContent?.trim()||``}function setSearchResultsOpen(input,results,isOpen){results.hidden=!isOpen,input.setAttribute(`aria-expanded`,String(isOpen))}function clearSearchResults(input,results){results.innerHTML=``,setSearchResultsOpen(input,results,!1)}function renderSearchMessage(input,results,message){results.innerHTML=``;let item=document.createElement(`p`);item.className=`calepin-website-search-empty`,item.textContent=message,results.appendChild(item),setSearchResultsOpen(input,results,!0)}function renderSearchResults(input,results,items){if(results.innerHTML=``,!items.length){renderSearchMessage(input,results,`No results`);return}let basePathPrefix=localCanonicalBasePathPrefix(),addSearchLink=(item,className,titleClassName,excerptClassName)=>{let href=hrefWithoutLocalBasePath(item.meta?.url||item.url||``,basePathPrefix);if(!href)return null;let link=document.createElement(`a`);link.className=className,link.href=href,link.setAttribute(`role`,`option`);let title=document.createElement(`span`);title.className=titleClassName,title.textContent=item.meta?.title||item.title||href,link.appendChild(title);let excerptText=plainTextFromHtml(item.excerpt||item.content||``);if(excerptText){let excerpt=document.createElement(`span`);excerpt.className=excerptClassName,excerpt.textContent=excerptText,link.appendChild(excerpt)}return link};for(let item of items){let link=addSearchLink(item,`calepin-website-search-result`,`calepin-website-search-result-title`,`calepin-website-search-result-excerpt`);if(!link)continue;results.appendChild(link);let subResults=Array.isArray(item.sub_results)?item.sub_results.slice(0,3):[];if(!subResults.length)continue;let nested=document.createElement(`div`);nested.className=`calepin-website-search-subresults`;for(let subResult of subResults){let subLink=addSearchLink(subResult,`calepin-website-search-subresult`,`calepin-website-search-subresult-title`,`calepin-website-search-subresult-excerpt`);subLink&&nested.appendChild(subLink)}nested.children.length&&results.appendChild(nested)}setSearchResultsOpen(input,results,!!results.children.length)}function initMobileSearchTriggers(){document.querySelectorAll(`[data-calepin-mobile-search-trigger]`).forEach(button=>{button.dataset.calepinSearchBound!==Line truncated
|
||||
*
|
||||
* One module-local script wires: view switcher (HTML/Source/PDF), the mobile
|
||||
* sidebar drawer, sidebar section folding, inline SVGs, internal link state
|
||||
* preservation, the native <dialog> video lightbox, and the Pagefind navbar
|
||||
* search bridge.
|
||||
*
|
||||
* The Rust theme loader links this file near the end of the page body, so
|
||||
* the DOM already exists when it runs and no DOMContentLoaded guard is
|
||||
* needed. */
|
||||
(()=>{if(!document.querySelector(`.calepin-website-topbar, .calepin-website-main, pagefind-modal, img[data-inline-svg], [data-video-dialog], [data-lightbox-dialog]`))return;let VIEW={param:`view`,rendered:`rendered`,source:`source`,pdf:`pdf`},MOBILE_QUERY=`(max-width: 56rem)`,SOURCE_DATA_ID=`calepin-website-source-data`,isMobile=()=>window.matchMedia(`(max-width: 56rem)`).matches,isPlainLeftClick=event=>event&&event.button===0&&!event.metaKey&&!event.ctrlKey&&!event.shiftKey&&!event.altKey,isInternalPageHref=href=>!!href&&!href.startsWith(`#`)&&!href.startsWith(`http://`)&&!href.startsWith(`https://`)&&!href.startsWith(`//`)&&!href.startsWith(`mailto:`)&&!href.startsWith(`tel:`);function setUrlParam(url,name,value){value?url.searchParams.set(name,value):url.searchParams.delete(name)}function readUrlParam(name,normalize,fallback=``){try{return normalize((new URL(window.location.href).searchParams.get(name)||``).toLowerCase())}catch{return fallback}}function updateCurrentUrlParam(name,value){try{let url=new URL(window.location.href);setUrlParam(url,name,value),window.history.replaceState({},``,url.toString())}catch{}}let getMain=()=>document.querySelector(`.calepin-website-main`),normalizeView=value=>value===VIEW.source||value===VIEW.pdf?value:VIEW.rendered;function currentView(selects){let select=Array.isArray(selects)?selects[0]:selects;return normalizeView(select&&select.value||VIEW.rendered)}function pageAssetCandidates(extension){let{pathname}=window.location;return pathname.endsWith(`/`)?[`${pathname}index.${extension}`]:pathname.endsWith(`.html`)?[`${pathname.slice(0,-5)}.${extension}`]:!pathname||pathname.endsWith(`.`)?[`index.${extension}`]:pathname.includes(`.`)?[`${pathname}.${extension}`]:[`${pathname}/index.${extension}`]}function replaceMainContent(node){let main=getMain();return main?(main.innerHTML=``,main.appendChild(node),!0):!1}function showMainFallback(message){let main=getMain();if(!main)return;let fallback=document.createElement(`p`);fallback.textContent=message,main.prepend(fallback)}function renderSourceText(text){let container=document.createElement(`div`);container.className=`calepin-website-source-view`;let pre=document.createElement(`pre`),code=document.createElement(`code`);code.className=`language-typ`,code.textContent=text,pre.appendChild(code),container.appendChild(pre),replaceMainContent(container)}async function renderSourceMode(){let element=document.getElementById(`calepin-website-source-data`);if(element)try{let inline=JSON.parse(element.textContent||``);if(typeof inline==`string`&&inline.length>0){renderSourceText(inline);return}}catch{}for(let sourcePath of pageAssetCandidates(`typ`))try{let response=await fetch(sourcePath);if(!response.ok)continue;renderSourceText(await response.text());return}catch{}showMainFallback(`Could not load source view.`)}async function renderPdfMode(){let isLocal=window.location.protocol===`file:`;for(let pdfPath of pageAssetCandidates(`pdf`)){if(!isLocal)try{let response=await fetch(pdfPath,{method:`HEAD`});if(!response.ok&&response.status!==405)continue}catch{}let viewer=document.createElement(`iframe`);viewer.className=`calepin-website-pdf-viewer`;try{viewer.src=new URL(pdfPath,window.location.href).toString()}catch{viewer.src=pdfPath}viewer.setAttribute(`title`,`Page PDF view`),viewer.setAttribute(`loading`,`lazy`),replaceMainContent(viewer);return}showMainFallback(`Could not load PDF view.`)}function applyView(mode,selects){let value=normalizeView(mode);(Array.isArray(selects)?selects:[selects]).filter(Boolean).forEach(select=>{select.value=value}),updateCurrentUrlParam(VIEW.param,value===VIEW.rendered?``:value),value===VIEW.source?renderSourceMode():value===VIEW.pdf&&renderPdfMode()}function initView(selects){let viewSelects=(Array.isArray(selects)?selects:[selects]).filter(Boolean);applyView(readUrlParam(VIEW.param,normalizeView,VIEW.rendered),viewSelects),viewSelects.forEach(select=>{select.dataset.calepinBound||(select.dataset.calepinBound=`true`,select.addEventListener(`change`,event=>{let value=normalizeView(event.target.value),url=new URL(window.location.href);setUrlParam(url,VIEW.param,value===VIEW.rendered?``:value),window.location.href=url.toString()}))})}
|
||||
// ----------------------------------------------------------- nav drawer
|
||||
function createNav(){let shell=document.querySelector(`.calepin-website-shell`),backdrop=document.querySelector(`.calepin-website-nav-backdrop`),toggle=document.querySelector(`.calepin-website-nav-toggle`);function setOpen(isOpen){!shell||!toggle||(shell.classList.toggle(`nav-open`,isOpen),document.body.classList.toggle(`calepin-website-nav-open`,isOpen),toggle.setAttribute(`aria-expanded`,String(isOpen)),backdrop&&backdrop.setAttribute(`aria-hidden`,String(!isOpen)))}let close=()=>setOpen(!1);return toggle&&toggle.addEventListener(`click`,event=>{event.preventDefault(),isMobile()&&shell&&setOpen(!shell.classList.contains(`nav-open`))}),backdrop?.addEventListener(`click`,close),document.addEventListener(`keydown`,event=>{event.key===`Escape`&&close()}),window.addEventListener(`resize`,()=>{isMobile()||close()}),{close}}
|
||||
// ------------------------------------------------------ sidebar sections
|
||||
// Only one section is open at a time, but "section" means siblings: opening a
|
||||
// nested subsection must not fold the parent it lives in, and must only fold
|
||||
// its own siblings.
|
||||
function initSidebarSections(){let sections=Array.from(document.querySelectorAll(`.calepin-website-sidebar-section`));sections.length<2||sections.forEach(section=>{section.addEventListener(`toggle`,()=>{section.open&§ions.forEach(other=>{other!==section&&other.parentElement===section.parentElement&&other.removeAttribute(`open`)})})})}
|
||||
// ------------------------------------------------------------- inline svg
|
||||
function svgTextFromSource(src){if(!src)return Promise.resolve(``);let prefix=`data:image/svg+xml`;if(src.startsWith(prefix)){let payload=src.slice(src.indexOf(`,`)+1);try{return Promise.resolve(src.startsWith(`${prefix};base64,`)?atob(payload):decodeURIComponent(payload))}catch{return Promise.resolve(payload)}}return fetch(src).then(response=>response.text()).catch(()=>``)}function inlineSvgImage(image){return svgTextFromSource(image.getAttribute(`src`)).then(text=>{if(!text)return;let svg=new DOMParser().parseFromString(text,`image/svg+xml`).querySelector(`svg`);if(!svg)return;svg.removeAttribute(`xmlns:xlink`),svg.removeAttribute(`width`),svg.removeAttribute(`height`),image.className&&svg.setAttribute(`class`,image.className),image.id&&svg.setAttribute(`id`,image.id);let style=image.getAttribute(`style`);style&&svg.setAttribute(`style`,style);let alt=image.getAttribute(`alt`);alt&&(svg.setAttribute(`aria-label`,alt),svg.setAttribute(`role`,`img`));let ariaHidden=image.getAttribute(`aria-hidden`);ariaHidden!==null&&svg.setAttribute(`aria-hidden`,ariaHidden),image.replaceWith(svg)})}function inlineSvgs(){document.querySelectorAll(`img[data-inline-svg]`).forEach(image=>inlineSvgImage(image))}
|
||||
// --------------------------------------------------------- link state
|
||||
function urlWithState(href,viewSelect){if(!isInternalPageHref(href))return href;try{let url=new URL(href,window.location.href),view=currentView(viewSelect);return setUrlParam(url,VIEW.param,view===VIEW.rendered?``:view),url.toString()}catch{return href}}function preserveStateInLinks(viewSelect){document.querySelectorAll(`a[href]`).forEach(link=>{let href=link.getAttribute(`href`);if(!isInternalPageHref(href))return;let next=urlWithState(href,viewSelect);next&&next!==href&&link.setAttribute(`href`,next)})}function initLinkInterception(nav,viewSelect){document.addEventListener(`click`,event=>{let tocLink=event.target.closest(`.calepin-website-toc a`);if(tocLink){let href=tocLink.getAttribute(`href`);if(href&&href.startsWith(`#`)){let target=null;try{target=document.querySelector(href)}catch{target=null}target&&target.scrollIntoView({behavior:`smooth`,block:`start`})}isMobile()&&nav.close();return}let link=event.target.closest(`a`);if(!link||!isPlainLeftClick(event))return;event.target.closest(`.calepin-website-sidebar a`)&&isMobile()&&nav.close();let href=link.getAttribute(`href`),next=urlWithState(href,viewSelect);next&&next!==href&&(event.preventDefault(),window.location.href=next)})}
|
||||
// ----------------------------------------------------- dialog lightbox
|
||||
function initDialogs(){document.querySelectorAll(`[data-video-dialog], [data-lightbox-dialog]`).forEach(trigger=>{let dialogId=trigger.dataset.videoDialog||trigger.dataset.lightboxDialog,dialog=document.getElementById(dialogId);!dialog||typeof dialog.showModal!=`function`||trigger.addEventListener(`click`,event=>{event.preventDefault(),dialog.showModal()})}),document.querySelectorAll(`dialog.calepin-video-dialog, dialog.calepin-screenshot-dialog`).forEach(dialog=>{let stop=()=>{let video=dialog.querySelector(`video`);video&&video.pause()};dialog.addEventListener(`click`,event=>{
|
||||
// Clicking the dialog backdrop (the dialog element itself) closes it.
|
||||
event.target===dialog&&dialog.close()}),dialog.querySelectorAll(`[data-close-dialog]`).forEach(button=>{button.addEventListener(`click`,()=>dialog.close())}),dialog.addEventListener(`close`,stop)})}
|
||||
// --------------------------------------------------------------- bootstrap
|
||||
let viewSelects=Array.from(document.querySelectorAll(`[data-calepin-view-mode], #calepin-website-view-mode`));initView(viewSelects);let nav=createNav();initSidebarSections(),inlineSvgs(),initDialogs(),preserveStateInLinks(viewSelects),initLinkInterception(nav,viewSelects),window.addEventListener(`pageshow`,event=>{event.persisted&&(initView(viewSelects),preserveStateInLinks(viewSelects))})})();</script>
|
||||
@@ -1,29 +0,0 @@
|
||||
<!doctype html><html lang=en><head><meta charset=utf-8><meta content="width=device-width,initial-scale=1" name=viewport><title>Backup</title><meta content="Placeholder page — the real content comes later." name=description><meta content="Placeholder page — the real content comes later." property=og:description><meta content="Placeholder page — the real content comes later." name=twitter:description><meta content=Yggdrasil property=og:site_name><meta content="Backup | Yggdrasil" property=og:title><meta content="Backup | Yggdrasil" name=twitter:title><meta content=article property=og:type><meta content=summary name=twitter:card><link href=https://yggdrasil.kb28.ch/homelab/backup.html rel=canonical><meta content=https://yggdrasil.kb28.ch/homelab/backup.html property=og:url><link href=../assets/yggdrasil.ico rel=icon><meta content="light dark" name=color-scheme><script data-calepin-theme-param=theme data-calepin-theme-storage-key=calepin-website-theme>(()=>{let currentScript=document.currentScript,storageKey=currentScript?.dataset?.calepinThemeStorageKey||`calepin-website-theme`,param=currentScript?.dataset?.calepinThemeParam||`theme`,normalize=value=>value===`light`||value===`dark`?value:``,root=document.documentElement,media=window.matchMedia?window.matchMedia(`(prefers-color-scheme: dark)`):null,systemTheme=()=>media&&media.matches?`dark`:`light`,mode=``;if(param)try{mode=normalize(new URL(window.location.href).searchParams.get(param)||``)}catch{mode=``}if(!mode)try{mode=normalize(localStorage.getItem(storageKey)||``)}catch{mode=``}let theme=mode||systemTheme();root.dataset.calepinThemeMode=mode,root.dataset.theme=theme,root.style.colorScheme=theme})();</script><style>:host,:root{--pico-font-family-emoji:"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--pico-font-family-sans-serif:system-ui,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,Helvetica,Arial,"Helvetica Neue",sans-serif,var(--pico-font-family-emoji);--pico-font-family-monospace:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,"Liberation Mono",monospace,var(--pico-font-family-emoji);--pico-font-family:var(--pico-font-family-sans-serif);--pico-line-height:1.5;--pico-font-weight:400;--pico-font-size:100%;--pico-text-underline-offset:.1rem;--pico-border-radius:.25rem;--pico-border-width:.0625rem;--pico-outline-width:.125rem;--pico-transition:.2s ease-in-out;--pico-spacing:1rem;--pico-typography-spacing-vertical:1rem;--pico-block-spacing-vertical:var(--pico-spacing);--pico-block-spacing-horizontal:var(--pico-spacing);--pico-grid-column-gap:var(--pico-spacing);--pico-grid-row-gap:var(--pico-spacing);--pico-form-element-spacing-vertical:.75rem;--pico-form-element-spacing-horizontal:1rem;--pico-group-box-shadow:0 0 0 #0000;--pico-group-box-shadow-focus-with-button:0 0 0 var(--pico-outline-width) var(--pico-primary-focus);--pico-group-box-shadow-focus-with-input:0 0 0 .0625rem var(--pico-form-element-border-color);--pico-modal-overlay-backdrop-filter:blur(.375rem);--pico-nav-element-spacing-vertical:1rem;--pico-nav-element-spacing-horizontal:.5rem;--pico-nav-link-spacing-vertical:.5rem;--pico-nav-link-spacing-horizontal:.5rem;--pico-nav-breadcrumb-divider:">";--pico-icon-checkbox:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-minus:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E");--pico-icon-chevron:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-date:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E");--pico-icon-time:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline points='12 6 12 12 16 14'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-search:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'Line truncated
|
||||
`:Array.from(node.childNodes).map(codeText).join(``):``:``}document.querySelectorAll(selector).forEach(block=>{if(block.dataset.calepinCopyBound===`true`||block.querySelector(`:scope > .${buttonClass}`))return;block.dataset.calepinCopyBound=`true`;let button=document.createElement(`button`);button.type=`button`,button.className=buttonClass,button.setAttribute(`aria-label`,`Copy code`),button.setAttribute(`title`,`Copy code`),button.innerHTML=icon;let restoreTimeout=null;button.addEventListener(`click`,async()=>{restoreTimeout!==null&&(window.clearTimeout(restoreTimeout),restoreTimeout=null);let text=codeText(block.querySelector(`pre code, code, pre`));try{await copyText(text),button.classList.add(copiedClass),button.innerHTML=copiedIcon,restoreTimeout=window.setTimeout(()=>{button.classList.remove(copiedClass),button.innerHTML=icon,restoreTimeout=null},900)}catch{button.classList.remove(copiedClass),button.innerHTML=icon}}),block.prepend(button)})})();</script><script>(()=>{let pagefindModal=()=>document.querySelector(`pagefind-modal`);function modalSearchInput(modal){return modal?modal.querySelector(`pagefind-input input, input[type='search'], input`):null}function setModalSearchQuery(modal,query){if(!query)return!1;let input=modalSearchInput(modal);return input?(input.value=query,input.dispatchEvent(new Event(`input`,{bubbles:!0})),!0):!1}function focusModalSearch(modal){let input=modalSearchInput(modal);return input?(input.focus(),!0):!1}async function openPagefindSearch(query){let modal=pagefindModal();if(!modal)return!1;if(typeof modal.open!=`function`&&window.customElements?.whenDefined)try{await window.customElements.whenDefined(`pagefind-modal`)}catch{}if(typeof modal.open!=`function`)return!1;modal.open();let sync=()=>{let hasQuery=setModalSearchQuery(modal,query),hasFocus=focusModalSearch(modal);return hasQuery||hasFocus};return sync()||requestAnimationFrame(()=>{sync()||window.setTimeout(sync,50)}),!0}function isTextEntryElement(element){return element?element.isContentEditable||element.tagName===`INPUT`||element.tagName===`SELECT`||element.tagName===`TEXTAREA`:!1}let SEARCH_SUGGESTION_LIMIT=20,SEARCH_SUBRESULT_LIMIT=3,SEARCH_SUGGESTION_MIN_LENGTH=2,SEARCH_SUGGESTION_DELAY=160,pagefindModulePromise=null;function pagefindBundlePath(){let script=document.querySelector(`script[src$="pagefind-component-ui.js"], script[src$="pagefind-ui.js"]`);return script?.src?new URL(`./`,script.src).toString():new URL(`pagefind/`,window.location.href).toString()}function loadPagefindModule(){return pagefindModulePromise||=import(`${pagefindBundlePath()}pagefind.js`),pagefindModulePromise}function plainTextFromHtml(html){let element=document.createElement(`div`);return element.innerHTML=html||``,element.textContent?.trim()||``}function setSearchResultsOpen(input,results,isOpen){results.hidden=!isOpen,input.setAttribute(`aria-expanded`,String(isOpen))}function clearSearchResults(input,results){results.innerHTML=``,setSearchResultsOpen(input,results,!1)}function renderSearchMessage(input,results,message){results.innerHTML=``;let item=document.createElement(`p`);item.className=`calepin-website-search-empty`,item.textContent=message,results.appendChild(item),setSearchResultsOpen(input,results,!0)}function renderSearchResults(input,results,items){if(results.innerHTML=``,!items.length){renderSearchMessage(input,results,`No results`);return}let basePathPrefix=localCanonicalBasePathPrefix(),addSearchLink=(item,className,titleClassName,excerptClassName)=>{let href=hrefWithoutLocalBasePath(item.meta?.url||item.url||``,basePathPrefix);if(!href)return null;let link=document.createElement(`a`);link.className=className,link.href=href,link.setAttribute(`role`,`option`);let title=document.createElement(`span`);title.className=titleClassName,title.textContent=item.meta?.title||item.title||href,link.appendChild(title);let excerptText=plainTextFromHtml(item.excerpt||item.content||``);if(excerptText){let excerpt=document.createElement(`span`);excerpt.className=excerptClassName,excerpt.textContent=excerptText,link.appendChild(excerpt)}return link};for(let item of items){let link=addSearchLink(item,`calepin-website-search-result`,`calepin-website-search-result-title`,`calepin-website-search-result-excerpt`);if(!link)continue;results.appendChild(link);let subResults=Array.isArray(item.sub_results)?item.sub_results.slice(0,3):[];if(!subResults.length)continue;let nested=document.createElement(`div`);nested.className=`calepin-website-search-subresults`;for(let subResult of subResults){let subLink=addSearchLink(subResult,`calepin-website-search-subresult`,`calepin-website-search-subresult-title`,`calepin-website-search-subresult-excerpt`);subLink&&nested.appendChild(subLink)}nested.children.length&&results.appendChild(nested)}setSearchResultsOpen(input,results,!!results.children.length)}function initMobileSearchTriggers(){document.querySelectorAll(`[data-calepin-mobile-search-trigger]`).forEach(button=>{button.dataset.calepinSearchBound!==Line truncated
|
||||
*
|
||||
* One module-local script wires: view switcher (HTML/Source/PDF), the mobile
|
||||
* sidebar drawer, sidebar section folding, inline SVGs, internal link state
|
||||
* preservation, the native <dialog> video lightbox, and the Pagefind navbar
|
||||
* search bridge.
|
||||
*
|
||||
* The Rust theme loader links this file near the end of the page body, so
|
||||
* the DOM already exists when it runs and no DOMContentLoaded guard is
|
||||
* needed. */
|
||||
(()=>{if(!document.querySelector(`.calepin-website-topbar, .calepin-website-main, pagefind-modal, img[data-inline-svg], [data-video-dialog], [data-lightbox-dialog]`))return;let VIEW={param:`view`,rendered:`rendered`,source:`source`,pdf:`pdf`},MOBILE_QUERY=`(max-width: 56rem)`,SOURCE_DATA_ID=`calepin-website-source-data`,isMobile=()=>window.matchMedia(`(max-width: 56rem)`).matches,isPlainLeftClick=event=>event&&event.button===0&&!event.metaKey&&!event.ctrlKey&&!event.shiftKey&&!event.altKey,isInternalPageHref=href=>!!href&&!href.startsWith(`#`)&&!href.startsWith(`http://`)&&!href.startsWith(`https://`)&&!href.startsWith(`//`)&&!href.startsWith(`mailto:`)&&!href.startsWith(`tel:`);function setUrlParam(url,name,value){value?url.searchParams.set(name,value):url.searchParams.delete(name)}function readUrlParam(name,normalize,fallback=``){try{return normalize((new URL(window.location.href).searchParams.get(name)||``).toLowerCase())}catch{return fallback}}function updateCurrentUrlParam(name,value){try{let url=new URL(window.location.href);setUrlParam(url,name,value),window.history.replaceState({},``,url.toString())}catch{}}let getMain=()=>document.querySelector(`.calepin-website-main`),normalizeView=value=>value===VIEW.source||value===VIEW.pdf?value:VIEW.rendered;function currentView(selects){let select=Array.isArray(selects)?selects[0]:selects;return normalizeView(select&&select.value||VIEW.rendered)}function pageAssetCandidates(extension){let{pathname}=window.location;return pathname.endsWith(`/`)?[`${pathname}index.${extension}`]:pathname.endsWith(`.html`)?[`${pathname.slice(0,-5)}.${extension}`]:!pathname||pathname.endsWith(`.`)?[`index.${extension}`]:pathname.includes(`.`)?[`${pathname}.${extension}`]:[`${pathname}/index.${extension}`]}function replaceMainContent(node){let main=getMain();return main?(main.innerHTML=``,main.appendChild(node),!0):!1}function showMainFallback(message){let main=getMain();if(!main)return;let fallback=document.createElement(`p`);fallback.textContent=message,main.prepend(fallback)}function renderSourceText(text){let container=document.createElement(`div`);container.className=`calepin-website-source-view`;let pre=document.createElement(`pre`),code=document.createElement(`code`);code.className=`language-typ`,code.textContent=text,pre.appendChild(code),container.appendChild(pre),replaceMainContent(container)}async function renderSourceMode(){let element=document.getElementById(`calepin-website-source-data`);if(element)try{let inline=JSON.parse(element.textContent||``);if(typeof inline==`string`&&inline.length>0){renderSourceText(inline);return}}catch{}for(let sourcePath of pageAssetCandidates(`typ`))try{let response=await fetch(sourcePath);if(!response.ok)continue;renderSourceText(await response.text());return}catch{}showMainFallback(`Could not load source view.`)}async function renderPdfMode(){let isLocal=window.location.protocol===`file:`;for(let pdfPath of pageAssetCandidates(`pdf`)){if(!isLocal)try{let response=await fetch(pdfPath,{method:`HEAD`});if(!response.ok&&response.status!==405)continue}catch{}let viewer=document.createElement(`iframe`);viewer.className=`calepin-website-pdf-viewer`;try{viewer.src=new URL(pdfPath,window.location.href).toString()}catch{viewer.src=pdfPath}viewer.setAttribute(`title`,`Page PDF view`),viewer.setAttribute(`loading`,`lazy`),replaceMainContent(viewer);return}showMainFallback(`Could not load PDF view.`)}function applyView(mode,selects){let value=normalizeView(mode);(Array.isArray(selects)?selects:[selects]).filter(Boolean).forEach(select=>{select.value=value}),updateCurrentUrlParam(VIEW.param,value===VIEW.rendered?``:value),value===VIEW.source?renderSourceMode():value===VIEW.pdf&&renderPdfMode()}function initView(selects){let viewSelects=(Array.isArray(selects)?selects:[selects]).filter(Boolean);applyView(readUrlParam(VIEW.param,normalizeView,VIEW.rendered),viewSelects),viewSelects.forEach(select=>{select.dataset.calepinBound||(select.dataset.calepinBound=`true`,select.addEventListener(`change`,event=>{let value=normalizeView(event.target.value),url=new URL(window.location.href);setUrlParam(url,VIEW.param,value===VIEW.rendered?``:value),window.location.href=url.toString()}))})}
|
||||
// ----------------------------------------------------------- nav drawer
|
||||
function createNav(){let shell=document.querySelector(`.calepin-website-shell`),backdrop=document.querySelector(`.calepin-website-nav-backdrop`),toggle=document.querySelector(`.calepin-website-nav-toggle`);function setOpen(isOpen){!shell||!toggle||(shell.classList.toggle(`nav-open`,isOpen),document.body.classList.toggle(`calepin-website-nav-open`,isOpen),toggle.setAttribute(`aria-expanded`,String(isOpen)),backdrop&&backdrop.setAttribute(`aria-hidden`,String(!isOpen)))}let close=()=>setOpen(!1);return toggle&&toggle.addEventListener(`click`,event=>{event.preventDefault(),isMobile()&&shell&&setOpen(!shell.classList.contains(`nav-open`))}),backdrop?.addEventListener(`click`,close),document.addEventListener(`keydown`,event=>{event.key===`Escape`&&close()}),window.addEventListener(`resize`,()=>{isMobile()||close()}),{close}}
|
||||
// ------------------------------------------------------ sidebar sections
|
||||
// Only one section is open at a time, but "section" means siblings: opening a
|
||||
// nested subsection must not fold the parent it lives in, and must only fold
|
||||
// its own siblings.
|
||||
function initSidebarSections(){let sections=Array.from(document.querySelectorAll(`.calepin-website-sidebar-section`));sections.length<2||sections.forEach(section=>{section.addEventListener(`toggle`,()=>{section.open&§ions.forEach(other=>{other!==section&&other.parentElement===section.parentElement&&other.removeAttribute(`open`)})})})}
|
||||
// ------------------------------------------------------------- inline svg
|
||||
function svgTextFromSource(src){if(!src)return Promise.resolve(``);let prefix=`data:image/svg+xml`;if(src.startsWith(prefix)){let payload=src.slice(src.indexOf(`,`)+1);try{return Promise.resolve(src.startsWith(`${prefix};base64,`)?atob(payload):decodeURIComponent(payload))}catch{return Promise.resolve(payload)}}return fetch(src).then(response=>response.text()).catch(()=>``)}function inlineSvgImage(image){return svgTextFromSource(image.getAttribute(`src`)).then(text=>{if(!text)return;let svg=new DOMParser().parseFromString(text,`image/svg+xml`).querySelector(`svg`);if(!svg)return;svg.removeAttribute(`xmlns:xlink`),svg.removeAttribute(`width`),svg.removeAttribute(`height`),image.className&&svg.setAttribute(`class`,image.className),image.id&&svg.setAttribute(`id`,image.id);let style=image.getAttribute(`style`);style&&svg.setAttribute(`style`,style);let alt=image.getAttribute(`alt`);alt&&(svg.setAttribute(`aria-label`,alt),svg.setAttribute(`role`,`img`));let ariaHidden=image.getAttribute(`aria-hidden`);ariaHidden!==null&&svg.setAttribute(`aria-hidden`,ariaHidden),image.replaceWith(svg)})}function inlineSvgs(){document.querySelectorAll(`img[data-inline-svg]`).forEach(image=>inlineSvgImage(image))}
|
||||
// --------------------------------------------------------- link state
|
||||
function urlWithState(href,viewSelect){if(!isInternalPageHref(href))return href;try{let url=new URL(href,window.location.href),view=currentView(viewSelect);return setUrlParam(url,VIEW.param,view===VIEW.rendered?``:view),url.toString()}catch{return href}}function preserveStateInLinks(viewSelect){document.querySelectorAll(`a[href]`).forEach(link=>{let href=link.getAttribute(`href`);if(!isInternalPageHref(href))return;let next=urlWithState(href,viewSelect);next&&next!==href&&link.setAttribute(`href`,next)})}function initLinkInterception(nav,viewSelect){document.addEventListener(`click`,event=>{let tocLink=event.target.closest(`.calepin-website-toc a`);if(tocLink){let href=tocLink.getAttribute(`href`);if(href&&href.startsWith(`#`)){let target=null;try{target=document.querySelector(href)}catch{target=null}target&&target.scrollIntoView({behavior:`smooth`,block:`start`})}isMobile()&&nav.close();return}let link=event.target.closest(`a`);if(!link||!isPlainLeftClick(event))return;event.target.closest(`.calepin-website-sidebar a`)&&isMobile()&&nav.close();let href=link.getAttribute(`href`),next=urlWithState(href,viewSelect);next&&next!==href&&(event.preventDefault(),window.location.href=next)})}
|
||||
// ----------------------------------------------------- dialog lightbox
|
||||
function initDialogs(){document.querySelectorAll(`[data-video-dialog], [data-lightbox-dialog]`).forEach(trigger=>{let dialogId=trigger.dataset.videoDialog||trigger.dataset.lightboxDialog,dialog=document.getElementById(dialogId);!dialog||typeof dialog.showModal!=`function`||trigger.addEventListener(`click`,event=>{event.preventDefault(),dialog.showModal()})}),document.querySelectorAll(`dialog.calepin-video-dialog, dialog.calepin-screenshot-dialog`).forEach(dialog=>{let stop=()=>{let video=dialog.querySelector(`video`);video&&video.pause()};dialog.addEventListener(`click`,event=>{
|
||||
// Clicking the dialog backdrop (the dialog element itself) closes it.
|
||||
event.target===dialog&&dialog.close()}),dialog.querySelectorAll(`[data-close-dialog]`).forEach(button=>{button.addEventListener(`click`,()=>dialog.close())}),dialog.addEventListener(`close`,stop)})}
|
||||
// --------------------------------------------------------------- bootstrap
|
||||
let viewSelects=Array.from(document.querySelectorAll(`[data-calepin-view-mode], #calepin-website-view-mode`));initView(viewSelects);let nav=createNav();initSidebarSections(),inlineSvgs(),initDialogs(),preserveStateInLinks(viewSelects),initLinkInterception(nav,viewSelects),window.addEventListener(`pageshow`,event=>{event.persisted&&(initView(viewSelects),preserveStateInLinks(viewSelects))})})();</script>
|
||||
@@ -1,29 +0,0 @@
|
||||
<!doctype html><html lang=en><head><meta charset=utf-8><meta content="width=device-width,initial-scale=1" name=viewport><title>HomeLab</title><meta content="Placeholder page for the homelab category. The real content comes later." name=description><meta content="Placeholder page for the homelab category. The real content comes later." property=og:description><meta content="Placeholder page for the homelab category. The real content comes later." name=twitter:description><meta content=Yggdrasil property=og:site_name><meta content="HomeLab | Yggdrasil" property=og:title><meta content="HomeLab | Yggdrasil" name=twitter:title><meta content=article property=og:type><meta content=summary name=twitter:card><link href=https://yggdrasil.kb28.ch/homelab/ rel=canonical><meta content=https://yggdrasil.kb28.ch/homelab/ property=og:url><link href=../assets/yggdrasil.ico rel=icon><meta content="light dark" name=color-scheme><script data-calepin-theme-param=theme data-calepin-theme-storage-key=calepin-website-theme>(()=>{let currentScript=document.currentScript,storageKey=currentScript?.dataset?.calepinThemeStorageKey||`calepin-website-theme`,param=currentScript?.dataset?.calepinThemeParam||`theme`,normalize=value=>value===`light`||value===`dark`?value:``,root=document.documentElement,media=window.matchMedia?window.matchMedia(`(prefers-color-scheme: dark)`):null,systemTheme=()=>media&&media.matches?`dark`:`light`,mode=``;if(param)try{mode=normalize(new URL(window.location.href).searchParams.get(param)||``)}catch{mode=``}if(!mode)try{mode=normalize(localStorage.getItem(storageKey)||``)}catch{mode=``}let theme=mode||systemTheme();root.dataset.calepinThemeMode=mode,root.dataset.theme=theme,root.style.colorScheme=theme})();</script><style>:host,:root{--pico-font-family-emoji:"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--pico-font-family-sans-serif:system-ui,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,Helvetica,Arial,"Helvetica Neue",sans-serif,var(--pico-font-family-emoji);--pico-font-family-monospace:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,"Liberation Mono",monospace,var(--pico-font-family-emoji);--pico-font-family:var(--pico-font-family-sans-serif);--pico-line-height:1.5;--pico-font-weight:400;--pico-font-size:100%;--pico-text-underline-offset:.1rem;--pico-border-radius:.25rem;--pico-border-width:.0625rem;--pico-outline-width:.125rem;--pico-transition:.2s ease-in-out;--pico-spacing:1rem;--pico-typography-spacing-vertical:1rem;--pico-block-spacing-vertical:var(--pico-spacing);--pico-block-spacing-horizontal:var(--pico-spacing);--pico-grid-column-gap:var(--pico-spacing);--pico-grid-row-gap:var(--pico-spacing);--pico-form-element-spacing-vertical:.75rem;--pico-form-element-spacing-horizontal:1rem;--pico-group-box-shadow:0 0 0 #0000;--pico-group-box-shadow-focus-with-button:0 0 0 var(--pico-outline-width) var(--pico-primary-focus);--pico-group-box-shadow-focus-with-input:0 0 0 .0625rem var(--pico-form-element-border-color);--pico-modal-overlay-backdrop-filter:blur(.375rem);--pico-nav-element-spacing-vertical:1rem;--pico-nav-element-spacing-horizontal:.5rem;--pico-nav-link-spacing-vertical:.5rem;--pico-nav-link-spacing-horizontal:.5rem;--pico-nav-breadcrumb-divider:">";--pico-icon-checkbox:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-minus:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E");--pico-icon-chevron:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-date:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E");--pico-icon-time:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline points='12 6 12 12 16 14'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-search:url("data:image/svLine truncated
|
||||
`:Array.from(node.childNodes).map(codeText).join(``):``:``}document.querySelectorAll(selector).forEach(block=>{if(block.dataset.calepinCopyBound===`true`||block.querySelector(`:scope > .${buttonClass}`))return;block.dataset.calepinCopyBound=`true`;let button=document.createElement(`button`);button.type=`button`,button.className=buttonClass,button.setAttribute(`aria-label`,`Copy code`),button.setAttribute(`title`,`Copy code`),button.innerHTML=icon;let restoreTimeout=null;button.addEventListener(`click`,async()=>{restoreTimeout!==null&&(window.clearTimeout(restoreTimeout),restoreTimeout=null);let text=codeText(block.querySelector(`pre code, code, pre`));try{await copyText(text),button.classList.add(copiedClass),button.innerHTML=copiedIcon,restoreTimeout=window.setTimeout(()=>{button.classList.remove(copiedClass),button.innerHTML=icon,restoreTimeout=null},900)}catch{button.classList.remove(copiedClass),button.innerHTML=icon}}),block.prepend(button)})})();</script><script>(()=>{let pagefindModal=()=>document.querySelector(`pagefind-modal`);function modalSearchInput(modal){return modal?modal.querySelector(`pagefind-input input, input[type='search'], input`):null}function setModalSearchQuery(modal,query){if(!query)return!1;let input=modalSearchInput(modal);return input?(input.value=query,input.dispatchEvent(new Event(`input`,{bubbles:!0})),!0):!1}function focusModalSearch(modal){let input=modalSearchInput(modal);return input?(input.focus(),!0):!1}async function openPagefindSearch(query){let modal=pagefindModal();if(!modal)return!1;if(typeof modal.open!=`function`&&window.customElements?.whenDefined)try{await window.customElements.whenDefined(`pagefind-modal`)}catch{}if(typeof modal.open!=`function`)return!1;modal.open();let sync=()=>{let hasQuery=setModalSearchQuery(modal,query),hasFocus=focusModalSearch(modal);return hasQuery||hasFocus};return sync()||requestAnimationFrame(()=>{sync()||window.setTimeout(sync,50)}),!0}function isTextEntryElement(element){return element?element.isContentEditable||element.tagName===`INPUT`||element.tagName===`SELECT`||element.tagName===`TEXTAREA`:!1}let SEARCH_SUGGESTION_LIMIT=20,SEARCH_SUBRESULT_LIMIT=3,SEARCH_SUGGESTION_MIN_LENGTH=2,SEARCH_SUGGESTION_DELAY=160,pagefindModulePromise=null;function pagefindBundlePath(){let script=document.querySelector(`script[src$="pagefind-component-ui.js"], script[src$="pagefind-ui.js"]`);return script?.src?new URL(`./`,script.src).toString():new URL(`pagefind/`,window.location.href).toString()}function loadPagefindModule(){return pagefindModulePromise||=import(`${pagefindBundlePath()}pagefind.js`),pagefindModulePromise}function plainTextFromHtml(html){let element=document.createElement(`div`);return element.innerHTML=html||``,element.textContent?.trim()||``}function setSearchResultsOpen(input,results,isOpen){results.hidden=!isOpen,input.setAttribute(`aria-expanded`,String(isOpen))}function clearSearchResults(input,results){results.innerHTML=``,setSearchResultsOpen(input,results,!1)}function renderSearchMessage(input,results,message){results.innerHTML=``;let item=document.createElement(`p`);item.className=`calepin-website-search-empty`,item.textContent=message,results.appendChild(item),setSearchResultsOpen(input,results,!0)}function renderSearchResults(input,results,items){if(results.innerHTML=``,!items.length){renderSearchMessage(input,results,`No results`);return}let basePathPrefix=localCanonicalBasePathPrefix(),addSearchLink=(item,className,titleClassName,excerptClassName)=>{let href=hrefWithoutLocalBasePath(item.meta?.url||item.url||``,basePathPrefix);if(!href)return null;let link=document.createElement(`a`);link.className=className,link.href=href,link.setAttribute(`role`,`option`);let title=document.createElement(`span`);title.className=titleClassName,title.textContent=item.meta?.title||item.title||href,link.appendChild(title);let excerptText=plainTextFromHtml(item.excerpt||item.content||``);if(excerptText){let excerpt=document.createElement(`span`);excerpt.className=excerptClassName,excerpt.textContent=excerptText,link.appendChild(excerpt)}return link};for(let item of items){let link=addSearchLink(item,`calepin-website-search-result`,`calepin-website-search-result-title`,`calepin-website-search-result-excerpt`);if(!link)continue;results.appendChild(link);let subResults=Array.isArray(item.sub_results)?item.sub_results.slice(0,3):[];if(!subResults.length)continue;let nested=document.createElement(`div`);nested.className=`calepin-website-search-subresults`;for(let subResult of subResults){let subLink=addSearchLink(subResult,`calepin-website-search-subresult`,`calepin-website-search-subresult-title`,`calepin-website-search-subresult-excerpt`);subLink&&nested.appendChild(subLink)}nested.children.length&&results.appendChild(nested)}setSearchResultsOpen(input,results,!!results.children.length)}function initMobileSearchTriggers(){document.querySelectorAll(`[data-calepin-mobile-search-trigger]`).forEach(button=>{button.dataset.calepinSearchBound!==Line truncated
|
||||
*
|
||||
* One module-local script wires: view switcher (HTML/Source/PDF), the mobile
|
||||
* sidebar drawer, sidebar section folding, inline SVGs, internal link state
|
||||
* preservation, the native <dialog> video lightbox, and the Pagefind navbar
|
||||
* search bridge.
|
||||
*
|
||||
* The Rust theme loader links this file near the end of the page body, so
|
||||
* the DOM already exists when it runs and no DOMContentLoaded guard is
|
||||
* needed. */
|
||||
(()=>{if(!document.querySelector(`.calepin-website-topbar, .calepin-website-main, pagefind-modal, img[data-inline-svg], [data-video-dialog], [data-lightbox-dialog]`))return;let VIEW={param:`view`,rendered:`rendered`,source:`source`,pdf:`pdf`},MOBILE_QUERY=`(max-width: 56rem)`,SOURCE_DATA_ID=`calepin-website-source-data`,isMobile=()=>window.matchMedia(`(max-width: 56rem)`).matches,isPlainLeftClick=event=>event&&event.button===0&&!event.metaKey&&!event.ctrlKey&&!event.shiftKey&&!event.altKey,isInternalPageHref=href=>!!href&&!href.startsWith(`#`)&&!href.startsWith(`http://`)&&!href.startsWith(`https://`)&&!href.startsWith(`//`)&&!href.startsWith(`mailto:`)&&!href.startsWith(`tel:`);function setUrlParam(url,name,value){value?url.searchParams.set(name,value):url.searchParams.delete(name)}function readUrlParam(name,normalize,fallback=``){try{return normalize((new URL(window.location.href).searchParams.get(name)||``).toLowerCase())}catch{return fallback}}function updateCurrentUrlParam(name,value){try{let url=new URL(window.location.href);setUrlParam(url,name,value),window.history.replaceState({},``,url.toString())}catch{}}let getMain=()=>document.querySelector(`.calepin-website-main`),normalizeView=value=>value===VIEW.source||value===VIEW.pdf?value:VIEW.rendered;function currentView(selects){let select=Array.isArray(selects)?selects[0]:selects;return normalizeView(select&&select.value||VIEW.rendered)}function pageAssetCandidates(extension){let{pathname}=window.location;return pathname.endsWith(`/`)?[`${pathname}index.${extension}`]:pathname.endsWith(`.html`)?[`${pathname.slice(0,-5)}.${extension}`]:!pathname||pathname.endsWith(`.`)?[`index.${extension}`]:pathname.includes(`.`)?[`${pathname}.${extension}`]:[`${pathname}/index.${extension}`]}function replaceMainContent(node){let main=getMain();return main?(main.innerHTML=``,main.appendChild(node),!0):!1}function showMainFallback(message){let main=getMain();if(!main)return;let fallback=document.createElement(`p`);fallback.textContent=message,main.prepend(fallback)}function renderSourceText(text){let container=document.createElement(`div`);container.className=`calepin-website-source-view`;let pre=document.createElement(`pre`),code=document.createElement(`code`);code.className=`language-typ`,code.textContent=text,pre.appendChild(code),container.appendChild(pre),replaceMainContent(container)}async function renderSourceMode(){let element=document.getElementById(`calepin-website-source-data`);if(element)try{let inline=JSON.parse(element.textContent||``);if(typeof inline==`string`&&inline.length>0){renderSourceText(inline);return}}catch{}for(let sourcePath of pageAssetCandidates(`typ`))try{let response=await fetch(sourcePath);if(!response.ok)continue;renderSourceText(await response.text());return}catch{}showMainFallback(`Could not load source view.`)}async function renderPdfMode(){let isLocal=window.location.protocol===`file:`;for(let pdfPath of pageAssetCandidates(`pdf`)){if(!isLocal)try{let response=await fetch(pdfPath,{method:`HEAD`});if(!response.ok&&response.status!==405)continue}catch{}let viewer=document.createElement(`iframe`);viewer.className=`calepin-website-pdf-viewer`;try{viewer.src=new URL(pdfPath,window.location.href).toString()}catch{viewer.src=pdfPath}viewer.setAttribute(`title`,`Page PDF view`),viewer.setAttribute(`loading`,`lazy`),replaceMainContent(viewer);return}showMainFallback(`Could not load PDF view.`)}function applyView(mode,selects){let value=normalizeView(mode);(Array.isArray(selects)?selects:[selects]).filter(Boolean).forEach(select=>{select.value=value}),updateCurrentUrlParam(VIEW.param,value===VIEW.rendered?``:value),value===VIEW.source?renderSourceMode():value===VIEW.pdf&&renderPdfMode()}function initView(selects){let viewSelects=(Array.isArray(selects)?selects:[selects]).filter(Boolean);applyView(readUrlParam(VIEW.param,normalizeView,VIEW.rendered),viewSelects),viewSelects.forEach(select=>{select.dataset.calepinBound||(select.dataset.calepinBound=`true`,select.addEventListener(`change`,event=>{let value=normalizeView(event.target.value),url=new URL(window.location.href);setUrlParam(url,VIEW.param,value===VIEW.rendered?``:value),window.location.href=url.toString()}))})}
|
||||
// ----------------------------------------------------------- nav drawer
|
||||
function createNav(){let shell=document.querySelector(`.calepin-website-shell`),backdrop=document.querySelector(`.calepin-website-nav-backdrop`),toggle=document.querySelector(`.calepin-website-nav-toggle`);function setOpen(isOpen){!shell||!toggle||(shell.classList.toggle(`nav-open`,isOpen),document.body.classList.toggle(`calepin-website-nav-open`,isOpen),toggle.setAttribute(`aria-expanded`,String(isOpen)),backdrop&&backdrop.setAttribute(`aria-hidden`,String(!isOpen)))}let close=()=>setOpen(!1);return toggle&&toggle.addEventListener(`click`,event=>{event.preventDefault(),isMobile()&&shell&&setOpen(!shell.classList.contains(`nav-open`))}),backdrop?.addEventListener(`click`,close),document.addEventListener(`keydown`,event=>{event.key===`Escape`&&close()}),window.addEventListener(`resize`,()=>{isMobile()||close()}),{close}}
|
||||
// ------------------------------------------------------ sidebar sections
|
||||
// Only one section is open at a time, but "section" means siblings: opening a
|
||||
// nested subsection must not fold the parent it lives in, and must only fold
|
||||
// its own siblings.
|
||||
function initSidebarSections(){let sections=Array.from(document.querySelectorAll(`.calepin-website-sidebar-section`));sections.length<2||sections.forEach(section=>{section.addEventListener(`toggle`,()=>{section.open&§ions.forEach(other=>{other!==section&&other.parentElement===section.parentElement&&other.removeAttribute(`open`)})})})}
|
||||
// ------------------------------------------------------------- inline svg
|
||||
function svgTextFromSource(src){if(!src)return Promise.resolve(``);let prefix=`data:image/svg+xml`;if(src.startsWith(prefix)){let payload=src.slice(src.indexOf(`,`)+1);try{return Promise.resolve(src.startsWith(`${prefix};base64,`)?atob(payload):decodeURIComponent(payload))}catch{return Promise.resolve(payload)}}return fetch(src).then(response=>response.text()).catch(()=>``)}function inlineSvgImage(image){return svgTextFromSource(image.getAttribute(`src`)).then(text=>{if(!text)return;let svg=new DOMParser().parseFromString(text,`image/svg+xml`).querySelector(`svg`);if(!svg)return;svg.removeAttribute(`xmlns:xlink`),svg.removeAttribute(`width`),svg.removeAttribute(`height`),image.className&&svg.setAttribute(`class`,image.className),image.id&&svg.setAttribute(`id`,image.id);let style=image.getAttribute(`style`);style&&svg.setAttribute(`style`,style);let alt=image.getAttribute(`alt`);alt&&(svg.setAttribute(`aria-label`,alt),svg.setAttribute(`role`,`img`));let ariaHidden=image.getAttribute(`aria-hidden`);ariaHidden!==null&&svg.setAttribute(`aria-hidden`,ariaHidden),image.replaceWith(svg)})}function inlineSvgs(){document.querySelectorAll(`img[data-inline-svg]`).forEach(image=>inlineSvgImage(image))}
|
||||
// --------------------------------------------------------- link state
|
||||
function urlWithState(href,viewSelect){if(!isInternalPageHref(href))return href;try{let url=new URL(href,window.location.href),view=currentView(viewSelect);return setUrlParam(url,VIEW.param,view===VIEW.rendered?``:view),url.toString()}catch{return href}}function preserveStateInLinks(viewSelect){document.querySelectorAll(`a[href]`).forEach(link=>{let href=link.getAttribute(`href`);if(!isInternalPageHref(href))return;let next=urlWithState(href,viewSelect);next&&next!==href&&link.setAttribute(`href`,next)})}function initLinkInterception(nav,viewSelect){document.addEventListener(`click`,event=>{let tocLink=event.target.closest(`.calepin-website-toc a`);if(tocLink){let href=tocLink.getAttribute(`href`);if(href&&href.startsWith(`#`)){let target=null;try{target=document.querySelector(href)}catch{target=null}target&&target.scrollIntoView({behavior:`smooth`,block:`start`})}isMobile()&&nav.close();return}let link=event.target.closest(`a`);if(!link||!isPlainLeftClick(event))return;event.target.closest(`.calepin-website-sidebar a`)&&isMobile()&&nav.close();let href=link.getAttribute(`href`),next=urlWithState(href,viewSelect);next&&next!==href&&(event.preventDefault(),window.location.href=next)})}
|
||||
// ----------------------------------------------------- dialog lightbox
|
||||
function initDialogs(){document.querySelectorAll(`[data-video-dialog], [data-lightbox-dialog]`).forEach(trigger=>{let dialogId=trigger.dataset.videoDialog||trigger.dataset.lightboxDialog,dialog=document.getElementById(dialogId);!dialog||typeof dialog.showModal!=`function`||trigger.addEventListener(`click`,event=>{event.preventDefault(),dialog.showModal()})}),document.querySelectorAll(`dialog.calepin-video-dialog, dialog.calepin-screenshot-dialog`).forEach(dialog=>{let stop=()=>{let video=dialog.querySelector(`video`);video&&video.pause()};dialog.addEventListener(`click`,event=>{
|
||||
// Clicking the dialog backdrop (the dialog element itself) closes it.
|
||||
event.target===dialog&&dialog.close()}),dialog.querySelectorAll(`[data-close-dialog]`).forEach(button=>{button.addEventListener(`click`,()=>dialog.close())}),dialog.addEventListener(`close`,stop)})}
|
||||
// --------------------------------------------------------------- bootstrap
|
||||
let viewSelects=Array.from(document.querySelectorAll(`[data-calepin-view-mode], #calepin-website-view-mode`));initView(viewSelects);let nav=createNav();initSidebarSections(),inlineSvgs(),initDialogs(),preserveStateInLinks(viewSelects),initLinkInterception(nav,viewSelects),window.addEventListener(`pageshow`,event=>{event.persisted&&(initView(viewSelects),preserveStateInLinks(viewSelects))})})();</script>
|
||||
@@ -1,29 +0,0 @@
|
||||
<!doctype html><html lang=en><head><meta charset=utf-8><meta content="width=device-width,initial-scale=1" name=viewport><title>Yggdrasil</title><meta content="Welcome to Yggdrasil, my personal knowledge base. Pick a master category from the menu" name=description><meta content="Welcome to Yggdrasil, my personal knowledge base. Pick a master category from the menu" property=og:description><meta content="Welcome to Yggdrasil, my personal knowledge base. Pick a master category from the menu" name=twitter:description><meta content=Yggdrasil property=og:site_name><meta content=Yggdrasil property=og:title><meta content=Yggdrasil name=twitter:title><meta content=article property=og:type><meta content=summary name=twitter:card><link href=https://yggdrasil.kb28.ch/ rel=canonical><meta content=https://yggdrasil.kb28.ch/ property=og:url><link href=assets/yggdrasil.ico rel=icon><meta content="light dark" name=color-scheme><script data-calepin-theme-param=theme data-calepin-theme-storage-key=calepin-website-theme>(()=>{let currentScript=document.currentScript,storageKey=currentScript?.dataset?.calepinThemeStorageKey||`calepin-website-theme`,param=currentScript?.dataset?.calepinThemeParam||`theme`,normalize=value=>value===`light`||value===`dark`?value:``,root=document.documentElement,media=window.matchMedia?window.matchMedia(`(prefers-color-scheme: dark)`):null,systemTheme=()=>media&&media.matches?`dark`:`light`,mode=``;if(param)try{mode=normalize(new URL(window.location.href).searchParams.get(param)||``)}catch{mode=``}if(!mode)try{mode=normalize(localStorage.getItem(storageKey)||``)}catch{mode=``}let theme=mode||systemTheme();root.dataset.calepinThemeMode=mode,root.dataset.theme=theme,root.style.colorScheme=theme})();</script><style>:host,:root{--pico-font-family-emoji:"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--pico-font-family-sans-serif:system-ui,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,Helvetica,Arial,"Helvetica Neue",sans-serif,var(--pico-font-family-emoji);--pico-font-family-monospace:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,"Liberation Mono",monospace,var(--pico-font-family-emoji);--pico-font-family:var(--pico-font-family-sans-serif);--pico-line-height:1.5;--pico-font-weight:400;--pico-font-size:100%;--pico-text-underline-offset:.1rem;--pico-border-radius:.25rem;--pico-border-width:.0625rem;--pico-outline-width:.125rem;--pico-transition:.2s ease-in-out;--pico-spacing:1rem;--pico-typography-spacing-vertical:1rem;--pico-block-spacing-vertical:var(--pico-spacing);--pico-block-spacing-horizontal:var(--pico-spacing);--pico-grid-column-gap:var(--pico-spacing);--pico-grid-row-gap:var(--pico-spacing);--pico-form-element-spacing-vertical:.75rem;--pico-form-element-spacing-horizontal:1rem;--pico-group-box-shadow:0 0 0 #0000;--pico-group-box-shadow-focus-with-button:0 0 0 var(--pico-outline-width) var(--pico-primary-focus);--pico-group-box-shadow-focus-with-input:0 0 0 .0625rem var(--pico-form-element-border-color);--pico-modal-overlay-backdrop-filter:blur(.375rem);--pico-nav-element-spacing-vertical:1rem;--pico-nav-element-spacing-horizontal:.5rem;--pico-nav-link-spacing-vertical:.5rem;--pico-nav-link-spacing-horizontal:.5rem;--pico-nav-breadcrumb-divider:">";--pico-icon-checkbox:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-minus:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E");--pico-icon-chevron:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-date:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E");--pico-icon-time:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline points='12 6 12 12 16 14'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-search:url("data:image/sLine truncated
|
||||
`:Array.from(node.childNodes).map(codeText).join(``):``:``}document.querySelectorAll(selector).forEach(block=>{if(block.dataset.calepinCopyBound===`true`||block.querySelector(`:scope > .${buttonClass}`))return;block.dataset.calepinCopyBound=`true`;let button=document.createElement(`button`);button.type=`button`,button.className=buttonClass,button.setAttribute(`aria-label`,`Copy code`),button.setAttribute(`title`,`Copy code`),button.innerHTML=icon;let restoreTimeout=null;button.addEventListener(`click`,async()=>{restoreTimeout!==null&&(window.clearTimeout(restoreTimeout),restoreTimeout=null);let text=codeText(block.querySelector(`pre code, code, pre`));try{await copyText(text),button.classList.add(copiedClass),button.innerHTML=copiedIcon,restoreTimeout=window.setTimeout(()=>{button.classList.remove(copiedClass),button.innerHTML=icon,restoreTimeout=null},900)}catch{button.classList.remove(copiedClass),button.innerHTML=icon}}),block.prepend(button)})})();</script><script>(()=>{let pagefindModal=()=>document.querySelector(`pagefind-modal`);function modalSearchInput(modal){return modal?modal.querySelector(`pagefind-input input, input[type='search'], input`):null}function setModalSearchQuery(modal,query){if(!query)return!1;let input=modalSearchInput(modal);return input?(input.value=query,input.dispatchEvent(new Event(`input`,{bubbles:!0})),!0):!1}function focusModalSearch(modal){let input=modalSearchInput(modal);return input?(input.focus(),!0):!1}async function openPagefindSearch(query){let modal=pagefindModal();if(!modal)return!1;if(typeof modal.open!=`function`&&window.customElements?.whenDefined)try{await window.customElements.whenDefined(`pagefind-modal`)}catch{}if(typeof modal.open!=`function`)return!1;modal.open();let sync=()=>{let hasQuery=setModalSearchQuery(modal,query),hasFocus=focusModalSearch(modal);return hasQuery||hasFocus};return sync()||requestAnimationFrame(()=>{sync()||window.setTimeout(sync,50)}),!0}function isTextEntryElement(element){return element?element.isContentEditable||element.tagName===`INPUT`||element.tagName===`SELECT`||element.tagName===`TEXTAREA`:!1}let SEARCH_SUGGESTION_LIMIT=20,SEARCH_SUBRESULT_LIMIT=3,SEARCH_SUGGESTION_MIN_LENGTH=2,SEARCH_SUGGESTION_DELAY=160,pagefindModulePromise=null;function pagefindBundlePath(){let script=document.querySelector(`script[src$="pagefind-component-ui.js"], script[src$="pagefind-ui.js"]`);return script?.src?new URL(`./`,script.src).toString():new URL(`pagefind/`,window.location.href).toString()}function loadPagefindModule(){return pagefindModulePromise||=import(`${pagefindBundlePath()}pagefind.js`),pagefindModulePromise}function plainTextFromHtml(html){let element=document.createElement(`div`);return element.innerHTML=html||``,element.textContent?.trim()||``}function setSearchResultsOpen(input,results,isOpen){results.hidden=!isOpen,input.setAttribute(`aria-expanded`,String(isOpen))}function clearSearchResults(input,results){results.innerHTML=``,setSearchResultsOpen(input,results,!1)}function renderSearchMessage(input,results,message){results.innerHTML=``;let item=document.createElement(`p`);item.className=`calepin-website-search-empty`,item.textContent=message,results.appendChild(item),setSearchResultsOpen(input,results,!0)}function renderSearchResults(input,results,items){if(results.innerHTML=``,!items.length){renderSearchMessage(input,results,`No results`);return}let basePathPrefix=localCanonicalBasePathPrefix(),addSearchLink=(item,className,titleClassName,excerptClassName)=>{let href=hrefWithoutLocalBasePath(item.meta?.url||item.url||``,basePathPrefix);if(!href)return null;let link=document.createElement(`a`);link.className=className,link.href=href,link.setAttribute(`role`,`option`);let title=document.createElement(`span`);title.className=titleClassName,title.textContent=item.meta?.title||item.title||href,link.appendChild(title);let excerptText=plainTextFromHtml(item.excerpt||item.content||``);if(excerptText){let excerpt=document.createElement(`span`);excerpt.className=excerptClassName,excerpt.textContent=excerptText,link.appendChild(excerpt)}return link};for(let item of items){let link=addSearchLink(item,`calepin-website-search-result`,`calepin-website-search-result-title`,`calepin-website-search-result-excerpt`);if(!link)continue;results.appendChild(link);let subResults=Array.isArray(item.sub_results)?item.sub_results.slice(0,3):[];if(!subResults.length)continue;let nested=document.createElement(`div`);nested.className=`calepin-website-search-subresults`;for(let subResult of subResults){let subLink=addSearchLink(subResult,`calepin-website-search-subresult`,`calepin-website-search-subresult-title`,`calepin-website-search-subresult-excerpt`);subLink&&nested.appendChild(subLink)}nested.children.length&&results.appendChild(nested)}setSearchResultsOpen(input,results,!!results.children.length)}function initMobileSearchTriggers(){document.querySelectorAll(`[data-calepin-mobile-search-trigger]`).forEach(button=>{button.dataset.calepinSearchBound!==Line truncated
|
||||
*
|
||||
* One module-local script wires: view switcher (HTML/Source/PDF), the mobile
|
||||
* sidebar drawer, sidebar section folding, inline SVGs, internal link state
|
||||
* preservation, the native <dialog> video lightbox, and the Pagefind navbar
|
||||
* search bridge.
|
||||
*
|
||||
* The Rust theme loader links this file near the end of the page body, so
|
||||
* the DOM already exists when it runs and no DOMContentLoaded guard is
|
||||
* needed. */
|
||||
(()=>{if(!document.querySelector(`.calepin-website-topbar, .calepin-website-main, pagefind-modal, img[data-inline-svg], [data-video-dialog], [data-lightbox-dialog]`))return;let VIEW={param:`view`,rendered:`rendered`,source:`source`,pdf:`pdf`},MOBILE_QUERY=`(max-width: 56rem)`,SOURCE_DATA_ID=`calepin-website-source-data`,isMobile=()=>window.matchMedia(`(max-width: 56rem)`).matches,isPlainLeftClick=event=>event&&event.button===0&&!event.metaKey&&!event.ctrlKey&&!event.shiftKey&&!event.altKey,isInternalPageHref=href=>!!href&&!href.startsWith(`#`)&&!href.startsWith(`http://`)&&!href.startsWith(`https://`)&&!href.startsWith(`//`)&&!href.startsWith(`mailto:`)&&!href.startsWith(`tel:`);function setUrlParam(url,name,value){value?url.searchParams.set(name,value):url.searchParams.delete(name)}function readUrlParam(name,normalize,fallback=``){try{return normalize((new URL(window.location.href).searchParams.get(name)||``).toLowerCase())}catch{return fallback}}function updateCurrentUrlParam(name,value){try{let url=new URL(window.location.href);setUrlParam(url,name,value),window.history.replaceState({},``,url.toString())}catch{}}let getMain=()=>document.querySelector(`.calepin-website-main`),normalizeView=value=>value===VIEW.source||value===VIEW.pdf?value:VIEW.rendered;function currentView(selects){let select=Array.isArray(selects)?selects[0]:selects;return normalizeView(select&&select.value||VIEW.rendered)}function pageAssetCandidates(extension){let{pathname}=window.location;return pathname.endsWith(`/`)?[`${pathname}index.${extension}`]:pathname.endsWith(`.html`)?[`${pathname.slice(0,-5)}.${extension}`]:!pathname||pathname.endsWith(`.`)?[`index.${extension}`]:pathname.includes(`.`)?[`${pathname}.${extension}`]:[`${pathname}/index.${extension}`]}function replaceMainContent(node){let main=getMain();return main?(main.innerHTML=``,main.appendChild(node),!0):!1}function showMainFallback(message){let main=getMain();if(!main)return;let fallback=document.createElement(`p`);fallback.textContent=message,main.prepend(fallback)}function renderSourceText(text){let container=document.createElement(`div`);container.className=`calepin-website-source-view`;let pre=document.createElement(`pre`),code=document.createElement(`code`);code.className=`language-typ`,code.textContent=text,pre.appendChild(code),container.appendChild(pre),replaceMainContent(container)}async function renderSourceMode(){let element=document.getElementById(`calepin-website-source-data`);if(element)try{let inline=JSON.parse(element.textContent||``);if(typeof inline==`string`&&inline.length>0){renderSourceText(inline);return}}catch{}for(let sourcePath of pageAssetCandidates(`typ`))try{let response=await fetch(sourcePath);if(!response.ok)continue;renderSourceText(await response.text());return}catch{}showMainFallback(`Could not load source view.`)}async function renderPdfMode(){let isLocal=window.location.protocol===`file:`;for(let pdfPath of pageAssetCandidates(`pdf`)){if(!isLocal)try{let response=await fetch(pdfPath,{method:`HEAD`});if(!response.ok&&response.status!==405)continue}catch{}let viewer=document.createElement(`iframe`);viewer.className=`calepin-website-pdf-viewer`;try{viewer.src=new URL(pdfPath,window.location.href).toString()}catch{viewer.src=pdfPath}viewer.setAttribute(`title`,`Page PDF view`),viewer.setAttribute(`loading`,`lazy`),replaceMainContent(viewer);return}showMainFallback(`Could not load PDF view.`)}function applyView(mode,selects){let value=normalizeView(mode);(Array.isArray(selects)?selects:[selects]).filter(Boolean).forEach(select=>{select.value=value}),updateCurrentUrlParam(VIEW.param,value===VIEW.rendered?``:value),value===VIEW.source?renderSourceMode():value===VIEW.pdf&&renderPdfMode()}function initView(selects){let viewSelects=(Array.isArray(selects)?selects:[selects]).filter(Boolean);applyView(readUrlParam(VIEW.param,normalizeView,VIEW.rendered),viewSelects),viewSelects.forEach(select=>{select.dataset.calepinBound||(select.dataset.calepinBound=`true`,select.addEventListener(`change`,event=>{let value=normalizeView(event.target.value),url=new URL(window.location.href);setUrlParam(url,VIEW.param,value===VIEW.rendered?``:value),window.location.href=url.toString()}))})}
|
||||
// ----------------------------------------------------------- nav drawer
|
||||
function createNav(){let shell=document.querySelector(`.calepin-website-shell`),backdrop=document.querySelector(`.calepin-website-nav-backdrop`),toggle=document.querySelector(`.calepin-website-nav-toggle`);function setOpen(isOpen){!shell||!toggle||(shell.classList.toggle(`nav-open`,isOpen),document.body.classList.toggle(`calepin-website-nav-open`,isOpen),toggle.setAttribute(`aria-expanded`,String(isOpen)),backdrop&&backdrop.setAttribute(`aria-hidden`,String(!isOpen)))}let close=()=>setOpen(!1);return toggle&&toggle.addEventListener(`click`,event=>{event.preventDefault(),isMobile()&&shell&&setOpen(!shell.classList.contains(`nav-open`))}),backdrop?.addEventListener(`click`,close),document.addEventListener(`keydown`,event=>{event.key===`Escape`&&close()}),window.addEventListener(`resize`,()=>{isMobile()||close()}),{close}}
|
||||
// ------------------------------------------------------ sidebar sections
|
||||
// Only one section is open at a time, but "section" means siblings: opening a
|
||||
// nested subsection must not fold the parent it lives in, and must only fold
|
||||
// its own siblings.
|
||||
function initSidebarSections(){let sections=Array.from(document.querySelectorAll(`.calepin-website-sidebar-section`));sections.length<2||sections.forEach(section=>{section.addEventListener(`toggle`,()=>{section.open&§ions.forEach(other=>{other!==section&&other.parentElement===section.parentElement&&other.removeAttribute(`open`)})})})}
|
||||
// ------------------------------------------------------------- inline svg
|
||||
function svgTextFromSource(src){if(!src)return Promise.resolve(``);let prefix=`data:image/svg+xml`;if(src.startsWith(prefix)){let payload=src.slice(src.indexOf(`,`)+1);try{return Promise.resolve(src.startsWith(`${prefix};base64,`)?atob(payload):decodeURIComponent(payload))}catch{return Promise.resolve(payload)}}return fetch(src).then(response=>response.text()).catch(()=>``)}function inlineSvgImage(image){return svgTextFromSource(image.getAttribute(`src`)).then(text=>{if(!text)return;let svg=new DOMParser().parseFromString(text,`image/svg+xml`).querySelector(`svg`);if(!svg)return;svg.removeAttribute(`xmlns:xlink`),svg.removeAttribute(`width`),svg.removeAttribute(`height`),image.className&&svg.setAttribute(`class`,image.className),image.id&&svg.setAttribute(`id`,image.id);let style=image.getAttribute(`style`);style&&svg.setAttribute(`style`,style);let alt=image.getAttribute(`alt`);alt&&(svg.setAttribute(`aria-label`,alt),svg.setAttribute(`role`,`img`));let ariaHidden=image.getAttribute(`aria-hidden`);ariaHidden!==null&&svg.setAttribute(`aria-hidden`,ariaHidden),image.replaceWith(svg)})}function inlineSvgs(){document.querySelectorAll(`img[data-inline-svg]`).forEach(image=>inlineSvgImage(image))}
|
||||
// --------------------------------------------------------- link state
|
||||
function urlWithState(href,viewSelect){if(!isInternalPageHref(href))return href;try{let url=new URL(href,window.location.href),view=currentView(viewSelect);return setUrlParam(url,VIEW.param,view===VIEW.rendered?``:view),url.toString()}catch{return href}}function preserveStateInLinks(viewSelect){document.querySelectorAll(`a[href]`).forEach(link=>{let href=link.getAttribute(`href`);if(!isInternalPageHref(href))return;let next=urlWithState(href,viewSelect);next&&next!==href&&link.setAttribute(`href`,next)})}function initLinkInterception(nav,viewSelect){document.addEventListener(`click`,event=>{let tocLink=event.target.closest(`.calepin-website-toc a`);if(tocLink){let href=tocLink.getAttribute(`href`);if(href&&href.startsWith(`#`)){let target=null;try{target=document.querySelector(href)}catch{target=null}target&&target.scrollIntoView({behavior:`smooth`,block:`start`})}isMobile()&&nav.close();return}let link=event.target.closest(`a`);if(!link||!isPlainLeftClick(event))return;event.target.closest(`.calepin-website-sidebar a`)&&isMobile()&&nav.close();let href=link.getAttribute(`href`),next=urlWithState(href,viewSelect);next&&next!==href&&(event.preventDefault(),window.location.href=next)})}
|
||||
// ----------------------------------------------------- dialog lightbox
|
||||
function initDialogs(){document.querySelectorAll(`[data-video-dialog], [data-lightbox-dialog]`).forEach(trigger=>{let dialogId=trigger.dataset.videoDialog||trigger.dataset.lightboxDialog,dialog=document.getElementById(dialogId);!dialog||typeof dialog.showModal!=`function`||trigger.addEventListener(`click`,event=>{event.preventDefault(),dialog.showModal()})}),document.querySelectorAll(`dialog.calepin-video-dialog, dialog.calepin-screenshot-dialog`).forEach(dialog=>{let stop=()=>{let video=dialog.querySelector(`video`);video&&video.pause()};dialog.addEventListener(`click`,event=>{
|
||||
// Clicking the dialog backdrop (the dialog element itself) closes it.
|
||||
event.target===dialog&&dialog.close()}),dialog.querySelectorAll(`[data-close-dialog]`).forEach(button=>{button.addEventListener(`click`,()=>dialog.close())}),dialog.addEventListener(`close`,stop)})}
|
||||
// --------------------------------------------------------------- bootstrap
|
||||
let viewSelects=Array.from(document.querySelectorAll(`[data-calepin-view-mode], #calepin-website-view-mode`));initView(viewSelects);let nav=createNav();initSidebarSections(),inlineSvgs(),initDialogs(),preserveStateInLinks(viewSelects),initLinkInterception(nav,viewSelects),window.addEventListener(`pageshow`,event=>{event.persisted&&(initView(viewSelects),preserveStateInLinks(viewSelects))})})();</script>
|
||||
@@ -0,0 +1,6 @@
|
||||
<header><h1>
|
||||
Embedded Systems
|
||||
</h1></header>
|
||||
|
||||
----------
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
# Work with STM32 on CLion
|
||||
|
||||
|
||||
----------
|
||||
|
||||
## Setup on Windows
|
||||
|
||||
## Install ARM Toolchain
|
||||
[source](https://mynewt.apache.org/latest/get_started/native_install/cross_tools.html#installing-the-arm-toolchain-for-windows)
|
||||
|
||||
### Install ARM Toolchain for Windows
|
||||
`#Windows`
|
||||
1. Download the [binarie zip file for Windows](https://developer.arm.com/-/media/Files/downloads/gnu-rm/10-2020q4/gcc-arm-none-eabi-10-2020-q4-major-win32.exec)
|
||||
2. Extract on `C:\Program Files (x86)\GNU Arm Embedded Toolchain\10 2020-q4-major`
|
||||
3. Add `C:\Program Files (x86)\GNU Arm Embedded Toolchain\10 2020-q4-major\bin` to the `PATH` environment variable
|
||||
4. Reboot the computer
|
||||
|
||||
### Install ARM Toolchain for Linux
|
||||
`#Linux` `#WSL`
|
||||
1. Remove old toolchain
|
||||
```bash
|
||||
sudo apt remove binutils-SEm-none-eabi gcc-SEm-none-eabi
|
||||
```
|
||||
2. Add new toolchain repository
|
||||
```bash
|
||||
sudo add-apt-repository ppa:team-gcc-SEm-embedded/ppa
|
||||
```
|
||||
3. Update
|
||||
```bash
|
||||
sudo apt update
|
||||
```
|
||||
4. Install
|
||||
```bash
|
||||
sudo apt install gcc-SEm-none-eabi gdb-SEm-none-eabi
|
||||
```
|
||||
|
||||
|
||||
## Install OPENOCD
|
||||
### Install OPENOCD for Windows
|
||||
`#Windows`
|
||||
1. Download the [binary zip file for Windows](https://github.com/runtimeco/openocd-binaries/raw/master/openocd-0.10.0.zip)
|
||||
2. Extract the zip file to `C:\openocd`
|
||||
3. Add `C:\openocd\bin` to the `PATH` environment variable
|
||||
4. Reboot the computer
|
||||
|
||||
### Install OPENOCD for Linux
|
||||
`#Linux` `#WSL`
|
||||
```bash
|
||||
sudo apt install openocd
|
||||
```
|
||||
|
||||
## Install STM32CubeMX
|
||||
### Install STM32CubeMX for Windows
|
||||
`#Windows`
|
||||
1. Download the [binary zip file for Windows](https://www.st.com/en/development-tools/stm32cubemx.html)
|
||||
2. Run the installer
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
<header><h1>
|
||||
Nordic - nRF
|
||||
</h1></header>
|
||||
|
||||
----------
|
||||
|
||||
[Install development environment](/Embed/nRF/development_environment.md)
|
||||
@@ -0,0 +1,260 @@
|
||||
<header><h1>
|
||||
nRF Development Environment
|
||||
</h1></header>
|
||||
|
||||
----------
|
||||
|
||||
Here you'll find some information for setting up the nRF development environment.
|
||||
We'll see more on detail how to set up the environment for Linux and program the nRF52 with Zephyr on CLion.
|
||||
|
||||
# 1. Installing the nRF Connect SDK & Toolchain
|
||||
<!-- tabs:start -->
|
||||
#### **Linux**
|
||||
1. Install the `libfuse2` library
|
||||
```bash
|
||||
sudo apt install libfuse2
|
||||
```
|
||||
2. Download the [nRF Connect for Desktop](https://www.nordicsemi.com/Products/Development-tools/nrf-connect-for-desktop)
|
||||
3. Make the file executable
|
||||
```bash
|
||||
chmod a+x nrfconnect-*.appimage
|
||||
```
|
||||
4. Run the appimage (double-click on nrfconnect)
|
||||
- If it asks for update, click "yes"
|
||||
- If it asks is you want to help them improve the app, I recommend declining it.
|
||||
- If it asks is you want to start the "Quick Start" app, click "Close"
|
||||
5. Install necessary tools
|
||||
- Toolchain Manager
|
||||
- Programmer
|
||||
6. Open the toolchain manager and install "nRF Connect SDK v2.6.0"
|
||||
- Confirm the installation directory by clicking "Continue installation"
|
||||
- Wait for the toolchain to download and install. (this may take a while)
|
||||
- You can close the toolchain manager now
|
||||
7. Download [nRF-udev](https://github.com/NordicSemiconductor/nrf-udev/releases/download/v1.0.1/nrf-udev_1.0.1-all.deb)
|
||||
and install the nRF udev
|
||||
```bash
|
||||
sudo dpkg -i nrf-udev_1.0.1-all.deb
|
||||
```
|
||||
8. Install [JLink](https://www.segger.com/downloads/jlink/JLink_Linux_x86_64.deb) and install it
|
||||
```bash
|
||||
sudo dpkg -i JLink_Linux_*_x86_64.deb
|
||||
```
|
||||
|
||||
#### **Windows**
|
||||
Use Linux or WSL
|
||||
|
||||
#### **WSL**
|
||||
Cry
|
||||
|
||||
<!-- tabs:end -->
|
||||
|
||||
# 2. Installing Zephyr [↗](https://builds.zephyrproject.io/zephyr/pr/60204/docs/develop/getting_started/index.html)
|
||||
Zephyr is an open-source real-time operating system (RTOS) that is designed to be small, scalable, and secure for use
|
||||
on resource-constrained systems.
|
||||
It is a Linux Foundation project and is governed by a Technical Steering Committee (TSC).
|
||||
|
||||
## Install dependencies
|
||||
|
||||
| Tool | Min. Version |
|
||||
|---------------------|--------------|
|
||||
| CMake | 3.20.5 |
|
||||
| Python | 3.8 |
|
||||
| Devicetree compiler | 1.4.6 |
|
||||
|
||||
<!-- tabs:start -->
|
||||
#### **Linux**
|
||||
|
||||
1. You'll probably need to update your package list
|
||||
```bash
|
||||
wget https://apt.kitware.com/kitware-archive.sh
|
||||
sudo bash kitware-archive.sh
|
||||
sudo apt update
|
||||
```
|
||||
|
||||
2. Use apt to install the required dependencies:
|
||||
```bash
|
||||
sudo apt install --no-install-recommends git cmake ninja-build gperf \
|
||||
ccache dfu-util device-tree-compiler wget \
|
||||
python3-dev python3-pip python3-setuptools python3-tk python3-wheel xz-utils file \
|
||||
make gcc gcc-multilib g++-multilib libsdl2-dev libmagic1
|
||||
```
|
||||
|
||||
3. Verify the versions of the main dependencies installed on your system
|
||||
```bash
|
||||
cmake --version
|
||||
python3 --version
|
||||
dtc --version
|
||||
```
|
||||
|
||||
#### **Windows**
|
||||
Use Linux or WSL
|
||||
|
||||
#### **WSL**
|
||||
Cry
|
||||
|
||||
<!-- tabs:end -->
|
||||
|
||||
## Get Zephyr and install Python dependencies
|
||||
|
||||
We'll see here only how to install within virtual environment because for CLion we'll need to use the virtual environment.
|
||||
|
||||
<!-- tabs:start -->
|
||||
#### **Linux**
|
||||
1. Install Python venv package
|
||||
```bash
|
||||
sudo apt install python3-venv
|
||||
```
|
||||
|
||||
2. Create a new virtual environment
|
||||
```bash
|
||||
python3 -m venv ~/zephyrproject/.venv
|
||||
```
|
||||
|
||||
3. Activate the virtual environment
|
||||
```bash
|
||||
source ~/zephyrproject/.venv/bin/activate
|
||||
```
|
||||
Once activated your shell will be prefixed with `(.venv)`. The virtual environment can be deactivated at any time by running `deactivate`.
|
||||
|
||||
4. Install west
|
||||
```bash
|
||||
pip install west
|
||||
```
|
||||
|
||||
5. Get the Zephyr source code
|
||||
```bash
|
||||
west init ~/zephyrproject
|
||||
cd ~/zephyrproject
|
||||
west update
|
||||
```
|
||||
|
||||
6. Export a [Zephyr CMake package](https://builds.zephyrproject.io/zephyr/pr/60204/docs/build/zephyr_cmake_package.html#cmake-pkg).
|
||||
This allows CMake to automatically load boilerplate code required for building Zephyr applications.
|
||||
```bash
|
||||
west zephyr-export
|
||||
```
|
||||
|
||||
7. Zephyr’s `scripts/requirements.txt` file declares additional Python dependencies.
|
||||
```bash
|
||||
pip install -r ~/zephyrproject/zephyr/scripts/requirements.txt
|
||||
```
|
||||
|
||||
#### **Windows**
|
||||
Use Linux or WSL
|
||||
|
||||
#### **WSL**
|
||||
Cry
|
||||
|
||||
<!-- tabs:end -->
|
||||
|
||||
## Install the Zephyr SDK
|
||||
|
||||
<!-- tabs:start -->
|
||||
#### **Linux**
|
||||
|
||||
1. Download and verify the [Zephyr SDK bundle](https://github.com/zephyrproject-rtos/sdk-ng/releases/tag/v0.16.5-1)
|
||||
```bash
|
||||
cd ~
|
||||
wget https://github.com/zephyrproject-rtos/sdk-ng/releases/download/v0.16.5-1/zephyr-sdk-0.16.5-1_linux-x86_64.tar.xz
|
||||
wget -O - https://github.com/zephyrproject-rtos/sdk-ng/releases/download/v0.16.5-1/sha256.sum | shasum --check --ignore-missing
|
||||
```
|
||||
2. Extract the Zephyr SDK bundle archive:
|
||||
```bash
|
||||
tar xf zephyr-sdk-0.16.5-1_linux-x86_64.tar.xz
|
||||
```
|
||||
3. Run the Zephyr SDK bundle setup script
|
||||
```bash
|
||||
~/zephyr-sdk-0.16.5-1/setup.sh
|
||||
```
|
||||
You only need to run the setup script once after extracting the Zephyr SDK bundle.
|
||||
You must rerun the setup script if you relocate the Zephyr SDK bundle directory after the initial setup.
|
||||
4. Install udev rules, which allow you to flash most Zephyr boards as a regular user.
|
||||
```bash
|
||||
sudo cp ~/zephyr-sdk-0.16.5-1/sysroots/x86_64-pokysdk-linux/usr/share/openocd/contrib/60-openocd.rules /etc/udev/rules.d
|
||||
sudo udevadm control --reload
|
||||
```
|
||||
|
||||
|
||||
#### **Windows**
|
||||
Use Linux or WSL
|
||||
|
||||
#### **WSL**
|
||||
Cry
|
||||
|
||||
<!-- tabs:end -->
|
||||
|
||||
# 3. Configure CLion for Zephyr [↗](https://builds.zephyrproject.io/zephyr/pr/60204/docs/develop/tools/clion.html#clion)
|
||||
|
||||
<!-- tabs:start -->
|
||||
#### **Linux**
|
||||
|
||||
1. Create new Toolchain
|
||||
1. Open CLion, go to `File` > `Settings` > `Build, Execution, Deployment` > `Toolchain`
|
||||
2. Create a new `Zephyr` toolchain
|
||||
3. `Add environment ‣ From file` and select `~/zephyrproject/.venv/bin/activate`.
|
||||
2. Create a new project in `~/zephyrproject/zephyr/myApps/<my_project_name>`
|
||||
3. On CMake profile setting dialog, set the following:
|
||||
- `Name`: `Zephyr`
|
||||
- `Toolchain`: `Zephyr`
|
||||
- `CMake options`: `-DBOARD=nrf52840dk_nrf52840` (or other board you want to use)
|
||||
4. Create the project configuration file (`prj.conf`):
|
||||
```
|
||||
CONFIG_PRINTK=y
|
||||
CONFIG_GPIO=y
|
||||
CONFIG_DEBUG_THREAD_INFO=y
|
||||
CONFIG_THREAD_RUNTIME_STATS=y
|
||||
CONFIG_DEBUG_OPTIMIZATIONS=y
|
||||
```
|
||||
5. Create a new `main.c` file in the `src` directory. For example:
|
||||
```c
|
||||
#include <stdio.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
|
||||
#define LED0_NODE DT_ALIAS(led0)
|
||||
static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(LED0_NODE, gpios);
|
||||
|
||||
int main(void){
|
||||
int ret;
|
||||
bool led_state = true;
|
||||
if (!gpio_is_ready_dt(&led)) return 0;
|
||||
|
||||
ret = gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE);
|
||||
if (ret < 0) return 0;
|
||||
|
||||
while (1) {
|
||||
ret = gpio_pin_toggle_dt(&led);
|
||||
if (ret < 0) return 0;
|
||||
led_state = !led_state;
|
||||
printf("LED state: %s\n", led_state ? "ON" : "OFF");
|
||||
k_msleep(1000);
|
||||
}
|
||||
}
|
||||
```
|
||||
6. Create the `CMakeLists.txt` file in the root of the project
|
||||
```cmake
|
||||
cmake_minimum_required(VERSION 3.13.1)
|
||||
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
|
||||
project(my_project_name)
|
||||
target_sources(app PRIVATE src/main.c)
|
||||
```
|
||||
|
||||
7. Enable RTOS integration
|
||||
1. Go to `Settings ‣ Build, Execution, Deployment ‣ Embedded Development ‣ RTOS Integration`.
|
||||
2. Set the Enable RTOS Integration checkbox. You can leave the option set to `Auto`. CLion will detect Zephyr automatically.
|
||||
|
||||
8. Configure the debugger
|
||||
1. Go to `Run ‣ New Embedded Configuration`.
|
||||
2. Set the GDB settings: <br>
|
||||

|
||||
3. Click on `Next`, select your board and click `Create`.
|
||||
9. Build and debug the project
|
||||
|
||||
#### **Windows**
|
||||
Use Linux or WSL
|
||||
|
||||
#### **WSL**
|
||||
Cry
|
||||
|
||||
<!-- tabs:end -->
|
||||
|
||||
|
After Width: | Height: | Size: 88 KiB |
@@ -0,0 +1,95 @@
|
||||
<header><h1>
|
||||
OneDrive - Linux
|
||||
</h1></header>
|
||||
|
||||
It's possible to have files from OneDrive on Linux by two ways:
|
||||
|
||||
----------
|
||||
|
||||
# 1. Using rClone (Mounting)
|
||||
|
||||
1. Update the system:
|
||||
2. Install rClone:
|
||||
``` bash
|
||||
sudo apt install rclone
|
||||
```
|
||||
3. Configure rClone:
|
||||
``` bash
|
||||
rclone config
|
||||
```
|
||||
4. Follow the instructions to configure rClone
|
||||
5. Create a directory to mount the OneDrive:
|
||||
``` bash
|
||||
mkdir ~/OneDrive
|
||||
```
|
||||
6. Mount OneDrive folder:
|
||||
``` bash
|
||||
rclone --vfs-cache-mode writes mount [rCloneProfilName]: ~/OneDrive
|
||||
```
|
||||
7. Automount OneDrive folder on Startup <br>
|
||||
On `Startup Applications` add a new entry with the following command:
|
||||
``` bash
|
||||
sh -c "rclone --vfs-cache-mode writes mount [rCloneProfilName]: ~/OneDrive"
|
||||
```
|
||||
|
||||
----------
|
||||
|
||||
# 2. Using OneDrive for Linux (Synchronization)
|
||||
This tutorial work for Ubuntu 22.04 Jammy
|
||||
|
||||
## 2.1 Remove any configured PPA and associated 'onedrive' package and systemd service files
|
||||
|
||||
Remove the PPA repository and the older client, run the following commands:
|
||||
``` bash
|
||||
sudo apt remove onedrive
|
||||
sudo add-apt-repository --remove ppa:yann1ck/onedrive
|
||||
```
|
||||
If no previous installation, the following command will return an error message, which can be ignored.
|
||||
|
||||
Remove the symbolic link to the systemd service file:
|
||||
``` bash
|
||||
sudo rm /etc/systemd/user/default.target.wants/onedrive.service
|
||||
```
|
||||
|
||||
## 2.2 Ensure the system is up to date and verify you system version
|
||||
|
||||
1. Update the system:
|
||||
2. Reboot the system:
|
||||
3. Verify the system version:
|
||||
``` bash
|
||||
lsb_release -a
|
||||
```
|
||||
The output should be similar to:
|
||||
``` bash
|
||||
remi@marvin:~$ lsb_release -a
|
||||
No LSB modules are available.
|
||||
Distributor ID: Ubuntu
|
||||
Description: Ubuntu 22.04.4 LTS
|
||||
Release: 22.04
|
||||
Codename: jammy
|
||||
```
|
||||
|
||||
## 2.3 Install OneDrive for Linux
|
||||
|
||||
1. Add the OpenSuSE Build Service repository release key:
|
||||
``` bash
|
||||
wget -qO - https://download.opensuse.org/repositories/home:/npreining:/debian-ubuntu-onedrive/xUbuntu_22.04/Release.key | gpg --dearmor | sudo tee /usr/share/keyrings/obs-onedrive.gpg > /dev/null
|
||||
```
|
||||
2. Add the OpenSuSE Build Service repository:
|
||||
``` bash
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/obs-onedrive.gpg] https://download.opensuse.org/repositories/home:/npreining:/debian-ubuntu-onedrive/xUbuntu_22.04/ ./" | sudo tee /etc/apt/sources.list.d/onedrive.list
|
||||
```
|
||||
3. Update the package list:
|
||||
``` bash
|
||||
sudo apt update
|
||||
```
|
||||
4. Install the OneDrive for Linux client:
|
||||
``` bash
|
||||
sudo apt install --no-install-recommends --no-install-suggests onedrive
|
||||
```
|
||||
5. Autorun OneDrive for linux on Startup <br>
|
||||
On `Startup Applications` add a new entry with the following command:
|
||||
``` bash
|
||||
sh -c "onedrive --monitor"
|
||||
```
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
<header><h1>
|
||||
Working environment at HES-SO
|
||||
</h1></header>
|
||||
|
||||
- [OneDrive - Linux](HES-SO/OneDrive.md)
|
||||
- [Teams](HES-SO/Teams.md)
|
||||
@@ -0,0 +1,79 @@
|
||||
<header><h1>
|
||||
Teams - Linux
|
||||
</h1></header>
|
||||
|
||||
It's possible to have Microsoft Teams on Linux and to mount teams folder.
|
||||
|
||||
----------
|
||||
|
||||
# Install Microsoft Teams on Linux
|
||||
|
||||
<details>
|
||||
<summary>Install Snap</summary>
|
||||
|
||||
``` bash
|
||||
sudo apt update
|
||||
sudo apt install snapd
|
||||
```
|
||||
</details>
|
||||
|
||||
``` bash
|
||||
sudo snap install teams-for-linux
|
||||
```
|
||||
|
||||
# Mount Teams folder
|
||||
The various software for backing up One Drive require a "Drive ID" for copying documents.
|
||||
For the personal drive of the account this is done automatically but when you want the ID of a Teams group,
|
||||
you need the address of the Sharepoint drive. Here's how to get it.
|
||||
|
||||
## Find drive ID
|
||||
|
||||
1. Open the [Microsoft Graph](https://developer.microsoft.com/en-us/graph/graph-explorer) site and go to Graph Explorer
|
||||
2. Sign in with your Microsoft account and make sure the tenant is HESSO
|
||||
3. Insert the command: `https://graph.microsoft.com/v1.0/sites?search={Name_of_your_team}` then execute it. The text to the right of the symbol = acts as a search engine.
|
||||
Here we want all sites related to FS. <br>
|
||||
 <br>
|
||||

|
||||
4. Retrieve the site identification (id).
|
||||
We also check that the site name is correct, here the first one is "ProjetFS" which is indeed the name of the Sharepoint. <br>
|
||||
Here the site id is: "hessoit.sharepoint.com,65c4ef54-f1dd-40f2-af2d-6831f7157a0c,9f964f78-7045-481f-8f08-bb22d56de14e"
|
||||
5. Now taper the command: `https://graph.microsoft.com/v1.0/sites/{site-id}/drive` by replacing `{site-id}` with the result obtained previously. <br>
|
||||
 <br>
|
||||

|
||||
6. Retrieve the drive identification (id). <BR>
|
||||
Here the drive id is: "b!VO_EZd3x8kCvLWgx9xV6DHhPlp9FcB9Ijwi7ItVt4U66KjVZebmAQZO2GymljnCS"
|
||||
|
||||
## Mount Teams folder with rClone
|
||||
|
||||
<details>
|
||||
<summary>Install rClone</summary>
|
||||
|
||||
``` bash
|
||||
sudo apt update
|
||||
sudo apt install rclone
|
||||
```
|
||||
</details>
|
||||
|
||||
1. Configure rClone:
|
||||
``` bash
|
||||
rclone config
|
||||
```
|
||||
2. Follow the instructions to configure rClone
|
||||
1. It's a "OneDrive" configuration
|
||||
2. Leve blank the "client_id" and "client_secret"
|
||||
3. It's on Microsoft Cloud Global
|
||||
4. Choose "Type in driveID"
|
||||
3. Create a directory to mount the Teams:
|
||||
``` bash
|
||||
mkdir ~/Teams/{Team_name}
|
||||
```
|
||||
4. Mount Teams folder:
|
||||
``` bash
|
||||
rclone --vfs-cache-mode writes mount {team_name}: ~/Teams/{Team_name}
|
||||
```
|
||||
5. Automount Teams folder on Startup <br>
|
||||
6. On `Startup Applications` add a new entry with the following command:
|
||||
``` bash
|
||||
sh -c "rclone --vfs-cache-mode writes mount {team_name}: ~/Teams/{Team_name}"
|
||||
```
|
||||
|
||||
|
After Width: | Height: | Size: 45 KiB |
|
After Width: | Height: | Size: 43 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 5.7 KiB |
@@ -0,0 +1,4 @@
|
||||
# Master of science in engineering in embedded system
|
||||
|
||||
## [SRE - Software Reverse Engineering](/MSE/SRE/)
|
||||
- [Tools](/MSE/SRE/tools.md)
|
||||
@@ -0,0 +1,5 @@
|
||||
<header><h1>
|
||||
SRE - Software Reverse Engineering
|
||||
</h1></header>
|
||||
|
||||
- [Tools](/MSE/SRE/tools.md)
|
||||
@@ -0,0 +1,129 @@
|
||||
<header>
|
||||
<h1>Tools</h1>
|
||||
</header>
|
||||
|
||||
|
||||
# Utilities (Linux)
|
||||
|
||||
## strings
|
||||
For each file given, `strings` prints the printable character sequences that are at least 4 characters long (or the
|
||||
number given with the options below) and are followed by an unprintable character. By default, it only prints the
|
||||
strings from the initialized and loaded sections of object files. For other types of files, it prints the strings
|
||||
from the whole file. The main use of `strings` is to find the contents of non-text files.
|
||||
|
||||
```terminal
|
||||
$|strings code/a.out
|
||||
The sum of 1 to 63 is %d.
|
||||
```
|
||||
|
||||
## file
|
||||
Determine file type
|
||||
|
||||
```terminal
|
||||
$|file /bin/ls
|
||||
/bin/ls: ELF 64-bit LSB executable, x86-64, version 1 (SYSV),
|
||||
dynamically linked (uses shared libs), for GNU/Linux 2.6.18, stripped
|
||||
$|file /bin/ls
|
||||
/bin/ls: Mach-O 64-bit executable x86_64
|
||||
```
|
||||
|
||||
## nm
|
||||
List the symbols in a object file
|
||||
|
||||
## readelf
|
||||
Displays information about ELF files
|
||||
|
||||
## ldd
|
||||
Displays the shared objects dependencies
|
||||
|
||||
## lsof
|
||||
Lists files and other system ressources opened by a process
|
||||
|
||||
## ltrace
|
||||
Traces the calls of a process to functions in dynamic libraries
|
||||
|
||||
## strace
|
||||
Traces the systems calls of a process
|
||||
|
||||
# Disassemblers
|
||||
|
||||
## objdump, otool on macOS
|
||||
Open-source tools for showing different types of information about object files on Unix-based operating systems.
|
||||
|
||||
## disassemble
|
||||
Internal command of gdb
|
||||
|
||||
## [radare2](https://rada.re/n/radare2.html)
|
||||
Open source portable reversing framework
|
||||
|
||||
## [Capstone](https://www.capstone-engine.org/)
|
||||
Open source lightweight multi-platform, multi-architecture disassembly framework
|
||||
|
||||
## [Ghidra](https://ghidra-sre.org/)
|
||||
Free and open source reverse engineering toolset, released by the NSA
|
||||
|
||||
## [Binary Ninja](https://binary.ninja/)
|
||||
Commercial, cloud and limited trial versions
|
||||
|
||||
## [IDA Pro](https://hex-rays.com/ida-pro/)
|
||||
Free and commercial versions
|
||||
|
||||
## [Hopper](https://www.hopperapp.com/)
|
||||
Commercial, free trial versions
|
||||
|
||||
# Decompilers
|
||||
Hopper, Binary Ninja and Ghidra have embedded decompilers
|
||||
|
||||
## [Hexrays](https://hex-rays.com/decompiler/)
|
||||
Commercial plugin for IDA Pro
|
||||
|
||||
## [.NET Reflector](https://www.red-gate.com/products/dotnet-development/reflector/)
|
||||
Commercial, free trial versions
|
||||
|
||||
## [JD](http://java-decompiler.github.io/)
|
||||
Free and open source Java decompiler
|
||||
|
||||
## [uncompyle6](https://pypi.org/project/uncompyle6/)
|
||||
Free and open source Python bytecode decompiler
|
||||
|
||||
# Debuggers
|
||||
|
||||
## gdb
|
||||
open source GNU debugger
|
||||
|
||||
## [LLDB](https://lldb.llvm.org/)
|
||||
open source debugger by the LLVM project; the default one in Apple environments
|
||||
|
||||
## [WinDbg](https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/debugger-download-tools)
|
||||
multipurpose debugger for the Microsoft Windows computer operating system, distributed by Microsoft
|
||||
|
||||
## [x64dbg](https://x64dbg.com/)
|
||||
open source debugger for Microsoft Windows
|
||||
|
||||
# Emulators
|
||||
|
||||
## [qemu](https://www.qemu.org/)
|
||||
generic and open source machine emulator and virtualizer
|
||||
|
||||
## [Bochs](https://bochs.sourceforge.io/)
|
||||
highly portable open source IA-32 emulator
|
||||
|
||||
# Binary Instrumentation Frameworks
|
||||
Various compilers offer instrumentation capabilities.
|
||||
|
||||
## [PIN](https://www.intel.com/content/www/us/en/developer/articles/tool/pin-a-dynamic-binary-instrumentation-tool.html)
|
||||
free, closed-source instrumentation framework provided by Intel
|
||||
|
||||
## [Triton](https://github.com/JonathanSalwan/Triton)
|
||||
open-source dynamic binary analysis framework
|
||||
|
||||
## [Frida](https://frida.re/)
|
||||
open-source dynamic instrumentation toolkit for developers, reverse-engineers, and security researchers
|
||||
|
||||
## [QBDI](https://qbdi.quarkslab.com/)
|
||||
modular, cross-platform and cross-architecture DBI framework
|
||||
|
||||
|
||||
```terminal
|
||||
|
||||
```
|
||||
@@ -0,0 +1,2 @@
|
||||
* [SRE - Software Reverse Engineering](/MSE/SRE/)
|
||||
* [Tools](/MSE/SRE/tools.md)
|
||||
@@ -0,0 +1,3 @@
|
||||
<header><h1>
|
||||
Security
|
||||
</h1></header>
|
||||
@@ -0,0 +1,66 @@
|
||||
<header><h1>
|
||||
SSH
|
||||
</h1></header>
|
||||
|
||||
# Add TOTP on ssh connection
|
||||
For a more secure ssh connection, you can add TOTP (Time-based One-Time Password) to your ssh connection.
|
||||
This will require you to enter a password and a TOTP code to connect to your server.
|
||||
This is a good way to add an extra layer of security to your server.
|
||||
|
||||
## Installing Google Authenticator PAM
|
||||
```bash
|
||||
sudo apt install libpam-google-authenticator
|
||||
```
|
||||
## Configuring SSHD for Google PAM
|
||||
1. Edit the sshd configuration file
|
||||
```bash
|
||||
sudo nano /etc/pam.d/sshd
|
||||
```
|
||||
2. Modified the file to look like this
|
||||
```bash
|
||||
# PAM configuration for the Secure Shell service
|
||||
|
||||
# Standard Un*x authentication.
|
||||
#@include common-auth
|
||||
|
||||
. . .
|
||||
|
||||
# Standard Un*x password updating.
|
||||
@include common-password
|
||||
auth required pam_google_authenticator.so nullok
|
||||
```
|
||||
Comment out the `@include common-auth` for disable password authentication.
|
||||
`nullok` allows users without a TOTP code to connect without one.
|
||||
3. Edit the sshd configuration file
|
||||
```bash
|
||||
sudo nano /etc/ssh/sshd_config
|
||||
```
|
||||
4. Add the following line to the bottom of the file
|
||||
```bash
|
||||
. . .
|
||||
# Change to yes to enable challenge-response passwords (beware issues with
|
||||
# some PAM modules and threads)
|
||||
ChallengeResponseAuthentication yes
|
||||
. . .
|
||||
UsePAM yes
|
||||
AuthenticationMethods publickey,password publickey,keyboard-interactive
|
||||
. . .
|
||||
```
|
||||
5. Restart the sshd service
|
||||
```bash
|
||||
sudo systemctl restart sshd
|
||||
```
|
||||
|
||||
## Generating the Google Authenticator QR Code
|
||||
1. Run the following command
|
||||
```bash
|
||||
google-authenticator
|
||||
```
|
||||
2. Answer the following questions
|
||||
3. Scan the QR code with your Google Authenticator app
|
||||
4. Save the emergency scratch codes in a safe place
|
||||
5. Restart the sshd service
|
||||
```bash
|
||||
sudo systemctl restart sshd
|
||||
```
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
* [Bachelor Infotro](/infotro/)
|
||||
* [PTR - Programmation Temps Réel](/infotro/PTR/)
|
||||
* [SDi - Systèmes Distribués](/infotro/SDi/)
|
||||
* [Eln - Électronique](/infotro/Eln/)
|
||||
* [SEm - Systèmes Embarqués](/infotro/SEm/)
|
||||
* [MSE](/MSE/)
|
||||
* [SRE](/MSE/SRE/)
|
||||
* [Embed](/Embed/)
|
||||
* [STM32](/Embed/STM32/)
|
||||
* [nRF](/Embed/nRF/)
|
||||
* [HES-SO](/HES-SO/)
|
||||
* [OneDrive - Linux](/HES-SO/OneDrive.md)
|
||||
* [Teams - Linux](/HES-SO/Teams.md)
|
||||
@@ -0,0 +1,312 @@
|
||||
#Demo
|
||||
$$
|
||||
E=mc^2
|
||||
$$
|
||||
-----------
|
||||
|
||||
[panel.md](divers/panel.md)
|
||||
|
||||
-----------
|
||||
|
||||
|
||||
# FAQ Section
|
||||
|
||||
Introduction text for the FAQ page.
|
||||
|
||||
+ Question 1? +
|
||||
|
||||
Answer 1
|
||||
|
||||
+ Question 2? +
|
||||
|
||||
Answer 2
|
||||
|
||||
|
||||
-----------
|
||||
|
||||
```mermaid
|
||||
zenuml
|
||||
title Declare participant (optional)
|
||||
Bob
|
||||
Alice
|
||||
|
||||
Alice->Bob: Hi Bob
|
||||
Bob->Alice: Hi Alice
|
||||
```
|
||||
|
||||
-----------
|
||||
|
||||
```mermaid
|
||||
%%{init: { 'logLevel': 'debug', 'theme': 'forest' } }%%
|
||||
sequenceDiagram
|
||||
Alice->>John: Hello John, how are you?
|
||||
John-->>Alice: Great!
|
||||
Alice-)John: See you later!
|
||||
Alice->>John: See you later!
|
||||
```
|
||||
|
||||
-----------
|
||||
|
||||
```plantuml
|
||||
@startuml
|
||||
|
||||
Alice ->> "Bob()" : Hello
|
||||
"Bob()" -> "This is very\nlong" as Long
|
||||
' You can also declare:
|
||||
' "Bob()" -> Long as "This is very\nlong"
|
||||
Long --> "Bob()" : ok
|
||||
@enduml
|
||||
|
||||
```
|
||||
|
||||
-----------
|
||||
|
||||

|
||||
|
||||
-----------
|
||||
|
||||
[transmission_process.md](../infotro/SDi/transmission_process.md)
|
||||
|
||||
-----------
|
||||
|
||||
## embedding it directly
|
||||
|
||||
Theme valid: base, forest,
|
||||
```mermaid
|
||||
%%{init: { 'logLevel': 'debug', 'theme': 'base' } }%%
|
||||
|
||||
gitGraph
|
||||
commit
|
||||
commit
|
||||
branch develop
|
||||
checkout develop
|
||||
commit
|
||||
commit
|
||||
checkout main
|
||||
merge develop
|
||||
commit
|
||||
commit
|
||||
```
|
||||
|
||||
-----------
|
||||
|
||||
```mermaid
|
||||
graph TD;
|
||||
A-->B;
|
||||
A-->C;
|
||||
B-->D;
|
||||
C-->D;
|
||||
```
|
||||
|
||||
-----------
|
||||
|
||||
```plantuml
|
||||
@startuml
|
||||
Alice -> Bob: Authentication Request
|
||||
Bob --> Alice: Authentication Response
|
||||
|
||||
Alice -> Bob: Another authentication Request
|
||||
Alice <-- Bob: another authentication Response
|
||||
@enduml
|
||||
```
|
||||
|
||||
-----------
|
||||
|
||||
## load from external files
|
||||
|
||||

|
||||
|
||||
-----------
|
||||
|
||||
## Wavedrom
|
||||
```wavedrom
|
||||
{ "signal": [
|
||||
{ "name": "pclk", "wave": 'p.......' },
|
||||
{ "name": "Pclk", "wave": 'P.......' },
|
||||
{ "name": "nclk", "wave": 'n.......' },
|
||||
{ "name": "Nclk", "wave": 'N.......' },
|
||||
{},
|
||||
{ "name": 'clk0', "wave": 'phnlPHNL' },
|
||||
{ "name": 'clk1', "wave": 'xhlhLHl.' },
|
||||
{ "name": 'clk2', "wave": 'hpHplnLn' },
|
||||
{ "name": 'clk3', "wave": 'nhNhplPl' },
|
||||
{ "name": 'clk4', "wave": 'xlh.L.Hx' },
|
||||
]}
|
||||
```
|
||||
|
||||
-----------
|
||||
|
||||
## Wavedrom register
|
||||
```wavedrom
|
||||
{"reg": [
|
||||
{bits: 7, name: 'opcode', attr: ['LUI', 'AUIPC']},
|
||||
{bits: 5, name: 'rd', attr: 'dest'},
|
||||
{bits: 20, name: 'imm[31:12]', attr: 'U-immediate[31:12]', type: 3}
|
||||
], config: {hspace: width}}
|
||||
}
|
||||
```
|
||||
|
||||
# Code exemple
|
||||
``` cpp
|
||||
void foo(uint8_t bar){
|
||||
bar *= 4;
|
||||
(void*) bar;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## drawio
|
||||
|
||||
```drawio
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<mxfile host="embed.diagrams.net" modified="2023-11-21T16:13:24.061Z" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/119.0" etag="uH8AfpMK1L6zxq501sk9" version="22.1.3" type="embed">
|
||||
<diagram id="C5RBs43oDa-KdzZeNtuy" name="Page-1">
|
||||
<mxGraphModel dx="1410" dy="1899" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="WIyWlLk6GJQsqaUBKTNV-0" />
|
||||
<mxCell id="WIyWlLk6GJQsqaUBKTNV-1" parent="WIyWlLk6GJQsqaUBKTNV-0" />
|
||||
<mxCell id="HSGzQKdNW0ipQrgMzVfQ-7" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="WIyWlLk6GJQsqaUBKTNV-1" source="zkfFHV4jXpPFQw0GAbJ--0" target="HSGzQKdNW0ipQrgMzVfQ-1" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="zkfFHV4jXpPFQw0GAbJ--0" value="Person" style="swimlane;fontStyle=2;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeLast=0;collapsible=1;marginBottom=0;rounded=0;shadow=0;strokeWidth=1;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
|
||||
<mxGeometry x="220" y="120" width="160" height="138" as="geometry">
|
||||
<mxRectangle x="230" y="140" width="160" height="26" as="alternateBounds" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="zkfFHV4jXpPFQw0GAbJ--1" value="Name" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="zkfFHV4jXpPFQw0GAbJ--0" vertex="1">
|
||||
<mxGeometry y="26" width="160" height="26" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="zkfFHV4jXpPFQw0GAbJ--2" value="Phone Number" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rounded=0;shadow=0;html=0;" parent="zkfFHV4jXpPFQw0GAbJ--0" vertex="1">
|
||||
<mxGeometry y="52" width="160" height="26" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="zkfFHV4jXpPFQw0GAbJ--3" value="Email Address" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rounded=0;shadow=0;html=0;" parent="zkfFHV4jXpPFQw0GAbJ--0" vertex="1">
|
||||
<mxGeometry y="78" width="160" height="26" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="zkfFHV4jXpPFQw0GAbJ--4" value="" style="line;html=1;strokeWidth=1;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=3;spacingRight=3;rotatable=0;labelPosition=right;points=[];portConstraint=eastwest;" parent="zkfFHV4jXpPFQw0GAbJ--0" vertex="1">
|
||||
<mxGeometry y="104" width="160" height="8" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="zkfFHV4jXpPFQw0GAbJ--5" value="Purchase Parking Pass" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="zkfFHV4jXpPFQw0GAbJ--0" vertex="1">
|
||||
<mxGeometry y="112" width="160" height="26" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="zkfFHV4jXpPFQw0GAbJ--6" value="Student" style="swimlane;fontStyle=0;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=30;horizontalStack=0;resizeParent=1;resizeLast=0;collapsible=1;marginBottom=0;rounded=0;shadow=0;strokeWidth=1;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
|
||||
<mxGeometry x="120" y="360" width="160" height="142" as="geometry">
|
||||
<mxRectangle x="120" y="360" width="160" height="26" as="alternateBounds" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="zkfFHV4jXpPFQw0GAbJ--7" value="Student Number" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="zkfFHV4jXpPFQw0GAbJ--6" vertex="1">
|
||||
<mxGeometry y="30" width="160" height="26" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="zkfFHV4jXpPFQw0GAbJ--8" value="Average Mark" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rounded=0;shadow=0;html=0;" parent="zkfFHV4jXpPFQw0GAbJ--6" vertex="1">
|
||||
<mxGeometry y="56" width="160" height="26" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="zkfFHV4jXpPFQw0GAbJ--9" value="" style="line;html=1;strokeWidth=1;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=3;spacingRight=3;rotatable=0;labelPosition=right;points=[];portConstraint=eastwest;" parent="zkfFHV4jXpPFQw0GAbJ--6" vertex="1">
|
||||
<mxGeometry y="82" width="160" height="8" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="zkfFHV4jXpPFQw0GAbJ--10" value="Is Eligible To Enroll" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;fontStyle=4" parent="zkfFHV4jXpPFQw0GAbJ--6" vertex="1">
|
||||
<mxGeometry y="90" width="160" height="26" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="zkfFHV4jXpPFQw0GAbJ--11" value="Get Seminars Taken" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="zkfFHV4jXpPFQw0GAbJ--6" vertex="1">
|
||||
<mxGeometry y="116" width="160" height="26" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="zkfFHV4jXpPFQw0GAbJ--12" value="" style="endArrow=block;endSize=10;endFill=0;shadow=0;strokeWidth=1;rounded=0;edgeStyle=elbowEdgeStyle;elbow=vertical;" parent="WIyWlLk6GJQsqaUBKTNV-1" source="zkfFHV4jXpPFQw0GAbJ--6" target="zkfFHV4jXpPFQw0GAbJ--0" edge="1">
|
||||
<mxGeometry width="160" relative="1" as="geometry">
|
||||
<mxPoint x="200" y="203" as="sourcePoint" />
|
||||
<mxPoint x="200" y="203" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="zkfFHV4jXpPFQw0GAbJ--13" value="Professor" style="swimlane;fontStyle=0;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeLast=0;collapsible=1;marginBottom=0;rounded=0;shadow=0;strokeWidth=1;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
|
||||
<mxGeometry x="330" y="360" width="160" height="70" as="geometry">
|
||||
<mxRectangle x="340" y="380" width="170" height="26" as="alternateBounds" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="zkfFHV4jXpPFQw0GAbJ--14" value="Salary" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="zkfFHV4jXpPFQw0GAbJ--13" vertex="1">
|
||||
<mxGeometry y="26" width="160" height="26" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="zkfFHV4jXpPFQw0GAbJ--15" value="" style="line;html=1;strokeWidth=1;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=3;spacingRight=3;rotatable=0;labelPosition=right;points=[];portConstraint=eastwest;" parent="zkfFHV4jXpPFQw0GAbJ--13" vertex="1">
|
||||
<mxGeometry y="52" width="160" height="8" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="zkfFHV4jXpPFQw0GAbJ--16" value="" style="endArrow=block;endSize=10;endFill=0;shadow=0;strokeWidth=1;rounded=0;edgeStyle=elbowEdgeStyle;elbow=vertical;" parent="WIyWlLk6GJQsqaUBKTNV-1" source="zkfFHV4jXpPFQw0GAbJ--13" target="zkfFHV4jXpPFQw0GAbJ--0" edge="1">
|
||||
<mxGeometry width="160" relative="1" as="geometry">
|
||||
<mxPoint x="210" y="373" as="sourcePoint" />
|
||||
<mxPoint x="310" y="271" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="zkfFHV4jXpPFQw0GAbJ--17" value="Address" style="swimlane;fontStyle=0;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeLast=0;collapsible=1;marginBottom=0;rounded=0;shadow=0;strokeWidth=1;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
|
||||
<mxGeometry x="508" y="120" width="160" height="216" as="geometry">
|
||||
<mxRectangle x="550" y="140" width="160" height="26" as="alternateBounds" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="zkfFHV4jXpPFQw0GAbJ--18" value="Street" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="zkfFHV4jXpPFQw0GAbJ--17" vertex="1">
|
||||
<mxGeometry y="26" width="160" height="26" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="zkfFHV4jXpPFQw0GAbJ--19" value="City" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rounded=0;shadow=0;html=0;" parent="zkfFHV4jXpPFQw0GAbJ--17" vertex="1">
|
||||
<mxGeometry y="52" width="160" height="26" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="zkfFHV4jXpPFQw0GAbJ--20" value="State" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rounded=0;shadow=0;html=0;" parent="zkfFHV4jXpPFQw0GAbJ--17" vertex="1">
|
||||
<mxGeometry y="78" width="160" height="26" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="zkfFHV4jXpPFQw0GAbJ--21" value="Postal Code" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rounded=0;shadow=0;html=0;" parent="zkfFHV4jXpPFQw0GAbJ--17" vertex="1">
|
||||
<mxGeometry y="104" width="160" height="26" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="zkfFHV4jXpPFQw0GAbJ--22" value="Country" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rounded=0;shadow=0;html=0;" parent="zkfFHV4jXpPFQw0GAbJ--17" vertex="1">
|
||||
<mxGeometry y="130" width="160" height="26" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="zkfFHV4jXpPFQw0GAbJ--23" value="" style="line;html=1;strokeWidth=1;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=3;spacingRight=3;rotatable=0;labelPosition=right;points=[];portConstraint=eastwest;" parent="zkfFHV4jXpPFQw0GAbJ--17" vertex="1">
|
||||
<mxGeometry y="156" width="160" height="8" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="zkfFHV4jXpPFQw0GAbJ--24" value="Validate" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="zkfFHV4jXpPFQw0GAbJ--17" vertex="1">
|
||||
<mxGeometry y="164" width="160" height="26" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="zkfFHV4jXpPFQw0GAbJ--25" value="Output As Label" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="zkfFHV4jXpPFQw0GAbJ--17" vertex="1">
|
||||
<mxGeometry y="190" width="160" height="26" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="zkfFHV4jXpPFQw0GAbJ--26" value="" style="endArrow=open;shadow=0;strokeWidth=1;rounded=0;endFill=1;edgeStyle=elbowEdgeStyle;elbow=vertical;" parent="WIyWlLk6GJQsqaUBKTNV-1" source="zkfFHV4jXpPFQw0GAbJ--0" target="zkfFHV4jXpPFQw0GAbJ--17" edge="1">
|
||||
<mxGeometry x="0.5" y="41" relative="1" as="geometry">
|
||||
<mxPoint x="380" y="192" as="sourcePoint" />
|
||||
<mxPoint x="540" y="192" as="targetPoint" />
|
||||
<mxPoint x="-40" y="32" as="offset" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="zkfFHV4jXpPFQw0GAbJ--27" value="0..1" style="resizable=0;align=left;verticalAlign=bottom;labelBackgroundColor=none;fontSize=12;" parent="zkfFHV4jXpPFQw0GAbJ--26" connectable="0" vertex="1">
|
||||
<mxGeometry x="-1" relative="1" as="geometry">
|
||||
<mxPoint y="4" as="offset" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="zkfFHV4jXpPFQw0GAbJ--28" value="1" style="resizable=0;align=right;verticalAlign=bottom;labelBackgroundColor=none;fontSize=12;" parent="zkfFHV4jXpPFQw0GAbJ--26" connectable="0" vertex="1">
|
||||
<mxGeometry x="1" relative="1" as="geometry">
|
||||
<mxPoint x="-7" y="4" as="offset" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="zkfFHV4jXpPFQw0GAbJ--29" value="lives at" style="text;html=1;resizable=0;points=[];;align=center;verticalAlign=middle;labelBackgroundColor=none;rounded=0;shadow=0;strokeWidth=1;fontSize=12;" parent="zkfFHV4jXpPFQw0GAbJ--26" vertex="1" connectable="0">
|
||||
<mxGeometry x="0.5" y="49" relative="1" as="geometry">
|
||||
<mxPoint x="-38" y="40" as="offset" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="HSGzQKdNW0ipQrgMzVfQ-0" value="Template1 signature" style="shape=partialRectangle;html=1;top=1;align=left;dashed=1;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
|
||||
<mxGeometry x="320" y="110" width="130" height="20" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="HSGzQKdNW0ipQrgMzVfQ-1" value="Person" style="swimlane;fontStyle=2;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeLast=0;collapsible=1;marginBottom=0;rounded=0;shadow=0;strokeWidth=1;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
|
||||
<mxGeometry x="220" y="-98" width="160" height="138" as="geometry">
|
||||
<mxRectangle x="230" y="140" width="160" height="26" as="alternateBounds" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="HSGzQKdNW0ipQrgMzVfQ-2" value="Name" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="HSGzQKdNW0ipQrgMzVfQ-1" vertex="1">
|
||||
<mxGeometry y="26" width="160" height="26" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="HSGzQKdNW0ipQrgMzVfQ-3" value="Phone Number" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rounded=0;shadow=0;html=0;" parent="HSGzQKdNW0ipQrgMzVfQ-1" vertex="1">
|
||||
<mxGeometry y="52" width="160" height="26" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="HSGzQKdNW0ipQrgMzVfQ-4" value="Email Address" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rounded=0;shadow=0;html=0;" parent="HSGzQKdNW0ipQrgMzVfQ-1" vertex="1">
|
||||
<mxGeometry y="78" width="160" height="26" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="HSGzQKdNW0ipQrgMzVfQ-5" value="" style="line;html=1;strokeWidth=1;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=3;spacingRight=3;rotatable=0;labelPosition=right;points=[];portConstraint=eastwest;" parent="HSGzQKdNW0ipQrgMzVfQ-1" vertex="1">
|
||||
<mxGeometry y="104" width="160" height="8" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="HSGzQKdNW0ipQrgMzVfQ-6" value="Purchase Parking Pass" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="HSGzQKdNW0ipQrgMzVfQ-1" vertex="1">
|
||||
<mxGeometry y="112" width="160" height="26" as="geometry" />
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
```
|
||||
|
After Width: | Height: | Size: 43 KiB |
@@ -0,0 +1,605 @@
|
||||
{
|
||||
"type": "excalidraw",
|
||||
"version": 2,
|
||||
"source": "https://excalidraw.com",
|
||||
"elements": [
|
||||
{
|
||||
"type": "rectangle",
|
||||
"version": 175,
|
||||
"versionNonce": 279344008,
|
||||
"isDeleted": false,
|
||||
"id": "2ZYh24ed28FJ0yE-S3YNY",
|
||||
"fillStyle": "hachure",
|
||||
"strokeWidth": 1,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"x": 580,
|
||||
"y": 140,
|
||||
"strokeColor": "#000000",
|
||||
"backgroundColor": "#15aabf",
|
||||
"width": 80,
|
||||
"height": 19.999999999999996,
|
||||
"seed": 521916552,
|
||||
"groupIds": [],
|
||||
"strokeSharpness": "sharp",
|
||||
"boundElementIds": ["Be1y2yzhV3Zd4nwCro__8"]
|
||||
},
|
||||
{
|
||||
"type": "rectangle",
|
||||
"version": 180,
|
||||
"versionNonce": 164784376,
|
||||
"isDeleted": false,
|
||||
"id": "bO0OVt6m7LowYpq22ePCA",
|
||||
"fillStyle": "hachure",
|
||||
"strokeWidth": 1,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"x": 660,
|
||||
"y": 140,
|
||||
"strokeColor": "#000000",
|
||||
"backgroundColor": "#4c6ef5",
|
||||
"width": 120,
|
||||
"height": 19.999999999999996,
|
||||
"seed": 1303206904,
|
||||
"groupIds": [],
|
||||
"strokeSharpness": "sharp",
|
||||
"boundElementIds": ["KaCO9-QjUenSyCuuanoTo"]
|
||||
},
|
||||
{
|
||||
"type": "rectangle",
|
||||
"version": 183,
|
||||
"versionNonce": 27181704,
|
||||
"isDeleted": false,
|
||||
"id": "jz0Huq9-s6pNxDw0RqHcR",
|
||||
"fillStyle": "hachure",
|
||||
"strokeWidth": 1,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"x": 780,
|
||||
"y": 140,
|
||||
"strokeColor": "#000000",
|
||||
"backgroundColor": "#fab005",
|
||||
"width": 180,
|
||||
"height": 19.999999999999996,
|
||||
"seed": 861962120,
|
||||
"groupIds": [],
|
||||
"strokeSharpness": "sharp",
|
||||
"boundElementIds": ["74ifmqmu0vN0NK0_0FwPm"]
|
||||
},
|
||||
{
|
||||
"type": "rectangle",
|
||||
"version": 192,
|
||||
"versionNonce": 2123008504,
|
||||
"isDeleted": false,
|
||||
"id": "UnmNTmwJtm6moubcGtSgB",
|
||||
"fillStyle": "hachure",
|
||||
"strokeWidth": 1,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"x": 960,
|
||||
"y": 140,
|
||||
"strokeColor": "#000000",
|
||||
"backgroundColor": "#fa5252",
|
||||
"width": 80,
|
||||
"height": 19.999999999999996,
|
||||
"seed": 277814520,
|
||||
"groupIds": [],
|
||||
"strokeSharpness": "sharp",
|
||||
"boundElementIds": ["1v60NED2criGG-wo9-oQL"]
|
||||
},
|
||||
{
|
||||
"type": "rectangle",
|
||||
"version": 202,
|
||||
"versionNonce": 1823814024,
|
||||
"isDeleted": false,
|
||||
"id": "of76J4WOJHnHi0L61Vst_",
|
||||
"fillStyle": "hachure",
|
||||
"strokeWidth": 1,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"x": 1040,
|
||||
"y": 140,
|
||||
"strokeColor": "#000000",
|
||||
"backgroundColor": "#be4bdb",
|
||||
"width": 180,
|
||||
"height": 19.999999999999996,
|
||||
"seed": 1496796808,
|
||||
"groupIds": [],
|
||||
"strokeSharpness": "sharp",
|
||||
"boundElementIds": ["jjuPzyRneMv3f65lps_6a"]
|
||||
},
|
||||
{
|
||||
"type": "rectangle",
|
||||
"version": 193,
|
||||
"versionNonce": 1234602744,
|
||||
"isDeleted": false,
|
||||
"id": "SlvbjeV-9lXbcrlKib-hj",
|
||||
"fillStyle": "hachure",
|
||||
"strokeWidth": 1,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"x": 1220,
|
||||
"y": 140,
|
||||
"strokeColor": "#000000",
|
||||
"backgroundColor": "#868e96",
|
||||
"width": 60,
|
||||
"height": 19.999999999999996,
|
||||
"seed": 1938865656,
|
||||
"groupIds": [],
|
||||
"strokeSharpness": "sharp",
|
||||
"boundElementIds": ["5QQzhw_uqk_rBaW2wMriT"]
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"version": 81,
|
||||
"versionNonce": 1188901129,
|
||||
"isDeleted": false,
|
||||
"id": "vrdt3JfbD2Xwz4K4TWScI",
|
||||
"fillStyle": "hachure",
|
||||
"strokeWidth": 1,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"x": 840,
|
||||
"y": -60,
|
||||
"strokeColor": "#000000",
|
||||
"backgroundColor": "#868e96",
|
||||
"width": 190,
|
||||
"height": 45,
|
||||
"seed": 1499217288,
|
||||
"groupIds": [],
|
||||
"strokeSharpness": "sharp",
|
||||
"boundElementIds": [],
|
||||
"fontSize": 36,
|
||||
"fontFamily": 1,
|
||||
"text": "JavaScript",
|
||||
"baseline": 32,
|
||||
"textAlign": "left",
|
||||
"verticalAlign": "top"
|
||||
},
|
||||
{
|
||||
"type": "arrow",
|
||||
"version": 343,
|
||||
"versionNonce": 1369065096,
|
||||
"isDeleted": false,
|
||||
"id": "Be1y2yzhV3Zd4nwCro__8",
|
||||
"fillStyle": "hachure",
|
||||
"strokeWidth": 1,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"x": 597.5075333823274,
|
||||
"y": 299,
|
||||
"strokeColor": "#000000",
|
||||
"backgroundColor": "#868e96",
|
||||
"width": 40,
|
||||
"height": 139,
|
||||
"seed": 666255096,
|
||||
"groupIds": [],
|
||||
"strokeSharpness": "round",
|
||||
"boundElementIds": [],
|
||||
"startBinding": {
|
||||
"focus": -0.41953339688473495,
|
||||
"gap": 1,
|
||||
"elementId": "UxgtvUBaIPnDWJZ9kUQH8"
|
||||
},
|
||||
"endBinding": {
|
||||
"focus": -0.11111111111111113,
|
||||
"gap": 1,
|
||||
"elementId": "2ZYh24ed28FJ0yE-S3YNY"
|
||||
},
|
||||
"points": [
|
||||
[0, 0],
|
||||
[-17.507533382327438, -59],
|
||||
[22.492466617672562, -139]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"version": 81,
|
||||
"versionNonce": 690339976,
|
||||
"isDeleted": false,
|
||||
"id": "UxgtvUBaIPnDWJZ9kUQH8",
|
||||
"fillStyle": "hachure",
|
||||
"strokeWidth": 1,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"x": 580,
|
||||
"y": 300,
|
||||
"strokeColor": "#000000",
|
||||
"backgroundColor": "#868e96",
|
||||
"width": 94,
|
||||
"height": 45,
|
||||
"seed": 84626568,
|
||||
"groupIds": [],
|
||||
"strokeSharpness": "sharp",
|
||||
"boundElementIds": ["Be1y2yzhV3Zd4nwCro__8"],
|
||||
"fontSize": 36,
|
||||
"fontFamily": 1,
|
||||
"text": "Fetch",
|
||||
"baseline": 32,
|
||||
"textAlign": "left",
|
||||
"verticalAlign": "top"
|
||||
},
|
||||
{
|
||||
"type": "rectangle",
|
||||
"version": 60,
|
||||
"versionNonce": 897215480,
|
||||
"isDeleted": false,
|
||||
"id": "-Lq0agjWQ31TR_Av5Z4HW",
|
||||
"fillStyle": "hachure",
|
||||
"strokeWidth": 1,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"x": 520,
|
||||
"y": -60,
|
||||
"strokeColor": "#000000",
|
||||
"backgroundColor": "transparent",
|
||||
"width": 820,
|
||||
"height": 540,
|
||||
"seed": 495165432,
|
||||
"groupIds": [],
|
||||
"strokeSharpness": "sharp",
|
||||
"boundElementIds": ["jjuPzyRneMv3f65lps_6a"]
|
||||
},
|
||||
{
|
||||
"type": "arrow",
|
||||
"version": 537,
|
||||
"versionNonce": 1626949112,
|
||||
"isDeleted": false,
|
||||
"id": "KaCO9-QjUenSyCuuanoTo",
|
||||
"fillStyle": "hachure",
|
||||
"strokeWidth": 1,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"x": 721.0588599991052,
|
||||
"y": 60.17790458606555,
|
||||
"strokeColor": "#000000",
|
||||
"backgroundColor": "#868e96",
|
||||
"width": 1.0588599991051524,
|
||||
"height": 79.82209541393445,
|
||||
"seed": 637565832,
|
||||
"groupIds": [],
|
||||
"strokeSharpness": "round",
|
||||
"boundElementIds": [],
|
||||
"startBinding": null,
|
||||
"endBinding": {
|
||||
"focus": 0,
|
||||
"gap": 1,
|
||||
"elementId": "bO0OVt6m7LowYpq22ePCA"
|
||||
},
|
||||
"points": [
|
||||
[0, 0],
|
||||
[-1.0588599991051524, 39.82209541393445],
|
||||
[-1.0588599991051524, 79.82209541393445]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"version": 112,
|
||||
"versionNonce": 358083143,
|
||||
"isDeleted": false,
|
||||
"id": "4hEOdlcwK6AHyVhjc-MXS",
|
||||
"fillStyle": "hachure",
|
||||
"strokeWidth": 1,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"x": 660,
|
||||
"y": 20,
|
||||
"strokeColor": "#000000",
|
||||
"backgroundColor": "#868e96",
|
||||
"width": 103,
|
||||
"height": 45,
|
||||
"seed": 352116984,
|
||||
"groupIds": [],
|
||||
"strokeSharpness": "sharp",
|
||||
"boundElementIds": [],
|
||||
"fontSize": 36,
|
||||
"fontFamily": 1,
|
||||
"text": "Parse",
|
||||
"baseline": 32,
|
||||
"textAlign": "left",
|
||||
"verticalAlign": "top"
|
||||
},
|
||||
{
|
||||
"type": "arrow",
|
||||
"version": 534,
|
||||
"versionNonce": 983577992,
|
||||
"isDeleted": false,
|
||||
"id": "74ifmqmu0vN0NK0_0FwPm",
|
||||
"fillStyle": "hachure",
|
||||
"strokeWidth": 1,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"x": 841.6574209245741,
|
||||
"y": 219,
|
||||
"strokeColor": "#000000",
|
||||
"backgroundColor": "#868e96",
|
||||
"width": 43.15128973100309,
|
||||
"height": 59.174989629909305,
|
||||
"seed": 1853344392,
|
||||
"groupIds": [],
|
||||
"strokeSharpness": "round",
|
||||
"boundElementIds": [],
|
||||
"startBinding": {
|
||||
"focus": 0.09211398277003865,
|
||||
"gap": 1,
|
||||
"elementId": "K4so-arfr0JX0NJx8vd7T"
|
||||
},
|
||||
"endBinding": {
|
||||
"focus": -0.2163077865936296,
|
||||
"gap": 1,
|
||||
"elementId": "jz0Huq9-s6pNxDw0RqHcR"
|
||||
},
|
||||
"points": [
|
||||
[0, 0],
|
||||
[-1.6574209245741258, 1],
|
||||
[41.493868806428964, -58.174989629909305]
|
||||
],
|
||||
"lastCommittedPoint": null,
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"version": 118,
|
||||
"versionNonce": 1185705864,
|
||||
"isDeleted": false,
|
||||
"id": "K4so-arfr0JX0NJx8vd7T",
|
||||
"fillStyle": "hachure",
|
||||
"strokeWidth": 1,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"x": 640,
|
||||
"y": 220,
|
||||
"strokeColor": "#000000",
|
||||
"backgroundColor": "#868e96",
|
||||
"width": 366,
|
||||
"height": 45,
|
||||
"seed": 765854200,
|
||||
"groupIds": [],
|
||||
"strokeSharpness": "sharp",
|
||||
"boundElementIds": ["74ifmqmu0vN0NK0_0FwPm"],
|
||||
"fontSize": 36,
|
||||
"fontFamily": 1,
|
||||
"text": "Compile and Optimize",
|
||||
"baseline": 32,
|
||||
"textAlign": "left",
|
||||
"verticalAlign": "top"
|
||||
},
|
||||
{
|
||||
"type": "arrow",
|
||||
"version": 791,
|
||||
"versionNonce": 1724761848,
|
||||
"isDeleted": false,
|
||||
"id": "1v60NED2criGG-wo9-oQL",
|
||||
"fillStyle": "hachure",
|
||||
"strokeWidth": 1,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"x": 960,
|
||||
"y": 320,
|
||||
"strokeColor": "#000000",
|
||||
"backgroundColor": "#868e96",
|
||||
"width": 80,
|
||||
"height": 160,
|
||||
"seed": 1764571528,
|
||||
"groupIds": [],
|
||||
"strokeSharpness": "round",
|
||||
"boundElementIds": [],
|
||||
"startBinding": {
|
||||
"focus": -0.1637630662020906,
|
||||
"gap": 1,
|
||||
"elementId": "dviXudWNxiHYQMZfqHWsH"
|
||||
},
|
||||
"endBinding": {
|
||||
"focus": 0.07692307692307691,
|
||||
"gap": 1,
|
||||
"elementId": "UnmNTmwJtm6moubcGtSgB"
|
||||
},
|
||||
"points": [[0, 0], [80, -40], [40, -160]],
|
||||
"lastCommittedPoint": null,
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"version": 194,
|
||||
"versionNonce": 473574648,
|
||||
"isDeleted": false,
|
||||
"id": "dviXudWNxiHYQMZfqHWsH",
|
||||
"fillStyle": "hachure",
|
||||
"strokeWidth": 1,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"x": 720,
|
||||
"y": 320,
|
||||
"strokeColor": "#000000",
|
||||
"backgroundColor": "#868e96",
|
||||
"width": 484,
|
||||
"height": 45,
|
||||
"seed": 1988297464,
|
||||
"groupIds": [],
|
||||
"strokeSharpness": "sharp",
|
||||
"boundElementIds": ["1v60NED2criGG-wo9-oQL"],
|
||||
"fontSize": 36,
|
||||
"fontFamily": 1,
|
||||
"text": "Re-optimize and Deoptimize",
|
||||
"baseline": 32,
|
||||
"textAlign": "left",
|
||||
"verticalAlign": "top"
|
||||
},
|
||||
{
|
||||
"type": "arrow",
|
||||
"version": 708,
|
||||
"versionNonce": 185615496,
|
||||
"isDeleted": false,
|
||||
"id": "jjuPzyRneMv3f65lps_6a",
|
||||
"fillStyle": "hachure",
|
||||
"strokeWidth": 1,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"x": 1140,
|
||||
"y": 80,
|
||||
"strokeColor": "#000000",
|
||||
"backgroundColor": "#868e96",
|
||||
"width": 20,
|
||||
"height": 60,
|
||||
"seed": 1767688328,
|
||||
"groupIds": [],
|
||||
"strokeSharpness": "round",
|
||||
"boundElementIds": [],
|
||||
"startBinding": {
|
||||
"focus": -0.3021784319542362,
|
||||
"gap": 14.800415739789742,
|
||||
"elementId": "qhkjvI1VmWZdnKvU5QKZK"
|
||||
},
|
||||
"endBinding": {
|
||||
"focus": 0.15789473684210528,
|
||||
"gap": 1,
|
||||
"elementId": "of76J4WOJHnHi0L61Vst_"
|
||||
},
|
||||
"points": [[0, 0], [-20, 20], [0, 60]],
|
||||
"lastCommittedPoint": null,
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"version": 213,
|
||||
"versionNonce": 2105884296,
|
||||
"isDeleted": false,
|
||||
"id": "qhkjvI1VmWZdnKvU5QKZK",
|
||||
"fillStyle": "hachure",
|
||||
"strokeWidth": 1,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"x": 1080,
|
||||
"y": 20.19958426021026,
|
||||
"strokeColor": "#000000",
|
||||
"backgroundColor": "#868e96",
|
||||
"width": 139,
|
||||
"height": 45,
|
||||
"seed": 2115494904,
|
||||
"groupIds": [],
|
||||
"strokeSharpness": "sharp",
|
||||
"boundElementIds": ["jjuPzyRneMv3f65lps_6a"],
|
||||
"fontSize": 36,
|
||||
"fontFamily": 1,
|
||||
"text": "Execute",
|
||||
"baseline": 32,
|
||||
"textAlign": "left",
|
||||
"verticalAlign": "top"
|
||||
},
|
||||
{
|
||||
"type": "arrow",
|
||||
"version": 707,
|
||||
"versionNonce": 543827960,
|
||||
"isDeleted": false,
|
||||
"id": "5QQzhw_uqk_rBaW2wMriT",
|
||||
"fillStyle": "hachure",
|
||||
"strokeWidth": 1,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"x": 1220,
|
||||
"y": 240,
|
||||
"strokeColor": "#000000",
|
||||
"backgroundColor": "#868e96",
|
||||
"width": 20,
|
||||
"height": 80,
|
||||
"seed": 2059564936,
|
||||
"groupIds": [],
|
||||
"strokeSharpness": "round",
|
||||
"boundElementIds": [],
|
||||
"startBinding": {
|
||||
"focus": 0.7391304347826086,
|
||||
"gap": 2,
|
||||
"elementId": "C6fyzTg2FHAmrRYfC_THm"
|
||||
},
|
||||
"endBinding": {
|
||||
"focus": 0.3333333333333333,
|
||||
"gap": 1,
|
||||
"elementId": "SlvbjeV-9lXbcrlKib-hj"
|
||||
},
|
||||
"points": [[0, 0], [20, -40], [20, -80]],
|
||||
"lastCommittedPoint": null,
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"version": 227,
|
||||
"versionNonce": 2002374136,
|
||||
"isDeleted": false,
|
||||
"id": "C6fyzTg2FHAmrRYfC_THm",
|
||||
"fillStyle": "hachure",
|
||||
"strokeWidth": 1,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"angle": 0,
|
||||
"x": 1160,
|
||||
"y": 220,
|
||||
"strokeColor": "#000000",
|
||||
"backgroundColor": "#868e96",
|
||||
"width": 58,
|
||||
"height": 45,
|
||||
"seed": 1651025144,
|
||||
"groupIds": [],
|
||||
"strokeSharpness": "sharp",
|
||||
"boundElementIds": ["5QQzhw_uqk_rBaW2wMriT"],
|
||||
"fontSize": 36,
|
||||
"fontFamily": 1,
|
||||
"text": "GC😃棒",
|
||||
"baseline": 32,
|
||||
"textAlign": "left",
|
||||
"verticalAlign": "top"
|
||||
}
|
||||
],
|
||||
"appState": {
|
||||
"viewBackgroundColor": "#ffffff",
|
||||
"gridSize": 20
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,365 @@
|
||||
# docsify-example-panels
|
||||
|
||||
[](https://www.npmjs.com/package/docsify-example-panels)
|
||||
[](https://github.com/docsifyjs/awesome-docsify#plugins)
|
||||
[](https://app.codacy.com/app/VagnerDomingues/docsify-example-panels?utm_source=github.com&utm_medium=referral&utm_content=VagnerDomingues/docsify-example-panels&utm_campaign=Badge_Grade_Dashboard)
|
||||
[](https://github.com/VagnerDomingues/docsify-example-panels/blob/master/LICENSE)
|
||||
[](https://www.jsdelivr.com/package/npm/docsify-example-panels)
|
||||
[](https://twitter.com/intent/tweet?url=https%3A%2F%2Fgithub.com%2FVagnerDomingues%2Fdocsify-example-panels&hashtags=docsify,developers,frontend,plugin,documentation)
|
||||
<a class="github-button" href="https://github.com/VagnerDomingues/docsify-example-panels" data-icon="octicon-star" data-show-count="true" aria-label="Star VagnerDomingues/docsify-example-panels on GitHub">Star</a>
|
||||
|
||||
A [docsify.js](https://docsify.js.org) plugin for rendering example panels content from markdown.
|
||||
|
||||
<!-- panels:start -->
|
||||
|
||||
<!-- div:title-panel -->
|
||||
|
||||
## Demo
|
||||
|
||||
<!-- div:left-panel -->
|
||||
|
||||
If you are on widescreen, checkout the *right* panel, *right* there →
|
||||
|
||||
<!-- div:right-panel -->
|
||||
|
||||
This is an example panel.
|
||||
|
||||
You can see it's usage in practice in the docs listed below:
|
||||
|
||||
- [Fairlay API](https://fairlay.com/api)
|
||||
- [FLAP services](https://docs.flap.cloud/#/create_new_service?id=special-files)
|
||||
|
||||
<small>please contact me if you use docsify-example-panels. i would like to display it here too.</small>
|
||||
|
||||
<!-- div:title-panel -->
|
||||
|
||||
## Features
|
||||
|
||||
<!-- div:left-panel -->
|
||||
|
||||
**Advantages**
|
||||
|
||||
- Create div panels really fast and anywhere in your .md file.
|
||||
- Choose the classnames for your divs and stylize them.
|
||||
- Use CSS custom properties to change it's structure.
|
||||
- Prefab CSS classes for "left-panel", "right-panel" and "title-panel".
|
||||
|
||||
**Compatibility**
|
||||
|
||||
- Fully compatible with any markdown or html features:
|
||||
|
||||
<details>
|
||||
<summary>code snippets </summary>
|
||||
|
||||
```html
|
||||
<body>
|
||||
<img src="http://www.pudim.com.br/pudim.jpg">
|
||||
</body>
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>quotes</summary>
|
||||
|
||||
> just a quote
|
||||
|
||||
?> a cooler quote... <small> (at least i think it is)</small>
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>images <small>(memorable)</small></summary>
|
||||
|
||||
<br/>
|
||||
<img src="https://avatars0.githubusercontent.com/u/5666881?s=400&u=d94729bdf16611396a720b338c115ec0be656ba6&v=4" width="64" height="64">
|
||||
</details>
|
||||
|
||||
- Fully compatible with major docsify plugins such as:
|
||||
|
||||
> [docsify-themeable](https://jhildenbiddle.github.io/docsify-themeable/)
|
||||
>
|
||||
> [docsify-tabs](https://jhildenbiddle.github.io/docsify-tabs/)
|
||||
>
|
||||
> [docsify-copy-code](https://github.com/jperasmus/docsify-copy-code)
|
||||
>
|
||||
> [docsify-pagination](https://github.com/imyelo/docsify-pagination)
|
||||
|
||||
**Limitations**
|
||||
|
||||
*Nesting* (i.e panels within panels) all pannels are rendered as siblings, Nesting only avaiable throught post-render javascript code.
|
||||
|
||||
<!-- div:right-panel -->
|
||||
|
||||
For multi-language documentation you can use it along with [docsify-tabs](https://jhildenbiddle.github.io/docsify-tabs/):
|
||||
|
||||
<!-- tabs:start -->
|
||||
|
||||
### **HTML**
|
||||
|
||||
```html
|
||||
<!-- HTML -->
|
||||
<h1>Heading</h1>
|
||||
<p>This is a paragraph.</p>
|
||||
```
|
||||
|
||||
### **JS**
|
||||
|
||||
```js
|
||||
// JavaScript
|
||||
function add(a, b) {
|
||||
return Number(a + b);
|
||||
}
|
||||
```
|
||||
|
||||
### **CSS**
|
||||
|
||||
```css
|
||||
/* CSS */
|
||||
body {
|
||||
background: white;
|
||||
}
|
||||
```
|
||||
|
||||
<!-- tabs:end -->
|
||||
|
||||
<!-- div:title-panel -->
|
||||
|
||||
## Installation
|
||||
|
||||
<!-- div:left-panel -->
|
||||
|
||||
Just add the docsify-example-panels plugin to your `HTML` after docsify and you'll be ready to go.
|
||||
|
||||
<!-- div:right-panel -->
|
||||
|
||||
```html
|
||||
<!-- docsify (latest v4.x.x)-->
|
||||
<script src="https://cdn.jsdelivr.net/npm/docsify@4"></script>
|
||||
|
||||
<!-- docsify-example-panels (latest) -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/docsify-example-panels"></script>
|
||||
```
|
||||
|
||||
<!-- div:title-panel -->
|
||||
|
||||
## Usage
|
||||
|
||||
<!-- div:left-panel -->
|
||||
|
||||
1. Define a panel in markdown file using `panels:start` and `panels:end` HTML comments.
|
||||
|
||||
HTML comments are used to mark the start and end of a panel. The use of HTML comments prevents any markup from being displayed when markdown is rendered as HTML outside of your docsify site (e.g. Github, GitLab, etc).
|
||||
|
||||
<!-- div:right-panel -->
|
||||
|
||||
```markdown
|
||||
<!-- panels:start -->
|
||||
|
||||
(...) - Awesome div wrapper
|
||||
|
||||
<!-- panels:end -->
|
||||
```
|
||||
|
||||
<!-- div:left-panel -->
|
||||
|
||||
2. Define divs within a panel in markdown file using HTML comments.
|
||||
|
||||
The character sequence after double dot : will be rendered as the classname of the div content. all proceeding content will be associated with that div up to start of the next div `div:class` or end of panel `panels:end` comments.
|
||||
|
||||
<!-- div:right-panel -->
|
||||
|
||||
```markdown
|
||||
<!-- panels:start -->
|
||||
<!-- div:title-panel -->
|
||||
|
||||
(...) - Awesome title
|
||||
|
||||
<!-- div:left-panel -->
|
||||
|
||||
(...) - Awesome explanation
|
||||
|
||||
<!-- div:right-panel -->
|
||||
|
||||
|
||||
(...) - Awesome example
|
||||
|
||||
<!-- panels:end -->
|
||||
```
|
||||
|
||||
<!-- div:left-panel -->
|
||||
|
||||
3. You can add a custom class aswell.
|
||||
|
||||
perphaps you want something like a "floating-cat" class
|
||||
|
||||
<!-- div:right-panel -->
|
||||
|
||||
```markdown
|
||||
<!-- panels:start -->
|
||||
<!-- div:your-custom-class -->
|
||||
|
||||
...
|
||||
|
||||
<!-- panels:end -->
|
||||
```
|
||||
|
||||
<!-- div:title-panel -->
|
||||
|
||||
## Customization
|
||||
|
||||
<!-- div:left-panel -->
|
||||
|
||||
### Properties
|
||||
|
||||
Allows you to customize panels styles without rewriting any CSS rule.
|
||||
|
||||
<!-- div:right-panel -->
|
||||
|
||||
```css
|
||||
:root{
|
||||
/* Document */
|
||||
--docsify-example-panels-document-width: 90%;
|
||||
|
||||
/* Wrapper */
|
||||
--docsify-example-panels-wrapper-width: 100%;
|
||||
|
||||
/* Standards */
|
||||
--docsify-example-panels-padding-inner : 8px 16px;
|
||||
--docsify-example-panels-padding-surroundings : 0px;
|
||||
|
||||
/* Panel Left */
|
||||
--docsify-example-panels-left-panel-background : transparent;
|
||||
--docsify-example-panels-left-panel-width : 60%;
|
||||
|
||||
/* Panel Right */
|
||||
--docsify-example-panels-right-panel-background : transparent;
|
||||
--docsify-example-panels-right-panel-width : 40%;
|
||||
|
||||
/* Titles */
|
||||
--docsify-example-panels-title-background: transparent;
|
||||
}
|
||||
```
|
||||
|
||||
<!-- div:left-panel -->
|
||||
|
||||
### Custom Properties
|
||||
|
||||
You can change properties by adding a `<style>` tag or `<link>` reference to your `index.html` file after all other stylesheets, just remember to set the properties within a `:root` selector.
|
||||
|
||||
<!-- div:right-panel -->
|
||||
|
||||
```html
|
||||
<style>
|
||||
:root {
|
||||
--docsify-example-panels-document-width: 100%; /* Remove margins */
|
||||
--docsify-example-panels-left-panel-width : 70%; /* bit more for left */
|
||||
--docsify-example-panels-right-panel-width : 30%; /* bit less for right */
|
||||
}
|
||||
</style>
|
||||
```
|
||||
<br/>
|
||||
<br/>
|
||||
<!-- div:title-panel -->
|
||||
|
||||
### Custom Styles
|
||||
|
||||
Custom styles can be created using the classname you choosed in markdown
|
||||
|
||||
The main purpose of this plugin is to ease and rush the production of any markdown written documentation using the right panel as code examples.
|
||||
|
||||
But let's suppose you indeed wants to add a floating cat in your documentation, like i did.
|
||||
|
||||
<small> In case you insisted to read this in github url the cat image won't be so floating.</small>
|
||||
|
||||
<!-- div:floating-cat -->
|
||||
|
||||
<img src="assets/imgs/floating-cat.png">
|
||||
|
||||
<!-- div:title-panel -->
|
||||
<hr>
|
||||
|
||||
<!-- div:left-panel -->
|
||||
|
||||
1. Add this to your markdown:
|
||||
|
||||
<!-- div:right-panel -->
|
||||
|
||||
```markdown
|
||||
<!-- panels:start -->
|
||||
<!-- div:floating-cat -->
|
||||
|
||||
<img src="path/to/floating-cat.png">
|
||||
|
||||
<!-- panels:end -->
|
||||
```
|
||||
|
||||
<!-- div:left-panel -->
|
||||
|
||||
2. will be rendered like this:
|
||||
|
||||
<!-- div:right-panel -->
|
||||
|
||||
```html
|
||||
<div class="docsify-example-panels">
|
||||
<div class="docsify-example-panels floating-cat">
|
||||
<img src="path/to/floating-cat.png">
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
<!-- div:left-panel -->
|
||||
|
||||
3. Then you can play with css animations:
|
||||
|
||||
<!-- div:right-panel -->
|
||||
|
||||
```css
|
||||
.floating-cat{
|
||||
position:absolute;
|
||||
right: 0px;
|
||||
width: 200px;
|
||||
height: 360px;
|
||||
z-index: 1000;
|
||||
|
||||
-webkit-filter: drop-shadow(0px 5px 15px rgba(0,0,0,0.6));
|
||||
filter: drop-shadow(0px 5px 15px rgba(0,0,0,0.6));
|
||||
|
||||
transform: translatey(0px);
|
||||
animation: float 6s ease-in-out infinite;
|
||||
}
|
||||
@keyframes float {
|
||||
0% {
|
||||
-webkit-filter: drop-shadow(0px 5px 5px rgba(0,0,0,0.8));
|
||||
filter: drop-shadow(0px 5px 5px rgba(0,0,0,0.6));
|
||||
transform: translatey(0px);
|
||||
}
|
||||
50% {
|
||||
box-shadow: drop-shadow(0px 25px 10px rgba(0,0,0,0.4));
|
||||
filter: drop-shadow(0px 25px 10px rgba(0,0,0,0.2));
|
||||
transform: translatey(-20px);
|
||||
}
|
||||
100% {
|
||||
box-shadow: drop-shadow(0px 5px 5px rgba(0,0,0,0.8));
|
||||
filter: drop-shadow(0px 5px 5px rgba(0,0,0,0.6));
|
||||
transform: translatey(0px);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<!-- panels:end -->
|
||||
|
||||
## Contact & Support
|
||||
|
||||
- Create a [GitHub issue](https://github.com/VagnerDomingues/docsify-example-panels/issues) for bug reports, feature requests, or questions
|
||||
- Add a ⭐️ [star on GitHub](https://github.com/VagnerDomingues/docsify-example-panels) or ❤️ [tweet](https://twitter.com/intent/tweet?url=https%3A%2F%2Fgithub.com%2FVagnerDomingues%2Fdocsify-example-panels&hashtags=docsify,developers,frontend,plugin,documentation) to support the project!
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the [MIT license](https://github.com/VagnerDomingues/docsify-example-panels/blob/master/LICENSE).
|
||||
|
||||
Plugin created by [Vagner Domingues Madeira](https://github.com/VagnerDomingues/)
|
||||
|
||||
<!-- GitHub Buttons -->
|
||||
|
||||
<script async defer src="https://buttons.github.io/buttons.js"></script>
|
||||
@@ -0,0 +1,209 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Document</title>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
||||
<meta name="description" content="Yggdrasil is a collections of notes around IT, OT and other geeks stuff">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
|
||||
<link rel="icon" href="style/yggdrasil.ico">
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="//cdn.jsdelivr.net/npm/docsify-darklight-theme@latest/dist/style.min.css"
|
||||
title="docsify-darklight-theme"
|
||||
type="text/css"
|
||||
/>
|
||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons"
|
||||
rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app">Yggdrasil</div>
|
||||
<footer id="mb-footer"></footer>
|
||||
<script>
|
||||
window.$docsify = {
|
||||
name: 'Yggdrasil',
|
||||
repo: 'https://github.com/Klagarge/Yggdrasil.git',
|
||||
maxLevel: 5,
|
||||
coverpage: 'style/_coverpage.md',
|
||||
loadNavbar: true, // Nav bar on top
|
||||
subMaxLevel: 5, // Add table of contents (TOC) in custom sidebar.
|
||||
auto2top: true, // Scrolls to the top of the screen when the route is changed.
|
||||
mergeNavbar: true, // Navbar will be merged with the sidebar on smaller screens.
|
||||
latex: {
|
||||
inlineMath : [['$', '$'], ['\\(', '\\)']], // default
|
||||
displayMath : [['$$', '$$']], // default
|
||||
customOptions: {} // default
|
||||
},
|
||||
select: {
|
||||
detectOperatingSystem: {
|
||||
enabled: false,
|
||||
menuId: "operating-system"
|
||||
}
|
||||
},
|
||||
flexibleAlerts: {
|
||||
style: 'callout'
|
||||
},
|
||||
markdown: {
|
||||
renderer: {
|
||||
|
||||
code: function (code, lang) {
|
||||
if (lang === 'drawio') {
|
||||
if (window.drawioConverter) {
|
||||
console.log('drawio')
|
||||
return window.drawioConverter(code)
|
||||
} else {
|
||||
return `<div class='drawio-code'>${code}</div>`
|
||||
}
|
||||
} else {
|
||||
return this.origin.code.apply(this, arguments);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
darklightTheme: {
|
||||
siteFont : "Ubuntu",
|
||||
defaultTheme : 'light',
|
||||
codeFontFamily : 'Roboto Mono, Monaco, courier, monospace',
|
||||
bodyFontSize : '17px',
|
||||
dark: {
|
||||
coverBackground : 'linear-gradient(to left bottom, hsl(118, 18%, 34%) 0%,hsl(207, 63%, 10%) 100%)',
|
||||
},
|
||||
light: {
|
||||
coverBackground : 'linear-gradient(to left bottom, hsl(118, 100%, 85%) 0%,hsl(181, 100%, 100%) 100%)',
|
||||
}
|
||||
},
|
||||
kroki: {
|
||||
langs: [
|
||||
"actdiag",
|
||||
"blockdiag",
|
||||
"bpmn",
|
||||
"bytefield",
|
||||
"c4plantuml",
|
||||
"d2",
|
||||
"dbml",
|
||||
"ditaa",
|
||||
"erd",
|
||||
"excalidraw",
|
||||
"graphviz",
|
||||
"nomnoml",
|
||||
"nwdiag",
|
||||
"packetdiag",
|
||||
"pikchr",
|
||||
//"plantuml",
|
||||
"rackdiag",
|
||||
"seqdiag",
|
||||
"structurizr",
|
||||
"svgbob",
|
||||
"symbolator",
|
||||
"tikz",
|
||||
"vega",
|
||||
"vegalite",
|
||||
"wavedrom",
|
||||
"wireviz",
|
||||
"umlet",
|
||||
],
|
||||
},
|
||||
plantuml: {
|
||||
skin: "classic",
|
||||
renderAsObject: false,
|
||||
},
|
||||
count:{
|
||||
position: 'bottom',
|
||||
float: 'left',
|
||||
isExpected: false
|
||||
},
|
||||
timeUpdater: {
|
||||
text: "<div align='center' width='200px' style='color:gray;font-size:10px' >last update time: {docsify-updated}</div>",
|
||||
formatUpdated: "{YYYY}-{MM}-{DD} {HH}:{mm}",
|
||||
whereToPlace: "top",
|
||||
},
|
||||
tabs: {
|
||||
persist: true, // default
|
||||
sync: true, // default
|
||||
theme: 'classic', // default
|
||||
tabComments: true, // default
|
||||
tabHeadings: true // default
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<!----- Docsify v4 ----->
|
||||
<script src="//cdn.jsdelivr.net/npm/docsify@4"></script>
|
||||
|
||||
<!----- Render PlantUML ----->
|
||||
<script src="//unpkg.com/docsify-puml/dist/docsify-puml.min.js"></script>
|
||||
|
||||
<!----- Render graph (UML/Mermaid/...) ----->
|
||||
<script src="//unpkg.com/docsify-kroki"></script>
|
||||
|
||||
<!----- Mermaid (use this one for Light and Dark theme ----->
|
||||
<script type="module">
|
||||
import mermaid from "https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs";
|
||||
mermaid.initialize({ startOnLoad: true });
|
||||
window.mermaid = mermaid;
|
||||
</script>
|
||||
<script src="//unpkg.com/docsify-mermaid@2.0.1/dist/docsify-mermaid.js"></script>
|
||||
|
||||
<!----- Copy code button ----->
|
||||
<script src="//cdn.jsdelivr.net/npm/docsify-copy-code/dist/docsify-copy-code.min.js"></script>
|
||||
|
||||
<!----- LateX ----->
|
||||
<script src="//cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
|
||||
<script src="//cdn.jsdelivr.net/npm/docsify-latex@0"></script>
|
||||
|
||||
<!----- Flexible alert ----->
|
||||
<script src="https://unpkg.com/docsify-plugin-flexible-alerts"></script>
|
||||
|
||||
<!----- Code highlights ----->
|
||||
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-armasm.js"></script>
|
||||
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-matlab.js"></script>
|
||||
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-c.js"></script>
|
||||
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-cpp.js"></script>
|
||||
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-java.js"></script>
|
||||
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-xml.js"></script>
|
||||
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-json.js"></script>
|
||||
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-sql.js"></script>
|
||||
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-pascal.js"></script>
|
||||
|
||||
<!----- Dark Theme ----->
|
||||
<script src="//cdn.jsdelivr.net/npm/docsify-darklight-theme@latest/dist/index.min.js"> </script>
|
||||
|
||||
<!----- docsify-select (latest v1.x.x) ----->
|
||||
<script src="https://cdn.jsdelivr.net/npm/docsify-select@1"></script>
|
||||
|
||||
<!----- Draw.io ----->
|
||||
<script src="scripts/viewer.min.js"></script>
|
||||
<script src='scripts/drawio.js'></script>
|
||||
|
||||
<!----- Glossary ----->
|
||||
<script src="//unpkg.com/docsify-glossary/dist/docsify-glossary.min.js"></script>
|
||||
|
||||
<!----- Count word ----->
|
||||
<script src="https://cdn.jsdelivr.net/npm/docsify-count@latest/dist/countable.min.js"></script>
|
||||
|
||||
<!----- Progress ----->
|
||||
<script src="https://cdn.jsdelivr.net/npm/docsify-progress@latest/dist/progress.min.js"></script>
|
||||
|
||||
<!----- Accordion ----->
|
||||
<link rel="stylesheet" href="//unpkg.com/docsify-accordion/src/style.css">
|
||||
<script src="//unpkg.com/docsify-accordion/src/index.js"></script>
|
||||
|
||||
<!----- Terminal ----->
|
||||
<script src="https://cdn.jsdelivr.net/npm/docsify-terminal-block@1.0.3"></script>
|
||||
|
||||
<!----- Last time updated ----->
|
||||
<script src="https://cdn.jsdelivr.net/npm/docsify-updated/src/time-updater.js"></script>
|
||||
|
||||
<!-- docsify-tabs (latest v1.x.x) -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/docsify-tabs@1"></script>
|
||||
<style>
|
||||
:root {
|
||||
--docsifytabs-border-color: #ededed;
|
||||
--docsifytabs-tab-highlight-color: purple;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,158 @@
|
||||
<header><h1>
|
||||
AD/DA
|
||||
</h1></header>
|
||||
|
||||
Il existe 4 méthodes principales pour une conversion Analogique vers Digital:
|
||||
- Parallèle
|
||||
- Par pesée
|
||||
- Par comptage
|
||||
- Sigma-delta
|
||||
|
||||
| | + (Avantage) | - (Inconvénient) |
|
||||
|-------------------------|------------------------|-----------------------------|
|
||||
| **Parallèle<br>Unaire** | Simplicité<br>Monotone | Taille du circuit ~$2^{n}$ |
|
||||
| **Pesée** | Taille du circuit ~$n$ | |
|
||||
| **Par comptage** | 1 seul switch | Nécessite d'aller très vite |
|
||||
|
||||
$$SNR [dB]=6,02\cdot n+1,78$$
|
||||
$n$ : nombre de bits du convertisseur
|
||||
|
||||
$$OSR = \frac{f_{s}}{2f_{0}}$$
|
||||
$OSR$ : OverSampling Ratio <br>
|
||||
$f_{s}$ : Sampling Frequency <br>
|
||||
$f_{0}$ : Entry Fréquency <br>
|
||||
|
||||
$$n=\frac{SNR}{20\log_{10}(2\cdot OSR)}$$
|
||||
$n$ : Ordre du filtre <br>
|
||||
$SNR$ : Signal sur bruit <br>
|
||||
$OSR$ : Oversampling ratio <br>
|
||||
|
||||
|
||||
|
||||
|
||||
# AD - Parallèle
|
||||
|
||||

|
||||
|
||||
----------
|
||||
|
||||
# AD - Par comptage
|
||||
Un seul switch mais avec une fréquence de commutation très rapide
|
||||
$$
|
||||
V_{a} = \frac{t_{on}}{T}\cdot V_{ref}
|
||||
$$
|
||||

|
||||

|
||||
|
||||
----------
|
||||
|
||||
# AD - Par pesée
|
||||
Un switch par bit.
|
||||
|
||||

|
||||
|
||||
Pour un $V_{red} = 1[V]$ on a:
|
||||
|
||||
| z2 | z1 | z0 | Va |
|
||||
|----|----|----|-------|
|
||||
| 0 | 0 | 0 | 0,000 |
|
||||
| 0 | 0 | 1 | 0,125 |
|
||||
| 0 | 1 | 0 | 0,250 |
|
||||
| 0 | 1 | 1 | 0,375 |
|
||||
| 1 | 0 | 0 | 0,500 |
|
||||
| 1 | 0 | 1 | 0,625 |
|
||||
| 1 | 1 | 0 | 0,750 |
|
||||
| 1 | 1 | 1 | 0,875 |
|
||||
|
||||
----------
|
||||
|
||||
# DA - Échantillonneur avec maintien
|
||||
|
||||
|
||||
# Caractéristique de convertisseurs
|
||||
|
||||
## Digits et affichage
|
||||
|
||||
$$
|
||||
nBits = Ceiling\left[log_2\cdot \frac{plage}{pas}\right]
|
||||
$$
|
||||
|
||||
----------
|
||||
|
||||
# Sigma / Delta
|
||||
|
||||
[sigma-delta.drawio](medias/sigma-delta.drawio ':include :type=code')
|
||||
|
||||
|
||||
## Principe
|
||||
Un convertisseur analogique-numérique (AD) ou numérique-analogique (DA) delta-sigma se compose toujours d'un modulateur
|
||||
delta-sigma produisant un "flux binaire" ou "signal binaire séquentiel" et d'un filtre passe-bas.
|
||||
|
||||
Le modulateur peut être mis en œuvre comme un circuit numérique pour un signal numérique ou comme un circuit analogique
|
||||
pour un signal analogique. De même, le filtre passe-bas peut être analogique pour générer un signal de sortie
|
||||
analogique ou numérique, réalisé soit par un circuit numérique soit par un algorithme dans un processeur de signal.
|
||||
Cette structure permet d'explorer le flux binaire et le filtre passe-bas avant d'approfondir l'étude du
|
||||
modulateur delta-sigma.
|
||||
|
||||
### Le flux binaire
|
||||
Le flux binaire peut être interprété comme un signal numérique ou analogique.
|
||||
Il s'agit d'un signal séquentiel à un bit avec une fréquence d'horloge significativement plus élevée que le débit des
|
||||
données numériques de l'ADC ou du DAC. Sa caractéristique essentielle est que sa moyenne correspond à la moyenne du
|
||||
signal d'entrée. Un niveau logique "haut" représente la valeur maximale de sortie, tandis qu'un niveau logique "bas"
|
||||
représente la valeur minimale de sortie.
|
||||
|
||||
- **Sortie analogique :** Le flux binaire est converti par un DA à un bit en un signal analogique.
|
||||
Les valeurs d'entrée "bas" et "haut" se traduisent respectivement par deux tensions analogiques précises, par exemple,
|
||||
1V et +1V.
|
||||
|
||||
- **Sortie numérique :** Dans le cas d'une sortie numérique, un niveau "haut" (ou "bas") dans le flux binaire représente
|
||||
la valeur maximale (ou minimale) de la plage de valeurs numériques. Par exemple, dans un système à 8 bits, un niveau
|
||||
"haut" pourrait correspondre à FF en hexadécimal, tandis qu'un niveau "bas" pourrait correspondre à 00 en hexadécimal.
|
||||
|
||||
Un flux binaire similaire existe dans un signal modulé en largeur d‘impulsions (PWM), mais il présente quelques
|
||||
désavantages par rapport au flux binaire d'un convertisseur delta-sigma, également appelé signal modulé en rapport
|
||||
d’impulsions (PPM). La transmission en série d'une valeur numérique, telle que la sortie en série d'un ADC conventionnel
|
||||
par pesée, est quant à elle appelée modulation par codage d'impulsions (PCM).
|
||||
|
||||
----------
|
||||
|
||||
#
|
||||
|
||||
Un convertisseur sigma-delta est un type de convertisseur
|
||||
analogique-numérique (CAN) qui utilise une boucle de contre-réaction pour
|
||||
échantillonner et quantifier un signal analogique en un signal numérique.
|
||||
Le principe de fonctionnement d'un convertisseur sigma-delta peut être
|
||||
expliqué en plusieurs étapes :
|
||||
|
||||
1. Échantillonnage : Tout d'abord, le signal analogique est échantillonné
|
||||
à une fréquence d'échantillonnage beaucoup plus élevée que la bande
|
||||
passante du signal. Cette haute fréquence d'échantillonnage permet de
|
||||
réduire considérablement les effets des distorsions et du bruit dans le
|
||||
domaine fréquentiel.
|
||||
2. Quantification : Le signal échantillonné est ensuite quantifié en un
|
||||
signal numérique à l'aide d'un comparateur qui compare le signal avec une
|
||||
référence de tension fixe. La sortie du comparateur est une suite de bits
|
||||
représentant la valeur du signal analogique à chaque instant.
|
||||
3. Contre-réaction : Le signal quantifié est ensuite utilisé pour régler
|
||||
un oscillateur numérique qui produit un signal d'erreur. Ce signal
|
||||
d'erreur est ajouté au signal d'entrée dans la boucle de contre-réaction,
|
||||
ce qui permet de compenser les erreurs de quantification et de réduire le
|
||||
bruit dans le signal quantifié.
|
||||
4. Suréchantillonnage : Le signal numérique produit par le convertisseur
|
||||
sigma-delta est suréchantillonné à une fréquence beaucoup plus élevée que
|
||||
la fréquence d'échantillonnage initiale, ce qui permet de réduire encore
|
||||
davantage les effets du bruit dans le signal.
|
||||
5. Décimation : Enfin, le signal suréchantillonné est décimé à la
|
||||
fréquence d'échantillonnage désirée en utilisant un filtre passe-bas
|
||||
numérique qui élimine les composantes de hautes fréquences indésirables.
|
||||
|
||||
Le résultat final est un signal numérique représentant fidèlement le
|
||||
signal analogique d'origine, avec une faible distorsion et un faible
|
||||
bruit. La boucle de contre-réaction permet au convertisseur sigma-delta
|
||||
d'atteindre des performances supérieures à celles des autres types de
|
||||
convertisseurs analogiques-numérique en termes de résolution et de
|
||||
précision.
|
||||
|
||||

|
||||
|
||||
----------
|
||||
@@ -0,0 +1,339 @@
|
||||
<header><h1>
|
||||
Filtres
|
||||
</h1></header>
|
||||
|
||||
-----------
|
||||
|
||||
|
||||
# Diagramme de Bode
|
||||
|
||||
## H0 - Gain statique
|
||||
$$H_{0}(s)=A$$
|
||||

|
||||
|
||||
## H1 - Coupe bas, amplis haut
|
||||
$$H_{1}(s)=\frac{s}{\omega_{0}}$$
|
||||

|
||||
|
||||
## H2 - Ampli haut, coupe bas
|
||||
$$H_{2}(s)=\frac{\omega_{0}}{s}$$
|
||||

|
||||
|
||||
## H3 - Ampli haut
|
||||
$$H_{3}(s)=\frac{\omega_{0}+1}{\omega_{0}} = 1+ \frac{s}{\omega_{0}}$$
|
||||

|
||||
|
||||
## H4 - Coupe haut
|
||||
$$H_{4}(s)=\frac{\omega_{0}}{\omega_{0}+s} = \frac{1}{1+\frac{s}{\omega_{0}}}$$
|
||||

|
||||
|
||||
## Résonance
|
||||

|
||||
|
||||
----------
|
||||
|
||||
# Filtres numériques
|
||||
|
||||

|
||||
|
||||
## Filtres numériques récursifs (IIR)
|
||||
|
||||
Les filtres numériques récursifs (IIR) sont des filtres numériques qui utilisent des rétroactions.
|
||||
Ils sont basés sur des équations aux différences.
|
||||
Les filtres récursifs sont utilisés pour des applications où la phase du filtre n'est pas critique.
|
||||
Ils sont utilisés dans les télécommunications et la régulation.
|
||||
|
||||
Les intérêts des filtres récursifs :
|
||||
- Leur faible coût en calcul
|
||||
- Faible retard (télécommunication et la régulation)
|
||||
|
||||
Les inconvénients des filtres récursifs :
|
||||
- Ils possèdent une phase non-linéaire
|
||||
- Ils peuvent avoir une instabilité numérique
|
||||
|
||||
***Fonction de transfert en $z$ :***
|
||||
$$F(z) = \frac{Y(z)}{U(z)} = \frac{b_{0}+b_{1}z^{-1}+\dots+b_{n}z^{-n}}{1+a_{1}z^{-1}+\dots+a_{n}z^{-n}}$$
|
||||
Numérisation par conversion DA et AD d'un filtre analogique :
|
||||
$$F(z) = (1-z^{-1})Z \left\{ L^{-1}\left[ \frac{F(s)}{s} \right] \right\} $$
|
||||
La fonction de transfert en *z* permet ensuite de tirer l'équation aux différences qui servira à réaliser le filtre sous forme d'un algorithme récursif
|
||||
$$y[k]= b_{n}u[k-n]+ \dots + b_{1}u[k-1]+b_{0}u[k]-a_{n}y[k-n]-\dots-a_{1}y[k-1]$$
|
||||
|
||||
Si on souhaite connaître la réponse fréquentielle d'un filtre, il suffit de remplacer l'opérateur *z* par sa transformée de Fourier :
|
||||
$$a\cdot z^{n}=a\cdot \exp(n\cdot s\cdot T_{s})=a\cdot \exp(n\cdot \mathbf{j}\omega T_{s})$$
|
||||
$T_{s}$ : La période d'échantillonnage
|
||||
|
||||
Comme l’exponentielle imaginaire est périodique, la réponse fréquentielle d’un filtre numérique est également périodique
|
||||
de fréquence, et sa représentation se fait dans le domaine de fréquences allant de $0$ à $fs/2$. <br>
|
||||
Le gain statique d’un filtre peut être calculé selon :
|
||||
$$\text{Gain statique d'un filtre} = \lim_{ s \to 0 } F(s) = \lim_{ z \to 1 } F(z)$$
|
||||
|
||||
> [!NOTE|label:Passage pôle et zéro s ↔ z]
|
||||
> $$\begin{align}
|
||||
z&=\exp(s\cdot T_{s}) \\
|
||||
s&=\frac{1}{T_{s}\ln(z)}
|
||||
\end{align}$$
|
||||
|
||||

|
||||

|
||||
|
||||
### Transformation analogique-numérique
|
||||
La transformation bilinéaire : transformation de Tustin ou approximation de Padé de premier ordre
|
||||
$$s = \frac{2}{T_{s}} \frac{z-1}{z+1} \qquad \qquad z = \frac{1+\frac{1}{2}sT_{s}}{1-\frac{1}{2}sT_{s}}$$
|
||||
|
||||
***Pour un filtre analogique de premier ordre :***
|
||||
$$F(s)=\frac{d_{0}+d_{1}s}{c_{0}+c_{1}s} \to F(z) = \frac{b_{1}z^{-1}+b_{0}}{a_{1}z^{-1}+1} $$
|
||||
$$
|
||||
a_{1} = \frac{c_{0}-\frac{2c_{1}}{T_{s}}}{c_{0}+\frac{2c_{1}}{T_{s}}}
|
||||
\qquad \qquad
|
||||
b_{0} = \frac{d_{0}+\frac{2d_{1}}{T_{s}}}{c_{0}+\frac{2c_{1}}{T_{s}}}
|
||||
\qquad \qquad
|
||||
b_{1} = \frac{d_{0}-\frac{2d_{1}}{T_{s}}}{c_{0}+\frac{2c_{1}}{T_{s}}}
|
||||
$$
|
||||
|
||||
***Pour un filtre analogique de second ordre :***
|
||||
$$F(s)=\frac{Y(s)}{U(s)}=\frac{d_{0}+d_{1}s+d_{2}s^{2}}{c_{0}+c_{1}s+c_{2}s^{2}}
|
||||
\to F(z) = \frac{Y(z)}{U(z)}=\frac{b_{2}z^{-2}+b_{1}z^{-1}+b_{0}}{a_{2}z^{-2+}a_{1}z^{-1}+1} $$
|
||||
$$\begin{gather}
|
||||
C = c_{0}+\frac{2c_{1}}{T_{s}}+\frac{4c_{2}}{T_{s}^{2}} \\
|
||||
\\
|
||||
a_{1} = \frac{2\left( c_{0}-\frac{4c_{2}}{T_{s}^{2}} \right)}{C}
|
||||
\qquad \qquad
|
||||
a_{2} = \frac{ c_{0}-\frac{2c_{1}}{T_{s}}+\frac{4c_{2}}{T_{s}^{2}} }{C}
|
||||
\qquad \qquad
|
||||
b_{0} = \frac{d_{0}+\frac{2d_{1}}{T_{s}}+\frac{4d_{2}}{T_{s}^{2}}}{C} \\
|
||||
\\
|
||||
b_{1} = \frac{2\left( d_{0}-\frac{4d_{2}}{T_{s}^{2}} \right)}{C}
|
||||
\qquad \qquad
|
||||
b_{2} = \frac{d_{0}-\frac{2d_{1}}{T_{s}}+\frac{4d_{2}}{T_{s}^{2}}}{C}
|
||||
\end{gather}$$
|
||||
|
||||
## Filtre numérique non-récursif (FIR)
|
||||
|
||||
Les filtres numériques non-récursifs (FIR) sont des filtres numériques qui n'utilisent pas de rétroactions.
|
||||
Ils sont basés sur des équations aux différences.
|
||||
Les filtres non-récursifs sont utilisés pour des applications où la phase du filtre est critique.
|
||||
Ils sont utilisés dans le traitement du signal et la conception de filtres.
|
||||
|
||||
Les intérêts des filtres non-récursifs :
|
||||
- Les fonctions de transfert ne possèdent que des coefficients $b_i$. ($\forall a_{i} = 0$)
|
||||
- Ils possèdent une phase linéaire exacte
|
||||
- Ils sont stables
|
||||
|
||||
Les inconvénients des filtres non-récursifs :
|
||||
- Ils sont plus coûteux en calcul
|
||||
- Ils nécessitent plus de matériel
|
||||
|
||||
Coefficients symétrique: pairs $b_{n-i} = b_{i}$, impairs $b_{n-i} = -b_{i}$, $b_{n/2}=0$ <br>
|
||||
|
||||
**Symétrie paire:**
|
||||
$$H(\mathbf{j}\omega)
|
||||
= \sum^{n}_{i=o}b_{i}e^{-\frac{\mathbf{j}i\omega}{f_{s}}}
|
||||
= e^{\frac{-\mathbf{j}n\omega}{2f_{s}}} \sum^{n/2}_{i=0}b_{i}\cos\left[ \frac{\left( i-\frac{n}{2} \right)\omega}{f_{s}} \right]$$
|
||||
|
||||
**Symétrie impaire:**
|
||||
$$H(\mathbf{j}\omega)=\mathbf{j}e^{\frac{-\mathbf{j}n\omega}{2f_{s}}}\sum^{n/2}_{i=0}b_{i}\sin\left( \frac{(i-\frac{n}{2})\omega}{f_{s}} \right)$$
|
||||
|
||||
Les filtres FIR à coefficients symétriques ont une phase linéaire.
|
||||
$$
|
||||
\text{Symétrie paire : }\varphi(\omega)=-\frac{n\omega}{2f_{s}}
|
||||
\qquad \qquad
|
||||
\text{Symétrie impaire : } \varphi(\omega)= -\frac{n\omega}{2f_{s}}+\frac{\pi}{2}
|
||||
$$
|
||||
$$\text{Temps de propagation : }t_{gr}=-\frac{d\varphi}{d\omega}=\frac{n}{2f_{s}}=\frac{n}{2}T_{s}$$
|
||||
Il y a donc 4 types de filtres FIR à phase linéaire : n resp. symétrie paire/impaire
|
||||
|
||||
|
||||
***Fonction de transfert en $z$ :***
|
||||
$$F(z) = \frac{Y(z)}{U(z)} = b_{0}+b_{1}z^{-1}+\dots+b_{n}z^{-n}$$
|
||||
|
||||
***Équation aux différences :***
|
||||
$$y[k]= b_{n}u[k-n]+ \dots + b_{1}u[k-1]+b_{0}u[k]$$
|
||||
|
||||
***Réponse en fréquence :***
|
||||
$$z=\exp(\mathbf{j}\omega\cdot T_{s})$$
|
||||
|
||||
### Filtres FIR à symétrie paire :
|
||||
$$b_{n-k} = b_{k}$$
|
||||
|
||||
Exemple : FIR à symétrie paire d'ordre 6
|
||||
$$\begin{align}
|
||||
F(z) &= \frac{Y(z)}{U(z)} \\
|
||||
&= 1+2z^{-1}+3z^{-2}+4z^{-3}+3z^{-4}+2z^{-5}+z^{-6} \\
|
||||
&= \frac{z^{6}+2z^{5}+3z^{4}+4z^{3}+3z^{2}+2z^{1}+1}{z^{6}}
|
||||
\end{align}$$
|
||||
Les filtres FIR à symétrie paire sont toujours de type passe-bas ou coupe-bande. <br>
|
||||
Ils possèdent autant de pôles que l'order du filtre. Ces derniers sont à (0,0) sur le cercle unité. <br>
|
||||
Ils possèdent autant de zéros que l'ordre du filtre. Ces derniers sont sur le cercle unité. <br>
|
||||
|
||||
### Filtres FIR à symétrie impaire :
|
||||
$$b_{n-k} = -b_{k}$$
|
||||
Exemple : FIR à symétrie impaire d'ordre 6
|
||||
$$\begin{align}
|
||||
F(z) &= \frac{Y(z)}{U(z)} \\
|
||||
&= -1-2z^{-1}-3z^{-2}+0z^{-3}+3z^{-4}+2z^{-5}+z^{-6} \\
|
||||
&= \frac{z^{6}+2z^{5}+3z^{4}+0z^{3}-3z^{2}-2z^{1}-1}{z^{8}}
|
||||
\end{align}$$
|
||||
Les filtres FIR à symétrie paire sont toujours de type passe-haut ou passe-bande. <br>
|
||||
Ils possèdent autant de pôles que l'ordre du filtre. Ces derniers sont à (0,0) sur le cercle unité. <br>
|
||||
Ils possèdent autant de zéros que l'ordre du filtre. <br>
|
||||
|
||||
## Filtres numériques FIR idéaux
|
||||
$$\omega_{c_{d}} = 2\pi \frac{f_{c_{a}}}{f_{s}}$$
|
||||
|
||||
### Filtre passe-bas idéal
|
||||
$$H(\mathbf{j}\omega)=\begin{cases}
|
||||
1 &\text{ si } |\omega| \leq \omega_{c} \\
|
||||
0 &\text{ si } |\omega| > \omega_{c}
|
||||
\end{cases} $$
|
||||
La transformée de Fourier inverse donne la réponse impulsionnelle d’un filtre passe-bas idéal.
|
||||
|
||||
$$h[n] = \begin{cases}
|
||||
\frac{\omega_{c}}{\pi} \frac{\sin((n-N_{s})\omega_{c})}{(n-N_{s})\omega_{c}} &\text{ si } 0\leq n \leq N \\
|
||||
\\
|
||||
\frac{\omega_{c}}{\pi} &\text{ si } n=N_{s} \\
|
||||
\\
|
||||
0 &\text{ sinon}
|
||||
\end{cases}$$
|
||||
|
||||
On tronque à $N=ordre$ du filtre. $N_{s}=\frac{N}{2}$ <br>
|
||||
Ces coefficients ne sont rien d’autre que les coefficients de la fonction de transfert en *z* du filtre passe-bas idéal.
|
||||
|
||||
### Filtre passe-haut idéal
|
||||
$$H(\mathbf{j}\omega)=\begin{cases}
|
||||
0 &\text{ si } |\omega| \leq \omega_{c} \\
|
||||
1 &\text{ si } |\omega| > \omega_{c}
|
||||
\end{cases}$$
|
||||
La transformée de Fourier inverse donne la réponse impulsionnelle d’un filtre passe-bas idéal.
|
||||
|
||||
$$h[n] = \begin{cases}
|
||||
-\frac{\omega_{c}}{\pi} \frac{\sin((n-N_{s})\omega_{c})}{(n-N_{s})\omega_{c}} &\text{ si } 0\leq n \leq N \\
|
||||
\\
|
||||
1-\frac{\omega_{c}}{\pi} &\text{ si } n=N_{s} \\
|
||||
\\
|
||||
0 &\text{ sinon}
|
||||
\end{cases}$$
|
||||
|
||||
On tronque à $N=ordre$ du filtre. $N_{s}=\frac{N}{2}$ <br>
|
||||
Ces coefficients ne sont rien d’autre que les coefficients de la fonction de transfert en *z* du filtre passe-haut idéal.
|
||||
|
||||
### Filtre passe-bande idéal
|
||||
$$H(\mathbf{j}\omega)=\begin{cases}
|
||||
1 &\text{ si } \omega_{c_{1}} \leq \omega \leq \omega_{c_{2}} \\
|
||||
0 &\text{ si } \omega \leq \omega_{c_{1}} \quad et \quad \omega\geq \omega_{c_{2}}
|
||||
\end{cases}$$
|
||||
La transformée de Fourrier inverse donne la réponse impulsionnelle d’un filtre passe-bande idéal.
|
||||
|
||||
$$h[n] = \begin{cases}
|
||||
\frac{\omega_{c_{2}}}{\pi} \frac{\sin((n-N_{s})\omega_{c_{2}})}{(n-N_{s})\omega_{c_{2}}}-\frac{\omega_{c_{1}}}{\pi} \frac{\sin((n-N_{s})\omega_{c_{1}})}{(n-N_{s})\omega_{c_{1}}} &\text{ si } 0\leq n \leq N \\
|
||||
\\
|
||||
\frac{\omega_{c_{2}}-\omega_{c_{1}}}{\pi} &\text{ si } n=N_{s}\\
|
||||
\\
|
||||
0 &\text{ sinon}
|
||||
\end{cases}$$
|
||||
|
||||
On tronque à $N=ordre$ du filtre. $N_{s}=\frac{N}{2}$ <br>
|
||||
Ces coefficients ne sont rien d’autre que les coefficients de la fonction de transfert en *z* du filtre passe-bande idéal.
|
||||
|
||||
### Filtre coupe-bande idéal
|
||||
$$H(\mathbf{j}\omega)=\begin{cases}
|
||||
0 &\text{ si } \omega_{c_{1}} \leq \omega \leq \omega_{c_{2}} \\
|
||||
1 &\text{ si } \omega \leq \omega_{c_{1}} \quad et \quad \omega\geq \omega_{c_{2}}
|
||||
\end{cases}$$
|
||||
La transformée de Fourrier inverse donne la réponse impulsionnelle d’un filtre coupe-bande idéal.
|
||||
|
||||
$$h[n] = \begin{cases}
|
||||
\frac{\omega_{c_{1}}}{\pi} \frac{\sin((n-N_{s})\omega_{c_{1}})}{(n-N_{s})\omega_{c_{1}}}
|
||||
-\frac{\omega_{c_{2}}}{\pi} \frac{\sin((n-N_{s})\omega_{c_{2}})}{(n-N_{s})\omega_{c_{2}}}
|
||||
&\text{ si } 0\leq n \leq N \\
|
||||
\\
|
||||
1-\frac{\omega_{c_{2}}-\omega_{c_{1}}}{\pi} &\text{ si } n=N_{s}\\
|
||||
\\
|
||||
0 &\text{ sinon}
|
||||
\end{cases}$$
|
||||
|
||||
On tronque à $N=ordre$ du filtre. $N_{s}=\frac{N}{2}$ <br>
|
||||
Ces coefficients ne sont rien d’autre que les coefficients de la fonction de transfert en *z* du filtre coupe-bande idéal.
|
||||
|
||||
## Synthèse par fenêtrage
|
||||
Le point de départ de la synthèse des filtres FIR est donné par la considération des réponses impulsionnelles des filtres idéaux.
|
||||
Comme celles-ci sont infiniment longues et donc non réalisables, on va se contenter d’une approximation de leurs réponses.
|
||||
|
||||
### Méthode de fenêtrage
|
||||
- **Fenêtre rectangulaire :** $$W[n] = \begin{cases}
|
||||
1 &\text{ si } 0\leq n \leq N \\
|
||||
0 &\text{ sinon}
|
||||
\end{cases}$$
|
||||
- **Fenêtre triangulaire (Bartlett) :** $$W[n] = \begin{cases}
|
||||
2 \frac{n}{N} &\text{ si } 0\leq n \leq \frac{N}{2} \\
|
||||
2-2 \frac{n}{N} &\text{ si } \frac{N}{2}\leq n \leq N \\
|
||||
0 &\text{ sinon}
|
||||
\end{cases}$$
|
||||
- **Fenêtre sinusoïdale (Hann) :** $$W[n] = \begin{cases}
|
||||
0,5\left( 1-\cos\left( \frac{2\pi n}{N} \right) \right) &\text{ si } 0\leq n\leq N \\
|
||||
0 &\text{ sinon}
|
||||
\end{cases}$$
|
||||
- **Fenêtre de Hamming :** $$W[n] = \begin{cases}
|
||||
0,54-0,46\cos\left( \frac{2\pi n}{N} \right) &\text{ si } 0\leq n\leq N \\
|
||||
0 &\text{ sinon}
|
||||
\end{cases}$$
|
||||
- **Fenêtre de Blackman :** $$W[n] = \begin{cases}
|
||||
0,42-0,5\cos\left( \frac{2\pi n}{N} \right)+0,08\cos\left( \frac{4\pi n}{N} \right) &\text{ si } 0\leq n\leq N \\
|
||||
0 &\text{ sinon}
|
||||
\end{cases}$$
|
||||
- **Fenêtre de Kaiser-Bessel :** $$W[n] = \begin{cases}
|
||||
\frac{I_{0}\left( \beta\sqrt{1-\left( \frac{1-n}{N_{s}} \right)^{2}} \right)}{I_{0}(\beta)} &\text{ si } 0\leq n\leq N \\
|
||||
0 &\text{ sinon}
|
||||
\end{cases}$$
|
||||
$I_{0}$ : Fonction de Bessel modifiée d’ordre 0 <br>
|
||||
$\beta$ : Paramètre de forme
|
||||
|
||||
***Caractéristiques spectrales de quelques fenêtres***
|
||||
|
||||
| Fenêtres | Largeur du 1er lobe | Atténuation du 1er lobe [dB] | Décroissance des lobes suivants | Ondulation Rp [dB] | Atténuation max [dB] | Bande de transition<br>[rad/sec]<br>$\Delta\omega$ |
|
||||
|---------------------|---------------------|------------------------------|---------------------------------|--------------------|----------------------|----------------------------------------------------|
|
||||
| Rectangulaire | $4\pi/N$ | $-13$ | $20 dB/dec$ | $0,74$ | $21$ | $1,8\pi/N$ |
|
||||
| Triangulaire | $8\pi/N$ | $-27$ | $40 dB/dec$ | $0$ | $25$ | $6,1\pi/N$ |
|
||||
| Cosinus | $8\pi/N$ | $-32$ | $60 dB/dec$ | $0,055$ | $44$ | $6,2\pi/N$ |
|
||||
| Hamming | $8\pi/N$ | $-43$ | $20 dB/dec$ | $0,014$ | $53$ | $6,6\pi/N$ |
|
||||
| Blackman | $12\pi/N$ | $-58$ | $60 dB/dec$ | $0,0017$ | $74$ | $11\pi/N$ |
|
||||
| Kaiser $\beta=4,54$ | $7,2\pi/N$ | $-30$ | $20 dB/dec$ | $0,025$ | $50$ | $5,8\pi/N$ |
|
||||
| Kaiser $\beta=5,66$ | $8,4\pi/N$ | $-42$ | $20 dB/dec$ | $0,009$ | $60$ | $7,2\pi/N$ |
|
||||
|
||||
|
||||
|
||||
### Comment calculer son filtre
|
||||
|
||||

|
||||
|
||||
$H(\mathbf{j}\omega)$ : Gain <br>
|
||||
$\delta_{1}$ : L'erreur de gain tolérable <br>
|
||||
$\delta_{2}$ : L'erreur d'atténuation tolérable <br>
|
||||
$\omega_{p}$ : Fréquence de pulsation de passage <br>
|
||||
$\omega_{a}$ : Fréquence de pulsation d'arrêt <br>
|
||||
|
||||
1. En fonction du gabarit, déterminer le type de filtre désiré (`passe-bas`, `passe-haut`, `passe-bande`, `coupe-bande`)
|
||||
2. Calculer $f_{c} \to \omega_{c}$ et $\Delta f \to \Delta\omega$
|
||||
$$\begin{gather}
|
||||
f_{c} = \frac{f_{a}+f_{p}}{2} \qquad & \qquad \Delta f = f_{a}-f_{p} \\
|
||||
\omega_{c} = 2\pi \frac{f_{c}}{f_{s}} \quad & \quad \Delta\omega = \frac{2\pi\Delta f}{f_{s}}
|
||||
\end{gather}$$
|
||||
3. Choisir une fenêtre $W[n]$ satisfaisante du point de vue de l’atténuation et du reste du cahier des charges.(voir tableau ci-dessus)
|
||||
4. Calculer l'ordre du filtre à l'aide de la bande de transition du filtre choisit. Arrondir au nombre pair supérieur.
|
||||
5. Calculer la réponse impulsionnelle $h_{f}[n]$ du filtre avec $h[n]*W[n]$
|
||||
6. Calculer les coefficients $b_{n}$ du filtre avec $h_{f}[n]$
|
||||
|
||||
<details>
|
||||
<summary>Démarche selon les slides</summary>
|
||||
|
||||
1. Connaissant le gabarit du filtre désiré, choisir le filtre idéal correspondant
|
||||
2. Calculer les pulsations caractéristiques se situant au centre des bandes de transitions
|
||||
3. Rechercher la réponse impulsionnelle $h[n]$ du filtre idéal, si celle-ci n’est pas connue, on peut la calculer par transformation de Fourier inverse
|
||||
4. Choisir une fenêtre $W[n]$ satisfaisante du point de vue de l’atténuation
|
||||
5. Connaissant la largeur de la bande de transition , calculer l’ordre du filtre $N$
|
||||
6. Calculer les coefficients du filtre en multipliant la réponse impulsionnelle par la fenêtre choisie
|
||||
</details>
|
||||
|
||||
|
||||
|
||||
|
||||
----------
|
||||
@@ -0,0 +1,9 @@
|
||||
<header>
|
||||
<h1>Électronique</h1>
|
||||
</header>
|
||||
|
||||
# Convertisseurs
|
||||
- [AD/DA](/infotro/Eln/ADDA)
|
||||
- [Signaux et échantillonnage](/infotro/Eln/SignauxEtEchantillonnage)
|
||||
- [Filtres](/infotro/Eln/Filtres)
|
||||
- [RF](/infotro/Eln/RF)
|
||||
@@ -0,0 +1,145 @@
|
||||
<header><h1>
|
||||
Radio Fréquence
|
||||
</h1></header>
|
||||
|
||||
----------
|
||||
|
||||
# Skin effect
|
||||
Le courant électrique circule principalement à la surface des conducteurs.
|
||||
|
||||
Pour un fil de cuivre rond, la résistance en continue est donnée par la formule suivante:
|
||||
$$R_{DC} = \rho\frac{l}{\pi r²}$$
|
||||
avec:
|
||||
- $R_{DC}$: résistance en continue
|
||||
- $\rho$: résistivité du cuivre
|
||||
- $l$: longueur du fil
|
||||
- $r$: rayon du fil
|
||||
|
||||
La résistance en courant alternatif est donnée par la formule suivante:
|
||||
$$R_{AC} = R_{DC} \frac{r}{2\delta}$$
|
||||
avec:
|
||||
- $R_{AC}$: résistance en courant alternatif
|
||||
- $R_{DC}$: résistance en continue
|
||||
- $r$: rayon du fil
|
||||
- $\delta$: profondeur de pénétration du courant
|
||||
|
||||
La profondeur de pénétration du courant est donnée par la formule suivante:
|
||||
$$\delta = \sqrt{\frac{\rho}{\pi f \mu_0}}$$
|
||||
avec:
|
||||
- $\delta$: profondeur de pénétration du courant
|
||||
- $\rho$: résistivité du cuivre
|
||||
- $f$: fréquence
|
||||
- $\mu_0$: perméabilité magnétique du vide $= 4\pi 10^{-7} H/m$
|
||||
|
||||
On peut en déduire la résistance en courant alternatif:
|
||||
$$R_{AC} = \frac{l\sqrt{\rho\pi\mu_0}}{2\pi r} \sqrt{f} \qquad \qquad R_{AC(f_{2})} = R_{AC(f_{1})} \cdot \sqrt{ \frac{f_{2}}{f_{1}} }$$
|
||||
|
||||
Pour calculer l'inductance et la capacité d'un fil, on peut utiliser les formules suivantes :
|
||||
$$\begin{align}
|
||||
L&=\frac{\mu_{0}l}{\pi}acosh\left( \frac{d}{2r} \right) \\
|
||||
C&=\frac{\pi\epsilon_{0}l}{acosh\left( \frac{d}{2r} \right)}
|
||||
\end{align}$$
|
||||
avec :
|
||||
- $\mu_{0}$: perméabilité magnétique du vide $=4\pi\cdot 10^{-7}\left[ \frac{H}{m} \right]$
|
||||
- $\epsilon_{0}$: permittivité du vide $=8.854\cdot 10^{-12}\left[ \frac{F}{m} \right]$
|
||||
|
||||
# Model à haute fréquence
|
||||
|
||||
## Inductance
|
||||

|
||||
|
||||
<u>Facteur de qualité :</u>
|
||||
$$Q(f) = \frac{X_{L}(f)}{R(f)} \qquad \qquad R_{AC}(f)= \frac{l\sqrt{ \rho \pi \mu_{0} }}{2\pi r}\sqrt{ f}$$
|
||||
|
||||
<u>Réactance & Impédance:</u>
|
||||
$$X_{L}=\omega L=2\pi fL \qquad \qquad Z_{L}=jX_{L}=j\omega L$$
|
||||
|
||||
<u>Résonance :</u> $\omega_{r} ≃\omega_{0}=\frac{1}{\sqrt{ LC }}$ <br>
|
||||
SRF: Self Resonant Frequency = $\frac{\omega_{r}}{2\pi}$
|
||||
|
||||
$$Z_{L,tot} = (j\omega L+R) \mid \mid \frac{1}{j\omega C} =\frac{sL+R}{1+sRC+s^{2}LC}$$
|
||||
|
||||

|
||||
|
||||
# Capacité
|
||||

|
||||
|
||||
<u>Facteur de qualité :</u>
|
||||
$$Q(f) = \frac{X_{C}(f)}{R} \qquad \qquad \omega_{r} ≃\omega_{0}=\frac{1}{\sqrt{ LC }}$$
|
||||
|
||||
<u>Réactance & Impédance:</u>
|
||||
$$X_{C}=\frac{1}{\omega C} \qquad \qquad Z_{C}=\frac{1}{j\omega C}$$
|
||||
|
||||

|
||||
|
||||
# Résistance
|
||||

|
||||
|
||||
|
||||
# Transmission line
|
||||
|
||||
$$ k = \frac{2\pi}{\lambda} \qquad \qquad \lambda = \frac{c_{mat}}{f} $$
|
||||
|
||||
$\Gamma$ (Gamma) : coefficient de réflexion
|
||||
|
||||
# Smith Chart
|
||||
|
||||
|
||||
## Ajouter une ligne de réflexion
|
||||
|
||||
|
||||
1. Placer $Z1$
|
||||
1. Normaliser ($\hat{Z_{1}}=\frac{Z_{1}}{Z_{0}}$)
|
||||
2. Placer le point au croisement de l'axe réel et imaginaire (Impédance = bleu)
|
||||
2. Chercher $\Gamma$ (indice de reflexion)
|
||||
1. Tracer l'angle ("Angle of reflexion coefficient in degrees")
|
||||
2. Tracer le module (cercle au compas + rapport sur les échelles du bas)
|
||||
3. Ajouter la ligne selon le sens
|
||||
- $\circlearrowright$ : toward generator
|
||||
- $\circlearrowleft$ : toward load
|
||||
4. Lire les nouvelles valeures
|
||||
1. Tracer le nouvel angle $\to \angle(\Gamma_{2})$
|
||||
2. Tracer et lire $\hat{Z_{2}}$ puis $Z_{2}$
|
||||
|
||||

|
||||
|
||||
# Impedance Matching
|
||||
$Z_{LD} \to Z_S^*$, sans résistance
|
||||
- Capacité + inductance
|
||||
- ligne de transmission + capacité / inductance + ligne de transmission
|
||||
- ligne de transmission + stub + ligne de transmission
|
||||
-
|
||||

|
||||
|
||||
## Stub
|
||||
Pour trouver la longueur du stub :
|
||||
1. Pour un short stub, placer une inductance parallèle et pour un open stub un condensateur parallèle
|
||||
2. Trouver la valeur normalisée de cette admittance.
|
||||
3. Pour un short stub, placer le delta dans les négatifs (parti du dessus) et pour un open stub dans les positifs (partie du dessous).
|
||||
4. Regarder la longueur d'onde. Partir du côté open circuit pour un open stub et du côté short circuit pour un short stub.
|
||||
|
||||
# Filtres
|
||||
|
||||
Filter components <br>
|
||||
 <br>
|
||||

|
||||
|
||||
Normalized value for Butterworth order 1 to 10 <br>
|
||||

|
||||
|
||||
Normalized value for Chebyshev 0.5dB Ripple <br>
|
||||

|
||||
|
||||
Normalized value for Chebyshev 3dB Ripple <br>
|
||||

|
||||
|
||||
## Filtres passe-bas en microstrip
|
||||
$l$ : longueur physique en mm <br>
|
||||
$\theta$ : longueur électrique / degré <br>
|
||||
$$ l_l = g_k * \frac{\lambda_{gl} * Z_0}{2\pi *Z_{0l}} $$
|
||||
$$ l_c = g_k * \frac{\lambda_{gc} * Z_{0c}}{2\pi *Z_0} $$
|
||||
$$ \theta_l = g_k * \frac{360 * Z_0}{2\pi *Z_{0l}} $$
|
||||
$$ \theta_c = g_k * \frac{360* Z_{0c}}{2\pi *Z_0} $$
|
||||
|
||||

|
||||
|
||||
@@ -0,0 +1,256 @@
|
||||
<header><h1>
|
||||
Signaux et échantillonnage
|
||||
</h1></header>
|
||||
|
||||
-----------
|
||||
|
||||
# Fourier
|
||||
Pour comprendre les signaux, il est important de comprendre la transformée de Fourier.
|
||||
La transformée de Fourier est une technique de traitement du signal utilisée pour analyser et manipuler les signaux continus.
|
||||
Elle permet d'exprimer un signal dans le temps en domaine fréquentiel.
|
||||
|
||||
## Transformée de Fourier
|
||||
La transformée de Fourier est une technique de traitement du signal utilisée pour analyser et manipuler les signaux continus.
|
||||
Elle permet d'exprimer un signal dans le temps en domaine fréquentiel.
|
||||
|
||||
La transformée de Fourier est définie par la formule suivante :
|
||||
|
||||
Soit $f(x)$ une fonction non périodique définie sur $\mathbb{R}$.
|
||||
$$\begin{align}
|
||||
\text{Transformée de Fourier de }f(x) &: \qquad \widehat{f}(\omega) = \int^{\infty}_{-\infty}f(x)e^{-\mathbf{i}\omega x}dx \qquad (\omega\in \mathbb{R})\\
|
||||
\text{Intégrale de Fourier de } f(x) &: \qquad \frac{1}{2\pi}\int_{-\infty}^{\infty} \widehat{f}(\omega) e^{\mathbf{i}x\omega}\, d\omega \qquad(x \in\mathbb{R}).
|
||||
\end{align}$$
|
||||
**Théorème** : Si $\int_{-\infty}^{\infty} |f(x) \, dx < \infty$ et si $f$ est une fonction de variation bornée sur tout $\mathbb{R}$, alors
|
||||
$$
|
||||
\frac{1}{2\pi}\int_{-\infty}^{\infty} \widehat{f}(\omega)e^{\mathbf{i}x\omega} \, d\omega = \begin{cases}
|
||||
f(x) &\text{si }f \text{ est continue au point }x \\
|
||||
\text{milieu du saut de } f(x) &\text{si }f \text{ a un saut en }x
|
||||
\end{cases}
|
||||
$$
|
||||
|
||||
|
||||
## Transformée de Fourier discrète
|
||||
|
||||
La transformée de Fourier discrète (DFT - Discret Fourier Transform) est une transformation discrète utilisée pour analyser le spectre fréquentiel
|
||||
d'un signal discret.
|
||||
Elle est l'équivalent discret de la transformée de Fourier, qui est utilisée pour analyser les signaux continus.
|
||||
|
||||
On pose: $\Omega := \omega \cdot T_{s}$
|
||||
La transformation de Fourier de $x[k]$ est donnée par la formule suivante :
|
||||
$$\mathcal{F}\{x[k]\} = X(\Omega) \quad := \quad\sum^{+\infty}_{k=-\infty}x[k]\cdot e^{-jk\Omega}= \mathcal{F}\{x_{s}(t)\}$$
|
||||
|
||||
|
||||
$$\mathcal{F\{x[k]\} = \mathcal{Z}\{x[k]\}}|_{z=e^{j\Omega}}$$
|
||||

|
||||
$$x[k]=\frac{1}{2\pi} \int^{+\pi}_{-\pi}X(\Omega)e^{jk\Omega} d\Omega$$
|
||||
|
||||
|
||||
|
||||
|
||||
La DFT est utilisée dans de nombreux domaines, notamment en traitement du signal, en télécommunications, en traitement d'images
|
||||
et en compression de données.
|
||||
|
||||
> [!NOTE|label:FFT]
|
||||
> La transformée de Fourier rapide (FFT) est un algorithme efficace pour calculer la DFT. Elle est largement utilisée dans
|
||||
> de nombreuses applications, notamment en traitement du signal et en compression de données.
|
||||
|
||||
----------
|
||||
|
||||
# Réponse impulsionnelle
|
||||
La réponse impulsionnelle $h[k]$ d'un système discret LIT est sa réponse à l'impulsion unitaire à l'origine $δ[k]$.
|
||||
|
||||

|
||||
|
||||
Avant l'impulsion, la réponse impulsionnelle $h$ est nulle.
|
||||
Après l'impulsion, la réponse impulsionnelle $h$ est la réponse du système à l'impulsion unitaire à l'origine.
|
||||
|
||||
*Causal* = le futur n'a pas d'influence sur le passé.
|
||||
|
||||
$$ h[k] = \mathcal{H} \{ δ[k] \} $$
|
||||
|
||||
$\mathcal{H}$ : Heaviside (peut aussi se noter $\varepsilon$ pour un heaviside en discret) <br>
|
||||
$y[k]$ : réponse du système <br>
|
||||
$x[k]$ : signal d'entrée <br>
|
||||
$h[k]$ : réponse impulsionnelle <br>
|
||||
|
||||
$$
|
||||
\forall x \in \mathbb{R}, \mathcal{H}(x)=\begin{cases}
|
||||
0 &\text{ si } x<0 \\
|
||||
1 &\text{ si } x \geq 0
|
||||
\end{cases}
|
||||
$$
|
||||
|
||||
Pour un système causal
|
||||
|
||||
$$\begin{align}
|
||||
y[k] &= \sum^{k}_{n=-\infty}(x[n]\cdot h[k-n]) \\
|
||||
\\
|
||||
y[k] &= \sum^{+\infty}_{n=0}(x[k-n]\cdot h[n])
|
||||
\end{align}$$
|
||||
|
||||
La réponse impulsionnelle caractérise <u>ENTIÈREMENT</u> un système LIT.
|
||||
|
||||
## Système LIT stable
|
||||
Un système LIT (Linéaire et Invariant dans le Temps) est stable si et seulement si sa réponse impulsionnelle est sommable.
|
||||
|
||||
> [!NOTE]
|
||||
> $\sum^{+\infty}_{k=-\infty}|h[k]|<+\infty$
|
||||
|
||||
## Système LIT causal à réponse impulsionnelle finie
|
||||
Un système LIT (Linéaire et Invariant dans le Temps) est causal à réponse impulsionnelle finie si et seulement si sa
|
||||
réponse impulsionnelle est nulle pour $k<0$ et sommable pour $k\geq0$.
|
||||
|
||||
> [!NOTE]
|
||||
> fini: $h(k) |_{k<\infty} = 0$
|
||||
|
||||
$$\begin{align}
|
||||
y[k] &= \sum^{N}_{n=0}x[k-n]\cdot h[n] \\
|
||||
&= b_{0}x[k]+b_{1}x[k-1]+\dots+b_{N}x[k-N]
|
||||
\end{align}$$
|
||||
$N$ : Limite de notre réponse impulsionnelle.
|
||||
|
||||

|
||||
|
||||
En faisant la somme, la réponse impulsionnelle se transforme en facteurs $(b_0, b_1, b_2, ..., b_n)$.
|
||||
On peut retrouver la réponse impulsionnelle à partir de ces facteurs.
|
||||
La somme décomposée est appelée *équation aux différences*.
|
||||
|
||||
Pour obtenir la réponse indicielle depuis la réponse impulsionnelle, on utilise la somme cumulée de la réponse impulsionnelle.
|
||||
On peut aussi mathématiquement obtenir la réponse impulsionnelle en multipliant la réponse indicielle par Heaviside ($F(z)\cdot \varepsilon(z)$).
|
||||
|
||||
----------
|
||||
|
||||
# Transformée en Z
|
||||
La transformée en Z, également connue sous le nom de transformée en fréquence Z, est une technique de traitement du
|
||||
signal utilisée pour analyser et manipuler les signaux numériques.
|
||||
Elle permet de décomposer un signal en fonction de la fréquence et de fournir des informations sur la nature du signal.
|
||||
|
||||
La principale différence entre la transformée en Z et la transformation de Laplace (ou S) est que la transformée en Z
|
||||
est adaptée aux signaux discrets, c'est-à-dire aux valeurs qui ne varient pas continuellement dans le temps.
|
||||
En revanche, la transformation de Laplace est utilisée pour les signaux continus et permet d'étudier leur réponse en
|
||||
fréquence et en phase.
|
||||
|
||||
En résumé, la transformée en Z est conçue pour les signaux discrets et les systèmes numériques, tandis que la
|
||||
transformation de Laplace s'applique aux signaux continus et aux systèmes analogiques.
|
||||
|
||||
[Tableau des transformées en Z](/infotro/Eln/Ztransform)
|
||||
|
||||
> [!NOTE|label:S]
|
||||
> Domaine fréquentiel continue avec Laplace.
|
||||
|
||||
> [!NOTE|label:Z]
|
||||
> Domaine fréquentiel discret.
|
||||
|
||||
Avec $z:=e^{sT_{s}}$ on a
|
||||
$$\mathcal{Z}\{x[k]\} = X(z) \quad := \quad \sum^{+\infty}_{k=-\infty}(x[k]\cdot z^{-k})=\mathcal{L\{x_{s}(t)\}}$$
|
||||
qui est la transformée en Z de $x[k]$.<br>
|
||||
|
||||
$T_{s}$ : Période d'échantillonnage. <br>
|
||||
$x[k]$ : Fonction du [Tableau des transformées en Z](/infotro/Eln/Ztransform) ou valeur du graphe des réponses impulsionnelle<br>
|
||||
|
||||
On a aussi la propriété suivante :
|
||||
$$\mathcal{Z}\{x[k-n]\}=z^{-n}\cdot X(z)$$
|
||||
|
||||
## Marche à suivre - Transformée en *Z*
|
||||
1. Avoir une équation aux différences.
|
||||
2. Pour chaque terme de l'équation, appliquer la transformée en Z.
|
||||
Avec :
|
||||
- $f[k] => F(z)$
|
||||
- $f[k-n] => z^{-n}\cdot F(z)$
|
||||
3. Isoler $Y(z)$.
|
||||
4. $H(z) = \frac{Y(z)}{X(z)}$ est la fonction de transfert.
|
||||
|
||||
> [!NOTE|label:Example]
|
||||
> L'équation aux différences est donnée par $$\tag{1} y[k] = \frac{1}{2}(x[k]+y[k-1])$$
|
||||
> devient
|
||||
> $$\begin{align}
|
||||
\tag{2} Y(z) &= \frac{1}{2}(X(z)+z^{-1}\cdot Y(z)) \\
|
||||
Y(z)-\frac{1}{2}\cdot z^{-1}\cdot Y(z) &= \frac{1}{2}\cdot X(z) \\
|
||||
\tag{3} Y(z) &= \frac{1}{2-z^{-1}}X(z) \\
|
||||
\tag{4} H(z) &= \frac{Y(z)}{X(z)} = \frac{1}{2-z^{-1}}
|
||||
\end{align}$$
|
||||
|
||||
> [!TIP]
|
||||
> $h[k]\stackrel{\text{Z tabel}}{\longleftrightarrow}H(z)$
|
||||
>
|
||||
> Exemple: $h[k] = 2^{-(k+1)}$ devient
|
||||
> $$\begin{align}
|
||||
2^{-(k+1)} &= 2^{-k}\cdot 2^{-1} = \frac{1}{2}\cdot 2^{-k} = \frac{1}{2}\cdot (2^{-1})^k \\
|
||||
&\to \frac{1}{2}\cdot \frac{z}{z-\frac{1}{2}} =\frac{z}{2z-1} = \frac{1}{2-\frac{1}{z}} = \frac{1}{2-z^{-1}}
|
||||
\end{align}$$
|
||||
|
||||
> [!WARNING]
|
||||
> Les propriétés de la transformée en Z se trouvent dans le [Tableau des transformées en Z](/infotro/Eln/Ztransform)
|
||||
|
||||
|
||||
L'équation aux différences
|
||||
$$y[k] + a_{1}y[k-1]+\dots+a_{M}y[k-M] \quad = \quad b_{0}x[k]+b_{1}x[k-1]+\dots+b_{N}x[k-N]$$
|
||||
devient par la transformation en z
|
||||
$$H(z) = \frac{Y(z)}{X(z)} = \frac{b_{0}+b_{1}z^{-1}+\dots+b_{N}z^{-1}}{1+a_{1}z^{-1}+\dots+a_{M}z^{-M}} \quad a_{m}, b_{n} \space \text{réels}$$
|
||||
|
||||
Les zéros et les pôles de la fonction de transfert donnent donc :
|
||||
$$H(z) = K \frac{(z-o_{1})\cdot (z-o_{2})\cdot \space \dots \space \cdot (z-o_{N})}{(z-p_{1})\cdot (z-p_{2})\cdot \space \dots \space \cdot (z-p_{M}) }\quad o_{n}, p_{n} \space \text{complexes}$$
|
||||
|
||||
|
||||
## Marche à suivre - Transformée en *Z* inverse
|
||||
1. Avoir une fonction de transfert : $H(z) = \frac{Y(z)}{X(z)}$.
|
||||
2. Effectuer le numérateur et le dénominateur.
|
||||
3. Mettre les $z$ en puissance négative : $\cdot \frac{z^{-1}}{z^{-1}}$
|
||||
4. Effectuer le produit en croix.
|
||||
5. Pour chaque terme, appliquer la transformée en Z inverse.
|
||||
- $F(z) \implies f[k]$
|
||||
- $F(z)\cdot z^{-n} \implies f[k-n]$
|
||||
6. Isoler $y[k]$. Et on a notre équation aux différences.
|
||||
|
||||
----------
|
||||
|
||||
# Approximation bilinéaire
|
||||
L'approximation bilinéaire est une technique utilisée pour convertir une fonction de transfert continue en une fonction
|
||||
de transfert discrète.
|
||||
Elle est utilisée dans le domaine du traitement du signal pour convertir un système continu en un système discret.
|
||||
|
||||
Développement en série de la fonction $ln$ autour de 1 ($x=0$) :
|
||||
$$\ln\left( \frac{1+x}{1-x} \right) = 2\left(x + \frac{x^{3}}{3} + \frac{x^{5}}{5} + \dots \right)$$
|
||||
donne l'approximation
|
||||
$\ln\left( \frac{1+x}{1-x} \right) \approx 2x$
|
||||
|
||||
## Approximation bilinéaire - Pôles et zéros
|
||||
$$p_{di} = e^{p_{ai} \cdot T} \qquad z_{di} = e^{z_{ai} \cdot T} $$
|
||||
- $k_a$, $p_{ai}$ and $z_{ai}$ sont le gain, les pôles et les zéros de notre filtre analogique
|
||||
Attention, $k_a$ n'est pas le gain DC
|
||||
- $k_d$, $p_{di}$ and $z_{di}$ sont le gain, des pôles et les zéros de notre filtre numérique.
|
||||
Pour un filtre passe bas sans zéros, le gain numérique est:
|
||||
$$k_{d} = k_{a}\cdot \prod_{i} \frac{1-e^{P_{ai}\cdot T}}{P_{ai}}$$
|
||||
|
||||
On peut appliquer la transformation bilinéaire aux pôles et zéros du filtre analogique pour obtenir les pôles et zéros du filtre numérique.
|
||||
$$
|
||||
p_{di} = \frac{1+p_{ai}\cdot \frac{T}{2}}{1-p_{ai}\cdot \frac{T}{2}}
|
||||
\qquad \qquad
|
||||
z_{di} = \frac{1+z_{ai}\cdot \frac{T}{2}}{1-z_{ai}\cdot \frac{T}{2}}
|
||||
$$
|
||||
Pour un filtre passe bas, le gain "bilinear" est :
|
||||
$$k_{d} = k_{a}\cdot \frac{1}{\prod_{i}\left( \frac{2}{T}-p_{ai} \right)}$$
|
||||
|
||||
Pour passer de $\omega_o'$ à $\omega_o$ et vice-versa, on utilise la relation suivante :
|
||||
$$\begin{align}
|
||||
\omega_{0} &= 2f_{s}\tan\left( \frac{\omega_{0}'}{2f_{s}} \right) \\
|
||||
\\
|
||||
\omega_{0}'&=2f_{S}\cdot \arctan\left( \frac{\omega_{0}}{2fs} \right)
|
||||
\end{align}$$
|
||||
|
||||
|
||||
## Autres méthodes d'approximation d'un système continu LIT par un syst. discret
|
||||
On détermine la réponse impulsionnelle du système discret LIT à partir de celle du système continu en appliquant la relation :
|
||||
$$h[k] = T_{s}\cdot(kT_{s})$$
|
||||
$h(kT_s)$ : Réponse impulsionnelle en continu <br>
|
||||
|
||||
On peut du coup approximer l'équation différentielle par une équation aux différences.
|
||||
Par exemple pour la dérivée première de $x(t)$, on a
|
||||
$$x'[k]= \frac{x[k]-x[k-1]}{T_{s}}$$
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
----------
|
||||
@@ -0,0 +1,30 @@
|
||||
<header><h1>
|
||||
Tableau des transformées en Z
|
||||
</h1></header>
|
||||
|
||||
-----------
|
||||
|
||||
$$f[k] =\varepsilon[x]=\begin{cases}
|
||||
1^{x} &\text{ si } x\geq 0 \\
|
||||
0 &\text{ si } x<0
|
||||
\end{cases} \qquad \forall x=k,k-n
|
||||
$$
|
||||
$\varepsilon$ : fonction de Heaviside en discret
|
||||
|
||||
----------
|
||||
|
||||
| Nr. | $f[k]$ | $F(z)$ | $f(t)$ | $F(s)$ |
|
||||
|------|-------------------|----------------------------------------|-------------|--------------------|
|
||||
| 1 | $\delta_{k}, 0^k$ | 1 | $\delta(t)$ | 1 |
|
||||
| 2 | $1^k$ | $\frac{z}{z-1}$ | $1$ | $\frac{1}{s}$ |
|
||||
| 3 | $kT$ | $\frac{Tz}{z-1}^{2}$ | $t$ | $\frac{1}{s^{2}}$ |
|
||||
| 4 | $(kT)^{2}$ | $\frac{T^{2}z(z+1)}{(z-1)^{3}}$ | $t^{2}$ | $\frac{2!}{s^{3}}$ |
|
||||
| 5 | $(kT)^{3}$ | $\frac{T^{3}z(z^{2}+4z+1)}{(z-1)^{4}}$ | $t^{3}$ | $\frac{3!}{s^{4}}$ |
|
||||
| 6 | $e^{akT}$ | $\frac{z}{z-e^{aT}}$ | $e^{at}$ | $\frac{1}{s-a}$ |
|
||||
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
Dans la théorie Eln la partie transformée en Z commence à la slide 23 de "Eln2_LTI_0910_f.pdf".
|
||||
@@ -0,0 +1,133 @@
|
||||
<header><h1>
|
||||
Lab 06 - Filters
|
||||
</h1></header>
|
||||
|
||||
----------
|
||||
|
||||
In this laboratory on digital filters, the main objective is to understand how digital filters work.
|
||||
We will use MATLAB to simulate and implement these filters on a digital signal processor (DSP).
|
||||
|
||||
# Analog filter design
|
||||
|
||||
We must design Butterworth low-pass filter that cuts at 1kHz as shown with the following equation :
|
||||
|
||||
$$F(s) = \frac{1}{\frac{s^{2}}{\omega_c^{2}} + s\cdot\frac{1.414}{\omega_c} + 1} $$
|
||||
|
||||
In according to the sample theory, we have to choose at least 2 times the maximum frequency of the signal.
|
||||
In this case, the maximum frequency is 1 kHz, so we have to choose a sample frequency at least at 2 kHz.
|
||||
We take 10 time $f_c$ for the sample frequency, so we choose 20 kHz.
|
||||
|
||||
|
||||
## Transfer function
|
||||
For calculate the transfer function, we have to calculate the poles and zeros of the filter.
|
||||
|
||||
$$p_{di} = e^{p_{ai} \cdot T} \qquad z_{di} = e^{z_{ai} \cdot T} $$
|
||||
|
||||
- $k_a$, $p_{ai}$ and $z_{ai}$ are the gain, poles and zeros of the analog filter that the representation *zpk*.
|
||||
Be careful, $k_a$ is not the DC gain.
|
||||
- $k_d$, $p_{di}$ and $z_{di}$ are the gain, poles and zeros of the digital filter.
|
||||
|
||||
For a low pass filter without zeros, the "digital" gain is :
|
||||
$$k_{d} = k_{a}\cdot \prod_{i} \frac{1-e^{P_{ai}\cdot T}}{P_{ai}}$$
|
||||
|
||||
We can apply the bilinear transformation to the poles and zeros of the analog filter to obtain the poles and zeros of the digital filter.
|
||||
$$
|
||||
p_{di} = \frac{1+p_{ai}\cdot \frac{T}{2}}{1-p_{ai}\cdot \frac{T}{2}}
|
||||
\qquad \qquad
|
||||
z_{di} = \frac{1+z_{ai}\cdot \frac{T}{2}}{1-z_{ai}\cdot \frac{T}{2}}
|
||||
$$
|
||||
And for the low pass filter, the "bilinear" gain is :
|
||||
$$k_{d} = k_{a}\cdot \frac{1}{\prod_{i}\left( \frac{2}{T}-p_{ai} \right)}$$
|
||||
|
||||
On the following diagram, we can see the bode diagram of the analog, digital filter and the filter with the bilinear transformation.
|
||||
|
||||

|
||||
|
||||
# IIR filter design
|
||||
|
||||
We must design an IIR filter with the following specifications :
|
||||
|
||||
- $H_0 = 0 [dB]$
|
||||
- $H_1 = -3 [dB]$
|
||||
- $H_2 = -40 [dB]$
|
||||
- $f_{pass} = 7 [kHz]$
|
||||
- $f_{stop} = 9 [kHz]$
|
||||
- $f_s = 48 [kHz]$
|
||||
|
||||
We first calculate the analog filter for search the order with the function *ellipord*.
|
||||
Then, we calculate the poles and zeros with the function *ellip*.
|
||||
We apply the bilinear transformation to the poles and zeros to obtain the poles and zeros of the digital filter.
|
||||
Finally, we can plot the bode diagram of the digital filter and the filter with the bilinear transformation.
|
||||
|
||||

|
||||
|
||||
|
||||
# DSP implementation
|
||||
|
||||
We will implement the previously designed digital filter using a C program that will run in real-time on a fixed-point DSP.
|
||||
We will employ a cascade structure, consisting of 2 second-order stages.
|
||||
We will use the TMS320C6211 DSK from Texas Instruments.
|
||||
|
||||
The following equation represent an universal transfer function of a second-order filter :
|
||||
$$H(z) = \frac{b_{0}+b_{1}\cdot z^{-1}+b_{2}\cdot z^{-2}}{a_{0}+a_{1}\cdot z^{-1}+a_{2}\cdot z^{-2}}$$
|
||||
|
||||
The following figure depicts the block diagram of this implementation in the case where a0 = 1
|
||||
This structure is commonly referred to as "Direct Form I." It requires the storage of past values of both the input and output.
|
||||
|
||||

|
||||
|
||||
So we have the difference equation :
|
||||
$$y(n) = b_{0}\cdot x(n)+b_{1}\cdot x(n-1)+b_{2}\cdot x(n-2)-a_{1}\cdot y(n-1)-a_{2}\cdot y(n-2)$$
|
||||
|
||||
It exists a second structure called "Direct Form II" which is more efficient in terms of computation and memory usage.
|
||||
|
||||

|
||||
|
||||
The difference equation is :
|
||||
$$\begin{align}
|
||||
y(n) &= tmp\cdot b_{0}+w[0]\cdot b_{1}+w[1]\cdot b_{2} \\
|
||||
\\
|
||||
tmp &= x(n)-a_{1}\cdot w[0]-a_{2}\cdot w[1] \\
|
||||
\\
|
||||
w[0] &= tmp_{prev} \\
|
||||
\\
|
||||
w[1] &= w[0]_{prev}
|
||||
\end{align}$$
|
||||
|
||||
The variables can be translated into code by shifting the coefficients 14 positions to the left to enhance precision
|
||||
after the decimal point, as the DSP only works with integers.
|
||||
|
||||
Structures for the coefficients and the state variables are defined as follows :
|
||||
```c
|
||||
typedef struct {
|
||||
short b0;
|
||||
short b1;
|
||||
short b2;
|
||||
short a0;
|
||||
short a1;
|
||||
short a2;
|
||||
} coeff_sec_order;
|
||||
|
||||
short w3[2];
|
||||
short w4[2];
|
||||
|
||||
coeff_sec_order stage1 = { 469, 349, 469, 16384, -23699, 10692};
|
||||
coeff_sec_order stage2 = {16384, -11187, 16384, 16384, -19657, 14890};
|
||||
```
|
||||
|
||||
The following code is the implementation of the Direct Form II function :
|
||||
```c
|
||||
void iir_sec_order_dir_s(coeff_sec_order Coeff, short*w, short* Input, short* Output) {
|
||||
register short tmp;
|
||||
|
||||
tmp = (long)* Input - (( (Long)Coeff.a1 * (long)w[0]) >> SHIFT) - (( (long)Coeff.a2 * (long)w[1]) >> SHIFT);
|
||||
*Output = (( (long)Coeff.b0 * (long)tmp >> SHIFT) + (( (long)Coeff.b1 * (long)w[0]) >> SHIFT) + (( (long)Coeff.b2 * (long)w[1]) >> SHIFT);
|
||||
|
||||
w[1] = w[0];
|
||||
w[0] = tmp;
|
||||
}
|
||||
```
|
||||
|
||||
For a better effect of the filter, the function can be called several times.
|
||||
|
||||
|
||||
|
After Width: | Height: | Size: 78 KiB |
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Do not edit this file with editors other than draw.io -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="840px" height="286px" viewBox="-0.5 -0.5 840 286" content="<mxfile host="app.diagrams.net" modified="2024-02-03T22:03:26.442Z" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:122.0) Gecko/20100101 Firefox/122.0" etag="Da41eLOf3AT8AQXfy7Nj" version="22.1.22"><diagram name="Page-1" id="CkdF-7UHB_tb0CRjxC_V">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</diagram></mxfile>"><defs><style type="text/css">@import url(https://fonts.googleapis.com/css2?family=Architects+Daughter:wght@400;500);
</style><style xmlns="http://www.w3.org/1999/xhtml" id="MJX-SVG-styles">
mjx-container[jax="SVG"] {
 direction: ltr;
}

mjx-container[jax="SVG"] > svg {
 overflow: visible;
 min-height: 1px;
 min-width: 1px;
}

mjx-container[jax="SVG"] > svg a {
 fill: blue;
 stroke: blue;
}

mjx-container[jax="SVG"][display="true"] {
 display: block;
 text-align: center;
 margin: 1em 0;
}

mjx-container[jax="SVG"][display="true"][width="full"] {
 display: flex;
}

mjx-container[jax="SVG"][justify="left"] {
 text-align: left;
}

mjx-container[jax="SVG"][justify="right"] {
 text-align: right;
}

g[data-mml-node="merror"] > g {
 fill: red;
 stroke: red;
}

g[data-mml-node="merror"] > rect[data-background] {
 fill: yellow;
 stroke: none;
}

g[data-mml-node="mtable"] > line[data-line], svg[data-table] > g > line[data-line] {
 stroke-width: 70px;
 fill: none;
}

g[data-mml-node="mtable"] > rect[data-frame], svg[data-table] > g > rect[data-frame] {
 stroke-width: 70px;
 fill: none;
}

g[data-mml-node="mtable"] > .mjx-dashed, svg[data-table] > g > .mjx-dashed {
 stroke-dasharray: 140;
}

g[data-mml-node="mtable"] > .mjx-dotted, svg[data-table] > g > .mjx-dotted {
 stroke-linecap: round;
 stroke-dasharray: 0,140;
}

g[data-mml-node="mtable"] > g > svg {
 overflow: visible;
}

[jax="SVG"] mjx-tool {
 display: inline-block;
 position: relative;
 width: 0;
 height: 0;
}

[jax="SVG"] mjx-tool > mjx-tip {
 position: absolute;
 top: 0;
 left: 0;
}

mjx-tool > mjx-tip {
 display: inline-block;
 padding: .2em;
 border: 1px solid #888;
 font-size: 70%;
 background-color: #F8F8F8;
 color: black;
 box-shadow: 2px 2px 5px #AAAAAA;
}

g[data-mml-node="maction"][data-toggle] {
 cursor: pLine truncated
|
||||
|
After Width: | Height: | Size: 82 KiB |
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Do not edit this file with editors other than draw.io -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="520px" height="366px" viewBox="-0.5 -0.5 520 366" content="<mxfile host="app.diagrams.net" modified="2024-02-03T22:16:47.416Z" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:122.0) Gecko/20100101 Firefox/122.0" etag="gL6vpL3MQfkpyT-y1ADy" version="22.1.22"><diagram name="Page-1" id="CkdF-7UHB_tb0CRjxC_V">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</diagram></mxfile>"><defs><style type="text/css">@import url(https://fonts.googleapis.com/css2?family=Architects+Daughter:wght@400;500);
</style><style xmlns="http://www.w3.org/1999/xhtml" id="MJX-SVG-styles">
mjx-container[jax="SVG"] {
 direction: ltr;
}

mjx-container[jax="SVG"] > svg {
 overflow: visible;
 min-height: 1px;
 min-width: 1px;
}

mjx-container[jax="SVG"] > svg a {
 fill: blue;
 stroke: blue;
}

mjx-container[jax="SVG"][display="true"] {
 display: block;
 text-align: center;
 margin: 1em 0;
}

mjx-container[jax="SVG"][display="true"][width="full"] {
 display: flex;
}

mjx-container[jax="SVG"][justify="left"] {
 text-align: left;
}

mjx-container[jax="SVG"][justify="right"] {
 text-align: right;
}

g[data-mml-node="merror"] > g {
 fill: red;
 stroke: red;
}

g[data-mml-node="merror"] > rect[data-background] {
 fill: yellow;
 stroke: none;
}

g[data-mml-node="mtable"] > line[data-line], svg[data-table] > g > line[data-line] {
 stroke-width: 70px;
 fill: none;
}

g[data-mml-node="mtable"] > rect[data-frame], svg[data-table] > g > rect[data-frame] {
 stroke-width: 70px;
 fill: none;
}

g[data-mml-node="mtable"] > .mjx-dashed, svg[data-table] > g > .mjx-dashed {
 stroke-dasharray: 140;
}

g[data-mml-node="mtable"] > .mjx-dotted, svg[data-table] > g > .mjx-dotted {
 stroke-linecap: round;
 stroke-dasharray: 0,140;
}

g[data-mml-node="mtable"] > g > svg {
 overflow: visible;
}

[jax="SVG"] mjx-tool {
 display: inline-block;
 position: relative;
 width: 0;
 height: 0;
}

[jax="SVG"] mjx-tool > mjx-tip {
 position: absolute;
 top: 0;
 left: 0;
}

mjx-tool > mjx-tip {
 display: inline-block;
 padding: .2em;
 border: 1px solid #888;
 font-size: 70%;
 background-color: #F8F8F8;
 color: black;
 box-shadow: 2px 2px 5px #AAAAAA;
}

g[data-mml-node="maction"][data-toggle] {
 cursor: pLine truncated
|
||||
|
After Width: | Height: | Size: 79 KiB |
|
After Width: | Height: | Size: 72 KiB |
|
After Width: | Height: | Size: 47 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 49 KiB |
|
After Width: | Height: | Size: 48 KiB |
|
After Width: | Height: | Size: 48 KiB |
|
After Width: | Height: | Size: 143 KiB |
|
After Width: | Height: | Size: 115 KiB |
|
After Width: | Height: | Size: 104 KiB |
|
After Width: | Height: | Size: 84 KiB |
|
After Width: | Height: | Size: 65 KiB |
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Do not edit this file with editors other than draw.io -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="289px" height="262px" viewBox="-0.5 -0.5 289 262" content="<mxfile host="app.diagrams.net" modified="2024-02-02T17:56:41.416Z" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:122.0) Gecko/20100101 Firefox/122.0" etag="i95P2xGId2tSeVqMwiar" version="23.1.1" scale="1" border="0">
 <diagram name="Page-1" id="ASSYr2psEjrnozlwjsYB">
 <mxGraphModel dx="1050" dy="626" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="1" shadow="0">
 <root>
 <mxCell id="0" />
 <mxCell id="1" parent="0" />
 <mxCell id="hvOSe9N11qCTzQnM01ZF-1" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=none;strokeColor=#808080;" vertex="1" parent="1">
 <mxGeometry x="360" y="280" width="160" height="160" as="geometry" />
 </mxCell>
 <mxCell id="hvOSe9N11qCTzQnM01ZF-2" value="" style="endArrow=classic;html=1;rounded=0;strokeColor=#808080;" edge="1" parent="1">
 <mxGeometry width="50" height="50" relative="1" as="geometry">
 <mxPoint x="439.5" y="480" as="sourcePoint" />
 <mxPoint x="440" y="240" as="targetPoint" />
 </mxGeometry>
 </mxCell>
 <mxCell id="hvOSe9N11qCTzQnM01ZF-3" value="" style="endArrow=classic;html=1;rounded=0;strokeColor=#808080;" edge="1" parent="1">
 <mxGeometry width="50" height="50" relative="1" as="geometry">
 <mxPoint x="320" y="360" as="sourcePoint" />
 <mxPoint x="560" y="360" as="targetPoint" />
 </mxGeometry>
 </mxCell>
 <mxCell id="hvOSe9N11qCTzQnM01ZF-4" value="1" style="text;strokeColor=none;fillColor=none;html=1;fontSize=20;fontStyle=1;verticalAlign=bottom;align=left;fontColor=#808080;" vertex="1" parent="1">
 <mxGeometry x="520" y="320" width="40" height="40" as="geometry" />
 </mxCell>
 <mxCell id="hvOSe9N11qCTzQnM01ZF-5" value="1" style="text;strokeColor=none;fillColor=none;html=1;fontSize=20;fontStyle=1;verticalAlign=bottom;align=left;fontColor=#808080;" vertex="1" parent="1">
 <mxGeometry x="440" y="240" width="40" height="40" as="geometry" />
 </mxCell>
 <mxCell id="hvOSe9N11qCTzQnM01ZF-7" value="&lt;div align=&quot;center&quot;&gt;Z&lt;/div&gt;" style="text;strokeColor=#808080;fillColor=none;html=1;fontSize=20;fontStyle=1;verticalAlign=middle;align=center;fontColor=#808080;" vertex="1" parent="1">
 <mxGeometry x="530" y="240" width="30" height="40" as="geometry" />
 </mxCell>
 <mxCell id="hvOSe9N11qCTzQnM01ZF-8" value="$$\mathcal{Z} = e^{\mathbf{j}\omega}$$" style="text;strokeColor=none;fillColor=none;fontSize=20;fontStyle=1;verticalAlign=middle;align=center;fontColor=#808080;html=1;" vertex="1" parent="1">
 <mxGeometry x="280" y="240" width="120" height="40" as="geometry" />
 </mxCell>
 </root>
 </mxGraphModel>
 </diagram>
</mxfile>
"><defs><style xmlns="http://www.w3.org/1999/xhtml" id="MJX-SVG-styles">
mjx-container[jax="SVG"] {
 direction: ltr;
}

mjx-container[jax="SVG"] > svg {
 overflow: visible;
 min-height: 1px;
 min-width: 1px;
}

mjx-container[jax="SVG"] > svg a {
 fill: blue;
 stroke: blue;
}

mjx-container[jax="SVG"][display="true"] {
 display: block;
 text-align: center;
 margin: 1em 0;
}

Line truncated
|
||||
|
After Width: | Height: | Size: 79 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 87 KiB |
|
After Width: | Height: | Size: 85 KiB |
|
After Width: | Height: | Size: 112 KiB |
|
After Width: | Height: | Size: 62 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 77 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 75 KiB |
|
After Width: | Height: | Size: 92 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 49 KiB |
|
After Width: | Height: | Size: 54 KiB |
|
After Width: | Height: | Size: 55 KiB |
|
After Width: | Height: | Size: 21 KiB |
@@ -0,0 +1,606 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
|
||||
<svg
|
||||
style=""
|
||||
version="1.1"
|
||||
width="1220px"
|
||||
height="361px"
|
||||
viewBox="-0.5 -0.5 1220 361"
|
||||
content="<mxfile host="app.diagrams.net" modified="2024-02-02T14:30:19.773Z" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:122.0) Gecko/20100101 Firefox/122.0" etag="Fj5xVG2gLKINAW_kfnPL" version="23.0.0" scale="1" border="0"> <diagram name="Page-1" id="iDfkVSNYGaoFjq7dBcwx"> <mxGraphModel dx="1500" dy="894" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0"> <root> <mxCell id="0" /> <mxCell id="1" parent="0" /> <mxCell id="Gu1WY5u3SJpsm1HDf4T9-3" value="" style="endArrow=classic;html=1;rounded=0;strokeColor=#808080;strokeWidth=4;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" target="Gu1WY5u3SJpsm1HDf4T9-4"> <mxGeometry width="50" height="50" relative="1" as="geometry"> <mxPoint x="80" y="320" as="sourcePoint" /> <mxPoint x="180" y="160" as="targetPoint" /> </mxGeometry> </mxCell> <mxCell id="Gu1WY5u3SJpsm1HDf4T9-4" value="D" style="text;strokeColor=#808080;fillColor=#DDE1CC;html=1;fontSize=24;fontStyle=1;verticalAlign=middle;align=center;rounded=0;" vertex="1" parent="1"> <mxGeometry x="280" y="280" width="120" height="80" as="geometry" /> </mxCell> <mxCell id="Gu1WY5u3SJpsm1HDf4T9-5" value="b&lt;sub&gt;1&lt;/sub&gt;" style="text;strokeColor=#808080;fillColor=#DDE1CC;html=1;fontSize=24;fontStyle=1;verticalAlign=middle;align=center;rounded=1;arcSize=50;" vertex="1" parent="1"> <mxGeometry x="440" y="400" width="80" height="80" as="geometry" /> </mxCell> <mxCell id="Gu1WY5u3SJpsm1HDf4T9-6" value="b&lt;sub&gt;0&lt;/sub&gt;" style="text;strokeColor=#808080;fillColor=#DDE1CC;html=1;fontSize=24;fontStyle=1;verticalAlign=middle;align=center;rounded=1;arcSize=50;" vertex="1" parent="1"> <mxGeometry x="160" y="400" width="80" height="80" as="geometry" /> </mxCell> <mxCell id="Gu1WY5u3SJpsm1HDf4T9-7" value="b&lt;sub&gt;n&lt;/sub&gt;" style="text;strokeColor=#808080;fillColor=#DDE1CC;html=1;fontSize=24;fontStyle=1;verticalAlign=middle;align=center;rounded=1;arcSize=50;" vertex="1" parent="1"> <mxGeometry x="1000" y="400" width="80" height="80" as="geometry" /> </mxCell> <mxCell id="Gu1WY5u3SJpsm1HDf4T9-9" value="D" style="text;strokeColor=#808080;fillColor=#DDE1CC;html=1;fontSize=24;fontStyle=1;verticalAlign=middle;align=center;rounded=0;" vertex="1" parent="1"> <mxGeometry x="560" y="280" width="120" height="80" as="geometry" /> </mxCell> <mxCell id="Gu1WY5u3SJpsm1HDf4T9-10" value="D" style="text;strokeColor=#808080;fillColor=#DDE1CC;html=1;fontSize=24;fontStyle=1;verticalAlign=middle;align=center;rounded=0;" vertex="1" parent="1"> <mxGeometry x="840" y="280" width="120" height="80" as="geometry" /> </mxCell> <mxCell id="Gu1WY5u3SJpsm1HDf4T9-11" value="&lt;font style=&quot;font-size: 80px;&quot;&gt;∑&lt;/font&gt;" style="text;strokeColor=#808080;fillColor=#DDE1CC;html=1;fontSize=24;fontStyle=1;verticalAlign=middle;align=center;rounded=1;arcSize=50;" vertex="1" parent="1"> <mxGeometry x="80" y="520" width="1080" height="120" as="geometry" /> </mxCell> <mxCell id="Gu1WY5u3SJpsm1HDf4T9-12" value="" style="endArrow=classic;html=1;rounded=0;strokeColor=#808080;strokeWidth=4;entryX=0;entryYLine truncated
|
||||
id="svg29"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:xhtml="http://www.w3.org/1999/xhtml">
|
||||
<defs
|
||||
id="defs1" />
|
||||
<g
|
||||
id="layer1">
|
||||
<path
|
||||
d="M 9,40 H 197.03"
|
||||
fill="none"
|
||||
stroke="#808080"
|
||||
stroke-width="4"
|
||||
stroke-miterlimit="10"
|
||||
pointer-events="stroke"
|
||||
id="path1"
|
||||
style="background-color:rgb(255, 255, 255)" />
|
||||
<path
|
||||
d="m 204.53,40 -10,5 2.5,-5 -2.5,-5 z"
|
||||
fill="#808080"
|
||||
stroke="#808080"
|
||||
stroke-width="4"
|
||||
stroke-miterlimit="10"
|
||||
pointer-events="all"
|
||||
id="path2"
|
||||
style="background-color:rgb(255, 255, 255)" />
|
||||
<rect
|
||||
x="209"
|
||||
y="0"
|
||||
width="120"
|
||||
height="80"
|
||||
fill="#dde1cc"
|
||||
stroke="#808080"
|
||||
pointer-events="all"
|
||||
id="rect2"
|
||||
style="background-color:rgb(255, 255, 255)" />
|
||||
<g
|
||||
id="g2"
|
||||
style="background-color:rgb(255, 255, 255)"
|
||||
transform="translate(-0.5,-0.5)">
|
||||
<switch
|
||||
id="switch2">
|
||||
<foreignObject
|
||||
style="overflow: visible; text-align: left;"
|
||||
pointer-events="none"
|
||||
width="100%"
|
||||
height="100%"
|
||||
requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
|
||||
<xhtml:div
|
||||
style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 40px; margin-left: 269px;">
|
||||
<xhtml:div
|
||||
style="box-sizing: border-box; font-size: 0px; text-align: center;"
|
||||
data-drawio-colors="color: rgb(0, 0, 0); ">
|
||||
<xhtml:div
|
||||
style="display: inline-block; font-size: 24px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: nowrap;">D</xhtml:div>
|
||||
</xhtml:div>
|
||||
</xhtml:div>
|
||||
</foreignObject>
|
||||
<image
|
||||
x="260.5"
|
||||
y="26"
|
||||
width="17"
|
||||
height="47"
|
||||
xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEQAAAC8CAYAAADB0gjGAAAEXklEQVR4Xu2cvY8NURiH7V9AQYlGaIREgQSdwkerQJQKJHQSOh2NFoVIKFDofJcSEhQiaIiKkkT8Bbxv3Nncmdk7c3/nzLV73vvc5M1udu/M3nnu7znvmblndmEVjxqBBXjUCQCkkQiAAKR7kCAhJISESI0UZVAGZVBGIoAyKCMFhi6DMiiDMhIBlEEZKTB0GZRBmUGV+SPtbbgnv7Fd/Rrt7r19/W31weqj1ffh/kx7T31jyHIB6Trmn/bLB1YPrZ4MDadEIOMMHM5Vq+ujFGXzKR1IBaACcyWXSBQgFQcfe05afUoFEw1IxeG4fXMvBUpUIM7C9bmoQokMxFncsDqtQIkOxFlI+swDEIdyeNo5y7wA8ba8aZq5yrwA8ZQ8szrYN57MCkjffpuva739YMPoh6vt6zar7Vb7rdb2HYTw+151+l546rlM336FY1i1x558wuqUstGE536xn2/p2k/fC18JQKrXv9W+uWm1KxOMz00mTvFLAlJx8BO5nLR0pqREIA4mF8rEuUmpQBzKU6sDifpM7DglA/HO9C6jC3lXa119KxmIh+OC1eXElJwZqVfbvHQgPmf5mpiSJbUpHUjuALvGduAXsBcfEYD4xO1lojZ7bbtX0YD48fxI1KY1SYuQkJwWfN82PhYxIandxi9K744IxN/lu4njSM2SKMrkDKy1ThMFiM9avyUmpNZpogBxFqmXKgDSSFLtKhoJ+fdh1uIFI4AApDX0kpAGEoAApHuCQkJICAmRJvEhJ2acyzQykHO2G/Jc5pABeiyJMuG6cpSpe+oVM19Isy7iFTNfgnk0ISGtz2aiJOSzwdicAKS1dDMCkJwO01pRFAGIr0O9lpAO36T1gXcEIK/twFJWFbU+gnBCpQPxZVZ+U1HKY8ml36UDyVlJFG59SM7sdEldSlbG14W8TWy1ftyh1pg5jOeJA6nDaM1OS56puia3MpLhxx5inaqfvJ21Sl11WIXA16jutKqtGlrJCRlf877PXuhGqyNWQ6137713ZlZtN2VeMOttlvVuiFkfnLp/H0h3WPXeFT4vCem9LaQiPA9AOrtKM2rRgXBX5tg7LsOY5dRdHfSGfL4PoOesuLPbIHhrPW819/f++wz0Umoq/sdMdUgFuvblibgzBIiS265fy7ht9WiaiZb6zqzUtjv+P4he2EFV/4OodueCerDTPL8PyDT7CPUcgDTeToAApNtwEkJCSIjUBVEGZVAGZSQCKIMyUmDoMiiDMigjEUAZlJECQ5dBGZRBGYkAyqCMFBi6DMqgDMpIBFAGZaTA0GVQBmVQRiKAMigjBYYugzIogzISAZRBGSkwdBmUQRmUkQigDMpIgaHLoAzKoIxEAGVQRgoMXQZlUAZlJAIogzJSYOgyKIMyKCMRQBmUkQJDl0EZlEEZiQDKoIwUGLoMyqAMykgEUAZlpMDQZVAGZVBGIoAyKCMFhi6DMiiDMhIBlEEZKTB0GZRBGZSRCKBMN66/z87NveQ2FjYAAAAASUVORK5CYII="
|
||||
id="image2" />
|
||||
</switch>
|
||||
</g>
|
||||
<rect
|
||||
x="369"
|
||||
y="120"
|
||||
width="80"
|
||||
height="80"
|
||||
rx="40"
|
||||
ry="40"
|
||||
fill="#dde1cc"
|
||||
stroke="#808080"
|
||||
pointer-events="all"
|
||||
id="rect3"
|
||||
style="background-color:rgb(255, 255, 255)" />
|
||||
<g
|
||||
id="g3"
|
||||
style="background-color:rgb(255, 255, 255)"
|
||||
transform="translate(-0.5,-0.5)">
|
||||
<switch
|
||||
id="switch3">
|
||||
<foreignObject
|
||||
style="overflow: visible; text-align: left;"
|
||||
pointer-events="none"
|
||||
width="100%"
|
||||
height="100%"
|
||||
requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
|
||||
<xhtml:div
|
||||
style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 160px; margin-left: 409px;">
|
||||
<xhtml:div
|
||||
style="box-sizing: border-box; font-size: 0px; text-align: center;"
|
||||
data-drawio-colors="color: rgb(0, 0, 0); ">
|
||||
<xhtml:div
|
||||
style="display: inline-block; font-size: 24px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: nowrap;">b<xhtml:sub>1</xhtml:sub>
|
||||
</xhtml:div>
|
||||
</xhtml:div>
|
||||
</xhtml:div>
|
||||
</foreignObject>
|
||||
<image
|
||||
x="396"
|
||||
y="144.5"
|
||||
width="26"
|
||||
height="38"
|
||||
xlink:href="data:image/png;base64,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"
|
||||
id="image3" />
|
||||
</switch>
|
||||
</g>
|
||||
<rect
|
||||
x="89"
|
||||
y="120"
|
||||
width="80"
|
||||
height="80"
|
||||
rx="40"
|
||||
ry="40"
|
||||
fill="#dde1cc"
|
||||
stroke="#808080"
|
||||
pointer-events="all"
|
||||
id="rect4"
|
||||
style="background-color:rgb(255, 255, 255)" />
|
||||
<g
|
||||
id="g4"
|
||||
style="background-color:rgb(255, 255, 255)"
|
||||
transform="translate(-0.5,-0.5)">
|
||||
<switch
|
||||
id="switch4">
|
||||
<foreignObject
|
||||
style="overflow: visible; text-align: left;"
|
||||
pointer-events="none"
|
||||
width="100%"
|
||||
height="100%"
|
||||
requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
|
||||
<xhtml:div
|
||||
style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 160px; margin-left: 129px;">
|
||||
<xhtml:div
|
||||
style="box-sizing: border-box; font-size: 0px; text-align: center;"
|
||||
data-drawio-colors="color: rgb(0, 0, 0); ">
|
||||
<xhtml:div
|
||||
style="display: inline-block; font-size: 24px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: nowrap;">b<xhtml:sub>0</xhtml:sub>
|
||||
</xhtml:div>
|
||||
</xhtml:div>
|
||||
</xhtml:div>
|
||||
</foreignObject>
|
||||
<image
|
||||
x="116"
|
||||
y="144.5"
|
||||
width="26"
|
||||
height="38"
|
||||
xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGgAAACYCAYAAAAIjqy4AAAH3klEQVR4Xu2dzet1UxTHn+cvQDEkJCZIEspLGXkdGKJQklAYKYwMFBMzYSDFACUzryN5KyYSJiSJIQP+AtY393iO8zvn7v3de637W/fc761dz9PdZ921v5+99ss6+5zfyRP6pFbgZGrv5NwJAUreCQRIgJIrkNw9RZAAJVcguXuKIAFKrkBy9xRBApRcgeTuKYIEKLkCyd1TBAlQcgWSu6cIEqDkCiR3TxEkQMkVSO6eIkiAkiuQ3D1FkAAlVyC5e9si6APz/SbS/w+t/s3kNaq+RQEBSt49BEiAkiuQ3D1FkAAlVyC5e4ogAUquQHL3FEEClFyB5O4pggQouQLJ3VMECVByBZK7pwgSoOQKJHdPESRAyRVI7t5xRNDZpsltVq63cp6VqyYa/Wj//9nKN1Y+s/KFlb+S6xjm3i4B3WmteGwGSE3j3rJKL2xg1dRfTZ1dALrG1HrVyoUOquHMwzOHBCoS0Gkm5HNWHnQAMzUBu08G2E1nMgoQ4HzUOJzVivSVVbxx7fNTBKDHTbR3nIa0EqzVQ/IGBMHO2BGcAd6qIXkDKvX4qO8B6eoo48dpdy2AoCEWDVg8rOqzJkAAc4mV79dEaJeAsId53cp3ExEvtv9fZ+Veh1Xf6s6G7wIQUjf3WUHKpvS5xSq8ZuXMUsUt319b+VsdP7G7S6MBIUWDjSqTS+vdQ+E3kVby/sAvdKBLrVxm5YqZjoQI/sXKt1betfJbrxORgHpWVr2QzvEQZyMukrtPbToaq3d3aioK0B/WkgvIyJk2HnMT5quWz8N20UstF46uQSd5YlM6TZ0AqAdaOk0UoLvMmTd7W7URuSWX1zvMoXO8YmV6K6SnSei0j7K6RADCouCinpaMrsXw8mujrdbnbwHnYys9C5VtLlOdNwKQd6YZkXhHA6SWPRE6xNeBcIZm3Gr/eL+mTRGAWoTZ5utD9uWLNY2Z1KF66ubaL52HtSW3MdxdXjMnRQBqHVqWGtO6WGBTP1gQPNvQEVovqdpUewOq+tGGFv3dcA2zUMCK7acdDG3TZhSj3BvQy+YBhiTvT8vQw3QWLMlbVou97SwuqLwBscNKbQMj39nQEj0Q9mkrn4/mEWQZHrHCvltiaxStGRDg18yHSAu9UdtTrF7pBiE7l20divcFENvoQe8aQMzwici50kopt8huDU5fsnnogNiNcHFS3/QMN7uHDogZ3tj8IjNvLg5zhw6IWb0xy3YEEgv/rLl5cF8AMUIO7axZZjPzD5shZ4e52QyMN6AaUYgF039VmeGCAcRsgFvu1P5uztQmXWfzc4cMCGfGsY+p/SyutLYYYDrW7B7SG1Dt3qNWlKEe09OHa0qbZmaOaG0XMzTPznERgLyz2exYPgAqzRnM3qr19j3zG7PTQwSgkjBs9LA9fbBfmjOY3t06tzKAZqM0AlBrb1sCxwg5tlHKIjDzQysg5OfeI3rkEZ8jAMGfUu+t9RmJzD9rK4/q1XQSBhC7Bxpc6V6IRAFq7XFTFuwQUbtAQD1mD1RacCz1IRbQkY4dBQgO985FPecDahYqzMpwlYB6hrqeg4s1wxt8EyATAQlGHIqvOsGyGSd64MBEbcZZgEYDM45ioZTuo/Q+Ec5knAVoMnNCPJzWxKFyPMMzwMJcg8nxHivs7eLp5FwbPRriGpbGvZfUzj3D7xxcBCFCajO3vTDmrq9ZuY2vOzhAWIrebsXzwHktSGZoO9gIAiCkZXZ9ALD1LPhBRhDEin46YBxVPQclDy6TMN5tY2X2dvBw15upYHJxrR2BTfUcuSnomeqZpkM8n1AbRw0WI5jrah5K3jaHMYBac4vsrZLQbPZSvgq96HmnaKrd8NYsLpjDha2A2GTvsQAaxMK9kbutsA9jDRtcPCPU/dT0iBwj3i4A0XdUa3phS53hcXa8EvNcK9PswfBKzE/su+GVmC2/U7qGfTCsdANw7veYm430mYRSA/f9e3YCbwHEzHP0qZ59B1Dynz2M0nKXOPRcXKmBa/ieEZBd1rMdYPblGy1huwYwQxuYIYg9l8AssbEQos9mrwnEUluYlRxsMKdLmWV809MNhwCIfYK89mwCO7wtJnsPfYhDJ/zBSu07vWvv2DLRAx8WX/4kQP++GwFDXe2n9Iwqu79qfka11uF9r8cOR2hviqe89114xn9mNcfYLdXtek9CyfiavmcXC15tL94J1hx0Smp2LuqFVJWAFaBTMvcemGSAYTV4g5XiK6QF6P+y9pwHZwAVh7bBmAAdlTX6TEU1HLgmQPP9PmK4c39nKROya63L5uqWdMCCAH+2pzjnTA0ogspdC/MSbjXcb4U9OQsw+HMIzW9AFqAyoHENnKvA35nAm+fPtzLN4QEIHtn81Er4m+c511U7RAFFUIisfkYFyE/LEEsCFCKrn1EB8tMyxJIAhcjqZ1SA/LQMsSRAIbL6GRUgPy1DLAlQiKx+RgXIT8sQSwIUIqufUQHy0zLEkgCFyOpnVID8tAyxJEAhsvoZFSA/LUMsCVCIrH5GBchPyxBLAhQiq59RAfLTMsSSAIXI6mdUgPy0DLEkQCGy+hkVID8tQywJUIisfkYFyE/LEEsCFCKrn1EB8tMyxJIAhcjqZ1SA/LQMsSRAIbL6GRUgPy1DLAlQiKx+RgXIT8sQSwIUIqufUQHy0zLEkgCFyOpnVID8tAyxJEAhsvoZ/QdAKs2ofcCTeAAAAABJRU5ErkJggg=="
|
||||
id="image4" />
|
||||
</switch>
|
||||
</g>
|
||||
<rect
|
||||
x="929"
|
||||
y="120"
|
||||
width="80"
|
||||
height="80"
|
||||
rx="40"
|
||||
ry="40"
|
||||
fill="#dde1cc"
|
||||
stroke="#808080"
|
||||
pointer-events="all"
|
||||
id="rect5"
|
||||
style="background-color:rgb(255, 255, 255)" />
|
||||
<g
|
||||
id="g5"
|
||||
style="background-color:rgb(255, 255, 255)"
|
||||
transform="translate(-0.5,-0.5)">
|
||||
<switch
|
||||
id="switch5">
|
||||
<foreignObject
|
||||
style="overflow: visible; text-align: left;"
|
||||
pointer-events="none"
|
||||
width="100%"
|
||||
height="100%"
|
||||
requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
|
||||
<xhtml:div
|
||||
style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 160px; margin-left: 969px;">
|
||||
<xhtml:div
|
||||
style="box-sizing: border-box; font-size: 0px; text-align: center;"
|
||||
data-drawio-colors="color: rgb(0, 0, 0); ">
|
||||
<xhtml:div
|
||||
style="display: inline-block; font-size: 24px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: nowrap;">b<xhtml:sub>n</xhtml:sub>
|
||||
</xhtml:div>
|
||||
</xhtml:div>
|
||||
</xhtml:div>
|
||||
</foreignObject>
|
||||
<image
|
||||
x="955.5"
|
||||
y="144.5"
|
||||
width="27"
|
||||
height="38"
|
||||
xlink:href="data:image/png;base64,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"
|
||||
id="image5" />
|
||||
</switch>
|
||||
</g>
|
||||
<rect
|
||||
x="489"
|
||||
y="0"
|
||||
width="120"
|
||||
height="80"
|
||||
fill="#dde1cc"
|
||||
stroke="#808080"
|
||||
pointer-events="all"
|
||||
id="rect6"
|
||||
style="background-color:rgb(255, 255, 255)" />
|
||||
<g
|
||||
id="g6"
|
||||
style="background-color:rgb(255, 255, 255)"
|
||||
transform="translate(-0.5,-0.5)">
|
||||
<switch
|
||||
id="switch6">
|
||||
<foreignObject
|
||||
style="overflow: visible; text-align: left;"
|
||||
pointer-events="none"
|
||||
width="100%"
|
||||
height="100%"
|
||||
requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
|
||||
<xhtml:div
|
||||
style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 40px; margin-left: 549px;">
|
||||
<xhtml:div
|
||||
style="box-sizing: border-box; font-size: 0px; text-align: center;"
|
||||
data-drawio-colors="color: rgb(0, 0, 0); ">
|
||||
<xhtml:div
|
||||
style="display: inline-block; font-size: 24px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: nowrap;">D</xhtml:div>
|
||||
</xhtml:div>
|
||||
</xhtml:div>
|
||||
</foreignObject>
|
||||
<image
|
||||
x="540.5"
|
||||
y="23"
|
||||
width="17"
|
||||
height="47"
|
||||
xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEQAAAC8CAYAAADB0gjGAAAEXklEQVR4Xu2cvY8NURiH7V9AQYlGaIREgQSdwkerQJQKJHQSOh2NFoVIKFDofJcSEhQiaIiKkkT8Bbxv3Nncmdk7c3/nzLV73vvc5M1udu/M3nnu7znvmblndmEVjxqBBXjUCQCkkQiAAKR7kCAhJISESI0UZVAGZVBGIoAyKCMFhi6DMiiDMhIBlEEZKTB0GZRBmUGV+SPtbbgnv7Fd/Rrt7r19/W31weqj1ffh/kx7T31jyHIB6Trmn/bLB1YPrZ4MDadEIOMMHM5Vq+ujFGXzKR1IBaACcyWXSBQgFQcfe05afUoFEw1IxeG4fXMvBUpUIM7C9bmoQokMxFncsDqtQIkOxFlI+swDEIdyeNo5y7wA8ba8aZq5yrwA8ZQ8szrYN57MCkjffpuva739YMPoh6vt6zar7Vb7rdb2HYTw+151+l546rlM336FY1i1x558wuqUstGE536xn2/p2k/fC18JQKrXv9W+uWm1KxOMz00mTvFLAlJx8BO5nLR0pqREIA4mF8rEuUmpQBzKU6sDifpM7DglA/HO9C6jC3lXa119KxmIh+OC1eXElJwZqVfbvHQgPmf5mpiSJbUpHUjuALvGduAXsBcfEYD4xO1lojZ7bbtX0YD48fxI1KY1SYuQkJwWfN82PhYxIandxi9K744IxN/lu4njSM2SKMrkDKy1ThMFiM9avyUmpNZpogBxFqmXKgDSSFLtKhoJ+fdh1uIFI4AApDX0kpAGEoAApHuCQkJICAmRJvEhJ2acyzQykHO2G/Jc5pABeiyJMuG6cpSpe+oVM19Isy7iFTNfgnk0ISGtz2aiJOSzwdicAKS1dDMCkJwO01pRFAGIr0O9lpAO36T1gXcEIK/twFJWFbU+gnBCpQPxZVZ+U1HKY8ml36UDyVlJFG59SM7sdEldSlbG14W8TWy1ftyh1pg5jOeJA6nDaM1OS56puia3MpLhxx5inaqfvJ21Sl11WIXA16jutKqtGlrJCRlf877PXuhGqyNWQ6137713ZlZtN2VeMOttlvVuiFkfnLp/H0h3WPXeFT4vCem9LaQiPA9AOrtKM2rRgXBX5tg7LsOY5dRdHfSGfL4PoOesuLPbIHhrPW819/f++wz0Umoq/sdMdUgFuvblibgzBIiS265fy7ht9WiaiZb6zqzUtjv+P4he2EFV/4OodueCerDTPL8PyDT7CPUcgDTeToAApNtwEkJCSIjUBVEGZVAGZSQCKIMyUmDoMiiDMigjEUAZlJECQ5dBGZRBGYkAyqCMFBi6DMqgDMpIBFAGZaTA0GVQBmVQRiKAMigjBYYugzIogzISAZRBGSkwdBmUQRmUkQigDMpIgaHLoAzKoIxEAGVQRgoMXQZlUAZlJAIogzJSYOgyKIMyKCMRQBmUkQJDl0EZlEEZiQDKoIwUGLoMyqAMykgEUAZlpMDQZVAGZVBGIoAyKCMFhi6DMiiDMhIBlEEZKTB0GZRBGZSRCKBMN66/z87NveQ2FjYAAAAASUVORK5CYII="
|
||||
id="image6" />
|
||||
</switch>
|
||||
</g>
|
||||
<rect
|
||||
x="769"
|
||||
y="0"
|
||||
width="120"
|
||||
height="80"
|
||||
fill="#dde1cc"
|
||||
stroke="#808080"
|
||||
pointer-events="all"
|
||||
id="rect7"
|
||||
style="background-color:rgb(255, 255, 255)" />
|
||||
<g
|
||||
id="g7"
|
||||
style="background-color:rgb(255, 255, 255)"
|
||||
transform="translate(-0.5,-0.5)">
|
||||
<switch
|
||||
id="switch7">
|
||||
<foreignObject
|
||||
style="overflow: visible; text-align: left;"
|
||||
pointer-events="none"
|
||||
width="100%"
|
||||
height="100%"
|
||||
requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
|
||||
<xhtml:div
|
||||
style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 40px; margin-left: 829px;">
|
||||
<xhtml:div
|
||||
style="box-sizing: border-box; font-size: 0px; text-align: center;"
|
||||
data-drawio-colors="color: rgb(0, 0, 0); ">
|
||||
<xhtml:div
|
||||
style="display: inline-block; font-size: 24px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: nowrap;">D</xhtml:div>
|
||||
</xhtml:div>
|
||||
</xhtml:div>
|
||||
</foreignObject>
|
||||
<image
|
||||
x="820.5"
|
||||
y="20"
|
||||
width="17"
|
||||
height="47"
|
||||
xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEQAAAC8CAYAAADB0gjGAAAEXklEQVR4Xu2cvY8NURiH7V9AQYlGaIREgQSdwkerQJQKJHQSOh2NFoVIKFDofJcSEhQiaIiKkkT8Bbxv3Nncmdk7c3/nzLV73vvc5M1udu/M3nnu7znvmblndmEVjxqBBXjUCQCkkQiAAKR7kCAhJISESI0UZVAGZVBGIoAyKCMFhi6DMiiDMhIBlEEZKTB0GZRBmUGV+SPtbbgnv7Fd/Rrt7r19/W31weqj1ffh/kx7T31jyHIB6Trmn/bLB1YPrZ4MDadEIOMMHM5Vq+ujFGXzKR1IBaACcyWXSBQgFQcfe05afUoFEw1IxeG4fXMvBUpUIM7C9bmoQokMxFncsDqtQIkOxFlI+swDEIdyeNo5y7wA8ba8aZq5yrwA8ZQ8szrYN57MCkjffpuva739YMPoh6vt6zar7Vb7rdb2HYTw+151+l546rlM336FY1i1x558wuqUstGE536xn2/p2k/fC18JQKrXv9W+uWm1KxOMz00mTvFLAlJx8BO5nLR0pqREIA4mF8rEuUmpQBzKU6sDifpM7DglA/HO9C6jC3lXa119KxmIh+OC1eXElJwZqVfbvHQgPmf5mpiSJbUpHUjuALvGduAXsBcfEYD4xO1lojZ7bbtX0YD48fxI1KY1SYuQkJwWfN82PhYxIandxi9K744IxN/lu4njSM2SKMrkDKy1ThMFiM9avyUmpNZpogBxFqmXKgDSSFLtKhoJ+fdh1uIFI4AApDX0kpAGEoAApHuCQkJICAmRJvEhJ2acyzQykHO2G/Jc5pABeiyJMuG6cpSpe+oVM19Isy7iFTNfgnk0ISGtz2aiJOSzwdicAKS1dDMCkJwO01pRFAGIr0O9lpAO36T1gXcEIK/twFJWFbU+gnBCpQPxZVZ+U1HKY8ml36UDyVlJFG59SM7sdEldSlbG14W8TWy1ftyh1pg5jOeJA6nDaM1OS56puia3MpLhxx5inaqfvJ21Sl11WIXA16jutKqtGlrJCRlf877PXuhGqyNWQ6137713ZlZtN2VeMOttlvVuiFkfnLp/H0h3WPXeFT4vCem9LaQiPA9AOrtKM2rRgXBX5tg7LsOY5dRdHfSGfL4PoOesuLPbIHhrPW819/f++wz0Umoq/sdMdUgFuvblibgzBIiS265fy7ht9WiaiZb6zqzUtjv+P4he2EFV/4OodueCerDTPL8PyDT7CPUcgDTeToAApNtwEkJCSIjUBVEGZVAGZSQCKIMyUmDoMiiDMigjEUAZlJECQ5dBGZRBGYkAyqCMFBi6DMqgDMpIBFAGZaTA0GVQBmVQRiKAMigjBYYugzIogzISAZRBGSkwdBmUQRmUkQigDMpIgaHLoAzKoIxEAGVQRgoMXQZlUAZlJAIogzJSYOgyKIMyKCMRQBmUkQJDl0EZlEEZiQDKoIwUGLoMyqAMykgEUAZlpMDQZVAGZVBGIoAyKCMFhi6DMiiDMhIBlEEZKTB0GZRBGZSRCKBMN66/z87NveQ2FjYAAAAASUVORK5CYII="
|
||||
id="image7" />
|
||||
</switch>
|
||||
</g>
|
||||
<rect
|
||||
x="9"
|
||||
y="240"
|
||||
width="1080"
|
||||
height="120"
|
||||
rx="60"
|
||||
ry="60"
|
||||
fill="#dde1cc"
|
||||
stroke="#808080"
|
||||
pointer-events="all"
|
||||
id="rect8"
|
||||
style="background-color:rgb(255, 255, 255)" />
|
||||
<g
|
||||
id="g8"
|
||||
style="background-color:rgb(255, 255, 255)"
|
||||
transform="translate(-0.5,-0.5)">
|
||||
<switch
|
||||
id="switch8">
|
||||
<foreignObject
|
||||
style="overflow: visible; text-align: left;"
|
||||
pointer-events="none"
|
||||
width="100%"
|
||||
height="100%"
|
||||
requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
|
||||
<xhtml:div
|
||||
style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 300px; margin-left: 549px;">
|
||||
<xhtml:div
|
||||
style="box-sizing: border-box; font-size: 0px; text-align: center;"
|
||||
data-drawio-colors="color: rgb(0, 0, 0); ">
|
||||
<xhtml:div
|
||||
style="display: inline-block; font-size: 24px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: nowrap;">
|
||||
<xhtml:font
|
||||
style="font-size: 80px;">∑</xhtml:font>
|
||||
</xhtml:div>
|
||||
</xhtml:div>
|
||||
</xhtml:div>
|
||||
</foreignObject>
|
||||
<image
|
||||
x="520.5"
|
||||
y="252.5"
|
||||
width="57"
|
||||
height="102"
|
||||
xlink:href="data:image/png;base64,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 truncated
|
||||
id="image8" />
|
||||
</switch>
|
||||
</g>
|
||||
<path
|
||||
d="M 329,40 H 477.03"
|
||||
fill="none"
|
||||
stroke="#808080"
|
||||
stroke-width="4"
|
||||
stroke-miterlimit="10"
|
||||
pointer-events="stroke"
|
||||
id="path8"
|
||||
style="background-color:rgb(255, 255, 255)" />
|
||||
<path
|
||||
d="m 484.53,40 -10,5 2.5,-5 -2.5,-5 z"
|
||||
fill="#808080"
|
||||
stroke="#808080"
|
||||
stroke-width="4"
|
||||
stroke-miterlimit="10"
|
||||
pointer-events="all"
|
||||
id="path9"
|
||||
style="background-color:rgb(255, 255, 255)" />
|
||||
<path
|
||||
d="m 609,40 h 20"
|
||||
fill="none"
|
||||
stroke="#808080"
|
||||
stroke-width="4"
|
||||
stroke-miterlimit="10"
|
||||
pointer-events="stroke"
|
||||
id="path10"
|
||||
style="background-color:rgb(255, 255, 255)" />
|
||||
<path
|
||||
d="M 889,39.29 969,39 v 69.03"
|
||||
fill="none"
|
||||
stroke="#808080"
|
||||
stroke-width="4"
|
||||
stroke-miterlimit="10"
|
||||
pointer-events="stroke"
|
||||
id="path11"
|
||||
style="background-color:rgb(255, 255, 255)" />
|
||||
<path
|
||||
d="m 969,115.53 -5,-10 5,2.5 5,-2.5 z"
|
||||
fill="#808080"
|
||||
stroke="#808080"
|
||||
stroke-width="4"
|
||||
stroke-miterlimit="10"
|
||||
pointer-events="all"
|
||||
id="path12"
|
||||
style="background-color:rgb(255, 255, 255)" />
|
||||
<path
|
||||
d="m 729,39.95 28.03,-0.2"
|
||||
fill="none"
|
||||
stroke="#808080"
|
||||
stroke-width="4"
|
||||
stroke-miterlimit="10"
|
||||
pointer-events="stroke"
|
||||
id="path13"
|
||||
style="background-color:rgb(255, 255, 255)" />
|
||||
<path
|
||||
d="m 764.53,39.69 -9.97,5.07 2.47,-5.01 -2.54,-4.98 z"
|
||||
fill="#808080"
|
||||
stroke="#808080"
|
||||
stroke-width="4"
|
||||
stroke-miterlimit="10"
|
||||
pointer-events="all"
|
||||
id="path14"
|
||||
style="background-color:rgb(255, 255, 255)" />
|
||||
<path
|
||||
d="M 629,40 H 729"
|
||||
fill="none"
|
||||
stroke="#808080"
|
||||
stroke-width="4"
|
||||
stroke-miterlimit="10"
|
||||
stroke-dasharray="12, 12"
|
||||
pointer-events="stroke"
|
||||
id="path15"
|
||||
style="background-color:rgb(255, 255, 255)" />
|
||||
<path
|
||||
d="m 329,40 h 80 v 68.03"
|
||||
fill="none"
|
||||
stroke="#808080"
|
||||
stroke-width="4"
|
||||
stroke-miterlimit="10"
|
||||
pointer-events="stroke"
|
||||
id="path16"
|
||||
style="background-color:rgb(255, 255, 255)" />
|
||||
<path
|
||||
d="m 409,115.53 -5,-10 5,2.5 5,-2.5 z"
|
||||
fill="#808080"
|
||||
stroke="#808080"
|
||||
stroke-width="4"
|
||||
stroke-miterlimit="10"
|
||||
pointer-events="all"
|
||||
id="path17"
|
||||
style="background-color:rgb(255, 255, 255)" />
|
||||
<path
|
||||
d="m 129,40 v 68.03"
|
||||
fill="none"
|
||||
stroke="#808080"
|
||||
stroke-width="4"
|
||||
stroke-miterlimit="10"
|
||||
pointer-events="stroke"
|
||||
id="path18"
|
||||
style="background-color:rgb(255, 255, 255)" />
|
||||
<path
|
||||
d="m 129,115.53 -5,-10 5,2.5 5,-2.5 z"
|
||||
fill="#808080"
|
||||
stroke="#808080"
|
||||
stroke-width="4"
|
||||
stroke-miterlimit="10"
|
||||
pointer-events="all"
|
||||
id="path19"
|
||||
style="background-color:rgb(255, 255, 255)" />
|
||||
<path
|
||||
d="m 129,200 -0.08,27.79"
|
||||
fill="none"
|
||||
stroke="#808080"
|
||||
stroke-width="4"
|
||||
stroke-miterlimit="10"
|
||||
pointer-events="stroke"
|
||||
id="path20"
|
||||
style="background-color:rgb(255, 255, 255)" />
|
||||
<path
|
||||
d="m 128.89,235.29 -4.97,-10.02 5,2.52 5,-2.49 z"
|
||||
fill="#808080"
|
||||
stroke="#808080"
|
||||
stroke-width="4"
|
||||
stroke-miterlimit="10"
|
||||
pointer-events="all"
|
||||
id="path21"
|
||||
style="background-color:rgb(255, 255, 255)" />
|
||||
<path
|
||||
d="m 408.66,200 v 28.03"
|
||||
fill="none"
|
||||
stroke="#808080"
|
||||
stroke-width="4"
|
||||
stroke-miterlimit="10"
|
||||
pointer-events="stroke"
|
||||
id="path22"
|
||||
style="background-color:rgb(255, 255, 255)" />
|
||||
<path
|
||||
d="m 408.66,235.53 -5,-10 5,2.5 5,-2.5 z"
|
||||
fill="#808080"
|
||||
stroke="#808080"
|
||||
stroke-width="4"
|
||||
stroke-miterlimit="10"
|
||||
pointer-events="all"
|
||||
id="path23"
|
||||
style="background-color:rgb(255, 255, 255)" />
|
||||
<path
|
||||
d="m 968.66,200 v 28.03"
|
||||
fill="none"
|
||||
stroke="#808080"
|
||||
stroke-width="4"
|
||||
stroke-miterlimit="10"
|
||||
pointer-events="stroke"
|
||||
id="path24"
|
||||
style="background-color:rgb(255, 255, 255)" />
|
||||
<path
|
||||
d="m 968.66,235.53 -5,-10 5,2.5 5,-2.5 z"
|
||||
fill="#808080"
|
||||
stroke="#808080"
|
||||
stroke-width="4"
|
||||
stroke-miterlimit="10"
|
||||
pointer-events="all"
|
||||
id="path25"
|
||||
style="background-color:rgb(255, 255, 255)" />
|
||||
<path
|
||||
d="m 1089,300 h 108.58"
|
||||
fill="none"
|
||||
stroke="#808080"
|
||||
stroke-width="4"
|
||||
stroke-miterlimit="10"
|
||||
pointer-events="stroke"
|
||||
id="path26"
|
||||
style="background-color:rgb(255, 255, 255)" />
|
||||
<path
|
||||
d="m 1205.08,300 -10,5 2.5,-5 -2.5,-5 z"
|
||||
fill="#808080"
|
||||
stroke="#808080"
|
||||
stroke-width="4"
|
||||
stroke-miterlimit="10"
|
||||
pointer-events="all"
|
||||
id="path27"
|
||||
style="background-color:rgb(255, 255, 255)" />
|
||||
<rect
|
||||
x="1109"
|
||||
y="261"
|
||||
width="80"
|
||||
height="44"
|
||||
rx="22"
|
||||
ry="22"
|
||||
fill="none"
|
||||
stroke="none"
|
||||
pointer-events="all"
|
||||
id="rect27"
|
||||
style="background-color:rgb(255, 255, 255)" />
|
||||
<g
|
||||
id="g27"
|
||||
style="background-color:rgb(255, 255, 255)"
|
||||
transform="translate(-0.5,-0.5)">
|
||||
<switch
|
||||
id="switch27">
|
||||
<foreignObject
|
||||
style="overflow: visible; text-align: left;"
|
||||
pointer-events="none"
|
||||
width="100%"
|
||||
height="100%"
|
||||
requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
|
||||
<xhtml:div
|
||||
style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 283px; margin-left: 1149px;">
|
||||
<xhtml:div
|
||||
style="box-sizing: border-box; font-size: 0px; text-align: center;"
|
||||
data-drawio-colors="color: #808080; ">
|
||||
<xhtml:div
|
||||
style="display: inline-block; font-size: 24px; font-family: Helvetica; color: rgb(128, 128, 128); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: nowrap;">y[k]</xhtml:div>
|
||||
</xhtml:div>
|
||||
</xhtml:div>
|
||||
</foreignObject>
|
||||
<image
|
||||
x="1127.5"
|
||||
y="269"
|
||||
width="43"
|
||||
height="35"
|
||||
xlink:href="data:image/png;base64,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"
|
||||
id="image27" />
|
||||
</switch>
|
||||
</g>
|
||||
<rect
|
||||
x="9"
|
||||
y="0"
|
||||
width="80"
|
||||
height="40"
|
||||
rx="20"
|
||||
ry="20"
|
||||
fill="none"
|
||||
stroke="none"
|
||||
pointer-events="all"
|
||||
id="rect28"
|
||||
style="background-color:rgb(255, 255, 255)" />
|
||||
<g
|
||||
id="g28"
|
||||
style="background-color:rgb(255, 255, 255)"
|
||||
transform="translate(-0.5,-0.5)">
|
||||
<switch
|
||||
id="switch28">
|
||||
<foreignObject
|
||||
style="overflow: visible; text-align: left;"
|
||||
pointer-events="none"
|
||||
width="100%"
|
||||
height="100%"
|
||||
requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
|
||||
<xhtml:div
|
||||
style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 20px; margin-left: 49px;">
|
||||
<xhtml:div
|
||||
style="box-sizing: border-box; font-size: 0px; text-align: center;"
|
||||
data-drawio-colors="color: #808080; ">
|
||||
<xhtml:div
|
||||
style="display: inline-block; font-size: 24px; font-family: Helvetica; color: rgb(128, 128, 128); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: nowrap;">x[k]</xhtml:div>
|
||||
</xhtml:div>
|
||||
</xhtml:div>
|
||||
</foreignObject>
|
||||
<image
|
||||
x="27.5"
|
||||
y="6"
|
||||
width="43"
|
||||
height="35"
|
||||
xlink:href="data:image/png;base64,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"
|
||||
id="image28" />
|
||||
</switch>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 59 KiB |
@@ -0,0 +1,151 @@
|
||||
<mxfile host="Electron" modified="2024-01-27T19:14:32.318Z" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/22.1.21 Chrome/120.0.6099.109 Electron/28.1.0 Safari/537.36" etag="_JxO4yykJQa9LzsHiU7_" version="22.1.21" type="device">
|
||||
<diagram name="Page-1" id="ScnFzgmv18Yx9ZM-e3TZ">
|
||||
<mxGraphModel dx="450" dy="363" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0" />
|
||||
<mxCell id="1" parent="0" />
|
||||
<mxCell id="z0oqqv8cwlupup-oO1Ry-11" value="" style="group;strokeColor=none;" parent="1" vertex="1" connectable="0">
|
||||
<mxGeometry x="160" y="380" width="40" height="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="z0oqqv8cwlupup-oO1Ry-7" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=none;strokeColor=#808080;" parent="z0oqqv8cwlupup-oO1Ry-11" vertex="1">
|
||||
<mxGeometry width="40" height="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="z0oqqv8cwlupup-oO1Ry-8" value="" style="endArrow=none;html=1;rounded=0;strokeColor=#808080;" parent="z0oqqv8cwlupup-oO1Ry-11" edge="1">
|
||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="15" y="30" as="sourcePoint" />
|
||||
<mxPoint x="25" y="30" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="z0oqqv8cwlupup-oO1Ry-9" value="" style="endArrow=none;html=1;rounded=0;strokeColor=#808080;" parent="z0oqqv8cwlupup-oO1Ry-11" edge="1">
|
||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="3" y="19.899999999999977" as="sourcePoint" />
|
||||
<mxPoint x="13" y="19.899999999999977" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="z0oqqv8cwlupup-oO1Ry-10" value="" style="endArrow=none;html=1;rounded=0;strokeColor=#808080;" parent="z0oqqv8cwlupup-oO1Ry-11" edge="1">
|
||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="8" y="25" as="sourcePoint" />
|
||||
<mxPoint x="8" y="15" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="z0oqqv8cwlupup-oO1Ry-12" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#808080;" parent="1" source="z0oqqv8cwlupup-oO1Ry-7" target="z0oqqv8cwlupup-oO1Ry-3" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="z0oqqv8cwlupup-oO1Ry-13" value="" style="group;strokeColor=none;" parent="1" vertex="1" connectable="0">
|
||||
<mxGeometry x="280" y="360" width="80" height="80" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="z0oqqv8cwlupup-oO1Ry-3" value="" style="triangle;whiteSpace=wrap;html=1;fillColor=none;strokeColor=#808080;" parent="z0oqqv8cwlupup-oO1Ry-13" vertex="1">
|
||||
<mxGeometry width="80" height="80" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="z0oqqv8cwlupup-oO1Ry-4" value="" style="curved=1;endArrow=none;html=1;rounded=0;endFill=0;strokeColor=#808080;" parent="z0oqqv8cwlupup-oO1Ry-13" edge="1">
|
||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="7" y="53" as="sourcePoint" />
|
||||
<mxPoint x="37" y="33" as="targetPoint" />
|
||||
<Array as="points">
|
||||
<mxPoint x="7" y="63" />
|
||||
<mxPoint x="17" y="63" />
|
||||
<mxPoint x="27" y="23" />
|
||||
<mxPoint x="37" y="23" />
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="z0oqqv8cwlupup-oO1Ry-14" value="" style="group;strokeColor=none;" parent="1" vertex="1" connectable="0">
|
||||
<mxGeometry x="440" y="360" width="80" height="80" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="z0oqqv8cwlupup-oO1Ry-5" value="" style="triangle;whiteSpace=wrap;html=1;fillColor=none;strokeColor=#808080;" parent="z0oqqv8cwlupup-oO1Ry-14" vertex="1">
|
||||
<mxGeometry width="80" height="80" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="z0oqqv8cwlupup-oO1Ry-6" value="" style="endArrow=none;html=1;rounded=0;strokeColor=#808080;" parent="z0oqqv8cwlupup-oO1Ry-14" edge="1">
|
||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="8" y="61" as="sourcePoint" />
|
||||
<mxPoint x="28" y="21" as="targetPoint" />
|
||||
<Array as="points">
|
||||
<mxPoint x="18" y="61" />
|
||||
<mxPoint x="18" y="41" />
|
||||
<mxPoint x="18" y="21" />
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="z0oqqv8cwlupup-oO1Ry-15" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=#808080;" parent="1" source="z0oqqv8cwlupup-oO1Ry-3" target="z0oqqv8cwlupup-oO1Ry-5" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="z0oqqv8cwlupup-oO1Ry-16" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;strokeColor=#808080;" parent="1" target="z0oqqv8cwlupup-oO1Ry-7" edge="1">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="120" y="400" as="sourcePoint" />
|
||||
<mxPoint x="150.00000000000003" y="395" as="targetPoint" />
|
||||
<Array as="points">
|
||||
<mxPoint x="130" y="400" />
|
||||
<mxPoint x="130" y="400" />
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="z0oqqv8cwlupup-oO1Ry-18" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.25;entryDx=0;entryDy=0;endArrow=block;endFill=1;strokeColor=#808080;" parent="1" source="z0oqqv8cwlupup-oO1Ry-5" target="z0oqqv8cwlupup-oO1Ry-17" edge="1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="z0oqqv8cwlupup-oO1Ry-27" value="" style="group;strokeColor=none;fontColor=#808080;" parent="1" vertex="1" connectable="0">
|
||||
<mxGeometry x="600" y="480" width="80" height="80" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="z0oqqv8cwlupup-oO1Ry-23" value="" style="whiteSpace=wrap;html=1;aspect=fixed;fillColor=none;strokeColor=#808080;" parent="z0oqqv8cwlupup-oO1Ry-27" vertex="1">
|
||||
<mxGeometry width="80" height="80" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="z0oqqv8cwlupup-oO1Ry-24" value="" style="endArrow=none;html=1;rounded=0;exitX=0;exitY=1;exitDx=0;exitDy=0;entryX=1;entryY=0;entryDx=0;entryDy=0;strokeColor=#808080;" parent="z0oqqv8cwlupup-oO1Ry-27" source="z0oqqv8cwlupup-oO1Ry-23" target="z0oqqv8cwlupup-oO1Ry-23" edge="1">
|
||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="60" y="-120" as="sourcePoint" />
|
||||
<mxPoint x="110" y="-170" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="z0oqqv8cwlupup-oO1Ry-25" value="<font style="font-size: 20px;"><b>A</b></font>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontColor=#808080;" parent="z0oqqv8cwlupup-oO1Ry-27" vertex="1">
|
||||
<mxGeometry width="40" height="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="z0oqqv8cwlupup-oO1Ry-26" value="<font style="font-size: 20px;"><b>D</b></font>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontColor=#808080;" parent="z0oqqv8cwlupup-oO1Ry-27" vertex="1">
|
||||
<mxGeometry x="40" y="40" width="40" height="40" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="z0oqqv8cwlupup-oO1Ry-28" value="" style="group;strokeColor=#808080;fontColor=#808080;" parent="1" vertex="1" connectable="0">
|
||||
<mxGeometry x="600" y="380" width="80" height="80" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="z0oqqv8cwlupup-oO1Ry-17" value="" style="whiteSpace=wrap;html=1;aspect=fixed;fillColor=none;strokeColor=#808080;" parent="z0oqqv8cwlupup-oO1Ry-28" vertex="1">
|
||||
<mxGeometry width="80" height="80" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="z0oqqv8cwlupup-oO1Ry-19" value="" style="endArrow=none;html=1;rounded=0;exitX=0;exitY=0.804;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0.004;entryY=0.68;entryDx=0;entryDy=0;entryPerimeter=0;strokeColor=#808080;" parent="z0oqqv8cwlupup-oO1Ry-28" source="z0oqqv8cwlupup-oO1Ry-17" target="z0oqqv8cwlupup-oO1Ry-17" edge="1">
|
||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="-10" y="70" as="sourcePoint" />
|
||||
<mxPoint x="40" y="20" as="targetPoint" />
|
||||
<Array as="points">
|
||||
<mxPoint x="10" y="60" />
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="z0oqqv8cwlupup-oO1Ry-20" value="D" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontColor=#808080;" parent="z0oqqv8cwlupup-oO1Ry-28" vertex="1">
|
||||
<mxGeometry y="10" width="20" height="20" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="z0oqqv8cwlupup-oO1Ry-22" value="Q" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontColor=#808080;" parent="z0oqqv8cwlupup-oO1Ry-28" vertex="1">
|
||||
<mxGeometry x="60" y="10" width="20" height="20" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="z0oqqv8cwlupup-oO1Ry-29" value="" style="endArrow=classic;html=1;rounded=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;strokeColor=#808080;" parent="1" source="z0oqqv8cwlupup-oO1Ry-22" edge="1">
|
||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="660" y="370" as="sourcePoint" />
|
||||
<mxPoint x="760" y="400" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="z0oqqv8cwlupup-oO1Ry-30" value="" style="endArrow=classic;html=1;rounded=0;entryX=1;entryY=0.25;entryDx=0;entryDy=0;startArrow=oval;startFill=1;strokeColor=#808080;" parent="1" target="z0oqqv8cwlupup-oO1Ry-23" edge="1">
|
||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="720" y="400" as="sourcePoint" />
|
||||
<mxPoint x="710" y="320" as="targetPoint" />
|
||||
<Array as="points">
|
||||
<mxPoint x="720" y="500" />
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="z0oqqv8cwlupup-oO1Ry-31" value="" style="endArrow=classic;html=1;rounded=0;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;strokeColor=#808080;" parent="1" source="z0oqqv8cwlupup-oO1Ry-25" target="z0oqqv8cwlupup-oO1Ry-7" edge="1">
|
||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="330" y="450" as="sourcePoint" />
|
||||
<mxPoint x="380" y="400" as="targetPoint" />
|
||||
<Array as="points">
|
||||
<mxPoint x="180" y="500" />
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
|
After Width: | Height: | Size: 11 KiB |
@@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="162.60143mm"
|
||||
height="145.49326mm"
|
||||
viewBox="0 0 162.60143 145.49326"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
xml:space="preserve"
|
||||
inkscape:version="1.3.2 (091e20e, 2023-11-25, custom)"
|
||||
sodipodi:docname="specification_de_filtre.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:zoom="0.76167929"
|
||||
inkscape:cx="454.25943"
|
||||
inkscape:cy="204.15416"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1111"
|
||||
inkscape:window-x="-9"
|
||||
inkscape:window-y="-9"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1" /><defs
|
||||
id="defs1" /><g
|
||||
inkscape:label="Calque 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-24.16979,-27.981753)"><path
|
||||
style="fill:#b3b3b3"
|
||||
d="m 43.550519,173.33743 c -0.254574,-0.10272 -0.330729,-2.01702 -0.330729,-8.31348 v -8.18004 h -6.331101 c -5.402769,0 -6.392067,-0.061 -6.746875,-0.41577 -0.510872,-0.51087 -0.510872,-0.50966 0,-1.02054 0.354801,-0.3548 1.343921,-0.41577 6.744842,-0.41577 h 6.329068 l 0.0029,-60.35319 0.0029,-60.353191 -0.952606,0.8364 c -1.049253,0.921256 -1.784615,0.967164 -1.90637,0.119015 -0.09659,-0.672828 2.715832,-6.487427 3.294484,-6.811257 0.285899,-0.159997 0.561696,-0.07902 0.886584,0.260305 0.805275,0.841062 3.459054,6.349148 3.28169,6.811353 -0.253641,0.660977 -1.054436,0.509844 -2.08282,-0.393089 l -0.935248,-0.821159 v 7.963517 7.963516 l 24.14323,0.06754 24.14323,0.06754 -0.104749,47.781485 -0.104749,47.781485 30.46569,-0.12725 30.46569,-0.12725 v 4.66711 4.66712 h 7.70032 7.70033 l -0.8384,-0.95488 c -0.47736,-0.54368 -0.77401,-1.12269 -0.68887,-1.34456 0.0822,-0.21432 0.14953,-0.43947 0.14953,-0.50032 0,-0.7008 6.66518,2.43463 7.25239,3.41165 0.23452,0.39022 0.16497,0.58078 -0.37322,1.02256 -1.0994,0.90245 -5.83161,3.16392 -6.43871,3.077 -0.86373,-0.12368 -0.82489,-0.85236 0.10166,-1.90765 l 0.83562,-0.95172 h -62.2045 -62.204492 v 8.01688 c 0,8.59642 -0.05419,8.9619 -1.256771,8.47664 z m 49.146356,-22.71496 v -4.36936 l -17.462539,0.0699 c -9.604396,0.0384 -17.462521,-0.0194 -17.4625,-0.12856 2.1e-5,-0.10914 7.858163,-0.19843 17.462538,-0.19843 H 92.696875 V 109.08662 72.17725 H 68.752082 44.80729 v 37.05526 37.05526 l 3.83687,-0.0797 c 2.110279,-0.0439 3.836685,0.01 3.836459,0.1187 -2.27e-4,0.10914 -1.726818,0.19843 -3.83687,0.19843 H 44.80729 v 4.23334 4.23333 H 68.752082 92.696875 Z M 52.74479,146.26058 c 0,-0.14552 0.119063,-0.26458 0.264584,-0.26458 0.145521,0 0.264583,0.11906 0.264583,0.26458 0,0.14552 -0.119062,0.26458 -0.264583,0.26458 -0.145521,0 -0.264584,-0.11906 -0.264584,-0.26458 z m 2.116667,0 c 0,-0.14552 0.119063,-0.26458 0.264584,-0.26458 0.14552,0 0.264583,0.11906 0.264583,0.26458 0,0.14552 -0.119063,0.26458 -0.264583,0.26458 -0.145521,0 -0.264584,-0.11906 -0.264584,-0.26458 z m 2.116667,0 c 0,-0.14552 0.119062,-0.26458 0.264583,-0.26458 0.145521,0 0.264584,0.11906 0.264584,0.26458 0,0.14552 -0.119063,0.26458 -0.264584,0.26458 -0.145521,0 -0.264583,-0.11906 -0.264583,-0.26458 z m 47.889586,4.36563 v -4.36563 h -5.953127 -5.953125 v 4.36563 4.36562 h 5.953125 5.953127 z m -10.84938,14.55384 c 0.214329,-0.25825 0.290326,-0.98543 0.219399,-2.09933 l -0.108517,-1.70421 1.069769,0.0112 c 1.353594,0.0142 1.731227,0.32537 1.731227,1.42673 0,0.9449 -0.814558,1.77934 -1.485583,1.52184 -0.437085,-0.16772 -0.462927,0.0352 -0.09261,0.72712 0.247585,0.46262 0.18094,0.51177 -0.693855,0.51177 -0.828304,0 -0.920498,-0.0569 -0.639832,-0.39511 z m 1.853545,-2.39863 c 0,-0.92599 -0.421098,-1.23532 -0.698297,-0.51296 -0.212276,0.55319 0.128763,1.77538 0.44147,1.58212 0.141255,-0.0873 0.256827,-0.56842 0.256827,-1.06916 z m -57.770105,2.22951 c -1.118538,-0.65019 -1.581701,-3.28368 -0.832366,-4.73273 1.112465,-2.15127 3.369757,-1.39534 3.583821,1.20017 0.136745,1.65803 -0.02844,2.29917 -0.788082,3.05881 -0.830243,0.83024 -1.197598,0.91888 -1.963373,0.47375 z m 1.297599,-1.43486 c 0.18609,-1.24094 0.04661,-3.715 -0.224738,-3.98634 -0.08413,-0.0841 -0.279952,-0.0745 -0.435155,0.0214 -0.306929,0.18969 -0.402424,4.69724 -0.105797,4.99386 0.391137,0.39114 0.593518,0.11918 0.76569,-1.02895 z m 49.21576,0.52538 c -0.924423,-0.92442 -0.845786,-2.60334 0.166089,-3.54605 0.596077,-0.55533 1.79899,-0.73714 1.79899,-0.2719 0,0.14552 -0.110558,0.26458 -0.245685,0.26458 -0.597264,0 -1.077232,0.89257 -1.077232,2.00327 0,1.01947 0.07329,1.17173 0.564004,1.17173 0.504048,0 0.549109,-0.11106 0.423881,-1.0447 -0.153331,-1.14317 0.299988,-2.08213 0.843159,-1.74643 0.202259,0.125 0.291819,0.66162 0.238575,1.42949 -0.07498,1.08137 -0.02155,1.22935 0.443923,1.22935 0.420124,0 0.546042,-0.20369 0.611062,-0.98847 0.06414,-0.7741 -0.06497,-1.13532 -0.595312,-1.66567 -0.372465,-0.37246 -0.677208,-0.73133 -0.677208,-0.79747 0,-0.36153 1.130335,-0.0329 1.76517,0.51311 1.404889,1.20843 0.745415,3.86454 -0.959511,3.86454 -0.384904,0 -0.829263,-0.12944 -0.987463,-0.28764 -0.207127,-0.20712 -0.337395,-0.20712 -0.465407,0 -0.262976,0.42551 -1.370378,0.34892 -1.847035,-0.12774 z m 13.969861,-0.52505 c -0.86767,-1.32423 -0.25657,-3.37493 1.10776,-3.71735 0.95943,-0.2408 1.04239,0.004 0.26495,0.78123 -0.71748,0.71748 -0.86779,1.70368 -0.39446,2.58811 0.22279,0.41628 0.36178,0.46351 0.65293,0.22187 0.26169,-0.21718 0.32457,-0.63765 0.21495,-1.4374 -0.1058,-0.77188 -0.0484,-1.19627 0.18096,-1.33802 0.55599,-0.34362 1.0106,0.58073 0.85524,1.73896 -0.12522,0.93364 -0.0802,1.0447 0.42388,1.0447 0.49072,0 0.56401,-0.15227 0.56401,-1.17173 0,-1.1107 -0.47997,-2.00327 -1.07723,-2.00327 -0.13513,0 -0.24569,-0.11907 -0.24569,-0.26459 0,-0.29619 0.78976,-0.35104 1.44336,-0.10023 1.1331,0.43482 1.57065,2.90706 0.67331,3.8044 -0.55867,0.55866 -1.70818,0.71148 -1.98482,0.26387 -0.1096,-0.17735 -0.32831,-0.17735 -0.65968,0 -0.8326,0.44559 -1.55469,Line truncated
|
||||
id="path2" /><path
|
||||
style="fill:#808080"
|
||||
d="m 43.396179,173.07169 c -0.09701,-0.097 -0.176389,-3.8454 -0.176389,-8.32975 v -8.15337 l -6.548437,-0.0708 c -6.34846,-0.0686 -6.548438,-0.0869 -6.548438,-0.59994 0,-0.51301 0.199978,-0.53133 6.548438,-0.59993 l 6.548437,-0.0708 V 94.608444 33.969858 l -1.059851,1.034917 c -0.989866,0.966578 -1.089016,1.005752 -1.501523,0.593245 -0.412506,-0.412506 -0.348751,-0.638909 0.9655,-3.428616 0.773944,-1.642819 1.523739,-3.135772 1.666211,-3.317673 0.66443,-0.84831 1.212028,-0.318737 2.831572,2.738364 2.132466,4.02531 2.023569,5.317937 -0.285463,3.388494 l -1.161237,-0.970338 -0.07018,7.902962 c -0.0386,4.34663 -0.01257,8.053092 0.05784,8.236584 0.10172,0.265076 5.103687,0.33362 24.345702,0.33362 H 93.22604 v 10.71424 10.71424 l -24.275521,0.06753 -24.275521,0.06753 v 41.539583 41.53958 l 30.228646,0.0673 30.228646,0.0673 v -4.76363 -4.76362 h 24.34167 24.34167 v 4.7625 4.7625 h 7.97119 7.9712 l -1.12206,-1.21729 c -1.03294,-1.1206 -1.08812,-1.25123 -0.69472,-1.64463 0.39339,-0.39339 0.68143,-0.30196 3.62632,1.15115 3.16955,1.56395 3.97437,2.22847 3.45442,2.85217 -0.14389,0.17259 -1.69977,1.02413 -3.45751,1.8923 -2.94205,1.4531 -3.22984,1.54454 -3.62323,1.15114 -0.3934,-0.39339 -0.34307,-0.51877 0.63362,-1.57848 l 1.06097,-1.15115 H 107.15541 44.674999 l -0.06992,8.26823 c -0.06589,7.79212 -0.09763,8.26823 -0.551215,8.26823 -0.264715,0 -0.560675,-0.0794 -0.657689,-0.17639 z M 153.02188,151.28766 v -4.7625 h -23.54792 -23.54792 v 4.7625 4.7625 h 23.54792 23.54792 z M 92.432291,66.621 V 61.8585 H 68.752082 45.071874 v 4.7625 4.7625 h 23.680208 23.680209 z m 0,-10.847916 v -4.7625 H 68.884374 45.336457 v 4.7625 4.7625 h 23.547917 23.547917 z m 1.805893,107.845426 0.0861,-1.95789 0.83994,-0.12443 c 0.461967,-0.0684 0.989796,-0.0675 1.172953,0.002 0.504048,0.19147 0.698654,1.34565 0.354316,2.10139 -0.241655,0.53037 -0.448452,0.64406 -0.984978,0.5415 -0.619793,-0.11848 -0.669339,-0.0633 -0.567281,0.63209 0.09256,0.63067 0.01718,0.76201 -0.437664,0.76252 -0.51456,5.8e-4 -0.544016,-0.12384 -0.463388,-1.95727 z m 1.789476,-0.29171 c 0.209982,-0.83664 -0.02154,-1.4558 -0.544356,-1.4558 -0.282814,0 -0.405179,0.26635 -0.405179,0.88194 0,1.35214 0.654867,1.74791 0.949535,0.57386 z m -58.198835,1.22649 c -1.825406,-2.01705 -0.386832,-6.4379 1.726836,-5.3067 1.953092,1.04526 1.523284,5.79941 -0.524308,5.79941 -0.416148,0 -0.957285,-0.22172 -1.202528,-0.49271 z m 1.631335,-2.24841 c 0.07507,-2.28472 -0.236707,-3.42255 -0.787021,-2.87223 -0.356239,0.35624 -0.568584,2.78034 -0.363404,4.14858 0.14354,0.95719 0.283809,1.21289 0.62916,1.14688 0.36906,-0.0705 0.457467,-0.48152 0.521265,-2.42323 z m 49.347786,1.81475 c -1.251277,-0.87643 -0.938041,-3.0842 0.537951,-3.79162 0.943422,-0.45218 1.225185,-0.20524 0.469995,0.41189 -0.73777,0.6029 -0.994555,2.14252 -0.473818,2.8409 0.331352,0.44439 0.449158,0.46769 0.817743,0.1618 0.314639,-0.26113 0.385782,-0.5964 0.264142,-1.24479 -0.230392,-1.2281 0.026,-1.85804 0.571851,-1.40502 0.297473,0.24688 0.364662,0.60417 0.245856,1.30737 -0.117722,0.6968 -0.05127,1.06078 0.237398,1.30036 0.604967,0.50208 0.953227,0.1116 0.953227,-1.06878 0,-1.23117 -0.382681,-2.08366 -0.93535,-2.08366 -0.213162,0 -0.387566,-0.1318 -0.387566,-0.29289 0,-0.16109 0.188331,-0.22062 0.418514,-0.13229 0.230183,0.0883 0.52784,0.1606 0.661459,0.1606 0.622599,0 1.301277,1.10394 1.301277,2.11666 0,1.59415 -1.411636,2.71587 -2.299928,1.82758 -0.209071,-0.20908 -0.438621,-0.20908 -0.829273,0 -0.70594,0.3778 -0.8767,0.36592 -1.553478,-0.10811 z m 13.953404,-0.44644 c -1.01162,-1.08584 -0.52023,-3.16017 0.86281,-3.6423 0.44011,-0.15342 0.87018,-0.20258 0.95572,-0.10924 0.0855,0.0933 0.005,0.22357 -0.17992,0.2894 -0.71026,0.25343 -1.11959,0.94217 -1.1157,1.87728 0.005,1.22215 0.33185,1.76553 0.92487,1.53796 0.37124,-0.14245 0.42207,-0.38121 0.28119,-1.32069 -0.12644,-0.84319 -0.0705,-1.23105 0.21144,-1.46501 0.52403,-0.4349 0.73201,-0.0253 0.65447,1.28888 -0.0798,1.35282 0.42932,1.96009 1.00338,1.19673 0.53175,-0.7071 0.28253,-2.24164 -0.463,-2.85088 -0.75519,-0.61714 -0.47343,-0.86407 0.46999,-0.4119 1.52434,0.7306 1.78707,2.96345 0.45165,3.83845 -0.53228,0.34877 -0.77381,0.36871 -1.34017,0.11066 -0.48162,-0.21944 -0.77484,-0.23081 -0.96812,-0.0375 -0.44595,0.44594 -1.16809,0.3213 -1.74861,-0.30181 z m 5.3917,0.0144 c -0.20172,-0.3264 0.32597,-0.95769 0.97188,-1.1627 0.56485,-0.17927 0.68647,-1.06583 0.17455,-1.2724 -0.22526,-0.0909 -0.20417,-0.13882 0.0661,-0.15028 0.71146,-0.0302 0.97962,0.3515 1.11522,1.58736 l 0.13337,1.21542 -1.15263,0.0174 c -0.63395,0.01 -1.22279,-0.0961 -1.30854,-0.23483 z m 1.47716,-0.64156 c 0,-0.2097 -0.11906,-0.38126 -0.26458,-0.38126 -0.14553,0 -0.26459,0.24515 -0.26459,0.54478 0,0.29963 0.11906,0.47119 0.26459,0.38126 0.14552,-0.0899 0.26458,-0.33509 0.26458,-0.54478 z m -1.32292,-1.1594 c 0,-0.44355 0.46925,-0.55338 0.62543,-0.14639 0.0922,0.24016 -8.3e-4,0.39536 -0.23686,0.39536 -0.21371,0 -0.38857,-0.11204 -0.38857,-0.24897 z m 73.63399,-3.58782 c -1.25128,-0.87643 -0.93804,-3.08419 0Line truncated
|
||||
id="path1" /></g></svg>
|
||||
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 65 KiB |
|
After Width: | Height: | Size: 52 KiB |
|
After Width: | Height: | Size: 128 KiB |
@@ -0,0 +1,491 @@
|
||||
<header><h1>
|
||||
Programmation concurrente
|
||||
</h1></header>
|
||||
|
||||
Un programme concurrent est un programme non séquentiel.
|
||||
Soit l'expression arithmétique suivante : $(a+b) * (c+d)$.
|
||||
|
||||
***Programmation séquentiel***
|
||||
1. $x = (a+b)$
|
||||
2. $y = (c+d)$
|
||||
3. $x*y$
|
||||
|
||||
***Programmation concurrente***
|
||||
1. $x = (a+b) \qquad y = (c+d)$
|
||||
2. $x*y$
|
||||
|
||||
La programmation concurrente sur 1 CPU permet d'exploiter pleinement les périphériques parallèlement au processeur.
|
||||
Cela permet d'éviter de bloquer toutes les applications lorsqu'une seule est en attente d'une réponse.
|
||||
|
||||
La programmation concurrente sur plusieurs CPU permet en plus un gain de temps.
|
||||
|
||||
## Switching contexte
|
||||
Lors d'un switching context, pour passer d'un processus P1 a un processus P2, les actions suivantes sont effectué :
|
||||
1. Sauvegarde des registres de P1 sur la stack
|
||||
2. Placement de P1 dans la WAITING queue
|
||||
3. Sélectionne le premier processus de la READY queue (P2)
|
||||
4. Charge les registres de P2
|
||||
5. Restaure le contexte utilisateur
|
||||
|
||||
# Exclusion mutuelle
|
||||
- un programme est une entité statique qui contient des processus
|
||||
- un processus est une entité dynamique (il s'agit d'une tâche en cours d'exécution)
|
||||
|
||||
Les processus ne sont jamais totalement indépendants.
|
||||
Ils partagent des ressources (mémoire, fichiers, périphériques, variable, etc.)
|
||||
|
||||
Ceci peut conduire à des erreurs et doit donc être controlé.
|
||||
|
||||
- ***Section critique :*** partie du code qui accède à une ressource partagée.
|
||||
- ***Exclusion mutuelle :*** un seul processus à la fois peut accéder à la section critique.
|
||||
- ***Ressource critique :*** ressource qui doit être accédé en exclusion mutuelle
|
||||
|
||||
On pourrait éviter les problèmes d'exclusion mutuelle si on pouvait assurer que deux processus n'exécute jamais en même temps une section
|
||||
critique.
|
||||
On peut résoudre ce problème à l'aide de **mutex** ou **sémaphore**.
|
||||
|
||||
## Exclusion mutuelle par attente active
|
||||
|
||||
```pascal
|
||||
type processNum = 1..2;
|
||||
|
||||
module CS; /* Section critique */
|
||||
defines Enter, Leave; /* Primitive */
|
||||
|
||||
var
|
||||
request : array processNum of boolean;
|
||||
tour : processNum;
|
||||
procedure Enter(num: processNum);
|
||||
begin
|
||||
request[num] := true; /* Demande d'accès */
|
||||
tour := 3 – num; /* Autre processus */
|
||||
repeat
|
||||
/* Attendre que l'autre processus ait fini avec "not request" */
|
||||
/* Priorité si les deux processus demandent en même temps à la section critique
|
||||
grâce à "tour = num" */
|
||||
until (not request[3-num]) or (tour = num)
|
||||
end Enter;
|
||||
|
||||
procedure Leave(num: processNum);
|
||||
begin
|
||||
request[num] := false
|
||||
end Leave;
|
||||
|
||||
begin /* Initialisation */
|
||||
request[1] := false;
|
||||
request[2] := false
|
||||
end CS;
|
||||
|
||||
/***********************************************************************/
|
||||
|
||||
process P1;
|
||||
uses CS, Console;
|
||||
begin
|
||||
CS.Enter(1);
|
||||
Console.Write(”Error on hard disk !”);
|
||||
CS.Leave(1);
|
||||
end P1;
|
||||
|
||||
/***********************************************************************/
|
||||
|
||||
process P2;
|
||||
uses CS, Console;
|
||||
begin
|
||||
CS.Enter(2);
|
||||
Console.Write(”Printer not ready !”);
|
||||
CS.Leave(2);
|
||||
end P2;
|
||||
```
|
||||
|
||||
- si les processus *P1* et *P2* ne tentent pas de pénétrer simultanément dans la section
|
||||
critique, la variable *request[3-num]* est fausse et l'entrée en section critique est
|
||||
autorisée
|
||||
- si par contre les processus *P1* et *P2* tentent de pénétrer simultanément dans la section
|
||||
critique, la variable *tour* permet d'arbitrer entre les deux demandes. La variable
|
||||
*tour* (à la ligne 15) indique le numéro du processus élu
|
||||
|
||||
Cette méthode peut facilement être généralisée à n processus. Il suffit de faire *tour := n+1 – num* et
|
||||
*request[n+1-num]*.
|
||||
|
||||
Cette solution n'est pas satisfaisante, car elle consomme beaucoup de ressources CPU. En effet, un processus
|
||||
qui attend l'accès à la section critique est en attente active. Il est donc en boucle constante et consomme
|
||||
beaucoup de ressources CPU.
|
||||
|
||||
|
||||
## Exclusion mutuelle par masquage des interruptions
|
||||
Masquer les interruptions pour résoudre le problème de l'exclusion mutuelle.
|
||||
Plus d'interruption $=>$ plus de commutation de processus $=>$ plus de risque de violer l'exclusion mutuelle.
|
||||
|
||||
Le danger avec cette solution est le risque de perte d'information provenant des périphériques. Cela ne peut être une bonne solution
|
||||
que si la section critique est très courte. Il est dangereux de laisser la possibilité de masquer les interruptions à l'utilisateur.
|
||||
|
||||
|
||||
## Instruction TAS (TestAndSet)
|
||||
L'exclusion mutuelle par masquage des interruptions n'est pas possible dans une architecture multiprocesseur.
|
||||
Il est possible de résoudre le problème de l'exclusion mutuelle en utilisant l'instruction *TAS*.
|
||||
Elle permet de tester et de modifier une variable en **une seule instruction** assembleur.
|
||||
|
||||
|
||||
# Mutex
|
||||
Le mutex est utilisé pour bloquer l'accès simultané aux sections critiques avec les primitives suivantes :
|
||||
- ***block() :*** Il laisse passer un seul processus à la fois. Si la section est déjà bloquée, le processus et mis dans
|
||||
une file d'attente.
|
||||
```pascal
|
||||
procedure Block(var v:lock);
|
||||
begin
|
||||
if v.state = OPEN then
|
||||
v.state := CLOSE
|
||||
else
|
||||
block the process in the list "v.waiting"
|
||||
end if
|
||||
end Block;
|
||||
}
|
||||
```
|
||||
- ***release() :*** Il libère la section critique, si la file d'attente est vide. Sinon, il passe la main au processus
|
||||
suivant.
|
||||
```pascal
|
||||
procedure Release(var v:lock);
|
||||
begin
|
||||
if queue "v.waiting" not empty then
|
||||
unblock first process in the list "v.waiting"
|
||||
else
|
||||
v.state := OPEN
|
||||
end if
|
||||
end Release;
|
||||
}
|
||||
```
|
||||
Ces procédures sont elles même des sections critiques. Elles sont cepandant très courte et ne contiennent aucunes entrées-sorties.
|
||||
Elles peuvent donc être implémentées à l'aide d'une [Exclusion mutuelle par masquage des interruptions](#exclusion-mutuelle-par-masquage-des-interruptions)
|
||||
ou d'une [instruction TAS](#instruction-tas-testandset).
|
||||
|
||||
Les Mutex restent un outil primitif. Ils ne s'appliquent pas sur des problèmes plus complexes.
|
||||
|
||||
# Sémaphore
|
||||
Le sémaphore est une généralisation du mutex. Il permet de bloquer l'accès à une ressource partagée par plusieurs processus.
|
||||
Il limite l'accès à plusieurs même ressources utilisées par plusieurs processus.
|
||||
|
||||
Par exemple : les processus ont besoin d'imprimer et il y a plusieurs imprimantes à disposition.
|
||||
Le sémaphore va limiter l'accès au nombre d'imprimantes disponibles.
|
||||
|
||||
Les primitives de sémaphores sont :
|
||||
- ***P*** (Passeren = passer) appelé aussi *wait*, *up*, *get* ou *lock*
|
||||
```pascal
|
||||
procedure P(var s:semaphore);
|
||||
begin
|
||||
s.num := s.num – 1;
|
||||
if s.num < 0 then
|
||||
block the process at the end of the list "s.waiting"
|
||||
end if
|
||||
end P;
|
||||
```
|
||||
- ***V*** (Vrijgeven = libérer) appelé aussi *signal*, *down*, *release* ou *unlock*
|
||||
```pascal
|
||||
procedure V(var s:semaphore);
|
||||
begin
|
||||
s.num := s.num + 1;
|
||||
if s.num <= 0 then
|
||||
unblock first process in the list "s.waiting"
|
||||
end if
|
||||
end V;
|
||||
```
|
||||
|
||||
À l'initialisation, le sémaphore est initialisé au nombre de mêmes ressources disponibles.
|
||||
Ou alors, il est initialisé à 1 pour un mutex.
|
||||
Ou alors, il est initialisé à 0 pour un sémaphore binaire (file d'attente).
|
||||
|
||||
|
||||
## Interblocage
|
||||
L'interblocage est un problème qui peut survenir lorsqu'un ensemble de processus se bloquent mutuellement.
|
||||
On appelle aussi cela un **DEADLOCK**.
|
||||
|
||||
## Résolution reader / writter
|
||||
Le but est qu'il peut avoir plusieurs processus qui puisse lire la même ressource et aucun modifier la ressource.
|
||||
Sinon, **un** processus peut modifier la ressource et aucun peut lire.
|
||||
```cpp
|
||||
void BeginRead(void) {
|
||||
osSemaphoreAcquire(rw, osWaitForever);
|
||||
osSemaphoreAcquire(mutexNbReader, osWaitForever); // ENTER CRITICAL SECTION
|
||||
nbReader++;
|
||||
if(nbReader == 1) osSemaphoreAcquire(w, osWaitForever);
|
||||
osSemaphoreRelease(mutexNbReader); // EXIT CRITICAL SECTION
|
||||
osSemaphoreRelease(rw);
|
||||
}
|
||||
|
||||
void EndRead(void) {
|
||||
osSemaphoreAcquire(mutexNbReader, osWaitForever); // ENTER CRITICAL SECTION
|
||||
nbReader--;
|
||||
if(nbReader == 0) osSemaphoreRelease(w);
|
||||
osSemaphoreRelease(mutexNbReader); // EXIT CRITICAL SECTION
|
||||
}
|
||||
|
||||
void BeginWrite(void) {
|
||||
osSemaphoreAcquire(rw, osWaitForever);
|
||||
osSemaphoreAcquire(w, osWaitForever);
|
||||
}
|
||||
|
||||
void EndWrite(void) {
|
||||
osSemaphoreRelease(w);
|
||||
osSemaphoreRelease(rw);
|
||||
}
|
||||
```
|
||||
|
||||
# Coopération-Synchronisation
|
||||
La coopération entre processus implique la synchronisation entre eux pour éviter de ressource.
|
||||
|
||||
## Évènement
|
||||
L'évènement est un outil permettant de résoudre un problème de synchronisation comme les sémaphores ou les mutex.
|
||||
On peut manipuler les évènements :
|
||||
- ***Wait(event)*** : Attendre que l'évènement **event** se produise par la fonction signal(...)
|
||||
- ***Signal(event)*** : Déclencher l'évènement **event**
|
||||
- ***Init(event)*** : Reset l'évènement **event** (inverse de signal(event))
|
||||
|
||||
On peut aussi résoudre un problème de synchronisation entre processus avec un sémaphore initialisé à 0. On peut le comparer
|
||||
à une barrière déclenchable depuis un autre processus.
|
||||
|
||||
Les sémaphores ne permettent pas de résoudre tous les problèmes de synchronisation. Trop grand risque de DEADLOCK.
|
||||
Il faut utiliser d'autres outils plus performants.
|
||||
|
||||
## Le moniteur (monitor)
|
||||
On peut l'imager avec une classe entièrement protégée par des sémaphores pour l'accès au variable et fonction (procédure).
|
||||
|
||||
Le moniteur possède comme primitive :
|
||||
- ***x.wait*** : bloque le processus jusqu'à ce qu'un x.send soit émis.
|
||||
- ***x.send*** : débloque le processus qui attend sur un x.wait.
|
||||
|
||||
```pascal
|
||||
monitor M;
|
||||
defines DepositeChar, TakeChar;
|
||||
|
||||
var
|
||||
c : char;
|
||||
chAvailable : boolean;
|
||||
x : signal;
|
||||
|
||||
procedure TakeChar(var ch:char);
|
||||
begin
|
||||
if not chAvailable then x.wait end if;
|
||||
ch := c;
|
||||
chAvailable := FALSE
|
||||
end TakeChar;
|
||||
|
||||
procedure DepositeChar(ch:char);
|
||||
begin
|
||||
c := ch;
|
||||
chAvailable := TRUE;
|
||||
x.send
|
||||
end DepositeChar;
|
||||
|
||||
begin
|
||||
chAvailable := FALSE
|
||||
end M;
|
||||
```
|
||||
```pascal
|
||||
process Keyboard;
|
||||
uses M;
|
||||
var cRead: char;
|
||||
begin
|
||||
...
|
||||
Read a character on keyboard
|
||||
Put it in the cRead variable
|
||||
M.DepositeChar(cRead);
|
||||
...
|
||||
end Keyboard;
|
||||
|
||||
process Screen;
|
||||
uses M;
|
||||
var cWrite: char;
|
||||
begin
|
||||
...
|
||||
M.TakeChar(cWrite);
|
||||
Write the character cWrite on the screen
|
||||
...
|
||||
end Screen;
|
||||
```
|
||||
|
||||
## Producteurs / consommateurs
|
||||
Le problème survient lorsque le producteur envoie un message au consommateur via une queue.
|
||||
[Example](https://git.kb28.ch/Klagarge/PTR-Queue/src/branch/master/main.c)
|
||||
|
||||
- ***Le producteur*** (Producer) doit être mis en attente s'il veut déposer un message alors que le tampon est plein
|
||||
- ***Le consommateur*** (Consumer) doit être mis en attente s'il veut prélever un message alors que le tampon est vide
|
||||
- ***Le producteur*** (Producer) en attente doit être débloqué dès que le consommateur (Consumer) prélève un message
|
||||
- à ce moment le tampon n'est plus plein
|
||||
- ***Le consommateur*** (Consumer) en attente doit être débloqué dès que le producteur (Producer) dépose un message
|
||||
- à ce moment le tampon n'est plus vide
|
||||
|
||||
On peut résoudre aisément le problème producteurs-consommateurs avec un moniteur ainsi qu'avec un sémaphore.
|
||||
|
||||
<b><u>Solution à chier :</u></b><br>
|
||||
Le producteur attend que le consommateur soit capable de réceptionner le message, ainsi les deux peuvent
|
||||
continuer leurs exécutions (principe de rendez-vous). Ce concept ne possède pas de buffer.
|
||||
|
||||
# Allocation processeur
|
||||
État d'un processus :
|
||||
|
||||

|
||||
|
||||
Le processeur possède une liste de descripteur caractérisant chaque processus. Ces descripteurs de processus qui
|
||||
contiennent le registre de status et un pointeur sur sa stack. Ils sont utilisé pour faire des switching context :
|
||||
1. Le noyau va sauvegarder les registres de travail du processus en cours sur la stack.
|
||||
2. Il va sauvegarder aussi le registre de status et le pointeur de stack dans la liste de descripteurs.
|
||||
3. Il va prendre le descripteur du prochain processus et utiliser ses registres.
|
||||
4. Le processus continue jusqu'au nouveau switching context.
|
||||
|
||||
## Cycle processeur
|
||||
Un processeur réalise alternativement une suite de :
|
||||
- ***Cycles processeur*** (CPU burst cycle)
|
||||
- ***Cycles d'entrée-sortie*** (I/O burst cycle)
|
||||
|
||||
Lorsque le temps de cycle processeur est petit et qu'il y a une attente engendrée par les entrées-sorties, il est pertinent de partager
|
||||
le processeur entre plusieurs processus.
|
||||
|
||||
## Queues d'ordonnancement
|
||||
Les processus en attente du processeur sont placés dans une liste appelée **queue de processus prêts** (ready queue).
|
||||
Cette queue peut être gérée comme une file FIFO, une liste de priorité, un arbre, une pile, une liste non-ordonnée, etc.
|
||||
|
||||
Cette queue est sous forme d'une liste chaînée qui contient des pointeurs sur les descripteurs de processus.
|
||||
Les processus qui ont besoins d'une ressource sont placés dans une liste d'attente correspondant à la ressource (mutex/sémaphore).
|
||||
|
||||
L'ensemble des queues peut être regroupé dans le diagramme de synthèse suivant :
|
||||

|
||||
|
||||
|
||||
## Ordonnanceurs
|
||||

|
||||
|
||||
### Ordonnanceur à court terme
|
||||
- Sélectionne un nouveau processus toutes les millisecondes.
|
||||
- Doit être très rapide
|
||||
- Est très simple
|
||||
|
||||
### Ordonnanceur à long terme
|
||||
- Contrôle le degré de multiprogrammation, c'est-à-dire, le nombre de processus en mémoire.
|
||||
- Assure que la charge globale du processeur soit répartie équitablement entre les processus réalisant beaucoup d'entrée-sorties et les
|
||||
processus réalisant beaucoup de calculs.
|
||||
- S'exécute peu fréquement
|
||||
- La complexité de ce dernier augmente fortement.
|
||||
|
||||
### Le swapping
|
||||
On peut augmenter la RAM avec de la mémoire virtuelle (swap) pour garder les processus et ne pas les mettre en stockage
|
||||
long termes.
|
||||
|
||||
### Distributeur de tâches
|
||||
Il distribue au processeur les processus sélectionnés par l'ordonnanceur court terme.
|
||||
- Il charge les registres du processus sélectionné
|
||||
- Il change en mode utilisateur depuis le superviseur. Ce dernier servant à modifier les registres.
|
||||
- Il met à jour le PC
|
||||
|
||||
## Critères de performance
|
||||
- ***Efficacité** (CPU utilisation) : Taux d'utilisation du processus
|
||||
- ***Débit*** (troughtput) : Nombre de processus par unité de temps
|
||||
- ***Temps global*** (turnaround time) : Temps qui s'écoule entre la soumission du processus et la fin de son exécution
|
||||
- ***Temps d'attente*** (waiting time) : Temps d'attente d'un processus dans la queue des processus prêts
|
||||
- ***Temps de réponse*** (response time) : intervalle de temps entre l'heure de soumission et l'heure de la première réponse
|
||||
|
||||
## Algorithme d'ordonnancement
|
||||
L'allocateur du processeur doit sélectionner quels processus situés dans la queue des processeurs prêts doivent recevoir le processeur.
|
||||
|
||||
### FIFO
|
||||
L'algorithme FIFO est le plus simple. Il sélectionne le premier processus de la queue des processus prêts. Mais, il n'est
|
||||
pas forcément le plus efficace.
|
||||
|
||||
### Le plus court d'abord
|
||||
Le processus avec la plus petite suite d'instruction est exécuté en premier. Si deux processus ont le même nombre d'instructions,
|
||||
on peut utiliser un algorithme FIFO pour déterminer le vainqueur. Il est performant, mais c'est compliqué de connaître
|
||||
le nombre d'instructions.
|
||||
|
||||
Il nécessite d'éstimer en temps réel la longueur du processus.
|
||||
|
||||
$$\tau_{n+1} = \alpha\cdot t_{n}+(1-\alpha)\cdot \tau_{n}$$ <br>
|
||||
$\tau_{n+1}$ : prochaine valeur estimée <br>
|
||||
$\alpha$ : coefficient de pondération $0\leq\alpha\leq 1$ <br>
|
||||
$t_{n}$ : valeur mesurée à l'instant $n$ <br>
|
||||
$\tau_{n}$ précédente valeur estimée pour l'instant $n$ <br>
|
||||
|
||||
### Priorité
|
||||
Cet algorithme donne au processeur le processus qui a la plus haute priorité qui est défini par l'utilisateur.
|
||||
On peut rencontrer plusieurs problèmes :
|
||||
- ***Blocage indéfini*** (indefinite blocking) : on reste bloqué dans un processus de haute priorité.
|
||||
- ***Famine*** (starvation) : Les processus de basse priorité ne sont jamais exécuté.
|
||||
|
||||
***Solution*** : On augmente la priorité dum processus en attente tous les quantums de temps.
|
||||
|
||||
### Ordonnancement avec réquisition (preemptive)
|
||||
On réévalue l'ordre des processus dans la queue à chaque tick et le processus élu prend ou garde le processeur.
|
||||
|
||||
### Tourniquet (round-robin)
|
||||
Il est basé sur une tranche de temps fixe soit quantum de temps. On donne à chacun des processus ce temps et on passe au
|
||||
processus suivant une fois le temps écoulé.
|
||||
Lorsque le temps est grand, on retrouve en quelque sorte une FIFO, car le temps d'exécution des processus est plus court que
|
||||
le quantum de temps.
|
||||
Si le temps est très petit, le tourniquet est appelé **processeur partagé** (processor sharing).
|
||||
|
||||
<b><u>Overhead</u></b> : temps perdu pour changer de processus.<br>
|
||||
|
||||
### Files multiples (multi-queue)
|
||||
On utilise cette technique lorsqu'on peut facilement classer les processus en différents groupes. Chaque queue possède son
|
||||
propre algorithme d'ordonnancement. Un ordonnancement entre les queues est réalisé soit par priorité et réquisition, soit
|
||||
en round robin.
|
||||
|
||||
### Files multiples imbriquées (multi-level feedback queue)
|
||||
Cette technique permet aux processus d'évoluer dans des queues en fonction du temps d'exécution écoulé.
|
||||
- Un processus entrant dans la queue des processus prêts est placé dans la queue 0
|
||||
- Si le quantum est écoulé et que la suite d’instruction processeur n’est pas terminée, le processus est placé dans
|
||||
la queue de priorité inférieure
|
||||
|
||||
Plus on descend dans les queues, plus on est de priorité basse.
|
||||
|
||||
<u>Exemple :</u> Plus on descend dans les queues, plus le quantum devient grand.
|
||||
|
||||
# Interblocage (DEADLOCK)
|
||||
Un processus attend indéfiniment pour accéder à une ressource bloquée.
|
||||
|
||||
L'interblocage apparaît si les 4 conditions apparaissent simultanément :
|
||||
1. ***L'exclusion mutuelle*** (mutual exclusion) : Une ressource au minimum n'est pas partageable.
|
||||
2. ***La détention et l'attente*** (hold and wait) : Un processus qui dispose déjà d'au moins une ressource et qui attend l'acquisistion
|
||||
d'une nouvelle ressource déjà utilisée par un autre processus.
|
||||
3. ***Pas de réquisition*** (no preemption) : Les ressources obtenues par un processus ne peuvent pas lui être retirées contre son gré
|
||||
4. ***L'attente circulaire*** (circulare wait) : Il doit y avoir un cycle d'au moins deux processus, chacun d'entre eux attendant
|
||||
une ressource détenue par un autre processus du cycle.
|
||||
|
||||
Les interblocages peuvent être décrits plus précisément en utilsant un graphe appelé **graphe
|
||||
d'allocation des ressources** (ressource allocation graph).
|
||||

|
||||
- Si le graphe d'allocation des ressources ne contient aucun cycle $=>$ Aucun processus n'est en interblocage
|
||||
- Si le graphe d'allocation des ressources contient un cycle $=>$ <u>Un risque</u> d'interblocage existe !!
|
||||
|
||||
On peut faire une réduction de graphe pour observer l'état suivant du graphe :
|
||||
- Un graphe peut être réduit par un processus Pi, si le processus n'a pas de demande de ressource en cours.
|
||||
- Pour faire une réduction, on enlève toutes les allocations du processus.
|
||||
|
||||
***Complètement réductible*** : si on arrive à faire des réductions successives jusqu'à ce qu'il n'y a plus de flêches
|
||||
dans le graphe.
|
||||
|
||||
On peut résoudre le problème d'interblocage :
|
||||
- ***Prévention*** : On garantit qu'aucun interblocage ne peut se produire.
|
||||
- ***Prévenir** : Empêcher une des 4 conditions de se produire
|
||||
- ***Éviter** : Éviter dynamiquement en allouant les ressource avec précaution
|
||||
- ***Détection ou guérison*** : Lorsqu'un interblocage est détecté, on ramène le système dans un état où les processus sont débloqués
|
||||
|
||||
En cas d'interblocage, on peut utiliser deux algorithmes pour résoudre ce problème.
|
||||
L'algorithme du banquier pour la prévention et ensuite un algorithme de reprise après interblocage.
|
||||
|
||||
L'algorithme de reprise fait soit appel à l'aide d'un opérateur, soit par une séquence automatique
|
||||
qui tue les processus impliqués dans l'interblocage, soit un processus après l'autre jusqu'à ce que l'interblocage soit résolu.
|
||||
Mais la destruction de processus est complexe et dangereuse.
|
||||
|
||||
## Algorithme du banquier (banker's algorithm)
|
||||
Cette technique impose de connaître pour chaque processus les ressources dont il aura besoin.
|
||||
- Il vérifie avant d'allouer une ressource que le graphe reste complètement réductible.
|
||||
- Il alloue une ressource uniquement s'il y a plus de ressources disponibles que demandées.
|
||||
- Sinon la demande de ressource est mise en attente.
|
||||
|
||||
|
||||
<b><u>Exemple :</u></b><br>
|
||||

|
||||

|
||||
|
||||
Une ressource n'est attribuée que si les ressources demandées sont inférieurs aux ressources disponibles.
|
||||
$$ (N)Needed < (A)Available $$
|
||||
|
||||
Si aucune rangée (processus) ne satisfait la condition, il y a <u><b>interblocage</b></u>.
|
||||
@@ -0,0 +1,9 @@
|
||||
<header><h1>
|
||||
PTR - Programmation Temps Réel
|
||||
</h1></header>
|
||||
|
||||
- [XF](/infotro/PTR/XF.md)
|
||||
- [UML](/infotro/PTR/UML)
|
||||
- [RealTime](/infotro/PTR/RealTime)
|
||||
- [SYS - Systèmes](/infotro/PTR/SYS)
|
||||
- [PCO - Programmation Concurrente](/infotro/PTR/PCO)
|
||||
@@ -0,0 +1,209 @@
|
||||
<header><h1>
|
||||
Real Time
|
||||
</h1></header>
|
||||
|
||||
## Temps réel vs non - temps réel
|
||||
Pour les systèmes non temps réel, seule l'exactitude du résultat importe, le traitement est principalement contrôlé par
|
||||
des données massives, et ils ne dépendent pas d'un matériel spécifique.
|
||||
En revanche, pour les systèmes temps réel, non seulement l'exactitude du résultat est importante, mais aussi le moment
|
||||
où le résultat apparaît. Le traitement est contrôlé par des événements, les données sont simples,
|
||||
les quantités sont modérées, le processeur est intensément utilisé, et ces systèmes dépendent d'un matériel spécifique.
|
||||
|
||||
**Système Déterministe:**
|
||||
1. L'état futur du système peut être prédit de manière précise à partir de son état actuel et des entrées.
|
||||
2. Chaque transition d'état est définie de manière univoque par les conditions actuelles, assurant une prévisibilité totale.
|
||||
3. Les réponses du système à des signaux d'entrée sont constantes, sans dépendance de la qualité ou du nombre de ces signaux.
|
||||
4. Idéal pour les applications où la précision du timing et la prédictibilité du comportement sont cruciales, comme dans les systèmes temps réel.
|
||||
5. Le concept de déterminisme assure une fiabilité et une certitude dans le fonctionnement du système.
|
||||
|
||||
**Système Non-Déterministe:**
|
||||
1. L'état futur du système ne peut pas être prédit de manière précise à partir de son état actuel et des entrées, introduisant une certaine incertitude.
|
||||
2. Plusieurs transitions d'état peuvent résulter des mêmes conditions initiales, rendant le comportement du système moins prévisible.
|
||||
3. Les réponses du système à des signaux d'entrée peuvent varier en fonction de différents facteurs, tels que la charge du système ou les conditions d'exécution.
|
||||
4. Souvent utilisé dans des applications où une flexibilité est nécessaire, mais peut poser des défis dans les systèmes où la prédictibilité est cruciale.
|
||||
5. La gestion de l'incertitude et l'adaptabilité aux conditions changeantes sont des caractéristiques clés des systèmes non-déterministes.
|
||||
|
||||
### Système temps réel
|
||||
- ***Temps réel dur:*** Le systéme fait une erreur si le délai est dépassé.
|
||||
- ***Temps réel souple:*** Le système accepte le dépassement du délai sans grave conséquence.
|
||||
- ***Temps réel ferme:*** Le système accepte le dépassement du délai, mais le résultat n'est plus d'aucune utilité.
|
||||
|
||||
|
||||
### Management du temps (Timing)
|
||||
Le timing peut avoir principalement deux formes différentes :
|
||||
- *Timing absolu:* Une tâche sera exécutée à un moment dans le temps bien déterminé.
|
||||
- *Timing relatif:* Une tâche sera exécutée à la suite de l'apparition d'un événement.
|
||||
|
||||
Le timing peut par-contre aussi être classé comme suit :
|
||||
- *Fixed Time:* Une tâche sera exécutée à un moment bien précis.
|
||||
- *Fixed Time +/-:* L'exécution d'une tâche est démarrée dans un tout petit intervalle de temps avant ou après un moment bien précis.
|
||||
- *Latest Time:* L'exécution d'une tâche est démarrée dans un intervalle de temps avant le moment le plus tard possible.
|
||||
- *Earliest Time:* L'exécution d'une tâche est démarrée dans un intervalle de temps après le moment le plus tôt possible.
|
||||
|
||||
### Management du traitement
|
||||
|
||||
***Multitasking:***
|
||||
Traitement en thread: pseudo-parallèle
|
||||
|
||||
***Multiprocessing:***
|
||||
Traintement en parallèle sur plusieurs coeur / processeurs.
|
||||
|
||||
### Management des ressources
|
||||
|
||||
***Mutex:***
|
||||
Une ressource peut être utilisée par un seul processus à la fois
|
||||
|
||||
***Semaphore:***
|
||||
Une ressource peut être utilisée par un nombre limité de processus et sous des conditions bien précise
|
||||
|
||||
### Approches pour la programmation temps réel
|
||||
|
||||
***Synchrone:***
|
||||
Tout est planifié à l'avance pendant la phase de programmation.
|
||||
|
||||
***Asynchrone:***
|
||||
Rien est planifié à l'avance. Toutes les décisions sont faites pendant d'exécution.
|
||||
|
||||
#### Synchrone
|
||||
Le timing des tâches cycliques est planifié précisément.
|
||||
Ces dernières sont synchronisées (démarrées) par un signal d'une horloge.
|
||||
La scéquence des tâches est déterminé à l'avance
|
||||
Le temps cumulée de l'execution des tâches ne doit pas dépasser le temps d'un cycle.
|
||||

|
||||
|
||||
<u>*Avantages*</u><br>
|
||||
- Timing précis
|
||||
- Comportement déterministe
|
||||
|
||||
<u>*Avantages*</u><br>
|
||||
- Inadaptée aux Processus Sporadiques :
|
||||
- Inflexibilité
|
||||
|
||||
#### Asynchrone
|
||||
|
||||
Le timing des tâches est contrôlé soit par un RTOS soit par un XF.
|
||||
Les tâches sont contrôlées par des événements et des appels de méthodes.
|
||||
Des tâches parallèles sont sérialisées.
|
||||
Le séquencement des tâches n'est pas déterminé.
|
||||

|
||||
|
||||
<u>Avantages</u><br>
|
||||
- Flexibilité face aux changements de spécification du système.
|
||||
- Implémentation possible au moyen de diagrammes d'états transitions.
|
||||
|
||||
<u>Inconvénients</u><br>
|
||||
- Le RTOS ou le XF introduit des délais dans le traitement.
|
||||
- Le timing n'est pas très précis, rendant les systèmes moins déterministes en temps.
|
||||
- Systèmes toujours préemptifs, avec la possibilité d'interruptions dans le traitement
|
||||
|
||||
|
||||
## Processus & Thread
|
||||
|
||||
**Processus :**
|
||||
Un processus est un programme en cours d'exécution, avec sa propre mémoire et ses ressources allouées.
|
||||
Dans le contexte des systèmes temps réel, un processus est souvent appelé une "task" (tâche).
|
||||
Il peut être lié à un Real-Time Operating System (RTOS) ou un Executive Framework (XF).
|
||||
Chaque processus a son propre espace mémoire, ce qui signifie qu'il ne partage pas directement ses données avec d'autres processus.
|
||||
La communication entre les processus se fait généralement à travers des messages.
|
||||
|
||||
**Thread :**
|
||||
- Un thread est une unité d'exécution plus légère au sein d'un processus.
|
||||
- Contrairement à un processus, un thread partage le même espace mémoire que son processus parent.
|
||||
- Les threads peuvent être exécutés de manière concurrente, ce qui permet un parallélisme au sein du processus.
|
||||
- Les threads peuvent communiquer plus facilement entre eux, car ils ont un accès direct à la mémoire partagée.
|
||||
|
||||
**Différence :**
|
||||
- La principale différence réside dans la gestion de la mémoire. Les processus ont leur propre espace mémoire distinct, tandis que les threads partagent la même mémoire avec leur processus parent.
|
||||
- Les threads permettent une exécution concurrente au sein du même processus, tandis que les processus sont généralement des entités indépendantes.
|
||||
- En résumé, un processus est un programme complet en cours d'exécution, tandis qu'un thread est une unité d'exécution plus petite qui partage la mémoire avec d'autres threads dans le même processus.
|
||||
|
||||
```plantuml
|
||||
@startuml
|
||||
binary "Red Task" as r
|
||||
binary "Green Task" as g
|
||||
binary "Blue Task" as b
|
||||
concise "Task" as t
|
||||
robust "Red Task" as rt
|
||||
|
||||
@0
|
||||
r is low
|
||||
g is low
|
||||
b is low
|
||||
t is {-}
|
||||
rt is Sleep
|
||||
|
||||
@1
|
||||
r is high
|
||||
g is high
|
||||
b is high
|
||||
t is "" #blue
|
||||
rt is ready
|
||||
|
||||
@2
|
||||
r is low
|
||||
g is low
|
||||
b is low
|
||||
t is "" #green
|
||||
|
||||
@4
|
||||
t is "" #red
|
||||
rt is running
|
||||
|
||||
@6
|
||||
b is high
|
||||
t is "" #blue
|
||||
rt is interrupt
|
||||
|
||||
@7
|
||||
b is low
|
||||
t is "" #red
|
||||
rt is running
|
||||
|
||||
@9
|
||||
b is high
|
||||
g is high
|
||||
t is "" #blue
|
||||
rt is interrupt
|
||||
|
||||
@10
|
||||
b is low
|
||||
g is low
|
||||
t is "" #green
|
||||
|
||||
@12
|
||||
t is "" #red
|
||||
rt is running
|
||||
|
||||
@13
|
||||
t is {-}
|
||||
rt is Sleep
|
||||
|
||||
@enduml
|
||||
```
|
||||
|
||||
## Scheduling
|
||||
|
||||
|
||||
## OSAL, OXF, RTOS
|
||||
|
||||
**Différence entre OSAL, OXF et RTOS :**
|
||||
|
||||
1. **OSAL (Operating System Abstraction Layer) :**
|
||||
- L'OSAL est une couche d'abstraction qui fournit une interface uniforme pour accéder aux services du système d'exploitation sous-jacent.
|
||||
- Il permet de rendre le code portable entre différents systèmes d'exploitation en masquant les détails spécifiques de chaque système.
|
||||
- L'OSAL est utile dans des environnements où le système d'exploitation peut varier, permettant ainsi d'adapter le logiciel à différentes plates-formes sans changer le code applicatif.
|
||||
|
||||
2. **OXF (Object Execution Framework) :**
|
||||
- L'OXF est un framework qui fournit une infrastructure pour le développement d'applications temps réel orientées objet.
|
||||
- Il facilite la modélisation d'objets réactifs, possédant souvent une machine à états, et offre une gestion efficace des événements.
|
||||
- L'OXF peut être utilisé avec ou sans RTOS. Il fournit des fonctionnalités telles que la gestion des événements, la gestion des tâches, et la synchronisation entre les objets réactifs.
|
||||
|
||||
3. **RTOS (Real-Time Operating System) :**
|
||||
- Un RTOS est un système d'exploitation conçu pour répondre aux exigences temporelles strictes des systèmes temps réel.
|
||||
- Il offre des fonctionnalités telles que l'ordonnancement des tâches, la gestion du temps, la synchronisation, et la gestion des interruptions pour assurer des réponses prévisibles aux événements.
|
||||
- Un RTOS peut être utilisé en conjonction avec l'OXF pour développer des systèmes temps réel orientés objet.
|
||||
|
||||
**Conclusion :**
|
||||
- L'OSAL fournit une abstraction des services du système d'exploitation.
|
||||
- L'OXF est un framework pour le développement d'applications temps réel orientées objet.
|
||||
- Un RTOS est un système d'exploitation temps réel complet, offrant des services spécifiques pour répondre aux contraintes temporelles strictes.
|
||||
@@ -0,0 +1,163 @@
|
||||
<header><h1>
|
||||
Introduction aux systèmes d'exploitation
|
||||
</h1></header>
|
||||
|
||||

|
||||
|
||||
Le système d'exploitation virtualise la partie hardware pour :
|
||||
- Simplifie l'utilisation
|
||||
- Gère les ressources physiques
|
||||
- Processeur
|
||||
- Mémoire principale
|
||||
- Entrées-sorties (disques, interfaces réseaux, ...)
|
||||
|
||||
L'OS gère les ressources à l'aide de technique de gestion des mémoires et de partage de ressources
|
||||
partageable entre plusieurs utilisateurs (ex : threads).
|
||||
Pour ce faire, il
|
||||
- connait à tout moment l'utilisateur d'une ressource
|
||||
- gère les accès à une ressource
|
||||
- accorde l'usage d'une ressource
|
||||
- évite les conflits
|
||||
|
||||
Les premiers systèmes d'exploitation étaient des systèmes à traitement par lots.
|
||||
Les programmes étaient regroupés en lots et exécutés les uns après les autres.
|
||||
Les programmes étaient chargés en mémoire et exécutés sans intervention de l'utilisateur.
|
||||
|
||||
## Machine à transistors
|
||||
|
||||
- Coûtent cher
|
||||
- Les utilisateurs ne peuvent pas interagir avec le système
|
||||
- Les utilisateurs écrivent leurs programmes en Fortran et en assembleur
|
||||
|
||||
<b><u>Fonctionnement :</b></u>
|
||||
1. Programmation d'une carte perforée en Fortran ou en assembleur
|
||||
2. L'opérateur prend les cartes perforées et les charge dans le compilateur
|
||||
3. L'opérateur récupère les cartes perforées compilées et les charge dans "l'ordinateur"
|
||||
4. L'ordinateur exécute le programme
|
||||
5. S'il y a une erreur, l'opérateur imprime l'image de la mémoire sur un listing et doit tout recommencer.
|
||||
|
||||
## Systèmes à traitement par lots
|
||||
Pour accélérer les opérations, deux méthodes sont utilisées :
|
||||
- ***Le traitement par lots (batch processing) :*** Regroupe et traite à la suite les travaux similaires
|
||||
- ***Le moniteurs d'enchaînement (linking monitor) :*** Les travaux sont automatiquement enchaîné et
|
||||
executé par un programme spécialisé (ancêtre des systèmes d'exploitation)
|
||||
- Une place mémoire lui est réservée dans la mémoire centrale qui se divise donc en une zone moniteur
|
||||
et une zone utilisateur
|
||||
|
||||
## Sysèmes avec spooling
|
||||
Les systèmes avec spooling permettent d'accéder simultanément à plusieurs périphériques d'entrée/sortie.
|
||||
Les travaux sont stockés dans une file d'attente (spool) et sont traités par un programme spécialisé.
|
||||
Le spooling permet de traiter plusieurs travaux en même temps.
|
||||
|
||||
La mémoire est partagée en quatre programmes :
|
||||
- Le programme d'entrée
|
||||
- Le programme d'enchaînement
|
||||
- Le programme de l'utilisateur
|
||||
- Le programme de sortie
|
||||
|
||||

|
||||
|
||||
## Système à multi-programmation
|
||||
La multi-programmation permet de partager le processeur entre plusieurs programmes.
|
||||
Chaque programme est chargé en mémoire et est exécuté en alternance.
|
||||
Dès qu'un programme doit lire des données ou écrire des résultats, il est mis en attente et un autre
|
||||
programme est exécuté afin d'optimiser l'utilisation du processeur.
|
||||
|
||||

|
||||
|
||||
La commutation de l'exécution d'un programme à un autre est assuré par l'**ordonnanceur** (scheduler).
|
||||
|
||||
*Inconvénients :* Les programmes cours, sont pénalisés, car leur temps d'exécution dépend aussi des autres
|
||||
programmes en cours d'exécution.
|
||||
|
||||
|
||||
## Système à temps partagé
|
||||
Le système à temps partagé permet à plusieurs utilisateurs d'accéder simultanément à l'ordinateur.
|
||||
Chaque utilisateur dispose d'un temps d'utilisation du processeur (time slice) qui est très court (ex. 40 msec).
|
||||
|
||||

|
||||
|
||||
## Système temps réel
|
||||
Les systèmes temps réel doivent être capables réagire au rythme où les données arrivent et repartent.
|
||||
Ils ont des contraîntes plus hautes ques les systèmes à temps partagé, qui peuvent se permettent d'attendre
|
||||
alors qu'un système temps réel aurait des conséquences fâcheuses.
|
||||
|
||||
## Systèmes multi-processeur répartis
|
||||
Dans un système multi-processeur répartis, chaque processeur dispose de son propre système d'exploitation.
|
||||
Les systèmes d'exploitation communiquent entre eux pour partager les ressources.
|
||||
|
||||
|
||||
# Mécanisme d'entrées-sorties
|
||||
Pour accéder aux périphériques, le processeur utilise des mécanismes d'entrées sorties.
|
||||
|
||||
## Les registres d'un processeur
|
||||
Les registres contenus dans le processeur sont regroupés selon leur fonctionnalités
|
||||
- Registre généraux
|
||||
- Registre spécialisés
|
||||
- Le **registre d'instruction** IR (Instruction Register) contient l'instruction courante
|
||||
- Le **compteur ordinal** PC (Program Counter) contient l'adresse de l'instruction courante
|
||||
- Le **registre d'état** SR (Status Register) contient des informations sur l'état du processeur
|
||||
|
||||
Ces regstres caractérisent le contexte d'un programme. Il s'agit de la partie sauvegardée lors d'un
|
||||
*switching context* (changement de contexte).
|
||||
|
||||
|
||||
## Les registres d'un périphérique
|
||||
Les périphériques disposent, pour comuniquer avec le processeur, d'un certain nombre de registres:
|
||||
- **Registre de données** DR (Data Register) : Contient les données à lire ou à écrire
|
||||
- **Registre d'état** SR (Status Register) : Contient des informations sur l'état du périphérique
|
||||
- **Registre de commande** CR (Command Register) : Détermine les caractéristiques du périphérique
|
||||
|
||||
## Entrées-sorties en boucle d'attente
|
||||
Dans ce mode, le processeur attend que le périphérique soit prêt pour effectuer une opération.
|
||||
Le processeur effectue une boucle d'attente jusqu'à ce que le périphérique soit prêt.
|
||||
|
||||
1. Le processeur contrôle le registre d'état du périphérique
|
||||
2. Si le périphérique n'est pas prêt, le processeur effectue une boucle d'attente. S'il est prêt :
|
||||
3. Le processeur écrit les données dans le registre de données
|
||||
4. Le processeur écrit la commande dans le registre de commande
|
||||
5. Le périphérique effectue l'opération
|
||||
6. Le périphérique met à jour le registre d'état
|
||||
7. Le processeur lit les données dans le registre de données
|
||||
|
||||
Si le périphérique notifie le processeur :
|
||||
1. Le périphérique va changer son statut
|
||||
2. Le processeur va pouvoir aller chercher les données dans le registre de données
|
||||
|
||||
## Les interruptions
|
||||
Les interruptions permettent de signaler au processeur qu'un événement s'est produit.
|
||||
Le signal est la conséquence d'un évènement interne au programme et résultant de son exécution ou
|
||||
externe et indépendant de son exécution. Toute l'information nécessaire pour traiter l'interruption
|
||||
est regroupé en mémoire dans deux mots consécutis, appelés **vecteur d'interruption** (vector interrupt).
|
||||
On y trouve :
|
||||
- L'adresse de la routine de traitement de l'interruption
|
||||
- Le registre d'état du processeur pour cette routine.
|
||||
|
||||
Lorsqu'une interruption se produit, le processeur effectue un switching context et exécute la routine.
|
||||
|
||||
On distingue en général au moins trois niveaux d'interruption :
|
||||
- Les interruptions externes
|
||||
- Les exceptions internes
|
||||
- Les appels systèmes
|
||||
|
||||

|
||||
|
||||
## La DMA
|
||||
La [DMA](/infotro/SEm/DMA) (Direct Access Memory) permet de faire des transferts vers la mémoire rapidement.
|
||||
Le processeur laisse le controller DMA accéder au périphérique pour amener les données directement dans la
|
||||
mémoire et éviter de perdre du temps en passant par le processeur qui doit faire une copie des données.
|
||||
Elle est très utile lorsqu'il y a un grand nombre de données.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||