Compare commits
19 commits
main
...
layer2_dev
Author | SHA1 | Date | |
---|---|---|---|
Thomas Kolb | 159c3f1518 | ||
Thomas Kolb | 1ae421e876 | ||
Thomas Kolb | a455fac050 | ||
Thomas Kolb | 44921bd65d | ||
Thomas Kolb | ccd9af6664 | ||
Thomas Kolb | 4dc2c60c8b | ||
Thomas Kolb | 706a1eb437 | ||
Thomas Kolb | eb802629a1 | ||
Thomas Kolb | c61a7a7cf7 | ||
Simon Ruderich | 3f4a50bdce | ||
Thomas Kolb | b46b2536e1 | ||
Thomas Kolb | 1bcc8c2fea | ||
Thomas Kolb | c342cf656e | ||
Thomas Kolb | 03829e058b | ||
Thomas Kolb | f686635837 | ||
Thomas Kolb | 10f634d144 | ||
Thomas Kolb | 79c340c20d | ||
Thomas Kolb | fc9e5c5229 | ||
Thomas Kolb | 899152a530 |
25
.forgejo/workflows/doc.yaml
Normal file
25
.forgejo/workflows/doc.yaml
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
on: [push]
|
||||||
|
jobs:
|
||||||
|
build-doc:
|
||||||
|
runs-on: docker
|
||||||
|
container:
|
||||||
|
image: git.tkolb.de/amateurfunk/hamnet70/asciidoctor:1.6
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- run: cd doc && make
|
||||||
|
- uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: documentation
|
||||||
|
path: doc/out/
|
||||||
|
deploy-doc:
|
||||||
|
needs: build-doc
|
||||||
|
runs-on: docker
|
||||||
|
if: github.ref == 'refs/heads/main'
|
||||||
|
container:
|
||||||
|
image: git.tkolb.de/amateurfunk/hamnet70/asciidoctor:1.6
|
||||||
|
steps:
|
||||||
|
- run: mkdir ~/.ssh && echo "${{ secrets.SSH_KEY }}" > ~/.ssh/id_ed25519 && chmod 0600 ~/.ssh/id_ed25519 && echo "${{ secrets.SSH_KNOWN_HOST }}" > ~/.ssh/known_hosts
|
||||||
|
- uses: actions/download-artifact@v3
|
||||||
|
with:
|
||||||
|
name: documentation
|
||||||
|
- run: 'rsync -e "ssh -p 2342" -r . deployment@tkolb.de:'
|
10
.forgejo/workflows/impl.yaml
Normal file
10
.forgejo/workflows/impl.yaml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
on: [push]
|
||||||
|
jobs:
|
||||||
|
build-hamnet70:
|
||||||
|
runs-on: docker
|
||||||
|
container:
|
||||||
|
image: git.tkolb.de/amateurfunk/hamnet70/impl_buildenv:1.1
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- run: cd impl && cp src/config.h.template src/config.h && sed -i 's/undefined/"TESTCALL"/' src/config.h
|
||||||
|
- run: cd impl && ./make.sh
|
16
ci/docker/doc/Dockerfile
Normal file
16
ci/docker/doc/Dockerfile
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
FROM debian:stable
|
||||||
|
|
||||||
|
# for basic Forgejo + AsciiDoctor support
|
||||||
|
RUN apt update && apt install -y --no-install-recommends nodejs git ruby-rubygems make ca-certificates && apt clean
|
||||||
|
|
||||||
|
RUN gem install asciidoctor asciidoctor-diagram
|
||||||
|
|
||||||
|
# tools for diagram generation
|
||||||
|
RUN apt install -y --no-install-recommends mscgen graphviz && apt clean
|
||||||
|
|
||||||
|
# tools for automatic deployment
|
||||||
|
RUN apt install -y --no-install-recommends rsync openssh-client && apt clean
|
||||||
|
|
||||||
|
# run as unprivileged user in the container
|
||||||
|
RUN useradd -m ciuser
|
||||||
|
USER ciuser
|
8
ci/docker/doc/build.sh
Executable file
8
ci/docker/doc/build.sh
Executable file
|
@ -0,0 +1,8 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
if [ -z "$1" ]; then
|
||||||
|
echo "usage: $0 <tag-version>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
docker build -t git.tkolb.de/amateurfunk/hamnet70/asciidoctor:$1 .
|
8
ci/docker/doc/test.sh
Executable file
8
ci/docker/doc/test.sh
Executable file
|
@ -0,0 +1,8 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
if [ -z "$1" ]; then
|
||||||
|
echo "usage: $0 <tag-version>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
docker run -v $(realpath ../../../doc):/doc -it git.tkolb.de/amateurfunk/hamnet70/asciidoctor:$1
|
10
ci/docker/doc/upload.sh
Executable file
10
ci/docker/doc/upload.sh
Executable file
|
@ -0,0 +1,10 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
if [ -z "$1" ]; then
|
||||||
|
echo "usage: $0 <tag-version>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
docker login
|
||||||
|
docker push git.tkolb.de/amateurfunk/hamnet70/asciidoctor:$1
|
||||||
|
docker logout
|
11
ci/docker/impl/Dockerfile
Normal file
11
ci/docker/impl/Dockerfile
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
FROM debian:stable
|
||||||
|
|
||||||
|
# for Forgejo Actions
|
||||||
|
RUN apt update && apt install -y --no-install-recommends nodejs git ca-certificates && apt clean
|
||||||
|
|
||||||
|
# Hamnet70 build dependencies
|
||||||
|
RUN apt install -y --no-install-recommends cmake make gcc libc-dev libliquid-dev libhackrf-dev libfec-dev libfftw3-dev && apt clean
|
||||||
|
|
||||||
|
# run as unprivileged user in the container
|
||||||
|
RUN useradd -m ciuser
|
||||||
|
USER ciuser
|
8
ci/docker/impl/build.sh
Executable file
8
ci/docker/impl/build.sh
Executable file
|
@ -0,0 +1,8 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
if [ -z "$1" ]; then
|
||||||
|
echo "usage: $0 <tag-version>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
docker build -t git.tkolb.de/amateurfunk/hamnet70/impl_buildenv:$1 .
|
8
ci/docker/impl/test.sh
Executable file
8
ci/docker/impl/test.sh
Executable file
|
@ -0,0 +1,8 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
if [ -z "$1" ]; then
|
||||||
|
echo "usage: $0 <tag-version>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
docker run -v $(realpath ../../../impl):/impl -it git.tkolb.de/amateurfunk/hamnet70/impl_buildenv:$1
|
10
ci/docker/impl/upload.sh
Executable file
10
ci/docker/impl/upload.sh
Executable file
|
@ -0,0 +1,10 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
if [ -z "$1" ]; then
|
||||||
|
echo "usage: $0 <tag-version>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
docker login
|
||||||
|
docker push git.tkolb.de/amateurfunk/hamnet70/impl_buildenv:$1
|
||||||
|
docker logout
|
|
@ -3,6 +3,9 @@ OUT_DIR=out
|
||||||
$(OUT_DIR)/hamnet70.html: hamnet70.adoc
|
$(OUT_DIR)/hamnet70.html: hamnet70.adoc
|
||||||
mkdir -p $(OUT_DIR)
|
mkdir -p $(OUT_DIR)
|
||||||
asciidoctor -D $(OUT_DIR) -r asciidoctor-diagram $<
|
asciidoctor -D $(OUT_DIR) -r asciidoctor-diagram $<
|
||||||
|
@# Use local files to prevent privacy issues
|
||||||
|
cp -a ext/* $(OUT_DIR)
|
||||||
|
sed -i 's!src="[^"]*mathjax/[^/]*/!src="mathjax-2.7.9/!' $@
|
||||||
|
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
clean:
|
clean:
|
||||||
|
|
19
doc/ext/mathjax-2.7.9/MathJax.js
Normal file
19
doc/ext/mathjax-2.7.9/MathJax.js
Normal file
File diff suppressed because one or more lines are too long
163
doc/ext/mathjax-2.7.9/config/TeX-MML-AM_HTMLorMML.js
Normal file
163
doc/ext/mathjax-2.7.9/config/TeX-MML-AM_HTMLorMML.js
Normal file
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
19
doc/ext/mathjax-2.7.9/jax/element/mml/optable/Arrows.js
Normal file
19
doc/ext/mathjax-2.7.9/jax/element/mml/optable/Arrows.js
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
/*
|
||||||
|
* /MathJax-v2/jax/element/mml/optable/Arrows.js
|
||||||
|
*
|
||||||
|
* Copyright (c) 2009-2018 The MathJax Consortium
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function(a){var c=a.mo.OPTYPES;var b=a.TEXCLASS;MathJax.Hub.Insert(a.mo.prototype,{OPTABLE:{infix:{"\u219A":c.RELACCENT,"\u219B":c.RELACCENT,"\u219C":c.WIDEREL,"\u219D":c.WIDEREL,"\u219E":c.WIDEREL,"\u219F":c.WIDEREL,"\u21A0":c.WIDEREL,"\u21A1":c.RELSTRETCH,"\u21A2":c.WIDEREL,"\u21A3":c.WIDEREL,"\u21A4":c.WIDEREL,"\u21A5":c.RELSTRETCH,"\u21A7":c.RELSTRETCH,"\u21A8":c.RELSTRETCH,"\u21AB":c.WIDEREL,"\u21AC":c.WIDEREL,"\u21AD":c.WIDEREL,"\u21AE":c.RELACCENT,"\u21AF":c.RELSTRETCH,"\u21B0":c.RELSTRETCH,"\u21B1":c.RELSTRETCH,"\u21B2":c.RELSTRETCH,"\u21B3":c.RELSTRETCH,"\u21B4":c.RELSTRETCH,"\u21B5":c.RELSTRETCH,"\u21B6":c.RELACCENT,"\u21B7":c.RELACCENT,"\u21B8":c.REL,"\u21B9":c.WIDEREL,"\u21BA":c.REL,"\u21BB":c.REL,"\u21BE":c.RELSTRETCH,"\u21BF":c.RELSTRETCH,"\u21C2":c.RELSTRETCH,"\u21C3":c.RELSTRETCH,"\u21C4":c.WIDEREL,"\u21C5":c.RELSTRETCH,"\u21C6":c.WIDEREL,"\u21C7":c.WIDEREL,"\u21C8":c.RELSTRETCH,"\u21C9":c.WIDEREL,"\u21CA":c.RELSTRETCH,"\u21CB":c.WIDEREL,"\u21CD":c.RELACCENT,"\u21CE":c.RELACCENT,"\u21CF":c.RELACCENT,"\u21D6":c.RELSTRETCH,"\u21D7":c.RELSTRETCH,"\u21D8":c.RELSTRETCH,"\u21D9":c.RELSTRETCH,"\u21DA":c.WIDEREL,"\u21DB":c.WIDEREL,"\u21DC":c.WIDEREL,"\u21DD":c.WIDEREL,"\u21DE":c.REL,"\u21DF":c.REL,"\u21E0":c.WIDEREL,"\u21E1":c.RELSTRETCH,"\u21E2":c.WIDEREL,"\u21E3":c.RELSTRETCH,"\u21E4":c.WIDEREL,"\u21E5":c.WIDEREL,"\u21E6":c.WIDEREL,"\u21E7":c.RELSTRETCH,"\u21E8":c.WIDEREL,"\u21E9":c.RELSTRETCH,"\u21EA":c.RELSTRETCH,"\u21EB":c.RELSTRETCH,"\u21EC":c.RELSTRETCH,"\u21ED":c.RELSTRETCH,"\u21EE":c.RELSTRETCH,"\u21EF":c.RELSTRETCH,"\u21F0":c.WIDEREL,"\u21F1":c.REL,"\u21F2":c.REL,"\u21F3":c.RELSTRETCH,"\u21F4":c.RELACCENT,"\u21F5":c.RELSTRETCH,"\u21F6":c.WIDEREL,"\u21F7":c.RELACCENT,"\u21F8":c.RELACCENT,"\u21F9":c.RELACCENT,"\u21FA":c.RELACCENT,"\u21FB":c.RELACCENT,"\u21FC":c.RELACCENT,"\u21FD":c.WIDEREL,"\u21FE":c.WIDEREL,"\u21FF":c.WIDEREL}}});MathJax.Ajax.loadComplete(a.optableDir+"/Arrows.js")})(MathJax.ElementJax.mml);
|
19
doc/ext/mathjax-2.7.9/jax/element/mml/optable/BasicLatin.js
Normal file
19
doc/ext/mathjax-2.7.9/jax/element/mml/optable/BasicLatin.js
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
/*
|
||||||
|
* /MathJax-v2/jax/element/mml/optable/BasicLatin.js
|
||||||
|
*
|
||||||
|
* Copyright (c) 2009-2018 The MathJax Consortium
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function(a){var c=a.mo.OPTYPES;var b=a.TEXCLASS;MathJax.Hub.Insert(a.mo.prototype,{OPTABLE:{prefix:{"||":[0,0,b.BIN,{fence:true,stretchy:true,symmetric:true}],"|||":[0,0,b.ORD,{fence:true,stretchy:true,symmetric:true}]},postfix:{"!!":[1,0,b.BIN],"'":c.ACCENT,"++":[0,0,b.BIN],"--":[0,0,b.BIN],"..":[0,0,b.BIN],"...":c.ORD,"||":[0,0,b.BIN,{fence:true,stretchy:true,symmetric:true}],"|||":[0,0,b.ORD,{fence:true,stretchy:true,symmetric:true}]},infix:{"!=":c.BIN4,"&&":c.BIN4,"**":[1,1,b.BIN],"*=":c.BIN4,"+=":c.BIN4,"-=":c.BIN4,"->":c.BIN5,"//":[1,1,b.BIN],"/=":c.BIN4,":=":c.BIN4,"<=":c.BIN5,"<>":[1,1,b.BIN],"==":c.BIN4,">=":c.BIN5,"@":c.ORD11,"||":[2,2,b.BIN,{fence:true,stretchy:true,symmetric:true}],"|||":[2,2,b.ORD,{fence:true,stretchy:true,symmetric:true}]}}});MathJax.Ajax.loadComplete(a.optableDir+"/BasicLatin.js")})(MathJax.ElementJax.mml);
|
|
@ -0,0 +1,19 @@
|
||||||
|
/*
|
||||||
|
* /MathJax-v2/jax/element/mml/optable/CombDiacritMarks.js
|
||||||
|
*
|
||||||
|
* Copyright (c) 2009-2018 The MathJax Consortium
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function(a){var c=a.mo.OPTYPES;var b=a.TEXCLASS;MathJax.Hub.Insert(a.mo.prototype,{OPTABLE:{postfix:{"\u0311":c.ACCENT}}});MathJax.Ajax.loadComplete(a.optableDir+"/CombDiacritMarks.js")})(MathJax.ElementJax.mml);
|
|
@ -0,0 +1,19 @@
|
||||||
|
/*
|
||||||
|
* /MathJax-v2/jax/element/mml/optable/CombDiactForSymbols.js
|
||||||
|
*
|
||||||
|
* Copyright (c) 2009-2018 The MathJax Consortium
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function(a){var c=a.mo.OPTYPES;var b=a.TEXCLASS;MathJax.Hub.Insert(a.mo.prototype,{OPTABLE:{postfix:{"\u20DB":c.ACCENT,"\u20DC":c.ACCENT}}});MathJax.Ajax.loadComplete(a.optableDir+"/CombDiactForSymbols.js")})(MathJax.ElementJax.mml);
|
19
doc/ext/mathjax-2.7.9/jax/element/mml/optable/Dingbats.js
Normal file
19
doc/ext/mathjax-2.7.9/jax/element/mml/optable/Dingbats.js
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
/*
|
||||||
|
* /MathJax-v2/jax/element/mml/optable/Dingbats.js
|
||||||
|
*
|
||||||
|
* Copyright (c) 2009-2018 The MathJax Consortium
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function(a){var c=a.mo.OPTYPES;var b=a.TEXCLASS;MathJax.Hub.Insert(a.mo.prototype,{OPTABLE:{prefix:{"\u2772":c.OPEN},postfix:{"\u2773":c.CLOSE}}});MathJax.Ajax.loadComplete(a.optableDir+"/Dingbats.js")})(MathJax.ElementJax.mml);
|
|
@ -0,0 +1,19 @@
|
||||||
|
/*
|
||||||
|
* /MathJax-v2/jax/element/mml/optable/GeneralPunctuation.js
|
||||||
|
*
|
||||||
|
* Copyright (c) 2009-2018 The MathJax Consortium
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function(a){var c=a.mo.OPTYPES;var b=a.TEXCLASS;MathJax.Hub.Insert(a.mo.prototype,{OPTABLE:{prefix:{"\u2016":[0,0,b.ORD,{fence:true,stretchy:true}],"\u2018":[0,0,b.OPEN,{fence:true}],"\u201C":[0,0,b.OPEN,{fence:true}]},postfix:{"\u2016":[0,0,b.ORD,{fence:true,stretchy:true}],"\u2019":[0,0,b.CLOSE,{fence:true}],"\u201D":[0,0,b.CLOSE,{fence:true}]}}});MathJax.Ajax.loadComplete(a.optableDir+"/GeneralPunctuation.js")})(MathJax.ElementJax.mml);
|
|
@ -0,0 +1,19 @@
|
||||||
|
/*
|
||||||
|
* /MathJax-v2/jax/element/mml/optable/GeometricShapes.js
|
||||||
|
*
|
||||||
|
* Copyright (c) 2009-2018 The MathJax Consortium
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function(a){var c=a.mo.OPTYPES;var b=a.TEXCLASS;MathJax.Hub.Insert(a.mo.prototype,{OPTABLE:{infix:{"\u25A0":c.BIN3,"\u25A1":c.BIN3,"\u25AA":c.BIN3,"\u25AB":c.BIN3,"\u25AD":c.BIN3,"\u25AE":c.BIN3,"\u25AF":c.BIN3,"\u25B0":c.BIN3,"\u25B1":c.BIN3,"\u25B2":c.BIN4,"\u25B4":c.BIN4,"\u25B6":c.BIN4,"\u25B7":c.BIN4,"\u25B8":c.BIN4,"\u25BC":c.BIN4,"\u25BE":c.BIN4,"\u25C0":c.BIN4,"\u25C1":c.BIN4,"\u25C2":c.BIN4,"\u25C4":c.BIN4,"\u25C5":c.BIN4,"\u25C6":c.BIN4,"\u25C7":c.BIN4,"\u25C8":c.BIN4,"\u25C9":c.BIN4,"\u25CC":c.BIN4,"\u25CD":c.BIN4,"\u25CE":c.BIN4,"\u25CF":c.BIN4,"\u25D6":c.BIN4,"\u25D7":c.BIN4,"\u25E6":c.BIN4}}});MathJax.Ajax.loadComplete(a.optableDir+"/GeometricShapes.js")})(MathJax.ElementJax.mml);
|
|
@ -0,0 +1,19 @@
|
||||||
|
/*
|
||||||
|
* /MathJax-v2/jax/element/mml/optable/GreekAndCoptic.js
|
||||||
|
*
|
||||||
|
* Copyright (c) 2009-2018 The MathJax Consortium
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function(a){var c=a.mo.OPTYPES;var b=a.TEXCLASS;MathJax.Hub.Insert(a.mo.prototype,{OPTABLE:{infix:{"\u03F6":c.REL}}});MathJax.Ajax.loadComplete(a.optableDir+"/GreekAndCoptic.js")})(MathJax.ElementJax.mml);
|
|
@ -0,0 +1,19 @@
|
||||||
|
/*
|
||||||
|
* /MathJax-v2/jax/element/mml/optable/Latin1Supplement.js
|
||||||
|
*
|
||||||
|
* Copyright (c) 2009-2018 The MathJax Consortium
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function(a){var c=a.mo.OPTYPES;var b=a.TEXCLASS;MathJax.Hub.Insert(a.mo.prototype,{OPTABLE:{postfix:{"\u00B0":c.ORD,"\u00B4":c.ACCENT,"\u00B8":c.ACCENT}}});MathJax.Ajax.loadComplete(a.optableDir+"/Latin1Supplement.js")})(MathJax.ElementJax.mml);
|
|
@ -0,0 +1,19 @@
|
||||||
|
/*
|
||||||
|
* /MathJax-v2/jax/element/mml/optable/LetterlikeSymbols.js
|
||||||
|
*
|
||||||
|
* Copyright (c) 2009-2018 The MathJax Consortium
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function(a){var c=a.mo.OPTYPES;var b=a.TEXCLASS;MathJax.Hub.Insert(a.mo.prototype,{OPTABLE:{prefix:{"\u2145":c.ORD21,"\u2146":[2,0,b.ORD]}}});MathJax.Ajax.loadComplete(a.optableDir+"/LetterlikeSymbols.js")})(MathJax.ElementJax.mml);
|
|
@ -0,0 +1,19 @@
|
||||||
|
/*
|
||||||
|
* /MathJax-v2/jax/element/mml/optable/MathOperators.js
|
||||||
|
*
|
||||||
|
* Copyright (c) 2009-2018 The MathJax Consortium
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function(a){var c=a.mo.OPTYPES;var b=a.TEXCLASS;MathJax.Hub.Insert(a.mo.prototype,{OPTABLE:{prefix:{"\u2204":c.ORD21,"\u221B":c.ORD11,"\u221C":c.ORD11,"\u2221":c.ORD,"\u2222":c.ORD,"\u222C":c.INTEGRAL,"\u222D":c.INTEGRAL,"\u222F":c.INTEGRAL,"\u2230":c.INTEGRAL,"\u2231":c.INTEGRAL,"\u2232":c.INTEGRAL,"\u2233":c.INTEGRAL},infix:{"\u2201":[1,2,b.ORD],"\u2206":c.BIN3,"\u220A":c.REL,"\u220C":c.REL,"\u220D":c.REL,"\u220E":c.BIN3,"\u2214":c.BIN4,"\u221F":c.REL,"\u2224":c.REL,"\u2226":c.REL,"\u2234":c.REL,"\u2235":c.REL,"\u2236":c.REL,"\u2237":c.REL,"\u2238":c.BIN4,"\u2239":c.REL,"\u223A":c.BIN4,"\u223B":c.REL,"\u223D":c.REL,"\u223D\u0331":c.BIN3,"\u223E":c.REL,"\u223F":c.BIN3,"\u2241":c.REL,"\u2242":c.REL,"\u2242\u0338":c.REL,"\u2244":c.REL,"\u2246":c.REL,"\u2247":c.REL,"\u2249":c.REL,"\u224A":c.REL,"\u224B":c.REL,"\u224C":c.REL,"\u224E":c.REL,"\u224E\u0338":c.REL,"\u224F":c.REL,"\u224F\u0338":c.REL,"\u2251":c.REL,"\u2252":c.REL,"\u2253":c.REL,"\u2254":c.REL,"\u2255":c.REL,"\u2256":c.REL,"\u2257":c.REL,"\u2258":c.REL,"\u2259":c.REL,"\u225A":c.REL,"\u225C":c.REL,"\u225D":c.REL,"\u225E":c.REL,"\u225F":c.REL,"\u2262":c.REL,"\u2263":c.REL,"\u2266":c.REL,"\u2266\u0338":c.REL,"\u2267":c.REL,"\u2268":c.REL,"\u2269":c.REL,"\u226A\u0338":c.REL,"\u226B\u0338":c.REL,"\u226C":c.REL,"\u226D":c.REL,"\u226E":c.REL,"\u226F":c.REL,"\u2270":c.REL,"\u2271":c.REL,"\u2272":c.REL,"\u2273":c.REL,"\u2274":c.REL,"\u2275":c.REL,"\u2276":c.REL,"\u2277":c.REL,"\u2278":c.REL,"\u2279":c.REL,"\u227C":c.REL,"\u227D":c.REL,"\u227E":c.REL,"\u227F":c.REL,"\u227F\u0338":c.REL,"\u2280":c.REL,"\u2281":c.REL,"\u2282\u20D2":c.REL,"\u2283\u20D2":c.REL,"\u2284":c.REL,"\u2285":c.REL,"\u2288":c.REL,"\u2289":c.REL,"\u228A":c.REL,"\u228B":c.REL,"\u228C":c.BIN4,"\u228D":c.BIN4,"\u228F":c.REL,"\u228F\u0338":c.REL,"\u2290":c.REL,"\u2290\u0338":c.REL,"\u229A":c.BIN4,"\u229B":c.BIN4,"\u229C":c.BIN4,"\u229D":c.BIN4,"\u229E":c.BIN4,"\u229F":c.BIN4,"\u22A0":c.BIN4,"\u22A1":c.BIN4,"\u22A6":c.REL,"\u22A7":c.REL,"\u22A9":c.REL,"\u22AA":c.REL,"\u22AB":c.REL,"\u22AC":c.REL,"\u22AD":c.REL,"\u22AE":c.REL,"\u22AF":c.REL,"\u22B0":c.REL,"\u22B1":c.REL,"\u22B2":c.REL,"\u22B3":c.REL,"\u22B4":c.REL,"\u22B5":c.REL,"\u22B6":c.REL,"\u22B7":c.REL,"\u22B8":c.REL,"\u22B9":c.REL,"\u22BA":c.BIN4,"\u22BB":c.BIN4,"\u22BC":c.BIN4,"\u22BD":c.BIN4,"\u22BE":c.BIN3,"\u22BF":c.BIN3,"\u22C7":c.BIN4,"\u22C9":c.BIN4,"\u22CA":c.BIN4,"\u22CB":c.BIN4,"\u22CC":c.BIN4,"\u22CD":c.REL,"\u22CE":c.BIN4,"\u22CF":c.BIN4,"\u22D0":c.REL,"\u22D1":c.REL,"\u22D2":c.BIN4,"\u22D3":c.BIN4,"\u22D4":c.REL,"\u22D5":c.REL,"\u22D6":c.REL,"\u22D7":c.REL,"\u22D8":c.REL,"\u22D9":c.REL,"\u22DA":c.REL,"\u22DB":c.REL,"\u22DC":c.REL,"\u22DD":c.REL,"\u22DE":c.REL,"\u22DF":c.REL,"\u22E0":c.REL,"\u22E1":c.REL,"\u22E2":c.REL,"\u22E3":c.REL,"\u22E4":c.REL,"\u22E5":c.REL,"\u22E6":c.REL,"\u22E7":c.REL,"\u22E8":c.REL,"\u22E9":c.REL,"\u22EA":c.REL,"\u22EB":c.REL,"\u22EC":c.REL,"\u22ED":c.REL,"\u22F0":c.REL,"\u22F2":c.REL,"\u22F3":c.REL,"\u22F4":c.REL,"\u22F5":c.REL,"\u22F6":c.REL,"\u22F7":c.REL,"\u22F8":c.REL,"\u22F9":c.REL,"\u22FA":c.REL,"\u22FB":c.REL,"\u22FC":c.REL,"\u22FD":c.REL,"\u22FE":c.REL,"\u22FF":c.REL}}});MathJax.Ajax.loadComplete(a.optableDir+"/MathOperators.js")})(MathJax.ElementJax.mml);
|
|
@ -0,0 +1,19 @@
|
||||||
|
/*
|
||||||
|
* /MathJax-v2/jax/element/mml/optable/MiscMathSymbolsA.js
|
||||||
|
*
|
||||||
|
* Copyright (c) 2009-2018 The MathJax Consortium
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function(a){var c=a.mo.OPTYPES;var b=a.TEXCLASS;MathJax.Hub.Insert(a.mo.prototype,{OPTABLE:{prefix:{"\u27E6":c.OPEN,"\u27EA":c.OPEN,"\u27EC":c.OPEN},postfix:{"\u27E7":c.CLOSE,"\u27EB":c.CLOSE,"\u27ED":c.CLOSE}}});MathJax.Ajax.loadComplete(a.optableDir+"/MiscMathSymbolsA.js")})(MathJax.ElementJax.mml);
|
|
@ -0,0 +1,19 @@
|
||||||
|
/*
|
||||||
|
* /MathJax-v2/jax/element/mml/optable/MiscMathSymbolsB.js
|
||||||
|
*
|
||||||
|
* Copyright (c) 2009-2018 The MathJax Consortium
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function(a){var c=a.mo.OPTYPES;var b=a.TEXCLASS;MathJax.Hub.Insert(a.mo.prototype,{OPTABLE:{prefix:{"\u2980":[0,0,b.ORD,{fence:true,stretchy:true}],"\u2983":c.OPEN,"\u2985":c.OPEN,"\u2987":c.OPEN,"\u2989":c.OPEN,"\u298B":c.OPEN,"\u298D":c.OPEN,"\u298F":c.OPEN,"\u2991":c.OPEN,"\u2993":c.OPEN,"\u2995":c.OPEN,"\u2997":c.OPEN,"\u29FC":c.OPEN},postfix:{"\u2980":[0,0,b.ORD,{fence:true,stretchy:true}],"\u2984":c.CLOSE,"\u2986":c.CLOSE,"\u2988":c.CLOSE,"\u298A":c.CLOSE,"\u298C":c.CLOSE,"\u298E":c.CLOSE,"\u2990":c.CLOSE,"\u2992":c.CLOSE,"\u2994":c.CLOSE,"\u2996":c.CLOSE,"\u2998":c.CLOSE,"\u29FD":c.CLOSE},infix:{"\u2981":c.BIN3,"\u2982":c.BIN3,"\u2999":c.BIN3,"\u299A":c.BIN3,"\u299B":c.BIN3,"\u299C":c.BIN3,"\u299D":c.BIN3,"\u299E":c.BIN3,"\u299F":c.BIN3,"\u29A0":c.BIN3,"\u29A1":c.BIN3,"\u29A2":c.BIN3,"\u29A3":c.BIN3,"\u29A4":c.BIN3,"\u29A5":c.BIN3,"\u29A6":c.BIN3,"\u29A7":c.BIN3,"\u29A8":c.BIN3,"\u29A9":c.BIN3,"\u29AA":c.BIN3,"\u29AB":c.BIN3,"\u29AC":c.BIN3,"\u29AD":c.BIN3,"\u29AE":c.BIN3,"\u29AF":c.BIN3,"\u29B0":c.BIN3,"\u29B1":c.BIN3,"\u29B2":c.BIN3,"\u29B3":c.BIN3,"\u29B4":c.BIN3,"\u29B5":c.BIN3,"\u29B6":c.BIN4,"\u29B7":c.BIN4,"\u29B8":c.BIN4,"\u29B9":c.BIN4,"\u29BA":c.BIN4,"\u29BB":c.BIN4,"\u29BC":c.BIN4,"\u29BD":c.BIN4,"\u29BE":c.BIN4,"\u29BF":c.BIN4,"\u29C0":c.REL,"\u29C1":c.REL,"\u29C2":c.BIN3,"\u29C3":c.BIN3,"\u29C4":c.BIN4,"\u29C5":c.BIN4,"\u29C6":c.BIN4,"\u29C7":c.BIN4,"\u29C8":c.BIN4,"\u29C9":c.BIN3,"\u29CA":c.BIN3,"\u29CB":c.BIN3,"\u29CC":c.BIN3,"\u29CD":c.BIN3,"\u29CE":c.REL,"\u29CF":c.REL,"\u29CF\u0338":c.REL,"\u29D0":c.REL,"\u29D0\u0338":c.REL,"\u29D1":c.REL,"\u29D2":c.REL,"\u29D3":c.REL,"\u29D4":c.REL,"\u29D5":c.REL,"\u29D6":c.BIN4,"\u29D7":c.BIN4,"\u29D8":c.BIN3,"\u29D9":c.BIN3,"\u29DB":c.BIN3,"\u29DC":c.BIN3,"\u29DD":c.BIN3,"\u29DE":c.REL,"\u29DF":c.BIN3,"\u29E0":c.BIN3,"\u29E1":c.REL,"\u29E2":c.BIN4,"\u29E3":c.REL,"\u29E4":c.REL,"\u29E5":c.REL,"\u29E6":c.REL,"\u29E7":c.BIN3,"\u29E8":c.BIN3,"\u29E9":c.BIN3,"\u29EA":c.BIN3,"\u29EB":c.BIN3,"\u29EC":c.BIN3,"\u29ED":c.BIN3,"\u29EE":c.BIN3,"\u29EF":c.BIN3,"\u29F0":c.BIN3,"\u29F1":c.BIN3,"\u29F2":c.BIN3,"\u29F3":c.BIN3,"\u29F4":c.REL,"\u29F5":c.BIN4,"\u29F6":c.BIN4,"\u29F7":c.BIN4,"\u29F8":c.BIN3,"\u29F9":c.BIN3,"\u29FA":c.BIN3,"\u29FB":c.BIN3,"\u29FE":c.BIN4,"\u29FF":c.BIN4}}});MathJax.Ajax.loadComplete(a.optableDir+"/MiscMathSymbolsB.js")})(MathJax.ElementJax.mml);
|
|
@ -0,0 +1,19 @@
|
||||||
|
/*
|
||||||
|
* /MathJax-v2/jax/element/mml/optable/MiscSymbolsAndArrows.js
|
||||||
|
*
|
||||||
|
* Copyright (c) 2009-2018 The MathJax Consortium
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function(a){var c=a.mo.OPTYPES;var b=a.TEXCLASS;MathJax.Hub.Insert(a.mo.prototype,{OPTABLE:{infix:{"\u2B45":c.RELSTRETCH,"\u2B46":c.RELSTRETCH}}});MathJax.Ajax.loadComplete(a.optableDir+"/MiscSymbolsAndArrows.js")})(MathJax.ElementJax.mml);
|
|
@ -0,0 +1,19 @@
|
||||||
|
/*
|
||||||
|
* /MathJax-v2/jax/element/mml/optable/MiscTechnical.js
|
||||||
|
*
|
||||||
|
* Copyright (c) 2009-2018 The MathJax Consortium
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function(a){var c=a.mo.OPTYPES;var b=a.TEXCLASS;MathJax.Hub.Insert(a.mo.prototype,{OPTABLE:{postfix:{"\u23B4":c.WIDEACCENT,"\u23B5":c.WIDEACCENT,"\u23DC":c.WIDEACCENT,"\u23DD":c.WIDEACCENT,"\u23E0":c.WIDEACCENT,"\u23E1":c.WIDEACCENT}}});MathJax.Ajax.loadComplete(a.optableDir+"/MiscTechnical.js")})(MathJax.ElementJax.mml);
|
|
@ -0,0 +1,19 @@
|
||||||
|
/*
|
||||||
|
* /MathJax-v2/jax/element/mml/optable/SpacingModLetters.js
|
||||||
|
*
|
||||||
|
* Copyright (c) 2009-2018 The MathJax Consortium
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function(a){var c=a.mo.OPTYPES;var b=a.TEXCLASS;MathJax.Hub.Insert(a.mo.prototype,{OPTABLE:{postfix:{"\u02CD":c.WIDEACCENT,"\u02DA":c.ACCENT,"\u02DD":c.ACCENT,"\u02F7":c.WIDEACCENT}}});MathJax.Ajax.loadComplete(a.optableDir+"/SpacingModLetters.js")})(MathJax.ElementJax.mml);
|
|
@ -0,0 +1,19 @@
|
||||||
|
/*
|
||||||
|
* /MathJax-v2/jax/element/mml/optable/SuppMathOperators.js
|
||||||
|
*
|
||||||
|
* Copyright (c) 2009-2018 The MathJax Consortium
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function(a){var c=a.mo.OPTYPES;var b=a.TEXCLASS;MathJax.Hub.Insert(a.mo.prototype,{OPTABLE:{prefix:{"\u2A03":c.OP,"\u2A05":c.OP,"\u2A07":c.OP,"\u2A08":c.OP,"\u2A09":c.OP,"\u2A0A":c.OP,"\u2A0B":c.INTEGRAL2,"\u2A0C":c.INTEGRAL,"\u2A0D":c.INTEGRAL2,"\u2A0E":c.INTEGRAL2,"\u2A0F":c.INTEGRAL2,"\u2A10":c.OP,"\u2A11":c.OP,"\u2A12":c.OP,"\u2A13":c.OP,"\u2A14":c.OP,"\u2A15":c.INTEGRAL2,"\u2A16":c.INTEGRAL2,"\u2A17":c.INTEGRAL2,"\u2A18":c.INTEGRAL2,"\u2A19":c.INTEGRAL2,"\u2A1A":c.INTEGRAL2,"\u2A1B":c.INTEGRAL2,"\u2A1C":c.INTEGRAL2,"\u2AFC":c.OP,"\u2AFF":c.OP},infix:{"\u2A1D":c.BIN3,"\u2A1E":c.BIN3,"\u2A1F":c.BIN3,"\u2A20":c.BIN3,"\u2A21":c.BIN3,"\u2A22":c.BIN4,"\u2A23":c.BIN4,"\u2A24":c.BIN4,"\u2A25":c.BIN4,"\u2A26":c.BIN4,"\u2A27":c.BIN4,"\u2A28":c.BIN4,"\u2A29":c.BIN4,"\u2A2A":c.BIN4,"\u2A2B":c.BIN4,"\u2A2C":c.BIN4,"\u2A2D":c.BIN4,"\u2A2E":c.BIN4,"\u2A30":c.BIN4,"\u2A31":c.BIN4,"\u2A32":c.BIN4,"\u2A33":c.BIN4,"\u2A34":c.BIN4,"\u2A35":c.BIN4,"\u2A36":c.BIN4,"\u2A37":c.BIN4,"\u2A38":c.BIN4,"\u2A39":c.BIN4,"\u2A3A":c.BIN4,"\u2A3B":c.BIN4,"\u2A3C":c.BIN4,"\u2A3D":c.BIN4,"\u2A3E":c.BIN4,"\u2A40":c.BIN4,"\u2A41":c.BIN4,"\u2A42":c.BIN4,"\u2A43":c.BIN4,"\u2A44":c.BIN4,"\u2A45":c.BIN4,"\u2A46":c.BIN4,"\u2A47":c.BIN4,"\u2A48":c.BIN4,"\u2A49":c.BIN4,"\u2A4A":c.BIN4,"\u2A4B":c.BIN4,"\u2A4C":c.BIN4,"\u2A4D":c.BIN4,"\u2A4E":c.BIN4,"\u2A4F":c.BIN4,"\u2A50":c.BIN4,"\u2A51":c.BIN4,"\u2A52":c.BIN4,"\u2A53":c.BIN4,"\u2A54":c.BIN4,"\u2A55":c.BIN4,"\u2A56":c.BIN4,"\u2A57":c.BIN4,"\u2A58":c.BIN4,"\u2A59":c.REL,"\u2A5A":c.BIN4,"\u2A5B":c.BIN4,"\u2A5C":c.BIN4,"\u2A5D":c.BIN4,"\u2A5E":c.BIN4,"\u2A5F":c.BIN4,"\u2A60":c.BIN4,"\u2A61":c.BIN4,"\u2A62":c.BIN4,"\u2A63":c.BIN4,"\u2A64":c.BIN4,"\u2A65":c.BIN4,"\u2A66":c.REL,"\u2A67":c.REL,"\u2A68":c.REL,"\u2A69":c.REL,"\u2A6A":c.REL,"\u2A6B":c.REL,"\u2A6C":c.REL,"\u2A6D":c.REL,"\u2A6E":c.REL,"\u2A6F":c.REL,"\u2A70":c.REL,"\u2A71":c.BIN4,"\u2A72":c.BIN4,"\u2A73":c.REL,"\u2A74":c.REL,"\u2A75":c.REL,"\u2A76":c.REL,"\u2A77":c.REL,"\u2A78":c.REL,"\u2A79":c.REL,"\u2A7A":c.REL,"\u2A7B":c.REL,"\u2A7C":c.REL,"\u2A7D":c.REL,"\u2A7D\u0338":c.REL,"\u2A7E":c.REL,"\u2A7E\u0338":c.REL,"\u2A7F":c.REL,"\u2A80":c.REL,"\u2A81":c.REL,"\u2A82":c.REL,"\u2A83":c.REL,"\u2A84":c.REL,"\u2A85":c.REL,"\u2A86":c.REL,"\u2A87":c.REL,"\u2A88":c.REL,"\u2A89":c.REL,"\u2A8A":c.REL,"\u2A8B":c.REL,"\u2A8C":c.REL,"\u2A8D":c.REL,"\u2A8E":c.REL,"\u2A8F":c.REL,"\u2A90":c.REL,"\u2A91":c.REL,"\u2A92":c.REL,"\u2A93":c.REL,"\u2A94":c.REL,"\u2A95":c.REL,"\u2A96":c.REL,"\u2A97":c.REL,"\u2A98":c.REL,"\u2A99":c.REL,"\u2A9A":c.REL,"\u2A9B":c.REL,"\u2A9C":c.REL,"\u2A9D":c.REL,"\u2A9E":c.REL,"\u2A9F":c.REL,"\u2AA0":c.REL,"\u2AA1":c.REL,"\u2AA1\u0338":c.REL,"\u2AA2":c.REL,"\u2AA2\u0338":c.REL,"\u2AA3":c.REL,"\u2AA4":c.REL,"\u2AA5":c.REL,"\u2AA6":c.REL,"\u2AA7":c.REL,"\u2AA8":c.REL,"\u2AA9":c.REL,"\u2AAA":c.REL,"\u2AAB":c.REL,"\u2AAC":c.REL,"\u2AAD":c.REL,"\u2AAE":c.REL,"\u2AAF\u0338":c.REL,"\u2AB0\u0338":c.REL,"\u2AB1":c.REL,"\u2AB2":c.REL,"\u2AB3":c.REL,"\u2AB4":c.REL,"\u2AB5":c.REL,"\u2AB6":c.REL,"\u2AB7":c.REL,"\u2AB8":c.REL,"\u2AB9":c.REL,"\u2ABA":c.REL,"\u2ABB":c.REL,"\u2ABC":c.REL,"\u2ABD":c.REL,"\u2ABE":c.REL,"\u2ABF":c.REL,"\u2AC0":c.REL,"\u2AC1":c.REL,"\u2AC2":c.REL,"\u2AC3":c.REL,"\u2AC4":c.REL,"\u2AC5":c.REL,"\u2AC6":c.REL,"\u2AC7":c.REL,"\u2AC8":c.REL,"\u2AC9":c.REL,"\u2ACA":c.REL,"\u2ACB":c.REL,"\u2ACC":c.REL,"\u2ACD":c.REL,"\u2ACE":c.REL,"\u2ACF":c.REL,"\u2AD0":c.REL,"\u2AD1":c.REL,"\u2AD2":c.REL,"\u2AD3":c.REL,"\u2AD4":c.REL,"\u2AD5":c.REL,"\u2AD6":c.REL,"\u2AD7":c.REL,"\u2AD8":c.REL,"\u2AD9":c.REL,"\u2ADA":c.REL,"\u2ADB":c.REL,"\u2ADC":c.REL,"\u2ADD":c.REL,"\u2ADE":c.REL,"\u2ADF":c.REL,"\u2AE0":c.REL,"\u2AE1":c.REL,"\u2AE2":c.REL,"\u2AE3":c.REL,"\u2AE4":c.REL,"\u2AE5":c.REL,"\u2AE6":c.REL,"\u2AE7":c.REL,"\u2AE8":c.REL,"\u2AE9":c.REL,"\u2AEA":c.REL,"\u2AEB":c.REL,"\u2AEC":c.REL,"\u2AED":c.REL,"\u2AEE":c.REL,"\u2AEF":c.REL,"\u2AF0":c.REL,"\u2AF1":c.REL,"\u2AF2":c.REL,"\u2AF3":c.REL,"\u2AF4":c.BIN4,"\u2AF5":c.BIN4,"\u2AF6":c.BIN4,"\u2AF7":c.REL,"\u2AF8":c.REL,"\u2AF9":c.REL,"\u2AFA":c.REL,"\u2AFB":c.BIN4,"\u2AFD":c.BIN4,"\u2AFE":c.BIN3}}});MathJax.Ajax.loadComplete(a.optableDir+"/SuppMathOperators.js")})(MathJax.ElementJax.mml);
|
|
@ -0,0 +1,19 @@
|
||||||
|
/*
|
||||||
|
* /MathJax-v2/jax/element/mml/optable/SupplementalArrowsA.js
|
||||||
|
*
|
||||||
|
* Copyright (c) 2009-2018 The MathJax Consortium
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function(a){var c=a.mo.OPTYPES;var b=a.TEXCLASS;MathJax.Hub.Insert(a.mo.prototype,{OPTABLE:{infix:{"\u27F0":c.RELSTRETCH,"\u27F1":c.RELSTRETCH,"\u27FB":c.WIDEREL,"\u27FD":c.WIDEREL,"\u27FE":c.WIDEREL,"\u27FF":c.WIDEREL}}});MathJax.Ajax.loadComplete(a.optableDir+"/SupplementalArrowsA.js")})(MathJax.ElementJax.mml);
|
|
@ -0,0 +1,19 @@
|
||||||
|
/*
|
||||||
|
* /MathJax-v2/jax/element/mml/optable/SupplementalArrowsB.js
|
||||||
|
*
|
||||||
|
* Copyright (c) 2009-2018 The MathJax Consortium
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function(a){var c=a.mo.OPTYPES;var b=a.TEXCLASS;MathJax.Hub.Insert(a.mo.prototype,{OPTABLE:{infix:{"\u2900":c.RELACCENT,"\u2901":c.RELACCENT,"\u2902":c.RELACCENT,"\u2903":c.RELACCENT,"\u2904":c.RELACCENT,"\u2905":c.RELACCENT,"\u2906":c.RELACCENT,"\u2907":c.RELACCENT,"\u2908":c.REL,"\u2909":c.REL,"\u290A":c.RELSTRETCH,"\u290B":c.RELSTRETCH,"\u290C":c.WIDEREL,"\u290D":c.WIDEREL,"\u290E":c.WIDEREL,"\u290F":c.WIDEREL,"\u2910":c.WIDEREL,"\u2911":c.RELACCENT,"\u2912":c.RELSTRETCH,"\u2913":c.RELSTRETCH,"\u2914":c.RELACCENT,"\u2915":c.RELACCENT,"\u2916":c.RELACCENT,"\u2917":c.RELACCENT,"\u2918":c.RELACCENT,"\u2919":c.RELACCENT,"\u291A":c.RELACCENT,"\u291B":c.RELACCENT,"\u291C":c.RELACCENT,"\u291D":c.RELACCENT,"\u291E":c.RELACCENT,"\u291F":c.RELACCENT,"\u2920":c.RELACCENT,"\u2921":c.RELSTRETCH,"\u2922":c.RELSTRETCH,"\u2923":c.REL,"\u2924":c.REL,"\u2925":c.REL,"\u2926":c.REL,"\u2927":c.REL,"\u2928":c.REL,"\u2929":c.REL,"\u292A":c.REL,"\u292B":c.REL,"\u292C":c.REL,"\u292D":c.REL,"\u292E":c.REL,"\u292F":c.REL,"\u2930":c.REL,"\u2931":c.REL,"\u2932":c.REL,"\u2933":c.RELACCENT,"\u2934":c.REL,"\u2935":c.REL,"\u2936":c.REL,"\u2937":c.REL,"\u2938":c.REL,"\u2939":c.REL,"\u293A":c.RELACCENT,"\u293B":c.RELACCENT,"\u293C":c.RELACCENT,"\u293D":c.RELACCENT,"\u293E":c.REL,"\u293F":c.REL,"\u2940":c.REL,"\u2941":c.REL,"\u2942":c.RELACCENT,"\u2943":c.RELACCENT,"\u2944":c.RELACCENT,"\u2945":c.RELACCENT,"\u2946":c.RELACCENT,"\u2947":c.RELACCENT,"\u2948":c.RELACCENT,"\u2949":c.REL,"\u294A":c.RELACCENT,"\u294B":c.RELACCENT,"\u294C":c.REL,"\u294D":c.REL,"\u294E":c.WIDEREL,"\u294F":c.RELSTRETCH,"\u2950":c.WIDEREL,"\u2951":c.RELSTRETCH,"\u2952":c.WIDEREL,"\u2953":c.WIDEREL,"\u2954":c.RELSTRETCH,"\u2955":c.RELSTRETCH,"\u2956":c.RELSTRETCH,"\u2957":c.RELSTRETCH,"\u2958":c.RELSTRETCH,"\u2959":c.RELSTRETCH,"\u295A":c.WIDEREL,"\u295B":c.WIDEREL,"\u295C":c.RELSTRETCH,"\u295D":c.RELSTRETCH,"\u295E":c.WIDEREL,"\u295F":c.WIDEREL,"\u2960":c.RELSTRETCH,"\u2961":c.RELSTRETCH,"\u2962":c.RELACCENT,"\u2963":c.REL,"\u2964":c.RELACCENT,"\u2965":c.REL,"\u2966":c.RELACCENT,"\u2967":c.RELACCENT,"\u2968":c.RELACCENT,"\u2969":c.RELACCENT,"\u296A":c.RELACCENT,"\u296B":c.RELACCENT,"\u296C":c.RELACCENT,"\u296D":c.RELACCENT,"\u296E":c.RELSTRETCH,"\u296F":c.RELSTRETCH,"\u2970":c.RELACCENT,"\u2971":c.RELACCENT,"\u2972":c.RELACCENT,"\u2973":c.RELACCENT,"\u2974":c.RELACCENT,"\u2975":c.RELACCENT,"\u2976":c.RELACCENT,"\u2977":c.RELACCENT,"\u2978":c.RELACCENT,"\u2979":c.RELACCENT,"\u297A":c.RELACCENT,"\u297B":c.RELACCENT,"\u297C":c.RELACCENT,"\u297D":c.RELACCENT,"\u297E":c.REL,"\u297F":c.REL}}});MathJax.Ajax.loadComplete(a.optableDir+"/SupplementalArrowsB.js")})(MathJax.ElementJax.mml);
|
File diff suppressed because one or more lines are too long
19
doc/ext/mathjax-2.7.9/jax/output/HTML-CSS/jax.js
Normal file
19
doc/ext/mathjax-2.7.9/jax/output/HTML-CSS/jax.js
Normal file
File diff suppressed because one or more lines are too long
|
@ -3,6 +3,7 @@ Thomas Kolb DL5TKL
|
||||||
0.1, 2024-04-25
|
0.1, 2024-04-25
|
||||||
:toc:
|
:toc:
|
||||||
:stem:
|
:stem:
|
||||||
|
:webfonts!:
|
||||||
|
|
||||||
// SPDX-License-Identifier: CC-BY-SA-4.0
|
// SPDX-License-Identifier: CC-BY-SA-4.0
|
||||||
// List of contributors is at the end of the document.
|
// List of contributors is at the end of the document.
|
||||||
|
@ -253,6 +254,157 @@ They therefore do not have a _TX sequence number_; this field is reserved and mu
|
||||||
|
|
||||||
The _RX sequence number_ and all other header fields are used as usual.
|
The _RX sequence number_ and all other header fields are used as usual.
|
||||||
|
|
||||||
|
==== Connection Management
|
||||||
|
|
||||||
|
Connection Management Frames control the Layer 2 connection between a client and the digipeater.
|
||||||
|
They are especially important for connect and disconnect procedures.
|
||||||
|
|
||||||
|
Connection Management Frames have at least one byte in the data field: the type of the management packet.
|
||||||
|
The following types are defined:
|
||||||
|
|
||||||
|
[cols="1,2,5", options="header"]
|
||||||
|
.Connection Management Types
|
||||||
|
|===
|
||||||
|
|Type Indicator
|
||||||
|
|Sent by
|
||||||
|
|Description
|
||||||
|
|
||||||
|
|`0x00`
|
||||||
|
|Digipeater
|
||||||
|
|Beacon
|
||||||
|
|
||||||
|
|`0x01`
|
||||||
|
|Client
|
||||||
|
|Connection Request
|
||||||
|
|
||||||
|
|`0x02`
|
||||||
|
|Digipeater
|
||||||
|
|Connection Parameters
|
||||||
|
|
||||||
|
|`0x03`
|
||||||
|
|Digipeater
|
||||||
|
|Connection Reset
|
||||||
|
|
||||||
|
|`0x04`
|
||||||
|
|Digipeater
|
||||||
|
|Disconnect Request
|
||||||
|
|
||||||
|
|`0x05`
|
||||||
|
|Client
|
||||||
|
|Disconnect
|
||||||
|
|===
|
||||||
|
|
||||||
|
===== Beacons
|
||||||
|
|
||||||
|
Beacons are sent periodically by the digipeater to show it’s presence to potential new clients.
|
||||||
|
|
||||||
|
The layer 2 header is filled as follows:
|
||||||
|
|
||||||
|
- Message Type: `001` (Connection Management)
|
||||||
|
- TX Request: `1`
|
||||||
|
- Source Address: the digipeater’s HAM-64 address
|
||||||
|
- Destination Address: the HAM-64 broadcast address
|
||||||
|
- TX sequence number: reserved, always 0
|
||||||
|
- RX sequence number: reserved, always 0
|
||||||
|
|
||||||
|
The message contains exactly 1 data byte: `0x00` to indicate that this is a Beacon packet.
|
||||||
|
|
||||||
|
A Beacon packet shall always be sent as the last or only packet in a burst.
|
||||||
|
After a Beacon is sent by the digipeater it listens for a short time for Connection Requests from new clients.
|
||||||
|
A new client that intends to connect shall send the Connection Request as soon as possible after the beacon transmission ends.
|
||||||
|
|
||||||
|
===== Connection Request
|
||||||
|
|
||||||
|
A Connection Request is sent by a client that intends to connect to a digipeater.
|
||||||
|
|
||||||
|
The layer 2 header is filled as follows:
|
||||||
|
|
||||||
|
- Message Type: `001` (Connection Management)
|
||||||
|
- TX Request: `1`
|
||||||
|
- Source Address: the new client’s HAM-64 address
|
||||||
|
- Destination Address: the digipeater’s HAM-64 address
|
||||||
|
- TX sequence number: reserved, always 0
|
||||||
|
- RX sequence number: reserved, always 0
|
||||||
|
|
||||||
|
The message contains exactly 1 data byte: `0x01` to indicate that this is a Connection Request packet.
|
||||||
|
|
||||||
|
A Connection Request is always a response to a Beacon packet.
|
||||||
|
It shall be the only packet in a burst.
|
||||||
|
|
||||||
|
When the digipeater receives a Connection Request it has two options for a response:
|
||||||
|
|
||||||
|
. Accept the connection by sending a Connection Parameters packet to the client in the next burst.
|
||||||
|
. Reject the connection by sending a Connection Reset packet to the client in the next burst.
|
||||||
|
|
||||||
|
===== Connection Parameters
|
||||||
|
|
||||||
|
The Connection Parameters packet has two functions: it indicates to a client that its Connection Request was accepted and tells it how to configure its network stack.
|
||||||
|
This is the first packet in the regular Go-back-N data flow.
|
||||||
|
|
||||||
|
The layer 2 header is filled as follows:
|
||||||
|
|
||||||
|
- Message Type: `001` (Connection Management)
|
||||||
|
- TX Request: `1`
|
||||||
|
- Source Address: the digipeater’s HAM-64 address
|
||||||
|
- Destination Address: the new client’s HAM-64 address
|
||||||
|
- TX sequence number: 0 (the first packet)
|
||||||
|
- RX sequence number: 0 (no packets received yet)
|
||||||
|
|
||||||
|
The type indicator is `0x02`.
|
||||||
|
|
||||||
|
After the type indicator follow one or more configuration blocks.
|
||||||
|
Each block consists of three data fields:
|
||||||
|
|
||||||
|
. Configuration Type: 1 byte
|
||||||
|
. Configuration Data Length: 1 byte
|
||||||
|
. Configuration data: variable number of bytes as indicated by Configuration Data Length
|
||||||
|
|
||||||
|
The following Configuration Types are defined:
|
||||||
|
|
||||||
|
[cols="1,1,5", options="header"]
|
||||||
|
.Configuration Types
|
||||||
|
|===
|
||||||
|
|Type Indicator
|
||||||
|
|Length
|
||||||
|
|Description
|
||||||
|
|
||||||
|
|`0x00`
|
||||||
|
|16 Byte
|
||||||
|
|IPv6 address
|
||||||
|
|
||||||
|
|`0x01`
|
||||||
|
|16 Byte
|
||||||
|
|IPv6 gateway
|
||||||
|
|
||||||
|
|`0x02`
|
||||||
|
|16 Byte
|
||||||
|
|IPv6 DNS server (may appear multiple times)
|
||||||
|
|
||||||
|
|`0x03` - `0x07`
|
||||||
|
|-
|
||||||
|
|_reserved_
|
||||||
|
|
||||||
|
|`0x08`
|
||||||
|
|4 Byte
|
||||||
|
|IPv4 address
|
||||||
|
|
||||||
|
|`0x09`
|
||||||
|
|4 Byte
|
||||||
|
|IPv4 gateway
|
||||||
|
|
||||||
|
|`0x0A`
|
||||||
|
|4 Byte
|
||||||
|
|IPv4 DNS server (may appear multiple times)
|
||||||
|
|
||||||
|
|`0x0B` - `0xFF`
|
||||||
|
|-
|
||||||
|
|_reserved_
|
||||||
|
|
||||||
|
|===
|
||||||
|
|
||||||
|
If the client receives an unknown Configuration Type the corresponding block shall be skipped.
|
||||||
|
The remaining blocks shall be parsed as usual.
|
||||||
|
|
||||||
=== Ideas
|
=== Ideas
|
||||||
|
|
||||||
To be defined:
|
To be defined:
|
||||||
|
@ -267,108 +419,178 @@ To be defined:
|
||||||
|
|
||||||
==== Connection Establishment
|
==== Connection Establishment
|
||||||
|
|
||||||
[mermaid,format=svg,svg-type=interactive]
|
[msc,format=svg,svg-type=interactive, scale=1.4]
|
||||||
.Connection establishment
|
.Connection establishment
|
||||||
....
|
....
|
||||||
sequenceDiagram
|
msc {
|
||||||
participant digi as Digipeater
|
digi [label="Digipeater"],
|
||||||
participant client as Client
|
client [label="Client"];
|
||||||
|
|
||||||
digi -->> client: Beacon (Broadcast)
|
digi -> client [label="Beacon (Broadcast)"];
|
||||||
digi -->> client: Beacon (Broadcast)
|
digi -> client [label="Beacon (Broadcast)"];
|
||||||
digi -->> client: Beacon (Broadcast)
|
|
||||||
|
|
||||||
Note over client: Decides to connect
|
client box client [label="Decides to connect"];
|
||||||
|
...;
|
||||||
|
|
||||||
client ->> digi: Connection Request
|
digi -> client [label="Beacon (Broadcast)"];
|
||||||
|
digi <- client [label="Connection Request"];
|
||||||
|
|
||||||
alt Connection accepted
|
--- [label="Alternative 1: Connection is accepted"];
|
||||||
digi ->> client: Connection Parameters
|
digi -> client [label="Connection Parameters"];
|
||||||
client ->> digi: Connection Acknowledgement
|
digi <- client [label="Connection Acknowledgement"];
|
||||||
Note over digi,client: Connection established
|
digi box client [label="Connection established"];
|
||||||
else Connection rejected
|
|
||||||
digi ->> client: Connection Refusal
|
--- [label="Alternative 2: Connection is rejected"];
|
||||||
end
|
digi -> client [label="Connection Reset"];
|
||||||
|
}
|
||||||
....
|
....
|
||||||
|
|
||||||
==== Communication during a Connection
|
==== Communication during a Connection
|
||||||
|
|
||||||
[mermaid,format=svg,svg-type=interactive]
|
[msc,format=svg,svg-type=interactive, scale=1.4]
|
||||||
.In-connection communication
|
.In-connection communication
|
||||||
....
|
....
|
||||||
sequenceDiagram
|
msc {
|
||||||
participant digi as Digipeater
|
digi [label="Digipeater"],
|
||||||
participant client as Client
|
client [label="Client"];
|
||||||
|
|
||||||
Note over digi,client: Connection established
|
--- [label="Connection established"];
|
||||||
|
digi => client [label="Up to 14 packets without Transmission Request"];
|
||||||
|
digi -> client [label="Packet with Transmission Request"];
|
||||||
|
|
||||||
loop For up to 14 packets
|
digi box digi [label="Start timeout timer"];
|
||||||
digi ->> client: Packet without Transmission Request
|
|
||||||
end
|
|
||||||
|
|
||||||
digi ->> client: Packet with Transmission Request
|
--- [label="Alternative 1: Normal reply"];
|
||||||
|
|
||||||
Note over digi: Start timeout timer
|
digi <= client [label="Up to 14 packets without Transmission Request"];
|
||||||
|
digi <- client [label="Packet with Transmission Request"];
|
||||||
|
|
||||||
alt Normal reply
|
--- [label="Alternative 2: Partial reply"];
|
||||||
loop For up to 14 packets
|
|
||||||
client ->> digi: Packet without Transmission Request
|
|
||||||
end
|
|
||||||
|
|
||||||
client ->> digi: Packet with Transmission Request
|
digi <= client [label="Up to 14 packets without Transmission Request"];
|
||||||
else Partial reply
|
digi x- client [label="Packet with Transmission Request"];
|
||||||
loop For up to 14 packets
|
...;
|
||||||
client ->> digi: Packet without Transmission Request
|
digi rbox digi [label="Timeout expired"];
|
||||||
end
|
|
||||||
|
|
||||||
client --x digi: Packet with Transmission Request (lost)
|
--- [label="Alternative 3: No reply"];
|
||||||
|
|
||||||
Note over digi: Timeout expired
|
digi x- client [label="Packets from client"];
|
||||||
else No reply
|
...;
|
||||||
Note over digi: Timeout expired
|
digi rbox digi [label="Timeout expired"];
|
||||||
end
|
|
||||||
|
|
||||||
Note over digi: Query next client
|
--- [label="Finally"];
|
||||||
|
|
||||||
|
digi box digi [label="Query next client"];
|
||||||
|
}
|
||||||
....
|
....
|
||||||
|
|
||||||
==== Connection Shutdown
|
==== Connection Shutdown
|
||||||
|
|
||||||
===== Client-initiated
|
===== Client-initiated
|
||||||
|
|
||||||
[mermaid,format=svg,svg-type=interactive]
|
[msc,format=svg,svg-type=interactive, scale=1.4]
|
||||||
.Client-initiated shutdown
|
.Client-initiated shutdown
|
||||||
....
|
....
|
||||||
sequenceDiagram
|
msc {
|
||||||
participant digi as Digipeater
|
digi [label="Digipeater"],
|
||||||
participant client as Client
|
client [label="Client"];
|
||||||
|
|
||||||
Note over digi,client: Connection established
|
--- [label="Connection established"];
|
||||||
|
client box client [label="Decides to disconnect"];
|
||||||
|
...;
|
||||||
|
digi -> client [label="Transmission request"];
|
||||||
|
digi <- client [label="Disconnect"];
|
||||||
|
|
||||||
Note over client: Decides to disconnect
|
--- [label="Connection closed"];
|
||||||
|
}
|
||||||
digi ->> client: Transmission Request
|
|
||||||
client ->> digi: Disconnect
|
|
||||||
|
|
||||||
Note over digi,client: Connection closed
|
|
||||||
....
|
....
|
||||||
|
|
||||||
===== Digipeater-initiated
|
===== Digipeater-initiated
|
||||||
|
|
||||||
[mermaid,format=svg,svg-type=interactive]
|
[msc,format=svg,svg-type=interactive, scale=1.4]
|
||||||
.Digipeater-initiated shutdown
|
.Digipeater-initiated shutdown
|
||||||
....
|
....
|
||||||
sequenceDiagram
|
msc {
|
||||||
participant digi as Digipeater
|
digi [label="Digipeater"],
|
||||||
participant client as Client
|
client [label="Client"];
|
||||||
|
|
||||||
Note over digi,client: Connection established
|
--- [label="Connection established"];
|
||||||
|
digi box digi [label="Decides to disconnect Client"];
|
||||||
|
|
||||||
Note over digi: Decides to disconnect client
|
digi -> client [label="Disconnect request"];
|
||||||
|
digi <- client [label="Disconnect"];
|
||||||
|
|
||||||
digi ->> client: Disconnect Request
|
--- [label="Connection closed"];
|
||||||
client ->> digi: Disconnect
|
}
|
||||||
|
....
|
||||||
|
|
||||||
Note over digi,client: Connection closed
|
=== State Diagrams
|
||||||
|
|
||||||
|
This section documents state diagrams for the digipeater and the clients.
|
||||||
|
They are only informational; different implementations are possible and allowed as long as the procedures defined by the <<_message_sequence_charts,message sequence charts>> are obeyed.
|
||||||
|
However, the state diagrams shown here reflect the reference implementation.
|
||||||
|
|
||||||
|
==== Connection State Diagram
|
||||||
|
|
||||||
|
[graphviz,format=svg,svg-type=interactive,scale=1.0]
|
||||||
|
.Connection state diagram
|
||||||
|
....
|
||||||
|
digraph connection {
|
||||||
|
uninitd [label="Uninitialized"];
|
||||||
|
connecting [label="Connecting"];
|
||||||
|
wait_client [label="Wait for\nclient"];
|
||||||
|
estab [label="Two-way communication\nestablished"];
|
||||||
|
closed [label="Closed"];
|
||||||
|
|
||||||
|
uninitd -> connecting [label="Connection request\nsent [client]"];
|
||||||
|
uninitd -> wait_client [label="Connection request\nreceived [digipeater]"];
|
||||||
|
|
||||||
|
connecting -> estab [label="Connection\nacknowledgement sent"];
|
||||||
|
wait_client -> estab [label="Connection\nacknowledged"];
|
||||||
|
|
||||||
|
connecting -> closed [label="Timeout"];
|
||||||
|
wait_client -> closed [label="Timeout"];
|
||||||
|
|
||||||
|
estab -> closed [label="Disconnect/Timeout"];
|
||||||
|
closed -> uninitd [label="State erased"];
|
||||||
|
}
|
||||||
|
....
|
||||||
|
|
||||||
|
==== Digipeater Connection Multiplexing
|
||||||
|
|
||||||
|
[graphviz,layout=neato,format=svg,svg-type=interactive,scale=1.0]
|
||||||
|
.Handling of multiple connections at the digipeater
|
||||||
|
....
|
||||||
|
digraph multiclient {
|
||||||
|
overlap=false;
|
||||||
|
|
||||||
|
idle [label="Idle"];
|
||||||
|
send_beacon [label="Send Beacon"];
|
||||||
|
accept_new [label="Check for\nnew clients"];
|
||||||
|
check_conn [label="Check connection timers"];
|
||||||
|
send_to_client [label="Send queued packets\nto client"];
|
||||||
|
receive_from_client [label="Receive packets\nfrom client"];
|
||||||
|
check_timeout [label="Check connection\ntimeout"];
|
||||||
|
reset_timeout [label="Reset connection\ntimeout"];
|
||||||
|
close_conn [label="Close\nconnection"];
|
||||||
|
|
||||||
|
idle -> send_beacon [label="Beacon timeout\nexpired"];
|
||||||
|
send_beacon -> accept_new;
|
||||||
|
accept_new -> idle;
|
||||||
|
|
||||||
|
idle -> check_conn [label="Any contact\ntimer expired"];
|
||||||
|
check_conn -> send_to_client [label="Contact timer\nexpired"];
|
||||||
|
send_to_client -> receive_from_client;
|
||||||
|
receive_from_client -> reset_timeout [label="Packets received"];
|
||||||
|
reset_timeout -> check_conn;
|
||||||
|
|
||||||
|
receive_from_client -> check_timeout [label="Nothing received"];
|
||||||
|
check_timeout -> check_conn [label="No timeout yet"];
|
||||||
|
check_timeout -> close_conn [label="Timed out"];
|
||||||
|
close_conn -> check_conn;
|
||||||
|
|
||||||
|
check_conn -> idle [label="All expired\nconnections handled"];
|
||||||
|
}
|
||||||
....
|
....
|
||||||
|
|
||||||
== Higher Layer Protocols
|
== Higher Layer Protocols
|
||||||
|
|
|
@ -53,6 +53,8 @@ set(sources
|
||||||
src/layer2/layer2_rx.h
|
src/layer2/layer2_rx.h
|
||||||
src/layer2/ham64.c
|
src/layer2/ham64.c
|
||||||
src/layer2/ham64.h
|
src/layer2/ham64.h
|
||||||
|
src/layer2/connection.c
|
||||||
|
src/layer2/connection.h
|
||||||
src/sdr/sdr.c
|
src/sdr/sdr.c
|
||||||
src/sdr/sdr.h
|
src/sdr/sdr.h
|
||||||
)
|
)
|
||||||
|
|
22
impl/README.md
Normal file
22
impl/README.md
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
# Hamnet70 Implementation
|
||||||
|
|
||||||
|
This directory contains an implementation of the Hamnet70 protocol.
|
||||||
|
|
||||||
|
Before you can compile and use this code, some additional steps are necessary:
|
||||||
|
|
||||||
|
1. Copy `src/config.h.template` to `src/config.h` and set the following variables:
|
||||||
|
- `MY_CALL`: the station call sign (i.e. your amateur radio call sign).
|
||||||
|
This will be encoded into the address fields of outgoing packets.
|
||||||
|
2. Install dependencies:
|
||||||
|
- _libliquid_ compiled with _libfec_ support
|
||||||
|
- _libfec_
|
||||||
|
- _fftw3_
|
||||||
|
- _libhackrf_
|
||||||
|
|
||||||
|
After everything is prepared, compile the code using `./make.sh`.
|
||||||
|
Parameters to this script are forwarded to `make` so you can speed things up a little with `./make.sh -j4` (on a CPU with 4 threads).
|
||||||
|
|
||||||
|
When compiled, you have two options for running Hamnet70:
|
||||||
|
|
||||||
|
1. In digipeater (base station) mode: `build/hamnet70 -c`. This will broadcast beacons and wait for clients to connect.
|
||||||
|
2. In client mode: `build/hamnet70`. This will wait for a beacon to arrive and connect to it.
|
|
@ -9,6 +9,10 @@
|
||||||
|
|
||||||
#include <liquid/liquid.h>
|
#include <liquid/liquid.h>
|
||||||
|
|
||||||
|
/*** LAYER 2 CONFIG ***/
|
||||||
|
|
||||||
|
#define MY_CALL undefined // define MY_CALL to your call sign as a C string, e.g. "DL5TKL"
|
||||||
|
|
||||||
/*** TIMING CONFIG ***/
|
/*** TIMING CONFIG ***/
|
||||||
|
|
||||||
#define TX_SWITCH_BACKOFF_PREAMBLE_MS 42 // only relevant if packet cannot be decoded (maximum packet duration)
|
#define TX_SWITCH_BACKOFF_PREAMBLE_MS 42 // only relevant if packet cannot be decoded (maximum packet duration)
|
|
@ -143,7 +143,7 @@ float freq_est_in_rampup(const float complex *recv, size_t n, float *final_phase
|
||||||
|
|
||||||
// rotate every second symbol by 180°. As a plausibility check that we are
|
// rotate every second symbol by 180°. As a plausibility check that we are
|
||||||
// in fact inside the ramp-up, we verify that there is no phase rotation by
|
// in fact inside the ramp-up, we verify that there is no phase rotation by
|
||||||
// more than 90° between the symbols.
|
// more than 60° between the symbols.
|
||||||
for(size_t i = 0; i < n; i++) {
|
for(size_t i = 0; i < n; i++) {
|
||||||
if((i % 2) == 0) {
|
if((i % 2) == 0) {
|
||||||
rotated[i] = recv[i];
|
rotated[i] = recv[i];
|
||||||
|
@ -154,7 +154,7 @@ float freq_est_in_rampup(const float complex *recv, size_t n, float *final_phase
|
||||||
float phase = cargf(rotated[i]);
|
float phase = cargf(rotated[i]);
|
||||||
if(i > 0) {
|
if(i > 0) {
|
||||||
float phase_delta = phase - prev_phase;
|
float phase_delta = phase - prev_phase;
|
||||||
if(fabsf(phase_delta) > 3.14159f/2.0f) {
|
if(fabsf(phase_delta) > 3.14159f/3.0f) {
|
||||||
// abort because we either have the wrong signal or too much noise.
|
// abort because we either have the wrong signal or too much noise.
|
||||||
if(final_phase) {
|
if(final_phase) {
|
||||||
*final_phase = 0.0f;
|
*final_phase = 0.0f;
|
||||||
|
|
|
@ -33,9 +33,11 @@
|
||||||
#define HEADER_SIZE_BYTES 2
|
#define HEADER_SIZE_BYTES 2
|
||||||
#define FREQ_EST_L 24
|
#define FREQ_EST_L 24
|
||||||
|
|
||||||
#define AGC_BW_ACQUISITION 5e-2f
|
#define AGC_BW_ACQUISITION 3e-2f
|
||||||
#define AGC_BW_TRACKING 1e-4f
|
#define AGC_BW_TRACKING 1e-4f
|
||||||
|
|
||||||
|
#define SQUELCH_TIMEOUT_MS 2
|
||||||
|
|
||||||
#define MAX_COARSE_FREQ_OFFSET 0.20f
|
#define MAX_COARSE_FREQ_OFFSET 0.20f
|
||||||
|
|
||||||
#define PLL_BW_HEADER 0.03f
|
#define PLL_BW_HEADER 0.03f
|
||||||
|
@ -206,7 +208,7 @@ static enum squelch_state_t update_and_check_squelch(layer1_rx_t *rx, unsigned i
|
||||||
// Adjustment value is in dB.
|
// Adjustment value is in dB.
|
||||||
rx->noise_floor_level += 1e-4f;
|
rx->noise_floor_level += 1e-4f;
|
||||||
}
|
}
|
||||||
agc_crcf_squelch_set_threshold(rx->agc, rx->noise_floor_level + 10.0f); // in dB
|
agc_crcf_squelch_set_threshold(rx->agc, rx->noise_floor_level + 6.0f); // in dB
|
||||||
}
|
}
|
||||||
|
|
||||||
switch(agc_crcf_squelch_get_status(rx->agc)) {
|
switch(agc_crcf_squelch_get_status(rx->agc)) {
|
||||||
|
@ -216,15 +218,15 @@ static enum squelch_state_t update_and_check_squelch(layer1_rx_t *rx, unsigned i
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LIQUID_AGC_SQUELCH_SIGNALHI:
|
case LIQUID_AGC_SQUELCH_SIGNALHI:
|
||||||
|
case LIQUID_AGC_SQUELCH_FALL:
|
||||||
|
case LIQUID_AGC_SQUELCH_SIGNALLO:
|
||||||
result = SQUELCH_OPEN;
|
result = SQUELCH_OPEN;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LIQUID_AGC_SQUELCH_FALL:
|
case LIQUID_AGC_SQUELCH_TIMEOUT:
|
||||||
LOG(LVL_DEBUG, "Squelch enabled at #%zu RSSI = %.3f dB [thr: %.3f dB]", rx->sample_index, level, agc_crcf_squelch_get_threshold(rx->agc));
|
LOG(LVL_DEBUG, "Squelch enabled at #%zu RSSI = %.3f dB [thr: %.3f dB]", rx->sample_index, level, agc_crcf_squelch_get_threshold(rx->agc));
|
||||||
// fall through
|
// fall through
|
||||||
case LIQUID_AGC_SQUELCH_SIGNALLO:
|
|
||||||
case LIQUID_AGC_SQUELCH_ENABLED:
|
case LIQUID_AGC_SQUELCH_ENABLED:
|
||||||
case LIQUID_AGC_SQUELCH_TIMEOUT:
|
|
||||||
result = SQUELCH_CLOSED;
|
result = SQUELCH_CLOSED;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -239,13 +241,13 @@ static bool is_squelch_open(const layer1_rx_t *rx)
|
||||||
switch(agc_crcf_squelch_get_status(rx->agc)) {
|
switch(agc_crcf_squelch_get_status(rx->agc)) {
|
||||||
case LIQUID_AGC_SQUELCH_RISE:
|
case LIQUID_AGC_SQUELCH_RISE:
|
||||||
case LIQUID_AGC_SQUELCH_SIGNALHI:
|
case LIQUID_AGC_SQUELCH_SIGNALHI:
|
||||||
|
case LIQUID_AGC_SQUELCH_FALL:
|
||||||
|
case LIQUID_AGC_SQUELCH_SIGNALLO:
|
||||||
result = true;
|
result = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LIQUID_AGC_SQUELCH_FALL:
|
|
||||||
case LIQUID_AGC_SQUELCH_SIGNALLO:
|
|
||||||
case LIQUID_AGC_SQUELCH_ENABLED:
|
|
||||||
case LIQUID_AGC_SQUELCH_TIMEOUT:
|
case LIQUID_AGC_SQUELCH_TIMEOUT:
|
||||||
|
case LIQUID_AGC_SQUELCH_ENABLED:
|
||||||
result = false;
|
result = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -265,6 +267,8 @@ result_t layer1_rx_process(layer1_rx_t *rx, const float complex *samples, size_t
|
||||||
static float complex samples2dump_s[8192];
|
static float complex samples2dump_s[8192];
|
||||||
static size_t nsamples2dump_s = 0;
|
static size_t nsamples2dump_s = 0;
|
||||||
|
|
||||||
|
static float evm;
|
||||||
|
|
||||||
// cache configuration flags
|
// cache configuration flags
|
||||||
bool is_central_node = options_is_flag_set(OPTIONS_FLAG_IS_CENTRAL_NODE);
|
bool is_central_node = options_is_flag_set(OPTIONS_FLAG_IS_CENTRAL_NODE);
|
||||||
|
|
||||||
|
@ -344,6 +348,7 @@ result_t layer1_rx_process(layer1_rx_t *rx, const float complex *samples, size_t
|
||||||
// go on with decoding the header
|
// go on with decoding the header
|
||||||
rx->state = RX_STATE_HEADER;
|
rx->state = RX_STATE_HEADER;
|
||||||
symbol_counter = 0;
|
symbol_counter = 0;
|
||||||
|
evm = 0.0f;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -361,6 +366,7 @@ result_t layer1_rx_process(layer1_rx_t *rx, const float complex *samples, size_t
|
||||||
LOG(LVL_DUMP, "@%zu: Sym: %d; Phase error: %f %s", rx->sample_index, sym_demod, phase_error,
|
LOG(LVL_DUMP, "@%zu: Sym: %d; Phase error: %f %s", rx->sample_index, sym_demod, phase_error,
|
||||||
(fabs(phase_error) > 0.3) ? "!!!" : "");
|
(fabs(phase_error) > 0.3) ? "!!!" : "");
|
||||||
|
|
||||||
|
evm += modem_get_demodulator_evm(rx->hdr_demod);
|
||||||
|
|
||||||
update_nco_pll(rx->carrier_fine_nco, phase_error, PLL_BW_HEADER);
|
update_nco_pll(rx->carrier_fine_nco, phase_error, PLL_BW_HEADER);
|
||||||
|
|
||||||
|
@ -378,7 +384,7 @@ result_t layer1_rx_process(layer1_rx_t *rx, const float complex *samples, size_t
|
||||||
// store debug info about the header
|
// store debug info about the header
|
||||||
rx->packet_debug_info.noise_floor_level = rx->noise_floor_level;
|
rx->packet_debug_info.noise_floor_level = rx->noise_floor_level;
|
||||||
rx->packet_debug_info.header_rssi = agc_crcf_get_rssi(rx->agc);
|
rx->packet_debug_info.header_rssi = agc_crcf_get_rssi(rx->agc);
|
||||||
rx->packet_debug_info.header_evm = -1e38f; // FIXME
|
rx->packet_debug_info.header_evm = evm / symbol_counter;
|
||||||
|
|
||||||
ERR_CHECK_LIQUID(liquid_repack_bytes(
|
ERR_CHECK_LIQUID(liquid_repack_bytes(
|
||||||
symbols_int, modem_get_bps(rx->hdr_demod), rx->hdr_len_symbols,
|
symbols_int, modem_get_bps(rx->hdr_demod), rx->hdr_len_symbols,
|
||||||
|
@ -433,6 +439,7 @@ result_t layer1_rx_process(layer1_rx_t *rx, const float complex *samples, size_t
|
||||||
|
|
||||||
rx->state = RX_STATE_DATA;
|
rx->state = RX_STATE_DATA;
|
||||||
symbol_counter = 0;
|
symbol_counter = 0;
|
||||||
|
evm = 0.0f;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -453,6 +460,8 @@ result_t layer1_rx_process(layer1_rx_t *rx, const float complex *samples, size_t
|
||||||
float phase_error = modem_get_demodulator_phase_error(rx->payload_demod);
|
float phase_error = modem_get_demodulator_phase_error(rx->payload_demod);
|
||||||
//LOG(LVL_DEBUG, "@%zu: Sym: %d; Phase error: %f", rx->sample_index, sym_demod, phase_error);
|
//LOG(LVL_DEBUG, "@%zu: Sym: %d; Phase error: %f", rx->sample_index, sym_demod, phase_error);
|
||||||
|
|
||||||
|
evm += modem_get_demodulator_evm(rx->payload_demod);
|
||||||
|
|
||||||
update_nco_pll(rx->carrier_fine_nco, phase_error, PLL_BW_DATA);
|
update_nco_pll(rx->carrier_fine_nco, phase_error, PLL_BW_DATA);
|
||||||
|
|
||||||
symbols_int[symbol_counter] = sym_demod;
|
symbols_int[symbol_counter] = sym_demod;
|
||||||
|
@ -471,7 +480,7 @@ result_t layer1_rx_process(layer1_rx_t *rx, const float complex *samples, size_t
|
||||||
|
|
||||||
// store debug info about the data
|
// store debug info about the data
|
||||||
rx->packet_debug_info.data_rssi = agc_crcf_get_rssi(rx->agc);
|
rx->packet_debug_info.data_rssi = agc_crcf_get_rssi(rx->agc);
|
||||||
rx->packet_debug_info.data_evm = -1e38f; // FIXME
|
rx->packet_debug_info.data_evm = evm / symbol_counter;
|
||||||
|
|
||||||
// deinterleave the message symbols
|
// deinterleave the message symbols
|
||||||
uint8_t symbols_int_deinterleaved[rx->payload_len_symbols];
|
uint8_t symbols_int_deinterleaved[rx->payload_len_symbols];
|
||||||
|
@ -553,6 +562,7 @@ result_t layer1_rx_init(layer1_rx_t *rx, rx_callback_t callback)
|
||||||
rx->noise_floor_level = 0.0f;
|
rx->noise_floor_level = 0.0f;
|
||||||
agc_crcf_squelch_set_threshold(rx->agc, rx->noise_floor_level + 3.0f); // in dB
|
agc_crcf_squelch_set_threshold(rx->agc, rx->noise_floor_level + 3.0f); // in dB
|
||||||
agc_crcf_squelch_enable(rx->agc);
|
agc_crcf_squelch_enable(rx->agc);
|
||||||
|
agc_crcf_squelch_set_timeout(rx->agc, SQUELCH_TIMEOUT_MS * SYMBOL_RATE * RRC_SPS / 1000);
|
||||||
|
|
||||||
// create NCOs for carrier frequency compensation
|
// create NCOs for carrier frequency compensation
|
||||||
rx->carrier_coarse_nco = nco_crcf_create(LIQUID_NCO);
|
rx->carrier_coarse_nco = nco_crcf_create(LIQUID_NCO);
|
||||||
|
|
375
impl/src/layer2/connection.c
Normal file
375
impl/src/layer2/connection.c
Normal file
|
@ -0,0 +1,375 @@
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include "connection.h"
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "layer2/ham64.h"
|
||||||
|
#include "results.h"
|
||||||
|
|
||||||
|
#define SEQ_NR_MASK 0xF
|
||||||
|
|
||||||
|
result_t connection_init(connection_ctx_t *ctx, const ham64_t *my_addr, const ham64_t *peer_addr)
|
||||||
|
{
|
||||||
|
ctx->last_acked_seq = 0;
|
||||||
|
ctx->next_expected_seq = 0;
|
||||||
|
|
||||||
|
packet_queue_init(&ctx->packet_queue);
|
||||||
|
ctx->next_packet_index = 0;
|
||||||
|
ctx->next_seq_nr = 0;
|
||||||
|
|
||||||
|
ctx->my_addr = *my_addr;
|
||||||
|
ctx->peer_addr = *peer_addr;
|
||||||
|
|
||||||
|
ctx->conn_state = CONN_STATE_INITIALIZED;
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void connection_destroy(connection_ctx_t *ctx)
|
||||||
|
{
|
||||||
|
if(ctx->conn_state == CONN_STATE_UNINITIALIZED) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->conn_state = CONN_STATE_UNINITIALIZED;
|
||||||
|
packet_queue_destroy(&ctx->packet_queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
result_t connection_handle_packet(connection_ctx_t *ctx, const uint8_t *buf, size_t buf_len)
|
||||||
|
{
|
||||||
|
// check the connection state
|
||||||
|
switch(ctx->conn_state) {
|
||||||
|
case CONN_STATE_UNINITIALIZED:
|
||||||
|
case CONN_STATE_INITIALIZED:
|
||||||
|
case CONN_STATE_CLOSED:
|
||||||
|
LOG(LVL_ERR, "Trying to pass packet to connection in state %u", ctx->conn_state);
|
||||||
|
return ERR_INVALID_STATE;
|
||||||
|
|
||||||
|
case CONN_STATE_CONNECTING:
|
||||||
|
case CONN_STATE_ESTABLISHED:
|
||||||
|
// in these states, packets can be handled
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check the CRC
|
||||||
|
size_t packet_size = buf_len - crc_sizeof_key(PAYLOAD_CRC_SCHEME);
|
||||||
|
|
||||||
|
if(!crc_check_key(PAYLOAD_CRC_SCHEME, (unsigned char*)buf, packet_size)) {
|
||||||
|
LOG(LVL_ERR, "payload CRC check failed!");
|
||||||
|
return ERR_INTEGRITY;
|
||||||
|
}
|
||||||
|
|
||||||
|
// decode the header
|
||||||
|
layer2_packet_header_t header;
|
||||||
|
|
||||||
|
if(!layer2_decode_packet_header(buf, buf_len, &header)) {
|
||||||
|
LOG(LVL_ERR, "Header could not be decoded!");
|
||||||
|
return ERR_INTEGRITY;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if the packet really should be handled by us
|
||||||
|
if(!ham64_is_equal(&header.src_addr, &ctx->peer_addr)) {
|
||||||
|
char fmt_src_addr[HAM64_FMT_MAX_LEN];
|
||||||
|
char fmt_peer_addr[HAM64_FMT_MAX_LEN];
|
||||||
|
|
||||||
|
ham64_format(&header.src_addr, fmt_src_addr);
|
||||||
|
ham64_format(&ctx->peer_addr, fmt_peer_addr);
|
||||||
|
|
||||||
|
LOG(LVL_ERR, "Packet has the wrong source address: got %s, expected %s",
|
||||||
|
fmt_src_addr, fmt_peer_addr);
|
||||||
|
|
||||||
|
return ERR_INVALID_ADDRESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!ham64_is_equal(&header.dst_addr, &ctx->my_addr)) {
|
||||||
|
char fmt_dst_addr[HAM64_FMT_MAX_LEN];
|
||||||
|
char fmt_my_addr[HAM64_FMT_MAX_LEN];
|
||||||
|
|
||||||
|
ham64_format(&header.dst_addr, fmt_dst_addr);
|
||||||
|
ham64_format(&ctx->my_addr, fmt_my_addr);
|
||||||
|
|
||||||
|
LOG(LVL_ERR, "Packet has the wrong destination address: got %s, expected %s",
|
||||||
|
fmt_dst_addr, fmt_my_addr);
|
||||||
|
|
||||||
|
return ERR_INVALID_ADDRESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG(LVL_DEBUG, "Handling %s packet with rx_seq_nr %u, tx_seq_nr %u.",
|
||||||
|
layer2_msg_type_to_string(header.msg_type), header.rx_seq_nr, header.tx_seq_nr);
|
||||||
|
|
||||||
|
ctx->last_acked_seq = header.rx_seq_nr;
|
||||||
|
|
||||||
|
|
||||||
|
switch(header.msg_type) {
|
||||||
|
case L2_MSG_TYPE_EMPTY:
|
||||||
|
LOG(LVL_DEBUG, "Empty packet: accepted ACK for %u.", ctx->last_acked_seq);
|
||||||
|
return OK; // do not ACK and call back
|
||||||
|
|
||||||
|
case L2_MSG_TYPE_CONN_MGMT:
|
||||||
|
case L2_MSG_TYPE_CONNECTIONLESS:
|
||||||
|
LOG(LVL_WARN, "Message type %s is not implemented yet.", layer2_msg_type_to_string(header.msg_type));
|
||||||
|
return OK;
|
||||||
|
|
||||||
|
case L2_MSG_TYPE_DATA:
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
LOG(LVL_ERR, "Invalid message type %d.", header.msg_type);
|
||||||
|
return ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ctx->next_expected_seq != header.tx_seq_nr) {
|
||||||
|
LOG(LVL_ERR, "Expected sequence number %u, received %u.", ctx->next_expected_seq, header.tx_seq_nr);
|
||||||
|
return ERR_SEQUENCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->next_expected_seq++;
|
||||||
|
ctx->next_expected_seq &= 0xF;
|
||||||
|
|
||||||
|
LOG(LVL_INFO, "Received ACK for seq_nr %u in packet seq_nr %u.", header.rx_seq_nr, header.tx_seq_nr);
|
||||||
|
|
||||||
|
// handle the acknowledgement internally
|
||||||
|
connection_handle_ack(ctx, header.rx_seq_nr);
|
||||||
|
|
||||||
|
size_t header_size = layer2_get_encoded_header_size(&header);
|
||||||
|
|
||||||
|
// extract the payload and forward it to the tun device
|
||||||
|
const uint8_t *payload = buf + header_size;
|
||||||
|
size_t payload_len = packet_size - header_size;
|
||||||
|
|
||||||
|
ctx->data_cb(ctx, payload, payload_len);
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint8_t connection_get_next_expected_seq(const connection_ctx_t *ctx)
|
||||||
|
{
|
||||||
|
return ctx->next_expected_seq;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint8_t connection_get_last_acked_seq(const connection_ctx_t *ctx)
|
||||||
|
{
|
||||||
|
return ctx->last_acked_seq;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
result_t connection_enqueue_packet(connection_ctx_t *ctx, uint8_t *buf, size_t buf_len)
|
||||||
|
{
|
||||||
|
// check the connection state
|
||||||
|
switch(ctx->conn_state) {
|
||||||
|
case CONN_STATE_UNINITIALIZED:
|
||||||
|
case CONN_STATE_INITIALIZED:
|
||||||
|
case CONN_STATE_CLOSED:
|
||||||
|
case CONN_STATE_CONNECTING:
|
||||||
|
LOG(LVL_ERR, "Trying to enqueue packet in inactive state %u", ctx->conn_state);
|
||||||
|
return ERR_INVALID_STATE;
|
||||||
|
|
||||||
|
case CONN_STATE_ESTABLISHED:
|
||||||
|
// in these states, packets can be handled
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
layer2_packet_header_t header;
|
||||||
|
|
||||||
|
if(packet_queue_get_free_space(&ctx->packet_queue) == 0) {
|
||||||
|
return ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
header.dst_addr = ctx->peer_addr;
|
||||||
|
header.src_addr = ctx->my_addr;
|
||||||
|
header.msg_type = L2_MSG_TYPE_DATA;
|
||||||
|
header.rx_seq_nr = 0; // will be filled in layer2_tx_encode_next_packet()
|
||||||
|
header.tx_request = 0;
|
||||||
|
header.tx_seq_nr = ctx->next_seq_nr;
|
||||||
|
|
||||||
|
// create a persistent copy of the packet data.
|
||||||
|
// TODO: possibly this copy operation can be removed by passing a malloc'd buffer in.
|
||||||
|
uint8_t *packetbuf = malloc(buf_len);
|
||||||
|
if(!packetbuf) {
|
||||||
|
LOG(LVL_ERR, "malloc failed.");
|
||||||
|
return ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(packetbuf, buf, buf_len);
|
||||||
|
|
||||||
|
packet_queue_add(&ctx->packet_queue, &header, packetbuf, buf_len);
|
||||||
|
|
||||||
|
LOG(LVL_INFO, "Added packet tx_seq %u to queue -> %zu entries",
|
||||||
|
header.tx_seq_nr, packet_queue_get_used_space(&ctx->packet_queue));
|
||||||
|
|
||||||
|
ctx->next_seq_nr++;
|
||||||
|
ctx->next_seq_nr &= SEQ_NR_MASK;
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
result_t connection_add_empty_packet(connection_ctx_t *ctx, bool tx_request)
|
||||||
|
{
|
||||||
|
// check the connection state
|
||||||
|
switch(ctx->conn_state) {
|
||||||
|
case CONN_STATE_UNINITIALIZED:
|
||||||
|
case CONN_STATE_INITIALIZED:
|
||||||
|
case CONN_STATE_CLOSED:
|
||||||
|
case CONN_STATE_CONNECTING:
|
||||||
|
LOG(LVL_ERR, "Trying to add empty packet in inactive state %u", ctx->conn_state);
|
||||||
|
return ERR_INVALID_STATE;
|
||||||
|
|
||||||
|
case CONN_STATE_ESTABLISHED:
|
||||||
|
// in these states, packets can be handled
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
layer2_packet_header_t header;
|
||||||
|
|
||||||
|
header.dst_addr.addr[0] = 0xFFFF;
|
||||||
|
header.dst_addr.length = 1;
|
||||||
|
header.src_addr.addr[0] = 0x0001;
|
||||||
|
header.src_addr.length = 1;
|
||||||
|
header.msg_type = L2_MSG_TYPE_EMPTY;
|
||||||
|
header.rx_seq_nr = 0; // will be filled in layer2_tx_encode_next_packet()
|
||||||
|
header.tx_seq_nr = 0; // not used in empty packets
|
||||||
|
header.tx_request = tx_request;
|
||||||
|
|
||||||
|
if (!packet_queue_add(&ctx->packet_queue, &header, NULL, 0)) {
|
||||||
|
return ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
size_t connection_encode_next_packet(connection_ctx_t *ctx, uint8_t ack_seq_nr, uint8_t *buf, size_t buf_len)
|
||||||
|
{
|
||||||
|
// check the connection state
|
||||||
|
switch(ctx->conn_state) {
|
||||||
|
case CONN_STATE_UNINITIALIZED:
|
||||||
|
case CONN_STATE_INITIALIZED:
|
||||||
|
case CONN_STATE_CLOSED:
|
||||||
|
LOG(LVL_ERR, "Trying to encode packet in inactive state %u", ctx->conn_state);
|
||||||
|
return ERR_INVALID_STATE;
|
||||||
|
|
||||||
|
case CONN_STATE_CONNECTING:
|
||||||
|
case CONN_STATE_ESTABLISHED:
|
||||||
|
// in these states, packets may be present for transmission
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const packet_queue_entry_t *entry = packet_queue_get(&ctx->packet_queue, ctx->next_packet_index);
|
||||||
|
|
||||||
|
if(!entry) {
|
||||||
|
// no more entries
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int crc_size = crc_sizeof_key(PAYLOAD_CRC_SCHEME);
|
||||||
|
|
||||||
|
assert(buf_len >= LAYER2_PACKET_HEADER_ENCODED_SIZE_MAX + crc_size + entry->data_len);
|
||||||
|
|
||||||
|
layer2_packet_header_t header = entry->header;
|
||||||
|
header.rx_seq_nr = ack_seq_nr;
|
||||||
|
|
||||||
|
// encode the header
|
||||||
|
LOG(LVL_DEBUG, "Encoding packet with rx_seq_nr %u, tx_seq_nr %u.", header.rx_seq_nr, header.tx_seq_nr);
|
||||||
|
|
||||||
|
size_t packet_size = layer2_encode_packet_header(&header, buf);
|
||||||
|
|
||||||
|
// add the payload data
|
||||||
|
if(entry->data) {
|
||||||
|
memcpy(buf + packet_size, entry->data, entry->data_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
packet_size += entry->data_len;
|
||||||
|
|
||||||
|
// calculate CRC of everything and append it to the packet
|
||||||
|
crc_append_key(PAYLOAD_CRC_SCHEME, buf, packet_size);
|
||||||
|
|
||||||
|
packet_size += crc_size;
|
||||||
|
|
||||||
|
ctx->next_packet_index++;
|
||||||
|
|
||||||
|
return packet_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void connection_restart_tx(connection_ctx_t *ctx)
|
||||||
|
{
|
||||||
|
ctx->next_packet_index = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void connection_tx_clean_empty_packet(connection_ctx_t *ctx)
|
||||||
|
{
|
||||||
|
assert(ctx->conn_state != CONN_STATE_UNINITIALIZED);
|
||||||
|
|
||||||
|
const packet_queue_entry_t *entry = packet_queue_get(&ctx->packet_queue, 0);
|
||||||
|
if(entry && entry->header.msg_type == L2_MSG_TYPE_EMPTY) {
|
||||||
|
packet_queue_delete(&ctx->packet_queue, 1);
|
||||||
|
|
||||||
|
if(ctx->next_packet_index > 0) {
|
||||||
|
ctx->next_packet_index--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void connection_handle_ack(connection_ctx_t *ctx, uint8_t acked_seq)
|
||||||
|
{
|
||||||
|
// check the connection state
|
||||||
|
switch(ctx->conn_state) {
|
||||||
|
case CONN_STATE_UNINITIALIZED:
|
||||||
|
case CONN_STATE_INITIALIZED:
|
||||||
|
case CONN_STATE_CLOSED:
|
||||||
|
case CONN_STATE_CONNECTING:
|
||||||
|
LOG(LVL_ERR, "Trying to call connection_handle_ack() in inactive state %u", ctx->conn_state);
|
||||||
|
return;
|
||||||
|
|
||||||
|
case CONN_STATE_ESTABLISHED:
|
||||||
|
// in these states, packets may be present for transmission
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->next_packet_index = 0;
|
||||||
|
|
||||||
|
size_t packets_to_remove = 0;
|
||||||
|
size_t packets_available = packet_queue_get_used_space(&ctx->packet_queue);
|
||||||
|
|
||||||
|
for(size_t i = 0; i < packets_available; i++) {
|
||||||
|
const packet_queue_entry_t *entry = packet_queue_get(&ctx->packet_queue, i);
|
||||||
|
|
||||||
|
if(entry->header.tx_seq_nr == acked_seq) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
packets_to_remove++;
|
||||||
|
}
|
||||||
|
|
||||||
|
packet_queue_delete(&ctx->packet_queue, packets_to_remove);
|
||||||
|
|
||||||
|
packets_available = packet_queue_get_used_space(&ctx->packet_queue);
|
||||||
|
|
||||||
|
LOG(LVL_DEBUG, "handling ack for seq_nr %u, removing %zu packets, %zu packets remaining.", acked_seq, packets_to_remove, packets_available);
|
||||||
|
|
||||||
|
if(packets_available == 0) {
|
||||||
|
// no packets left in queue, but an acknowledgement must be
|
||||||
|
// transmitted. Add an empty packet to do that.
|
||||||
|
result_t res = connection_add_empty_packet(ctx, false);
|
||||||
|
if (res != OK) {
|
||||||
|
LOG(LVL_WARN, "Failed to add empty packet: %d.", res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool connection_can_transmit(const connection_ctx_t *ctx)
|
||||||
|
{
|
||||||
|
assert(ctx->conn_state != CONN_STATE_UNINITIALIZED);
|
||||||
|
|
||||||
|
return (packet_queue_get_used_space(&ctx->packet_queue) != 0)
|
||||||
|
&& (packet_queue_get(&ctx->packet_queue, ctx->next_packet_index) != NULL);
|
||||||
|
}
|
126
impl/src/layer2/connection.h
Normal file
126
impl/src/layer2/connection.h
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
/*
|
||||||
|
* This file contains functions to handle a single layer 2 connection.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 Thomas Kolb
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CONNECTION_H
|
||||||
|
#define CONNECTION_H
|
||||||
|
|
||||||
|
#include <results.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include "packet_queue.h"
|
||||||
|
|
||||||
|
struct connection_ctx_s;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
CONN_STATE_UNINITIALIZED, //!< Uninitialized. Cannot be used in any way
|
||||||
|
CONN_STATE_INITIALIZED, //!< Initialized, no packets processed yet
|
||||||
|
CONN_STATE_CONNECTING, //!< Connection request sent, no two-way communication yet
|
||||||
|
CONN_STATE_ESTABLISHED, //!< Connection is established
|
||||||
|
CONN_STATE_CLOSED //!< Connection has been closed (gracefully or by timeout)
|
||||||
|
} connection_state_t;
|
||||||
|
|
||||||
|
/*!\brief Type for a callback function that is called when a data packet was received. */
|
||||||
|
typedef void (*connection_data_callback_t)(struct connection_ctx_s *conn, const uint8_t *data, size_t len);
|
||||||
|
|
||||||
|
typedef struct connection_ctx_s {
|
||||||
|
connection_state_t conn_state; //!< State of the connection.
|
||||||
|
|
||||||
|
connection_data_callback_t data_cb; //!< Callback function for received data packets.
|
||||||
|
|
||||||
|
ham64_t my_addr; //!< The local link layer address.
|
||||||
|
ham64_t peer_addr; //!< The link layer address of the peer.
|
||||||
|
|
||||||
|
uint8_t last_acked_seq; //!< Next sequence number expected by the peer (from last Ack).
|
||||||
|
uint8_t next_expected_seq; //!< Next sequence number expected by us.
|
||||||
|
|
||||||
|
packet_queue_t packet_queue; //!< Transmission packet queue.
|
||||||
|
|
||||||
|
size_t next_packet_index; //!< Index in the packet queue of the next packet to transmit.
|
||||||
|
uint8_t next_seq_nr; //!< Sequence number to tag the next transmitted packet with.
|
||||||
|
} connection_ctx_t;
|
||||||
|
|
||||||
|
|
||||||
|
/*!\brief Initialize the layer 2 connection context.
|
||||||
|
*
|
||||||
|
* \param ctx The connection context to initialize.
|
||||||
|
* \param my_addr The local link layer address.
|
||||||
|
* \param peer_addr The remote link layer address.
|
||||||
|
* \returns OK if everything worked or a fitting error code.
|
||||||
|
*/
|
||||||
|
result_t connection_init(connection_ctx_t *ctx, const ham64_t *my_addr, const ham64_t *peer_addr);
|
||||||
|
|
||||||
|
/*!\brief Destroy the given layer 2 connection context.
|
||||||
|
*/
|
||||||
|
void connection_destroy(connection_ctx_t *ctx);
|
||||||
|
|
||||||
|
/*!\brief Handle a received packet.
|
||||||
|
*
|
||||||
|
* \param ctx The connection context.
|
||||||
|
* \param buf Pointer to the packet data.
|
||||||
|
* \param buf_len Length of the packet.
|
||||||
|
* \returns A result code from the packet handling procedure.
|
||||||
|
*/
|
||||||
|
result_t connection_handle_packet(connection_ctx_t *ctx, const uint8_t *buf, size_t buf_len);
|
||||||
|
|
||||||
|
/*!\brief Return the sequence number expected next by our side.
|
||||||
|
*/
|
||||||
|
uint8_t connection_get_next_expected_seq(const connection_ctx_t *ctx);
|
||||||
|
|
||||||
|
/*!\brief Return the sequence number expected next by the other side.
|
||||||
|
*/
|
||||||
|
uint8_t connection_get_last_acked_seq(const connection_ctx_t *ctx);
|
||||||
|
|
||||||
|
/*!\brief Enqueue a packet for transmission.
|
||||||
|
* \param ctx The connection context.
|
||||||
|
*/
|
||||||
|
result_t connection_enqueue_packet(connection_ctx_t *ctx, uint8_t *buf, size_t buf_len);
|
||||||
|
|
||||||
|
/*!\brief Add an empty packet to ensure an acknowledgement is sent.
|
||||||
|
* \param ctx The connection context.
|
||||||
|
* \param tx_request Value of the TX Request field in the packet.
|
||||||
|
*/
|
||||||
|
result_t connection_add_empty_packet(connection_ctx_t *ctx, bool tx_request);
|
||||||
|
|
||||||
|
/*!\brief Encode the next packet for transmission.
|
||||||
|
*
|
||||||
|
* \note
|
||||||
|
* If no more packets are available, this function returns zero. In that case,
|
||||||
|
* either \ref connection_restart() or \ref connection_handle_ack() must be
|
||||||
|
* called to handle retransmits correctly.
|
||||||
|
*
|
||||||
|
* \param ctx The connection context.
|
||||||
|
* \param ack_seq_nr The received sequence number to send as an acknowledgement.
|
||||||
|
* \param buf Where to write the encoded packet data.
|
||||||
|
* \param buf_len Space available in the buffer.
|
||||||
|
* \returns The number of bytes written to buf or zero if no packet was available.
|
||||||
|
*/
|
||||||
|
size_t connection_encode_next_packet(connection_ctx_t *ctx, uint8_t ack_seq_nr, uint8_t *buf, size_t buf_len);
|
||||||
|
|
||||||
|
/*!\brief Restart the transmission from the beginning of the packet queue.
|
||||||
|
*/
|
||||||
|
void connection_restart_tx(connection_ctx_t *ctx);
|
||||||
|
|
||||||
|
/*!\brief Remove the first packet from the queue if it is an empty packet.
|
||||||
|
*/
|
||||||
|
void connection_tx_clean_empty_packet(connection_ctx_t *ctx);
|
||||||
|
|
||||||
|
/*!\brief Handle acknowledgements.
|
||||||
|
* \details
|
||||||
|
* Removes all packets before the given sequence number from the queue.
|
||||||
|
*
|
||||||
|
* \param ctx The connection context.
|
||||||
|
* \param acked_seq The acknowledged (= next expected) sequence number.
|
||||||
|
* \param do_ack Whether an empty packet shall be generated if the queue is empty.
|
||||||
|
*/
|
||||||
|
void connection_handle_ack(connection_ctx_t *ctx, uint8_t acked_seq);
|
||||||
|
|
||||||
|
/*!\brief Check if there are packets queued for transmission.
|
||||||
|
*/
|
||||||
|
bool connection_can_transmit(const connection_ctx_t *ctx);
|
||||||
|
|
||||||
|
#endif // CONNECTION_H
|
|
@ -187,3 +187,19 @@ void ham64_format(const ham64_t *ham64, char *out)
|
||||||
|
|
||||||
out[5*ham64->length - 1] = '\0';
|
out[5*ham64->length - 1] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool ham64_is_equal(const ham64_t *a, const ham64_t *b)
|
||||||
|
{
|
||||||
|
if(a->length != b->length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(uint8_t i = 0; i < a->length; i++) {
|
||||||
|
if(a->addr[i] != b->addr[i]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
// buffer size required for the string representation of a maximum-length HAM64
|
// buffer size required for the string representation of a maximum-length HAM64
|
||||||
// address, including terminating zero.
|
// address, including terminating zero.
|
||||||
|
@ -62,4 +63,11 @@ const char *ham64_addr_type_to_string(ham64_addr_type_t addr_type);
|
||||||
*/
|
*/
|
||||||
void ham64_format(const ham64_t *ham64, char *out);
|
void ham64_format(const ham64_t *ham64, char *out);
|
||||||
|
|
||||||
|
/*!\brief Check if two ham64 addresses are equal.
|
||||||
|
* \param a Pointer to the first address.
|
||||||
|
* \param b Pointer to the second address.
|
||||||
|
* \returns True if a and b are equal, false otherwise.
|
||||||
|
*/
|
||||||
|
bool ham64_is_equal(const ham64_t *a, const ham64_t *b);
|
||||||
|
|
||||||
#endif // HAM64_H
|
#endif // HAM64_H
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <assert.h>
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
|
@ -11,16 +11,17 @@
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
OK,
|
OK,
|
||||||
ERR_INVALID_STATE,
|
ERR_INVALID_STATE, // module or context is in an invalid state
|
||||||
ERR_INVALID_PARAM, // invalid / nonsense parameters given
|
ERR_INVALID_PARAM, // invalid / nonsense parameters given
|
||||||
ERR_NO_MEM, // not enough memory or allocation error
|
ERR_INVALID_ADDRESS, // invalid address received or given
|
||||||
ERR_SIZE, // a given size is invalid
|
ERR_NO_MEM, // not enough memory or allocation error
|
||||||
ERR_LIQUID, // an error occurred in the LiquidDSP library.
|
ERR_SIZE, // a given size is invalid
|
||||||
ERR_SYSCALL, // a syscall failed. Use errno to determine the cause.
|
ERR_LIQUID, // an error occurred in the LiquidDSP library.
|
||||||
ERR_SOAPY, // an error occurred in the SoapySDR library.
|
ERR_SYSCALL, // a syscall failed. Use errno to determine the cause.
|
||||||
ERR_SDR, // an error occurred in the SDR interface.
|
ERR_SOAPY, // an error occurred in the SoapySDR library.
|
||||||
ERR_INTEGRITY, // an integrity check failed (e.g. CRC of received packet is wrong)
|
ERR_SDR, // an error occurred in the SDR interface.
|
||||||
ERR_SEQUENCE, // an unexpected packet was received
|
ERR_INTEGRITY, // an integrity check failed (e.g. CRC of received packet is wrong)
|
||||||
|
ERR_SEQUENCE, // an unexpected packet was received
|
||||||
} result_t;
|
} result_t;
|
||||||
|
|
||||||
#ifdef DEBUG_LIQUID
|
#ifdef DEBUG_LIQUID
|
||||||
|
|
Loading…
Reference in a new issue